home *** CD-ROM | disk | FTP | other *** search
/ Gold Fish 3 / goldfish_volume_3.bin / files / misc / math / fastplot / source / plotlibrary.mod < prev    next >
Encoding:
Modula Implementation  |  1995-03-27  |  30.4 KB  |  1,152 lines

  1. (*********************************************************************)
  2. (*                                                                   *)
  3. (* Module PlotLibrary Copyright © 1994 by Computer Inspirations      *)
  4. (*                                                                   *)
  5. (*********************************************************************)
  6.  
  7. IMPLEMENTATION MODULE PlotLibrary;
  8.  
  9. (* Amiga-specific operating system modules *)
  10. FROM Intuition   IMPORT ScreenPtr, CloseScreen, NewScreen,
  11.                         ScreenFlagSet, CustomScreen,
  12.                         OpenScreen;
  13. FROM Pens        IMPORT SetAPen, SetBPen, Move, Draw,
  14.                         RectFill, SetDrPt, SetDrMd;
  15. FROM Rasters     IMPORT RastPortPtr, Jam1, Jam2;
  16. FROM Text        IMPORT Text;
  17. FROM Views       IMPORT SetRGB4, ViewModeSet, ViewModes;
  18.  
  19. (* General Modula-2 modules *)
  20. FROM MathLib0    IMPORT log, power;
  21. FROM Storage     IMPORT ALLOCATE, DEALLOCATE;
  22. FROM SYSTEM      IMPORT ADR, BYTE;
  23.  
  24. (* M2Sprint-specific module *)
  25. FROM RealSupport IMPORT OpenRealTrans;
  26.  
  27. (* Custom module developed to support PlotLibrary *)
  28. FROM StringUtils IMPORT LengthStr, ConvRealToStr;
  29.  
  30.  
  31. CONST
  32.   BackColour = 0;
  33.   Colour1    = 1;
  34.   Colour2    = 2;
  35.   Colour3    = 3;
  36.  
  37.   CharHeight = 8;
  38.   CharWidth  = 8;
  39.  
  40.  
  41. TYPE
  42.   ClassType = (Left, Right, Top, Bottom);
  43.   ClassSet  = SET OF ClassType;
  44.   PlotSet   = SET OF PlotKindType;
  45.  
  46. CONST
  47.   XLog = PlotSet{LogX, Log};
  48.   YLog = PlotSet{LogY, Log};
  49.  
  50.  
  51. TYPE
  52.   PlotType = POINTER TO PlotRecord;
  53.   PlotRecord =
  54.     RECORD
  55.       Screen                 : ScreenPtr;
  56.       Drawing                : RastPortPtr;
  57.       PlotIs                 : PlotKindType;
  58.       GridColour             : CARDINAL;
  59.       TextColour             : CARDINAL;
  60.       PlotColour             : CARDINAL;
  61.       UserLabels             : BOOLEAN;
  62.       GetLabel               : LabelProc;(* label routine *)
  63.       ScatterPlot            : BOOLEAN;  (* scatter plots *)
  64.       ScatterChar            : ARRAY [0..1] OF CHAR;
  65.       ZeroLineOn             : BOOLEAN;  (* line on/off   *)
  66.       Minx, Miny, Maxx, Maxy : REAL;     (* real coords.  *)
  67.       GMnx, GMny, GMxx, GMxy : INTEGER;  (* screen outline*)
  68.       Scrw, Scrh             : INTEGER;  (* & dimensions. *)
  69.       Divx, Divy,                        (* major divs.   *)
  70.       SubDivx, SubDivy,                  (* minor divs.   *)
  71.       Decx, Decy             : CARDINAL; (* decimal places*)
  72.       xscale, yscale         : REAL;     (* x/y scaling   *)
  73.       xoff, yoff             : INTEGER;  (* plot offsets  *)
  74.       GridDrawn              : BOOLEAN;  (* draw once     *)
  75.       CopyOfAPlot            : BOOLEAN;  (* not original  *)
  76.     END;
  77.  
  78. (*-------------------------------------------------------*)
  79. (* The following are a number of utility routines used   *)
  80. (* only within this module to facilitate the graphing.   *)
  81.  
  82. PROCEDURE CreateScreen(width, height, depth : INTEGER;
  83.                        HighResolution       : BOOLEAN;
  84.                        Interlaced           : BOOLEAN;
  85.                        title                : ARRAY OF CHAR)
  86.                : ScreenPtr;
  87. VAR
  88.   ScreenInfo : NewScreen;
  89.   Views : ViewModeSet;
  90. BEGIN
  91.   Views := ViewModeSet{};
  92.   IF HighResolution THEN INCL(Views, HiRes) END;
  93.   IF Interlaced     THEN INCL(Views, Lace) END;
  94.   WITH ScreenInfo DO
  95.     LeftEdge    := 0;
  96.     TopEdge     := 0;
  97.     Width       := width;
  98.     Height      := height;
  99.     Depth       := depth;
  100.     DetailPen   := BYTE(0);
  101.     BlockPen    := BYTE(1);
  102.     ViewModes   := Views;
  103.     Type        := CustomScreen;
  104.     Font        := NIL;
  105.     DefaultTitle:= NIL;
  106.     Gadgets     := NIL;
  107.     CustomBitMap:= NIL;
  108.     IF title[0] # 0C THEN
  109.       DefaultTitle := ADR(title);
  110.     END;
  111.   END;
  112.  
  113.   RETURN OpenScreen(ADR(ScreenInfo));
  114. END CreateScreen;
  115.  
  116.  
  117. PROCEDURE Round(In : REAL) : INTEGER;
  118. BEGIN
  119.   RETURN TRUNC(In + 0.5);
  120. END Round;
  121.  
  122.  
  123. PROCEDURE TenTo(x : REAL) : REAL;
  124. BEGIN
  125.   RETURN power(10.0, x);
  126. END TenTo;
  127.  
  128.  
  129. PROCEDURE LogOf(x : REAL) : REAL;
  130. BEGIN
  131.   IF x <= 0.0 THEN (* capture illegal values *)
  132.     RETURN 0.0;    (* and return log(1)      *)
  133.   END;
  134.   RETURN log(x);
  135. END LogOf;
  136.  
  137.  
  138. PROCEDURE xyToCoords(Plot       : PlotType;
  139.                      x, y       : REAL;
  140.                      VAR xG, yG : INTEGER);
  141. (* Convert floating point world coordinates to graph space
  142.    coordinates. *)
  143. BEGIN
  144.   WITH Plot^ DO
  145.     xG := Round((x-Minx)*FLOAT(GMxx-GMnx)/(Maxx-Minx))+GMnx;
  146.     yG := GMxy-Round((y-Miny)*FLOAT(GMxy-GMny)/(Maxy-Miny));
  147.   END;
  148. END xyToCoords;
  149.  
  150.  
  151. PROCEDURE CoordsToxy(Plot     : PlotType;
  152.                      xG, yG   : INTEGER;
  153.                      VAR x, y : REAL);
  154. (* Convert graph space coordinates to floating point world
  155.    coordinates. *)
  156. BEGIN
  157.   WITH Plot^ DO
  158.     x := FLOAT(xG-GMnx)*(Maxx-Minx)/FLOAT(GMxx-GMnx)+Minx;
  159.     y := FLOAT(GMxy-yG)*(Maxy-Miny)/FLOAT(GMxy-GMny)+Miny;
  160.   END;
  161. END CoordsToxy;
  162.  
  163.  
  164. PROCEDURE OffsetPoint(Plot : PlotType; VAR x, y : INTEGER);
  165. (* Offset the graph points by the plot amounts *)
  166. BEGIN
  167.   INC(x, Plot^.xoff);
  168.   INC(y, Plot^.yoff);
  169. END OffsetPoint;
  170.  
  171.  
  172. PROCEDURE DrawLine(Plot               : PlotType;
  173.                    Colour             : CARDINAL;
  174.                    Gx1, Gy1, Gx2, Gy2 : INTEGER);
  175. (* Line clipping algorithm : two points which make up a
  176.    line are passed (Gx1, Gy1) and (Gx2, Gy2) to be
  177.    clipped within the Plot space.  NOTE: The Amiga screen
  178.    coordinates have y = 0 at the top of the screen.
  179.  
  180.    From an algorithm published in "Microcomputer Graphics
  181.    for the IBM PC" by Roy E. Myers. *)
  182. VAR
  183.   x, y    : ARRAY [1..2] OF INTEGER;
  184.   xl, xh  : INTEGER;
  185.   yl, yh  : INTEGER;
  186.   Offset  : INTEGER;
  187.   Bound   : ARRAY [1..2] OF ClassSet;
  188.   Discard : BOOLEAN;
  189.   i       : CARDINAL;
  190.  
  191.   PROCEDURE Classify (VAR Boundary : ClassSet;
  192.                       x, y         : INTEGER);
  193.   BEGIN
  194.     Boundary := ClassSet{}; (* assume inside bounds  *)
  195.     WITH Plot^ DO
  196.       IF x < xl THEN
  197.         INCL(Boundary, Left);   (* outside left bounds   *)
  198.       ELSIF x > xh THEN
  199.         INCL(Boundary, Right);  (* outside right bounds  *)
  200.       END;
  201.       IF y > yh THEN
  202.         INCL(Boundary, Bottom); (* outside bottom bounds *)
  203.       ELSIF y < yl THEN
  204.         INCL(Boundary, Top);    (* outside top bounds    *)
  205.       END;
  206.     END;
  207.   END Classify;
  208.  
  209.   PROCEDURE Clip (Boundary : ClassSet);
  210.   VAR
  211.     dy, dx : INTEGER;
  212.   BEGIN
  213.     WITH Plot^ DO
  214.       dy := y[2] - y[1];
  215.       dx := x[2] - x[1];
  216.       IF Left IN Boundary THEN
  217.         x[i] := xl;
  218.         y[i] := y[1] + dy * (xl - x[1]) DIV dx;
  219.       ELSIF Right IN Boundary THEN
  220.         x[i] := xh;
  221.         y[i] := y[1] + dy * (xh - x[1]) DIV dx;
  222.       ELSIF Top IN Boundary THEN
  223.         x[i] := x[1] + dx * (yl - y[1]) DIV dy;
  224.         y[i] := yl;
  225.       ELSIF Bottom IN Boundary THEN
  226.         x[i] := x[1] + dx * (yh - y[1]) DIV dy;
  227.         y[i] := yh;
  228.       END;
  229.     END;
  230.   END Clip;
  231.  
  232.   PROCEDURE PlotLine(x1, y1, x2, y2 : INTEGER);
  233.   (* Draw a clipped line to the screen. *)
  234.   CONST
  235.     HalfHeight = CharHeight DIV 2;
  236.     HalfWidth  = CharWidth  DIV 2;
  237.   BEGIN
  238.     WITH Plot^ DO
  239.       SetAPen(Drawing, Colour);    (* set line colour *)
  240.       IF ScatterPlot THEN
  241.         (* move to the character centre and place char *)
  242.         Move(Drawing, x1-HalfWidth, y1+HalfHeight);
  243.         Text(Drawing, ADR(ScatterChar), 1);
  244.       ELSE
  245.         (* move to 1st pt and draw to 2nd point *)
  246.         Move(Drawing, x1, y1);
  247.         Draw(Drawing, x2, y2);
  248.       END;
  249.     END;
  250.   END PlotLine;
  251.  
  252. BEGIN
  253.   (* Initialize clipping routines *)
  254.   x[1] := Gx1;  y[1] := Gy1;
  255.   x[2] := Gx2;  y[2] := Gy2;
  256.   WITH Plot^ DO
  257.     IF GridDrawn THEN
  258.       Offset := 1;         (* allow the outer grid    *)
  259.     ELSE                   (* lines to be drawn first *)
  260.       Offset := 0;
  261.     END;
  262.     xl := GMnx + Offset;   (* prevent overwriting the *)
  263.     xh := GMxx - Offset;   (* grid boundary           *)
  264.     yl := GMny + Offset;
  265.     yh := GMxy - Offset;
  266.   END;
  267.  
  268.   (* Classify the endpoints *)
  269.   Classify(Bound[1], x[1], y[1]);
  270.   Classify(Bound[2], x[2], y[2]);
  271.  
  272.   (* Perform the line clipping *)
  273.   IF Bound[1] + Bound[2] = ClassSet{} THEN
  274.     (* Within plot area -- just draw the line *)
  275.     PlotLine(x[1], y[1], x[2], y[2]);
  276.   ELSE
  277.     (* Just output a clipped line segment *)
  278.     i := 1;
  279.     REPEAT
  280.       Discard := Bound[1] * Bound[2] # ClassSet{};
  281.       IF NOT Discard THEN
  282.         IF Bound[i] # ClassSet{} THEN
  283.           (* Handle the line clipping *)
  284.           Clip(Bound[i]);
  285.           Classify(Bound[i], x[i], y[i]);
  286.         ELSE
  287.           INC(i);
  288.         END;
  289.       END;
  290.     UNTIL (i > 2) OR Discard;
  291.     IF NOT Discard THEN
  292.       PlotLine(x[1], y[1], x[2], y[2]);  (* Clipped line *)
  293.     END;
  294.   END;
  295. END DrawLine;
  296.  
  297.  
  298. PROCEDURE IntPlotLine(Plot           : PlotType;
  299.                       Colour         : CARDINAL;
  300.                       x1, y1, x2, y2 : REAL);
  301. (* Draw a line between points (x1,y1) and (x2,y2).
  302.  
  303.    This routine accepts log(x) values and displays them
  304.    properly during log plots. *)
  305. VAR
  306.   Gx1, Gy1, Gx2, Gy2 : INTEGER;
  307. BEGIN
  308.   xyToCoords(Plot, x1, y1, Gx1, Gy1);  (* to pixel coords *)
  309.   xyToCoords(Plot, x2, y2, Gx2, Gy2);
  310.   OffsetPoint(Plot, Gx1, Gy1);         (* offset plot *)
  311.   OffsetPoint(Plot, Gx2, Gy2);
  312.   DrawLine(Plot, Colour, Gx1, Gy1, Gx2, Gy2);
  313. END IntPlotLine;
  314.  
  315.  
  316. PROCEDURE PlotLine(Plot           : PlotType;
  317.                    Colour         : CARDINAL;
  318.                    x1, y1, x2, y2 : REAL);
  319. (* Draw a line between points (x1,y1) and (x2,y2).
  320.  
  321.    This routine converts points to logarithmic points
  322.    during log plots to make external appearances
  323.    identical to the caller of this routine. *)
  324. BEGIN
  325.   CASE Plot^.PlotIs OF
  326.     LogX :
  327.       IntPlotLine(Plot, Colour, LogOf(x1), y1,
  328.                                 LogOf(x2), y2);
  329.     |
  330.     LogY :
  331.       IntPlotLine(Plot, Colour, x1, LogOf(y1),
  332.                                 x2, LogOf(y2));
  333.     |
  334.     Log :
  335.       IntPlotLine(Plot, Colour, LogOf(x1), LogOf(y1),
  336.                                 LogOf(x2), LogOf(y2));
  337.     ELSE
  338.       IntPlotLine(Plot, Colour, x1, y1, x2, y2);
  339.   END;
  340. END PlotLine;
  341.  
  342.  
  343. PROCEDURE PlotBar(Plot   : PlotType;
  344.                   Colour : CARDINAL;
  345.                   width  : REAL;
  346.                   x, y   : REAL);
  347. (* Draw a bar centered around x of height y and width
  348.    across. *)
  349. VAR
  350.   Half : REAL;
  351.   Gxl, Gyl, Gxu, Gyu : INTEGER;
  352. BEGIN
  353.   WITH Plot^ DO
  354.     Half := ABS(width) / 2.0;
  355.     IF y >= 0.0 THEN
  356.       xyToCoords(Plot, x-Half, y, Gxl, Gyl);
  357.       xyToCoords(Plot, x+Half, 0.0, Gxu, Gyu);
  358.     ELSE
  359.       xyToCoords(Plot, x-Half, 0.0, Gxl, Gyl);
  360.       xyToCoords(Plot, x+Half, y, Gxu, Gyu);
  361.     END;
  362.  
  363.     IF Gyl # Gyu THEN
  364.       OffsetPoint(Plot, Gxl, Gyl);    (* offset bar *)
  365.       OffsetPoint(Plot, Gxu, Gyu);
  366.       SetAPen(Drawing, Colour);
  367.       RectFill(Drawing, Gxl, Gyl, Gxu, Gyu);
  368.     END;
  369.   END;
  370. END PlotBar;
  371.  
  372.  
  373. PROCEDURE PlotXLabel(Plot : PlotType;
  374.                      int  : INTEGER;
  375.                      x, y : REAL);
  376. (* Place a label on the x-axis *)
  377. VAR
  378.   Gx, Gy : INTEGER;
  379.   xlog   : REAL;
  380.   str    : ARRAY [0..40] OF CHAR;
  381. BEGIN
  382.   WITH Plot^ DO
  383.     (* compensate for log scales along x-axis *)
  384.     IF PlotIs IN XLog THEN
  385.       xlog := TenTo(x);
  386.     ELSE
  387.       xlog := x;
  388.     END;
  389.  
  390.     (* produce the x-axis labels *)
  391.     IF UserLabels THEN
  392.       GetLabel(int, str);   (* call-back to get label   *)
  393.     ELSIF NOT ConvRealToStr(str, xlog, 2, Decx, ' ') THEN
  394.       str := "***";         (* number conversion failed *)
  395.     END;
  396.  
  397.     (* label the axis *)
  398.     xyToCoords(Plot, x, y, Gx, Gy);
  399.     DEC(Gx, LengthStr(str) * CharWidth DIV 2);
  400.     INC(Gy, 5 * CharHeight DIV 4);
  401.     LabelX(Plot, str, Gx, Gy);
  402.   END;
  403. END PlotXLabel;
  404.  
  405.  
  406. PROCEDURE PlotYLabel(Plot : PlotType;
  407.                      x, y : REAL);
  408. (* Place a label on the y-axis *)
  409. VAR
  410.   Gx, Gy : INTEGER;
  411.   ylog   : REAL;
  412.   str    : ARRAY [0..40] OF CHAR;
  413. BEGIN
  414.   WITH Plot^ DO
  415.     (* compensate for log scales along y-axis *)
  416.     IF PlotIs IN YLog THEN
  417.       ylog := TenTo(y);
  418.     ELSE
  419.       ylog := y;
  420.     END;
  421.  
  422.     (* produce the y-axis labels *)
  423.     IF NOT ConvRealToStr(str, ylog, 2, Decy, ' ') THEN
  424.       str := "***";
  425.     END;
  426.     xyToCoords(Plot, x, y, Gx, Gy);
  427.     DEC(Gx, (LengthStr(str) + 1) * CharWidth);
  428.     INC(Gy, CharHeight DIV 4);
  429.     LabelX(Plot, str, Gx, Gy);
  430.   END;
  431. END PlotYLabel;
  432.  
  433.  
  434. PROCEDURE DrawGrid(Plot : PlotType);
  435. (* Create the graph grid. *)
  436. CONST
  437.   DashLine = BITSET{1,3,5,7,9,11,13,15};
  438.   NormLine = BITSET{0..15};
  439. VAR
  440.   vCnt, hCnt : CARDINAL;
  441.   oldx, oldy : INTEGER;
  442.   x, y, xint, yint : REAL;
  443.   ScatterState : BOOLEAN;
  444.  
  445.   PROCEDURE PlotMinorX;
  446.   VAR
  447.     Cnt : CARDINAL;
  448.     xm, xi : REAL;
  449.   BEGIN
  450.     WITH Plot^ DO
  451.       IF SubDivx > 1 THEN
  452.         (* Change to a dashed line *)
  453.         SetDrPt(Drawing, DashLine);
  454.  
  455.         (* Output the minor grid lines *)
  456.         IF PlotIs IN XLog THEN
  457.           xi := TenTo(xint) / FLOAT(SubDivx+1);
  458.           FOR Cnt := 2 TO SubDivx DO
  459.             xm := x + LogOf(FLOAT(Cnt) * xi);
  460.             IntPlotLine(Plot, GridColour, xm, Miny,
  461.                                           xm, Maxy);
  462.           END;
  463.         ELSE
  464.           xi := xint / FLOAT(SubDivx);
  465.           FOR Cnt := 1 TO SubDivx-1 DO
  466.             xm := x + FLOAT(Cnt) * xi;
  467.             IntPlotLine(Plot, GridColour, xm, Miny,
  468.                                           xm, Maxy);
  469.           END;
  470.         END;
  471.  
  472.         (* Back to a normal line *)
  473.         SetDrPt(Drawing, NormLine);
  474.       END;
  475.     END;
  476.   END PlotMinorX;
  477.  
  478.   PROCEDURE PlotMinorY;
  479.   VAR
  480.     Cnt : CARDINAL;
  481.     ym, yi : REAL;
  482.   BEGIN
  483.     WITH Plot^ DO
  484.       IF SubDivy > 1 THEN
  485.         (* Change to a dashed line *)
  486.         SetDrPt(Drawing, DashLine);
  487.  
  488.         (* Output the minor grid lines *)
  489.         IF PlotIs IN YLog THEN
  490.           yi := TenTo(yint) / FLOAT(SubDivy+1);
  491.           FOR Cnt := 2 TO SubDivy DO
  492.             ym := y + LogOf(FLOAT(Cnt) * yi);
  493.             IntPlotLine(Plot, GridColour, Minx, ym,
  494.                                           Maxx, ym);
  495.           END;
  496.         ELSE
  497.           yi := yint / FLOAT(SubDivy);
  498.           FOR Cnt := 1 TO SubDivy-1 DO
  499.             ym := y + FLOAT(Cnt) * yi;
  500.             IntPlotLine(Plot, GridColour, Minx, ym,
  501.                                           Maxx, ym);
  502.           END;
  503.         END;
  504.  
  505.         (* Back to a normal line *)
  506.         SetDrPt(Drawing, NormLine);
  507.       END;
  508.     END;
  509.   END PlotMinorY;
  510.  
  511. BEGIN
  512.   WITH Plot^ DO
  513.     IF GridDrawn THEN RETURN END;
  514.  
  515.     (* Draw the vertical grid lines or label bars *)
  516.     ScatterState := Plot^.ScatterPlot;
  517.     Plot^.ScatterPlot := FALSE;  (* draw lines!     *)
  518.     ClearPlot(Plot);             (* clear plot area *)
  519.     oldx := xoff;
  520.     oldy := yoff;
  521.     SetPlotOffset(Plot, 0, 0);   (* no grid offset  *)
  522.     SetDrMd(Plot^.Drawing, Jam1);
  523.     IF Divx > 0 THEN
  524.       xint := (Maxx - Minx) / FLOAT(Divx);
  525.       FOR vCnt := 0 TO Divx DO
  526.         x := Minx + FLOAT(vCnt) * xint;
  527.         PlotMinorX;
  528.         IntPlotLine(Plot, GridColour, x, Miny, x, Maxy);
  529.         PlotXLabel(Plot, vCnt, x, Miny);
  530.       END;
  531.     END;
  532.  
  533.     (* Draw the horizontal grid lines *)
  534.     IF Divy > 0 THEN
  535.       yint := (Maxy - Miny) / FLOAT(Divy);
  536.       FOR hCnt := 0 TO Divy DO
  537.         y := Miny + FLOAT(hCnt) * yint;
  538.         PlotMinorY;
  539.         IntPlotLine(Plot, GridColour, Minx, y, Maxx, y);
  540.         PlotYLabel(Plot, Minx, y);
  541.       END;
  542.     END;
  543.     SetPlotOffset(Plot, oldx, oldy);   (* user offset *)
  544.     SetDrMd(Plot^.Drawing, Jam2);
  545.     GridDrawn := TRUE;
  546.     Plot^.ScatterPlot := ScatterState;
  547.   END;
  548. END DrawGrid;
  549.  
  550.  
  551. (*-------------------------------------------------------*)
  552. (* The main graphing routines.                           *)
  553.  
  554. PROCEDURE Empty() : PlotType;
  555. (* Used to initialize or pass empty plot parameters *)
  556. BEGIN
  557.   RETURN NIL;
  558. END Empty;
  559.  
  560.  
  561. PROCEDURE SetPlotLimits(Plot                   : PlotType;
  562.                         xMin, xMax, yMin, yMax : REAL);
  563. (* Alter the current plot limits -- useful for overlaying
  564.    plots with different minima/maxima *)
  565. BEGIN
  566.   WITH Plot^ DO
  567.     Miny := yMin;  Maxy := yMax;
  568.     Minx := xMin;  Maxx := xMax;
  569.     IF PlotIs IN XLog THEN
  570.       Minx := LogOf(xMin);
  571.       IF xMax <= 0.0 THEN (* make a log multiple of xMin *)
  572.         Maxx := Minx + FLOAT(Divx);
  573.       ELSE
  574.         Maxx := LogOf(xMax);
  575.       END;
  576.     END;
  577.     IF PlotIs IN YLog THEN
  578.       Miny := LogOf(yMin);
  579.       IF yMax <= 0.0 THEN (* make a log multiple of yMin *)
  580.         Maxy := Miny + FLOAT(Divy);
  581.       ELSE
  582.         Maxy := LogOf(yMax);
  583.       END;
  584.     END;
  585.   END;
  586. END SetPlotLimits;
  587.  
  588.  
  589. PROCEDURE SetPlotOffset(Plot       : PlotType;
  590.                         xoff, yoff : INTEGER);
  591. (* Alter the current plot base position -- useful for
  592.    realigning bar charts or giving three dimensional
  593.    looking graphs *)
  594. BEGIN
  595.   Plot^.xoff := xoff;
  596.   Plot^.yoff := yoff;
  597. END SetPlotOffset;
  598.  
  599.  
  600. PROCEDURE SetPlotScale(Plot           : PlotType;
  601.                        xscale, yscale : REAL);
  602. (* Alter the plot scaling along x- and y-axes *)
  603. BEGIN
  604.   Plot^.xscale := ABS(xscale);
  605.   Plot^.yscale := ABS(yscale);
  606.  
  607.   (* limit to 1/10 reduction *)
  608.   IF Plot^.xscale < 0.1 THEN
  609.     Plot^.xscale := 0.1;
  610.   END;
  611.   IF Plot^.yscale < 0.1 THEN
  612.     Plot^.yscale := 0.1;
  613.   END;
  614. END SetPlotScale;
  615.  
  616.  
  617. PROCEDURE SetColourMap(Plot             : PlotType;
  618.                        ColourIndex      : CARDINAL;
  619.                        Red, Green, Blue : CARDINAL);
  620. (* Change the set of predefined colours for the plots *)
  621. BEGIN
  622.   WITH Plot^ DO
  623.     SetRGB4(ADR(Screen^.VPort),ColourIndex,Red,Green,Blue);
  624.   END;
  625. END SetColourMap;
  626.  
  627.  
  628. PROCEDURE SetScatterPlot(Plot  : PlotType);
  629. (* Set to plot single points or a scatter plot *)
  630. BEGIN
  631.   Plot^.ScatterPlot := TRUE;
  632.   Plot^.ScatterChar := "O";   (* default character *)
  633. END SetScatterPlot;
  634.  
  635.  
  636. PROCEDURE SetScatterChar(Plot  : PlotType;
  637.                          Ch    : CHAR);
  638. (* Change the default `.' character for scatter plots *)
  639. BEGIN
  640.   Plot^.ScatterChar[0] := Ch;
  641. END SetScatterChar;
  642.  
  643.  
  644. PROCEDURE InitOffsetPlot
  645.                   (VAR Plot               : PlotType;
  646.                    PlotKind               : PlotKindType;
  647.                    xMin, xMax, yMin, yMax : REAL;
  648.                    width, height          : INTEGER;
  649.                    xDiv, yDiv             : CARDINAL;
  650.                    xSubDiv, ySubDiv       : CARDINAL;
  651.                    xDec, yDec             : CARDINAL;
  652.                    xOffset, yOffset       : INTEGER;
  653.                    OldPlot                : PlotType)
  654.                    : BOOLEAN;
  655. CONST
  656.   TopBorder = 20;
  657. VAR
  658.   Ok      : BOOLEAN;
  659.   Border1 : INTEGER;
  660.   Border2 : INTEGER;
  661.   DumStr  : ARRAY [0..40] OF CHAR;
  662. BEGIN
  663.   ALLOCATE(Plot, SIZE(Plot^));
  664.   IF Plot = NIL THEN
  665.     RETURN FALSE;
  666.   END;
  667.  
  668.  
  669.   WITH Plot^ DO
  670.     (* If OldPlot is defined use screen *)
  671.     IF OldPlot # NIL THEN
  672.       Screen := OldPlot^.Screen;
  673.  
  674.       (* Extract the RastPort drawing surface information *)
  675.       Drawing := ADR(Screen^.RPort);
  676.     END;
  677.  
  678.     (* Initialize the plot variables for later use *)
  679.     IF ConvRealToStr(DumStr, yMax, 2, yDec, ' ') THEN
  680.       Border1 := (LengthStr(DumStr) + 3) * CharWidth;
  681.     END;
  682.     IF ConvRealToStr(DumStr, yMin, 2, yDec, ' ') THEN
  683.       Border2 := (LengthStr(DumStr) + 3) * CharWidth;
  684.     END;
  685.     IF Border1 > Border2 THEN
  686.       GMnx := Border1;
  687.     ELSE
  688.       GMnx := Border2;
  689.     END;
  690.     GMny := TopBorder + yOffset;
  691.     GMxx := width - GMnx DIV 2 + xOffset;
  692.     GMxy := height - CharHeight * 3 + yOffset;
  693.     INC(GMnx, xOffset);
  694.     Divx := xDiv;  SubDivx := xSubDiv; Decx := xDec;
  695.     Divy := yDiv;  SubDivy := ySubDiv; Decy := yDec;
  696.     Scrw := width; Scrh := height;
  697.     SetPlotOffset(Plot, 0, 0);  (* no initial offset   *)
  698.  
  699.     (* No scaling initially *)
  700.     xscale := 1.0;
  701.     yscale := 1.0;
  702.  
  703.     (* Miscellaneous plot values *)
  704.     PlotColour  := Colour1;     (* default plot colour *)
  705.     TextColour  := Colour1;     (* default text colour *)
  706.     GridColour  := Colour2;     (* default grid colour *)
  707.     PlotIs      := PlotKind;
  708.     GridDrawn   := FALSE;
  709.     UserLabels  := FALSE;       (* use internal labels *)
  710.     ScatterPlot := FALSE;       (* not a scatter plot  *)
  711.     ZeroLineOn  := TRUE;        (* zero line enabled   *)
  712.     CopyOfAPlot := TRUE;        (* this is a copy      *)
  713.  
  714.     (* Initialize the plot minimum and maximums *)
  715.     SetPlotLimits(Plot, xMin, xMax, yMin, yMax);
  716.   END;
  717.   RETURN TRUE;
  718. END InitOffsetPlot;
  719.  
  720.  
  721. PROCEDURE InitPlot(VAR Plot               : PlotType;
  722.                    MainTitle              : ARRAY OF CHAR;
  723.                    PlotKind               : PlotKindType;
  724.                    xMin, xMax, yMin, yMax : REAL;
  725.                    width, height          : INTEGER;
  726.                    xDiv, yDiv             : CARDINAL;
  727.                    xSubDiv, ySubDiv       : CARDINAL;
  728.                    xDec, yDec             : CARDINAL;
  729.                    NumberOfColours        : CARDINAL)
  730.                    : BOOLEAN;
  731. (* Set up the initial plot characteristics and create
  732.    the initial custom graph screen. *)
  733. VAR
  734.   Depth  : INTEGER;
  735.   Screen : ScreenPtr;
  736. BEGIN
  737.   (* Create the graphics screen *)
  738.   CASE NumberOfColours OF
  739.     0..2  : Depth := 1 |
  740.     3..4  : Depth := 2 |
  741.     5..8  : Depth := 3 |
  742.     9..16 : Depth := 4 |
  743.     ELSE    Depth := 5
  744.   END;
  745.   Screen := CreateScreen(width, height, Depth,
  746.                          width > 320, height > 200,
  747.                          MainTitle);
  748.   IF Screen = NIL THEN
  749.     RETURN FALSE;
  750.   END;
  751.  
  752.   (* Initialize most of the plot variables *)
  753.   IF NOT InitOffsetPlot(Plot, PlotKind,
  754.                         xMin, xMax, yMin, yMax,
  755.                         width, height, xDiv, yDiv,
  756.                         xSubDiv, ySubDiv,
  757.                         xDec, yDec, 0, 0, NIL) THEN
  758.     RETURN FALSE;
  759.   END;
  760.  
  761.   (* Extract the RastPort drawing surface information *)
  762.   Plot^.Screen := Screen;
  763.   Plot^.Drawing := ADR(Screen^.RPort);
  764.   Plot^.CopyOfAPlot := FALSE; (* this is the original *)
  765.   RETURN TRUE;
  766. END InitPlot;
  767.  
  768.  
  769. PROCEDURE DonePlot(VAR Plot : PlotType);
  770. (* Close off the plot -- when done *)
  771. BEGIN
  772.   IF Plot # NIL THEN
  773.     WITH Plot^ DO
  774.       IF (Screen # NIL) & NOT CopyOfAPlot THEN
  775.         CloseScreen(Plot^.Screen);
  776.       END;
  777.     END;
  778.     DEALLOCATE(Plot, SIZE(Plot^));
  779.     Plot := NIL;
  780.   END;
  781. END DonePlot;
  782.  
  783.  
  784. PROCEDURE PlotZero(Plot : PlotType);
  785. (* Plot the zero line *)
  786. BEGIN
  787.   (* emphasize y = 0 line *)
  788.   WITH Plot^ DO
  789.     IF ZeroLineOn THEN
  790.       IF ScatterPlot THEN
  791.         SetDrMd(Drawing, Jam2);
  792.         ScatterPlot := FALSE;
  793.         IF NOT (PlotIs IN YLog) THEN
  794.           IntPlotLine(Plot, Colour3, Minx, 0.0, Maxx, 0.0);
  795.         END;
  796.         ScatterPlot := TRUE;
  797.       ELSE
  798.         IF NOT (PlotIs IN YLog) THEN
  799.           IntPlotLine(Plot, Colour3, Minx, 0.0, Maxx, 0.0);
  800.         END;
  801.       END;
  802.     END;
  803.   END;
  804. END PlotZero;
  805.  
  806.  
  807. PROCEDURE PlotFx(Plot : PlotType; Fx : PlotFunction);
  808. (* Plot the passed function *)
  809. VAR
  810.   lastx, lasty, x, y, int, dx, dy : REAL;
  811.   OldMinx, OldMaxx, OldMiny, OldMaxy : REAL;
  812. BEGIN
  813.   WITH Plot^ DO
  814.     (* save the old plot window dimensions *)
  815.     OldMinx := Minx;  OldMaxx := Maxx;
  816.     OldMiny := Miny;  OldMaxy := Maxy;
  817.  
  818.     (* calculate scaled plot window dimensions *)
  819.     dx := (Maxx - Minx) / xscale;
  820.     dy := (Maxy - Miny) / yscale;
  821.     Minx := Minx + dx * (xscale - 1.0) * 0.5;
  822.     Maxx := Minx + dx;
  823.     Miny := Miny + dy * (yscale - 1.0) * 0.5;
  824.     Maxy := Miny + dy;
  825.  
  826.     (* draw the plot grid *)
  827.     DrawGrid(Plot);
  828.  
  829.     (* initialize the plot variables *)
  830.     lastx := Minx;
  831.     lasty := Fx(Minx);
  832.     x := lastx;
  833.     y := lasty;
  834.     IF PlotIs IN PlotSet{Bar, Line} THEN
  835.       int := (Maxx - Minx) / FLOAT(Divx);
  836.     ELSE
  837.       int := (Maxx - Minx) / FLOAT(GMxx - GMnx);
  838.     END;
  839.  
  840.     IF ScatterPlot THEN
  841.       SetDrMd(Plot^.Drawing, Jam1);
  842.     END;
  843.  
  844.     (* go through the range of x values and plot y values *)
  845.     REPEAT
  846.       IF PlotIs = Bar THEN
  847.         PlotBar(Plot, PlotColour, int * 0.25, x, y);
  848.       ELSE
  849.         IntPlotLine(Plot, PlotColour, lastx, lasty, x, y);
  850.       END;
  851.       lastx := x;
  852.       lasty := y;
  853.       x := x + int;
  854.       CASE PlotIs OF
  855.         LogX :
  856.           y := Fx(TenTo(x));
  857.           |
  858.         LogY :
  859.           y := LogOf(Fx(x));
  860.           |
  861.         Log :
  862.           y := LogOf(Fx(TenTo(x)));
  863.           |
  864.         ELSE
  865.           y := Fx(x);
  866.       END;
  867.     UNTIL x > Maxx;
  868.     PlotZero(Plot);
  869.  
  870.     (* restore the old plot window dimensions *)
  871.     Minx := OldMinx;  Maxx := OldMaxx;
  872.     Miny := OldMiny;  Maxy := OldMaxy;
  873.   END;
  874. END PlotFx;
  875.  
  876.  
  877. PROCEDURE SetPlotColour(Plot   : PlotType;
  878.                         Colour : CARDINAL);
  879. (* Change the plot colour *)
  880. BEGIN
  881.   Plot^.PlotColour := Colour;
  882. END SetPlotColour;
  883.  
  884.  
  885. PROCEDURE SetTextColour(Plot   : PlotType;
  886.                         Colour : CARDINAL);
  887. (* Change the plot colour *)
  888. BEGIN
  889.   Plot^.TextColour := Colour;
  890. END SetTextColour;
  891.  
  892.  
  893. PROCEDURE SetGridColour(Plot   : PlotType;
  894.                         Colour : CARDINAL);
  895. (* Change the plot colour *)
  896. BEGIN
  897.   Plot^.GridColour := Colour;
  898. END SetGridColour;
  899.  
  900.  
  901. PROCEDURE SetZeroLine(Plot : PlotType;
  902.                       On   : BOOLEAN);
  903. (* Enable/disable zero line *)
  904. BEGIN
  905.   Plot^.ZeroLineOn := On;
  906. END SetZeroLine;
  907.  
  908.  
  909. PROCEDURE SetLabelRoutine(Plot         : PlotType;
  910.                           LabelRoutine : LabelProc);
  911. (* Overrides the numerical labels with user-defined
  912.    labels for each grid position along the x-axis *)
  913. BEGIN
  914.   Plot^.GetLabel   := LabelRoutine;
  915.   Plot^.UserLabels := TRUE;          (* use LabelRoutine *)
  916. END SetLabelRoutine;
  917.  
  918.  
  919. PROCEDURE ClearPlot(Plot : PlotType);
  920. (* Clear the plot area *)
  921. BEGIN
  922.   WITH Plot^ DO
  923.     SetAPen(Drawing, BackColour); (* background colour *)
  924.     RectFill(Drawing, GMnx, GMny, GMxx, GMxy);
  925.     GridDrawn := FALSE;
  926.   END;
  927. END ClearPlot;
  928.  
  929.  
  930. PROCEDURE LabelX(Plot  : PlotType;
  931.                  Label : ARRAY OF CHAR;
  932.                  x, y  : INTEGER);
  933. (* Place text horizontally on the plot *)
  934. BEGIN
  935.   (* Change bounds of screen to allow text outside plot *)
  936.   WITH Plot^ DO
  937.     IF (x >= 2)  & (x <= Screen^.Width-1) &
  938.        (y >= 12) & (y <= Screen^.Height-1) THEN
  939.       SetAPen(Drawing, TextColour); (* set text colour *)
  940.       Move(Drawing, x, y);          (* set text position *)
  941.       Text(Drawing, ADR(Label), LengthStr(Label));
  942.     END;
  943.   END;
  944. END LabelX;
  945.  
  946.  
  947. PROCEDURE LabelY(Plot  : PlotType;
  948.                  Label : ARRAY OF CHAR;
  949.                  x, y  : INTEGER);
  950. (* Place text vertically on the plot *)
  951. VAR
  952.   ChCnt : CARDINAL;
  953.   Str   : ARRAY [0..1] OF CHAR;
  954. BEGIN
  955.   Str[1] := 0C;
  956.   FOR ChCnt := 1 TO LengthStr(Label) DO
  957.     Str[0] := Label[ChCnt-1];
  958.     LabelX(Plot, Str, x, y);
  959.     INC(y, CharHeight);
  960.   END;
  961. END LabelY;
  962.  
  963.  
  964. PROCEDURE CenterLabelX(Plot  : PlotType;
  965.                        Label : ARRAY OF CHAR;
  966.                        y     : INTEGER);
  967. (* Center the text label horizontally *)
  968. VAR
  969.   x : INTEGER;
  970. BEGIN
  971.   WITH Plot^ DO
  972.     x := (GMxx - GMnx) DIV 2 + GMnx;   (* center position *)
  973.     DEC(x, LengthStr(Label) * CharWidth DIV 2); (* offset *)
  974.     LabelX(Plot, Label, x, y);
  975.   END;
  976. END CenterLabelX;
  977.  
  978.  
  979. PROCEDURE CenterLabelY(Plot  : PlotType;
  980.                        Label : ARRAY OF CHAR;
  981.                        x     : INTEGER);
  982. (* Center the text label vertically *)
  983. VAR
  984.   y : INTEGER;
  985. BEGIN
  986.   WITH Plot^ DO
  987.     y := (GMxy - GMny) DIV 2 + GMny;   (* center position *)
  988.     DEC(y, LengthStr(Label) * CharHeight DIV 2);
  989.     LabelY(Plot, Label, x, y);
  990.   END;
  991. END CenterLabelY;
  992.  
  993.  
  994. PROCEDURE LabelMinMax(Plot : PlotType;
  995.                       Fx   : PlotFunction);
  996. (* Label local minima and maxima by tracking whether the
  997.    first derivative of Fx changes sign *)
  998. TYPE
  999.   SignType = (Positive, Negative);
  1000. VAR
  1001.   sign, signp : SignType;
  1002.   x, xlog, xplot, y, yp, yplot, delta : REAL;
  1003.   Gx, Gy : INTEGER;
  1004.   str : ARRAY [0..40] OF CHAR;
  1005. BEGIN
  1006.   WITH Plot^ DO
  1007.     x := Minx;
  1008.     signp := Positive;
  1009.     IF PlotIs IN PlotSet{Bar, Line} THEN
  1010.       delta := (Maxx - Minx) / FLOAT(Divx);
  1011.     ELSE
  1012.       delta := (Maxx - Minx) / FLOAT(GMxx - GMnx);
  1013.     END;
  1014.     CASE PlotIs OF
  1015.       LogX :
  1016.         yp := Fx(TenTo(x - delta));
  1017.         |
  1018.       LogY :
  1019.         yp := LogOf(Fx(x - delta));
  1020.         |
  1021.       Log :
  1022.         yp := LogOf(TenTo(Fx(x - delta)));
  1023.         |
  1024.       ELSE
  1025.         yp := Fx(x - delta);
  1026.     END;
  1027.     REPEAT
  1028.       IF PlotIs IN XLog THEN
  1029.         xlog := TenTo(x);
  1030.       ELSE
  1031.         xlog := x;
  1032.       END;
  1033.       IF PlotIs IN YLog THEN
  1034.         y := LogOf(Fx(xlog));
  1035.       ELSE
  1036.         y := Fx(xlog);
  1037.       END;
  1038.       IF y - yp >= 0.0 THEN
  1039.         sign := Positive;
  1040.       ELSE
  1041.         sign := Negative;
  1042.       END;
  1043.       IF (sign # signp) OR
  1044.          ((PlotIs = Bar) & (yp # 0.0)) THEN
  1045.         (* found a local minima/maxima *)
  1046.         IF PlotIs IN PlotSet{Bar, Line} THEN
  1047.           xplot := x - delta;
  1048.           yplot := yp;
  1049.         ELSE
  1050.           xplot := x;
  1051.           yplot := y;
  1052.         END;
  1053.         xyToCoords(Plot, xplot, yplot, Gx, Gy);
  1054.         IF yplot <= 0.0 THEN
  1055.           INC(Gy, CharHeight);  (* value below curve *)
  1056.         ELSE
  1057.           DEC(Gy, CharHeight DIV 3);  (* above curve *)
  1058.         END;
  1059.         IF ConvRealToStr(str, yplot, 2, Decy+2, ' ') THEN
  1060.           DEC(Gx, LengthStr(str) * CharWidth DIV 2);
  1061.           LabelX(Plot, str, Gx, Gy);
  1062.         END;
  1063.         signp := sign;
  1064.       END;
  1065.       x := x + delta;
  1066.       yp := y;
  1067.     UNTIL x > Maxx;
  1068.   END;
  1069. END LabelMinMax;
  1070.  
  1071.  
  1072. PROCEDURE InformationBox(Plot          : PlotType;
  1073.                          Position      : PositionType;
  1074.                          Lines         : ARRAY OF LineType;
  1075.                          FillColour    : CARDINAL;
  1076.                          OutlineColour : CARDINAL);
  1077. (* Place an information box in the plot area *)
  1078. VAR
  1079.   Gx, Gy    : INTEGER;
  1080.   Gxs, Gys  : INTEGER;
  1081.   Gw, Gh    : INTEGER;
  1082.   ChCnt     : CARDINAL;
  1083.   LineCnt   : CARDINAL;
  1084.   OldColour : CARDINAL;
  1085. BEGIN
  1086.   Gxs := 2 * CharWidth;       (* side border width  *)
  1087.   Gys := 2 * CharHeight;      (* upper/lower border *)
  1088.  
  1089.   (* Determine the text box width and height *)
  1090.   Gh := 2 * Gys + INTEGER(HIGH(Lines) + 1) * CharHeight;
  1091.   Gw := 2 * Gxs;
  1092.   ChCnt := 0;
  1093.   FOR LineCnt := 0 TO HIGH(Lines) DO
  1094.     IF LengthStr(Lines[LineCnt]) > ChCnt THEN
  1095.       ChCnt := LengthStr(Lines[LineCnt]);
  1096.     END;
  1097.   END;
  1098.   INC(Gw, ChCnt * CharWidth);
  1099.  
  1100.   (* Determine the text box position *)
  1101.   WITH Plot^ DO
  1102.     CASE Position OF
  1103.       UpperLeft :
  1104.         Gx := GMnx;
  1105.         Gy := GMny;
  1106.       |
  1107.       UpperMiddle :
  1108.         Gx := (GMxx - GMnx - Gw) DIV 2 + GMnx;
  1109.         Gy := GMny;
  1110.       |
  1111.       UpperRight :
  1112.         Gx := GMxx - Gw;
  1113.         Gy := GMny;
  1114.       |
  1115.       LowerLeft :
  1116.         Gx := GMnx;
  1117.         Gy := GMxy - Gh;
  1118.       |
  1119.       LowerMiddle :
  1120.         Gx := (GMxx - GMnx - Gw) DIV 2 + GMnx;
  1121.         Gy := GMxy - Gh;
  1122.       |
  1123.       LowerRight :
  1124.         Gx := GMxx - Gw;
  1125.         Gy := GMxy - Gh;
  1126.       |
  1127.     END;
  1128.  
  1129.     (* Draw the box *)
  1130.     SetAPen(Drawing, OutlineColour);
  1131.     RectFill(Drawing, Gx, Gy, Gx+Gw, Gy+Gh);
  1132.     SetAPen(Drawing, FillColour);
  1133.     RectFill(Drawing, Gx+2, Gy+2, Gx+Gw-2, Gy+Gh-2);
  1134.  
  1135.     (* Output the text strings *)
  1136.     INC(Gx, Gxs);
  1137.     INC(Gy, Gys);
  1138.     SetBPen(Drawing, FillColour); (* set text background *)
  1139.     FOR LineCnt := 0 TO HIGH(Lines) DO
  1140.       INC(Gy, CharHeight);
  1141.       LabelX(Plot, Lines[LineCnt], Gx, Gy);
  1142.     END;
  1143.     SetBPen(Drawing, BackColour); (* normal background   *)
  1144.   END;
  1145. END InformationBox;
  1146.  
  1147.  
  1148. BEGIN
  1149.   IF OpenRealTrans() THEN
  1150.     (* dummy statement *)
  1151.   END;
  1152. END PlotLibrary.