2

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?

XBasic3000
  • 3,418
  • 5
  • 46
  • 91
  • 1
    Do you really want the *line*? I think you mean the line *segment*. (Lines have infinite length.) – Rob Kennedy Jun 14 '11 at 04:19
  • 1
    Possible duplicate of [Shortest distance between a point and a line segment](http://stackoverflow.com/questions/849211/shortest-distance-between-a-point-and-a-line-segment). When the distance is zero, you have a hit. – Rob Kennedy Jun 14 '11 at 04:21

1 Answers1

4

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)

Glorfindel
  • 21,988
  • 13
  • 81
  • 109
RRUZ
  • 134,889
  • 20
  • 356
  • 483
  • -1 Inline assembly? No need for that. Also no real need for trig. – David Heffernan Jun 14 '11 at 07:06
  • @David I just added a equivalent function using the `ArcTan2` and `SinCos` functions from the Math unit. – RRUZ Jun 14 '11 at 08:06
  • That's better. I'd still personally prefer a solution that didn't require trig but this is fine for the purpose. I think you should delete the inline assembly version altogether because it just misleads people into thinking it might be better. – David Heffernan Jun 14 '11 at 08:11
  • A straight arithmetic solution: `function OnSameLine( const A,B,C : TPoint): Boolean; begin // Result := dxAB*dyAC - dxAC*dyAB < epsilon; Result := Math.SameValue((A.x-B.x)*(A.y-C.y),(A.x-C.x)*(A.y-B.y)); end;` Basically this formula compares the slopes of a line between point A and B with the slope of a line between A and C. – LU RD Oct 12 '17 at 19:46