Алгоритм раздувания/дефляции (смещения, буферизации) полигонов

Как бы я "раздул" многоугольник? То есть, я хочу сделать что-то похожее на это:

alt text

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

Математический термин для того, что я ищу, - это фактически смещение влево/вправо по отношению к полигону. +1 к balint для указания этого. Альтернативное именование буферизация полигонов.

Результаты моего поиска:

Вот несколько ссылок:

Ответ 1

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

В то время как Clipper в первую очередь предназначен для операций отсечения полигонов, он также выполняет многоугольную коррекцию. Библиотека бесплатное ПО с открытым исходным кодом написано в Delphi, С++ и С#. Он имеет очень свободную лицензию Boost, позволяющую бесплатно использовать ее в бесплатных и коммерческих приложениях.

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

Polygon offsetting styles

Ответ 2

Нужный многоугольник в вычислительной геометрии называется смещенным внутрь/наружу многоугольником, и он тесно связан с прямым скелетом.

Это несколько смещенных многоугольников для сложного многоугольника:

И это прямой скелет для другого многоугольника:

Как указывалось и в других комментариях, в зависимости от того, насколько далеко вы планируете "надувать/выкачивать" ваш полигон, вы можете получить разные возможности подключения для вывода.

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

Руководство по пакету должно дать вам хорошее начальное представление о том, как построить эти структуры, даже если вы не собираетесь использовать CGAL, и содержит ссылки на статьи с математическими определениями и свойствами:

Руководство CGAL: 2D прямолинейное смещение скелета и многоугольника

Ответ 3

Похоже, что вы хотите, это:

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

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

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

Ответ 4

Для таких вещей я обычно использую JTS. В демонстрационных целях я создал этот jsFiddle, который использует JSTS (порт JavaScript JTS). Вам просто нужно преобразовать нужные вам координаты в координаты JSTS:

function vectorCoordinates2JTS (polygon) {
  var coordinates = [];
  for (var i = 0; i < polygon.length; i++) {
    coordinates.push(new jsts.geom.Coordinate(polygon[i].x, polygon[i].y));
  }
  return coordinates;
}

Результат примерно такой:

enter image description here

Дополнительная информация: Я обычно использую этот тип надувания/выкачивания (немного модифицированный для моих целей) для установки границ с радиусом на многоугольниках, которые нарисованы на карте (с помощью листовок или карт Google). Вы просто конвертируете (lat, lng) пары в координаты JSTS, а все остальное тоже самое. Пример:

enter image description here

Ответ 5

Каждая строка должна разделить плоскость на "внутри" и "контур"; вы можете найти это, используя обычный метод внутреннего продукта.

Переместите все линии на некоторое расстояние.

Рассмотрим все пары соседних строк (строки, а не отрезок линии), найдите пересечение. Это новая вершина.

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

(a) Случай 1:

 0--7  4--3
 |  |  |  |
 |  6--5  |
 |        |
 1--------2

если вы потратите его на один, вы получили следующее:

0----a----3
|    |    |
|    |    |
|    b    |
|         |
|         |
1---------2

7 и 4 перекрываются. Если вы видите это, вы удаляете эту точку и все точки между ними.

(b) случай 2

 0--7  4--3
 |  |  |  |
 |  6--5  |
 |        |
 1--------2

если вы потратите его на два, вы получите следующее:

0----47----3
|    ||    |
|    ||    |
|    ||    |
|    56    |
|          |
|          |
|          |
1----------2

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

(c) случай 3

       4--3
 0--X9 |  |
 |  78 |  |
 |  6--5  |
 |        |
 1--------2

расходуется на 1. Это более общий случай для случая 1.

(d) случай 4

то же, что и case3, но потратьте на два.

Собственно, если вы можете обрабатывать случай 4. Все остальные случаи являются лишь частным случаем с некоторой перекрывающейся линией или вершиной.

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

Ответ 6

Вот альтернативное решение, посмотрите, нравится ли вам это лучше.

  • Сделайте triangulation, это не должно быть delaunay - любая триангуляция будет делать.

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

  • Объединить их с помощью модифицированного алгоритма отсечения Weiler-Atherton

Ответ 8

Я написал несколько сообщений в блоге о своих опытах по попытке реализовать алгоритм прямого скелета для этого случая (расширение/сокращение многоугольника путем смещения). В то время у меня было два блогов. Может быть полезно прочитать некоторые проблемы, с которыми я столкнулся:

Привет,

Max

Ответ 9

Большое спасибо Ангусу Джонсону за его библиотеку клиперов. Есть хорошие примеры кода для создания отсечения на странице клипера в http://www.angusj.com/delphi/clipper.php#code но я не видел примера для переноса многоугольника. Поэтому я подумал, что, возможно, это полезно для кого-то, если я отправлю свой код:

    public static List<Point> GetOffsetPolygon(List<Point> originalPath, double offset)
    {
        List<Point> resultOffsetPath = new List<Point>();

        List<ClipperLib.IntPoint> polygon = new List<ClipperLib.IntPoint>();
        foreach (var point in originalPath)
        {
            polygon.Add(new ClipperLib.IntPoint(point.X, point.Y));
        }

        ClipperLib.ClipperOffset co = new ClipperLib.ClipperOffset();
        co.AddPath(polygon, ClipperLib.JoinType.jtRound, ClipperLib.EndType.etClosedPolygon);

        List<List<ClipperLib.IntPoint>> solution = new List<List<ClipperLib.IntPoint>>();
        co.Execute(ref solution, offset);

        foreach (var offsetPath in solution)
        {
            foreach (var offsetPathPoint in offsetPath)
            {
                resultOffsetPath.Add(new Point(Convert.ToInt32(offsetPathPoint.X), Convert.ToInt32(offsetPathPoint.Y)));
            }
        }

        return resultOffsetPath;
    }

Ответ 11

Основываясь на рекомендации @JoshO'Brian, кажется, что пакет rGeos на языке R реализует этот алгоритм. См. rGeos::gBuffer.

Ответ 12

Еще один вариант - использовать boost:: polygon - документации недостаточно, но вы должны найти, что методы resize и bloat, а также перегруженный оператор +=, который фактически реализует буферизацию. Так, например, увеличение размера многоугольника (или множества полигонов) некоторым значением может быть таким же простым, как:

poly += 2; // buffer polygon by 2

Ответ 13

Есть несколько библиотек, которые можно использовать (также можно использовать для наборов данных 3D).

  1. https://github.com/otherlab/openmesh
  2. https://github.com/alecjacobson/nested_cages
  3. http://homepage.tudelft.nl/h05k3/Projects/MeshThickeningProj.htm

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

Последний имеет наименьшие зависимости, является автономным и может читать в файлах .obj.

С наилучшими пожеланиями, Стефан

Ответ 14

Я использую простую геометрию: векторы и/или тригонометрию

  1. В каждом углу найдите средний вектор и средний угол. Средний вектор - среднее арифметическое двух единичных векторов, определяемых ребрами угла. Средний угол - это половина угла, определенного краями.

  2. Если вам нужно расширить (или сжать) свой многоугольник на величину d от каждого ребра; Вы должны выйти (в) на величину d/sin (midAngle), чтобы получить новую угловую точку.

  3. Повторите это для всех углов

*** Будьте осторожны в вашем направлении. Сделайте CounterClockWise Test, используя три точки, определяющие угол; выяснить, какой выход, или в.