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());