page%swap@Sun.COM (Bob Page) (11/13/89)
Submitted-by: dg3i+@andrew.cmu.edu (David Gay) Posting-number: Volume 89, Issue 207 Archive-name: applications/graph.4 # 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: # graphics.h # grph.c # This is archive 4 of a 7-part kit. # This archive created: Sun Nov 12 18:23:30 1989 echo "extracting graphics.h" sed 's/^X//' << \SHAR_EOF > graphics.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/* Various graphic extensions */ X#ifndef BIG_GRAPHICS_H X#define BIG_GRAPHICS_H X X/* Now, where did I get this idea ??? */ Xstruct TextExtent { X UWORD te_Width; X UWORD te_Height; X struct Rectangle te_Extent; X}; X X/* Draw a line longer than 1008 pixels */ Xvoid BigDraw(struct RastPort *rp, long x, long y); X/* Idem, but take account of PenWidth, PenHeight (must be odd) */ Xvoid ThickDraw(struct RastPort *rp, long x, long y); X/* Only works for rastport's with no clipping */ Xvoid BigSetRast(struct RastPort *rp, long colour); X/* Determine real extent of text(-> Rectangle), return it in TextExtent ext */ Xvoid TextExtent(char *text, struct TextFont *font, struct TextExtent *ext); X X#endif X SHAR_EOF echo "extracting grph.c" sed 's/^X//' << \SHAR_EOF > grph.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/* Graph manipulation */ X X#include <exec/types.h> X#include <graphics/gfxbase.h> X#include <graphics/rastport.h> X#include <intuition/intuition.h> X#include <devices/prtbase.h> X#include <devices/printer.h> X#include <stdio.h> X#include <string.h> X#include <math.h> X#include <limits.h> X#include <iff/iff.h> X#include <iff/ilbm.h> X X#include "grph.h" X#include "file.h" X#include "graphics.h" X#include "graph.h" X#include "uio.h" X#include "object.h" X#include "list.h" X#include "coords.h" X#include "user/gadgets.h" X#include "tracker.h" X X#include <proto/exec.h> X#include <proto/graphics.h> X#include <proto/diskfont.h> X#include <proto/intuition.h> X X#define INCH 2.54e-2 X#define DIGHEIGHT 8 /* Digit font size, in points (1 point = 1/72 inch) */ X#define XTICK 0.05 /* Size of tick for X axis, In inches */ X#define YTICK 0.05 X#define XTEXT 0.12 X#define YTEXT 0.09 X X#define MAXPRINTPAGES 1 /* Max length for printing */ X#define CHOOSELIST 2 /* for choose req */ X#define IFFDISK 2 /* For iff req */ X X/* A function to send a slice of output to the chosen device (disk/file) */ Xtypedef int (*prtfunc)(struct graph *g, struct RastPort *rp, int w, int h, int Xy, int slice, struct Requester *abreq); X Xextern struct GfxBase *GfxBase; X Xstatic UWORD scr_dpmx, scr_dpmy; /* The screen 'resolution' */ X X/* Data for printer output */ Xstatic struct IODRPReq *prtio; Xstatic struct MsgPort *prtport; Xstatic struct PrinterExtendedData *ped; Xstatic struct Preferences *prtprefs; Xstatic struct ColorMap *prtcm; X X/* Data for iff output */ Xstatic FILE *iff_file; Xstatic long form_pos, body_pos; Xstatic char iff_filename[FILELEN]; Xstatic struct Gadget *iffg; X Xstatic char *prt_error[] = { X "Unknown error", X "Print cancelled", X "Not a graphics printer !", X "Obsolete", X "Illegal print dimensions", X "Obsolete", X "No memory (internal)", X "No memory (for buffers)" X}; X X#define MAX_PRT_ERROR (sizeof(prt_error) / sizeof(char *) - 1) X X/* Check that ax can be displayed */ Xstatic int ax_ok(const struct ax *a, const struct ax *other) X{ X return (a->ax == NOVAL || X ((!other->log || a->ax > 0.0) && X (a->cstep == NOVAL || X (a->cstep > 0.0 && X (a->every == INOVAL || a->every > 0))))); X} X X/* Check that ax has legal values */ Xstatic int ax_ok2(const struct ax *a) X{ X return a->min != NOVAL && a->max != NOVAL && a->min < a->max && X (!a->log || a->min > 0.0); X} X X/* Check if graph is displayable */ Xstatic int graph_ok(struct graph *g) X{ X g->a.ok = ax_ok(&g->a.x, &g->a.y) && ax_ok(&g->a.y, &g->a.x) && X (g->a.ratio == NOVAL || g->a.ratio > 0.0); X X return ax_ok2(&g->a.x) && ax_ok2(&g->a.x); X} X X/* Redraw graph after changes */ Xstatic void check_graph(struct graph *g) X{ X g->saved = FALSE; X g->ok = graph_ok(g); X set_scale(g); X draw_graph(g, TRUE); X} X X/* Find object in graph by name */ Xstatic struct object *find_object(struct graph *g, char *name) X{ X struct object *o; X X for (o = first(&g->o_list); succ(o); o = succ(o)) X if (strcmp(o->name, name) == 0) return o; X X return NULL; X} X X/* Convert inches to rastport dots, using current dpm */ X/* Use integer arithmetic if needs to be called often */ Xint xinch2dots(struct graph *g, double x) X{ X return (int)(x * INCH * g->io.dpmx + 0.5); X} X Xint yinch2dots(struct graph *g, double y) X{ X return (int)(y * INCH * g->io.dpmy + 0.5); X} X X/* Open font in pts points */ Xstruct TextFont *open_font(struct graph *g, char *name, int pts, int style, int X flags) X{ X struct TextAttr ta; X struct TextFont *tf1, *tf2; X X ta.ta_Name = name; X ta.ta_YSize = yinch2dots(g, pts / 72.0); X ta.ta_Style = style; X ta.ta_Flags = flags; X X tf1 = OpenFont(&ta); X if (!tf1) X return OpenDiskFont(&ta); X else if (tf1->tf_YSize != ta.ta_YSize) X { X tf2 = OpenDiskFont(&ta); X X if (tf2) X { X CloseFont(tf1); X return tf2; X } X else X return tf2; X } X else X return tf1; X} X X/* add object to graph (object is already displayed) */ Xstruct object *add_object(struct graph *g, struct object *o) X{ X if (o) X { X if (o->name[0] != '\0' && find_object(g, o->name)) X { X message(g, "Name already used", (char *)NULL); X refresh_graph(g, TRUE, o->delete(o)); X return NULL; X } X add_head(&g->o_list, o); X g->saved = FALSE; X } X return o; X} X X/* Remove object & redisplay graph */ Xvoid remove_object(struct graph *g, struct object *o) X{ X if (o == g->s.current) X { X o->deselect(o); X g->s.current = NULL; X disable_rect_menus(g); X disable_object_menus(g); X } X remove(o); X g->saved = FALSE; X refresh_graph(g, TRUE, o->delete(o)); X} X X/* Make object o selected */ Xvoid select_object(struct graph *g, struct object *o) X{ X if (o) X { X g->s.current = o; X o->select(o); /* Inform object of this */ X enable_object_menus(g); X set_title(g); X } X} X X/* Deslect current object */ Xvoid deselect(struct graph *g) X{ X struct Region *ref; X X /* Deselect object */ X ref = g->s.current ? g->s.current->deselect(g->s.current) : NULL; X g->s.current = NULL; X X disable_rect_menus(g); X disable_object_menus(g); X refresh_graph(g, TRUE, ref); X} X X/* User pressed mouse button */ Xvoid mouse_down(struct graph *g, WORD sx, WORD sy) X{ X if (g->ok && g->io.rw) X { X /* Get real pos */ X g->s.x = g->io.rw->x(g->io.rw, sx); X g->s.y = g->io.rw->y(g->io.rw, sy); X /* If nothing selected, or if not clicking in selected object */ X if (!g->s.current || !g->s.current->down(g->s.current)) X { X deselect(g); X X if (g->s.select_mode) /* Try to select something */ X { X struct object *o; X X for (o = first(&g->o_list); succ(o); o = succ(o)) X if (o->down(o)) /* Inside object ? */ X { X select_object(g, o); X break; /* exit for loop */ X } X } X else /* Start a new rectangle */ X if (g->s.current = (struct object *)new_pos(g)) X enable_rect_menus(g); X X } X if (g->s.current) /* Something is now selected, keep track of mouse */ X { X /* Adjust position for offset in object */ X g->s.x = g->io.rw->x(g->io.rw, sx - g->s.current->mx); X g->s.y = g->io.rw->y(g->io.rw, sy - g->s.current->my); X ReportMouse(g->io.win, TRUE); X g->s.mouse = TRUE; X } X set_title(g); X } X} X X/* Mouse has moved */ Xvoid mouse_move(struct graph *g, WORD sx, WORD sy) X{ X if (g->s.mouse) X { X /* Adjust for offset, calc pos in coord system */ X sx -= g->s.current->mx; X sy -= g->s.current->my; X g->s.x = g->io.rw->x(g->io.rw, sx); X g->s.y = g->io.rw->y(g->io.rw, sy); X X /* Inform selection of movement */ X g->s.current->move(g->s.current); X X set_title(g); X } X} X X/* Mouse button released */ Xvoid mouse_up(struct graph *g, WORD sx, WORD sy) X{ X if (g->s.mouse) /* mouse was down */ X { X g->saved = FALSE; ./* Graph has very probably changed */ X g->s.mouse = FALSE; X X sx -= g->s.current->mx; X sy -= g->s.current->my; X g->s.x = g->io.rw->x(g->io.rw, sx); X g->s.y = g->io.rw->y(g->io.rw, sy); X X ReportMouse(g->io.win, FALSE); X /* Redaw whatever is necessary */ X refresh_graph(g, TRUE, g->s.current->up(g->s.current)); X } X} X X/* Handler for object selection requester */ Xstatic int choose_handler(struct Gadget *gg, ULONG class, struct Requester *req X, struct graph *g) X{ X if (gg->GadgetID == CHOOSELIST) /* In a list */ X { X if (ModifyList(gg, req, req->RWindow, class == GADGETUP) == 2) X { X EndRequest(req, req->RWindow); X return TRUE; X } X } X else return std_ghandler(gg, class, req, g); X} X X/* Ask user to choose an object, by name */ Xstruct object *choose_object(struct graph *g, char *op) X{ X struct object *o, *sel = NULL; X char name[FNAMELEN]; X tlist l; X char what[30]; X int ok; X X /* Construct title */ X what[29] = '\0'; name[0] = '\0'; X strncpy(what, op, 29); X strncat(what, " function", 29 - strlen(what)); X X /* Construct list of named objects */ X new_list(&l); X ok = TRUE; X for (o = first(&g->o_list); succ(o); o = succ(o)) X if (o->name[0] != '\0') X { X tnode *n = alloc_node(sizeof(tnode)); X X if (!n) X { X ok = FALSE; X break; X } X n->ln_Name = o->name; X add_tail(&l, n); X } X X if (ok) /* list constructed ok */ X { X /* Create requester */ X struct Requester *req; X struct Memory *m; X struct Gadget *gl = NULL; X X if ((m = NewMemory()) && X (req = InitReq(50, 20, 200, 120, m)) && X SetReqBorder(req, 1, m) && X AddIntuiText(&req->ReqText, what, 100 - 4 * strlen(what), 6, m) && X AddList(&gl, CHOOSELIST, "Name", &l, name, FNAMELEN, 0, RELVERIFY | X ENDGADGET, 20, 20, 160, 80, TRUE, m) && X AddBox(&gl, TRUE, "Ok", 0, RELVERIFY | ENDGADGET, 18, 95, 65, 15, F XALSE, m) && X AddBox(&gl, FALSE, "Cancel", 0, RELVERIFY | ENDGADGET, 118, 95, 65, X 15, FALSE, m)) X { X SetReqGadgets(req, gl); X if (DoRequest(req, g, choose_handler)) X { X /* Remove blanks */ X strip(name); X if (*name) X { X sel = find_object(g, name); X if (!sel) message(g, "No such function", (char *)NULL); X } X } X } X Free(m); X } X free_list((list *)&l, sizeof(tnode)); X return sel; X} X X/* Define coordinate system */ Xvoid enter_limits(struct graph *g) X{ X struct Requester *req; X struct Memory *m; X struct Gadget *gl = NULL, *x_log, *y_log; X char xmin[NBLEN], xmax[NBLEN], ymin[NBLEN], ymax[NBLEN], ratio[NBLEN]; X X double2str(xmin, g->a.x.min); X double2str(xmax, g->a.x.max); X double2str(ymin, g->a.y.min); X double2str(ymax, g->a.y.max); X double2str(ratio, g->a.ratio); X X if ((m = NewMemory()) && X (req = InitReq(50, 20, 325, 105, m)) && X SetReqBorder(req, 1, m) && X AddIntuiText(&req->ReqText, "Limits", 138, 6, m) && X AddText(&gl, 0, "X: Min ", FALSE, xmin, NBLEN, TRUE, 0, RELVERIFY, 67, X20, 80, 10, TRUE, m) && X AddText(&gl, 0, "Max ", FALSE, xmax, NBLEN, TRUE, 0, RELVERIFY, 190, 20 X, 80, 10, TRUE, m) && X (x_log = AddOption(&gl, 0, "Log", FALSE, g->a.x.log * SELECTED, 0, 305, X 20, 10, 10, m)) && X AddText(&gl, 0, "Y: Min ", FALSE, ymin, NBLEN, TRUE, 0, RELVERIFY, 67, X40, 80, 10, TRUE, m) && X AddText(&gl, 0, "Max ", FALSE, ymax, NBLEN, TRUE, 0, RELVERIFY, 190, 40 X, 80, 10, TRUE, m) && X (y_log = AddOption(&gl, 0, "Log", FALSE, g->a.y.log * SELECTED, 0, 305, X 40, 10, 10, m)) && X AddText(&gl, 0, "Ratio (Y/X) ", FALSE, ratio, NBLEN, TRUE, 0, RELVERIFY X, 107, 60, 80, 10, TRUE, m) && X AddBox(&gl, TRUE, "Ok", 0, RELVERIFY | ENDGADGET, 49, 80, 65, 15, FALSE X, m) && X AddBox(&gl, FALSE, "Cancel", 0, RELVERIFY | ENDGADGET, 211, 80, 65, 15, X FALSE, m)) X { X SetReqGadgets(req, gl); X if (DoRequest(req, g, std_ghandler)) X { X g->a.x.min = str2double(xmin); X g->a.x.max = str2double(xmax); X g->a.x.log = (x_log->Flags & SELECTED) != 0; X g->a.y.min = str2double(ymin); X g->a.y.max = str2double(ymax); X g->a.y.log = (y_log->Flags & SELECTED) != 0; X g->a.ratio = str2double(ratio); X check_graph(g); X } X } X Free(m); X} X X/* Choose axes display options */ Xvoid enter_axes(struct graph *g) X{ X struct Requester *req; X struct Memory *m; X struct Gadget *gl = NULL; X char ax_x[NBLEN], ax_y[NBLEN], step_x[NBLEN], step_y[NBLEN], X every_x[INTLEN], every_y[INTLEN]; X X double2str(ax_x, g->a.x.ax); X double2str(step_x, g->a.x.cstep); X int2str(every_x, g->a.x.every); X double2str(ax_y, g->a.y.ax); X double2str(step_y, g->a.y.cstep); X int2str(every_y, g->a.y.every); X X if ((m = NewMemory()) && X (req = InitReq(50, 15, 225, 165, m)) && X SetReqBorder(req, 1, m) && X AddIntuiText(&req->ReqText, "Axes", 96, 6, m) && X AddText(&gl, 0, "X: Axe at ", FALSE, ax_x, NBLEN, TRUE, 0, RELVERIFY, 9 X1, 20, 80, 10, TRUE, m) && X AddText(&gl, 0, " Ticks every ", FALSE, step_x, NBLEN, TRUE, 0, RELVE XRIFY, 131, 40, 80, 10, TRUE, m) && X AddText(&gl, 0, " numbered every ", FALSE, every_x, INTLEN, TRUE, 0, XRELVERIFY, 155, 60, 32, 10, TRUE, m) && X AddText(&gl, 0, "Y: Axe at ", FALSE, ax_y, NBLEN, TRUE, 0, RELVERIFY, 9 X1, 80, 80, 10, TRUE, m) && X AddText(&gl, 0, " Ticks every ", FALSE, step_y, NBLEN, TRUE, 0, RELVE XRIFY, 131, 100, 80, 10, TRUE, m) && X AddText(&gl, 0, " numbered every ", FALSE, every_y, INTLEN, TRUE, 0, XRELVERIFY, 155, 120, 32, 10, TRUE, m) && X AddBox(&gl, TRUE, "Ok", 0, RELVERIFY | ENDGADGET, 24, 140, 65, 15, FALS XE, m) && X AddBox(&gl, FALSE, "Cancel", 0, RELVERIFY | ENDGADGET, 136, 140, 65, 15 X, FALSE, m)) X { X SetReqGadgets(req, gl); X if (DoRequest(req, g, std_ghandler)) X { X g->a.x.ax = str2double(ax_x); X g->a.x.cstep = str2double(step_x); X g->a.x.every = str2int(every_x); X g->a.y.ax = str2double(ax_y); X g->a.y.cstep = str2double(step_y); X g->a.y.every = str2int(every_y); X check_graph(g); X } X } X Free(m); X} X X/* Change mode */ Xvoid set_mode(struct graph *g, int newmode) X{ X if (newmode != g->s.select_mode) deselect(g); X g->s.select_mode = newmode; X} X X/* Recalc & display title */ X/* assumes GNAMELEN < TITLELEN */ Xvoid set_title(struct graph *g) X{ X strcpy(g->io.title, g->name); X if (!g->ok) strncat(g->io.title, "(Bad)", TITLELEN - 1 - strlen(g->io.title X)); X if (g->s.current) X if (g->s.current->name[0] != '\0') X { X strncat(g->io.title, ", function is ", TITLELEN - 1 - strlen(g->io. Xtitle)); X strncat(g->io.title, g->s.current->name, TITLELEN - 1 - strlen(g->i Xo.title)); X if (!g->s.current->ok) strncat(g->io.title, "(Bad)", TITLELEN - 1 - X strlen(g->io.title)); X } X else strncat(g->io.title, ", object selected", TITLELEN -1 - strlen(g-> Xio.title)); X else strncat(g->io.title, ", nothing selected", TITLELEN - 1 - strlen(g->io X.title)); X if (g->s.mouse) X { X char x[NBLEN], y[NBLEN]; X X double2str(x, g->s.x); double2str(y, g->s.y); X strncat(g->io.title, " x=", TITLELEN - 1 - strlen(g->io.title)); X strncat(g->io.title, x, TITLELEN - 1 - strlen(g->io.title)); X strncat(g->io.title, ", y=", TITLELEN - 1 - strlen(g->io.title)); X strncat(g->io.title, y, TITLELEN - 1 - strlen(g->io.title)); X } X X SetWindowTitles(g->io.win, g->io.title, "Graph"); X} X X/* Window has changed size, recreate coord system, taking into account the X desired ratio */ Xvoid set_scale(struct graph *g) X{ X /* Standard borders */ X long x0offset = g->io.win->BorderLeft + 8; X long x1offset = g->io.win->BorderRight + 8; X long y0offset = g->io.win->BorderBottom + 8; X long y1offset = g->io.win->BorderTop + 8; X X /* Save size used */ X g->io.oldwidth = g->io.win->Width; g->io.oldheight = g->io.win->Height; X X /* Delete old coords */ X if (g->io.rw) g->io.rw->delete(g->io.rw); X X /* Create new coords at max size */ X g->io.rw = new_RWindow(g->io.win->RPort, g->io.win->Width, g->io.win->Heigh Xt, X x0offset, y0offset, x1offset, y1offset, X g->a.x.min, g->a.y.min, g->a.x.max, g->a.y.max, X g->a.x.log, g->a.y.log, TRUE); X if (!g->io.rw) X { X message(g, "Couldn't make coords", (char *)NULL); X return; X } X X SetRast(g->io.win->RPort, 0); /* Clear whole window */ X X if (g->ok && g->a.ok && g->a.ratio != NOVAL) X { X /* Adjust for desired ratio */ X double r; X X /* Current ratio */ X r = g->a.ratio / X fabs(((g->io.rw->sy(g->io.rw, g->a.y.log ? 10.0 : 2.0) - g->io.rw-> Xsy(g->io.rw, 1.0)) * scr_dpmx) / X ((g->io.rw->sx(g->io.rw, g->a.x.log ? 10.0 : 2.0) - g->io.rw-> Xsx(g->io.rw, 1.0)) * scr_dpmy)); X X g->io.rw->delete(g->io.rw); X X /* Adjust borders */ X if (r > 1.0) /* make X smaller */ X { X long width = g->io.win->Width - x0offset - x1offset; X long delta = width - width / r; X X x0offset += delta / 2; X x1offset += delta - delta / 2; X } X else /* make Y smaller */ X { X long height = g->io.win->Height - y0offset - y1offset; X long delta = height - height * r; X X y0offset += delta / 2; X y1offset += delta - delta / 2; X } X /* & create new coord system */ X g->io.rw = new_RWindow(g->io.win->RPort, g->io.win->Width, g->io.win->H Xeight, X x0offset, y0offset, x1offset, y1offset, X g->a.x.min, g->a.y.min, g->a.x.max, g->a.y.max, X g->a.x.log, g->a.y.log, TRUE); X if (!g->io.rw) X { X message(g, "Couldn't make coords", (char *)NULL); X return; X } X } X} X X/* Change output resolution. X This may require opening of new resources (eg fonts) in the objects, which X can obviously fail, yet we don't want the graph to be left in an inconsis- X tent state, hence inform & confirm (cf object.guidelines). */ Xint set_dpm(struct graph *g, int dpmx, int dpmy) X{ X int olddpmx = g->io.dpmx, olddpmy = g->io.dpmy; X struct TextFont *digits; X X g->io.dpmx = dpmx; g->io.dpmy = dpmy; X /* Open correct font for this resolution */ X if (digits = open_font(g, "digits.font", DIGHEIGHT, 0, 0)) X { X int ok = TRUE; X struct object *scan, *scan2; X X /* Inform objects */ X for (scan = first(&g->o_list); ok && succ(scan); scan = succ(scan)) X if (!scan->inform(scan)) ok = FALSE; X X if (ok) X { X /* Everything worked, confoirm changes */ X for (scan = first(&g->o_list); ok && succ(scan); scan = succ(scan)) X X scan->confirm(scan, TRUE); X X /* Free old resources */ X CloseFont(g->io.digits); X g->io.digits = digits; X return TRUE; X } X /* Return to previous state */ X for (scan2 = first(&g->o_list); scan2 != scan; scan2 = succ(scan2)) X scan2->confirm(scan2, FALSE); X X CloseFont(digits); X } X else X message(g, "Couldn't open digits.font", (char *)NULL); X g->io.dpmx = olddpmx; g->io.dpmy = olddpmy; X return FALSE; X} X X /* Set pen to one ptsize wide. Should really modify g->io.rw->rp, but ... * X/ Xvoid set_pensize(struct graph *g, struct RastPort *rp, double ptsize) X{ X int xsize = xinch2dots(g, ptsize / 72.0); X int ysize = yinch2dots(g, ptsize / 72.0); X X if ((xsize & 1) == 0) xsize++; X if ((ysize & 1) == 0) ysize++; X X rp->PenWidth = xsize; X rp->PenHeight = ysize; X} X X/* Use a "thin" line (1 pixel wide) */ Xvoid set_thin(struct graph *g, struct RastPort *rp) X{ X rp->PenWidth = 1; X rp->PenWidth = 1; X} X X/* Change graph limits */ Xvoid zoom_in(struct graph *g, double x0, double y0, double x1, double y1) X{ X if (g->ok && x0 != x1 && y0 != y1) X { X double xmin = min(x0, x1); X double xmax = max(x0, x1); X double ymin = min(y0, y1); X double ymax = max(y0, y1); X X g->a.x.min = max(g->a.x.min, xmin); X g->a.x.max = min(g->a.x.max, xmax); X g->a.y.min = max(g->a.y.min, ymin); X g->a.y.max = min(g->a.y.max, ymax); X X check_graph(g); X } X else X message(g, "No rectangle to zoom into", (char *)NULL); X} X X/* Calculate new limits for ax a for a zoom factor of mult */ Xstatic void zoom_ax(struct ax *a, double mult) X{ X if (a->log) X { X /* Zoom on a log scale ... Real fun with this one :-) */ X double centre = sqrt(a->min * a->max); X double delta = pow(centre, 1.0 - mult); X X a->min = pow(a->min, mult) * delta; X a->max = pow(a->max, mult) * delta; X } X else X { X /* ax becomes mult times longer, with same centre */ X double centre = (a->min + a->max) / 2; X double delta = centre - a->min; X X a->min = centre - mult * delta; X a->max = centre + mult * delta; X } X} X X/* Zoom out by a factor factor */ Xvoid zoom_factor(struct graph *g, double factor) X{ X if (g->ok) X { X zoom_ax(&g->a.x, factor); X zoom_ax(&g->a.y, factor); X check_graph(g); X } X else X message(g, "No scale set !", (char *)NULL); X} X X/* Define new centre for ax */ Xstatic void center_ax(struct ax *a, double centre) X{ X if (a->log) X { X /* more & more fun ... */ X double delta = sqrt(a->max / a->min); X X a->max = centre * delta; X a->min = centre / delta; X } X else X { X /* obvious */ X double delta = (a->max - a->min) / 2.0; X X a->max = centre + delta; X a->min = centre - delta; X } X} X X/* New centre for graph */ Xvoid center_graph(struct graph *g, double x, double y) X{ X if (g->ok && x != NOVAL) X { X center_ax(&g->a.x, max(min(x, g->a.x.max), g->a.x.min)); X center_ax(&g->a.y, max(min(y, g->a.y.max), g->a.y.min)); X check_graph(g); X } X else X message(g, "Nothing to center on", (char *)NULL); X} X X/* Redraw ax a (of graph g), at position xorig on other ax. y_ax is true for X drawing of y axis */ Xstatic void draw_ax(struct graph *g, struct ax *a, double xorig, int y_ax) X{ X struct TextFont *oldfont, *digits = g->io.digits; X struct RWindow *const rwin = g->io.rw; X struct RastPort *const rp = rwin->rp; X int xtick = yinch2dots(g, XTICK), ytick = xinch2dots(g, YTICK); X int xtext = xinch2dots(g, XTEXT), ytext = yinch2dots(g, YTEXT); X X oldfont = rp->Font; X SetFont(rp, digits); X SetDrMd(rp, JAM1); X if (a->ax != NOVAL) X { X /* Draw ax */ X if (y_ax) X { X RMove(rwin, a->ax, a->max); X Draw(rwin->rp, rwin->rp->cp_x, ftol(rwin->sy(rwin, a->min))); X } X else X { X RMove(rwin, a->max, a->ax); X Draw(rwin->rp, ftol(rwin->sx(rwin, a->min)), rwin->rp->cp_y); X } X if (a->cstep != NOVAL) X { X /* Draw ticks and numbers */ X if (a->log) X { X /* logarithmic ax */ X double nax; X int emin, emax, e, count, i; X X /* Normalise origin (0 < nax < 10). There will be count ticks, X at nax, nax + cstep, nax + 2 * cstep, etc for every exponent X X value between min & max */ X nax = xorig * pow(10.0, -floor(log10(xorig))); X /* Exponent range */ X emin = floor(log10(a->min / nax)); X emax = ceil(log10(a->max / nax)); X /* Number of ticks for every exponent value */ X count = ceil(9 * nax / a->cstep); X X for (e = emin; e <= emax; e++) X { X double const p = pow(10.0, (double)e); X double const st = a->cstep * p; /* step between ticks */ X double x = p * nax; /* POos. of main tick */ X long cx, cy; X X /* Display main value (at nax, with exponent) */ X if (a->every != INOVAL && x != xorig) X { X if (y_ax) X RMove(rwin, a->ax, x); X else X RMove(rwin, x, a->ax); X X cx = rp->cp_x; cy = rp->cp_y; X X if (e == 0) X { X /* don't display 10^0 */ X char nb[NBLEN]; X int l; X X /* Display nax */ X double2str(nb, nax); X l = strlen(nb); X if (y_ax) X Move(rp, cx + xtext, cy + digits->tf_Baseline / X 2); X else X Move(rp, cx - digits->tf_XSize * l / 2, cy + di Xgits->tf_Baseline + ytext); X Text(rp, nb, l); X } X else X { X char nb[NBLEN + 3], expo[NBLEN]; X int l1, l2; X X if (nax == 1) X strcpy(nb, "10"); X else X { X double2str(nb, nax); X strcat(nb, "*10"); X } X sprintf(expo, "%d", e); X l1 = strlen(nb); X l2 = strlen(expo); X /* Display base */ X if (y_ax) X Move(rp, cx + xtext, cy + digits->tf_Baseline / X 2); X else X Move(rp, cx, cy + digits->tf_Baseline + digits- X>tf_Baseline / 2 + ytext); X Text(rp, nb, l1); X /* Display exponent */ X Move(rp, rp->cp_x, rp->cp_y - digits->tf_Baseline / X 2); X Text(rp, expo, l2); X X } X } X /* Now for the ticks ... */ X for (i = 0; i < count; i++, x += st) X { X /* Draw tick */ X if (y_ax) X RMove(rwin, a->ax, x); X else X RMove(rwin, x, a->ax); X X cx = rp->cp_x; cy = rp->cp_y; X X if (y_ax) X { X Move(rp, cx + ytick, cy); X Draw(rp, cx - ytick, cy); X } X else X { X Move(rp, cx, cy + xtick); X Draw(rp, cx, cy - xtick); X } X /* Display digits */ X if (i != 0 && a->every != INOVAL && (i % a->every) == 0 X) X { X char nb[NBLEN]; X int l; X X /* Only display mantissa */ X double2str(nb, nax + i * a->cstep); X l = strlen(nb); X if (y_ax) X Move(rp, cx + xtext, cy + digits->tf_Baseline / X 2); X else X Move(rp, cx - digits->tf_XSize * l / 2 + 1, cy X+ digits->tf_Baseline + ytext); X Text(rp, nb, l); X } X } X } X } X else X { X /* linear ax */ X long count, disp_digits; X double x; X X /* Number of ticks */ X count = ceil((a->min - xorig) / a->cstep); X /* First tick at which to show value */ X disp_digits = a->every == INOVAL ? X count - 1 : /* No digits displayed */ X a->every * (long)ceil((a->min - xorig) / (a->cs Xtep * a->every)); X X for(x = count * a->cstep + xorig; x <= a->max; x += a->cstep, c Xount++) X { X long cx, cy; X X /* Draw tick */ X if (y_ax) X RMove(rwin, a->ax, x); X else X RMove(rwin, x, a->ax); X X cx = rp->cp_x; cy = rp->cp_y; X X if (y_ax) X { X Move(rp, cx + ytick, cy); X Draw(rp, cx - ytick, cy); X } X else X { X Move(rp, cx, cy + xtick); X Draw(rp, cx, cy - xtick); X } X X /* Display digits */ X if (count == disp_digits) X { X char nb[NBLEN]; X int l; X X /* Next one in every ticks */ X disp_digits += a->every; X X if (count != 0) /* Not at origin */ X { X double2str(nb, x); X l = strlen(nb); X if (y_ax) X Move(rp, cx + xtext, cy + digits->tf_Baseline / X 2); X else X Move(rp, cx - digits->tf_XSize * l / 2, cy + di Xgits->tf_Baseline + ytext); X Text(rp, nb, l); X } X } X } X } X } X } X SetFont(rp, oldfont); X} X X/* Draws directly into the rastport, used internally. allow_mes nust be FALSE X if called during window refresh */ Xstatic void do_draw(struct graph *g, int allow_mes) X{ X struct object *o; X X if (g->ok && g->io.rw) X { X /* Draw axes */ X SetAPen(g->io.rw->rp, 1L); X if (g->a.ok) X { X draw_ax(g, &g->a.x, g->a.y.ax, FALSE); X draw_ax(g, &g->a.y, g->a.x.ax, TRUE); X } X X /* Draw objects */ X for (o = first(&g->o_list); succ(o); o = succ(o)) X if (o != g->s.current && o->ok) o->draw(o, allow_mes); X X /* Current object is always last so that it appears "on top" */ X if (g->s.current && g->s.current->ok) g->s.current->draw(g->s.current, Xallow_mes); X } X} X X/* Redraw graph completely */ Xvoid draw_graph(struct graph *g, int allow_mes) X{ X if (allow_mes) set_title(g); X X SetRast(g->io.rw->rp, 0); /* Clear window */ X X do_draw(g, allow_mes); X} X X/* Redraw graph partially (ref is NULL for no redraw). ref is disposed when X refresh is done */ Xvoid refresh_graph(struct graph *g, int allow_mes, struct Region *ref) X{ X if (ref) X { X if (g->io.rw) X { X /* Setup clipping */ X struct Region *oldRegion = InstallClipRegion(g->io.rw->rp->Layer, r Xef); X X SetRast(g->io.rw->rp, 0); X do_draw(g, allow_mes); X X InstallClipRegion(g->io.rw->rp->Layer, oldRegion); X } X DisposeRegion(ref); X } X} X X/* Returns a region that will fully refresh g. X (makes a copy of the current region) */ Xstruct Region *full_refresh(struct graph *g) X{ X struct Region *r; X X if ((r = NewRegion()) && g->io.rw) X { X struct Region *old = InstallClipRegion(g->io.rw->rp->Layer, NULL); X X /* Make copy */ X if (!OrRegionRegion(old, r)) X { X DisposeRegion(r); X r = NULL; X } X X InstallClipRegion(g->io.rw->rp->Layer, old); X } X return r; X} X X/* Open printer.device */ Xstatic int open_prt(void) X{ X if (prtport = CreatePort(0L, 0L)) X { X if (prtio = (struct IODRPReq *)CreateExtIO(prtport, sizeof(struct IODRP XReq))) X { X if (OpenDevice("printer.device", 0, (struct IORequest *)prtio, 0) = X= 0) X { X ped = &((struct PrinterData *)prtio->io_Device)->pd_SegmentData X->ps_PED; X prtprefs = &((struct PrinterData *)prtio->io_Device)->pd_Prefer Xences; X X return TRUE; X } X DeleteExtIO((struct IORequest *)prtport); X } X DeletePort(prtport); X } X return FALSE; X} X X/* Close printer device */ Xstatic void close_prt(void) X{ X CloseDevice((struct IORequest *)prtio); X DeleteExtIO((struct IORequest *)prtio); X DeletePort(prtport); X} X X/* Easy access to DumpRPort. wait : DoIO or SendIO ? */ Xstatic void prt_raster(int wait, struct RastPort *rp, struct ColorMap *cm, ULON XG m, UWORD sx, UWORD sy, UWORD w, UWORD h, LONG dc, LONG dr, UWORD special) X{ X prtio->io_RastPort = rp; X prtio->io_ColorMap = cm; X prtio->io_Modes = m; X prtio->io_SrcX = sx; X prtio->io_SrcY = sy; X prtio->io_SrcWidth = w; X prtio->io_SrcHeight = h; X prtio->io_DestCols = dc; X prtio->io_DestRows = dr; X prtio->io_Special = special; X prtio->io_Command = PRD_DUMPRPORT; X if (wait) DoIO(prtio); X else SendIO(prtio); X} X X/* Print a slice of the dump to the printer */ Xstatic int prt_slice(struct graph *g, struct RastPort *rp, int w, int h, int y, X int slice, struct Requester *abreq) X{ X struct RWindow *old = g->io.rw; X int ret = FALSE; X ULONG prtsig = 1 << prtio->io_Message.mn_ReplyPort->mp_SigBit; X ULONG winsig = 1 << g->io.win->UserPort->mp_SigBit; X X /* Create coords for slice */ X g->io.rw = new_RWindow(rp, w, slice, X 0, y + slice - h, 0, -y, X g->a.x.min, g->a.y.min, g->a.x.max, g->a.y.max, X g->a.x.log, g->a.y.log, FALSE); X if (g->io.rw) X { X int done = FALSE; X X /* Draw into rastport. Note that it may be > 1024 x 1024 ! */ X BigSetRast(rp, 0); X do_draw(g, TRUE); X prt_raster(FALSE, rp, prtcm, 0L, 0, 0, (UWORD)w, (UWORD)slice, w, slice X, SPECIAL_NOFORMFEED); X X do { /* Wait for end of printing or abort */ X Wait(prtsig | winsig); X if (aborted(abreq)) X { X done = TRUE; X ret = FALSE; /* Stop! */ X if (!CheckIO(prtio)) X { X AbortIO(prtio); X WaitIO(prtio); X } X prtio->io_Error = 0; X } X else if (CheckIO(prtio)) X { X done = TRUE; X ret = prtio->io_Error == 0; X } X } while (!done); X g->io.rw->delete(g->io.rw); X } X else X nomem(g->io.win); X g->io.rw = old; X return ret; X} X X/* Work out the max size for a slice (don't use more than half the largest X chunk of chip ram). The height must be a multiple of quanta */ Xstatic int get_slice_height(int w, int h, int quanta) X{ X long use = AvailMem(MEMF_CHIP | MEMF_LARGEST) / 2; X long min = 2 * RASSIZE(w, quanta); X long nb = use / min; X X if (nb == 0) /* Not much mem ... */ return quanta; X else if (nb * quanta > h) return h; X else return nb * quanta; X} X X/* Create a 2 plane Rastport with clipping at its limits (-> Layer) */ Xstatic struct RastPort *alloc_ras(int w, int h) X{ X struct Layer_Info *li; X struct BitMap *bm; X BYTE *data; X struct Layer *l; X X /* Alloc components */ X if (li = NewLayerInfo()) X { X if (bm = AllocMem(sizeof(struct BitMap), 0L)) X { X if (data = AllocMem(2 * RASSIZE(w, h), MEMF_CHIP)) X { X /* Set up data structure */ X InitBitMap(bm, 2, w, h); X bm->Planes[0] = (PLANEPTR)data; X bm->Planes[1] = (PLANEPTR)(data + RASSIZE(w, h)); X X if (l = CreateUpfrontLayer(li, bm, 0, 0, w - 1, h - 1, LAYERSIM XPLE, NULL)) X return l->rp; X X FreeMem(data, 2 * RASSIZE(w, h)); X } X FreeMem(bm, sizeof(struct BitMap)); X } X DisposeLayerInfo(li); X } X return NULL; X} X X/* Free rastport created by alloc_ras */ Xstatic void free_ras(struct RastPort *rp) X{ X struct Layer_Info *li = rp->Layer->LayerInfo; X struct BitMap *bm = rp->BitMap; X X DeleteLayer(li, rp->Layer); X FreeMem(bm->Planes[0], bm->BytesPerRow * bm->Rows * bm->Depth); X FreeMem(bm, sizeof(struct BitMap)); X DisposeLayerInfo(li); X} X X/* Print graph into a bitmap which is w by h pixels, resolution xdpm, ydpm, X quanta is the size of the print head (if any, 1 otherwise). dump_slice is X called for every slice. X Rem: printing is broken into horizontal slices, according to available X memory */ Xstatic void prt(struct graph *g, int w, int h, int quanta, int xdpm, int ydpm, Xprtfunc dump_slice) X{ X int slice, y, ok = TRUE; X struct RastPort *rp; X struct Requester *abreq; X struct Requester *req; X struct Memory *m; X struct Gadget *gl = NULL, *thin; X char size[NBLEN]; X X size[0] = '\0'; X X /* Ask user for print characteristics (pen size) */ X if ((m = NewMemory()) && X (req = InitReq(50, 20, 180, 85, m)) && X SetReqBorder(req, 1, m) && X AddIntuiText(&req->ReqText, "Print Characteristics", 6, 6, m) && X (thin = AddRadio(&gl, 0, "Thin", TRUE, SELECTED, RELVERIFY, 2, 11, 20, X10, 10, m)) && X AddRadio(&gl, 0, "Thick,", TRUE, 0, RELVERIFY, 1, 11, 40, 10, 10, m) && X X AddText(&gl, 0, "Size ", FALSE, size, NBLEN, TRUE, 0, RELVERIFY, 128, 4 X1, 32, 10, TRUE, m) && X AddBox(&gl, TRUE, "Ok", 0, RELVERIFY | ENDGADGET, 13, 60, 65, 15, FALSE X, m) && X AddBox(&gl, FALSE, "Cancel", 0, RELVERIFY | ENDGADGET, 103, 60, 65, 15, X FALSE, m)) X { X SetReqGadgets(req, gl); X if (DoRequest(req, g, std_ghandler)) X { X double ptsize = str2double(size); X X if ((thin->Flags & SELECTED) || (ptsize > 0.0 && ptsize != NOVAL)) X { X /* Set up for printing */ X if (set_dpm(g, xdpm, ydpm)) X { X slice = get_slice_height(w, h, quanta); X if (rp = alloc_ras(w, slice)) X { X if (abreq = abort_request(g, "Printing, slice X ")) X { X if (thin->Flags & SELECTED) set_thin(g, rp); X else set_pensize(g, rp, ptsize); X X /* Print all the slices */ X for (y = 0; ok && y < h; y += slice) X { X char msg[30]; X X sprintf(msg, "Printing, slice %d of %d", y / sl Xice + 1, (h + slice - 1) / slice); X set_abort_msg(abreq, msg); X ok = dump_slice(g, rp, w, h, y, min(slice, h - Xy), abreq); X } X end_abort_request(abreq); X } X else X message(g, "Abort requester failed", (char *)NULL); X X free_ras(rp); X } X else X nomem(g->io.win); X /* Return to screen */ X set_dpm(g, scr_dpmx, scr_dpmy); X } X } X else X message(g, "Illegal line size", (char *)NULL); X } X } X Free(m); X} X X/* get max size of printed output */ Xstatic int get_max_size(struct graph *g, int *w, int *h, int *quanta) X{ X prt_raster(TRUE, g->io.win->RPort, prtcm, 0L, 0, 0, g->io.win->Width, g->io X.win->Height, LONG_MAX, LONG_MAX, SPECIAL_NOPRINT); X X *w = prtio->io_DestCols; X *h = prtio->io_DestRows; X /* If length = infinity, use MAXPRINTPAGES pages */ X if (*h == LONG_MAX) X *h = (ped->ped_YDotsInch * MAXPRINTPAGES * prtprefs->PaperLength) / (pr Xtprefs->PrintSpacing == SIX_LPI ? 6 : 8); X *quanta = ped->ped_NumRows; X X return prtio->io_Error == 0; X} X X/* Determine size which fits asked for ratio (when no absolute size set in X preferences) */ Xstatic int get_size(struct graph *g, int *w, int *h, int *quanta) X{ X if (get_max_size(g, w, h, quanta)) X { X if (g->ok && g->a.ok && g->a.ratio != NOVAL) X { X /* adjust for ratio */ X double r = g->a.ratio / X fabs((*h / (g->a.y.log ? log10(g->a.y.max / g->a.y.min) : (g->a X.y.max - g->a.y.min)) * ped->ped_XDotsInch) / X (*w / (g->a.x.log ? log10(g->a.x.max / g->a.x.min) : (g->a X.x.max - g->a.x.min)) * ped->ped_YDotsInch)); X X if (r > 1.0) /* make X smaller */ X *w /= r; X else /* make Y smaller */ X *h *= r; X } X if (*w == 0) *w = 1; X if (*h == 0) *h = 1; X return TRUE; X } X return FALSE; X} X X/* determine size to use when user has set abs. size in preferences */ Xstatic int get_abs_size(struct graph *g, int *w, int *h, int *quanta) X{ X if (get_max_size(g, w, h, quanta)) X { X if (g->ok && g->a.ok && g->a.ratio != NOVAL) X { X /* Adjust only if dimension is free (size set to 0) */ X double r = g->a.ratio / X fabs((*h / (g->a.y.log ? log10(g->a.y.max / g->a.y.min) : (g->a X.y.max - g->a.y.min)) * ped->ped_XDotsInch) / X (*w / (g->a.x.log ? log10(g->a.x.max / g->a.x.min) : (g->a X.x.max - g->a.x.min)) * ped->ped_YDotsInch)); X X if (r > 1.0 && prtprefs->PrintMaxWidth == 0) /* make X smaller */ X *w /= r; X else if (r < 1.0 && prtprefs->PrintMaxHeight == 0) /* make Y smalle Xr */ X *h *= r; X } X if (*w == 0) *w = 1; X if (*h == 0) *h = 1; X return TRUE; X } X return FALSE; X} X X/* Print a graph */ Xvoid prt_graph(struct graph *g) X{ X int w, h, quanta; X X if (g->ok) X if (open_prt()) X { X prtprefs->PrintFlags &= ~INTEGER_SCALING; /* We produce nice output X anyway ! */ X prtprefs->PrintAspect = ASPECT_HORIZ; /* No support for vertical pl Xots */ X X switch (prtprefs->PrintFlags & DIMENSIONS_MASK) X { X case MULTIPLY_DIMENSIONS: /* Ignored */ X prtprefs->PrintFlags &= ~DIMENSIONS_MASK; X /* FALLTHROUGH */ X case IGNORE_DIMENSIONS: X case BOUNDED_DIMENSIONS: X if (get_size(g, &w, &h, &quanta)) X { X prtprefs->PrintFlags &= ~DIMENSIONS_MASK; X prt(g, w, h, quanta, (int)(ped->ped_XDotsInch / INCH), X(int)(ped->ped_YDotsInch / INCH), prt_slice); X } X break; X X case ABSOLUTE_DIMENSIONS: X case PIXEL_DIMENSIONS: X if (get_abs_size(g, &w, &h, &quanta)) X { X prtprefs->PrintFlags &= ~DIMENSIONS_MASK; X prt(g, w, h, quanta, (int)(ped->ped_XDotsInch / INCH), X(int)(ped->ped_YDotsInch / INCH), prt_slice); X } X break; X } X if (prtio->io_Error > 0) /* Printer error */ X message(g, "Printer Error", prt_error[prtio->io_Error > MAX_PRT X_ERROR ? 0 : prtio->io_Error], (char *)NULL); X close_prt(); X } X else X message(g, "Couldn't open printer\n", (char *)NULL); X} X X/* Write slice into body. Must add byteRun1 compression */ Xstatic int write_iffslice(struct BitMap *bm) X{ X int y, plane; X X for (y = 0; y < bm->Rows; y++) X { X /* Interleave bit planes */ X for (plane = 0; plane < bm->Depth; plane++) X if (!fwrite(bm->Planes[plane] + y * bm->BytesPerRow, bm->BytesPerRo Xw, 1, iff_file)) X return FALSE; X } X return TRUE; X} X X/* Draw & write into slice for iff output */ Xstatic int iff_slice(struct graph *g, struct RastPort *rp, int w, int h, int y, X int slice, struct Requester *abreq) X{ X struct RWindow *old = g->io.rw; X int ret = FALSE; X X g->io.rw = new_RWindow(rp, w, slice, X 0, y + slice - h, 0, -y, X g->a.x.min, g->a.y.min, g->a.x.max, g->a.y.max, X g->a.x.log, g->a.y.log, FALSE); X if (g->io.rw) X { X BigSetRast(rp, 0); X do_draw(g, TRUE); X if (write_iffslice(rp->BitMap)) X { X if (!aborted(abreq)) ret = TRUE; X } X else X message(g, "Error writing file", (char *)NULL); X X g->io.rw->delete(g->io.rw); X } X else X nomem(g->io.win); X g->io.rw = old; X return ret; X} X X/* Write ILBM header to file (BMHD, CMAP & start of BODY). Save positions for X writing sizes */ Xstatic int start_iff(char *name, int w, int h, int xdpi, int ydpi) X{ X if (iff_file = fopen(name, "w")) X { X if (fwrite("FORM", 4, 1, iff_file) && (form_pos = ftell(iff_file)) != - X1L && X fwrite("\0\0\0\0ILBMBMHD\0\0\0\024", 16, 1, iff_file)) X { X static BitMapHeader bm = { X 0, 0, 0, 0, 2, mskNone, cmpNone, 0, 0 X }; X bm.w = w; bm.h = h; X bm.xAspect = ydpi; bm.yAspect = xdpi; X bm.pageWidth = w; bm.pageHeight = h; X X if (fwrite((char *)&bm, sizeof(bm), 1, iff_file) && X fwrite("CMAP\0\0\0\14", 8, 1, iff_file) && X fwrite("\xff\xff\xff\0\0\0\0\0\0\0\0\0", 12, 1, iff_file) && X fwrite("BODY", 4, 1, iff_file) && (body_pos = ftell(iff_file)) X!= -1 && X fwrite("\0\0\0\0", 4, 1, iff_file)) X { X return TRUE; X } X } X /* If failed, delete file */ X if (fclose(iff_file) == 0) unlink(name); X } X return FALSE; X} X X/* Write end of iff_file, ie pad, write lengths & close */ Xstatic int end_iff(void) X{ X long end = ftell(iff_file), end2 = end; X X if (end != -1) X { X if ((end & 1) == 0 || (end2++, fwrite("\0", 1, 1, iff_file))) /* pad. y Xuck. */ X { X long formlen = end2 - form_pos - 4; /* Includes pad byte */ X long bodylen = end - body_pos - 4; /* Doesn't */ X X if (fseek(iff_file, form_pos, 0) != -1 && X fwrite((char *)&formlen, sizeof(long), 1, iff_file) && X fseek(iff_file, body_pos, 0) != -1 && X fwrite((char *)&bodylen, sizeof(long), 1, iff_file)) X { X return fclose(iff_file) == 0; X } X } X } X return FALSE; X} X X/* Handle iff requester: display file req when user clicks on disk */ Xstatic int iff_handler(struct Gadget *gg, ULONG class, struct Requester *req, s Xtruct graph *g) X{ X if (gg->GadgetID == IFFDISK) X { X char file[FILELEN]; X X if (getfile(file, "IFF Output file")) X { X UWORD pos = RemoveGList(req->RWindow, iffg, 1); X strcpy(iff_filename, file); X RefreshGList(iffg, req->RWindow, req, 1); X AddGList(req->RWindow, iffg, pos, 1, req); X } X ActivateGadget(iffg, req->RWindow, req); X return FALSE; X } X else return std_ghandler(gg, class, req, g); X} X X/* Output graph as ILBM. Asks for bitmap size, and resolution. Ignores graph X ratio. */ Xvoid iff_todisk(struct graph *g) X{ X struct Requester *req; X struct Memory *m; X struct Gadget *gl = NULL; X char xsize[NBLEN], ysize[NBLEN], xdpi[NBLEN], ydpi[NBLEN]; X X xsize[0] = '\0'; ysize[0] = '\0'; X strcpy(xdpi, "72"); strcpy(ydpi, "72"); X X iff_filename[0] = '\0'; X if ((m = NewMemory()) && X (req = InitReq(50, 20, 280, 110, m)) && X SetReqBorder(req, 1, m) && X AddIntuiText(&req->ReqText, "Write IFF", 104, 6, m) && X (iffg = AddText(&gl, 0, "File ", FALSE, iff_filename, FILELEN, TRUE, 0, X RELVERIFY, 51, 20, 144, 10, TRUE, m)) && X AddBox(&gl, IFFDISK, "Disk", 0, RELVERIFY, 205, 17, 65, 15, FALSE, m) & X& X AddText(&gl, 0, "Size: X ", FALSE, xsize, INTLEN, TRUE, 0, RELVERIFY, 7 X5, 45, 32, 10, TRUE, m) && X AddText(&gl, 0, "Y ", FALSE, ysize, INTLEN, TRUE, 0, RELVERIFY, 140, 45 X, 32, 10, TRUE, m) && X AddText(&gl, 0, "DPI: X ", FALSE, xdpi, INTLEN, TRUE, 0, RELVERIFY, 83, X 65, 32, 10, TRUE, m) && X AddText(&gl, 0, "Y ", FALSE, ydpi, INTLEN, TRUE, 0, RELVERIFY, 140, 65, X 32, 10, TRUE, m) && X AddBox(&gl, TRUE, "Ok", 0, RELVERIFY | ENDGADGET, 38, 85, 65, 15, FALSE X, m) && X AddBox(&gl, FALSE, "Cancel", 0, RELVERIFY | ENDGADGET, 178, 85, 65, 15, X FALSE, m)) X { X SetReqGadgets(req, gl); X if (DoRequest(req, g, iff_handler)) X { X int w = str2int(xsize); X int h = str2int(ysize); X int xdp = str2int(xdpi); X int ydp = str2int(ydpi); X X if (w > 0 && h > 0 && xdp > 0 && ydp > 0 && X w != NOVAL && h != NOVAL && xdp != NOVAL && ydp != NOVAL) X if (start_iff(iff_filename, w, h, xdp, ydp)) X { X prt(g, w, h, 1, (int)(xdp / INCH), (int)(ydp / INCH), iff_s Xlice); X if (!end_iff()) X message(g, "Error writing file", (char *)NULL); X } X else X message(g, "Error writing file", (char *)NULL); X else X message(g, "Illegal dimensions !", (char *)NULL); X } X } X Free(m); X} X X/* Write graph to file f */ Xint save_graph(struct graph *g, FILE *f) X{ X int ok = FALSE; X short tag = GRAPH_TAG; X X /* Write graph information */ X if (WRITE(f, tag) && X WRITE(f, g->io.win->LeftEdge) && X WRITE(f, g->io.win->TopEdge) && X WRITE(f, g->io.win->Width) && X WRITE(f, g->io.win->Height) && X WRITE(f, g->name) && X WRITE(f, g->a)) X { X struct object *scan; X X ok = TRUE; X /* Write all objects */ X for (scan = first(&g->o_list); ok && succ(scan); scan = succ(scan)) X { X if (!scan->save(scan, f)) ok = FALSE; X } X /* Write end tag */ X if (ok) X { X tag = GRAPH_END; X ok = WRITE(f, tag); X g->saved = TRUE; X } X } X return ok; X} X X/* Load & create new graph from file f (graph tag already read) */ Xstruct graph *load_graph(struct graph *from, FILE *f) X{ X struct graph *g = new_graph(from); X X if (g) X { X WORD leftedge, topedge, width, height; X int ok = FALSE; X X /* Read graph info */ X if (READ(f, leftedge) && X READ(f, topedge) && X READ(f, width) && X READ(f, height) && X READ(f, g->name) && X READ(f, g->a)) X { X int done = FALSE; X X ok = TRUE; X do /* Read objects */ X { X short tag; X struct object *o = NULL; X X if (READ(f, tag)) X switch (tag) /* We have to know which objects exist ... */ X { X case GRAPH_END: X done = TRUE; X break; X case LABEL_TAG: X o = (struct object *)load_label(g, f); X break; X case FUNCTION_TAG: X o = (struct object *)load_function(g, f); X break; X default: X message(g, "File is not a graph", (char *)NULL); X break; X } X ok = done || o != NULL; X if (o) X add_tail(&g->o_list, o); X } while (!done && ok); X X } X if (!ok) X { X delete_graph(g); X g = NULL; X } X else /* All ok, display */ X { X check_graph(g); X g->saved = TRUE; X } X } X return g; X} X X/* Delete a graph */ Xvoid delete_graph(struct graph *g) X{ X struct object *o, *next; X X /* Deselect */ X if (g->s.current) g->s.current->deselect(g->s.current); X X /* Delete all objects */ X for (o = first(&g->o_list); next = succ(o); o = next) X { X struct Region *ref = o->delete(o); X X DisposeRegion(ref); X } X X /* Delete all local resources */ X if (g->io.rw) g->io.rw->delete(g->io.rw); X CloseFont(g->io.digits); X cleanup_uio(g); /* Clears menus, closes window ... */ X FreeMem(g, sizeof(struct graph)); X} X X/* Create a new graph */ Xstruct graph *new_graph(struct graph *from) X{ X struct graph *const g = AllocMem(sizeof(struct graph), 0L); X char *msg; X const static struct graph def_g = { /* Default values */ X { NULL }, X FALSE, TRUE, X "Graph", X { MENUNULL }, X { NULL }, X { X { X NOVAL, NOVAL, X NOVAL, NOVAL, X INOVAL, X FALSE X }, X { X NOVAL, NOVAL, X NOVAL, NOVAL, X INOVAL, X FALSE X }, X NOVAL, X FALSE, FALSE X }, X { X FALSE, FALSE, X NOVAL, NOVAL, X NULL X } X }; X X if (g) X { X *g = def_g; X g->io.dpmx = scr_dpmx; g->io.dpmy = scr_dpmy; X new_list(&g->o_list); /* no objects */ X if (g->io.digits = open_font(g, "digits.font", DIGHEIGHT, 0, 0)) X { X if (init_uio(g)) /* Open window */ X { X set_scale(g); X draw_graph(g, TRUE); /* & display */ X return g; X } X else msg = "No window"; X CloseFont(g->io.digits); X } X else msg = "digits.font missing"; X FreeMem(g, sizeof(struct graph)); X } X else msg = "No memory !"; X X message(from, "Couldn't create graph", msg, (char *)NULL); X X return NULL; X} X X/* Global initialisation */ Xint init_grph(void) X{ X struct Screen wbscr; X X /* Find screen resolution */ X scr_dpmx = GfxBase->NormalDPMX; X scr_dpmy = GfxBase->NormalDPMY; X if (!GetScreenData((char *)&wbscr, sizeof(struct Screen), WBENCHSCREEN, NUL XL)) X return alert(NULL, "No Workbench !", NULL), FALSE; X X if (wbscr.ViewPort.Modes & HIRES) scr_dpmx *= 2; X if (wbscr.ViewPort.Modes & LACE) scr_dpmy *= 2; X X /* Color map for printer. Add colours ? */ X if (!(prtcm = GetColorMap(4))) return nomem(NULL), FALSE; X SetRGB4CM(prtcm, 0, 15, 15, 15); X SetRGB4CM(prtcm, 1, 0, 0, 0); X SetRGB4CM(prtcm, 2, 0, 0, 0); X SetRGB4CM(prtcm, 3, 0, 0, 0); X X return TRUE; X} X X/* Free any global resources */ Xvoid cleanup_grph(void) X{ X if (prtcm) FreeColorMap(prtcm); X} X SHAR_EOF echo "End of archive 4 (of 7)" # if you want to concatenate archives, remove anything after this line exit