Prosty algorytm przecięcia wielokątów

Szukam bardzo prostego algorytmu do obliczania przecięcia/obcinania wielokątów. Czyli biorąc pod uwagę wielokąty P, Q, chcę znaleźć wielokąt T, który jest zawarty w P i w Q, i chcę T być maksymalnym wśród wszystkich możliwych wielokątów.

Nie przeszkadza mi czas wykonywania (mam kilka bardzo małych wielokątów), mogę sobie również pozwolić na uzyskanie przybliżenia przecięcia wielokątów (czyli wielokąt z mniejszą liczbą punktów, ale który nadal jest zawarty w wielokątach" skrzyżowanie).

Ale bardzo ważne jest dla mnie, aby algorytm był prosty (tańsze Testowanie) i najlepiej krótki (mniej kodu).

Edit: proszę zauważyć, że chcę uzyskać wielokąt, który reprezentuje przecięcie. Nie potrzebuję tylko logicznej odpowiedzi na pytanie, czy dwa wielokąty się przecinają.

Author: Elazar Leibovich, 2010-02-16

9 answers

Rozumiem, że oryginalny plakat szukał prostego rozwiązania, ale niestety naprawdę nie ma prostego rozwiązania.

Niemniej jednak, niedawno stworzyłem darmową bibliotekę clipping open-source (napisaną w Delphi, C++ i C#), która klipuje wszelkiego rodzaju wielokąty (w tym przecinające się). Ta Biblioteka jest dość prosta w użyciu: http://sourceforge.net/projects/polyclipping /.

 50
Author: Angus Johnson,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-05-30 08:08:28

Możesz użyć algorytmu obcinania wielokątów , aby znaleźć przecięcie między dwoma wielokątami. Jednak te wydają się być skomplikowane algorytmy, gdy wszystkie przypadki krawędzi są brane pod uwagę.

Jedną z implementacji obcinania wielokątów, której możesz użyć swojej ulubionej wyszukiwarki, jest Weiler-Atherton . artykuł na Wikipedii O Weiler-Atherton

Alan Murta ma kompletną implementację obcinacza wielokątów GPC .

Edit:

Innym podejściem jest najpierw podzielenie każdego wielokąta na zbiór trójkątów, z którymi łatwiej sobie radzić. Twierdzenie o dwóch uszach autorstwa Gary ' ego H. Meistersa rozwiązuje ten problem. Ta strona w McGill dobrze wyjaśnia podział trójkąta.

 17
Author: Doug Ferguson,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-02-17 00:49:31

Jeśli używasz C++ i nie chcesz tworzyć algorytmu samodzielnie, możesz użyć Boost.Geometria . Wykorzystuje zaadaptowaną wersję wspomnianego powyżej algorytmu weilera-Athertona.

 11
Author: Barend,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-02-21 10:15:14

Nie dałeś nam swojej reprezentacji wielokąta. Więc wybieram (raczej sugeruję) jeden dla Ciebie:)

Reprezentuj każdy wielokąt jako jeden duży wielokąt wypukły oraz listę mniejszych wielokątów wypukłych, które należy 'odjąć' od tego dużego wielokąta wypukłego.

Teraz biorąc pod uwagę dwa wielokąty w tej reprezentacji, można obliczyć przecięcie jako:

Oblicz przecięcie dużych wielokątów wypukłych, aby utworzyć duży wielokąt przecięcia. Następnie "odjąć" przecięcia wszystkich mniejszych z obu, aby uzyskać listę podkategorii wielokątów.

Otrzymujesz nowy wielokąt po tej samej reprezentacji.

Ponieważ przecięcie wielokątów wypukłych jest łatwe, znalezienie przecięcia również powinno być łatwe.

Wydaje się, że to powinno zadziałać, ale nie myślałem o tym głębiej, jeśli chodzi o poprawność/złożoność czasu/przestrzeni.

 6
Author: ,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-02-16 17:44:40

Oto podejście oparte na triangulacji, które jest dość proste do wdrożenia i może być uruchomione w O (N2).

BTW, O (N2) jest optymalny dla tego problemu. Wyobraź sobie dwa wielokąty w kształcie łopatek widełek przecinających się pod kątem prostym. Każdy z nich ma liczbę segmentów proporcjonalną do liczby zębów; liczba wielokątów na przecięciu jest proporcjonalna do kwadratu liczby zębów.

  1. Po pierwsze, triangulować każdy wielokąt.

  2. Porównaj wszystkie trójkąty z P parami ze wszystkimi trójkątami z Q, aby wykryć przecięcia. Każda para przecinających się trójkątów może być podzielona na mniejsze Trójkąty, z których każdy jest w P, W Q lub w przecięciu. (Cokolwiek zostało użyte w kroku 1, można ponownie wykorzystać w tym celu.) Zachowaj tylko Trójkąty znajdujące się na skrzyżowaniu.

  3. Oblicz sąsiadów każdego trójkąta, porównując je parami, i zbuduj Wykres przyległości. Ten wykres będzie zawierać jeden połączony podgraf dla każdego wielokąta na przecięciu P I Q.

  4. Dla każdego takiego podgrafu wybierz Trójkąt, przejdź do krawędzi, a następnie przejdź wokół krawędzi, tworząc segmenty obwiedni odpowiadające wielokątowi wyjściowemu.

 5
Author: Eric,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-06-06 11:31:42

Oto proste i głupie podejście: przy wejściu dyskrecjonuj swoje wielokąty w bitmapę. Przecinać i bitmapy razem. Aby wytworzyć wielokąty wyjściowe, wyznaczaj poszarpane granice bitmapy i wygładzaj je za pomocą algorytmu przybliżania wielokątów . (Nie pamiętam, czy ten link daje najbardziej odpowiednie algorytmy, to tylko pierwszy hit Google. Możesz sprawdzić jedno z dostępnych narzędzi do konwersji obrazów bitmapowych na reprezentacje wektorowe. Może mógłbyś zadzwonić na nich bez ponownego wdrożenia algorytmu?)

Najbardziej złożoną częścią byłoby wytyczanie granic, tak myślę.

Na początku lat 90-tych miałem do czynienia z czymś takim w pracy, tak przy okazji. Wymyśliłem (zupełnie inny) algorytm, który działałby na współrzędnych liczb rzeczywistych, ale wydawało się, że natknie się na zupełnie nieosiągalną masę zdegenerowanych przypadków w obliczu rzeczywistości zmiennoprzecinkowej (i hałaśliwego wejścia). Być może z pomocą internet zrobiłbym lepiej!

 3
Author: Darius Bacon,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-02-16 23:09:37

Nie mam bardzo prostego rozwiązania, ale oto główne kroki dla prawdziwego algorytmu:

  1. wykonaj custom double linked list for the polygon vertices and krawędzie. Użycie std::list nie da rady, ponieważ musisz zamienić następny i poprzednie wskaźniki / przesunięcia się do specjalnej operacji na węzły. Jest to jedyny sposób, aby mieć prosty kod, a to da dobry występ.
  2. znajdź punkty przecięcia, porównując każdą parę krawędzi. Uwaga że porównując każdy para krawędzi da Czas O (N2), ale poprawa algorytm do O (N * logN) będzie później łatwy. Dla jakiejś pary krawędziach (powiedzmy A→b I c → d), punkt przecięcia znajduje się za pomocą parametr (od 0 do 1) na krawędzi a→b, który jest dany przez tₐ=d₀/(d₀-d₁), gdzie d₀ (Z-A)×(B-A) i d₁ E (D-A)×(W-a). × jest iloczyn krzyżowy 2D, np. p×q=pₓ * qᵧ-pᵧ * qₓ. Po znalezieniu tₐ, znalezienie punktu przecięcia to użycie go jako interpolacji liniowej parametr na segmencie a→b: P = a + tₐ (b-a)
  3. Podziel każdą krawędź dodając wierzchołki (i węzły na liście połączonych) gdzie przecinają się segmenty.
  4. Następnie musiszprzekroczyć węzły w punktach przecięcia. To jest operacja, dla której trzeba było wykonać niestandardowe podwójne połączenie lista. Musisz zamienić parę następny wskaźnik (i zaktualizować poprzednie odpowiednio).

Następnie masz nieprzetworzony wynik algorytmu rozwiązywania przecięć wielokątów. Zwykle będziesz chcesz wybrać jakiś region zgodnie z numerem uzwojenia każdego regionu. Poszukaj Liczby uzwojenia wielokąta, aby uzyskać wyjaśnienie na ten temat.

Jeśli chcesz zrobić algorytm O(N·logN) z tego o(N2), musisz zrobić dokładnie to samo, z wyjątkiem tego, że robisz to wewnątrz algorytmu zamiatania linii. Poszukaj algorytmu Bentleya Ottmana . Wewnętrzny algorytm będzie taki sam, z tą tylko różnicą, że będziesz miał zmniejszoną liczbę krawędzi do porównania, wewnątrz pętla.

 0
Author: Dom,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-05-26 13:43:26

Sposób, w jaki pracowałem nad tym samym problemem

  1. rozbicie wielokąta na odcinki linii
  2. znajdź przecinającą się linię używając IntervalTrees LUB LineSweepAlgo
  3. znajdowanie zamkniętej ścieżki używając GrahamScanAlgo, aby znaleźć zamkniętą ścieżkę z sąsiednimi wierzchołkami
  4. Odsyłacz 3. z DinicAlgo aby je rozpuścić

Uwaga: mój scenariusz był inny, ponieważ wielokąty miały wspólny wierzchołek. Ale mam nadzieję, że to może pomóc

 0
Author: Ansh David,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-08-20 15:17:46

Może to być ogromne przybliżenie w zależności od twoich wielokątów, ale oto jeden:

  • Oblicz środek masy dla każdego wielokąt.
  • Oblicz min lub max lub średnią odległość od każdego punktu wielokąt do środka masy.
  • Jeśli C1C2 (gdzie C1/2 jest środkiem pierwszego/drugiego wielokąta) >= D1 + D2 (gdzie D1 / 2 jest odległością obliczoną dla pierwszego / drugiego wielokąta), to dwa wielokąty "przecinają się".

Chociaż powinno to być bardzo skuteczne, ponieważ każda transformacja do wielokąta odnosi się w ten sam sposób do środka masy, A odległości środek-węzeł można obliczyć tylko raz.

 -2
Author: Sylvestre Equy,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-02-16 11:08:58