FleetL
FleetL

Reputation: 41

Calculating distance of point to linear line

I'm working on a paint-like program in C#. I want to be able to erase a line when clicking close to it (distance < 10 pixels for example). I have tried different calculations and the problem I continuously end up having is that the line will only be erased when I click near either the starting point of the line or the end point. Anything in between definitely does not seem to work.

Let p be the point the user had clicked on in the form, startp and endp the end points of the line.

double a = (endp.Y - startp.Y) / (endp.X - startp.X); // gradient
double b = endp.Y - a * endp.X; // y intercept

// condition such that it only works when i click close to the line segment,
// not the entire infinite line for which the calculation y=ax+b works

double yvalue = p.X * a + b;    // value the line segment has at the x-value which the user clicks on
double alpha = Math.Atan((endp.X - startp.X) / (endp.Y - startp.Y));    
double distance = Math.Sin(alpha) * Math.Abs((yvalue - p.Y));
if (distance<10)
     // remove line

Why does this code only work for points close to the start or end points? I'm confident that it is not because of the conditions I use which I have left out of my example here

Upvotes: 2

Views: 5364

Answers (4)

MBo
MBo

Reputation: 80107

If you have line with base point startP and normalized direction vector d and point P, the simplest way to find distance from point to line is using cross product

Dist = Abs(Cross(P-startP, d)) = 
       Abs((P.x -startP.x) * d.y - (P.y -startP.y) * d.x)

But you want distance to line segment (not infinite line). In this case you also need checking - whether projection of point lies in the segment range. Calculate parameter with scalar product:

t = Dot(P-startP, d)

if t is in range 0..Len(D), then projection is on segment, otherwise the shortest distance is distance from point to one of segment ends (starting for negative t, ending for large t).

Note that vector approach is more universal than slope using, it does not fail on horizontal or vertical lines.

Upvotes: 0

T.Lucas
T.Lucas

Reputation: 848

The distance you want to calculate can be seen as the altitude of P in the triangle P-startP-endP. Thus, this give the following formula:

a = dist(startp, endp)
b = dist(startp, p)
c = dist(endp, p)
s = (a + b + c)/2
distance = 2 * sqrt(s(s-a)(s-b)(s-c)) / a

Cf. Altitude (triangle)

Upvotes: 5

Icemanind
Icemanind

Reputation: 48686

Try using this to see if this is what you need:

int variance = 10; // +/- distance
PointF lineStart = new PointF(80, 80); // Starting line point
PointF lineEnd = new PointF(200, 200); // Ending line point

double x1 = lineStart.X;
double x2 = lineEnd.X;
double y1 = lineStart.Y;
double y2 = lineEnd.Y;

double mouseX = e.X; // Mouse X position
double mouseY = e.Y; // Mouse Y position

double AB = Math.Sqrt((x2 - x1) * (x2 - x1) + (y2 - y1) * (y2 - y1));
double AP = Math.Sqrt((mouseX - x1) * (mouseX - x1) + (mouseY - y1) * (mouseY - y1));
double PB = Math.Sqrt((x2 - mouseX) * (x2 - mouseX) + (y2 - mouseY) * (y2 - mouseY));

if ((AP + PB) >= (AB - variance / 4) && (AP + PB) <= (AB + variance / 4))
{
    // It's within the line and variance
    // so erase Line
}

Upvotes: 1

Ricardo Alves
Ricardo Alves

Reputation: 1121

This is more a math problem. What I would do is the following:

1: Find the line vector v = (x2 - x1; y2 -y1)

2: Rotate the line vector 90 degrees: v1 = (-vy; vx)

3: Having the mouse position m = (mx; my) find the intersection of the the lines: (mx;my) + k1(-vy; vx) = (x1; y1) + k2(vx; vy)

4: Check if the distance between the result found in the step 4 to the point m is lesser than the result you want.

Upvotes: 0

Related Questions