#ifndef PLOTERRFUNC_H #define PLOTERRFUNC_H #include #include #include #include /** * helper class to plot error development stored within Statistics. * statistics are given as pointers and may be altered outside. * when plot() is called, everything is rebuild using the current contents * of each Statistics object */ class PlotErrFunc { /** group name and statistics together */ struct Entry { std::string name; const K::Statistics* stats; K::GnuplotPlotElementLines* line = nullptr; Entry(const std::string& name, const K::Statistics* stats) : name(name), stats(stats) {;} }; std::vector entries; K::Gnuplot gp; K::GnuplotPlot gplot; //std::vector colors = {"#000000", "#ff0000", "#00bb00", "#0000ff"}; std::vector colors = {"#000000", "#ff0000", "#00ff00", "#0000ff", "#00aaaa", "#aa00aa"}; struct Markers { bool median = false; bool quantil75 = false; } markers; std::string codeFile; struct Range { int fromPercent = 0; int toPercent = 0; int increment = 0; } range; public: /** empty ctor */ PlotErrFunc() { setYRange(0, 90, 5); } /** ctor with x-axis label */ PlotErrFunc(const std::string& xLabel, const std::string& yLabel) { gplot.getAxisX().setLabel(xLabel); gplot.getAxisY().setLabel(yLabel); setYRange(0, 90, 5); //gplot.getAxisX().setRange(K::GnuplotAxis::Range(0, K::GnuplotAxis::Range::AUTO)); gplot.getAxisX().setRange(K::GnuplotAxis::Range(K::GnuplotAxis::Range::AUTO, K::GnuplotAxis::Range::AUTO)); } const std::vector& getEntries() { return entries; } /** set the percentage range to show */ void setYRange(const int fromPercent, const int toPercent, const int increment = 5) { this->range.fromPercent = fromPercent; this->range.toPercent = toPercent; this->range.increment = increment; gplot.getAxisY().setRange(K::GnuplotAxis::Range(this->range.fromPercent, K::GnuplotAxis::Range::AUTO)); } /** add one curve. Statistics are allowed to be altered outside afterwards! */ void add(const std::string name, const K::Statistics* stats) { Entry entry(name, stats); entry.line = new K::GnuplotPlotElementLines(); entry.line->setTitle(name); entry.line->getStroke().setWidth(2); gplot.add(entry.line); entries.push_back(entry); } /** remove all previously added entries */ void clear() { for (Entry& e : entries) { gplot.remove(e.line); delete e.line; } entries.clear(); } K::Gnuplot& getGP() { return gp; } K::GnuplotPlot& getPlot() { return gplot; } /** write the gnuplot commands to file for later re-use */ void writePlotToFile(const std::string& file) { this->gp.writePlotToFile(file); } /** whether to show additional markers */ void showMarkers(const bool median, const bool quantil75) { this->markers.median = median; this->markers.quantil75 = quantil75; } /** plot all curves */ void plot() { gp << "set grid\n"; for (size_t i = 0; i < entries.size(); ++i) { const Entry& e = entries[i]; e.line->clear(); e.line->getStroke().getColor().setHexStr(colors[i]); // distancen between min and max const float minX = e.stats->getQuantile(0); const float range = e.stats->getQuantile(0.85) - minX; const float space = range * 0.07; // [from:stepsSize:to] //for (int j = this->range.fromPercent; j <= this->range.toPercent; j += this->range.increment) { for (int j = 0; j <= 100; j += this->range.increment) { const float y = j / 100.0f; const float x = e.stats->getQuantile(y); K::GnuplotPoint2 gp2(x, y*100); e.line->add(gp2); // show additional markers? int id = (i+1) * 100; if (j == 50 && markers.median) { gp << "set object " << id+1 << " circle at " << x << "," << j << " size screen 0.02,0.02\n"; gp << "set label " << id+2 << " at " << x+space << "," << j << " '" << x << "'\n"; gp << "set arrow " << id+3 << " from " << x << "," << 0 << " to " << x << "," << j << " nohead\n"; gp << "set arrow " << id+4 << " from " << 0 << "," << j << " to " << x << "," << j << " nohead\n"; } else if (j == 75 && markers.quantil75) { gp << "set object " << id+5 << " circle at " << x << "," << j << " size screen 0.02,0.02\n"; gp << "set label " << id+6 << " at " << x+space << "," << j << " '" << x << "'\n"; gp << "set arrow " << id+7 << " from " << x << "," << 0 << " to " << x << "," << j << " nohead\n"; gp << "set arrow " << id+8 << " from " << 0 << "," << j << " to " << x << "," << j << " nohead\n"; } } } // render gp.draw(gplot); gp.flush(); } }; #endif // PLOTERRFUNC_H