Problem można ugryźć tak: (MarekR22 napisał co trzeba zrobić, ale nie napisał jak to zrobić :) )
- Wyszukujesz spójne plamy - flood fill.
- Wyznaczasz obwiednię - algorytm mrówkowy czy jakoś tak (nie mylić z ACO) i przy okazji bounding box. Środki pikseli brzegowych plamy tworzą wielokąt. Np obwiednia teh plamy
*******
*A*****
**BCDE*
***FGH*
***IJK*
*******
to kolejno punkty: EDCBABFIJKH
- Trudne - łączysz plamy które są blisko siebie: dla każdej pary plam których bounding box'y są odpowiednio blisko siebie wyszukujesz dwa najbliższe piksele (dwa najbliższe wierzchołki dwóch różnych wielokątów). Jeśli są odpowiednio blisko to łączysz wielokąty w jeden: rozrywasz połączenia w tych dwóch punktach (w punktach! krawędzie zostają jakie były) i prowadzisz dwie nowe krawędzie pomiędzy tymi punktami. Tylko te nowe krawędzie nie mogą być tak dobrane, że obwiednia się będzie "krzyżować". Np jeśli mamy takie fragmenty plam:
-----A A'-----
\ /
B B'
/ \
-----C C'`code>punkty B i B' to są te najbliższe 2 punkty które będziemy łączyć. Nowa obwiednia (już jeden wielokąt):`
-----A A'-----
\ /
B===B'
/ \
-----C C'-----
nowa obwiednia idzie kolejno przez punkty: ..., A, B, B', A', ..., C', B', B, C, ...
ale nie może iść tak: ..., A, B, B', C', ..., A', B', B, C, ... bo będzie się krzyżować.
Jak to wykonać:
- wyznaczasz kąty B'BA, B'BC oraz BB'A', BB'C'
- łączysz wzdłuż mniejszego kąta z większym oraz większego z mniejszym, czyli B'BA jest mniejszy od B'BC, a BB'A' jest większy BB'C' dlatego łączysz ABB'A' oraz CBB'C'
Tu się kryje jeszcze jedna pułapka. Jeśli A==C to zamiast nich bierzesz poprzednie czyli poprzednik A oraz D. Jeśli one są równe to znowu poprzednie i tak aż do skutku. Jeśli wszystkie będą parami równe (czyli plama to linia) to wszystko jedno jak połączysz wielokąty.
- Trudne - usuwasz z tego wielokąta wierzchołki będące wierzchołkami kątów wklęsłych aż pozbędziesz się wszystkich takich kątów. Nowo powstałe brzegi wielokąta to twoje czerwone linie. Jak to zrobić:
Bierzesz dowolne dwa sąsiednie wierzchołki (nazwijmy A i B). Wyznaczasz dwusieczną odcinka ich łączącego. Wyznaczasz wszystkie punkty przecięcia tej dwusiecznej z innymi krawędziami wielokąta. Wybierasz tą stronę dwusiecznej po której leży parzysta ilość przecięć. Jest to strona zewnętrzna wielokąta.
I tu znowu kryje się podobna pułapka jak poprzednio. Nie wybieraj pary sąsiednich wierzchołków miedzy którymi są dwie krawędzie. Natomiast jeśli nie ma innej pary (plama to linia) wtedy wszystko jedno jaki kierunek wybierzesz.
Dalej już wiesz po której stronie są kąty zewnętrzne/wewnętrzne. Przykładowo jeśli idąc od A do B zewnętrze jest po prawej stronie to wędrujesz po wierzchołkach w kierunku A, B, C itd:
liczysz kąt ABC (gdzie B i C to kolejne wierzchołki w obranym kierunku), jeśli mniejszy niż 180* usuwasz wierzchołek kąta, jeśli nie to przechodzisz do następnego punktu. I tak w kółko aż przejdziesz dookoła. Jeśli byś szedł w przeciwnym kierunku trzeba sprawdzać odwrotnie, czy kąty są większe niż 180*.
Kąty 0*/360* to zawsze kąty wypukłe, nie przyrównuj ich do 180*. Nawet ich nie licz, sprawdź czy ramiona kąta się pokrywają.
Oczywiście wszędzie jest mowa o kątach lewoskrętnych.
Punkty 1 i 2 można połączyć.
Kąt ABC jest większy od 180* jeśli (Ax-Bx)(Cy-By)<(Ay-By)(Cx-Bx), dzięki tej zależności można działać na liczbach całkowitych! Chyba wszystkie obliczenia da się przeprowadzić na liczbach całkowitych, korzystaj z iloczynu skalarnego i wektorowego, własności funkcji trygonometrycznych.
Same proste rzeczy, w tym przepisie nie ma przynajmniej żadnych abstrakcji ;)