From a73e88fee28593a0e66be1e67d37ae444b0bfc6c Mon Sep 17 00:00:00 2001 From: Stefan Karschti Date: Mon, 6 Nov 2023 20:43:58 +0200 Subject: [PATCH] Custom drawable layer: Simple Fill (#1810) Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Alex Cristici --- CMakeLists.txt | 2 + bazel/core.bzl | 2 + include/mbgl/gfx/drawable_builder.hpp | 2 + include/mbgl/gfx/fill_generator.hpp | 25 +++ .../style/layers/custom_drawable_layer.hpp | 29 ++- .../app/ExampleCustomDrawableStyleLayer.mm | 94 +++++--- src/mbgl/gfx/fill_generator.cpp | 151 +++++++++++++ src/mbgl/renderer/buckets/fill_bucket.cpp | 98 +-------- .../style/layers/custom_drawable_layer.cpp | 205 +++++++++++++++--- test/api/custom_drawable_layer.test.cpp | 164 ++++++++------ .../custom_drawable_layer/basic/expected.png | Bin 30223 -> 30225 bytes 11 files changed, 551 insertions(+), 221 deletions(-) create mode 100644 include/mbgl/gfx/fill_generator.hpp create mode 100644 src/mbgl/gfx/fill_generator.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 09d71e917..d837dfb82 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -238,6 +238,7 @@ list(APPEND INCLUDE_FILES ${PROJECT_SOURCE_DIR}/include/mbgl/gfx/backend_scope.hpp ${PROJECT_SOURCE_DIR}/include/mbgl/gfx/gfx_types.hpp ${PROJECT_SOURCE_DIR}/include/mbgl/gfx/polyline_generator.hpp + ${PROJECT_SOURCE_DIR}/include/mbgl/gfx/fill_generator.hpp ${PROJECT_SOURCE_DIR}/include/mbgl/gfx/renderable.hpp ${PROJECT_SOURCE_DIR}/include/mbgl/gfx/renderer_backend.hpp ${PROJECT_SOURCE_DIR}/include/mbgl/gfx/rendering_stats.hpp @@ -470,6 +471,7 @@ list(APPEND SRC_FILES ${PROJECT_SOURCE_DIR}/src/mbgl/gfx/index_vector.hpp ${PROJECT_SOURCE_DIR}/src/mbgl/gfx/offscreen_texture.hpp ${PROJECT_SOURCE_DIR}/src/mbgl/gfx/polyline_generator.cpp + ${PROJECT_SOURCE_DIR}/src/mbgl/gfx/fill_generator.cpp ${PROJECT_SOURCE_DIR}/src/mbgl/gfx/program.hpp ${PROJECT_SOURCE_DIR}/src/mbgl/gfx/render_pass.hpp ${PROJECT_SOURCE_DIR}/src/mbgl/gfx/renderbuffer.hpp diff --git a/bazel/core.bzl b/bazel/core.bzl index d8bab43ec..018afb979 100644 --- a/bazel/core.bzl +++ b/bazel/core.bzl @@ -613,6 +613,7 @@ MLN_CORE_SOURCE = [ "src/mbgl/util/quaternion.cpp", "src/mbgl/util/quaternion.hpp", "src/mbgl/gfx/polyline_generator.cpp", + "src/mbgl/gfx/fill_generator.cpp", "src/mbgl/util/rapidjson.cpp", "src/mbgl/util/rapidjson.hpp", "src/mbgl/util/rect.hpp", @@ -820,6 +821,7 @@ MLN_CORE_HEADERS = [ "include/mbgl/util/noncopyable.hpp", "include/mbgl/util/platform.hpp", "include/mbgl/gfx/polyline_generator.hpp", + "include/mbgl/gfx/fill_generator.hpp", "include/mbgl/util/premultiply.hpp", "include/mbgl/util/projection.hpp", "include/mbgl/util/range.hpp", diff --git a/include/mbgl/gfx/drawable_builder.hpp b/include/mbgl/gfx/drawable_builder.hpp index d5cca13c4..5013842ea 100644 --- a/include/mbgl/gfx/drawable_builder.hpp +++ b/include/mbgl/gfx/drawable_builder.hpp @@ -204,6 +204,8 @@ public: /// return the curent vertex count std::size_t curVertexCount() const; + bool empty() const { return !(drawables.size() || curVertexCount()); } + protected: /// Create an instance of the appropriate drawable type virtual UniqueDrawable createDrawable() const = 0; diff --git a/include/mbgl/gfx/fill_generator.hpp b/include/mbgl/gfx/fill_generator.hpp new file mode 100644 index 000000000..c273b1ee0 --- /dev/null +++ b/include/mbgl/gfx/fill_generator.hpp @@ -0,0 +1,25 @@ +#pragma once + +#include +#include +#include +#include +#include + +namespace mbgl { +namespace gfx { + +void generateFillBuffers(const GeometryCollection& geometry, + gfx::VertexVector& vertices, + SegmentVector& triangleSegments, + gfx::IndexVector& triangles); + +void generateFillAndOutineBuffers(const GeometryCollection& geometry, + gfx::VertexVector& vertices, + SegmentVector& lineSegments, + gfx::IndexVector& lines, + SegmentVector& triangleSegments, + gfx::IndexVector& triangles); + +} // namespace gfx +} // namespace mbgl diff --git a/include/mbgl/style/layers/custom_drawable_layer.hpp b/include/mbgl/style/layers/custom_drawable_layer.hpp index 561a922d0..7deb77057 100644 --- a/include/mbgl/style/layers/custom_drawable_layer.hpp +++ b/include/mbgl/style/layers/custom_drawable_layer.hpp @@ -41,6 +41,11 @@ public: gfx::PolylineGeneratorOptions geometry; }; + struct FillOptions { + Color color; + float opacity = 1.f; + }; + public: /// @brief Construct a new Interface object (internal core use only) Interface(RenderLayer& layer, @@ -72,6 +77,13 @@ public: */ void setLineOptions(const LineOptions& options); + /** + * @brief Set the fill options + * + * @param options + */ + void setFillOptions(const FillOptions& options); + /** * @brief Add a polyline * @@ -80,17 +92,14 @@ public: */ void addPolyline(const GeometryCoordinates& coordinates); + void addFill(const GeometryCollection& geometry); + /** - * @brief Finishe the current drawable building session + * @brief Finish the current drawable building session * */ void finish(); -protected: - gfx::ShaderPtr lineShaderDefault() const; - - std::unique_ptr createBuilder(const std::string& name, gfx::ShaderPtr shader) const; - public: RenderLayer& layer; LayerGroupBasePtr& layerGroup; @@ -102,9 +111,17 @@ public: UniqueChangeRequestVec& changes; private: + gfx::ShaderPtr lineShaderDefault() const; + gfx::ShaderPtr fillShaderDefault() const; + + std::unique_ptr createBuilder(const std::string& name, gfx::ShaderPtr shader) const; + + gfx::ShaderPtr lineShader; + gfx::ShaderPtr fillShader; std::unique_ptr builder; std::optional tileID; LineOptions lineOptions; + FillOptions fillOptions; }; class CustomDrawableLayer final : public Layer { diff --git a/platform/darwin/app/ExampleCustomDrawableStyleLayer.mm b/platform/darwin/app/ExampleCustomDrawableStyleLayer.mm index 2c4d88429..a3756dce8 100644 --- a/platform/darwin/app/ExampleCustomDrawableStyleLayer.mm +++ b/platform/darwin/app/ExampleCustomDrawableStyleLayer.mm @@ -24,7 +24,6 @@ class ExampleCustomDrawableStyleLayerHost; return self = [super initWithPendingLayer:std::move(layer)]; } - @end class ExampleCustomDrawableStyleLayerHost : public mbgl::style::CustomDrawableLayerHost { @@ -47,43 +46,76 @@ public: // set tile interface.setTileID({11, 327, 791}); + constexpr float extent = mbgl::util::EXTENT; // add polylines - using namespace mbgl; - - constexpr auto numLines = 6; - Interface::LineOptions options[numLines] { - {/*color=*/Color::red(), /*blur=*/0.0f, /*opacity=*/1.0f, /*gapWidth=*/0.0f, /*offset=*/0.0f, /*width=*/8.0f, {} }, - {/*color=*/Color::blue(), /*blur=*/4.0f, /*opacity=*/1.0f, /*gapWidth=*/2.0f, /*offset=*/-1.0f, /*width=*/4.0f, {} }, - {/*color=*/Color(1.f, 0.5f, 0, 0.5f), /*blur=*/16.0f, /*opacity=*/1.0f, /*gapWidth=*/1.0f, /*offset=*/2.0f, /*width=*/16.0f, {} }, - {/*color=*/Color(1.f, 1.f, 0, 0.3f), /*blur=*/2.0f, /*opacity=*/1.0f, /*gapWidth=*/1.0f, /*offset=*/-2.0f, /*width=*/2.0f, {} }, - {/*color=*/Color::black(), /*blur=*/0.5f, /*opacity=*/0.5f, /*gapWidth=*/1.0f, /*offset=*/0.5f, /*width=*/0.5f, {} }, - {/*color=*/Color(1.f, 0, 1.f, 0.2f), /*blur=*/24.0f, /*opacity=*/0.5f, /*gapWidth=*/1.0f, /*offset=*/-5.0f, /*width=*/24.0f, {} }, - }; - for(auto& opt: options) { - opt.geometry.beginCap = style::LineCapType::Round; - opt.geometry.endCap = style::LineCapType::Round; - opt.geometry.joinType = style::LineJoinType::Round; - } - - constexpr auto numPoints = 100; - GeometryCoordinates polyline; - for (auto ipoint{0}; ipoint < numPoints; ++ipoint) { - polyline.emplace_back(ipoint * util::EXTENT / numPoints, std::sin(ipoint * 2 * M_PI / numPoints) * util::EXTENT / numLines / 2.f); - } - - for (auto index {0}; index < numLines; ++index) { - for(auto &p : polyline) { - p.y += util::EXTENT / numLines; + { + using namespace mbgl; + + constexpr auto numLines = 6; + Interface::LineOptions options[numLines] { + {/*color=*/Color::red(), /*blur=*/0.0f, /*opacity=*/1.0f, /*gapWidth=*/0.0f, /*offset=*/0.0f, /*width=*/8.0f, {} }, + {/*color=*/Color::blue(), /*blur=*/4.0f, /*opacity=*/1.0f, /*gapWidth=*/2.0f, /*offset=*/-1.0f, /*width=*/4.0f, {} }, + {/*color=*/Color(1.f, 0.5f, 0, 0.5f), /*blur=*/16.0f, /*opacity=*/1.0f, /*gapWidth=*/1.0f, /*offset=*/2.0f, /*width=*/16.0f, {} }, + {/*color=*/Color(1.f, 1.f, 0, 0.3f), /*blur=*/2.0f, /*opacity=*/1.0f, /*gapWidth=*/1.0f, /*offset=*/-2.0f, /*width=*/2.0f, {} }, + {/*color=*/Color::black(), /*blur=*/0.5f, /*opacity=*/0.5f, /*gapWidth=*/1.0f, /*offset=*/0.5f, /*width=*/0.5f, {} }, + {/*color=*/Color(1.f, 0, 1.f, 0.2f), /*blur=*/24.0f, /*opacity=*/0.5f, /*gapWidth=*/1.0f, /*offset=*/-5.0f, /*width=*/24.0f, {} }, + }; + for(auto& opt: options) { + opt.geometry.beginCap = style::LineCapType::Round; + opt.geometry.endCap = style::LineCapType::Round; + opt.geometry.joinType = style::LineJoinType::Round; } - // set property values - interface.setLineOptions(options[index]); + constexpr auto numPoints = 100; + GeometryCoordinates polyline; + for (auto ipoint{0}; ipoint < numPoints; ++ipoint) { + polyline.emplace_back(ipoint * extent / numPoints, std::sin(ipoint * 2 * M_PI / numPoints) * extent / numLines / 2.f); + } - // add polyline - interface.addPolyline(polyline); + for (auto index {0}; index < numLines; ++index) { + for(auto &p : polyline) { + p.y += extent / numLines; + } + + // set property values + interface.setLineOptions(options[index]); + + // add polyline + interface.addPolyline(polyline); + } } + // add fill polygon + { + using namespace mbgl; + + GeometryCollection geometry{ + { + // ring 1 + {static_cast(extent* 0.1f), static_cast(extent* 0.2f)}, + {static_cast(extent* 0.5f), static_cast(extent* 0.5f)}, + {static_cast(extent* 0.7f), static_cast(extent* 0.5f)}, + {static_cast(extent* 0.5f), static_cast(extent* 1.0f)}, + {static_cast(extent* 0.0f), static_cast(extent* 0.5f)}, + {static_cast(extent* 0.1f), static_cast(extent* 0.2f)}, + }, + { + // ring 2 + {static_cast(extent* 0.1f), static_cast(extent* 0.25f)}, + {static_cast(extent* 0.15f), static_cast(extent* 0.5f)}, + {static_cast(extent* 0.25f), static_cast(extent* 0.45f)}, + {static_cast(extent* 0.1f), static_cast(extent* 0.25f)}, + }, + }; + + // set properties + interface.setFillOptions({/*color=*/Color::green(), /*opacity=*/0.5f}); + + // add fill + interface.addFill(geometry); + } + // finish interface.finish(); } diff --git a/src/mbgl/gfx/fill_generator.cpp b/src/mbgl/gfx/fill_generator.cpp new file mode 100644 index 000000000..f275ccbd7 --- /dev/null +++ b/src/mbgl/gfx/fill_generator.cpp @@ -0,0 +1,151 @@ +#include + +#ifdef _MSC_VER +#pragma warning(push) +#pragma warning(disable : 4244) +#endif + +#include + +#ifdef _MSC_VER +#pragma warning(pop) +#endif + +#include +#include + +namespace mapbox { +namespace util { +template <> +struct nth<0, mbgl::GeometryCoordinate> { + static int64_t get(const mbgl::GeometryCoordinate& t) { return t.x; }; +}; + +template <> +struct nth<1, mbgl::GeometryCoordinate> { + static int64_t get(const mbgl::GeometryCoordinate& t) { return t.y; }; +}; +} // namespace util +} // namespace mapbox + +namespace mbgl { +namespace gfx { + +struct GeometryTooLongException : std::exception {}; + +namespace { + +std::size_t addRingVertices(gfx::VertexVector& vertices, const GeometryCoordinates& ring) { + for (auto& point : ring) { + vertices.emplace_back(FillProgram::layoutVertex(point)); + } + return ring.size(); +} + +std::size_t totalVerticesCheck(const GeometryCollection& polygon) { + std::size_t totalVertices = 0; + for (const auto& ring : polygon) { + totalVertices += ring.size(); + } + if (totalVertices > std::numeric_limits::max()) throw GeometryTooLongException(); + return totalVertices; +} + +void addFillIndices(SegmentVector& triangleSegments, + gfx::IndexVector& triangles, + std::vector& indices, + std::size_t startVertices, + std::size_t totalVertices) { + std::size_t nIndices = indices.size(); + assert(nIndices % 3 == 0); + + if (triangleSegments.empty() || + triangleSegments.back().vertexLength + totalVertices > std::numeric_limits::max()) { + triangleSegments.emplace_back(startVertices, triangles.elements()); + } + + auto& triangleSegment = triangleSegments.back(); + assert(triangleSegment.vertexLength <= std::numeric_limits::max()); + const auto triangleIndex = static_cast(triangleSegment.vertexLength); + + for (std::size_t i = 0; i < nIndices; i += 3) { + triangles.emplace_back( + triangleIndex + indices[i], triangleIndex + indices[i + 1], triangleIndex + indices[i + 2]); + } + + triangleSegment.vertexLength += totalVertices; + triangleSegment.indexLength += nIndices; +} + +void addOutlineIndices(std::size_t base, + std::size_t nVertices, + SegmentVector& lineSegments, + gfx::IndexVector& lines) { + if (nVertices == 0) return; + + if (lineSegments.empty() || lineSegments.back().vertexLength + nVertices > std::numeric_limits::max()) { + lineSegments.emplace_back(base, lines.elements()); + } + + auto& lineSegment = lineSegments.back(); + assert(lineSegment.vertexLength <= std::numeric_limits::max()); + const auto lineIndex = static_cast(lineSegment.vertexLength); + + lines.emplace_back(static_cast(lineIndex + nVertices - 1), lineIndex); + for (std::size_t i = 1; i < nVertices; i++) { + lines.emplace_back(static_cast(lineIndex + i - 1), static_cast(lineIndex + i)); + } + + lineSegment.vertexLength += nVertices; + lineSegment.indexLength += nVertices * 2; +} + +} // namespace + +void generateFillBuffers(const GeometryCollection& geometry, + gfx::VertexVector& vertices, + SegmentVector& triangleSegments, + gfx::IndexVector& triangles) { + for (auto& polygon : classifyRings(geometry)) { + // Optimize polygons with many interior rings for earcut tesselation. + limitHoles(polygon, 500); + + std::size_t totalVertices = totalVerticesCheck(polygon); + std::size_t startVertices = vertices.elements(); + + for (const auto& ring : polygon) { + addRingVertices(vertices, ring); + } + + std::vector indices = mapbox::earcut(polygon); + addFillIndices(triangleSegments, triangles, indices, startVertices, totalVertices); + } +} + +void generateFillAndOutineBuffers(const GeometryCollection& geometry, + gfx::VertexVector& vertices, + SegmentVector& lineSegments, + gfx::IndexVector& lines, + SegmentVector& triangleSegments, + gfx::IndexVector& triangles) { + for (auto& polygon : classifyRings(geometry)) { + // Optimize polygons with many interior rings for earcut tesselation. + limitHoles(polygon, 500); + + std::size_t totalVertices = totalVerticesCheck(polygon); + std::size_t startVertices = vertices.elements(); + + for (const auto& ring : polygon) { + std::size_t base = vertices.elements(); + std::size_t nVertices = addRingVertices(vertices, ring); + addOutlineIndices(base, nVertices, lineSegments, lines); + } + + std::vector indices = mapbox::earcut(polygon); + addFillIndices(triangleSegments, triangles, indices, startVertices, totalVertices); + } +} + +} // namespace gfx + +} // namespace mbgl diff --git a/src/mbgl/renderer/buckets/fill_bucket.cpp b/src/mbgl/renderer/buckets/fill_bucket.cpp index 87e2d8a27..805c78257 100644 --- a/src/mbgl/renderer/buckets/fill_bucket.cpp +++ b/src/mbgl/renderer/buckets/fill_bucket.cpp @@ -4,44 +4,15 @@ #include #include #include - -#ifdef _MSC_VER -#pragma warning(push) -#pragma warning(disable : 4244) -#endif - -#include - -#ifdef _MSC_VER -#pragma warning(pop) -#endif - -#include - -namespace mapbox { -namespace util { -template <> -struct nth<0, mbgl::GeometryCoordinate> { - static int64_t get(const mbgl::GeometryCoordinate& t) { return t.x; }; -}; - -template <> -struct nth<1, mbgl::GeometryCoordinate> { - static int64_t get(const mbgl::GeometryCoordinate& t) { return t.y; }; -}; -} // namespace util -} // namespace mapbox +#include namespace mbgl { -using namespace style; - -struct GeometryTooLongException : std::exception {}; - FillBucket::FillBucket(const FillBucket::PossiblyEvaluatedLayoutProperties&, const std::map>& layerPaintProperties, const float zoom, const uint32_t) { + using namespace style; for (const auto& pair : layerPaintProperties) { paintPropertyBinders.emplace(std::piecewise_construct, std::forward_as_tuple(pair.first), @@ -59,68 +30,8 @@ void FillBucket::addFeature(const GeometryTileFeature& feature, const PatternLayerMap& patternDependencies, std::size_t index, const CanonicalTileID& canonical) { - for (auto& polygon : classifyRings(geometry)) { - // Optimize polygons with many interior rings for earcut tesselation. - limitHoles(polygon, 500); - - std::size_t totalVertices = 0; - - for (const auto& ring : polygon) { - totalVertices += ring.size(); - if (totalVertices > std::numeric_limits::max()) throw GeometryTooLongException(); - } - - std::size_t startVertices = vertices.elements(); - - for (const auto& ring : polygon) { - std::size_t nVertices = ring.size(); - - if (nVertices == 0) continue; - - if (lineSegments.empty() || - lineSegments.back().vertexLength + nVertices > std::numeric_limits::max()) { - lineSegments.emplace_back(vertices.elements(), lines.elements()); - } - - auto& lineSegment = lineSegments.back(); - assert(lineSegment.vertexLength <= std::numeric_limits::max()); - const auto lineIndex = static_cast(lineSegment.vertexLength); - - vertices.emplace_back(FillProgram::layoutVertex(ring[0])); - lines.emplace_back(static_cast(lineIndex + nVertices - 1), lineIndex); - - for (std::size_t i = 1; i < nVertices; i++) { - vertices.emplace_back(FillProgram::layoutVertex(ring[i])); - lines.emplace_back(static_cast(lineIndex + i - 1), static_cast(lineIndex + i)); - } - - lineSegment.vertexLength += nVertices; - lineSegment.indexLength += nVertices * 2; - } - - std::vector indices = mapbox::earcut(polygon); - - std::size_t nIndicies = indices.size(); - assert(nIndicies % 3 == 0); - - if (triangleSegments.empty() || - triangleSegments.back().vertexLength + totalVertices > std::numeric_limits::max()) { - triangleSegments.emplace_back(startVertices, triangles.elements()); - } - - auto& triangleSegment = triangleSegments.back(); - assert(triangleSegment.vertexLength <= std::numeric_limits::max()); - const auto triangleIndex = static_cast(triangleSegment.vertexLength); - - for (std::size_t i = 0; i < nIndicies; i += 3) { - triangles.emplace_back(static_cast(triangleIndex + indices[i]), - static_cast(triangleIndex + indices[i + 1]), - static_cast(triangleIndex + indices[i + 2])); - } - - triangleSegment.vertexLength += totalVertices; - triangleSegment.indexLength += nIndicies; - } + // generate buffers + gfx::generateFillAndOutineBuffers(geometry, vertices, lineSegments, lines, triangleSegments, triangles); for (auto& pair : paintPropertyBinders) { const auto it = patternDependencies.find(pair.first); @@ -155,6 +66,7 @@ bool FillBucket::hasData() const { } float FillBucket::getQueryRadius(const RenderLayer& layer) const { + using namespace style; const auto& evaluated = getEvaluated(layer.evaluatedProperties); const std::array& translate = evaluated.get(); return util::length(translate[0], translate[1]); diff --git a/src/mbgl/style/layers/custom_drawable_layer.cpp b/src/mbgl/style/layers/custom_drawable_layer.cpp index 17e16921f..bd08cdaf0 100644 --- a/src/mbgl/style/layers/custom_drawable_layer.cpp +++ b/src/mbgl/style/layers/custom_drawable_layer.cpp @@ -15,8 +15,15 @@ #include #include #include +#include +#include +#include +#include + +#include namespace mbgl { + namespace style { namespace { @@ -141,6 +148,78 @@ private: shaders::LinePropertiesUBO linePropertiesUBO; }; +class FillDrawableTweaker : public gfx::DrawableTweaker { +public: + FillDrawableTweaker(const Color& color_, float opacity_) + : color(color_), + opacity(opacity_) {} + ~FillDrawableTweaker() override = default; + + void init(gfx::Drawable&) override{}; + + void execute(gfx::Drawable& drawable, const PaintParameters& parameters) override { + if (!drawable.getTileID().has_value()) { + return; + } + + const UnwrappedTileID tileID = drawable.getTileID()->toUnwrapped(); + mat4 tileMatrix; + parameters.state.matrixFor(/*out*/ tileMatrix, tileID); + + const auto matrix = LayerTweaker::getTileMatrix( + tileID, parameters, {{0, 0}}, style::TranslateAnchorType::Viewport, false, false, false); + + static const StringIdentity idFillDrawableUBOName = stringIndexer().get("FillDrawableUBO"); + const shaders::FillDrawableUBO fillUBO{/*matrix = */ util::cast(matrix)}; + + static const StringIdentity idFillEvaluatedPropsUBOName = stringIndexer().get("FillEvaluatedPropsUBO"); + const shaders::FillEvaluatedPropsUBO fillPropertiesUBO{ + /* .color = */ color, + /* .opacity = */ opacity, + 0, + 0, + 0, + }; + + static const StringIdentity idFillInterpolateUBOName = stringIndexer().get("FillInterpolateUBO"); + const shaders::FillInterpolateUBO fillInterpolateUBO{ + /* .color_t = */ 0.f, + /* .opacity_t = */ 0.f, + 0, + 0, + }; + auto& uniforms = drawable.mutableUniformBuffers(); + uniforms.createOrUpdate(idFillDrawableUBOName, &fillUBO, parameters.context); + uniforms.createOrUpdate(idFillEvaluatedPropsUBOName, &fillPropertiesUBO, parameters.context); + uniforms.createOrUpdate(idFillInterpolateUBOName, &fillInterpolateUBO, parameters.context); + +#if MLN_RENDER_BACKEND_METAL + const auto zoom = parameters.state.getZoom(); + static const StringIdentity idExpressionInputsUBOName = stringIndexer().get("ExpressionInputsUBO"); + const auto expressionUBO = LayerTweaker::buildExpressionUBO(zoom, parameters.frameCount); + uniforms.createOrUpdate(idExpressionInputsUBOName, &expressionUBO, parameters.context); + + static const StringIdentity idFillPermutationUBOName = stringIndexer().get("FillPermutationUBO"); + const shaders::FillPermutationUBO permutationUBO = { + /* .color = */ {/*.source=*/shaders::AttributeSource::Constant, /*.expression=*/{}}, + /* .opacity = */ {/*.source=*/shaders::AttributeSource::Constant, /*.expression=*/{}}, + /* .overdrawInspector = */ false, + 0, + 0, + 0, + 0, + 0, + 0, + }; + uniforms.createOrUpdate(idFillPermutationUBOName, &permutationUBO, parameters.context); +#endif // MLN_RENDER_BACKEND_METAL + }; + +private: + Color color; + float opacity; +}; + CustomDrawableLayerHost::Interface::Interface(RenderLayer& layer_, LayerGroupBasePtr& layerGroup_, gfx::ShaderRegistry& shaders_, @@ -180,39 +259,104 @@ void CustomDrawableLayerHost::Interface::setLineOptions(const LineOptions& optio lineOptions = options; } +void CustomDrawableLayerHost::Interface::setFillOptions(const FillOptions& options) { + finish(); + fillOptions = options; +} + void CustomDrawableLayerHost::Interface::addPolyline(const GeometryCoordinates& coordinates) { - if (!builder) { - builder = createBuilder("thick-lines", lineShaderDefault()); - } else { - // TODO: check builder + if (!lineShader) lineShader = lineShaderDefault(); + assert(lineShader); + if (!builder || builder->getShader() != lineShader) { + builder = createBuilder("lines", lineShader); } + assert(builder); + assert(builder->getShader() == lineShader); builder->addPolyline(coordinates, lineOptions.geometry); } +void CustomDrawableLayerHost::Interface::addFill(const GeometryCollection& geometry) { + if (!fillShader) fillShader = fillShaderDefault(); + assert(fillShader); + if (!builder || builder->getShader() != fillShader) { + builder = createBuilder("fill", fillShader); + } + assert(builder); + assert(builder->getShader() == fillShader); + + // provision buffers for fill vertices, indexes and segments + using VertexVector = gfx::VertexVector; + const std::shared_ptr sharedVertices = std::make_shared(); + VertexVector& vertices = *sharedVertices; + + using TriangleIndexVector = gfx::IndexVector; + const std::shared_ptr sharedTriangles = std::make_shared(); + TriangleIndexVector& triangles = *sharedTriangles; + + SegmentVector triangleSegments; + + // generate fill geometry into buffers + gfx::generateFillBuffers(geometry, vertices, triangleSegments, triangles); + + // add to builder + static const StringIdentity idVertexAttribName = stringIndexer().get("a_pos"); + builder->setVertexAttrNameId(idVertexAttribName); + gfx::VertexAttributeArray attrs; + if (const auto& attr = attrs.add(idVertexAttribName)) { + attr->setSharedRawData(sharedVertices, + offsetof(FillLayoutVertex, a1), + /*vertexOffset=*/0, + sizeof(FillLayoutVertex), + gfx::AttributeDataType::Short2); + } + builder->setVertexAttributes(std::move(attrs)); + builder->setRawVertices({}, vertices.elements(), gfx::AttributeDataType::Short2); + builder->setSegments(gfx::Triangles(), sharedTriangles, triangleSegments.data(), triangleSegments.size()); + + // flush current builder drawable + builder->flush(); +} + void CustomDrawableLayerHost::Interface::finish() { - if (builder && builder->curVertexCount()) { - // create tweaker - const shaders::LinePropertiesUBO linePropertiesUBO{lineOptions.color, - lineOptions.blur, - lineOptions.opacity, - lineOptions.gapWidth, - lineOptions.offset, - lineOptions.width, - 0, - 0, - 0}; - - auto tweaker = std::make_shared(linePropertiesUBO); - + if (builder && !builder->empty()) { // finish - builder->flush(); - for (auto& drawable : builder->clearDrawables()) { - assert(tileID.has_value()); - drawable->setTileID(tileID.value()); - drawable->addTweaker(tweaker); + const auto finish_ = [this](auto& tweaker) { + builder->flush(); + for (auto& drawable : builder->clearDrawables()) { + assert(tileID.has_value()); + drawable->setTileID(tileID.value()); + drawable->addTweaker(tweaker); - TileLayerGroup* tileLayerGroup = static_cast(layerGroup.get()); - tileLayerGroup->addDrawable(RenderPass::Translucent, tileID.value(), std::move(drawable)); + TileLayerGroup* tileLayerGroup = static_cast(layerGroup.get()); + tileLayerGroup->addDrawable(RenderPass::Translucent, tileID.value(), std::move(drawable)); + } + }; + + if (builder->getShader() == lineShader) { + // finish building lines + + // create line tweaker + const shaders::LinePropertiesUBO linePropertiesUBO{lineOptions.color, + lineOptions.blur, + lineOptions.opacity, + lineOptions.gapWidth, + lineOptions.offset, + lineOptions.width, + 0, + 0, + 0}; + auto tweaker = std::make_shared(linePropertiesUBO); + + // finish drawables + finish_(tweaker); + } else if (builder->getShader() == fillShader) { + // finish building fills + + // create fill tweaker + auto tweaker = std::make_shared(fillOptions.color, fillOptions.opacity); + + // finish drawables + finish_(tweaker); } } } @@ -232,6 +376,17 @@ gfx::ShaderPtr CustomDrawableLayerHost::Interface::lineShaderDefault() const { return lineShaderGroup->getOrCreateShader(context, propertiesAsUniforms); } +gfx::ShaderPtr CustomDrawableLayerHost::Interface::fillShaderDefault() const { + gfx::ShaderGroupPtr fillShaderGroup = shaders.getShaderGroup("FillShader"); + + const std::unordered_set propertiesAsUniforms{ + stringIndexer().get("a_color"), + stringIndexer().get("a_opacity"), + }; + + return fillShaderGroup->getOrCreateShader(context, propertiesAsUniforms); +} + std::unique_ptr CustomDrawableLayerHost::Interface::createBuilder(const std::string& name, gfx::ShaderPtr shader) const { std::unique_ptr builder_ = context.createDrawableBuilder(name); diff --git a/test/api/custom_drawable_layer.test.cpp b/test/api/custom_drawable_layer.test.cpp index 5673cbe14..d592c5831 100644 --- a/test/api/custom_drawable_layer.test.cpp +++ b/test/api/custom_drawable_layer.test.cpp @@ -30,76 +30,108 @@ public: interface.setTileID({11, 327, 791}); // add polylines - using namespace mbgl; + { + using namespace mbgl; - constexpr auto numLines = 6; - Interface::LineOptions options[numLines]{ - {/*color=*/Color::red(), - /*blur=*/0.0f, - /*opacity=*/1.0f, - /*gapWidth=*/0.0f, - /*offset=*/0.0f, - /*width=*/8.0f, - {}}, - {/*color=*/Color::blue(), - /*blur=*/4.0f, - /*opacity=*/1.0f, - /*gapWidth=*/2.0f, - /*offset=*/-1.0f, - /*width=*/4.0f, - {}}, - {/*color=*/Color(1.f, 0.5f, 0, 0.5f), - /*blur=*/16.0f, - /*opacity=*/1.0f, - /*gapWidth=*/1.0f, - /*offset=*/2.0f, - /*width=*/16.0f, - {}}, - {/*color=*/Color(1.f, 1.f, 0, 0.3f), - /*blur=*/2.0f, - /*opacity=*/1.0f, - /*gapWidth=*/1.0f, - /*offset=*/-2.0f, - /*width=*/2.0f, - {}}, - {/*color=*/Color::black(), - /*blur=*/0.5f, - /*opacity=*/0.5f, - /*gapWidth=*/1.0f, - /*offset=*/0.5f, - /*width=*/0.5f, - {}}, - {/*color=*/Color(1.f, 0, 1.f, 0.2f), - /*blur=*/24.0f, - /*opacity=*/0.5f, - /*gapWidth=*/1.0f, - /*offset=*/-5.0f, - /*width=*/24.0f, - {}}, - }; - for (auto& opt : options) { - opt.geometry.beginCap = style::LineCapType::Round; - opt.geometry.endCap = style::LineCapType::Round; - opt.geometry.joinType = style::LineJoinType::Round; - } - - constexpr auto numPoints = 100; - GeometryCoordinates polyline; - for (auto ipoint{0}; ipoint < numPoints; ++ipoint) { - polyline.emplace_back(ipoint * util::EXTENT / numPoints, - std::sin(ipoint * 2 * M_PI / numPoints) * util::EXTENT / numLines / 2.f); - } - - for (auto index{0}; index < numLines; ++index) { - for (auto& p : polyline) { - p.y += util::EXTENT / numLines; + constexpr auto numLines = 6; + Interface::LineOptions options[numLines]{ + {/*color=*/Color::red(), + /*blur=*/0.0f, + /*opacity=*/1.0f, + /*gapWidth=*/0.0f, + /*offset=*/0.0f, + /*width=*/8.0f, + {}}, + {/*color=*/Color::blue(), + /*blur=*/4.0f, + /*opacity=*/1.0f, + /*gapWidth=*/2.0f, + /*offset=*/-1.0f, + /*width=*/4.0f, + {}}, + {/*color=*/Color(1.f, 0.5f, 0, 0.5f), + /*blur=*/16.0f, + /*opacity=*/1.0f, + /*gapWidth=*/1.0f, + /*offset=*/2.0f, + /*width=*/16.0f, + {}}, + {/*color=*/Color(1.f, 1.f, 0, 0.3f), + /*blur=*/2.0f, + /*opacity=*/1.0f, + /*gapWidth=*/1.0f, + /*offset=*/-2.0f, + /*width=*/2.0f, + {}}, + {/*color=*/Color::black(), + /*blur=*/0.5f, + /*opacity=*/0.5f, + /*gapWidth=*/1.0f, + /*offset=*/0.5f, + /*width=*/0.5f, + {}}, + {/*color=*/Color(1.f, 0, 1.f, 0.2f), + /*blur=*/24.0f, + /*opacity=*/0.5f, + /*gapWidth=*/1.0f, + /*offset=*/-5.0f, + /*width=*/24.0f, + {}}, + }; + for (auto& opt : options) { + opt.geometry.beginCap = style::LineCapType::Round; + opt.geometry.endCap = style::LineCapType::Round; + opt.geometry.joinType = style::LineJoinType::Round; } - // set property values - interface.setLineOptions(options[index]); + constexpr auto numPoints = 100; + GeometryCoordinates polyline; + for (auto ipoint{0}; ipoint < numPoints; ++ipoint) { + polyline.emplace_back(ipoint * util::EXTENT / numPoints, + std::sin(ipoint * 2 * M_PI / numPoints) * util::EXTENT / numLines / 2.f); + } - // add polyline - interface.addPolyline(polyline); + for (auto index{0}; index < numLines; ++index) { + for (auto& p : polyline) { + p.y += util::EXTENT / numLines; + } + + // set property values + interface.setLineOptions(options[index]); + + // add polyline + interface.addPolyline(polyline); + } + } + + // add fill polygon + { + using namespace mbgl; + + GeometryCollection geometry{ + { + // ring 1 + {static_cast(util::EXTENT * 0.1f), static_cast(util::EXTENT * 0.2f)}, + {static_cast(util::EXTENT * 0.5f), static_cast(util::EXTENT * 0.5f)}, + {static_cast(util::EXTENT * 0.7f), static_cast(util::EXTENT * 0.5f)}, + {static_cast(util::EXTENT * 0.5f), static_cast(util::EXTENT * 1.0f)}, + {static_cast(util::EXTENT * 0.0f), static_cast(util::EXTENT * 0.5f)}, + {static_cast(util::EXTENT * 0.1f), static_cast(util::EXTENT * 0.2f)}, + }, + { + // ring 2 + {static_cast(util::EXTENT * 0.1f), static_cast(util::EXTENT * 0.25f)}, + {static_cast(util::EXTENT * 0.15f), static_cast(util::EXTENT * 0.5f)}, + {static_cast(util::EXTENT * 0.25f), static_cast(util::EXTENT * 0.45f)}, + {static_cast(util::EXTENT * 0.1f), static_cast(util::EXTENT * 0.25f)}, + }, + }; + + // set properties + interface.setFillOptions({/*color=*/Color::green(), /*opacity=*/0.5f}); + + // add fill + interface.addFill(geometry); } // finish diff --git a/test/fixtures/custom_drawable_layer/basic/expected.png b/test/fixtures/custom_drawable_layer/basic/expected.png index 4aa9e67a43c171df99c382fcfea7b28f001aa984..10572d2719d907ddd59b9b5278bce5d7df403a2a 100644 GIT binary patch literal 30225 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� zXZqlvBAHcZC|XSn{DlGSm8_xx8d9H`e~1h&Rq_K)_$W>gm7L*x7iu>Zb+)FAPLii> zpP)$jR5t8ZfHRVD&`gwXLQ-m;9}Cwtru{y%?G+6xTwiYeyYw;yfF)a|N>W3N7{9w7 z87jEF5XG(TS=6df;1KK&F?xdf-LHi#7g>3H;m{Sm{_~Tp&RjOT9(1Iw=i}3dTA@eq z%RivM!~-U4{6kv#<(M;E%Sl73%1)3ob=@4jqxGAGw#)JPNc?te@!>ttF12sc9GgP? z1QC-8X7>V+*Ydf-5Jfs?bqgKvjrC5bIRqgJr-hM5QQ}CjfN~&bn>3O>m$5LPST&Lw z!j@!I`?JmYKBYh{4xnQYM#k*7C5Qva6S44MZW;5E^_%4Yv7rjOkSfr8X#L_p4nY_D z9EH3UA3()*GbiT8qlu_7au>bKu+tKjSQ`W}&y|WQ9oUc{{y6Y^^}v1bWL|SHhu;(2 z|G8%MZh7@`g7b%i%-vkfUz>7?chOgB4_1Ug5ma{Te~Y}^6k2&{xQOpPL%BXi!RGYW z?79q}_~+D-u4mP2my0#6k9)sKZvMq{=}Wy(mxl-nRs|7Qk-)5?V+x;`_GK&o>0xKv z&JtDnY%iNcR!^3AOJqy!qRaJt+ru<(oma!P8v~XlgUw)EO(mE@QA=pyv6GRD6Le&q#^9o@NOF!z#j+IHGuHMi|J zZ*zdkS}x1T=S<(_b_<=X|2Lw%`s#1`d~Yqr;_j8d>U-Wa5#^8a%4WA>uJ}m~{Syd= zUR+CZ2CBVy#E_vKocspsycVY%v2R=vnqt|4=llP5C5mu??iN${RJFNq;UvUf3be(#OHGUsuexaJ;!CjPVE&D7rtzC$VwEm~A znBQ{>nS(y7d^`2c1$=(HRBu)!7m2e<_rvGd+ts|ymU{z52YCZ;wL9D>M3vu^qfMQv zsPLEoh?ED1}e%^Fq4lwp2i{d`KL#RQSaA*VtPRtLJ6GUzj9Z1`orD zgI3a{9mN3JTf{#!^XJmXR=|a-Hs=3Y=QuOfj!FZCd1IqHE=}UfuYM&32^O^Q!U94u z<(zYm93O#azBj+vSG{!FSBfg9^HT^dw?cXAm+O?Q<0s z_U&V#!&mdt+`_MM6Abc((EOKfEM)}fED7e8(yx#qLpzcu$aMAIdmT^{I$peq!hZD=(cFY z!NUv!N+Akqa@&9DQo`>r+hL^K^eJUtmIGu+h15r)+ekHh&>m0usJOvhbU0DKns%Xo zNt&As0@t`)`1RMHU><;bGzJ=z9YmsjtwsOE6=s1nY=)BnwI4$oqk^JTQQmR8Z;xg6 zpWL6tUyCyBFZ|;zkVRv^HL?6oEZnmQ%;p9xr2?ENRFDCKe2OQsX50Q13e@T(oE_+x zOWzx|PYthpdC;7&lnm#34{PxJ1IoY=z9&DNIpS z9f;YbvJYKbG5ewZmv3J?T=(nQg567!5mj01-f)*%ahq5So#_C@Ljalum>iS?t^E8Y z$ovA%LbKPik%g1}pQAxng7M59_j0gH{OAiHZNe; z_8KR#qs^*sA$`Of8LMBGi1rj=Z;xCA!RvSA!KZOwXZ#(zeSr{y!W*mHbD((tFeZ-BKR5endV^TKw$Xlz4XeP_e07u#48~ z`JeN0CEBP5{8asl!Q}uneoX;161rT%iWTKGgK>QHD_JLr@-Z4jh`c$pP{tR#Tbe;s z?bwhoG~iXveSRMx1QbN3$nPd0e+Bu=Q=x$Mo-;szXK)jAUVY4S2B=f!=Dc>T)`WY_ zySqHihHIJQvmC#j^;}b~$$Nh8eK-moYrbfJKnvA;=HI}(C)IM8+-f{?rNJggVVt$Y z(3lLr4-`Joyaqwq)RVWS&>TIHNn!wDu4X5)uZ5sLjuY4#9}5 z^7cXxC{Id;$r>YswjNpoYl7YKnaQ{#+3(X|SQZ?B591Y8If0l|)uTIF4oC~1J^^eQ zj~UfE=Sz7i+6P4He}96f+5M)S#Jzi&#g*1SuybpdY7sWP{OI+{Z|10*9AZ_#3E37S zUUU8eGQEAg&wd$($xd;mZQ;={+zH0}QQFYWz20vyUB+9}*$5srmML8~eZV^ShLN_V z`eZORxA35B`0vWSHPXPl#H`7cOOMHeN<_DovrqFl20e<8$l)IYOEoXrsd1ar^fRW> z$*9nVfIM{W3purZ$uJ?sZ;d3WK!*gtR{+x&d5GIW@)NW-n)$|okk9OC>?_p5$(VK3 zEzm*7ZY$zxa=5pXfuXvr^@m_P5Sp3OGjh0>eViSqbxH$cf1p+(IIc9>M-o;4>(8*a zb-Pv!zHV_jSsrPuOBPH?b?qI{K><+M{6_;ATeYs=&$7gU_KSsXU%k=++lMCt!lKbx z_?1OCA(_2t&lPB%Ud%JP-fz-D_$Vnx zMy3koUvqI2BcoQ+BGEG-9N@W-Q6~LbH|kwEe+$nl!jc98Aes`wN~9bugfT#9G>UL2C?PR z=O6Iaj6o3J&eLZCbJZswg(jvpGiD9W569fA^;{q0#WxxeC%;F1S=G~5=a9gO&_?17 zx-c!zi|)am9VMZ1V(~Y?UAwor4LKP*cfyeDlucHjKO`ZT1;|X$6M$WiY4CzDJR1QC z4g@iGsC5CPGCpI8!^or7ll9fBpbt6JLZ)t^$2uz(c6;dX2EL>UFKYXll@QIrUL}!d zt#FlpRBdTm4nT`H)rva;UGZPJ`);$AeBj$4iS|*_?nfx>T{+q z4VP0X_E38eXn+{<+j_jJ#JsVD7c2_zt%zNbjv3jpo$P$ujcxue{4L#vg z-mMmTh9ix13ndkQk2E$XQocsmuecy8W? zT1=@8g;aE5b|~S>d-2eH!GE+ievo%Mes?`m626a}@;1vSxYnOf`TJ!bP_L{XSiatW zV?W5(oH^Q{`#LE4yt(E?p?G2^!28WBj?OlM9BmL&6ul7MVErJ3%`4GPNO9zo+AQ5{ zzYO!+INwyjlqt;XGwoTMfBaN?lp6lL>`Ao4cjd6c78ohBKC@Bk3f2=XWP3pU^{As1 z2I6llS=UltaA6RK2cdC1&q(dfK+)=PqPAv0YvAo`X6sAsa8f^QlStAa5$?&4Nbo2< ztUkmRPfbB%>&trwiqYq`q&H+2jHL^|N7Ozd_?eAwHs3ER=e!G{{Imlu3>FmPmU~F1 zY5-}qGLIlrJ$q9A?=}*mOU^CCSPkQY1Dp!d)F@@oY#3{&HQ?_oV})iuO@v5FwHIA0 zYA$9K#BlAqnos}z^8J_-x+oisL~$&gnt=AIdCKHdpTA#Ckj{@*iSdv=`+#>lq`3|E zlKj?`us(Qy)vcaKX@KHA=fh^=mD>H3qlzuT66cXH6+;>hzR1s}dO&IVODg0>`K7(bn$p0x&X}u-9p&# z=``m2P#6|WXskF$who4Y`k+2);iRw-=x7oTmPpztUkwBS+Qo5EuoRj?{Gu0lD$qd7 zz&lI`{ZKryJB1-=pS&u|^uaszS`p(fZxfG~#($i9Qb1Y>N2wI80d3f@{3_ZIa&d07 z7EC(=oFJ86dZ5@OL$V7OcFw0dx-9~DKb2ad#@X=q)`ra(i>@PH!GdvNkWPtJ(;KFGkcP+_({in37qf@58x$4)s zC_T ztLeiYdz)<1d6_!XAz<^bKhmth+Lj`i2{pqK^b9a1S|K@#kLQ5c=~G94{Bnm(&X^jD@nnqY%=FG}8U#q9hl{2fnYIFMR_3nFiDz zz6+>ZT3YpN(2{`Ag>>jb61>S&A3$0;+Rh+qkRH}w{!sYY4#pzv1_NF@%Nf1T$*(*UFco5`!lZ1H2gR^oqt{XX!vY*IAfh-zmo+y zinl%urbF`y9dHYIc1SwOhgac_dtBZ%4rA;&skxW`_OfB-kH^NbgO^~knyf3SR+w}Z*wCwW3e-+aDLKpH_nA?e*J0s8} z0X9m9G=9GE)Upi*lGcKCm8gOj9deb?+a6H|*`&iRaXbJvMRg%Xrju0obN2>*5Iwxd|hH)Y3FjfCve_R z2zp7@q{vl!!6ks>KTfyDJXTWF6jJWQ0B@v5`yg|&3q=WqGTjJ4QFFvI3kR+SPMPkfbCZOWIP%w6~HGxf8KVC zxC}1idmF=17_$CAM*J+|B|2(iRcRu}Uu9a2y!DSRL?II?=}G9fz4PJjokZ9gd+A ztpa{8Ixl`q^{JQL7gQ96FZ#;INJe6Boi;}88t%NnKRqJ#hjHyU8E(8e;ciY?|LG4M z;aXBX?@8UnhVE5tRcx$KV$<*Qk+w>)`z3+?Dd*n>8R%1p(7Q=CVPKEs%&RdrGmV&+ z;!pO>|9CW%I1f;2PWC_w&&eq~SrRJUEPeukWHn zasinp4KT7RF$qFj0?FU)&@TW5t`ti&VMVZ#u!41(e6x)CW<+$p@|sja?h^aCHlH1Iaec1L~zxrg(;51A6-c2agoq7QjziL^~% z%|1uTDnBT}Kd*=XI_!D(eyVl;OplTk zW=V^X7_xAuy=$TPXQ=YOsGM%uzbFF~?nogx5@$sfdNlQIg3+3;xw>it?k^R(7tYY? zkie}Y$FXTY+vwIB;1Nt61$yZ+^tq++gN^AUW_!2Fh|&W_d&1dlF3^K?UWhijq&?Ui z4FjUuwXsh8;EWaTce<=SioxX5dAGv=yrh!zU}9viEjwUGWt3Q)q(gAwA?8>s1o%iZ zRe+}U~9j%(uvLA4vu;fVk9|811#B+Kxqfl%a(FK)JgQ9ykf5Rw4u@6=a6nU z@^4D^ew0?6D?CaweyA@+y`Zpexm z#BQMJODUE(3K+3E{IB6#4~tVdK+`{;8_ZnE_EB@oI9%=z45Z`%2u;vc!>Yn=96onx z+G~S)=@xqNJOZllFX%p=4i=MFXfPJ~6}T9s@p(Uu!f;hD4MzD8IW$Ng^4w2{dyOY- z-V4&|aD@%C#Y#L~QQmlZrX4Q zFZq5yGr>#fXrgZH#rFmK1V78LsM9x5q>G43T?ZK07ECdQC>TYPo*XO6&w>;7o2y@6{H1jMW@U}K`hBeTrrCMT14;^e zyR51zv!eX`Q8GyZ>L*+_xJl&*R!KVYm%)?3?<$0ErCprfO0tMqzJatdz(UOL9U4Qq*FJ^i zm(qq9>Ousz#Bym5jP^YCLow>dlA4nw6u#<8zYN?tWmB?t4Y&S&gV09fe{^Nx;f`fE zSu(D8{A3~_PKvs4dBp=a%H)FAIU9b0rJF4e{wC_A>YcL*iLAe)LH{+oy=qj}Vv+n7 zvg#NBw@3TRck$cWb0UeCsgugvoQOO~_Q)$Z;U3-!iWni#*SG#(_5i*icAQAJtYk?( zNB&O$2oysPcomnTPS4)Q077Q~P06J)0mrzOukPwr7Lspu?r~l{CN<(_25`gN1Gv&* zM@-YjahGMB9dev##Qx9DGk9oAn&RrJR`tn;qvRQsx8_ZtMy@ta)+*o~9{yDa1^bhT zFE^FjXG&7pUrmwScQ=$CJM*Op`dVMpAe4=FaV{6TqT0W)R?Xb!b@I=*4yGqsV1aw1 zC4tfE%aj#;=3;4zimGHl@{}V2IrIGmTT3QdCX=j>gxN#@CV{sTw8d@vz36Xjd5>Uf zGw1>peRa$oq`tD`t=JSo6E1V3n~gnkaaPIn_3qu`605l zXQh0dWDf7}Jflnc4~PIi(Y@$yGD2rN49*I%?Sejy$jFLwSLXwoILBE(t54pQ`+0rAPB12;kToi z$#IV0IKjeESREh=AaY}DU#o7QVOjc*k5|@~9Qc1VeRW)v&lm3QvcMA3DYb-zbax{m zUD6E#A`Q~8G)R|#ASo>ZDqTx=E2wm*?9#Qs-S6+-`^Wq5e9p`{GiTm&p63}a&}J0T zniV%hm10nGoHAyLUFy|O!WkEt#cs7${?d006B;MnTmKkyN{SdnxCt8M-0Qj#tnJS0 zjeG+Nfz6HRoaM3+8C+h2a6FmVDkvPV(s<|(3-67W%`0)d=oGl!ad$8c)>&o zrHz`QV}wdi>4dnNBt=It-q%1A-N!j2;bV|z!)F9X6@>jt;seygmk>}Z;;3RVX~9{b z8n+`=TzcE^w&xzv2L=&Z%uXgzgHcYt!HrNUB|uJnmkpnrw5fH7QnGr@vZ|DUnRJtd zTI;J9gw(B=J?dnv)GdGDnmjG>t|aD)2$h7Syd$^XyyH&Ej5;-U)1dA|8FbSki1p;2 zt4r%Rmm-WKnlwyiKnpd*5%_AAv(){8pnO0(&G7^N=0lO~yc&WHjIKxZ>MW4$z3Ee~ zE|9|mW)XkI*uWOLilT(A%l(8hQ^Lf6FEEM1^F5V>8TWLBc>&((X;3Bah%8eGPwOgi zM9uM5+VFW;nDHB1R3W=mj^~nzDksaGjG{g$(mLO_LybsBTP*swsna79l17{-^vhOQwil`94l`xKNcVxW+jqg?H z176O$9d0FKR|@sBJc9tAKH36cKNPIRfW?4xtV%q&9ijr5cye3^;3L`av0OzLM>OCD zm`GOi@1)NZ@-x`JV3p%i<~EOg>0Swh2=NklG=|P`;|H<(C7Q@)mQ&^6u=DaGNx;qQ zPBwhfF~&A+Cm;6+sfIjBS2)YBMz`8)JKvpkZUhlg_lwqsup}U|mlBjvCR^IUbY3sU z4k~i5<#FU_0xbw0$&0(6y(N{RXB0F3MY83C$@4rD*Z^9>hy%3c3C(TBHHwUQD&Z54 zKEMgLWneaO#lc&h}~Euq4od;^VjxqOKJAT<|_snFNCu0UuMG zMsNDpNzPV$q4TdCira&id=Z!QONpLsSD%XZ5sa49L zKQTkG_Q&tH`%qcF4{l9bJN7!pJll>Px*G8SDWCzR4Q)fiQx&Cn8^>RaRHfj1!%`g% z@Wd-MMmfhhGxX9$VWDhe_c#x%E!y2V+|8dRBvdD8kE^kkQ|*oa;DBhSHbb3s{rlng zxK~sfX~@Xd+J~;zDb*;yq4U3;DN2FpWT1W&fK%;!!aQ(o`73CINcPD{rw;@!?bGT@ z!bZ42lLLcM$y1UI56DLm?XhD1GiXkCt9I25`_+yAuTPnSak{t%G39$@^~$9^5V_<~ z;WoCN{pp=-e>v540G26H0joQqyzG`Km4@duh!}4ik@FR0H|W{H)xog8%+sZHu>Q9> zRS?{k)v+1m%|0cq#`f2vAbUVG!QZE4OC^})S|IUSK8+zqQCbiN2jT&Aj8mAaTStBw zWPmzf5`By=ay`s6(lTPA12H6#Mv}M$K35cIV|z z2%-zSEhYao=s|uSm1g`BQab0EJ4>4~ z9O=cW=%NjXwrFGx{@F|Jm(;e%IbAm)w(yU-1+xbo%>5pCk%ReEHlaf-x8DDAms;T5 zY~F$^E>u}{-2CO|>lGK7P@x1w)-TbTt5fO|=5&VTh-qFT?ty7vh(+9VR%L=+rTv(| z7lqGe#l_!erb`3%rfa3_hhKH=mDKr97uDTF;B~Ecg>-KpFE^i2I|dK^TYQ91bhbHC69PzuGhi@Vb`7zIrJ$jJ`!8C1KzWW;dz#nNtFL)2a-uN{6Qxa1he#b z^Q2X(tcF!>Nzu%WAHdkr51*3sz>C;_=9IG%!9mophKW&bxDXeM{5$7qRoErcc{<(v z^-y3lsLr{yq2)F$9i7!Kj6<cuVr6dXHyI%mw$p)XaKkDBT5o=K8HB{VsaSp z_Xpwiq@?Ulp8E>i$m$-Gkftg6?&>k7{oQg;t=S`cbg^lp^lap?KM*jj@T3%zopIjc z<(s{CF9u)snnKzZ?lW(E8Gt!*HZ{XBzv@+L-gvhUSW?W=32?Ex%k#aECmklko^#=| zA^H2{ti7DmJ$?BQr8(Q45fDm0Nkb;xmGU#3ZRJZBx-f^})_Wm*m%{~XOxrQFM`hA9U+#Z-( zw)qIxS$3w&URzUlpH1eWZVzk9RHYuSgMtz0o0z7k>zZz9*BTx#>V3PX`a>3@z{h9@24bOutnWE5;%=k@_IWI(k0m|qu`&o zgrcn-vbwZV-nF0tXI^lvz?vqLo-pdvj4OXyBXM>b_bHoTe8FqmT3d6|wa@Lu^`3ag zuj5PG!pRb7%@Djzzm0t9bUSc`zT`%*{lD2Nqtp&i42D;rZj9HFp>kTyyL20W0@aCc z9bR~xQ1cpvCEWEXpsrtqAA*%r=LY-%Ba;K!K2}qX1vhk4$38UOtHrWmGhNsSr-d|a z_hyU18#;;LsZSFBG8dr_D)iQC@Vc@wS+6b1;QI@jo2EsXZEX7M)I$Fa>Co-0#tz$o zmmK(WZb%J&CMQ5Kj4%o0P6<=F6G)|V>rFr3OlgBPdHzmX@ zlpYDDN2g?VoY#1J5IVN07cY~3MoA#IDz;WM4>~+Ucq{KXxZ02A6Twx`&M$oT`LqiU zt`7GNqr|qsQAJKh$y=62L)WU2Q3EfgZE5l1GFiU9+H$!GMoYj*F`!y~2*MODt_41R zJQLv(QO103aGgc1Q5d^8!Ify48@YA*t8nyTK~~t1rA+J0Gt4gLq1%MJp^Y~C zr4(VD$#;o~=gKxYFRx|C4V`L&#bK{+-MmRU`_LS4&qmBha@m8*&S2C@_e+637c?qG zcp-SwZ|NldoFvr@77MEf2A~^`?wyShg&~)8xyH2Or|SFELN1rzmy8C{DQ1$`fa|2@ z`%}C@?XmW#qH1*rveY`4?aSWZoQ}K2oA_`m%ul(|es~c^dXwt3OHAw7SC_BddOjKP zWhCF`uQi3LX<^%R2apmH>1g8~k|IKr z?m+xR4ILWEOihRbDze-gUYa`$^CXe6?A^J|u0<*3d(h?>V=QW-@y$I?>=Q;vw)wZ{ z$E)rs`($7MhCeoh5{iW+AyR;@!lpOAjWrFYMaMh|OS>C^00BPVQVE}vS*ijV;RoC* zR0=Q#$e5%}ZiSt9G`yKw`STyI&}WSj=6urNIva66J$LzhB)YvGO9zDXY2muRCgFUg zxAG@>hfAgVdL=)lFs2|#ZC%SZON zKAyJ*2k=PK`I-q~4Fm!lXU7Eab2ON5Ob&X`0y=~x)HvR|$iBxdtS6z{b6R(nG5DK< zt{Zp9cRKHTf)L!h4r8xI?3sGOi6T;`!-2$lZ?{74yVfbVLrE-q7%nm8?63`eKG@EZ z8ye@;CaOb{nb3#TNr^!eOS zknR!_VV5gN-T9?cvXmkII(L@ihSm4s`W{;D1i*_x7+jsIUpzfQgS_;7Wl!)m3>WN< zcvoT@2|`${EaWwMLG!Lp1%GlLeaCiIg`zn0cid%ma+a66?ym$|{g-|7 zwuUiI!3<%j7!;*qIuTYBqg3TKtm^e3-TDl5=fZNmEzMr7^UWE(?NB!=PA zx{rjDMf^>s+H#JguChyB_+h#x7@dSA(u!=w?sy-x1b0N-rCuoF*72Zv9NYbo;F}DY z`Lgc<@z)tY8`tQ=e+~x~HPU>MHV-zwHZNsRllAZ%NW}6Ebe2W8UOK>>04F}$bjm}^a(e@>= z^C$pkhZuM>m~l8?eMt|%_(=tK*_+!nYYlNLfNFyHGRwC^^o_lUxx@#k*t@Pxt+U~U zpu6F)=M9w8!h7LNy^`!}aX_wP6ELt%Q zW;66rI4t`Dl9R{hJd0HzeP>}}1VaIOF<6MxggQl_N8uAu4sS25k|M)b7I z9cWB>r?NSs%O?0@{`q=gaS*Zq0J+kSSdDD2`+knr3ZVz2$5?irFaJRq?`+6cG-*6UN2{IFf(QDPpww)X%Di^nvSXewHT1uwLJ2 zu0_lP#JU?Iy5zDQkS6mn&pnWbC~Hbz&i_dqc@$BPI~ST#1f z%O))dml?ZMnBFDb(iZeck1z<%W0OAqC~*jv+S<2d;Cn^JgDT9oj{3Y#ZuIs;R%}iM zOv&aiT8T36y%;YRiy8_6KE5OZYKkm=_QU93NK#Cz4`LcbI4?ZlrA)0!uw_i=QLeXy zv1r3-jaGkLw-9Ke5Wl)B9nkcS4{a;ZWSaN)#pWt(Sf?JmrLRt(wP9ozt=49&5at9X zO7#p-x^Hvn&r*Q^K~?_849sMMtQ=Tw#Kt*5bkkyUp6X1Lk}>{+kkXOM*O%uhC)Bpv z_7sAh(lOp${5YWAjlT3!2rdu$O3Lr^zTU~}G)g-PCEqt|LkfByHJt*j1=S~v28MZP z!I(V0GcA6_hj!7_xBabdUku2V3wHmiURjIkQ%hZmvNE>WI046_%XaGrcIlJ2FM_ue zNK}s;z5$>!uKUw!&?M};WP+VpSjB_p|}x>cVeU>Q&_p7crx|Ii%F&b{40kH5_+}bkXDkM7a2m9qcE4f?O-dyU6JCTH#rN+Wu#H!b2 z1lC{Iqo^h$*pM`zK<}-~)m{H3Gk``8;1xm!Y5;s}m4fIf17xN-VF5wiY~$uJ3Ch>CRgVaUWdd3PADdnYg9&k9J<>A;y6k^F2t` zZ}-Eu%y$fHVxgfVFuT1ue5h9mda-h>5|uIgV#7Gp@mKxr8o!7tTM1@9Es=cQmS_8c zkoc=Cf!LDu_RBy6wvYCGTlKivO0U!PJ`gPrWI+wwc) zY2dE-hsPYi@ME1P$tEcgkdgkI7twc=jv!#HDT}t5!WSIpko`BTeh!y~T{kRckb|m3 zNsUj6Y74?w@+~^Q1N(ig&V;`oR=)Re7rdC_vxiW>DaH`d18d zUi`D+_UoP09t={}c`rT?7K($4!cl;YhXU#5F(t-Rpp!2qb1{b7#3C+O+HH(Tlt`BSS+J5%OfIb8v9E#vbh!#v)f>LEDMT z&z?(X`((6~7P!c!&&mPem5Bc(cq72k6N?4F2<8O8#!Y1QvG!>LsO-&z!sFoX^!8UE z0py9$DPPbr$0&3_w-r=QAs15Og6 z1q%a{p5FITUakxWclvMQctx!Vdr4r_GCEE)x!{E4e0m5goAbtgD+>=mdL(Z7r5Gr^ z*$M~ICvMt_xl@aeNYich9rWGS`4P+J{((6wVezr!SNisRZbqvOqZOXebI9a6N%?+D zNa9Nb`Z9!dstB$p;iP!m|FZVlW{S^h@j98y4*y``)*c|Rn(gK@EK#2=| zvw6}N#JhFOj`WdQ$J>ew@{=pg>o7!V? ze; z!K8pO1jO`mWE2^jcr02#uX9qsS$S9;UlQz8N|VSYQbNOs8o z40~5@w0Q?IjT2?9&;mp&8f!vU;6qzm*nEM|B})!m2%U~TAB>h?u56t#zVK}qDPY`H z4wceRKX~WfpgoUa0jRfj!pc_$qit$*wzIY3da335xF6*^nB&em#0eh;_D1XupAf7t zs^A~9v03ru^m!&0AWdd~G&3h%=iQo4<7 z%T(g$U-awvtD&~9P5h(0?EP`s<=SAhyrp<*;i-ncPBG%2ys~csQ@yfT3yYiN{b)7v z20Csj$!7R3AF#wH1o#^%r~~Z}yHg_9jA^&>*cXq6r11q{+&GNiC9zDz_L_a{-w*=E zl1Ak-NlJ$yAs{|ISO)+NXJnKD`9qKd1%?QDBEGhI)BF2x@V8z&9CA>;&@SaP|1!Lq zpN{UF5zM41vu24sc1{G1S9_%ucwSII;5t4pQ|Wuc0SQ9Rv|N`|#*aGo8DX#Z6OT6q z_Z?)ySGG&{Xv8_#F|@|8DF{ARh&w$GzracrlpC;-^cqcJgw&rSiRc;GxQIUJ@8ClL z5#zIhastE~P_CA9rVo;ZYO|n4@+OXmLxDwJ@{<{`h@M_BTM$nJ+rzXzM+uW1Z?dIeA!g!t)sw3`l!BRfb``%wP1`ZQr#b*+D5xlo#*Gg-O;a8aRHDR(kh>+*?YSsyGai5gm6WXAlTG^E}oAn|( z=i+}g$#gFucG+6;BnmhN?61fxmA$|UB)2@6J{Zcs=yq1o*N{zrlyGs0MllYFufp!> zwT{hU)ELX>)2n;4wIGENo+UpLE&7uhkT-Y-hl_Figl+v?JnAtAg}KIjv0@r7iW#G$ zZKHooq`OD!S$P9;$L(N4mH}SvOhhR&QV^JC$TWxi3TKLo?a;1#mka?2E9^>=huru# zS+e57r?yh>xx+D|-dVEGdk#O+i}!!x3*<_17-u)?Q?8!l;n9X=uB0T87x zfGtV8VbSjWS^gA%|iuT{uG8<5wgYZ^GW%M#9!%0z^LeL6;J552`F zQr7xeOH^T^;>=H|=aWQbA@I5_BHuDN9taO}Mc0@G$tC_Oluo=C;P)sR9)=Mt@<42? zIdsY&ac+pYPBX2z6!Dd^02#f3h#g=>d}9XTfi205xslQEV~$)84f4pka(iL@W@8q7A5`rpCxO{19jYqRx*&HXwinn_lPY=FBQ#bKbi?b!afAHLqZRKLC7 zslXK{tpBslR&ewn%?|;j%5$FmhRSXPu&k$kF{`#@4v+m!Gt$N&xMmU;BmPrxlocu| z1z;hj#0~CGrW!Y@3)4I$~s9tD|bBcV`(m_!A0Ut zJM)J_q5IHJQH_{tc7NW{ePTC)1b&>{uJWPQE=tX(oK7fMf5-H}on_d>!uUXOA6q8p z68uwyR`5wPgv)M-My_j({NieXKX{cyR02XdI2z6ka(k%e`yZ1frKE z;nOslZ%dOKT)|={VZv^Mwbbx~(W9hej|2u~11`JAUfW^D#N;;w*q>8!20QPIj-g)D zhr%4gi(jNZK6%#T>@%+faGVYlN+Uk&k{S!Ebd+r%vb`AHIa)52{4-7}uU%G!NX} zVv~P===%%vO;_p5{7D*C1ixX7MM7o3*5<=I2tB!?A_Yc_=n$QL%4mq{P_WvPGDjH1PS3C_ibLBh)rHKL_XVWbB%l z$H^7{kcy!Y_mH))MXXVS7PmA+Pk6~&RvhoP5EahL!u#mKqUV)FM+nkCkh8OS#D$;o z&^|>ECwOI|MK`8Rm4CLRsp84+^;fs(##cTXB)?S7XB_;%TUvEVP#~|0$7rO#L7K7A zzn7Y^B+0wgf9-L>Oe`x71ndj{(LDUBR)_Po_B+E2A!#Mlqb2-34=wTk9bEpu!M!@; zT79aH+!Gq#zJG2_XAuYLh_vp9Z+uTVU|-xCBm110nZuhJlN)6R=D`;?2}#^#ZjH5( zMpqALZyK{=UFy^bIT20AAJ3s5oICI1k{u3bYH!&wvYyb=VLxxTJ|-QSW?FxSe=9|& zyQl2ZDkQF11@P&PWrs_`&;$S&70WT5aqw}?%v$)Mq8O| z-uI>r-Z)S75J&Zmm~4Eb6;H5MBzl>~jP;-V#$U`KU&&!}b1CJj)59$A-+bPPgy1_rGrx@Uk86fx9dQJ+ z^CXq7Mac@w;NougICdf7(fH{oQ*$Ei|j2x|G@))#R3_<4Z<5iaV4lnfsj=;6PWCONjyL;^JZ zk??Be-+a5BAFrroh9zl7d4!rz#?}Ruy|+hx_$^NFQtXPm^d&{C>A}(uF@sHWd9nAD zv&J%dOC>M2&dY@d^!C*jx6r|1qCBs+|<)^_kB)ax*#bp+a?Ak|Lb2Xa3(B?85 zX$@WG$NjkHmM%)k zLTl|K5bQ|$8#=((Pnkzz?N@)D&`y!oUwfROaaxU{YKW$DsI$c`>-R;(*xb1(5_Qf|>)bDm1l-@NPXJ~@;d`;zsH zB0#`NV?xSFaN=P~a_-tge<9IxcEG8N3%a76UAEvR4MC8%x7W6_N)Ka}I49J`yOEGZ zNxvw{Rq&3c;K_fAxzjZ9WbeM9w_Gk*BSYMXN^6TwD8gt)8pS@bdAM)a1qp!BO#oc)rnDcoo~(P zhy{nAoS3r&s`NYGv0c4ivtH|)1{MWhQZ#CHNKM&~$wQY1y|Q89Xz37QoV4Y!4N3Ic zN~i)Fx+K3|F?a5-^wG_`bxOVyUG{hVg+5Ma%)Bg1T)y?vxbjZ-z@4!q24QpN(|68w zshlJg@r6yo;x6+NyFX*hQomPTaDX&1e?W!G&E@$9*2KKPKy?cR{59(TRA<7?@S)&9 z7RD#O0JA^)u3by|5YvSE9*Gf{I$ep0ETL#7z(WXVoC@GiLRdZ6ik*b7hZD&zph+Ft z5OPwvam3_WP})l3t__-O-*WMH^w6GG)g28&L)N!Ija()h*W60-zzCM{ ziZFvZSZ93~AkE zDve4lRx+HIHD%O%iq@@b6R=$1IQkm{yMw>8%>&q<0$)aE&!(MfG>o4`S-@Wihsm~) z{!spF>RG`1ui+C$$Ig&J;U!;q-CZPEUx3pwcc5<1YUy-XW!1wbDe!?G6ICm6@K8-4 zLlY3SB8m(SoKhuR`xDH3zFQ{Hrm}`}^Q;Oi4U(~Hgzw{4aH5UwI~x~$kpUORuTW<} z7|62hC4^M3Jqk)cb0^Q!WmcDCMoNu5>*FmGl!Q@VugLQXTA!sdvCJ zqrwHg=~lcc344chtMf<6VeFNbw{0>Iq$J^A-}0v>px+4y>-PO^HUXt`-SrH{-z~05 z*;zU5X1}I-Ew$$`AazZA>r8OVK(W^RH)nj4Ch*H)?#7TR1C4*&`Iqe}wdKZk?akbD zQhx;*-mD@OIAt}3IJbz!a)p1s>_YEPR}#Eg(r>&Ib14^IBa*XIhBHJwA85ZL8<;VW zuRXrnE6TM31S?1Jr$v#kiBw1k4m$xR?Q^W+)xRr=gIa9lRli(3{3)_>dWqRVJZ&K@ zS2rgidCB%{VaK|`4rM;Jz3S9 z24{vVL_AqTX#{e$3#rVh6#cB zu%VdbJFE+)v!{St{{ip8+3e%%)M|z|80;EF*q@oPBt8ZuqNwI+R>1|9RiW-kvwsX# z(-$iJS2HVqNMPvE-@*(DW@-H(5QBaoU~2C;oNZ72KPyqDzkJKd%d>>rRtzqFIKJD*wt_samH*k93)+=UPdtwtF65@WBqAVB#q-k-{I2e%I0E0!4yQ9z?u9!PC2CVWeV$`H@w(;Cl?|qMXLqVyVwpPJ1`Q zVfEe@ci5|nvZb)DUow`_>oTh&S5G>P7w%NG_d-59_9esbGU?%h=+KSTi%CVh->Axs zuU2H)xHvDOx}a|}pv#GhbC%(pxx>siY3nvDca#S74I>AHAO0GK-;<5UMLsx+zoFV| zGO*yd(N)nHs1wuv5ckPE4W~%7{$Bi-oj51_m~V6Cu@;1mA>-jA3QHok3^vV&RGhXxBTogrh-Xd*;0 z>>*v|Fls)*O2NqLFKb5|HFHB(_zpsDOmJG;B^|y-W!Y4(;t19RGV7}Czt|Vg{hVF9 z0=|Dv`hI%M$4=U$y27_{7!Nq2ykr>iAN|pX9q==BgvQ}P?Nm9p<(Xevk8BQqFvH2A z#4Z1?I;`$rgGXIdC%~@Q0=i%rLve~lVEJX@sZ1H#?wMtbsXfi<8UO=-t?=@(j|$;3 zegA)JOePe&N-?M7X0TjjS{A7Kjqyt=I&v{yL%1l7pI?s`jp93@W7c}JJ`G4~Be#ha zQJM%${7ARy^X2ZwiG%XF)3>M@hi_e1n2Gi_k*b;4+EtdXY|a5?Adv)=6A3_^_*VI} z%ikZN{WoZc_33c(v=klUQ@})ztQ$U3@Vxdi#qhfU)maL$>{KWxP2kZQX)wb+j`V-A zTzo}Pp0{m5(x_>UzE+&-@N2c@?r z>i!~8_8ds}cJQ_$LRp=br#$5}3J4Q6nbISeEWB|n#$Wo~pd{lD9gJ{&vBubku@a@ARVr*S2b zB$65yDy1S6|CYc>bGg`*vy~yWPS_E*-9fv}#OobEP%Z}UWmD#JMNmBsr|GH7r_Q%> z1gMB1aiqhCP&P{%u-1OIoYyY{7G`iRbF3PpMCmBaARSA183qCYJ=kL$YbPJ1%Ib)zs-2-L&GBaVwLjfm3|nin0nF$^bl|H-}!v5 zk150a|04$-FlqEv(sQ>tvlvXuSCaZ{$SW~p94GRA;x&0HxYL9SLZ{PpAYe_I>94K<(eM|5{?PHb?7KLR$QR2e@b6+8iU63G>ZnuhY@0O|z3g{ks*>P*S9 ziRZ~zKmL$^Ogx!7+BINB}0MhO=+j-o%#=nq% z$Hk65Dewh(Rl_m__J&fq z9OdlQp#H7&RPG@h_`}jG25B^6(u0M8pVC=-+kBo}9!c@_iNok>S^gdDXxoq@R;I9V z)du%&=)`mr7iX4srkqC=q!pn35m-1Y zn01x5Km!0WZ_%$+W39M`jEa(kgfYHmUZE;sydIBS7(KFfeXbroBlKLHGY;(s>-gMo zQ4k|4ZZPq7?ukJ}Sh~FS8vp(iKdMi!C6?pEHqGtAHhaE2WzONg)3>DB7|v{}A!ZSd zFv7xH9>S7@Db`t+0}g&tGr5)9~= zDeH1@YDL$Y|E&5^p=geF{r+8G-K?lz-{ryuL(xe~-!SK5Xt02xH^J@_P&gwK!BEVA zBgbExqWcSD3w_9nFtW8A%?>ek3I6~h1k literal 30223 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