如果您只想查看鼠标是否靠近线段,则无需知道像素的确切位置——您只需要知道它们在逻辑上是否在一定距离内。
这是我拼凑的一个小班。它只是使用直线的正常公式y = mx+c
来计算任何特定点是否在直线的一定距离(公差)内。
给定两点,p1
即p2
您要进行命中测试的线端点的坐标,您可以像这样初始化它:
var hitTest = new LineIntersectionChecker(p1, p2);
然后检查另一个点p
是否在这样的线上:
if (hitTest.IsOnLine(p))
...
类实现:
public sealed class LineIntersectionChecker
{
private readonly PointF _p1;
private readonly PointF _p2;
private readonly double _slope;
private readonly double _yIntersect;
private readonly double _tolerance;
private readonly double _x1;
private readonly double _x2;
private readonly double _y1;
private readonly double _y2;
private readonly bool _isHorizontal;
private readonly bool _isVertical;
public LineIntersectionChecker(PointF p1, PointF p2, double tolerance = 1.0)
{
_p1 = p1;
_p2 = p2;
_tolerance = tolerance;
_isVertical = (Math.Abs(p1.X - p2.X) < 0.01);
_isHorizontal = (Math.Abs(p1.Y - p2.Y) < 0.01);
if (_isVertical)
{
_slope = double.NaN;
_yIntersect = double.NaN;
}
else // Useable.
{
_slope = (p1.Y - p2.Y)/(double) (p1.X - p2.X);
_yIntersect = p1.Y - _slope * p1.X ;
}
if (_p1.X < _p2.X)
{
_x1 = _p1.X - _tolerance;
_x2 = _p2.X + _tolerance;
}
else
{
_x1 = _p2.X - _tolerance;
_x2 = _p1.X + _tolerance;
}
if (_p1.Y < _p2.Y)
{
_y1 = _p1.Y - _tolerance;
_y2 = _p2.Y + _tolerance;
}
else
{
_y1 = _p2.Y - _tolerance;
_y2 = _p1.Y + _tolerance;
}
}
public bool IsOnLine(PointF p)
{
if (!inRangeX(p.X) || !inRangeY(p.Y))
return false;
if (_isHorizontal)
return inRangeY(p.Y);
if (_isVertical)
return inRangeX(p.X);
double expectedY = p.X*_slope + _yIntersect;
return (Math.Abs(expectedY - p.Y) <= _tolerance);
}
private bool inRangeX(double x)
{
return (_x1 <= x) && (x <= _x2);
}
private bool inRangeY(double y)
{
return (_y1 <= y) && (y <= _y2);
}
}
您可以通过使用要进行命中测试的线两端的点来实例化它来使用它,然后调用IsOnLine(p)
要针对该线检查的每个点。
您将从 MouseMove 或 MouseDown 消息中获得要检查的点。
请注意,您可以在构造函数中设置不同的容差。我将其默认为 1,因为“在 1 个像素内”似乎是一个合理的默认值。
这是我测试它的代码:
double m = 0.5;
double c = 1.5;
Func<double, float> f = x => (float)(m*x + c);
Random rng = new Random();
PointF p1 = new PointF(-1000, f(-1000));
PointF p2 = new PointF(1000, f(1000));
var intersector = new LineIntersectionChecker(p1, p2, 0.1);
Debug.Assert(intersector.IsOnLine(new PointF(0f, 1.5f)));
for (int i = 0; i < 1000; ++i)
{
float x = rng.Next((int)p1.X+2, (int)p2.X-2);
PointF p = new PointF(x, f(x));
Debug.Assert(intersector.IsOnLine(p));
}