Skip to content
GitLab
Projects
Groups
Snippets
/
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Sign in
Toggle navigation
Menu
Open sidebar
CityDoctor
CityDoctor2
Commits
54c56f2d
Commit
54c56f2d
authored
Apr 20, 2026
by
Numanoglu
Browse files
Test with a synthetic Building added
parent
7eb83cde
Pipeline
#12392
failed with stage
in 1 minute and 28 seconds
Changes
1
Pipelines
1
Hide whitespace changes
Inline
Side-by-side
CityDoctorParent/CityDoctorValidation/src/test/java/de/hft/stuttgart/citydoctor2/checks/util/SolidSelfIntersectionBuildingTest.java
0 → 100644
View file @
54c56f2d
package
de.hft.stuttgart.citydoctor2.checks.util
;
import
static
org
.
junit
.
Assert
.
assertEquals
;
import
static
org
.
junit
.
Assert
.
assertFalse
;
import
static
org
.
junit
.
Assert
.
assertNotNull
;
import
static
org
.
junit
.
Assert
.
assertTrue
;
import
java.util.List
;
import
org.junit.jupiter.api.Test
;
import
de.hft.stuttgart.citydoctor2.datastructure.Building
;
import
de.hft.stuttgart.citydoctor2.datastructure.ConcretePolygon
;
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.LinearRing
;
import
de.hft.stuttgart.citydoctor2.datastructure.LinearRing.LinearRingType
;
import
de.hft.stuttgart.citydoctor2.datastructure.Lod
;
import
de.hft.stuttgart.citydoctor2.datastructure.Polygon
;
import
de.hft.stuttgart.citydoctor2.datastructure.Vertex
;
import
de.hft.stuttgart.citydoctor2.datastructure.bht.AABB
;
import
de.hft.stuttgart.citydoctor2.datastructure.bht.BoundingVolumeHierarchyTree
;
import
de.hft.stuttgart.citydoctor2.utils.PolygonIntersection
;
public
class
SolidSelfIntersectionBuildingTest
{
private
static
final
double
DELTA
=
0.001
;
private
static
final
int
MIN_EXPECTED_POLYGONS
=
60
;
@Test
public
void
testBvhBuildAndQueryOnLod2AndLod3
()
{
Building
building
=
createDenseBuildingWithoutIntersections
();
assertBvhBuildAndQuery
(
building
.
getGeometry
(
GeometryType
.
SOLID
,
Lod
.
LOD2
),
"LOD2"
);
assertBvhBuildAndQuery
(
building
.
getGeometry
(
GeometryType
.
SOLID
,
Lod
.
LOD3
),
"LOD3"
);
}
@Test
public
void
testOldVsNewSameResultCountOnIntersectingLod2AndLod3
()
{
Building
building
=
createDenseBuildingWithIntersections
();
assertOldVsNewComparison
(
building
.
getGeometry
(
GeometryType
.
SOLID
,
Lod
.
LOD2
),
"LOD2"
);
assertOldVsNewComparison
(
building
.
getGeometry
(
GeometryType
.
SOLID
,
Lod
.
LOD3
),
"LOD3"
);
}
private
void
assertBvhBuildAndQuery
(
Geometry
geometry
,
String
lodLabel
)
{
List
<
Polygon
>
polygons
=
requireValidGeometry
(
geometry
,
lodLabel
);
BoundingVolumeHierarchyTree
<
Polygon
>
tree
=
new
BoundingVolumeHierarchyTree
<>(
polygons
,
p
->
AABB
.
of
(
p
.
getOriginal
()),
BoundingVolumeHierarchyTree
.
BuildConfig
.
binaryDefault
());
Polygon
probe
=
polygons
.
get
(
0
);
AABB
probeAabb
=
AABB
.
of
(
probe
.
getOriginal
());
assertNotNull
(
"Probe AABB must not be null for "
+
lodLabel
,
probeAabb
);
List
<
Polygon
>
candidates
=
tree
.
getAllIntersectingElements
(
probeAabb
);
int
nonEmptyQueries
=
0
;
int
totalCandidates
=
0
;
int
maxCandidates
=
0
;
for
(
Polygon
polygon
:
polygons
)
{
AABB
query
=
AABB
.
of
(
polygon
.
getOriginal
());
assertNotNull
(
"Query AABB must not be null for "
+
lodLabel
,
query
);
List
<
Polygon
>
perPolygonCandidates
=
tree
.
getAllIntersectingElements
(
query
);
if
(!
perPolygonCandidates
.
isEmpty
())
{
nonEmptyQueries
++;
}
int
currentSize
=
perPolygonCandidates
.
size
();
totalCandidates
+=
currentSize
;
if
(
currentSize
>
maxCandidates
)
{
maxCandidates
=
currentSize
;
}
}
printBvhStats
(
lodLabel
,
polygons
.
size
(),
candidates
.
size
(),
nonEmptyQueries
,
totalCandidates
,
maxCandidates
);
assertTrue
(
"Expected at least one non-empty BVH query on "
+
lodLabel
,
nonEmptyQueries
>
0
);
}
private
void
assertOldVsNewComparison
(
Geometry
geometry
,
String
lodLabel
)
{
List
<
Polygon
>
polygons
=
requireValidGeometry
(
geometry
,
lodLabel
);
List
<
PolygonIntersection
>
oldRes
=
SelfIntersectionUtil
.
calculateSolidSelfIntersection0
(
geometry
,
DELTA
);
assertNotNull
(
"Old result list must not be null for "
+
lodLabel
,
oldRes
);
BoundingVolumeHierarchyTree
<
Polygon
>
externalTree
=
new
BoundingVolumeHierarchyTree
<>(
polygons
,
p
->
AABB
.
of
(
p
.
getOriginal
()),
BoundingVolumeHierarchyTree
.
BuildConfig
.
binaryDefault
());
List
<
PolygonIntersection
>
oldTreeRes
=
SelfIntersectionUtil
.
calculateSolidSelfIntersection
(
geometry
,
DELTA
,
externalTree
);
assertNotNull
(
"Old+tree result list must not be null for "
+
lodLabel
,
oldTreeRes
);
List
<
PolygonIntersection
>
newRes
=
SelfIntersectionUtil
.
calculateSolidSelfIntersectionWithTree
(
geometry
,
DELTA
);
assertNotNull
(
"New result list must not be null for "
+
lodLabel
,
newRes
);
printComparisonStats
(
lodLabel
,
polygons
.
size
(),
oldRes
.
size
(),
oldTreeRes
.
size
(),
newRes
.
size
());
assertEquals
(
"Old vs external-tree differs for "
+
lodLabel
,
oldRes
.
size
(),
oldTreeRes
.
size
());
assertEquals
(
"Old vs new-tree differs for "
+
lodLabel
,
oldRes
.
size
(),
newRes
.
size
());
}
private
List
<
Polygon
>
requireValidGeometry
(
Geometry
geometry
,
String
lodLabel
)
{
assertNotNull
(
"Expected geometry for "
+
lodLabel
,
geometry
);
List
<
Polygon
>
polygons
=
geometry
.
getPolygons
();
assertNotNull
(
"Polygon list must not be null for "
+
lodLabel
,
polygons
);
assertFalse
(
"Polygon list must not be empty for "
+
lodLabel
,
polygons
.
isEmpty
());
assertTrue
(
"Expected many polygons for "
+
lodLabel
+
" (got "
+
polygons
.
size
()
+
")"
,
polygons
.
size
()
>=
MIN_EXPECTED_POLYGONS
);
return
polygons
;
}
private
void
printBvhStats
(
String
lodLabel
,
int
polygonCount
,
int
probeCandidateCount
,
int
nonEmptyQueries
,
int
totalCandidates
,
int
maxCandidates
)
{
System
.
out
.
printf
(
"[BVH][%s] polygons=%d, probeCandidates=%d, nonEmptyQueries=%d/%d, totalCandidates=%d, maxPerQuery=%d%n"
,
lodLabel
,
polygonCount
,
probeCandidateCount
,
nonEmptyQueries
,
polygonCount
,
totalCandidates
,
maxCandidates
);
}
private
void
printComparisonStats
(
String
lodLabel
,
int
polygonCount
,
int
oldCount
,
int
oldTreeCount
,
int
newCount
)
{
System
.
out
.
printf
(
"[SelfInt][%s] polygons=%d, old=%d, old+tree=%d, new=%d%n"
,
lodLabel
,
polygonCount
,
oldCount
,
oldTreeCount
,
newCount
);
}
private
Building
createDenseBuildingWithoutIntersections
()
{
Building
b
=
new
Building
();
b
.
addGeometry
(
createGridGeometry
(
Lod
.
LOD2
,
4
,
4
,
false
));
b
.
addGeometry
(
createGridGeometry
(
Lod
.
LOD3
,
6
,
6
,
false
));
return
b
;
}
private
Building
createDenseBuildingWithIntersections
()
{
Building
b
=
new
Building
();
b
.
addGeometry
(
createGridGeometry
(
Lod
.
LOD2
,
4
,
4
,
true
));
b
.
addGeometry
(
createGridGeometry
(
Lod
.
LOD3
,
6
,
6
,
true
));
return
b
;
}
private
Geometry
createGridGeometry
(
Lod
lod
,
int
xCount
,
int
yCount
,
boolean
addIntersections
)
{
Geometry
g
=
new
Geometry
(
GeometryType
.
SOLID
,
lod
,
Orientation
.
OUTWARD
);
double
spacing
=
6.0
;
double
width
=
4.0
;
double
depth
=
4.0
;
double
height
=
4.0
;
for
(
int
ix
=
0
;
ix
<
xCount
;
ix
++)
{
for
(
int
iy
=
0
;
iy
<
yCount
;
iy
++)
{
double
x
=
ix
*
spacing
;
double
y
=
iy
*
spacing
;
addBox
(
g
,
x
,
y
,
0.0
,
width
,
depth
,
height
);
}
}
// TODO Re-validate the Intersction-Idee
if
(
addIntersections
)
{
// Two additional boxes overlap each other and the grid neighborhood.
addBox
(
g
,
spacing
*
1.2
,
spacing
*
1.2
,
0.5
,
6.0
,
2.8
,
3.5
);
addBox
(
g
,
spacing
*
1.4
,
spacing
*
1.0
,
0.0
,
2.8
,
6.0
,
4.2
);
}
g
.
updateEdgesAndVertices
();
return
g
;
}
///----------------------------------- add geometric sub-entities ---------------------------///
private
void
addBox
(
Geometry
geometry
,
double
x
,
double
y
,
double
z
,
double
width
,
double
depth
,
double
height
)
{
Vertex
v000
=
new
Vertex
(
x
,
y
,
z
);
Vertex
v100
=
new
Vertex
(
x
+
width
,
y
,
z
);
Vertex
v110
=
new
Vertex
(
x
+
width
,
y
+
depth
,
z
);
Vertex
v010
=
new
Vertex
(
x
,
y
+
depth
,
z
);
Vertex
v001
=
new
Vertex
(
x
,
y
,
z
+
height
);
Vertex
v101
=
new
Vertex
(
x
+
width
,
y
,
z
+
height
);
Vertex
v111
=
new
Vertex
(
x
+
width
,
y
+
depth
,
z
+
height
);
Vertex
v011
=
new
Vertex
(
x
,
y
+
depth
,
z
+
height
);
addQuad
(
geometry
,
v000
,
v100
,
v110
,
v010
);
// bottom
addQuad
(
geometry
,
v001
,
v011
,
v111
,
v101
);
// top
addQuad
(
geometry
,
v000
,
v001
,
v101
,
v100
);
// front
addQuad
(
geometry
,
v100
,
v101
,
v111
,
v110
);
// right
addQuad
(
geometry
,
v110
,
v111
,
v011
,
v010
);
// back
addQuad
(
geometry
,
v010
,
v011
,
v001
,
v000
);
// left
}
private
void
addQuad
(
Geometry
geometry
,
Vertex
a
,
Vertex
b
,
Vertex
c
,
Vertex
d
)
{
ConcretePolygon
polygon
=
new
ConcretePolygon
();
LinearRing
ring
=
new
LinearRing
(
LinearRingType
.
EXTERIOR
);
polygon
.
setExteriorRing
(
ring
);
// ACHTUNG :Ensure polygon->geometry parent relation exists before adding vertices.
// LinearRing.addVertex() updates vertex adjacency via parent geometry.
geometry
.
addPolygon
(
polygon
);
ring
.
addVertex
(
a
);
ring
.
addVertex
(
b
);
ring
.
addVertex
(
c
);
ring
.
addVertex
(
d
);
ring
.
addVertex
(
a
);
}
}
Write
Preview
Supports
Markdown
0%
Try again
or
attach a new file
.
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment