Как получить многоугольник из boost :: geometry :: model :: polygon?

Я пытаюсь вычислить разницу между двумя полигонами, используя boost::geometry::difference, где boost::geometry::model::polygon представляет мои полигоны.

В случае, когда первый многоугольник содержит второй, результатом операции будет один boost::geometry::model::polygon с внутренним и внешним кольцами, заполненными координатами исходных многоугольников.

Как мне получить многоугольник (в смысле элементарной геометрии) из boost::geometry::model::polygon?

Уточнение:

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

Внешнее кольцо boost::geometry::model::polygon - многоугольник, внутренние кольца тоже многоугольники. В целом boost::geometry::model::polygon не является многоугольником.

Итак, о чем я спрашиваю: как преобразовать boost::geometry::model::polygon в нормальный многоугольник (имеющий единственную цепочку отрезков прямых линий), который представляет ту же область на плоскости.

Вот чего я пытаюсь достичь:

polygon1   = (0,0), (0,8), (8,8), (8,0), (0,0)
polygon2   = (2,2), (2,6), (6,6), (6,2), (2,2)

Полигоны 1 и 2 зеленого цвета / oker:

difference = (0,0), (0,4), (2,4), (2,2), (6,2), (6,6), (2,6), (2,4), (0,4), (0,8), (8,8), (8,0), (0,0)

Ожидаемая разница в сером:

Я знаю, что тот же boost::geometry::model::polygon, имеющий внутренние кольца, может быть представлен бесконечным количеством различных нормальных многоугольников. Мне все равно, какой я получу.


person empty'void    schedule 06.05.2016    source источник
comment
Не могли бы вы прояснить свой вопрос? У вас уже есть объект, представляющий многоугольник, какую еще форму многоугольника вы хотите получить и чего вы действительно пытаетесь достичь? Может быть, вы хотите построить многоугольник?   -  person Marko Popovic    schedule 06.05.2016
comment
Re. ваше редактирование Я знаю, что один и тот же boost :: geometry :: model :: polygon, имеющий внутренние кольца, может быть представлен бесконечным количеством различных нормальных многоугольников. Мне все равно, какой я получу. - кажется, что это как раз противоречит вашим реальным требованиям: вы, кажется, очень заботитесь. Это просто так, потому что одно внешнее кольцо с одним или несколькими внутренними кольцами невозможно представить как нормальный многоугольник¹ по-другому. (¹ваше определение: (имеющий единственную цепочку отрезков прямых))   -  person sehe    schedule 08.05.2016
comment
Как вы можете видеть в моем примере, чтобы представить разницу между двумя многоугольниками, я ввел отрезок прямой линии (0,4) - (2,4), создав слабо простой многоугольник, очевидно, что вместо него можно использовать любой другой отрезок прямой, соединяющий внутреннюю и внешнюю границы, что приведет к бесконечному множеству решений для такой разницы. И определение не мое, оно взято из Википедии.   -  person empty'void    schedule 08.05.2016
comment
Думаю, я понял ваш подход. Я бы не сказал, что ожидаемые результаты будут топологически / математически эквивалентными, но я понимаю, как их можно рассматривать как прагматически эквивалентные. Вскоре я опубликую удар по пользовательскому алгоритму.   -  person sehe    schedule 08.05.2016
comment
Спасибо за иллюстрации, хотя похоже, что polygon1 отображается как квадрат со стороной 10 вместо 8.   -  person empty'void    schedule 09.05.2016
comment
Это может быть интересно github.com/ivanfratric/polypartition/blob / master / src /   -  person Jens Åkerblom    schedule 18.02.2017


Ответы (2)


Вы можете легко построить кольцо, моделирующее ваш ожидаемый слабый простой многоугольник. Первый:

Предостережение

Однако обратите внимание, что результат недействителен для дальнейшего использования с алгоритмами библиотеки Boost Geometry.

Возьмем ваш буквальный пример:

std::string reason;
poly expected;
bg::read_wkt("POLYGON((0 0, 0 4, 2 4, 2 2, 6 2, 6 6, 2 6, 2 4, 0 4, 0 8, 8 8, 8 0, 0 0))", expected);
bool ok = bg::is_valid(expected, reason);
std::cout << "Expected: " << bg::dsv(expected) << (ok?" valid":" invalid: '" + reason + "'") << "\n";

Печать

Ожидается: (((0, 0), (0, 4), (2, 4), (2, 2), (6, 2), (6, 6), (2, 6), (2, 4) ), (0, 4), (0, 8), (8, 8), (8, 0), (0, 0))) недопустимо: 'Геометрия имеет недопустимые самопересечения. Точка самопересечения была найдена в (0, 4); метод: t; операции: х / у; идентификаторы сегментов {источник, мульти, кольцо, сегмент}: {0, -1, -1, 0} / {0, -1, -1, 7} '

Реализация алгоритма

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

ring weak_simple_ring(poly& p) {
    ring r = p.outer();

    for (auto& i: p.inners()) {
        auto last = r.back();
        r.insert(r.end(), i.rbegin(), i.rend());
        r.insert(r.end(), last);
    }

    return r;
}

Единственный более тонкий момент - это изменить направление (CW / CCW) внутренних колец, чтобы оно соответствовало направлению внешнего кольца.

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

ДЕМО

Вот полная живая демонстрация

Live On Coliru

Если ввод -

bg::read_wkt("POLYGON((0 0,0 10,10 10,10 0,0 0))", a);
bg::read_wkt("POLYGON((2 2, 2 6, 6 6, 6 2, 2 2))", b);

Преобразование

std::vector<poly> output;
bg::difference(a, b, output);

for (auto& p : output) {
    ring r = weak_simple_ring(p);
    bg::convert(r, p);
}

Результат станет .

Более сложный образец

Представьте, когда у b была дыра:

bg::read_wkt("POLYGON((0 0,0 10,10 10,10 0,0 0))", a);
bg::read_wkt("POLYGON((2 2, 2 6, 6 6, 6 2, 2 2)(3 3, 5 3, 5 5, 3 5, 3 3))", b);

Результат с тем же кодом становится .

person sehe    schedule 08.05.2016
comment
Да, я уже имел в виду подобное решение. К сожалению, в целом это не работает: между выбранными вершинами могут быть другие дыры или другой порядок вершин (см. модифицированная демонстрация). Я знаю, что проблема может быть решена путем поиска ближайшей пары вершин и интеграции одного внутреннего кольца за раз, хотя я надеялся на более производительный алгоритм. - person empty'void; 10.05.2016

Это старый ответ. После редактирования вопроса я опубликовал простую реализацию алгоритма, подходящего для данного образца

Уже есть.

Если вы имеете в виду «простой» многоугольник без дырок, то внешнее кольцо - это все, что вам нужно. Просто выбросьте внутренние кольца.

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

Образец:

bg::read_wkt("POLYGON((0 0,0 10,10 10,10 0,0 0))", a);
bg::read_wkt("POLYGON((2 -2,2 12,5 12,5 -2,2 -2))", b);

Здесь b разрезает a на две отдельные части. Итак, вы должны быть готовы обрабатывать несколько разрозненных выходных полигонов:

Live On Coliru

#include <boost/geometry.hpp>
#include <boost/geometry/geometries/point_xy.hpp>
#include <boost/geometry/geometries/polygon.hpp>
#include <boost/geometry/io/io.hpp>
#include <iostream>
#include <fstream>

namespace bg = boost::geometry;
using pt   = bg::model::d2::point_xy<int>;
using poly = bg::model::polygon<pt>;

int main() {
    poly a, b;
    bg::read_wkt("POLYGON((0 0,0 10,10 10,10 0,0 0))", a);
    bg::read_wkt("POLYGON((2 -2,2 12,5 12,5 -2,2 -2))", b);

    std::cout << bg::dsv(a) << "\n";
    std::cout << bg::dsv(b) << "\n";

    {
        std::ofstream svg("/tmp/input.svg");
        boost::geometry::svg_mapper<pt> mapper(svg, 400, 400);
        mapper.add(a);
        mapper.add(b);

        mapper.map(a, "fill-opacity:0.5;fill:rgb(153,204,0);stroke:rgb(153,204,0);stroke-width:2");
        mapper.map(b, "fill-opacity:0.5;fill:rgb(204,153,0);stroke:rgb(202,153,0);stroke-width:2");
    }

    std::vector<poly> output;
    bg::difference(a, b, output);
    for (auto& p : output)
        std::cout << "\n\t" << bg::dsv(p);

    {
        std::ofstream svg("/tmp/output.svg");
        boost::geometry::svg_mapper<pt> mapper(svg, 400, 400);
        assert(output.size() == 2);
        mapper.add(output[0]);
        mapper.add(output[1]);
        mapper.add(b);

        mapper.map(output[0], "fill-opacity:0.5;fill:rgb(153,204,0);stroke:rgb(153,204,0);stroke-width:2");
        mapper.map(output[1], "fill-opacity:0.5;fill:rgb(153,204,0);stroke:rgb(153,204,0);stroke-width:2");
        mapper.map(b, "fill-opacity:0.15;fill:rgb(153,153,153);stroke-width:0");
    }
}

Печать:

(((0, 0), (0, 10), (10, 10), (10, 0), (0, 0)))
(((2, -2), (2, 12), (5, 12), (5, -2), (2, -2)))

    (((5, 10), (10, 10), (10, 0), (5, 0), (5, 10)))
    (((2, 10), (2, 0), (0, 0), (0, 10), (2, 10)))

Визуализированные SVG:

введите описание изображения здесь

person sehe    schedule 06.05.2016
comment
Вся моя цель использования boost::geometry::difference заключалась в том, чтобы исключить область, представленную вторым многоугольником, поэтому мне нет смысла отбрасывать внутренние кольца. И да, я знаю, что результатом может быть список полигонов, меня это вполне устраивает. - person empty'void; 07.05.2016
comment
Все, что я могу сказать, это то, что, возможно, ваши вопросы должны были быть немного яснее (например, такими же ясными, как мой образец). Это предотвратит потерю времени. - person sehe; 08.05.2016
comment
Извините, я думаю, что многоугольник в смысле элементарной геометрии будет достаточно ясным. - person empty'void; 08.05.2016