Sindbad~EG File Manager

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

/* graph a relation f(x,y)=0
   M. Beeson, for Mathpert
  11.15.90 file created
  10.2.97 code last modified
  6.14.98 last modified
  4.19.00 eliminated call to user_interrupt
  2.24.24 changed include cgraph. to svgGraph.h
  10.16.24 made graphrelation return 1 for out of buffer space.
  10.17.24 removed printerfill and related code as obsolete
*/

#include <math.h>
#include <stdlib.h>
#include <stdio.h>  // debugging printf calls
#include <assert.h>
#include "globals.h"
#include "graphstr.h"
#include "svgGraph.h"  /* already calls #include terms.h and meta.h*/
#include "grapher.h"
#include "deval.h"
#include "heap.h"
/*____________________________________________________________________________*/
static double verysmall,epsilon; /* used to compensate for roundoff error */
#define NEARCHANGE1(x,y) ((x < verysmall && y > epsilon) || (y < verysmall && x > epsilon))
#define NEARCHANGE2(x,y) NEARCHANGE1(-x,-y)
#define SIGNCHANGE(x,y) ((x != BADVAL && y != BADVAL && (x > 0 ? (y <= 0 ? 1 : NEARCHANGE1(x,y)) : y > 0 ? 1 : NEARCHANGE2(x,y))) && fabs(x-y) < 10000)
#define MAXABSFUNCTOR(a,b)  ((a) > 0 ? ((b) <= 0 ? (a) : (a) > (b) ? (a) : (b)) : (b) > 0 ? (b) : (a) < (b) ? -(a) : - (b))
/*_______________________________________________________________*/

static int promising(double a,double b,double c,double d,double e);
int nearzero(term b, double *xp, double deltax, double scale);

/* First version of graphrelation simply computed values of f at every
pixel and looked for sign changes in both directions, coloring two pixels
when it found a sign change.  To do this we need two arrays to keep two
columns of values of f.  This is too slow.

This version divides the screen into 9 by 9 rectangles, and computes them
by columns, computing values only at the corners and middle unless the rectangle
is promising.  Then it computes a double array of values and plots the
corresponding points.
*/

int graphrelation(term f,   /* relation to be graphed is f =0 or f > 0 */
                             /* f can be an AND of mathematical terms,
                                in which case the relation is the conjunction
                                of inequalities ARG(i,f) > 0 or >= 0 */
                   term boundary, /* zero on the boundary */
                   double *xp, double *yp, /* pointers to values of x,y */
                   unsigned  color,  /* for the boundary line */
                   unsigned  fillcolor,  /* for the interior */
                   int shadeflag  /*  nonzero to shade the interior */
                  )
/* graph f(x,y)=0  or f(x,y) > 0 over rectangle specified by viewport and world
   coordinates.  Return 0 for success, 1 for out of buffer space  */

{ int nx1,nx2,ny1,ny2;  /* pixel coordinates of the viewport */
  int bigstep;
  svgDevice *device = get_device();
  int i,j;   /* loop variables */
  double e;
  double xpixel = device -> xpixel;
  double ypixel = device -> ypixel;
  double square[32][32];  /* for promising rectangles */
  double *col0;   /* dynamic array for one column of numbers */
  double *col1;   /* alternate with col0 to save copying */
  double *col,*othercol;    /* will be col0 and col1 alternately */
  int u,v;
  int err = 0;
  double savex,savey;
  unsigned changeflag;
  int fillflag = shadeflag;
  int andflag = FUNCTOR(f) == AND;
  verysmall = fabs(device->xmax - device->xmin) * VERYSMALL;
  epsilon = 1000.0 * verysmall;
  bigstep = 11;  // was 9, then 7, then 11
  assert(bigstep <= 29);
  /* don't ever make it bigger or bounds on 'square' will be exceeded */
  /* larger values in the 20s make inaccuracies e.g. on y^2 < sin^2 x  */
  svg_style_rectangles(fillcolor);  /* so we can call unstyled_rect below, and also it affects draw_pixel. */
  nx1 = device->pxmin;
  nx2 = device->pxmax;
  ny1 = device->pymin;
  ny2 = device->pymax;
  assert(ny1 < ny2);
      /* pixel coordinates are smaller at the top */
      /* so ny1 goes with ymax, and ny2 goes with ymin */
  col0 = (double *) calloc(ny2-ny1+bigstep+1,sizeof(double));
  if(col0==NULL)
     nospace();
  col1 = (double *) calloc(ny2-ny1+bigstep+1,sizeof(double));
  if(col1==NULL)
     nospace();
  col0 -= ny1-bigstep;  /* so  legal indices are col[ny1-bigstep]...col[ny2] */
  col1 -= ny1-bigstep;
  *xp = device->xmin + 0.5*device->xpixel;
    /* world coordinates begin half a pixel left of the first pixel */
  for(i=nx1; i<=nx2; i+=bigstep)
     { *yp = device->ymin + 0.5 * device->ypixel;
       if( i & 1 )  /* i is odd */
          { col = col1;
            othercol = col0;
          }
       else  /* i is even */
          { col = col0;
            othercol = col1;
          }
       deval(f,col+ny2);   /* outside loop so don't have to check j > ny2 in loop */
       *yp += bigstep * ypixel;  /* set correct initial value of y */
       for(j=ny2-bigstep; j>ny1-bigstep; j-=bigstep)  /* compute the i-th column */
          { deval(f,col + j);
            savex = *xp;
            savey = *yp;
            *xp -= 0.5 * bigstep * xpixel;
            *yp -= 0.5 * bigstep * ypixel;
            deval(f,&e);
            *xp = savex;
            *yp = savey;
            if(e==BADVAL)
              { *yp += bigstep *ypixel;
                continue;
              }
            if(i > nx1 && promising(col[j],col[j+bigstep],othercol[j+bigstep],othercol[j],e))
                 /* then fill in the 'square' array with every-pixel
                    values for the (bigstep+1) by (bigstep+3)-pixel rectangle
                     with (*xp,*yp) near the upper right.  Precisely,
                     the left side is bigstep+1 pixels to the left of *xp
                     so u = bigstep+1 corresponds to x-coordinate *xp
                     and u = bigstep+2 is one pixel to the right of *xp
                 */
               {
                 *xp -= (bigstep+1) * xpixel;  /* one to left of little square */
                 for(u=0;u <= bigstep+2;u++)
                    { *yp = savey - (bigstep+1) *ypixel;  /* one below little square */
                      for(v=0; v <= bigstep+2;v++)
                         { // assert(u < 32 && v < 32);  since bigstep <= 29
                           deval(f,&square[u][v]);
                           if(square[u][v] == BADVAL)
                              { double saveit = *xp;
                                *xp += epsilon;
                                /* Give it a second try.  If this edge of
                                   the square corresponds to a singularity
                                   such as we have in 1/(x+1) <= 6 we can
                                   avoid leaving a blank column. */
                                deval(f,&square[u][v]);
                                *xp = saveit;
                                if(square[u][v] == BADVAL)
                                  continue;  /* without coloring anything */
                              }
                           changeflag = SIGNCHANGE(square[u][v],square[u][v-1]);
                           if(v>0 && changeflag)
                              { /* crossing a boundary vertically */
                                /* determine by evaluating 'boundary' whether we
                                   have to color the boundary */
                                if(!nearzero(boundary,yp,-ypixel,MAXABSFUNCTOR(square[u][v],square[u][v-1])))
                                   { /* don't color the boundary */
                                     /* No need to check fillflag because for
                                        inequalities it is always 1 */
                                     if(square[u][v] > 0)
                                        { err = draw_pixel(i-bigstep-1+u,j+bigstep+1-v,fillcolor);
                                          if(err)
                                             goto out;
                                        }
                                     if(square[u][v] < 0)
                                        { err = draw_pixel(i-bigstep-1+u,j+bigstep+2-v,fillcolor);
                                          if(err)
                                             goto out;
                                        }
                                     if(andflag)
                                        changeflag = SIGNCHANGE(square[u-1][v],square[u][v]);
                                     else
                                        changeflag = SIGNCHANGE(square[u-1][v],square[u][v]) ? (unsigned)(-1) : 0;
                                     if(u>0 && changeflag)
                                        { /* crossing both horizontally and vertically */
                                          if(square[u-1][v] > 0)
                                             { err = draw_pixel(i-bigstep+u-2,j+bigstep+1-v,fillcolor);
                                               if(err)
                                                  goto out;
                                             }
                                          if(square[u][v] > 0)
                                             { err = draw_pixel(i-bigstep+u-1,j+bigstep+1-v,fillcolor);
                                               if(err)
                                                  goto out;
                                             }
                                          if(nearzero(boundary,yp,ypixel,MAXABSFUNCTOR(square[u-1][v],square[u][v])))
                                             /* color the boundary after all */
                                             { err = draw_pixel(i-bigstep-1 + u,j+bigstep+1-v,color);
                                               if(err)
                                                  goto out;
                                               err = draw_pixel(i-bigstep-1 + u,j+bigstep+2-v,color);
                                               if(err)
                                                  goto out;
#if 0 /* draws better without these two lines, I don't know why. */
                                               if(u>0 && SIGNCHANGE(square[u-1][v],square[u][v]))
                                                   { err = draw_pixel(i-bigstep+u-2,j+bigstep+1-v,color);
                                                     if(err)
                                                        goto out;
                                                   }
#endif
                                             }
                                        }
                                   }
                                else  /* draw the boundary in color */
                                   { err = draw_pixel(i-bigstep-1 + u,j+bigstep+1-v,color);
                                     if(err)
                                        goto out;
                                     err = draw_pixel(i-bigstep-1 + u,j+bigstep+2-v,color);
                                     if(err)
                                        goto out;
#if 0 /* draws better without these two lines, I don't know why. */
                                     if(u>0 && SIGNCHANGE(square[u-1][v],square[u][v]))
                                        { err = draw_pixel(i-bigstep+u-2,j+bigstep+1-v,color);
                                          if(err)
                                             goto out;
                                        }
#endif
                                   }

                              }
                           else if(u>0 && (changeflag = SIGNCHANGE(square[u-1][v],square[u][v]))!=0)
                                /* = instead of == is not a mistake here! */
                                /* The somewhat convoluted syntax uses this principle:
                                   instead of saying  if(a=b) we say if((a=b)!=0)
                                   in order to avoid a warning message.
                                */
                              { /* crossing a boundary horizontally and not vertically */
                                /* Do we have to color the boundary? */
                                if(!nearzero(boundary,xp,-xpixel,MAXABSFUNCTOR(square[u-1][v],square[u][v])))
                                   { /* don't color */
                                     if(square[u][v] > 0)
                                        { err = draw_pixel(i-bigstep+u-1,j+bigstep+1-v,fillcolor);
                                          if(err)
                                             goto out;
                                        }
                                     if(square[u-1][v] > 0)
                                        { err = draw_pixel(i-bigstep+u-2,j+bigstep+1-v,fillcolor);
                                          if(err)
                                             goto out;
                                        }
                                   }
                                else
                                   { err = draw_pixel(i-bigstep+u-1,j+bigstep+1-v,color);
                                     if(err)
                                        goto out;
#if 0
/* It draws better without this line, even though it seems like it should be there. */
                                     draw_pixel(i-bigstep+u,j+bigstep+1-v,color);
#endif
                                   }
                              }
                           else if(v>0 && fillflag)
                              { if(square[u][v] > 0)
                                   { err = draw_pixel(i-bigstep+u-1,j+bigstep+1-v,fillcolor);
                                     if(err)
                                        goto out;
                                   }
                              }
                           *yp += ypixel;
                         }
                      *xp += xpixel;
                    }
                 *xp = savex;
                 *yp = savey;
               }
            else if(fillflag && e != BADVAL &&  e > 0)
               { /* this entire large rectangle should be shaded */
                 // PixelRect(i-bigstep+1,j+1,i+2,j+bigstep,fillcolor);
                 // svg_filledrect(i-bigstep, j, i, j+bigstep, fillcolor);
                 // This is shortened to
                 err = svg_unstyledrect(i-bigstep,j,i,j+bigstep);
                 //  printf("%d ", countrects++);  never more than 6000, often less 
                 if(err)
                    { /* out of buffer space to write the graph into */
                      goto out;
                    }
               }
           *yp += bigstep *ypixel;
         }
       *xp += bigstep * xpixel;
     }
  out:
  free(col0 + ny1-bigstep);
  free(col1 + ny1-bigstep);
  return err;
}

/*______________________________________________________________________*/
static int promising(double a,double b,double c, double d,double e)
/* a,b,c,d, are function values of f at corners of a rectangle,
in  clockwise order, and e is the value in the middle.
Is there any chance of a zero of f inside the
rectangle?  If so return 1, else return 0. */

{ double laplacian;
  if(SIGNCHANGE(a,b))
     return 1;
  if(SIGNCHANGE(b,c))
     return 1;
  if(SIGNCHANGE(c,d))
     return 1;
  if(SIGNCHANGE(d,e))
     return 1;
  /* Now they all have the same sign */
  /* Compute the Laplacian; does it has the same sign as f?  */
  laplacian = a+b+c+d -4.0*e;
  if(! SIGNCHANGE(a,laplacian))
      /* Laplacian has opposite sign, e.g. positive f is concave down,
         so very unlikely to find a zero here */
     return 0;
      /* But if positive f is concave up, check size of f */
  if( fabs(a+b+c+d+e) <= 2.0 * fabs(laplacian))
     return 1; /* it's a possibility */
#if 0  /* this was intended to fix some gaps near double zeroes, but it
          doesn't help. */
  else if(fabs(a+b+c+d) <= 0.1 && fabs(laplacian) <= fabs(a+b+c+d))
     /* laplacian is small and function is small, maybe we're near a double zero. */
     return 1;
#endif
  return 0;  /* function is large compared to laplacian, very little chance of zero. */
}
/*_________________________________________________________________________*/
int nearzero(term b, double *xp, double deltax, double scale)
/*  xp is a value pointer of an atom in b.  Evaluate (using deval)
b and then (temporarily) change *xp by deltax.  Return 1 if somewhere between
these values, b is zero or close enough to count as zero.  The last
parameter, scale, is provided to help decide what's close.  This represents
the max of the endpoint absolute values of the function that computes the
interior of the region being graphed, over the interval in question; that
function changes sign, so 'scale' should be small.  If b takes a value on that
order of magnitude, it should count as zero.  */
{ double z,w,savexp,mid;
  deval(b,&z);
  if(z == BADVAL)
     return 0;
  if(z < 10*scale)
     return 1;
  if(z > 50*scale)
     return 0;
  savexp = *xp;
  *xp += deltax;
  deval(b,&w);
  *xp = savexp;
  if(w == BADVAL)
     return 0;
  if(w < 10* scale)
     return 1;
  *xp += 0.5 * deltax;
  deval(b,&mid);
  *xp = savexp;
  if(mid == BADVAL)
     return 0;
  if(mid < 10* scale)
     return 1;
  return 0;
}

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