Merge pull request #343 from mapbox/style-v3

Parse v3 style natively
This commit is contained in:
Konstantin Käfer
2014-07-03 19:34:57 +02:00
111 changed files with 6529 additions and 7231 deletions

View File

@@ -28,7 +28,7 @@ script:
notifications:
hipchat:
rooms:
secure: "GGP1hopxPUtKABYyMIzwu8nJco3r14RhYAlpzZwlj1Ypl3okRCHh51VxkMpmPb6WgPhjL4O7/AVxS2mDnAyMqKWdeW3wl9edirZu0kjROCfxRoeqEeLC1JLHLfjdpIvJVv++jnvp2xrw+YUMI24Y2klHvOMpHhm8+CLpmmpidfY="
secure: "SgMjGDp8MP+UTAY/aWJwSQH2hv1Q0vhEc1BdIde59+lEL3u6gNMSexUTSJoVxRlIQTFXDJBU7v58ISdgJX8cPa+UvIC8hhLsayM+cbm+J3jgy7V9E9wllq3RpmXN33TxY7nwKgWdIms2t9YKZco3y4dKKAUyHXrtMvw8NNPQM+4="
template:
- 'Build <a href="%{build_url}">#%{build_number}</a>: %{author} tested <a href="%{compare_url}">%{commit}</a> in branch <a href="https://github.com/%{repository_slug}/tree/%{branch}">%{branch}</a> of <a href="https://github.com/%{repository_slug}">%{repository_name}</a>: <b>%{commit_message}</b><br>%{message} (%{duration})'
format: html

File diff suppressed because it is too large Load Diff

View File

@@ -78,17 +78,8 @@ void GLFWView::key(GLFWwindow *window, int key, int /*scancode*/, int action, in
break;
case GLFW_KEY_R:
if (!mods) {
std::set<std::string> newAppliedClasses;
if (view->map->getAppliedClasses().count("default") && view->map->getAppliedClasses().count("night")) {
if (view->map->getAppliedClasses().count("night")) {
newAppliedClasses.insert("default");
} else {
newAppliedClasses.insert("default");
newAppliedClasses.insert(newAppliedClasses.end(), "night");
}
view->map->setDefaultTransitionDuration(300);
view->map->setAppliedClasses(newAppliedClasses);
}
view->map->setDefaultTransitionDuration(300);
view->map->toggleClass("night");
}
break;
case GLFW_KEY_N:

View File

@@ -1,9 +1,5 @@
#include <llmr/util/constants.hpp>
const char *llmr::kSpriteURL = "http://mapbox-kkaefer.s3.amazonaws.com/static/outdoors-gl/sprite";
const char *llmr::kVectorTileURL = "http://a.tiles.mapbox.com/v3/mapbox.mapbox-terrain-v1,mapbox.mapbox-streets-v5/%d/%d/%d.vector.pbf";
const char *llmr::kGlyphURL = "http://mapbox.s3.amazonaws.com/gl-glyphs-256/%s/%d-%d.pbf";
const float llmr::util::tileSize = 512.0f;
#if defined(DEBUG)

View File

@@ -32,6 +32,10 @@ public:
return pos / itemSize;
}
inline bool empty() const {
return pos == 0;
}
// Transfers this buffer to the GPU and binds the buffer to the GL context.
void bind(bool force = false) {
if (buffer == 0) {

View File

@@ -1,128 +0,0 @@
#ifndef LLMR_MAP_FILTER_EXPRESSION
#define LLMR_MAP_FILTER_EXPRESSION
#include <llmr/style/value.hpp>
#include <llmr/util/recursive_wrapper.hpp>
#include <vector>
#include <string>
namespace llmr {
enum class FilterOperator {
Equal,
NotEqual,
Greater,
GreaterEqual,
Less,
LessEqual
};
enum class ExpressionOperator {
Or,
And
};
inline FilterOperator filterOperatorType(const std::string &op) {
if (op == "!=" || op == "not") {
return FilterOperator::NotEqual;
} else if (op == "==" || op == "eq") {
return FilterOperator::Equal;
} else if (op == ">" || op == "gt") {
return FilterOperator::Greater;
} else if (op == ">=" || op == "gte") {
return FilterOperator::GreaterEqual;
} else if (op == "<" || op == "lt") {
return FilterOperator::Less;
} else if (op == "<=" || op == "lte") {
return FilterOperator::LessEqual;
} else {
fprintf(stderr, "[WARNING] filter operator '%s' unrecognized\n", op.c_str());
return FilterOperator::Equal;
}
}
inline ExpressionOperator expressionOperatorType(const std::string &op) {
if (op == "&&" || op == "and") {
return ExpressionOperator::And;
} else if (op == "||" || op == "or") {
return ExpressionOperator::Or;
} else {
fprintf(stderr, "[WARNING] expression operator '%s' unrecognized\n", op.c_str());
return ExpressionOperator::Or;
}
}
class PropertyFilter;
class PropertyExpression;
typedef util::variant<
util::recursive_wrapper<PropertyFilter>,
util::recursive_wrapper<PropertyExpression>,
std::true_type
> PropertyFilterExpression;
class PropertyFilter {
public:
inline PropertyFilter(const std::string &field, FilterOperator op, const Value &value) : field(field), op(op), value(value) {};
inline PropertyFilter(PropertyFilter &&filter)
: field(std::move(filter.field)),
op(filter.op),
value(std::move(filter.value)) {
}
// Returns true if the filter passes, even if the key is missing.
inline bool isMissingFieldOkay() const {
switch (op) {
case FilterOperator::NotEqual:
return true;
default:
return false;
}
}
inline bool compare(const Value &other) const {
switch (op) {
case FilterOperator::Equal:
return util::relaxed_equal(other, value);
case FilterOperator::NotEqual:
return !util::relaxed_equal(other, value);
case FilterOperator::Greater:
return util::relaxed_greater(other, value);
case FilterOperator::GreaterEqual:
return util::relaxed_greater_equal(other, value);
case FilterOperator::Less:
return util::relaxed_less(other, value);
case FilterOperator::LessEqual:
return util::relaxed_less_equal(other, value);
default:
return false;
}
}
public:
std::string field;
FilterOperator op = FilterOperator::Equal;
Value value;
};
class PropertyExpression {
public:
inline PropertyExpression() {}
inline PropertyExpression(PropertyExpression &&expression)
: op(expression.op),
operands(std::move(expression.operands)) {
}
public:
ExpressionOperator op = ExpressionOperator::Or;
std::vector<PropertyFilterExpression> operands;
};
}
#endif

View File

@@ -20,8 +20,7 @@
namespace llmr {
class Source;
typedef std::map<std::string, const std::unique_ptr<Source>> Sources;
class SpriteAtlas;
class Map : private util::noncopyable {
public:
@@ -53,8 +52,8 @@ public:
void resize(uint16_t width, uint16_t height, float ratio, uint16_t fb_width, uint16_t fb_height);
// Styling
void setAppliedClasses(std::set<std::string> appliedClasses);
std::set<std::string> getAppliedClasses() const;
void toggleClass(const std::string &name);
const std::vector<std::string> &getAppliedClasses() const;
void setDefaultTransitionDuration(uint64_t duration_milliseconds = 0);
void setStyleJSON(std::string newStyleJSON);
std::string getStyleJSON() const;
@@ -107,8 +106,8 @@ public:
inline std::shared_ptr<SpriteAtlas> getSpriteAtlas() { return spriteAtlas; }
inline std::shared_ptr<Texturepool> getTexturepool() { return texturepool; }
inline std::shared_ptr<uv::loop> getLoop() { return loop; }
inline time getAnimationTime() const { return animationTime; }
inline const Sources &getSources() { return sources; }
inline timestamp getAnimationTime() const { return animationTime; }
inline timestamp getTime() const { return animationTime; }
private:
// uv async callbacks
@@ -133,8 +132,8 @@ private:
// Unconditionally performs a render with the current map state.
void render();
void renderLayers(const std::vector<LayerDescription>& layers);
void renderLayer(const LayerDescription& layer_desc, RenderPass pass);
void renderLayers(std::shared_ptr<StyleLayerGroup> group);
void renderLayer(std::shared_ptr<StyleLayer> layer_desc, RenderPass pass);
private:
// If cleared, the next time the render thread attempts to render the map, it will *actually*
@@ -162,12 +161,10 @@ private:
Painter painter;
Sources sources;
std::string styleJSON = "";
bool debug = false;
time animationTime = 0;
timestamp animationTime = 0;
int indent = 0;

View File

@@ -16,8 +16,8 @@ public:
~RasterTileData();
virtual void parse();
virtual void render(Painter &painter, const LayerDescription& layer_desc);
virtual bool hasData(const LayerDescription& layer_desc) const;
virtual void render(Painter &painter, std::shared_ptr<StyleLayer> layer_desc);
virtual bool hasData(std::shared_ptr<StyleLayer> layer_desc) const;
protected:
RasterBucket bucket;

View File

@@ -5,6 +5,7 @@
#include <llmr/map/tile_data.hpp>
#include <llmr/util/noncopyable.hpp>
#include <llmr/util/time.hpp>
#include <llmr/style/types.hpp>
#include <list>
#include <forward_list>
@@ -16,58 +17,49 @@
namespace llmr {
class Map;
class TransformState;
class Painter;
class Texturepool;
class Source : public std::enable_shared_from_this<Source>, private util::noncopyable {
public:
enum class Type {
vector,
raster
};
Source(SourceType type = SourceType::Vector, const std::string &url = "",
uint32_t tile_size = 512, uint32_t min_zoom = 0, uint32_t max_zoom = 22);
public:
Source(Map &map, Painter &painter,
const char *url = "", Type type = Type::vector, uint32_t tile_size = 512,
uint32_t min_zoom = 0, uint32_t max_zoom = 14, bool enabled = true);
bool update();
void updateMatrices(const TransformState &transform);
void drawClippingMasks();
bool update(Map &map);
void updateMatrices(const mat4 &projMatrix, const TransformState &transform);
void drawClippingMasks(Painter &painter);
size_t getTileCount() const;
void render(const LayerDescription& layer_desc, const BucketDescription &bucket_desc);
void finishRender();
void render(Painter &painter, std::shared_ptr<StyleLayer> layer_desc);
void finishRender(Painter &painter);
std::forward_list<Tile::ID> getIDs() const;
void updateClipIDs(const std::map<Tile::ID, ClipID> &mapping);
static std::string normalizeSourceURL(const std::string &url);
public:
bool enabled;
private:
bool findLoadedChildren(const Tile::ID& id, int32_t maxCoveringZoom, std::forward_list<Tile::ID>& retain);
bool findLoadedParent(const Tile::ID& id, int32_t minCoveringZoom, std::forward_list<Tile::ID>& retain);
std::forward_list<Tile::ID> covering_tiles(int32_t zoom, const box& points);
bool updateTiles();
TileData::State addTile(const Tile::ID& id);
TileData::State hasTile(const Tile::ID& id);
double getZoom() const;
private:
Map& map;
Painter& painter;
const Type type;
const SourceType type;
const std::string url;
const uint32_t tile_size;
const int32_t min_zoom;
const int32_t max_zoom;
private:
bool findLoadedChildren(const Tile::ID& id, int32_t maxCoveringZoom, std::forward_list<Tile::ID>& retain);
bool findLoadedParent(const Tile::ID& id, int32_t minCoveringZoom, std::forward_list<Tile::ID>& retain);
std::forward_list<Tile::ID> covering_tiles(const TransformState &state, int32_t clamped_zoom, const box& points);
bool updateTiles(Map &map);
TileData::State addTile(Map &map, const Tile::ID& id);
TileData::State hasTile(const Tile::ID& id);
double getZoom(const TransformState &state) const;
private:
// Stores the time when this source was most recently updated.
timestamp updated = 0;
std::map<Tile::ID, std::unique_ptr<Tile>> tiles;
std::map<Tile::ID, std::weak_ptr<TileData>> tile_data;
};

View File

@@ -16,7 +16,7 @@ namespace llmr {
class Map;
class Painter;
class LayerDescription;
class StyleLayer;
class TileData : public std::enable_shared_from_this<TileData>,
private util::noncopyable {
@@ -49,8 +49,8 @@ public:
virtual void beforeParse();
virtual void parse() = 0;
virtual void afterParse();
virtual void render(Painter &painter, const LayerDescription& layer_desc) = 0;
virtual bool hasData(const LayerDescription& layer_desc) const = 0;
virtual void render(Painter &painter, std::shared_ptr<StyleLayer> layer_desc) = 0;
virtual bool hasData(std::shared_ptr<StyleLayer> layer_desc) const = 0;
public:

View File

@@ -6,6 +6,7 @@
#include <llmr/text/glyph_store.hpp>
#include <llmr/text/glyph.hpp>
#include <llmr/util/utf.hpp>
#include <llmr/style/filter_expression.hpp>
namespace llmr {
@@ -14,8 +15,13 @@ class GlyphAtlas;
class GlyphStore;
class SpriteAtlas;
class VectorTileData;
class StyleLayer;
class StyleLayerGroup;
class StyleBucket;
class StyleBucketFill;
class StyleBucketLine;
class StyleBucketIcon;
class LayerDescription;
class Bucket;
class TileParser {
@@ -32,15 +38,17 @@ public:
private:
bool obsolete() const;
void parseGlyphs();
void parseStyleLayers(const std::vector<LayerDescription>& layers);
void parseStyleLayers(std::shared_ptr<StyleLayerGroup> group);
void addGlyph(uint64_t tileid, const std::string stackname, const std::u32string &string, const FontStack &fontStack, GlyphAtlas &glyphAtlas, GlyphPositions &face);
std::unique_ptr<Bucket> createBucket(const BucketDescription& bucket_desc);
std::unique_ptr<Bucket> createFillBucket(const VectorTileLayer& layer, const BucketDescription& bucket_desc);
std::unique_ptr<Bucket> createLineBucket(const VectorTileLayer& layer, const BucketDescription& bucket_desc);
std::unique_ptr<Bucket> createIconBucket(const VectorTileLayer& layer, const BucketDescription& bucket_desc);
std::unique_ptr<Bucket> createTextBucket(const VectorTileLayer& layer, const BucketDescription& bucket_desc);
template <class Bucket> void addBucketFeatures(Bucket& bucket, const VectorTileLayer& layer, const BucketDescription& bucket_desc);
template <class Bucket, typename ...Args> void addBucketFeatures(Bucket& bucket, const VectorTileLayer& layer, const BucketDescription& bucket_desc, Args&& ...args);
std::unique_ptr<Bucket> createBucket(std::shared_ptr<StyleBucket> bucket_desc);
std::unique_ptr<Bucket> createFillBucket(const VectorTileLayer& layer, const FilterExpression &filter, const StyleBucketFill &fill);
std::unique_ptr<Bucket> createLineBucket(const VectorTileLayer& layer, const FilterExpression &filter, const StyleBucketLine &line);
std::unique_ptr<Bucket> createIconBucket(const VectorTileLayer& layer, const FilterExpression &filter, const StyleBucketIcon &icon);
std::unique_ptr<Bucket> createTextBucket(const VectorTileLayer& layer, const FilterExpression &filter, const StyleBucketText &text);
template <class Bucket> void addBucketFeatures(Bucket& bucket, const VectorTileLayer& layer, const FilterExpression &filter);
template <class Bucket, typename ...Args> void addBucketFeatures(Bucket& bucket, const VectorTileLayer& layer, const FilterExpression &filter, Args&& ...args);
private:
const VectorTile vector_data;

View File

@@ -23,18 +23,18 @@ public:
uint16_t fb_width, uint16_t fb_height);
// Position
void moveBy(double dx, double dy, time duration = 0);
void setLonLat(double lon, double lat, time duration = 0);
void setLonLatZoom(double lon, double lat, double zoom, time duration = 0);
void moveBy(double dx, double dy, timestamp duration = 0);
void setLonLat(double lon, double lat, timestamp duration = 0);
void setLonLatZoom(double lon, double lat, double zoom, timestamp duration = 0);
void getLonLat(double& lon, double& lat) const;
void getLonLatZoom(double& lon, double& lat, double& zoom) const;
void startPanning();
void stopPanning();
// Zoom
void scaleBy(double ds, double cx = -1, double cy = -1, time duration = 0);
void setScale(double scale, double cx = -1, double cy = -1, time duration = 0);
void setZoom(double zoom, time duration = 0);
void scaleBy(double ds, double cx = -1, double cy = -1, timestamp duration = 0);
void setScale(double scale, double cx = -1, double cy = -1, timestamp duration = 0);
void setZoom(double zoom, timestamp duration = 0);
double getZoom() const;
double getScale() const;
void startScaling();
@@ -43,8 +43,8 @@ public:
double getMaxZoom() const;
// Angle
void rotateBy(double sx, double sy, double ex, double ey, time duration = 0);
void setAngle(double angle, time duration = 0);
void rotateBy(double sx, double sy, double ex, double ey, timestamp duration = 0);
void setAngle(double angle, timestamp duration = 0);
void setAngle(double angle, double cx, double cy);
double getAngle() const;
void startRotating();
@@ -53,7 +53,7 @@ public:
// Transitions
bool needsTransition() const;
void updateTransitions(time now);
void updateTransitions(timestamp now);
void cancelTransitions();
// Transform state
@@ -63,10 +63,10 @@ public:
private:
// Functions prefixed with underscores will *not* perform any locks. It is the caller's
// responsibility to lock this object.
void _moveBy(double dx, double dy, time duration = 0);
void _setScale(double scale, double cx, double cy, time duration = 0);
void _setScaleXY(double new_scale, double xn, double yn, time duration = 0);
void _setAngle(double angle, time duration = 0);
void _moveBy(double dx, double dy, timestamp duration = 0);
void _setScale(double scale, double cx, double cy, timestamp duration = 0);
void _setScaleXY(double new_scale, double xn, double yn, timestamp duration = 0);
void _setAngle(double angle, timestamp duration = 0);
void _clearPanning();
void _clearRotating();
void _clearScaling();

View File

@@ -5,7 +5,7 @@
#include <llmr/util/vec.hpp>
#include <llmr/style/value.hpp>
#include <llmr/text/glyph.hpp>
#include <llmr/map/filter_expression.hpp>
#include <llmr/style/filter_expression.hpp>
#include <vector>
#include <map>
#include <unordered_map>
@@ -40,6 +40,22 @@ public:
std::ostream& operator<<(std::ostream&, const VectorTileFeature& feature);
class VectorTileTagExtractor {
public:
VectorTileTagExtractor(const VectorTileLayer &layer);
void setTags(const pbf &pbf);
std::forward_list<Value> getValues(const std::string &key) const;
void setType(FilterExpression::GeometryType type);
FilterExpression::GeometryType getType() const;
private:
const VectorTileLayer &layer_;
pbf tags_;
FilterExpression::GeometryType type_ = FilterExpression::GeometryType::Any;
};
/*
* Allows iterating over the features of a VectorTileLayer using a
* BucketDescription as filter. Only features matching the descriptions will
@@ -54,11 +70,6 @@ public:
bool operator!=(const iterator& other) const;
const pbf& operator*() const;
private:
bool matchesFilterExpression(const PropertyFilterExpression &filterExpression, const pbf &tags_pbf);
bool matchesExpression(const PropertyExpression &expression, const pbf &tags_pbf);
bool matchesFilter(const PropertyFilter &filter, const pbf &tags_pbf);
private:
const FilteredVectorTileLayer& parent;
bool valid = false;
@@ -67,14 +78,14 @@ public:
};
public:
FilteredVectorTileLayer(const VectorTileLayer& layer, const BucketDescription& bucket_desc);
FilteredVectorTileLayer(const VectorTileLayer& layer, const FilterExpression &filterExpression);
iterator begin() const;
iterator end() const;
private:
const VectorTileLayer& layer;
const BucketDescription& bucket_desc;
const FilterExpression& filterExpression;
};
std::ostream& operator<<(std::ostream&, const GlyphPlacement& placement);

View File

@@ -29,8 +29,8 @@ public:
virtual void beforeParse();
virtual void parse();
virtual void afterParse();
virtual void render(Painter &painter, const LayerDescription& layer_desc);
virtual bool hasData(const LayerDescription& layer_desc) const;
virtual void render(Painter &painter, std::shared_ptr<StyleLayer> layer_desc);
virtual bool hasData(std::shared_ptr<StyleLayer> layer_desc) const;
protected:
// Holds the actual geometries in this tile.

View File

@@ -10,10 +10,11 @@
namespace llmr {
class Painter;
class StyleLayer;
class Bucket : private util::noncopyable {
public:
virtual void render(Painter& painter, const std::string& layer_name, const Tile::ID& id) = 0;
virtual void render(Painter& painter, std::shared_ptr<StyleLayer> layer_desc, const Tile::ID& id) = 0;
virtual bool hasData() const = 0;
virtual ~Bucket() {}

View File

@@ -2,7 +2,6 @@
#define LLMR_RENDERER_DEBUGBUCKET
#include <llmr/renderer/bucket.hpp>
#include <llmr/style/bucket_description.hpp>
#include <llmr/geometry/debug_font_buffer.hpp>
#include <llmr/geometry/vao.hpp>
@@ -21,7 +20,7 @@ class DebugBucket : public Bucket {
public:
DebugBucket(DebugFontBuffer& fontBuffer);
virtual void render(Painter& painter, const std::string& layer_name, const Tile::ID& id);
virtual void render(Painter& painter, std::shared_ptr<StyleLayer> layer_desc, const Tile::ID& id);
virtual bool hasData() const;
void drawLines(PlainShader& shader);

View File

@@ -2,9 +2,9 @@
#define LLMR_RENDERER_FILLBUCKET
#include <llmr/renderer/bucket.hpp>
#include <llmr/style/bucket_description.hpp>
#include <llmr/geometry/elements_buffer.hpp>
#include <llmr/geometry/fill_buffer.hpp>
#include <llmr/style/style_bucket.hpp>
#include <clipper/clipper.hpp>
#include <libtess2/tesselator.h>
@@ -41,10 +41,10 @@ public:
FillBucket(FillVertexBuffer& vertexBuffer,
TriangleElementsBuffer& triangleElementsBuffer,
LineElementsBuffer& lineElementsBuffer,
const BucketFillDescription& properties);
const StyleBucketFill& properties);
~FillBucket();
virtual void render(Painter& painter, const std::string& layer_name, const Tile::ID& id);
virtual void render(Painter& painter, std::shared_ptr<StyleLayer> layer_desc, const Tile::ID& id);
virtual bool hasData() const;
void addGeometry(pbf& data);
@@ -55,7 +55,7 @@ public:
void drawVertices(OutlineShader& shader);
public:
const BucketFillDescription &properties;
const StyleBucketFill &properties;
private:
TESSalloc *allocator;

View File

@@ -11,17 +11,17 @@
namespace llmr {
struct FrameSnapshot {
explicit inline FrameSnapshot(time timestamp, float z) : timestamp(timestamp), z(z) {}
float timestamp;
explicit inline FrameSnapshot(timestamp t, float z) : t(t), z(z) {}
float t;
float z;
};
class FrameHistory {
public:
// Record frame history that will be used to calculate fading params
void record(time now, float zoom);
void record(timestamp now, float zoom);
bool needsAnimation(time duration) const;
bool needsAnimation(timestamp duration) const;
public:
std::deque<FrameSnapshot> history;

View File

@@ -2,9 +2,9 @@
#define LLMR_RENDERER_ICONBUCKET
#include <llmr/renderer/bucket.hpp>
#include <llmr/style/bucket_description.hpp>
#include <llmr/geometry/elements_buffer.hpp>
#include <llmr/geometry/icon_buffer.hpp>
#include <llmr/style/style_bucket.hpp>
#include <vector>
#include <memory>
@@ -26,9 +26,9 @@ class VectorTileFeature;
class IconBucket : public Bucket {
public:
IconBucket(IconVertexBuffer& vertexBuffer,
const BucketIconDescription& properties);
const StyleBucketIcon& properties);
virtual void render(Painter& painter, const std::string& layer_name, const Tile::ID& id);
virtual void render(Painter& painter, std::shared_ptr<StyleLayer> layer_desc, const Tile::ID& id);
virtual bool hasData() const;
void addFeature(const VectorTileFeature &feature, SpriteAtlas &sprite_atlas);
@@ -37,7 +37,7 @@ public:
void drawIcons(DotShader& shader);
public:
const BucketIconDescription &properties;
const StyleBucketIcon &properties;
private:

View File

@@ -2,10 +2,11 @@
#define LLMR_RENDERER_LINEBUCKET
#include "bucket.hpp"
#include <llmr/style/bucket_description.hpp>
#include <llmr/geometry/vao.hpp>
#include <llmr/geometry/elements_buffer.hpp>
#include <llmr/geometry/line_buffer.hpp>
#include <llmr/style/style_bucket.hpp>
#include <memory>
#include <vector>
@@ -25,9 +26,9 @@ public:
LineBucket(LineVertexBuffer& vertexBuffer,
TriangleElementsBuffer& triangleElementsBuffer,
PointElementsBuffer& pointElementsBuffer,
const BucketLineDescription& properties);
const StyleBucketLine& properties);
virtual void render(Painter& painter, const std::string& layer_name, const Tile::ID& id);
virtual void render(Painter& painter, std::shared_ptr<StyleLayer> layer_desc, const Tile::ID& id);
virtual bool hasData() const;
void addGeometry(pbf& data);
@@ -39,7 +40,7 @@ public:
void drawPoints(LinejoinShader& shader);
public:
const BucketLineDescription &properties;
const StyleBucketLine &properties;
private:

View File

@@ -7,7 +7,7 @@
#include <llmr/util/mat4.hpp>
#include <llmr/util/noncopyable.hpp>
#include <llmr/renderer/frame_history.hpp>
#include <llmr/style/properties.hpp>
#include <llmr/style/types.hpp>
#include <llmr/shader/plain_shader.hpp>
#include <llmr/shader/outline_shader.hpp>
@@ -24,6 +24,8 @@
#include <llmr/map/transform_state.hpp>
#include <map>
#include <unordered_map>
#include <set>
namespace llmr {
@@ -39,11 +41,12 @@ class IconBucket;
class TextBucket;
class RasterBucket;
struct FillProperties;
struct CompositeProperties;
class LayerDescription;
class RasterTileData;
typedef std::map<std::string, const std::unique_ptr<Source>> Sources;
class Painter : private util::noncopyable {
public:
Painter(Map &map);
@@ -64,7 +67,7 @@ public:
void changeMatrix();
// Renders a particular layer from a tile.
void renderTileLayer(const Tile& tile, const LayerDescription &layer_desc);
void renderTileLayer(const Tile& tile, std::shared_ptr<StyleLayer> layer_desc);
// Renders debug information for a tile.
void renderTileDebug(const Tile& tile);
@@ -79,15 +82,17 @@ public:
void renderDebugText(DebugBucket& bucket);
void renderDebugText(const std::vector<std::string> &strings);
void renderFill(FillBucket& bucket, const FillProperties& properties, const Tile::ID& id, const mat4 &mat);
void renderFill(FillBucket& bucket, const std::string& layer_name, const Tile::ID& id);
void renderLine(LineBucket& bucket, const std::string& layer_name, const Tile::ID& id);
void renderIcon(IconBucket& bucket, const std::string& layer_name, const Tile::ID& id);
void renderText(TextBucket& bucket, const std::string& layer_name, const Tile::ID& id);
void renderRaster(RasterBucket& bucket, const std::string& layer_name, const Tile::ID& id);
void renderFill(FillBucket& bucket, std::shared_ptr<StyleLayer> layer_desc, const Tile::ID& id);
void renderLine(LineBucket& bucket, std::shared_ptr<StyleLayer> layer_desc, const Tile::ID& id);
void renderIcon(IconBucket& bucket, std::shared_ptr<StyleLayer> layer_desc, const Tile::ID& id);
void renderText(TextBucket& bucket, std::shared_ptr<StyleLayer> layer_desc, const Tile::ID& id);
void renderRaster(RasterBucket& bucket, std::shared_ptr<StyleLayer> layer_desc, const Tile::ID& id);
void preparePrerender(PrerenderedTexture &texture);
void finishPrerender(PrerenderedTexture &texture);
void renderPrerenderedTexture(PrerenderedTexture &texture, const GenericProperties &properties);
template <typename Properties>
void renderPrerenderedTexture(PrerenderedTexture &texture, const Properties &properties);
void resize();
@@ -101,7 +106,7 @@ public:
// Configures the painter strata that is used for early z-culling of fragments.
void setStrata(float strata);
void drawClippingMasks(const Sources &sources);
void drawClippingMasks(const std::set<std::shared_ptr<Source>> &sources);
void drawClippingMask(const mat4& matrix, const ClipID& clip);
void clearFramebuffers();
@@ -115,7 +120,7 @@ public:
bool needsAnimation() const;
private:
void setupShaders();
const mat4 &translatedMatrix(const std::array<float, 2> &translation, const Tile::ID &id, TranslateAnchor anchor = TranslateAnchor::Map);
const mat4 &translatedMatrix(const std::array<float, 2> &translation, const Tile::ID &id, TranslateAnchorType anchor = TranslateAnchorType::Default);
void prepareTile(const Tile& tile);

View File

@@ -3,7 +3,7 @@
#include <llmr/util/noncopyable.hpp>
#include <llmr/platform/gl.hpp>
#include <llmr/style/properties.hpp>
#include <llmr/style/rasterize_properties.hpp>
namespace llmr {
@@ -11,7 +11,7 @@ class Painter;
class PrerenderedTexture : private util::noncopyable {
public:
PrerenderedTexture(const PrerenderProperties &properties);
PrerenderedTexture(const RasterizedProperties &properties);
~PrerenderedTexture();
void bindTexture();
@@ -23,7 +23,7 @@ public:
void blur(Painter& painter, uint16_t passes);
public:
const PrerenderProperties properties;
const RasterizedProperties properties;
private:
GLint previous_fbo = 0;

View File

@@ -2,7 +2,6 @@
#define LLMR_RENDERER_RASTERBUCKET
#include <llmr/renderer/bucket.hpp>
#include <llmr/style/bucket_description.hpp>
#include <llmr/util/raster.hpp>
namespace llmr {
@@ -15,7 +14,7 @@ class RasterBucket : public Bucket {
public:
RasterBucket(const std::shared_ptr<Texturepool> &texturepool);
virtual void render(Painter& painter, const std::string& layer_name, const Tile::ID& id);
virtual void render(Painter& painter, std::shared_ptr<StyleLayer> layer_desc, const Tile::ID& id);
virtual bool hasData() const;
bool setImage(const std::string &data);

View File

@@ -2,12 +2,13 @@
#define LLMR_RENDERER_TEXTBUCKET
#include "bucket.hpp"
#include <llmr/style/bucket_description.hpp>
#include <llmr/geometry/vao.hpp>
#include <llmr/geometry/elements_buffer.hpp>
#include <llmr/map/vector_tile.hpp>
#include <llmr/text/types.hpp>
#include <llmr/text/glyph.hpp>
#include <llmr/style/style_bucket.hpp>
#include <memory>
#include <map>
#include <vector>
@@ -28,9 +29,9 @@ public:
TextBucket(
TextVertexBuffer &vertexBuffer,
TriangleElementsBuffer &triangleElementsBuffer,
const BucketTextDescription &properties, Placement &placement);
const StyleBucketText &properties, Placement &placement);
virtual void render(Painter &painter, const std::string &layer_name, const Tile::ID &id);
virtual void render(Painter &painter, std::shared_ptr<StyleLayer> layer_desc, const Tile::ID &id);
virtual bool hasData() const;
void addGlyphs(const PlacedGlyphs &glyphs, float placementZoom,
@@ -43,7 +44,7 @@ public:
void drawGlyphs(TextShader &shader);
public:
const BucketTextDescription &properties;
const StyleBucketText &properties;
private:
TextVertexBuffer& vertexBuffer;

View File

@@ -0,0 +1,39 @@
#ifndef LLMR_STYLE_APPLIED_CLASS_PROPERTIES
#define LLMR_STYLE_APPLIED_CLASS_PROPERTIES
#include <llmr/style/property_value.hpp>
#include <llmr/style/class_dictionary.hpp>
#include <llmr/util/time.hpp>
#include <list>
namespace llmr {
class AppliedClassProperty {
public:
AppliedClassProperty(ClassID class_id, timestamp begin, timestamp end, const PropertyValue &value);
public:
const ClassID name;
const timestamp begin;
const timestamp end;
const PropertyValue value;
};
class AppliedClassProperties {
public:
std::list<AppliedClassProperty> properties;
public:
// Returns thie ID of the most recent
ClassID mostRecent() const;
void add(ClassID class_id, timestamp begin, timestamp end, const PropertyValue &value);
bool hasTransitions() const;
void cleanup(timestamp now);
bool empty() const;
};
}
#endif

View File

@@ -1,150 +0,0 @@
#ifndef LLMR_STYLE_BUCKET_DESCRIPTION
#define LLMR_STYLE_BUCKET_DESCRIPTION
#include <string>
#include <vector>
#include <cmath>
#include <llmr/util/vec.hpp>
#include <llmr/map/filter_expression.hpp>
#include <llmr/style/value.hpp>
namespace llmr {
enum class BucketType {
None = 0,
Fill = 1,
Line = 2,
Icon = 3,
Text = 4,
Raster = 5
};
enum class CapType {
None = 0,
Round = 1,
Butt = 2,
Square = 3
};
enum class JoinType {
None = 0,
Miter = 1,
Bevel = 2,
Round = 3
};
enum class TextPathType {
Horizontal = 0,
Curve = 1
};
inline BucketType bucketType(const std::string& type) {
if (type == "fill") return BucketType::Fill;
else if (type == "line") return BucketType::Line;
else if (type == "point") return BucketType::Icon;
else if (type == "text") return BucketType::Text;
else if (type == "raster") return BucketType::Raster;
else return BucketType::None;
}
inline CapType capType(const std::string& cap) {
if (cap == "round") return CapType::Round;
else if (cap == "butt") return CapType::Butt;
else if (cap == "square") return CapType::Square;
else return CapType::None;
}
inline JoinType joinType(const std::string& join) {
if (join == "miter") return JoinType::Miter;
else if (join == "bevel") return JoinType::Bevel;
else if (join == "round") return JoinType::Round;
else return JoinType::None;
}
inline TextPathType textPathType(const std::string& path) {
if (path == "horizontal") return TextPathType::Horizontal;
else if (path == "curve") return TextPathType::Curve;
else return TextPathType::Horizontal;
};
inline float alignmentType(const std::string& alignment) {
if (alignment == "right") return 1.0f;
else if (alignment == "left") return 0.0f;
else return 0.5;
};
inline float verticalAlignmentType(const std::string& alignment) {
if (alignment == "bottom") return 1.0f;
else if (alignment == "top") return 0.0f;
else return 0.5;
};
class BucketFillDescription {
public:
};
class BucketLineDescription {
public:
CapType cap = CapType::None;
JoinType join = JoinType::None;
float miter_limit = 2.0f;
float round_limit = 1.0f;
};
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:
BucketType feature_type = BucketType::None;
BucketType type = BucketType::None;
// Specify what data to pull into this bucket
std::string source_name;
std::string source_layer;
PropertyFilterExpression filter = std::true_type();
// Specifies how the geometry for this bucket should be created
BucketRenderDescription render;
};
std::ostream& operator<<(std::ostream&, const BucketDescription& bucket);
std::ostream& operator<<(std::ostream&, BucketType type);
}
#endif

View File

@@ -1,39 +0,0 @@
#ifndef LLMR_STYLE_CLASS_DESCRIPTION
#define LLMR_STYLE_CLASS_DESCRIPTION
#include <string>
#include <unordered_map>
#include "properties.hpp"
namespace llmr {
class ClassDescription {
public:
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;
};
}
#endif

View File

@@ -0,0 +1,32 @@
#ifndef LLMR_STYLE_CLASS_DICTIONARY
#define LLMR_STYLE_CLASS_DICTIONARY
#include <cstdint>
#include <string>
#include <unordered_map>
namespace llmr {
enum class ClassID : uint32_t {
Fallback = 0, // These values are from the fallback properties
Default = 1, // These values are from the default style for a layer
Named = 2 // These values (and all subsequent IDs) are from a named style from the layer
};
class ClassDictionary {
public:
// Returns an ID for a class name. If the class name does not yet have an ID, one is
// auto-generated and stored for future reference.
static ClassID Lookup(const std::string &class_name);
// Returns either Fallback, Default or Named, depending on the type of the class id.
static ClassID Normalize(ClassID id);
private:
static std::unordered_map<std::string, ClassID> store;
static uint32_t offset;
};
}
#endif

View File

@@ -0,0 +1,43 @@
#ifndef LLMR_STYLE_CLASS_PROPERTIES
#define LLMR_STYLE_CLASS_PROPERTIES
#include <llmr/style/property_key.hpp>
#include <llmr/style/property_value.hpp>
#include <llmr/style/property_transition.hpp>
#include <map>
namespace llmr {
class ClassProperties {
public:
inline ClassProperties() {}
inline ClassProperties(ClassProperties &&properties)
: properties(std::move(properties.properties)) {}
inline void set(PropertyKey key, const PropertyValue &value) {
properties.emplace(key, value);
}
inline void set(PropertyKey key, const PropertyTransition &transition) {
transitions.emplace(key, transition);
}
const PropertyTransition &getTransition(PropertyKey key, const PropertyTransition &defaultTransition) const;
// Route-through iterable interface so that you can iterate on the object as is.
inline std::map<PropertyKey, PropertyValue>::const_iterator begin() const {
return properties.begin();
}
inline std::map<PropertyKey, PropertyValue>::const_iterator end() const {
return properties.end();
}
public:
std::map<PropertyKey, PropertyValue> properties;
std::map<PropertyKey, PropertyTransition> transitions;
};
}
#endif

View File

@@ -0,0 +1,117 @@
#ifndef LLMR_STYLE_FILTER_EXPRESSION
#define LLMR_STYLE_FILTER_EXPRESSION
#include <llmr/style/value.hpp>
#include <llmr/util/recursive_wrapper.hpp>
#include <forward_list>
#include <string>
#include <ostream>
#include <iostream>
namespace llmr {
class VectorTileTagExtractor;
class FilterComparison {
public:
enum class Operator : uint8_t {
Equal,
NotEqual,
Greater,
GreaterEqual,
Less,
LessEqual,
In,
NotIn
};
class Instance {
public:
Instance(Operator op, std::forward_list<Value> &&values)
: op(op), values(values) {}
bool compare(const Value &property_value) const;
bool compare(const std::forward_list<Value> &property_values) const;
private:
template <typename Comparer> inline bool includes(const Value &property_value, const Comparer &comparer) const;
template <typename Comparer> inline bool compare(const Value &property_value, const Comparer &comparer) const;
template <typename Comparer> inline bool all(const std::forward_list<Value> &property_values, const Comparer &comparer) const;
private:
Operator op = Operator::Equal;
std::forward_list<Value> values;
friend std::ostream& operator <<(std::ostream &, const Instance &);
};
public:
FilterComparison(const std::string &field) : field(field) {};
const std::string &getField() const;
inline bool compare(const VectorTileTagExtractor &extractor) const;
template <typename ...Args>
inline void add(Args&& ...args) {
instances.emplace_front(::std::forward<Args>(args)...);
}
private:
std::string field;
std::forward_list<Instance> instances;
friend std::ostream& operator <<(std::ostream &, const FilterComparison &);
};
std::ostream& operator <<(std::ostream &s, const FilterComparison &comparison);
std::ostream& operator <<(std::ostream &s, const FilterComparison::Instance &instance);
FilterComparison::Operator parseFilterComparisonOperator(const std::string &op);
class FilterExpression {
public:
typedef util::recursive_wrapper<FilterExpression> Wrapper;
enum class Operator : uint8_t {
And,
Or,
Xor,
Nor
};
enum class GeometryType : uint8_t {
Any,
Point,
Line,
Polygon
};
public:
FilterExpression() = default;
FilterExpression(Operator op) : op(op) {};
bool empty() const;
bool compare(const VectorTileTagExtractor &extractor) const;
void add(const FilterComparison &comparison);
void add(const FilterExpression &expression);
void setGeometryType(GeometryType g);
private:
Operator op = Operator::And;
GeometryType type = GeometryType::Any;
std::forward_list<FilterComparison> comparisons;
std::forward_list<FilterExpression::Wrapper> expressions;
friend std::ostream& operator <<(std::ostream &, const FilterExpression &);
};
std::ostream& operator <<(std::ostream &s, const FilterExpression &expression);
FilterExpression::GeometryType parseGeometryType(const std::string &geometry);
}
#endif

View File

@@ -0,0 +1,79 @@
#ifndef LLMR_STYLE_FUNCTION_PROPERTIES
#define LLMR_STYLE_FUNCTION_PROPERTIES
#include <llmr/util/variant.hpp>
#include <vector>
namespace llmr {
template <typename T>
struct ConstantFunction {
inline ConstantFunction(const T &value) : value(value) {}
inline T evaluate(float) const { return value; }
private:
const T value;
};
template <typename T>
struct LinearFunction {
inline LinearFunction(const T &value, float z_base, float slope, const T &min, const T &max)
: value(value), min(min), max(max), z_base(z_base), slope(slope) {}
T evaluate(float z) const;
private:
const T value, min, max;
const float z_base, slope;
};
template <typename T>
struct ExponentialFunction {
inline ExponentialFunction(const T &value, float z_base, float slope, const T &min,
const T &max)
: value(value), min(min), max(max), z_base(z_base), slope(slope) {}
T evaluate(float z) const;
private:
const T value, min, max;
const float z_base, slope;
};
template <typename T>
struct StopsFunction {
inline StopsFunction(const std::vector<std::pair<float, T>> &values) : values(values) {}
T evaluate(float z) const;
private:
const std::vector<std::pair<float, T>> values;
};
template <typename T>
using Function = util::variant<
std::false_type,
ConstantFunction<T>,
LinearFunction<T>,
ExponentialFunction<T>,
StopsFunction<T>
>;
template <typename T>
struct FunctionEvaluator {
typedef T result_type;
inline FunctionEvaluator(float z) : z(z) {}
inline result_type operator()(const std::false_type &) {
return result_type();
}
template <template <typename> class Fn>
inline result_type operator()(const Fn<T>& fn) {
return fn.evaluate(z);
}
private:
float z;
};
}
#endif

View File

@@ -1,22 +0,0 @@
#ifndef LLMR_STYLE_LAYER_DESCRIPTION
#define LLMR_STYLE_LAYER_DESCRIPTION
#include <string>
#include <vector>
namespace llmr {
class LayerDescription {
public:
std::string name;
std::string bucket_name;
std::vector<LayerDescription> child_layer;
size_t size() const;
};
std::ostream& operator<<(std::ostream&, const LayerDescription& layer);
}
#endif

View File

@@ -1,324 +0,0 @@
#ifndef LLMR_STYLE_PROPERTIES
#define LLMR_STYLE_PROPERTIES
#include <array>
#include <vector>
#include <string>
#include <limits>
#include <set>
#include <map>
#include <type_traits>
#include <memory>
#include <boost/optional.hpp>
#include <llmr/util/variant.hpp>
namespace llmr {
// Stores a premultiplied color, with all four channels ranging from 0..1
typedef std::array<float, 4> Color;
struct PropertyTransition {
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;
};
enum class TransitionablePropertyKey {
Translate = 1,
FillColor = 2,
StrokeColor = 3,
Opacity = 4,
Width = 5,
Offset = 6,
Color = 7,
DashArray = 8,
Radius = 9,
Blur = 10,
Halo = 11,
HaloRadius = 12,
HaloBlur = 13,
};
enum class Winding {
EvenOdd = 1,
NonZero = 2
};
// enum LineCap {
// Round = 1
// };
// enum LineJoin {
// Butt = 1,
// Bevel = 2
// };
enum class Property {
Null = 1,
Constant = 2,
Stops = 3,
Linear = 4
};
enum class TranslateAnchor : uint8_t {
Map = 1,
Viewport = 2
};
namespace functions {
float null(float z, const std::vector<float>&);
float constant(float z, const std::vector<float>& values);
float min(float z, const std::vector<float>& values);
float max(float z, const std::vector<float>& values);
float stops(float z, const std::vector<float>& values);
float linear(float z, const std::vector<float>& values);
float exponential(float z, const std::vector<float>& values);
}
struct FunctionProperty {
typedef float (*fn)(float z, const std::vector<float>& values);
fn function;
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); }
};
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 {
float buffer = 1.0f / 32.0f;
uint16_t size = 256;
uint16_t blur = 0;
};
struct GenericProperties {
bool enabled = true;
std::array<float, 2> translate = {{ 0, 0 }};
TranslateAnchor translateAnchor = TranslateAnchor::Map;
float opacity = 1.0;
// These are unresolved properties because the value here is per tile, so it might differ.
boost::optional<FunctionProperty> prerender;
boost::optional<FunctionProperty> prerenderBuffer;
boost::optional<FunctionProperty> prerenderSize;
boost::optional<FunctionProperty> prerenderBlur;
inline bool getPrerender(int8_t z) const {
return prerender && prerender.get().evaluate<bool>(z);
}
// Obtains prerender properties by integer zoom level.
inline PrerenderProperties getPrerenderProperties(int8_t z) const {
PrerenderProperties props;
if (prerenderBuffer) props.buffer = prerenderBuffer.get().evaluate<float>(z);
if (prerenderSize) props.size = prerenderSize.get().evaluate<uint16_t>(z);
if (prerenderBlur) props.blur = prerenderBlur.get().evaluate<uint16_t>(z);
return props;
}
virtual bool isVisible() const {
if (!enabled) { return false; }
if (opacity <= 0) { return false; }
return true;
}
};
struct IconProperties : public GenericProperties {
inline IconProperties() : GenericProperties() {}
float size = 0;
Color color = {{ 1, 1, 1, 1 }};
std::string image;
float radius = 0;
float blur = 0;
};
struct LineProperties : public GenericProperties {
inline LineProperties() : GenericProperties() {}
float width = 0;
float offset = 0;
Color color = {{ 0, 0, 0, 1 }};
std::array<float, 2> dash_array = {{ 1, -1 }};
};
struct FillProperties : public GenericProperties {
inline FillProperties() : GenericProperties() {}
Winding winding = Winding::NonZero;
bool antialias = true;
Color fill_color = {{ 0, 0, 0, 1 }};
Color stroke_color = {{ 0, 0, 0, 1 }};
float blur = 0.0f;
std::string image;
};
struct TextProperties : public GenericProperties {
inline TextProperties() : GenericProperties() {}
Color color = {{ 0, 0, 0, 1 }};
Color halo = {{ 1, 1, 1, 0.75 }};
float halo_radius = 0.25f;
float halo_blur = 1.0f;
float size = 12.0f;
float rotate = 0.0f;
bool always_visible = false;
};
struct BackgroundProperties : public GenericProperties {
inline BackgroundProperties() : GenericProperties() {}
Color color = {{ 1, 1, 1, 1 }};
};
struct RasterProperties : public GenericProperties {
inline RasterProperties() : GenericProperties() {}
};
struct CompositeProperties : public GenericProperties {
inline CompositeProperties() : GenericProperties() {}
};
const IconProperties defaultIconProperties;
const LineProperties defaultLineProperties;
const FillProperties defaultFillProperties;
const TextProperties defaultTextProperties;
const BackgroundProperties defaultBackgroundProperties;
const RasterProperties defaultRasterProperties;
const CompositeProperties defaultCompositeProperties;
}
namespace std {
template <> struct hash<llmr::TransitionablePropertyKey> {
public:
inline size_t operator()(llmr::TransitionablePropertyKey prop) const {
typedef typename std::underlying_type<llmr::TransitionablePropertyKey>::type type;
return std::hash<type>()(static_cast<type>(prop));
}
};
}
#endif

View File

@@ -0,0 +1,29 @@
#ifndef LLMR_STYLE_PROPERTY_FALLBACK
#define LLMR_STYLE_PROPERTY_FALLBACK
#include <llmr/style/property_key.hpp>
#include <llmr/style/property_value.hpp>
#include <map>
namespace llmr {
class PropertyFallbackValue {
public:
static const PropertyValue &Get(PropertyKey key) {
auto it = properties.find(key);
if (it != properties.end()) {
return it->second;
} else {
return defaultProperty;
}
}
private:
static const std::map<PropertyKey, PropertyValue> properties;
static const PropertyValue defaultProperty;
};
}
#endif

View File

@@ -0,0 +1,63 @@
#ifndef LLMR_STYLE_PROPERTY_KEY
#define LLMR_STYLE_PROPERTY_KEY
namespace llmr {
enum class PropertyKey {
FillEnabled,
FillAntialias,
FillOpacity,
FillColor,
FillOutlineColor,
FillTranslate, // for transitions only
FillTranslateX,
FillTranslateY,
FillTranslateAnchor,
FillImage,
LineEnabled,
LineOpacity,
LineColor,
LineTranslate, // for transitions only
LineTranslateX,
LineTranslateY,
LineTranslateAnchor,
LineWidth,
LineOffset,
LineBlur,
LineDashArray, // for transitions only
LineDashLand,
LineDashGap,
LineImage,
IconEnabled,
IconOpacity,
IconRotate,
IconRotateAnchor,
TextEnabled,
TextOpacity,
TextSize,
TextColor,
TextHaloColor,
TextHaloWidth,
TextHaloBlur,
CompositeEnabled,
CompositeOpacity,
RasterEnabled,
RasterOpacity,
RasterSpin,
RasterBrightnessLow,
RasterBrightnessHigh,
RasterSaturation,
RasterContrast,
RasterFade,
BackgroundColor
};
}
#endif

View File

@@ -0,0 +1,15 @@
#ifndef LLMR_STYLE_PROPERTY_TRANSITION
#define LLMR_STYLE_PROPERTY_TRANSITION
#include <cstdint>
namespace llmr {
struct PropertyTransition {
uint16_t duration = 0;
uint16_t delay = 0;
};
}
#endif

View File

@@ -0,0 +1,21 @@
#ifndef LLMR_STYLE_PROPERTY_VALUE
#define LLMR_STYLE_PROPERTY_VALUE
#include <llmr/util/variant.hpp>
#include <llmr/style/function_properties.hpp>
#include <llmr/style/types.hpp>
namespace llmr {
typedef util::variant<
std::string,
TranslateAnchorType,
RotateAnchorType,
Function<bool>,
Function<float>,
Function<Color>
> PropertyValue;
}
#endif

View File

@@ -0,0 +1,38 @@
#ifndef LLMR_STYLE_RASTERIZE_PROPERTIES
#define LLMR_STYLE_RASTERIZE_PROPERTIES
#include <llmr/style/function_properties.hpp>
namespace llmr {
// The calculated properties for a layer in a tile.
class RasterizedProperties {
public:
float buffer = 1.0f / 32.0f;
uint16_t size = 256;
uint8_t blur = 0;
};
class RasterizeProperties {
public:
Function<bool> enabled = ConstantFunction<bool>(false);
Function<float> buffer = ConstantFunction<float>(1.0f / 32.0f);
Function<float> size = ConstantFunction<float>(256);
Function<float> blur = ConstantFunction<float>(0);
inline bool isEnabled(const int8_t z) const {
return util::apply_visitor(FunctionEvaluator<bool>(z), enabled);
}
inline RasterizedProperties get(const int8_t z) const {
RasterizedProperties properties;
properties.buffer = util::apply_visitor(FunctionEvaluator<float>(z), buffer);
properties.size = util::apply_visitor(FunctionEvaluator<float>(z), size);
properties.blur = util::apply_visitor(FunctionEvaluator<float>(z), blur);
return properties;
}
};
}
#endif

View File

@@ -1,14 +1,14 @@
#ifndef LLMR_STYLE_SPRITE
#define LLMR_STYLE_SPRITE
#include <map>
#include <llmr/util/raster.hpp>
#include <llmr/util/vec.hpp>
#include <string>
#include <mutex>
#include <memory>
#include <atomic>
#include <llmr/util/raster.hpp>
#include <llmr/util/vec.hpp>
#include <unordered_map>
namespace llmr {
@@ -55,7 +55,7 @@ private:
std::string body;
std::string image;
std::atomic<bool> loaded;
std::map<std::string, SpritePosition> pos;
std::unordered_map<std::string, SpritePosition> pos;
const SpritePosition empty;
};

View File

@@ -1,16 +1,12 @@
#ifndef LLMR_STYLE_STYLE
#define LLMR_STYLE_STYLE
#include <cstdint>
#include <llmr/style/property_transition.hpp>
#include <llmr/util/pbf.hpp>
#include <llmr/style/bucket_description.hpp>
#include <llmr/style/layer_description.hpp>
#include <llmr/style/class_description.hpp>
#include <llmr/geometry/sprite_atlas.hpp>
#include <llmr/util/transition.hpp>
#include <llmr/util/time.hpp>
#include <llmr/util/uv.hpp>
#include <cstdint>
#include <map>
#include <unordered_map>
#include <vector>
@@ -20,6 +16,10 @@
namespace llmr {
class Sprite;
class Source;
class StyleLayer;
class StyleLayerGroup;
struct BackgroundProperties;
class Style {
public:
@@ -28,91 +28,46 @@ public:
public:
Style();
void reset();
void loadJSON(const uint8_t *const data);
size_t layerCount() const;
void cascade(float z);
void updateProperties(float z, timestamp t);
bool needsTransition() const;
void updateTransitions(time now);
void cancelTransitions();
void setDefaultTransitionDuration(uint16_t duration_milliseconds = 0);
void setDefaultTransitionDuration(uint64_t duration_milliseconds = 0);
const std::set<std::shared_ptr<Source>> getActiveSources() const;
const std::vector<std::string> &getAppliedClasses() const;
void toggleClass(const std::string &name);
// Updates the styling information to reflect the current array
// of applied classes.
void updateClasses();
bool hasTransitions() const;
const BackgroundProperties &getBackgroundProperties() const;
public:
std::shared_ptr<Sprite> sprite;
// This is static information parsed from the stylesheet.
std::map<std::string, BucketDescription> buckets;
std::vector<LayerDescription> layers;
std::map<std::string, ClassDescription> classes;
// Currently applied settings.
std::set<std::string> appliedClasses;
struct {
BackgroundProperties background;
std::unordered_map<std::string, FillProperties> fills;
std::unordered_map<std::string, LineProperties> lines;
std::unordered_map<std::string, IconProperties> icons;
std::unordered_map<std::string, TextProperties> texts;
std::unordered_map<std::string, RasterProperties> rasters;
std::unordered_map<std::string, CompositeProperties> composites;
std::unordered_map<std::string, std::unordered_map<TransitionablePropertyKey, std::string>> effective_classes;
} computed;
std::shared_ptr<StyleLayerGroup> layers;
std::vector<std::string> appliedClasses;
std::string sprite_url;
std::string glyph_url;
private:
bool transitionInProgress(const std::string &layer_name, TransitionablePropertyKey key, time start);
bool transitionExists(const std::string &layer_name, TransitionablePropertyKey key) const;
bool inNeedOfTransition(const std::string &layer_name, TransitionablePropertyKey key) const;
uint64_t transitionDuration(const std::string &layer_name, TransitionablePropertyKey key) const;
uint64_t transitionDelay(const std::string &layer_name, TransitionablePropertyKey key) const;
void updateSources();
void updateSources(const std::shared_ptr<StyleLayerGroup> &group);
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.
struct {
BackgroundProperties background;
std::unordered_map<std::string, FillProperties> fills;
std::unordered_map<std::string, LineProperties> lines;
std::unordered_map<std::string, IconProperties> icons;
std::unordered_map<std::string, TextProperties> texts;
std::unordered_map<std::string, RasterProperties> rasters;
std::unordered_map<std::string, CompositeProperties> composites;
std::unordered_map<std::string, std::unordered_map<TransitionablePropertyKey, std::string>> effective_classes;
} previous;
// Settings values currently being transitioned.
struct {
BackgroundProperties background;
std::unordered_map<std::string, FillProperties> fills;
std::unordered_map<std::string, LineProperties> lines;
std::unordered_map<std::string, IconProperties> icons;
std::unordered_map<std::string, TextProperties> texts;
std::unordered_map<std::string, RasterProperties> rasters;
std::unordered_map<std::string, CompositeProperties> composites;
} transitioning;
std::unordered_map<std::string, std::unordered_map<TransitionablePropertyKey, PropertyTransition>> properties_to_transition;
std::unordered_map<std::string, std::unordered_map<TransitionablePropertyKey, std::shared_ptr<util::transition>>> transitions;
uint64_t default_transition_duration = 0;
std::set<std::shared_ptr<Source>> activeSources;
PropertyTransition defaultTransition;
bool initial_render_complete = false;
mutable uv::rwlock mtx;
};
}
#endif
#endif

View File

@@ -0,0 +1,85 @@
#ifndef LLMR_STYLE_STYLE_BUCKET
#define LLMR_STYLE_STYLE_BUCKET
#include <llmr/style/types.hpp>
#include <llmr/style/filter_expression.hpp>
#include <llmr/util/vec.hpp>
#include <llmr/util/variant.hpp>
#include <memory>
#include <forward_list>
namespace llmr {
class Source;
class StyleBucketFill {
public:
WindingType winding = WindingType::Default;
};
class StyleBucketLine {
public:
CapType cap = CapType::Default;
JoinType join = JoinType::Default;
float miter_limit = 2.0f;
float round_limit = 1.0f;
};
class StyleBucketIcon {
public:
uint16_t size = 16;
vec2<float> translate {0, 0};
TranslateAnchorType translate_anchor = TranslateAnchorType::Default;
std::string icon;
float spacing = 0.0f;
float padding = 2.0f;
};
class StyleBucketText {
public:
std::string field;
TextPathType path = TextPathType::Default;
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};
TranslateAnchorType translate_anchor = TranslateAnchorType::Default;
float max_angle_delta = M_PI;
float min_distance = 250.0f;
float rotate = 0.0f; // what is this?
float padding = 2.0f;
float slant = 0.0f;
bool always_visible = false;
};
class StyleBucketRaster {
public:
};
typedef util::variant<StyleBucketFill, StyleBucketLine, StyleBucketIcon,
StyleBucketText, StyleBucketRaster,
std::false_type> StyleBucketRender;
class StyleBucket {
public:
typedef std::shared_ptr<StyleBucket> Ptr;
BucketType type = BucketType::None;
std::string name;
std::shared_ptr<Source> source;
std::string source_layer;
FilterExpression filter;
StyleBucketRender render = std::false_type();
};
};
#endif

View File

@@ -0,0 +1,92 @@
#ifndef LLMR_STYLE_STYLE_LAYER
#define LLMR_STYLE_STYLE_LAYER
#include <llmr/style/class_dictionary.hpp>
#include <llmr/style/class_properties.hpp>
#include <llmr/style/style_properties.hpp>
#include <llmr/style/rasterize_properties.hpp>
#include <llmr/style/applied_class_properties.hpp>
#include <vector>
#include <memory>
#include <string>
#include <map>
#include <set>
namespace llmr {
class StyleBucket;
class StyleLayerGroup;
class StyleLayer {
public:
StyleLayer(const std::string &id, std::map<ClassID, ClassProperties> &&styles,
std::unique_ptr<const RasterizeProperties> &&rasterize);
template <typename T> const T &getProperties() {
if (properties.is<T>()) {
return properties.get<T>();
} else {
return defaultStyleProperties<T>();
}
}
// Determines whether this layer is the background layer.
bool isBackground() const;
// Updates the StyleProperties information in this layer by evaluating all
// pending transitions and applied classes in order.
void updateProperties(float z, timestamp now);
// Sets the list of classes and creates transitions to the currently applied values.
void setClasses(const std::vector<std::string> &class_names, timestamp now,
const PropertyTransition &defaultTransition);
bool hasTransitions() const;
private:
// Applies all properties from a class, if they haven't been applied already.
void applyClassProperties(ClassID class_id, std::set<PropertyKey> &already_applied,
timestamp now, const PropertyTransition &defaultTransition);
// Sets the properties of this object by evaluating all pending transitions and
// aplied classes in order.
template <typename T> void applyStyleProperties(float z, timestamp now);
template <typename T> void applyStyleProperty(PropertyKey key, T &, float z, timestamp now);
// Removes all expired style transitions.
void cleanupAppliedStyleProperties(timestamp now);
public:
// The name of this layer.
const std::string id;
// Bucket information, telling the renderer how to generate the geometries
// for this layer (feature property filters, tessellation instructions, ...).
std::shared_ptr<StyleBucket> bucket;
// Contains all style classes that can be applied to this layer.
const std::map<ClassID, ClassProperties> styles;
private:
// For every property, stores a list of applied property values, with
// optional transition times.
std::map<PropertyKey, AppliedClassProperties> appliedStyle;
public:
// Stores the evaluated, and cascaded styling information, specific to this
// layer's type.
StyleProperties properties;
// Rasterization properties are used for prerendering the tile to a bitmap,
// which is then used as a raster image instead of rendering this layer
// directly in every frame.
const std::unique_ptr<const RasterizeProperties> rasterize;
// Child layer array (if this layer has child layers).
std::shared_ptr<StyleLayerGroup> layers;
};
}
#endif

View File

@@ -0,0 +1,23 @@
#ifndef LLMR_STYLE_STYLE_LAYER_GROUP
#define LLMR_STYLE_STYLE_LAYER_GROUP
#include <llmr/style/style_layer.hpp>
#include <vector>
namespace llmr {
class StyleLayerGroup {
public:
void setClasses(const std::vector<std::string> &class_names, timestamp now,
const PropertyTransition &defaultTransition);
void updateProperties(float z, timestamp t);
bool hasTransitions() const;
public:
std::vector<std::shared_ptr<StyleLayer>> layers;
};
}
#endif

View File

@@ -3,63 +3,113 @@
#include <rapidjson/document.h>
#include <llmr/style/style.hpp>
#include <llmr/map/source.hpp>
#include <llmr/style/filter_expression.hpp>
#include <llmr/style/class_properties.hpp>
#include <llmr/style/rasterize_properties.hpp>
#include <llmr/style/style_bucket.hpp>
#include <unordered_map>
#include <forward_list>
#include <tuple>
namespace llmr {
enum class ClassID : uint32_t;
class StyleLayer;
class StyleLayerGroup;
class StyleParser {
public:
using JSVal = const rapidjson::Value&;
void parseBuckets(JSVal value, std::map<std::string, BucketDescription>& buckets);
void parseLayers(JSVal value, std::vector<LayerDescription>& layers);
void parseConstants(JSVal value);
void parseClasses(JSVal value, std::map<std::string, ClassDescription> &classes,
const std::map<std::string, BucketDescription> &buckets,
const std::vector<LayerDescription> &layers);
void parse(JSVal document);
private:
LayerDescription parseLayer(JSVal value);
BucketDescription parseBucket(JSVal value);
std::pair<std::string, ClassDescription>
parseClassDescription(JSVal value,
const std::map<std::string, BucketDescription> &buckets,
const std::map<std::string, std::string> &layerBuckets);
void parseClass(const std::string &name, JSVal value, ClassDescription &class_desc,
const std::map<std::string, BucketDescription> &buckets,
const std::map<std::string, std::string> &layerBuckets);
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);
JSVal replaceConstant(JSVal value);
Value parseValue(JSVal value);
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]));
}
std::shared_ptr<StyleLayerGroup> getLayers() {
return root;
}
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);
std::string getSprite() const {
return sprite;
}
std::string getGlyphURL() const {
return glyph_url;
}
private:
std::map<std::string, const rapidjson::Value *> constants;
void parseConstants(JSVal value);
JSVal replaceConstant(JSVal value);
void parseSources(JSVal value);
std::unique_ptr<StyleLayerGroup> createLayers(JSVal value);
std::shared_ptr<StyleLayer> createLayer(JSVal value);
void parseLayers();
void parseLayer(std::pair<JSVal, std::shared_ptr<StyleLayer>> &pair);
void parseStyles(JSVal value, std::map<ClassID, ClassProperties> &styles);
void parseStyle(JSVal, ClassProperties &properties);
std::unique_ptr<RasterizeProperties> parseRasterize(JSVal value);
void parseReference(JSVal value, std::shared_ptr<StyleLayer> &layer);
void parseBucket(JSVal value, std::shared_ptr<StyleLayer> &layer);
void parseRender(JSVal value, std::shared_ptr<StyleLayer> &layer);
void parseSprite(JSVal value);
void parseGlyphURL(JSVal value);
// Parses optional properties into a render bucket.
template<typename T>
bool parseRenderProperty(JSVal value, T &target, const char *name);
template <typename T, typename Parser>
bool parseRenderProperty(JSVal value, T &target, const char *name, Parser &parser);
// Parses optional properties into style class properties.
template <typename T>
bool parseOptionalProperty(const char *property_name, PropertyKey key, ClassProperties &klass, JSVal value);
template <typename T>
bool parseOptionalProperty(const char *property_name, const std::vector<PropertyKey> &keys, ClassProperties &klass, JSVal value);
template <typename T>
bool parseOptionalProperty(const char *property_name, T &target, JSVal value);
template <typename T>
bool setProperty(JSVal value, const char *property_name, PropertyKey key, ClassProperties &klass);
template <typename T>
bool setProperty(JSVal value, const char *property_name, T &target);
template <typename T>
std::tuple<bool, T> parseProperty(JSVal value, const char *property_name);
template <typename T>
bool parseFunction(PropertyKey key, ClassProperties &klass, JSVal value);
template <typename T>
std::tuple<bool, Function<T>> parseFunction(JSVal value);
template <typename T>
T parseFunctionArgument(JSVal value);
FilterExpression parseFilter(JSVal, FilterExpression::Operator op);
FilterExpression parseFilter(JSVal);
Value parseValue(JSVal value);
std::forward_list<Value> parseValues(JSVal values);
private:
std::unordered_map<std::string, const rapidjson::Value *> constants;
std::unordered_map<std::string, const std::shared_ptr<Source>> sources;
// This stores the root layer.
std::shared_ptr<StyleLayerGroup> root;
// This maps ids to Layer objects, with all items being at the root level.
std::unordered_map<std::string, std::pair<JSVal, std::shared_ptr<StyleLayer>>> layers;
// Store a stack of layers we're parsing right now. This is to prevent reference cycles.
std::forward_list<StyleLayer *> stack;
// Base URL of the sprite image.
std::string sprite;
// URL template for glyph PBFs.
std::string glyph_url;
};
}

View File

@@ -0,0 +1,122 @@
#ifndef LLMR_STYLE_STYLE_PROPERTIES
#define LLMR_STYLE_STYLE_PROPERTIES
#include <llmr/util/variant.hpp>
#include <llmr/style/types.hpp>
#include <llmr/style/function_properties.hpp>
#include <array>
#include <string>
#include <type_traits>
#include <memory>
namespace llmr {
struct FillProperties {
FillProperties() {}
bool enabled = true;
bool antialias = true;
float opacity = 1.0f;
Color fill_color = {{ 0, 0, 0, 1 }};
Color stroke_color = {{ 0, 0, 0, -1 }};
std::array<float, 2> translate = {{ 0, 0 }};
TranslateAnchorType translateAnchor = TranslateAnchorType::Default;
std::string image;
inline bool isVisible() const {
return enabled && opacity > 0 && (fill_color[3] > 0 || stroke_color[3] > 0);
}
};
struct LineProperties {
inline LineProperties() {}
bool enabled = true;
float opacity = 1.0f;
Color color = {{ 0, 0, 0, 1 }};
std::array<float, 2> translate = {{ 0, 0 }};
TranslateAnchorType translateAnchor = TranslateAnchorType::Default;
float width = 0;
float offset = 0;
float blur = 0;
std::array<float, 2> dash_array = {{ 1, -1 }};
std::string image;
inline bool isVisible() const {
return enabled && opacity > 0 && color[3] > 0 && width > 0;
}
};
struct IconProperties {
inline IconProperties() {}
bool enabled = true;
float opacity = 1.0f;
float rotate = 0.0f;
RotateAnchorType rotate_anchor = RotateAnchorType::Default;
inline bool isVisible() const {
return enabled && opacity > 0;
}
};
struct TextProperties {
inline TextProperties() {}
bool enabled = true;
float opacity = 1.0f;
float size = 12.0f;
Color color = {{ 0, 0, 0, 1 }};
Color halo_color = {{ 1, 1, 1, 0.75 }};
float halo_width = 0.25f;
float halo_blur = 1.0f;
inline bool isVisible() const {
return enabled && opacity > 0 && (color[3] > 0 || halo_color[3] > 0) && size > 0;
}
};
struct CompositeProperties {
inline CompositeProperties() {}
bool enabled = true;
float opacity = 1.0f;
inline bool isVisible() const {
return enabled && opacity > 0;
}
};
struct RasterProperties {
inline RasterProperties() {}
bool enabled = true;
float opacity = 1.0f;
float spin = 0.0f;
std::array<float, 2> brightness = {{ 0, 1 }};
float saturation = 0.0f;
float contrast = 0.0f;
float fade = 0.0f;
inline bool isVisible() const {
return enabled && opacity > 0;
}
};
struct BackgroundProperties {
inline BackgroundProperties() {}
Color color = {{ 1, 1, 1, 1 }};
};
typedef util::variant<
FillProperties,
LineProperties,
IconProperties,
TextProperties,
CompositeProperties,
RasterProperties,
BackgroundProperties,
std::false_type
> StyleProperties;
template <typename T>
const T &defaultStyleProperties();
}
#endif

View File

@@ -0,0 +1,136 @@
#ifndef LLMR_STYLE_TYPES
#define LLMR_STYLE_TYPES
#include <string>
#include <array>
namespace llmr {
// Stores a premultiplied color, with all four channels ranging from 0..1
typedef std::array<float, 4> Color;
enum class BucketType : uint8_t {
None,
Fill,
Line,
Icon,
Text,
Raster,
Default = None
};
BucketType parseBucketType(const std::string &type);
const char *stringifyBucketType(BucketType type);
std::ostream& operator<<(std::ostream&, BucketType type);
enum class WindingType : uint8_t {
EvenOdd,
NonZero,
Default = NonZero
};
enum class CapType : uint8_t {
None,
Round,
Butt,
Square,
Default = None
};
enum class JoinType : uint8_t {
None,
Miter,
Bevel,
Round,
Default = None
};
enum class TextPathType : uint8_t {
Horizontal,
Curve,
Default = Horizontal
};
enum class TranslateAnchorType : uint8_t {
Map,
Viewport,
Default = Map
};
enum class RotateAnchorType : uint8_t {
Map,
Viewport,
Default = Viewport
};
enum class SourceType : uint8_t {
Vector,
Raster,
GeoJSON,
Video,
Default = Vector
};
inline WindingType parseWindingType(const std::string &type) {
if (type == "even-odd") return WindingType::EvenOdd;
if (type == "non-zero") return WindingType::NonZero;
return WindingType::Default;
}
inline CapType parseCapType(const std::string &cap) {
if (cap == "round") return CapType::Round;
if (cap == "butt") return CapType::Butt;
if (cap == "square") return CapType::Square;
return CapType::None;
}
inline JoinType parseJoinType(const std::string &join) {
if (join == "miter") return JoinType::Miter;
if (join == "bevel") return JoinType::Bevel;
if (join == "round") return JoinType::Round;
return JoinType::None;
}
inline TextPathType parseTextPathType(const std::string &path) {
if (path == "horizontal") return TextPathType::Horizontal;
if (path == "curve") return TextPathType::Curve;
return TextPathType::Default;
}
inline TranslateAnchorType parseTranslateAnchorType(const std::string &anchor) {
if (anchor == "map") return TranslateAnchorType::Map;
if (anchor == "viewport") return TranslateAnchorType::Viewport;
return TranslateAnchorType::Default;
}
inline RotateAnchorType parseRotateAnchorType(const std::string &anchor) {
if (anchor == "map") return RotateAnchorType::Map;
if (anchor == "viewport") return RotateAnchorType::Viewport;
return RotateAnchorType::Default;
}
inline float parseAlignmentType(const std::string &alignment) {
if (alignment == "right") return 1.0f;
if (alignment == "left") return 0.0f;
return 0.5f;
}
inline float parseVerticalAlignmentType(const std::string &alignment) {
if (alignment == "bottom") return 1.0f;
if (alignment == "top") return 0.0f;
return 0.5f;
}
inline SourceType parseSourceType(const std::string &source) {
if (source == "vector") return SourceType::Vector;
if (source == "raster") return SourceType::Raster;
if (source == "geojson") return SourceType::GeoJSON;
if (source == "video") return SourceType::Video;
return SourceType::Default;
}
}
#endif

View File

@@ -62,6 +62,8 @@ template <> struct string_to_number<uint64_t> {
template <typename Operator>
struct relaxed_operator_visitor {
typedef bool result_type;
bool operator()(bool lhs, bool rhs) const { return Operator()(lhs, rhs); }
template <typename T, class = typename std::enable_if<std::is_arithmetic<T>::value>::type>
@@ -114,23 +116,23 @@ struct relaxed_less_equal_operator {
} // end namespace detail
inline bool relaxed_equal(Value const &lhs, Value const &rhs) {
return apply_visitor(lhs, rhs, detail::relaxed_operator_visitor<detail::relaxed_equal_operator>());
return apply_visitor(detail::relaxed_operator_visitor<detail::relaxed_equal_operator>(), lhs, rhs);
}
inline bool relaxed_greater(Value const &lhs, Value const &rhs) {
return apply_visitor(lhs, rhs, detail::relaxed_operator_visitor<detail::relaxed_greater_operator>());
return apply_visitor(detail::relaxed_operator_visitor<detail::relaxed_greater_operator>(), lhs, rhs);
}
inline bool relaxed_greater_equal(Value const &lhs, Value const &rhs) {
return apply_visitor(lhs, rhs, detail::relaxed_operator_visitor<detail::relaxed_greater_equal_operator>());
return apply_visitor(detail::relaxed_operator_visitor<detail::relaxed_greater_equal_operator>(), lhs, rhs);
}
inline bool relaxed_less(Value const &lhs, Value const &rhs) {
return apply_visitor(lhs, rhs, detail::relaxed_operator_visitor<detail::relaxed_less_operator>());
return apply_visitor(detail::relaxed_operator_visitor<detail::relaxed_less_operator>(), lhs, rhs);
}
inline bool relaxed_less_equal(Value const &lhs, Value const &rhs) {
return apply_visitor(lhs, rhs, detail::relaxed_operator_visitor<detail::relaxed_less_equal_operator>());
return apply_visitor(detail::relaxed_operator_visitor<detail::relaxed_less_equal_operator>(), lhs, rhs);
}
}

View File

@@ -12,7 +12,7 @@ namespace llmr {
typedef std::pair<uint16_t, uint16_t> GlyphRange;
// Note: this only works for the BMP
GlyphRange getGlyphRange(uint32_t glyph);
GlyphRange getGlyphRange(char32_t glyph);
struct GlyphMetrics {
operator bool() const {

View File

@@ -51,7 +51,7 @@ private:
class GlyphPBF {
public:
GlyphPBF(const std::string &fontStack, GlyphRange glyphRange);
GlyphPBF(const std::string &glyphURL, const std::string &fontStack, GlyphRange glyphRange);
void parse(FontStack &stack);
@@ -67,6 +67,8 @@ private:
// Manages Glyphrange PBF loading.
class GlyphStore {
public:
GlyphStore(const std::string &glyphURL);
// Block until all specified GlyphRanges of the specified font stack are loaded.
void waitForGlyphRanges(const std::string &fontStack, const std::set<GlyphRange> &glyphRanges);
@@ -78,6 +80,9 @@ private:
FontStack &createFontStack(const std::string &fontStack);
public:
const std::string glyphURL;
private:
std::unordered_map<std::string, std::map<GlyphRange, std::unique_ptr<GlyphPBF>>> ranges;
std::unordered_map<std::string, std::unique_ptr<FontStack>> stacks;

View File

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

View File

@@ -7,10 +7,6 @@
namespace llmr {
extern const char *kSpriteURL;
extern const char *kVectorTileURL;
extern const char *kGlyphURL;
namespace util {
extern const float tileSize;

View File

@@ -31,7 +31,7 @@ public:
// transitions
void beginFadeInTransition();
bool needsTransition() const;
void updateTransitions(time now);
void updateTransitions(timestamp now);
public:
// loaded image dimensions

View File

@@ -1,19 +1,15 @@
#ifndef LLMR_UTIL_VARIANT_RECURSIVE_WRAPPER_HPP
#define LLMR_UTIL_VARIANT_RECURSIVE_WRAPPER_HPP
#ifndef MAPBOX_UTIL_VARIANT_RECURSIVE_WRAPPER_HPP
#define MAPBOX_UTIL_VARIANT_RECURSIVE_WRAPPER_HPP
#include <utility>
namespace llmr {
namespace util {
namespace llmr { namespace util {
template <typename T>
class recursive_wrapper
{
public:
typedef T type;
using type = T;
private:
T* p_;
@@ -34,19 +30,19 @@ private:
public:
recursive_wrapper& operator=(const recursive_wrapper& rhs)
inline recursive_wrapper& operator=(recursive_wrapper const& rhs)
{
assign( rhs.get() );
return *this;
}
recursive_wrapper& operator=(const T& rhs)
inline recursive_wrapper& operator=(T const& rhs)
{
assign( rhs );
return *this;
}
void swap(recursive_wrapper& operand) noexcept
inline void swap(recursive_wrapper& operand) noexcept
{
T* temp = operand.p_;
operand.p_ = p_;
@@ -71,10 +67,10 @@ public:
T& get() { return *get_pointer(); }
const T& get() const { return *get_pointer(); }
T* get_pointer() { return p_; }
const T* get_pointer() const { return p_; }
operator T const&() const { return this->get(); }
operator T&() { return this->get(); }
};
template <typename T>
@@ -103,8 +99,9 @@ recursive_wrapper<T>::recursive_wrapper(T const& operand)
template <typename T>
recursive_wrapper<T>::recursive_wrapper(recursive_wrapper&& operand)
: p_(new T( std::move(operand.get()) ))
: p_(operand.p_)
{
operand.p_ = nullptr;
}
template <typename T>
@@ -125,8 +122,6 @@ inline void swap(recursive_wrapper<T>& lhs, recursive_wrapper<T>& rhs) noexcept
lhs.swap(rhs);
}
}
}}
}
#endif // LLMR_UTIL_VARIANT_RECURSIVE_WRAPPER_HPP
#endif // MAPBOX_UTIL_VARIANT_RECURSIVE_WRAPPER_HPP

View File

@@ -5,7 +5,7 @@
namespace llmr {
typedef uint64_t time;
typedef uint64_t timestamp;
namespace util {
@@ -13,29 +13,29 @@ namespace util {
// Returns time in nanoseconds since an arbitrary point in the past. This has
// no relation to the current time, but is continuously increasing, so
// comparisons between different timestamps produce meaningful values.
inline time now() {
inline timestamp now() {
return uv_hrtime();
}
}
// used for time conversions
constexpr time operator"" _nanoseconds (long double ns) { return ns; }
constexpr time operator"" _nanosecond (long double ns) { return ns; }
constexpr time operator"" _microseconds (long double us) { return us * 1e3; }
constexpr time operator"" _microsecond (long double us) { return us * 1e3; }
constexpr time operator"" _milliseconds (long double ms) { return ms * 1e6; }
constexpr time operator"" _millisecond (long double ms) { return ms * 1e6; }
constexpr time operator"" _seconds (long double s) { return s * 1e9; }
constexpr time operator"" _second (long double s) { return s * 1e9; }
constexpr timestamp operator"" _nanoseconds (long double ns) { return ns; }
constexpr timestamp operator"" _nanosecond (long double ns) { return ns; }
constexpr timestamp operator"" _microseconds (long double us) { return us * 1e3; }
constexpr timestamp operator"" _microsecond (long double us) { return us * 1e3; }
constexpr timestamp operator"" _milliseconds (long double ms) { return ms * 1e6; }
constexpr timestamp operator"" _millisecond (long double ms) { return ms * 1e6; }
constexpr timestamp operator"" _seconds (long double s) { return s * 1e9; }
constexpr timestamp operator"" _second (long double s) { return s * 1e9; }
constexpr time operator"" _nanoseconds (unsigned long long ns) { return ns; }
constexpr time operator"" _nanosecond (unsigned long long ns) { return ns; }
constexpr time operator"" _microseconds (unsigned long long us) { return us * 1e3; }
constexpr time operator"" _microsecond (unsigned long long us) { return us * 1e3; }
constexpr time operator"" _milliseconds (unsigned long long ms) { return ms * 1e6; }
constexpr time operator"" _millisecond (unsigned long long ms) { return ms * 1e6; }
constexpr time operator"" _seconds (unsigned long long s) { return s * 1e9; }
constexpr time operator"" _second (unsigned long long s) { return s * 1e9; }
constexpr timestamp operator"" _nanoseconds (unsigned long long ns) { return ns; }
constexpr timestamp operator"" _nanosecond (unsigned long long ns) { return ns; }
constexpr timestamp operator"" _microseconds (unsigned long long us) { return us * 1e3; }
constexpr timestamp operator"" _microsecond (unsigned long long us) { return us * 1e3; }
constexpr timestamp operator"" _milliseconds (unsigned long long ms) { return ms * 1e6; }
constexpr timestamp operator"" _millisecond (unsigned long long ms) { return ms * 1e6; }
constexpr timestamp operator"" _seconds (unsigned long long s) { return s * 1e9; }
constexpr timestamp operator"" _second (unsigned long long s) { return s * 1e9; }
}

View File

@@ -3,7 +3,7 @@
#include <llmr/util/noncopyable.hpp>
#include <llmr/util/time.hpp>
#include <llmr/style/properties.hpp>
#include <llmr/style/types.hpp>
namespace llmr {
namespace util {
@@ -15,18 +15,18 @@ public:
complete
};
inline transition(time start, time duration)
inline transition(timestamp start, timestamp duration)
: start(start),
duration(duration) {}
inline float progress(time now) const {
inline float progress(timestamp now) const {
if (duration == 0) return 1;
if (start > now) return 0;
return (float)(now - start) / duration;
}
virtual state update(time now) const = 0;
virtual state update(timestamp now) const = 0;
virtual ~transition();
protected:
@@ -36,19 +36,19 @@ protected:
std::array<float, 2> interpolateFloatArray(std::array<float, 2> from, std::array<float, 2> to, double t) const;
protected:
const time start, duration;
const timestamp start, duration;
};
template <typename T>
class ease_transition : public transition {
public:
ease_transition(T from, T to, T& value, time start, time duration)
ease_transition(T from, T to, T& value, timestamp start, timestamp duration)
: transition(start, duration),
from(from),
to(to),
value(value) {}
state update(time now) const;
state update(timestamp now) const;
private:
const T from, to;
@@ -59,12 +59,12 @@ private:
template <typename T>
class timeout : public transition {
public:
timeout(T final_value, T& value, time start, time duration)
timeout(T final_value, T& value, timestamp start, timestamp duration)
: transition(start, duration),
final_value(final_value),
value(value) {}
state update(time now) const {
state update(timestamp now) const {
if (progress(now) >= 1) {
value = final_value;
return complete;

View File

@@ -5,7 +5,12 @@
// g++/libstdc++ is missing c++11 codecvt support
#ifdef __linux__
#pragma GCC diagnostic push
#ifndef __clang__
#pragma GCC diagnostic ignored "-Wunused-local-typedefs"
#endif
#include <boost/locale.hpp>
#pragma GCC diagnostic pop
#else
#include <codecvt>
#include <locale>

View File

@@ -1,5 +1,5 @@
#ifndef UTIL_VARIANT_HPP
#define UTIL_VARIANT_HPP
#ifndef MAPBOX_UTIL_VARIANT_HPP
#define MAPBOX_UTIL_VARIANT_HPP
#include <utility>
#include <typeinfo>
@@ -9,50 +9,119 @@
#include <new> // operator new
#include <cstddef> // size_t
#include <iosfwd>
#include <string>
#ifdef NDEBUG
#define VARIANT_INLINE inline __attribute__((always_inline))
#include "recursive_wrapper.hpp"
#ifdef _MSC_VER
// http://msdn.microsoft.com/en-us/library/z8y1yy88.aspx
#ifdef NDEBUG
#define VARIANT_INLINE __forceinline
#else
#define VARIANT_INLINE __declspec(noinline)
#endif
#else
#define VARIANT_INLINE __attribute__((noinline))
#ifdef NDEBUG
#define VARIANT_INLINE inline __attribute__((always_inline))
#else
#define VARIANT_INLINE __attribute__((noinline))
#endif
#endif
namespace llmr {
#define VARIANT_MAJOR_VERSION 0
#define VARIANT_MINOR_VERSION 1
#define VARIANT_PATCH_VERSION 0
namespace util {
// translates to 100
#define VARIANT_VERSION (VARIANT_MAJOR_VERSION*100000) + (VARIANT_MINOR_VERSION*100) + (VARIANT_PATCH_VERSION)
namespace detail {
namespace llmr { namespace util { namespace detail {
static constexpr std::size_t invalid_value = std::size_t(-1);
template <typename T, typename...Types>
struct type_traits;
struct direct_type;
template <typename T, typename First, typename...Types>
struct type_traits<T, First, Types...>
struct direct_type<T, First, Types...>
{
static constexpr std::size_t id = std::is_same<T, First>::value ? sizeof...(Types) : type_traits<T, Types...>::id;
static constexpr std::size_t index = std::is_same<T, First>::value
? sizeof...(Types) : direct_type<T, Types...>::index;
};
template <typename T>
struct type_traits<T>
struct direct_type<T>
{
static constexpr std::size_t id = invalid_value;
static constexpr std::size_t index = invalid_value;
};
template <typename T, typename...Types>
struct convertible_type;
template <typename T, typename First, typename...Types>
struct convertible_type<T, First, Types...>
{
static constexpr std::size_t index = std::is_convertible<T, First>::value
? sizeof...(Types) : convertible_type<T, Types...>::index;
};
template <typename T>
struct convertible_type<T>
{
static constexpr std::size_t index = invalid_value;
};
template <typename T, typename...Types>
struct value_traits
{
static constexpr std::size_t direct_index = direct_type<T, Types...>::index;
static constexpr std::size_t index =
(direct_index == invalid_value) ? convertible_type<T, Types...>::index : direct_index;
};
template <typename T, typename...Types>
struct is_valid_type;
template <typename T, typename First, typename... Types>
struct is_valid_type<T,First,Types...>
struct is_valid_type<T, First, Types...>
{
static constexpr bool value = std::is_same<T,First>::value
|| is_valid_type<T,Types...>::value;
static constexpr bool value = std::is_convertible<T, First>::value
|| is_valid_type<T, Types...>::value;
};
template <typename T>
struct is_valid_type<T> : std::false_type {};
}
template <std::size_t N, typename ... Types>
struct select_type
{
static_assert(N < sizeof...(Types), "index out of bounds");
};
template <std::size_t N, typename T, typename ... Types>
struct select_type<N, T, Types...>
{
using type = typename select_type<N - 1, Types...>::type;
};
template <typename T, typename ... Types>
struct select_type<0, T, Types...>
{
using type = T;
};
} // namespace detail
// static visitor
template <typename R = void>
struct static_visitor
{
using result_type = R;
protected:
static_visitor() {}
~static_visitor() {}
};
template <std::size_t arg1, std::size_t ... others>
struct static_max;
@@ -125,37 +194,77 @@ template<> struct variant_helper<>
namespace detail {
template <typename T>
struct unwrapper
{
T const& operator() (T const& obj) const
{
return obj;
}
T& operator() (T & obj) const
{
return obj;
}
};
template <typename T>
struct unwrapper<recursive_wrapper<T>>
{
auto operator() (recursive_wrapper<T> const& obj) const
-> typename recursive_wrapper<T>::type const&
{
return obj.get();
}
};
template <typename F, typename V, typename...Types>
struct dispatcher;
template <typename F, typename V, typename T, typename...Types>
struct dispatcher<F,V,T,Types...>
struct dispatcher<F, V, T, Types...>
{
typedef typename std::result_of<F(V const&)>::type result_type;
VARIANT_INLINE static result_type apply(V const& v, F f)
using result_type = typename F::result_type;
VARIANT_INLINE static result_type apply_const(V const& v, F f)
{
if (v.get_type_id() == sizeof...(Types))
if (v.get_type_index() == sizeof...(Types))
{
return f(v. template get<T>());
return f(unwrapper<T>()(v. template get<T>()));
}
else
{
return dispatcher<F,V,Types...>::apply(v, f);
return dispatcher<F, V, Types...>::apply_const(v, f);
}
}
VARIANT_INLINE static result_type apply(V & v, F f)
{
if (v.get_type_index() == sizeof...(Types))
{
return f(unwrapper<T>()(v. template get<T>()));
}
else
{
return dispatcher<F, V, Types...>::apply(v, f);
}
}
};
template<typename F,typename V>
struct dispatcher<F,V>
template<typename F, typename V>
struct dispatcher<F, V>
{
typedef typename std::result_of<F(V const&)>::type result_type;
VARIANT_INLINE static result_type apply(V const&, F)
using result_type = typename F::result_type;
VARIANT_INLINE static result_type apply_const(V const&, F)
{
throw std::runtime_error("unary dispatch: FAIL");
throw std::runtime_error(std::string("unary dispatch: FAIL ") + typeid(V).name());
}
VARIANT_INLINE static result_type apply(V &, F)
{
throw std::runtime_error(std::string("unary dispatch: FAIL ") + typeid(V).name());
}
};
@@ -163,28 +272,46 @@ template <typename F, typename V, typename T, typename...Types>
struct binary_dispatcher_rhs;
template <typename F, typename V, typename T0, typename T1, typename...Types>
struct binary_dispatcher_rhs<F,V,T0,T1,Types...>
struct binary_dispatcher_rhs<F, V, T0, T1, Types...>
{
typedef typename std::result_of<F(V const&, V const&)>::type result_type;
VARIANT_INLINE static result_type apply(V const& lhs, V const& rhs, F f)
using result_type = typename F::result_type;
VARIANT_INLINE static result_type apply_const(V const& lhs, V const& rhs, F f)
{
if (rhs.get_type_id() == sizeof...(Types)) // call binary functor
if (rhs.get_type_index() == sizeof...(Types)) // call binary functor
{
return f(lhs. template get<T0>(), rhs. template get<T1>());
return f(unwrapper<T0>()(lhs. template get<T0>()),
unwrapper<T1>()(rhs. template get<T1>()));
}
else
{
return binary_dispatcher_rhs<F,V,T0,Types...>::apply(lhs, rhs, f);
return binary_dispatcher_rhs<F, V, T0, Types...>::apply_const(lhs, rhs, f);
}
}
VARIANT_INLINE static result_type apply(V & lhs, V & rhs, F f)
{
if (rhs.get_type_index() == sizeof...(Types)) // call binary functor
{
return f(unwrapper<T0>()(lhs. template get<T0>()),
unwrapper<T1>()(rhs. template get<T1>()));
}
else
{
return binary_dispatcher_rhs<F, V, T0, Types...>::apply(lhs, rhs, f);
}
}
};
template<typename F,typename V, typename T>
struct binary_dispatcher_rhs<F,V,T>
template<typename F, typename V, typename T>
struct binary_dispatcher_rhs<F, V, T>
{
typedef typename std::result_of<F(V const&, V const&)>::type result_type;
VARIANT_INLINE static result_type apply(V const&, V const&, F)
using result_type = typename F::result_type;
VARIANT_INLINE static result_type apply_const(V const&, V const&, F)
{
throw std::runtime_error("binary dispatch: FAIL");
}
VARIANT_INLINE static result_type apply(V &, V &, F)
{
throw std::runtime_error("binary dispatch: FAIL");
}
@@ -195,28 +322,45 @@ template <typename F, typename V, typename T, typename...Types>
struct binary_dispatcher_lhs;
template <typename F, typename V, typename T0, typename T1, typename...Types>
struct binary_dispatcher_lhs<F,V,T0,T1,Types...>
struct binary_dispatcher_lhs<F, V, T0, T1, Types...>
{
typedef typename std::result_of<F(V const&, V const&)>::type binary_result_type;
VARIANT_INLINE static binary_result_type apply(V const& lhs, V const& rhs, F f)
using result_type = typename F::result_type;
VARIANT_INLINE static result_type apply_const(V const& lhs, V const& rhs, F f)
{
if (lhs.get_type_id() == sizeof...(Types)) // call binary functor
if (lhs.get_type_index() == sizeof...(Types)) // call binary functor
{
return f(lhs. template get<T1>(), rhs. template get<T0>());
}
else
{
return binary_dispatcher_lhs<F,V,T0,Types...>::apply(lhs, rhs, f);
return binary_dispatcher_lhs<F, V, T0, Types...>::apply_const(lhs, rhs, f);
}
}
VARIANT_INLINE static result_type apply(V & lhs, V & rhs, F f)
{
if (lhs.get_type_index() == sizeof...(Types)) // call binary functor
{
return f(lhs. template get<T1>(), rhs. template get<T0>());
}
else
{
return binary_dispatcher_lhs<F, V, T0, Types...>::apply(lhs, rhs, f);
}
}
};
template<typename F,typename V, typename T>
struct binary_dispatcher_lhs<F,V,T>
template<typename F, typename V, typename T>
struct binary_dispatcher_lhs<F, V, T>
{
typedef typename std::result_of<F(V const&, V const&)>::type result_type;
using result_type = typename F::result_type;
VARIANT_INLINE static result_type apply_const(V const&, V const&, F)
{
throw std::runtime_error("binary dispatch: FAIL");
}
VARIANT_INLINE static result_type apply(V const&, V const&, F)
VARIANT_INLINE static result_type apply(V &, V &, F)
{
throw std::runtime_error("binary dispatch: FAIL");
}
@@ -226,37 +370,60 @@ template <typename F, typename V, typename...Types>
struct binary_dispatcher;
template <typename F, typename V, typename T, typename...Types>
struct binary_dispatcher<F,V,T,Types...>
struct binary_dispatcher<F, V, T, Types...>
{
typedef typename std::result_of<F(V const&, V const&)>::type binary_result_type;
VARIANT_INLINE static binary_result_type apply(V const& v0, V const& v1, F f)
using result_type = typename F::result_type;
VARIANT_INLINE static result_type apply_const(V const& v0, V const& v1, F f)
{
if (v0.get_type_id() == sizeof...(Types))
if (v0.get_type_index() == sizeof...(Types))
{
if (v0.get_type_id() == v1.get_type_id())
if (v0.get_type_index() == v1.get_type_index())
{
return f(v0. template get<T>(), v1. template get<T>()); // call binary functor
}
else
{
return binary_dispatcher_rhs<F,V,T,Types...>::apply(v0, v1, f);
return binary_dispatcher_rhs<F, V, T, Types...>::apply_const(v0, v1, f);
}
}
else if (v1.get_type_id() == sizeof...(Types))
else if (v1.get_type_index() == sizeof...(Types))
{
return binary_dispatcher_lhs<F,V,T,Types...>::apply(v0, v1, f);
return binary_dispatcher_lhs<F, V, T, Types...>::apply_const(v0, v1, f);
}
return binary_dispatcher<F,V,Types...>::apply(v0, v1, f);
return binary_dispatcher<F, V, Types...>::apply_const(v0, v1, f);
}
VARIANT_INLINE static result_type apply(V & v0, V & v1, F f)
{
if (v0.get_type_index() == sizeof...(Types))
{
if (v0.get_type_index() == v1.get_type_index())
{
return f(v0. template get<T>(), v1. template get<T>()); // call binary functor
}
else
{
return binary_dispatcher_rhs<F, V, T, Types...>::apply(v0, v1, f);
}
}
else if (v1.get_type_index() == sizeof...(Types))
{
return binary_dispatcher_lhs<F, V, T, Types...>::apply(v0, v1, f);
}
return binary_dispatcher<F, V, Types...>::apply(v0, v1, f);
}
};
template<typename F,typename V>
struct binary_dispatcher<F,V>
template<typename F, typename V>
struct binary_dispatcher<F, V>
{
typedef typename std::result_of<F(V const&, V const&)>::type binary_result_type;
using result_type = typename F::result_type;
VARIANT_INLINE static result_type apply_const(V const&, V const&, F)
{
throw std::runtime_error("binary dispatch: FAIL");
}
VARIANT_INLINE static binary_result_type apply(V const&, V const&, F)
VARIANT_INLINE static result_type apply(V &, V &, F)
{
throw std::runtime_error("binary dispatch: FAIL");
}
@@ -275,19 +442,19 @@ struct equal_comp
struct less_comp
{
template <typename T>
bool operator()(const T& lhs, const T& rhs) const
bool operator()(T const& lhs, T const& rhs) const
{
return lhs < rhs;
}
};
template <typename Variant, typename Comp>
class comparer
class comparer : public static_visitor<bool>
{
public:
explicit comparer(Variant const& lhs) noexcept
: lhs_(lhs) {}
comparer& operator=(const comparer&) = delete;
comparer& operator=(comparer const&) = delete;
// visitor
template<typename T>
bool operator()(T const& rhs_content) const
@@ -301,16 +468,16 @@ private:
// operator<< helper
template <typename Out>
class printer
class printer : public static_visitor<>
{
public:
explicit printer(Out & out)
: out_(out) {}
printer& operator=(printer const &) = delete;
printer& operator=(printer const&) = delete;
// visitor
template <typename T>
void operator()( T const & operand) const
void operator()(T const& operand) const
{
out_ << operand;
}
@@ -318,7 +485,7 @@ private:
Out & out_;
};
} // detail
} // namespace detail
template<typename... Types>
class variant
@@ -329,81 +496,106 @@ private:
static const std::size_t data_align = static_max<alignof(Types)...>::value;
using data_type = typename std::aligned_storage<data_size, data_align>::type;
using helper_type = variant_helper<Types...>;
std::size_t type_id;
std::size_t type_index;
data_type data;
public:
VARIANT_INLINE variant()
: type_id(detail::invalid_value) {}
template <typename T>
VARIANT_INLINE explicit variant(T const& val) noexcept
: type_id(detail::type_traits<T, Types...>::id)
: type_index(sizeof...(Types) - 1)
{
static_assert(detail::is_valid_type<T,Types...>::value, "Not a valid type for this variant");
new (&data) T(val);
new (&data) typename detail::select_type<0, Types...>::type();
}
template <typename T>
VARIANT_INLINE variant(T && val) noexcept
: type_id(detail::type_traits<T,Types...>::id)
template <typename T, class = typename std::enable_if<
detail::is_valid_type<T, Types...>::value>::type>
VARIANT_INLINE explicit variant(T const& val) noexcept
: type_index(detail::value_traits<T, Types...>::index)
{
static_assert(detail::is_valid_type<T,Types...>::value, "Not a valid type for this variant");
new (&data) T(std::forward<T>(val)); // nothrow
constexpr std::size_t index = sizeof...(Types) - detail::value_traits<T, Types...>::index - 1;
using target_type = typename detail::select_type<index, Types...>::type;
new (&data) target_type(val);
}
template <typename T, class = typename std::enable_if<
detail::is_valid_type<T, Types...>::value>::type>
VARIANT_INLINE variant(T && val) noexcept
: type_index(detail::value_traits<T, Types...>::index)
{
constexpr std::size_t index = sizeof...(Types) - detail::value_traits<T, Types...>::index - 1;
using target_type = typename detail::select_type<index, Types...>::type;
new (&data) target_type(std::forward<T>(val)); // nothrow
}
VARIANT_INLINE variant(variant<Types...> const& old)
: type_id(old.type_id)
: type_index(old.type_index)
{
helper_type::copy(old.type_id, &old.data, &data);
helper_type::copy(old.type_index, &old.data, &data);
}
VARIANT_INLINE variant(variant<Types...>&& old) noexcept
: type_id(old.type_id)
: type_index(old.type_index)
{
helper_type::move(old.type_id, &old.data, &data);
helper_type::move(old.type_index, &old.data, &data);
}
friend void swap(variant<Types...> & first, variant<Types...> & second)
{
using std::swap; //enable ADL
swap(first.type_id, second.type_id);
swap(first.type_index, second.type_index);
swap(first.data, second.data);
}
VARIANT_INLINE variant<Types...>& operator= (variant<Types...> other)
VARIANT_INLINE variant<Types...>& operator=(variant<Types...> other)
{
swap(*this, other);
return *this;
}
// conversions
// move-assign
template <typename T>
VARIANT_INLINE variant<Types...>& operator=(T && rhs) noexcept
{
variant<Types...> temp(std::move(rhs));
swap(*this, temp);
return *this;
}
// copy-assign
template <typename T>
VARIANT_INLINE variant<Types...>& operator=(T const& rhs)
{
variant<Types...> temp(rhs);
swap(*this, temp);
return *this;
}
template<typename T>
VARIANT_INLINE bool is() const
{
return (type_id == detail::type_traits<T, Types...>::id);
return (type_index == detail::direct_type<T, Types...>::index);
}
VARIANT_INLINE bool valid() const
{
return (type_id != detail::invalid_value);
return (type_index != detail::invalid_value);
}
template<typename T, typename... Args>
VARIANT_INLINE void set(Args&&... args)
{
helper_type::destroy(type_id, &data);
helper_type::destroy(type_index, &data);
new (&data) T(std::forward<Args>(args)...);
type_id = detail::type_traits<T,Types...>::id;
type_index = detail::direct_type<T, Types...>::index;
}
template<typename T>
VARIANT_INLINE T& get()
{
if (type_id == detail::type_traits<T,Types...>::id)
if (type_index == detail::direct_type<T, Types...>::index)
{
return *reinterpret_cast<T*>(&data);
}
@@ -416,7 +608,7 @@ public:
template<typename T>
VARIANT_INLINE T const& get() const
{
if (type_id == detail::type_traits<T,Types...>::id)
if (type_index == detail::direct_type<T, Types...>::index)
{
return *reinterpret_cast<T const*>(&data);
}
@@ -426,37 +618,57 @@ public:
}
}
VARIANT_INLINE std::size_t get_type_id() const
VARIANT_INLINE std::size_t get_type_index() const
{
return type_id;
return type_index;
}
// visitor
// unary
template <typename F, typename V>
typename std::result_of<F(V const&)>::type
VARIANT_INLINE static visit(V const& v, F f)
auto VARIANT_INLINE
static visit(V const& v, F f)
-> decltype(detail::dispatcher<F, V, Types...>::apply_const(v, f))
{
return detail::dispatcher<F, V, Types...>::apply_const(v, f);
}
// non-const
template <typename F, typename V>
auto VARIANT_INLINE
static visit(V & v, F f)
-> decltype(detail::dispatcher<F, V, Types...>::apply(v, f))
{
return detail::dispatcher<F, V, Types...>::apply(v, f);
}
// binary
// const
template <typename F, typename V>
typename std::result_of<F(V const&, V const& )>::type
VARIANT_INLINE static binary_visit(V const& v0, V const& v1, F f)
auto VARIANT_INLINE
static binary_visit(V const& v0, V const& v1, F f)
-> decltype(detail::binary_dispatcher<F, V, Types...>::apply_const(v0, v1, f))
{
return detail::binary_dispatcher<F, V, Types...>::apply_const(v0, v1, f);
}
// non-const
template <typename F, typename V>
auto VARIANT_INLINE
static binary_visit(V& v0, V& v1, F f)
-> decltype(detail::binary_dispatcher<F, V, Types...>::apply(v0, v1, f))
{
return detail::binary_dispatcher<F, V, Types...>::apply(v0, v1, f);
}
~variant() noexcept
{
helper_type::destroy(type_id, &data);
helper_type::destroy(type_index, &data);
}
// comparison operators
// equality
VARIANT_INLINE bool operator==(variant const& rhs) const
{
if (this->get_type_id() != rhs.get_type_id())
if (this->get_type_index() != rhs.get_type_index())
return false;
detail::comparer<variant, detail::equal_comp> visitor(*this);
return visit(rhs, visitor);
@@ -464,9 +676,9 @@ public:
// less than
VARIANT_INLINE bool operator<(variant const& rhs) const
{
if (this->get_type_id() != rhs.get_type_id())
if (this->get_type_index() != rhs.get_type_index())
{
return this->get_type_id() < rhs.get_type_id();
return this->get_type_index() < rhs.get_type_index();
// ^^ borrowed from boost::variant
}
detail::comparer<variant, detail::less_comp> visitor(*this);
@@ -475,17 +687,30 @@ public:
};
// unary visitor interface
// const
template <typename V, typename F>
typename std::result_of<F(V const&)>::type
VARIANT_INLINE static apply_visitor(V const& v, F f)
auto VARIANT_INLINE static apply_visitor(F f, V const& v) -> decltype(V::visit(v, f))
{
return V::visit(v,f);
return V::visit(v, f);
}
// non-const
template <typename V, typename F>
auto VARIANT_INLINE static apply_visitor(F f, V & v) -> decltype(V::visit(v, f))
{
return V::visit(v, f);
}
// binary visitor interface
// const
template <typename V, typename F>
typename std::result_of<F(V const&, V const&)>::type
VARIANT_INLINE static apply_visitor(V const& v0, V const& v1, F f)
auto VARIANT_INLINE static apply_visitor(F f, V const& v0, V const& v1) -> decltype(V::binary_visit(v0, v1, f))
{
return V::binary_visit(v0, v1, f);
}
// non-const
template <typename V, typename F>
auto VARIANT_INLINE static apply_visitor(F f, V & v0, V & v1) -> decltype(V::binary_visit(v0, v1, f))
{
return V::binary_visit(v0, v1, f);
}
@@ -493,14 +718,14 @@ VARIANT_INLINE static apply_visitor(V const& v0, V const& v1, F f)
// operator<<
template <typename charT, typename traits, typename Variant>
VARIANT_INLINE std::basic_ostream<charT,traits>&
operator<< (std::basic_ostream<charT,traits>& out, Variant const& rhs)
VARIANT_INLINE std::basic_ostream<charT, traits>&
operator<< (std::basic_ostream<charT, traits>& out, Variant const& rhs)
{
detail::printer<std::basic_ostream<charT,traits> > visitor(out);
apply_visitor(rhs, visitor);
detail::printer<std::basic_ostream<charT, traits>> visitor(out);
apply_visitor(visitor, rhs);
return out;
}
}
}
#endif // UTIL_VARIANT_HPP
}}
#endif // MAPBOX_UTIL_VARIANT_HPP

View File

@@ -7,7 +7,7 @@
{
'target_name': 'shaders',
'type': 'none',
'hard_dependency': 1,
# 'hard_dependency': 1,
'actions': [
{
'action_name': 'Build Shaders',

View File

@@ -9,6 +9,9 @@
#include <llmr/util/string.hpp>
#include <llmr/util/constants.hpp>
#include <llmr/util/uv.hpp>
#include <llmr/style/style_layer_group.hpp>
#include <llmr/style/style_bucket.hpp>
#include <llmr/geometry/sprite_atlas.hpp>
#include <algorithm>
#include <memory>
@@ -21,7 +24,6 @@ Map::Map(View& view)
transform(),
style(std::make_shared<Style>()),
glyphAtlas(std::make_shared<GlyphAtlas>(1024, 1024)),
glyphStore(std::make_shared<GlyphStore>()),
spriteAtlas(std::make_shared<SpriteAtlas>(512, 512)),
texturepool(std::make_shared<Texturepool>()),
painter(*this),
@@ -36,6 +38,10 @@ Map::Map(View& view)
}
Map::~Map() {
// Clear the style first before the rest of the constructor deletes members of this object.
// This is required because members of the style reference the Map object in their destructors.
style.reset();
if (async) {
stop();
}
@@ -72,13 +78,6 @@ void Map::stop() {
uv_thread_join(&thread);
uv_close((uv_handle_t *)async_terminate, delete_async);
async_terminate = nullptr;
uv_close((uv_handle_t *)async_render, delete_async);
async_render = nullptr;
uv_close((uv_handle_t *)async_cleanup, delete_async);
async_cleanup = nullptr;
// Run the event loop once to make sure our async delete handlers are called.
uv_run(**loop, UV_RUN_ONCE);
@@ -157,7 +156,13 @@ void Map::render(uv_async_t *async) {
}
void Map::terminate(uv_async_t *async) {
uv_stop(static_cast<uv_loop_t *>(async->data));
// Closes all open handles on the loop. This means that the loop will automatically terminate.
uv_loop_t *loop = static_cast<uv_loop_t *>(async->data);
uv_walk(loop, [](uv_handle_t *handle, void *arg) {
if (!uv_is_closing(handle)) {
uv_close(handle, NULL);
}
}, NULL);
}
#pragma mark - Setup
@@ -166,23 +171,10 @@ void Map::setup() {
view.make_active();
painter.setup();
sources.emplace("outdoors",
std::unique_ptr<Source>(new Source(*this,
painter,
kVectorTileURL,
Source::Type::vector,
512,
0,
14,
true)));
setStyleJSON(styleJSON);
}
void Map::setStyleJSON(std::string newStyleJSON) {
styleJSON.swap(newStyleJSON);
style->cancelTransitions();
style->loadJSON((const uint8_t *)styleJSON.c_str());
update();
}
@@ -386,16 +378,15 @@ bool Map::getDebug() const {
return debug;
}
void Map::setAppliedClasses(std::set<std::string> appliedClasses) {
style->cancelTransitions();
style->appliedClasses.swap(appliedClasses);
update();
void Map::toggleClass(const std::string &name) {
style->toggleClass(name);
if (style->hasTransitions()) {
update();
}
}
std::set<std::string> Map::getAppliedClasses() const {
return style->appliedClasses;
const std::vector<std::string> &Map::getAppliedClasses() const {
return style->getAppliedClasses();
}
void Map::setDefaultTransitionDuration(uint64_t duration_milliseconds) {
@@ -403,32 +394,23 @@ void Map::setDefaultTransitionDuration(uint64_t duration_milliseconds) {
}
void Map::updateTiles() {
for (auto &pair : sources) {
const std::unique_ptr<Source> &source = pair.second;
if (source->enabled) {
source->update();
}
for (const std::shared_ptr<Source> &source : style->getActiveSources()) {
source->update(*this);
}
}
void Map::updateRenderState() {
std::forward_list<Tile::ID> ids;
for (const auto &pair : sources) {
Source &source = *pair.second;
if (source.enabled) {
ids.splice_after(ids.before_begin(), source.getIDs());
source.updateMatrices(state);
}
for (const std::shared_ptr<Source> &source : style->getActiveSources()) {
ids.splice_after(ids.before_begin(), source->getIDs());
source->updateMatrices(painter.projMatrix, state);
}
const std::map<Tile::ID, ClipID> clipIDs = computeClipIDs(ids);
for (const auto &pair : sources) {
Source &source = *pair.second;
if (source.enabled) {
source.updateClipIDs(clipIDs);
}
for (const std::shared_ptr<Source> &source : style->getActiveSources()) {
source->updateClipIDs(clipIDs);
}
}
@@ -450,23 +432,28 @@ void Map::prepare() {
if (pixelRatioChanged) {
style->sprite = std::make_shared<Sprite>(*this, state.getPixelRatio());
style->sprite->load(kSpriteURL);
if (style->sprite_url.size()) {
style->sprite->load(style->sprite_url);
}
spriteAtlas->resize(state.getPixelRatio());
}
// Create a new glyph store object in case the glyph URL changed.
// TODO: Move this to a less frequently called place; we only need to do this when the
// stylesheet changes.
if (glyphStore && glyphStore->glyphURL != style->glyph_url) {
glyphStore.reset();
}
if (!glyphStore && style->glyph_url.size()) {
glyphStore = std::make_shared<GlyphStore>(style->glyph_url);
}
if (pixelRatioChanged || dimensionsChanged) {
painter.clearFramebuffers();
}
style->cascade(state.getNormalizedZoom());
// Update style transitions.
animationTime = util::now();
if (style->needsTransition()) {
style->updateTransitions(animationTime);
update();
}
style->updateProperties(state.getNormalizedZoom(), animationTime);
// Allow the sprite atlas to potentially pull new sprite images if needed.
if (style->sprite && style->sprite->isLoaded()) {
@@ -490,14 +477,13 @@ void Map::render() {
updateRenderState();
painter.drawClippingMasks(sources);
painter.drawClippingMasks(style->getActiveSources());
#if defined(DEBUG)
// Generate debug information
size_t source_id = 0;
for (const auto &pair : sources) {
const Source &source = *pair.second;
size_t count = source.getTileCount();
for (const std::shared_ptr<Source> &source : style->getActiveSources()) {
size_t count = source->getTileCount();
debug.emplace_back(util::sprintf<100>("source %d: %d\n", source_id, count));
source_id++;
}
@@ -511,11 +497,8 @@ void Map::render() {
// This guarantees that we have at least one function per tile called.
// When only rendering layers via the stylesheet, it's possible that we don't
// ever visit a tile during rendering.
for (const auto &pair : sources) {
Source &source = *pair.second;
const std::string &name = pair.first;
gl::group group(std::string("debug ") + name);
source.finishRender();
for (const std::shared_ptr<Source> &source : style->getActiveSources()) {
source->finishRender(painter);
}
painter.renderMatte();
@@ -525,16 +508,21 @@ void Map::render() {
#endif
// Schedule another rerender when we definitely need a next frame.
if (transform.needsTransition()) {
if (transform.needsTransition() || style->hasTransitions()) {
update();
}
glFlush();
}
void Map::renderLayers(const std::vector<LayerDescription>& layers) {
void Map::renderLayers(std::shared_ptr<StyleLayerGroup> group) {
if (!group) {
// Make sure that we actually do have a layer group.
return;
}
// TODO: Correctly compute the number of layers recursively beforehand.
float strata_thickness = 1.0f / (layers.size() + 1);
float strata_thickness = 1.0f / (group->layers.size() + 1);
// - FIRST PASS ------------------------------------------------------------
// Render everything top-to-bottom by using reverse iterators. Render opaque
@@ -543,9 +531,8 @@ void Map::renderLayers(const std::vector<LayerDescription>& layers) {
if (debug::renderTree) {
std::cout << std::string(indent++ * 4, ' ') << "OPAQUE {" << std::endl;
}
typedef std::vector<LayerDescription>::const_reverse_iterator riterator;
int i = 0;
for (riterator it = layers.rbegin(), end = layers.rend(); it != end; ++it, ++i) {
for (auto it = group->layers.rbegin(), end = group->layers.rend(); it != end; ++it, ++i) {
painter.setOpaque();
painter.setStrata(i * strata_thickness);
renderLayer(*it, Opaque);
@@ -560,9 +547,8 @@ void Map::renderLayers(const std::vector<LayerDescription>& layers) {
if (debug::renderTree) {
std::cout << std::string(indent++ * 4, ' ') << "TRANSLUCENT {" << std::endl;
}
typedef std::vector<LayerDescription>::const_iterator iterator;
--i;
for (iterator it = layers.begin(), end = layers.end(); it != end; ++it, --i) {
for (auto it = group->layers.begin(), end = group->layers.end(); it != end; ++it, --i) {
painter.setTranslucent();
painter.setStrata(i * strata_thickness);
renderLayer(*it, Translucent);
@@ -572,48 +558,22 @@ void Map::renderLayers(const std::vector<LayerDescription>& layers) {
}
}
template <typename Styles>
typename Styles::const_iterator find_style(const Styles &styles, const LayerDescription &layer_desc) {
auto it = styles.find(layer_desc.name);
#if defined(DEBUG)
if (it == styles.end()) {
if (debug::renderWarnings) {
fprintf(stderr, "[WARNING] can't find class named '%s' in style\n",
layer_desc.name.c_str());
}
}
#endif
return it;
}
template <typename Styles>
bool is_invisible(const Styles &styles, const LayerDescription &layer_desc) {
auto it = find_style(styles, layer_desc);
if (it == styles.end()) {
// We don't have a style, so we fall back to the default style which is
// always visible.
return false;
}
return !it->second.isVisible();
}
void Map::renderLayer(const LayerDescription& layer_desc, RenderPass pass) {
if (layer_desc.child_layer.size()) {
void Map::renderLayer(std::shared_ptr<StyleLayer> layer_desc, RenderPass pass) {
if (layer_desc->layers) {
// This is a layer group. We render them during our translucent render pass.
if (pass == Translucent) {
auto it = find_style(style->computed.composites, layer_desc);
const CompositeProperties &properties = (it != style->computed.composites.end()) ? it->second : defaultCompositeProperties;
const CompositeProperties &properties = layer_desc->getProperties<CompositeProperties>();
if (properties.isVisible()) {
gl::group group(std::string("group: ") + layer_desc.name);
gl::group group(std::string("group: ") + layer_desc->id);
if (debug::renderTree) {
std::cout << std::string(indent++ * 4, ' ') << "+ " << layer_desc.name
std::cout << std::string(indent++ * 4, ' ') << "+ " << layer_desc->id
<< " (Composite) {" << std::endl;
}
painter.pushFramebuffer();
renderLayers(layer_desc.child_layer);
renderLayers(layer_desc->layers);
GLuint texture = painter.popFramebuffer();
@@ -625,63 +585,51 @@ void Map::renderLayer(const LayerDescription& layer_desc, RenderPass pass) {
}
}
}
} else if (layer_desc.bucket_name == "background") {
} else if (layer_desc->id == "background") {
// This layer defines the background color.
} else {
// This is a singular layer. Try to find the bucket associated with
// this layer and render it.
auto bucket_it = style->buckets.find(layer_desc.bucket_name);
if (bucket_it != style->buckets.end()) {
const BucketDescription &bucket_desc = bucket_it->second;
// Abort early if we can already deduce from the bucket type that
// we're not going to render anything anyway during this pass.
switch (bucket_desc.type) {
case BucketType::Fill:
if (is_invisible(style->computed.fills, layer_desc)) return;
break;
case BucketType::Line:
if (pass == Opaque) return;
if (is_invisible(style->computed.lines, layer_desc)) return;
break;
case BucketType::Icon:
if (pass == Opaque) return;
if (is_invisible(style->computed.icons, layer_desc)) return;
break;
case BucketType::Text:
if (pass == Opaque) return;
if (is_invisible(style->computed.texts, layer_desc)) return;
break;
case BucketType::Raster:
if (pass == Translucent) return;
if (is_invisible(style->computed.rasters, layer_desc)) return;
break;
default:
break;
}
// Find the source and render all tiles in it.
auto source_it = sources.find(bucket_desc.source_name);
if (source_it != sources.end()) {
if (debug::renderTree) {
std::cout << std::string(indent * 4, ' ') << "- " << layer_desc.name << " ("
<< bucket_desc.type << ")" << std::endl;
}
const std::unique_ptr<Source> &source = source_it->second;
source->render(layer_desc, bucket_desc);
} else {
if (debug::renderWarnings) {
fprintf(stderr, "[WARNING] can't find source '%s' required for bucket '%s'\n",
bucket_desc.source_name.c_str(), layer_desc.bucket_name.c_str());
}
}
} else {
if (debug::renderWarnings) {
fprintf(stderr, "[WARNING] can't find bucket '%s' required for layer '%s'\n",
layer_desc.bucket_name.c_str(), layer_desc.name.c_str());
}
// This is a singular layer.
if (!layer_desc->bucket) {
fprintf(stderr, "[WARNING] layer '%s' is missing bucket\n", layer_desc->id.c_str());
return;
}
if (!layer_desc->bucket->source) {
fprintf(stderr, "[WARNING] can't find source for layer '%s'\n", layer_desc->id.c_str());
return;
}
// Abort early if we can already deduce from the bucket type that
// we're not going to render anything anyway during this pass.
switch (layer_desc->bucket->type) {
case BucketType::Fill:
if (!layer_desc->getProperties<FillProperties>().isVisible()) return;
break;
case BucketType::Line:
if (pass == Opaque) return;
if (!layer_desc->getProperties<LineProperties>().isVisible()) return;
break;
case BucketType::Icon:
if (pass == Opaque) return;
if (!layer_desc->getProperties<IconProperties>().isVisible()) return;
break;
case BucketType::Text:
if (pass == Opaque) return;
if (!layer_desc->getProperties<TextProperties>().isVisible()) return;
break;
case BucketType::Raster:
if (pass == Translucent) return;
if (!layer_desc->getProperties<RasterProperties>().isVisible()) return;
break;
default:
break;
}
if (debug::renderTree) {
std::cout << std::string(indent * 4, ' ') << "- " << layer_desc->id << " ("
<< layer_desc->bucket->type << ")" << std::endl;
}
layer_desc->bucket->source->render(painter, layer_desc);
}
}

View File

@@ -1,6 +1,6 @@
#include <llmr/map/map.hpp>
#include <llmr/map/raster_tile_data.hpp>
#include <llmr/style/layer_description.hpp>
#include <llmr/style/style.hpp>
using namespace llmr;
@@ -25,10 +25,10 @@ void RasterTileData::parse() {
}
}
void RasterTileData::render(Painter &painter, const LayerDescription& layer_desc) {
bucket.render(painter, layer_desc.name, id);
void RasterTileData::render(Painter &painter, std::shared_ptr<StyleLayer> layer_desc) {
bucket.render(painter, layer_desc, id);
}
bool RasterTileData::hasData(const LayerDescription& layer_desc) const {
bool RasterTileData::hasData(std::shared_ptr<StyleLayer> layer_desc) const {
return bucket.hasData();
}

View File

@@ -9,27 +9,38 @@
#include <llmr/util/vec.hpp>
#include <llmr/util/std.hpp>
#include <llmr/geometry/glyph_atlas.hpp>
#include <llmr/style/style_layer.hpp>
#include <llmr/map/vector_tile_data.hpp>
#include <llmr/map/raster_tile_data.hpp>
using namespace llmr;
namespace llmr {
Source::Source(Map &map, Painter &painter,
const char *url, Source::Type type, uint32_t tile_size,
uint32_t min_zoom, uint32_t max_zoom, bool enabled)
: enabled(enabled),
map(map),
painter(painter),
type(type),
url(url),
Source::Source(SourceType type, const std::string &url,
uint32_t tile_size, uint32_t min_zoom, uint32_t max_zoom)
: type(type),
url(normalizeSourceURL(url)),
tile_size(tile_size),
min_zoom(min_zoom),
max_zoom(max_zoom) {}
bool Source::update() {
return updateTiles();
std::string Source::normalizeSourceURL(const std::string &url) {
const std::string t = "mapbox://";
if (url.compare(0, t.length(), t) == 0) {
return std::string("http://api.tiles.mapbox.com/v3/") + url.substr(t.length()) + "/%d/%d/%d.vector.pbf";
} else {
return url;
}
}
bool Source::update(Map &map) {
if (map.getTime() > updated) {
return updateTiles(map);
} else {
return false;
}
}
void Source::updateClipIDs(const std::map<Tile::ID, ClipID> &mapping) {
@@ -44,11 +55,11 @@ void Source::updateClipIDs(const std::map<Tile::ID, ClipID> &mapping) {
});
}
void Source::updateMatrices(const TransformState &transform) {
void Source::updateMatrices(const mat4 &projMatrix, const TransformState &transform) {
for (std::pair<const Tile::ID, std::unique_ptr<Tile>> &pair : tiles) {
Tile &tile = *pair.second;
transform.matrixFor(tile.matrix, tile.id);
matrix::multiply(tile.matrix, painter.projMatrix, tile.matrix);
matrix::multiply(tile.matrix, projMatrix, tile.matrix);
}
}
@@ -56,7 +67,7 @@ size_t Source::getTileCount() const {
return tiles.size();
}
void Source::drawClippingMasks() {
void Source::drawClippingMasks(Painter &painter) {
for (std::pair<const Tile::ID, std::unique_ptr<Tile>> &pair : tiles) {
Tile &tile = *pair.second;
gl::group group(util::sprintf<32>("mask %d/%d/%d", tile.id.z, tile.id.y, tile.id.z));
@@ -64,10 +75,8 @@ void Source::drawClippingMasks() {
}
}
void Source::render(const LayerDescription& layer_desc, const BucketDescription &bucket_desc) {
if (!enabled) return;
gl::group group(std::string("layer: ") + layer_desc.name);
void Source::render(Painter &painter, std::shared_ptr<StyleLayer> layer_desc) {
gl::group group(std::string("layer: ") + layer_desc->id);
for (const std::pair<const Tile::ID, std::unique_ptr<Tile>> &pair : tiles) {
Tile &tile = *pair.second;
if (tile.data && tile.data->state == TileData::State::parsed) {
@@ -76,9 +85,7 @@ void Source::render(const LayerDescription& layer_desc, const BucketDescription
}
}
void Source::finishRender() {
if (!enabled) return;
void Source::finishRender(Painter &painter) {
for (std::pair<const Tile::ID, std::unique_ptr<Tile>> &pair : tiles) {
Tile &tile = *pair.second;
painter.renderTileDebug(tile);
@@ -108,7 +115,7 @@ TileData::State Source::hasTile(const Tile::ID& id) {
return TileData::State::invalid;
}
TileData::State Source::addTile(const Tile::ID& id) {
TileData::State Source::addTile(Map &map, const Tile::ID& id) {
const TileData::State state = hasTile(id);
if (state != TileData::State::invalid) {
@@ -138,12 +145,14 @@ TileData::State Source::addTile(const Tile::ID& id) {
std::string formed_url;
if (type == Source::Type::vector) {
if (type == SourceType::Vector) {
formed_url = util::sprintf<256>(url, normalized_id.z, normalized_id.x, normalized_id.y);
new_tile.data = std::make_shared<VectorTileData>(normalized_id, map, formed_url);
} else {
} else if (type == SourceType::Raster) {
formed_url = util::sprintf<256>(url, normalized_id.z, normalized_id.x, normalized_id.y, (map.getState().getPixelRatio() > 1.0 ? "@2x" : ""));
new_tile.data = std::make_shared<RasterTileData>(normalized_id, map, formed_url);
} else {
throw std::runtime_error("source type not implemented");
}
new_tile.data->request();
@@ -204,27 +213,27 @@ bool Source::findLoadedParent(const Tile::ID& id, int32_t minCoveringZoom, std::
return false;
}
bool Source::updateTiles() {
bool Source::updateTiles(Map &map) {
bool changed = false;
// Figure out what tiles we need to load
int32_t zoom = map.getState().getIntegerZoom();
if (zoom > max_zoom) zoom = max_zoom;
if (zoom < min_zoom) zoom = min_zoom;
int32_t clamped_zoom = map.getState().getIntegerZoom();
if (clamped_zoom > max_zoom) clamped_zoom = max_zoom;
if (clamped_zoom < min_zoom) clamped_zoom = min_zoom;
int32_t max_covering_zoom = zoom + 1;
int32_t max_covering_zoom = clamped_zoom + 1;
if (max_covering_zoom > max_zoom) max_covering_zoom = max_zoom;
int32_t min_covering_zoom = zoom - 10;
int32_t min_covering_zoom = clamped_zoom - 10;
if (min_covering_zoom < min_zoom) min_covering_zoom = min_zoom;
// Map four viewport corners to pixel coordinates
box box = map.getState().cornersToBox(zoom);
box box = map.getState().cornersToBox(clamped_zoom);
// Performs a scanline algorithm search that covers the rectangle of the box
// and sorts them by proximity to the center.
std::forward_list<Tile::ID> required = covering_tiles(zoom, box);
std::forward_list<Tile::ID> required = covering_tiles(map.getState(), clamped_zoom, box);
// Retain is a list of tiles that we shouldn't delete, even if they are not
// the most ideal tile for the current viewport. This may include tiles like
@@ -233,7 +242,7 @@ bool Source::updateTiles() {
// Add existing child/parent tiles if the actual tile is not yet loaded
for (const Tile::ID& id : required) {
const TileData::State state = addTile(id);
const TileData::State state = addTile(map, id);
if (state != TileData::State::parsed) {
// if (use_raster && (transform.rotating || transform.scaling || transform.panning))
@@ -288,6 +297,8 @@ bool Source::updateTiles() {
}
});
updated = map.getTime();
return changed;
}
@@ -353,30 +364,30 @@ void _scanTriangle(const llmr::vec2<double> a, const llmr::vec2<double> b, const
if (bc.dy) _scanSpans(ca, bc, ymin, ymax, scanLine);
}
double Source::getZoom() const {
double Source::getZoom(const TransformState &state) const {
double offset = log(util::tileSize / tile_size) / log(2);
offset += (map.getState().getPixelRatio() > 1.0 ? 1 :0);
return map.getState().getZoom() + offset;
offset += (state.getPixelRatio() > 1.0 ? 1 :0);
return state.getZoom() + offset;
}
std::forward_list<llmr::Tile::ID> Source::covering_tiles(int32_t zoom, const box& points) {
int32_t dim = std::pow(2, zoom);
std::forward_list<llmr::Tile::ID> Source::covering_tiles(const TransformState &state, int32_t clamped_zoom, const box& points) {
int32_t dim = std::pow(2, clamped_zoom);
std::forward_list<llmr::Tile::ID> tiles;
bool is_raster = (type == Type::raster);
double search_zoom = getZoom();
bool is_raster = (type == SourceType::Raster);
double search_zoom = getZoom(state);
auto scanLine = [&tiles, zoom, is_raster, search_zoom](int32_t x0, int32_t x1, int32_t y, int32_t ymax) {
auto scanLine = [&tiles, clamped_zoom, is_raster, search_zoom](int32_t x0, int32_t x1, int32_t y, int32_t ymax) {
int32_t x;
if (y >= 0 && y <= ymax) {
for (x = x0; x < x1; x++) {
if (is_raster) {
Tile::ID id = Tile::ID(zoom, x, y);
Tile::ID id = Tile::ID(clamped_zoom, x, y);
auto ids = id.children(search_zoom);
for (const Tile::ID& child_id : ids) {
tiles.emplace_front(child_id.z, child_id.x, child_id.y);
}
} else {
tiles.emplace_front(zoom, x, y);
tiles.emplace_front(clamped_zoom, x, y);
}
}
}
@@ -400,3 +411,5 @@ std::forward_list<llmr::Tile::ID> Source::covering_tiles(int32_t zoom, const box
return tiles;
}
}

View File

@@ -2,6 +2,8 @@
#include <llmr/map/vector_tile_data.hpp>
#include <llmr/style/style.hpp>
#include <llmr/style/style_layer.hpp>
#include <llmr/style/style_layer_group.hpp>
#include <llmr/renderer/fill_bucket.hpp>
#include <llmr/renderer/line_bucket.hpp>
#include <llmr/renderer/icon_bucket.hpp>
@@ -62,72 +64,69 @@ void TileParser::addGlyph(uint64_t tileid, const std::string stackname,
}
}
void TileParser::parseStyleLayers(const std::vector<LayerDescription> &layers) {
for (const LayerDescription &layer_desc : layers) {
void TileParser::parseStyleLayers(std::shared_ptr<StyleLayerGroup> group) {
if (!group) {
return;
}
for (const std::shared_ptr<StyleLayer> &layer_desc : group->layers) {
// Cancel early when parsing.
if (obsolete()) {
return;
}
if (layer_desc.bucket_name == "background") {
if (layer_desc->isBackground()) {
// background is a special, fake bucket
continue;
}
if (layer_desc.child_layer.size()) {
} else if (layer_desc->layers) {
// This is a layer group.
// TODO: create framebuffer
parseStyleLayers(layer_desc.child_layer);
// TODO: render framebuffer on previous framebuffer
} else {
parseStyleLayers(layer_desc->layers);
} else if (layer_desc->bucket) {
// This is a singular layer. Check if this bucket already exists. If not,
// parse this bucket.
auto bucket_it = tile.buckets.find(layer_desc.bucket_name);
auto bucket_it = tile.buckets.find(layer_desc->bucket->name);
if (bucket_it == tile.buckets.end()) {
auto bucket_it = style->buckets.find(layer_desc.bucket_name);
if (bucket_it != style->buckets.end()) {
// Only create the new bucket if we have an actual specification
// for it.
std::unique_ptr<Bucket> bucket = createBucket(bucket_it->second);
if (bucket) {
// Bucket creation might fail because the data tile may not
// contain any data that falls into this bucket.
tile.buckets[layer_desc.bucket_name] = std::move(bucket);
}
} else {
// There is no proper specification for this bucket, even though
// it is referenced in the stylesheet.
if (debug::tileParseWarnings) {
fprintf(stderr, "Stylesheet specifies bucket %s, but it is not defined\n",
layer_desc.bucket_name.c_str());
}
// We need to create this bucket since it doesn't exist yet.
std::unique_ptr<Bucket> bucket = createBucket(layer_desc->bucket);
if (bucket) {
// Bucket creation might fail because the data tile may not
// contain any data that falls into this bucket.
tile.buckets[layer_desc->bucket->name] = std::move(bucket);
}
}
} else {
fprintf(stderr, "[WARNING] layer '%s' does not have child layers or buckets\n", layer_desc->id.c_str());
}
}
}
std::unique_ptr<Bucket> TileParser::createBucket(const BucketDescription &bucket_desc) {
auto layer_it = vector_data.layers.find(bucket_desc.source_layer);
std::unique_ptr<Bucket> TileParser::createBucket(std::shared_ptr<StyleBucket> bucket_desc) {
if (!bucket_desc) {
fprintf(stderr, "missing bucket desc\n");
return nullptr;
}
auto layer_it = vector_data.layers.find(bucket_desc->source_layer);
if (layer_it != vector_data.layers.end()) {
const VectorTileLayer &layer = layer_it->second;
if (bucket_desc.type == BucketType::Fill) {
return createFillBucket(layer, bucket_desc);
} else if (bucket_desc.type == BucketType::Line) {
return createLineBucket(layer, bucket_desc);
} else if (bucket_desc.type == BucketType::Icon) {
return createIconBucket(layer, bucket_desc);
} else if (bucket_desc.type == BucketType::Text) {
return createTextBucket(layer, bucket_desc);
if (bucket_desc->render.is<StyleBucketFill>()) {
return createFillBucket(layer, bucket_desc->filter, bucket_desc->render.get<StyleBucketFill>());
} else if (bucket_desc->render.is<StyleBucketLine>()) {
return createLineBucket(layer, bucket_desc->filter, bucket_desc->render.get<StyleBucketLine>());
} else if (bucket_desc->render.is<StyleBucketIcon>()) {
return createIconBucket(layer, bucket_desc->filter, bucket_desc->render.get<StyleBucketIcon>());
} else if (bucket_desc->render.is<StyleBucketText>()) {
return createTextBucket(layer, bucket_desc->filter, bucket_desc->render.get<StyleBucketText>());
} else if (bucket_desc->render.is<StyleBucketRaster>()) {
return nullptr;
} else {
throw std::runtime_error("unknown bucket type");
fprintf(stderr, "[WARNING] unknown bucket render type for layer '%s'\n", bucket_desc->source_layer.c_str());
}
} else {
// The layer specified in the bucket does not exist. Do nothing.
if (debug::tileParseWarnings) {
fprintf(stderr, "[WARNING] layer '%s' does not exist in tile %d/%d/%d of source '%s'\n",
bucket_desc.source_layer.c_str(), tile.id.z, tile.id.x, tile.id.y,
bucket_desc.source_name.c_str());
fprintf(stderr, "[WARNING] layer '%s' does not exist in tile %d/%d/%d\n",
bucket_desc->source_layer.c_str(), tile.id.z, tile.id.x, tile.id.y);
}
}
@@ -135,9 +134,8 @@ std::unique_ptr<Bucket> TileParser::createBucket(const BucketDescription &bucket
}
template <class Bucket>
void TileParser::addBucketFeatures(Bucket &bucket, const VectorTileLayer &layer,
const BucketDescription &bucket_desc) {
FilteredVectorTileLayer filtered_layer(layer, bucket_desc);
void TileParser::addBucketFeatures(Bucket& bucket, const VectorTileLayer& layer, const FilterExpression &filter) {
FilteredVectorTileLayer filtered_layer(layer, filter);
for (pbf feature : filtered_layer) {
if (obsolete())
return;
@@ -154,64 +152,44 @@ void TileParser::addBucketFeatures(Bucket &bucket, const VectorTileLayer &layer,
}
template <class Bucket, typename... Args>
void TileParser::addBucketFeatures(Bucket &bucket, const VectorTileLayer &layer,
const BucketDescription &bucket_desc, Args &&... args) {
FilteredVectorTileLayer filtered_layer(layer, bucket_desc);
void TileParser::addBucketFeatures(Bucket& bucket, const VectorTileLayer& layer, const FilterExpression &filter, Args&& ...args) {
FilteredVectorTileLayer filtered_layer(layer, filter);
for (const pbf &feature_pbf : filtered_layer) {
if (obsolete())
if (obsolete()) {
return;
}
bucket->addFeature({feature_pbf, layer}, std::forward<Args>(args)...);
}
}
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.render.get<BucketFillDescription>());
addBucketFeatures(bucket, layer, bucket_desc);
std::unique_ptr<Bucket> TileParser::createFillBucket(const VectorTileLayer& layer, const FilterExpression &filter, const StyleBucketFill &fill) {
std::unique_ptr<FillBucket> bucket = std::make_unique<FillBucket>(tile.fillVertexBuffer, tile.triangleElementsBuffer, tile.lineElementsBuffer, fill);
addBucketFeatures(bucket, layer, filter);
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.render.get<BucketLineDescription>());
addBucketFeatures(bucket, layer, bucket_desc);
std::unique_ptr<Bucket> TileParser::createLineBucket(const VectorTileLayer& layer, const FilterExpression &filter, const StyleBucketLine &line) {
std::unique_ptr<LineBucket> bucket = std::make_unique<LineBucket>(tile.lineVertexBuffer, tile.triangleElementsBuffer, tile.pointElementsBuffer, line);
addBucketFeatures(bucket, layer, filter);
return obsolete() ? nullptr : std::move(bucket);
}
std::unique_ptr<Bucket> TileParser::createIconBucket(const VectorTileLayer &layer,
const BucketDescription &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);
std::unique_ptr<Bucket> TileParser::createIconBucket(const VectorTileLayer& layer, const FilterExpression &filter, const StyleBucketIcon &icon) {
std::unique_ptr<IconBucket> bucket = std::make_unique<IconBucket>(tile.iconVertexBuffer, icon);
addBucketFeatures(bucket, layer, filter, *spriteAtlas);
return obsolete() ? nullptr : std::move(bucket);
}
typedef std::pair<uint16_t, uint16_t> GlyphRange;
std::unique_ptr<Bucket> TileParser::createTextBucket(const VectorTileLayer& layer, const FilterExpression &filter, const StyleBucketText &text) {
std::unique_ptr<Bucket> TileParser::createTextBucket(const VectorTileLayer &layer,
const BucketDescription &bucket_desc) {
if (!bucket_desc.render.valid() || !bucket_desc.render.is<BucketTextDescription>()) {
// Make sure that we always have a valid glyph store. If this is not set, the stylesheet
// doesn't specify a glyph URL.
if (!glyphStore) {
return nullptr;
}
const BucketTextDescription &properties = bucket_desc.render.get<BucketTextDescription>();
const StyleBucketText &properties = text;
std::unique_ptr<TextBucket> bucket = std::make_unique<TextBucket>(
tile.textVertexBuffer, tile.triangleElementsBuffer, properties, placement);
@@ -224,7 +202,7 @@ std::unique_ptr<Bucket> TileParser::createTextBucket(const VectorTileLayer &laye
{
std::set<GlyphRange> ranges;
FilteredVectorTileLayer filtered_layer(layer, bucket_desc);
FilteredVectorTileLayer filtered_layer(layer, filter);
for (const pbf &feature_pbf : filtered_layer) {
if (obsolete())
return nullptr;
@@ -234,7 +212,7 @@ std::unique_ptr<Bucket> TileParser::createTextBucket(const VectorTileLayer &laye
util::replaceTokens(properties.field, feature.properties));
// Loop through all characters of this text and collect unique codepoints.
for (uint32_t chr : string) {
for (char32_t chr : string) {
ranges.insert(getGlyphRange(chr));
}

View File

@@ -44,13 +44,13 @@ bool Transform::resize(const uint16_t w, const uint16_t h, const float ratio,
#pragma mark - Position
void Transform::moveBy(const double dx, const double dy, const time duration) {
void Transform::moveBy(const double dx, const double dy, const timestamp duration) {
uv::writelock lock(mtx);
_moveBy(dx, dy, duration);
}
void Transform::_moveBy(const double dx, const double dy, const time duration) {
void Transform::_moveBy(const double dx, const double dy, const timestamp duration) {
// This is only called internally, so we don't need a lock here.
final.x = current.x + std::cos(current.angle) * dx + std::sin(current.angle) * dy;
@@ -69,7 +69,7 @@ void Transform::_moveBy(const double dx, const double dy, const time duration) {
current.y = final.y;
} else {
// Use a common start time for all of the transitions to avoid divergent transitions.
time start = util::now();
timestamp start = util::now();
transitions.emplace_front(
std::make_shared<util::ease_transition<double>>(current.x, final.x, current.x, start, duration));
transitions.emplace_front(
@@ -79,7 +79,7 @@ void Transform::_moveBy(const double dx, const double dy, const time duration) {
platform::notify_map_change();
}
void Transform::setLonLat(const double lon, const double lat, const time duration) {
void Transform::setLonLat(const double lon, const double lat, const timestamp duration) {
uv::writelock lock(mtx);
const double f = std::fmin(std::fmax(std::sin(D2R * lat), -0.9999), 0.9999);
@@ -90,7 +90,7 @@ void Transform::setLonLat(const double lon, const double lat, const time duratio
}
void Transform::setLonLatZoom(const double lon, const double lat, const double zoom,
const time duration) {
const timestamp duration) {
uv::writelock lock(mtx);
double new_scale = std::pow(2.0, zoom);
@@ -128,7 +128,7 @@ void Transform::startPanning() {
// Add a 200ms timeout for resetting this to false
current.panning = true;
time start = util::now();
timestamp start = util::now();
pan_timeout = std::make_shared<util::timeout<bool>>(false, current.panning, start, 200_milliseconds);
transitions.emplace_front(pan_timeout);
}
@@ -149,7 +149,7 @@ void Transform::_clearPanning() {
#pragma mark - Zoom
void Transform::scaleBy(const double ds, const double cx, const double cy, const time duration) {
void Transform::scaleBy(const double ds, const double cx, const double cy, const timestamp duration) {
uv::writelock lock(mtx);
// clamp scale to min/max values
@@ -164,13 +164,13 @@ void Transform::scaleBy(const double ds, const double cx, const double cy, const
}
void Transform::setScale(const double scale, const double cx, const double cy,
const time duration) {
const timestamp duration) {
uv::writelock lock(mtx);
_setScale(scale, cx, cy, duration);
}
void Transform::setZoom(const double zoom, const time duration) {
void Transform::setZoom(const double zoom, const timestamp duration) {
uv::writelock lock(mtx);
_setScale(std::pow(2.0, zoom), -1, -1, duration);
@@ -195,7 +195,7 @@ void Transform::startScaling() {
// Add a 200ms timeout for resetting this to false
current.scaling = true;
time start = util::now();
timestamp start = util::now();
scale_timeout = std::make_shared<util::timeout<bool>>(false, current.scaling, start, 200_milliseconds);
transitions.emplace_front(scale_timeout);
}
@@ -228,7 +228,7 @@ void Transform::_clearScaling() {
}
}
void Transform::_setScale(double new_scale, double cx, double cy, const time duration) {
void Transform::_setScale(double new_scale, double cx, double cy, const timestamp duration) {
// This is only called internally, so we don't need a lock here.
// Ensure that we don't zoom in further than the maximum allowed.
@@ -262,7 +262,7 @@ void Transform::_setScale(double new_scale, double cx, double cy, const time dur
}
void Transform::_setScaleXY(const double new_scale, const double xn, const double yn,
const time duration) {
const timestamp duration) {
// This is only called internally, so we don't need a lock here.
final.scale = new_scale;
@@ -280,7 +280,7 @@ void Transform::_setScaleXY(const double new_scale, const double xn, const doubl
current.y = final.y;
} else {
// Use a common start time for all of the transitions to avoid divergent transitions.
time start = util::now();
timestamp start = util::now();
transitions.emplace_front(std::make_shared<util::ease_transition<double>>(
current.scale, final.scale, current.scale, start, duration));
transitions.emplace_front(
@@ -313,7 +313,7 @@ void Transform::constrain(double& scale, double& y) const {
#pragma mark - Angle
void Transform::rotateBy(const double start_x, const double start_y, const double end_x,
const double end_y, const time duration) {
const double end_y, const timestamp duration) {
uv::writelock lock(mtx);
double center_x = current.width / 2, center_y = current.height / 2;
@@ -343,7 +343,7 @@ void Transform::rotateBy(const double start_x, const double start_y, const doubl
_setAngle(ang, duration);
}
void Transform::setAngle(const double new_angle, const time duration) {
void Transform::setAngle(const double new_angle, const timestamp duration) {
uv::writelock lock(mtx);
_setAngle(new_angle, duration);
@@ -367,7 +367,7 @@ void Transform::setAngle(const double new_angle, const double cx, const double c
}
}
void Transform::_setAngle(double new_angle, const time duration) {
void Transform::_setAngle(double new_angle, const timestamp duration) {
// This is only called internally, so we don't need a lock here.
while (new_angle > M_PI)
@@ -383,7 +383,7 @@ void Transform::_setAngle(double new_angle, const time duration) {
if (duration == 0) {
current.angle = final.angle;
} else {
time start = util::now();
timestamp start = util::now();
transitions.emplace_front(std::make_shared<util::ease_transition<double>>(
current.angle, final.angle, current.angle, start, duration));
}
@@ -404,7 +404,7 @@ void Transform::startRotating() {
// Add a 200ms timeout for resetting this to false
current.rotating = true;
time start = util::now();
timestamp start = util::now();
rotate_timeout = std::make_shared<util::timeout<bool>>(false, current.rotating, start, 200_milliseconds);
transitions.emplace_front(rotate_timeout);
}
@@ -437,7 +437,7 @@ bool Transform::needsTransition() const {
return !transitions.empty();
}
void Transform::updateTransitions(const time now) {
void Transform::updateTransitions(const timestamp now) {
uv::writelock lock(mtx);
transitions.remove_if([now](const std::shared_ptr<util::transition> &transition) {

View File

@@ -1,5 +1,4 @@
#include <llmr/map/vector_tile.hpp>
#include <llmr/style/bucket_description.hpp>
#include <algorithm>
#include <iostream>
@@ -102,9 +101,9 @@ VectorTileLayer::VectorTileLayer(pbf layer) : data(layer) {
}
}
FilteredVectorTileLayer::FilteredVectorTileLayer(const VectorTileLayer& layer, const BucketDescription& bucket_desc)
FilteredVectorTileLayer::FilteredVectorTileLayer(const VectorTileLayer& layer, const FilterExpression &filterExpression)
: layer(layer),
bucket_desc(bucket_desc) {
filterExpression(filterExpression) {
}
FilteredVectorTileLayer::iterator FilteredVectorTileLayer::begin() const {
@@ -122,119 +121,167 @@ FilteredVectorTileLayer::iterator::iterator(const FilteredVectorTileLayer& paren
operator++();
}
bool FilteredVectorTileLayer::iterator::matchesFilterExpression(const PropertyFilterExpression &filterExpression, const pbf &tags_pbf) {
if (filterExpression.is<util::recursive_wrapper<PropertyFilter>>()) {
return matchesFilter(filterExpression.get<util::recursive_wrapper<PropertyFilter>>().get(), tags_pbf);
} else if (filterExpression.is<util::recursive_wrapper<PropertyExpression>>()) {
return matchesExpression(filterExpression.get<util::recursive_wrapper<PropertyExpression>>().get(), tags_pbf);
} else if (filterExpression.is<std::true_type>()) {
return true;
} else {
return false;
}
//bool FilteredVectorTileLayer::iterator::matchesFilterExpression(const FilterExpression &filterExpression, const pbf &tags_pbf) {
// if (filterExpression.empty()) {
// return true;
// }
//
//
//
//
// if (filterExpression.is<util::recursive_wrapper<PropertyFilter>>()) {
// return matchesFilter(filterExpression.get<util::recursive_wrapper<PropertyFilter>>().get(), tags_pbf);
// } else if (filterExpression.is<util::recursive_wrapper<PropertyExpression>>()) {
// return matchesExpression(filterExpression.get<util::recursive_wrapper<PropertyExpression>>().get(), tags_pbf);
// } else if (filterExpression.is<std::true_type>()) {
// return true;
// } else {
// return false;
// }
// return true;
//}
//
//bool FilteredVectorTileLayer::iterator::matchesFilter(const PropertyFilter &filter, const pbf &const_tags_pbf) {
// auto field_it = parent.layer.key_index.find(filter.field);
// if (field_it != parent.layer.key_index.end()) {
// const uint32_t filter_key = field_it->second;
//
// // Now loop through all the key/value pair tags.
// // tags are packed varints. They should have an even length.
// pbf tags_pbf = const_tags_pbf;
// while (tags_pbf) {
// uint32_t tag_key = tags_pbf.varint();
// if (!tags_pbf) {
// // This should not happen; otherwise the vector tile
// // is invalid.
// throw std::runtime_error("uneven number of feature tag ids");
// }
// uint32_t tag_val = tags_pbf.varint();
//
// if (tag_key == filter_key) {
// if (parent.layer.values.size() > tag_val) {
// const Value &value = parent.layer.values[tag_val];
// return filter.compare(value);
// } else {
// throw std::runtime_error("feature references out of range value");
// }
// }
// }
// }
//
// // The feature doesn't contain the field that we're looking to compare.
// // Depending on the filter, this may still be okay.
// return filter.isMissingFieldOkay();
//}
//
//bool FilteredVectorTileLayer::iterator::matchesExpression(const PropertyExpression &expression, const pbf &tags_pbf) {
// if (expression.op == ExpressionOperator::Or) {
// for (const PropertyFilterExpression &filterExpression : expression.operands) {
// if (matchesFilterExpression(filterExpression, tags_pbf)) {
// return true;
// }
// }
// return false;
// } else if (expression.op == ExpressionOperator::And) {
// for (const PropertyFilterExpression &filterExpression : expression.operands) {
// if (!matchesFilterExpression(filterExpression, tags_pbf)) {
// return false;
// }
// }
// return true;
// } else {
// return false;
// }
//}
//
VectorTileTagExtractor::VectorTileTagExtractor(const VectorTileLayer &layer) : layer_(layer) {}
void VectorTileTagExtractor::setTags(const pbf &pbf) {
tags_ = pbf;
}
std::forward_list<Value> VectorTileTagExtractor::getValues(const std::string &key) const {
std::forward_list<Value> values;
bool FilteredVectorTileLayer::iterator::matchesFilter(const PropertyFilter &filter, const pbf &const_tags_pbf) {
auto field_it = parent.layer.key_index.find(filter.field);
if (field_it != parent.layer.key_index.end()) {
auto field_it = layer_.key_index.find(key);
if (field_it != layer_.key_index.end()) {
auto it = values.before_begin();
const uint32_t filter_key = field_it->second;
// Now loop through all the key/value pair tags.
// tags are packed varints. They should have an even length.
pbf tags_pbf = const_tags_pbf;
pbf tags_pbf = tags_;
uint32_t tag_key, tag_val;
while (tags_pbf) {
uint32_t tag_key = tags_pbf.varint();
tag_key = tags_pbf.varint();
if (!tags_pbf) {
// This should not happen; otherwise the vector tile
// is invalid.
throw std::runtime_error("uneven number of feature tag ids");
// This should not happen; otherwise the vector tile is invalid.
fprintf(stderr, "[WARNING] uneven number of feature tag ids\n");
return values;
}
uint32_t tag_val = tags_pbf.varint();
// Note: We need to run this command in all cases, even if the keys don't match.
tag_val = tags_pbf.varint();
if (tag_key == filter_key) {
if (parent.layer.values.size() > tag_val) {
const Value &value = parent.layer.values[tag_val];
return filter.compare(value);
if (layer_.values.size() > tag_val) {
it = values.emplace_after(it, layer_.values[tag_val]);
} else {
throw std::runtime_error("feature references out of range value");
fprintf(stderr, "[WARNING] feature references out of range value\n");
break;
}
}
}
}
// The feature doesn't contain the field that we're looking to compare.
// Depending on the filter, this may still be okay.
return filter.isMissingFieldOkay();
return values;
}
bool FilteredVectorTileLayer::iterator::matchesExpression(const PropertyExpression &expression, const pbf &tags_pbf) {
if (expression.op == ExpressionOperator::Or) {
for (const PropertyFilterExpression &filterExpression : expression.operands) {
if (matchesFilterExpression(filterExpression, tags_pbf)) {
return true;
}
}
return false;
} else if (expression.op == ExpressionOperator::And) {
for (const PropertyFilterExpression &filterExpression : expression.operands) {
if (!matchesFilterExpression(filterExpression, tags_pbf)) {
return false;
}
}
return true;
} else {
return false;
}
void VectorTileTagExtractor::setType(FilterExpression::GeometryType type) {
type_ = type;
}
FilterExpression::GeometryType VectorTileTagExtractor::getType() const {
return type_;
}
void FilteredVectorTileLayer::iterator::operator++() {
valid = false;
const PropertyFilterExpression &expression = parent.bucket_desc.filter;
const FilterExpression &expression = parent.filterExpression;
while (data.next(2)) { // feature
feature = data.message();
pbf feature_pbf = feature;
BucketType type = BucketType::None;
bool matched = false;
// Treat the absence of any expression filters as a match.
if (expression.is<std::true_type>()) {
if (expression.empty()) {
matched = true;
}
while (feature_pbf.next()) {
if (feature_pbf.tag == 2) { // tags
if (matched) {
// There is no filter, so we want to parse the entire layer anyway.
feature_pbf.skip();
} else {
// We only want to parse some features.
const pbf tags_pbf = feature_pbf.message();
matched = matchesFilterExpression(expression, tags_pbf);
if (!matched) {
break; // feature_pbf loop early
}
}
} else if (feature_pbf.tag == 3) { // geometry type
switch (feature_pbf.varint()) {
case 1 /* Point */: type = BucketType::Icon; break;
case 2 /* LineString */: type = BucketType::Line; break;
case 3 /* Polygon */: type = BucketType::Fill; break;
default: type = BucketType::None; break;
}
if (!matched) {
VectorTileTagExtractor extractor(parent.layer);
if (type != parent.bucket_desc.feature_type) {
matched = false;
break; // feature_pbf loop early
// Retrieve the basic information
while (feature_pbf.next()) {
if (feature_pbf.tag == 2) { // tags
extractor.setTags(feature_pbf.message());
} else if (feature_pbf.tag == 3) { // geometry type
switch (FeatureType(feature_pbf.varint())) {
case FeatureType::Point: extractor.setType(FilterExpression::GeometryType::Point); break;
case FeatureType::LineString: extractor.setType(FilterExpression::GeometryType::Line); break;
case FeatureType::Polygon: extractor.setType(FilterExpression::GeometryType::Polygon); break;
default: break;
}
} else {
feature_pbf.skip();
}
} else {
feature_pbf.skip();
}
matched = expression.compare(extractor);
}
if (matched) {

View File

@@ -1,6 +1,8 @@
#include <llmr/map/vector_tile_data.hpp>
#include <llmr/util/std.hpp>
#include <llmr/map/map.hpp>
#include <llmr/style/style_layer.hpp>
#include <llmr/style/style_bucket.hpp>
using namespace llmr;
@@ -9,7 +11,10 @@ VectorTileData::VectorTileData(Tile::ID id, Map &map, const std::string url)
}
VectorTileData::~VectorTileData() {
map.getGlyphAtlas()->removeGlyphs(id.to_uint64());
std::shared_ptr<GlyphAtlas> glyphAtlas = map.getGlyphAtlas();
if (glyphAtlas) {
glyphAtlas->removeGlyphs(id.to_uint64());
}
}
void VectorTileData::beforeParse() {
@@ -43,22 +48,23 @@ void VectorTileData::afterParse() {
parser.reset();
}
void VectorTileData::render(Painter &painter, const LayerDescription& layer_desc) {
auto databucket_it = buckets.find(layer_desc.bucket_name);
if (databucket_it != buckets.end()) {
assert(databucket_it->second);
databucket_it->second->render(painter, layer_desc.name, id);
void VectorTileData::render(Painter &painter, std::shared_ptr<StyleLayer> layer_desc) {
if (state == State::parsed && layer_desc->bucket) {
auto databucket_it = buckets.find(layer_desc->bucket->name);
if (databucket_it != buckets.end()) {
assert(databucket_it->second);
databucket_it->second->render(painter, layer_desc, id);
}
}
}
bool VectorTileData::hasData(const LayerDescription& layer_desc) const {
if (state != State::parsed) return false;
auto databucket_it = buckets.find(layer_desc.bucket_name);
if (databucket_it != buckets.end()) {
assert(databucket_it->second);
return databucket_it->second->hasData();
} else {
return false;
bool VectorTileData::hasData(std::shared_ptr<StyleLayer> layer_desc) const {
if (state == State::parsed && layer_desc->bucket) {
auto databucket_it = buckets.find(layer_desc->bucket->name);
if (databucket_it != buckets.end()) {
assert(databucket_it->second);
return databucket_it->second->hasData();
}
}
return false;
}

View File

@@ -14,7 +14,7 @@ DebugBucket::DebugBucket(DebugFontBuffer& fontBuffer)
: fontBuffer(fontBuffer) {
}
void DebugBucket::render(Painter& painter, const std::string& /*layer_name*/, const Tile::ID& /*id*/) {
void DebugBucket::render(Painter& painter, std::shared_ptr<StyleLayer> layer_desc, const Tile::ID& /*id*/) {
painter.renderDebugText(*this);
}

View File

@@ -33,7 +33,7 @@ void FillBucket::free(void *, void *ptr) {
FillBucket::FillBucket(FillVertexBuffer &vertexBuffer,
TriangleElementsBuffer &triangleElementsBuffer,
LineElementsBuffer &lineElementsBuffer,
const BucketFillDescription &properties)
const StyleBucketFill &properties)
: properties(properties),
allocator(new TESSalloc{&alloc, &realloc, &free, nullptr, // userData
64, // meshEdgeBucketSize
@@ -204,8 +204,8 @@ void FillBucket::tessellate() {
lineGroup.vertex_length += total_vertex_count;
}
void FillBucket::render(Painter& painter, const std::string& layer_name, const Tile::ID& id) {
painter.renderFill(*this, layer_name, id);
void FillBucket::render(Painter& painter, std::shared_ptr<StyleLayer> layer_desc, const Tile::ID& id) {
painter.renderFill(*this, layer_desc, id);
}
bool FillBucket::hasData() const {

View File

@@ -3,7 +3,7 @@
using namespace llmr;
// Record frame history that will be used to calculate fading params
void FrameHistory::record(time now, float zoom) {
void FrameHistory::record(timestamp now, float zoom) {
// first frame ever
if (!history.size()) {
history.emplace_back(FrameSnapshot{0, zoom});
@@ -15,7 +15,7 @@ void FrameHistory::record(time now, float zoom) {
}
}
bool FrameHistory::needsAnimation(const time duration) const {
bool FrameHistory::needsAnimation(const timestamp duration) const {
if (!history.size()) {
return false;
}
@@ -26,7 +26,7 @@ bool FrameHistory::needsAnimation(const time duration) const {
const FrameSnapshot &pivot = history.back();
int i = -1;
while ((int)history.size() > i + 1 && history[i + 1].timestamp + duration < pivot.timestamp) {
while ((int)history.size() > i + 1 && history[i + 1].t + duration < pivot.t) {
i++;
}

View File

@@ -2,6 +2,7 @@
#include <llmr/geometry/icon_buffer.hpp>
#include <llmr/geometry/elements_buffer.hpp>
#include <llmr/geometry/geometry.hpp>
#include <llmr/geometry/sprite_atlas.hpp>
#include <llmr/renderer/painter.hpp>
#include <llmr/style/style.hpp>
@@ -19,7 +20,7 @@ struct geometry_too_long_exception : std::exception {};
using namespace llmr;
IconBucket::IconBucket(IconVertexBuffer& vertexBuffer,
const BucketIconDescription& properties)
const StyleBucketIcon& properties)
: properties(properties),
vertexBuffer(vertexBuffer),
vertex_start(vertexBuffer.index()) {
@@ -57,8 +58,8 @@ void IconBucket::addFeature(const VectorTileFeature &feature, SpriteAtlas &sprit
vertex_end = vertexBuffer.index();
}
void IconBucket::render(Painter& painter, const std::string& layer_name, const Tile::ID& id) {
painter.renderIcon(*this, layer_name, id);
void IconBucket::render(Painter& painter, std::shared_ptr<StyleLayer> layer_desc, const Tile::ID& id) {
painter.renderIcon(*this, layer_desc, id);
}
bool IconBucket::hasData() const {

View File

@@ -20,7 +20,7 @@ using namespace llmr;
LineBucket::LineBucket(LineVertexBuffer& vertexBuffer,
TriangleElementsBuffer& triangleElementsBuffer,
PointElementsBuffer& pointElementsBuffer,
const BucketLineDescription& properties)
const StyleBucketLine& properties)
: properties(properties),
vertexBuffer(vertexBuffer),
triangleElementsBuffer(triangleElementsBuffer),
@@ -341,8 +341,8 @@ void LineBucket::addGeometry(const std::vector<Coordinate>& vertices) {
}
}
void LineBucket::render(Painter& painter, const std::string& layer_name, const Tile::ID& id) {
painter.renderLine(*this, layer_name, id);
void LineBucket::render(Painter& painter, std::shared_ptr<StyleLayer> layer_desc, const Tile::ID& id) {
painter.renderLine(*this, layer_desc, id);
}
bool LineBucket::hasData() const {

View File

@@ -1,5 +1,6 @@
#include <llmr/renderer/painter.hpp>
#include <llmr/map/map.hpp>
#include <llmr/style/style_layer.hpp>
#include <llmr/util/std.hpp>
#include <llmr/util/string.hpp>
#include <llmr/util/time.hpp>
@@ -135,13 +136,10 @@ void Painter::clear() {
gl::group group("clear");
glStencilMask(0xFF);
depthMask(true);
#ifdef NVIDIA
// We're painting in a way that allows us to skip clearing the color buffer.
// On Tegra hardware, this is faster.
glClear(GL_STENCIL_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
#else
const BackgroundProperties &properties = map.getStyle()->getBackgroundProperties();
glClearColor(properties.color[0], properties.color[1], properties.color[2], properties.color[3]);
glClear(GL_COLOR_BUFFER_BIT | GL_STENCIL_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
#endif
}
void Painter::setOpaque() {
@@ -172,10 +170,10 @@ void Painter::prepareTile(const Tile& tile) {
glStencilFunc(GL_EQUAL, id, mask);
}
void Painter::renderTileLayer(const Tile& tile, const LayerDescription &layer_desc) {
void Painter::renderTileLayer(const Tile& tile, std::shared_ptr<StyleLayer> layer_desc) {
assert(tile.data);
if (tile.data->hasData(layer_desc)) {
gl::group group(util::sprintf<32>("render %d/%d/%d", tile.id.z, tile.id.y, tile.id.z));
gl::group group(util::sprintf<32>("render %d/%d/%d\n", tile.id.z, tile.id.y, tile.id.z));
prepareTile(tile);
tile.data->render(*this, layer_desc);
frameHistory.record(map.getAnimationTime(), map.getState().getNormalizedZoom());
@@ -183,14 +181,14 @@ void Painter::renderTileLayer(const Tile& tile, const LayerDescription &layer_de
}
const mat4 &Painter::translatedMatrix(const std::array<float, 2> &translation, const Tile::ID &id, TranslateAnchor anchor) {
const mat4 &Painter::translatedMatrix(const std::array<float, 2> &translation, const Tile::ID &id, TranslateAnchorType anchor) {
if (translation[0] == 0 && translation[1] == 0) {
return matrix;
} else {
// TODO: Get rid of the 8 (scaling from 4096 to tile size)
const double factor = ((double)(1 << id.z)) / map.getState().getScale() * (4096.0 / util::tileSize);
if (anchor == TranslateAnchor::Viewport) {
if (anchor == TranslateAnchorType::Viewport) {
const double sin_a = std::sin(-map.getState().getAngle());
const double cos_a = std::cos(-map.getState().getAngle());
matrix::translate(vtxMatrix, matrix,

View File

@@ -6,34 +6,24 @@
using namespace llmr;
void Painter::drawClippingMasks(const Sources &sources) {
void Painter::drawClippingMasks(const std::set<std::shared_ptr<Source>> &sources) {
gl::group group("clipping masks");
useProgram(plainShader->program);
glDisable(GL_DEPTH_TEST);
depthMask(false);
glColorMask(false, false, false, false);
glDepthRange(1.0f, 1.0f);
glStencilMask(0xFF);
const BackgroundProperties &properties = map.getStyle()->computed.background;
Color background = properties.color;
const float opacity = properties.opacity;
background[0] *= opacity;
background[1] *= opacity;
background[2] *= opacity;
background[3] *= opacity;
plainShader->setColor(background);
coveringPlainArray.bind(*plainShader, tileStencilBuffer, BUFFER_OFFSET(0));
for (const auto &pair : sources) {
Source &source = *pair.second;
if (source.enabled) {
source.drawClippingMasks();
}
for (const std::shared_ptr<Source> &source : sources) {
source->drawClippingMasks(*this);
}
glEnable(GL_DEPTH_TEST);
glColorMask(true, true, true, true);
depthMask(true);
glStencilMask(0x0);
}

View File

@@ -1,4 +1,5 @@
#include <llmr/renderer/painter.hpp>
#include <llmr/style/style_properties.hpp>
#include <iostream>

View File

@@ -63,6 +63,10 @@ void Painter::renderDebugFrame() {
}
void Painter::renderDebugText(const std::vector<std::string> &strings) {
if (strings.empty()) {
return;
}
gl::group group("debug text");
glDisable(GL_DEPTH_TEST);
@@ -78,20 +82,21 @@ void Painter::renderDebugText(const std::vector<std::string> &strings) {
line += 20;
}
// draw debug info
VertexArrayObject debugFontArray;
debugFontArray.bind(*plainShader, debugFontBuffer, BUFFER_OFFSET(0));
plainShader->setColor(1.0f, 1.0f, 1.0f, 1.0f);
lineWidth(4.0f * map.getState().getPixelRatio());
glDrawArrays(GL_LINES, 0, (GLsizei)debugFontBuffer.index());
#ifndef GL_ES_VERSION_2_0
glPointSize(2);
glDrawArrays(GL_POINTS, 0, (GLsizei)debugFontBuffer.index());
#endif
plainShader->setColor(0.0f, 0.0f, 0.0f, 1.0f);
lineWidth(2.0f * map.getState().getPixelRatio());
glDrawArrays(GL_LINES, 0, (GLsizei)debugFontBuffer.index());
if (!debugFontBuffer.empty()) {
// draw debug info
VertexArrayObject debugFontArray;
debugFontArray.bind(*plainShader, debugFontBuffer, BUFFER_OFFSET(0));
plainShader->setColor(1.0f, 1.0f, 1.0f, 1.0f);
lineWidth(4.0f * map.getState().getPixelRatio());
glDrawArrays(GL_LINES, 0, (GLsizei)debugFontBuffer.index());
#ifndef GL_ES_VERSION_2_0
glPointSize(2);
glDrawArrays(GL_POINTS, 0, (GLsizei)debugFontBuffer.index());
#endif
plainShader->setColor(0.0f, 0.0f, 0.0f, 1.0f);
lineWidth(2.0f * map.getState().getPixelRatio());
glDrawArrays(GL_LINES, 0, (GLsizei)debugFontBuffer.index());
}
glEnable(GL_DEPTH_TEST);
}

View File

@@ -1,5 +1,6 @@
#include <llmr/renderer/painter.hpp>
#include <llmr/renderer/fill_bucket.hpp>
#include <llmr/style/style_layer.hpp>
#include <llmr/map/map.hpp>
#include <llmr/style/sprite.hpp>
#include <llmr/geometry/sprite_atlas.hpp>
@@ -17,10 +18,14 @@ void Painter::renderFill(FillBucket& bucket, const FillProperties& properties, c
fill_color[3] *= properties.opacity;
Color stroke_color = properties.stroke_color;
stroke_color[0] *= properties.opacity;
stroke_color[1] *= properties.opacity;
stroke_color[2] *= properties.opacity;
stroke_color[3] *= properties.opacity;
if (stroke_color[3] < 0) {
stroke_color = fill_color;
} else {
stroke_color[0] *= properties.opacity;
stroke_color[1] *= properties.opacity;
stroke_color[2] *= properties.opacity;
stroke_color[3] *= properties.opacity;
}
bool outline = properties.antialias && properties.stroke_color != properties.fill_color;
bool fringeline = properties.antialias && properties.stroke_color == properties.fill_color;
@@ -140,26 +145,21 @@ void Painter::renderFill(FillBucket& bucket, const FillProperties& properties, c
}
}
void Painter::renderFill(FillBucket& bucket, const std::string& layer_name, const Tile::ID& id) {
void Painter::renderFill(FillBucket& bucket, std::shared_ptr<StyleLayer> layer_desc, const Tile::ID& id) {
// Abort early.
if (!bucket.hasData()) return;
const std::unordered_map<std::string, FillProperties> &fill_properties = map.getStyle()->computed.fills;
const std::unordered_map<std::string, FillProperties>::const_iterator fill_properties_it = fill_properties.find(layer_name);
const FillProperties &properties = fill_properties_it != fill_properties.end()
? fill_properties_it->second
: defaultFillProperties;
const FillProperties &properties = layer_desc->getProperties<FillProperties>();
if (!properties.enabled) return;
if (properties.prerender && properties.getPrerender(id.z)) {
if (layer_desc->rasterize && layer_desc->rasterize->isEnabled(id.z)) {
if (pass == Translucent) {
const RasterizedProperties rasterize = layer_desc->rasterize->get(id.z);
// Buffer value around the 0..4096 extent that will be drawn into the 256x256 pixel
// texture. We later scale the texture so that the actual bounds will align with this
// tile's bounds. The reason we do this is so that the
if (!bucket.prerendered) {
const PrerenderProperties prerender = properties.getPrerenderProperties(id.z);
bucket.prerendered = std::make_unique<PrerenderedTexture>(prerender);
bucket.prerendered = std::make_unique<PrerenderedTexture>(rasterize);
bucket.prerendered->bindFramebuffer();
preparePrerender(*bucket.prerendered);
@@ -172,7 +172,7 @@ void Painter::renderFill(FillBucket& bucket, const std::string& layer_name, cons
// When drawing the fill, we want to draw a buffer around too, so we
// essentially downscale everyting, and then upscale it later when rendering.
const int buffer = prerender.buffer * 4096.0f;
const int buffer = rasterize.buffer * 4096.0f;
const mat4 vtxMatrix = [&]{
mat4 vtxMatrix;
matrix::ortho(vtxMatrix, -buffer, 4096 + buffer, -4096 - buffer, buffer, 0, 1);
@@ -186,8 +186,8 @@ void Painter::renderFill(FillBucket& bucket, const std::string& layer_name, cons
setTranslucent();
renderFill(bucket, modifiedProperties, id, vtxMatrix);
if (prerender.blur > 0) {
bucket.prerendered->blur(*this, prerender.blur);
if (rasterize.blur > 0) {
bucket.prerendered->blur(*this, rasterize.blur);
}
// RESET STATE

View File

@@ -116,7 +116,7 @@ void Painter::pushFramebuffer() {
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
glColorMask(false, false, false, false);
drawClippingMasks(map.getSources());
drawClippingMasks(map.getStyle()->getActiveSources());
glColorMask(true, true, true, true);
fbo_depth_stencil_valid = true;

View File

@@ -2,36 +2,29 @@
#include <llmr/renderer/icon_bucket.hpp>
#include <llmr/map/map.hpp>
#include <llmr/style/sprite.hpp>
#include <llmr/style/style_layer.hpp>
#include <llmr/geometry/sprite_atlas.hpp>
#include <llmr/util/math.hpp>
using namespace llmr;
void Painter::renderIcon(IconBucket& bucket, const std::string& layer_name, const Tile::ID& id) {
void Painter::renderIcon(IconBucket& bucket, std::shared_ptr<StyleLayer> layer_desc, const Tile::ID& id) {
// Abort early.
if (!bucket.hasData()) return;
if (pass == Opaque) return;
const std::unordered_map<std::string, IconProperties> &icon_properties = map.getStyle()->computed.icons;
const std::unordered_map<std::string, IconProperties>::const_iterator icon_properties_it = icon_properties.find(layer_name);
const IconProperties &properties = icon_properties_it != icon_properties.end()
? icon_properties_it->second
: defaultIconProperties;
const IconProperties &properties = layer_desc->getProperties<IconProperties>();
if (!properties.enabled) return;
Color color = properties.color;
color[0] *= properties.opacity;
color[1] *= properties.opacity;
color[2] *= properties.opacity;
color[3] *= properties.opacity;
const mat4 &vtxMatrix = translatedMatrix(properties.translate, id, properties.translateAnchor);
// TODO: when translating icon, are we doing this in the bucket already?
// const mat4 &vtxMatrix = translatedMatrix(properties.translate, id, properties.translateAnchor);
SpriteAtlas &spriteAtlas = *map.getSpriteAtlas();
useProgram(iconShader->program);
iconShader->setMatrix(vtxMatrix);
iconShader->setColor(color);
iconShader->setMatrix(matrix);
// TODO: update
// iconShader->setColor(color);
iconShader->setImage(0);
iconShader->setRatio(map.getState().getPixelRatio());
iconShader->setDimension({{

View File

@@ -1,20 +1,16 @@
#include <llmr/renderer/painter.hpp>
#include <llmr/renderer/line_bucket.hpp>
#include <llmr/style/style_layer.hpp>
#include <llmr/map/map.hpp>
using namespace llmr;
void Painter::renderLine(LineBucket& bucket, const std::string& layer_name, const Tile::ID& id) {
void Painter::renderLine(LineBucket& bucket, std::shared_ptr<StyleLayer> layer_desc, const Tile::ID& id) {
// Abort early.
if (pass == Opaque) return;
if (!bucket.hasData()) return;
const std::unordered_map<std::string, LineProperties> &line_properties = map.getStyle()->computed.lines;
const std::unordered_map<std::string, LineProperties>::const_iterator line_properties_it = line_properties.find(layer_name);
const LineProperties &properties = line_properties_it != line_properties.end()
? line_properties_it->second
: defaultLineProperties;
const LineProperties &properties = layer_desc->getProperties<LineProperties>();
if (!properties.enabled) return;
float width = properties.width;

View File

@@ -1,4 +1,5 @@
#include <llmr/renderer/painter.hpp>
#include <llmr/style/style_properties.hpp>
using namespace llmr;
@@ -23,7 +24,8 @@ void Painter::finishPrerender(PrerenderedTexture &texture) {
glViewport(0, 0, gl_viewport[0], gl_viewport[1]);
}
void Painter::renderPrerenderedTexture(PrerenderedTexture &texture, const GenericProperties &properties) {
template <typename Properties>
void Painter::renderPrerenderedTexture(PrerenderedTexture &texture, const Properties &properties) {
const int buffer = texture.properties.buffer * 4096.0f;
// draw the texture on a quad
@@ -41,3 +43,5 @@ void Painter::renderPrerenderedTexture(PrerenderedTexture &texture, const Generi
coveringRasterArray.bind(*rasterShader, tileStencilBuffer, BUFFER_OFFSET(0));
glDrawArrays(GL_TRIANGLES, 0, (GLsizei)tileStencilBuffer.index());
}
template void Painter::renderPrerenderedTexture(PrerenderedTexture &, const FillProperties &);

View File

@@ -1,18 +1,14 @@
#include <llmr/renderer/painter.hpp>
#include <llmr/renderer/raster_bucket.hpp>
#include <llmr/style/style_layer.hpp>
#include <llmr/map/map.hpp>
using namespace llmr;
void Painter::renderRaster(RasterBucket& bucket, const std::string& layer_name, const Tile::ID& /*id*/) {
void Painter::renderRaster(RasterBucket& bucket, std::shared_ptr<StyleLayer> layer_desc, const Tile::ID& /*id*/) {
if (pass == Translucent) return;
const std::unordered_map<std::string, RasterProperties> &raster_properties = map.getStyle()->computed.rasters;
const std::unordered_map<std::string, RasterProperties>::const_iterator raster_properties_it = raster_properties.find(layer_name);
const RasterProperties &properties = raster_properties_it != raster_properties.end()
? raster_properties_it->second
: defaultRasterProperties;
const RasterProperties &properties = layer_desc->getProperties<RasterProperties>();
if (!properties.enabled) return;
depthMask(false);

View File

@@ -1,21 +1,17 @@
#include <llmr/renderer/painter.hpp>
#include <llmr/renderer/text_bucket.hpp>
#include <llmr/style/style_layer.hpp>
#include <llmr/map/map.hpp>
#include <cmath>
using namespace llmr;
void Painter::renderText(TextBucket& bucket, const std::string& layer_name, const Tile::ID& id) {
void Painter::renderText(TextBucket& bucket, std::shared_ptr<StyleLayer> layer_desc, const Tile::ID& id) {
// Abort early.
if (pass == Opaque) return;
if (!bucket.hasData()) return;
const std::unordered_map<std::string, TextProperties> &text_properties = map.getStyle()->computed.texts;
const std::unordered_map<std::string, TextProperties>::const_iterator text_properties_it = text_properties.find(layer_name);
const TextProperties &properties = text_properties_it != text_properties.end()
? text_properties_it->second
: defaultTextProperties;
const TextProperties &properties = layer_desc->getProperties<TextProperties>();
if (!properties.enabled) return;
mat4 exMatrix;
@@ -24,19 +20,16 @@ void Painter::renderText(TextBucket& bucket, const std::string& layer_name, cons
matrix::rotate_z(exMatrix, exMatrix, map.getState().getAngle());
}
const float rotate = properties.rotate;
if (rotate != 0.0f) {
matrix::rotate_z(exMatrix, exMatrix, rotate);
}
// If layerStyle.size > bucket.info.fontSize then labels may collide
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);
// TODO: figure out whether we actually need to account for this while painting; we might already have
// done this during label placement.
// const mat4 &vtxMatrix = translatedMatrix(properties.translate, id, properties.translateAnchor);
useProgram(textShader->program);
textShader->setMatrix(vtxMatrix);
textShader->setMatrix(matrix);
textShader->setExtrudeMatrix(exMatrix);
GlyphAtlas &glyphAtlas = *map.getGlyphAtlas();
@@ -45,7 +38,7 @@ void Painter::renderText(TextBucket& bucket, const std::string& layer_name, cons
static_cast<float>(glyphAtlas.height)}});
// Convert the -pi..pi to an int8 range.
float angle = std::round((map.getState().getAngle() + rotate) / M_PI * 128);
float angle = std::round((map.getState().getAngle()) / M_PI * 128);
// adjust min/max zooms for variable font sies
float zoomAdjust = log(fontSize / bucket.properties.max_size) / log(2);
@@ -55,17 +48,17 @@ void Painter::renderText(TextBucket& bucket, const std::string& layer_name, cons
textShader->setZoom((map.getState().getNormalizedZoom() - zoomAdjust) * 10); // current zoom level
// Label fading
const time duration = 300_milliseconds;
const time currentTime = util::now();
const timestamp duration = 300_milliseconds;
const timestamp currentTime = util::now();
std::deque<FrameSnapshot> &history = frameHistory.history;
// Remove frames until only one is outside the duration, or until there are only three
while (history.size() > 3 && history[1].timestamp + duration < currentTime) {
while (history.size() > 3 && history[1].t + duration < currentTime) {
history.pop_front();
}
if (history[1].timestamp + duration < currentTime) {
if (history[1].t + duration < currentTime) {
history[0].z = history[1].z;
}
@@ -80,7 +73,7 @@ void Painter::renderText(TextBucket& bucket, const std::string& layer_name, cons
// Calculate the speed of zooming, and how far it would zoom in terms of zoom levels in one duration
float zoomDiff = endingZ - history[1].z,
timeDiff = lastFrame.timestamp - history[1].timestamp;
timeDiff = lastFrame.t - history[1].t;
float fadedist = zoomDiff / (timeDiff / duration);
#if defined(DEBUG)
@@ -89,7 +82,7 @@ void Painter::renderText(TextBucket& bucket, const std::string& layer_name, cons
// At end of a zoom when the zoom stops changing continue pretending to zoom at that speed
// bump is how much farther it would have been if it had continued zooming at the same rate
float bump = (currentTime - lastFrame.timestamp) / duration * fadedist;
float bump = (currentTime - lastFrame.t) / duration * fadedist;
textShader->setFadeDist(fadedist * 10);
textShader->setMinFadeZoom(std::floor(lowZ * 10));
@@ -98,12 +91,21 @@ void Painter::renderText(TextBucket& bucket, const std::string& layer_name, cons
// We're drawing in the translucent pass which is bottom-to-top, so we need
// to draw the halo first.
if (properties.halo[3] > 0.0f) {
if (properties.halo_color[3] > 0.0f) {
// TODO: Get rid of the 2.4 magic value. It is currently 24 / 10, with 24 being the font size
// of the SDF glyphs.
textShader->setGamma(properties.halo_blur * 2.4f / fontSize / map.getState().getPixelRatio());
textShader->setColor(properties.halo);
textShader->setBuffer(properties.halo_radius);
if (properties.opacity < 1.0f) {
Color color = properties.halo_color;
color[0] *= properties.opacity;
color[1] *= properties.opacity;
color[2] *= properties.opacity;
color[3] *= properties.opacity;
textShader->setColor(color);
} else {
textShader->setColor(properties.halo_color);
}
textShader->setBuffer(properties.halo_width);
glDepthRange(strata, 1.0f);
bucket.drawGlyphs(*textShader);
}
@@ -113,7 +115,16 @@ void Painter::renderText(TextBucket& bucket, const std::string& layer_name, cons
// TODO: Get rid of the 2.4 magic value. It is currently 24 / 10, with 24 being the font size
// of the SDF glyphs.
textShader->setGamma(2.4f / fontSize / map.getState().getPixelRatio());
textShader->setColor(properties.color);
if (properties.opacity < 1.0f) {
Color color = properties.color;
color[0] *= properties.opacity;
color[1] *= properties.opacity;
color[2] *= properties.opacity;
color[3] *= properties.opacity;
textShader->setColor(color);
} else {
textShader->setColor(properties.color);
}
textShader->setBuffer((256.0f - 64.0f) / 256.0f);
glDepthRange(strata + strata_epsilon, 1.0f);
bucket.drawGlyphs(*textShader);

View File

@@ -4,7 +4,7 @@
using namespace llmr;
PrerenderedTexture::PrerenderedTexture(const PrerenderProperties &properties)
PrerenderedTexture::PrerenderedTexture(const RasterizedProperties &properties)
: properties(properties) {
}

View File

@@ -7,8 +7,8 @@ RasterBucket::RasterBucket(const std::shared_ptr<Texturepool> &texturepool)
: raster(texturepool) {
}
void RasterBucket::render(Painter &painter, const std::string &layer_name, const Tile::ID &id) {
painter.renderRaster(*this, layer_name, id);
void RasterBucket::render(Painter &painter, std::shared_ptr<StyleLayer> layer_desc, const Tile::ID &id) {
painter.renderRaster(*this, layer_desc, id);
}
bool RasterBucket::setImage(const std::string &data) {

View File

@@ -22,7 +22,7 @@ using namespace llmr;
TextBucket::TextBucket(
TextVertexBuffer &vertexBuffer,
TriangleElementsBuffer &triangleElementsBuffer,
const BucketTextDescription &properties, Placement &placement)
const StyleBucketText &properties, Placement &placement)
: properties(properties),
vertexBuffer(vertexBuffer),
triangleElementsBuffer(triangleElementsBuffer),
@@ -124,9 +124,9 @@ void TextBucket::addFeature(const pbf &geom_pbf,
}
}
void TextBucket::render(Painter &painter, const std::string &layer_name,
void TextBucket::render(Painter &painter, std::shared_ptr<StyleLayer> layer_desc,
const Tile::ID &id) {
painter.renderText(*this, layer_name, id);
painter.renderText(*this, layer_desc, id);
}
bool TextBucket::hasData() const {

View File

@@ -0,0 +1,52 @@
#include <llmr/style/applied_class_properties.hpp>
namespace llmr {
AppliedClassProperty::AppliedClassProperty(ClassID class_id, timestamp begin, timestamp end, const PropertyValue &value)
: name(class_id),
begin(begin),
end(end),
value(value) {}
// Returns thie ID of the most recent
ClassID AppliedClassProperties::mostRecent() const {
return properties.size() ? properties.back().name : ClassID::Fallback;
}
void AppliedClassProperties::add(ClassID class_id, timestamp begin, timestamp end, const PropertyValue &value) {
properties.emplace_back(class_id, begin, end, value);
}
bool AppliedClassProperties::hasTransitions() const {
return properties.size() > 1;
}
// Erase all items in the property list that are before a completed transition.
// Then, if the only remaining property is a Fallback value, remove it too.
void AppliedClassProperties::cleanup(timestamp now) {
// Iterate backwards, but without using the rbegin/rend interface since we need forward
// iterators to use .erase().
for (auto it = properties.end(), begin = properties.begin(); it != begin;) {
// If the property is finished, break iteration and delete all remaining items.
if ((--it)->end <= now) {
// Removes all items that precede the current iterator, but *not* the element currently
// pointed to by the iterator. This preserves the last completed transition as the
// first element in the property list.
properties.erase(begin, it);
// Also erase the pivot element if it's a fallback value. This means we can remove the
// entire applied properties object as well, because we already have the fallback
// value set as the default.
if (it->name == ClassID::Fallback) {
properties.erase(it);
}
break;
}
}
}
bool AppliedClassProperties::empty() const {
return properties.empty();
}
}

View File

@@ -1,24 +0,0 @@
#include <llmr/style/bucket_description.hpp>
#include <iostream>
std::ostream& llmr::operator<<(std::ostream& os, const BucketDescription& bucket) {
os << "Bucket:" << std::endl;
os << " - type: " << (uint32_t)bucket.type << std::endl;
os << " - source_name: " << bucket.source_name << std::endl;
os << " - source_layer: " << bucket.source_layer << std::endl;
return os;
}
std::ostream& llmr::operator<<(std::ostream& os, BucketType type) {
switch (type) {
case BucketType::None: os << "None"; return os;
case BucketType::Fill: os << "Fill"; return os;
case BucketType::Line: os << "Line"; return os;
case BucketType::Icon: os << "Icon"; return os;
case BucketType::Text: os << "Text"; return os;
case BucketType::Raster: os << "Raster"; return os;
default: os << "<unknown>"; return os;
}
}

View File

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

View File

@@ -0,0 +1,29 @@
#include <llmr/style/class_dictionary.hpp>
namespace llmr {
ClassID ClassDictionary::Lookup(const std::string &class_name) {
auto it = store.find(class_name);
if (it == store.end()) {
// Insert the class name into the store.
ClassID id = ClassID(uint32_t(ClassID::Named) + offset++);
store.emplace(class_name, id);
return id;
} else {
return it->second;
}
}
ClassID ClassDictionary::Normalize(ClassID id) {
if (id >= ClassID::Named) {
return ClassID::Named;
} else {
return id;
}
}
std::unordered_map<std::string, ClassID> ClassDictionary::store = { { "", ClassID::Default } };
uint32_t ClassDictionary::offset = 0;
}

View File

@@ -0,0 +1,14 @@
#include <llmr/style/class_properties.hpp>
namespace llmr {
const PropertyTransition &ClassProperties::getTransition(PropertyKey key, const PropertyTransition &defaultTransition) const {
auto it = transitions.find(key);
if (it == transitions.end()) {
return defaultTransition;
} else {
return it->second;
}
}
}

View File

@@ -0,0 +1,284 @@
#include <llmr/style/filter_expression.hpp>
#include <llmr/map/vector_tile.hpp>
namespace llmr {
template <typename Comparer>
inline bool FilterComparison::Instance::includes(const Value &property_value, const Comparer &comparer) const {
for (const Value &filter_value : values) {
if (comparer(property_value, filter_value)) {
return true;
}
}
return false;
}
template <typename Comparer>
inline bool FilterComparison::Instance::compare(const Value &property_value, const Comparer &comparer) const {
for (const Value &filter_value : values) {
if (!comparer(property_value, filter_value)) {
return false;
}
}
return true;
}
template <typename Comparer>
inline bool FilterComparison::Instance::all(const std::forward_list<Value> &property_values, const Comparer &comparer) const {
for (const Value &property_value : property_values) {
if (!compare(property_value, comparer)) {
return false;
}
}
return true;
}
bool FilterComparison::Instance::compare(const Value &property_value) const {
switch (op) {
case Operator::Equal:
case Operator::In:
return includes(property_value, util::relaxed_equal);
case Operator::NotEqual:
case Operator::NotIn:
return !includes(property_value, util::relaxed_equal);
case Operator::Greater:
return compare(property_value, util::relaxed_greater);
case Operator::GreaterEqual:
return compare(property_value, util::relaxed_greater_equal);
case Operator::Less:
return compare(property_value, util::relaxed_less);
case Operator::LessEqual:
return compare(property_value, util::relaxed_less_equal);
default:
return false;
}
}
bool FilterComparison::Instance::compare(const std::forward_list<Value> &property_values) const {
switch (op) {
case Operator::Equal:
for (const Value &property_value : property_values) {
if (!includes(property_value, util::relaxed_equal)) {
return false;
}
}
return true;
case Operator::NotEqual:
for (const Value &property_value : property_values) {
if (includes(property_value, util::relaxed_equal)) {
return false;
}
}
return true;
case Operator::In:
for (const Value &property_value : property_values) {
if (includes(property_value, util::relaxed_equal)) {
return true;
}
}
return false;
case Operator::NotIn:
for (const Value &property_value : property_values) {
if (!includes(property_value, util::relaxed_equal)) {
return true;
}
}
return false;
case Operator::Greater:
return all(property_values, util::relaxed_greater);
case Operator::GreaterEqual:
return all(property_values, util::relaxed_greater_equal);
case Operator::Less:
return all(property_values, util::relaxed_less);
case Operator::LessEqual:
return all(property_values, util::relaxed_less_equal);
default:
return false;
}
}
const std::string &FilterComparison::getField() const {
return field;
}
inline bool FilterComparison::compare(const VectorTileTagExtractor &extractor) const {
const std::forward_list<Value> values = extractor.getValues(field);
// All instances are ANDed together.
for (const Instance &instance : instances) {
if (!instance.compare(values)) {
return false;
}
}
return true;
}
std::ostream& operator <<(std::ostream &s, const FilterComparison &comparison) {
s << "comparison" << std::endl;
for (const FilterComparison::Instance &instance : comparison.instances) {
s << " - " << comparison.field << " " << instance << std::endl;
}
return s;
}
std::ostream& operator <<(std::ostream &s, const FilterComparison::Instance &instance) {
switch (instance.op) {
case FilterComparison::Operator::Equal: s << "=="; break;
case FilterComparison::Operator::NotEqual: s << "!="; break;
case FilterComparison::Operator::Greater: s << ">"; break;
case FilterComparison::Operator::GreaterEqual: s << ">="; break;
case FilterComparison::Operator::Less: s << "<"; break;
case FilterComparison::Operator::LessEqual: s << "<="; break;
case FilterComparison::Operator::In: s << "in"; break;
case FilterComparison::Operator::NotIn: s << "!in"; break;
}
s << " [ ";
for (const Value &value : instance.values) {
s << toString(value) << " ";
}
s << "]";
return s;
}
FilterComparison::Operator parseFilterComparisonOperator(const std::string &op) {
if (op == "==") return FilterComparison::Operator::Equal;
if (op == "!=") return FilterComparison::Operator::NotEqual;
if (op == ">") return FilterComparison::Operator::Greater;
if (op == ">=") return FilterComparison::Operator::GreaterEqual;
if (op == "<") return FilterComparison::Operator::Less;
if (op == "<=") return FilterComparison::Operator::LessEqual;
if (op == "in") return FilterComparison::Operator::In;
if (op == "!in") return FilterComparison::Operator::NotIn;
return FilterComparison::Operator::Equal;
}
std::ostream& operator <<(std::ostream &s, FilterExpression::Operator op) {
switch (op) {
case FilterExpression::Operator::And: s << "AND"; break;
case FilterExpression::Operator::Or: s << "OR"; break;
case FilterExpression::Operator::Xor: s << "XOR"; break;
case FilterExpression::Operator::Nor: s << "NOR"; break;
}
return s;
}
std::ostream& operator <<(std::ostream &s, FilterExpression::GeometryType type) {
switch (type) {
case FilterExpression::GeometryType::Point: s << "point"; break;
case FilterExpression::GeometryType::Line: s << "line"; break;
case FilterExpression::GeometryType::Polygon: s << "polygon"; break;
case FilterExpression::GeometryType::Any: s << "any"; break;
}
return s;
}
bool FilterExpression::compare(const VectorTileTagExtractor &extractor) const {
if (type != GeometryType::Any && extractor.getType() != type && extractor.getType() != GeometryType::Any) {
return false;
}
switch (op) {
case Operator::And:
for (const FilterComparison &comparison : comparisons) {
if (!comparison.compare(extractor)) {
return false;
}
}
for (const FilterExpression &expression: expressions) {
if (!expression.compare(extractor)) {
return false;
}
}
return true;
case Operator::Or:
for (const FilterComparison &comparison : comparisons) {
if (comparison.compare(extractor)) {
return true;
}
}
for (const FilterExpression &expression: expressions) {
if (expression.compare(extractor)) {
return true;
}
}
return false;
case Operator::Xor: {
int count = 0;
for (const FilterComparison &comparison : comparisons) {
count += comparison.compare(extractor);
if (count > 1) {
return false;
}
}
for (const FilterExpression &expression: expressions) {
count += expression.compare(extractor);
if (count > 1) {
return false;
}
}
return count == 1;
}
case Operator::Nor:
for (const FilterComparison &comparison : comparisons) {
if (comparison.compare(extractor)) {
return false;
}
}
for (const FilterExpression &expression: expressions) {
if (expression.compare(extractor)) {
return false;
}
}
return true;
default:
return true;
}
}
bool FilterExpression::empty() const {
return type == GeometryType::Any && comparisons.empty() && expressions.empty();
}
void FilterExpression::add(const FilterComparison &comparison) {
comparisons.emplace_front(comparison);
}
void FilterExpression::add(const FilterExpression &expression) {
expressions.emplace_front(expression);
}
void FilterExpression::setGeometryType(GeometryType g) {
type = g;
}
FilterExpression::GeometryType parseGeometryType(const std::string &geometry) {
if (geometry == "point") return FilterExpression::GeometryType::Point;
if (geometry == "line") return FilterExpression::GeometryType::Line;
if (geometry == "polygon") return FilterExpression::GeometryType::Polygon;
return FilterExpression::GeometryType::Any;
}
std::ostream& operator <<(std::ostream &s, const FilterExpression &expression) {
s << "expression " << expression.op << std::endl;
s << " - $type = " << expression.type << std::endl;
for (const FilterComparison &comparison : expression.comparisons) {
s << comparison;
}
s << "end expression" << std::endl;
return s;
}
}

View File

@@ -0,0 +1,131 @@
#include <llmr/style/function_properties.hpp>
#include <llmr/style/types.hpp>
#include <cmath>
namespace llmr {
template <>
bool LinearFunction<bool>::evaluate(float z) const {
return z < z_base ? slope >= 0 : z > z_base ? slope >= 0 : value;
}
template <>
float LinearFunction<float>::evaluate(float z) const {
return std::fmin(std::fmax(min, value + (z - z_base) * slope), max);
}
template <>
Color LinearFunction<Color>::evaluate(float z) const {
return {{
std::fmin(std::fmax(min[0], value[0] + (z - z_base) * slope), max[0]),
std::fmin(std::fmax(min[1], value[1] + (z - z_base) * slope), max[1]),
std::fmin(std::fmax(min[2], value[2] + (z - z_base) * slope), max[2]),
std::fmin(std::fmax(min[3], value[3] + (z - z_base) * slope), max[3])
}};
}
template <>
bool ExponentialFunction<bool>::evaluate(float z) const {
return z < z_base ? slope >= 0 : z > z_base ? slope >= 0 : value;
}
template <>
float ExponentialFunction<float>::evaluate(float z) const {
return std::fmin(std::fmax(min, value + std::pow(1.75, (z - z_base)) * slope), max);
}
template <>
Color ExponentialFunction<Color>::evaluate(float z) const {
return {{
std::fmin(std::fmax(min[0], value[0] + float(std::pow(1.75, (z - z_base))) * slope), max[0]),
std::fmin(std::fmax(min[1], value[1] + float(std::pow(1.75, (z - z_base))) * slope), max[1]),
std::fmin(std::fmax(min[2], value[2] + float(std::pow(1.75, (z - z_base))) * slope), max[2]),
std::fmin(std::fmax(min[3], value[3] + float(std::pow(1.75, (z - z_base))) * slope), max[3])
}};
}
template <typename T>
inline T exponentialInterpolate(T smaller, T larger, const float factor);
template <>
inline float exponentialInterpolate(const float smaller, const float larger, const float factor) {
// Linear interpolation if base is 0
if (smaller == 0.0f) {
return factor * larger;
}
// Exponential interpolation between the values
return smaller * std::pow(larger / smaller, factor);
}
template <>
inline bool exponentialInterpolate(const bool smaller, const bool larger, const float factor) {
return exponentialInterpolate(float(smaller), float(larger), factor);
}
template <>
inline Color exponentialInterpolate(const Color smaller, const Color larger, const float factor) {
return {{
exponentialInterpolate(smaller[0], larger[0], factor),
exponentialInterpolate(smaller[1], larger[1], factor),
exponentialInterpolate(smaller[2], larger[2], factor),
exponentialInterpolate(smaller[3], larger[3], factor)
}};
}
template <typename T>
T exponentialDefault();
template <> bool exponentialDefault() { return true; }
template <> float exponentialDefault() { return 1.0f; }
template <> Color exponentialDefault() { return {{ 0, 0, 0, 1 }}; }
template <typename T>
T StopsFunction<T>::evaluate(float z) const {
bool smaller = false;
float smaller_z = 0.0f;
T smaller_val {};
bool larger = false;
float larger_z = 0.0f;
T larger_val {};
for (uint32_t i = 0; i < values.size(); i++) {
float stop_z = values[i].first;
T stop_val = values[i].second;
if (stop_z <= z && (!smaller || smaller_z < stop_z)) {
smaller = true;
smaller_z = stop_z;
smaller_val = stop_val;
}
if (stop_z >= z && (!larger || larger_z > stop_z)) {
larger = true;
larger_z = stop_z;
larger_val = stop_val;
}
}
if (smaller && larger) {
if (larger_z == smaller_z || larger_val == smaller_val) {
return smaller_val;
}
float factor = (z - smaller_z) / (larger_z - smaller_z);
return exponentialInterpolate<T>(smaller_val, larger_val, factor);
} else if (larger) {
return larger_val;
} else if (smaller) {
return smaller_val;
} else {
// No stop defined.
return exponentialDefault<T>();
}
}
template bool StopsFunction<bool>::evaluate(float z) const;
template float StopsFunction<float>::evaluate(float z) const;
template Color StopsFunction<Color>::evaluate(float z) const;
}

View File

@@ -1,22 +0,0 @@
#include <llmr/style/layer_description.hpp>
#include <iostream>
using namespace llmr;
size_t LayerDescription::size() const {
size_t count = 1;
for (const LayerDescription &layer : child_layer) {
count += layer.size();
}
return count;
}
std::ostream& llmr::operator<<(std::ostream& os, const LayerDescription& layer) {
os << "Structure: " << layer.name << std::endl;
os << " - bucket_name: " << layer.bucket_name << std::endl;
for (const LayerDescription& value : layer.child_layer) {
os << " - child_layer: " << value << std::endl;
}
return os;
}

View File

@@ -1,88 +0,0 @@
#include <llmr/style/properties.hpp>
#include <llmr/util/constants.hpp>
#include <cassert>
#include <cmath>
using namespace llmr;
float functions::null(float, const std::vector<float>&) {
return 0;
}
float functions::constant(float, const std::vector<float>& values) {
return values.size() >= 1 ? values.front() : 0.0f;
}
float functions::min(float z, const std::vector<float>& values) {
return values.size() >= 1 && z >= values.front();
}
float functions::max(float z, const std::vector<float>& values) {
return values.size() >= 1 && z <= values.front();
}
float functions::stops(float z, const std::vector<float>& stops) {
// We need an even number of stop values.
if (stops.size() % 2 != 0) return 0;
bool smaller = false;
float smaller_z = 0.0;
float smaller_val = 0.0;
bool larger = false;
float larger_z = 0.0;
float larger_val = 0.0;
for (uint32_t i = 0; i < stops.size(); i += 2) {
float stop_z = stops[i];
float stop_val = stops[i + 1];
if (stop_z <= z && (!smaller || smaller_z < stop_z)) { smaller = true; smaller_z = stop_z; smaller_val = stop_val; }
if (stop_z >= z && (!larger || larger_z > stop_z)) { larger = true; larger_z = stop_z; larger_val = stop_val; }
}
if (smaller && larger) {
if (larger_z == smaller_z || larger_val == smaller_val) return smaller_val;
float factor = (z - smaller_z) / (larger_z - smaller_z);
// Linear interpolation if base is 0
if (smaller_val == 0) return factor * larger_val;
// Exponential interpolation between the values
return smaller_val * std::pow(larger_val / smaller_val, factor);
} else if (larger) {
return larger_val;
} else if (smaller) {
return smaller_val;
} else {
// No stop defined.
return 1;
}
}
float functions::linear(float z, const std::vector<float>& values) {
if (values.size() != 5) {
return 0;
}
const float z_base = values[0];
const float val = values[1];
const float slope = values[2];
const float min = values[3];
const float max = values[4];
return std::fmin(std::fmax(min, val + (z - z_base) * slope), max);
}
float functions::exponential(float z, const std::vector<float>& values) {
if (values.size() != 5) {
return 0;
}
const float z_base = values[0];
const float val = values[1];
const float slope = values[2];
const float min = values[3];
const float max = values[4];
return std::fmin(std::fmax(min, val + std::pow(1.75, (z - z_base)) * slope), max);
}

View File

@@ -0,0 +1,56 @@
#include <llmr/style/property_fallback.hpp>
namespace llmr {
const std::map<PropertyKey, PropertyValue> PropertyFallbackValue::properties = {
{ PropertyKey::FillEnabled, true },
{ PropertyKey::FillAntialias, true },
{ PropertyKey::FillOpacity, 1.0f },
{ PropertyKey::FillColor, Color({{ 0, 0, 0, 1 }}) },
{ PropertyKey::FillTranslateX, 0.0f },
{ PropertyKey::FillTranslateY, 0.0f },
{ PropertyKey::FillTranslateAnchor, TranslateAnchorType::Map },
{ PropertyKey::LineEnabled, true },
{ PropertyKey::LineOpacity, 1.0f },
{ PropertyKey::LineColor, Color({{ 0, 0, 0, 1 }}) },
{ PropertyKey::LineTranslateX, 0.0f },
{ PropertyKey::LineTranslateY, 0.0f },
{ PropertyKey::LineTranslateAnchor, TranslateAnchorType::Map },
{ PropertyKey::LineWidth, 1.0f },
{ PropertyKey::LineOffset, 0.0f },
{ PropertyKey::LineBlur, 1.0f },
{ PropertyKey::LineDashLand, 1.0f },
{ PropertyKey::LineDashGap, -1.0f },
{ PropertyKey::IconEnabled, true },
{ PropertyKey::IconOpacity, 1.0f },
{ PropertyKey::IconRotate, 0.0f },
{ PropertyKey::IconRotateAnchor, RotateAnchorType::Viewport },
{ PropertyKey::TextEnabled, true },
{ PropertyKey::TextOpacity, 1.0f },
{ PropertyKey::TextSize, 16.0f },
{ PropertyKey::TextColor, Color({{ 0, 0, 0, 1 }}) },
{ PropertyKey::TextHaloColor, Color({{ 0, 0, 0, 0 }}) },
{ PropertyKey::TextHaloWidth, 0.25f },
{ PropertyKey::TextHaloBlur, 1.0f },
{ PropertyKey::CompositeEnabled, true },
{ PropertyKey::CompositeOpacity, 1.0f },
{ PropertyKey::RasterEnabled, true },
{ PropertyKey::RasterOpacity, 1.0f },
{ PropertyKey::RasterSpin, 0.0f },
{ PropertyKey::RasterBrightnessLow, 0.0f },
{ PropertyKey::RasterBrightnessHigh, 1.0f },
{ PropertyKey::RasterSaturation, 0.0f },
{ PropertyKey::RasterContrast, 0.0f },
{ PropertyKey::RasterFade, 0.0f },
{ PropertyKey::BackgroundColor, Color({{ 0, 0, 0, 0 }}) },
};
const PropertyValue PropertyFallbackValue::defaultProperty = false;
}

View File

@@ -83,6 +83,8 @@ void Sprite::parseJSON(std::shared_ptr<Sprite> &sprite) {
sprite->body.clear();
if (d.IsObject()) {
std::unordered_map<std::string, SpritePosition> pos;
for (rapidjson::Value::ConstMemberIterator itr = d.MemberBegin(); itr != d.MemberEnd(); ++itr) {
const std::string& name = itr->name.GetString();
const rapidjson::Value& value = itr->value;
@@ -99,9 +101,11 @@ void Sprite::parseJSON(std::shared_ptr<Sprite> &sprite) {
if (value.HasMember("width")) width = value["width"].GetInt();
if (value.HasMember("height")) height = value["height"].GetInt();
if (value.HasMember("pixelRatio")) pixelRatio = value["pixelRatio"].GetInt();
sprite->pos.emplace(name, SpritePosition { x, y, width, height, pixelRatio });
pos.emplace(name, SpritePosition { x, y, width, height, pixelRatio });
}
}
sprite->pos.swap(pos);
}
}

File diff suppressed because it is too large Load Diff

302
src/style/style_layer.cpp Normal file
View File

@@ -0,0 +1,302 @@
#include <llmr/style/style_layer.hpp>
#include <llmr/style/style_bucket.hpp>
#include <llmr/map/source.hpp>
#include <llmr/style/style_layer_group.hpp>
#include <llmr/style/property_fallback.hpp>
namespace llmr {
StyleLayer::StyleLayer(const std::string &id, std::map<ClassID, ClassProperties> &&styles,
std::unique_ptr<const RasterizeProperties> &&rasterize)
: id(id), styles(std::move(styles)), rasterize(std::move(rasterize)) {}
bool StyleLayer::isBackground() const {
return id == "background";
}
void StyleLayer::setClasses(const std::vector<std::string> &class_names, const timestamp now,
const PropertyTransition &defaultTransition) {
// Stores all keys that we have already added transitions for.
std::set<PropertyKey> already_applied;
// Reverse iterate through all class names and apply them last to first.
for (auto it = class_names.rbegin(); it != class_names.rend(); it++) {
const std::string &class_name = *it;
// From here on, we're only dealing with IDs to avoid comparing strings all the time.
const ClassID class_id = ClassDictionary::Lookup(class_name);
applyClassProperties(class_id, already_applied, now, defaultTransition);
}
// As the last class, apply the default class.
applyClassProperties(ClassID::Default, already_applied, now, defaultTransition);
// Make sure that we also transition to the fallback value for keys that aren't changed by
// any applied classes.
for (std::pair<const PropertyKey, AppliedClassProperties> &property_pair : appliedStyle) {
const PropertyKey key = property_pair.first;
if (already_applied.find(key) != already_applied.end()) {
// This property has already been set by a previous class, so we don't need to
// transition to the fallback.
continue;
}
AppliedClassProperties &appliedProperties = property_pair.second;
// Make sure that we don't do double transitions to the fallback value.
if (appliedProperties.mostRecent() != ClassID::Fallback) {
// This property key hasn't been set by a previous class, so we need to add a transition
// to the fallback value for that key.
const timestamp begin = now + defaultTransition.delay * 1_millisecond;
const timestamp end = begin + defaultTransition.duration * 1_millisecond;
const PropertyValue &value = PropertyFallbackValue::Get(key);
appliedProperties.add(ClassID::Fallback, begin, end, value);
}
}
// Update all child layers as well.
if (layers) {
layers->setClasses(class_names, now, defaultTransition);
}
}
// Helper function for applying all properties of a a single class that haven't been applied yet.
void StyleLayer::applyClassProperties(const ClassID class_id,
std::set<PropertyKey> &already_applied, timestamp now,
const PropertyTransition &defaultTransition) {
auto style_it = styles.find(class_id);
if (style_it == styles.end()) {
// There is no class in this layer with this class_name.
return;
}
// Loop through all the properties in this style, and add transitions to them, if they're
// not already the most recent transition.
const ClassProperties &properties = style_it->second;
for (const std::pair<PropertyKey, PropertyValue> &property_pair : properties) {
PropertyKey key = property_pair.first;
if (already_applied.find(key) != already_applied.end()) {
// This property has already been set by a previous class.
continue;
}
// Mark this property as written by a previous class, so that subsequent
// classes won't override this.
already_applied.insert(key);
// If the most recent transition is not the one with the highest priority, create
// a transition.
AppliedClassProperties &appliedProperties = appliedStyle[key];
if (appliedProperties.mostRecent() != class_id) {
const PropertyTransition &transition =
properties.getTransition(key, defaultTransition);
const timestamp begin = now + transition.delay * 1_millisecond;
const timestamp end = begin + transition.duration * 1_millisecond;
const PropertyValue &value = property_pair.second;
appliedProperties.add(class_id, begin, end, value);
}
}
}
template <typename T>
struct PropertyEvaluator {
typedef T result_type;
PropertyEvaluator(float z) : z(z) {}
template <typename P, typename std::enable_if<std::is_convertible<P, T>::value, int>::type = 0>
T operator()(const P &value) const {
return value;
}
T operator()(const Function<T> &value) const {
return util::apply_visitor(FunctionEvaluator<T>(z), value);
}
template <typename P, typename std::enable_if<!std::is_convertible<P, T>::value, int>::type = 0>
T operator()(const P &) const {
return T();
}
private:
const float z;
};
inline float interpolate(const float a, const float b, const float t) {
return (1.0f - t) * a + t * b;
}
inline Color interpolate(const Color &a, const Color &b, const float t) {
const float rt = 1.0f - t;
return Color {{
rt * a[0] + t * b[0],
rt * a[1] + t * b[1],
rt * a[2] + t * b[2],
rt * a[3] + t * b[3]
}};
}
template <typename T>
inline T interpolate(const T a, const T b, const float t) {
return t >= 0.5 ? b : a;
}
template <typename T>
void StyleLayer::applyStyleProperty(PropertyKey key, T &target, const float z, const timestamp now) {
auto it = appliedStyle.find(key);
if (it != appliedStyle.end()) {
AppliedClassProperties &applied = it->second;
// Iterate through all properties that we need to apply in order.
const PropertyEvaluator<T> evaluator(z);
for (AppliedClassProperty &property : applied.properties) {
if (now >= property.end) {
// We overwrite the current property with the new value.
target = util::apply_visitor(evaluator, property.value);
} else if (now >= property.begin) {
// We overwrite the current property partially with the new value.
float progress = float(now - property.begin) / float(property.end - property.begin);
target = interpolate(target, util::apply_visitor(evaluator, property.value), progress);
} else {
// Do not apply this property because its transition hasn't begun yet.
}
}
}
}
template <>
void StyleLayer::applyStyleProperties<FillProperties>(const float z, const timestamp now) {
properties.set<FillProperties>();
FillProperties &fill = properties.get<FillProperties>();
applyStyleProperty(PropertyKey::FillEnabled, fill.enabled, z, now);
applyStyleProperty(PropertyKey::FillAntialias, fill.antialias, z, now);
applyStyleProperty(PropertyKey::FillOpacity, fill.opacity, z, now);
applyStyleProperty(PropertyKey::FillColor, fill.fill_color, z, now);
applyStyleProperty(PropertyKey::FillOutlineColor, fill.stroke_color, z, now);
applyStyleProperty(PropertyKey::FillTranslateX, fill.translate[0], z, now);
applyStyleProperty(PropertyKey::FillTranslateY, fill.translate[1], z, now);
applyStyleProperty(PropertyKey::FillTranslateAnchor, fill.translateAnchor, z, now);
applyStyleProperty(PropertyKey::FillImage, fill.image, z, now);
}
template <>
void StyleLayer::applyStyleProperties<LineProperties>(const float z, const timestamp now) {
properties.set<LineProperties>();
LineProperties &line = properties.get<LineProperties>();
applyStyleProperty(PropertyKey::LineEnabled, line.enabled, z, now);
applyStyleProperty(PropertyKey::LineOpacity, line.opacity, z, now);
applyStyleProperty(PropertyKey::LineColor, line.color, z, now);
applyStyleProperty(PropertyKey::LineTranslateX, line.translate[0], z, now);
applyStyleProperty(PropertyKey::LineTranslateY, line.translate[1], z, now);
applyStyleProperty(PropertyKey::LineTranslateAnchor, line.translateAnchor, z, now);
applyStyleProperty(PropertyKey::LineWidth, line.width, z, now);
applyStyleProperty(PropertyKey::LineOffset, line.offset, z, now);
applyStyleProperty(PropertyKey::LineBlur, line.blur, z, now);
applyStyleProperty(PropertyKey::LineDashLand, line.dash_array[0], z, now);
applyStyleProperty(PropertyKey::LineDashGap, line.dash_array[1], z, now);
applyStyleProperty(PropertyKey::LineImage, line.image, z, now);
}
template <>
void StyleLayer::applyStyleProperties<IconProperties>(const float z, const timestamp now) {
properties.set<IconProperties>();
IconProperties &icon = properties.get<IconProperties>();
applyStyleProperty(PropertyKey::IconEnabled, icon.enabled, z, now);
applyStyleProperty(PropertyKey::IconOpacity, icon.opacity, z, now);
applyStyleProperty(PropertyKey::IconRotate, icon.rotate, z, now);
applyStyleProperty(PropertyKey::IconRotateAnchor, icon.rotate_anchor, z, now);
}
template <>
void StyleLayer::applyStyleProperties<TextProperties>(const float z, const timestamp now) {
properties.set<TextProperties>();
TextProperties &text = properties.get<TextProperties>();
applyStyleProperty(PropertyKey::TextEnabled, text.enabled, z, now);
applyStyleProperty(PropertyKey::TextOpacity, text.opacity, z, now);
applyStyleProperty(PropertyKey::TextSize, text.size, z, now);
applyStyleProperty(PropertyKey::TextColor, text.color, z, now);
applyStyleProperty(PropertyKey::TextHaloColor, text.halo_color, z, now);
applyStyleProperty(PropertyKey::TextHaloWidth, text.halo_width, z, now);
applyStyleProperty(PropertyKey::TextHaloBlur, text.halo_blur, z, now);
}
template <>
void StyleLayer::applyStyleProperties<CompositeProperties>(const float z, const timestamp now) {
properties.set<CompositeProperties>();
CompositeProperties &composite = properties.get<CompositeProperties>();
applyStyleProperty(PropertyKey::CompositeEnabled, composite.enabled, z, now);
applyStyleProperty(PropertyKey::CompositeOpacity, composite.opacity, z, now);
}
template <>
void StyleLayer::applyStyleProperties<RasterProperties>(const float z, const timestamp now) {
properties.set<RasterProperties>();
RasterProperties &raster = properties.get<RasterProperties>();
applyStyleProperty(PropertyKey::RasterEnabled, raster.enabled, z, now);
applyStyleProperty(PropertyKey::RasterOpacity, raster.opacity, z, now);
applyStyleProperty(PropertyKey::RasterSpin, raster.spin, z, now);
applyStyleProperty(PropertyKey::RasterBrightnessLow, raster.brightness[0], z, now);
applyStyleProperty(PropertyKey::RasterBrightnessHigh, raster.brightness[1], z, now);
applyStyleProperty(PropertyKey::RasterSaturation, raster.saturation, z, now);
applyStyleProperty(PropertyKey::RasterContrast, raster.contrast, z, now);
applyStyleProperty(PropertyKey::RasterFade, raster.fade, z, now);
}
template <>
void StyleLayer::applyStyleProperties<BackgroundProperties>(const float z, const timestamp now) {
properties.set<BackgroundProperties>();
BackgroundProperties &background = properties.get<BackgroundProperties>();
applyStyleProperty(PropertyKey::BackgroundColor, background.color, z, now);
}
void StyleLayer::updateProperties(float z, const timestamp now) {
if (layers) {
layers->updateProperties(z, now);
}
// Accomodate for different tile size.
if (bucket && bucket->source) {
z += std::log(bucket->source->tile_size / 256.0f) / M_LN2;
}
cleanupAppliedStyleProperties(now);
if (layers) {
applyStyleProperties<CompositeProperties>(z, now);
} else if (bucket) {
switch (bucket->type) {
case BucketType::Fill: applyStyleProperties<FillProperties>(z, now); break;
case BucketType::Line: applyStyleProperties<LineProperties>(z, now); break;
case BucketType::Icon: applyStyleProperties<IconProperties>(z, now); break;
case BucketType::Text: applyStyleProperties<TextProperties>(z, now); break;
case BucketType::Raster: applyStyleProperties<RasterProperties>(z, now); break;
default: properties.set<std::false_type>(); break;
}
} else {
return applyStyleProperties<BackgroundProperties>(z, now);
}
}
bool StyleLayer::hasTransitions() const {
for (const std::pair<PropertyKey, AppliedClassProperties> &pair : appliedStyle) {
if (pair.second.hasTransitions()) {
return true;
}
}
return false;
}
void StyleLayer::cleanupAppliedStyleProperties(timestamp now) {
auto it = appliedStyle.begin();
const auto end = appliedStyle.end();
while (it != end) {
AppliedClassProperties &properties = it->second;
properties.cleanup(now);
// If the current properties object is empty, remove it from the map entirely.
if (properties.empty()) {
appliedStyle.erase(it++);
} else {
++it;
}
}
}
}

View File

@@ -0,0 +1,35 @@
#include <llmr/style/style_layer_group.hpp>
namespace llmr {
void StyleLayerGroup::setClasses(const std::vector<std::string> &class_names, timestamp now,
const PropertyTransition &defaultTransition) {
for (const std::shared_ptr<StyleLayer> &layer : layers) {
if (layer) {
layer->setClasses(class_names, now, defaultTransition);
}
}
}
void StyleLayerGroup::updateProperties(float z, timestamp t) {
for (const std::shared_ptr<StyleLayer> &layer: layers) {
if (layer) {
layer->updateProperties(z, t);
}
}
}
bool StyleLayerGroup::hasTransitions() const {
for (const std::shared_ptr<const StyleLayer> &layer: layers) {
if (layer) {
if (layer->hasTransitions()) {
return true;
}
}
}
return false;
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,13 @@
#include <llmr/style/style_properties.hpp>
namespace llmr {
template<> const FillProperties &defaultStyleProperties() { static const FillProperties p; return p; }
template<> const LineProperties &defaultStyleProperties() { static const LineProperties p; return p; }
template<> const IconProperties &defaultStyleProperties() { static const IconProperties p; return p; }
template<> const TextProperties &defaultStyleProperties() { static const TextProperties p; return p; }
template<> const CompositeProperties &defaultStyleProperties() { static const CompositeProperties p; return p; }
template<> const RasterProperties &defaultStyleProperties() { static const RasterProperties p; return p; }
template<> const BackgroundProperties &defaultStyleProperties() { static const BackgroundProperties p; return p; }
}

Some files were not shown because too many files have changed in this diff Show More