Files
MapLibreParticleLab/src/style/style.cpp
2014-05-20 11:26:52 -07:00

189 lines
7.0 KiB
C++

#include <llmr/style/style.hpp>
#include <llmr/style/style_parser.hpp>
#include <llmr/util/constants.hpp>
#include <csscolorparser/csscolorparser.hpp>
#include <rapidjson/document.h>
using namespace llmr;
Style::Style() {
appliedClasses.insert("default");
}
void Style::reset() {
computed.fills.clear();
computed.lines.clear();
computed.icons.clear();
computed.texts.clear();
computed.rasters.clear();
}
void Style::cascade(float z) {
reset();
// Accomodate for different tile size.
// TODO: Make this per-layer once individual layers have a specific tile size.
z += std::log(util::tileSize / 256.0f) / M_LN2;
// Recalculate style
// Basic cascading
for (const auto& class_pair : classes) {
const std::string& class_name = class_pair.first;
const ClassDescription& sheetClass = class_pair.second;
// Not enabled
if (appliedClasses.find(class_name) == appliedClasses.end()) continue;
// Cascade fill classes
for (const auto& fill_pair : sheetClass.fill) {
const std::string& layer_name = fill_pair.first;
const llmr::FillClass& layer = fill_pair.second;
// TODO: This should be restricted to fill styles that have actual
// values so as to not override with default values.
llmr::FillProperties& fill = computed.fills[layer_name];
fill.enabled = layer.enabled.evaluate<bool>(z);
fill.translate = {{ layer.translate[0].evaluate<float>(z),
layer.translate[1].evaluate<float>(z) }};
fill.translateAnchor = layer.translateAnchor;
fill.winding = layer.winding;
fill.antialias = layer.antialias.evaluate<bool>(z);
fill.fill_color = layer.fill_color;
fill.stroke_color = layer.stroke_color;
fill.opacity = layer.opacity.evaluate<float>(z);
fill.image = layer.image;
}
// Cascade line classes
for (const auto& line_pair : sheetClass.line) {
const std::string& layer_name = line_pair.first;
const llmr::LineClass& layer = line_pair.second;
// TODO: This should be restricted to line styles that have actual
// values so as to not override with default values.
llmr::LineProperties& stroke = computed.lines[layer_name];
stroke.enabled = layer.enabled.evaluate<bool>(z);
stroke.translate = {{ layer.translate[0].evaluate<float>(z),
layer.translate[1].evaluate<float>(z) }};
stroke.translateAnchor = layer.translateAnchor;
stroke.width = layer.width.evaluate<float>(z);
stroke.offset = layer.offset.evaluate<float>(z);
stroke.color = layer.color;
stroke.dash_array = {{ layer.dash_array[0].evaluate<float>(z),
layer.dash_array[1].evaluate<float>(z) }};
stroke.opacity = layer.opacity.evaluate<float>(z);
}
// Cascade icon classes
for (const auto& icon_pair : sheetClass.icon) {
const std::string& layer_name = icon_pair.first;
const llmr::IconClass& layer = icon_pair.second;
// TODO: This should be restricted to icon styles that have actual
// values so as to not override with default values.
llmr::IconProperties& icon = computed.icons[layer_name];
icon.enabled = layer.enabled.evaluate<bool>(z);
icon.translate = {{ layer.translate[0].evaluate<float>(z),
layer.translate[1].evaluate<float>(z) }};
icon.translateAnchor = layer.translateAnchor;
icon.color = layer.color;
icon.size = layer.size.evaluate<float>(z);
icon.opacity = layer.opacity.evaluate<float>(z);
icon.image = layer.image;
icon.radius = layer.radius.evaluate<float>(z);
icon.blur = layer.blur.evaluate<float>(z);
}
// Cascade text classes
for (const auto& text_pair : sheetClass.text) {
const std::string& layer_name = text_pair.first;
const llmr::TextClass& layer = text_pair.second;
// TODO: This should be restricted to text styles that have actual
// values so as to not override with default values.
llmr::TextProperties& text = computed.texts[layer_name];
text.enabled = layer.enabled.evaluate<bool>(z);
text.translate = {{ layer.translate[0].evaluate<float>(z),
layer.translate[1].evaluate<float>(z) }};
text.translateAnchor = layer.translateAnchor;
text.color = layer.color;
text.size = layer.size.evaluate<float>(z);
text.halo = layer.halo;
text.halo_radius = layer.halo_radius.evaluate<float>(z);
text.halo_blur = layer.halo_blur.evaluate<float>(z);
text.rotate = layer.rotate.evaluate<float>(z);
text.alwaysVisible = layer.alwaysVisible.evaluate<bool>(z);
text.opacity = layer.opacity.evaluate<float>(z);
}
// Cascade raster classes
for (const auto& raster_pair : sheetClass.raster) {
const std::string& layer_name = raster_pair.first;
const llmr::RasterClass& layer = raster_pair.second;
// TODO: This should be restricted to raster styles that have actual
// values so as to not override with default values.
llmr::RasterProperties& raster = computed.rasters[layer_name];
raster.enabled = layer.enabled.evaluate<bool>(z);
raster.opacity = layer.opacity.evaluate<float>(z);
}
// Cascade background
computed.background.color = sheetClass.background.color;
computed.background.opacity = sheetClass.background.opacity.evaluate<float>(z);
}
}
size_t Style::layerCount() const {
size_t count = 0;
for (const LayerDescription &layer : layers) {
count += layer.size();
}
return count;
}
void Style::loadJSON(const uint8_t *const data, size_t bytes) {
rapidjson::Document doc;
if (bytes <= 0) {
return;
}
if (data[bytes - 1] != 0) {
throw exception("style JSON string is not 0-terminated");
}
doc.Parse<0>((const char *const)data);
const rapidjson::Document& document = doc;
buckets.clear();
layers.clear();
classes.clear();
StyleParser parser;
if (document.IsObject()) {
if (document.HasMember("buckets")) {
parser.parseBuckets(document["buckets"], buckets);
}
if (document.HasMember("structure")) {
parser.parseLayers(document["structure"], layers);
}
if (document.HasMember("constants")) {
parser.parseConstants(document["constants"]);
}
if (document.HasMember("classes")) {
parser.parseClasses(document["classes"], classes, buckets, layers);
}
} else {
throw Style::exception("stylesheet root must be an object");
}
}