Skip to content

Commit 8c8817d

Browse files
authored
perf(perception): improved speed of Wall Occlusion (#443)
* achieved by a) using a more sensible pre-selection of walls * b) not using the 3D intersections eval from VectorUtils --------- Signed-off-by: Moritz Schweppenhäuser <[email protected]>
1 parent ef7ba5b commit 8c8817d

File tree

4 files changed

+80
-16
lines changed

4 files changed

+80
-16
lines changed

fed/mosaic-application/src/main/java/org/eclipse/mosaic/fed/application/ambassador/simulation/perception/errormodels/WallOcclusion.java

+2-8
Original file line numberDiff line numberDiff line change
@@ -34,8 +34,6 @@
3434
*/
3535
public class WallOcclusion implements PerceptionModifier {
3636

37-
private final Vector3d intersectionResult = new Vector3d();
38-
3937
@Override
4038
public <T extends SpatialObject> List<T> apply(PerceptionModuleOwner owner, List<T> spatialObjects) {
4139
if (spatialObjects.isEmpty()) {
@@ -46,6 +44,7 @@ public <T extends SpatialObject> List<T> apply(PerceptionModuleOwner owner, List
4644
if (walls.isEmpty()) {
4745
return spatialObjects;
4846
}
47+
Vector3d ownerPosition = owner.getVehicleData().getProjectedPosition().toVector3d();
4948
final List<T> result = new ArrayList<>();
5049
for (T spatialObject : spatialObjects) {
5150
List<Vector3d> pointsToEvaluate = spatialObject.getBoundingBox().getAllCorners();
@@ -56,11 +55,7 @@ public <T extends SpatialObject> List<T> apply(PerceptionModuleOwner owner, List
5655
boolean pointOccluded = false;
5756
for (Edge<Vector3d> wall : walls) {
5857
// SpatialObjects with PointBoundingBoxes won't occlude anything, as they have no edges defined
59-
boolean isOccluded = VectorUtils.computeXZEdgeIntersectionPoint(
60-
owner.getVehicleData().getProjectedPosition().toVector3d(),
61-
point, wall.a, wall.b, intersectionResult
62-
);
63-
if (isOccluded) {
58+
if (VectorUtils.doesXZIntersect(ownerPosition, point, wall.a, wall.b)) {
6459
pointOccluded = true;
6560
break;
6661
}
@@ -76,5 +71,4 @@ public <T extends SpatialObject> List<T> apply(PerceptionModuleOwner owner, List
7671
}
7772
return result;
7873
}
79-
8074
}

fed/mosaic-application/src/main/java/org/eclipse/mosaic/fed/application/ambassador/simulation/perception/index/providers/WallTree.java

+11-2
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,10 @@
2929
import java.util.List;
3030

3131
public class WallTree extends WallIndex {
32-
32+
/**
33+
* The longest wall, relevant for tree setup, so that all walls will be included. [m]
34+
*/
35+
private double maxWallLength = 50;
3336
private final int bucketSize;
3437

3538
private KdTree<Edge<Vector3d>> wallTree;
@@ -42,22 +45,28 @@ public WallTree(int bucketSize) {
4245
@Override
4346
public void initialize() {
4447
List<Edge<Vector3d>> walls = new ArrayList<>();
48+
double maxWallLength = 0;
4549
for (Building building : super.getDatabase().getBuildings()) {
4650
for (Wall wall : building.getWalls()) {
4751
walls.add(new Edge<>(
4852
wall.getFromCorner().getCartesianPosition().toVector3d(),
4953
wall.getToCorner().getCartesianPosition().toVector3d()
5054
));
55+
if (wall.getLength() > maxWallLength) {
56+
maxWallLength = wall.getLength();
57+
}
5158
}
5259
}
60+
this.maxWallLength = maxWallLength;
5361
wallTree = new KdTree<>(new SpatialItemAdapter.EdgeAdapter<>(), walls, bucketSize);
5462
wallTraverser = new org.eclipse.mosaic.lib.database.spatial.Edge.InRadius<>();
5563
}
5664

5765
@Override
5866
public Collection<Edge<Vector3d>> getSurroundingWalls(PerceptionModel perceptionModel) {
67+
// overestimating the initial list of walls by extending max bounding box radius with maximal wall length
5968
wallTraverser.setup(perceptionModel.getBoundingBox().center,
60-
perceptionModel.getBoundingBox().center.distanceSqrTo(perceptionModel.getBoundingBox().min)); // overestimating distance
69+
perceptionModel.getBoundingBox().center.distanceTo(perceptionModel.getBoundingBox().min) + maxWallLength);
6170
wallTraverser.traverse(wallTree);
6271
return wallTraverser.getResult();
6372
}

fed/mosaic-application/src/test/java/org/eclipse/mosaic/fed/application/ambassador/simulation/perception/PerceptionModifierTest.java

+4-6
Original file line numberDiff line numberDiff line change
@@ -192,7 +192,7 @@ public void testHeadingModifier() {
192192

193193
// ASSERT if headings differ from ground truth
194194
for (VehicleObject realVehicle : getAllVehicles()) {
195-
for (VehicleObject perceivedVehicle: perceivedVehicles) {
195+
for (VehicleObject perceivedVehicle : perceivedVehicles) {
196196
if (realVehicle.getId().equals(perceivedVehicle.getId())) {
197197
assertNotEquals(realVehicle.getHeading(), perceivedVehicle.getHeading(), 0.0001);
198198
// when adjusting heading the position should change too, since it currently points to the front bumper of the vehicle
@@ -204,7 +204,7 @@ public void testHeadingModifier() {
204204

205205
@Test
206206
public void testDimensionsModifier() {
207-
DimensionsModifier dimensionsModifier = new DimensionsModifier(rng, 1.0, 0.0, 0.0);
207+
DimensionsModifier dimensionsModifier = new DimensionsModifier(rng, 1.0, 0.0, 0.0);
208208
simplePerceptionModule.enable(
209209
new SimplePerceptionConfiguration.Builder(VIEWING_ANGLE, VIEWING_RANGE).addModifier(dimensionsModifier).build()
210210
);
@@ -217,7 +217,7 @@ public void testDimensionsModifier() {
217217

218218
// ASSERT if headings differ from ground truth
219219
for (VehicleObject realVehicle : getAllVehicles()) {
220-
for (VehicleObject perceivedVehicle: perceivedVehicles) {
220+
for (VehicleObject perceivedVehicle : perceivedVehicles) {
221221
if (realVehicle.getId().equals(perceivedVehicle.getId())) {
222222
assertNotEquals(realVehicle.getLength(), perceivedVehicle.getLength(), 0.0001);
223223
// when adjusting length the position should change too, since it currently points to the front bumper of the vehicle
@@ -270,9 +270,7 @@ public void testIndexedObjectsNotChanged() {
270270
.stream().collect(Collectors.toMap(VehicleObject::getId, v -> new Vector3d(v.getPosition())));
271271

272272
// ASSERT that all positions in the index are still the same as before applying modifier
273-
allVehiclesInIndexPre.forEach((id, pos) -> {
274-
assertTrue(pos.isFuzzyEqual(allVehiclesInIndexPost.get(id)));
275-
});
273+
allVehiclesInIndexPre.forEach((id, pos) -> assertTrue(pos.isFuzzyEqual(allVehiclesInIndexPost.get(id))));
276274

277275
// ASSERT that all modified positions differ from the positions before (or after) applying the modifier
278276
for (VehicleObject object : perceivedAndAlteredObjects) {

lib/mosaic-geomath/src/main/java/org/eclipse/mosaic/lib/math/VectorUtils.java

+63
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,69 @@ public static Vector3d nearestPointOnLine(Vector3d point, Vector3d linePt1, Vect
6969
return result;
7070
}
7171

72+
/**
73+
* Enum to differentiate between orientations of a point relative to a line segment.
74+
*/
75+
private enum Orientation2d {
76+
COLLINEAR, CLOCKWISE, COUNTERCLOCKWISE
77+
}
78+
79+
/**
80+
* Function to check if two line segments (a1, b1) and (a2, b2) intersect.
81+
*
82+
* @param a1 First point of first line segment
83+
* @param b1 Second point of first line segment
84+
* @param a2 First point of second line segment
85+
* @param b2 Second point of second line segment
86+
* @return {@code true} if the two line segments intersect, else {@code false}
87+
*/
88+
public static boolean doesXZIntersect(Vector3d a1, Vector3d b1, Vector3d a2, Vector3d b2) {
89+
Orientation2d o1 = isCollinear(a1.x, a1.z, b1.x, b1.z, a2.x, a2.z);
90+
Orientation2d o2 = isCollinear(a1.x, a1.z, b1.x, b1.z, b2.x, b2.z);
91+
Orientation2d o3 = isCollinear(a2.x, a2.z, b2.x, b2.z, a1.x, a1.z);
92+
Orientation2d o4 = isCollinear(a2.x, a2.z, b2.x, b2.z, b1.x, b1.z);
93+
94+
// General case
95+
if (o1 != o2 && o3 != o4) {
96+
return true;
97+
}
98+
// Special cases
99+
if (o1 == Orientation2d.COLLINEAR && isOnSegment(a1.x, a1.z, a2.x, a2.z, b1.x, b1.z)) {
100+
return true;
101+
}
102+
if (o2 == Orientation2d.COLLINEAR && isOnSegment(a1.x, a1.z, b2.x, b2.z, b1.x, b1.z)) {
103+
return true;
104+
}
105+
if (o3 == Orientation2d.COLLINEAR && isOnSegment(a2.x, a2.z, a1.x, a1.z, b2.x, b2.z)) {
106+
return true;
107+
}
108+
if (o4 == Orientation2d.COLLINEAR && isOnSegment(a2.x, a2.z, b1.x, b1.z, b2.x, b2.z)) {
109+
return true;
110+
}
111+
return false; // Doesn't fall in any of the above cases
112+
}
113+
114+
/**
115+
* Function to calculate the orientation of the triplet (ax, ay), (bx,bqy), (rx, ry).
116+
*/
117+
private static Orientation2d isCollinear(double ax, double ay, double bx, double by, double rx, double ry) {
118+
double val = (by - ay) * (rx - bx) - (bx - ax) * (ry - by);
119+
if (Math.abs(val) <= MathUtils.EPSILON_D) { // Collinear
120+
return Orientation2d.COLLINEAR;
121+
}
122+
return (val > 0) ? Orientation2d.CLOCKWISE : Orientation2d.COUNTERCLOCKWISE; // Clockwise or Counterclockwise
123+
}
124+
125+
/**
126+
* Function to check if point (ax, ay) lies on a segment (bx, by) to (rx, ry).
127+
*/
128+
private static boolean isOnSegment(double ax, double ay, double bx, double by, double rx, double ry) {
129+
return bx <= Math.max(ax, rx)
130+
&& bx >= Math.min(ax, rx)
131+
&& by <= Math.max(ay, ry)
132+
&& by >= Math.min(ay, ry);
133+
}
134+
72135
/**
73136
* Returns the intersection point of two lines in the XZ plane. Notice that, although 3D
74137
* coordinates are used, only the X and Z components are considered. The Y component of the

0 commit comments

Comments
 (0)