Grundlagen der Computergeometrie
Transcription
Grundlagen der Computergeometrie
Grundlagen der Computergeometrie Übung 3 1 Affinkombination von Punkten Gegeben sei ein Affiner Raum ππ = (π«π«, π±π±). Wir haben in der letzten Übung gelernt, dass man a) zwei Punkte subtrahieren kann. Das Ergebnis ist ein Vektor. b) einen Punkt und einen Vektor addieren kann. Das Ergebnis ist eine Verschiebung des Punktes. Beide Möglichkeiten kann man aus den Axiomen für den Affinen Raum herleiten. Weitere Operationen mit Punkten sind nicht möglich. Insbesondere kann man Punkte nicht addieren. In der Vorlesung haben Sie aber eine Operation hergeleitet, die einer Addition von Punkten sehr ähnlich sieht, nämlich die Affinkombination von Punkten. Dabei wird ein Punkt ππ β π«π« als gewichtete Summe der ππ Punkte ππ1 , β¦ , ππππ β π«π« dargestellt. Die Gewichte sind die reellen Zahlen πΌπΌ1 , β¦ , πΌπΌππ β β. ππ = πΌπΌ1 β ππ1 + β¦ + πΌπΌππ β ππππ Für die Gewichte gilt dabei immer, dass ihre Summe gleich 1 ist. πΌπΌ1 + β¦ + πΌπΌππ = 1 Für den Fall, dass ππ eine Affinkombination von nur zwei Punkten ist, gilt folgender Zusammenhang bei den Gewichten. πΌπΌ1 + πΌπΌ2 = 1 πΌπΌ1 = 1 β πΌπΌ2 Den Punkt ππ kann man dann wie folgt darstellen. ππ = (1 β πΌπΌ2 ) β ππ1 + πΌπΌ2 β ππ2 Diese Gleichung kann man so umstellen, dass ππ als Verschiebung des Punktes ππ1 um das πΌπΌ2 -fache des Vektors ππ2 β ππ1 dargestellt wird. ππ = 1 β ππ1 β πΌπΌ2 β ππ1 + πΌπΌ2 β ππ2 Aufgabe ππ = ππ1 + πΌπΌ2 β (ππ2 β ππ1 ) Gegeben seien die drei Punkte π΄π΄, π΅π΅, ππ β π«π«. Beschreiben Sie einen Algorithmus, der bestimmt, ob der οΏ½οΏ½οΏ½οΏ½ liegt. (3 Punkte) Punkt ππ auf der Strecke π΄π΄π΄π΄ Lösung Der folgende Algorithmus basiert darauf, dass wir den Punkt ππ zunächst als Affinkombination der Punkte π΄π΄ und π΅π΅ darstellen. ππ = πΌπΌ β π΄π΄ + π½π½ β π΅π΅ Die Summe der Gewichte ist 1. Wir können damit eines der Gewichte, nämlich πΌπΌ, eliminieren und den Punkt ππ als Verschiebung des Punktes π΄π΄ um das π½π½-fache des Vektors π΅π΅ β π΄π΄ darstellen. ππ = (1 β π½π½) β π΄π΄ + π½π½ β π΅π΅ ππ = 1 β π΄π΄ β π½π½ β π΄π΄ + π½π½ β π΅π΅ ππ = π΄π΄ + π½π½ β (π΅π΅ β π΄π΄) Im Hinblick auf die Aufgabenstellung gilt folgendes: β’ β’ β’ β’ Wenn π½π½ = 0 ist, dann ist ππ = π΄π΄. Wenn π½π½ = 1 ist, dann ist ππ = π΅π΅. Wenn 0 < π½π½ < 1 ist, dann liegt ππ genau zwischen π΄π΄ und π΅π΅. Wenn π½π½ < 0 oder π½π½ > 1 ist, dann liegt der Punkt nicht auf der Strecke οΏ½οΏ½οΏ½οΏ½ π΄π΄π΄π΄. Wir müssen also den Wert von π½π½ berechnen und prüfen, ob dieser im Intervall [0,1] liegt. Dazu betrachten wir zunächst die Gleichung für ππ. πππ₯π₯ π΄π΄π₯π₯ π΅π΅π₯π₯ β π΄π΄π₯π₯ οΏ½ππ οΏ½ = οΏ½π΄π΄ οΏ½ + π½π½ β οΏ½π΅π΅ β π΄π΄ οΏ½ π¦π¦ π¦π¦ π¦π¦ π¦π¦ Faktisch haben wir hier ein Gleichungssystem mit zwei Gleichungen und einer Unbekannten. Dieses gilt es zu lösen. Wie stellen die Gleichungen auf π½π½ um. π½π½1 = π½π½2 = πππ₯π₯ β π΄π΄π₯π₯ οΏ½π΅π΅ β π΄π΄ π₯π₯ π₯π₯ πππ¦π¦ β π΄π΄π¦π¦ οΏ½π΅π΅ β π΄π΄ π¦π¦ π¦π¦ Wenn π½π½1 β π½π½2 ist, dann haben wir den Fall, dass der Punkt ππ nicht auf der Geraden liegt, die durch π΄π΄ und π΅π΅ geht. Wenn aber π½π½ = π½π½1 = π½π½2 ist, dann liegt ππ auf dieser Geraden. Wenn π½π½ außerdem im Intervall [0,1] liegt, dann liegt ππ auch auf der Strecke οΏ½οΏ½οΏ½οΏ½ π΄π΄π΄π΄. 2 Baryzentrische Koordinaten Gegeben sei ein Affiner Raum ππ = (π«π«, π±π±). Die Dimension von ππ sei ππ. Gegeben seien weiterhin ππ + 1 Punkte ππ0 β¦ ππππ β π«π« und ππ + 1 Gewichte πΌπΌ0 β¦ πΌπΌππ β β mit πΌπΌ0 + β¦ + πΌπΌππ = 1. Der Punkt ππ β π«π« sei die folgende Affinkombination. ππ = πΌπΌ0 ππ0 + β¦ + πΌπΌππ ππππ Das Besondere dabei ist, dass der Punkt ππ im Bezug auf die Punkte ππ0 β¦ ππππ eindeutig durch die Gewichte πΌπΌ0 β¦ πΌπΌππ festgelegt ist. Letztere nennt man die baryzentrischen Koordinaten von ππ. Wichtig ist, dass die Summe der baryzentrischen Koordinaten gleich 1 ist und dass die Anzahl der Bezugspunkte genau um eins größer ist als die Dimension des Affinen Raums. Darüber hinaus müssen die Bezugspunkte paarweise verschieden sein. Die Menge der ππ + 1 Bezugspunkte nennt man einen ππ-Simplex. Da wir in der Ebene drei Bezugspunkte benötigen, beziehen sich die baryzentrischen Koordinaten eines Punktes in der Ebene immer auf ein bestimmtes Dreieck (2-Simplex) ππ0 ππ1 ππ2 . Entsprechend beziehen sich die baryzentrischen Koordinaten eines Punktes im Raum immer auf einen bestimmten Tetraeder (3-Simplex) ππ0 ππ1 ππ2 ππ3 . Auch Vektoren kann man mittels baryzentrischer Koordinaten darstellen. Seien π½π½0 β¦ π½π½ππ β β die baryzentrischen Koordinaten eines Vektors π£π£ β π±π± bezogen auf einen bestimmten ππ-Simplex. Dann ist die Summe der Koordinaten π½π½0 + β¦ + π½π½ππ gleich 0. Aufgabe 1 4 3 3 a) Gegeben seien die Punkte π΄π΄ = οΏ½ οΏ½, π΅π΅ = οΏ½ οΏ½, πΆπΆ = οΏ½ οΏ½ und ππ = οΏ½ οΏ½. Berechnen Sie die 2 1 7 3 baryzentrischen Koordinaten von ππ bezüglich des Dreiecks π΄π΄π΄π΄π΄π΄. (3 Punkte) b) Nennen Sie einen Nachteil baryzentrischer Koordinaten gegenüber kartesischen Koordinaten, insbesondere im Hinblick auf eine effiziente Implementierung. (1 Punkt) c) Implementieren Sie die Methode Triangle2.GetBarycentricCoordinates(). Starten Sie anschließend das Programm und evaluieren Sie, ob Ihre Implementierung korrekt ist. Lösung a) Für die Berechnung der baryzentrischen Koordinaten kann man die Cramersche Regel anwenden. πππ₯π₯ det οΏ½πππ¦π¦ 1 πππ΄π΄ = π΄π΄π₯π₯ det οΏ½π΄π΄π¦π¦ 1 π΄π΄π₯π₯ det οΏ½π΄π΄π¦π¦ 1 πππ΅π΅ = π΄π΄π₯π₯ det οΏ½π΄π΄π¦π¦ 1 π΄π΄π₯π₯ det οΏ½π΄π΄π¦π¦ 1 πππΆπΆ = π΄π΄π₯π₯ det οΏ½π΄π΄π¦π¦ 1 π΅π΅π₯π₯ π΅π΅π¦π¦ 1 π΅π΅π₯π₯ π΅π΅π¦π¦ 1 π΅π΅π₯π₯ πΆπΆπ₯π₯ 3 π΅π΅π¦π¦ πΆπΆπ¦π¦ οΏ½ det οΏ½3 1 1 1 = π΅π΅π₯π₯ πΆπΆπ₯π₯ 1 π΅π΅π¦π¦ πΆπΆπ¦π¦ οΏ½ det οΏ½2 1 1 1 πππ₯π₯ πππ¦π¦ 1 π΅π΅π₯π₯ π΅π΅π¦π¦ 1 πΆπΆπ₯π₯ 1 πΆπΆπ¦π¦ οΏ½ det οΏ½2 1 1 = πΆπΆπ₯π₯ 1 πΆπΆπ¦π¦ οΏ½ det οΏ½2 1 1 πππ₯π₯ 1 πππ¦π¦ οΏ½ det οΏ½2 1 1 = πΆπΆπ₯π₯ 1 πΆπΆπ¦π¦ οΏ½ det οΏ½2 1 1 Es gilt offensichtlich πππ΄π΄ + πππ΅π΅ + πππΆπΆ = 1. 4 1 1 4 1 1 4 1 1 4 1 1 3 3 1 4 1 1 3 7οΏ½ 1 = 4 3 17 7οΏ½ 1 3 7οΏ½ 1 = 8 3 17 7οΏ½ 1 3 3οΏ½ 1 = 1 β ππ β ππ = 5 π΄π΄ π΅π΅ 3 17 7οΏ½ 1 b) Die baryzentrischen Koordinaten eines Punktes oder Vektors im ππ-dimensionalen Raum bestehen aus ππ + 1 reellen Zahlen. Die kartesischen Koordinaten bestehen dagegen nur aus ππ reellen Zahlen. Letztere sind also theoretisch speichereffizienter. Andererseits reicht es aus, immer nur ππ baryzentrische Koordinaten zu speichern. Da die Summe aller Koordinaten stets 1 ist, kann die nicht gespeicherte Koordinate immer aus den gespeicherten ππ Koordinaten hergeleitet werden. c) Für die Evaluierung sollte der Mauszeiger nacheinander auf die Punkte π΄π΄, π΅π΅ und πΆπΆ geschoben werden. Die baryzentrischen Koordinaten des Punktes π΄π΄ sollten πππ΄π΄ = 1, πππ΅π΅ = 0 und πππΆπΆ = 0, die des Punktes π΅π΅ sollten πππ΄π΄ = 0, πππ΅π΅ = 1 und πππΆπΆ = 0 und die des Punktes πΆπΆ sollten πππ΄π΄ = 0, πππ΅π΅ = 0 und πππΆπΆ = 1 sein. Weiterhin sollten die baryzentrischen Koordinaten von Punkten innerhalb des Dreiecks alle größer als 0 sein. Nur bei Punkten außerhalb des Dreiecks sollten einzelne Koordinaten negativ sein. Bei Punkten auf dem Rand des Dreiecks, die keine Eckpunkte sind, ist stets eine Koordinate gleich 0. Außerdem sollte die Summe der baryzentrischen Koordinaten in allen Fällen stets gleich 1 sein. public struct Triangle2 { // ... /// <summary> /// Berechnet die baryzentrischen Koordinaten eines /// Punktes bezogen auf dieses Dreieck. /// </summary> /// <param name="P"> /// Der Punkt, dessen baryzentrische Koordinaten berechnet /// werden sollen. /// </param> /// <returns> /// Ein 3-Tupel mit den baryzentrischen Koordinaten von /// <paramref name="P"/> bezogen auf dieses Dreieck. /// </returns> public Tuple3 GetBarycentricCoordinates(Point2 P) { // Anwendung der Cramerschen Regel. // Determinante für die Nenner. double detN = A.X * (B.Y - C.Y) + B.X * (C.Y - A.Y) + C.X * (A.Y - B.Y); // Determinanten für die Zähler. double detA = P.X * (B.Y - C.Y) + B.X * (C.Y - P.Y) + C.X * (P.Y - B.Y); double detB = A.X * (P.Y - C.Y) + P.X * (C.Y - A.Y) + C.X * (A.Y - P.Y); // Baryzentrische Koordinaten. double l1 = detA / detN; double l2 = detB / detN; double l3 = 1.0 β l1 β l2; } } // Tupel erzeugen und zurückgeben. return new Tuple3(l1, l2, l3); 3 Punkttest für ebene Dreiecke Der Punkttest gehört zu den wichtigsten Problemstellungen in der Computergeometrie. Dabei geht es um die Frage, ob ein gegebener Punkt ππ auf einer Strecke, Kurve, Fläche oder in einem Dreieck, Viereck, Fünfeck liegt oder zu einer wie auch immer beschaffenen Menge von Punkten gehört. Für ein ebenes Dreieck π΄π΄π΄π΄π΄π΄ gibt es mehrere Möglichkeiten für den Punkttest. Eine Variante basiert auf den Eigenschaften baryzentrischer Koordinaten. Dazu werden die baryzentrischen Koordinaten des Punktes ππ bezüglich des Dreiecks π΄π΄π΄π΄π΄π΄ berechnet und geprüft, ob alle Koordinaten größer gleich 0 sind liegen. Wenn dem so ist, dann liegt der Punkt im Dreieck. οΏ½οΏ½οΏ½οΏ½οΏ½β, ππππ οΏ½οΏ½οΏ½οΏ½οΏ½β und β ππππ οΏ½οΏ½οΏ½οΏ½οΏ½β , ππππ οΏ½οΏ½οΏ½οΏ½οΏ½β. οΏ½οΏ½οΏ½οΏ½οΏ½β , β ππππ οΏ½οΏ½οΏ½οΏ½οΏ½β, ππππ In einer anderen Variante berechnet man die Summe der Winkel β ππππ Nur wenn diese Summe gleich 360° ist, liegt der Punkt ππ im Dreieck. Aufgabe a) Finden Sie heraus, welche der beiden genannten Varianten des Punkttests für ebene Dreiecke effizienter ist. Gehen Sie dabei von den folgenden Annahmen aus. (3 Punkte) β’ Der Vergleich zweier reeller Zahlen benötigt einen Rechentakt. β’ Die Addition (bzw. Subtraktion) zweier reeller Zahlen benötigt ebenfalls einen Rechentakt. β’ Die Multiplikation (bzw. Division) zweier reeller Zahlen benötigt fünf Rechentakte. β’ Alle sonstigen Funktionen (Quadratwurzel, Sinus, usw.) benötigen jeweils 20 Rechentakte. b) Gegeben sei ein ebenes, überschneidungsfreies Viereck π΄π΄π΄π΄π΄π΄π΄π΄. Beschreiben Sie kurz einen Algorithmus, der prüft, ob ein Punkt ππ in diesem Viereck liegt. (1 Punkt) c) Implementieren Sie die Methode Triangle2.Contains(). Lösung a) In der ersten Variante müssen β unter Anwendung der Cramerschen Regel β die baryzentrischen Koordinaten wie folgt berechnet werden: πππ₯π₯ det οΏ½πππ¦π¦ 1 πππ΄π΄ = π΄π΄π₯π₯ det οΏ½π΄π΄π¦π¦ 1 π΄π΄π₯π₯ det οΏ½π΄π΄π¦π¦ 1 πππ΅π΅ = π΄π΄π₯π₯ det οΏ½π΄π΄π¦π¦ 1 π΅π΅π₯π₯ π΅π΅π¦π¦ 1 π΅π΅π₯π₯ π΅π΅π¦π¦ 1 πππ₯π₯ πππ¦π¦ 1 π΅π΅π₯π₯ π΅π΅π¦π¦ 1 πΆπΆπ₯π₯ πΆπΆπ¦π¦ οΏ½ 1 πΆπΆπ₯π₯ πΆπΆπ¦π¦ οΏ½ 1 πΆπΆπ₯π₯ πΆπΆπ¦π¦ οΏ½ 1 πΆπΆπ₯π₯ πΆπΆπ¦π¦ οΏ½ 1 πππΆπΆ = 1 β πππ΄π΄ β πππ΅π΅ Es sind also zunächst drei verschiedene Determinanten von 3 × 3-Matrizen zu berechnen, deren letzte Zeile nur aus Einsen besteht. Beispielhaft sei das im Folgenden für die Determinante ausgeführt, die jeweils im Nenner steht. π΄π΄π₯π₯ det οΏ½π΄π΄π¦π¦ 1 π΅π΅π₯π₯ π΅π΅π¦π¦ 1 πΆπΆπ₯π₯ πΆπΆπ¦π¦ οΏ½ = π΄π΄π₯π₯ β οΏ½π΅π΅π¦π¦ β πΆπΆπ¦π¦ οΏ½ + π΅π΅π₯π₯ β οΏ½πΆπΆπ¦π¦ β π΄π΄π¦π¦ οΏ½ + πΆπΆπ₯π₯ β οΏ½π΄π΄π¦π¦ β π΅π΅π¦π¦ οΏ½ 1 Hier haben wir 3 Multiplikationen und 5 Additionen. Wir benötigen also pro Determinante 20 Rechentakte. Bei drei Determinanten macht das zusammen 60 Rechentakte. Für die Berechnung der ersten zwei baryzentrischen Koordinaten sind außerdem noch zwei Divisionen nötig. Diese schlagen mit weiteren 2 β 5 = 10 Rechentakten zu Buche, macht zusammen 70 Rechentakte. Die Berechnung der dritten baryzentrischen Koordinate erfordert zwei Additionen, folglich zwei weitere Rechentakte. Macht zusammen 72 Rechentakte. Für alle drei Koordinaten muss zum Schluss geprüft werden, ob sie jeweils größer als 0 sind. Das sind zusätzlich 3 Vergleiche bzw. 3 Rechentakte. Für die erste Variante des Punkttests sind also insgesamt 75 Rechentakte nötig. οΏ½οΏ½οΏ½οΏ½οΏ½β, ππππ οΏ½οΏ½οΏ½οΏ½οΏ½β berechnen. Das sind οΏ½οΏ½οΏ½οΏ½οΏ½β und ππππ Bei der zweiten Variante müssen wir zunächst die Vektoren ππππ insgesamt 6 Additionen bzw. 6 Rechentakte. Anschließend berechnen wir die Längen der drei Vektoren. Für jede Länge sind 2 Multiplikationen, eine Addition und eine Wurzel, also 31 Rechentakte nötig. Die Berechnung aller drei Längen erfordert also 93 Rechentakte. Danach οΏ½ , ππππ οΏ½ . Pro Vektor sind das 2 οΏ½ und ππππ berechnen wir die Normierungen der Vektoren. Diese seien ππππ Multiplikationen bzw. 10 Rechentakte. Die Normierung aller Vektoren erfordert also 30 Rechentakte. Zusammengefasst benötigen wir bisher schon 129 Rechentakte für die Berechnung und Normalisierung dreier Vektoren. Im nächsten Schritt müssen die drei Winkel berechnet werden. Für jeden Winkel ist der Arkuskosinus aus dem Skalarprodukt zweier normierter Vektoren zu berechnen. οΏ½ , ππππ οΏ½ βͺοΏ½ πΎπΎ = acosοΏ½β©ππππ Das sind zwei Multiplikationen (10 Rechentakte), eine Addition (1 Rechentakt) und ein Arkuskosinus (20 Rechentakte) pro Winkel, zusammen also 31 Rechentakte pro Winkel und insgesamt 93 Rechentakte für drei Winkel. In der Zwischensumme liegen wir jetzt bei 222 Rechentakten. Die Berechnung der Summe der Winkel erfordert zwei weitere Rechentakte und der Vergleich der Summe mit dem Vollwinkel einen weiteren Rechentakt. Wir benötigen für die zweite Variante also insgesamt 225 Rechentakte. Zusammenfassend ist die erste Variante deutlich effizienter. b) Die einfachste Möglichkeit ist die Zerlegung des Vierecks in zwei Dreiecke. Wenn der Punkt in einem der beiden Dreieck liegt (oder auf dem Rand der gemeinsamen Seite beider Dreiecke), dann liegt er auch im Viereck. Wir reduzieren also den Punkttest für ein Viereck auf zwei Punkttests für Dreiecke. c) public struct Triangle2 { // ... /// <summary> /// Prüft, ob dieses Dreieck einen bestimmten Punkt enthält. /// </summary> /// <param name="P">Der Punkt.</param> /// <returns> /// <see langword="true"/>, falls der Punkt <paramref name="P"/> /// in diesem Dreieck liegt, sonst <see langword="false"/>. /// </returns> public bool Contains(Point2 P) { // baryzentrische Koordinaten berechnen. Tuple3 Bary = GetBarycentricCoordinates(P); // if if if } } Koordinaten müssen alle größer gleich 0 sein. (Bary.A1 < 0) return false; (Bary.A2 < 0) return false; (Bary.A3 < 0) return false; // Punkt liegt im Dreieck. return true; // ...