Commit 1fc540d9 authored by Matthias Betz's avatar Matthias Betz
Browse files

fix solid self intersection false positives

parent ad52781c
Pipeline #12155 passed with stage
in 1 minute and 58 seconds
...@@ -59,6 +59,7 @@ public class SchematronError implements CheckError { ...@@ -59,6 +59,7 @@ public class SchematronError implements CheckError {
@Override @Override
public void report(ErrorReport report) { public void report(ErrorReport report) {
report.add("errorId", errorId); report.add("errorId", errorId);
report.add("message", nameOfAttribute);
} }
@Override @Override
......
...@@ -61,15 +61,11 @@ public class BridgeObject extends CityObject { ...@@ -61,15 +61,11 @@ public class BridgeObject extends CityObject {
private BridgeObject parent; private BridgeObject parent;
public BridgeObject(AbstractBridge ab) { public BridgeObject(AbstractBridge ab) {
this.ab = ab; this(BridgeType.BRIDGE, ab, null);
this.type = BridgeType.BRIDGE;
this.parent = null;
} }
public BridgeObject(AbstractBridge ab, BridgeObject parent) { public BridgeObject(AbstractBridge ab, BridgeObject parent) {
this.ab = ab; this(BridgeType.BRIDGE_PART, ab, parent);
this.type = BridgeType.BRIDGE_PART;
this.parent = parent;
} }
private BridgeObject(BridgeType type, AbstractBridge ab, BridgeObject parent) { private BridgeObject(BridgeType type, AbstractBridge ab, BridgeObject parent) {
......
package de.hft.stuttgart.citydoctor2.datastructure; package de.hft.stuttgart.citydoctor2.datastructure;
import de.hft.stuttgart.citydoctor2.check.CheckableVisitor; import java.io.Serial;
import de.hft.stuttgart.citydoctor2.parser.ParserConfiguration; import java.util.ArrayList;
import de.hft.stuttgart.citydoctor2.utils.CityGmlUtils; import java.util.List;
import javafx.scene.paint.Color;
import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.Logger;
import org.citygml4j.core.model.core.AbstractSpaceBoundaryProperty; import org.citygml4j.core.model.core.AbstractSpaceBoundaryProperty;
import org.citygml4j.core.model.deprecated.bridge.DeprecatedPropertiesOfBridgeConstructiveElement;
import org.citygml4j.core.util.geometry.GeometryFactory; import org.citygml4j.core.util.geometry.GeometryFactory;
import org.xmlobjects.gml.model.geometry.GeometryProperty;
import org.xmlobjects.gml.model.geometry.aggregates.MultiSurface; import org.xmlobjects.gml.model.geometry.aggregates.MultiSurface;
import org.xmlobjects.gml.model.geometry.aggregates.MultiSurfaceProperty; import org.xmlobjects.gml.model.geometry.aggregates.MultiSurfaceProperty;
import org.xmlobjects.gml.model.geometry.complexes.CompositeSurface;
import org.xmlobjects.gml.model.geometry.primitives.Solid; import org.xmlobjects.gml.model.geometry.primitives.Solid;
import org.xmlobjects.gml.model.geometry.primitives.SolidProperty; import org.xmlobjects.gml.model.geometry.primitives.SolidProperty;
import org.xmlobjects.gml.model.geometry.primitives.SurfaceProperty;
import java.io.Serial; import de.hft.stuttgart.citydoctor2.check.CheckableVisitor;
import java.util.ArrayList; import de.hft.stuttgart.citydoctor2.parser.ParserConfiguration;
import java.util.List; import de.hft.stuttgart.citydoctor2.utils.CityGmlUtils;
import javafx.scene.paint.Color;
public class BuildingConstructiveElement extends CityObject { public class BuildingConstructiveElement extends CityObject {
......
...@@ -48,10 +48,6 @@ public class Opening extends CityObject { ...@@ -48,10 +48,6 @@ public class Opening extends CityObject {
private AbstractFillingSurface ao; private AbstractFillingSurface ao;
private Opening(OpeningType type) {
this.type = type;
}
public Opening(OpeningType type, SurfaceFeatureType featureType, BoundarySurface partOf, public Opening(OpeningType type, SurfaceFeatureType featureType, BoundarySurface partOf,
AbstractFillingSurface ao) { AbstractFillingSurface ao) {
this.featureType = featureType; this.featureType = featureType;
......
...@@ -9,6 +9,8 @@ public class TunnelPart extends AbstractTunnel { ...@@ -9,6 +9,8 @@ public class TunnelPart extends AbstractTunnel {
private Tunnel parent; private Tunnel parent;
private TunnelPart() {
}
public TunnelPart(Tunnel parent) { public TunnelPart(Tunnel parent) {
this.parent = parent; this.parent = parent;
...@@ -34,6 +36,4 @@ public class TunnelPart extends AbstractTunnel { ...@@ -34,6 +36,4 @@ public class TunnelPart extends AbstractTunnel {
return "TunnelPart [id=" + getGmlId() + "]"; return "TunnelPart [id=" + getGmlId() + "]";
} }
private TunnelPart() {
}
} }
...@@ -87,8 +87,13 @@ public class Triangle3d implements Serializable { ...@@ -87,8 +87,13 @@ public class Triangle3d implements Serializable {
Vector3d n2 = v1.cross(v2); Vector3d n2 = v1.cross(v2);
return new Plane(n2, p1); return new Plane(n2, p1);
} }
public boolean doesIntersect(Triangle3d other) { public boolean doesIntersect(Triangle3d other) {
return doesIntersect(other, EPSILON);
}
public boolean doesIntersect(Triangle3d other, double epsilon) {
// plane of other triangle // plane of other triangle
Plane planeT2 = other.getPlane(); Plane planeT2 = other.getPlane();
// check if all points are on one side of the plane // check if all points are on one side of the plane
...@@ -96,13 +101,13 @@ public class Triangle3d implements Serializable { ...@@ -96,13 +101,13 @@ public class Triangle3d implements Serializable {
double distanceP2T2 = planeT2.getSignedDistance(p2); double distanceP2T2 = planeT2.getSignedDistance(p2);
double distanceP3T2 = planeT2.getSignedDistance(p3); double distanceP3T2 = planeT2.getSignedDistance(p3);
if (Math.abs(distanceP1T2) < EPSILON) { if (Math.abs(distanceP1T2) < epsilon) {
distanceP1T2 = 0.0; distanceP1T2 = 0.0;
} }
if (Math.abs(distanceP2T2) < EPSILON) { if (Math.abs(distanceP2T2) < epsilon) {
distanceP2T2 = 0.0; distanceP2T2 = 0.0;
} }
if (Math.abs(distanceP3T2) < EPSILON) { if (Math.abs(distanceP3T2) < epsilon) {
distanceP3T2 = 0.0; distanceP3T2 = 0.0;
} }
...@@ -123,13 +128,13 @@ public class Triangle3d implements Serializable { ...@@ -123,13 +128,13 @@ public class Triangle3d implements Serializable {
double distanceP2T1 = planeT1.getSignedDistance(other.getP2()); double distanceP2T1 = planeT1.getSignedDistance(other.getP2());
double distanceP3T1 = planeT1.getSignedDistance(other.getP3()); double distanceP3T1 = planeT1.getSignedDistance(other.getP3());
if (Math.abs(distanceP1T1) < EPSILON) { if (Math.abs(distanceP1T1) < epsilon) {
distanceP1T1 = 0.0; distanceP1T1 = 0.0;
} }
if (Math.abs(distanceP2T1) < EPSILON) { if (Math.abs(distanceP2T1) < epsilon) {
distanceP2T1 = 0.0; distanceP2T1 = 0.0;
} }
if (Math.abs(distanceP3T1) < EPSILON) { if (Math.abs(distanceP3T1) < epsilon) {
distanceP3T1 = 0.0; distanceP3T1 = 0.0;
} }
...@@ -139,11 +144,15 @@ public class Triangle3d implements Serializable { ...@@ -139,11 +144,15 @@ public class Triangle3d implements Serializable {
return false; return false;
} }
return checkTriangleLineIntersection(other, p1, p2) || checkTriangleLineIntersection(other, p1, p3) boolean intersects = checkTriangleLineIntersection(other, p1, p2) || checkTriangleLineIntersection(other, p1, p3)
|| checkTriangleLineIntersection(other, p2, p3) || checkTriangleLineIntersection(other, p2, p3)
|| checkTriangleLineIntersection(this, other.p1, other.p2) || checkTriangleLineIntersection(this, other.p1, other.p2)
|| checkTriangleLineIntersection(this, other.p1, other.p3) || checkTriangleLineIntersection(this, other.p1, other.p3)
|| checkTriangleLineIntersection(this, other.p2, other.p3); || checkTriangleLineIntersection(this, other.p2, other.p3);
if (intersects) {
System.out.println();
}
return intersects;
} }
private boolean checkTriangleLineIntersection(Triangle3d other, Vector3d a, Vector3d b) { private boolean checkTriangleLineIntersection(Triangle3d other, Vector3d a, Vector3d b) {
...@@ -171,7 +180,11 @@ public class Triangle3d implements Serializable { ...@@ -171,7 +180,11 @@ public class Triangle3d implements Serializable {
private boolean doesIntersectCoplanarTriangle(Triangle3d other) { private boolean doesIntersectCoplanarTriangle(Triangle3d other) {
Triangle2d t1 = projectTo2d(); Triangle2d t1 = projectTo2d();
Triangle2d t2 = other.projectTo2d(); Triangle2d t2 = other.projectTo2d();
return t1.intersects(t2); boolean intersects = t1.intersects(t2);
if (intersects) {
System.out.println();
}
return intersects;
} }
public Triangle2d projectTo2d() { public Triangle2d projectTo2d() {
......
...@@ -49,6 +49,15 @@ public class EarcutTesselator { ...@@ -49,6 +49,15 @@ public class EarcutTesselator {
start = addRingToArray(innerRing, vertices, start, axis); start = addRingToArray(innerRing, vertices, start, axis);
} }
// center vertices?
// double[] projectedCenter = new double[2];
// Geometry geometry = p.getParent();
// Vector3d center = geometry.getCenter();
// axis.writeCoordinatesOfVectorInArray(center, projectedCenter, 0);
// for (int i = 0; i < vertices.length; i++) {
// vertices[i] = vertices[i] - projectedCenter[i % 2];
// }
// triangulation // triangulation
List<Integer> indices = Earcut.earcut(vertices, holeStart, 2); List<Integer> indices = Earcut.earcut(vertices, holeStart, 2);
List<Triangle3d> triangles = new ArrayList<>(); List<Triangle3d> triangles = new ArrayList<>();
...@@ -56,14 +65,15 @@ public class EarcutTesselator { ...@@ -56,14 +65,15 @@ public class EarcutTesselator {
throw new IllegalStateException(); throw new IllegalStateException();
} }
for (int i = 0; i < indices.size(); i = i + 3) { for (int i = 0; i < indices.size(); i = i + 3) {
Triangle3d t = new Triangle3d(vertexObjects.get(indices.get(i + 0)), Vertex v1 = vertexObjects.get(indices.get(i + 0));
vertexObjects.get(indices.get(i + 1)), Vertex v2 = vertexObjects.get(indices.get(i + 1));
vertexObjects.get(indices.get(i + 2))); Vertex v3 = vertexObjects.get(indices.get(i + 2));
Triangle3d t = new Triangle3d(v1, v2, v3);
triangles.add(t); triangles.add(t);
} }
return new TesselatedPolygon(triangles, p); return new TesselatedPolygon(triangles, p);
} }
private static void addVerticesToList(LinearRing ring, List<Vertex> vertexObjects) { private static void addVerticesToList(LinearRing ring, List<Vertex> vertexObjects) {
List<Vertex> vertices = ring.getVertices(); List<Vertex> vertices = ring.getVertices();
for (int i = 0; i < vertices.size() - 1; i++) { for (int i = 0; i < vertices.size() - 1; i++) {
......
...@@ -203,26 +203,60 @@ public class Checker { ...@@ -203,26 +203,60 @@ public class Checker {
} }
private void handleSchematronResults(SvrlContentHandler handler) { private void handleSchematronResults(SvrlContentHandler handler) {
model.addGlobalErrors(handler.getGeneralErrors()); handleSchematronErrorsGlobal(handler.getGeneralErrors());
Map<String, CityObject> featureMap = new HashMap<>(); Map<String, CityObject> featureMap = new HashMap<>();
model.createFeatureStream().forEach(f -> featureMap.put(f.getGmlId().getGmlString(), f)); boolean onlySchematron = execLayers.isEmpty();
if (onlySchematron) {
CheckableUtilsVisitor visitor = new CheckableUtilsVisitor() {
@Override
public void check(Checkable checkable) {
checkable.setValidated(true);
}
};
model.createFeatureStream().forEach(f -> {
featureMap.put(f.getGmlId().getGmlString(), f);
f.accept(visitor);
});
} else {
model.createFeatureStream().forEach(f -> featureMap.put(f.getGmlId().getGmlString(), f));
}
handler.getFeatureErrors().forEach((k, v) -> { handler.getFeatureErrors().forEach((k, v) -> {
if (k.trim().isEmpty()) { if (k.trim().isEmpty()) {
// missing gml id, ignore? handleSchematronErrorsGlobal(v);
return; return;
} }
CityObject co = featureMap.get(k); CityObject co = featureMap.get(k);
if (co == null) { if (co == null) {
// gml id reported by schematron was not found, add to general errors // gml id reported by schematron was not found, add to general errors
for (SchematronError se : v) { handleSchematronErrorsGlobal(v);
model.addGlobalError(se);
} // for (SchematronError se : v) {
// model.addGlobalError(se);
// }
} else { } else {
handleSchematronErrorsForCityObject(v, co); handleSchematronErrorsForCityObject(v, co);
co.setValidated(true);
} }
}); });
} }
private void handleSchematronErrorsGlobal(List<SchematronError> v) {
for (SchematronError se : v) {
// CheckError err;
// if (AttributeMissingError.ID.getIdString().equals(se.getErrorIdString())) {
// err = new AttributeMissingError(null, se.getChildId(), se.getNameOfAttribute());
// } else if (AttributeValueWrongError.ID.getIdString().equals(se.getErrorIdString())) {
// err = new AttributeValueWrongError(null, se.getChildId(), se.getNameOfAttribute());
// } else if (AttributeInvalidError.ID.getIdString().equals(se.getErrorIdString())) {
// err = new AttributeInvalidError(null, se.getChildId(), se.getNameOfAttribute());
// } else {
// throw new IllegalStateException(
// "Unknown error ID was given in schematron file: " + se.getErrorIdString());
// }
model.addGlobalError(se);
}
}
public static void handleSchematronErrorsForCityObject(List<SchematronError> v, CityObject co) { public static void handleSchematronErrorsForCityObject(List<SchematronError> v, CityObject co) {
int count = 0; int count = 0;
for (SchematronError se : v) { for (SchematronError se : v) {
...@@ -435,7 +469,6 @@ public class Checker { ...@@ -435,7 +469,6 @@ public class Checker {
} }
} }
@SuppressWarnings("resource")
public static SvrlContentHandler executeSchematronValidationIfAvailable(ValidationConfiguration config, public static SvrlContentHandler executeSchematronValidationIfAvailable(ValidationConfiguration config,
InputStream in) { InputStream in) {
if (config.getSchematronFilePath() != null && !config.getSchematronFilePath().isEmpty()) { if (config.getSchematronFilePath() != null && !config.getSchematronFilePath().isEmpty()) {
......
...@@ -106,7 +106,7 @@ public class SvrlContentHandler implements ContentHandler { ...@@ -106,7 +106,7 @@ public class SvrlContentHandler implements ContentHandler {
throw new IllegalStateException( throw new IllegalStateException(
"Schematron File is not formed according to specification for CityDoctor."); "Schematron File is not formed according to specification for CityDoctor.");
} }
String gmlId = split[0]; String gmlId = split[0].strip();
String childId = split[1]; String childId = split[1];
String errorId = split[2]; String errorId = split[2];
String nameOfAttribute = split[3]; String nameOfAttribute = split[3];
......
...@@ -57,10 +57,12 @@ import de.hft.stuttgart.citydoctor2.tesselation.TesselatedPolygon; ...@@ -57,10 +57,12 @@ import de.hft.stuttgart.citydoctor2.tesselation.TesselatedPolygon;
*/ */
public class PlanarCheck extends Check { public class PlanarCheck extends Check {
private static final String DISTANCE = "distance"; public static final String ANGLE = "angle";
private static final String DISTANCE_TOLERANCE = "distanceTolerance"; public static final String DISTANCE = "distance";
public static final String BOTH = "both";
public static final String DISTANCE_TOLERANCE = "distanceTolerance";
private static final String ANGLE_TOLERANCE = "angleTolerance"; private static final String ANGLE_TOLERANCE = "angleTolerance";
private static final String TYPE = "type"; public static final String TYPE = "type";
private static final List<CheckId> dependencies; private static final List<CheckId> dependencies;
...@@ -97,7 +99,7 @@ public class PlanarCheck extends Check { ...@@ -97,7 +99,7 @@ public class PlanarCheck extends Check {
public void check(Polygon p) { public void check(Polygon p) {
if (DISTANCE.equals(planarCheckType)) { if (DISTANCE.equals(planarCheckType)) {
planarDistance(p); planarDistance(p);
} else if ("angle".equals(planarCheckType)) { } else if (ANGLE.equals(planarCheckType)) {
planarNormalDeviation(p); planarNormalDeviation(p);
} else if ("both".equals(planarCheckType)) { } else if ("both".equals(planarCheckType)) {
planarDistance(p); planarDistance(p);
......
...@@ -21,13 +21,13 @@ package de.hft.stuttgart.citydoctor2.checks.geometry; ...@@ -21,13 +21,13 @@ package de.hft.stuttgart.citydoctor2.checks.geometry;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.Map;
import java.util.Set; import java.util.Set;
import de.hft.stuttgart.citydoctor2.check.Check; import de.hft.stuttgart.citydoctor2.check.Check;
import de.hft.stuttgart.citydoctor2.check.CheckError; import de.hft.stuttgart.citydoctor2.check.CheckError;
import de.hft.stuttgart.citydoctor2.check.CheckId; import de.hft.stuttgart.citydoctor2.check.CheckId;
import de.hft.stuttgart.citydoctor2.check.CheckResult; import de.hft.stuttgart.citydoctor2.check.CheckResult;
import de.hft.stuttgart.citydoctor2.check.GeometrySelfIntersection;
import de.hft.stuttgart.citydoctor2.check.Requirement; import de.hft.stuttgart.citydoctor2.check.Requirement;
import de.hft.stuttgart.citydoctor2.check.RequirementType; import de.hft.stuttgart.citydoctor2.check.RequirementType;
import de.hft.stuttgart.citydoctor2.check.ResultStatus; import de.hft.stuttgart.citydoctor2.check.ResultStatus;
...@@ -36,6 +36,7 @@ import de.hft.stuttgart.citydoctor2.checks.util.CollectionUtils; ...@@ -36,6 +36,7 @@ import de.hft.stuttgart.citydoctor2.checks.util.CollectionUtils;
import de.hft.stuttgart.citydoctor2.checks.util.SelfIntersectionUtil; import de.hft.stuttgart.citydoctor2.checks.util.SelfIntersectionUtil;
import de.hft.stuttgart.citydoctor2.datastructure.Geometry; import de.hft.stuttgart.citydoctor2.datastructure.Geometry;
import de.hft.stuttgart.citydoctor2.datastructure.GeometryType; import de.hft.stuttgart.citydoctor2.datastructure.GeometryType;
import de.hft.stuttgart.citydoctor2.parser.ParserConfiguration;
import de.hft.stuttgart.citydoctor2.utils.PolygonIntersection; import de.hft.stuttgart.citydoctor2.utils.PolygonIntersection;
/** /**
...@@ -47,7 +48,9 @@ import de.hft.stuttgart.citydoctor2.utils.PolygonIntersection; ...@@ -47,7 +48,9 @@ import de.hft.stuttgart.citydoctor2.utils.PolygonIntersection;
public class SolidSelfIntCheck extends Check { public class SolidSelfIntCheck extends Check {
private static final List<CheckId> dependencies; private static final List<CheckId> dependencies;
private double delta = 0.01;
static { static {
ArrayList<CheckId> deps = new ArrayList<>(); ArrayList<CheckId> deps = new ArrayList<>();
dependencies = Collections.unmodifiableList(deps); dependencies = Collections.unmodifiableList(deps);
...@@ -68,6 +71,13 @@ public class SolidSelfIntCheck extends Check { ...@@ -68,6 +71,13 @@ public class SolidSelfIntCheck extends Check {
deps.add(CheckId.C_GE_S_NON_MANIFOLD_VERTEX); deps.add(CheckId.C_GE_S_NON_MANIFOLD_VERTEX);
deps.add(CheckId.C_GE_S_POLYGON_WRONG_ORIENTATION); deps.add(CheckId.C_GE_S_POLYGON_WRONG_ORIENTATION);
} }
@Override
public void init(Map<String, String> parameters, ParserConfiguration config) {
if (parameters.containsKey(PlanarCheck.DISTANCE_TOLERANCE)) {
delta = Double.parseDouble(parameters.get(PlanarCheck.DISTANCE_TOLERANCE));
}
}
@Override @Override
public void check(Geometry g) { public void check(Geometry g) {
...@@ -75,7 +85,7 @@ public class SolidSelfIntCheck extends Check { ...@@ -75,7 +85,7 @@ public class SolidSelfIntCheck extends Check {
return; return;
} }
CheckResult cr; CheckResult cr;
List<PolygonIntersection> intersections = SelfIntersectionUtil.calculateSolidSelfIntersection(g); List<PolygonIntersection> intersections = SelfIntersectionUtil.calculateSolidSelfIntersection(g, delta);
if (intersections.isEmpty()) { if (intersections.isEmpty()) {
cr = new CheckResult(this, ResultStatus.OK, null); cr = new CheckResult(this, ResultStatus.OK, null);
} else { } else {
...@@ -85,18 +95,6 @@ public class SolidSelfIntCheck extends Check { ...@@ -85,18 +95,6 @@ public class SolidSelfIntCheck extends Check {
g.addCheckResult(cr); g.addCheckResult(cr);
} }
@SuppressWarnings("unused")
private CheckResult oldIntersectionAlgorithm(Geometry g) {
CheckResult cr;
GeometrySelfIntersection intersect = SelfIntersectionUtil.doesSolidSelfIntersect(g);
if (intersect != null) {
cr = new CheckResult(this, ResultStatus.ERROR, null);
} else {
cr = new CheckResult(this, ResultStatus.OK, null);
}
return cr;
}
@Override @Override
public List<CheckId> getDependencies() { public List<CheckId> getDependencies() {
return dependencies; return dependencies;
......
...@@ -21,6 +21,7 @@ package de.hft.stuttgart.citydoctor2.checks.util; ...@@ -21,6 +21,7 @@ package de.hft.stuttgart.citydoctor2.checks.util;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
import java.util.IdentityHashMap; import java.util.IdentityHashMap;
import java.util.Iterator;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
...@@ -39,6 +40,8 @@ import org.locationtech.jts.geom.impl.CoordinateArraySequence; ...@@ -39,6 +40,8 @@ import org.locationtech.jts.geom.impl.CoordinateArraySequence;
import org.locationtech.jts.operation.overlay.OverlayOp; import org.locationtech.jts.operation.overlay.OverlayOp;
import org.locationtech.jts.operation.overlay.snap.SnapIfNeededOverlayOp; import org.locationtech.jts.operation.overlay.snap.SnapIfNeededOverlayOp;
import Jama.EigenvalueDecomposition;
import Jama.Matrix;
import de.hft.stuttgart.citydoctor2.check.GeometrySelfIntersection; import de.hft.stuttgart.citydoctor2.check.GeometrySelfIntersection;
import de.hft.stuttgart.citydoctor2.datastructure.ConcretePolygon; import de.hft.stuttgart.citydoctor2.datastructure.ConcretePolygon;
import de.hft.stuttgart.citydoctor2.datastructure.Geometry; import de.hft.stuttgart.citydoctor2.datastructure.Geometry;
...@@ -50,8 +53,10 @@ import de.hft.stuttgart.citydoctor2.edge.EdgePolygon; ...@@ -50,8 +53,10 @@ import de.hft.stuttgart.citydoctor2.edge.EdgePolygon;
import de.hft.stuttgart.citydoctor2.edge.IntersectPlanarPolygons; import de.hft.stuttgart.citydoctor2.edge.IntersectPlanarPolygons;
import de.hft.stuttgart.citydoctor2.edge.MeshSurface; import de.hft.stuttgart.citydoctor2.edge.MeshSurface;
import de.hft.stuttgart.citydoctor2.edge.PolygonPolygonIntersection; import de.hft.stuttgart.citydoctor2.edge.PolygonPolygonIntersection;
import de.hft.stuttgart.citydoctor2.math.CovarianceMatrix;
import de.hft.stuttgart.citydoctor2.math.MovedPolygon; import de.hft.stuttgart.citydoctor2.math.MovedPolygon;
import de.hft.stuttgart.citydoctor2.math.MovedRing; import de.hft.stuttgart.citydoctor2.math.MovedRing;
import de.hft.stuttgart.citydoctor2.math.OrthogonalRegressionPlane;
import de.hft.stuttgart.citydoctor2.math.Plane; import de.hft.stuttgart.citydoctor2.math.Plane;
import de.hft.stuttgart.citydoctor2.math.PlaneSegmentIntersection; import de.hft.stuttgart.citydoctor2.math.PlaneSegmentIntersection;
import de.hft.stuttgart.citydoctor2.math.PlaneSegmentIntersection.Type; import de.hft.stuttgart.citydoctor2.math.PlaneSegmentIntersection.Type;
...@@ -83,18 +88,37 @@ public class SelfIntersectionUtil { ...@@ -83,18 +88,37 @@ public class SelfIntersectionUtil {
private SelfIntersectionUtil() { private SelfIntersectionUtil() {
} }
public static List<PolygonIntersection> calculateSolidSelfIntersection(Geometry g) { public static List<PolygonIntersection> calculateSolidSelfIntersection(Geometry g, double delta) {
List<TesselatedPolygon> tesselatedPolygons = new ArrayList<>(); List<TesselatedPolygon> tesselatedPolygons = new ArrayList<>();
for (Polygon p : g.getPolygons()) { for (Polygon p : g.getPolygons()) {
tesselatedPolygons.add(EarcutTesselator.tesselatePolygon(p)); TesselatedPolygon tessPolygon = EarcutTesselator.tesselatePolygon(p);
for (Iterator<Triangle3d> iterator = tessPolygon.getTriangles().iterator(); iterator.hasNext();) {
Triangle3d t = iterator.next();
List<Vector3d> vertices = new ArrayList<>(3);
vertices.add(t.getP1());
vertices.add(t.getP2());
vertices.add(t.getP3());
Vector3d centroid = CovarianceMatrix.getCentroid(vertices);
EigenvalueDecomposition ed = OrthogonalRegressionPlane.decompose(vertices, centroid);
Matrix eigenValues = ed.getD();
double[] eigenValuesArray = new double[3];
eigenValuesArray[0] = eigenValues.get(0, 0);
eigenValuesArray[1] = eigenValues.get(1, 1);
eigenValuesArray[2] = eigenValues.get(2, 2);
if (eigenValuesArray[1] < delta) {
iterator.remove();
}
}
tesselatedPolygons.add(tessPolygon);
} }
List<PolygonIntersection> intersections = new ArrayList<>(); List<PolygonIntersection> intersections = new ArrayList<>();
for (int i = 0; i < tesselatedPolygons.size() - 1; i++) { for (int i = 0; i < tesselatedPolygons.size() - 1; i++) {
TesselatedPolygon p1 = tesselatedPolygons.get(i); TesselatedPolygon p1 = tesselatedPolygons.get(i);
for (int j = i + 1; j < tesselatedPolygons.size(); j++) { for (int j = i + 1; j < tesselatedPolygons.size(); j++) {
TesselatedPolygon p2 = tesselatedPolygons.get(j); TesselatedPolygon p2 = tesselatedPolygons.get(j);
GeometrySelfIntersection intersection = doPolygonsIntersect(p1, p2); GeometrySelfIntersection intersection = doPolygonsIntersect(p1, p2, delta);
if (intersection != null) { if (intersection != null) {
intersections.add(PolygonIntersection.triangles(intersection.t1(), intersection.t2())); intersections.add(PolygonIntersection.triangles(intersection.t1(), intersection.t2()));
} }
...@@ -103,8 +127,8 @@ public class SelfIntersectionUtil { ...@@ -103,8 +127,8 @@ public class SelfIntersectionUtil {
return intersections; return intersections;
} }
public static GeometrySelfIntersection doesSolidSelfIntersect(Geometry g) { public static GeometrySelfIntersection doesSolidSelfIntersect(Geometry g, double epsilon) {
return selfIntersectionJava(g); return selfIntersectionJava(g, epsilon);
} }
public static List<PolygonIntersection> doesSolidSelfIntersect2(Geometry g) { public static List<PolygonIntersection> doesSolidSelfIntersect2(Geometry g) {
...@@ -404,7 +428,7 @@ public class SelfIntersectionUtil { ...@@ -404,7 +428,7 @@ public class SelfIntersectionUtil {
return sign != 0; return sign != 0;
} }
private static GeometrySelfIntersection selfIntersectionJava(Geometry g) { private static GeometrySelfIntersection selfIntersectionJava(Geometry g, double epsilon) {
List<TesselatedPolygon> tessPolys = new ArrayList<>(); List<TesselatedPolygon> tessPolys = new ArrayList<>();
for (Polygon p : g.getPolygons()) { for (Polygon p : g.getPolygons()) {
tessPolys.add(JoglTesselator.tesselatePolygon(p)); tessPolys.add(JoglTesselator.tesselatePolygon(p));
...@@ -413,7 +437,7 @@ public class SelfIntersectionUtil { ...@@ -413,7 +437,7 @@ public class SelfIntersectionUtil {
TesselatedPolygon p1 = tessPolys.get(i); TesselatedPolygon p1 = tessPolys.get(i);
for (int j = i + 1; j < tessPolys.size(); j++) { for (int j = i + 1; j < tessPolys.size(); j++) {
TesselatedPolygon p2 = tessPolys.get(j); TesselatedPolygon p2 = tessPolys.get(j);
GeometrySelfIntersection intersection = doPolygonsIntersect(p1, p2); GeometrySelfIntersection intersection = doPolygonsIntersect(p1, p2, epsilon);
if (intersection != null) { if (intersection != null) {
return intersection; return intersection;
} }
...@@ -422,12 +446,12 @@ public class SelfIntersectionUtil { ...@@ -422,12 +446,12 @@ public class SelfIntersectionUtil {
return null; return null;
} }
private static GeometrySelfIntersection doPolygonsIntersect(TesselatedPolygon p1, TesselatedPolygon p2) { private static GeometrySelfIntersection doPolygonsIntersect(TesselatedPolygon p1, TesselatedPolygon p2, double epsilon) {
for (int p1Index = 0; p1Index < p1.getTriangles().size(); p1Index++) { for (int p1Index = 0; p1Index < p1.getTriangles().size(); p1Index++) {
for (int p2Index = 0; p2Index < p2.getTriangles().size(); p2Index++) { for (int p2Index = 0; p2Index < p2.getTriangles().size(); p2Index++) {
Triangle3d t1 = p1.getTriangles().get(p1Index); Triangle3d t1 = p1.getTriangles().get(p1Index);
Triangle3d t2 = p2.getTriangles().get(p2Index); Triangle3d t2 = p2.getTriangles().get(p2Index);
if (t1.doesIntersect(t2)) { if (t1.doesIntersect(t2, epsilon)) {
logger.trace("{} intersects {}", t1, t2); logger.trace("{} intersects {}", t1, t2);
logger.trace("GML-ID: {} intersects {}", t1.getPartOf().getOriginal().getGmlId(), logger.trace("GML-ID: {} intersects {}", t1.getPartOf().getOriginal().getGmlId(),
t2.getPartOf().getOriginal().getGmlId()); t2.getPartOf().getOriginal().getGmlId());
......
...@@ -25,14 +25,18 @@ import static org.junit.Assert.assertNotNull; ...@@ -25,14 +25,18 @@ import static org.junit.Assert.assertNotNull;
import org.junit.Assert; import org.junit.Assert;
import org.junit.Test; import org.junit.Test;
import de.hft.stuttgart.citydoctor2.check.CheckId;
import de.hft.stuttgart.citydoctor2.check.CheckResult; import de.hft.stuttgart.citydoctor2.check.CheckResult;
import de.hft.stuttgart.citydoctor2.check.Checker; import de.hft.stuttgart.citydoctor2.check.Checker;
import de.hft.stuttgart.citydoctor2.check.ResultStatus; import de.hft.stuttgart.citydoctor2.check.ResultStatus;
import de.hft.stuttgart.citydoctor2.check.ValidationConfiguration; import de.hft.stuttgart.citydoctor2.check.ValidationConfiguration;
import de.hft.stuttgart.citydoctor2.check.error.NonPlanarPolygonDistancePlaneError;
import de.hft.stuttgart.citydoctor2.checks.util.GeometryTestUtils; import de.hft.stuttgart.citydoctor2.checks.util.GeometryTestUtils;
import de.hft.stuttgart.citydoctor2.datastructure.Building; import de.hft.stuttgart.citydoctor2.datastructure.Building;
import de.hft.stuttgart.citydoctor2.datastructure.CityDoctorModel; import de.hft.stuttgart.citydoctor2.datastructure.CityDoctorModel;
import de.hft.stuttgart.citydoctor2.datastructure.ConcretePolygon;
import de.hft.stuttgart.citydoctor2.datastructure.Geometry; import de.hft.stuttgart.citydoctor2.datastructure.Geometry;
import de.hft.stuttgart.citydoctor2.datastructure.Geometry.Orientation;
import de.hft.stuttgart.citydoctor2.datastructure.GeometryType; import de.hft.stuttgart.citydoctor2.datastructure.GeometryType;
import de.hft.stuttgart.citydoctor2.datastructure.Lod; import de.hft.stuttgart.citydoctor2.datastructure.Lod;
import de.hft.stuttgart.citydoctor2.parser.CityGmlParseException; import de.hft.stuttgart.citydoctor2.parser.CityGmlParseException;
...@@ -45,6 +49,22 @@ import de.hft.stuttgart.citydoctor2.parser.InvalidGmlFileException; ...@@ -45,6 +49,22 @@ import de.hft.stuttgart.citydoctor2.parser.InvalidGmlFileException;
* *
*/ */
public class SolidSelfIntCheckTest { public class SolidSelfIntCheckTest {
@Test
public void testDependencyChecking() {
Geometry geom = new Geometry(GeometryType.SOLID, Lod.LOD2, Orientation.OUTWARD);
ConcretePolygon p = new ConcretePolygon();
geom.addPolygon(p);
NonPlanarPolygonDistancePlaneError err = new NonPlanarPolygonDistancePlaneError(p, 2, null, null);
CheckResult cr = new CheckResult(CheckId.C_GE_P_NON_PLANAR, ResultStatus.ERROR, err);
p.addCheckResult(cr);
SolidSelfIntCheck selfIntCheck = new SolidSelfIntCheck();
boolean canExecute = selfIntCheck.canExecute(geom);
assertFalse(canExecute);
}
@Test @Test
public void testGoodGeometry() { public void testGoodGeometry() {
......
...@@ -31,6 +31,7 @@ import de.hft.stuttgart.citydoctor2.check.CheckResult; ...@@ -31,6 +31,7 @@ import de.hft.stuttgart.citydoctor2.check.CheckResult;
import de.hft.stuttgart.citydoctor2.check.Checker; import de.hft.stuttgart.citydoctor2.check.Checker;
import de.hft.stuttgart.citydoctor2.check.ErrorId; import de.hft.stuttgart.citydoctor2.check.ErrorId;
import de.hft.stuttgart.citydoctor2.check.ResultStatus; import de.hft.stuttgart.citydoctor2.check.ResultStatus;
import de.hft.stuttgart.citydoctor2.checks.geometry.PlanarCheck;
import de.hft.stuttgart.citydoctor2.datastructure.CityDoctorModel; import de.hft.stuttgart.citydoctor2.datastructure.CityDoctorModel;
import de.hft.stuttgart.citydoctor2.datastructure.Polygon; import de.hft.stuttgart.citydoctor2.datastructure.Polygon;
import de.hft.stuttgart.citydoctor2.parser.CityGmlParseException; import de.hft.stuttgart.citydoctor2.parser.CityGmlParseException;
...@@ -142,7 +143,7 @@ public class PlanarTest { ...@@ -142,7 +143,7 @@ public class PlanarTest {
public void testPlanarPolygon6() throws CityGmlParseException, IOException, InvalidGmlFileException { public void testPlanarPolygon6() throws CityGmlParseException, IOException, InvalidGmlFileException {
Map<String, Map<String, String>> paramMap = new HashMap<>(); Map<String, Map<String, String>> paramMap = new HashMap<>();
Map<String, String> parameter = new HashMap<>(); Map<String, String> parameter = new HashMap<>();
parameter.put("type", "both"); parameter.put(PlanarCheck.TYPE, PlanarCheck.BOTH);
parameter.put("distanceTolerance", "0.5"); parameter.put("distanceTolerance", "0.5");
paramMap.put(RequirementId.R_GE_P_NON_PLANAR.toString(), parameter); paramMap.put(RequirementId.R_GE_P_NON_PLANAR.toString(), parameter);
CityDoctorModel c = TestUtil.loadAndCheckCityModel("src/test/resources/SimpleSolid_SrefBS-GE-gml-PO-0002-T0002.gml", paramMap); CityDoctorModel c = TestUtil.loadAndCheckCityModel("src/test/resources/SimpleSolid_SrefBS-GE-gml-PO-0002-T0002.gml", paramMap);
......
...@@ -538,60 +538,45 @@ public class HealerController { ...@@ -538,60 +538,45 @@ public class HealerController {
} }
public void injectSolid(boolean useBs, boolean useBi, boolean useLod2, boolean useLod3, boolean useLod4) { public void injectSolid(boolean useBs, boolean useBi, boolean useLod2, boolean useLod3, boolean useLod4) {
if (model == null || currentFeature == null) { // if (model == null || currentFeature == null) {
return; // return;
} // }
CityObject oldFeature = currentFeature;
currentFeature.prepareForChecking(); for (Building b : model.getBuildings()) {
if (currentFeature instanceof BoundarySurface) { // collect polygons
BoundarySurface surface = (BoundarySurface) currentFeature; Map<Lod, List<Polygon>> availablePolygons = new EnumMap<>(Lod.class);
currentFeature = surface.getParent(); Map<Lod, Set<Polygon>> existingPolygons = new EnumMap<>(Lod.class);
} collectPolygons(useBs, useBi, availablePolygons, existingPolygons, b);
if (!(currentFeature instanceof Building)) { if (useLod2) {
return; insertSolidGeometry(availablePolygons, b, existingPolygons, Lod.LOD2);
} }
// collect polygons if (useLod3) {
Map<Lod, List<Polygon>> availablePolygons = new EnumMap<>(Lod.class); insertSolidGeometry(availablePolygons, b, existingPolygons, Lod.LOD3);
Map<Lod, Set<Polygon>> existingPolygons = new EnumMap<>(Lod.class); }
Building b = (Building) currentFeature; if (useLod4) {
collectPolygons(useBs, useBi, availablePolygons, existingPolygons, b); insertSolidGeometry(availablePolygons, b, existingPolygons, Lod.LOD4);
if (useLod2) { }
insertSolidGeometry(availablePolygons, b, existingPolygons, Lod.LOD2); for (BuildingPart part : b.getBuildingParts()) {
} // collect polygons
if (useLod3) { availablePolygons = new EnumMap<>(Lod.class);
insertSolidGeometry(availablePolygons, b, existingPolygons, Lod.LOD3); existingPolygons = new EnumMap<>(Lod.class);
} collectPolygons(useBs, useBi, availablePolygons, existingPolygons, part);
if (useLod4) { if (useLod2) {
insertSolidGeometry(availablePolygons, b, existingPolygons, Lod.LOD4); insertSolidGeometry(availablePolygons, part, existingPolygons, Lod.LOD2);
}
if (useLod3) {
insertSolidGeometry(availablePolygons, part, existingPolygons, Lod.LOD3);
}
if (useLod4) {
insertSolidGeometry(availablePolygons, part, existingPolygons, Lod.LOD4);
}
}
} }
nextFeature = null;
nextGeometry = null;
Lod lod = currentGeometry.getLod();
GeometryType type = GeometryType.SOLID;
currentGeometry = Objects.requireNonNull(currentFeature.getGeometry(type, lod));
checker.executeChecksForCheckable(currentFeature);
healerView.getNextErrorList().getItems().clear();
healerView.getNextMeshGroup().getChildren().clear();
updateCurrentErrors();
currentTriangulatedGeometry = TriangulatedGeometry.of(currentGeometry);
currentErrorVisitor.setGeometry(currentTriangulatedGeometry);
currentTriangulatedGeometry.setCullFace(culling);
currentTriangulatedGeometry.setDrawMode(drawMode);
healerView.zoomOutForBoundingBox(currentGeometry.calculateBoundingBox());
healerView.getCurrentMeshGroup().getChildren().clear();
healerView.getCurrentMeshGroup().getChildren().addAll(currentTriangulatedGeometry.getMeshes());
healerView.getNextStepBtn().setDisable(false);
healerView.getHealCompleteBtn().setDisable(false);
healerView.getAcceptBtn().setDisable(true);
healerView.getCancelBtn().setDisable(true);
healerView.getSolidInjectorBtn().setDisable(true);
currentFeature = oldFeature;
} }
private void collectPolygons(boolean useBs, boolean useBi, Map<Lod, List<Polygon>> availablePolygons, private void collectPolygons(boolean useBs, boolean useBi, Map<Lod, List<Polygon>> availablePolygons,
Map<Lod, Set<Polygon>> existingPolygons, Building b) { Map<Lod, Set<Polygon>> existingPolygons, AbstractBuilding b) {
if (useBs) { if (useBs) {
collectPolygons(availablePolygons, b.getBoundarySurfaces()); collectPolygons(availablePolygons, b.getBoundarySurfaces());
} }
...@@ -604,13 +589,13 @@ public class HealerController { ...@@ -604,13 +589,13 @@ public class HealerController {
collectPolygons(availablePolygons, bi.getBoundarySurfaces()); collectPolygons(availablePolygons, bi.getBoundarySurfaces());
} }
} }
for (Geometry geom : b.getGeometries()) { // for (Geometry geom : b.getGeometries()) {
Set<Polygon> polygons = existingPolygons.computeIfAbsent(geom.getLod(), l -> new HashSet<>()); // Set<Polygon> polygons = existingPolygons.computeIfAbsent(geom.getLod(), l -> new HashSet<>());
polygons.addAll(geom.getPolygons()); // polygons.addAll(geom.getPolygons());
} // }
} }
private void insertSolidGeometry(Map<Lod, List<Polygon>> availablePolygons, Building b, private void insertSolidGeometry(Map<Lod, List<Polygon>> availablePolygons, AbstractBuilding b,
Map<Lod, Set<Polygon>> existingPolygons, Lod lod) { Map<Lod, Set<Polygon>> existingPolygons, Lod lod) {
List<Polygon> newPolygons = availablePolygons.get(lod); List<Polygon> newPolygons = availablePolygons.get(lod);
if (newPolygons != null) { if (newPolygons != null) {
......
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