home *** CD-ROM | disk | FTP | other *** search
/ Chip 2001 September / Chip_2001-09_cd1.bin / zkuste / delphi / kolekce / d12345 / CHEMPLOT.ZIP / TPlot / Axis.pas < prev    next >
Pascal/Delphi Source File  |  2001-05-29  |  66KB  |  2,016 lines

  1. unit Axis;
  2.  
  3. {$I Plot.inc}
  4.  
  5. {-----------------------------------------------------------------------------
  6. The contents of this file are subject to the Q Public License
  7. ("QPL"); you may not use this file except in compliance
  8. with the QPL. You may obtain a copy of the QPL from 
  9. the file QPL.html in this distribution, derived from:
  10.  
  11. http://www.trolltech.com/products/download/freelicense/license.html
  12.  
  13. The QPL prohibits development of proprietary software. 
  14. There is a Professional Version of this software available for this. 
  15. Contact sales@chemware.hypermart.net for more information.
  16.  
  17. Software distributed under the QPL is distributed on an "AS IS" basis,
  18. WITHOUT WARRANTY OF ANY KIND, either expressed or implied. See the QPL for
  19. the specific language governing rights and limitations under the QPL.
  20.  
  21. The Original Code is: Axis.pas, released 12 September 2000.
  22.  
  23. The Initial Developer of the Original Code is Mat Ballard.
  24. Portions created by Mat Ballard are Copyright (C) 1999 Mat Ballard.
  25. Portions created by Microsoft are Copyright (C) 1998, 1999 Microsoft Corp.
  26. All Rights Reserved.
  27.  
  28. Contributor(s): Mat Ballard                 e-mail: mat.ballard@chemware.hypermart.net.
  29.  
  30. Last Modified: 04/09/2001
  31. Current Version: 2.00
  32.  
  33. You may retrieve the latest version of this file from:
  34.  
  35.         http://Chemware.hypermart.net/
  36.  
  37. This work was created with the Project JEDI VCL guidelines:
  38.  
  39.         http://www.delphi-jedi.org/Jedi:VCLVCL
  40.  
  41. in mind. 
  42.  
  43. Purpose:
  44. To implement an Axis component for use by the main TPlot graphing component.
  45.  
  46. Known Issues:
  47.  
  48. History:
  49.  1.01 21 September 2000: fix FontWidth bug in TAxis.Draw
  50.         add LabelText property to TAxis (for columns)
  51. -----------------------------------------------------------------------------}
  52.  
  53. interface
  54.  
  55. uses
  56.   Classes, SysUtils,
  57. {$IFDEF WINDOWS}
  58.   WinTypes, WinProcs,
  59.   Graphics,
  60. {$ENDIF}
  61. {$IFDEF WIN32}
  62.   Windows,
  63.   Graphics,
  64. {$ENDIF}
  65. {$IFDEF LINUX}
  66.   Types,
  67.   QGraphics,
  68. {$ENDIF}
  69.  
  70. {$IFNDEF NO_MATH}
  71.   Math,
  72. {$ENDIF}
  73.   Misc, NoMath, Plotdefs, Titles;
  74.  
  75. {const}
  76.  
  77. type
  78.   TAxisType = (atPrimary, atSecondary, atTertiary);
  79.  
  80.   TLabelFormat = (
  81.     lfGeneral, lfExponent, lfFixed, lfNumber, lfCurrency,
  82.     lfSI, lfPercent,
  83.     lfSeconds, lfMinutes, lfHours, lfDays, lfShortTime, lfShortDate);
  84. {lfGeneral ... lfCurrency are just TFloatFormat.}
  85. {}
  86. {We then add SI and Percentage, then the rest are so that we can display times in various formats.}
  87. {}
  88. {NOTE: SI means the standard SI postfixes: p, n u, m, -, K, M, G, T}
  89.  
  90. {  TOnPositionChangeEvent = procedure(
  91.     Sender: TObject;
  92.     bIntercept: Boolean; did the Intercept change ? or the screen position ?
  93.     var TheIntercept: Single;
  94.     var ThePosition: Integer) of object;}
  95.  
  96. {Begin TAxisLabel declarations ------------------------------------------------}
  97.   TAxisLabel = class(TCaption)
  98.   private
  99.     FDirection: TDirection;
  100.     FDigits: Byte;
  101.     FPrecision: Byte;
  102.     FNumberFormat: TLabelFormat;
  103.  
  104.     {OnChange: TNotifyEvent; is in TRectangle !}
  105.  
  106.     procedure SetDirection(Value: TDirection);
  107.     procedure SetDigits(Value: Byte);
  108.     procedure SetPrecision(Value: Byte);
  109.     procedure SetNumberFormat(Value: TLabelFormat);
  110.  
  111.   protected
  112.  
  113.   public
  114.     Constructor Create(AOwner: TPersistent); override;
  115. {The standard constructor, where standard properties are set.}
  116.     Destructor Destroy; override;
  117. {The standard destructor, where the OnChange event is "freed".}
  118.  
  119.     {procedure Assign(Source: TPersistent); override;}
  120.     procedure AssignTo(Dest: TPersistent); override;
  121.  
  122.   published
  123.     Property Direction: TDirection read FDirection write SetDirection;
  124. {Is the Label Horizontal (X) or Vertical (Y or Y2).}
  125.     Property Digits: Byte read FDigits write SetDigits;
  126. {This (and Precision) control the numeric format of the Axis Labels.
  127.  See the Borland documentation on FloatToStrF for the precise meaning of
  128.  this property, or simply experiment in the IDE designer.}
  129.     Property Precision: Byte read FPrecision write SetPrecision;
  130. {This (and Digits) control the numeric format of the Axis Labels.
  131.  See the Borland documentation on FloatToStrF for the precise meaning of
  132.  this property, or simply experiment in the IDE designer.}
  133.     Property NumberFormat: TLabelFormat read FNumberFormat write SetNumberFormat;
  134. {This property controls how the numbers of the Axis labels are displayed.}
  135.  
  136.   end;
  137.  
  138. {Begin TAxis declarations ---------------------------------------------------}
  139.   TAxis = class(TRectangle)
  140.   private
  141.     FArrowSize: Byte;
  142.     FAutoScale: Boolean;
  143.     FAxisType: TAxisType;
  144.     FDirection: TDirection;
  145.     FIntercept: Single;
  146.     FLabels: TAxisLabel;
  147.     FLabelSeries: TPersistent;
  148.     FLogScale: Boolean;
  149.     FLogSpan: Single;
  150.     FMin: Single;
  151.     FMax: Single;
  152.     FPen: TPen;
  153.     FStepSize: Single;
  154.     FStepStart: Single;
  155.     FSpan: Single;
  156.     FTickMinor: Byte;
  157.     FTickSign: Integer;
  158.     FTickSize: Byte;
  159.     FTickDirection: TOrientation;
  160.     FTickNum: Byte;
  161.     FTitle: TTitle;
  162.     FZoomIntercept: Single;
  163.     FZoomMin: Single;
  164.     FZoomMax: Single;
  165.  
  166.     PrecisionAdded: Integer;
  167.  
  168.     procedure SetupHorizontalEnvelope;
  169.     procedure SetupVerticalEnvelope(StrWidth: Integer);
  170.  
  171.   protected
  172. {Set procedures:}
  173.     procedure SetArrowSize(Value: Byte);
  174.     procedure SetAutoScale(Value: Boolean);
  175.     procedure SetDirection(Value: TDirection);
  176.     procedure SetIntercept(Value: Single);
  177.     procedure SetLogScale(Value: Boolean);
  178.     procedure SetMin(Value: Single);
  179.     procedure SetMax(Value: Single);
  180.     procedure SetPen(Value: TPen);
  181.     procedure SetStepSize(Value: Single);
  182.     procedure SetStepStart(Value: Single);
  183.     procedure SetTickMinor(Value: Byte);
  184.     {procedure SetTickNum(Value: Byte);}
  185.     procedure SetTickSize(Value: Byte);
  186.     procedure SetOrientation(Value: TOrientation);
  187.  
  188.     procedure StyleChange(Sender: TObject); virtual;
  189.     procedure TitleChange(Sender: TObject); virtual;
  190.  
  191.   public
  192.     procedure ReScale;
  193.  
  194.     Property AxisType: TAxisType read FAxisType write FAxisType;
  195. {What sort of axis is this ?}
  196.     Property ZoomIntercept: Single read FZoomIntercept write FZoomIntercept;
  197. {The (old) ZOOMED OUT Intercept in data co-ordinates.}
  198.     Property ZoomMin: Single read FZoomMin write FZoomMin;
  199. {The (old) ZOOMED OUT minimum, Left or Bottom of the Axis, in data co-ordinates.}
  200.     Property ZoomMax: Single read FZoomMax write FZoomMax;
  201. {The (old) ZOOMED OUT maximum, Right or Top of the Axis, in data co-ordinates.}
  202.  
  203.     Constructor Create(AOwner: TPersistent); {$IFDEF DELPHI4_UP}reintroduce;{$ENDIF} {squelch the error message}
  204. {The standard constructor, where sub-components are created, and standard
  205.  properties are set.}
  206.  
  207.  
  208.     Destructor Destroy; override;
  209. {The standard destructor, where sub-components and the OnChange event is "freed".}
  210.  
  211.     procedure Draw(ACanvas: TCanvas); virtual;
  212. {This draws the Axis on the given Canvas.}
  213.     function GetNextXValue(XValue: Single): Single;
  214. {This calculates the next tick point. Used externally by TCustomPlot.DrawGrid}
  215.     function LabelToStrF(Value: Single): String;
  216. {This method converts a number to a string, given the current Labels' NumberFormat.}
  217.     function StrToLabel(Value: String): Single;
  218. {This method converts a string to a number, given the current Labels' NumberFormat.}
  219.     function FofX(X: Single): Integer;
  220. {This converts an X data value to a screen X co-ordinate.}
  221.     function FofY(Y: Single): Integer;
  222. {This converts a Y data value to a screen Y co-ordinate.}
  223.     function XofF(F: Integer): Single;
  224. {This converts a screen X co-ordinate to a X data value.}
  225.     function YofF(F: Integer): Single;
  226. {This converts a screen Y co-ordinate to a Y data value.}
  227.     procedure SetLabelSeries(Value: TPersistent);
  228. {This is called by a series to set the X data as strings.}
  229.     procedure SetMinFromSeries(Value: Single);
  230. {This sets the Min property of the Axis. It is used exclusively by TSeries.}
  231.     procedure SetMaxFromSeries(Value: Single);
  232. {This sets the Max property of the Axis. It is used exclusively by TSeries.
  233.  Exactly how it affects the Axis depends on TPlot.DisplayMode.}
  234.     procedure SetMinMaxFromSeries(AMin, AMax: Single);
  235. {This sets the Min and Max properties of the Axis. It is used exclusively by TSeries.
  236.  Exactly how it affects the Axis depends on TPlot.DisplayMode.}
  237.  
  238.     {procedure Assign(Source: TPersistent); override;}
  239.     procedure AssignTo(Dest: TPersistent); override;
  240.  
  241.   published
  242.     Property ArrowSize: Byte read FArrowSize write SetArrowSize;
  243. {This is the size (in pixels) of the arrowhead on the Axis.}
  244.     Property AutoScale: Boolean read FAutoScale write SetAutoScale default TRUE;
  245. {Do we use the StepSize property or does TPlot work them out ?}
  246.     Property Title: TTitle read FTitle write FTitle;
  247. {The Title on and of the Axis. Note that the Title can be clicked and dragged
  248.  around the Axis.}
  249.     Property Direction: TDirection read FDirection write SetDirection;
  250. {Is the Axis Horizontal (X) or Vertical (Y or Y2).}
  251.     Property Intercept: Single read FIntercept write SetIntercept;
  252. {The intercept of this Axis on the complementary Axis.}
  253.     Property Labels: TAxisLabel read FLabels write FLabels;
  254. {The numerals on the Axis.}
  255.     Property LogScale: Boolean read FLogScale write SetLogScale;
  256. {Is this Axis on a logarithmic scale ?}
  257.     Property Min: Single read FMin write SetMin;
  258. {The minimum, Left or Bottom of the Axis, in data co-ordinates.}
  259.     Property Max: Single read FMax write SetMax;
  260. {The maximum, Right or Top of the Axis, in data co-ordinates.}
  261.     Property Pen: TPen read FPen write SetPen;
  262. {The Pen that the Axis is drawn with.}
  263.     Property StepSize: Single read FStepSize write SetStepSize;
  264. {The interval between tick (and labels) on the Axis.}
  265. {}
  266. {If the axis is a Log Scale, then this is the multiple, not the interval !}
  267.     Property StepStart: Single read FStepStart write SetStepStart;
  268. {The interval between tick (and labels) on the Axis.}
  269.     Property TickMinor: Byte read FTickMinor write SetTickMinor;
  270. {Sets the number of minor ticks between labels.}
  271.     Property TickSize: Byte read FTickSize write SetTickSize;
  272. {The Length of the Ticks, in screen pixels.}
  273.     Property TickDirection: TOrientation read FTickDirection write SetOrientation;
  274. {Are the Ticks to the left or right of the Axis ?}
  275.     {Property TickNum: Byte read FTickNum write SetTickNum;}
  276. {The approximate number of ticks: TPlot recalculates the number of ticks
  277.  depending on the StepSize.}
  278.  
  279.   end;
  280.  
  281.   TAngleAxis = class(TAxis)
  282. {The TAngleAxis class is a TAxis that is at any angle.
  283.  It will be used in the 3D and Polar PlotTypes.}
  284. {Note that the (Left, Top) is now interpreted as the origin}
  285.   private
  286.     FAngle: Word;
  287.     FAngleRadians: Single;
  288.     FLength: Word;
  289.  
  290.     FEndX,
  291.     FEndY: Integer;
  292.     FSin,
  293.     FCos,
  294.     FSinM30,
  295.     FCosM30,
  296.     FSinP30,
  297.     FCosP30: Extended;
  298.   protected
  299.     procedure SetAngle(Value: Word);
  300.     procedure SetLength(Value: Word);
  301.  
  302.   public
  303.     constructor Create(AOwner: TPersistent);
  304.     destructor Destroy; override;
  305.  
  306.     function ClickedOn(iX, iY: Integer): Boolean; override;
  307. {Was this Z Axis clicked on ?}
  308.     procedure Outline(ACanvas: TCanvas);
  309.  
  310.     function dFofZ(Z: Single): TPoint;
  311.  
  312.     procedure Draw(ACanvas: TCanvas); override;
  313.  
  314.   published
  315.     property Angle: Word read FAngle write SetAngle;
  316. {Angle is the angle (in degrees) between the vertical Y Axis, and this axis,
  317.  in a clockwise direction.}
  318.     property Length: Word read FLength write SetLength;
  319. {This is the (screen) length of the axis.}
  320.   end;
  321.  
  322. implementation
  323.  
  324. uses
  325.   Data, Plot;
  326.  
  327. {TAxislabel methods ---------------------------------------------------------}
  328. {Constructor and Destructor:-------------------------------------------------}
  329. {------------------------------------------------------------------------------
  330.   Constructor: TAxisLabel.Create
  331.   Description: standard Constructor
  332.        Author: Mat Ballard
  333.  Date created: 04/25/2000
  334. Date modified: 04/25/2000 by Mat Ballard
  335.       Purpose: sets the Precision and Digits Properties
  336.  Known Issues:
  337.  ------------------------------------------------------------------------------}
  338. Constructor TAxisLabel.Create(AOwner: TPersistent);
  339. begin
  340. {First call the ancestor:}
  341.   inherited Create(AOwner);
  342.  
  343. {Put your own initialisation (memory allocation, etc) here:}
  344.  
  345. {we insert the default values that cannot be "defaulted":}
  346.   FPrecision := 3;
  347.   FDigits := 1;
  348. end;
  349.  
  350. {------------------------------------------------------------------------------
  351.   Destructor: TAxisLabel.Destroy
  352.   Description: standard Destructor
  353.        Author: Mat Ballard
  354.  Date created: 04/25/2000
  355. Date modified: 04/25/2000 by Mat Ballard
  356.       Purpose: frees the OnChange event
  357.  Known Issues:
  358.  ------------------------------------------------------------------------------}
  359. Destructor TAxisLabel.Destroy;
  360. begin
  361.   OnChange := nil;
  362. {Put your de-allocation, etc, here:}
  363.  
  364. {then call ancestor:}
  365.  inherited Destroy;
  366. end;
  367.  
  368. {End Constructor and Destructor:---------------------------------------------}
  369.  
  370. {------------------------------------------------------------------------------
  371.     Procedure: TAxisLabel.Assign
  372.   Description: standard Assign method
  373.        Author: Mat Ballard
  374.  Date created: 07/06/2000
  375. Date modified: 07/06/2000 by Mat Ballard
  376.       Purpose: implements Assign
  377.  Known Issues:
  378.  ------------------------------------------------------------------------------}
  379. {procedure TAxisLabel.Assign(Source: TPersistent);
  380. begin
  381.   inherited Assign(Source);
  382.   FDigits := TAxisLabel(Source).Digits;
  383.   FNumberFormat := TAxisLabel(Source).NumberFormat;
  384.   FPrecision := TAxisLabel(Source).Precision;
  385. end;}
  386.  
  387. {------------------------------------------------------------------------------
  388.     Procedure: TAxisLabel.Assign
  389.   Description: standard Assign method
  390.        Author: Mat Ballard
  391.  Date created: 07/06/2000
  392. Date modified: 07/06/2000 by Mat Ballard
  393.       Purpose: implements Assign
  394.  Known Issues:
  395.  ------------------------------------------------------------------------------}
  396. procedure TAxisLabel.AssignTo(Dest: TPersistent);
  397. begin
  398.   inherited AssignTo(Dest);
  399.   TAxisLabel(Dest).Digits := FDigits;
  400.   TAxisLabel(Dest).NumberFormat := FNumberFormat;
  401.   TAxisLabel(Dest).Precision := FPrecision;
  402. end;
  403.  
  404. {Begin Set Procedures --------------------------------------------------------}
  405. {------------------------------------------------------------------------------
  406.     Procedure: TAxisLabel.SetDirection
  407.   Description: standard property Set procedure
  408.        Author: Mat Ballard
  409.  Date created: 03/25/2001
  410. Date modified: 03/25/2001 by Mat Ballard
  411.       Purpose: sets the Direction Property
  412.  Known Issues:
  413.  ------------------------------------------------------------------------------}
  414. procedure TAxisLabel.SetDirection(Value: TDirection);
  415. begin
  416.   if (FDirection = Value) then exit;
  417.  
  418.   FDirection := Value;
  419.   if Assigned(OnChange) then OnChange(Self);
  420. end;
  421.  
  422. {------------------------------------------------------------------------------
  423.     Procedure: TAxisLabel.SetDigits
  424.   Description: standard property Set procedure
  425.        Author: Mat Ballard
  426.  Date created: 04/25/2000
  427. Date modified: 04/25/2000 by Mat Ballard
  428.       Purpose: sets the Digits Property
  429.  Known Issues:
  430.  ------------------------------------------------------------------------------}
  431. procedure TAxisLabel.SetDigits(Value: Byte);
  432. begin
  433.   if (FDigits = Value) then exit;
  434.  
  435.   if (FDigits > 18) then exit;
  436.  
  437.   case FNumberFormat of
  438.     lfGeneral:  if (FDigits > 4) then exit;
  439.     lfExponent: if (FDigits > 4) then exit;
  440.   end;
  441.   FDigits := Value;
  442.  
  443.   if Assigned(OnChange) then OnChange(Self);
  444. end;
  445.  
  446. {------------------------------------------------------------------------------
  447.     Procedure: TAxisLabel.SetPrecision
  448.   Description: standard property Set procedure
  449.        Author: Mat Ballard
  450.  Date created: 04/25/2000
  451. Date modified: 04/25/2000 by Mat Ballard
  452.       Purpose: sets the Precision Property
  453.  Known Issues:
  454.  ------------------------------------------------------------------------------}
  455. procedure TAxisLabel.SetPrecision(Value: Byte);
  456. begin
  457.   if (FPrecision = Value) then exit;
  458.   if (FPrecision > 7) then exit;
  459.   FPrecision := Value;
  460.  
  461.   if Assigned(OnChange) then OnChange(Self);
  462. end;
  463.  
  464. {------------------------------------------------------------------------------
  465.     Procedure: TAxisLabel.SetNumberFormat
  466.   Description: standard property Set procedure
  467.        Author: Mat Ballard
  468.  Date created: 04/25/2000
  469. Date modified: 04/25/2000 by Mat Ballard
  470.       Purpose: sets the NumberFormat Property
  471.  Known Issues:
  472.  ------------------------------------------------------------------------------}
  473. procedure TAxisLabel.SetNumberFormat(Value: TLabelFormat);
  474. begin
  475.   if (FNumberFormat = Value) then exit;
  476.   FNumberFormat := Value;
  477.   case FNumberFormat of
  478.     lfGeneral:  if (FDigits > 4) then FDigits := 4;
  479.     lfExponent: if (FDigits > 4) then FDigits := 4;
  480.   end;
  481.  
  482.   if Assigned(OnChange) then OnChange(Self);
  483. end;
  484.  
  485. {TAxis methods --------------------------------------------------------------}
  486. {Constructor and Destructor:-------------------------------------------------}
  487. {------------------------------------------------------------------------------
  488.   Constructor: TAxis.Create
  489.   Description: standard Constructor
  490.        Author: Mat Ballard
  491.  Date created: 04/25/2000
  492. Date modified: 04/25/2000 by Mat Ballard
  493.       Purpose: creates the subcomponents and sets various Properties
  494.  Known Issues:
  495.  ------------------------------------------------------------------------------}
  496. Constructor TAxis.Create(AOwner: TPersistent);
  497. begin
  498. {First call the ancestor:}
  499.   inherited Create(AOwner);
  500.  
  501. {Create Pen:}
  502.   FPen := TPen.Create;
  503.   FPen.Color := clRed;
  504.  
  505.   FLabels := TAxisLabel.Create(Self);
  506.   FLabels.OnChange := StyleChange;
  507.   FLabelSeries := nil;
  508.  
  509. {create the Title geometry manager:}
  510.   FTitle := TTitle.Create(Self);
  511.   FTitle.OnChange := StyleChange;
  512.   FTitle.OnCaptionChange := TitleChange;
  513.   FTitle.Caption := 'X-Axis';
  514.   FTitle.Font.Size := MEDIUM_FONT_SIZE;
  515.  
  516.   FArrowSize := 10;
  517.   FAutoScale := TRUE;
  518.   FAxisType := atPrimary;
  519.   SetDirection(drHorizontal);
  520.   FIntercept := 0;
  521.   FMin := 0;
  522.   FMax := 10;
  523.   FTickDirection := orRight;
  524.   FTickSize := 10;
  525.   FTickNum := 5;
  526.   Alignment := taRightJustify;
  527.   Visible := TRUE;
  528.   ReScale;
  529. end;
  530.  
  531. {------------------------------------------------------------------------------
  532.   Destructor: TAxis.Destroy
  533.   Description: standard Destructor
  534.        Author: Mat Ballard
  535.  Date created: 04/25/2000
  536. Date modified: 04/25/2000 by Mat Ballard
  537.       Purpose: frees the subcomponents and the OnChange event
  538.  Known Issues:
  539.  ------------------------------------------------------------------------------}
  540. Destructor TAxis.Destroy;
  541. begin
  542.   OnChange := nil;
  543. {Put your de-allocation, etc, here:}
  544.   FLabels.Free;
  545.   FPen.Free;
  546.   FTitle.Free;
  547.  
  548. {then call ancestor:}
  549.   inherited Destroy;
  550. end;
  551. {End Constructor and Destructor:---------------------------------------------}
  552.  
  553. {------------------------------------------------------------------------------
  554.     Procedure: TAxis.TitleChange
  555.   Description: sets the Name and Label's Name
  556.        Author: Mat Ballard
  557.  Date created: 04/25/2000
  558. Date modified: 04/25/2000 by Mat Ballard
  559.       Purpose: responds to a change in the Title
  560.  Known Issues:
  561.  ------------------------------------------------------------------------------}
  562. procedure TAxis.TitleChange(Sender: TObject);
  563. begin
  564.   if (Pos('xis', FTitle.Caption) > 0) then
  565.   begin
  566.     Name := FTitle.Caption;
  567.     FLabels.Name := FTitle.Caption + ' Labels';
  568.   end
  569.   else
  570.   begin
  571. {Stick Axis in in the names:}
  572.     Name := FTitle.Caption + ' Axis';
  573.     FLabels.Name := FTitle.Caption + ' Axis Labels';
  574.   end;
  575. end;
  576.  
  577. {Begin normal Set Procedures -------------------------------------------------}
  578. {------------------------------------------------------------------------------
  579.     Procedure: TAxis.SetArrowSize
  580.   Description: standard property Set procedure
  581.        Author: Mat Ballard
  582.  Date created: 04/25/2000
  583. Date modified: 04/25/2000 by Mat Ballard
  584.       Purpose: sets the ArrowSize Property
  585.  Known Issues:
  586.  ------------------------------------------------------------------------------}
  587. procedure TAxis.SetArrowSize(Value: Byte);
  588. begin
  589.   if (Value = FArrowSize) then exit;
  590.  
  591.   FArrowSize := Value;
  592.   StyleChange(Self);
  593. end;
  594.  
  595. {------------------------------------------------------------------------------
  596.     Procedure: TAxis.SetAutoScale
  597.   Description: standard property Set procedure
  598.        Author: Mat Ballard
  599.  Date created: 04/25/2000
  600. Date modified: 04/25/2000 by Mat Ballard
  601.       Purpose: sets the AutoScale Property
  602.  Known Issues:
  603.  ------------------------------------------------------------------------------}
  604. procedure TAxis.SetAutoScale(Value: Boolean);
  605. begin
  606.   if (Value = FAutoScale) then exit;
  607.  
  608.   FAutoScale := Value;
  609.   StyleChange(Self);
  610. end;
  611.  
  612. {------------------------------------------------------------------------------
  613.     Procedure: TAxis.SetDirection
  614.   Description: standard property Set procedure
  615.        Author: Mat Ballard
  616.  Date created: 04/25/2000
  617. Date modified: 04/25/2000 by Mat Ballard
  618.       Purpose: sets the Direction Property
  619.  Known Issues:
  620.  ------------------------------------------------------------------------------}
  621. procedure TAxis.SetDirection(Value: TDirection);
  622. begin
  623.   if (Value = FDirection) then exit;
  624.  
  625.   FDirection := Value;
  626.   FTitle.Direction := Value;
  627. {TTitle.SetDirection usually fires the OnChange:}
  628.   if ((not FTitle.Visible) and
  629.       assigned(OnChange) and
  630.       Visible) then OnChange(Self);
  631. end;
  632.  
  633. {------------------------------------------------------------------------------
  634.     Procedure: TAxis.SetIntercept
  635.   Description: standard property Set procedure
  636.        Author: Mat Ballard
  637.  Date created: 04/25/2000
  638. Date modified: 04/25/2000 by Mat Ballard
  639.       Purpose: sets the Intercept virtual Property
  640.  Known Issues:
  641.  ------------------------------------------------------------------------------}
  642. procedure TAxis.SetIntercept(Value: Single);
  643. begin
  644.   if (FIntercept = Value) then exit;
  645.   FIntercept := Value;
  646.   {FAutoScale := FALSE;}
  647.   StyleChange(Self);
  648. end;
  649.  
  650. {------------------------------------------------------------------------------
  651.     Procedure: TAxis.SetLogScale
  652.   Description: standard property Set procedure
  653.        Author: Mat Ballard
  654.  Date created: 04/25/2000
  655. Date modified: 04/25/2000 by Mat Ballard
  656.       Purpose: sets the LogScale Property
  657.  Known Issues:
  658.  ------------------------------------------------------------------------------}
  659. procedure TAxis.SetLogScale(Value: Boolean);
  660. begin
  661.   if (Value = FLogScale) then exit;
  662.   if (Value = TRUE) then
  663.   begin {we are going to a log scale:}
  664.     if (FMin <= 0) then exit;
  665.     if (FMax <= 0) then exit;
  666.   end;
  667.  
  668.   FLogScale := Value;
  669.   ReScale;
  670. end;
  671.  
  672. {------------------------------------------------------------------------------
  673.     Procedure: TAxis.SetMin
  674.   Description: standard property Set procedure
  675.        Author: Mat Ballard
  676.  Date created: 04/25/2000
  677. Date modified: 04/25/2000 by Mat Ballard
  678.       Purpose: sets the Min Property
  679.  Known Issues:
  680.  ------------------------------------------------------------------------------}
  681. procedure TAxis.SetMin(Value: Single);
  682. begin
  683.   if (Value = FMin) then exit;
  684.   if (Value >= FMax) then exit;
  685.   if ((Value <= 0) and (FLogScale)) then exit;
  686.  
  687.   FMin := Value;
  688.   ReScale;
  689. end;
  690.  
  691. {------------------------------------------------------------------------------
  692.     Procedure: TAxis.SetMax
  693.   Description: standard property Set procedure
  694.        Author: Mat Ballard
  695.  Date created: 04/25/2000
  696. Date modified: 04/25/2000 by Mat Ballard
  697.       Purpose: sets the Max Property
  698.  Known Issues:
  699.  ------------------------------------------------------------------------------}
  700. procedure TAxis.SetMax(Value: Single);
  701. begin
  702.   if (Value = FMax) then exit;
  703.   if (Value <= FMin) then exit;
  704.  
  705.   FMax := Value;
  706.   ReScale;
  707. end;
  708.  
  709. {------------------------------------------------------------------------------
  710.     Procedure: TAxis.SetMinFromSeries
  711.   Description: property Setting procedure for calling by a Series
  712.        Author: Mat Ballard
  713.  Date created: 04/25/2000
  714. Date modified: 04/25/2000 by Mat Ballard
  715.       Purpose: sets the Min Property when new data is added to a Series
  716.  Known Issues:
  717.  ------------------------------------------------------------------------------}
  718. procedure TAxis.SetMinFromSeries(Value: Single);
  719. begin
  720.   if (Value >= FMin) then exit;
  721.   if ((Value <= 0) and (FLogScale)) then exit;
  722.  
  723.   FMin := Value;
  724.   Rescale;
  725. end;
  726.  
  727. {------------------------------------------------------------------------------
  728.     Procedure: TAxis.SetMaxFromSeries
  729.   Description: property Setting procedure for calling by a Series
  730.        Author: Mat Ballard
  731.  Date created: 04/25/2000
  732. Date modified: 04/25/2000 by Mat Ballard
  733.       Purpose: sets the Max Property when new data is added to a Series
  734.  Known Issues:
  735.  ------------------------------------------------------------------------------}
  736. procedure TAxis.SetMaxFromSeries(Value: Single);
  737. begin
  738.   if (Value <= FMax) then exit;
  739.  
  740.   FMax := Value;
  741.   if ((TPlot(Owner).DisplayMode = dmRun) and
  742.       (FDirection = drHorizontal)) then
  743.   begin
  744. {We are in a "run", and so we can expect more data with increasing X values.
  745.  Rather than force a complete screen re-draw every time a data point is
  746.  added, we extend the X Axis by 100%:}
  747.     FMax := 2.0 * FMax;
  748.   end;
  749.   Rescale;
  750. end;
  751.  
  752. {------------------------------------------------------------------------------
  753.     Procedure: TAxis.SetMinMaxFromSeries
  754.   Description: multiple property Setting procedure for calling by a Series
  755.        Author: Mat Ballard
  756.  Date created: 05/29/2001
  757. Date modified: 05/29/2001 by Mat Ballard
  758.       Purpose: sets the Min Property when new data is added to a Series
  759.  Known Issues:
  760.  ------------------------------------------------------------------------------}
  761. procedure TAxis.SetMinMaxFromSeries(AMin, AMax: Single);
  762. begin
  763.   if (AMin >= AMax) then exit;
  764.   if ((AMin = FMin) and (AMax = FMax)) then exit;
  765.   if ((AMin <= 0) and (FLogScale)) then exit;
  766.  
  767.   FMin := AMin;
  768.   FMax := AMax;
  769.   Rescale;
  770. end;
  771.  
  772.  
  773. {------------------------------------------------------------------------------
  774.     Procedure: TAxis.SetPen
  775.   Description: standard property Set procedure
  776.        Author: Mat Ballard
  777.  Date created: 04/25/2000
  778. Date modified: 04/25/2000 by Mat Ballard
  779.       Purpose: sets the Pen Property
  780.  Known Issues:
  781.  ------------------------------------------------------------------------------}
  782. procedure TAxis.SetPen(Value: TPen);
  783. begin
  784.   FPen.Assign(Value);
  785.   {FFont.Color := FPen.Color;
  786.   FLabels.Font.Color := FPen.Color;}
  787.   StyleChange(Self);
  788. end;
  789.  
  790. {------------------------------------------------------------------------------
  791.     Procedure: TAxis.SetOrientation
  792.   Description: standard property Set procedure
  793.        Author: Mat Ballard
  794.  Date created: 04/25/2000
  795. Date modified: 04/25/2000 by Mat Ballard
  796.       Purpose: sets the Orientation Property
  797.  Known Issues:
  798.  ------------------------------------------------------------------------------}
  799. procedure TAxis.SetOrientation(Value: TOrientation);
  800. begin
  801.   {if (Value = FTickDirection) then exit;}
  802.  
  803.   FTickDirection := Value;
  804.   if (FTickDirection = orRight) then
  805.     FTickSign := 1
  806.    else
  807.     FTickSign := -1;
  808. {check the names of the titles and labels}
  809.   {TitleChange(Self);}
  810.   StyleChange(Self);
  811. end;
  812.  
  813. {------------------------------------------------------------------------------
  814.     Procedure: TAxis.SetStepSize
  815.   Description: standard property Set procedure
  816.        Author: Mat Ballard
  817.  Date created: 04/25/2000
  818. Date modified: 04/25/2000 by Mat Ballard
  819.       Purpose: sets the StepSize (distance between ticks) Property
  820.  Known Issues:
  821.  ------------------------------------------------------------------------------}
  822. procedure TAxis.SetStepSize(Value: Single);
  823. begin
  824.   if (FAutoScale) then exit;
  825.   if (Value = FStepSize) then exit;
  826.  
  827.   FStepSize := Value;
  828.   StyleChange(Self);
  829. end;
  830.  
  831. {------------------------------------------------------------------------------
  832.     Procedure: TAxis.SetStepStart
  833.   Description: standard property Set procedure
  834.        Author: Mat Ballard
  835.  Date created: 04/25/2000
  836. Date modified: 04/25/2000 by Mat Ballard
  837.       Purpose: sets the StepStart (where ticks start) Property
  838.  Known Issues:
  839.  ------------------------------------------------------------------------------}
  840. procedure TAxis.SetStepStart(Value: Single);
  841. begin
  842.   if (FAutoScale) then exit;
  843.   if (Value = FStepStart) then exit;
  844.  
  845.   FStepStart := Value;
  846.   StyleChange(Self);
  847. end;
  848.  
  849. {------------------------------------------------------------------------------
  850.     Procedure: TAxis.SetTickMinor
  851.   Description: standard property Set procedure
  852.        Author: Mat Ballard
  853.  Date created: 04/25/2000
  854. Date modified: 04/25/2000 by Mat Ballard
  855.       Purpose: sets the TickMinor (number of minor ticks) Property
  856.  Known Issues:
  857.  ------------------------------------------------------------------------------}
  858. procedure TAxis.SetTickMinor(Value: Byte);
  859. begin
  860.   if (Value = FTickMinor) then exit;
  861. {limit the number of minors:}
  862.   if (Value > 9) then
  863.     Value := 9;
  864.  
  865.   FTickMinor := Value;
  866.   StyleChange(Self);
  867. end;
  868.  
  869. {------------------------------------------------------------------------------
  870.     Procedure: TAxis.SetTickNum
  871.   Description: standard property Set procedure
  872.        Author: Mat Ballard
  873.  Date created: 04/25/2000
  874. Date modified: 04/25/2000 by Mat Ballard
  875.       Purpose: sets the TickNum Property
  876.  Known Issues:
  877.  ------------------------------------------------------------------------------
  878. procedure TAxis.SetTickNum(Value: Byte);
  879. begin
  880.   if (Value = FTickNum) then exit;
  881.  
  882.   FTickNum := Value;
  883.   ReScale;
  884. end;}
  885.  
  886. {------------------------------------------------------------------------------
  887.     Procedure: TAxis.SetTickSize
  888.   Description: standard property Set procedure
  889.        Author: Mat Ballard
  890.  Date created: 04/25/2000
  891. Date modified: 04/25/2000 by Mat Ballard
  892.       Purpose: sets the TickSize Property
  893.  Known Issues:
  894.  ------------------------------------------------------------------------------}
  895. procedure TAxis.SetTickSize(Value: Byte);
  896. begin
  897.   if (Value = FTickSize) then exit;
  898.  
  899.   FTickSize := Value;
  900.   ReScale;
  901. end;
  902.  
  903. {Various other Functions and Procedures--------------------------------------}
  904. {------------------------------------------------------------------------------
  905.     Procedure: TAxis.StyleChange
  906.   Description: event firing proedure
  907.        Author: Mat Ballard
  908.  Date created: 04/25/2000
  909. Date modified: 04/25/2000 by Mat Ballard
  910.       Purpose: fires the OnChange event
  911.  Known Issues:
  912.  ------------------------------------------------------------------------------}
  913. procedure TAxis.StyleChange(Sender: TObject);
  914. begin
  915.   if (assigned(OnChange) and Visible) then OnChange(Sender);
  916. end;
  917.  
  918. {------------------------------------------------------------------------------
  919.     Procedure: TAxis.ReScale
  920.   Description: geometry manager
  921.        Author: Mat Ballard
  922.  Date created: 04/25/2000
  923. Date modified: 04/25/2000 by Mat Ballard
  924.       Purpose: determines the ticks and labels
  925.  Known Issues:
  926.  ------------------------------------------------------------------------------}
  927. procedure TAxis.ReScale;
  928. {This method determines the Axis geometry (StepStart and StepSize).}
  929. var
  930.   i,
  931.   Exponent: Integer;
  932.   RoughStepSize: Single;
  933.   Mantissa: Extended;
  934. begin
  935.   PrecisionAdded := 0;
  936.   
  937.   if (not FAutoScale) then
  938.   begin
  939.     FStepStart := FMin;
  940.     FSpan := FMax - FMin;
  941.     exit;
  942.   end;
  943.  
  944.   if (FLogScale) then
  945.   begin
  946.     FLogSpan := Log10(FMax / FMin);
  947.     DeSci(FMin, Mantissa, Exponent);
  948. {work out a starting point, 1 x 10^Exponent:}
  949.     FStepStart := IntPower(10.0, Exponent);
  950.  
  951.     if (not FAutoScale) then
  952.     begin
  953.       if (FLogSpan >= 2) then
  954.       begin
  955. {many decades of data:}
  956.         if (not FAutoScale) then
  957.           FStepSize := 10;
  958.       end
  959.       else
  960.       begin
  961.         RoughStepSize := FLogSpan / (FTickNum+1);
  962.         RoughStepSize := Power(10.0, RoughStepSize);
  963.         if (RoughStepSize > 1.5) then
  964.         begin
  965. {get the Mantissa and Exponent:}
  966.           DeSci(RoughStepSize, Mantissa, Exponent);
  967.           FStepSize := Round(Mantissa) * IntPower(10.0, Exponent);
  968.         end
  969.         else
  970.         begin
  971.           FStepSize := RoughStepSize;
  972.         end;
  973. {$IFDEF DELPHI3_UP}
  974.         Assert(FStepSize > 1.0,
  975.           'TAxis.ReScale Error: The calculated StepSize on a Log scale is ' +
  976.             FloatToStr(FStepSize));
  977. {$ENDIF}
  978.       end; {how big is FLogSpan ?}
  979.     end; {not AutoScale}
  980.     while (FStepStart < FMin) do
  981. {go to next multiple of FStepSize:}
  982.       FStepStart := FStepSize * FStepStart;
  983.   end
  984.   else
  985.   begin {normal linear scale:}
  986.     FSpan := FMax - FMin;
  987.     if ((FAutoScale) or (FStepSize <= 0)) then
  988.     begin
  989.       RoughStepSize := FSpan / (FTickNum+1);
  990. {get the Mantissa and Exponent:}
  991.       DeSci(RoughStepSize, Mantissa, Exponent);
  992.  
  993.       {FStepSize := Round(Mantissa);
  994.       if (Exponent < 0) then
  995.       begin
  996.         for i := 1 to -Exponent do
  997.           FStepSize := FStepSize / 10;
  998.       end
  999.       else
  1000.       begin
  1001.         for i := 1 to Exponent do
  1002.           FStepSize := FStepSize * 10;
  1003.       end;}
  1004.  
  1005.       FStepSize := Round(Mantissa) * IntPower(10.0, Exponent);
  1006.       {FTickNum := Trunc(FSpan / FStepSize);}
  1007.     end;
  1008.     FStepStart := FStepSize * Int((FMin / FStepSize) + 0.999);
  1009. {increase FStepStart by FStepSize:}
  1010.     while (FStepStart < FMin) do
  1011.       FStepStart := FStepSize + FStepStart;
  1012.  
  1013. {PrecisionAdded is the added precision needed to display numerical labels with
  1014.  sufficient precision to be distinguishable:}      
  1015.     if (Exponent <= -FLabels.FDigits) then
  1016.       PrecisionAdded := 1 - FLabels.FDigits - Exponent;
  1017.   end;
  1018.  
  1019.   StyleChange(Self);
  1020. end;
  1021.  
  1022. {------------------------------------------------------------------------------
  1023.     Procedure: TAxis.GetNextXValue
  1024.   Description: auxilary procedure for  Drawing 
  1025.        Author: Mat Ballard
  1026.  Date created: 02/28/2001
  1027. Date modified: 02/28/2001 by Mat Ballard
  1028.       Purpose: calculates the next tick point
  1029.  Known Issues:
  1030.  ------------------------------------------------------------------------------}
  1031. function TAxis.GetNextXValue(XValue: Single): Single;
  1032. begin
  1033.   if (FLogScale) then
  1034.     GetNextXValue := XValue * FStepSize
  1035.    else
  1036.     GetNextXValue := XValue + FStepSize;
  1037. end;
  1038.  
  1039.  
  1040. {------------------------------------------------------------------------------
  1041.     Procedure: TAxis.Draw
  1042.   Description: standard Drawing procedure
  1043.        Author: Mat Ballard
  1044.  Date created: 04/25/2000
  1045. Date modified: 04/25/2000 by Mat Ballard
  1046.       Purpose: draws the Axis on a given canvas
  1047.  Known Issues:
  1048.  ------------------------------------------------------------------------------}
  1049. procedure TAxis.Draw(ACanvas: TCanvas);
  1050. {Comments:
  1051.  This method is quite complex, in a tedious way.
  1052.  It has to account for the following variations:
  1053.     1. Visible or not;
  1054.     2. Arrows visible or not;
  1055.     3. Axis direction (Horizontal or vertical);
  1056.     4. Tick (and Label and Title) direction);
  1057.     5. Title Alignment Direction and Orientation;
  1058.     6. Tick, Label and Title visibility.
  1059.  An added complication is that we must generate a vertical font for the Title
  1060.  of vertical axes. Note that this only works with TrueType fonts - NOT fonts
  1061.  that are purely screen or printer.}
  1062. var
  1063.   i,
  1064.   iX,
  1065.   iY,
  1066.   iYLabel,
  1067.   FontHeight,
  1068.   FontWidth,
  1069.   iFontWidth,
  1070.   FontDescent,
  1071.   MinorTickSize: Integer;
  1072.   MinorStepSize,
  1073.   MinorStepStart,
  1074.   YValue,
  1075.   XValue: Single;
  1076.   DoTextLabels: Boolean;
  1077.   TheText: String;
  1078.  
  1079.   function GetNextMinorXValue(XValue: Single): Single;
  1080.   begin
  1081.     if (FLogScale) then
  1082.       GetNextMinorXValue := XValue * MinorStepSize
  1083.      else
  1084.       GetNextMinorXValue := XValue + MinorStepSize;
  1085.   end;
  1086.  
  1087.   procedure LabelOut;
  1088.   begin
  1089.     iFontWidth := ACanvas.TextWidth(TheText);
  1090.     if (iFontWidth > FontWidth) then
  1091.       FontWidth := iFontWidth;
  1092.     if (FLabels.Direction = drHorizontal) then
  1093.       ACanvas.TextOut(iX - iFontWidth div 2, iYLabel, TheText)
  1094.      else
  1095.       if (FTickDirection = orLeft) then
  1096.         TextOutAngle(ACanvas, 90, iX - FontHeight div 2, iYLabel, TheText)
  1097.        else
  1098.         TextOutAngle(ACanvas, 90, iX - FontHeight div 2, iYLabel + FontWidth, TheText);
  1099.   end;
  1100.   
  1101. begin
  1102. {the most common reason for exit:}
  1103.   if (not Visible) then exit;
  1104. {$IFDEF DELPHI3_UP}
  1105.   Assert(ACanvas <> nil, 'TAxis.Draw: ACanvas is nil !');
  1106. {$ENDIF}
  1107.  
  1108.   FontWidth := 1;
  1109.   ACanvas.Pen.Assign(FPen);
  1110.   if (FDirection = drHorizontal) then
  1111.   begin
  1112.     DoTextLabels := FALSE;
  1113.     if (FLabelSeries <> nil) then
  1114.       if (TSeries(FLabelSeries).XStringData <> nil) then
  1115.         if (TSeries(FLabelSeries).XStringData.Count > 0) then
  1116.           DoTextLabels := TRUE;
  1117.  
  1118. {Draw the axis:}
  1119.     ACanvas.MoveTo(Left, MidY);
  1120.     ACanvas.LineTo(Right, MidY);
  1121. {Draw the arrows on the axis:}
  1122.     if (FArrowSize > 0) then
  1123.     begin
  1124.       if (Alignment = taLeftJustify) then
  1125.       begin
  1126.         ACanvas.MoveTo(Left+FArrowSize, Top);
  1127.         ACanvas.LineTo(Left, MidY);
  1128.         ACanvas.LineTo(Left+FArrowSize, Bottom);
  1129.       end;
  1130.       if (Alignment = taRightJustify) then
  1131.       begin
  1132.         ACanvas.LineTo(Right-FArrowSize, Top);
  1133.         ACanvas.MoveTo(Right, MidY);
  1134.         ACanvas.LineTo(Right-FArrowSize, Bottom);
  1135.       end; {taCenter therefore means no arrows !}
  1136.     end;
  1137.  
  1138.     if (FLabels.Visible) then
  1139.     begin
  1140.       ACanvas.Font.Assign(FLabels.Font);
  1141.       FontHeight := ACanvas.TextHeight('9');
  1142.       if ((FTickDirection = orLeft) and
  1143.           (FLabels.Direction = drHorizontal)) then
  1144.         Dec(iYLabel, FontHeight);
  1145.     end;
  1146.  
  1147.     iY := MidY;
  1148.     iYLabel := MidY + FTickSign*FTickSize;
  1149. {Major Ticks on the axis:}
  1150.     if ((FTickSize > 0) and
  1151.         (not DoTextLabels)) then
  1152.     begin
  1153.       XValue := FStepStart;
  1154.       while (XValue < FMax) do
  1155.       begin
  1156.         iX := FofX(XValue);
  1157.         ACanvas.MoveTo(iX, iY);
  1158.         ACanvas.LineTo(iX, iYLabel);
  1159.         XValue := GetNextXValue(XValue);
  1160.       end;
  1161. {Minor Ticks on the axis:}
  1162.       if (FTickMinor > 0) then
  1163.       begin
  1164. {find out where the minors start:}
  1165.         MinorStepSize := FStepSize / (FTickMinor+1);
  1166.         MinorStepStart := FStepStart;
  1167.         MinorTickSize := FTickSign * FTickSize div 2;
  1168.         while ((MinorStepStart - MinorStepSize) >= FMin) do
  1169.           MinorStepStart := MinorStepStart - MinorStepSize;
  1170.         iY := MidY;
  1171.         XValue := MinorStepStart;
  1172.         while (XValue < FMax) do
  1173.         begin
  1174.           iX := FofX(XValue);
  1175.           ACanvas.MoveTo(iX, iY);
  1176.           ACanvas.LineTo(iX, iY + MinorTickSize);
  1177.           XValue := GetNextMinorXValue(XValue);
  1178.         end;
  1179.       end; {minors}
  1180.     end; {Ticks}
  1181.  
  1182. {Set text output orientation.}
  1183. {Labels on the axis:}
  1184.     if (FLabels.Visible) then
  1185.     begin
  1186.       if (DoTextLabels) then
  1187.       begin
  1188.         for i := 0 to TSeries(FLabelSeries).XStringData.Count-1 do
  1189.         begin
  1190.           iX := FofX(TSeries(FLabelSeries).XData^[i]);
  1191.           ACanvas.MoveTo(iX, iY);
  1192.           ACanvas.LineTo(iX, iYLabel);
  1193.           TheText := TSeries(FLabelSeries).XStringData.Strings[i];
  1194.           LabelOut;
  1195.         end;
  1196.       end
  1197.       else
  1198.       begin
  1199.         //i := 0;
  1200.         iX := 0;
  1201.         XValue := FStepStart;
  1202.         while (XValue < FMax) do
  1203.         begin
  1204.           iX := FofX(XValue);
  1205.           ACanvas.MoveTo(iX, iY);
  1206.           ACanvas.LineTo(iX, iYLabel);
  1207.           TheText := LabelToStrF(XValue);
  1208.           XValue := GetNextXValue(XValue);
  1209.           LabelOut;
  1210.           //Inc(i);
  1211.         end;
  1212.  
  1213.   {record the position of the labels for use by TPlot:}
  1214.         FLabels.Left := FofX(FStepStart) - FontWidth div 2;
  1215.         FLabels.Top := iY;
  1216.         FLabels.Bottom := iY + FontHeight;
  1217.         FLabels.Right := iX + FontWidth div 2;
  1218.       end;
  1219.     end;
  1220.  
  1221.     SetupHorizontalEnvelope;
  1222.   end
  1223.   else
  1224.   begin
  1225. {Draw the Vertical axis:}
  1226.     ACanvas.MoveTo(MidX, Bottom);
  1227.     ACanvas.LineTo(MidX, Top);
  1228. {Draw the arrows on the axis:}
  1229.     if (FArrowSize > 0) then
  1230.     begin
  1231.       ACanvas.LineTo(Left, Top+FArrowSize);
  1232.       ACanvas.MoveTo(MidX, Top);
  1233.       ACanvas.LineTo(Right, Top+FArrowSize);
  1234.     end;
  1235.  
  1236. {Ticks on the axis:}
  1237.     if (FTickSize > 0) then
  1238.     begin
  1239.       iX := MidX;
  1240.       YValue := FStepStart;
  1241.       while (YValue < FMax) do
  1242.       begin
  1243.         iY := FofY(YValue);
  1244.         ACanvas.MoveTo(iX, iY);
  1245.         ACanvas.LineTo(iX + FTickSign*FTickSize, iY);
  1246.         YValue := GetNextXValue(YValue);
  1247.       end;
  1248. {Minor Ticks on the axis:}
  1249.       if (FTickMinor > 0) then
  1250.       begin
  1251. {find out where the minors start:}
  1252.         MinorStepSize := FStepSize / (FTickMinor + 1);
  1253.         MinorStepStart := FStepStart;
  1254.         MinorTickSize := FTickSign * FTickSize div 2;
  1255.         while ((MinorStepStart - MinorStepSize) >= FMin) do
  1256.           MinorStepStart := MinorStepStart - MinorStepSize;
  1257.         iX := MidX;
  1258.         YValue := MinorStepStart;
  1259.         while (YValue < FMax) do
  1260.         begin
  1261.           iY := FofY(YValue);
  1262.           ACanvas.MoveTo(iX, iY);
  1263.           ACanvas.LineTo(iX + MinorTickSize, iY);
  1264.           YValue := GetNextMinorXValue(YValue);
  1265.         end;
  1266.       end; {minors}
  1267.     end; {Ticks}
  1268.  
  1269.     FontWidth := 1; {see below}
  1270. {Labels on the axis:}
  1271.     if (FLabels.Visible) then
  1272.     begin
  1273.       ACanvas.Font.Assign(FLabels.Font);
  1274.       FontWidth := ACanvas.TextWidth('9');
  1275.       FontHeight := ACanvas.TextHeight('9');
  1276. {We could call GetOutlineTextMetrics to get
  1277.  the Descent (gap between baseline and bottom of a font), but:}
  1278.       FontDescent := FontHeight div 5;
  1279.       iX := MidX + FTickSign*(FTickSize + FontWidth div 5);
  1280.       iY := 0; {see below}
  1281.       YValue := FStepStart;
  1282.       //i := 0;
  1283.       while (YValue < FMax) do
  1284.       begin
  1285.         {if (FLabelSeries <> nil) then
  1286.           if (i < FLabelSeries.Count) then
  1287.             TheText := FLabelSeries.Strings[i]
  1288.            else
  1289.             break
  1290.         else}
  1291.           TheText := LabelToStrF(YValue);
  1292.         iY := FofY(YValue) - FontHeight + FontDescent;
  1293.         iFontWidth := ACanvas.TextWidth(TheText);
  1294. {remember which label is widest:}
  1295.         if (FontWidth < iFontWidth) then
  1296.           FontWidth := iFontWidth;
  1297.         if (FTickDirection = orRight) then
  1298.           ACanvas.TextOut(iX, iY, TheText)
  1299.          else
  1300.           ACanvas.TextOut(iX - iFontWidth, iY, TheText);
  1301.         YValue := GetNextXValue(YValue);
  1302.       end;
  1303.  
  1304.       FLabels.Bottom := FofY(FStepStart);
  1305. {record the position of the labels for use by TPlot:}
  1306.       FLabels.Top := iY - Abs(ACanvas.Font.Height);
  1307.       if (FTickDirection = orRight) then
  1308.       begin
  1309.         FLabels.Left := iX;
  1310.         FLabels.Right := iX + FontWidth;
  1311.       end
  1312.       else
  1313.       begin
  1314.         FLabels.Left := iX - FontWidth;
  1315.         FLabels.Right := iX;
  1316.       end;
  1317.     end; {Labels Visible}
  1318.  
  1319.     SetupVerticalEnvelope(FontWidth);
  1320.   end; {Horizontal or Vertical}
  1321. {Print the axis Title:}
  1322.   FTitle.Draw(ACanvas);
  1323. end;
  1324.  
  1325. {------------------------------------------------------------------------------
  1326.      Function: TAxis.FofX
  1327.   Description: standard X transform
  1328.        Author: Mat Ballard
  1329.  Date created: 04/25/2000
  1330. Date modified: 04/25/2000 by Mat Ballard
  1331.       Purpose: returns the pixel position on screen as a function of the real data ordinate X
  1332.  Known Issues:
  1333.  ------------------------------------------------------------------------------}
  1334. function TAxis.FofX(X: Single): Integer;
  1335. begin
  1336. {$IFDEF DELPHI3_UP}
  1337.   Assert(FDirection = drHorizontal, 'A vertical Axis cannot return F(X) !');
  1338. {$ENDIF}
  1339.  
  1340.   if (FLogScale) then
  1341.     FofX := Round(Left + Width * ((Log10(X / FMin)) / FLogSpan))
  1342.    else
  1343.     FofX := Round(Left + Width * ((X - FMin) / (FSpan)));
  1344. end;
  1345.  
  1346. {------------------------------------------------------------------------------
  1347.      Function: TAxis.FofY
  1348.   Description: standard Y transform
  1349.        Author: Mat Ballard
  1350.  Date created: 04/25/2000
  1351. Date modified: 04/25/2000 by Mat Ballard
  1352.       Purpose: returns the pixel position on screen as a function of the real data co-ordinate Y
  1353.  Known Issues:
  1354.  ------------------------------------------------------------------------------}
  1355. function TAxis.FofY(Y: Single): Integer;
  1356. begin
  1357. {$IFDEF DELPHI3_UP}
  1358.   Assert(FDirection = drVertical, 'A Horizontal Axis cannot return F(Y) !');
  1359. {$ENDIF}
  1360.  
  1361.   if (FLogScale) then
  1362.     FofY := Round(Bottom - Height * ((Log10(Y / FMin)) / FLogSpan))
  1363.    else
  1364.     FofY := Round(Bottom - Height * ((Y - FMin) / (FSpan)));
  1365. end;
  1366.  
  1367. {------------------------------------------------------------------------------
  1368.      Function: TAxis.XofF
  1369.   Description: inverse X transform
  1370.        Author: Mat Ballard
  1371.  Date created: 04/25/2000
  1372. Date modified: 04/25/2000 by Mat Ballard
  1373.       Purpose: returns the real data ordinate X as a function of the pixel position on screen
  1374.  Known Issues:
  1375.  ------------------------------------------------------------------------------}
  1376. function TAxis.XofF(F: Integer): Single;
  1377. {this function returns the real data ordinate X
  1378.  as a function of the pixel position F on screen:}
  1379. begin
  1380. {$IFDEF DELPHI3_UP}
  1381.   Assert(FDirection = drHorizontal, 'A Vertical Axis cannot return F(X) !');
  1382. {$ENDIF}
  1383.  
  1384.   if (FLogScale) then
  1385.     XofF := FMin * Power(10.0, (FLogSpan * (F-Left) / Width))
  1386.    else
  1387.     XofF := FSpan * ((F-Left) / Width) + FMin;
  1388. end;
  1389.  
  1390. {------------------------------------------------------------------------------
  1391.      Function: TAxis.YofF
  1392.   Description: inverse Y transform
  1393.        Author: Mat Ballard
  1394.  Date created: 04/25/2000
  1395. Date modified: 04/25/2000 by Mat Ballard
  1396.       Purpose: returns the real data ordinate Y as a function of the pixel position on screen
  1397.  Known Issues:
  1398.  ------------------------------------------------------------------------------}
  1399. function TAxis.YofF(F: Integer): Single;
  1400. {this function returns the real data ordinate X
  1401.  as a function of the pixel position F on screen:}
  1402. begin
  1403. {$IFDEF DELPHI3_UP}
  1404.   Assert(FDirection = drVertical, 'A Horizontal Axis cannot return F(Y) !');
  1405. {$ENDIF}
  1406.  
  1407.   if (FLogScale) then
  1408.     YofF := FMin * Power(10.0, (FLogSpan * (Bottom-F) / Height))
  1409.    else
  1410.     YofF := FSpan * ((Bottom-F) / Height) + FMin;
  1411. end;
  1412.  
  1413. {------------------------------------------------------------------------------
  1414.      Function: TAxis.StrToLabel
  1415.   Description: converts a string to a number, depending on the NumberFormat
  1416.        Author: Mat Ballard
  1417.  Date created: 04/25/2000
  1418. Date modified: 04/25/2000 by Mat Ballard
  1419.       Purpose: user IO
  1420.  Known Issues:
  1421.  ------------------------------------------------------------------------------}
  1422. function TAxis.StrToLabel(Value: String): Single;
  1423. begin
  1424.   case (FLabels.NumberFormat) of
  1425.     lfGeneral .. lfCurrency:
  1426.       StrToLabel := StrToFloat(Value);
  1427.     lfPercent:
  1428.       StrToLabel := StrToFloat(Value) / 100;
  1429.     lfSeconds:
  1430.       StrToLabel := StrToFloat(Value);
  1431.     lfMinutes:
  1432.       StrToLabel := 60 * StrToFloat(Value);
  1433.     lfHours:
  1434.       StrToLabel := 3600 * StrToFloat(Value);
  1435.     lfDays:
  1436.       StrToLabel := 86400 * StrToFloat(Value);
  1437.     lfShortTime:
  1438.       StrToLabel := StrToDateTime(Value);
  1439.     lfShortDate:
  1440.       StrToLabel := StrToDateTime(Value);
  1441.     else
  1442.       StrToLabel := 0.0;
  1443.   end;
  1444. end;
  1445.  
  1446. {------------------------------------------------------------------------------
  1447.      Function: TAxis.LabelToStrF
  1448.   Description: converts a number to a string, depending on the NumberFormat
  1449.        Author: Mat Ballard
  1450.  Date created: 04/25/2000
  1451. Date modified: 04/25/2000 by Mat Ballard
  1452.       Purpose: user IO
  1453.  Known Issues:
  1454.  ------------------------------------------------------------------------------}
  1455. function TAxis.LabelToStrF(Value: Single): String;
  1456. var
  1457.   TheText: String;
  1458.   Mantissa: Extended;
  1459.   Exponent: Integer;
  1460.   TheDateTime: TDateTime;
  1461. begin
  1462.   case (FLabels.NumberFormat) of
  1463.     lfGeneral .. lfCurrency:
  1464. {See Rescale for definition of PrecisionAdded}
  1465.       TheText := FloatToStrF(Value, TFloatFormat(FLabels.NumberFormat),
  1466.         FLabels.Precision + PrecisionAdded, FLabels.Digits);
  1467.     lfSI:
  1468.       begin
  1469.         DeSci(Value, Mantissa, Exponent);
  1470.         case Exponent of {p, n u, m, -, K, M, G, T}
  1471.           -12 .. -10: TheText := 'p';
  1472.           -9 .. -7: TheText := 'n';
  1473.           -6 .. -4: TheText := 'u';
  1474.           -3 .. -1: TheText := 'm';
  1475.           3 .. 5: TheText := 'K';
  1476.           6 .. 8: TheText := 'M';
  1477.           9 .. 11: TheText := 'G';
  1478.           12 .. 14: TheText := 'T';
  1479.         else ;
  1480.           TheText := '';
  1481.         end;
  1482.         if (Length(TheText) > 0) then
  1483.         begin
  1484.           Exponent := (Exponent + 99) mod 3;
  1485.           Mantissa := Mantissa * IntPower(10, Exponent);
  1486.           TheText := FloatToStrF(Mantissa, TFloatFormat(lfFixed),
  1487.             FLabels.Precision, FLabels.Digits) + TheText;
  1488.         end
  1489.         else
  1490.           TheText := FloatToStrF(Value, TFloatFormat(lfGeneral),
  1491.             FLabels.Precision, FLabels.Digits);
  1492.       end;
  1493.     lfPercent:
  1494.       TheText := FloatToStrF(100 * Value, TFloatFormat(FLabels.NumberFormat),
  1495.         FLabels.Precision, FLabels.Digits);
  1496.     lfSeconds:
  1497.       TheText := FloatToStrF(Round(Value), ffGeneral,
  1498.         FLabels.Precision, FLabels.Digits);
  1499.     lfMinutes:
  1500.       TheText := FloatToStrF(Round(Value / 60), ffGeneral,
  1501.         FLabels.Precision, FLabels.Digits);
  1502.     lfHours:
  1503.       TheText := FloatToStrF(Round(Value / 3600), ffGeneral,
  1504.         FLabels.Precision, FLabels.Digits);
  1505.     lfDays:
  1506.       TheText := FloatToStrF(Round(Value / 86400), ffGeneral,
  1507.         FLabels.Precision, FLabels.Digits);
  1508.     lfShortTime:
  1509.       begin
  1510.         TheDateTime := Value;
  1511.         TheText := FormatDateTime('t', TheDateTime);
  1512.       end;
  1513.     lfShortDate:
  1514.       begin
  1515.         TheDateTime := Value;
  1516.         TheText := FormatDateTime('ddddd', TheDateTime);
  1517.       end;
  1518.   end;
  1519.  
  1520.   LabelToStrF := TheText;
  1521. end;
  1522.  
  1523. {------------------------------------------------------------------------------
  1524.     Procedure: TAxis.SetupHorizontalEnvelope
  1525.   Description: sets up the Horizontal (X Axis) envelope around which the Title dances
  1526.        Author: Mat Ballard
  1527.  Date created: 04/25/2000
  1528. Date modified: 04/25/2000 by Mat Ballard
  1529.       Purpose: manages the appearance of the Axis
  1530.  Known Issues:
  1531.  ------------------------------------------------------------------------------}
  1532. procedure TAxis.SetupHorizontalEnvelope;
  1533. var
  1534.   TheRect: TRect;
  1535. begin
  1536.   TheRect.Left := Left;
  1537.   TheRect.Right := Right;
  1538.   if (FTickDirection = orLeft) then
  1539.   begin
  1540.     TheRect.Top := MidY - FTickSize;
  1541.     TheRect.Bottom := MidY + 1;
  1542.     if (FLabels.Visible) then
  1543.       TheRect.Top := TheRect.Top - Abs(FLabels.Font.Height);
  1544.   end
  1545.   else  {oRight}
  1546.   begin
  1547.     TheRect.Top := MidY - 1;
  1548.     TheRect.Bottom := MidY + FTickSize;
  1549.     if (FLabels.Visible) then
  1550.       TheRect.Bottom := TheRect.Bottom + Abs(FLabels.Font.Height);
  1551.   end; {FTickDirection}
  1552.   FTitle.Envelope := TheRect;
  1553. end;
  1554.  
  1555. {------------------------------------------------------------------------------
  1556.     Procedure: TAxis.SetupVerticalEnvelope
  1557.   Description: sets up the Vertical (Y Axis) envelope around which the Title dances
  1558.        Author: Mat Ballard
  1559.  Date created: 04/25/2000
  1560. Date modified: 04/25/2000 by Mat Ballard
  1561.       Purpose: manages the appearance of the Axis
  1562.  Known Issues:
  1563.  ------------------------------------------------------------------------------}
  1564. procedure TAxis.SetupVerticalEnvelope(StrWidth: Integer);
  1565. var
  1566.   TheRect: TRect;
  1567. begin
  1568.   TheRect.Top := Top;
  1569.   TheRect.Bottom := Bottom;
  1570.   if (FTickDirection = orLeft) then
  1571.   begin
  1572.     TheRect.Left := MidX - FTickSize - Abs(FLabels.Font.Height) div 2;
  1573.     TheRect.Right := MidX + 1;
  1574.     if (FLabels.Visible) then
  1575.       TheRect.Left := TheRect.Left - StrWidth;
  1576.   end
  1577.   else {oRight}
  1578.   begin
  1579.     TheRect.Left := MidX - 1;
  1580.     TheRect.Right := MidX + FTickSize;
  1581.     if (FLabels.Visible) then
  1582.       TheRect.Right := TheRect.Right + StrWidth;
  1583.   end; {FTickDirection}
  1584.   FTitle.Envelope := TheRect;
  1585. end;
  1586.  
  1587. {------------------------------------------------------------------------------
  1588.     Procedure: TAxis.Assign
  1589.   Description: standard Assign method
  1590.        Author: Mat Ballard
  1591.  Date created: 07/06/2000
  1592. Date modified: 07/06/2000 by Mat Ballard
  1593.       Purpose: implements Assign
  1594.  Known Issues:
  1595.  ------------------------------------------------------------------------------}
  1596. {procedure TAxis.Assign(Source: TPersistent);
  1597. begin
  1598.   inherited Assign(Source);
  1599.   FArrowSize := TAxis(Source).ArrowSize;
  1600.   FDirection := TAxis(Source).Direction;
  1601.   FIntercept := TAxis(Source).Intercept;
  1602.   FLogscale := TAxis(Source).Logscale;
  1603.   FMax := TAxis(Source).Max;
  1604.   FMin := TAxis(Source).Min;
  1605.   FStepSize := TAxis(Source).StepSize;
  1606.   FTickDirection := TAxis(Source).TickDirection;
  1607.   FTickNum := TAxis(Source).TickNum;
  1608.   FTickSize := TAxis(Source).TickSize;
  1609.  
  1610.   FLabels.Assign(TAxis(Source).Labels);
  1611.   FPen.Assign(TAxis(Source).Pen);
  1612.   FTitle.Assign(TAxis(Source).Title);
  1613. end;}
  1614.  
  1615. {------------------------------------------------------------------------------
  1616.     Procedure: TAxis.AssignTo
  1617.   Description: standard AssignTo method
  1618.        Author: Mat Ballard
  1619.  Date created: 07/06/2000
  1620. Date modified: 07/06/2000 by Mat Ballard
  1621.       Purpose: implements AssignTo
  1622.  Known Issues:
  1623.  ------------------------------------------------------------------------------}
  1624. procedure TAxis.AssignTo(Dest: TPersistent);
  1625. begin
  1626.   inherited AssignTo(Dest);
  1627.   TAxis(Dest).ArrowSize := FArrowSize;
  1628.   TAxis(Dest).Direction := FDirection;
  1629.   {TAxis(Dest).Intercept := FIntercept;}
  1630.   TAxis(Dest).Logscale := FLogscale;
  1631.   TAxis(Dest).Max := FMax;
  1632.   TAxis(Dest).Min := FMin;
  1633.   TAxis(Dest).StepSize := FStepSize;
  1634.   TAxis(Dest).TickDirection := FTickDirection;
  1635.   TAxis(Dest).TickSize := FTickSize;
  1636.  
  1637.   TAxis(Dest).Labels.Assign(FLabels);
  1638.   TAxis(Dest).Pen.Assign(FPen);
  1639.   TAxis(Dest).Title.Assign(FTitle);
  1640. end;
  1641.  
  1642. procedure TAxis.SetLabelSeries(Value: TPersistent);
  1643. begin
  1644. {Note: Labeltext is maintained within the TSeries, NOT in TAxis !}
  1645.   FLabelSeries := Value;
  1646.   StyleChange(Self);
  1647. end;
  1648.  
  1649. {TAngleAxis methods ---------------------------------------------------------}
  1650. {Constructor and Destructor:-------------------------------------------------}
  1651. {------------------------------------------------------------------------------
  1652.   Constructor: TAngleAxis.Create
  1653.   Description: standard Constructor
  1654.        Author: Mat Ballard
  1655.  Date created: 04/25/2000
  1656. Date modified: 04/25/2000 by Mat Ballard
  1657.       Purpose: sets the Precision and Digits Properties
  1658.  Known Issues:
  1659.  ------------------------------------------------------------------------------}
  1660. Constructor TAngleAxis.Create(AOwner: TPersistent);
  1661. begin
  1662. {First call the ancestor:}
  1663.   inherited Create(AOwner);
  1664.  
  1665.   FireEvents := FALSE;
  1666.   FLength := 100;
  1667.   Angle := 225;
  1668.   FireEvents := TRUE;
  1669. end;
  1670.  
  1671. {------------------------------------------------------------------------------
  1672.   Destructor: TAngleAxis.Destroy
  1673.   Description: standard Destructor
  1674.        Author: Mat Ballard
  1675.  Date created: 01/16/2001
  1676. Date modified: 01/16/2001 by Mat Ballard
  1677.       Purpose:
  1678.  Known Issues:
  1679.  ------------------------------------------------------------------------------}
  1680. Destructor TAngleAxis.Destroy;
  1681. begin
  1682.   inherited Destroy;
  1683. end;
  1684.  
  1685.  
  1686. {------------------------------------------------------------------------------
  1687.     Procedure: TAxis.SetAngle
  1688.   Description: standard property Set procedure
  1689.        Author: Mat Ballard
  1690.  Date created: 01/16/2001
  1691. Date modified: 01/16/2001 by Mat Ballard
  1692.       Purpose: sets the Angle Property
  1693.  Known Issues:
  1694.  ------------------------------------------------------------------------------}
  1695. procedure TAngleAxis.SetAngle(Value: Word);
  1696. begin
  1697.   FAngle := Value Mod 360;
  1698.   FAngleRadians := Pi * FAngle / 180;
  1699. {this is twice as fast as calling them individually:}
  1700.   SinCos(FAngleRadians, FSin, FCos);
  1701. {look back along the axis, then 30 degrees less, for the arrow:}
  1702.   SinCos(FAngleRadians + Pi*(1/2 - 1/6), FSinM30, FCosM30);
  1703. {look back along the axis, then 30 degrees more:}
  1704.   SinCos(FAngleRadians + Pi*(1/2 + 1/6), FSinP30, FCosP30);
  1705.  
  1706.   StyleChange(Self);
  1707. end;
  1708.  
  1709. {------------------------------------------------------------------------------
  1710.     Procedure: TAxis.SetLength
  1711.   Description: standard property Set procedure
  1712.        Author: Mat Ballard
  1713.  Date created: 01/16/2001
  1714. Date modified: 01/16/2001 by Mat Ballard
  1715.       Purpose: sets the Length Property
  1716.  Known Issues:
  1717.  ------------------------------------------------------------------------------}
  1718. procedure TAngleAxis.SetLength(Value: Word);
  1719. begin
  1720.   if (Value = FLength) then exit;
  1721.  
  1722.   FLength := Value;
  1723.   StyleChange(Self);
  1724. end;
  1725.  
  1726. {------------------------------------------------------------------------------
  1727.     Procedure: TAxis.Draw
  1728.   Description: standard Drawing procedure
  1729.        Author: Mat Ballard
  1730.  Date created: 04/25/2000
  1731. Date modified: 04/25/2000 by Mat Ballard
  1732.       Purpose: draws the Axis on a given canvas
  1733.  Known Issues:
  1734.  ------------------------------------------------------------------------------}
  1735. procedure TAngleAxis.Draw(ACanvas: TCanvas);
  1736. {Comments:
  1737.  This method is quite complex, in a tedious way.
  1738.  It has to account for the following variations:
  1739.     1. Visible or not;
  1740.     2. Arrows visible or not;
  1741.     3. Axis direction (Horizontal or vertical);
  1742.     4. Tick (and Label and Title) direction);
  1743.     5. Title Alignment Direction and Orientation;
  1744.     6. Tick, Label and Title visibility.
  1745.     7. Angle !
  1746.  An added complication is that we must generate a vertical font for the Title
  1747.  of vertical axes. Note that this only works with TrueType fonts - NOT fonts
  1748.  that are purely screen or printer.}
  1749. var
  1750.   OldFireEvents: Boolean;
  1751.   i,
  1752.   iX,
  1753.   iY,
  1754.   FontHeight,
  1755.   FontWidth,
  1756.   iFontWidth,
  1757.   FontDescent,
  1758.   MinorTickSize: Integer;
  1759.   MinorStepSize,
  1760.   MinorStepStart,
  1761.   {NewAngle,}
  1762.   ZValue: Single;
  1763.   TheText: String;
  1764.   dTick,
  1765.   TheTickStart: TPoint;
  1766.  
  1767. {begin internal functions:}  
  1768.   function GetNextXValue(XValue: Single): Single;
  1769.   begin
  1770.     if (FLogScale) then
  1771.       GetNextXValue := XValue * FStepSize
  1772.      else
  1773.       GetNextXValue := XValue + FStepSize;
  1774.   end;
  1775.  
  1776.   function GetNextMinorXValue(XValue: Single): Single;
  1777.   begin
  1778.     if (FLogScale) then
  1779.       GetNextMinorXValue := XValue * MinorStepSize
  1780.      else
  1781.       GetNextMinorXValue := XValue + MinorStepSize;
  1782.   end;
  1783.  
  1784. begin
  1785. {the most common reason for exit:}
  1786.   if (not Visible) then exit;
  1787. {$IFDEF DELPHI3_UP}
  1788.   Assert(ACanvas <> nil, 'TAngleAxis.Draw: ACanvas is nil !');
  1789. {$ENDIF}
  1790.  
  1791.   FontWidth := 1;
  1792.   ACanvas.Pen.Assign(FPen);
  1793.  
  1794. {Do the geometry:}
  1795. {first, squelch any "OnChange" events:}
  1796.   OldFireEvents := FireEvents;
  1797.   FireEvents := FALSE;
  1798.   FEndX := Left + Round(FLength * FSin);
  1799.   FEndY := Top + Round(-FLength * FCos);
  1800.  
  1801. {Draw the axis:}
  1802.   ACanvas.MoveTo(Left, Top);
  1803.   ACanvas.LineTo(FEndX, FEndY);
  1804. {Draw the arrows on the axis:}
  1805.   if (FArrowSize > 0) then
  1806.   begin
  1807.     if (Alignment = taRightJustify) then
  1808.     begin
  1809.       ACanvas.MoveTo(FEndX, FEndY);
  1810.       iX := FEndX + Round(FArrowSize * FCosM30);
  1811.       iY := FEndY + Round(FArrowSize * FSinM30);
  1812.       ACanvas.LineTo(iX, iY);
  1813.       ACanvas.MoveTo(FEndX, FEndY);
  1814. {look back along the axis, then 30 degrees less:}
  1815.       iX := FEndX + Round(FArrowSize * FCosP30);
  1816.       iY := FEndY + Round(FArrowSize * FSinP30);
  1817.       ACanvas.LineTo(iX, iY);
  1818.     end; {taLeftJustify and taCenter therefore means no arrows !}
  1819.   end;
  1820.  
  1821. {Prepare fonts for Labels on the axis:}
  1822.   if (FLabels.Visible) then
  1823.   begin
  1824.     ACanvas.Font.Assign(FLabels.Font);
  1825.     FontWidth := ACanvas.TextWidth('9');
  1826.     FontHeight := ACanvas.TextHeight('9');
  1827. {We could call GetOutlineTextMetrics to get
  1828. the Descent (gap between baseline and bottom of a font), but:}
  1829.     {FontDescent := FontHeight div 5;}
  1830.   end;
  1831.  
  1832. {Ticks on the axis:}
  1833.  
  1834.   dTick.x := 0;
  1835.   dTick.y := 0;
  1836.   case FAngle of
  1837.     0: dTick.x := -FTickSize;
  1838.     1 .. 60: dTick.x := FTickSize;
  1839.     61 .. 120: dTick.y := FTickSize;
  1840.     121 .. 180: dTick.x := FTickSize;
  1841.     181 .. 240: dTick.x := -FTickSize;
  1842.     241 .. 300: dTick.y := FTickSize;
  1843.     301 .. 359: dTick.x := -FTickSize;
  1844.   end;
  1845.  
  1846.   ZValue := FStepStart;
  1847.   //i := 0;
  1848.   while (ZValue < FMax) do
  1849.   begin
  1850.     TheTickStart := dFofZ(ZValue);
  1851.     Inc(TheTickStart.x, Left);
  1852.     Inc(TheTickStart.y, Top);
  1853.     if (FTickSize > 1) then
  1854.       ACanvas.MoveTo(TheTickStart.x, TheTickStart.y);
  1855.     Inc(TheTickStart.x, dTick.x);
  1856.     Inc(TheTickStart.y, dTick.y);
  1857.     if (FTickSize > 1) then
  1858.       ACanvas.LineTo(TheTickStart.x, TheTickStart.y);
  1859.  
  1860.     if (FLabels.Visible) then
  1861.     begin
  1862.       {if (FLabelSeries <> nil) then
  1863.         if (i < FLabelSeries.Count) then
  1864.           TheText := FLabelSeries.Strings[i]
  1865.          else
  1866.           break
  1867.       else}
  1868.         TheText := LabelToStrF(ZValue);
  1869.       iFontWidth := ACanvas.TextWidth(TheText);
  1870.       if (iFontWidth > FontWidth) then
  1871.         FontWidth := iFontWidth;
  1872.       if (dTick.x < 0) then
  1873.         Dec(TheTickStart.x, iFontWidth);
  1874.       if (dTick.y > 0) then
  1875.       begin
  1876.         Inc(TheTickStart.y, FontHeight);
  1877.         Dec(TheTickStart.x, iFontWidth div 2);
  1878.       end;
  1879. {$IFDEF MSWINDOWS}
  1880.       ACanvas.TextOut(
  1881.         TheTickStart.x,
  1882.         TheTickStart.y - Abs(ACanvas.Font.Height),
  1883.         TheText);
  1884. {$ENDIF}
  1885. {$IFDEF LINUX}
  1886.       ACanvas.TextOut(
  1887.         TheTickStart.x,
  1888.         TheTickStart.y {+ Abs(ACanvas.Font.Height)},
  1889.         TheText);
  1890. {$ENDIF}
  1891.     end;
  1892.  
  1893.     //Inc(i);
  1894.     ZValue := GetNextXValue(ZValue);
  1895.   end; {while ZValue < FMax}
  1896.  
  1897. {Minor Ticks on the axis:}
  1898.   if ((FTickSize > 1) and (FTickMinor > 0)) then
  1899.   begin
  1900. {find out where the minors start:}
  1901.     MinorStepSize := FStepSize / (FTickMinor+1);
  1902.     MinorStepStart := FStepStart;
  1903.     while ((MinorStepStart - MinorStepSize) >= FMin) do
  1904.       MinorStepStart := MinorStepStart - MinorStepSize;
  1905.     //iY := MidY;
  1906.     dTick.x := dTick.x div 2;
  1907.     dTick.y := dTick.y div 2;
  1908.  
  1909.     ZValue := MinorStepStart;
  1910.     //i := 0;
  1911.     while (ZValue < FMax) do
  1912.     begin
  1913.       TheTickStart := dFofZ(ZValue);
  1914.       Inc(TheTickStart.x, Left);
  1915.       Inc(TheTickStart.y, Top);
  1916.       if (FTickSize > 1) then
  1917.         ACanvas.MoveTo(TheTickStart.x, TheTickStart.y);
  1918.       Inc(TheTickStart.x, dTick.x);
  1919.       Inc(TheTickStart.y, dTick.y);
  1920.       if (FTickSize > 1) then
  1921.         ACanvas.LineTo(TheTickStart.x, TheTickStart.y);
  1922.       ZValue := GetNextMinorXValue(ZValue);
  1923.     end;
  1924.   end; {minor ticks}
  1925.  
  1926.   FireEvents := OldFireEvents;
  1927. end;
  1928.  
  1929. {------------------------------------------------------------------------------
  1930.      Function: TAngleAxis.FofZ
  1931.   Description: standard Z transform
  1932.        Author: Mat Ballard
  1933.  Date created: 01/18/2001
  1934. Date modified: 01/18/2001 by Mat Ballard
  1935.       Purpose: returns the change in pixel position on screen as a function of the real data ordinate Z
  1936.  Known Issues:
  1937.  ------------------------------------------------------------------------------}
  1938. function TAngleAxis.dFofZ(Z: Single): TPoint;
  1939. begin
  1940.   if (FLogScale) then
  1941.   begin
  1942.     Result.x := Round(FSin * FLength * ((Log10(Z / FMin)) / FLogSpan));
  1943.     Result.y := -Round(FCos * FLength * ((Log10(Z / FMin)) / FLogSpan));
  1944.   end
  1945.   else
  1946.   begin
  1947.     Result.x := Round(FSin * FLength * ((Z - FMin) / (FSpan)));
  1948.     Result.y := -Round(FCos * FLength * ((Z - FMin) / (FSpan)));
  1949.   end;
  1950. end;
  1951.  
  1952. {------------------------------------------------------------------------------
  1953.     Procedure: TAngleAxis.ClickedOn
  1954.   Description: Was this TRectangle clicked on ?
  1955.        Author: Mat Ballard
  1956.  Date created: 01/17/2001
  1957. Date modified: 01/17/2001 by Mat Ballard
  1958.       Purpose: screen click management
  1959.  Known Issues: overrides TRectangle.ClickedOn
  1960.  ------------------------------------------------------------------------------}
  1961. function TAngleAxis.ClickedOn(iX, iY: Integer): Boolean;
  1962. var
  1963.   Slope, Intercept, Distance: Single;
  1964. begin
  1965.   if ((FAngle = 0) or
  1966.       (FAngle = 90) or
  1967.       (FAngle = 180) or
  1968.       (FAngle = 270)) then
  1969.     Result := inherited ClickedOn(iX, iY)
  1970.   else
  1971.   begin
  1972.     Result := FALSE;
  1973.  
  1974.     if (iX < NoMath.Min(Left, FEndX)) then exit;
  1975.     if (iX > NoMath.Max(Left, FEndX)) then exit;
  1976.     if (iY < NoMath.Min(Top, FEndY)) then exit;
  1977.     if (iY > NoMath.Max(Top, FEndY)) then exit;
  1978.  
  1979.     Slope := - (Top - FEndY) / (Left - FEndX);
  1980.     Intercept := Top + Slope * Left;
  1981.     Distance := Abs((Slope * iX - Intercept + iY) * Sin(FAngleRadians));
  1982.     if (Distance < FTicksize) then
  1983.       Result := TRUE;
  1984.   end;
  1985. end;
  1986.  
  1987. {------------------------------------------------------------------------------
  1988.     Procedure: TAngleAxis.Outline
  1989.   Description: Draws an Outline around this AngleAxis
  1990.        Author: Mat Ballard
  1991.  Date created: 01/22/2001
  1992. Date modified: 01/22/2001 by Mat Ballard
  1993.       Purpose: gives the user a guide to what they are moving with the mouse
  1994.  Known Issues:
  1995.  ------------------------------------------------------------------------------}
  1996. procedure TAngleAxis.Outline(ACanvas: TCanvas);
  1997. var
  1998.   dP: TPoint;
  1999. begin
  2000.   ACanvas.Pen.Color := clBlack;
  2001.   ACanvas.Pen.Mode := pmNotXOR;
  2002.   ACanvas.Pen.Style := psDash;
  2003.  
  2004.   dP.x := Round(FTickSize * FCos);
  2005.   dP.y := -Round(FTickSize * FSin);
  2006.  
  2007.   ACanvas.Polygon([
  2008.     Point(Left + dP.x, Top + dP.y),
  2009.     Point(Left - dP.x, Top - dP.y),
  2010.     Point(FEndX - dP.x, FEndY - dP.y),
  2011.     Point(FEndX + dP.x, FEndY + dP.y)]);
  2012. end;
  2013.  
  2014.  
  2015. end.
  2016.