Commit aeae0705 authored by Numanoglu's avatar Numanoglu
Browse files

Fix outgoing edge collection for sweep healing

parent 4c8cddf2
Pipeline #12464 passed with stage
in 2 minutes and 47 seconds
......@@ -8,6 +8,7 @@ import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import de.hft.stuttgart.citydoctor2.check.error.NonPlanarPolygonDistancePlaneError;
import de.hft.stuttgart.citydoctor2.datastructure.CityObject;
import de.hft.stuttgart.citydoctor2.datastructure.LinearRing;
import de.hft.stuttgart.citydoctor2.datastructure.Polygon;
import de.hft.stuttgart.citydoctor2.datastructure.Vertex;
......@@ -21,6 +22,7 @@ public class SweepUtilities {
private Plane targetPlane;
private Polygon errorPolygon;
private CityObject topLevelFeature;
private double minSweepEdgeLength = 0.01;
private double pointOnPlaneTolerance = 0.00011;
......@@ -42,6 +44,7 @@ public class SweepUtilities {
targetPlane = err.getPlane();
errorPolygon = err.getPolygon();
topLevelFeature = findTopLevelFeature(errorPolygon);
}
......@@ -59,7 +62,7 @@ public class SweepUtilities {
collectSweepVertices(errorPolygon);
for (SweepVertex v : sweepVertices) {
v.collectOutgoingEdges(errorPolygon, minSweepEdgeLength);
v.collectOutgoingEdges(topLevelFeature, errorPolygon, minSweepEdgeLength, pointOnPlaneTolerance);
v.initStatus(targetPlane, pointOnPlaneTolerance, true);
}
......@@ -207,7 +210,6 @@ public class SweepUtilities {
Vector3d thirdPoint = multiVerts.get(2).getSweepVertex();
Vector3d firstDirection = secondPoint.minus(firstPoint);
Vector3d secondDirection = thirdPoint.minus(firstPoint);
// Normalieren?!
Vector3d newNormal = firstDirection.cross(secondDirection);
if (newNormal.getLength() < pointOnPlaneTolerance) {
......@@ -267,4 +269,12 @@ public class SweepUtilities {
return sweepVertices;
}
// Kaskadiert via Gettres bis ganz nach oben
private CityObject findTopLevelFeature(Polygon polygon) {
if (polygon == null || polygon.getParent() == null || polygon.getParent().getParent() == null) {
return null;
}
return polygon.getParent().getParent().getTopLevelCityObject();
}
}
package de.hft.stuttgart.citydoctor2.healing;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import de.hft.stuttgart.citydoctor2.check.CheckableUtilsVisitor;
import de.hft.stuttgart.citydoctor2.datastructure.CityObject;
import de.hft.stuttgart.citydoctor2.datastructure.Vertex;
import de.hft.stuttgart.citydoctor2.datastructure.Edge;
import de.hft.stuttgart.citydoctor2.datastructure.Geometry;
import de.hft.stuttgart.citydoctor2.datastructure.LinearRing;
import de.hft.stuttgart.citydoctor2.datastructure.Polygon;
import de.hft.stuttgart.citydoctor2.math.Plane;
import de.hft.stuttgart.citydoctor2.math.Vector3d;
......@@ -72,6 +76,47 @@ public class SweepVertex {
}
}
public void collectOutgoingEdges(CityObject topLevelFeature, Polygon errorPoly, double minSweepEdgeLength,
double pointTolerance) {
if (topLevelFeature == null) {
collectOutgoingEdges(errorPoly, minSweepEdgeLength);
return;
}
outgoingEdges.clear();
if (errorPoly == null) {
logger.error("Cannot collect outgoing edges: polygon is null");
status = SweepVertexStatus.ERROR;
return;
}
if (pointTolerance <= 0) {
logger.error("Cannot collect outgoing edges: pointTolerance must be positive");
status = SweepVertexStatus.ERROR;
return;
}
for (Geometry geometry : collectGeometries(topLevelFeature)) {
if (geometry.getEdges() == null) {
geometry.updateEdgesAndVertices();
}
for (Edge edge : geometry.getEdges()) {
Vertex oppositeVertex = findOppositeEndpointAtSweepVertex(edge, pointTolerance);
if (oppositeVertex == null) {
continue;
}
if (isEdgeOfErrorPolygon(edge, errorPoly, pointTolerance)) {
continue;
}
if (sweepVertex.getDistance(oppositeVertex) < minSweepEdgeLength) {
logger.error("Cannot collect outgoing edges: outgoing edge is shorter than minSweepEdgeLength");
status = SweepVertexStatus.ERROR;
return;
}
addOutgoingEdgeIfMissing(new Edge(sweepVertex, oppositeVertex), pointTolerance);
}
}
}
public void initStatus (Plane targetPlane, double pointOnPlaneTolerance, boolean bWithMulti) {
if (targetPlane == null) {
......@@ -200,4 +245,96 @@ public class SweepVertex {
return newPoint != null;
}
/**
* Sammelt alle Geometrien des Top-Level-Features. Damit sieht der Sweep auch
* Nachbarflächen, die in CityGML nicht in derselben Geometry wie das
* Fehlerpolygon liegen.
*/
private List<Geometry> collectGeometries(CityObject topLevelFeature) {
List<Geometry> geometries = new ArrayList<>();
topLevelFeature.accept(new CheckableUtilsVisitor() {
@Override
public void check(Geometry geometry) {
geometries.add(geometry);
}
});
return geometries;
}
/**
* Prüft, ob genau ein Endpunkt der Kante koordinatengleich zum Sweep-Punkt ist.
* Falls ja, wird der andere Endpunkt als Richtungspunkt der abführenden Kante
* geliefert.
*/
private Vertex findOppositeEndpointAtSweepVertex(Edge edge, double pointTolerance) {
boolean fromIsSweepVertex = edge.getFrom().equalsWithEpsilon(sweepVertex, pointTolerance);
boolean toIsSweepVertex = edge.getTo().equalsWithEpsilon(sweepVertex, pointTolerance);
if (fromIsSweepVertex == toIsSweepVertex) {
return null;
}
return fromIsSweepVertex ? edge.getTo() : edge.getFrom();
}
/**
* Erkennt Kanten, die zur Fehlerfläche selbst gehören. Diese sind nicht
* abführend, auch wenn sie am Sweep-Punkt liegen.
*/
private boolean isEdgeOfErrorPolygon(Edge edge, Polygon errorPoly, double pointTolerance) {
if (isEdgeOfRing(edge, errorPoly.getExteriorRing(), pointTolerance)) {
return true;
}
for (LinearRing innerRing : errorPoly.getInnerRings()) {
if (isEdgeOfRing(edge, innerRing, pointTolerance)) {
return true;
}
}
return false;
}
/**
* Prüft eine Kante gegen alle Kanten eines Rings. Die Ringrichtung ist dabei
* egal, weil dieselbe geometrische Kante in Nachbarflächen umgekehrt auftreten
* kann.
*/
private boolean isEdgeOfRing(Edge edge, LinearRing ring, double pointTolerance) {
if (ring == null) {
return false;
}
List<Vertex> vertices = ring.getVertices();
for (int i = 0; i < vertices.size() - 1; i++) {
Vertex first = vertices.get(i);
Vertex second = vertices.get(i + 1);
if (sameUndirectedSegment(edge.getFrom(), edge.getTo(), first, second, pointTolerance)) {
return true;
}
}
return false;
}
/**
* Vergleicht zwei ungerichtete Strecken mit Punkt-Toleranz.
*/
private boolean sameUndirectedSegment(Vertex firstA, Vertex secondA, Vertex firstB, Vertex secondB,
double pointTolerance) {
return firstA.equalsWithEpsilon(firstB, pointTolerance) && secondA.equalsWithEpsilon(secondB, pointTolerance)
|| firstA.equalsWithEpsilon(secondB, pointTolerance)
&& secondA.equalsWithEpsilon(firstB, pointTolerance);
}
/**
* Fügt die gefundene Kante nur einmal hinzu. Die synthetische Edge enthält den
* echten sweepVertex, damit SweepEdge.create später getOppositeVertex verwenden
* kann.
*/
private void addOutgoingEdgeIfMissing(Edge candidate, double pointTolerance) {
Vertex candidateTarget = candidate.getOppositeVertex(sweepVertex);
for (Edge existing : outgoingEdges) {
Vertex existingTarget = existing.getOppositeVertex(sweepVertex);
if (existingTarget != null && existingTarget.equalsWithEpsilon(candidateTarget, pointTolerance)) {
return;
}
}
outgoingEdges.add(candidate);
}
}
package de.hft.stuttgart.citydoctor2.healing;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import java.io.IOException;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.EnumMap;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
......@@ -32,39 +35,28 @@ import de.hft.stuttgart.citydoctor2.parser.InvalidGmlFileException;
/*
* @Numanoglu
*
* WIP !
* */
/**
* Export einzelner Buildings funktioniert noch immer nicht
*
*
* Führt den Planaritätscheck über den normalen CityDoctor-Checker aus.
*
* Wichtig: Der PlanarCheck sollte hier nicht isoliert per
* geometry.accept(check) aufgerufen werden. Der Checker bildet den echten
* Validierungspfad nach: Er initialisiert die Check-Parameter, berücksichtigt
* Abhängigkeiten und bereitet die Geometrien vor. Dadurch entspricht der Test
* eher dem Verhalten aus GUI und regulärer Validierung.
*
* Der Parameter type = both aktiviert beide Planaritätsvarianten:
* distance: Punktabstand zur Regressionsebene, liefert
* GE_P_NON_PLANAR_POLYGON_DISTANCE_PLANE.
* angle: Normalenabweichung der triangulierten Teilflächen, liefert
* GE_P_NON_PLANAR_POLYGON_NORMALS_DEVIATION.
* Der Test unterscheidet rohe Fehler-Einträge und eindeutige Fehler-Fazetten.
* Das ist nötig, weil linked polygons denselben fachlichen Fehler mehrfach in
* die Error-Sammlung bringen können. Deduplication geschieht deshalb über
* Feature-ID, Geometry-ID und Polygon-ID, nicht über Java-Objektidentität.
*
* Für den Sweep ist momentan vor allem der DistancePlane-Fehler relevant,
* weil dieser bereits eine Zielebene im NonPlanarPolygonDistancePlaneError
* mitliefert. Die Normalenabweichung wird hier trotzdem mitgeprüft, damit der
* Test sichtbar macht, ob eine reale GUI-Meldung zwar non-planar ist, aber
* noch nicht in den aktuellen Sweep-Pfad fällt.
* Nach doHeal() wird der Checker erneut auf dem ganzen Modell ausgeführt. Damit
* bildet der Test zumindest den ersten Schritt des GUI-Healers nach: eine
* Fazette heilen, Geometrie neu prüfen, danach sehen, ob im Gesamtmodell neue
* Fehler entstanden sind.
*/
public class SweepUtilitiesRealGmlTest {
private static final double POINT_ON_PLANE_TOLERANCE = 0.001;
private static final double SWEEP_EDGE_AND_ANGLE_TOLERANCE = 0.001;
private static final String STRICT_PLANAR_DISTANCE_TOLERANCE = "0.001";
private static final String PLANAR_CHECK_TYPE = PlanarCheck.BOTH;
private static final String PLANAR_DISTANCE_TOLERANCE = "0.01";
private static final String PLANAR_CHECK_TYPE = PlanarCheck.DISTANCE;
private static final boolean PRINT_EACH_RELEVANT_SWEEP_VERTEX = true;
/**
* Reales ganzes CityGML:NpR1 . Der Test lädt die Datei über den
......@@ -76,53 +68,35 @@ public class SweepUtilitiesRealGmlTest {
checkRealGmlAndTrySweepHealing("src/test/resources/sweep/non-planar-distance-plane/NP_R_1.gml");
}
/**
* Reales größeres GML:LoD21005WithVal3dityErrors: mit bekannten val3dity-Volumenfehlern(AG-Qual).
* Für den Sweep ist hier zuerst wichtig, ob zusätzlich passende nicht-planare Polygonfehler vom
* Typ DistancePlane gefunden werden. (Da einzelne Gebäude-Exports mit solchen Fehler 0byte-Daten)
*/
@Test
public void reportsAndTriesSweepForLoD21005WithVal3dityErrors()
throws CityGmlParseException, IOException, InvalidGmlFileException {
checkRealGmlAndTrySweepHealing(
"src/test/resources/sweep/non-planar-distance-plane/LoD2_10_05_mit_val3dity_volumenfehlern.gml");
}
/**
* Reales CityGML: LoD2_426_6002_2_SH. Diese Datei wurde im GUI Starter bereits
* als passend untersucht.
* Es muss NonPlanarDistancePlane-Fehler enthalten!
*/
@Test
public void reportsAndTriesSweepForLoD242660022Sh()
throws CityGmlParseException, IOException, InvalidGmlFileException {
checkRealGmlAndTrySweepHealing(
"src/test/resources/sweep/non-planar-distance-plane/LoD2_426_6002_2_SH.xml");
}
private void checkRealGmlAndTrySweepHealing(String path)
throws CityGmlParseException, IOException, InvalidGmlFileException {
String fileName = Path.of(path).getFileName().toString();
ValidationConfiguration config = ValidationConfiguration.loadStandardValidationConfig();
config.setSchematronFilePathInGlobalParameters(null);
config.getRequirements().get(Requirement.R_GE_P_NON_PLANAR.toString()).getParameters()
.put(PlanarCheck.TYPE, PLANAR_CHECK_TYPE);
config.getRequirements().get(Requirement.R_GE_P_NON_PLANAR.toString()).getParameters()
.put(PlanarCheck.DISTANCE_TOLERANCE, STRICT_PLANAR_DISTANCE_TOLERANCE);
.put(PlanarCheck.DISTANCE_TOLERANCE, PLANAR_DISTANCE_TOLERANCE);
CityDoctorModel model = TestUtil.loadCityModel(path, config);
Checker checker = new Checker(config, model);
checker.runChecks();
List<NonPlanarPolygonDistancePlaneError> errors = collectDistancePlaneErrors(model);
Map<ErrorId, Integer> errorCounts = countErrorsById(model);
List<CheckError> rawErrors = collectErrors(model);
List<NonPlanarPolygonDistancePlaneError> errors = collectUniqueDistancePlaneErrors(rawErrors);
Map<ErrorId, Integer> rawErrorCounts = countErrorsById(rawErrors);
int rawDistancePlaneCount = countDistancePlaneErrors(rawErrors);
System.out.println();
System.out.println("=== Real-GML Sweep-Test ===");
System.out.println("Datei: " + path);
System.out.println("Datei: " + fileName);
System.out.println("Pfad: " + path);
printModelSummary(model);
System.out.println("PlanarCheck: Typ " + PLANAR_CHECK_TYPE + ", distanceTolerance = "
+ STRICT_PLANAR_DISTANCE_TOLERANCE);
System.out.println("Fehlerzählung nach ErrorId: " + errorCounts);
System.out.println("Gefundene NonPlanarDistancePlane-Fehler: " + errors.size());
+ PLANAR_DISTANCE_TOLERANCE);
System.out.println("Fehlerzählung roh nach ErrorId: " + rawErrorCounts);
System.out.println("Rohe NonPlanarDistancePlane-Einträge: " + rawDistancePlaneCount);
System.out.println("Eindeutige NonPlanarDistancePlane-Fazetten: " + errors.size());
System.out.println("Mehrfach eingesammelte DistancePlane-Einträge: " + (rawDistancePlaneCount - errors.size()));
assertFalse("Die Datei muss mindestens einen NonPlanarPolygonDistancePlaneError enthalten.", errors.isEmpty());
int healedCount = 0;
......@@ -133,7 +107,7 @@ public class SweepUtilitiesRealGmlTest {
SWEEP_EDGE_AND_ANGLE_TOLERANCE);
System.out.println();
System.out.println("Fehler " + (i + 1) + " von " + errors.size());
System.out.println("Fehler " + (i + 1) + " von " + errors.size() + " in Datei " + fileName);
printErrorSummary(error);
utilities.initialize();
......@@ -145,6 +119,11 @@ public class SweepUtilitiesRealGmlTest {
utilities.doHeal();
boolean polygonPlanarAfterHealing = isPolygonPlanar(error.getPolygon());
System.out.println("PlanarCheck nach doHeal() für diese Fazette: " + polygonPlanarAfterHealing);
assertTrue("Die aktuell geheilte Fazette muss nach doHeal() planar sein.", polygonPlanarAfterHealing);
recheckWholeModel(checker, model);
int remainingDistancePlaneFacets = printModelErrorSummaryAfterHealing(model);
assertEquals("Nach doHeal() und erneutem Checker-Lauf dürfen keine DistancePlane-Fazetten übrig sein.",
0, remainingDistancePlaneFacets);
if (polygonPlanarAfterHealing) {
healedCount++;
} else {
......@@ -164,39 +143,103 @@ public class SweepUtilitiesRealGmlTest {
healedCount > 0);
}
// Evtl.Obsolet
private PlanarCheck createStrictDistancePlanarCheck() {
PlanarCheck check = new PlanarCheck();
Map<CheckId, Map<String, String>> parameters = new HashMap<>();
Map<String, String> planarParameters = new HashMap<>();
planarParameters.put(PlanarCheck.TYPE, PLANAR_CHECK_TYPE);
planarParameters.put(PlanarCheck.DISTANCE_TOLERANCE, STRICT_PLANAR_DISTANCE_TOLERANCE);
planarParameters.put(PlanarCheck.DISTANCE_TOLERANCE, PLANAR_DISTANCE_TOLERANCE);
parameters.put(CheckId.C_GE_P_NON_PLANAR, planarParameters);
check.init(parameters, null);
return check;
}
private List<NonPlanarPolygonDistancePlaneError> collectDistancePlaneErrors(CityDoctorModel model) {
/**
* Prüft das komplette Modell erneut. Das ist näher am GUI-Healer als eine reine
* Einzelpolygon-Prüfung, weil dadurch auch neu entstandene Nachbarfehler
* sichtbar werden.
*/
private void recheckWholeModel(Checker checker, CityDoctorModel model) {
for (CityObject cityObject : model.createFeatureStream().toList()) {
cityObject.prepareForChecking();
checker.executeChecksForCheckable(cityObject);
}
}
/**
* Druckt die Fehlerlage nach einem Heal-Schritt. Besonders wichtig ist der
* Vergleich zwischen rohen DistancePlane-Einträgen und eindeutigen Fazetten.
*/
private int printModelErrorSummaryAfterHealing(CityDoctorModel model) {
List<CheckError> errorsAfterHealing = collectErrors(model);
List<NonPlanarPolygonDistancePlaneError> distancePlaneErrorsAfterHealing = collectUniqueDistancePlaneErrors(
errorsAfterHealing);
Map<ErrorId, Integer> errorCountsAfterHealing = countErrorsById(errorsAfterHealing);
int rawDistancePlaneCountAfterHealing = countDistancePlaneErrors(errorsAfterHealing);
System.out.println("Fehlerzählung im Gesamtmodell nach doHeal(): " + errorCountsAfterHealing);
System.out.println("Rohe NonPlanarDistancePlane-Einträge nach doHeal(): " + rawDistancePlaneCountAfterHealing);
System.out.println(
"Eindeutige NonPlanarDistancePlane-Fazetten nach doHeal(): " + distancePlaneErrorsAfterHealing.size());
return distancePlaneErrorsAfterHealing.size();
}
/**
* Sammelt alle aktuell im Modell gespeicherten CheckError-Objekte.
*/
private List<CheckError> collectErrors(CityDoctorModel model) {
List<CheckError> allErrors = new ArrayList<>();
for (CityObject cityObject : model.createFeatureStream().toList()) {
cityObject.collectContainedErrors(allErrors);
}
return allErrors;
}
List<NonPlanarPolygonDistancePlaneError> distancePlaneErrors = new ArrayList<>();
/**
* Filtert DistancePlane-Fehler und entfernt fachliche Duplikate. Linked
* Polygons können denselben Fehler mehrfach liefern; für den Sweep wollen wir
* jede betroffene Fazette nur einmal behandeln.
*/
private List<NonPlanarPolygonDistancePlaneError> collectUniqueDistancePlaneErrors(List<CheckError> allErrors) {
Map<String, NonPlanarPolygonDistancePlaneError> distancePlaneErrorsByFacet = new LinkedHashMap<>();
for (CheckError error : allErrors) {
if (error.getErrorId() == ErrorId.GE_P_NON_PLANAR_POLYGON_DISTANCE_PLANE) {
distancePlaneErrors.add((NonPlanarPolygonDistancePlaneError) error);
NonPlanarPolygonDistancePlaneError distancePlaneError = (NonPlanarPolygonDistancePlaneError) error;
distancePlaneErrorsByFacet.putIfAbsent(createFacetKey(distancePlaneError), distancePlaneError);
}
}
return distancePlaneErrors;
return new ArrayList<>(distancePlaneErrorsByFacet.values());
}
private Map<ErrorId, Integer> countErrorsById(CityDoctorModel model) {
List<CheckError> allErrors = new ArrayList<>();
for (CityObject cityObject : model.createFeatureStream().toList()) {
cityObject.collectContainedErrors(allErrors);
/**
* Zählt rohe DistancePlane-Einträge ohne Deduplication.
*/
private int countDistancePlaneErrors(List<CheckError> allErrors) {
int count = 0;
for (CheckError error : allErrors) {
if (error.getErrorId() == ErrorId.GE_P_NON_PLANAR_POLYGON_DISTANCE_PLANE) {
count++;
}
}
return count;
}
/**
* Stabiler fachlicher Schlüssel für eine Fazette. Objektidentität reicht hier
* nicht, weil derselbe GML-Fehler über linked polygons mehrfach auftauchen kann.
*/
private String createFacetKey(NonPlanarPolygonDistancePlaneError error) {
Polygon polygon = error.getPolygon();
Geometry geometry = polygon.getParent();
CityObject cityObject = geometry.getParent();
return cityObject.getGmlId().getGmlString() + "|" + geometry.getGmlId().getGmlString() + "|"
+ polygon.getGmlId().getGmlString();
}
/**
* Zählt Fehler nach ErrorId für kompakte Debug-Ausgaben.
*/
private Map<ErrorId, Integer> countErrorsById(List<CheckError> allErrors) {
Map<ErrorId, Integer> errorCounts = new HashMap<>();
for (CheckError error : allErrors) {
errorCounts.merge(error.getErrorId(), 1, Integer::sum);
......@@ -234,6 +277,7 @@ public class SweepUtilitiesRealGmlTest {
System.out.println("Gebäude/Feature: " + cityObject.getGmlId().getGmlString());
System.out.println("Geometrie: " + geometry.getGmlId().getGmlString());
System.out.println("Polygone in Parent-Geometrie: " + geometry.getPolygons().size());
System.out.println("Kaputte Fazette: " + polygon.getGmlId().getGmlString());
System.out.println("Maximaler Abstand laut Error: " + error.getDistance());
Vertex errorVertex = error.getVertex();
......@@ -250,22 +294,46 @@ public class SweepUtilitiesRealGmlTest {
}
Map<SweepVertexStatus, Integer> statusCounts = new EnumMap<>(SweepVertexStatus.class);
Map<Integer, Integer> outgoingEdgeCounts = new HashMap<>();
for (SweepVertexStatus status : SweepVertexStatus.values()) {
statusCounts.put(status, 0);
}
for (SweepVertex sweepVertex : sweepVertices) {
SweepVertexStatus status = sweepVertex.getStatus();
statusCounts.put(status, statusCounts.get(status) + 1);
outgoingEdgeCounts.merge(sweepVertex.countOutgoingEdges(), 1, Integer::sum);
}
System.out.println("initialize(): SweepVertices gesamt: " + sweepVertices.size());
System.out.println("Statuszählung: " + statusCounts);
System.out.println("Abführende-Kanten-Zählung pro Punkt: " + outgoingEdgeCounts);
System.out.println("Fall-3-Kandidaten nach Topologie, also Punkte mit mehr als 1 abführender Kante: "
+ countMultiCandidatesByOutgoingEdges(sweepVertices));
System.out.println(
"Hinweis: MULTI kann nach initialize() schon wieder verschwunden sein, weil initialize() bei MULTI die Zielebene anpasst und danach neu klassifiziert.");
for (int i = 0; i < sweepVertices.size(); i++) {
SweepVertex sweepVertex = sweepVertices.get(i);
if (!PRINT_EACH_RELEVANT_SWEEP_VERTEX || !isRelevantForDebugOutput(sweepVertex)) {
continue;
}
Vertex vertex = sweepVertex.getSweepVertex();
System.out.println(" SweepVertex " + i + ": Status " + sweepVertex.getStatus() + ", abführende Kanten "
+ sweepVertex.countOutgoingEdges() + ", Punkt x=" + vertex.getX() + ", y=" + vertex.getY()
+ ", z=" + vertex.getZ());
}
}
private int countMultiCandidatesByOutgoingEdges(List<SweepVertex> sweepVertices) {
int count = 0;
for (SweepVertex sweepVertex : sweepVertices) {
if (sweepVertex.countOutgoingEdges() > 1) {
count++;
}
}
return count;
}
private boolean isRelevantForDebugOutput(SweepVertex sweepVertex) {
return sweepVertex.getStatus() != SweepVertexStatus.ON_PLANE || sweepVertex.countOutgoingEdges() > 0;
}
}
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