.NET Установление мыши на линии, проведенной между двумя произвольными точками - PullRequest
7 голосов
/ 19 марта 2010

У меня есть стрелка, нарисованная между двумя объектами в Winform.

Какой самый простой способ определить, что моя мышь в данный момент находится над или около этой линии?

Я рассмотрел вопрос о том, пересекает ли точка мыши квадрат, определенный и экстраполированный двумя точками, однако это было бы возможно только в том случае, если две точки имели очень похожие значения x или y.

Я также думаю, что эта проблема, скорее всего, больше связана с линейной алгеброй, чем с простой тригонометрией, и хотя я помню более простые аспекты матриц, эта проблема выходит за рамки моих знаний о линейной алгебре.

С другой стороны, если библиотека .NET справится с этой функцией, даже лучше.

EDIT Спасибо за ответы, есть несколько очень хороших, которые заслуживают того, чтобы их отметили как отвеченные.

Я выбрал ответ Coincoin в качестве принятого, так как мне нравится, что он может быть применен к любой нарисованной фигуре, однако в итоге реализовал уравнение Тима Робинсона, так как он оказался намного эффективнее с простым уравнением, а не обновлял графические пути и ручки , как и в моем случае, мне нужно сделать это на MouseMove для 1-n различных отношений (очевидно, было бы некоторое кэширование и оптимизация, но суть все еще остается)

Основная проблема с уравнением заключалась в том, что оно, по-видимому, рассматривало линию как бесконечную, поэтому я также добавил тест границ.

Код (начальный вырез, я, вероятно, немного опрятный), для тех, кто заинтересован, ниже

    if (Math.Sqrt( Math.Pow(_end.X - _start.X, 2) + 
           Math.Pow(_end.Y - _start.Y, 2) ) == 0)
    {
        _isHovering =
            new RectangleF(e.X, e.Y, 1, 1).IntersectsWith(_bounds);
    }
    else
    {
        float threshold = 10.0f;

        float distance = (float)Math.Abs( 
            ( ( (_end.X - _start.X) * (_start.Y - e.Y) ) -
            ( (_start.X - e.X) * (_end.Y - _start.Y) ) ) /
            Math.Sqrt( Math.Pow(_end.X - _start.X, 2) + 
            Math.Pow(_end.Y - _start.Y, 2) ));

        _isHovering = (
            distance <= threshold &&
                new RectangleF(e.X, e.Y, 1, 1).IntersectsWith(_bounds)
            );
    }

и _bounds определяется как:

    _bounds = new Rectangle(
    Math.Min(_start.X, _end.X),
    Math.Min(_start.Y, _end.Y),
    Math.Abs(_start.X - _end.X), Math.Abs(_start.Y - _end.Y));

Ответы [ 5 ]

7 голосов
/ 19 марта 2010

Если вы хотите легко создавать тесты попадания для произвольных нарисованных фигур, вы можете создать путь, содержащий ваш чертеж, затем удалить его и выполнить тест видимости, используя только функции каркаса.

Например, здесь мы создаем путь со строкой:

GraphicsPath path = new GraphicsPath();

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

Затем расширите путь и создайте область для теста попадания:

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

Наконец, тест на попадание:

region.IsVisible(point);

Преимущество этого метода в том, что он может легко распространяться на сплайны, стрелки, дуги, пироги или почти все, что можно нарисовать с помощью GDI +. Извлекая его, можно использовать один и тот же путь в логике HitTest и Draw.

Вот код, объединяющий все это:

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);
}
4 голосов
/ 19 марта 2010

Чтобы ответить «Мышь находится над этой линией?», Вам нужно проверить пересечение точки с линией. Однако, поскольку вы спрашиваете «находится ли мышь рядом с линией?», Звучит так, будто вы хотите вычислить расстояние между точкой мыши и линией.

Вот довольно подробное объяснение расстояния между точками: http://mathworld.wolfram.com/Point-LineDistance2-Dimensional.html

Я бы сказал, что вам нужно реализовать эту формулу в вашем коде: (украдено с wolfram.com)

Где:

  • (x0, x0) - местоположение указателя мыши
  • (x1, y1) - один конец строки
  • (x2, y2) - другой конец линии
  • |n| is Math.Abs(n)
  • Нижняя половина Math.Sqrt
  • Вы можете игнорировать |v.r|, если хотите
2 голосов
/ 19 марта 2010

Я бы рассчитал уравнение наклона-пересечения (y = mx + b) для моей линии, а затем использовал его для проверки координат мыши. Вы можете легко установить диапазон около y, чтобы убедиться, что вы «близки».

Редактировать для образца.

Я думаю, что-то вроде этого работает:

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.
}
1 голос
/ 19 марта 2010

Вам необходимо построить две (условные) граничные линии, параллельные идеальному пути. Тогда вам нужно только вычислить для каждой позиции мыши, находится ли мышь снаружи или внутри канала, образованного этими линиями.

Вам не нужно вычислять расстояние от мыши до основной линии.

0 голосов
/ 19 марта 2010

Проверьте MouseEnter (отправитель объекта, EventArgs e). Ловушка, когда она «входит» в зону управления.

...