[comp.sources.amiga] v89i210: graph - plot mathematical functions, Part07/07

page%swap@Sun.COM (Bob Page) (11/13/89)

Submitted-by: dg3i+@andrew.cmu.edu (David Gay)
Posting-number: Volume 89, Issue 210
Archive-name: applications/graph.7

# 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:
#	uio.c
#	uio.h
#	x_y.c
# This is archive 7 of a 7-part kit.
# This archive created: Sun Nov 12 18:23:32 1989
echo "extracting uio.c"
sed 's/^X//' << \SHAR_EOF > uio.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/* User interface routines */
X
X#include <exec/types.h>
X#include <exec/interrupts.h>
X#include <exec/ports.h>
X#include <exec/io.h>
X#include <exec/interrupts.h>
X#include <devices/input.h>
X#include <devices/inputevent.h>
X#define INTUITIONPRIVATE
X#include <intuition/intuitionbase.h>
X#include <intuition/intuition.h>
X#include <graphics/text.h>
X#include <libraries/diskfont.h>
X#include "libraries/arpbase.h"
X#include <math.h>
X#include <stdio.h>
X#include <string.h>
X#include <assert.h>
X#include <dos.h>
X#include <stdarg.h>
X#include <ctype.h>
X
X#include "uio.h"
X#include "graph.h"
X#include "grph.h"
X#include "list.h"
X#include "object.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#include <proto/diskfont.h>
X#define NODOS
X#include "proto/arp.h"
X
X#define DEFAVAILSIZE 2048 /* Default space reserved for AvailFonts */
X#define ARROW 42 /* Identifier of arrow gadget */
X#define CROSS 666 /* Identifer of cross gadget */
X
Xstruct fnode /* font node */
X{
X    tnode node;
X    char name[FONTLEN];
X};
X
Xtlist flist;
X
Xextern struct IntuitionBase *IntuitionBase;
X
X/* Default graph window */
Xstatic struct NewWindow graph_win = {
X    0, 0,
X    640, 200,
X    -1, -1,
X    RAWKEY | REFRESHWINDOW | CLOSEWINDOW | MENUPICK | GADGETDOWN | GADGETUP | R
XEQCLEAR | REQSET | MOUSEBUTTONS | MOUSEMOVE,
X    WINDOWSIZING | WINDOWDRAG | WINDOWDEPTH | WINDOWCLOSE | SIMPLE_REFRESH | AC
XTIVATE,
X    NULL,
X    NULL,
X    "Graph",
X    NULL,
X    NULL,
X    85, 60,
X    -1, -1,
X    WBENCHSCREEN
X};
X
X/* The two icon images */
Xstatic UWORD chip arrow_data[] = {
X    0x7fff, 0x7fff, 0x601f, 0x603f, 0x607f, 0x607f, 0x603f, 0x661f, 0x6f0f, 0x7
Xf87, 0x7fc3, 0x7fe3, 0x7fff,
X    0x0000, 0x0000, 0x1fe0, 0x1fc0, 0x1f80, 0x1f80, 0x1fc0, 0x19e0, 0x10f0, 0x0
X078, 0x003c, 0x001c, 0x0000
X};
X
Xstatic struct Image arrow_image = {
X    -1, 0, 16, 13, 2,
X    arrow_data,
X    3, 0
X};
X
Xstatic struct Gadget arrow = {
X    NULL,
X    -18, 18, 15, 13,
X    GADGHCOMP | GADGIMAGE | GRELRIGHT,
X    RELVERIFY | RIGHTBORDER | TOGGLESELECT,
X    BOOLGADGET,
X    (APTR)&arrow_image, NULL, NULL,
X    2,
X    NULL,
X    ARROW
X};
X
Xstatic UWORD chip cross_data[] = {
X    0x7fff, 0x7f7f, 0x7f7f, 0x7f7f, 0x7f7f, 0x7e3f, 0x4081, 0x7e3f, 0x7f7f, 0x7
Xf7f, 0x7f7f, 0x7f7f, 0x7fff,
X    0x0000, 0x0080, 0x0080, 0x0080, 0x0080, 0x01c0, 0x3f7e, 0x01c0, 0x0080, 0x0
X080, 0x0080, 0x0080, 0x0000
X};
X
Xstatic struct Image cross_image = {
X    -1, 0, 16, 13, 2,
X    cross_data,
X    3, 0
X};
X
Xstatic struct Gadget cross = {
X    NULL,
X    -18, 36, 15, 13,
X    GADGHCOMP | GADGIMAGE | GRELRIGHT | SELECTED,
X    RELVERIFY | RIGHTBORDER | TOGGLESELECT,
X    BOOLGADGET,
X    (APTR)&cross_image, NULL, NULL,
X    1,
X    NULL,
X    CROSS
X};
X
Xstatic struct Memory *abort_mem; /* for abort requester */
X
Xstatic struct TextAttr alert_attr = { "topaz.font", 8 };
Xstatic struct TextFont *alert_font;
X
Xstatic struct Task *me;
Xstatic struct MsgPort *inputDevPort; /* for input device */
Xstatic struct IOStdReq *inputRequestBlock;
Xstatic struct Interrupt handlerStuff;
Xstatic int inputOpen;
X
Xstatic long window_sigs;  /* Combined sigs of all windows */
X
Xstatic struct TextAttr font = { "topaz.font", 8 }; /* font for menus, requester
Xs, etc */
Xstatic struct Requester *req;    /* Current requester */
Xstatic int ok;                   /* Value of last GadgetID */
Xstatic short reqdone;            /* Req must go away. Don't activate next str g
Xadget */
Xstatic gadgevent *gadgethandler; /* Handler for gadget events */
X
X/* Add .font extension to fname (assumed of size FONTLEN) */
Xchar *addfont(char *fname)
X{
X    return strncat(fname, ".font", FONTLEN - 1 - strlen(fname));
X}
X
X/* Removes .font extension if present */
Xchar *remfont(char *fname)
X{
X    int l = strlen(fname);
X
X    if (l >= 5 && strcmp(&fname[l - 5], ".font") == 0) fname[l - 5] = '\0';
X
X    return fname;
X}
X
X/* Make a list of font names, one entry per font. Sizes, etc ignored. */
Xint make_font_list(void)
X{
X    int ok = FALSE;
X    char *abuf;
X
X    new_list(&flist);
X    if (abuf = AllocMem(DEFAVAILSIZE, 0L)) /* Alloc default avail buffer */
X    {
X        int needs = AvailFonts(abuf, DEFAVAILSIZE, AFF_MEMORY | AFF_DISK);
X
X        ok = TRUE;
X        if (needs != 0) /* Need a bigger buffer */
X        {
X            FreeMem(abuf, DEFAVAILSIZE);
X            if (abuf = AllocMem(DEFAVAILSIZE + needs, 0L))
X                if (AvailFonts(abuf, DEFAVAILSIZE + needs, AFF_MEMORY | AFF_DIS
XK))
X                    ok = FALSE; /* Definite failure */
X        }
X        if (ok) /* Construct font list */
X        {
X            struct AvailFontsHeader *hdr = (struct AvailFontsHeader *)abuf;
X            struct AvailFonts *fl = (struct AvailFonts *)(hdr + 1);
X            int i;
X
X            /* Add font entries to sorted list, by name. Duplicate entries remo
Xved */
X            for (i = hdr->afh_NumEntries; i > 0; i--, fl++)
X            {
X                struct fnode *scan;
X                char *name = fl->af_Attr.ta_Name;
X                int cmp = -1;
X
X                remfont(name); /* remove extension */
X
X                /* Find insertion position */
X                for (scan = first(&flist); succ(scan) && (cmp = strcmp(name, sc
Xan->name)) > 0; scan = succ(scan))
X                    ;
X                if (cmp != 0) /* Not already present, add to list */
X                {
X                    struct fnode *n = alloc_node(sizeof(struct fnode));
X
X                    if (!n)
X                    {
X                        ok = FALSE;
X                        break;
X                    }
X                    n->node.ln_Name = n->name;
X                    n->name[FONTLEN - 1] = '\0';
X                    strncpy(n->name, name, FONTLEN - 1);
X
X                    /* Add at correct position */
X                    insert(&flist, n, scan->node.ln_Pred);
X                }
X            }
X            if (!ok)
X            {
X                free_list((list *)&flist, sizeof(struct fnode));
X                new_list(&flist);
X            }
X        }
X        FreeMem(abuf, DEFAVAILSIZE + needs);
X    }
X    if (!ok) nomem(NULL);
X    return ok;
X}
X
X
X/* Implement mutual exclude seeing Intuition is lazy */
X/* (for border gadgets)                              */
X/* ------------------------------------------------- */
Xstatic void MutualExclude(struct Gadget *us, struct Gadget *gadg, struct Window
X *win)
X{
X    register int i;
X    register struct Gadget *gp;
X    register LONG mutex = us->MutualExclude;
X    UWORD pos;
X
X    /* scan gadget list */
X    for (i = 1, gp = gadg; gp && i != 0; i <<= 1, gp = gp->NextGadget)
X        if (i & mutex)
X        {
X            pos = RemoveGadget(win, gp);
X            gp->Flags &= ~SELECTED; /* unselect */
X            AddGadget(win, gp, pos);
X        }
X    pos = RemoveGadget(win, us);
X    us->Flags |= SELECTED;
X    AddGadget(win, us, pos);
X
X    RefreshWindowFrame(win); /* This works for border gadgets */
X}
X
X/* Implement mutual exclude seeing Intuition is lazy */
X/* (for requester gadgets)                           */
Xstatic void MutEx(struct Gadget *us, struct Requester *req)
X{
X    register int nb = 0, doneus = FALSE;
X    register struct Gadget *gp, *first = NULL;
X    register LONG mutex = us->MutualExclude;
X    UWORD pos;
X
X    /* scan gadget list */
X    for (gp = req->ReqGadget; gp && (mutex != 0 || !doneus); mutex >>= 1, gp =
Xgp->NextGadget)
X    {
X        if ((mutex & 1) || (doneus = gp == us))
X        {
X            if (!first) first = gp;
X            pos = RemoveGList(req->RWindow, gp, 1);
X            if (gp == us)
X                gp->Flags |= SELECTED;  /* select */
X            else
X                gp->Flags &= ~SELECTED; /* unselect */
X            AddGList(req->RWindow, gp, pos, 1, req);
X        }
X        if (first) nb++;
X    }
X    if (first) RefreshGList(first, req->RWindow, req, nb);
X}
X
X/* Display requester, & handle everything until it goes away */
Xint DoRequest(struct Requester *r, struct graph *g, gadgevent *handle)
X{
X    r->Flags |= NOISYREQ; /* We want keystrokes */
X
X    ok = FALSE;
X    if (Request(r, g->io.win))
X    {
X        /* setup vital info */
X        gadgethandler = handle;
X        req = r;
X        reqdone = FALSE;
X        while (next_command().command != reqgone) ; /* Wait till it leaves */
X        req = NULL; /* No req. present */
X        gadgethandler = NULL;
X    }
X    return ok;
X}
X
X/* Find first string gadget */
Xstatic struct Gadget *NextText(struct Gadget *look)
X{
X    while (look && (look->GadgetType & ~GADGETTYPE) != STRGADGET) look = look->
XNextGadget;
X    return look;
X}
X
X/* Default gadget handler, activates string gadgets in sequence & handles
X   mutual exclude. Will normally be called by custom handlers if they don't
X   have anything special to do.
X   Returens the new value of ok */
Xint std_ghandler(struct Gadget *gg, ULONG class, struct Requester *req, struct
Xgraph *g)
X{
X    if ((gg->GadgetType & ~GADGETTYPE) == STRGADGET && !reqdone) /* Activate ne
Xxt one */
X    {
X        struct Gadget *ng = NextText(gg->NextGadget);
X        if (!ng) ng = NextText(req->ReqGadget);
X        if (ng) ActivateGadget(ng, req->RWindow, req);
X    }
X    else if (gg->MutualExclude != 0) MutEx(gg, req);
X
X    return gg->GadgetID != 0;
X}
X
X/* Insert ins in front of into, checking for string overflow
X   ( sizeof(into)=maxlen ) */
Xstatic char *strinsert(char *into, char *ins, int maxlen)
X{
X    int delta = strlen(ins);
X    int start = strlen(into);
X    int i;
X
X    if (start + delta >= maxlen) start = maxlen - delta - 1;
X
X    for (i = start - 1; i >= 0; i--) into[i + delta] = into[i];
X    into[start + delta] = '\0';
X    memcpy(into, ins, delta);
X
X    return into;
X}
X
X/* Convert a lock to a path, store in to (maxlen chars long) */
Xstatic char *pathstr(char *to, long l, int maxlen)
X{
X    long tl;
X    int notfirst = FALSE;
X    struct FileInfoBlock *fib = (struct FileInfoBlock *)AllocMem(sizeof(struct
XFileInfoBlock), 0);
X
X    if (!fib) return(NULL);
X    to[0] = '\0';
X
X    do {
X        if (!Examine(l, fib))
X        {
X            to = NULL;
X            goto error;
X        }
X        if (fib->fib_DirEntryType > 0) strinsert(to, "/", maxlen);
X        /* Is this still necessary ? */
X        if (fib->fib_FileName[0] == '\0') strinsert(to, "RAM", maxlen);
X        else strinsert(to, fib->fib_FileName, maxlen);
X        tl = l;
X        l = ParentDir(l);
X        if (notfirst) UnLock(tl); /* Release allocated locks */
X        notfirst = TRUE;
X    } while (l);
X
X    *(strchr(to, '/')) = ':'; /* First name is disk name */
X
Xerror:
X    FreeMem((char *)fib, sizeof(struct FileInfoBlock));
X    return(to);
X}
X
X/* Request a file from the user (save in file), return TRUE if OK,
X   FALSE if cancelled or failed. Currently uses arp file requester. */
Xint getfile(char *file, char *msg)
X{
X    static char directory[DSIZE + 1];
X    static struct FileRequester FR;
X    char filename[FCHARS + 1];
X
X    filename[0] = '\0';
X    FR.fr_Hail = msg;
X    FR.fr_File = filename;
X    FR.fr_Dir = directory;
X
X    if (FileRequest(&FR))
X    {
X        long lock = Lock(directory, SHARED_LOCK);
X
X        if (lock)
X        {
X            if (!pathstr(file, lock, FILELEN)) /* get dir path */
X            {
X                UnLock(lock);
X                return FALSE;
X            }
X            strncat(file, filename, FILELEN - 1 - strlen(file));
X
X            UnLock(lock);
X            return TRUE;
X        }
X        else
X            alert(NULL, "Failed to lock directory", directory);
X    }
X    return FALSE;
X}
X
X/* Setup an "abort" requester, in graph g with text msg (must be as long as max
X message) */
Xstruct Requester *abort_request(struct graph *g, char *msg)
X{
X    int len = strlen(msg);
X    struct Requester *req;
X    struct Gadget *gl = NULL;
X    int height, width;
X    static struct Gadget text = {
X        NULL,
X        10, 10, 1, 1,
X        GADGHNONE, 0L, BOOLGADGET | REQGADGET
X    };
X
X    /* Construct requester */
X    height = 8 * 1 + 10 + 12 + 25;
X    width = 8 * len + 2 * 10;
X    if (width < 85) width = 85;
X
X    text.GadgetText = NULL;
X    if ((abort_mem = NewMemory()) &&
X        (req = InitReq(50, 15, width, height, abort_mem)) &&
X        SetReqBorder(req, 1, abort_mem) &&
X        AddBox(&gl, TRUE, "Stop!", 0, RELVERIFY, (width - 65) / 2, height - 25,
X 65, 15, FALSE, abort_mem) &&
X        AddIntuiText(&text.GadgetText, msg, 0, 0, abort_mem))
X    {
X        SetReqGadgets(req, gl);
X        text.NextGadget = req->ReqGadget;
X        req->ReqGadget = &text;
X        if (!Request(req, g->io.win)) req = NULL; /* display req */
X    }
X    else
X        req = NULL;
X
X    if (!req) Free(abort_mem);
X
X    return req;
X}
X
X/* Change abort requester message. msg must not be longer than the first msg */
X     
Xvoid set_abort_msg(struct Requester *req, char *msg)
X{
X    req->ReqGadget->GadgetText->IText = msg;
X    RefreshGList(req->ReqGadget, req->RWindow, req, 1);
X}
X
X/* Clear abort requester */
Xvoid end_abort_request(struct Requester *req)
X{
X    EndRequest(req, req->RWindow);
X    Free(abort_mem);
X}
X
X/* Has the user asked for an abort ? */
Xint aborted(struct Requester *req)
X{
X    int abort = FALSE;
X    struct IntuiMessage *msg;
X
X    while (msg = (struct IntuiMessage *)GetMsg(req->RWindow->UserPort))
X    {
X        ULONG class = msg->Class;
X
X        ReplyMsg((struct Message *)msg);
X
X        if (class == REFRESHWINDOW) /* Ignore refreshes at this time */
X        {
X            BeginRefresh(req->RWindow);
X            EndRefresh(req->RWindow, TRUE);
X        }
X        else if (class == GADGETUP) abort = TRUE;
X    }
X    return abort;
X}
X
X/* Display a message in graph g. You pass as many string as you want, followed
X   by (char *)NULL. This will try very hard to actually display it, calling
X   alert with the first two strings if it fails. */
Xvoid message(struct graph *g, ...)
X{
X    int nb, len;
X    va_list msgs;
X    char *scan;
X    struct Memory *m;
X    struct Requester *req;
X    struct Gadget *gl = NULL;
X    int height, width;
X    int ok = FALSE;
X
X    /* Find number of lines and maxmimum length */
X    nb = 0; len = 0;
X    va_start(msgs, g);
X    while (scan = va_arg(msgs, char *))
X    {
X        int nl = strlen(scan);
X
X        nb++;
X        if (nl > len) len = nl;
X    }
X    va_end(msgs);
X
X    /* Construct requester */
X    height = 8 * nb + 10 + 12 + 25;
X    width = 8 * len + 2 * 10;
X    if (width < 85) width = 85;
X
X    if ((m = NewMemory()) &&
X        (req = InitReq(50, 15, width, height, m)) &&
X        SetReqBorder(req, 1, m) &&
X        AddBox(&gl, TRUE, "Ok", 0, RELVERIFY | ENDGADGET, (width - 65) / 2, hei
Xght - 25, 65, 15, FALSE, m))
X    {
X        int y = 10 - 8;
X
X        ok = TRUE;
X        SetReqGadgets(req, gl);
X
X        /* Add message strings */
X        va_start(msgs, g);
X        while (ok && (scan = va_arg(msgs, char *)))
X            ok = ok && AddIntuiText(&req->ReqText, scan, 10, (y += 8), m);
X        va_end(msgs);
X
X        /* You'll have a surprise if you press Amiga-B ... */
X        if (ok && g) ok = DoRequest(req, g, std_ghandler);
X    }
X    Free(m);
X    if (!ok || !g) /* call alert */
X    {
X        va_start(msgs, g);
X        if (nb == 1)
X            alert(g ? g->io.win : NULL, va_arg(msgs, char *), NULL);
X        else if (nb >= 2)
X        {
X            char *m1 = va_arg(msgs, char *);
X            char *m2 = va_arg(msgs, char *);
X
X            alert(g ? g->io.win : NULL, m1, m2);
X        }
X    }
X}
X
X/* Display a two line auto request. Doesn't alloc any resources */
Xvoid alert(struct Window *win, char *msg1, char *msg2)
X{
X    struct IntuiText text1, text2, negative;
X    const static struct IntuiText template = {
X        0, 1, JAM1,
X        8, 0,
X        &alert_attr
X    };
X    int width, height;
X    int ysize = alert_font ? alert_font->tf_YSize : 8;
X
X    text1 = text2 = negative = template;
X    text1.TopEdge = 8;
X    text1.IText = msg1;
X    width = IntuiTextLength(&text1) + 20;
X    height = 37 + 2 * ysize;
X    if (msg2 != NULL)
X    {
X        int w;
X
X        text1.NextText = &text2;
X        text2.TopEdge = text1.TopEdge + ysize;
X        text2.IText = msg2;
X
X        height += ysize;
X        w = IntuiTextLength(&text2) + 20;
X        if (w > width) width = w;
X    }
X    negative.LeftEdge = 6;
X    negative.TopEdge = 4;
X    negative.IText = "Ok";
X
X    AutoRequest(win, &text1, NULL, &negative, 0L, 0L, width, height);
X}
X
X/* Easy no mem requester */
Xvoid nomem(struct Window *win)
X{
X    alert(win, "No memory !", NULL);
X}
X
X/* Return next menu selection that is a command (in graph g).
X   *choice is the item number of the next menu selection */
Xstatic struct cmd process(UWORD *choice, struct graph *g)
X{
X    struct MenuItem *item;
X    UWORD itemnb, subnb;
X    struct cmd cmd;
X    struct pos *rect = (struct pos *)g->s.current;
X
X    /* Prepare command */
X    cmd.command = none;
X    cmd.g = g;
X
X    /* Try & find a command */
X    while (cmd.command == none && *choice != MENUNULL)
X    {
X        item = ItemAddress(g->io.menu, *choice);
X        itemnb = ITEMNUM(*choice);
X        subnb = SUBNUM(*choice);
X
X        /* Rem: illegal choices are disabled ==> no checking here */
X        switch (MENUNUM(*choice)) {
X            case 0 : switch (itemnb) { /* Project */
X                case 0 : /* New Graph */
X                    cmd.command = _new_graph;
X                    break;
X                case 1 : /* Delete Graph */
X                    cmd.command = close;
X                    break;
X                case 2 : /* Load Graph */
X                    cmd.command = _load_graph;
X                    break;
X                case 3 : /* Save Graph */
X                    cmd.command = _save_graph;
X                    break;
X                case 4 : /* Output Graph */
X                    switch (subnb)
X                    {
X                        case 0 : /* To Printer */
X                            cmd.command = print_graph;
X                            break;
X                        case 1 : /* To Disk */
X                            cmd.command = iff_graph;
X                            break;
X                    }
X                    break;
X                case 6 : /* Load Variables */
X                    cmd.command = load_vars;
X                    break;
X                case 7 : /* Save Variables */
X                    cmd.command = save_vars;
X                    break;
X                case 9 : /* Quit */
X                    cmd.command = quit;
X                    break;
X                } break;
X
X            case 1 : switch (itemnb) { /* Graph */
X                case 0 : /* Scale */
X                    deselect(g);
X                    cmd.command = limits;
X                    break;
X                case 1 : /* Axes */
X                    deselect(g);
X                    cmd.command = axes;
X                    break;
X                case 2 : /* Zoom */
X                    cmd.command = zoom;
X                    cmd.data.zoom_in.x0 = rect->x0;
X                    cmd.data.zoom_in.y0 = rect->y0;
X                    cmd.data.zoom_in.x1 = rect->x1;
X                    cmd.data.zoom_in.y1 = rect->y1;
X                    deselect(g);
X                    break;
X                case 3 : /* Zoom Out */
X                    cmd.command = zoom_out;
X                    cmd.data.zoom_out = 2.0;
X                    break;
X                case 4 : /* Center */
X                    cmd.command = center;
X                    cmd.data.pt.x = rect->x0;
X                    cmd.data.pt.y = rect->y0;
X                    deselect(g);
X                    break;
X                } break;
X
X            case 2 : switch (itemnb) { /* Add */
X                case 0 : /* Function */
X                    deselect(g);
X                    cmd.command = add_function;
X                    break;
X                case 1 : /* Label */
X                    cmd.command = add_label;
X                    cmd.data.pt.x = rect->x0;
X                    cmd.data.pt.y = rect->y0;
X                    deselect(g);
X                    break;
X                } break;
X
X            case 3 : switch (itemnb) { /* Edit */
X                case 0 : /* Variables */
X                    cmd.command = edit_vars;
X                    break;
X                case 2 : /* Select function */
X                    {   /* Processed locally */
X                        struct object *o;
X
X                        if (o = choose_object(g, "Select"))
X                        {
X                            deselect(g);
X                            select_object(g, o);
X                        }
X                    }
X                    break;
X                case 3 : /* Deselect */
X                    /* Processed locally */
X                    deselect(g);
X                    break;
X                case 5 : /* Edit */
X                    cmd.data.o = g->s.current;
X                    cmd.command = edit;
X                    break;
X                case 6 : /* Improve */
X                    cmd.data.f = g->s.current;
X                    cmd.command = improve;
X                    break;
X                case 7 : /* Delete */
X                    cmd.data.o = g->s.current;
X                    cmd.command = del_object;
X                    break;
X                } break;
X        }
X        *choice = item->NextSelect;
X        *choice = MENUNULL;
X    }
X    return cmd;
X}
X
X/* Return next command (in any graph) */
Xstruct cmd next_command(void)
X{
X    struct cmd cmd;
X    static int gone = FALSE;
X
X    cmd.command = none;
X
X    while (cmd.command == none) /* Wait for one */
X    {
X        struct graph *g;
X
X        /* Scan all graphs, checking for commands */
X        for (g = first(&graph_list); cmd.command == none && succ(g); g = succ(g
X))
X        {
X            int moved = FALSE;
X            struct IntuiMessage *msg;
X            WORD sx, sy;
X
X            cmd.g = g;
X            /* Any pending menu selections ? */
X            if (!req && g->io.nextmenu != MENUNULL) cmd = process(&g->io.nextme
Xnu, g);
X
X            /* Check for messages on window */
X            while (cmd.command == none && (msg = (struct IntuiMessage *)GetMsg(
Xg->io.win->UserPort)))
X            {
X                /* Save interesting info */
X                ULONG class = msg->Class;
X                UWORD code = msg->Code;
X                UWORD qualifier = msg->Qualifier;
X                struct Gadget *gg = (struct Gadget *)msg->IAddress, *gadg;
X
X                sx = msg->MouseX; sy = msg->MouseY;
X                ReplyMsg((struct Message *)msg);
X
X                if (class == MOUSEMOVE) /* Accumulate moves */
X                    moved = TRUE;
X                else
X                {
X                    if (moved) mouse_move(g, sx, sy); /* process any mouse move
Xment */
X                    moved = FALSE;
X                    /* Most messages imply that the mouse button is released. M
Xake sure that the program thinks so */
X                    if (class != MOUSEBUTTONS && class != REFRESHWINDOW) mouse_
Xup(g, sx, sy);
X
X                    switch (class)
X                    {
X                        /* Note that most messages are ignored while a
X                           requester is up in *any* window */
X
X                        case RAWKEY:
X                            /* Check for Amiga-V/B (Ok, Cancel shortcut) */
X                            if (req && (qualifier & AMIGALEFT) && (code == KEYC
XODE_V || code == KEYCODE_B))
X                            {
X                                ok = code == KEYCODE_V;
X                                /* Never let this happen while a string gadget
Xis active ... */
X                                EndRequest(req, req->RWindow);
X                            }
X                            break;
X                        case CLOSEWINDOW:
X                            if (!req) cmd.command = close;
X                            break;
X                        case MENUPICK:
X                            if (!req)
X                            {
X                                g->io.nextmenu = code;
X                                cmd = process(&g->io.nextmenu, g);
X                            }
X                            break;
X                        case REFRESHWINDOW:
X                            {
X                                int changed;
X
X                                /* Refresh partially only if size hasn't change
Xd */
X                                BeginRefresh(g->io.win);
X                                changed = g->io.win->Width != g->io.oldwidth ||
X g->io.win->Height != g->io.oldheight;
X                                if (!changed) draw_graph(g, FALSE); /* No messa
Xges during refresh !!! */
X                                EndRefresh(g->io.win, TRUE);
X                                if (changed)
X                                {
X                                    set_scale(g);
X                                    draw_graph(g, TRUE);
X                                }
X                                /* Wait for refresh after requester's disappear
Xance
X                                   before signaling it */
X                                if (gone)
X                                {
X                                    gone = FALSE;
X                                    cmd.command = reqgone;
X                                }
X                            }
X                            break;
X                        case REQSET: /* Activate first string gadget */
X                            gadg = NextText(req->ReqGadget);
X                            if (gadg && !reqdone) ActivateGadget(gadg, g->io.wi
Xn, req);
X                            break;
X                        case REQCLEAR:
X                            /* Allow 1 refresh before signaling this. There is
X                               probably a better way ! */
X                            gone = TRUE;
X                            break;
X                        case GADGETUP: case GADGETDOWN:
X                            /* Ack! Hack to avoid bug in string gadget activati
Xon (V1.2-1.3) */
X                            if (class == GADGETUP && (gg->GadgetType & STRGADGE
XT) != 0 && IntuitionBase->LibNode.lib_Version <= 34) IntuitionBase->ActiveGadget
X = NULL;
X
X                            /* Handle requester hgadgets */
X                            if (gadgethandler) ok = gadgethandler(gg, class, re
Xq, g);
X                            else if (gg->GadgetID == ARROW || gg->GadgetID == C
XROSS)
X                            {   /* Select new mode */
X                                MutualExclude(gg, g->io.gadgets, g->io.win);
X                                set_mode(g, gg->GadgetID == ARROW);
X                            }
X                            break;
X                        case MOUSEBUTTONS: /* Pass on to graph */
X                            if (code == SELECTDOWN)
X                                mouse_down(g, sx, sy);
X                            else
X                                mouse_up(g, sx, sy);
X                            break;
X                    }
X                }
X            }
X            /* Handle any pending moves */
X            if (moved) mouse_move(g, sx, sy);
X        }
X        /* Wait for something to happen */
X        if (cmd.command == none) Wait(window_sigs);
X    }
X    return cmd;
X}
X
X
X/* Enable/Disable various menus */
X
Xvoid disable_rect_menus(struct graph *g)
X{
X    struct Window *win = g->io.win;
X
X    OffMenu(win, SHIFTMENU(1) | SHIFTITEM(2) | SHIFTSUB(NOSUB)); /* Zoom */
X    OffMenu(win, SHIFTMENU(1) | SHIFTITEM(4) | SHIFTSUB(NOSUB)); /* Center */
X    OffMenu(win, SHIFTMENU(2) | SHIFTITEM(1) | SHIFTSUB(NOSUB)); /* Label */
X    OffMenu(win, SHIFTMENU(3) | SHIFTITEM(3) | SHIFTSUB(NOSUB)); /* Deselect */
X     
X}
X
Xvoid enable_rect_menus(struct graph *g)
X{
X    struct Window *win = g->io.win;
X
X    OnMenu(win, SHIFTMENU(1) | SHIFTITEM(2) | SHIFTSUB(NOSUB)); /* Zoom */
X    OnMenu(win, SHIFTMENU(1) | SHIFTITEM(4) | SHIFTSUB(NOSUB)); /* Center */
X    OnMenu(win, SHIFTMENU(2) | SHIFTITEM(1) | SHIFTSUB(NOSUB)); /* Label */
X    OnMenu(win, SHIFTMENU(3) | SHIFTITEM(3) | SHIFTSUB(NOSUB)); /* Deselect */
X}
X
Xvoid disable_object_menus(struct graph *g)
X{
X    struct Window *win = g->io.win;
X
X    OffMenu(win, SHIFTMENU(3) | SHIFTITEM(3) | SHIFTSUB(NOSUB)); /* Deselect */
X     
X    OffMenu(win, SHIFTMENU(3) | SHIFTITEM(5) | SHIFTSUB(NOSUB)); /* Edit */
X    OffMenu(win, SHIFTMENU(3) | SHIFTITEM(6) | SHIFTSUB(NOSUB)); /* Improve */
X    OffMenu(win, SHIFTMENU(3) | SHIFTITEM(7) | SHIFTSUB(NOSUB)); /* Delete */
X}
X
Xvoid enable_object_menus(struct graph *g)
X{
X    struct Window *win = g->io.win;
X
X    OnMenu(win, SHIFTMENU(3) | SHIFTITEM(3) | SHIFTSUB(NOSUB)); /* Deselect */
X    OnMenu(win, SHIFTMENU(3) | SHIFTITEM(5) | SHIFTSUB(NOSUB)); /* Edit */
X    OnMenu(win, SHIFTMENU(3) | SHIFTITEM(6) | SHIFTSUB(NOSUB)); /* Improve */
X    OnMenu(win, SHIFTMENU(3) | SHIFTITEM(7) | SHIFTSUB(NOSUB)); /* Delete */
X}
X
X/* Convert double to nice string (must be of size NBLEN) */
Xchar *double2str(char *to, double x)
X{
X    int l;
X
X    if (x == NOVAL) to[0] = '\0';
X    else sprintf(to, "%-5.3g", x);
X
X    l = strlen(to);
X    while (l > 0 && to[--l] == ' ') to[l] = '\0';
X
X    return to;
X}
X
X
X/* Convert string to double */
Xdouble str2double(char *from)
X{
X    double x;
X
X    if (sscanf(from, "%lf", &x) != 1) x = NOVAL;
X
X    return x;
X}
X
X/* convert x to a string (must be of size INTLEN) */
Xchar *int2str(char *to, int x)
X{
X    if (x == INOVAL) to[0] = '\0';
X    else sprintf(to, "%d", x);
X
X    return to;
X}
X
X/* convert string to integer */
Xint str2int(char *from)
X{
X    int x;
X
X    if (sscanf(from, "%d", &x) != 1) x = INOVAL;
X
X    return x;
X}
X
X/* Removes leading & trailing blanks fron name. Returns name */
Xchar *strip(char *name)
X{
X    char *scan = name;
X
X    while (isspace(*scan)) scan++;
X
X    if (*scan)
X    {
X        char *copy = name;
X
X        while (*scan) *(copy++) = *(scan++);
X
X        while (isspace(*--copy))
X            ;
X        *(copy + 1) = '\0';
X    }
X    else
X        *name = '\0';
X
X    return name;
X}
X
X/* Hack to allow you to press Amiga-V/B even while entering a string in a
X   string requester */
Xstatic long __saveds __asm handle_ok_cancel(register __a0 struct InputEvent *ev
X, register __a1 void *dummy)
X{
X    struct InputEvent *ep, *laste;
X    static struct InputEvent retkey;
X
X    if (req)
X        /* run down the list of events to see if they pressed the magic key */
X        for (ep = ev, laste = NULL; ep != NULL; ep = ep->ie_NextEvent)
X        {
X            if (ep->ie_Class == IECLASS_RAWKEY && (ep->ie_Qualifier & AMIGALEFT
X) && (ep->ie_Code == KEYCODE_V || ep->ie_Code == KEYCODE_B) && IntuitionBase->Ac
XtiveWindow == req->RWindow)
X            {
X                reqdone = TRUE; /* The requester is going away */
X                /* Add an extra "return key" event */
X                retkey.ie_Class = IECLASS_RAWKEY;
X                retkey.ie_SubClass = 0;
X                retkey.ie_Code = 0x44; /* return key */
X                retkey.ie_Qualifier = 0;
X                retkey.ie_position = ep->ie_position;
X                retkey.ie_TimeStamp = ep->ie_TimeStamp;
X                retkey.ie_NextEvent = ep;
X                /* we can handle this event so take it off the chain */
X                if (laste == NULL)
X                    ev = &retkey;
X                else
X                    laste->ie_NextEvent = &retkey;
X                break;
X            }
X            else
X                laste = ep;
X        }
X
X   /* pass on the pointer to the event */
X   return (long)ev;
X}
X
X/* Create window, menus for a new graph */
Xint init_uio(struct graph *g)
X{
X    /* Create mode select gadgets */
X    struct Gadget *a = AllocMem(sizeof(struct Gadget), 0L);
X    struct Gadget *c = AllocMem(sizeof(struct Gadget), 0L);
X
X    ModSys(0, 1, JAM2, &font);
X    if (a && c)
X    {
X        struct Menu *ml = NULL, *project, *edit, *add, *graph;
X        struct MenuItem *print;
X
X        *a = arrow;
X        *c = cross;
X        a->NextGadget = c;
X        g->io.gadgets = graph_win.FirstGadget = a;
X        g->io.mem = NULL;
X
X        /* Create window & menus */
X        if (g->io.win = OpenWindow(&graph_win))
X            if ((g->io.mem = NewMemory()) &&
X                (project = AddMenu(&ml, NULL, "Project", MENUENABLED, g->io.mem
X)) &&
X                    AddItem(project, "New Graph", ITEMENABLED | HIGHCOMP, 0, 'N
X', FALSE, g->io.mem) &&
X                    AddItem(project, "Delete Graph", ITEMENABLED | HIGHCOMP, 0,
X 0, FALSE, g->io.mem) &&
X                    AddItem(project, "Load Graph...", ITEMENABLED | HIGHCOMP, 0
X, 'O', FALSE, g->io.mem) &&
X                    AddItem(project, "Save Graph...", ITEMENABLED | HIGHCOMP, 0
X, 'S', FALSE, g->io.mem) &&
X                    (print = AddItem(project, "Output Graph", ITEMENABLED | HIG
XHCOMP, 0, 0, TRUE, g->io.mem)) &&
X                        AddSub(print, "To Printer", ITEMENABLED | HIGHCOMP, 0,
X'P', g->io.mem) &&
X                        AddSub(print, "To Disk...", ITEMENABLED | HIGHCOMP, 0,
X0, g->io.mem) &&
X                    AddRule(project, g->io.mem) &&
X                    AddItem(project, "Load Variables...", ITEMENABLED | HIGHCOM
XP, 0, 0, FALSE, g->io.mem) &&
X                    AddItem(project, "Save Variables...", ITEMENABLED | HIGHCOM
XP, 0, 0, FALSE, g->io.mem) &&
X                    AddRule(project, g->io.mem) &&
X                    AddItem(project, "Quit", ITEMENABLED | HIGHCOMP, 0, 'Q', FA
XLSE, g->io.mem) &&
X                (graph = AddMenu(&ml, NULL, "Graph", MENUENABLED, g->io.mem)) &
X&
X                    AddItem(graph, "Scale...", ITEMENABLED | HIGHCOMP, 0, 'L',
XFALSE, g->io.mem) &&
X                    AddItem(graph, "Axes...", ITEMENABLED | HIGHCOMP, 0, 'X', F
XALSE, g->io.mem) &&
X                    AddItem(graph, "Zoom", ITEMENABLED | HIGHCOMP, 0, 'Z', FALS
XE, g->io.mem) &&
X                    AddItem(graph, "Zoom out", ITEMENABLED | HIGHCOMP, 0, 0, FA
XLSE, g->io.mem) &&
X                    AddItem(graph, "Centre", ITEMENABLED | HIGHCOMP, 0, 'C', FA
XLSE, g->io.mem) &&
X                (add = AddMenu(&ml, NULL, "Add", MENUENABLED, g->io.mem)) &&
X                    AddItem(add, "Function...", ITEMENABLED | HIGHCOMP, 0, 'F',
X FALSE, g->io.mem) &&
X                    AddItem(add, "Label...", ITEMENABLED | HIGHCOMP, 0, 0, FALS
XE, g->io.mem) &&
X                (edit = AddMenu(&ml, NULL, "Edit", MENUENABLED, g->io.mem)) &&
X                    AddItem(edit, "Variables...", ITEMENABLED | HIGHCOMP, 0, 'V
X', FALSE, g->io.mem) &&
X                    AddRule(edit, g->io.mem) &&
X                    AddItem(edit, "Select function...", ITEMENABLED | HIGHCOMP,
X 0, 0, FALSE, g->io.mem) &&
X                    AddItem(edit, "Deselect", ITEMENABLED | HIGHCOMP, 0, 0, FAL
XSE, g->io.mem) &&
X                    AddRule(edit, g->io.mem) &&
X                    AddItem(edit, "Edit...", ITEMENABLED | HIGHCOMP, 0, 'E', FA
XLSE, g->io.mem) &&
X                    AddItem(edit, "Improve", ITEMENABLED | HIGHCOMP, 0, 'I', FA
XLSE, g->io.mem) &&
X                    AddItem(edit, "Delete", ITEMENABLED | HIGHCOMP, 0, 0, FALSE
X, g->io.mem))
X            {
X                SetMenuStrip(g->io.win, ml);
X                g->io.menu = ml;
X                disable_rect_menus(g);
X                disable_object_menus(g);
X                /* Add signal bit */
X                window_sigs |= 1 << g->io.win->UserPort->mp_SigBit;
X
X                return TRUE; /* all done ok */
X            }
X            else
X            {
X                Free(g->io.mem);
X                CloseWindow(g->io.win);
X                alert(NULL, "No memory !", NULL);
X            }
X        else alert(NULL, "Couldn't open window", NULL);
X    }
X    else
X        alert(NULL, "No memory !", NULL);
X
X    if (a) FreeMem(a, sizeof(struct Gadget));
X    if (c) FreeMem(c, sizeof(struct Gadget));
X
X    return FALSE;
X}
X
X/* Close window, etc */
Xvoid cleanup_uio(struct graph *g)
X{
X    struct Gadget *gg, *next;
X
X    ClearMenuStrip(g->io.win);
X    CloseWindow(g->io.win);
X    Free(g->io.mem);
X    for (gg = g->io.gadgets; gg; gg = next)
X    {
X        next = gg->NextGadget;
X        FreeMem(gg, sizeof(struct Gadget));
X    }
X    /* Construct new signal list */
X    window_sigs = 0;
X    for (g = first(&graph_list); succ(g); g = succ(g))
X        window_sigs |= 1 << g->io.win->UserPort->mp_SigBit;
X}
X
X/* Global initialisation */
Xint init_user()
X{
X    if (!make_font_list()) return FALSE;
X    /* Open alert font */
X    alert_font = OpenFont(&alert_attr);
X
X    /* Install input handler */
X    me = FindTask(0);
X    inputDevPort = CreatePort(0,0);
X    if (inputDevPort)
X    {
X        if (inputRequestBlock = CreateStdIO(inputDevPort))
X        {
X
X            handlerStuff.is_Data = NULL;
X            handlerStuff.is_Code = (void *)handle_ok_cancel;
X            handlerStuff.is_Node.ln_Pri = 51;
X
X            if (OpenDevice("input.device", 0, inputRequestBlock, 0) == 0)
X            {
X                inputOpen = TRUE;
X                inputRequestBlock->io_Command = IND_ADDHANDLER;
X                inputRequestBlock->io_Data    = (APTR)&handlerStuff;
X
X                DoIO(inputRequestBlock);
X                return TRUE;
X            }
X        }
X    }
X    alert(NULL, "Couldn't install input handler", NULL);
X    return FALSE;
X}
X
X/* Global cleanup */
Xvoid cleanup_user()
X{
X    if (inputOpen)
X    {
X        inputRequestBlock->io_Command = IND_REMHANDLER;
X        inputRequestBlock->io_Data    = (APTR)&handlerStuff;
X        DoIO(inputRequestBlock);
X        CloseDevice(inputRequestBlock);
X    }
X    if (inputRequestBlock) DeleteStdIO(inputRequestBlock);
X    if (inputDevPort) DeletePort(inputDevPort);
X    free_list((list *)&flist, sizeof(struct fnode));
X}
X
SHAR_EOF
echo "extracting uio.h"
sed 's/^X//' << \SHAR_EOF > uio.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/* User interface routines (windows, menus, requesters, etc) */
X#ifndef UIO_H
X#define UIO_H
X
X#include "list.h"
X
X#define FONTLEN 30 /* font name length */
X#define NBLEN 25
X#define INTLEN 11
X
X/* A gadget handler routine, called for every gadget event when a requester is
Xpresent */
Xtypedef int gadgevent(struct Gadget *gadg, ULONG class, struct Requester *req,
Xstruct graph *g);
X
X/* All possible commands */
Xenum commands {none, reqgone, /* internal */
X               close, /* window closed */
X               /* menu functions */
X               _new_graph, _load_graph, _save_graph, print_graph, iff_graph, lo
Xad_vars, save_vars, quit,
X               limits, axes, zoom, zoom_out, center,
X               add_function, add_label,
X               edit, improve, del_object, edit_vars
X              };
X
X/* Description of selected command, union depends on command */
Xstruct cmd {
X    enum commands command;
X    struct graph *g;
X    union {
X        struct function *f;
X        struct object *o;
X        double zoom_out;
X        struct {
X            double x0, y0, x1, y1;
X        } zoom_in;
X        struct {
X            double x, y;
X        } pt;
X    } data;
X};
X
Xextern tlist flist;                 /* List of available font names */
X
Xint make_font_list(void);           /* (Re)make flist */
Xchar *addfont(char *fname);         /* Add/Remove ".font" from font names */
Xchar *remfont(char *fname);
X
X/* Display requester(in graph g), wait for it to leave */
X/* handle will be called for every gadget event */
Xint DoRequest(struct Requester *r, struct graph *g, gadgevent *handle);
X/* The default gadget handler (switches between text gadgets) */
Xint std_ghandler(struct Gadget *gg, ULONG class, struct Requester *req, struct
Xgraph *g);
Xint getfile(char *file, char *msg);                          /* File requester
X*/
Xstruct Requester *abort_request(struct graph *g, char *msg); /* Setup an abort
Xrequester */
Xvoid set_abort_msg(struct Requester *req, char *msg);        /* Change abort ms
Xg */
Xvoid end_abort_request(struct Requester *req);               /* Clear abort req
X */
Xint aborted(struct Requester *req);                          /* Did user abort
X? */
Xvoid message(struct graph *g, ...);                          /* Display a messa
Xge (in a graph) */
Xvoid alert(struct Window *win, char *msg1, char *msg2);      /* Use when no gra
Xph available */
Xvoid nomem(struct Window *win);                              /* Standard no mem
Xory alert */
X
Xstruct cmd next_command(void);      /* Return next selected command */
X
X/* String utility functions, convert numbers to strings */
Xchar *double2str(char *to, double x);
Xdouble str2double(char *from);
Xchar *int2str(char *to, int x);
Xint str2int(char *from);
Xchar *strip(char *name);            /* Strip leading & trailing blanks */
X
X/* Enable/Disable various menus */
Xvoid disable_rect_menus(struct graph *g);   /* Menus for 'pos' object */
Xvoid enable_rect_menus(struct graph *g);
Xvoid disable_object_menus(struct graph *g); /* For selected objects */
Xvoid enable_object_menus(struct graph *g);
X
Xint init_user(void);
Xvoid cleanup_user(void);
Xint init_uio(struct graph *g);      /* Setup/Cleanup interface for each graph *
X/
Xvoid cleanup_uio(struct graph *g);
X
X#endif
X
SHAR_EOF
echo "extracting x_y.c"
sed 's/^X//' << \SHAR_EOF > x_y.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 "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
Xstruct x_y {
X    struct function f;
X    char x[EXPRLEN], y[EXPRLEN];
X    value x_t, y_t, dx, dy;
X};
X
Xstruct point_x_y {
X    point p;
X    double t;
X};
X
Xtypedef struct point_x_y point_x_y;
X
X/*-------------------------------------------------------------------------*/
X/*                        x_y class implementation                         */
X/*-------------------------------------------------------------------------*/
X
X/* Is the function displayable ? */
Xstatic int x_y_ok(const struct x_y *this)
X{
X    return this->f.min != NOVAL && this->f.max != NOVAL &&
X           this->f.min < this->f.max &&
X           (this->f.steps == INOVAL || this->f.steps >= 3);
X}
X
X/* Free resources */
Xstatic void destroy_x_y(struct x_y *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->x_t) free_expr(this->x_t);
X    if (this->dx) free_expr(this->dx);
X    if (this->y_t) free_expr(this->y_t);
X    if (this->dy) free_expr(this->dy);
X    this->dx = this->dy = this->x_t = this->y_t = NULL;
X}
X
X/* Init dependant parts of function */
Xstatic int create_x_y(struct x_y *this)
X{
X    this->f.calc = FALSE;
X    this->f.var.name = this->f.vname;
X    this->x_t = compile(this->x);
X    if (eval_error != 0)
X    {
X        message(this->f.o.g, "Compilation error in x(t):", eval_messages[eval_e
Xrror], (char *)NULL);
X        return FALSE;
X    }
X    this->dx = differentiate(this->x_t, this->f.vname);
X    if (eval_error != NOT_DIFFERENTIABLE && eval_error != 0)
X    {
X        message(this->f.o.g, "Differentiation error (x):", eval_messages[eval_e
Xrror], (char *)NULL);
X        return FALSE;
X    }
X    this->y_t = compile(this->y);
X    if (eval_error != 0)
X    {
X        message(this->f.o.g, "Compilation error in y(t):", eval_messages[eval_e
Xrror], (char *)NULL);
X        return FALSE;
X    }
X    this->dy = differentiate(this->y_t, this->f.vname);
X    if (eval_error != NOT_DIFFERENTIABLE && eval_error != 0)
X    {
X        message(this->f.o.g, "Differentiation error(y):", eval_messages[eval_er
Xror], (char *)NULL);
X        return FALSE;
X    }
X    if (!make_var_list(this->x_t, &this->f.used) || !make_var_list(this->y_t, &
Xthis->f.used))
X        init_var_list(&this->f.used);
X    return TRUE;
X}
X
X/* Allow user to define function */
Xstatic int edit_x_y(struct x_y *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], x[EXPRLEN], y[EXPRLEN], tname[V
XARLEN], colour[INTLEN];
X    int ret = FALSE;
X
X    *ref = NULL;
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(x, this->x);
X    strcpy(y, this->y);
X    strcpy(tname, this->f.vname);
X
X    if ((m = NewMemory()) &&
X        (req = InitReq(50, 20, 255, 165, m)) &&
X        SetReqBorder(req, 1, m) &&
X        AddIntuiText(&req->ReqText, "Function", 95, 6, m) &&
X        AddText(&gl, 0, "x(", FALSE, tname, VARLEN, TRUE, 0, RELVERIFY, 25, 20,
X 24, 10, TRUE, m) &&
X        AddText(&gl, 0, ")=", FALSE, x, EXPRLEN, TRUE, 0, RELVERIFY, 81, 20, 16
X0, 10, TRUE, m) &&
X        AddText(&gl, 0, "y(   )=", FALSE, y, EXPRLEN, TRUE, 0, RELVERIFY, 81, 4
X0, 160, 10, TRUE, m) &&
X        AddText(&gl, 0, "from ", FALSE, from, NBLEN, TRUE, 0, RELVERIFY, 49, 60
X, 80, 10, TRUE, m) &&
X        AddText(&gl, 0, "to ", FALSE, to, NBLEN, TRUE, 0, RELVERIFY, 167, 60, 8
X0, 10, TRUE, m) &&
X        AddText(&gl, 0, "steps ", FALSE, steps, INTLEN, TRUE, 0, RELVERIFY, 57,
X 80, 32, 10, TRUE, m) &&
X        AddText(&gl, 0, "colour ", FALSE, colour, INTLEN, TRUE, 0, RELVERIFY, 1
X56, 80, 32, 10, TRUE, m) &&
X        (sd = AddOption(&gl, 0, "Show discontinuities", TRUE, this->f.showdisc
X* SELECTED, 0, 9, 100, 10, 10, m)) &&
X        (nd = AddOption(&gl, 0, "Allow flat discontinuities", TRUE, this->f.nic
Xedisc * SELECTED, 0, 9, 120, 10, 10, m)) &&
X        AddBox(&gl, TRUE, "Ok", 0, RELVERIFY | ENDGADGET, 40, 140, 65, 15, FALS
XE, m) &&
X        AddBox(&gl, FALSE, "Cancel", 0, RELVERIFY | ENDGADGET, 145, 140, 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);
X
X            /* Extract 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->x, x);
X            strcpy(this->y, y);
X            strcpy(this->f.vname, tname);
X
X            /* Create function */
X            destroy_x_y(this);
X            if (this->f.o.ok = x_y_ok(this)) this->f.o.ok = create_x_y(this);
X        }
X    }
X    Free(m);
X
X    return ret;
X}
X
X/* Calculate points of function */
Xstatic int calc_x_y(struct x_y *this, int allow_mes)
X{
X    double t;
X    int i;
X    struct graph *const g = this->f.o.g;
X    double const tmin = this->f.min;
X    double const tmax = this->f.max;
X    int const steps = this->f.steps == INOVAL ? DEFSTEPS : this->f.steps;
X    double const step = (tmax - tmin) / (steps - 1);
X    static char func[FNAMELEN + 30];
X
X    strcpy(func, "Can't calculate points for ");
X    strcat(func, this->f.o.name);
X    strcat(func, ":");
X
X    new_list(&this->f.pts);
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    /* Calculate steps points, spread evenly from min to max */
X    for (i = 0, t = tmin; i < steps; i++, t += step)
X    {
X        point_x_y *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            else alert(g->io.win, func, "No memory");
X            return FALSE;
X        }
X        add_tail(&this->f.pts, pt);
X
X        set_quick(&this->f.var, t);
X        pt->t = t;
X
X        pt->p.x = quick_eval(this->x_t);
X        pt->p.state = (eval_error == 0) ? EXISTS : 0;
X        if (pt->p.state == EXISTS)
X        {
X            pt->p.y = quick_eval(this->y_t);
X            pt->p.state = (eval_error == 0) ? EXISTS : 0;
X        }
X    }
X    free_quick(&this->f.var);
X    return TRUE;
X}
X
X/* Try to improve look of function by adding points. If fails, decides that
X   there is a discontinuity */
X/* see f_of_x.c for details */
Xstatic int improve_x_y(struct x_y *this)
X{
X    struct graph *const g = this->f.o.g;
X    point_x_y *pt, *next;
X    int ok = FALSE, iter, abort = FALSE;
X    double flatx = FLAT * (g->a.y.max - g->a.y.min) / g->io.win->Height;
X    double flaty = FLAT * (g->a.x.max - g->a.x.min) / g->io.win->Width;
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) flatx = flaty = 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->dx || !this->dy)
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    if (!(req = abort_request(g, "Improve: Pass 1")))
X        message(g, "No Memory !", (char *)NULL);
X    else
X    {
X        full = full_refresh(this->f.o.g);
X
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;
X
X            for (pt = first(&this->f.pts); succ(next = succ(pt)); pt = next)
X            {
X                if ((pt->p.state & (EXISTS | OK)) == EXISTS) /* Only exists */
X                {
X                    double dx, dy;
X
X                    pt->p.state |= OK;
X                    pt->p.state &= ~DISC;
X
X                    set_quick(&this->f.var, pt->t);
X                    dx = quick_eval(this->dx);
X                    if (eval_error == 0)
X                    {
X                        dy = quick_eval(this->dy);
X                        if (eval_error == 0)
X                        {
X                            double ecartx = next->p.x - pt->p.x;
X                            double errorx = fabs(ecartx - (next->t - pt->t) * d
Xx);
X                            double ecarty = next->p.y - pt->p.y;
X                            double errory = fabs(ecarty - (next->t - pt->t) * d
Xy);
X
X                            /* Check both axes */
X                            if ((errorx > fabs(ecartx) * MAXERROR && (!this->f.
Xnicedisc || errorx > flatx)) ||
X                                (errory > fabs(ecarty) * MAXERROR && (!this->f.
Xnicedisc || errory > flaty)))
X                            {
X                                pt->p.state &= ~OK;
X                                ok = FALSE;
X
X                                if (iter == MAXITER) pt->p.state |= DISC;
X                                else /* cut interval in 2 */
X                                {
X                                    point_x_y *newpt = alloc_node(this->f.sizep
Xt);
X
X                                    if (!newpt)
X                                    {
X                                        nomem(g->io.win);
X                                        abort = TRUE;
X                                        break;
X                                    }
X
X                                    newpt->t = (pt->t + next->t) / 2;
X                                    set_quick(&this->f.var, newpt->t);
X                                    newpt->p.x = quick_eval(this->x_t);
X                                    newpt->p.state = (eval_error == 0) ? EXISTS
X : 0;
X                                    if (newpt->p.state == EXISTS)
X                                    {
X                                        newpt->p.y = quick_eval(this->y_t);
X                                        newpt->p.state = (eval_error == 0) ? EX
XISTS : 0;
X                                    }
X                                    insert(&this->f.pts, newpt, pt);
X                                }
X                            }
X                        }
X                    }
X                }
X            }
X        }
X        end_abort_request(req);
X    }
X    free_quick(&this->f.var);
X    return full;
X}
X
X/* String representation of function */
Xstatic char *f2str_x_y(struct x_y *this, char *buf, int maxlen)
X{
X    buf[maxlen - 1] = '\0';
X    strncpy(buf, this->f.o.name, maxlen - 1);
X    strncat(buf, ": x(", 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->x, maxlen - strlen(buf) - 1);
X    strncat(buf, ", y(", 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->y, maxlen - strlen(buf) - 1);
X
X    return buf;
X}
X
X/* Save local data to file */
Xstatic int save_x_y(struct x_y *this, FILE *f)
X{
X    short tag = X_Y_TAG;
X    short end = X_Y_END;
X
X    return WRITE(f, tag) &&
X           WRITE(f, this->x) &&
X           WRITE(f, this->y) &&
X           WRITE(f, end);
X}
X
X/* free function */
Xstatic struct Region *delete_x_y(struct x_y *this)
X{
X    struct Region *full = full_refresh(this->f.o.g);
X
X    destroy_x_y(this);
X    FreeMem(this, sizeof(struct x_y));
X
X    return full;
X}
X
X/* Create a new function */
Xstruct x_y *new_x_y(struct graph *g, char *name)
X{
X    struct x_y *this = AllocMem(sizeof(struct x_y), MEMF_CLEAR);
X
X    if (this)
X    {
X        /* Standard init */
X        init_function(&this->f, g, name);
X        /* Local methods */
X        this->f.o.delete = (void *)delete_x_y;
X        this->f.o.edit = (void *)edit_x_y;
X        this->f.o.improve = (void *)improve_x_y;
X        this->f.o.f2str = (void *)f2str_x_y;
X        this->f.calcf = (void *)calc_x_y;
X        this->f.save = (void *)save_x_y;
X        this->f.sizept = sizeof(point_x_y);
X        return this;
X    }
X    message(g, "Couldn't create function:", "No memory", (char *)NULL);
X    return NULL;
X}
X
X/* load from file */
Xstruct x_y *load_x_y(struct graph *g, FILE *f)
X{
X    struct x_y *this = new_x_y(g, "");
X
X    if (this)
X    {
X        short end;
X
X        /* Read local data */
X        if (READ(f, this->x) &&
X            READ(f, this->y) &&
X            READ(f, end) &&
X            end == X_Y_END)
X        {
X            load_rest(&this->f, f); /* Read standard data */
X            if (this->f.o.ok = x_y_ok(this)) this->f.o.ok = create_x_y(this);
X
X            return this;
X        }
X        delete_x_y(this);
X    }
X    return NULL;
X}
X
SHAR_EOF
echo "End of archive 7 (of 7)"
# if you want to concatenate archives, remove anything after this line
exit