Sindbad~EG File Manager
/* M. Beeson, for Mathpert
Graphing inequalities f(x) < y < g(x) by graph_inequality2
Graphing inequalities y < f(x) by graph_inequality1
Original date 9.6.96
Last modified 6.14.98
2.24.24 changed include cgraph.h to svgGraph.h
changed svgDevice to svgDevice
changed set_pencolor to set_graphpencolor and
set_backgroundcolor to set_graphbackgroundcolor
added code involving check1 and check2
3.23.24 sprinkled begin_path() and end_path() through fill_quad and graph_inequality2.
3.24.24 corrected graph_inequality2 to set g2->xmin etc.
8.14.24 corrected that correction!
12.4.24 altered draw_inequality2 to use oldxmin etc.
2.3.25 2*pix at lines 316 and 908, to keep gap from getting too small.
2.16.25 removed unused variables xmin, xmax, etc. in draw_inequality2 in grafineq.c
also removed 'i', replacing the loop with i in it by while(x <= xmax)
3.5.25 supplied new visibility argument to filled_circle
*/
#include <math.h>
#include <stdlib.h> /* NULL */
#include <assert.h>
#include <stdio.h> // printf for debugging
#include "globals.h"
#include "graphstr.h"
#include "svgGraph.h"
#include "grapher.h"
#include "heap.h" /* my own malloc and free */
#include "eqn.h" /* solve */
#include "deval.h"
#include "sing.h" /* check_restrictions */
#include "mpdoc.h" /* PGRAPHDATA */
#include "grafineq.h"
#define OPPOSITE_SIGN(x,y) (((x) < 0.0 && (y) > 0.0) || ((x) > 0.0 && (y) < 0.0) || ((x == 0.0 || y == 0.0) && x*y != 0.0))
#define NSTORE 15
static int badpt(double);
static void graph_inequality1(int, graph*, int);
static double iota = VERYSMALL; /* tolerance for roundoff error;
scaled to graph ranges before use */
/*__________________________________________________________*/
#define BADLIMIT 1.0E300 /* this value must mesh with matherr.c and deval.c */
static const double overflow = (double) 1.0e200 * (double) 1.0e200;
/* 80x87 returns this value on overflow instead of generating a floating-point
exception. This variable is also needed in graph2.c.
Compiling this line causes a warning message. This warning message
is suppressed by the pragma commands above and below. */
static int badpt(double x)
{ if( x > BADLIMIT)
return 1; /*depends on definition of matherr() */
else if(x < -BADLIMIT)
return 1; /*which returns large values for errors */
return x==overflow ? 1 : 0;
}
/*_______________________________________________________________*/
static unsigned fillcolor, graphcolor, graphcolor2;
/* Problem: When rectangle n is filled, it wipes out part of the
graph line drawn at the "top" of rectangles n-1, n-2, ... when
the line is steep.
Solution: remember the last NSTORE line segments and each time
that many have been drawn, redraw them. This is done in fill_quad.
*/
/*__________________________________________________________________*/
static void fill_quad(double x1, double x2, double y1, double y2, double y3, double y4, int flag)
/* fill the quadrilateral (x1,y1), (x2,y2), (x2,y3), (x1,y4) with
fillcolor, provided flag is zero or y1 <= y4 and y2 <= y3. Assumes the background color
is already set to fillcolor.
Every NSTORE calls, play back the preceding NSTORE line segments as
discussed above.
If flag is 1, reset the counter k to 0 and do nothing else.
If flag is 2 or 4, just replay the stored calls, disregarding the
other inputs. If it's 4, assume both top and bottom lines are stored;
if 2, only one line is stored for each polygon.
If flag == 3, require y1 <= y2 and y2 <= y3 as mentioned above
before drawing. Also if flag == 3, use graphcolor2 as well as
graphcolor when redrawing old polygons, and redraw both top and bottom.
Leaves the pen at (x1,y1) unless flag is 1 or 2, when it does
not affect the pen position.
*/
{ static int k;
static double xx1[NSTORE];
static double xx2[NSTORE];
static double yy1[NSTORE];
static double yy2[NSTORE];
static double yy3[NSTORE];
static double yy4[NSTORE];
int i;
double x[4];
double y[4];
if(flag == 3 && (y4 + iota < y1 || y3 + iota < y2))
return; /* doing nothing at all */
/* without iota to correct for roundoff error, this causes
omission of the last triangle of a fill region sometimes,
when y3 == y2 but equality is not exact because of
roundoff error. */
if(flag == 1)
{ k = 0;
return;
}
if(flag == 2 || flag == 4)
{ set_graphpencolor(graphcolor);
begin_path();
for(i=0;i<k;i++)
{ move_to(xx1[i],yy1[i]);
line_to(xx2[i],yy2[i]);
}
if(flag == 4)
{ set_graphpencolor(graphcolor2);
for(i=0;i<k;i++)
{ move_to(xx1[i],yy4[i]);
line_to(xx2[i],yy3[i]);
}
}
end_path();
return;
}
x[0] = x1;
x[1] = x2;
x[2] = x2;
x[3] = x1;
y[0] = y1;
y[1] = y2;
y[2] = y3;
y[3] = y4;
set_graphpencolor(fillcolor);
fill_polygon(x,y,4);
set_graphpencolor(graphcolor);
if(k < NSTORE-1)
{ xx1[k] = x1;
xx2[k] = x2;
yy1[k] = y1;
yy2[k] = y2;
if(flag == 3)
{ yy3[k] = y3;
yy4[k] = y4;
}
++k;
}
else
{ begin_path();
for(i=0;i<k;i++)
{ move_to(xx1[i],yy1[i]);
line_to(xx2[i],yy2[i]);
}
if(flag == 3)
{ set_graphpencolor(graphcolor2);
for(i=0;i<k;i++)
{ move_to(xx1[i],yy4[i]);
line_to(xx2[i],yy3[i]);
}
}
end_path();
xx1[0] = x1;
xx2[0] = x2;
yy1[0] = y1;
yy2[0] = y2;
if(flag == 3)
{ yy3[0] = y3;
yy4[0] = y4;
}
k =1;
}
begin_path();
move_to(x1,y1);
end_path();
}
/*______________________________________________________________________*/
void graph_inequality(int whichcase, graph *g)
/* Whichcase serves to distinguish the two possible situations
with regard to singularities:
1 = numerical singularities;
3 = no singularities;
g->function will have one of the following forms:
y < f(x) or y <= f(x)
f(x) < y or f(x) <= y
or with <= for one or both < signs
or the same possibilities with > instead of < and >= instead of <=.
It is presumed that g->xfunction < y < g->yfunction is what is
to be graphed, where g->xfunction could be minusinfinity and
g->yfunction could be infinity; whether the inequalities are
< or LE can be found out by examining the original inequality g->function.
The functions f(x) and g(x) are graphed as in carefulgraph,
with proper attention to jumps and singularities.
The regions between the curves in which the inequality is true
are filled with g->fillcolor.
*/
{ fillcolor = g->fillcolor;
graphcolor = g->graphcolor;
set_graphbackgroundcolor(fillcolor);
if(equals(g->yfunction,infinity))
graph_inequality1(whichcase,g,1);
else if(equals(g->xfunction,minusinfinity))
graph_inequality1(whichcase,g,2);
set_graphbackgroundcolor(g->background);
}
/*_______________________________________________________________*/
static void graph_inequality1(int whichcase, graph *g, int kind)
/* do the work of graph_inequality on a graph of the form f(x) < y
when kind==1 or y < f(x) when kind==2. (It must be 1 or 2).
*/
{ int err;
svgDevice *device = get_device();
term f = kind == 1 ? g->xfunction : g->yfunction;
/* the function to be graphed */
term fprime = kind == 1 ? g->fprime : g->gprime; /* the derivative */
term xvar = g->independent_variable;
double *xp =(double *) ARGPTR(xvar); /* pointer to value of the independent variable */
double xmin = g->xmin;
double xmax = g->xmax;
double ymin = g->ymin;
double ymax = g->ymax;
int m = g->numberofpoints; /* number of points to be plotted */
int msing = g->nsingularities; /* dimension of array of singularities */
int mjumps = g->njumps;
double *numjumps = g->jumps;
double *singularities = g->singularities; /* sorted array of numerical singularities */
int flag,newflag; /* 0 value says last point was bad */
double x,y,oldx,oldy,slope,yprime,oldyprime,nextsing;
double gap = (xmax - xmin) / (m - 1);
double nominalgap = gap;
double epsilon = gap/1000.0;
double deltayprime;
double pix = device->xpixel;
double savex,ext;
double slopeone = (ymax-ymin)/(xmax-xmin);
double xjump,yjump,xplus,xminus,yplus,yminus;
double yboundary = kind == 1 ? g->ymax : g->ymin;
int i=0;
int next=0; /* index of next singularity to watch out for */
int nextjump = 0; /* index of next jump to watch out for */
double t;
int hradius; /* pixel radius of circles to draw at jumps */
double wradius;
fill_quad(0.0,0.0,0.0,0.0,0.0,0.0,1); /* reset the counter in fill_quad */
hradius = (int)(0.25 * device->fontheight); /* works for printer or screen */
wradius = hradius * device->xpixel; /* world coordinates of radius in x-direction */
x = *xp = xmin;
deval(f,&y);
flag = badpt(y) ; /* 1 if the function is not defined; 0 if it is */
while(flag) /* we only enter this loop if first point was bad */
{ x += gap;
*xp = x;
if(x >= xmax)
return; /* function undefined */
if(fabs(x) < VERYSMALL * (xmax - xmin))
x = *xp = 0.0;
/* probably it's really zero, this is roundoff error */
/* For example graphing log x, we get a small positive x
which should be zero. Without this we get a spurious line
because we have crossed a singularity */
if(whichcase == 1 && next < msing && x > singularities[next])
++next;
/* There can be 'singularities' that are not in the (closure of the)
domain of the function, e.g. when graphing sqrt(x tan^2x).
See sing_exp in file singular.c for more explanation.
*/
deval(f,&y); /* keep advancing till we get a good starting point */
++i; /* e.g. if the function is undefined for a while */
flag = badpt(y);
}
oldy = y;
deval(fprime,&yprime);
if(whichcase == 1 && next > 0 &&
x-gap <= singularities[next-1] &&
singularities[next-1] < x
)
/* an initial singularity. Example: 1/x < y */
{ if(yprime > 0.0 && yprime != BADVAL)
{ move_to(singularities[next-1],-device->ycliplimit);
fill_quad(singularities[next-1],x,-device->ycliplimit,y,yboundary, yboundary,0);
line_to(x,y);
}
else if(yprime < 0.0 && yprime != BADVAL)
{ move_to(singularities[next-1],device->ycliplimit);
fill_quad(singularities[next-1],x,device->ycliplimit,y,yboundary,yboundary,0);
line_to(x,y);
}
}
move_to(x,y); /* initialize graphics cursor (if nothing was drawn yet) */
/* Now here comes the main for-loop that draws segment after segment */
for (++i; x <= xmax ;i++) /* i is initialized to 1 + the number of initial bad points */
{ oldx = x; /* x[i-1] */
*xp = x = x+gap; /* x[i] */
oldy = y; /* y[i-1] */
deval(f,&y); /* y[i] */
newflag = badpt(y); /* flag refers to point i-1 */
if(flag && newflag)
{ if(next < msing && x > singularities[next])
++next; /* crossed a 'singularity' outside domain of function */
if(nextjump < mjumps && x > numjumps[nextjump])
++nextjump; /* crossed a 'jump' outside domain of function */
flag = newflag;
continue; /* function was undefined at previous point
and also at this point */
}
if(!flag && !newflag) /* previous point and this one both defined */
{ oldyprime = yprime;
deval(fprime,&yprime);
if(yprime != BADVAL)
/* if the second derivative is large we should decrease the gap */
/* or what amounts to the same thing, if y' is changing too rapidly */
/* unless the gap is already pixel-sized */
{ deltayprime = fabs(yprime-oldyprime);
if(deltayprime > slopeone && gap > 2*pix)
{ gap /= 2.0;
*xp = x = x-gap;
deval(f,&y);
deval(fprime,&yprime);
}
else if(deltayprime < 0.1 * slopeone && gap < nominalgap)
/* increase the gap again after getting through the wiggly part */
gap *= 2.0;
}
/* now test for crossing the next critical point */
if (oldyprime != BADVAL && yprime != BADVAL && OPPOSITE_SIGN(oldyprime,yprime))
/* even if they aren't BADVAL, it's conceivable we might be crossing
a pole of even order here; but nothing will be drawn in that case
anyway */
{ err = solve(fprime,xvar,x-gap,x,&ext); /* Using Brent's method, find
the exact critical point */
*xp = x; /* ssolve changes it */
if(!err) /* although we know the root is bracketed here, it can
still fail, e.g. when graphing x sin x^3 across x=0,
due to "too many iterations" */
{ savex = *xp;
*xp = x = ext;
deval(f,&t);
if (t != BADVAL &&
(nextjump >= mjumps || x <= numjumps[nextjump])
/* and not crossing a jump */
)
{ fill_quad(oldx,ext,oldy,t,yboundary,yboundary,0);
move_to(oldx,oldy);
line_to(ext,t); /* then draw! */
}
else
{ move_to(ext,t); /* without drawing */
if(nextjump < mjumps && x > numjumps[nextjump])
++nextjump;
}
*xp = x = savex;
if (t != BADVAL &&
(nextjump >= mjumps || x <= numjumps[nextjump])
/* and not crossing a jump */
)
{ fill_quad(ext,x,t,y,yboundary,yboundary,0);
move_to(ext,t);
line_to(x,y); /* then draw! */
}
else
{ move_to(x,y); /* without drawing */
if(nextjump < mjumps && x > numjumps[nextjump])
++nextjump;
}
flag = newflag;
continue;
}
/* we get here (1) when the curve is too FLAT for Brent's
method, so we aren't in danger of too many wiggles;
or (2) when crossing an even-order singularity. But
we don't know which yet, so we can't draw. */
}
}
/* That takes care of getting extrema drawn correctly. Now let's
worry about getting singularities and jumps drawn correctly.
The code within the following switch draws singularities. The
code AFTER the switch draws circles on jumps. Thus we should
escape from the switch with 'continue' unless we have passed
a jump. */
switch(whichcase)
{ case 1: /* numerical singularities */
if ( (next >= msing) || (x < singularities[next]))
/* Not crossing a singularity. */
{ if (flag == 0 && newflag==0 && /* both points ok */
(nextjump >= mjumps || x <= numjumps[nextjump])
/* and not crossing a jump */
)
{ fill_quad(oldx,x,oldy,y,yboundary,yboundary,0);
line_to(x,y); /* then draw! */
flag =newflag;
continue;
}
else if(flag || newflag)
{ if(nextjump < mjumps && x + epsilon > numjumps[nextjump])
break; /* crossing a jump */
}
/* else one or both of x and oldx are bad values!
Don't draw */
if(newflag == 0)
move_to(x,y);
}
else /* crossing singularity, don't just draw, but make sure we
draw if necessary to make the graph go off-screen,
and finally update next. */
{ nextsing = singularities[next];
/* Check if the last point was on-screen: */
if( (i > 0) && (oldy < ymax) && (oldy > ymin))
{ /* then it was on-screen so we have to draw something*/
/* But which way? up or down? */
savex = *xp;
*xp = nextsing-epsilon;
deval(fprime,&slope);
if(slope == BADVAL)
{ *xp = nextsing - 10*epsilon;
deval(fprime,&slope);
if(slope == BADVAL)
{ *xp = nextsing - 100 *epsilon;
deval(fprime,&slope);
}
}
*xp = savex;
if(slope > slopeone) /* going up */
{ fill_quad(x,nextsing,y,device->ycliplimit,yboundary,yboundary,0);
line_to(nextsing,device->ycliplimit); /* go offscreen up */
}
else if(slope < -slopeone) /* going down */
{ fill_quad(x,nextsing,y,ymin-device->ycliplimit,yboundary,yboundary,0);
line_to(nextsing,device->ymin-device->ycliplimit); /*go offscreen down*/
}
/* else it's probably a spurious singularity
as in 1/x -1/(x-a) when a=0--why else wouldn't it be
achieving a slope of one in device coordinates when
we're only epsilon from the singularity?
*/
}
/* Now on the other side of the singularity */
if ((y < ymax) && (y > ymin)) /* then must draw*/
{ savex = *xp;
*xp = nextsing + epsilon;
deval(fprime,&slope);
if(slope == BADVAL)
{ *xp = nextsing + 10*epsilon;
deval(fprime,&slope);
if(slope == BADVAL)
{ *xp = nextsing + 100 *epsilon;
deval(fprime,&slope);
}
}
*xp = savex;
if(slope > slopeone )
/* then we are coming on from below */
{ move_to(nextsing,device->ymin-device->ycliplimit);
fill_quad(nextsing,x,device->ymin-device->ycliplimit,y,yboundary,yboundary,0);
line_to(x,y);
}
else if( slope < slopeone ) /* coming on from above */
{ move_to(nextsing,device->ycliplimit);
fill_quad(nextsing,x,device->ycliplimit,y,yboundary,yboundary,0);
line_to(x,y);
}
*xp += gap;
}
move_to(x,y); /* harmless if we're already there, necessary if not */
++next; /* prepare for the next singularity crossing */
}
continue;
default: /* draw if both points are good. If not, don't draw */
if(nextjump < mjumps && x+epsilon > numjumps[nextjump])
break; /* crossing a jump */
if(!flag && !newflag)
{ fill_quad(oldx,x,oldy,y,yboundary,yboundary,0);
line_to(x,y); /* then draw! */
}
else
move_to(x,y);
flag = newflag;
continue;
} /* close switch */
/* Now we have to cross a jump, or we have a bad point */
/* Put in the open or closed circles for this jump */
set_graphbackgroundcolor(g->background); /* it was set to fillcolor */
xjump = numjumps[nextjump];
++nextjump;
*xp = xjump;
deval(f,&yjump);
if(flag) /* probably oldx == the jump */
{ assert(oldy==BADVAL);
*xp = oldx = xjump-epsilon;
deval(f,&oldy);
flag = 0;
}
if(newflag) /* probably x == the jump */
{ assert(y == BADVAL);
*xp = x = xjump+epsilon;
deval(f,&y);
newflag = 0;
}
if(yjump == BADVAL)
{ /* Function not defined at the jump;
draw open circles at both ends
*/
open_circle(xjump,oldy,wradius);
open_circle(xjump,y,wradius);
}
else /* Function defined at the jump */
{ xplus = xjump + epsilon;
xminus = xjump - epsilon;
*xp = xminus;
deval(f,&yminus);
*xp = xplus;
deval(f,&yplus);
if(fabs(yminus-yjump) > 2*hradius*device->ypixel)
open_circle(xjump,yminus,wradius);
if(fabs(yplus-yjump) > 2*hradius*device->ypixel)
open_circle(xjump,yplus,wradius);
filled_circle(xjump,yjump,wradius,1);
}
/* Now, try not to write through any open circles you've just drawn */
*xp = x+hradius;
if(
(nextjump >= mjumps || *xp < numjumps[nextjump]) &&
(next >= msing || *xp < singularities[next])
)
{ double y2;
deval(f,&y2);
if(y2 != BADVAL)
move_to(*xp,y2);
else
move_to(x,y);
}
else if(newflag == 0)
move_to(x,y);
set_graphbackgroundcolor(fillcolor);
} /*close for*/
fill_quad(0.0,0.0,0.0,0.0,0.0,0.0,2); /* redraw last few line segments */
}
/*_______________________________________________________________*/
void graph_inequality2(graph *g, graph *g2)
/* Draw a graph of the form f(x) < y < h(x), or with <= in one or both positions.
The function receives two graph structures, one initialized to make
an ordinary graph of f, and one initialized to make an ordinary graph
of h. It must also draw the axes, as for this type of graph 'draw' is
not called, but only this function, since two graph structures are involved.
The graph structures must either have numerical singularities or
no singularities.
It presumes that the functions to be graphed are in g->xfunction
and g2->yfunction. The original interval-inequality is in g->function,
where it can be used for the title.
*/
{ int count,singflag, singflag2;
int jumpflag, jumpflag2, crossflag;
int domflag = 0; // set to 1 when both functions are defined, 0 when not
svgDevice *device;
term f = g->xfunction;
term h = g2->yfunction; /* the functions to be graphed */
term fprime = g->fprime; /* their derivatives */
term hprime = g2->fprime;
term xvar = g->independent_variable;
double *xp =(double *) ARGPTR(xvar); /* pointer to value of the independent variable */
int m = g->numberofpoints; /* number of points to be plotted */
int msing = g->nsingularities; /* dimension of array of singularities */
int msing2 = g2->nsingularities;
int mjumps = g->njumps;
int mjumps2 = g2->njumps;
double *numjumps = g->jumps;
double *numjumps2 = g2->jumps;
double *singularities = g->singularities; /* sorted array of numerical singularities */
double *singularities2 = g2->singularities;
int flag,newflag,flag2,newflag2; /* 0 value says last point was bad */
double x,y,oldx,oldy,slope,yprime,oldyprime, nextsing;
double y2,oldy2,yprime2,oldyprime2,slope2;
preparetograph(g);
/* preparetograph changes g->xmin etc. so g2 must change to match.*/
g2->xmin = g->xmin;
g2->xmax = g->xmax;
g2->ymin = g->ymin;
g2->ymax = g->ymax;
double xmin = g->xmin;
double xmax = g->xmax;
double ymin = g->ymin;
double ymax = g->ymax;
iota = (ymax - ymin) * VERYSMALL;
double gap = (xmax - xmin) / (m - 1);
double nominalgap = gap;
double epsilon = gap/1000.0;
double deltayprime,pix;
double savex,ext,ext2;
double yleft,yleft2,yright,yright2;
double slopeone = (ymax-ymin)/(xmax-xmin);
double xjump,yjump,xplus,xminus,yplus,yminus,yjump2,yplus2,yminus2,p;
double z,z2;
int criticalflag;
int next=0; /* index of next singularity to watch out for */
int next2 = 0;
int nextjump = 0; /* index of next jump to watch out for */
int nextjump2 = 0;
double t,t2;
int hradius; /* pixel radius of circles to draw at jumps */
double wradius;
g->update = 0; /* it won't need updating after we draw it */
g2->update = 0;
device = get_device(); /* must be done after set_world so xpixel is set */
pix = device->xpixel;
set_linewidth(g->linewidth);
fillcolor = g->fillcolor;
graphcolor = g->graphcolor;
graphcolor2 = g2->graphcolor;
set_graphbackgroundcolor(fillcolor);
fill_quad(0.0,0.0,0.0,0.0,0.0,0.0,1); /* reset the counter in fill_quad */
hradius = (int)(0.25 * device->fontheight); /* works for printer or screen */
wradius = hradius * device->xpixel; /* world coordinates of radius in x-direction */
x = *xp = xmin;
deval(f,&y);
deval(h,&y2);
flag = badpt(y);
flag2 = badpt(y2);
while(flag && flag2)
/* we only enter this loop if both f and h are undefined at the left */
{ x += gap;
*xp = x;
if(x >= xmax)
return; /* f undefined throughout the graph */
if(fabs(x) < VERYSMALL * (xmax - xmin))
x = *xp = 0.0;
/* probably it's really zero, this is roundoff error */
/* For example graphing log x, we get a small positive x
which should be zero. Without this we get a spurious line
because we have crossed a singularity */
if(next < msing && x > singularities[next])
++next;
/* There can be 'singularities' that are not in the (closure of the)
domain of the function, e.g. when graphing sqrt(x tan^2 x).
See sing_exp in file singular.c for more explanation.
*/
if(next2 < msing2 && x > singularities2[next2])
++next2;
deval(f,&y); /* keep advancing till we get a good starting point */
deval(h,&y2);
flag = badpt(y);
flag2 = badpt(y2);
}
/* Now one or the other is defined (or both are defined) */
oldy = y;
oldy2 = y2;
deval(fprime,&yprime);
deval(hprime,&yprime2);
flag = badpt(y);
flag2 = badpt(y2);
if(!flag && !flag2)
domflag = 1;
if(!flag && !flag2 && /* both functions defined */
msing && next > 0 &&
x-gap <= singularities[next-1] &&
singularities[next-1] < x &&
msing2 && next2 > 0 &&
x-gap <= singularities2[next2-1] &&
singularities2[next-1] < x
)
/* simultaneous initial singularities of both functions.
Example: 1/x < y < ln x
In case the singularities are not exactly simultaneous the
following code needs improvement.
*/
{ int check1 = 0; // set when yleft is defined
int check2 = 0; // set when yleft2 is defined
if(yprime > 0.0 && yprime != BADVAL)
{ yleft = -device->ycliplimit;
check1 = 1;
}
else if(yprime < 0.0 && yprime != BADVAL)
{ yleft = device->ycliplimit;
check1 = 1;
}
if(yprime2 > 0.0 && yprime2 != BADVAL)
{ yleft2 = -device->ycliplimit;
check2 = 1;
}
else if(yprime2 < 0.0 && yprime2 != BADVAL)
{ yleft2 = device->ycliplimit;
check2 = 1;
}
if(check1 && check2)
{ set_graphpencolor(g->graphcolor);
begin_path();
move_to(singularities[next-1],yleft);
line_to(x,y);
set_graphpencolor(g2->graphcolor);
move_to(singularities2[next2-1],yleft2);
line_to(x,y);
end_path();
fill_quad(singularities[next-1],x,yleft,y,y2,yleft2,3);
}
}
else if(msing && next > 0 && y != BADVAL &&
x-gap <= singularities[next-1] &&
singularities[next-1] < x
)
/* an initial singularity of f, but h is not singular */
{ if(yprime > 0.0 && yprime != BADVAL)
yleft = -device->ycliplimit;
else if(yprime < 0.0 && yprime != BADVAL)
yleft = device->ycliplimit;
else
yleft = BADVAL; /* just a signal, no meaning */
if(yleft != BADVAL)
{ begin_path();
move_to(singularities[next-1],yleft);
set_graphpencolor(g->graphcolor);
line_to(x,y);
*xp = singularities[next-1];
deval(h,&yleft2);
*xp = x;
move_to(singularities[next-1],yleft2);
set_graphpencolor(g2->graphcolor);
line_to(x,y2);
end_path();
fill_quad(singularities[next-1],x,yleft,y,y2, yleft2,3);
}
}
else if(msing2 && next2 > 0 && y2 != BADVAL &&
x-gap <= singularities2[next2-1] &&
singularities2[next2-1] < x
)
/* an initial singularity of h, but f is not singular */
{ if(yprime2 > 0.0 && yprime2 != BADVAL)
yleft2 = -device->ycliplimit;
else if(yprime2 < 0.0 && yprime2 != BADVAL)
yleft2 = device->ycliplimit;
else
yleft2 = BADVAL; /* just a signal, no meaning */
if(yleft2 != BADVAL)
{ begin_path();
move_to(singularities2[next2-1],yleft2);
set_graphpencolor(g2->graphcolor);
line_to(x,y2);
*xp = singularities[next-1];
deval(f,&yleft);
*xp = x;
move_to(singularities[next-1],yleft2);
set_graphpencolor(g->graphcolor);
line_to(x,y);
end_path();
fill_quad(singularities2[next2-1],x,yleft2,y2,y, yleft,3);
}
}
begin_path();
move_to(x,y); /* initialize graphics cursor (if nothing was drawn yet) */
end_path();
newflag = flag;
newflag2 = flag2;
/* Now here comes the main for-loop that draws segment after segment */
while (x <= xmax)
{ oldx = x;
*xp = x = x+gap;
/* Now ensure that between oldx and x there is at most one
singularity or jump. Of course f and h can have simultaneous
singularities, or one can have a jump where the other has a
singularity, but we can avoid crossing two DIFFERENT
singularities or jumps in one step. */
count = 0;
if( next < msing && singularities[next] < x && oldx < singularities[next])
{ p = singularities[next];
++count;
}
if( next2 < msing2 && singularities2[next2] < x && oldx < singularities2[next2])
{ if(count && p > singularities2[next2])
p = singularities2[next2];
else if(!count)
p = singularities2[next2];
++count;
}
if( nextjump < mjumps && numjumps[nextjump] < x && oldx < numjumps[nextjump])
{ if(count && p > numjumps[nextjump])
p = numjumps[nextjump];
else if(!count)
p = numjumps[nextjump];
++count;
}
if( nextjump2 < mjumps2 && numjumps2[nextjump2] < x && oldx < numjumps2[nextjump2])
{ if(count && p > numjumps2[nextjump2])
p = numjumps2[nextjump2];
else if(!count)
p = numjumps2[nextjump2];
++count;
}
if(count)
x = *xp = p;
oldy = y; /* y[i-1] */
oldy2 = y2;
deval(f,&y); /* y[i] */
deval(h,&y2);
flag = newflag;
flag2 = newflag2;
newflag = badpt(y); /* flag refers to point i-1 */
newflag2 = badpt(y2);
if(domflag == 0 && !newflag && !newflag2)
{ domflag = 1;
// find more exactly the edge of the common domain
double delta = gap/2.0;
for(int i = 0;i<4;i++)
{ if (domflag)
{ x = *xp = x-delta;
}
else
{ x = *xp = x + delta;
}
deval(f,&y);
deval(h,&y2);
newflag = badpt(y);
newflag2 = badpt(y2);
domflag = (!newflag && !newflag2);
delta /= 2.0;
}
}
if(!newflag && !newflag2 && !flag && !flag2)
{ /* check for a crossing point of the two functions */
/* Without 'iota' we get stuck in a loop on x < y < sqrt x
at x = 1; oldy and oldy2 are both 1.0 according to the
debugger, but not EXACTLY, and ext comes out equal to *xp below,
finding a solution at the left of the interval. A crossing
point at oldx is not allowed.
*/
if(oldy < oldy2-iota && y2 + iota < y)
crossflag = 1;
else if (oldy > oldy2 + iota && y2 > y + iota)
crossflag = 1;
else
crossflag = 0;
if(crossflag && !solve(sum(f,tnegate(h)),xvar,oldx,x,&ext))
{ /* we have a crossing point */
savex = x;
x = *xp = ext;
deval(f,&y);
deval(h,&y2);
*xp = savex;
}
}
if(flag && newflag)
{ if(next < msing && x > singularities[next])
++next; /* crossed a 'singularity' outside domain of function */
if(nextjump < mjumps && x > numjumps[nextjump])
++nextjump; /* crossed a 'jump' outside domain of function */
}
if(flag2 && newflag2)
{ if(next2 < msing2 && x > singularities2[next2])
++next2; /* crossed a 'singularity' outside domain of function */
if(nextjump2 < mjumps2 && x > numjumps2[nextjump2])
++nextjump2; /* crossed a 'jump' outside domain of function */
}
if(flag && newflag && flag2 && newflag2)
continue;
/* function (pair) (f,h) was undefined at previous point
and also at this point */
if((!flag && !newflag) ||(!flag2 && !newflag2))
/* at least one of the functions defined at oldx and at x;
so at least one graph line must be drawn */
{ oldyprime = yprime;
oldyprime2 = yprime2;
deval(fprime,&yprime);
deval(hprime,&yprime2);
if(yprime != BADVAL || yprime2 != BADVAL)
/* if the second derivative is large we should decrease the gap */
/* or what amounts to the same thing, if y' is changing too rapidly */
/* unless the gap is already pixel-sized */
{ if(!flag && !flag2 && yprime != BADVAL && yprime2 != BADVAL && oldyprime != BADVAL && oldyprime2 != BADVAL)
deltayprime = 0.5 *(fabs(yprime-oldyprime) + fabs(yprime2-oldyprime2));
else if(!flag && yprime != BADVAL && oldyprime != BADVAL)
deltayprime = 0.5 *fabs(yprime-oldyprime);
else if(!flag2 && yprime2 != BADVAL && oldyprime2 != BADVAL)
deltayprime = 0.5 *fabs(yprime2-oldyprime2);
else
deltayprime = 0.0;
if(deltayprime > slopeone && gap > 2*pix)
{ gap /= 2.0;
*xp = x = x-gap;
deval(f,&y);
deval(h,&y2);
deval(fprime,&yprime);
deval(hprime,&yprime2);
}
else if(deltayprime < 0.1 * slopeone && gap < nominalgap)
/* increase the gap again after getting through the wiggly part */
gap *= 2.0;
}
/* now test for crossing the next critical point
of either function. If we pass a critical point,
use Brent's method, find the exact critical point
and ensure that a point is plotted there.
*/
if(
(oldyprime != BADVAL && yprime != BADVAL &&
OPPOSITE_SIGN(oldyprime,yprime) &&
!solve(fprime,xvar,x-gap,x,&ext) /* solve returns 0 for success */
)
)
{ criticalflag = 1;
*xp = x; /* solve changes *xp to ext */
/* Perhaps we are crossing extrema of BOTH
functions. In that case we want the lesser one:
*/
if(
oldyprime2 != BADVAL && yprime2 != BADVAL &&
OPPOSITE_SIGN(oldyprime2,yprime2) &&
!solve(hprime,xvar,x-gap,x,&ext2) &&
ext2 < x
)
ext = ext2;
}
else if(
oldyprime2 != BADVAL && yprime2 != BADVAL &&
OPPOSITE_SIGN(oldyprime2,yprime2) &&
!solve(hprime,xvar,x-gap,x,&ext)
)
{ criticalflag = 2;
*xp = x;
}
else
{ criticalflag = 0;
*xp= x;
}
if(criticalflag)
/* passing a critical point */
{ savex = *xp;
*xp = x = ext;
deval(f,&t);
deval(h,&t2);
if(t != BADVAL && t2 != BADVAL &&
(nextjump >= mjumps || x <= numjumps[nextjump]) &&
(nextjump2 >= mjumps2 || x <= numjumps2[nextjump2])
/* and not crossing a jump */
)
{ set_graphpencolor(g->graphcolor);
begin_path();
move_to(oldx,oldy);
line_to(ext,t);
set_graphpencolor(g2->graphcolor);
move_to(oldx,oldy2);
line_to(ext,t2);
end_path();
fill_quad(oldx,ext,oldy,t,t2,oldy2,3);
}
else
{ begin_path();
move_to(ext,t); /* without drawing */
end_path();
if(nextjump < mjumps && x > numjumps[nextjump])
++nextjump;
if(nextjump2 < mjumps2 && x > numjumps2[nextjump2])
++nextjump2;
}
*xp = x = savex;
if(t != BADVAL && t2 != BADVAL &&
(nextjump >= mjumps || x <= numjumps[nextjump]) &&
(nextjump2 >= mjumps2 || x <= numjumps2[nextjump2])
/* and not crossing a jump */
)
{ fill_quad(ext,x,t,y,y2,t2,3);
set_graphpencolor(g->graphcolor);
begin_path();
move_to(ext,t);
line_to(x,y);
set_graphpencolor(g2->graphcolor);
move_to(ext,t2);
line_to(x,y2);
end_path();
}
else
{ begin_path();
move_to(x,y); /* without drawing */
end_path();
if(nextjump < mjumps && x > numjumps[nextjump])
++nextjump;
if(nextjump2 < mjumps2 && x > numjumps2[nextjump2])
++nextjump2;
}
// flag = newflag; value never used after this
continue;
}
/* we get here (1) when the curve is too FLAT for Brent's
method, so we aren't in danger of too many wiggles;
or (2) when crossing an even-order singularity. But
we don't know which yet, so we can't draw. */
}
/* That takes care of getting extrema drawn correctly. Now let's
worry about getting singularities and jumps drawn correctly.
*/
singflag = next < msing && x > singularities[next];
singflag2 = next2 < msing2 && x > singularities2[next2];
/* these flags tell whether we are crossing a singularity of f
or of h, respectively. */
jumpflag = nextjump < mjumps && x > numjumps[nextjump];
jumpflag2 = nextjump2 < mjumps2 && x > numjumps2[nextjump2];
if( !singflag && !singflag2 && !jumpflag && !jumpflag2)
{ /* Not crossing a singularity or a jump of either f or h */
if(flag == 0 && newflag==0) /* both points ok */
{ set_graphpencolor(g->graphcolor);
begin_path();
move_to(oldx,oldy);
line_to(x,y);
end_path();
}
if(flag2 == 0 && newflag2==0)
{ set_graphpencolor(g2->graphcolor);
begin_path();
move_to(oldx,oldy2);
line_to(x,y2);
end_path();
}
if(!flag && !flag2 && !newflag && !newflag2)
fill_quad(oldx,x,oldy,y,y2,oldy2,3);
else if(!flag && !flag2 && newflag2 && !newflag && nextjump2 < mjumps2 && fabs(x-numjumps2[nextjump2]) < epsilon)
{ /* we're just coming up to a jump in the second function */
*xp = x-epsilon;
deval(h,&z);
*xp = x;
if(z != BADVAL)
fill_quad(oldx,x,oldy,y,z,oldy2,3);
}
else if(!flag && !flag2 && newflag && !newflag2 && nextjump < mjumps && fabs(x-numjumps[nextjump]) < epsilon)
{ /* just coming up to a jump in the first function */
*xp = x-epsilon;
deval(f,&z);
*xp = x;
if(z != BADVAL)
fill_quad(oldx,x,oldy,z,y2,oldy2,3);
}
else if(!flag && !flag2 && newflag && newflag2 && nextjump < mjumps && fabs(x-numjumps[nextjump]) < epsilon)
{ /* just coming up to a simultaneous jump in both functions */
*xp = x-epsilon;
deval(f,&z);
deval(h,&z2);
*xp = x;
if(z != BADVAL)
fill_quad(oldx,x,oldy,z,z2,oldy2,3);
}
continue;
}
else if(singflag || singflag2) /* crossing a singularity or f or h, or of both */
{ nextsing = singflag ? singularities[next] : singularities2[next2];
savex = *xp;
*xp = nextsing-epsilon;
deval(fprime,&slope);
if(singflag && slope == BADVAL && !flag)
/* crossing a singularity of f */
{ *xp = nextsing - 10*epsilon;
deval(fprime,&slope);
if(slope == BADVAL)
{ *xp = nextsing - 100*epsilon;
deval(fprime,&slope);
}
}
*xp = savex;
deval(hprime,&slope2);
if(singflag2 && slope2 == BADVAL && !flag2)
/* crossing a singularity of h */
{ *xp = nextsing - 10*epsilon;
deval(hprime,&slope2);
if(slope2 == BADVAL)
{ *xp = nextsing - 100 *epsilon;
deval(hprime,&slope2);
}
}
*xp = savex;
if(!singflag)
{ *xp = nextsing;
deval(f,&yright);
*xp = savex;
}
else if(slope > slopeone) /* going up */
yright = device->ycliplimit;
else if(slope < -slopeone)
yright = -device->ycliplimit;
else
yright = BADVAL;
if(!singflag2)
{ *xp = nextsing;
deval(h,&yright2);
*xp = savex;
}
else if(slope2 > slopeone) /* going up */
yright2 = device->ycliplimit;
else if(slope2 < -slopeone)
yright2 = -device->ycliplimit;
else
yright2 = BADVAL; /* a signal value only */
if(yright != BADVAL && yright2 != BADVAL)
{ if(!flag)
{ set_graphpencolor(g->graphcolor);
begin_path();
move_to(oldx,oldy);
line_to(nextsing,yright);
end_path();
}
if(!flag2)
{ set_graphpencolor(g2->graphcolor);
begin_path();
move_to(oldx,oldy2);
line_to(x,yright2);
end_path();
}
if(!flag && !flag2)
fill_quad(x,nextsing,y,yright,yright2,y2,3);
}
else if(yright != BADVAL && !flag)
{ set_graphpencolor(g->graphcolor);
begin_path();
move_to(oldx,oldy);
line_to(nextsing,yright);
end_path();
}
else if(yright2 != BADVAL && !flag2)
{ set_graphpencolor(g2->graphcolor);
begin_path();
move_to(oldx,oldy2);
line_to(nextsing,yright2);
end_path();
}
/* Now on the other side of the singularity */
savex = *xp;
*xp = nextsing + epsilon;
deval(fprime,&slope);
deval(hprime,&slope2);
if(singflag && slope == BADVAL)
{ *xp = nextsing + 10*epsilon;
deval(fprime,&slope);
if(slope == BADVAL)
{ *xp = nextsing + 100 *epsilon;
deval(fprime,&slope);
}
}
*xp = savex;
if(singflag2 && slope2 == BADVAL)
{ *xp = nextsing + 10*epsilon;
deval(hprime,&slope2);
if(slope == BADVAL)
{ *xp = nextsing + 100 *epsilon;
deval(hprime,&slope2);
}
}
*xp = savex;
if(!singflag)
{ *xp = nextsing;
deval(f,&yleft);
*xp = savex;
}
else if(slope > slopeone )
/* then we are coming on from below */
yleft = -device->ycliplimit;
else if(slope < - slopeone)
yleft = device->ycliplimit;
else
yleft = BADVAL;
if(!singflag2)
{ *xp = nextsing;
deval(h,&yleft2);
*xp = savex;
}
else if(slope2 > slopeone )
yleft2 = -device->ycliplimit;
else if(slope2 < - slopeone)
yleft2 = device->ycliplimit;
else
yleft2 = BADVAL;
if(yleft != BADVAL && yleft2 != BADVAL)
{ if(!newflag)
{ set_graphpencolor(g->graphcolor);
begin_path();
move_to(nextsing,yleft);
line_to(x,y);
end_path();
}
if(!newflag2)
{ set_graphpencolor(g2->graphcolor);
begin_path();
move_to(nextsing,yleft2);
line_to(x,y2);
end_path();
}
if(!newflag && !newflag2)
fill_quad(nextsing,x,yleft,y,y2,yleft2,3);
}
else if(yleft != BADVAL && newflag)
{ set_graphpencolor(g->graphcolor);
begin_path();
move_to(nextsing,yleft);
line_to(x,y);
end_path();
}
else if(yleft2 != BADVAL && newflag2)
{ set_graphpencolor(g2->graphcolor);
begin_path();
move_to(nextsing,yleft2);
line_to(x,y2);
end_path();
}
*xp += gap;
if(singflag)
++next; /* prepare for the next singularity crossing */
if(singflag2)
++next2;
}
else if(jumpflag || jumpflag2)
{ /* crossing a jump; but then at least we're not crossing
a singularity, as arranged above. */
/* Put in the open or closed circles for this jump */
xjump = jumpflag ? numjumps[nextjump] : numjumps2[nextjump2];
if(jumpflag)
++nextjump;
if(jumpflag2)
++nextjump2;
*xp = xjump;
deval(f,&yjump);
deval(h,&yjump2);
savex = *xp;
if(flag || flag2) /* probably oldx == the jump */
{ assert(oldy==BADVAL || oldy2 == BADVAL);
*xp = oldx = xjump-epsilon;
if(oldy == BADVAL && y != BADVAL)
/* jump in f at left of interval */
{ deval(f,&oldy);
*xp = xjump + epsilon;
deval(f,&yright);
fill_quad(xjump,x,yright,y,y2,oldy2,3);
}
if(oldy2 == BADVAL && y != BADVAL)
/* jump in h at left */
{ deval(h,&oldy2);
*xp = xjump + epsilon;
deval(h,&yright2);
fill_quad(xjump,x,oldy,y,yright2,y2,3);
}
if(oldy == BADVAL && y == BADVAL)
/* simultaneous jumps at left */
{ deval(f,&oldy);
deval(h,&oldy2);
*xp = xjump+epsilon;
deval(f,&yright);
deval(h,&yright);
fill_quad(xjump,x,yright,y,yright2,y2,3);
}
// flag = flag2 = 0; values never used after this
*xp = savex;
}
else if(newflag || newflag2) /* probably x == the jump */
{ assert(y == BADVAL || y2 == BADVAL);
*xp = x = xjump+epsilon;
if(y == BADVAL && y2 != BADVAL)
/* f jumps at the right */
{ deval(f,&y);
*xp = xjump-epsilon;
deval(f,&yleft);
fill_quad(oldx,xjump,oldy,yleft,y2,oldy2,3);
}
if(y2 == BADVAL && y != BADVAL)
/* h jumps at the right */
{ deval(h,&y2);
*xp = xjump-epsilon;
deval(h,&yleft2);
fill_quad(oldx,xjump,oldy,y,yleft2,oldy2,3);
}
if(y2 == BADVAL && y == BADVAL)
/* simultaneous jumps at the right */
{ *xp = xjump-epsilon;
deval(f,&yleft);
deval(h,&yleft2);
fill_quad(oldx,xjump,oldy,yleft,yleft2,oldy2,3);
}
newflag = newflag2 = 0;
*xp = savex;
}
else /* jump is between oldx and x */
{ savex = *xp;
*xp = xjump;
if(jumpflag && !jumpflag2) /* only f jumps */
{ deval(h,&yjump2);
*xp = xjump-epsilon;
deval(f,&yleft);
*xp = xjump+epsilon;
deval(f,&yright);
fill_quad(oldx,xjump,oldy,yleft,yjump2,oldy2,3);
fill_quad(xjump,x,yright,y,y2,yjump2,3);
}
if(jumpflag2 && !jumpflag) /* only h jumps */
{ deval(f,&yjump);
*xp = xjump-epsilon;
deval(h,&yleft2);
*xp = xjump+epsilon;
deval(h,&yright2);
fill_quad(oldx,xjump,oldy,yjump,yleft2,oldy2,3);
fill_quad(xjump,x,yjump,y,y2,yright2,3);
}
if(jumpflag && jumpflag2)
{ /* simultaneous jumps inside the interval */
*xp = xjump-epsilon;
deval(f,&yleft);
deval(h,&yleft2);
*xp = xjump+epsilon;
deval(f,&yright);
deval(h,&yright2);
fill_quad(oldx,xjump,oldy,yleft,yleft2,oldy2,3);
fill_quad(xjump,x,yright,y,y2,yright2,3);
}
*xp = savex;
}
set_graphbackgroundcolor(g->background); /* it was set to fillcolor */
if(jumpflag && yjump == BADVAL)
{ /* f not defined at the jump;
draw open circles at both ends
*/
set_graphpencolor(g->graphcolor);
open_circle(xjump,oldy,wradius);
open_circle(xjump,y,wradius);
}
else if(jumpflag) /* f defined at the jump */
{ xplus = xjump + epsilon;
xminus = xjump - epsilon;
*xp = xminus;
deval(f,&yminus);
*xp = xplus;
deval(f,&yplus);
set_graphpencolor(g->graphcolor);
if(fabs(yminus-yjump) > 2*hradius*device->ypixel)
open_circle(xjump,yminus,wradius);
if(fabs(yplus-yjump) > 2*hradius*device->ypixel)
open_circle(xjump,yplus,wradius);
filled_circle(xjump,yjump,wradius,1);
}
if(jumpflag2 && yjump2 == BADVAL)
{ /* h not defined at the jump;
draw open circles at both ends
*/
set_graphpencolor(g2->graphcolor);
open_circle(xjump,oldy2,wradius);
open_circle(xjump,y2,wradius);
}
else if(jumpflag2) /* h defined at the jump */
{ xplus = xjump + epsilon;
xminus = xjump - epsilon;
*xp = xminus;
deval(h,&yminus2);
*xp = xplus;
deval(h,&yplus2);
set_graphpencolor(g2->graphcolor);
if(fabs(yminus2-yjump2) > 2*hradius*device->ypixel)
open_circle(xjump,yminus2,wradius);
if(fabs(yplus2-yjump2) > 2*hradius*device->ypixel)
open_circle(xjump,yplus2,wradius);
filled_circle(xjump,yjump2,wradius,1);
}
set_graphbackgroundcolor(fillcolor);
}
#if 0
/* Now, try not to write through any open circles you've just drawn */
*xp = x+hradius;
if(jumpflag || jumpflag2)
{ double y2;
deval(f,&y2);
if(y2 != BADVAL)
move_to(*xp,y2);
else
move_to(x,y);
}
else if(newflag == 0)
move_to(x,y);
#endif
}
fill_quad(0.0,0.0,0.0,0.0,0.0,0.0,4); /* redraw last few line segments */
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(g2->nsingularities)
{ free2(g2->singularities); /* allocated by compute_singularities */
g2->nsingularities = 0;
g2->singularities = NULL;
}
if(g2->njumps)
{ free2(g2->jumps); /* allocated by compute_jumps */
g2->njumps = 0;
g2->jumps = NULL;
}
set_graphbackgroundcolor(g->background);
redraw_axes(g); /* draw axes etc. AFTER filling regions */
}
/*_________________________________________________________________________*/
int draw_inequality2(PGRAPHDATA pGraphData)
/* draw an inequality of type f(x) < y < g(x).
return 0 if it's drawn as an MC_INEQ. Return 1 if the singularities
are too hard to compute. Nothing is drawn in that case.
Draw at nine times the size (3 times width and height) so it
can be scrolled and zoomed.
*/
{ int err = 0;
int i;
graph *g;
graph **graphs = pGraphData->graphs;
double oldxmin, oldxmax,width;
for(i=0;i<2;i++)
{ g = pGraphData->graphs[i];
oldxmin = g->xmin;
oldxmax = g->xmax;
width = g->xmax-g->xmin;
g->xmin -= width;
g->xmax += width;
if(g->dimslist)
{ if(equals(g->slist[0],undefined))
err = 1;
else
err = compute_singularities(g);
if(err)
{ g->xmin = oldxmin;
g->xmax = oldxmax;
break;
}
}
if(g->dimjumplist)
{ if(equals(g->jumplist[0],undefined))
err = 1;
else
err = compute_jumps(g);
if(err)
{ g->xmin = oldxmin;
g->xmax = oldxmax;
break;
}
}
g->xmin = oldxmin;
g->xmax = oldxmax;
}
if(!err)
{ // make the graphs 3 times as wide and high before drawing
graph *g = graphs[0];
graph *h = graphs[1];
double oldxmin,oldxmax,oldymin,oldymax;
oldxmin = g->xmin;
oldxmax = g->xmax;
oldymin = g->ymin;
oldymax = g->ymax;
// code modeled after drawLarge
double olddxmin, olddxmax, olddymin, olddymax;
int oldpxmin, oldpxmax, oldpymin, oldpymax;
int oldbordercolor;
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 larger graphs, setting h as well as g
g->border = g->background; // prevents draw() from outlining the viewport
// so sendDocument must outline the viewport if necessary
h->border = h->background;
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;
// and for h as well:
h->xmax += worldwidth;
h->xmin -= worldwidth;
h->ymax += worldheight;
h->ymin -= worldheight;
h->dxmax += devicewidth;
h->dxmin -= devicewidth;
h->dymax += deviceheight;
h->dymin -= deviceheight;
h->pxmax += pixelwidth;
h->pxmin -= pixelwidth;
h->pymax += pixelheight;
h->pymin -= pixelheight;
theDevice->pxmin = g->pxmin;
theDevice->pxmax = g->pxmax;
theDevice->pymin = g->pymin;
theDevice->pymax = g->pymax;
// end of code modeled after drawLarge
graph_inequality2(g,h);
// Now restore the original values
g->dxmin = h->dxmin = olddxmin;
g->dxmax = h->dxmax = olddxmax;
g->dymin = h->dymin = olddymin;
g->dymax = h->dymax = olddymax;
g->pxmin = h->pxmin = oldpxmin;
g->pxmax = h->pxmax = oldpxmax;
g->pymin = h->pymin = oldpymin;
g->pymax = h->pymax = oldpymax;
g->border = h->border = oldbordercolor;
// unlike in drawLarge we just restore g->xmin etc.
g->xmin = h->xmin = oldxmin;
g->xmax = h->xmax = oldxmax;
g->ymin = h->ymin = oldymin;
g->ymax = h->ymax = oldymax;
}
return err;
}
Sindbad File Manager Version 1.0, Coded By Sindbad EG ~ The Terrorists