NAPISD
PAHdb website C++ backend
Loading...
Searching...
No Matches
Canvas.cpp
1#include "Canvas.h"
2
3Canvas::Canvas()
4 : _color("ffffff"), _size({300, 200}), _defaultcharacterheight(2.25),
5 _defaultticklength(3.0) {
6 _items.reserve(4);
7}
8
9void Canvas::add(CanvasItem &item) { _items.emplace_back(item.clone()); }
10
11void Canvas::add(std::vector<Plot> &plots) {
12
13 for (auto &plot : plots) {
14
15 _items.emplace_back(plot.clone());
16 }
17}
18
19PLINT Canvas::lookUpColor(std::string_view hex) {
20
21 auto index = std::find(_colormap.begin(), _colormap.end(), hex);
22
23 PLINT entry = std::distance(_colormap.begin(), index);
24
25 if (index == _colormap.end()) {
26
27 _colormap.emplace_back(hex);
28
29 std::stringstream ss;
30
31 ss << std::hex << hex.substr(0, 6);
32
33 int rgb;
34
35 ss >> rgb;
36
37 int a = 255;
38
39 if (hex.size() == 8) {
40
41 ss.str(std::string());
42
43 ss.clear();
44
45 ss << std::hex << hex.substr(6);
46
47 ss >> a;
48 }
49
50 _plstream->scol0a(entry, (rgb >> 16) & 0xff, (rgb >> 8) & 0xff, rgb & 0xff,
51 static_cast<float>(a) / 255.0f);
52 }
53
54 return (entry);
55}
56
57void Canvas::paintOnScreen() {
58
59 _plstream = std::make_unique<plstream>();
60
61 _plstream->sdev("xwin");
62
63 _plstream->spause(true);
64
65 _plstream->spage(72.0, 72.0, _size.at(0), _size.at(1), 0.0, 0.0);
66
67 paint();
68}
69
70void Canvas::paintOnPostscript(std::string_view filename) {
71
72 _plstream = std::make_unique<plstream>();
73
74 _plstream->sdev("epscairo");
75
76 std::string f(filename);
77
78 _plstream->sfnam(f.append(".ps").c_str());
79
80 _plstream->spause(false);
81
82 _plstream->spage(0.0, 0.0, _size.at(0), _size.at(1), 0.0, 0.0);
83
84 //_plstream->sdiori(1);
85
86 _plstream->setopt("aspect", "1");
87
88 paint();
89}
90
91void Canvas::paintOnPNG(std::string_view filename) {
92
93 _plstream = std::make_unique<plstream>();
94
95 _plstream->sdev("pngcairo");
96
97 std::string f(filename);
98
99 _plstream->sfnam(f.append(".png").c_str());
100
101 _plstream->spause(false);
102
103 _plstream->spage(0.0, 0.0, _size.at(0), _size.at(1), 0.0, 0.0);
104
105 _defaultcharacterheight *= 1.5;
106
107 paint();
108}
109
110void Canvas::paintOnJPEG(std::string_view filename) {
111
112 _plstream = std::make_unique<plstream>();
113
114 _plstream->sdev("cairojpeg");
115
116 std::string f(filename);
117
118 _plstream->sfnam(f.append(".jpeg").c_str());
119
120 _plstream->spause(false);
121
122 _plstream->spage(0.0, 0.0, _size.at(0), _size.at(1), 0.0, 0.0);
123
124 _defaultcharacterheight *= 1.5;
125
126 paint();
127}
128
129void Canvas::paint() {
130
131 bool hadfirstpage = false;
132
133 _plstream->scmap0n(64);
134
135 _colormap.clear();
136
137 lookUpColor(_color);
138
139 _plstream->init();
140
141 _plstream->fontld(1);
142
143 _plstream->font(2);
144
145 _plstream->schr(_defaultcharacterheight, 1.0);
146
147 std::array<double, 2> margins;
148
149 for (auto &item : _items) {
150
151 switch (item->type) {
152
153 case CanvasItem::Type::I_Plot: {
154
155 Plot *plot = dynamic_cast<Plot *>(item.get());
156
157 if (plot->isAdvance() || !hadfirstpage) {
158
159 _plstream->adv(0);
160
161 hadfirstpage = true;
162 }
163
164 draw(plot);
165 } break;
166
167 case CanvasItem::Type::I_Panels: {
168 if (!hadfirstpage) {
169
170 _plstream->adv(0);
171
172 hadfirstpage = true;
173 }
174
175 Panels *panels = dynamic_cast<Panels *>(item.get());
176
177 margins[0] = panels->getYMargins().at(0);
178
179 margins[1] = panels->getYMargins().at(0) +
180 (panels->getYMargins().at(1) - panels->getYMargins().at(0)) /
181 static_cast<double>(panels->getRows());
182
183 for (auto &panel : *panels) {
184
185 Plot *plot = dynamic_cast<Plot *>(panel.get());
186
187 plot->setYMargins(margins);
188
189 if (plot->isAdvance()) {
190
191 margins[0] = margins[1];
192
193 margins[1] = margins[1] + (panels->getYMargins().at(1) -
194 panels->getYMargins().at(0)) /
195 static_cast<double>(panels->getRows());
196 }
197
198 draw(plot);
199 }
200
201 } break;
202 case CanvasItem::Type::I_Text: {
203 if (!hadfirstpage) {
204
205 _plstream->adv(0);
206
207 hadfirstpage = true;
208 }
209
210 Text *text = dynamic_cast<Text *>(item.get());
211
212 _plstream->vpor(0.0, 1.0, 0.0, 1.0);
213
214 _plstream->wind(0.0, 1.0, 0.0, 1.0);
215
216 _plstream->col0(lookUpColor(text->getColor()));
217
218 _plstream->schr(_defaultcharacterheight, text->getSize());
219
220 _plstream->ptex(text->getCoordinates().at(0),
221 text->getCoordinates().at(1),
222 cosf(M_PI * text->getAngle() / 180.0),
223 sinf(M_PI * text->getAngle() / 180.0),
224 text->getJustification(), text->getText().data());
225 } break;
226 default: {
227
228 throw(Exception("Unsupported graphics type"));
229 } break;
230 }
231 }
232}
233
234void Canvas::draw(Plot *plot) {
235
236 _plstream->vpor(plot->getXMargins().at(0), plot->getXMargins().at(1),
237 plot->getYMargins().at(0), plot->getYMargins().at(1));
238
239 float xmin, xmax, ymin, ymax;
240
241 xmin = plot->getXLimits().at(0);
242
243 xmax = plot->getXLimits().at(1);
244
245 ymin = plot->getYLimits().at(0);
246
247 ymax = plot->getYLimits().at(1);
248
249 _plstream->wind(xmin, xmax, ymin, ymax);
250
251 _plstream->width(plot->getLineWidth());
252
253 _plstream->lsty(static_cast<PLINT>(plot->getLineStyle()));
254
255 if (plot->isDrawHorizontalGrid() || plot->isDrawHorizontalFineGrid()) {
256
257 _plstream->col0(lookUpColor(plot->getHorizontalGrid().getColor()));
258
259 _plstream->lsty(
260 static_cast<PLINT>(plot->getHorizontalGrid().getLineStyle()));
261
262 _plstream->width(plot->getHorizontalGrid().getLineWidth());
263
264 if (plot->isDrawHorizontalFineGrid()) {
265
266 _plstream->box("gh", 0.0, 0.0, "", 0.0, 0.0);
267 } else {
268
269 _plstream->box("g", 0.0, 0.0, "", 0.0, 0.0);
270 }
271 }
272
273 if (plot->isDrawVerticalGrid() || plot->isDrawVerticalFineGrid()) {
274
275 _plstream->col0(lookUpColor(plot->getVerticalGrid().getColor()));
276
277 _plstream->lsty(static_cast<PLINT>(plot->getVerticalGrid().getLineStyle()));
278
279 _plstream->width(plot->getVerticalGrid().getLineWidth());
280
281 if (plot->isDrawVerticalFineGrid()) {
282
283 _plstream->box("", 0, 0, "gh", 0, 0);
284 } else {
285
286 _plstream->box("", 0, 0, "g", 0, 0);
287 }
288 }
289
290 for (auto &p : *plot) {
291
292 switch (p->type) {
293
294 case CanvasItem::Type::I_Point: {
295
296 Point *point = dynamic_cast<Point *>(p.get());
297
298 _plstream->col0(lookUpColor(point->getColor()));
299
300 _plstream->width(plot->getLineWidth());
301
302 _plstream->lsty(static_cast<PLINT>(plot->getLineStyle()));
303
304 _plstream->ssym(_defaultcharacterheight, point->getSize());
305
306 PLFLT px = point->getCoordinates().at(0);
307
308 PLFLT py = point->getCoordinates().at(1);
309
310 _plstream->sym(1, &px, &py, point->getSymbol());
311 } break;
312 case CanvasItem::Type::I_Line: {
313
314 Line *line = dynamic_cast<Line *>(p.get());
315
316 _plstream->col0(lookUpColor(line->getColor()));
317
318 _plstream->lsty(static_cast<PLINT>(line->getLineStyle()));
319
320 _plstream->width(line->getLineWidth());
321
322 _plstream->join(
323 line->getStartCoordinates().at(0), line->getStartCoordinates().at(1),
324 line->getEndCoordinates().at(0), line->getEndCoordinates().at(1));
325 } break;
326 case CanvasItem::Type::I_Curve: {
327
328 Curve *curve = dynamic_cast<Curve *>(p.get());
329
330 if (curve->getX().size() == 0) {
331
332 continue;
333 }
334
335 const PLFLT *x = curve->getX().data();
336
337 const PLFLT *xerr = curve->getXErr().data();
338
339 const PLFLT *y = curve->getY().data();
340
341 const PLFLT *yerr = curve->getYErr().data();
342
343 size_t n = curve->getX().size();
344
345 if (xerr || yerr) {
346
347 _plstream->lsty(static_cast<PLINT>(curve->getLineStyle()));
348
349 _plstream->width(curve->getLineWidth());
350
351 _plstream->col0(lookUpColor(curve->getColor()));
352
353 std::vector<PLFLT> min(n);
354
355 std::vector<PLFLT> max(n);
356
357 if (xerr) {
358
359 std::transform(curve->getX().begin(), curve->getX().end(),
360 curve->getXErr().begin(), min.begin(),
361 std::minus<PLFLT>());
362
363 std::transform(curve->getX().begin(), curve->getX().end(),
364 curve->getXErr().begin(), max.begin(),
365 std::plus<PLFLT>());
366
367 _plstream->errx(n, x, min.data(), max.data());
368 }
369 if (yerr) {
370
371 std::transform(curve->getY().begin(), curve->getY().end(),
372 curve->getYErr().begin(), min.begin(),
373 std::minus<PLFLT>());
374
375 std::transform(curve->getY().begin(), curve->getY().end(),
376 curve->getYErr().begin(), max.begin(),
377 std::plus<PLFLT>());
378
379 _plstream->erry(n, x, min.data(), max.data());
380 }
381 }
382
383 if (curve->isFill() && curve->getSymbol() == 0) {
384
385 _plstream->col0(lookUpColor(curve->getFillColor()));
386
387 if (y[0] == 0.0f && y[n - 1] == 0.0f) {
388
389 _plstream->fill(n, x, y);
390 } else {
391
392 std::vector<PLFLT> a(curve->getX().begin(), curve->getX().end());
393
394 std::vector<PLFLT> b(curve->getY().begin(), curve->getY().end());
395
396 if (y[n - 1] != 0.0f) {
397
398 a.push_back(x[n - 1]);
399
400 b.push_back(0.0f);
401 }
402
403 if (y[0] != 0.0f) {
404
405 a.insert(a.begin(), x[0]);
406
407 b.insert(b.begin(), 0.0f);
408 }
409
410 _plstream->fill(a.size(), a.data(), b.data());
411 }
412 }
413
414 _plstream->col0(lookUpColor(curve->getColor()));
415
416 if (curve->getSymbol() == 0) {
417
418 _plstream->lsty(static_cast<PLINT>(curve->getLineStyle()));
419
420 _plstream->width(curve->getLineWidth());
421
422 _plstream->line(n, x, y);
423 } else {
424
425 _plstream->ssym(_defaultcharacterheight, curve->getSymbolSize());
426
427 _plstream->poin(n, x, y, curve->getSymbol());
428 }
429 } break;
430 case CanvasItem::Type::I_Text: {
431
432 Text *text = dynamic_cast<Text *>(p.get());
433
434 _plstream->col0(lookUpColor(text->getColor()));
435
436 _plstream->width(plot->getLineWidth());
437
438 _plstream->lsty(static_cast<PLINT>(plot->getLineStyle()));
439
440 _plstream->schr(_defaultcharacterheight, text->getSize());
441
442 if (text->getSystem() == Text::CoordinateSystem::DATA) {
443
444 _plstream->ptex(text->getCoordinates().at(0),
445 text->getCoordinates().at(1),
446 (xmax - xmin) * cosf(M_PI * text->getAngle() / 180.0),
447 (ymax - ymin) * sinf(M_PI * text->getAngle() / 180.0),
448 text->getJustification(), text->getText().data());
449 } else if (text->getSystem() == Text::CoordinateSystem::NORMAL) {
450
451 _plstream->ptex(xmin + (xmax - xmin) * text->getCoordinates().at(0),
452 ymin + (ymax - ymin) * text->getCoordinates().at(1),
453 (xmax - xmin) * cosf(M_PI * text->getAngle() / 180.0),
454 (ymax - ymin) * sinf(M_PI * text->getAngle() / 180.0),
455 text->getJustification(), text->getText().data());
456 }
457 } break;
458 case CanvasItem::Type::I_Panels:
459 default:
460
461 throw(Exception("Unsupported graphics type"));
462 break;
463 }
464 }
465
466 _plstream->col0(lookUpColor(plot->getColor()));
467
468 _plstream->lsty(static_cast<PLINT>(plot->getLineStyle()));
469
470 _plstream->width(plot->getLineWidth());
471
472 _plstream->schr(_defaultcharacterheight, plot->getFontSize());
473
474 _plstream->smaj(_defaultticklength, plot->getMajorTickLength());
475
476 _plstream->smin(_defaultticklength, plot->getMinorTickLength());
477
478 for (auto &ax : plot->getXAxis()) {
479
480 _plstream->sxax(ax.getMaxDigits(), ax.getPrecision());
481
482 if (ax.getAxisOptString().find('n') != std::string::npos &&
483 ax.getStyle() == Axis::Style::WritePowerInFrame) {
484
485 float order;
486
487 order = floor(log10(xmax));
488
489 if (std::abs(order) > 1.0) {
490
491 std::ostringstream ostr;
492
493 ostr << "(x10#u" << order << "#d)";
494
495 _plstream->ptex(0.9 * (xmax - xmin) + xmin, 0.05 * (ymax - ymin) + ymin,
496 xmax, 0.0, 0.0, ostr.str().c_str());
497
498 xmin /= pow(10, order);
499
500 xmax /= pow(10, order);
501 }
502 }
503
504 if (ax.getStyle() != Axis::Style::Default) {
505
506 _plstream->wind(xmin, xmax, ymin, ymax);
507 }
508
509 if (ax.getTickFinder()) {
510
511 std::string axisoptstr(ax.getAxisOptString());
512
513 const std::string opts("mnst");
514
515 std::string::size_type i;
516 for (const char opt : opts) {
517
518 if ((i = axisoptstr.find(opt)) != std::string::npos) {
519
520 axisoptstr.erase(i, 1);
521 }
522 }
523
524 _plstream->axes(0.0, 0.0, axisoptstr.data(), 0, 0, "", 0, 0);
525
526 PLFLT p_xmin, p_xmax, p_ymin, p_ymax;
527
528 _plstream->gspa(p_xmin, p_xmax, p_ymin, p_ymax);
529
530 PLFLT dx = abs(xmax - xmin);
531
532 double dy = (axisoptstr.find('i') == std::string::npos ? -1.0 : 1.0) *
533 (ymax - ymin) * _defaultticklength *
534 plot->getMajorTickLength() /
535 ((plot->getYMargins().at(1) - plot->getYMargins().at(0)) *
536 (p_ymax - p_ymin));
537
538 PLFLT ticks[16];
539
540 PLINT nticks = ax.getTickFinder()(PL_X_AXIS, sizeof(ticks), ticks, xmin,
541 xmax, nullptr);
542
543 for (PLINT i = 0; i < nticks; i++) {
544
545 _plstream->join(ticks[i], ymax + dy, ticks[i], ymax);
546
547 double pos = (ticks[i] - xmin) / dx;
548
549 if (xmin > xmax) {
550
551 pos = (xmin - ticks[i]) / dx;
552 }
553
554 char label[16];
555
556 if (ax.getLabelFormatter()) {
557
558 ax.getLabelFormatter()(PL_X_AXIS, ticks[i], label, sizeof(label),
559 nullptr);
560 } else {
561
562 snprintf(label, sizeof(label), "%g", ticks[i]);
563 }
564
565 _plstream->mtex("t", 1.5, pos, 0.5, label);
566 }
567 } else {
568
569 _plstream->slabelfunc(ax.getLabelFormatter(), nullptr);
570
571 _plstream->axes(0.0, 0.0, ax.getAxisOptString().data(), 0, 0, "", 0, 0);
572 }
573
574 if (ax.getAxisOptString().find('m') != std::string::npos) {
575
576 _plstream->mtex("t", 3.5, 0.5, 0.5, ax.getTitle().data());
577 } else {
578
579 _plstream->mtex("b", 3.5, 0.5, 0.5, ax.getTitle().data());
580 }
581 }
582
583 for (auto &ax : plot->getYAxis()) {
584
585 _plstream->syax(ax.getMaxDigits(), ax.getPrecision());
586
587 if (ax.getAxisOptString().find('n') != std::string::npos &&
588 ax.getStyle() == Axis::Style::WritePowerInFrame) {
589
590 float order;
591
592 order = floor(log10(ymax));
593
594 if (std::abs(order) > 1.0) {
595
596 std::ostringstream ostr;
597
598 ostr << "(x10#u" << order << "#d)";
599
600 _plstream->ptex(0.05 * (xmax - xmin) + xmin, 0.9 * (ymax - ymin) + ymin,
601 (xmax - xmin), 0.0, 0.0, ostr.str().c_str());
602
603 ymin /= pow(10, order);
604
605 ymax /= pow(10, order);
606 }
607 }
608
609 if (ax.getStyle() != Axis::Style::Default) {
610
611 _plstream->wind(xmin, xmax, ymin, ymax);
612 }
613
614 if (ax.getTickFinder()) {
615
616 std::string axisoptstr(ax.getAxisOptString());
617
618 const std::string opts("mnst");
619
620 std::string::size_type i;
621 for (const char opt : opts) {
622
623 if ((i = axisoptstr.find(opt)) != std::string::npos) {
624
625 axisoptstr.erase(i, 1);
626 }
627 }
628
629 _plstream->axes(0.0, 0.0, axisoptstr.data(), 0, 0, "", 0, 0);
630
631 PLFLT p_xmin, p_xmax, p_ymin, p_ymax;
632
633 _plstream->gspa(p_xmin, p_xmax, p_ymin, p_ymax);
634
635 double dx = (axisoptstr.find('i') == std::string::npos ? -1.0 : 1.0) *
636 (xmax - xmin) * _defaultticklength *
637 plot->getMajorTickLength() /
638 ((plot->getXMargins().at(1) - plot->getXMargins().at(0)) *
639 (p_xmax - p_xmin));
640
641 PLFLT dy = abs(ymax - ymin);
642
643 PLFLT ticks[16];
644
645 PLINT nticks = ax.getTickFinder()(PL_Y_AXIS, sizeof(ticks), ticks, ymin,
646 ymax, nullptr);
647
648 for (PLINT i = 0; i < nticks; i++) {
649
650 _plstream->join(xmax + dx, ticks[i], xmax, ticks[i]);
651
652 double pos = (ticks[i] - ymin) / dy;
653
654 if (ymin > ymax) {
655
656 pos = (ymin - ticks[i]) / dy;
657 }
658
659 char label[16];
660
661 if (ax.getLabelFormatter()) {
662
663 ax.getLabelFormatter()(PL_Y_AXIS, ticks[i], label, sizeof(label),
664 nullptr);
665 } else {
666
667 snprintf(label, sizeof(label), "%g", ticks[i]);
668 }
669
670 _plstream->mtex("l", 1.5, pos, 0.5, label);
671 }
672 } else {
673
674 _plstream->slabelfunc(ax.getLabelFormatter(), nullptr);
675
676 _plstream->axes(0.0, 0.0, "", 0, 0, ax.getAxisOptString().data(), 0, 0);
677 }
678
679 if (ax.getAxisOptString().find('m') != std::string::npos) {
680
681 _plstream->mtex("r", 3.5, 0.5, 0.5, ax.getTitle().data());
682 } else {
683
684 _plstream->mtex("l", 3.5, 0.5, 0.5, ax.getTitle().data());
685 }
686 }
687}
Definition Curve.h:11
Definition Line.h:9
Definition Plot.h:20
Definition Point.h:11
Definition Text.h:13

Since FY2019 the NASA Ames PAH IR Spectroscopic Database is being supported through a directed Work Package at NASA Ames titled: "Laboratory Astrophysics - The NASA Ames PAH IR Spectroscopic Database".
Since FY2023 the NASA Ames PAH IR Spectroscopic Database is being supported through the Laboratory Astrophysics Rd 2 directed Work Package at NASA Ames.
© Copyright 2021-2025, Christiaan Boersma