Plot2D.frink

Download or view Plot2D.frink in plain text format


/*
   This is a simple but rather interesting program that graphs equations.
   You enter equations in terms of x and y, something like one of the
   following:
  
    y = sin[x]
  
    x^2 + y^2 = 81
  
    y cos[x] = x sin[y]
  
   This version of the program can also graph INEQUALITIES, which have
   less-than or greater-than symbols instead of just equals.
  
   For example, try
  
   abs[y^2 + x^4 - 1] < cos[x]
   
   This uses a recursive method to subdivide and test rectangles.
  
   This version of the program adds automatic generation of grid lines and
   axis labels using the Grid.frink sample library.
*/

   
use Grid.frink

class Plot2D
{
   // Boundaries of the plot.  These can be changed by calling setBounds.
   var xmin = -10
   var xmax =  10
   var ymin = -10
   var ymax =  10

   // Change the doublings to vary the number of pixels.  This is the number
   // of doublings, so if the number is 10 we have 2^10=1024 doublings for
   // a resolution of 1024x1024.  (That is over a million pixels!  Don't
   // be surprised that graphing at that resolution takes a long time!)
   // Be warned that increasing the doublings by 1 makes 4 times as many pixels!
   var doublings = 10

   /** Plots the function (specified as a string) and returns a VoxelArray. */
   plot[func] :=
   {
      hasInequality = false
      certEq = undef
      lasteq = certFunc = func

      g = new graphics

      // If there's an inequality, let's make a test equation to see if we can
      // plot an entire rectangle using the "CERTAINLY" comparators.
      if func =~ %r/([<>]|!=)/
      {
         hasInequality = true
         g.antialiased[false]
         certFunc =~ %s/<=/ CLE /g  // Replace <= with certainly less than or equals
         certFunc =~ %s/>=/ CGE /g  // Replace >= with certainly greater than or equals
         certFunc =~ %s/</ CLT /g   // Replace <  with certainly less than
         certFunc =~ %s/>/ CGT /g   // Replace >  with certainly greater than
         certFunc =~ %s/!=/ CNE /g   // Replace = with certainly not equals
         certFunc =~ %s/=/ CEQ /g   // Replace = with certainly equals
         certEq = parseToExpression[certFunc]
      }

      // These replacements turn normal comparator and equality tests into
      // "POSSIBLY EQUALS" tests.
      func =~ %s/<=/ PLE /g  // Replace <= with possibly less than or equals
      func =~ %s/>=/ PGE /g  // Replace >= with possibly greater than or equals
      func =~ %s/</ PLT /g   // Replace <  with possibly less than
      func =~ %s/>/ PGT /g   // Replace >  with possibly greater than
      func =~ %s/!=/ PNE /g   // Replace = with possibly not equals
      func =~ %s/=/ PEQ /g   // Replace = with possibly equals
      eq = parseToExpression[func]

      // Change the last number to vary the resolution.  This is the number
      // of doublings, so if the number is 10 we have 2^10=1024 doublings for
      // a resolution of 1024x1024.
      testRect[xmin, xmax, ymin, ymax, g, eq, certEq, doublings]

      grid = new Grid
      grid.auto[g]
      g.add[grid.getGrid[]]

      return g
   }

   /* Recursive function to test an interval containing the specified bounds.
      If no possible solution exists, the recursion halts.  If a possible
      solution exists, this breaks it down into 4 sub-rectangles and tests each
      of them recursively.  level is the maximum number of levels to split, so
      the total resolution of the final graph will be 2^level.
   */

   testRect[x1, x2, y1, y2, g, eq, certEq, level] :=
   {
      nextLevel = level - 1
      x = new interval[x1, x2]
      y = new interval[y1, y2]
      
      // Test the rectangle.  If it possibly contains solutions, recursively
      // subdivide.
      res = eval[eq]
      
      if res or res==undef
      {
         if (nextLevel >= 0)
         {
            // Do we have inequalities and a CERTAINLY test?
            if (certEq != undef)
               certRes = eval[certEq]

            if certRes == true
            {
               // If the entire rectangle is a solution, fill the rectangle
               // and stop further recursion on this rectangle.
               g.fillRectSides[x1, -y1, x2, -y2]
               return
            }

            // Further subdivide the rectangle into 4 quadrants and
            // recursively test them all
            cx = (x1 + x2)/2
            cy = (y1 + y2)/2
            testRect[x1, cx, y1, cy, g, eq, certEq, nextLevel]
            testRect[cx, x2, y1, cy, g, eq, certEq, nextLevel]
            testRect[x1, cx, cy, y2, g, eq, certEq, nextLevel]
            testRect[cx, x2, cy, y2, g, eq, certEq, nextLevel]
         } else
         if (res)             // Valid point
            g.fillRectSides[x1, -y1, x2, -y2]
         else
         {
            // Error in evaluating point, plot in red.
            g.color[1,0,0]
            g.fillRectSides[x1, -y1, x2, -y2]
            g.color[0,0,0]
         }
      }            
   }

   /** Shows an already-rendered graphics on the screen. */
   show[g] :=
   {
      g.show[]
   }

   /** Sets the bounds to be plotted. */
   setBounds[xmin, xmax, ymin, ymax] :=
   {
      this.xmin = xmin
      this.xmax = xmax
      this.ymin = ymin
      this.ymax = ymax
   }

   /** Sets the number of doublings.  The number of voxels rendered will be
       2^doublings on each side. */

   setDoublings[d] :=
   {
      doublings = d
   }
}

"Plot2D included okay."


Download or view Plot2D.frink in plain text format


This is a program written in the programming language Frink.
For more information, view the Frink Documentation or see More Sample Frink Programs.

Alan Eliasen, eliasen@mindspring.com