mirror of
https://gitlab.com/freepascal.org/lazarus/lazarus.git
synced 2025-04-05 05:28:17 +02:00
1353 lines
36 KiB
ObjectPascal
1353 lines
36 KiB
ObjectPascal
{ Unit to plot a function on a canvas
|
|
|
|
Copyright (C) 2008 Michael Van Canneyt Michael@freepascal.org
|
|
|
|
This library is free software; you can redistribute it and/or modify it
|
|
under the terms of the GNU Library General Public License as published by
|
|
the Free Software Foundation; either version 2 of the License, or (at your
|
|
option) any later version with the following modification:
|
|
|
|
As a special exception, the copyright holders of this library give you
|
|
permission to link this library with independent modules to produce an
|
|
executable, regardless of the license terms of these independent modules,and
|
|
to copy and distribute the resulting executable under terms of your choice,
|
|
provided that you also meet, for each linked independent module, the terms
|
|
and conditions of the license of that module. An independent module is a
|
|
module which is not derived from or based on this library. If you modify
|
|
this library, you may extend this exception to your version of the library,
|
|
but you are not obligated to do so. If you do not wish to do so, delete this
|
|
exception statement from your version.
|
|
|
|
This program is distributed in the hope that it will be useful, but WITHOUT
|
|
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License
|
|
for more details.
|
|
|
|
You should have received a copy of the GNU Library General Public License
|
|
along with this library; if not, write to the Free Software Foundation,
|
|
Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1335, USA.
|
|
}
|
|
|
|
unit plotpanel;
|
|
|
|
{$mode objfpc}{$H+}
|
|
|
|
interface
|
|
|
|
uses
|
|
Classes, SysUtils, Controls, graphics, extctrls, ldocktree;
|
|
|
|
Const
|
|
DefLeftMargin = 60;
|
|
DefRightMargin = 20;
|
|
DefTopMargin = 40;
|
|
DefBottomMargin = DefTopMargin;
|
|
DefXTickSize = 5;
|
|
DefYTickSize = DefXTickSize;
|
|
DefXTicks = 10;
|
|
DefYTicks = DefXTicks;
|
|
DefAxisColor = clGreen;
|
|
DefTickColor = DefAxisColor;
|
|
DefLegendInterval = 2;
|
|
DefInterval = 100;
|
|
DefCaptionAlignment = taRightJustify;
|
|
DefGridInterval = 1;
|
|
DefGridColor = clSilver;
|
|
DefPlotColor = clRed;
|
|
DefLineWidth = 1;
|
|
|
|
Type
|
|
TPlotFloat = Double;
|
|
TCanvasPlotter = Class;
|
|
|
|
{ TPlotCaption }
|
|
|
|
TPlotCaption = Class(TPersistent)
|
|
private
|
|
FAlignment: TAlignment;
|
|
FFont: TFont;
|
|
FOnChange: TNotifyEvent;
|
|
FTitle: String;
|
|
procedure SetAlignment(const AValue: TAlignment);
|
|
procedure SetFont(const AValue: TFont);
|
|
procedure SetTitle(const AValue: String);
|
|
Protected
|
|
Procedure Changed;
|
|
Property OnChange : TNotifyEvent Read FOnChange Write FOnChange;
|
|
Public
|
|
Constructor Create;
|
|
Destructor Destroy; override;
|
|
Published
|
|
Property Title : String Read FTitle Write SetTitle;
|
|
Property Font : TFont Read FFont Write SetFont;
|
|
Property Alignment : TAlignment Read FAlignment Write SetAlignment;
|
|
end;
|
|
|
|
{ TPlotAxis }
|
|
TTickMode = (tmCount,tmDelta);
|
|
TPlotAxis = Class(TPersistent)
|
|
private
|
|
FDrawZero: Boolean;
|
|
FGridColor: TColor;
|
|
FGridLinewidth: Integer;
|
|
FGridInterval: Integer;
|
|
FInterval: TPlotFloat;
|
|
FCaption: TPlotCaption;
|
|
FLegendInterval: Integer;
|
|
FLegendFormat: String;
|
|
FOrigin: TPlotFloat;
|
|
FPlotter : TCanvasPlotter;
|
|
FColor: TColor;
|
|
FLinewidth: Integer;
|
|
FTickColor: TColor;
|
|
FTickFont: TFont;
|
|
FTickMode: TTickMode;
|
|
FTicks: Integer;
|
|
FTickSize: integer;
|
|
FTickLinewidth: Integer;
|
|
FZeroLinewidth: Integer;
|
|
procedure SetAxisColor(const AValue: TColor);
|
|
procedure SetLinewidth(const AValue: Integer);
|
|
procedure SetDrawZero(const AValue: Boolean);
|
|
procedure SetInterval(const AValue: TPlotFloat);
|
|
procedure SetCaption(const AValue: TPlotCaption);
|
|
procedure SetGridColor(const AValue: TColor);
|
|
procedure SetGridInterval(const AValue: Integer);
|
|
procedure SetGridLinewidth(const AValue: Integer);
|
|
procedure SetLegendInterval(const AValue: Integer);
|
|
procedure SetLegendFormat(const AValue: String);
|
|
procedure SetOrigin(const AValue: TPlotFloat);
|
|
procedure SetTickColor(const AValue: TColor);
|
|
procedure SetTickFont(const AValue: TFont);
|
|
procedure SetTickLinewidth(const AValue: Integer);
|
|
procedure SetTickMode(const AValue: TTickMode);
|
|
procedure SetTicks(const AValue: Integer);
|
|
procedure SetTickSize(const AValue: integer);
|
|
procedure SetZeroLinewidth(const AValue: Integer);
|
|
Protected
|
|
Procedure DoCaptionChange(Sender : TObject);
|
|
procedure Changed;
|
|
Property Plotter : TCanvasPlotter read FPlotter;
|
|
Function TickDelta : Double;
|
|
Function ValueDelta : TPlotFloat;
|
|
Function GetDimension : Integer;virtual; abstract;
|
|
Function Margin1 : Integer;virtual; abstract;
|
|
Function Margin2 : Integer;virtual; abstract;
|
|
Public
|
|
Constructor Create;virtual;
|
|
Destructor Destroy; override;
|
|
Published
|
|
// Linewidth of axis line
|
|
Property LineWidth: Integer read FLineWidth write SetLineWidth default DefLineWidth;
|
|
// Graph color
|
|
Property Color : TColor Read FColor Write SetAxisColor default defAxisColor;
|
|
// Color of ticks on axis
|
|
Property TickColor : TColor Read FTickColor Write SetTickColor default defAxisColor;
|
|
// Number or distance (in pixels) between ticks on axis
|
|
Property Ticks : Integer Read FTicks Write SetTicks;
|
|
// Length of ticks on axis
|
|
Property TickSize : integer Read FTickSize Write SetTickSize;
|
|
// Linewidth of ticks on axis
|
|
Property TickLinewidth: Integer read FTickLinewidth write SetTickLinewidth default DefLinewidth;
|
|
// Ticks is number of ticks or distance (in pixels) between ticks ?
|
|
Property TickMode : TTickMode Read FTickMode Write SetTickMode;
|
|
// Font for tick legend
|
|
Property TickFont : TFont Read FTickFont Write SetTickFont;
|
|
// Caption of axis
|
|
Property Caption : TPlotCaption Read FCaption Write SetCaption;
|
|
// Draw zero axis if interval if interval starts at negative values ?
|
|
Property DrawZero : Boolean Read FDrawZero Write SetDrawZero;
|
|
// Value in X/Y of origin of the axis. X/Y value starts here.
|
|
Property Origin : TPlotFloat Read FOrigin Write SetOrigin;
|
|
// Interval to cover in X/Y. X/Y run in [Origin,Origin+Interval]
|
|
Property Interval : TPlotFloat Read FInterval Write SetInterval;
|
|
// Write value in X/Y of ticks every LegendInterval ticks. 0 is no legend.
|
|
Property LegendInterval : Integer Read FLegendInterval Write SetLegendInterval default DeflegendInterval;
|
|
// Format for legend (formatfloat);
|
|
Property LegendFormat : String Read FLegendFormat write SetLegendFormat;
|
|
// Interval (in ticks) of grid. 0 means no grid.
|
|
Property GridInterval : Integer Read FGridInterval Write SetGridInterval default DefGridInterval;
|
|
// Grid color.
|
|
Property GridColor : TColor Read FGridColor Write SetGridColor default DefGridColor;
|
|
// Grid linewidth
|
|
Property GridLinewidth: Integer Read FGridLinewidth Write SetGridLinewidth default DefLineWidth;
|
|
// Width of zero lines
|
|
Property ZeroLineWidth: Integer read FZeroLineWidth Write SetZeroLineWidth default DefLineWidth;
|
|
end;
|
|
|
|
{ TPlotXAxis }
|
|
|
|
TPlotXAxis = Class(TPlotAxis)
|
|
private
|
|
FLeftMargin: Integer;
|
|
FRightMargin: Integer;
|
|
procedure SetLeftMargin(const AValue: Integer);
|
|
procedure SetRightMargin(const AValue: Integer);
|
|
Protected
|
|
Function GetDimension : Integer;override;
|
|
Function Margin1 : Integer;override;
|
|
Function Margin2 : Integer;override;
|
|
Public
|
|
Constructor Create; override;
|
|
Published
|
|
// Start of X origin in pixels.
|
|
Property LeftMargin : Integer Read FLeftMargin Write SetLeftMargin;
|
|
// End of X range from right edge in pixels.
|
|
Property RightMargin : Integer Read FRightMargin Write SetRightMargin;
|
|
Property Ticks Default DefXTicks;
|
|
Property TickSize Default DefXTickSize;
|
|
end;
|
|
|
|
{ TPlotYAxis }
|
|
|
|
TPlotYAxis = Class(TPlotAxis)
|
|
private
|
|
FBottomMargin: Integer;
|
|
FTopMargin: Integer;
|
|
procedure SetBottomMargin(const AValue: Integer);
|
|
procedure SetTopMargin(const AValue: Integer);
|
|
Protected
|
|
Function GetDimension : Integer;override;
|
|
Function Margin1 : Integer;override;
|
|
Function Margin2 : Integer;override;
|
|
Public
|
|
Constructor Create; override;
|
|
Published
|
|
// End of Y range from top edge in pixels.
|
|
Property TopMargin : Integer Read FTopMargin Write SetTopMargin;
|
|
// Start of Y range (Y origin) from bottom edge in pixels.
|
|
Property BottomMargin : Integer Read FBottomMargin Write SetBottomMargin;
|
|
end;
|
|
|
|
{ TCanvasPlotter }
|
|
|
|
TCanvasPlotter = Class(TComponent)
|
|
private
|
|
FActive: Boolean;
|
|
FBkColor: TColor;
|
|
FBoundsRect: TRect;
|
|
FCaption: TPlotCaption;
|
|
FColor: TColor;
|
|
FPlotColor: TColor;
|
|
FPlotLineWidth: Integer;
|
|
FXaxis: TPlotXAxis;
|
|
FYaxis: TPlotYAxis;
|
|
FCanvas: TCanvas;
|
|
FBitmap : TBitmap;
|
|
FLastLegend : String;
|
|
FLastFont : TFont;
|
|
procedure DrawCaption(ACanvas: TCanvas);
|
|
procedure SetActive(const AValue: Boolean);
|
|
procedure SetBkColor(const AValue: TColor);
|
|
procedure SetBoundsRect(const AValue: TRect);
|
|
procedure SetCanvas(const AValue: TCanvas);
|
|
procedure SetCaption(const AValue: TPlotCaption);
|
|
procedure SetColor(const AValue: TColor);
|
|
procedure SetPlotColor(const AValue: TColor);
|
|
procedure SetPlotLineWidth(const AValue: Integer);
|
|
procedure SetXAxis(const AValue: TPlotXAxis);
|
|
procedure SetYAxis(const AValue: TPlotYAxis);
|
|
Protected
|
|
function GetRotatedLegend(ACanvas : TCanvas): TBitmap;
|
|
Procedure DrawBackground(ACanvas: TCanvas);
|
|
procedure DrawHAxis(ACanvas: TCanvas; Const AHAxis,AVAxis : TPlotAxis); virtual;
|
|
procedure DrawVAxis(ACanvas: TCanvas; Const AVAxis,AHAxis : TPlotAxis); virtual;
|
|
Procedure PlotFunction(ACanvas : TCanvas); virtual;
|
|
Function GetHDimension : Integer; virtual;
|
|
Function GetVDimension : Integer; virtual;
|
|
Function CalcFunction(X : TPlotFloat) : TPlotFloat; virtual;
|
|
Procedure Changed; virtual;
|
|
Public
|
|
Constructor Create(AOwner : TComponent); override;
|
|
Destructor Destroy; override;
|
|
Procedure Draw;
|
|
Property BoundsRect: TRect Read FBoundsRect Write SetBoundsRect;
|
|
Property Canvas : TCanvas Read FCanvas Write SetCanvas;
|
|
Property XAxis : TPlotXAxis Read FXaxis Write SetXAxis;
|
|
Property YAxis : TPlotYAxis Read FYaxis Write SetYAxis;
|
|
Property BkColor : TColor Read FBkColor Write SetBkColor;
|
|
Property Color: TColor Read FColor write SetColor;
|
|
Property PlotColor : TColor Read FPlotColor Write SetPlotColor;
|
|
Property PlotLinewidth: Integer read FPlotLineWidth Write SetPlotLineWidth;
|
|
Property Active : Boolean Read FActive Write SetActive;
|
|
Property Caption : TPlotCaption Read FCaption Write SetCaption;
|
|
end;
|
|
|
|
{ TControlPlotter }
|
|
|
|
TControlPlotter = Class(TCanvasPlotter)
|
|
Protected
|
|
FControl : TControl;
|
|
Procedure Changed; override;
|
|
Public
|
|
Constructor Create(AOwner : TComponent); override;
|
|
end;
|
|
|
|
{ TEventControlPlotter }
|
|
TOnCalcPlotEvent = Procedure(Const X : TPlotFloat; Out Y : TPlotFloat) of Object;
|
|
|
|
TEventControlPlotter = Class(TCanvasPlotter)
|
|
private
|
|
FOnCalcPlot : TOnCalcPlotEvent;
|
|
procedure SetOnCalcPlot(const AValue: TOnCalcPlotEvent);
|
|
Protected
|
|
Function CalcFunction(X : TPlotFloat) : TPlotFloat; override;
|
|
Public
|
|
Property OnCalcPlot : TOnCalcPlotEvent Read FOnCalcPlot Write SetOnCalcPlot;
|
|
end;
|
|
|
|
{ TCustomPlotFunctionPanel }
|
|
|
|
TCustomPlotFunctionPanel = Class(TGraphicControl)// (CustomPanel)
|
|
private
|
|
FPlotter: TCanvasPlotter;
|
|
function GetActive: Boolean;
|
|
function GetBkColor: TColor;
|
|
function GetCaption: TPlotCaption;
|
|
function GetColor: TColor;
|
|
function GetPlotColor: TColor;
|
|
function GetPlotLineWidth: Integer;
|
|
function GetXaxis: TPlotXAxis;
|
|
function GetYaxis: TPlotYAxis;
|
|
procedure SetActive(const AValue: Boolean);
|
|
procedure SetBkColor(const AValue: TColor);
|
|
procedure SetCaption(const AValue: TPlotCaption);
|
|
procedure SetPlotColor(const AValue: TColor);
|
|
procedure SetPlotLineWidth(const AValue: Integer);
|
|
procedure SetXAxis(const AValue: TPlotXAxis);
|
|
procedure SetYAxis(const AValue: TPlotYAxis);
|
|
Protected
|
|
Function CreatePlotter : TCanvasPlotter; virtual;
|
|
procedure Paint; override;
|
|
Property Plotter : TCanvasPlotter Read FPlotter;
|
|
procedure SetColor(Value: TColor); override;
|
|
Public
|
|
Constructor Create(AOwner : TComponent); override;
|
|
Destructor Destroy; override;
|
|
Property Align;
|
|
Property Active : Boolean Read GetActive Write SetActive;
|
|
Property BkColor: TColor read GetBkColor write SetBkColor default clDefault;
|
|
Property Caption : TPlotCaption Read GetCaption Write SetCaption;
|
|
Property Color: TColor read GetColor write SetColor default clDefault;
|
|
Property PlotColor : TColor Read GetPlotColor Write SetPlotColor;
|
|
Property PlotLineWidth: Integer read GetPlotLinewidth Write SetPlotLinewidth default DefLinewidth;
|
|
Property XAxis : TPlotXAxis Read GetXaxis Write SetXAxis;
|
|
Property YAxis : TPlotYAxis Read GetYaxis Write SetYAxis;
|
|
end;
|
|
|
|
{ TPlotFunctionPanel }
|
|
|
|
TPlotFunctionPanel = Class(TCustomPlotFunctionPanel)
|
|
private
|
|
function GetOnCalcPlot: TOnCalcPlotEvent;
|
|
procedure SetOnCalcPlot(AValue: TOnCalcPlotEvent);
|
|
Protected
|
|
Function CreatePlotter : TCanvasPlotter; override;
|
|
Published
|
|
Property OnCalcPlot : TOnCalcPlotEvent Read GetOnCalcPlot Write SetOnCalcPlot;
|
|
Property Align;
|
|
Property Anchors;
|
|
Property Active;
|
|
Property BkColor;
|
|
Property BorderSpacing;
|
|
Property Color;
|
|
Property PlotColor;
|
|
property PlotLinewidth;
|
|
Property XAxis;
|
|
Property YAxis;
|
|
end;
|
|
|
|
EPlotPanel = Class(Exception);
|
|
|
|
implementation
|
|
|
|
uses
|
|
lcltype, // Rotated font support
|
|
lclintf,
|
|
graphtype,
|
|
intfgraphics,
|
|
fpimage,
|
|
interfacebase; // To detect widget set.
|
|
|
|
|
|
resourcestring
|
|
SerrInvalidInterval = 'Invalid interval. Interval must be a positive number: %f';
|
|
// DefXCaption = 'X values';
|
|
// DefYCaption = 'Y values';
|
|
|
|
function Max(a, b: Integer): Integer;
|
|
begin
|
|
if a > b then Result := a else Result := b;
|
|
end;
|
|
|
|
{ TCustomPlotFunctionPanel }
|
|
|
|
procedure TCustomPlotFunctionPanel.SetXAxis(const AValue: TPlotXAxis);
|
|
begin
|
|
PLotter.XAxis.assign(AValue);
|
|
end;
|
|
|
|
function TCustomPlotFunctionPanel.GetActive: Boolean;
|
|
begin
|
|
Result:=FPlotter.Active;
|
|
end;
|
|
|
|
function TCustomPlotFunctionPanel.GetBkColor: TColor;
|
|
begin
|
|
Result := FPlotter.BkColor;
|
|
end;
|
|
|
|
function TCustomPlotFunctionPanel.GetCaption: TPlotCaption;
|
|
begin
|
|
Result:=FPlotter.Caption;
|
|
end;
|
|
|
|
function TCustomPlotFunctionPanel.GetColor: TColor;
|
|
begin
|
|
Result := FPlotter.Color;
|
|
end;
|
|
|
|
function TCustomPlotFunctionPanel.GetPlotColor: TColor;
|
|
begin
|
|
Result:=FPlotter.PlotColor;
|
|
end;
|
|
|
|
function TCustomPlotFunctionPanel.GetPlotLinewidth: Integer;
|
|
begin
|
|
Result := FPlotter.PlotLinewidth;
|
|
end;
|
|
|
|
function TCustomPlotFunctionPanel.GetXaxis: TPlotXAxis;
|
|
begin
|
|
Result:=FPlotter.XAxis;
|
|
end;
|
|
|
|
function TCustomPlotFunctionPanel.GetYaxis: TPlotYAxis;
|
|
begin
|
|
Result:=FPlotter.YAxis;
|
|
end;
|
|
|
|
procedure TCustomPlotFunctionPanel.SetActive(const AValue: Boolean);
|
|
begin
|
|
FPlotter.Active:=AValue;
|
|
end;
|
|
|
|
procedure TCustomPlotFunctionPanel.SetBkColor(const AValue: TColor);
|
|
begin
|
|
FPlotter.BkColor := AValue;
|
|
end;
|
|
|
|
procedure TCustomPlotFunctionPanel.SetCaption(const AValue: TPlotCaption);
|
|
begin
|
|
FPlotter.Caption.Assign(AValue);
|
|
end;
|
|
|
|
procedure TCustomPlotFunctionPanel.SetPlotColor(const AValue: TColor);
|
|
begin
|
|
FPlotter.PlotColor:=AValue;
|
|
end;
|
|
|
|
procedure TCustomPlotFunctionPanel.SetPlotLineWidth(const AValue: Integer);
|
|
begin
|
|
FPlotter.PlotLinewidth := Max(1, AValue);
|
|
end;
|
|
|
|
procedure TCustomPlotFunctionPanel.SetYAxis(const AValue: TPlotYAxis);
|
|
begin
|
|
FPlotter.Yaxis.Assign(AValue);
|
|
end;
|
|
|
|
|
|
procedure TCustomPlotFunctionPanel.Paint;
|
|
begin
|
|
FPlotter.FBoundsRect:=Self.ClientRect;
|
|
FPlotter.Draw;
|
|
end;
|
|
|
|
procedure TCustomPlotFunctionPanel.SetColor(Value: TColor);
|
|
begin
|
|
inherited SetColor(Value);
|
|
If Assigned(FPlotter) then
|
|
FPLotter.Color:=Value;
|
|
end;
|
|
|
|
|
|
constructor TCustomPlotFunctionPanel.Create(AOwner: TComponent);
|
|
begin
|
|
inherited Create(AOwner);
|
|
Width:=320;
|
|
Height:=200;
|
|
FPlotter:=CreatePlotter;
|
|
FPlotter.FCanvas:=Self.Canvas;
|
|
end;
|
|
|
|
destructor TCustomPlotFunctionPanel.Destroy;
|
|
begin
|
|
inherited Destroy;
|
|
end;
|
|
|
|
function TCustomPlotFunctionPanel.CreatePlotter: TCanvasPlotter;
|
|
begin
|
|
Result:=TControlPlotter.Create(Self);
|
|
end;
|
|
|
|
{ TPlotAxis }
|
|
|
|
procedure TPlotAxis.SetAxisColor(const AValue: TColor);
|
|
begin
|
|
if FColor=AValue then exit;
|
|
FColor:=AValue;
|
|
Changed;
|
|
end;
|
|
|
|
procedure TPlotAxis.SetLinewidth(const AValue: Integer);
|
|
begin
|
|
if FLinewidth = AValue then exit;
|
|
FLinewidth := Max(1, AValue);
|
|
Changed;
|
|
end;
|
|
|
|
procedure TPlotAxis.SetDrawZero(const AValue: Boolean);
|
|
begin
|
|
if FDrawZero=AValue then exit;
|
|
FDrawZero:=AValue;
|
|
Changed;
|
|
end;
|
|
|
|
procedure TPlotAxis.SetInterval(const AValue: TPlotFloat);
|
|
begin
|
|
if FInterval=AValue then exit;
|
|
If FInterval<=0 then
|
|
Raise EPlotPanel.CreateFmt(SerrInvalidInterval,[AValue]);
|
|
FInterval:=AValue;
|
|
Changed;
|
|
end;
|
|
|
|
procedure TPlotAxis.SetCaption(const AValue: TPlotCaption);
|
|
begin
|
|
if FCaption=AValue then exit;
|
|
FCaption.Assign(AValue);
|
|
Changed;
|
|
end;
|
|
|
|
procedure TPlotAxis.SetGridColor(const AValue: TColor);
|
|
begin
|
|
if FGridColor=AValue then exit;
|
|
FGridColor := AValue;
|
|
Changed;
|
|
end;
|
|
|
|
procedure TPlotAxis.SetGridInterval(const AValue: Integer);
|
|
begin
|
|
if FGridInterval = AValue then exit;
|
|
FGridInterval := AValue;
|
|
Changed;
|
|
end;
|
|
|
|
procedure TPlotAxis.SetGridLinewidth(const AValue: Integer);
|
|
begin
|
|
if FGridLinewidth = AValue then exit;
|
|
FGridLinewidth := Max(1, AValue);
|
|
Changed;
|
|
end;
|
|
|
|
procedure TPlotAxis.SetLegendInterval(const AValue: Integer);
|
|
begin
|
|
if FLegendInterval=AValue then exit;
|
|
FLegendInterval:=Max(0, AValue);
|
|
Changed;
|
|
end;
|
|
|
|
procedure TPlotAxis.SetLegendFormat(const AValue: String);
|
|
begin
|
|
if FLegendFormat=AValue then exit;
|
|
FLegendFormat:=AValue;
|
|
Changed;
|
|
end;
|
|
|
|
procedure TPlotAxis.SetOrigin(const AValue: TPlotFloat);
|
|
begin
|
|
if FOrigin=AValue then exit;
|
|
FOrigin:=AValue;
|
|
Changed;
|
|
end;
|
|
|
|
procedure TPlotAxis.SetTickColor(const AValue: TColor);
|
|
begin
|
|
if FTickColor=AValue then exit;
|
|
FTickColor:=AValue;
|
|
Changed;
|
|
end;
|
|
|
|
procedure TPlotAxis.SetTickFont(const AValue: TFont);
|
|
begin
|
|
if FTickFont=AValue then exit;
|
|
FTickFont:=AValue;
|
|
Changed;
|
|
end;
|
|
|
|
procedure TPlotAxis.SetTickLinewidth(const AValue: Integer);
|
|
begin
|
|
if FTickLinewidth = AValue then exit;
|
|
FTickLinewidth := Max(1, AValue);
|
|
Changed;
|
|
end;
|
|
|
|
procedure TPlotAxis.SetTickMode(const AValue: TTickMode);
|
|
begin
|
|
if FTickMode=AValue then exit;
|
|
If Not Assigned(FPlotter) then
|
|
FTicks:=1
|
|
else
|
|
FTicks:=GetDimension div FTicks;
|
|
FTickMode:=AValue;
|
|
Changed;
|
|
end;
|
|
|
|
procedure TPlotAxis.SetTicks(const AValue: Integer);
|
|
begin
|
|
if FTicks=AValue then exit;
|
|
FTicks:=Max(1, AValue);
|
|
Changed;
|
|
end;
|
|
|
|
procedure TPlotAxis.SetTickSize(const AValue: integer);
|
|
begin
|
|
if FTickSize=AValue then exit;
|
|
FTickSize:=Max(1, AValue);
|
|
Changed;
|
|
end;
|
|
|
|
procedure TPlotAxis.SetZeroLinewidth(const AValue: Integer);
|
|
begin
|
|
if FZeroLinewidth = AValue then exit;
|
|
FZeroLinewidth := Max(1, AValue);
|
|
Changed;
|
|
end;
|
|
|
|
procedure TPlotAxis.DoCaptionChange(Sender: TObject);
|
|
begin
|
|
Changed;
|
|
end;
|
|
|
|
procedure TPlotAxis.Changed;
|
|
begin
|
|
If Assigned(FPlotter) then
|
|
FPlotter.Changed;
|
|
end;
|
|
|
|
function TPlotAxis.TickDelta: Double;
|
|
begin
|
|
Case FTickMode of
|
|
tmCount : Result:=GetDimension / (Ticks);
|
|
tmDelta : Result:=Ticks;
|
|
end;
|
|
end;
|
|
|
|
function TPlotAxis.ValueDelta: TPlotFloat;
|
|
begin
|
|
Case FTickMode of
|
|
tmCount : Result:=Interval / Ticks;
|
|
tmDelta : Result:=Interval / TickDelta;
|
|
end;
|
|
end;
|
|
|
|
constructor TPlotAxis.Create;
|
|
begin
|
|
inherited Create;
|
|
FCaption:=TPlotCaption.Create;
|
|
FCaption.FOnChange:=@DoCaptionChange;
|
|
FCaption.Font.OnChange:= @DoCaptionChange;
|
|
FColor:=DefAxisColor;
|
|
FLineWidth:= DefLineWidth;
|
|
FTickFont:=TFont.Create;
|
|
FTickFont.OnChange := @DoCaptionChange;
|
|
FTickLinewidth := DefLinewidth;
|
|
FLegendInterval:=DefLegendInterval;
|
|
FInterval:=DefInterval;
|
|
FTickColor:=DefTickColor;
|
|
FGridColor:=DefGridColor;
|
|
FGridLinewidth:=DefLineWidth;
|
|
FGridInterval:=DefGridInterval;
|
|
FZeroLinewidth:=DefLinewidth;
|
|
end;
|
|
|
|
destructor TPlotAxis.Destroy;
|
|
begin
|
|
FreeAndNil(FTickFont);
|
|
FreeAndNil(FCaption);
|
|
inherited Destroy;
|
|
end;
|
|
|
|
{ TPlotXAxis }
|
|
|
|
procedure TPlotXAxis.SetLeftMargin(const AValue: Integer);
|
|
begin
|
|
if FLeftMargin=AValue then exit;
|
|
FLeftMargin:=AValue;
|
|
Changed;
|
|
end;
|
|
|
|
procedure TPlotXAxis.SetRightMargin(const AValue: Integer);
|
|
begin
|
|
if FRightMargin=AValue then exit;
|
|
FRightMargin:=AValue;
|
|
Changed;
|
|
end;
|
|
|
|
function TPlotXAxis.GetDimension: Integer;
|
|
begin
|
|
Result:=FPlotter.GetHDimension-Leftmargin-RightMargin;
|
|
If Result<0 then
|
|
Result:=0;
|
|
end;
|
|
|
|
function TPlotXAxis.Margin1: Integer;
|
|
begin
|
|
Result:=FLeftmargin;
|
|
end;
|
|
|
|
function TPlotXAxis.Margin2: Integer;
|
|
begin
|
|
Result:=FRightMargin;
|
|
end;
|
|
|
|
constructor TPlotXAxis.Create;
|
|
begin
|
|
inherited Create;
|
|
FLeftMargin:=DefLeftMargin;
|
|
FRightMargin:=DefRightMargin;
|
|
FTicks:=DefXTicks;
|
|
FTickSize:=DefXTickSize;
|
|
// FCaption.Title:=DefXCaption;
|
|
end;
|
|
|
|
{ TPlotYAxis }
|
|
|
|
procedure TPlotYAxis.SetBottomMargin(const AValue: Integer);
|
|
begin
|
|
if FBottomMargin=AValue then exit;
|
|
FBottomMargin:=AValue;
|
|
Changed;
|
|
end;
|
|
|
|
procedure TPlotYAxis.SetTopMargin(const AValue: Integer);
|
|
begin
|
|
if FTopMargin=AValue then exit;
|
|
FTopMargin:=AValue;
|
|
Changed;
|
|
end;
|
|
|
|
function TPlotYAxis.GetDimension: Integer;
|
|
begin
|
|
Result:=FPLotter.GetVDimension-TopMargin-BottomMargin;
|
|
end;
|
|
|
|
function TPlotYAxis.Margin1: Integer;
|
|
begin
|
|
Result:=FBottomMargin;
|
|
end;
|
|
|
|
function TPlotYAxis.Margin2: Integer;
|
|
begin
|
|
Result:=FTopMargin;
|
|
end;
|
|
|
|
constructor TPlotYAxis.Create;
|
|
begin
|
|
inherited Create;
|
|
FTopMargin:=DefTopMargin;
|
|
FBottomMargin:=DefBottomMargin;
|
|
FTicks:=DefYTicks;
|
|
FTickSize:=DefYTickSize;
|
|
// FCaption.FTitle:=DefYCaption;
|
|
end;
|
|
|
|
{ TCanvasPlotter }
|
|
|
|
procedure TCanvasPlotter.SetActive(const AValue: Boolean);
|
|
begin
|
|
if FActive=AValue then exit;
|
|
FActive:=AValue;
|
|
Changed;
|
|
end;
|
|
|
|
procedure TCanvasPlotter.SetBkColor(const AValue: TColor);
|
|
begin
|
|
if FBkColor=AValue then exit;
|
|
FBkColor:=AValue;
|
|
Changed;
|
|
end;
|
|
|
|
procedure TCanvasPlotter.SetBoundsRect(const AValue: TRect);
|
|
begin
|
|
if (FBoundsRect.Left=AValue.left) and
|
|
(FBoundsRect.Top=AValue.Top) and
|
|
(FBoundsRect.Bottom=AValue.Bottom) and
|
|
(FBoundsRect.RIght=AValue.Right) then exit;
|
|
FBoundsRect:=AValue;
|
|
Changed;
|
|
end;
|
|
|
|
procedure TCanvasPlotter.SetCanvas(const AValue: TCanvas);
|
|
begin
|
|
if FCanvas=AValue then exit;
|
|
FCanvas:=AValue;
|
|
Changed;
|
|
end;
|
|
|
|
procedure TCanvasPlotter.SetCaption(const AValue: TPlotCaption);
|
|
begin
|
|
if FCaption=AValue then exit;
|
|
FCaption.Assign(AValue);
|
|
Changed;
|
|
end;
|
|
|
|
procedure TCanvasPlotter.SetColor(const AValue: TColor);
|
|
begin
|
|
if FColor = AValue then exit;
|
|
FColor := AValue;
|
|
Changed;
|
|
end;
|
|
|
|
procedure TCanvasPlotter.SetPlotColor(const AValue: TColor);
|
|
begin
|
|
If (FPlotColor=AValue) then Exit;
|
|
FPlotColor:=AValue;
|
|
Changed;
|
|
end;
|
|
|
|
procedure TCanvasPlotter.SetPlotLineWidth(const AValue: Integer);
|
|
begin
|
|
if (FPlotLinewidth=AValue) then exit;
|
|
FPlotLineWidth := AValue;
|
|
Changed;
|
|
end;
|
|
|
|
procedure TCanvasPlotter.SetXAxis(const AValue: TPlotXAxis);
|
|
begin
|
|
if FXaxis=AValue then exit;
|
|
FXaxis.Assign(AValue);
|
|
Changed;
|
|
end;
|
|
|
|
procedure TCanvasPlotter.SetYAxis(const AValue: TPlotYAxis);
|
|
begin
|
|
if FYaxis=AValue then exit;
|
|
FYaxis.Assign(AValue);
|
|
Changed;
|
|
end;
|
|
|
|
|
|
function TCanvasPlotter.CalcFunction(X: TPlotFloat): TPlotFloat;
|
|
begin
|
|
Result:=0;
|
|
end;
|
|
|
|
procedure TCanvasPlotter.Changed;
|
|
|
|
begin
|
|
Draw;
|
|
end;
|
|
|
|
constructor TCanvasPlotter.Create(AOwner: TComponent);
|
|
begin
|
|
inherited Create(AOwner);
|
|
FXAxis:=TPlotXAxis.Create;
|
|
FXAxis.FPlotter:=Self;
|
|
FYAxis:=TPlotYAxis.Create;
|
|
FYAxis.FPlotter:=Self;
|
|
FPlotColor:=DefPlotColor;
|
|
FPlotLinewidth:=DefLinewidth;
|
|
FCaption:=TPlotCaption.Create;
|
|
FBkColor := clDefault;
|
|
FColor := clDefault;
|
|
end;
|
|
|
|
destructor TCanvasPlotter.Destroy;
|
|
begin
|
|
inherited Destroy;
|
|
FreeAndNil(FXAxis);
|
|
FreeAndNil(FYAxis);
|
|
FreeAndNil(FCaption);
|
|
FreeAndNil(FBitmap);
|
|
FreeAndNil(FLastFont);
|
|
end;
|
|
|
|
procedure TCanvasPlotter.Draw;
|
|
begin
|
|
If Not Assigned(FCanvas) then
|
|
Exit;
|
|
DrawBackGround(FCanvas);
|
|
If Active then
|
|
PlotFunction(FCanvas);
|
|
If (FCaption.Title<>'') then
|
|
DrawCaption(FCanvas);
|
|
end;
|
|
|
|
procedure TCanvasPlotter.DrawCaption(ACanvas: TCanvas);
|
|
|
|
Var
|
|
CW,CH,CX : Integer;
|
|
|
|
begin
|
|
CW:=ACanvas.TextWidth(FCaption.Title);
|
|
CH:=ACanvas.TextHeight(FCaption.Title);
|
|
Case Caption.Alignment of
|
|
taLeftJustify : CX:=BoundsRect.Left+FXAxis.LeftMargin;
|
|
taRightJustify : CX:=BoundsRect.Right-CW-FXAxis.RightMargin;
|
|
taCenter : CX:=BoundsRect.Left+FXAxis.LeftMargin+(GetHDimension-CW-FXAxis.RightMargin-FXAxis.LeftMargin) div 2;
|
|
end;
|
|
ACanvas.Font:=FCaption.Font;
|
|
CH:=BoundsRect.Top+(FYAxis.TopMargin-CH) div 2;
|
|
ACanvas.TextOut(CX,CH,FCaption.Title);
|
|
end;
|
|
|
|
procedure TCanvasPlotter.DrawBackground(ACanvas: TCanvas);
|
|
|
|
begin
|
|
// Color paints the entire diagram background
|
|
ACanvas.Brush.Color:=Color;
|
|
ACanvas.Brush.Style:=bsSolid;
|
|
ACanvas.FillRect(BoundsRect);
|
|
|
|
// BkColor paints the background spanned by the axes
|
|
ACanvas.Brush.Color := BkColor;
|
|
ACanvas.FillRect(
|
|
FXAxis.LeftMargin,
|
|
FYAxis.TopMargin,
|
|
BoundsRect.Width-FXAxis.RightMargin,
|
|
BoundsRect.Height-FYAxis.BottomMargin
|
|
);
|
|
|
|
DrawHAxis(ACanvas,FXAxis,FYAxis);
|
|
DrawVAxis(ACanvas,FYAxis,FXAxis);
|
|
end;
|
|
|
|
procedure TCanvasPlotter.DrawHAxis(ACanvas: TCanvas; Const AHAxis,AVAxis : TPlotAxis);
|
|
|
|
Var
|
|
OX,OY,EX,EY : Integer;
|
|
CX,CY,CW : Integer;
|
|
I,X,TE : integer;
|
|
TickDelta : Double;
|
|
V,VD : TPlotFloat;
|
|
S,L : String;
|
|
|
|
begin
|
|
OX:=FBoundsRect.Left+AHAxis.Margin1;
|
|
EX:=FBoundsRect.Right-AHAxis.Margin2;
|
|
OY:=FBoundsRect.Bottom-AVAxis.Margin1;
|
|
EY:=FBoundsRect.Top+AVAxis.Margin2;
|
|
// Writeln(Format('(%d,%d) -> (%d,%d) (%d,%d) (%d,%d)',[width,height,ox,oy,ox,ey,ex,oy]));
|
|
// X axis
|
|
ACanvas.Brush.Style := bsClear;
|
|
ACanvas.Pen.Color:=AHAxis.Color;
|
|
ACanvas.Pen.Width:=AHAxis.LineWidth;
|
|
ACanvas.Line(OX,OY,EX,OY);
|
|
Canvas.Font:=AHAxis.TickFont;
|
|
TickDelta:=AHAxis.TickDelta;
|
|
VD:=AHAxis.ValueDelta;
|
|
TE:=OY+AHAxis.TickSize;
|
|
I:=0;
|
|
V:=AHAxis.Origin;
|
|
L:=AHAxis.LegendFormat;
|
|
If (L='') then
|
|
L:='#0.#';
|
|
Repeat
|
|
ACanvas.Pen.Color:=AHAxis.TickColor;
|
|
ACanvas.Pen.Width := AHAxis.TickLinewidth;
|
|
X:=OX+Round(I*TickDelta);
|
|
ACanvas.Line(X,OY,X,TE);
|
|
If (AHAxis.GridInterval<>0) and ((I mod AHAxis.GridInterval)=0) then
|
|
begin
|
|
ACanvas.Pen.Color:=AHAxis.GridColor;
|
|
ACanvas.Pen.Width := AHAxis.GridLinewidth;
|
|
ACanvas.Line(X,OY,X,EY);
|
|
end;
|
|
If (AHAxis.LegendInterval<>0) and ((I mod AHAxis.LegendInterval)=0) then
|
|
begin
|
|
S:=FormatFloat(L,V);
|
|
CW:=Canvas.TextWidth(S);
|
|
Canvas.TextOut(X-(CW Div 2),TE+4,S);
|
|
end;
|
|
Inc(I);
|
|
V:=V+VD;
|
|
Until X>=EX;
|
|
if AHAxis.DrawZero and ((AHAxis.Origin<0) and ((AHAxis.Origin+AHAxis.Interval)>0)) then
|
|
begin
|
|
X:=OX+Round((EX-OX)*Abs(AHAxis.Origin)/AHAxis.Interval);
|
|
ACanvas.Pen.Color:=AHAxis.TickColor;
|
|
ACanvas.Pen.Width := AHAxis.ZeroLinewidth;
|
|
ACanvas.Line(X,OY,X,EY);
|
|
end;
|
|
Canvas.Font:=AHAxis.Caption.Font;
|
|
CW:=ACanvas.TextWidth(AHAxis.Caption.Title);
|
|
Case AHAxis.Caption.Alignment of
|
|
taLeftJustify : CX:=OX;
|
|
taRightJustify : CX:=EX-CW;
|
|
taCenter : CX:=(EX+OX-CW) div 2;
|
|
end;
|
|
// Writeln(Format('Caption at (%d,%d) : %s',[CX,CY,AHAxis.Caption]));
|
|
CY:=OY+AHAxis.TickSize+4+ACanvas.TextHeight('X')+4;
|
|
ACanvas.TextOut(CX,CY,AHAxis.Caption.Title);
|
|
end;
|
|
|
|
procedure TCanvasPlotter.DrawVAxis(ACanvas: TCanvas; Const AVAxis,AHAxis : TPlotAxis);
|
|
|
|
Var
|
|
OX,OY,EX,EY : Integer;
|
|
CY,CH,CW : Integer;
|
|
I,Y,TE : integer;
|
|
TickDelta : Double;
|
|
V,VD : TPlotFloat;
|
|
S,L : String;
|
|
// Vertical font support
|
|
OldFont, RotatedFont: HFONT;
|
|
ALogFont : TLogFont;
|
|
// GTK 1
|
|
BMP : TBitmap;
|
|
|
|
begin
|
|
// ACanvas.DrawText(FXAxis.Caption);
|
|
// Y axis
|
|
OX:=FBoundsRect.Left+AHAxis.Margin1;
|
|
EX:=FBoundsRect.Right-AHAxis.Margin2;
|
|
OY:=FBoundsRect.Bottom-AVAxis.Margin1;
|
|
EY:=FBoundsRect.Top+AVAxis.Margin2;
|
|
ACanvas.Brush.Style := bsClear;
|
|
ACanvas.Pen.Color:=AVAxis.Color;
|
|
ACanvas.Pen.Width:=AVAxis.Linewidth;
|
|
ACanvas.Line(OX,OY,OX,EY);
|
|
TickDelta:=AVAxis.TickDelta;
|
|
VD:=AVAxis.ValueDelta;
|
|
TE:=OX-AVAxis.TickSize;
|
|
V:=AVAxis.Origin;
|
|
L:=AVAxis.LegendFormat;
|
|
Canvas.Font:=AVAxis.TickFont;
|
|
If (L='') then
|
|
L:='#0.#';
|
|
I:=0;
|
|
CH:=Canvas.TextHeight('X') div 2;
|
|
Repeat
|
|
Y:=OY-Round(I*TickDelta);
|
|
ACanvas.Pen.Color:=AVAxis.TickColor;
|
|
ACanvas.Pen.Width := AVAxis.TickLinewidth;
|
|
ACanvas.Line(TE,Y,OX,Y);
|
|
If (Y<>OY) and (AVAxis.GridInterval<>0) and ((I mod AVAxis.GridInterval)=0) then
|
|
begin
|
|
ACanvas.Pen.Color:=AVAxis.GridColor;
|
|
ACanvas.Pen.Width := AVAxis.GridLinewidth;
|
|
ACanvas.Line(OX,Y,EX,Y);
|
|
end;
|
|
If (AVAxis.LegendInterval<>0) and ((I mod AVAxis.LegendInterval)=0) then
|
|
begin
|
|
S:=FormatFloat(L,V);
|
|
CW:=Canvas.TextWidth(S);
|
|
Canvas.TextOut(TE-CW-4,Y-CH,S);
|
|
end;
|
|
Inc(I);
|
|
V:=V+VD;
|
|
Until Y<=EY;
|
|
if AVAxis.DrawZero and ((AVAxis.Origin<0) and ((AVAxis.Origin+AVAxis.Interval)>0)) then
|
|
begin
|
|
Y:=OY-Round((OY-EY)*Abs(AVAxis.Origin)/AVAxis.Interval);
|
|
ACanvas.Pen.Color:=AVAxis.TickColor;
|
|
ACanvas.Pen.Width := AVAxis.ZeroLinewidth;
|
|
ACanvas.Line(OX,Y,EX,Y);
|
|
end;
|
|
L:=AVAxis.Caption.Title;
|
|
ACanvas.Font:=AVAxis.Caption.Font;
|
|
CH:=ACanvas.TextHeight(L);
|
|
If CompareText(WidgetSet.ClassName,'TGTK1WidgetSet')<>0 then
|
|
begin
|
|
// Use Vertical font
|
|
OldFont := 0;
|
|
if GetObject(ACanvas.Font.Reference.Handle, SizeOf(ALogFont), @ALogFont) <> 0 then
|
|
begin
|
|
ALogFont.lfEscapement := 900;
|
|
RotatedFont := CreateFontIndirect(ALogFont);
|
|
if RotatedFont <> 0 then
|
|
OldFont:=SelectObject(ACanvas.Handle, RotatedFont);
|
|
CW:=ACanvas.TextWidth(L);
|
|
Case AVAxis.Caption.Alignment of
|
|
taLeftJustify : CY:=OY;
|
|
taRightJustify : CY:=EY+CW;
|
|
taCenter : CY:=(EY+OY+CW) div 2;
|
|
end;
|
|
TextOut(ACanvas.Handle, 4, CY, PChar(L), Length(L));
|
|
if OldFont <> 0 then
|
|
DeleteObject(SelectObject(ACanvas.Handle, OldFont));
|
|
end;
|
|
end
|
|
else
|
|
begin
|
|
BMP:=GetRotatedLegend(ACanvas);
|
|
CW:=BMP.Height;
|
|
Case AVAxis.Caption.Alignment of
|
|
taLeftJustify : CY:=OY-CW;
|
|
taRightJustify : CY:=EY;
|
|
taCenter : CY:=(EY+OY-CW) div 2;
|
|
end;
|
|
Canvas.Draw(4,CY,Bmp);
|
|
end;
|
|
end;
|
|
|
|
Function TCanvasPlotter.GetRotatedLegend(ACanvas : TCanvas) : TBitmap;
|
|
{
|
|
This is an expensive operation, so we do it once and cache the result.
|
|
When the parameters (font,legend) change, we regenerate the bitmap
|
|
}
|
|
Var
|
|
CW,CH,I,J : Integer;
|
|
BMP : TBitmap;
|
|
SrcIntfImg, DestIntfImg: TLazIntfImage;
|
|
L : String;
|
|
ImgHandle,ImgMaskHandle: HBitmap;
|
|
|
|
begin
|
|
If (FBitmap=Nil) then
|
|
begin
|
|
FBitmap:=TBitmap.Create;
|
|
FLastFont:=TFont.Create;
|
|
end
|
|
else
|
|
begin
|
|
If (FLastLegend=FYAxis.Caption.Title) and
|
|
(FLastFont.Name=FYaxis.Caption.Font.name) and
|
|
(FLastFont.Style=FYaxis.Caption.Font.Style) and
|
|
(FLastFont.Size=FYaxis.Caption.Font.Size) then
|
|
// NOthing changed, return last bitmap.
|
|
Exit(FBitmap);
|
|
FBitmap.Clear;
|
|
end;
|
|
L:=FYAxis.Caption.Title;
|
|
ACanvas.Font:=FYAxis.Caption.Font;
|
|
FLastLegend:=L;
|
|
FLastFont.Assign(FYAxis.Caption.Font);
|
|
BMP:=TBitmap.Create;
|
|
try
|
|
BMP.Canvas.Font:=FYAxis.Caption.Font;
|
|
CH:=BMP.Canvas.TextHeight(L);
|
|
CW:=BMP.Canvas.TextWidth(L);
|
|
BMP.Width:=CW;
|
|
BMP.Height:=CH;
|
|
BMP.Canvas.Brush.Color:=Self.Color;
|
|
BMP.Canvas.Brush.Style:=bsSolid;
|
|
BMP.Canvas.FillRect(0,0,CW,CH);
|
|
BMP.Canvas.TextOut(0,0,L);
|
|
SrcIntfImg:=TLazIntfImage.Create(0,0);
|
|
try
|
|
SrcIntfImg.LoadFromBitmap(BMP.Handle,BMP.MaskHandle);
|
|
DestIntfImg:=TLazIntfImage.Create(0,0);
|
|
try
|
|
DestIntfImg.LoadFromBitmap(BMP.Handle,BMP.MaskHandle);
|
|
DestIntfImg.Width:=CH;
|
|
DestIntfImg.Height:=CW;
|
|
For I:=0 to CW-1 do
|
|
For J:=0 to CH-1 do
|
|
DestIntfImg.Colors[J,CW-1-I]:=SrcIntfImg.Colors[I,J];
|
|
DestIntfImg.CreateBitmaps(ImgHandle,ImgMaskHandle,True);
|
|
FBitmap.Handle:=ImgHandle;
|
|
FBitmap.MaskHandle:=ImgMaskHandle;
|
|
Result:=FBitmap;
|
|
finally
|
|
DestIntfImg.Free;
|
|
end;
|
|
finally
|
|
SrcIntfImg.Free;
|
|
end;
|
|
finally
|
|
BMP.Free;
|
|
end;
|
|
end;
|
|
|
|
|
|
procedure TCanvasPlotter.PlotFunction(ACanvas: TCanvas);
|
|
|
|
Var
|
|
POX,PX,PXW,PY,POY,PEY,PYH,PLX,PLY : Integer; // Pixel units
|
|
X,Y,XI,YI,YO : TPlotFloat; // Plot units
|
|
R: TRect;
|
|
clp: Boolean;
|
|
|
|
begin
|
|
// X-origin in pixels.
|
|
// NOTE: this is not the location of "zero" but the left edge of the plot area.
|
|
POX:=FXAxis.LeftMargin;
|
|
// Width in pixels
|
|
PXW:= BoundsRect.Width - FXAxis.RightMargin - POX + 1;
|
|
// Y origin in pixels
|
|
// NOTE: this is not the location of "zero" but the bottom edge of the plot area.
|
|
POY := BoundsRect.Height - FYAxis.BottomMargin;
|
|
// Height in pixels
|
|
PYH:=POY-FYAxis.TopMargin+1;
|
|
// Y top
|
|
YI:=PYH/FYAxis.Interval;
|
|
// Interval in plot units
|
|
XI:=FXAxis.Interval/PXW;
|
|
// Y plot Origin, i.e. bottom edge of the plot area, in world coordinates
|
|
YO:=FYAxis.Origin;
|
|
// Y plot max value
|
|
PEY:=FYAxis.TopMargin;
|
|
// Y interval
|
|
YI:=PYH/FYAxis.Interval;
|
|
// Start value
|
|
X:=FXAxis.Origin;
|
|
ACanvas.Pen.Color:=PlotColor;
|
|
ACanvas.Pen.Width := FPlotLineWidth;
|
|
PLX:=POX;
|
|
PLY:=POY;
|
|
R := ACanvas.ClipRect;
|
|
clp := ACanvas.Clipping;
|
|
ACanvas.ClipRect := Rect(POX, FYAxis.TopMargin, PXW + POX, POY);
|
|
ACanvas.Clipping := true;
|
|
try
|
|
For PX:=0 to PXW do
|
|
begin
|
|
try
|
|
Y:=CalcFunction(X);
|
|
PY:=POY-Trunc((Y-YO)*YI);
|
|
except
|
|
// Catch math calculation exceptions.
|
|
On E : EMathError do
|
|
begin
|
|
PY:=PEY+1;
|
|
end;
|
|
On E : EIntError do
|
|
begin
|
|
PY:=PEY+1;
|
|
end;
|
|
On E : Exception do
|
|
Raise;
|
|
end;
|
|
If (PX>0) and (PY>=PEY) and (PY<=POY) then
|
|
begin
|
|
// Writeln(Format('(%f,%f) -> (%d,%d)',[X,Y,PX+Pox,PY]));
|
|
// ACanvas.Pixels[PX+Pox,PY]:=PlotColor;
|
|
ACanvas.Line(Pox+PLX,PLY,POX+PX,PY);
|
|
end;
|
|
PLX:=PX;
|
|
PLY:=PY;
|
|
X:=X+XI;
|
|
|
|
end;
|
|
finally
|
|
ACanvas.ClipRect := R;
|
|
ACanvas.Clipping := clp;
|
|
end;
|
|
end;
|
|
|
|
function TCanvasPlotter.GetHDimension: Integer;
|
|
begin
|
|
Result:=FBoundsRect.Right-FBoundsRect.Left+1;
|
|
end;
|
|
|
|
function TCanvasPlotter.GetVDimension: Integer;
|
|
begin
|
|
Result:=FBoundsRect.Bottom-FBoundsRect.Top+1;
|
|
end;
|
|
|
|
{ TControlPlotter }
|
|
|
|
procedure TControlPlotter.Changed;
|
|
begin
|
|
// If Not (FControl.ComponentState in [csLoading]) then;
|
|
FControl.Invalidate;
|
|
end;
|
|
|
|
constructor TControlPlotter.Create(AOwner: TComponent);
|
|
begin
|
|
inherited Create(AOwner);
|
|
If AOwner is TCOntrol then
|
|
begin
|
|
FControl:=AOwner as TControl;
|
|
if FColor = clDefault then
|
|
FColor := FControl.Color;
|
|
if FBkColor = clDefault then
|
|
FBkColor := FControl.Color;
|
|
end;
|
|
end;
|
|
|
|
{ TEventControlPlotter }
|
|
|
|
procedure TEventControlPlotter.SetOnCalcPlot(const AValue: TOnCalcPlotEvent);
|
|
begin
|
|
FOnCalcPlot:=AValue;
|
|
Changed;
|
|
end;
|
|
|
|
function TEventControlPlotter.CalcFunction(X: TPlotFloat): TPlotFloat;
|
|
begin
|
|
If Assigned(FOnCalcPlot) then
|
|
FOnCalcPlot(X,Result)
|
|
else
|
|
Result:=inherited CalcFunction(X);
|
|
end;
|
|
|
|
|
|
{ TPlotFunctionPanel }
|
|
|
|
function TPlotFunctionPanel.GetOnCalcPlot: TOnCalcPlotEvent;
|
|
begin
|
|
If Assigned(FPlotter) then
|
|
Result:=TEventControlPlotter(FPlotter).OnCalcPlot
|
|
else
|
|
Result:=Nil;
|
|
end;
|
|
|
|
procedure TPlotFunctionPanel.SetOnCalcPlot(AValue: TOnCalcPlotEvent);
|
|
begin
|
|
If Assigned(FPlotter) then
|
|
TEventControlPlotter(FPlotter).OnCalcPlot:=Avalue
|
|
end;
|
|
|
|
function TPlotFunctionPanel.CreatePlotter: TCanvasPlotter;
|
|
begin
|
|
Result:=TEventControlPlotter.Create(Self);
|
|
end;
|
|
|
|
{ TPlotCaption }
|
|
|
|
procedure TPlotCaption.SetAlignment(const AValue: TAlignment);
|
|
begin
|
|
if FAlignment=AValue then exit;
|
|
FAlignment:=AValue;
|
|
Changed;
|
|
end;
|
|
|
|
procedure TPlotCaption.SetFont(const AValue: TFont);
|
|
begin
|
|
if FFont=AValue then exit;
|
|
FFont:=AValue;
|
|
Changed;
|
|
end;
|
|
|
|
procedure TPlotCaption.SetTitle(const AValue: String);
|
|
begin
|
|
if FTitle=AValue then exit;
|
|
FTitle:=AValue;
|
|
Changed;
|
|
end;
|
|
|
|
procedure TPlotCaption.Changed;
|
|
begin
|
|
If Assigned(FOnChange) then
|
|
FOnChange(Self);
|
|
end;
|
|
|
|
constructor TPlotCaption.Create;
|
|
begin
|
|
FFont:=TFont.Create;
|
|
FFont.OnChange := FOnChange;
|
|
end;
|
|
|
|
destructor TPlotCaption.Destroy;
|
|
begin
|
|
FreeAndNil(FFont);
|
|
inherited Destroy;
|
|
end;
|
|
|
|
end.
|
|
|