GNUPlotCPP - a tool for creating function plots from C++ code

DSP, Plugin and Host development discussion.
RELATED
PRODUCTS

Post

These days I find myself using Desmos whenever possible. There are limits to what it can do, probably the biggest issue for DSP is the lack of complex arithmetics, but for what it can do it's hard to beat the workflow where the graph updates every keystroke as you write a formula.

For plotting from C++ code though, I tend to just write a simple visualizer directly; as long as you have a "more or less" immediate mode vector API, this can be quite simple.

Post

I've been using ImGui+ImPlot for interactive visualisation. [...] when it comes to tweaking things in realtime and seeing the results immediately within the c++ code, gnuplot doesn't work as nice.
What do you mean by "tweaking things in realtime"? How does your workflow look like when doing this? Do you mean, you can do things like defining a function with a parameter p, like - say - f(x) = sin(p*x) and then attach the parameter p to a slider and tweak that in realtime like we can do in desmos?

https://www.desmos.com/calculator/txi2rlnado

These days I find myself using Desmos whenever possible.
Yes, I also use desmos a lot for quickly plotting a function, tweak some parameters, etc.

For plotting from C++ code though, I tend to just write a simple visualizer directly; as long as you have a "more or less" immediate mode vector API, this can be quite simple.
Hmm...yeah...I guess, I could do something similar within one of my JUCE-based projects (or set one up for that purpose). But then, to get access to the plotting facilities, I would have to be at a point inside my code, where JUCE and all the plotting facilities that I wrote on top of it, are already included. And that is typically not the case when I work inside my DSP library which is supposed to be a low-level number-crunching library with no external dependencies (and certainly not a giant one like JUCE). Granted, to make plotting available there, I need to include GNUPlotter.h/cpp somewhere at the lowest level (at least in debug builds) - but these are just two moderately sized files, so that's acceptable for me and I can easily strip these #includes away for release builds with some #defines.

What I gain is the ability to just inject 1-liner plot commands everywhere in my DSP code so I can at any point investigate, what my data looks like - even right in the middle of some algorithm. An example is at the bottom of this function:

Code: Select all

template<class T>
template<class Tx>
void rsNumericIntegrator<T>::trapezoidal(const Tx* x, const T* y, T* yi, int N, T c)
{
  Tx xo; 
  T  yo, zo, tmp;
  xo = x[0]; yo = y[0]; zo = c; yi[0] = zo;    // "old" values (at index n-1)
  for(int n = 1; n < N; n++) {
    tmp = zo + (x[n]-xo)*(y[n]+yo)*T(0.5);     // compute integral by trapezoidal rule
    xo = x[n]; yo = y[n]; zo = tmp;            // update integrator state variables
    yi[n] = tmp;                               // write integral to output array
    //rsAssert(rsIsFiniteNumber(tmp));
  }
  rsPlotArraysXY(N, x, y, yi);                 // uncomment for debug
}

I can just inject plot "commands" like rsPlotArraysXY at the bottom (which is a small convenience function that invokes the plotter) everywhere in the code and as soon as the execution gets to that point, a window pops up and shows me the plot. Within that window, I can zoom, pan etc. Very convenient for debugging.

Generally, my workflow when developing algorithms looks as follows: I have a project that builds a little commandline application and within the main function, I call one or more script-like experimental functions that generate some data, invoke some algorithms from the library on that data and then produce plots for me to look at. For example, this little script/experiment here tests my numerical differentiation and integration facilities:

Code: Select all

void nonUniformArrayDiffAndInt()
{
  // Tests numerical differentiation and integration routines. We sample a sinewave at 
  // nonequidistant sample points and find the numeric derivative and integral at these sample
  // points and compare them to the true derivative/integral values.

  using Real = double;
  using AT = rsArrayTools;
  using ND = rsNumericDifferentiator<Real>;
  using NI = rsNumericIntegrator<Real>;

  static const int N = 100;   // Number of sample points
  Real p = 1.0;               // Start-phase
  Real w = 2.0;               // Radian frequency 
  Real xMax = 10.0;           // Maximum x-axis value
  Real x[N], y[N];            // x- and y-axis values
  Real yd[N], ydn[N];         // True and numeric derivative
  Real yi[N], yin[N];         // True and numeric integral

  // Create x-axis:
  AT::fillWithRandomValues(x, N, 0.1, 1.5, 0);
  AT::cumulativeSum(x, x, N);
  Real scaler = xMax/x[N-1];
  AT::scale(x, N, scaler);

  // Compute sine and its derivative and integral analytically at the samples:
  int n;
  for(n = 0; n < N; n++) {
    y[n]  =        sin(w*x[n] + p);
    yd[n] =      w*cos(w*x[n] + p);
    yi[n] = -(1/w)*cos(w*x[n] + p); }

  // Compute the numeric derivative and integral:
  ND::derivative( x, y, ydn, N, true);
  NI::trapezoidal(x, y, yin, N, yi[0]);

  // Plot function along with true and numeric derivatives and integrals:
  GNUPlotter plt;
  plt.setToDarkMode();
  plt.setGraphColors("BBBBBB", "444499", "8888ff", "883333", "ff8888");
  plt.plotFunctionTables(N, x, y, yd, ydn, yi, yin);
}
and produces the following plot:
NumDiffInt.png
when I zoom in, I can see the small difference between the numerically and analytically evaluated derivative and integral. Here shown for the derivative (blue):
NumDiffIntZoomedIn.png
That's my typical workflow these days when developing algorithms. Granted, changing the data to be plotted requires recompilation which takes a few seconds so it's not as quick as in a REPL environment.
You do not have the required permissions to view the files attached to this post.
Last edited by Music Engineer on Sat Sep 16, 2023 9:04 pm, edited 1 time in total.
My website: rs-met.com, My presences on: YouTube, GitHub, Facebook

Post

Music Engineer wrote: Sat Sep 16, 2023 5:54 pm Generally, my workflow when developing algorithms looks as follows: I have a project that builds a little commandline application and within the main function, I call one or more script-like experimental functions that generate some data, invoke some algorithms from the library on that data and then produce plots for me to look at.
My workflow is actually often just to crash the ASIO drivers with code written directly into a plugin (hey, doing this for a while really improves your ability to write code that works the first time).. and then use plugin analyzers to see if it looks like it should.

Post

mystran wrote: Sat Sep 16, 2023 6:15 pm My workflow is actually often just to crash the ASIO drivers with code written directly into a plugin (hey, doing this for a while really improves your ability to write code that works the first time).. and then use plugin analyzers to see if it looks like it should.
I occasionally do that, too - but for some things, I really like to have some sort of "lab" environment with well defined inputs, controlled conditions and exactly reproducible results. Kinda like in a unit test but instead of programmatically verifying the output, I plot it. Sometimes, I later also include programmatic verifications of the outputs and sometimes such experiments morph into a unit test over time. At the bottom of these experimental scripts, I also often have a comment section where I write down my observations.
My website: rs-met.com, My presences on: YouTube, GitHub, Facebook

Post

Music Engineer wrote: Sat Sep 16, 2023 8:29 pm
mystran wrote: Sat Sep 16, 2023 6:15 pm My workflow is actually often just to crash the ASIO drivers with code written directly into a plugin (hey, doing this for a while really improves your ability to write code that works the first time).. and then use plugin analyzers to see if it looks like it should.
I occasionally do that, too - but for some things, I really like to have some sort of "lab" environment with well defined inputs, controlled conditions and exactly reproducible results. Kinda like in a unit test but instead of programmatically verifying the output, I plot it. Sometimes, I later also include programmatic verifications of the outputs and sometimes such experiments morph into a unit test over time. At the bottom of these experimental scripts, I also often have a comment section where I write down my observations.
Sure. It depends a bit on exactly what you are doing as well.

Post

Music Engineer wrote: Sat Sep 16, 2023 5:54 pm
I've been using ImGui+ImPlot for interactive visualisation. [...] when it comes to tweaking things in realtime and seeing the results immediately within the c++ code, gnuplot doesn't work as nice.
What do you mean by "tweaking things in realtime"? How does your workflow look like when doing this? Do you mean, you can do things like defining a function with a parameter p, like - say - f(x) = sin(p*x) and then attach the parameter p to a slider and tweak that in realtime like we can do in desmos?
Pretty much, but for the things that are not so easy to compute in desmos. Such as PDEs, results from iterative processes or non-elementary functions that come from specific libraries.

Post

2DaT wrote: Sun Sep 17, 2023 1:44 amPretty much, but for the things that are not so easy to compute in desmos. Such as PDEs, results from iterative processes or non-elementary functions that come from specific libraries.
OK - yes - that's what I have guessed as well.

I have now integrated more color maps into the plotter class and created a little demo that shows how to use that new functionality. I also started a discussion thread in the github repo where I show some of my personal favorite color maps:

https://github.com/RobinSchmidt/GNUPlot ... cussions/9

...in the last few days, I found a lot of resources on the topic of color maps. I collected some links into this textfile:

https://github.com/RobinSchmidt/GNUPlot ... orMaps.txt

It's currently a rather disorganized mess, though. I think, I really like ChromaJS library:

https://gka.github.io/chroma.js/

It seems like the color maps created with that just look right. Unfortunately, the web interface here:

https://gka.github.io/palettes/#/11|d|0 ... 93003a|1|1

is unusable. I have no idea, how to use this "Select and arrange input colors" section in a meaningful way. :-| The color-picker widgets do not seem to make much sense with regard to what colors they allow to select - or I just don't get it. But anyway - I think, I'll need a break from color maps now and do something else :shrug: I was actually working on some math stuff for which I needed such contour plots for visualization. The feature is now good enough for that purpose for me so I can now continue working on my math. Maybe I'll return to this color stuff at some later point and try to create more color maps myself. It's actually a fascinating topic. One where art meets science. I'm generally interested in all things where math and/or science meets art. Musical audio signal processing is one of these fields and happens to be my main interest but image processing and computer graphics are also very interesting topics.
My website: rs-met.com, My presences on: YouTube, GitHub, Facebook

Post Reply

Return to “DSP and Plugin Development”