13using Point = std::pair<double, double>;
16 using std::vector<
Point>::vector;
17 void store(
double x,
double y) { emplace_back(x, y); }
22 explicit Gnuplot(
const std::string& args =
"") {
23 std::string cmd =
"gnuplot " + args;
24 pipe_ = popen(cmd.c_str(),
"w");
26 throw std::runtime_error(
"could not open gnuplot -- is it installed?");
36 fputs(cmd.c_str(), pipe_);
40 for (
const auto& [x, y] : data)
41 fprintf(pipe_,
"%.15g %.15g\n", x, y);
48 FILE* pipe_ =
nullptr;
53 gp <<
"set style line 1 lt 1 lw 2 pt 7 ps 0.8 lc rgb 'black'\n"
54 <<
"set style line 2 lt 2 lw 2 pt 5 ps 0.8 lc rgb 'black'\n"
55 <<
"set style line 3 lt 3 lw 2 pt 9 ps 0.8 lc rgb 'black'\n"
56 <<
"set style line 4 lt 4 lw 2 pt 13 ps 0.8 lc rgb 'black'\n"
57 <<
"set style line 5 lt 5 lw 2 pt 11 ps 0.8 lc rgb 'black'\n"
58 <<
"set style line 6 lt 6 lw 2 pt 15 ps 0.8 lc rgb 'black'\n"
59 <<
"set style line 100 lt 1 lw 0.5 lc rgb '#cccccc'\n"
60 <<
"set grid back ls 100\n"
61 <<
"set border 3 lw 1.5\n"
62 <<
"set tics nomirror\n"
63 <<
"set key top left Left reverse samplen 3 spacing 1.2\n"
64 <<
"set key box lt 1 lw 0.5\n";
68 gp <<
"set logscale xy\nset format x '10^{%L}'\nset format y '10^{%L}'\n";
71 gp <<
"set logscale x\nset format x '10^{%L}'\n";
73inline void save_png(
Gnuplot& gp,
const std::string& filename,
int w = 900,
int h = 600) {
74 gp <<
"set terminal pngcairo size " + std::to_string(w) +
"," + std::to_string(h)
75 +
" enhanced font 'Arial,11'\n"
76 <<
"set output '" + filename +
"'\n";
90 std::vector<double> data;
98 std::vector<SeriesEntry> series;
99 std::vector<HeatmapEntry> heatmaps;
100 std::string title_, xlabel_, ylabel_;
101 std::string xrange_, yrange_;
102 std::string palette_;
103 bool legend_ =
false;
110 std::vector<Panel> panels;
111 int mp_rows_ = 0, mp_cols_ = 0;
113 void reset() { *
this = State{}; }
116inline State& state() {
123inline void write_panel(FILE* pipe,
const Panel& p,
int block_offset) {
124 if (p.series.empty() && p.heatmaps.empty())
128 if (!p.title_.empty())
129 fprintf(pipe,
"set title '%s'\n", p.title_.c_str());
131 fputs(
"unset title\n", pipe);
132 if (!p.xlabel_.empty())
133 fprintf(pipe,
"set xlabel '%s'\n", p.xlabel_.c_str());
135 fputs(
"unset xlabel\n", pipe);
136 if (!p.ylabel_.empty())
137 fprintf(pipe,
"set ylabel '%s'\n", p.ylabel_.c_str());
139 fputs(
"unset ylabel\n", pipe);
141 if (!p.heatmaps.empty()) {
143 const auto& hm = p.heatmaps[0];
144 if (!p.palette_.empty())
145 fprintf(pipe,
"set palette %s\n", p.palette_.c_str());
147 fputs(
"set palette defined "
148 "(0 'white', 0.35 '#ffffb2', 0.65 '#fd8d3c', 1 '#bd0026')\n",
150 fprintf(pipe,
"set cbrange [%g:%g]\n", hm.vmin, hm.vmax);
151 fputs(
"set pm3d map\n", pipe);
152 fputs(
"set size ratio 1\n", pipe);
153 if (!p.xrange_.empty())
154 fprintf(pipe,
"set xrange %s\n", p.xrange_.c_str());
156 fputs(
"set xrange [*:*]\n", pipe);
157 if (!p.yrange_.empty())
158 fprintf(pipe,
"set yrange %s\n", p.yrange_.c_str());
160 fputs(
"set yrange [*:*]\n", pipe);
161 fputs(
"unset key\n", pipe);
162 fprintf(pipe,
"splot $d_%d with pm3d notitle\n", block_offset);
165 fputs(
"unset pm3d\n", pipe);
166 if (!p.xrange_.empty())
167 fprintf(pipe,
"set xrange %s\n", p.xrange_.c_str());
169 fputs(
"set xrange [*:*]\n", pipe);
170 if (!p.yrange_.empty())
171 fprintf(pipe,
"set yrange %s\n", p.yrange_.c_str());
173 fputs(
"set yrange [*:*]\n", pipe);
175 if (p.logx_ && p.logy_) {
176 fputs(
"set logscale xy\nset format x '10^{%L}'\nset format y "
179 }
else if (p.logx_) {
180 fputs(
"set logscale x\nset format x '10^{%L}'\n", pipe);
181 }
else if (p.logy_) {
182 fputs(
"set logscale y\nset format y '10^{%L}'\n", pipe);
184 fputs(
"unset logscale\n", pipe);
188 fputs(
"set key top right Left reverse samplen 3 spacing 1.2\n"
189 "set key box lt 1 lw 0.5\n",
192 fputs(
"unset key\n", pipe);
195 fputs(
"plot ", pipe);
196 for (std::size_t i = 0; i < p.series.size(); ++i) {
199 const auto&
e = p.series[i];
201 "$d_%d with %s ls %zu",
202 block_offset + (
int)i,
205 if (!
e.label.empty())
206 fprintf(pipe,
" title '%s'",
e.label.c_str());
208 fputs(
" notitle", pipe);
214inline void flush_to(FILE* pipe,
const std::string& outfile) {
218 std::vector<Panel> all = s.panels;
219 all.push_back(s.current);
221 bool multiplot = (s.mp_rows_ > 0);
224 if (outfile.empty()) {
225 int h = multiplot ? 300 * s.mp_rows_ : 600;
226 fprintf(pipe,
"set terminal qt size 900,%d\n", h);
228 std::string ext = outfile.size() > 4 ? outfile.substr(outfile.size() - 4) :
"";
230 double h = multiplot ? 3.0 * s.mp_rows_ : 4.0;
231 fprintf(pipe,
"set terminal pdfcairo size 6,%.0f font 'Arial,11'\n", h);
233 int h = multiplot ? 350 * s.mp_rows_ : 600;
235 "set terminal pngcairo size 900,%d enhanced font 'Arial,11'\n",
238 fprintf(pipe,
"set output '%s'\n", outfile.c_str());
242 fputs(
"set style line 1 lt 1 lw 2 pt 7 ps 0.7 lc rgb '#2c3e50'\n", pipe);
243 fputs(
"set style line 2 lt 2 lw 2 pt 5 ps 0.7 lc rgb '#c0392b'\n", pipe);
244 fputs(
"set style line 3 lt 3 lw 2 pt 9 ps 0.7 lc rgb '#2980b9'\n", pipe);
245 fputs(
"set style line 4 lt 4 lw 2 pt 13 ps 0.7 lc rgb '#27ae60'\n", pipe);
246 fputs(
"set style line 5 lt 5 lw 2 pt 11 ps 0.7 lc rgb '#8e44ad'\n", pipe);
247 fputs(
"set style line 100 lt 1 lw 0.5 lc rgb '#cccccc'\n", pipe);
248 fputs(
"set grid back ls 100\n", pipe);
249 fputs(
"set border 3 lw 1.5\n", pipe);
250 fputs(
"set tics nomirror\n", pipe);
255 for (
const auto& p : all) {
256 for (
const auto&
e : p.series) {
257 fprintf(pipe,
"$d_%d << EOD\n", block++);
258 for (
const auto& [x, y] :
e.data)
259 fprintf(pipe,
"%.15g %.15g\n", x, y);
260 fputs(
"EOD\n", pipe);
262 for (
const auto& hm : p.heatmaps) {
263 fprintf(pipe,
"$d_%d << EOD\n", block++);
264 for (
int i = 0; i < hm.N; ++i) {
265 double xi = (i + 1) * hm.h;
266 for (
int j = 0; j < hm.N; ++j)
271 hm.data[
static_cast<std::size_t
>(i) * hm.N + j]);
274 fputs(
"EOD\n", pipe);
280 "set multiplot layout %d,%d spacing 0.08,0.12\n",
284 for (
const auto& p : all) {
285 write_panel(pipe, p, off);
286 off += (int)p.series.size() + (int)p.heatmaps.size();
288 fputs(
"unset multiplot\n", pipe);
290 write_panel(pipe, all.back(), 0);
301inline void plot(
const Series& data,
302 const std::string& label =
"",
303 const std::string& style =
"lines") {
304 detail::state().current.series.push_back({data, label, style});
308inline void plot(
const std::vector<double>& x,
309 const std::vector<double>& y,
310 const std::string& label =
"",
311 const std::string& style =
"lines") {
314 for (std::size_t i = 0; i < x.size() && i < y.size(); ++i)
315 s.emplace_back(x[i], y[i]);
316 detail::state().current.series.push_back({std::move(s), label, style});
321inline void title(
const std::string& t) {
322 detail::state().current.title_ = t;
324inline void xlabel(
const std::string& l) {
325 detail::state().current.xlabel_ = l;
327inline void ylabel(
const std::string& l) {
328 detail::state().current.ylabel_ = l;
332inline void xlim(
double lo,
double hi) {
333 detail::state().current.xrange_ =
334 "[" + std::to_string(lo) +
":" + std::to_string(hi) +
"]";
337inline void ylim(
double lo,
double hi) {
338 detail::state().current.yrange_ =
339 "[" + std::to_string(lo) +
":" + std::to_string(hi) +
"]";
343inline void legend() {
344 detail::state().current.legend_ =
true;
348inline void loglog() {
349 detail::state().current.logx_ = detail::state().current.logy_ =
true;
352inline void semilogy() {
353 detail::state().current.logy_ =
true;
356inline void semilogx() {
357 detail::state().current.logx_ =
true;
370template<
typename Container>
371inline void heatmap(
const Container& u,
376 detail::HeatmapEntry
e;
377 e.data.assign(u.data(), u.data() + u.size());
382 detail::state().current.heatmaps.push_back(std::move(
e));
388inline void colormap(
const std::string& palette) {
389 detail::state().current.palette_ = palette;
395inline void subplot(
int rows,
int cols = 1) {
396 detail::state().reset();
397 detail::state().mp_rows_ = rows;
398 detail::state().mp_cols_ = cols;
403 detail::state().panels.push_back(detail::state().current);
404 detail::state().current = detail::Panel{};
412 FILE* pipe = popen(
"gnuplot",
"w");
414 throw std::runtime_error(
"could not open gnuplot -- is it installed?");
415 detail::flush_to(pipe,
"");
416 fputs(
"pause mouse close\n", pipe);
419 detail::state().reset();
424inline void savefig(
const std::string& filename) {
425 FILE* pipe = popen(
"gnuplot",
"w");
427 throw std::runtime_error(
"could not open gnuplot -- is it installed?");
428 detail::flush_to(pipe, filename);
431 detail::state().reset();
436 detail::state().reset();
Gnuplot(const std::string &args="")
Gnuplot & operator=(const Gnuplot &)=delete
void send1d(const Series &data)
Gnuplot(const Gnuplot &)=delete
Gnuplot & operator<<(const std::string &cmd)
void heatmap(const ScalarField2D &g, double vmin=0.0, double vmax=1.0)
void set_loglog(Gnuplot &gp)
void apply_siam_style(Gnuplot &gp)
Apply SIAM-style theme to a raw Gnuplot pipe.
void save_png(Gnuplot &gp, const std::string &filename, int w=900, int h=600)
std::pair< double, double > Point
void set_logx(Gnuplot &gp)
void store(double x, double y)