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