.NET Ascertaining mouse is on line drawn between two arbitrary points

If you want to easly make hit tests on arbitrary drawn shapes, you can create a path containing your drawing, then widden the path and make a visibility test using only framework functions.

For instance, here we create a path with a line:

GraphicsPath path = new GraphicsPath();

path.AddLine(x1, y1, x2, y2);
path.CloseFigure();

Then, widen the path and create a region for the hit test:

path.Widen(new Pen(Color.Black, 3));
region = new Region(path);

Finally, the hit test:

region.IsVisible(point);

The advantage of that method is it can easily extend to splines, arrows, arc, pies or pretty much anything drawable with GDI+. The same path can be used in both the HitTest and Draw logic by extracting it.

Here is the code combining it all:

public GraphicsPath Path
{
    get { 
        GraphicsPath path = new GraphicsPath();
        path.AddLine(x1, y1, x2, y2);
        path.CloseFigure();

        return path;
    }
}

bool HitTest(Point point)
{
    using(Pen new pen = Pen(Color.Black, 3))
    using(GraphicsPaht path = Path)
    {
        path.Widen(pen);

        using(Region region = new Region(path))
            return region.IsVisible(point);
    }
}


void Draw(Graphics graphics)
{
    using(Pen pen = new Pen(Color.Blue, 0))
    using(GraphicsPaht path = Path)
        graphics.DrawPath(pen, path);
}

To answer "Is the mouse hovering over this line?", you need to check for point-line intersection. However, since you're asking "is the mouse near the line?", it sounds like you want to calculate the distance between the mouse point and the line.

Here's a reasonably thorough explanation of point-line distance: http://mathworld.wolfram.com/Point-LineDistance2-Dimensional.html

I'd say you need to implement this formula in your code: (stolen from wolfram.com)

Where:

  • (x0, x0) is the location of the mouse pointer
  • (x1, y1) is one end of the line
  • (x2, y2) is the other end of the line
  • |n| is Math.Abs(n)
  • The bottom half is Math.Sqrt
  • You can ignore the |v.r| if you want

I would calculate the Slope-Intercept equation (y = mx + b) for my line and then use that to test the mouse coordinates. You could easily put a range around y to see if you're "close."

Edit for sample.

I think something like this works:

PointF currentPoint;
PointF p1, p2;
float threshold = 2.0f;
float m = (p1.Y - p2.Y) / (p1.X - p2.X);
float b = p1.Y - (m * p1.X);

if (Math.Abs(((m * currentPoint.X) + b) - currentPoint.Y) <= threshold)
{
    //On it.
}