delphicanvasdrawinglinesangle

How to Determine if MousePos touches the Line?


Im not sure if this clear enough to describe the problem...

I have 2 different points, Start -> End then it forms a Line.

I would like to make an event on MouseMove if the MousePos touches the lines..

what i did was using the PtInRect but the results are for the rectangle area not the line. is there any function to use or manually made. any idea?


Solution

  • Check this code (original source check if the Cursor is on a line?) from torry's

    type
      TForm73 = class(TForm)
        Button1: TButton;
        procedure FormCreate(Sender: TObject);
        procedure FormMouseMove(Sender: TObject; Shift: TShiftState; X, Y: Integer);
        procedure Button1Click(Sender: TObject);
      private
        x1,y1,x2,y2 : Integer;
      public
      end;
    
    var
      Form73: TForm73;
    
    implementation
    
    {$R *.dfm}
    
    function PontInLine(X, Y, x1, y1, x2, y2, d: Integer): Boolean;
    var
      sine, cosinus: Double;
      dx, dy, len: Integer;
    begin
      if d = 0 then d := 1;
      asm
        fild(y2)
        fisub(y1) // Y-Difference
        fild(x2)
        fisub(x1) // X-Difference
        fpatan    // Angle of the line in st(0)
        fsincos   // Cosinus in st(0), Sinus in st(1)
        fstp cosinus
        fstp sine
      end;
      dx  := Round(cosinus * (x - x1) + sine * (y - y1));
      dy  := Round(cosinus * (y - y1) - sine * (x - x1));
      len := Round(cosinus * (x2 - x1) + sine * (y2 - y1)); // length of line
      Result:= (dy > -d) and (dy < d) and (dx > -d) and (dx < len + d);
    end;
    
    
    procedure TForm73.Button1Click(Sender: TObject);
    begin
      with Canvas do
      begin
        Pen.Color := clRed;
        MoveTo(x1,y1);
        LineTo(x2,y2);
      end;
    end;
    
    procedure TForm73.FormCreate(Sender: TObject);
    begin
       x1:=10;
       y1:=100;
       x2:=200;
       y2:=150;
    end;
    
    procedure TForm73.FormMouseMove(Sender: TObject; Shift: TShiftState; X,
      Y: Integer);
    var
      p: TPoint;
    begin
      GetCursorPos(p);
      p := ScreenToClient(p);
      if PontInLine(p.x, p.y, x1, y1, x2, y2, 1) then
        Caption := 'Mouse on line.'
      else
        Caption := 'Mouse not on line.'
    end;
    

    UPDATE

    This is the equivalent function PontInLine without use assembly (directly).

    uses
      Math;
    
    function PontInLine(X, Y, x1, y1, x2, y2, d: Integer): Boolean;
    var
      Theta,  sine, cosinus: Double;
      dx, dy, len: Integer;
    begin
      if d = 0 then d := 1;
      //calc the angle of the line
      Theta:=ArcTan2( (y2-y1),(x2-x1));
      SinCos(Theta,sine, cosinus);
      dx  := Round(cosinus * (x - x1) + sine * (y - y1));
      dy  := Round(cosinus * (y - y1) - sine * (x - x1));
      len := Round(cosinus * (x2 - x1) + sine * (y2 - y1)); // length of line
      Result:= (dy > -d) and (dy < d) and (dx > -d) and (dx < len + d);
    end;
    

    enter image description here
    (source: swissdelphicenter.ch)