Any help appreciated
I am currently using a TChart with 1 or more (up to 50+) Lineseries for displaying "sticks" - 2 points in the end - connected by a line.
The user needs to "click" the chart surface, and he clicks "near" to one of these lines / points but not exactly on the line or the point.
This way I can not use the "OnClickSeries()" Method. This one works fine with accurate pointing the line / point.
What I need is a function to find the correct series which is the nearest to the clicked point on the chart.
Hope I explaining it well enough.
Thanks in advance.
Christian
[SOLVED]How to find the nearest "series" when clic
[SOLVED]How to find the nearest "series" when clic
Last edited by ChZiegelt on Sun Dec 28, 2008 3:35 pm, edited 1 time in total.
more or less solved - replies needed
I found a solution but only with modifying existing "nearest Tool" code.
Maybe someone else has a better idea how to do it ?
Help is appreciated
The below Code workes for me - simple Chart and 5 line series.
Maybe someone else has a better idea how to do it ?
Help is appreciated
The below Code workes for me - simple Chart and 5 line series.
Code: Select all
{*------------------------------------------------------------------------------
Procedure : TeeDistance()
Author : Christian Ziegelt
Date : 27-Dez-2008
@param x ParameterDescription
@param y ParameterDescription
@return ResultDescription
------------------------------------------------------------------------------*}
Function TeeDistance(const x,y:Double):Double; {$IFDEF D9}inline;{$ENDIF} // 7.0 changed to "double"
begin
result:=Sqrt(Sqr(x)+Sqr(y));
end;
{*------------------------------------------------------------------------------
Procedure : PointInRect()
Author : Christian Ziegelt
Date : 27-Dez-2008
@param Rect ParameterDescription
@param x ParameterDescription
@param y ParameterDescription
@return ResultDescription
------------------------------------------------------------------------------*}
function PointInRect(Const Rect:TRect; x,y:Integer):Boolean; {$IFDEF D9}inline;{$ENDIF}
begin
result:=(x>=Rect.Left) and (y>=Rect.Top) and
(x<=Rect.Right) and (y<=Rect.Bottom); // 7.0
end;
{*------------------------------------------------------------------------------
Procedure : TeeGetFirstLastSeries()
Author : Christian Ziegelt
Date : 27-Dez-2008
@param Series ParameterDescription
@param AMin ParameterDescription
@param AMax ParameterDescription
@return ResultDescription
------------------------------------------------------------------------------*}
Function TForm1.TeeGetFirstLastSeries(Series:TChartSeries;
out AMin,AMax:Integer):Boolean; { 5.01 }
begin
AMin:=Series.FirstValueIndex;
if AMin<0 then AMin:=0;
AMax:=Series.LastValueIndex;
if AMax<0 then AMax:=Series.Count-1
else
if AMax>=Series.Count then AMax:=Series.Count-1; { 5.03 }
result:=(Series.Count>0) and (AMin<=Series.Count) and (AMax<=Series.Count);
end;
{*------------------------------------------------------------------------------
Procedure : GetNearestPoint()
Author : Christian Ziegelt
Date : 27-Dez-2008
@param Series ParameterDescription
@param X ParameterDescription
@param Y ParameterDescription
@param IncludeNulls ParameterDescription
@return ResultDescription
------------------------------------------------------------------------------*}
Function TForm1.GetNearestPointSeries(X,Y:Integer; IncludeNulls:Boolean):Integer;
var seriesI: Integer;
t : Integer;
Dif : Integer;
Dist : Integer;
tmpMin : Integer;
tmpMax : Integer;
tmpX : Integer;
tmpY : Integer;
tmpZList : TChartValueList;
tmpZ : Integer;
tmpHit : Boolean;
series: TCHartSeries;
PointNum: Integer;
begin
{ Anfangs Distanz }
Dif:=1000000;
for seriesI := 0 to chart1.SeriesCount -1 do
begin
Series := chart1.Series[seriesI];
PointNum := Series.Clicked(X,Y);
{ Ein Punkt wurde direkt angeklickt - serie zurückgeben }
if PointNum > TeeNoPointClicked then
begin
Result := seriesI;
exit;
end
{ Kein Punkt direkt angeklickt - ermitteln }
else if PointNum = TeeNoPointClicked then
begin
if TeeGetFirstLastSeries(Series,tmpMin,tmpMax) then
begin
tmpZList:=Series.GetYValueList('Z'); // 7.0
for t:=tmpMin to tmpMax do { <-- traverse all points in a Series... }
if IncludeNulls or (not Series.IsNull(t)) then // 7.0
begin
{ calculate point Selection Position in pixels }
TSeriesAccess(Series).CalcSelectionPos(t,tmpX,tmpY);
if Series.HasZValues then
begin
tmpZ:=Series.ParentChart.Axes.Depth.CalcPosValue(tmpZList.Value[t]);
with Series.ParentChart.Canvas.Calculate3DPosition(tmpX,tmpY,tmpZ) do
begin
tmpX:=X;
tmpY:=Y;
end;
tmpHit:=True;
end
else
tmpHit:=PointInRect(Series.ParentChart.ChartRect,tmpX,tmpY);
if tmpHit then
begin
// Calculate distance in pixels...
Dist:=Round(TeeDistance(X-tmpX,Y-tmpY));
if Dist<Dif then { store if distance is lower... }
begin
Dif:=Dist;
pointNum := t; { <-- set this point to be the nearest... }
result := seriesI;
end;
end;
end; { Nulls included }
end; { min / max get }
end;{ if not Point clicked }
end; { for all series }
end;
{*------------------------------------------------------------------------------
Procedure : Chart1ClickBackground()
Author : Christian Ziegelt
Date : 27-Dez-2008
@param Sender ParameterDescription
@param Button ParameterDescription
@param Shift ParameterDescription
@param X ParameterDescription
@param Y ParameterDescription
@return ResultDescription
------------------------------------------------------------------------------*}
procedure TForm1.Chart1ClickBackground(Sender: TCustomChart; Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
var
i: Integer;
begin
{ Zur Klick Position noch die nächste Serie entdecken }
i := GetNearestPointSeries(X,Y, true);
MessageDlg(Format('ClickBackgr: Tag: %d Index: %d', [chart1.Series[i].Tag, i]), mtWarning, [mbOK], 0);
end;