Page 1 of 1

[SOLVED]How to find the nearest "series" when clic

Posted: Sat Dec 27, 2008 2:27 pm
by 10546441
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

more or less solved - replies needed

Posted: Sat Dec 27, 2008 8:10 pm
by 10546441
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.

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;