[core] Introduce CollisionBoundaries and use it in CollisionIndex

This commit is contained in:
Mikhail Pozdnyakov
2020-01-08 10:13:25 +02:00
parent a46b1a4e4f
commit 141e4e7935
3 changed files with 106 additions and 97 deletions

View File

@@ -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) {

View File

@@ -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;

View File

@@ -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>() ||