page%swap@Sun.COM (Bob Page) (11/13/89)
Submitted-by: dg3i+@andrew.cmu.edu (David Gay) Posting-number: Volume 89, Issue 204 Archive-name: applications/graph.1 This program draws mathematical functions on a plane. Text & axes may be added, and the result saved to disk or printed. To compile it, you'll need: - Lattice C V5.02 - My eval library (which, if I'm not mistaken, and for obscure reasons, came over comp.binaries.amiga a few months ago. It's also on fish disk 192). - The ARP include files, with a few changes to libraries/arp_pragmas.h: Add a #ifndef NODOS/#endif around the #pragmas Open to Execute. - 1 Meg of memory ... # This is a shell archive. # Remove anything above and including the cut line. # Then run the rest of the file through 'sh'. # Unpacked files will be owned by you and have default permissions. #----cut here-----cut here-----cut here-----cut here----# #!/bin/sh # shar: SHell ARchive # Run the following text through 'sh' to create: # README # coords.c # coords.h # default.c # default.h # f_of_x.c # This is archive 1 of a 7-part kit. # This archive created: Sun Nov 12 18:23:29 1989 echo "extracting README" sed 's/^X//' << \SHAR_EOF > README X This program draws mathematical functions on a plane. Text & axes may X be added, and the result saved to disk or printed. X X To compile it, you'll need: X X - Lattice C V5.02 X - My eval library (which, if I'm not mistaken, and for obscure reasons, X came over comp.binaries.amiga a few months ago. It's also on fish X disk 192). X - The ARP include files, with a few changes to libraries/arp_pragmas.h: X Add a #ifndef NODOS/#endif around the #pragmas Open to Execute. X - 1 Meg of memory ... X X To install the source correctly, create a directory graph, unshar all X this in it, and Execute mv2dir. On a floppy system, format a new disk X and unshar onto that ... (You'll have to change a few things because X the debuggable objects & executable won't fit on one disk). X X To compile, the file eval.h (from the eval library) should be accessible X with a '#include "user/eval.h"'. Now, simply type X X lmk -f lmkO ; for an optimised version X X lmk ; for a debuggable version X X The program is commented, though not extensively. If you want to add any X objects, read the file object.guidelines first. X X X~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ XDavid Gay X "(p.suiv :=: q.prec.suiv).prec :=: q.prec" X You don't want to know about this language ! X XGAY_D@ELMA.EPFL.CH, or GAY_D%ELMA.EPFL.CH@CLSEPF51.bitnet X(Till mid-august 89) X X~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ X SHAR_EOF echo "extracting coords.c" sed 's/^X//' << \SHAR_EOF > coords.c X/* X * GRAPH, Version 1.00 - 4 August 1989 X * X * Copyright 1989, David Gay. All Rights Reserved. X * This software is freely redistrubatable. X */ X X/* Set up a coordinate system in a Rastport */ X/* Copyright 1989, David Gay */ X#include <exec/types.h> X#include <graphics/regions.h> X#include <intuition/intuition.h> X#include <proto/graphics.h> X#include <proto/layers.h> X X#include <limits.h> X#include <math.h> X X#include "coords.h" X#include "tracker.h" X Xextern int _FPERR; X X/* Actual structure used */ Xstruct RWin { X struct RWindow rw; X double xmin, xscale, ymin, yscale; X double xoffset, yoffset; X struct Region *clip, *oldRegion; X int lostpos; X}; X X/*-------------------------------------------------------------------------*/ X/* RWindow definition */ X/*-------------------------------------------------------------------------*/ X X/* convert double to integer, round down. cf floor */ Xlong ftol(double x) X{ X if (x >= 0) return (long)x; X else return (long)floor(x); X} X X/* Do Move/Draw style operation func, checking for overflow, etc */ Xstatic void rdo_RWin(struct RWin *this, void (*func)(struct RastPort *rp, long Xsx, long sy), double x, double y) X{ X double sx, sy; X X if (this->lostpos) func = Move; X X _FPERR = 0; X sx = this->rw.sx((struct RWindow *)this, x); X sy = this->rw.sy((struct RWIndow *)this, y); X /* Move, Draw only accept shorts */ X this->lostpos = (_FPERR != 0 || fabs(sx) > SHRT_MAX || fabs(sy) > SHRT_MAX) X; X X if (!this->lostpos) func(this->rw.rp, ftol(sx), ftol(sy)); X} X X/* The various conversion routines, to/from ints, for lin orr log scales, x or Xy ax */ Xstatic double sx_lin(struct RWin *this, double x) X{ X return (x - this->xmin) * this->xscale + this->xoffset; X} X Xstatic double sx_log(struct RWin *this, double x) X{ X return (log10(x) - this->xmin) * this->xscale + this->xoffset; X} X Xstatic double sy_lin(struct RWin *this, double y) X{ X return (y - this->ymin) * this->yscale + this->yoffset; X} X Xstatic double sy_log(struct RWin *this, double y) X{ X return (log10(y) - this->ymin) * this->yscale + this->yoffset; X} X Xstatic double x_lin(struct RWin *this, long sx) X{ X return (sx - this->xoffset) / this->xscale + this->xmin; X} X Xstatic double x_log(struct RWin *this, long sx) X{ X return pow(10.0, (sx - this->xoffset) / this->xscale + this->xmin); X} X Xstatic double y_lin(struct RWin *this, long sy) X{ X return (sy - this->yoffset) / this->yscale + this->ymin; X} X Xstatic double y_log(struct RWin *this, long sy) X{ X return pow(10.0, (sy - this->yoffset) / this->yscale + this->ymin); X} X X/* Delete a member of this class */ Xstatic void delete_RWin(struct RWin *this) X{ X if (this->clip) X { X InstallClipRegion(this->rw.rp->Layer, this->oldRegion); X DisposeRegion(this->clip); X } X FreeMem(this, sizeof(struct RWin)); X} X X/* Create a coordinate system in Rastport rp (w by h pixels), X {...}offset : offset in rp at which coords starts (normally > 0) X {x,y}{min,max} : limits for coords X logx, logy : logarithmic scale ? X clip : setup clipping to {...}offset boundaries ? X*/ Xstruct RWindow *new_RWindow(struct RastPort *rp, long w, long h, X long leftoffset, long bottomoffset, long rightoffse Xt, long topoffset, X double xmin, double ymin, double xmax, double ymax, X X long logx, long logy, long clip) X{ X long width, height; X struct Rectangle rect; X struct Region *r; X struct RWin *this = AllocMem(sizeof(struct RWin), 0L); X X if (this) X { X /* Setup class methods, and private data */ X this->rw.delete = (void *)delete_RWin; X this->rw.rdo = (void *)rdo_RWin; X /* Setup scaling */ X this->rw.rp = rp; X this->xoffset = leftoffset; X this->yoffset = h - bottomoffset - 1; X width = w - leftoffset - rightoffset - 1; X height = h - bottomoffset - topoffset - 1; X if (logx) X { X this->xmin = log10(xmin); X this->xscale = width / (log10(xmax) - this->xmin); X this->rw.sx = (void *)sx_log; X this->rw.x = (void *)x_log; X } X else X { X this->xmin = xmin; X this->xscale = width / (xmax - this->xmin); X this->rw.sx = (void *)sx_lin; X this->rw.x = (void *)x_lin; X } X if (logy) X { X this->ymin = log10(ymin); X this->yscale = height / (this->ymin - log10(ymax)); X this->rw.sy = (void *)sy_log; X this->rw.y = (void *)y_log; X } X else X { X this->ymin = ymin; X this->yscale = height / (this->ymin - ymax); X this->rw.sy = (void *)sy_lin; X this->rw.y = (void *)y_lin; X } X X if (clip) X { X /* Setup clipping */ X if (r = NewRegion()) X { X rect.MinX = leftoffset; X rect.MaxX = w - rightoffset - 1; X rect.MinY = topoffset; X rect.MaxY = h - bottomoffset - 1; X if (OrRectRegion(r, &rect)) X { X this->clip = r; X X/* Remark: Due to a bug(?) in InstallClipRegion, make sure that the currently X installed region when EndRefresh is called is not NULL (trashed windows X otherwise...). */ X this->oldRegion = InstallClipRegion(rp->Layer, r); X } X else X { X DisposeRegion(r); X r = NULL; X } X } X if (!r) X { X FreeMem(this, sizeof(struct RWin)); X this = NULL; X } X } X else X this->clip = NULL; X } X /* Return the newly allocated instance */ X return (struct RWindow *)this; X} X SHAR_EOF echo "extracting coords.h" sed 's/^X//' << \SHAR_EOF > coords.h X/* X * GRAPH, Version 1.00 - 4 August 1989 X * X * Copyright 1989, David Gay. All Rights Reserved. X * This software is freely redistrubatable. X */ X X/* Set up a coordinate system in a Rastport */ X#ifndef COORDS_H X#define COORDS_H X X/* The class used */ Xstruct RWindow { X struct RastPort *rp; /* The rastport associated woth this coord system */ X /* The methods : */ X void (*delete)(struct RWindow *this); X /* Do a Movw/Draw, checks for overflow, etc */ X void (*rdo)(struct RWindow *this, void (*func)(struct RastPort *rp, long sx X, long sy), double x, double y); X double (*sx)(struct RWindow *this, double x); /* real coords -> rastport co Xords */ X double (*sy)(struct RWindow *this, double y); X double (*x)(struct RWindow *this, long sx); /* rastport coords -> real co Xords */ X double (*y)(struct RWindow *this, long sy); X}; X X/* Create a coordinate system in Rastport rp (w by h pixels), X {x,y}{min,max}offset : offset in rp at which coords starts (normally > 0) X {x,y}{min,max} : limits for coords X logx, logy : logarithmic scale ? X clip : setup clipping to {x,y}{min,max}offset boundaries ? X*/ Xstruct RWindow *new_RWindow(struct RastPort *rp, long w, long h, X long xminoffset, long yminoffset, long xmaxoffset, Xlong ymaxoffset, X double xmin, double ymin, double xmax, double ymax, X X long logx, long logy, long clip); Xlong ftol(double x); /* convert double to integer, round down. cf floor */ X Xextern void Move(), BigDraw(); X X/* Easy calling for Move, Draw in real coords */ X#define RMove(rwin, x, y) ((rwin)->rdo((rwin), Move, (x), (y))) X#define RDraw(rwin, x, y) ((rwin)->rdo((rwin), BigDraw, (x), (y))) X X#endif X SHAR_EOF echo "extracting default.c" sed 's/^X//' << \SHAR_EOF > default.c X/* X * GRAPH, Version 1.00 - 4 August 1989 X * X * Copyright 1989, David Gay. All Rights Reserved. X * This software is freely redistrubatable. X */ X X/* Default object methods */ X X#include "object.h" X#include "object/default.h" X#include "uio.h" X Xint notdone(struct object *this) X{ X message(this->g, "Routine unimplemented", (char *)NULL); X X return FALSE; X} X Xstruct Region *ref_uncalled(struct object *this) X{ X message(this->g, "Hurrah! You've found a bug!", (char *)NULL); X X return NULL; X} X Xint uncalled(struct object *this) X{ X message(this->g, "Hurrah! You've found a bug!", (char *)NULL); X X return FALSE; X} X SHAR_EOF echo "extracting default.h" sed 's/^X//' << \SHAR_EOF > default.h X/* X * GRAPH, Version 1.00 - 4 August 1989 X * X * Copyright 1989, David Gay. All Rights Reserved. X * This software is freely redistrubatable. X */ X X/* Default object methods */ X#ifndef DEFAULT_H X#define DEFAULT_H X Xint uncalled(struct object *this); Xstruct Region *ref_uncalled(struct object *this); Xint notdone(struct object *this); X X#endif X SHAR_EOF echo "extracting f_of_x.c" sed 's/^X//' << \SHAR_EOF > f_of_x.c X/* X * GRAPH, Version 1.00 - 4 August 1989 X * X * Copyright 1989, David Gay. All Rights Reserved. X * This software is freely redistrubatable. X */ X X#include <exec/types.h> X#include <intuition/intuition.h> X#include <graphics/text.h> X#include <math.h> X#include <string.h> X X#include "object.h" X#include "object/function.h" X#include "object/default.h" X#include "file.h" X#include "graph.h" X#include "uio.h" X#include "coords.h" X#include "list.h" X#include "grph.h" X#include "user/eval.h" X#include "user/gadgets.h" X#include "tracker.h" X X#include <proto/exec.h> X#include <proto/intuition.h> X#include <proto/graphics.h> X X/* (private) class f_of_x, inherited from function */ Xstruct f_of_x { X struct function f; X char expr[EXPRLEN]; /* the function */ X double oldxmin, oldxmax; /* limits used at last calculation */ X int waslog; /* previous x axis type */ X value function, derivee; /* the compiled function & its differential */ X}; X X/*-------------------------------------------------------------------------*/ X/* f_of_x class implementation */ X/*-------------------------------------------------------------------------*/ X X/* Return TRUE if f is displayable */ Xstatic int f_of_x_ok(const struct f_of_x *this) X{ X return (this->f.min == NOVAL || this->f.max == NOVAL || this->f.min < this- X>f.max) && X (this->f.steps == INOVAL || this->f.steps >= 3); X} X X/* free resources used by this */ Xstatic void destroy_f_of_x(struct f_of_x *this) X{ X free_var_list(&this->f.used); X if (this->f.calc) free_list(&this->f.pts, this->f.sizept); X this->f.calc = FALSE; X if (this->function) free_expr(this->function); X if (this->derivee) free_expr(this->derivee); X this->function = this->derivee = NULL; X} X X/* Initialise dependent parts of f_of_x */ Xstatic int create_f_of_x(struct f_of_x *this) X{ X this->f.calc = FALSE; X this->f.var.name = this->f.vname; X this->function = compile(this->expr); X if (eval_error != 0) X { X message(this->f.o.g, "Compilation error:", eval_messages[eval_error], ( Xchar *)NULL); X return FALSE; X } X this->derivee = differentiate(this->function, this->f.vname); X if (eval_error != NOT_DIFFERENTIABLE && eval_error != 0) X { X message(this->f.o.g, "Differentiation error:", eval_messages[eval_error X], (char *)NULL); X return FALSE; X } X if (!make_var_list(this->function, &this->f.used)) X init_var_list(&this->f.used); X return TRUE; X} X X/* Allow the user to edit this function (ref: area to refresh) */ Xstatic int edit_f_of_x(struct f_of_x *this, struct Region **ref) X{ X struct Requester *req; X struct Memory *m; X struct Gadget *gl = NULL, *sd, *nd; X char from[NBLEN], to[NBLEN], steps[INTLEN], expr[EXPRLEN], xname[VARLEN], c Xolour[INTLEN]; X int ret = FALSE; X X /* Create requester */ X double2str(from, this->f.min); X double2str(to, this->f.max); X int2str(steps, this->f.steps); X int2str(colour, this->f.colour); X strcpy(expr, this->expr); X strcpy(xname, this->f.vname); X X *ref = NULL; X if ((m = NewMemory()) && X (req = InitReq(50, 20, 255, 145, m)) && X SetReqBorder(req, 1, m) && X AddIntuiText(&req->ReqText, "Function", 95, 6, m) && X AddText(&gl, 0, "f(", FALSE, xname, VARLEN, TRUE, 0, RELVERIFY, 25, 20, X 25, 10, TRUE, m) && X AddText(&gl, 0, ")=", FALSE, expr, EXPRLEN, TRUE, 0, RELVERIFY, 81, 20, X 160, 10, TRUE, m) && X AddText(&gl, 0, "from ", FALSE, from, NBLEN, TRUE, 0, RELVERIFY, 49, 40 X, 80, 10, TRUE, m) && X AddText(&gl, 0, "to ", FALSE, to, NBLEN, TRUE, 0, RELVERIFY, 167, 40, 8 X0, 10, TRUE, m) && X AddText(&gl, 0, "steps ", FALSE, steps, INTLEN, TRUE, 0, RELVERIFY, 57, X 60, 32, 10, TRUE, m) && X AddText(&gl, 0, "colour ", FALSE, colour, INTLEN, TRUE, 0, RELVERIFY, 1 X56, 60, 32, 10, TRUE, m) && X (sd = AddOption(&gl, 0, "Show discontinuities", TRUE, this->f.showdisc X* SELECTED, 0, 9, 80, 10, 10, m)) && X (nd = AddOption(&gl, 0, "Allow flat discontinuities", TRUE, this->f.nic Xedisc * SELECTED, 0, 9, 100, 10, 10, m)) && X AddBox(&gl, TRUE, "Ok", 0, RELVERIFY | ENDGADGET, 40, 120, 65, 15, FALS XE, m) && X AddBox(&gl, FALSE, "Cancel", 0, RELVERIFY | ENDGADGET, 145, 120, 65, 15 X, FALSE, m)) X { X SetReqGadgets(req, gl); X if (ret = DoRequest(req, this->f.o.g, std_ghandler)) X { X *ref = full_refresh(this->f.o.g); /* Redraw everything */ X /* Extract typed info */ X this->f.min = str2double(from); X this->f.max = str2double(to); X this->f.steps = str2int(steps); X if ((this->f.colour = str2int(colour)) == INOVAL) this->f.colour = X1; X this->f.showdisc = (sd->Flags & SELECTED) != 0; X this->f.nicedisc = (nd->Flags & SELECTED) != 0; X strcpy(this->expr, expr); X strcpy(this->f.vname, xname); X X /* Calc new dependent info */ X destroy_f_of_x(this); X if (this->f.o.ok = f_of_x_ok(this)) this->f.o.ok = create_f_of_x(th Xis); X } X } X Free(m); X X return ret; X} X X/* Calculate the points of the function */ Xstatic int calc_f_of_x(struct f_of_x *this, int allow_mes) X{ X double x; X int i; X struct graph *const g = this->f.o.g; X /* Use graph limits if none given */ X double const xmin = this->f.min == NOVAL ? g->a.x.min : this->f.min; X double const xmax = this->f.max == NOVAL ? g->a.x.max : this->f.max; X int const xlog = g->a.x.log; X int const steps = (this->f.steps == INOVAL ? DEFSTEPS : this->f.steps) - 1; X X double const step = xlog ? pow(xmax / xmin, 1.0 / steps) : (xmax - xmin) / Xsteps; X char func[FNAMELEN + 30]; X X new_list(&this->f.pts); X X strcpy(func, "Can't calculate points for "); X strcat(func, this->f.o.name); X strcat(func, ":"); X X if (xmin >= xmax) X { X if (allow_mes) message(g, func, "xmin >= xmax", (char *)NULL); X else alert(g->io.win, "xmin >= xmax", NULL); X return FALSE; X } X if (!create_quick(&this->f.var)) X { X if (allow_mes) message(g, func, "Couldn't create variable", (char *)NUL XL); X else alert(g->io.win, func, "Couldn't create variable"); X return FALSE; X } X X /* For all steps x values (evenly spaced *on screen*) */ X for (i = 0, x = xmin; i <= steps; i++, x = xlog ? x * step : x + step) X { X point *pt = alloc_node(this->f.sizept); X X if (!pt) X { /* No mem */ X free_list(&this->f.pts, this->f.sizept); X free_quick(&this->f.var); X if (allow_mes) message(g, func, "No memory", (char *)NULL); X return FALSE; X } X add_tail(&this->f.pts, pt); X X pt->x = x; X set_quick(&this->f.var, x); X pt->y = quick_eval(this->function); X pt->state = (eval_error == 0) ? EXISTS : 0; X } X free_quick(&this->f.var); X return TRUE; X} X X/* Draw function */ Xstatic void draw_f_of_x(struct f_of_x *this, int allow_mes) X{ X struct graph *g = this->f.o.g; X X /* If xmax or xmin not specified, track values in graph */ X /* ==> function may need recalculating */ X if (this->f.calc) X if ((this->f.min == NOVAL && this->oldxmin != g->a.x.min) || X (this->f.max == NOVAL && this->oldxmax != g->a.x.max) || X this->waslog != g->a.x.log) X { X this->f.calc = FALSE; X free_list(&this->f.pts, this->f.sizept); X } X X if (!this->f.calc) this->f.calc = calc_f_of_x(this, allow_mes); X X if (this->f.calc) X { X this->oldxmin = g->a.x.min; X this->oldxmax = g->a.x.max; X this->waslog = g->a.x.log; X display_function(&this->f); X } X} X X/* Try to improve look of function by adding points. If fails, decides that X there is a discontinuity */ Xstatic struct Region *improve_f_of_x(struct f_of_x *this) X{ X struct graph *const g = this->f.o.g; X point *pt, *next; X int ok = FALSE, abort = FALSE, iter; X /*what y step constitutes a "flat" segment ? Based on window height, could Xbe better */ X double flat = FLAT * (g->a.y.max - g->a.y.min) / g->io.win->Height; X char msg[FNAMELEN + 30]; X char pass[20]; X struct Requester *req; X struct Region *full = NULL; X X /* Flat has no meaning when graph incorrect */ X if (!this->f.o.g->ok) flat = 0.0; X X if (!this->f.calc) X { X strcpy(msg, this->f.o.name); X strcpy(msg, "not calculated!"); X message(g, msg, (char *)NULL); X return NULL; X } X if (!this->derivee) X { X strcpy(msg, this->f.o.name); X strcat(msg, " wasn't differentiable"); X message(g, msg, (char *)NULL); X return NULL; X } X if (!create_quick(&this->f.var)) X { X message(g, "Couldn't create variable", (char *)NULL); X return NULL; X } X X /* Allow user to abort (can take a very long time !) */ X if (!(req = abort_request(g, "Improve: Pass 1"))) X message(g, "No Memory !", (char *)NULL); X else X { X /* Whole graph will need redrawing */ X full = full_refresh(this->f.o.g); X X /* Do MAXITER passes, or until every point ok */ X for (iter = 1; iter <= MAXITER && !ok && !abort; iter++) X { X sprintf(pass, "Improve: Pass %d", iter); X set_abort_msg(req, pass); X ok = TRUE; /* True as long as no improvements made this pass */ X X /* Scan all but last point */ X for (pt = first(&this->f.pts); succ(next = succ(pt)); pt = next) X { X if (aborted(req)) { abort = TRUE; break; } X X if ((pt->state & (EXISTS | OK)) == EXISTS) /* Only exists. Igno Xre points who's segment is "ok" */ X { X double dx; X X pt->state |= OK; X pt->state &= ~DISC; X X /* Idea: check if the differential at this point provides X a good approximation of the next point. If not, add an X extra one. X Bad idea: Use the differential at the mid point of the X segment. Allows "angles" to remain in the output. X Remark: I've tried various other schemes. This one was X the best. X */ X set_quick(&this->f.var, pt->x); X dx = quick_eval(this->derivee); X if (eval_error == 0) X { X double ecart = next->y - pt->y; X /* error: difference between first order taylor approx. X X and actual point. */ X double error = fabs(ecart - (next->x - pt->x) * dx); X X /* Should we add a point ? error compared with X difference (on y axis) between the two points, if X nicedisc, small errors (<2 pixels) are accepted X without this check */ X if (error > fabs(ecart) * MAXERROR && (!this->f.nicedis Xc || error > flat)) X { X /* Add ONE extra point between the two */ X pt->state &= ~OK; X ok = FALSE; /* We've added a point */ X X if (iter == MAXITER) pt->state |= DISC; /* This is X(maybe) a discontinuity */ X else /* currently ignores BREAKUP(Extension: add mo Xre than one point) */ X { X point *newpt = alloc_node(this->f.sizept); X X if (!newpt) X { X message(g, "No memory for point !", (char * X)NULL); X abort = TRUE; X break; /* Exit from loop ! */ X } X newpt->x = (pt->x + next->x) / 2; X set_quick(&this->f.var, newpt->x); X newpt->y = quick_eval(this->function); X newpt->state = (eval_error == 0) ? EXISTS : 0; X insert(&this->f.pts, newpt, pt); X } X } X } X } X } X } X end_abort_request(req); X } X free_quick(&this->f.var); X return full; X} X X/* Provide quick textual form of function */ Xstatic char *f2str_f_of_x(struct f_of_x *this, char *buf, int maxlen) X{ X buf[maxlen - 1] = '\0'; X strncpy(buf, this->f.o.name, maxlen - 1); X strncat(buf, "(", maxlen - strlen(buf) - 1); X strncat(buf, this->f.vname, maxlen - strlen(buf) - 1); X strncat(buf, ")=", maxlen - strlen(buf) - 1); X strncat(buf, this->expr, maxlen - strlen(buf) - 1); X X return buf; X} X X/* Did user select us ? */ Xstatic int down_f_of_x(struct f_of_x *this) X{ X struct graph *g = this->f.o.g; X X if (this->f.o.ok && this->f.calc) /* visible ? */ X { X int inside; X X if (!create_quick(&this->f.var)) X { X message(g, "Couldn't create variable", (char *)NULL); X return FALSE; X } X X set_quick(&this->f.var, g->s.x); X /* Calculate y = f(x click pos) and compare with y click pos */ X inside = fabs(g->io.rw->sy(g->io.rw, quick_eval(this->function)) - g->i Xo.rw->sy(g->io.rw, g->s.y)) < FDIST && X eval_error == 0; X free_quick(&this->f.var); X X return inside; X } X return FALSE; X} X X/* Write f_of_x specific info */ Xstatic int save_f_of_x(struct f_of_x *this, FILE *f) X{ X short tag = F_OF_X_TAG; X short end = F_OF_X_END; X X return WRITE(f, tag) && X WRITE(f, this->expr) && X WRITE(f, end); X} X X/* Delete a member of class f_of_x */ Xstatic struct Region *delete_f_of_x(struct f_of_x *this) X{ X struct Region *full = full_refresh(this->f.o.g); X X destroy_f_of_x(this); X FreeMem(this, sizeof(struct f_of_x)); X return full; X} X X/* Create a new instance of f_of_x */ Xstruct f_of_x *new_f_of_x(struct graph *g, char *name) X{ X struct f_of_x *this = AllocMem(sizeof(struct f_of_x), MEMF_CLEAR); X X if (this) X { X /* Standard init */ X init_function(&this->f, g, name); X /* Setup methods */ X this->f.save = (void *)save_f_of_x; X this->f.o.delete = (void *)delete_f_of_x; X this->f.o.down = (void *)down_f_of_x; X this->f.o.draw = (void *)draw_f_of_x; X this->f.o.edit = (void *)edit_f_of_x; X this->f.o.improve = (void *)improve_f_of_x; X this->f.o.f2str = (void *)f2str_f_of_x; X this->f.sizept = sizeof(point); X return this; X } X message(g, "Couldn't create function:", "No memory", (char *)NULL); X return NULL; X} X X/* Load f_of_x from a file */ Xstruct f_of_x *load_f_of_x(struct graph *g, FILE *f) X{ X struct f_of_x *this = new_f_of_x(g, ""); X X if (this) X { X short end; X X if (READ(f, this->expr) && X READ(f, end) && X end == F_OF_X_END) X { X /* Load standard part */ X load_rest(&this->f, f); X if (this->f.o.ok = f_of_x_ok(this)) this->f.o.ok = create_f_of_x(th Xis); X X return this; X } X delete_f_of_x(this); X } X return NULL; X} X X X SHAR_EOF echo "End of archive 1 (of 7)" # if you want to concatenate archives, remove anything after this line exit