Commit caf5d65b authored by Numanoglu's avatar Numanoglu
Browse files

Add regression plane sweep tests

parent 2799ce51
Pipeline #12433 failed with stage
in 2 minutes and 20 seconds
package de.hft.stuttgart.citydoctor2.healing;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import java.util.List;
import org.junit.Test;
import de.hft.stuttgart.citydoctor2.check.CheckResult;
import de.hft.stuttgart.citydoctor2.check.ResultStatus;
import de.hft.stuttgart.citydoctor2.check.error.NonPlanarPolygonDistancePlaneError;
import de.hft.stuttgart.citydoctor2.checks.geometry.PlanarCheck;
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.OrthogonalRegressionPlane;
import de.hft.stuttgart.citydoctor2.math.Plane;
import de.hft.stuttgart.citydoctor2.math.Vector3d;
public class SweepUtilitiesRegressionPlaneTest {
private static final double ASSERTION_EPS = 0.000001;
private static final double POINT_ON_PLANE_TOLERANCE = 0.001;
private static final double SWEEP_EDGE_AND_ANGLE_TOLERANCE = 0.001;
/**
* Würfelmodell : Ein Deckelpunkt steht nach oben ab.
* Die ZielEbene wird hier als Regressionsebene aus allen
* eindeutigen Punkten der fehlerhaften Sweep-Fazette berechnet.
* Dadurch wird
* keine perfekte Würfeldeckelebene erzwungen, sondern die beste Ausgleichsebene
* der gesamten nicht-planaren Fazette verwendet.
*/
@Test
public void sweepsRaisedCubePointToRegressionPlaneOfSweepFacet() {
Building building = SyntheticSweepPlanarityModelFactory.createRaisedTopCornerCube();
Polygon sweepPolygon = getPolygonAtIndex(building, 5);
Vertex raisedPoint = sweepPolygon.getExteriorRing().getVertices().get(2);
Plane targetPlane = regressionPlaneFromVertices(
sweepPolygon.getExteriorRing().getVertices().get(0),
sweepPolygon.getExteriorRing().getVertices().get(1),
sweepPolygon.getExteriorRing().getVertices().get(2),
sweepPolygon.getExteriorRing().getVertices().get(3));
SweepUtilities utilities = createSweepUtilities(sweepPolygon, targetPlane);
System.out.println();
System.out.println("=== Regressionsebene aus ganzer Sweep-Fazette: abstehender Würfel-Deckelpunkt ===");
System.out.println("Modell: Deckelfläche hat drei Punkte bei z = 4 und einen abstehenden Punkt bei z = 5.");
System.out.println("ZielEbene: Orthogonale Regression aus allen vier eindeutigen Deckelpunkten.");
System.out.println("Erwartung: Der abstehende Punkt wird auf diese berechnete Ausgleichsebene gesweept.");
printPoint("Abstehender Deckelpunkt vor doHeal()", raisedPoint);
System.out.println("Abstand des Punktes zur Regressionsebene vor doHeal(): " + targetPlane.getDistance(raisedPoint));
utilities.initialize();
assertTrue(utilities.calculate());
utilities.doHeal();
printPoint("Abstehender Deckelpunkt nach doHeal()", raisedPoint);
System.out.println("Abstand des Punktes zur Regressionsebene nach doHeal(): " + targetPlane.getDistance(raisedPoint));
System.out.println();
assertEquals(0, targetPlane.getDistance(raisedPoint), ASSERTION_EPS);
}
/**
* Produktivpfad für die ZielEbene: Nicht der Test baut die Ebene selbst,
* sondern PlanarCheck berechnet die Regressionsebene und steckt sie in den
* NonPlanarPolygonDistancePlaneError. SweepUtilities muss dann genau diese
* Error-Ebene verwenden.
*/
@Test
public void sweepsRaisedCubePointUsingRegressionPlaneFromPlanarCheck() {
Building building = SyntheticSweepPlanarityModelFactory.createRaisedTopCornerCube();
Polygon sweepPolygon = getPolygonAtIndex(building, 5);
Vertex raisedPoint = sweepPolygon.getExteriorRing().getVertices().get(2);
NonPlanarPolygonDistancePlaneError error = errorFromPlanarCheck(sweepPolygon);
SweepUtilities utilities = createSweepUtilities(error);
System.out.println();
System.out.println("=== Produktivpfad: PlanarCheck liefert die Regressionsebene für den Sweep ===");
System.out.println("Modell: Würfel-Deckelfläche mit einem abstehenden Punkt bei z = 5.");
System.out.println("ZielEbene: kommt aus NonPlanarPolygonDistancePlaneError.getPlane(), erzeugt durch PlanarCheck.");
System.out.println("Erwartung: Der Punkt wird auf genau diese vom Check berechnete Ebene gesweept.");
printPoint("Abstehender Deckelpunkt vor doHeal()", raisedPoint);
System.out.println("Abstand zur PlanarCheck-Ebene vor doHeal(): " + error.getPlane().getDistance(raisedPoint));
utilities.initialize();
assertTrue(utilities.calculate());
utilities.doHeal();
printPoint("Abstehender Deckelpunkt nach doHeal()", raisedPoint);
System.out.println("Abstand zur PlanarCheck-Ebene nach doHeal(): " + error.getPlane().getDistance(raisedPoint));
System.out.println();
assertEquals(0, error.getPlane().getDistance(raisedPoint), ASSERTION_EPS);
}
/**
* Gleicher Würfel, aber andere ZielEbenen-Strategie: Der abstehende Punkt ist
* im Test bewusst als fehlerhafter Sweep-Punkt bekannt.
* Die Regressionsebene wird deshalb
* nur aus den drei stabilen Deckelpunkten berechnet.
* Bei diesem einfachen Würfel ergibt das die naheliegende perfekte Deckelebene z = 4.
*/
@Test
public void sweepsRaisedCubePointToRegressionPlaneOfStableFacetVertices() {
Building building = SyntheticSweepPlanarityModelFactory.createRaisedTopCornerCube();
Polygon sweepPolygon = getPolygonAtIndex(building, 5);
Vertex raisedPoint = sweepPolygon.getExteriorRing().getVertices().get(2);
Plane targetPlane = regressionPlaneFromVertices(
sweepPolygon.getExteriorRing().getVertices().get(0),
sweepPolygon.getExteriorRing().getVertices().get(1),
sweepPolygon.getExteriorRing().getVertices().get(3));
SweepUtilities utilities = createSweepUtilities(sweepPolygon, targetPlane);
System.out.println();
System.out.println("=== Regressionsebene aus stabilen Deckelpunkten: abstehender Würfel-Deckelpunkt ===");
System.out.println("Modell: Der Punkt bei (4,4,5) wird als fehlerhafter Sweep-Punkt verstanden.");
System.out.println("ZielEbene: Orthogonale Regression aus den drei übrigen Deckelpunkten bei z = 4.");
System.out.println("Erwartung: Der Punkt landet auf der perfekten Deckelebene z = 4.");
printPoint("Abstehender Deckelpunkt vor doHeal()", raisedPoint);
utilities.initialize();
assertTrue(utilities.calculate());
utilities.doHeal();
printPoint("Abstehender Deckelpunkt nach doHeal()", raisedPoint);
System.out.println("Abstand des Punktes zur stabilen Regressionsebene nach doHeal(): "
+ targetPlane.getDistance(raisedPoint));
System.out.println();
assertEquals(4, raisedPoint.getZ(), ASSERTION_EPS);
assertEquals(0, targetPlane.getDistance(raisedPoint), ASSERTION_EPS);
}
/**
* Entarteter Würfel: Der fehlerhafte Deckelpunkt liegt unter seinem
* angrenzenden Vertikalpunkt. Die Regressionsebene aus den stabilen
* Deckelpunkten ist weiterhin z = 4. Der Schnitt der abführenden Kante mit
* dieser Ebene liegt jedoch vor dem referencePoint; die Kante würde also kippen
* und der aktuelle Fall-2-Sweep muss ablehnen.
*/
@Test
public void rejectsDegeneratedCubeWhenStableRegressionPlaneWouldFlipOutgoingEdge() {
Building building = SyntheticSweepPlanarityModelFactory.createNonPlanarCube(4, 0, -5);
Polygon sweepPolygon = getPolygonAtIndex(building, 2);
Vertex degeneratedPoint = sweepPolygon.getExteriorRing().getVertices().get(1);
Plane targetPlane = regressionPlaneFromVertices(
sweepPolygon.getExteriorRing().getVertices().get(0),
sweepPolygon.getExteriorRing().getVertices().get(2),
sweepPolygon.getExteriorRing().getVertices().get(3));
SweepUtilities utilities = createSweepUtilities(sweepPolygon, targetPlane);
System.out.println();
System.out.println("=== Regressionsebene aus stabilen Deckelpunkten: entarteter Würfel-Kippfall ===");
System.out.println("Modell: Der bekannte Deckelpunkt liegt bei z = -1 unter seinem angrenzenden Vertikalpunkt.");
System.out.println("ZielEbene: Regression aus den übrigen Deckelpunkten bei z = 4.");
System.out.println("Erwartung: calculate() lehnt ab, weil der Schnitt vor dem referencePoint liegt.");
printPoint("Entarteter Deckelpunkt vor calculate()", degeneratedPoint);
utilities.initialize();
assertFalse(utilities.calculate());
printPoint("Entarteter Deckelpunkt nach calculate()", degeneratedPoint);
System.out.println("Berechnet: calculate() == false, Punkt bleibt unverändert.");
System.out.println();
assertEquals(-1, degeneratedPoint.getZ(), ASSERTION_EPS);
}
/**
* Kleiner Fall-2-Kippfall ohne Würfel: Die stabile Regressionsebene wird aus
* drei Punkten der Sweep-Fazette berechnet und entspricht x = 0. Die
* abführende Kante des bekannten Sweep-Punktes startet aber bei x = 0.5 und
* läuft zu x = 1. Der Schnitt mit x = 0 liegt deshalb vor dem referencePoint.
*/
@Test
public void rejectsCase2WhenStableRegressionPlaneWouldFlipOutgoingEdge() {
Building building = SyntheticSweepPlanarityModelFactory.createSweepCase2IntersectionBeforeReferencePoint();
Polygon sweepPolygon = getPolygonAtIndex(building, 0);
Vertex sweepPoint = sweepPolygon.getExteriorRing().getVertices().get(1);
Plane targetPlane = regressionPlaneFromVertices(
sweepPolygon.getExteriorRing().getVertices().get(0),
sweepPolygon.getExteriorRing().getVertices().get(2),
sweepPolygon.getExteriorRing().getVertices().get(3));
SweepUtilities utilities = createSweepUtilities(sweepPolygon, targetPlane);
System.out.println();
System.out.println("=== Regressionsebene aus stabilen Fazettenpunkten: kleiner Fall-2-Kippfall ===");
System.out.println("Modell: Sweep-Punkt bei x = 1, referencePoint der abführenden Kante bei x = 0.5.");
System.out.println("ZielEbene: Regression aus drei stabilen Punkten der Sweep-Fazette; geometrisch x = 0.");
System.out.println("Erwartung: calculate() lehnt ab, weil der Schnittpunkt bei x = 0 vor dem referencePoint liegt.");
printPoint("Sweep-Punkt vor calculate()", sweepPoint);
utilities.initialize();
assertFalse(utilities.calculate());
printPoint("Sweep-Punkt nach calculate()", sweepPoint);
System.out.println("Berechnet: calculate() == false, Punkt bleibt unverändert.");
System.out.println();
assertEquals(1, sweepPoint.getX(), ASSERTION_EPS);
}
/**
* Produktivpfad für einen Kippfall: PlanarCheck berechnet die Regressionsebene
* aus dem nicht-planaren Polygon und liefert sie über den Error. Der Sweep
* bekommt also keine manuell gesetzte ZielEbene. Die Kante darf nicht geheilt
* werden, wenn der daraus berechnete Schnitt vor dem referencePoint liegt.
*/
@Test
public void rejectsCase2WhenRegressionPlaneFromPlanarCheckWouldFlipOutgoingEdge() {
Building building = SyntheticSweepPlanarityModelFactory.createSweepCase2IntersectionBeforeReferencePoint();
Polygon sweepPolygon = getPolygonAtIndex(building, 0);
Vertex sweepPoint = sweepPolygon.getExteriorRing().getVertices().get(1);
NonPlanarPolygonDistancePlaneError error = errorFromPlanarCheck(sweepPolygon);
SweepUtilities utilities = createSweepUtilities(error);
System.out.println();
System.out.println("=== Produktivpfad: PlanarCheck-Ebene im kleinen Fall-2-Kippfall ===");
System.out.println("Modell: Sweep-Punkt bei x = 1, referencePoint der abführenden Kante bei x = 0.5.");
System.out.println("ZielEbene: kommt aus NonPlanarPolygonDistancePlaneError.getPlane(), erzeugt durch PlanarCheck.");
System.out.println("Erwartung: calculate() lehnt ab, wenn der Schnitt vor dem referencePoint liegt.");
printPoint("Sweep-Punkt vor calculate()", sweepPoint);
System.out.println("Abstand zur PlanarCheck-Ebene vor calculate(): " + error.getPlane().getDistance(sweepPoint));
utilities.initialize();
assertFalse(utilities.calculate());
printPoint("Sweep-Punkt nach calculate()", sweepPoint);
System.out.println("Berechnet: calculate() == false, Punkt bleibt unverändert.");
System.out.println();
assertEquals(1, sweepPoint.getX(), ASSERTION_EPS);
}
private SweepUtilities createSweepUtilities(Polygon sweepPolygon, Plane targetPlane) {
NonPlanarPolygonDistancePlaneError error = new NonPlanarPolygonDistancePlaneError(sweepPolygon, 0, null,
targetPlane);
return createSweepUtilities(error);
}
private SweepUtilities createSweepUtilities(NonPlanarPolygonDistancePlaneError error) {
return new SweepUtilities(error, POINT_ON_PLANE_TOLERANCE, SWEEP_EDGE_AND_ANGLE_TOLERANCE);
}
private Polygon getPolygonAtIndex(Building building, int index) {
Geometry geometry = building.getGeometries().get(0);
return geometry.getPolygons().get(index);
}
private NonPlanarPolygonDistancePlaneError errorFromPlanarCheck(Polygon sweepPolygon) {
PlanarCheck check = new PlanarCheck();
check.check(sweepPolygon);
CheckResult checkResult = sweepPolygon.getCheckResult(check);
assertNotNull(checkResult);
assertEquals(ResultStatus.ERROR, checkResult.getResultStatus());
assertTrue(checkResult.getError() instanceof NonPlanarPolygonDistancePlaneError);
return (NonPlanarPolygonDistancePlaneError) checkResult.getError();
}
private Plane regressionPlaneFromVertices(Vertex... vertices) {
List<Vertex> regressionVertices = List.of(vertices);
return OrthogonalRegressionPlane.calculateOrthogonalRegressionPlane(regressionVertices);
}
private void printPoint(String label, Vector3d point) {
System.out.println(label + ": x=" + point.getX() + ", y=" + point.getY() + ", z=" + point.getZ());
}
}
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