@@ -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
|
||||
|
||||
6076
bin/style.js
6076
bin/style.js
File diff suppressed because it is too large
Load Diff
@@ -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:
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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() {}
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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:
|
||||
|
||||
|
||||
@@ -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:
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
39
include/llmr/style/applied_class_properties.hpp
Normal file
39
include/llmr/style/applied_class_properties.hpp
Normal 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
|
||||
@@ -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
|
||||
@@ -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
|
||||
32
include/llmr/style/class_dictionary.hpp
Normal file
32
include/llmr/style/class_dictionary.hpp
Normal 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
|
||||
43
include/llmr/style/class_properties.hpp
Normal file
43
include/llmr/style/class_properties.hpp
Normal 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
|
||||
117
include/llmr/style/filter_expression.hpp
Normal file
117
include/llmr/style/filter_expression.hpp
Normal 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
|
||||
79
include/llmr/style/function_properties.hpp
Normal file
79
include/llmr/style/function_properties.hpp
Normal 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
|
||||
@@ -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
|
||||
@@ -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
|
||||
29
include/llmr/style/property_fallback.hpp
Normal file
29
include/llmr/style/property_fallback.hpp
Normal 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
|
||||
63
include/llmr/style/property_key.hpp
Normal file
63
include/llmr/style/property_key.hpp
Normal 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
|
||||
15
include/llmr/style/property_transition.hpp
Normal file
15
include/llmr/style/property_transition.hpp
Normal 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
|
||||
21
include/llmr/style/property_value.hpp
Normal file
21
include/llmr/style/property_value.hpp
Normal 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
|
||||
38
include/llmr/style/rasterize_properties.hpp
Normal file
38
include/llmr/style/rasterize_properties.hpp
Normal 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
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
85
include/llmr/style/style_bucket.hpp
Normal file
85
include/llmr/style/style_bucket.hpp
Normal 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
|
||||
92
include/llmr/style/style_layer.hpp
Normal file
92
include/llmr/style/style_layer.hpp
Normal 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
|
||||
23
include/llmr/style/style_layer_group.hpp
Normal file
23
include/llmr/style/style_layer_group.hpp
Normal 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
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
122
include/llmr/style/style_properties.hpp
Normal file
122
include/llmr/style/style_properties.hpp
Normal 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
|
||||
136
include/llmr/style/types.hpp
Normal file
136
include/llmr/style/types.hpp
Normal 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
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -7,10 +7,6 @@
|
||||
|
||||
namespace llmr {
|
||||
|
||||
extern const char *kSpriteURL;
|
||||
extern const char *kVectorTileURL;
|
||||
extern const char *kGlyphURL;
|
||||
|
||||
namespace util {
|
||||
|
||||
extern const float tileSize;
|
||||
|
||||
@@ -31,7 +31,7 @@ public:
|
||||
// transitions
|
||||
void beginFadeInTransition();
|
||||
bool needsTransition() const;
|
||||
void updateTransitions(time now);
|
||||
void updateTransitions(timestamp now);
|
||||
|
||||
public:
|
||||
// loaded image dimensions
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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; }
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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
|
||||
|
||||
2
llmr.gyp
2
llmr.gyp
@@ -7,7 +7,7 @@
|
||||
{
|
||||
'target_name': 'shaders',
|
||||
'type': 'none',
|
||||
'hard_dependency': 1,
|
||||
# 'hard_dependency': 1,
|
||||
'actions': [
|
||||
{
|
||||
'action_name': 'Build Shaders',
|
||||
|
||||
264
src/map/map.cpp
264
src/map/map.cpp
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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++;
|
||||
}
|
||||
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#include <llmr/renderer/painter.hpp>
|
||||
#include <llmr/style/style_properties.hpp>
|
||||
|
||||
#include <iostream>
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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({{
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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 &);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
|
||||
using namespace llmr;
|
||||
|
||||
PrerenderedTexture::PrerenderedTexture(const PrerenderProperties &properties)
|
||||
PrerenderedTexture::PrerenderedTexture(const RasterizedProperties &properties)
|
||||
: properties(properties) {
|
||||
}
|
||||
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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 {
|
||||
|
||||
52
src/style/applied_class_properties.cpp
Normal file
52
src/style/applied_class_properties.cpp
Normal 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();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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));
|
||||
}
|
||||
|
||||
}
|
||||
29
src/style/class_dictionary.cpp
Normal file
29
src/style/class_dictionary.cpp
Normal 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;
|
||||
|
||||
}
|
||||
14
src/style/class_properties.cpp
Normal file
14
src/style/class_properties.cpp
Normal 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;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
284
src/style/filter_expression.cpp
Normal file
284
src/style/filter_expression.cpp
Normal 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;
|
||||
}
|
||||
|
||||
}
|
||||
131
src/style/function_properties.cpp
Normal file
131
src/style/function_properties.cpp
Normal 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;
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
56
src/style/property_fallback.cpp
Normal file
56
src/style/property_fallback.cpp
Normal 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;
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
1058
src/style/style.cpp
1058
src/style/style.cpp
File diff suppressed because it is too large
Load Diff
302
src/style/style_layer.cpp
Normal file
302
src/style/style_layer.cpp
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
35
src/style/style_layer_group.cpp
Normal file
35
src/style/style_layer_group.cpp
Normal 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
13
src/style/style_properties.cpp
Normal file
13
src/style/style_properties.cpp
Normal 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
Reference in New Issue
Block a user