[comp.sources.amiga] v89i204: graph - plot mathematical functions, Part01/07

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