Skip to content

Repository files navigation

#+OPTIONS: H:3 toc:t \n:nil ::t |:t ^:{} -:t f:t *:t tex:t d:t tags:not-in-toc
#+BLOG: wordpress
#+POSTID: 931
#+DATE: [2019-12-06 Fri 19:40]
#+TITLE: Easy Creation of GnuPlot Scripts from C++
#+TAGS: Cpp, GnuPlot

# trick from https://github.com/rexim/org-cliplink/

[[https://travis-ci.org/vincent-picaud/GnuPlotScript][file:https://travis-ci.org/vincent-picaud/GnuPlotScripting.svg?branch=master]]

* Table of contents                                            :TOC:noexport:
- [[#what-is-it][What is it?]]
  - [[#news][News]]
  - [[#contributors][Contributors]]
- [[#installation][Installation]]
- [[#examples][Examples]]
  - [[#plot-and-fit-data][Plot and fit data]]
  - [[#ascii-matrix-data][Ascii matrix data]]
  - [[#new-matrix-data][(NEW) Matrix data]]
  - [[#histogram][Histogram]]
  - [[#graph][Graph]]
  - [[#density-plot][Density plot]]
  - [[#pipe-example][Pipe example]]
  - [[#supported-export-formats][Supported export formats]]
  - [[#global-config-demonstration][Global config demonstration]]
- [[#documentation][Documentation]]
  - [[#data-classes][Data classes]]
  - [[#script-classes][Script classes]]
  - [[#global_config-class][Global_Config class]]
- [[#references][References]]
- [[#faq][FAQ]]

* What is it?

A simple C++17 library that allows easy creation and execution of
gnuplot scripts. These scripts will embed their data and can be
replayed/modified later. 

The library depends on [[https://github.com/fmtlib/fmt][{fmt} library]].

I use it when I want to plot some data with a minimal effort when I
develop stuff in C++.

For the moment the library is only tested under Linux (it should also
works under Windows but I have not checked yet).

# Feel free to use it: [[https://github.com/vincent-picaud/GnuPlotScripting][GitHub, GnuPlotScripting]].


** News
   
   - [2020-10-20 Tue] \\
     - *Version v1.1.0*!
     - some compilation warning fixes
     - a new Data_Array_2 class
     
   - [2020-01-18 Sat 09:17] \\
     - *Version v1.0.0 release!*
     - More documentation
     - Fix a bug in export_as(...,path/output_filename): the path was ignored
   - [2020-01-09 Thu 22:46] \\
     - Added =Data_Supervised= class and associated example (see *Density plot*
       in *Example* section)
     - Code cleaning (no breaking change!)
     - Tagged as v0.2.0 (v1.0.0 gets closer :-) )
   - [2019-12-09 Mon 18:34] \\
     Added SVG, TGIF, PNGCairo and PDFCairo export formats.
     Tagged as v0.0.3.

   - [2019-12-08 Sun 20:21] \\
     Some minor fixes + doc concerning Global_Config logger methods.
     Tagged as v0.0.2 

   - [2019-12-08 Sun 11:21] \\
     Initial version.
     Tagged as v0.0.1 

** Contributors 

  - [[https://github.com/Gjacquenot][Guillaume Jacquenot]]: Travis CI

* Installation 

The library currently uses the [[https://mesonbuild.com/][meson]] build system.

If you are not familiar with meson, the compilation procedure is as follows:

#+BEGIN_SRC sh :eval never
git clone https://github.com/vincent-picaud/GnuPlotScripting.git
cd GnuPlotScripting/
meson build
cd build
ninja test
#+END_SRC 

Examples can then be found in the =build/examples/= directory.

If you want to install the package:
#+BEGIN_SRC sh :eval never
ninja install
#+END_SRC 

Note that you can define custom installation path, at the beginning of
the compilation procedure, use:

#+BEGIN_SRC sh :eval never
meson --prefix=/install_path/ build
#+END_SRC 

instead of =meson build=.

* Examples 

** Plot and fit data 

#+BEGIN_SRC sh :wrap "src cpp :eval never" :results output :exports results
cat $(pwd)/examples/plot.cpp
#+END_SRC

#+RESULTS:
#+BEGIN_src cpp :eval never
#include "GnuPlotScripting/GnuPlotScripting.hpp"

#include <iostream>
#include <vector>

using namespace GnuPlotScripting;

// From: https://www.cs.hmc.edu/~vrable/gnuplot/using-gnuplot.html

int
main()
{
  std::vector<double> time, angle, stdvar;

  time = {0.0,  1.0,  2.1,  3.1,  4.2,  5.2,  6.2,  7.2,  8.2,  9.1,  10.0, 11.0, 12.0,
          12.9, 13.8, 14.9, 15.9, 17.0, 17.9, 18.9, 20.0, 21.0, 22.0, 23.0, 24.0, 25.0,
          26.0, 27.0, 28.0, 29.0, 30.0, 31.0, 32.0, 32.9, 33.8, 34.7, 35.7, 36.6, 37.7};

  angle = {-14.7, 8.6,  28.8, 46.7, 47.4, 36.5, 37.0, 5.1,  -11.2, -22.4, -35.5, -33.6, -21.1,
           -15.0, -1.6, 19.5, 27.5, 32.6, 27.5, 20.2, 13.8, -1.3,  -24.5, -25.0, -25.0, -20.2,
           -9.9,  5.8,  14.7, 21.8, 29.8, 21.4, 24.6, 25.8, 0.6,   -16.6, -24.0, -24.6, -19.8};

  stdvar = {3.6, 3.6, 3.0, 3.4, 3.5, 3.4, 10.3, 3.4,  3.4, 3.5, 3.6, 3.9, 3.9,
            4.2, 2.7, 3.2, 2.8, 3.5, 2.7, 3.3,  3.4,  4.2, 6.7, 3.3, 3.1, 3.6,
            3.2, 3.2, 3.0, 3.5, 2.7, 4.1, 2.7,  12.0, 2.9, 3.2, 3.7, 3.8, 3.5};

  Script_File script("plot.gp");

  Data_Vector data(
      time, angle, stdvar);  // <- you can stack as many vector/valarray etc.. as you want
                             //    only size() and operator[] are required.

  script.free_form("set title 'Cavendish Data'");
  script.free_form("set xlabel 'Time (s)'");
  script.free_form("set ylabel 'Angle (mrad)'");
  script.free_form("set grid");
  script.free_form("plot {} with yerrorbars notitle", data);
  script.free_form("replot {} u 1:2 with lines title '{}'", data, "raw data");
  script.free_form("theta(t) = theta0 + a * exp(-t / tau) * sin(2 * pi * t / T + phi)");
  script.free_form("fit theta(x) {} using 1:2:3 via a, tau, phi, T, theta0", data);
  script.free_form("replot theta(x) lw {} lc {} title 'best-fit curve'", 2, 4);
  script.export_as(PNG(), "plot");
}
#+END_src

It generates this figure:

[[file:figures/plot.png]]

*Note:* the generated =plot.gp= gnutplot script embeds the data and you
 can replay it whenever you want:
#+BEGIN_SRC sh :eval never
gnuplot plot.pg -
#+END_SRC 

** Ascii matrix data 

#+BEGIN_SRC sh :wrap "src cpp :eval never" :results output :exports results
cat $(pwd)/examples/matrix.cpp
#+END_SRC

#+RESULTS:
#+BEGIN_src cpp :eval never
#include "GnuPlotScripting/GnuPlotScripting.hpp"

#include <iostream>

using namespace GnuPlotScripting;

// Example from: https://stackoverflow.com/a/27049991/2001017
// Also see: https://stackoverflow.com/q/32458753/2001017
//
int
main()
{
  Data_Ascii data(
      "0.00 0.65 0.65 0.25\n"
      "0.25 0.00 0.75 0.25\n"
      "0.50 0.60 0.00 0.25\n"
      "0.75 0.25 0.10 0.00\n");

  Script_File script("matrix.gp");

  script.free_form("set autoscale fix");
  script.free_form("set cbrange [-1:1]");
  script.free_form("unset colorbox");
  script.free_form("unset key");
  script.free_form(
      "plot {} matrix using 1:2:3 with image, '' matrix using "
      "1:2:(sprintf('%.2f', $3)) with labels font ',16'",
      data);
  script.export_as(PNG(), "matrix");
  script.export_as(EPSLATEX().set_standalone(true), "matrix");
}
#+END_src

It generates this figure:

[[file:figures/matrix.png]]

It also generates a standalone =matrix.tex= file you can process with
=pdflatex matrix.tex= to get a monochrome =matrix.pdf= file. If you want
colorized pdf simply use:
#+begin_src cpp :eval never 
EPSLATEX().set_standalone(true).set_color(true)
#+end_src

** (NEW) Matrix data 

You can also use the =Array_2= container to temporary store your data

#+BEGIN_SRC sh :wrap "src cpp :eval never" :results output :exports results
cat $(pwd)/examples/array_2.cpp
#+END_SRC

#+RESULTS:
#+begin_src cpp :eval never
#include "GnuPlotScripting/array_2.hpp"
#include "GnuPlotScripting/GnuPlotScripting.hpp"

#include <iostream>

using namespace GnuPlotScripting;

int
main()
{
  const double X = 2, Y = 2;
  const std::size_t I = 100, J = 120;

  const auto f = [=](const std::size_t i, const std::size_t j) {
    const double x = (2 * i / double(I - 1) - 1) * X;
    const double y = (2 * j / double(J - 1) - 1) * Y;

    return exp(-x * x - y * y);
  };

  Array_2 array_2(I, J, f);

  Data_Array_2 data(array_2);

  Script_File script("array_2.gp");

  script.free_form("set autoscale fix");
  script.free_form("plot {} matrix using 1:2:3 with image", data);
  script.export_as(PNG(), "array_2");
}
#+end_src

It generates this figure:

[[file:figures/array_2.png][file:figures/array_2.png]]

** Histogram

#+BEGIN_SRC sh :wrap "src cpp :eval never" :results output :exports results
cat $(pwd)/examples/histogram.cpp
#+END_SRC

#+RESULTS:
#+BEGIN_src cpp :eval never
#include "GnuPlotScripting/GnuPlotScripting.hpp"

#include <iostream>
#include <random>

using namespace GnuPlotScripting;

// Example from:
// https://stackoverflow.com/a/7454274/2001017
//
template <typename T>
void
gnuplot_histogram(Script& script,
                  const std::vector<T>& data,
                  const size_t n_bin,
                  const typename std::vector<T>::value_type min,
                  const typename std::vector<T>::value_type max)
{
  assert(max > min);
  assert(n_bin > 0);

  Data_Vector gnuplot_data(data);

  const double width = (max - min) / n_bin;
  script.free_form("width={}", width);
  script.free_form("set title 'Histogram min={}, max={}, Δbin={}, #bins={}, #sample={}'",
                   min,
                   max,
                   width,
                   n_bin,
                   data.size());
  script.free_form("hist(x,width)=width*floor(x/width)+width/2.0");
  script.free_form("set boxwidth width*0.9");
  script.free_form("set style fill solid 0.5");
  script.free_form("plot {} u (hist($1,width)):(1.0) smooth freq w boxes notitle", gnuplot_data);
}

int
main()
{
  std::random_device rd;
  std::mt19937 gen(rd());
  const double a = 2, b = 1;
  std::gamma_distribution<> distribution(a, b);

  std::vector<double> data(10000);
  for (auto& data_i : data) data_i = distribution(gen);

  Script_File script("histogram.gp");

  gnuplot_histogram(script, data, 100, 0, 3);

  script.export_as(PNG(), "histogram");
}
#+END_src

The generated figure is:

[[file:figures/histogram.png]]

** Graph

#+BEGIN_SRC sh :wrap "src cpp :eval never" :results output :exports results
cat $(pwd)/examples/graph.cpp
#+END_SRC

#+RESULTS:
#+BEGIN_src cpp :eval never

#include "GnuPlotScripting/GnuPlotScripting.hpp"

#include <iostream>

using namespace GnuPlotScripting;

// Example from the "Gnuplot in Action" book
int
main()
{
  Data_Ascii data(
      "-1 -1 0    # A\n"
      "-1  1 0    # B\n"
      " 1  0 0    # C\n"
      " 0  0 1.75 # D\n"
      "\n\n"
      "-1 -1 0   -1 1 0     \n"
      "-1 -1 0    1 0 0     \n"
      "-1 -1 0    0 0 1.750 \n"
      "-1  1 0    1 0 0     \n"
      "-1  1 0    0 0 1.75  \n"
      " 1  0 0    0 0 1.75  \n");

  Script_File script_a("graph_3D.gp");

  script_a.free_form("unset border");
  script_a.free_form("unset tics");
  script_a.free_form("unset key");
  script_a.free_form("set view 75,35");
  script_a.free_form("splot {} index 0 with points pointtype 7 pointsize 3", data);
  script_a.free_form("replot {} index 1 u 1:2:3:($4-$1):($5-$2):($6-$3) with vectors nohead", data);
  script_a.free_form("pause -1");

  Script_File script_b("graph_2D.gp");

  script_b.free_form("unset border");
  script_b.free_form("unset tics");
  script_b.free_form("unset key");
  script_b.free_form("plot {} index 0 with points pointtype 7 pointsize 3", data);
  script_b.free_form("replot {} index 1 u 1:2:($4-$1):($5-$2) with vectors nohead", data);
  script_b.export_as(PNG(), "graph");
}
#+END_src

It generates this figure:

[[file:figures/graph.png]]

but also an active gnuplot 3D figure you can rotate etc...
** Density plot

This demo shows how to use the =Data_Supervised= class.

#+BEGIN_SRC sh :wrap "src cpp :eval never" :results output :exports results
cat $(pwd)/examples/density_plot.cpp
#+END_SRC

#+RESULTS:
#+BEGIN_src cpp :eval never
#include "GnuPlotScripting/GnuPlotScripting.hpp"

#include <array>
#include <iostream>

using namespace GnuPlotScripting;

std::array<double, 10> X_1 = {0.1, 0.3, 0.1, 0.6, 0.4, 0.6, 0.5, 0.9, 0.4, 0.7};
std::array<double, 10> X_2 = {0.1, 0.4, 0.5, 0.9, 0.2, 0.3, 0.6, 0.2, 0.4, 0.6};
std::array<int, 10> Y      = {1, 1, 1, 1, 1, 0, 0, 0, 0, 0};

int
main()
{
  Data_Supervised data(Y, X_1, X_2);

  Script_File script("density_plot.gp");

  script.free_form("set title 'Supervised learning'");

  script.free_form("set pm3d map interpolate 2,2");
  script.free_form("set palette model RGB defined ( 0 'gray80', 1 'white' )");
  script.free_form("set contour base");
  script.free_form("set cntrparam levels discrete 0.5");
  script.free_form("unset colorbox");  // no palette

  // CAVEAT: for contour use pm3d and not image
  script.free_form(
      "splot 'density_plot_data.txt' u ($1/60):($2/60):3 matrix with pm3d lw 2 notitle");

  for (size_t i = 0; i < data.index_size(); i++)
  {
    // CAVEAT: to prevent
    //         <<warning: Cannot contour non grid data. Please use "set dgrid3d">>
    //         do not forget "nocontour"
    script.free_form(
        "replot {0} index {1} u 1:2:3 with points pt '{1}' ps 2 notitle nocontour", data, i);
  }

  script.export_as(PNG(), "density_plot");

  return EXIT_SUCCESS;
}
#+END_src

Generated figure:

[[file:figures/density_plot.png]]

** Pipe example

Instead of creating a file, we can create a pipe with =popen()= to
directly send data to gnuplot.

#+BEGIN_SRC sh :wrap "src cpp :eval never" :results output :exports results
cat $(pwd)/examples/pipe.cpp
#+END_SRC

#+RESULTS:
#+BEGIN_src cpp :eval never
#include "GnuPlotScripting/GnuPlotScripting.hpp"

#include <chrono>
#include <iostream>
#include <thread>
#include <utility>  // std::pair
#include <vector>

using namespace GnuPlotScripting;

int
main()
{
  // AFAIK one has to replot all data at each iteration
  //
  std::vector<std::pair<size_t, double>> data;

  Script_Pipe pipe(Script_Pipe_Mode_Enum::Not_Persistent);

  pipe.free_form("set xlabel 'iterations'");

  for (size_t i = 0; i < 100; i++)
  {
    data.push_back({i, 1 / (i + 1.)});

    pipe.free_form("plot '-' using 1:2 with lines t \"residue\" ");
    for (const auto& data_i : data)
    {
      pipe.free_form("{} {}", data_i.first, data_i.second);
    }
    pipe.free_form("e");
    pipe.flush();

    std::this_thread::sleep_for(std::chrono::milliseconds(50));
  }
}
#+END_src
** Supported export formats

This example silently exports a basic plot in all supported formats:

#+BEGIN_SRC sh :wrap "src cpp :eval never" :results output :exports results
cat $(pwd)/examples/available_export_formats.cpp
#+END_SRC

#+RESULTS:
#+begin_src cpp :eval never
#include "GnuPlotScripting/GnuPlotScripting.hpp"

#include <iostream>

using namespace GnuPlotScripting;

int
main()
{
  Script_File script("available_export_formats.gp", Script_File_Mode_Enum::Silent);

  script.free_form("plot sin(x) t 'sin(x)'");

  script.export_as(PNG(), "available_export_formats");
  script.export_as(EPSLATEX().set_standalone(true), "available_export_formats");
  script.export_as(SVG(), "available_export_formats");
  script.export_as(TGIF(), "available_export_formats");
  script.export_as(PNGCairo(), "available_export_formats_cairo");
  script.export_as(PNGCairo().set_color(false), "available_export_formats_cairo_nocolor");
  script.export_as(PDFCairo(), "available_export_formats_pdfcairo");
}
#+end_src

** Global config demonstration

This last example shows how to use =Global_Config=.

#+BEGIN_SRC sh :wrap "src cpp :eval never" :results output :exports results
cat $(pwd)/examples/global_config.cpp
#+END_SRC

#+RESULTS:
#+BEGIN_src cpp :eval never
#include "GnuPlotScripting/GnuPlotScripting.hpp"

#include <iostream>

using namespace GnuPlotScripting;

int
main()
{
  global_config().set_logger(
      [](const char *const msg) { std::cerr << "====> My logger " << msg << std::endl; });
  // If you want to remove logger:  global_config().set_logger();
  // If you want to restore the default one: global_config().set_default_logger();

  // If you want to globally overwrite Script_File_Mode_Enum to Persistent, do:
  global_config().set_script_file_mode(Script_File_Mode_Enum::Persistent);

  for (size_t i = 1; i < 5; i++)
  {
    Script_File script(fmt::format("script_{}.gp", i), Script_File_Mode_Enum::Silent);

    script.free_form("plot sin({0}*x) t 'sin({0}*x)'", i);
  }

  // To stop overwriting local choice:
  global_config().set_script_file_mode();

  // Now this will silently run scripts
  for (size_t i = 1; i < 5; i++)
  {
    Script_File script(fmt::format("script_{}.gp", i), Script_File_Mode_Enum::Silent);

    script.free_form("plot sin({0}*x) t 'sin({0}*x)'", i);
  }
}
#+END_src

* Documentation
The library is quite simple and there is only 3 things you must know:
- Data_XXX are classes to store your data 
- Script_XXX are script classes to write your scripts
- global_config() returns a Global_Config object used to define global options.

** Data classes

=Data= classes store data that is embedded into the generated gnuplot
scripts. These classes internally use an =uuid= that insures that data
is embedded only once. By example, when you write:

#+BEGIN_SRC cpp :eval never
script.free_form("plot {} u 1:2",data);
script.free_form("replot {} u 1:3",data);
script.free_form("replot {} u 1:4",data);
#+END_SRC

data is copied *only once* into the script file.

*** =Data_Ascii= 

The most basic =Data= classe is the =Data_Ascii= one. It directly uses
data defined by a =std::string=. By example:
#+BEGIN_SRC cpp :eval never
Data_Ascii data(
    "0.00 0.65 0.65 0.25\n"
    "0.25 0.00 0.75 0.25\n"
    "0.50 0.60 0.00 0.25\n"
    "0.75 0.25 0.10 0.00\n");
#+END_SRC

*Note:* it is really easy to define your own =Data= class. By example
=Data_Ascii= code is as simple as:
#+BEGIN_SRC cpp :eval never
class Data_Ascii final : public Data
{
 public:
  Data_Ascii(const std::string& data) : Data(data) {}
};
#+END_SRC


*** (NEW) =Data_Array_2=

Data to plot is defined using an basic two-dimensional array container, Array_2.


#+BEGIN_SRC cpp :eval never
Array_2 array_2(row_size,column_size);

// fill component: array_2(i,j)= ...

Data_Array_2 data(array_2);

// plot data
#+END_SRC

*** =Data_Vector=

Creates columns of data from =std::vector=, =std::valarray=... In fact
only the =size()= method and the =operator[]= operator are used and you
can use any object defining these two methods. By example:

#+BEGIN_SRC cpp :eval never
std::vector<double> v1(10);
std::vector<int> v2(10);
std::valarray<double> v3(10);
// ...
Data_Vector data(v1,v2,v3);
#+END_SRC

*** =Data_Supervised=

The =Data_Supervised= class is similar to the =Data_Vector= except that it
uses and extra _category_ vector. It can be used to plot points
associated to a supervised learning task. By example:

#+BEGIN_SRC cpp :eval never
std::array<double, 10> X_1 = {0.1, 0.3, 0.1, 0.6, 0.4, 0.6, 0.5, 0.9, 0.4, 0.7};  // X_1 feature
std::array<double, 10> X_2 = {0.1, 0.4, 0.5, 0.9, 0.2, 0.3, 0.6, 0.2, 0.4, 0.6};  // X_2 feature
std::array<int, 10> Y      = {1, 1, 1, 1, 1, 0, 0, 0, 0, 0};                      // category (=label)

Data_Supervised data(Y, X_1, X_2); // note: the category vector Y is always the _first_ argument

std::cout << data.data();
#+END_SRC

*Note:* as AFAK it is not possible to directly plot points with symbols
 retrieved from the the Y column (see [[https://stackoverflow.com/questions/29622885/how-set-point-type-from-data-in-gnuplot][SO
 how-set-point-type-from-data-in-gnuplot]]), hence the =Data_Supervised=
 class sorts and groups the sample according to their categories. By
 example the previous code prints:

#+BEGIN_EXAMPLE
0.6 0.3 0 
0.9 0.2 0 
0.4 0.4 0 


0.1 0.1 1 
0.1 0.5 1 
0.6 0.9 1 
0.4 0.2 1 


0.5 0.6 2 
0.7 0.6 2 


0.3 0.4 3 
#+END_EXAMPLE
Creating these groups allows to use the gnuplot *index* keyword to plot
all points associated to a given category. By example:
#+BEGIN_SRC gnuplot :eval never
replot "$data_uuid" index i u 1:2:3 with points; # plot points of category i
#+END_SRC


Also note that when data is embedded, the =Y= category column is the
*last one*. The rational is that for:
#+BEGIN_SRC cpp :eval never
Data_Supervised data_a(Y, X_1, X_2); 
Data_Vector data_b(X_1, X_2); 
#+END_SRC
then in *both* cases, =X_1= column index is 1 and =X_2= column index is 2.

** Script classes

There are two script classes:
- =Script_File= creates a file to store the script. 
- =Script_Pipe= creates a pipe to push data directly to GnuPlot, in that
  case no file is created.
They inherits from the =Script= base class that provides the following methods:
#+BEGIN_SRC cpp :eval never
template <typename... ARGS>
Script& free_form(ARGS&&... args);

Script& export_as(const Export_As& export_as, const std::filesystem::path& output);

void flush();
#+END_SRC

- =free_form= allows you to write free form using the =fmt= library, by example:
#+BEGIN_SRC cpp :eval never
script.free_form("plot '{}' u {}:{}","data_file.dat",1,2);
#+END_SRC

- =flush()= forces buffer to be flushed

- =export_as()= generates script code to export the figure in the given
  format, by example:

#+BEGIN_SRC cpp :eval never
script.export_as(EPSLATEX().set_standalone(true),"filename");
#+END_SRC

*Note:* 
- the right extension for =filename= is automatically added (here this
  would be =.tex=).
- currently supported formats are =PNG=, =EPSLATEX=, =SVG=, =TGIF=,
  =PNGCairo= and =PDFCairo=.  

*** =Script_File= class 

The only relevant part is the constructor:
#+BEGIN_SRC cpp :eval never
Script_File(const std::filesystem::path& filename,
            Script_File_Mode_Enum script_file_mode = Script_File_Mode_Enum::Persistent);
#+END_SRC
- =Filename= is the gnuplot script file name (you are free to use the
  file extension you want, on my side I use the =.gp= extension).
- =script_file_mode= is important as it defines what happens at destruction time
  - =Script_File_Mode_Enum::None= does nothing
  - =Script_File_Mode_Enum::Silent= silently runs GnuPlot (this will generate your exported figures)
  - =Script_File_Mode_Enum::Persistent= runs GnuPlot in persistent mode,
    it will generates your figures and left a window opened that
    allows you to see the result. This is only an opened window and
    *not* an active gnuplot session (you *cannot* interact with the plot).

*Note:* to get an active GnuPlot session, you can replay your script with:
#+BEGIN_SRC sh :eval never
gnuplot filename.gp -
#+END_SRC
(note the final '-', see GnuPlot documentation for further details).

Another possibility is to add a pause in your gnuplot script:
#+BEGIN_SRC cpp :eval never
script.free_form("pause -1");
#+END_SRC

*** =Script_Pipe= class

Here instead of writing into a file, we open a pipe with =popen=. This
allows you to directly command GnuPlot during your code execution. Note
that this is only a *unidirectional* channel.

The constructor is:
#+BEGIN_SRC cpp :eval never
Script_Pipe(Script_Pipe_Mode_Enum script_pipe_mode = Script_Pipe_Mode_Enum::Persistent);
#+END_SRC

as for =Script_File= class, =script_pipe_mode= defines what happens at destruction time:
- =Script_Pipe_Mode_Enum::Not_Persistent= does not keep an opened window
- =Script_Pipe_Mode_Enum::Persistent= keeps an opened, but inactive, window

** Global_Config class

This class allows you to define or overwrite globally some options

#+BEGIN_SRC cpp :eval never
const char* gnuplot_exe() const;
Global_Config& set_gnuplot_exe(const char* const gnuplot_executable);

Global_Config& set_logger();          // removes logger
Global_Config& set_default_logger();  // reuses default one
Global_Config& set_logger(const std::function<void(const char* const msg)>& f);  // defines your own
bool has_logger() const;
Global_Config& set_log_message(const char* const msg);

Global_Config& set_script_file_mode(
    Script_File_Mode_Enum mode);        // globally overwrite local 'script_file_mode'
Global_Config& set_script_file_mode();  // stop overwriting local 'script_file_mode'
std::optional<Script_File_Mode_Enum> script_file_mode() const;
#+END_SRC

- =set/gnuplot_exe()= functions allow you to define GnuPlot executable
  filename, by default this is =gnuplot= or =gnuplot.exe= for windows.
- =set/logger()= functions allow you to stop or redirect logs, by example:
#+BEGIN_SRC cpp :eval never
global_config().set_logger([](const char *const msg) {
  std::cerr << "====> My logger " << msg << std::endl;
});
#+END_SRC
- =set_script_file_mode()= functions are more interesting as they allow
  you to overwrite globally what happens at =Script_File= destruction
  time. A typical use case is as follows:

  Imagine that your code silently generates a lot of scripts:

#+BEGIN_SRC cpp :eval never
for (size_t i = 1; i < 5; i++)
{
  Script_File script(fmt::format("script_{}.gp", i), Script_File_Mode_Enum::Silent);

  script.free_form("plot sin({0}*x) t 'sin({0}*x)'", i);
}
#+END_SRC
  
   However at debug time, you want to force visualization to see what
   happens. In that case you simply have to add
#+BEGIN_SRC cpp :eval never
global_config().set_script_file_mode(Script_File_Mode_Enum::Persistent);
#+END_SRC
   *before* 
#+BEGIN_SRC cpp :eval never
for (size_t i = 1; i < 5; i++)
{
  ...
}
#+END_SRC
   This will force all =Script_File= to use
   =Script_File_Mode_Enum::Persistent=
 
* References

- [[http://www.gnuplot.info/][GnuPlot]] official page  
- [[https://www.manning.com/books/gnuplot-in-action-second-edition][GnuPlot in Action]] a very well written book 
- [[http://www.gnuplotting.org/][www.gnuplotting.org]] a lot of great examples
- [[http://folk.uio.no/inf3330/scripting/doc/gnuplot/Kawano/index-e.html][GnuPlot not so Frequently Asked Questions]]
- [[https://en.wikipedia.org/wiki/Gnuplot][Wikipedia]] the free encyclopedia...

# figures/matrix.png http://pixorblog.files.wordpress.com/2019/12/matrix.png
# figures/histogram.png http://pixorblog.files.wordpress.com/2019/12/histogram.png
# figures/graph.png http://pixorblog.files.wordpress.com/2019/12/graph.png

# figures/plot.png http://pixorblog.files.wordpress.com/2019/12/plot.png

* FAQ
-> your question here

About

A simple C++17 lib that helps you to quickly plot your data with GnuPlot

Topics

Resources

Stars

51 stars

Watchers

5 watching

Forks

Releases

Packages

Contributors

Languages