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