Теперь, когда мы знаем немного больше о том, что такое диаграммы Вороного, и об их особенностях, давайте углубимся в лучший способ их вычисления, который представляет собой алгоритм развертки плоскости, вдохновленный работой Стивена Форчуна.
Алгоритм развертки плоскости
Развертка плоской линии — тип алгоритма, широко используемый в вычислительной геометрии. Идея заключается в том, чтобы иметь линию, которая перемещает план в одном направлении — вертикально или горизонтально — и выполняет операцию всякий раз, когда линия сталкивается с точкой.
Применительно к нашей проблеме Вороного у нас возникло бы искушение сказать что-то вроде «когда линия встречает семя, мы создаем его область Вороного». Проблема в том, что когда мы находимся на определенном семени, у нас все равно нет возможности узнать, где начинается и заканчивается его регион. Нам нужно немного пошалить.
Параболы
Одним из решений этой проблемы является использование парабол. Почему параболы?
Что ж, параболы часто известны по их уравнениям в форме y = ax² + bx + c, где a является постоянной и отличной от нуля.
Но есть и другой способ их определения. Парабола может быть построена с использованием одной точки, называемой фокусом, и линии, называемой директрисой. При использовании этих двух элементов парабола состоит из всех точек, равноудаленных от фокуса и от ближайшей точки на директрисе.
Важной частью здесь является слово «эквидистантный», оно будет очень полезно для создания ребер Вороного.
Пляжная линия
Зная, что теперь мы можем изменить наш подход к алгоритму развертки. Всякий раз, когда мы сталкиваемся с семенем, мы создаем новую параболу. Его фокус будет семенем, а его направляющей будет сама линия развертки. Мы сохраним в памяти список всех существующих дуг, которые назовем береговой линией.
Визуально береговая линия состоит из ряда смежных парабол разного размера, что напоминает линии, проведенные водой на пляже, отсюда и название.
Строительство кромок
Когда вы добавляете новую параболу к линии пляжа, сначала это будет вертикальная линия, которая будет пересекаться с другой параболой в одной точке, учитывая, что линия пляжа не пуста. Эта точка является началом ребра Вороного. Когда линия развертки продолжится, будет создана парабола, которая будет расти. Эта парабола пересекается с другой параболой. По определению точка пересечения теперь равноудалена от фокусных точек обеих парабол. Точка является частью ребра Вороного между двумя точками фокусировки. По мере того, как линия развертки продолжается, а параболы растут, точки пересечения будут перемещаться, и по этим точкам строятся края.
Обработка края / Закрытие области
Визуально, когда линия перемещается, мы можем видеть, что в некоторых точках несколько ребер сойдутся в одну точку, а одна или несколько дуг параболы полностью исчезнут. От этой точки схождения оставшиеся параболические дуги встретятся и начнут новое ребро.
Потенциальные кандидаты на вершину
Всякий раз, когда мы добавляем новую дугу, мы можем искать возможные вершины-кандидаты. Нам просто нужно найти центр описанной окружности только что добавленного участка и две его предыдущие дуги, а затем центр описанной окружности вновь добавленной и двух следующих дуг. Если они есть конечно. Нам не нужно проверять случай, когда вновь добавленная точка находится посередине, потому что созданные ребра будут расходиться.
И в этом вся хитрость. Для каждой потенциальной вершины мы проверяем, сходятся ли созданные дуги. Если это не так, значит, вершина находится выше береговой линии и уже пройдена, учитывая, что плоскость заметается сверху вниз.
Описание алгоритма
Теперь у нас есть все необходимое для простого описания алгоритма:
- Создайте приоритетную очередь, содержащую все сайты. Плоскость заметается сверху вниз, поэтому мы хотим, чтобы очередь сортировалась по убыванию координат y.
- Пока очередь не пуста
- Возьмите первый элемент
- Если элемент представляет собой участок, добавьте новую параболу с фокусом на эту точку к линии пляжа. Затем проверьте возможные вершины и добавьте их в очередь приоритетов. Создайте полуребра пересечений.
- Если элемент является вершиной, это означает, что дуга исчезает. Удалите его с береговой линии, закончите края относительно него и создайте новые, начиная с него. Затем проверьте возможные вершины между новыми соседними дугами и добавьте их в очередь событий.
- Когда очередь событий пуста, дело сделано. Если вам нужно отобразить график, вам придется связать диаграмму, чтобы иметь возможность отображать «бесконечные» ребра — те, что из неограниченных регионов.
Вот и все, что касается глобальной идеи. В следующей статье мы обсудим, как это реализовать. Это не так просто, как кажется, и я все еще в процессе завершения, так что статья может занять некоторое время, прежде чем она появится, но она обязательно появится.
Предыдущая статья из цикла:
Ссылки, использованные при написании этой статьи:
Вычислительная геометрия: алгоритмы и приложения, Марк ван Кревельд, Марк де Берг, Марк Овермарс.
https://www.desmos.com/calculator Для создания изображений