[core] Introduce CollisionBoundaries and use it in CollisionIndex
This commit is contained in:
@@ -65,66 +65,72 @@ float CollisionIndex::approximateTileDistance(const TileDistance& tileDistance,
|
||||
(incidenceStretch - 1) * lastSegmentTile * std::abs(std::sin(lastSegmentAngle));
|
||||
}
|
||||
|
||||
bool CollisionIndex::isOffscreen(float x1, float y1, float x2, float y2) const {
|
||||
return x2 < viewportPadding || x1 >= screenRightBoundary || y2 < viewportPadding || y1 >= screenBottomBoundary;
|
||||
bool CollisionIndex::isOffscreen(const CollisionBoundaries& boundaries) const {
|
||||
return boundaries[2] < viewportPadding || boundaries[0] >= screenRightBoundary || boundaries[3] < viewportPadding ||
|
||||
boundaries[1] >= screenBottomBoundary;
|
||||
}
|
||||
|
||||
bool CollisionIndex::isInsideGrid(float x1, float y1, float x2, float y2) const {
|
||||
return x2 >= 0 && x1 < gridRightBoundary && y2 >= 0 && y1 < gridBottomBoundary;
|
||||
bool CollisionIndex::isInsideGrid(const CollisionBoundaries& boundaries) const {
|
||||
return boundaries[2] >= 0 && boundaries[0] < gridRightBoundary && boundaries[3] >= 0 &&
|
||||
boundaries[1] < gridBottomBoundary;
|
||||
}
|
||||
|
||||
CollisionTileBoundaries CollisionIndex::projectTileBoundaries(const mat4& posMatrix) const {
|
||||
|
||||
CollisionBoundaries CollisionIndex::projectTileBoundaries(const mat4& posMatrix) const {
|
||||
Point<float> topLeft = projectPoint(posMatrix, { 0, 0 });
|
||||
Point<float> bottomRight = projectPoint(posMatrix, { util::EXTENT, util::EXTENT });
|
||||
|
||||
return {{ topLeft.x, topLeft.y, bottomRight.x, bottomRight.y }};
|
||||
|
||||
}
|
||||
|
||||
// The tile border checks below are only well defined when the tile boundaries are axis-aligned
|
||||
// We are relying on it only being used in MapMode::Tile, where that is always the case
|
||||
inline bool CollisionIndex::isInsideTile(
|
||||
float x1, float y1, float x2, float y2, const CollisionTileBoundaries& tileBoundaries) const {
|
||||
return x1 >= tileBoundaries[0] && y1 >= tileBoundaries[1] && x2 < tileBoundaries[2] && y2 < tileBoundaries[3];
|
||||
inline bool CollisionIndex::isInsideTile(const CollisionBoundaries& boundaries,
|
||||
const CollisionBoundaries& tileBoundaries) const {
|
||||
return boundaries[0] >= tileBoundaries[0] && boundaries[1] >= tileBoundaries[1] &&
|
||||
boundaries[2] < tileBoundaries[2] && boundaries[3] < tileBoundaries[3];
|
||||
}
|
||||
|
||||
inline bool CollisionIndex::overlapsTile(
|
||||
float x1, float y1, float x2, float y2, const CollisionTileBoundaries& tileBoundaries) const {
|
||||
return x1 < tileBoundaries[2] && x2 > tileBoundaries[0] && y1 < tileBoundaries[3] && y2 > tileBoundaries[1];
|
||||
inline bool CollisionIndex::overlapsTile(const CollisionBoundaries& boundaries,
|
||||
const CollisionBoundaries& tileBoundaries) const {
|
||||
return boundaries[0] < tileBoundaries[2] && boundaries[2] > tileBoundaries[0] &&
|
||||
boundaries[1] < tileBoundaries[3] && boundaries[3] > tileBoundaries[1];
|
||||
}
|
||||
|
||||
bool CollisionIndex::featureIntersectsTileBorders(const CollisionFeature& feature,
|
||||
Point<float> shift,
|
||||
const mat4& posMatrix,
|
||||
const float textPixelRatio,
|
||||
const CollisionTileBoundaries& tileEdges) const {
|
||||
const CollisionBoundaries& tileEdges) const {
|
||||
assert(!feature.alongLine);
|
||||
assert(!feature.boxes.empty());
|
||||
const CollisionBox& box = feature.boxes.front();
|
||||
const auto projectedPoint = projectAndGetPerspectiveRatio(posMatrix, box.anchor);
|
||||
const float tileToViewport = textPixelRatio * projectedPoint.second;
|
||||
float px1 = (box.x1 + shift.x) * tileToViewport + projectedPoint.first.x;
|
||||
float py1 = (box.y1 + shift.y) * tileToViewport + projectedPoint.first.y;
|
||||
float px2 = (box.x2 + shift.x) * tileToViewport + projectedPoint.first.x;
|
||||
float py2 = (box.y2 + shift.y) * tileToViewport + projectedPoint.first.y;
|
||||
CollisionBoundaries collisionBoundaries{{
|
||||
(box.x1 + shift.x) * tileToViewport + projectedPoint.first.x,
|
||||
(box.y1 + shift.y) * tileToViewport + projectedPoint.first.y,
|
||||
(box.x2 + shift.x) * tileToViewport + projectedPoint.first.x,
|
||||
(box.y2 + shift.y) * tileToViewport + projectedPoint.first.y,
|
||||
}};
|
||||
|
||||
return overlapsTile(px1, py1, px2, py2, tileEdges) && !isInsideTile(px1, py1, px2, py2, tileEdges);
|
||||
return overlapsTile(collisionBoundaries, tileEdges) && !isInsideTile(collisionBoundaries, tileEdges);
|
||||
}
|
||||
|
||||
std::pair<bool,bool> CollisionIndex::placeFeature(const CollisionFeature& feature,
|
||||
Point<float> shift,
|
||||
const mat4& posMatrix,
|
||||
const mat4& labelPlaneMatrix,
|
||||
const float textPixelRatio,
|
||||
const PlacedSymbol& symbol,
|
||||
const float scale,
|
||||
const float fontSize,
|
||||
const bool allowOverlap,
|
||||
const bool pitchWithMap,
|
||||
const bool collisionDebug,
|
||||
const optional<CollisionTileBoundaries>& avoidEdges,
|
||||
const optional<std::function<bool(const IndexedSubfeature&)>> collisionGroupPredicate,
|
||||
std::vector<ProjectedCollisionBox>& projectedBoxes) {
|
||||
std::pair<bool, bool> CollisionIndex::placeFeature(
|
||||
const CollisionFeature& feature,
|
||||
Point<float> shift,
|
||||
const mat4& posMatrix,
|
||||
const mat4& labelPlaneMatrix,
|
||||
const float textPixelRatio,
|
||||
const PlacedSymbol& symbol,
|
||||
const float scale,
|
||||
const float fontSize,
|
||||
const bool allowOverlap,
|
||||
const bool pitchWithMap,
|
||||
const bool collisionDebug,
|
||||
const optional<CollisionBoundaries>& avoidEdges,
|
||||
const optional<std::function<bool(const IndexedSubfeature&)>> collisionGroupPredicate,
|
||||
std::vector<ProjectedCollisionBox>& projectedBoxes) {
|
||||
assert(projectedBoxes.empty());
|
||||
if (!feature.alongLine) {
|
||||
const CollisionBox& box = feature.boxes.front();
|
||||
@@ -136,31 +142,32 @@ std::pair<bool,bool> CollisionIndex::placeFeature(const CollisionFeature& featur
|
||||
float py2 = (box.y2 + shift.y) * tileToViewport + projectedPoint.first.y;
|
||||
projectedBoxes.emplace_back(px1, py1, px2, py2);
|
||||
|
||||
if ((avoidEdges && !isInsideTile(px1, py1, px2, py2, *avoidEdges)) ||
|
||||
!isInsideGrid(px1, py1, px2, py2) ||
|
||||
CollisionBoundaries collisionBoundaries{{px1, py1, px2, py2}};
|
||||
if ((avoidEdges && !isInsideTile(collisionBoundaries, *avoidEdges)) || !isInsideGrid(collisionBoundaries) ||
|
||||
(!allowOverlap && collisionGrid.hitTest(projectedBoxes.back().box(), collisionGroupPredicate))) {
|
||||
return { false, false };
|
||||
}
|
||||
|
||||
return {true, isOffscreen(px1, py1, px2, py2)};
|
||||
return {true, isOffscreen(collisionBoundaries)};
|
||||
} else {
|
||||
return placeLineFeature(feature, posMatrix, labelPlaneMatrix, textPixelRatio, symbol, scale, fontSize, allowOverlap, pitchWithMap, collisionDebug, avoidEdges, collisionGroupPredicate, projectedBoxes);
|
||||
}
|
||||
}
|
||||
|
||||
std::pair<bool,bool> CollisionIndex::placeLineFeature(const CollisionFeature& feature,
|
||||
const mat4& posMatrix,
|
||||
const mat4& labelPlaneMatrix,
|
||||
const float textPixelRatio,
|
||||
const PlacedSymbol& symbol,
|
||||
const float scale,
|
||||
const float fontSize,
|
||||
const bool allowOverlap,
|
||||
const bool pitchWithMap,
|
||||
const bool collisionDebug,
|
||||
const optional<CollisionTileBoundaries>& avoidEdges,
|
||||
const optional<std::function<bool(const IndexedSubfeature&)>> collisionGroupPredicate,
|
||||
std::vector<ProjectedCollisionBox>& projectedBoxes) {
|
||||
std::pair<bool, bool> CollisionIndex::placeLineFeature(
|
||||
const CollisionFeature& feature,
|
||||
const mat4& posMatrix,
|
||||
const mat4& labelPlaneMatrix,
|
||||
const float textPixelRatio,
|
||||
const PlacedSymbol& symbol,
|
||||
const float scale,
|
||||
const float fontSize,
|
||||
const bool allowOverlap,
|
||||
const bool pitchWithMap,
|
||||
const bool collisionDebug,
|
||||
const optional<CollisionBoundaries>& avoidEdges,
|
||||
const optional<std::function<bool(const IndexedSubfeature&)>> collisionGroupPredicate,
|
||||
std::vector<ProjectedCollisionBox>& projectedBoxes) {
|
||||
assert(feature.alongLine);
|
||||
assert(projectedBoxes.empty());
|
||||
const auto tileUnitAnchorPoint = symbol.anchorPoint;
|
||||
@@ -249,17 +256,18 @@ std::pair<bool,bool> CollisionIndex::placeLineFeature(const CollisionFeature& fe
|
||||
}
|
||||
|
||||
previousCirclePlaced = true;
|
||||
float px1 = projectedPoint.x - radius;
|
||||
float px2 = projectedPoint.x + radius;
|
||||
float py1 = projectedPoint.y - radius;
|
||||
float py2 = projectedPoint.y + radius;
|
||||
|
||||
CollisionBoundaries collisionBoundaries{{projectedPoint.x - radius,
|
||||
projectedPoint.y - radius,
|
||||
projectedPoint.x + radius,
|
||||
projectedPoint.y + radius}};
|
||||
|
||||
projectedBoxes[i] = ProjectedCollisionBox{projectedPoint.x, projectedPoint.y, radius};
|
||||
|
||||
entirelyOffscreen &= isOffscreen(px1, py1, px2, py2);
|
||||
inGrid |= isInsideGrid(px1, py1, px2, py2);
|
||||
|
||||
if ((avoidEdges && !isInsideTile(px1, py1, px2, py2, *avoidEdges)) ||
|
||||
entirelyOffscreen &= isOffscreen(collisionBoundaries);
|
||||
inGrid |= isInsideGrid(collisionBoundaries);
|
||||
|
||||
if ((avoidEdges && !isInsideTile(collisionBoundaries, *avoidEdges)) ||
|
||||
(!allowOverlap && collisionGrid.hitTest(projectedBoxes[i].circle(), collisionGroupPredicate))) {
|
||||
if (!collisionDebug) {
|
||||
return {false, false};
|
||||
@@ -274,7 +282,6 @@ std::pair<bool,bool> CollisionIndex::placeLineFeature(const CollisionFeature& fe
|
||||
return {!collisionDetected && firstAndLastGlyph && inGrid, entirelyOffscreen};
|
||||
}
|
||||
|
||||
|
||||
void CollisionIndex::insertFeature(const CollisionFeature& feature, const std::vector<ProjectedCollisionBox>& projectedBoxes, bool ignorePlacement, uint32_t bucketInstanceId, uint16_t collisionGroupId) {
|
||||
if (feature.alongLine) {
|
||||
for (auto& circle : projectedBoxes) {
|
||||
|
||||
@@ -13,8 +13,8 @@ namespace mbgl {
|
||||
class PlacedSymbol;
|
||||
|
||||
struct TileDistance;
|
||||
|
||||
using CollisionTileBoundaries = std::array<float,4>;
|
||||
|
||||
using CollisionBoundaries = std::array<float, 4>; // [x1, y1, x2, y2]
|
||||
|
||||
class CollisionIndex {
|
||||
public:
|
||||
@@ -25,51 +25,53 @@ public:
|
||||
Point<float> shift,
|
||||
const mat4& posMatrix,
|
||||
const float textPixelRatio,
|
||||
const CollisionTileBoundaries& tileEdges) const;
|
||||
const CollisionBoundaries& tileEdges) const;
|
||||
|
||||
std::pair<bool,bool> placeFeature(const CollisionFeature& feature,
|
||||
Point<float> shift,
|
||||
const mat4& posMatrix,
|
||||
const mat4& labelPlaneMatrix,
|
||||
const float textPixelRatio,
|
||||
const PlacedSymbol& symbol,
|
||||
const float scale,
|
||||
const float fontSize,
|
||||
const bool allowOverlap,
|
||||
const bool pitchWithMap,
|
||||
const bool collisionDebug,
|
||||
const optional<CollisionTileBoundaries>& avoidEdges,
|
||||
const optional<std::function<bool(const IndexedSubfeature&)>> collisionGroupPredicate,
|
||||
std::vector<ProjectedCollisionBox>& /*out*/);
|
||||
std::pair<bool, bool> placeFeature(
|
||||
const CollisionFeature& feature,
|
||||
Point<float> shift,
|
||||
const mat4& posMatrix,
|
||||
const mat4& labelPlaneMatrix,
|
||||
const float textPixelRatio,
|
||||
const PlacedSymbol& symbol,
|
||||
const float scale,
|
||||
const float fontSize,
|
||||
const bool allowOverlap,
|
||||
const bool pitchWithMap,
|
||||
const bool collisionDebug,
|
||||
const optional<CollisionBoundaries>& avoidEdges,
|
||||
const optional<std::function<bool(const IndexedSubfeature&)>> collisionGroupPredicate,
|
||||
std::vector<ProjectedCollisionBox>& /*out*/);
|
||||
|
||||
void insertFeature(const CollisionFeature& feature, const std::vector<ProjectedCollisionBox>&, bool ignorePlacement, uint32_t bucketInstanceId, uint16_t collisionGroupId);
|
||||
|
||||
std::unordered_map<uint32_t, std::vector<IndexedSubfeature>> queryRenderedSymbols(const ScreenLineString&) const;
|
||||
|
||||
CollisionTileBoundaries projectTileBoundaries(const mat4& posMatrix) const;
|
||||
|
||||
CollisionBoundaries projectTileBoundaries(const mat4& posMatrix) const;
|
||||
|
||||
const TransformState& getTransformState() const { return transformState; }
|
||||
|
||||
private:
|
||||
bool isOffscreen(float x1, float y1, float x2, float y2) const;
|
||||
bool isInsideGrid(float x1, float y1, float x2, float y2) const;
|
||||
bool isInsideTile(float x1, float y1, float x2, float y2, const CollisionTileBoundaries& tileBoundaries) const;
|
||||
bool overlapsTile(float x1, float y1, float x2, float y2, const CollisionTileBoundaries& tileBoundaries) const;
|
||||
bool isOffscreen(const CollisionBoundaries&) const;
|
||||
bool isInsideGrid(const CollisionBoundaries&) const;
|
||||
bool isInsideTile(const CollisionBoundaries& box, const CollisionBoundaries& tileBoundaries) const;
|
||||
bool overlapsTile(const CollisionBoundaries& box, const CollisionBoundaries& tileBoundaries) const;
|
||||
|
||||
std::pair<bool, bool> placeLineFeature(
|
||||
const CollisionFeature& feature,
|
||||
const mat4& posMatrix,
|
||||
const mat4& labelPlaneMatrix,
|
||||
const float textPixelRatio,
|
||||
const PlacedSymbol& symbol,
|
||||
const float scale,
|
||||
const float fontSize,
|
||||
const bool allowOverlap,
|
||||
const bool pitchWithMap,
|
||||
const bool collisionDebug,
|
||||
const optional<CollisionBoundaries>& avoidEdges,
|
||||
const optional<std::function<bool(const IndexedSubfeature&)>> collisionGroupPredicate,
|
||||
std::vector<ProjectedCollisionBox>& /*out*/);
|
||||
|
||||
std::pair<bool,bool> placeLineFeature(const CollisionFeature& feature,
|
||||
const mat4& posMatrix,
|
||||
const mat4& labelPlaneMatrix,
|
||||
const float textPixelRatio,
|
||||
const PlacedSymbol& symbol,
|
||||
const float scale,
|
||||
const float fontSize,
|
||||
const bool allowOverlap,
|
||||
const bool pitchWithMap,
|
||||
const bool collisionDebug,
|
||||
const optional<CollisionTileBoundaries>& avoidEdges,
|
||||
const optional<std::function<bool(const IndexedSubfeature&)>> collisionGroupPredicate,
|
||||
std::vector<ProjectedCollisionBox>& /*out*/);
|
||||
|
||||
float approximateTileDistance(const TileDistance& tileDistance, const float lastSegmentAngle, const float pixelsToTileUnits, const float cameraToAnchorDistance, const bool pitchWithMap);
|
||||
|
||||
std::pair<float,float> projectAnchor(const mat4& posMatrix, const Point<float>& point) const;
|
||||
|
||||
@@ -174,8 +174,8 @@ void Placement::placeBucket(const SymbolBucket& bucket,
|
||||
auto partiallyEvaluatedTextSize = bucket.textSizeBinder->evaluateForZoom(state.getZoom());
|
||||
auto partiallyEvaluatedIconSize = bucket.iconSizeBinder->evaluateForZoom(state.getZoom());
|
||||
|
||||
optional<CollisionTileBoundaries> tileBorders;
|
||||
optional<CollisionTileBoundaries> avoidEdges;
|
||||
optional<CollisionBoundaries> tileBorders;
|
||||
optional<CollisionBoundaries> avoidEdges;
|
||||
if (mapMode == MapMode::Tile) tileBorders = collisionIndex.projectTileBoundaries(posMatrix);
|
||||
|
||||
if (tileBorders && (layout.get<style::SymbolAvoidEdges>() ||
|
||||
|
||||
Reference in New Issue
Block a user