Reputation: 41
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
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
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
Upvotes: 5
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
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