Files
MapLibreParticleLab/src/mbgl/text/cross_tile_symbol_index.cpp
2024-05-06 23:45:26 +00:00

263 lines
11 KiB
C++

#include <mbgl/text/cross_tile_symbol_index.hpp>
#include <mbgl/layout/symbol_instance.hpp>
#include <mbgl/renderer/buckets/symbol_bucket.hpp>
#include <mbgl/renderer/render_tile.hpp>
#include <mbgl/tile/tile.hpp>
namespace mbgl {
TileLayerIndex::TileLayerIndex(OverscaledTileID coord_,
std::vector<SymbolInstance>& symbolInstances,
uint32_t bucketInstanceId_,
std::string bucketLeaderId_)
: coord(coord_),
bucketInstanceId(bucketInstanceId_),
bucketLeaderId(std::move(bucketLeaderId_)) {
for (const SymbolInstance& symbolInstance : symbolInstances) {
if (symbolInstance.crossTileID == SymbolInstance::invalidCrossTileID()) continue;
indexedSymbolInstances[symbolInstance.key].emplace_back(symbolInstance.crossTileID,
getScaledCoordinates(symbolInstance, coord));
}
}
Point<int64_t> TileLayerIndex::getScaledCoordinates(const SymbolInstance& symbolInstance,
const OverscaledTileID& childTileCoord) const {
// Round anchor positions to roughly 4 pixel grid
const double roundingFactor = 512.0 / util::EXTENT / 2.0;
const double scale = roundingFactor / std::pow(2, childTileCoord.canonical.z - coord.canonical.z);
return {static_cast<int64_t>(
std::floor((childTileCoord.canonical.x * util::EXTENT + symbolInstance.anchor.point.x) * scale)),
static_cast<int64_t>(
std::floor((childTileCoord.canonical.y * util::EXTENT + symbolInstance.anchor.point.y) * scale))};
}
void TileLayerIndex::findMatches(SymbolBucket& bucket,
const OverscaledTileID& newCoord,
std::set<uint32_t>& zoomCrossTileIDs) const {
auto& symbolInstances = bucket.symbolInstances;
float tolerance = coord.canonical.z < newCoord.canonical.z
? 1.0f
: static_cast<float>(std::pow(2, coord.canonical.z - newCoord.canonical.z));
if (bucket.bucketLeaderID != bucketLeaderId) return;
for (auto& symbolInstance : symbolInstances) {
if (symbolInstance.crossTileID) {
// already has a match, skip
continue;
}
auto it = indexedSymbolInstances.find(symbolInstance.key);
if (it == indexedSymbolInstances.end()) {
// No symbol with this key in this bucket
continue;
}
auto scaledSymbolCoord = getScaledCoordinates(symbolInstance, newCoord);
for (const IndexedSymbolInstance& thisTileSymbol : it->second) {
// Return any symbol with the same keys whose coordinates are within
// 1 grid unit. (with a 4px grid, this covers a 12px by 12px area)
if (std::abs(thisTileSymbol.coord.x - scaledSymbolCoord.x) <= tolerance &&
std::abs(thisTileSymbol.coord.y - scaledSymbolCoord.y) <= tolerance &&
zoomCrossTileIDs.find(thisTileSymbol.crossTileID) == zoomCrossTileIDs.end()) {
// Once we've marked ourselves duplicate against this parent
// symbol, don't let any other symbols at the same zoom level
// duplicate against the same parent (see issue #10844)
zoomCrossTileIDs.insert(thisTileSymbol.crossTileID);
symbolInstance.crossTileID = thisTileSymbol.crossTileID;
break;
}
}
}
}
CrossTileSymbolLayerIndex::CrossTileSymbolLayerIndex(uint32_t& maxCrossTileID_)
: maxCrossTileID(maxCrossTileID_) {}
/*
* Sometimes when a user pans across the antimeridian the longitude value gets
* wrapped. To prevent labels from flashing out and in we adjust the tileID
* values in the indexes so that they match the new wrapped version of the map.
*/
void CrossTileSymbolLayerIndex::handleWrapJump(float newLng) {
const auto wrapDelta = static_cast<int>(std::round((newLng - lng) / 360.0f));
if (wrapDelta != 0) {
std::map<uint8_t, std::map<OverscaledTileID, TileLayerIndex>> newIndexes;
for (auto& zoomIndex : indexes) {
std::map<OverscaledTileID, TileLayerIndex> newZoomIndex;
for (auto& index : zoomIndex.second) {
// change the tileID's wrap and move its index
index.second.coord = index.second.coord.unwrapTo(index.second.coord.wrap + wrapDelta);
newZoomIndex.emplace(index.second.coord, std::move(index.second));
}
newIndexes.emplace(zoomIndex.first, std::move(newZoomIndex));
}
indexes = std::move(newIndexes);
}
lng = newLng;
}
namespace {
bool isInVewport(const mat4& posMatrix, const Point<float>& point) {
vec4 p = {{point.x, point.y, 0, 1}};
matrix::transformMat4(p, p, posMatrix);
// buffer covers area of the next zoom level (current zoom - 1 covered area).
constexpr double buffer = 1.0;
constexpr double edge = 1.0 + buffer;
const double x = p[0] / p[3];
const double y = p[1] / p[3];
return (x > -edge && y > -edge && x < edge && y < edge);
}
} // namespace
bool CrossTileSymbolLayerIndex::addBucket(const OverscaledTileID& tileID,
const mat4& tileMatrix,
SymbolBucket& bucket) {
auto& thisZoomIndexes = indexes[tileID.overscaledZ];
auto previousIndex = thisZoomIndexes.find(tileID);
if (previousIndex != thisZoomIndexes.end()) {
if (previousIndex->second.bucketInstanceId == bucket.bucketInstanceId && !bucket.hasUninitializedSymbols) {
return false;
} else {
// We're replacing this bucket with an updated version
// Remove the old bucket's "used crossTileIDs" now so that the new
// bucket can claim them. We have to keep the old index entries
// themselves until the end of 'addBucket' so that we can copy them
// with 'findMatches'.
removeBucketCrossTileIDs(tileID.overscaledZ, previousIndex->second);
}
}
bucket.hasUninitializedSymbols = false;
if (tileID.overscaleFactor() > 1u) {
// For overscaled tiles the viewport might be showing only a small part of the tile,
// so we filter out the off-screen symbols to improve the performance.
for (auto& symbolInstance : bucket.symbolInstances) {
if (isInVewport(tileMatrix, symbolInstance.anchor.point)) {
symbolInstance.crossTileID = 0u;
} else {
symbolInstance.crossTileID = SymbolInstance::invalidCrossTileID();
bucket.hasUninitializedSymbols = true;
}
}
} else {
for (auto& symbolInstance : bucket.symbolInstances) {
symbolInstance.crossTileID = 0u;
}
}
auto& thisZoomUsedCrossTileIDs = usedCrossTileIDs[tileID.overscaledZ];
for (auto& it : indexes) {
auto zoom = it.first;
const auto& zoomIndexes = it.second;
if (zoom > tileID.overscaledZ) {
for (auto& childIndex : zoomIndexes) {
if (childIndex.second.coord.isChildOf(tileID)) {
childIndex.second.findMatches(bucket, tileID, thisZoomUsedCrossTileIDs);
}
}
} else {
auto parentTileID = tileID.scaledTo(zoom);
auto parentIndex = zoomIndexes.find(parentTileID);
if (parentIndex != zoomIndexes.end()) {
parentIndex->second.findMatches(bucket, tileID, thisZoomUsedCrossTileIDs);
}
}
}
for (auto& symbolInstance : bucket.symbolInstances) {
if (!symbolInstance.crossTileID) {
// symbol did not match any known symbol, assign a new id
symbolInstance.crossTileID = ++maxCrossTileID;
thisZoomUsedCrossTileIDs.insert(symbolInstance.crossTileID);
}
}
thisZoomIndexes.erase(tileID);
thisZoomIndexes.emplace(
std::piecewise_construct,
std::forward_as_tuple(tileID),
std::forward_as_tuple(tileID, bucket.symbolInstances, bucket.bucketInstanceId, bucket.bucketLeaderID));
return true;
}
void CrossTileSymbolLayerIndex::removeBucketCrossTileIDs(uint8_t zoom, const TileLayerIndex& removedBucket) {
for (const auto& key : removedBucket.indexedSymbolInstances) {
for (auto indexedSymbolInstance : key.second) {
usedCrossTileIDs[zoom].erase(indexedSymbolInstance.crossTileID);
}
}
}
bool CrossTileSymbolLayerIndex::removeStaleBuckets(const std::unordered_set<uint32_t>& currentIDs) {
bool tilesChanged = false;
for (auto& zoomIndexes : indexes) {
for (auto it = zoomIndexes.second.begin(); it != zoomIndexes.second.end();) {
if (!currentIDs.count(it->second.bucketInstanceId)) {
removeBucketCrossTileIDs(zoomIndexes.first, it->second);
it = zoomIndexes.second.erase(it);
tilesChanged = true;
} else {
++it;
}
}
}
return tilesChanged;
}
CrossTileSymbolIndex::CrossTileSymbolIndex() = default;
auto CrossTileSymbolIndex::addLayer(const RenderLayer& layer, float lng) -> AddLayerResult {
auto found = layerIndexes.find(layer.getID());
if (found == layerIndexes.end()) {
found = layerIndexes
.emplace(std::piecewise_construct,
std::forward_as_tuple(layer.getID()),
std::forward_as_tuple(maxCrossTileID))
.first;
}
auto& layerIndex = found->second;
AddLayerResult result = AddLayerResult::NoChanges;
std::unordered_set<uint32_t> currentBucketIDs;
layerIndex.handleWrapJump(lng);
for (const auto& item : layer.getPlacementData()) {
const RenderTile& renderTile = item.tile;
Bucket& bucket = item.bucket;
auto pair = bucket.registerAtCrossTileIndex(layerIndex, renderTile);
assert(pair.first != 0u);
if (pair.second) result |= AddLayerResult::BucketsAdded;
currentBucketIDs.insert(pair.first);
}
if (layerIndex.removeStaleBuckets(currentBucketIDs)) result |= AddLayerResult::BucketsRemoved;
return result;
}
void CrossTileSymbolIndex::pruneUnusedLayers(const std::set<std::string>& usedLayers) {
for (auto it = layerIndexes.begin(); it != layerIndexes.end();) {
if (usedLayers.find(it->first) == usedLayers.end()) {
it = layerIndexes.erase(it);
} else {
++it;
}
}
}
void CrossTileSymbolIndex::reset() {
layerIndexes.clear();
}
} // namespace mbgl