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
2c9eea60
Commit
2c9eea60
authored
Jun 23, 2026
by
Numanoglu
Browse files
Refine AABB BVH heuristic exploration
parent
35a777b9
Changes
52
Hide whitespace changes
Inline
Side-by-side
CityDoctorParent/CityDoctorValidation/src/main/java/de/hft/stuttgart/citydoctor2/checks/util/BvhUsagePolicy.java
View file @
2c9eea60
...
...
@@ -4,8 +4,8 @@ import java.util.List;
import
java.util.Objects
;
import
java.util.function.Function
;
import
de.hft.stuttgart.citydoctor2.datastructure.
bht
.AABB
;
import
de.hft.stuttgart.citydoctor2.datastructure.
bht
.SplitStrategy
;
import
de.hft.stuttgart.citydoctor2.datastructure.
aabb
.AABB
;
import
de.hft.stuttgart.citydoctor2.datastructure.
aabb
.SplitStrategy
;
/**
* Central policy for cheap BVH decisions in geometry checks.
...
...
@@ -250,7 +250,7 @@ public final class BvhUsagePolicy {
public
static
boolean
shouldUseTree
(
BvhCheckType
checkType
,
BvhInputSummary
summary
)
{
int
elementCount
=
summary
.
getElementCount
();
if
(
elementCount
<
2
)
{
if
(
elementCount
<
2
0
)
{
return
false
;
}
if
(
elementCount
<
thresholdFor
(
checkType
))
{
...
...
CityDoctorParent/CityDoctorValidation/src/main/java/de/hft/stuttgart/citydoctor2/checks/util/SelfIntersectionUtil.java
View file @
2c9eea60
...
...
@@ -49,8 +49,8 @@ import de.hft.stuttgart.citydoctor2.datastructure.LinearRing;
import
de.hft.stuttgart.citydoctor2.datastructure.LinearRing.LinearRingType
;
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.datastructure.
aabb
.AABB
;
import
de.hft.stuttgart.citydoctor2.datastructure.
aabb
.BoundingVolumeHierarchyTree
;
import
de.hft.stuttgart.citydoctor2.edge.EdgePolygon
;
import
de.hft.stuttgart.citydoctor2.edge.IntersectPlanarPolygons
;
import
de.hft.stuttgart.citydoctor2.edge.MeshSurface
;
...
...
CityDoctorParent/CityDoctorValidation/src/test/java/de/hft/stuttgart/citydoctor2/checks/
bht
/BvhUsagePolicyTest.java
→
CityDoctorParent/CityDoctorValidation/src/test/java/de/hft/stuttgart/citydoctor2/checks/
aabb/core
/BvhUsagePolicyTest.java
View file @
2c9eea60
package
de.hft.stuttgart.citydoctor2.checks.
bht
;
package
de.hft.stuttgart.citydoctor2.checks.
aabb.core
;
import
de.hft.stuttgart.citydoctor2.checks.aabb.fixtures.SyntheticCityGmlLikeGeometryFactory
;
import
de.hft.stuttgart.citydoctor2.checks.aabb.fixtures.SyntheticRingGeometryFactory
;
import
de.hft.stuttgart.citydoctor2.checks.aabb.fixtures.SyntheticSolidGeometryFactory
;
import
static
org
.
junit
.
Assert
.
assertEquals
;
import
static
org
.
junit
.
Assert
.
assertFalse
;
import
static
org
.
junit
.
Assert
.
assertTrue
;
...
...
@@ -15,9 +19,17 @@ import de.hft.stuttgart.citydoctor2.datastructure.LinearRing;
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.SplitStrategy
;
import
de.hft.stuttgart.citydoctor2.datastructure.aabb.AABB
;
import
de.hft.stuttgart.citydoctor2.datastructure.aabb.SplitStrategy
;
/**
* Covers the current tree-usage policy with small, readable synthetic inputs.
*
* These tests are not meant to prove an optimal heuristic. They guard the
* conservative decisions that are currently wired into BvhUsagePolicy.
*
* @author Numanoglu
*/
public
class
BvhUsagePolicyTest
{
@Test
...
...
@@ -71,10 +83,12 @@ public class BvhUsagePolicyTest {
}
private
static
BvhUsagePolicy
.
BvhInputSummary
summaryForPolygons
(
Geometry
geometry
)
{
// Production code computes the same cheap summary from each element's AABB.
return
BvhUsagePolicy
.
BvhInputSummary
.
ofElements
(
geometry
.
getPolygons
(),
polygon
->
AABB
.
of
(
polygon
.
getOriginal
()));
}
private
static
List
<
AABB
>
edgeBoxes
(
Geometry
geometry
)
{
// Ring self-intersection uses segment boxes rather than whole polygon boxes.
List
<
AABB
>
boxes
=
new
ArrayList
<>();
for
(
Polygon
polygon
:
geometry
.
getPolygons
())
{
LinearRing
ring
=
polygon
.
getExteriorRing
();
...
...
CityDoctorParent/CityDoctorValidation/src/test/java/de/hft/stuttgart/citydoctor2/checks/
bht
/GeometryChecksWithAABBTest.java
→
CityDoctorParent/CityDoctorValidation/src/test/java/de/hft/stuttgart/citydoctor2/checks/
aabb/core
/GeometryChecksWithAABBTest.java
View file @
2c9eea60
package
de.hft.stuttgart.citydoctor2.checks.
bht
;
package
de.hft.stuttgart.citydoctor2.checks.
aabb.core
;
import
de.hft.stuttgart.citydoctor2.datastructure.ConcretePolygon
;
import
de.hft.stuttgart.citydoctor2.datastructure.Geometry
;
import
de.hft.stuttgart.citydoctor2.datastructure.GeometryType
;
import
de.hft.stuttgart.citydoctor2.datastructure.LinearRing
;
import
de.hft.stuttgart.citydoctor2.datastructure.Lod
;
import
de.hft.stuttgart.citydoctor2.datastructure.Vertex
;
import
de.hft.stuttgart.citydoctor2.datastructure.Geometry
;
import
java.util.Arrays
;
import
java.util.List
;
//
import de.hft.stuttgart.citydoctor2.checks.
geometry.*
;
//
import de.hft.stuttgart.citydoctor2.checks.aabb.
*; //<-evtl *geomCheks hier verlagern
import
de.hft.stuttgart.citydoctor2.
datastructure.*
;
import
de.hft.stuttgart.citydoctor2.checks.
aabb.NestedRingsCheckAABB
;
import
de.hft.stuttgart.citydoctor2.checks.aabb.
RingSelfIntCheckAABB
;
import
de.hft.stuttgart.citydoctor2.
checks.aabb.SolidSelfIntCheckAABB
;
/**
* Manual test runner for basic geometry checks with AABB involvement.
* Manual smoke runner for the older AABB check classes.
*
* The class is intentionally simple and prints its result to the console. It is
* useful when the checks are inspected locally, while systematic assertions live
* in the dedicated correctness tests.
*
* @author Numanoglu
*/
public
class
GeometryChecksWithAABBTest
{
public
static
void
main
(
String
[]
args
)
{
System
.
out
.
println
(
"=== Geometry Checks with AABB
–
Manual Test Runner ==="
);
System
.
out
.
println
(
"=== Geometry Checks with AABB
-
Manual Test Runner ==="
);
//testTooFewPoints();
testNestedRings
();
...
...
@@ -95,7 +103,7 @@ public class GeometryChecksWithAABBTest {
ConcretePolygon
p
=
new
ConcretePolygon
();
p
.
setExteriorRing
(
ring
);
//
TODO Check semantics of Orientation
//
Orientation is not the focus here; the geometry mainly provides parent links.
Geometry
g
=
new
Geometry
(
GeometryType
.
SOLID
,
Lod
.
LOD2
,
Geometry
.
Orientation
.
OUTWARD
);
g
.
addPolygon
(
p
);
// ensures parent links exist
...
...
CityDoctorParent/CityDoctorValidation/src/test/java/de/hft/stuttgart/citydoctor2/checks/
bht
/NestedRingCheckBvhVariantCityGmlTest.java
→
CityDoctorParent/CityDoctorValidation/src/test/java/de/hft/stuttgart/citydoctor2/checks/
aabb/correctness
/NestedRingCheckBvhVariantCityGmlTest.java
View file @
2c9eea60
package
de.hft.stuttgart.citydoctor2.checks.
bht
;
package
de.hft.stuttgart.citydoctor2.checks.
aabb.correctness
;
import
static
org
.
junit
.
Assert
.
assertEquals
;
import
static
org
.
junit
.
Assert
.
assertFalse
;
...
...
CityDoctorParent/CityDoctorValidation/src/test/java/de/hft/stuttgart/citydoctor2/checks/
bht
/NestedRingCheckBvhVariantSyntheticTest.java
→
CityDoctorParent/CityDoctorValidation/src/test/java/de/hft/stuttgart/citydoctor2/checks/
aabb/correctness
/NestedRingCheckBvhVariantSyntheticTest.java
View file @
2c9eea60
package
de.hft.stuttgart.citydoctor2.checks.
bht
;
package
de.hft.stuttgart.citydoctor2.checks.
aabb.correctness
;
import
de.hft.stuttgart.citydoctor2.checks.aabb.fixtures.SyntheticNestedRingGeometryFactory
;
import
static
org
.
junit
.
Assert
.
assertEquals
;
import
static
org
.
junit
.
Assert
.
assertFalse
;
import
static
org
.
junit
.
Assert
.
assertNotNull
;
...
...
CityDoctorParent/CityDoctorValidation/src/test/java/de/hft/stuttgart/citydoctor2/checks/
bht
/RingSelfIntCheckBvhVariantCityGmlTest.java
→
CityDoctorParent/CityDoctorValidation/src/test/java/de/hft/stuttgart/citydoctor2/checks/
aabb/correctness
/RingSelfIntCheckBvhVariantCityGmlTest.java
View file @
2c9eea60
package
de.hft.stuttgart.citydoctor2.checks.
bht
;
package
de.hft.stuttgart.citydoctor2.checks.
aabb.correctness
;
import
static
org
.
junit
.
Assert
.
assertEquals
;
import
static
org
.
junit
.
Assert
.
assertNotNull
;
...
...
CityDoctorParent/CityDoctorValidation/src/test/java/de/hft/stuttgart/citydoctor2/checks/
bht
/RingSelfIntCheckBvhVariantSyntheticTest.java
→
CityDoctorParent/CityDoctorValidation/src/test/java/de/hft/stuttgart/citydoctor2/checks/
aabb/correctness
/RingSelfIntCheckBvhVariantSyntheticTest.java
View file @
2c9eea60
package
de.hft.stuttgart.citydoctor2.checks.
bht
;
package
de.hft.stuttgart.citydoctor2.checks.
aabb.correctness
;
import
de.hft.stuttgart.citydoctor2.checks.aabb.fixtures.SyntheticRingGeometryFactory
;
import
static
org
.
junit
.
Assert
.
assertEquals
;
import
static
org
.
junit
.
Assert
.
assertNotNull
;
...
...
CityDoctorParent/CityDoctorValidation/src/test/java/de/hft/stuttgart/citydoctor2/checks/
bht
/SolidSelfIntersectionBVHUtilTest.java
→
CityDoctorParent/CityDoctorValidation/src/test/java/de/hft/stuttgart/citydoctor2/checks/
aabb/correctness
/SolidSelfIntersectionBVHUtilTest.java
View file @
2c9eea60
package
de.hft.stuttgart.citydoctor2.checks.
bht
;
package
de.hft.stuttgart.citydoctor2.checks.
aabb.correctness
;
import
static
org
.
junit
.
Assert
.
assertNotNull
;
import
static
org
.
junit
.
Assert
.
assertTrue
;
...
...
@@ -18,8 +18,8 @@ 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.datastructure.
aabb
.AABB
;
import
de.hft.stuttgart.citydoctor2.datastructure.
aabb
.BoundingVolumeHierarchyTree
;
import
de.hft.stuttgart.citydoctor2.exceptions.CityDoctorWriteException
;
import
de.hft.stuttgart.citydoctor2.checks.util.GeometryTestUtils
;
import
de.hft.stuttgart.citydoctor2.checks.util.SelfIntersectionUtil
;
...
...
CityDoctorParent/CityDoctorValidation/src/test/java/de/hft/stuttgart/citydoctor2/checks/
bht
/SolidSelfIntersectionBuildingTest.java
→
CityDoctorParent/CityDoctorValidation/src/test/java/de/hft/stuttgart/citydoctor2/checks/
aabb/correctness
/SolidSelfIntersectionBuildingTest.java
View file @
2c9eea60
package
de.hft.stuttgart.citydoctor2.checks.
bht
;
package
de.hft.stuttgart.citydoctor2.checks.
aabb.correctness
;
import
static
org
.
junit
.
Assert
.
assertEquals
;
import
static
org
.
junit
.
Assert
.
assertFalse
;
...
...
@@ -19,8 +19,8 @@ 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.datastructure.
aabb
.AABB
;
import
de.hft.stuttgart.citydoctor2.datastructure.
aabb
.BoundingVolumeHierarchyTree
;
import
de.hft.stuttgart.citydoctor2.checks.util.SelfIntersectionUtil
;
import
de.hft.stuttgart.citydoctor2.utils.PolygonIntersection
;
...
...
CityDoctorParent/CityDoctorValidation/src/test/java/de/hft/stuttgart/citydoctor2/checks/
bht
/SolidSelfIntersectionBvhVariantsSyntheticTest.java
→
CityDoctorParent/CityDoctorValidation/src/test/java/de/hft/stuttgart/citydoctor2/checks/
aabb/correctness
/SolidSelfIntersectionBvhVariantsSyntheticTest.java
View file @
2c9eea60
package
de.hft.stuttgart.citydoctor2.checks.
bht
;
package
de.hft.stuttgart.citydoctor2.checks.
aabb.correctness
;
import
de.hft.stuttgart.citydoctor2.checks.aabb.fixtures.SyntheticSolidGeometryFactory
;
import
static
org
.
junit
.
Assert
.
assertEquals
;
import
static
org
.
junit
.
Assert
.
assertNotNull
;
...
...
@@ -14,9 +16,9 @@ import org.junit.jupiter.api.Test;
import
de.hft.stuttgart.citydoctor2.datastructure.Geometry
;
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.datastructure.
bht
.SplitStrategy
;
import
de.hft.stuttgart.citydoctor2.datastructure.
aabb
.AABB
;
import
de.hft.stuttgart.citydoctor2.datastructure.
aabb
.BoundingVolumeHierarchyTree
;
import
de.hft.stuttgart.citydoctor2.datastructure.
aabb
.SplitStrategy
;
import
de.hft.stuttgart.citydoctor2.checks.util.SelfIntersectionUtil
;
import
de.hft.stuttgart.citydoctor2.utils.PolygonIntersection
;
...
...
CityDoctorParent/CityDoctorValidation/src/test/java/de/hft/stuttgart/citydoctor2/checks/
bht
/SolidSelfIntersectionOldVsNewTest.java
→
CityDoctorParent/CityDoctorValidation/src/test/java/de/hft/stuttgart/citydoctor2/checks/
aabb/correctness
/SolidSelfIntersectionOldVsNewTest.java
View file @
2c9eea60
package
de.hft.stuttgart.citydoctor2.checks.
bht
;
package
de.hft.stuttgart.citydoctor2.checks.
aabb.correctness
;
import
static
org
.
junit
.
Assert
.*;
...
...
@@ -18,8 +18,8 @@ import de.hft.stuttgart.citydoctor2.parser.CityGmlParser;
import
de.hft.stuttgart.citydoctor2.parser.InvalidGmlFileException
;
import
de.hft.stuttgart.citydoctor2.checks.util.SelfIntersectionUtil
;
import
de.hft.stuttgart.citydoctor2.utils.PolygonIntersection
;
import
de.hft.stuttgart.citydoctor2.datastructure.
bht
.AABB
;
import
de.hft.stuttgart.citydoctor2.datastructure.
bht
.BoundingVolumeHierarchyTree
;
import
de.hft.stuttgart.citydoctor2.datastructure.
aabb
.AABB
;
import
de.hft.stuttgart.citydoctor2.datastructure.
aabb
.BoundingVolumeHierarchyTree
;
/*
* First comparison SolidSelfIntersection version with Bounding Volume Tree vs old version
...
...
CityDoctorParent/CityDoctorValidation/src/test/java/de/hft/stuttgart/citydoctor2/checks/
bht
/BvhSyntheticScenarioCatalog.java
→
CityDoctorParent/CityDoctorValidation/src/test/java/de/hft/stuttgart/citydoctor2/checks/
aabb/fixtures
/BvhSyntheticScenarioCatalog.java
View file @
2c9eea60
package
de.hft.stuttgart.citydoctor2.checks.
bht
;
package
de.hft.stuttgart.citydoctor2.checks.
aabb.fixtures
;
import
java.util.List
;
...
...
@@ -15,14 +15,14 @@ import de.hft.stuttgart.citydoctor2.datastructure.Lod;
*
* @author Numanoglu
*/
final
class
BvhSyntheticScenarioCatalog
{
public
final
class
BvhSyntheticScenarioCatalog
{
private
static
final
double
EPSILON
=
0.001
;
private
BvhSyntheticScenarioCatalog
()
{
}
static
List
<
SsiScenario
>
ssiScenarios
()
{
public
static
List
<
SsiScenario
>
ssiScenarios
()
{
return
List
.
of
(
new
SsiScenario
(
"sep-grid-small"
,
SyntheticSolidGeometryFactory
.
separatedBoxGrid
(
Lod
.
LOD2
,
6
,
6
)),
...
...
@@ -74,7 +74,7 @@ final class BvhSyntheticScenarioCatalog {
SyntheticCityGmlLikeGeometryFactory
.
variedRoofDistrict
(
Lod
.
LOD2
,
240
)));
}
static
List
<
NestedScenario
>
nestedScenarios
()
{
public
static
List
<
NestedScenario
>
nestedScenarios
()
{
return
List
.
of
(
new
NestedScenario
(
"disjoint-small"
,
SyntheticNestedRingGeometryFactory
.
manyDisjointInnerRings
(
120
)),
...
...
@@ -108,7 +108,7 @@ final class BvhSyntheticScenarioCatalog {
SyntheticNestedRingGeometryFactory
.
spreadSweepInnerRings
(
240
,
20.0
)));
}
static
List
<
RsiScenario
>
rsiScenarios
()
{
public
static
List
<
RsiScenario
>
rsiScenarios
()
{
return
List
.
of
(
new
RsiScenario
(
"rsi-small"
,
SyntheticRingGeometryFactory
.
performanceRingGeometry
(
200
,
100
,
EPSILON
)),
...
...
@@ -132,9 +132,9 @@ final class BvhSyntheticScenarioCatalog {
SyntheticRingGeometryFactory
.
spreadSweepGeometry
(
240
,
30.0
)));
}
static
final
class
SsiScenario
{
final
String
name
;
final
Geometry
geometry
;
public
static
final
class
SsiScenario
{
public
final
String
name
;
public
final
Geometry
geometry
;
SsiScenario
(
String
name
,
Geometry
geometry
)
{
this
.
name
=
name
;
...
...
@@ -142,9 +142,9 @@ final class BvhSyntheticScenarioCatalog {
}
}
static
final
class
NestedScenario
{
final
String
name
;
final
ConcretePolygon
polygon
;
public
static
final
class
NestedScenario
{
public
final
String
name
;
public
final
ConcretePolygon
polygon
;
NestedScenario
(
String
name
,
ConcretePolygon
polygon
)
{
this
.
name
=
name
;
...
...
@@ -152,9 +152,9 @@ final class BvhSyntheticScenarioCatalog {
}
}
static
final
class
RsiScenario
{
final
String
name
;
final
Geometry
geometry
;
public
static
final
class
RsiScenario
{
public
final
String
name
;
public
final
Geometry
geometry
;
RsiScenario
(
String
name
,
Geometry
geometry
)
{
this
.
name
=
name
;
...
...
CityDoctorParent/CityDoctorValidation/src/test/java/de/hft/stuttgart/citydoctor2/checks/
bht
/SyntheticCityGmlLikeGeometryFactory.java
→
CityDoctorParent/CityDoctorValidation/src/test/java/de/hft/stuttgart/citydoctor2/checks/
aabb/fixtures
/SyntheticCityGmlLikeGeometryFactory.java
View file @
2c9eea60
package
de.hft.stuttgart.citydoctor2.checks.
bht
;
package
de.hft.stuttgart.citydoctor2.checks.
aabb.fixtures
;
import
de.hft.stuttgart.citydoctor2.datastructure.ConcretePolygon
;
import
de.hft.stuttgart.citydoctor2.datastructure.Geometry
;
...
...
@@ -19,7 +19,7 @@ import de.hft.stuttgart.citydoctor2.datastructure.Vertex;
*
* @author Numanoglu
*/
final
class
SyntheticCityGmlLikeGeometryFactory
{
public
final
class
SyntheticCityGmlLikeGeometryFactory
{
private
SyntheticCityGmlLikeGeometryFactory
()
{
}
...
...
@@ -28,7 +28,7 @@ final class SyntheticCityGmlLikeGeometryFactory {
* Creates several blocks with mixed compact, elongated, and roof-like buildings.
* Useful as a balanced city-district fixture with moderate spatial separation.
*/
static
Geometry
mixedUrbanDistrict
(
Lod
lod
,
int
blockColumns
,
int
blockRows
)
{
public
static
Geometry
mixedUrbanDistrict
(
Lod
lod
,
int
blockColumns
,
int
blockRows
)
{
Geometry
geometry
=
newSolid
(
lod
);
for
(
int
blockX
=
0
;
blockX
<
blockColumns
;
blockX
++)
{
for
(
int
blockY
=
0
;
blockY
<
blockRows
;
blockY
++)
{
...
...
@@ -45,7 +45,7 @@ final class SyntheticCityGmlLikeGeometryFactory {
* Creates block-perimeter buildings around open courtyards. This produces many
* nearby but not necessarily intersecting AABBs, closer to dense urban blocks.
*/
static
Geometry
courtyardDistrict
(
Lod
lod
,
int
blockCount
)
{
public
static
Geometry
courtyardDistrict
(
Lod
lod
,
int
blockCount
)
{
Geometry
geometry
=
newSolid
(
lod
);
for
(
int
block
=
0
;
block
<
blockCount
;
block
++)
{
double
originX
=
(
block
%
4
)
*
62.0
;
...
...
@@ -64,7 +64,7 @@ final class SyntheticCityGmlLikeGeometryFactory {
* Creates many small buildings with alternating flat and gabled roof shapes.
* Useful for a larger polygon count with city-like height and footprint jitter.
*/
static
Geometry
variedRoofDistrict
(
Lod
lod
,
int
buildingCount
)
{
public
static
Geometry
variedRoofDistrict
(
Lod
lod
,
int
buildingCount
)
{
Geometry
geometry
=
newSolid
(
lod
);
for
(
int
i
=
0
;
i
<
buildingCount
;
i
++)
{
double
x
=
(
i
%
12
)
*
13.0
+
((
i
/
12
)
%
2
)
*
4.0
;
...
...
CityDoctorParent/CityDoctorValidation/src/test/java/de/hft/stuttgart/citydoctor2/checks/
bht
/SyntheticNestedRingGeometryFactory.java
→
CityDoctorParent/CityDoctorValidation/src/test/java/de/hft/stuttgart/citydoctor2/checks/
aabb/fixtures
/SyntheticNestedRingGeometryFactory.java
View file @
2c9eea60
package
de.hft.stuttgart.citydoctor2.checks.
bht
;
package
de.hft.stuttgart.citydoctor2.checks.
aabb.fixtures
;
import
java.io.File
;
import
java.util.ArrayList
;
...
...
@@ -56,12 +56,12 @@ import de.hft.stuttgart.citydoctor2.datastructure.Vertex;
* @author Numanoglu
*/
final
class
SyntheticNestedRingGeometryFactory
{
public
final
class
SyntheticNestedRingGeometryFactory
{
private
SyntheticNestedRingGeometryFactory
()
{
}
static
Geometry
complexNestedRingGeometry
()
{
public
static
Geometry
complexNestedRingGeometry
()
{
Geometry
geometry
=
new
Geometry
(
GeometryType
.
SOLID
,
Lod
.
LOD2
,
Orientation
.
OUTWARD
);
for
(
ConcretePolygon
polygon
:
complexNestedRingPolygons
())
{
geometry
.
addPolygon
(
polygon
);
...
...
@@ -70,7 +70,7 @@ final class SyntheticNestedRingGeometryFactory {
return
geometry
;
}
static
List
<
ConcretePolygon
>
complexNestedRingPolygons
()
{
public
static
List
<
ConcretePolygon
>
complexNestedRingPolygons
()
{
List
<
ConcretePolygon
>
polygons
=
new
ArrayList
<>();
polygons
.
add
(
manyDisjointInnerRings
(
100
));
polygons
.
add
(
oneNestedPairAmongMany
(
96
));
...
...
@@ -81,7 +81,7 @@ final class SyntheticNestedRingGeometryFactory {
return
polygons
;
}
static
ConcretePolygon
manyDisjointInnerRings
(
int
count
)
{
public
static
ConcretePolygon
manyDisjointInnerRings
(
int
count
)
{
ConcretePolygon
polygon
=
basePolygon
(
count
);
for
(
int
i
=
0
;
i
<
count
;
i
++)
{
double
x
=
gridX
(
i
,
10
)
*
8.0
+
4.0
;
...
...
@@ -91,14 +91,14 @@ final class SyntheticNestedRingGeometryFactory {
return
polygon
;
}
static
ConcretePolygon
oneNestedPairAmongMany
(
int
count
)
{
public
static
ConcretePolygon
oneNestedPairAmongMany
(
int
count
)
{
ConcretePolygon
polygon
=
manyDisjointInnerRings
(
Math
.
max
(
0
,
count
-
2
));
polygon
.
addInteriorRing
(
squareRing
(
5.0
,
85.0
,
5.0
));
polygon
.
addInteriorRing
(
squareRing
(
5.0
,
85.0
,
1.0
));
return
polygon
;
}
static
ConcretePolygon
concentricNestedRings
(
int
count
)
{
public
static
ConcretePolygon
concentricNestedRings
(
int
count
)
{
ConcretePolygon
polygon
=
basePolygon
(
count
);
for
(
int
i
=
0
;
i
<
count
;
i
++)
{
double
halfSize
=
Math
.
max
(
1.0
,
count
-
i
);
...
...
@@ -107,7 +107,7 @@ final class SyntheticNestedRingGeometryFactory {
return
polygon
;
}
static
ConcretePolygon
overlappingAabbsButNotNested
(
int
count
)
{
public
static
ConcretePolygon
overlappingAabbsButNotNested
(
int
count
)
{
ConcretePolygon
polygon
=
basePolygon
(
count
);
for
(
int
i
=
0
;
i
<
count
;
i
++)
{
double
x
=
6.0
+
(
i
%
12
)
*
6.0
;
...
...
@@ -118,7 +118,7 @@ final class SyntheticNestedRingGeometryFactory {
return
polygon
;
}
static
ConcretePolygon
clusteredInnerRings
(
int
clusterCount
,
int
ringsPerCluster
)
{
public
static
ConcretePolygon
clusteredInnerRings
(
int
clusterCount
,
int
ringsPerCluster
)
{
ConcretePolygon
polygon
=
basePolygon
(
clusterCount
*
ringsPerCluster
);
for
(
int
cluster
=
0
;
cluster
<
clusterCount
;
cluster
++)
{
double
clusterX
=
8.0
+
(
cluster
%
4
)
*
22.0
;
...
...
@@ -132,14 +132,14 @@ final class SyntheticNestedRingGeometryFactory {
return
polygon
;
}
static
ConcretePolygon
clusteredInnerRingsWithNestedPair
(
int
clusterCount
,
int
ringsPerCluster
)
{
public
static
ConcretePolygon
clusteredInnerRingsWithNestedPair
(
int
clusterCount
,
int
ringsPerCluster
)
{
ConcretePolygon
polygon
=
clusteredInnerRings
(
clusterCount
,
ringsPerCluster
);
polygon
.
addInteriorRing
(
squareRing
(
88.0
,
88.0
,
5.0
));
polygon
.
addInteriorRing
(
squareRing
(
88.0
,
88.0
,
1.0
));
return
polygon
;
}
static
ConcretePolygon
aspectSweepInnerRings
(
int
count
,
double
aspectRatio
)
{
public
static
ConcretePolygon
aspectSweepInnerRings
(
int
count
,
double
aspectRatio
)
{
int
columns
=
(
int
)
Math
.
ceil
(
Math
.
sqrt
(
count
));
double
width
=
Math
.
max
(
1.0
,
aspectRatio
);
double
extent
=
columns
*
(
width
+
4.0
)
+
20.0
;
...
...
@@ -152,7 +152,7 @@ final class SyntheticNestedRingGeometryFactory {
return
polygon
;
}
static
ConcretePolygon
spreadSweepInnerRings
(
int
count
,
double
spacing
)
{
public
static
ConcretePolygon
spreadSweepInnerRings
(
int
count
,
double
spacing
)
{
int
columns
=
(
int
)
Math
.
ceil
(
Math
.
sqrt
(
count
));
double
extent
=
columns
*
spacing
+
20.0
;
ConcretePolygon
polygon
=
basePolygonWithExtent
(
extent
);
...
...
CityDoctorParent/CityDoctorValidation/src/test/java/de/hft/stuttgart/citydoctor2/checks/
bht
/SyntheticRingGeometryFactory.java
→
CityDoctorParent/CityDoctorValidation/src/test/java/de/hft/stuttgart/citydoctor2/checks/
aabb/fixtures
/SyntheticRingGeometryFactory.java
View file @
2c9eea60
package
de.hft.stuttgart.citydoctor2.checks.
bht
;
package
de.hft.stuttgart.citydoctor2.checks.
aabb.fixtures
;
import
java.io.File
;
...
...
@@ -42,12 +42,12 @@ import de.hft.stuttgart.citydoctor2.datastructure.Vertex;
*
* @author Numanoglu
*/
final
class
SyntheticRingGeometryFactory
{
public
final
class
SyntheticRingGeometryFactory
{
private
SyntheticRingGeometryFactory
()
{
}
static
Geometry
correctnessRingGeometry
(
double
epsilon
)
{
public
static
Geometry
correctnessRingGeometry
(
double
epsilon
)
{
Geometry
geometry
=
new
Geometry
(
GeometryType
.
SOLID
,
Lod
.
LOD2
,
Orientation
.
OUTWARD
);
addRingPolygon
(
geometry
,
rectangle
());
addRingPolygon
(
geometry
,
bowTie
());
...
...
@@ -58,7 +58,7 @@ final class SyntheticRingGeometryFactory {
return
geometry
;
}
static
Geometry
performanceRingGeometry
(
int
convexVertexCount
,
int
zigZagSegments
,
double
epsilon
)
{
public
static
Geometry
performanceRingGeometry
(
int
convexVertexCount
,
int
zigZagSegments
,
double
epsilon
)
{
Geometry
geometry
=
new
Geometry
(
GeometryType
.
SOLID
,
Lod
.
LOD2
,
Orientation
.
OUTWARD
);
addRingPolygon
(
geometry
,
largeConvexRing
(
convexVertexCount
,
0.0
,
0.0
,
0.0
));
addRingPolygon
(
geometry
,
zigZagCorridor
(
zigZagSegments
));
...
...
@@ -68,7 +68,7 @@ final class SyntheticRingGeometryFactory {
return
geometry
;
}
static
Geometry
aspectSweepGeometry
(
int
ringCount
,
double
aspectRatio
)
{
public
static
Geometry
aspectSweepGeometry
(
int
ringCount
,
double
aspectRatio
)
{
Geometry
geometry
=
new
Geometry
(
GeometryType
.
SOLID
,
Lod
.
LOD2
,
Orientation
.
OUTWARD
);
double
width
=
Math
.
max
(
1.0
,
aspectRatio
);
for
(
int
i
=
0
;
i
<
ringCount
;
i
++)
{
...
...
@@ -80,7 +80,7 @@ final class SyntheticRingGeometryFactory {
return
geometry
;
}
static
Geometry
spreadSweepGeometry
(
int
ringCount
,
double
spacing
)
{
public
static
Geometry
spreadSweepGeometry
(
int
ringCount
,
double
spacing
)
{
Geometry
geometry
=
new
Geometry
(
GeometryType
.
SOLID
,
Lod
.
LOD2
,
Orientation
.
OUTWARD
);
for
(
int
i
=
0
;
i
<
ringCount
;
i
++)
{
double
x
=
(
i
%
20
)
*
spacing
;
...
...
CityDoctorParent/CityDoctorValidation/src/test/java/de/hft/stuttgart/citydoctor2/checks/
bht
/SyntheticSolidGeometryFactory.java
→
CityDoctorParent/CityDoctorValidation/src/test/java/de/hft/stuttgart/citydoctor2/checks/
aabb/fixtures
/SyntheticSolidGeometryFactory.java
View file @
2c9eea60
package
de.hft.stuttgart.citydoctor2.checks.
bht
;
package
de.hft.stuttgart.citydoctor2.checks.
aabb.fixtures
;
import
java.io.File
;
...
...
@@ -30,12 +30,12 @@ import de.hft.stuttgart.citydoctor2.parser.ParserConfiguration;
*
* @author Numanoglu
*/
final
class
SyntheticSolidGeometryFactory
{
public
final
class
SyntheticSolidGeometryFactory
{
private
SyntheticSolidGeometryFactory
()
{
}
static
Geometry
separatedBoxGrid
(
Lod
lod
,
int
xCount
,
int
yCount
)
{
public
static
Geometry
separatedBoxGrid
(
Lod
lod
,
int
xCount
,
int
yCount
)
{
Geometry
geometry
=
newSolid
(
lod
);
double
spacing
=
6.0
;
for
(
int
ix
=
0
;
ix
<
xCount
;
ix
++)
{
...
...
@@ -47,7 +47,7 @@ final class SyntheticSolidGeometryFactory {
return
geometry
;
}
static
Geometry
overlappingBoxGrid
(
Lod
lod
,
int
xCount
,
int
yCount
)
{
public
static
Geometry
overlappingBoxGrid
(
Lod
lod
,
int
xCount
,
int
yCount
)
{
Geometry
geometry
=
separatedBoxGrid
(
lod
,
xCount
,
yCount
);
addBox
(
geometry
,
6.0
,
6.0
,
0.5
,
6.0
,
2.8
,
3.5
);
addBox
(
geometry
,
8.0
,
5.5
,
0.0
,
2.8
,
6.0
,
4.2
);
...
...
@@ -55,7 +55,7 @@ final class SyntheticSolidGeometryFactory {
return
geometry
;
}
static
Geometry
denseBoxClusters
(
Lod
lod
,
int
clusterCount
,
int
boxesPerCluster
)
{
public
static
Geometry
denseBoxClusters
(
Lod
lod
,
int
clusterCount
,
int
boxesPerCluster
)
{
Geometry
geometry
=
newSolid
(
lod
);
double
clusterSpacing
=
30.0
;
for
(
int
cluster
=
0
;
cluster
<
clusterCount
;
cluster
++)
{
...
...
@@ -70,7 +70,7 @@ final class SyntheticSolidGeometryFactory {
return
geometry
;
}
static
Geometry
longThinSlabs
(
Lod
lod
,
int
count
)
{
public
static
Geometry
longThinSlabs
(
Lod
lod
,
int
count
)
{
Geometry
geometry
=
newSolid
(
lod
);
for
(
int
i
=
0
;
i
<
count
;
i
++)
{
double
y
=
i
*
0.75
;
...
...
@@ -80,7 +80,7 @@ final class SyntheticSolidGeometryFactory {
return
geometry
;
}
static
Geometry
flatBoxGrid
(
Lod
lod
,
int
count
)
{
public
static
Geometry
flatBoxGrid
(
Lod
lod
,
int
count
)
{
Geometry
geometry
=
newSolid
(
lod
);
for
(
int
i
=
0
;
i
<
count
;
i
++)
{
double
x
=
(
i
%
10
)
*
5.0
;
...
...
@@ -91,7 +91,7 @@ final class SyntheticSolidGeometryFactory {
return
geometry
;
}
static
Geometry
thinRatioSweepBoxes
(
Lod
lod
,
int
count
,
double
thinRatio
)
{
public
static
Geometry
thinRatioSweepBoxes
(
Lod
lod
,
int
count
,
double
thinRatio
)
{
Geometry
geometry
=
newSolid
(
lod
);
int
columns
=
(
int
)
Math
.
ceil
(
Math
.
sqrt
(
count
));
int
thinCount
=
(
int
)
Math
.
round
(
count
*
thinRatio
);
...
...
@@ -108,7 +108,7 @@ final class SyntheticSolidGeometryFactory {
return
geometry
;
}
static
Geometry
aspectSweepBoxes
(
Lod
lod
,
int
count
,
double
aspectRatio
)
{
public
static
Geometry
aspectSweepBoxes
(
Lod
lod
,
int
count
,
double
aspectRatio
)
{
Geometry
geometry
=
newSolid
(
lod
);
int
columns
=
(
int
)
Math
.
ceil
(
Math
.
sqrt
(
count
));
double
width
=
Math
.
max
(
1.0
,
aspectRatio
);
...
...
@@ -122,7 +122,7 @@ final class SyntheticSolidGeometryFactory {
return
geometry
;
}
static
Geometry
spreadSweepBoxes
(
Lod
lod
,
int
count
,
double
spacing
)
{
public
static
Geometry
spreadSweepBoxes
(
Lod
lod
,
int
count
,
double
spacing
)
{
Geometry
geometry
=
newSolid
(
lod
);
int
columns
=
(
int
)
Math
.
ceil
(
Math
.
sqrt
(
count
));
for
(
int
i
=
0
;
i
<
count
;
i
++)
{
...
...
@@ -134,7 +134,7 @@ final class SyntheticSolidGeometryFactory {
return
geometry
;
}
static
Geometry
relativeVolumeSweepBoxes
(
Lod
lod
,
int
count
,
double
boxSize
,
double
spacingFactor
)
{
public
static
Geometry
relativeVolumeSweepBoxes
(
Lod
lod
,
int
count
,
double
boxSize
,
double
spacingFactor
)
{
Geometry
geometry
=
newSolid
(
lod
);
int
columns
=
(
int
)
Math
.
ceil
(
Math
.
sqrt
(
count
));
double
spacing
=
boxSize
*
spacingFactor
;
...
...
CityDoctorParent/CityDoctorValidation/src/test/java/de/hft/stuttgart/citydoctor2/checks/
bht
/BvhStrategyHeuristicExplorationTest.java
→
CityDoctorParent/CityDoctorValidation/src/test/java/de/hft/stuttgart/citydoctor2/checks/
aabb/heuristics
/BvhStrategyHeuristicExplorationTest.java
View file @
2c9eea60
package
de.hft.stuttgart.citydoctor2.checks.
bht
;
package
de.hft.stuttgart.citydoctor2.checks.
aabb.heuristics
;
import
de.hft.stuttgart.citydoctor2.checks.aabb.fixtures.BvhSyntheticScenarioCatalog
;
import
de.hft.stuttgart.citydoctor2.checks.aabb.support.BvhExplorationCsvWriter
;
import
de.hft.stuttgart.citydoctor2.checks.aabb.support.BvhHeuristicTimingSupport
;
import
de.hft.stuttgart.citydoctor2.checks.aabb.support.BvhInputMetricsCollector
;
import
java.io.IOException
;
import
java.nio.file.Path
;
import
java.util.ArrayList
;
import
java.util.Collections
;
import
java.util.LinkedHashMap
;
...
...
@@ -19,14 +26,18 @@ import de.hft.stuttgart.citydoctor2.datastructure.Geometry;
import
de.hft.stuttgart.citydoctor2.datastructure.LinearRing
;
import
de.hft.stuttgart.citydoctor2.datastructure.Polygon
;
import
de.hft.stuttgart.citydoctor2.datastructure.Vertex
;
import
de.hft.stuttgart.citydoctor2.datastructure.
bht
.AABB
;
import
de.hft.stuttgart.citydoctor2.datastructure.
bht
.BoundingVolumeHierarchyTree
;
import
de.hft.stuttgart.citydoctor2.datastructure.
bht
.SplitStrategy
;
import
de.hft.stuttgart.citydoctor2.datastructure.
aabb
.AABB
;
import
de.hft.stuttgart.citydoctor2.datastructure.
aabb
.BoundingVolumeHierarchyTree
;
import
de.hft.stuttgart.citydoctor2.datastructure.
aabb
.SplitStrategy
;
/**
* Collects compact timing observations and broad-phase metrics for BVH strategy
* selection from synthetic fixtures.
*
* The synthetic scenarios are the calibration side of the heuristic workflow:
* they deliberately vary shape and distribution properties so candidate rules
* can be proposed before they are checked against real CityGML data.
*
* @author Numanoglu
*/
@Tag
(
"performance"
)
...
...
@@ -34,19 +45,44 @@ public class BvhStrategyHeuristicExplorationTest {
private
static
final
double
DELTA
=
0.001
;
private
static
final
double
EPSILON
=
0.001
;
private
static
final
Path
OUTPUT_DIRECTORY
=
Path
.
of
(
"target"
,
"bvh-exploration"
);
@Test
public
void
exploreSsiNestedAndRsiStrategyCandidates
()
{
public
void
exploreSsiNestedAndRsiStrategyCandidates
()
throws
IOException
{
// Keep all three checks in one table so metric trends can be compared side by side.
List
<
BvhHeuristicTimingSupport
.
Observation
>
observations
=
new
ArrayList
<>();
addSsiObservations
(
observations
);
addNestedObservations
(
observations
);
addRsiObservations
(
observations
);
printObservationSummary
(
observations
);
printBucketSummary
(
observations
);
List
<
BvhExplorationCsvWriter
.
BucketSummaryRecord
>
bucketSummary
=
printBucketSummary
(
observations
);
BvhExplorationCsvWriter
.
writeObservations
(
OUTPUT_DIRECTORY
.
resolve
(
"synthetic_observations.csv"
),
toSyntheticRecords
(
observations
));
BvhExplorationCsvWriter
.
writeBucketSummary
(
OUTPUT_DIRECTORY
.
resolve
(
"synthetic_bucket_summary.csv"
),
bucketSummary
);
}
private
static
List
<
BvhExplorationCsvWriter
.
ObservationRecord
>
toSyntheticRecords
(
List
<
BvhHeuristicTimingSupport
.
Observation
>
observations
)
{
// Synthetic observations are labelled as calibration data, not as final evaluation data.
List
<
BvhExplorationCsvWriter
.
ObservationRecord
>
records
=
new
ArrayList
<>(
observations
.
size
());
for
(
BvhHeuristicTimingSupport
.
Observation
observation
:
observations
)
{
records
.
add
(
new
BvhExplorationCsvWriter
.
ObservationRecord
(
"synthetic"
,
"calibration"
,
"synthetic-scenario-catalog"
,
"controlled-synthetic-scenarios"
,
BvhExplorationCsvWriter
.
CANDIDATE_POLICY_VERSION
,
observation
));
}
return
records
;
}
private
static
void
addSsiObservations
(
List
<
BvhHeuristicTimingSupport
.
Observation
>
observations
)
{
// SSI uses polygons as BVH elements and compares against the brute-force solid check.
for
(
BvhSyntheticScenarioCatalog
.
SsiScenario
scenario
:
BvhSyntheticScenarioCatalog
.
ssiScenarios
())
{
List
<
Polygon
>
polygons
=
scenario
.
geometry
.
getPolygons
();
BvhInputMetricsCollector
.
Metrics
metrics
=
...
...
@@ -63,6 +99,7 @@ public class BvhStrategyHeuristicExplorationTest {
}
private
static
void
addNestedObservations
(
List
<
BvhHeuristicTimingSupport
.
Observation
>
observations
)
{
// Nested rings use inner rings as searchable elements; the exact containment check stays unchanged.
for
(
BvhSyntheticScenarioCatalog
.
NestedScenario
scenario
:
BvhSyntheticScenarioCatalog
.
nestedScenarios
())
{
List
<
LinearRing
>
rings
=
scenario
.
polygon
.
getInnerRings
();
BvhInputMetricsCollector
.
Metrics
metrics
=
...
...
@@ -79,6 +116,7 @@ public class BvhStrategyHeuristicExplorationTest {
}
private
static
void
addRsiObservations
(
List
<
BvhHeuristicTimingSupport
.
Observation
>
observations
)
{
// RSI broad-phase behavior is driven by edge boxes rather than whole polygon boxes.
for
(
BvhSyntheticScenarioCatalog
.
RsiScenario
scenario
:
BvhSyntheticScenarioCatalog
.
rsiScenarios
())
{
BvhInputMetricsCollector
.
Metrics
metrics
=
BvhInputMetricsCollector
.
collectBoxesCheap
(
scenario
.
name
,
collectEdgeBoxes
(
scenario
.
geometry
));
...
...
@@ -94,6 +132,7 @@ public class BvhStrategyHeuristicExplorationTest {
}
private
static
int
calculateSsiWithTree
(
Geometry
geometry
,
SplitStrategy
strategy
)
{
// The tree indexes original polygons; SelfIntersectionUtil still performs the exact intersection checks.
BoundingVolumeHierarchyTree
<
Polygon
>
tree
=
BoundingVolumeHierarchyTree
.
newWithStrategy
(
geometry
.
getPolygons
(),
...
...
@@ -182,8 +221,8 @@ public class BvhStrategyHeuristicExplorationTest {
metrics
.
averageRelativeBoxVolume
,
metrics
.
centerSpreadRatio
,
metrics
.
averageAspectRatio
,
observation
.
synthet
icPrediction
,
observation
.
candidatePrediction
,
observation
.
currentPol
ic
y
Prediction
,
observation
.
candidate
Rule
Prediction
,
observation
.
fastestBvh
.
variant
,
observation
.
fastestBvh
.
averageMillis
()));
}
...
...
@@ -192,7 +231,9 @@ public class BvhStrategyHeuristicExplorationTest {
System
.
out
.
println
();
}
private
static
void
printBucketSummary
(
List
<
BvhHeuristicTimingSupport
.
Observation
>
observations
)
{
private
static
List
<
BvhExplorationCsvWriter
.
BucketSummaryRecord
>
printBucketSummary
(
List
<
BvhHeuristicTimingSupport
.
Observation
>
observations
)
{
// Buckets reduce continuous metrics to readable intervals for first-pass rule discovery.
Map
<
String
,
BucketStats
>
buckets
=
new
LinkedHashMap
<>();
for
(
BvhHeuristicTimingSupport
.
Observation
observation
:
observations
)
{
addBucket
(
buckets
,
observation
,
"thin"
,
thinBucket
(
observation
.
metrics
.
thinBoxRate
));
...
...
@@ -234,6 +275,12 @@ public class BvhStrategyHeuristicExplorationTest {
}
}
System
.
out
.
println
();
List
<
BvhExplorationCsvWriter
.
BucketSummaryRecord
>
summary
=
new
ArrayList
<>(
buckets
.
size
());
for
(
BucketStats
bucket
:
buckets
.
values
())
{
summary
.
add
(
bucket
.
toRecord
());
}
return
summary
;
}
private
static
void
addBucket
(
...
...
@@ -312,6 +359,9 @@ public class BvhStrategyHeuristicExplorationTest {
final
Map
<
String
,
Integer
>
winners
=
new
LinkedHashMap
<>();
int
total
;
/**
* Counts which split strategy wins inside one metric bucket.
*/
BucketStats
(
String
checkName
,
String
metricName
,
String
bucketName
)
{
this
.
checkName
=
checkName
;
this
.
metricName
=
metricName
;
...
...
@@ -341,6 +391,46 @@ public class BvhStrategyHeuristicExplorationTest {
}
return
(
double
)
winners
.
get
(
mostCommonWinner
())
/
total
;
}
int
winnerCount
()
{
return
winners
.
getOrDefault
(
mostCommonWinner
(),
0
);
}
int
secondBestCount
()
{
int
best
=
0
;
int
second
=
0
;
for
(
int
count
:
winners
.
values
())
{
if
(
count
>
best
)
{
second
=
best
;
best
=
count
;
}
else
if
(
count
>
second
)
{
second
=
count
;
}
}
return
second
;
}
double
winnerMargin
()
{
if
(
total
==
0
)
{
return
0.0
;
}
return
(
double
)
(
winnerCount
()
-
secondBestCount
())
/
total
;
}
BvhExplorationCsvWriter
.
BucketSummaryRecord
toRecord
()
{
return
new
BvhExplorationCsvWriter
.
BucketSummaryRecord
(
checkName
,
metricName
,
bucketName
,
total
,
winners
,
mostCommonWinner
(),
winnerCount
(),
mostCommonShare
(),
secondBestCount
(),
winnerMargin
(),
total
>=
3
&&
mostCommonShare
()
>=
0.65
);
}
}
}
CityDoctorParent/CityDoctorValidation/src/test/java/de/hft/stuttgart/citydoctor2/checks/
bht
/RealCityGmlBvhHeuristicExplorationTest.java
→
CityDoctorParent/CityDoctorValidation/src/test/java/de/hft/stuttgart/citydoctor2/checks/
aabb/heuristics
/RealCityGmlBvhHeuristicExplorationTest.java
View file @
2c9eea60
package
de.hft.stuttgart.citydoctor2.checks.
bht
;
package
de.hft.stuttgart.citydoctor2.checks.
aabb.heuristics
;
import
de.hft.stuttgart.citydoctor2.checks.aabb.support.BvhExplorationCsvWriter
;
import
de.hft.stuttgart.citydoctor2.checks.aabb.support.BvhHeuristicTimingSupport
;
import
de.hft.stuttgart.citydoctor2.checks.aabb.support.BvhInputMetricsCollector
;
import
static
org
.
junit
.
Assert
.
assertFalse
;
import
static
org
.
junit
.
jupiter
.
api
.
Assumptions
.
assumeTrue
;
import
java.io.IOException
;
import
java.io.OutputStream
;
import
java.io.PrintStream
;
import
java.nio.charset.StandardCharsets
;
import
java.nio.file.Files
;
import
java.nio.file.Path
;
import
java.util.ArrayList
;
import
java.util.Collections
;
import
java.util.Comparator
;
import
java.util.IdentityHashMap
;
import
java.util.LinkedHashMap
;
import
java.util.List
;
import
java.util.Locale
;
import
java.util.Map
;
import
java.util.Set
;
import
java.util.stream.Collectors
;
...
...
@@ -29,9 +36,9 @@ import de.hft.stuttgart.citydoctor2.datastructure.Geometry;
import
de.hft.stuttgart.citydoctor2.datastructure.LinearRing
;
import
de.hft.stuttgart.citydoctor2.datastructure.Polygon
;
import
de.hft.stuttgart.citydoctor2.datastructure.Vertex
;
import
de.hft.stuttgart.citydoctor2.datastructure.
bht
.AABB
;
import
de.hft.stuttgart.citydoctor2.datastructure.
bht
.BoundingVolumeHierarchyTree
;
import
de.hft.stuttgart.citydoctor2.datastructure.
bht
.SplitStrategy
;
import
de.hft.stuttgart.citydoctor2.datastructure.
aabb
.AABB
;
import
de.hft.stuttgart.citydoctor2.datastructure.
aabb
.BoundingVolumeHierarchyTree
;
import
de.hft.stuttgart.citydoctor2.datastructure.
aabb
.SplitStrategy
;
import
de.hft.stuttgart.citydoctor2.parser.CityGmlParser
;
import
de.hft.stuttgart.citydoctor2.parser.ParserConfiguration
;
import
de.hft.stuttgart.citydoctor2.utils.visitors.CityObjectCollector
;
...
...
@@ -42,6 +49,8 @@ import de.hft.stuttgart.citydoctor2.utils.visitors.CityObjectCollector;
*
* The test measures broad-phase costs and compares candidate strategy rules with
* the fastest measured BVH split strategy.
* Dataset labels come from datasets.csv so calibration and evaluation data can
* be separated without changing the test code.
*
* @author Numanoglu
*/
...
...
@@ -50,14 +59,17 @@ public class RealCityGmlBvhHeuristicExplorationTest {
private
static
final
Path
REAL_CITYGML_ROOT
=
Path
.
of
(
"src"
,
"test"
,
"resources"
,
"real-citygml"
);
private
static
final
Path
DATASET_MANIFEST
=
REAL_CITYGML_ROOT
.
resolve
(
"datasets.csv"
);
private
static
final
Path
OUTPUT_DIRECTORY
=
Path
.
of
(
"target"
,
"bvh-exploration"
);
private
static
final
double
EDGE_BOX_PADDING
=
0.001
;
private
static
final
int
FILE_COLUMN_WIDTH
=
28
;
private
static
final
String
TABLE_FORMAT
=
"%-28s | %-6s | %7s | %5s | %7s | %6s | %7s | %23s | %23s | %5s | %9s | %9s | %9s"
;
"%-28s | %-6s | %7s | %5s | %7s | %6s | %7s | %23s | %23s | %5s | %9s | %9s |
%9s | %9s |
%9s"
;
private
static
final
PrintStream
SILENT_OUT
=
new
PrintStream
(
OutputStream
.
nullOutputStream
());
@Test
public
void
printRealCityGmlMetricsAndBvhTiming
()
throws
Exception
{
// Each subdirectory is treated as one labelled dataset group.
assumeTrue
(
Files
.
isDirectory
(
REAL_CITYGML_ROOT
),
"Real CityGML test data directory is not available: "
+
REAL_CITYGML_ROOT
);
List
<
Path
>
datasetDirectories
=
listDatasetDirectories
(
REAL_CITYGML_ROOT
);
...
...
@@ -65,15 +77,21 @@ public class RealCityGmlBvhHeuristicExplorationTest {
"Expected real CityGML dataset directories in "
+
REAL_CITYGML_ROOT
);
assertFalse
(
"No real CityGML dataset directories found in "
+
REAL_CITYGML_ROOT
,
datasetDirectories
.
isEmpty
());
Map
<
String
,
DatasetMetadata
>
datasetMetadata
=
readDatasetManifest
(
DATASET_MANIFEST
);
List
<
RealObservation
>
allObservations
=
new
ArrayList
<>();
for
(
Path
datasetDirectory
:
datasetDirectories
)
{
String
datasetName
=
datasetDirectory
.
getFileName
().
toString
();
DatasetMetadata
metadata
=
datasetMetadata
.
get
(
datasetName
);
if
(
metadata
==
null
)
{
throw
new
IllegalStateException
(
"Dataset is not labelled in "
+
DATASET_MANIFEST
+
": "
+
datasetName
);
}
List
<
Path
>
files
=
listGmlFiles
(
datasetDirectory
);
if
(
files
.
isEmpty
())
{
continue
;
}
printHeader
(
dataset
Directory
.
getFileName
().
toString
()
);
printHeader
(
dataset
Name
+
" / "
+
metadata
.
semanticLabel
+
" / "
+
metadata
.
dataSplit
);
for
(
Path
file
:
files
)
{
ModelElements
elements
=
runQuietly
(()
->
{
CityDoctorModel
model
=
CityGmlParser
.
parseCityGmlFile
(
...
...
@@ -81,41 +99,87 @@ public class RealCityGmlBvhHeuristicExplorationTest {
new
ParserConfiguration
(
8
,
false
));
return
collectElements
(
model
);
});
printAndCollect
(
allObservations
,
data
setDirectory
,
file
,
"SSI"
,
printAndCollect
(
allObservations
,
meta
data
,
file
,
"SSI"
,
measureBroadPhase
(
"SSI"
,
file
.
getFileName
()
+
":polygons"
,
elements
.
polygonBoxes
,
QueryMode
.
INTERSECTING
));
printAndCollect
(
allObservations
,
data
setDirectory
,
file
,
"NESTED"
,
printAndCollect
(
allObservations
,
meta
data
,
file
,
"NESTED"
,
measureBroadPhase
(
"NESTED"
,
file
.
getFileName
()
+
":innerRings"
,
elements
.
innerRingBoxes
,
QueryMode
.
CONTAINED
));
printAndCollect
(
allObservations
,
data
setDirectory
,
file
,
"RSI"
,
printAndCollect
(
allObservations
,
meta
data
,
file
,
"RSI"
,
measureBroadPhase
(
"RSI"
,
file
.
getFileName
()
+
":edges"
,
elements
.
edgeBoxes
,
QueryMode
.
INTERSECTING
));
printSeparator
();
}
}
BvhExplorationCsvWriter
.
writeObservations
(
OUTPUT_DIRECTORY
.
resolve
(
"real_citygml_observations.csv"
),
toCsvRecords
(
allObservations
));
printAggregateSummary
(
allObservations
);
printFooter
();
}
private
static
List
<
BvhExplorationCsvWriter
.
ObservationRecord
>
toCsvRecords
(
List
<
RealObservation
>
observations
)
{
// Preserve dataset labels in the CSV so pandas can group by split and scenario type later.
List
<
BvhExplorationCsvWriter
.
ObservationRecord
>
records
=
new
ArrayList
<>(
observations
.
size
());
for
(
RealObservation
observation
:
observations
)
{
records
.
add
(
new
BvhExplorationCsvWriter
.
ObservationRecord
(
"real-citygml"
,
observation
.
dataSplit
,
observation
.
datasetName
,
observation
.
semanticLabel
,
BvhExplorationCsvWriter
.
CANDIDATE_POLICY_VERSION
,
observation
.
observation
));
}
return
records
;
}
private
static
void
printAndCollect
(
List
<
RealObservation
>
allObservations
,
P
at
h
datasetDirectory
,
D
at
asetMetadata
metadata
,
Path
file
,
String
checkName
,
BvhHeuristicTimingSupport
.
Observation
observation
)
{
printObservation
(
file
.
getFileName
().
toString
(),
observation
);
allObservations
.
add
(
new
RealObservation
(
datasetDirectory
.
getFileName
().
toString
(),
metadata
.
datasetName
,
metadata
.
semanticLabel
,
metadata
.
dataSplit
,
file
.
getFileName
().
toString
(),
checkName
,
observation
));
}
private
static
Map
<
String
,
DatasetMetadata
>
readDatasetManifest
(
Path
manifest
)
throws
IOException
{
// The manifest makes the real data split explicit instead of relying on directory-name conventions.
if
(!
Files
.
isRegularFile
(
manifest
))
{
throw
new
IllegalStateException
(
"Real CityGML dataset manifest is missing: "
+
manifest
);
}
Map
<
String
,
DatasetMetadata
>
metadata
=
new
LinkedHashMap
<>();
List
<
String
>
lines
=
Files
.
readAllLines
(
manifest
,
StandardCharsets
.
UTF_8
);
for
(
int
i
=
1
;
i
<
lines
.
size
();
i
++)
{
String
line
=
lines
.
get
(
i
).
trim
();
if
(
line
.
isEmpty
())
{
continue
;
}
String
[]
columns
=
line
.
split
(
","
,
-
1
);
if
(
columns
.
length
!=
4
)
{
throw
new
IllegalStateException
(
"Invalid dataset manifest row "
+
(
i
+
1
)
+
": "
+
line
);
}
DatasetMetadata
row
=
new
DatasetMetadata
(
columns
[
0
],
columns
[
1
],
columns
[
2
]);
if
(
metadata
.
put
(
row
.
datasetName
,
row
)
!=
null
)
{
throw
new
IllegalStateException
(
"Duplicate dataset in manifest: "
+
row
.
datasetName
);
}
}
return
metadata
;
}
private
static
BvhHeuristicTimingSupport
.
Observation
measureBroadPhase
(
String
checkName
,
String
scenarioName
,
List
<
AABB
>
boxes
,
QueryMode
queryMode
)
{
// Real-data exploration measures candidate search only; exact validation cost is deliberately excluded.
BvhInputMetricsCollector
.
Metrics
metrics
=
BvhInputMetricsCollector
.
collectBoxesCheap
(
scenarioName
,
boxes
);
return
BvhHeuristicTimingSupport
.
measureBvhStrategies
(
...
...
@@ -127,6 +191,7 @@ public class RealCityGmlBvhHeuristicExplorationTest {
}
private
static
int
runBroadPhase
(
List
<
AABB
>
boxes
,
QueryMode
queryMode
,
SplitStrategy
strategy
)
{
// Index numbers are enough here because the broad phase only needs to count returned candidates.
List
<
Integer
>
indices
=
new
ArrayList
<>(
boxes
.
size
());
for
(
int
i
=
0
;
i
<
boxes
.
size
();
i
++)
{
indices
.
add
(
i
);
...
...
@@ -147,6 +212,7 @@ public class RealCityGmlBvhHeuristicExplorationTest {
}
private
static
<
T
>
T
runQuietly
(
ThrowingSupplier
<
T
>
operation
)
throws
Exception
{
// Parser and database startup logs would otherwise drown the timing table.
Level
originalRootLevel
=
LogManager
.
getRootLogger
().
getLevel
();
PrintStream
originalOut
=
System
.
out
;
PrintStream
originalErr
=
System
.
err
;
...
...
@@ -186,6 +252,7 @@ public class RealCityGmlBvhHeuristicExplorationTest {
}
private
static
ModelElements
collectElements
(
CityDoctorModel
model
)
{
// Walk through all city objects because real files often contain more than one building-like feature.
ModelElements
elements
=
new
ModelElements
();
CityObjectCollector
collector
=
new
CityObjectCollector
();
model
.
createFeatureStream
()
...
...
@@ -199,6 +266,7 @@ public class RealCityGmlBvhHeuristicExplorationTest {
}
private
static
void
collectFromCityObject
(
CityObject
cityObject
,
ModelElements
elements
)
{
// Identity tracking prevents the same polygon instance from being counted twice.
for
(
Geometry
geometry
:
cityObject
.
getGeometries
())
{
if
(
geometry
==
null
)
{
continue
;
...
...
@@ -240,7 +308,8 @@ public class RealCityGmlBvhHeuristicExplorationTest {
String
header
=
String
.
format
(
TABLE_FORMAT
,
"file"
,
"check"
,
"n"
,
"thin"
,
"relVol"
,
"spread"
,
"aspect"
,
"candidatePred"
,
"winner"
,
"match"
,
"best ms"
,
"pred ms"
,
"loss ms"
);
"candidatePred"
,
"winner"
,
"match"
,
"best ms"
,
"pred ms"
,
"regret ms"
,
"regret %"
,
"slowdown"
);
printSeparator
(
header
.
length
());
System
.
out
.
println
(
header
);
printSeparator
(
header
.
length
());
...
...
@@ -259,44 +328,60 @@ public class RealCityGmlBvhHeuristicExplorationTest {
String
.
format
(
"%.5f"
,
metrics
.
averageRelativeBoxVolume
),
String
.
format
(
"%.3f"
,
metrics
.
centerSpreadRatio
),
String
.
format
(
"%.1f"
,
metrics
.
averageAspectRatio
),
observation
.
candidatePrediction
,
observation
.
candidate
Rule
Prediction
,
winnerLabel
(
observation
),
observation
.
candidate
Prediction
MatchLabel
(),
observation
.
candidate
Rule
MatchLabel
(),
bestMillisLabel
(
observation
),
predictedMillisLabel
(
observation
),
lossMillisLabel
(
observation
)));
regretMillisLabel
(
observation
),
regretRatioLabel
(
observation
),
slowdownLabel
(
observation
)));
}
private
static
String
winnerLabel
(
BvhHeuristicTimingSupport
.
Observation
observation
)
{
if
(
"OLD"
.
equals
(
observation
.
candidatePrediction
))
{
if
(
"OLD"
.
equals
(
observation
.
candidate
Rule
Prediction
))
{
return
"OLD"
;
}
return
observation
.
fastestBvh
.
variant
;
}
private
static
String
bestMillisLabel
(
BvhHeuristicTimingSupport
.
Observation
observation
)
{
if
(
"OLD"
.
equals
(
observation
.
candidatePrediction
))
{
if
(
"OLD"
.
equals
(
observation
.
candidate
Rule
Prediction
))
{
return
"-"
;
}
return
String
.
format
(
"%.3f"
,
observation
.
fastestBvh
.
averageMillis
());
}
private
static
String
predictedMillisLabel
(
BvhHeuristicTimingSupport
.
Observation
observation
)
{
if
(
observation
.
predictedBvh
==
null
)
{
if
(
observation
.
candidateRuleMeasurement
==
null
)
{
return
"-"
;
}
return
String
.
format
(
"%.3f"
,
observation
.
candidateRuleMeasurement
.
averageMillis
());
}
private
static
String
regretMillisLabel
(
BvhHeuristicTimingSupport
.
Observation
observation
)
{
if
(
observation
.
candidateRuleMeasurement
==
null
)
{
return
"-"
;
}
return
String
.
format
(
"%.3f"
,
observation
.
candidateRegretMillis
());
}
private
static
String
regretRatioLabel
(
BvhHeuristicTimingSupport
.
Observation
observation
)
{
if
(!
Double
.
isFinite
(
observation
.
candidateRegretRatio
()))
{
return
"-"
;
}
return
String
.
format
(
"%.
3f
"
,
observation
.
predictedBvh
.
averageMillis
()
);
return
String
.
format
(
"%.
1f%%
"
,
observation
.
candidateRegretRatio
()
*
100.0
);
}
private
static
String
lossMillis
Label
(
BvhHeuristicTimingSupport
.
Observation
observation
)
{
if
(
observation
.
predictedBvh
==
null
)
{
private
static
String
slowdown
Label
(
BvhHeuristicTimingSupport
.
Observation
observation
)
{
if
(
!
Double
.
isFinite
(
observation
.
candidateSlowdownFactor
())
)
{
return
"-"
;
}
double
lossMillis
=
observation
.
predictedBvh
.
averageMillis
()
-
observation
.
fastestBvh
.
averageMillis
();
return
String
.
format
(
"%.3f"
,
lossMillis
);
return
String
.
format
(
"%.2fx"
,
observation
.
candidateSlowdownFactor
());
}
private
static
void
printAggregateSummary
(
List
<
RealObservation
>
observations
)
{
// The summary is a quick plausibility check before exporting richer tables with pandas.
System
.
out
.
println
();
System
.
out
.
println
(
"[REAL-CITYGML-BVH-HEURISTIC-SUMMARY]"
);
System
.
out
.
println
(
String
.
format
(
...
...
@@ -333,10 +418,10 @@ public class RealCityGmlBvhHeuristicExplorationTest {
private
static
void
printCheckSummary
(
String
checkName
,
List
<
RealObservation
>
observations
)
{
long
tested
=
observations
.
stream
()
.
filter
(
observation
->
!
"OLD"
.
equals
(
observation
.
observation
.
candidatePrediction
))
.
filter
(
observation
->
!
"OLD"
.
equals
(
observation
.
observation
.
candidate
Rule
Prediction
))
.
count
();
long
matches
=
observations
.
stream
()
.
filter
(
observation
->
"yes"
.
equals
(
observation
.
observation
.
candidate
Prediction
MatchLabel
()))
.
filter
(
observation
->
"yes"
.
equals
(
observation
.
observation
.
candidate
Rule
MatchLabel
()))
.
count
();
String
accuracy
=
tested
==
0L
?
"n/a"
:
String
.
format
(
"%.1f%%"
,
100.0
*
matches
/
tested
);
System
.
out
.
println
(
String
.
format
(
...
...
@@ -402,7 +487,7 @@ public class RealCityGmlBvhHeuristicExplorationTest {
private
static
void
printSeparator
()
{
printSeparator
(
String
.
format
(
TABLE_FORMAT
,
""
,
""
,
""
,
""
,
""
,
""
,
""
,
""
,
""
,
""
,
""
,
""
,
""
).
length
());
""
,
""
,
""
,
""
,
""
,
""
,
""
,
""
,
""
,
""
,
""
,
""
,
""
,
""
,
""
).
length
());
}
private
static
void
printSeparator
(
int
width
)
{
...
...
@@ -432,23 +517,41 @@ public class RealCityGmlBvhHeuristicExplorationTest {
}
private
static
final
class
RealObservation
{
final
String
datasetName
;
final
String
datasetName
;
final
String
semanticLabel
;
final
String
dataSplit
;
final
String
fileName
;
final
String
checkName
;
final
BvhHeuristicTimingSupport
.
Observation
observation
;
RealObservation
(
String
datasetName
,
String
semanticLabel
,
String
dataSplit
,
String
fileName
,
String
checkName
,
BvhHeuristicTimingSupport
.
Observation
observation
)
{
this
.
datasetName
=
datasetName
;
this
.
semanticLabel
=
semanticLabel
;
this
.
dataSplit
=
dataSplit
;
this
.
fileName
=
fileName
;
this
.
checkName
=
checkName
;
this
.
observation
=
observation
;
}
}
private
static
final
class
DatasetMetadata
{
final
String
datasetName
;
final
String
semanticLabel
;
final
String
dataSplit
;
DatasetMetadata
(
String
datasetName
,
String
semanticLabel
,
String
dataSplit
)
{
this
.
datasetName
=
datasetName
;
this
.
semanticLabel
=
semanticLabel
;
this
.
dataSplit
=
dataSplit
;
}
}
private
interface
MetricValue
{
double
value
(
RealObservation
observation
);
}
...
...
CityDoctorParent/CityDoctorValidation/src/test/java/de/hft/stuttgart/citydoctor2/checks/
bht
/NestedRingCheckBvhPerformanceTest.java
→
CityDoctorParent/CityDoctorValidation/src/test/java/de/hft/stuttgart/citydoctor2/checks/
aabb/performance
/NestedRingCheckBvhPerformanceTest.java
View file @
2c9eea60
package
de.hft.stuttgart.citydoctor2.checks.
bht
;
package
de.hft.stuttgart.citydoctor2.checks.
aabb.performance
;
import
de.hft.stuttgart.citydoctor2.checks.aabb.fixtures.SyntheticNestedRingGeometryFactory
;
import
de.hft.stuttgart.citydoctor2.checks.aabb.support.BvhInputMetricsCollector
;
import
de.hft.stuttgart.citydoctor2.checks.aabb.support.BvhPerformanceTestSupport
;
import
static
org
.
junit
.
Assert
.
assertEquals
;
import
java.util.ArrayList
;
...
...
@@ -12,8 +16,17 @@ import de.hft.stuttgart.citydoctor2.check.CheckResult;
import
de.hft.stuttgart.citydoctor2.check.ResultStatus
;
import
de.hft.stuttgart.citydoctor2.checks.geometry.NestedRingsCheck
;
import
de.hft.stuttgart.citydoctor2.datastructure.ConcretePolygon
;
import
de.hft.stuttgart.citydoctor2.datastructure.
bht
.SplitStrategy
;
import
de.hft.stuttgart.citydoctor2.datastructure.
aabb
.SplitStrategy
;
/**
* Manual performance probe for nested inner-ring detection.
*
* The old check, the simple AABB filter, and all BVH variants are run on the
* same synthetic polygon so correctness and candidate-search cost can be read
* from one table.
*
* @author Numanoglu
*/
@Tag
(
"performance"
)
public
class
NestedRingCheckBvhPerformanceTest
{
...
...
@@ -28,6 +41,7 @@ public class NestedRingCheckBvhPerformanceTest {
}
private
static
void
measureScenario
(
String
scenario
,
ConcretePolygon
polygon
)
{
// Inner rings are the indexed elements; the exact nested-ring decision stays in NestedRingsCheck.
int
ringCount
=
polygon
.
getInnerRings
().
size
();
BvhInputMetricsCollector
.
print
(
BvhInputMetricsCollector
.
forRings
(
scenario
,
polygon
.
getInnerRings
()));
List
<
BvhPerformanceTestSupport
.
Measurement
>
measurements
=
new
ArrayList
<>();
...
...
@@ -69,6 +83,7 @@ public class NestedRingCheckBvhPerformanceTest {
}
private
static
NestedRingsCheck
.
Variant
variantFor
(
SplitStrategy
strategy
)
{
// The production check exposes concrete BVH strategies through its Variant enum.
switch
(
strategy
)
{
case
BINARY_OBJECT_MEDIAN:
return
NestedRingsCheck
.
Variant
.
BVH_BINARY_OBJECT_MEDIAN
;
...
...
Prev
1
2
3
Next
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