performance - является - Как определить, находится ли 2D-точка внутри многоугольника?
принадлежность точки многоугольнику с++ (20)
Obj-C версия ответа nirg с образцом метода для тестирования точек. Ответ Нирга сработал хорошо для меня.
Я пытаюсь создать быструю двумерную точку внутри алгоритма многоугольника, для использования при хит-тестировании (например, Polygon.contains(p:Point) ). Предложения по эффективным методам будут оценены.
Быстрая версия ответа по nirg :
Вот версия ответа C #, предоставленная nirg , которая исходит от here . Обратите внимание, что использование кода из этого источника RPI требует атрибуции.
В верхней части была добавлена проверка рамки. Тем не менее, как отмечает Джеймс Браун, основной код почти так же быстро, как и ограничивающий блок, сам проверяет, поэтому проверка ограничивающего блока может фактически замедлить общую операцию в том случае, если большинство точек, которые вы проверяете, находятся в ограничивающей рамке , Таким образом, вы можете оставить контрольную коробку, или альтернативой было бы прекомпотировать ограничивающие прямоугольники ваших полигонов, если они не меняют форму слишком часто.
Вычислить ориентированную сумму углов между точкой p и каждой из вершин многоугольника. Если общий ориентированный угол равен 360 градусам, точка внутри. Если сумма равна 0, точка находится вне.
Мне нравится этот метод лучше, потому что он более надежный и менее зависимый от числовой точности.
Методы, которые вычисляют четность числа пересечений, ограничены, потому что вы можете «ударить» вершину при вычислении числа пересечений.
EDIT: By The Way, этот метод работает с вогнутыми и выпуклыми многоугольниками.
EDIT: Недавно я нашел целую статью в Википедии по этой теме.
На самом деле нравится решение, отправленное Nirg и отредактированное bobobobo. Я просто сделал его javascript дружественным и немного более разборчивым для моего использования:
Ответ Дэвида Сегонда в значительной степени является стандартным общим ответом, а Ричард Т - наиболее распространенная оптимизация, хотя некоторые другие. Другие сильные оптимизации основаны на менее общих решениях. Например, если вы собираетесь проверять один и тот же полигон с большим количеством точек, триангуляция полигона может ускорить работу, так как существует множество очень быстрых алгоритмов поиска TIN. Другим является то, что многоугольник и точки находятся на ограниченной плоскости при низком разрешении, например, на экране, вы можете рисовать многоугольник в буфер отображения отображаемой памяти в заданном цвете и проверять цвет данного пикселя, чтобы увидеть, в многоугольниках.
Как и многие оптимизации, они основаны на конкретных, а не общих случаях, и дают преимущества, основанные на амортизированном времени, а не на одном использовании.
Работая в этом поле, я нашел полезную геометрию вычислений Джозефа О'Руркса в C 'ISBN 0-521-44034-3.
Этот вопрос так интересен. У меня есть другая работающая идея, отличная от других ответов этого сообщения.
Идея состоит в том, чтобы использовать сумму углов, чтобы решить, находится ли цель внутри или снаружи. Если цель находится внутри области, сумма формы угла по цели и каждые две граничные точки будет равна 360. Если цель находится снаружи, сумма не будет равна 360. Угол имеет направление. Если угол идет назад, угол является отрицательным. Это точно так же, как вычисление числа обмотки .
Обратитесь к этому изображению, чтобы получить базовое представление об этой идее:
Мой алгоритм предполагает, что по часовой стрелке есть положительное направление. Вот потенциальный ввод:
Ниже приведен код python, который реализует идею:
Я думаю, что следующий фрагмент кода - лучшее решение (взято here ):
аргументы- nvert : Число вершин в многоугольнике. Повторение первой вершины в конце было обсуждено в статье, упомянутой выше.
- vertx, verty : Массивы, содержащие x- и y-координаты вершин многоугольника.
- testx, testy : X- и y-координаты контрольной точки.
Он как короткий, так и эффективный и работает как для выпуклых, так и вогнутых полигонов. Как было предложено ранее, вы должны сначала проверить ограничивающий прямоугольник и обрабатывать многоугольные отверстия отдельно.
Идея этого довольно проста. Автор описывает это следующим образом:
Я запускаю полубесконечный луч горизонтально (увеличивая x, фиксированный y) из тестовой точки и подсчитывая, сколько ребер пересекает. На каждом пересечении луч переключается между внутренней и внешней. Это называется теоремой о кривых Жордана.
Переменная c переключается с 0 на 1 и 1 на 0 каждый раз, когда горизонтальный луч пересекает любое ребро. Поэтому в основном он отслеживает, четное или нечетное число пересекаемых ребер. 0 означает, что четный и 1 означает нечетный.
Я понимаю, что это старо, но вот алгоритм кастинга лучей, реализованный в Cocoa, в случае, если кто-то заинтересован. Не уверен, что это самый эффективный способ сделать что-то, но это может помочь кому-то.
VBA VERSION:
Примечание. Помните, что если ваш многоугольник является областью внутри карты, то значение Широта / Долгота - это значения Y / X, а не X / Y (Latitude = Y, Longitude = X) из-за того, что я понимаю, являются историческими последствиями из прошлого, когда Долгота не была измерением.
МОДУЛЬ КЛАССА: CPoint
Вот голанская версия ответа @nirg (вдохновленная кодом C # на @@ m-katz)
Вы можете сделать это, проверив, соответствует ли область, образованная путем подключения нужной точки к вершинам вашего многоугольника, к области самого полигона.
Или вы могли бы проверить, составляет ли сумма внутренних углов от вашей точки до каждой пары двух последовательных вершин многоугольника к вашей контрольной точке сумма до 360, но у меня есть ощущение, что первый вариант быстрее, потому что он не включает в себя дивизии и вычисления обратного к тригонометрическим функциям.
Я не знаю, что произойдет, если ваш полигон имеет отверстие внутри него, но мне кажется, что основную идею можно адаптировать к этой ситуации
Вы можете также задать вопрос в математическом сообществе. Уверен, у них есть один миллион способов сделать это
Если вы ищете библиотеку Java-скриптов, для класса Polygon существует расширение javascript google maps v3 для определения того, находится ли в нем точка.
Я тоже думал, что суммирование сумм только для выпуклых полигонов, но это не так.
На этом сайте есть хорошая диаграмма, показывающая именно это, и хорошее объяснение при тестировании ударов: Gamasutra - Crashing into the New Year: Collision Detection
Для решения следующих особых случаев в алгоритме литья лучей :
- Луч перекрывает одну из сторон многоугольника.
- Точка находится внутри многоугольника, и луч проходит через вершину многоугольника.
- Точка находится за пределами многоугольника, и луч просто касается одного из углов многоугольника.
Проверьте, находится ли точка внутри сложного многоугольника . В статье представлен простой способ их устранения, поэтому для вышеуказанных случаев не требуется специального лечения.
Нет ничего более прекрасного, чем индуктивное определение проблемы. Для полноты здесь у вас есть версия в прологе, которая также может прояснить, что происходит за лучей :
На основе моделирования алгоритма простоты в here
Некоторые вспомогательные предикаты:
Уравнение прямой, заданной 2 точками A и B (Линия (A, B)):
Важно, чтобы направление вращения линии было настроено по часовой стрелке для границ и против часовой стрелки для отверстий. Мы будем проверять, находится ли точка (X, Y), т.е. тестируемая точка в левой полуплоскости нашей линии (это вопрос вкуса, она также может быть правой, а также направление границ строки должны быть изменены в этом случае), это проецирование луча из точки вправо (или влево) и подтверждение пересечения с линией. Мы решили проецировать луч в горизонтальном направлении (опять же это вопрос вкуса, его также можно было бы сделать вертикально с аналогичными ограничениями), поэтому мы имеем:
Теперь нам нужно знать, находится ли точка на левой (или правой) стороне отрезка линии, а не на всей плоскости, поэтому нам нужно ограничивать поиск только этим сегментом, но это легко, так как находиться внутри сегмента только одна точка в линии может быть выше Y по вертикальной оси. Поскольку это более сильное ограничение, оно должно быть первым, чтобы проверить, поэтому сначала мы берем только те линии, которые отвечают этому требованию, а затем проверяют его наличие. По теореме Иордановой кривой любой луч, проецируемый на многоугольник, должен пересекаться с четным числом прямых. Итак, мы закончили, мы будем бросать луч вправо, а затем каждый раз, когда он пересекает линию, переключает ее состояние. Однако в нашей реализации мы собираемся проверить длину пакета решений, отвечающих заданным ограничениям, и решить, в чем он заключается. для каждой линии в многоугольнике это нужно сделать.
При использовании qt (Qt 4.3+) можно использовать функцию QPolygon containsPoint
Это точка в полигоне в C, которая не использует лучевое литье. И он может работать для перекрывающихся областей (самопересечений), см. use_holes Аргумент.
Примечание. Это один из менее оптимальных методов, поскольку он включает в себя множество вызовов atan2f , но может быть интересен разработчикам, читающим этот поток (в моих тестах его