[core] Get rid of user-specified refs

This commit is contained in:
John Firebaugh
2016-12-19 17:10:51 -08:00
parent 59803d3025
commit 0c0064c050
44 changed files with 645 additions and 69 deletions

View File

@@ -60,6 +60,8 @@ mason_use(pixelmatch VERSION 0.10.0 HEADER_ONLY)
mason_use(geojson VERSION 0.4.0 HEADER_ONLY)
mason_use(polylabel VERSION 1.0.2 HEADER_ONLY)
add_definitions(-DRAPIDJSON_HAS_STDSTRING=1)
if(WITH_COVERAGE)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} --coverage")
set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} --coverage")

View File

@@ -221,6 +221,8 @@ set(MBGL_CORE_FILES
src/mbgl/style/cross_faded_property_evaluator.cpp
src/mbgl/style/cross_faded_property_evaluator.hpp
src/mbgl/style/function.cpp
src/mbgl/style/group_by_layout.cpp
src/mbgl/style/group_by_layout.hpp
src/mbgl/style/layer.cpp
src/mbgl/style/layer_impl.cpp
src/mbgl/style/layer_impl.hpp
@@ -260,6 +262,7 @@ set(MBGL_CORE_FILES
include/mbgl/style/conversion/property_value.hpp
include/mbgl/style/conversion/source.hpp
include/mbgl/style/conversion/tileset.hpp
src/mbgl/style/conversion/stringify.hpp
# style/layers
include/mbgl/style/layers/background_layer.hpp

View File

@@ -73,10 +73,12 @@ set(MBGL_TEST_FILES
# style/conversion
test/style/conversion/geojson_options.test.cpp
test/style/conversion/stringify.test.cpp
# style
test/style/filter.test.cpp
test/style/functions.test.cpp
test/style/group_by_layout.test.cpp
test/style/source.test.cpp
test/style/style.test.cpp
test/style/style_layer.test.cpp

View File

@@ -116,6 +116,8 @@ public:
// Private implementation
const std::unique_ptr<Impl> baseImpl;
friend std::string layoutKey(const Layer&);
};
} // namespace style

View File

@@ -36,6 +36,7 @@ public:
static Color blue() { return { 0.0f, 0.0f, 1.0f, 1.0f }; };
static optional<Color> parse(const std::string&);
std::string stringify() const;
};
inline bool operator==(const Color& colorA, const Color& colorB) {

View File

@@ -23,7 +23,7 @@
"express": "^4.11.1",
"lodash": "^4.16.4",
"mapbox-gl-style-spec": "mapbox/mapbox-gl-style-spec#49e8b407bdbbe6f7c92dbcb56d3d51f425fc2653",
"mapbox-gl-test-suite": "mapbox/mapbox-gl-test-suite#2bdca923fc4190156b5a22af71be29442451db54",
"mapbox-gl-test-suite": "mapbox/mapbox-gl-test-suite#7bf5725a2dfe848463db85ff2c85a0f9551d18bf",
"mkdirp": "^0.5.1",
"node-cmake": "^1.2.1",
"pixelmatch": "^4.0.2",

View File

@@ -24,12 +24,12 @@ void StyleSourcedAnnotationImpl::updateStyle(Style& style) const {
return;
if (sourceLayer->is<LineLayer>()) {
std::unique_ptr<Layer> layer = sourceLayer->baseImpl->copy(layerID, "", AnnotationManager::SourceID);
std::unique_ptr<Layer> layer = sourceLayer->baseImpl->copy(layerID, AnnotationManager::SourceID);
layer->as<LineLayer>()->setSourceLayer(layerID);
layer->as<LineLayer>()->setVisibility(VisibilityType::Visible);
style.addLayer(std::move(layer), sourceLayer->getID());
} else if (sourceLayer->is<FillLayer>()) {
std::unique_ptr<Layer> layer = sourceLayer->baseImpl->copy(layerID, "", AnnotationManager::SourceID);
std::unique_ptr<Layer> layer = sourceLayer->baseImpl->copy(layerID, AnnotationManager::SourceID);
layer->as<FillLayer>()->setSourceLayer(layerID);
layer->as<FillLayer>()->setVisibility(VisibilityType::Visible);
style.addLayer(std::move(layer), sourceLayer->getID());

View File

@@ -3,6 +3,7 @@
#include <mbgl/layout/clip_lines.hpp>
#include <mbgl/renderer/symbol_bucket.hpp>
#include <mbgl/style/filter_evaluator.hpp>
#include <mbgl/style/layer.hpp>
#include <mbgl/sprite/sprite_atlas.hpp>
#include <mbgl/text/glyph_atlas.hpp>
#include <mbgl/text/get_anchors.hpp>
@@ -26,7 +27,7 @@ namespace mbgl {
using namespace style;
SymbolLayout::SymbolLayout(std::string bucketName_,
SymbolLayout::SymbolLayout(std::vector<std::unique_ptr<Layer>> layers_,
std::string sourceLayerName_,
uint32_t overscaling_,
float zoom_,
@@ -36,7 +37,7 @@ SymbolLayout::SymbolLayout(std::string bucketName_,
style::SymbolLayoutProperties::Evaluated layout_,
float textMaxSize_,
SpriteAtlas& spriteAtlas_)
: bucketName(std::move(bucketName_)),
: layers(std::move(layers_)),
sourceLayerName(std::move(sourceLayerName_)),
overscaling(overscaling_),
zoom(zoom_),
@@ -253,7 +254,7 @@ void SymbolLayout::addFeature(const SymbolFeature& feature,
? SymbolPlacementType::Point
: layout.get<SymbolPlacement>();
const float textRepeatDistance = symbolSpacing / 2;
IndexedSubfeature indexedFeature = {feature.index, sourceLayerName, bucketName, symbolInstances.size()};
IndexedSubfeature indexedFeature = {feature.index, sourceLayerName, layers.at(0)->getID(), symbolInstances.size()};
auto addSymbolInstance = [&] (const GeometryCoordinates& line, Anchor& anchor) {
// https://github.com/mapbox/vector-tile-spec/tree/master/2.1#41-layers

View File

@@ -21,13 +21,14 @@ class SymbolBucket;
namespace style {
class Filter;
class Layer;
} // namespace style
struct Anchor;
class SymbolLayout {
public:
SymbolLayout(std::string bucketName_,
SymbolLayout(std::vector<std::unique_ptr<style::Layer>>,
std::string sourceLayerName_,
uint32_t overscaling,
float zoom,
@@ -55,7 +56,7 @@ public:
State state = Pending;
const std::string bucketName;
const std::vector<std::unique_ptr<style::Layer>> layers;
const std::string sourceLayerName;
private:

View File

@@ -0,0 +1,256 @@
#pragma once
#include <mbgl/style/filter.hpp>
#include <mbgl/style/property_value.hpp>
#include <mbgl/style/layout_property.hpp>
#include <mbgl/util/enum.hpp>
#include <mbgl/util/color.hpp>
#include <mbgl/util/feature.hpp>
#include <mbgl/util/ignore.hpp>
#include <mbgl/util/rapidjson.hpp>
#include <array>
#include <vector>
#include <unordered_map>
namespace mbgl {
namespace style {
namespace conversion {
template <class Writer>
void stringify(Writer& writer, NullValue) {
writer.Null();
}
template <class Writer>
void stringify(Writer& writer, bool v) {
writer.Bool(v);
}
template <class Writer>
void stringify(Writer& writer, uint64_t v) {
writer.Uint64(v);
}
template <class Writer>
void stringify(Writer& writer, int64_t v) {
writer.Int64(v);
}
template <class Writer>
void stringify(Writer& writer, double v) {
writer.Double(v);
}
template <class Writer>
void stringify(Writer& writer, const std::string& v) {
writer.String(v);
}
template <class Writer, class T, class Enable = std::enable_if_t<std::is_enum<T>::value>>
void stringify(Writer& writer, const T& v) {
writer.String(Enum<T>::toString(v));
}
template <class Writer>
void stringify(Writer& writer, const Color& v) {
writer.String(v.stringify());
}
template <class Writer>
void stringify(Writer& writer, const std::array<float, 2>& v) {
writer.StartArray();
writer.Double(v[0]);
writer.Double(v[1]);
writer.EndArray();
}
template <class Writer>
void stringify(Writer& writer, const std::array<float, 4>& v) {
writer.StartArray();
writer.Double(v[0]);
writer.Double(v[1]);
writer.Double(v[2]);
writer.Double(v[3]);
writer.EndArray();
}
template <class Writer>
void stringify(Writer&, const Value&);
template <class Writer, class T>
void stringify(Writer& writer, const std::vector<T>& v) {
writer.StartArray();
for (const auto& e : v) {
stringify(writer, e);
}
writer.EndArray();
}
template <class Writer, class T>
void stringify(Writer& writer, const std::unordered_map<std::string, T>& m) {
writer.StartObject();
for (const auto& p : m) {
writer.Key(p.first.data(), static_cast<unsigned>(p.first.size()));
stringify(writer, p.second);
}
writer.EndObject();
}
template <class Writer>
void stringify(Writer& writer, const Value& v) {
Value::visit(v, [&] (const auto& v_) { stringify(writer, v_); });
}
template <class Writer>
class StringifyFilter {
public:
Writer& writer;
void operator()(const NullFilter&) {
writer.Null();
}
void operator()(const EqualsFilter& f) {
stringifyBinaryFilter(f, "==");
}
void operator()(const NotEqualsFilter& f) {
stringifyBinaryFilter(f, "!=");
}
void operator()(const LessThanFilter& f) {
stringifyBinaryFilter(f, "<");
}
void operator()(const LessThanEqualsFilter& f) {
stringifyBinaryFilter(f, "<=");
}
void operator()(const GreaterThanFilter& f) {
stringifyBinaryFilter(f, ">");
}
void operator()(const GreaterThanEqualsFilter& f) {
stringifyBinaryFilter(f, ">=");
}
void operator()(const InFilter& f) {
stringifySetFilter(f, "in");
}
void operator()(const NotInFilter& f) {
stringifySetFilter(f, "!in");
}
void operator()(const AllFilter& f) {
stringifyCompoundFilter(f, "all");
}
void operator()(const AnyFilter& f) {
stringifyCompoundFilter(f, "any");
}
void operator()(const NoneFilter& f) {
stringifyCompoundFilter(f, "none");
}
void operator()(const HasFilter& f) {
stringifyUnaryFilter(f, "has");
}
void operator()(const NotHasFilter& f) {
stringifyUnaryFilter(f, "!has");
}
private:
template <class F>
void stringifyBinaryFilter(const F& f, const char * op) {
writer.StartArray();
writer.String(op);
writer.String(f.key);
stringify(writer, f.value);
writer.EndArray();
}
template <class F>
void stringifySetFilter(const F& f, const char * op) {
writer.StartArray();
writer.String(op);
writer.String(f.key);
for (const auto& value : f.values) {
stringify(writer, value);
}
writer.EndArray();
}
template <class F>
void stringifyCompoundFilter(const F& f, const char * op) {
writer.StartArray();
writer.String(op);
for (const auto& filter : f.filters) {
Filter::visit(filter, *this);
}
writer.EndArray();
}
template <class F>
void stringifyUnaryFilter(const F& f, const char * op) {
writer.StartArray();
writer.String(op);
writer.String(f.key);
writer.EndArray();
}
};
template <class Writer>
void stringify(Writer& writer, const Filter& f) {
Filter::visit(f, StringifyFilter<Writer> { writer });
}
template <class Writer>
void stringify(Writer& writer, const Undefined&) {
assert(false); // Should be omitted entirely instead.
writer.Null();
}
template <class Writer, class T>
void stringify(Writer& writer, const Function<T>& f) {
writer.StartObject();
writer.Key("base");
writer.Double(f.getBase());
writer.Key("stops");
writer.StartArray();
for (const auto& stop : f.getStops()) {
writer.StartArray();
writer.Double(stop.first);
stringify(writer, stop.second);
writer.EndArray();
}
writer.EndArray();
writer.EndObject();
}
template <class Writer, class T>
void stringify(Writer& writer, const PropertyValue<T>& v) {
v.evaluate([&] (const auto& v_) { stringify(writer, v_); });
}
template <class Property, class Writer, class T>
void stringify(Writer& writer, const PropertyValue<T>& value) {
if (value) {
writer.Key(Property::key);
stringify(writer, value);
}
}
template <class Writer, class... Ps>
void stringify(Writer& writer, const LayoutProperties<Ps...>& ps) {
writer.StartObject();
util::ignore({ (stringify<Ps>(writer, ps.unevaluated.template get<Ps>()), 0)... });
writer.EndObject();
}
} // namespace conversion
} // namespace style
} // namespace mbgl

View File

@@ -0,0 +1,51 @@
#include <mbgl/style/group_by_layout.hpp>
#include <mbgl/style/layer.hpp>
#include <mbgl/style/layer_impl.hpp>
#include <mbgl/style/conversion/stringify.hpp>
#include <mbgl/util/rapidjson.hpp>
#include <rapidjson/writer.h>
#include <rapidjson/stringbuffer.h>
#include <unordered_map>
namespace mbgl {
namespace style {
std::string layoutKey(const Layer& layer) {
using namespace conversion;
rapidjson::StringBuffer s;
rapidjson::Writer<rapidjson::StringBuffer> writer(s);
writer.StartArray();
writer.Uint(static_cast<uint32_t>(layer.type));
writer.String(layer.baseImpl->source);
writer.String(layer.baseImpl->sourceLayer);
writer.Double(layer.baseImpl->minZoom);
writer.Double(layer.baseImpl->maxZoom);
writer.Uint(static_cast<uint32_t>(layer.baseImpl->visibility));
stringify(writer, layer.baseImpl->filter);
layer.baseImpl->stringifyLayout(writer);
writer.EndArray();
return s.GetString();
}
std::vector<std::vector<std::unique_ptr<Layer>>> groupByLayout(std::vector<std::unique_ptr<Layer>> layers) {
std::unordered_map<std::string, std::vector<std::unique_ptr<Layer>>> map;
for (auto& layer : layers) {
auto& vector = map[layoutKey(*layer)];
vector.push_back(std::move(layer));
}
std::vector<std::vector<std::unique_ptr<Layer>>> result;
for (auto& pair : map) {
result.push_back(std::move(pair.second));
}
return result;
}
} // namespace style
} // namespace mbgl

View File

@@ -0,0 +1,14 @@
#pragma once
#include <vector>
#include <memory>
namespace mbgl {
namespace style {
class Layer;
std::vector<std::vector<std::unique_ptr<Layer>>> groupByLayout(std::vector<std::unique_ptr<Layer>>);
} // namespace style
} // namespace mbgl

View File

@@ -4,19 +4,13 @@ namespace mbgl {
namespace style {
std::unique_ptr<Layer> Layer::Impl::copy(const std::string& id_,
const std::string& ref_,
const std::string& source_) const {
std::unique_ptr<Layer> result = clone();
result->baseImpl->id = id_;
result->baseImpl->ref = ref_;
result->baseImpl->source = source_;
return result;
}
const std::string& Layer::Impl::bucketName() const {
return ref.empty() ? id : ref;
}
bool Layer::Impl::hasRenderPass(RenderPass pass) const {
return bool(passes & pass);
}

View File

@@ -8,6 +8,9 @@
#include <mbgl/util/noncopyable.hpp>
#include <mbgl/tile/geometry_tile_data.hpp>
#include <rapidjson/writer.h>
#include <rapidjson/stringbuffer.h>
#include <memory>
#include <string>
#include <limits>
@@ -37,20 +40,19 @@ class Layer::Impl {
public:
virtual ~Impl() = default;
// Create a new layer with the specified `id`, `ref`, and `sourceID`. All other properties
// Create a new layer with the specified `id` and `sourceID`. All other properties
// are copied from this layer.
std::unique_ptr<Layer> copy(const std::string& id,
const std::string& ref,
const std::string& sourceID) const;
// Create an identical copy of this layer.
virtual std::unique_ptr<Layer> clone() const = 0;
// Create a layer, copying all properties except id, ref, and paint properties from this layer.
// Create a layer, copying all properties except id and paint properties from this layer.
virtual std::unique_ptr<Layer> cloneRef(const std::string& id) const = 0;
// If the layer has a ref, the ref. Otherwise, the id.
const std::string& bucketName() const;
// Utility function for automatic layer grouping.
virtual void stringifyLayout(rapidjson::Writer<rapidjson::StringBuffer>&) const = 0;
// Partially evaluate paint properties based on a set of classes.
virtual void cascade(const CascadeParameters&) = 0;
@@ -78,7 +80,6 @@ public:
public:
std::string id;
std::string ref;
std::string source;
std::string sourceLayer;
Filter filter;

View File

@@ -2,6 +2,7 @@
#include <mbgl/style/layers/background_layer.hpp>
#include <mbgl/style/layers/background_layer_impl.hpp>
#include <mbgl/style/conversion/stringify.hpp>
namespace mbgl {
namespace style {
@@ -26,11 +27,13 @@ std::unique_ptr<Layer> BackgroundLayer::Impl::clone() const {
std::unique_ptr<Layer> BackgroundLayer::Impl::cloneRef(const std::string& id_) const {
auto result = std::make_unique<BackgroundLayer>(*this);
result->impl->id = id_;
result->impl->ref = this->id;
result->impl->paint = BackgroundPaintProperties();
return std::move(result);
}
void BackgroundLayer::Impl::stringifyLayout(rapidjson::Writer<rapidjson::StringBuffer>&) const {
}
// Layout properties

View File

@@ -11,6 +11,7 @@ class BackgroundLayer::Impl : public Layer::Impl {
public:
std::unique_ptr<Layer> clone() const override;
std::unique_ptr<Layer> cloneRef(const std::string& id) const override;
void stringifyLayout(rapidjson::Writer<rapidjson::StringBuffer>&) const override;
void cascade(const CascadeParameters&) override;
bool evaluate(const PropertyEvaluationParameters&) override;

View File

@@ -2,6 +2,7 @@
#include <mbgl/style/layers/circle_layer.hpp>
#include <mbgl/style/layers/circle_layer_impl.hpp>
#include <mbgl/style/conversion/stringify.hpp>
namespace mbgl {
namespace style {
@@ -27,11 +28,13 @@ std::unique_ptr<Layer> CircleLayer::Impl::clone() const {
std::unique_ptr<Layer> CircleLayer::Impl::cloneRef(const std::string& id_) const {
auto result = std::make_unique<CircleLayer>(*this);
result->impl->id = id_;
result->impl->ref = this->id;
result->impl->paint = CirclePaintProperties();
return std::move(result);
}
void CircleLayer::Impl::stringifyLayout(rapidjson::Writer<rapidjson::StringBuffer>&) const {
}
// Source
const std::string& CircleLayer::getSourceID() const {

View File

@@ -24,11 +24,10 @@ bool CircleLayer::Impl::evaluate(const PropertyEvaluationParameters& parameters)
std::unique_ptr<Bucket> CircleLayer::Impl::createBucket(BucketParameters& parameters, const GeometryTileLayer& layer) const {
auto bucket = std::make_unique<CircleBucket>(parameters.mode);
auto& name = bucketName();
parameters.eachFilteredFeature(filter, layer, [&] (const auto& feature, std::size_t index, const std::string& layerName) {
auto geometries = feature.getGeometries();
bucket->addGeometry(geometries);
parameters.featureIndex.insert(geometries, index, layerName, name);
parameters.featureIndex.insert(geometries, index, layerName, id);
});
return std::move(bucket);

View File

@@ -11,6 +11,7 @@ class CircleLayer::Impl : public Layer::Impl {
public:
std::unique_ptr<Layer> clone() const override;
std::unique_ptr<Layer> cloneRef(const std::string& id) const override;
void stringifyLayout(rapidjson::Writer<rapidjson::StringBuffer>&) const override;
void cascade(const CascadeParameters&) override;
bool evaluate(const PropertyEvaluationParameters&) override;

View File

@@ -34,6 +34,9 @@ std::unique_ptr<Layer> CustomLayer::Impl::cloneRef(const std::string&) const {
return std::make_unique<CustomLayer>(*this);
}
void CustomLayer::Impl::stringifyLayout(rapidjson::Writer<rapidjson::StringBuffer>&) const {
}
void CustomLayer::Impl::initialize() {
assert(initializeFn);
initializeFn(context);

View File

@@ -27,6 +27,7 @@ public:
private:
std::unique_ptr<Layer> clone() const override;
std::unique_ptr<Layer> cloneRef(const std::string& id) const override;
void stringifyLayout(rapidjson::Writer<rapidjson::StringBuffer>&) const override;
void cascade(const CascadeParameters&) final {}
bool evaluate(const PropertyEvaluationParameters&) final;

View File

@@ -2,6 +2,7 @@
#include <mbgl/style/layers/fill_extrusion_layer.hpp>
#include <mbgl/style/layers/fill_extrusion_layer_impl.hpp>
#include <mbgl/style/conversion/stringify.hpp>
namespace mbgl {
namespace style {
@@ -27,11 +28,13 @@ std::unique_ptr<Layer> FillExtrusionLayer::Impl::clone() const {
std::unique_ptr<Layer> FillExtrusionLayer::Impl::cloneRef(const std::string& id_) const {
auto result = std::make_unique<FillExtrusionLayer>(*this);
result->impl->id = id_;
result->impl->ref = this->id;
result->impl->paint = FillExtrusionPaintProperties();
return std::move(result);
}
void FillExtrusionLayer::Impl::stringifyLayout(rapidjson::Writer<rapidjson::StringBuffer>&) const {
}
// Source
const std::string& FillExtrusionLayer::getSourceID() const {

View File

@@ -11,6 +11,7 @@ class FillExtrusionLayer::Impl : public Layer::Impl {
public:
std::unique_ptr<Layer> clone() const override;
std::unique_ptr<Layer> cloneRef(const std::string& id) const override;
void stringifyLayout(rapidjson::Writer<rapidjson::StringBuffer>&) const override;
void cascade(const CascadeParameters&) override;
bool evaluate(const PropertyEvaluationParameters&) override;

View File

@@ -2,6 +2,7 @@
#include <mbgl/style/layers/fill_layer.hpp>
#include <mbgl/style/layers/fill_layer_impl.hpp>
#include <mbgl/style/conversion/stringify.hpp>
namespace mbgl {
namespace style {
@@ -27,11 +28,13 @@ std::unique_ptr<Layer> FillLayer::Impl::clone() const {
std::unique_ptr<Layer> FillLayer::Impl::cloneRef(const std::string& id_) const {
auto result = std::make_unique<FillLayer>(*this);
result->impl->id = id_;
result->impl->ref = this->id;
result->impl->paint = FillPaintProperties();
return std::move(result);
}
void FillLayer::Impl::stringifyLayout(rapidjson::Writer<rapidjson::StringBuffer>&) const {
}
// Source
const std::string& FillLayer::getSourceID() const {

View File

@@ -33,11 +33,10 @@ bool FillLayer::Impl::evaluate(const PropertyEvaluationParameters& parameters) {
std::unique_ptr<Bucket> FillLayer::Impl::createBucket(BucketParameters& parameters, const GeometryTileLayer& layer) const {
auto bucket = std::make_unique<FillBucket>();
auto& name = bucketName();
parameters.eachFilteredFeature(filter, layer, [&] (const auto& feature, std::size_t index, const std::string& layerName) {
auto geometries = feature.getGeometries();
bucket->addGeometry(geometries);
parameters.featureIndex.insert(geometries, index, layerName, name);
parameters.featureIndex.insert(geometries, index, layerName, id);
});
return std::move(bucket);

View File

@@ -11,6 +11,7 @@ class FillLayer::Impl : public Layer::Impl {
public:
std::unique_ptr<Layer> clone() const override;
std::unique_ptr<Layer> cloneRef(const std::string& id) const override;
void stringifyLayout(rapidjson::Writer<rapidjson::StringBuffer>&) const override;
void cascade(const CascadeParameters&) override;
bool evaluate(const PropertyEvaluationParameters&) override;

View File

@@ -7,6 +7,7 @@
#include <mbgl/style/layers/<%- type.replace('-', '_') %>_layer.hpp>
#include <mbgl/style/layers/<%- type.replace('-', '_') %>_layer_impl.hpp>
#include <mbgl/style/conversion/stringify.hpp>
namespace mbgl {
namespace style {
@@ -40,11 +41,19 @@ std::unique_ptr<Layer> <%- camelize(type) %>Layer::Impl::clone() const {
std::unique_ptr<Layer> <%- camelize(type) %>Layer::Impl::cloneRef(const std::string& id_) const {
auto result = std::make_unique<<%- camelize(type) %>Layer>(*this);
result->impl->id = id_;
result->impl->ref = this->id;
result->impl->paint = <%- camelize(type) %>PaintProperties();
return std::move(result);
}
<% if (layoutProperties.length) { -%>
void <%- camelize(type) %>Layer::Impl::stringifyLayout(rapidjson::Writer<rapidjson::StringBuffer>& writer) const {
conversion::stringify(writer, layout);
}
<% } else { -%>
void <%- camelize(type) %>Layer::Impl::stringifyLayout(rapidjson::Writer<rapidjson::StringBuffer>&) const {
}
<% } -%>
<% if (type !== 'background') { -%>
// Source

View File

@@ -16,6 +16,7 @@ namespace style {
<% for (const property of layoutProperties) { -%>
struct <%- camelize(property.name) %> : LayoutProperty<<%- propertyType(property) %>> {
static constexpr const char * key = "<%- property.name %>";
static <%- propertyType(property) %> defaultValue() { return <%- defaultValue(property) %>; }
};

View File

@@ -2,6 +2,7 @@
#include <mbgl/style/layers/line_layer.hpp>
#include <mbgl/style/layers/line_layer_impl.hpp>
#include <mbgl/style/conversion/stringify.hpp>
namespace mbgl {
namespace style {
@@ -27,11 +28,14 @@ std::unique_ptr<Layer> LineLayer::Impl::clone() const {
std::unique_ptr<Layer> LineLayer::Impl::cloneRef(const std::string& id_) const {
auto result = std::make_unique<LineLayer>(*this);
result->impl->id = id_;
result->impl->ref = this->id;
result->impl->paint = LinePaintProperties();
return std::move(result);
}
void LineLayer::Impl::stringifyLayout(rapidjson::Writer<rapidjson::StringBuffer>& writer) const {
conversion::stringify(writer, layout);
}
// Source
const std::string& LineLayer::getSourceID() const {

View File

@@ -31,11 +31,10 @@ std::unique_ptr<Bucket> LineLayer::Impl::createBucket(BucketParameters& paramete
bucket->layout = layout.evaluate(PropertyEvaluationParameters(parameters.tileID.overscaledZ));
auto& name = bucketName();
parameters.eachFilteredFeature(filter, layer, [&] (const auto& feature, std::size_t index, const std::string& layerName) {
auto geometries = feature.getGeometries();
bucket->addGeometry(geometries);
parameters.featureIndex.insert(geometries, index, layerName, name);
parameters.featureIndex.insert(geometries, index, layerName, id);
});
return std::move(bucket);

View File

@@ -11,6 +11,7 @@ class LineLayer::Impl : public Layer::Impl {
public:
std::unique_ptr<Layer> clone() const override;
std::unique_ptr<Layer> cloneRef(const std::string& id) const override;
void stringifyLayout(rapidjson::Writer<rapidjson::StringBuffer>&) const override;
void cascade(const CascadeParameters&) override;
bool evaluate(const PropertyEvaluationParameters&) override;

View File

@@ -10,18 +10,22 @@ namespace mbgl {
namespace style {
struct LineCap : LayoutProperty<LineCapType> {
static constexpr const char * key = "line-cap";
static LineCapType defaultValue() { return LineCapType::Butt; }
};
struct LineJoin : LayoutProperty<LineJoinType> {
static constexpr const char * key = "line-join";
static LineJoinType defaultValue() { return LineJoinType::Miter; }
};
struct LineMiterLimit : LayoutProperty<float> {
static constexpr const char * key = "line-miter-limit";
static float defaultValue() { return 2; }
};
struct LineRoundLimit : LayoutProperty<float> {
static constexpr const char * key = "line-round-limit";
static float defaultValue() { return 1; }
};

View File

@@ -2,6 +2,7 @@
#include <mbgl/style/layers/raster_layer.hpp>
#include <mbgl/style/layers/raster_layer_impl.hpp>
#include <mbgl/style/conversion/stringify.hpp>
namespace mbgl {
namespace style {
@@ -27,11 +28,13 @@ std::unique_ptr<Layer> RasterLayer::Impl::clone() const {
std::unique_ptr<Layer> RasterLayer::Impl::cloneRef(const std::string& id_) const {
auto result = std::make_unique<RasterLayer>(*this);
result->impl->id = id_;
result->impl->ref = this->id;
result->impl->paint = RasterPaintProperties();
return std::move(result);
}
void RasterLayer::Impl::stringifyLayout(rapidjson::Writer<rapidjson::StringBuffer>&) const {
}
// Source
const std::string& RasterLayer::getSourceID() const {

View File

@@ -11,6 +11,7 @@ class RasterLayer::Impl : public Layer::Impl {
public:
std::unique_ptr<Layer> clone() const override;
std::unique_ptr<Layer> cloneRef(const std::string& id) const override;
void stringifyLayout(rapidjson::Writer<rapidjson::StringBuffer>&) const override;
void cascade(const CascadeParameters&) override;
bool evaluate(const PropertyEvaluationParameters&) override;

View File

@@ -2,6 +2,7 @@
#include <mbgl/style/layers/symbol_layer.hpp>
#include <mbgl/style/layers/symbol_layer_impl.hpp>
#include <mbgl/style/conversion/stringify.hpp>
namespace mbgl {
namespace style {
@@ -27,11 +28,14 @@ std::unique_ptr<Layer> SymbolLayer::Impl::clone() const {
std::unique_ptr<Layer> SymbolLayer::Impl::cloneRef(const std::string& id_) const {
auto result = std::make_unique<SymbolLayer>(*this);
result->impl->id = id_;
result->impl->ref = this->id;
result->impl->paint = SymbolPaintProperties();
return std::move(result);
}
void SymbolLayer::Impl::stringifyLayout(rapidjson::Writer<rapidjson::StringBuffer>& writer) const {
conversion::stringify(writer, layout);
}
// Source
const std::string& SymbolLayer::getSourceID() const {

View File

@@ -29,7 +29,9 @@ std::unique_ptr<Bucket> SymbolLayer::Impl::createBucket(BucketParameters&, const
return nullptr;
}
std::unique_ptr<SymbolLayout> SymbolLayer::Impl::createLayout(BucketParameters& parameters, const GeometryTileLayer& layer) const {
std::unique_ptr<SymbolLayout> SymbolLayer::Impl::createLayout(BucketParameters& parameters,
const GeometryTileLayer& layer,
std::vector<std::unique_ptr<Layer>> group) const {
PropertyEvaluationParameters p(parameters.tileID.overscaledZ);
SymbolLayoutProperties::Evaluated evaluated = layout.evaluate(p);
@@ -59,7 +61,7 @@ std::unique_ptr<SymbolLayout> SymbolLayer::Impl::createLayout(BucketParameters&
evaluated.get<IconSize>() = layout.evaluate<IconSize>(PropertyEvaluationParameters(p.z + 1));
evaluated.get<TextSize>() = layout.evaluate<TextSize>(PropertyEvaluationParameters(p.z + 1));
return std::make_unique<SymbolLayout>(id,
return std::make_unique<SymbolLayout>(std::move(group),
layer.getName(),
parameters.tileID.overscaleFactor(),
parameters.tileID.overscaledZ,

View File

@@ -45,12 +45,14 @@ class SymbolLayer::Impl : public Layer::Impl {
public:
std::unique_ptr<Layer> clone() const override;
std::unique_ptr<Layer> cloneRef(const std::string& id) const override;
void stringifyLayout(rapidjson::Writer<rapidjson::StringBuffer>&) const override;
void cascade(const CascadeParameters&) override;
bool evaluate(const PropertyEvaluationParameters&) override;
std::unique_ptr<Bucket> createBucket(BucketParameters&, const GeometryTileLayer&) const override;
std::unique_ptr<SymbolLayout> createLayout(BucketParameters&, const GeometryTileLayer&) const;
std::unique_ptr<SymbolLayout> createLayout(BucketParameters&, const GeometryTileLayer&,
std::vector<std::unique_ptr<Layer>>) const;
SymbolPropertyValues iconPropertyValues(const SymbolLayoutProperties::Evaluated&) const;
SymbolPropertyValues textPropertyValues(const SymbolLayoutProperties::Evaluated&) const;

View File

@@ -10,138 +10,172 @@ namespace mbgl {
namespace style {
struct SymbolPlacement : LayoutProperty<SymbolPlacementType> {
static constexpr const char * key = "symbol-placement";
static SymbolPlacementType defaultValue() { return SymbolPlacementType::Point; }
};
struct SymbolSpacing : LayoutProperty<float> {
static constexpr const char * key = "symbol-spacing";
static float defaultValue() { return 250; }
};
struct SymbolAvoidEdges : LayoutProperty<bool> {
static constexpr const char * key = "symbol-avoid-edges";
static bool defaultValue() { return false; }
};
struct IconAllowOverlap : LayoutProperty<bool> {
static constexpr const char * key = "icon-allow-overlap";
static bool defaultValue() { return false; }
};
struct IconIgnorePlacement : LayoutProperty<bool> {
static constexpr const char * key = "icon-ignore-placement";
static bool defaultValue() { return false; }
};
struct IconOptional : LayoutProperty<bool> {
static constexpr const char * key = "icon-optional";
static bool defaultValue() { return false; }
};
struct IconRotationAlignment : LayoutProperty<AlignmentType> {
static constexpr const char * key = "icon-rotation-alignment";
static AlignmentType defaultValue() { return AlignmentType::Auto; }
};
struct IconSize : LayoutProperty<float> {
static constexpr const char * key = "icon-size";
static float defaultValue() { return 1; }
};
struct IconTextFit : LayoutProperty<IconTextFitType> {
static constexpr const char * key = "icon-text-fit";
static IconTextFitType defaultValue() { return IconTextFitType::None; }
};
struct IconTextFitPadding : LayoutProperty<std::array<float, 4>> {
static constexpr const char * key = "icon-text-fit-padding";
static std::array<float, 4> defaultValue() { return {{ 0, 0, 0, 0 }}; }
};
struct IconImage : LayoutProperty<std::string> {
static constexpr const char * key = "icon-image";
static std::string defaultValue() { return ""; }
};
struct IconRotate : LayoutProperty<float> {
static constexpr const char * key = "icon-rotate";
static float defaultValue() { return 0; }
};
struct IconPadding : LayoutProperty<float> {
static constexpr const char * key = "icon-padding";
static float defaultValue() { return 2; }
};
struct IconKeepUpright : LayoutProperty<bool> {
static constexpr const char * key = "icon-keep-upright";
static bool defaultValue() { return false; }
};
struct IconOffset : LayoutProperty<std::array<float, 2>> {
static constexpr const char * key = "icon-offset";
static std::array<float, 2> defaultValue() { return {{ 0, 0 }}; }
};
struct TextPitchAlignment : LayoutProperty<AlignmentType> {
static constexpr const char * key = "text-pitch-alignment";
static AlignmentType defaultValue() { return AlignmentType::Auto; }
};
struct TextRotationAlignment : LayoutProperty<AlignmentType> {
static constexpr const char * key = "text-rotation-alignment";
static AlignmentType defaultValue() { return AlignmentType::Auto; }
};
struct TextField : LayoutProperty<std::string> {
static constexpr const char * key = "text-field";
static std::string defaultValue() { return ""; }
};
struct TextFont : LayoutProperty<std::vector<std::string>> {
static constexpr const char * key = "text-font";
static std::vector<std::string> defaultValue() { return { "Open Sans Regular", "Arial Unicode MS Regular" }; }
};
struct TextSize : LayoutProperty<float> {
static constexpr const char * key = "text-size";
static float defaultValue() { return 16; }
};
struct TextMaxWidth : LayoutProperty<float> {
static constexpr const char * key = "text-max-width";
static float defaultValue() { return 10; }
};
struct TextLineHeight : LayoutProperty<float> {
static constexpr const char * key = "text-line-height";
static float defaultValue() { return 1.2; }
};
struct TextLetterSpacing : LayoutProperty<float> {
static constexpr const char * key = "text-letter-spacing";
static float defaultValue() { return 0; }
};
struct TextJustify : LayoutProperty<TextJustifyType> {
static constexpr const char * key = "text-justify";
static TextJustifyType defaultValue() { return TextJustifyType::Center; }
};
struct TextAnchor : LayoutProperty<TextAnchorType> {
static constexpr const char * key = "text-anchor";
static TextAnchorType defaultValue() { return TextAnchorType::Center; }
};
struct TextMaxAngle : LayoutProperty<float> {
static constexpr const char * key = "text-max-angle";
static float defaultValue() { return 45; }
};
struct TextRotate : LayoutProperty<float> {
static constexpr const char * key = "text-rotate";
static float defaultValue() { return 0; }
};
struct TextPadding : LayoutProperty<float> {
static constexpr const char * key = "text-padding";
static float defaultValue() { return 2; }
};
struct TextKeepUpright : LayoutProperty<bool> {
static constexpr const char * key = "text-keep-upright";
static bool defaultValue() { return true; }
};
struct TextTransform : LayoutProperty<TextTransformType> {
static constexpr const char * key = "text-transform";
static TextTransformType defaultValue() { return TextTransformType::None; }
};
struct TextOffset : LayoutProperty<std::array<float, 2>> {
static constexpr const char * key = "text-offset";
static std::array<float, 2> defaultValue() { return {{ 0, 0 }}; }
};
struct TextAllowOverlap : LayoutProperty<bool> {
static constexpr const char * key = "text-allow-overlap";
static bool defaultValue() { return false; }
};
struct TextIgnorePlacement : LayoutProperty<bool> {
static constexpr const char * key = "text-ignore-placement";
static bool defaultValue() { return false; }
};
struct TextOptional : LayoutProperty<bool> {
static constexpr const char * key = "text-optional";
static bool defaultValue() { return false; }
};

View File

@@ -128,7 +128,7 @@ void GeometryTile::onError(std::exception_ptr err) {
}
Bucket* GeometryTile::getBucket(const Layer& layer) {
const auto it = buckets.find(layer.baseImpl->bucketName());
const auto it = buckets.find(layer.baseImpl->id);
if (it == buckets.end()) {
return nullptr;
}

View File

@@ -50,7 +50,7 @@ public:
class LayoutResult {
public:
std::unordered_map<std::string, std::unique_ptr<Bucket>> buckets;
std::unordered_map<std::string, std::shared_ptr<Bucket>> buckets;
std::unique_ptr<FeatureIndex> featureIndex;
std::unique_ptr<GeometryTileData> tileData;
uint64_t correlationID;
@@ -59,7 +59,7 @@ public:
class PlacementResult {
public:
std::unordered_map<std::string, std::unique_ptr<Bucket>> buckets;
std::unordered_map<std::string, std::shared_ptr<Bucket>> buckets;
std::unique_ptr<CollisionTile> collisionTile;
uint64_t correlationID;
};
@@ -80,7 +80,7 @@ private:
uint64_t correlationID = 0;
optional<PlacementConfig> requestedConfig;
std::unordered_map<std::string, std::unique_ptr<Bucket>> buckets;
std::unordered_map<std::string, std::shared_ptr<Bucket>> buckets;
std::unique_ptr<FeatureIndex> featureIndex;
std::unique_ptr<const GeometryTileData> data;
};

View File

@@ -5,6 +5,7 @@
#include <mbgl/text/glyph_atlas.hpp>
#include <mbgl/layout/symbol_layout.hpp>
#include <mbgl/style/bucket_parameters.hpp>
#include <mbgl/style/group_by_layout.hpp>
#include <mbgl/style/layers/symbol_layer.hpp>
#include <mbgl/style/layers/symbol_layer_impl.hpp>
#include <mbgl/renderer/symbol_bucket.hpp>
@@ -201,48 +202,58 @@ void GeometryTileWorker::redoLayout() {
return;
}
// We're doing a fresh parse of the tile, because the underlying data or style has changed.
symbolLayouts.clear();
std::vector<std::string> symbolOrder;
for (auto it = layers->rbegin(); it != layers->rend(); it++) {
if ((*it)->is<SymbolLayer>()) {
symbolOrder.push_back((*it)->getID());
}
}
// We're storing a set of bucket names we've parsed to avoid parsing a bucket twice that is
// referenced from more than one layer
std::unordered_set<std::string> parsed;
std::unordered_map<std::string, std::unique_ptr<Bucket>> buckets;
std::unordered_map<std::string, std::unique_ptr<SymbolLayout>> symbolLayoutMap;
std::unordered_map<std::string, std::shared_ptr<Bucket>> buckets;
auto featureIndex = std::make_unique<FeatureIndex>();
BucketParameters parameters { id, obsolete, *featureIndex, mode };
for (auto i = layers->rbegin(); i != layers->rend(); i++) {
std::vector<std::vector<std::unique_ptr<Layer>>> groups = groupByLayout(std::move(*layers));
for (auto& group : groups) {
if (obsolete) {
return;
}
const Layer* layer = i->get();
const std::string& bucketName = layer->baseImpl->bucketName();
featureIndex->addBucketLayerName(bucketName, layer->baseImpl->id);
if (parsed.find(bucketName) != parsed.end()) {
continue;
}
parsed.emplace(bucketName);
if (!*data) {
continue; // Tile has no data.
}
auto geometryLayer = (*data)->getLayer(layer->baseImpl->sourceLayer);
const Layer& leader = *group.at(0);
auto geometryLayer = (*data)->getLayer(leader.baseImpl->sourceLayer);
if (!geometryLayer) {
continue;
}
if (layer->is<SymbolLayer>()) {
symbolLayouts.push_back(layer->as<SymbolLayer>()->impl->createLayout(parameters, *geometryLayer));
for (const auto& layer : group) {
featureIndex->addBucketLayerName(leader.getID(), layer->getID());
}
if (leader.is<SymbolLayer>()) {
symbolLayoutMap.emplace(leader.getID(),
leader.as<SymbolLayer>()->impl->createLayout(parameters, *geometryLayer, std::move(group)));
} else {
std::unique_ptr<Bucket> bucket = layer->baseImpl->createBucket(parameters, *geometryLayer);
if (bucket->hasData()) {
buckets.emplace(layer->baseImpl->bucketName(), std::move(bucket));
std::shared_ptr<Bucket> bucket = leader.baseImpl->createBucket(parameters, *geometryLayer);
if (!bucket->hasData()) {
continue;
}
for (const auto& layer : group) {
buckets.emplace(layer->getID(), bucket);
}
}
}
symbolLayouts.clear();
for (const auto& symbolLayerID : symbolOrder) {
auto it = symbolLayoutMap.find(symbolLayerID);
if (it != symbolLayoutMap.end()) {
symbolLayouts.push_back(std::move(it->second));
}
}
@@ -297,7 +308,7 @@ void GeometryTileWorker::attemptPlacement() {
}
auto collisionTile = std::make_unique<CollisionTile>(*placementConfig);
std::unordered_map<std::string, std::unique_ptr<Bucket>> buckets;
std::unordered_map<std::string, std::shared_ptr<Bucket>> buckets;
for (auto& symbolLayout : symbolLayouts) {
if (obsolete) {
@@ -305,9 +316,13 @@ void GeometryTileWorker::attemptPlacement() {
}
symbolLayout->state = SymbolLayout::Placed;
if (symbolLayout->hasSymbolInstances()) {
buckets.emplace(symbolLayout->bucketName,
symbolLayout->place(*collisionTile));
if (!symbolLayout->hasSymbolInstances()) {
continue;
}
std::shared_ptr<Bucket> bucket = symbolLayout->place(*collisionTile);
for (const auto& layer : symbolLayout->layers) {
buckets.emplace(layer->getID(), bucket);
}
}

View File

@@ -1,4 +1,5 @@
#include <mbgl/util/color.hpp>
#include <mbgl/util/string.hpp>
#include <csscolorparser/csscolorparser.hpp>
@@ -17,4 +18,12 @@ optional<Color> Color::parse(const std::string& s) {
}};
}
std::string Color::stringify() const {
return "rgba(" +
util::toString(r * 255) + "," +
util::toString(g * 255) + "," +
util::toString(b * 255) + "," +
util::toString(a) + ")";
}
} // namespace mbgl

View File

@@ -0,0 +1,98 @@
#include <mbgl/test/util.hpp>
#include <mbgl/style/conversion/stringify.hpp>
#include <mbgl/style/types.hpp>
#include <mbgl/style/layers/symbol_layer_properties.hpp>
#include <mbgl/util/rapidjson.hpp>
#include <rapidjson/writer.h>
#include <rapidjson/stringbuffer.h>
using namespace mbgl;
using namespace mbgl::style;
using namespace mbgl::style::conversion;
template <class T>
std::string stringify(const T& t) {
rapidjson::StringBuffer s;
rapidjson::Writer<rapidjson::StringBuffer> writer(s);
stringify(writer, t);
return s.GetString();
}
TEST(Stringify, NullValue) {
ASSERT_EQ(stringify(NullValue()), "null");
}
TEST(Stringify, Boolean) {
ASSERT_EQ(stringify(true), "true");
ASSERT_EQ(stringify(false), "false");
}
TEST(Stringify, Uint64) {
ASSERT_EQ(stringify(uint64_t(0)), "0");
ASSERT_EQ(stringify(uint64_t(1)), "1");
}
TEST(Stringify, Int64) {
ASSERT_EQ(stringify(int64_t(0)), "0");
ASSERT_EQ(stringify(int64_t(-1)), "-1");
}
TEST(Stringify, Double) {
ASSERT_EQ(stringify(0.0), "0.0");
ASSERT_EQ(stringify(10.1234), "10.1234");
}
TEST(Stringify, String) {
ASSERT_EQ(stringify(std::string("test")), "\"test\"");
}
TEST(Stringify, Enum) {
ASSERT_EQ(stringify(VisibilityType::Visible), "\"visible\"");
}
TEST(Stringify, Color) {
ASSERT_EQ(stringify(Color::blue()), "\"rgba(0,0,255,1)\"");
}
TEST(Stringify, Array) {
ASSERT_EQ(stringify(std::array<float, 2> {{ 1, 2 }}), "[1.0,2.0]");
}
TEST(Stringify, Vector) {
ASSERT_EQ(stringify(std::vector<float> {{ 1, 2 }}), "[1.0,2.0]");
}
TEST(Stringify, Map) {
ASSERT_EQ(stringify(std::unordered_map<std::string, float> {{ "a", 1 }}), "{\"a\":1.0}");
}
TEST(Stringify, Value) {
ASSERT_EQ(stringify(Value(true)), "true");
ASSERT_EQ(stringify(Value(uint64_t(0))), "0");
ASSERT_EQ(stringify(Value(1.2)), "1.2");
}
TEST(Stringify, Filter) {
ASSERT_EQ(stringify(NullFilter()), "null");
ASSERT_EQ(stringify(EqualsFilter { "a", 1.0 }), "[\"==\",\"a\",1.0]");
}
TEST(Stringify, Function) {
ASSERT_EQ(stringify(Function<float>({{0, 1}}, 2)), "{\"base\":2.0,\"stops\":[[0.0,1.0]]}");
}
TEST(Stringify, PropertyValue) {
ASSERT_EQ(stringify(PropertyValue<float>(1)), "1.0");
ASSERT_EQ(stringify(PropertyValue<float>(Function<float>({{0, 1}}, 2))), "{\"base\":2.0,\"stops\":[[0.0,1.0]]}");
}
TEST(Stringify, Layout) {
ASSERT_EQ(stringify(SymbolLayoutProperties()), "{}");
SymbolLayoutProperties layout;
layout.unevaluated.get<SymbolAvoidEdges>() = true;
layout.unevaluated.get<IconPadding>() = 2.0;
ASSERT_EQ(stringify(layout), "{\"symbol-avoid-edges\":true,\"icon-padding\":2.0}");
}

View File

@@ -0,0 +1,44 @@
#include <mbgl/test/util.hpp>
#include <mbgl/style/group_by_layout.hpp>
#include <mbgl/style/layers/background_layer.hpp>
#include <mbgl/style/layers/circle_layer.hpp>
#include <mbgl/style/layers/line_layer.hpp>
using namespace mbgl;
using namespace mbgl::style;
TEST(GroupByLayout, Related) {
std::vector<std::unique_ptr<Layer>> layers;
layers.push_back(std::make_unique<LineLayer>("a", "source"));
layers.push_back(std::make_unique<LineLayer>("b", "source"));
auto result = groupByLayout(std::move(layers));
ASSERT_EQ(1u, result.size());
ASSERT_EQ(2u, result[0].size());
}
TEST(GroupByLayout, UnrelatedType) {
std::vector<std::unique_ptr<Layer>> layers;
layers.push_back(std::make_unique<BackgroundLayer>("background"));
layers.push_back(std::make_unique<CircleLayer>("circle", "source"));
auto result = groupByLayout(std::move(layers));
ASSERT_EQ(2u, result.size());
}
TEST(GroupByLayout, UnrelatedFilter) {
std::vector<std::unique_ptr<Layer>> layers;
layers.push_back(std::make_unique<LineLayer>("a", "source"));
layers.push_back(std::make_unique<LineLayer>("b", "source"));
layers[0]->as<LineLayer>()->setFilter(EqualsFilter());
auto result = groupByLayout(std::move(layers));
ASSERT_EQ(2u, result.size());
}
TEST(GroupByLayout, UnrelatedLayout) {
std::vector<std::unique_ptr<Layer>> layers;
layers.push_back(std::make_unique<LineLayer>("a", "source"));
layers.push_back(std::make_unique<LineLayer>("b", "source"));
layers[0]->as<LineLayer>()->setLineCap(LineCapType::Square);
auto result = groupByLayout(std::move(layers));
ASSERT_EQ(2u, result.size());
}