4 : _color(
"ffffff"), _size({300, 200}), _defaultcharacterheight(2.25),
5 _defaultticklength(3.0) {
9void Canvas::add(
CanvasItem &item) { _items.emplace_back(item.clone()); }
11void Canvas::add(std::vector<Plot> &plots) {
13 for (
auto &plot : plots) {
15 _items.emplace_back(plot.clone());
19PLINT Canvas::lookUpColor(std::string_view hex) {
21 auto index = std::find(_colormap.begin(), _colormap.end(), hex);
23 PLINT entry = std::distance(_colormap.begin(), index);
25 if (index == _colormap.end()) {
27 _colormap.emplace_back(hex);
31 ss << std::hex << hex.substr(0, 6);
39 if (hex.size() == 8) {
41 ss.str(std::string());
45 ss << std::hex << hex.substr(6);
50 _plstream->scol0a(entry, (rgb >> 16) & 0xff, (rgb >> 8) & 0xff, rgb & 0xff,
51 static_cast<float>(a) / 255.0f);
57void Canvas::paintOnScreen() {
59 _plstream = std::make_unique<plstream>();
61 _plstream->sdev(
"xwin");
63 _plstream->spause(
true);
65 _plstream->spage(72.0, 72.0, _size.at(0), _size.at(1), 0.0, 0.0);
70void Canvas::paintOnPostscript(std::string_view filename) {
72 _plstream = std::make_unique<plstream>();
74 _plstream->sdev(
"epscairo");
76 std::string f(filename);
78 _plstream->sfnam(f.append(
".ps").c_str());
80 _plstream->spause(
false);
82 _plstream->spage(0.0, 0.0, _size.at(0), _size.at(1), 0.0, 0.0);
86 _plstream->setopt(
"aspect",
"1");
91void Canvas::paintOnPDF(std::string_view filename) {
93 _plstream = std::make_unique<plstream>();
95 _plstream->sdev(
"pdfcairo");
97 std::string f(filename);
99 _plstream->sfnam(f.append(
".pdf").c_str());
101 _plstream->spause(
false);
103 _plstream->spage(0.0, 0.0, _size.at(0), _size.at(1), 0.0, 0.0);
107 _plstream->setopt(
"aspect",
"1");
112void Canvas::paintOnPNG(std::string_view filename) {
114 _plstream = std::make_unique<plstream>();
116 _plstream->sdev(
"pngcairo");
118 std::string f(filename);
120 _plstream->sfnam(f.append(
".png").c_str());
122 _plstream->spause(
false);
124 _plstream->spage(0.0, 0.0, _size.at(0), _size.at(1), 0.0, 0.0);
126 _defaultcharacterheight *= 1.5;
131void Canvas::paintOnJPEG(std::string_view filename) {
133 _plstream = std::make_unique<plstream>();
135 _plstream->sdev(
"cairojpeg");
137 std::string f(filename);
139 _plstream->sfnam(f.append(
".jpeg").c_str());
141 _plstream->spause(
false);
143 _plstream->spage(0.0, 0.0, _size.at(0), _size.at(1), 0.0, 0.0);
145 _defaultcharacterheight *= 1.5;
150void Canvas::paint() {
152 bool hadfirstpage =
false;
154 _plstream->scmap0n(64);
162 _plstream->fontld(1);
166 _plstream->schr(_defaultcharacterheight, 1.0);
168 std::array<double, 2> margins;
170 for (
auto &item : _items) {
172 switch (item->type) {
174 case CanvasItem::Type::I_Plot: {
176 Plot *plot =
dynamic_cast<Plot *
>(item.get());
178 if (plot->isAdvance() || !hadfirstpage) {
188 case CanvasItem::Type::I_Panels: {
198 margins[0] = panels->getYMargins().at(0);
200 margins[1] = panels->getYMargins().at(0) +
201 (panels->getYMargins().at(1) - panels->getYMargins().at(0)) /
202 static_cast<double>(panels->getRows());
204 for (
auto &panel : *panels) {
206 Plot *plot =
dynamic_cast<Plot *
>(panel.get());
208 plot->setYMargins(margins);
210 if (plot->isAdvance()) {
212 margins[0] = margins[1];
214 margins[1] = margins[1] + (panels->getYMargins().at(1) -
215 panels->getYMargins().at(0)) /
216 static_cast<double>(panels->getRows());
223 case CanvasItem::Type::I_Text: {
231 Text *text =
dynamic_cast<Text *
>(item.get());
233 _plstream->vpor(0.0, 1.0, 0.0, 1.0);
235 _plstream->wind(0.0, 1.0, 0.0, 1.0);
237 _plstream->col0(lookUpColor(text->getColor()));
239 _plstream->schr(_defaultcharacterheight, text->getSize());
241 _plstream->ptex(text->getCoordinates().at(0),
242 text->getCoordinates().at(1),
243 cosf(M_PI * text->getAngle() / 180.0),
244 sinf(M_PI * text->getAngle() / 180.0),
245 text->getJustification(), text->getText().data());
249 throw(
Exception(
"Unsupported graphics type"));
255void Canvas::draw(
Plot *plot) {
257 _plstream->vpor(plot->getXMargins().at(0), plot->getXMargins().at(1),
258 plot->getYMargins().at(0), plot->getYMargins().at(1));
260 float xmin, xmax, ymin, ymax;
262 xmin = plot->getXLimits().at(0);
264 xmax = plot->getXLimits().at(1);
266 ymin = plot->getYLimits().at(0);
268 ymax = plot->getYLimits().at(1);
270 _plstream->wind(xmin, xmax, ymin, ymax);
272 _plstream->width(plot->getLineWidth());
274 _plstream->lsty(
static_cast<PLINT
>(plot->getLineStyle()));
276 if (plot->isDrawHorizontalGrid() || plot->isDrawHorizontalFineGrid()) {
278 _plstream->col0(lookUpColor(plot->getHorizontalGrid().getColor()));
281 static_cast<PLINT
>(plot->getHorizontalGrid().getLineStyle()));
283 _plstream->width(plot->getHorizontalGrid().getLineWidth());
285 if (plot->isDrawHorizontalFineGrid()) {
287 _plstream->box(
"gh", 0.0, 0.0,
"", 0.0, 0.0);
290 _plstream->box(
"g", 0.0, 0.0,
"", 0.0, 0.0);
294 if (plot->isDrawVerticalGrid() || plot->isDrawVerticalFineGrid()) {
296 _plstream->col0(lookUpColor(plot->getVerticalGrid().getColor()));
298 _plstream->lsty(
static_cast<PLINT
>(plot->getVerticalGrid().getLineStyle()));
300 _plstream->width(plot->getVerticalGrid().getLineWidth());
302 if (plot->isDrawVerticalFineGrid()) {
304 _plstream->box(
"", 0, 0,
"gh", 0, 0);
307 _plstream->box(
"", 0, 0,
"g", 0, 0);
311 for (
auto &p : *plot) {
315 case CanvasItem::Type::I_Point: {
317 Point *point =
dynamic_cast<Point *
>(p.get());
319 _plstream->col0(lookUpColor(point->getColor()));
321 _plstream->width(plot->getLineWidth());
323 _plstream->lsty(
static_cast<PLINT
>(plot->getLineStyle()));
325 _plstream->ssym(_defaultcharacterheight, point->getSize());
327 PLFLT px = point->getCoordinates().at(0);
329 PLFLT py = point->getCoordinates().at(1);
331 _plstream->sym(1, &px, &py, point->getSymbol());
333 case CanvasItem::Type::I_Line: {
335 Line *line =
dynamic_cast<Line *
>(p.get());
337 _plstream->col0(lookUpColor(line->getColor()));
339 _plstream->lsty(
static_cast<PLINT
>(line->getLineStyle()));
341 _plstream->width(line->getLineWidth());
344 line->getStartCoordinates().at(0), line->getStartCoordinates().at(1),
345 line->getEndCoordinates().at(0), line->getEndCoordinates().at(1));
347 case CanvasItem::Type::I_Curve: {
349 Curve *curve =
dynamic_cast<Curve *
>(p.get());
351 if (curve->getX().size() == 0) {
356 const PLFLT *x = curve->getX().data();
358 const PLFLT *xerr = curve->getXErr().data();
360 const PLFLT *y = curve->getY().data();
362 const PLFLT *yerr = curve->getYErr().data();
364 size_t n = curve->getX().size();
368 _plstream->lsty(
static_cast<PLINT
>(curve->getLineStyle()));
370 _plstream->width(curve->getLineWidth());
372 _plstream->col0(lookUpColor(curve->getColor()));
374 std::vector<PLFLT> min(n);
376 std::vector<PLFLT> max(n);
380 std::transform(curve->getX().begin(), curve->getX().end(),
381 curve->getXErr().begin(), min.begin(),
382 std::minus<PLFLT>());
384 std::transform(curve->getX().begin(), curve->getX().end(),
385 curve->getXErr().begin(), max.begin(),
388 _plstream->errx(n, x, min.data(), max.data());
392 std::transform(curve->getY().begin(), curve->getY().end(),
393 curve->getYErr().begin(), min.begin(),
394 std::minus<PLFLT>());
396 std::transform(curve->getY().begin(), curve->getY().end(),
397 curve->getYErr().begin(), max.begin(),
400 _plstream->erry(n, x, min.data(), max.data());
404 if (curve->isFill() && curve->getSymbol() == 0) {
406 _plstream->col0(lookUpColor(curve->getFillColor()));
408 if (y[0] == 0.0f && y[n - 1] == 0.0f) {
410 _plstream->fill(n, x, y);
413 std::vector<PLFLT> a(curve->getX().begin(), curve->getX().end());
415 std::vector<PLFLT> b(curve->getY().begin(), curve->getY().end());
417 if (y[n - 1] != 0.0f) {
419 a.push_back(x[n - 1]);
426 a.insert(a.begin(), x[0]);
428 b.insert(b.begin(), 0.0f);
431 _plstream->fill(a.size(), a.data(), b.data());
435 _plstream->col0(lookUpColor(curve->getColor()));
437 if (curve->getSymbol() == 0) {
439 _plstream->lsty(
static_cast<PLINT
>(curve->getLineStyle()));
441 _plstream->width(curve->getLineWidth());
443 _plstream->line(n, x, y);
446 _plstream->ssym(_defaultcharacterheight, curve->getSymbolSize());
448 _plstream->poin(n, x, y, curve->getSymbol());
451 case CanvasItem::Type::I_Text: {
453 Text *text =
dynamic_cast<Text *
>(p.get());
455 _plstream->col0(lookUpColor(text->getColor()));
457 _plstream->width(plot->getLineWidth());
459 _plstream->lsty(
static_cast<PLINT
>(plot->getLineStyle()));
461 _plstream->schr(_defaultcharacterheight, text->getSize());
463 if (text->getSystem() == Text::CoordinateSystem::DATA) {
465 _plstream->ptex(text->getCoordinates().at(0),
466 text->getCoordinates().at(1),
467 (xmax - xmin) * cosf(M_PI * text->getAngle() / 180.0),
468 (ymax - ymin) * sinf(M_PI * text->getAngle() / 180.0),
469 text->getJustification(), text->getText().data());
470 }
else if (text->getSystem() == Text::CoordinateSystem::NORMAL) {
472 _plstream->ptex(xmin + (xmax - xmin) * text->getCoordinates().at(0),
473 ymin + (ymax - ymin) * text->getCoordinates().at(1),
474 (xmax - xmin) * cosf(M_PI * text->getAngle() / 180.0),
475 (ymax - ymin) * sinf(M_PI * text->getAngle() / 180.0),
476 text->getJustification(), text->getText().data());
479 case CanvasItem::Type::I_Panels:
482 throw(
Exception(
"Unsupported graphics type"));
487 _plstream->col0(lookUpColor(plot->getColor()));
489 _plstream->lsty(
static_cast<PLINT
>(plot->getLineStyle()));
491 _plstream->width(plot->getLineWidth());
493 _plstream->schr(_defaultcharacterheight, plot->getFontSize());
495 _plstream->smaj(_defaultticklength, plot->getMajorTickLength());
497 _plstream->smin(_defaultticklength, plot->getMinorTickLength());
499 for (
auto &ax : plot->getXAxis()) {
501 _plstream->sxax(ax.getMaxDigits(), ax.getPrecision());
503 if (ax.getAxisOptString().find(
'n') != std::string::npos &&
504 ax.getStyle() == Axis::Style::WritePowerInFrame) {
508 order = floor(log10(xmax));
510 if (std::abs(order) > 1.0) {
512 std::ostringstream ostr;
514 ostr <<
"(x10#u" << order <<
"#d)";
516 _plstream->ptex(0.9 * (xmax - xmin) + xmin, 0.05 * (ymax - ymin) + ymin,
517 xmax, 0.0, 0.0, ostr.str().c_str());
519 xmin /= pow(10, order);
521 xmax /= pow(10, order);
525 if (ax.getStyle() != Axis::Style::Default) {
527 _plstream->wind(xmin, xmax, ymin, ymax);
530 if (ax.getTickFinder()) {
532 std::string axisoptstr(ax.getAxisOptString());
534 const std::string opts(
"mnst");
536 std::string::size_type i;
537 for (
const char opt : opts) {
539 if ((i = axisoptstr.find(opt)) != std::string::npos) {
541 axisoptstr.erase(i, 1);
545 _plstream->axes(0.0, 0.0, axisoptstr.data(), 0, 0,
"", 0, 0);
547 PLFLT p_xmin, p_xmax, p_ymin, p_ymax;
549 _plstream->gspa(p_xmin, p_xmax, p_ymin, p_ymax);
551 PLFLT dx = abs(xmax - xmin);
553 double dy = (axisoptstr.find(
'i') == std::string::npos ? -1.0 : 1.0) *
554 (ymax - ymin) * _defaultticklength *
555 plot->getMajorTickLength() /
556 ((plot->getYMargins().at(1) - plot->getYMargins().at(0)) *
561 PLINT nticks = ax.getTickFinder()(PL_X_AXIS,
sizeof(ticks), ticks, xmin,
564 for (PLINT i = 0; i < nticks; i++) {
566 _plstream->join(ticks[i], ymax + dy, ticks[i], ymax);
568 double pos = (ticks[i] - xmin) / dx;
572 pos = (xmin - ticks[i]) / dx;
577 if (ax.getLabelFormatter()) {
579 ax.getLabelFormatter()(PL_X_AXIS, ticks[i], label,
sizeof(label),
583 snprintf(label,
sizeof(label),
"%g", ticks[i]);
586 _plstream->mtex(
"t", 1.5, pos, 0.5, label);
590 _plstream->slabelfunc(ax.getLabelFormatter(),
nullptr);
592 _plstream->axes(0.0, 0.0, ax.getAxisOptString().data(), 0, 0,
"", 0, 0);
595 if (ax.getAxisOptString().find(
'm') != std::string::npos) {
597 _plstream->mtex(
"t", 3.5, 0.5, 0.5, ax.getTitle().data());
600 _plstream->mtex(
"b", 3.5, 0.5, 0.5, ax.getTitle().data());
604 for (
auto &ax : plot->getYAxis()) {
606 _plstream->syax(ax.getMaxDigits(), ax.getPrecision());
608 if (ax.getAxisOptString().find(
'n') != std::string::npos &&
609 ax.getStyle() == Axis::Style::WritePowerInFrame) {
613 order = floor(log10(ymax));
615 if (std::abs(order) > 1.0) {
617 std::ostringstream ostr;
619 ostr <<
"(x10#u" << order <<
"#d)";
621 _plstream->ptex(0.05 * (xmax - xmin) + xmin, 0.9 * (ymax - ymin) + ymin,
622 (xmax - xmin), 0.0, 0.0, ostr.str().c_str());
624 ymin /= pow(10, order);
626 ymax /= pow(10, order);
630 if (ax.getStyle() != Axis::Style::Default) {
632 _plstream->wind(xmin, xmax, ymin, ymax);
635 if (ax.getTickFinder()) {
637 std::string axisoptstr(ax.getAxisOptString());
639 const std::string opts(
"mnst");
641 std::string::size_type i;
642 for (
const char opt : opts) {
644 if ((i = axisoptstr.find(opt)) != std::string::npos) {
646 axisoptstr.erase(i, 1);
650 _plstream->axes(0.0, 0.0, axisoptstr.data(), 0, 0,
"", 0, 0);
652 PLFLT p_xmin, p_xmax, p_ymin, p_ymax;
654 _plstream->gspa(p_xmin, p_xmax, p_ymin, p_ymax);
656 double dx = (axisoptstr.find(
'i') == std::string::npos ? -1.0 : 1.0) *
657 (xmax - xmin) * _defaultticklength *
658 plot->getMajorTickLength() /
659 ((plot->getXMargins().at(1) - plot->getXMargins().at(0)) *
662 PLFLT dy = abs(ymax - ymin);
666 PLINT nticks = ax.getTickFinder()(PL_Y_AXIS,
sizeof(ticks), ticks, ymin,
669 for (PLINT i = 0; i < nticks; i++) {
671 _plstream->join(xmax + dx, ticks[i], xmax, ticks[i]);
673 double pos = (ticks[i] - ymin) / dy;
677 pos = (ymin - ticks[i]) / dy;
682 if (ax.getLabelFormatter()) {
684 ax.getLabelFormatter()(PL_Y_AXIS, ticks[i], label,
sizeof(label),
688 snprintf(label,
sizeof(label),
"%g", ticks[i]);
691 _plstream->mtex(
"l", 1.5, pos, 0.5, label);
695 _plstream->slabelfunc(ax.getLabelFormatter(),
nullptr);
697 _plstream->axes(0.0, 0.0,
"", 0, 0, ax.getAxisOptString().data(), 0, 0);
700 if (ax.getAxisOptString().find(
'm') != std::string::npos) {
702 _plstream->mtex(
"r", 3.5, 0.5, 0.5, ax.getTitle().data());
705 _plstream->mtex(
"l", 3.5, 0.5, 0.5, ax.getTitle().data());