Commit 5a5d8118 authored by Numanoglu's avatar Numanoglu
Browse files

WIP preserve sweep branch work

parent a0a195e2
...@@ -7,23 +7,58 @@ import de.hft.stuttgart.citydoctor2.check.HealingID; ...@@ -7,23 +7,58 @@ import de.hft.stuttgart.citydoctor2.check.HealingID;
import de.hft.stuttgart.citydoctor2.check.HealingMethod; import de.hft.stuttgart.citydoctor2.check.HealingMethod;
import de.hft.stuttgart.citydoctor2.check.ModificationListener; import de.hft.stuttgart.citydoctor2.check.ModificationListener;
import de.hft.stuttgart.citydoctor2.check.error.NonPlanarPolygonDistancePlaneError; import de.hft.stuttgart.citydoctor2.check.error.NonPlanarPolygonDistancePlaneError;
import de.hft.stuttgart.citydoctor2.math.Plane;
import de.hft.stuttgart.citydoctor2.math.Vector3d;
public class HealPlanarPolygonSweep implements HealingMethod { public class HealPlanarPolygonSweep implements HealingMethod {
private static final Logger logger = LogManager.getLogger(HealPlanarPolygonSweep.class); private static final Logger logger = LogManager.getLogger(HealPlanarPolygonSweep.class);
/* todo: in HealPlanarPolygonSweep visit-Methode austauschen
public boolean visit(NonPlanarPolygonDistancePlaneError err, ModificationListener l) {
logger.debug("Executing Repair for NonPlanarPolygonDistancePlaneError");
SweepUtilities utils = new SweepUtilities(err);
todo: Epsilon-Werte setzen
utils.initialize();
if ( utils.calculate() ) {
utils.doHeal();
return true;
}
else {
logger.debug("Repair by Sweep not possible");
return false;
}
}
*/
// todo : dann entferne Dummy
@Override @Override
public boolean visit(NonPlanarPolygonDistancePlaneError err, ModificationListener l) { public boolean visit(NonPlanarPolygonDistancePlaneError err, ModificationListener l) {
logger.debug("Executing Repair for NonPlanarPolygonDistancePlaneError"); logger.debug("Executing Repair for NonPlanarPolygonDistancePlaneError");
Plane plane = Plane.of(err.getPolygon()); if (err == null) {
plane.getDistance(new Vector3d(5, 5, 5)); logger.error("Cannot execute sweep repair: error is null");
return false;
}
if (err.getPlane() == null) {
logger.error("Cannot execute sweep repair: target plane is null");
return false;
}
if (err.getPolygon() == null) {
logger.error("Cannot execute sweep repair: polygon is null");
return false;
}
SweepUtilities utils = new SweepUtilities(err);
utils.initialize();
if (utils.calculate()) {
utils.doHeal();
return true; return true;
} }
logger.debug("Repair by Sweep not possible");
return false;
}
@Override @Override
public HealPlanarPolygonSweep createNew() { public HealPlanarPolygonSweep createNew() {
return new HealPlanarPolygonSweep(); return new HealPlanarPolygonSweep();
......
package de.hft.stuttgart.citydoctor2.healing; package de.hft.stuttgart.citydoctor2.healing;
import de.hft.stuttgart.citydoctor2.datastructure.Edge; import de.hft.stuttgart.citydoctor2.datastructure.Edge;
import de.hft.stuttgart.citydoctor2.datastructure.Polygon; import de.hft.stuttgart.citydoctor2.datastructure.Vertex;
import de.hft.stuttgart.citydoctor2.math.Plane; import de.hft.stuttgart.citydoctor2.math.Plane;
import de.hft.stuttgart.citydoctor2.math.UnitVector3d;
import de.hft.stuttgart.citydoctor2.math.Vector3d; import de.hft.stuttgart.citydoctor2.math.Vector3d;
enum SweepIntersectionPosition {
ON_EDGE,
BEFORE_SWEEP_VERTEX,
BEYOND_OUTGOING_EDGE_TARGET,
UNKNOWN
}
public class SweepEdge { public class SweepEdge {
private Edge edge; private Edge outgoingEdge;
private Vector3d refPt; private Vertex sweepVertex;
private Vector3d dir; //todo: falls es eine extra Klasse für einheitsvektoren gibt, diese Klasse benutzen private Vertex outgoingEdgeTarget;
boolean bValid = false; private Vector3d referencePoint;
private UnitVector3d direction;
private double outgoingEdgeLength;
private boolean valid = false;
public SweepEdge(Edge outgoingEdge, Vertex sweepVertex, double minSweepEdgeLength) {
public SweepEdge(Edge e, Polygon p, double epsPointDist) { if (outgoingEdge == null || sweepVertex == null) {
return;
}
edge = e; Vertex outgoingEdgeTarget = outgoingEdge.getOppositeVertex(sweepVertex);
//todo: falls geschickter --> Ring statt Polygon uebergeben if (outgoingEdgeTarget == null) {
return;
}
//todo: Wenn die Kante nicht zu kurz ist (epsPointDist) Vector3d edgeVector = outgoingEdgeTarget.minus(sweepVertex);
// --> Anfangs- oder Endpunkt als Referenzpunkt, je nachdem welcher Punkt zum Polygon gehoert if (edgeVector.getLength() < minSweepEdgeLength) {
// --> Richtungsvektor dir als Einheitsvektor berechnen return;
// --> valid = true; }
this.outgoingEdge = outgoingEdge;
this.sweepVertex = sweepVertex;
this.outgoingEdgeTarget = outgoingEdgeTarget;
this.referencePoint = sweepVertex;
this.direction = edgeVector.normalize();
this.outgoingEdgeLength = edgeVector.getLength();
this.valid = true;
} }
public boolean valid() { public boolean valid() {
return bValid; return valid;
}
public Vector3d intersection(Plane plane, double minLinePlaneAngleRatio) {
if (!hasIntersectionPoint(plane, minLinePlaneAngleRatio)) {
return null;
} }
public boolean intersection(Plane plane, Vector3d ipt, double eps) { double denominator = plane.getNormal().dot(direction);
//todo: Schnittpunkt berechnen double lineParameter = (plane.getD() - plane.getNormal().dot(referencePoint)) / denominator;
return referencePoint.plus(direction.mult(lineParameter));
}
public boolean hasIntersectionPoint(Plane plane, double minLinePlaneAngleRatio) {
if (!valid || plane == null) {
return false; return false;
} }
public boolean isOnEdge (Vector3d pt) { double denominator = plane.getNormal().dot(direction);
return Math.abs(denominator) >= minLinePlaneAngleRatio;
}
public boolean intersection(Plane plane, Vector3d intersectionPoint, double minLinePlaneAngleRatio) {
//todo: pruefen ob pt zwischen Anfangs- und Endpunkt liegt if (intersectionPoint == null) {
return false; return false;
} }
Vector3d point = intersection(plane, minLinePlaneAngleRatio);
if (point == null) {
return false;
}
intersectionPoint.setX(point.getX());
intersectionPoint.setY(point.getY());
intersectionPoint.setZ(point.getZ());
return true;
}
//------------------------------------------------------------------------------------------------------
// Nicht mehr notwendig wegen Position- Flags- -nur noch Konfort
public boolean isOnEdge (Vector3d intersectionPoint, double eps) {
return classifyIntersectionPosition(intersectionPoint, eps) == SweepIntersectionPosition.ON_EDGE;
}
//-------------------------------------------------------------------------------------------------------
public SweepIntersectionPosition classifyIntersectionPosition(Vector3d intersectionPoint, double eps) {
if (!valid || intersectionPoint == null) {
return SweepIntersectionPosition.UNKNOWN;
}
Vector3d sweepToIntersection = intersectionPoint.minus(sweepVertex);
double lineParameter = sweepToIntersection.dot(direction);
if (lineParameter < -eps) {
return SweepIntersectionPosition.BEFORE_SWEEP_VERTEX;
}
if (lineParameter > outgoingEdgeLength + eps) {
return SweepIntersectionPosition.BEYOND_OUTGOING_EDGE_TARGET;
}
return SweepIntersectionPosition.ON_EDGE;
}
Edge getOutgoingEdge() {
return outgoingEdge;
}
Vector3d getReferencePoint() {
return referencePoint;
}
UnitVector3d getDirection() {
return direction;
}
} }
...@@ -6,6 +6,9 @@ import java.util.Set; ...@@ -6,6 +6,9 @@ import java.util.Set;
import java.util.Collections; import java.util.Collections;
import java.util.HashSet; import java.util.HashSet;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import de.hft.stuttgart.citydoctor2.check.ModificationListener; import de.hft.stuttgart.citydoctor2.check.ModificationListener;
import de.hft.stuttgart.citydoctor2.check.error.NonPlanarPolygonDistancePlaneError; import de.hft.stuttgart.citydoctor2.check.error.NonPlanarPolygonDistancePlaneError;
import de.hft.stuttgart.citydoctor2.datastructure.LinearRing; import de.hft.stuttgart.citydoctor2.datastructure.LinearRing;
...@@ -35,20 +38,26 @@ import de.hft.stuttgart.citydoctor2.math.Vector3d; ...@@ -35,20 +38,26 @@ import de.hft.stuttgart.citydoctor2.math.Vector3d;
public class SweepUtilities { public class SweepUtilities {
private static final Logger logger = LogManager.getLogger(SweepUtilities.class);
private Plane targetPlane; private Plane targetPlane;
private Polygon pol; private Polygon pol;
//todo: weitere Attribute hinzufuegen (fuer collectOutgoingEdges, ...) //todo: weitere Attribute hinzufuegen (fuer collectOutgoingEdges, ...)
// todo: Epsilons passend initialisieren // todo: Toleranzen passend initialisieren
private double epsPointDist = 0.001; //Mindestlaenge einer Kante private double minSweepEdgeLength = 0.001; // Mindestlaenge einer Kante
private double epsPlaneDist = 0.001; //Maximalabstand zur Ebene private double maxPointPlaneDistance = 0.001; // Maximalabstand zur Ebene
private double epsIntersection = 0.001; //Epsilon fuer Winkel zwischen Kante und Ebene private double minLinePlaneAngleRatio = 0.001; // MindestNeigung: vgl. <EbenenenNormale,GeradenRichtVektor>
private List<SweepVertex> verts; private List<SweepVertex> verts;
public SweepUtilities(NonPlanarPolygonDistancePlaneError err) { public SweepUtilities(NonPlanarPolygonDistancePlaneError err) {
if (err == null) {
return;
}
targetPlane = err.getPlane(); targetPlane = err.getPlane();
pol = err.getPolygon(); pol = err.getPolygon();
...@@ -56,46 +65,49 @@ public class SweepUtilities { ...@@ -56,46 +65,49 @@ public class SweepUtilities {
public void initialize() { public void initialize() {
if (pol == null || targetPlane == null) {
logger.error("Cannot initialize sweep: polygon or target plane is null");
verts = null;
return;
}
int nVerts = countSweepVertices(pol); int nVerts = countSweepVertices(pol);
verts = new ArrayList<>(nVerts); verts = new ArrayList<>(nVerts);// <Vertex>??
collectSweepVertices(pol); collectSweepVertices(pol);
for (SweepVertex v : verts) { for (SweepVertex v : verts) {
v.collectOutgoingEdges(pol/* todo: weitere Uebergabeparameter hinzufuegen */, epsPointDist); v.collectOutgoingEdges(pol, minSweepEdgeLength);
v.initStatus(targetPlane, epsPlaneDist); v.initStatus(targetPlane, maxPointPlaneDistance);
} }
} }
public boolean calculate() { public boolean calculate() {
// if there are no vertices --> no calculations if (verts == null || targetPlane == null) {
if ( null == verts )
return false; return false;
}
//if there is already an error vertex --> no calculations if (hasErrorStatus()) {
if ( !canHeal() )
return false; return false;
}
boolean bError = false;
//todo spaeter: Anzahl der Ecken mit mehr als einer outging Edge zaehlen //todo spaeter: Anzahl der Ecken mit mehr als einer outging Edge zaehlen
// Wenn es alle diese Ecken auf der Zielebene liegen --> weiter // Wenn es alle diese Ecken auf der Zielebene liegen --> weiter
// Wenn es maximal drei solche Ecken gibt: Zielebene neu berechnen // Wenn es maximal drei solche Ecken gibt: Zielebene neu berechnen
// Wenn es mehr als drei solche Ecken gibt: diese Ecken bekommen den ERROR-Status, // Wenn es mehr als drei solche Ecken gibt: diese Ecken bekommen den ERROR-Status,
// bError = true; // hasErrorStatus() verhindert danach das Heilen.
/* /*
// If there is a new target plane, new initialization of status is necessary: // If there is a new target plane, new initialization of status is necessary:
for (SweepVertex v : verts) { for (SweepVertex v : verts) {
v.initStatus(targetPlane, epsPlaneDist); v.initStatus(targetPlane, maxPointPlaneDistance);
} }
*/ */
if ( !bError ) {
for (SweepVertex v : verts) { for (SweepVertex v : verts) {
v.calculateNewPoint(targetPlane, pol, epsPointDist); v.calculateNewPoint(targetPlane, pol, minSweepEdgeLength, minLinePlaneAngleRatio);
}
} }
return canHeal(); return canHeal();
...@@ -103,6 +115,11 @@ public class SweepUtilities { ...@@ -103,6 +115,11 @@ public class SweepUtilities {
public void doHeal() { public void doHeal() {
if (!canHeal()) {
logger.error("Cannot execute sweep heal: calculated sweep vertices are not healable");
return;
}
for (SweepVertex v : verts) { for (SweepVertex v : verts) {
v.doHeal(); v.doHeal();
} }
...@@ -112,23 +129,53 @@ public class SweepUtilities { ...@@ -112,23 +129,53 @@ public class SweepUtilities {
for (SweepVertex v : verts) { for (SweepVertex v : verts) {
SweepVertexStatus status = v.getStatus(); SweepVertexStatus status = v.getStatus();
if ( SweepVertexStatus.ERROR == status || SweepVertexStatus.NONE == status ) switch (status) {
case ON_PLANE:
break;
case PROJECT:
case SWEEP:
if (!v.hasNewPoint())
return false; return false;
break;
case NONE:
case ERROR:
return false;
}
}
return true;
} }
private boolean hasErrorStatus() {
for (SweepVertex v : verts) {
SweepVertexStatus status = v.getStatus();
if (status == SweepVertexStatus.ERROR || status == SweepVertexStatus.NONE) {
return true; return true;
} }
}
return false;
}
private int countSweepVertices(Polygon p) { private int countSweepVertices(Polygon p) {
int nVerts = p.getExteriorRing().getVertices().size(); int nVerts = countSweepVertices(p.getExteriorRing());
for (LinearRing lr : p.getInnerRings()) { for (LinearRing lr : p.getInnerRings()) {
nVerts += lr.getVertices().size(); nVerts += countSweepVertices(lr);
} }
return nVerts; return nVerts;
} }
private int countSweepVertices(LinearRing ring) {
if (ring == null) {
return 0;
}
return Math.max(0, ring.getVertices().size() - 1); //redundande übervorsicht
}
private void collectSweepVertices(Polygon p) { private void collectSweepVertices(Polygon p) {
collectSweepVertices(p.getExteriorRing()); collectSweepVertices(p.getExteriorRing());
...@@ -139,8 +186,12 @@ public class SweepUtilities { ...@@ -139,8 +186,12 @@ public class SweepUtilities {
private void collectSweepVertices(LinearRing ring) { private void collectSweepVertices(LinearRing ring) {
for (Vertex v : ring.getVertices()) { if (ring == null) {
verts.add(new SweepVertex(v)); return;
}
List<Vertex> vertices = ring.getVertices();
for (int i = 0; i < vertices.size() - 1; i++) {
verts.add(new SweepVertex(vertices.get(i)));
} }
} }
......
...@@ -3,8 +3,12 @@ package de.hft.stuttgart.citydoctor2.healing; ...@@ -3,8 +3,12 @@ package de.hft.stuttgart.citydoctor2.healing;
import java.util.LinkedList; import java.util.LinkedList;
import java.util.List; import java.util.List;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import de.hft.stuttgart.citydoctor2.datastructure.Vertex; import de.hft.stuttgart.citydoctor2.datastructure.Vertex;
import de.hft.stuttgart.citydoctor2.datastructure.Edge; 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.Polygon;
import de.hft.stuttgart.citydoctor2.math.Plane; import de.hft.stuttgart.citydoctor2.math.Plane;
import de.hft.stuttgart.citydoctor2.math.Vector3d; import de.hft.stuttgart.citydoctor2.math.Vector3d;
...@@ -19,6 +23,8 @@ enum SweepVertexStatus { ...@@ -19,6 +23,8 @@ enum SweepVertexStatus {
public class SweepVertex { public class SweepVertex {
private static final Logger logger = LogManager.getLogger(SweepVertex.class);
private SweepVertexStatus status = SweepVertexStatus.NONE; private SweepVertexStatus status = SweepVertexStatus.NONE;
private Vertex vert; private Vertex vert;
...@@ -30,16 +36,46 @@ public class SweepVertex { ...@@ -30,16 +36,46 @@ public class SweepVertex {
vert = v; vert = v;
} }
//todo: weitere Uebergabeparameter hinzufuegen public void collectOutgoingEdges(Polygon pol, double minSweepEdgeLength) {
public void collectOutgoingEdges(Polygon pol/* todo: weitere Uebergabeparameter hinzufuegen */, double epsPointDist) {
//todo: Liste outgoingEdges fuellen outgoingEdges.clear();
if (pol == null || vert == null) {
logger.error("Cannot collect outgoing edges: polygon or vertex is null");
status = SweepVertexStatus.ERROR;
return;
}
Geometry geometry = pol.getParent();
if (geometry == null) {
logger.error("Cannot collect outgoing edges: polygon has no parent geometry");
status = SweepVertexStatus.ERROR;
return;
}
if (geometry.getEdges() == null) {
geometry.updateEdgesAndVertices();
}
for (Edge edge : geometry.getEdgesAdjacentTo(vert)) {
if (edge.getAdjacentPolygons().contains(pol)) {
continue;
}
if (edge.getFrom().getDistance(edge.getTo()) < minSweepEdgeLength) {
logger.error("Cannot collect outgoing edges: outgoing edge is shorter than minSweepEdgeLength");
status = SweepVertexStatus.ERROR;
return;
}
outgoingEdges.add(edge);
}
} }
public void initStatus (Plane targetPlane, double distPlaneEps) { public void initStatus (Plane targetPlane, double maxPointPlaneDistance) {
if ( targetPlane.getDistance(vert) < distPlaneEps ) { if (targetPlane == null || vert == null) {
status = SweepVertexStatus.ERROR;
return;
}
if ( targetPlane.getDistance(vert) < maxPointPlaneDistance ) {
status = SweepVertexStatus.ON_PLANE; status = SweepVertexStatus.ON_PLANE;
} }
else if ( outgoingEdges.size() < 1 ) { else if ( outgoingEdges.size() < 1 ) {
...@@ -55,38 +91,117 @@ public class SweepVertex { ...@@ -55,38 +91,117 @@ public class SweepVertex {
*/ */
else if ( outgoingEdges.size() == 1 ) else if ( outgoingEdges.size() == 1 )
status = SweepVertexStatus.SWEEP; status = SweepVertexStatus.SWEEP;
else else {
/*
* Fall 3 nach Piepereit:
* Mehr als eine abführende Kante darf später nicht pauschal fehlschlagen.
* Zuerst müssen die abführenden Kanten nach Sweep-Richtung klassifiziert
* werden: negativ bedeutet, die Kante würde verkürzt; positiv bedeutet, die
* Kante würde verlängert.
*
* Geplante Aufteilung:
* - nur negative Kanten: Split-Cut, Vertex vervielfältigen und Kanten auf
* die Kopien verteilen.
* - nur positive Kanten: Split-Pull, Vertex duplizieren und eine neue Kante
* entlang der Schnittgeraden zweier Nachbarflächen einfügen.
* - gemischt: positive und negative Kanten auf Original/Duplikat trennen und
* danach erneut als Fall 2, Fall 3.1 oder Fall 3.2 behandeln.
*
* Bis diese Topologieoperationen implementiert sind, bleibt Fall 3 bewusst
* ERROR.
*/
status = SweepVertexStatus.ERROR; status = SweepVertexStatus.ERROR;
} }
}
public void calculateNewPoint (Plane targetPlane, Polygon p, double epsPointDist) { public void calculateNewPoint (Plane targetPlane, Polygon p, double minSweepEdgeLength, double minLinePlaneAngleRatio) {
if ( SweepVertexStatus.PROJECT == status ) { newPoint = null;
if (targetPlane == null || vert == null) {
status = SweepVertexStatus.ERROR;
return;
}
switch (status) {
case ON_PLANE:
return;
case PROJECT:
newPoint = targetPlane.projectPointToPlane(vert); newPoint = targetPlane.projectPointToPlane(vert);
if (newPoint == null) {
status = SweepVertexStatus.ERROR;
} }
else if ( SweepVertexStatus.SWEEP == status ) { return;
if ( 1 != getNoOutgoingEdges() ) {
case SWEEP:
if (outgoingEdges == null || outgoingEdges.size() != 1) {
status = SweepVertexStatus.ERROR; status = SweepVertexStatus.ERROR;
return;
} }
else {
SweepEdge e = new SweepEdge(outgoingEdges.getFirst(), p, epsPointDist); Edge outgoingEdge = outgoingEdges.getFirst();
//todo: Schnittpunkt der outgoing Edge mit der targePlane berechnen if (outgoingEdge == null) {
//todo: dafuer Hilfsklasse SweepEdge fuer Parameterdarstellung usw. nutzen status = SweepVertexStatus.ERROR;
//todo: Parameter des Schnittpunkts auf der Edge berechnen return;
// --> wenn die Edge "umkippt" oder zum Punkt degeneriert (epsPointDist): status = SweepVertexStatus.ERROR }
SweepEdge sweepEdge = new SweepEdge(outgoingEdge, vert, minSweepEdgeLength);
if (!sweepEdge.valid()) {
status = SweepVertexStatus.ERROR;
return;
} }
Vector3d intersectionPoint = sweepEdge.intersection(targetPlane, minLinePlaneAngleRatio);
if (intersectionPoint == null) {
status = SweepVertexStatus.ERROR;
return;
}
SweepIntersectionPosition position = sweepEdge.classifyIntersectionPosition(intersectionPoint, minSweepEdgeLength);
switch (position) {
case ON_EDGE:
case BEYOND_OUTGOING_EDGE_TARGET:
/*
* Fall 2 nach Piepereit:
* Auf der Kante bedeutet Verkuerzen, hinter dem Kantenziel bedeutet
* Verlaengern. In beiden Faellen ist der Schnittpunkt die neue Position
* des Sweep-Punktes.
*/
newPoint = intersectionPoint;
return;
case BEFORE_SWEEP_VERTEX:
case UNKNOWN:
default:
status = SweepVertexStatus.ERROR;
return;
}
case NONE:
case ERROR:
default:
status = SweepVertexStatus.ERROR;
return;
} }
} }
public void doHeal () { public void doHeal () {
if ( SweepVertexStatus.SWEEP == status || SweepVertexStatus.PROJECT == status ) { if (status != SweepVertexStatus.SWEEP && status != SweepVertexStatus.PROJECT) {
return;
}
if (vert == null || newPoint == null) {
status = SweepVertexStatus.ERROR;
return;
}
vert.setX(newPoint.getX()); vert.setX(newPoint.getX());
vert.setY(newPoint.getY()); vert.setY(newPoint.getY());
vert.setZ(newPoint.getZ()); vert.setZ(newPoint.getZ());
} }
}
public SweepVertexStatus getStatus () { public SweepVertexStatus getStatus () {
...@@ -97,4 +212,9 @@ public class SweepVertex { ...@@ -97,4 +212,9 @@ public class SweepVertex {
return outgoingEdges.size(); return outgoingEdges.size();
} }
public boolean hasNewPoint() {
return newPoint != null;
}
} }
package de.hft.stuttgart.citydoctor2.healing;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import org.junit.Test;
import de.hft.stuttgart.citydoctor2.check.error.NonPlanarPolygonDistancePlaneError;
import de.hft.stuttgart.citydoctor2.datastructure.Building;
import de.hft.stuttgart.citydoctor2.datastructure.Geometry;
import de.hft.stuttgart.citydoctor2.datastructure.Polygon;
import de.hft.stuttgart.citydoctor2.datastructure.Vertex;
import de.hft.stuttgart.citydoctor2.math.Plane;
import de.hft.stuttgart.citydoctor2.math.Vector3d;
public class SweepUtilitiesSyntheticTest {
private static final double EPS = 0.000001;
@Test
public void repairsCase1ByOrthogonalProjection() {
Building building = SyntheticSweepPlanarityModelFactory.createWallWithProtrudingWindowInnerRingPoint();
Polygon sweepPolygon = getPolygon(building, 0);
Vertex protrudingPoint = sweepPolygon.getInnerRings().get(0).getVertices().get(2);
Plane targetPlane = wallPlaneX0();
assertEquals(0.3, protrudingPoint.getX(), EPS);
assertTrue(runSweep(sweepPolygon, targetPlane));
assertEquals(0, protrudingPoint.getX(), EPS);
assertEquals(0, targetPlane.getDistance(protrudingPoint), EPS);
}
@Test
public void repairsCase2WhenOutgoingEdgeIsShortened() {
Building building = SyntheticSweepPlanarityModelFactory.createSweepCase2ShorteningEdge();
Polygon sweepPolygon = getPolygon(building, 0);
Vertex sweepPoint = sweepPolygon.getExteriorRing().getVertices().get(1);
Plane targetPlane = wallPlaneX0();
assertEquals(1, sweepPoint.getX(), EPS);
assertTrue(runSweep(sweepPolygon, targetPlane));
assertEquals(0, sweepPoint.getX(), EPS);
assertEquals(0, targetPlane.getDistance(sweepPoint), EPS);
}
@Test
public void repairsCase2WhenOutgoingEdgeIsExtended() {
Building building = SyntheticSweepPlanarityModelFactory.createSweepCase2ExtendingEdge();
Polygon sweepPolygon = getPolygon(building, 0);
Vertex sweepPoint = sweepPolygon.getExteriorRing().getVertices().get(1);
Plane targetPlane = wallPlaneX0();
assertEquals(1, sweepPoint.getX(), EPS);
assertTrue(runSweep(sweepPolygon, targetPlane));
assertEquals(0, sweepPoint.getX(), EPS);
assertEquals(0, targetPlane.getDistance(sweepPoint), EPS);
}
@Test
public void rejectsCase2WhenIntersectionIsBeforeSweepVertex() {
Building building = SyntheticSweepPlanarityModelFactory.createSweepCase2IntersectionBeforeSweepVertex();
Polygon sweepPolygon = getPolygon(building, 0);
Vertex sweepPoint = sweepPolygon.getExteriorRing().getVertices().get(1);
assertFalse(runSweep(sweepPolygon, wallPlaneX0()));
assertEquals(1, sweepPoint.getX(), EPS);
}
@Test
public void rejectsCase2WhenOutgoingEdgeIsParallelToTargetPlane() {
Building building = SyntheticSweepPlanarityModelFactory.createSweepCase2ParallelOutgoingEdge();
Polygon sweepPolygon = getPolygon(building, 0);
Vertex sweepPoint = sweepPolygon.getExteriorRing().getVertices().get(1);
assertFalse(runSweep(sweepPolygon, wallPlaneX0()));
assertEquals(1, sweepPoint.getX(), EPS);
}
private boolean runSweep(Polygon sweepPolygon, Plane targetPlane) {
NonPlanarPolygonDistancePlaneError error = new NonPlanarPolygonDistancePlaneError(sweepPolygon, 0, null,
targetPlane);
SweepUtilities utilities = new SweepUtilities(error);
utilities.initialize();
if (!utilities.calculate()) {
return false;
}
utilities.doHeal();
return true;
}
private Polygon getPolygon(Building building, int index) {
Geometry geometry = building.getGeometries().get(0);
return geometry.getPolygons().get(index);
}
private Plane wallPlaneX0() {
return new Plane(new Vector3d(1, 0, 0), new Vector3d(0, 0, 0));
}
}
...@@ -47,6 +47,16 @@ public class SyntheticSweepPlanarityModelFactory { ...@@ -47,6 +47,16 @@ public class SyntheticSweepPlanarityModelFactory {
createSlopedRoofWithProtrudingOpeningRingPoint(), createSlopedRoofWithProtrudingOpeningRingPoint(),
new File("sweep-case1-sloped-roof-opening-ring-point.gml"), new File("sweep-case1-sloped-roof-opening-ring-point.gml"),
"SweepCase1SlopedRoofOpeningRingPoint"); "SweepCase1SlopedRoofOpeningRingPoint");
exportForGui(
createSweepCase2ShorteningEdge(),
new File("sweep-case2-shortening-edge.gml"),
"SweepCase2ShorteningEdge");
exportForGui(
createSweepCase2ExtendingEdge(),
new File("sweep-case2-extending-edge.gml"),
"SweepCase2ExtendingEdge");
} }
static Building createNonPlanarCube(double boxsize, double bottomerr, double topLift) { static Building createNonPlanarCube(double boxsize, double bottomerr, double topLift) {
...@@ -200,6 +210,55 @@ public class SyntheticSweepPlanarityModelFactory { ...@@ -200,6 +210,55 @@ public class SyntheticSweepPlanarityModelFactory {
return building; return building;
} }
/**
* Fall 2 model: the faulty sweep point has exactly one outgoing edge. The target
* plane x = 0 cuts the finite outgoing edge, so the edge is shortened.
*/
static Building createSweepCase2ShorteningEdge() {
return createSweepCase2SingleOutgoingEdge(new Vertex(-1, 1, 0));
}
/**
* Fall 2 model: the faulty sweep point has exactly one outgoing edge. The target
* plane x = 0 lies behind the outgoing edge target, so the edge is extended.
*/
static Building createSweepCase2ExtendingEdge() {
return createSweepCase2SingleOutgoingEdge(new Vertex(0.5, 1, 0));
}
/**
* Fall 2 error model: the line through the outgoing edge hits the target plane
* before the sweep point. The current sweep implementation rejects this case.
*/
static Building createSweepCase2IntersectionBeforeSweepVertex() {
return createSweepCase2SingleOutgoingEdge(new Vertex(2, 1, 0));
}
/**
* Fall 2 error model: the outgoing edge is parallel to the target plane x = 0.
* The current sweep implementation cannot calculate a stable line-plane cut.
*/
static Building createSweepCase2ParallelOutgoingEdge() {
return createSweepCase2SingleOutgoingEdge(new Vertex(1, 2, 0));
}
private static Building createSweepCase2SingleOutgoingEdge(Vertex outgoingTarget) {
Building building = new Building();
Geometry geom = new Geometry(GeometryType.MULTI_SURFACE, Lod.LOD2, Orientation.OUTWARD);
building.addGeometry(geom);
Vertex sharedAnchor = new Vertex(0, 0, 0);
Vertex sweepPoint = new Vertex(1, 1, 0);
Vertex upperRight = new Vertex(0, 2, 0);
Vertex upperLeft = new Vertex(0, 2, 2);
addPolygon(geom, sharedAnchor, sweepPoint, upperRight, upperLeft);
addPolygon(geom, sweepPoint, outgoingTarget, sharedAnchor);
geom.updateEdgesAndVertices();
return building;
}
private static void exportForGui(Building building, File output, String gmlId) throws CityDoctorWriteException { private static void exportForGui(Building building, File output, String gmlId) throws CityDoctorWriteException {
CityDoctorModel model = createCityDoctorModel(building, output, gmlId); CityDoctorModel model = createCityDoctorModel(building, output, gmlId);
model.saveAs(output.getAbsolutePath(), false); model.saveAs(output.getAbsolutePath(), false);
......
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