[core] Support [within expression] with layout property

This commit is contained in:
zmiao
2020-02-12 12:28:05 +02:00
parent c57af51842
commit 759dfe8bf6
10 changed files with 142 additions and 45 deletions

View File

@@ -73,6 +73,20 @@ public:
finalDefaultValue);
}
T evaluate(const GeometryTileFeature& feature, const CanonicalTileID& canonical, T finalDefaultValue) const {
return evaluate(expression::EvaluationContext(&feature).withCanonicalTileID(&canonical), finalDefaultValue);
}
T evaluate(const GeometryTileFeature& feature,
const std::set<std::string>& availableImages,
const CanonicalTileID& canonical,
T finalDefaultValue) const {
return evaluate(expression::EvaluationContext(&feature)
.withAvailableImages(&availableImages)
.withCanonicalTileID(&canonical),
finalDefaultValue);
}
T evaluate(float zoom, const GeometryTileFeature& feature, T finalDefaultValue) const {
return evaluate(expression::EvaluationContext(zoom, &feature), finalDefaultValue);
}
@@ -85,6 +99,25 @@ public:
finalDefaultValue);
}
T evaluate(float zoom,
const GeometryTileFeature& feature,
const std::set<std::string>& availableImages,
const CanonicalTileID& canonical,
T finalDefaultValue) const {
return evaluate(expression::EvaluationContext(zoom, &feature)
.withAvailableImages(&availableImages)
.withCanonicalTileID(&canonical),
finalDefaultValue);
}
T evaluate(float zoom,
const GeometryTileFeature& feature,
const CanonicalTileID& canonical,
T finalDefaultValue) const {
return evaluate(expression::EvaluationContext(zoom, &feature).withCanonicalTileID(&canonical),
finalDefaultValue);
}
T evaluate(float zoom, const GeometryTileFeature& feature, const FeatureState& state, T finalDefaultValue) const {
assert(!isFeatureConstant());
return evaluate(expression::EvaluationContext(zoom, &feature, &state), finalDefaultValue);

View File

@@ -132,6 +132,5 @@
"render-tests/text-variable-anchor/left-top-right-buttom-offset-tile-map-mode":"https://github.com/mapbox/mapbox-gl-js/pull/9202",
"render-tests/line-pattern/with-dasharray":"https://github.com/mapbox/mapbox-gl-js/pull/9189",
"render-tests/symbol-sort-key/placement-tile-boundary-right-then-left": "https://github.com/mapbox/mapbox-gl-js/pull/9054",
"render-tests/line-dasharray/zero-length-gap":"https://github.com/mapbox/mapbox-gl-js/pull/9246",
"render-tests/within/layout-text": "TODO: Fix by enabling `within` expreesion with layout property"
"render-tests/line-dasharray/zero-length-gap":"https://github.com/mapbox/mapbox-gl-js/pull/9246"
}

View File

@@ -45,7 +45,8 @@ struct PatternFeatureInserter<void> {
std::unique_ptr<GeometryTileFeature> feature,
PatternLayerMap patternDependencyMap,
float /*zoom*/,
const PropertiesType&) {
const PropertiesType&,
const CanonicalTileID&) {
features.emplace_back(index, std::move(feature), std::move(patternDependencyMap));
}
};
@@ -58,9 +59,10 @@ struct PatternFeatureInserter {
std::unique_ptr<GeometryTileFeature> feature,
PatternLayerMap patternDependencyMap,
float zoom,
const PropertiesType& properties) {
const PropertiesType& properties,
const CanonicalTileID& canonical) {
const auto& sortKeyProperty = properties.template get<SortKeyPropertyType>();
float sortKey = sortKeyProperty.evaluate(*feature, zoom, SortKeyPropertyType::defaultValue());
float sortKey = sortKeyProperty.evaluate(*feature, zoom, canonical, SortKeyPropertyType::defaultValue());
PatternFeature patternFeature{index, std::move(feature), std::move(patternDependencyMap), sortKey};
const auto lowerBound = std::lower_bound(features.cbegin(), features.cend(), patternFeature);
features.insert(lowerBound, std::move(patternFeature));
@@ -127,12 +129,17 @@ public:
const auto min = patternProperty.evaluate(*feature,
zoom - 1,
layoutParameters.availableImages,
parameters.tileID.canonical,
PatternPropertyType::defaultValue());
const auto mid = patternProperty.evaluate(*feature,
zoom,
layoutParameters.availableImages,
parameters.tileID.canonical,
PatternPropertyType::defaultValue());
const auto mid = patternProperty.evaluate(
*feature, zoom, layoutParameters.availableImages, PatternPropertyType::defaultValue());
const auto max = patternProperty.evaluate(*feature,
zoom + 1,
layoutParameters.availableImages,
parameters.tileID.canonical,
PatternPropertyType::defaultValue());
layoutParameters.imageDependencies.emplace(min.to.id(), ImageType::Pattern);
@@ -145,14 +152,17 @@ public:
}
}
PatternFeatureInserter<SortKeyPropertyType>::insert(
features, i, std::move(feature), std::move(patternDependencyMap), zoom, layout);
PatternFeatureInserter<SortKeyPropertyType>::insert(features,
i,
std::move(feature),
std::move(patternDependencyMap),
zoom,
layout,
parameters.tileID.canonical);
}
};
bool hasDependencies() const override {
return hasPattern;
}
bool hasDependencies() const override { return hasPattern; }
void createBucket(const ImagePositions& patternPositions,
std::unique_ptr<FeatureIndex>& featureIndex,

View File

@@ -86,6 +86,7 @@ SymbolLayout::SymbolLayout(const BucketParameters& parameters,
sourceLayer(std::move(sourceLayer_)),
overscaling(parameters.tileID.overscaleFactor()),
zoom(parameters.tileID.overscaledZ),
canonicalID(parameters.tileID.canonical),
mode(parameters.mode),
pixelRatio(parameters.pixelRatio),
tileSize(util::tileSize * overscaling),
@@ -141,9 +142,9 @@ SymbolLayout::SymbolLayout(const BucketParameters& parameters,
ft.index = i;
if (hasText) {
auto formatted = layout->evaluate<TextField>(zoom, ft, layoutParameters.availableImages);
auto textTransform = layout->evaluate<TextTransform>(zoom, ft);
FontStack baseFontStack = layout->evaluate<TextFont>(zoom, ft);
auto formatted = layout->evaluate<TextField>(zoom, ft, layoutParameters.availableImages, canonicalID);
auto textTransform = layout->evaluate<TextTransform>(zoom, ft, canonicalID);
FontStack baseFontStack = layout->evaluate<TextFont>(zoom, ft, canonicalID);
ft.formattedText = TaggedString();
for (const auto & section : formatted.sections) {
@@ -188,13 +189,13 @@ SymbolLayout::SymbolLayout(const BucketParameters& parameters,
}
if (hasIcon) {
ft.icon = layout->evaluate<IconImage>(zoom, ft, layoutParameters.availableImages);
ft.icon = layout->evaluate<IconImage>(zoom, ft, layoutParameters.availableImages, canonicalID);
layoutParameters.imageDependencies.emplace(ft.icon->id(), ImageType::Icon);
}
if (ft.formattedText || ft.icon) {
if (sortFeaturesByKey) {
ft.sortKey = layout->evaluate<SymbolSortKey>(zoom, ft);
ft.sortKey = layout->evaluate<SymbolSortKey>(zoom, ft, canonicalID);
const auto lowerBound = std::lower_bound(features.begin(), features.end(), ft);
features.insert(lowerBound, std::move(ft));
} else {
@@ -355,14 +356,16 @@ void SymbolLayout::prepareSymbols(const GlyphMap& glyphMap,
ShapedTextOrientations shapedTextOrientations;
optional<PositionedIcon> shapedIcon;
std::array<float, 2> textOffset{{0.0f, 0.0f}};
const float layoutTextSize = layout->evaluate<TextSize>(zoom + 1, feature);
const float layoutTextSizeAtBucketZoomLevel = layout->evaluate<TextSize>(zoom, feature);
const float layoutIconSize = layout->evaluate<IconSize>(zoom + 1, feature);
const float layoutTextSize = layout->evaluate<TextSize>(zoom + 1, feature, canonicalID);
const float layoutTextSizeAtBucketZoomLevel = layout->evaluate<TextSize>(zoom, feature, canonicalID);
const float layoutIconSize = layout->evaluate<IconSize>(zoom + 1, feature, canonicalID);
// if feature has text, shape the text
if (feature.formattedText && layoutTextSize > 0.0f) {
const float lineHeight = layout->get<TextLineHeight>() * util::ONE_EM;
const float spacing = util::i18n::allowsLetterSpacing(feature.formattedText->rawText()) ? layout->evaluate<TextLetterSpacing>(zoom, feature) * util::ONE_EM : 0.0f;
const float spacing = util::i18n::allowsLetterSpacing(feature.formattedText->rawText())
? layout->evaluate<TextLetterSpacing>(zoom, feature, canonicalID) * util::ONE_EM
: 0.0f;
auto applyShaping = [&](const TaggedString& formattedText,
WritingModeType writingMode,
@@ -371,7 +374,7 @@ void SymbolLayout::prepareSymbols(const GlyphMap& glyphMap,
const Shaping result = getShaping(
/* string */ formattedText,
/* maxWidth: ems */
isPointPlacement ? layout->evaluate<TextMaxWidth>(zoom, feature) * util::ONE_EM : 0.0f,
isPointPlacement ? layout->evaluate<TextMaxWidth>(zoom, feature, canonicalID) * util::ONE_EM : 0.0f,
/* ems */ lineHeight,
textAnchor,
textJustify,
@@ -389,22 +392,24 @@ void SymbolLayout::prepareSymbols(const GlyphMap& glyphMap,
return result;
};
const std::vector<style::TextVariableAnchorType> variableTextAnchor = layout->evaluate<TextVariableAnchor>(zoom, feature);
const SymbolAnchorType textAnchor = layout->evaluate<TextAnchor>(zoom, feature);
const std::vector<style::TextVariableAnchorType> variableTextAnchor =
layout->evaluate<TextVariableAnchor>(zoom, feature, canonicalID);
const SymbolAnchorType textAnchor = layout->evaluate<TextAnchor>(zoom, feature, canonicalID);
if (variableTextAnchor.empty()) {
// Layers with variable anchors use the `text-radial-offset` property and the [x, y] offset vector
// is calculated at placement time instead of layout time
const float radialOffset = layout->evaluate<TextRadialOffset>(zoom, feature);
const float radialOffset = layout->evaluate<TextRadialOffset>(zoom, feature, canonicalID);
if (radialOffset > 0.0f) {
// The style spec says don't use `text-offset` and `text-radial-offset` together
// but doesn't actually specify what happens if you use both. We go with the radial offset.
textOffset = evaluateRadialOffset(textAnchor, radialOffset * util::ONE_EM);
} else {
textOffset = {{layout->evaluate<TextOffset>(zoom, feature)[0] * util::ONE_EM,
layout->evaluate<TextOffset>(zoom, feature)[1] * util::ONE_EM}};
textOffset = {{layout->evaluate<TextOffset>(zoom, feature, canonicalID)[0] * util::ONE_EM,
layout->evaluate<TextOffset>(zoom, feature, canonicalID)[1] * util::ONE_EM}};
}
}
TextJustifyType textJustify = textAlongLine ? TextJustifyType::Center : layout->evaluate<TextJustify>(zoom, feature);
TextJustifyType textJustify =
textAlongLine ? TextJustifyType::Center : layout->evaluate<TextJustify>(zoom, feature, canonicalID);
const auto addVerticalShapingForPointLabelIfNeeded = [&] {
if (allowVerticalPlacement && feature.formattedText->allowsVerticalWritingMode()) {
@@ -474,8 +479,8 @@ void SymbolLayout::prepareSymbols(const GlyphMap& glyphMap,
if (image != imageMap.end()) {
iconType = SymbolContent::IconRGBA;
shapedIcon = PositionedIcon::shapeIcon(imagePositions.at(feature.icon->id()),
layout->evaluate<IconOffset>(zoom, feature),
layout->evaluate<IconAnchor>(zoom, feature));
layout->evaluate<IconOffset>(zoom, feature, canonicalID),
layout->evaluate<IconAnchor>(zoom, feature, canonicalID));
if (image->second->sdf) {
iconType = SymbolContent::IconSDF;
}
@@ -520,13 +525,13 @@ void SymbolLayout::addFeature(const std::size_t layoutFeatureIndex,
const float minScale = 0.5f;
const float glyphSize = 24.0f;
const std::array<float, 2> iconOffset = layout->evaluate<IconOffset>(zoom, feature);
const std::array<float, 2> iconOffset = layout->evaluate<IconOffset>(zoom, feature, canonicalID);
// To reduce the number of labels that jump around when zooming we need
// to use a text-size value that is the same for all zoom levels.
// This calculates text-size at a high zoom level so that all tiles can
// use the same value when calculating anchor positions.
const float textMaxSize = layout->evaluate<TextSize>(18, feature);
const float textMaxSize = layout->evaluate<TextSize>(18, feature, canonicalID);
const float fontScale = layoutTextSize / glyphSize;
const float textBoxScale = tilePixelRatio * fontScale;
@@ -536,15 +541,15 @@ void SymbolLayout::addFeature(const std::size_t layoutFeatureIndex,
const float textPadding = layout->get<TextPadding>() * tilePixelRatio;
const float iconPadding = layout->get<IconPadding>() * tilePixelRatio;
const float textMaxAngle = layout->get<TextMaxAngle>() * util::DEG2RAD;
const float iconRotation = layout->evaluate<IconRotate>(zoom, feature);
const float textRotation = layout->evaluate<TextRotate>(zoom, feature);
const float iconRotation = layout->evaluate<IconRotate>(zoom, feature, canonicalID);
const float textRotation = layout->evaluate<TextRotate>(zoom, feature, canonicalID);
std::array<float, 2> variableTextOffset;
if (!textRadialOffset.isUndefined()) {
variableTextOffset = {{layout->evaluate<TextRadialOffset>(zoom, feature) * util::ONE_EM,
INVALID_OFFSET_VALUE}};
variableTextOffset = {
{layout->evaluate<TextRadialOffset>(zoom, feature, canonicalID) * util::ONE_EM, INVALID_OFFSET_VALUE}};
} else {
variableTextOffset = {{layout->evaluate<TextOffset>(zoom, feature)[0] * util::ONE_EM,
layout->evaluate<TextOffset>(zoom, feature)[1] * util::ONE_EM}};
variableTextOffset = {{layout->evaluate<TextOffset>(zoom, feature, canonicalID)[0] * util::ONE_EM,
layout->evaluate<TextOffset>(zoom, feature, canonicalID)[1] * util::ONE_EM}};
}
const SymbolPlacementType textPlacement = layout->get<TextRotationAlignment>() != AlignmentType::Map

View File

@@ -115,6 +115,7 @@ private:
const std::unique_ptr<GeometryTileLayer> sourceLayer;
const float overscaling;
const float zoom;
const CanonicalTileID canonicalID;
const MapMode mode;
const float pixelRatio;

View File

@@ -115,7 +115,7 @@ void FillBucket::addFeature(const GeometryTileFeature& feature,
for (auto& pair : paintPropertyBinders) {
const auto it = patternDependencies.find(pair.first);
if (it != patternDependencies.end()){
if (it != patternDependencies.end()) {
pair.second.populateVertexVectors(
feature, vertices.elements(), index, patternPositions, it->second, canonical);
} else {

View File

@@ -34,7 +34,7 @@ void LineBucket::addFeature(const GeometryTileFeature& feature,
std::size_t index,
const CanonicalTileID& canonical) {
for (auto& line : geometryCollection) {
addGeometry(line, feature);
addGeometry(line, feature, canonical);
}
for (auto& pair : paintPropertyBinders) {
@@ -97,7 +97,9 @@ private:
double total;
};
void LineBucket::addGeometry(const GeometryCoordinates& coordinates, const GeometryTileFeature& feature) {
void LineBucket::addGeometry(const GeometryCoordinates& coordinates,
const GeometryTileFeature& feature,
const CanonicalTileID& canonical) {
const FeatureType type = feature.getType();
const std::size_t len = [&coordinates] {
std::size_t l = coordinates.size();
@@ -137,7 +139,7 @@ void LineBucket::addGeometry(const GeometryCoordinates& coordinates, const Geome
*numericValue<double>(clip_start_it->second), *numericValue<double>(clip_end_it->second), total_length};
}
const LineJoinType joinType = layout.evaluate<LineJoin>(zoom, feature);
const LineJoinType joinType = layout.evaluate<LineJoin>(zoom, feature, canonical);
const float miterLimit = joinType == LineJoinType::Bevel ? 1.05f : float(layout.get<LineMiterLimit>());

View File

@@ -52,7 +52,7 @@ public:
std::map<std::string, LineProgram::Binders> paintPropertyBinders;
private:
void addGeometry(const GeometryCoordinates&, const GeometryTileFeature&);
void addGeometry(const GeometryCoordinates&, const GeometryTileFeature&, const CanonicalTileID&);
struct TriangleElement {
TriangleElement(uint16_t a_, uint16_t b_, uint16_t c_) : a(a_), b(b_), c(c_) {}

View File

@@ -50,6 +50,14 @@ public:
);
}
template <class Feature>
T evaluate(const Feature& feature, float zoom, const CanonicalTileID& canonical, T defaultValue) const {
return this->match([&](const T& constant_) { return constant_; },
[&](const style::PropertyExpression<T>& expression) {
return expression.evaluate(zoom, feature, canonical, defaultValue);
});
}
template <class Feature>
T evaluate(const Feature& feature, float zoom, const FeatureState& featureState, T defaultValue) const {
return this->match([&](const T& constant_) { return constant_; },
@@ -96,13 +104,15 @@ public:
Faded<T> evaluate(const Feature& feature,
float zoom,
const std::set<std::string>& availableImages,
const CanonicalTileID& canonical,
T defaultValue) const {
return this->match(
[&] (const Faded<T>& constant_) { return constant_; },
[&] (const style::PropertyExpression<T>& expression) {
if (!expression.isZoomConstant()) {
const T min = expression.evaluate(floor(zoom), feature, availableImages, defaultValue);
const T max = expression.evaluate(floor(zoom) + 1, feature, availableImages, defaultValue);
const T min = expression.evaluate(floor(zoom), feature, availableImages, canonical, defaultValue);
const T max =
expression.evaluate(floor(zoom) + 1, feature, availableImages, canonical, defaultValue);
return Faded<T> {min, max};
} else {
const T evaluated = expression.evaluate(feature, availableImages, defaultValue);

View File

@@ -168,6 +168,11 @@ public:
return t;
}
template <class T>
static T evaluate(float, const GeometryTileFeature&, const CanonicalTileID&, const T& t, const T&) {
return t;
}
template <class T>
static T evaluate(float z,
const GeometryTileFeature& feature,
@@ -193,6 +198,30 @@ public:
[&](const PropertyExpression<T>& t) { return t.evaluate(z, feature, availableImages, defaultValue); });
}
template <class T>
static T evaluate(float z,
const GeometryTileFeature& feature,
const PossiblyEvaluatedPropertyValue<T>& v,
const T& defaultValue,
const std::set<std::string>& availableImages,
const CanonicalTileID& canonical) {
return v.match([&](const T& t) { return t; },
[&](const PropertyExpression<T>& t) {
return t.evaluate(z, feature, availableImages, canonical, defaultValue);
});
}
template <class T>
static T evaluate(float z,
const GeometryTileFeature& feature,
const CanonicalTileID& canonical,
const PossiblyEvaluatedPropertyValue<T>& v,
const T& defaultValue) {
return v.match(
[&](const T& t) { return t; },
[&](const PropertyExpression<T>& t) { return t.evaluate(z, feature, canonical, defaultValue); });
}
template <class T>
static T evaluate(float z, const GeometryTileFeature& feature, const FeatureState& state,
const PossiblyEvaluatedPropertyValue<T>& v, const T& defaultValue) {
@@ -220,6 +249,14 @@ public:
return evaluate(z, feature, this->template get<P>(), P::defaultValue(), availableImages);
}
template <class P>
auto evaluate(float z,
const GeometryTileFeature& feature,
const std::set<std::string>& availableImages,
const CanonicalTileID& canonical) const {
return evaluate(z, feature, this->template get<P>(), P::defaultValue(), availableImages, canonical);
}
Evaluated evaluate(float z, const GeometryTileFeature& feature) const {
return Evaluated {
evaluate<Ps>(z, feature)...