mirror of
https://gitlab.com/freepascal.org/lazarus/lazarus.git
synced 2025-05-01 18:43:41 +02:00
TAChart: Extract TADataTools unit
git-svn-id: trunk@38432 -
This commit is contained in:
parent
4cf420b246
commit
ad680a75f4
1
.gitattributes
vendored
1
.gitattributes
vendored
@ -3017,6 +3017,7 @@ components/tachart/tachartwmf.lpk svneol=native#text/plain
|
|||||||
components/tachart/tachartwmf.pas svneol=native#text/pascal
|
components/tachart/tachartwmf.pas svneol=native#text/pascal
|
||||||
components/tachart/tacustomseries.pas svneol=native#text/plain
|
components/tachart/tacustomseries.pas svneol=native#text/plain
|
||||||
components/tachart/tacustomsource.pas svneol=native#text/pascal
|
components/tachart/tacustomsource.pas svneol=native#text/pascal
|
||||||
|
components/tachart/tadatatools.pas svneol=native#text/pascal
|
||||||
components/tachart/tadbsource.pas svneol=native#text/pascal
|
components/tachart/tadbsource.pas svneol=native#text/pascal
|
||||||
components/tachart/tadraweraggpas.pas svneol=native#text/pascal
|
components/tachart/tadraweraggpas.pas svneol=native#text/pascal
|
||||||
components/tachart/tadrawerbgra.pas svneol=native#text/pascal
|
components/tachart/tadrawerbgra.pas svneol=native#text/pascal
|
||||||
|
@ -34,7 +34,7 @@
|
|||||||
for details about the copyright.
|
for details about the copyright.
|
||||||
"/>
|
"/>
|
||||||
<Version Major="1"/>
|
<Version Major="1"/>
|
||||||
<Files Count="37">
|
<Files Count="38">
|
||||||
<Item1>
|
<Item1>
|
||||||
<Filename Value="tagraph.pas"/>
|
<Filename Value="tagraph.pas"/>
|
||||||
<HasRegisterProc Value="True"/>
|
<HasRegisterProc Value="True"/>
|
||||||
@ -201,6 +201,10 @@
|
|||||||
<AddToUsesPkgSection Value="False"/>
|
<AddToUsesPkgSection Value="False"/>
|
||||||
<UnitName Value="TAChartTeeChart"/>
|
<UnitName Value="TAChartTeeChart"/>
|
||||||
</Item37>
|
</Item37>
|
||||||
|
<Item38>
|
||||||
|
<Filename Value="tadatatools.pas"/>
|
||||||
|
<UnitName Value="TADataTools"/>
|
||||||
|
</Item38>
|
||||||
</Files>
|
</Files>
|
||||||
<LazDoc Paths="$(LazarusDir)\components\tachart\fpdoc"/>
|
<LazDoc Paths="$(LazarusDir)\components\tachart\fpdoc"/>
|
||||||
<Type Value="RunAndDesignTime"/>
|
<Type Value="RunAndDesignTime"/>
|
||||||
|
@ -13,7 +13,7 @@ uses
|
|||||||
TALegendPanel, TARadialSeries, TACustomSource, TAGeometry, TANavigation,
|
TALegendPanel, TARadialSeries, TACustomSource, TAGeometry, TANavigation,
|
||||||
TADrawerCanvas, TADrawerSVG, TAIntervalSources, TAChartAxisUtils,
|
TADrawerCanvas, TADrawerSVG, TAIntervalSources, TAChartAxisUtils,
|
||||||
TAChartListbox, TAEnumerators, TADataPointsEditor, TAChartExtentLink,
|
TAChartListbox, TAEnumerators, TADataPointsEditor, TAChartExtentLink,
|
||||||
TAToolEditors, TAMath, TAChartImageList, LazarusPackageIntf;
|
TAToolEditors, TAMath, TAChartImageList, TADataTools, LazarusPackageIntf;
|
||||||
|
|
||||||
implementation
|
implementation
|
||||||
|
|
||||||
|
417
components/tachart/tadatatools.pas
Normal file
417
components/tachart/tadatatools.pas
Normal file
@ -0,0 +1,417 @@
|
|||||||
|
{
|
||||||
|
|
||||||
|
*****************************************************************************
|
||||||
|
* *
|
||||||
|
* See the file COPYING.modifiedLGPL.txt, included in this distribution, *
|
||||||
|
* for details about the copyright. *
|
||||||
|
* *
|
||||||
|
* 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. *
|
||||||
|
* *
|
||||||
|
*****************************************************************************
|
||||||
|
|
||||||
|
Authors: Alexander Klenin
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
unit TADataTools;
|
||||||
|
|
||||||
|
{$H+}
|
||||||
|
|
||||||
|
interface
|
||||||
|
|
||||||
|
uses
|
||||||
|
Classes, TAChartUtils, TAGraph, TATools, TATypes;
|
||||||
|
|
||||||
|
type
|
||||||
|
TDataPointDistanceTool = class;
|
||||||
|
|
||||||
|
TDataPointDistanceToolMeasureEvent =
|
||||||
|
procedure (ASender: TDataPointDistanceTool) of object;
|
||||||
|
TDataPointGetDistanceTextEvent =
|
||||||
|
procedure (ASender: TDataPointDistanceTool; var AText: String) of object;
|
||||||
|
|
||||||
|
TDataPointDistanceToolPointer = class(TSeriesPointer)
|
||||||
|
published
|
||||||
|
property Style default psVertBar;
|
||||||
|
end;
|
||||||
|
|
||||||
|
TDataPointDistanceToolMarks = class(TCustomChartMarks)
|
||||||
|
public
|
||||||
|
procedure Assign(ASource: TPersistent); override;
|
||||||
|
constructor Create(AOwner: TCustomChart);
|
||||||
|
published
|
||||||
|
property Distance default DEF_MARKS_DISTANCE;
|
||||||
|
property Format;
|
||||||
|
property Frame;
|
||||||
|
property LabelBrush;
|
||||||
|
property LinkPen;
|
||||||
|
end;
|
||||||
|
|
||||||
|
|
||||||
|
TDataPointDistanceTool = class(TDataPointDrawTool)
|
||||||
|
published
|
||||||
|
type
|
||||||
|
TOptions = set of (dpdoLockToData);
|
||||||
|
|
||||||
|
strict private
|
||||||
|
// Workaround for FPC 2.6 bug. Remove after migration to 2.8.
|
||||||
|
FAnchors: array of TObject;
|
||||||
|
FMarks: TDataPointDistanceToolMarks;
|
||||||
|
FMeasureMode: TChartDistanceMode;
|
||||||
|
FOnGetDistanceText: TDataPointGetDistanceTextEvent;
|
||||||
|
FOnMeasure: TDataPointDistanceToolMeasureEvent;
|
||||||
|
FOptions: TOptions;
|
||||||
|
FPointerEnd: TDataPointDistanceToolPointer;
|
||||||
|
FPointerStart: TDataPointDistanceToolPointer;
|
||||||
|
function GetPointEnd: TDataPointTool.TPointRef; inline;
|
||||||
|
function GetPointStart: TDataPointTool.TPointRef;
|
||||||
|
procedure SetMarks(AValue: TDataPointDistanceToolMarks);
|
||||||
|
procedure SetOptions(AValue: TOptions);
|
||||||
|
procedure SetPointerEnd(AValue: TDataPointDistanceToolPointer);
|
||||||
|
procedure SetPointerStart(AValue: TDataPointDistanceToolPointer);
|
||||||
|
|
||||||
|
strict protected
|
||||||
|
procedure DoDraw; override;
|
||||||
|
function FindRef(APoint: TPoint; ADest: TDataPointTool.TPointRef): Boolean;
|
||||||
|
function GetDistanceText: String;
|
||||||
|
function SameTransformations(ASeries1, ASeries2: TBasicChartSeries): Boolean;
|
||||||
|
|
||||||
|
public
|
||||||
|
constructor Create(AOwner: TComponent); override;
|
||||||
|
destructor Destroy; override;
|
||||||
|
function Distance(AUnits: TChartUnits = cuGraph): Double;
|
||||||
|
procedure KeyDown(APoint: TPoint); override;
|
||||||
|
procedure KeyUp(APoint: TPoint); override;
|
||||||
|
procedure MouseDown(APoint: TPoint); override;
|
||||||
|
procedure MouseMove(APoint: TPoint); override;
|
||||||
|
procedure MouseUp(APoint: TPoint); override;
|
||||||
|
property PointEnd: TDataPointTool.TPointRef read GetPointEnd;
|
||||||
|
property PointStart: TDataPointTool.TPointRef read GetPointStart;
|
||||||
|
|
||||||
|
published
|
||||||
|
property DrawingMode;
|
||||||
|
property GrabRadius default 20;
|
||||||
|
property LinePen: TChartPen read FPen write SetPen;
|
||||||
|
property Marks: TDataPointDistanceToolMarks read FMarks write SetMarks;
|
||||||
|
property MeasureMode: TChartDistanceMode
|
||||||
|
read FMeasureMode write FMeasureMode default cdmXY;
|
||||||
|
property Options: TOptions read FOptions write SetOptions default [];
|
||||||
|
property PointerEnd: TDataPointDistanceToolPointer
|
||||||
|
read FPointerEnd write SetPointerEnd;
|
||||||
|
property PointerStart: TDataPointDistanceToolPointer
|
||||||
|
read FPointerStart write SetPointerStart;
|
||||||
|
published
|
||||||
|
property OnGetDistanceText: TDataPointGetDistanceTextEvent
|
||||||
|
read FOnGetDistanceText write FOnGetDistanceText;
|
||||||
|
property OnMeasure: TDataPointDistanceToolMeasureEvent
|
||||||
|
read FOnMeasure write FOnMeasure;
|
||||||
|
end;
|
||||||
|
|
||||||
|
|
||||||
|
implementation
|
||||||
|
|
||||||
|
uses
|
||||||
|
FPCanvas, Graphics, GraphMath, LCLIntf, LCLType, Math, SysUtils, Types,
|
||||||
|
TAChartAxis, TACustomSeries, TAGeometry;
|
||||||
|
|
||||||
|
const
|
||||||
|
DEF_DISTANCE_FORMAT = '%0:.9g';
|
||||||
|
|
||||||
|
{ TDataPointDistanceToolMarks }
|
||||||
|
|
||||||
|
procedure TDataPointDistanceToolMarks.Assign(ASource: TPersistent);
|
||||||
|
begin
|
||||||
|
if ASource is TChartMarks then
|
||||||
|
with TDataPointDistanceToolMarks(ASource) do begin
|
||||||
|
Self.FLabelBrush.Assign(FLabelBrush);
|
||||||
|
Self.FLabelFont.Assign(FLabelFont);
|
||||||
|
Self.FLinkPen.Assign(FLinkPen);
|
||||||
|
end;
|
||||||
|
inherited Assign(ASource);
|
||||||
|
end;
|
||||||
|
|
||||||
|
constructor TDataPointDistanceToolMarks.Create(AOwner: TCustomChart);
|
||||||
|
begin
|
||||||
|
inherited Create(AOwner);
|
||||||
|
FDistance := DEF_MARKS_DISTANCE;
|
||||||
|
FLabelBrush.Color := clYellow;
|
||||||
|
Format := DEF_DISTANCE_FORMAT;
|
||||||
|
end;
|
||||||
|
|
||||||
|
{ TDataPointDistanceTool }
|
||||||
|
|
||||||
|
constructor TDataPointDistanceTool.Create(AOwner: TComponent);
|
||||||
|
begin
|
||||||
|
inherited;
|
||||||
|
SetLength(FAnchors, 2);
|
||||||
|
FAnchors[0] := TDataPointTool.TPointRef.Create;
|
||||||
|
FAnchors[1] := TDataPointTool.TPointRef.Create;
|
||||||
|
FMarks := TDataPointDistanceToolMarks.Create(nil);
|
||||||
|
FPointerEnd := TDataPointDistanceToolPointer.Create(nil);
|
||||||
|
FPointerStart := TDataPointDistanceToolPointer.Create(nil);
|
||||||
|
end;
|
||||||
|
|
||||||
|
destructor TDataPointDistanceTool.Destroy;
|
||||||
|
begin
|
||||||
|
FAnchors[0].Free;
|
||||||
|
FAnchors[1].Free;
|
||||||
|
FreeAndNil(FMarks);
|
||||||
|
FreeAndNil(FPointerEnd);
|
||||||
|
FreeAndNil(FPointerStart);
|
||||||
|
inherited;
|
||||||
|
end;
|
||||||
|
|
||||||
|
function TDataPointDistanceTool.Distance(AUnits: TChartUnits): Double;
|
||||||
|
var
|
||||||
|
p1, p2: TDoublePoint;
|
||||||
|
begin
|
||||||
|
case AUnits of
|
||||||
|
cuPercent: exit(0); // Not implemented.
|
||||||
|
cuAxis: begin
|
||||||
|
p1 := PointStart.AxisPos;
|
||||||
|
p2 := PointEnd.AxisPos;
|
||||||
|
end;
|
||||||
|
cuGraph: begin
|
||||||
|
p1 := PointStart.GraphPos;
|
||||||
|
p2 := PointEnd.GraphPos;
|
||||||
|
end;
|
||||||
|
cuPixel: begin
|
||||||
|
with FChart.GraphToImage(PointStart.GraphPos) do
|
||||||
|
p1 := DoublePoint(X, Y);
|
||||||
|
with FChart.GraphToImage(PointEnd.GraphPos) do
|
||||||
|
p2 := DoublePoint(X, Y);
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
case MeasureMode of
|
||||||
|
cdmOnlyX: Result := Abs(p1.X - p2.X);
|
||||||
|
cdmOnlyY: Result := Abs(p1.Y - p2.Y);
|
||||||
|
// The user is responsible for ensuring that both axes have
|
||||||
|
// the same physical dimensions: the xy distance is not
|
||||||
|
// meaningful, for example, if x is in days and y in Dollars.
|
||||||
|
cdmXY: Result := Norm([p1.X - p2.X, p1.Y - p2.Y]);
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TDataPointDistanceTool.DoDraw;
|
||||||
|
|
||||||
|
procedure DrawXorText(ACanvas: TCanvas; APoint: TPoint; const AText: String);
|
||||||
|
var
|
||||||
|
bmp: TBitmap;
|
||||||
|
ext: TSize;
|
||||||
|
begin
|
||||||
|
ext := ACanvas.TextExtent(AText);
|
||||||
|
bmp := TBitmap.Create;
|
||||||
|
try
|
||||||
|
bmp.SetSize(ext.cx, ext.cy);
|
||||||
|
bmp.Canvas.Brush.Style := bsClear;
|
||||||
|
bmp.Canvas.Font := ACanvas.Font;
|
||||||
|
bmp.Canvas.Font.Color := clWhite;
|
||||||
|
bmp.Canvas.TextOut(0, 0, AText);
|
||||||
|
APoint -= ext div 2;
|
||||||
|
BitBlt(
|
||||||
|
ACanvas.Handle, APoint.X, APoint.Y, ext.cx, ext.cy,
|
||||||
|
bmp.Canvas.Handle, 0, 0, SRCINVERT);
|
||||||
|
finally
|
||||||
|
bmp.Free;
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
|
||||||
|
var
|
||||||
|
a: Double;
|
||||||
|
|
||||||
|
procedure DrawPointer(APointer: TDataPointDistanceToolPointer; APos: TPoint);
|
||||||
|
var
|
||||||
|
oldMode: TFPPenMode;
|
||||||
|
oldColor: TColor;
|
||||||
|
oldStyle: TFPBrushStyle;
|
||||||
|
begin
|
||||||
|
with APointer do begin
|
||||||
|
if not Visible then exit;
|
||||||
|
if EffectiveDrawingMode = tdmXor then begin
|
||||||
|
oldMode := Pen.Mode;
|
||||||
|
oldColor := Pen.Color;
|
||||||
|
oldStyle := Brush.Style;
|
||||||
|
Pen.Mode := pmXor;
|
||||||
|
Pen.Color := clWhite;
|
||||||
|
Brush.Style := bsClear;
|
||||||
|
end;
|
||||||
|
try
|
||||||
|
DrawSize(FChart.Drawer, APos, Point(HorizSize, VertSize), clTAColor, a);
|
||||||
|
finally
|
||||||
|
if EffectiveDrawingMode = tdmXor then begin
|
||||||
|
Pen.Mode := oldMode;
|
||||||
|
Pen.Color := oldColor;
|
||||||
|
Brush.Style := oldStyle;
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
|
||||||
|
var
|
||||||
|
p1, p2: TPoint;
|
||||||
|
dummy: TPointArray = nil;
|
||||||
|
begin
|
||||||
|
p1 := FChart.GraphToImage(PointStart.GraphPos);
|
||||||
|
p2 := FChart.GraphToImage(PointEnd.GraphPos);
|
||||||
|
case MeasureMode of
|
||||||
|
cdmOnlyX: p2.Y := p1.Y;
|
||||||
|
cdmOnlyY: p2.X := p1.X;
|
||||||
|
end;
|
||||||
|
if p1 = p2 then exit;
|
||||||
|
if LinePen.Visible then
|
||||||
|
FChart.Drawer.Line(p1, p2);
|
||||||
|
a := ArcTan2(p2.Y - p1.Y, p2.X - p1.X);
|
||||||
|
DrawPointer(PointerStart, p1);
|
||||||
|
DrawPointer(PointerEnd, p2);
|
||||||
|
if Marks.Visible then begin
|
||||||
|
p1 := (p1 + p2) div 2;
|
||||||
|
if EffectiveDrawingMode = tdmNormal then
|
||||||
|
Marks.DrawLabel(FChart.Drawer, p1, p1, GetDistanceText, dummy)
|
||||||
|
else
|
||||||
|
DrawXorText(FChart.Canvas, p1, GetDistanceText);
|
||||||
|
end;
|
||||||
|
inherited;
|
||||||
|
end;
|
||||||
|
|
||||||
|
function TDataPointDistanceTool.FindRef(
|
||||||
|
APoint: TPoint; ADest: TDataPointTool.TPointRef): Boolean;
|
||||||
|
begin
|
||||||
|
if dpdoLockToData in Options then begin
|
||||||
|
FindNearestPoint(APoint);
|
||||||
|
if FSeries = nil then exit(false);
|
||||||
|
with ADest do begin
|
||||||
|
FGraphPos := FNearestGraphPoint;
|
||||||
|
FIndex := PointIndex;
|
||||||
|
FSeries := Self.FSeries;
|
||||||
|
end;
|
||||||
|
end
|
||||||
|
else
|
||||||
|
ADest.SetGraphPos(FChart.ImageToGraph(APoint));
|
||||||
|
Result := true;
|
||||||
|
end;
|
||||||
|
|
||||||
|
// Use Marks.Format and/or OnGetDistanceText event handler to create the text
|
||||||
|
// to be displayed along the connecting line between PointStart and PointEnd.
|
||||||
|
// OnGetDistanceText is useful for converting the distance, for example, to a
|
||||||
|
// datetime string.
|
||||||
|
function TDataPointDistanceTool.GetDistanceText: String;
|
||||||
|
begin
|
||||||
|
Result := Format(Marks.Format, [Distance]);
|
||||||
|
if Assigned(OnGetDistanceText) then
|
||||||
|
OnGetDistanceText(Self, Result);
|
||||||
|
end;
|
||||||
|
|
||||||
|
function TDataPointDistanceTool.GetPointEnd: TDataPointTool.TPointRef;
|
||||||
|
begin
|
||||||
|
Result := TDataPointTool.TPointRef(FAnchors[High(FAnchors)]);
|
||||||
|
end;
|
||||||
|
|
||||||
|
function TDataPointDistanceTool.GetPointStart: TDataPointTool.TPointRef;
|
||||||
|
begin
|
||||||
|
Result := TDataPointTool.TPointRef(FAnchors[0]);
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TDataPointDistanceTool.KeyDown(APoint: TPoint);
|
||||||
|
begin
|
||||||
|
MouseDown(APoint);
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TDataPointDistanceTool.KeyUp(APoint: TPoint);
|
||||||
|
begin
|
||||||
|
MouseUp(APoint);
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TDataPointDistanceTool.MouseDown(APoint: TPoint);
|
||||||
|
begin
|
||||||
|
DoHide;
|
||||||
|
if not FindRef(APoint, PointStart) then exit;
|
||||||
|
Activate;
|
||||||
|
PointEnd.Assign(PointStart);
|
||||||
|
Handled;
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TDataPointDistanceTool.MouseMove(APoint: TPoint);
|
||||||
|
var
|
||||||
|
newEnd: TPointRef;
|
||||||
|
begin
|
||||||
|
if not IsActive then exit;
|
||||||
|
DoHide;
|
||||||
|
newEnd := TPointRef.Create;
|
||||||
|
try
|
||||||
|
if
|
||||||
|
FindRef(APoint, newEnd) and
|
||||||
|
SameTransformations(PointStart.Series, newEnd.Series)
|
||||||
|
then
|
||||||
|
PointEnd.Assign(newEnd);
|
||||||
|
finally
|
||||||
|
FreeAndNil(newEnd);
|
||||||
|
end;
|
||||||
|
if EffectiveDrawingMode = tdmXor then
|
||||||
|
DoDraw;
|
||||||
|
Handled;
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TDataPointDistanceTool.MouseUp(APoint: TPoint);
|
||||||
|
begin
|
||||||
|
MouseMove(APoint);
|
||||||
|
if Assigned(OnMeasure) and (PointStart.GraphPos <> PointEnd.GraphPos) then
|
||||||
|
OnMeasure(Self);
|
||||||
|
Deactivate;
|
||||||
|
end;
|
||||||
|
|
||||||
|
function TDataPointDistanceTool.SameTransformations(
|
||||||
|
ASeries1, ASeries2: TBasicChartSeries): Boolean;
|
||||||
|
|
||||||
|
function CheckAxis(AAxisIndex1, AAxisIndex2: Integer): Boolean; inline;
|
||||||
|
begin
|
||||||
|
Result :=
|
||||||
|
TransformByAxis(FChart.AxisList, AAxisIndex1) =
|
||||||
|
TransformByAxis(FChart.AxisList, AAxisIndex2);
|
||||||
|
end;
|
||||||
|
|
||||||
|
var
|
||||||
|
s1: TCustomChartSeries absolute ASeries1;
|
||||||
|
s2: TCustomChartSeries absolute ASeries2;
|
||||||
|
begin
|
||||||
|
Result :=
|
||||||
|
(ASeries1 = ASeries2) or
|
||||||
|
(ASeries1 is TCustomChartSeries) and
|
||||||
|
(ASeries2 is TCustomChartSeries) and
|
||||||
|
((MeasureMode = cdmOnlyY) or CheckAxis(s1.AxisIndexX, s2.AxisIndexX)) and
|
||||||
|
((MeasureMode = cdmOnlyX) or CheckAxis(s1.AxisIndexY, s2.AxisIndexY));
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TDataPointDistanceTool.SetMarks(AValue: TDataPointDistanceToolMarks);
|
||||||
|
begin
|
||||||
|
if FMarks = AValue then exit;
|
||||||
|
FMarks.Assign(AValue);
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TDataPointDistanceTool.SetOptions(AValue: TOptions);
|
||||||
|
begin
|
||||||
|
if FOptions = AValue then exit;
|
||||||
|
FOptions := AValue;
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TDataPointDistanceTool.SetPointerEnd(
|
||||||
|
AValue: TDataPointDistanceToolPointer);
|
||||||
|
begin
|
||||||
|
if FPointerEnd = AValue then exit;
|
||||||
|
FPointerEnd.Assign(AValue);
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TDataPointDistanceTool.SetPointerStart(
|
||||||
|
AValue: TDataPointDistanceToolPointer);
|
||||||
|
begin
|
||||||
|
if FPointerStart = AValue then exit;
|
||||||
|
FPointerStart.Assign(AValue);
|
||||||
|
end;
|
||||||
|
|
||||||
|
initialization
|
||||||
|
|
||||||
|
RegisterChartToolClass(TDataPointDistanceTool, 'Distance measurement');
|
||||||
|
|
||||||
|
end.
|
||||||
|
|
@ -345,8 +345,6 @@ type
|
|||||||
|
|
||||||
TChartDistanceMode = (cdmXY, cdmOnlyX, cdmOnlyY);
|
TChartDistanceMode = (cdmXY, cdmOnlyX, cdmOnlyY);
|
||||||
|
|
||||||
{ TDataPointTool }
|
|
||||||
|
|
||||||
TDataPointTool = class(TChartTool)
|
TDataPointTool = class(TChartTool)
|
||||||
public
|
public
|
||||||
type
|
type
|
||||||
@ -511,87 +509,6 @@ type
|
|||||||
property Size: Integer read FSize write FSize default -1;
|
property Size: Integer read FSize write FSize default -1;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
TDataPointDistanceTool = class;
|
|
||||||
|
|
||||||
TDataPointDistanceToolMeasureEvent =
|
|
||||||
procedure (ASender: TDataPointDistanceTool) of object;
|
|
||||||
TDataPointGetDistanceTextEvent =
|
|
||||||
procedure (ASender: TDataPointDistanceTool; var AText: String) of object;
|
|
||||||
|
|
||||||
TDataPointDistanceToolPointer = class(TSeriesPointer)
|
|
||||||
published
|
|
||||||
property Style default psVertBar;
|
|
||||||
end;
|
|
||||||
|
|
||||||
TDataPointDistanceToolMarks = class(TCustomChartMarks)
|
|
||||||
public
|
|
||||||
procedure Assign(ASource: TPersistent); override;
|
|
||||||
constructor Create(AOwner: TCustomChart);
|
|
||||||
published
|
|
||||||
property Distance default DEF_MARKS_DISTANCE;
|
|
||||||
property Format;
|
|
||||||
property Frame;
|
|
||||||
property LabelBrush;
|
|
||||||
property LinkPen;
|
|
||||||
end;
|
|
||||||
|
|
||||||
TDataPointDistanceTool = class(TDataPointDrawTool)
|
|
||||||
published
|
|
||||||
type
|
|
||||||
TOptions = set of (dpdoLockToData);
|
|
||||||
|
|
||||||
strict private
|
|
||||||
FAnchors: array of TDataPointTool.TPointRef;
|
|
||||||
FMarks: TDataPointDistanceToolMarks;
|
|
||||||
FMeasureMode: TChartDistanceMode;
|
|
||||||
FOnGetDistanceText: TDataPointGetDistanceTextEvent;
|
|
||||||
FOnMeasure: TDataPointDistanceToolMeasureEvent;
|
|
||||||
FOptions: TOptions;
|
|
||||||
FPointerEnd: TDataPointDistanceToolPointer;
|
|
||||||
FPointerStart: TDataPointDistanceToolPointer;
|
|
||||||
function GetPointEnd: TDataPointTool.TPointRef; inline;
|
|
||||||
procedure SetMarks(AValue: TDataPointDistanceToolMarks);
|
|
||||||
procedure SetOptions(AValue: TOptions);
|
|
||||||
procedure SetPointerEnd(AValue: TDataPointDistanceToolPointer);
|
|
||||||
procedure SetPointerStart(AValue: TDataPointDistanceToolPointer);
|
|
||||||
|
|
||||||
strict protected
|
|
||||||
procedure DoDraw; override;
|
|
||||||
function FindRef(APoint: TPoint; ADest: TDataPointTool.TPointRef): Boolean;
|
|
||||||
function GetDistanceText: String;
|
|
||||||
function SameTransformations(ASeries1, ASeries2: TBasicChartSeries): Boolean;
|
|
||||||
|
|
||||||
public
|
|
||||||
constructor Create(AOwner: TComponent); override;
|
|
||||||
destructor Destroy; override;
|
|
||||||
function Distance(AUnits: TChartUnits = cuGraph): Double;
|
|
||||||
procedure KeyDown(APoint: TPoint); override;
|
|
||||||
procedure KeyUp(APoint: TPoint); override;
|
|
||||||
procedure MouseDown(APoint: TPoint); override;
|
|
||||||
procedure MouseMove(APoint: TPoint); override;
|
|
||||||
procedure MouseUp(APoint: TPoint); override;
|
|
||||||
property PointEnd: TDataPointTool.TPointRef read GetPointEnd;
|
|
||||||
property PointStart: TDataPointTool.TPointRef read FAnchors[0];
|
|
||||||
|
|
||||||
published
|
|
||||||
property DrawingMode;
|
|
||||||
property GrabRadius default 20;
|
|
||||||
property LinePen: TChartPen read FPen write SetPen;
|
|
||||||
property Marks: TDataPointDistanceToolMarks read FMarks write SetMarks;
|
|
||||||
property MeasureMode: TChartDistanceMode
|
|
||||||
read FMeasureMode write FMeasureMode default cdmXY;
|
|
||||||
property Options: TOptions read FOptions write SetOptions default [];
|
|
||||||
property PointerEnd: TDataPointDistanceToolPointer
|
|
||||||
read FPointerEnd write SetPointerEnd;
|
|
||||||
property PointerStart: TDataPointDistanceToolPointer
|
|
||||||
read FPointerStart write SetPointerStart;
|
|
||||||
published
|
|
||||||
property OnGetDistanceText: TDataPointGetDistanceTextEvent
|
|
||||||
read FOnGetDistanceText write FOnGetDistanceText;
|
|
||||||
property OnMeasure: TDataPointDistanceToolMeasureEvent
|
|
||||||
read FOnMeasure write FOnMeasure;
|
|
||||||
end;
|
|
||||||
|
|
||||||
TReticuleTool = class(TChartTool)
|
TReticuleTool = class(TChartTool)
|
||||||
public
|
public
|
||||||
procedure MouseMove(APoint: TPoint); override;
|
procedure MouseMove(APoint: TPoint); override;
|
||||||
@ -607,12 +524,9 @@ var
|
|||||||
implementation
|
implementation
|
||||||
|
|
||||||
uses
|
uses
|
||||||
Graphics, GraphMath, InterfaceBase, LCLIntf, LCLType, Math, SysUtils,
|
GraphMath, InterfaceBase, Math, SysUtils,
|
||||||
TAChartAxis, TACustomSeries, TADrawerCanvas, TAEnumerators, TAGeometry, TAMath;
|
TAChartAxis, TACustomSeries, TADrawerCanvas, TAEnumerators, TAGeometry, TAMath;
|
||||||
|
|
||||||
const
|
|
||||||
DEF_DISTANCE_FORMAT = '%0:.9g';
|
|
||||||
|
|
||||||
function InitBuiltinTools(AChart: TChart): TBasicChartToolset;
|
function InitBuiltinTools(AChart: TChart): TBasicChartToolset;
|
||||||
var
|
var
|
||||||
ts: TChartToolset;
|
ts: TChartToolset;
|
||||||
@ -642,27 +556,6 @@ begin
|
|||||||
ToolsClassRegistry.AddObject(ACaption, TObject(AToolClass));
|
ToolsClassRegistry.AddObject(ACaption, TObject(AToolClass));
|
||||||
end;
|
end;
|
||||||
|
|
||||||
{ TDataPointDistanceToolMarks }
|
|
||||||
|
|
||||||
procedure TDataPointDistanceToolMarks.Assign(ASource: TPersistent);
|
|
||||||
begin
|
|
||||||
if ASource is TChartMarks then
|
|
||||||
with TDataPointDistanceToolMarks(ASource) do begin
|
|
||||||
Self.FLabelBrush.Assign(FLabelBrush);
|
|
||||||
Self.FLabelFont.Assign(FLabelFont);
|
|
||||||
Self.FLinkPen.Assign(FLinkPen);
|
|
||||||
end;
|
|
||||||
inherited Assign(ASource);
|
|
||||||
end;
|
|
||||||
|
|
||||||
constructor TDataPointDistanceToolMarks.Create(AOwner: TCustomChart);
|
|
||||||
begin
|
|
||||||
inherited Create(AOwner);
|
|
||||||
FDistance := DEF_MARKS_DISTANCE;
|
|
||||||
FLabelBrush.Color := clYellow;
|
|
||||||
Format := DEF_DISTANCE_FORMAT;
|
|
||||||
end;
|
|
||||||
|
|
||||||
{ TDataPointTool.TPointRef }
|
{ TDataPointTool.TPointRef }
|
||||||
|
|
||||||
procedure TDataPointTool.TPointRef.Assign(ASource: TPointRef);
|
procedure TDataPointTool.TPointRef.Assign(ASource: TPointRef);
|
||||||
@ -1826,271 +1719,6 @@ begin
|
|||||||
end;
|
end;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
|
||||||
{ TDataPointDistanceTool }
|
|
||||||
|
|
||||||
constructor TDataPointDistanceTool.Create(AOwner: TComponent);
|
|
||||||
begin
|
|
||||||
inherited;
|
|
||||||
SetLength(FAnchors, 2);
|
|
||||||
FAnchors[0] := TDataPointTool.TPointRef.Create;
|
|
||||||
FAnchors[1] := TDataPointTool.TPointRef.Create;
|
|
||||||
FMarks := TDataPointDistanceToolMarks.Create(nil);
|
|
||||||
FPointerEnd := TDataPointDistanceToolPointer.Create(nil);
|
|
||||||
FPointerStart := TDataPointDistanceToolPointer.Create(nil);
|
|
||||||
end;
|
|
||||||
|
|
||||||
destructor TDataPointDistanceTool.Destroy;
|
|
||||||
begin
|
|
||||||
FAnchors[0].Free;
|
|
||||||
FAnchors[1].Free;
|
|
||||||
FreeAndNil(FMarks);
|
|
||||||
FreeAndNil(FPointerEnd);
|
|
||||||
FreeAndNil(FPointerStart);
|
|
||||||
inherited;
|
|
||||||
end;
|
|
||||||
|
|
||||||
function TDataPointDistanceTool.Distance(AUnits: TChartUnits): Double;
|
|
||||||
var
|
|
||||||
p1, p2: TDoublePoint;
|
|
||||||
begin
|
|
||||||
case AUnits of
|
|
||||||
cuPercent: exit(0); // Not implemented.
|
|
||||||
cuAxis: begin
|
|
||||||
p1 := PointStart.AxisPos;
|
|
||||||
p2 := PointEnd.AxisPos;
|
|
||||||
end;
|
|
||||||
cuGraph: begin
|
|
||||||
p1 := PointStart.GraphPos;
|
|
||||||
p2 := PointEnd.GraphPos;
|
|
||||||
end;
|
|
||||||
cuPixel: begin
|
|
||||||
with FChart.GraphToImage(PointStart.GraphPos) do
|
|
||||||
p1 := DoublePoint(X, Y);
|
|
||||||
with FChart.GraphToImage(PointEnd.GraphPos) do
|
|
||||||
p2 := DoublePoint(X, Y);
|
|
||||||
end;
|
|
||||||
end;
|
|
||||||
case MeasureMode of
|
|
||||||
cdmOnlyX: Result := Abs(p1.X - p2.X);
|
|
||||||
cdmOnlyY: Result := Abs(p1.Y - p2.Y);
|
|
||||||
// The user is responsible for ensuring that both axes have
|
|
||||||
// the same physical dimensions: the xy distance is not
|
|
||||||
// meaningful, for example, if x is in days and y in Dollars.
|
|
||||||
cdmXY: Result := Norm([p1.X - p2.X, p1.Y - p2.Y]);
|
|
||||||
end;
|
|
||||||
end;
|
|
||||||
|
|
||||||
procedure TDataPointDistanceTool.DoDraw;
|
|
||||||
|
|
||||||
procedure DrawXorText(ACanvas: TCanvas; APoint: TPoint; const AText: String);
|
|
||||||
var
|
|
||||||
bmp: TBitmap;
|
|
||||||
ext: TSize;
|
|
||||||
begin
|
|
||||||
ext := ACanvas.TextExtent(AText);
|
|
||||||
bmp := TBitmap.Create;
|
|
||||||
try
|
|
||||||
bmp.SetSize(ext.cx, ext.cy);
|
|
||||||
bmp.Canvas.Brush.Style := bsClear;
|
|
||||||
bmp.Canvas.Font := ACanvas.Font;
|
|
||||||
bmp.Canvas.Font.Color := clWhite;
|
|
||||||
bmp.Canvas.TextOut(0, 0, AText);
|
|
||||||
APoint -= ext div 2;
|
|
||||||
BitBlt(
|
|
||||||
ACanvas.Handle, APoint.X, APoint.Y, ext.cx, ext.cy,
|
|
||||||
bmp.Canvas.Handle, 0, 0, SRCINVERT);
|
|
||||||
finally
|
|
||||||
bmp.Free;
|
|
||||||
end;
|
|
||||||
end;
|
|
||||||
|
|
||||||
var
|
|
||||||
a: Double;
|
|
||||||
|
|
||||||
procedure DrawPointer(APointer: TDataPointDistanceToolPointer; APos: TPoint);
|
|
||||||
var
|
|
||||||
oldMode: TFPPenMode;
|
|
||||||
oldColor: TColor;
|
|
||||||
oldStyle: TFPBrushStyle;
|
|
||||||
begin
|
|
||||||
with APointer do begin
|
|
||||||
if not Visible then exit;
|
|
||||||
if EffectiveDrawingMode = tdmXor then begin
|
|
||||||
oldMode := Pen.Mode;
|
|
||||||
oldColor := Pen.Color;
|
|
||||||
oldStyle := Brush.Style;
|
|
||||||
Pen.Mode := pmXor;
|
|
||||||
Pen.Color := clWhite;
|
|
||||||
Brush.Style := bsClear;
|
|
||||||
end;
|
|
||||||
try
|
|
||||||
DrawSize(FChart.Drawer, APos, Point(HorizSize, VertSize), clTAColor, a);
|
|
||||||
finally
|
|
||||||
if EffectiveDrawingMode = tdmXor then begin
|
|
||||||
Pen.Mode := oldMode;
|
|
||||||
Pen.Color := oldColor;
|
|
||||||
Brush.Style := oldStyle;
|
|
||||||
end;
|
|
||||||
end;
|
|
||||||
end;
|
|
||||||
end;
|
|
||||||
|
|
||||||
var
|
|
||||||
p1, p2: TPoint;
|
|
||||||
dummy: TPointArray = nil;
|
|
||||||
begin
|
|
||||||
p1 := FChart.GraphToImage(PointStart.GraphPos);
|
|
||||||
p2 := FChart.GraphToImage(PointEnd.GraphPos);
|
|
||||||
case MeasureMode of
|
|
||||||
cdmOnlyX: p2.Y := p1.Y;
|
|
||||||
cdmOnlyY: p2.X := p1.X;
|
|
||||||
end;
|
|
||||||
if p1 = p2 then exit;
|
|
||||||
if LinePen.Visible then
|
|
||||||
FChart.Drawer.Line(p1, p2);
|
|
||||||
a := ArcTan2(p2.Y - p1.Y, p2.X - p1.X);
|
|
||||||
DrawPointer(PointerStart, p1);
|
|
||||||
DrawPointer(PointerEnd, p2);
|
|
||||||
if Marks.Visible then begin
|
|
||||||
p1 := (p1 + p2) div 2;
|
|
||||||
if EffectiveDrawingMode = tdmNormal then
|
|
||||||
Marks.DrawLabel(FChart.Drawer, p1, p1, GetDistanceText, dummy)
|
|
||||||
else
|
|
||||||
DrawXorText(FChart.Canvas, p1, GetDistanceText);
|
|
||||||
end;
|
|
||||||
inherited;
|
|
||||||
end;
|
|
||||||
|
|
||||||
function TDataPointDistanceTool.FindRef(
|
|
||||||
APoint: TPoint; ADest: TDataPointTool.TPointRef): Boolean;
|
|
||||||
begin
|
|
||||||
if dpdoLockToData in Options then begin
|
|
||||||
FindNearestPoint(APoint);
|
|
||||||
if FSeries = nil then exit(false);
|
|
||||||
with ADest do begin
|
|
||||||
FGraphPos := FNearestGraphPoint;
|
|
||||||
FIndex := PointIndex;
|
|
||||||
FSeries := Self.FSeries;
|
|
||||||
end;
|
|
||||||
end
|
|
||||||
else
|
|
||||||
ADest.SetGraphPos(FChart.ImageToGraph(APoint));
|
|
||||||
Result := true;
|
|
||||||
end;
|
|
||||||
|
|
||||||
// Use Marks.Format and/or OnGetDistanceText event handler to create the text
|
|
||||||
// to be displayed along the connecting line between PointStart and PointEnd.
|
|
||||||
// OnGetDistanceText is useful for converting the distance, for example, to a
|
|
||||||
// datetime string.
|
|
||||||
function TDataPointDistanceTool.GetDistanceText: String;
|
|
||||||
begin
|
|
||||||
Result := Format(Marks.Format, [Distance]);
|
|
||||||
if Assigned(OnGetDistanceText) then
|
|
||||||
OnGetDistanceText(Self, Result);
|
|
||||||
end;
|
|
||||||
|
|
||||||
function TDataPointDistanceTool.GetPointEnd: TDataPointTool.TPointRef;
|
|
||||||
begin
|
|
||||||
Result := FAnchors[High(FAnchors)];
|
|
||||||
end;
|
|
||||||
|
|
||||||
procedure TDataPointDistanceTool.KeyDown(APoint: TPoint);
|
|
||||||
begin
|
|
||||||
MouseDown(APoint);
|
|
||||||
end;
|
|
||||||
|
|
||||||
procedure TDataPointDistanceTool.KeyUp(APoint: TPoint);
|
|
||||||
begin
|
|
||||||
MouseUp(APoint);
|
|
||||||
end;
|
|
||||||
|
|
||||||
procedure TDataPointDistanceTool.MouseDown(APoint: TPoint);
|
|
||||||
begin
|
|
||||||
DoHide;
|
|
||||||
if not FindRef(APoint, PointStart) then exit;
|
|
||||||
Activate;
|
|
||||||
PointEnd.Assign(PointStart);
|
|
||||||
Handled;
|
|
||||||
end;
|
|
||||||
|
|
||||||
procedure TDataPointDistanceTool.MouseMove(APoint: TPoint);
|
|
||||||
var
|
|
||||||
newEnd: TPointRef;
|
|
||||||
begin
|
|
||||||
if not IsActive then exit;
|
|
||||||
DoHide;
|
|
||||||
newEnd := TPointRef.Create;
|
|
||||||
try
|
|
||||||
if
|
|
||||||
FindRef(APoint, newEnd) and
|
|
||||||
SameTransformations(PointStart.Series, newEnd.Series)
|
|
||||||
then
|
|
||||||
PointEnd.Assign(newEnd);
|
|
||||||
finally
|
|
||||||
FreeAndNil(newEnd);
|
|
||||||
end;
|
|
||||||
if EffectiveDrawingMode = tdmXor then
|
|
||||||
DoDraw;
|
|
||||||
Handled;
|
|
||||||
end;
|
|
||||||
|
|
||||||
procedure TDataPointDistanceTool.MouseUp(APoint: TPoint);
|
|
||||||
begin
|
|
||||||
MouseMove(APoint);
|
|
||||||
if Assigned(OnMeasure) and (PointStart.GraphPos <> PointEnd.GraphPos) then
|
|
||||||
OnMeasure(Self);
|
|
||||||
Deactivate;
|
|
||||||
end;
|
|
||||||
|
|
||||||
function TDataPointDistanceTool.SameTransformations(
|
|
||||||
ASeries1, ASeries2: TBasicChartSeries): Boolean;
|
|
||||||
|
|
||||||
function CheckAxis(AAxisIndex1, AAxisIndex2: Integer): Boolean; inline;
|
|
||||||
begin
|
|
||||||
Result :=
|
|
||||||
TransformByAxis(FChart.AxisList, AAxisIndex1) =
|
|
||||||
TransformByAxis(FChart.AxisList, AAxisIndex2);
|
|
||||||
end;
|
|
||||||
|
|
||||||
var
|
|
||||||
s1: TCustomChartSeries absolute ASeries1;
|
|
||||||
s2: TCustomChartSeries absolute ASeries2;
|
|
||||||
begin
|
|
||||||
Result :=
|
|
||||||
(ASeries1 = ASeries2) or
|
|
||||||
(ASeries1 is TCustomChartSeries) and
|
|
||||||
(ASeries2 is TCustomChartSeries) and
|
|
||||||
((MeasureMode = cdmOnlyY) or CheckAxis(s1.AxisIndexX, s2.AxisIndexX)) and
|
|
||||||
((MeasureMode = cdmOnlyX) or CheckAxis(s1.AxisIndexY, s2.AxisIndexY));
|
|
||||||
end;
|
|
||||||
|
|
||||||
procedure TDataPointDistanceTool.SetMarks(AValue: TDataPointDistanceToolMarks);
|
|
||||||
begin
|
|
||||||
if FMarks = AValue then exit;
|
|
||||||
FMarks.Assign(AValue);
|
|
||||||
end;
|
|
||||||
|
|
||||||
procedure TDataPointDistanceTool.SetOptions(AValue: TOptions);
|
|
||||||
begin
|
|
||||||
if FOptions = AValue then exit;
|
|
||||||
FOptions := AValue;
|
|
||||||
end;
|
|
||||||
|
|
||||||
procedure TDataPointDistanceTool.SetPointerEnd(
|
|
||||||
AValue: TDataPointDistanceToolPointer);
|
|
||||||
begin
|
|
||||||
if FPointerEnd = AValue then exit;
|
|
||||||
FPointerEnd.Assign(AValue);
|
|
||||||
end;
|
|
||||||
|
|
||||||
procedure TDataPointDistanceTool.SetPointerStart(
|
|
||||||
AValue: TDataPointDistanceToolPointer);
|
|
||||||
begin
|
|
||||||
if FPointerStart = AValue then exit;
|
|
||||||
FPointerStart.Assign(AValue);
|
|
||||||
end;
|
|
||||||
|
|
||||||
initialization
|
initialization
|
||||||
|
|
||||||
ToolsClassRegistry := TStringList.Create;
|
ToolsClassRegistry := TStringList.Create;
|
||||||
@ -2106,7 +1734,6 @@ initialization
|
|||||||
RegisterChartToolClass(TDataPointDragTool, 'Data point drag');
|
RegisterChartToolClass(TDataPointDragTool, 'Data point drag');
|
||||||
RegisterChartToolClass(TDataPointHintTool, 'Data point hint');
|
RegisterChartToolClass(TDataPointHintTool, 'Data point hint');
|
||||||
RegisterChartToolClass(TDataPointCrosshairTool, 'Data point crosshair');
|
RegisterChartToolClass(TDataPointCrosshairTool, 'Data point crosshair');
|
||||||
RegisterChartToolClass(TDataPointDistanceTool, 'Distance measurement');
|
|
||||||
RegisterChartToolClass(TUserDefinedTool, 'User-defined');
|
RegisterChartToolClass(TUserDefinedTool, 'User-defined');
|
||||||
|
|
||||||
finalization
|
finalization
|
||||||
|
Loading…
Reference in New Issue
Block a user