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