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
35a777b9
Commit
35a777b9
authored
May 29, 2026
by
Numanoglu
Browse files
Add BVH split strategy heuristics
parent
fb72860f
Pipeline
#12403
passed with stage
in 2 minutes and 10 seconds
Changes
15
Pipelines
1
Hide whitespace changes
Inline
Side-by-side
CityDoctorParent/.gitignore
View file @
35a777b9
...
@@ -309,3 +309,6 @@ gradle-app.setting
...
@@ -309,3 +309,6 @@ gradle-app.setting
/.idea/codeStyles/codeStyleConfig.xml
/.idea/codeStyles/codeStyleConfig.xml
/GUISettings.properties
/GUISettings.properties
/.idea/inspectionProfiles/Project_Default.xml
/.idea/inspectionProfiles/Project_Default.xml
# Local empirical data for BVH exploration
/CityDoctorValidation/src/test/resources/real-citygml/
CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/bht/AABB.java
View file @
35a777b9
...
@@ -47,6 +47,30 @@ public class AABB {
...
@@ -47,6 +47,30 @@ public class AABB {
return
new
AABB
(
minX
,
minY
,
minZ
,
maxX
,
maxY
,
maxZ
);
return
new
AABB
(
minX
,
minY
,
minZ
,
maxX
,
maxY
,
maxZ
);
}
}
public
static
AABB
enclosing
(
List
<
AABB
>
boxes
)
{
Objects
.
requireNonNull
(
boxes
,
"boxes"
);
if
(
boxes
.
isEmpty
())
{
throw
new
IllegalArgumentException
(
"boxes must not be empty"
);
}
double
minX
=
Double
.
POSITIVE_INFINITY
;
double
minY
=
Double
.
POSITIVE_INFINITY
;
double
minZ
=
Double
.
POSITIVE_INFINITY
;
double
maxX
=
Double
.
NEGATIVE_INFINITY
;
double
maxY
=
Double
.
NEGATIVE_INFINITY
;
double
maxZ
=
Double
.
NEGATIVE_INFINITY
;
for
(
AABB
box
:
boxes
)
{
minX
=
Math
.
min
(
minX
,
box
.
getMinX
());
minY
=
Math
.
min
(
minY
,
box
.
getMinY
());
minZ
=
Math
.
min
(
minZ
,
box
.
getMinZ
());
maxX
=
Math
.
max
(
maxX
,
box
.
getMaxX
());
maxY
=
Math
.
max
(
maxY
,
box
.
getMaxY
());
maxZ
=
Math
.
max
(
maxZ
,
box
.
getMaxZ
());
}
return
new
AABB
(
minX
,
minY
,
minZ
,
maxX
,
maxY
,
maxZ
);
}
/**
/**
* Computes an AABB for a single vertex degenerate AABB with min=max=vertex
* Computes an AABB for a single vertex degenerate AABB with min=max=vertex
* coordinates
* coordinates
...
@@ -190,6 +214,26 @@ public class AABB {
...
@@ -190,6 +214,26 @@ public class AABB {
return
maxZ
;
return
maxZ
;
}
}
public
double
getExtentX
()
{
return
maxX
-
minX
;
}
public
double
getExtentY
()
{
return
maxY
-
minY
;
}
public
double
getExtentZ
()
{
return
maxZ
-
minZ
;
}
public
double
getLongestExtent
()
{
return
Math
.
max
(
getExtentX
(),
Math
.
max
(
getExtentY
(),
getExtentZ
()));
}
public
double
getShortestExtent
()
{
return
Math
.
min
(
getExtentX
(),
Math
.
min
(
getExtentY
(),
getExtentZ
()));
}
public
double
getCenterX
()
{
public
double
getCenterX
()
{
return
(
minX
+
maxX
)
/
2.0
;
return
(
minX
+
maxX
)
/
2.0
;
}
}
...
@@ -234,9 +278,9 @@ public class AABB {
...
@@ -234,9 +278,9 @@ public class AABB {
* Returns the index of the longest axis in the AABB
* Returns the index of the longest axis in the AABB
*/
*/
public
int
findLongestAxis
()
{
public
int
findLongestAxis
()
{
double
x
=
get
MaxX
()
-
getMin
X
();
double
x
=
get
Extent
X
();
double
y
=
get
MaxY
()
-
getMin
Y
();
double
y
=
get
Extent
Y
();
double
z
=
get
MaxZ
()
-
getMin
Z
();
double
z
=
get
Extent
Z
();
if
(
x
>
y
&&
x
>
z
)
if
(
x
>
y
&&
x
>
z
)
return
0
;
return
0
;
...
@@ -247,9 +291,9 @@ public class AABB {
...
@@ -247,9 +291,9 @@ public class AABB {
/** Returns true if the AABB is (nearly) flat in at least two axes. */
/** Returns true if the AABB is (nearly) flat in at least two axes. */
public
boolean
isDegenerate
(
double
tol
)
{
public
boolean
isDegenerate
(
double
tol
)
{
double
dx
=
get
MaxX
()
-
getMin
X
();
double
dx
=
get
Extent
X
();
double
dy
=
get
MaxY
()
-
getMin
Y
();
double
dy
=
get
Extent
Y
();
double
dz
=
get
MaxZ
()
-
getMin
Z
();
double
dz
=
get
Extent
Z
();
int
flatAxes
=
0
;
int
flatAxes
=
0
;
if
(
dx
<=
tol
)
if
(
dx
<=
tol
)
flatAxes
++;
flatAxes
++;
...
...
CityDoctorParent/CityDoctorValidation/src/main/java/de/hft/stuttgart/citydoctor2/checks/geometry/NestedRingsCheck.java
View file @
35a777b9
...
@@ -35,7 +35,6 @@ import de.hft.stuttgart.citydoctor2.check.Requirement;
...
@@ -35,7 +35,6 @@ import de.hft.stuttgart.citydoctor2.check.Requirement;
import
de.hft.stuttgart.citydoctor2.check.ResultStatus
;
import
de.hft.stuttgart.citydoctor2.check.ResultStatus
;
import
de.hft.stuttgart.citydoctor2.check.error.NestedRingError
;
import
de.hft.stuttgart.citydoctor2.check.error.NestedRingError
;
import
de.hft.stuttgart.citydoctor2.checks.util.BvhUsagePolicy
;
import
de.hft.stuttgart.citydoctor2.checks.util.BvhUsagePolicy
;
import
de.hft.stuttgart.citydoctor2.checks.util.BvhUsagePolicy.BvhCheckType
;
import
de.hft.stuttgart.citydoctor2.checks.util.CollectionUtils
;
import
de.hft.stuttgart.citydoctor2.checks.util.CollectionUtils
;
import
de.hft.stuttgart.citydoctor2.datastructure.LinearRing
;
import
de.hft.stuttgart.citydoctor2.datastructure.LinearRing
;
import
de.hft.stuttgart.citydoctor2.datastructure.Polygon
;
import
de.hft.stuttgart.citydoctor2.datastructure.Polygon
;
...
@@ -136,11 +135,32 @@ public class NestedRingsCheck extends Check {
...
@@ -136,11 +135,32 @@ public class NestedRingsCheck extends Check {
checkWithBoundingBoxFilter
(
p
);
checkWithBoundingBoxFilter
(
p
);
}
else
if
(
variant
.
isBvh
())
{
}
else
if
(
variant
.
isBvh
())
{
checkWithBvhFilter
(
p
,
variant
.
getSplitStrategy
());
checkWithBvhFilter
(
p
,
variant
.
getSplitStrategy
());
}
else
if
(
BvhUsagePolicy
.
shouldUseTree
(
BvhCheckType
.
NESTED_RINGS
,
p
.
getInnerRings
().
size
()))
{
}
else
{
checkAuto
(
p
);
}
}
private
void
checkAuto
(
Polygon
p
)
{
List
<
LinearRing
>
innerRings
=
p
.
getInnerRings
();
if
(
innerRings
.
size
()
<
2
)
{
checkOriginal
(
p
);
return
;
}
Map
<
LinearRing
,
AABB
>
ringBoxes
=
new
HashMap
<>(
innerRings
.
size
());
List
<
AABB
>
boxes
=
new
ArrayList
<>(
innerRings
.
size
());
for
(
LinearRing
ring
:
innerRings
)
{
AABB
box
=
AABB
.
of
(
ring
);
ringBoxes
.
put
(
ring
,
box
);
boxes
.
add
(
box
);
}
BvhUsagePolicy
.
BvhInputSummary
summary
=
BvhUsagePolicy
.
BvhInputSummary
.
ofAabbs
(
boxes
);
if
(
BvhUsagePolicy
.
shouldUseTree
(
BvhUsagePolicy
.
BvhCheckType
.
NESTED_RINGS
,
summary
))
{
SplitStrategy
splitStrategy
=
BvhUsagePolicy
.
chooseSplitStrategy
(
SplitStrategy
splitStrategy
=
BvhUsagePolicy
.
chooseSplitStrategy
(
BvhCheckType
.
NESTED_RINGS
,
p
.
getInnerRings
().
size
()
);
BvhUsagePolicy
.
BvhCheckType
.
NESTED_RINGS
,
summary
);
checkWithBvhFilter
(
p
,
splitStrategy
);
checkWithBvhFilter
(
p
,
splitStrategy
,
ringBoxes
);
}
else
if
(
p
.
getI
nnerRings
()
.
size
()
>
3
)
{
}
else
if
(
i
nnerRings
.
size
()
>
3
)
{
checkWithBoundingBoxFilter
(
p
);
checkWithBoundingBoxFilter
(
p
);
}
else
{
}
else
{
checkOriginal
(
p
);
checkOriginal
(
p
);
...
@@ -183,6 +203,11 @@ public class NestedRingsCheck extends Check {
...
@@ -183,6 +203,11 @@ public class NestedRingsCheck extends Check {
ringBoxes
.
put
(
ring
,
AABB
.
of
(
ring
));
ringBoxes
.
put
(
ring
,
AABB
.
of
(
ring
));
}
}
checkWithBvhFilter
(
p
,
splitStrategy
,
ringBoxes
);
}
private
void
checkWithBvhFilter
(
Polygon
p
,
SplitStrategy
splitStrategy
,
Map
<
LinearRing
,
AABB
>
ringBoxes
)
{
List
<
LinearRing
>
innerRings
=
p
.
getInnerRings
();
BoundingVolumeHierarchyTree
<
LinearRing
>
ringTree
=
BoundingVolumeHierarchyTree
<
LinearRing
>
ringTree
=
BoundingVolumeHierarchyTree
.
newWithStrategy
(
innerRings
,
ringBoxes:
:
get
,
splitStrategy
);
BoundingVolumeHierarchyTree
.
newWithStrategy
(
innerRings
,
ringBoxes:
:
get
,
splitStrategy
);
...
...
CityDoctorParent/CityDoctorValidation/src/main/java/de/hft/stuttgart/citydoctor2/checks/geometry/RingSelfIntCheck.java
View file @
35a777b9
...
@@ -37,7 +37,6 @@ import de.hft.stuttgart.citydoctor2.check.error.PointTouchesEdgeError;
...
@@ -37,7 +37,6 @@ import de.hft.stuttgart.citydoctor2.check.error.PointTouchesEdgeError;
import
de.hft.stuttgart.citydoctor2.check.error.RingEdgeIntersectionError
;
import
de.hft.stuttgart.citydoctor2.check.error.RingEdgeIntersectionError
;
import
de.hft.stuttgart.citydoctor2.checks.Checks
;
import
de.hft.stuttgart.citydoctor2.checks.Checks
;
import
de.hft.stuttgart.citydoctor2.checks.util.BvhUsagePolicy
;
import
de.hft.stuttgart.citydoctor2.checks.util.BvhUsagePolicy
;
import
de.hft.stuttgart.citydoctor2.checks.util.BvhUsagePolicy.BvhCheckType
;
import
de.hft.stuttgart.citydoctor2.checks.util.CollectionUtils
;
import
de.hft.stuttgart.citydoctor2.checks.util.CollectionUtils
;
import
de.hft.stuttgart.citydoctor2.datastructure.Edge
;
import
de.hft.stuttgart.citydoctor2.datastructure.Edge
;
import
de.hft.stuttgart.citydoctor2.datastructure.Geometry
;
import
de.hft.stuttgart.citydoctor2.datastructure.Geometry
;
...
@@ -158,9 +157,9 @@ public class RingSelfIntCheck extends Check {
...
@@ -158,9 +157,9 @@ public class RingSelfIntCheck extends Check {
}
}
if
(
variant
==
Variant
.
AUTO
)
{
if
(
variant
==
Variant
.
AUTO
)
{
int
edgeCount
=
Math
.
max
(
0
,
lr
.
getVertices
().
size
()
-
1
);
int
edgeCount
=
Math
.
max
(
0
,
lr
.
getVertices
().
size
()
-
1
);
if
(
BvhUsagePolicy
.
shouldUseTree
(
BvhCheckType
.
RING_SELF_INTERSECTION
,
edgeCount
))
{
if
(
BvhUsagePolicy
.
shouldUseTree
(
BvhUsagePolicy
.
BvhCheckType
.
RING_SELF_INTERSECTION
,
edgeCount
))
{
SplitStrategy
splitStrategy
=
BvhUsagePolicy
.
chooseSplitStrategy
(
SplitStrategy
splitStrategy
=
BvhUsagePolicy
.
chooseSplitStrategy
(
BvhCheckType
.
RING_SELF_INTERSECTION
,
edgeCount
);
BvhUsagePolicy
.
BvhCheckType
.
RING_SELF_INTERSECTION
,
edgeCount
);
checkRingBvh
(
lr
,
splitStrategy
);
checkRingBvh
(
lr
,
splitStrategy
);
}
else
{
}
else
{
checkRingOld
(
lr
);
checkRingOld
(
lr
);
...
...
CityDoctorParent/CityDoctorValidation/src/main/java/de/hft/stuttgart/citydoctor2/checks/geometry/SolidSelfIntCheck.java
View file @
35a777b9
...
@@ -33,11 +33,11 @@ import de.hft.stuttgart.citydoctor2.check.RequirementType;
...
@@ -33,11 +33,11 @@ import de.hft.stuttgart.citydoctor2.check.RequirementType;
import
de.hft.stuttgart.citydoctor2.check.ResultStatus
;
import
de.hft.stuttgart.citydoctor2.check.ResultStatus
;
import
de.hft.stuttgart.citydoctor2.check.error.SolidSelfIntError
;
import
de.hft.stuttgart.citydoctor2.check.error.SolidSelfIntError
;
import
de.hft.stuttgart.citydoctor2.checks.util.BvhUsagePolicy
;
import
de.hft.stuttgart.citydoctor2.checks.util.BvhUsagePolicy
;
import
de.hft.stuttgart.citydoctor2.checks.util.BvhUsagePolicy.BvhCheckType
;
import
de.hft.stuttgart.citydoctor2.checks.util.CollectionUtils
;
import
de.hft.stuttgart.citydoctor2.checks.util.CollectionUtils
;
import
de.hft.stuttgart.citydoctor2.checks.util.SelfIntersectionUtil
;
import
de.hft.stuttgart.citydoctor2.checks.util.SelfIntersectionUtil
;
import
de.hft.stuttgart.citydoctor2.datastructure.Geometry
;
import
de.hft.stuttgart.citydoctor2.datastructure.Geometry
;
import
de.hft.stuttgart.citydoctor2.datastructure.GeometryType
;
import
de.hft.stuttgart.citydoctor2.datastructure.GeometryType
;
import
de.hft.stuttgart.citydoctor2.datastructure.bht.AABB
;
import
de.hft.stuttgart.citydoctor2.datastructure.bht.BoundingVolumeHierarchyTree
;
import
de.hft.stuttgart.citydoctor2.datastructure.bht.BoundingVolumeHierarchyTree
;
import
de.hft.stuttgart.citydoctor2.datastructure.bht.SplitStrategy
;
import
de.hft.stuttgart.citydoctor2.datastructure.bht.SplitStrategy
;
import
de.hft.stuttgart.citydoctor2.parser.ParserConfiguration
;
import
de.hft.stuttgart.citydoctor2.parser.ParserConfiguration
;
...
@@ -155,12 +155,12 @@ public class SolidSelfIntCheck extends Check {
...
@@ -155,12 +155,12 @@ public class SolidSelfIntCheck extends Check {
return
calculateIntersectionsWithTree
(
g
,
variant
.
getSplitStrategy
());
return
calculateIntersectionsWithTree
(
g
,
variant
.
getSplitStrategy
());
}
}
int
polygonCount
=
g
.
getPolygons
()
.
size
(
);
BvhUsagePolicy
.
BvhInputSummary
summary
=
BvhUsagePolicy
.
BvhInputSummary
.
ofElements
(
g
.
getPolygons
()
,
AABB:
:
of
);
if
(!
BvhUsagePolicy
.
shouldUseTree
(
BvhCheckType
.
SOLID_SELF_INTERSECTION
,
polygonCount
))
{
if
(!
BvhUsagePolicy
.
shouldUseTree
(
BvhUsagePolicy
.
BvhCheckType
.
SOLID_SELF_INTERSECTION
,
summary
))
{
return
SelfIntersectionUtil
.
calculateSolidSelfIntersection
(
g
,
delta
);
return
SelfIntersectionUtil
.
calculateSolidSelfIntersection
(
g
,
delta
);
}
}
SplitStrategy
splitStrategy
=
BvhUsagePolicy
.
chooseSplitStrategy
(
SplitStrategy
splitStrategy
=
BvhUsagePolicy
.
chooseSplitStrategy
(
BvhCheckType
.
SOLID_SELF_INTERSECTION
,
polygonCount
);
BvhUsagePolicy
.
BvhCheckType
.
SOLID_SELF_INTERSECTION
,
summary
);
return
calculateIntersectionsWithTree
(
g
,
splitStrategy
);
return
calculateIntersectionsWithTree
(
g
,
splitStrategy
);
}
}
...
...
CityDoctorParent/CityDoctorValidation/src/main/java/de/hft/stuttgart/citydoctor2/checks/util/BvhUsagePolicy.java
View file @
35a777b9
package
de.hft.stuttgart.citydoctor2.checks.util
;
package
de.hft.stuttgart.citydoctor2.checks.util
;
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.bht.SplitStrategy
;
/**
/**
...
@@ -10,6 +15,8 @@ public final class BvhUsagePolicy {
...
@@ -10,6 +15,8 @@ public final class BvhUsagePolicy {
private
static
final
int
SOLID_SELF_INTERSECTION_TREE_THRESHOLD
=
16
;
private
static
final
int
SOLID_SELF_INTERSECTION_TREE_THRESHOLD
=
16
;
private
static
final
int
NESTED_RINGS_TREE_THRESHOLD
=
8
;
private
static
final
int
NESTED_RINGS_TREE_THRESHOLD
=
8
;
private
static
final
int
RING_SELF_INTERSECTION_TREE_THRESHOLD
=
32
;
private
static
final
int
RING_SELF_INTERSECTION_TREE_THRESHOLD
=
32
;
private
static
final
double
THIN_BOX_ASPECT_RATIO
=
20.0
;
private
static
final
double
DEGENERATE_TOLERANCE
=
1
e
-
12
;
private
BvhUsagePolicy
()
{
private
BvhUsagePolicy
()
{
}
}
...
@@ -20,18 +27,253 @@ public final class BvhUsagePolicy {
...
@@ -20,18 +27,253 @@ public final class BvhUsagePolicy {
RING_SELF_INTERSECTION
RING_SELF_INTERSECTION
}
}
public
static
final
class
BvhInputSummary
{
private
final
int
elementCount
;
private
final
double
extentX
;
private
final
double
extentY
;
private
final
double
extentZ
;
private
final
double
flatnessRatio
;
private
final
double
averageRelativeBoxExtent
;
private
final
double
thinBoxRatio
;
private
final
double
averageRelativeBoxVolume
;
private
final
double
centerSpreadRatio
;
private
BvhInputSummary
(
int
elementCount
,
double
extentX
,
double
extentY
,
double
extentZ
,
double
flatnessRatio
,
double
averageRelativeBoxExtent
,
double
thinBoxRatio
,
double
averageRelativeBoxVolume
,
double
centerSpreadRatio
)
{
this
.
elementCount
=
elementCount
;
this
.
extentX
=
extentX
;
this
.
extentY
=
extentY
;
this
.
extentZ
=
extentZ
;
this
.
flatnessRatio
=
flatnessRatio
;
this
.
averageRelativeBoxExtent
=
averageRelativeBoxExtent
;
this
.
thinBoxRatio
=
thinBoxRatio
;
this
.
averageRelativeBoxVolume
=
averageRelativeBoxVolume
;
this
.
centerSpreadRatio
=
centerSpreadRatio
;
}
public
static
BvhInputSummary
ofAabbs
(
List
<
AABB
>
boxes
)
{
Objects
.
requireNonNull
(
boxes
,
"boxes"
);
if
(
boxes
.
isEmpty
())
{
return
empty
();
}
AABB
totalBox
=
AABB
.
enclosing
(
boxes
);
double
extentX
=
totalBox
.
getExtentX
();
double
extentY
=
totalBox
.
getExtentY
();
double
extentZ
=
totalBox
.
getExtentZ
();
double
relativeExtentSum
=
0.0
;
double
relativeVolumeSum
=
0.0
;
double
centerXSum
=
0.0
;
double
centerYSum
=
0.0
;
double
centerZSum
=
0.0
;
int
thinBoxCount
=
0
;
for
(
AABB
box
:
boxes
)
{
relativeExtentSum
+=
relativeExtent
(
box
.
getExtentX
(),
extentX
);
relativeExtentSum
+=
relativeExtent
(
box
.
getExtentY
(),
extentY
);
relativeExtentSum
+=
relativeExtent
(
box
.
getExtentZ
(),
extentZ
);
relativeVolumeSum
+=
relativeVolume
(
box
,
totalBox
);
if
(
aspectRatio
(
box
)
>=
THIN_BOX_ASPECT_RATIO
)
{
thinBoxCount
++;
}
centerXSum
+=
box
.
getCenterX
();
centerYSum
+=
box
.
getCenterY
();
centerZSum
+=
box
.
getCenterZ
();
}
double
maxExtent
=
totalBox
.
getLongestExtent
();
double
minExtent
=
totalBox
.
getShortestExtent
();
double
flatnessRatio
=
maxExtent
==
0.0
?
0.0
:
minExtent
/
maxExtent
;
double
averageRelativeBoxExtent
=
relativeExtentSum
/
(
boxes
.
size
()
*
3.0
);
double
thinBoxRatio
=
(
double
)
thinBoxCount
/
boxes
.
size
();
double
averageRelativeBoxVolume
=
relativeVolumeSum
/
boxes
.
size
();
double
centerSpreadRatio
=
centerSpreadRatio
(
boxes
,
centerXSum
/
boxes
.
size
(),
centerYSum
/
boxes
.
size
(),
centerZSum
/
boxes
.
size
(),
totalBox
);
return
new
BvhInputSummary
(
boxes
.
size
(),
extentX
,
extentY
,
extentZ
,
flatnessRatio
,
averageRelativeBoxExtent
,
thinBoxRatio
,
averageRelativeBoxVolume
,
centerSpreadRatio
);
}
public
static
<
E
>
BvhInputSummary
ofElements
(
List
<
E
>
elements
,
Function
<
E
,
AABB
>
aabbFunction
)
{
Objects
.
requireNonNull
(
elements
,
"elements"
);
Objects
.
requireNonNull
(
aabbFunction
,
"aabbFunction"
);
return
ofAabbs
(
elements
.
stream
().
map
(
aabbFunction
).
toList
());
}
public
static
BvhInputSummary
countOnly
(
int
elementCount
)
{
return
new
BvhInputSummary
(
elementCount
,
0.0
,
0.0
,
0.0
,
1.0
,
0.0
,
0.0
,
0.0
,
0.0
);
}
private
static
BvhInputSummary
empty
()
{
return
countOnly
(
0
);
}
private
static
double
relativeExtent
(
double
localExtent
,
double
totalExtent
)
{
return
totalExtent
==
0.0
?
0.0
:
localExtent
/
totalExtent
;
}
private
static
double
relativeVolume
(
AABB
box
,
AABB
totalBox
)
{
double
localMeasure
=
1.0
;
double
totalMeasure
=
1.0
;
boolean
hasActiveExtent
=
false
;
if
(
box
.
getExtentX
()
>
DEGENERATE_TOLERANCE
)
{
localMeasure
*=
box
.
getExtentX
();
totalMeasure
*=
totalBox
.
getExtentX
();
hasActiveExtent
=
true
;
}
if
(
box
.
getExtentY
()
>
DEGENERATE_TOLERANCE
)
{
localMeasure
*=
box
.
getExtentY
();
totalMeasure
*=
totalBox
.
getExtentY
();
hasActiveExtent
=
true
;
}
if
(
box
.
getExtentZ
()
>
DEGENERATE_TOLERANCE
)
{
localMeasure
*=
box
.
getExtentZ
();
totalMeasure
*=
totalBox
.
getExtentZ
();
hasActiveExtent
=
true
;
}
if
(!
hasActiveExtent
||
totalMeasure
<=
DEGENERATE_TOLERANCE
)
{
return
0.0
;
}
return
localMeasure
/
totalMeasure
;
}
private
static
double
aspectRatio
(
AABB
box
)
{
double
longest
=
box
.
getLongestExtent
();
double
shortest
=
shortestPositiveExtent
(
box
);
return
shortest
==
0.0
?
Double
.
POSITIVE_INFINITY
:
longest
/
shortest
;
}
private
static
double
shortestPositiveExtent
(
AABB
box
)
{
double
shortest
=
Double
.
POSITIVE_INFINITY
;
if
(
box
.
getExtentX
()
>
0.0
)
{
shortest
=
Math
.
min
(
shortest
,
box
.
getExtentX
());
}
if
(
box
.
getExtentY
()
>
0.0
)
{
shortest
=
Math
.
min
(
shortest
,
box
.
getExtentY
());
}
if
(
box
.
getExtentZ
()
>
0.0
)
{
shortest
=
Math
.
min
(
shortest
,
box
.
getExtentZ
());
}
return
shortest
==
Double
.
POSITIVE_INFINITY
?
0.0
:
shortest
;
}
private
static
double
centerSpreadRatio
(
List
<
AABB
>
boxes
,
double
meanCenterX
,
double
meanCenterY
,
double
meanCenterZ
,
AABB
totalBox
)
{
double
varianceX
=
0.0
;
double
varianceY
=
0.0
;
double
varianceZ
=
0.0
;
for
(
AABB
box
:
boxes
)
{
varianceX
+=
square
(
box
.
getCenterX
()
-
meanCenterX
);
varianceY
+=
square
(
box
.
getCenterY
()
-
meanCenterY
);
varianceZ
+=
square
(
box
.
getCenterZ
()
-
meanCenterZ
);
}
double
spreadX
=
normalizedStdDev
(
varianceX
,
boxes
.
size
(),
totalBox
.
getExtentX
());
double
spreadY
=
normalizedStdDev
(
varianceY
,
boxes
.
size
(),
totalBox
.
getExtentY
());
double
spreadZ
=
normalizedStdDev
(
varianceZ
,
boxes
.
size
(),
totalBox
.
getExtentZ
());
return
(
spreadX
+
spreadY
+
spreadZ
)
/
3.0
;
}
private
static
double
normalizedStdDev
(
double
varianceSum
,
int
count
,
double
extent
)
{
if
(
extent
==
0.0
)
{
return
0.0
;
}
return
Math
.
sqrt
(
varianceSum
/
count
)
/
extent
;
}
private
static
double
square
(
double
value
)
{
return
value
*
value
;
}
public
int
getElementCount
()
{
return
elementCount
;
}
public
double
getExtentX
()
{
return
extentX
;
}
public
double
getExtentY
()
{
return
extentY
;
}
public
double
getExtentZ
()
{
return
extentZ
;
}
public
double
getFlatnessRatio
()
{
return
flatnessRatio
;
}
public
double
getAverageRelativeBoxExtent
()
{
return
averageRelativeBoxExtent
;
}
public
double
getThinBoxRatio
()
{
return
thinBoxRatio
;
}
public
double
getAverageRelativeBoxVolume
()
{
return
averageRelativeBoxVolume
;
}
public
double
getCenterSpreadRatio
()
{
return
centerSpreadRatio
;
}
}
public
static
boolean
shouldUseTree
(
BvhCheckType
checkType
,
int
elementCount
)
{
public
static
boolean
shouldUseTree
(
BvhCheckType
checkType
,
int
elementCount
)
{
return
shouldUseTree
(
checkType
,
BvhInputSummary
.
countOnly
(
elementCount
));
}
public
static
boolean
shouldUseTree
(
BvhCheckType
checkType
,
BvhInputSummary
summary
)
{
int
elementCount
=
summary
.
getElementCount
();
if
(
elementCount
<
2
)
{
if
(
elementCount
<
2
)
{
return
false
;
return
false
;
}
}
return
elementCount
>=
thresholdFor
(
checkType
);
if
(
elementCount
<
thresholdFor
(
checkType
))
{
return
false
;
}
if
(
summary
.
getAverageRelativeBoxExtent
()
>
0.65
&&
elementCount
<
128
)
{
return
false
;
}
return
true
;
}
}
public
static
SplitStrategy
chooseSplitStrategy
(
BvhCheckType
checkType
,
int
elementCount
)
{
public
static
SplitStrategy
chooseSplitStrategy
(
BvhCheckType
checkType
,
int
elementCount
)
{
return
chooseSplitStrategy
(
checkType
,
BvhInputSummary
.
countOnly
(
elementCount
));
}
public
static
SplitStrategy
chooseSplitStrategy
(
BvhCheckType
checkType
,
BvhInputSummary
summary
)
{
if
(
checkType
==
BvhCheckType
.
RING_SELF_INTERSECTION
)
{
if
(
checkType
==
BvhCheckType
.
RING_SELF_INTERSECTION
)
{
return
SplitStrategy
.
BINARY_SPATIAL_MEDIAN
;
return
SplitStrategy
.
BINARY_SPATIAL_MEDIAN
;
}
}
if
(
elementCount
>=
128
)
{
if
(
summary
.
getFlatnessRatio
()
<
0.02
)
{
return
SplitStrategy
.
BINARY_SPATIAL_MEDIAN
;
}
if
(
summary
.
getElementCount
()
>=
128
)
{
return
SplitStrategy
.
OCTONARY_OBJECT_MEAN
;
return
SplitStrategy
.
OCTONARY_OBJECT_MEAN
;
}
}
return
SplitStrategy
.
BINARY_SPATIAL_MEDIAN
;
return
SplitStrategy
.
BINARY_SPATIAL_MEDIAN
;
...
...
CityDoctorParent/CityDoctorValidation/src/test/java/de/hft/stuttgart/citydoctor2/checks/bht/BvhHeuristicTimingSupport.java
0 → 100644
View file @
35a777b9
package
de.hft.stuttgart.citydoctor2.checks.bht
;
import
static
org
.
junit
.
Assert
.
assertEquals
;
import
java.util.ArrayList
;
import
java.util.List
;
import
de.hft.stuttgart.citydoctor2.checks.geometry.NestedRingsCheck
;
import
de.hft.stuttgart.citydoctor2.checks.geometry.RingSelfIntCheck
;
import
de.hft.stuttgart.citydoctor2.checks.util.BvhUsagePolicy
;
import
de.hft.stuttgart.citydoctor2.datastructure.bht.SplitStrategy
;
public
final
class
BvhHeuristicTimingSupport
{
private
BvhHeuristicTimingSupport
()
{
}
public
static
Observation
measureVariants
(
String
checkName
,
String
scenarioName
,
BvhInputMetricsCollector
.
Metrics
metrics
,
int
inputSize
,
String
baselineName
,
MeasuredOperation
baselineOperation
,
StrategyOperation
strategyOperation
)
{
List
<
BvhPerformanceTestSupport
.
Measurement
>
measurements
=
new
ArrayList
<>();
BvhPerformanceTestSupport
.
Measurement
baseline
=
BvhPerformanceTestSupport
.
measure
(
scenarioName
,
baselineName
,
inputSize
,
baselineOperation:
:
run
);
measurements
.
add
(
baseline
);
for
(
SplitStrategy
strategy
:
BvhPerformanceTestSupport
.
concreteStrategies
())
{
BvhPerformanceTestSupport
.
Measurement
bvhMeasurement
=
BvhPerformanceTestSupport
.
measure
(
scenarioName
,
strategy
.
name
(),
inputSize
,
()
->
strategyOperation
.
run
(
strategy
));
measurements
.
add
(
bvhMeasurement
);
assertEquals
(
checkName
+
" result differs for "
+
scenarioName
+
" / "
+
strategy
,
baseline
.
resultCount
,
bvhMeasurement
.
resultCount
);
}
String
syntheticPrediction
=
predictFromSyntheticPolicy
(
checkName
,
metrics
);
String
candidatePrediction
=
predictCandidateStrategy
(
checkName
,
metrics
);
return
new
Observation
(
checkName
,
metrics
,
fastest
(
measurements
),
fastestBvh
(
measurements
),
baseline
,
syntheticPrediction
,
candidatePrediction
,
measurementForPrediction
(
measurements
,
candidatePrediction
));
}
public
static
Observation
measureBvhStrategies
(
String
checkName
,
String
scenarioName
,
BvhInputMetricsCollector
.
Metrics
metrics
,
int
inputSize
,
StrategyOperation
strategyOperation
)
{
List
<
BvhPerformanceTestSupport
.
Measurement
>
measurements
=
new
ArrayList
<>();
BvhPerformanceTestSupport
.
Measurement
baseline
=
null
;
for
(
SplitStrategy
strategy
:
BvhPerformanceTestSupport
.
concreteStrategies
())
{
BvhPerformanceTestSupport
.
Measurement
bvhMeasurement
=
BvhPerformanceTestSupport
.
measure
(
scenarioName
,
strategy
.
name
(),
inputSize
,
()
->
strategyOperation
.
run
(
strategy
));
if
(
baseline
==
null
)
{
baseline
=
bvhMeasurement
;
}
else
{
assertEquals
(
checkName
+
" broad-phase count differs for "
+
scenarioName
+
" / "
+
strategy
,
baseline
.
resultCount
,
bvhMeasurement
.
resultCount
);
}
measurements
.
add
(
bvhMeasurement
);
}
String
syntheticPrediction
=
predictFromSyntheticPolicy
(
checkName
,
metrics
);
String
candidatePrediction
=
predictCandidateStrategy
(
checkName
,
metrics
);
return
new
Observation
(
checkName
,
metrics
,
fastest
(
measurements
),
fastestBvh
(
measurements
),
baseline
,
syntheticPrediction
,
candidatePrediction
,
measurementForPrediction
(
measurements
,
candidatePrediction
));
}
public
static
String
predictFromSyntheticPolicy
(
String
checkName
,
BvhInputMetricsCollector
.
Metrics
metrics
)
{
BvhUsagePolicy
.
BvhCheckType
checkType
=
checkTypeFor
(
checkName
);
if
(!
BvhUsagePolicy
.
shouldUseTree
(
checkType
,
metrics
.
summary
))
{
return
"OLD"
;
}
return
BvhUsagePolicy
.
chooseSplitStrategy
(
checkType
,
metrics
.
summary
).
name
();
}
public
static
String
predictCandidateStrategy
(
String
checkName
,
BvhInputMetricsCollector
.
Metrics
metrics
)
{
BvhUsagePolicy
.
BvhCheckType
checkType
=
checkTypeFor
(
checkName
);
if
(!
BvhUsagePolicy
.
shouldUseTree
(
checkType
,
metrics
.
summary
))
{
return
"OLD"
;
}
if
(
"SSI"
.
equals
(
checkName
)
&&
metrics
.
averageAspectRatio
>=
20.0
&&
metrics
.
averageAspectRatio
<
80.0
)
{
return
SplitStrategy
.
OCTONARY_SPATIAL_MEDIAN
.
name
();
}
if
(
"NESTED"
.
equals
(
checkName
)
&&
metrics
.
averageRelativeBoxVolume
<=
0.0001
)
{
return
SplitStrategy
.
OCTONARY_OBJECT_MEAN
.
name
();
}
if
(
"RSI"
.
equals
(
checkName
)
&&
metrics
.
thinBoxRate
>
0.75
)
{
return
SplitStrategy
.
OCTONARY_SPATIAL_MEDIAN
.
name
();
}
if
(
"RSI"
.
equals
(
checkName
)
&&
metrics
.
averageAspectRatio
>=
80.0
)
{
return
SplitStrategy
.
OCTONARY_SPATIAL_MEDIAN
.
name
();
}
if
(
"RSI"
.
equals
(
checkName
)
&&
metrics
.
averageRelativeBoxVolume
<=
0.0001
)
{
return
SplitStrategy
.
OCTONARY_SPATIAL_MEDIAN
.
name
();
}
return
BvhUsagePolicy
.
chooseSplitStrategy
(
checkType
,
metrics
.
summary
).
name
();
}
public
static
NestedRingsCheck
.
Variant
nestedVariantFor
(
SplitStrategy
strategy
)
{
switch
(
strategy
)
{
case
BINARY_OBJECT_MEDIAN:
return
NestedRingsCheck
.
Variant
.
BVH_BINARY_OBJECT_MEDIAN
;
case
BINARY_OBJECT_MEAN:
return
NestedRingsCheck
.
Variant
.
BVH_BINARY_OBJECT_MEAN
;
case
BINARY_SPATIAL_MEDIAN:
return
NestedRingsCheck
.
Variant
.
BVH_BINARY_SPATIAL_MEDIAN
;
case
OCTONARY_OBJECT_MEDIAN:
return
NestedRingsCheck
.
Variant
.
BVH_OCTONARY_OBJECT_MEDIAN
;
case
OCTONARY_OBJECT_MEAN:
return
NestedRingsCheck
.
Variant
.
BVH_OCTONARY_OBJECT_MEAN
;
case
OCTONARY_SPATIAL_MEDIAN:
return
NestedRingsCheck
.
Variant
.
BVH_OCTONARY_SPATIAL_MEDIAN
;
case
AUTO:
default
:
throw
new
IllegalArgumentException
(
"Unsupported nested-ring BVH strategy: "
+
strategy
);
}
}
public
static
RingSelfIntCheck
.
Variant
rsiVariantFor
(
SplitStrategy
strategy
)
{
switch
(
strategy
)
{
case
BINARY_OBJECT_MEDIAN:
return
RingSelfIntCheck
.
Variant
.
BVH_BINARY_OBJECT_MEDIAN
;
case
BINARY_OBJECT_MEAN:
return
RingSelfIntCheck
.
Variant
.
BVH_BINARY_OBJECT_MEAN
;
case
BINARY_SPATIAL_MEDIAN:
return
RingSelfIntCheck
.
Variant
.
BVH_BINARY_SPATIAL_MEDIAN
;
case
OCTONARY_OBJECT_MEDIAN:
return
RingSelfIntCheck
.
Variant
.
BVH_OCTONARY_OBJECT_MEDIAN
;
case
OCTONARY_OBJECT_MEAN:
return
RingSelfIntCheck
.
Variant
.
BVH_OCTONARY_OBJECT_MEAN
;
case
OCTONARY_SPATIAL_MEDIAN:
return
RingSelfIntCheck
.
Variant
.
BVH_OCTONARY_SPATIAL_MEDIAN
;
case
AUTO:
default
:
throw
new
IllegalArgumentException
(
"Unsupported ring-self-intersection BVH strategy: "
+
strategy
);
}
}
private
static
BvhUsagePolicy
.
BvhCheckType
checkTypeFor
(
String
checkName
)
{
if
(
"SSI"
.
equals
(
checkName
))
{
return
BvhUsagePolicy
.
BvhCheckType
.
SOLID_SELF_INTERSECTION
;
}
if
(
"NESTED"
.
equals
(
checkName
))
{
return
BvhUsagePolicy
.
BvhCheckType
.
NESTED_RINGS
;
}
if
(
"RSI"
.
equals
(
checkName
))
{
return
BvhUsagePolicy
.
BvhCheckType
.
RING_SELF_INTERSECTION
;
}
throw
new
IllegalArgumentException
(
"Unsupported check name: "
+
checkName
);
}
private
static
BvhPerformanceTestSupport
.
Measurement
fastest
(
List
<
BvhPerformanceTestSupport
.
Measurement
>
measurements
)
{
BvhPerformanceTestSupport
.
Measurement
fastest
=
measurements
.
get
(
0
);
for
(
BvhPerformanceTestSupport
.
Measurement
measurement
:
measurements
)
{
if
(
measurement
.
averageNanos
<
fastest
.
averageNanos
)
{
fastest
=
measurement
;
}
}
return
fastest
;
}
private
static
BvhPerformanceTestSupport
.
Measurement
fastestBvh
(
List
<
BvhPerformanceTestSupport
.
Measurement
>
measurements
)
{
BvhPerformanceTestSupport
.
Measurement
fastest
=
null
;
for
(
BvhPerformanceTestSupport
.
Measurement
measurement
:
measurements
)
{
if
(
"BRUTE_FORCE"
.
equals
(
measurement
.
variant
)
||
NestedRingsCheck
.
Variant
.
OLD
.
name
().
equals
(
measurement
.
variant
)
||
RingSelfIntCheck
.
Variant
.
OLD
.
name
().
equals
(
measurement
.
variant
))
{
continue
;
}
if
(
fastest
==
null
||
measurement
.
averageNanos
<
fastest
.
averageNanos
)
{
fastest
=
measurement
;
}
}
return
fastest
;
}
private
static
BvhPerformanceTestSupport
.
Measurement
measurementForPrediction
(
List
<
BvhPerformanceTestSupport
.
Measurement
>
measurements
,
String
prediction
)
{
for
(
BvhPerformanceTestSupport
.
Measurement
measurement
:
measurements
)
{
if
(
measurement
.
variant
.
equals
(
prediction
))
{
return
measurement
;
}
}
return
null
;
}
public
interface
MeasuredOperation
{
int
run
();
}
public
interface
StrategyOperation
{
int
run
(
SplitStrategy
strategy
);
}
public
static
final
class
Observation
{
public
final
String
checkName
;
public
final
BvhInputMetricsCollector
.
Metrics
metrics
;
public
final
BvhPerformanceTestSupport
.
Measurement
fastest
;
public
final
BvhPerformanceTestSupport
.
Measurement
fastestBvh
;
public
final
BvhPerformanceTestSupport
.
Measurement
baseline
;
public
final
BvhPerformanceTestSupport
.
Measurement
predictedBvh
;
public
final
String
syntheticPrediction
;
public
final
String
candidatePrediction
;
Observation
(
String
checkName
,
BvhInputMetricsCollector
.
Metrics
metrics
,
BvhPerformanceTestSupport
.
Measurement
fastest
,
BvhPerformanceTestSupport
.
Measurement
fastestBvh
,
BvhPerformanceTestSupport
.
Measurement
baseline
,
String
syntheticPrediction
,
String
candidatePrediction
,
BvhPerformanceTestSupport
.
Measurement
predictedBvh
)
{
this
.
checkName
=
checkName
;
this
.
metrics
=
metrics
;
this
.
fastest
=
fastest
;
this
.
fastestBvh
=
fastestBvh
;
this
.
baseline
=
baseline
;
this
.
syntheticPrediction
=
syntheticPrediction
;
this
.
candidatePrediction
=
candidatePrediction
;
this
.
predictedBvh
=
predictedBvh
;
}
public
String
candidatePredictionMatchLabel
()
{
if
(
"OLD"
.
equals
(
candidatePrediction
))
{
return
"n/a"
;
}
return
candidatePrediction
.
equals
(
fastestBvh
.
variant
)
?
"yes"
:
"no"
;
}
}
}
CityDoctorParent/CityDoctorValidation/src/test/java/de/hft/stuttgart/citydoctor2/checks/bht/BvhInputMetricsCollector.java
View file @
35a777b9
...
@@ -8,12 +8,13 @@ import java.util.function.Function;
...
@@ -8,12 +8,13 @@ import java.util.function.Function;
import
de.hft.stuttgart.citydoctor2.datastructure.LinearRing
;
import
de.hft.stuttgart.citydoctor2.datastructure.LinearRing
;
import
de.hft.stuttgart.citydoctor2.datastructure.Polygon
;
import
de.hft.stuttgart.citydoctor2.datastructure.Polygon
;
import
de.hft.stuttgart.citydoctor2.datastructure.bht.AABB
;
import
de.hft.stuttgart.citydoctor2.datastructure.bht.AABB
;
import
de.hft.stuttgart.citydoctor2.checks.util.BvhUsagePolicy
;
/**
/**
* Collects cheap broad-phase shape metrics for BVH strategy experiments.
* Collects cheap broad-phase shape metrics for BVH strategy experiments.
*
*
* These metrics are intentionally geometry-agnostic: the same collector can be
* These metrics are intentionally geometry-agnostic: the same collector can be
* used for solid polygons, nested-ring rings, ring-self-intersection edges
later
,
* used for solid polygons, nested-ring rings, ring-self-intersection edges,
* synthetic fixtures, and parsed CityGML models.
* synthetic fixtures, and parsed CityGML models.
*
*
* @author Numanoglu
* @author Numanoglu
...
@@ -22,6 +23,7 @@ final class BvhInputMetricsCollector {
...
@@ -22,6 +23,7 @@ final class BvhInputMetricsCollector {
private
static
final
double
DEGENERATE_TOLERANCE
=
1
e
-
12
;
private
static
final
double
DEGENERATE_TOLERANCE
=
1
e
-
12
;
private
static
final
long
MAX_PAIR_SAMPLES
=
20_000L
;
private
static
final
long
MAX_PAIR_SAMPLES
=
20_000L
;
private
static
final
double
THIN_BOX_ASPECT_RATIO
=
20.0
;
private
BvhInputMetricsCollector
()
{
private
BvhInputMetricsCollector
()
{
}
}
...
@@ -30,14 +32,30 @@ final class BvhInputMetricsCollector {
...
@@ -30,14 +32,30 @@ final class BvhInputMetricsCollector {
return
collect
(
scenario
,
polygons
,
polygon
->
AABB
.
of
(
polygon
.
getOriginal
()));
return
collect
(
scenario
,
polygons
,
polygon
->
AABB
.
of
(
polygon
.
getOriginal
()));
}
}
static
Metrics
forPolygonsCheap
(
String
scenario
,
List
<?
extends
Polygon
>
polygons
)
{
return
collect
(
scenario
,
polygons
,
polygon
->
AABB
.
of
(
polygon
.
getOriginal
()),
false
);
}
static
Metrics
forRings
(
String
scenario
,
List
<?
extends
LinearRing
>
rings
)
{
static
Metrics
forRings
(
String
scenario
,
List
<?
extends
LinearRing
>
rings
)
{
return
collect
(
scenario
,
rings
,
AABB:
:
of
);
return
collect
(
scenario
,
rings
,
AABB:
:
of
);
}
}
static
Metrics
forRingsCheap
(
String
scenario
,
List
<?
extends
LinearRing
>
rings
)
{
return
collect
(
scenario
,
rings
,
AABB:
:
of
,
false
);
}
/**
/**
* Collects metrics from any element type that can be converted to an AABB.
* Collects metrics from any element type that can be converted to an AABB.
*/
*/
static
<
E
>
Metrics
collect
(
String
scenario
,
List
<
E
>
elements
,
Function
<
E
,
AABB
>
aabbFunction
)
{
static
<
E
>
Metrics
collect
(
String
scenario
,
List
<
E
>
elements
,
Function
<
E
,
AABB
>
aabbFunction
)
{
return
collect
(
scenario
,
elements
,
aabbFunction
,
true
);
}
static
<
E
>
Metrics
collect
(
String
scenario
,
List
<
E
>
elements
,
Function
<
E
,
AABB
>
aabbFunction
,
boolean
samplePairs
)
{
List
<
AABB
>
boxes
=
new
ArrayList
<>(
elements
.
size
());
List
<
AABB
>
boxes
=
new
ArrayList
<>(
elements
.
size
());
for
(
E
element
:
elements
)
{
for
(
E
element
:
elements
)
{
AABB
aabb
=
aabbFunction
.
apply
(
element
);
AABB
aabb
=
aabbFunction
.
apply
(
element
);
...
@@ -46,7 +64,7 @@ final class BvhInputMetricsCollector {
...
@@ -46,7 +64,7 @@ final class BvhInputMetricsCollector {
}
}
}
}
return
collectBoxes
(
scenario
,
boxes
);
return
collectBoxes
(
scenario
,
boxes
,
samplePairs
);
}
}
/**
/**
...
@@ -54,16 +72,27 @@ final class BvhInputMetricsCollector {
...
@@ -54,16 +72,27 @@ final class BvhInputMetricsCollector {
* are sampled when the full pairs set would be too large.
* are sampled when the full pairs set would be too large.
*/
*/
static
Metrics
collectBoxes
(
String
scenario
,
List
<
AABB
>
boxes
)
{
static
Metrics
collectBoxes
(
String
scenario
,
List
<
AABB
>
boxes
)
{
return
collectBoxes
(
scenario
,
boxes
,
true
);
}
static
Metrics
collectBoxesCheap
(
String
scenario
,
List
<
AABB
>
boxes
)
{
return
collectBoxes
(
scenario
,
boxes
,
false
);
}
private
static
Metrics
collectBoxes
(
String
scenario
,
List
<
AABB
>
boxes
,
boolean
samplePairs
)
{
int
count
=
boxes
.
size
();
int
count
=
boxes
.
size
();
if
(
count
==
0
)
{
if
(
count
==
0
)
{
return
Metrics
.
empty
(
scenario
);
return
Metrics
.
empty
(
scenario
);
}
}
Aggregate
aggregate
=
aggregate
(
boxes
);
Aggregate
aggregate
=
aggregate
(
boxes
);
PairSample
pairSample
=
samplePairs
(
boxes
);
PairSample
pairSample
=
samplePairs
?
samplePairs
(
boxes
)
:
PairSample
.
empty
();
BvhUsagePolicy
.
BvhInputSummary
summary
=
BvhUsagePolicy
.
BvhInputSummary
.
ofAabbs
(
boxes
);
int
degenerateCount
=
0
;
int
degenerateCount
=
0
;
int
thinBoxCount
=
0
;
double
totalAspectRatio
=
0.0
;
double
totalAspectRatio
=
0.0
;
double
totalRelativeVolume
=
0.0
;
double
totalDx
=
0.0
;
double
totalDx
=
0.0
;
double
totalDy
=
0.0
;
double
totalDy
=
0.0
;
double
totalDz
=
0.0
;
double
totalDz
=
0.0
;
...
@@ -76,13 +105,18 @@ final class BvhInputMetricsCollector {
...
@@ -76,13 +105,18 @@ final class BvhInputMetricsCollector {
degenerateCount
++;
degenerateCount
++;
}
}
double
dx
=
extentX
(
box
);
double
dx
=
box
.
getExtentX
();
double
dy
=
extentY
(
box
);
double
dy
=
box
.
getExtentY
();
double
dz
=
extentZ
(
box
);
double
dz
=
box
.
getExtentZ
();
double
currentAspectRatio
=
aspectRatio
(
dx
,
dy
,
dz
);
if
(
currentAspectRatio
>=
THIN_BOX_ASPECT_RATIO
)
{
thinBoxCount
++;
}
totalDx
+=
dx
;
totalDx
+=
dx
;
totalDy
+=
dy
;
totalDy
+=
dy
;
totalDz
+=
dz
;
totalDz
+=
dz
;
totalAspectRatio
+=
aspectRatio
(
dx
,
dy
,
dz
);
totalAspectRatio
+=
currentAspectRatio
;
totalRelativeVolume
+=
relativeVolume
(
dx
,
dy
,
dz
,
aggregate
);
totalCx
+=
box
.
getCenterX
();
totalCx
+=
box
.
getCenterX
();
totalCy
+=
box
.
getCenterY
();
totalCy
+=
box
.
getCenterY
();
totalCz
+=
box
.
getCenterZ
();
totalCz
+=
box
.
getCenterZ
();
...
@@ -108,13 +142,19 @@ final class BvhInputMetricsCollector {
...
@@ -108,13 +142,19 @@ final class BvhInputMetricsCollector {
pairSample
.
overlapRate
(),
pairSample
.
overlapRate
(),
pairSample
.
containmentRate
(),
pairSample
.
containmentRate
(),
(
double
)
degenerateCount
/
count
,
(
double
)
degenerateCount
/
count
,
(
double
)
thinBoxCount
/
count
,
totalAspectRatio
/
count
,
totalAspectRatio
/
count
,
totalRelativeVolume
/
count
,
totalDx
/
count
,
totalDx
/
count
,
totalDy
/
count
,
totalDy
/
count
,
totalDz
/
count
,
totalDz
/
count
,
Math
.
sqrt
(
varianceX
/
count
)
/
positiveOrOne
(
aggregate
.
dx
),
Math
.
sqrt
(
varianceX
/
count
)
/
positiveOrOne
(
aggregate
.
dx
),
Math
.
sqrt
(
varianceY
/
count
)
/
positiveOrOne
(
aggregate
.
dy
),
Math
.
sqrt
(
varianceY
/
count
)
/
positiveOrOne
(
aggregate
.
dy
),
Math
.
sqrt
(
varianceZ
/
count
)
/
positiveOrOne
(
aggregate
.
dz
));
Math
.
sqrt
(
varianceZ
/
count
)
/
positiveOrOne
(
aggregate
.
dz
),
(
Math
.
sqrt
(
varianceX
/
count
)
/
positiveOrOne
(
aggregate
.
dx
)
+
Math
.
sqrt
(
varianceY
/
count
)
/
positiveOrOne
(
aggregate
.
dy
)
+
Math
.
sqrt
(
varianceZ
/
count
)
/
positiveOrOne
(
aggregate
.
dz
))
/
3.0
,
summary
);
}
}
/**
/**
...
@@ -125,22 +165,33 @@ final class BvhInputMetricsCollector {
...
@@ -125,22 +165,33 @@ final class BvhInputMetricsCollector {
}
}
private
static
Aggregate
aggregate
(
List
<
AABB
>
boxes
)
{
private
static
Aggregate
aggregate
(
List
<
AABB
>
boxes
)
{
double
minX
=
Double
.
POSITIVE_INFINITY
;
AABB
totalBox
=
AABB
.
enclosing
(
boxes
);
double
minY
=
Double
.
POSITIVE_INFINITY
;
return
new
Aggregate
(
totalBox
.
getExtentX
(),
totalBox
.
getExtentY
(),
totalBox
.
getExtentZ
());
double
minZ
=
Double
.
POSITIVE_INFINITY
;
}
double
maxX
=
Double
.
NEGATIVE_INFINITY
;
double
maxY
=
Double
.
NEGATIVE_INFINITY
;
double
maxZ
=
Double
.
NEGATIVE_INFINITY
;
for
(
AABB
box
:
boxes
)
{
private
static
double
relativeVolume
(
double
dx
,
double
dy
,
double
dz
,
Aggregate
aggregate
)
{
minX
=
Math
.
min
(
minX
,
box
.
getMinX
());
double
localMeasure
=
1.0
;
minY
=
Math
.
min
(
minY
,
box
.
getMinY
());
double
totalMeasure
=
1.0
;
minZ
=
Math
.
min
(
minZ
,
box
.
getMinZ
());
boolean
hasActiveExtent
=
false
;
maxX
=
Math
.
max
(
maxX
,
box
.
getMaxX
());
if
(
dx
>
DEGENERATE_TOLERANCE
)
{
maxY
=
Math
.
max
(
maxY
,
box
.
getMaxY
());
localMeasure
*=
dx
;
maxZ
=
Math
.
max
(
maxZ
,
box
.
getMaxZ
());
totalMeasure
*=
aggregate
.
dx
;
hasActiveExtent
=
true
;
}
if
(
dy
>
DEGENERATE_TOLERANCE
)
{
localMeasure
*=
dy
;
totalMeasure
*=
aggregate
.
dy
;
hasActiveExtent
=
true
;
}
if
(
dz
>
DEGENERATE_TOLERANCE
)
{
localMeasure
*=
dz
;
totalMeasure
*=
aggregate
.
dz
;
hasActiveExtent
=
true
;
}
}
return
new
Aggregate
(
maxX
-
minX
,
maxY
-
minY
,
maxZ
-
minZ
);
if
(!
hasActiveExtent
||
totalMeasure
<=
DEGENERATE_TOLERANCE
)
{
return
0.0
;
}
return
localMeasure
/
totalMeasure
;
}
}
private
static
PairSample
samplePairs
(
List
<
AABB
>
boxes
)
{
private
static
PairSample
samplePairs
(
List
<
AABB
>
boxes
)
{
...
@@ -174,18 +225,6 @@ final class BvhInputMetricsCollector {
...
@@ -174,18 +225,6 @@ final class BvhInputMetricsCollector {
return
(
long
)
count
*
(
count
-
1
)
/
2L
;
return
(
long
)
count
*
(
count
-
1
)
/
2L
;
}
}
private
static
double
extentX
(
AABB
box
)
{
return
box
.
getMaxX
()
-
box
.
getMinX
();
}
private
static
double
extentY
(
AABB
box
)
{
return
box
.
getMaxY
()
-
box
.
getMinY
();
}
private
static
double
extentZ
(
AABB
box
)
{
return
box
.
getMaxZ
()
-
box
.
getMinZ
();
}
private
static
double
aspectRatio
(
double
dx
,
double
dy
,
double
dz
)
{
private
static
double
aspectRatio
(
double
dx
,
double
dy
,
double
dz
)
{
double
max
=
Math
.
max
(
dx
,
Math
.
max
(
dy
,
dz
));
double
max
=
Math
.
max
(
dx
,
Math
.
max
(
dy
,
dz
));
double
min
=
Math
.
min
(
positiveOrMax
(
dx
),
Math
.
min
(
positiveOrMax
(
dy
),
positiveOrMax
(
dz
)));
double
min
=
Math
.
min
(
positiveOrMax
(
dx
),
Math
.
min
(
positiveOrMax
(
dy
),
positiveOrMax
(
dz
)));
...
@@ -237,6 +276,10 @@ final class BvhInputMetricsCollector {
...
@@ -237,6 +276,10 @@ final class BvhInputMetricsCollector {
double
containmentRate
()
{
double
containmentRate
()
{
return
sampledPairs
==
0L
?
0.0
:
(
double
)
containedPairs
/
sampledPairs
;
return
sampledPairs
==
0L
?
0.0
:
(
double
)
containedPairs
/
sampledPairs
;
}
}
static
PairSample
empty
()
{
return
new
PairSample
(
0L
,
0L
,
0L
);
}
}
}
static
final
class
Metrics
{
static
final
class
Metrics
{
...
@@ -247,13 +290,17 @@ final class BvhInputMetricsCollector {
...
@@ -247,13 +290,17 @@ final class BvhInputMetricsCollector {
final
double
sampledOverlapRate
;
final
double
sampledOverlapRate
;
final
double
sampledContainmentRate
;
final
double
sampledContainmentRate
;
final
double
degenerateRate
;
final
double
degenerateRate
;
final
double
thinBoxRate
;
final
double
averageAspectRatio
;
final
double
averageAspectRatio
;
final
double
averageRelativeBoxVolume
;
final
double
averageExtentX
;
final
double
averageExtentX
;
final
double
averageExtentY
;
final
double
averageExtentY
;
final
double
averageExtentZ
;
final
double
averageExtentZ
;
final
double
normalizedCenterSpreadX
;
final
double
normalizedCenterSpreadX
;
final
double
normalizedCenterSpreadY
;
final
double
normalizedCenterSpreadY
;
final
double
normalizedCenterSpreadZ
;
final
double
normalizedCenterSpreadZ
;
final
double
centerSpreadRatio
;
final
BvhUsagePolicy
.
BvhInputSummary
summary
;
private
Metrics
(
private
Metrics
(
String
scenario
,
String
scenario
,
...
@@ -263,13 +310,17 @@ final class BvhInputMetricsCollector {
...
@@ -263,13 +310,17 @@ final class BvhInputMetricsCollector {
double
sampledOverlapRate
,
double
sampledOverlapRate
,
double
sampledContainmentRate
,
double
sampledContainmentRate
,
double
degenerateRate
,
double
degenerateRate
,
double
thinBoxRate
,
double
averageAspectRatio
,
double
averageAspectRatio
,
double
averageRelativeBoxVolume
,
double
averageExtentX
,
double
averageExtentX
,
double
averageExtentY
,
double
averageExtentY
,
double
averageExtentZ
,
double
averageExtentZ
,
double
normalizedCenterSpreadX
,
double
normalizedCenterSpreadX
,
double
normalizedCenterSpreadY
,
double
normalizedCenterSpreadY
,
double
normalizedCenterSpreadZ
)
{
double
normalizedCenterSpreadZ
,
double
centerSpreadRatio
,
BvhUsagePolicy
.
BvhInputSummary
summary
)
{
this
.
scenario
=
scenario
;
this
.
scenario
=
scenario
;
this
.
elementCount
=
elementCount
;
this
.
elementCount
=
elementCount
;
this
.
totalPairs
=
totalPairs
;
this
.
totalPairs
=
totalPairs
;
...
@@ -277,25 +328,30 @@ final class BvhInputMetricsCollector {
...
@@ -277,25 +328,30 @@ final class BvhInputMetricsCollector {
this
.
sampledOverlapRate
=
sampledOverlapRate
;
this
.
sampledOverlapRate
=
sampledOverlapRate
;
this
.
sampledContainmentRate
=
sampledContainmentRate
;
this
.
sampledContainmentRate
=
sampledContainmentRate
;
this
.
degenerateRate
=
degenerateRate
;
this
.
degenerateRate
=
degenerateRate
;
this
.
thinBoxRate
=
thinBoxRate
;
this
.
averageAspectRatio
=
averageAspectRatio
;
this
.
averageAspectRatio
=
averageAspectRatio
;
this
.
averageRelativeBoxVolume
=
averageRelativeBoxVolume
;
this
.
averageExtentX
=
averageExtentX
;
this
.
averageExtentX
=
averageExtentX
;
this
.
averageExtentY
=
averageExtentY
;
this
.
averageExtentY
=
averageExtentY
;
this
.
averageExtentZ
=
averageExtentZ
;
this
.
averageExtentZ
=
averageExtentZ
;
this
.
normalizedCenterSpreadX
=
normalizedCenterSpreadX
;
this
.
normalizedCenterSpreadX
=
normalizedCenterSpreadX
;
this
.
normalizedCenterSpreadY
=
normalizedCenterSpreadY
;
this
.
normalizedCenterSpreadY
=
normalizedCenterSpreadY
;
this
.
normalizedCenterSpreadZ
=
normalizedCenterSpreadZ
;
this
.
normalizedCenterSpreadZ
=
normalizedCenterSpreadZ
;
this
.
centerSpreadRatio
=
centerSpreadRatio
;
this
.
summary
=
summary
;
}
}
static
Metrics
empty
(
String
scenario
)
{
static
Metrics
empty
(
String
scenario
)
{
return
new
Metrics
(
scenario
,
0
,
0L
,
0L
,
0.0
,
0.0
,
0.0
,
0.0
,
return
new
Metrics
(
scenario
,
0
,
0L
,
0L
,
0.0
,
0.0
,
0.0
,
0.0
,
0.0
,
0.0
,
0.0
,
0.0
,
0.0
,
0.0
);
0.0
,
0.0
,
0.0
,
0.0
,
0.0
,
0.0
,
0.0
,
0.0
,
0.0
,
BvhUsagePolicy
.
BvhInputSummary
.
countOnly
(
0
));
}
}
String
toSummaryLine
()
{
String
toSummaryLine
()
{
return
String
.
format
(
Locale
.
ROOT
,
return
String
.
format
(
Locale
.
ROOT
,
"[BVH-METRICS] %s n=%d pairs=%d sampled=%d overlap=%.4f containment=%.4f"
"[BVH-METRICS] %s n=%d pairs=%d sampled=%d overlap=%.4f containment=%.4f"
+
" degenerate=%.4f aspectAvg=%.2f
avgExtent=(%.2f, %.2f, %.2f)
"
+
" degenerate=%.4f
thin=%.4f
aspectAvg=%.2f
relVol=%.6f
"
+
" centerSpread=(%.3f, %.3f, %.3f)"
,
+
"
avgExtent=(%.2f, %.2f, %.2f)
centerSpread=(%.3f, %.3f, %.3f)
spread=%.3f
"
,
scenario
,
scenario
,
elementCount
,
elementCount
,
totalPairs
,
totalPairs
,
...
@@ -303,13 +359,16 @@ final class BvhInputMetricsCollector {
...
@@ -303,13 +359,16 @@ final class BvhInputMetricsCollector {
sampledOverlapRate
,
sampledOverlapRate
,
sampledContainmentRate
,
sampledContainmentRate
,
degenerateRate
,
degenerateRate
,
thinBoxRate
,
averageAspectRatio
,
averageAspectRatio
,
averageRelativeBoxVolume
,
averageExtentX
,
averageExtentX
,
averageExtentY
,
averageExtentY
,
averageExtentZ
,
averageExtentZ
,
normalizedCenterSpreadX
,
normalizedCenterSpreadX
,
normalizedCenterSpreadY
,
normalizedCenterSpreadY
,
normalizedCenterSpreadZ
);
normalizedCenterSpreadZ
,
centerSpreadRatio
);
}
}
}
}
}
}
CityDoctorParent/CityDoctorValidation/src/test/java/de/hft/stuttgart/citydoctor2/checks/bht/BvhStrategyHeuristicExplorationTest.java
View file @
35a777b9
package
de.hft.stuttgart.citydoctor2.checks.bht
;
package
de.hft.stuttgart.citydoctor2.checks.bht
;
import
static
org
.
junit
.
Assert
.
assertEquals
;
import
java.util.ArrayList
;
import
java.util.ArrayList
;
import
java.util.Collections
;
import
java.util.LinkedHashMap
;
import
java.util.List
;
import
java.util.List
;
import
java.util.Map
;
import
org.junit.jupiter.api.Tag
;
import
org.junit.jupiter.api.Tag
;
import
org.junit.jupiter.api.Test
;
import
org.junit.jupiter.api.Test
;
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.checks.geometry.RingSelfIntCheck
;
import
de.hft.stuttgart.citydoctor2.checks.util.SelfIntersectionUtil
;
import
de.hft.stuttgart.citydoctor2.checks.util.SelfIntersectionUtil
;
import
de.hft.stuttgart.citydoctor2.datastructure.ConcretePolygon
;
import
de.hft.stuttgart.citydoctor2.datastructure.Geometry
;
import
de.hft.stuttgart.citydoctor2.datastructure.Geometry
;
import
de.hft.stuttgart.citydoctor2.datastructure.L
od
;
import
de.hft.stuttgart.citydoctor2.datastructure.L
inearRing
;
import
de.hft.stuttgart.citydoctor2.datastructure.Polygon
;
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.AABB
;
import
de.hft.stuttgart.citydoctor2.datastructure.bht.BoundingVolumeHierarchyTree
;
import
de.hft.stuttgart.citydoctor2.datastructure.bht.BoundingVolumeHierarchyTree
;
import
de.hft.stuttgart.citydoctor2.datastructure.bht.SplitStrategy
;
import
de.hft.stuttgart.citydoctor2.datastructure.bht.SplitStrategy
;
/**
/**
* Collects SSI timing observations together with BVH input metrics.
* Collects compact timing observations and broad-phase metrics for BVH strategy
*
* selection from synthetic fixtures.
* The summary is meant as a working note for deriving conservative strategy
* rules after the fixture set changes.
*
*
* @author Numanoglu
* @author Numanoglu
*/
*/
...
@@ -28,64 +33,67 @@ import de.hft.stuttgart.citydoctor2.datastructure.bht.SplitStrategy;
...
@@ -28,64 +33,67 @@ import de.hft.stuttgart.citydoctor2.datastructure.bht.SplitStrategy;
public
class
BvhStrategyHeuristicExplorationTest
{
public
class
BvhStrategyHeuristicExplorationTest
{
private
static
final
double
DELTA
=
0.001
;
private
static
final
double
DELTA
=
0.001
;
private
static
final
double
EPSILON
=
0.001
;
@Test
@Test
public
void
exploreSsiStrategyCandidatesFromMetricsAndTiming
()
{
public
void
exploreSsiNestedAndRsiStrategyCandidates
()
{
List
<
Scenario
>
scenarios
=
new
ArrayList
<>();
List
<
BvhHeuristicTimingSupport
.
Observation
>
observations
=
new
ArrayList
<>();
scenarios
.
add
(
new
Scenario
(
"ssi-separated-grid"
,
addSsiObservations
(
observations
);
SyntheticSolidGeometryFactory
.
separatedBoxGrid
(
Lod
.
LOD2
,
12
,
12
)));
addNestedObservations
(
observations
);
scenarios
.
add
(
new
Scenario
(
"ssi-overlapping-grid"
,
addRsiObservations
(
observations
);
SyntheticSolidGeometryFactory
.
overlappingBoxGrid
(
Lod
.
LOD2
,
12
,
12
)));
scenarios
.
add
(
new
Scenario
(
"ssi-dense-clusters"
,
SyntheticSolidGeometryFactory
.
denseBoxClusters
(
Lod
.
LOD2
,
8
,
20
)));
scenarios
.
add
(
new
Scenario
(
"ssi-long-thin-slabs"
,
SyntheticSolidGeometryFactory
.
longThinSlabs
(
Lod
.
LOD2
,
160
)));
scenarios
.
add
(
new
Scenario
(
"ssi-flat-box-grid"
,
SyntheticSolidGeometryFactory
.
flatBoxGrid
(
Lod
.
LOD2
,
200
)));
scenarios
.
add
(
new
Scenario
(
"ssi-citylike-mixed"
,
SyntheticCityGmlLikeGeometryFactory
.
mixedUrbanDistrict
(
Lod
.
LOD2
,
5
,
4
)));
scenarios
.
add
(
new
Scenario
(
"ssi-citylike-courtyard"
,
SyntheticCityGmlLikeGeometryFactory
.
courtyardDistrict
(
Lod
.
LOD2
,
16
)));
scenarios
.
add
(
new
Scenario
(
"ssi-citylike-roofs"
,
SyntheticCityGmlLikeGeometryFactory
.
variedRoofDistrict
(
Lod
.
LOD2
,
180
)));
List
<
Observation
>
observations
=
new
ArrayList
<>();
for
(
Scenario
scenario
:
scenarios
)
{
observations
.
add
(
measureScenario
(
scenario
));
}
printObservationSummary
(
observations
);
printObservationSummary
(
observations
);
printBucketSummary
(
observations
);
}
}
private
static
Observation
measureScenario
(
Scenario
scenario
)
{
private
static
void
addSsiObservations
(
List
<
BvhHeuristicTimingSupport
.
Observation
>
observations
)
{
List
<
Polygon
>
polygons
=
scenario
.
geometry
.
getPolygons
();
for
(
BvhSyntheticScenarioCatalog
.
SsiScenario
scenario
:
BvhSyntheticScenarioCatalog
.
ssiScenarios
())
{
BvhInputMetricsCollector
.
Metrics
metrics
=
List
<
Polygon
>
polygons
=
scenario
.
geometry
.
getPolygons
();
BvhInputMetricsCollector
.
forPolygons
(
scenario
.
name
,
polygons
);
BvhInputMetricsCollector
.
Metrics
metrics
=
BvhInputMetricsCollector
.
forPolygonsCheap
(
scenario
.
name
,
polygons
);
List
<
BvhPerformanceTestSupport
.
Measurement
>
measurements
=
new
ArrayList
<>();
observations
.
add
(
BvhHeuristicTimingSupport
.
measureVariants
(
BvhPerformanceTestSupport
.
Measurement
bruteForce
=
BvhPerformanceTestSupport
.
measure
(
"SSI"
,
scenario
.
name
,
"BRUTE_FORCE"
,
polygons
.
size
(),
()
->
SelfIntersectionUtil
.
calculateSolidSelfIntersection0
(
scenario
.
geometry
,
DELTA
).
size
());
measurements
.
add
(
bruteForce
);
for
(
SplitStrategy
strategy
:
BvhPerformanceTestSupport
.
concreteStrategies
())
{
BvhPerformanceTestSupport
.
Measurement
bvhMeasurement
=
BvhPerformanceTestSupport
.
measure
(
scenario
.
name
,
scenario
.
name
,
strategy
.
name
()
,
metrics
,
polygons
.
size
(),
polygons
.
size
(),
()
->
calculateWithTree
(
scenario
.
geometry
,
strategy
));
"BRUTE_FORCE"
,
measurements
.
add
(
bvhMeasurement
);
()
->
SelfIntersectionUtil
.
calculateSolidSelfIntersection0
(
scenario
.
geometry
,
DELTA
).
size
(),
assertEquals
(
"SSI result count differs for "
+
scenario
.
name
+
" / "
+
strategy
,
strategy
->
calculateSsiWithTree
(
scenario
.
geometry
,
strategy
)));
bruteForce
.
resultCount
,
bvhMeasurement
.
resultCount
);
}
}
}
BvhPerformanceTestSupport
.
printScenarioSummary
(
scenario
.
name
,
measurements
);
private
static
void
addNestedObservations
(
List
<
BvhHeuristicTimingSupport
.
Observation
>
observations
)
{
return
new
Observation
(
metrics
,
fastest
(
measurements
),
fastestBvh
(
measurements
),
bruteForce
);
for
(
BvhSyntheticScenarioCatalog
.
NestedScenario
scenario
:
BvhSyntheticScenarioCatalog
.
nestedScenarios
())
{
List
<
LinearRing
>
rings
=
scenario
.
polygon
.
getInnerRings
();
BvhInputMetricsCollector
.
Metrics
metrics
=
BvhInputMetricsCollector
.
forRingsCheap
(
scenario
.
name
,
rings
);
observations
.
add
(
BvhHeuristicTimingSupport
.
measureVariants
(
"NESTED"
,
scenario
.
name
,
metrics
,
rings
.
size
(),
NestedRingsCheck
.
Variant
.
OLD
.
name
(),
()
->
runNestedCheck
(
scenario
.
polygon
,
NestedRingsCheck
.
Variant
.
OLD
),
strategy
->
runNestedCheck
(
scenario
.
polygon
,
BvhHeuristicTimingSupport
.
nestedVariantFor
(
strategy
))));
}
}
}
private
static
int
calculateWithTree
(
Geometry
geometry
,
SplitStrategy
strategy
)
{
private
static
void
addRsiObservations
(
List
<
BvhHeuristicTimingSupport
.
Observation
>
observations
)
{
for
(
BvhSyntheticScenarioCatalog
.
RsiScenario
scenario
:
BvhSyntheticScenarioCatalog
.
rsiScenarios
())
{
BvhInputMetricsCollector
.
Metrics
metrics
=
BvhInputMetricsCollector
.
collectBoxesCheap
(
scenario
.
name
,
collectEdgeBoxes
(
scenario
.
geometry
));
observations
.
add
(
BvhHeuristicTimingSupport
.
measureVariants
(
"RSI"
,
scenario
.
name
,
metrics
,
countEdges
(
scenario
.
geometry
),
RingSelfIntCheck
.
Variant
.
OLD
.
name
(),
()
->
runRsiCheck
(
scenario
.
geometry
,
RingSelfIntCheck
.
Variant
.
OLD
),
strategy
->
runRsiCheck
(
scenario
.
geometry
,
BvhHeuristicTimingSupport
.
rsiVariantFor
(
strategy
))));
}
}
private
static
int
calculateSsiWithTree
(
Geometry
geometry
,
SplitStrategy
strategy
)
{
BoundingVolumeHierarchyTree
<
Polygon
>
tree
=
BoundingVolumeHierarchyTree
<
Polygon
>
tree
=
BoundingVolumeHierarchyTree
.
newWithStrategy
(
BoundingVolumeHierarchyTree
.
newWithStrategy
(
geometry
.
getPolygons
(),
geometry
.
getPolygons
(),
...
@@ -94,109 +102,245 @@ public class BvhStrategyHeuristicExplorationTest {
...
@@ -94,109 +102,245 @@ public class BvhStrategyHeuristicExplorationTest {
return
SelfIntersectionUtil
.
calculateSolidSelfIntersection
(
geometry
,
DELTA
,
tree
).
size
();
return
SelfIntersectionUtil
.
calculateSolidSelfIntersection
(
geometry
,
DELTA
,
tree
).
size
();
}
}
private
static
BvhPerformanceTestSupport
.
Measurement
fastest
(
private
static
int
runNestedCheck
(
ConcretePolygon
polygon
,
NestedRingsCheck
.
Variant
variant
)
{
List
<
BvhPerformanceTestSupport
.
Measurement
>
measurements
)
{
NestedRingsCheck
check
=
new
NestedRingsCheck
(
variant
);
BvhPerformanceTestSupport
.
Measurement
fastest
=
measurements
.
get
(
0
);
check
.
check
(
polygon
);
for
(
BvhPerformanceTestSupport
.
Measurement
measurement
:
measurements
)
{
CheckResult
result
=
polygon
.
getCheckResult
(
check
);
if
(
measurement
.
averageNanos
<
fastest
.
averageNanos
)
{
return
result
.
getResultStatus
()
==
ResultStatus
.
ERROR
?
1
:
0
;
fastest
=
measurement
;
}
private
static
int
runRsiCheck
(
Geometry
geometry
,
RingSelfIntCheck
.
Variant
variant
)
{
int
errorCount
=
0
;
for
(
Polygon
polygon
:
geometry
.
getPolygons
())
{
RingSelfIntCheck
check
=
new
RingSelfIntCheck
(
variant
);
check
.
init
(
Collections
.
singletonMap
(
"minVertexDistance"
,
String
.
valueOf
(
EPSILON
)),
null
);
check
.
check
(
polygon
.
getExteriorRing
());
CheckResult
result
=
polygon
.
getExteriorRing
().
getCheckResult
(
check
);
if
(
result
.
getResultStatus
()
==
ResultStatus
.
ERROR
)
{
errorCount
++;
}
}
}
}
return
fastes
t
;
return
errorCoun
t
;
}
}
private
static
BvhPerformanceTestSupport
.
Measurement
fastestBvh
(
private
static
int
countEdges
(
Geometry
geometry
)
{
List
<
BvhPerformanceTestSupport
.
Measurement
>
measurements
)
{
int
edgeCount
=
0
;
BvhPerformanceTestSupport
.
Measurement
fastest
=
null
;
for
(
Polygon
polygon
:
geometry
.
getPolygons
())
{
for
(
BvhPerformanceTestSupport
.
Measurement
measurement
:
measurements
)
{
LinearRing
ring
=
polygon
.
getExteriorRing
();
if
(
"BRUTE_FORCE"
.
equals
(
measurement
.
variant
))
{
edgeCount
+=
Math
.
max
(
0
,
ring
.
getVertices
().
size
()
-
1
);
continue
;
}
}
return
edgeCount
;
if
(
fastest
==
null
||
measurement
.
averageNanos
<
fastest
.
averageNanos
)
{
}
fastest
=
measurement
;
private
static
List
<
LinearRing
>
collectExteriorRings
(
Geometry
geometry
)
{
List
<
LinearRing
>
rings
=
new
ArrayList
<>();
for
(
Polygon
polygon
:
geometry
.
getPolygons
())
{
rings
.
add
(
polygon
.
getExteriorRing
());
}
return
rings
;
}
private
static
List
<
AABB
>
collectEdgeBoxes
(
Geometry
geometry
)
{
List
<
AABB
>
boxes
=
new
ArrayList
<>();
for
(
LinearRing
ring
:
collectExteriorRings
(
geometry
))
{
List
<
Vertex
>
vertices
=
ring
.
getVertices
();
for
(
int
i
=
0
;
i
<
vertices
.
size
()
-
1
;
i
++)
{
boxes
.
add
(
AABB
.
of
(
vertices
.
get
(
i
),
vertices
.
get
(
i
+
1
),
EPSILON
));
}
}
}
}
return
fast
es
t
;
return
box
es
;
}
}
private
static
void
printObservationSummary
(
List
<
Observation
>
observations
)
{
private
static
void
printObservationSummary
(
List
<
BvhHeuristicTimingSupport
.
Observation
>
observations
)
{
System
.
out
.
println
();
System
.
out
.
println
();
System
.
out
.
println
(
"[BVH-HEURISTIC-EXPLORATION]
SSI observations
"
);
System
.
out
.
println
(
"[BVH-HEURISTIC-EXPLORATION]"
);
System
.
out
.
println
(
"---------------------------------------------------------------------------------------------------------------"
);
System
.
out
.
println
(
"---------------------------------------------------------------------------------------------------------------
----------------
"
);
System
.
out
.
println
(
String
.
format
(
System
.
out
.
println
(
String
.
format
(
"%-24s %7s %9s %9s %9s %10s %12s %22s %22s"
,
"%-7s %-24s %7s %7s %8s %7s %8s %24s %24s %24s %10s"
,
"check"
,
"scenario"
,
"scenario"
,
"n"
,
"n"
,
"
overlap
"
,
"
thin
"
,
"
contain
"
,
"
relVol
"
,
"
degen
"
,
"
spread
"
,
"aspect"
,
"aspect"
,
"winner"
,
"policyPred"
,
"candidatePred"
,
"fastest BVH"
,
"fastest BVH"
,
"
initial candidate rule
"
));
"
bvh ms
"
));
System
.
out
.
println
(
"---------------------------------------------------------------------------------------------------------------"
);
System
.
out
.
println
(
"---------------------------------------------------------------------------------------------------------------
----------------
"
);
for
(
Observation
observation
:
observations
)
{
for
(
BvhHeuristicTimingSupport
.
Observation
observation
:
observations
)
{
BvhInputMetricsCollector
.
Metrics
metrics
=
observation
.
metrics
;
BvhInputMetricsCollector
.
Metrics
metrics
=
observation
.
metrics
;
System
.
out
.
println
(
String
.
format
(
System
.
out
.
println
(
String
.
format
(
"%-24s %7d %9.4f %9.4f %9.4f %10.2f %12s %28s %28s"
,
"%-7s %-24s %7d %7.3f %8.5f %7.3f %8.1f %24s %24s %24s %10.3f"
,
observation
.
checkName
,
metrics
.
scenario
,
metrics
.
scenario
,
metrics
.
elementCount
,
metrics
.
elementCount
,
metrics
.
sampledOverlap
Rate
,
metrics
.
thinBox
Rate
,
metrics
.
sampledContainmentRat
e
,
metrics
.
averageRelativeBoxVolum
e
,
metrics
.
deg
ener
ate
Rat
e
,
metrics
.
c
en
t
er
Spread
Rat
io
,
metrics
.
averageAspectRatio
,
metrics
.
averageAspectRatio
,
observation
.
fastest
.
variant
,
observation
.
syntheticPrediction
,
observation
.
candidatePrediction
,
observation
.
fastestBvh
.
variant
,
observation
.
fastestBvh
.
variant
,
candidateRule
(
observation
)));
observation
.
fastestBvh
.
averageMillis
(
)));
}
}
System
.
out
.
println
(
"---------------------------------------------------------------------------------------------------------------"
);
System
.
out
.
println
(
"---------------------------------------------------------------------------------------------------------------
----------------
"
);
System
.
out
.
println
(
"
Candidate rules are prelim; check them against real CityGML model
s."
);
System
.
out
.
println
(
"
Fastest BVH is the target for candidate strategy rule
s."
);
System
.
out
.
println
();
System
.
out
.
println
();
}
}
private
static
String
candidateRule
(
Observation
observation
)
{
private
static
void
printBucketSummary
(
List
<
BvhHeuristicTimingSupport
.
Observation
>
observations
)
{
BvhInputMetricsCollector
.
Metrics
metrics
=
observation
.
metrics
;
Map
<
String
,
BucketStats
>
buckets
=
new
LinkedHashMap
<>();
if
(
"BRUTE_FORCE"
.
equals
(
observation
.
fastest
.
variant
))
{
for
(
BvhHeuristicTimingSupport
.
Observation
observation
:
observations
)
{
return
"consider brute below/near this n"
;
addBucket
(
buckets
,
observation
,
"thin"
,
thinBucket
(
observation
.
metrics
.
thinBoxRate
));
addBucket
(
buckets
,
observation
,
"relVol"
,
relVolBucket
(
observation
.
metrics
.
averageRelativeBoxVolume
));
addBucket
(
buckets
,
observation
,
"spread"
,
spreadBucket
(
observation
.
metrics
.
centerSpreadRatio
));
addBucket
(
buckets
,
observation
,
"aspect"
,
aspectBucket
(
observation
.
metrics
.
averageAspectRatio
));
}
}
if
(
metrics
.
sampledOverlapRate
<
0.01
&&
metrics
.
elementCount
>
200
)
{
return
"low overlap: BVH likely"
;
System
.
out
.
println
(
"[BVH-WINNER-BY-METRIC-BUCKET]"
);
System
.
out
.
println
(
"-------------------------------------------------------------------------------"
);
System
.
out
.
println
(
String
.
format
(
"%-7s %-7s %-14s %7s %24s %8s"
,
"check"
,
"metric"
,
"bucket"
,
"cases"
,
"mostCommonBvh"
,
"share"
));
System
.
out
.
println
(
"-------------------------------------------------------------------------------"
);
for
(
BucketStats
bucket
:
buckets
.
values
())
{
System
.
out
.
println
(
String
.
format
(
"%-7s %-7s %-14s %7d %24s %7.1f%%"
,
bucket
.
checkName
,
bucket
.
metricName
,
bucket
.
bucketName
,
bucket
.
total
,
bucket
.
mostCommonWinner
(),
bucket
.
mostCommonShare
()
*
100.0
));
}
}
if
(
metrics
.
degenerateRate
>
0.50
||
metrics
.
averageAspectRatio
>
50.0
)
{
return
"flat/skinny: prefer measured stable BVH"
;
System
.
out
.
println
();
System
.
out
.
println
(
"[BVH-CANDIDATE-RULES]"
);
System
.
out
.
println
(
"Minimum support: 3 cases; minimum winner share: 65%."
);
for
(
BucketStats
bucket
:
buckets
.
values
())
{
if
(
bucket
.
total
>=
3
&&
bucket
.
mostCommonShare
()
>=
0.65
)
{
System
.
out
.
println
(
String
.
format
(
"if check=%s and %s in %s -> %s (%d cases, %.1f%%)"
,
bucket
.
checkName
,
bucket
.
metricName
,
bucket
.
bucketName
,
bucket
.
mostCommonWinner
(),
bucket
.
total
,
bucket
.
mostCommonShare
()
*
100.0
));
}
}
}
if
(
metrics
.
sampledOverlapRate
>
0.10
)
{
System
.
out
.
println
();
return
"high overlap: compare BVH vs brute"
;
}
private
static
void
addBucket
(
Map
<
String
,
BucketStats
>
buckets
,
BvhHeuristicTimingSupport
.
Observation
observation
,
String
metricName
,
String
bucketName
)
{
String
key
=
observation
.
checkName
+
"|"
+
metricName
+
"|"
+
bucketName
;
BucketStats
bucket
=
buckets
.
computeIfAbsent
(
key
,
ignored
->
new
BucketStats
(
observation
.
checkName
,
metricName
,
bucketName
));
bucket
.
add
(
observation
.
fastestBvh
.
variant
);
}
private
static
String
thinBucket
(
double
value
)
{
if
(
value
==
0.0
)
{
return
"0"
;
}
if
(
value
<=
0.25
)
{
return
"(0,0.25]"
;
}
if
(
value
<=
0.50
)
{
return
"(0.25,0.50]"
;
}
if
(
value
<=
0.75
)
{
return
"(0.50,0.75]"
;
}
}
return
"
citylike/mixed: prefer fastest BVH candidate
"
;
return
"
(0.75,1]
"
;
}
}
private
static
final
class
Scenario
{
private
static
String
relVolBucket
(
double
value
)
{
final
String
name
;
if
(
value
<=
0.00001
)
{
final
Geometry
geometry
;
return
"<=1e-5"
;
}
if
(
value
<=
0.0001
)
{
return
"<=1e-4"
;
}
if
(
value
<=
0.001
)
{
return
"<=1e-3"
;
}
if
(
value
<=
0.01
)
{
return
"<=1e-2"
;
}
return
">1e-2"
;
}
private
static
String
spreadBucket
(
double
value
)
{
if
(
value
<
0.15
)
{
return
"<0.15"
;
}
if
(
value
<
0.25
)
{
return
"0.15..0.25"
;
}
if
(
value
<
0.35
)
{
return
"0.25..0.35"
;
}
return
">=0.35"
;
}
Scenario
(
String
name
,
Geometry
geometry
)
{
private
static
String
aspectBucket
(
double
value
)
{
this
.
name
=
name
;
if
(
value
<
5.0
)
{
this
.
geometry
=
geometry
;
return
"<5"
;
}
if
(
value
<
20.0
)
{
return
"5..20"
;
}
}
if
(
value
<
80.0
)
{
return
"20..80"
;
}
return
">=80"
;
}
}
private
static
final
class
Observation
{
private
static
final
class
BucketStats
{
final
BvhInputMetricsCollector
.
Metrics
metrics
;
final
String
checkName
;
final
BvhPerformanceTestSupport
.
Measurement
fastest
;
final
String
metricName
;
final
BvhPerformanceTestSupport
.
Measurement
fastestBvh
;
final
String
bucketName
;
final
BvhPerformanceTestSupport
.
Measurement
bruteForce
;
final
Map
<
String
,
Integer
>
winners
=
new
LinkedHashMap
<>();
int
total
;
BucketStats
(
String
checkName
,
String
metricName
,
String
bucketName
)
{
this
.
checkName
=
checkName
;
this
.
metricName
=
metricName
;
this
.
bucketName
=
bucketName
;
}
void
add
(
String
winner
)
{
total
++;
winners
.
merge
(
winner
,
1
,
Integer:
:
sum
);
}
Observation
(
String
mostCommonWinner
()
{
BvhInputMetricsCollector
.
Metrics
metrics
,
String
bestWinner
=
""
;
BvhPerformanceTestSupport
.
Measurement
fastest
,
int
bestCount
=
-
1
;
BvhPerformanceTestSupport
.
Measurement
fastestBvh
,
for
(
Map
.
Entry
<
String
,
Integer
>
entry
:
winners
.
entrySet
())
{
BvhPerformanceTestSupport
.
Measurement
bruteForce
)
{
if
(
entry
.
getValue
()
>
bestCount
)
{
this
.
metrics
=
metrics
;
bestWinner
=
entry
.
getKey
();
this
.
fastest
=
fastest
;
bestCount
=
entry
.
getValue
();
this
.
fastestBvh
=
fastestBvh
;
}
this
.
bruteForce
=
bruteForce
;
}
return
bestWinner
;
}
double
mostCommonShare
()
{
if
(
total
==
0
)
{
return
0.0
;
}
return
(
double
)
winners
.
get
(
mostCommonWinner
())
/
total
;
}
}
}
}
}
}
CityDoctorParent/CityDoctorValidation/src/test/java/de/hft/stuttgart/citydoctor2/checks/bht/BvhSyntheticScenarioCatalog.java
0 → 100644
View file @
35a777b9
package
de.hft.stuttgart.citydoctor2.checks.bht
;
import
java.util.List
;
import
de.hft.stuttgart.citydoctor2.datastructure.ConcretePolygon
;
import
de.hft.stuttgart.citydoctor2.datastructure.Geometry
;
import
de.hft.stuttgart.citydoctor2.datastructure.Lod
;
/**
* Central catalog of synthetic BVH exploration scenarios.
*
* The scenarios intentionally vary count, thinness, relative box volume, center
* spread, and city-like clustering so heuristic experiments can compare metric
* ranges without duplicating fixture lists in several tests.
*
* @author Numanoglu
*/
final
class
BvhSyntheticScenarioCatalog
{
private
static
final
double
EPSILON
=
0.001
;
private
BvhSyntheticScenarioCatalog
()
{
}
static
List
<
SsiScenario
>
ssiScenarios
()
{
return
List
.
of
(
new
SsiScenario
(
"sep-grid-small"
,
SyntheticSolidGeometryFactory
.
separatedBoxGrid
(
Lod
.
LOD2
,
6
,
6
)),
new
SsiScenario
(
"sep-grid-medium"
,
SyntheticSolidGeometryFactory
.
separatedBoxGrid
(
Lod
.
LOD2
,
12
,
12
)),
new
SsiScenario
(
"overlap-grid"
,
SyntheticSolidGeometryFactory
.
overlappingBoxGrid
(
Lod
.
LOD2
,
12
,
12
)),
new
SsiScenario
(
"dense-clusters-small"
,
SyntheticSolidGeometryFactory
.
denseBoxClusters
(
Lod
.
LOD2
,
4
,
20
)),
new
SsiScenario
(
"dense-clusters-large"
,
SyntheticSolidGeometryFactory
.
denseBoxClusters
(
Lod
.
LOD2
,
10
,
24
)),
new
SsiScenario
(
"long-thin-slabs-small"
,
SyntheticSolidGeometryFactory
.
longThinSlabs
(
Lod
.
LOD2
,
80
)),
new
SsiScenario
(
"long-thin-slabs-large"
,
SyntheticSolidGeometryFactory
.
longThinSlabs
(
Lod
.
LOD2
,
240
)),
new
SsiScenario
(
"flat-box-grid-small"
,
SyntheticSolidGeometryFactory
.
flatBoxGrid
(
Lod
.
LOD2
,
80
)),
new
SsiScenario
(
"flat-box-grid-large"
,
SyntheticSolidGeometryFactory
.
flatBoxGrid
(
Lod
.
LOD2
,
240
)),
new
SsiScenario
(
"thin-ratio-025"
,
SyntheticSolidGeometryFactory
.
thinRatioSweepBoxes
(
Lod
.
LOD2
,
120
,
0.25
)),
new
SsiScenario
(
"thin-ratio-050"
,
SyntheticSolidGeometryFactory
.
thinRatioSweepBoxes
(
Lod
.
LOD2
,
120
,
0.50
)),
new
SsiScenario
(
"thin-ratio-075"
,
SyntheticSolidGeometryFactory
.
thinRatioSweepBoxes
(
Lod
.
LOD2
,
120
,
0.75
)),
new
SsiScenario
(
"aspect-005"
,
SyntheticSolidGeometryFactory
.
aspectSweepBoxes
(
Lod
.
LOD2
,
120
,
5.0
)),
new
SsiScenario
(
"aspect-020"
,
SyntheticSolidGeometryFactory
.
aspectSweepBoxes
(
Lod
.
LOD2
,
120
,
20.0
)),
new
SsiScenario
(
"aspect-080"
,
SyntheticSolidGeometryFactory
.
aspectSweepBoxes
(
Lod
.
LOD2
,
120
,
80.0
)),
new
SsiScenario
(
"spread-tight"
,
SyntheticSolidGeometryFactory
.
spreadSweepBoxes
(
Lod
.
LOD2
,
120
,
3.5
)),
new
SsiScenario
(
"spread-wide"
,
SyntheticSolidGeometryFactory
.
spreadSweepBoxes
(
Lod
.
LOD2
,
120
,
20.0
)),
new
SsiScenario
(
"relvol-compact"
,
SyntheticSolidGeometryFactory
.
relativeVolumeSweepBoxes
(
Lod
.
LOD2
,
80
,
5.0
,
1.1
)),
new
SsiScenario
(
"relvol-sparse"
,
SyntheticSolidGeometryFactory
.
relativeVolumeSweepBoxes
(
Lod
.
LOD2
,
80
,
5.0
,
5.0
)),
new
SsiScenario
(
"citylike-mixed-small"
,
SyntheticCityGmlLikeGeometryFactory
.
mixedUrbanDistrict
(
Lod
.
LOD2
,
3
,
3
)),
new
SsiScenario
(
"citylike-mixed-large"
,
SyntheticCityGmlLikeGeometryFactory
.
mixedUrbanDistrict
(
Lod
.
LOD2
,
6
,
5
)),
new
SsiScenario
(
"citylike-courtyard"
,
SyntheticCityGmlLikeGeometryFactory
.
courtyardDistrict
(
Lod
.
LOD2
,
16
)),
new
SsiScenario
(
"citylike-roofs-small"
,
SyntheticCityGmlLikeGeometryFactory
.
variedRoofDistrict
(
Lod
.
LOD2
,
80
)),
new
SsiScenario
(
"citylike-roofs-large"
,
SyntheticCityGmlLikeGeometryFactory
.
variedRoofDistrict
(
Lod
.
LOD2
,
240
)));
}
static
List
<
NestedScenario
>
nestedScenarios
()
{
return
List
.
of
(
new
NestedScenario
(
"disjoint-small"
,
SyntheticNestedRingGeometryFactory
.
manyDisjointInnerRings
(
120
)),
new
NestedScenario
(
"disjoint-large"
,
SyntheticNestedRingGeometryFactory
.
manyDisjointInnerRings
(
1_000
)),
new
NestedScenario
(
"one-pair-small"
,
SyntheticNestedRingGeometryFactory
.
oneNestedPairAmongMany
(
120
)),
new
NestedScenario
(
"one-pair-large"
,
SyntheticNestedRingGeometryFactory
.
oneNestedPairAmongMany
(
1_000
)),
new
NestedScenario
(
"concentric-small"
,
SyntheticNestedRingGeometryFactory
.
concentricNestedRings
(
80
)),
new
NestedScenario
(
"concentric-large"
,
SyntheticNestedRingGeometryFactory
.
concentricNestedRings
(
300
)),
new
NestedScenario
(
"overlap-no-error"
,
SyntheticNestedRingGeometryFactory
.
overlappingAabbsButNotNested
(
1_000
)),
new
NestedScenario
(
"clustered-small"
,
SyntheticNestedRingGeometryFactory
.
clusteredInnerRings
(
8
,
40
)),
new
NestedScenario
(
"clustered-large"
,
SyntheticNestedRingGeometryFactory
.
clusteredInnerRings
(
20
,
80
)),
new
NestedScenario
(
"clustered-pair"
,
SyntheticNestedRingGeometryFactory
.
clusteredInnerRingsWithNestedPair
(
16
,
80
)),
new
NestedScenario
(
"aspect-005"
,
SyntheticNestedRingGeometryFactory
.
aspectSweepInnerRings
(
180
,
5.0
)),
new
NestedScenario
(
"aspect-020"
,
SyntheticNestedRingGeometryFactory
.
aspectSweepInnerRings
(
180
,
20.0
)),
new
NestedScenario
(
"aspect-080"
,
SyntheticNestedRingGeometryFactory
.
aspectSweepInnerRings
(
180
,
80.0
)),
new
NestedScenario
(
"spread-tight"
,
SyntheticNestedRingGeometryFactory
.
spreadSweepInnerRings
(
240
,
2.0
)),
new
NestedScenario
(
"spread-wide"
,
SyntheticNestedRingGeometryFactory
.
spreadSweepInnerRings
(
240
,
20.0
)));
}
static
List
<
RsiScenario
>
rsiScenarios
()
{
return
List
.
of
(
new
RsiScenario
(
"rsi-small"
,
SyntheticRingGeometryFactory
.
performanceRingGeometry
(
200
,
100
,
EPSILON
)),
new
RsiScenario
(
"rsi-medium"
,
SyntheticRingGeometryFactory
.
performanceRingGeometry
(
1_000
,
500
,
EPSILON
)),
new
RsiScenario
(
"rsi-large"
,
SyntheticRingGeometryFactory
.
performanceRingGeometry
(
5_000
,
2_000
,
EPSILON
)),
new
RsiScenario
(
"rsi-convex-heavy"
,
SyntheticRingGeometryFactory
.
performanceRingGeometry
(
6_000
,
200
,
EPSILON
)),
new
RsiScenario
(
"rsi-zigzag-heavy"
,
SyntheticRingGeometryFactory
.
performanceRingGeometry
(
400
,
4_000
,
EPSILON
)),
new
RsiScenario
(
"rsi-aspect-005"
,
SyntheticRingGeometryFactory
.
aspectSweepGeometry
(
240
,
5.0
)),
new
RsiScenario
(
"rsi-aspect-020"
,
SyntheticRingGeometryFactory
.
aspectSweepGeometry
(
240
,
20.0
)),
new
RsiScenario
(
"rsi-aspect-080"
,
SyntheticRingGeometryFactory
.
aspectSweepGeometry
(
240
,
80.0
)),
new
RsiScenario
(
"rsi-spread-tight"
,
SyntheticRingGeometryFactory
.
spreadSweepGeometry
(
240
,
4.0
)),
new
RsiScenario
(
"rsi-spread-wide"
,
SyntheticRingGeometryFactory
.
spreadSweepGeometry
(
240
,
30.0
)));
}
static
final
class
SsiScenario
{
final
String
name
;
final
Geometry
geometry
;
SsiScenario
(
String
name
,
Geometry
geometry
)
{
this
.
name
=
name
;
this
.
geometry
=
geometry
;
}
}
static
final
class
NestedScenario
{
final
String
name
;
final
ConcretePolygon
polygon
;
NestedScenario
(
String
name
,
ConcretePolygon
polygon
)
{
this
.
name
=
name
;
this
.
polygon
=
polygon
;
}
}
static
final
class
RsiScenario
{
final
String
name
;
final
Geometry
geometry
;
RsiScenario
(
String
name
,
Geometry
geometry
)
{
this
.
name
=
name
;
this
.
geometry
=
geometry
;
}
}
}
CityDoctorParent/CityDoctorValidation/src/test/java/de/hft/stuttgart/citydoctor2/checks/bht/BvhUsagePolicyTest.java
0 → 100644
View file @
35a777b9
package
de.hft.stuttgart.citydoctor2.checks.bht
;
import
static
org
.
junit
.
Assert
.
assertEquals
;
import
static
org
.
junit
.
Assert
.
assertFalse
;
import
static
org
.
junit
.
Assert
.
assertTrue
;
import
java.util.ArrayList
;
import
java.util.List
;
import
org.junit.jupiter.api.Test
;
import
de.hft.stuttgart.citydoctor2.checks.util.BvhUsagePolicy
;
import
de.hft.stuttgart.citydoctor2.datastructure.Geometry
;
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
;
public
class
BvhUsagePolicyTest
{
@Test
public
void
smallInputsStayWithoutTree
()
{
Geometry
smallSolid
=
SyntheticSolidGeometryFactory
.
separatedBoxGrid
(
Lod
.
LOD2
,
1
,
1
);
BvhUsagePolicy
.
BvhInputSummary
summary
=
summaryForPolygons
(
smallSolid
);
assertFalse
(
BvhUsagePolicy
.
shouldUseTree
(
BvhUsagePolicy
.
BvhCheckType
.
SOLID_SELF_INTERSECTION
,
summary
));
assertFalse
(
BvhUsagePolicy
.
shouldUseTree
(
BvhUsagePolicy
.
BvhCheckType
.
NESTED_RINGS
,
4
));
assertFalse
(
BvhUsagePolicy
.
shouldUseTree
(
BvhUsagePolicy
.
BvhCheckType
.
RING_SELF_INTERSECTION
,
16
));
}
@Test
public
void
largeDistributedInputsUseOctonaryObjectMean
()
{
Geometry
cityLikeGeometry
=
SyntheticCityGmlLikeGeometryFactory
.
variedRoofDistrict
(
Lod
.
LOD2
,
180
);
BvhUsagePolicy
.
BvhInputSummary
summary
=
summaryForPolygons
(
cityLikeGeometry
);
assertTrue
(
BvhUsagePolicy
.
shouldUseTree
(
BvhUsagePolicy
.
BvhCheckType
.
SOLID_SELF_INTERSECTION
,
summary
));
assertEquals
(
SplitStrategy
.
OCTONARY_OBJECT_MEAN
,
BvhUsagePolicy
.
chooseSplitStrategy
(
BvhUsagePolicy
.
BvhCheckType
.
SOLID_SELF_INTERSECTION
,
summary
));
}
@Test
public
void
flatInputsUseBinarySpatialMedian
()
{
Geometry
flatGeometry
=
SyntheticSolidGeometryFactory
.
flatBoxGrid
(
Lod
.
LOD2
,
200
);
BvhUsagePolicy
.
BvhInputSummary
summary
=
summaryForPolygons
(
flatGeometry
);
assertTrue
(
BvhUsagePolicy
.
shouldUseTree
(
BvhUsagePolicy
.
BvhCheckType
.
SOLID_SELF_INTERSECTION
,
summary
));
assertEquals
(
SplitStrategy
.
BINARY_SPATIAL_MEDIAN
,
BvhUsagePolicy
.
chooseSplitStrategy
(
BvhUsagePolicy
.
BvhCheckType
.
SOLID_SELF_INTERSECTION
,
summary
));
}
@Test
public
void
ringSelfIntersectionUsesBinarySpatialMedian
()
{
Geometry
ringGeometry
=
SyntheticRingGeometryFactory
.
performanceRingGeometry
(
200
,
100
,
0.001
);
BvhUsagePolicy
.
BvhInputSummary
summary
=
BvhUsagePolicy
.
BvhInputSummary
.
ofAabbs
(
edgeBoxes
(
ringGeometry
));
assertTrue
(
BvhUsagePolicy
.
shouldUseTree
(
BvhUsagePolicy
.
BvhCheckType
.
RING_SELF_INTERSECTION
,
summary
));
assertEquals
(
SplitStrategy
.
BINARY_SPATIAL_MEDIAN
,
BvhUsagePolicy
.
chooseSplitStrategy
(
BvhUsagePolicy
.
BvhCheckType
.
RING_SELF_INTERSECTION
,
summary
));
}
@Test
public
void
summaryExposesThinVolumeAndSpreadMetrics
()
{
Geometry
slabGeometry
=
SyntheticSolidGeometryFactory
.
longThinSlabs
(
Lod
.
LOD2
,
160
);
BvhUsagePolicy
.
BvhInputSummary
summary
=
summaryForPolygons
(
slabGeometry
);
assertTrue
(
"Expected long-thin fixture to contain thin boxes"
,
summary
.
getThinBoxRatio
()
>
0.0
);
assertTrue
(
"Relative box volume must be non-negative"
,
summary
.
getAverageRelativeBoxVolume
()
>=
0.0
);
assertTrue
(
"Expected distributed slab centers"
,
summary
.
getCenterSpreadRatio
()
>
0.0
);
}
private
static
BvhUsagePolicy
.
BvhInputSummary
summaryForPolygons
(
Geometry
geometry
)
{
return
BvhUsagePolicy
.
BvhInputSummary
.
ofElements
(
geometry
.
getPolygons
(),
polygon
->
AABB
.
of
(
polygon
.
getOriginal
()));
}
private
static
List
<
AABB
>
edgeBoxes
(
Geometry
geometry
)
{
List
<
AABB
>
boxes
=
new
ArrayList
<>();
for
(
Polygon
polygon
:
geometry
.
getPolygons
())
{
LinearRing
ring
=
polygon
.
getExteriorRing
();
List
<
Vertex
>
vertices
=
ring
.
getVertices
();
for
(
int
i
=
0
;
i
<
vertices
.
size
()
-
1
;
i
++)
{
boxes
.
add
(
AABB
.
of
(
vertices
.
get
(
i
),
vertices
.
get
(
i
+
1
),
0.001
));
}
}
return
boxes
;
}
}
CityDoctorParent/CityDoctorValidation/src/test/java/de/hft/stuttgart/citydoctor2/checks/bht/RealCityGmlBvhHeuristicExplorationTest.java
0 → 100644
View file @
35a777b9
package
de.hft.stuttgart.citydoctor2.checks.bht
;
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.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.List
;
import
java.util.Locale
;
import
java.util.Set
;
import
java.util.stream.Collectors
;
import
org.apache.logging.log4j.Level
;
import
org.apache.logging.log4j.LogManager
;
import
org.apache.logging.log4j.core.config.Configurator
;
import
org.junit.jupiter.api.Tag
;
import
org.junit.jupiter.api.Test
;
import
de.hft.stuttgart.citydoctor2.datastructure.CityDoctorModel
;
import
de.hft.stuttgart.citydoctor2.datastructure.CityObject
;
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.parser.CityGmlParser
;
import
de.hft.stuttgart.citydoctor2.parser.ParserConfiguration
;
import
de.hft.stuttgart.citydoctor2.utils.visitors.CityObjectCollector
;
/**
* Extracts broad-phase BVH input metrics and timing observations from real CityGML
* files.
*
* The test measures broad-phase costs and compares candidate strategy rules with
* the fastest measured BVH split strategy.
*
* @author Numanoglu
*/
@Tag
(
"performance"
)
public
class
RealCityGmlBvhHeuristicExplorationTest
{
private
static
final
Path
REAL_CITYGML_ROOT
=
Path
.
of
(
"src"
,
"test"
,
"resources"
,
"real-citygml"
);
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"
;
private
static
final
PrintStream
SILENT_OUT
=
new
PrintStream
(
OutputStream
.
nullOutputStream
());
@Test
public
void
printRealCityGmlMetricsAndBvhTiming
()
throws
Exception
{
assumeTrue
(
Files
.
isDirectory
(
REAL_CITYGML_ROOT
),
"Real CityGML test data directory is not available: "
+
REAL_CITYGML_ROOT
);
List
<
Path
>
datasetDirectories
=
listDatasetDirectories
(
REAL_CITYGML_ROOT
);
assumeTrue
(!
datasetDirectories
.
isEmpty
(),
"Expected real CityGML dataset directories in "
+
REAL_CITYGML_ROOT
);
assertFalse
(
"No real CityGML dataset directories found in "
+
REAL_CITYGML_ROOT
,
datasetDirectories
.
isEmpty
());
List
<
RealObservation
>
allObservations
=
new
ArrayList
<>();
for
(
Path
datasetDirectory
:
datasetDirectories
)
{
List
<
Path
>
files
=
listGmlFiles
(
datasetDirectory
);
if
(
files
.
isEmpty
())
{
continue
;
}
printHeader
(
datasetDirectory
.
getFileName
().
toString
());
for
(
Path
file
:
files
)
{
ModelElements
elements
=
runQuietly
(()
->
{
CityDoctorModel
model
=
CityGmlParser
.
parseCityGmlFile
(
file
.
toString
(),
new
ParserConfiguration
(
8
,
false
));
return
collectElements
(
model
);
});
printAndCollect
(
allObservations
,
datasetDirectory
,
file
,
"SSI"
,
measureBroadPhase
(
"SSI"
,
file
.
getFileName
()
+
":polygons"
,
elements
.
polygonBoxes
,
QueryMode
.
INTERSECTING
));
printAndCollect
(
allObservations
,
datasetDirectory
,
file
,
"NESTED"
,
measureBroadPhase
(
"NESTED"
,
file
.
getFileName
()
+
":innerRings"
,
elements
.
innerRingBoxes
,
QueryMode
.
CONTAINED
));
printAndCollect
(
allObservations
,
datasetDirectory
,
file
,
"RSI"
,
measureBroadPhase
(
"RSI"
,
file
.
getFileName
()
+
":edges"
,
elements
.
edgeBoxes
,
QueryMode
.
INTERSECTING
));
printSeparator
();
}
}
printAggregateSummary
(
allObservations
);
printFooter
();
}
private
static
void
printAndCollect
(
List
<
RealObservation
>
allObservations
,
Path
datasetDirectory
,
Path
file
,
String
checkName
,
BvhHeuristicTimingSupport
.
Observation
observation
)
{
printObservation
(
file
.
getFileName
().
toString
(),
observation
);
allObservations
.
add
(
new
RealObservation
(
datasetDirectory
.
getFileName
().
toString
(),
file
.
getFileName
().
toString
(),
checkName
,
observation
));
}
private
static
BvhHeuristicTimingSupport
.
Observation
measureBroadPhase
(
String
checkName
,
String
scenarioName
,
List
<
AABB
>
boxes
,
QueryMode
queryMode
)
{
BvhInputMetricsCollector
.
Metrics
metrics
=
BvhInputMetricsCollector
.
collectBoxesCheap
(
scenarioName
,
boxes
);
return
BvhHeuristicTimingSupport
.
measureBvhStrategies
(
checkName
,
scenarioName
,
metrics
,
boxes
.
size
(),
strategy
->
runBroadPhase
(
boxes
,
queryMode
,
strategy
));
}
private
static
int
runBroadPhase
(
List
<
AABB
>
boxes
,
QueryMode
queryMode
,
SplitStrategy
strategy
)
{
List
<
Integer
>
indices
=
new
ArrayList
<>(
boxes
.
size
());
for
(
int
i
=
0
;
i
<
boxes
.
size
();
i
++)
{
indices
.
add
(
i
);
}
BoundingVolumeHierarchyTree
<
Integer
>
tree
=
BoundingVolumeHierarchyTree
.
newWithStrategy
(
indices
,
boxes:
:
get
,
strategy
);
int
candidateCount
=
0
;
for
(
int
i
=
0
;
i
<
boxes
.
size
();
i
++)
{
AABB
box
=
boxes
.
get
(
i
);
if
(
queryMode
==
QueryMode
.
CONTAINED
)
{
candidateCount
+=
tree
.
getAllElementsContainedIn
(
box
).
size
();
}
else
{
candidateCount
+=
tree
.
getAllIntersectingElements
(
box
).
size
();
}
}
return
candidateCount
;
}
private
static
<
T
>
T
runQuietly
(
ThrowingSupplier
<
T
>
operation
)
throws
Exception
{
Level
originalRootLevel
=
LogManager
.
getRootLogger
().
getLevel
();
PrintStream
originalOut
=
System
.
out
;
PrintStream
originalErr
=
System
.
err
;
try
{
Configurator
.
setRootLevel
(
Level
.
WARN
);
Configurator
.
setAllLevels
(
"com.zaxxer.hikari"
,
Level
.
WARN
);
System
.
setOut
(
SILENT_OUT
);
System
.
setErr
(
SILENT_OUT
);
return
operation
.
get
();
}
finally
{
System
.
setOut
(
originalOut
);
System
.
setErr
(
originalErr
);
Configurator
.
setRootLevel
(
originalRootLevel
);
}
}
private
interface
ThrowingSupplier
<
T
>
{
T
get
()
throws
Exception
;
}
private
static
List
<
Path
>
listGmlFiles
(
Path
directory
)
throws
IOException
{
try
(
var
stream
=
Files
.
list
(
directory
))
{
return
stream
.
filter
(
path
->
path
.
getFileName
().
toString
().
toLowerCase
().
endsWith
(
".gml"
))
.
sorted
(
Comparator
.
comparing
(
path
->
path
.
getFileName
().
toString
()))
.
toList
();
}
}
private
static
List
<
Path
>
listDatasetDirectories
(
Path
root
)
throws
IOException
{
try
(
var
stream
=
Files
.
list
(
root
))
{
return
stream
.
filter
(
Files:
:
isDirectory
)
.
sorted
(
Comparator
.
comparing
(
path
->
path
.
getFileName
().
toString
()))
.
toList
();
}
}
private
static
ModelElements
collectElements
(
CityDoctorModel
model
)
{
ModelElements
elements
=
new
ModelElements
();
CityObjectCollector
collector
=
new
CityObjectCollector
();
model
.
createFeatureStream
()
.
filter
(
cityObject
->
cityObject
!=
null
)
.
forEach
(
cityObject
->
cityObject
.
accept
(
collector
));
for
(
CityObject
cityObject
:
collector
.
getCityObjects
())
{
collectFromCityObject
(
cityObject
,
elements
);
}
return
elements
;
}
private
static
void
collectFromCityObject
(
CityObject
cityObject
,
ModelElements
elements
)
{
for
(
Geometry
geometry
:
cityObject
.
getGeometries
())
{
if
(
geometry
==
null
)
{
continue
;
}
for
(
Polygon
polygon
:
geometry
.
getPolygons
())
{
if
(
polygon
==
null
)
{
continue
;
}
if
(!
elements
.
seenPolygons
.
add
(
polygon
))
{
continue
;
}
elements
.
polygons
.
add
(
polygon
);
elements
.
polygonBoxes
.
add
(
AABB
.
of
(
polygon
.
getOriginal
()));
elements
.
innerRings
.
addAll
(
polygon
.
getInnerRings
());
for
(
LinearRing
innerRing
:
polygon
.
getInnerRings
())
{
elements
.
innerRingBoxes
.
add
(
AABB
.
of
(
innerRing
));
}
collectEdgeBoxes
(
polygon
.
getExteriorRing
(),
elements
.
edgeBoxes
);
for
(
LinearRing
innerRing
:
polygon
.
getInnerRings
())
{
collectEdgeBoxes
(
innerRing
,
elements
.
edgeBoxes
);
}
}
}
}
private
static
void
collectEdgeBoxes
(
LinearRing
ring
,
List
<
AABB
>
edgeBoxes
)
{
if
(
ring
==
null
)
{
return
;
}
List
<
Vertex
>
vertices
=
ring
.
getVertices
();
for
(
int
i
=
0
;
i
<
vertices
.
size
()
-
1
;
i
++)
{
edgeBoxes
.
add
(
AABB
.
of
(
vertices
.
get
(
i
),
vertices
.
get
(
i
+
1
),
EDGE_BOX_PADDING
));
}
}
private
static
void
printHeader
(
String
datasetName
)
{
System
.
out
.
println
();
System
.
out
.
println
(
"[REAL-CITYGML-BVH-HEURISTIC-EXPLORATION] "
+
datasetName
);
String
header
=
String
.
format
(
TABLE_FORMAT
,
"file"
,
"check"
,
"n"
,
"thin"
,
"relVol"
,
"spread"
,
"aspect"
,
"candidatePred"
,
"winner"
,
"match"
,
"best ms"
,
"pred ms"
,
"loss ms"
);
printSeparator
(
header
.
length
());
System
.
out
.
println
(
header
);
printSeparator
(
header
.
length
());
}
private
static
void
printObservation
(
String
fileName
,
BvhHeuristicTimingSupport
.
Observation
observation
)
{
BvhInputMetricsCollector
.
Metrics
metrics
=
observation
.
metrics
;
System
.
out
.
println
(
String
.
format
(
TABLE_FORMAT
,
shortName
(
fileName
),
observation
.
checkName
,
metrics
.
elementCount
,
String
.
format
(
"%.3f"
,
metrics
.
thinBoxRate
),
String
.
format
(
"%.5f"
,
metrics
.
averageRelativeBoxVolume
),
String
.
format
(
"%.3f"
,
metrics
.
centerSpreadRatio
),
String
.
format
(
"%.1f"
,
metrics
.
averageAspectRatio
),
observation
.
candidatePrediction
,
winnerLabel
(
observation
),
observation
.
candidatePredictionMatchLabel
(),
bestMillisLabel
(
observation
),
predictedMillisLabel
(
observation
),
lossMillisLabel
(
observation
)));
}
private
static
String
winnerLabel
(
BvhHeuristicTimingSupport
.
Observation
observation
)
{
if
(
"OLD"
.
equals
(
observation
.
candidatePrediction
))
{
return
"OLD"
;
}
return
observation
.
fastestBvh
.
variant
;
}
private
static
String
bestMillisLabel
(
BvhHeuristicTimingSupport
.
Observation
observation
)
{
if
(
"OLD"
.
equals
(
observation
.
candidatePrediction
))
{
return
"-"
;
}
return
String
.
format
(
"%.3f"
,
observation
.
fastestBvh
.
averageMillis
());
}
private
static
String
predictedMillisLabel
(
BvhHeuristicTimingSupport
.
Observation
observation
)
{
if
(
observation
.
predictedBvh
==
null
)
{
return
"-"
;
}
return
String
.
format
(
"%.3f"
,
observation
.
predictedBvh
.
averageMillis
());
}
private
static
String
lossMillisLabel
(
BvhHeuristicTimingSupport
.
Observation
observation
)
{
if
(
observation
.
predictedBvh
==
null
)
{
return
"-"
;
}
double
lossMillis
=
observation
.
predictedBvh
.
averageMillis
()
-
observation
.
fastestBvh
.
averageMillis
();
return
String
.
format
(
"%.3f"
,
lossMillis
);
}
private
static
void
printAggregateSummary
(
List
<
RealObservation
>
observations
)
{
System
.
out
.
println
();
System
.
out
.
println
(
"[REAL-CITYGML-BVH-HEURISTIC-SUMMARY]"
);
System
.
out
.
println
(
String
.
format
(
"%-7s | %5s | %7s | %7s | %8s | %10s | %10s | %10s | %10s"
,
"check"
,
"cases"
,
"tested"
,
"match"
,
"accuracy"
,
"thin med"
,
"relVol med"
,
"spread med"
,
"aspect med"
));
System
.
out
.
println
(
"-----------------------------------------------------------------------------------------------"
);
for
(
String
checkName
:
List
.
of
(
"SSI"
,
"NESTED"
,
"RSI"
))
{
List
<
RealObservation
>
checkObservations
=
observations
.
stream
()
.
filter
(
observation
->
checkName
.
equals
(
observation
.
checkName
))
.
collect
(
Collectors
.
toList
());
printCheckSummary
(
checkName
,
checkObservations
);
}
System
.
out
.
println
();
System
.
out
.
println
(
"[REAL-CITYGML-BVH-METRIC-QUANTILES]"
);
System
.
out
.
println
(
String
.
format
(
"%-7s | %-7s | %10s | %10s | %10s | %10s | %10s"
,
"check"
,
"metric"
,
"min"
,
"p25"
,
"median"
,
"p75"
,
"max"
));
System
.
out
.
println
(
"--------------------------------------------------------------------------------"
);
for
(
String
checkName
:
List
.
of
(
"SSI"
,
"NESTED"
,
"RSI"
))
{
List
<
RealObservation
>
checkObservations
=
observations
.
stream
()
.
filter
(
observation
->
checkName
.
equals
(
observation
.
checkName
))
.
collect
(
Collectors
.
toList
());
printMetricQuantiles
(
checkName
,
"thin"
,
checkObservations
,
observation
->
observation
.
observation
.
metrics
.
thinBoxRate
);
printMetricQuantiles
(
checkName
,
"relVol"
,
checkObservations
,
observation
->
observation
.
observation
.
metrics
.
averageRelativeBoxVolume
);
printMetricQuantiles
(
checkName
,
"spread"
,
checkObservations
,
observation
->
observation
.
observation
.
metrics
.
centerSpreadRatio
);
printMetricQuantiles
(
checkName
,
"aspect"
,
checkObservations
,
observation
->
observation
.
observation
.
metrics
.
averageAspectRatio
);
}
}
private
static
void
printCheckSummary
(
String
checkName
,
List
<
RealObservation
>
observations
)
{
long
tested
=
observations
.
stream
()
.
filter
(
observation
->
!
"OLD"
.
equals
(
observation
.
observation
.
candidatePrediction
))
.
count
();
long
matches
=
observations
.
stream
()
.
filter
(
observation
->
"yes"
.
equals
(
observation
.
observation
.
candidatePredictionMatchLabel
()))
.
count
();
String
accuracy
=
tested
==
0L
?
"n/a"
:
String
.
format
(
"%.1f%%"
,
100.0
*
matches
/
tested
);
System
.
out
.
println
(
String
.
format
(
"%-7s | %5d | %7d | %7d | %8s | %10.3f | %10.5f | %10.3f | %10.1f"
,
checkName
,
observations
.
size
(),
tested
,
matches
,
accuracy
,
median
(
observations
,
observation
->
observation
.
observation
.
metrics
.
thinBoxRate
),
median
(
observations
,
observation
->
observation
.
observation
.
metrics
.
averageRelativeBoxVolume
),
median
(
observations
,
observation
->
observation
.
observation
.
metrics
.
centerSpreadRatio
),
median
(
observations
,
observation
->
observation
.
observation
.
metrics
.
averageAspectRatio
)));
}
private
static
void
printMetricQuantiles
(
String
checkName
,
String
metricName
,
List
<
RealObservation
>
observations
,
MetricValue
metricValue
)
{
double
[]
values
=
sortedValues
(
observations
,
metricValue
);
System
.
out
.
println
(
String
.
format
(
Locale
.
ROOT
,
"%-7s | %-7s | %10.5f | %10.5f | %10.5f | %10.5f | %10.5f"
,
checkName
,
metricName
,
quantile
(
values
,
0.0
),
quantile
(
values
,
0.25
),
quantile
(
values
,
0.5
),
quantile
(
values
,
0.75
),
quantile
(
values
,
1.0
)));
}
private
static
double
median
(
List
<
RealObservation
>
observations
,
MetricValue
metricValue
)
{
return
quantile
(
sortedValues
(
observations
,
metricValue
),
0.5
);
}
private
static
double
[]
sortedValues
(
List
<
RealObservation
>
observations
,
MetricValue
metricValue
)
{
return
observations
.
stream
()
.
mapToDouble
(
metricValue:
:
value
)
.
sorted
()
.
toArray
();
}
private
static
double
quantile
(
double
[]
sortedValues
,
double
probability
)
{
if
(
sortedValues
.
length
==
0
)
{
return
0.0
;
}
double
position
=
probability
*
(
sortedValues
.
length
-
1
);
int
lower
=
(
int
)
Math
.
floor
(
position
);
int
upper
=
(
int
)
Math
.
ceil
(
position
);
if
(
lower
==
upper
)
{
return
sortedValues
[
lower
];
}
double
fraction
=
position
-
lower
;
return
sortedValues
[
lower
]
*
(
1.0
-
fraction
)
+
sortedValues
[
upper
]
*
fraction
;
}
private
static
void
printFooter
()
{
System
.
out
.
println
(
"Broad-phase timing only; full validation checks and AUTO thresholds are not changed here."
);
System
.
out
.
println
();
}
private
static
void
printSeparator
()
{
printSeparator
(
String
.
format
(
TABLE_FORMAT
,
""
,
""
,
""
,
""
,
""
,
""
,
""
,
""
,
""
,
""
,
""
,
""
,
""
).
length
());
}
private
static
void
printSeparator
(
int
width
)
{
System
.
out
.
println
(
"-"
.
repeat
(
width
));
}
private
static
String
shortName
(
String
fileName
)
{
String
name
=
fileName
.
replace
(
".gml"
,
""
);
if
(
name
.
length
()
<=
FILE_COLUMN_WIDTH
)
{
return
name
;
}
return
name
.
substring
(
0
,
FILE_COLUMN_WIDTH
-
1
)
+
"."
;
}
private
enum
QueryMode
{
INTERSECTING
,
CONTAINED
}
private
static
final
class
ModelElements
{
final
Set
<
Polygon
>
seenPolygons
=
Collections
.
newSetFromMap
(
new
IdentityHashMap
<>());
final
List
<
Polygon
>
polygons
=
new
ArrayList
<>();
final
List
<
AABB
>
polygonBoxes
=
new
ArrayList
<>();
final
List
<
LinearRing
>
innerRings
=
new
ArrayList
<>();
final
List
<
AABB
>
innerRingBoxes
=
new
ArrayList
<>();
final
List
<
AABB
>
edgeBoxes
=
new
ArrayList
<>();
}
private
static
final
class
RealObservation
{
final
String
datasetName
;
final
String
fileName
;
final
String
checkName
;
final
BvhHeuristicTimingSupport
.
Observation
observation
;
RealObservation
(
String
datasetName
,
String
fileName
,
String
checkName
,
BvhHeuristicTimingSupport
.
Observation
observation
)
{
this
.
datasetName
=
datasetName
;
this
.
fileName
=
fileName
;
this
.
checkName
=
checkName
;
this
.
observation
=
observation
;
}
}
private
interface
MetricValue
{
double
value
(
RealObservation
observation
);
}
}
CityDoctorParent/CityDoctorValidation/src/test/java/de/hft/stuttgart/citydoctor2/checks/bht/SyntheticNestedRingGeometryFactory.java
View file @
35a777b9
...
@@ -139,8 +139,37 @@ final class SyntheticNestedRingGeometryFactory {
...
@@ -139,8 +139,37 @@ final class SyntheticNestedRingGeometryFactory {
return
polygon
;
return
polygon
;
}
}
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
;
ConcretePolygon
polygon
=
basePolygonWithExtent
(
extent
);
for
(
int
i
=
0
;
i
<
count
;
i
++)
{
double
x
=
5.0
+
(
i
%
columns
)
*
(
width
+
4.0
);
double
y
=
5.0
+
(
i
/
columns
)
*
5.0
;
polygon
.
addInteriorRing
(
rectangleRing
(
x
,
y
,
x
+
width
,
y
+
1.0
,
LinearRingType
.
INTERIOR
));
}
return
polygon
;
}
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
);
for
(
int
i
=
0
;
i
<
count
;
i
++)
{
double
centerX
=
5.0
+
(
i
%
columns
)
*
spacing
;
double
centerY
=
5.0
+
(
i
/
columns
)
*
spacing
;
polygon
.
addInteriorRing
(
squareRing
(
centerX
,
centerY
,
0.8
));
}
return
polygon
;
}
private
static
ConcretePolygon
basePolygon
(
int
expectedInnerRingCount
)
{
private
static
ConcretePolygon
basePolygon
(
int
expectedInnerRingCount
)
{
double
extent
=
Math
.
max
(
120.0
,
Math
.
ceil
(
Math
.
sqrt
(
Math
.
max
(
1
,
expectedInnerRingCount
)))
*
10.0
+
20.0
);
double
extent
=
Math
.
max
(
120.0
,
Math
.
ceil
(
Math
.
sqrt
(
Math
.
max
(
1
,
expectedInnerRingCount
)))
*
10.0
+
20.0
);
return
basePolygonWithExtent
(
extent
);
}
private
static
ConcretePolygon
basePolygonWithExtent
(
double
extent
)
{
ConcretePolygon
polygon
=
new
ConcretePolygon
();
ConcretePolygon
polygon
=
new
ConcretePolygon
();
polygon
.
setExteriorRing
(
rectangleRing
(
0.0
,
0.0
,
extent
,
extent
,
LinearRingType
.
EXTERIOR
));
polygon
.
setExteriorRing
(
rectangleRing
(
0.0
,
0.0
,
extent
,
extent
,
LinearRingType
.
EXTERIOR
));
return
polygon
;
return
polygon
;
...
...
CityDoctorParent/CityDoctorValidation/src/test/java/de/hft/stuttgart/citydoctor2/checks/bht/SyntheticRingGeometryFactory.java
View file @
35a777b9
...
@@ -68,6 +68,29 @@ final class SyntheticRingGeometryFactory {
...
@@ -68,6 +68,29 @@ final class SyntheticRingGeometryFactory {
return
geometry
;
return
geometry
;
}
}
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
++)
{
double
x
=
(
i
%
20
)
*
(
width
+
4.0
);
double
y
=
(
i
/
20
)
*
5.0
;
addRingPolygon
(
geometry
,
rectangle
(
x
,
y
,
width
,
1.0
));
}
geometry
.
updateEdgesAndVertices
();
return
geometry
;
}
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
;
double
y
=
(
i
/
20
)
*
spacing
;
addRingPolygon
(
geometry
,
rectangle
(
x
,
y
,
3.0
,
3.0
));
}
geometry
.
updateEdgesAndVertices
();
return
geometry
;
}
private
static
void
addRingPolygon
(
Geometry
geometry
,
double
[][]
coordinates
)
{
private
static
void
addRingPolygon
(
Geometry
geometry
,
double
[][]
coordinates
)
{
ConcretePolygon
polygon
=
new
ConcretePolygon
();
ConcretePolygon
polygon
=
new
ConcretePolygon
();
LinearRing
ring
=
new
LinearRing
(
LinearRingType
.
EXTERIOR
);
LinearRing
ring
=
new
LinearRing
(
LinearRingType
.
EXTERIOR
);
...
@@ -105,6 +128,16 @@ final class SyntheticRingGeometryFactory {
...
@@ -105,6 +128,16 @@ final class SyntheticRingGeometryFactory {
};
};
}
}
private
static
double
[][]
rectangle
(
double
x
,
double
y
,
double
width
,
double
height
)
{
return
new
double
[][]
{
{
x
,
y
,
0.0
},
{
x
+
width
,
y
,
0.0
},
{
x
+
width
,
y
+
height
,
0.0
},
{
x
,
y
+
height
,
0.0
},
{
x
,
y
,
0.0
}
};
}
private
static
double
[][]
bowTie
()
{
private
static
double
[][]
bowTie
()
{
return
new
double
[][]
{
return
new
double
[][]
{
{
20.0
,
0.0
,
0.0
},
{
20.0
,
0.0
,
0.0
},
...
...
CityDoctorParent/CityDoctorValidation/src/test/java/de/hft/stuttgart/citydoctor2/checks/bht/SyntheticSolidGeometryFactory.java
View file @
35a777b9
...
@@ -91,6 +91,62 @@ final class SyntheticSolidGeometryFactory {
...
@@ -91,6 +91,62 @@ final class SyntheticSolidGeometryFactory {
return
geometry
;
return
geometry
;
}
}
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
);
for
(
int
i
=
0
;
i
<
count
;
i
++)
{
double
x
=
(
i
%
columns
)
*
50.0
;
double
y
=
(
i
/
columns
)
*
8.0
;
if
(
i
<
thinCount
)
{
addBox
(
geometry
,
x
,
y
,
0.0
,
40.0
,
0.35
,
1.0
);
}
else
{
addBox
(
geometry
,
x
,
y
,
0.0
,
4.0
,
4.0
,
4.0
);
}
}
geometry
.
updateEdgesAndVertices
();
return
geometry
;
}
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
);
double
spacingX
=
width
+
3.0
;
for
(
int
i
=
0
;
i
<
count
;
i
++)
{
double
x
=
(
i
%
columns
)
*
spacingX
;
double
y
=
(
i
/
columns
)
*
4.0
;
addBox
(
geometry
,
x
,
y
,
0.0
,
width
,
1.0
,
1.0
);
}
geometry
.
updateEdgesAndVertices
();
return
geometry
;
}
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
++)
{
double
x
=
(
i
%
columns
)
*
spacing
;
double
y
=
(
i
/
columns
)
*
spacing
;
addBox
(
geometry
,
x
,
y
,
0.0
,
3.0
,
3.0
,
3.0
);
}
geometry
.
updateEdgesAndVertices
();
return
geometry
;
}
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
;
for
(
int
i
=
0
;
i
<
count
;
i
++)
{
double
x
=
(
i
%
columns
)
*
spacing
;
double
y
=
(
i
/
columns
)
*
spacing
;
addBox
(
geometry
,
x
,
y
,
0.0
,
boxSize
,
boxSize
,
boxSize
);
}
geometry
.
updateEdgesAndVertices
();
return
geometry
;
}
private
static
Geometry
newSolid
(
Lod
lod
)
{
private
static
Geometry
newSolid
(
Lod
lod
)
{
return
new
Geometry
(
GeometryType
.
SOLID
,
lod
,
Orientation
.
OUTWARD
);
return
new
Geometry
(
GeometryType
.
SOLID
,
lod
,
Orientation
.
OUTWARD
);
}
}
...
...
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