summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorChris Loer <chris.loer@gmail.com>2017-05-18 12:54:11 -0700
committerAnsis Brammanis <brammanis@gmail.com>2017-07-11 09:10:23 -0700
commit7b29b90e7fb379ca4f592e98fd3144d424937632 (patch)
tree5a2674d7edbc583590288914444a773ebf915bc7
parent8f5e0b66ab13cff7d35ed46afaddbdca9ab1993f (diff)
downloadqtlocation-mapboxgl-7b29b90e7fb379ca4f592e98fd3144d424937632.tar.gz
[core] Extend collision feature boxes to accommodate potential pitch-scaling.
-rw-r--r--src/mbgl/text/collision_feature.cpp57
1 files changed, 49 insertions, 8 deletions
diff --git a/src/mbgl/text/collision_feature.cpp b/src/mbgl/text/collision_feature.cpp
index 71d7cc74e0..58a7949a8e 100644
--- a/src/mbgl/text/collision_feature.cpp
+++ b/src/mbgl/text/collision_feature.cpp
@@ -49,7 +49,11 @@ CollisionFeature::CollisionFeature(const GeometryCoordinates& line,
void CollisionFeature::bboxifyLabel(const GeometryCoordinates& line, GeometryCoordinate& anchorPoint,
const int segment, const float labelLength, const float boxSize) {
const float step = boxSize / 2;
- const unsigned int nBoxes = std::floor(labelLength / step);
+ const int nBoxes = std::floor(labelLength / step);
+ // We calculate line collision boxes out to 150% of what would normally be our
+ // max size, to allow collision detection to work on labels that expand as
+ // they move into the distance
+ const int nPitchPaddingBoxes = std::floor(nBoxes / 4);
// offset the center of the first box by half a box so that the edge of the
// box is at the edge of the label.
@@ -58,24 +62,40 @@ void CollisionFeature::bboxifyLabel(const GeometryCoordinates& line, GeometryCoo
GeometryCoordinate &p = anchorPoint;
int index = segment + 1;
float anchorDistance = firstBoxOffset;
+ const float labelStartDistance = -labelLength / 2;
+ const float paddingStartDistance = labelStartDistance - labelLength / 8;
// move backwards along the line to the first segment the label appears on
do {
index--;
- // there isn't enough room for the label after the beginning of the line
- // checkMaxAngle should have already caught this
- if (index < 0) return;
+ if (index < 0) {
+ if (anchorDistance > labelStartDistance) {
+ // there isn't enough room for the label after the beginning of the line
+ // checkMaxAngle should have already caught this
+ return;
+ } else {
+ // The line doesn't extend far enough back for all of our padding,
+ // but we got far enough to show the label under most conditions.
+ index = 0;
+ break;
+ }
+ }
anchorDistance -= util::dist<float>(line[index], p);
p = line[index];
- } while (anchorDistance > -labelLength / 2);
+ } while (anchorDistance > paddingStartDistance);
auto segmentLength = util::dist<float>(line[index], line[index + 1]);
- for (unsigned int i = 0; i < nBoxes; i++) {
+ for (int i = -nPitchPaddingBoxes; i < nBoxes + nPitchPaddingBoxes; i++) {
// the distance the box will be from the anchor
- const float boxDistanceToAnchor = -labelLength / 2 + i * step;
+ const float boxDistanceToAnchor = labelStartDistance + i * step;
+ if (boxDistanceToAnchor < anchorDistance) {
+ // The line doesn't extend far enough back for this box, skip it
+ // (This could allow for line collisions on distant tiles)
+ continue;
+ }
// the box is not on the current segment. Move to the next segment.
while (anchorDistance + segmentLength < boxDistanceToAnchor) {
@@ -99,8 +119,29 @@ void CollisionFeature::bboxifyLabel(const GeometryCoordinates& line, GeometryCoo
p0.y + segmentBoxDistance / segmentLength * (p1.y - p0.y)
};
+ // Distance from label anchor point to inner (towards center) edge of this box
+ // The tricky thing here is that box positioning doesn't change with scale,
+ // but box size does change with scale.
+ // Technically, distanceToInnerEdge should be:
+ // Math.max(Math.abs(boxDistanceToAnchor - firstBoxOffset) - (step / scale), 0);
+ // But using that formula would make solving for maxScale more difficult, so we
+ // approximate with scale=2.
+ // This makes our calculation spot-on at scale=2, and on the conservative side for
+ // lower scales
const float distanceToInnerEdge = std::max(std::fabs(boxDistanceToAnchor - firstBoxOffset) - step / 2, 0.0f);
- const float maxScale = labelLength / 2 / distanceToInnerEdge;
+ float maxScale = labelLength / 2 / distanceToInnerEdge;
+
+ // The box maxScale calculations are designed to be conservative on collisions in the scale range
+ // [1,2]. At scale=1, each box has 50% overlap, and at scale=2, the boxes are lined up edge
+ // to edge (beyond scale 2, gaps start to appear, which could potentially allow missed collisions).
+ // We add "pitch padding" boxes to the left and right to handle effective underzooming
+ // (scale < 1) when labels are in the distance. The overlap approximation could cause us to use
+ // these boxes when the scale is greater than 1, but we prevent that because we know
+ // they're only necessary for scales less than one.
+ // This preserves the pre-pitch-padding behavior for unpitched maps.
+ if (i < 0 || i >= nBoxes) {
+ maxScale = std::min(maxScale, 0.99f);
+ }
boxes.emplace_back(boxAnchor, -boxSize / 2, -boxSize / 2, boxSize / 2, boxSize / 2, maxScale);
}