home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Chip 2001 October
/
Chip_2001-10_cd1.bin
/
zkuste
/
delphi
/
kompon
/
d123456
/
CHEMPLOT.ZIP
/
TPlot
/
Axis.pas
< prev
next >
Wrap
Pascal/Delphi Source File
|
2001-07-23
|
72KB
|
2,208 lines
unit Axis;
{$I Plot.inc}
{-----------------------------------------------------------------------------
The contents of this file are subject to the Q Public License
("QPL"); you may not use this file except in compliance
with the QPL. You may obtain a copy of the QPL from
the file QPL.html in this distribution, derived from:
http://www.trolltech.com/products/download/freelicense/license.html
The QPL prohibits development of proprietary software.
There is a Professional Version of this software available for this.
Contact sales@chemware.hypermart.net for more information.
Software distributed under the QPL is distributed on an "AS IS" basis,
WITHOUT WARRANTY OF ANY KIND, either expressed or implied. See the QPL for
the specific language governing rights and limitations under the QPL.
The Original Code is: Axis.pas, released 12 September 2000.
The Initial Developer of the Original Code is Mat Ballard.
Portions created by Mat Ballard are Copyright (C) 1999 Mat Ballard.
Portions created by Microsoft are Copyright (C) 1998, 1999 Microsoft Corp.
All Rights Reserved.
Contributor(s): Mat Ballard e-mail: mat.ballard@chemware.hypermart.net.
Last Modified: 04/09/2001
Current Version: 2.00
You may retrieve the latest version of this file from:
http://Chemware.hypermart.net/
This work was created with the Project JEDI VCL guidelines:
http://www.delphi-jedi.org/Jedi:VCLVCL
in mind.
Purpose:
To implement an Axis component for use by the main TPlot graphing component.
Known Issues:
History:
1.01 21 September 2000: fix FontWidth bug in TAxis.Draw
add LabelText property to TAxis (for columns)
-----------------------------------------------------------------------------}
interface
uses
Classes, SysUtils,
{$IFDEF WINDOWS}
WinTypes, WinProcs,
Graphics,
{$ENDIF}
{$IFDEF WIN32}
Windows,
Graphics,
{$ENDIF}
{$IFDEF LINUX}
Types,
QGraphics,
{$ENDIF}
{$IFNDEF NO_MATH}
Math,
{$ENDIF}
Misc, NoMath, Plotdefs, Titles;
{const}
type
TAxisType = (atPrimary, atSecondary, atTertiary, atZ);
TLabelFormat = (
lfGeneral, lfExponent, lfFixed, lfNumber, lfCurrency,
lfSI, lfPercent,
lfSeconds, lfMinutes, lfHours, lfDays, lfShortTime, lfShortDate);
{lfGeneral ... lfCurrency are just TFloatFormat.}
{}
{We then add SI and Percentage, then the rest are so that we can display times in various formats.}
{}
{NOTE: SI means the standard SI postfixes: p, n u, m, -, K, M, G, T}
{ TOnPositionChangeEvent = procedure(
Sender: TObject;
bIntercept: Boolean; did the Intercept change ? or the screen position ?
var TheIntercept: Single;
var ThePosition: Integer) of object;}
{Begin TAxisLabel declarations ------------------------------------------------}
TAxisLabel = class(TCaption)
private
FDirection: TDirection;
FDigits: Byte;
FPrecision: Byte;
FNumberFormat: TLabelFormat;
{OnChange: TNotifyEvent; is in TRectangle !}
procedure SetDirection(Value: TDirection);
procedure SetDigits(Value: Byte);
procedure SetPrecision(Value: Byte);
procedure SetNumberFormat(Value: TLabelFormat);
protected
public
Constructor Create(AOwner: TPersistent); override;
{The standard constructor, where standard properties are set.}
Destructor Destroy; override;
{The standard destructor, where the OnChange event is "freed".}
{procedure Assign(Source: TPersistent); override;}
procedure AssignTo(Dest: TPersistent); override;
published
Property Direction: TDirection read FDirection write SetDirection;
{Is the Label Horizontal (X) or Vertical (Y or Y2).}
Property Digits: Byte read FDigits write SetDigits;
{This (and Precision) control the numeric format of the Axis Labels.
See the Borland documentation on FloatToStrF for the precise meaning of
this property, or simply experiment in the IDE designer.}
Property Precision: Byte read FPrecision write SetPrecision;
{This (and Digits) control the numeric format of the Axis Labels.
See the Borland documentation on FloatToStrF for the precise meaning of
this property, or simply experiment in the IDE designer.}
Property NumberFormat: TLabelFormat read FNumberFormat write SetNumberFormat;
{This property controls how the numbers of the Axis labels are displayed.}
end;
{Begin TAxis declarations ---------------------------------------------------}
TAxis = class(TRectangle)
private
FArrowSize: Byte;
FAutoScale: Boolean;
FAutoZero: Boolean;
FAxisType: TAxisType;
FDirection: TDirection;
FIntercept: Single;
FLabels: TAxisLabel;
FLabelSeries: TPersistent;
FLimitLower: Single;
FLimitUpper: Single;
FLimitsVisible: Boolean;
FLogScale: Boolean;
FLogSpan: Single;
FMin: Single;
FMax: Single;
FPen: TPen;
FStepSize: Single;
FStepStart: Single;
FSpan: Single;
FTickMinor: Byte;
FTickSign: Integer;
FTickSize: Byte;
FTickDirection: TOrientation;
FTickNum: Byte;
FTitle: TTitle;
FZoomIntercept: Single;
FZoomMin: Single;
FZoomMax: Single;
PrecisionAdded: Integer;
procedure SetupHorizontalEnvelope;
procedure SetupVerticalEnvelope;
protected
{Set procedures:}
procedure SetArrowSize(Value: Byte);
procedure SetAutoScale(Value: Boolean);
procedure SetAutoZero(Value: Boolean);
procedure SetDirection(Value: TDirection);
procedure SetIntercept(Value: Single);
procedure SetLimitLower(Value: Single);
procedure SetLimitUpper(Value: Single);
procedure SetLimitsVisible(Value: Boolean);
procedure SetLogScale(Value: Boolean);
procedure SetMin(Value: Single);
procedure SetMax(Value: Single);
procedure SetPen(Value: TPen);
procedure SetStepSize(Value: Single);
procedure SetStepStart(Value: Single);
procedure SetTickMinor(Value: Byte);
{procedure SetTickNum(Value: Byte);}
procedure SetTickSize(Value: Byte);
procedure SetOrientation(Value: TOrientation);
procedure StyleChange(Sender: TObject); virtual;
procedure TitleChange(Sender: TObject); virtual;
public
procedure ReScale;
Property AxisType: TAxisType read FAxisType write FAxisType;
{What sort of axis is this ?}
Property ZoomIntercept: Single read FZoomIntercept write FZoomIntercept;
{The (old) ZOOMED OUT Intercept in data co-ordinates.}
Property ZoomMin: Single read FZoomMin write FZoomMin;
{The (old) ZOOMED OUT minimum, Left or Bottom of the Axis, in data co-ordinates.}
Property ZoomMax: Single read FZoomMax write FZoomMax;
{The (old) ZOOMED OUT maximum, Right or Top of the Axis, in data co-ordinates.}
Constructor Create(AOwner: TPersistent); {$IFDEF DELPHI4_UP}reintroduce;{$ENDIF} {squelch the error message}
{The standard constructor, where sub-components are created, and standard
properties are set.}
Destructor Destroy; override;
{The standard destructor, where sub-components and the OnChange event is "freed".}
procedure Draw(ACanvas: TCanvas; LimitPos: Integer); virtual;
{This draws the Axis on the given Canvas.}
function GetNextXValue(XValue: Single): Single;
{This calculates the next tick point. Used externally by TCustomPlot.DrawGrid}
function LabelToStrF(Value: Single): String;
{This method converts a number to a string, given the current Labels' NumberFormat.}
function StrToLabel(Value: String): Single;
{This method converts a string to a number, given the current Labels' NumberFormat.}
function FofX(X: Single): Integer;
{This converts an X data value to a screen X co-ordinate.}
function FofY(Y: Single): Integer;
{This converts a Y data value to a screen Y co-ordinate.}
function XofF(F: Integer): Single;
{This converts a screen X co-ordinate to a X data value.}
function YofF(F: Integer): Single;
{This converts a screen Y co-ordinate to a Y data value.}
procedure SetLabelSeries(Value: TPersistent);
{This is called by a series to set the X data as strings.}
procedure SetMinFromSeries(Value: Single);
{This sets the Min property of the Axis. It is used exclusively by TSeries.}
procedure SetMaxFromSeries(Value: Single);
{This sets the Max property of the Axis. It is used exclusively by TSeries.
Exactly how it affects the Axis depends on TPlot.DisplayMode.}
procedure SetMinMaxFromSeries(AMin, AMax: Single);
{This sets the Min and Max properties of the Axis. It is used exclusively by TSeries.
Exactly how it affects the Axis depends on TPlot.DisplayMode.}
{procedure Assign(Source: TPersistent); override;}
procedure AssignTo(Dest: TPersistent); override;
published
Property ArrowSize: Byte read FArrowSize write SetArrowSize;
{This is the size (in pixels) of the arrowhead on the Axis.}
Property AutoScale: Boolean read FAutoScale write SetAutoScale default TRUE;
{Do we use the StepSize property or does TPlot work them out ?}
Property AutoZero: Boolean read FAutoZero write SetAutoZero;
{Do we use the StepSize property or does TPlot work them out ?}
Property Title: TTitle read FTitle write FTitle;
{The Title on and of the Axis. Note that the Title can be clicked and dragged
around the Axis.}
Property Direction: TDirection read FDirection write SetDirection;
{Is the Axis Horizontal (X) or Vertical (Y or Y2).}
Property Intercept: Single read FIntercept write SetIntercept;
{The intercept of this Axis on the complementary Axis.}
Property Labels: TAxisLabel read FLabels write FLabels;
{The numerals on the Axis.}
property LimitLower: Single read FLimitLower write SetLimitLower;
{The lower limit, drawn perpendicular to the axis with a dashed line, if LimitsVisible.}
property LimitUpper: Single read FLimitUpper write SetLimitUpper;
{The upper limit, drawn perpendicular to the axis with a dashed line, if LimitsVisible.}
property LimitsVisible: Boolean read FLimitsVisible write SetLimitsVisible;
{Determines if Limits perpendicular to the Axis are drawn.}
Property LogScale: Boolean read FLogScale write SetLogScale;
{Is this Axis on a logarithmic scale ?}
Property Min: Single read FMin write SetMin;
{The minimum, Left or Bottom of the Axis, in data co-ordinates.}
Property Max: Single read FMax write SetMax;
{The maximum, Right or Top of the Axis, in data co-ordinates.}
Property Pen: TPen read FPen write SetPen;
{The Pen that the Axis is drawn with.}
Property StepSize: Single read FStepSize write SetStepSize;
{The interval between tick (and labels) on the Axis.}
{}
{If the axis is a Log Scale, then this is the multiple, not the interval !}
Property StepStart: Single read FStepStart write SetStepStart;
{The interval between tick (and labels) on the Axis.}
Property TickMinor: Byte read FTickMinor write SetTickMinor;
{Sets the number of minor ticks between labels.}
Property TickSize: Byte read FTickSize write SetTickSize;
{The Length of the Ticks, in screen pixels.}
Property TickDirection: TOrientation read FTickDirection write SetOrientation;
{Are the Ticks to the left or right of the Axis ?}
{Property TickNum: Byte read FTickNum write SetTickNum;}
{The approximate number of ticks: TPlot recalculates the number of ticks
depending on the StepSize.}
end;
TAngleAxis = class(TAxis)
{The TAngleAxis class is a TAxis that is at any angle.
It will be used in the 3D and Polar PlotTypes.}
{Note that the (Left, Top) is now interpreted as the origin}
private
FAngle: Word;
FAngleRadians: Single;
FLength: Word;
FZInterceptY: Single;
FEndX,
FEndY: Integer;
FSin,
FCos,
FSinM30,
FCosM30,
FSinP30,
FCosP30: Extended;
protected
procedure SetAngle(Value: Word);
procedure SetLength(Value: Word);
procedure SetZInterceptY(Value: Single);
public
property EndX: Integer read FEndX;
property EndY: Integer read FEndY;
constructor Create(AOwner: TPersistent);
destructor Destroy; override;
function ClickedOn(iX, iY: Integer): Boolean; override;
{Was this Z Axis clicked on ?}
procedure Outline(ACanvas: TCanvas); override;
function FofZ(Z: Single): Integer;
function dFofZ(Z: Single): TPoint;
procedure Draw(ACanvas: TCanvas; LimitPos: Integer); override;
published
property Angle: Word read FAngle write SetAngle;
{Angle is the angle (in degrees) between the vertical Y Axis, and this axis,
in a clockwise direction.}
property Length: Word read FLength write SetLength;
{This is the (screen) length of the axis.}
property ZInterceptY: Single read FZInterceptY write SetZInterceptY;
{The intercept of this Z Axis on the Y Axis.}
{}
{Z Axex have _TWO_ intercepts - an X and a Y.}
{}
{Note that we now, in TAngleAxis, interpret the Intercept property as the
intercept of the Z Axis with the X Axis.}
end;
implementation
uses
Data, Plot;
{TAxislabel methods ---------------------------------------------------------}
{Constructor and Destructor:-------------------------------------------------}
{------------------------------------------------------------------------------
Constructor: TAxisLabel.Create
Description: standard Constructor
Author: Mat Ballard
Date created: 04/25/2000
Date modified: 04/25/2000 by Mat Ballard
Purpose: sets the Precision and Digits Properties
Known Issues:
------------------------------------------------------------------------------}
Constructor TAxisLabel.Create(AOwner: TPersistent);
begin
{First call the ancestor:}
inherited Create(AOwner);
{Put your own initialisation (memory allocation, etc) here:}
{we insert the default values that cannot be "defaulted":}
FPrecision := 3;
FDigits := 1;
end;
{------------------------------------------------------------------------------
Destructor: TAxisLabel.Destroy
Description: standard Destructor
Author: Mat Ballard
Date created: 04/25/2000
Date modified: 04/25/2000 by Mat Ballard
Purpose: frees the OnChange event
Known Issues:
------------------------------------------------------------------------------}
Destructor TAxisLabel.Destroy;
begin
OnChange := nil;
{Put your de-allocation, etc, here:}
{then call ancestor:}
inherited Destroy;
end;
{End Constructor and Destructor:---------------------------------------------}
{------------------------------------------------------------------------------
Procedure: TAxisLabel.Assign
Description: standard Assign method
Author: Mat Ballard
Date created: 07/06/2000
Date modified: 07/06/2000 by Mat Ballard
Purpose: implements Assign
Known Issues:
------------------------------------------------------------------------------}
{procedure TAxisLabel.Assign(Source: TPersistent);
begin
inherited Assign(Source);
FDigits := TAxisLabel(Source).Digits;
FNumberFormat := TAxisLabel(Source).NumberFormat;
FPrecision := TAxisLabel(Source).Precision;
end;}
{------------------------------------------------------------------------------
Procedure: TAxisLabel.Assign
Description: standard Assign method
Author: Mat Ballard
Date created: 07/06/2000
Date modified: 07/06/2000 by Mat Ballard
Purpose: implements Assign
Known Issues:
------------------------------------------------------------------------------}
procedure TAxisLabel.AssignTo(Dest: TPersistent);
begin
inherited AssignTo(Dest);
TAxisLabel(Dest).Digits := FDigits;
TAxisLabel(Dest).NumberFormat := FNumberFormat;
TAxisLabel(Dest).Precision := FPrecision;
end;
{Begin Set Procedures --------------------------------------------------------}
{------------------------------------------------------------------------------
Procedure: TAxisLabel.SetDirection
Description: standard property Set procedure
Author: Mat Ballard
Date created: 03/25/2001
Date modified: 03/25/2001 by Mat Ballard
Purpose: sets the Direction Property
Known Issues:
------------------------------------------------------------------------------}
procedure TAxisLabel.SetDirection(Value: TDirection);
begin
if (FDirection = Value) then exit;
FDirection := Value;
if Assigned(OnChange) then OnChange(Self);
end;
{------------------------------------------------------------------------------
Procedure: TAxisLabel.SetDigits
Description: standard property Set procedure
Author: Mat Ballard
Date created: 04/25/2000
Date modified: 04/25/2000 by Mat Ballard
Purpose: sets the Digits Property
Known Issues:
------------------------------------------------------------------------------}
procedure TAxisLabel.SetDigits(Value: Byte);
begin
if (FDigits = Value) then exit;
if (FDigits > 18) then exit;
case FNumberFormat of
lfGeneral: if (FDigits > 4) then exit;
lfExponent: if (FDigits > 4) then exit;
end;
FDigits := Value;
if Assigned(OnChange) then OnChange(Self);
end;
{------------------------------------------------------------------------------
Procedure: TAxisLabel.SetPrecision
Description: standard property Set procedure
Author: Mat Ballard
Date created: 04/25/2000
Date modified: 04/25/2000 by Mat Ballard
Purpose: sets the Precision Property
Known Issues:
------------------------------------------------------------------------------}
procedure TAxisLabel.SetPrecision(Value: Byte);
begin
if (FPrecision = Value) then exit;
if (FPrecision > 7) then exit;
FPrecision := Value;
if Assigned(OnChange) then OnChange(Self);
end;
{------------------------------------------------------------------------------
Procedure: TAxisLabel.SetNumberFormat
Description: standard property Set procedure
Author: Mat Ballard
Date created: 04/25/2000
Date modified: 04/25/2000 by Mat Ballard
Purpose: sets the NumberFormat Property
Known Issues:
------------------------------------------------------------------------------}
procedure TAxisLabel.SetNumberFormat(Value: TLabelFormat);
begin
if (FNumberFormat = Value) then exit;
FNumberFormat := Value;
case FNumberFormat of
lfGeneral: if (FDigits > 4) then FDigits := 4;
lfExponent: if (FDigits > 4) then FDigits := 4;
end;
if Assigned(OnChange) then OnChange(Self);
end;
{TAxis methods --------------------------------------------------------------}
{Constructor and Destructor:-------------------------------------------------}
{------------------------------------------------------------------------------
Constructor: TAxis.Create
Description: standard Constructor
Author: Mat Ballard
Date created: 04/25/2000
Date modified: 04/25/2000 by Mat Ballard
Purpose: creates the subcomponents and sets various Properties
Known Issues:
------------------------------------------------------------------------------}
Constructor TAxis.Create(AOwner: TPersistent);
begin
{First call the ancestor:}
inherited Create(AOwner);
{Create Pen:}
FPen := TPen.Create;
FPen.Color := clRed;
FLabels := TAxisLabel.Create(Self);
FLabels.OnChange := StyleChange;
FLabelSeries := nil;
{create the Title geometry manager:}
FTitle := TTitle.Create(Self);
FTitle.OnChange := StyleChange;
FTitle.OnCaptionChange := TitleChange;
FTitle.Caption := 'X-' + sAxis;
FTitle.Font.Size := MEDIUM_FONT_SIZE;
FArrowSize := 10;
FAutoScale := TRUE;
FAxisType := atPrimary;
SetDirection(drHorizontal);
FIntercept := 0;
FMin := 0;
FMax := 10;
FLimitLower := 3;
FLimitUpper := 7;
FTickDirection := orRight;
FTickSize := 10;
FTickNum := 5;
Alignment := taRightJustify;
Visible := TRUE;
ReScale;
end;
{------------------------------------------------------------------------------
Destructor: TAxis.Destroy
Description: standard Destructor
Author: Mat Ballard
Date created: 04/25/2000
Date modified: 04/25/2000 by Mat Ballard
Purpose: frees the subcomponents and the OnChange event
Known Issues:
------------------------------------------------------------------------------}
Destructor TAxis.Destroy;
begin
OnChange := nil;
{Put your de-allocation, etc, here:}
FLabels.Free;
FPen.Free;
FTitle.Free;
{then call ancestor:}
inherited Destroy;
end;
{End Constructor and Destructor:---------------------------------------------}
{------------------------------------------------------------------------------
Procedure: TAxis.TitleChange
Description: sets the Name and Label's Name
Author: Mat Ballard
Date created: 04/25/2000
Date modified: 04/25/2000 by Mat Ballard
Purpose: responds to a change in the Title
Known Issues:
------------------------------------------------------------------------------}
procedure TAxis.TitleChange(Sender: TObject);
begin
if (Pos('xis', FTitle.Caption) > 0) then
begin
Name := FTitle.Caption;
FLabels.Name := FTitle.Caption + ' ' + sLabels;
end
else
begin
{Stick Axis in in the names:}
Name := FTitle.Caption + ' ' + sAxis;
FLabels.Name := FTitle.Caption + ' ' + sAxis + ' ' + sLabels;
end;
end;
{Begin normal Set Procedures -------------------------------------------------}
{------------------------------------------------------------------------------
Procedure: TAxis.SetArrowSize
Description: standard property Set procedure
Author: Mat Ballard
Date created: 04/25/2000
Date modified: 04/25/2000 by Mat Ballard
Purpose: sets the ArrowSize Property
Known Issues:
------------------------------------------------------------------------------}
procedure TAxis.SetArrowSize(Value: Byte);
begin
if (Value = FArrowSize) then exit;
FArrowSize := Value;
StyleChange(Self);
end;
{------------------------------------------------------------------------------
Procedure: TAxis.SetAutoScale
Description: standard property Set procedure
Author: Mat Ballard
Date created: 04/25/2000
Date modified: 04/25/2000 by Mat Ballard
Purpose: sets the AutoScale Property
Known Issues:
------------------------------------------------------------------------------}
procedure TAxis.SetAutoScale(Value: Boolean);
begin
if (Value = FAutoScale) then exit;
FAutoScale := Value;
StyleChange(Self);
end;
{------------------------------------------------------------------------------
Procedure: TAxis.SetAutoZero
Description: standard property Set procedure
Author: Mat Ballard
Date created: 04/25/2000
Date modified: 04/25/2000 by Mat Ballard
Purpose: sets the AutoZero Property
Known Issues:
------------------------------------------------------------------------------}
procedure TAxis.SetAutoZero(Value: Boolean);
begin
if (Value = FAutoZero) then exit;
FAutoZero := Value;
StyleChange(Self);
end;
{------------------------------------------------------------------------------
Procedure: TAxis.SetDirection
Description: standard property Set procedure
Author: Mat Ballard
Date created: 04/25/2000
Date modified: 04/25/2000 by Mat Ballard
Purpose: sets the Direction Property
Known Issues:
------------------------------------------------------------------------------}
procedure TAxis.SetDirection(Value: TDirection);
begin
if (Value = FDirection) then exit;
FDirection := Value;
FTitle.Direction := Value;
{TTitle.SetDirection usually fires the OnChange:}
if ((not FTitle.Visible) and
assigned(OnChange) and
Visible) then OnChange(Self);
end;
{------------------------------------------------------------------------------
Procedure: TAxis.SetIntercept
Description: standard property Set procedure
Author: Mat Ballard
Date created: 04/25/2000
Date modified: 04/25/2000 by Mat Ballard
Purpose: sets the Intercept virtual Property
Known Issues:
------------------------------------------------------------------------------}
procedure TAxis.SetIntercept(Value: Single);
begin
if (FIntercept = Value) then exit;
FIntercept := Value;
{FAutoScale := FALSE;}
StyleChange(Self);
end;
procedure TAxis.SetLimitLower(Value: Single);
begin
FLimitLower := Value;
if (FLimitLower > FLimitUpper) then
FLimitUpper := Value;
StyleChange(Self);
end;
procedure TAxis.SetLimitUpper(Value: Single);
begin
FLimitUpper := Value;
if (FLimitUpper < FLimitLower) then
FLimitLower := Value;
StyleChange(Self);
end;
procedure TAxis.SetLimitsVisible(Value: Boolean);
begin
FLimitsVisible := Value;
StyleChange(Self);
end;
{------------------------------------------------------------------------------
Procedure: TAxis.SetLogScale
Description: standard property Set procedure
Author: Mat Ballard
Date created: 04/25/2000
Date modified: 04/25/2000 by Mat Ballard
Purpose: sets the LogScale Property
Known Issues:
------------------------------------------------------------------------------}
procedure TAxis.SetLogScale(Value: Boolean);
begin
if (Value = FLogScale) then exit;
if (Value = TRUE) then
begin {we are going to a log scale:}
if (FMin <= 0) then exit;
if (FMax <= 0) then exit;
end;
FLogScale := Value;
ReScale;
end;
{------------------------------------------------------------------------------
Procedure: TAxis.SetMin
Description: standard property Set procedure
Author: Mat Ballard
Date created: 04/25/2000
Date modified: 04/25/2000 by Mat Ballard
Purpose: sets the Min Property
Known Issues:
------------------------------------------------------------------------------}
procedure TAxis.SetMin(Value: Single);
begin
if (Value = FMin) then exit;
if (Value >= FMax) then exit;
if ((Value <= 0) and (FLogScale)) then exit;
FMin := Value;
ReScale;
end;
{------------------------------------------------------------------------------
Procedure: TAxis.SetMax
Description: standard property Set procedure
Author: Mat Ballard
Date created: 04/25/2000
Date modified: 04/25/2000 by Mat Ballard
Purpose: sets the Max Property
Known Issues:
------------------------------------------------------------------------------}
procedure TAxis.SetMax(Value: Single);
begin
if (Value = FMax) then exit;
if (Value <= FMin) then exit;
FMax := Value;
ReScale;
end;
{------------------------------------------------------------------------------
Procedure: TAxis.SetMinFromSeries
Description: property Setting procedure for calling by a Series
Author: Mat Ballard
Date created: 04/25/2000
Date modified: 04/25/2000 by Mat Ballard
Purpose: sets the Min Property when new data is added to a Series
Known Issues:
------------------------------------------------------------------------------}
procedure TAxis.SetMinFromSeries(Value: Single);
begin
if (Value >= FMin) then exit;
if ((Value <= 0) and (FLogScale)) then exit;
FMin := Value;
Rescale;
end;
{------------------------------------------------------------------------------
Procedure: TAxis.SetMaxFromSeries
Description: property Setting procedure for calling by a Series
Author: Mat Ballard
Date created: 04/25/2000
Date modified: 04/25/2000 by Mat Ballard
Purpose: sets the Max Property when new data is added to a Series
Known Issues:
------------------------------------------------------------------------------}
procedure TAxis.SetMaxFromSeries(Value: Single);
begin
if (Value <= FMax) then exit;
FMax := Value;
if ((TPlot(Owner).DisplayMode = dmRun) and
(FDirection = drHorizontal)) then
begin
{We are in a "run", and so we can expect more data with increasing X values.
Rather than force a complete screen re-draw every time a data point is
added, we extend the X Axis by 100%:}
FMax := 2.0 * FMax;
end;
Rescale;
end;
{------------------------------------------------------------------------------
Procedure: TAxis.SetMinMaxFromSeries
Description: multiple property Setting procedure for calling by a Series
Author: Mat Ballard
Date created: 05/29/2001
Date modified: 05/29/2001 by Mat Ballard
Purpose: sets the Min Property when new data is added to a Series
Known Issues:
------------------------------------------------------------------------------}
procedure TAxis.SetMinMaxFromSeries(AMin, AMax: Single);
begin
if (AMin >= AMax) then exit;
if ((AMin = FMin) and (AMax = FMax)) then exit;
if ((AMin <= 0) and (FLogScale)) then exit;
FMin := AMin;
FMax := AMax;
Rescale;
end;
{------------------------------------------------------------------------------
Procedure: TAxis.SetPen
Description: standard property Set procedure
Author: Mat Ballard
Date created: 04/25/2000
Date modified: 04/25/2000 by Mat Ballard
Purpose: sets the Pen Property
Known Issues:
------------------------------------------------------------------------------}
procedure TAxis.SetPen(Value: TPen);
begin
FPen.Assign(Value);
{FFont.Color := FPen.Color;
FLabels.Font.Color := FPen.Color;}
StyleChange(Self);
end;
{------------------------------------------------------------------------------
Procedure: TAxis.SetOrientation
Description: standard property Set procedure
Author: Mat Ballard
Date created: 04/25/2000
Date modified: 04/25/2000 by Mat Ballard
Purpose: sets the Orientation Property
Known Issues:
------------------------------------------------------------------------------}
procedure TAxis.SetOrientation(Value: TOrientation);
begin
{if (Value = FTickDirection) then exit;}
FTickDirection := Value;
if (FTickDirection = orRight) then
FTickSign := 1
else
FTickSign := -1;
{check the names of the titles and labels}
{TitleChange(Self);}
StyleChange(Self);
end;
{------------------------------------------------------------------------------
Procedure: TAxis.SetStepSize
Description: standard property Set procedure
Author: Mat Ballard
Date created: 04/25/2000
Date modified: 04/25/2000 by Mat Ballard
Purpose: sets the StepSize (distance between ticks) Property
Known Issues:
------------------------------------------------------------------------------}
procedure TAxis.SetStepSize(Value: Single);
begin
if (FAutoScale) then exit;
if (Value = FStepSize) then exit;
FStepSize := Value;
StyleChange(Self);
end;
{------------------------------------------------------------------------------
Procedure: TAxis.SetStepStart
Description: standard property Set procedure
Author: Mat Ballard
Date created: 04/25/2000
Date modified: 04/25/2000 by Mat Ballard
Purpose: sets the StepStart (where ticks start) Property
Known Issues:
------------------------------------------------------------------------------}
procedure TAxis.SetStepStart(Value: Single);
begin
if (FAutoScale) then exit;
if (Value = FStepStart) then exit;
FStepStart := Value;
StyleChange(Self);
end;
{------------------------------------------------------------------------------
Procedure: TAxis.SetTickMinor
Description: standard property Set procedure
Author: Mat Ballard
Date created: 04/25/2000
Date modified: 04/25/2000 by Mat Ballard
Purpose: sets the TickMinor (number of minor ticks) Property
Known Issues:
------------------------------------------------------------------------------}
procedure TAxis.SetTickMinor(Value: Byte);
begin
if (Value = FTickMinor) then exit;
{limit the number of minors:}
if (Value > 9) then
Value := 9;
FTickMinor := Value;
StyleChange(Self);
end;
{------------------------------------------------------------------------------
Procedure: TAxis.SetTickNum
Description: standard property Set procedure
Author: Mat Ballard
Date created: 04/25/2000
Date modified: 04/25/2000 by Mat Ballard
Purpose: sets the TickNum Property
Known Issues:
------------------------------------------------------------------------------
procedure TAxis.SetTickNum(Value: Byte);
begin
if (Value = FTickNum) then exit;
FTickNum := Value;
ReScale;
end;}
{------------------------------------------------------------------------------
Procedure: TAxis.SetTickSize
Description: standard property Set procedure
Author: Mat Ballard
Date created: 04/25/2000
Date modified: 04/25/2000 by Mat Ballard
Purpose: sets the TickSize Property
Known Issues:
------------------------------------------------------------------------------}
procedure TAxis.SetTickSize(Value: Byte);
begin
if (Value = FTickSize) then exit;
FTickSize := Value;
ReScale;
end;
{Various other Functions and Procedures--------------------------------------}
{------------------------------------------------------------------------------
Procedure: TAxis.StyleChange
Description: event firing proedure
Author: Mat Ballard
Date created: 04/25/2000
Date modified: 04/25/2000 by Mat Ballard
Purpose: fires the OnChange event
Known Issues:
------------------------------------------------------------------------------}
procedure TAxis.StyleChange(Sender: TObject);
begin
if (assigned(OnChange) and Visible) then OnChange(Sender);
end;
{------------------------------------------------------------------------------
Procedure: TAxis.ReScale
Description: geometry manager
Author: Mat Ballard
Date created: 04/25/2000
Date modified: 04/25/2000 by Mat Ballard
Purpose: determines the ticks and labels
Known Issues:
------------------------------------------------------------------------------}
procedure TAxis.ReScale;
{This method determines the Axis geometry (StepStart and StepSize).}
var
Exponent: Integer;
RoughStepSize: Single;
Mantissa: Extended;
begin
PrecisionAdded := 0;
if (not FAutoScale) then
begin
FStepStart := FMin;
FSpan := FMax - FMin;
exit;
end;
if (FLogScale) then
begin
FLogSpan := Log10(FMax / FMin);
DeSci(FMin, Mantissa, Exponent);
{work out a starting point, 1 x 10^Exponent:}
FStepStart := IntPower(10.0, Exponent);
if (not FAutoScale) then
begin
if (FLogSpan >= 2) then
begin
{many decades of data:}
if (not FAutoScale) then
FStepSize := 10;
end
else
begin
RoughStepSize := FLogSpan / (FTickNum+1);
RoughStepSize := Power(10.0, RoughStepSize);
if (RoughStepSize > 1.5) then
begin
{get the Mantissa and Exponent:}
DeSci(RoughStepSize, Mantissa, Exponent);
FStepSize := Round(Mantissa) * IntPower(10.0, Exponent);
end
else
begin
FStepSize := RoughStepSize;
end;
{$IFDEF DELPHI3_UP}
Assert(FStepSize > 1.0,
'TAxis.ReScale ' + sRescale1 +
FloatToStr(FStepSize));
{$ENDIF}
end; {how big is FLogSpan ?}
end; {not AutoScale}
while (FStepStart <= FMin) do
{go to next multiple of FStepSize:}
FStepStart := FStepSize * FStepStart;
end
else
begin {normal linear scale:}
FSpan := FMax - FMin;
if ((FAutoScale) or (FStepSize <= 0)) then
begin
RoughStepSize := FSpan / (FTickNum+1);
{get the Mantissa and Exponent:}
DeSci(RoughStepSize, Mantissa, Exponent);
FStepSize := Round(Mantissa) * IntPower(10.0, Exponent);
{FTickNum := Trunc(FSpan / FStepSize);}
end;
FStepStart := FStepSize * Int((FMin / FStepSize) + 0.999);
{increase FStepStart by FStepSize:}
while (FStepStart <= FMin) do
FStepStart := FStepSize + FStepStart;
{PrecisionAdded is the added precision needed to display numerical labels with
sufficient precision to be distinguishable:}
if (Exponent <= -FLabels.FDigits) then
PrecisionAdded := 1 - FLabels.FDigits - Exponent;
end;
StyleChange(Self);
end;
{------------------------------------------------------------------------------
Procedure: TAxis.GetNextXValue
Description: auxilary procedure for Drawing
Author: Mat Ballard
Date created: 02/28/2001
Date modified: 02/28/2001 by Mat Ballard
Purpose: calculates the next tick point
Known Issues:
------------------------------------------------------------------------------}
function TAxis.GetNextXValue(XValue: Single): Single;
begin
if (FLogScale) then
GetNextXValue := XValue * FStepSize
else
GetNextXValue := XValue + FStepSize;
end;
{------------------------------------------------------------------------------
Procedure: TAxis.Draw
Description: standard Drawing procedure
Author: Mat Ballard
Date created: 04/25/2000
Date modified: 04/25/2000 by Mat Ballard
Purpose: draws the Axis on a given canvas
Known Issues:
------------------------------------------------------------------------------}
procedure TAxis.Draw(ACanvas: TCanvas; LimitPos: Integer);
{Comments:
This method is quite complex, in a tedious way.
It has to account for the following variations:
1. Visible or not;
2. Arrows visible or not;
3. Axis direction (Horizontal or vertical);
4. Tick (and Label and Title) direction);
5. Title Alignment Direction and Orientation;
6. Tick, Label and Title visibility.
An added complication is that we must generate a vertical font for the Title
of vertical axes. Note that this only works with TrueType fonts - NOT fonts
that are purely screen or printer.}
var
i,
iX, iY,
iXLabel, iYLabel,
iXTick, iYTick,
FontHeight,
FontWidth,
iFontWidth,
FontDescent,
MinorTickSize: Integer;
MinorStepSize,
MinorStepStart,
YValue,
XValue: Single;
DoTextLabels: Boolean;
TheText: String;
function GetNextMinorXValue(XValue: Single): Single;
begin
if (FLogScale) then
GetNextMinorXValue := XValue * MinorStepSize
else
GetNextMinorXValue := XValue + MinorStepSize;
end;
procedure XLabelOut;
begin
iFontWidth := ACanvas.TextWidth(TheText);
if (iFontWidth > FontWidth) then
FontWidth := iFontWidth;
if (FLabels.Direction = drHorizontal) then
ACanvas.TextOut(iX - iFontWidth div 2, iYLabel, TheText)
else
if (FTickDirection = orLeft) then
TextOutAngle(ACanvas, 90, iX - FontHeight div 2, iYLabel, TheText)
else
TextOutAngle(ACanvas, 90, iX - FontHeight div 2, iYLabel + FontWidth, TheText);
end;
procedure YLabelOut;
begin
iFontWidth := ACanvas.TextWidth(TheText);
if (iFontWidth > FontWidth) then
FontWidth := iFontWidth;
if (FLabels.Direction = drHorizontal) then
begin
if (FTickDirection = orLeft) then
ACanvas.TextOut(iXLabel - iFontWidth, iY - FontHeight + FontDescent, TheText)
else
ACanvas.TextOut(iXLabel, iY - FontHeight + FontDescent, TheText)
end
else
if (FTickDirection = orLeft) then
TextOutAngle(ACanvas, 90, iXLabel - FontHeight, iY + FontWidth div 2, TheText)
else
TextOutAngle(ACanvas, 90, iXLabel, iY + FontWidth div 2, TheText);
end;
procedure DoHorzArrow(Point, Size: Integer);
begin
ACanvas.MoveTo(Point + Size, Top);
ACanvas.LineTo(Point, MidY);
ACanvas.LineTo(Point + Size, Bottom);
end;
procedure DoArrow(P1, P2, P3: TPoint);
begin
ACanvas.MoveTo(P1.x, P1.y);
ACanvas.LineTo(P2.x, P2.y);
ACanvas.LineTo(P3.x, P3.y);
end;
begin
{the most common reason for exit:}
if (not Visible) then exit;
{$IFDEF DELPHI3_UP}
Assert(ACanvas <> nil, 'TAxis.Draw: ' + sACanvasIsNil);
{$ENDIF}
FontWidth := 1;
ACanvas.Pen.Assign(FPen);
if (FLabels.Visible) then
begin
ACanvas.Font.Assign(FLabels.Font);
FontHeight := ACanvas.TextHeight('9');
FontWidth := ACanvas.TextWidth('9');
{We could call GetOutlineTextMetrics to get
the Descent (gap between baseline and bottom of a font), but:}
FontDescent := FontHeight div 5;
end;
{Provide ability for Y Axis labels:}
DoTextLabels := FALSE;
if (FLabelSeries <> nil) then
if (TSeries(FLabelSeries).XStringData <> nil) then
if (TSeries(FLabelSeries).XStringData.Count > 0) then
DoTextLabels := TRUE;
if (FDirection = drHorizontal) then
begin
{Draw the axis:}
ACanvas.MoveTo(Left, MidY);
ACanvas.LineTo(Right, MidY);
{Draw the arrows on the axis:}
if (FArrowSize > 0) then
begin {taCenter therefore means no arrows !}
if (Alignment = taLeftJustify) then
DoArrow(Point(Left+FArrowSize, Top), Point(Left, MidY), Point(Left+FArrowSize, Bottom));
if (Alignment = taRightJustify) then
DoArrow(Point(Right-FArrowSize, Top), Point(Right, MidY), Point(Right-FArrowSize, Bottom));
end;
if (Self.LimitsVisible) then
begin
ACanvas.Pen.Style := psDot;
iX := Self.FofX(FLimitLower);
ACanvas.MoveTo(iX, Self.MidY);
ACanvas.LineTo(iX, LimitPos);
iX := Self.FofX(FLimitUpper);
ACanvas.MoveTo(iX, Self.MidY);
ACanvas.LineTo(iX, LimitPos);
ACanvas.Pen.Style := Self.Pen.Style;
end;
iY := MidY;
iYTick := MidY + FTickSign*FTickSize;
iYLabel := iYTick;
if ((FTickDirection = orLeft) and
(FLabels.Direction = drHorizontal)) then
Dec(iYLabel, FontHeight);
if (DoTextLabels) then
begin
{Text instead of Numeric Labels on the axis:}
for i := 0 to TSeries(FLabelSeries).XStringData.Count-1 do
begin
iX := FofX(TSeries(FLabelSeries).XData^[i]);
{Major Ticks on the axis:}
if (FTickSize > 0) then
begin
ACanvas.MoveTo(iX, iY);
ACanvas.LineTo(iX, iYTick);
end;
if (FLabels.Visible) then
begin
TheText := TSeries(FLabelSeries).XStringData.Strings[i];
XLabelOut;
end;
end;
end
else
begin
{Normal numeric labels:}
XValue := FStepStart;
while (XValue < FMax) do
begin
iX := FofX(XValue);
{Major Ticks on the axis:}
if (FTickSize > 0) then
begin
ACanvas.MoveTo(iX, iY);
ACanvas.LineTo(iX, iYTick);
end;
{Numeric labels:}
if (FLabels.Visible) then
begin
TheText := LabelToStrF(XValue);
XLabelOut;
end;
XValue := GetNextXValue(XValue);
end;
{Minor Ticks on the axis:}
if ((FTickMinor > 0) and
(FTickSize > 0)) then
begin
{find out where the minors start:}
MinorStepSize := FStepSize / (FTickMinor+1);
MinorStepStart := FStepStart;
MinorTickSize := FTickSign * FTickSize div 2;
while ((MinorStepStart - MinorStepSize) >= FMin) do
MinorStepStart := MinorStepStart - MinorStepSize;
iY := MidY;
XValue := MinorStepStart;
while (XValue < FMax) do
begin
iX := FofX(XValue);
ACanvas.MoveTo(iX, iY);
ACanvas.LineTo(iX, iY + MinorTickSize);
XValue := GetNextMinorXValue(XValue);
end;
end; {minors}
end; {Ticks}
{record the position of the labels for use by TPlot in moving labels and ticks:}
if (FLabels.Direction = drHorizontal) then
begin
iFontWidth := FontWidth div 2;
FLabels.Left := FofX(FStepStart) - iFontWidth;
FLabels.Right := iX + iFontWidth;
if (FTickDirection = orLeft) then
begin
FLabels.Top := iYLabel;
FLabels.Bottom := iYLabel + FontHeight;
end
else
begin
FLabels.Top := iYLabel;
FLabels.Bottom := iYLabel + FontHeight;
end;
end
else
begin
iFontWidth := FontHeight div 2;
FLabels.Left := FofX(FStepStart) - iFontWidth;
FLabels.Right := iX + iFontWidth;
if (FTickDirection = orLeft) then
begin
FLabels.Top := iYLabel - FontWidth;
FLabels.Bottom := iYLabel;
end
else
begin
FLabels.Top := iYLabel;
FLabels.Bottom := iYLabel + FontWidth;
end;
end;
SetupHorizontalEnvelope;
end
else {Draw the Vertical axis:}
begin
ACanvas.MoveTo(MidX, Bottom);
ACanvas.LineTo(MidX, Top);
{Draw the arrows on the axis:}
if (FArrowSize > 0) then
begin
{taCenter therefore means no arrows !}
if (Alignment = taLeftJustify) then
DoArrow(Point(Left, Bottom-FArrowSize), Point(MidX, Bottom), Point(Right, Bottom-FArrowSize));
if (Alignment = taRightJustify) then
DoArrow(Point(Left, Top+FArrowSize), Point(MidX, Top), Point(Right, Top+FArrowSize));
end;
if (Self.LimitsVisible) then
begin
ACanvas.Pen.Style := psDot;
iY := Self.FofY(LimitLower);
ACanvas.MoveTo(Self.MidX, iY);
ACanvas.LineTo(LimitPos, iY);
iY := Self.FofY(LimitUpper);
ACanvas.MoveTo(Self.MidX, iY);
ACanvas.LineTo(LimitPos, iY);
ACanvas.Pen.Style := Self.Pen.Style;
end;
iX := MidX;
iXLabel := MidX + FTickSign*(FTickSize + FontWidth div 5);
iXTick := MidX + FTickSign*FTickSize;
iY := 0; {see below}
if (DoTextLabels) then
begin
{Text instead of Numeric Labels on the axis:}
for i := 0 to TSeries(FLabelSeries).XStringData.Count-1 do
begin
iY := FofY(TSeries(FLabelSeries).YData^[i]);
{Major Ticks on the axis:}
if (FTickSize > 0) then
begin
ACanvas.MoveTo(iX, iY);
ACanvas.LineTo(iXTick, iY);
end;
if (FLabels.Visible) then
begin
TheText := TSeries(FLabelSeries).XStringData.Strings[i];
YLabelOut;
end;
end;
end
else
begin
{Normal numeric labels:}
YValue := FStepStart;
while (YValue < FMax) do
begin
iY := FofY(YValue);
{Major Ticks on the axis:}
if (FTickSize > 0) then
begin
ACanvas.MoveTo(iX, iY);
ACanvas.LineTo(iXTick, iY);
end;
{Numeric labels:}
if (FLabels.Visible) then
begin
TheText := LabelToStrF(YValue);
YLabelOut;
end;
YValue := GetNextXValue(YValue);
end;
{Minor Ticks on the axis:}
if ((FTickMinor > 0) and
(FTickSize > 0)) then
begin
{find out where the minors start:}
MinorStepSize := FStepSize / (FTickMinor+1);
MinorStepStart := FStepStart;
MinorTickSize := FTickSign * FTickSize div 2;
while ((MinorStepStart - MinorStepSize) >= FMin) do
MinorStepStart := MinorStepStart - MinorStepSize;
YValue := MinorStepStart;
while (YValue < FMax) do
begin
iY := FofY(YValue);
ACanvas.MoveTo(iX, iY);
ACanvas.LineTo(iX + MinorTickSize, iY);
YValue := GetNextMinorXValue(YValue);
end;
end; {minors}
end; {Ticks}
{record the position of the labels for use by TPlot:}
if (FLabels.Direction = drHorizontal) then
begin
FLabels.Top := iY - FontHeight;
FLabels.Bottom := FofY(FStepStart);
if (FTickDirection = orLeft) then
begin
FLabels.Left := iXLabel - FontWidth;
FLabels.Right := iXlabel;
end
else
begin
FLabels.Left := iXLabel;
FLabels.Right := iXLabel + FontWidth;
end;
end
else
begin
iFontWidth := FontWidth div 2;
FLabels.Top := iY - iFontWidth;
FLabels.Bottom := FofY(FStepStart) + iFontWidth;
if (FTickDirection = orLeft) then
begin
FLabels.Left := iXLabel - FontHeight;
FLabels.Right := iXlabel;
end
else
begin
FLabels.Left := iXLabel;
FLabels.Right := iXLabel + FontHeight;
end;
end;
SetupVerticalEnvelope;
end; {Horizontal or Vertical}
{Print the axis Title:}
FTitle.Draw(ACanvas);
end;
{------------------------------------------------------------------------------
Function: TAxis.FofX
Description: standard X transform
Author: Mat Ballard
Date created: 04/25/2000
Date modified: 04/25/2000 by Mat Ballard
Purpose: returns the pixel position on screen as a function of the real data ordinate X
Known Issues:
------------------------------------------------------------------------------}
function TAxis.FofX(X: Single): Integer;
begin
{$IFDEF DELPHI3_UP}
Assert(FDirection = drHorizontal, sFofX1);
{$ENDIF}
if (FLogScale) then
FofX := Round(Left + Width * ((Log10(X / FMin)) / FLogSpan))
else
FofX := Round(Left + Width * ((X - FMin) / (FSpan)));
end;
{------------------------------------------------------------------------------
Function: TAxis.FofY
Description: standard Y transform
Author: Mat Ballard
Date created: 04/25/2000
Date modified: 04/25/2000 by Mat Ballard
Purpose: returns the pixel position on screen as a function of the real data co-ordinate Y
Known Issues:
------------------------------------------------------------------------------}
function TAxis.FofY(Y: Single): Integer;
begin
{$IFDEF DELPHI3_UP}
Assert(FDirection = drVertical, sFofY1);
{$ENDIF}
if (FLogScale) then
FofY := Round(Bottom - Height * ((Log10(Y / FMin)) / FLogSpan))
else
FofY := Round(Bottom - Height * ((Y - FMin) / (FSpan)));
end;
{------------------------------------------------------------------------------
Function: TAxis.XofF
Description: inverse X transform
Author: Mat Ballard
Date created: 04/25/2000
Date modified: 04/25/2000 by Mat Ballard
Purpose: returns the real data ordinate X as a function of the pixel position on screen
Known Issues:
------------------------------------------------------------------------------}
function TAxis.XofF(F: Integer): Single;
{this function returns the real data ordinate X
as a function of the pixel position F on screen:}
begin
{$IFDEF DELPHI3_UP}
Assert(FDirection = drHorizontal, sFofX1);
{$ENDIF}
if (FLogScale) then
XofF := FMin * Power(10.0, (FLogSpan * (F-Left) / Width))
else
XofF := FSpan * ((F-Left) / Width) + FMin;
end;
{------------------------------------------------------------------------------
Function: TAxis.YofF
Description: inverse Y transform
Author: Mat Ballard
Date created: 04/25/2000
Date modified: 04/25/2000 by Mat Ballard
Purpose: returns the real data ordinate Y as a function of the pixel position on screen
Known Issues:
------------------------------------------------------------------------------}
function TAxis.YofF(F: Integer): Single;
{this function returns the real data ordinate X
as a function of the pixel position F on screen:}
begin
{$IFDEF DELPHI3_UP}
Assert(FDirection = drVertical, sFofY1);
{$ENDIF}
if (FLogScale) then
YofF := FMin * Power(10.0, (FLogSpan * (Bottom-F) / Height))
else
YofF := FSpan * ((Bottom-F) / Height) + FMin;
end;
{------------------------------------------------------------------------------
Function: TAxis.StrToLabel
Description: converts a string to a number, depending on the NumberFormat
Author: Mat Ballard
Date created: 04/25/2000
Date modified: 04/25/2000 by Mat Ballard
Purpose: user IO
Known Issues:
------------------------------------------------------------------------------}
function TAxis.StrToLabel(Value: String): Single;
begin
case (FLabels.NumberFormat) of
lfGeneral .. lfCurrency:
StrToLabel := StrToFloat(Value);
lfPercent:
StrToLabel := StrToFloat(Value) / 100;
lfSeconds:
StrToLabel := StrToFloat(Value);
lfMinutes:
StrToLabel := 60 * StrToFloat(Value);
lfHours:
StrToLabel := 3600 * StrToFloat(Value);
lfDays:
StrToLabel := 86400 * StrToFloat(Value);
lfShortTime:
StrToLabel := StrToDateTime(Value);
lfShortDate:
StrToLabel := StrToDateTime(Value);
else
StrToLabel := 0.0;
end;
end;
{------------------------------------------------------------------------------
Function: TAxis.LabelToStrF
Description: converts a number to a string, depending on the NumberFormat
Author: Mat Ballard
Date created: 04/25/2000
Date modified: 04/25/2000 by Mat Ballard
Purpose: user IO
Known Issues:
------------------------------------------------------------------------------}
function TAxis.LabelToStrF(Value: Single): String;
var
TheText: String;
Mantissa: Extended;
Exponent: Integer;
TheDateTime: TDateTime;
begin
case (FLabels.NumberFormat) of
lfGeneral .. lfCurrency:
{See Rescale for definition of PrecisionAdded}
TheText := FloatToStrF(Value, TFloatFormat(FLabels.NumberFormat),
FLabels.Precision + PrecisionAdded, FLabels.Digits);
lfSI:
begin
DeSci(Value, Mantissa, Exponent);
case Exponent of {p, n u, m, -, K, M, G, T}
-12 .. -10: TheText := 'p';
-9 .. -7: TheText := 'n';
-6 .. -4: TheText := 'u';
-3 .. -1: TheText := 'm';
3 .. 5: TheText := 'K';
6 .. 8: TheText := 'M';
9 .. 11: TheText := 'G';
12 .. 14: TheText := 'T';
else TheText := '';
end;
if (Length(TheText) > 0) then
begin
Exponent := (Exponent + 99) mod 3;
Mantissa := Mantissa * IntPower(10, Exponent);
TheText := FloatToStrF(Mantissa, TFloatFormat(lfFixed),
FLabels.Precision, FLabels.Digits) + TheText;
end
else
TheText := FloatToStrF(Value, TFloatFormat(lfGeneral),
FLabels.Precision, FLabels.Digits);
end;
lfPercent:
TheText := FloatToStrF(100 * Value, TFloatFormat(FLabels.NumberFormat),
FLabels.Precision, FLabels.Digits);
lfSeconds:
TheText := FloatToStrF(Round(Value), ffGeneral,
FLabels.Precision, FLabels.Digits);
lfMinutes:
TheText := FloatToStrF(Round(Value / 60), ffGeneral,
FLabels.Precision, FLabels.Digits);
lfHours:
TheText := FloatToStrF(Round(Value / 3600), ffGeneral,
FLabels.Precision, FLabels.Digits);
lfDays:
TheText := FloatToStrF(Round(Value / 86400), ffGeneral,
FLabels.Precision, FLabels.Digits);
lfShortTime:
begin
TheDateTime := Value;
TheText := FormatDateTime('t', TheDateTime);
end;
lfShortDate:
begin
TheDateTime := Value;
TheText := FormatDateTime('ddddd', TheDateTime);
end;
end;
LabelToStrF := TheText;
end;
{------------------------------------------------------------------------------
Procedure: TAxis.SetupHorizontalEnvelope
Description: sets up the Horizontal (X Axis) envelope around which the Title dances
Author: Mat Ballard
Date created: 04/25/2000
Date modified: 04/25/2000 by Mat Ballard
Purpose: manages the appearance of the Axis
Known Issues:
------------------------------------------------------------------------------}
procedure TAxis.SetupHorizontalEnvelope;
var
TheRect: TRect;
begin
TheRect.Left := Left;
TheRect.Right := Right;
if (FTickDirection = orLeft) then
begin
TheRect.Top := MidY - FTickSize;
if (FLabels.Visible) then
TheRect.Top := TheRect.Top - FLabels.Height;
TheRect.Bottom := MidY + 1;
end
else {oRight}
begin
TheRect.Top := MidY - 1;
TheRect.Bottom := MidY + FTickSize;
if (FLabels.Visible) then
TheRect.Bottom := TheRect.Bottom + FLabels.Height;
end; {FTickDirection}
FTitle.Envelope := TheRect;
end;
{------------------------------------------------------------------------------
Procedure: TAxis.SetupVerticalEnvelope
Description: sets up the Vertical (Y Axis) envelope around which the Title dances
Author: Mat Ballard
Date created: 04/25/2000
Date modified: 04/25/2000 by Mat Ballard
Purpose: manages the appearance of the Axis
Known Issues:
------------------------------------------------------------------------------}
procedure TAxis.SetupVerticalEnvelope;
var
TheRect: TRect;
begin
TheRect.Top := Top;
TheRect.Bottom := Bottom;
if (FTickDirection = orLeft) then
begin
TheRect.Left := MidX - FTickSize;
if (FLabels.Visible) then
TheRect.Left := TheRect.Left - FLabels.Width;
TheRect.Right := MidX + 1;
end
else {oRight}
begin
TheRect.Left := MidX - 1;
TheRect.Right := MidX + FTickSize;
if (FLabels.Visible) then
TheRect.Right := TheRect.Right + FLabels.Width;
end; {FTickDirection}
FTitle.Envelope := TheRect;
end;
{------------------------------------------------------------------------------
Procedure: TAxis.Assign
Description: standard Assign method
Author: Mat Ballard
Date created: 07/06/2000
Date modified: 07/06/2000 by Mat Ballard
Purpose: implements Assign
Known Issues:
------------------------------------------------------------------------------}
{procedure TAxis.Assign(Source: TPersistent);
begin
inherited Assign(Source);
FArrowSize := TAxis(Source).ArrowSize;
FDirection := TAxis(Source).Direction;
FIntercept := TAxis(Source).Intercept;
FLogscale := TAxis(Source).Logscale;
FMax := TAxis(Source).Max;
FMin := TAxis(Source).Min;
FStepSize := TAxis(Source).StepSize;
FTickDirection := TAxis(Source).TickDirection;
FTickNum := TAxis(Source).TickNum;
FTickSize := TAxis(Source).TickSize;
FLabels.Assign(TAxis(Source).Labels);
FPen.Assign(TAxis(Source).Pen);
FTitle.Assign(TAxis(Source).Title);
end;}
{------------------------------------------------------------------------------
Procedure: TAxis.AssignTo
Description: standard AssignTo method
Author: Mat Ballard
Date created: 07/06/2000
Date modified: 07/06/2000 by Mat Ballard
Purpose: implements AssignTo
Known Issues:
------------------------------------------------------------------------------}
procedure TAxis.AssignTo(Dest: TPersistent);
begin
inherited AssignTo(Dest);
TAxis(Dest).ArrowSize := FArrowSize;
TAxis(Dest).Direction := FDirection;
{TAxis(Dest).Intercept := FIntercept;}
TAxis(Dest).Logscale := FLogscale;
TAxis(Dest).Max := FMax;
TAxis(Dest).Min := FMin;
TAxis(Dest).StepSize := FStepSize;
TAxis(Dest).TickDirection := FTickDirection;
TAxis(Dest).TickSize := FTickSize;
TAxis(Dest).Labels.Assign(FLabels);
TAxis(Dest).Pen.Assign(FPen);
TAxis(Dest).Title.Assign(FTitle);
end;
procedure TAxis.SetLabelSeries(Value: TPersistent);
begin
{Note: Labeltext is maintained within the TSeries, NOT in TAxis !}
FLabelSeries := Value;
StyleChange(Self);
end;
{TAngleAxis methods ---------------------------------------------------------}
{Constructor and Destructor:-------------------------------------------------}
{------------------------------------------------------------------------------
Constructor: TAngleAxis.Create
Description: standard Constructor
Author: Mat Ballard
Date created: 04/25/2000
Date modified: 04/25/2000 by Mat Ballard
Purpose: sets the Precision and Digits Properties
Known Issues:
------------------------------------------------------------------------------}
Constructor TAngleAxis.Create(AOwner: TPersistent);
begin
{First call the ancestor:}
inherited Create(AOwner);
FireEvents := FALSE;
FLength := 100;
Angle := 225;
FireEvents := TRUE;
end;
{------------------------------------------------------------------------------
Destructor: TAngleAxis.Destroy
Description: standard Destructor
Author: Mat Ballard
Date created: 01/16/2001
Date modified: 01/16/2001 by Mat Ballard
Purpose:
Known Issues:
------------------------------------------------------------------------------}
Destructor TAngleAxis.Destroy;
begin
inherited Destroy;
end;
{------------------------------------------------------------------------------
Procedure: TAxis.SetAngle
Description: standard property Set procedure
Author: Mat Ballard
Date created: 01/16/2001
Date modified: 01/16/2001 by Mat Ballard
Purpose: sets the Angle Property
Known Issues:
------------------------------------------------------------------------------}
procedure TAngleAxis.SetAngle(Value: Word);
begin
FAngle := Value Mod 360;
FAngleRadians := Pi * FAngle / 180;
{this is twice as fast as calling them individually:}
SinCos(FAngleRadians, FSin, FCos);
{look back along the axis, then 30 degrees less, for the arrow:}
SinCos(FAngleRadians + Pi*(1/2 - 1/6), FSinM30, FCosM30);
{look back along the axis, then 30 degrees more:}
SinCos(FAngleRadians + Pi*(1/2 + 1/6), FSinP30, FCosP30);
StyleChange(Self);
end;
{------------------------------------------------------------------------------
Procedure: TAxis.SetLength
Description: standard property Set procedure
Author: Mat Ballard
Date created: 01/16/2001
Date modified: 01/16/2001 by Mat Ballard
Purpose: sets the Length Property
Known Issues:
------------------------------------------------------------------------------}
procedure TAngleAxis.SetLength(Value: Word);
begin
if (Value = FLength) then exit;
FLength := Value;
StyleChange(Self);
end;
{------------------------------------------------------------------------------
Procedure: TAxis.SetZInterceptY
Description: standard property Set procedure
Author: Mat Ballard
Date created: 04/25/2000
Date modified: 04/25/2000 by Mat Ballard
Purpose: sets the ZInterceptY Property: the intercept of the Z Axis with the Y Axis
Known Issues:
------------------------------------------------------------------------------}
procedure TAngleAxis.SetZInterceptY(Value: Single);
begin
if (FZInterceptY = Value) then exit;
FZInterceptY := Value;
StyleChange(Self);
end;
{------------------------------------------------------------------------------
Procedure: TAxis.Draw
Description: standard Drawing procedure
Author: Mat Ballard
Date created: 04/25/2000
Date modified: 04/25/2000 by Mat Ballard
Purpose: draws the Axis on a given canvas
Known Issues:
------------------------------------------------------------------------------}
procedure TAngleAxis.Draw(ACanvas: TCanvas; LimitPos: Integer);
{Comments:
This method is quite complex, in a tedious way.
It has to account for the following variations:
1. Visible or not;
2. Arrows visible or not;
3. Axis direction (Horizontal or vertical);
4. Tick (and Label and Title) direction);
5. Title Alignment Direction and Orientation;
6. Tick, Label and Title visibility.
7. Angle !
An added complication is that we must generate a vertical font for the Title
of vertical axes. Note that this only works with TrueType fonts - NOT fonts
that are purely screen or printer.}
var
OldFireEvents: Boolean;
i,
iX,
iY,
FontHeight,
FontWidth,
iFontWidth,
FontDescent,
MinorTickSize: Integer;
MinorStepSize,
MinorStepStart,
{NewAngle,}
ZValue: Single;
TheText: String;
dTick,
TheTickStart: TPoint;
{begin internal functions:}
function GetNextXValue(XValue: Single): Single;
begin
if (FLogScale) then
GetNextXValue := XValue * FStepSize
else
GetNextXValue := XValue + FStepSize;
end;
function GetNextMinorXValue(XValue: Single): Single;
begin
if (FLogScale) then
GetNextMinorXValue := XValue * MinorStepSize
else
GetNextMinorXValue := XValue + MinorStepSize;
end;
begin
{the most common reason for exit:}
if (not Visible) then exit;
{$IFDEF DELPHI3_UP}
Assert(ACanvas <> nil, 'TAngleAxis.Draw: ' + sACanvasIsNil);
{$ENDIF}
FontWidth := 1;
ACanvas.Pen.Assign(FPen);
{Do the geometry:}
{first, squelch any "OnChange" events:}
OldFireEvents := FireEvents;
FireEvents := FALSE;
FEndX := Left + Round(FLength * FSin);
FEndY := Top + Round(-FLength * FCos);
{Draw the axis:}
ACanvas.MoveTo(Left, Top);
ACanvas.LineTo(FEndX, FEndY);
{Draw the arrows on the axis:}
if (FArrowSize > 0) then
begin
if (Alignment = taRightJustify) then
begin
ACanvas.MoveTo(FEndX, FEndY);
iX := FEndX + Round(FArrowSize * FCosM30);
iY := FEndY + Round(FArrowSize * FSinM30);
ACanvas.LineTo(iX, iY);
ACanvas.MoveTo(FEndX, FEndY);
{look back along the axis, then 30 degrees less:}
iX := FEndX + Round(FArrowSize * FCosP30);
iY := FEndY + Round(FArrowSize * FSinP30);
ACanvas.LineTo(iX, iY);
end; {taLeftJustify and taCenter therefore means no arrows !}
end;
{Prepare fonts for Labels on the axis:}
if (FLabels.Visible) then
begin
ACanvas.Font.Assign(FLabels.Font);
FontWidth := ACanvas.TextWidth('9');
FontHeight := ACanvas.TextHeight('9');
{We could call GetOutlineTextMetrics to get
the Descent (gap between baseline and bottom of a font), but:}
{FontDescent := FontHeight div 5;}
end;
{Ticks on the axis:}
dTick.x := 0;
dTick.y := 0;
case FAngle of
0: dTick.x := -FTickSize;
1 .. 60: dTick.x := FTickSize;
61 .. 120: dTick.y := FTickSize;
121 .. 180: dTick.x := FTickSize;
181 .. 240: dTick.x := -FTickSize;
241 .. 300: dTick.y := FTickSize;
301 .. 359: dTick.x := -FTickSize;
end;
ZValue := FStepStart;
//i := 0;
while (ZValue < FMax) do
begin
TheTickStart := dFofZ(ZValue);
Inc(TheTickStart.x, Left);
Inc(TheTickStart.y, Top);
if (FTickSize > 1) then
ACanvas.MoveTo(TheTickStart.x, TheTickStart.y);
Inc(TheTickStart.x, dTick.x);
Inc(TheTickStart.y, dTick.y);
if (FTickSize > 1) then
ACanvas.LineTo(TheTickStart.x, TheTickStart.y);
if (FLabels.Visible) then
begin
{if (FLabelSeries <> nil) then
if (i < FLabelSeries.Count) then
TheText := FLabelSeries.Strings[i]
else
break
else}
TheText := LabelToStrF(ZValue);
iFontWidth := ACanvas.TextWidth(TheText);
if (iFontWidth > FontWidth) then
FontWidth := iFontWidth;
if (dTick.x < 0) then
Dec(TheTickStart.x, iFontWidth);
if (dTick.y > 0) then
begin
Inc(TheTickStart.y, FontHeight);
Dec(TheTickStart.x, iFontWidth div 2);
end;
{$IFDEF MSWINDOWS}
ACanvas.TextOut(
TheTickStart.x,
TheTickStart.y - Abs(ACanvas.Font.Height),
TheText);
{$ENDIF}
{$IFDEF LINUX}
ACanvas.TextOut(
TheTickStart.x,
TheTickStart.y {+ Abs(ACanvas.Font.Height)},
TheText);
{$ENDIF}
end;
//Inc(i);
ZValue := GetNextXValue(ZValue);
end; {while ZValue < FMax}
{Minor Ticks on the axis:}
if ((FTickSize > 1) and (FTickMinor > 0)) then
begin
{find out where the minors start:}
MinorStepSize := FStepSize / (FTickMinor+1);
MinorStepStart := FStepStart;
while ((MinorStepStart - MinorStepSize) >= FMin) do
MinorStepStart := MinorStepStart - MinorStepSize;
//iY := MidY;
dTick.x := dTick.x div 2;
dTick.y := dTick.y div 2;
ZValue := MinorStepStart;
//i := 0;
while (ZValue < FMax) do
begin
TheTickStart := dFofZ(ZValue);
Inc(TheTickStart.x, Left);
Inc(TheTickStart.y, Top);
if (FTickSize > 1) then
ACanvas.MoveTo(TheTickStart.x, TheTickStart.y);
Inc(TheTickStart.x, dTick.x);
Inc(TheTickStart.y, dTick.y);
if (FTickSize > 1) then
ACanvas.LineTo(TheTickStart.x, TheTickStart.y);
ZValue := GetNextMinorXValue(ZValue);
end;
end; {minor ticks}
FireEvents := OldFireEvents;
end;
{------------------------------------------------------------------------------
Function: TAngleAxis.FofZ
Description: standard Z transform
Author: Mat Ballard
Date created: 07/25/2001
Date modified: 07/25/2001 by Mat Ballard
Purpose: returns the 3D pixel position as a function of the real data ordinate X
Known Issues:
------------------------------------------------------------------------------}
function TAngleAxis.FofZ(Z: Single): Integer;
begin
if (FLogScale) then
FofZ := Round(Left + Width * ((Log10(Z / FMin)) / FLogSpan))
else
FofZ := Round(Left + Width * ((Z - FMin) / (FSpan)));
end;
{------------------------------------------------------------------------------
Function: TAngleAxis.FofZ
Description: standard Z transform
Author: Mat Ballard
Date created: 01/18/2001
Date modified: 01/18/2001 by Mat Ballard
Purpose: returns the change in pixel position on screen as a function of the real data ordinate Z
Known Issues:
------------------------------------------------------------------------------}
function TAngleAxis.dFofZ(Z: Single): TPoint;
begin
if (FLogScale) then
begin
Result.x := Round(FSin * FLength * ((Log10(Z / FMin)) / FLogSpan));
Result.y := -Round(FCos * FLength * ((Log10(Z / FMin)) / FLogSpan));
end
else
begin
Result.x := Round(FSin * FLength * ((Z - FMin) / (FSpan)));
Result.y := -Round(FCos * FLength * ((Z - FMin) / (FSpan)));
end;
end;
{------------------------------------------------------------------------------
Procedure: TAngleAxis.ClickedOn
Description: Was this TRectangle clicked on ?
Author: Mat Ballard
Date created: 01/17/2001
Date modified: 01/17/2001 by Mat Ballard
Purpose: screen click management
Known Issues: overrides TRectangle.ClickedOn
------------------------------------------------------------------------------}
function TAngleAxis.ClickedOn(iX, iY: Integer): Boolean;
var
Slope, TheIntercept, Distance: Single;
begin
if ((FAngle = 0) or
(FAngle = 90) or
(FAngle = 180) or
(FAngle = 270)) then
Result := inherited ClickedOn(iX, iY)
else
begin
Result := FALSE;
if (iX < NoMath.Min(Left, FEndX)) then exit;
if (iX > NoMath.Max(Left, FEndX)) then exit;
if (iY < NoMath.Min(Top, FEndY)) then exit;
if (iY > NoMath.Max(Top, FEndY)) then exit;
Slope := - (Top - FEndY) / (Left - FEndX);
TheIntercept := Top + Slope * Left;
Distance := Abs((Slope * iX - TheIntercept + iY) * Sin(FAngleRadians));
if (Distance < FTicksize) then
Result := TRUE;
end;
end;
{------------------------------------------------------------------------------
Procedure: TAngleAxis.Outline
Description: Draws an Outline around this AngleAxis
Author: Mat Ballard
Date created: 01/22/2001
Date modified: 01/22/2001 by Mat Ballard
Purpose: gives the user a guide to what they are moving with the mouse
Known Issues:
------------------------------------------------------------------------------}
procedure TAngleAxis.Outline(ACanvas: TCanvas);
var
dP: TPoint;
begin
ACanvas.Pen.Color := clBlack;
ACanvas.Pen.Mode := pmNotXOR;
ACanvas.Pen.Style := psDash;
dP.x := Round(FTickSize * FCos);
dP.y := -Round(FTickSize * FSin);
ACanvas.Polygon([
Point(Left + dP.x, Top + dP.y),
Point(Left - dP.x, Top - dP.y),
Point(FEndX - dP.x, FEndY - dP.y),
Point(FEndX + dP.x, FEndY + dP.y)]);
end;
end.