38
bin/style.js
38
bin/style.js
@@ -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",
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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:
|
||||
|
||||
|
||||
@@ -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:
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
|
||||
|
||||
@@ -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() {}
|
||||
};
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
9
src/style/class_description.cpp
Normal file
9
src/style/class_description.cpp
Normal 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));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user