unit Plotlib;
{ ------------------------------------------------------------------------------
  Graph plotting library V1.0 30/9/96
  30/5/97 ... Brush style/color now restored properly after DrawMarkerShape call
  ------------------------------------------------------------------------------}


interface
uses
    ExtCtrls,Shared,Global, WinTypes, WinProcs, Classes, SysUtils, Graphics,
    Forms, Printers, maths ;

procedure DrawAxes( const Canvas : TCanvas ; var Plot : TPlot ; const xy : TObject ) ;
procedure DefineAxis( const Plot : TPlot ; var Axis : TAxis ;
                      AxisType : char ; const PlotObj : TObject ) ;
procedure DrawTick( const Canvas : TCanvas ; const Axis : TAxis ; xPix,yPix : LongInt ;
                    TickValue : single ; AxisType : string ) ;
procedure DrawLine( const Canvas : TCanvas ; const Plot : TPlot ; const xy : TxyBuf ) ;
procedure DrawMarkers( const Canvas : TCanvas ;
                       const Plot : TPlot ;
                       const xy : TxyBuf ) ;
Procedure DrawMarkerShape( const Canvas : TCanvas ;
                           xPix,yPix : LongInt ;
                           const xy : TxyBuf ) ;
procedure DrawHistogram( const Canvas : TCanvas ; var Plot : TPlot ; Hist : THistBuf ) ;
function TidyNumber( const RawNumber : string ) : string ;
function PrinterPointsToPixels( PointSize : Integer ) : Integer ;
function PrinterCmToPixels( const Axis : string ; cm : single ) : Integer ;
procedure VerticalBar( const Canvas : TCanvas ;
                       const PrChan : TChannel ; ShowLabels : boolean ;
                       BarValue : single ; const BarUnits : string ) ;
procedure HorizontalBar( const Canvas : TCanvas ;
                         const PrChan : TChannel ; ShowLabels : boolean ;
                         BarValue,dt,TScale : single  ; const BarUnits : string ) ;
procedure PrintHorizontalCursor( const Canvas : TCanvas ;
                                 Const Chan : TChannel ; Level : Integer ) ;
implementation

procedure DrawAxes( const Canvas : TCanvas ; var Plot : TPlot ; const xy : TObject ) ;
{ ------------------------------------------------------------------
  Draw a set of X and Y axes
  Canvas = the canvas on which the axes are to be drawn.
  Plot = the record containing the specifications of the axes.
  xy = the X,Y data to be plotted (used for automatically setting axes ranges)
  ------------------------------------------------------------------}
var
   i,xPix,yPix,TickSize : LongInt ;
   x,y,x0,y0,xs,ys,logxs,logys,Temp,yEnd,yNeg,xEnd,xNeg : Single ;
begin

     If Plot.XAxis.AutoRange Then  DefineAxis( Plot, Plot.XAxis, 'X', xy ) ;
     If Plot.YAxis.AutoRange Then  DefineAxis( Plot, Plot.YAxis, 'Y', xy ) ;

     { If logarithmic axes are in use, ensure that ranges are positive and non-zero }

     Plot.XAxis.Lo1 := Plot.XAxis.Lo ;
     Plot.XAxis.Hi1 := Plot.XAxis.Hi ;
     If Plot.XAxis.log Then begin
        If Plot.XAxis.Hi1 <= 0. Then Plot.XAxis.Hi1 := 1. ;
        If Plot.XAxis.Lo1 <= 0. Then Plot.XAxis.Lo1 := Plot.XAxis.Hi1 * 0.01 ;
        Plot.XAxis.Hi1 := log10(Plot.XAxis.Hi1) ;
        Plot.XAxis.Lo1 := log10(Plot.XAxis.Lo1) ;
        end ;

     Plot.YAxis.Lo1 := Plot.YAxis.Lo ;
     Plot.YAxis.Hi1 := Plot.YAxis.Hi ;
     If Plot.YAxis.log Then begin
        If Plot.YAxis.Hi1 <= 0. Then Plot.YAxis.Hi1 := 1. ;
        If Plot.YAxis.Lo1 <= 0. Then Plot.YAxis.Lo1 := Plot.YAxis.Hi1 * 0.01 ;
        Plot.YAxis.Lo1 := log10(Plot.YAxis.Lo1) ;
        Plot.YAxis.Hi1 := log10(Plot.YAxis.Hi1) ;
        end ;

     { Ensure that axes have non-zero ranges and tics }
     If Plot.XAxis.Hi1 = Plot.XAxis.Lo1 Then Plot.XAxis.Hi1 := Plot.XAxis.Lo1 + 1. ;
     If Plot.YAxis.Hi1 = Plot.YAxis.Lo1 Then Plot.YAxis.Hi1 := Plot.YAxis.Lo1 + 1. ;
     if Plot.XAxis.tic <= 0. then
        Plot.xAxis.tic := (Plot.xAxis.Hi1 - Plot.xAxis.Lo1) / 5. ;
     if Plot.yAxis.tic <= 0. then
        Plot.yAxis.tic := (Plot.yAxis.Hi1 - Plot.yAxis.Lo1) / 5. ;

     { Calculate Axes data-->pixel scaling factors }
     Plot.XAxis.Scale := Abs(Plot.Right - Plot.Left) /
                         Abs(Plot.XAxis.Hi1 - Plot.XAxis.Lo1) ;
     Plot.YAxis.Scale := Abs(Plot.Bottom - Plot.Top) /
                         Abs(Plot.YAxis.Hi1 - Plot.YAxis.Lo1) ;

     { Draw X axis }
     If Plot.yAxis.log Then Temp := Plot.YAxis.Lo1
                  Else Temp := MinFlt([MaxFlt([Plot.YAxis.Lo1,0.]),Plot.YAxis.Hi1]) ;

     Plot.XAxis.Position := -Trunc((Temp - Plot.YAxis.Lo1)*Plot.YAxis.Scale )
                            + Plot.Bottom ;
     Canvas.polyline( [ Point( Plot.Left, Plot.XAxis.Position ),
                           Point( Plot.Right,Plot.XAxis.Position ) ] ) ;
     { X Axis label }
     yPix := Plot.Bottom + (5*Canvas.TextHeight(Plot.XAxis.Lab)) div 2 ;
     xPix := ( Plot.Left + Plot.Right
             - Canvas.TextWidth(Plot.XAxis.Lab) ) div 2 ;
     Canvas.TextOut( xPix, yPix, Plot.XAxis.Lab ) ;

     { Draw Y axis }
     If Plot.xAxis.log Then Temp := Plot.XAxis.Lo1
                  Else Temp := MinFlt([MaxFlt([Plot.XAxis.Lo1,0.]),Plot.XAxis.Hi1]);
     Plot.YAxis.Position := trunc( (Temp - Plot.XAxis.Lo1)*Plot.XAxis.Scale )
                            + Plot.Left ;
     Canvas.polyline( [point(Plot.YAxis.Position, Plot.Top),
                          point(Plot.YAxis.Position, Plot.Bottom)]) ;
     { Y Axis label }
     yPix := Plot.Top - Canvas.TextHeight( Plot.YAxis.Lab ) ;
     xPix := Plot.YAxis.Position ;
     Canvas.TextOut( xPix, yPix, Plot.YAxis.Lab ) ;

     { Draw calibration ticks on X-Axis
       ================================}
     TickSize := Canvas.TextHeight('X') ;
     If Plot.XAxis.log Then begin
        { Logarithmic tics on X axis }
        x := Int(Plot.XAxis.Lo1) ;
        if Plot.xAxis.Lo1 > x then x := x - 1. ;

        While x <= Plot.XAxis.Hi1 do begin
              { Small Tics between powers of 10 }
              x0 := antilog10(x) ;
              For i := 2 To 9 do begin
                  xs := x0 * i ;
                  logxs := log10(xs) ;
                  If (xs >= Plot.XAxis.Lo) And (xs <= Plot.XAxis.Hi) Then begin
                     xPix := Trunc( (logxs - Plot.XAxis.Lo1)*Plot.XAxis.Scale )
                             + Plot.Left ;
                     Canvas.polyline( [ point( xPix, Plot.XAxis.Position ),
                            point( xPix, Plot.XAxis.Position + (TickSize div 2) ) ] ) ;
                     end ;
                  end ;
              { Large ticks }
              xPix := Trunc( (x - Plot.XAxis.Lo1)*Plot.XAxis.Scale )+ Plot.Left ;
              DrawTick(Canvas, Plot.XAxis, xPix, Plot.XAxis.Position, antilog10(x), 'X') ;
              x := x + 1. ;
              end ;
        end
     Else begin
        { ** Linear ticks on x axis ** }
        { Note. If axis include zero make ticks always start from there }
        if ( Plot.XAxis.Lo1*Plot.XAxis.Hi1 <= 0. ) then begin
             x := 0. ;
             xEnd := MaxFlt( [Abs(Plot.xAxis.Lo1),Abs(Plot.xAxis.Hi1)] ) ;
             end
        else begin
             x := Plot.xAxis.Lo1 ;
             xEnd := Plot.xAxis.Hi1 ;
             end ;
        While x <= xEnd do begin
             if x <= Plot.xAxis.Hi1 then begin
                 xPix := Trunc( (x - Plot.XAxis.Lo1)*Plot.XAxis.Scale )
                         + Plot.Left ;
                 DrawTick( Canvas, Plot.XAxis, xPix, Plot.XAxis.Position, x, 'X') ;
             end ;
             xNeg := -x ;
             if xNeg >= Plot.xAxis.Lo1 then begin
                 xPix := Trunc( (xNeg - Plot.XAxis.Lo1)*Plot.XAxis.Scale )
                         + Plot.Left ;
                 DrawTick( Canvas, Plot.XAxis, xPix, Plot.XAxis.Position, xNeg, 'X') ;
                 end ;
             x := x + Plot.XAxis.tic ;
             end ;
        end ;

     { Draw calibration ticks on Y-Axis
       ================================}
     TickSize := Canvas.TextWidth('X') ;
     If Plot.YAxis.log Then begin
        { Logarithmic tics on Y axis }
        y  := Int(Plot.YAxis.Lo1) ;
        if Plot.YAxis.Lo1 > y then y := y - 1. ;
        While y <= Plot.YAxis.Hi1 do begin
              { Small Tics between powers of 10 }
              y0 := antilog10(y) ;
              For i := 2 To 9 do begin
                  ys := y0 * i ;
                  logys := log10(ys) ;
                  If (ys >= Plot.YAxis.Lo) And (ys <= Plot.YAxis.Hi) Then begin
                     yPix := Plot.Bottom - Trunc( (logys - Plot.YAxis.Lo1 )
                             * Plot.YAxis.Scale)  ;
                     Canvas.polyline( [ point( Plot.YAxis.Position, yPix ),
                           point( Plot.YAxis.Position - (TickSize div 2), yPix ) ] ) ;
                     end ;
                  end ;
              { Large tics }
              yPix := Plot.Bottom - Trunc( (y - Plot.YAxis.Lo1 ) * Plot.YAxis.Scale)  ;
              DrawTick(Canvas, Plot.YAxis, Plot.YAxis.Position,yPix, antilog10(y), 'Y') ;
              y := y + 1. ;
              end ;
        end
     Else begin
          { Linear ticks on Y axis }
          if ( Plot.YAxis.Lo1*Plot.YAxis.Hi1 < 0. ) then begin
             y := 0. ;
             yEnd := MaxFlt( [Abs(Plot.YAxis.Lo1),Abs(Plot.YAxis.Hi1)] ) ;
             end
          else begin
               y := Plot.YAxis.Lo1 ;
               yEnd := Plot.YAxis.Hi1 ;
               end ;

          While y <= yEnd do begin
                if y <= Plot.YAxis.Hi1 then begin
                   yPix := -Trunc( (y - Plot.YAxis.Lo1 ) * Plot.YAxis.Scale)
                           + Plot.Bottom ;
                   DrawTick(Canvas, Plot.YAxis, Plot.YAxis.Position, yPix, y, 'Y') ;
                   end ;
                yNeg := -y ;
                if YNeg >= Plot.YAxis.Lo1 then begin
                   yPix := -Trunc( (yNeg - Plot.YAxis.Lo1 ) * Plot.YAxis.Scale)
                           + Plot.Bottom ;
                   DrawTick(Canvas, Plot.YAxis, Plot.YAxis.Position, yPix, yNeg, 'Y') ;
                   end ;
                y := y + Plot.YAxis.tic ;
                end ;
          end ;
     End ;

procedure DrawTick( const Canvas : TCanvas ;
                    const Axis : TAxis ;
                    xPix,yPix : LongInt ;
                    TickValue : single ;
                    AxisType : string ) ;
{ Draws a tick line on a graph axis,  and its value near it.
Enter with:
' Canvas =   the canvas on which the tick is to be drawn
' Axis =  the Axis definition record
' xPix =    the X position of the tick
' yPix =    the Y position of the tick
' TickValue = The value of the tick to be displayed
' Axis = 'X' for an X axis tick, 'Y' for a Y axis. }
var
   CharWidth,CharHeight,TickSize,i : LongInt ;
   PowerOfTen : Boolean ;
   TickString,Exponent,Mantissa : string ;
begin
        CharWidth := Canvas.TextWidth('X') ;
        CharHeight := Canvas.TextHeight('X') ;

        {   Get tick value. If this is a logarithmic axis set PowerofTen% = TRUE
        to force the tick to be displayed as a power of ten irrespective of
        its magnitude }

        If Axis.log Then PowerofTen := True
                    else PowerofTen := False ;

        { Turn tick value into string }

        If TickValue = 0. Then begin
             { Zero value }
             TickString := '0' ;
             PowerofTen := False ;
             end
        Else If (Abs(TickValue) <= 999. ) And (Abs(TickValue) >= 0.01 )
             And (PowerofTen = False) Then begin
             { Print values }
             TickString := TidyNumber(Format('%8.3g',[TickValue])) ;
             PowerofTen := False ;
             end
        Else begin
            { Create tick as scientic notation (e.g. 2.E+003 )
              and separate out its mantissa and exponent, i.e.
                   3
             2 x 10 (Note this mode is always used if axis is logarithmic) }

             TickString := Format('%12.1e', [TickValue] ) ;
             i := Pos('E',TickString) ;
             If i > 0 Then begin
                { Extract mantissa part of number }
                Mantissa := Copy( TickString, 1, i-1 ) ;
                Mantissa := TidyNumber(Mantissa ) + 'x10' ;
                If Mantissa = '1.0x10' Then Mantissa := '10' ;
                { Get sign of exponent }
                i := i + 1 ;
                Exponent := Copy(TickString, i, Length(TickString)-i+1 ) ;
                Exponent := IntToStr( ExtractInt( Exponent ) );
                TickString := Mantissa ;

               PowerofTen := True ;
               end ;
            end ;

        { Plot tick line and value }

        If AxisType = 'X' Then begin
           {X axis tick }
            TickSize := CharHeight div 2 ;
            Canvas.polyline( [ Point(xPix,yPix), Point(xPix,yPix+TickSize)]) ;
            Canvas.TextOut( xPix - (Canvas.TextWidth(TickString) div 2),
                               yPix + (CharHeight div 2) + TickSize, TickString ) ;
            if PowerOfTen then Canvas.TextOut(
                               xPix + (Canvas.TextWidth(TickString) div 2),
                               yPix + TickSize, Exponent ) ;
            end
        Else begin
            {Y axis tick }
            TickSize := CharWidth ;
            Canvas.polyline( [ Point(xPix,yPix), Point(xPix-TickSize,yPix)]) ;
            Canvas.TextOut( xPix - TickSize
                               - Canvas.TextWidth(TickString+' '),
                               yPix - (Canvas.TextHeight(TickString) div 2),
                               TickString ) ;
            if PowerOfTen then Canvas.TextOut( xPix - TickSize
                                          - Canvas.TextWidth(' '),
                                          yPix - CharHeight, Exponent ) ;
            End ;
        end ;

procedure DefineAxis( const Plot : TPlot ; var Axis : TAxis ;
                      AxisType : char ; const PlotObj : TObject ) ;

{ Find a suitable range for the selected axis plot (X or Y)
' to plot the data in the polyline type "xyin"
' Enter with:
' xyin = X/Y graph
' Axis = "X" for X axis, "Y" for Y axis
' LogAxis% = TRUE for logarithmic axis }
var
   R,Max,Min,MinPositive,dr,Sign,Range,Start,Step : Single ;
   g,i : LongInt ;
   xy : TxyBuf ;
   Hist : THistBuf ;
begin

     if PlotObj is TxyBuf then begin
        { Object is an X/Y graph }
        xy := TxyBuf(PlotObj) ;
        { Find max./min. range of data }
        Min := MaxSingle ;
        Max := -MaxSingle ;
        MinPositive := MaxSingle ;
        for g := 0 to Plot.NumGraphs-1 do begin
            For i := 0 To xy.NumPoints-1 do begin
                If AxisType = 'X' Then R := xy.x[i]
                                  Else R := xy.y[i] ;
                If R <= Min Then Min := R ;
                If R >= Max Then Max := R ;
                If (R > 0) And (R <= MinPositive) Then MinPositive := R ;
                end ;
            end ;
        end
     else if PlotObj is THistBuf then begin
        { Plot object is a histogram }
        Hist := THistBuf(PlotObj) ;
        if AxisType = 'X' then begin
           Min := Hist.Bin[0].Lo ;
           Max := Hist.Bin[Hist.NumBins-1].Hi ;
           end
        else begin
           for i := 0 to Hist.NumBins-1 do
               if Hist.Bin[i].y > Max then Max := Hist.Bin[i].y ;
           Min := 0. ;
           Max := Hist.yHi ;
           end ;
        end ;

    Axis.Hi := Max ;
    Axis.Lo := Min ;

    { Adjust axis range if Min and Max are same value }
    if Abs(Axis.Hi - Axis.Lo) <= 1E-37 then begin
       if Axis.Hi < 0. then begin
          Axis.Lo := Axis.Lo*2. ;
          Axis.Hi := 0. ;
          end
       else begin
            Axis.Hi := Axis.Hi * 2. ;
            if Axis.Hi = 0. then Axis.Hi := Axis.Hi + 1. ;
            Axis.Lo := 0. ;
            end ;
       end ;

    If Axis.Log = False Then begin  {* Linear axis *}

        { Set upper limit of axis }

        If Abs(Axis.Hi) <= 1E-37 Then
           { If Upper limit is (or is close to zero) set to zero }
           Axis.Hi := 0.
        Else begin
            { Otherwise ... }
            If Axis.Hi < 0. Then begin
               { Make positive for processing }
               Axis.Hi := -Axis.Hi ;
               Sign := -1. ;
               end
            else Sign := 1. ;

            Start := AntiLog10(Int(log10(axis.Hi))) ;
            if Start > Axis.Hi then Start := Start * 0.1 ;
            Step := 1. ;
            While (Step*Start) < Axis.Hi do Step := Step + 1. ;
            Axis.Hi := Start*Step ;
            end ;

        { Set lower limit of axis }

        If Abs(Axis.Lo) <= 1E-37 Then
           { If lower limit is (or is close to zero) set to zero }
           Axis.Lo := 0.
        else begin
                  { Otherwise ... }
            If Axis.Lo < 0. Then begin
               { Make positive for processing }
               Axis.Lo := -Axis.Lo ;
               Sign := -1. ;
               end
            else Sign := 1. ;

            Start := AntiLog10(Int(log10(axis.Lo))) ;
            if Start > Axis.Lo then Start := Start * 0.1 ;
            Step := 1. ;
            While (Step*Start) <= Axis.Lo do Step := Step + 1. ;
            if Sign > 0. then Step := Step - 1. ;
            Axis.Lo := Start*Step*Sign ;
            end ;

        { Use zero as one of the limits, if the range of data points
          is not to narrow. }

        If (Axis.Hi > 0. ) And (Axis.Lo > 0. ) And
           ( Abs( (Axis.Hi - Axis.Lo)/Axis.Hi ) > 0.1 ) Then Axis.Lo := 0.
        Else If (Axis.Hi < 0. ) And (Axis.Lo < 0. ) And
           ( Abs( (Axis.Lo - Axis.Hi)/Axis.Lo ) > 0.1 ) Then Axis.Hi := 0. ;


        { Choose a tick interval ( 1, 2, 2.5, 5 ) }

        Range := Abs(Axis.Hi - Axis.Lo) ;
        Axis.tic := antilog10( Int(log10( Range/5. )) - 1. ) ;
        if Range/Axis.tic > 6. then Axis.tic := Axis.tic*2. ;
        if Range/Axis.tic > 6. then Axis.tic := Axis.tic*2.5 ;
        if Range/Axis.tic > 6. then Axis.tic := Axis.tic*2. ;
        if Range/Axis.tic > 6. then Axis.tic := Axis.tic*2. ;
        if Range/Axis.tic > 6. then Axis.tic := Axis.tic*2.5 ;
        end
    Else begin  { * Logarithmic axis * }

        {Set upper limit }

        dr := log10(Axis.Hi) - Int(log10(Axis.Hi)) ;
        If dr < log10(2) Then Axis.Hi := antilog10(Int(log10(Axis.Hi)) + log10(2. ))
        Else Axis.Hi := antilog10(Int(log10(Axis.Hi)) + 1) ;

        { Set lower limit (note that minimum *positive* value
          is used since log. axes cannot deal with negative numbers) }

        Axis.Lo := MinPositive ;
        dr := log10(Axis.Lo) - Int(log10(Axis.Lo)) ;
        If dr < log10(2) Then Axis.Lo := antilog10(Int(log10(Axis.Lo) - 1. ) + log10(9))
        Else Axis.Lo := antilog10(Int(log10(Axis.Lo))) ;
        Axis.tic := 10. ;
        end ;

    Axis.AutoRange := False ;

    end ;


procedure DrawLine( const Canvas : TCanvas ; const Plot : TPlot ; const xy : TxyBuf ) ;
{ --------------------------------------------
  Draw a line on a graph
  Canvas = Canvas on which line is to be drawn
  Plot = specifies where the plot is on the canvas, axes range etc.
  xy = Contains the data points to be plotted
  ------------------------------------------- }
var
   xPix,yPix,i : LongInt ;
   x,y : single ;
   BadPoint, LineBreak : Boolean ;
begin
     LineBreak := True ;
     for i := 0 to xy.NumPoints-1 do begin

          BadPoint := False ;

         { If X is a log axis ... convert x point to log }
         x := xy.x[i] ;
         if Plot.xAxis.log then begin
            if x > 0. then x:= log10(x)
                      else BadPoint := True ;
            end ;

         { If X coord is outside the axis range ... don't plot it }
         if (x < Plot.xAxis.Lo1 ) or (x > Plot.xAxis.hi1) then BadPoint := True ;

         { If Y is a log axis ... convert y point to log }
         y := xy.y[i] ;
         if Plot.yAxis.log then begin
            if y > 0. then y:= log10(y)
                      else BadPoint := True ;
            end ;

         { If Y coord is outside the axis range ... don't plot it }
         if (y < Plot.yAxis.Lo1 ) or (y > Plot.yAxis.hi1) then BadPoint := True ;

         if not BadPoint then begin
            { Plot line segment }
            xPix :=  Trunc( (x - Plot.xAxis.Lo1)*Plot.xAxis.Scale )
                    + Plot.Left ;
            yPix := -Trunc( (y - Plot.yAxis.Lo1)*Plot.yAxis.Scale )
                    + Plot.Bottom ;
            { Note. If LineBreak set .... don't draw line but move
              to next point }
            if LineBreak then Canvas.MoveTo( xPix, yPix )
                         else Canvas.LineTo( xPix, yPix ) ;
            LineBreak := False ;
            end
         else LineBreak := True ;
         end ;
     end ;

procedure DrawMarkers( const Canvas : TCanvas ;
                       const Plot : TPlot ;
                       const xy : TxyBuf ) ;
{ --------------------------------------------
  Draw a series of markers on a graph
  Canvas = Canvas on which line is to be drawn
  Plot = specifies where the plot is on the canvas, axes range etc.
  xy = Contains the data points to be plotted
  ------------------------------------------- }
var
   xPix,yPix,i : LongInt ;
   x,y : single ;
   BadPoint, LineBreak : Boolean ;
   OldSize : Integer ;
begin

     for i := 0 to xy.NumPoints-1 do begin

          BadPoint := False ;

         { If X is a log axis ... convert x point to log }
         x := xy.x[i] ;
         if Plot.xAxis.log then begin
            if x > 0. then x:= log10(x)
                      else BadPoint := True ;
            end ;

         { If X coord is outside the axis range ... don't plot it }
         if (x < Plot.xAxis.Lo1 ) or (x > Plot.xAxis.hi1) then BadPoint := True ;

         { If Y is a log axis ... convert y point to log }
         y := xy.y[i] ;
         if Plot.yAxis.log then begin
            if y > 0. then y:= log10(y)
                      else BadPoint := True ;
            end ;

         { If Y coord is outside the axis range ... don't plot it }
         if (y < Plot.yAxis.Lo1 ) or (y > Plot.yAxis.hi1) then BadPoint := True ;

         if not BadPoint then begin
            { Plot marker }
            xPix :=  Trunc( (x - Plot.xAxis.Lo1)*Plot.xAxis.Scale )
                    + Plot.Left ;
            yPix := -Trunc( (y - Plot.yAxis.Lo1)*Plot.yAxis.Scale )
                    + Plot.Bottom ;
            DrawMarkerShape( Canvas, xPix, yPix, xy ) ;
            end ;
         end ;
     end ;

procedure DrawMarkerShape( const Canvas : TCanvas ;
                           xPix,yPix : LongInt ;
                           const xy : TxyBuf ) ;
var
   OldColor : TColor ;
   OldStyle : TBrushStyle ;
   HalfSize : LongInt ;
begin
      OldColor := Canvas.Brush.Color ;
      OldStyle := Canvas.Brush.Style ;

     {Marker Size }
     HalfSize := xy.MarkerSize div 2 ;
     { Marker solid or empty }
     if xy.MarkerSolid then Canvas.brush.style := bsSolid
                       else Canvas.brush.style := bsClear ;
     { Marker colour }
     Canvas.brush.color := xy.color ;

     { Draw Marker }
     case xy.MarkerShape of
          SquareMarker :
                Canvas.rectangle( xPix - HalfSize, yPix - HalfSize,
                                     xPix + HalfSize, yPix + HalfSize ) ;
          CircleMarker :
                Canvas.Ellipse( xPix - HalfSize, yPix - HalfSize,
                                   xPix + HalfSize, yPix + HalfSize ) ;
          end ;

    { Restore old brush }
    Canvas.Brush.Color := OldColor ;
    Canvas.Brush.Style := OldStyle ;

    end ;


procedure DrawHistogram( const Canvas : TCanvas ; var Plot : TPlot ; Hist : THistBuf ) ;
{ Plot a histogram }
var
   xPixLo,xPixHi,yPix,i : LongInt ;
   BinLo,BinHi,BinY : Single ;
   OffYAxis,FirstBin : Boolean ;
   OldBrush : TBrush ;
   OldColor : TColor ;
   OldStyle : TBrushStyle ;

begin
     OldColor := Canvas.Brush.Color ;
     OldStyle := Canvas.Brush.Style ;

     Canvas.brush.color := Plot.BinFillColor ;
     Canvas.brush.Style := Plot.BinFillStyle ;

     FirstBin := True ;
     for i := 0 to Hist.NumBins-1 do begin

         BinLo := Hist.Bin[i].Lo  ;
         BinHi := Hist.Bin[i].Hi  ;

         if (BinLo >= Plot.xAxis.Lo1) and (BinHi <= Plot.xAxis.Hi1) then begin

            { Convert X's to logs if log. X axis }
            if Plot.xAxis.Log then begin
               BinLo := log10( BinLo ) ;
               BinHi := log10( BinHi ) ;
               end ;

            { Keep bin Y value to limits of plot, but determine
              whether it has gone off scale }

            OffYAxis := False ;
            BinY := Hist.Bin[i].y ;
            if BinY < Plot.yAxis.Lo then begin
               BinY := Plot.yAxis.Lo ;
               OffYAxis := True ;
               end ;
            if BinY > Plot.yAxis.Hi then begin
               BinY := Plot.yAxis.Hi ;
               OffYAxis := True ;
               end ;

            if Plot.yAxis.Log then BinY := log10(BinY) ;

            { Convert to window coordinates }

            xPixLo := Trunc( (BinLo - Plot.xAxis.Lo1)*Plot.xAxis.Scale )
                 + Plot.Left ;
            xPixHi := Trunc( (BinHi - Plot.xAxis.Lo1)*Plot.xAxis.Scale )
                       + Plot.Left ;
            yPix := -Trunc( (BinY - Plot.yAxis.Lo1)*Plot.yAxis.Scale )
                       + Plot.Bottom ;

            {if Canvas.Brush.Style <> bsClear then}

               Canvas.FillRect( Rect( xPixLo, yPix, xPixHi, Plot.Bottom) ) ;

            { Draw left edge of histogram box }

            if FirstBin then begin
                 Canvas.MoveTo( xPixLo,Plot.Bottom ) ;
                 FirstBin := False ;
                 end ;

            Canvas.lineto( xPixLo,yPix ) ;

            { Draw top of histogram box (but not if bin is off scale) }
            if OffYAxis then Canvas.MoveTo( xPixHi,yPix )
                        else Canvas.LineTo( xPixHi,yPix ) ;

            { Plot right hand edge of bin if all edges of bin are to
              be displayed }
            if Plot.BinBorders then Canvas.lineto( xPixHi,Plot.Bottom ) ;

            end ;
         end ;

     { Make sure right hand edge of last bin is drawn }
     if (BinHi <= Plot.xAxis.Hi1) then Canvas.LineTo( xPixHi,Plot.Bottom ) ;

     Canvas.Brush.Color := OldColor ;
     Canvas.Brush.Style := OldStyle ;

     end ;

function TidyNumber( const RawNumber : string ) : string ;
var
   i0,i1 : LongInt ;
begin
     i0 := 1 ;
     while (RawNumber[i0] = ' ') and (i0 < length(RawNumber)) do
           i0 := i0 + 1 ;
     i1 := length(RawNumber) ;
     while (RawNumber[i1] = ' ') and (i1 > 1) do i1 := i1 - 1 ;
     if i1 >= i0 then TidyNumber := Copy( RawNumber, i0, i1-i0+1 )
                 else TidyNumber := '?' ;
     end ;

function PrinterPointsToPixels( PointSize : Integer ) : Integer ;
var
   PixelsPerInch : single ;
begin

     { Get height and width of page (in mm) and calculate
       the size of a pixel (in cm) }
     PixelsPerInch := GetDeviceCaps( printer.handle, LOGPIXELSX ) ;
     PrinterPointsToPixels := Trunc( (PointSize*PixelsPerInch) / 72. ) ;
     end ;

function PrinterCmToPixels(const Axis : string; cm : single ) : Integer ;
{ -------------------------------------------
  Convert from cm (on printer page) to pixels
  -------------------------------------------}
var
   PixelWidth,PixelHeight : single ;
begin
     { Get height and width of page (in mm) and calculate
       the size of a pixel (in cm) }
     if UpperCase(Axis) = 'H' then begin
        { Printer pixel width (mm) }
        PixelWidth := GetDeviceCaps( printer.handle, HORZSIZE ) ;
        Result := Trunc( ( 10. * cm * printer.pagewidth) / PixelWidth );
        end
     else begin
        { Printer pixel height (mm) }
        PixelHeight := GetDeviceCaps( printer.handle, VERTSIZE ) ;
        Result := Trunc( ( printer.pageheight * 10. * cm )/ PixelHeight ) ;
        end ;
     end ;


procedure VerticalBar( const Canvas : TCanvas ;
                       const PrChan : TChannel ; ShowLabels : boolean ;
                       BarValue : single ; const BarUnits : string ) ;
{ --------------------------------------------------------------------------
  Draw a vertical calibration bar of size <BarValues> in units <BarUnits>
  for the signal channel defined by <PrChan> on to <Canvas>. If <showlabels>
  is false do not print labels.
  --------------------------------------------------------------------------}

var
   Tick : Integer ;
   BarText : string ;
   StartPoint,EndPoint : TPoint ;
begin
     Tick := Canvas.TextWidth( 'X' ) div 2 ;
     if BarValue > 0. then begin

        BarText := format('%.1f %s', [BarValue,BarUnits]) ;
        StartPoint := Point(PrChan.Left -
                                     Canvas.textwidth('XXXXX') - 1,
                                     PrChan.Bottom) ;
        EndPoint := Point( StartPoint.x, StartPoint.y -
                           Trunc( BarValue*PrChan.yScale/PrChan.ADCScale ) ) ;
        Canvas.Polyline( [StartPoint,EndPoint] ) ;

        { Draw ticks at end of bar }
        Canvas.Polyline( [Point(StartPoint.x-Tick,StartPoint.y),
                                Point(StartPoint.x+Tick,StartPoint.y) ]);
        Canvas.Polyline( [Point(EndPoint.x+Tick,EndPoint.y),
                                Point(EndPoint.x-Tick,EndPoint.y) ]);
        if Settings.ShowLabels then begin
           { Print bar label }
           Canvas.TextOut( StartPoint.X -
                           Canvas.TextWidth(BarText) div 2,
                           StartPoint.Y + Canvas.TextHeight(BarText), BarText ) ;
           end ;
        end ;
     end ;

procedure HorizontalBar( const Canvas : TCanvas ;
                         const PrChan : TChannel ; ShowLabels : boolean ;
                         BarValue,dt,TScale : single  ; const BarUnits : string ) ;
{ -------------------------------------------------------------------------
  Draw a horizontal calibration bar of size <BarValue> in units <BarUnits>
  for channel <PrChan> on canvas <Canvas>. <dt> is sampling intervals (secs),
  <TScale> is time scaling (from internal units (always secs) to BarUnits)
  If <ShowLabels> is false don't label bar
  -------------------------------------------------------------------------}
var
   Tick,LineHeight : Integer ;
   StartPoint,EndPoint : TPoint ;
begin
     Tick := Canvas.TextHeight( 'X' ) div 2 ;
     LineHeight := (Canvas.TextHeight('X')*12) div 10 ;

     if BarValue > 0. then begin

        StartPoint := Point( PrChan.Left,PrChan.Bottom + LineHeight) ;
        EndPoint :=   Point( StartPoint.x + Trunc( BarValue*PrChan.xScale/dt ) ,
                             StartPoint.y ) ;

        Canvas.Polyline( [StartPoint,EndPoint] ) ;
        { Ticks at end of bar }
        Canvas.Polyline( [Point(StartPoint.x,StartPoint.y+Tick),
                          Point(StartPoint.x,StartPoint.y-Tick) ]);
        Canvas.Polyline( [Point(EndPoint.x,EndPoint.y+Tick),
                          Point(EndPoint.x,EndPoint.y-Tick) ]);

        { Value of calibration bar (Note s-->ms) }
        if Settings.ShowLabels then
           Canvas.TextOut( StartPoint.X, StartPoint.Y + Canvas.TextHeight('X'),
                           format( '%.1f %s',[BarValue*TScale,BarUnits ] ) ) ;
        end ;
     end ;


procedure PrintHorizontalCursor( const Canvas : TCanvas ;
                                 Const Chan : TChannel ; Level : Integer ) ;
{ Draw dotted horizontal cursor at ADC level 'Level'
  in printer display area defined by  record data channel 'Chan'}
var
   yPix : Integer ;
   OldColor : TColor ;
   OldStyle : TPenStyle ;
   OldMode : TPenMode ;
   OldPenWidth : Integer ;
begin
     with Canvas do begin
          OldColor := pen.color ;
          OldStyle := pen.Style ;
          OldMode := pen.mode ;
          OldPenWidth := Pen.Width ;
          pen.mode := pmMask ;
          pen.style := psDot ;
          pen.Width := 1 ;
          end ;

     yPix := Trunc( Chan.Bottom - (Level - Chan.yMin)*Chan.yScale ) ;
     Canvas.polyline([Point(Chan.Left,yPix),Point(Chan.Right,yPix)]);

     with Canvas do begin
          pen.style := OldStyle ;
          pen.color := OldColor ;
          pen.mode := OldMode ;
          Pen.Width := OldPenWidth ;
          end ;

     end ;




end.

