2009-10-06 10 views
29

Questo sembra non banale (viene chiesto molto sui vari forum), ma ho assolutamente bisogno di questo come un elemento fondamentale per un algoritmo più complesso.Come intersecare due poligoni?

Ingresso: 2 poligoni (A e B) in 2D, indicati come un elenco di spigoli [(x0, y0, x1, y2), ...] ciascuno. I punti sono rappresentati da coppie di double s. Non so se sono dati in senso orario, antiorario o in qualsiasi direzione. I do sanno che non sono necessariamente convessi.

Uscita: 3 poligoni che rappresentano A, B e il poligono intersecante AB. Uno dei due può essere un poligono vuoto (?), Ad es. null.

Suggerimento per l'ottimizzazione: questi poligoni rappresentano i limiti del vano e del pavimento. Quindi il confine della stanza normalmente si intersecherà completamente con il confine del piano, a meno che non appartenga a un altro piano sullo stesso piano (argh!).

Sto sperando che qualcuno abbia già fatto ciò in C# e mi consentirà di usare la sua strategia/codice, poiché ciò che ho trovato finora su questo problema è piuttosto scoraggiante.

EDIT: Quindi sembra che io non sia interamente pollo per svenimento alla prospettiva di farlo. Vorrei ribadire l'uscita desiderata qui, in quanto questo è un caso speciale e potrebbe rendere semplice calcolo:

uscita: primo poligono meno tutti i bit intersecano, poligoni intersezione (plurale è ok). Non mi interessa davvero il secondo poligono, solo la sua intersezione con il primo.

EDIT2: Attualmente sto usando la libreria GPC (General Polygon Clipper) che lo rende veramente facile!

+2

Questo è più complicato di quanto si possa pensare. Come pensi di rappresentare la forma risultante? Ricordare che i due ingressi potrebbero (a) non intersecarsi affatto, (b) intersecarsi parzialmente, risultando in una forma convessa o concava chiusa, (c) intersecarsi completamente, risultando in una forma con DUE contorni (cioè, ciambella) dove devi trovare un modo per rappresentare l'interno e l'esterno della forma. –

+3

Infatti, Jon ha ragione. Il tuo problema non è ben definito; il set risultante * potrebbe non essere un poligono *, e quindi l'output della funzione non dovrebbe essere un poligono. Cosa vuoi fare nel caso in cui la forma risultante non sia connessa? Immaginate ad esempio un polis a forma di C che interseca un poli a forma di I, risultante in due punti. –

+0

grazie, dovremo pensarci su e trovare una soluzione. Non ci avevo pensato prima ... –

risposta

10

quello che penso si dovrebbe fare

Non tentare di farlo da soli, se si può forse farne a meno. Invece, utilizzare uno dei molti algoritmi di intersezione di poligono disponibili già esistenti.

Stavo considerando seriamente il seguente codebase sulla forza del loro codice dimostrativo e sul fatto che hanno menzionato la loro gestione della maggior parte/tutti i casi strani. Dovresti donare un importo (di tua scelta/della tua azienda) se lo usi commercialmente, ma ne vale la pena per ottenere una versione robusta di questo tipo di codice.

http://www.cs.man.ac.uk/~toby/gpc/

Quello che ho fatto è stato in realtà di utilizzare un algoritmo poligono di intersezione che fa parte delle librerie Java2D. È possibile trovare qualcosa di simile nelle librerie C# di MS da utilizzare.

Ci sono anche altre opzioni; cercare "poligono clipper" o "poligono di ritaglio", poiché gli stessi algoritmi di base che gestiscono l'intersezione del poligono tendono anche a essere utilizzabili per i casi di clipping generali.

Una volta che si dispone di una libreria di ritaglio poligonale, è sufficiente sottrarre il poligono B dal poligono A per ottenere il primo pezzo di output e intersecare i poligoni A e B per ottenere il secondo pezzo di output.

Come a rotolare il proprio, per il irrimediabilmente masochista

Quando mi stava prendendo in considerazione rotazione mia, ho trovato l'algoritmo di Weiler-Atherton ad avere le maggiori potenzialità di generale poligono-taglio. Ho usato il seguente come riferimento:

http://cs1.bradley.edu/public/jcm/weileratherton.html

http://en.wikipedia.org/wiki/Weiler-Atherton

I dettagli, come si dice, sono troppo densi per includere qui, ma non ho dubbi che sarete in grado di trovare i riferimenti su Weiler-Atherton per gli anni a venire. Essenzialmente, dividi tutti i punti in quelli che stanno entrando nel poligono finale o uscendo dal poligono finale, quindi formi un grafico da tutti i punti, e poi percorri il grafico nelle direzioni appropriate per estrarre tutti i pezzi di poligono che volere. Cambiando il modo in cui definite e trattate i poligoni "in entrata" e "in uscita", potete ottenere diverse intersezioni di poligoni (AND, OR, XOR, ecc.).

In realtà è abbastanza implementabile, ma come con qualsiasi codice di geometria computazionale, il diavolo si trova nelle degenerazioni.

+0

ha appena rivisitato questo. ho finito per usare la libreria gpc (link in alto). è fantastico. –

17

La libreria di Arash Partow FastGEO contiene implementazioni di molti algoritmi interessanti nella geometria computazionale. L'intersezione del poligono è uno di questi. È scritto in Pascal, ma implementa solo la matematica, quindi è abbastanza leggibile. Si noti che sarà necessario pre-elaborare i bordi un po ', per farli in senso orario o antiorario.

ETA: Ma davvero, il modo migliore per farlo è non farlo. Trova un altro modo per affrontare il problema che non coinvolge le intersezioni poligonali arbitrarie.

+7

Se fai questo, stai estremamente attento e non cambiare qualsiasi cosa dell'algoritmo dal codice originale. Se possibile, prendi un compilatore Pascal-to-C o compilarlo in una libreria che puoi usare, piuttosto che provare a tradurre il codice da solo. – jprete

2

Si potrebbe anche voler dare un'occhiata allo NetTopologySuite o anche provare a importarlo nel server SQL 2008 & è strumenti spaziali.

+0

+1 per questo. Stavo cercando una porta .NET di JTS, e sembra che sia adatta al progetto. –

2

Un poligono è descritto da una ordinata lista di punti (P1, P2, ..., Pn). I bordi sono (P1 - P2), (P2 - P3), ..., (Pn - P1). Se si hanno due poligoni A e B che si sovrappongono, ci sarà un punto An (dall'elenco sui punti che descrivono il poligono A) che si trova all'interno dell'area circondata dal poligono B o viceversa (un punto di B si trova in A). Se non viene rilevato alcun punto, i poligoni non si sovrappongono. Se un tale punto viene trovato (cioè Ai) controlla i punti adiacenti del poligono A (i-1) e A (i + 1). Ripeti finché non trovi un punto al di fuori dell'area o tutti i punti sono controllati (quindi il primo poligono giace completamente all'interno del secondo poligono). Se hai trovato un punto all'esterno, puoi calcolare il punto di attraversamento. Trova il bordo corrispondente del poligono B e seguilo con i ruoli resersed = ora verifica se i punti del poligono B si trovano all'interno del poligono A. In questo modo puoi creare un elenco di punti che descrivono il poligono sovrapposto. Se necessario, è necessario verificare se i poligoni sono identici (P1, P2, P3) === (P2, P3, P1).

Questa è solo un'idea e forse ci sono modi migliori. Se si trova un lavoro e di soluzione testata mi sento di raccomandare che lo si utilizza ...

narozed

2

tenta di utilizzare strumenti GIS per questo, come ArcObjects, TopologySuite, GEOS, OGR, etc.Non sono sicuro che tutte queste distribuzioni siano disponibili su .net, ma tutte fanno lo stesso.

+1

FYI, OGR (da GDAL/OGR) utilizza la libreria GEOS (http://trac.osgeo.org/geos/), quindi non vi è alcuna differenza riguardo all'implementazione della geometria computazionale tra questi due. – mloskot

+0

humn, buono a sapersi :) –

11

Se si sta programmando in .NET Framework, si consiglia di dare un'occhiata a classe SqlGeometry disponibile in assembly .NET spediti come Microsoft SQL Server System CLR Types

La classe SqlGeometry fornisce STIntersection metodo

SqlGeometry g1 = SqlGeometry.Parse("POLYGON ((...))"); 
SqlGeometry g2 = SqlGeometry.Parse("POLYGON ((...))"); 
SqlGeometry intersection = g1.STIntersection(g2); 
+3

Questa risposta è errata, quindi ha ottenuto un downvote? Se lo è, si prega di indicare dove si trova un bug. – mloskot

+1

sono d'accordo! cosa c'è di sbagliato in questa soluzione? Personalmente faccio tutte le mie cose spaziali nel DB ... ma se hai bisogno di farlo nel codice, usa lo stesso codice :) –

2

Clipper è un open source freeware libreria di ritaglio poligonale (scritta in Delphi e C++) che fa esattamente quello che stai chiedendo - http://sourceforge.net/projects/polyclipping/

Nel mio test, Clipper è molto più veloce e molto meno soggetto a errori rispetto a GPC (vedere i confronti più dettagliati qui - http://www.angusj.com/delphi/clipper.php#features). Inoltre, mentre il codice sorgente è disponibile sia per Delphi che per C++, la libreria Clipper include anche una DLL compilata per semplificare l'uso delle funzioni di ritaglio anche in altre lingue (Windows).