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::paintOnPNG(std::string_view filename) {
93 _plstream = std::make_unique<plstream>();
95 _plstream->sdev(
"pngcairo");
97 std::string f(filename);
99 _plstream->sfnam(f.append(
".png").c_str());
101 _plstream->spause(
false);
103 _plstream->spage(0.0, 0.0, _size.at(0), _size.at(1), 0.0, 0.0);
105 _defaultcharacterheight *= 1.5;
110void Canvas::paintOnJPEG(std::string_view filename) {
112 _plstream = std::make_unique<plstream>();
114 _plstream->sdev(
"cairojpeg");
116 std::string f(filename);
118 _plstream->sfnam(f.append(
".jpeg").c_str());
120 _plstream->spause(
false);
122 _plstream->spage(0.0, 0.0, _size.at(0), _size.at(1), 0.0, 0.0);
124 _defaultcharacterheight *= 1.5;
129void Canvas::paint() {
131 bool hadfirstpage =
false;
133 _plstream->scmap0n(64);
141 _plstream->fontld(1);
145 _plstream->schr(_defaultcharacterheight, 1.0);
147 std::array<double, 2> margins;
149 for (
auto &item : _items) {
151 switch (item->type) {
153 case CanvasItem::Type::I_Plot: {
155 Plot *plot =
dynamic_cast<Plot *
>(item.get());
157 if (plot->isAdvance() || !hadfirstpage) {
167 case CanvasItem::Type::I_Panels: {
177 margins[0] = panels->getYMargins().at(0);
179 margins[1] = panels->getYMargins().at(0) +
180 (panels->getYMargins().at(1) - panels->getYMargins().at(0)) /
181 static_cast<double>(panels->getRows());
183 for (
auto &panel : *panels) {
185 Plot *plot =
dynamic_cast<Plot *
>(panel.get());
187 plot->setYMargins(margins);
189 if (plot->isAdvance()) {
191 margins[0] = margins[1];
193 margins[1] = margins[1] + (panels->getYMargins().at(1) -
194 panels->getYMargins().at(0)) /
195 static_cast<double>(panels->getRows());
202 case CanvasItem::Type::I_Text: {
210 Text *text =
dynamic_cast<Text *
>(item.get());
212 _plstream->vpor(0.0, 1.0, 0.0, 1.0);
214 _plstream->wind(0.0, 1.0, 0.0, 1.0);
216 _plstream->col0(lookUpColor(text->getColor()));
218 _plstream->schr(_defaultcharacterheight, text->getSize());
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());
228 throw(
Exception(
"Unsupported graphics type"));
234void Canvas::draw(
Plot *plot) {
236 _plstream->vpor(plot->getXMargins().at(0), plot->getXMargins().at(1),
237 plot->getYMargins().at(0), plot->getYMargins().at(1));
239 float xmin, xmax, ymin, ymax;
241 xmin = plot->getXLimits().at(0);
243 xmax = plot->getXLimits().at(1);
245 ymin = plot->getYLimits().at(0);
247 ymax = plot->getYLimits().at(1);
249 _plstream->wind(xmin, xmax, ymin, ymax);
251 _plstream->width(plot->getLineWidth());
253 _plstream->lsty(
static_cast<PLINT
>(plot->getLineStyle()));
255 if (plot->isDrawHorizontalGrid() || plot->isDrawHorizontalFineGrid()) {
257 _plstream->col0(lookUpColor(plot->getHorizontalGrid().getColor()));
260 static_cast<PLINT
>(plot->getHorizontalGrid().getLineStyle()));
262 _plstream->width(plot->getHorizontalGrid().getLineWidth());
264 if (plot->isDrawHorizontalFineGrid()) {
266 _plstream->box(
"gh", 0.0, 0.0,
"", 0.0, 0.0);
269 _plstream->box(
"g", 0.0, 0.0,
"", 0.0, 0.0);
273 if (plot->isDrawVerticalGrid() || plot->isDrawVerticalFineGrid()) {
275 _plstream->col0(lookUpColor(plot->getVerticalGrid().getColor()));
277 _plstream->lsty(
static_cast<PLINT
>(plot->getVerticalGrid().getLineStyle()));
279 _plstream->width(plot->getVerticalGrid().getLineWidth());
281 if (plot->isDrawVerticalFineGrid()) {
283 _plstream->box(
"", 0, 0,
"gh", 0, 0);
286 _plstream->box(
"", 0, 0,
"g", 0, 0);
290 for (
auto &p : *plot) {
294 case CanvasItem::Type::I_Point: {
296 Point *point =
dynamic_cast<Point *
>(p.get());
298 _plstream->col0(lookUpColor(point->getColor()));
300 _plstream->width(plot->getLineWidth());
302 _plstream->lsty(
static_cast<PLINT
>(plot->getLineStyle()));
304 _plstream->ssym(_defaultcharacterheight, point->getSize());
306 PLFLT px = point->getCoordinates().at(0);
308 PLFLT py = point->getCoordinates().at(1);
310 _plstream->sym(1, &px, &py, point->getSymbol());
312 case CanvasItem::Type::I_Line: {
314 Line *line =
dynamic_cast<Line *
>(p.get());
316 _plstream->col0(lookUpColor(line->getColor()));
318 _plstream->lsty(
static_cast<PLINT
>(line->getLineStyle()));
320 _plstream->width(line->getLineWidth());
323 line->getStartCoordinates().at(0), line->getStartCoordinates().at(1),
324 line->getEndCoordinates().at(0), line->getEndCoordinates().at(1));
326 case CanvasItem::Type::I_Curve: {
328 Curve *curve =
dynamic_cast<Curve *
>(p.get());
330 if (curve->getX().size() == 0) {
335 const PLFLT *x = curve->getX().data();
337 const PLFLT *xerr = curve->getXErr().data();
339 const PLFLT *y = curve->getY().data();
341 const PLFLT *yerr = curve->getYErr().data();
343 size_t n = curve->getX().size();
347 _plstream->lsty(
static_cast<PLINT
>(curve->getLineStyle()));
349 _plstream->width(curve->getLineWidth());
351 _plstream->col0(lookUpColor(curve->getColor()));
353 std::vector<PLFLT> min(n);
355 std::vector<PLFLT> max(n);
359 std::transform(curve->getX().begin(), curve->getX().end(),
360 curve->getXErr().begin(), min.begin(),
361 std::minus<PLFLT>());
363 std::transform(curve->getX().begin(), curve->getX().end(),
364 curve->getXErr().begin(), max.begin(),
367 _plstream->errx(n, x, min.data(), max.data());
371 std::transform(curve->getY().begin(), curve->getY().end(),
372 curve->getYErr().begin(), min.begin(),
373 std::minus<PLFLT>());
375 std::transform(curve->getY().begin(), curve->getY().end(),
376 curve->getYErr().begin(), max.begin(),
379 _plstream->erry(n, x, min.data(), max.data());
383 if (curve->isFill() && curve->getSymbol() == 0) {
385 _plstream->col0(lookUpColor(curve->getFillColor()));
387 if (y[0] == 0.0f && y[n - 1] == 0.0f) {
389 _plstream->fill(n, x, y);
392 std::vector<PLFLT> a(curve->getX().begin(), curve->getX().end());
394 std::vector<PLFLT> b(curve->getY().begin(), curve->getY().end());
396 if (y[n - 1] != 0.0f) {
398 a.push_back(x[n - 1]);
405 a.insert(a.begin(), x[0]);
407 b.insert(b.begin(), 0.0f);
410 _plstream->fill(a.size(), a.data(), b.data());
414 _plstream->col0(lookUpColor(curve->getColor()));
416 if (curve->getSymbol() == 0) {
418 _plstream->lsty(
static_cast<PLINT
>(curve->getLineStyle()));
420 _plstream->width(curve->getLineWidth());
422 _plstream->line(n, x, y);
425 _plstream->ssym(_defaultcharacterheight, curve->getSymbolSize());
427 _plstream->poin(n, x, y, curve->getSymbol());
430 case CanvasItem::Type::I_Text: {
432 Text *text =
dynamic_cast<Text *
>(p.get());
434 _plstream->col0(lookUpColor(text->getColor()));
436 _plstream->width(plot->getLineWidth());
438 _plstream->lsty(
static_cast<PLINT
>(plot->getLineStyle()));
440 _plstream->schr(_defaultcharacterheight, text->getSize());
442 if (text->getSystem() == Text::CoordinateSystem::DATA) {
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) {
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());
458 case CanvasItem::Type::I_Panels:
461 throw(
Exception(
"Unsupported graphics type"));
466 _plstream->col0(lookUpColor(plot->getColor()));
468 _plstream->lsty(
static_cast<PLINT
>(plot->getLineStyle()));
470 _plstream->width(plot->getLineWidth());
472 _plstream->schr(_defaultcharacterheight, plot->getFontSize());
474 _plstream->smaj(_defaultticklength, plot->getMajorTickLength());
476 _plstream->smin(_defaultticklength, plot->getMinorTickLength());
478 for (
auto &ax : plot->getXAxis()) {
480 _plstream->sxax(ax.getMaxDigits(), ax.getPrecision());
482 if (ax.getAxisOptString().find(
'n') != std::string::npos &&
483 ax.getStyle() == Axis::Style::WritePowerInFrame) {
487 order = floor(log10(xmax));
489 if (std::abs(order) > 1.0) {
491 std::ostringstream ostr;
493 ostr <<
"(x10#u" << order <<
"#d)";
495 _plstream->ptex(0.9 * (xmax - xmin) + xmin, 0.05 * (ymax - ymin) + ymin,
496 xmax, 0.0, 0.0, ostr.str().c_str());
498 xmin /= pow(10, order);
500 xmax /= pow(10, order);
504 if (ax.getStyle() != Axis::Style::Default) {
506 _plstream->wind(xmin, xmax, ymin, ymax);
509 if (ax.getTickFinder()) {
511 std::string axisoptstr(ax.getAxisOptString());
513 const std::string opts(
"mnst");
515 std::string::size_type i;
516 for (
const char opt : opts) {
518 if ((i = axisoptstr.find(opt)) != std::string::npos) {
520 axisoptstr.erase(i, 1);
524 _plstream->axes(0.0, 0.0, axisoptstr.data(), 0, 0,
"", 0, 0);
526 PLFLT p_xmin, p_xmax, p_ymin, p_ymax;
528 _plstream->gspa(p_xmin, p_xmax, p_ymin, p_ymax);
530 PLFLT dx = abs(xmax - xmin);
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)) *
540 PLINT nticks = ax.getTickFinder()(PL_X_AXIS,
sizeof(ticks), ticks, xmin,
543 for (PLINT i = 0; i < nticks; i++) {
545 _plstream->join(ticks[i], ymax + dy, ticks[i], ymax);
547 double pos = (ticks[i] - xmin) / dx;
551 pos = (xmin - ticks[i]) / dx;
556 if (ax.getLabelFormatter()) {
558 ax.getLabelFormatter()(PL_X_AXIS, ticks[i], label,
sizeof(label),
562 snprintf(label,
sizeof(label),
"%g", ticks[i]);
565 _plstream->mtex(
"t", 1.5, pos, 0.5, label);
569 _plstream->slabelfunc(ax.getLabelFormatter(),
nullptr);
571 _plstream->axes(0.0, 0.0, ax.getAxisOptString().data(), 0, 0,
"", 0, 0);
574 if (ax.getAxisOptString().find(
'm') != std::string::npos) {
576 _plstream->mtex(
"t", 3.5, 0.5, 0.5, ax.getTitle().data());
579 _plstream->mtex(
"b", 3.5, 0.5, 0.5, ax.getTitle().data());
583 for (
auto &ax : plot->getYAxis()) {
585 _plstream->syax(ax.getMaxDigits(), ax.getPrecision());
587 if (ax.getAxisOptString().find(
'n') != std::string::npos &&
588 ax.getStyle() == Axis::Style::WritePowerInFrame) {
592 order = floor(log10(ymax));
594 if (std::abs(order) > 1.0) {
596 std::ostringstream ostr;
598 ostr <<
"(x10#u" << order <<
"#d)";
600 _plstream->ptex(0.05 * (xmax - xmin) + xmin, 0.9 * (ymax - ymin) + ymin,
601 (xmax - xmin), 0.0, 0.0, ostr.str().c_str());
603 ymin /= pow(10, order);
605 ymax /= pow(10, order);
609 if (ax.getStyle() != Axis::Style::Default) {
611 _plstream->wind(xmin, xmax, ymin, ymax);
614 if (ax.getTickFinder()) {
616 std::string axisoptstr(ax.getAxisOptString());
618 const std::string opts(
"mnst");
620 std::string::size_type i;
621 for (
const char opt : opts) {
623 if ((i = axisoptstr.find(opt)) != std::string::npos) {
625 axisoptstr.erase(i, 1);
629 _plstream->axes(0.0, 0.0, axisoptstr.data(), 0, 0,
"", 0, 0);
631 PLFLT p_xmin, p_xmax, p_ymin, p_ymax;
633 _plstream->gspa(p_xmin, p_xmax, p_ymin, p_ymax);
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)) *
641 PLFLT dy = abs(ymax - ymin);
645 PLINT nticks = ax.getTickFinder()(PL_Y_AXIS,
sizeof(ticks), ticks, ymin,
648 for (PLINT i = 0; i < nticks; i++) {
650 _plstream->join(xmax + dx, ticks[i], xmax, ticks[i]);
652 double pos = (ticks[i] - ymin) / dy;
656 pos = (ymin - ticks[i]) / dy;
661 if (ax.getLabelFormatter()) {
663 ax.getLabelFormatter()(PL_Y_AXIS, ticks[i], label,
sizeof(label),
667 snprintf(label,
sizeof(label),
"%g", ticks[i]);
670 _plstream->mtex(
"l", 1.5, pos, 0.5, label);
674 _plstream->slabelfunc(ax.getLabelFormatter(),
nullptr);
676 _plstream->axes(0.0, 0.0,
"", 0, 0, ax.getAxisOptString().data(), 0, 0);
679 if (ax.getAxisOptString().find(
'm') != std::string::npos) {
681 _plstream->mtex(
"r", 3.5, 0.5, 0.5, ax.getTitle().data());
684 _plstream->mtex(
"l", 3.5, 0.5, 0.5, ax.getTitle().data());