Как узнать, принадлежит ли точка определенной линии?

Как узнать, принадлежит ли точка определенной линии?

Примеры приветствуются, если это возможно.


person Wahid Bitar    schedule 25.05.2009    source источник
comment
Пожалуйста, будьте более конкретными. Какая информация у вас есть для начала? У вас есть упорядоченная пара точки и уравнения?   -  person Bravery Onions    schedule 25.05.2009


Ответы (11)


В простейшей форме просто подставьте координаты в уравнение линии и проверьте на равенство.

Данный:

Point p (X=4, Y=5)
Line l (Slope=1, YIntersect=1)

Подставьте X и Y:

   Y = Slope * X + YIntersect
=> 5 = 1 * 4 + 1
=> 5 = 5

Так что да, дело в линии.

Если ваши линии представлены в форме (X1, Y1), (X2, Y2), вы можете рассчитать наклон с помощью:

 Slope = (y1 - y2) / (x1-x2)

А затем получите Y-Intersect следующим образом:

 YIntersect = - Slope * X1 + Y1;

Изменить: я исправил Y-пересечение (которое было X1/Y1...)

Вам нужно будет проверить, что x1 - x2 не является 0. Если это так, то проверка того, находится ли точка на линии, — это простая проверка того, равно ли значение Y в вашей точке x1 или x2. Кроме того, убедитесь, что X точки не равен «x1» или «x2».

person Eclipse    schedule 25.05.2009
comment
Что это за языковая библиотека? - person Autodidact; 25.05.2009
comment
Я бы подумал о перемещении EDIT: исправление формулы Y-Intersect поверх исходной неправильной версии. Потребовалось второе чтение, чтобы заметить это. - person jacob; 18.09.2012
comment
Самый простой способ — сравнить результаты Math.Atan2 как начальной, так и конечной точек сегмента с точкой объекта. Смотрите мой ответ ниже для примера. Не беспокойтесь о горизонтальных или вертикальных проблемах или насколько близко к нулю перед нулевым , который дает метод slope-intercept. - person IAbstract; 02.04.2016

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

  • Нечеткость. Должно быть место для ошибки, так как функция используется для выбора линий щелчком по ним.
  • У линии есть EndPoint и StartPoint, а не бесконечные линии.
  • Должен обрабатывать прямые вертикальные и горизонтальные линии, (x2 - x1) == 0 вызывает деление на ноль в других ответах.
private const double SELECTION_FUZZINESS = 3;

internal override bool ContainsPoint(Point point)
{
    LineGeometry lineGeo = geometry as LineGeometry;
    Point leftPoint;
    Point rightPoint;

    // Normalize start/end to left right to make the offset calc simpler.
    if (lineGeo.StartPoint.X <= lineGeo.EndPoint.X)
    {
        leftPoint   = lineGeo.StartPoint;
        rightPoint  = lineGeo.EndPoint;
    }
    else
    {
        leftPoint   = lineGeo.EndPoint;
        rightPoint  = lineGeo.StartPoint;
    }

    // If point is out of bounds, no need to do further checks.                  
    if (point.X + SELECTION_FUZZINESS < leftPoint.X || rightPoint.X < point.X - SELECTION_FUZZINESS)
        return false;
    else if (point.Y + SELECTION_FUZZINESS < Math.Min(leftPoint.Y, rightPoint.Y) || Math.Max(leftPoint.Y, rightPoint.Y) < point.Y - SELECTION_FUZZINESS)
        return false;

    double deltaX = rightPoint.X - leftPoint.X;
    double deltaY = rightPoint.Y - leftPoint.Y;

    // If the line is straight, the earlier boundary check is enough to determine that the point is on the line.
    // Also prevents division by zero exceptions.
    if (deltaX == 0 || deltaY == 0) 
        return true;

    double slope        = deltaY / deltaX;
    double offset       = leftPoint.Y - leftPoint.X * slope;
    double calculatedY  = point.X * slope + offset;

    // Check calculated Y matches the points Y coord with some easing.
    bool lineContains = point.Y - SELECTION_FUZZINESS <= calculatedY && calculatedY <= point.Y + SELECTION_FUZZINESS;

    return lineContains;            
}
person Robin Andersson    schedule 06.12.2012
comment
Почему это не принятый ответ? Все остальные просто математика и бла. Это реальный мир, закаленная в боях функция, и ее следует предпочесть. Я имею в виду, что это StackOverflow, а не MathOverflow. - person Robin Rodricks; 18.02.2014
comment
это лучший ответ, спасибо, это работает. но что было бы лучшим значением для SELECTION_FUZZINESS ?? - person shakil.k; 20.05.2016
comment
@shakil.k, SELECTION_FUZZINESS соответствует ширине вашей строки. Чем меньше значение, тем выше точность - person Pierre-Luc; 11.05.2019

Лучший способ определить, лежит ли точка R = (rx, ry) на прямой, соединяющей точки P = (px, py) и Q = (qx, qy), — это проверить, соответствует ли определитель матрицы

{{qx - px, qy - py}, {rx - px, ry - py}},

а именно (qx - px) * (ry - py) - (qy - py) * (rx - px) близко к 0. Это решение имеет несколько связанных преимуществ по сравнению с другими опубликованными: во-первых, оно не требует специального случая для вертикальных линий , во-вторых, он не делится (обычно медленная операция), в-третьих, он не вызывает плохого поведения с плавающей запятой, когда линия почти, но не совсем вертикальна.

person Dave    schedule 25.05.2009
comment
Для линии от 0,0 до 10,10 с точкой 5.1, 5.1 определитель равен нулю. Но дело не в линии. - person Andy; 14.04.2013
comment
что именно означает близко к 0? - person Leggy7; 02.01.2014
comment
Это гораздо лучший ответ, чем принятый. Единственное, чего не хватает, так это определения близкого к. Это следует понимать в контексте вычитания чисел: так как есть 5 вычитаний, есть 5 возможностей для значительной потери точности, а это означает, что на самом деле довольно сложно поставить хорошую характеристику рядом. - person Floris; 28.02.2014
comment
@Andy: Подумайте еще раз - линия от (0,0) до (10,10) может быть описана уравнением y = x, и все точки, которые решают это уравнение, лежат на линии. (5.1, 5.1) решает уравнение и, таким образом, лежит на прямой. - person Tomas Aschan; 13.05.2014

Даны две точки на линии L0 и L1 и точка для проверки P.

               (L1 - L0) * (P - L0)
n = (P - L0) - --------------------- (L1 - L0)
               (L1 - L0) * (L1 - L0)

Нормой вектора n является расстояние точки P от прямой, проходящей через L0 и L1. Если это расстояние равно нулю или достаточно мало (в случае ошибок округления), точка лежит на прямой.

Символ * представляет скалярное произведение.

Пример

P = (5, 5)

L0 = (0, 10)
L1 = (20, -10)

L1 - L0 = (20, -20)
P  - L0 = (5, -5)

              (20, -20) * (5, -5)
n = (5, -5) - --------------------- (20, -20)
              (20, -20) * (20, -20)

              200
  = (5, -5) - --- (20, -20)
              800

  = (5, -5) - (5, -5)

  = (0, 0)
person Daniel Brückner    schedule 25.05.2009
comment
+1 за упоминание ошибок округления. Использование точного равенства в арифметике с плавающей запятой во многих случаях приведет к сбою других предложенных решений. Я не уверен в численной надежности предложенного алгоритма, но численная робастность достаточно сложна, поэтому, если важна точность, то желательно посмотреть научную литературу по теме. Или, по крайней мере, используйте библиотеку, в которой есть вероятность, что автор провел исследование. - person Ants Aasma; 26.05.2009
comment
Я не думаю, что ваш пример правильный, потому что после некоторых преобразований n = (p - L0) - (p - L0) и в каждом случае вы всегда будете получать n = (0, 0). - person nenito; 07.01.2012

Я думаю, что г-н Патрик Макдональд дал почти правильный ответ, и это исправление его ответа:

public bool IsOnLine(Point endPoint1, Point endPoint2, Point checkPoint)
{
    return (((double)checkPoint.Y - endPoint1.Y)) / ((double)(checkPoint.X - endPoint1.X))
        == ((double)(endPoint2.Y - endPoint1.Y)) / ((double)(endPoint2.X - endPoint1.X));
}

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

Спасибо за всех.

person Wahid Bitar    schedule 25.05.2009
comment
Дает вам div на ноль, если checkPoint.x == endPoint.x или если конечные точки имеют одинаковое значение x - person ThiefMaster; 16.10.2010

y = m * x + c

Это уравнение прямой. x & y - координаты. Каждая линия характеризуется своим наклоном (m) и точкой пересечения с осью y (c).

Таким образом, учитывая m и c для линии, вы можете определить, находится ли точка (x1, y1) на линии, проверив, выполняется ли уравнение для x = x1 и y = y1

person Gishu    schedule 25.05.2009
comment
За исключением того, что это уравнение не может описать вертикальную линию, и за исключением того, что вы не упомянули возможность того, что линия имеет ненулевую толщину. - person ChrisW; 25.05.2009
comment
У линии нет толщины -- она ​​есть, когда она нарисована на экране (т.е. когда это вопрос программирования): ее толщина составляет не менее одного пикселя, а может быть и больше. - person ChrisW; 25.05.2009
comment
Я бы считал линию толщиной 1 пиксель (при рисовании на экране), что работает с этим уравнением. Если вы хотите найти, находится ли точка на линии с толщиной X, вы действительно спрашиваете, находится ли точка в прямоугольнике. - person ebrown; 26.05.2009

Двумерная линия обычно представляется с помощью уравнения с двумя переменными x и y, здесь хорошо известное уравнение

y-y1 = (y1-y2)/(x1-x2) (x-x1)

Теперь представьте, что ваша линия GDI+ нарисована от (0,0) до (100, 100), тогда значение m=(0-100)/(0-100) = 1, таким образом, уравнение для вашей линии y-0=1 *(х-0) => у=х

Теперь, когда у нас есть уравнение для рассматриваемой линии, легко проверить, принадлежит ли точка этой линии. Данная точка (x3, y3) принадлежит этой прямой, если она удовлетворяет уравнению прямой при подстановке x=x3 и y=y3. Например, точка (10, 10) принадлежит этой прямой, поскольку 10=10, но (10,12) не принадлежит этой прямой, поскольку 12 != 10.

ПРИМЕЧАНИЕ. Для вертикальной линии значение уклона (м) бесконечно, но в этом особом случае вы можете использовать уравнение для вертикальной линии непосредственно x=c, где c = x1 = x2.

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

Надеюсь это поможет.

person Autodidact    schedule 25.05.2009

Если у вас есть линия, определяемая ее конечными точками

PointF pt1, pt2;

и у вас есть пункт, который вы хотите проверить

PointF checkPoint;

то вы можете определить функцию следующим образом:

bool IsOnLine(PointF endPoint1, PointF endPoint2, PointF checkPoint) 
{
    return (checkPoint.Y - endPoint1.Y) / (endPoint2.Y - endPoint1.Y)
        == (checkPoint.X - endPoint1.X) / (endPoint2.X - endPoint1.X);
}

и назовите его следующим образом:

if (IsOnLine(pt1, pt2, checkPoint) {
    // Is on line
}

Однако вам нужно будет проверить деление на ноль.

person Patrick McDonald    schedule 25.05.2009
comment
Это не может быть правильным... Поскольку координаты точек являются целыми числами, у вас будет (критическая) потеря точности, когда контрольная точка близка к endPoint1 и далека от endPoint2. Возможно, если бы вы изменили его на десятичное или двойное, это сработало бы для обеих сторон, но я все равно не стал бы доверять точности этого уравнения. - person configurator; 25.05.2009
comment
Fair Point (каламбур), изменил их на PointF - person Patrick McDonald; 26.05.2009
comment
Осталось две проблемы: вы не проверили конец строки, поэтому (4,6) будет между (1,2) и (3,4) в соответствии с этой функцией, и есть проблема точности - предположим линия идет от (1100) до (2200). В середине нет ни одной точки с целочисленными координатами — проверка всегда будет ложной для целочисленных координат. - person configurator; 28.05.2009
comment
java.lang.ArithmeticException: делить на ноль - НЕ ОК - person MSA; 25.06.2013
comment
Поскольку алгоритм проверяет только коэффициент наклона, параллельные линии дадут ложноположительные результаты. - person diegoaguilar; 17.11.2013

Уравнение линии:

y = mx + c

Таким образом, точка (a, b) находится на этой линии, если она удовлетворяет этому уравнению, т.е. b = ma + c

person Naveen    schedule 25.05.2009

Не могли бы вы быть более конкретным?

О каком языке программирования вы говорите?

О каком окружении вы говорите?

О каких "линиях" вы говорите? Текст? Какая точка? XY на экране?

person joshcomley    schedule 25.05.2009
comment
Извините, я должен был прокомментировать исходный вопрос - person joshcomley; 25.05.2009
comment
Вы все еще можете удалить свой пост и оставить комментарий :) - person Boris Guéry; 25.05.2009

В качестве альтернативы методу slope/y-intercept я выбрал такой подход с использованием Math.Atan2:

// as an extension method
public static bool Intersects(this Vector2 v, LineSegment s) {
    //  check from line segment start perspective
    var reference = Math.Atan2(s.Start.Y - s.End.Y, s.Start.X - s.End.X);
    var aTanTest = Math.Atan2(s.Start.Y - v.Y, s.Start.X - v.X);

    //  check from line segment end perspective
    if (reference == aTanTest) {
        reference = Math.Atan2(s.End.Y - s.Start.Y, s.End.X - s.Start.X);
        aTanTest = Math.Atan2(s.End.Y - v.Y, s.End.X - v.X);
    }

    return reference == aTanTest;
}

Первая проверка reference определяет arcTan от начальной точки отрезка до его конечной точки. Затем с точки зрения начальной точки мы определяем arcTan для вектора v.

Если эти значения равны, мы проверяем с точки зрения конечной точки.

Простой и обрабатывает горизонтальные, вертикальные и все остальное между ними.

person IAbstract    schedule 01.04.2016