Схождение точек останова в алгоритме Fortune

Я реализую алгоритм развертки Fortune для вычисления диаграмм Вороного. Моя основная ссылка — «Вычислительная геометрия: алгоритмы и приложения» де Берга и др., и хотя их освещение темы очень ясно, они пропускают несколько небольших, но важных деталей, которые мне было трудно решить самостоятельно. Я искал помощь в Интернете, но другие веб-сайты либо дают более полный обзор, чем учебник, либо дают точно такой же псевдокод, как и в книге.

Мне нужен способ определить, сходится или расходится пара точек останова, определенных тройкой дуг на береговой линии, чтобы обнаруживать предстоящие события круга. Похоже, что для принятия решения мне потребуются знания о форме краев ячейки Вороного, которые прослеживаются контрольными точками по мере продвижения алгоритма Fortune. Например, если бы я мог найти наклон ребра, трассируемого точкой останова, я мог бы рассчитать, где пересекаются две линии, образованные точками останова, и их соответствующие наклоны, и решить, сходятся ли они на основе этого результата. Однако я понятия не имею, как получить информацию о склонах, только текущее положение точек останова.

Единственная информация, с которой мне приходится работать, — это координаты x,y трех участков и текущая координата y линии развертки (я использую горизонтальную линию развертки).

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

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


person Drake    schedule 08.03.2012    source источник
comment
Вы решили триангуляцию Делони?   -  person Gigamegs    schedule 08.03.2012
comment
Нет, это моя первая попытка заниматься вычислительной геометрией. Я знаю, что они двойственны друг другу, но я хотел бы сначала реализовать простой алгоритм Fortune.   -  person Drake    schedule 08.03.2012


Ответы (3)


Добро пожаловать, Дрейк. Я реализовал это, проверив, сходятся ли точки останова физически в центре круга в «фиктивном» приращении положения линии развертки. На самом деле это немного усложняет саму себя, потому что в некоторых случаях центр круга может быть почти или точно в положении линии развертки, поэтому приращение линии развертки должно быть пропорционально разнице между текущим положением линии развертки и центром круга, сгенерированным, как вы рекомендуете.

Сказать:

1. currentSweeplineY = 1.0f; circleCenterY = 0.5f (и мы движемся вниз, т.е. в сторону уменьшения y).

2. Set sweepYIncrement = (circleCenterY - currentSweepLineY) / 10.0f (делитель 10.0f выбирается произвольно).

3. Check new breakpoint positions at new sweepline position.

4. Check distance to circle center.

5. If both distances are less than current distances, the breakpoints converge.

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

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

person Kristian D'Amato    schedule 17.06.2013
comment
Очень интересно. Скорее логический, чем математический подход, но, если подумать, кажется, что это должно сработать. Я думаю, что эта часть алгоритма — главный кандидат на модульное тестирование. - person Drake; 30.06.2013

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

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

Пока три сайта не лежат на одной линии, то ребра, которые перпендикулярно делят пополам отрезки между узлами, также касаются окружности, ребро которой содержит все три узла. Следовательно, точки останова, определенные тройкой узлов Вороного, сходятся, если центр окружности, определяемой тремя узлами, находится перед средним узлом, где «спереди» и «сзади» зависят от координаты система и выравнивание линии развертки, которые вы выбрали.

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

Редактировать: Кристиан Д'Амато справедливо отмечает, что приведенный выше алгоритм пропускает некоторые случаи сходимости. Окончательный алгоритм, который я использовал, приведен ниже. Конечно, я не уверен, что это на 100% правильно, но, похоже, это работает во всех случаях, в которых я его пробовал.

Given left, middle, right sites
    if they are collinear, return false
    center = ComputeCircleCenterDefinedBy3Points(left, middle, right)
    return IsRightOfLine(left, middle, center) && IsRightOfLine(middle, right, center)

IsRightOfLine(start, end, point)
    ((end.X - start.X) * (point.Y - start.Y) - (end.Y - start.Y) * (point.X - start.X)) <= 0
person Drake    schedule 08.03.2012
comment
Меня тоже немного раздражало, что никто, казалось, не предлагал быстрого и простого способа проверки сходимости. Ваш вопрос (и ответ) был отличной находкой. Но я думаю, что есть определенные случаи, которые сходятся и которые описанный вами алгоритм не охватывает. Это случаи, когда точки останова фактически перемещаются назад (в том смысле, в каком вы его определили, противоположном движению по линии развертки). Это может произойти, когда три площадки расположены слева направо в противоположном направлении, в котором появляются их дуги параболы; точки останова все равно будут сходиться в будущем ниже береговой линии. - person Kristian D'Amato; 06.06.2013
comment
@KristianD'Amato Спасибо, что напомнили мне о моем ответе здесь, я обновил внизу сообщения улучшенный алгоритм, который я считаю правильным. - person Drake; 14.06.2013
comment
Основываясь на полном ответе Кристиана Д'Амато, я должен уточнить, что моя функция IsRightOfLine специфична для системы координат и ориентации линии развертки (в частности, линия развертки идет от большего y к меньшему y), которую я выбрал, и если вы адаптируете функцию для использования в своей собственной реализации вы должны проверить, чтобы убедиться, что это работает для вашей настройки координат. - person Drake; 30.06.2013

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

Вот фрагмент кода C# для вычисления центра описанной окружности d точек a, b, c:

        Vector2 ba = b - a;
        Vector2 ca = c - a;     
        float baLength = (ba.x * ba.x) + (ba.y * ba.y);
        float caLength = (ca.x * ca.x) + (ca.y * ca.y); 
        float denominator = 2f * (ba.x * ca.y - ba.y * ca.x);   
        if (denominator <= 0f ) { // Equals 0 for colinear points.  Less than zero if points are ccw and arc is diverging.
            return false;  // Don't use this circle event!
        };
        d.x = a.x + (ca.y * baLength - ba.y * caLength) / denominator ;
        d.y = a.y + (ba.x * caLength - ca.x * baLength) / denominator ;
person Brian Upton    schedule 10.01.2015
comment
Это единственный правильный ответ. Все остальные дают слишком окольные решения. Имея значение определителя, мы также можем обнаружить случай коллинеарных точек, что приводит к параллельным ребрам. - person Tomilov Anatoliy; 10.05.2016
comment
@Orient Меня смущает интуиция, стоящая за этим, возможно, потому, что я не полностью изучил интуицию, стоящую за детерминантным методом вычисления описанных окружностей. Есть ли шанс, что вы могли бы объяснить? В частности, я не совсем уверен в том, что означает «по часовой стрелке» и «против часовой стрелки», и у меня есть несколько примеров, которые я не уверен, как классифицировать. - person mnoronha; 14.07.2017
comment
@mnoronha Я? Это ответ Брайана Аптона. - person Tomilov Anatoliy; 14.07.2017
comment
@Ориент Хорошо. Причина, по которой я связался с вами, заключалась в том, что первоначальный автор неактивен, и вы, похоже, указали, что поняли ответ в своем комментарии. - person mnoronha; 15.07.2017
comment
@mnoronha Я не думаю, что смогу объяснить лучше, чем, скажем, вики. Вот моя реализация алгоритма Fortune. - person Tomilov Anatoliy; 15.07.2017
comment
@Ориент Не беспокойся. Спасибо за ссылку на вашу работу! - person mnoronha; 16.07.2017