COB logoCOB honor code logo

BA 371 — VC#.NET, Processing, JavaScript (Google Charts) & gnuplot

In this lab we explore communicating data between our application program and a few other, external programs. We have already done some of this when our program interacted with a database management system. However, programmatic interaction with many third party programs is not quite as 'nice' (although things are getting quite a bit better these days.)

We'll explore four examples: one in which we simulate the Monty Hall problem in our program and then ask our browser to graphically display the results of the simulation, a second one in which we use the same setup to plot the computation of the number Phi (φ), a third one in which we send data to the plotting package gnuplot and a final one in which we send those same data to the Google Chart API.

To start, let's simulate the Monty Hall problem in our (console!!) program and then ask our browser to graphically display the results.

Here's what we want to do:

We will use files to communicate the data from our program to the Web browser. This is, of course, a somewhat clumsy method. It would be far nicer to 'pipe' the data directly from our program into the browser. However, this sort of Inter-Process Communication (IPC) would require some more explanation and understanding. Hence, for this lab we stick with the more primitive 'file' method.

Here's the (console!!!) program (!!! --ATTENTION-- !!! This program tries to write files to the Z:\public_html folder (this is the folder which is under control of the ONID Web server. Hence, files in that folder are accessible through HTTP/Web-browser). So, make sure that you have that folder — !!! The public_html folder may be hidden on your Z:-drive. If so, set your file browser to display hidden files and folders !!!):

 

class MontyHall
{
    //setup configuration parameters

    //  !!! --ATTENTION-- !!!
    // This program tries to write files to the Z:\public_html folder.
    // So, make sure that you have that folder.

    //Set up some constants
    const int N_MOVES = 1000; //number of games we'll play
    const int HEIGHT = 300;   //height of the plot in pixels
    const int WIDTH = 1000;   //width of the plot in pixels
    const string HTML_FILE_WINDOWS_ONID = "Z:\\public_html\\monty_hall.html";
    const string PDE_FILE_WINDOWS_ONID = "Z:/public_html/monty_hall.pde"; //file with 'processing' plotting code
    const string PDE_FILE_REF = "monty_hall.pde";
    const string PROCESSING_FILE = "http://classes.bus.oregonstate.edu/ba371/reitsma/VCSharplabs/processing-1.3.0.js"; //library
 
    static string userName = System.Security.Principal.WindowsIdentity.GetCurrent().Name;
    static string[] nameparts = userName.Split('\\');
    static string HTML_FILE = "http://people.oregonstate.edu/~" + nameparts[1] + "/monty_hall.html"; //file we'll load into the browser

    //setup random seed
    static System.Random rand = new System.Random();

    static void Main(string[] args)
    {
        int door_chosen, door_wins;
        int n_wins_stay = 0, n_wins_switch = 0;
        float win_fraction_stay, win_fraction_switch;
        float last_win_fraction_stay = 0.0F, last_win_fraction_switch = 0.0F;

        //write the monty_hall.html file
        System.IO.FileStream htmlFile = null;
        System.IO.StreamWriter htmlWriter;
        try
        {
            htmlFile = new System.IO.FileStream(HTML_FILE_WINDOWS_ONID,
                                                System.IO.FileMode.Create,
                                                System.IO.FileAccess.Write);
        }
        catch (System.Exception ex) { System.Console.WriteLine(ex.Message); }
        htmlWriter = new System.IO.StreamWriter(htmlFile);
        htmlWriter.WriteLine("<html>");
        htmlWriter.WriteLine("<script type=\"text/javascript\" src=\"" + PROCESSING_FILE + "\"></script>");
        htmlWriter.WriteLine("<canvas data-processing-sources=\"" + PDE_FILE_REF + "\"></canvas>");
        htmlWriter.WriteLine("</html>");
        htmlWriter.Close();

        //initialize the monty_hall.pde file
        System.IO.FileStream pdeFile = null;
        System.IO.StreamWriter pdeWriter;
        try
        {
            pdeFile = new System.IO.FileStream(PDE_FILE_WINDOWS_ONID,
                                               System.IO.FileMode.Create,
                                               System.IO.FileAccess.Write);
        }
        catch (System.Exception ex) { System.Console.WriteLine(ex.Message); }
        pdeWriter = new System.IO.StreamWriter(pdeFile);
        pdeWriter.WriteLine("void setup()");
        pdeWriter.WriteLine("{");
        pdeWriter.WriteLine("size(" + N_MOVES + "," + HEIGHT + ");");
        pdeWriter.WriteLine("background(0);");
        pdeWriter.WriteLine("noLoop();");
        pdeWriter.WriteLine("stroke(255);");
        pdeWriter.WriteLine("}");
        pdeWriter.WriteLine("void draw()");
        pdeWriter.WriteLine("{");

        //process N_MOVES games
        for (int i = 0; i < N_MOVES; i++)
        {
            //pick the winning door at random
            door_wins = rand.Next(3);

            //pick the chosen door at random
            door_chosen = rand.Next(3);

            //determine win and lose
            if (door_chosen == door_wins)
                n_wins_stay++;
            else
                n_wins_switch++;

            //compute the running fraction of wins
            win_fraction_stay = n_wins_stay / (i + 1.0F);
            win_fraction_switch = n_wins_switch / (i + 1.0F);

            //add the processing instructions to monty_hall.pde
            if (i > 0)
            {
                int x1 = i - 1;
                int x2 = i;
                int y1 = System.Convert.ToInt16(HEIGHT - HEIGHT * last_win_fraction_stay);
                int y2 = System.Convert.ToInt16(HEIGHT - HEIGHT * win_fraction_stay);
                pdeWriter.WriteLine("line(" + x1 + "," + y1 + "," + x2 + "," + y2 + ");");
                y1 = System.Convert.ToInt16(HEIGHT - HEIGHT * last_win_fraction_switch);
                y2 = System.Convert.ToInt16(HEIGHT - HEIGHT * win_fraction_switch);
                pdeWriter.WriteLine("line(" + x1 + "," + y1 + "," + x2 + "," + y2 + ");");
                last_win_fraction_stay = win_fraction_stay;
                last_win_fraction_switch = win_fraction_switch;
            }
        }

        //System.Console.WriteLine("switch wins: " + n_wins_switch);
        //System.Console.WriteLine("stay wins: " + n_wins_stay);

        //finish the monty_hall.pde file
        pdeWriter.WriteLine("text(\"% wins\", 10, 25)");
        pdeWriter.WriteLine("text(\"trial count (" + N_MOVES + " trials)\"" + ", " + (WIDTH / 2) + ", " + (HEIGHT - 10) + ")");
        pdeWriter.WriteLine("text(\"stay wins: " + n_wins_stay + "/" + N_MOVES + "\", 50, 50);");
        pdeWriter.WriteLine("text(\"switch wins: " + n_wins_switch + "/" + N_MOVES + "\", 50, 65);");
        pdeWriter.WriteLine("}");
        pdeWriter.Close();

        //call the OS to display monty_hall.html; it'll call the default browser
        try { System.Diagnostics.Process.Start(HTML_FILE); }
        catch (System.Exception ex) { System.Console.WriteLine(ex.Message); }
    }
}

Compile and run (if you receive a message in your browser that a file in your Z:\public_html folder cannot be accessed, change its Security Properties to Read for Everyone).

A few things to notice:

Just so that you realize, the Monty Hall program itself could, of course, easily have been written in JavaScript in its entirety and hence, have all the work done by the browser rather than dividing it over a C# program and the browser. Still, it's not unusual to compute data in one program and have those data rendered by another program.

 

Let's try this same approach with a different problem, namely the computation of Phi (φ) a famous number (1.618) also known as the golden ratio. Phi is hiding (in plain sight) in many places in the world, from the way sunflower seeds are set in their flower and how the nautilus builds its shell to lots of graphic design, architecture and printed page formats.

One way of computing Phi is from a simple sequence of numbers known as the Fibonacci series. This series is very easy to create. Start with the numbers 1 and 1 and add numbers which consist of the sum of the previous two numbers; hence, 1, 1, 2, 3, 5, 8, 13, 21, etc. When we now compute the ratio of each number to its preceding number, we find that, starting at the beginning of the series, these ratios quickly settle down to the value 1.618. (Before we program this, quickly verify this with a spreadsheet, a calculator or Google). Here's the program that computes the first 25 Fibonacci numbers and plots the associated value of Phi in your Web browser (Nothing really new here; it's just a modified version of the Monty Hall program).


class Fibonacci
{
    //setup configuration parameters

    //  !!! --ATTENTION-- !!!
    // This program tries to write files to the Z:\public_html folder.
    // So, make sure that you have that folder.

    //Set up some constants:
    const int ITERATIONS = 25; //number of games we'll play
    const int HEIGHT = 300;   //height of the plot in pixels
    const int WIDTH = 1000;   //width of the plot in pixels
    const double MAX_PHI = 2.5;
    const string HTML_FILE_WINDOWS_ONID = "Z:\\public_html\\fibonacci.html";
    const string PDE_FILE_WINDOWS_ONID = "Z:/public_html/fibonacci.pde"; //file with 'processing' plotting code
    const string PDE_FILE_REF = "fibonacci.pde";
    const string PROCESSING_FILE = "http://classes.bus.oregonstate.edu/ba371/reitsma/VCSharplabs/processing-1.3.0.js"; //library
 
    static string userName = System.Security.Principal.WindowsIdentity.GetCurrent().Name;
    static string[] nameparts = userName.Split('\\');
    static string HTML_FILE = "http://people.oregonstate.edu/~" + nameparts[1] + "/fibonacci.html"; //file we'll load into the browser

    static void Main(string[] args)
    {
        //write the fibonacci.html file
        System.IO.FileStream htmlFile = null;
        System.IO.StreamWriter htmlWriter;
        try
        {
            htmlFile = new System.IO.FileStream(HTML_FILE_WINDOWS_ONID,
                                                System.IO.FileMode.Create,
                                                System.IO.FileAccess.Write);
        }
        catch (System.Exception ex) { System.Console.WriteLine(ex.Message); }
        htmlWriter = new System.IO.StreamWriter(htmlFile);
        htmlWriter.WriteLine("<html>");
        htmlWriter.WriteLine("<script type=\"text/javascript\" src=\"" + PROCESSING_FILE + "\"></script>");
        htmlWriter.WriteLine("<canvas data-processing-sources=\"" + PDE_FILE_REF + "\"></canvas>");
        htmlWriter.WriteLine("</html>");
        htmlWriter.Close();

        //initialize the fibonacci.pde file
        System.IO.FileStream pdeFile = null;
        System.IO.StreamWriter pdeWriter;
        try
        {
            pdeFile = new System.IO.FileStream(PDE_FILE_WINDOWS_ONID,
                                               System.IO.FileMode.Create,
                                               System.IO.FileAccess.Write);
        }
        catch (System.Exception ex) { System.Console.WriteLine(ex.Message); }
        pdeWriter = new System.IO.StreamWriter(pdeFile);
        pdeWriter.WriteLine("void setup()");
        pdeWriter.WriteLine("{");
        pdeWriter.WriteLine("size(" + WIDTH + "," + HEIGHT + ");");
        pdeWriter.WriteLine("background(0);");
        pdeWriter.WriteLine("noLoop();");
        pdeWriter.WriteLine("stroke(255);");
        pdeWriter.WriteLine("}");
        pdeWriter.WriteLine("void draw()");
        pdeWriter.WriteLine("{");

        //process ITERATIONS
        long first = 1;
        long second = 1;
        double phi_previous = 1;
        double phi_now = 0.0;

        for (int i = 2; i <= ITERATIONS; i++)
        {
            long sum = first + second;
            first = second;
            second = sum;
            phi_now = (double)second / first;

                //add the processing instructions to monty_hall.pde
            double d_iterations = ITERATIONS;
            int x1 = System.Convert.ToInt16((i - 1) / d_iterations * WIDTH);
            int x2 = System.Convert.ToInt16(i / d_iterations * WIDTH);
            int y1 = System.Convert.ToInt16(HEIGHT - HEIGHT * phi_previous/MAX_PHI);
            int y2 = System.Convert.ToInt16(HEIGHT - HEIGHT * phi_now/MAX_PHI);
            if (i == 2)
            {
              y1 = y2; x1 = x2;
            }
            pdeWriter.WriteLine("line(" + x1 + "," + y1 + "," + x2 + "," + y2 + ");");
            pdeWriter.WriteLine("text(\"" + second + "\"," + x2 + "," + (HEIGHT - HEIGHT * .1) + ")"  );
            phi_previous = phi_now;
        }

        //System.Console.WriteLine("switch wins: " + n_wins_switch);
        //System.Console.WriteLine("stay wins: " + n_wins_stay);

        //finish the monty_hall.pde file
        pdeWriter.WriteLine("text(\"phi: " + phi_now + "\", 10, 25)");
        pdeWriter.WriteLine("text(\"trial count (" + ITERATIONS + " trials)\"" + ", " + (WIDTH / 2) + ", " + (HEIGHT - 10) + ")");
        pdeWriter.WriteLine("}");
        pdeWriter.Close();

        //call the OS to display monty_hall.html; it'll call the default browser
        try { System.Diagnostics.Process.Start(HTML_FILE); }
        catch (System.Exception ex) { System.Console.WriteLine(ex.Message); }
    }
}

Next, we'll send data to gnuplot, a quasi free and open source (FOSS), generic utility for plotting data. The plotter has several output formats among which are the screen (kind of handy), png, jpeg, etc.

For those of you who are thinking about writing your own plotting software, beware; it's hard work and the devil is in the details. Getting a basic line graph or bar graph up is not hard (see the Monty Hall example above), but getting it lined up properly and managing the tick marks, axis formatting, colors, fonts, the labels, the legends, etc., is not at all trivial (note, for instance, that our Monty Hall graph did not have any drawn axes and axis tick marks.)

Tools such as gnuplot are nice because they work well and can be interfaced with your own, custom-written software. Although gnuplot was originally written for Unix and hence, runs well on Linux, some time ago it was ported to Windows. The port, however, was just that; it was a port, not a rewrite. Hence it's a little rough on the Windows side... good enough for our purposes, though.

gnuplot is an interactive, command-driven program. It takes its input (the commands), however, in several ways. For instance, we can feed it files —similar to what we did with the 'processing' example— or we can talk to it using its command-line interpreter. We can also freely switch from one to the other; i.e., first feed it a file with commands and then fine tune or add to the plot with further commands given on its command line.

To get a feeling for gnuplot, we will first run it all by itself, before we let our program call it.

Feeding gnuplot data from a program

We want to use gnuplot as a plotter of any data we might have computed or extracted from a database with a VC#.Net program; e.g., we could use our VC#.Net software to let users specify what sort of plot they want and for retrieving the associated data from the database. VC#.Net will then prepare two files with gnuplot data:
  1. The gnuplot data file: a text file containing the data to be plotted.
  2. The gnuplot control file: a text file with gnuplot commands.
The control file contains the commands for gnuplot to set up the plot; e.g., plot a line graph with time on the x-axis, color schemes, etc., as well as the plot command itself. The control file's plot command contains a reference to the data file.

Below are two links pointing to an example of a gnuplot control file and its associated data file. The statements starting with a '#' in the control file indicate comments and contain explanations of what the entries in the control file mean. Note that the data file contains data collected from a set of five temperature sensors built into the wall of a building. The data comprise five timeseries in that data are taken from each of the five sensors every five minutes for a period of 24 hours.

Note how the control file's plot command points to the data file.

gnuplot control file.txt

gnuplot data file.txt

Important: both the control file and the data file can be written by a program such as one written in C#. This then suggests the following general method for using gnuplot from inside a C# program:
  1. Let the C# program collect the the data that should be plotted; perhaps by doing queries to a database.
  2. Let the program write both the gnuplot control file and the gnuplot data file.
  3. Let (as in the example above) the control file point gnuplot to the data file.
  4. Let the program 'call' the operating system asking it to start a gnuplot session and hand it over the control file.
Step 4. is demonstrated with the following C# code (run this as a console app):

CAREFUL!!! Since your instructor has no control over where you store the gnuplot control and data file on your system, you must make SURE(!!!!) that the references to those files—both in your program and in the gnuplot control file—are correct. If you point gnuplot to the wrong file or somewhere into oblivion, things will not work correctly.


class gnuplot
{
  static void Main()
  {
    string control_file = "z:\\gnuplot_controlfile.txt";
    string winroot;
    string strCmdLine;
    string strCmdParam;

    winroot = System.Environment.GetEnvironmentVariable("windir");
    strCmdLine = winroot + "\\system32\\cmd";
    strCmdParam = "/C C:\\\"Program Files (x86)\"\\gnuplot\\bin\\pgnuplot /noend " + control_file;

    try { System.Diagnostics.Process.Start(strCmdLine, strCmdParam); }
    catch(System.Exception ex) { System.Console.WriteLine(ex.Message); }
  }
}


Notice, first of all, how short the program is. The reason for this is that all the data to be plotted already exist in the gnuplot data file and all the plotting logic already exists in the gnuplot control file (in real life your program would write both these files on-the-fly). Furthermore, gnuplot will take care of all the plotting for you.

Note: despite its brevity, this is 'dense' code. It does a lot in just a few lines; so here's a bit of explanation:

  1. The string winroot is set to the root of your operating system using the system's environment variable windir (you do not have to set this variable; it's part of your OS. You can check it yourself (Control Panel-->System-->Advanced Settings-->Environment Variables).
  2. Next, we build the OS command to start a Windows command-line interpreter (winroot + \\system32\\cmd).
  3. Next, we set the string strCmdParam to the actual command we want to hand over to the Windows command interpreter (the /C option says that a command will follow).

    Notice how we specify a command to start gnuplot (pgnuplot), handing it over the gnuplot_controlfile.

    Notice how since the file path contains spaces in "Program Files (x86)" (a very nasty habit that should be discouraged!), we must include the folder name in double quotation marks (good exercise: try deciphering the command string with its double quotes and backslashes; it's a little tricky).
  4. Finally, we call the OS (System.Diagnostics.Process.Start) asking it to start the Windows command line interpreter (strCmdLine) passing it the actual command (strCmdParam).

In summary, from out of C# we build a command to be executed by the OS command-line interpreter, then call the OS to start the command-line interpreter, handing it over the command to run inside the command-line interpreter... Makes sense?

Note also that the file names in the code above are hardwired and that in practice, you most likely want to create them on-the-fly, passing them around as strings. Note also that the gnuplot_datafile referenced in the gnuplot_controlfile must exist(!) and must be referenced with a full path so that gnuplot can actually find it (this might be a bit of a puzzle if you're not used to addressing files using the operating system).

Compile and run this code.

Also, in the actual plot, note the gap in the curve for variable 'third.' Compare this with the NaN (Not a Number) entries in the gnuplot_datafile.

Note that when gnuplot starts, it also starts a gnuplot interpreter window. Although in our case we'll very likely not make much use of this, you can use the menus in this window to fine tune the graph. You can also issue additional gnuplot commands through the interpreter. For instance, try running the following commands in the gnuplot interpreter window:

Note that when we invoke gnuplot through a console application, gnuplot gets started from a Windows command shell that stays on the screen until we quit gnuplot. However, if you instead use the techniques learned in the lesson on VC#.Net Visual Programming and launch the application from a Windows Forms application, the command-line window does not show at all.

 

Now let's see if we can display the same data in a Web browser using the Google Chart API:

class GoogleChart
{
    //setup configuration parameters

    //  !!! --ATTENTION-- !!!
    // This program tries to write files to the Z:\public_html folder.
    // So, make sure that you have that folder.

    //Set up some constants
    const int WIDTH = 1000;   //width of the plot in pixels
    const string FILENAME = "a_google_chart.html";
    const string HTML_FILE_WINDOWS_ONID = "z:\\public_html\\" + FILENAME;
    const string DATAFILE = "z:\\gnuplot_datafile.txt";

    static string userName = System.Security.Principal.WindowsIdentity.GetCurrent().Name;
    static string[] nameparts = userName.Split('\\');
    static string HTML_FILE = "http://people.oregonstate.edu/~" + nameparts[1] + "/" + FILENAME; //file we'll load into the browser

    static void Main(string[] args)
    {
        //Declare two streams (FileStream) for the input and output files:
        System.IO.FileStream InFile = null;
        System.IO.FileStream OutFile = null;

        //Declare StreamReader and StreamWriter for reading and writing:
        System.IO.StreamReader Reader;
        System.IO.StreamWriter Writer;

        //Open the streams by hooking them up to the files:
        try
        {
            InFile = new System.IO.FileStream(DATAFILE, System.IO.FileMode.Open, System.IO.FileAccess.Read);
            OutFile = new System.IO.FileStream(HTML_FILE_WINDOWS_ONID, System.IO.FileMode.OpenOrCreate, System.IO.FileAccess.Write);
        }
        catch (System.Exception e)
        {
            System.Console.WriteLine("Error... " + e.Message);
            System.Environment.Exit(1);
        }

        //Connect the StreamReader and StreamWriter to the streams:
        Reader = new System.IO.StreamReader(InFile);
        Writer = new System.IO.StreamWriter(OutFile);

        //start the HTML file
        Writer.WriteLine("<html><head>");
        
        //Start the Google chart script
        Writer.WriteLine("<script type=\"text/javascript\" src=\"https://www.google.com/jsapi\"></script>"); 
        Writer.WriteLine("<script type=\"text/javascript\">"); 
        Writer.WriteLine("google.load(\"visualization\", \"1\", {packages:[\"corechart\"]});");
        Writer.WriteLine("google.setOnLoadCallback(drawChart);"); 
        Writer.WriteLine("function drawChart()"); 
        Writer.WriteLine("{"); 
        Writer.WriteLine("var data = google.visualization.arrayToDataTable("); 

        Writer.WriteLine("[");
        Writer.WriteLine("['Time', 'First', 'Second', 'Third', 'Fourth', 'Fifth', 'Sixth', 'Seventh'],");

        //read the data file into a string
        string file_read = Reader.ReadToEnd();

        //replace NaN (Gnuplot) with 'null' (Google Chart)
        string file_content = file_read.Replace("NaN", "null"); 

        //split the file content into an array of strings; split on the newline character
        string[] file_strings = file_content.Split('\n');
        int n_of_strings = file_strings.GetLength(0) - 1;
        for (int i = 0; i < n_of_strings; i++)
        {
            //data in data file:
            //29/01/2011*15:00 19.9669 19.8084 19.5332 21.0978 1.0758 21.7414 22.964 25.0533
            Writer.Write("[");
            
            //split the line into columns over the space character
            string[] reads = file_strings[i].Split(' ');
            int n_of_cols = reads.GetLength(0) - 1;
            for (int j = 0; j < n_of_cols - 1; j++)
            {
              //convert the Gnuplot date into a Google chart date (j == 0)
              //Google chart date format: new Date(yyyy, mm,  dd, hh, mi)
                if (j == 0)
                {
                    char[] date_str = reads[j].ToCharArray(0, 16);
                    string day = new string(date_str, 0, 2);
                    string month = new string(date_str, 3, 2);
                    string year = new string(date_str, 6, 4);
                    string hour = new string(date_str, 11, 2);
                    string minute = new string(date_str, 14, 2);
                    Writer.Write("new Date(" + year + "," + month + "," + day + "," +
                                               hour + "," + minute + "), ");
                }
                else
                {
                    if (j < n_of_cols - 2)
                        Writer.Write(reads[j] + ", ");
                    else
                        Writer.Write(reads[j]);
                }
            }
            
            if (i < n_of_strings - 1)
              Writer.Write("],");
            else
              Writer.Write("]");
            Writer.WriteLine();
        }
        Writer.WriteLine("]);");

        //add the remainder Google chart stuf
        Writer.WriteLine("var options ="); 
        Writer.WriteLine("{"); 
        Writer.WriteLine("vAxis: {title: 'Temperature',  titleTextStyle: {color: 'blue'}},");
        Writer.WriteLine("legend: {position: 'none'},");
        Writer.WriteLine("hAxis: {title: 'Time',  titleTextStyle: {color: 'blue'}}"); 
        Writer.WriteLine("};"); 

        Writer.WriteLine("var chart = new google.visualization.LineChart(document.getElementById('chart_div'));"); 
        Writer.WriteLine("chart.draw(data, options);"); 
        Writer.WriteLine("}");
        Writer.WriteLine("</script>");
    
        //end the HTML file, including the chart div where the chart will go
        Writer.WriteLine("</head><body>" +
                         "<div id=\"chart_div\" style=\"width: 900px; height: 500px\"></div>" + 
                         "</body></html>");
        Writer.Close();
   
        //call the OS to display a_google_graph.html; it'll call the default browser
        try { System.Diagnostics.Process.Start(HTML_FILE); }
        catch (System.Exception ex) { System.Console.WriteLine(ex.Message); }
    }
}

Notice that just as in the case of the Monty Hall code earlier, the Google Chart plotting code is written in JavaScript. Therefore, our C# program actually writes the JavaScript code to the HTML file, which is then loaded into the Web browser, whereupon the Web browser executes the JavaScript code.

You can easily find the JavaScript code in the HTML page by reviewing its source code (right-click on page in browser --> Page Source) Look for the <script> tag.

 

To practice some of this, try writing a tiny Windows application with a form containing but a single button. Define an event handler for this button and let the event handler call gnuplot.

Next, try and see if you can make your program not only call gnuplot but also, prior to calling gnuplot, write the control file and data file; e.g., make the program write a data file with 100 random numbers, the numbers 1-100, or any other series of numbers. Hint: to make a bare bones plot, the gnuplot data file must only contain a bunch of numbers, one per line, and the gnuplot control file must only contain the plot your_datafile command. Give it a shot!

To top it all off, make an Access database with a few numbers in a column and write a program that connects to the database, does a query to get the numbers out, then writes them to the gnuplot datafile, then writes the gnuplot control file and finally calls gnuplot.

Want to see another cool little thing? Ignore the gnuplot datafile altogether and plot a mathematical function directly from the control file. All the control file needs to contain is one of the following strings: plot(sin(x)) or plot(tan(x), or plot 1. Experiment a little; see what happens.

Finally, gnuplot is a powerful(!!) plotting package that can handle pretty much any sort of plot you can think of. To acquaint yourself with it, refer to the on-line gnuplot manual.