Skip to content
This repository was archived by the owner on Aug 8, 2023. It is now read-only.

Commit 65d2f7e

Browse files
committed
[core] Introduce ProjectedCollisionBox type
ProjectedCollisionBox encapsulates geometry of the projected collision box, it is using union and thus provides memory save - 12 bytes per collision box instance.
1 parent 55581b6 commit 65d2f7e

5 files changed

Lines changed: 83 additions & 60 deletions

File tree

src/mbgl/text/collision_feature.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -155,7 +155,7 @@ void CollisionFeature::bboxifyLabel(const GeometryCoordinates& line, GeometryCoo
155155
0 :
156156
(boxDistanceToAnchor - firstBoxOffset) * 0.8;
157157

158-
boxes.emplace_back(boxAnchor, -boxSize / 2, -boxSize / 2, boxSize / 2, boxSize / 2, paddedAnchorDistance, boxSize / 2);
158+
boxes.emplace_back(boxAnchor, -boxSize / 2, -boxSize / 2, boxSize / 2, boxSize / 2, paddedAnchorDistance);
159159
}
160160
}
161161

src/mbgl/text/collision_feature.hpp

Lines changed: 41 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,46 @@
99

1010
namespace mbgl {
1111

12+
class ProjectedCollisionBox {
13+
public:
14+
enum class Type : char {
15+
Unknown,
16+
Box,
17+
Circle
18+
};
19+
20+
ProjectedCollisionBox() = default;
21+
ProjectedCollisionBox(float x1, float y1, float x2, float y2) : geometry(x1, y1, x2, y2), type(Type::Box) {}
22+
ProjectedCollisionBox(float x, float y, float r) : geometry(x, y, r), type(Type::Circle) {}
23+
24+
const mapbox::geometry::box<float>& box() const {
25+
assert(isBox());
26+
return geometry.box;
27+
}
28+
29+
const geometry::circle<float>& circle() const {
30+
assert(isCircle());
31+
return geometry.circle;
32+
}
33+
34+
bool isCircle() const { return type == Type::Circle; }
35+
bool isBox() const { return type == Type::Box; }
36+
37+
private:
38+
union Geometry {
39+
Geometry(float x1, float y1, float x2, float y2) : box({x1, y1}, {x2, y2}) {}
40+
Geometry(float x, float y, float r) : circle({x, y}, r) {}
41+
Geometry() {}
42+
mapbox::geometry::box<float> box;
43+
geometry::circle<float> circle;
44+
} geometry;
45+
Type type = Type::Unknown;
46+
};
47+
1248
class CollisionBox {
1349
public:
14-
CollisionBox(Point<float> _anchor, float _x1, float _y1, float _x2, float _y2, float _signedDistanceFromAnchor = 0, float _radius = 0) :
15-
anchor(std::move(_anchor)), x1(_x1), y1(_y1), x2(_x2), y2(_y2), used(true), signedDistanceFromAnchor(_signedDistanceFromAnchor), radius(_radius) {}
50+
CollisionBox(Point<float> _anchor, float _x1, float _y1, float _x2, float _y2, float _signedDistanceFromAnchor = 0) :
51+
anchor(std::move(_anchor)), x1(_x1), y1(_y1), x2(_x2), y2(_y2), signedDistanceFromAnchor(_signedDistanceFromAnchor) {}
1652

1753
// the box is centered around the anchor point
1854
Point<float> anchor;
@@ -27,19 +63,10 @@ class CollisionBox {
2763
float x2;
2864
float y2;
2965

30-
// Projected box geometry: generated/updated at placement time
31-
float px1;
32-
float py1;
33-
float px2;
34-
float py2;
35-
36-
// Projected circle geometry: generated/updated at placement time
37-
float px;
38-
float py;
39-
bool used;
40-
4166
float signedDistanceFromAnchor;
42-
float radius;
67+
68+
// generated/updated at placement time
69+
ProjectedCollisionBox projected;
4370
};
4471

4572
class CollisionFeature {

src/mbgl/text/collision_index.cpp

Lines changed: 37 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -55,12 +55,12 @@ float CollisionIndex::approximateTileDistance(const TileDistance& tileDistance,
5555
(incidenceStretch - 1) * lastSegmentTile * std::abs(std::sin(lastSegmentAngle));
5656
}
5757

58-
bool CollisionIndex::isOffscreen(const CollisionBox& box) const {
59-
return box.px2 < viewportPadding || box.px1 >= screenRightBoundary || box.py2 < viewportPadding || box.py1 >= screenBottomBoundary;
58+
bool CollisionIndex::isOffscreen(float x1, float y1, float x2, float y2) const {
59+
return x2 < viewportPadding || x1 >= screenRightBoundary || y2 < viewportPadding || y1 >= screenBottomBoundary;
6060
}
6161

62-
bool CollisionIndex::isInsideGrid(const CollisionBox& box) const {
63-
return box.px2 >= 0 && box.px1 < gridRightBoundary && box.py2 >= 0 && box.py1 < gridBottomBoundary;
62+
bool CollisionIndex::isInsideGrid(float x1, float y1, float x2, float y2) const {
63+
return x2 >= 0 && x1 < gridRightBoundary && y2 >= 0 && y1 < gridBottomBoundary;
6464
}
6565

6666
CollisionTileBoundaries CollisionIndex::projectTileBoundaries(const mat4& posMatrix) const {
@@ -71,11 +71,11 @@ CollisionTileBoundaries CollisionIndex::projectTileBoundaries(const mat4& posMat
7171

7272
}
7373

74-
bool CollisionIndex::isInsideTile(const CollisionBox& box, const CollisionTileBoundaries& tileBoundaries) const {
74+
bool CollisionIndex::isInsideTile(float x1, float y1, float x2, float y2, const CollisionTileBoundaries& tileBoundaries) const {
7575
// This check is only well defined when the tile boundaries are axis-aligned
7676
// We are relying on it only being used in MapMode::Tile, where that is always the case
7777

78-
return box.px1 >= tileBoundaries[0] && box.py1 >= tileBoundaries[1] && box.px2 < tileBoundaries[2] && box.py2 < tileBoundaries[3];
78+
return x1 >= tileBoundaries[0] && y1 >= tileBoundaries[1] && x2 < tileBoundaries[2] && y2 < tileBoundaries[3];
7979
}
8080

8181

@@ -96,19 +96,19 @@ std::pair<bool,bool> CollisionIndex::placeFeature(CollisionFeature& feature,
9696
CollisionBox& box = feature.boxes.front();
9797
const auto projectedPoint = projectAndGetPerspectiveRatio(posMatrix, box.anchor);
9898
const float tileToViewport = textPixelRatio * projectedPoint.second;
99-
box.px1 = (box.x1 + shift.x) * tileToViewport + projectedPoint.first.x;
100-
box.py1 = (box.y1 + shift.y) * tileToViewport + projectedPoint.first.y;
101-
box.px2 = (box.x2 + shift.x) * tileToViewport + projectedPoint.first.x;
102-
box.py2 = (box.y2 + shift.y) * tileToViewport + projectedPoint.first.y;
103-
104-
105-
if ((avoidEdges && !isInsideTile(box, *avoidEdges)) ||
106-
!isInsideGrid(box) ||
107-
(!allowOverlap && collisionGrid.hitTest({{ box.px1, box.py1 }, { box.px2, box.py2 }}, collisionGroupPredicate))) {
99+
float px1 = (box.x1 + shift.x) * tileToViewport + projectedPoint.first.x;
100+
float py1 = (box.y1 + shift.y) * tileToViewport + projectedPoint.first.y;
101+
float px2 = (box.x2 + shift.x) * tileToViewport + projectedPoint.first.x;
102+
float py2 = (box.y2 + shift.y) * tileToViewport + projectedPoint.first.y;
103+
box.projected = ProjectedCollisionBox{ px1, py1, px2, py2 };
104+
105+
if ((avoidEdges && !isInsideTile(px1, py1, px2, py2, *avoidEdges)) ||
106+
!isInsideGrid(px1, py1, px2, py2) ||
107+
(!allowOverlap && collisionGrid.hitTest(box.projected.box(), collisionGroupPredicate))) {
108108
return { false, false };
109109
}
110110

111-
return {true, isOffscreen(box)};
111+
return {true, isOffscreen(px1, py1, px2, py2)};
112112
} else {
113113
return placeLineFeature(feature, posMatrix, labelPlaneMatrix, textPixelRatio, symbol, scale, fontSize, allowOverlap, pitchWithMap, collisionDebug, avoidEdges, collisionGroupPredicate);
114114
}
@@ -126,7 +126,7 @@ std::pair<bool,bool> CollisionIndex::placeLineFeature(CollisionFeature& feature,
126126
const bool collisionDebug,
127127
const optional<CollisionTileBoundaries>& avoidEdges,
128128
const optional<std::function<bool(const IndexedSubfeature&)>> collisionGroupPredicate) {
129-
129+
assert(feature.alongLine);
130130
const auto tileUnitAnchorPoint = symbol.anchorPoint;
131131
const auto projectedAnchor = projectAnchor(posMatrix, tileUnitAnchorPoint);
132132

@@ -173,7 +173,6 @@ std::pair<bool,bool> CollisionIndex::placeLineFeature(CollisionFeature& feature,
173173
// The label either doesn't fit on its line or we
174174
// don't need to use this circle because the label
175175
// doesn't extend this far. Either way, mark the circle unused.
176-
circle.used = false;
177176
previousCirclePlaced = false;
178177
continue;
179178
}
@@ -184,9 +183,10 @@ std::pair<bool,bool> CollisionIndex::placeLineFeature(CollisionFeature& feature,
184183

185184
if (previousCirclePlaced) {
186185
const CollisionBox& previousCircle = feature.boxes[i - 1];
187-
assert(previousCircle.used);
188-
const float dx = projectedPoint.x - previousCircle.px;
189-
const float dy = projectedPoint.y - previousCircle.py;
186+
assert(previousCircle.projected.isCircle());
187+
const auto& previousCenter = previousCircle.projected.circle().center;
188+
const float dx = projectedPoint.x - previousCenter.x;
189+
const float dy = projectedPoint.y - previousCenter.y;
190190
// The circle edges touch when the distance between their centers is 2x the radius
191191
// When the distance is 1x the radius, they're doubled up, and we could remove
192192
// every other circle while keeping them all in touch.
@@ -204,7 +204,6 @@ std::pair<bool,bool> CollisionIndex::placeLineFeature(CollisionFeature& feature,
204204
// Hide significantly overlapping circles, unless this is the last one we can
205205
// use, in which case we want to keep it in place even if it's tightly packed
206206
// with the one before it.
207-
circle.used = false;
208207
previousCirclePlaced = false;
209208
continue;
210209
}
@@ -213,22 +212,18 @@ std::pair<bool,bool> CollisionIndex::placeLineFeature(CollisionFeature& feature,
213212
}
214213

215214
previousCirclePlaced = true;
216-
circle.px1 = projectedPoint.x - radius;
217-
circle.px2 = projectedPoint.x + radius;
218-
circle.py1 = projectedPoint.y - radius;
219-
circle.py2 = projectedPoint.y + radius;
220-
221-
circle.used = true;
222-
223-
circle.px = projectedPoint.x;
224-
circle.py = projectedPoint.y;
225-
circle.radius = radius;
215+
float px1 = projectedPoint.x - radius;
216+
float px2 = projectedPoint.x + radius;
217+
float py1 = projectedPoint.y - radius;
218+
float py2 = projectedPoint.y + radius;
219+
220+
circle.projected = ProjectedCollisionBox{projectedPoint.x, projectedPoint.y, radius};
226221

227-
entirelyOffscreen &= isOffscreen(circle);
228-
inGrid |= isInsideGrid(circle);
222+
entirelyOffscreen &= isOffscreen(px1, py1, px2, py2);
223+
inGrid |= isInsideGrid(px1, py1, px2, py2);
229224

230-
if ((avoidEdges && !isInsideTile(circle, *avoidEdges)) ||
231-
(!allowOverlap && collisionGrid.hitTest({{circle.px, circle.py}, circle.radius}, collisionGroupPredicate))) {
225+
if ((avoidEdges && !isInsideTile(px1, py1, px2, py2, *avoidEdges)) ||
226+
(!allowOverlap && collisionGrid.hitTest(circle.projected.circle(), collisionGroupPredicate))) {
232227
if (!collisionDebug) {
233228
return {false, false};
234229
} else {
@@ -246,34 +241,35 @@ std::pair<bool,bool> CollisionIndex::placeLineFeature(CollisionFeature& feature,
246241
void CollisionIndex::insertFeature(CollisionFeature& feature, bool ignorePlacement, uint32_t bucketInstanceId, uint16_t collisionGroupId) {
247242
if (feature.alongLine) {
248243
for (auto& circle : feature.boxes) {
249-
if (!circle.used) {
244+
if (!circle.projected.isCircle()) {
250245
continue;
251246
}
252247

253248
if (ignorePlacement) {
254249
ignoredGrid.insert(
255250
IndexedSubfeature(feature.indexedFeature, bucketInstanceId, collisionGroupId),
256-
{{ circle.px, circle.py }, circle.radius}
251+
circle.projected.circle()
257252
);
258253
} else {
259254
collisionGrid.insert(
260255
IndexedSubfeature(feature.indexedFeature, bucketInstanceId, collisionGroupId),
261-
{{ circle.px, circle.py }, circle.radius}
256+
circle.projected.circle()
262257
);
263258
}
264259
}
265260
} else {
266261
assert(feature.boxes.size() == 1);
267262
auto& box = feature.boxes[0];
263+
assert(box.projected.isBox());
268264
if (ignorePlacement) {
269265
ignoredGrid.insert(
270266
IndexedSubfeature(feature.indexedFeature, bucketInstanceId, collisionGroupId),
271-
{{ box.px1, box.py1 }, { box.px2, box.py2 }}
267+
box.projected.box()
272268
);
273269
} else {
274270
collisionGrid.insert(
275271
IndexedSubfeature(feature.indexedFeature, bucketInstanceId, collisionGroupId),
276-
{{ box.px1, box.py1 }, { box.px2, box.py2 }}
272+
box.projected.box()
277273
);
278274
}
279275
}

src/mbgl/text/collision_index.hpp

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -45,9 +45,9 @@ class CollisionIndex {
4545
const TransformState& getTransformState() const { return transformState; }
4646

4747
private:
48-
bool isOffscreen(const CollisionBox&) const;
49-
bool isInsideGrid(const CollisionBox&) const;
50-
bool isInsideTile(const CollisionBox&, const CollisionTileBoundaries& tileBoundaries) const;
48+
bool isOffscreen(float x1, float y1, float x2, float y2) const;
49+
bool isInsideGrid(float x1, float y1, float x2, float y2) const;
50+
bool isInsideTile(float x1, float y1, float x2, float y2, const CollisionTileBoundaries& tileBoundaries) const;
5151

5252
std::pair<bool,bool> placeLineFeature(CollisionFeature& feature,
5353
const mat4& posMatrix,

src/mbgl/text/placement.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -628,7 +628,7 @@ void Placement::updateBucketOpacities(SymbolBucket& bucket, const TransformState
628628
return;
629629
}
630630
for (const CollisionBox& box : feature.boxes) {
631-
const auto& dynamicVertex = CollisionBoxProgram::dynamicVertex(placed, !box.used, {});
631+
const auto& dynamicVertex = CollisionBoxProgram::dynamicVertex(placed, !box.projected.isCircle(), {});
632632
bucket.collisionCircle->dynamicVertices.extend(4, dynamicVertex);
633633
}
634634
};

0 commit comments

Comments
 (0)