Move Point Between Two Other Points Using Mouse with any Distance and Direction

Quokka

Member
Greetings,

I have 3 points: p1, p2, and p3. I am trying to create a slider effect using p2 and the mouse along a line of any distance and angle as defined by positions of p1 and p3:

* p1 and p3 define the points of a line, and
* p2 should be able to move freely, following the mouse, between p1 and p3,
* p2's movement should be restricted by the length of p1 to p3 and the direction from p1 to p3
(so p2 cannot move beyond the length of the line and must only be moving in the direction of the line).

It is almost working, but it seems my use of point_distance with p1 is incorrect. The distance from p1 to the mouse does not easily reach '0' so p2 does not get to exactly the same position as p1, and distance starts increasing again when the mouse moves away from p1 in a different direction. My goal is to select p2 and, if the mouse moves to p1 or p3 (or beyond either point) it slides and stops at those points.

Here's my code - any ideas?

GML:
my_dist = point_distance(p1.x, p1.y, mouse_x, mouse_y);
my_dir = point_direction(p1.x, p1.y, p3.x, p3.y);

ldx = lengthdir_x(my_dist, my_dir);
ldy = lengthdir_y(my_dist, my_dir);

x = p1.x + ldx;
y = p1.y + ldy;

Quokka
 

Roldy

Member
If you have a line then the closest point on that line to some arbitrary point is the intersection of the perpendicular line that contains the arbitrary point.

I like parameterized lines, so if we use a parameterized line (n, d) then the closest parameter on that line to a point p is found by the perpendicular dot product of n and p:


GML:
// Find the perpindicular normal (clockwise) i.e. the direction of the line

var perpNX = n.y;
var perpNY = -n.x;

// Get the dot product of the point and perpN, this is the perpDot of n and p
// The perpDot is parameter t on the line (n,d) closest to the point p

var perpDot = perpNX * p.x + perpNY * p.y;

// as said, the perpDot is the parameter of intersection so lets just rename it real quick

var intersectionT = perpDot ;

// Now that we have intersectionT we can calculate the point for it
// Get the point t = 0 on the line

var tZeroX = n.x * d;
var tZeroY = n.y * d;

// Get the intersection point on the line

intersectionPoint.x = tZeroX + (perpNX * intersectionT);
intersectionPoint.y  = tZeroY + (perpNY * intersectionT);
The above is for a line, not a line segment. You can easily define a line segment by supplying t0, t1 parameters for the end points. Then if intersectionT is < t0 then just clamp to t0. If intersectionT is > t1 then clamp to t1.

Here is some rough code to make a parameterized line from a point and direction:

GML:
// A parameterized line is composed of the normal for the line and distance from origin
// (n, d)

// Given a point p on the line and the direction the line 'travels' we can build (n,d)
// assuming clockwise perpDot and crossproducts we need to rotate counter clockwise to find normal

n.x = -lineDirection.y;
n.y = lineDirection.x;

// insure n is normalized

var length = n.x * n.x + n.y * n.y;
n.x /= length;
n.y /= length;

// Find the distance from origin by the dot product of p and n

d = p.x * n.x + p.y * n.y;

// n and d are our parameterized line
To calculate the point for any parameter t:

GML:
// Find the direction of the line
// The direction is simply the perpendicular of n (clockwise)
var dirX = n.y;
var dirY = -n.x;

// Find the point t = 0

var tZeroX = n.x * d;
var tZeroY = n.y * d;

// Find the point for parameter t

p.x = tZeroX + dirX * t;
p.y = tZeroY + dirY * t;
I'll leave it to you how to make a parameterized line from two points (its basically the same as the point/direction method above).

Hope that helps.

* crosses fingers there is not too many typos in the above code *

EDIT: DOH! found an error. We need to rotate the direction counter-clockwise when constructing the line from point and direction
 
Last edited:
Top