Commit 7088483b authored by Numanoglu's avatar Numanoglu
Browse files

Add sweep utilities step tests and radian tolerance

parent 5da02385
Pipeline #12432 failed with stage
in 2 minutes and 24 seconds
......@@ -15,6 +15,7 @@ import de.hft.stuttgart.citydoctor2.math.Plane;
public class SweepUtilities {
private static final Logger logger = LogManager.getLogger(SweepUtilities.class);
private static final double PI = Math.PI;
private Plane targetPlane;
private Polygon errorPolygon;
......@@ -22,7 +23,7 @@ public class SweepUtilities {
// todo: Toleranzen passend initialisieren
private double minSweepEdgeLength = 0.001; // Mindestlaenge einer Kante
private double maxPointPlaneDistance = 0.001; // Maximalabstand zur Ebene
private double minLinePlaneAngle = Math.toRadians(1.0); // Mindestwinkel zwischen Gerade und Ebene
private double minLinePlaneAngle = PI / 180.0; // Mindestwinkel zwischen Gerade und Ebene im Bogenmass
private List<SweepVertex> sweepVertices;
......@@ -148,7 +149,8 @@ public class SweepUtilities {
if (ring == null) {
return 0;
}
return Math.max(0, ring.getVertices().size() - 1); //redundante übervorsicht
// TODO wh ob ohnehin safe ohne .max()
return Math.max(0, ring.getVertices().size() - 1);
}
private void collectSweepVertices(Polygon p) {
......
package de.hft.stuttgart.citydoctor2.healing;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
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 SweepUtilitiesStepTest {
private static final double EPS = 0.000001;
/**
* initialize(): Das synthetische Wandmodell hat vier Außenringpunkte und danach
* vier Innenringpunkte. Der abstehende Fensterpunkt liegt im Innenring an Index
* 2, also in der SweepVertex-Liste an Index 6. Er besitzt keine abführende
* Kante und muss als Fall 1 PROJECT klassifiziert werden.
*/
@Test
public void initializeClassifiesKnownInnerRingPointAsProject() {
Building building = SyntheticSweepPlanarityModelFactory.createWallWithProtrudingWindowInnerRingPoint();
Polygon sweepPolygon = getPolygonAtIndex(building, 0);
Vertex protrudingPoint = sweepPolygon.getInnerRings().get(0).getVertices().get(2);
Plane targetPlane = planeFromPoints(
new Vector3d(0, 0, 0),
new Vector3d(0, 1, 0),
new Vector3d(0, 0, 1));
SweepUtilities utilities = createSweepUtilities(sweepPolygon, targetPlane);
System.out.println();
System.out.println("=== initialize(): Fall 1 PROJECT klassifizieren ===");
System.out.println("Modell: Wandebene x = 0 mit Fenster-Innenring. Ein Innenringpunkt steht bei x = 0.3 aus der Wand heraus.");
System.out.println("Warum Fall 1: Der Punkt gehoert nur zum Innenring und hat keine abfuehrende Kante.");
System.out.println("Bekannter Testpunkt: Innenring Index 2; in der SweepVertex-Liste daher Index 6.");
printPoint("Testpunkt vor initialize()", protrudingPoint);
utilities.initialize();
SweepVertex sweepVertex = utilities.getSweepVertices().get(6);
System.out.println("Erwartet: 8 SweepVertices, Status PROJECT, 0 abfuehrende Kanten.");
System.out.println("Berechnet: " + utilities.getSweepVertices().size() + " SweepVertices, Status "
+ sweepVertex.getStatus() + ", " + sweepVertex.getOutgoingEdges().size() + " abfuehrende Kanten.");
System.out.println();
assertEquals(protrudingPoint, sweepVertex.getSweepVertex());
assertEquals(SweepVertexStatus.PROJECT, sweepVertex.getStatus());
assertEquals(0, sweepVertex.getOutgoingEdges().size());
assertEquals(0.3, protrudingPoint.getX(), EPS);
}
/**
* initialize(): Im Fall-2-Verkürzungsmodell hat der bekannte Außenringpunkt
* genau eine abführende Kante. initialize() muss ihn deshalb als SWEEP
* klassifizieren.
*/
@Test
public void initializeClassifiesKnownCase2PointAsSweep() {
Building building = SyntheticSweepPlanarityModelFactory.createSweepCase2ShorteningEdge();
Polygon sweepPolygon = getPolygonAtIndex(building, 0);
Vertex sweepPoint = sweepPolygon.getExteriorRing().getVertices().get(1);
Plane targetPlane = planeFromPoints(
new Vector3d(0, 0, 0),
new Vector3d(0, 1, 0),
new Vector3d(0, 0, 1));
SweepUtilities utilities = createSweepUtilities(sweepPolygon, targetPlane);
System.out.println();
System.out.println("=== initialize(): Fall 2 SWEEP klassifizieren ===");
System.out.println("Modell: Außenringpunkt bei x = 1 ist mit genau einer abführenden Dreieckskante verbunden.");
System.out.println("Warum Fall 2: Der Punkt liegt nicht auf x = 0 und hat genau eine abführende Kante.");
System.out.println("Bekannter Testpunkt: Außenring Index 1; in der SweepVertex-Liste daher Index 1.");
printPoint("Testpunkt vor initialize()", sweepPoint);
utilities.initialize();
SweepVertex sweepVertex = utilities.getSweepVertices().get(1);
System.out.println("Erwartet: Status SWEEP, 1 abführende Kante.");
System.out.println("Berechnet: Status " + sweepVertex.getStatus() + ", "
+ sweepVertex.getOutgoingEdges().size() + " abführende Kante(n).");
System.out.println();
assertEquals(sweepPoint, sweepVertex.getSweepVertex());
assertEquals(SweepVertexStatus.SWEEP, sweepVertex.getStatus());
assertEquals(1, sweepVertex.getOutgoingEdges().size());
}
/**
* initialize(): Im Fall-2-Verkürzungsmodell liegt der Außenringpunkt an Index 0
* bereits auf der Zielebene x = 0. initialize() muss ihn daher als ON_PLANE
* klassifizieren.
*/
@Test
public void initializeClassifiesKnownPointOnTargetPlaneAsOnPlane() {
Building building = SyntheticSweepPlanarityModelFactory.createSweepCase2ShorteningEdge();
Polygon sweepPolygon = getPolygonAtIndex(building, 0);
Vertex pointOnPlane = sweepPolygon.getExteriorRing().getVertices().get(0);
Plane targetPlane = planeFromPoints(
new Vector3d(0, 0, 0),
new Vector3d(0, 1, 0),
new Vector3d(0, 0, 1));
SweepUtilities utilities = createSweepUtilities(sweepPolygon, targetPlane);
System.out.println();
System.out.println("=== initialize(): Punkt auf Zielebene als ON_PLANE klassifizieren ===");
System.out.println("Modell: Außenringpunkt Index 0 liegt bereits in der Wandebene x = 0.");
System.out.println("Warum ON_PLANE: Der Punkt hat Abstand 0 zur Zielebene und muss nicht gesweept werden.");
System.out.println("Bekannter Testpunkt: Außenring Index 0; in der SweepVertex-Liste daher Index 0.");
printPoint("Testpunkt vor initialize()", pointOnPlane);
utilities.initialize();
SweepVertex sweepVertex = utilities.getSweepVertices().get(0);
System.out.println("Erwartet: Status ON_PLANE, Abstand zur Zielebene 0.");
System.out.println("Berechnet: Status " + sweepVertex.getStatus() + ", Abstand "
+ targetPlane.getDistance(pointOnPlane) + ".");
System.out.println();
assertEquals(pointOnPlane, sweepVertex.getSweepVertex());
assertEquals(SweepVertexStatus.ON_PLANE, sweepVertex.getStatus());
assertEquals(0, targetPlane.getDistance(pointOnPlane), EPS);
}
/**
* calculate(): Im Fall-2-Verkürzungsmodell liegt der Sweep-Punkt im Außenring
* an Index 1. calculate() soll den neuen Schnittpunkt mit der aus drei Punkten
* konstruierten Zielebene x = 0 berechnen, den echten Vertex aber noch nicht
* verschieben.
*/
@Test
public void calculateComputesNewPointButDoesNotMoveKnownCase2Point() {
Building building = SyntheticSweepPlanarityModelFactory.createSweepCase2ShorteningEdge();
Polygon sweepPolygon = getPolygonAtIndex(building, 0);
Vertex sweepPoint = sweepPolygon.getExteriorRing().getVertices().get(1);
Plane targetPlane = planeFromPoints(
new Vector3d(0, 0, 0),
new Vector3d(0, 1, 0),
new Vector3d(0, 0, 1));
SweepUtilities utilities = createSweepUtilities(sweepPolygon, targetPlane);
System.out.println();
System.out.println("=== calculate(): Fall 2 Schnittpunkt berechnen, Vertex noch nicht bewegen ===");
System.out.println("Modell: Sweep-Punkt liegt bei x = 1; seine einzige abfuehrende Kante laeuft durch die Zielebene x = 0.");
System.out.println("Warum Fall 2: Genau eine abfuehrende Kante, also Schnitt Gerade/Ebene statt Orthogonalprojektion.");
System.out.println("Bekannter Testpunkt: Aussenring Index 1; in der SweepVertex-Liste daher Index 1.");
printPoint("Originalpunkt vor calculate()", sweepPoint);
utilities.initialize();
SweepVertex sweepVertex = utilities.getSweepVertices().get(1);
assertTrue(utilities.calculate());
System.out.println("Erwartet: Status SWEEP, 1 abfuehrende Kante, neuer Punkt auf x = 0.");
System.out.println("Berechnet: Status " + sweepVertex.getStatus() + ", "
+ sweepVertex.getOutgoingEdges().size() + " abfuehrende Kante(n).");
printPoint("Berechneter neuer Punkt nach calculate()", sweepVertex.getNewPoint());
printPoint("Originalpunkt nach calculate() - soll noch unveraendert sein", sweepPoint);
System.out.println();
assertEquals(sweepPoint, sweepVertex.getSweepVertex());
assertEquals(SweepVertexStatus.SWEEP, sweepVertex.getStatus());
assertEquals(1, sweepVertex.getOutgoingEdges().size());
assertNotNull(sweepVertex.getNewPoint());
assertEquals(0, sweepVertex.getNewPoint().getX(), EPS);
assertEquals(1, sweepPoint.getX(), EPS);
}
/**
* calculate(): Im Fall-1-Fenstermodell besitzt der abstehende Innenringpunkt
* keine abführende Kante. calculate() soll deshalb einen orthogonal projizierten
* newPoint berechnen, aber den echten Vertex noch nicht verschieben.
*/
@Test
public void calculateComputesProjectedPointButDoesNotMoveKnownCase1Point() {
Building building = SyntheticSweepPlanarityModelFactory.createWallWithProtrudingWindowInnerRingPoint();
Polygon sweepPolygon = getPolygonAtIndex(building, 0);
Vertex protrudingPoint = sweepPolygon.getInnerRings().get(0).getVertices().get(2);
Plane targetPlane = planeFromPoints(
new Vector3d(0, 0, 0),
new Vector3d(0, 1, 0),
new Vector3d(0, 0, 1));
SweepUtilities utilities = createSweepUtilities(sweepPolygon, targetPlane);
System.out.println();
System.out.println("=== calculate(): Fall 1 Projektionspunkt berechnen, Vertex noch nicht bewegen ===");
System.out.println("Modell: Innenringpunkt steht bei x = 0.3 aus der Wandebene x = 0 heraus.");
System.out.println("Warum Fall 1: Keine abführende Kante, also Orthogonalprojektion auf die Zielebene.");
System.out.println("Bekannter Testpunkt: Innenring Index 2; in der SweepVertex-Liste daher Index 6.");
printPoint("Originalpunkt vor calculate()", protrudingPoint);
utilities.initialize();
SweepVertex sweepVertex = utilities.getSweepVertices().get(6);
assertTrue(utilities.calculate());
System.out.println("Erwartet: Status PROJECT, 0 abführende Kanten, neuer Punkt auf x = 0.");
System.out.println("Berechnet: Status " + sweepVertex.getStatus() + ", "
+ sweepVertex.getOutgoingEdges().size() + " abführende Kante(n).");
printPoint("Berechneter neuer Punkt nach calculate()", sweepVertex.getNewPoint());
printPoint("Originalpunkt nach calculate() - soll noch unverändert sein", protrudingPoint);
System.out.println();
assertEquals(protrudingPoint, sweepVertex.getSweepVertex());
assertEquals(SweepVertexStatus.PROJECT, sweepVertex.getStatus());
assertEquals(0, sweepVertex.getOutgoingEdges().size());
assertNotNull(sweepVertex.getNewPoint());
assertEquals(0, sweepVertex.getNewPoint().getX(), EPS);
assertEquals(0.3, protrudingPoint.getX(), EPS);
}
/**
* calculate(): Ein Punkt, der bereits auf der Zielebene liegt, bleibt ON_PLANE.
* calculate() soll für ihn keinen newPoint erzeugen und den Originalvertex nicht
* bewegen.
*/
@Test
public void calculateLeavesKnownPointOnTargetPlaneWithoutNewPoint() {
Building building = SyntheticSweepPlanarityModelFactory.createSweepCase2ShorteningEdge();
Polygon sweepPolygon = getPolygonAtIndex(building, 0);
Vertex pointOnPlane = sweepPolygon.getExteriorRing().getVertices().get(0);
Plane targetPlane = planeFromPoints(
new Vector3d(0, 0, 0),
new Vector3d(0, 1, 0),
new Vector3d(0, 0, 1));
SweepUtilities utilities = createSweepUtilities(sweepPolygon, targetPlane);
System.out.println();
System.out.println("=== calculate(): ON_PLANE-Punkt braucht keinen neuen Punkt ===");
System.out.println("Modell: Außenringpunkt Index 0 liegt schon auf x = 0.");
System.out.println("Warum kein newPoint: ON_PLANE bedeutet, dass keine Projektion und kein Kantensweep nötig ist.");
System.out.println("Bekannter Testpunkt: Außenring Index 0; in der SweepVertex-Liste daher Index 0.");
printPoint("Originalpunkt vor calculate()", pointOnPlane);
utilities.initialize();
SweepVertex sweepVertex = utilities.getSweepVertices().get(0);
assertTrue(utilities.calculate());
System.out.println("Erwartet: Status ON_PLANE, newPoint null, Originalpunkt weiterhin auf x = 0.");
System.out.println("Berechnet: Status " + sweepVertex.getStatus() + ", newPoint "
+ sweepVertex.getNewPoint() + ".");
printPoint("Originalpunkt nach calculate()", pointOnPlane);
System.out.println();
assertEquals(pointOnPlane, sweepVertex.getSweepVertex());
assertEquals(SweepVertexStatus.ON_PLANE, sweepVertex.getStatus());
assertNull(sweepVertex.getNewPoint());
assertEquals(0, pointOnPlane.getX(), EPS);
assertEquals(0, targetPlane.getDistance(pointOnPlane), EPS);
}
/**
* doHeal(): Nach erfolgreichem initialize() und calculate() wird der bekannte
* Fall-2-Sweep-Punkt aus Außenring-Index 1 tatsächlich auf den berechneten
* Schnittpunkt der Zielebene x = 0 gesetzt.
*/
@Test
public void doHealMovesKnownCase2PointToCalculatedPoint() {
Building building = SyntheticSweepPlanarityModelFactory.createSweepCase2ShorteningEdge();
Polygon sweepPolygon = getPolygonAtIndex(building, 0);
Vertex sweepPoint = sweepPolygon.getExteriorRing().getVertices().get(1);
Plane targetPlane = planeFromPoints(
new Vector3d(0, 0, 0),
new Vector3d(0, 1, 0),
new Vector3d(0, 0, 1));
SweepUtilities utilities = createSweepUtilities(sweepPolygon, targetPlane);
System.out.println();
System.out.println("=== doHeal(): berechneten Fall-2-Punkt auf den echten Vertex schreiben ===");
System.out.println("Modell: Derselbe Fall-2-Verkuerzungsfall wie im calculate-Test.");
System.out.println(" calculate() berechnet nur newPoint; doHeal() veraendert erst die Vertex-Koordinaten.");
System.out.println("Erwartet: Der Originalpunkt wechselt von x = 1 auf die Zielebene x = 0.");
printPoint("Originalpunkt vor doHeal()", sweepPoint);
utilities.initialize();
assertTrue(utilities.calculate());
utilities.doHeal();
printPoint("Originalpunkt nach doHeal()", sweepPoint);
System.out.println();
assertEquals(0, sweepPoint.getX(), EPS);
assertEquals(0, targetPlane.getDistance(sweepPoint), EPS);
}
/**
* doHeal(): Im Fall-1-Fenstermodell wird der zuvor berechnete Projektionspunkt
* tatsächlich auf den abstehenden Innenring-Vertex geschrieben.
*/
@Test
public void doHealMovesKnownCase1PointByProjection() {
Building building = SyntheticSweepPlanarityModelFactory.createWallWithProtrudingWindowInnerRingPoint();
Polygon sweepPolygon = getPolygonAtIndex(building, 0);
Vertex protrudingPoint = sweepPolygon.getInnerRings().get(0).getVertices().get(2);
Plane targetPlane = planeFromPoints(
new Vector3d(0, 0, 0),
new Vector3d(0, 1, 0),
new Vector3d(0, 0, 1));
SweepUtilities utilities = createSweepUtilities(sweepPolygon, targetPlane);
System.out.println();
System.out.println("=== doHeal(): berechneten Fall-1-Projektionspunkt auf den echten Vertex schreiben ===");
System.out.println("Modell: Innenringpunkt steht bei x = 0.3 aus der Wandebene x = 0 heraus.");
System.out.println(" calculate() berechnet nur newPoint; doHeal() ändert erst die Vertex-Koordinaten.");
System.out.println("Erwartet: Der Originalpunkt wechselt von x = 0.3 auf die Zielebene x = 0.");
printPoint("Originalpunkt vor doHeal()", protrudingPoint);
utilities.initialize();
assertTrue(utilities.calculate());
utilities.doHeal();
printPoint("Originalpunkt nach doHeal()", protrudingPoint);
System.out.println();
assertEquals(0, protrudingPoint.getX(), EPS);
assertEquals(0, targetPlane.getDistance(protrudingPoint), EPS);
}
/**
* doHeal(): Ein ON_PLANE-Punkt hat keinen newPoint. doHeal() darf ihn deshalb
* nicht verändern.
*/
@Test
public void doHealLeavesKnownPointOnTargetPlaneUnchanged() {
Building building = SyntheticSweepPlanarityModelFactory.createSweepCase2ShorteningEdge();
Polygon sweepPolygon = getPolygonAtIndex(building, 0);
Vertex pointOnPlane = sweepPolygon.getExteriorRing().getVertices().get(0);
Plane targetPlane = planeFromPoints(
new Vector3d(0, 0, 0),
new Vector3d(0, 1, 0),
new Vector3d(0, 0, 1));
SweepUtilities utilities = createSweepUtilities(sweepPolygon, targetPlane);
System.out.println();
System.out.println("=== doHeal(): ON_PLANE-Punkt unverändert lassen ===");
System.out.println("Modell: Außenringpunkt Index 0 liegt schon auf der Zielebene x = 0.");
System.out.println("calculate() berechnet keinen newPoint; doHeal() soll daher keine Vertex-Koordinaten ändern.");
System.out.println("Erwartet: Der Originalpunkt bleibt auf x = 0.");
printPoint("Originalpunkt vor doHeal()", pointOnPlane);
utilities.initialize();
assertTrue(utilities.calculate());
utilities.doHeal();
printPoint("Originalpunkt nach doHeal()", pointOnPlane);
System.out.println();
assertEquals(0, pointOnPlane.getX(), EPS);
assertEquals(0, targetPlane.getDistance(pointOnPlane), EPS);
}
private SweepUtilities createSweepUtilities(Polygon sweepPolygon, Plane targetPlane) {
NonPlanarPolygonDistancePlaneError error = new NonPlanarPolygonDistancePlaneError(sweepPolygon, 0, null,
targetPlane);
return new SweepUtilities(error);
}
private Polygon getPolygonAtIndex(Building building, int index) {
Geometry geometry = building.getGeometries().get(0);
return geometry.getPolygons().get(index);
}
private Plane planeFromPoints(Vector3d p1, Vector3d p2, Vector3d p3) {
Vector3d normal = p2.minus(p1).cross(p3.minus(p1));
return new Plane(normal, p1);
}
private void printPoint(String label, Vector3d point) {
System.out.println(label + ": x=" + point.getX() + ", y=" + point.getY() + ", z=" + point.getZ());
}
}
......@@ -79,8 +79,8 @@ public class SweepUtilitiesSyntheticTest {
/**
* Fehlerfall: Der Schnitt mit x = 0 liegt vor oder zu nah am referencePoint.
* Vgl. rote Bereich aus der Skizze vom Margitta ist (vorerst?) nicht
* sweepbar.
* Vgl. rote Bereich aus der Skizze von Margitta- ist nicht
* sweepbar : "Die Kante kippt"
*/
@Test
public void rejectsCase2WhenIntersectionIsBeforeReferencePoint() {
......@@ -113,6 +113,7 @@ public class SweepUtilitiesSyntheticTest {
targetPlane);
SweepUtilities utilities = new SweepUtilities(error);
utilities.initialize();
if (!utilities.calculate()) {
return 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