2

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

FleetL
  • 41
  • 1
  • 4
  • 2
    Remember when using division to *always* check for dividing by zero. – JosephDoggie Nov 06 '18 at 14:18
  • 2
    I'd suggest to work on your definition of "close to it" first, before you draft the maths, then implement. what is the trigonometric principle of your distance computation? do you want to test a bounding rectangle, have two bounding parallel lines, with or without semicircular "hotspots" at the ends? – Cee McSharpface Nov 06 '18 at 14:21
  • 1
    @JosephDoggie thanks! I'm aware and have done so in my code. I'm really just wondering why it's not working in general cases – FleetL Nov 06 '18 at 14:27
  • 1
    @dlatikay yeah I should have mentioned but I'm using trigonometry to check the shortest distance from my point to the line. – FleetL Nov 06 '18 at 14:31
  • you want the perpendicular distance; what your code seems to do if I'm not mistaken is something quite different. the `y = kx + d` formula can't deal with vertical lines (when k would be infinite - dividing by zero) so the idea to use trigonometry is ok but which approach are you actually using and what is `yvalue`? please provide a minimal complete example. – Cee McSharpface Nov 06 '18 at 14:40
  • related: https://stackoverflow.com/a/20059470/1132334 – Cee McSharpface Nov 06 '18 at 14:43

4 Answers4

5

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)

T.Lucas
  • 848
  • 4
  • 8
1

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
}
Icemanind
  • 47,519
  • 50
  • 171
  • 296
0

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.

Ricardo Alves
  • 1,071
  • 18
  • 36
0

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.

MBo
  • 77,366
  • 5
  • 53
  • 86