Disclaimer: Diese Mitschrift ist als Ergänzung zur Vorlesung gedacht. Wir erheben keinen Anspruch auf Vollständigkeit und Korrektheit. Wir sind jederzeit froh um Hinweise zu Fehlern oder Unklarheiten an grafdan@ethz.ch.
Wenn Sie an dieser Mitschrift mitarbeiten möchten, melden Sie sich hier.
Letztes Mal haben wir zwei Dinge gesehen:
Heute wollen wir zwei Dinge tun:
Wir betrachten nun beliebige Liniensegmente, jeweils definiert durch die beiden Endpunkte
Allgemeine Lage: Wir wollen alle Randfälle vermeiden. Wir nehmen dazu an, dass die Liniensegmente in allgemeiner Lage sind, was so viel heisst, wie dass keine zwei Linien parallel sind und dass kein Segment senkrecht steht. Ferner seien alle
Zuerst wollen wir nur entscheiden ob es einen Schnitt gibt: Dazu lassen wir eine Scanlinie von links nach rechts auf der obigen Skizze laufen. Sobald wir einen Schnittpunkt finden brechen wir alles ab. Wir wollen also (noch) nicht sämtliche Schnittpunkte finden.
Für eine bestimmte Scanlineposition gibt es wie letztes Mal schlafende, aktive und tote Segemente. Betrachten wir die aktiven Segement entlang der Scanline von unten nach oben: Wir sagen zwei Segmente sind benachbart, wenn sie in dieser Reihenfolge aufeinanderfolgend auftreten.
Für zwei sich schneidende Segmente gibt es nun einen
Wenn wir die Scanlinie ganz wenig nach rechts verschieben, was passiert dann mit den Nachbarschaften? Die
Wir testen also alle benachbarten aktiven Segmente und finden so einen allfälligen Schnittpunkt. Sobald es beim Weiterführen der Scanline neue Nachbarschaften gibt, prüfen wir die neuen Nachbarpaare auch. Also alles was jemals benachbart wird, prüfen wir.
Wo ändert sich etwas? An den Anfangs- und Endpunkten der Segmente. Das sind also unsere Haltepunkte für die Scanline. Wenn wir die aktiven Segmente nach
Anfangshaltepunkte: Einfügen
Die grünen Pfeile markieren die getesteten, neu benachbarten Segmentpaare.
Endhaltepunkte: Entfernen
Wie halten wir die aktiven Segmente sortiert? Müssen wir die Segmente umsortieren, wenn die Scanline vorwärts bewegt wird? Wir beobachten: liegt Segment
Wir verwenden einen AVL-Baum als Scanline-Datenstruktur, also zur Verwaltung der aktiven Segmente. In den Schlüsseln des AVL-Baums speichern wir die komplette Information zu den Segmenten, also die Endpunkte und Geradengleichungen. Damit können wir Schlüsselvergleiche als "Liegt Segment
Die Schnittests beim Einfügen funktionieren dann so: Wir fügen das neue Segment in den AVL-Baum ein und testen es dann mit seinem symmetrischen Vorgänger und symmetrischen Nachfolger.
Beim Entfernen testen wir zuerst die symmetrischen Vorgänger und symmetrischen Nachfolger (da sie neu benachbart sein werden) und entfernen dann das Segment aus dem AVL-Baum.
Beachte: Unser Algorithmus findet die Schnittpunkte nicht in der Reihenfolge von links nach rechts. Es kann auch sein, dass wir ein und denselben Schnittpunkt mehrfach entdecken.
Wir betrachten
Für jeden Anfangspunkt führen wir eine Einfügeoperation in einen AVL-Baum und zwei Schnitt-Tests durch. Für jeden Endpunkt braucht es eine Entferneoperation auf dem AVL-Baum und einen Schnitt-Test. Da die Vergleiche innerhalb des AVL-Baumes nur konstante Zeit brauchen, kostet jeder Haltepunkt also nur Zeit
Insgesamt sind wir also bei der Laufzeit
Was tun wir nun, wenn wir sämtliche Schnittpunkte ausgeben wollen? Dann können wir nicht einfach abbrechen beim ersten gefundenen Schnittpunkt.
Streicht die Scanline über einen Schnittpunkt, so sind die beiden sich kreuzenden Segemente benachbart in der Scanlinereihenfolge, sowohl unmittelbar vor als auch unmittelbar nach dem Schnittpunkt. Es ändert sich also nur die Reihenfolge der beiden sich schneidenden Segmente. Ein solches Tauschen von Nachbarn können wir auch leicht in unserem AVL-Baum implementieren.
Detektieren wir einen Schnittpunkt, so müssen wir uns diesen nicht nur für die Ausgabe merken, sondern auch einen neuen Haltepunkt bei diesem Schnittpunkt einplanen. Denn die beiden Segmente tauschen ihre Reihenfolge ja noch nicht gleich beim Haltepunkt, wo wir den Schnitt detektieren, sondern erst später, dann wenn die Scanline den Schnittpunkt überstreicht.
Wie merken wir uns so eine dynamische Menge von Haltepunkten? Zuerst haben wir nur die
Was tun wir nun an den Haltepunkten?
Anfang eines Segmentes (linkes Ende)
Ende eines Segmentes (rechtes Ende)
Kreuzungspunkt
In der Praxis gilt es die vielen Spezialfälle, die wir mit unserer allgemeinen Lage unter den Teppich gekehrt haben, genau zu analysieren. Software-Libraries für Geometrie tun dies und müssen sich neben den degenerierten Fällen auch mit der Präzision der Berechnungen herumschlagen.[1]
Sei
Ist das schnell? Erwarten wir nur wenige Schnittpunkte, dann sind wir damit sicher sehr zufrieden. Doch maximal kann es
Nun wollen wir noch ein weiteres Beispiel anschauen - auch um zu demonstrieren wie allgemein und mächtig diese induktive Herangehensweise für Geometrieprobleme ist. Erneut werden wir dabei sehen, dass die recht spezifischen Datenstrukturen, wie der AVL-Baum, die wir im Verlaufe des Semesters entworfen haben, in solchen Aufgaben extrem nützliche Anwendungen finden.
Im Chipdesign sind nicht nur die Leiterbahnen kritisch, sondern auf einer Platine werden unzählige Komponenten verbaut und wir wollen sicherstellen, dass wir den selben Platinenbereich nicht für mehrere Komponenten eingeplant haben. Vereinfacht kann jede Komponente als ein achsenparalleles Rechteck dargestellt werden.
Wir nehmen uns zuerst wieder dem Detection Problem an: Gegeben
Auch hier können wir eine Scanline von links nach rechts fahren lassen. Für zwei sich schneidende Rechtecke gibt es immer einen linkesten Punkt im Schnittbereich. Dies entspricht der
Als Haltepunkte nehme wir also die linken und rechten Rand-$-x-Koordinaten der Rechtecke. Für alle aktiven Rechtecke müssen wir uns ihren
Bei den Haltepunkten machen wir folgendes: Bei einem linken Ende führen wir eine Überlappungsanfrage durch. Überlappt sich das
Nun bleibt nur noch zu klären wie wir eine Datenstruktur bauen können, welche diese Intervall-Überlappungsanfragen unterstützt. Schauen wir uns die Intervall entlang der Scanline an. Wir drehen gedanklich die Scanline um 90 Grad und legen sie waagrecht vor uns hin:
Jedes Interval wird beschrieben durch seine beiden Grenzen
Betrachten wir die Intervalle als Punkte im zweidimensionalen Raum (eine Dimension für die linke Grenze, eine Dimension für die rechte), so entspricht die Schnittmenge mit einem Anfrageinterval einer Bereichsanfrage wie folgt:
Wie wir eine Datenstruktur entwerfen, welche diese Bereichsanfragen effizient unterstützt, werden wir morgen sehen.