Контейнер Line Segment для быстрого пересечения Ray? (2D)

У меня есть луч, мне нужно найти ближайший отрезок, в который он попадает. Я думаю, что это можно сделать за время O (log n), если я сначала отсортирую сегменты линии, но я не могу вспомнить, как их сортировать... Я думаю, что лучше всего подойдет какое-то дерево, но как мне сортировать их как по начальной, так и по конечной точке? Я также хотел бы быстрых вставок в эту структуру данных, если это возможно.

Там много кода для одного луча и одного линейного сегмента, но мне нужно что-то для одного луча и многих линейных сегментов... Я не знаю, какие термины искать в Google.

Ссылка на соответствующую статью — это хорошо, код на C++ — еще лучше. Спасибо! :)

PS: сегменты линий на самом деле являются ребрами несамопересекающихся многоугольников, отсортированных по часовой стрелке... но я думаю, что может быть какое-то преимущество в сортировке их другим способом?

Это все 2D.


Если подумать, я не совсем уверен, что это возможно. Может помочь какое-то пространственное разбиение, но иначе я не могу придумать никакого способа отсортировать линии так, чтобы их можно было сравнить с произвольным лучом.


person mpen    schedule 09.04.2009    source источник


Ответы (5)


Вы можете взять ограничивающую рамку многоугольника (координаты min-max x, y) и построить сетку внутри рамки. Затем для каждой ячейки запомните все линии, пересекающие ячейку.

Найдите пересечение, подобное этому:

  • Узнать, в какую ячейку луч попадает первым (O(1))
  • Используйте алгоритм обхода сетки, чтобы "прорисовать" луч через сетку. Когда вы нажмете непустую ячейку, проверьте все ее линии, проверьте, находится ли пересечение внутри ячейки, и выберите ближайшее пересечение. Если все пересечения находятся за пределами ячейки, продолжайте (это O (длина сетки)).

Вы также можете сделать сетку иерархической (например, quadtree — дерево, которое спрашивая), и пройтись по тому же алгоритму. Это делается при трассировке лучей в 3D, а временная сложность составляет O(sqrt( Н)).


Или используйте подход, который я сделал в своем трассировщике лучей:

  • Создайте quadtree, содержащий строки (построение quadtree описано в статье) - вы разделяете узлы (= области), если они содержат слишком много объектов, на 4 подузла (подобласти)
  • Соберите все конечные узлы дерева квадрантов, на которые попал луч:

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

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

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

Это O (кварт (N)).

При обходе сетки, когда вы найдете пересечение, вы можете прекратить поиск. Чтобы добиться этого с помощью обхода дерева квадрантов, вам придется искать дочерние элементы в правильном порядке — либо сортировать 4 пересечения прямоугольников по расстоянию, либо ловко обходить сетку из 4 ячеек (мы возвращаемся к обходу).

Это просто другой подход, сравнительно такой же сложный в реализации, я думаю, и хорошо работает (я тестировал его на реальных данных - O(sqrt(N))). Опять же, вы выиграете от этого подхода только в том случае, если у вас есть хотя бы пара линий, когда многоугольник имеет 10 ребер, преимущество по сравнению с простым тестированием всех из них будет небольшим, я думаю.

person Martin Konicek    schedule 14.06.2009
comment
Нет, O (log n), о котором я думал, был только для горизонтальных или вертикальных сегментов, я полагаю. Алгоритм Брезенхэма выглядит так, будто он может пропускать пересечения, не так ли? Он не заполняет каждую ячейку, в которую входит. В противном случае это выглядит как хороший подход. - person mpen; 15.06.2009
comment
Вы правы, обход сетки должен осуществляться по алгоритму, указанному здесь devmaster.net/articles/raytracing_series /part4.php, посмотрите раздел Обход сетки. Обратите внимание, что вы, вероятно, выиграете, только если ваш многоугольник имеет много (~ сотни) ребер, как показано в статье. - person Martin Konicek; 15.06.2009
comment
Ну, наверное, сотни ребер не будет, но одни и те же ребра могут использоваться тысячи раз. Похоже, это лучший ответ на то, что я считал мертвым вопросом, поэтому я дам вам чек;) Спасибо. - person mpen; 15.06.2009
comment
Спасибо :) Я проголосовал +1 за ваш вопрос, потому что я нахожу его интересным. Если ребер будет всего несколько, то каждый ray-тест будет очень быстрым, но структура не поможет вам ускорить серию тестов в целом - каждый из них будет браться по отдельности, так что O(#rays *sqrt(#края)). Хм, интересно. Вы можете проводить лучевые тесты параллельно, чтобы извлечь выгоду из многоядерных процессоров, поскольку тесты независимы. - person Martin Konicek; 15.06.2009
comment
Если вам интересно, этот вопрос был о том, как сделать этот алгоритм mnbayazit.com/406/bayazit более эффективным . Относится к: stackoverflow.com/questions/694108/ - person mpen; 17.06.2009

Вы ищете методы, основанные на scanline/Active Edge Table? Вы можете взглянуть на статью в Википедии для Scanline Rendering или выполнить поиск по Graphics Gems каталог для алгоритмов (в основном C, но также и код C++) .

person dirkgently    schedule 09.04.2009
comment
На первый взгляд кажется, что это работает только потому, что полигоны/линейные сегменты отсортированы по оси Y, а строки сканирования строго горизонтальны. Мои лучи могут быть направлены в любом направлении... это может быть серьезной проблемой. - person mpen; 09.04.2009
comment
@Mark: Если бы вы могли дать нам общую картину того, что вы пытаетесь сделать (например, заполнение полигонов), мы могли бы указать на некоторые специализированные алгоритмы. - person dirkgently; 09.04.2009

Имейте в виду, что сортировка в лучшем случае выполняется за O(n log n). Может быть, вам лучше просто проверить каждый в отдельности.

person rlbond    schedule 09.04.2009
comment
Я не против. Весь мой алгоритм будет в лучшем случае O (n ^ 2 log n), и мне нужно отбросить много лучей... - person mpen; 09.04.2009
comment
Сортировка не является операцией O(N log N). Сортировка с использованием только двухэлементных транзитивных сравнений есть, но не сортировка вообще. - person SPWorley; 15.06.2009

Как вы уверены, что попадете в любой из них? Если это линии, то маловероятно.

Если вы действительно пытаетесь протестировать многоугольник (т. е. планарный), обычный способ сделать это — сначала пересечь плоскость, а затем проверить эту точку (в координатах 2d) для внутреннего/внешнего многоугольника.

Может быть, я неправильно понял, что вы на самом деле делаете.

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

[Обновление: я неправильно понял первоначальный замысел] Вы все еще можете использовать (2d) пространственное разбиение, но накладные расходы могут того не стоить. Индивидуальные тесты дешевы, если ваши полигоны не сложны, может быть дешевле просто пройтись по ним. Трудно сказать по описанию.

person simon    schedule 09.04.2009
comment
Я могу гарантировать, что луч попадет хотя бы в один отрезок прямой. Луч исходит из вершины внутрь многоугольника. (Это 2D) - person mpen; 09.04.2009
comment
Если бы вы знали, что луч всегда будет направлен в одну и ту же точку многоугольника, вы могли бы отсортировать ребра по углу. Ваш луч также имеет угол, поэтому должна быть возможность выполнить двоичный поиск по логарифму (N). - person Martin Konicek; 14.06.2009

Попросите разъяснений, это правильно?

  • У вас есть динамический набор сегментов линии L.
  • Запрос: учитывая любую точку (x,y) и любое направление луча из этой точки, вы хотите определить ближайшую линию в L (если есть)?

Значит, точка (x, y) не фиксирована? (Это может быть любая точка и любое направление?)

person jacob    schedule 15.06.2009
comment
Это верно, Джейкоб. Направление задается другой точкой, а не углом, если это уместно. Несколько лучей будут сравниваться с одним и тем же набором сегментов линий, поэтому я полагаю, что может быть целесообразно выполнить некоторую предварительную обработку линий, если это поможет. - person mpen; 15.06.2009
comment
Возможно, мне также следует указать, что мы, вероятно, рассматриваем только около 20 сегментов линий и в среднем 5 лучей, но жесткой верхней границы нет. Причина, по которой это должно быть эффективным, заключается в том, что это часть рекурсивного алгоритма, который может стать довольно дорогостоящим. (Одна из моих реализаций заняла около минуты, чтобы обработать ~ 13 строк, и дальше вы даже не хотите пытаться). - person mpen; 15.06.2009
comment
Даже реализация грубой силы с использованием 20 строк и 5 лучей должна занимать несколько миллисекунд? Почему у тебя так долго? - person jacob; 15.06.2009
comment
Под грубой силой я подразумеваю: по заданному лучу проверить все возможные отрезки прямой, пересекает ли он его, и определить отрезок с минимальным расстоянием. Это должно иметь время выполнения O (r * n) для r лучей и n сегментов линии. - person jacob; 15.06.2009
comment
не могли бы вы дать схему, как вы сделали проверку? - person jacob; 15.06.2009
comment
Вы правы, это занимает всего несколько миллисекунд, но, как я уже сказал, это часть более крупного алгоритма. Я отбрасываю несколько лучей, выбираю лучшее решение, а затем разбиваю полигон на две меньшие части и повторяю... снова и снова и снова. - person mpen; 17.06.2009
comment
Причина, по которой я был так обеспокоен этим, заключалась в том, что эта подзадача влияет на асимптотическое время выполнения всего моего алгоритма. - person mpen; 17.06.2009