Commit c6f23035 authored by Numanoglu's avatar Numanoglu
Browse files

sketchs and notes sweep

parent 3032c1f9
package de.hft.stuttgart.citydoctor2.healing.sweep;
import de.hft.stuttgart.citydoctor2.datastructure.Edge;
import de.hft.stuttgart.citydoctor2.datastructure.LinearRing;
import de.hft.stuttgart.citydoctor2.datastructure.Polygon;
import de.hft.stuttgart.citydoctor2.datastructure.Vertex;
/**
* Gerichtetes Vorkommen einer Kante in genau einem Ring.
* * C++-Semantik: `CoEdge ist nicht die Kante selbst, sondern die Kante
* innerhalb eines konkreten `Loop.
* Dadurch kann dieselbe geometrische Kante in zwei Flächen vorkommen,
* mit entgegengesetzter Richtung und mit je eigenen Vorgänger-/Nachfolgerbeziehungen.
* wir könnten diese Klasse als Sweep-Arbeitsobjekt verwenden:
* * - `edge: die aus CityDoctor abgeleitete Kante.
* * - `polygon/ring: das konkrete Ringvorkommen.
* * - `start/end: Richtung dieser CoEdge im Ring.
* * - `previous/next: lokale Ringverkettung.
* * - `partner: CoEdge derselben Kante in der Nachbarfläche.* */
public class CoEdge {
private Edge edge;
private Polygon polygon;
private LinearRing ring;
private Vertex start;
private Vertex end;
private CoEdge previous;
private CoEdge next;
private CoEdge partner;
public CoEdge(Edge edge, Polygon polygon, LinearRing ring, Vertex start, Vertex end) {
this.edge = edge;
this.polygon = polygon;
this.ring = ring;
this.start = start;
this.end = end;
}
public Edge getEdge() {
return edge;
}
public Polygon getPolygon() {
return polygon;
}
public LinearRing getRing() {
return ring;
}
public Vertex getStart() {
return start;
}
public Vertex getEnd() {
return end;
}
public CoEdge getPrevious() {
return previous;
}
public void setPrevious(CoEdge previous) {
this.previous = previous;
}
public CoEdge getNext() {
return next;
}
public void setNext(CoEdge next) {
this.next = next;
}
public CoEdge getPartner() {
return partner;
}
public void setPartner(CoEdge partner) {
this.partner = partner;
}
/**
* Platzhalter für spätere Topologieänderungen, wenn eine CoEdge nach einem
* Vertex-Split auf eine neue Kante oder neue Vertex-Kopie zeigen muss.
*/
void reset(Edge edge, Vertex start, Vertex end) {
this.edge = edge;
this.start = start;
this.end = end;
}
}
package de.hft.stuttgart.citydoctor2.healing.sweep;
import java.util.ArrayList;
import java.util.List;
import de.hft.stuttgart.citydoctor2.datastructure.LinearRing;
import de.hft.stuttgart.citydoctor2.datastructure.Polygon;
/**
* Arbeitsrepräsentation eines LinearRing als Folge gerichteter CoEdges.
*
* In CityDoctor ist der Ring eine Vertexliste.
* Für Fall 3 des Sweeps müssen wir aber wissen,
* welche gerichtete Kante vor und nach einem Vertex liegt.
*
* Darum bildet diese Klasse die C++-Loop-Idee ab, ohne das CityDoctor-Datenmodell
* dauerhaft zu ersetzen.
*/
public class CoEdgeRing {
private final Polygon polygon;
private final LinearRing ring;
private final List<CoEdge> coEdges = new ArrayList<>();
public CoEdgeRing(Polygon polygon, LinearRing ring) {
this.polygon = polygon;
this.ring = ring;
}
public Polygon getPolygon() {
return polygon;
}
public LinearRing getRing() {
return ring;
}
public List<CoEdge> getCoEdges() {
return coEdges;
}
/**
* Später: CoEdge 'vor' und 'nach' einem Vertex finden. in C++-Vgl:
* findPrevAndNextCoEdge(...).
*/
public List<CoEdge> findCoEdgesAtVertex() {
// TODO Parameter Vertex ergänzen, sobald die erste Fall-3-Operation gebaut wird.
return List.of();
}
}
package de.hft.stuttgart.citydoctor2.healing.sweep;
import java.util.ArrayList;
import java.util.List;
import de.hft.stuttgart.citydoctor2.datastructure.Edge;
import de.hft.stuttgart.citydoctor2.datastructure.Geometry;
import de.hft.stuttgart.citydoctor2.datastructure.Polygon;
import de.hft.stuttgart.citydoctor2.datastructure.Vertex;
/**
* Findet die abführenden Kanten eines Sweep-Punktes.
*
* C++-Vorbild ist getOtherEdges(vertex, face) aus BRepObject_Navigate. Dort
* bedeutet "other edge": Kante hängt am Vertex, ist aber nicht Teil der
* Sweep-Fläche.
*
* In Java sind Kanten aus den LinearRing-Punktlisten abgeleitete Metadaten.
* Darum nutzt diese Klasse die vorhandenen Methoden Geometry.getEdgesAdjacentTo
* und Edge.getAdjacentPolygons statt eine eigene Kantenliste dauerhaft zu
* speichern.
*/
public class OutgoingEdgeFinder {
/**
* Liefert alle Kanten, die am Vertex liegen, aber nicht zum Fehlerpolygon
* gehören.
*/
public List<Edge> findOutgoingEdges(Geometry geometry, Polygon sweepPolygon, Vertex vertex) {
List<Edge> outgoing = new ArrayList<>();
if (geometry == null || sweepPolygon == null || vertex == null || geometry.getEdges() == null) {
return outgoing;
}
for (Edge edge : geometry.getEdgesAdjacentTo(vertex)) {
if (!edge.getAdjacentPolygons().contains(sweepPolygon)) {
outgoing.add(edge);
}
}
return outgoing;
}
}
package de.hft.stuttgart.citydoctor2.healing.sweep;
import de.hft.stuttgart.citydoctor2.check.error.NonPlanarPolygonDistancePlaneError;
/**
* Orchestriert den Punkt-Sweep für ein nicht-planares Polygon.
*
* Diese Klasse soll später aus HealPlanarPolygonSweep aufgerufen werden. Sie ist
* bewusst nicht selbst der HealingMethod-Einstieg, damit die fachliche
* Sweep-Algorithmik getrennt von CityDoctor-Fehlerdispatch, Logging und
* Listener-Anbindung bleibt.
*
* Semantische Vorlage ist Piepereit Kapitel 7.2.3 sowie der C++-Code in
* HealingMethod_Planarity::sweepFace(...) beziehungsweise
* Method_AdvancedSweep::sweepFace(...).
*
* Geplanter Ablauf:
* - Zielebene für das Fehlerpolygon bestimmen.
* - Alle Punkte der Sweep-Fläche betrachten.
* - Punkte, die schon in der Zielebene liegen, überspringen.
* - Abführende Kanten bestimmen.
* - Fall 1: keine abführende Kante, orthogonal projizieren.
* - Fall 2: eine abführende Kante, entlang der Kante zur Zielebene sweepen.
* - Fall 3: mehrere abführende Kanten, Vertex topologisch aufteilen. -->3.1, 3.2, 3.3
*/
public class PlanarPolygonSweeper {
private final SweepTargetPlaneFactory targetPlaneFactory;
private final OutgoingEdgeFinder outgoingEdgeFinder;
private final SweepPointOperation pointOperation;
private final SweepDirectionClassifier directionClassifier;
private final TopologyEditor topologyEditor;
public PlanarPolygonSweeper() {
this(new SweepTargetPlaneFactory(), new OutgoingEdgeFinder(), new SweepPointOperation(),
new SweepDirectionClassifier(), new TopologyEditor());
}
PlanarPolygonSweeper(SweepTargetPlaneFactory targetPlaneFactory, OutgoingEdgeFinder outgoingEdgeFinder,
SweepPointOperation pointOperation, SweepDirectionClassifier directionClassifier,
TopologyEditor topologyEditor) {
this.targetPlaneFactory = targetPlaneFactory;
this.outgoingEdgeFinder = outgoingEdgeFinder;
this.pointOperation = pointOperation;
this.directionClassifier = directionClassifier;
this.topologyEditor = topologyEditor;
}
/**
* Platzhalter für die spätere Portierung.
*
* Hier sollen die Fälle aus Piepereit 7.2.3 dispatcht werden. Der aktuelle
* Methodenrumpf macht noch nichts, damit wir die Architektur gefahrlos
* einführen können.
*/
public boolean sweep(NonPlanarPolygonDistancePlaneError error) {
// TODO Sweep-Portierung:
// Plane targetPlane = targetPlaneFactory.createFor(error.getPolygon());
// for (Vertex vertex : sweepTopology.verticesOf(error.getPolygon())) { ... }
// List<Edge> outgoing = outgoingEdgeFinder.findOutgoingEdges(geometry, polygon, vertex);
// switch (outgoing.size()) { case 0 -> Fall 1; case 1 -> Fall 2; default -> Fall 3; }
return false;
}
SweepTargetPlaneFactory getTargetPlaneFactory() {
return targetPlaneFactory;
}
OutgoingEdgeFinder getOutgoingEdgeFinder() {
return outgoingEdgeFinder;
}
SweepPointOperation getPointOperation() {
return pointOperation;
}
SweepDirectionClassifier getDirectionClassifier() {
return directionClassifier;
}
TopologyEditor getTopologyEditor() {
return topologyEditor;
}
}
package de.hft.stuttgart.citydoctor2.healing.sweep;
import java.util.Optional;
import de.hft.stuttgart.citydoctor2.math.Line3d;
import de.hft.stuttgart.citydoctor2.math.Plane;
import de.hft.stuttgart.citydoctor2.math.Vector3d;
/**
* Mathematische Schnittoperationen
*
* In cpp verwendet er dafür freie Funktionen aus mathutils.h, vor allem
* plane_str_int(...) für Ebene-Gerade und plane_plane_int(...) für Ebene-Ebene.
*
* Im Java-Projekt existiert bisher Plane.intersects(Segment3d),
* also nur Segment-Ebene.
*
* Diese Klasse ist vorläuf. im Sweep-Paket, weil sie aus der Portierung heraus
* entsteht. Wenn sie sich bewährt, kann die Funktionalität nach citydoctor2.math
* wandern.
*/
public final class PlaneIntersections {
private static final double EPSILON = 0.0000001;
private PlaneIntersections() {
}
/**
* Schnitt einer unendlichen Gerade mit einer Ebene.
*
* Formel für Gerade p + t*d und Ebene n*x = D:
* t = (D - n*p) / (n*d).
*
*
* Vorläuf. mit Container'Optional' aus java.utils
*/
public static Optional<Vector3d> intersectLine(Plane plane, Line3d line) {
double denominator = plane.getNormal().dot(line.direction());
if (Math.abs(denominator) < EPSILON) {
return Optional.empty();
}
double t = (plane.getD() - plane.getNormal().dot(line.point())) / denominator;
return Optional.of(line.point().plus(line.direction().mult(t)));
}
/**
* Schnitt zweier Ebenen als Gerade.
*
* Wird später für Fall 3.2 benötigt: die neue Kante liegt auf der
* Schnittgeraden der zwei Nachbarflächen zur Sweep-Fläche.
*/
public static Optional<Line3d> intersectPlanes(Plane first, Plane second) {
// TODO plane_plane_int aus C++ semantisch nachbauen.
// Richtung wäre n1 x n2; ein Punkt auf der Gerade muss (num. robust!) berechnet werden.
return Optional.empty();
}
}
package de.hft.stuttgart.citydoctor2.healing.sweep;
/**
* Sweep-Richtung einer abführenden Kante bezogen auf die Zielebene.
*
* Die Benennung folgt Piepereit:
* - NEGATIVE: Kante wird beim Sweep verkürzt und kann kollabieren.
* - POSITIVE: Kante wird beim Sweep verlängert.
* - UNDEFINED: Sonderfall, etwa parallele Gerade zur Zielebene.
*/
public enum SweepDirection {
NEGATIVE,
POSITIVE,
UNDEFINED
}
package de.hft.stuttgart.citydoctor2.healing.sweep;
import java.util.EnumMap;
import java.util.List;
import java.util.Map;
import de.hft.stuttgart.citydoctor2.datastructure.Edge;
import de.hft.stuttgart.citydoctor2.datastructure.Vertex;
import de.hft.stuttgart.citydoctor2.math.Line3d;
import de.hft.stuttgart.citydoctor2.math.Plane;
import de.hft.stuttgart.citydoctor2.math.Vector3d;
/**
* Klassifiziert abführende Kanten in positive und negative Sweep-Richtung.
*
* C++-Vorbild ist getVertexComplexity(...). Dort wird pro abführender Kante die
* Trägergerade mit der Zielebene geschnitten. Dann wird verglichen:
*
* dist1 = Abstand(otherVertex, Schnittpunkt)
* dist2 = Abstand(otherVertex, alter Sweep-Punkt)
*
* Wenn dist1 kleiner als dist2 ist, hat die Kante negative Richtung und wird
* verkürzt. Sonst hat sie positive Richtung und wird verlängert.
*
* Aus den Zählungen entstehen später die Fälle:
* - nur negative Kanten: Split-Cut, Punkt vervielfältigen.
* - mindestens zwei positive Kanten: Split-Pull, Punkt duplizieren.
* - gemischt: erst trennen, dann rekursiv weiterbehandeln.
*/
public class SweepDirectionClassifier {
public SweepDirection classify(Vertex sweepVertex, Edge outgoingEdge, Plane targetPlane) {
if (sweepVertex == null || outgoingEdge == null || targetPlane == null) {
return SweepDirection.UNDEFINED;
}
Vertex otherVertex = outgoingEdge.getOppositeVertex(sweepVertex);
if (otherVertex == null) {
return SweepDirection.UNDEFINED;
}
Line3d carrierLine = new Line3d(sweepVertex, otherVertex.minus(sweepVertex));
return PlaneIntersections.intersectLine(targetPlane, carrierLine)
.map(intersection -> classifyByDistance(sweepVertex, otherVertex, intersection))
.orElse(SweepDirection.UNDEFINED);
}
public Map<SweepDirection, Integer> countDirections(Vertex sweepVertex, List<Edge> outgoingEdges, Plane targetPlane) {
Map<SweepDirection, Integer> result = new EnumMap<>(SweepDirection.class);
for (SweepDirection direction : SweepDirection.values()) {
result.put(direction, 0);
}
for (Edge edge : outgoingEdges) {
SweepDirection direction = classify(sweepVertex, edge, targetPlane);
result.put(direction, result.get(direction) + 1);
}
return result;
}
private SweepDirection classifyByDistance(Vertex sweepVertex, Vertex otherVertex, Vector3d intersection) {
double distToIntersection = otherVertex.getDistance(intersection);
double distToCurrentPoint = otherVertex.getDistance(sweepVertex);
if (distToIntersection < distToCurrentPoint) {
return SweepDirection.NEGATIVE;
}
return SweepDirection.POSITIVE;
}
}
package de.hft.stuttgart.citydoctor2.healing.sweep;
import java.util.Optional;
import de.hft.stuttgart.citydoctor2.datastructure.Edge;
import de.hft.stuttgart.citydoctor2.datastructure.Vertex;
import de.hft.stuttgart.citydoctor2.math.Line3d;
import de.hft.stuttgart.citydoctor2.math.Plane;
import de.hft.stuttgart.citydoctor2.math.Vector3d;
/**
* *
* Verschiebt einzelne Punkte gemäß Raul Fall 1 und Fall 2.
*
* PS könnte nach HealPlanarPolygonSweep verlegt werden
*
* Diese Klasse enthält bewusst nur Punktbewegung, keine Topologieänderung. Fall
* 3 wird später durch TopologyEditor vorbereitet und danach wieder auf Fall 2
* zurückgeführt.
*/
public class SweepPointOperation {
/**
* Fall 1: Der Punkt besitzt keine abführende Kante und wird orthogonal auf die
* Zielebene projiziert.
*/
public void projectOrthogonal(Vertex vertex, Plane targetPlane) {
Vector3d projected = targetPlane.projectPointToPlane(vertex);
move(vertex, projected);
}
/**
* Fall 2: Der Punkt besitzt genau eine abführende Kante. Die neue Position ist
* der Schnittpunkt aus Zielebene und Trägergerade dieser Kante.
*/
public boolean sweepAlongEdge(Vertex vertex, Edge outgoingEdge, Plane targetPlane) {
Vertex otherVertex = outgoingEdge.getOppositeVertex(vertex);
if (otherVertex == null) {
return false;
}
Line3d carrierLine = new Line3d(vertex, otherVertex.minus(vertex));
Optional<Vector3d> intersection = PlaneIntersections.intersectLine(targetPlane, carrierLine);// aktuell noch dummy
intersection.ifPresent(point -> move(vertex, point));
return intersection.isPresent();
}
private void move(Vertex vertex, Vector3d point) {
vertex.setX(point.getX());
vertex.setY(point.getY());
vertex.setZ(point.getZ());
}
}
package de.hft.stuttgart.citydoctor2.healing.sweep;
import de.hft.stuttgart.citydoctor2.datastructure.Polygon;
import de.hft.stuttgart.citydoctor2.math.Plane;
/**
* Erzeugt die Zielebene für den Planaritäts-Sweep.
*
* Später kann hier Rauls-Sonderlogik aus cpp: createTargetPlane_planarity(...)
* ergänzt werden:
* - keine komplexen Punkte (nicht mehr als eine abführ. Kante): Regressionsebene über alle Punkte.
* - ein komplexer Punkt: Ebene mit alter Normalenrichtung durch diesen Punkt.
* - zwei komplexe Punkte: Ebene durch beide Punkte, möglichst nahe an alter Ebene.
* - drei oder mehr komplexe Punkte: Ebene durch beziehungsweise über diese Punkte.
*
* Für den ersten Portierungsschritt reicht die vorhandene CityDoctor-Logik
* Plane.of(Polygon): eine orthogonale Regressionsebene über alle Punkte des
* Polygons, ( wahrschein. inklusive innerer Ringe.)
*
*/
public class SweepTargetPlaneFactory {
/**
* Startvariante: vorhandene Regressionsebene des Projekts verwenden.
*/
public Plane createFor(Polygon polygon) {
return Plane.of(polygon);
}
}
package de.hft.stuttgart.citydoctor2.healing.sweep;
import java.util.ArrayList;
import java.util.List;
import de.hft.stuttgart.citydoctor2.datastructure.Geometry;
import de.hft.stuttgart.citydoctor2.datastructure.LinearRing;
import de.hft.stuttgart.citydoctor2.datastructure.Polygon;
/**
* Temporäre Topologiesicht auf eine CityDoctor-Geometry.
*
* Motiv:
* CityDoctor speichert Topologie primär über Polygon, LinearRing und Vertex.
* Die Klasse Edge wird daraus neu aufgebaut.
*
* Der C++-Code arbeitet dagegen mit einer
* expliziten BRep-Topologie aus Face, Loop, CoEdge, Edge und Vertex.
*
* Diese Klasse könnte später anhand der vorhandenen Ringen gerichtete CoEdge und
* CoEdgeRing-Objekte(temp.Loop á la CityDoctor) erzeugen.
*
* Sie ist dann Arbeitsmodell für den Sweep, nicht dauerhaftes Datenmodell des Projekts.
*/
public class SweepTopology {
private final Geometry geometry;
private final List<CoEdgeRing> rings = new ArrayList<>();
public SweepTopology(Geometry geometry) {
this.geometry = geometry;
}
public Geometry getGeometry() {
return geometry;
}
public List<CoEdgeRing> getRings() {
return rings;
}
/**
* Späterer Builder-Schritt: aus allen Polygonringen gerichtete CoEdges
* erzeugen und Partner über gemeinsame Kanten bestimmen.
*/
public void rebuild() {
rings.clear();
if (geometry == null || geometry.getPolygons() == null) {
return;
}
for (Polygon polygon : geometry.getPolygons()) {
addRing(polygon, polygon.getExteriorRing());
for (LinearRing innerRing : polygon.getInnerRings()) {
addRing(polygon, innerRing);
}
}
// TODO Partnerbeziehungen zwischen CoEdges ergänzen.
}
private void addRing(Polygon polygon, LinearRing ring) {
if (ring != null) {
rings.add(new CoEdgeRing(polygon, ring));
}
}
}
package de.hft.stuttgart.citydoctor2.healing.sweep;
import java.util.List;
import de.hft.stuttgart.citydoctor2.datastructure.Edge;
import de.hft.stuttgart.citydoctor2.datastructure.Geometry;
import de.hft.stuttgart.citydoctor2.datastructure.Polygon;
import de.hft.stuttgart.citydoctor2.datastructure.Vertex;
/**
* Führt spätere Topologieänderungen für Fall 3 aus.
*
* C++-Vorbild:
* - splitVertexCut(...) für mehrere negative abführende Kanten.
* - splitVertexPull(...) für mehrere positive abführende Kanten.
*
* In Java sollen diese Operationen nicht direkt in Geometry landen, weil sie
* sehr spezifisch für den Sweep sind. Geometry soll weiterhin allgemeines
* Datenmodell bleiben; der Editor übersetzt Sweep-Entscheidungen in konkrete
* Ringänderungen.
*/
public class TopologyEditor {
/**
* Fall 3.1: alle abführenden Kanten sind negativ. Der Punkt wird in mehrere
* Punkte aufgeteilt, Kanten werden je einer Kopie zugeordnet und neue
* Verbindungskanten werden in die betroffenen Ringe eingefügt.
*/
public List<Vertex> splitForNegativeOutgoingEdges(Geometry geometry, Polygon sweepPolygon, Vertex vertex,
List<Edge> outgoingEdges) {
// TODO Entspricht grob C++ splitVertexCut(...).
// Danach kann jeder neue Punkt über Fall 2 entlang genau einer Kante gesweept werden.
return List.of(vertex);
}
/**
* Fall 3.2: alle abführenden Kanten sind positiv. Es entsteht eine neue Kante
* entlang der Schnittgeraden der beiden Nachbarflächen; Originalpunkt bleibt,
* Duplikat wird gesweept.
*/
public Vertex splitForPositiveOutgoingEdges(Geometry geometry, Polygon sweepPolygon, Vertex vertex,
List<Edge> outgoingEdges) {
// TODO Entspricht grob C++ splitVertexPull(...).
return vertex;
}
/**
* Fall 3.3: gemischte positive und negative Kanten. Zuerst werden die
* Kantengruppen auf Original und Duplikat verteilt; anschließend werden die
* Teilprobleme wieder wie Fall 3.1, 3.2 oder Fall 2 behandelt.
*/
public Vertex splitForMixedOutgoingEdges(Geometry geometry, Polygon sweepPolygon, Vertex vertex,
List<Edge> positiveEdges, List<Edge> negativeEdges) {
// TODO Rauls Dissert. Fall 3.3.
return vertex;
}
}
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment