Monday 20 March 2023

a dirty C++ gnuplot caller

Since I couldn't force my dotnet/c# program to call gnuplot with both data and script (the -e option), I created this shitty thing. Perhaps c# Process knows how to do i/o redirection and pipes, but I found it faster to workaround it. Thank you: SO/SE, gnuplotting.org, gnuplot documentation etc.

Assumptions: 

(i) your program needs to (s)plot (a lot of* - no... see bottom) data and you prefer to avoid making datafiles, parametrizing scripts with datafiles' paths etc, calling gnuplot < scriptfile etc. (Though in my case I had to parametrize command sets with output file name—I was making dozens of thousands of images, then an animation using ffmpeg. (If you're curious: no, writing the whole thing using gnuplot only was out of the question.)

(ii) you have some grid-like structure behind for variables x and y, and the data should look like

x11 y11 z11
...
x1n y1n z1n

x21 y21 z21
...
x2n y2n z2n

[all the rest]

xm1 ym1 zm1
...
xmn ymn zmn
e

The three dots denote all the data entries (triples here, could be pairs) between the first one and the last one per one. Note the empty line between the pieces with first index fixed and the character 'e' at the end. 

(iii) you have to be able to produce sets of plot commands. Each (if they differ) should be a single line string with commands separated by semicolons, like this:

reset;unset key;unset border;set size square;unset tics;unset colorbox;set view map;set pm3d lighting primary 0.2 specular 0.3;set style fill transparent solid 0.4;set pm3d interpolate 0,0;set dgrid3d 100, 100, box;set samples 100;set isosamples 100;set palette defined ( 0.1 0.1 0.1 0.1, .88 .88 .88 .88 ); set terminal pngcairo transparent enhanced size 1920,1080;set xrange[0:100];set yrange[0:100];set zrange[-1:1];set object 1 circle at 25,45,0 radius 1.5 fillstyle solid 0.4 fillcolor 'black';set object 2 circle at 50,35,0 radius 1.5 fillstyle solid 0.4 fillcolor 'black';set object 3 polygon from 25,45,0 to 50,35,0 to 25,45,0 linewidth 1 fillstyle solid 0.8 fillcolor 'gray';set object 4 circle at 10,10,0 radius 2 fillstyle solid 0.3 fillcolor 'red';set output 'img_0.png';splot '-' with pm3d;

Note that the numbers marked in red must be consistent with n x m structure of the data, also the x- and y-ranges must be chosen accordingly, unless you want to hide something. The '-' thing tells gnuplot to take data from standard input. 

The following code does the job and may be used as a separate process with 2 string-type arguments: data and command set. Perhaps it's buggy and you may have to change the two fixed paths defined here:

#include<sstream>
#include<iostream>
#include<string>
#include<fstream>
 
void GnuplotRunner(char* argv);
 
const std::string EchoPath = "/bin/echo";
const std::string GnuplotPath = "/usr/bin/gnuplot";

void GnuplotRunner(char** argv)
{
    std::stringstream command;
    command 
        << EchoPath 
        << " \"" << argv[1] <<"\" | " 
        << GnuplotPath << " -e \""
        << argv[2]<<"\"";

    system(command.str().c_str());
}

Use at your own risk. Here's what I got when doing tests of shadows creation. The pinkish point shines around, the black points are joint by a line that casts a shadow. No rocket science, yet.
 
Some more lousy graphics—a 9-segment closed line and the shadows that are generated by its vertices—these will reflect waves. Huygens' principle in a nutshell... Gotta work on the
set pm3d interpolate x,y commandthis is responsible for most of the pictures' lousiness :D [no, the guilty party was set dgrid3d or so, it turns out it's not needed]
 









* Apparently there is some upper bound on the length of .net Process.Argument length, so the only way out for me is to write the data on the disk. Shit happens.

No comments:

Post a Comment