unit zplot;
Interface
uses
Graph,zMath; (* NOTE ZMATH IS FURTHER DOWN THIS FILE *)
Type
Dimension = (Xaxis,Yaxis);
InBlock = array[1..8192] of Single ;
const
Margin_L: integer = 85;
Margin_B: integer = 25;
(* Draw semilog Axis *)
Procedure AxisLog(Dir : Dimension;
Mmin,Mmax :Single ;
Plot_L,Plot_R,Plot_T,Plot_B: integer;
AxisLabel : string);
(* Draw a LINEAR axis *)
Procedure AxisLin(Dir : Dimension;
Mmin,Mmax :Single ;
Plot_L,Plot_R,Plot_T,Plot_B: integer;
AxisLabel : string);
(* Plot data on a Linear/Linear graph *)
Procedure PlotLin(VAR Data: inblock; (* data to be plotted *)
N: integer; (* number of data points *)
var Ymin, Ymax: Single ; (* info for drawing axis *)
Plot_L,Plot_R,Plot_T,Plot_B: integer);
(* Plot data on a Log/Linear graph *)
Procedure PlotSemi(Data : InBlock;
dX : Single;
N : Integer;
Plot_L,Plot_R,Plot_T,Plot_B : integer);
Procedure EndGraph;
Implementation
Procedure EndGraph;
Begin
CloseGraph
End;
(* Draw semilog Axis *)
Procedure AxisLog(Dir : Dimension;
Mmin,Mmax :Single ;
Plot_L,Plot_R,Plot_T,Plot_B: integer;
AxisLabel : string);
(* Draw semilog Axis *)
(* This proceedure will draw a semilog axis to fit the points of a
function begining at Mmin and ending at Mmax, according to
plot specifications. *)
(* Plot_L : integer; left edge in viewport
Plot_R : integer; right edge in viewport
Plot_T : integer; top edge in viewport
Plot_B : integer; Bottom edge in viewport
Mmin : Single ; smallest axis point, must be positive
Mmax : Single ; largest axis point, also >=0
Dir : enumerated On which dimension to generate the axis *)
var
LastTick : Single ; (* Right-most tick on the plot *)
Tick : Single ; (* Plot tick marks *)
DeltaTick : Single ; (* space between tick marks *)
DeltaTick15 : Single ; (* Sense new decade *)
Scale : Single ; (* scaler used in mapping to Plot dim *)
LnMmax : Single ; (* Log of last data point *)
TicMap : integer;(* Tick mapped to Plot *)
TickLabel : string[9]; (* label at tick mark*)
Begin
plot_l := plot_l + Margin_L; (* leave room for label *)
plot_b := plot_b - Margin_B; (* leave room for label *)
(* Computing the nearest tick mark for the last point of the plot.
Tick marks are at 1 2 3 4 5 6 7 8 9 10 times powers of 10.
Last tick mark is computed as follows:
shift decimal point between first and second significant digit
take the integer part. *)
(* Get the nearest power of 10 less than Mmax, initial DeltaTick *)
DeltaTick := Exp10(Floor(Log10(Mmax)));
(* Divide into Mmax to get nearest integer < *)
LastTick := Int(Mmax/DeltaTick);
If LastTick = 1.0 then
Begin (* If last tick at 1 then must be a solid line *)
SetLineStyle(SolidLn,0,NormWidth);
LastTick := LastTick*DeltaTick; (* restore power of 10 *)
DeltaTick := 0.1*DeltaTick; (* DeltaTick is for next decade *)
End
else
Begin
SetLineStyle(DottedLn,0,NormWidth);
LastTick := LastTick*DeltaTick; (* restore power of 10 *)
end;
LnMmax := Ln(Mmax);(* part of map to plot, save the calc in loop *)
(* When scaling the axis points to the plot dimensions, the
following is required *)
If Dir = Xaxis then
Scale := (Plot_R - Plot_L)/(Ln(Mmax/Mmin))
else
Scale := (Plot_B - Plot_T)/(Ln(Mmax/Mmin));
(* Generate tic marks from LastTick to Mmin *)
Tick := LastTick;
(* DeltaTick15 is 1.5 times DeltaTick, used to determine if tick
has gone to the next decade *)
DeltaTick15 := 1.5*DeltaTick;
While Tick >= Mmin Do (* Generate tick marks from LastTick to Mmin *)
Begin
If Dir = Xaxis then
Begin
(* Tick point mapped to Plot dimensions *)
TicMap := Plot_R-Round((LnMmax-Ln(Tick))*Scale);
(* Draw line at Tick mark
Assumes graphics has been initialized.
Note the conversion from real to integer above *)
Line(TicMap,Plot_T,TicMap,Plot_B);
Str(Tick:10:2,TickLabel);
OutTextXY(plot_l - Margin_L,ticmap,TickLabel);
End
Else (* Y axis plot *)
Begin
(* Tick point mapped to Plot dimensions *)
TicMap := Plot_T+Round((LnMmax-Ln(Tick))*Scale);
(* Draw line at Tick mark
Assumes graphics has been initialized.
Note the conversion from real to integer above *)
Line(Plot_L,TicMap,Plot_R,TicMap);
Str(Tick:10:2,TickLabel);
OutTextXY(plot_l - Margin_L,ticmap,TickLabel);
End;
Tick := Tick - DeltaTick; (* Decrement tick mark *)
If Tick < DeltaTick15 then (* In next decade ? *)
Begin (* Yes *)
DeltaTick := 0.1*DeltaTick; (* New DeltaTick *)
DeltaTick15 := 1.5*DeltaTick; (* New DeltaTick15 *)
SetLineStyle(SolidLn,0,NormWidth); (* solid line *)
End
else
SetLineStyle(DottedLn,0,NormWidth);(* dotted line *)
End;
(* Now draw at the min and max points with a thick linetype *)
SetLineStyle(SolidLn,0,ThickWidth); (* thick solid line *)
If Dir = Xaxis then
Begin
Line(Plot_L,Plot_T,Plot_L,Plot_B);
Line(Plot_R,Plot_T,Plot_R,Plot_B);
End
else (* Y axis *)
Begin
Line(Plot_L,Plot_T,Plot_R,Plot_T);
Line(Plot_L,Plot_B,Plot_R,Plot_B);
End;
(* Now add axis labels *)
If Dir = Xaxis then
Begin
OutTextXY(Plot_L,Plot_B + 15,AxisLabel);
End
else (* Y axis *)
Begin
OutTextXY(0,0,AxisLabel);
End;
end; (* AxisLog *)
Procedure AxisLin(Dir : Dimension;
Mmin,Mmax : Single ;
Plot_L,Plot_R,Plot_T,Plot_B : integer;
AxisLabel : string);
(* Draw a LINEAR axis *)
(* This proceedure will draw a linear axis to fit the points of a
function begining at Mmin and ending at Mmax, according to
plot specifications. *)
(* Plot_L : integer; left edge in viewport
Plot_R : integer; right edge in viewport
Plot_T : integer; top edge in viewport
Plot_B : integer; Bottom edge in viewport
Mmin : Single ; smallest axis point
Mmax : Single ; largest axis point
Dir : enumerated On which dimension to generate the axis *)
var
FirstTick : Single ; (* Left most tick mark *)
LastTick : Single ; (* Right-most tick mark *)
Tick : Single ; (* Plot tick marks *)
DeltaTick : Single ; (* space between tick marks *)
Scale : Single ; (* scaler used in mapping to Plot dim *)
TicMap : integer;(* Tick mapped to Plot *)
TickLabel : string[8];(* label at tick mark *)
Begin
plot_l := plot_l + Margin_L; (* leave room for label *)
plot_b := plot_b - Margin_B; (* leave room for label *)
(* Computing the nearest tick mark for the last point of the plot.
Tick marks are at 0 1 2 3 4 5 6 7 8 9
Last tick mark is computed as follows:
Find x, the power of 10 of the span of the plot.
Tick marks are at the units of this power *)
(* Get the nearest power of 10 of the span, initial DeltaTick *)
DeltaTick := Exp10(Floor(Log10(Mmax-Mmin)));
SetLineStyle(SolidLn,0,NormWidth); (* Linear plot tic marks use solid lines *)
If ((Mmax-Mmin)/DeltaTick) < 1.5 then (* assure at least 2 ticks *)
DeltaTick := 0.1*DeltaTick; (* DeltaTick is finer resolution *)
LastTick := Int(Mmax/DeltaTick)*DeltaTick;
(* When scaling the axis points to the plot dimensions, the
following is required *)
If Dir = Xaxis then
Scale := (Plot_R - Plot_L)/(Mmax-Mmin)
else
Scale := (Plot_B - Plot_T)/(Mmax-Mmin);
(* Generate tic marks from LastTick to Mmin *)
Tick := LastTick;
While Tick >= Mmin Do
Begin
If Dir = Xaxis then
Begin
(* Tick point mapped to Plot dimensions *)
TicMap := Plot_R-Round((Mmax-Tick)*Scale);
(* Draw line at Tick mark and label it
Assumes graphics has been initialized.
Note the conversion from real to integer above *)
Line(TicMap,Plot_T,TicMap,Plot_B);
Str(Tick:10:2,TickLabel);
OutTextXY(ticmap - Margin_B,plot_b + 5, TickLabel);
End
Else (* Y axis plot *)
Begin
(* Tick point mapped to Plot dimensions *)
TicMap := Plot_T+Round((Mmax-Tick)*Scale);
(* Draw line at Tick mark and label it
Assumes graphics has been initialized.
Note the conversion from real to integer above *)
Line(Plot_L,TicMap,Plot_R,TicMap);
Str(Tick:10:2,TickLabel);
OutTextXY(plot_l - Margin_L,ticmap,TickLabel);
End;
Tick := Tick - DeltaTick; (* Decrement tick mark *)
End; (* while *)
(* Now draw at the min and max points with a thick linetype *)
SetLineStyle(SolidLn,0,ThickWidth); (* thick solid line *)
If Dir = Xaxis then
Begin
Line(Plot_L,Plot_T,Plot_L,Plot_B);
Line(Plot_R,Plot_T,Plot_R,Plot_B);
End
else (* Y axis *)
Begin
Line(Plot_L,Plot_T,Plot_R,Plot_T);
Line(Plot_L,Plot_B,Plot_R,Plot_B);
End;
(* Now add axis labels *)
If Dir = Xaxis then
Begin
OutTextXY(Plot_L, Plot_B + 15, AxisLabel);
End
else (* Y axis *)
Begin
OutTextXY(Plot_L - Margin_L, Plot_T - 15, AxisLabel);
End;
End; (* AxisLin *)
Procedure PlotLin(VAR Data: inblock; (* data to be plotted *)
N: integer; (* number of data points *)
var Ymin, Ymax: Single ; (* return to calling program *)
(* info for drawing axis *)
Plot_L,Plot_R,Plot_T,Plot_B: integer);
(* Plot data on a linear linear graph *)
(* This procedure will plot data contained in Data on a graph with x and
y axis both linear. *)
var X,
Vertsize, HorzIncr : Single ;
X1,Y1,X2,Y2,I: integer;
Bot : integer;
begin
SetLineStyle(SolidLn,0,NormWidth);
(* artificially set max and min Y and refine later *)
Ymax := Data[1]; (* maximum value on Y axis *)
Ymin := Data[1]; (* minimum value on Y axis *)
For i := 2 to N Do
begin
if Ymax < Data[i] then Ymax := Data[i];
if Ymin > Data[i] then Ymin := Data[i];
end; (* endfor *)
If Ymin = Ymax Then (* avoid divide by zero *)
Begin
Ymax := Ymax + 1.0 ; (* data will be centered when plotted *)
Ymin := Ymin - 1.0 ;
End;
Vertsize := (Plot_B - Plot_T - Margin_B)/(Ymax - Ymin);
HorzIncr := (Plot_R - Plot_L - Margin_L)/(N - 1.0);
Bot := Plot_B - Margin_B;
Y1 := Bot - round((data[1]-Ymin)*Vertsize);
X1 := Margin_L + Plot_L;
X := 1.0 * X1;
For i := 2 to N Do
begin
Y2 := Bot - round((data[i] - Ymin) * Vertsize);
X := X + HorzIncr;
X2 := Round(X);
line(X1, Y1, X2, Y2);
X1 := X2;
Y1 := Y2;
end; {endfor}
end; {plotlin}
Procedure PlotSemi(Data: inblock; dX: Single; N: integer;
Plot_L,Plot_R,Plot_T,Plot_B: integer);
(* Plot data on a log linear graph *)
(* This procedure will plot data contained in Data on a graph with x linear
and y axis log. X axis goes from 0 to dX * N/2. *)
var maxY,minY,maxX,minX: Single;
Vertsize,Horzsize: Single;
X1,Y1,X2,Y2,i: integer;
begin
SetLineStyle(SolidLn,0,NormWidth);
(* artificially set max and min Y and refine later *)
maxY := Data[1]; (* maximum value on Y axis *)
minY := Data[1]; (* minimum value on Y axis *)
maxX := dX * (N/2);
minX := 0;
For i := 2 to N div 2 do
begin
if maxY < Data[i] then maxY := Data[i];
if minY > Data[i] then minY := Data[i];
end; (* endfor *)
If minY = maxY Then (* avoid divide by zero *)
Begin
maxY := maxY * 1.1 ; (* data will be centered when plotted *)
minY := minY * 0.9 ;
End;
Vertsize := (Plot_B - Plot_T - Margin_B)/Ln(maxY / minY);
Horzsize := (Plot_R - Plot_L - Margin_L)/(maxX - minX);
Y1 := (Plot_B - Plot_T - Margin_B) - round((Ln(data[1])-Ln(minY))*Vertsize);
X1 := Margin_L;
For i := 2 to N div 2 do
begin
Y2 := (Plot_B - Plot_T - Margin_B) - round((Ln(data[i]) - Ln(minY)) * Vertsize);
X2 := Margin_L + round((i-1) * dX * Horzsize);
line(X1, Y1, X2, Y2);
X1 := X2;
Y1 := Y2;
end; {endfor}
end; {plotsemi}
Var
Graphmode : Integer;
Graphdriver : integer;
Begin (* Initialize Graphics *)
Graphdriver := Detect;
InitGraph(Graphdriver, Graphmode, '');
End.
{ What follows is unit zMath, which is just a shortened version of Mathunit }
(* written by Andrew L. Hamm, IBM Corporation, Manassas Virginia 22110 *)
UNIT zMath;
INTERFACE
FUNCTION Floor(X : Single) : Single; (* The next lowest integer *)
FUNCTION Ceiling(X : Single) : Single; (* The next highest integer *)
FUNCTION X_intercept(P1x,P1y,P2x,P2y:Single):Single; (* x intrcpt from P1&P2 *)
FUNCTION Y_intercept(P1x,P1y,P2x,P2y:Single):Single; (* y intrcpt from P1&P2 *)
FUNCTION Log10(X : Single) : Single; (* Base 10 log of x *)
FUNCTION Exp10(X : Single) : Single; (* 10 raised to the x power *)
FUNCTION PwrXY(X, Y : Single) : Single; (* raise x to the power y *)
FUNCTION QDBV(Volts : Single) : Single; (* convert from volts to dB *)
FUNCTION QDBW(Watts : Single) : Single; (* convert from watts to dB *)
FUNCTION QWATTS(DB : Single) : Single; (* convert from dB to watts *)
FUNCTION QVOLTS(DB : Single) : Single; (* convert from dB to volts *)
FUNCTION SINH(X : Single) : Single; (* hyperbolic sine *)
FUNCTION COSH(X : Single) : Single; (* hyperbolic cosine *)
FUNCTION TANH(X : Single) : Single; (* hyperbolic tangent *)
FUNCTION ISINH(X : Single) : Single; (* arc hyperbolic sine *)
FUNCTION ICOSH(X : Single) : Single; (* arc hyperbolic cosine *)
FUNCTION ITANH(X : Single) : Single; (* arc hyperbolic tangent *)
FUNCTION ARCSIN(X : Single) : Single; (* arc sine using TP arctan function *)
FUNCTION ARCCOS(X : Single) : Single; (* inverse cosine using TP arctan *)
FUNCTION ATAN2(X, Y : Single) : Single; (* arctan function with quadrant check
X is Single axis value or denominator, Y is imaginary axis value or numerator *)
FUNCTION TAN(X : Single) : Single; (* tangent of X *)
FUNCTION GAUSS (Mean, StdDev : Single) : Single; (* gaussian random number *)
FUNCTION RESIDUE(Radix, Number : Single) : Single; (* remainder of number/radix
*)
FUNCTION MINIMUM(A, B : Single) : Single; (* the minimum of a and b *)
FUNCTION MAXIMUM(A, B : Single) : Single; (* the maximum of a and b *)
IMPLEMENTATION
VAR
Ln10 : Single;
FUNCTION Floor (X : Single) : Single; (* The next lowest integer *)
(* note that INT will not work when X is a negative number *)
BEGIN
IF X >= 0.0 THEN
Floor := Int(X)
ELSE (* Use int for positive x *)
IF Frac(X) = 0.0 THEN
Floor := X
ELSE (* no shift on exact integer *)
Floor := - Int(1 - X) (* Round away from zero *)
END; (* Floor *)
FUNCTION Ceiling (X : Single) : Single; (* The next highest integer *)
BEGIN
IF X <= 0.0 THEN
Ceiling := Int(X)
ELSE (* Use int for negative x *)
IF Frac(X) = 0.0 THEN
Ceiling := X
ELSE (* no shift on exact integer *)
Ceiling := 1 - Int(- X) (* Shift x to negative *)
END; (* Ceiling *)
FUNCTION X_intercept(P1x,P1y,P2x,P2y:Single):Single; (* x intrcpt from P1&P2 *)
var Minv : Single; (* inverse slope *)
BEGIN
Minv := (P2x - P1x)/(P2y - P1y);
X_intercept := P2x - Minv * P2y
END;
FUNCTION Y_intercept(P1x,P1y,P2x,P2y:Single):Single; (* Y intrcpt from P1&P2 *)
var M : Single; (* slope *)
BEGIN
M := (P2y - P1y)/(P2x - P1x);
Y_intercept := P2y - M * P2x
END;
FUNCTION Log10 (X : Single) : Single; (* Base 10 log of x *)
BEGIN (* Ln(10) supplied for speed *)
Log10 := Ln(X) / Ln10 (* Easily derived *)
END; (* Log10 *)
FUNCTION Exp10 (X : Single) : Single; (* 10 raised to the x power *)
BEGIN (* Ln(10) supplied for speed *)
Exp10 := Exp(X * Ln10) (* easily derived *)
END; (* Exp10 *)
FUNCTION PwrXY (X, Y : Single) : Single; (* raise x to the power y *)
BEGIN
IF (Y = 0.0) THEN
PwrXY := 1.0
ELSE
IF (X <= 0.0) AND (Frac(Y) = 0.0) THEN
IF (Frac(Y / 2)) = 0.0 THEN
PwrXY := Exp(Y * Ln(Abs(X)))
ELSE
PwrXY := - Exp(Y * Ln(Abs(X)))
ELSE
PwrXY := Exp(Y * Ln(X));
END; (* PwrXY *)
FUNCTION QDBV (Volts : Single) : Single; (* convert from volts to dB *)
BEGIN
QDBV := 20.0 * Log10(Volts)
END; (* QDBV *)
FUNCTION QDBW (Watts : Single) : Single; (* convert from watts to dB *)
BEGIN
QDBW := 10.0 * Log10(Watts)
END; (* QDBW *)
FUNCTION QWATTS (DB : Single) : Single; (* convert from dB to watts *)
BEGIN
QWATTS := Exp10(DB / 10.0);
END; (* QWATTS *)
FUNCTION QVOLTS (DB : Single) : Single; (* convert from dB to volts *)
BEGIN
QVOLTS := Exp10(DB / 20.0);
END; (* QVOLTS *)
FUNCTION SINH (X : Single) : Single; (* hyperbolic sine *)
BEGIN
SINH := 0.5 * (Exp(X) - Exp(- X))
END; (* SINH *)
FUNCTION COSH (X : Single) : Single; (* hyperbolic cosine *)
BEGIN
COSH := 0.5 * (Exp(X) + Exp(- X))
END; (* COSH *)
FUNCTION TANH (X : Single) : Single; (* hyperbolic tangent *)
BEGIN
X := Exp(2.0 * X);
TANH := (X - 1.0) / (X + 1.0)
END; (* TANH *)
FUNCTION ISINH (X : Single) : Single; (* arc hyperbolic sine *)
BEGIN
ISINH := Ln(Sqrt(1.0 + X * X) + X)
END; (* ISINH *)
FUNCTION ICOSH (X : Single) : Single; (* arc hyperbolic cosine *)
BEGIN
ICOSH := Ln(X + Sqrt(X * X - 1.0))
END; (* ICOSH *)
FUNCTION ITANH (X : Single) : Single; (* arc hyperbolic tangent *)
BEGIN
ITANH := Ln((1.0 + X) / (1.0 - X))
END; (* ITANH *)
FUNCTION ARCSIN (X : Single) : Single; (* arc sine using TP arctan function *)
BEGIN (* answer returned in radians *)
IF X = 1.0 THEN
ARCSIN := Pi / 2.0
ELSE
IF X = - 1.0 THEN
ARCSIN := Pi / - 2.0
ELSE
ARCSIN := Arctan(X / Sqrt(1.0 - Sqr(X)))
END; (* ARCSIN *)
FUNCTION ARCCOS (X : Single) : Single; (* inverse cosine using TP arctan *)
BEGIN (* answer returned in radians *)
IF X = 0.0 THEN
ARCCOS := Pi / 2.0
ELSE
IF X < 0.0 THEN
ARCCOS := Pi - Arctan(Sqrt(1.0 - Sqr(X)) / Abs(X))
ELSE
ARCCOS := Arctan(Sqrt(1.0 - Sqr(X)) / Abs(X))
END; (* ARCCOS *)
FUNCTION ATAN2(X, Y : Single) : Single; (* arctan function with quadrant check
X is Single axis value or denominator, Y is imaginary axis value or numerator *)
BEGIN (* answer returned in radians *)
IF Y <> 0.0 THEN
IF X <> 0.0 THEN (* point not on an axis *)
IF X > 0.0 THEN (* 1st or 4th quadrant use std arctan *)
ATAN2 := Arctan(Y / X)
ELSE
IF Y > 0.0 THEN (* 2nd quadrant *)
ATAN2 := Pi + Arctan(Y / X)
ELSE
ATAN2 := Arctan(Y / X) - Pi (* 3rd quadrant *)
ELSE (* point on the Y axis *)
IF Y >= 0.0 THEN
ATAN2 := Pi / 2.0 (* positive Y axis *)
ELSE
ATAN2 := - Pi / 2.0 (* negative Y axis *)
ELSE (* point on the X axis *)
IF X >= 0.0 THEN
ATAN2 := 0.0 (* positive X axis *)
ELSE
ATAN2 := - Pi (* negative X axis *)
END; (* ATAN2 *)
FUNCTION TAN (X : Single) : Single; (* tangent of X *)
BEGIN
TAN := Sin(X) / Cos(X)
END; (* TAN *)
FUNCTION GAUSS (Mean, StdDev : Single) : Single; (* gaussian random number *)
VAR
I : BYTE; (* index for loop *)
T : Single; (* temporary variable *)
BEGIN (* Based on the central limit theorem *)
T := - 6.0; (* maximum deviation is 6 sigma, remove
the mean first *)
FOR I := 1 TO 12 DO
T := T + Random; (* 12 uniform over 0 to 1 *)
GAUSS := Mean + T * StdDev (* adjust mean and standard deviation *)
END; (* GAUSS *)
FUNCTION RESIDUE (Radix, Number : Single) : Single; (* remainder of
radix/number *)
(* uses APL residue definition *)
BEGIN
RESIDUE := Number - Radix * Floor(Number / Radix)
END; (* RESIDUE *)
FUNCTION MINIMUM (A, B : Single) : Single; (* the minimum of a and b *)
BEGIN
IF A < B THEN
MINIMUM := A
ELSE
MINIMUM := B
END; (* MINIMUM *)
FUNCTION MAXIMUM (A, B : Single) : Single; (* the maximum of a and b *)
BEGIN
IF A < B THEN
MAXIMUM := B
ELSE
MAXIMUM := A
END; (* MAXIMUM *)
BEGIN
Randomize;
Ln10 := Ln(10.0)
END.