home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Gold Fish 3
/
goldfish_volume_3.bin
/
files
/
misc
/
math
/
fastplot
/
source
/
plotlibrary.mod
< prev
next >
Encoding:
Amiga (detected)
Atari
Commodore
DOS
FM Towns/JPY
Macintosh
Macintosh JP
Macintosh to JP
NeXTSTEP
RISC OS/Acorn
Shift JIS
UTF-8
Wrap
Modula Implementation
|
1995-03-27
|
30.4 KB
|
1,152 lines
(*********************************************************************)
(* *)
(* Module PlotLibrary Copyright © 1994 by Computer Inspirations *)
(* *)
(*********************************************************************)
IMPLEMENTATION MODULE PlotLibrary;
(* Amiga-specific operating system modules *)
FROM Intuition IMPORT ScreenPtr, CloseScreen, NewScreen,
ScreenFlagSet, CustomScreen,
OpenScreen;
FROM Pens IMPORT SetAPen, SetBPen, Move, Draw,
RectFill, SetDrPt, SetDrMd;
FROM Rasters IMPORT RastPortPtr, Jam1, Jam2;
FROM Text IMPORT Text;
FROM Views IMPORT SetRGB4, ViewModeSet, ViewModes;
(* General Modula-2 modules *)
FROM MathLib0 IMPORT log, power;
FROM Storage IMPORT ALLOCATE, DEALLOCATE;
FROM SYSTEM IMPORT ADR, BYTE;
(* M2Sprint-specific module *)
FROM RealSupport IMPORT OpenRealTrans;
(* Custom module developed to support PlotLibrary *)
FROM StringUtils IMPORT LengthStr, ConvRealToStr;
CONST
BackColour = 0;
Colour1 = 1;
Colour2 = 2;
Colour3 = 3;
CharHeight = 8;
CharWidth = 8;
TYPE
ClassType = (Left, Right, Top, Bottom);
ClassSet = SET OF ClassType;
PlotSet = SET OF PlotKindType;
CONST
XLog = PlotSet{LogX, Log};
YLog = PlotSet{LogY, Log};
TYPE
PlotType = POINTER TO PlotRecord;
PlotRecord =
RECORD
Screen : ScreenPtr;
Drawing : RastPortPtr;
PlotIs : PlotKindType;
GridColour : CARDINAL;
TextColour : CARDINAL;
PlotColour : CARDINAL;
UserLabels : BOOLEAN;
GetLabel : LabelProc;(* label routine *)
ScatterPlot : BOOLEAN; (* scatter plots *)
ScatterChar : ARRAY [0..1] OF CHAR;
ZeroLineOn : BOOLEAN; (* line on/off *)
Minx, Miny, Maxx, Maxy : REAL; (* real coords. *)
GMnx, GMny, GMxx, GMxy : INTEGER; (* screen outline*)
Scrw, Scrh : INTEGER; (* & dimensions. *)
Divx, Divy, (* major divs. *)
SubDivx, SubDivy, (* minor divs. *)
Decx, Decy : CARDINAL; (* decimal places*)
xscale, yscale : REAL; (* x/y scaling *)
xoff, yoff : INTEGER; (* plot offsets *)
GridDrawn : BOOLEAN; (* draw once *)
CopyOfAPlot : BOOLEAN; (* not original *)
END;
(*-------------------------------------------------------*)
(* The following are a number of utility routines used *)
(* only within this module to facilitate the graphing. *)
PROCEDURE CreateScreen(width, height, depth : INTEGER;
HighResolution : BOOLEAN;
Interlaced : BOOLEAN;
title : ARRAY OF CHAR)
: ScreenPtr;
VAR
ScreenInfo : NewScreen;
Views : ViewModeSet;
BEGIN
Views := ViewModeSet{};
IF HighResolution THEN INCL(Views, HiRes) END;
IF Interlaced THEN INCL(Views, Lace) END;
WITH ScreenInfo DO
LeftEdge := 0;
TopEdge := 0;
Width := width;
Height := height;
Depth := depth;
DetailPen := BYTE(0);
BlockPen := BYTE(1);
ViewModes := Views;
Type := CustomScreen;
Font := NIL;
DefaultTitle:= NIL;
Gadgets := NIL;
CustomBitMap:= NIL;
IF title[0] # 0C THEN
DefaultTitle := ADR(title);
END;
END;
RETURN OpenScreen(ADR(ScreenInfo));
END CreateScreen;
PROCEDURE Round(In : REAL) : INTEGER;
BEGIN
RETURN TRUNC(In + 0.5);
END Round;
PROCEDURE TenTo(x : REAL) : REAL;
BEGIN
RETURN power(10.0, x);
END TenTo;
PROCEDURE LogOf(x : REAL) : REAL;
BEGIN
IF x <= 0.0 THEN (* capture illegal values *)
RETURN 0.0; (* and return log(1) *)
END;
RETURN log(x);
END LogOf;
PROCEDURE xyToCoords(Plot : PlotType;
x, y : REAL;
VAR xG, yG : INTEGER);
(* Convert floating point world coordinates to graph space
coordinates. *)
BEGIN
WITH Plot^ DO
xG := Round((x-Minx)*FLOAT(GMxx-GMnx)/(Maxx-Minx))+GMnx;
yG := GMxy-Round((y-Miny)*FLOAT(GMxy-GMny)/(Maxy-Miny));
END;
END xyToCoords;
PROCEDURE CoordsToxy(Plot : PlotType;
xG, yG : INTEGER;
VAR x, y : REAL);
(* Convert graph space coordinates to floating point world
coordinates. *)
BEGIN
WITH Plot^ DO
x := FLOAT(xG-GMnx)*(Maxx-Minx)/FLOAT(GMxx-GMnx)+Minx;
y := FLOAT(GMxy-yG)*(Maxy-Miny)/FLOAT(GMxy-GMny)+Miny;
END;
END CoordsToxy;
PROCEDURE OffsetPoint(Plot : PlotType; VAR x, y : INTEGER);
(* Offset the graph points by the plot amounts *)
BEGIN
INC(x, Plot^.xoff);
INC(y, Plot^.yoff);
END OffsetPoint;
PROCEDURE DrawLine(Plot : PlotType;
Colour : CARDINAL;
Gx1, Gy1, Gx2, Gy2 : INTEGER);
(* Line clipping algorithm : two points which make up a
line are passed (Gx1, Gy1) and (Gx2, Gy2) to be
clipped within the Plot space. NOTE: The Amiga screen
coordinates have y = 0 at the top of the screen.
From an algorithm published in "Microcomputer Graphics
for the IBM PC" by Roy E. Myers. *)
VAR
x, y : ARRAY [1..2] OF INTEGER;
xl, xh : INTEGER;
yl, yh : INTEGER;
Offset : INTEGER;
Bound : ARRAY [1..2] OF ClassSet;
Discard : BOOLEAN;
i : CARDINAL;
PROCEDURE Classify (VAR Boundary : ClassSet;
x, y : INTEGER);
BEGIN
Boundary := ClassSet{}; (* assume inside bounds *)
WITH Plot^ DO
IF x < xl THEN
INCL(Boundary, Left); (* outside left bounds *)
ELSIF x > xh THEN
INCL(Boundary, Right); (* outside right bounds *)
END;
IF y > yh THEN
INCL(Boundary, Bottom); (* outside bottom bounds *)
ELSIF y < yl THEN
INCL(Boundary, Top); (* outside top bounds *)
END;
END;
END Classify;
PROCEDURE Clip (Boundary : ClassSet);
VAR
dy, dx : INTEGER;
BEGIN
WITH Plot^ DO
dy := y[2] - y[1];
dx := x[2] - x[1];
IF Left IN Boundary THEN
x[i] := xl;
y[i] := y[1] + dy * (xl - x[1]) DIV dx;
ELSIF Right IN Boundary THEN
x[i] := xh;
y[i] := y[1] + dy * (xh - x[1]) DIV dx;
ELSIF Top IN Boundary THEN
x[i] := x[1] + dx * (yl - y[1]) DIV dy;
y[i] := yl;
ELSIF Bottom IN Boundary THEN
x[i] := x[1] + dx * (yh - y[1]) DIV dy;
y[i] := yh;
END;
END;
END Clip;
PROCEDURE PlotLine(x1, y1, x2, y2 : INTEGER);
(* Draw a clipped line to the screen. *)
CONST
HalfHeight = CharHeight DIV 2;
HalfWidth = CharWidth DIV 2;
BEGIN
WITH Plot^ DO
SetAPen(Drawing, Colour); (* set line colour *)
IF ScatterPlot THEN
(* move to the character centre and place char *)
Move(Drawing, x1-HalfWidth, y1+HalfHeight);
Text(Drawing, ADR(ScatterChar), 1);
ELSE
(* move to 1st pt and draw to 2nd point *)
Move(Drawing, x1, y1);
Draw(Drawing, x2, y2);
END;
END;
END PlotLine;
BEGIN
(* Initialize clipping routines *)
x[1] := Gx1; y[1] := Gy1;
x[2] := Gx2; y[2] := Gy2;
WITH Plot^ DO
IF GridDrawn THEN
Offset := 1; (* allow the outer grid *)
ELSE (* lines to be drawn first *)
Offset := 0;
END;
xl := GMnx + Offset; (* prevent overwriting the *)
xh := GMxx - Offset; (* grid boundary *)
yl := GMny + Offset;
yh := GMxy - Offset;
END;
(* Classify the endpoints *)
Classify(Bound[1], x[1], y[1]);
Classify(Bound[2], x[2], y[2]);
(* Perform the line clipping *)
IF Bound[1] + Bound[2] = ClassSet{} THEN
(* Within plot area -- just draw the line *)
PlotLine(x[1], y[1], x[2], y[2]);
ELSE
(* Just output a clipped line segment *)
i := 1;
REPEAT
Discard := Bound[1] * Bound[2] # ClassSet{};
IF NOT Discard THEN
IF Bound[i] # ClassSet{} THEN
(* Handle the line clipping *)
Clip(Bound[i]);
Classify(Bound[i], x[i], y[i]);
ELSE
INC(i);
END;
END;
UNTIL (i > 2) OR Discard;
IF NOT Discard THEN
PlotLine(x[1], y[1], x[2], y[2]); (* Clipped line *)
END;
END;
END DrawLine;
PROCEDURE IntPlotLine(Plot : PlotType;
Colour : CARDINAL;
x1, y1, x2, y2 : REAL);
(* Draw a line between points (x1,y1) and (x2,y2).
This routine accepts log(x) values and displays them
properly during log plots. *)
VAR
Gx1, Gy1, Gx2, Gy2 : INTEGER;
BEGIN
xyToCoords(Plot, x1, y1, Gx1, Gy1); (* to pixel coords *)
xyToCoords(Plot, x2, y2, Gx2, Gy2);
OffsetPoint(Plot, Gx1, Gy1); (* offset plot *)
OffsetPoint(Plot, Gx2, Gy2);
DrawLine(Plot, Colour, Gx1, Gy1, Gx2, Gy2);
END IntPlotLine;
PROCEDURE PlotLine(Plot : PlotType;
Colour : CARDINAL;
x1, y1, x2, y2 : REAL);
(* Draw a line between points (x1,y1) and (x2,y2).
This routine converts points to logarithmic points
during log plots to make external appearances
identical to the caller of this routine. *)
BEGIN
CASE Plot^.PlotIs OF
LogX :
IntPlotLine(Plot, Colour, LogOf(x1), y1,
LogOf(x2), y2);
|
LogY :
IntPlotLine(Plot, Colour, x1, LogOf(y1),
x2, LogOf(y2));
|
Log :
IntPlotLine(Plot, Colour, LogOf(x1), LogOf(y1),
LogOf(x2), LogOf(y2));
ELSE
IntPlotLine(Plot, Colour, x1, y1, x2, y2);
END;
END PlotLine;
PROCEDURE PlotBar(Plot : PlotType;
Colour : CARDINAL;
width : REAL;
x, y : REAL);
(* Draw a bar centered around x of height y and width
across. *)
VAR
Half : REAL;
Gxl, Gyl, Gxu, Gyu : INTEGER;
BEGIN
WITH Plot^ DO
Half := ABS(width) / 2.0;
IF y >= 0.0 THEN
xyToCoords(Plot, x-Half, y, Gxl, Gyl);
xyToCoords(Plot, x+Half, 0.0, Gxu, Gyu);
ELSE
xyToCoords(Plot, x-Half, 0.0, Gxl, Gyl);
xyToCoords(Plot, x+Half, y, Gxu, Gyu);
END;
IF Gyl # Gyu THEN
OffsetPoint(Plot, Gxl, Gyl); (* offset bar *)
OffsetPoint(Plot, Gxu, Gyu);
SetAPen(Drawing, Colour);
RectFill(Drawing, Gxl, Gyl, Gxu, Gyu);
END;
END;
END PlotBar;
PROCEDURE PlotXLabel(Plot : PlotType;
int : INTEGER;
x, y : REAL);
(* Place a label on the x-axis *)
VAR
Gx, Gy : INTEGER;
xlog : REAL;
str : ARRAY [0..40] OF CHAR;
BEGIN
WITH Plot^ DO
(* compensate for log scales along x-axis *)
IF PlotIs IN XLog THEN
xlog := TenTo(x);
ELSE
xlog := x;
END;
(* produce the x-axis labels *)
IF UserLabels THEN
GetLabel(int, str); (* call-back to get label *)
ELSIF NOT ConvRealToStr(str, xlog, 2, Decx, ' ') THEN
str := "***"; (* number conversion failed *)
END;
(* label the axis *)
xyToCoords(Plot, x, y, Gx, Gy);
DEC(Gx, LengthStr(str) * CharWidth DIV 2);
INC(Gy, 5 * CharHeight DIV 4);
LabelX(Plot, str, Gx, Gy);
END;
END PlotXLabel;
PROCEDURE PlotYLabel(Plot : PlotType;
x, y : REAL);
(* Place a label on the y-axis *)
VAR
Gx, Gy : INTEGER;
ylog : REAL;
str : ARRAY [0..40] OF CHAR;
BEGIN
WITH Plot^ DO
(* compensate for log scales along y-axis *)
IF PlotIs IN YLog THEN
ylog := TenTo(y);
ELSE
ylog := y;
END;
(* produce the y-axis labels *)
IF NOT ConvRealToStr(str, ylog, 2, Decy, ' ') THEN
str := "***";
END;
xyToCoords(Plot, x, y, Gx, Gy);
DEC(Gx, (LengthStr(str) + 1) * CharWidth);
INC(Gy, CharHeight DIV 4);
LabelX(Plot, str, Gx, Gy);
END;
END PlotYLabel;
PROCEDURE DrawGrid(Plot : PlotType);
(* Create the graph grid. *)
CONST
DashLine = BITSET{1,3,5,7,9,11,13,15};
NormLine = BITSET{0..15};
VAR
vCnt, hCnt : CARDINAL;
oldx, oldy : INTEGER;
x, y, xint, yint : REAL;
ScatterState : BOOLEAN;
PROCEDURE PlotMinorX;
VAR
Cnt : CARDINAL;
xm, xi : REAL;
BEGIN
WITH Plot^ DO
IF SubDivx > 1 THEN
(* Change to a dashed line *)
SetDrPt(Drawing, DashLine);
(* Output the minor grid lines *)
IF PlotIs IN XLog THEN
xi := TenTo(xint) / FLOAT(SubDivx+1);
FOR Cnt := 2 TO SubDivx DO
xm := x + LogOf(FLOAT(Cnt) * xi);
IntPlotLine(Plot, GridColour, xm, Miny,
xm, Maxy);
END;
ELSE
xi := xint / FLOAT(SubDivx);
FOR Cnt := 1 TO SubDivx-1 DO
xm := x + FLOAT(Cnt) * xi;
IntPlotLine(Plot, GridColour, xm, Miny,
xm, Maxy);
END;
END;
(* Back to a normal line *)
SetDrPt(Drawing, NormLine);
END;
END;
END PlotMinorX;
PROCEDURE PlotMinorY;
VAR
Cnt : CARDINAL;
ym, yi : REAL;
BEGIN
WITH Plot^ DO
IF SubDivy > 1 THEN
(* Change to a dashed line *)
SetDrPt(Drawing, DashLine);
(* Output the minor grid lines *)
IF PlotIs IN YLog THEN
yi := TenTo(yint) / FLOAT(SubDivy+1);
FOR Cnt := 2 TO SubDivy DO
ym := y + LogOf(FLOAT(Cnt) * yi);
IntPlotLine(Plot, GridColour, Minx, ym,
Maxx, ym);
END;
ELSE
yi := yint / FLOAT(SubDivy);
FOR Cnt := 1 TO SubDivy-1 DO
ym := y + FLOAT(Cnt) * yi;
IntPlotLine(Plot, GridColour, Minx, ym,
Maxx, ym);
END;
END;
(* Back to a normal line *)
SetDrPt(Drawing, NormLine);
END;
END;
END PlotMinorY;
BEGIN
WITH Plot^ DO
IF GridDrawn THEN RETURN END;
(* Draw the vertical grid lines or label bars *)
ScatterState := Plot^.ScatterPlot;
Plot^.ScatterPlot := FALSE; (* draw lines! *)
ClearPlot(Plot); (* clear plot area *)
oldx := xoff;
oldy := yoff;
SetPlotOffset(Plot, 0, 0); (* no grid offset *)
SetDrMd(Plot^.Drawing, Jam1);
IF Divx > 0 THEN
xint := (Maxx - Minx) / FLOAT(Divx);
FOR vCnt := 0 TO Divx DO
x := Minx + FLOAT(vCnt) * xint;
PlotMinorX;
IntPlotLine(Plot, GridColour, x, Miny, x, Maxy);
PlotXLabel(Plot, vCnt, x, Miny);
END;
END;
(* Draw the horizontal grid lines *)
IF Divy > 0 THEN
yint := (Maxy - Miny) / FLOAT(Divy);
FOR hCnt := 0 TO Divy DO
y := Miny + FLOAT(hCnt) * yint;
PlotMinorY;
IntPlotLine(Plot, GridColour, Minx, y, Maxx, y);
PlotYLabel(Plot, Minx, y);
END;
END;
SetPlotOffset(Plot, oldx, oldy); (* user offset *)
SetDrMd(Plot^.Drawing, Jam2);
GridDrawn := TRUE;
Plot^.ScatterPlot := ScatterState;
END;
END DrawGrid;
(*-------------------------------------------------------*)
(* The main graphing routines. *)
PROCEDURE Empty() : PlotType;
(* Used to initialize or pass empty plot parameters *)
BEGIN
RETURN NIL;
END Empty;
PROCEDURE SetPlotLimits(Plot : PlotType;
xMin, xMax, yMin, yMax : REAL);
(* Alter the current plot limits -- useful for overlaying
plots with different minima/maxima *)
BEGIN
WITH Plot^ DO
Miny := yMin; Maxy := yMax;
Minx := xMin; Maxx := xMax;
IF PlotIs IN XLog THEN
Minx := LogOf(xMin);
IF xMax <= 0.0 THEN (* make a log multiple of xMin *)
Maxx := Minx + FLOAT(Divx);
ELSE
Maxx := LogOf(xMax);
END;
END;
IF PlotIs IN YLog THEN
Miny := LogOf(yMin);
IF yMax <= 0.0 THEN (* make a log multiple of yMin *)
Maxy := Miny + FLOAT(Divy);
ELSE
Maxy := LogOf(yMax);
END;
END;
END;
END SetPlotLimits;
PROCEDURE SetPlotOffset(Plot : PlotType;
xoff, yoff : INTEGER);
(* Alter the current plot base position -- useful for
realigning bar charts or giving three dimensional
looking graphs *)
BEGIN
Plot^.xoff := xoff;
Plot^.yoff := yoff;
END SetPlotOffset;
PROCEDURE SetPlotScale(Plot : PlotType;
xscale, yscale : REAL);
(* Alter the plot scaling along x- and y-axes *)
BEGIN
Plot^.xscale := ABS(xscale);
Plot^.yscale := ABS(yscale);
(* limit to 1/10 reduction *)
IF Plot^.xscale < 0.1 THEN
Plot^.xscale := 0.1;
END;
IF Plot^.yscale < 0.1 THEN
Plot^.yscale := 0.1;
END;
END SetPlotScale;
PROCEDURE SetColourMap(Plot : PlotType;
ColourIndex : CARDINAL;
Red, Green, Blue : CARDINAL);
(* Change the set of predefined colours for the plots *)
BEGIN
WITH Plot^ DO
SetRGB4(ADR(Screen^.VPort),ColourIndex,Red,Green,Blue);
END;
END SetColourMap;
PROCEDURE SetScatterPlot(Plot : PlotType);
(* Set to plot single points or a scatter plot *)
BEGIN
Plot^.ScatterPlot := TRUE;
Plot^.ScatterChar := "O"; (* default character *)
END SetScatterPlot;
PROCEDURE SetScatterChar(Plot : PlotType;
Ch : CHAR);
(* Change the default `.' character for scatter plots *)
BEGIN
Plot^.ScatterChar[0] := Ch;
END SetScatterChar;
PROCEDURE InitOffsetPlot
(VAR Plot : PlotType;
PlotKind : PlotKindType;
xMin, xMax, yMin, yMax : REAL;
width, height : INTEGER;
xDiv, yDiv : CARDINAL;
xSubDiv, ySubDiv : CARDINAL;
xDec, yDec : CARDINAL;
xOffset, yOffset : INTEGER;
OldPlot : PlotType)
: BOOLEAN;
CONST
TopBorder = 20;
VAR
Ok : BOOLEAN;
Border1 : INTEGER;
Border2 : INTEGER;
DumStr : ARRAY [0..40] OF CHAR;
BEGIN
ALLOCATE(Plot, SIZE(Plot^));
IF Plot = NIL THEN
RETURN FALSE;
END;
WITH Plot^ DO
(* If OldPlot is defined use screen *)
IF OldPlot # NIL THEN
Screen := OldPlot^.Screen;
(* Extract the RastPort drawing surface information *)
Drawing := ADR(Screen^.RPort);
END;
(* Initialize the plot variables for later use *)
IF ConvRealToStr(DumStr, yMax, 2, yDec, ' ') THEN
Border1 := (LengthStr(DumStr) + 3) * CharWidth;
END;
IF ConvRealToStr(DumStr, yMin, 2, yDec, ' ') THEN
Border2 := (LengthStr(DumStr) + 3) * CharWidth;
END;
IF Border1 > Border2 THEN
GMnx := Border1;
ELSE
GMnx := Border2;
END;
GMny := TopBorder + yOffset;
GMxx := width - GMnx DIV 2 + xOffset;
GMxy := height - CharHeight * 3 + yOffset;
INC(GMnx, xOffset);
Divx := xDiv; SubDivx := xSubDiv; Decx := xDec;
Divy := yDiv; SubDivy := ySubDiv; Decy := yDec;
Scrw := width; Scrh := height;
SetPlotOffset(Plot, 0, 0); (* no initial offset *)
(* No scaling initially *)
xscale := 1.0;
yscale := 1.0;
(* Miscellaneous plot values *)
PlotColour := Colour1; (* default plot colour *)
TextColour := Colour1; (* default text colour *)
GridColour := Colour2; (* default grid colour *)
PlotIs := PlotKind;
GridDrawn := FALSE;
UserLabels := FALSE; (* use internal labels *)
ScatterPlot := FALSE; (* not a scatter plot *)
ZeroLineOn := TRUE; (* zero line enabled *)
CopyOfAPlot := TRUE; (* this is a copy *)
(* Initialize the plot minimum and maximums *)
SetPlotLimits(Plot, xMin, xMax, yMin, yMax);
END;
RETURN TRUE;
END InitOffsetPlot;
PROCEDURE InitPlot(VAR Plot : PlotType;
MainTitle : ARRAY OF CHAR;
PlotKind : PlotKindType;
xMin, xMax, yMin, yMax : REAL;
width, height : INTEGER;
xDiv, yDiv : CARDINAL;
xSubDiv, ySubDiv : CARDINAL;
xDec, yDec : CARDINAL;
NumberOfColours : CARDINAL)
: BOOLEAN;
(* Set up the initial plot characteristics and create
the initial custom graph screen. *)
VAR
Depth : INTEGER;
Screen : ScreenPtr;
BEGIN
(* Create the graphics screen *)
CASE NumberOfColours OF
0..2 : Depth := 1 |
3..4 : Depth := 2 |
5..8 : Depth := 3 |
9..16 : Depth := 4 |
ELSE Depth := 5
END;
Screen := CreateScreen(width, height, Depth,
width > 320, height > 200,
MainTitle);
IF Screen = NIL THEN
RETURN FALSE;
END;
(* Initialize most of the plot variables *)
IF NOT InitOffsetPlot(Plot, PlotKind,
xMin, xMax, yMin, yMax,
width, height, xDiv, yDiv,
xSubDiv, ySubDiv,
xDec, yDec, 0, 0, NIL) THEN
RETURN FALSE;
END;
(* Extract the RastPort drawing surface information *)
Plot^.Screen := Screen;
Plot^.Drawing := ADR(Screen^.RPort);
Plot^.CopyOfAPlot := FALSE; (* this is the original *)
RETURN TRUE;
END InitPlot;
PROCEDURE DonePlot(VAR Plot : PlotType);
(* Close off the plot -- when done *)
BEGIN
IF Plot # NIL THEN
WITH Plot^ DO
IF (Screen # NIL) & NOT CopyOfAPlot THEN
CloseScreen(Plot^.Screen);
END;
END;
DEALLOCATE(Plot, SIZE(Plot^));
Plot := NIL;
END;
END DonePlot;
PROCEDURE PlotZero(Plot : PlotType);
(* Plot the zero line *)
BEGIN
(* emphasize y = 0 line *)
WITH Plot^ DO
IF ZeroLineOn THEN
IF ScatterPlot THEN
SetDrMd(Drawing, Jam2);
ScatterPlot := FALSE;
IF NOT (PlotIs IN YLog) THEN
IntPlotLine(Plot, Colour3, Minx, 0.0, Maxx, 0.0);
END;
ScatterPlot := TRUE;
ELSE
IF NOT (PlotIs IN YLog) THEN
IntPlotLine(Plot, Colour3, Minx, 0.0, Maxx, 0.0);
END;
END;
END;
END;
END PlotZero;
PROCEDURE PlotFx(Plot : PlotType; Fx : PlotFunction);
(* Plot the passed function *)
VAR
lastx, lasty, x, y, int, dx, dy : REAL;
OldMinx, OldMaxx, OldMiny, OldMaxy : REAL;
BEGIN
WITH Plot^ DO
(* save the old plot window dimensions *)
OldMinx := Minx; OldMaxx := Maxx;
OldMiny := Miny; OldMaxy := Maxy;
(* calculate scaled plot window dimensions *)
dx := (Maxx - Minx) / xscale;
dy := (Maxy - Miny) / yscale;
Minx := Minx + dx * (xscale - 1.0) * 0.5;
Maxx := Minx + dx;
Miny := Miny + dy * (yscale - 1.0) * 0.5;
Maxy := Miny + dy;
(* draw the plot grid *)
DrawGrid(Plot);
(* initialize the plot variables *)
lastx := Minx;
lasty := Fx(Minx);
x := lastx;
y := lasty;
IF PlotIs IN PlotSet{Bar, Line} THEN
int := (Maxx - Minx) / FLOAT(Divx);
ELSE
int := (Maxx - Minx) / FLOAT(GMxx - GMnx);
END;
IF ScatterPlot THEN
SetDrMd(Plot^.Drawing, Jam1);
END;
(* go through the range of x values and plot y values *)
REPEAT
IF PlotIs = Bar THEN
PlotBar(Plot, PlotColour, int * 0.25, x, y);
ELSE
IntPlotLine(Plot, PlotColour, lastx, lasty, x, y);
END;
lastx := x;
lasty := y;
x := x + int;
CASE PlotIs OF
LogX :
y := Fx(TenTo(x));
|
LogY :
y := LogOf(Fx(x));
|
Log :
y := LogOf(Fx(TenTo(x)));
|
ELSE
y := Fx(x);
END;
UNTIL x > Maxx;
PlotZero(Plot);
(* restore the old plot window dimensions *)
Minx := OldMinx; Maxx := OldMaxx;
Miny := OldMiny; Maxy := OldMaxy;
END;
END PlotFx;
PROCEDURE SetPlotColour(Plot : PlotType;
Colour : CARDINAL);
(* Change the plot colour *)
BEGIN
Plot^.PlotColour := Colour;
END SetPlotColour;
PROCEDURE SetTextColour(Plot : PlotType;
Colour : CARDINAL);
(* Change the plot colour *)
BEGIN
Plot^.TextColour := Colour;
END SetTextColour;
PROCEDURE SetGridColour(Plot : PlotType;
Colour : CARDINAL);
(* Change the plot colour *)
BEGIN
Plot^.GridColour := Colour;
END SetGridColour;
PROCEDURE SetZeroLine(Plot : PlotType;
On : BOOLEAN);
(* Enable/disable zero line *)
BEGIN
Plot^.ZeroLineOn := On;
END SetZeroLine;
PROCEDURE SetLabelRoutine(Plot : PlotType;
LabelRoutine : LabelProc);
(* Overrides the numerical labels with user-defined
labels for each grid position along the x-axis *)
BEGIN
Plot^.GetLabel := LabelRoutine;
Plot^.UserLabels := TRUE; (* use LabelRoutine *)
END SetLabelRoutine;
PROCEDURE ClearPlot(Plot : PlotType);
(* Clear the plot area *)
BEGIN
WITH Plot^ DO
SetAPen(Drawing, BackColour); (* background colour *)
RectFill(Drawing, GMnx, GMny, GMxx, GMxy);
GridDrawn := FALSE;
END;
END ClearPlot;
PROCEDURE LabelX(Plot : PlotType;
Label : ARRAY OF CHAR;
x, y : INTEGER);
(* Place text horizontally on the plot *)
BEGIN
(* Change bounds of screen to allow text outside plot *)
WITH Plot^ DO
IF (x >= 2) & (x <= Screen^.Width-1) &
(y >= 12) & (y <= Screen^.Height-1) THEN
SetAPen(Drawing, TextColour); (* set text colour *)
Move(Drawing, x, y); (* set text position *)
Text(Drawing, ADR(Label), LengthStr(Label));
END;
END;
END LabelX;
PROCEDURE LabelY(Plot : PlotType;
Label : ARRAY OF CHAR;
x, y : INTEGER);
(* Place text vertically on the plot *)
VAR
ChCnt : CARDINAL;
Str : ARRAY [0..1] OF CHAR;
BEGIN
Str[1] := 0C;
FOR ChCnt := 1 TO LengthStr(Label) DO
Str[0] := Label[ChCnt-1];
LabelX(Plot, Str, x, y);
INC(y, CharHeight);
END;
END LabelY;
PROCEDURE CenterLabelX(Plot : PlotType;
Label : ARRAY OF CHAR;
y : INTEGER);
(* Center the text label horizontally *)
VAR
x : INTEGER;
BEGIN
WITH Plot^ DO
x := (GMxx - GMnx) DIV 2 + GMnx; (* center position *)
DEC(x, LengthStr(Label) * CharWidth DIV 2); (* offset *)
LabelX(Plot, Label, x, y);
END;
END CenterLabelX;
PROCEDURE CenterLabelY(Plot : PlotType;
Label : ARRAY OF CHAR;
x : INTEGER);
(* Center the text label vertically *)
VAR
y : INTEGER;
BEGIN
WITH Plot^ DO
y := (GMxy - GMny) DIV 2 + GMny; (* center position *)
DEC(y, LengthStr(Label) * CharHeight DIV 2);
LabelY(Plot, Label, x, y);
END;
END CenterLabelY;
PROCEDURE LabelMinMax(Plot : PlotType;
Fx : PlotFunction);
(* Label local minima and maxima by tracking whether the
first derivative of Fx changes sign *)
TYPE
SignType = (Positive, Negative);
VAR
sign, signp : SignType;
x, xlog, xplot, y, yp, yplot, delta : REAL;
Gx, Gy : INTEGER;
str : ARRAY [0..40] OF CHAR;
BEGIN
WITH Plot^ DO
x := Minx;
signp := Positive;
IF PlotIs IN PlotSet{Bar, Line} THEN
delta := (Maxx - Minx) / FLOAT(Divx);
ELSE
delta := (Maxx - Minx) / FLOAT(GMxx - GMnx);
END;
CASE PlotIs OF
LogX :
yp := Fx(TenTo(x - delta));
|
LogY :
yp := LogOf(Fx(x - delta));
|
Log :
yp := LogOf(TenTo(Fx(x - delta)));
|
ELSE
yp := Fx(x - delta);
END;
REPEAT
IF PlotIs IN XLog THEN
xlog := TenTo(x);
ELSE
xlog := x;
END;
IF PlotIs IN YLog THEN
y := LogOf(Fx(xlog));
ELSE
y := Fx(xlog);
END;
IF y - yp >= 0.0 THEN
sign := Positive;
ELSE
sign := Negative;
END;
IF (sign # signp) OR
((PlotIs = Bar) & (yp # 0.0)) THEN
(* found a local minima/maxima *)
IF PlotIs IN PlotSet{Bar, Line} THEN
xplot := x - delta;
yplot := yp;
ELSE
xplot := x;
yplot := y;
END;
xyToCoords(Plot, xplot, yplot, Gx, Gy);
IF yplot <= 0.0 THEN
INC(Gy, CharHeight); (* value below curve *)
ELSE
DEC(Gy, CharHeight DIV 3); (* above curve *)
END;
IF ConvRealToStr(str, yplot, 2, Decy+2, ' ') THEN
DEC(Gx, LengthStr(str) * CharWidth DIV 2);
LabelX(Plot, str, Gx, Gy);
END;
signp := sign;
END;
x := x + delta;
yp := y;
UNTIL x > Maxx;
END;
END LabelMinMax;
PROCEDURE InformationBox(Plot : PlotType;
Position : PositionType;
Lines : ARRAY OF LineType;
FillColour : CARDINAL;
OutlineColour : CARDINAL);
(* Place an information box in the plot area *)
VAR
Gx, Gy : INTEGER;
Gxs, Gys : INTEGER;
Gw, Gh : INTEGER;
ChCnt : CARDINAL;
LineCnt : CARDINAL;
OldColour : CARDINAL;
BEGIN
Gxs := 2 * CharWidth; (* side border width *)
Gys := 2 * CharHeight; (* upper/lower border *)
(* Determine the text box width and height *)
Gh := 2 * Gys + INTEGER(HIGH(Lines) + 1) * CharHeight;
Gw := 2 * Gxs;
ChCnt := 0;
FOR LineCnt := 0 TO HIGH(Lines) DO
IF LengthStr(Lines[LineCnt]) > ChCnt THEN
ChCnt := LengthStr(Lines[LineCnt]);
END;
END;
INC(Gw, ChCnt * CharWidth);
(* Determine the text box position *)
WITH Plot^ DO
CASE Position OF
UpperLeft :
Gx := GMnx;
Gy := GMny;
|
UpperMiddle :
Gx := (GMxx - GMnx - Gw) DIV 2 + GMnx;
Gy := GMny;
|
UpperRight :
Gx := GMxx - Gw;
Gy := GMny;
|
LowerLeft :
Gx := GMnx;
Gy := GMxy - Gh;
|
LowerMiddle :
Gx := (GMxx - GMnx - Gw) DIV 2 + GMnx;
Gy := GMxy - Gh;
|
LowerRight :
Gx := GMxx - Gw;
Gy := GMxy - Gh;
|
END;
(* Draw the box *)
SetAPen(Drawing, OutlineColour);
RectFill(Drawing, Gx, Gy, Gx+Gw, Gy+Gh);
SetAPen(Drawing, FillColour);
RectFill(Drawing, Gx+2, Gy+2, Gx+Gw-2, Gy+Gh-2);
(* Output the text strings *)
INC(Gx, Gxs);
INC(Gy, Gys);
SetBPen(Drawing, FillColour); (* set text background *)
FOR LineCnt := 0 TO HIGH(Lines) DO
INC(Gy, CharHeight);
LabelX(Plot, Lines[LineCnt], Gx, Gy);
END;
SetBPen(Drawing, BackColour); (* normal background *)
END;
END InformationBox;
BEGIN
IF OpenRealTrans() THEN
(* dummy statement *)
END;
END PlotLibrary.