
// GnuplotTest.java
// Andrew Davison, ad@coe.psu.ac.th, Nov. 2022

/* Gnuplot has a tremendous range of features. In this example,
   I'm concentrating on how it can plot 3D functions -- parametric,
   surfaces, polar coordinates, etc. 

   For more conventional 2D charts, JFreeChart is a good 
   alternative, which I consider at
   http://coe.psu.ac.th/~ad/party3/jfreechart/  

   There's a gplot() function at the end of this file which
   hides some of the details of setting up gnuplot.

   Gnuplot: http://www.gnuplot.info/

   Examples:
   * https://psy.swansea.ac.uk/staff/carter/gnuplot/
   * https://web.physics.utah.edu/~detar/phys6720/resources/Gnuplot_tutorial.html
   * https://alvinalexander.com/technology/gnuplot-charts-graphs-examples/
   * http://hirophysics.com/gnuplot/gnuplot10.html
   * "Some Fun with GNUPLOT", https://soukoreff.com/gnuplot/

   Several examples come from "GnuPlot Cookbook", Lee Phillips, 
   Packt Pub, 2012.

    Usage:
      > compile GnuplotTest.java
      > run GnuplotTest
*/

import java.io.*;

import org.zeroturnaround.exec.*;


public class GnuplotTest
{

  public static void main(String[] args)
  {
    if ( ZUtils.cmdExit("gnuplot", "-V") == 0)
      System.out.println("Gnuplot installed");
    else {
      System.out.println("Gnuplot not installed");
      return;
    }

    // lots of examples; uncomment the code you want to try out...

    // running Gnuplot commands stored in a separate file...
    // ZUtils.cmdOut("gnuplot", "sinePlot.txt");
    // ZUtils.cmdOut("gnuplot", "plot3D.txt");


    // plotting a 2D function (with plot) or 3D function (with splot)
    // gplot("plot (sin(1/x) - cos(x))*erfc(x)");
    // gplot("splot sin(sqrt(x**2+y**2))");

    // drawParametric();
    // drawParametric3DLine();
    // drawParametricSurface();
    // drawPolar();
    // plotWorld();
    drawSurface();
    // saveSurface();
    // plotSurface();
    // paletteMap();
    // torus();
  }  // end of main()



  private static void drawParametric()
  // A Lissajous curve. From "Gnuplot Cookbook", ch 1
  {
    String[] cmds = { 
        "set samples 1000",
         "set parametric",
         "plot sin(7*t), cos(11*t) notitle"  };
    gplot(cmds);
  }  // end of drawParametric()


  private static void drawParametric3DLine()
  // A squiglly 3D line. From "Gnuplot Cookbook", ch 8
  {
    String[] cmds = { 
        "set samp 100",
        "set xtics .4", "set ytics .4",
        "set parametric",
        "set urange [-pi:pi]",
        "set ztics 1",
        "splot cos(u),sin(3*u),cos(5*u) lw 2"
    };
    gplot(cmds);
  }  // end of drawParametric3DLine()


  private static void drawParametricSurface()
  // From "Gnuplot Cookbook", ch 8
  {
    String[] cmds = { 
        "set param", 
        "set iso 50", "set hidd",
        "set ztics .5", "set xtics .4", "set ytics .4",
        "set urange [-pi:pi]", "set vrange [-pi:pi]", 
        "splot cos(u)*cos(v), sin(u)*cos(v), sin(u)"
    };
    gplot(cmds);
  }  // end of drawParametricSurface()


  private static void drawPolar()
  // Archimedean spiral. From "Gnuplot Cookbook", ch 1
  {
    String[] cmds = { 
       "set xtics axis nomirror",
       "set ytics axis nomirror", 
       "set zeroaxis",
       "unset border",
       "set samples 500",
       "set polar",
       "plot [0:12*pi] t" };
    gplot(cmds);
  }  // end of drawPolar()


  private static void plotWorld()
  /* From "Gnuplot Cookbook", ch 10;
     world.dat should be in the Gnuplot demos folder, but I've 
     copied it to this folder for safety's sake.
     Also see
    https://stackoverflow.com/questions/52889408/gnuplot-curve-from-file-and-parametric-sphere
  */
  {
    String[] cmds = { 
      "set mapping spherical",
      "set angles degrees",
      "set isosamples 30",
      "set xrange [-1:1]", "set yrange [-1:1]",
      "unset tics", "unset border",
      "set parametric", "set hidden3d",
      "set urange [-90:90]", "set vrange [0:360]",
      "unset key",
      "splot cos(u)*cos(v),cos(u)*sin(v),sin(u) with lines lt rgb '#dddddd', 'world.dat' with lines lt -1"
    };
    gplot(cmds);
  }  // end of plotWorld()


  private static void drawSurface()
  // 3D surface plot
  {
    String[] cmds = { 
         "set isosamples 30",
         "set hidden3d",
         "splot sin(sqrt(x**2+y**2))" };
    gplot(cmds);
  }  // end of drawSurface()



  private static void saveSurface()
  /* Another 3D surface, but one that's also saved to
     surface.png via a Gnuplot command. It's also
     possible to export an image to PNG via a menu item
     in the Gnuplot display window. 

     Note: the writing to output raises a warning in my
     anti-virus software. Allow the operation, and subsequent
     calls will not raise red flags.
  */
  {
    String[] plotLines = { 
         "set xrange[-3:7]",
         "set yrange[1:5]",
         "set isosamples 30",
         "set hidden3d",

         "set terminal push",   // save plot to png
         "set terminal png size 800,600",
         "set output 'surface.png'",
         "splot exp(-0.2*x)*cos(x*y)*sin(y)",

         "set terminal pop", "set output", // redraw in window
         "replot",
         "pause mouse close" };

    String plotFnm = ZUtils.writeToTemp(plotLines);
    
    System.out.println("Saving plot to surface.png");
    ZUtils.cmdOut("gnuplot", plotFnm);
  }  // end of saveSurface()



  private static void plotSurface()
  /* Plot a 3D surface based on data points. First the data is
     saved to a temporary file, then the filename is
     passed to Gnuplot for drawing.

     From https://web.physics.utah.edu/~detar/phys6720/resources/Gnuplot_tutorial.html
  */
  {
    // write data to a file
    String dataFnm = ZUtils.makeTemp(".txt");
    try {
      PrintWriter pw = new PrintWriter( new FileWriter(dataFnm));
      pw.printf("%d %d %.2f\n", 0, 0, 1.0);
      pw.printf("%d %d %.2f\n", 0, 2, 1.1);
      pw.printf("%d %d %.2f\n", 0, 3, 1.2);
      pw.printf("%d %d %.2f\n", 0, 4, 1.33);
      pw.println();
      pw.printf("%d %d %.2f\n", 1, 0, 0.9);
      pw.printf("%d %d %.2f\n", 1, 2, 1.06);
      pw.printf("%d %d %.2f\n", 1, 3, 1.15);
      pw.printf("%d %d %.2f\n", 1, 4, 1.22);
      pw.println();
      pw.printf("%d %d %.2f\n", 2, 0, 0.7);
      pw.printf("%d %d %.2f\n", 2, 2, 1.0);
      pw.printf("%d %d %.2f\n", 2, 3, 1.09);
      pw.printf("%d %d %.2f\n", 2, 4, 1.25);
      pw.close();
    }
    catch (IOException e)
    { System.out.println("Unable to write data to file");
      return;
    }

    // execute the necessary Gnuplot command
    gplot("splot '" + dataFnm + "' title 'test' with lines" );
  }  // end of plotSurface()


  private static void paletteMap()
  /* Plot the (x,y,z) coordinates loaded from sinePts.txt,
     but with an enhanced color palette.
     From https://psy.swansea.ac.uk/staff/carter/gnuplot/gnuplot_3d.htm
  */
  {
    String[] cmds = { 
        "set key off",
        "splot 'sinePts.txt' using 1:2:3 with points palette pointsize 3 pointtype 7"  };
    gplot(cmds);
  }  // end of paletteMap()


  private static void torus()
  /* Plot an equation for a torus. 
     From the website "Some Fun with Gnuplot", https://soukoreff.com/gnuplot/
  */
  {
    String[] cmds = { 
       "set nokey",
       "set parametric",
       "set hidden3d", 
       "set view 30",
       "set isosamples 30,20",
       "splot [-pi:pi][-pi:pi] cos(u)*(cos(v)+3), sin(u)*(cos(v)+3), sin(v)"
     };
    gplot(cmds);
  }  // end of torus()



  // -------------------- utils -----------------------


  private static void gplot(String cmd)
  // execute a single gnuplot command
  {  gplot( new String[]{ cmd });  }


  private static void gplot(String[] cmds)
  /* An array of gnuplot commands is saved to a temporary
     file and executed. An extra command, "pause mouse close",
     is added to the end so that the gnuplot window stays onscreen
     until it's close box is clicked
  */
  {
    String[] plotLines = new String[cmds.length + 1];  // one cell bigger
    System.arraycopy(cmds, 0, plotLines, 0, cmds.length);

    plotLines[cmds.length] = "pause mouse close";

    String plotFnm = ZUtils.writeToTemp(plotLines);
    ZUtils.cmdOut("gnuplot", plotFnm);
  }  // end of gplot


}  // end of GnuplotTest class