mirror of
https://gitlab.com/freepascal.org/lazarus/lazarus.git
synced 2025-09-14 11:39:25 +02:00
TAChart: Redo NearestPoint system for finer control.
git-svn-id: trunk@53929 -
This commit is contained in:
parent
cb61dd8c18
commit
deb7ac4360
@ -96,11 +96,14 @@ type
|
||||
TDoublePointBoolArr = array [Boolean] of Double;
|
||||
|
||||
TNearestPointTarget = (
|
||||
nptPoint, // looking for nearest point at (x,y) only
|
||||
nptPointList, // ... and values in YList
|
||||
nptInside // depends on series type (like "inside bar", etc.)
|
||||
nptPoint, // Look for the nearest point at (x, y)
|
||||
nptXList, // Check additional x values in XList
|
||||
nptYList, // Check additional y values in YList
|
||||
nptCustom // Depends on series type (e.g., TBarSeries --> click inside bar.)
|
||||
);
|
||||
|
||||
TNearestPointTargets = set of TNearestPointTarget;
|
||||
|
||||
{ TIntervalList }
|
||||
|
||||
TIntervalList = class
|
||||
|
@ -29,13 +29,14 @@ type
|
||||
FOptimizeX: Boolean;
|
||||
FPoint: TPoint;
|
||||
FRadius: Integer;
|
||||
FTarget: TNearestPointTarget;
|
||||
FTargets: TNearestPointTargets;
|
||||
end;
|
||||
|
||||
TNearestPointResults = record
|
||||
FDist: Integer;
|
||||
FImg: TPoint;
|
||||
FIndex: Integer; // Point index
|
||||
FXIndex: Integer; // Index to be used in Source.GetX()
|
||||
FYIndex: Integer; // Index to be used in Source.GetY()
|
||||
FValue: TDoublePoint;
|
||||
end;
|
||||
@ -1275,6 +1276,7 @@ var
|
||||
begin
|
||||
AResults.FDist := Sqr(AParams.FRadius) + 1;
|
||||
AResults.FIndex := -1;
|
||||
AResults.FXIndex := 0;
|
||||
AResults.FYIndex := 0;
|
||||
if AParams.FOptimizeX then
|
||||
Source.FindBounds(
|
||||
@ -1284,38 +1286,63 @@ begin
|
||||
lb := 0;
|
||||
ub := Count - 1;
|
||||
end;
|
||||
|
||||
for i := lb to ub do begin
|
||||
sp := Source[i]^.Point;
|
||||
if IsNan(sp) then continue;
|
||||
if IsNan(sp) then
|
||||
continue;
|
||||
|
||||
// Since axis transformation may be non-linear, the distance should be
|
||||
// measured in screen coordinates. With high zoom ratios this may lead to
|
||||
// an integer overflow, so ADistFunc should use saturation arithmetics.
|
||||
pt := ParentChart.GraphToImage(AxisToGraph(sp));
|
||||
dist := AParams.FDistFunc(AParams.FPoint, pt);
|
||||
case AParams.FTarget of
|
||||
nptPoint: ;
|
||||
nptPointList:
|
||||
begin
|
||||
tmpSp := sp;
|
||||
for j := 0 to Source.YCount - 2 do begin
|
||||
if FStacked then
|
||||
tmpSp.Y += Source[i]^.YList[j] else
|
||||
tmpSp.Y := Source[i]^.YList[j];
|
||||
tmpPt := ParentChart.GraphToImage(AxisToGraph(tmpSp));
|
||||
tmpDist := AParams.FDistFunc(AParams.FPoint, tmpPt);
|
||||
if tmpDist < dist then begin
|
||||
dist := tmpDist;
|
||||
sp := tmpSp;
|
||||
pt := tmpPt;
|
||||
AResults.FYIndex := j + 1; // FYIndex = 0 is the regular y
|
||||
end;
|
||||
end;
|
||||
end;
|
||||
nptInside:
|
||||
// to be handled by descendants
|
||||
exit;
|
||||
|
||||
// Find nearest point of datapoint at (x, y)
|
||||
if (nptPoint in AParams.FTargets) then begin
|
||||
pt := ParentChart.GraphToImage(AxisToGraph(sp));
|
||||
dist := AParams.FDistFunc(AParams.FPoint, pt);
|
||||
end;
|
||||
if dist >= AResults.FDist then continue;
|
||||
|
||||
// Find nearest point to additional y values (at x).
|
||||
// In case of stacked data points check the stacked values.
|
||||
if (nptYList in AParams.FTargets) then begin
|
||||
tmpSp := sp;
|
||||
for j := 0 to Source.YCount - 2 do begin
|
||||
if FStacked then
|
||||
tmpSp.Y += Source[i]^.YList[j] else
|
||||
tmpSp.Y := Source[i]^.YList[j];
|
||||
tmpPt := ParentChart.GraphToImage(AxisToGraph(tmpSp));
|
||||
tmpDist := AParams.FDistFunc(AParams.FPoint, tmpPt);
|
||||
if tmpDist < dist then begin
|
||||
dist := tmpDist;
|
||||
sp := tmpSp;
|
||||
pt := tmpPt;
|
||||
AResults.FYIndex := j + 1; // FYIndex = 0 refers to the regular y
|
||||
end;
|
||||
end;
|
||||
end;
|
||||
|
||||
// Find nearest point of additional x values (at y)
|
||||
if (nptXList in AParams.FTargets) then begin
|
||||
tmpSp := sp;
|
||||
for j := 0 to Source.XCount - 2 do begin
|
||||
tmpSp.X := Source[i]^.XList[j];
|
||||
tmpPt := parentChart.GraphToImage(AxisToGraph(tmpSp));
|
||||
tmpDist := AParams.FDistFunc(AParams.FPoint, tmpPt);
|
||||
if tmpDist < dist then begin
|
||||
dist := tmpDist;
|
||||
sp := tmpSp;
|
||||
pt := tmpPt;
|
||||
AResults.FXIndex := j + 1; // FXindex = 0 refers to the regular x
|
||||
end;
|
||||
end;
|
||||
end;
|
||||
|
||||
// The case nptCustom is not handled here, it depends on the series type.
|
||||
// TBarSeries, for example, checks whether AParams.FPoint is inside a bar.
|
||||
|
||||
if dist >= AResults.FDist then
|
||||
continue;
|
||||
|
||||
AResults.FDist := dist;
|
||||
AResults.FIndex := i;
|
||||
AResults.FImg := pt;
|
||||
|
@ -403,7 +403,7 @@ type
|
||||
FDistanceMode: TChartDistanceMode;
|
||||
FGrabRadius: Integer;
|
||||
FMouseInsideOnly: Boolean;
|
||||
FTarget: TNearestPointTarget;
|
||||
FTargets: TNearestPointTargets;
|
||||
function GetAffectedSeries: String; inline;
|
||||
function GetIsSeriesAffected(AIndex: Integer): Boolean; inline;
|
||||
procedure SetAffectedSeries(AValue: String); inline;
|
||||
@ -416,8 +416,8 @@ type
|
||||
procedure FindNearestPoint(APoint: TPoint);
|
||||
property MouseInsideOnly: Boolean
|
||||
read FMouseInsideOnly write FMouseInsideOnly default false;
|
||||
property Target: TNearestPointTarget
|
||||
read FTarget write FTarget default nptPoint;
|
||||
property Targets: TNearestPointTargets
|
||||
read FTargets write FTargets default [nptPoint, nptXList, nptYList, nptCustom];
|
||||
public
|
||||
constructor Create(AOwner: TComponent); override;
|
||||
public
|
||||
@ -460,7 +460,7 @@ type
|
||||
property ActiveCursor default crSizeAll;
|
||||
property EscapeCancels default true;
|
||||
property KeepDistance: Boolean read FKeepDistance write FKeepDistance default false;
|
||||
property Target;
|
||||
property Targets;
|
||||
property OnDrag: TDataPointDragEvent read FOnDrag write FOnDrag;
|
||||
property OnDragStart: TDataPointDragEvent
|
||||
read FOnDragStart write FOnDragStart;
|
||||
@ -477,7 +477,7 @@ type
|
||||
procedure MouseUp(APoint: TPoint); override;
|
||||
published
|
||||
property ActiveCursor;
|
||||
property Target;
|
||||
property Targets;
|
||||
property OnPointClick: TChartToolEvent
|
||||
read FOnPointClick write FOnPointClick;
|
||||
end;
|
||||
@ -518,7 +518,7 @@ type
|
||||
procedure MouseUp(APoint: TPoint); override;
|
||||
published
|
||||
property ActiveCursor;
|
||||
property Target;
|
||||
property Targets;
|
||||
property OnHint: TChartToolHintEvent read FOnHint write FOnHint;
|
||||
property OnHintLocation: TChartToolHintLocationEvent
|
||||
read FOnHintLocation write FOnHintLocation;
|
||||
@ -581,7 +581,7 @@ type
|
||||
property Shape: TChartCrosshairShape
|
||||
read FShape write FShape default ccsCross;
|
||||
property Size: Integer read FSize write FSize default -1;
|
||||
property Target;
|
||||
property Targets;
|
||||
end;
|
||||
|
||||
TReticuleTool = class(TChartTool)
|
||||
@ -1613,6 +1613,7 @@ begin
|
||||
SetPropDefaults(Self, ['GrabRadius']);
|
||||
FPointIndex := -1;
|
||||
FYIndex := 0;
|
||||
FTargets := [nptPoint, nptXList, nptYList, nptCustom]; // check all targets
|
||||
end;
|
||||
|
||||
procedure TDataPointTool.FindNearestPoint(APoint: TPoint);
|
||||
@ -1662,7 +1663,7 @@ begin
|
||||
p.FPoint := APoint;
|
||||
p.FRadius := GrabRadius;
|
||||
p.FOptimizeX := DistanceMode <> cdmOnlyY;
|
||||
p.FTarget := Target;
|
||||
p.FTargets := Targets;
|
||||
best.FDist := MaxInt;
|
||||
for s in CustomSeries(FChart, FAffectedSeries.AsBooleans(FChart.SeriesCount)) do
|
||||
if
|
||||
|
Loading…
Reference in New Issue
Block a user