Многоугольник внутри многоугольника внутри многоугольника

У меня есть несколько простых многоугольников, которые не пересекаются друг с другом, но некоторые многоугольники могут быть встроены в другие.

Например:

+--------------------------------------------+
|                                            |
|   +----------------+          +--------+   |
|   |                |         /         |   |
|   |   +--------+   |        /          |   |
|   |   |        |   |       /  +----(2)-+   |
|   |   |        |   |      /   |            |
|   |   +----(3)-+   |     /    |   +---+    |
|   |                |    +-----+   |   |    |
|   +------------(2)-+              +(2)+    |
|                                            |
+----------------------------------------(1)-+

Как узнать «глубину» всех полигонов? Другими словами, как узнать, сколько полигонов охватывает полигон? «Глубина» - это числа в скобках.

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


person Ecir Hana    schedule 17.01.2013    source источник
comment
Какие структуры данных вы используете для хранения полигонов?   -  person High Performance Mark    schedule 17.01.2013
comment
[[v1, v2, v3], [v4, v5, v6, v7], [v8, v9, ... - в основном плоский список, без какой-либо древовидной структуры, если вы это имели в виду.   -  person Ecir Hana    schedule 17.01.2013
comment
@EcirHana под простыми многоугольниками вы на самом деле имеете в виду простые многоугольники или прямоугольные простые многоугольники, выровненные по оси?   -  person mmgp    schedule 17.01.2013
comment
@mmgp: Я имею в виду любые простые многоугольники.   -  person Ecir Hana    schedule 17.01.2013
comment
@EcirHana, этим занимается документ «Вложение многоугольников и надежность», я мог бы включить ответ позже, если его никто не даст раньше.   -  person mmgp    schedule 18.01.2013
comment
Если вам нужно знать глубину данного многоугольника P, и у вас есть точка O, которая гарантированно находится вне (каждого многоугольника), возможно, подсчитывая количество многоугольников, однозначно пересекаемых отрезком между O и вершиной P, ближайшей к O может работать.   -  person didierc    schedule 18.01.2013
comment
Фигуры на самом деле многоугольники или прямоугольники? Алгоритм значительно упрощается, если использовать прямоугольники.   -  person Nuclearman    schedule 18.01.2013
comment
@MC: Я имею в виду любые простые многоугольники.   -  person Ecir Hana    schedule 18.01.2013
comment
@mmgp: спасибо, бумага выглядит очень хорошо, но кода нет. Пожалуйста, случайно, у вас нет кода? Или хотя бы подробный псевдокод?   -  person Ecir Hana    schedule 18.01.2013
comment
Похоже, что вы ищете извилистое число (потому что мы можем рассматривать все эти многоугольники как один большой самоперекрывающийся многоугольник): geomalgorithms.com/a03-_inclusion.html?   -  person Samuel Audet    schedule 22.01.2013
comment
@SamuelAudet: спасибо, но я не совсем понимаю, что вы имеете в виду - как мне выбрать P? И нужно ли мне рассчитывать количество витков всех вершин?   -  person Ecir Hana    schedule 22.01.2013
comment
Выберите одну из конечных точек и в идеале используйте симуляцию простоты, как описано на этой странице ecse.rpi.edu/Homepages/wrf/Research/Short_Notes/pnpoly.html ... Я мог бы попытаться дать ответ, если это похоже на то, что вам нужно   -  person Samuel Audet    schedule 22.01.2013
comment
@SamuelAudet Я, конечно, был бы благодарен за ответ. Но я не понимаю, что вы предлагаете, поэтому не знаю, похоже ли это на то, что мне нужно. Чем это отличается от подсчета того, сколько раз точка многоугольника находится внутри всех других многоугольников? Можете ли вы оценить его сложность? Другими словами, если в худшем случае я получу эти значения глубины лучше, чем за квадратичное время, то да, пожалуйста, опубликуйте их ниже.   -  person Ecir Hana    schedule 22.01.2013
comment
Хм, я думаю, это в значительной степени то, что вы объясняете в своем вопросе. Мы можем использовать такой алгоритм, как развертка плоскости, чтобы снизить его до что-то вроде O(n log n), но да, это усложняется ...   -  person Samuel Audet    schedule 23.01.2013
comment
@SamuelAudet, не могли бы вы объяснить мне, как использовать линейную развертку, чтобы уменьшить время до O (nlogn)?   -  person Ecir Hana    schedule 23.01.2013
comment
давайте продолжим обсуждение в чате   -  person Samuel Audet    schedule 23.01.2013


Ответы (5)


Поместите свои многоугольники в какую-нибудь структуру пространственного поиска, например R-tree на основе минимальных ограничивающих прямоугольников для многоугольников. Тогда вы сможете вычислить отношение включения, которое вам нужно, в O (n log n). (Если только вы не находитесь в патологическом случае, когда многие из минимальных ограничивающих прямоугольников для ваших многоугольников перекрываются, что кажется маловероятным, исходя из вашего описания.)

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

person Gareth Rees    schedule 18.01.2013
comment
Это очень упрощенный подход, который вообще не требует наличия патологических случаев. Предположим, есть большой многоугольник в форме C, если мы поместим квадрат в свободную пустую область C, почему вы скажете, что этот квадрат содержится в другом многоугольнике? Или, может быть, это патологический случай, и я добавляю ненужные требования к вопросу, который мне не принадлежит. - person mmgp; 18.01.2013
comment
Спасибо за ответ, но не думаю, что это сработает. Проблема заключается во времени, необходимом для создания структуры поиска, поскольку она больше не будет использоваться повторно (т.е. полигоны могут каждый раз быть разными). Кроме того, bbox-ы могут перекрываться, а могут и не перекрываться, это 50%, и это много :). - person Ecir Hana; 18.01.2013
comment
Создание R-дерева стоит O (n log n), поэтому я не думаю, что это вызывает беспокойство. - person Gareth Rees; 18.01.2013
comment
@EcirHana, это будет работать, создание пространственного индекса может быть очень быстрым, я рекомендую использовать квадродерево прямоугольника PMR в качестве пространственного индекса. - person AlexWien; 30.03.2013

(Этот подход основан на идее, аналогичной подходу @GarethRees: во-первых, дешево определите, какие пары многоугольников вам не нужно проверять на включение. Как только количество пар, все еще необходимое для проверки, станет приемлемым, выполните точное (дорогое) геометрическое проверить.)

Для каждого многоугольника p легко вычислить ограничивающий прямоугольник, то есть левый, правый, верхний и нижний, так что давайте сделаем это в первую очередь. Например. слева: p.L = min { x : (x,y) is a vertex of p } Время линейно зависит от количества точек.

Чтобы не сравнивать каждый многоугольник друг с другом, вы можете разделить область на сетку 2x2. Предположим, что ширина и высота области соответственно задаются Dx и Dy, тогда вы можете создать девять наборов top,bottom,left,right,topright,topleft,bottomright,bottomleft,rest и сделать следующее:

for p in polygons:
    in_top    = p.B > Dy/2
    in_bottom = p.T < Dy/2
    in_left   = p.R < Dx/2
    in_right  = p.L > Dx/2 
    if in_top:
        if in_left:
            add p to topleft
        elsif in_right:
            add p to topright
        else:
            add p to top
    elsif in_bottom:
        if in_left:
            add p to bottomleft
        elsif in_right:
            add p to bottomright
        else:
            add p to bottom

    if in_right and not (in_top or in_bottom):
        add p to right
    elsif in_left and not (in_top or in_bottom):
        add p to left

    if not (in_top or in_bottom or in_left or in_right):
        add p to rest

Это снова линейное время. Каждый многоугольник был разделен на наиболее "плотно" содержащий сектор. Что вы этим приобрели? Что ж, вы знаете, например, что для любого многоугольника p в left не может быть никаких отношений включения с набором right, поэтому вам не нужно их сравнивать. Аналогично между bottomleft и right, bottomleft и topleft и так далее. Вот как это будет выглядеть на вашем примере:

                      Dx/2
+----------------------|---------------------+
|                      |                     |
|   +----------------+ |        +--------+   |
|   |                | |       /         |   |
|   |   +--------+   | |      /          |   |
|___|___|________|___|_|____ /__+===d(2)=+___|_ Dy/2
|   |   |        |   | |    /   |            |
|   |   +---b(3)-+   | |   /    |   +---+    |
|   |                | |  +-----+   |   |    |
|   +-----------c(2)-+ |            e(2)+    |
|                      |                     |
+----------------------|----------------a(1)-+

So rest = {a}, top = {}, bottom = {}, left = {b,c}, right = {d}

topleft = {}, topright = {}, bottomleft = {}, bottomright = {e}

Итак, теперь вам нужно сравнить (с дорогостоящим точным чеком) не более b с c, d с e и a со всеми остальными - на самом деле, если вы заказываете чеки с умом, вам не нужно будет сравните a со всеми остальными, потому что включение является транзитивным, поэтому, если вы заметили, что c включает b, а a включает c, тогда вам не нужно проверять, включает ли a b.

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

person mitchus    schedule 29.01.2013
comment
Спасибо за развернутый ответ! К сожалению, меня очень интересуют эти дорогие точные чеки. Я знаю, что могу использовать пространственное хеширование, ограничивающие прямоугольники или ограничивающие иерархии, но, насколько я понимаю, ни один из них не ускоряет худший случай. Статья «Вложенность полигонов и надежность», упомянутая выше @mmgp, кажется наиболее близкой к правильному подходу, но немного сложно преобразовать ее в рабочий код. - person Ecir Hana; 29.01.2013
comment
прежде чем вы это сделаете, какая у них сложность? - person Ecir Hana; 29.01.2013
comment
@EcirHana: количество вершин многоугольника 1 умножается на количество вершин многоугольника 2. - person mitchus; 29.01.2013
comment
Делится на два, да. Это то, что я предлагаю в этом вопросе и хотел бы улучшить его. - person Ecir Hana; 29.01.2013
comment
@EcirHana: насколько я понимаю, вы предлагаете проверить каждую пару полигонов, т.е. что-то квадратичное по количеству полигонов. Я предлагаю сначала избавиться от многих таких пар с помощью простой эвристики линейного времени, а затем для всех случаев, которые мы не исключили, провести точную проверку, которая квадратична по количеству вершин оставшихся многоугольников . Дайте мне знать, если вы согласны. - person mitchus; 29.01.2013
comment
позвольте нам продолжить обсуждение в чате - person Ecir Hana; 29.01.2013

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

Шаг 1

Предположим, мы определяем отношение '‹' между многоугольниками следующим образом: A‹ B, если A находится внутри B. Так бывает, что если A ‹B и B‹ C, то A ‹C (т.е. если многоугольник A находится внутри B, а B - внутри B. внутри C, тогда A должен быть внутри C). Теперь у нас есть строгое слабое упорядочение между произвольными многоугольниками.

[Изменить: вам нужно будет использовать какой-то тест «точка внутри невыпуклого многоугольника», но, по-видимому, вы уже это делаете.]

Шаг 2

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

[Edit: Это важный шаг, потому что он избавляет от квадратичной сложности.]

Шаг 3

Убедитесь, что «самый большой» (то есть самый внешний) элемент стоит первым в вашем отсортированном массиве многоугольников. (Если необходимо, переверните список - он линейный по количеству полигонов).

Шаг 4

Теперь «самый большой» (то есть самый внешний) многоугольник должен быть первым элементом.

[Edit: Фактически, полигоны были отсортированы по их глубине. Однако два многоугольника с одинаковой глубиной могут отображаться в разном порядке в зависимости от того, была ли сортировка стабильной. Для нас это не имеет значения; нас интересуют глубокие изменения.]

Теперь мы назначим глубину каждому многоугольнику следующим образом. Во-первых, инициализируйте глубину каждого до 0 ([Edit: инициализировать 1, согласно примеру]). Затем выполните итерацию по отсортированному списку, но на этот раз сравните каждый элемент p только со следующим элементом p + 1. Если (p + 1 ‹p) истинно, то увеличить глубину p + 1. В противном случае установите глубину p + 1 равной глубине p.

person maditya    schedule 29.03.2013
comment
Что делать, если ни один из полигонов не вложен? То есть, что, если все полигоны самые внешние? - person Ecir Hana; 30.03.2013
comment
Тогда в любом порядке они будут считаться отсортированными, и все они будут иметь глубину 0, поскольку каждое сравнение < вернет false. Разве ты не этого хочешь? - person maditya; 30.03.2013
comment
Кроме того, только что понял, что ваша самая внешняя глубина должна быть 1 (в соответствии с вашим примером). Это достигается за счет инициализации глубины равной 1 вместо 0 на шаге 4. - person maditya; 30.03.2013
comment
Я пытаюсь сказать, что сортировка выполняет n log n сравнений, и каждое сравнение имеет линейную сложность. Так это O (n n log n) - хуже наивной версии из вопроса. - person Ecir Hana; 30.03.2013

Шаг 1. Сориентируйте многоугольники в одном направлении, скажем против часовой стрелки.

Шаг 2: Для любой точки (x, y), для которой необходимо вычислить «глубину» для вычисления общего числа витков. Это можно сделать несколькими способами; самый быстрый на практике - вычислить ПОДПИСАННОЕ количество пересечений между горизонтальным (или вертикальным) лучом, берущим начало в (x, y).

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

person Michael    schedule 27.08.2013
comment
Шаг 2 имеет линейную сложность, и я должен повторить его для всех многоугольников, чтобы он имел ту же квадратичную сложность, что и исходный подход, описанный выше. - person Ecir Hana; 28.08.2013

В вашем примере вы изображаете невыпуклый многоугольник, поэтому подход rtree, предложенный другими, не будет эффективен сам по себе. Но вы можете объединить это с дополнительной проверкой, чтобы увидеть, находится ли одна из вершин многоугольника внутри другой, когда вы найдете совпадение с подходом rtree. Это уменьшит количество проверок "точка в многоугольнике".

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

  1. Изначально каждый многоугольник является корневым узлом и отдельным деревом.
  2. Вы должны перебрать свой набор корневых узлов и проверить, находится ли он внутри другого корневого узла, используя тест «точка в многоугольнике».
  3. Когда вы найдете совпадение, удалите меньший многоугольник из набора корневых узлов и добавьте его в качестве дочернего узла к большему многоугольнику. Вы начали строить осмысленную древовидную структуру, и вы также уменьшаете размер контейнера, который вы повторяете (корневые узлы), поэтому будущие итерации будут быстрее.
  4. Продолжайте так, пока количество корневых узлов не перестанет меняться.
  5. Помните, что в основном вложенном цикле вы сравниваете, только если один корневой узел находится внутри другого. Когда вы найдете совпадение (маленький узел внутри большого узла), вы можете пропустить поддерево меньшего узла. Но вам нужно пройти поддерево большего узла и выполнить проверки, чтобы найти правильное место для меньшего узла в иерархии. Но все же вы избегаете множества проверок по сравнению с простым вложенным циклом с квадратичной сложностью.
person Ranjeeth Mahankali    schedule 27.12.2019