Search and Intersection Schnitt zweier Segmente Schnitt von Segment und Dreieck Punkt im Polygon Schnitt konvexer Polygone Schnitt zweier Segmente - Grundlagen A a b Lab p(1/2) Segment Menge aller Punkte auf einer Geraden zwischen zwei Punkten a und b Darstellung eines Segmentes ? Sei A = b – a. Jeder Punkt auf Lab kann durch die Vektorsumme p(s) = a + sA dargestellt werden. Beispiele: p(0) = a p(1) = a + A = a + b – a = b p(1/2) = (a + b)/2 => p(s) für s [0,1] repräsentiert alle Punkte auf dem Segment ab Schnitt zweier Segmente - math. Umsetzung Gegeben seien nun zwei Segmente ab : p(s) = a + sA für s [0,1] cd : q(t) = c + tC für t [0,1] d a c b Schnittpunkt der Segmente Der Schnittpunkt der Segmente ab und cd wird durch diejenigen Werte von s und t spezifiziert, für die p(s) = q(t) gilt: a + sA = c + tC Diese Vektorgleichung beinhaltet zwei Gleichungen mit zwei Unbekannten, nämlich die x- und y-Gleichungen, mit jeweils s und t als Unbekannte. Als Lösung dieses Gleichungssystems ergibt sich: s = [ax(dy – cy) + cx(ay – dy) + dx(cy – ay)] / D t = – [ax(cy – by) + bx(ay – cy ) + cx(by – ay)] / D D = ax(dy – cy) + bx(cy – dy) + dx(by – ay) + cx(ay – by) D=0 Segmente sind parallel Schnitt zweier Segmente - Quellcode num = a[X] * (double) ( d[Y] – c[Y] ) + c[X] * (double) ( a[Y] – d[Y] ) + d[X] * (double) ( c[Y] – a[Y] ); s = num / denom; #define X 0 #define Y 1 typedef enum {FALSE, TRUE} bool; #define DIM 2 typedef int tPointi[DIM]; typedef double tPointd[DIM]; num = - ( a[X] * (double) ( c[Y] – b[Y] ) + b[X] * (double) ( a[Y] – c[Y] ) + c[X] * (double) ( b[Y] – a[Y] ) ); t = num / denom; bool SegSegInt( tPointi a, tPointi b, tPointi c, tPointi d, tPointd p ) { double s, t; double num, denom; p[X] = a[X] + s * ( b[X] – a[X] ); p[Y] = a[Y] + s * ( b[Y] – a[Y] ); denom = a[X] * (double) ( d[Y] – c[Y] ) + b[X] * (double) ( c[Y] – d[Y] ) + d[X] * (double) ( b[Y] – a[Y] ) + c[x] * (double) ( a[Y] – b[Y] ) ; if ( ( 0.0 <= s ) && ( s <= 1.0) && ( 0.0 <= t) && ( t <<= 1.0) ) return TRUE; else return FALSE; if ( denom == 0.0 ) return FALSE; } Schnitt zweier Segmente Schwächen des Quellcodes: 1. Bei parallelen Segmenten wird standardmäßig „FALSE“ zurückgegeben, obwohl auch diese sich schneiden (überlappen) können. 2. Da viele Anwendungen zwischen echtem („proper“) und entartetem („improper“) Schnitt unterscheiden müssen, wäre es sinnvoll, diese Information, z.B. codiert als Character-Wert, zurückzuliefern. Beispiel: „e“ : Segmente überlappen kollinear, teilen mindestens einen Punkt ( e = „edge“) „v“ : Endpunkt des einen Segmentes ist auf dem anderen Segment, aber „e“ trifft nicht zu ( v = „vertex“) „1“ : Schnitt ist „proper“; Segmente teilen einen Punkt und weder „e“, noch „v“ treffen zu „0“ : Segmente schneiden sich nicht Schnitt zweier Segmente – verbesserter Quellcode char SegSegInt( tPointi a, tPointi b, tPointi c, tPointi d, tPointd p ) { double s, t; double num, denom; char code; // denom bestimmen if ( denom == 0.0 ) return ParallelInt(a,b,c,d,p); Bei parallelen Segmenten: // num bestimmen für s if ( num == 0.0 || num == denom) code = ‘v‘; s = num / denom; Sonderbehandlung durch Funktion ParallelInt(…) // num bestimmen für t if ( num == 0.0 || num == denom) code = ‘v‘; t = num / denom; // p[X] und p[Y] bestimmen if ( ( 0.0 <= s ) && ( s <= 1.0) && ( 0.0 <= t) && ( t <<= 1.0) ) code = ‘1‘; else if ( ( 0.0 > s ) || ( s > 1.0) || ( 0.0 > t) || ( t > 1.0) ) code = ‘0‘; return code; } Anwendung: - Schnitt konvexer Polygone (später) Schnitt Segment / Dreieck - Grundlagen Anwendung: Ray – Tracing Gegeben sei ein Segment qr und ein Dreieck T = abc. 1. Schritt : Bestimmung des Schnittpunktes von qr mit der Ebene π, die T enthält. π N = (A,B,C) p = (x,y,z) Ebenengleichung: π : Ax + By + Cz = D bzw. als Punktprodukt geschrieben: π : (x, y ,z) * (A, B, C) = D N = (A, B, C) ist Normalenvektor der Ebene π. D/ |N| (0,0,0) Schnitt Segment / Dreieck – Schnitt Segment/Ebene Wie auch im Zweidimensionalen kann jeder Punkt p auf dem Segment qr durch die Gleichung p(t) = q + t * (r – q) ( t [0,1]) dargestellt werden. Gesucht ist nun ein Wert für den Parameter t, so dass p(t) auf der Ebene π liegt. Da jeder Punkt p = (x,y,z) auf π der Ebenengleichung (x,y,z) * (A,B,C) = D genügen muss, gilt: p (t ) ( A, B, C ) D p (t ) N D [ q t ( r q )] N D q N t (r q) N D t D qN (r q) N Gilt t [0,1], dann schneidet Das Segment qr die Ebene π. t eingesetzt in p(t) liefert die Koordinaten des Schnittpunktes von qr mit π Schnitt Segment / Dreieck – Projektion in 2D 2. Schritt : Liegt der Schnittpunkt p von Segment qr und Ebene π innerhalb von T = abc? Beobachtung : Eigentlich zweidimensionales Problem! Lösung : Projektion des Dreiecks abc und p auf zwei Dimensionen. r π p Projektion : Streichen einer Koordinate in T = abc und in p. Problem : Was, wenn π z.B. vertikal zur xy – Ebene liegt ? q Streichen derjenigen Koordinate, die im Normalenvektor N der Ebene π am größten ist. p‘ z y x Schnitt Segment / Dreieck – signed areas Es gilt : p liegt in T, falls der projizierte Punkt p‘ innerhalb des projizierten Dreiecks T‘ liegt. Wie stellt man die Lage von p‘ zu T‘ fest? => Berechnung von ‘signed areas‘ bezüglich p‘ : Deutung: +-- +-+ 2 --+ Alle 3 areas positiv oder negativ: p‘ ist streng innerhalb von T‘ 1 2 areas „0“: p‘ liegt auf Eckpunkt +++ ++- -++ 0 -+- 1 area „0“: p‘ liegt auf Kante, falls die beiden anderen areas die gleichen Vorzeichen haben Punkt im Polygon Gegeben ist ein fixes Polygon P und ein Punkt q. Dieser Abschnitt beschäftigt sich mit Möglichkeiten, herauszufinden, ob q in P liegt oder nicht. 2 Fälle: 1. Polygon konvex: LeftOn – Test für jede Kante des Polygons 2. Polygon nicht konvex: - Winding Number - Ray Crossings Anmerkung: Obwohl beide Algorithmen in O(n) arbeiten, ist die Methode der Ray Crossings bis zu 20mal schneller. Punkt im Polygon – Winding number p q P Idee: Man steht auf Punkt q und betrachtet Punkt p, der einmal auf dem Rand des Polygons entgegen dem Uhrzeigersinn entlangläuft, bis er wieder seine Ursprungsposition erreicht hat. Beobachtung: q ∉ P : totale Drehung 0 q P : totale Drehung 2π (Drehung gegen Uhrzeigersinn: positiv!) Punkt im Polygon – Winding number Winding Number: Die Winding Number von q bezüglich P ist die Anzahl der Umdrehungen die ein, auf dem Rand von P entlanglaufender Punkt p um q macht: Die totale Winkeldrehung geteilt durch 2π ( da man am Ende wieder die Ausgangsorientierung erreicht hat, ist die totale Winkeldrehung ein Vielfaches von 2π und also die Winding Number eine ganze Zahl ) Problem des Algorithmus: Abhängigkeit von Fließpunkt-Berechnungen ( speziell: trigonometrische Berech. ) => bis zu 20mal langsamer als Ray – Crossings – Algorithmus Punkt im Polygon – Ray Crossings 25 Idee: 16 20 15 21 22 18 q1 23 19 14 17 q2 Ist diese Zahl ungerade, liegt q innerhalb von P, sonst nicht. P 26 12 10 24 8 27 6 q3 9 Beispiele: q1 : 3 Schnitte => q1 P q2 : 2 Schnitte => q2 P 2 7 4 3 0 13 Ziehe von q aus einen Strahl in beliebige (z.B. x- ) Richtung und zähle die Schnitte mit dem Rand von P. 5 11 Aber: Betrachte q3! 1 Was, wenn der Strahl eine Ecke trifft, oder kollinear mit einer Kante verläuft? Punkt im Polygon – Ray Crossings Problem: Der von q ausgehende Strahl R trifft Ecke von P oder verläuft kollinear zu einer Kante von P. Beobachtung: Die meisten Schwierigkeiten kann man umgehen, indem man folgende Regel aufstellt: Damit eine Kante e von P als Schnitt zählt, muss folgendes gelten: 1. Einer von e‘s Endpunkten muss streng oberhalb von R liegen 2. Der zweite Endpunkt von e muss auf oder unterhalb von R liegen 3. Die x-Koordinate des Schnittpunktes muss echt größer als die von q sein. ( => keine Kante, die kollinear mit R verläuft kann als Schnitt zählen ) Punkt im Polygon – Ray Crossings Unter Beachtung der eben genannten Regel, ergibt sich nun folgende Situation: 25 16 20 15 22 21 23 18 14 13 Problem für Punkte gelöst, die echt innerhalb von P liegen. 19 17 P 12 26 8 10 24 27 6 2 7 5 3 1 Aber: Inkonsistente Behandlung für Punkte, die auf dem Rand von P liegen! 9 4 0 Dicke Kanten und schwarze Ecken gelten als „innerhalb von P“ , dünne Kanten und weiße Ecken Gelten als „außerhalb von P“. 11 ( P ist geschlossenes Gebilde => Punkte auf dem Rand von P sollen als „innerhalb“ gelten ) Punkt im Polygon – Ray Crossings Lösung: Ziehe von q ausgehend zusätzlich einen Strahl in (–x) – Richtung. Für Kanten e von P gelten hierbei umgekehrte Voraussetzungen um als Schnitt zu zählen: 1. Einer von e‘s Endpunkten muss streng unterhalb von R liegen 2. Der zweite Endpunkt von e muss auf oder oberhalb von R liegen 3. Die x-Koordinate des Schnittpunktes muss echt kleiner als die von q sein. Ergeben sich nun unterschiedliche Ergebnisse für die entgegengesetzten Strahlen, so gilt q P. Aber: Diese Methode funktioniert nur für Punkte, die innerhalb einer Kante von P liegen, nicht jedoch für die Ecken von P ! => Zu Beginn des Algorithmus einfachen Test durchführen, ob q Kante ist. Punkt im Polygon – Ray Crossings – Quellcode char InPoly( tPointi q, tPolygoni P, int n) { int i, i1; //point index int d; //dimension index double x; //x intersection of e with ray int RCross = 0; //number of right edge/ray crossings int LCross = 0; //number of left edge/ray crossings bool RStrad, LStrad; //flags indicating the edge strads //the x - axis //Shift so that q is the origin. Note this destroys polygon for( i = 0; i < n; i++ ) { for( d = 0; d < DIM; d++ ) P[i][d] = P[i][d] - q[d]; } //For each edge e = (i-1,i), see if crosses ray for( i = 0; i < n; i++) { //first check if q = (0,0) is vertex if( P[i][X] == 0 && P[i][y] == 0) return 'v'; i1 = ( i + n - 1) % n; //Check if e straddles x axis, with bias above/below RStrad = ( P[i][Y] > 0 ) != ( P[i1][Y] > 0 ); LStrad = ( P[i][Y] < 0 ) != ( P[i1][Y] < 0 ); if(RStrad || LStrad) { //Compute intersection of e with x - axis x = ( P[i][X] * P[i1][Y] - P[i1][X] * P[i][Y]) / (double)(P[i1][Y] - P[i][Y]); if (RStrad && x > 0 ) RCross ++; if (LStrad && x < 0 ) LCross ++; } } //q on an edge if L/RCross counts are not the same parity if( ( RCross % 2) != ( LCross % 2) ) return 'e'; //q inside iff an odd number of crossings if ( RCross % 2) == 1) return 'i'; else return 'o'; } ‘i‘ : q liegt streng innerhalb von P ‘o‘ : q liegt streng außerhalb von P ‘e‘ : q liegt auf Kante, aber ist kein Endpunkt ‘v‘ : q ist Ecke von P Punkt im Polyeder Methode: Ray Crossings Funktioniert im Prinzip wie in 2D, aber: keine Sonderbehandlung für Spezialfälle, sondern: Generierung eines Zufallsstrahls. Schnitt konvexer Polygone Der Schnitt zweier beliebiger Polygone mit m und n Ecken kann die quadratische Komplexität Ω(nm) haben : Beschränkt man sich jedoch auf konvexe Polygone, ergibt sich die lineare Komplexität O(n+m). Der erste lineare Algorithmus wurde 1978 von Shamos gefunden. Im Folgenden wird ein Algorithmus vorgestellt, der 1982 von O‘Rourke entwickelt wurde. Schnitt konvexer Polygone – Algorithmus von O‘Rourke Gegeben sind zwei konvexe Polygone, Q und P, deren Ränder gegen den Uhrzeigersinn orientiert sind. A und B sind gerichtete Kanten auf den Rändern von Q und P. Grundsätzliche Idee: A und B „jagen“ einander, d.h. sie durchlaufen die Kanten von Q und P gegen den Uhrzeigersinn und adjustieren ihre Geschwindigkeiten so, dass sie sich bei jedem Schnitt der Ränder von Q und P treffen: A B Q P Schnitt konvexer Polygone – Algorithmus von O‘Rourke Grundstruktur des Algorithmus: Algorithm: INTERSECTION OF CONVEX POLYGONS Choose A and B arbitrarily. repeat if A intersects B then Check for termination. Update an inside flag. Advance either A or B, depending on geometric conditions. until both A and B cycle their polygons. Handle P Q = and Q P and P Q cases. Schwierigkeit des Algorithmus liegt darin, Regeln für das Weitersetzen von A und B zu finden! Schnitt konvexer Polygone – Algorithmus von O‘Rourke Idee: Wenn B auf die Gerade „zielt“, die A enthält, diese aber nicht schneidet, dann wird B weitergesetzt, um näher an einen möglichen Schnittpunkt mit A zu gelangen. ( alle durchgezogenen Linien in der Graphik) Diese Situation kann folgendermaßen beschrieben werden: - Sei H(A) die offene Halbebene auf der linken Seite von A. - „A x B > 0“ soll bedeuten, dass die z-Koordinate des Kreuzproduktes > 0 ist. ( => die kürzeste Drehung von A nach B ist gegen den Uhrzeigersinn ) Schnitt konvexer Polygone – Algorithmus von O‘Rourke falls A x B > 0 und b H(A), oder falls A x B < 0 und b H(A), => B wird weitergesetzt falls A x B < 0 und a H(B), oder falls A x B > 0 und a H(B), => A wird weitergesetzt ( BxA= -Ax B) AxB >0 >0 <0 <0 Halfplane Condition b H(A) b H(A) a H(B) a H(B) Advance Rule A B . B A Schnitt konvexer Polygone – Details des Algorithmus Polygone Q und P sind Arrays von Eckpunkten, die über die Variablen a und b indiziert werden. a1 und b1 indizieren jeweils die Eckpunkte, die eine Position vor a und b liegen. a1 ist Anfangspunkt von A, a Endpunkt von A ( Entsprechendes für B ) a1 A b1 a 0 4 6 5 3 B 4 Q b P 5 0 2 1 3 2 1 Schnitt konvexer Polygone – Details des Algorithmus a1 A Am Anfang des Algorithmus ist das inside-flag auf „unknown“ gesetzt. b1 a 0 B b P 5 5 3 4 S Q Die Kanten A und B laufen nun solange weiter (entsprechend den geometrischen Vorraussetzungen), bis sie sich zum ersten Mal schneiden. 4 6 0 2 1 3 2 1 Dieser Schnittpunkt ist der erste Eckpunkt des Schnittpolygons S. Außerdem wird nun das inside-flag auf „Pin“ oder „Qin“ gesetzt. Solange nun „Qin“ gilt, gehören die Kanten von A zu S, bei „Pin“ die von B. Bei jedem Schnittpunkt von A und B kann sich der Wert des inside-flags ändern! Außerdem gehört jeder Schnittpunkt von A und B zu S. Schnitt konvexer Polygone – Details des Algorithmus Abbruchbedingung: Der Algorithmus ist fertig, wenn A und B ihre Polygone einmal umlaufen haben. Jedoch gibt es mögliche Fälle, in denen eine der beiden Kanten nie nie weitergesetzt wird. Die Hauptschleife des Algorithmus bricht ab, wenn: - A und B ihre Polygone einmal umlaufen haben, oder - eine der beiden Kanten ihr Polygon zweimal umlaufen hat Anmerkung: Ist die inside – Flag nach Beendigung der Hauptschleife noch immer auf „unknown“ gesetzt, weiß man, dass sich die Polygone nicht geschnitten haben! Schnitt konvexer Polygone – Algorithmus / Sonderfälle 1. A und B überlappen und sind entgegengesetzt orientiert => Die Kante der Überlappung ist der gesamte Schnitt der beiden Polygone P Q 2. A B A und B sind parallel ( A x B = 0) und a ist rechts von B, sowie b rechts von A => P Q = a P B Q A b Search and Intersection - Ausblick Weitere Themen, die unter „Search and Intersection“ fallen, aber aufgrund ihres Umfangs in diesem Vortrag nicht mehr behandelt werden können, sind z.B.: - der Schnitt von nicht-konvexen Polygonen - Extrempunkte von Polygonen - Extrempunkte von Polytopen - Lage eines Punktes in der Ebene