TAChart: Redo NearestPoint system for finer control.

git-svn-id: trunk@53929 -
This commit is contained in:
wp 2017-01-11 19:59:52 +00:00
parent cb61dd8c18
commit deb7ac4360
3 changed files with 69 additions and 38 deletions

View File

@ -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

View File

@ -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;

View File

@ -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