119 std::vector<double> data;
127 std::vector<SeriesEntry> series;
128 std::vector<HeatmapEntry> heatmaps;
129 std::string title_, xlabel_, ylabel_;
130 std::string xrange_, yrange_;
131 std::string palette_;
132 bool legend_ =
false;
139 std::vector<Panel> panels;
140 int mp_rows_ = 0, mp_cols_ = 0;
142 void reset() { *
this = State{}; }
145inline State& state() {
152inline void write_panel(FILE* pipe,
const Panel& p,
int block_offset) {
153 if (p.series.empty() && p.heatmaps.empty())
157 if (!p.title_.empty())
158 fprintf(pipe,
"set title '%s'\n", p.title_.c_str());
160 fputs(
"unset title\n", pipe);
161 if (!p.xlabel_.empty())
162 fprintf(pipe,
"set xlabel '%s'\n", p.xlabel_.c_str());
164 fputs(
"unset xlabel\n", pipe);
165 if (!p.ylabel_.empty())
166 fprintf(pipe,
"set ylabel '%s'\n", p.ylabel_.c_str());
168 fputs(
"unset ylabel\n", pipe);
170 if (!p.heatmaps.empty()) {
172 const auto& hm = p.heatmaps[0];
173 if (!p.palette_.empty())
174 fprintf(pipe,
"set palette %s\n", p.palette_.c_str());
176 fputs(
"set palette defined "
177 "(0 'white', 0.35 '#ffffb2', 0.65 '#fd8d3c', 1 '#bd0026')\n",
179 fprintf(pipe,
"set cbrange [%g:%g]\n", hm.vmin, hm.vmax);
180 fputs(
"set pm3d map\n", pipe);
181 fputs(
"set size ratio 1\n", pipe);
182 if (!p.xrange_.empty())
183 fprintf(pipe,
"set xrange %s\n", p.xrange_.c_str());
185 fputs(
"set xrange [*:*]\n", pipe);
186 if (!p.yrange_.empty())
187 fprintf(pipe,
"set yrange %s\n", p.yrange_.c_str());
189 fputs(
"set yrange [*:*]\n", pipe);
190 fputs(
"unset key\n", pipe);
191 fprintf(pipe,
"splot $d_%d with pm3d notitle\n", block_offset);
194 fputs(
"unset pm3d\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);
204 if (p.logx_ && p.logy_) {
205 fputs(
"set logscale xy\nset format x '10^{%L}'\nset format y "
208 }
else if (p.logx_) {
209 fputs(
"set logscale x\nset format x '10^{%L}'\n", pipe);
210 }
else if (p.logy_) {
211 fputs(
"set logscale y\nset format y '10^{%L}'\n", pipe);
213 fputs(
"unset logscale\n", pipe);
217 fputs(
"set key top right Left reverse samplen 3 spacing 1.2\n"
218 "set key box lt 1 lw 0.5\n",
221 fputs(
"unset key\n", pipe);
224 fputs(
"plot ", pipe);
225 for (std::size_t i = 0; i < p.series.size(); ++i) {
228 const auto&
e = p.series[i];
230 "$d_%d with %s ls %zu",
231 block_offset + (
int)i,
234 if (!
e.label.empty())
235 fprintf(pipe,
" title '%s'",
e.label.c_str());
237 fputs(
" notitle", pipe);
243inline void flush_to(FILE* pipe,
const std::string& outfile) {
247 std::vector<Panel> all = s.panels;
248 all.push_back(s.current);
250 bool multiplot = (s.mp_rows_ > 0);
253 if (outfile.empty()) {
254 int h = multiplot ? 300 * s.mp_rows_ : 600;
255 fprintf(pipe,
"set terminal qt size 900,%d\n", h);
257 std::string ext = outfile.size() > 4 ? outfile.substr(outfile.size() - 4) :
"";
259 double h = multiplot ? 3.0 * s.mp_rows_ : 4.0;
260 fprintf(pipe,
"set terminal pdfcairo size 6,%.0f font 'Arial,11'\n", h);
262 int h = multiplot ? 350 * s.mp_rows_ : 600;
263 fprintf(pipe,
"set terminal pngcairo size 900,%d enhanced font 'Arial,11'\n", h);
265 fprintf(pipe,
"set output '%s'\n", outfile.c_str());
269 fputs(
"set style line 1 lt 1 lw 2 pt 7 ps 0.7 lc rgb '#2c3e50'\n", pipe);
270 fputs(
"set style line 2 lt 2 lw 2 pt 5 ps 0.7 lc rgb '#c0392b'\n", pipe);
271 fputs(
"set style line 3 lt 3 lw 2 pt 9 ps 0.7 lc rgb '#2980b9'\n", pipe);
272 fputs(
"set style line 4 lt 4 lw 2 pt 13 ps 0.7 lc rgb '#27ae60'\n", pipe);
273 fputs(
"set style line 5 lt 5 lw 2 pt 11 ps 0.7 lc rgb '#8e44ad'\n", pipe);
274 fputs(
"set style line 100 lt 1 lw 0.5 lc rgb '#cccccc'\n", pipe);
275 fputs(
"set grid back ls 100\n", pipe);
276 fputs(
"set border 3 lw 1.5\n", pipe);
277 fputs(
"set tics nomirror\n", pipe);
282 for (
const auto& p : all) {
283 for (
const auto&
e : p.series) {
284 fprintf(pipe,
"$d_%d << EOD\n", block++);
285 for (
const auto& [x, y] :
e.data)
286 fprintf(pipe,
"%.15g %.15g\n", x, y);
287 fputs(
"EOD\n", pipe);
289 for (
const auto& hm : p.heatmaps) {
290 fprintf(pipe,
"$d_%d << EOD\n", block++);
291 for (
int i = 0; i < hm.N; ++i) {
292 double xi = (i + 1) * hm.h;
293 for (
int j = 0; j < hm.N; ++j)
298 hm.data[
static_cast<std::size_t
>(i) * hm.N + j]);
301 fputs(
"EOD\n", pipe);
307 "set multiplot layout %d,%d spacing 0.08,0.12\n",
311 for (
const auto& p : all) {
312 write_panel(pipe, p, off);
313 off += (int)p.series.size() + (int)p.heatmaps.size();
315 fputs(
"unset multiplot\n", pipe);
317 write_panel(pipe, all.back(), 0);
328inline void plot(
const Series& data,
329 const std::string& label =
"",
330 const std::string& style =
"lines") {
331 detail::state().current.series.push_back({data, label, style});
335inline void plot(
const std::vector<double>& x,
336 const std::vector<double>& y,
337 const std::string& label =
"",
338 const std::string& style =
"lines") {
341 for (std::size_t i = 0; i < x.size() && i < y.size(); ++i)
342 s.emplace_back(x[i], y[i]);
343 detail::state().current.series.push_back({std::move(s), label, style});
348inline void title(
const std::string& t) {
349 detail::state().current.title_ = t;
351inline void xlabel(
const std::string& l) {
352 detail::state().current.xlabel_ = l;
354inline void ylabel(
const std::string& l) {
355 detail::state().current.ylabel_ = l;
359inline void xlim(
double lo,
double hi) {
360 detail::state().current.xrange_ =
361 "[" + std::to_string(lo) +
":" + std::to_string(hi) +
"]";
364inline void ylim(
double lo,
double hi) {
365 detail::state().current.yrange_ =
366 "[" + std::to_string(lo) +
":" + std::to_string(hi) +
"]";
370inline void legend() {
371 detail::state().current.legend_ =
true;
375inline void loglog() {
376 detail::state().current.logx_ = detail::state().current.logy_ =
true;
379inline void semilogy() {
380 detail::state().current.logy_ =
true;
383inline void semilogx() {
384 detail::state().current.logx_ =
true;
397template<
typename Container>
398inline void heatmap(
const Container& u,
403 detail::HeatmapEntry
e;
404 e.data.assign(u.data(), u.data() + u.size());
409 detail::state().current.heatmaps.push_back(std::move(
e));
412inline void heatmap(
const ScalarField2D& g,
double vmin = 0.0,
double vmax = 1.0) {
413 heatmap<ScalarField2D>(g, g.N(), g.h(), vmin, vmax);
419inline void colormap(
const std::string& palette) {
420 detail::state().current.palette_ = palette;
426inline void subplot(
int rows,
int cols = 1) {
427 detail::state().reset();
428 detail::state().mp_rows_ = rows;
429 detail::state().mp_cols_ = cols;
434 detail::state().panels.push_back(detail::state().current);
435 detail::state().current = detail::Panel{};
443 FILE* pipe = popen(
"gnuplot",
"w");
445 throw std::runtime_error(
"could not open gnuplot -- is it installed?");
446 detail::flush_to(pipe,
"");
447 fputs(
"pause mouse close\n", pipe);
450 detail::state().reset();
455inline void savefig(
const std::string& filename) {
456 FILE* pipe = popen(
"gnuplot",
"w");
458 throw std::runtime_error(
"could not open gnuplot -- is it installed?");
459 detail::flush_to(pipe, filename);
462 detail::state().reset();
467 detail::state().reset();
Series col_slice(const Vector &u, int N, double h, int col)
Extract one column as plot data.
Series row_slice(const Vector &u, int N, double h, int row)
Extract one row as plot data.