Merge pull request #322 from mapbox/style-v3

Style Preparations
This commit is contained in:
Konstantin Käfer
2014-06-16 18:58:04 +02:00
23 changed files with 952 additions and 764 deletions

View File

@@ -659,7 +659,7 @@ module.exports = {
"fontSize": 24,
"feature_type": "point",
"type": "text",
"maxWidth": 5,
"maxWidth": 5
},
"country_label_line": {
"source": "outdoors",
@@ -735,7 +735,7 @@ module.exports = {
"fontSize": 30,
"maxWidth": 8,
"letterSpacing": 0.4,
"lineHeight": 2,
"lineHeight": 2
},
"marine_label_point_2": {
"source": "outdoors",
@@ -750,7 +750,7 @@ module.exports = {
"fontSize": 24,
"maxWidth": 8,
"letterSpacing": 0.2,
"lineHeight": 1.5,
"lineHeight": 1.5
},
"marine_label_point_3": {
"source": "outdoors",
@@ -765,7 +765,7 @@ module.exports = {
"fontSize": 18,
"maxWidth": 8,
"letterSpacing": 0.1,
"lineHeight": 1.3,
"lineHeight": 1.3
},
"marine_label_point_other": {
"source": "outdoors",
@@ -780,7 +780,7 @@ module.exports = {
"fontSize": 16,
"maxWidth": 8,
"letterSpacing": 0.1,
"lineHeight": 1.2,
"lineHeight": 1.2
},
"state_label": {
"source": "outdoors",
@@ -848,7 +848,7 @@ module.exports = {
"fontSize": 18,
"maxWidth": 6,
"feature_type": "point",
"type": "text",
"type": "text"
},
"road_label_1": {
"source": "outdoors",
@@ -961,7 +961,7 @@ module.exports = {
"source": "outdoors",
"layer": "poi_label",
"icon": "{{maki}}",
"field": "scalerank",
"field": "scalerank",
"value": 4,
"size": 12,
"type": "point"
@@ -992,7 +992,7 @@ module.exports = {
"font": "Open Sans Semibold, Arial Unicode MS Bold",
"fontSize": 12,
"feature_type": "point",
"type": "text",
"type": "text"
},
"poi_label_3": {
"source": "outdoors",
@@ -1008,7 +1008,7 @@ module.exports = {
"font": "Open Sans Semibold, Arial Unicode MS Bold",
"fontSize": 11,
"feature_type": "point",
"type": "text",
"type": "text"
},
"poi_label_4_4": {
"source": "outdoors",
@@ -1027,7 +1027,7 @@ module.exports = {
"font": "Open Sans Semibold, Arial Unicode MS Bold",
"fontSize": 10,
"feature_type": "point",
"type": "text",
"type": "text"
},
"poi_label_4_16": {
"source": "outdoors",
@@ -1046,12 +1046,12 @@ module.exports = {
"font": "Open Sans Semibold, Arial Unicode MS Bold",
"fontSize": 10,
"feature_type": "point",
"type": "text",
"type": "text"
},
"poi_label_4_all": {
"source": "outdoors",
"layer": "poi_label",
"field": "scalerank",
"field": "scalerank",
"value": 4,
"text_field": "{{name_en}}",
"path": "horizontal",
@@ -1062,7 +1062,7 @@ module.exports = {
"font": "Open Sans Semibold, Arial Unicode MS Bold",
"fontSize": 10,
"feature_type": "point",
"type": "text",
"type": "text"
}
},
"structure": [
@@ -2703,7 +2703,7 @@ module.exports = {
"size": "poi_label_1-2_size",
"stroke": "land",
"strokeWidth": 0.3,
"strokeBlur": 1,
"strokeBlur": 1
},
"poi_label_3": {
"color": "#444",
@@ -2727,7 +2727,7 @@ module.exports = {
],
"stroke": "land",
"strokeWidth": 0.3,
"strokeBlur": 1,
"strokeBlur": 1
},
"poi_label_4_16": {
"color": "#444",
@@ -2739,7 +2739,7 @@ module.exports = {
],
"stroke": "land",
"strokeWidth": 0.3,
"strokeBlur": 1,
"strokeBlur": 1
},
"poi_label_4_all": {
"color": "#444",
@@ -2751,7 +2751,7 @@ module.exports = {
],
"stroke": "land",
"strokeWidth": 0.3,
"strokeBlur": 1,
"strokeBlur": 1
},
"poi_aerodrome": {
"opacity": ["stops",
@@ -3503,11 +3503,11 @@ module.exports = {
},
"water_label": {
"color": "text_water_night",
"stroke": "water_night",
"stroke": "water_night"
},
"waterway_label": {
"color": "text_water_night",
"stroke": "water_night",
"stroke": "water_night"
},
"poi": {
"color": "white",

View File

@@ -41,7 +41,7 @@ public:
FillBucket(FillVertexBuffer& vertexBuffer,
TriangleElementsBuffer& triangleElementsBuffer,
LineElementsBuffer& lineElementsBuffer,
const BucketDescription& bucket_desc);
const BucketFillDescription& properties);
~FillBucket();
virtual void render(Painter& painter, const std::string& layer_name, const Tile::ID& id);
@@ -55,7 +55,7 @@ public:
void drawVertices(OutlineShader& shader);
public:
const BucketGeometryDescription geom_desc;
const BucketFillDescription &properties;
private:
TESSalloc *allocator;

View File

@@ -26,7 +26,7 @@ class VectorTileFeature;
class IconBucket : public Bucket {
public:
IconBucket(IconVertexBuffer& vertexBuffer,
const BucketDescription& bucket_desc);
const BucketIconDescription& properties);
virtual void render(Painter& painter, const std::string& layer_name, const Tile::ID& id);
virtual bool hasData() const;
@@ -37,7 +37,7 @@ public:
void drawIcons(DotShader& shader);
public:
const BucketGeometryDescription geometry;
const BucketIconDescription &properties;
private:

View File

@@ -25,7 +25,7 @@ public:
LineBucket(LineVertexBuffer& vertexBuffer,
TriangleElementsBuffer& triangleElementsBuffer,
PointElementsBuffer& pointElementsBuffer,
const BucketDescription& bucket_desc);
const BucketLineDescription& properties);
virtual void render(Painter& painter, const std::string& layer_name, const Tile::ID& id);
virtual bool hasData() const;
@@ -39,7 +39,7 @@ public:
void drawPoints(LinejoinShader& shader);
public:
const BucketGeometryDescription geometry;
const BucketLineDescription &properties;
private:

View File

@@ -28,7 +28,7 @@ public:
TextBucket(
TextVertexBuffer &vertexBuffer,
TriangleElementsBuffer &triangleElementsBuffer,
const BucketDescription &bucket_desc, Placement &placement);
const BucketTextDescription &properties, Placement &placement);
virtual void render(Painter &painter, const std::string &layer_name, const Tile::ID &id);
virtual bool hasData() const;
@@ -43,7 +43,7 @@ public:
void drawGlyphs(TextShader &shader);
public:
const BucketGeometryDescription &geom_desc;
const BucketTextDescription &properties;
private:
TextVertexBuffer& vertexBuffer;

View File

@@ -82,31 +82,50 @@ inline float verticalAlignmentType(const std::string& alignment) {
else return 0.5;
};
class BucketFillDescription {
public:
};
class BucketGeometryDescription {
class BucketLineDescription {
public:
CapType cap = CapType::None;
JoinType join = JoinType::None;
std::string font;
std::string field;
std::string icon;
float size = 0.0f;
float miter_limit = 2.0f;
float round_limit = 1.0f;
TextPathType path = TextPathType::Horizontal;
float alignment = 0.5;
float vertical_alignment = 0.5;
float line_height = 1.2 * 24;
float max_width = 15.0f * 24;
float letter_spacing = 0;
vec2<float> translate {0, 0};
float padding = 2.0f;
float textMinDistance = 250.0f;
float rotate = 0.0f; // what is this?
float maxAngleDelta = M_PI;
bool alwaysVisible = false;
};
class BucketIconDescription {
public:
uint16_t size = 16;
vec2<float> translate {0, 0};
std::string icon;
};
class BucketTextDescription {
public:
std::string field;
TextPathType path = TextPathType::Horizontal;
std::string font;
float max_size = 16.0f;
float max_width = 15.0f * 24;
float line_height = 1.2f * 24;
float letter_spacing = 0.0f;
float alignment = 0.5f;
float vertical_alignment = 0.5;
vec2<float> translate {0, 0};
float max_angle_delta = M_PI;
float min_distance = 250.0f;
float rotate = 0.0f; // what is this?
float padding = 2.0f;
bool always_visible = false;
};
class BucketRasterDescription {
public:
};
typedef util::variant<BucketFillDescription, BucketLineDescription, BucketIconDescription,
BucketTextDescription, BucketRasterDescription> BucketRenderDescription;
class BucketDescription {
public:
@@ -120,7 +139,7 @@ public:
PropertyFilterExpression filter = std::true_type();
// Specifies how the geometry for this bucket should be created
BucketGeometryDescription geometry;
BucketRenderDescription render;
};
std::ostream& operator<<(std::ostream&, const BucketDescription& bucket);

View File

@@ -2,21 +2,35 @@
#define LLMR_STYLE_CLASS_DESCRIPTION
#include <string>
#include <vector>
#include <map>
#include <unordered_map>
#include "properties.hpp"
namespace llmr {
class ClassDescription {
public:
BackgroundClass background;
std::map<std::string, FillClass> fill;
std::map<std::string, LineClass> line;
std::map<std::string, IconClass> icon;
std::map<std::string, TextClass> text;
std::map<std::string, RasterClass> raster;
std::map<std::string, CompositeClass> composite;
inline const ClassProperties &operator[](const std::string &name) const {
auto it = styles.find(name);
if (it != styles.end()) {
return it->second;
} else {
return defaultProperties;
}
}
void addClass(const std::string &name, ClassProperties &&properties);
inline std::unordered_map<std::string, ClassProperties>::const_iterator begin() const {
return styles.begin();
}
inline std::unordered_map<std::string, ClassProperties>::const_iterator end() const {
return styles.end();
}
private:
std::unordered_map<std::string, ClassProperties> styles;
static const ClassProperties defaultProperties;
};

View File

@@ -6,9 +6,12 @@
#include <string>
#include <limits>
#include <set>
#include <map>
#include <type_traits>
#include <memory>
#include <boost/optional.hpp>
#include <llmr/util/variant.hpp>
namespace llmr {
@@ -16,7 +19,10 @@ namespace llmr {
typedef std::array<float, 4> Color;
struct PropertyTransition {
inline PropertyTransition(uint16_t duration, uint16_t delay = 0) : duration(duration), delay(delay) {}
inline PropertyTransition(uint16_t duration, uint16_t delay = 0)
: duration(duration), delay(delay) {}
inline PropertyTransition(const PropertyTransition &transition)
: duration(transition.duration), delay(transition.delay) {}
uint16_t duration = 0;
uint16_t delay = 0;
};
@@ -82,21 +88,124 @@ struct FunctionProperty {
std::vector<float> values;
inline FunctionProperty() : function(&functions::null) {}
inline FunctionProperty(const FunctionProperty &property)
: function(property.function), values(property.values) {}
inline FunctionProperty(FunctionProperty &&property)
: function(property.function), values(std::move(property.values)) {}
inline void operator=(const FunctionProperty &rhs) {
function = rhs.function;
values = rhs.values;
}
inline FunctionProperty(float value) : function(&functions::constant), values(1, value) {}
template <typename T> inline T evaluate(float z) const { return function(z, values); }
};
struct GenericClass {
boost::optional<FunctionProperty> enabled;
boost::optional<std::array<FunctionProperty, 2>> translate;
boost::optional<PropertyTransition> translate_transition;
boost::optional<TranslateAnchor> translateAnchor;
boost::optional<FunctionProperty> opacity;
boost::optional<PropertyTransition> opacity_transition;
boost::optional<FunctionProperty> prerender;
boost::optional<FunctionProperty> prerenderBuffer;
boost::optional<FunctionProperty> prerenderSize;
boost::optional<FunctionProperty> prerenderBlur;
enum class RenderType {
Fill,
Line,
Icon,
Text,
Raster,
Composite,
Background
};
enum class ClassPropertyKey {
Enabled,
TranslateX,
TranslateY,
TranslateTransition,
TranslateAnchor,
Opacity,
OpacityTransition,
Prerender,
PrerenderBuffer,
PrerenderSize,
PrerenderBlur,
FillWinding,
FillAntialias,
FillColor,
FillColorTransition,
FillStrokeColor,
FillStrokeColorTransition,
FillImage,
LineWidth,
LineWidthTransition,
LineOffset,
LineOffsetTransition,
LineColor,
LineColorTransition,
LineDashLand,
LineDashGap,
LineDashTransition,
IconSize,
IconColor,
IconColorTransition,
IconImage,
IconRadius,
IconRadiusTransition,
IconBlur,
IconBlurTransition,
TextColor,
TextColorTransition,
TextHaloColor,
TextHaloColorTransition,
TextHaloRadius,
TextHaloRadiusTransition,
TextHaloBlur,
TextHaloBlurTransition,
TextSize,
TextRotate,
TextAlwaysVisible,
BackgroundColor,
BackgroundColorTransition,
};
typedef util::variant<
FunctionProperty,
PropertyTransition,
TranslateAnchor,
Color,
Winding,
std::string,
bool
> ClassPropertyValue;
class ClassProperties {
public:
inline ClassProperties(RenderType type) : type(type) {}
inline ClassProperties(ClassProperties &&properties)
: type(properties.type), properties(std::move(properties.properties)) {}
template <typename T>
inline const T *get(ClassPropertyKey key) const {
const auto it = properties.find(key);
if (it != properties.end() && it->second.is<T>()) {
return ::std::addressof(it->second.get<T>());
} else {
return nullptr;
}
}
template <typename ...Args>
inline void emplace(Args&& ...args) {
properties.emplace(::std::forward<Args>(args)...);
}
const RenderType type;
private:
std::map<ClassPropertyKey, ClassPropertyValue> properties;
};
struct PrerenderProperties {
@@ -137,17 +246,6 @@ struct GenericProperties {
}
};
struct IconClass : public GenericClass {
boost::optional<FunctionProperty> size;
boost::optional<Color> color;
boost::optional<PropertyTransition> color_transition;
boost::optional<std::string> image;
boost::optional<FunctionProperty> radius;
boost::optional<PropertyTransition> radius_transition;
boost::optional<FunctionProperty> blur;
boost::optional<PropertyTransition> blur_transition;
};
struct IconProperties : public GenericProperties {
inline IconProperties() : GenericProperties() {}
float size = 0;
@@ -157,17 +255,6 @@ struct IconProperties : public GenericProperties {
float blur = 0;
};
struct LineClass : public GenericClass {
boost::optional<FunctionProperty> width;
boost::optional<PropertyTransition> width_transition;
boost::optional<FunctionProperty> offset;
boost::optional<PropertyTransition> offset_transition;
boost::optional<Color> color;
boost::optional<PropertyTransition> color_transition;
boost::optional<std::array<FunctionProperty, 2>> dash_array;
boost::optional<PropertyTransition> dash_array_transition;
};
struct LineProperties : public GenericProperties {
inline LineProperties() : GenericProperties() {}
float width = 0;
@@ -176,16 +263,6 @@ struct LineProperties : public GenericProperties {
std::array<float, 2> dash_array = {{ 1, -1 }};
};
struct FillClass : public GenericClass {
boost::optional<Winding> winding;
boost::optional<FunctionProperty> antialias;
boost::optional<Color> fill_color;
boost::optional<PropertyTransition> fill_color_transition;
boost::optional<Color> stroke_color;
boost::optional<PropertyTransition> stroke_color_transition;
boost::optional<std::string> image;
};
struct FillProperties : public GenericProperties {
inline FillProperties() : GenericProperties() {}
Winding winding = Winding::NonZero;
@@ -196,20 +273,6 @@ struct FillProperties : public GenericProperties {
std::string image;
};
struct TextClass : public GenericClass {
boost::optional<Color> color;
boost::optional<PropertyTransition> color_transition;
boost::optional<Color> halo;
boost::optional<PropertyTransition> halo_transition;
boost::optional<FunctionProperty> halo_radius;
boost::optional<PropertyTransition> halo_radius_transition;
boost::optional<FunctionProperty> halo_blur;
boost::optional<PropertyTransition> halo_blur_transition;
boost::optional<FunctionProperty> size;
boost::optional<FunctionProperty> rotate;
boost::optional<FunctionProperty> always_visible;
};
struct TextProperties : public GenericProperties {
inline TextProperties() : GenericProperties() {}
Color color = {{ 0, 0, 0, 1 }};
@@ -221,26 +284,15 @@ struct TextProperties : public GenericProperties {
bool always_visible = false;
};
struct BackgroundClass : public GenericClass {
boost::optional<Color> color;
boost::optional<PropertyTransition> color_transition;
};
struct BackgroundProperties : public GenericProperties {
inline BackgroundProperties() : GenericProperties() {}
Color color = {{ 1, 1, 1, 1 }};
};
struct RasterClass : public GenericClass {
};
struct RasterProperties : public GenericProperties {
inline RasterProperties() : GenericProperties() {}
};
struct CompositeClass : public GenericClass {
};
struct CompositeProperties : public GenericProperties {
inline CompositeProperties() : GenericProperties() {}
};

View File

@@ -69,7 +69,14 @@ private:
uint64_t transitionDuration(const std::string &layer_name, TransitionablePropertyKey key) const;
uint64_t transitionDelay(const std::string &layer_name, TransitionablePropertyKey key) const;
void cascadeProperties(GenericProperties &properties, const GenericClass& klass, const std::string& layer_name, const std::string& class_name, float z);
void cascadeProperties(GenericProperties &properties, const ClassProperties& klass, const std::string& layer_name, const std::string& class_name, float z);
void cascadeProperties(FillProperties &properties, const ClassProperties& klass, const std::string& layer_name, const std::string& class_name, float z);
void cascadeProperties(LineProperties &properties, const ClassProperties& klass, const std::string& layer_name, const std::string& class_name, float z);
void cascadeProperties(IconProperties &properties, const ClassProperties& klass, const std::string& layer_name, const std::string& class_name, float z);
void cascadeProperties(TextProperties &properties, const ClassProperties& klass, const std::string& layer_name, const std::string& class_name, float z);
void cascadeProperties(RasterProperties &properties, const ClassProperties& klass, const std::string& layer_name, const std::string& class_name, float z);
void cascadeProperties(CompositeProperties &properties, const ClassProperties& klass, const std::string& layer_name, const std::string& class_name, float z);
void cascadeProperties(BackgroundProperties &properties, const ClassProperties& klass, const std::string& layer_name, const std::string& class_name, float z);
private:
// Last applied settings.

View File

@@ -30,26 +30,33 @@ private:
const std::map<std::string, BucketDescription> &buckets,
const std::map<std::string, std::string> &layerBuckets);
void parseGenericClass(GenericClass &klass, JSVal value);
FillClass parseFillClass(JSVal value);
LineClass parseLineClass(JSVal value);
IconClass parseIconClass(JSVal value);
TextClass parseTextClass(JSVal value);
BackgroundClass parseBackgroundClass(JSVal value);
RasterClass parseRasterClass(JSVal value);
CompositeClass parseCompositeClass(JSVal value);
void parseGenericClass(ClassProperties &klass, JSVal value);
ClassProperties parseFillClass(JSVal value);
ClassProperties parseLineClass(JSVal value);
ClassProperties parseIconClass(JSVal value);
ClassProperties parseTextClass(JSVal value);
ClassProperties parseBackgroundClass(JSVal value);
ClassProperties parseRasterClass(JSVal value);
ClassProperties parseCompositeClass(JSVal value);
PropertyFilterExpression parseFilterOrExpression(JSVal value);
bool parseBoolean(JSVal value);
std::string parseString(JSVal value);
JSVal replaceConstant(JSVal value);
std::vector<FunctionProperty> parseArray(JSVal value, uint16_t expected_count);
Color parseColor(JSVal value);
Value parseValue(JSVal value);
FunctionProperty::fn parseFunctionType(JSVal type);
FunctionProperty parseFunction(JSVal value);
boost::optional<PropertyTransition> parseTransition(JSVal value, std::string property_name);
template <typename T>
T parse(JSVal value);
template <typename T>
inline void parse(const char *property_name, ClassPropertyKey key, ClassProperties &klass, JSVal value) {
if (value.HasMember(property_name)) {
klass.emplace(key, parse<T>(value[property_name]));
}
}
void parseTransition(const char *property_name, ClassPropertyKey key, ClassProperties &klass, JSVal value);
void parseFunctionArray(const char *property_name, const std::vector<ClassPropertyKey> &keys, ClassProperties &klass, JSVal value);
private:
std::map<std::string, const rapidjson::Value *> constants;

View File

@@ -10,14 +10,14 @@
namespace llmr {
class TextBucket;
class BucketGeometryDescription;
class BucketTextDescription;
class Placement {
public:
Placement(int8_t zoom);
void addFeature(TextBucket &bucket, const std::vector<Coordinate> &line,
const BucketGeometryDescription &info,
const BucketTextDescription &info,
const GlyphPositions &face,
const Shaping &shaping);

View File

@@ -166,24 +166,38 @@ void TileParser::addBucketFeatures(Bucket &bucket, const VectorTileLayer &layer,
std::unique_ptr<Bucket> TileParser::createFillBucket(const VectorTileLayer &layer,
const BucketDescription &bucket_desc) {
if (!bucket_desc.render.valid() || !bucket_desc.render.is<BucketFillDescription>()) {
return nullptr;
}
std::unique_ptr<FillBucket> bucket = std::make_unique<FillBucket>(
tile.fillVertexBuffer, tile.triangleElementsBuffer, tile.lineElementsBuffer, bucket_desc);
tile.fillVertexBuffer, tile.triangleElementsBuffer, tile.lineElementsBuffer,
bucket_desc.render.get<BucketFillDescription>());
addBucketFeatures(bucket, layer, bucket_desc);
return obsolete() ? nullptr : std::move(bucket);
}
std::unique_ptr<Bucket> TileParser::createLineBucket(const VectorTileLayer &layer,
const BucketDescription &bucket_desc) {
if (!bucket_desc.render.valid() || !bucket_desc.render.is<BucketLineDescription>()) {
return nullptr;
}
std::unique_ptr<LineBucket> bucket = std::make_unique<LineBucket>(
tile.lineVertexBuffer, tile.triangleElementsBuffer, tile.pointElementsBuffer, bucket_desc);
tile.lineVertexBuffer, tile.triangleElementsBuffer, tile.pointElementsBuffer,
bucket_desc.render.get<BucketLineDescription>());
addBucketFeatures(bucket, layer, bucket_desc);
return obsolete() ? nullptr : std::move(bucket);
}
std::unique_ptr<Bucket> TileParser::createIconBucket(const VectorTileLayer &layer,
const BucketDescription &bucket_desc) {
std::unique_ptr<IconBucket> bucket =
std::make_unique<IconBucket>(tile.iconVertexBuffer, bucket_desc);
if (!bucket_desc.render.valid() || !bucket_desc.render.is<BucketIconDescription>()) {
return nullptr;
}
std::unique_ptr<IconBucket> bucket = std::make_unique<IconBucket>(
tile.iconVertexBuffer, bucket_desc.render.get<BucketIconDescription>());
addBucketFeatures(bucket, layer, bucket_desc, *spriteAtlas);
return obsolete() ? nullptr : std::move(bucket);
}
@@ -192,8 +206,15 @@ typedef std::pair<uint16_t, uint16_t> GlyphRange;
std::unique_ptr<Bucket> TileParser::createTextBucket(const VectorTileLayer &layer,
const BucketDescription &bucket_desc) {
if (!bucket_desc.render.valid() || !bucket_desc.render.is<BucketTextDescription>()) {
return nullptr;
}
const BucketTextDescription &properties = bucket_desc.render.get<BucketTextDescription>();
std::unique_ptr<TextBucket> bucket = std::make_unique<TextBucket>(
tile.textVertexBuffer, tile.triangleElementsBuffer, bucket_desc, placement);
tile.textVertexBuffer, tile.triangleElementsBuffer, properties, placement);
util::utf8_to_utf32 ucs4conv;
@@ -210,7 +231,7 @@ std::unique_ptr<Bucket> TileParser::createTextBucket(const VectorTileLayer &laye
VectorTileFeature feature{feature_pbf, layer};
const std::u32string string = ucs4conv.convert(
util::replaceTokens(bucket_desc.geometry.field, feature.properties));
util::replaceTokens(properties.field, feature.properties));
// Loop through all characters of this text and collect unique codepoints.
for (uint32_t chr : string) {
@@ -220,23 +241,23 @@ std::unique_ptr<Bucket> TileParser::createTextBucket(const VectorTileLayer &laye
labels.emplace_back(string, feature.geometry);
}
glyphStore->waitForGlyphRanges(bucket_desc.geometry.font, ranges);
glyphStore->waitForGlyphRanges(properties.font, ranges);
}
// Create a copy!
const FontStack &fontStack = glyphStore->getFontStack(bucket_desc.geometry.font);
const FontStack &fontStack = glyphStore->getFontStack(properties.font);
GlyphPositions face;
// Shape and place all labels.
for (const std::pair<std::u32string, pbf> &label : labels) {
// Shape labels.
const Shaping shaping = fontStack.getShaping(label.first, bucket_desc.geometry.max_width,
bucket_desc.geometry.line_height, bucket_desc.geometry.alignment,
bucket_desc.geometry.vertical_alignment, bucket_desc.geometry.letter_spacing);
const Shaping shaping = fontStack.getShaping(label.first, properties.max_width,
properties.line_height, properties.alignment,
properties.vertical_alignment, properties.letter_spacing);
// Place labels.
addGlyph(tile.id.to_uint64(), bucket_desc.geometry.font, label.first, fontStack, *glyphAtlas,
addGlyph(tile.id.to_uint64(), properties.font, label.first, fontStack, *glyphAtlas,
face);
bucket->addFeature(label.second, face, shaping);

View File

@@ -30,30 +30,26 @@ void FillBucket::free(void *, void *ptr) {
::free(ptr);
}
FillBucket::FillBucket(FillVertexBuffer& vertexBuffer,
TriangleElementsBuffer& triangleElementsBuffer,
LineElementsBuffer& lineElementsBuffer,
const BucketDescription& bucket_desc)
: geom_desc(bucket_desc.geometry),
allocator(new TESSalloc {
&alloc,
&realloc,
&free,
nullptr, // userData
64, // meshEdgeBucketSize
64, // meshVertexBucketSize
32, // meshFaceBucketSize
64, // dictNodeBucketSize
8, // regionBucketSize
128, // extraVertices allocated for the priority queue.
}),
tesselator(tessNewTess(allocator)),
vertexBuffer(vertexBuffer),
triangleElementsBuffer(triangleElementsBuffer),
lineElementsBuffer(lineElementsBuffer),
vertex_start(vertexBuffer.index()),
triangle_elements_start(triangleElementsBuffer.index()),
line_elements_start(lineElementsBuffer.index()) {
FillBucket::FillBucket(FillVertexBuffer &vertexBuffer,
TriangleElementsBuffer &triangleElementsBuffer,
LineElementsBuffer &lineElementsBuffer,
const BucketFillDescription &properties)
: properties(properties),
allocator(new TESSalloc{&alloc, &realloc, &free, nullptr, // userData
64, // meshEdgeBucketSize
64, // meshVertexBucketSize
32, // meshFaceBucketSize
64, // dictNodeBucketSize
8, // regionBucketSize
128, // extraVertices allocated for the priority queue.
}),
tesselator(tessNewTess(allocator)),
vertexBuffer(vertexBuffer),
triangleElementsBuffer(triangleElementsBuffer),
lineElementsBuffer(lineElementsBuffer),
vertex_start(vertexBuffer.index()),
triangle_elements_start(triangleElementsBuffer.index()),
line_elements_start(lineElementsBuffer.index()) {
assert(tesselator);
}

View File

@@ -19,8 +19,8 @@ struct geometry_too_long_exception : std::exception {};
using namespace llmr;
IconBucket::IconBucket(IconVertexBuffer& vertexBuffer,
const BucketDescription& bucket_desc)
: geometry(bucket_desc.geometry),
const BucketIconDescription& properties)
: properties(properties),
vertexBuffer(vertexBuffer),
vertex_start(vertexBuffer.index()) {
}
@@ -28,15 +28,15 @@ IconBucket::IconBucket(IconVertexBuffer& vertexBuffer,
void IconBucket::addFeature(const VectorTileFeature &feature, SpriteAtlas &sprite_atlas) {
std::string field;
if (geometry.field.size()) {
field = util::replaceTokens(geometry.field, feature.properties);
if (properties.icon.size()) {
field = util::replaceTokens(properties.icon, feature.properties);
}
if (!field.size()) {
field = "<circle>";
}
const Rect<uint16_t> rect = sprite_atlas.getIcon(geometry.size, field);
const Rect<uint16_t> rect = sprite_atlas.getIcon(properties.size, field);
const uint16_t tx = rect.x + rect.w / 2;
const uint16_t ty = rect.y + rect.h / 2;

View File

@@ -20,8 +20,8 @@ using namespace llmr;
LineBucket::LineBucket(LineVertexBuffer& vertexBuffer,
TriangleElementsBuffer& triangleElementsBuffer,
PointElementsBuffer& pointElementsBuffer,
const BucketDescription& bucket_desc)
: geometry(bucket_desc.geometry),
const BucketLineDescription& properties)
: properties(properties),
vertexBuffer(vertexBuffer),
triangleElementsBuffer(triangleElementsBuffer),
pointElementsBuffer(pointElementsBuffer),
@@ -60,9 +60,6 @@ struct TriangleElement {
typedef uint16_t PointElement;
void LineBucket::addGeometry(const std::vector<Coordinate>& vertices) {
const JoinType join = geometry.join;
const CapType cap = geometry.cap;
const float miterLimit = geometry.miter_limit;
// TODO: use roundLimit
// const float roundLimit = geometry.round_limit;
@@ -80,8 +77,8 @@ void LineBucket::addGeometry(const std::vector<Coordinate>& vertices) {
return;
}
CapType beginCap = cap;
CapType endCap = closed ? CapType::Butt : cap;
CapType beginCap = properties.cap;
CapType endCap = closed ? CapType::Butt : properties.cap;
JoinType currentJoin = JoinType::None;
@@ -111,7 +108,7 @@ void LineBucket::addGeometry(const std::vector<Coordinate>& vertices) {
if (currentVertex) prevVertex = currentVertex;
currentVertex = vertices[i];
currentJoin = join;
currentJoin = properties.join;
if (prevVertex) distance += util::dist<double>(currentVertex, prevVertex);
@@ -164,7 +161,7 @@ void LineBucket::addGeometry(const std::vector<Coordinate>& vertices) {
// Switch to miter joins if the angle is very low.
if (currentJoin != JoinType::Miter) {
if (std::fabs(joinAngularity) < 0.5 && roundness < miterLimit) {
if (std::fabs(joinAngularity) < 0.5 && roundness < properties.miter_limit) {
currentJoin = JoinType::Miter;
}
}
@@ -213,14 +210,14 @@ void LineBucket::addGeometry(const std::vector<Coordinate>& vertices) {
// The two normals are almost parallel.
joinNormal.x = -nextNormal.y;
joinNormal.y = nextNormal.x;
} else if (roundness > miterLimit) {
} else if (roundness > properties.miter_limit) {
// If the miter grows too large, flip the direction to make a
// bevel join.
joinNormal.x = (prevNormal.x - nextNormal.x) / joinAngularity;
joinNormal.y = (prevNormal.y - nextNormal.y) / joinAngularity;
}
if (roundness > miterLimit) {
if (roundness > properties.miter_limit) {
flip = -flip;
}

View File

@@ -41,7 +41,7 @@ void Painter::renderIcon(IconBucket& bucket, const std::string& layer_name, cons
spriteAtlas.bind(map.getState().isChanging());
const float iconSize = bucket.geometry.size * map.getState().getPixelRatio();
const float iconSize = bucket.properties.size * map.getState().getPixelRatio();
iconShader->setSize(iconSize);
#ifndef GL_ES_VERSION_2_0
glPointSize(iconSize);

View File

@@ -20,7 +20,7 @@ void Painter::renderText(TextBucket& bucket, const std::string& layer_name, cons
mat4 exMatrix;
matrix::copy(exMatrix, projMatrix);
if (bucket.geom_desc.path == TextPathType::Curve) {
if (bucket.properties.path == TextPathType::Curve) {
matrix::rotate_z(exMatrix, exMatrix, map.getState().getAngle());
}
@@ -30,7 +30,7 @@ void Painter::renderText(TextBucket& bucket, const std::string& layer_name, cons
}
// If layerStyle.size > bucket.info.fontSize then labels may collide
float fontSize = std::fmin(properties.size, bucket.geom_desc.size);
float fontSize = std::fmin(properties.size, bucket.properties.max_size);
matrix::scale(exMatrix, exMatrix, fontSize / 24.0f, fontSize / 24.0f, 1.0f);
const mat4 &vtxMatrix = translatedMatrix(properties.translate, id, properties.translateAnchor);
@@ -48,10 +48,10 @@ void Painter::renderText(TextBucket& bucket, const std::string& layer_name, cons
float angle = std::round((map.getState().getAngle() + rotate) / M_PI * 128);
// adjust min/max zooms for variable font sies
float zoomAdjust = log(fontSize / bucket.geom_desc.size) / log(2);
float zoomAdjust = log(fontSize / bucket.properties.max_size) / log(2);
textShader->setAngle((int32_t)(angle + 256) % 256);
textShader->setFlip(bucket.geom_desc.path == TextPathType::Curve ? 1 : 0);
textShader->setFlip(bucket.properties.path == TextPathType::Curve ? 1 : 0);
textShader->setZoom((map.getState().getNormalizedZoom() - zoomAdjust) * 10); // current zoom level
// Label fading

View File

@@ -22,8 +22,8 @@ using namespace llmr;
TextBucket::TextBucket(
TextVertexBuffer &vertexBuffer,
TriangleElementsBuffer &triangleElementsBuffer,
const BucketDescription &bucket_desc, Placement &placement)
: geom_desc(bucket_desc.geometry),
const BucketTextDescription &properties, Placement &placement)
: properties(properties),
vertexBuffer(vertexBuffer),
triangleElementsBuffer(triangleElementsBuffer),
placement(placement),
@@ -113,14 +113,14 @@ void TextBucket::addFeature(const pbf &geom_pbf,
while ((cmd = geometry.next(x, y)) != Geometry::end) {
if (cmd == Geometry::move_to) {
if (!line.empty()) {
placement.addFeature(*this, line, geom_desc, face, shaping);
placement.addFeature(*this, line, properties, face, shaping);
line.clear();
}
}
line.emplace_back(x, y);
}
if (line.size()) {
placement.addFeature(*this, line, geom_desc, face, shaping);
placement.addFeature(*this, line, properties, face, shaping);
}
}

View File

@@ -8,10 +8,6 @@ std::ostream& llmr::operator<<(std::ostream& os, const BucketDescription& bucket
os << " - type: " << (uint32_t)bucket.type << std::endl;
os << " - source_name: " << bucket.source_name << std::endl;
os << " - source_layer: " << bucket.source_layer << std::endl;
os << " - cap: " << (uint32_t)bucket.geometry.cap << std::endl;
os << " - join: " << (uint32_t)bucket.geometry.join << std::endl;
os << " - font: " << bucket.geometry.font << std::endl;
os << " - size: " << bucket.geometry.size << std::endl;
return os;
}

View File

@@ -0,0 +1,9 @@
#include <llmr/style/class_description.hpp>
namespace llmr {
void ClassDescription::addClass(const std::string &name, ClassProperties &&properties) {
styles.emplace(name, std::move(properties));
}
}

View File

@@ -25,36 +25,275 @@ void Style::reset() {
properties_to_transition.clear();
}
void Style::cascadeProperties(GenericProperties &properties, const GenericClass& klass, const std::string& layer_name, const std::string& class_name, float z) {
if (klass.enabled) {
properties.enabled = klass.enabled.get().evaluate<bool>(z);
void Style::cascadeProperties(GenericProperties &properties, const ClassProperties& klass, const std::string& layer_name, const std::string& class_name, float z) {
using Key = ClassPropertyKey;
if (const FunctionProperty *enabled = klass.get<FunctionProperty>(Key::Enabled)) {
properties.enabled = enabled->evaluate<bool>(z);
}
if (klass.translate) {
properties.translate = {{ klass.translate.get()[0].evaluate<float>(z),
klass.translate.get()[1].evaluate<float>(z) }};
if (const FunctionProperty *translateX = klass.get<FunctionProperty>(Key::TranslateX)) {
properties.translate[0] = translateX->evaluate<float>(z);
computed.effective_classes[layer_name][TransitionablePropertyKey::Translate] = class_name;
if (klass.translate_transition && klass.translate_transition.get().duration) {
properties_to_transition[layer_name].emplace(TransitionablePropertyKey::Translate, klass.translate_transition.get());
}
if (const FunctionProperty *translateY = klass.get<FunctionProperty>(Key::TranslateY)) {
properties.translate[1] = translateY->evaluate<float>(z);
computed.effective_classes[layer_name][TransitionablePropertyKey::Translate] = class_name;
}
if (const PropertyTransition *translateTransition = klass.get<PropertyTransition>(Key::TranslateTransition)) {
if (translateTransition->duration) {
properties_to_transition[layer_name].emplace(TransitionablePropertyKey::Translate, *translateTransition);
}
}
if (klass.translateAnchor) {
properties.translateAnchor = klass.translateAnchor.get();
if (const TranslateAnchor *translateAnchor = klass.get<TranslateAnchor>(Key::TranslateAnchor)) {
properties.translateAnchor = *translateAnchor;
}
if (klass.opacity) {
properties.opacity = klass.opacity.get().evaluate<float>(z);
if (const FunctionProperty *opacity = klass.get<FunctionProperty>(Key::Opacity)) {
properties.opacity = opacity->evaluate<float>(z);
computed.effective_classes[layer_name][TransitionablePropertyKey::Opacity] = class_name;
if (klass.opacity_transition && klass.opacity_transition.get().duration) {
properties_to_transition[layer_name].emplace(TransitionablePropertyKey::Opacity, klass.opacity_transition.get());
}
if (const PropertyTransition *opacityTransition = klass.get<PropertyTransition>(Key::OpacityTransition)) {
if (opacityTransition->duration) {
properties_to_transition[layer_name].emplace(TransitionablePropertyKey::Opacity, *opacityTransition);
}
}
properties.prerender = klass.prerender;
properties.prerenderBuffer = klass.prerenderBuffer;
properties.prerenderSize = klass.prerenderSize;
properties.prerenderBlur = klass.prerenderBlur;
if (const FunctionProperty *prerender = klass.get<FunctionProperty>(Key::Prerender)) {
properties.prerender = *prerender;
}
if (const FunctionProperty *prerenderBuffer = klass.get<FunctionProperty>(Key::PrerenderBuffer)) {
properties.prerenderBuffer = *prerenderBuffer;
}
if (const FunctionProperty *prerenderSize = klass.get<FunctionProperty>(Key::PrerenderSize)) {
properties.prerenderSize = *prerenderSize;
}
if (const FunctionProperty *prerenderBlur = klass.get<FunctionProperty>(Key::PrerenderBlur)) {
properties.prerenderBlur = *prerenderBlur;
}
}
void Style::cascadeProperties(FillProperties &properties, const ClassProperties& klass, const std::string& layer_name, const std::string& class_name, float z) {
cascadeProperties(reinterpret_cast<GenericProperties &>(properties), klass, layer_name, class_name, z);
using Key = ClassPropertyKey;
if (const Winding *winding = klass.get<Winding>(Key::FillWinding)) {
properties.winding = *winding;
}
if (const FunctionProperty *antialias = klass.get<FunctionProperty>(Key::FillAntialias)) {
properties.antialias = antialias->evaluate<bool>(z);
}
if (const Color *fillColor = klass.get<Color>(Key::FillColor)) {
properties.fill_color = *fillColor;
computed.effective_classes[layer_name][TransitionablePropertyKey::FillColor] = class_name;
}
if (const PropertyTransition *fillColorTransition = klass.get<PropertyTransition>(Key::FillColorTransition)) {
if (fillColorTransition->duration) {
properties_to_transition[layer_name].emplace(TransitionablePropertyKey::FillColor, *fillColorTransition);
}
}
if (const Color *fillStrokeColor = klass.get<Color>(Key::FillStrokeColor)) {
properties.stroke_color = *fillStrokeColor;
computed.effective_classes[layer_name][TransitionablePropertyKey::StrokeColor] = class_name;
} else if (const Color *fillColor = klass.get<Color>(Key::FillColor)) {
properties.stroke_color = *fillColor;
computed.effective_classes[layer_name][TransitionablePropertyKey::StrokeColor] = class_name;
if (const PropertyTransition *fillColorTransition = klass.get<PropertyTransition>(Key::FillColorTransition)) {
if (fillColorTransition->duration) {
properties_to_transition[layer_name].emplace(TransitionablePropertyKey::FillColor, *fillColorTransition);
}
}
}
if (const std::string *image = klass.get<std::string>(Key::FillImage)) {
properties.image = *image;
}
}
void Style::cascadeProperties(LineProperties &properties, const ClassProperties& klass, const std::string& layer_name, const std::string& class_name, float z) {
cascadeProperties(reinterpret_cast<GenericProperties &>(properties), klass, layer_name, class_name, z);
using Key = ClassPropertyKey;
if (const FunctionProperty *width = klass.get<FunctionProperty>(Key::LineWidth)) {
properties.width = width->evaluate<float>(z);
computed.effective_classes[layer_name][TransitionablePropertyKey::Width] = class_name;
}
if (const PropertyTransition *width_transition = klass.get<PropertyTransition>(Key::LineWidthTransition)) {
properties_to_transition[layer_name].emplace(TransitionablePropertyKey::Width, *width_transition);
}
if (const FunctionProperty *offset = klass.get<FunctionProperty>(Key::LineOffset)) {
properties.offset = offset->evaluate<float>(z);
computed.effective_classes[layer_name][TransitionablePropertyKey::Offset] = class_name;
}
if (const PropertyTransition *offset_transition = klass.get<PropertyTransition>(Key::LineOffsetTransition)) {
properties_to_transition[layer_name].emplace(TransitionablePropertyKey::Offset, *offset_transition);
}
if (const Color *color = klass.get<Color>(Key::LineColor)) {
properties.color = *color;
computed.effective_classes[layer_name][TransitionablePropertyKey::Color] = class_name;
}
if (const PropertyTransition *color_transition = klass.get<PropertyTransition>(Key::LineColorTransition)) {
properties_to_transition[layer_name].emplace(TransitionablePropertyKey::Color, *color_transition);
}
if (const FunctionProperty *dash_array = klass.get<FunctionProperty>(Key::LineDashLand)) {
properties.dash_array[0] = dash_array->evaluate<float>(z);
computed.effective_classes[layer_name][TransitionablePropertyKey::DashArray] = class_name;
}
if (const FunctionProperty *dash_array = klass.get<FunctionProperty>(Key::LineDashGap)) {
properties.dash_array[1] = dash_array->evaluate<float>(z);
computed.effective_classes[layer_name][TransitionablePropertyKey::DashArray] = class_name;
}
if (const PropertyTransition *dash_array_transition = klass.get<PropertyTransition>(Key::LineDashTransition)) {
properties_to_transition[layer_name].emplace(TransitionablePropertyKey::DashArray, *dash_array_transition);
}
}
void Style::cascadeProperties(TextProperties &properties, const ClassProperties& klass, const std::string& layer_name, const std::string& class_name, float z) {
cascadeProperties(reinterpret_cast<GenericProperties &>(properties), klass, layer_name, class_name, z);
using Key = ClassPropertyKey;
if (const Color *color = klass.get<Color>(Key::TextColor)) {
properties.color = *color;
computed.effective_classes[layer_name][TransitionablePropertyKey::Color] = class_name;
}
if (const PropertyTransition *colorTransition = klass.get<PropertyTransition>(Key::TextColorTransition)) {
properties_to_transition[layer_name].emplace(TransitionablePropertyKey::Color, *colorTransition);
}
if (const FunctionProperty *size = klass.get<FunctionProperty>(Key::TextSize)) {
properties.size = size->evaluate<float>(z);
}
if (const Color *haloColor = klass.get<Color>(Key::TextHaloColor)) {
properties.halo = *haloColor;
computed.effective_classes[layer_name][TransitionablePropertyKey::Halo] = class_name;
}
if (const PropertyTransition *haloColorTransition = klass.get<PropertyTransition>(Key::TextHaloColorTransition)) {
properties_to_transition[layer_name].emplace(TransitionablePropertyKey::Halo, *haloColorTransition);
}
if (const FunctionProperty *haloRadius = klass.get<FunctionProperty>(Key::TextHaloRadius)) {
properties.halo_radius = haloRadius->evaluate<float>(z);
computed.effective_classes[layer_name][TransitionablePropertyKey::HaloRadius] = class_name;
}
if (const PropertyTransition *haloRadiusTransition = klass.get<PropertyTransition>(Key::TextHaloRadiusTransition)) {
properties_to_transition[layer_name].emplace(TransitionablePropertyKey::HaloRadius, *haloRadiusTransition);
}
if (const FunctionProperty *haloBlur = klass.get<FunctionProperty>(Key::TextHaloBlur)) {
properties.halo_blur = haloBlur->evaluate<float>(z);
computed.effective_classes[layer_name][TransitionablePropertyKey::HaloBlur] = class_name;
}
if (const PropertyTransition *haloBlurTransition = klass.get<PropertyTransition>(Key::TextHaloBlurTransition)) {
properties_to_transition[layer_name].emplace(TransitionablePropertyKey::HaloBlur, *haloBlurTransition);
}
if (const FunctionProperty *rotate = klass.get<FunctionProperty>(Key::TextRotate)) {
properties.rotate = rotate->evaluate<float>(z);
}
if (const FunctionProperty *alwaysVisible = klass.get<FunctionProperty>(Key::TextAlwaysVisible)) {
properties.always_visible = alwaysVisible->evaluate<bool>(z);
}
}
void Style::cascadeProperties(IconProperties &properties, const ClassProperties& klass, const std::string& layer_name, const std::string& class_name, float z) {
cascadeProperties(reinterpret_cast<GenericProperties &>(properties), klass, layer_name, class_name, z);
using Key = ClassPropertyKey;
if (const Color *color = klass.get<Color>(Key::IconColor)) {
properties.color = *color;
computed.effective_classes[layer_name][TransitionablePropertyKey::Color] = class_name;
}
if (const PropertyTransition *colorTransition = klass.get<PropertyTransition>(Key::IconColorTransition)) {
if (colorTransition->duration) {
properties_to_transition[layer_name].emplace(TransitionablePropertyKey::Color, *colorTransition);
}
}
if (const FunctionProperty *size = klass.get<FunctionProperty>(Key::IconSize)) {
properties.size = size->evaluate<float>(z);
}
if (const std::string *image = klass.get<std::string>(Key::IconImage)) {
properties.image = *image;
}
if (const FunctionProperty *radius = klass.get<FunctionProperty>(Key::IconRadius)) {
properties.radius = radius->evaluate<float>(z);
computed.effective_classes[layer_name][TransitionablePropertyKey::Radius] = class_name;
}
if (const PropertyTransition *radiusTransition = klass.get<PropertyTransition>(Key::IconRadiusTransition)) {
if (radiusTransition->duration) {
properties_to_transition[layer_name].emplace(TransitionablePropertyKey::Radius, *radiusTransition);
}
}
if (const FunctionProperty *blur = klass.get<FunctionProperty>(Key::IconBlur)) {
properties.blur = blur->evaluate<float>(z);
computed.effective_classes[layer_name][TransitionablePropertyKey::Blur] = class_name;
}
if (const PropertyTransition *blurTransition = klass.get<PropertyTransition>(Key::IconBlurTransition)) {
if (blurTransition->duration) {
properties_to_transition[layer_name].emplace(TransitionablePropertyKey::Blur, *blurTransition);
}
}
}
void Style::cascadeProperties(RasterProperties &properties, const ClassProperties& klass, const std::string& layer_name, const std::string& class_name, float z) {
cascadeProperties(reinterpret_cast<GenericProperties &>(properties), klass, layer_name, class_name, z);
}
void Style::cascadeProperties(CompositeProperties &properties, const ClassProperties& klass, const std::string& layer_name, const std::string& class_name, float z) {
cascadeProperties(reinterpret_cast<GenericProperties &>(properties), klass, layer_name, class_name, z);
}
void Style::cascadeProperties(BackgroundProperties &properties, const ClassProperties& klass, const std::string& layer_name, const std::string& class_name, float z) {
cascadeProperties(reinterpret_cast<GenericProperties &>(properties), klass, layer_name, class_name, z);
using Key = ClassPropertyKey;
if (const Color *backgroundColor = klass.get<Color>(Key::BackgroundColor)) {
properties.color = *backgroundColor;
computed.effective_classes[layer_name][TransitionablePropertyKey::Color] = class_name;
}
if (const PropertyTransition *colorTransition = klass.get<PropertyTransition>(Key::TextColorTransition)) {
if (colorTransition->duration) {
properties_to_transition[layer_name].emplace(TransitionablePropertyKey::Color, *colorTransition);
}
}
}
@@ -85,217 +324,38 @@ void Style::cascade(float z) {
const std::string& class_name = class_pair.first;
const ClassDescription& sheetClass = class_pair.second;
using Key = ClassPropertyKey;
// Skip if not enabled.
if (appliedClasses.find(class_name) == appliedClasses.end()) continue;
// Cascade fill classes.
for (const auto& fill_pair : sheetClass.fill) {
const std::string& layer_name = fill_pair.first;
const llmr::FillClass& layer = fill_pair.second;
for (const auto &pair : sheetClass) {
const std::string& layer_name = pair.first;
const ClassProperties& properties = pair.second;
llmr::FillProperties& fill = computed.fills[layer_name];
cascadeProperties(fill, layer, layer_name, class_name, z);
if (layer.winding) {
fill.winding = layer.winding.get();
}
if (layer.antialias) {
fill.antialias = layer.antialias.get().evaluate<bool>(z);
}
if (layer.fill_color) {
fill.fill_color = layer.fill_color.get();
computed.effective_classes[layer_name][TransitionablePropertyKey::FillColor] = class_name;
if (layer.fill_color_transition && layer.fill_color_transition.get().duration) {
properties_to_transition[layer_name].emplace(TransitionablePropertyKey::FillColor, layer.fill_color_transition.get());
}
}
if (layer.stroke_color) {
fill.stroke_color = layer.stroke_color.get();
computed.effective_classes[layer_name][TransitionablePropertyKey::StrokeColor] = class_name;
if (layer.stroke_color_transition && layer.stroke_color_transition.get().duration) {
properties_to_transition[layer_name].emplace(TransitionablePropertyKey::StrokeColor, layer.stroke_color_transition.get());
}
}
if (layer.image) {
fill.image = layer.image.get();
}
}
// Cascade line classes.
for (const auto& line_pair : sheetClass.line) {
const std::string& layer_name = line_pair.first;
const llmr::LineClass& layer = line_pair.second;
llmr::LineProperties& stroke = computed.lines[layer_name];
cascadeProperties(stroke, layer, layer_name, class_name, z);
if (layer.width) {
stroke.width = layer.width.get().evaluate<float>(z);
computed.effective_classes[layer_name][TransitionablePropertyKey::Width] = class_name;
if (layer.width_transition && layer.width_transition.get().duration) {
properties_to_transition[layer_name].emplace(TransitionablePropertyKey::Width, layer.width_transition.get());
}
}
if (layer.offset) {
stroke.offset = layer.offset.get().evaluate<float>(z);
computed.effective_classes[layer_name][TransitionablePropertyKey::Offset] = class_name;
if (layer.offset_transition && layer.offset_transition.get().duration) {
properties_to_transition[layer_name].emplace(TransitionablePropertyKey::Offset, layer.offset_transition.get());
}
}
if (layer.color) {
stroke.color = layer.color.get();
computed.effective_classes[layer_name][TransitionablePropertyKey::Color] = class_name;
if (layer.color_transition && layer.color_transition.get().duration) {
properties_to_transition[layer_name].emplace(TransitionablePropertyKey::Color, layer.color_transition.get());
}
}
if (layer.dash_array) {
stroke.dash_array = {{ layer.dash_array.get()[0].evaluate<float>(z),
layer.dash_array.get()[1].evaluate<float>(z) }};
computed.effective_classes[layer_name][TransitionablePropertyKey::DashArray] = class_name;
if (layer.dash_array_transition && layer.dash_array_transition.get().duration) {
properties_to_transition[layer_name].emplace(TransitionablePropertyKey::DashArray, layer.dash_array_transition.get());
}
}
}
// Cascade icon classes.
for (const auto& icon_pair : sheetClass.icon) {
const std::string& layer_name = icon_pair.first;
const llmr::IconClass& layer = icon_pair.second;
llmr::IconProperties& icon = computed.icons[layer_name];
cascadeProperties(icon, layer, layer_name, class_name, z);
if (layer.color) {
icon.color = layer.color.get();
computed.effective_classes[layer_name][TransitionablePropertyKey::Color] = class_name;
if (layer.color_transition && layer.color_transition.get().duration) {
properties_to_transition[layer_name].emplace(TransitionablePropertyKey::Color, layer.color_transition.get());
}
}
if (layer.size) {
icon.size = layer.size.get().evaluate<float>(z);
}
if (layer.image) {
icon.image = layer.image.get();
}
if (layer.radius) {
icon.radius = layer.radius.get().evaluate<float>(z);
computed.effective_classes[layer_name][TransitionablePropertyKey::Radius] = class_name;
if (layer.radius_transition && layer.radius_transition.get().duration) {
properties_to_transition[layer_name].emplace(TransitionablePropertyKey::Radius, layer.radius_transition.get());
}
}
if (layer.blur) {
icon.blur = layer.blur.get().evaluate<float>(z);
computed.effective_classes[layer_name][TransitionablePropertyKey::Blur] = class_name;
if (layer.blur_transition && layer.blur_transition.get().duration) {
properties_to_transition[layer_name].emplace(TransitionablePropertyKey::Blur, layer.blur_transition.get());
}
}
}
// Cascade text classes.
for (const auto& text_pair : sheetClass.text) {
const std::string& layer_name = text_pair.first;
const llmr::TextClass& layer = text_pair.second;
llmr::TextProperties& text = computed.texts[layer_name];
cascadeProperties(text, layer, layer_name, class_name, z);
if (layer.color) {
text.color = layer.color.get();
computed.effective_classes[layer_name][TransitionablePropertyKey::Color] = class_name;
if (layer.color_transition && layer.color_transition.get().duration) {
properties_to_transition[layer_name].emplace(TransitionablePropertyKey::Color, layer.color_transition.get());
}
}
if (layer.size) {
text.size = layer.size.get().evaluate<float>(z);
}
if (layer.halo) {
text.halo = layer.halo.get();
computed.effective_classes[layer_name][TransitionablePropertyKey::Halo] = class_name;
if (layer.halo_transition && layer.halo_transition.get().duration) {
properties_to_transition[layer_name].emplace(TransitionablePropertyKey::Halo, layer.halo_transition.get());
}
}
if (layer.halo_radius) {
text.halo_radius = layer.halo_radius.get().evaluate<float>(z);
computed.effective_classes[layer_name][TransitionablePropertyKey::HaloRadius] = class_name;
if (layer.halo_radius_transition && layer.halo_radius_transition.get().duration) {
properties_to_transition[layer_name].emplace(TransitionablePropertyKey::HaloRadius, layer.halo_radius_transition.get());
}
}
if (layer.halo_blur) {
text.halo_blur = layer.halo_blur.get().evaluate<float>(z);
computed.effective_classes[layer_name][TransitionablePropertyKey::HaloBlur] = class_name;
if (layer.halo_blur_transition && layer.halo_blur_transition.get().duration) {
properties_to_transition[layer_name].emplace(TransitionablePropertyKey::HaloBlur, layer.halo_blur_transition.get());
}
}
if (layer.rotate) {
text.rotate = layer.rotate.get().evaluate<float>(z);
}
if (layer.always_visible) {
text.always_visible = layer.always_visible.get().evaluate<bool>(z);
}
}
// Cascade raster classes.
for (const auto& raster_pair : sheetClass.raster) {
const std::string& layer_name = raster_pair.first;
const llmr::RasterClass& layer = raster_pair.second;
llmr::RasterProperties& raster = computed.rasters[layer_name];
cascadeProperties(raster, layer, layer_name, class_name, z);
}
// Cascade composite classes.
for (const auto& composite_pair : sheetClass.composite) {
const std::string& layer_name = composite_pair.first;
const llmr::CompositeClass& layer = composite_pair.second;
llmr::CompositeProperties& composite = computed.composites[layer_name];
cascadeProperties(composite, layer, layer_name, class_name, z);
}
// Cascade background.
{
cascadeProperties(computed.background, sheetClass.background, "background", class_name, z);
if (sheetClass.background.color) {
computed.background.color = sheetClass.background.color.get();
computed.effective_classes["background"][TransitionablePropertyKey::Color] = class_name;
if (sheetClass.background.color_transition && sheetClass.background.color_transition.get().duration) {
properties_to_transition["background"].emplace(TransitionablePropertyKey::Color, sheetClass.background.color_transition.get());
}
// Find out what bucket this class is
switch (properties.type) {
case RenderType::Fill:
cascadeProperties(computed.fills[layer_name], properties, layer_name, class_name, z);
break;
case RenderType::Line:
cascadeProperties(computed.lines[layer_name], properties, layer_name, class_name, z);
break;
case RenderType::Icon:
cascadeProperties(computed.icons[layer_name], properties, layer_name, class_name, z);
break;
case RenderType::Text:
cascadeProperties(computed.texts[layer_name], properties, layer_name, class_name, z);
break;
case RenderType::Raster:
cascadeProperties(computed.rasters[layer_name], properties, layer_name, class_name, z);
break;
case RenderType::Composite:
cascadeProperties(computed.composites[layer_name], properties, layer_name, class_name, z);
break;
case RenderType::Background:
cascadeProperties(computed.background, properties, layer_name, class_name, z);
break;
}
}
}

View File

@@ -1,8 +1,9 @@
#include <llmr/style/style_parser.hpp>
#include <llmr/style/properties.hpp>
#include <llmr/util/constants.hpp>
#include <csscolorparser/csscolorparser.hpp>
using namespace llmr;
namespace llmr {
using JSVal = const rapidjson::Value&;
@@ -73,134 +74,40 @@ PropertyFilterExpression StyleParser::parseFilterOrExpression(JSVal value) {
BucketDescription StyleParser::parseBucket(JSVal value) {
BucketDescription bucket;
rapidjson::Value::ConstMemberIterator itr = value.MemberBegin();
for (; itr != value.MemberEnd(); ++itr) {
const std::string name(itr->name.GetString(), itr->name.GetStringLength());
JSVal value = itr->value;
if (name == "type") {
if (value.IsString()) {
bucket.type = bucketType({ value.GetString(), value.GetStringLength() });
} else {
throw Style::exception("bucket type must be a string");
}
} else if (name == "feature_type") {
if (value.IsString()) {
bucket.feature_type = bucketType({ value.GetString(), value.GetStringLength() });
} else {
throw Style::exception("feature type must be a string");
}
} else if (name == "source") {
if (value.IsString()) {
bucket.source_name = { value.GetString(), value.GetStringLength() };
} else {
throw Style::exception("source name must be a string");
}
} else if (name == "layer") {
if (value.IsString()) {
bucket.source_layer = { value.GetString(), value.GetStringLength() };
} else {
throw Style::exception("layer name must be a string");
}
} else if (name == "cap") {
if (value.IsString()) {
bucket.geometry.cap = capType({ value.GetString(), value.GetStringLength() });
} else {
throw Style::exception("cap type must be a string");
}
} else if (name == "join") {
if (value.IsString()) {
bucket.geometry.join = joinType({ value.GetString(), value.GetStringLength() });
} else {
throw Style::exception("join type must be a string");
}
} else if (name == "font") {
if (value.IsString()) {
bucket.geometry.font = { value.GetString(), value.GetStringLength() };
} else {
throw Style::exception("font stack must be a string");
}
} else if (name == "fontSize" || name == "size") {
if (value.IsNumber()) {
bucket.geometry.size = value.GetDouble();
} else {
throw Style::exception("font size must be a number");
}
} else if (name == "text_field" || name == "icon") {
if (value.IsString()) {
bucket.geometry.field = { value.GetString(), value.GetStringLength() };
} else {
throw Style::exception("text field must be a string");
}
} else if (name == "path") {
if (value.IsString()) {
bucket.geometry.path = textPathType({ value.GetString(), value.GetStringLength() });
} else {
throw Style::exception("curve must be a string");
}
} else if (name == "alignment") {
if (value.IsString()) {
bucket.geometry.alignment = alignmentType({ value.GetString(), value.GetStringLength() });
} else {
throw Style::exception("alignment must be a string");
}
} else if (name == "translate") {
if (value.IsArray()) {
bucket.geometry.translate.x = value[(rapidjson::SizeType)0].GetDouble() * 24;
bucket.geometry.translate.y = value[(rapidjson::SizeType)1].GetDouble() * -24;
} else {
throw Style::exception("translate must be a string");
}
} else if (name == "verticalAlignment") {
if (value.IsString()) {
bucket.geometry.vertical_alignment = verticalAlignmentType({ value.GetString(), value.GetStringLength() });
} else {
throw Style::exception("verticalAlignment must be a string");
}
} else if (name == "lineHeight") {
if (value.IsNumber()) {
bucket.geometry.line_height = value.GetDouble() * 24;
} else {
throw Style::exception("line height must be a number");
}
} else if (name == "maxWidth") {
if (value.IsNumber()) {
bucket.geometry.max_width = value.GetDouble() * 24;
} else {
throw Style::exception("max width must be a number");
}
} else if (name == "letterSpacing") {
if (value.IsNumber()) {
bucket.geometry.letter_spacing = value.GetDouble() * 24;
} else {
throw Style::exception("letter spacing must be a number");
}
} else if (name == "miterLimit") {
if (value.IsNumber()) {
bucket.geometry.miter_limit = value.GetDouble();
} else {
throw Style::exception("miter limit must be a number");
}
} else if (name == "roundLimit") {
if (value.IsNumber()) {
bucket.geometry.round_limit = value.GetDouble();
} else {
throw Style::exception("round limit must be a number");
}
} else if (name == "textMinDistance") {
if (value.IsNumber()) {
bucket.geometry.textMinDistance = value.GetDouble();
} else {
throw Style::exception("text min distance must be a number");
}
} else if (name == "maxAngleDelta") {
if (value.IsNumber()) {
bucket.geometry.maxAngleDelta = value.GetDouble();
} else {
throw Style::exception("max angle delta must be a number");
}
if (value.HasMember("type")) {
JSVal type = value["type"];
if (type.IsString()) {
bucket.type = bucketType({ type.GetString(), type.GetStringLength() });
} else {
throw Style::exception("bucket type must be a string");
}
}
if (value.HasMember("feature_type")) {
JSVal feature_type = value["feature_type"];
if (feature_type.IsString()) {
bucket.feature_type = bucketType({ feature_type.GetString(), feature_type.GetStringLength() });
} else {
throw Style::exception("feature type must be a string");
}
}
if (value.HasMember("source")) {
JSVal source = value["source"];
if (source.IsString()) {
bucket.source_name = { source.GetString(), source.GetStringLength() };
} else {
throw Style::exception("source name must be a string");
}
}
if (value.HasMember("layer")) {
JSVal source_layer = value["layer"];
if (source_layer.IsString()) {
bucket.source_layer = { source_layer.GetString(), source_layer.GetStringLength() };
} else {
throw Style::exception("layer name must be a string");
}
}
if (value.HasMember("filter")) {
@@ -214,6 +121,206 @@ BucketDescription StyleParser::parseBucket(JSVal value) {
bucket.feature_type = bucket.type;
}
switch (bucket.type) {
case BucketType::Fill: {
bucket.render = BucketFillDescription{};
} break;
case BucketType::Line: {
bucket.render = BucketLineDescription{};
BucketLineDescription &render = bucket.render.get<BucketLineDescription>();
if (value.HasMember("cap")) {
JSVal property = value["cap"];
if (property.IsString()) {
render.cap = capType({property.GetString(), property.GetStringLength()});
} else {
throw Style::exception("cap type must be a string");
}
}
if (value.HasMember("join")) {
JSVal property = value["join"];
if (property.IsString()) {
render.join = joinType({property.GetString(), property.GetStringLength()});
} else {
throw Style::exception("join type must be a string");
}
}
if (value.HasMember("miterLimit")) {
JSVal property = value["miterLimit"];
if (property.IsNumber()) {
render.miter_limit = property.GetDouble();
} else {
throw Style::exception("miter limit must be a number");
}
}
if (value.HasMember("roundLimit")) {
JSVal property = value["roundLimit"];
if (property.IsNumber()) {
render.round_limit = property.GetDouble();
} else {
throw Style::exception("round limit must be a number");
}
}
} break;
case BucketType::Icon: {
bucket.render = BucketIconDescription{};
BucketIconDescription &render = bucket.render.get<BucketIconDescription>();
if (value.HasMember("size")) {
JSVal property = value["size"];
if (property.IsNumber()) {
render.size = property.GetDouble();
} else {
throw Style::exception("font size must be a number");
}
}
if (value.HasMember("icon")) {
JSVal property = value["icon"];
if (property.IsString()) {
render.icon = {property.GetString(), property.GetStringLength()};
} else {
throw Style::exception("text field must be a string");
}
}
if (value.HasMember("translate")) {
JSVal property = value["translate"];
if (property.IsArray()) {
render.translate.x = property[(rapidjson::SizeType)0].GetDouble() * 24;
render.translate.y = property[(rapidjson::SizeType)1].GetDouble() * -24;
} else {
throw Style::exception("translate must be a string");
}
}
} break;
case BucketType::Text: {
bucket.render = BucketTextDescription{};
BucketTextDescription &render = bucket.render.get<BucketTextDescription>();
if (value.HasMember("text_field")) {
JSVal property = value["text_field"];
if (property.IsString()) {
render.field = {property.GetString(), property.GetStringLength()};
} else {
throw Style::exception("text field must be a string");
}
}
if (value.HasMember("path")) {
JSVal property = value["path"];
if (property.IsString()) {
render.path = textPathType({property.GetString(), property.GetStringLength()});
} else {
throw Style::exception("curve must be a string");
}
}
if (value.HasMember("font")) {
JSVal property = value["font"];
if (property.IsString()) {
render.font = {property.GetString(), property.GetStringLength()};
} else {
throw Style::exception("font stack must be a string");
}
}
if (value.HasMember("fontSize")) {
JSVal property = value["fontSize"];
if (property.IsNumber()) {
render.max_size = property.GetDouble();
} else {
throw Style::exception("font size must be a number");
}
}
if (value.HasMember("maxWidth")) {
JSVal property = value["maxWidth"];
if (property.IsNumber()) {
render.max_width = property.GetDouble() * 24;
} else {
throw Style::exception("max width must be a number");
}
}
if (value.HasMember("lineHeight")) {
JSVal property = value["lineHeight"];
if (property.IsNumber()) {
render.line_height = property.GetDouble() * 24;
} else {
throw Style::exception("line height must be a number");
}
}
if (value.HasMember("letterSpacing")) {
JSVal property = value["letterSpacing"];
if (property.IsNumber()) {
render.letter_spacing = property.GetDouble() * 24;
} else {
throw Style::exception("letter spacing must be a number");
}
}
if (value.HasMember("alignment")) {
JSVal property = value["alignment"];
if (property.IsString()) {
render.alignment =
alignmentType({property.GetString(), property.GetStringLength()});
} else {
throw Style::exception("alignment must be a string");
}
}
if (value.HasMember("verticalAlignment")) {
JSVal property = value["verticalAlignment"];
if (property.IsString()) {
render.vertical_alignment =
verticalAlignmentType({property.GetString(), property.GetStringLength()});
} else {
throw Style::exception("verticalAlignment must be a string");
}
}
if (value.HasMember("translate")) {
JSVal property = value["translate"];
if (property.IsArray()) {
render.translate.x = property[(rapidjson::SizeType)0].GetDouble() * 24;
render.translate.y = property[(rapidjson::SizeType)1].GetDouble() * -24;
} else {
throw Style::exception("translate must be a string");
}
}
if (value.HasMember("maxAngleDelta")) {
JSVal property = value["maxAngleDelta"];
if (property.IsNumber()) {
render.max_angle_delta = property.GetDouble();
} else {
throw Style::exception("max angle delta must be a number");
}
}
if (value.HasMember("textMinDistance")) {
JSVal property = value["textMinDistance"];
if (property.IsNumber()) {
render.min_distance = property.GetDouble();
} else {
throw Style::exception("text min distance must be a number");
}
}
} break;
case BucketType::Raster: {
bucket.render = BucketRasterDescription{};
} break;
default:
break;
}
return bucket;
}
@@ -275,7 +382,7 @@ void StyleParser::parseConstants(JSVal value) {
}
}
TranslateAnchor parseTranslateAnchor(JSVal anchor) {
template<> TranslateAnchor StyleParser::parse(JSVal anchor) {
if (anchor.IsString()) {
std::string a { anchor.GetString(), anchor.GetStringLength() };
if (a == "viewport") {
@@ -376,10 +483,10 @@ void StyleParser::parseClass(const std::string &name, JSVal value, ClassDescript
const std::string &bucket_name = layer_bucket_it->second;
if (bucket_name == "background") {
// background buckets are fake
class_desc.background = parseBackgroundClass(value);
class_desc.addClass("background", parseBackgroundClass(value));
} else if (bucket_name.length() == 0) {
// no bucket name == composite bucket.
class_desc.composite.insert({ name, std::forward<CompositeClass>(parseCompositeClass(value)) });
class_desc.addClass(name, parseCompositeClass(value));
} else {
auto bucket_it = buckets.find(bucket_name);
if (bucket_it == buckets.end()) {
@@ -391,22 +498,22 @@ void StyleParser::parseClass(const std::string &name, JSVal value, ClassDescript
BucketType type = bucket_it->second.type;
if (type == BucketType::Fill) {
class_desc.fill.insert({ name, std::forward<FillClass>(parseFillClass(value)) });
class_desc.addClass(name, parseFillClass(value));
} else if (type == BucketType::Line) {
class_desc.line.insert({ name, std::forward<LineClass>(parseLineClass(value)) });
class_desc.addClass(name, parseLineClass(value));
} else if (type == BucketType::Icon) {
class_desc.icon.insert({ name, std::forward<IconClass>(parseIconClass(value)) });
class_desc.addClass(name, parseIconClass(value));
} else if (type == BucketType::Text) {
class_desc.text.insert({ name, std::forward<TextClass>(parseTextClass(value)) });
class_desc.addClass(name, parseTextClass(value));
} else if (type == BucketType::Raster) {
class_desc.raster.insert({ name, std::forward<RasterClass>(parseRasterClass(value)) });
class_desc.addClass(name, parseRasterClass(value));
} else {
throw Style::exception("unknown class type name");
}
}
}
bool StyleParser::parseBoolean(JSVal value) {
template<> bool StyleParser::parse(JSVal value) {
if (!value.IsBool()) {
throw Style::exception("boolean value must be a boolean");
}
@@ -414,7 +521,7 @@ bool StyleParser::parseBoolean(JSVal value) {
return value.GetBool();
}
std::string StyleParser::parseString(JSVal value) {
template<> std::string StyleParser::parse(JSVal value) {
if (!value.IsString()) {
throw Style::exception("string value must be a string");
}
@@ -434,26 +541,7 @@ JSVal StyleParser::replaceConstant(JSVal value) {
return value;
}
std::vector<FunctionProperty> StyleParser::parseArray(JSVal value, uint16_t expected_count) {
JSVal rvalue = replaceConstant(value);
if (!rvalue.IsArray()) {
throw Style::exception("array value must be an array");
}
if (rvalue.IsArray() && rvalue.Size() != expected_count) {
throw Style::exception("array value has unexpected number of elements");
}
std::vector<FunctionProperty> values;
values.reserve(expected_count);
for (uint16_t i = 0; i < expected_count; i++) {
values.push_back(parseFunction(rvalue[(rapidjson::SizeType)i]));
}
return values;
}
Color StyleParser::parseColor(JSVal value) {
template<> Color StyleParser::parse(JSVal value) {
JSVal rvalue = replaceConstant(value);
if (rvalue.IsArray()) {
// [ r, g, b, a] array
@@ -487,7 +575,7 @@ Color StyleParser::parseColor(JSVal value) {
css_color.a}};
}
FunctionProperty::fn StyleParser::parseFunctionType(JSVal type) {
FunctionProperty::fn parseFunctionType(JSVal type) {
if (type.IsString()) {
std::string t { type.GetString(), type.GetStringLength() };
if (t == "constant") {
@@ -510,7 +598,7 @@ FunctionProperty::fn StyleParser::parseFunctionType(JSVal type) {
}
}
FunctionProperty StyleParser::parseFunction(JSVal value) {
template<> FunctionProperty StyleParser::parse(JSVal value) {
JSVal rvalue = replaceConstant(value);
FunctionProperty property;
@@ -557,11 +645,28 @@ FunctionProperty StyleParser::parseFunction(JSVal value) {
return property;
}
boost::optional<PropertyTransition> StyleParser::parseTransition(JSVal value, std::string property_name) {
void StyleParser::parseFunctionArray(const char *property_name, const std::vector<ClassPropertyKey> &keys, ClassProperties &klass, JSVal value) {
if (value.HasMember(property_name)) {
JSVal rvalue = replaceConstant(value[property_name]);
if (!rvalue.IsArray()) {
throw Style::exception("array value must be an array");
}
if (rvalue.Size() != keys.size()) {
throw Style::exception("array value has unexpected number of elements");
}
for (uint16_t i = 0; i < keys.size(); i++) {
klass.emplace(keys[i], parse<FunctionProperty>(rvalue[(rapidjson::SizeType)i]));
}
}
}
void StyleParser::parseTransition(const char *property_name, ClassPropertyKey key, ClassProperties &klass, JSVal value) {
uint16_t duration = 0, delay = 0;
std::string transition_property = std::string("transition-").append(property_name);
if (value.HasMember(transition_property.c_str())) {
JSVal elements = value[transition_property.c_str()];
if (value.HasMember(property_name)) {
JSVal elements = value[property_name];
if (elements.IsObject()) {
if (elements.HasMember("duration") && elements["duration"].IsNumber()) {
duration = elements["duration"].GetUint();
@@ -573,201 +678,113 @@ boost::optional<PropertyTransition> StyleParser::parseTransition(JSVal value, st
}
if (duration || delay) {
return boost::optional<PropertyTransition>(PropertyTransition { duration, delay });
} else {
return boost::optional<PropertyTransition>();
klass.emplace(key, PropertyTransition { duration, delay });
}
}
void StyleParser::parseGenericClass(GenericClass &klass, JSVal value) {
if (value.HasMember("enabled")) {
klass.enabled = parseFunction(value["enabled"]);
}
if (value.HasMember("translate")) {
std::vector<FunctionProperty> values = parseArray(value["translate"], 2);
klass.translate = std::array<FunctionProperty, 2> {{ values[0], values[1] }};
klass.translate_transition = parseTransition(value, "translate");
}
if (value.HasMember("translate-anchor")) {
klass.translateAnchor = parseTranslateAnchor(value["translate-anchor"]);
}
if (value.HasMember("opacity")) {
klass.opacity = parseFunction(value["opacity"]);
klass.opacity_transition = parseTransition(value, "opacity");
}
if (value.HasMember("prerender")) {
klass.prerender = parseFunction(value["prerender"]);
}
if (value.HasMember("prerender-buffer")) {
klass.prerenderBuffer = parseFunction(value["prerender-buffer"]);
}
if (value.HasMember("prerender-size")) {
klass.prerenderSize = parseFunction(value["prerender-size"]);
}
if (value.HasMember("prerender-blur")) {
klass.prerenderBlur = parseFunction(value["prerender-blur"]);
}
void StyleParser::parseGenericClass(ClassProperties &klass, JSVal value) {
using Key = ClassPropertyKey;
parse<FunctionProperty>("enabled", Key::Enabled, klass, value);
parseFunctionArray("translate", { Key::TranslateX, Key::TranslateY }, klass, value);
parseTransition("transition-translate", Key::TranslateTransition, klass, value);
parse<TranslateAnchor>("translate-anchor", Key::TranslateAnchor, klass, value);
parse<FunctionProperty>("opacity", Key::Opacity, klass, value);
parseTransition("transition-opacity", Key::OpacityTransition, klass, value);
parse<FunctionProperty>("prerender", Key::Prerender, klass, value);
parse<FunctionProperty>("prerender-buffer", Key::PrerenderBuffer, klass, value);
parse<FunctionProperty>("prerender-size", Key::PrerenderSize, klass, value);
parse<FunctionProperty>("prerender-blur", Key::PrerenderBlur, klass, value);
}
FillClass StyleParser::parseFillClass(JSVal value) {
FillClass klass;
ClassProperties StyleParser::parseFillClass(JSVal value) {
ClassProperties klass(RenderType::Fill);
parseGenericClass(klass, value);
if (value.HasMember("winding")) {
throw std::runtime_error("winding in stylesheets not yet supported");
}
if (value.HasMember("color")) {
klass.fill_color = parseColor(value["color"]);
klass.fill_color_transition = parseTransition(value, "color");
}
if (value.HasMember("stroke")) {
klass.stroke_color = parseColor(value["stroke"]);
klass.stroke_color_transition = parseTransition(value, "stroke");
} else {
klass.stroke_color = klass.fill_color;
}
if (value.HasMember("antialias")) {
klass.antialias = parseBoolean(value["antialias"]);
}
if (value.HasMember("image")) {
klass.image = parseString(value["image"]);
}
using Key = ClassPropertyKey;
parse<Color>("color", Key::FillColor, klass, value);
parseTransition("transition-color", Key::FillColorTransition, klass, value);
parse<Color>("stroke", Key::FillStrokeColor, klass, value);
parseTransition("transition-stroke", Key::FillStrokeColorTransition, klass, value);
parse<bool>("antialias", Key::FillAntialias, klass, value);
parse<std::string>("image", Key::FillImage, klass, value);
return klass;
}
LineClass StyleParser::parseLineClass(JSVal value) {
LineClass klass;
ClassProperties StyleParser::parseLineClass(JSVal value) {
ClassProperties klass(RenderType::Line);
parseGenericClass(klass, value);
if (value.HasMember("color")) {
klass.color = parseColor(value["color"]);
klass.color_transition = parseTransition(value, "color");
}
if (value.HasMember("width")) {
klass.width = parseFunction(value["width"]);
klass.width_transition = parseTransition(value, "width");
}
if (value.HasMember("dasharray")) {
std::vector<FunctionProperty> values = parseArray(value["dasharray"], 2);
klass.dash_array = std::array<FunctionProperty, 2> {{ values[0], values[1] }};
klass.dash_array_transition = parseTransition(value, "dasharray");
}
using Key = ClassPropertyKey;
parse<Color>("color", Key::LineColor, klass, value);
parseTransition("transition-color", Key::LineColorTransition, klass, value);
parse<FunctionProperty>("width", Key::LineWidth, klass, value);
parseTransition("transition-width", Key::LineWidthTransition, klass, value);
parseFunctionArray("dasharray", { Key::LineDashLand, Key::LineDashGap }, klass, value);
parseTransition("transition-dasharray", Key::LineDashTransition, klass, value);
return klass;
}
IconClass StyleParser::parseIconClass(JSVal value) {
IconClass klass;
ClassProperties StyleParser::parseIconClass(JSVal value) {
ClassProperties klass(RenderType::Icon);
parseGenericClass(klass, value);
if (value.HasMember("color")) {
klass.color = parseColor(value["color"]);
klass.color_transition = parseTransition(value, "color");
}
if (value.HasMember("image")) {
klass.image = parseString(value["image"]);
}
if (value.HasMember("size")) {
klass.size = parseFunction(value["size"]);
}
if (value.HasMember("radius")) {
klass.radius = parseFunction(value["radius"]);
klass.radius_transition = parseTransition(value, "radius");
}
if (value.HasMember("blur")) {
klass.blur = parseFunction(value["blur"]);
klass.blur_transition = parseTransition(value, "blur");
}
using Key = ClassPropertyKey;
parse<Color>("color", Key::IconColor, klass, value);
parseTransition("transition-color", Key::IconColorTransition, klass, value);
parse<std::string>("image", Key::IconImage, klass, value);
parse<FunctionProperty>("size", Key::IconSize, klass, value);
parse<FunctionProperty>("radius", Key::IconRadius, klass, value);
parseTransition("transition-radius", Key::IconRadiusTransition, klass, value);
parse<FunctionProperty>("blur", Key::IconBlur, klass, value);
parseTransition("transition-blur", Key::IconBlurTransition, klass, value);
return klass;
}
TextClass StyleParser::parseTextClass(JSVal value) {
TextClass klass;
ClassProperties StyleParser::parseTextClass(JSVal value) {
ClassProperties klass(RenderType::Text);
parseGenericClass(klass, value);
if (value.HasMember("color")) {
klass.color = parseColor(value["color"]);
klass.color_transition = parseTransition(value, "color");
}
using Key = ClassPropertyKey;
if (value.HasMember("stroke")) {
klass.halo = parseColor(value["stroke"]);
klass.halo_transition = parseTransition(value, "stroke");
}
if (value.HasMember("strokeWidth")) {
klass.halo_radius = parseFunction(value["strokeWidth"]);
klass.halo_radius_transition = parseTransition(value, "strokeWidth");
}
if (value.HasMember("strokeBlur")) {
klass.halo_blur = parseFunction(value["strokeBlur"]);
klass.halo_blur_transition = parseTransition(value, "strokeBlur");
}
if (value.HasMember("size")) {
klass.size = parseFunction(value["size"]);
}
if (value.HasMember("rotate")) {
klass.rotate = parseFunction(value["rotate"]);
}
if (value.HasMember("alwaysVisible")) {
klass.always_visible = parseFunction(value["alwaysVisible"]);
}
parse<Color>("color", Key::TextColor, klass, value);
parseTransition("transition-color", Key::TextColorTransition, klass, value);
parse<Color>("stroke", Key::TextHaloColor, klass, value);
parseTransition("transition-stroke", Key::TextHaloColorTransition, klass, value);
parse<FunctionProperty>("strokeWidth", Key::TextHaloRadius, klass, value);
parseTransition("transition-strokeWidth", Key::TextHaloRadiusTransition, klass, value);
parse<FunctionProperty>("strokeBlur", Key::TextHaloBlur, klass, value);
parseTransition("transition-strokeBlur", Key::TextHaloBlurTransition, klass, value);
parse<FunctionProperty>("size", Key::TextSize, klass, value);
parse<FunctionProperty>("rotate", Key::TextRotate, klass, value);
parse<FunctionProperty>("alwaysVisible", Key::TextAlwaysVisible, klass, value);
return klass;
}
RasterClass StyleParser::parseRasterClass(JSVal value) {
RasterClass klass;
ClassProperties StyleParser::parseRasterClass(JSVal value) {
ClassProperties klass(RenderType::Raster);
parseGenericClass(klass, value);
return klass;
}
CompositeClass StyleParser::parseCompositeClass(JSVal value) {
CompositeClass klass;
ClassProperties StyleParser::parseCompositeClass(JSVal value) {
ClassProperties klass(RenderType::Composite);
parseGenericClass(klass, value);
return klass;
}
BackgroundClass StyleParser::parseBackgroundClass(JSVal value) {
BackgroundClass klass;
ClassProperties StyleParser::parseBackgroundClass(JSVal value) {
ClassProperties klass(RenderType::Background);
parseGenericClass(klass, value);
if (value.HasMember("color")) {
klass.color = parseColor(value["color"]);
klass.color_transition = parseTransition(value, "color");
}
using Key = ClassPropertyKey;
parse<Color>("color", Key::BackgroundColor, klass, value);
parseTransition("transition-color", Key::BackgroundColorTransition, klass, value);
return klass;
}
@@ -797,3 +814,5 @@ Value StyleParser::parseValue(JSVal value) {
throw Style::exception("unhandled value type in style");
return false;
}
}

View File

@@ -244,19 +244,11 @@ void getGlyphs(PlacedGlyphs &glyphs, GlyphBoxes &boxes,
}
}
void Placement::addFeature(TextBucket& bucket,
const std::vector<Coordinate> &line,
const BucketGeometryDescription &info,
const GlyphPositions &face,
void Placement::addFeature(TextBucket &bucket, const std::vector<Coordinate> &line,
const BucketTextDescription &info, const GlyphPositions &face,
const Shaping &shaping) {
const bool horizontal = info.path == TextPathType::Horizontal;
const float padding = info.padding;
const float maxAngleDelta = info.maxAngleDelta;
const float textMinDistance = info.textMinDistance;
const float rotate = info.rotate;
const float fontScale =
(tileExtent / util::tileSize) / (glyphSize / info.size);
const float fontScale = (tileExtent / util::tileSize) / (glyphSize / info.max_size);
Anchors anchors;
@@ -268,7 +260,7 @@ void Placement::addFeature(TextBucket& bucket,
} else {
// Line labels
anchors = interpolate(line, textMinDistance, minScale);
anchors = interpolate(line, info.min_distance, minScale);
// Sort anchors by segment so that we can start placement with
// the anchors that can be shown at the lowest zoom levels.
@@ -279,11 +271,10 @@ void Placement::addFeature(TextBucket& bucket,
PlacedGlyphs glyphs;
GlyphBoxes boxes;
getGlyphs(glyphs, boxes, anchor, info.translate, shaping, face, fontScale, horizontal,
line, maxAngleDelta, rotate);
PlacementProperty place =
collision.place(boxes, anchor, anchor.scale, maxPlacementScale,
padding, horizontal, info.alwaysVisible);
getGlyphs(glyphs, boxes, anchor, info.translate, shaping, face, fontScale, horizontal, line,
info.max_angle_delta, info.rotate);
PlacementProperty place = collision.place(boxes, anchor, anchor.scale, maxPlacementScale,
info.padding, horizontal, info.always_visible);
if (place) {
bucket.addGlyphs(glyphs, place.zoom, place.rotationRange, zoom - zOffset);
}