Sindbad~EG File Manager
/*
M. Beeson, for Mathpert. Code to make the crosshairs snap to a
nearby extremum, zero crossing, or crossing with another graph on
the same axes.
8.27.98 original date
9.18.98 last modified
7.23.24 modified to use ->newaxes instead of ->whichgraph
*/
#include <assert.h>
#include <math.h>
#include <string.h>
#include <stdlib.h> /* gcvt, alloca */
#include "globals.h"
#include "graphstr.h"
#include "grapher.h"
#include "cgraph.h"
#include "ratsimp.h"
#include "mpdoc.h"
#include "snap.h"
#include "deval.h"
/*____________________________________________________________*/
#define SNAP_N 3
int snap(PDOCDATA pDocData)
/* if p = (g->selectedx, g->selectedy) is within three pixels of
an extremum of the function (for ordinary graphs) then adjust
it to be right on the extremum. Similarly for zero crossings and
y-intercepts (where g->selectedx is near zero).
If g is one of several graphs on the same axes, then use
pDocData to access the other graphs, and if p is within three
pixels of a crossing point of two graphs, adjust it to be
right on the crossing point.
So we are looking for zeroes of g->function, or zeros of
g->fprime, with respect to g->independent_variable, in case
g->graphtype is ORDINARY. g->whichgraph tells us if there are
lower-numbered graphs on the same axes; g->crosshairsflag tells
us which window owns the crosshairs.
Return 0 if the crosshairs will be relocated, 1 if not.
*/
{ int i,j,jj,k,min,max,err,k2;
term t, deriv,x;
double p,savex,delta,xpixel,test,dtest,oldtest,doldtest,temp,dcross;
double crosstest[MAXGRAPHS];
graph **graphs = pDocData->graphs;
for(i=0;i<pDocData->ngraphs;i++)
{ if(graphs[i]->crosshairsflag)
break;
}
if(i == pDocData->ngraphs)
return 1;
if(graphs[i]->graphtype != ORDINARY)
return 1;
j = i;
while(j >= 0 && graphs[j]->newaxes == 0)
--j;
min = j;
j = i+1;
if(j >= pDocData->ngraphs)
max = i;
else
{ while(j < pDocData->ngraphs && graphs[j]->newaxes == 0)
++j;
max = j == min? min : j-1;
}
/* graphs[min]... graphs[max] are the ones to examine
for crossings with graphs[i] */
x = graphs[i]->independent_variable;
t = graphs[i]->function;
deriv = graphs[i]->fprime;
p = graphs[i]->selectedx;
xpixel = (graphs[i]->xmax - graphs[i]->xmin)/
(graphs[i]->pxmax - graphs[i]->pxmin);
/* now xpixel is the measure of one pixel in world coordinates */
savex = VALUE(x);
SETVALUE(x,p);
/* First check if there are singularities in the vicinity. In
that case don't do any snapping. */
delta = SNAP_N *xpixel;
if(graphs[i]->nsingularities)
{ for(j=0;j<graphs[i]->nsingularities;j++)
{ if(fabs(graphs[i]->singularities[j]-p) < delta)
return 1;
}
}
/* Now look for zero-crossings, maxima, y-intercepts,
or crossings with other graphs.
We must snap to the NEAREST such point so we must search for all
four simultaneously and in both directions from g->selectedx at once.
*/
if(graphs[i]->selectedx == 0.0)
return 1; /* already on a y-intercept */
SETVALUE(x,graphs[i]->selectedx);
deval(t,&oldtest);
if(oldtest == 0.0 || oldtest == BADVAL)
{ SETVALUE(x,savex);
return 1;
}
deval(deriv,&doldtest);
if(doldtest == 0.0 || doldtest == BADVAL)
{ SETVALUE(x,savex);
return 1;
}
for(k=min;k<=max;k++)
deval(graphs[k]->function,&crosstest[k]);
for(jj=1; jj<=2*SNAP_N;jj++)
{ j = (jj & 1) ? 1 + jj/2 : -jj/2; /* 1,-1,2,-2,3,-3,... */
SETVALUE(x,p+j*xpixel);
deval(t,&test);
if(test == BADVAL)
{ SETVALUE(x,savex);
return 1;
}
if(test == 0.0)
{ graphs[i]->selectedx = p+j*xpixel;
SETVALUE(x,savex);
return 1;
}
if(fabs(p + j*xpixel) <= xpixel)
{ /* close to a y-intercept */
SETVALUE(x,0.0);
deval(t,&test);
if(test != BADVAL)
{ graphs[i]->selectedx = 0.0;
graphs[i]->selectedy = test; /* snap to the y-intercept */
return 0;
}
SETVALUE(x,p+j*xpixel);
}
if(oldtest < 0.0 && test > 0.0)
goto gotzero;
if(test < 0.0 && oldtest > 0.0)
goto gotzero;
deval(deriv,&dtest);
if(dtest == BADVAL || dtest == 0.0)
/* it's BADVAL e.g. at the minimum of abs(x) */
{ SETVALUE(x,savex);
graphs[i]->selectedx = p + j*xpixel;
return 1;
}
if(doldtest < 0.0 && dtest > 0.0)
goto gotextremum;
if(dtest < 0.0 && doldtest > 0.0)
goto gotextremum;
if(min == max)
continue; /* one graph only, no need to check for crossings */
/* Now check for a crossing with another graph */
for(k = min;k <= max; k++)
{ if(k==i)
continue; /* checking for a crossing of graph i with some other graph */
if(crosstest[k] == BADVAL)
continue;
deval(graphs[k]->function,&dcross);
if(dcross == BADVAL)
continue;
if(test == dcross)
{ SETVALUE(x,savex);
for(k2 = min; k2 <= max; k2++)
{ graphs[k2]->selectedy = test;
graphs[k2]->selectedx = p + j*xpixel;
}
return 1;
}
if(dcross-test < 0.0 && crosstest[k]-oldtest > 0.0)
goto gotcrossing;
if(dcross-test > 0.0 && crosstest[k]-oldtest < 0.0)
goto gotcrossing;
}
}
SETVALUE(x,savex);
return 1; /* found nothing */
gotzero:
err = solve(t,x,p+j*xpixel,p,&temp);
if(!err)
{ graphs[i]->selectedx = temp;
graphs[i]->selectedy = 0.0;
return 0;
}
SETVALUE(x,savex);
return 1;
gotextremum:
err = solve(deriv,x,p+j*xpixel,p,&temp);
if(!err)
{ graphs[i]->selectedx = temp;
SETVALUE(x,temp);
deval(t,&temp);
if(temp != BADVAL)
graphs[i]->selectedy = temp;
return 0;
}
SETVALUE(x,savex);
return 1;
gotcrossing:
err = solve(sum(t,tnegate(graphs[k]->function)),x,p+j*xpixel,p,&temp);
if(!err)
{ graphs[i]->selectedx = temp;
SETVALUE(x,temp);
deval(t,&temp);
if(temp != BADVAL)
graphs[i]->selectedy = temp;
for(k2 = min;k2<=max;k2++)
{ if(k2 == i)
continue;
graphs[k2]->selectedx = graphs[i]->selectedx;
graphs[k2]->selectedy = graphs[i]->selectedy;
}
return 0;
}
SETVALUE(x,savex);
return 1;
}
Sindbad File Manager Version 1.0, Coded By Sindbad EG ~ The Terrorists