Szakaszok és sokszögek metszéspontjának meghatározása

Egyenes szakaszok vagy sokszögek metszéspontjának meghatározása nem csupán egy iskolai matematikai feladvány, hanem számos gyakorlati esetben lehet szükség erre. A számítógépes grafikában például minden ütközésvizsgálat – legyen szó egy játékban elrepülő lövedékről vagy egy karakter mozgásáról – ilyen számításokra épül. Térinformatikai rendszerekben is szükséges ellenőrizni, hogy egy tervezett útvonal keresztezi-e egy zóna határát. A robotikában a navigáció alapja, hogy a robot előre látja, metszi-e mozgási pályáját valamely akadály. A mérnöki és építészeti tervezésben is kiemelt szerepe van ennek: falak, vezetékek, szerkezeti elemek találkozási pontjait meg kell tudni határozni.

Ha két egyenes szakaszt grafikonon ábrázolunk, akkor vizuálisan könnyen megállapíthatjuk, hogy azok metszik-e egymást, és ha igen, akkor mely pontban. Ha programmal akarjuk ugyanezt meghatározni, ahhoz némi koordinátageometriai levezetés szükséges, aminek az eredményét utána megfelelő módon programkódban meg tudunk valósítani a metszéspont koordinátáinak kiszámításához. Ezt a levezetést láthatjuk a következő ábrán, ahol a metszéspontot végeredményben vektoriális szorzatok – pontosabban azok síkra merőleges z komponensei – segítségével fejezzük ki.

Minthogy a síkbeli vektorok és a komplex számok kölcsönösen megfeleltethetők, így két egyenes szakasz metszéspontját komplex számokkal is kifejezhetjük. Ennek levezetését az alábbi ábrán lehet követni.

A két előbb vázolt matematikai megközelítés szerint megvalósított függvények definícióit láthajuk alább. Az elvi ábrák és a kommentek segítik a működés megértését.

A komplex számokat használó megvalósítást nem csupán érdekességként mutatjuk. A szakaszok végpontjait általában int vagy float típusú számként adjuk meg, de elvben nem kizárt hogy az int mellett Fraction vagy Decimal típust használjunk a törtértékek megadására. Ez utóbbi esetekben azonban jelentős futási idő megtakarítást tapasztalhatunk, ha a komplex számokra épülő függvényt alkalmazzuk. Erre vonatkozó teszteseteket mutatunk alább:

Minthogy a gyakorlatban a koordináták megadására többnyire int vagy float típusú számokat használunk, és ezekkel a vektoriális szorzatot alkalmazó függvény teljesít jobban, ezért alapértelmezetten ezzel fogunk a továbbiakban dolgozni.

Most tehát rendelkezünk olyan függvénnyel, amely két szakasz metszéspontjának koordinátáit adja vissza egy tuple objektumban. Ha nincs metszéspont vagy a szakaszok egy vonalba esnek, akkor az eredmény egy üres tuple.

Egy sokszög felfogható úgy, mint a csúcspontok által meghatározott egyenes szakaszok sorozata. Ha tehát azt akarjuk megtudni, hogy két sokszög hol, mely pontokban metszi egymást, akkor nem kell mást tenni, mint az egyik sokszög minden oldalának mint szakasznak a másik sokszög minden oldalával való metszéspontját vizsgálni. És, ha vannak metszéspontok, akkor azokat valamilyen módon sorban visszaadni.

Az ezt megvalósító függvényt láthatjuk alább:

Bár ez a megoldás O(n*m) időkomplexitású (ahol n és m a sokszögek éleinek száma), de ha a csúcspontok száma nem túl sok, akkor a futási idő általában elfogadható. Ha a csúcspontok száma nagyon sok, akkor az adott alkalmazáshoz illeszkedő, optimalizált algoritmus szükséges, amelyre vannak kidolgozott módszerek.

Mivel vizuálisan elég könnyen át tudjuk tekinteni, hogy két sokszög mely pontokban metszi egymást, ezért függvényeink tesztelésére egy GUI alkalmazást készítünk, amelynek programkódja a következő:

A tesztalkalmazás elindítása után megjelenő felületen két sokszöget rajzoltathatunk ki. Mindkettőt oly módon, hogy bal egérgomb kattintással kijelöljük a csúcspontokat. Ha a pontok megvannak, akkor a „Sokszög megrajzolás” gomb lenyomásával a sokszög megjelenik. Ha mindkét sokszög megvan, akkor a „Metszéspontok meghatározása” nyomógomb hatására a metszéspontok, ha vannak, megjelennek. Ha másik két sokszöget akarunk vizsgálni, akkor az „Új rajz” gombbal törölhetjük a felületet, és az előbb leírt módon készíthetünk két új sokszöget.

Példaként néhány eredménykép:

Ebben a bejegyzésben többször is használtunk iterálható objektumokat, iterátorokat és generátorokat, valamint az itertools modul iterátorait. Ezekről részleteiben, példákkal magyarázva a Python tudásépítés lépésről lépésre című e-könyvben a „Kérem a következőt! – iterálható objektumok”, a „Kifogyhatatlan sorozatlövők – generátorfüggvények” fejezetekben, valamint a „Készétel fogyasztás – a szabványos könyvtár moduljainak használata” fejezeten belül a „Speciális iterátorok” című alfejezetben lehet olvasni. A futási idő meghatározáshoz a „A programvégrehajtás felfüggesztése és a futási idő mérése” alfejezetet érdemes átnézni. A GUI alkalmazás fejlesztéséhez szükséges ismereteket pedig a „Grafikus felhasználói felület készítése” című fejezetből lehet elsajátítani.

Érdekel a Python tudásépítés lépésről lépésre az alapoktól az első asztali alkalmazásig című e-könyv.