[comp.sources.amiga] v89i207: graph - plot mathematical functions, Part04/07

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