Sindbad~EG File Manager

Current Path : /usr/home/beeson/MathXpert/cgraph/
Upload File :
Current File : /usr/home/beeson/MathXpert/cgraph/draw.c

/* M. Beeson for Mathpert
Draw one graph, given the graph structure (only)
Calls indirectly on code in the files making up deval.dll and heap.dll;
and directly on graphrelation, contourplot, complex_contourplot,
graphparametric, carefulgraph, and ode, as well as
compute_singularities (which computes numerical singularities;
the symbolic singularities are already in the graph structure).
Basic drawing is performed by functions in mygraph.c
Public functions defined here:  draw, text_size, direction_field
*/
/*
4.12.90 original date
1.21.99 last modified
2.25.00 modified at line 833
5.3.00  changed CONTRAST
6.24.00  added call to DrawPaper
7.15.00  moved DrawPaper call in draw above draw_border
         modified drawaxes to ensure axes are at least as thick as the major lines of the graph paper.
4.7.06  changed ltoa to sprintf and gcvt to _gcvt
8.20.07 added heapmax and reset_heap in draw
6.10.13  modified for RIEMANNSUMS
         added code to set left and right before calling draw_riemmann
2.24.24  adapted to WebMathXpert by eliminating Windows code, adding
         include "svgGraph.h" and changing some function calls to match
2.24.24  initialized whichgraph=4 in draw, correcting an error pointed
         out by the Clang compiler in Xcode.
2.25.24  added begin_path and end_path calls in draw_axes and put_ticks_on_axes
2.27.24  called begin_path and end_path in labelaxes
2.28.24  modified placement of label in labelaxes
2.29.24  initialized whichcase to 0 insted of 4.  (How was this not a bug in
         Windows MathXpert?  It prevents tan x from grqphing correctly.)
3.12.24  corrected vertical_tick and sideways_tick
3.13.24  moved set_world below Adjust..  in preparetograph
3.22.24  modified setting of g->right just before draw_riemann call
6.29.24  made draw call preparetograph even if g->newaxes == 0, as g->xmax etc. may
         need to be adjusted.
7.1.24   changed type of xmin1 etc. to double in preparetograph.
7.9.24   modified compute_tick_gap and labelaxes to use devicewidth
7.15.24  added calls to begin_path and end_path in direction_field
7.15.24  divided deltax and deltay by 3 in direction_field
7.29.24  removed calls to xormode, not used in WebMathXpert;
         if0'd out draw_grid, not used in MathXpert.
         Deleted a move_to at line 988, which was now causing a crash because
         !path_in, but would have no effect anyway
8.8.24   fixed a typo where g->pxmin is set in drawLarge.  I think I fixed it before.
         added theDevice->pxmi n = g->pxmin; etc. in drawLarge.
9.9.24   modified label_axes to put the x-label above the axis if there's room
8.14.24  oldxmin etc. in drawLarge changed to double
9.2.24   corrected the end of drawLarge
10.21.24  Made drawLarge call draw_all if g->erase==0.
12.3.24  modified drawxticks and verticaltick to take a y-coordinate
12.4.24  removed devicewidth from compute_tick_gap
2.16.25  removed unused variables xmin, xmax, etc. in drawLarge.
3.4.25   broke draw_paths out of draw()
3.6.25   added first and last lines to preparetograph, that were previously in draw()
3.8.25   added a few lines in two places in preparetograph near set_linewidth(g->linewidth);
3.8.25   added if(index < 0)  to guard against no parameter case in sendPaths
3.8.25   removed code near " we send paths for every value of the parameter"
3.10.25  added PARAMETRIC and POLAR_CIRCULAR to draw to get sliders to work there too
*/

#include <assert.h>
#include <math.h>
#include <string.h>
#include <stdlib.h>   /* gcvt */
#include <stdio.h>    /* sprintf */

#include "rgb.h"
#include "globals.h"
#include "graphstr.h"
#include "grapher.h"
#include "heap.h"
#include "mpmem.h"
#include "dcomplex.h"
#include "newton.h"
#include "deval.h"
#include "wcolors.h"
#include "polyroot.h"
#include "sing.h"
#include "ratsimp.h"
#include "prover.h"   /* contains_existentials */
#include "grpaper.h"  /* DrawPaper             */
#include "svgGraph.h"
#include "display1.h"  /* EIGHTPOINT */
#include "textwidth.h" /* svg_text_width */
#include "cflags.h"    /* get_currenttopic() */
#include "grafinit.h"  /* nonconstant_exponent */
#include "polynoms.h"
#include "draw.h"
#include "mpdoc.h"     /* PDOCDATA    */
#include "sendDocument.h"  /* sendTitle  */

static double y_coord_of_xaxis(double,double);
static void drawaxes(graph *g);
static double compute_tick_gap(double,double,int);
static void putticksonaxes(graph *g);
static void drawxticks(double,double,double,double,double,int,double,int);
static void drawyticks(double,double,double,double,double,int,double);
static void verticaltick(double,double,double,int,int,int);
static void sidewaystick(double,double,double,double,double,double,int);
static int verysmall(double,double);
static int divides_exactly(double,double);
static void labelaxes(graph *g);
static unsigned int gridcolor(unsigned int color);
static int deBoolean(term t, term *ans, term *boundary);
static int special_singularities(graph *g);
static void set_singularities(graph *g, term sings);
static void set_jumps(graph *g, term sings);
static int evalsub(term t, term x, term *ans);
static int draw_paths(graph *g, int visibility);
/*____________________________________________________________________*/
static char decimalpoint = '.';
void set_graph_decimalpoint(char n)
{ decimalpoint = n;
}
/*____________________________________________________________________*/
/* There is a function of this name in bblock.c which is
however different, producing n digits to the right of the
decimal point.  We use this older one for graphs, because it
switches over to e-notation sooner, and 12 digit numbers are
hard to read on a graph.  */

static void my_gcvt(double x, int n, char *s)
/* convert double x to %g format using only n significant digits;
   but don't leave a terminating period like gcvt does.
   Assumes s has enough characters allocated to write an extra 0
   at the end.
 */

{ int i=0;
  if(x == 0.0)
     { strcpy(s,"0");
       return;
     }
  sprintf(s, "%.*g", n, x);   // instead of  gcvt(x,n,s);
  while( s[i] != '\0' ) i++;
  if( s[i-1] == '.' )
     { s[i-1] = decimalpoint;
       s[i] = '0';
       s[i+1] = '\0';
     }
}

/*______________________________________________________________________*/
void preparetograph( graph *g)
/* Draw the axes, labels, and ticks of a graph
provided g->newaxes != 0;  and if g->newaxes == 0,
then adjust g->xmin etc. to match the earlier graph with g->newaxes nonzero.
*/

{ int mainchoice = g->graphtype;
   if(mainchoice == POLYROOT)
      g->newaxes = 1;
  set_viewport(g->dxmin,g->dxmax,g->dymin,g->dymax);
  static double xmin1, xmax1, ymin1, ymax1;  // hold results from graphs[0]
  svgDevice *theDevice = get_device();
  g->pxmin = theDevice->pxmin;
  g->pxmax = theDevice->pxmax;
  g->pymin = theDevice->pymin;
  g->pymax = theDevice->pymax;
  set_graphbackgroundcolor(g->background);
  set_linewidth(THIN);  /* else border of 2nd graph on same axes comes out thick */
  if(g->grpaper.ncolors && g->newaxes)
    // graph paper will be used and this is the first graph on these axes
     {
       int topic = get_currenttopic();
       AdjustGraphRanges(g, topic);
       set_world(g->xmin,g->xmax,g->ymin,g->ymax);  /* set up world coordinates */
       xmin1 = g->xmin;
       xmax1 = g->xmax;
       ymin1 = g->ymin;
       ymax1 = g->ymax;
       DrawPaper(g);
     }
   else if(g->newaxes == 0 && g->whichgraph != 0 && g->grpaper.ncolors == 0 && xmin1 != xmax1)
     { // adjust g->xmax etc. to be what they are on graphs[0];
       g->xmin = xmin1;
       g->xmax = xmax1;
       g->ymin = ymin1;
       g->ymax = ymax1;
       set_world(g->xmin,g->xmax,g->ymin,g->ymax);  /* set up world coordinates */
       // set_graphpencolor(g->border);
       // filled_rect(xmin1,ymin1,xmax1,ymax1);  No, this would cover up the
       // graph paper from the first graph
       set_graphpencolor(g->graphcolor);  /* set color required for graph */
       if(mainchoice != POLYROOT)
           set_linewidth(g->linewidth);
       return;
     }
   else if (g->newaxes == 0)
     {  set_graphpencolor(g->graphcolor);  /* set color required for graph */
        if(mainchoice != POLYROOT)
           set_linewidth(g->linewidth);
         return;
     }
   else
     {  set_world(g->xmin,g->xmax,g->ymin,g->ymax);  /* set up world coordinates */
        set_graphpencolor(g->border);
        filled_rect(g->xmin,g->ymin,g->xmax,g->ymax);
     }
  if(g->border != g->background) /* then draw a border around the viewport */
     { set_graphbordercolor(g->border);
        /*  Next outline the viewport  */
       draw_box(g->xmin, g->xmax, g->ymin , g->ymax);
     }
  drawaxes(g);
  if(g->ticks)
     putticksonaxes(g);
  if(g->labels)
     labelaxes(g);
  g->crosshairsflag = 0;         /* after redrawing the axes, crosshairs invisible */
  if(g->dfield_applicable && g->dfield==1)
        /* show direction field  */
     { g->dfield = 0; /* reset to zero or next line won't do anything */
       direction_field(g,1);  /* will set g->dfield back to 1         */
     }
  set_graphpencolor(g->graphcolor);  /* set color required for graph */
  if(mainchoice != POLYROOT)
       set_linewidth(g->linewidth);
}
/*__________________________________________________________*/
void text_size(char *t, double *width, double *height)
/* return text size of string t in world coordinates */
{ long w,h;
  svgDevice *device = get_device();
  h = 12;
  w = svg_text_width(t,EIGHTPOINT);   /* width in pixels */
  *width = (device -> xpixel) *w;
  *height = (device-> ypixel) *h;
  relinquish_device();
}

/*____________________________________________________________________*/
static double y_coord_of_xaxis(double ymin,double ymax)
{ if( (0.0 <= ymax) && (ymin <= 0.0) )
     return 0.0;
  if (0.0 <= ymax)
      return (ymin + 0.003 * (ymax - ymin));
  if (ymin <=0.0)
      return (ymax - 0.003 * (ymax - ymin));
  return 0.0;   /* can't get here, but it keeps Turbo C from complaining */
}

/*________________________________________________________________*/
static void drawaxes(graph *g)
{ int i;
  double x,y,rmax;
  set_graphpencolor(g->axescolor);
  // graphpencolor is the color used by line_to, so it must be set
  // before drawing lines.
  if(g->grpaper.ncolors > 0)
     { /* use thicker axes when graph paper is present;
          at least as thick as the major lines, but certainly > 1.
          In polar graphs, thickness can be less than one.
        */
       double thickness = g->grpaper.thickness1;
       if(fabs(thickness -1) < 0.01)
          thickness = 2;
       set_linewidth(thickness);
     }
  else
     set_linewidth(THIN);
  if ( (g-> graphtype) == POLAR_CIRCULAR )
     {
       rmax = g->xmax;
       for(i=1;i<=3;i++)
          draw_circle(0,0,rmax *(i/3.0));
       begin_path();
       move_to(0.0,0.0);
       for(i=0; i<12; i++)  /* draw 12 radial lines */
          { x = rmax * cos(i*PI_DECIMAL/6.0);
            y = rmax * sin(i*PI_DECIMAL/6.0);
            line_to(x,y);
            move_to(0.0,0.0);
          }
       end_path();
       return;
     }
   /* else if it's not POLAR_CIRCULAR then draw rectilinear axes */
   /* if the axis would not be visible we draw it at the edge of the
   screen anyway */

  y= y_coord_of_xaxis(g->ymin,g->ymax);
  x= y_coord_of_xaxis(g->xmin,g->xmax);
  begin_path();
  move_to(g->xmin,y);
  line_to(g->xmax,y);  /* draw the x-axis (if visible) */
  move_to(x,g->ymin);
  line_to(x,g->ymax);  /* draw the y-axis (if visible) */
  end_path();
}
/*_____________________________________________________________________*/
static double compute_tick_gap(double min,double max, int piflag)
/* find the value of gap that puts a reasonable number of ticks between
   min and max.  Min and max are world coordinates of g->xmin and g->ymax;
   piflag tells whether ticks should be at multiples of pi or not.  If piflag,
   then min and max have already been divided by pi and the result will be
   multiplied by pi.   Devicewidth is provided so we can proceed differently
   when a three-times-larger-than-viewport graph is being computed; in that
   case devicewidth will be three times larger too, so certainly more than 1.
*/
{
  double pixelwidth = max-min;
  max -= pixelwidth/3.0;
  min += pixelwidth/3.0;
  double interval = fabs(max - min)/2.0;
  double factor;
  int n;
  double k;
  if(interval == 0.0)
     assert(0);
  if(interval > 1.0)
     n = (int) log10(interval);
  else
     n = - (int)(- log10(interval)) -1;
  k = pow(10.0, (double) n);
  factor = interval/k;
  if( factor < 1.1 )
     return k/2.0;
  if( factor < 3.0 )
     return k;
  if( factor < 6.0 )
     return (piflag && k < 1 ) ? 2.5*k : 2*k;
  return 5*k;
}

/*______________________________________________________________________*/
/*  Draw and label ticks on the coordinate axes, or at the edge of the graph
if the axes are not visible. */
/*  Assumes pen color is correctly set before this function is called */
/*  But sets the TEXT color itself */
/*  Does not assume anything about the pen location */
/*  Does not put a tick or label at origin; does not put a tick or
    label at the ends of the axes where they would fight for space with
    an axes label, but if no axes labels are to be drawn, it does. */
/*  8.17.90 removed 'aspect' from formulae for tickheight and tickwidth
    as Third Wave does not use aspect ratios */

static void putticksonaxes(graph *g)
{ double xgap,ygap;
  double tickheight;       /* in world coordinates */
  double tickwidth;
  set_graphtextcolor(g->axescolor);
  tickheight = (g->ymax - g->ymin) * 0.01  /(g->dymax - g->dymin);
         /*  this is 1/100 of the WIDTH (not height) of the whole screen
             regardless of the viewport size; WIDTH since device
             coordinates already are less than 1 by the aspect ratio.
         */
  tickwidth  = (g->xmax - g->xmin) * 0.01  /(g->dxmax - g->dxmin);
         /*  this is 1/100 of the height (not width) of the whole screen */
         /*  Actually it's 1/100 of the maximum dimension; on portrait-oriented
             paper it would be the height */
  if(g->ticks == 1)
     xgap = compute_tick_gap(g->xmin,g->xmax,0);
  else
     { assert(g->ticks == 2); /* want multiples of pi */
       xgap = PI_DECIMAL * compute_tick_gap(g->xmin/PI_DECIMAL, g->xmax/PI_DECIMAL,1);
     }
  ygap = compute_tick_gap(g->ymin,g->ymax,0);
  drawxticks(g->xmin,g->xmax,g->ymin,g->ymax,tickheight,g->labels,xgap,g->ticks);
  drawyticks(g->xmin,g->xmax,g->ymin,g->ymax,tickwidth,g->labels,ygap);
}

/*___________________________________________________________________*/
static void drawxticks(double xmin,double xmax,double ymin,double ymax,
           double tickheight,int end, double gap, int piflag)
/*  put ticks on the x-axis (if y == 0, or the line at coordinate y if y is not zero)
at spacing gap.  Don't draw a tick at the right
end of the axis (where it would conflict with the axis label) unless
end == 2 (which means no labels on the axis).  If piflag is 2,
the ticks should be labelled as multiples of pi.
*/

{ int nleft,nright;   /* nleft *gap = leftmost tick; nright *gap = rightmost tick */
  int nticks;         /* total number of ticks */
  double hh,length;
  double x,y,xleft;
  int cnt;

  if ((xmin < 0.0) && divides_exactly(gap,xmin))
     { if (end == 2)
          nleft = -(int) (-xmin/gap + 0.5);
       else
          nleft = -(int) (-xmin/gap - 0.5);
     }
  else if (xmin < 0.0)
     nleft =  -(int)(-xmin/gap);
  else if (xmin > 0.0)
     nleft =  (int)(xmin /gap) + 1;
  else
     nleft = 0;
  if ((xmax > 0.0) && divides_exactly(gap, xmax))
     { if (end ==2)
          nright = (int) (xmax/gap + 0.5);
       else
          nright = (int) (xmax/gap - 0.5);
     }
  else if (xmax > 0.0)
     nright = (int) (xmax/gap);
  else if (xmax < 0.0)
     nright = -((int) (-xmax/gap) + 1);
  else
     nright = 0;
  nticks = -nleft + nright + 1;
  xleft = nleft * gap;   /* leftmost tick location */
  /* our graphs are actually 9 times bigger, 3 times as tall and wide,
     as the window to display them.  ymin and ymax here correspond to
     that larger graph. */
  double height = ymax-ymin;
  ymax -= height/3;
  ymin += height/3;
  y = y_coord_of_xaxis(ymin,ymax);
  for( cnt = 0; cnt < nticks; cnt++)
     { x = xleft + cnt *gap;
           /* Now watch out that you don't put a label like
             .0000000333 E-17  on the origin */
       if( ! verysmall(x, xmax-xmin) )
          {  /* then draw and label this tick */
            text_size("n",&length, &hh);
            if(y - ymin >= tickheight + 1.2 * hh ) /* can put labels below ticks */
                verticaltick(tickheight,x,y,4,0,piflag);
            else
                verticaltick(tickheight,x,y,4,1,piflag);
          }
     }
}

/* __________________________________________________________*/
static void drawyticks(double xmin,double xmax,double ymin,double ymax,
           double tickwidth,int end, double gap)
/*  put ticks on the y-axis (or the right or left edge of the graph)
at spacing 'gap'.  Don't draw a tick at the top
end of the axis (where it would conflict with the axis label) unless
end == 2 (which means no labels on the axis).
*/

{ int nleft,nright;   /* nleft  *gap = lowest tick, nright *gap = highest tick */
  int nticks;         /* total number of ticks */
  double x,y,ybot;
  int cnt;

  if ((ymin < 0.0) && divides_exactly(gap,ymin))
     { if (end == 2)
          nleft = -(int) (-ymin/gap + 0.5);
       else
          nleft = -(int) (-ymin/gap - 0.5);
     }
  else if (ymin < 0.0)
     nleft = - (int)(-ymin/gap);
  else if (ymin > 0.0)
     nleft = (int)(ymin/gap) + 1;
  else  nleft = 0;
  if ((ymax > 0.0) && divides_exactly(gap, ymax))
     { if (end ==2)
          nright = (int) (ymax/gap + 0.5);
       else
          nright = (int) (ymax/gap - 0.5);
          }
  else if (ymax > 0.0)
     nright = (int) (ymax/gap);
  else if (ymax < 0.0)
     nright = -((int)(-ymax/gap)+1);
  else nright = 0;
  nticks = -nleft + nright + 1;
  ybot = nleft * gap;   /* lowest tick location */
  double width = xmax-xmin;
  // Next lines account for the fact that the graph is really 3 times wider than it appears
  xmin += width/3;
  xmax -= width/3;
  x = y_coord_of_xaxis(xmin,xmax);  /* x coord of y axis actually */
  for( cnt = 0; cnt < nticks; cnt++)
     { y = ybot + cnt *gap;
       /* Now watch out that you don't put a label like
          .0000000333 E-17  on the origin */
       if( ! verysmall(y, ymax-ymin) )
          {  /* then draw and label this tick */
             sidewaystick(xmax,ymin,ymax,tickwidth,x,y,4);
          }
    }
}
/* __________________________________________________________*/
static void pi_label(double x, char *label, int flag)
/* get a nice label for x*pi.  It is assumed that x is a
nonnegative number which is a
multiple of 'gap', where 'gap' is either less than 1,
and 1, 2.5, or 5 times a power of ten, or 'gap' is more
than 1, and either 1, 2, or 5 times a power of ten.
Label is assumed to point to enough space to hold the answer.
flag is the number of significant digits if we have to
use decimal numbers.
*/

{ long kk;
  if(x < 0.0)  /* avoid having to treat -1 as a special case. */
     { label[0] = '-';
       pi_label(-x,label+1,flag);
       return;
     }
  /* is x an integer? */
  if(nearint(x,&kk))
     { if(kk==1)
         strcpy(label,"\\pi");
       else
         { sprintf(label,"%ld",kk);
           strcat(label,"\\pi");
         }
       return;
     }
  /* Is x a multiple of 1/2 ? */
  if(nearint(2*x,&kk))
     { if(kk==1)
          strcpy(label, "\\pi/2");
       else
          { sprintf(label,"%ld",kk);
            strcat(label,"\\pi/2");
          }
       return;
     }
  /* OK, how about a multiple of 1/4? */
  if(nearint(4*x,&kk))
     { if(kk==1)
          strcpy(label, "\\pi/4");
       else
          { sprintf(label,"%ld",kk);
            strcat(label,"\\pi/4");
          }
       return;
     }
  /* Give up and use decimals */
  my_gcvt(x,flag,label);
  strcat(label, "\\pi");
}
/* __________________________________________________________*/
/* draw and label a vertical tick */
/* presumes pen is on the axis at the location of the tick to be drawn */
/* does not reposition the pen before exiting */
/* x is the number to be used for a label */
/* provided flag is non-zero; flag is the number of
   significant digits desired in the labels. */
/* height is the desired height of the tick in world coordinates */
/* if above is nonzero, the labels go ABOVE the ticks, else they go below */
/* if piflag is 2, then the labels are supposed to be multiples of pi_term,
   not decimals. */

static void verticaltick(double height, double x, double y,int flag, int above, int piflag)
/* draw a tick at coordinate x,y  */
{ double length;    /* length of label string in world coordinates */
  double labelheight;
  char label[32];  /* that better be enough room! */
  text_size("h",&length,&labelheight);
  begin_path();
  move_to(x,y);
  line_rel(0,height);
  move_rel(0,-height);
  line_rel(0,-height);  /* draw the part below the x-axis */
  /*  Now:  do we want the label above or below the axis?  Below, unless
  it won't fit, in which case, above */
  if(above)
     move_rel(0,2* height + 0.2 * labelheight);  /* go to a little above the tick */
  else
     move_rel(0,- 1.2 * labelheight);    /* go a little more than one line below the tick */
  if(flag != 0 && piflag!=2)
     { my_gcvt(x,flag,label);
       text_size(label,&length,&labelheight); /* get size in world coordinates */
       move_rel(-length/2, 0.0);
       end_path();
       text(label);
     }
  else if(flag != 0)  /* && piflag==2 */
     { /* gap is 1, 2, or 5 times a nonnnegative power of ten times pi_term,
       or else is 1, 2.5, or 5 times a negative power of ten */
       x /= PI_DECIMAL;
       pi_label(x,label,flag);
       end_path();
       text(label);
     }
}
/*______________________________________________________________________*/

static void sidewaystick(double xmax,double ymin,double ymax,
                  double width, double x, double y, int flag)
{ char label[32];
  double textheight,textwidth;
  double xlabel,ylabel;
  begin_path();
  move_to(x,y);
  line_rel(width,0.0);
  move_rel(-width,0.0);
  line_rel(-width,0.0);
  if(flag != 0)
     { my_gcvt(y,flag,label);
       text_size(label,&textwidth,&textheight);

       /* Now we want to move down textheight/2, unless that puts us off the
       bottom; and be sure we don't go off the top either. */

       ylabel = y - textheight/2.0;
       if( ylabel - 0.1 * textheight < ymin)
          ylabel = ymin + 0.1*textheight;
       if( y + 0.6* textheight > ymax)
          ylabel = ymax - 1.1 * textheight;

 /* go one tick width to the right of the tick, unless that is too far right */
      xlabel = x + 2*width;
      if( xlabel + textwidth + (0.1/strlen(label)) * textwidth > xmax)
           /* then label would go off the graph or into the border */
           /* so put it on the LEFT side of the axis */
         xlabel = x - textwidth - width - (0.1/strlen(label))*textwidth;
      move_to(xlabel,ylabel);
      end_path();
      text(label);
     }
}
/*_____________________________________________________________________*/

/* divides_exactly and verysmall are intended to prevent numbers like
   1.000030004432 E-17 from being printed out where 0.0 should be
   due to inaccuracies in floating-point computation.  (This tends to
   happen if a numerical coprocessor is not present.)  */

static int verysmall(double y, double s)  /* y is very small compared to s */
{  double z = y/s;
   if ( (z < 0.0001) && (-0.0001  < z) )
      return 1;
   return 0;
}
/*_____________________________________________________________________*/
static int divides_exactly(double s, double b)  /* s for small, b for big */
/*  it is not required that small < big */
/*  test whether, for practical purposes (such as deciding whether or not
to place a tick label at the end of the axes), big is an exact integer
multiple of small.  Returns 1 for answer yes, it divides exactly; 0 means no. */
/*  should not be called if small==0.0; if it is, it returns 0; */

{ double x;
  int y;
  if (s==0.0)
     return 0;
  x = b/s;
  y = (int) x;
  if(y==0) /* then big < small in spite of the variable names */
    { if(verysmall(s-b, s))
         return 1;
      else
         return 0;
    }
  if( verysmall(1.0 - x/y, 1.0) )
     return 1;
  return 0;
}


/*______________________________________________________________________*/
/* produces numerical or symbolic labels.  Value of
   g->label value of 3 produce labels given by g->xmax times PI */

static void labelaxes(graph *g)
{  char xmaxlabel[32];
   char ymaxlabel[32];
   char xminlabel[32];
   char yminlabel[32];
   double devicewidth = g->dxmax-g->dxmin;
   double xmin, xmax, ymin,ymax;
   xmin = g->xmin;
   xmax = g->xmax;
   ymin = g->ymin;
   ymax = g->ymax;
   if(devicewidth > 1)
      { /* we're drawing a graph three times bigger than the viewport */
        double worldwidth = xmax-xmin;
        double worldheight = ymax-ymin;
        xmin += worldwidth/3.0;
        xmax -= worldwidth/3.0;
        ymin += worldheight/3.0;
        ymax -= worldheight/3.0;
      }
   svgDevice *device = get_device();
   double xpixel = device->xpixel;
   relinquish_device();
   double width,height;
   double x,y;
   set_graphtextcolor(g->axeslabelcolor);
   if(g->labels == 1)   /*  then produce numerical labels */
    {
      my_gcvt(xmax,4, xmaxlabel);
      my_gcvt(ymax,4, ymaxlabel);
      my_gcvt(xmin,4, xminlabel);
      my_gcvt(ymin,4, yminlabel);
    }
   if(g->labels == 3)   /*  then produce x-labels which are multiples of pi */
    {
      my_gcvt(xmax,4, xmaxlabel);
      my_gcvt(ymax,4, ymaxlabel);
      my_gcvt(xmin,4, xminlabel);
      my_gcvt(ymin,4, yminlabel);
      strcat(xmaxlabel,"\\pi");
      strcat(xminlabel,"\\pi");
    }
   if(g->labels == 2)   /*  then produce symbolic labels */
    { strcpy(xmaxlabel,g->xvariable);
      strcpy(ymaxlabel,g->yvariable);
    }
   /* now label the positive y-axis */
    text_size(ymaxlabel,&width,&height);
    x = y_coord_of_xaxis(xmin,xmax);  /* actually x_coord_of_yaxis */
    if(x-width > g->xmin)
        x -= 2 *width;
    else
        x += 3*xpixel;
    
    begin_path();
    move_to(x,ymax - (1.6)*height);
    end_path();
    text(ymaxlabel);       /* write the label on the + y axis */
       
   /* now label the positive x-axis (except for polar-circular graphs) */
    if (g->graphtype != POLAR_CIRCULAR)
       { text_size(xmaxlabel,&width,&height);
         y = y_coord_of_xaxis(ymin,ymax);
         x = xmax- width - 8*xpixel;
              /* this leaves some space at the right side blank
                 so the label doesn't touch the graph's border */
         if (y > ymax - 1.1 * height)
            y = ymin - 0.1 * height;   // put it below the axis, not enough room above.
         else
             y += 0.9 * height;  // put it above the axis
         begin_path();
         move_to(x,y);
         end_path();
         text(xmaxlabel);
       }

/* Now in case of numerical labels, label the negative x-axis */
/* y still has the correct value from labelling the positive x-axis */

    if ((g->graphtype != POLAR_CIRCULAR) && ( g->labels == 1))
       { text_size(xminlabel,&width,&height);
         begin_path();
         move_to(xmin + width*(0.1/ strlen(xminlabel)),y);
         end_path();
         text(xminlabel);
       }

/* Now in the case of numerical labels, label the negative y-axis */

    if ((g->graphtype != POLAR_CIRCULAR) && ( g->labels == 1))
       { text_size(yminlabel,&width,&height);
         x = y_coord_of_xaxis(xmin,xmax);  /* actually x_coord_of_yaxis */
         x = x - (width/2.0);
         if( x < xmin)
            x=xmin;                    /* make sure it fits on left */
         if( x + (width/2.0) > g->xmax)
            x -= xmax -x;  /* make sure it fits on right */
         begin_path();
         move_to(x,ymin + (0.1)*height);    /* leave room for bottom border*/
         end_path();
         text(yminlabel);                      /* write the label */
       }
}
/*______________________________________________________________________*/
/* draw one 2D graph, assuming the 'device' has been initialized
by set_svgDevice so the computer is ready to draw.  Will draw axes or
not according to the field 'newaxes' of the graph structure.  Will erase
old graph according to the field 'erase'.
  In the case of solving ODE's, we use the same 'graph' data structure,
but make use of the fields 'slist' and 'elist', which are dynamic arrays of
terms of dimension given by the fields 'dimslist' and 'dimelist',  to hold
the arrays of dependent variable names and right-hand sides, and the
dynamic array 'initial_values' to hold the starting values.
*/

int draw( graph *g)
/* The return value is zero for success, and nonzero for errors:
  1:  trouble computing singularities.  The graph will still be drawn.
  2:  function nowhere defined on the interval. (This isn't really an
      error, but it's treated as such--the user will get a message.)
  3:  trouble computing polyroots
  4:  trouble solving differential equations numerically
  5:  no longer used
  6:  trouble computing jumps
  7:  trouble computing both jumps and singularities
  8:  function nowhere defined (on any interval)
      This value is never returned by draw; draw only returns 2, but
      then the calling function in graphwin.c detects the situation and
      it returns 8.
  9:  correct symbolic singularities, but too hard to compute numerical values
 10:  function defined only at isolated points in the interval, no graph
      visible.  This can be due to too many BADVAL values, as in graphing
      1/x^1000000000.
 11:  out of buffer space in graph_relation
*/

{
   g->update = 0;  /* it won't need updating after we draw it */
                   /* g->update is only for 3d graphs anyway so this is obsolete code */
   preparetograph(g);
   return sendPaths(g);
}
/*______________________________________________________________________________*/
int sendPaths(graph *g)
/* This function sends <path>, <circle>, and <title> elements,
so it can be used to respond to an Ajax message; it does not send
the whole document.
*/
{
   if(g->whichgraph)
      set_graphpencolor(g->graphcolor);  /* not the first graph on these axes; so
                  title has done nothing and color is still set to the
                  color for the first graph */
   int index = g->activeparameter;
   if(index < 0) // no parameters
      return draw_paths(g,1);  // just call it once, with visibility 1 so it will show
   parameter p = g->parameters[index];  // the active parameter itself
   if(    // we send paths for every value of the parameter even if the parameter doesn't occur in this graph's function
          // for example if comparing sin(ax) and cos x, because it takes no time and is too hard to
          // have both paths with "param" labels and without.
      g->graphtype != ORDINARY  &&
      g->graphtype != PARAMETRIC &&
      g->graphtype != POLAR_CIRCULAR &&
      g->graphtype != POLAR   // I don't think this is used in WebMathXpert anyway
     )
      return draw_paths(g,1);  // just call it once, with visibility 1 so it will show
   else
     { // call draw_paths once for each value of the active parameter from rangemin to rangemax
       double z;
       double initialvalue = *(p.addr);
       int visibility;
       int err0 = 0;
       int err;
       double sliderchange = (p.rangemax-p.rangemin)/50.0;
       int graphnumber = g->whichgraph;  // so g = graphs[graphnumber]
       for(z = p.rangemin; z <= p.rangemax+ (sliderchange/5); z += sliderchange)
          { *(p.addr) = z;  // set the value of the parameter
            if( fabs(z-initialvalue) < sliderchange/4)
               { visibility = 1;
               // printf("draw.c line 848 making one visible\n");
               }
            else
               visibility = 0;
            err = draw_paths(g, visibility);
            if(fabs(z-initialvalue) < sliderchange/4)
               err0 = err;  // only show an error message for the initially-visible graph
            sendTitle(g,graphnumber,&p);
          }
       *(p.addr) = initialvalue;  // reset it where it was to begin with
       // Thus we haven't made a net change in the parameter value.
       return err0;
     }
}
/*_______________________________________________________________________*/
static int draw_paths(graph *g,int visibility)
// draw the actual SVG for the graph lines,
// assuming the graph paper, axes, ticks, and labels are already drawn.
// if visibility != 0 give them style display:block, else display:none
{  int k,err;
   int nroots;
   int jumperror = 0;
   int whichcase = 0;  /* 1 = numerical singularities;
                      2 = usable symbolic but not numerical singularities;
                      3 = no singularities;
                      4 = symbolic singularities too hard to compute, reason unknown
                      5 = symbolic singularities computable but contain two
                          existential parameters
                      6 = symbolic singularities computable, only one existential
                          parameter, but too hard to solve for numerical singularities.
                     (In cases 4,5,6 we can't draw them right)
                   */
   svgDevice *device = get_device();
   int mainchoice = g->graphtype;
   switch(mainchoice)
      { case ORDINARY:
           /* even if comparing several on same axes */
           /* First determine which case we are in regarding singularities
           (see whichcase above for the possibilities) */

           if( g->dimslist == 0 ||
               (g->dimslist==1 && FUNCTOR(g->slist[0]) == FALSEFUNCTOR)
             )
              whichcase = 3;  /* no singularities */
           else if( g->tempsing ||
                   (g->dimslist == 1 && FUNCTOR(g->slist[0])==UNDEFINED)
                  )
              { if(get_nparameters() > 0)
                     /* We may still be able to compute the singularities
                        using specific values of the parameters.  This way,
                        they have to be computed again every time 'draw' is
                        called, but at least the graph is correct. */

                     /* To test this code try graphing 1/sqrt(x^2-a) + 1/sqrt(x^2-ax+1);
                        for each value of a, Mathpert can calculate the singularities,
                        by applying reduce_to_rational to each summand, but it can't
                        calculate the singularities with parameter a.
                     */
                   whichcase = special_singularities(g); /* does jumps too if possible */
                else
                   whichcase = 4;  /* symbolic singularities too hard to compute */
              }

           if(whichcase != 3 && whichcase != 4)
              /* now we have symbolic singularities at least */
              { void *saveit = heapmax();
                err = compute_singularities(g);
                if(err)
                   reset_heap(saveit);
                else
                    { int mm = g->nsingularities * sizeof(double);
                      void *temp = malloc(mm);
                      memcpy(temp,g->singularities,mm);
                      reset_heap(saveit);
                      g->singularities = callocate(g->nsingularities, sizeof(double));
                      memcpy((void *) g->singularities,temp, mm);
                      free(temp);
                    }
                if(err==1)   /* numerical singularities too hard to compute */
                             /* but maybe we can still use the symbolic singularities */
                   { if(g->dimsrestrictions)
                        whichcase = 6;  /* can't handle restrictions */
                     else
                        { /* check for EXISTENTIAL parameters in the singularities formula */
                          whichcase = 2;  /* we hope it stays that way */
                          for(k=0;k<g->dimslist; k++)
                             { if(contains_existentials(g->slist[k]))
                                  { whichcase = 6;
                                    break;
                                   }
                             }
                        }
                   }
                else if (err==2)
                   whichcase = 5;  /* two existential parameters */
                else if( err == 0)
                   { if(g->nsingularities)
                        whichcase = 1;  /* numerical singularities */
                     else
                        whichcase = 3;  /* no singularities between xmin and xmax */
                   }
                else
                   assert(0);   /* all err values accounted for above */
              }
           if( g->dimjumplist != 0  /*  jumps to deal with */
               && FUNCTOR(g->jumplist[0]) != UNDEFINED
             )
              { err = compute_jumps(g);
                if(err==1)   /* numerical jumps too hard to compute */
                   { g->njumps = 0;
                     jumperror = 1;
                   }
              }
           else
              g->njumps = 0;
           err = carefulgraph(whichcase,g,visibility);
           if(g->nsingularities)
              { free2(g->singularities); /* allocated by compute_singularities */
                g->nsingularities = 0;
                g->singularities = NULL;
              }
           if(g->njumps)
              { free2(g->jumps); /* allocated by compute_jumps */
                g->njumps = 0;
                g->jumps = NULL;
              }
           if(err == 2)
              return 2;
           if(err == 4)
              return 10;  /* function evaluable only at isolated points */
           if((err || whichcase >= 4) && jumperror)
              return 7;
           if(whichcase == 4)
              return 1;
           if(whichcase > 4)
              return 9;
           if(err)
              return 1;
           if(jumperror)
              return 6;
           return 0;

        case PARAMETRIC:
           graphparametric(g->xfunction,g->yfunction,
                           (double *) (g->independent_variable.args),
                           g->tmin, g->tmax,
                           g->xmin, g->xmax,
                           g->ymin, g->ymax,
                           visibility,
                           g
                          );
           return 0;

        case POLAR_CIRCULAR:
        case POLAR:
           /* draw expects the following to be so already; it's
              done in grafinit.c.  In Windows MathXpert, it couldn't be done
              here because product, cos1, sin1 weren't part of graph.dll.
              g->xfunction = product(r,cos1(theta));
              g->yfunction = product(r,sin1(theta));
           */
           graphparametric(g->xfunction,g->yfunction,
                           (double *) (g->independent_variable.args),
                           g->tmin,g->tmax,
                           g->xmin,g->xmax,
                           g->ymin,g->ymax,
                           visibility,
                           g
                          );
           return 0;

        case ODE:
        case HDE:
           /* will draw both left and right */
           { int i,err,n;
             term t;
             double initial_values[32];
             double eps;  /* desired accuracy */
             double h1;   /* guessed initial step size */
             eps = (device->xpixel < device->ypixel) ? device->xpixel/10000.0 : device->ypixel/10000.0;
             h1 = (g->tmax - g->tmin) * 0.00001;
             t = g->independent_variable;
             n = g->dimslist;  /* number of independent variables */
             assert(n < 32);
             for(i=1;i<=n;i++)
                initial_values[i] = *(g->initial_values[i-1]);
                /* ode wants the indices starting at 1;  */
             if(initial_values[1] < g->xmax)
                { err =
                    ode( initial_values,
                         n,
                         initial_values[1],
                         g->xmax,
                         t,
                         g->elist -1,     /* array of dependent variables (as terms) */
                         g->slist -1,      /* array of right-hand sides f in y'=f(x,y) */
                                           /* adjusted so legal indices begin with 1 */
                         eps, h1, 6* device->xpixel, 6* device->ypixel
                       );
                  if(err && err != 2)
                     /* err == 2 means step size too small, which is OK.  See ode.c */
                     { move_to(0.9 * g->xmin + 0.1 * g->xmax, 0.9 * g->ymax  + 0.1 * g->ymin);
                       return 4;
                     }
                }
               /* Now draw leftwards from the starting point */
             if(initial_values[1] > g->xmin)
                { err =
                    ode( initial_values,
                         n,
                         initial_values[1],
                         g->xmin,
                         t,
                         g->elist -1,     /* array of dependent variables (as terms) */
                         g->slist -1,      /* array of right-hand sides f in y'=f(x,y) */
                                           /* adjusted so legal indices begin with 1 */
                         eps, h1, 6* device->xpixel, 6* device->ypixel
                       );
                  return err == 2 ? 0 : err;
                }
             return 0;
           }

        case ODE2:
        case ODESYSTEM:
          /* will draw only one direction, using the parameter */
           { int err,i,n;
             term t;
             double eps;  /* desired accuracy */
             double h1;   /* guessed initial step size */
             double initial_values[32];
             eps = (device->xpixel < device->ypixel) ? device->xpixel/10000.0 : device->ypixel/10000.0;
             h1 = (g->tmax - g->tmin) * 0.00001;
             t = g->independent_variable;
             n = g->dimslist;  /* number of independent variables */
             assert(n < 32);
             for(i=1;i<=n;i++)
                initial_values[i] = *(g->initial_values[i-1]);
                /* ode wants the indices starting at 1;  */
             err =
               ode( initial_values,
                    n,
                    g->tmin,
                    g->tmax,
                    t,
                    g->elist -1,      /* array of dependent variables (as terms) */
                    g->slist -1,      /* array of right-hand sides f in y'=f(x,y) */
                                      /* adjusted so legal indices begin with 1 */
                    eps, h1, 6* device->xpixel, 6* device->ypixel
                  );
             if(err && err != 2)
                {
                  return 4;
                }
             return 0;
           }
        case MC_INEQ:
           if( g->dimslist == 0 ||
               (g->dimslist==1 && FUNCTOR(g->slist[0]) == FALSEFUNCTOR)
             )
              whichcase = 3;  /* no singularities */
           else if(g->dimslist == 1 && FUNCTOR(g->slist[0])==UNDEFINED)
              whichcase = -1;   /* symbolic singularities too hard to compute */
           else  /* now we have symbolic singularities at least */
              { err = compute_singularities(g);
                if(err)   /* numerical singularities too hard to compute */
                   whichcase = -1;
                else
                   { if(g->nsingularities)
                        whichcase = 1;  /* numerical singularities */
                     else
                        whichcase = 3;  /* no singularities between xmin and xmax */
                   }
              }
           if(g->dimjumplist != 0 && FUNCTOR(g->jumplist[0]) == UNDEFINED)
               whichcase = -1;
           else if( g->dimjumplist != 0)  /*  jumps to deal with */
              { err = compute_jumps(g);
                if(err==1)   /* numerical jumps too hard to compute */
                   whichcase = -1;
              }
           else
              g->njumps = 0;
           if(whichcase != -1)
              { graph_inequality(whichcase,g);
                if(g->nsingularities)
                   { free2(g->singularities); /* allocated by compute_singularities */
                     g->nsingularities = 0;
                     g->singularities = NULL;
                   }
                if(g->njumps)
                   { free2(g->jumps); /* allocated by compute_jumps */
                     g->njumps = 0;
                     g->jumps = NULL;
                   }
                redraw_axes(g);  /* draw axes etc. AFTER filling regions */
                return 0;
              }
           /* and if whichcase == -1, fall through to SET and RELATION */
        case MC_SET:  /* fall through */
        case RELATION:
           { /* g->function contains an equation,
                inequality, or Boolean combination thereof;
                but graphrelation wants
                a term which it tests for zero-crossings,
                and shades the areas where it's positive.
             */
             term interior,boundary;  /* the terms to pass to graphrelation */
             int shadeflag;
             int err;
             shadeflag = deBoolean(g->function,&interior,&boundary);
             err = graphrelation(interior,boundary,
                           (double *) ARGPTR(g->independent_variable),
                           (double *) ARGPTR(g->dependent_variable),
                           g->graphcolor,
                           g->fillcolor,
                           shadeflag
                          );
             if(err)
                return 11;  // see above
             if(shadeflag & 1)
                redraw_axes(g);  /* draw axes again AFTER filling regions */
             return 0;
           }

        case CONTOUR:
           { contourplot(g->function,
                         (double *) ARGPTR(g->independent_variable),
                         (double *) ARGPTR(g->dependent_variable),
                         g->tmin,  /* this holds the contour spacing */
                         g->background
                        );
             return 0;
           }

        case COMPLEX_CONTOUR:
           { double *xptr = (double *) ARGPTR(g->independent_variable);
             double *yptr = xptr + 1;
                /* xptr points to varinfo[0].real, so
                   yptr points to varinfo[0].imag; but this
                   construction lets us avoid mentioning varinfo,
                   so this code can go into graph.dll.
                   If we pass   ((double *) ARGPTR(g->independent_variable)) + 1
                   directly to complex_contourplot, Borland C 4.0 gets
                   the pointer arithmetic wrong.
                */
             complex_contourplot(g->function, xptr, yptr,
                                 g->tmin,  /* this holds the contour spacing */
                                 g->background
                                );
             return 0;
           }
        case POLYROOT:
           { int err,degree;
             term t = g->function;
             term u;
             if(nonconstant_exponent(t))
                  { err = fix_exponents(t,&u);
                    assert(!err);  /* input has been checked already */
                  }
             else
                  u = t;
             term eigenvariable = get_eigenvariable();
             err = makepoly(u,eigenvariable,&g->yfunction);
             if(err)
                  { err = polyform(u,eigenvariable,&g->yfunction);
                  }
               /* polyroot expects a POLY term at g->yfunction */
             assert(!err);  /* input is supposed to have been checked already */
             assert(FUNCTOR(g->yfunction) == POLY);
             degree = ARITY(g->yfunction) - 1;
             assert(degree > 0);
             err = polyroot(g,g->yfunction,&nroots); /* file polyroot.c */
             if(err == 1)
                return 3;
             assert(err==0);  /* polyroot can only return 0 and 1 */
             return 0;
           }
        case RIEMANNSUMS:
          {
            draw_riemann(g);
            return 0;
          }

        case SIMPSONSRULE:
           draw_simpson(g);
           return 0;

        case TRAPEZOIDRULE:
           draw_trapezoid(g);
           return 0;
      }
   return 0;
}
/*_________________________________________________________________________*/
void direction_field(graph *g, int k)
/* if k is 1, show the direction field in graph g; if k ==0, erase it */
/* It's shown by a 'grid' of small arrows in the direction of the field */
/* g->dfield keeps track of whether it's visible or not */

/*  only used for TWO VARIABLE systems y[1]' = f[1](y), y[2]'=f[2](y);
    f[i] must not depend on the independent variable x
*/


{  double x,y;
   svgDevice *device = get_device();
   double deltax = 0.05 * (g->xmax - g->xmin); /* distance between field markers*/
   double deltay = 0.04 * (g->ymax - g->ymin);
   deltax = deltax/3;
   /* because of using drawLarge, the graph is 3 times wider than the window,
      so we divide by 3 */
   deltay = deltay/3;
   double htick = 6.0 * device->xpixel;  /* half width of one horizontal marker line */
   double vtick = htick * (g->ymax - g->ymin)/(g->xmax - g->xmin);
   double *yptr[2];
   double dy, dx;
   double fudge,tempx,tempy;
   term *f = g->slist;
   assert(g->dfield_applicable);
   if (g->dfield == k)
      return;   /* do nothing, request already satisfied */
   if (g->dfield < 0)
      return;   /* negative value means inappropriate request */
             /* this is set before calling ode() if equation is of wrong form */
   if ((g->dimslist) != 2)
      return;  /* only works for 2-variable systems */
   /* We must take care that the direction field is visible. */
   set_graphpencolor(gridcolor(g->background));
   unsigned rgb = get_graphpencolor();
   set_linewidth(THIN);
   g->dfield =  g->dfield ? 0 : 1;
       /* g->dfield keeps track of whether the direction field is visible */
   yptr[0] = (double *) (g->elist[0].args);
   defineArrowhead(rgb);  // must call this before arrow_to
   begin_path();
   yptr[1] = (double *) (g->elist[1].args);
   for(x = g->xmin ; x <= g->xmax; x += deltax)
   for(y = g->ymin ; y <= g->ymax; y += deltay)
     {  /* draw a very short line at x,y
           with slope g->slist[1]/g->slist[0] */
        *yptr[0] = x;
        *yptr[1] = y;
        deval(f[0],&dx);
        deval(f[1],&dy);
        if(fabs(dy) < 0.0001 * fabs(dx))  /* draw a horizontal line */
           { move_to(x-htick,y);
             line_to(x+htick,y);
           }
        else
           { /* scale dy and dx to keep same ratio dy/dx but make
                 dy^2/(vtick)^2 + dx^2/(htick^2) = 1 */
             tempx = dx/htick;
             tempy = dy/vtick;
             fudge = 1/ sqrt( tempx*tempx + tempy*tempy);
             dx *= fudge;
             dy *= fudge;
             move_to(x-dx,y-dy);
             arrow_to(x+dx,y+dy);
           }
     }
  end_path();
  set_linewidth(g->linewidth);  /* set to THIN above */
}

/*_____________________________________________________________*/
int draw_all( graph *g)
/* called when g->erase == 0.  If there is a history of old values
of the parameters, draw the graph several times, once for each set
of old values of the parameters, on the same axes, drawing the axes
only once. */
{ double values[MAXPARAMS];
  dlist *markers[MAXPARAMS];
  parameter *parameters = get_parameters();
  int i,err=0,count;
  int saveit = g->newaxes;
  int nparameters = get_nparameters();
  assert(nparameters > 0);  /* otherwise 'draw' should have been called */
   /* First, push the current values onto the history list,
      if they are different from what is already there. */
  push_parameters();
  for(i=0;i<nparameters;i++)
     { values[i] = *parameters[i].addr;  /* store the values */
       markers[i] = parameters[i].history;
     }
  for(count = 0; markers[0]; ++count)
     { for(i=0;i<nparameters;i++)
          { assert(markers[i]);
            *parameters[i].addr = markers[i]->data;
          }
       if(count == 1)
          g->newaxes = 0;
       err |= draw(g);
       for(i=0;i<nparameters;i++)
          markers[i] = markers[i]->next;  /* advance to the next values */
     }
 /* Now restore the original conditions */
 g->newaxes = saveit;
 for(i=0;i<nparameters;i++)
    *parameters[i].addr = values[i];
 return err;
}
/*___________________________________________________________________*/
#if 0
void draw_grid(graph *g, int flag)
/* draw a grid over the graph, in xor mode.  Flag is 1 if it's
desired to show the grid, 0 if it's desired to hide it.
Adjust g->grid before returning.  Does not reset the old
color; everybody sets the color before drawing.
*/
{ double a,b,c,d,x,y,xgap,ygap;
  int piflag = (g->labels == 3); /* use multiples of pi */
  double devicewidth = g->dxmax-g->dxmin;
  xgap = 0.2 * compute_tick_gap(g->xmin,g->xmax,piflag, devicewidth);
  ygap = 0.2 * compute_tick_gap(g->ymin,g->ymax,0,devicewidth);
  if((flag && g->grid) || (!flag && !g->grid))
     return;  /* nothing to do */
  g->grid = flag;
  a = g->xmin;
  b = g->xmax;
  c = g->ymin;
  d = g->ymax;
  set_linewidth(THIN);
  xormode(1);
  /* We must take care that the grid is visible. */
  set_graphpencolor(gridcolor(g->background));

  /* Now draw the grid.  Draw the vertical lines first. */
  if(a < 0 && b > 0)
     { x = -xgap;
       while(x > a)
          { move_to(x,c);
            line_to(x,d);
            x -= xgap;
          }
       x = xgap;
       while(x <b)
          { move_to(x,c);
            line_to(x,d);
            x += xgap;
          }
     }
  else  /* start at an exact multiple of xgap */
     { x = (int) (a/xgap) * xgap;
       if(x < a)
          x += xgap;
       while(x < b)
          { move_to(x,c);
            line_to(x,d);
            x += xgap;
          }
     }
  /* Draw the horizontal lines */
  if(c < 0 && d > 0)
     { y = -ygap;
       while(y > c)
          { move_to(a,y);
            line_to(b,y);
            y -= ygap;
          }
       y = ygap;
       while(y < d)
          { move_to(a,y);
            line_to(b,y);
            y += ygap;
          }
     }
  else  /* start at an exact multiple of ygap */
     { y = (int) (c/ygap) * ygap;
       if(y < c)
          y += ygap;
       while(y < d)
          { move_to(a,y);
            line_to(b,y);
            y += ygap;
          }
     }
  xormode(0);
  set_linewidth(g->linewidth);
}
#endif
/*_____________________________________________________________*/
#define CONTRAST  128
static unsigned int gridcolor(unsigned int color)
/* color is passed as the graph background color.
Return the color to use for a grid on a graph.  It should
be visible against a background of the specified color but
at low contrast, e.g. light gray on white or dark gray on
black.
  We separate the color into its RGB parts and then
for each part we bring the intensity towards the center
by CONTRAST, so pure black and pure white both become gray.
*/
{ unsigned long  r,g,b,rr,gg,bb;
  r = color & 0x00ff;
  g = (color & 0xff00) >> 8;
  b = (color & 0x00ff0000L) >> 16;
  rr = r > 128 ? r-CONTRAST : r+CONTRAST;
  gg = g > 128 ? g-CONTRAST : g+CONTRAST;
  bb = b > 128 ? b-CONTRAST : b+CONTRAST;
  return RGB(rr,gg,bb);
 }

/*________________________________________________________________________*/
int compute_singularities(graph *g)
/* Called by 'draw' just before graphing a function with singularities or extrema.
It must use the symbolic expressions recorded in the elist and slist fields
of g to compute the corresponding arrays of doubles (between xmin and xmax)
which are used by carefulgraphc.  It must allocate those arrays, too. */

/* It returns a nonzero value to indicate an error.
1 means formula too complicated to compute singularities
2 means more than one parameter in singularities formula (see below).
Otherwise it returns zero, whether or not there were any singularities.
That can be discovered by examining g->nsingularities.
*/

{  int err;
   term x;
   x = g->independent_variable;
   err = get_sing(g->xmin, g->xmax,x,g->slist,g->dimslist,g->srestrictions,
            g->dimsrestrictions, &(g->singularities),&(g->nsingularities));
   if(err)
      return err;
   return 0;
}

/*_________________________________________________________________*/
static int deBoolean(term t, term *ans, term *boundary)
/* t is an equation, inequality, or positive Boolean combination of
inequalities.  (Positive means negation isn't used.  Negation should first
be pushed inwards to the inequalities and eliminated.)
Return in *ans term which is positive exactly on the interior of the
set S(t) defined by t, and return in *boundary term which is zero on the
boundary points of the set defined by t which are members of S(t).
Return 1 if the interior is possibly nonempty (due to the presence of some
inequalities) and 0 if it is certainly empty.  Failure should never occur.
*/
{ term a,b,v,bv,temp;
  int i,r,retval;
  unsigned short f = FUNCTOR(t);
  unsigned short n;
  if(INEQUALITY(f))  /* this includes the case '='  */
     { if(f == GE || f == '>')
          { a = ARG(1,t);
            b = ARG(0,t);
            f = (unsigned short)(f == GE ? LE : '<');
          }
       else
          { a = ARG(0,t);
            b = ARG(1,t);
          }
       if(ISZERO(a))
          { *ans = b;
             switch(f)
               { case '<':
                    *boundary = one;  /* no boundary */
                    return 1;
                 case LE:
                    *boundary = b;
                    return 1;
                 case '=':
                    *boundary = b;
                    return 0;
               }
          }
       if(ISZERO(b) && f == '=')
          { *boundary = *ans = tnegate(a);
            return 0;
          }
       if(ISZERO(b))
          { *ans = tnegate(a);
            *boundary = f == '<' ? one : abs1(a);
            return 1;
          }
       *ans = sum(b,tnegate(a));  /* don't run it through polyval as it
                              may contain sums of powers that will expand
                              to  sums */
       *boundary = f == '<' ? one : *ans;
       return f == '=' ? 0 : 1;
     }
  if(f == OR)
     { /* A > 0 or B > 0  iff  max(A,0) + max(B,0) > 0,
                          iff  |A| + A + |B| + B > 0  */
       n = ARITY(t);
       *ans = make_term('+',(unsigned short)(2*n));
       temp = make_term('*',n);
       retval = 0;
       for(i=0;i<n;i++)
          { r = deBoolean(ARG(i,t),&v,&bv);
            ARGREP(*ans,2*i,v);
            ARGREP(*ans,2*i+1,abs1(v));
            ARGREP(temp,i,bv);
            if(r)
              retval = 1;
          }
       /* Now that we know the interior, compute the boundary term */
       /* The boundary is where temp == 0 and *ans == 0 */
       *boundary = sum(abs1(temp),abs1(*ans));
       return retval;
     }
  if(f == AND)
     { n = ARITY(t);
       *ans = make_term('*',n);
       temp = make_term('*',n);
       retval = 0;
       for(i=0;i<n;i++)
          { r = deBoolean(ARG(i,t),&v,&bv);
            ARGREP(*ans,i,v);
            ARGREP(temp,i,bv);
            if(r)
               retval = 1;
          }
       *boundary = sum(abs1(temp),abs1(*ans));  /* same as for unions */
       return retval;
     }
  assert(0);
  return 0;  /* avoid a warning message */
}
/*________________________________________________________*/
void redraw_axes(graph *g)
/* used after graphing a relation or inequality; otherwise
the axes may be largely obscured.
*/
{ drawaxes(g);
  if(g->ticks)
     putticksonaxes(g);
  if(g->labels)
     labelaxes(g);
  if(g->border != g->background) /* then draw a border around the viewport */
     { set_graphpencolor(g->border);
        /*  outline the viewport  */
       draw_box(g->xmin, g->xmax, g->ymin , g->ymax);
     }
}
/*__________________________________________________________________________*/
static int special_singularities(graph *g)
/* This is called when there are parameters, and the singularities of g
are too difficult to calculate in symbolic form involving parameters.
This function substitutes the current parameter values for the parameters
in g->function, then calls singularities to calculate the singularities
and jumps (now that g->function has only one variable).  If it succeeds,
it stashes the result in g->slist, and sets g->dimslist to the dimension
of that array; and sets g->tempsing to 1.  (g->tempsing will be examined
when parameters change, and then g->slist will be destroyed and reset
to 'undefined', so that under 'draw', this function will be called again.)
The return value is
  4 for failure,
  3 for successful calculation, and no symbolic singularities for these
    values of the parameters.
       between g->xmin and g->xmax
  1 for successful calculation of symbolic singularities for these
    parameter values.
If singularities succeeds, the parameter-free jump formula will also be
returned.  This function should stash the jumps in g->jumplist, and set
g->dimjumplist appropriately.  If it fails, it should set g->jumplist to
'undefined' and set g->dimjumplist to 1.
   When this function is called, g->dimslist is 1 and g->slist contains
the single entry 'undefined'.
*/

{ void  *savenode = heapmax();
  term sings,jumps,u,temp;
  term x = g->independent_variable;
  int i,err;
  term *varlist;
  int nvariables;
  if(!equals(get_eigenvariable(),x))
     { // assert(0);
       /* x should be the eigenvariable, but we ensure that here anyway. */
       varlist = get_varlist();
       nvariables = get_nvariables();
       for(i=0;i<nvariables;i++)
          { if(equals(varlist[i],x))
               { set_eigenvariable(i);
                 break;
               }
          }
     }
  err = evalsub(g->function,x,&temp);  /* replace the variables  by their current numerical values */
  if(err)
     return 4;
  ratsimp(temp,&u);  /* factor num and denom and simplify */
  err = singularities(u,&sings,&jumps);
  if(err == 0 || err == 1)
     { g->tempsing = 1;
       save_and_reset(or(sings,jumps),savenode,&temp);
       sings = ARG(0,temp);
       jumps = ARG(1,temp);
       set_singularities(g,sings);
       set_jumps(g,jumps);
       return equals(sings,falseterm) ? 3 : 1;
     }
  reset_heap(savenode);
  return 4;
}

/*_________________________________________________________________________*/
static void set_singularities(graph *g, term sings)
/* enter the terms in 'sings' into g->slist and set
g->dimslist */
{ if(FUNCTOR(sings) != AND)
     { g->slist[0] = sings;
       g->dimslist = equals(sings,falseterm) ? 0 : 1;
       return;
     }
  g->slist = ARGPTR(sings);
  g->dimslist = ARITY(sings);
}

/*_________________________________________________________________________*/
static void set_jumps(graph *g, term jumps)
/* enter the terms in 'jumps' into g->jumpslist and set
g->dimjumplist */
{ if(FUNCTOR(jumps) != AND)
     { g->jumplist[0] = jumps;
       g->dimjumplist = equals(jumps,falseterm) ? 0 : 1;
       return;
     }
  g->jumplist = ARGPTR(jumps);
  g->dimjumplist = ARITY(jumps);
}

/*_______________________________________________________________________*/
static int evalsub(term t, term x, term *ans)
/* x is a variable.  Use deval to evaluate all other free variables in t
(but not pi and e) and substitute the resulting numbers for the
free variables in t.  Put the result in *ans.
   Return 0 for success.  Return 1 if BADVAL results
from one of the deval calls; in that case *ans can be garbage.
*/

{ unsigned short n = ARITY(t);
  unsigned short f = FUNCTOR(t);
  int i,err;
  static int nbound;
  static term boundvars[MAXVARIABLES];
  double z;
  int j;
  long kk;
  if(OBJECT(t))
     { *ans = t;
       return 0;
     }
  if(ISATOM(t))
     { if(equals(t,x) || equals(t,pi_term) || equals(t,eulere) || equals(t,complexi) ||
          f == INFINITYFUNCTOR || f == UNDEFINED || f == BOUNDED_OSCILLATIONS ||
          f == UNBOUNDED_OSCILLATIONS || f == LEFT || f == RIGHT || f == TINY
         )
          { *ans = t;
            return 0;
          }
       for(j=0;j<nbound;j++)
          { if(equals(t,boundvars[j]))
              { *ans = t;
                return 0;
              }
          }
       deval(t,&z);
       if(z == BADVAL)
          return 1;
       if(nearint(z,&kk))
          { *ans = make_int(kk);
            return 0;
          }
       *ans = make_double(z);
       return 0;
     }
  if(BINDING(t))
     { boundvars[nbound] = BOUNDVAR(t);
       ++nbound;
     }
  *ans = make_term(FUNCTOR(t),n);
  for(i=0;i<n;i++)
     { err = evalsub(ARG(i,t),x,ARGPTR(*ans)+i);
       if(err)
          { RELEASE(*ans);
            if(BINDING(t))
               { --nbound;
                 if(nbound < 0)
                    assert(0);
               }
            return 1;
          }
     }
  if(BINDING(t))
     { --nbound;
       if(nbound < 0)
          assert(0);
     }
  return 0;
}

/*_________________________________________________________*/

int drawLarge(graph *g)
/*  draw an area of the graph 3 times the width and height that
will be visible, to permit scrolling without messages to the server.
The plan is to adjust g->xmin, g->dxmin, g->pxmin (etc.), reset them
for the bigger graph,  call draw(),
and then reset those values to the originals.
*/
{
   // first store the original values
   double olddxmin, olddxmax, olddymin, olddymax;
   int oldpxmin, oldpxmax, oldpymin, oldpymax;
   int oldbordercolor;
   int rval;
   olddxmin = g->dxmin;
   olddxmax = g->dxmax;
   olddymin = g->dymin;
   olddymax = g->dymax;
   oldpxmin = g->pxmin;
   oldpxmax = g->pxmax;
   oldpymin = g->pymin;
   oldpymax = g->pymax;
   oldbordercolor = g->border;
   svgDevice *theDevice= get_device();
   
   // Now reset them for a larger graph
   g->border = g->background;  // prevents draw() from outlining the viewport
      // so sendDocument must outline the viewport if necessary
   double worldwidth = g->xmax-g->xmin;
   double worldheight = g->ymax-g->ymin;
   double devicewidth = g->dxmax-g->dxmin;
   double deviceheight = g->dymax-g->dymin;
   double pixelwidth = g->pxmax-g->pxmin;
   double pixelheight = g->pymax-g->pymin;
   
   g->xmax += worldwidth;
   g->xmin -= worldwidth;
   g->ymax += worldheight;
   g->ymin -= worldheight;
   g->dxmax += devicewidth;
   g->dxmin -= devicewidth;
   g->dymax += deviceheight;
   g->dymin -= deviceheight;
   g->pxmax += pixelwidth;
   g->pxmin -= pixelwidth;
   g->pymax += pixelheight;
   g->pymin -= pixelheight;
   theDevice->pxmin = g->pxmin;
   theDevice->pxmax = g->pxmax;
   theDevice->pymin = g->pymin;
   theDevice->pymax = g->pymax;
   if(g->erase == 0)   // keep old parameter values
      rval = draw_all(g);
   else
      rval = draw(g);  // only use current parameter value
   
   // now restore the original values
   g->dxmin = olddxmin;
   g->dxmax = olddxmax;
   g->dymin = olddymin;
   g->dymax = olddymax;
   g->pxmin = oldpxmin;
   g->pxmax = oldpxmax;
   g->pymin = oldpymin;
   g->pymax = oldpymax;
   g->border = oldbordercolor;
   /*  draw calls AdjustGraphRanges, so you shouldn't just restore
   the original g->xmin, g->xmax, g->ymin, g->ymax.
   Instead compute them from the new ones. */
   worldwidth = g->xmax-g->xmin;
   worldheight = g->ymax-g->ymin;
   g->xmin += worldwidth/3.0;
   g->xmax -= worldwidth/3.0;
   g->ymin += worldheight/3.0;
   g->ymax -= worldheight/3.0;
   theDevice->pxmin = g->pxmin;
   theDevice->pxmax = g->pxmax;
   theDevice->pymin = g->pymin;
   theDevice->pymax = g->pymax;
   return rval;
}



Sindbad File Manager Version 1.0, Coded By Sindbad EG ~ The Terrorists