Commit 2d2487cc authored by Numanoglu's avatar Numanoglu
Browse files

Fix BVH query + update tests for Geometry/Stream API

parent 24f4d6cd
Pipeline #12353 failed with stage
in 2 minutes and 7 seconds
...@@ -17,11 +17,6 @@ public class AABB { ...@@ -17,11 +17,6 @@ public class AABB {
private double minX, minY, minZ; private double minX, minY, minZ;
private double maxX, maxY, maxZ; private double maxX, maxY, maxZ;
// public static AABB of(Geometry geom) {
// return of(geom.getPolygons());
// }
//
//
public static AABB ofPolygons(List<? extends Polygon> polygons) { public static AABB ofPolygons(List<? extends Polygon> polygons) {
List<Vector3d> points = new ArrayList<>(); List<Vector3d> points = new ArrayList<>();
for (Polygon p : polygons) { for (Polygon p : polygons) {
...@@ -37,8 +32,6 @@ public class AABB { ...@@ -37,8 +32,6 @@ public class AABB {
return of(ring); return of(ring);
} }
// NOTE: May replaced later: double[] extractBoundsFromPoints(List<? extends
// Point> points)
public static AABB ofPoints(List<? extends Vector3d> points) { public static AABB ofPoints(List<? extends Vector3d> points) {
double minX = Double.POSITIVE_INFINITY, minY = Double.POSITIVE_INFINITY, minZ = Double.POSITIVE_INFINITY; double minX = Double.POSITIVE_INFINITY, minY = Double.POSITIVE_INFINITY, minZ = Double.POSITIVE_INFINITY;
double maxX = Double.NEGATIVE_INFINITY, maxY = Double.NEGATIVE_INFINITY, maxZ = Double.NEGATIVE_INFINITY; double maxX = Double.NEGATIVE_INFINITY, maxY = Double.NEGATIVE_INFINITY, maxZ = Double.NEGATIVE_INFINITY;
...@@ -246,7 +239,7 @@ public class AABB { ...@@ -246,7 +239,7 @@ public class AABB {
/** /**
* Returns true if two AABBs overlap (exclusive). * Returns true if two AABBs overlap ( inclusive : touching counts as overlap )
*/ */
public boolean overlaps(AABB other) { public boolean overlaps(AABB other) {
return !(getMaxX() < other.getMinX() || return !(getMaxX() < other.getMinX() ||
...@@ -270,7 +263,7 @@ public class AABB { ...@@ -270,7 +263,7 @@ public class AABB {
} }
/** /**
* Checks if this and other boxes intersect (inclusive) * Checks if this and other boxes intersect ( exclusive : touching does not count )
*/ */
public boolean intersects(AABB other) { public boolean intersects(AABB other) {
return !(other.maxX <= this.minX || other.minX >= this.maxX || other.maxY <= this.minY || other.minY >= this.maxY return !(other.maxX <= this.minX || other.minX >= this.maxX || other.maxY <= this.minY || other.minY >= this.maxY
...@@ -304,5 +297,4 @@ public class AABB { ...@@ -304,5 +297,4 @@ public class AABB {
return "AABB [" + String.format("min: %.2f, %.2f, %.2f max: %.2f, %.2f, %.2f", minX, minY, minZ, maxX, maxY, maxZ) + "]"; return "AABB [" + String.format("min: %.2f, %.2f, %.2f max: %.2f, %.2f, %.2f", minX, minY, minZ, maxX, maxY, maxZ) + "]";
} }
}
}
...@@ -41,39 +41,5 @@ import de.hft.stuttgart.citydoctor2.datastructure.Vertex; ...@@ -41,39 +41,5 @@ import de.hft.stuttgart.citydoctor2.datastructure.Vertex;
} }
return set.size(); return set.size();
// if (ring == null || ring.getVertices() == null) return 0;
// // Use a quantization resolution based on tol (avoid div by zero).
// final double q = (tol > 0) ? tol : 1e-9;
//
// // Hash set of quantized integer triplets "ix|iy|iz"
// Set<Long> buckets = new HashSet<>(ring.getVertices().size() * 2);
// for (Vertex v : ring.getVertices()) {
// long ix = Math.round(v.getX() / q);
// long iy = Math.round(v.getY() / q);
// long iz = Math.round(v.getZ() / q);
// // pack into a 64-bit key (simple mixing; safe if ranges are reasonable)
// long key = mix3(ix, iy, iz);
// buckets.add(key);
// }
// return buckets.size();
} }
// Simple 3D integer mix to a 64-bit key (Xorshift-ish) from JavaDoc
// private static long mix3(long x, long y, long z) {
// long h = x * 73856093L ^ y * 19349663L ^ z * 83492791L;
// // final avalanche
// h ^= (h >>> 33);
// h *= 0xff51afd7ed558ccdL;
// h ^= (h >>> 33);
// h *= 0xc4ceb9fe1a85ec53L;
// h ^= (h >>> 33);
// return h;
// }
} }
...@@ -84,11 +84,6 @@ public class BoundingVolumeHierarchyTree<E> { ...@@ -84,11 +84,6 @@ public class BoundingVolumeHierarchyTree<E> {
this.root = root; this.root = root;
} }
public List<E> getAllIntersectingElements(AABB box) {
// TODO: implement
throw new UnsupportedOperationException();
}
/** /**
* Recursively builds a balanced AABB tree from the list of elements. * Recursively builds a balanced AABB tree from the list of elements.
*/ */
...@@ -116,35 +111,33 @@ public class BoundingVolumeHierarchyTree<E> { ...@@ -116,35 +111,33 @@ public class BoundingVolumeHierarchyTree<E> {
return node; return node;
} }
///////////////////////////////////////////////////////////////// // Works with overlaps()<--includes touching; not with intersects()
/* Sketch: method for rapid intersection dedection */ public List<E> getAllIntersectingElements(AABB query) {
/* List<E> result = new ArrayList<>();
* getAllIntersectingElementsRecursive(root, query, result);
* return result;
*/
public List<E> findCandidates(AABB query) {
List<E> candidates = new ArrayList<>();
findCandidatesRecursive(root, query, candidates);
return candidates;
} }
/*
* private void getAllIntersectingElementsRecursive(Node<E> node, AABB query, List<E> result) {
* if (node == null) {
* return;
*/ }
private void findCandidatesRecursive(Node<E> node, AABB query, List<E> result) {
if (node == null || !node.getAabb().intersects(query)) { // Broad-phase pruning (includes touching points)
if (!node.getAabb().overlaps(query)) {
return; return;
} }
if (node.isLeaf()) { if (node.isLeaf()) {
if (aabbFunction.apply(node.getElement()).intersects(query)) { // Leaf-AABB is all ready in heap
if (node.getAabb().overlaps(query)) {
result.add(node.getElement()); result.add(node.getElement());
} }
} else { return;
for (Node<E> child : node.getChildren()) {
findCandidatesRecursive(child, query, result);
} }
for (Node<E> child : node.getChildren()) {
getAllIntersectingElementsRecursive(child, query, result);
} }
} }
......
...@@ -10,11 +10,6 @@ public class Node<E> { ...@@ -10,11 +10,6 @@ public class Node<E> {
private E element; private E element;
public Node(E element, AABB aabb) { public Node(E element, AABB aabb) {
// // Anchor-3DPoint given by means of its coords
// double zeroX = 0;
// double zeroY = 0;
// double zeroZ = 0;
// this.aabb = new AABB(zeroX, zeroY, zeroZ, zeroX, zeroY, zeroZ);
this.aabb = aabb; this.aabb = aabb;
this.element = element; this.element = element;
} }
......
...@@ -21,18 +21,6 @@ public class AABBTest { ...@@ -21,18 +21,6 @@ public class AABBTest {
@Test @Test
public void testContainsAABB() { public void testContainsAABB() {
// System.out.println("=== AABB TEST ===");
ConcretePolygon polygon = new ConcretePolygon();
LinearRing exterior = new LinearRing(LinearRingType.EXTERIOR);
exterior.addVertex(new Vertex(0, 0, 0));
exterior.addVertex(new Vertex(1, 0, 0));
exterior.addVertex(new Vertex(0, 1, 0));
exterior.addVertex(new Vertex(0, 0, 0));
polygon.setExteriorRing(exterior);
// Define points of a tetrahedron // Define points of a tetrahedron
Point3d p1 = new Point3d(0, 0, 0); Point3d p1 = new Point3d(0, 0, 0);
Point3d p2 = new Point3d(1, 0, 0); Point3d p2 = new Point3d(1, 0, 0);
...@@ -75,7 +63,6 @@ public class AABBTest { ...@@ -75,7 +63,6 @@ public class AABBTest {
List<Polygon> polys = Arrays.asList(base, side1, side2, side3); List<Polygon> polys = Arrays.asList(base, side1, side2, side3);
AABB aabb = AABB.ofPolygons(polys); AABB aabb = AABB.ofPolygons(polys);
// System.out.println("\nComputed " + aabb);
/*** 3D-Point-coords with Test Properties ***/ /*** 3D-Point-coords with Test Properties ***/
// Inside laying Point // Inside laying Point
...@@ -94,18 +81,10 @@ public class AABBTest { ...@@ -94,18 +81,10 @@ public class AABBTest {
// inside point // inside point
Assert.assertTrue(aabb.contains(testInsideX, testInsideY, testInsideZ)); Assert.assertTrue(aabb.contains(testInsideX, testInsideY, testInsideZ));
// System.out.println("\nTest point (inside): " + testInsideX + " " + testInsideY + " " + testInsideZ);
// System.out.println("-> contained?: " + aabb.contains(testInsideX, testInsideY, testInsideZ));
// outside point // outside point
Assert.assertFalse(aabb.contains(testOutsideX, testOutsideY, testOutsideZ)); Assert.assertFalse(aabb.contains(testOutsideX, testOutsideY, testOutsideZ));
// System.out.println("\nTest point (outside): " + testOutsideX + " " + testOutsideY + " " + testOutsideZ);
// System.out.println("-> contained?: " + aabb.contains(testOutsideX, testOutsideY, testOutsideZ));
// edge point // edge point
Assert.assertTrue(aabb.contains(testOnEdgeX, testOnEdgeY, testOnEdgeZ)); Assert.assertTrue(aabb.contains(testOnEdgeX, testOnEdgeY, testOnEdgeZ));
// System.out.println("\nTest point (on edge): " + testOnEdgeX + " " + testOnEdgeY + " " + testOnEdgeZ);
// System.out.println("-> contained?: " + aabb.contains(testOnEdgeX, testOnEdgeY, testOnEdgeZ));
} }
} }
package de.hft.stuttgart.citydoctor2.datastructure.bht; package de.hft.stuttgart.citydoctor2.datastructure.bht;
//import de.hft.stuttgart.citydoctor2.checks.aabb.*; import java.util.ArrayList;
import java.io.IOException; import java.util.List;
import java.util.*;
import org.junit.Assert;
import org.junit.Test;
import de.hft.stuttgart.citydoctor2.datastructure.ConcretePolygon; import de.hft.stuttgart.citydoctor2.datastructure.ConcretePolygon;
import de.hft.stuttgart.citydoctor2.datastructure.LinearRing; import de.hft.stuttgart.citydoctor2.datastructure.LinearRing;
import de.hft.stuttgart.citydoctor2.datastructure.Vertex; import de.hft.stuttgart.citydoctor2.datastructure.Vertex;
import de.hft.stuttgart.citydoctor2.parser.CityGmlParseException;
import de.hft.stuttgart.citydoctor2.parser.InvalidGmlFileException;
public class TestHouseAABB { public class TestHouseAABB {
public static void main(String[] args) throws CityGmlParseException, IOException, InvalidGmlFileException { @Test
System.out.println("===TestHouseAABB==="); public void testHouseAabbContainsPoints() {
/* // 3D House: base 2x2, height 2, roof +1
* 3D House of St.Niclas
*
* */
// Haus-Basis: Würfelgröße 2x2x2, Dachhöhe +1
double baseSize = 2.0; double baseSize = 2.0;
double height = 2.0; double height = 2.0;
double roofHeight = 1.0; double roofHeight = 1.0;
// points for Ground floor // ground points
Point3d b1 = new Point3d(0, 0, 0); Point3d b1 = new Point3d(0, 0, 0);
Point3d b2 = new Point3d(baseSize, 0, 0); Point3d b2 = new Point3d(baseSize, 0, 0);
Point3d b3 = new Point3d(baseSize, baseSize, 0); Point3d b3 = new Point3d(baseSize, baseSize, 0);
Point3d b4 = new Point3d(0, baseSize, 0); Point3d b4 = new Point3d(0, baseSize, 0);
// points for Roof // top of walls
Point3d t1 = new Point3d(0, 0, height); Point3d t1 = new Point3d(0, 0, height);
Point3d t2 = new Point3d(baseSize, 0, height); Point3d t2 = new Point3d(baseSize, 0, height);
Point3d t3 = new Point3d(baseSize, baseSize, height); Point3d t3 = new Point3d(baseSize, baseSize, height);
Point3d t4 = new Point3d(0, baseSize, height); Point3d t4 = new Point3d(0, baseSize, height);
// Roofpeaks // roof peaks (slightly beyond footprint in Y to create overhang)
Point3d roofPeak1 = new Point3d(baseSize/2, -0.2, height + roofHeight); Point3d roofPeak1 = new Point3d(baseSize / 2, -0.2, height + roofHeight);
Point3d roofPeak2 = new Point3d(baseSize/2, baseSize + 0.2, height + roofHeight); Point3d roofPeak2 = new Point3d(baseSize / 2, baseSize + 0.2, height + roofHeight);
List<ConcretePolygon> house = new ArrayList<>(); List<ConcretePolygon> house = new ArrayList<>();
// floor (b1,b2,b3,b4) // floor
house.add(createQuadPolygon(b1, b2, b3, b4)); house.add(createQuadPolygon(b1, b2, b3, b4));
// Walls // walls
house.add(createQuadPolygon(b1, b2, t2, t1)); // vorne house.add(createQuadPolygon(b1, b2, t2, t1));
house.add(createQuadPolygon(b2, b3, t3, t2)); // rechts house.add(createQuadPolygon(b2, b3, t3, t2));
house.add(createQuadPolygon(b3, b4, t4, t3)); // hinten house.add(createQuadPolygon(b3, b4, t4, t3));
house.add(createQuadPolygon(b4, b1, t1, t4)); // links house.add(createQuadPolygon(b4, b1, t1, t4));
// sealing under roof // ceiling under roof
house.add(createQuadPolygon(t1, t2, t3, t4)); house.add(createQuadPolygon(t1, t2, t3, t4));
// triangles of roof // roof triangles
house.add(createTrianglePolygon(t1, t2, roofPeak1)); house.add(createTrianglePolygon(t1, t2, roofPeak1));
house.add(createTrianglePolygon(t4, t3, roofPeak2)); house.add(createTrianglePolygon(t4, t3, roofPeak2));
// faces of roof
// roof faces
house.add(createQuadPolygon(t2, t3, roofPeak2, roofPeak1)); house.add(createQuadPolygon(t2, t3, roofPeak2, roofPeak1));
house.add(createQuadPolygon(t1, t4, roofPeak2, roofPeak1)); house.add(createQuadPolygon(t1, t4, roofPeak2, roofPeak1));
AABB houseAABB = AABB.ofPolygons(house); AABB houseAABB = AABB.ofPolygons(house);
// houseAABB.print();
// Define test points // points
double[] insidePoint = {1.0, 1.0, 1.0}; // clearly inside (center of the base cube) double[] insidePoint = {1.0, 1.0, 1.0}; // inside base volume
double[] roofPoint = {1.0, 1.0, 2.8}; // inside roof area but near the top double[] roofPoint = {1.0, 1.0, 2.8}; // below roof peak (z=3.0), should be inside AABB
double[] outsidePoint = {3.0, 3.0, 3.0}; // clearly outside double[] outsidePoint= {3.0, 3.0, 3.0}; // outside
double[] edgePoint = {2.0, 2.0, 2.0}; // exactly on the upper cube corner double[] edgePoint = {2.0, 2.0, 2.0}; // on top corner of base cube
// Test containment // AABB containment assertions
System.out.println("\nTest point (inside): " + Arrays.toString(insidePoint)); Assert.assertTrue("insidePoint should be inside AABB",
System.out.println("-> contained? " + houseAABB.contains(insidePoint[0], insidePoint[1], insidePoint[2])); houseAABB.contains(insidePoint[0], insidePoint[1], insidePoint[2]));
System.out.println("\nTest point (on roof): " + Arrays.toString(roofPoint)); Assert.assertTrue("roofPoint should be inside AABB (AABB includes the roof peak z)",
System.out.println("-> contained? " + houseAABB.contains(roofPoint[0], roofPoint[1], roofPoint[2])); houseAABB.contains(roofPoint[0], roofPoint[1], roofPoint[2]));
System.out.println("\nTest point (outside): " + Arrays.toString(outsidePoint)); Assert.assertFalse("outsidePoint should be outside AABB",
System.out.println("-> contained? " + houseAABB.contains(outsidePoint[0], outsidePoint[1], outsidePoint[2])); houseAABB.contains(outsidePoint[0], outsidePoint[1], outsidePoint[2]));
System.out.println("\nTest point (on edge): " + Arrays.toString(edgePoint)); Assert.assertTrue("edgePoint should be inside AABB (contains is inclusive)",
System.out.println("-> contained? " + houseAABB.contains(edgePoint[0], edgePoint[1], edgePoint[2])); houseAABB.contains(edgePoint[0], edgePoint[1], edgePoint[2]));
// optional: check expected bounds explicitly (helps debugging)
Assert.assertEquals(0.0, houseAABB.getMinX(), 0.0);
Assert.assertEquals(-0.2, houseAABB.getMinY(), 0.0);
Assert.assertEquals(0.0, houseAABB.getMinZ(), 0.0);
Assert.assertEquals(2.0, houseAABB.getMaxX(), 0.0);
Assert.assertEquals(2.2, houseAABB.getMaxY(), 0.0);
Assert.assertEquals(3.0, houseAABB.getMaxZ(), 0.0);
} }
/** Creates a triangular polygon from three points */
private static ConcretePolygon createTrianglePolygon(Point3d p1, Point3d p2, Point3d p3) { private static ConcretePolygon createTrianglePolygon(Point3d p1, Point3d p2, Point3d p3) {
// alocate poly
ConcretePolygon poly = new ConcretePolygon(); ConcretePolygon poly = new ConcretePolygon();
// alocate ring
LinearRing ring = new LinearRing(LinearRing.LinearRingType.EXTERIOR); LinearRing ring = new LinearRing(LinearRing.LinearRingType.EXTERIOR);
// vertices of triangle
ring.addVertex(new Vertex(p1.getX(), p1.getY(), p1.getZ())); ring.addVertex(new Vertex(p1.getX(), p1.getY(), p1.getZ()));
ring.addVertex(new Vertex(p2.getX(), p2.getY(), p2.getZ())); ring.addVertex(new Vertex(p2.getX(), p2.getY(), p2.getZ()));
ring.addVertex(new Vertex(p3.getX(), p3.getY(), p3.getZ())); ring.addVertex(new Vertex(p3.getX(), p3.getY(), p3.getZ()));
ring.addVertex(new Vertex(p1.getX(), p1.getY(), p1.getZ())); // Close ring ring.addVertex(new Vertex(p1.getX(), p1.getY(), p1.getZ())); // close ring
//
poly.setExteriorRing(ring); poly.setExteriorRing(ring);
return poly; return poly;
} }
/** Creates a quadrilateral polygon from four points */
private static ConcretePolygon createQuadPolygon(Point3d p1, Point3d p2, Point3d p3, Point3d p4) { private static ConcretePolygon createQuadPolygon(Point3d p1, Point3d p2, Point3d p3, Point3d p4) {
ConcretePolygon poly = new ConcretePolygon(); ConcretePolygon poly = new ConcretePolygon();
LinearRing ring = new LinearRing(LinearRing.LinearRingType.EXTERIOR); LinearRing ring = new LinearRing(LinearRing.LinearRingType.EXTERIOR);
ring.addVertex(new Vertex(p1.getX(), p1.getY(), p1.getZ())); ring.addVertex(new Vertex(p1.getX(), p1.getY(), p1.getZ()));
ring.addVertex(new Vertex(p2.getX(), p2.getY(), p2.getZ())); ring.addVertex(new Vertex(p2.getX(), p2.getY(), p2.getZ()));
ring.addVertex(new Vertex(p3.getX(), p3.getY(), p3.getZ())); ring.addVertex(new Vertex(p3.getX(), p3.getY(), p3.getZ()));
ring.addVertex(new Vertex(p4.getX(), p4.getY(), p4.getZ())); ring.addVertex(new Vertex(p4.getX(), p4.getY(), p4.getZ()));
ring.addVertex(new Vertex(p1.getX(), p1.getY(), p1.getZ())); // Close ring ring.addVertex(new Vertex(p1.getX(), p1.getY(), p1.getZ())); // close ring
poly.setExteriorRing(ring); poly.setExteriorRing(ring);
return poly; return poly;
......
...@@ -48,6 +48,7 @@ import de.hft.stuttgart.citydoctor2.datastructure.bht.AABBUtils; ...@@ -48,6 +48,7 @@ import de.hft.stuttgart.citydoctor2.datastructure.bht.AABBUtils;
* @author Baris Numanoglu * @author Baris Numanoglu
* *
*/ */
// TODO extend check with tree
public class NestedRingsCheckAABB extends Check { public class NestedRingsCheckAABB extends Check {
private static final List<CheckId> dependencies; private static final List<CheckId> dependencies;
...@@ -74,7 +75,7 @@ public class NestedRingsCheckAABB extends Check { ...@@ -74,7 +75,7 @@ public class NestedRingsCheckAABB extends Check {
// --- NEW: cache AABBs of inner rings for broad-phase --- // --- NEW: cache AABBs of inner rings for broad-phase ---
Map<LinearRing, AABB> ringBoxes = new HashMap<>(inner.size()); Map<LinearRing, AABB> ringBoxes = new HashMap<>(inner.size());
for (LinearRing r : inner) { for (LinearRing r : inner) {
ringBoxes.put(r, AABBUtils.computeAABBFromRing(r)); ringBoxes.put(r, AABB.of(r));
} }
for (LinearRing interiorRing : inner) { for (LinearRing interiorRing : inner) {
...@@ -90,7 +91,7 @@ public class NestedRingsCheckAABB extends Check { ...@@ -90,7 +91,7 @@ public class NestedRingsCheckAABB extends Check {
AABB checkBox = ringBoxes.get(checkRing); AABB checkBox = ringBoxes.get(checkRing);
// If the inner AABB is not fully inside the other ring's AABB, // If the inner AABB is not fully inside the other ring's AABB,
// "nested" is impossible -> skip narrow phase. // "nested" is impossible -> skip narrow phase.
if (! AABBUtils.containsAabb(interiorBox, checkBox)) { if (! interiorBox.contains(checkBox)) {
continue; continue;
} }
......
...@@ -61,6 +61,8 @@ import de.hft.stuttgart.citydoctor2.datastructure.bht.AABBUtils; ...@@ -61,6 +61,8 @@ import de.hft.stuttgart.citydoctor2.datastructure.bht.AABBUtils;
* Axis Aligned Boundin Box Early-Out-Version of Matthias Betz's Check * Axis Aligned Boundin Box Early-Out-Version of Matthias Betz's Check
* @author Baris Numanoglu * @author Baris Numanoglu
*/ */
// TODO extend check with tree
public class RingSelfIntCheckAABB extends Check { public class RingSelfIntCheckAABB extends Check {
private static final String EPSILON_NAME = "minVertexDistance"; private static final String EPSILON_NAME = "minVertexDistance";
...@@ -111,7 +113,7 @@ public class RingSelfIntCheckAABB extends Check { ...@@ -111,7 +113,7 @@ public class RingSelfIntCheckAABB extends Check {
// Precompute padded AABBs for all edges (padding = epsilon) // Precompute padded AABBs for all edges (padding = epsilon)
List<EdgeBox> edgeBoxes = new ArrayList<>(edges.size()); List<EdgeBox> edgeBoxes = new ArrayList<>(edges.size());
for (Edge e : edges) { for (Edge e : edges) {
edgeBoxes.add(new EdgeBox(e, AABBUtils.edgeAabb(e.getFrom(), e.getTo(), epsilon))); edgeBoxes.add(new EdgeBox(e, AABB.of(e.getFrom(), e.getTo(), epsilon)));
} }
// Optimized "point touches edge": first filter by padded edge AABB // Optimized "point touches edge": first filter by padded edge AABB
...@@ -136,7 +138,7 @@ public class RingSelfIntCheckAABB extends Check { ...@@ -136,7 +138,7 @@ public class RingSelfIntCheckAABB extends Check {
//skip if AABBs don't overlap //skip if AABBs don't overlap
EdgeBox eb2 = edgeBoxes.get(j); EdgeBox eb2 = edgeBoxes.get(j);
if (!AABBUtils.overlaps(eb1.box, eb2.box)) { if (!eb1.box.overlaps(eb2.box)) {
continue; continue;
} }
...@@ -204,7 +206,7 @@ public class RingSelfIntCheckAABB extends Check { ...@@ -204,7 +206,7 @@ public class RingSelfIntCheckAABB extends Check {
continue; continue;
} }
// Broad-phase: reject if vertex is outside padded edge AABB // Broad-phase: reject if vertex is outside padded edge AABB
if (!eb.box.encloses(v.getX(), v.getY(), v.getZ())) { if (!eb.box.contains(v.getX(), v.getY(), v.getZ())) {
continue; continue;
} }
// Narrow-phase: exact segment-point distance // Narrow-phase: exact segment-point distance
......
...@@ -28,7 +28,6 @@ import de.hft.stuttgart.citydoctor2.check.Check; ...@@ -28,7 +28,6 @@ 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;
...@@ -38,11 +37,10 @@ import de.hft.stuttgart.citydoctor2.checks.util.SelfIntersectionUtil; ...@@ -38,11 +37,10 @@ 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.datastructure.Polygon; import de.hft.stuttgart.citydoctor2.datastructure.Polygon;
import de.hft.stuttgart.citydoctor2.datastructure.ConcretePolygon;
import de.hft.stuttgart.citydoctor2.utils.PolygonIntersection; import de.hft.stuttgart.citydoctor2.utils.PolygonIntersection;
import de.hft.stuttgart.citydoctor2.datastructure.bht.AABB; import de.hft.stuttgart.citydoctor2.datastructure.bht.AABB;
import de.hft.stuttgart.citydoctor2.datastructure.bht.AABBUtils; import de.hft.stuttgart.citydoctor2.datastructure.bht.BoundingVolumeHierarchyTree;
/** /**
* Check for self intersecting solids * Check for self intersecting solids
...@@ -80,36 +78,29 @@ public class SolidSelfIntCheckAABB extends Check { ...@@ -80,36 +78,29 @@ public class SolidSelfIntCheckAABB extends Check {
return; return;
} }
// --- cheap broad-phase using per-polygon AABBs --- // --- broad-phase via BVH (AABB tree) ---
List<Polygon> polys = g.getPolygons(); List<Polygon> polys = g.getPolygons();
if (polys == null || polys.size() <= 1) { if (polys == null || polys.size() <= 1) {
g.addCheckResult(new CheckResult(this, ResultStatus.OK, null)); g.addCheckResult(new CheckResult(this, ResultStatus.OK, null));
return; return;
} }
// Building BVH Tree CheckResult cr;
// Build AABBs once // Build BVH on polygons, but compute AABBs from the *original* polygons
AABB[] boxes = new AABB[polys.size()]; BoundingVolumeHierarchyTree<Polygon> tree =
for (int i = 0; i < polys.size(); i++) { new BoundingVolumeHierarchyTree<>(polys, p -> AABB.of(p.getOriginal()));
ConcretePolygon cp = polys.get(i).getOriginal(); // TODO: comparison with older version without tree
boxes[i] = AABB.of(cp); List<PolygonIntersection> intersections =
} SelfIntersectionUtil.calculateSolidSelfIntersection(g, 0.001, tree);
// If no AABB pair overlaps, self-intersection is impossible
if (!AABB.doAnyBoxesOverlap(boxes)) {
g.addCheckResult(new CheckResult(this, ResultStatus.OK, null));
return;
}
// --- ---
CheckResult cr;
List<PolygonIntersection> intersections = SelfIntersectionUtil.calculateSolidSelfIntersection(g, 0.001, tree);
if (intersections.isEmpty()) { if (intersections.isEmpty()) {
cr = new CheckResult(this, ResultStatus.OK, null); cr = new CheckResult(this, ResultStatus.OK, null);
} else { } else {
CheckError e = new SolidSelfIntError(g, intersections); CheckError e = new SolidSelfIntError(g, intersections);
cr = new CheckResult(this, ResultStatus.ERROR, e); cr = new CheckResult(this, ResultStatus.ERROR, e);
} }
g.addCheckResult(cr); g.addCheckResult(cr);
} }
......
...@@ -85,6 +85,7 @@ public class SolidSelfIntCheck extends Check { ...@@ -85,6 +85,7 @@ public class SolidSelfIntCheck extends Check {
return; return;
} }
CheckResult cr; CheckResult cr;
// TODO: reproduce older version by Mathias of calculateSolidSefInt to compare
List<PolygonIntersection> intersections = SelfIntersectionUtil.calculateSolidSelfIntersection(g, delta); 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);
......
...@@ -91,43 +91,107 @@ public class SelfIntersectionUtil { ...@@ -91,43 +91,107 @@ public class SelfIntersectionUtil {
} }
public static List<PolygonIntersection> calculateSolidSelfIntersection(Geometry g, double delta, BoundingVolumeHierarchyTree<Polygon> tree) { // Tesselation and filtering degenerated triangles
private static List<TesselatedPolygon> tesselateAndFilter(Geometry g, double delta) {
List<TesselatedPolygon> tesselatedPolygons = new ArrayList<>(); List<TesselatedPolygon> tesselatedPolygons = new ArrayList<>();
for (Polygon p : g.getPolygons()) { for (Polygon p : g.getPolygons()) {
TesselatedPolygon tessPolygon = EarcutTesselator.tesselatePolygon(p); TesselatedPolygon tessPolygon = EarcutTesselator.tesselatePolygon(p);
for (Iterator<Triangle3d> iterator = tessPolygon.getTriangles().iterator(); iterator.hasNext();) {
Triangle3d t = iterator.next(); for (Iterator<Triangle3d> it = tessPolygon.getTriangles().iterator(); it.hasNext();) {
Triangle3d t = it.next();
List<Vector3d> vertices = new ArrayList<>(3); List<Vector3d> vertices = new ArrayList<>(3);
vertices.add(t.getP1()); vertices.add(t.getP1());
vertices.add(t.getP2()); vertices.add(t.getP2());
vertices.add(t.getP3()); vertices.add(t.getP3());
Vector3d centroid = CovarianceMatrix.getCentroid(vertices); Vector3d centroid = CovarianceMatrix.getCentroid(vertices);
EigenvalueDecomposition ed = OrthogonalRegressionPlane.decompose(vertices, centroid); EigenvalueDecomposition ed = OrthogonalRegressionPlane.decompose(vertices, centroid);
Matrix eigenValues = ed.getD(); Matrix eigenValues = ed.getD();
double[] eigenValuesArray = new double[3]; double ev1 = eigenValues.get(1, 1);
eigenValuesArray[0] = eigenValues.get(0, 0);
eigenValuesArray[1] = eigenValues.get(1, 1); if (ev1 < delta) {
eigenValuesArray[2] = eigenValues.get(2, 2); it.remove();
if (eigenValuesArray[1] < delta) {
iterator.remove();
} }
} }
tesselatedPolygons.add(tessPolygon); tesselatedPolygons.add(tessPolygon);
} }
return tesselatedPolygons;
}
/*
* @ Baris Numanoglu
*
* New Version with tree query
* */
public static List<PolygonIntersection> calculateSolidSelfIntersection(
Geometry g, double delta, BoundingVolumeHierarchyTree<Polygon> tree) {
List<TesselatedPolygon> tesselatedPolygons = tesselateAndFilter(g, delta);
// Map: original ConcretePolygon (identity) -> index in tesselatedPolygons
IdentityHashMap<ConcretePolygon, Integer> indexByOriginal = new IdentityHashMap<>();
for (int i = 0; i < tesselatedPolygons.size(); i++) {
Polygon p = tesselatedPolygons.get(i).getOriginal(); // returns Polygon
ConcretePolygon orig = p.getOriginal(); // returns ConcretePolygon
indexByOriginal.put(orig, i);
}
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);
List<Polygon> candidates = tree.findCandidates(AABB.of(p1.getOriginal()));
// Query AABB based on original polygon (consistent with tree construction)
ConcretePolygon p1Orig = p1.getOriginal().getOriginal(); // p1:teselatedPoly->Polygon->Concrete Polygon
AABB q = AABB.of(p1Orig);
List<Polygon> candidates = tree.getAllIntersectingElements(q);
if (candidates.isEmpty()) { if (candidates.isEmpty()) {
// TODO: continue; // no candidate ergo no overlap
} }
for (Polygon cand : candidates) {
ConcretePolygon candOrig = cand.getOriginal();
Integer jObj = indexByOriginal.get(candOrig);
if (jObj == null) {
continue; // candidate not in tesselated-Liste (RE: edge case)
}
int j = jObj.intValue();
if (j <= i) {
continue; // avoids double pairwise checks and self-pair
}
TesselatedPolygon p2 = tesselatedPolygons.get(j);
// TODO may be later a further tree in here
GeometrySelfIntersection inter = doPolygonsIntersect(p1, p2, delta);
if (inter != null) {
intersections.add(PolygonIntersection.triangles(inter.t1(), inter.t2()));
}
}
}
return intersections;
}
// Older Version without Tree: going to be used for comparison
public static List<PolygonIntersection> calculateSolidSelfIntersection(Geometry g, double delta) {
List<TesselatedPolygon> tesselatedPolygons = tesselateAndFilter(g, delta);
List<PolygonIntersection> intersections = new ArrayList<>();
for (int i = 0; i < tesselatedPolygons.size() - 1; 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, delta); GeometrySelfIntersection inter = doPolygonsIntersect(p1, p2, delta);
if (intersection != null) { if (inter != null) {
intersections.add(PolygonIntersection.triangles(intersection.t1(), intersection.t2())); intersections.add(PolygonIntersection.triangles(inter.t1(), inter.t2()));
} }
} }
} }
......
...@@ -4,6 +4,7 @@ package de.hft.stuttgart.citydoctor2.checks.bht; ...@@ -4,6 +4,7 @@ package de.hft.stuttgart.citydoctor2.checks.bht;
import de.hft.stuttgart.citydoctor2.datastructure.ConcretePolygon; import de.hft.stuttgart.citydoctor2.datastructure.ConcretePolygon;
import de.hft.stuttgart.citydoctor2.datastructure.LinearRing; import de.hft.stuttgart.citydoctor2.datastructure.LinearRing;
import de.hft.stuttgart.citydoctor2.datastructure.Vertex; import de.hft.stuttgart.citydoctor2.datastructure.Vertex;
import de.hft.stuttgart.citydoctor2.datastructure.Geometry;
import java.util.Arrays; import java.util.Arrays;
import java.util.List; import java.util.List;
...@@ -94,7 +95,8 @@ public class GeometryChecksWithAABBTest { ...@@ -94,7 +95,8 @@ public class GeometryChecksWithAABBTest {
ConcretePolygon p = new ConcretePolygon(); ConcretePolygon p = new ConcretePolygon();
p.setExteriorRing(ring); p.setExteriorRing(ring);
Geometry g = new Geometry(GeometryType.SOLID, Lod.LOD2); // TODO Check semantics of Orientation
Geometry g = new Geometry(GeometryType.SOLID, Lod.LOD2, Geometry.Orientation.OUTWARD);
g.addPolygon(p); // ensures parent links exist g.addPolygon(p); // ensures parent links exist
g.updateEdgesAndVertices(); g.updateEdgesAndVertices();
...@@ -133,7 +135,8 @@ public class GeometryChecksWithAABBTest { ...@@ -133,7 +135,8 @@ public class GeometryChecksWithAABBTest {
)); ));
p2.setExteriorRing(r2); p2.setExteriorRing(r2);
Geometry solid = new Geometry(GeometryType.SOLID, Lod.LOD2); // TODO Check Orientation<-OUTWARD again
Geometry solid = new Geometry(GeometryType.SOLID, Lod.LOD2, Geometry.Orientation.OUTWARD);
solid.getPolygons().addAll(List.of(p1, p2)); solid.getPolygons().addAll(List.of(p1, p2));
SolidSelfIntCheckAABB check = new SolidSelfIntCheckAABB(); SolidSelfIntCheckAABB check = new SolidSelfIntCheckAABB();
......
package de.hft.stuttgart.citydoctor2.checks.util;
import static org.junit.Assert.*;
import java.util.List;
import org.junit.Test;
import de.hft.stuttgart.citydoctor2.check.ValidationConfiguration;
import de.hft.stuttgart.citydoctor2.datastructure.Building;
import de.hft.stuttgart.citydoctor2.datastructure.CityDoctorModel;
import de.hft.stuttgart.citydoctor2.datastructure.Geometry;
import de.hft.stuttgart.citydoctor2.datastructure.GeometryType;
import de.hft.stuttgart.citydoctor2.datastructure.Lod;
import de.hft.stuttgart.citydoctor2.datastructure.Polygon;
import de.hft.stuttgart.citydoctor2.datastructure.bht.AABB;
import de.hft.stuttgart.citydoctor2.datastructure.bht.BoundingVolumeHierarchyTree;
import de.hft.stuttgart.citydoctor2.parser.CityGmlParseException;
import de.hft.stuttgart.citydoctor2.parser.CityGmlParser;
import de.hft.stuttgart.citydoctor2.parser.InvalidGmlFileException;
import de.hft.stuttgart.citydoctor2.utils.PolygonIntersection;
public class SolidSelfIntersectionBVHUtilTest {
@Test
public void testBVHCalculateOnKnownGoodModel() throws CityGmlParseException, InvalidGmlFileException {
ValidationConfiguration config = ValidationConfiguration.loadStandardValidationConfig();
config.setSchematronFilePathInGlobalParameters(null);
CityDoctorModel m = CityGmlParser.parseCityGmlFile(
"src/test/resources/SolidSelfIntTest1.gml",
config.getParserConfiguration()
);
Building building = m.getBuildings().findFirst().orElseThrow();
Geometry g = building.getGeometry(GeometryType.SOLID, Lod.LOD2);
assertNotNull("Expected SOLID LOD2 geometry in test model", g);
List<Polygon> polys = g.getPolygons();
assertNotNull(polys);
assertTrue("Expected at least 2 polygons", polys.size() > 1);
BoundingVolumeHierarchyTree<Polygon> tree =
new BoundingVolumeHierarchyTree<>(polys, p -> AABB.of(p.getOriginal()));
double delta = 0.001;
// calls new method with trees
List<PolygonIntersection> intersections =
SelfIntersectionUtil.calculateSolidSelfIntersection(g, delta, tree);
// This file is a good example (no self intersection)
assertTrue("No self-intersections expected for SolidSelfIntTest1.gml",
intersections.isEmpty());
}
}
\ No newline at end of file
package de.hft.stuttgart.citydoctor2.checks.util;
import static org.junit.Assert.*;
import java.util.List;
import org.junit.Test;
import de.hft.stuttgart.citydoctor2.check.ValidationConfiguration;
import de.hft.stuttgart.citydoctor2.datastructure.Building;
import de.hft.stuttgart.citydoctor2.datastructure.CityDoctorModel;
import de.hft.stuttgart.citydoctor2.datastructure.Geometry;
import de.hft.stuttgart.citydoctor2.datastructure.GeometryType;
import de.hft.stuttgart.citydoctor2.datastructure.Lod;
import de.hft.stuttgart.citydoctor2.datastructure.Polygon;
import de.hft.stuttgart.citydoctor2.datastructure.bht.AABB;
import de.hft.stuttgart.citydoctor2.datastructure.bht.BoundingVolumeHierarchyTree;
import de.hft.stuttgart.citydoctor2.parser.CityGmlParseException;
import de.hft.stuttgart.citydoctor2.parser.CityGmlParser;
import de.hft.stuttgart.citydoctor2.parser.InvalidGmlFileException;
import de.hft.stuttgart.citydoctor2.utils.PolygonIntersection;
/*
* First comparison SolidSelfIntersection version with Bounding Volume Tree vs old version
*
* @ Baris Numanoglu
* */
public class SolidSelfIntersectionOldVsNewTest {
@Test
public void testOldVsNewSameResultCount() throws CityGmlParseException, InvalidGmlFileException {
compareOnFile("src/test/resources/SolidSelfIntTest1.gml", 0.001);
}
@Test
public void testOldVsNewSameResultCountFalsePositiveExample1() throws CityGmlParseException, InvalidGmlFileException {
compareOnFile("src/test/resources/SolidSelfIntTest-known_false_positive1.gml", 0.001);
}
@Test
public void testOldVsNewSameResultCountFalsePositiveExample2() throws CityGmlParseException, InvalidGmlFileException {
compareOnFile("src/test/resources/SolidSelfIntTest-known_false_positive2.gml", 0.001);
}
private void compareOnFile(String gmlPath, double delta) throws CityGmlParseException, InvalidGmlFileException {
ValidationConfiguration config = ValidationConfiguration.loadStandardValidationConfig();
config.setSchematronFilePathInGlobalParameters(null);
CityDoctorModel m = CityGmlParser.parseCityGmlFile(gmlPath, config.getParserConfiguration());
Building building = m.getBuildings().findFirst().orElseThrow();
Geometry g = building.getGeometry(GeometryType.SOLID, Lod.LOD2);
assertNotNull("Expected SOLID LOD2 geometry in test model: " + gmlPath, g);
List<Polygon> polys = g.getPolygons();
assertNotNull(polys);
assertTrue("Expected at least 2 polygons in: " + gmlPath, polys.size() > 1);
BoundingVolumeHierarchyTree<Polygon> tree =
new BoundingVolumeHierarchyTree<>(polys, p -> AABB.of(p.getOriginal()));
List<PolygonIntersection> oldRes = SelfIntersectionUtil.calculateSolidSelfIntersection(g, delta);
List<PolygonIntersection> newRes = SelfIntersectionUtil.calculateSolidSelfIntersection(g, delta, tree);
assertEquals("Old vs new self-intersection result count differs for: " + gmlPath,
oldRes.size(), newRes.size());
}
}
\ No newline at end of file
...@@ -61,6 +61,8 @@ import de.hft.stuttgart.citydoctor2.healer.Healer; ...@@ -61,6 +61,8 @@ import de.hft.stuttgart.citydoctor2.healer.Healer;
import de.hft.stuttgart.citydoctor2.parser.CityGmlParseException; import de.hft.stuttgart.citydoctor2.parser.CityGmlParseException;
import de.hft.stuttgart.citydoctor2.parser.CityGmlParser; import de.hft.stuttgart.citydoctor2.parser.CityGmlParser;
import de.hft.stuttgart.citydoctor2.parser.InvalidGmlFileException; import de.hft.stuttgart.citydoctor2.parser.InvalidGmlFileException;
import de.hft.stuttgart.citydoctor2.database.CityObjectCache;
import de.hft.stuttgart.citydoctor2.database.UnconnectedCache;
public class HealMissingSolidTest { public class HealMissingSolidTest {
...@@ -70,7 +72,13 @@ public class HealMissingSolidTest { ...@@ -70,7 +72,13 @@ public class HealMissingSolidTest {
@Test @Test
public void testCreateSolid() throws CityDoctorWriteException, IOException, CityGmlParseException, InvalidGmlFileException { public void testCreateSolid() throws CityDoctorWriteException, IOException, CityGmlParseException, InvalidGmlFileException {
ValidationConfiguration config = ValidationConfiguration.loadStandardValidationConfig(); ValidationConfiguration config = ValidationConfiguration.loadStandardValidationConfig();
CityDoctorModel model = new CityDoctorModel(config.getParserConfiguration(), new File("test")); // edit @Numanoglu
CityDoctorModel model =
new CityDoctorModel(
config.getParserConfiguration(),
new File("test"),
new UnconnectedCache()
);
model.setCityModel(new CityModel()); model.setCityModel(new CityModel());
Building b = new Building(); Building b = new Building();
model.addBuilding(b); model.addBuilding(b);
......
...@@ -50,7 +50,9 @@ public class TestCityGmlAABB { ...@@ -50,7 +50,9 @@ public class TestCityGmlAABB {
CityDoctorModel model = de.hft.stuttgart.citydoctor2.healing.TestUtil.loadCityModel(filePathForParser, config); CityDoctorModel model = de.hft.stuttgart.citydoctor2.healing.TestUtil.loadCityModel(filePathForParser, config);
System.out.println("Model loaded successfully.\n"); System.out.println("Model loaded successfully.\n");
List<Building> buildings = model.getBuildings(); // TODO : use stream Building
List<Building> buildings = model.getBuildings().toList()
;
// --- 3) Menu: choose granularity --- // --- 3) Menu: choose granularity ---
System.out.println("\nChoose granularity:"); System.out.println("\nChoose granularity:");
...@@ -84,13 +86,12 @@ public class TestCityGmlAABB { ...@@ -84,13 +86,12 @@ public class TestCityGmlAABB {
BoundingVolumeHierarchyTree<ConcretePolygon> bvh = new BoundingVolumeHierarchyTree<>( BoundingVolumeHierarchyTree<ConcretePolygon> bvh = new BoundingVolumeHierarchyTree<>(
new ArrayList<>(polys), new ArrayList<>(polys),
AABBUtils::getAABB AABB::of
); );
System.out.println("Polygons: " + polys.size()); System.out.println("Polygons: " + polys.size());
if (bvh.getRoot() != null) { if (bvh.getRoot() != null) {
System.out.print("Root AABB: "); System.out.print("Root AABB: ");
bvh.getRoot().aabb.print(); //bvh.getRoot().aabb.print();
} }
} }
...@@ -104,13 +105,13 @@ public class TestCityGmlAABB { ...@@ -104,13 +105,13 @@ public class TestCityGmlAABB {
BoundingVolumeHierarchyTree<LinearRing> bvh = new BoundingVolumeHierarchyTree<>( BoundingVolumeHierarchyTree<LinearRing> bvh = new BoundingVolumeHierarchyTree<>(
new ArrayList<>(rings), new ArrayList<>(rings),
AABBUtils::computeAABBFromRing AABB::of
); );
System.out.println(label + " rings: " + rings.size()); System.out.println(label + " rings: " + rings.size());
if (bvh.getRoot() != null) { if (bvh.getRoot() != null) {
System.out.print("Root AABB: "); // TODO Assertion BLOCK
bvh.getRoot().aabb.print(); System.out.println("Root AABB: " + bvh.getRoot().getAabb());
} }
} }
...@@ -120,13 +121,13 @@ public class TestCityGmlAABB { ...@@ -120,13 +121,13 @@ public class TestCityGmlAABB {
BoundingVolumeHierarchyTree<Vertex> bvh = new BoundingVolumeHierarchyTree<>( BoundingVolumeHierarchyTree<Vertex> bvh = new BoundingVolumeHierarchyTree<>(
new ArrayList<>(verts), new ArrayList<>(verts),
AABBUtils::computeAABBFromVertex AABB::of
); );
System.out.println("Vertices: " + verts.size()); System.out.println("Vertices: " + verts.size());
if (bvh.getRoot() != null) { if (bvh.getRoot() != null) {
System.out.print("Root AABB: "); System.out.print("Root AABB: ");
bvh.getRoot().aabb.print(); //bvh.getRoot().aabb.print();
} }
} }
...@@ -252,7 +253,7 @@ public class TestCityGmlAABB { ...@@ -252,7 +253,7 @@ public class TestCityGmlAABB {
"Resource nicht im Klassenpfad gefunden: " + resourcePath + " (versucht als: " + rp + ")" "Resource nicht im Klassenpfad gefunden: " + resourcePath + " (versucht als: " + rp + ")"
); );
} }
// TODO Remove if redund.
private static String normalizeResourcePath(String path) { private static String normalizeResourcePath(String path) {
String p = path.replace('\\', '/'); String p = path.replace('\\', '/');
// Quellordner-Präfixe entfernen, falls mit übergeben // Quellordner-Präfixe entfernen, falls mit übergeben
......
...@@ -58,7 +58,7 @@ public class TestExample { ...@@ -58,7 +58,7 @@ public class TestExample {
// visitor // visitor
// filter duplicate polygons out // filter duplicate polygons out
Set<ConcretePolygon> allBuildingPolygons = new HashSet<>(); Set<ConcretePolygon> allBuildingPolygons = new HashSet<>();
for (Building b : model.getBuildings()) { for (Building b : model.getBuildings().toList()) {
b.accept(new CheckableUtilsVisitor() { b.accept(new CheckableUtilsVisitor() {
public void check(Polygon poly) { public void check(Polygon poly) {
allBuildingPolygons.add(poly.getOriginal()); allBuildingPolygons.add(poly.getOriginal());
...@@ -70,7 +70,7 @@ public class TestExample { ...@@ -70,7 +70,7 @@ public class TestExample {
// // for loop // // for loop
List<Polygon> allBuildingPolygons2 = new ArrayList<>(); List<Polygon> allBuildingPolygons2 = new ArrayList<>();
for (Building b : model.getBuildings()) { for (Building b : model.getBuildings().toList()) {
for (Geometry geom : b.getGeometries()) { for (Geometry geom : b.getGeometries()) {
for (Polygon p : geom.getPolygons()) { for (Polygon p : geom.getPolygons()) {
allBuildingPolygons2.add(p); allBuildingPolygons2.add(p);
...@@ -85,7 +85,7 @@ public class TestExample { ...@@ -85,7 +85,7 @@ public class TestExample {
// visitor // visitor
// filter duplicate linear rings out // filter duplicate linear rings out
Set<LinearRing> allBuildingLinearRings = new HashSet<>(); Set<LinearRing> allBuildingLinearRings = new HashSet<>();
for (Building b : model.getBuildings()) { for (Building b : model.getBuildings().toList()) {
b.accept(new CheckableUtilsVisitor() { b.accept(new CheckableUtilsVisitor() {
public void check(LinearRing ring) { public void check(LinearRing ring) {
allBuildingLinearRings.add(ring); allBuildingLinearRings.add(ring);
...@@ -100,7 +100,7 @@ public class TestExample { ...@@ -100,7 +100,7 @@ public class TestExample {
// visitor // visitor
// filter duplicate exterior rings out // filter duplicate exterior rings out
Set<LinearRing> allBuildingExteriorRings = new HashSet<>(); Set<LinearRing> allBuildingExteriorRings = new HashSet<>();
for (Building b : model.getBuildings()) { for (Building b : model.getBuildings().toList()) {
b.accept(new CheckableUtilsVisitor() { b.accept(new CheckableUtilsVisitor() {
public void check(LinearRing ring) { public void check(LinearRing ring) {
allBuildingExteriorRings.add(ring.getParent().getExteriorRing()); // ACHTUNG getParent allBuildingExteriorRings.add(ring.getParent().getExteriorRing()); // ACHTUNG getParent
...@@ -110,24 +110,8 @@ public class TestExample { ...@@ -110,24 +110,8 @@ public class TestExample {
System.out.println("\nExteriorRings via visitor:" + allBuildingExteriorRings); //new System.out.println("\nExteriorRings via visitor:" + allBuildingExteriorRings); //new
// // new: ExteriorRings (List)
// // for loop
// List<LinearRing> allBuildingExteriorRings2 = new ArrayList<>();
// for (Building b : model.getBuildings()) {
// for (Geometry geom : b.getGeometries()) {
// for (Polygon p : geom.getPolygons()) {
// allBuildingExteriorRings2.add(p.getOriginal().getExteriorRing());
// }
// }
// }
// System.out.println("\nExteriorRing List:" + allBuildingExteriorRings2); //new
// new: InnerRings
// visitor
// filter duplicate inner rings out
Set<LinearRing> allBuildingInnerRings = new HashSet<>(); Set<LinearRing> allBuildingInnerRings = new HashSet<>();
for (Building b : model.getBuildings()) { for (Building b : model.getBuildings().toList()) {
b.accept(new CheckableUtilsVisitor() { b.accept(new CheckableUtilsVisitor() {
public void check(Polygon poly) { public void check(Polygon poly) {
allBuildingInnerRings.addAll(poly.getOriginal().getInnerRings());// ACHTUNG getOriginal allBuildingInnerRings.addAll(poly.getOriginal().getInnerRings());// ACHTUNG getOriginal
......
...@@ -87,8 +87,9 @@ public class TestExampleAABB { ...@@ -87,8 +87,9 @@ public class TestExampleAABB {
Checker c = new Checker(config, model); Checker c = new Checker(config, model);
c.runChecks(); c.runChecks();
Building building = model.getBuildings().get(0); Building building = model.getBuildings()
.findFirst()
.orElseThrow(() -> new IllegalStateException("No buildings found"));
// assertFalse(building.containsError(CheckId.C_GE_S_SELF_INTERSECTION)); // assertFalse(building.containsError(CheckId.C_GE_S_SELF_INTERSECTION));
// assertEquals(building.containsError(CheckId.C_GE_S_SELF_INTERSECTION)); // assertEquals(building.containsError(CheckId.C_GE_S_SELF_INTERSECTION));
// assertNotNull(building.containsError(CheckId.C_GE_S_SELF_INTERSECTION)); // assertNotNull(building.containsError(CheckId.C_GE_S_SELF_INTERSECTION));
......
...@@ -535,43 +535,35 @@ public class HealerController { ...@@ -535,43 +535,35 @@ 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) {
// return; return;
// }
for (Building b : model.getBuildings()) {
// collect polygons
Map<Lod, List<Polygon>> availablePolygons = new EnumMap<>(Lod.class);
Map<Lod, Set<Polygon>> existingPolygons = new EnumMap<>(Lod.class);
collectPolygons(useBs, useBi, availablePolygons, existingPolygons, b);
if (useLod2) {
insertSolidGeometry(availablePolygons, b, existingPolygons, Lod.LOD2);
}
if (useLod3) {
insertSolidGeometry(availablePolygons, b, existingPolygons, Lod.LOD3);
}
if (useLod4) {
insertSolidGeometry(availablePolygons, b, existingPolygons, Lod.LOD4);
}
for (BuildingPart part : b.getBuildingParts()) {
// collect polygons
availablePolygons = new EnumMap<>(Lod.class);
existingPolygons = new EnumMap<>(Lod.class);
collectPolygons(useBs, useBi, availablePolygons, existingPolygons, part);
if (useLod2) {
insertSolidGeometry(availablePolygons, part, existingPolygons, Lod.LOD2);
}
if (useLod3) {
insertSolidGeometry(availablePolygons, part, existingPolygons, Lod.LOD3);
}
if (useLod4) {
insertSolidGeometry(availablePolygons, part, existingPolygons, Lod.LOD4);
}
}
} }
} model.getBuildings().forEach(b -> {
// --- Building ---
Map<Lod, List<Polygon>> availablePolygonsB = new EnumMap<>(Lod.class);
Map<Lod, Set<Polygon>> existingPolygonsB = new EnumMap<>(Lod.class);
collectPolygons(useBs, useBi, availablePolygonsB, existingPolygonsB, b);
if (useLod2) insertSolidGeometry(availablePolygonsB, b, existingPolygonsB, Lod.LOD2);
if (useLod3) insertSolidGeometry(availablePolygonsB, b, existingPolygonsB, Lod.LOD3);
if (useLod4) insertSolidGeometry(availablePolygonsB, b, existingPolygonsB, Lod.LOD4);
// --- BuildingParts ---
// Falls getBuildingParts() jetzt auch Stream liefert, ist das so korrekt:
b.getBuildingParts().forEach(part -> {
Map<Lod, List<Polygon>> availablePolygonsP = new EnumMap<>(Lod.class);
Map<Lod, Set<Polygon>> existingPolygonsP = new EnumMap<>(Lod.class);
collectPolygons(useBs, useBi, availablePolygonsP, existingPolygonsP, part);
if (useLod2) insertSolidGeometry(availablePolygonsP, part, existingPolygonsP, Lod.LOD2);
if (useLod3) insertSolidGeometry(availablePolygonsP, part, existingPolygonsP, Lod.LOD3);
if (useLod4) insertSolidGeometry(availablePolygonsP, part, existingPolygonsP, Lod.LOD4);
});
});
}
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, AbstractBuilding b) { Map<Lod, Set<Polygon>> existingPolygons, AbstractBuilding b) {
if (useBs) { if (useBs) {
......
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