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

Skip fade animation for placed symbols that are currently offscreen. #10445

Merged
merged 1 commit into from
Nov 13, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions platform/node/test/ignores.json
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@
"render-tests/runtime-styling/image-add-sdf": "https://github.com/mapbox/mapbox-gl-native/issues/9847",
"render-tests/runtime-styling/paint-property-fill-flat-to-extrude": "https://github.com/mapbox/mapbox-gl-native/issues/6745",
"render-tests/runtime-styling/set-style-paint-property-fill-flat-to-extrude": "https://github.com/mapbox/mapbox-gl-native/issues/6745",
"render-tests/symbol-placement/line-overscaled": "https://github.com/mapbox/mapbox-gl-js/issues/5654",
"render-tests/symbol-visibility/visible": "https://github.com/mapbox/mapbox-gl-native/pull/10103",
"render-tests/text-pitch-alignment/auto-text-rotation-alignment-map": "https://github.com/mapbox/mapbox-gl-native/issues/9732",
"render-tests/text-pitch-alignment/map-text-rotation-alignment-map": "https://github.com/mapbox/mapbox-gl-native/issues/9732",
Expand Down
42 changes: 29 additions & 13 deletions src/mbgl/text/collision_index.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,13 @@ static const float viewportPadding = 100;
CollisionIndex::CollisionIndex(const TransformState& transformState_)
: transformState(transformState_)
, collisionGrid(transformState.getSize().width + 2 * viewportPadding, transformState.getSize().height + 2 * viewportPadding, 25)
, ignoredGrid(transformState.getSize().width + 2 * viewportPadding, transformState.getSize().height + 2 * viewportPadding, 25) {
pitchFactor = std::cos(transformState.getPitch()) * transformState.getCameraToCenterDistance();
}
, ignoredGrid(transformState.getSize().width + 2 * viewportPadding, transformState.getSize().height + 2 * viewportPadding, 25)
, screenRightBoundary(transformState.getSize().width + viewportPadding)
, screenBottomBoundary(transformState.getSize().height + viewportPadding)
, gridRightBoundary(transformState.getSize().width + 2 * viewportPadding)
, gridBottomBoundary(transformState.getSize().height + 2 * viewportPadding)
, pitchFactor(std::cos(transformState.getPitch()) * transformState.getCameraToCenterDistance())
{}

float CollisionIndex::approximateTileDistance(const TileDistance& tileDistance, const float lastSegmentAngle, const float pixelsToTileUnits, const float cameraToAnchorDistance, const bool pitchWithMap) {
// This is a quick and dirty solution for chosing which collision circles to use (since collision circles are
Expand All @@ -51,8 +55,16 @@ float CollisionIndex::approximateTileDistance(const TileDistance& tileDistance,
(incidenceStretch - 1) * lastSegmentTile * std::abs(std::sin(lastSegmentAngle));
}

bool CollisionIndex::isOffscreen(const CollisionBox& box) const {
return box.px2 < viewportPadding || box.px1 >= screenRightBoundary || box.py2 < viewportPadding || box.py1 >= screenBottomBoundary;
}

bool CollisionIndex::isInsideGrid(const CollisionBox& box) const {
return box.px2 >= 0 && box.px1 < gridRightBoundary && box.py2 >= 0 && box.py1 < gridBottomBoundary;
}


bool CollisionIndex::placeFeature(CollisionFeature& feature,
std::pair<bool,bool> CollisionIndex::placeFeature(CollisionFeature& feature,
const mat4& posMatrix,
const mat4& labelPlaneMatrix,
const float textPixelRatio,
Expand All @@ -70,20 +82,19 @@ bool CollisionIndex::placeFeature(CollisionFeature& feature,
box.py1 = box.y1 / tileToViewport + projectedPoint.first.y;
box.px2 = box.x2 / tileToViewport + projectedPoint.first.x;
box.py2 = box.y2 / tileToViewport + projectedPoint.first.y;

if (!allowOverlap) {
if (collisionGrid.hitTest({{ box.px1, box.py1 }, { box.px2, box.py2 }})) {
return false;
}

if (!isInsideGrid(box) ||
(!allowOverlap && collisionGrid.hitTest({{ box.px1, box.py1 }, { box.px2, box.py2 }}))) {
return { false, false };
}

return true;
return {true, isOffscreen(box)};
} else {
return placeLineFeature(feature, posMatrix, labelPlaneMatrix, textPixelRatio, symbol, scale, fontSize, allowOverlap, pitchWithMap, collisionDebug);
}
}

bool CollisionIndex::placeLineFeature(CollisionFeature& feature,
std::pair<bool,bool> CollisionIndex::placeLineFeature(CollisionFeature& feature,
const mat4& posMatrix,
const mat4& labelPlaneMatrix,
const float textPixelRatio,
Expand Down Expand Up @@ -115,6 +126,8 @@ bool CollisionIndex::placeLineFeature(CollisionFeature& feature,
/*return tile distance*/ true);

bool collisionDetected = false;
bool inGrid = false;
bool entirelyOffscreen = true;

const auto tileToViewport = projectedAnchor.first * textPixelRatio;
// equivalent to pixel_to_tile_units
Expand Down Expand Up @@ -183,11 +196,14 @@ bool CollisionIndex::placeLineFeature(CollisionFeature& feature,
circle.px = projectedPoint.x;
circle.py = projectedPoint.y;
circle.radius = radius;

entirelyOffscreen &= isOffscreen(circle);
inGrid |= isInsideGrid(circle);

if (!allowOverlap) {
if (collisionGrid.hitTest({{circle.px, circle.py}, circle.radius})) {
if (!collisionDebug) {
return false;
return {false, false};
} else {
// Don't early exit if we're showing the debug circles because we still want to calculate
// which circles are in use
Expand All @@ -197,7 +213,7 @@ bool CollisionIndex::placeLineFeature(CollisionFeature& feature,
}
}

return !collisionDetected && firstAndLastGlyph;
return {!collisionDetected && firstAndLastGlyph && inGrid, entirelyOffscreen};
}


Expand Down
17 changes: 13 additions & 4 deletions src/mbgl/text/collision_index.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ class CollisionIndex {

explicit CollisionIndex(const TransformState&);

bool placeFeature(CollisionFeature& feature,
std::pair<bool,bool> placeFeature(CollisionFeature& feature,
const mat4& posMatrix,
const mat4& labelPlaneMatrix,
const float textPixelRatio,
Expand All @@ -34,7 +34,10 @@ class CollisionIndex {


private:
bool placeLineFeature(CollisionFeature& feature,
bool isOffscreen(const CollisionBox&) const;
bool isInsideGrid(const CollisionBox&) const;

std::pair<bool,bool> placeLineFeature(CollisionFeature& feature,
const mat4& posMatrix,
const mat4& labelPlaneMatrix,
const float textPixelRatio,
Expand All @@ -51,11 +54,17 @@ class CollisionIndex {
std::pair<Point<float>,float> projectAndGetPerspectiveRatio(const mat4& posMatrix, const Point<float>& point) const;
Point<float> projectPoint(const mat4& posMatrix, const Point<float>& point) const;

TransformState transformState;
float pitchFactor;
const TransformState transformState;

CollisionGrid collisionGrid;
CollisionGrid ignoredGrid;

const float screenRightBoundary;
const float screenBottomBoundary;
const float gridRightBoundary;
const float gridBottomBoundary;

const float pitchFactor;
};

} // namespace mbgl
41 changes: 25 additions & 16 deletions src/mbgl/text/placement.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,11 @@

namespace mbgl {

OpacityState::OpacityState(bool placed_) : opacity(0), placed(placed_) {}
OpacityState::OpacityState(bool placed_, bool offscreen)
: opacity((offscreen && placed_) ? 1 : 0)
, placed(placed_)
{
}

OpacityState::OpacityState(const OpacityState& prevState, float increment, bool placed_) :
opacity(std::fmax(0, std::fmin(1, prevState.opacity + (prevState.placed ? increment : -increment)))),
Expand All @@ -18,9 +22,9 @@ bool OpacityState::isHidden() const {
return opacity == 0 && !placed;
}

JointOpacityState::JointOpacityState(bool placedIcon, bool placedText) :
icon(OpacityState(placedIcon)),
text(OpacityState(placedText)) {}
JointOpacityState::JointOpacityState(bool placedIcon, bool placedText, bool offscreen) :
icon(OpacityState(placedIcon, offscreen)),
text(OpacityState(placedText, offscreen)) {}

JointOpacityState::JointOpacityState(const JointOpacityState& prevOpacityState, float increment, bool placedIcon, bool placedText) :
icon(OpacityState(prevOpacityState.icon, increment, placedIcon)),
Expand Down Expand Up @@ -99,29 +103,34 @@ void Placement::placeLayerBucket(
if (seenCrossTileIDs.count(symbolInstance.crossTileID) == 0) {
bool placeText = false;
bool placeIcon = false;
bool offscreen = true;

if (symbolInstance.placedTextIndex) {
PlacedSymbol& placedSymbol = bucket.text.placedSymbols.at(*symbolInstance.placedTextIndex);
const float fontSize = evaluateSizeForFeature(partiallyEvaluatedTextSize, placedSymbol);

placeText = collisionIndex.placeFeature(symbolInstance.textCollisionFeature,
auto placed = collisionIndex.placeFeature(symbolInstance.textCollisionFeature,
posMatrix, textLabelPlaneMatrix, textPixelRatio,
placedSymbol, scale, fontSize,
bucket.layout.get<TextAllowOverlap>(),
bucket.layout.get<TextPitchAlignment>() == style::AlignmentType::Map,
showCollisionBoxes);
placeText = placed.first;
offscreen &= placed.second;
}

if (symbolInstance.placedIconIndex) {
PlacedSymbol& placedSymbol = bucket.icon.placedSymbols.at(*symbolInstance.placedIconIndex);
const float fontSize = evaluateSizeForFeature(partiallyEvaluatedIconSize, placedSymbol);

placeIcon = collisionIndex.placeFeature(symbolInstance.iconCollisionFeature,
auto placed = collisionIndex.placeFeature(symbolInstance.iconCollisionFeature,
posMatrix, iconLabelPlaneMatrix, textPixelRatio,
placedSymbol, scale, fontSize,
bucket.layout.get<IconAllowOverlap>(),
bucket.layout.get<IconPitchAlignment>() == style::AlignmentType::Map,
showCollisionBoxes);
placeIcon = placed.first;
offscreen &= placed.second;
}

// combine placements for icon and text
Expand All @@ -143,7 +152,7 @@ void Placement::placeLayerBucket(

assert(symbolInstance.crossTileID != 0);

placements.emplace(symbolInstance.crossTileID, PlacementPair(placeText, placeIcon));
placements.emplace(symbolInstance.crossTileID, JointPlacement(placeText, placeIcon, offscreen));
seenCrossTileIDs.insert(symbolInstance.crossTileID);
}
}
Expand All @@ -157,16 +166,16 @@ bool Placement::commit(const Placement& prevPlacement, TimePoint now) {
float increment = mapMode == MapMode::Still ? 1.0 : std::chrono::duration<float>(commitTime - prevPlacement.commitTime) / Duration(std::chrono::milliseconds(300));

// add the opacities from the current placement, and copy their current values from the previous placement
for (auto& placementPair : placements) {
auto prevOpacity = prevPlacement.opacities.find(placementPair.first);
for (auto& jointPlacement : placements) {
auto prevOpacity = prevPlacement.opacities.find(jointPlacement.first);
if (prevOpacity != prevPlacement.opacities.end()) {
opacities.emplace(placementPair.first, JointOpacityState(prevOpacity->second, increment, placementPair.second.icon, placementPair.second.text));
opacities.emplace(jointPlacement.first, JointOpacityState(prevOpacity->second, increment, jointPlacement.second.icon, jointPlacement.second.text));
placementChanged = placementChanged ||
placementPair.second.icon != prevOpacity->second.icon.placed ||
placementPair.second.text != prevOpacity->second.text.placed;
jointPlacement.second.icon != prevOpacity->second.icon.placed ||
jointPlacement.second.text != prevOpacity->second.text.placed;
} else {
opacities.emplace(placementPair.first, JointOpacityState(placementPair.second.icon, placementPair.second.text));
placementChanged = placementChanged || placementPair.second.icon || placementPair.second.text;
opacities.emplace(jointPlacement.first, JointOpacityState(jointPlacement.second.icon, jointPlacement.second.text, jointPlacement.second.offscreen));
placementChanged = placementChanged || jointPlacement.second.icon || jointPlacement.second.text;
}
}

Expand Down Expand Up @@ -207,7 +216,7 @@ void Placement::updateBucketOpacities(SymbolBucket& bucket, std::set<uint32_t>&
for (SymbolInstance& symbolInstance : bucket.symbolInstances) {
auto opacityState = seenCrossTileIDs.count(symbolInstance.crossTileID) == 0 ?
getOpacity(symbolInstance.crossTileID) :
JointOpacityState(false, false);
JointOpacityState(false, false, false);

seenCrossTileIDs.insert(symbolInstance.crossTileID);

Expand Down Expand Up @@ -269,7 +278,7 @@ JointOpacityState Placement::getOpacity(uint32_t crossTileSymbolID) const {
if (it != opacities.end()) {
return it->second;
} else {
return JointOpacityState(false, false);
return JointOpacityState(false, false, false);
}

}
Expand Down
22 changes: 15 additions & 7 deletions src/mbgl/text/placement.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ namespace mbgl {

class OpacityState {
public:
OpacityState(bool placed);
OpacityState(bool placed, bool offscreen);
OpacityState(const OpacityState& prevOpacityState, float increment, bool placed);
bool isHidden() const;
float opacity;
Expand All @@ -23,18 +23,26 @@ namespace mbgl {

class JointOpacityState {
public:
JointOpacityState(bool placedIcon, bool placedText);
JointOpacityState(bool placedIcon, bool placedText, bool offscreen);
JointOpacityState(const JointOpacityState& prevOpacityState, float increment, bool placedIcon, bool placedText);
bool isHidden() const;
OpacityState icon;
OpacityState text;
};

class PlacementPair {
class JointPlacement {
public:
PlacementPair(bool text_, bool icon_) : text(text_), icon(icon_) {}
bool text;
bool icon;
JointPlacement(bool text_, bool icon_, bool offscreen_)
: text(text_), icon(icon_), offscreen(offscreen_)
{}

const bool text;
const bool icon;
// offscreen = outside viewport, but within CollisionIndex::viewportPadding px of the edge
// Because these symbols aren't onscreen yet, we can skip the "fade in" animation,
// and if a subsequent viewport change brings them into view, they'll be fully
// visible right away.
const bool offscreen;
};

class Placement {
Expand Down Expand Up @@ -72,7 +80,7 @@ namespace mbgl {
MapMode mapMode;
TimePoint commitTime;

std::unordered_map<uint32_t, PlacementPair> placements;
std::unordered_map<uint32_t, JointPlacement> placements;
std::unordered_map<uint32_t, JointOpacityState> opacities;

TimePoint recentUntil;
Expand Down
2 changes: 1 addition & 1 deletion src/mbgl/util/grid_index.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@ bool GridIndex<T>::hitTest(const BCircle& queryBCircle) const {

template <class T>
bool GridIndex<T>::noIntersection(const BBox& queryBBox) const {
return queryBBox.max.x < 0 || queryBBox.min.x > width || queryBBox.max.y < 0 || queryBBox.min.y > height;
return queryBBox.max.x < 0 || queryBBox.min.x >= width || queryBBox.max.y < 0 || queryBBox.min.y >= height;
}

template <class T>
Expand Down