[comp.sources.amiga] v89i206: graph - plot mathematical functions, Part03/07

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

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

# 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:
#	graph.c
#	graph.doc
#	graph.h
#	graphics.c
# This is archive 3 of a 7-part kit.
# This archive created: Sun Nov 12 18:23:30 1989
echo "extracting graph.c"
sed 's/^X//' << \SHAR_EOF > graph.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/* Main program */
X
X#include <exec/types.h>
X#include <libraries/dos.h>
X#include <stdio.h>
X#include <string.h>
X#include <math.h>
X
X#include "graph.h"
X#include "file.h"
X#include "object.h"
X#include "uio.h"
X#include "list.h"
X#include "grph.h"
X#include "user/eval.h"
X#include "tracker.h"
X
X#include <proto/exec.h>
X#include <proto/dos.h>
X
X/* Gadget ids for variables requester */
X#define VARSLIST 2
X#define VARSDEL 3
X
Xconst char *eval_messages[] = {
X    "No error",
X    "Syntax error",
X    "Out of memory",
X    "Right bracket expected",
X    "Obsolete",
X    "Left bracket expected",
X    "Function not differentiable",
X    "Recursion detected",
X    "Result not numeric"
X};
X
X/* The various libraries used */
Xstruct LayersBase *LayersBase;
Xextern struct DiskfontBase *DiskfontBase;
Xextern struct GfxBase *GfxBase;
Xextern struct IntuitionBase *IntuitionBase;
Xstruct ArpBase *ArpBase;
X
Xlist graph_list;    /* List of graphs */
Xcontext vars;       /* List of variables */
X
Xstatic int vars_saved = TRUE;
X/* Used by variable requester */
Xstatic char var[VARLEN], val[EXPRLEN];
Xstatic struct Gadget *valg, *delg;
Xstatic struct ListInfo *var_info;
X
X/* Inform objects that variable var has changed value. They may need to
X  recalculate */
Xstatic void check_vars(char *var)
X{
X    struct graph *scan;
X    struct object *o;
X
X    /* Variables are global ==> for all graphs, all objects */
X    for (scan = first(&graph_list); succ(scan); scan = succ(scan))
X        for (o = first(&scan->o_list); succ(o); o = succ(o))
X            o->var_change(o, var);
X}
X
X/* Redraw all graphs (eg after variables have changed) */
Xstatic void draw_all(void)
X{
X    struct graph *g;
X
X    for (g = first(&graph_list); succ(g); g = succ(g))
X        draw_graph(g, TRUE);
X}
X
X/* Load a graph from a file into a new graph. See file format. */
Xstatic void load_file(struct graph *from)
X{
X    FILE *f;
X    char file[FILELEN];
X
X    if (getfile(file, "Load graph"))
X        if (f = fopen(file, "r"))
X        {
X            short tag;
X
X            if (READ(f, tag) && tag == GRAPH_TAG)
X            {
X                struct graph *g = load_graph(from, f);
X
X                if (g) add_head(&graph_list, g);
X            }
X            else
X                message(from, "File is not a graph", (char *)NULL);
X            fclose(f);
X        }
X        else
X            message(from, "Couldn't open file", file, (char *)NULL);
X}
X
X/* Save current graph to a file. See file format. */
X/* TBD: Provide some default file name */
Xstatic void save_file(struct graph *g)
X{
X    FILE *f;
X    char file[FILELEN];
X
X    if (getfile(file, "Save graph"))
X        if (f = fopen(file, "w"))
X        {
X            save_graph(g, f);
X            fclose(f);
X        }
X        else
X            message(g, "Couldn't open file", file, (char *)NULL);
X}
X
X/* Read variables from a file. See file format. This adds variables to the
X   current context, it doesn't eliminate the old ones. */
Xstatic void load_variables(struct graph *from)
X{
X    FILE *f;
X    char file[FILELEN];
X
X    if (getfile(file, "Load variables"))
X        if (f = fopen(file, "r"))
X        {
X            short tag;
X            int ok = FALSE;
X
X            if (READ(f, tag) && tag == FILE_TAG) /* This is a variable file */
X            {
X                int done = FALSE;
X
X                ok = TRUE;
X                do /* Read all the file */
X                {
X                    short tag;
X
X                    if (READ(f, tag))
X                    {
X                        switch (tag)
X                        {
X                            case FILE_END:
X                                done = TRUE;
X                                break;
X                            case VAR_TAG: /* Another variable */
X                                {
X                                    char name[VARLEN], val[EXPRLEN];
X
X                                    if (READ(f, name) &&
X                                        READ(f, val) &&
X                                        READ(f, tag) && tag == VAR_END)
X                                    {
X                                        value vv = compile(val);
X
X                                        if (!vv || !set_var_name(name, vv))
X                                            message(from, "Failed to set variab
Xle(no memory ?)", (char *)NULL);
X                                        check_vars(name);
X                                    }
X                                    else
X                                        ok = FALSE;
X                                }
X                                break;
X                            default:
X                                ok = FALSE;
X                                break;
X                        }
X                    }
X                    else
X                        ok = FALSE;
X                } while (!done && ok);
X            }
X            fclose(f);
X            if (!ok)
X                message(from, "File does not contain variables", (char *)NULL);
X     
X            else draw_all();
X        }
X        else
X            message(from, "Couldn't open file", file, (char *)NULL);
X}
X
X/* Save variables. See file format. */
Xstatic void save_variables(struct graph *g)
X{
X    FILE *f;
X    char file[FILELEN];
X
X    if (getfile(file, "Save variables"))
X        if (f = fopen(file, "w"))
X        {
X            short tag = FILE_TAG;
X            int ok = FALSE;
X
X            if (WRITE(f, tag))
X            {
X                tnode *var;
X
X                ok = TRUE;
X                /* For all variables */
X                for (var = first(&vars); ok && succ(var); var = succ(var))
X                {
X                    short end = VAR_END;
X                    char name[VARLEN], expr[EXPRLEN];
X                    value vv = get_var_name(var->ln_Name);
X
X                    name[VARLEN - 1] = '\0';
X                    strncpy(name, var->ln_Name, VARLEN - 1);
X                    if (vv) decompile(vv, expr, EXPRLEN);
X                    else expr[0] = '\0';
X
X                    tag = VAR_TAG;
X
X                    ok = WRITE(f, tag) &&
X                         WRITE(f, name) &&
X                         WRITE(f, expr) &&
X                         WRITE(f, end);
X                }
X                tag = FILE_END;
X                if (ok) ok = WRITE(f, tag);
X            }
X            fclose(f);
X            if (!ok) message(g, "Error writing file", (char *)NULL);
X            else vars_saved = TRUE;
X        }
X        else
X            message(g, "Couldn't open file", file, (char *)NULL);
X}
X
X/* Handle variable requester. Could it be cleaned up ??? */
Xint vars_handler(struct Gadget *gg, ULONG class, struct Requester *req, struct
Xgraph *g)
X{
X    int id = gg->GadgetID;
X    int change = FALSE;     /* Has the list changed ? */
X    int actval = FALSE;     /* Activate variable value gadget */
X    int actvar = FALSE;     /*    "        "     name    "    */
X    int refresh = FALSE;    /* value & name gadgets have changed */
X    UWORD valpos, varpos;
X    struct Gadget *varg = ListStr(var_info); /* The lists string gadget */
X
X    if (gg == valg) /* Value gadget message, class is always GADGETUP */
X    {
X        valpos = RemoveGList(req->RWindow, valg, 1);
X        varpos = RemoveGList(req->RWindow, varg, 1);
X        refresh = TRUE;
X        strip(var); /* remove blanks */
X        strlwr(var);
X        if (*var)
X        {
X            value vv = compile(val), old;
X
X            if (eval_error != 0) /* Invalid value, allow correction */
X            {
X                DisplayBeep(req->RWindow->WScreen);
X                actval = TRUE;
X            }
X            else /* Try & set variable */
X            {
X                if (old = get_var_name(var)) free_expr(old);
X                else /* list changes, there will be an extra var */
X                    change = TRUE;
X
X                check_vars(var);
X                if (!set_var_name(var, vv))
X                {
X                    alert(g->io.win, "Couldn't create variable", NULL);
X                    change = FALSE;
X                }
X                val[0] = var[0] = '\0';
X                actvar = TRUE;
X            }
X        }
X        else /* blank var name -- erase val */
X        {
X            DisplayBeep(req->RWindow->WScreen);
X            actvar = TRUE;
X        }
X    }
X    else if (id == VARSDEL) /* Delete selected */
X    {
X        value vv;
X
X        valpos = RemoveGList(req->RWindow, valg, 1);
X        varpos = RemoveGList(req->RWindow, varg, 1);
X        refresh = TRUE;
X        strip(var);
X        strlwr(var);
X        if (*var && (vv = get_var_name(var)))
X        {
X            free_expr(vv);
X            free_var_name(var);
X            change = TRUE;
X            check_vars(var);
X        }
X        var[0] = val[0] = '\0';
X        actvar = TRUE;
X    }
X    else if (id == VARSLIST) /* Something in list selected */
X    {
X        if (ModifyList(gg, req, req->RWindow, class == GADGETUP) != 0 && *var)
X        {   /* New value selected from list or typed, ie new name. Switch to va
Xlue gadget */
X            value vv;
X
X            valpos = RemoveGList(req->RWindow, valg, 1);
X            varpos = RemoveGList(req->RWindow, varg, 1);
X            refresh = TRUE;
X            strip(var);
X
X            if (!(vv = get_var_name(var)) || !decompile(vv, val, EXPRLEN)) val[
X0] = '\0';
X            actval = TRUE;
X        }
X    }
X    else
X        return std_ghandler(gg, class, req, g);
X
X    /* Handle requester services */
X    if (refresh)
X    {
X        AddGList(req->RWindow, varg, varpos, 1, req);
X        AddGList(req->RWindow, valg, valpos, 1, req);
X        RefreshGList(valg, req->RWindow, req, 1);
X        RefreshGList(varg, req->RWindow, req, 1);
X    }
X
X    if (change) /* Variable list changed */
X        if (!ChangeList(var_info, &vars, req, req->RWindow))
X        {
X            EndRequest(req, req->RWindow);
X            /* Not enough memory ... */
X            actval = actvar = FALSE;
X       }
X
X    if (actval) ActivateGadget(valg, req->RWindow, req);
X    if (actvar) ActivateGadget(varg, req->RWindow, req);
X    vars_saved = FALSE;
X
X    return FALSE;
X}
X
X/* Setup variables requester */
Xvoid enter_vars(struct graph *g)
X{
X    struct Requester *req;
X    struct Memory *m;
X    struct Gadget *gl = NULL;
X
X    if ((m = NewMemory()) &&
X        (req = InitReq(50, 20, 285, 126, m)) &&
X        SetReqBorder(req, 1, m) &&
X        AddIntuiText(&req->ReqText, "Edit variables", 86, 6, m) &&
X        (var_info = AddList(&gl, VARSLIST, "Variables", &vars, var, VARLEN, 0,
XRELVERIFY, 20, 20, 160, 80, TRUE, m)) &&
X        (valg = AddText(&gl, VARSLIST, "Value", FALSE, val, EXPRLEN, TRUE, 0, R
XELVERIFY, 64, 110, 116, 10, TRUE, m)) &&
X        AddBox(&gl, TRUE, "Ok", 0, RELVERIFY | ENDGADGET, 200, 23, 65, 15, FALS
XE, m) &&
X        AddBox(&gl, VARSDEL, "Delete", 0, RELVERIFY, 200, 86, 65, 15, FALSE, m)
X)
X    {
X        var[0] = val[0] = '\0';
X        SetReqGadgets(req, gl);
X        DoRequest(req, g, vars_handler);
X        /* vars_handler called for every gadget event */
X    }
X    Free(m);
X}
X
X/* Create a new graph */
Xstatic struct graph *add_graph(struct graph *from)
X{
X    struct graph *g = new_graph(from);
X
X    if (g) add_head(&graph_list, g);
X
X    return g;
X}
X
X/* Delete a graph, checking for save */
Xstatic void remove_graph(struct graph *g)
X{
X    if (!g->saved) save_file(g);
X    remove(g);
X    delete_graph(g);
X}
X
X/* Global initialisation */
Xstatic int init(void)
X{
X    if (GfxBase = (struct GfxBase *)OpenLibrary("graphics.library", 0))
X        if (DiskfontBase = (struct Library *)OpenLibrary("diskfont.library", 0)
X)
X            if (IntuitionBase = (struct IntuitionBase *)OpenLibrary("intuition.
Xlibrary", 33))
X                if (LayersBase = (struct LayersBase *)OpenLibrary("layers.libra
Xry", 33))
X                    if (ArpBase = (struct ArpBase *)OpenLibrary("arp.library",
X0))
X                    {
X                        new_list(&graph_list);
X                        if (init_user() && init_grph() && add_graph(NULL))
X                            if (init_expr())
X                            {
X                                init_context(&vars);
X                                set_context(&vars);
X                                return TRUE;
X                            }
X                            else alert(NULL, "Expression init failed", "No memo
Xry ?");
X                    }
X                    else alert(NULL, "arp.library required", NULL);
X                else alert(NULL, "layers.library V1.2 required", NULL);
X            else alert(NULL, "intuition.library V1.2 required", NULL);
X        else alert(NULL, "diskfont.library required", NULL);
X    else alert(NULL, "graphics.library required", NULL);
X
X    return FALSE;
X}
X
X/* Free ALL resources ! */
Xstatic void clean_up(void)
X{
X    struct graph *g, *next;
X
X    cleanup_expr();
X    cleanup_grph();
X    cleanup_user();
X
X    for (g = first(&graph_list); next = succ(g); g = next) remove_graph(g);
X
X    if (ArpBase) CloseLibrary((struct Library *)ArpBase);
X    if (LayersBase) CloseLibrary((struct Library *)LayersBase);
X    if (IntuitionBase) CloseLibrary((struct Library *)IntuitionBase);
X    if (DiskfontBase) CloseLibrary((struct Library *)DiskfontBase);
X    if (GfxBase) CloseLibrary((struct Library *)GfxBase);
X
X#ifdef DEBUG
X    TrackerExitReport();
X#endif
X}
X
Xvoid main(int argc, char **argv)
X{
X    struct cmd cmd;
X    int done = FALSE;
X
X    if (init())
X        do
X            switch ((cmd = next_command()).command)
X            {
X                case close:
X                    done = succ(succ(first(&graph_list))) == NULL; /* one graph
X left ? */
X                    if (done && !vars_saved) save_variables(cmd.g);
X                    remove_graph(cmd.g);
X                    break;
X                case _new_graph:
X                    add_graph(cmd.g);
X                    break;
X                case _load_graph:
X                    load_file(cmd.g);
X                    break;
X                case _save_graph:
X                    save_file(cmd.g);
X                    break;
X                case print_graph:
X                    prt_graph(cmd.g);
X                    break;
X                case iff_graph:
X                    iff_todisk(cmd.g);
X                    break;
X                case save_vars:
X                    save_variables(cmd.g);
X                    break;
X                case load_vars:
X                    load_variables(cmd.g);
X                    break;
X                case quit:
X                    if (!vars_saved) save_variables(cmd.g);
X                    done = TRUE;
X                    break;
X                case add_function:
X                    select_object(cmd.g, add_object(cmd.g, new_function(cmd.g))
X);
X                    break;
X                case add_label:
X                    add_object(cmd.g, (struct object *)new_label(cmd.g, cmd.dat
Xa.pt.x, cmd.data.pt.y));
X                    break;
X                case del_object:
X                    remove_object(cmd.g, cmd.data.o);
X                    break;
X                case limits:
X                    enter_limits(cmd.g);
X                    break;
X                case axes:
X                    enter_axes(cmd.g);
X                    break;
X                case zoom:
X                    zoom_in(cmd.g, cmd.data.zoom_in.x0, cmd.data.zoom_in.y0, cm
Xd.data.zoom_in.x1, cmd.data.zoom_in.y1);
X                    break;
X                case zoom_out:
X                    zoom_factor(cmd.g, cmd.data.zoom_out);
X                    break;
X                case center:
X                    center_graph(cmd.g, cmd.data.pt.x, cmd.data.pt.y);
X                    break;
X                case edit:
X                    {
X                        struct Region *ref;
X
X                        if (cmd.data.o->edit(cmd.data.o, &ref))
X                        {
X                            cmd.g->saved = FALSE;
X                            refresh_graph(cmd.g, TRUE, ref);
X                        }
X                    }
X                    break;
X                case improve:
X                    refresh_graph(cmd.g, TRUE, cmd.data.o->improve(cmd.data.o))
X;
X                    break;
X                case edit_vars:
X                    enter_vars(cmd.g);
X                    draw_all();
X                    break;
X            }
X        while (!done);
X
X    clean_up();
X}
X
SHAR_EOF
echo "extracting graph.doc"
sed 's/^X//' << \SHAR_EOF > graph.doc
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   mail:
X
X     David Gay
X     19 Chemin de la Source
X     1296 Coppet
X     Vaud
X     Switzerland
X
X     From September 89 to June 1990
X     Somewhere in Pittsburgh :-)
X
X   e-mail:
X     GAY_D@ELMA.EPFL.CH
X
X
X     From September 89 to June 1990
X     ???@???.cmu.edu
X
X   Thanks to:
X
X     Matt Dillon, for DME & DMouse
X
X     Checkpoint Technologies <ckp@grebyn.com> (whoever that is ...), for
X        finding a bug that I've been chasing for far too long.
X
X     Karl Lehenbauer for his tracking routines.
X
X     The ARP project, for its file requester (I didn't quite feel like
X     writing one ...).
X
X
X
X
X
X
X
X
X
X
X    CONTENTS
X
X
X    1) Introduction
X
X    2) Operation
X       a) Basic Concepts
X       b) User Interface
X       c) A Quick Overview
X       d) Example
X
X    3) Detailed description
X       a) Display
X       b) Expression Syntax
X       c) Menus
X
X    4) Appendices
X       a) Function "Improvement"
X       b) Known bugs, Possible enhancements
X
X
X
X
X
X    1) INTRODUCTION
X
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    All this is done through a fully Intuitionalised interface, with
X    multiple windows (for multiple graphs), requesters, etc. The whole
X    approach is WYSIWYG.
X
X    This program requires the arp.library (for the file requester), and
X    WB 1.3 (or WB 1.2 if you don't want to print anything).
X
X    This program should be fairly easy to use, I'm not sure how readable
X    this documentation is ! So, don't read it in isolation, experiment with
X    the program at the same time ...
X
X
X
X
X
X    2) OPERATION
X
X    a) Some basic concepts:
X
X    - One POINT = 1/72th of an inch.
X
X    - A GRAPH is composed of a SCALE (which selects which part of the plane
X      to view), optional X & Y AXES, and OBJECTS.
X
X    - The SCALE defines the minimum and maximum values for the X & Y axes,
X      as well as if these are linear or logarithmic (in which case the
X      minimum must be greater than 0). Finally you can give a display ratio
X      (ie the size/unit of the Y axis / size/unit of the X axis. With 1, a
X      line y = x will appear "correct", that is at 45 degrees).
X
X    - The (optional) drawing of AXES is specified through their position
X      on the other ax, the spacing between ticks, and the frequency with
X      which they should be numbered.
X      Note that the spacing between ticks is interpreted differently for
X      logarithmic axes. If x0 is the position of the Y ax on the (loga-
X      rithmic) X ax, and s is the tick spacing, you will get ticks at
X
X        x0, x0+s, x0+2s, x0+3s, ..., 10*x0, 10*x0+s, ...
X
X      For a linear ax, there is simply a tick every s, with a tick at x0.
X
X    - OBJECTS can (in this version) only be FUNCTIONS and LABELS (Planned
X      extensions include lines & rectangles).
X
X    - There are 4 types of FUNCTIONS:
X
X        y = f(x)
X        x = x(t), y = y(t)
X        r = r(theta)
X        r = r(t), theta = theta(t)
X
X      (the last two are in polar coordinates of course). A function
X      definition can contain VARIABLES (or if you prefer, constants :-)),
X      these are global.
X
X    - LABELS can be multi-line, in any font, size or colour (more on this
X      later).
X
X    - VARIABLES are defined globally to all graphs and functions, they
X      simply associate a name with an expression (which is evaluated when
X      the expression containing the variable is).
X
X    Positions are generally specified in planar coordinates, and sizes in
X    inches (so as to provide device-independent output).
X
X
X    b) User interface
X
X    The program is operated through a mix of menus, requesters, and mouse
X    clicking & dragging.
X
X    When a menu option contains the characters "..." it means that there is
X    a requester associated with this command, a >> indicates a submenu.
X
X    Most requesters contain OK and CANCEL gadgets, which act as expected.
X    You can use Left-Amiga V as a shortcut for clicking on OK, Left-Amiga B
X    for CANCEL, at *any* time. When entering text in a string gadget,
X    pressing RETURN will activate the next one, greatly simplyfing data
X    entry.
X
X    Leaving a blank when a number is expected means to use the default
X    value, or not to display whatever is associated with this (if this
X    makes any sense).
X
X    The two gadgets in the right hand border select the mode of operation
X    (see below).
X
X    While a requester is present in one window, all operations are disabled
X    in other windows.
X
X
X    c) A Quick Overview
X
X    A SCALE must be set before you can do most operations, this is done by
X    selecting the Scale menu option.
X
X    Two modes exist, selected by the gadgets in the window:
X
X      - Selection (Arrow gadget) : This mode allows you to select with the
X        mouse, and drag if applicable, objects on the graph.
X
X      - Position (Crosshairs gadget) : You define a point (by clicking), or
X        a rectangle (by clicking and dragging) for further operations.
X
X    You can add functions to the graph at any time (even before the scale
X    is set actually), to add a label you must have chosen the point where
X    it should appear. For a function, you must select a name for it and the
X    type you desire, you'll define the actual expression afterwards. For
X    text, you simply select the Add Label menu, and type the desired text.
X
X    Before doing anything with an object, you must first select it. This
X    can be done in several ways:
X
X      - By clicking on the object in select mode.
X
X      - Functions, which may not always be displayed, can be selected by
X        name (Select Function, in the Edit menu).
X
X      - A function is selected after it has been added(created).
X
X    (there is also a deselect operation).
X
X    The selected object can now be edited, deleted or improved (see
X    detailed description).
X
X    Edition for a function means defining the expression used, and the
X    boundaries for the parameter (eg x=3*cos(t), y=5*sin(t), with
X    0<=t<=6.3), for a label you can select the font, the colour and the
X    size (given in points) (see detailed description for the problems
X    involved here), change the text that you initially entered, its
X    position, its justification with respect to this. Also, you make the
X    text appear at an offset in inches from the given coordinates, for
X    example to make text appear at a constant distance above the axis
X    irrespective of the size of the output sheet.
X
X    The expressions for functions can contain VARIABLES (as noted above).
X    These are entered through the Edit Variables requester which works as
X    follows:
X
X        i) Enter the variable to be added/edited/deleted in the Variables
X           gadget and PRESS RETURN (if you don't do this, it won't work),
X           or select the variable in the list displayed.
X
X        ii) If the variable already exists, its value is displayed in the
X            Value gadget, which is activated.
X
X        iii) You can delete the variable by clicking on the delete gadget,
X
X        iv) Or, edit/add the value (which can be any expression, just avoid
X            circular definitions!). When this is done, PRESS RETURN
X            (otherwise the new value will be ignored). Syntax errors will
X            be indicated by a screen flash.
X
X    Don't forget that these variables are global, and shared by all graphs.
X    As such, they are saved/loaded separately to/from disk (with the
X    Load/Save Variables command).
X
X
X    AXES are added with the Axes menu option.
X
X
X    You can easily change the part of the plane you are viewing with the
X    Zoom/Zoom Out/Center options:
X
X      - Zoom: You first select a rectangle (in position mode), then choose
X        this option. The graph will be redrawn zoomed in this rectangle.
X
X      - Zoom Out: The plane area covered is made 4 times bigger (this isn't
X        really true if one or both axes is logarithmic).
X
X      - Center: The point selected becomes the new graph centre, the limits
X        are adjusted accordingly.
X
X
X    Graphs can be saved/loaded(this creates a new graph), and new graphs
X    created.
X
X    Finally, you can preserve your efforts for posterity with the Output
X    Graph submenu, either to your favourite printer or as an ILBM file. In
X    the latter case, you must specify the resolution you desire, as well as
X    the number of pixels per inch (to allow the sizes you specify in inches
X    to be calculated). You might specify 2400 by 3300, at 300 dpi if you
X    later plan to send the output to a laser printer (sizes of up to 32767
X    x 32767 should be possible). Note that with this option, the graph will
X    be full page, the Ratio specified in the SCALE is ignored. This isn't
X    necessarily the case with printer output, where the indications given
X    in.Preferences will be followed as far as makes sense.
X
X    You must then decide whether to have the functions drawn in a "thin"
X    pen (one pixel wide), or with a width of x points where you choose x.
X    Finally, your masterpiece will be printed/saved. As this can be a time
X    consuming process, you are given the possibility to abort it.
X
X
X    d) Example
X
X    We want to create a graph of the functions y = x, and y = e^(-x*x),
X    label it, and print the result. We proceed as follows:
X
X      i) run graph ; this helps :-)
X
X      ii) First we select a scale, x from -3 to 3, y from -0.1 to 1 is
X          reasonable, so:
X          Select menu option Scale (shortcut Amiga-L), and enter these
X          values for X: Min & Max, Y: Min & Max. One could also set the
X          ratio to 2. Click on OK, or press Right-Amiga V.
X
X      iii) We want the X & Y axes intersecting at (0,0) of course, you can
X           place ticks and numbering as you desire (eg a tick every 0.5 on
X           X, with values every two ticks). Press Amiga-X, and enter the
X           chosen values.
X
X      iv) It's now time to add the two functions, first y = x:
X          Press Amiga-F, and enter name "x" for the function, click on OK
X          (the default type, f(x), is the one we want). The function has
X          now been created, but we must still define it: Select Edit in the
X          Edit menu, this will edit the currently selected object (ie the
X          function we just created). If you've done something inbetween,
X          you can reselect the function in the Select Function option
X          (under the Edit menu), and then Edit. Define the function as
X          being f(x)=x, you can leave the "from" and "to" gadgets blank,
X          the limits of the x axis will be used.
X
X          y = exp(-x*x) : Add Function (giving it name exp), Edit it, and
X          define it as f(x)=exp(-sqr(x)) (don't forget that you must enter
X          the name of the independent variable, ie x).
X
X          The two functions should have been drawn as soon as you finished
X          editing them, if this wasn't the case, you probably made an error
X          (of which you should have been informed).
X
X      v) The graph is now complete, but you might want to label it a bit:
X         Add an "X" next to the X axis, a "Y" next to the Y axis, and a
X         title to the whole thing. Check that you are in "position" mode
X         before proceeding.
X
X         Labels on the axes: Click *on* the ax which you want to label, and
X         select Add Label, and type the text desired (ie "X" or "Y"). The
X         label will be drawn, rather unesthetically on the ax. Now pass
X         into "select" mode, and click on your label, it should be high-
X         lighted. Select Edit, and change the Y and dY fields (for the X
X         axis label): Set Y to 0, and dY to 0.1. This means that the label
X         will be drawn 0.1 inches *below* the Y ax. You could also change
X         the font, the font size (in points), etc
X
X         Title: In "position" mode, click wherever you want the title,
X         select Add Label and type the title. If you type a \, a line break
X         will be added at that place (two \\ are replaced by a single \),
X         so "Intersection of\two functions" would take two lines. As with
X         the labels on the axes, you can edit this label for font size and
X         so on.
X
X      vi) It is now time to save & print the graph. Save Graph will
X          accomplish the former, Output Graph / To Printer the latter. You
X          will be asked to select the line thickness, then printing will
X          start. You can press the Stop! gadget at any time if you get
X          bored (or want to abort the printing).
X
X      vii) You may want to change one of the functions, for example replace
X           y = x by y = a*x. Try the following:
X
X           - Select the function, either by clicking on it (in "select"
X             mode), or with Select Function in the Edit menu.
X
X           - Edit it, replacing f(x)=x by f(x)=a*x
X
X           - Select OK
X
X           The function will not be visible any more, because a has no
X           value.
X
X      viii) So, we must give "a" a value. Select Edit Variables (Amiga-V),
X            enter a in the Variables gadget, PRESS RETURN (!), enter the
X            value 0.5, PRESS RETURN (!!!). "a" should appear in the
X            variable list above. Select OK, and the line should be redrawn.
X
X      ix) That's it! (for now). You can close this graph with the close
X          gadget, or quit with the Quit option. You will be asked if you
X          want to save anything that has changed.
X
X
X
X
X
X
X
X    3) DETAILED DESCRIPTION
X
X    This section is more a reference than a tutorial, but should be read
X    carefully (all the details are here).
X
X    a) Display (ie the window)
X
X    The display is composed of a graph title, axes, and objects. It is a
X    view on a portion of the plane defined by the SCALE.
X
X    The graph title displays the name (for functions) of the currently
X    selected object, and the position on the plane where you
X
X      - currently are (while dragging something around)
X      - released the mouse button otherwise
X
X    (in this context, the rectangle/cross-hairs of "position" mode is
X    considered to be an object).
X
X    The axes are simply displayed as requested.
X
X    Objects are of two types:
X
X      - functions, which have a name, and are not always displayed.
X      - other objects (currently labels only) which are *always* visible.
X        If they are positioned outside the visible portion of the plane,
X        they are moved into it.
X
X    They can be selected by clicking on them (or near them for functions),
X    the currently selected object is highlighted and is the one on which
X    all object commands operate. If the operation is meaningful, they can
X    be dragged or resized with the mouse.
X
X    b) Expression syntax
X
X    The following is an informal description of the syntax of expressions,
X    the actual priorities of the operators are the usual arithmetic ones
X    (with unary minus higher than power), they all group left to right.
X    | indicates alternatives, [] optional parts.
X
X        expression := expression '+' expression |
X                      expression '-' expression |
X                      expression '*' expression |
X                      expression '/' expression |
X                      expression '^' expression |
X                      '+' expression |
X                      '-' expression |
X                      '(' expression ')' |
X                      number |
X                      function '(' expression ')' |
X                      variable
X
X        number := mantissa [exponent]
X
X        mantissa := integer [ '.' integer ]
X
X        exponent := 'E' [sign] integer
X
X        sign := '+' | '-'
X
X        function := <see Appendix B>
X
X        variable := <any name>
X
X
X    You will notice (?) that numbers do not allow a leading sign, negative
X    numbers are actually entered as an expression of the form -expression.
X
X    The following functions are available:
X
X        name        description
X        ----        -----------
X        sin         sine
X        cos         cosine
X        tan         tangent
X        asin        Arc sine
X        acos        Arc cosine
X        atan        Arc tangent
X        sinh        hyperbolic sine
X        cosh        hyperbolic cosine
X        tanh        hyperbolic tangent
X        asinh       inverse hyperbolic sine
X        acosh       inverse hyperbolic cosine
X        atanh       inverse hyperbolic tangent
X        exp         e^x
X        exp10       10^x
X        abs         absolute value - not differentiable
X        log         base e logarithm
X        log10       base 10 logarithm
X        sqrt        square root
X        sqr         x^2
X        gamma       gamma function - not differentiable
X
X
X    And these are the possible errors:
X
X      - the syntax of an expression was incorrect
X      - no memory for requested operation
X      - parenthesises are not matched
X      - left parenthesis expected (after a function)
X      - you have asked for a recursive evaluation ...
X      - the result isn't a number (quick_eval)
X
X    c) Menus
X
X      This is detailed description of what the various menu options do.
X
X      i) Project
X
X         New Graph: This creates a new window in which you can enter
X         another graph. All windows are independent, but variables are
X         shared. Note also that while a requester is up in one window, you
X         can't use the others.
X
X         Delete Graph: The current window is closed, this is equivalent to
X         the close window gadget. If this is the last window, the program
X         quits (see Quit). If the graph hasn't been saved, you are given a
X         last opportunity to do so (see Save Graph).
X
X         Load Graph: You choose a file to load (with the ARP file reques-
X         ter), if the file is really a saved graph it is loaded, and a new
X         graph created from it.
X
X         Save Graph: The graph in this window is saved to disk (you choose
X         a file with the ARP requester, again).
X
X         Output Graph: This is a submenu, for producing "hard" output
X         either to disk or printer. Sizes in inches are preserved (as far
X         as possible, see individual object descriptions under Add), but
X         the SCALE is adapted to the area available.
X
X         You must always choose a line thickness, either thin (1 pixel
X         wide), or in points (A requester appears just before output). You
X         can abort at any point by clicking on the Stop! gadget.
X
X         All the output is in black and white, this should be changed.
X
X            To Printer: The graph is sent to the printer, following the
X            indications in preferences as follows:
X
X            - Aspect is always horizontal.
X            - Scaling is always Fraction.
X            - The graph is printed at the chosen density (I hope you like
X              the output!).
X            - The Limits are interpreted as follows:
X              Multiply is equivalent to Ignore
X              Ignore is equivalent to Bounded, with the size limited to one
X              page (the size of one page is determined by the Page Length
X              and the Spacing).
X              In Bounded if no ratio is given the full size is used, other-
X              wise the size is determined to give the correct ratio.
X              In Absolute or Pixels, the given size is used. If one limit
X              is unspecified (ie width or height = 0) the ratio is used to
X              adjust it.
X
X            To Disk: A requester appears asking for the file name (you can
X            click on the Disk gadget to use the file requester), you must
X            give the size of the bitmap you want to produce, as well as its
X            resolution in dots per inch (to allow the size calculations to
X            be done). Once all this has been done, the graph is written as
X            an ILBM IFF file.
X
X
X         Load Variables: The variables are loaded from the specified file,
X         and are added to (or replace) the current ones.
X
X         Save Variables: All the existing variables are saved to a file.
X
X         Quit: All the graphs are closed (see Delete Graph). If the
X         variables have not been saved, Save Variables is called.
X
X
X      ii) Graph
X
X          Scale: This brings up a requester allowing you to set/change the
X          SCALE. If you leave the ratio blank, none will be used (ie the
X          graph will fill the wholde window/page).
X
X          Axes: Here you choose the options to display the AXES. If the
X          position of an ax is left blank none is displayed, if the tick
X          interval is blank no ticks are drawn, and if no frequency is
X          given no numbering is done.
X
X          Zoom: After defining a rectangle in "position" mode you can zoom
X          in to it with this option.
X
X          Zoom Out: The plane portion viewed is made "twice" as big,
X          keeping the same centre. The old visible portion now occupies
X          the centre of the new visible portion, and is half as wide (on
X          screen). If an ax is linear this is the expected effect: if
X          before we had -1<x<3, we get -3<x<5 (same centre (1), interval
X          twice as wide). However a logarithmic ax, 0.1<x<10 will become
X          0.01<x<100.
X
X          Centre: The point selected becomes the new graph centre (but the
X          graph stays the same "size"). Expect strange (or normal?)
X          behaviour similar to that described in Zoom Out.
X
X
X      iii) Add
X
X           Function: A new function is created, you must give it a unique
X           name, and select its type (which can not be changed afterwards).
X           Functions can not be dragged, they can be selected by clicking
X           on them when they are visible. The function is selected once you
X           have added it so that you can immediately define (edit) it.
X
X           Label: A new label is created at the current point (which must
X           exist!), you enter the text in a requester. A \ signals a new
X           line, except if it is followd by a second one in which case a
X           single \ is displayed. The label is created in the default font
X           and size (topaz, 10 points).
X
X           Labels are always visible, where they to be outside the shown
X           part of the plane they are moved just inside. They can be
X           selected and dragged around with the mouse.
X
X           The size of a font is specified in POINTS so that it appears the
X           same on all output devices (ever seen Topaz 8 at 240 dpi?).
X           However as the standard Amiga fonts are used, and these are not
X           scalable, the nearest available size is used. This can easily
X           cause problems if you ask for too big a font, which might exist
X           in the correct size to appear correctly on screen, but not on
X           paper. Beware! (You can work out the size needed from the reso-
X           lution of your printer, a 10 point font on a 300 dpi requires a
X           300*(10/72) = 42 pixel font on the Amiga). Also, the horizontal
X           size may vary a bit on different output devices ...
X
X
X      iv) Edit
X
X          Variables: The edit variables requester is brought up. It works
X          as follows:
X            - You select a variable by clicking in the list of existing
X              ones, or by typing its name in the Variables gadget and
X              PRESSING RETURN.
X            - Its value (if any) is then displayed. You can delete this
X              variable by clicking on the Delete gadget, or change its
X              value in the Value gadget. To store this value, you MUST
X              PRESS RETURN.
X            - When you are done, select OK.
X          Don't forget that the variables are global to all graphs.
X
X          Select Function: A list of all variables is presented, you can
X          choose one of them which then becomes the selected object (This
X          is most useful when a function is not displayed).
X
X          Deselect: Deselects the current selection.
X
X          Edit: Edits the currently selected object. This is of course
X          different for every type of object ...
X
X            Labels: You can change the text (max 255 characters), change
X            the font, font size and text colour. The position can be
X            entered exactly, and an offset in inches (dX & dY) can be
X            added. This allows for placement at a constant distance from
X            something else (object, ax) whatever the output device. Also
X            you can specify the justification of the rectangle containing
X            the text with respect to its position (by default, the chosen
X            point is in its top left corner).
X
X            Functions: For all types, you must define the independent
X            variable (eg x, t, but could be anything) for this function,
X            followed by the actual expressions defining the function (see
X            Expression Syntax). You must then give the bounds for the
X            independent variable, these are obligatory except for functions
X            of type f(x) where the bounds of the graph are used when none
X            are given here (Note that this implies that the points for the
X            function are recalculated every time the bounds for the graph
X            are changed, which is not the case when they are given expli-
X            citly). The numbers of points you wish to be calculated must
X            also be given (default: 100), the more the better the function
X            will look but the longer it will take to calculate & display
X            them. The colour can be selected, for the meaning of the Show
X            discontinuities and Allow flat discontinuities options see
X            Appendix on Improve. If the data given is valid, the points
X            will be calculated and the function drawn.
X
X            Improve: This command tries to improve the look of an object.
X            Currently this is only done for functions: extra points are
X            added if the program thinks this will make the function look
X            better. This requires some discussion ... (see Appendix). Note
X            that this improvement is temporary: If a graph is saved and
X            reloaded, if the function is changed (ie anything that necessi-
X            tates a recalculation of the points composing a function) will
X            discard the improvements made (Note that changing the bounds of
X            a graph will require the recalculation of functions of type
X            f(x) with unspecified bounds, as seen above).
X
X            Delete: The selected object is deleted, the graph redrawn.
X
X
X
X
X
X
X    4) APPENDICES
X
X       a) Function "Improvement"
X
X       When you draw a function like y = 1/x or y = sin(1/x) you encounter
X       various problems: discontinuities, lack of points to draw the func-
X       tion correctly. This function attemps to solve them by adding an
X       extra points between two points that are judged too far "apart". If
X       this process fails to solve the problem after a given number of
X       iterations (4) the program decides that there is a discontinuity
X       between the two points. This may well be incorrect (especially if
X       far too few points were initially calculated), so you can run
X       Improve repeatedly. This has the disadvantage that it may add a lot
X       of points.
X
X       This is were the Show/Allow flat discontinuities options intervene. If
X       the Show disc. option is not selected, a segment will be drawn between
X       two consecutive even if the above procedure thinks that there is a
X       discontinuity. The Allow flat disc. option proved emprically necessary,
X       it indicates that flat segments (ie quasi horizontal or vertical) should
X     
X       be ignored in the search for discontinuities (otherwise one got severe
X       problems with functions like sin(x) around pi/2).
X
X       How does one judge if two points are two far "apart" you wonder ?
X       Given two points x0, x1 (in the f(x) case), the value of f(x1) is
X       estimated by e = f(x0) + (x1 - x0) * f'(x0) (First order Taylor
X       approximation). If e - f(x1) is too big (if |e - f(x1)| > 0.2 *
X       |f(x1) - f(x0)|) an extra point is added. This of course requires
X       f'(x), which can easily be obtained from f(x). If that function
X       wasn't differentiable, you'll get an error message when you try to
X       use the Improve command. For other types of functions, the approach
X       is very similar.
X
X       Finally, note that the above can be a time (and memory) consuming
X       process, so you can abort it at any time by clicking on the Stop!
X       gadget.
X
X       Comments on the above from numerical analysts are not welcome :-)
X
X
X       b) Known bugs, Possible enhancements
X
X        - The program was designed to recover from lack of memory (it makes
X          many, many allocations) but this feature has not been tested and
X          I'm pretty sure doesn't quite work.
X
X        - The very many and small memory allocations made could easily
X          fragment memory extensively. Something should be done about this
X          (an allocation is done for every point of every function !).
X
X        - More object types (lines, rectangles, circles, splines) could be
X          added.
X
X        - Add AREXX interface (?)
X
X        - Postscript output (I'll have to learn postscript first!)
X
X        - Make variables local to graphs ?
X
X        - Allow user to choose simple or smart refresh windows.
X
X        - Colour printing.
X
X        Very ambitious additions:
X
X          - Add scalable fonts (Very big :-))
X
X          - Add additional types of graphs, eg 3d, sampled (as in
X            histograms, that is with a non continous axis).
X
X        - ...
X
SHAR_EOF
echo "extracting graph.h"
sed 's/^X//' << \SHAR_EOF > graph.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/* Main include file, global types, etc */
X#ifndef GRAPH_H
X#define GRAPH_H
X
X#include "list.h"
X#include "user/eval.h"
X
X#define NOVAL 1.2345e308 /* This means no value (in xmin, xmax... style fields)
X !!! */
X#define INOVAL -32768 /* idem, but for integer fields */
X/* Lemgths of various strings */
X#define VARLEN 10
X#define EXPRLEN 80
X#define FILELEN 256
X
Xextern list graph_list;              /* List of all graphs */
Xextern context vars;                 /* List of all variables */
Xextern const char *eval_messages[];  /* Text of eval error messages. What's it
Xdoing here ??? */
X
X#endif
X
SHAR_EOF
echo "extracting graphics.c"
sed 's/^X//' << \SHAR_EOF > graphics.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/* Various graphic extensions */
X#include <exec/types.h>
X#include <graphics/rastport.h>
X#include <math.h>
X#include <string.h>
X
X#include "graphics.h"
X
X#include <proto/graphics.h>
X
X/* Size limit for std routines */
X#define MAXPIXELS 1007
X
X/* Draws very long lines */
Xvoid BigDraw(struct RastPort *rp, long x1, long y1)
X{
X    short x0 = rp->cp_x;
X    short y0 = rp->cp_y;
X    short dx = x1 - x0;
X    short dy = y1 - y0;
X
X    if (rp->PenWidth > 1 || rp->PenHeight > 1)
X        ThickDraw(rp, x1, y1);
X    else
X    {
X        if (dx < 0) dx = -dx;
X        if (dy < 0) dy = -dy;
X
X        /* Use std routine if possible. It's 10 (or is it 100) x faster ! */
X        if (dx <= MAXPIXELS && dy <= MAXPIXELS ||
X            rp->BitMap->BytesPerRow <= (MAXPIXELS >> 3) && rp->BitMap->Rows <=
XMAXPIXELS)
X            Draw(rp, x1, y1);
X
X        else if (dx == 0) /* Vertical line */
X        {
X            if (y0 < y1)
X                do Draw(rp, x1, y0 = min(y0 + MAXPIXELS, y1)); while (y0 != y1)
X;
X            else
X                do Draw(rp, x1, y0 = max(y0 - MAXPIXELS, y1)); while (y0 != y1)
X;
X        }
X        else if (dy == 0) /* Horizontal line */
X        {
X            if (x0 < x1)
X                do Draw(rp, x0 = min(x0 + MAXPIXELS, x1), y1); while (x0 != x1)
X;
X            else
X                do Draw(rp, x0 = max(x0 - MAXPIXELS, x1), y1); while (x0 != x1)
X;
X        }
X        else /* "Standard" line drawing routine, with shifts. Could be recoded
X                in assembly, but as most time is spent in WritePixel, this
X                wouldn't help much. Probably faster to work out where to
X                break the line to be able to use the std draw routine (even
X                using real arithmetic). However, is rarely used, so ... */
X        {
X            register short x, y;
X            register short a;
X            register short add1;
X            register short upadd;
X            short end, oend, inc;
X
X            if (dx > dy) /* --> 1 pixel for each x */
X            {
X                /* We want to start at the lowest value of x */
X                if (x0 > x1)
X                {
X                    x = x1;
X                    y = y1;
X                    end = x0;
X                    oend = y0;
X                }
X                else
X                {
X                    x = x0;
X                    y = y0;
X                    end = x1;
X                    oend = y1;
X                }
X
X                inc =  (oend < y) ? -1 : 1; /* y direction */
X                a = 2 * dy - dx;            /* initial "error" */
X                add1 = 2 * dy;              /* Standard increment */
X                upadd = 2 * dy - 2 * dx;    /* Pixel increment */
X
X                while (x <= end)
X                {
X                    WritePixel(rp, x, y);
X
X                    if (a > 0) /* A y shift ! */
X                    {
X                        a += upadd;
X                        y += inc;
X                    }
X                    else
X                        a += add1;
X
X                    x += 1;
X                }
X            }
X            else /* 1 pixel for each y */
X            {
X                if (y0 > y1)
X                {
X                    y = y1;
X                    x = x1;
X                    end = y0;
X                    oend = x0;
X                }
X                else
X                {
X                    y = y0;
X                    x = x0;
X                    end = y1;
X                    oend = x1;
X                }
X
X                inc =  (oend < x) ? -1 : 1;
X                a = 2 * dx - dy;
X                add1 = 2 * dx;
X                upadd = 2 * dx - 2 * dy;
X
X                while (y <= end)
X                {
X                    WritePixel(rp, x, y);
X
X                    if (a > 0)
X                    {
X                        a += upadd;
X                        x += inc;
X                    }
X                    else
X                        a += add1;
X
X                    y += 1;
X                }
X            }
X            rp->cp_x = x1;
X            rp->cp_y = y1;
X        }
X    }
X}
X
X/* Only for RastPort's with no clipping ! */
Xvoid BigSetRast(struct RastPort *rp, long colour)
X{
X    struct BitMap *bm = rp->BitMap;
X    int i;
X
X    for (i = 0; i < bm->Depth; i++, colour = colour >> 1)
X        memset(bm->Planes[i], colour & 1 ? 255 : 0, bm->BytesPerRow * bm->Rows)
X;
X}
X
X/* Determine real text extent, if written in font font */
Xvoid TextExtent(char *text, struct TextFont *font, struct TextExtent *ext)
X{
X    static struct IntuiText it = {
X        1, 0, JAM1, 0, 0
X    };
X    struct TextAttr ta;
X
X    ta.ta_Name = font->tf_Message.mn_Node.ln_Name;
X    ta.ta_YSize = font->tf_YSize;
X    ta.ta_Style = font->tf_Style;
X    ta.ta_Flags = font->tf_Flags;
X
X    it.ITextFont = &ta;
X    it.IText = text;
X
X    ext->te_Extent.MaxX = ext->te_Width = IntuiTextLength(&it) - 1;
X    ext->te_Height = font->tf_YSize;
X    ext->te_Extent.MinY = - font->tf_Baseline;
X    ext->te_Extent.MaxY = font->tf_YSize - font->tf_Baseline - 1;
X    /* The tricky part: in a proportional font, with kerning, a letter may
X       start gto the left of the current position. */
X    ext->te_Extent.MinX =
X       (font->tf_CharKern && (UBYTE)(text[0]) >= font->tf_LoChar && (UBYTE)(tex
Xt[0]) <= font->tf_HiChar)
X           ? ((WORD *)(font->tf_CharKern))[text[0] - font->tf_LoChar]
X           : 0;
X}
X
X/* Assumes w,h not too big ( < MAXPIXELS ) ..., w and h odd */
Xvoid ThickDraw(struct RastPort *rp, long _x1, long _y1)
X{
X    short x0 = rp->cp_x;
X    short y0 = rp->cp_y;
X    short x1 = _x1;
X    short y1 = _y1;
X    short dx = x1 - x0;
X    short dy = y1 - y0;
X    short w = rp->PenWidth;
X    short h = rp->PenHeight;
X
X    if (dx < 0) dx = -dx;
X    if (dy < 0) dy = -dy;
X
X    if (dx == 0) /* Vertical line -> easy */
X    {
X        short x00 = x0 - w / 2;
X        short x01 = x0 + w / 2;;
X
X        if (y0 < y1)
X        {
X            y0 -= h / 2;
X            y1 += h / 2;
X            do {
X                short ny = min(y0 + MAXPIXELS, y1);
X                RectFill(rp, x00, y0, x01, ny);
X                y0 = ny;
X            } while (y0 != y1);
X        }
X        else
X        {
X            y1 -= h / 2;
X            y0 += h / 2;
X            do {
X                short ny = max(y0 - MAXPIXELS, y1);
X                RectFill(rp, x00, ny, x01, y0);
X                y0 = ny;
X            } while (y0 != y1);
X        }
X    }
X    else if (dy == 0) /* Horizontal line */
X    {
X        short y00 = y0 - h / 2;
X        short y01 = y0 + h / 2;
X
X        if (x0 < x1)
X        {
X            x0 -= w / 2;
X            x1 += w / 2;
X            do {
X                short nx = min(x0 + MAXPIXELS, x1);
X                RectFill(rp, x0, y00, nx, y01);
X                x0 = nx;
X            } while (x0 != x1);
X        }
X        else
X        {
X            x0 += w / 2;
X            x1 -= w / 2;
X            do {
X                short nx = max(x0 - MAXPIXELS, x1);
X                RectFill(rp, nx, y00, x0, y01);
X                x0 = nx;
X            } while (x0 != x1);
X        }
X    }
X    else /* Same algorithme as in BigDraw, the thickness is done by drawing
X            horiz. (or vert.) lines for each value of y (x) */
X    {
X        register short x, y;
X        register short a;
X        register short add1;
X        register short upadd;
X        short end, oend, inc;
X
X        if (dx > dy) /* 1 pixel for each x */
X        {
X            short barh, bary0, bary1, sx;
X
X            barh = (w * dy);
X            barh = barh / dx + h;
X
X            if (x0 > x1)
X            {
X                x = x1;
X                y = y1;
X                end = x0;
X                oend = y0;
X            }
X            else
X            {
X                x = x0;
X                y = y0;
X                end = x1;
X                oend = y1;
X            }
X
X            inc =  (oend < y) ? -1 : 1;
X            a = 2 * dy - dx;
X            add1 = 2 * dy;
X            upadd = 2 * dy - 2 * dx;
X
X            x -= w / 2;
X            end += w / 2;
X            if (inc > 0)
X            {
X                bary0 = y - h / 2;
X                bary1 = oend + h / 2;
X                y += h / 2 - barh + 1;
X                oend -= h / 2;
X            }
X            else
X            {
X                bary0 = y + h / 2;
X                bary1 = oend - h / 2;
X                y -= h / 2;
X                oend += h / 2 - barh + 1;
X            }
X            sx = x;
X            while (x <= end)
X            {
X                short y00 = y, y01 = y + barh - 1;
X
X                if (inc < 0)
X                {
X                    if (x < sx + w) y01 = bary0;
X                    if (x > end - w) y00 = bary1;
X                }
X                else
X                {
X                    if (x < sx + w) y00 = bary0;
X                    if (x > end - w) y01 = bary1;
X                }
X                Move(rp, x, y00);
X                Draw(rp, x, y01);
X
X                if (a > 0)
X                {
X                    a += upadd;
X                    y += inc;
X                }
X                else
X                    a += add1;
X
X                x += 1;
X            }
X        }
X        else /* 1 pixel for each y */
X        {
X            short barw, barx0, barx1, sy;
X
X            barw = h * dx;
X            barw = w + barw / dy;
X
X            if (y0 > y1)
X            {
X                y = y1;
X                x = x1;
X                end = y0;
X                oend = x0;
X            }
X            else
X            {
X                y = y0;
X                x = x0;
X                end = y1;
X                oend = x1;
X            }
X
X            inc =  (oend < x) ? -1 : 1;
X            a = 2 * dx - dy;
X            add1 = 2 * dx;
X            upadd = 2 * dx - 2 * dy;
X
X            y -= h / 2;
X            end += h / 2;
X            if (inc > 0)
X            {
X                barx0 = x - w / 2;
X                barx1 = oend + w / 2;
X                x += w / 2 - barw + 1;
X                oend -= w / 2;
X            }
X            else
X            {
X                barx0 = x + w / 2;
X                barx1 = oend - w / 2;
X                x -= w / 2;
X                oend += w / 2 - barw + 1;
X            }
X            sy = y;
X            while (y <= end)
X            {
X                short x00 = x, x01 = x + barw - 1;
X
X                if (inc > 0)
X                {
X                    if (y < sy + h) x00 = barx0;
X                    if (y > end - h) x01 = barx1;
X                }
X                else
X                {
X                    if (y < sy + h) x01 = barx0;
X                    if (y > end - h) x00 = barx1;
X                }
X                Move(rp, x00, y);
X                Draw(rp, x01, y);
X
X                if (a > 0)
X                {
X                    a += upadd;
X                    x += inc;
X                }
X                else
X                    a += add1;
X
X                y += 1;
X            }
X        }
X    }
X    rp->cp_x = _x1;
X    rp->cp_y = _y1;
X}
X
SHAR_EOF
echo "End of archive 3 (of 7)"
# if you want to concatenate archives, remove anything after this line
exit