39using Point = std::pair<double, double>;
43 using std::vector<
Point>::vector;
45 void store(
double x,
double y) {
56 explicit Gnuplot(
const std::string& args =
"") {
57 std::string cmd =
"gnuplot " + args;
58 pipe_ = popen(cmd.c_str(),
"w");
60 throw std::runtime_error(
61 "could not open gnuplot -- is it installed?");
71 fputs(cmd.c_str(), pipe_);
75 for (
const auto& [x, y] : data)
76 fprintf(pipe_,
"%.15g %.15g\n", x, y);
85 FILE* pipe_ =
nullptr;
90 gp <<
"set style line 1 lt 1 lw 2 pt 7 ps 0.8 lc rgb 'black'\n"
91 <<
"set style line 2 lt 2 lw 2 pt 5 ps 0.8 lc rgb 'black'\n"
92 <<
"set style line 3 lt 3 lw 2 pt 9 ps 0.8 lc rgb 'black'\n"
93 <<
"set style line 4 lt 4 lw 2 pt 13 ps 0.8 lc rgb 'black'\n"
94 <<
"set style line 5 lt 5 lw 2 pt 11 ps 0.8 lc rgb 'black'\n"
95 <<
"set style line 6 lt 6 lw 2 pt 15 ps 0.8 lc rgb 'black'\n"
96 <<
"set style line 100 lt 1 lw 0.5 lc rgb '#cccccc'\n"
97 <<
"set grid back ls 100\n"
98 <<
"set border 3 lw 1.5\n"
99 <<
"set tics nomirror\n"
100 <<
"set key top left Left reverse samplen 3 spacing 1.2\n"
101 <<
"set key box lt 1 lw 0.5\n";
105 gp <<
"set logscale xy\nset format x '10^{%L}'\nset format y '10^{%L}'\n";
108 gp <<
"set logscale x\nset format x '10^{%L}'\n";
111 const std::string& filename,
114 gp <<
"set terminal pngcairo size " + std::to_string(w) +
","
115 + std::to_string(h) +
" enhanced font 'Arial,11'\n"
116 <<
"set output '" + filename +
"'\n";
130 std::vector<double> data;
138 std::vector<SeriesEntry> series;
139 std::vector<HeatmapEntry> heatmaps;
140 std::string title_, xlabel_, ylabel_;
141 std::string xrange_, yrange_;
142 std::string palette_;
143 bool legend_ =
false;
150 std::vector<Panel> panels;
151 int mp_rows_ = 0, mp_cols_ = 0;
158inline State& state() {
165inline void write_panel(FILE* pipe,
const Panel& p,
int block_offset) {
166 if (p.series.empty() && p.heatmaps.empty())
170 if (!p.title_.empty())
171 fprintf(pipe,
"set title '%s'\n", p.title_.c_str());
173 fputs(
"unset title\n", pipe);
174 if (!p.xlabel_.empty())
175 fprintf(pipe,
"set xlabel '%s'\n", p.xlabel_.c_str());
177 fputs(
"unset xlabel\n", pipe);
178 if (!p.ylabel_.empty())
179 fprintf(pipe,
"set ylabel '%s'\n", p.ylabel_.c_str());
181 fputs(
"unset ylabel\n", pipe);
183 if (!p.heatmaps.empty()) {
185 const auto& hm = p.heatmaps[0];
186 if (!p.palette_.empty())
187 fprintf(pipe,
"set palette %s\n", p.palette_.c_str());
189 fputs(
"set palette defined "
190 "(0 'white', 0.35 '#ffffb2', 0.65 '#fd8d3c', 1 '#bd0026')\n",
192 fprintf(pipe,
"set cbrange [%g:%g]\n", hm.vmin, hm.vmax);
193 fputs(
"set pm3d map\n", pipe);
194 fputs(
"set size ratio 1\n", pipe);
195 if (!p.xrange_.empty())
196 fprintf(pipe,
"set xrange %s\n", p.xrange_.c_str());
198 fputs(
"set xrange [*:*]\n", pipe);
199 if (!p.yrange_.empty())
200 fprintf(pipe,
"set yrange %s\n", p.yrange_.c_str());
202 fputs(
"set yrange [*:*]\n", pipe);
203 fputs(
"unset key\n", pipe);
204 fprintf(pipe,
"splot $d_%d with pm3d notitle\n", block_offset);
207 fputs(
"unset pm3d\n", pipe);
208 if (!p.xrange_.empty())
209 fprintf(pipe,
"set xrange %s\n", p.xrange_.c_str());
211 fputs(
"set xrange [*:*]\n", pipe);
212 if (!p.yrange_.empty())
213 fprintf(pipe,
"set yrange %s\n", p.yrange_.c_str());
215 fputs(
"set yrange [*:*]\n", pipe);
217 if (p.logx_ && p.logy_) {
218 fputs(
"set logscale xy\nset format x '10^{%L}'\nset format y '10^{%L}'\n",
220 }
else if (p.logx_) {
221 fputs(
"set logscale x\nset format x '10^{%L}'\n", pipe);
222 }
else if (p.logy_) {
223 fputs(
"set logscale y\nset format y '10^{%L}'\n", pipe);
225 fputs(
"unset logscale\n", pipe);
229 fputs(
"set key top right Left reverse samplen 3 spacing 1.2\n"
230 "set key box lt 1 lw 0.5\n", pipe);
232 fputs(
"unset key\n", pipe);
235 fputs(
"plot ", pipe);
236 for (std::size_t i = 0; i < p.series.size(); ++i) {
239 const auto&
e = p.series[i];
241 "$d_%d with %s ls %zu",
242 block_offset + (
int)i,
245 if (!
e.label.empty())
246 fprintf(pipe,
" title '%s'",
e.label.c_str());
248 fputs(
" notitle", pipe);
254inline void flush_to(FILE* pipe,
const std::string& outfile) {
258 std::vector<Panel> all = s.panels;
259 all.push_back(s.current);
261 bool multiplot = (s.mp_rows_ > 0);
264 if (outfile.empty()) {
265 int h = multiplot ? 300 * s.mp_rows_ : 600;
266 fprintf(pipe,
"set terminal qt size 900,%d\n", h);
268 std::string ext = outfile.size() > 4
269 ? outfile.substr(outfile.size() - 4)
272 double h = multiplot ? 3.0 * s.mp_rows_ : 4.0;
274 "set terminal pdfcairo size 6,%.0f font 'Arial,11'\n",
277 int h = multiplot ? 350 * s.mp_rows_ : 600;
280 "set terminal pngcairo size 900,%d enhanced font 'Arial,11'\n",
283 fprintf(pipe,
"set output '%s'\n", outfile.c_str());
287 fputs(
"set style line 1 lt 1 lw 2 pt 7 ps 0.7 lc rgb '#2c3e50'\n", pipe);
288 fputs(
"set style line 2 lt 2 lw 2 pt 5 ps 0.7 lc rgb '#c0392b'\n", pipe);
289 fputs(
"set style line 3 lt 3 lw 2 pt 9 ps 0.7 lc rgb '#2980b9'\n", pipe);
290 fputs(
"set style line 4 lt 4 lw 2 pt 13 ps 0.7 lc rgb '#27ae60'\n", pipe);
291 fputs(
"set style line 5 lt 5 lw 2 pt 11 ps 0.7 lc rgb '#8e44ad'\n", pipe);
292 fputs(
"set style line 100 lt 1 lw 0.5 lc rgb '#cccccc'\n", pipe);
293 fputs(
"set grid back ls 100\n", pipe);
294 fputs(
"set border 3 lw 1.5\n", pipe);
295 fputs(
"set tics nomirror\n", pipe);
300 for (
const auto& p : all) {
301 for (
const auto&
e : p.series) {
302 fprintf(pipe,
"$d_%d << EOD\n", block++);
303 for (
const auto& [x, y] :
e.data)
304 fprintf(pipe,
"%.15g %.15g\n", x, y);
305 fputs(
"EOD\n", pipe);
307 for (
const auto& hm : p.heatmaps) {
308 fprintf(pipe,
"$d_%d << EOD\n", block++);
309 for (
int i = 0; i < hm.N; ++i) {
310 double xi = (i + 1) * hm.h;
311 for (
int j = 0; j < hm.N; ++j)
312 fprintf(pipe,
"%.8g %.8g %.8g\n",
314 hm.data[
static_cast<std::size_t
>(i) * hm.N + j]);
317 fputs(
"EOD\n", pipe);
323 "set multiplot layout %d,%d spacing 0.08,0.12\n",
327 for (
const auto& p : all) {
328 write_panel(pipe, p, off);
329 off += (int)p.series.size() + (int)p.heatmaps.size();
331 fputs(
"unset multiplot\n", pipe);
333 write_panel(pipe, all.back(), 0);
344inline void plot(
const Series& data,
345 const std::string& label =
"",
346 const std::string& style =
"lines") {
347 detail::state().current.series.push_back({data, label, style});
351inline void plot(
const std::vector<double>& x,
352 const std::vector<double>& y,
353 const std::string& label =
"",
354 const std::string& style =
"lines") {
357 for (std::size_t i = 0; i < x.size() && i < y.size(); ++i)
358 s.emplace_back(x[i], y[i]);
359 detail::state().current.series.push_back({std::move(s), label, style});
364inline void title(
const std::string& t) {
365 detail::state().current.title_ = t;
367inline void xlabel(
const std::string& l) {
368 detail::state().current.xlabel_ = l;
370inline void ylabel(
const std::string& l) {
371 detail::state().current.ylabel_ = l;
375inline void xlim(
double lo,
double hi) {
376 detail::state().current.xrange_ =
"[" + std::to_string(lo) +
":"
377 + std::to_string(hi) +
"]";
380inline void ylim(
double lo,
double hi) {
381 detail::state().current.yrange_ =
"[" + std::to_string(lo) +
":"
382 + std::to_string(hi) +
"]";
386inline void legend() {
387 detail::state().current.legend_ =
true;
391inline void loglog() {
392 detail::state().current.logx_ = detail::state().current.logy_ =
true;
395inline void semilogy() {
396 detail::state().current.logy_ =
true;
399inline void semilogx() {
400 detail::state().current.logx_ =
true;
421template<
typename Container>
422inline void heatmap(
const Container& u,
427 detail::HeatmapEntry
e;
428 e.data.assign(u.data(), u.data() + u.size());
429 e.N = N;
e.h = h;
e.vmin = vmin;
e.vmax = vmax;
430 detail::state().current.heatmaps.push_back(std::move(
e));
436inline void colormap(
const std::string& palette) {
437 detail::state().current.palette_ = palette;
451inline void subplot(
int rows,
int cols = 1) {
452 detail::state().reset();
453 detail::state().mp_rows_ = rows;
454 detail::state().mp_cols_ = cols;
459 detail::state().panels.push_back(detail::state().current);
460 detail::state().current = detail::Panel{};
468 FILE* pipe = popen(
"gnuplot",
"w");
470 throw std::runtime_error(
"could not open gnuplot -- is it installed?");
471 detail::flush_to(pipe,
"");
472 fputs(
"pause mouse close\n", pipe);
475 detail::state().reset();
480inline void savefig(
const std::string& filename) {
481 FILE* pipe = popen(
"gnuplot",
"w");
483 throw std::runtime_error(
"could not open gnuplot -- is it installed?");
484 detail::flush_to(pipe, filename);
487 detail::state().reset();
492 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)
Heatmap overload for ScalarField2D – no need to pass N or h separately.
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
(x, y) data point.
void set_logx(Gnuplot &gp)
void store(double x, double y)
Append a point to the series.