diff --git a/examples/quick/shapes/interactive.qml b/examples/quick/shapes/interactive.qml index f986eaff43..0525f4eed3 100644 --- a/examples/quick/shapes/interactive.qml +++ b/examples/quick/shapes/interactive.qml @@ -56,7 +56,7 @@ Rectangle { property Component shapeType: Component { ShapePath { id: quadShapePath - strokeColor: root.palette.windowText + strokeColor: strokeSwitch.checked ? root.palette.windowText : "transparent" strokeWidth: widthSlider.value fillColor: fillSwitch.checked ? "green" : "transparent" PathQuad { @@ -83,7 +83,7 @@ Rectangle { property Component shapeType: Component { ShapePath { id: cubicShapePath - strokeColor: root.palette.windowText + strokeColor: strokeSwitch.checked ? root.palette.windowText : "transparent" strokeWidth: widthSlider.value fillColor: fillSwitch.checked ? "green" : "transparent" PathCubic { @@ -109,6 +109,15 @@ Rectangle { } } } + ToolButton { + id: modifyButton + text: qsTr("Modify") + checkable: true + onCheckedChanged: { + if (checked) + showHandlesSwitch.checked = true; + } + } } Label { @@ -130,6 +139,12 @@ Rectangle { id: fillSwitch text: qsTr("Fill") } + + Switch { + id: strokeSwitch + text: qsTr("Stroke") + checked: true + } } Component { @@ -144,9 +159,11 @@ Rectangle { width: 20 height: width + radius: halfWidth visible: showHandlesSwitch.checked color: hh.hovered ? "yellow" : idleColor border.color: "grey" + opacity: 0.75 property real halfWidth: width / 2 property bool complete: false @@ -203,14 +220,16 @@ Rectangle { property ShapePath activePath: null onActiveChanged: { const tool = toolButtons.checkedButton; - if (active) { - activePath = tool.shapeType.createObject(root, { - startX: centroid.position.x, startY: centroid.position.y - }); - shape.data.push(activePath); - } else { - activePath.finishCreation(); - activePath = null; + if (tool != modifyButton) { + if (active) { + activePath = tool.shapeType.createObject(root, { + startX: centroid.position.x, startY: centroid.position.y + }); + shape.data.push(activePath); + } else { + activePath.finishCreation(); + activePath = null; + } } } onCentroidChanged: if (activePath) { diff --git a/examples/quick/shapes/shapegallery.qml b/examples/quick/shapes/shapegallery.qml index 34056a0ccd..b74c0b9631 100644 --- a/examples/quick/shapes/shapegallery.qml +++ b/examples/quick/shapes/shapegallery.qml @@ -159,8 +159,8 @@ Rectangle { } color: "darkBlue" font.pointSize: 12 - readonly property variant rendererStrings: [ qsTr("Unknown"), qsTr("Generic (QtGui triangulator)"), qsTr("GL_NV_path_rendering"), qsTr("Software (QPainter)") ] - text: qsTr("Active Shape backend: ") + rendererStrings[dummyShape.rendererType] + readonly property variant rendererStrings: [ qsTr("Unknown"), qsTr("Generic (QtGui triangulator)"), qsTr("GL_NV_path_rendering"), qsTr("Software (QPainter)"), qsTr("Curve Renderer") ] + text: "Active Shape backend: " + rendererStrings[dummyShape.rendererType] SequentialAnimation on opacity { NumberAnimation { from: 1 diff --git a/src/quick/util/qquickpath.cpp b/src/quick/util/qquickpath.cpp index 7562b74d46..53c6ddf93e 100644 --- a/src/quick/util/qquickpath.cpp +++ b/src/quick/util/qquickpath.cpp @@ -372,6 +372,9 @@ void QQuickPath::processPath() d->_path = createPath(QPointF(), QPointF(), d->_attributes, d->pathLength, d->_attributePoints, &d->closed); } + if (d->simplified) + d->_path = d->_path.simplified(); + emit changed(); } @@ -712,6 +715,32 @@ void QQuickPath::invalidateSequentialHistory() const d->prevBez.isValid = false; } +/*! \qmlproperty bool QtQuick::Path::simplified + \since 6.6 + + When set to true, the path will be simplified. This implies merging all subpaths that intersect, + creating a path where there are no self-intersections. Consecutive parallel lines will also be + merged. The simplified path is intended to be used with ShapePath.OddEvenFill. Bezier curves may + be flattened to line segments due to numerical instability of doing bezier curve intersections. +*/ +void QQuickPath::setSimplified(bool simplified) +{ + Q_D(QQuickPath); + if (d->simplified == simplified) + return; + + d->simplified = simplified; + processPath(); + + emit simplifiedChanged(); +} + +bool QQuickPath::simplified() const +{ + Q_D(const QQuickPath); + return d->simplified; +} + /*! \qmlproperty size QtQuick::Path::scale diff --git a/src/quick/util/qquickpath_p.h b/src/quick/util/qquickpath_p.h index 9333805389..2ef008fab2 100644 --- a/src/quick/util/qquickpath_p.h +++ b/src/quick/util/qquickpath_p.h @@ -487,6 +487,7 @@ class Q_QUICK_PRIVATE_EXPORT QQuickPath : public QObject, public QQmlParserStatu Q_PROPERTY(qreal startX READ startX WRITE setStartX NOTIFY startXChanged) Q_PROPERTY(qreal startY READ startY WRITE setStartY NOTIFY startYChanged) Q_PROPERTY(bool closed READ isClosed NOTIFY changed) + Q_PROPERTY(bool simplified READ simplified WRITE setSimplified NOTIFY simplifiedChanged REVISION(6, 6)) Q_PROPERTY(QSizeF scale READ scale WRITE setScale NOTIFY scaleChanged REVISION(2, 14)) Q_CLASSINFO("DefaultProperty", "pathElements") QML_NAMED_ELEMENT(Path) @@ -517,10 +518,14 @@ public: QSizeF scale() const; void setScale(const QSizeF &scale); + bool simplified() const; + void setSimplified(bool simplified); + Q_SIGNALS: void changed(); void startXChanged(); void startYChanged(); + Q_REVISION(6, 6) void simplifiedChanged(); Q_REVISION(2, 14) void scaleChanged(); protected: diff --git a/src/quick/util/qquickpath_p_p.h b/src/quick/util/qquickpath_p_p.h index 36067df5dd..6dbab4ea57 100644 --- a/src/quick/util/qquickpath_p_p.h +++ b/src/quick/util/qquickpath_p_p.h @@ -53,6 +53,7 @@ public: bool closed = false; bool componentComplete = true; bool isShapePath = false; + bool simplified = false; }; QT_END_NAMESPACE diff --git a/src/quickshapes/CMakeLists.txt b/src/quickshapes/CMakeLists.txt index 4448a63e9c..2644d8f154 100644 --- a/src/quickshapes/CMakeLists.txt +++ b/src/quickshapes/CMakeLists.txt @@ -20,12 +20,16 @@ qt_internal_add_qml_module(QuickShapesPrivate qquickshape_p_p.h qquickshapegenericrenderer.cpp qquickshapegenericrenderer_p.h qquickshapesglobal.h qquickshapesglobal_p.h + qquickshapecurverenderer.cpp qquickshapecurverenderer_p.h qquickshapecurverenderer_p_p.h + qt_delaunay_triangulator.cpp + qt_quadratic_bezier.cpp qquickshapesoftwarerenderer.cpp qquickshapesoftwarerenderer_p.h PUBLIC_LIBRARIES Qt::Core Qt::GuiPrivate Qt::Qml Qt::QuickPrivate + Qt::ShaderTools GENERATE_CPP_EXPORTS GENERATE_PRIVATE_CPP_EXPORTS ) @@ -51,4 +55,120 @@ qt_internal_add_shaders(QuickShapesPrivate "qtquickshapes_shaders" "shaders_ng/radialgradient.frag" "shaders_ng/conicalgradient.vert" "shaders_ng/conicalgradient.frag" + "shaders_ng/shapecurve.frag" + "shaders_ng/shapecurve.vert" + "shaders_ng/wireframe.frag" + "shaders_ng/wireframe.vert" +) + +qt_internal_add_shaders(QuickShapesPrivate "shaders_stroke" + BATCHABLE + PRECOMPILE + OPTIMIZED + DEFINES "STROKE" + PREFIX + "/qt-project.org/shapes" + FILES + "shaders_ng/shapecurve.frag" + "shaders_ng/shapecurve.vert" + OUTPUTS + "shaders_ng/shapecurve_stroke.frag.qsb" + "shaders_ng/shapecurve_stroke.vert.qsb" +) + +qt_internal_add_shaders(QuickShapesPrivate "shaders_lg" + BATCHABLE + PRECOMPILE + OPTIMIZED + DEFINES "LINEARGRADIENT" + PREFIX + "/qt-project.org/shapes" + FILES + "shaders_ng/shapecurve.frag" + "shaders_ng/shapecurve.vert" + OUTPUTS + "shaders_ng/shapecurve_lg.frag.qsb" + "shaders_ng/shapecurve_lg.vert.qsb" +) + +qt_internal_add_shaders(QuickShapesPrivate "shaders_lg_stroke" + BATCHABLE + PRECOMPILE + OPTIMIZED + DEFINES + "LINEARGRADIENT" + "STROKE" + PREFIX + "/qt-project.org/shapes" + FILES + "shaders_ng/shapecurve.frag" + "shaders_ng/shapecurve.vert" + OUTPUTS + "shaders_ng/shapecurve_lg_stroke.frag.qsb" + "shaders_ng/shapecurve_lg_stroke.vert.qsb" +) + +qt_internal_add_shaders(QuickShapesPrivate "shaders_rg" + BATCHABLE + PRECOMPILE + OPTIMIZED + DEFINES "RADIALGRADIENT" + PREFIX + "/qt-project.org/shapes" + FILES + "shaders_ng/shapecurve.frag" + "shaders_ng/shapecurve.vert" + OUTPUTS + "shaders_ng/shapecurve_rg.frag.qsb" + "shaders_ng/shapecurve_rg.vert.qsb" +) + +qt_internal_add_shaders(QuickShapesPrivate "shaders_rg_stroke" + BATCHABLE + PRECOMPILE + OPTIMIZED + DEFINES + "RADIALGRADIENT" + "STROKE" + PREFIX + "/qt-project.org/shapes" + FILES + "shaders_ng/shapecurve.frag" + "shaders_ng/shapecurve.vert" + OUTPUTS + "shaders_ng/shapecurve_rg_stroke.frag.qsb" + "shaders_ng/shapecurve_rg_stroke.vert.qsb" +) + +qt_internal_add_shaders(QuickShapesPrivate "shaders_cg" + BATCHABLE + PRECOMPILE + OPTIMIZED + DEFINES + "CONICALGRADIENT" + PREFIX + "/qt-project.org/shapes" + FILES + "shaders_ng/shapecurve.frag" + "shaders_ng/shapecurve.vert" + OUTPUTS + "shaders_ng/shapecurve_cg.frag.qsb" + "shaders_ng/shapecurve_cg.vert.qsb" +) + +qt_internal_add_shaders(QuickShapesPrivate "shaders_cg_stroke" + BATCHABLE + PRECOMPILE + OPTIMIZED + DEFINES + "CONICALGRADIENT" + "STROKE" + PREFIX + "/qt-project.org/shapes" + FILES + "shaders_ng/shapecurve.frag" + "shaders_ng/shapecurve.vert" + OUTPUTS + "shaders_ng/shapecurve_cg_stroke.frag.qsb" + "shaders_ng/shapecurve_cg_stroke.vert.qsb" ) diff --git a/src/quickshapes/qquickshape.cpp b/src/quickshapes/qquickshape.cpp index 551376778a..a33381570a 100644 --- a/src/quickshapes/qquickshape.cpp +++ b/src/quickshapes/qquickshape.cpp @@ -5,6 +5,7 @@ #include "qquickshape_p_p.h" #include "qquickshapegenericrenderer_p.h" #include "qquickshapesoftwarerenderer_p.h" +#include "qquickshapecurverenderer_p.h" #include #include #include @@ -639,6 +640,7 @@ void QQuickShapePrivate::_q_shapePathChanged() Q_Q(QQuickShape); spChanged = true; q->polish(); + emit q->boundingRectChanged(); } void QQuickShapePrivate::setStatus(QQuickShape::Status newStatus) @@ -678,6 +680,45 @@ QQuickShape::~QQuickShape() Pure QPainter drawing using the raster paint engine. This is the default, and only, option when the Qt Quick scenegraph is running with the \c software backend. + + \value Shape.CurveRenderer + Added as technology preview in Qt 6.6. + Experimental renderer which triangulates the polygonal internal hull of the shape, + similar to \c Shape.GeometryRenderer. But instead of also triangulating curved areas, + this renderer renders curved areas using a specialized fragment shader. This means that + the shape does not need to be re-tesselated when it changes size or is zoomed. For + supported shapes, this can give improved runtime performance in cases where the shapes + are repeatedly transformed. + + By default, \c Shape.GeometryRenderer will be selected unless the Qt Quick scenegraph is running + with the \c software backend. In that case, \c Shape.SoftwareRenderer will be used. + + \c Shape.CurveRenderer can be optionally selected using the \l preferredRendererType property. + + In addition to rendering smooth curves regardless of zoom level, this renderer applies + anti-aliasing without enabling MSAA on the surface, which may provide performance gain. + + Note that \c Shape.CurveRenderer is currently regarded as experimental and has several + limitations: + \list 1 + \li The \c GL_OES_standard_derivatives extension to OpenGL is required when the OpenGL + RHI backend is in use (this is available by default on OpenGL ES 3 and later, but + optional in OpenGL ES 2). + \li Only quadratic curves are supported (cubic curves will be approximated by quadratic + curves). + \li Shapes where elements intersect are not supported. Use the \l Path.simplified + property to remove self-intersections from such shapes. + \li Shapes that span a large numerical range, such as a long string of text, may have + issues. Consider splitting these shapes into multiple ones, for instance by making + a \l PathText for each individual word. + + Due to the fact that the \c Shape.CurveRenderer approximates cubic curves, there are certain + shapes it will not render accurately. For instance, circular arcs are not representable using quadratic + curves and will only look approximately correct. If the visual representation is + insufficient for a particular shape, consider using \c Shape.GeometryRenderer instead. + + \note The \c Shape.CurveRenderer is currently considered a tech preview, thus the name of + this enum may change in future versions of Qt and some shapes may render incorrectly. */ QQuickShape::RendererType QQuickShape::rendererType() const @@ -686,6 +727,56 @@ QQuickShape::RendererType QQuickShape::rendererType() const return d->rendererType; } +/*! + \qmlproperty enumeration QtQuick.Shapes::Shape::preferredRendererType + \since 6.6 + + Requests a specific backend to use for rendering the shape. The possible values are the same as + for \l rendererType. The default is Shape.UnknownRenderer, indicating no particular preference. + + If the requested renderer type is not supported for the current Qt Quick backend, the default + renderer for that backend will be used instead. This will be reflected in the \l rendererType + when the backend is initialized. + + \l Shape.SoftwareRenderer can currently not be selected without running the scenegraph with + the \c software backend, in which case it will be selected regardless of the + \c preferredRendererType. + + \note This API is considered tech preview and may change or be removed in future versions of + Qt. + + See \l rendererType for more information on the implications. +*/ + +QQuickShape::RendererType QQuickShape::preferredRendererType() const +{ + Q_D(const QQuickShape); + return d->preferredType; +} + +void QQuickShape::setPreferredRendererType(QQuickShape::RendererType preferredType) +{ + Q_D(QQuickShape); + if (d->preferredType == preferredType) + return; + + d->preferredType = preferredType; + // (could bail out here if selectRenderType shows no change?) + + for (int i = 0; i < d->sp.size(); ++i) { + QQuickShapePath *p = d->sp[i]; + QQuickShapePathPrivate *pp = QQuickShapePathPrivate::get(p); + pp->dirty |= QQuickShapePathPrivate::DirtyAll; + } + d->spChanged = true; + d->_q_shapePathChanged(); + polish(); + update(); + + emit preferredRendererTypeChanged(); +} + + /*! \qmlproperty bool QtQuick.Shapes::Shape::asynchronous @@ -719,6 +810,23 @@ void QQuickShape::setAsynchronous(bool async) } } +/*! + \qmlproperty rect QtQuick.Shapes::Shape::boundingRect + \since 6.6 + + Contains the united bounding rect of all sub paths in the shape. + */ +QRectF QQuickShape::boundingRect() const +{ + Q_D(const QQuickShape); + QRectF brect; + for (QQuickShapePath *path : d->sp) { + brect = brect.united(path->path().boundingRect()); + } + + return brect; +} + /*! \qmlproperty bool QtQuick.Shapes::Shape::vendorExtensionsEnabled @@ -902,6 +1010,12 @@ void QQuickShape::updatePolish() d->spChanged = false; d->effectRefCount = currentEffectRefCount; + QQuickShape::RendererType expectedRenderer = d->selectRendererType(); + if (d->rendererType != expectedRenderer) { + delete d->renderer; + d->renderer = nullptr; + } + if (!d->renderer) { d->createRenderer(); if (!d->renderer) @@ -936,36 +1050,73 @@ QSGNode *QQuickShape::updatePaintNode(QSGNode *node, UpdatePaintNodeData *) { // Called on the render thread, with the gui thread blocked. We can now // safely access gui thread data. - Q_D(QQuickShape); - if (d->renderer) { - if (!node) + + if (d->renderer || d->rendererChanged) { + if (!node || d->rendererChanged) { + d->rendererChanged = false; + delete node; node = d->createNode(); + } d->renderer->updateNode(); } return node; } +QQuickShape::RendererType QQuickShapePrivate::selectRendererType() +{ + QQuickShape::RendererType res = QQuickShape::UnknownRenderer; + Q_Q(QQuickShape); + QSGRendererInterface *ri = q->window()->rendererInterface(); + if (!ri) + return res; + + static const bool environmentPreferCurve = + qEnvironmentVariable("QT_QUICKSHAPES_BACKEND").toLower() == QLatin1String("curve"); + + switch (ri->graphicsApi()) { + case QSGRendererInterface::Software: + res = QQuickShape::SoftwareRenderer; + break; + default: + if (QSGRendererInterface::isApiRhiBased(ri->graphicsApi())) { + if (preferredType == QQuickShape::CurveRenderer || environmentPreferCurve) { + res = QQuickShape::CurveRenderer; + } else { + res = QQuickShape::GeometryRenderer; + } + } else { + qWarning("No path backend for this graphics API yet"); + } + break; + } + + return res; +} + // the renderer object lives on the gui thread void QQuickShapePrivate::createRenderer() { Q_Q(QQuickShape); - QSGRendererInterface *ri = q->window()->rendererInterface(); - if (!ri) + QQuickShape::RendererType selectedType = selectRendererType(); + if (selectedType == QQuickShape::UnknownRenderer) return; - switch (ri->graphicsApi()) { - case QSGRendererInterface::Software: - rendererType = QQuickShape::SoftwareRenderer; + rendererType = selectedType; + rendererChanged = true; + + switch (selectedType) { + case QQuickShape::SoftwareRenderer: renderer = new QQuickShapeSoftwareRenderer; break; + case QQuickShape::GeometryRenderer: + renderer = new QQuickShapeGenericRenderer(q); + break; + case QQuickShape::CurveRenderer: + renderer = new QQuickShapeCurveRenderer(q); + break; default: - if (QSGRendererInterface::isApiRhiBased(ri->graphicsApi())) { - rendererType = QQuickShape::GeometryRenderer; - renderer = new QQuickShapeGenericRenderer(q); - } else { - qWarning("No path backend for this graphics API yet"); - } + Q_UNREACHABLE(); break; } } @@ -989,9 +1140,15 @@ QSGNode *QQuickShapePrivate::createNode() break; default: if (QSGRendererInterface::isApiRhiBased(ri->graphicsApi())) { - node = new QQuickShapeGenericNode; - static_cast(renderer)->setRootNode( - static_cast(node)); + if (rendererType == QQuickShape::CurveRenderer) { + node = new QQuickShapeCurveNode; + static_cast(renderer)->setRootNode( + static_cast(node)); + } else { + node = new QQuickShapeGenericNode; + static_cast(renderer)->setRootNode( + static_cast(node)); + } } else { qWarning("No path backend for this graphics API yet"); } diff --git a/src/quickshapes/qquickshape_p.h b/src/quickshapes/qquickshape_p.h index f1e2eea546..a7c8611856 100644 --- a/src/quickshapes/qquickshape_p.h +++ b/src/quickshapes/qquickshape_p.h @@ -288,8 +288,12 @@ class Q_QUICKSHAPES_PRIVATE_EXPORT QQuickShape : public QQuickItem Q_PROPERTY(RendererType rendererType READ rendererType NOTIFY rendererChanged) Q_PROPERTY(bool asynchronous READ asynchronous WRITE setAsynchronous NOTIFY asynchronousChanged) Q_PROPERTY(bool vendorExtensionsEnabled READ vendorExtensionsEnabled WRITE setVendorExtensionsEnabled NOTIFY vendorExtensionsEnabledChanged) + Q_PROPERTY(RendererType preferredRendererType READ preferredRendererType + WRITE setPreferredRendererType NOTIFY preferredRendererTypeChanged REVISION(6, 6)) Q_PROPERTY(Status status READ status NOTIFY statusChanged) Q_PROPERTY(ContainsMode containsMode READ containsMode WRITE setContainsMode NOTIFY containsModeChanged REVISION(1, 11)) + Q_PROPERTY(QRectF boundingRect READ boundingRect NOTIFY boundingRectChanged REVISION(6, 6)) + Q_PROPERTY(QQmlListProperty data READ data) Q_CLASSINFO("DefaultProperty", "data") QML_NAMED_ELEMENT(Shape) @@ -300,7 +304,8 @@ public: UnknownRenderer, GeometryRenderer, NvprRenderer, - SoftwareRenderer + SoftwareRenderer, + CurveRenderer }; Q_ENUM(RendererType) @@ -325,6 +330,11 @@ public: bool asynchronous() const; void setAsynchronous(bool async); + Q_REVISION(6, 6) RendererType preferredRendererType() const; + Q_REVISION(6, 6) void setPreferredRendererType(RendererType preferredType); + + Q_REVISION(6, 6) QRectF boundingRect() const override; + bool vendorExtensionsEnabled() const; void setVendorExtensionsEnabled(bool enable); @@ -349,6 +359,8 @@ Q_SIGNALS: void asynchronousChanged(); void vendorExtensionsEnabledChanged(); void statusChanged(); + Q_REVISION(6, 6) void preferredRendererTypeChanged(); + Q_REVISION(6, 6) void boundingRectChanged(); Q_REVISION(1, 11) void containsModeChanged(); private: diff --git a/src/quickshapes/qquickshape_p_p.h b/src/quickshapes/qquickshape_p_p.h index 245ba659b2..deb65e0c34 100644 --- a/src/quickshapes/qquickshape_p_p.h +++ b/src/quickshapes/qquickshape_p_p.h @@ -41,7 +41,7 @@ public: enum FillGradientType { NoGradient = 0, LinearGradient, RadialGradient, ConicalGradient }; struct GradientDesc { // can fully describe a linear/radial/conical gradient QGradientStops stops; - QQuickShapeGradient::SpreadMode spread; + QQuickShapeGradient::SpreadMode spread = QQuickShapeGradient::PadSpread; QPointF a; // start (L) or center point (R/C) QPointF b; // end (L) or focal point (R) qreal v0; // center radius (R) or start angle (C) @@ -127,6 +127,7 @@ public: QQuickShapePrivate(); ~QQuickShapePrivate(); + QQuickShape::RendererType selectRendererType(); void createRenderer(); QSGNode *createNode(); void sync(); @@ -146,8 +147,10 @@ public: int syncTimeCounter = 0; QQuickShape::Status status = QQuickShape::Null; QQuickShape::RendererType rendererType = QQuickShape::UnknownRenderer; + QQuickShape::RendererType preferredType = QQuickShape::UnknownRenderer; QQuickShape::ContainsMode containsMode = QQuickShape::BoundingRectContains; bool spChanged = false; + bool rendererChanged = false; bool async = false; bool enableVendorExts = false; bool syncTimingActive = false; diff --git a/src/quickshapes/qquickshapecurverenderer.cpp b/src/quickshapes/qquickshapecurverenderer.cpp new file mode 100644 index 0000000000..cb40125503 --- /dev/null +++ b/src/quickshapes/qquickshapecurverenderer.cpp @@ -0,0 +1,1828 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only + +#include "qquickshapecurverenderer_p.h" +#include "qquickshapecurverenderer_p_p.h" +#include "qquickshapegenericrenderer_p.h" + +#include +#include +#include + +#include + +#include + +QT_BEGIN_NAMESPACE + +Q_LOGGING_CATEGORY(lcShapeCurveRenderer, "qt.shape.curverenderer"); + +#if !defined(QQUICKSHAPECURVERENDERER_CONVEX_CHECK_ERROR_MARGIN) +# define QQUICKSHAPECURVERENDERER_CONVEX_CHECK_ERROR_MARGIN (1.0f / 32.0f) +#endif + +#define QQUICKSHAPECURVERENDERER_GRADIENTS + +namespace { + + class QQuickShapeWireFrameMaterialShader : public QSGMaterialShader + { + public: + QQuickShapeWireFrameMaterialShader() + { + setShaderFileName(VertexStage, + QStringLiteral(":/qt-project.org/shapes/shaders_ng/wireframe.vert.qsb")); + setShaderFileName(FragmentStage, + QStringLiteral(":/qt-project.org/shapes/shaders_ng/wireframe.frag.qsb")); + } + + bool updateUniformData(RenderState &state, QSGMaterial *, QSGMaterial *) override + { + bool changed = false; + QByteArray *buf = state.uniformData(); + Q_ASSERT(buf->size() >= 64); + + if (state.isMatrixDirty()) { + const QMatrix4x4 m = state.combinedMatrix(); + + memcpy(buf->data(), m.constData(), 64); + changed = true; + } + + return changed; + } + }; + + class QQuickShapeWireFrameMaterial : public QSGMaterial + { + public: + QQuickShapeWireFrameMaterial() + { + setFlag(Blending, true); + } + + int compare(const QSGMaterial *other) const override + { + return (type() - other->type()); + } + + protected: + QSGMaterialType *type() const override + { + static QSGMaterialType t; + return &t; + } + QSGMaterialShader *createShader(QSGRendererInterface::RenderMode) const override + { + return new QQuickShapeWireFrameMaterialShader; + } + + }; + + class QQuickShapeWireFrameNode : public QSGGeometryNode + { + public: + struct WireFrameVertex + { + float x, y, u, v, w; + }; + + QQuickShapeWireFrameNode() + { + setFlag(OwnsGeometry, true); + setGeometry(new QSGGeometry(attributes(), 0, 0)); + activateMaterial(); + } + + void activateMaterial() + { + m_material.reset(new QQuickShapeWireFrameMaterial); + setMaterial(m_material.data()); + } + + static const QSGGeometry::AttributeSet &attributes() + { + static QSGGeometry::Attribute data[] = { + QSGGeometry::Attribute::createWithAttributeType(0, 2, QSGGeometry::FloatType, QSGGeometry::PositionAttribute), + QSGGeometry::Attribute::createWithAttributeType(1, 3, QSGGeometry::FloatType, QSGGeometry::TexCoordAttribute), + }; + static QSGGeometry::AttributeSet attrs = { 2, sizeof(WireFrameVertex), data }; + return attrs; + } + + protected: + QScopedPointer m_material; + }; + + class QQuickShapeLoopBlinnNode; + class QQuickShapeLoopBlinnMaterial : public QSGMaterial + { + public: + QQuickShapeLoopBlinnMaterial(QQuickShapeLoopBlinnNode *node, + QQuickAbstractPathRenderer::FillGradientType gradientType) + : m_node(node) + , m_gradientType(gradientType) + { + setFlag(Blending, true); + } + int compare(const QSGMaterial *other) const override; + + QQuickShapeLoopBlinnNode *node() const + { + return m_node; + } + + protected: + QSGMaterialType *type() const override; + QSGMaterialShader *createShader(QSGRendererInterface::RenderMode renderMode) const override; + + QQuickShapeLoopBlinnNode *m_node; + QQuickAbstractPathRenderer::FillGradientType m_gradientType; + }; + + class QQuickShapeLoopBlinnNode : public QSGGeometryNode + { + public: + QQuickShapeLoopBlinnNode(QQuickAbstractPathRenderer::FillGradientType gradientType); + + struct LoopBlinnVertex + { + float x, y, u, v, w; + float r, g, b, a; // Debug color, mixed in proportion to a + }; + static const QSGGeometry::AttributeSet &attributes() + { + static QSGGeometry::Attribute data[] = { + QSGGeometry::Attribute::createWithAttributeType(0, 2, QSGGeometry::FloatType, QSGGeometry::PositionAttribute), + QSGGeometry::Attribute::createWithAttributeType(1, 3, QSGGeometry::FloatType, QSGGeometry::TexCoordAttribute), + QSGGeometry::Attribute::createWithAttributeType(2, 4, QSGGeometry::FloatType, QSGGeometry::ColorAttribute), + }; + static QSGGeometry::AttributeSet attrs = { 3, sizeof(LoopBlinnVertex), data }; + return attrs; + } + + QColor color; + QColor strokeColor = Qt::transparent; + float strokeWidth = 0.0f; + QQuickAbstractPathRenderer::GradientDesc fillGradient; + + protected: + void activateMaterial(QQuickAbstractPathRenderer::FillGradientType gradientType); + + QScopedPointer m_material; + }; + + class QQuickShapeLoopBlinnMaterialShader : public QSGMaterialShader + { + public: + QQuickShapeLoopBlinnMaterialShader(QQuickAbstractPathRenderer::FillGradientType gradientType, + bool includeStroke); + + bool updateUniformData(RenderState &state, QSGMaterial *newEffect, QSGMaterial *oldEffect) override; + void updateSampledImage(RenderState &state, int binding, QSGTexture **texture, + QSGMaterial *newMaterial, QSGMaterial *oldMaterial) override; + + private: + QQuickAbstractPathRenderer::FillGradientType m_gradientType; + }; + + QQuickShapeLoopBlinnMaterialShader::QQuickShapeLoopBlinnMaterialShader(QQuickAbstractPathRenderer::FillGradientType gradientType, + bool includeStroke) + : m_gradientType(gradientType) + { + QString baseName = QStringLiteral(":/qt-project.org/shapes/shaders_ng/shapecurve"); + + if (gradientType == QQuickAbstractPathRenderer::LinearGradient) { + baseName += QStringLiteral("_lg"); + } else if (gradientType == QQuickAbstractPathRenderer::RadialGradient) { + baseName += QStringLiteral("_rg"); + } else if (gradientType == QQuickAbstractPathRenderer::ConicalGradient) { + baseName += QStringLiteral("_cg"); + } + + if (includeStroke) + baseName += QStringLiteral("_stroke"); + + setShaderFileName(VertexStage, baseName + QStringLiteral(".vert.qsb")); + setShaderFileName(FragmentStage, baseName + QStringLiteral(".frag.qsb")); + } + + void QQuickShapeLoopBlinnMaterialShader::updateSampledImage(RenderState &state, int binding, QSGTexture **texture, + QSGMaterial *newMaterial, QSGMaterial *oldMaterial) + { + Q_UNUSED(oldMaterial); + if (binding != 1 || m_gradientType == QQuickAbstractPathRenderer::NoGradient) + return; + + QQuickShapeLoopBlinnMaterial *m = static_cast(newMaterial); + QQuickShapeLoopBlinnNode *node = m->node(); + const QQuickShapeGradientCacheKey cacheKey(node->fillGradient.stops, node->fillGradient.spread); + QSGTexture *t = QQuickShapeGradientCache::cacheForRhi(state.rhi())->get(cacheKey); + t->commitTextureOperations(state.rhi(), state.resourceUpdateBatch()); + *texture = t; + } + + bool QQuickShapeLoopBlinnMaterialShader::updateUniformData(RenderState &state, QSGMaterial *newEffect, QSGMaterial *oldEffect) + { + bool changed = false; + QByteArray *buf = state.uniformData(); + Q_ASSERT(buf->size() >= 64); + + if (state.isMatrixDirty()) { + const QMatrix4x4 m = state.combinedMatrix(); + + memcpy(buf->data(), m.constData(), 64); + changed = true; + } + int offset = 64; + + QQuickShapeLoopBlinnMaterial *newMaterial = static_cast(newEffect); + QQuickShapeLoopBlinnMaterial *oldMaterial = static_cast(oldEffect); + + QQuickShapeLoopBlinnNode *newNode = newMaterial != nullptr ? newMaterial->node() : nullptr; + QQuickShapeLoopBlinnNode *oldNode = oldMaterial != nullptr ? oldMaterial->node() : nullptr; + + if (newNode == nullptr) + return changed; + + if (newNode->strokeColor.alpha() > 0 && newNode->strokeWidth > 0.0f) { + QVector4D newStrokeColor(newNode->strokeColor.redF(), + newNode->strokeColor.greenF(), + newNode->strokeColor.blueF(), + newNode->strokeColor.alphaF()); + QVector4D oldStrokeColor = oldNode != nullptr + ? QVector4D(oldNode->strokeColor.redF(), + oldNode->strokeColor.greenF(), + oldNode->strokeColor.blueF(), + oldNode->strokeColor.alphaF()) + : QVector4D{}; + + if (oldNode == nullptr || oldStrokeColor != newStrokeColor) { + memcpy(buf->data() + offset, &newStrokeColor, 16); + changed = true; + } + offset += 16; + + if (oldNode == nullptr || !qFuzzyCompare(newNode->strokeWidth, oldNode->strokeWidth || (state.isMatrixDirty() && newNode->strokeWidth > 0 ))) { + float matrixScale = qSqrt(qAbs(state.determinant())) * state.devicePixelRatio(); + float w = newNode->strokeWidth * matrixScale; + memcpy(buf->data() + offset, &w, 4); + changed = true; + } + offset += 16; + } + + if (m_gradientType == QQuickAbstractPathRenderer::NoGradient) { + Q_ASSERT(buf->size() >= offset + 16); + + QVector4D newColor = QVector4D(newNode->color.redF(), + newNode->color.greenF(), + newNode->color.blueF(), + newNode->color.alphaF()); + QVector4D oldColor = oldNode != nullptr + ? QVector4D(oldNode->color.redF(), + oldNode->color.greenF(), + oldNode->color.blueF(), + oldNode->color.alphaF()) + : QVector4D{}; + + if (oldColor != newColor) { + memcpy(buf->data() + offset, &newColor, 16); + changed = true; + } + + offset += 16; + } else if (m_gradientType == QQuickAbstractPathRenderer::LinearGradient) { + Q_ASSERT(buf->size() >= offset + 8 + 8); + + QVector2D newGradientStart = QVector2D(newNode->fillGradient.a); + QVector2D oldGradientStart = oldNode != nullptr + ? QVector2D(oldNode->fillGradient.a) + : QVector2D{}; + + if (newGradientStart != oldGradientStart || oldEffect == nullptr) { + memcpy(buf->data() + offset, &newGradientStart, 8); + changed = true; + } + offset += 8; + + QVector2D newGradientEnd = QVector2D(newNode->fillGradient.b); + QVector2D oldGradientEnd = oldNode!= nullptr + ? QVector2D(oldNode->fillGradient.b) + : QVector2D{}; + + if (newGradientEnd != oldGradientEnd || oldEffect == nullptr) { + memcpy(buf->data() + offset, &newGradientEnd, 8); + changed = true; + } + + offset += 8; + } else if (newNode != nullptr && m_gradientType == QQuickAbstractPathRenderer::RadialGradient) { + Q_ASSERT(buf->size() >= offset + 8 + 8 + 4 + 4); + + QVector2D newFocalPoint = QVector2D(newNode->fillGradient.b); + QVector2D oldFocalPoint = oldNode != nullptr + ? QVector2D(oldNode->fillGradient.b) + : QVector2D{}; + if (oldNode == nullptr || newFocalPoint != oldFocalPoint) { + memcpy(buf->data() + offset, &newFocalPoint, 8); + changed = true; + } + offset += 8; + + QVector2D newCenterPoint = QVector2D(newNode->fillGradient.a); + QVector2D oldCenterPoint = oldNode != nullptr + ? QVector2D(oldNode->fillGradient.a) + : QVector2D{}; + + QVector2D newCenterToFocal = newCenterPoint - newFocalPoint; + QVector2D oldCenterToFocal = oldCenterPoint - oldFocalPoint; + if (oldNode == nullptr || newCenterToFocal != oldCenterToFocal) { + memcpy(buf->data() + offset, &newCenterToFocal, 8); + changed = true; + } + offset += 8; + + float newCenterRadius = newNode->fillGradient.v0; + float oldCenterRadius = oldNode != nullptr + ? oldNode->fillGradient.v0 + : 0.0f; + if (oldNode == nullptr || !qFuzzyCompare(newCenterRadius, oldCenterRadius)) { + memcpy(buf->data() + offset, &newCenterRadius, 4); + changed = true; + } + offset += 4; + + float newFocalRadius = newNode->fillGradient.v1; + float oldFocalRadius = oldNode != nullptr + ? oldNode->fillGradient.v1 + : 0.0f; + if (oldNode == nullptr || !qFuzzyCompare(newFocalRadius, oldFocalRadius)) { + memcpy(buf->data() + offset, &newFocalRadius, 4); + changed = true; + } + offset += 4; + + } else if (m_gradientType == QQuickAbstractPathRenderer::ConicalGradient) { + Q_ASSERT(buf->size() >= offset + 8 + 4); + + QVector2D newFocalPoint = QVector2D(newNode->fillGradient.a); + QVector2D oldFocalPoint = oldNode != nullptr + ? QVector2D(oldNode->fillGradient.a) + : QVector2D{}; + if (oldNode == nullptr || newFocalPoint != oldFocalPoint) { + memcpy(buf->data() + offset, &newFocalPoint, 8); + changed = true; + } + offset += 8; + + float newAngle = newNode->fillGradient.v0; + float oldAngle = oldNode != nullptr + ? oldNode->fillGradient.v0 + : 0.0f; + if (oldNode == nullptr || !qFuzzyCompare(newAngle, oldAngle)) { + newAngle = -qDegreesToRadians(newAngle); + memcpy(buf->data() + offset, &newAngle, 4); + changed = true; + } + offset += 4; + } + + return changed; + } + + int QQuickShapeLoopBlinnMaterial::compare(const QSGMaterial *other) const + { + if (other->type() != type()) + return (type() - other->type()); + + const QQuickShapeLoopBlinnMaterial *otherMaterial = + static_cast(other); + + QQuickShapeLoopBlinnNode *a = node(); + QQuickShapeLoopBlinnNode *b = otherMaterial->node(); + if (a == b) + return 0; + + if (int d = a->strokeColor.rgba() - b->strokeColor.rgba()) + return d; + + if (m_gradientType == QQuickAbstractPathRenderer::NoGradient) { + if (int d = a->color.red() - b->color.red()) + return d; + if (int d = a->color.green() - b->color.green()) + return d; + if (int d = a->color.blue() - b->color.blue()) + return d; + if (int d = a->color.alpha() - b->color.alpha()) + return d; + } else { + const QQuickAbstractPathRenderer::GradientDesc &ga = a->fillGradient; + const QQuickAbstractPathRenderer::GradientDesc &gb = b->fillGradient; + + if (int d = ga.a.x() - gb.a.x()) + return d; + if (int d = ga.a.y() - gb.a.y()) + return d; + if (int d = ga.b.x() - gb.b.x()) + return d; + if (int d = ga.b.y() - gb.b.y()) + return d; + + if (int d = ga.v0 - gb.v0) + return d; + if (int d = ga.v1 - gb.v1) + return d; + + if (int d = ga.spread - gb.spread) + return d; + + if (int d = ga.stops.size() - gb.stops.size()) + return d; + + for (int i = 0; i < ga.stops.size(); ++i) { + if (int d = ga.stops[i].first - gb.stops[i].first) + return d; + if (int d = ga.stops[i].second.rgba() - gb.stops[i].second.rgba()) + return d; + } + } + + return 0; + } + + QSGMaterialType *QQuickShapeLoopBlinnMaterial::type() const + { + static QSGMaterialType type[8]; + return &type[m_gradientType + (node()->strokeColor.alpha() > 0 ? 4 : 0)]; + } + + QSGMaterialShader *QQuickShapeLoopBlinnMaterial::createShader(QSGRendererInterface::RenderMode renderMode) const + { + Q_UNUSED(renderMode); + return new QQuickShapeLoopBlinnMaterialShader(m_gradientType, + node()->strokeColor.alpha() > 0); + } + + QQuickShapeLoopBlinnNode::QQuickShapeLoopBlinnNode(QQuickAbstractPathRenderer::FillGradientType gradientType) + { + setFlag(OwnsGeometry, true); + setGeometry(new QSGGeometry(attributes(), 0, 0)); + + activateMaterial(gradientType); + } + + void QQuickShapeLoopBlinnNode::activateMaterial(QQuickAbstractPathRenderer::FillGradientType gradientType) + { + m_material.reset(new QQuickShapeLoopBlinnMaterial(this, gradientType)); + setMaterial(m_material.data()); + } +} + +QVector2D QuadPath::Element::pointAtFraction(float t) const +{ + if (isLine()) { + return sp + t * (ep - sp); + } else { + const float r = 1 - t; + return (r * r * sp) + (2 * t * r * cp) + (t * t * ep); + } +} + +// Returns the number of intersections between element and a horizontal line at y. +// The t values of max 2 intersection(s) are stored in the fractions array +int QuadPath::Element::intersectionsAtY(float y, float *fractions) const +{ + const float y0 = startPoint().y() - y; + const float y1 = controlPoint().y() - y; + const float y2 = endPoint().y() - y; + + int numRoots = 0; + const float a = y0 - (2 * y1) + y2; + if (a) { + const float b = (y1 * y1) - (y0 * y2); + if (b >= 0) { + const float sqr = qSqrt(b); + const float root1 = -(-y0 + y1 + sqr) / a; + if (qIsFinite(root1) && root1 >= 0 && root1 <= 1) + fractions[numRoots++] = root1; + const float root2 = (y0 - y1 + sqr) / a; + if (qIsFinite(root2) && root2 != root1 && root2 >= 0 && root2 <= 1) + fractions[numRoots++] = root2; + } + } else if (y1 != y2) { + const float root1 = (y2 - (2 * y1)) / (2 * (y2 - y1)); + if (qIsFinite(root1) && root1 >= 0 && root1 <= 1) + fractions[numRoots++] = root1; + } + + return numRoots; +} + +static float crossProduct(const QVector2D &sp, const QVector2D &p, const QVector2D &ep) +{ + QVector2D v1 = ep - p; + QVector2D v2 = p - sp; + return (v2.x() * v1.y()) - (v2.y() * v1.x()); +} + +bool QuadPath::isPointOnLeft(const QVector2D &p, const QVector2D &sp, const QVector2D &ep) +{ + // Use cross product to compare directions of base vector and vector from start to p + return crossProduct(sp, p, ep) >= 0.0f; +} + +bool QuadPath::isPointOnLine(const QVector2D &p, const QVector2D &sp, const QVector2D &ep) +{ + return qFuzzyIsNull(crossProduct(p, sp, ep)); +} + +bool QuadPath::isPointNearLine(const QVector2D &p, const QVector2D &sp, const QVector2D &ep) +{ + constexpr float epsilon = 1.0f; + return qAbs(crossProduct(p, sp, ep)) < epsilon; +} + +bool QuadPath::isControlPointOnLeft(const QuadPath::Element &element) +{ + return isPointOnLeft(element.cp, element.sp, element.ep); +} + +// NOTE: it is assumed that subpaths are closed +bool QuadPath::contains(const QVector2D &pt) const +{ + // if (!controlPointRect().contains(pt) : good opt when we add cpr caching + // return false; + + int winding_number = 0; + for (const Element &e : m_elements) { + int dir = 1; + float y1 = e.startPoint().y(); + float y2 = e.endPoint().y(); + if (y2 < y1) { + qSwap(y1, y2); + dir = -1; + } + if (e.m_isLine) { + if (pt.y() < y1 || pt.y() >= y2 || y1 == y2) + continue; + const float t = (pt.y() - e.startPoint().y()) / (e.endPoint().y() - e.startPoint().y()); + const float x = e.startPoint().x() + t * (e.endPoint().x() - e.startPoint().x()); + if (x <= pt.x()) + winding_number += dir; + } else { + y1 = qMin(y1, e.controlPoint().y()); + y2 = qMax(y2, e.controlPoint().y()); + if (pt.y() < y1 || pt.y() >= y2) + continue; + float ts[2]; + const int numRoots = e.intersectionsAtY(pt.y(), ts); + // Count if there is exactly one intersection to the left + bool oneHit = false; + float tForHit = -1; + for (int i = 0; i < numRoots; i++) { + if (e.pointAtFraction(ts[i]).x() <= pt.x()) { + oneHit = !oneHit; + tForHit = ts[i]; + } + } + if (oneHit) { + dir = e.tangentAtFraction(tForHit).y() < 0 ? -1 : 1; + winding_number += dir; + } + } + }; + + return (fillRule() == Qt::WindingFill ? (winding_number != 0) : ((winding_number % 2) != 0)); +} + +void QuadPath::addElement(const QVector2D &control, const QVector2D &endPoint, bool isLine) +{ + if (qFuzzyCompare(currentPoint, endPoint)) + return; // 0 length element, skip + + isLine = isLine || isPointNearLine(control, currentPoint, endPoint); // Turn flat quad into line + + m_elements.resize(m_elements.size() + 1); + Element &elem = m_elements.last(); + elem.sp = currentPoint; + elem.cp = isLine ? (0.5f * (currentPoint + endPoint)) : control; + elem.ep = endPoint; + elem.m_isLine = isLine; + elem.m_isSubpathStart = subPathToStart; + subPathToStart = false; + currentPoint = endPoint; +} + +QuadPath::Element::CurvatureFlags QuadPath::coordinateOrderOfElement(const QuadPath::Element &element) const +{ + QVector2D baseLine = element.endPoint() - element.startPoint(); + QVector2D midPoint = element.midPoint(); + // At the midpoint, the tangent of a quad is parallel to the baseline + QVector2D normal = QVector2D(-baseLine.y(), baseLine.x()).normalized(); + QVector2D justRightOfMid = midPoint + normal * QQUICKSHAPECURVERENDERER_CONVEX_CHECK_ERROR_MARGIN; + bool pathContainsPoint = contains(justRightOfMid); + return pathContainsPoint ? Element::FillOnRight : Element::CurvatureFlags(0); +} + +QVector2D QuadPath::closestPointOnLine(const QVector2D &start, + const QVector2D &end, + const QVector2D &p) +{ + QVector2D line = end - start; + float t = QVector2D::dotProduct(p - start, line) / QVector2D::dotProduct(line, line); + return start + qBound(0.0f, t, 1.0f) * line; +} + +QuadPath QuadPath::fromPainterPath(const QPainterPath &path) +{ + QuadPath res; + res.reserve(path.elementCount()); + res.setFillRule(path.fillRule()); + + QPointF sp; + for (int i = 0; i < path.elementCount(); ++i) { + QPainterPath::Element element = path.elementAt(i); + + QPointF ep(element); + switch (element.type) { + case QPainterPath::MoveToElement: + res.moveTo(QVector2D(ep)); + break; + case QPainterPath::LineToElement: + res.lineTo(QVector2D(ep)); + break; + case QPainterPath::CurveToElement: { + QPointF cp1 = ep; + QPointF cp2(path.elementAt(++i)); + ep = path.elementAt(++i); + QBezier b = QBezier::fromPoints(sp, cp1, cp2, ep); +#ifndef USE_TOQUADRATICS_IN_QBEZIER + const QPolygonF quads = qt_toQuadratics(b); +#else + const QPolygonF quads = b.toQuadratics(); +#endif + for (int i = 1; i < quads.size(); i += 2) { + QVector2D cp(quads[i]); + QVector2D ep(quads[i + 1]); + res.quadTo(cp, ep); + } + break; + } + default: + Q_UNREACHABLE(); + break; + } + sp = ep; + } + + return res; +} + +void QuadPath::addCurvatureData() +{ + // We use the convention that the inside of a curve is on the *right* side of the + // direction of the baseline.Thus, as long as this is true: if the control point is + // on the left side of the baseline, the curve is convex and otherwise it is + // concave. The paths we get can be arbitrary order, but each subpath will have a + // consistent order. Therefore, for the first curve element in a subpath, we can + // determine if the direction already follows the convention or not, and then we + // can easily detect curvature of all subsequent elements in the subpath. + + static bool checkAnomaly = qEnvironmentVariableIntValue("QT_QUICKSHAPES_CHECK_ALL_CURVATURE") != 0; + + Element::CurvatureFlags flags = Element::CurvatureUndetermined; + for (QuadPath::Element &element : m_elements) { + Q_ASSERT(element.childCount() == 0); + if (element.isSubpathStart()) { + flags = coordinateOrderOfElement(element); + } else if (checkAnomaly) { + Element::CurvatureFlags newFlags = coordinateOrderOfElement(element); + if (flags != newFlags) { + qDebug() << "Curvature anomaly detected:" << element + << "Subpath fill on right:" << (flags & Element::FillOnRight) + << "Element fill on right:" << (newFlags & Element::FillOnRight); + } + } + + if (element.isLine()) { + element.m_curvatureFlags = flags; + + // Set the control point to an arbitrary point on the inside side of the line + // (doesn't need to actually be inside the shape: it just makes our calculations + // easier later if it is at the same side as the fill). + const QVector2D &sp = element.sp; + const QVector2D &ep = element.ep; + QVector2D v = ep - sp; + element.cp = flags & Element::FillOnRight ? sp + QVector2D(-v.y(), v.x()) : sp + QVector2D(v.y(), -v.x()); + } else { + bool controlPointOnLeft = isControlPointOnLeft(element); + bool isFillOnRight = flags & Element::FillOnRight; + bool isConvex = controlPointOnLeft == isFillOnRight; + + if (isConvex) + element.m_curvatureFlags = Element::CurvatureFlags(flags | Element::Convex); + else + element.m_curvatureFlags = flags; + } + } +} + +QRectF QuadPath::controlPointRect() const +{ + QRectF res; + if (elementCount()) { + QVector2D min, max; + min = max = m_elements.constFirst().sp; + // No need to recurse, as split curve's controlpoints are within the parent curve's + for (const QuadPath::Element &e : std::as_const(m_elements)) { + min.setX(std::min({ min.x(), e.sp.x(), e.cp.x(), e.ep.x() })); + min.setY(std::min({ min.y(), e.sp.y(), e.cp.y(), e.ep.y() })); + max.setX(std::max({ max.x(), e.sp.x(), e.cp.x(), e.ep.x() })); + max.setY(std::max({ max.y(), e.sp.y(), e.cp.y(), e.ep.y() })); + } + res = QRectF(min.toPointF(), max.toPointF()); + } + return res; +} + +// Count leaf elements +qsizetype QuadPath::elementCountRecursive() const +{ + qsizetype count = 0; + iterateElements([&](const QuadPath::Element &) { count++; }); + return count; +} + +QPainterPath QuadPath::toPainterPath() const +{ + // Currently only converts the main, unsplit path; no need for the split path identified + QPainterPath res; + res.reserve(elementCount()); + res.setFillRule(fillRule()); + for (const Element &element : m_elements) { + if (element.m_isSubpathStart) + res.moveTo(element.startPoint().toPointF()); + if (element.m_isLine) + res.lineTo(element.endPoint().toPointF()); + else + res.quadTo(element.controlPoint().toPointF(), element.endPoint().toPointF()); + }; + return res; +} + +// Returns a new path since doing it inline would probably be less efficient +// (technically changing it from O(n) to O(n^2)) +// Note that this function should be called before splitting any elements, +// so we can assume that the structure is a list and not a tree +QuadPath QuadPath::subPathsClosed() const +{ + QuadPath res = *this; + res.m_elements = {}; + res.m_elements.reserve(elementCount()); + qsizetype subStart = -1; + qsizetype prevElement = -1; + for (qsizetype i = 0; i < elementCount(); i++) { + const auto &element = m_elements.at(i); + if (element.m_isSubpathStart) { + if (subStart >= 0 && m_elements[i - 1].ep != m_elements[subStart].sp) { + res.currentPoint = m_elements[i - 1].ep; + res.lineTo(m_elements[subStart].sp); + auto &endElement = res.m_elements.last(); + endElement.m_isSubpathEnd = true; + // lineTo() can bail out if the points are too close. + // In that case, just change the end point to be equal to the start + // (No need to test because the assignment is a no-op otherwise). + endElement.ep = m_elements[subStart].sp; + } else if (prevElement >= 0) { + res.m_elements[prevElement].m_isSubpathEnd = true; + } + subStart = i; + } + res.m_elements.append(element); + prevElement = res.m_elements.size() - 1; + } + + if (subStart >= 0 && m_elements.last().ep != m_elements[subStart].sp) { + res.currentPoint = m_elements.last().ep; + res.lineTo(m_elements[subStart].sp); + } + if (!res.m_elements.isEmpty()) { + auto &endElement = res.m_elements.last(); + endElement.m_isSubpathEnd = true; + endElement.ep = m_elements[subStart].sp; + } + return res; +} + +void QuadPath::splitElementAt(qsizetype index) +{ + const qsizetype newChildIndex = m_childElements.size(); + m_childElements.resize(newChildIndex + 2); + Element &parent = elementAt(index); + parent.m_numChildren = 2; + parent.m_firstChildIndex = newChildIndex; + + Element &quad1 = m_childElements[newChildIndex]; + const QVector2D mp = parent.midPoint(); + quad1.sp = parent.sp; + quad1.cp = 0.5f * (parent.sp + parent.cp); + quad1.ep = mp; + quad1.m_isSubpathStart = parent.m_isSubpathStart; + quad1.m_isSubpathEnd = false; + quad1.m_curvatureFlags = parent.m_curvatureFlags; + quad1.m_isLine = parent.m_isLine || isPointNearLine(quad1.cp, quad1.sp, quad1.ep); + + Element &quad2 = m_childElements[newChildIndex + 1]; + quad2.sp = mp; + quad2.cp = 0.5f * (parent.ep + parent.cp); + quad2.ep = parent.ep; + quad2.m_isSubpathStart = false; + quad2.m_isSubpathEnd = parent.m_isSubpathEnd; + quad2.m_curvatureFlags = parent.m_curvatureFlags; + quad2.m_isLine = parent.m_isLine || isPointNearLine(quad2.cp, quad2.sp, quad2.ep); + +#ifndef QT_NO_DEBUG + if (qFuzzyCompare(quad1.sp, quad1.ep) || qFuzzyCompare(quad2.sp, quad2.ep)) + qDebug() << "###FIXME: quad splitting has yielded ~null quad."; +#endif +} + +static void printElement(QDebug stream, const QuadPath::Element &element) +{ + auto printPoint = [&](QVector2D p) { stream << "(" << p.x() << ", " << p.y() << ") "; }; + stream << "{ "; + printPoint(element.startPoint()); + printPoint(element.controlPoint()); + printPoint(element.endPoint()); + stream << "} " << (element.isLine() ? "L " : "C ") << (element.isConvex() ? "X " : "O ") + << (element.isSubpathStart() ? "S" : element.isSubpathEnd() ? "E" : ""); +} + +QDebug operator<<(QDebug stream, const QuadPath::Element &element) +{ + QDebugStateSaver saver(stream); + stream.nospace(); + stream << "QuadPath::Element( "; + printElement(stream, element); + stream << " )"; + return stream; +} + +QDebug operator<<(QDebug stream, const QuadPath &path) +{ + QDebugStateSaver saver(stream); + stream.nospace(); + stream << "QuadPath(" << path.elementCount() << " main elements, " + << path.elementCountRecursive() << " leaf elements, " + << (path.fillRule() == Qt::OddEvenFill ? "OddEven" : "Winding") << Qt::endl; + qsizetype count = 0; + path.iterateElements([&](const QuadPath::Element &e) { + stream << " " << count++ << (e.isSubpathStart() ? " >" : " "); + printElement(stream, e); + stream << Qt::endl; + }); + stream << ")"; + return stream; +} + +QQuickShapeCurveNode::QQuickShapeCurveNode() { } + +QQuickShapeCurveRenderer::~QQuickShapeCurveRenderer() { } + +void QQuickShapeCurveRenderer::beginSync(int totalCount, bool *countChanged) +{ + if (countChanged != nullptr && totalCount != m_paths.size()) + *countChanged = true; + m_paths.resize(totalCount); +} + +void QQuickShapeCurveRenderer::setPath(int index, const QQuickPath *path) +{ + auto &pathData = m_paths[index]; + pathData.originalPath = path->path(); + pathData.m_dirty |= GeometryDirty; + +} + +void QQuickShapeCurveRenderer::setStrokeColor(int index, const QColor &color) +{ + auto &pathData = m_paths[index]; + const bool wasVisible = pathData.isStrokeVisible(); + pathData.pen.setColor(color); + if (pathData.isStrokeVisible() != wasVisible) + pathData.m_dirty |= GeometryDirty; + else + pathData.m_dirty |= UniformsDirty; +} + +void QQuickShapeCurveRenderer::setStrokeWidth(int index, qreal w) +{ + auto &pathData = m_paths[index]; + if (w > 0) { + pathData.validPenWidth = true; + pathData.pen.setWidthF(w); + } else { + pathData.validPenWidth = false; + } + pathData.m_dirty |= GeometryDirty; +} + +void QQuickShapeCurveRenderer::setFillColor(int index, const QColor &color) +{ + auto &pathData = m_paths[index]; + const bool wasVisible = pathData.isFillVisible(); + pathData.fillColor = color; + if (pathData.isFillVisible() != wasVisible) + pathData.m_dirty |= GeometryDirty; + else + pathData.m_dirty |= UniformsDirty; +} + +void QQuickShapeCurveRenderer::setFillRule(int index, QQuickShapePath::FillRule fillRule) +{ + auto &pathData = m_paths[index]; + pathData.fillRule = Qt::FillRule(fillRule); + pathData.m_dirty |= GeometryDirty; +} + +void QQuickShapeCurveRenderer::setJoinStyle(int index, + QQuickShapePath::JoinStyle joinStyle, + int miterLimit) +{ + auto &pathData = m_paths[index]; + pathData.pen.setJoinStyle(Qt::PenJoinStyle(joinStyle)); + pathData.pen.setMiterLimit(miterLimit); + pathData.m_dirty |= GeometryDirty; +} + +void QQuickShapeCurveRenderer::setCapStyle(int index, QQuickShapePath::CapStyle capStyle) +{ + auto &pathData = m_paths[index]; + pathData.pen.setCapStyle(Qt::PenCapStyle(capStyle)); + pathData.m_dirty |= GeometryDirty; +} + +void QQuickShapeCurveRenderer::setStrokeStyle(int index, + QQuickShapePath::StrokeStyle strokeStyle, + qreal dashOffset, + const QVector &dashPattern) +{ + auto &pathData = m_paths[index]; + pathData.pen.setStyle(Qt::PenStyle(strokeStyle)); + if (strokeStyle == QQuickShapePath::DashLine) { + pathData.pen.setDashPattern(dashPattern); + pathData.pen.setDashOffset(dashOffset); + } + pathData.m_dirty |= GeometryDirty; +} + +void QQuickShapeCurveRenderer::setFillGradient(int index, QQuickShapeGradient *gradient) +{ +#if defined(QQUICKSHAPECURVERENDERER_GRADIENTS) + PathData &pd(m_paths[index]); + pd.gradientType = NoGradient; + if (QQuickShapeLinearGradient *g = qobject_cast(gradient)) { + pd.gradientType = LinearGradient; + pd.gradient.stops = gradient->gradientStops(); + pd.gradient.spread = gradient->spread(); + pd.gradient.a = QPointF(g->x1(), g->y1()); + pd.gradient.b = QPointF(g->x2(), g->y2()); + } else if (QQuickShapeRadialGradient *g = qobject_cast(gradient)) { + pd.gradientType = RadialGradient; + pd.gradient.a = QPointF(g->centerX(), g->centerY()); + pd.gradient.b = QPointF(g->focalX(), g->focalY()); + pd.gradient.v0 = g->centerRadius(); + pd.gradient.v1 = g->focalRadius(); + } else if (QQuickShapeConicalGradient *g = qobject_cast(gradient)) { + pd.gradientType = ConicalGradient; + pd.gradient.a = QPointF(g->centerX(), g->centerY()); + pd.gradient.v0 = g->angle(); + } else +#endif + if (gradient != nullptr) { + static bool warned = false; + if (!warned) { + warned = true; + qCWarning(lcShapeCurveRenderer) << "Unsupported gradient fill"; + } + } + + if (pd.gradientType != NoGradient) { + pd.gradient.stops = gradient->gradientStops(); + pd.gradient.spread = gradient->spread(); + } + + pd.m_dirty |= GeometryDirty; +} + +void QQuickShapeCurveRenderer::setAsyncCallback(void (*callback)(void *), void *data) +{ + qCWarning(lcShapeCurveRenderer) << "Asynchronous creation not supported by CurveRenderer"; + Q_UNUSED(callback); + Q_UNUSED(data); +} + +void QQuickShapeCurveRenderer::endSync(bool async) +{ + Q_UNUSED(async); +} + + +bool QQuickShapeCurveRenderer::PathData::useFragmentShaderStroker() const +{ + static bool usePathStroker = qEnvironmentVariableIsSet("QT_QUICKSHAPES_STROKER") + && qEnvironmentVariableIntValue("QT_QUICKSHAPES_STROKER") == 0; + static bool newCodePath = qEnvironmentVariableIntValue("QT_QUICKSHAPES_ALTERNATIVE_CODE_PATH") != 0; + + return newCodePath && !usePathStroker && pen.style() == Qt::SolidLine && isFillVisible(); +} + +void QQuickShapeCurveRenderer::updateNode() +{ + if (!m_rootNode) + return; + static const int codePath = qEnvironmentVariableIntValue("QT_QUICKSHAPES_ALTERNATIVE_CODE_PATH"); + static const int overlapSolving = !qEnvironmentVariableIntValue("QT_QUICKSHAPES_DISABLE_OVERLAP_SOLVER"); + + auto addNodes = [&](const PathData &pathData, NodeList *debugNodes) { + if (codePath == 42) + return addPathNodesDelauneyTest(pathData, debugNodes); + else + return addPathNodesBasic(pathData, debugNodes); + }; + + for (PathData &pathData : m_paths) { + if (pathData.m_dirty & GeometryDirty) { + deleteAndClear(&pathData.strokeNodes); + deleteAndClear(&pathData.fillNodes); + deleteAndClear(&pathData.debugNodes); + + bool createStrokePath = pathData.isStrokeVisible() && !pathData.useFragmentShaderStroker(); + + static bool simplifyPath = qEnvironmentVariableIntValue("QT_QUICKSHAPES_SIMPLIFY_PATHS") != 0; + pathData.path = simplifyPath ? QuadPath::fromPainterPath(pathData.originalPath.simplified()) : QuadPath::fromPainterPath(pathData.originalPath); + pathData.path.setFillRule(pathData.fillRule); + + if (createStrokePath) + pathData.fillPath = pathData.path.toPainterPath(); // Without subpath closing etc. + + if (pathData.isFillVisible()) { + pathData.path = pathData.path.subPathsClosed(); + pathData.path.addCurvatureData(); + if (overlapSolving) + solveOverlaps(pathData.path); + pathData.fillNodes = addNodes(pathData, &pathData.debugNodes); + } + + if (createStrokePath) { + QPainterPathStroker stroker(pathData.pen); + QPainterPath strokePath = stroker.createStroke(pathData.fillPath); + + // Solid strokes are sometimes created with self-overlaps in the joins, + // causing the overlap detection to freak out. So while this is expensive + // we have to make sure the overlaps are removed first. + static bool simplifyStroke = qEnvironmentVariableIntValue("QT_QUICKSHAPES_DISABLE_SIMPLIFY_STROKE") == 0; + if (pathData.pen.isSolid() && simplifyStroke) + strokePath = strokePath.simplified(); + QuadPath strokeQuadPath = QuadPath::fromPainterPath(strokePath); + strokeQuadPath.addCurvatureData(); + strokePath = strokeQuadPath.toPainterPath(); + if (overlapSolving) + solveOverlaps(strokeQuadPath); + + PathData strokeData = pathData; + strokeData.path = strokeQuadPath; + strokeData.fillPath = strokePath; + strokeData.gradientType = NoGradient; + strokeData.fillColor = pathData.pen.color(); + pathData.strokeNodes = addNodes(strokeData, &pathData.debugNodes); + } + } else if (pathData.m_dirty & UniformsDirty) { + for (auto &pathNode : std::as_const(pathData.fillNodes)) + static_cast(pathNode)->color = pathData.fillColor; + + if (pathData.pen.style() != Qt::NoPen && pathData.pen.color().alpha() > 0) { + for (auto &strokeNode : std::as_const(pathData.strokeNodes)) + static_cast(strokeNode)->color = pathData.pen.color(); + } + } + pathData.m_dirty &= ~(GeometryDirty | UniformsDirty); + } +} + + +QVector QQuickShapeCurveRenderer::addPathNodesBasic(const PathData &pathData, NodeList *debugNodes) +{ + QVector ret; + + QList quadraticCurvesConvex; + QList quadraticCurvesConcave; + QList lineSegments; + QPainterPath internalHull; + internalHull.setFillRule(pathData.path.fillRule()); + + pathData.path.iterateElements([&](const QuadPath::Element &element){ + QPointF sp(element.startPoint().toPointF()); + QPointF cp(element.controlPoint().toPointF()); + QPointF ep(element.endPoint().toPointF()); + if (element.isSubpathStart()) + internalHull.moveTo(sp); + if (element.isLine()) { + internalHull.lineTo(ep); + lineSegments.append(QLineF(sp, ep)); + } else if (element.isConvex()) { + internalHull.lineTo(ep); + quadraticCurvesConvex.append(QPolygonF() << sp << cp << ep); + } else { + internalHull.lineTo(cp); + internalHull.lineTo(ep); + quadraticCurvesConcave.append(QPolygonF() << sp << cp << ep); + } + }); + + QTriangleSet triangles = qTriangulate(internalHull); + + // Add triangles for curves. Note: These have to be adapted to 1/32 grid, since this + // is the resolution of the triangulation + QVarLengthArray extraIndices; + for (const QPolygonF &quadraticCurve : quadraticCurvesConvex) { + QPointF v1 = quadraticCurve.at(0); + QPointF v2 = quadraticCurve.at(1); + QPointF v3 = quadraticCurve.at(2); + + extraIndices.append(triangles.vertices.size() / 2); + triangles.vertices.append(qRound(v1.x() * 32.0) / 32.0); + triangles.vertices.append(qRound(v1.y() * 32.0) / 32.0); + + extraIndices.append(triangles.vertices.size() / 2); + triangles.vertices.append(qRound(v2.x() * 32.0) / 32.0); + triangles.vertices.append(qRound(v2.y() * 32.0) / 32.0); + + extraIndices.append(triangles.vertices.size() / 2); + triangles.vertices.append(qRound(v3.x() * 32.0) / 32.0); + triangles.vertices.append(qRound(v3.y() * 32.0) / 32.0); + } + + int startConcaveCurves = triangles.vertices.size() / 2; + for (const QPolygonF &quadraticCurve : quadraticCurvesConcave) { + QPointF v1 = quadraticCurve.at(0); + QPointF v2 = quadraticCurve.at(1); + QPointF v3 = quadraticCurve.at(2); + + extraIndices.append(triangles.vertices.size() / 2); + triangles.vertices.append(qRound(v1.x() * 32.0) / 32.0); + triangles.vertices.append(qRound(v1.y() * 32.0) / 32.0); + + extraIndices.append(triangles.vertices.size() / 2); + triangles.vertices.append(qRound(v2.x() * 32.0) / 32.0); + triangles.vertices.append(qRound(v2.y() * 32.0) / 32.0); + + extraIndices.append(triangles.vertices.size() / 2); + triangles.vertices.append(qRound(v3.x() * 32.0) / 32.0); + triangles.vertices.append(qRound(v3.y() * 32.0) / 32.0); + } + + ret.append(addLoopBlinnNodes(triangles, + extraIndices, + startConcaveCurves, + pathData, + debugNodes)); + return ret; +} + +QSGGeometryNode *QQuickShapeCurveRenderer::addLoopBlinnNodes(const QTriangleSet &triangles, + const QVarLengthArray &extraIndices, + int startConcaveCurves, + const PathData &pathData, NodeList *debugNodes) +{ + const QColor &color = pathData.fillColor; + + // Basically we here need a way to determine if each triangle is: + // 1. A convex curve + // 2. A concave curve + // 3. A filled triangle + // + // For filled triangles, we make all texture coordinates (1.0, 0.0) + // For curves, we use (0, 0), (0.5, 0), (1, 1) + // We use a third texture coordinate for curves: 0 means convex curve and 1 means concave + + QQuickShapeLoopBlinnNode *node = new QQuickShapeLoopBlinnNode(pathData.gradientType); + node->fillGradient = pathData.gradient; + node->color = color; + + if (pathData.isStrokeVisible() && pathData.useFragmentShaderStroker()) { + node->strokeColor = pathData.pen.color(); + node->strokeWidth = pathData.pen.widthF(); + } + + QSGGeometry *g = node->geometry(); + + QSGGeometry::Type indexType = triangles.indices.type() == QVertexIndexVector::UnsignedInt + ? QSGGeometry::UnsignedIntType + : QSGGeometry::UnsignedShortType; + Q_ASSERT(indexType == QSGGeometry::UnsignedIntType); // Needs some code to support shorts + // since extraIndices is int32 + + QVector vertices; + for (int i = 0; i < triangles.vertices.size(); i += 2) { + QVector2D v(triangles.vertices.at(i), triangles.vertices.at(i + 1)); + QVector3D uv(0.0f, 1.0f, -1.0f); + bool visualizeDebug = debugVisualization() & DebugCurves; + vertices.append( { v.x(), v.y(), uv.x(), uv.y(), uv.z(), 0.0f, 1.0f, 0.0f, visualizeDebug ? 0.5f : 0.0f }); + } + + for (int i = 0; i < extraIndices.size(); ++i) { + int idx = extraIndices.at(i); + + QVector2D uv; + switch (i % 3) { + case 0: + uv = QVector2D(0.0f, 0.0f); + break; + case 1: + uv = QVector2D(0.5f, 0.0f); + break; + case 2: + uv = QVector2D(1.0f, 1.0f); + break; + } + + vertices[idx].u = uv.x(); + vertices[idx].v = uv.y(); + vertices[idx].w = idx >= startConcaveCurves ? 1.0f : -1.0f; + vertices[idx].r = idx >= startConcaveCurves ? 0.0f : 1.0f; + vertices[idx].g = 0.0f; + vertices[idx].b = idx >= startConcaveCurves ? 1.0f : 0.0f; + } + + if (g->indexType() != indexType) { + g = new QSGGeometry(QQuickShapeLoopBlinnNode::attributes(), + vertices.size(), + triangles.indices.size() + extraIndices.size(), + indexType); + node->setGeometry(g); + } else { + g->allocate(vertices.size(), triangles.indices.size() + extraIndices.size()); + } + + g->setDrawingMode(QSGGeometry::DrawTriangles); + memcpy(g->vertexData(), + vertices.data(), + g->vertexCount() * g->sizeOfVertex()); + memcpy(g->indexData(), + triangles.indices.data(), + triangles.indices.size() * g->sizeOfIndex()); + memcpy((uchar*)g->indexData() + triangles.indices.size() * g->sizeOfIndex(), + extraIndices.data(), + extraIndices.size() * g->sizeOfIndex()); + + m_rootNode->appendChildNode(node); + + if (debugVisualization() & DebugWireframe) { + QQuickShapeWireFrameNode *wfNode = new QQuickShapeWireFrameNode; + QSGGeometry *wfg = wfNode->geometry(); + + QVarLengthArray indices; + QVarLengthArray wfVertices; + for (int i = 0; i < triangles.indices.size() + extraIndices.size(); ++i) { + quint32 index = i < triangles.indices.size() + ? ((quint32*)triangles.indices.data())[i] + : extraIndices[i - triangles.indices.size()]; + + const QQuickShapeLoopBlinnNode::LoopBlinnVertex &vertex = vertices.at(index); + + float u = i % 3 == 0 ? 1.0f : 0.0f; + float v = i % 3 == 1 ? 1.0f : 0.0f; + float w = i % 3 == 2 ? 1.0f : 0.0f; + + indices.append(i); + wfVertices.append({ vertex.x, vertex.y, u, v, w }); + } + + if (wfg->indexType() != indexType) { + wfg = new QSGGeometry(QQuickShapeWireFrameNode::attributes(), + wfVertices.size(), + indices.size(), + indexType); + wfNode->setGeometry(wfg); + } else { + wfg->allocate(wfVertices.size(), indices.size()); + } + + wfg->setDrawingMode(QSGGeometry::DrawTriangles); + memcpy(wfg->indexData(), + indices.data(), + indices.size() * g->sizeOfIndex()); + memcpy(wfg->vertexData(), + wfVertices.data(), + wfg->vertexCount() * wfg->sizeOfVertex()); + + m_rootNode->appendChildNode(wfNode); + debugNodes->append(wfNode); + } + + return node; +} + +static bool testTriangulation(const QList &vertices, const QList &edges, const QList &triangles) +{ + // Verify that all our edges are part of a triangle: + bool ok = true; + auto edgeMatch = [](const QtPathEdge &edge, quint32 v1Index, quint32 v2Index) -> bool { + return (edge.startIndex == v1Index && edge.endIndex == v2Index) + || (edge.startIndex == v2Index && edge.endIndex == v1Index); + }; + + + for (const auto &edge : edges) { + // Just do it the simplest way possible, and O(n^2) be damned + bool found = false; + for (const auto &triangle : triangles) { + if (edgeMatch(edge, triangle.v1Index, triangle.v2Index) + || edgeMatch(edge, triangle.v2Index, triangle.v3Index) + || edgeMatch(edge, triangle.v3Index, triangle.v1Index)) { + found = true; + break; + } + } + if (!found) { + qDebug() << "*** MISSING LINK ***" << edge.startIndex << edge.endIndex + << vertices[edge.startIndex].point << vertices[edge.endIndex].point; + ok = false; + } + } + return ok; +} + +void QQuickShapeCurveRenderer::setRootNode(QQuickShapeCurveNode *node) +{ + m_rootNode = node; +} + +int QQuickShapeCurveRenderer::debugVisualizationFlags = QQuickShapeCurveRenderer::NoDebug; + +int QQuickShapeCurveRenderer::debugVisualization() +{ + static const int envFlags = qEnvironmentVariableIntValue("QT_QUICKSHAPES_DEBUG"); + return debugVisualizationFlags | envFlags; +} + +void QQuickShapeCurveRenderer::setDebugVisualization(int options) +{ + if (debugVisualizationFlags == options) + return; + debugVisualizationFlags = options; +} + +void QQuickShapeCurveRenderer::deleteAndClear(NodeList *nodeList) +{ + for (QSGNode *node : std::as_const(*nodeList)) + delete node; + nodeList->clear(); +} + +/* + Clever triangle overlap algorithm. Stack Overflow says: + + You can prove that the two triangles do not collide by finding an edge (out of the total 6 + edges that make up the two triangles) that acts as a separating line where all the vertices + of one triangle lie on one side and the vertices of the other triangle lie on the other side. + If you can find such an edge then it means that the triangles do not intersect otherwise the + triangles are colliding. +*/ + +// The sign of the determinant tells the winding order +static inline double determinant(QVector2D &p1, QVector2D &p2, QVector2D &p3) +{ + return p1.x() * (p2.y() - p3.y()) + + p2.x() * (p3.y() - p1.y()) + + p3.x() * (p1.y() - p2.y()); +} + +// Fix the triangle so that the determinant is positive +static void fixWinding(QVector2D &p1, QVector2D &p2, QVector2D &p3) +{ + double det = determinant(p1, p2, p3); + if (det < 0.0) { + qSwap(p1, p2); + } +} + +// Return true if the determinant is negative, i.e. if the winding order is opposite of the triangle p1,p2,p3. +// This means that p is strictly on the other side of p1-p2 relative to p3 [where p1,p2,p3 is a triangle with +// a positive determinant]. +bool checkEdge(QVector2D &p1, QVector2D &p2, QVector2D &p, float epsilon) +{ + return determinant(p1, p2, p) <= epsilon; +} + +bool checkTriangleOverlap(QVector2D *triangle1, QVector2D *triangle2, float epsilon = 1.0/32) +{ + // See if there is an edge of triangle1 such that all vertices in triangle2 are on the opposite side + fixWinding(triangle1[0], triangle1[1], triangle1[2]); + for (int i = 0; i < 3; i++) { + int ni = (i + 1) % 3; + if (checkEdge(triangle1[i], triangle1[ni], triangle2[0], epsilon) && + checkEdge(triangle1[i], triangle1[ni], triangle2[1], epsilon) && + checkEdge(triangle1[i], triangle1[ni], triangle2[2], epsilon)) + return false; + } + + // See if there is an edge of triangle2 such that all vertices in triangle1 are on the opposite side + fixWinding(triangle2[0], triangle2[1], triangle2[2]); + for (int i = 0; i < 3; i++) { + int ni = (i + 1) % 3; + + if (checkEdge(triangle2[i], triangle2[ni], triangle1[0], epsilon) && + checkEdge(triangle2[i], triangle2[ni], triangle1[1], epsilon) && + checkEdge(triangle2[i], triangle2[ni], triangle1[2], epsilon)) + return false; + } + + return true; +} + +bool checkLineTriangleOverlap(QVector2D *triangle, QVector2D *line, float epsilon = 1.0/32) +{ + // See if all vertices of the triangle are on the same side of the line + bool s1 = determinant(line[0], line[1], triangle[0]) < 0; + auto s2 = determinant(line[0], line[1], triangle[1]) < 0; + auto s3 = determinant(line[0], line[1], triangle[2]) < 0; + // If all determinants have the same sign, then there is no overlap + if (s1 == s2 && s2 == s3) { + return false; + } + // See if there is an edge of triangle1 such that both vertices in line are on the opposite side + fixWinding(triangle[0], triangle[1], triangle[2]); + for (int i = 0; i < 3; i++) { + int ni = (i + 1) % 3; + if (checkEdge(triangle[i], triangle[ni], line[0], epsilon) && + checkEdge(triangle[i], triangle[ni], line[1], epsilon)) + return false; + } + + return true; +} + +// We could slightly optimize this if we did fixWinding in advance +bool checkTriangleContains (QVector2D pt, QVector2D v1, QVector2D v2, QVector2D v3, float epsilon = 1.0/32) +{ + float d1, d2, d3; + d1 = determinant(pt, v1, v2); + d2 = determinant(pt, v2, v3); + d3 = determinant(pt, v3, v1); + + bool allNegative = d1 < epsilon && d2 < epsilon && d3 < epsilon; + bool allPositive = d1 > epsilon && d2 > epsilon && d3 > epsilon; + + return allNegative || allPositive; +} + +// e1 is always a concave curve. e2 can be curve or line +static bool isOverlap(const QuadPath &path, qsizetype e1, qsizetype e2) +{ + const QuadPath::Element &element1 = path.elementAt(e1); + const QuadPath::Element &element2 = path.elementAt(e2); + + QVector2D t1[3] = { element1.startPoint(), element1.controlPoint(), element1.endPoint() }; + + if (element2.isLine()) { + QVector2D line[2] = { element2.startPoint(), element2.endPoint() }; + return checkLineTriangleOverlap(t1, line); + } else { + QVector2D t2[3] = { element2.startPoint(), element2.controlPoint(), element2.endPoint() }; + return checkTriangleOverlap(t1, t2); + } + + return false; +} + +static bool isTooSmallOrTooStraight(const QuadPath::Element &quad) +{ + constexpr float epsilon = 1.0; + if (qFuzzyCompare(quad.startPoint(), quad.endPoint())) + return true; + float c = crossProduct(quad.controlPoint(), quad.startPoint(), quad.endPoint()); + return abs(c) < epsilon; +} + +static bool isOverlap(const QuadPath &path, qsizetype index, const QVector2D &vertex) +{ + const QuadPath::Element &elem = path.elementAt(index); + return checkTriangleContains(vertex, elem.startPoint(), elem.controlPoint(), elem.endPoint()); +} + +//TODO: we could optimize by preprocessing e1, since we call this function multiple times on the same +// elements +static void handleOverlap(QuadPath &path, qsizetype e1, qsizetype e2, int recursionLevel = 0) +{ + if (!isOverlap(path, e1, e2)) { + return; + } + + if (recursionLevel > 8) { + qDebug() << "Triangle overlap: recursion level" << recursionLevel << "aborting!"; + return; + } + + if (path.elementAt(e1).childCount() > 1) { + // qDebug() << "Case 1, recursion level" << recursionLevel; + auto e11 = path.indexOfChildAt(e1, 0); + auto e12 = path.indexOfChildAt(e1, 1); + handleOverlap(path, e11, e2, recursionLevel + 1); + handleOverlap(path, e12, e2, recursionLevel + 1); + } else if (path.elementAt(e2).childCount() > 1) { + // qDebug() << "Case 2, recursion level" << recursionLevel; + auto e21 = path.indexOfChildAt(e2, 0); + auto e22 = path.indexOfChildAt(e2, 1); + handleOverlap(path, e1, e21, recursionLevel + 1); + handleOverlap(path, e1, e22, recursionLevel + 1); + } else { + path.splitElementAt(e1); + auto e11 = path.indexOfChildAt(e1, 0); + auto e12 = path.indexOfChildAt(e1, 1); + bool overlap1 = isOverlap(path, e11, e2); + bool overlap2 = isOverlap(path, e12, e2); + // qDebug() << "actual split, recursion level" << recursionLevel << "new overlaps" << + // overlap1 << overlap2; + + if (!overlap1 && !overlap2) + return; // no more overlap: success! + + // We need to split more: + if (path.elementAt(e2).isLine()) { + // Splitting a line won't help, so we just split e1 further + if (overlap1) + handleOverlap(path, e11, e2, recursionLevel + 1); + if (overlap2) + handleOverlap(path, e12, e2, recursionLevel + 1); + } else { + // See if splitting e2 works: + path.splitElementAt(e2); + auto e21 = path.indexOfChildAt(e2, 0); + auto e22 = path.indexOfChildAt(e2, 1); + if (overlap1) { + handleOverlap(path, e11, e21, recursionLevel + 1); + handleOverlap(path, e11, e22, recursionLevel + 1); + } + if (overlap2) { + handleOverlap(path, e12, e21, recursionLevel + 1); + handleOverlap(path, e12, e22, recursionLevel + 1); + } + } + } +} + +// Test if element contains a start point of another element +static void handleOverlap(QuadPath &path, qsizetype e1, const QVector2D vertex, + int recursionLevel = 0) +{ + // First of all: Ignore the next element: it trivially overlaps (maybe not necessary: we do check for strict containment) + if (vertex == path.elementAt(e1).endPoint() || !isOverlap(path, e1, vertex)) + return; + if (recursionLevel > 8) { + qDebug() << "Vertex overlap: recursion level" << recursionLevel << "aborting!"; + return; + } + + // Don't split if we're already split + if (path.elementAt(e1).childCount() == 0) + path.splitElementAt(e1); + + handleOverlap(path, path.indexOfChildAt(e1, 0), vertex, recursionLevel + 1); + handleOverlap(path, path.indexOfChildAt(e1, 1), vertex, recursionLevel + 1); +} + +static void splitMeOneMoreTime(QuadPath &path, int index) +{ + auto &element = path.elementAt(index); + if (element.childCount() > 1) { + int child0 = path.indexOfChildAt(index, 0); + int child1 = path.indexOfChildAt(index, 1); + splitMeOneMoreTime(path, child0); + splitMeOneMoreTime(path, child1); + } else if (!element.isLine()) { + if (!isTooSmallOrTooStraight(element)) + path.splitElementAt(index); + } +} + +void QQuickShapeCurveRenderer::solveOverlaps(QuadPath &path) +{ + for (qsizetype i = 0; i < path.elementCount(); i++) { + auto &element = path.elementAt(i); + // only concave curve overlap is problematic, as long as we don't allow self-intersecting curves + if (element.isLine() || element.isConvex()) + continue; + + for (qsizetype j = 0; j < path.elementCount(); j++) { + if (i == j) + continue; // Would be silly to test overlap with self + auto &other = path.elementAt(j); + if (!other.isConvex() && !other.isLine() && j < i) + continue; // We have already tested this combination, so no need to test again + handleOverlap(path, i, j); + } + } + + static const int handleConcaveJoint = qEnvironmentVariableIntValue("QT_QUICKSHAPES_WIP_CONCAVE_JOINT"); + if (handleConcaveJoint) { + // Note that the joint between two non-concave elements can also be concave, so we have to + // test all convex elements to see if there is a vertex in any of them. We could do it the other way + // by identifying concave joints, but then we would have to know which side is the inside + // TODO: optimization potential! Maybe do that at the same time as we identify concave curves? + + // We do this in a separate loop, since the triangle/triangle test above is more expensive, and + // if we did this first, there would be more triangles to test + for (qsizetype i = 0; i < path.elementCount(); i++) { + auto &element = path.elementAt(i); + if (!element.isConvex()) + continue; + + for (qsizetype j = 0; j < path.elementCount(); j++) { + // We only need to check one point per element, since all subpaths are closed + // Could do smartness to skip elements that cannot overlap, but let's do it the easy way first + if (i == j) + continue; + const auto &other = path.elementAt(j); + handleOverlap(path, i, other.startPoint()); + } + } + } + + // split all curves one extra time, to avoid near-overlapping triangles. + // (Probably cheaper than trying to detect that case) + for (int i = 0; i < path.elementCount(); i++) + splitMeOneMoreTime(path, i); +} + +//#define ROUNDING +QVector QQuickShapeCurveRenderer::addPathNodesDelauneyTest(const PathData &pathData, NodeList *debugNodes) +{ + const QuadPath &thePath = pathData.path; + if (thePath.elementCount() == 0) + return QVector{}; + + QList vertices; + QList edges; + + // Generate the graph for the triangulator + // Start with adding an external bounding box + + auto boundingRect = pathData.path.controlPointRect(); + float extraBorder = qMax(boundingRect.width(), boundingRect.height()) * 0.10; // 10% extra + boundingRect.adjust(-extraBorder, -extraBorder, extraBorder, extraBorder); + + vertices << QtPathVertex{ QVector2D(boundingRect.topLeft()), 1 }; + vertices << QtPathVertex{ QVector2D(boundingRect.topRight()), 2 }; + vertices << QtPathVertex{ QVector2D(boundingRect.bottomRight()), 3 }; + vertices << QtPathVertex{ QVector2D(boundingRect.bottomLeft()), 4 }; + edges.append({ 0, 1, 1 }); + edges.append({ 1, 2, 2 }); + edges.append({ 2, 3, 3 }); + edges.append({ 3, 0, 4 }); + + // Then add all the edges that we want to have + // TODO: We ought actually have an index-based structure in QuadPath. + // For now, just duplicate points + +#if !defined(ROUNDING) + QHash, int> pointHash; +#else + QHash, int> pointHash; +#endif + thePath.iterateElements([&](const QuadPath::Element &element) { + auto findOrInsert = [&pointHash, &vertices](const QVector2D &p) { +#if defined(ROUNDING) + auto key = qMakePair(QFixed::fromReal(qRound(p.x() * 32.0) / 32.0).value(), + QFixed::fromReal(qRound(p.y() * 32.0) / 32.0).value()); +#else + auto key = qMakePair(p.x(), p.y()); +#endif + + auto it = pointHash.find(key); + if (it != pointHash.end()) { + return it.value(); + } else { + vertices << QtPathVertex{ p, 42}; + pointHash.insert(key, int(vertices.size() - 1)); + return int(vertices.size() - 1); + } + }; + + + quint32 startIdx = findOrInsert(element.startPoint()); + quint32 endIdx = findOrInsert(element.endPoint()); + + edges.append({ startIdx, endIdx, 42 }); + if (!element.isLine()) { + quint32 controlIdx = findOrInsert(element.controlPoint()); + edges.append({ startIdx, controlIdx, 42 }); + edges.append({ endIdx, controlIdx, 42 }); + } + }); + + //Add a "super triangle" for the Delauney algorithm + +{ + // Calculate the dimensions and center of the bounding box + float width = boundingRect.width(); + float height = boundingRect.height(); + float centerX = boundingRect.center().x(); + float centerY = boundingRect.center().y(); + + // Create a super-triangle with vertices outside the bounding box + int l = vertices.length(); + QtPathVertex v1 = {QVector2D(centerX, centerY - 3 * height), l++}; // Top vertex + QtPathVertex v2 = {QVector2D(centerX - 3 * width, centerY + 3 * height), l++}; // Bottom-left vertex + QtPathVertex v3 = {QVector2D(centerX + 3 * width, centerY + 3 * height), l}; // Bottom-right vertex + + vertices << v1 << v2 << v3; + +} + + QList triangles = qtDelaunayTriangulator(vertices, edges, pathData.originalPath); + + if (!triangles.isEmpty()) + testTriangulation(vertices, edges, triangles); + + // Quick-and-dirty triangle visualisation: + + // Semi-transparent triangle fill to debug coverage and overlap + auto *fillNode = new QQuickShapeLoopBlinnNode(QQuickAbstractPathRenderer::NoGradient); + + QSGGeometry *g = new QSGGeometry(QQuickShapeLoopBlinnNode::attributes(), + vertices.size(), triangles.size() * 3, QSGGeometry::UnsignedIntType); + + fillNode->setGeometry(g); + g->setDrawingMode(QSGGeometry::DrawTriangles); + m_rootNode->appendChildNode(fillNode); + + QVector vertexData; + vertexData.reserve(vertices.size()); + QVector indexData; + indexData.reserve(triangles.size() * 3); + + fillNode->color = QColor(0x7f, 0xff, 0, 0x7f); + for (const auto &v : std::as_const(vertices)) + vertexData.append({ v.point.x(), v.point.y(), 0.0f, 1.0f, -1.0f, 0.0f, 0.0f, 0.0f, 0.0f }); + for (const auto &t : std::as_const(triangles)) + indexData << t.v1Index << t.v2Index << t.v3Index; + + memcpy(g->vertexData(), + vertexData.data(), + g->vertexCount() * g->sizeOfVertex()); + memcpy(g->indexData(), + indexData.data(), + indexData.size() * g->sizeOfIndex()); + + + // A wireframe node to show the edges + QQuickShapeWireFrameNode *wfNode = new QQuickShapeWireFrameNode; + QSGGeometry *wfg = wfNode->geometry(); + + QVarLengthArray indices; + int iidx = 0; + QVarLengthArray wfVertices; + + auto addWireframeVertex = [&](int vertexIndex, int index){ + float u = index % 3 == 0 ? 1.0f : 0.0f; + float v = index % 3 == 1 ? 1.0f : 0.0f; + float w = index % 3 == 2 ? 1.0f : 0.0f; + + auto pt = vertices[vertexIndex].point; + indices.append(iidx++); + wfVertices.append({ pt.x(), pt.y(), u, v, w }); + }; + + for (const auto &t : std::as_const(triangles)) { + addWireframeVertex(t.v1Index, 0); + addWireframeVertex(t.v2Index, 1); + addWireframeVertex(t.v3Index, 2); + } + + if (wfg->indexType() != QSGGeometry::UnsignedIntType) { + wfg = new QSGGeometry(QQuickShapeWireFrameNode::attributes(), + wfVertices.size(), + indices.size(), + QSGGeometry::UnsignedIntType); + wfNode->setGeometry(wfg); + } else { + wfg->allocate(wfVertices.size(), indices.size()); + } + + wfg->setDrawingMode(QSGGeometry::DrawTriangles); + memcpy(wfg->indexData(), + indices.data(), + indices.size() * wfg->sizeOfIndex()); + memcpy(wfg->vertexData(), + wfVertices.data(), + wfg->vertexCount() * wfg->sizeOfVertex()); + + m_rootNode->appendChildNode(wfNode); + debugNodes->append(wfNode); + return { fillNode }; +} + +QT_END_NAMESPACE diff --git a/src/quickshapes/qquickshapecurverenderer_p.h b/src/quickshapes/qquickshapecurverenderer_p.h new file mode 100644 index 0000000000..761b9437ec --- /dev/null +++ b/src/quickshapes/qquickshapecurverenderer_p.h @@ -0,0 +1,345 @@ +// Copyright (C) 2022 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only + +#ifndef QQUICKSHAPECURVERENDERER_P_H +#define QQUICKSHAPECURVERENDERER_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists for the convenience +// of a number of Qt sources files. This header file may change from +// version to version without notice, or even be removed. +// +// We mean it. +// + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +QT_BEGIN_NAMESPACE + +class QuadPath +{ +public: + void moveTo(const QVector2D &to) + { + subPathToStart = true; + currentPoint = to; + } + + void lineTo(const QVector2D &to) + { + addElement({}, to, true); + } + + void quadTo(const QVector2D &control, const QVector2D &to) + { + addElement(control, to); + } + + QRectF controlPointRect() const; + + Qt::FillRule fillRule() const { return m_fillRule; } + void setFillRule(Qt::FillRule rule) { m_fillRule = rule; } + + void reserve(qsizetype size) { m_elements.reserve(size); } + qsizetype elementCount() const { return m_elements.size(); } + qsizetype elementCountRecursive() const; + + static QuadPath fromPainterPath(const QPainterPath &path); + QPainterPath toPainterPath() const; + QuadPath subPathsClosed() const; + + class Element + { + public: + Element () + : m_isSubpathStart(false), m_isSubpathEnd(false), m_isLine(false) + { + } + + bool isSubpathStart() const + { + return m_isSubpathStart; + } + + bool isSubpathEnd() const + { + return m_isSubpathEnd; + } + + bool isLine() const + { + return m_isLine; + } + + bool isConvex() const + { + return m_curvatureFlags & Convex; + } + + QVector2D startPoint() const + { + return sp; + } + + QVector2D controlPoint() const + { + return cp; + } + + QVector2D endPoint() const + { + return ep; + } + + QVector2D midPoint() const + { + return isLine() ? 0.5f * (sp + ep) : (0.25f * sp) + (0.5f * cp) + (0.25 * ep); + } + + QVector3D uvForPoint(QVector2D p) const; + + qsizetype childCount() const { return m_numChildren; } + + qsizetype indexOfChild(qsizetype childNumber) const + { + Q_ASSERT(childNumber >= 0 && childNumber < childCount()); + return -(m_firstChildIndex + 1 + childNumber); + } + + QVector2D pointAtFraction(float t) const; + + QVector2D tangentAtFraction(float t) const + { + return isLine() ? (ep - sp) : ((1 - t) * 2 * (cp - sp)) + (t * 2 * (ep - cp)); + } + + QVector2D normalAtFraction(float t) const + { + const QVector2D tan = tangentAtFraction(t); + return QVector2D(-tan.y(), tan.x()); + } + + private: + int intersectionsAtY(float y, float *fractions) const; + + enum CurvatureFlags : quint8 { + CurvatureUndetermined = 0, + FillOnRight = 1, + Convex = 2 + }; + + QVector2D sp; + QVector2D cp; + QVector2D ep; + int m_firstChildIndex = 0; + quint8 m_numChildren = 0; + CurvatureFlags m_curvatureFlags = CurvatureUndetermined; + quint8 m_isSubpathStart : 1; + quint8 m_isSubpathEnd : 1; + quint8 m_isLine : 1; + friend class QuadPath; + friend QDebug operator<<(QDebug, const QuadPath::Element &); + }; + + template + void iterateChildrenOf(Element &e, Func &&lambda) + { + const qsizetype lastChildIndex = e.m_firstChildIndex + e.childCount() - 1; + for (qsizetype i = e.m_firstChildIndex; i <= lastChildIndex; i++) { + Element &c = m_childElements[i]; + if (c.childCount() > 0) + iterateChildrenOf(c, lambda); + else + lambda(c); + } + } + + template + void iterateChildrenOf(const Element &e, Func &&lambda) const + { + const qsizetype lastChildIndex = e.m_firstChildIndex + e.childCount() - 1; + for (qsizetype i = e.m_firstChildIndex; i <= lastChildIndex; i++) { + const Element &c = m_childElements[i]; + if (c.childCount() > 0) + iterateChildrenOf(c, lambda); + else + lambda(c); + } + } + + template + void iterateElements(Func &&lambda) + { + for (auto &e : m_elements) { + if (e.childCount() > 0) + iterateChildrenOf(e, lambda); + else + lambda(e); + } + } + + template + void iterateElements(Func &&lambda) const + { + for (auto &e : m_elements) { + if (e.childCount() > 0) + iterateChildrenOf(e, lambda); + else + lambda(e); + } + } + + void splitElementAt(qsizetype index); + Element &elementAt(qsizetype i) { return i < 0 ? m_childElements[-(i + 1)] : m_elements[i]; } + const Element &elementAt(qsizetype i) const + { + return i < 0 ? m_childElements[-(i + 1)] : m_elements[i]; + } + + qsizetype indexOfChildAt(qsizetype i, qsizetype childNumber) const + { + return elementAt(i).indexOfChild(childNumber); + } + + void addCurvatureData(); + bool contains(const QVector2D &v) const; + +private: + void addElement(const QVector2D &control, const QVector2D &to, bool isLine = false); + Element::CurvatureFlags coordinateOrderOfElement(const Element &element) const; + static bool isControlPointOnLeft(const Element &element); + static QVector2D closestPointOnLine(const QVector2D &start, + const QVector2D &end, + const QVector2D &p); + static bool isPointOnLeft(const QVector2D &p, const QVector2D &sp, const QVector2D &ep); + static bool isPointOnLine(const QVector2D &p, const QVector2D &sp, const QVector2D &ep); + static bool isPointNearLine(const QVector2D &p, const QVector2D &sp, const QVector2D &ep); + + friend QDebug operator<<(QDebug, const QuadPath &); + + bool subPathToStart = true; + Qt::FillRule m_fillRule = Qt::OddEvenFill; + QVector2D currentPoint; + QList m_elements; + QList m_childElements; +}; + +QDebug operator<<(QDebug, const QuadPath::Element &); +QDebug operator<<(QDebug, const QuadPath &); + +class QQuickShapeCurveNode : public QSGNode +{ +public: + QQuickShapeCurveNode(); +}; + + +class QQuickShapeCurveRenderer : public QQuickAbstractPathRenderer +{ +public: + QQuickShapeCurveRenderer(QQuickItem *) + : m_rootNode(nullptr) + { } + ~QQuickShapeCurveRenderer() override; + + void beginSync(int totalCount, bool *countChanged) override; + void setPath(int index, const QQuickPath *path) override; + void setStrokeColor(int index, const QColor &color) override; + void setStrokeWidth(int index, qreal w) override; + void setFillColor(int index, const QColor &color) override; + void setFillRule(int index, QQuickShapePath::FillRule fillRule) override; + void setJoinStyle(int index, QQuickShapePath::JoinStyle joinStyle, int miterLimit) override; + void setCapStyle(int index, QQuickShapePath::CapStyle capStyle) override; + void setStrokeStyle(int index, QQuickShapePath::StrokeStyle strokeStyle, + qreal dashOffset, const QVector &dashPattern) override; + void setFillGradient(int index, QQuickShapeGradient *gradient) override; + void endSync(bool async) override; + void setAsyncCallback(void (*)(void *), void *) override; + Flags flags() const override { return Flags{}; } + + void updateNode() override; + + void setRootNode(QQuickShapeCurveNode *node); + + using NodeList = QVector; + + enum DirtyFlag + { + GeometryDirty = 1, + UniformsDirty = 2 + }; + + //### PathData used to be private, but I am using it in a static function. TODO: FIX ### + + struct PathData { + + bool isFillVisible() const { return fillColor.alpha() > 0 || gradientType != NoGradient; } + + bool isStrokeVisible() const + { + return validPenWidth && pen.color().alpha() > 0 && pen.style() != Qt::NoPen; + } + + bool useFragmentShaderStroker() const; + + FillGradientType gradientType = NoGradient; + GradientDesc gradient; + QPainterPath fillPath; + QPainterPath originalPath; + QuadPath path; + QColor fillColor; + Qt::FillRule fillRule = Qt::OddEvenFill; + QPen pen; + int m_dirty = 0; + bool validPenWidth = true; + bool convexConcaveResolved = false; + + NodeList fillNodes; + NodeList strokeNodes; + NodeList debugNodes; + }; + + enum DebugVisualizationOption { + NoDebug = 0, + DebugCurves = 0x01, + DebugWireframe = 0x02 + }; + + Q_QUICKSHAPES_PRIVATE_EXPORT static int debugVisualization(); + Q_QUICKSHAPES_PRIVATE_EXPORT static void setDebugVisualization(int options); + +private: + void deleteAndClear(NodeList *nodeList); + + QVector addPathNodesBasic(const PathData &pathData, NodeList *debugNodes); + + QVector addPathNodesDelauneyTest(const PathData &pathData, NodeList *debugNodes); + + QSGGeometryNode *addLoopBlinnNodes(const QTriangleSet &triangles, + const QVarLengthArray &extraIndices, + int startConcaveCurves, + const PathData &pathData, + NodeList *debugNodes); + + void solveOverlaps(QuadPath &path); + + QQuickShapeCurveNode *m_rootNode; + QVector m_paths; + static int debugVisualizationFlags; +}; + +QT_END_NAMESPACE + +#endif // QQUICKSHAPECURVERENDERER_P_H diff --git a/src/quickshapes/qquickshapecurverenderer_p_p.h b/src/quickshapes/qquickshapecurverenderer_p_p.h new file mode 100644 index 0000000000..4904cbc2b9 --- /dev/null +++ b/src/quickshapes/qquickshapecurverenderer_p_p.h @@ -0,0 +1,77 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only + + +#ifndef QQUICKSHAPECURVERENDERER_P_P_H +#define QQUICKSHAPECURVERENDERER_P_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists for the convenience +// of a number of Qt sources files. This header file may change from +// version to version without notice, or even be removed. +// +// We mean it. +// + +#include +#include +#include +#include + +QT_BEGIN_NAMESPACE + +Q_DECLARE_LOGGING_CATEGORY(lcShapeCurveRenderer); + +struct QtPathVertex +{ + QVector2D point; + int id; + quint32 binX = 0; + quint32 binY = 0; +}; + +struct QtPathEdge +{ + quint32 startIndex; + quint32 endIndex; + int id; +}; + +struct QtPathTriangle +{ + QtPathTriangle(quint32 i1, quint32 i2, quint32 i3, int d) : v1Index(i1), v2Index(i2), v3Index(i3), id(d) {} + quint32 v1Index; + quint32 v2Index; + quint32 v3Index; + + quint32 adjacentTriangle1 = quint32(-1); // Adjacent to v1-v2 + quint32 adjacentTriangle2 = quint32(-1); // Adjacent to v2-v3 + quint32 adjacentTriangle3 = quint32(-1); // Adjacent to v3-v1 + + // Used by triangulator + quint32 lastSeenVertex = quint32(-1); + + // Should this triangle be rendered? Set to false for triangles connecting to super-triangle + bool isValid = true; + + int id; +}; + +constexpr bool operator==(const QtPathTriangle& lhs, const QtPathTriangle& rhs) +{ + return lhs.id == rhs.id + && lhs.v1Index == rhs.v1Index + && lhs.v2Index == rhs.v2Index + && lhs.v3Index == rhs.v3Index; +} + +class QBezier; +Q_QUICKSHAPES_PRIVATE_EXPORT QPolygonF qt_toQuadratics(const QBezier &b, qreal errorLimit = 0.2); +Q_QUICKSHAPES_PRIVATE_EXPORT QList qtDelaunayTriangulator(const QList &vertices, const QList &edges, const QPainterPath &path); + +QT_END_NAMESPACE + +#endif //QQUICKSHAPECURVERENDERER_P_P_H diff --git a/src/quickshapes/qt_delaunay_triangulator.cpp b/src/quickshapes/qt_delaunay_triangulator.cpp new file mode 100644 index 0000000000..76ee616fc7 --- /dev/null +++ b/src/quickshapes/qt_delaunay_triangulator.cpp @@ -0,0 +1,1436 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only + +#include "qquickshapecurverenderer_p_p.h" +#include +#include +#include +#include +#include +#include +#include + +// #define QQUICKSHAPES_DEBUG_IMAGE 1 + +QT_BEGIN_NAMESPACE + +Q_LOGGING_CATEGORY(lcShapeTiming, "qt.shape.timing"); + +// Check if a point is inside the circumcircle of a triangle defined by three vertices (v1, v2, v3). +// It uses the determinant of a matrix formed from the coordinates of the point and the vertices +// to determine if the point is inside the circumcircle. +// If the determinant is > 0, the point is inside the circumcircle. +inline bool isPointInCircumcircle(const QVector2D &point, + const QVector2D &v1_, const QVector2D &v2_, const QVector2D &v3_) +{ + QVector2D v1 = v1_; + QVector2D v2 = v2_; + QVector2D v3 = v3_; + + // Vertices must be in ccw order + if ( ( ( (v2.x() - v1.x()) * (v3.y() - v1.y()) ) - ( (v3.x() - v1.x()) * (v2.y() - v1.y()) ) ) <= 0.0f) { + qSwap(v2, v3); + } + +#if 1 + float x13 = v1.x() - v3.x(); + float y13 = v1.y() - v3.y(); + float x23 = v2.x() - v3.x(); + float y23 = v2.y() - v3.y(); + float x1p = v1.x() - point.x(); + float y1p = v1.y() - point.y(); + float x2p = v2.x() - point.x(); + float y2p = v2.y() - point.y(); + + return (x13*x23 + y13*y23) * (x2p*y1p - x1p*y2p) < (x23*y13 - x13*y23) * (x2p*x1p + y1p*y2p); + +#else + float ax = v1.x() - point.x(); + float ay = v1.y() - point.y(); + float bx = v2.x() - point.x(); + float by = v2.y() - point.y(); + float cx = v3.x() - point.x(); + float cy = v3.y() - point.y(); + + float det = (ax * ax + ay * ay) * (bx * cy - cx * by) - + (bx * bx + by * by) * (ax * cy - cx * ay) + + (cx * cx + cy * cy) * (ax * by - bx * ay); + + return det > 0.0f; +#endif +} + +// Retrieves quad corresponding to t1 and t2 (assuming that they share one edge) +// In the end, the new diagonal is (i1, i3) and (i1, i2, i3, i4) is the ordered vertices +// in the quad. +inline void findQuad(const QtPathTriangle &t1, const QtPathTriangle &t2, + quint32 *i1, quint32 *i2, quint32 *i3, quint32 *i4) +{ + // First find index in t1 which is not in t2 and call this i1 (with i2, and i4 completing t1) + if (t1.v1Index != t2.v1Index && t1.v1Index != t2.v2Index && t1.v1Index != t2.v3Index) { + (*i1) = t1.v1Index; + (*i2) = t1.v2Index; + (*i4) = t1.v3Index; + } else if (t1.v2Index != t2.v1Index && t1.v2Index != t2.v2Index && t1.v2Index != t2.v3Index) { + (*i1) = t1.v2Index; + (*i2) = t1.v3Index; + (*i4) = t1.v1Index; + } else { + Q_ASSERT(t1.v3Index != t2.v1Index && t1.v3Index != t2.v2Index && t1.v3Index != t2.v3Index); + (*i1) = t1.v3Index; + (*i2) = t1.v1Index; + (*i4) = t1.v2Index; + } + + // Then find (*i3) as index in t2 which is not in t1 + if (t2.v1Index != t1.v1Index && t2.v1Index != t1.v2Index && t2.v1Index != t1.v3Index) { + (*i3) = t2.v1Index; + } else if (t2.v2Index != t1.v1Index && t2.v2Index != t1.v2Index && t2.v2Index != t1.v3Index) { + (*i3) = t2.v2Index; + } else { + Q_ASSERT(t2.v3Index != t1.v1Index && t2.v3Index != t1.v2Index && t2.v3Index != t1.v3Index); + (*i3) = t2.v3Index; + } +} + +inline void swapDiagonal(QtPathTriangle *t1, + QtPathTriangle *t2, + quint32 idx = quint32(-1)) +{ + quint32 i1, i2, i3, i4; + findQuad(*t1, *t2, &i1, &i2, &i3, &i4); + Q_ASSERT(idx == quint32(-1) || t2->v1Index == idx || t2->v2Index == idx || t2->v3Index == idx); + + t2->v1Index = i1; + t2->v2Index = i3; + t2->v3Index = i2; + + t1->v1Index = i1; + t1->v2Index = i3; + t1->v3Index = i4; +} + +inline bool isPointInTriangle(const QVector2D &v, + const QVector2D &v1, + const QVector2D &v2, + const QVector2D &v3) +{ +#if 0 + QPolygonF p; + p.append(v1.toPointF()); + p.append(v2.toPointF()); + p.append(v3.toPointF()); + return p.containsPoint(v.toPointF(), Qt::OddEvenFill); +#else + if ( (v1.x() > v.x() && v2.x() > v.x() && v3.x() > v.x()) + || (v1.x() < v.x() && v2.x() < v.x() && v3.x() < v.x()) + || (v1.y() > v.y() && v2.y() > v.y() && v3.y() > v.y()) + || (v1.y() < v.y() && v2.y() < v.y() && v3.y() < v.y())) { + return false; + } + + // Barycentric scalars + double denom = (v2.y() - v3.y()) * (v1.x() - v3.x()) + + (v3.x() - v2.x()) * (v1.y() - v3.y()); + double a = ((v2.y() - v3.y()) * (v.x() - v3.x()) + + (v3.x() - v2.x()) * (v.y() - v3.y())) + / denom; + double b = ((v3.y() - v1.y()) * (v.x() - v3.x()) + + (v1.x() - v3.x()) * (v.y() - v3.y())) + / denom; + double c = 1.0 - a - b; + + if (qFuzzyIsNull(a)) + a = 0.0; + if (qFuzzyIsNull(b)) + b = 0.0; + if (qFuzzyIsNull(c)) + c = 0.0; + + if (qFuzzyCompare(a, 1.0)) + a = 1.0; + if (qFuzzyCompare(b, 1.0)) + b = 1.0; + if (qFuzzyCompare(c, 1.0)) + c = 1.0; + + return a >= 0.0 && a <= 1.0 + && b >= 0.0 && b <= 1.0 + && c >= 0.0 && c <= 1.0; +#endif +} + +inline void saveDebugImage(bool shouldDraw, + const QPainterPath &path, + const QString &label, + const QList &triangles, + const QList &vertices, + const QList &highlightedPoints = QList{}, + const QList &highlightedTriangles = QList{}, + const QList > &highlightedEdges = QList >{}) +{ +#if defined(QQUICKSHAPES_DEBUG_IMAGE) + shouldDraw = true; +#endif + + static bool zoom = qEnvironmentVariableIntValue("QT_QUICKSHAPES_ZOOM_DEBUG_IMAGE"); + + if (!shouldDraw) + return; + + static int counter = 0; + + int size = 3000; + QImage image(size, size, QImage::Format_ARGB32); + image.fill(Qt::black); + + QList vs; + + if (zoom) { + for (auto it : highlightedPoints) + vs.append(vertices.at(it).point); + + for (auto it : highlightedEdges) { + vs.append(vertices.at(it.first).point); + vs.append(vertices.at(it.second).point); + } + + for (auto it : highlightedTriangles) { + vs.append(vertices.at(triangles.at(it).v1Index).point); + vs.append(vertices.at(triangles.at(it).v2Index).point); + vs.append(vertices.at(triangles.at(it).v3Index).point); + } + } else { + for (int i = 0; i < vertices.size() - 3; ++i) + vs.append(vertices.at(i).point); + } + + float minX = 0.0f; + float maxX = 0.0f; + float minY = 0.0f; + float maxY = 0.0f; + for (int i = 0; i < vs.size(); ++i) { + QVector2D p = vs.at(i); + if (i == 0 || minX > p.x()) + minX = p.x(); + if (i == 0 || maxX < p.x()) + maxX = p.x(); + if (i == 0 || minY > p.y()) + minY = p.y(); + if (i == 0 || maxY < p.y()) + maxY = p.y(); + } + + minX -= (maxX - minX) * 0.05f; + minY -= (maxY - minY) * 0.05f; + maxX += (maxX - minX) * 0.05f; + maxY += (maxY - minY) * 0.05f; + + { + QPainter painter(&image); + + float w = maxX - minX; + float h = maxY - minY; + + float max = qMax(w, h); + + painter.save(); + painter.scale(size / max, size / max); + painter.translate(-QPointF(minX, minY)); + + float fontSize = 18 * max / size; + + QFont font = painter.font(); + font.setPixelSize(qCeil(fontSize)); + + painter.setFont(font); + + if (!path.isEmpty()) { + painter.save(); + painter.setBrush(Qt::darkGray); + painter.drawPath(path); + painter.setOpacity(1); + painter.restore(); + } + + for (int i = 0; i < triangles.size(); ++i) { + QtPathTriangle triangle = triangles.at(i); + QPolygonF polygon; + polygon.append(vertices.at(triangle.v1Index).point.toPointF()); + polygon.append(vertices.at(triangle.v2Index).point.toPointF()); + polygon.append(vertices.at(triangle.v3Index).point.toPointF()); + + QPen pen(Qt::red, 3); + pen.setCosmetic(true); + + painter.setPen(pen); + painter.drawPolygon(polygon); + + painter.setPen(Qt::white); + painter.drawText(vertices.at(triangle.v1Index).point.toPointF(), + QString::number(triangle.v1Index)); + painter.drawText(vertices.at(triangle.v2Index).point.toPointF(), + QString::number(triangle.v2Index)); + painter.drawText(vertices.at(triangle.v3Index).point.toPointF(), + QString::number(triangle.v3Index)); + } + + for (int i = 0; i < highlightedTriangles.size(); ++i) { + int highlightedTriangle = highlightedTriangles.at(i); + QColor color = i > 0 ? Qt::magenta : Qt::green; + + QtPathTriangle triangle = triangles.at(highlightedTriangle); + QPolygonF polygon; + polygon.append(vertices.at(triangle.v1Index).point.toPointF()); + polygon.append(vertices.at(triangle.v2Index).point.toPointF()); + polygon.append(vertices.at(triangle.v3Index).point.toPointF()); + + QPen pen(color, 3); + pen.setCosmetic(true); + + painter.setPen(pen); + painter.drawPolygon(polygon); + + } + + for (int i = 0; i < highlightedPoints.size(); ++i) { + int highlightedPoint = highlightedPoints.at(i); + QPointF point = vertices.at(highlightedPoint).point.toPointF(); + + painter.setBrush(Qt::yellow); + + QPen pen(Qt::yellow, 3); + pen.setCosmetic(true); + painter.setPen(pen); + painter.drawEllipse(point, 5 * max / size, 5 * max / size); + } + + for (int i = 0; i < highlightedEdges.size(); ++i) { + auto highlightedEdge = highlightedEdges.at(i); + QLineF line(vertices.at(highlightedEdge.first).point.toPointF(), + vertices.at(highlightedEdge.second).point.toPointF()); + + QColor color = i == 0 ? Qt::yellow : Qt::cyan; + QPen pen(color, 3); + pen.setCosmetic(true); + painter.setPen(pen); + + painter.drawLine(line); + + } + + painter.setPen(Qt::white); + painter.setBrush(Qt::white); + + painter.restore(); + + QFont f = painter.font(); + f.setPixelSize(18); + painter.setFont(f); + QFontMetricsF fm(f); + + painter.fillRect(QRectF(0, size - fm.height() * 3, size, fm.height() * 3), Qt::red); + painter.drawText(QRectF(0, size - fm.height() * 3, size, fm.height() * 3), + Qt::AlignCenter, + QStringLiteral("%1, %2 triangles").arg(label).arg(triangles.size())); + } + + image.save(QStringLiteral("delaunay%1.png").arg(counter++)); +} + +// Checks if the quadrilateral made up of triangle1 and triangle2 is strictly convex +inline bool isConvexQuadrilateral(const QList &vertices, + const QtPathTriangle &triangle1, + const QtPathTriangle &triangle2) +{ + quint32 quad[4]; + + findQuad(triangle1, triangle2, &quad[0], &quad[1], &quad[2], &quad[3]); + + int mask = 0; + // Check if the cross product of all adjacent lines points the same way + for (int i = 0; i < 4 && mask <= 2; ++i) { + quint32 p1 = quad[i]; + quint32 p2 = quad[(i + 1) % 4]; + quint32 p3 = quad[(i + 2) % 4]; + + QVector2D v1 = vertices.at(p2).point - vertices.at(p1).point; + QVector2D v2 = vertices.at(p3).point - vertices.at(p2).point; + + // Parallel lines + if ((qFuzzyIsNull(v1.x()) && qFuzzyIsNull(v2.x())) + || (qFuzzyIsNull(v1.y()) && qFuzzyIsNull(v2.y()))) { + mask = 3; + } + + if (!qFuzzyIsNull(v1.x()) && qFuzzyIsNull(v2.x()) && qFuzzyIsNull(v1.y()) && qFuzzyIsNull(v2.y())) { + float v1Slope = v1.y() / v1.x(); + float v2Slope = v2.y() / v2.x(); + + if (mask <= 2 && (qFuzzyCompare(v1Slope, v2Slope) || qFuzzyCompare(v1Slope, -v2Slope))) + mask = 3; + } + +// if (mask <= 2 && (v1.normalized() == v2.normalized() || v1.normalized() == -v2.normalized())) +// mask |= 3; + + if (mask <= 2) { + float c = v1.x() * v2.y() - v1.y() * v2.x(); + if (c >= 0.0f) + mask |= 1; + else + mask |= 2; + } + + + } + return mask <= 2; +} + +inline bool lineIntersects(const QVector2D &v1_, const QVector2D &v2_, + const QVector2D &u1_, const QVector2D &u2_) +{ +#if 1 + QPointF v1 = v1_.toPointF(); + QPointF v2 = v2_.toPointF(); + QPointF u1 = u1_.toPointF(); + QPointF u2 = u2_.toPointF(); + + if ( (v1.x() < u1.x() && v1.x() < u2.x() && v2.x() < u1.x() && v2.x() < u2.x()) + || (v1.x() > u1.x() && v1.x() > u2.x() && v2.x() > u1.x() && v2.x() > u2.x()) + || (v1.y() < u1.y() && v1.y() < u2.y() && v2.y() < u1.y() && v2.y() < u2.y()) + || (v1.y() > u1.y() && v1.y() > u2.y() && v2.y() > u1.y() && v2.y() > u2.y())) { + return false; + } + + // Copied from QLineF::intersects() with some changes to ordering to speed + // up for our use case + const QPointF a = v2 - v1; + const QPointF b = u1 - u2; + const QPointF c = v1 - u1; + + const qreal denominator = a.y() * b.x() - a.x() * b.y(); + if (denominator == 0.0 || !qt_is_finite(denominator)) + return false; + + const qreal reciprocal = 1.0 / denominator; + qreal na = (b.y() * c.x() - b.x() * c.y()) * reciprocal; + if (na < 0.0 || na > 1.0) + return false; + + qreal nb = (a.x() * c.y() - a.y() * c.x()) * reciprocal; + if (nb < 0.0 || nb > 1.0) + return false; + + const QPointF intersectionPoint = v1 + a * na; + return intersectionPoint != v1 && intersectionPoint != v2 && intersectionPoint != u1 && intersectionPoint != u2; + return true; +#else + QLineF v(v1.toPointF(), v2.toPointF()); + QLineF u(u1.toPointF(), u2.toPointF()); + + QPointF intersectionPoint; + if (v.intersects(u, &intersectionPoint) == QLineF::BoundedIntersection) { + return (intersectionPoint != v.p1() && intersectionPoint != v.p2() + && intersectionPoint != u.p1() && intersectionPoint != u.p2()); + } + + return false; +#endif +} + +// Returns edges (and the adjacent triangles corresponding to them) which have visibility +// to the vertex +static void findAdjacentTrianglesOnSameSide(const QList &vertices, + quint32 vertexIdx, + const QtPathTriangle &triangle, + QVarLengthArray *possibleTriangles, + QVarLengthArray, 2> *edges = nullptr) +{ + Q_ASSERT(possibleTriangles != nullptr); + QVector2D v = vertices.at(vertexIdx).point; + QVector2D v1 = vertices.at(triangle.v1Index).point; + QVector2D v2 = vertices.at(triangle.v2Index).point; + QVector2D v3 = vertices.at(triangle.v3Index).point; + + bool d = ( ( ( (v2.x() - v1.x()) * (v3.y() - v1.y()) ) - ( (v3.x() - v1.x()) * (v2.y() - v1.y()) ) )) <= 0.0f; + + // Check v1 - v2 + if (triangle.adjacentTriangle1 != quint32(-1)) { + bool d1 = ((v.x() - v1.x()) * (v2.y() - v1.y()) - (v.y() - v1.y()) * (v2.x() - v1.x())) <= 0.0f; + if (d1 == d) { + possibleTriangles->append(triangle.adjacentTriangle1); + if (edges != nullptr) { + if (triangle.v1Index < triangle.v2Index) + edges->append(qMakePair(triangle.v1Index, triangle.v2Index)); + else + edges->append(qMakePair(triangle.v2Index, triangle.v1Index)); + } + } + } + + // Check v2 - v3 + if (triangle.adjacentTriangle2 != quint32(-1)) { + bool d2 = ((v.x() - v2.x()) * (v3.y() - v2.y()) - (v.y() - v2.y()) * (v3.x() - v2.x())) <= 0.0f; + if (d2 == d) { + possibleTriangles->append(triangle.adjacentTriangle2); + if (edges != nullptr) { + if (triangle.v2Index < triangle.v3Index) + edges->append(qMakePair(triangle.v2Index, triangle.v3Index)); + else + edges->append(qMakePair(triangle.v3Index, triangle.v2Index)); + } + } + } + + // Check v3 - v1 + if (triangle.adjacentTriangle3 != quint32(-1)) { + bool d3 = ((v.x() - v3.x()) * (v1.y() - v3.y()) - (v.y() - v3.y()) * (v1.x() - v3.x())) <= 0.0f; + if (d3 == d) { + possibleTriangles->append(triangle.adjacentTriangle3); + if (edges != nullptr) { + if (triangle.v3Index < triangle.v1Index) + edges->append(qMakePair(triangle.v3Index, triangle.v1Index)); + else + edges->append(qMakePair(triangle.v1Index, triangle.v3Index)); + } + } + } +} + +static quint32 findAdjacentTriangleOnSameSide(const QList &vertices, + quint32 vertexIdx, + const QtPathTriangle &triangle) +{ + QVarLengthArray possibleTriangles; + findAdjacentTrianglesOnSameSide(vertices, vertexIdx, triangle, &possibleTriangles); + + if (possibleTriangles.size() == 0) { + qCWarning(lcShapeCurveRenderer) << "Point" << vertexIdx + << "is not on the outside of any edge of triangle" + << triangle.v1Index << triangle.v2Index << triangle.v3Index; + return quint32(-1); + } + + return possibleTriangles.at(0); +} + +// CDT using Incremental Algorithm approach. +// the algorithm first inserts the edge's endpoints into the triangulation. +// Then, it finds the sequence of triangles intersected by the constraint +// edge and modifies them to include the edge. +QList qtDelaunayTriangulator(const QList &_vertices, + const QList &edges, + const QPainterPath &path) +{ + static bool saveImageOnError = qEnvironmentVariableIntValue("QT_QUICKSHAPES_SAVE_IMAGE_ON_ERROR") != 0; + static bool drawAllImages = qEnvironmentVariableIntValue("QT_QUICKSHAPES_SAVE_DEBUG_IMAGE") != 0; + static bool normalizeCoordinates = qEnvironmentVariableIntValue("QT_QUICKSHAPES_NORMALIZE_COORDINATES") != 0; + static bool validateResults = qEnvironmentVariableIntValue("QT_QUICKSHAPES_VALIDATE_RESULTS"); + + QList vertices; + if (Q_UNLIKELY(normalizeCoordinates)) { + vertices.reserve(_vertices.size()); + + qreal minX, minY, maxX, maxY; + for (int i = 0; i < _vertices.size(); ++i) { + const QtPathVertex &v = _vertices.at(i); + if (i == 0 || v.point.x() < minX) + minX = v.point.x(); + if (i == 0 || v.point.x() > maxX) + maxX = v.point.x(); + if (i == 0 || v.point.y() < minY) + minY = v.point.y(); + if (i == 0 || v.point.y() > maxY) + maxY = v.point.y(); + } + + for (int i = 0; i < _vertices.size(); ++i) { + QtPathVertex v = _vertices.at(i); + v.point -= QVector2D(minX, minY); + v.point /= QVector2D(maxX - minX, maxY - minY); + vertices.append(v); + } + } else { + vertices = _vertices; + } + + QList triangulation; + triangulation.reserve(2 * vertices.size() + 1); + + // Create a super-triangle + const quint32 l = int(vertices.size()); + QtPathTriangle superTriangle = { l - 3, l - 2, l - 1, 0}; + triangulation.append(superTriangle); + + QElapsedTimer t; + t.start(); + + typedef QPair Pair; + + QHash existingEdges; + QSet constrainedEdges; + existingEdges.reserve(3 * (2 * vertices.size() + 1)); + + auto registerAdjacency = [&triangulation](quint32 a, quint32 b, quint32 triangleIdx1, quint32 triangleIdx2) { + Q_ASSERT(triangleIdx1 < triangulation.size()); + Q_ASSERT(triangleIdx2 < triangulation.size()); + + QtPathTriangle &t1 = triangulation[triangleIdx1]; + QtPathTriangle &t2 = triangulation[triangleIdx2]; + + if ((t1.v1Index == a && t1.v2Index == b) || (t1.v1Index == b && t1.v2Index == a)) { + t1.adjacentTriangle1 = triangleIdx2; + } else if ((t1.v2Index == a && t1.v3Index == b) || (t1.v2Index == b && t1.v3Index == a)) { + t1.adjacentTriangle2 = triangleIdx2; + } else { + Q_ASSERT(((t1.v3Index == a && t1.v1Index == b) || (t1.v3Index == b && t1.v1Index == a))); + t1.adjacentTriangle3 = triangleIdx2; + } + + if ((t2.v1Index == a && t2.v2Index == b) || (t2.v1Index == b && t2.v2Index == a)) { + t2.adjacentTriangle1 = triangleIdx1; + } else if ((t2.v2Index == a && t2.v3Index == b) || (t2.v2Index == b && t2.v3Index == a)) { + t2.adjacentTriangle2 = triangleIdx1; + } else { + Q_ASSERT((t2.v3Index == a && t2.v1Index == b) || (t2.v3Index == b && t2.v1Index == a)); + t2.adjacentTriangle3 = triangleIdx1; + } + }; + + auto unregisterAdjacency = [&triangulation](quint32 triangleIdx1, quint32 triangleIdx2) { + Q_ASSERT(triangleIdx1 < triangulation.size()); + Q_ASSERT(triangleIdx2 < triangulation.size()); + + QtPathTriangle &t1 = triangulation[triangleIdx1]; + QtPathTriangle &t2 = triangulation[triangleIdx2]; + + if (t1.adjacentTriangle1 == triangleIdx2) { + t1.adjacentTriangle1 = quint32(-1); + } else if (t1.adjacentTriangle2 == triangleIdx2) { + t1.adjacentTriangle2 = quint32(-1); + } else { + Q_ASSERT(t1.adjacentTriangle3 == triangleIdx2); + t1.adjacentTriangle3 = quint32(-1); + } + + if (t2.adjacentTriangle1 == triangleIdx1) { + t2.adjacentTriangle1 = quint32(-1); + } else if (t2.adjacentTriangle2 == triangleIdx1) { + t2.adjacentTriangle2 = quint32(-1); + } else { + Q_ASSERT(t2.adjacentTriangle3 == triangleIdx1); + t2.adjacentTriangle3 = quint32(-1); + } + }; + + auto insertOrdered = [&existingEdges, ®isterAdjacency](quint32 a, quint32 b, quint32 triangleIdx) { + if (a > b) + qSwap(a, b); + + Pair edge = qMakePair(a, b); + auto it = existingEdges.find(edge); + if (it == existingEdges.end()) { + existingEdges.insert(edge, qMakePair(triangleIdx, quint32(-1))); + } else { + if (it.value().first == quint32(-1)) { + Q_ASSERT(triangleIdx != it.value().second); + it.value().first = triangleIdx; + } else { + Q_ASSERT(it.value().second == quint32(-1)); + Q_ASSERT(triangleIdx != it.value().first); + it.value().second = triangleIdx; + } + + if (it.value().first != quint32(-1) && it.value().second != quint32(-1)) + registerAdjacency(a, b, it.value().first, it.value().second); + } + }; + + auto removeTriangleEdge = [&existingEdges, &unregisterAdjacency](quint32 a, quint32 b, quint32 triangleIdx) { + if (a > b) + qSwap(a, b); + + Pair edge = qMakePair(a, b); + auto it = existingEdges.find(edge); + Q_ASSERT(it != existingEdges.end()); + + if (it.value().first != quint32(-1) && it.value().second != quint32(-1)) + unregisterAdjacency(it.value().first, it.value().second); + + // Mark that triangleIdx no longer shares the edge + if (it.value().first == triangleIdx) + it.value().first = quint32(-1); + else + it.value().second = quint32(-1); + + + // No more triangles refer to this (== it was the old diagonal), so we + // remove it from the existing edges list + if (it.value().first == quint32(-1) && it.value().second == quint32(-1)) + it = existingEdges.erase(it); + }; + + insertOrdered(superTriangle.v1Index, superTriangle.v2Index, 0); + insertOrdered(superTriangle.v2Index, superTriangle.v3Index, 0); + insertOrdered(superTriangle.v3Index, superTriangle.v1Index, 0); + + // Insert vertices one by one + // For each vertex, we search for the triangle that contains it and then split this at the + // point. We then check the Delaunay property of the new triangles and their neighbours, and + // swap diagonals to restore Delaunayness when needed. + quint32 lastFormedTriangle = 0; + for (quint32 vertexIdx = 0; vertexIdx < l - 3; ++vertexIdx) { + const QtPathVertex &vertex = vertices.at(vertexIdx); + + if (Q_UNLIKELY(drawAllImages)) { + saveDebugImage(true, + path, + QStringLiteral("Adding vertex %1: %2, %3").arg(vertexIdx).arg(vertex.point.x()).arg(vertex.point.y()), + triangulation, + vertices, + QList{} << vertexIdx); + } + + QStack > adjacentTriangles; + auto findAdjacent = [&existingEdges, &vertexIdx, &adjacentTriangles](quint32 i1, quint32 i2, quint32 triangleIdx) { + if (i1 != vertexIdx && i2 != vertexIdx) { + + if (i1 > i2) + qSwap(i1, i2); + Pair key = qMakePair(i1, i2); + auto it = existingEdges.find(key); + Q_ASSERT(it.value().first == triangleIdx || it.value().second == triangleIdx); + + if (it.value().first != quint32(-1) && it.value().second != quint32(-1)) { + if (it.value().first == triangleIdx) + adjacentTriangles.append(qMakePair(it.value().second, triangleIdx)); + else + adjacentTriangles.append(qMakePair(it.value().first, triangleIdx)); + } + } + }; + + { + quint32 triangleIdx1 = quint32(-1); + + // Start with the last formed triangle (or the super triangle on the first run) + // Use Lawson's visibility walk to find the triangle containing the point: + // First check the current triangle (starting with the last formed). If it does not + // contain the point, the next triangle to check is the one of the adjacent triangles + // in the direction of the point. This is done using a "visibility" check, i.e. whether + // the point is on the exterior half-plane of the shared edge.) + triangleIdx1 = lastFormedTriangle; + while (triangleIdx1 != quint32(-1) + && !isPointInTriangle(vertex.point, + vertices[triangulation.at(triangleIdx1).v1Index].point, + vertices[triangulation.at(triangleIdx1).v2Index].point, + vertices[triangulation.at(triangleIdx1).v3Index].point)) { + QtPathTriangle &triangle = triangulation[triangleIdx1]; + if (triangle.lastSeenVertex != quint32(-1) && triangle.lastSeenVertex == vertexIdx) { + qCWarning(lcShapeCurveRenderer) << "Revisiting triangle which has previously been checked"; + triangleIdx1 = quint32(-1); + break; + } + + triangle.lastSeenVertex = vertexIdx; + + // If the point is not in the current triangle, we find the edge(s) where the + // point is on the outside of the triangle. The adjacent triangle to this edge + // is the next in the search. If there are two such edges we pick the first. + // As long as the triangulation is a Delaunay triangulation, this should always + // find the triangle, but we keep a fail safe in case of bugs. + quint32 nextTriangle = findAdjacentTriangleOnSameSide(vertices, + vertexIdx, + triangle); + if (Q_UNLIKELY(drawAllImages)) { + saveDebugImage(true, + path, + QStringLiteral("Marching from %1 to %2 (%3, %4, %5)").arg(triangleIdx1).arg(nextTriangle), + triangulation, + vertices, + QList{} << vertexIdx, + QList{} << (nextTriangle != quint32(-1) ? nextTriangle : 0) << triangleIdx1, + QList{}); + } + + triangleIdx1 = nextTriangle; + } + + // Fail safe, if we failed to find the triangle using the optimized step, we do a brute + // force search. + if (validateResults || triangleIdx1 == quint32(-1)) { + if (triangleIdx1 == quint32(-1)) + qCWarning(lcShapeCurveRenderer) << "Can't find triangle using Lawson. Falling back to brute force"; + quint32 otherTriangle = triangleIdx1; + triangleIdx1 = 0; + for (; triangleIdx1 < triangulation.size(); ++triangleIdx1) { + const QtPathTriangle &triangle = triangulation.at(triangleIdx1); + if (isPointInTriangle(vertex.point, vertices[triangle.v1Index].point, + vertices[triangle.v2Index].point, + vertices[triangle.v3Index].point)) { + break; + } + } + + if (validateResults && otherTriangle != triangleIdx1 && triangleIdx1 != quint32(-1) && otherTriangle != quint32(-1)) { + qCWarning(lcShapeCurveRenderer) << "Lawson matched" << otherTriangle + << "whereas brute force matched" << triangleIdx1 + << "Lawson:" << vertices.at(triangulation.at(otherTriangle).v1Index).point + << vertices.at(triangulation.at(otherTriangle).v2Index).point + << vertices.at(triangulation.at(otherTriangle).v3Index).point + << "Brute:" << vertices.at(triangulation.at(triangleIdx1).v1Index).point + << vertices.at(triangulation.at(triangleIdx1).v2Index).point + << vertices.at(triangulation.at(triangleIdx1).v3Index).point + << "Testing point:" << vertices.at(vertexIdx).point; + + saveDebugImage(saveImageOnError, + path, + QStringLiteral("Two triangles containing the same point"), + triangulation, + vertices, + QList{} << vertexIdx, + QList{} << triangleIdx1 << otherTriangle); + } + } + + if (Q_UNLIKELY(drawAllImages)) { + saveDebugImage(true, + path, + QStringLiteral("Splitting triangle"), + triangulation, + vertices, + QList{} << vertexIdx, + QList{} << triangleIdx1); + } + + if (triangleIdx1 >= triangulation.size()) { + qCWarning(lcShapeCurveRenderer) << "Unable to find any triangle containing point" + << vertex.point + << ". Aborting."; + return QList{}; + } + + // Split triangle into three, connecting to the new vertex + quint32 t1v1Index; + quint32 t1v2Index; + quint32 t1v3Index; + { + QtPathTriangle &t1 = triangulation[triangleIdx1]; + t1v1Index = t1.v1Index; + t1v2Index = t1.v2Index; + t1v3Index = t1.v3Index; + t1.v3Index = vertexIdx; + + removeTriangleEdge(t1v2Index, t1v3Index, triangleIdx1); + removeTriangleEdge(t1v3Index, t1v1Index, triangleIdx1); + } + + + quint32 triangleIdx2 = triangulation.size(); + triangulation.append(QtPathTriangle(t1v2Index, t1v3Index, vertexIdx, triangulation.count())); + quint32 triangleIdx3 = triangulation.size(); + triangulation.append(QtPathTriangle(t1v3Index, t1v1Index, vertexIdx, triangulation.count())); + const QtPathTriangle &t2 = triangulation.at(triangleIdx2); + const QtPathTriangle &t3 = triangulation.at(triangleIdx3); + + lastFormedTriangle = triangleIdx3; + + t1v3Index = vertexIdx; + + insertOrdered(t1v2Index, t1v3Index, triangleIdx1); + insertOrdered(t1v3Index, t1v1Index, triangleIdx1); + + insertOrdered(t2.v1Index, t2.v2Index, triangleIdx2); + insertOrdered(t2.v2Index, t2.v3Index, triangleIdx2); + insertOrdered(t2.v3Index, t2.v1Index, triangleIdx2); + + insertOrdered(t3.v1Index, t3.v2Index, triangleIdx3); + insertOrdered(t3.v2Index, t3.v3Index, triangleIdx3); + insertOrdered(t3.v3Index, t3.v1Index, triangleIdx3); + + // Find triangles sharing edges with new triangles and push to stack + findAdjacent(t1v1Index, t1v2Index, triangleIdx1); + findAdjacent(t2.v1Index, t2.v2Index, triangleIdx2); + findAdjacent(t3.v1Index, t3.v2Index, triangleIdx3); + } + + // Check adjacent triangles to see that they do not contain the introduced vertex in their + // circumcircle. In cases where this happens, we take the quadrilateral consisting of the + // new triangle and its neighbour and turn it into two new triangles by swapping to the + // other diagonal. + while (!adjacentTriangles.isEmpty()) { + QPair pair = adjacentTriangles.pop(); + quint32 triangleIdx1 = pair.first; + quint32 triangleIdx2 = pair.second; + + // If opposing triangle circumcircle contains the vertex we are adding, we swap + // the diagonal of the quad made of the two opposing triangles + QtPathTriangle &triangle1 = triangulation[triangleIdx1]; + QtPathTriangle &triangle2 = triangulation[triangleIdx2]; + if (isPointInCircumcircle(vertex.point, vertices[triangle1.v1Index].point, + vertices[triangle1.v2Index].point, + vertices[triangle1.v3Index].point)) { + if (Q_UNLIKELY(drawAllImages)) { + saveDebugImage(true, + path, + QStringLiteral("About to swap diagonal"), + triangulation, + vertices, + QList{} << vertexIdx, + QList{} << triangleIdx1 << triangleIdx2); + } + + removeTriangleEdge(triangle1.v1Index, triangle1.v2Index, triangleIdx1); + removeTriangleEdge(triangle1.v2Index, triangle1.v3Index, triangleIdx1); + removeTriangleEdge(triangle1.v3Index, triangle1.v1Index, triangleIdx1); + + removeTriangleEdge(triangle2.v1Index, triangle2.v2Index, triangleIdx2); + removeTriangleEdge(triangle2.v2Index, triangle2.v3Index, triangleIdx2); + removeTriangleEdge(triangle2.v3Index, triangle2.v1Index, triangleIdx2); + + swapDiagonal(&triangle1, &triangle2, vertexIdx); + + if (Q_UNLIKELY(drawAllImages)) { + saveDebugImage(true, + path, + QStringLiteral("Has swapped diagonal"), + triangulation, + vertices, + QList{} << vertexIdx, + QList{} << triangleIdx1 << triangleIdx2); + } + + // Add back triangles sharing edges + insertOrdered(triangle1.v1Index, triangle1.v2Index, triangleIdx1); + insertOrdered(triangle1.v2Index, triangle1.v3Index, triangleIdx1); + insertOrdered(triangle1.v3Index, triangle1.v1Index, triangleIdx1); + + insertOrdered(triangle2.v1Index, triangle2.v2Index, triangleIdx2); + insertOrdered(triangle2.v2Index, triangle2.v3Index, triangleIdx2); + insertOrdered(triangle2.v3Index, triangle2.v1Index, triangleIdx2); + + findAdjacent(triangle1.v1Index, triangle1.v2Index, triangleIdx1); + findAdjacent(triangle1.v2Index, triangle1.v3Index, triangleIdx1); + findAdjacent(triangle1.v3Index, triangle1.v1Index, triangleIdx1); + + findAdjacent(triangle2.v1Index, triangle2.v2Index, triangleIdx2); + findAdjacent(triangle2.v2Index, triangle2.v3Index, triangleIdx2); + findAdjacent(triangle2.v3Index, triangle2.v1Index, triangleIdx2); + } + } + } + + qCDebug(lcShapeTiming) << "qtDelaunayTriangulator: Inserting vertices in" << t.elapsed() << "ms"; + + // Make a look-up for constrained edges to check self-intersections + for (const QtPathEdge &constraintEdge : edges) { + quint32 a = constraintEdge.startIndex; + quint32 b = constraintEdge.endIndex; + if (a > b) + qSwap(a, b); + constrainedEdges.insert(qMakePair(a, b)); + } + + // Insert constraint edges: + // For each constrained edge, we make sure it is in the triangulation: We first check if it + // already happens to be, and if so we do nothing. If it is not, we check which edges in the + // current graph it intersects, and remove these by swapping diagonals. We then go through + // newly created edges (except the constrained edge) and swap them for the other diagonals in + // cases where this is needed to satisfy Delaunay property. + for (const QtPathEdge &constraintEdge : edges) { + quint32 a = constraintEdge.startIndex; + quint32 b = constraintEdge.endIndex; + if (a > b) + qSwap(a, b); + + // If edge already exists, we continue + if (!existingEdges.contains(qMakePair(a, b))) { + const QVector2D &newEdgeP1 = vertices.at(a).point; + const QVector2D &newEdgeP2 = vertices.at(b).point; + + // Find all edges that intersect with [a, b] + QList intersectingEdges; + static bool permitSelfIntersections = qEnvironmentVariableIntValue("QT_QUICKSHAPES_PERMIT_SELF_INTERSECTIONS") != 0; + + // We start search with any triangle containing 'a' as a vertex + // ### Could we keep track of this as well in the vertex info? + quint32 triangleIdx = 0; + while (triangleIdx != quint32(-1) + && triangulation.at(triangleIdx).v1Index != a + && triangulation.at(triangleIdx).v2Index != a + && triangulation.at(triangleIdx).v3Index != a) { + const QtPathTriangle &triangle = triangulation.at(triangleIdx); + triangleIdx = findAdjacentTriangleOnSameSide(vertices, + a, + triangle); + } + + if (triangleIdx >= triangulation.size()) { + qCWarning(lcShapeCurveRenderer) + << "qtDelaunayTriangulator: Unable to find any triangle containing vertex" + << a << vertices.at(a).point + << ". Constrained edge will be missing from triangulation"; + continue; + } + + // We now circle 'a' by looking at adjacent triangles on edges containing 'a' (making + // sure we do not go backwards) until we find one which intersects [a, b]. We then + // use the visibility walk to check triangles in the direction of 'b', recording + // intersections as we go and moving via intersecting edges. We stop when there are no + // more intersections. + quint32 previousTriangleIdx = quint32(-1); + quint32 startingTriangleIdx = triangleIdx; + while (triangleIdx != quint32(-1)) { + quint32 nextTriangle = quint32(-1); + const QtPathTriangle &triangle = triangulation.at(triangleIdx); + + if (Q_UNLIKELY(drawAllImages)) { + saveDebugImage(true, + path, + QStringLiteral("Circling %1 to find first intersection (triangle: %2, %3, %4)").arg(a).arg(triangle.v1Index).arg(triangle.v2Index).arg(triangle.v3Index), + triangulation, + vertices, + QList{} << a, + QList{} << triangleIdx, + QList{} << qMakePair(a, b)); + } + + Pair existingEdge; + quint32 adjacentTriangle; + if (triangle.v1Index == a) { + existingEdge = qMakePair(triangle.v2Index, triangle.v3Index); + adjacentTriangle = triangle.adjacentTriangle2; + + if (triangle.adjacentTriangle1 != previousTriangleIdx + && triangle.adjacentTriangle1 != quint32(-1)) { + nextTriangle = triangle.adjacentTriangle1; // v1-v2 + } else { + nextTriangle = triangle.adjacentTriangle3; // v3-v1 + } + } else if (triangle.v2Index == a) { + existingEdge = qMakePair(triangle.v3Index, triangle.v1Index); + adjacentTriangle = triangle.adjacentTriangle3; + + if (triangle.adjacentTriangle1 != previousTriangleIdx + && triangle.adjacentTriangle1 != quint32(-1)) { + nextTriangle = triangle.adjacentTriangle1; // v1-v2 + } else { + nextTriangle = triangle.adjacentTriangle2; // v2-v3 + } + } else { // a == triangle.v3Index + Q_ASSERT(a == triangle.v3Index); + existingEdge = qMakePair(triangle.v1Index, triangle.v2Index); + adjacentTriangle = triangle.adjacentTriangle1; + + if (triangle.adjacentTriangle3 != previousTriangleIdx + && triangle.adjacentTriangle3 != quint32(-1)) { + nextTriangle = triangle.adjacentTriangle3; // v3-v1 + } else { + nextTriangle = triangle.adjacentTriangle2; // v2-v3 + } + } + + const QVector2D &existingEdgeP1 = vertices.at(existingEdge.first).point; + const QVector2D &existingEdgeP2 = vertices.at(existingEdge.second).point; + + if (lineIntersects(newEdgeP1, newEdgeP2, existingEdgeP1, existingEdgeP2)) { + // Go to next phase of search, where the intersections are recorded. + triangleIdx = adjacentTriangle; + if (existingEdge.first > existingEdge.second) + qSwap(existingEdge.first, existingEdge.second); + intersectingEdges.append(existingEdge); + + if (!permitSelfIntersections && constrainedEdges.contains(existingEdge)) { + qCWarning(lcShapeCurveRenderer) + << "qtDelaunayTriangulator: Exiting early due to self-intersection." + << "Edge 1:" << a << b << "(" << newEdgeP1 << newEdgeP2 << ")" + << "Edge 2:" << existingEdge.first << existingEdge.second << "(" << existingEdgeP1 << existingEdgeP2 << ")"; + saveDebugImage(saveImageOnError, + path, + QStringLiteral("Found intersection of two constrained edges. No solution."), + triangulation, + vertices, + QList{}, + QList{}, + QList >{} + << qMakePair(a, b) + << qMakePair(existingEdge.first, existingEdge.second)); + return QList{}; + } + + break; + } + + previousTriangleIdx = triangleIdx; + triangleIdx = nextTriangle; + + // No intersection found + if (triangleIdx == startingTriangleIdx) + triangleIdx = quint32(-1); + } + + // Now we march towards 'b' and record all intersections + while (triangleIdx != quint32(-1)) { + const QtPathTriangle &triangle = triangulation.at(triangleIdx); + + QVarLengthArray possibleTriangles; + QVarLengthArray edges; + + findAdjacentTrianglesOnSameSide(vertices, b, triangle, &possibleTriangles, &edges); + + // Check edge(s) which can see 'b' for intersections + triangleIdx = quint32(-1); + for (int i = 0; i < edges.size(); ++i) { + const Pair &existingEdge = edges.at(i); + + const QVector2D &existingEdgeP1 = vertices.at(existingEdge.first).point; + const QVector2D &existingEdgeP2 = vertices.at(existingEdge.second).point; + + if (existingEdge.first != a + && existingEdge.second != a + && lineIntersects(newEdgeP1, newEdgeP2, existingEdgeP1, existingEdgeP2)) { + if (!permitSelfIntersections && constrainedEdges.contains(existingEdge)) { + qCWarning(lcShapeCurveRenderer) + << "qtDelaunayTriangulator: Exiting early due to self-intersection." + << "Edge 1:" << a << b << "(" << newEdgeP1 << newEdgeP2 << ")" + << "Edge 2:" << existingEdge.first << existingEdge.second << "(" << existingEdgeP1 << existingEdgeP2 << ")"; + saveDebugImage(saveImageOnError, + path, + QStringLiteral("Found intersection of two constrained edges. No solution."), + triangulation, + vertices, + QList{}, + QList{}, + QList >{} + << qMakePair(a, b) + << qMakePair(existingEdge.first, existingEdge.second)); + return QList{}; + } + + intersectingEdges.append(existingEdge); + triangleIdx = possibleTriangles[i]; + + if (Q_UNLIKELY(drawAllImages)) { + saveDebugImage(true, + path, + QStringLiteral("Marching to %1 (triangle: %2, %3, %4)") + .arg(b) + .arg(triangulation.at(possibleTriangles.first()).v1Index) + .arg(triangulation.at(possibleTriangles.first()).v2Index) + .arg(triangulation.at(possibleTriangles.first()).v3Index), + triangulation, + vertices, + QList{} << b, + QList{} << triangleIdx << possibleTriangles.first(), + QList{} << qMakePair(a, b) << existingEdge); + } + } + } + } + + if (Q_UNLIKELY(validateResults)) { + QList intersectingEdgesFound = intersectingEdges; + intersectingEdges.clear(); + for (auto it = existingEdges.constBegin(); it != existingEdges.constEnd(); ++it) { + const Pair &existingEdge = it.key(); + + const QVector2D &existingEdgeP1 = vertices.at(existingEdge.first).point; + const QVector2D &existingEdgeP2 = vertices.at(existingEdge.second).point; + + if (existingEdge.first != a && existingEdge.second != a + && existingEdge.first != b && existingEdge.second != b + && lineIntersects(newEdgeP1, newEdgeP2, existingEdgeP1, existingEdgeP2)) { + if (!permitSelfIntersections && constrainedEdges.contains(existingEdge)) { + qCWarning(lcShapeCurveRenderer) + << "qtDelaunayTriangulator: Exiting early due to self-intersection." + << "Edge 1:" << a << b << "(" << newEdgeP1 << newEdgeP2 << ")" + << "Edge 2:" << existingEdge.first << existingEdge.second << "(" << existingEdgeP1 << existingEdgeP2 << ")"; + saveDebugImage(saveImageOnError, + path, + QStringLiteral("Found intersection of two constrained edges. No solution."), + triangulation, + vertices, + QList{}, + QList{}, + QList >{} << qMakePair(a, b) << qMakePair(existingEdge.first, existingEdge.second)); + return QList{}; + } + + intersectingEdges.append(existingEdge); + + if (Q_UNLIKELY(drawAllImages)) { + saveDebugImage(true, + path, + QStringLiteral("Found intersection of constrained edge (%1 so far)").arg(intersectingEdges.size()), + triangulation, + vertices, + QList{}, + QList{}, + QList >{} << qMakePair(a, b) << qMakePair(existingEdge.first, existingEdge.second)); + } + } + } + + for (const Pair &intersectingEdge : intersectingEdges) { + bool found = false; + for (const Pair &otherIntersectingEdge : intersectingEdgesFound) { + if (otherIntersectingEdge == intersectingEdge || qMakePair(otherIntersectingEdge.second, otherIntersectingEdge.first) == intersectingEdge) { + found = true; + break; + } + } + + if (!found) { + qCWarning(lcShapeCurveRenderer) << "Intersecting edge" << intersectingEdge.first << intersectingEdge.second << "not found with smart algorithm. Constrained edge:" << a << b; + + saveDebugImage(saveImageOnError, + path, + QStringLiteral("Intersecting edge not found"), + triangulation, + vertices, + QList{}, + QList{}, + QList >{} << qMakePair(a, b) << qMakePair(intersectingEdge.first, intersectingEdge.second)); + + return QList{}; + } + } + + for (const Pair &intersectingEdge : intersectingEdgesFound) { + bool found = false; + for (const Pair &otherIntersectingEdge : intersectingEdges) { + if (otherIntersectingEdge == intersectingEdge || qMakePair(otherIntersectingEdge.second, otherIntersectingEdge.first) == intersectingEdge) { + found = true; + break; + } + } + + if (!found) { + qCWarning(lcShapeCurveRenderer) << "Intersecting edge" << intersectingEdge.first << intersectingEdge.second << "not found with dumb algorithm. Constrained edge:" << a << b; + + saveDebugImage(saveImageOnError, + path, + QStringLiteral("Intersecting edge not found"), + triangulation, + vertices, + QList{}, + QList{}, + QList >{} << qMakePair(a, b) << qMakePair(intersectingEdge.first, intersectingEdge.second)); + + return QList{}; + } + } + } + + bool somethingChanged = false; + auto it = intersectingEdges.begin(); + QSet newlyCreatedEdges; + while (!intersectingEdges.isEmpty()) { + Q_ASSERT(it != intersectingEdges.end()); + + Pair existingEdge = *it; + + Q_ASSERT(existingEdges.contains(existingEdge)); + + // ### Since we are now finding edges via triangles, this information can be + // included in the intersecting edges list and we do not need the extra lookup. + Pair triangles = existingEdges.value(existingEdge); + + QtPathTriangle &triangle1 = triangulation[triangles.first]; + QtPathTriangle &triangle2 = triangulation[triangles.second]; + + // If the triangles do not together make a convex polygon, we skip this for now + // Otherwise, swap the diagonal + if (isConvexQuadrilateral(vertices, triangle1, triangle2)) { + somethingChanged = true; + + if (Q_UNLIKELY(drawAllImages)) { + saveDebugImage(true, + path, + QStringLiteral("About to swap diagonal"), + triangulation, + vertices, + QList{}, + QList{} << triangles.first << triangles.second, + QList >{} << qMakePair(existingEdge.first, existingEdge.second)); + } + + // The triangles are being altered, so we have to remove them from the edge + // list first + removeTriangleEdge(triangle1.v1Index, triangle1.v2Index, triangles.first); + removeTriangleEdge(triangle1.v2Index, triangle1.v3Index, triangles.first); + removeTriangleEdge(triangle1.v3Index, triangle1.v1Index, triangles.first); + + removeTriangleEdge(triangle2.v1Index, triangle2.v2Index, triangles.second); + removeTriangleEdge(triangle2.v2Index, triangle2.v3Index, triangles.second); + removeTriangleEdge(triangle2.v3Index, triangle2.v1Index, triangles.second); + + // If not, we swap the diagonal of the two triangles + swapDiagonal(&triangle1, &triangle2); + it = intersectingEdges.erase(it); + if (it == intersectingEdges.end()) { // Wrap over end of list + it = intersectingEdges.begin(); + somethingChanged = false; + } + + // Add back triangles sharing edges + insertOrdered(triangle1.v1Index, triangle1.v2Index, triangles.first); + insertOrdered(triangle1.v2Index, triangle1.v3Index, triangles.first); + insertOrdered(triangle1.v3Index, triangle1.v1Index, triangles.first); + + insertOrdered(triangle2.v1Index, triangle2.v2Index, triangles.second); + insertOrdered(triangle2.v2Index, triangle2.v3Index, triangles.second); + insertOrdered(triangle2.v3Index, triangle2.v1Index, triangles.second); + + Pair newDiagonal = triangle1.v1Index < triangle1.v2Index + ? qMakePair(triangle1.v1Index, triangle1.v2Index) + : qMakePair(triangle1.v2Index, triangle1.v1Index); + + if (Q_UNLIKELY(drawAllImages)) { + saveDebugImage(true, + path, + QStringLiteral("Swapped diagonal"), + triangulation, + vertices, + QList{}, + QList{} << triangles.first << triangles.second, + QList >{} << qMakePair(newDiagonal.first, newDiagonal.second)); + } + + // By convention, the new diagonal is now the two first vertices in the triangles + const QVector2D &newDiagonalP1 = vertices[triangle1.v1Index].point; + const QVector2D &newDiagonalP2 = vertices[triangle1.v2Index].point; + + if (newDiagonal.first != a + && newDiagonal.second != a + && newDiagonal.first != b + && newDiagonal.second != b + && lineIntersects(newEdgeP1, newEdgeP2, newDiagonalP1, newDiagonalP2)) { + it = intersectingEdges.insert(it, newDiagonal); + } else { + if (newDiagonal != qMakePair(a, b) && newDiagonal != qMakePair(b, a)) + newlyCreatedEdges.insert(newDiagonal); + + continue; // No need to increase iterator, since we erased + } + } else { + if (Q_UNLIKELY(drawAllImages)) { + saveDebugImage(true, + path, + QStringLiteral("Skipping non-convex quad"), + triangulation, + vertices, + QList{}, + QList{} << triangles.first << triangles.second, + QList >{} << qMakePair(existingEdge.first, existingEdge.second)); + } + } + + if (++it == intersectingEdges.end()) { // Wrap over end of list + if (!somethingChanged && !intersectingEdges.isEmpty()) { + intersectingEdges.prepend(qMakePair(a, b)); // Just for visuals + saveDebugImage(saveImageOnError, + path, + QStringLiteral("Looped through all edges with no changes. No solution."), + triangulation, + vertices, + QList{}, + QList{}, + intersectingEdges); + qCWarning(lcShapeCurveRenderer) << "Detected infinite loop. Exiting early."; + break; + } + + it = intersectingEdges.begin(); + somethingChanged = false; + } + } + + // Restore the delaunay properties + for (const Pair &newlyCreatedEdge : newlyCreatedEdges) { + auto it = existingEdges.find(newlyCreatedEdge); + Q_ASSERT(it != existingEdges.end()); + + Pair triangles = it.value(); + + QtPathTriangle &triangle1 = triangulation[triangles.first]; + QtPathTriangle &triangle2 = triangulation[triangles.second]; + + bool delaunaySatisfied = + !isPointInCircumcircle(vertices.at(triangle1.v1Index).point, + vertices.at(triangle2.v1Index).point, + vertices.at(triangle2.v2Index).point, + vertices.at(triangle2.v3Index).point) + && !isPointInCircumcircle(vertices.at(triangle1.v2Index).point, + vertices.at(triangle2.v1Index).point, + vertices.at(triangle2.v2Index).point, + vertices.at(triangle2.v3Index).point) + && !isPointInCircumcircle(vertices.at(triangle1.v3Index).point, + vertices.at(triangle2.v1Index).point, + vertices.at(triangle2.v2Index).point, + vertices.at(triangle2.v3Index).point) + && !isPointInCircumcircle(vertices.at(triangle2.v1Index).point, + vertices.at(triangle1.v1Index).point, + vertices.at(triangle1.v2Index).point, + vertices.at(triangle1.v3Index).point) + && !isPointInCircumcircle(vertices.at(triangle2.v2Index).point, + vertices.at(triangle1.v1Index).point, + vertices.at(triangle1.v2Index).point, + vertices.at(triangle1.v3Index).point) + && !isPointInCircumcircle(vertices.at(triangle2.v3Index).point, + vertices.at(triangle1.v1Index).point, + vertices.at(triangle1.v2Index).point, + vertices.at(triangle1.v3Index).point); + if (!delaunaySatisfied) { + removeTriangleEdge(triangle1.v1Index, triangle1.v2Index, triangles.first); + removeTriangleEdge(triangle1.v2Index, triangle1.v3Index, triangles.first); + removeTriangleEdge(triangle1.v3Index, triangle1.v1Index, triangles.first); + + removeTriangleEdge(triangle2.v1Index, triangle2.v2Index, triangles.second); + removeTriangleEdge(triangle2.v2Index, triangle2.v3Index, triangles.second); + removeTriangleEdge(triangle2.v3Index, triangle2.v1Index, triangles.second); + + // If not, we swap the diagonal of the two triangles + swapDiagonal(&triangle1, &triangle2); + + // Add back triangles sharing edges + insertOrdered(triangle1.v1Index, triangle1.v2Index, triangles.first); + insertOrdered(triangle1.v2Index, triangle1.v3Index, triangles.first); + insertOrdered(triangle1.v3Index, triangle1.v1Index, triangles.first); + + insertOrdered(triangle2.v1Index, triangle2.v2Index, triangles.second); + insertOrdered(triangle2.v2Index, triangle2.v3Index, triangles.second); + insertOrdered(triangle2.v3Index, triangle2.v1Index, triangles.second); + } + } + + if (Q_UNLIKELY(validateResults)) { + if (!existingEdges.contains(qMakePair(a, b))) { + qCWarning(lcShapeCurveRenderer) + << "Unable to produce triangulation with edge" << a << b; + saveDebugImage(saveImageOnError, + path, + QStringLiteral("Triangulation missing [%1, %2]").arg(a).arg(b), + triangulation, + vertices, + QList{}, + QList{}, + QList{} << qMakePair(a, b)); + } + } + } + } + + qCDebug(lcShapeTiming) << "qtDelaunayTriangulator: Inserting edges in" << t.elapsed() << "ms"; + + for (auto it = triangulation.begin(); it != triangulation.end(); ++it) { + QtPathTriangle &triangle = *it; + if (triangle.v1Index >= l - 3 || triangle.v2Index >= l - 3 || triangle.v3Index >= l - 3) + triangle.isValid = false; + } + + return triangulation; +} + +QT_END_NAMESPACE diff --git a/src/quickshapes/qt_quadratic_bezier.cpp b/src/quickshapes/qt_quadratic_bezier.cpp new file mode 100644 index 0000000000..93158010a1 --- /dev/null +++ b/src/quickshapes/qt_quadratic_bezier.cpp @@ -0,0 +1,139 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only + +// ---------------- Cubic -> Quadratic path stuff - temporarily here ----------- + +#include +#include + +QT_BEGIN_NAMESPACE + +#if 0 +static bool qt_isQuadratic(const QBezier &b) +{ + const qreal f = 3.0 / 2.0; + const QPointF c1 = b.pt1() + f * (b.pt2() - b.pt1()); + const QPointF c2 = b.pt4() + f * (b.pt3() - b.pt4()); + return c1 == c2; +} +#endif + +static qreal qt_scoreQuadratic(const QBezier &b, QPointF qcp) +{ + // Construct a cubic from the quadratic, and compare its control points to the originals' + const QRectF bounds = b.bounds(); + qreal dim = QLineF(bounds.topLeft(), bounds.bottomRight()).length(); + if (qFuzzyIsNull(dim)) + return 0; + const qreal f = 2.0 / 3; + const QPointF cp1 = b.pt1() + f * (qcp - b.pt1()); + const QPointF cp2 = b.pt4() + f * (qcp - b.pt4()); + const QLineF d1(b.pt2(), cp1); + const QLineF d2(b.pt3(), cp2); + return qMax(d1.length(), d2.length()) / dim; +} + +static qreal qt_quadraticForCubic(const QBezier &b, QPointF *qcp) +{ + const QLineF st = b.startTangent(); + const QLineF et = b.endTangent(); + if (st.intersects(et, qcp) == QLineF::NoIntersection) + *qcp = b.midPoint(); + return qt_scoreQuadratic(b, *qcp); +} + +static int qt_getInflectionPoints(const QBezier &orig, qreal *tpoints) +{ + auto isValidRoot = [](qreal r) { + return qIsFinite(r) && (r > 0) && (!qFuzzyIsNull(float(r))) && (r < 1) + && (!qFuzzyIsNull(float(r - 1))); + }; + + // normalize so pt1.x,pt1.y,pt4.y == 0 + QTransform xf; + const QLineF l(orig.pt1(), orig.pt4()); + xf.rotate(l.angle()); + xf.translate(-orig.pt1().x(), -orig.pt1().y()); + const QBezier n = orig.mapBy(xf); + Q_ASSERT(n.pt1() == QPoint() && qFuzzyIsNull(float(n.pt4().y()))); + + const qreal p = n.pt3().x() * n.pt2().y(); + const qreal q = n.pt4().x() * n.pt2().y(); + const qreal r = n.pt2().x() * n.pt3().y(); + const qreal s = n.pt4().x() * n.pt3().y(); + + const qreal a = 36 * ((-3 * p) + (2 * q) + (3 * r) - s); + if (!a) + return 0; + const qreal b = -18 * (((3 * p) - q) - (3 * r)); + const qreal c = 18 * (r - p); + const qreal rad = (b * b) - (2 * a * c); + if (rad < 0) + return 0; + const qreal sqr = qSqrt(rad); + const qreal root1 = (b + sqr) / a; + const qreal root2 = (b - sqr) / a; + + int res = 0; + if (isValidRoot(root1)) + tpoints[res++] = root1; + if (root2 != root1 && isValidRoot(root2)) + tpoints[res++] = root2; + + if (res == 2 && tpoints[0] > tpoints[1]) + qSwap(tpoints[0], tpoints[1]); + + return res; +} + +static void qt_addToQuadratics(const QBezier &b, QPolygonF *p, qreal spanlength, qreal errorLimit) +{ + Q_ASSERT((spanlength > 0) && !(spanlength > 1)); + + QPointF qcp; + bool isOk = (qt_quadraticForCubic(b, &qcp) < errorLimit); // error limit, careful + if (isOk || spanlength < 0.1) { + p->append(qcp); + p->append(b.pt4()); + } else { + QBezier rhs = b; + QBezier lhs; + rhs.parameterSplitLeft(0.5, &lhs); + qt_addToQuadratics(lhs, p, spanlength / 2, errorLimit); + qt_addToQuadratics(rhs, p, spanlength / 2, errorLimit); + } +} + +QPolygonF qt_toQuadratics(const QBezier &b, qreal errorLimit) +{ + + QPolygonF res; + res.reserve(16); + res.append(b.pt1()); + const QRectF cpr = b.bounds(); + qreal epsilon = QLineF(cpr.topLeft(), cpr.bottomRight()).length() * 0.5 * errorLimit; + bool degenerate = false; + if (QLineF(b.pt2(), b.pt1()).length() < epsilon) { + res.append(b.pt3()); + degenerate = true; + } else if (QLineF(b.pt4(), b.pt3()).length() < epsilon) { + res.append(b.pt2()); + degenerate = true; + } + if (degenerate) { + res.append(b.pt4()); + return res; + } + qreal infPoints[2]; + int numInfPoints = qt_getInflectionPoints(b, infPoints); + qreal t0 = 0; + for (int i = 0; i < numInfPoints + 1; i++) { // #segments == #inflectionpoints + 1 + qreal t1 = (i < numInfPoints) ? infPoints[i] : 1; + QBezier segment = b.bezierOnInterval(t0, t1); + qt_addToQuadratics(segment, &res, t1 - t0, errorLimit); + t0 = t1; + } + return res; +} + +QT_END_NAMESPACE diff --git a/src/quickshapes/shaders_ng/shapecurve.frag b/src/quickshapes/shaders_ng/shapecurve.frag new file mode 100644 index 0000000000..8753171fe2 --- /dev/null +++ b/src/quickshapes/shaders_ng/shapecurve.frag @@ -0,0 +1,108 @@ +#version 440 + +layout(location = 0) in vec3 qt_TexCoord; +layout(location = 1) in vec4 debugColor; + +#if defined(LINEARGRADIENT) +layout(location = 2) in float gradTabIndex; +#elif defined(RADIALGRADIENT) || defined(CONICALGRADIENT) +layout(location = 2) in vec2 coord; +#endif + +layout(location = 0) out vec4 fragColor; + +layout(std140, binding = 0) uniform buf { + mat4 qt_Matrix; + +#if defined(STROKE) + vec4 strokeColor; + float strokeWidth; + float reserved1; + float reserved2; + float reserved3; +#endif + +#if defined(LINEARGRADIENT) + vec2 gradientStart; + vec2 gradientEnd; +#elif defined(RADIALGRADIENT) + vec2 translationPoint; + vec2 focalToCenter; + float centerRadius; + float focalRadius; +#elif defined(CONICALGRADIENT) + vec2 translationPoint; + float angle; +#else + vec4 color; +#endif +} ubuf; + +#define INVERSE_2PI 0.1591549430918953358 + +#if defined(LINEARGRADIENT) || defined(RADIALGRADIENT) || defined(CONICALGRADIENT) +layout(binding = 1) uniform sampler2D gradTabTexture; +#endif + +vec4 baseColor() +{ +#if defined(LINEARGRADIENT) + return texture(gradTabTexture, vec2(gradTabIndex, 0.5)); +#elif defined(RADIALGRADIENT) + float rd = ubuf.centerRadius - ubuf.focalRadius; + float b = 2.0 * (rd * ubuf.focalRadius + dot(coord, ubuf.focalToCenter)); + float fmp2_m_radius2 = -ubuf.focalToCenter.x * ubuf.focalToCenter.x - ubuf.focalToCenter.y * ubuf.focalToCenter.y + rd * rd; + float inverse_2_fmp2_m_radius2 = 1.0 / (2.0 * fmp2_m_radius2); + float det = b * b - 4.0 * fmp2_m_radius2 * ((ubuf.focalRadius * ubuf.focalRadius) - dot(coord, coord)); + vec4 result = vec4(0.0); + if (det >= 0.0) { + float detSqrt = sqrt(det); + float w = max((-b - detSqrt) * inverse_2_fmp2_m_radius2, (-b + detSqrt) * inverse_2_fmp2_m_radius2); + if (ubuf.focalRadius + w * (ubuf.centerRadius - ubuf.focalRadius) >= 0.0) + result = texture(gradTabTexture, vec2(w, 0.5)); + } + + return result; +#elif defined(CONICALGRADIENT) + float t; + if (abs(coord.y) == abs(coord.x)) + t = (atan(-coord.y + 0.002, coord.x) + ubuf.angle) * INVERSE_2PI; + else + t = (atan(-coord.y, coord.x) + ubuf.angle) * INVERSE_2PI; + return texture(gradTabTexture, vec2(t - floor(t), 0.5)); +#else + return vec4(ubuf.color.rgb, 1.0) * ubuf.color.a; +#endif +} + +void main() +{ + float f = qt_TexCoord.z * (qt_TexCoord.x * qt_TexCoord.x - qt_TexCoord.y) // curve + + (1.0 - abs(qt_TexCoord.z)) * (qt_TexCoord.x - qt_TexCoord.y); // line + +#if defined(STROKE) + float _ddx = dFdx(f); + float _ddy = dFdy(f); + float df = length(vec2(_ddx, _ddy)); + float distance = (f / df); // distance from centre of fragment to line + + float halfStrokeWidth = ubuf.strokeWidth / 2.0; + + // calculate stroke + float strokeCoverage = 1.0 - clamp(0.5 + abs(distance) - halfStrokeWidth, 0.0, 1.0); + vec4 stroke = ubuf.strokeColor * strokeCoverage; + + float fillCoverage = clamp(0.5 + f / df, 0.0, 1.0); + vec4 fill = baseColor() * fillCoverage; + + vec4 combined = fill * (1.0 - stroke.a) + stroke * stroke.a; + + // finally mix in debug + fragColor = mix(combined, vec4(debugColor.rgb, 1.0), debugColor.a); + + // TODO: support both outline and fill +#else + float df = fwidth(f); + fragColor = mix(baseColor() * clamp(0.5 + f / df, 0.0, 1.0), vec4(debugColor.rgba), debugColor.a); +#endif +} diff --git a/src/quickshapes/shaders_ng/shapecurve.vert b/src/quickshapes/shaders_ng/shapecurve.vert new file mode 100644 index 0000000000..3bd281ddaa --- /dev/null +++ b/src/quickshapes/shaders_ng/shapecurve.vert @@ -0,0 +1,58 @@ +#version 440 + +layout(location = 0) in vec4 vertexCoord; +layout(location = 1) in vec3 vertexTexCoord; +layout(location = 2) in vec4 vertexDebugColor; + +layout(location = 0) out vec3 qt_TexCoord; +layout(location = 1) out vec4 debugColor; + +#if defined(LINEARGRADIENT) +layout(location = 2) out float gradTabIndex; +#elif defined(RADIALGRADIENT) || defined(CONICALGRADIENT) +layout(location = 2) out vec2 coord; +#endif + +layout(std140, binding = 0) uniform buf { + mat4 qt_Matrix; + +#if defined(STROKE) + vec4 strokeColor; + float strokeWidth; + float reserved1; + float reserved2; + float reserved3; +#endif + +#if defined(LINEARGRADIENT) + vec2 gradientStart; + vec2 gradientEnd; +#elif defined(RADIALGRADIENT) + vec2 translationPoint; + vec2 focalToCenter; + float centerRadius; + float focalRadius; +#elif defined(CONICALGRADIENT) + vec2 translationPoint; + float angle; +#else + vec4 color; +#endif +} ubuf; + +out gl_PerVertex { vec4 gl_Position; }; + +void main() +{ + qt_TexCoord = vertexTexCoord; + debugColor = vertexDebugColor; + +#if defined(LINEARGRADIENT) + vec2 gradVec = ubuf.gradientEnd - ubuf.gradientStart; + gradTabIndex = dot(gradVec, vertexCoord.xy - ubuf.gradientStart.xy) / dot(gradVec, gradVec); +#elif defined(RADIALGRADIENT) || defined(CONICALGRADIENT) + coord = vertexCoord.xy - ubuf.translationPoint; +#endif + + gl_Position = ubuf.qt_Matrix * vertexCoord; +} diff --git a/src/quickshapes/shaders_ng/wireframe.frag b/src/quickshapes/shaders_ng/wireframe.frag new file mode 100644 index 0000000000..1d81aaafc9 --- /dev/null +++ b/src/quickshapes/shaders_ng/wireframe.frag @@ -0,0 +1,18 @@ +#version 440 + +layout(location = 0) out vec4 fragColor; +layout(location = 0) in vec3 barycentric; + +layout(std140, binding = 0) uniform buf { + mat4 qt_Matrix; +} ubuf; + +void main() +{ + float f = min(barycentric.x, min(barycentric.y, barycentric.z)); + float d = fwidth(f * 1.5); + float alpha = smoothstep(0.0, d, f); + + //alpha = 1.0 - step(0.5, barycentric.x); + fragColor = vec4(1.0, 0.2, 1.0, 1.0) * (1.0 - alpha); +} diff --git a/src/quickshapes/shaders_ng/wireframe.vert b/src/quickshapes/shaders_ng/wireframe.vert new file mode 100644 index 0000000000..80451788ea --- /dev/null +++ b/src/quickshapes/shaders_ng/wireframe.vert @@ -0,0 +1,17 @@ +#version 440 + +layout(location = 0) in vec4 vertexCoord; +layout(location = 1) in vec3 vertexBarycentric; +layout(location = 0) out vec3 barycentric; + +layout(std140, binding = 0) uniform buf { + mat4 qt_Matrix; +} ubuf; + +out gl_PerVertex { vec4 gl_Position; }; + +void main() +{ + barycentric = vertexBarycentric; + gl_Position = ubuf.qt_Matrix * vertexCoord; +} diff --git a/tests/manual/painterpathquickshape/1535737773.svg b/tests/manual/painterpathquickshape/1535737773.svg new file mode 100644 index 0000000000..c836b8d18d --- /dev/null +++ b/tests/manual/painterpathquickshape/1535737773.svg @@ -0,0 +1,25 @@ + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/tests/manual/painterpathquickshape/CMakeLists.txt b/tests/manual/painterpathquickshape/CMakeLists.txt new file mode 100644 index 0000000000..a9616564a3 --- /dev/null +++ b/tests/manual/painterpathquickshape/CMakeLists.txt @@ -0,0 +1,65 @@ +# Copyright (C) 2023 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause + +qt_internal_add_manual_test(painterPathQuickShape + GUI + SOURCES + main.cpp + debugpaintitem.h debugpaintitem.cpp + svgpathloader.h svgpathloader.cpp + debugvisualizationcontroller.h debugvisualizationcontroller.cpp + LIBRARIES + Qt::Gui + Qt::Qml + Qt::Quick + Qt::QuickPrivate + Qt::QuickShapesPrivate + Qt::SvgPrivate +) + + +set(qml_resource_files + "1535737773.svg" + "main.qml" + "SvgShape.qml" + "ControlPanel.qml" + "ControlPoint.qml" + "TextShape.qml" + "SimpleShape.qml" + "SmallPolygon.qml" + "Squircle.qml" + "ControlledShape.qml" + "Graziano.ttf" + "CubicShape.qml" + "hand-print.svg" + "background.png" + "arcDirection.qml" + "arcRotation.qml" + "capStyles.qml" + "cubicCurve.qml" + "dashPattern.qml" + "ellipticalArcs.qml" + "fillRules.qml" + "gradientSpreadModes.qml" + "joinStyles.qml" + "largeOrSmallArc.qml" + "linearGradient.qml" + "quadraticCurve.qml" + "radialGradient.qml" + "strokeOrFill.qml" + "text.qml" + "tiger.qml" +) + +qt_internal_add_resource(painterPathQuickShape "qml" + PREFIX + "/" + FILES + ${qml_resource_files} +) + +qt_add_qml_module(painterPathQuickShape + VERSION 1.0 + URI InstancingTest + RESOURCE_PREFIX / +) diff --git a/tests/manual/painterpathquickshape/ControlPanel.qml b/tests/manual/painterpathquickshape/ControlPanel.qml new file mode 100644 index 0000000000..cae15a5b10 --- /dev/null +++ b/tests/manual/painterpathquickshape/ControlPanel.qml @@ -0,0 +1,281 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +import QtQuick +import QtQuick.Layouts +import QtQuick.Controls +import QtQuick.Shapes +import QtQuick.Dialogs + +Item { + property real scale: +scaleSlider.value.toFixed(4) + property color outlineColor: enableOutline.checked ? Qt.rgba(outlineColor.color.r, outlineColor.color.g, outlineColor.color.b, pathAlpha) : Qt.rgba(0,0,0,0) + property color fillColor: Qt.rgba(fillColor.color.r, fillColor.color.g, fillColor.color.b, pathAlpha) + property alias pathAlpha: alphaSlider.value + property alias outlineWidth: outlineWidth.value + property alias outlineStyle: outlineStyle.currentValue + property alias capStyle: capStyle.currentValue + property alias joinStyle: joinStyle.currentValue + property alias debugCurves: enableDebug.checked + property alias debugWireframe: enableWireframe.checked + property alias painterComparison: painterComparison.currentIndex + property alias painterComparisonColor: painterComparisonColor.color + property alias painterComparisonAlpha: painterComparisonColorAlpha.value + property alias outlineEnabled: enableOutline.checked + property alias gradientType: gradientType.currentIndex + property alias rendererName: rendererLabel.text + property alias preferCurve: rendererLabel.preferCurve + + property int subShape: pickSubShape.checked ? subShapeSelector.value : -1 + + property real pathMargin: 150 + + function setScale(x) { + scaleSlider.value = x + } + + signal pathChanged + function updatePath() + { + pathChanged() + } + + ColumnLayout { + anchors.fill: parent + anchors.margins: 10 + RowLayout { + Label { + text: "Renderer:" + color: "white" + } + Label { + id: rendererLabel + property bool preferCurve: true + color: "white" + text: "Unknown" + + TapHandler { + onTapped: { + rendererLabel.preferCurve = !rendererLabel.preferCurve + } + } + } + CheckBox { id: enableDebug } + Label { + text: "Debug" + color: "white" + } + CheckBox { id: enableWireframe } + Label { + text: "Wireframe" + color: "white" + } + ComboBox { + id: painterComparison + model: [ + "No QPainter comparison", + "Overlaid QPainter comparison", + "Underlaid QPainter comparison" + ] + } + Rectangle { + id: painterComparisonColor + color: "red" + width: 20 + height: 20 + MouseArea { + anchors.fill: parent + onClicked: { + painterComparisonColorDialog.open() + } + } + } + Slider { + id: painterComparisonColorAlpha + from: 0.0 + to: 1.0 + value: 1.0 + } + Label { + text: "Alpha" + color: "white" + } + CheckBox { id: pickSubShape } + Label { + text: "Pick SVG sub-shape" + color: "white" + } + SpinBox { + id: subShapeSelector + visible: pickSubShape.checked + value: 0 + to: 999 + editable: true + } + } + RowLayout { + Label { + text: "Scale:" + color: "white" + } + TextField { + id: scaleEdit + text: scaleSlider.value.toFixed(4) + onEditingFinished: { + let val = +text + if (val > 0) + scaleSlider.value = val + } + } + Slider { + id: scaleSlider + Layout.fillWidth: true + from: 0.01 + to: 500.0 + value: 0.2 + onValueChanged: scaleEdit.text = value.toFixed(4) + } + } + RowLayout { + Label { + text: "Fill color" + color: "white" + } + Rectangle { + id: fillColor + color: "#ffffff" + width: 20 + height: 20 + MouseArea { + anchors.fill: parent + onClicked: { + fillColorDialog.open() + } + } + } + ComboBox { + id: gradientType + model: [ "NoGradient", "LinearGradient", "RadialGradient", "ConicalGradient" ] + } + Label { + text: "Path alpha:" + color: "white" + } + Slider { + id: alphaSlider + Layout.fillWidth: true + from: 0.0 + to: 1.0 + value: 1.0 + } + } + RowLayout { + CheckBox { + id: enableOutline + text: "Enable outline" + palette.windowText: "white" + } + RowLayout { + opacity: enableOutline.checked ? 1 : 0 + Label { + text: "Outline color:" + color: "white" + } + Rectangle { + id: outlineColor + property color selectedColor: "#33ccbb" + color: selectedColor + width: 20 + height: 20 + MouseArea { + anchors.fill: parent + onClicked: { + outlineColorDialog.open() + } + } + } + ComboBox { + id: outlineStyle + textRole: "text" + valueRole: "style" + model: ListModel { + ListElement { + text: "Solid line" + style: ShapePath.SolidLine + } + ListElement { + text: "Dash line" + style: ShapePath.DashLine + } + } + } + ComboBox { + id: joinStyle + textRole: "text" + valueRole: "style" + model: ListModel { + ListElement { + text: "Miter join" + style: ShapePath.MiterJoin + } + ListElement { + text: "Bevel join" + style: ShapePath.BevelJoin + } + ListElement { + text: "Round join" + style: ShapePath.RoundJoin + } + } + } + ComboBox { + id: capStyle + textRole: "text" + valueRole: "style" + model: ListModel { + ListElement { + text: "Square cap" + style: ShapePath.SquareCap + } + ListElement { + text: "Round cap" + style: ShapePath.RoundCap + } + ListElement { + text: "Flat cap" + style: ShapePath.FlatCap + } + } + } + + Label { + text: "Outline width" + color: "white" + } + Slider { + id: outlineWidth + Layout.fillWidth: true + from: 0.0 + to: 30.0 + value: 10.0 + } + } + } + + } + ColorDialog { + id: outlineColorDialog + selectedColor: outlineColor.selectedColor + onAccepted: outlineColor.selectedColor = selectedColor + } + ColorDialog { + id: fillColorDialog + selectedColor: fillColor.color + onAccepted: fillColor.color = selectedColor + } + ColorDialog { + id: painterComparisonColorDialog + selectedColor: painterComparisonColor.color + onAccepted: painterComparisonColor.color = selectedColor + } +} diff --git a/tests/manual/painterpathquickshape/ControlPoint.qml b/tests/manual/painterpathquickshape/ControlPoint.qml new file mode 100644 index 0000000000..190d759392 --- /dev/null +++ b/tests/manual/painterpathquickshape/ControlPoint.qml @@ -0,0 +1,47 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +import QtQuick +import QtQuick.Shapes + +Rectangle { + id: point1 + color: "red" + border.width: 1 + border.color: "black" + opacity: 0.3 + width: 20 + height: 20 + + property real cx: 400 + property real cy: 800 + + property point pt: Qt.point(cx, cy) + + DragHandler { + xAxis.minimum: -controlPanel.pathMargin + yAxis.minimum: -controlPanel.pathMargin + } + onXChanged: { + cx = (x + width/2) / controlPanel.scale + controlPanel.updatePath() + } + onYChanged: { + cy = (y + height/2) / controlPanel.scale + controlPanel.updatePath() + } + + Component.onCompleted: { + x = cx * controlPanel.scale - width/2 + y = cy * controlPanel.scale - height/2 + } + + Connections { + target: controlPanel + function onScaleChanged() { + x = cx * controlPanel.scale - width/2 + y = cy * controlPanel.scale - height/2 + } + } + +} diff --git a/tests/manual/painterpathquickshape/ControlledShape.qml b/tests/manual/painterpathquickshape/ControlledShape.qml new file mode 100644 index 0000000000..7cdc866fd3 --- /dev/null +++ b/tests/manual/painterpathquickshape/ControlledShape.qml @@ -0,0 +1,134 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +import QtQuick +import QtQuick.Shapes +import io.qt + +Item { + id: topLevel + property alias fillColor: shapePath.fillColor + property alias strokeStyle: shapePath.strokeStyle + property alias capStyle: shapePath.capStyle + property alias strokeColor: shapePath.strokeColor + property alias strokeWidth: shapePath.strokeWidth + property alias fillRule: shapePath.fillRule + + property alias startX: shapePath.startX + property alias startY: shapePath.startY + + property rect boundingRect: shape.boundingRect + + width: boundingRect.width + height: boundingRect.height + + property vector2d startPoint: "0,0" + property list delegate + + LinearGradient { + id: linearGradient + x1: shape.boundingRect.left + y1: shape.boundingRect.top + x2: shape.boundingRect.right + y2: shape.boundingRect.bottom + GradientStop { position: 0; color: fillColor } + GradientStop { position: 1; color: Qt.rgba(1 - fillColor.r, 1 - fillColor.g, 1 - fillColor.b, 1) } + } + + RadialGradient { + id: radialGradient + centerX: 0.5 * (shape.boundingRect.right + shape.boundingRect.left) + centerY: 0.5 * (shape.boundingRect.top + shape.boundingRect.bottom) + focalX: centerX + focalY: centerY + centerRadius: 0.5 * (shape.boundingRect.right - shape.boundingRect.left) + focalRadius: 0.1 + GradientStop { position: 0.0; color: fillColor } + GradientStop { position: 1.0; color: Qt.rgba(1 - fillColor.r, 1 - fillColor.g, 1 - fillColor.b, 1) } + } + + ConicalGradient { + id: conicalGradient + centerX: 0.5 * (shape.boundingRect.right + shape.boundingRect.left) + centerY: 0.5 * (shape.boundingRect.top + shape.boundingRect.bottom) + GradientStop { position: 0.0; color: fillColor } + GradientStop { position: 0.5; color: Qt.rgba(1 - fillColor.r, 1 - fillColor.g, 1 - fillColor.b, 1) } + GradientStop { position: 1.0; color: fillColor } + } + + property var gradients: [ null, linearGradient, radialGradient, conicalGradient ] + + Shape { + id: shape + x: 0 + y: 0 + preferredRendererType: controlPanel.preferCurve ? Shape.CurveRenderer : Shape.UnknownRenderer + onRendererTypeChanged: { + controlPanel.rendererName = rendererType == Shape.SoftwareRenderer ? "Software" : + rendererType == Shape.GeometryRenderer ? "Geometry" : + rendererType == Shape.CurveRenderer ? "Curve" : "Unknown"; + } + + transform: [ + Scale { + xScale: controlPanel.scale + yScale: controlPanel.scale + origin.x: shape.implicitWidth / 2 + origin.y: shape.implicitHeight / 2 + } + ] + + ShapePath { + id: shapePath + fillRule: ShapePath.WindingFill + fillGradient: gradients[controlPanel.gradientType] + strokeColor: controlPanel.outlineColor + fillColor: controlPanel.fillColor + strokeWidth: controlPanel.outlineWidth + strokeStyle: controlPanel.outlineStyle + joinStyle: controlPanel.joinStyle + capStyle: controlPanel.capStyle + } + + Repeater { + model: topLevel.delegate + onModelChanged: { + shapePath.pathElements = [] + for (var i = 0; i < model.length; ++i) + shapePath.pathElements.push(model[i]) + } + } + } + + Connections { + target: controlPanel + function onPathChanged() { + debugPaintPath.update() + } + } + + DebugPaintItem { + id: debugPaintPath + shape: shapePath + visible: controlPanel.painterComparison > 0 + color: controlPanel.painterComparisonColor + opacity: controlPanel.painterComparisonAlpha + z: controlPanel.painterComparison > 1 ? -1 : 0 + pathScale: controlPanel.scale + fillRule: topLevel.fillRule + strokeStyle: topLevel.strokeStyle + strokeColor: controlPanel.outlineEnabled ? Qt.rgba(1 - color.r, 1 - color.g, 1 - color.b, 1) : "transparent" + capStyle: topLevel.capStyle + joinStyle: controlPanel.joinStyle + strokeWidth: topLevel.strokeWidth + + width: visible ? (shape.boundingRect.width + shape.boundingRect.x) * controlPanel.scale : 1 + height: visible ? (shape.boundingRect.height + shape.boundingRect.y) * controlPanel.scale : 1 + } + + DebugVisualizationController { + showCurves: controlPanel.debugCurves + showWireframe: controlPanel.debugWireframe + onSettingsChanged: { shapePath.changed() } + } +} diff --git a/tests/manual/painterpathquickshape/CubicShape.qml b/tests/manual/painterpathquickshape/CubicShape.qml new file mode 100644 index 0000000000..a492a3f91d --- /dev/null +++ b/tests/manual/painterpathquickshape/CubicShape.qml @@ -0,0 +1,73 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +import QtQuick +import QtQuick.Shapes + +ControlledShape { + fillRule: ShapePath.OddEvenFill + delegate: [ + PathMove { x: start.cx; y: start.cy}, + PathCubic { x: end.cx; y: end.cy; + control1X: control1.cx; control1Y: control1.cy + control2X: control2.cx; control2Y: control2.cy }, + PathMove { x: start2.cx; y: start2.cy }, + PathCubic { x: end2.cx; y: end2.cy; + control1X: control21.cx; control1Y: control21.cy + control2X: control22.cx; control2Y: control22.cy }, + PathLine { x: lineEnd.cx; y: lineEnd.cy } + ] + + // Cubic path 1 + ControlPoint { + id: start + cx: 200 + cy: 400 + } + ControlPoint { + id: control1 + color: "blue" + cx: 800 + cy: 0 + } + ControlPoint { + id: control2 + color: "blue" + cx: 800 + cy: 1000 + } + ControlPoint { + id: end + cx: 200 + cy: 600 + } + + // Cubic path 2 + ControlPoint { + id: start2 + cx: 2200 + cy: 200 + } + ControlPoint { + id: control21 + color: "blue" + cx: 1200 + cy: 600 + } + ControlPoint { + id: control22 + color: "blue" + cx: 3200 + cy: 1000 + } + ControlPoint { + id: end2 + cx: 2200 + cy: 1400 + } + ControlPoint { + id: lineEnd + cx: 1200 + cy: 200 + } +} diff --git a/tests/manual/painterpathquickshape/FONTLOG.txt b/tests/manual/painterpathquickshape/FONTLOG.txt new file mode 100644 index 0000000000..138634b237 --- /dev/null +++ b/tests/manual/painterpathquickshape/FONTLOG.txt @@ -0,0 +1,22 @@ +1. FONTLOG for Graziano + + This file provides detailed information on the Graziano + Font Software. This information should be distributed along with + the Graziano font and any derivative works. + +2. Basic Font Information + + A font based on my left-handed handwriting. + To take notes with Block and lowercase letters on plain + white paper without any guideline (with a large-tipped pen) + is a good training ground for writing. + +3. ChangeLog + + 28 May 2011 (Graziano Capelli) Graziano version 1.0. + - Initial release + +4 Acknowledgements + + None yet. Feature requests, bug reports and contributions should be + sent to Graziano Capelli (air at shweb dot it; femtosoft at libero dot it) diff --git a/tests/manual/painterpathquickshape/Graziano.ttf b/tests/manual/painterpathquickshape/Graziano.ttf new file mode 100644 index 0000000000..2c3bd35b9a Binary files /dev/null and b/tests/manual/painterpathquickshape/Graziano.ttf differ diff --git a/tests/manual/painterpathquickshape/OFL-FAQ.txt b/tests/manual/painterpathquickshape/OFL-FAQ.txt new file mode 100644 index 0000000000..f351a36568 --- /dev/null +++ b/tests/manual/painterpathquickshape/OFL-FAQ.txt @@ -0,0 +1,534 @@ +OFL FAQ - Frequently Asked Questions about the SIL Open Font License (OFL) +Version 1.1-update1 - 31 March 2009 +(See http://scripts.sil.org/OFL for updates) + + +1 ABOUT USING AND DISTRIBUTING FONTS LICENSED UNDER THE OFL + +1.1 Can I use the fonts in any publication, even embedded in the file? +Yes. You may use them like most other fonts, but unlike some fonts you may +include an embedded subset of the fonts in your document. Such use does not +require you to include this license or other files (listed in OFL condition 2), +nor does it require any type of acknowledgement within the publication. Some +mention of the font name within the publication information (such as in a +colophon) is usually appreciated. If you wish to include the complete font as a +separate file, you should distribute the full font package, including all +existing acknowledgements, and comply with the OFL conditions. Of course, +referencing or embedding an OFL font in any document does not change the +license of the document itself. The requirement for fonts to remain under the +OFL does not apply to any document created using the fonts and their +derivatives. Similarly, creating any kind of graphic using a font under OFL +does not make the resulting artwork subject to the OFL. + +1.2 Can I make web pages using these fonts? +Yes! Go ahead! Using CSS (Cascading Style Sheets) is recommended. Direct usage +of fonts retrieved from a remote server - also referred to as font linking - +using cross-platform open standards like @font-face is encouraged. This is +considered to be use and distribution for which the OFL explicitly grants +permission. The font file itself is not embedded in the webpage but referenced +through a web address (i.e. a URI regardless of file format or access protocol) +which will cause the browser to retrieve and use the corresponding font to +render the webpage. This usage scenario is different from font embedding within +a document (i.e. a PDF) where the font or some of its elements become part of +the document. Note that embedding in a document is also permitted by the +license as indicated in 1.1. (See 1.10 for details related to URL-based access +restrictions methods or DRM mechanisms). + +1.3 Can I make the fonts available to others from my web site? +Yes, as long as you meet the conditions of the license (do not sell by itself, +include the necessary files, include the necessary copyright and license +information, rename Modified Versions, do not abuse the Author(s)' name(s) and +do not sublicense). In the case where you are hosting fonts to be served on the +web, make sure the file contains the needed copyright notice(s) and licensing +information in its metadata. Please double-check the accuracy of every field to +prevent contradictory information. If you are making the font available for use +via the @font-face open standard, putting this information in the standard font +metadata fields is sufficient. Other font formats, including EOT and proposed +superior alternatives, already provide fields for this information. + +1.4 Can the fonts be included with Free/Libre and Open Source Software +collections such as GNU/Linux and BSD distributions? +Yes! Fonts licensed under the OFL can be freely aggregated with software under +FLOSS (Free/Libre and Open Source Software) licenses. Since fonts are much more +useful aggregated to than merged with existing software, possible +incompatibility with existing software licenses is not a problem. You can also +repackage the fonts and the accompanying components in a .rpm or .deb package +and include them in distro CD/DVDs and online repositories. + +1.5 I want to distribute the fonts with my program. Does this mean my program +also has to be free/libre and open source software? +No. Only the portions based on the Font Software are required to be released +under the OFL. The intent of the license is to allow aggregation or bundling +with software under restricted licensing as well. + +1.6 Can I include the fonts on a CD of freeware or commercial fonts? +Yes, as long some other font or software is also on the disk, so the OFL font +is not sold by itself. + +1.7 Can I sell a software package that includes these fonts? +Yes, you can do this with both the Original Version and a Modified Version. +Examples of bundling made possible by the OFL would include: word processors, +design and publishing applications, training and educational software, +edutainment software, etc. + +1.8 Why won't the OFL let me sell the fonts alone? +The intent is to keep people from making money by simply redistributing the +fonts. The only people who ought to profit directly from the fonts should be +the original authors, and those authors have kindly given up potential direct +income to distribute their fonts under the OFL. Please honor and respect their +contribution! + +1.9 I've come across a font released under the OFL. How can I easily get more +information about the Original Version? How can I know where it stands compared +to the Original Version or other Modified Versions? +Consult the copyright statement(s) in the license for ways to contact the +original authors. Consult the FONTLOG for information on how the font differs +from the Original Version, and get in touch with the various contributors via +the information in the acknowledgment section. Please consider using the +Original Versions of the fonts whenever possible. + +1.10 What do you mean in condition 4? Can you provide examples of abusive +promotion / endorsement / advertisement vs. normal acknowledgement? +The intent is that the goodwill and reputation of the author(s) should not be +used in a way that makes it sound like the original author(s) endorse or +approve of a specific Modified Version or software bundle. For example, it +would not be right to advertise a word processor by naming the author(s) in a +listing of software features, or to promote a Modified Version on a web site by +saying "designed by ...". However, it would be appropriate to acknowledge the +author(s) if your software package has a list of people who deserve thanks. We +realize that this can seem to be a gray area, but the standard used to judge an +acknowledgement is that if the acknowledgement benefits the author(s) it is +allowed, but if it primarily benefits other parties, or could reflect poorly on +the author(s), then it is not. + +1.11 Can Font Software released under the OFL be subject to URL-based access +restrictions methods or DRM mechanisms? +Yes, but these issues are out-of-scope for the OFL. The license itself neither +encourages their use nor prohibits them since such mechanisms are not +implemented in the components of the Font Software but through external +software. Such restrictions are put in place for many different purposes +corresponding to various usage scenarios. One common example is to limit +potentially dangerous cross-site scripting attacks. However, in the spirit of +libre/open fonts and unrestricted writing systems, we strongly encourage open +sharing and reuse of OFL fonts, and the establishment of an environment where +such restrictions are unnecessary. Note that whether you wish to use such +mechanisms or you prefer not to, you must still abide by the rules set forth by +the OFL when using fonts released by their authors under this license. +Derivative fonts must be licensed under the OFL, even if they are part of a +service for which you charge fees and/or for which access to source code is +restricted. You may not sell the fonts on their own - they must be part of a +larger software package or bundle. For example, even if the OFL font is +distributed in a software package or via an online service using a DRM +mechanism, the user would still have the right to extract that font, use, +study, modify and redistribute it under the OFL. + +1.12 What about distributing fonts with a document? Within a compressed folder +structure like an OpenDocument file (.odt) for example? Is it redistribution, +bundling or embedding? +The vast majority of the time, documents circulated in electronic form +reference a font name which will match the corresponding font on the target +system but do not carry the font within themselves. There may, however, be some +cases where you need to bundle a font with the document. Certain document +formats may allow the inclusion of an unmodified font within their file +structure which consists of a compressed folder containing the various +resources forming the document (such as pictures and thumbnails). Including +fonts within such a structure is understood as being different from embedding +but rather similar to bundling (or mere aggregation) for which the licensing +makes explicit provision. In this case the font is conveyed unchanged whereas +embedding a font transforms it from the original format. The OFL does not allow +anyone to extract the font from such a structure to then redistribute it under +another license. The explicit permission to redistribute and embed does not +cancel the requirement for the Font Software to remain under the license chosen +by its Author(s). + +1.13 If OFL fonts are extracted from a document in which they are embedded +(such as a PDF file), what can be done with them? Is this a risk to Author(s)? +The few utilities that can extract fonts embedded in a PDF will only output +limited amounts of outlines - not a complete font. To create a working font +from this method is much more difficult than finding the source of the original +OFL font. So there is little chance that an OFL font would be extracted and +redistributed inappropriately through this method. Even so, copyright laws +address any misrepresentation of authorship. All Font Software released under +the OFL and marked as such by the Author(s) is intended to remain under this +license regardless of the distribution method, and cannot be redistributed +under any other license. We strongly discourage any font extraction - we +recommend directly using the font sources instead - but if you extract font +outlines from a document please be considerate, use your common sense and +respect the work of the Author(s) and the licensing model. + +1.14 What about sharing OFL fonts with friends on a CD, DVD or USB stick? +You are very welcome to share open fonts with friends, family and colleagues on +such removable media. Please make sure that you share and share-alike as much +as possible from what the Author(s) released and that you don't strip away +useful information which may not be present in the binary font files +themselves. Just remember that in the case where you sell the font, it has to +come bundled with software. + + +2 ABOUT MODIFYING OFL LICENSED FONTS + +2.1 Can I change the fonts? Are there any limitations to what things I can and +cannot change? +You are allowed to change anything, as long as such changes do not violate the +terms of the license. In other words, you are not allowed to remove the +copyright statement(s) from the font, but you could add additional information +into it that covers your contribution. + +2.2 I have a font that needs a few extra glyphs - can I take them from an OFL +licensed font and copy them into mine? +Yes, but if you distribute that font to others it must be under the OFL, and +include the information mentioned in condition 2 of the license. + +2.3 Can I charge people for my additional work? In other words, if I add a +bunch of special glyphs and/or OpenType/Graphite code, can I sell the enhanced +font? +Not by itself. Derivative fonts must be released under the OFL and cannot be +sold by themselves. It is permitted, however, to include them in a larger +software package (such as text editors, office suites or operating systems), +even if the larger package is sold. In that case, you are strongly encouraged, +but not required, to also make that derived font easily and freely available +outside of the larger package. + +2.4 Can I pay someone to enhance the fonts for my use and distribution? +Yes. This is a good way to fund the further development of the fonts. Keep in +mind, however, that if the font is distributed to others it must be under the +OFL. You won't be able to recover your investment by exclusively selling the +font, but you will be making a valuable contribution to the community. Please +remember how you have benefitted from the contributions of others. + +2.5 I need to make substantial revisions to the font to make it work with my +program. It will be a lot of work, and a big investment, and I want to be sure +that it can only be distributed with my program. Can I restrict its use? +No. If you redistribute a Modified Version of the font it must be under the +OFL. You may not restrict it in any way. This is intended to ensure that all +released improvements to the fonts become available to everyone. But you will +likely get an edge over competitors by being the first to distribute a bundle +with the enhancements. Again, please remember how you have benefitted from the +contributions of others. + +2.6 Do I have to make any derivative fonts (including extended source files, +build scripts, documentation, etc.) publicly available? +No, but please do share your improvements with others. You may find that you +receive more than what you gave in return. + +2.7 Why can't I use the Reserved Font Name(s) in my derivative font names? I'd +like people to know where the design came from. +The best way to acknowledge the source of the design is to thank the original +authors and any other contributors in the files that are distributed with your +revised font (although no acknowledgement is required). The FONTLOG is a +natural place to do this. Reserved Font Name(s) ensure that the only fonts that +have the original names are the unmodified Original Versions. This allows +designers to maintain artistic integrity while allowing collaboration to +happen. It eliminates potential confusion and name conflicts. When choosing a +name be creative and avoid names that reuse almost all the same letters in the +same order or sound like the original. Keep in mind that the Copyright +Holder(s) can allow a specific trusted partner to use Reserved Font Name(s) +through a separate written agreement. + +2.8 What do you mean by "primary name as presented to the user"? Are you +referring to the font menu name? +Yes, the requirement to change the visible name used to differentiate the font +from others applies to the font menu name and other mechanisms to specify a +font in a document. It would be fine, for example, to keep a text reference to +the original fonts in the description field, in your modified source file or in +documentation provided alongside your derivative as long as no one could be +confused that your modified source is the original. But you cannot use the +Reserved Font Names in any way to identify the font to the user (unless the +Copyright Holder(s) allow(s) it through a separate agreement; see section 2.7). +Users who install derivatives ("Modified Versions") on their systems should not +see any of the original names ("Reserved Font Names") in their font menus, for +example. Again, this is to ensure that users are not confused and do not +mistake a font for another and so expect features only another derivative or +the Original Version can actually offer. Ultimately, creating name conflicts +will cause many problems for the users as well as for the designer of both the +Original and Modified versions, so please think ahead and find a good name for +your own derivative. Font substitution systems like fontconfig, or +application-level font fallback configuration within OpenOffice.org or Scribus, +will also get very confused if the name of the font they are configured to +substitute to actually refers to another physical font on the user's hard +drive. It will help everyone if Original Versions and Modified Versions can +easily be distinguished from one another and from other derivatives. The +substitution mechanism itself is outside the scope of the license. Users can +always manually change a font reference in a document or set up some kind of +substitution at a higher level but at the lower level the fonts themselves have +to respect the Reserved Font Name(s) requirement to prevent ambiguity. If a +substitution is currently active the user should be aware of it. + +2.9 Am I not allowed to use any part of the Reserved Font Names? +You may not use the words of the font names, but you would be allowed to use +parts of words, as long as you do not use any word from the Reserved Font Names +entirely. We do not recommend using parts of words because of potential +confusion, but it is allowed. For example, if "Foobar" was a Reserved Font +Name, you would be allowed to use "Foo" or "bar", although we would not +recommend it. Such an unfortunate choice would confuse the users of your fonts +as well as make it harder for other designers to contribute. + +2.10 So what should I, as an author, identify as Reserved Font Names? +Original authors are encouraged to name their fonts using clear, distinct +names, and only declare the unique parts of the name as Reserved Font Names. +For example, the author of a font called "Foobar Sans" would declare "Foobar" +as a Reserved Font Name, but not "Sans", as that is a common typographical +term, and may be a useful word to use in a derivative font name. Reserved Font +Names should also be single words. A font called "Flowing River" should have +Reserved Font Names "Flowing" and "River", not "Flowing River". + +2.11 Do I, as an author, have to identify any Reserved Font Names? +No, but we strongly encourage you to do so. This is to avoid confusion between +your work and Modified versions. You may, however, give certain trusted parties +the right to use any of your Reserved Font Names through separate written +agreements. For example, even if "Foobar" is a RFN, you could write up an +agreement to give company "XYZ" the right to distribute a modified version with +a name that includes "Foobar". This allows for freedom without confusion. + +2.12 Are any names (such as the main font name) reserved by default? +No. That is a change to the license as of version 1.1. If you want any names to +be Reserved Font Names, they must be specified after the copyright statement(s). + +2.13 What is this FONTLOG thing exactly? +It has three purposes: 1) to provide basic information on the font to users and +other developers, 2) to document changes that have been made to the font or +accompanying files, either by the original authors or others, and 3) to provide +a place to acknowledge the authors and other contributors. Please use it! See +below for details on how changes should be noted. + +2.14 Am I required to update the FONTLOG? +No, but users, designers and other developers might get very frustrated at you +if you don't! People need to know how derivative fonts differ from the +original, and how to take advantage of the changes, or build on them. + + +3 ABOUT THE FONTLOG + +The FONTLOG can take a variety of formats, but should include these four +sections: + +3.1 FONTLOG for +This file provides detailed information on the Font Software. +This information should be distributed along with the fonts +and any derivative works. + +3.2 Basic Font Information +(Here is where you would describe the purpose and brief specifications for the +font project, and where users can find more detailed documentation. It can also +include references to how changes can be contributed back to the Original +Version. You may also wish to include a short guide to the design, or a +reference to such a document.) + +3.3 ChangeLog +(This should list both major and minor changes, most recent first. Here are +some examples:) + +7 February 2007 (Pat Johnson) Version 1.3 +- Added Greek and Cyrillic glyphs +- Released as "" + +7 March 2006 (Fred Foobar) Version 1.2 +- Tweaked contextual behaviours +- Released as "" + +1 Feb 2005 (Jane Doe) Version 1.1 +- Improved build script performance and verbosity +- Extended the smart code documentation +- Corrected minor typos in the documentation +- Fixed position of combining inverted breve below (U+032F) +- Added OpenType/Graphite smart code for Armenian +- Added Armenian glyphs (U+0531 -> U+0587) +- Released as "" + +1 Jan 2005 (Joe Smith) Version 1.0 +- Initial release of font "" + +3.4 Acknowledgements +(Here is where contributors can be acknowledged. + +If you make modifications be sure to add your name (N), email (E), web-address +(W) and description (D). This list is sorted by last name in alphabetical +order.) + +N: Jane Doe +E: jane@university.edu +W: http://art.university.edu/projects/fonts +D: Contributor - Armenian glyphs and code + +N: Fred Foobar +E: fred@foobar.org +W: http://foobar.org +D: Contributor - misc Graphite fixes + +N: Pat Johnson +E: pat@fontstudio.org +W: http://pat.fontstudio.org +D: Designer - Greek & Cyrillic glyphs based on Roman design + +N: Tom Parker +E: tom@company.com +W: http://www.company.com/tom/projects/fonts +D: Engineer - original smart font code + +N: Joe Smith +E: joe@fontstudio.org +W: http://joe.fontstudio.org +D: Designer - original Roman glyphs + +(Original authors can also include information here about their organization.) + + +4 ABOUT MAKING CONTRIBUTIONS + +4.1 Why should I contribute my changes back to the original authors? +It would benefit many people if you contributed back to what you've received. +Providing your contributions and improvements to the fonts and other components +(data files, source code, build scripts, documentation, etc.) could be a +tremendous help and would encourage others to contribute as well and 'give +back', which means you will have an opportunity to benefit from other people's +contributions as well. Sometimes maintaining your own separate version takes +more effort than merging back with the original. Be aware that any +contributions, however, must be either your own original creation or work that +you own, and you may be asked to affirm that clearly when you contribute. + +4.2 I've made some very nice improvements to the font, will you consider +adopting them and putting them into future Original Versions? +Most authors would be very happy to receive such contributions. Keep in mind +that it is unlikely that they would want to incorporate major changes that +would require additional work on their end. Any contributions would likely need +to be made for all the fonts in a family and match the overall design and +style. Authors are encouraged to include a guide to the design with the fonts. +It would also help to have contributions submitted as patches or clearly marked +changes (the use of smart source revision control systems like subversion, svk, +mercurial, git or bzr is a good idea). Examples of useful contributions are bug +fixes, additional glyphs, stylistic alternates (and the smart font code to +access them) or improved hinting. + +4.3 How can I financially support the development of OFL fonts? +It is likely that most authors of OFL fonts would accept financial +contributions - contact them for instructions on how to do this. Such +contributions would support future development. You can also pay for others to +enhance the fonts and contribute the results back to the original authors for +inclusion in the Original Version. + + +5 ABOUT THE LICENSE + +5.1 I see that this is version 1.1 of the license. Will there be later changes? +Version 1.1 is the first minor revision of the OFL. We are confident that +version 1.1 will meet most needs, but are open to future improvements. Any +revisions would be for future font releases, and previously existing licenses +would remain in effect. No retroactive changes are possible, although the +Copyright Holder(s) can re-release the font under a revised OFL. All versions +will be available on our web site: http://scripts.sil.org/OFL. + +5.2 Can I use the SIL Open Font License for my own fonts? +Yes! We heartily encourage anyone to use the OFL to distribute their own +original fonts. It is a carefully constructed license that allows great freedom +along with enough artistic integrity protection for the work of the authors as +well as clear rules for other contributors and those who redistribute the +fonts. Some additional information about using the OFL is included at the end +of this FAQ. + +5.3 Does this license restrict the rights of the Copyright Holder(s)? +No. The Copyright Holder(s) still retain(s) all the rights to their creation; +they are only releasing a portion of it for use in a specific way. For example, +the Copyright Holder(s) may choose to release a 'basic' version of their font +under the OFL, but sell a restricted 'enhanced' version. Only the Copyright +Holder(s) can do this. + +5.4 Is the OFL a contract or a license? +The OFL is a license and not a contract and so does not require you to sign it +to have legal validity. By using, modifying and redistributing components under +the OFL you indicate that you accept the license. + +5.5 How about translating the license and the FAQ into other languages? +SIL certainly recognises the need for people who are not familiar with English +to be able to understand the OFL and this FAQ better in their own language. +Making the license very clear and readable is a key goal of the OFL. + +If you are an experienced translator, you are very welcome to help by +translating the OFL and its FAQ so that designers and users in your language +community can understand the license better. But only the original English +version of the license has legal value and has been approved by the community. +Translations do not count as legal substitutes and should only serve as a way +to explain the original license. SIL - as the author and steward of the license +for the community at large - does not approve any translation of the OFL as +legally valid because even small translation ambiguities could be abused and +create problems. + +We give permission to publish unofficial translations into other languages +provided that they comply with the following guidelines: + +- put the following disclaimer in both English and the target language stating +clearly that the translation is unofficial: + +"This is an unofficial translation of the SIL Open Font License into $language. +It was not published by SIL International, and does not legally state the +distribution terms for fonts that use the OFL. A release under the OFL is only +valid when using the original English text. + +However, we recognize that this unofficial translation will help users and +designers not familiar with English to understand the SIL OFL better and make +it easier to use and release font families under this collaborative font design +model. We encourage designers who consider releasing their creation under the +OFL to read the FAQ in their own language if it is available. + +Please go to http://scripts.sil.org/OFL for the official version of the license +and the accompanying FAQ." + +- keep your unofficial translation current and update it at our request if +needed, for example if there is any ambiguity which could lead to confusion. + +If you start such a unofficial translation effort of the OFL and its +accompanying FAQ please let us know, thank you. + + +6 ABOUT SIL INTERNATIONAL + +6.1 Who is SIL International and what does it do? +SIL International is a worldwide faith-based education and development +organization (NGO) that studies, documents, and assists in developing the +world's lesser-known languages through literacy, linguistics, translation, and +other academic disciplines. SIL makes its services available to all without +regard to religious belief, political ideology, gender, race, or ethnic +background. SIL's members and volunteers share a Christian commitment. + +6.2 What does this have to do with font licensing? +The ability to read, write, type and publish in one's own language is one of +the most critical needs for millions of people around the world. This requires +fonts that are widely available and support lesser-known languages. SIL +develops - and encourages others to develop - a complete stack of writing +systems implementation components available under open licenses. This open +stack includes input methods, smart fonts, smart rendering libraries and smart +applications. There has been a need for a common open license that is +specifically applicable to fonts and related software (a crucial component of +this stack) so SIL developed the SIL Open Font License with the help of the +FLOSS community. + +6.3 How can I contact SIL? +Our main web site is: http://www.sil.org/ +Our site about complex scripts is: http://scripts.sil.org/ +Information about this license (including contact email information) is at: +http://scripts.sil.org/OFL + + +7 ABOUT USING THE OFL FOR YOUR ORIGINAL FONTS + +If you want to release your fonts under the OFL, you only need to do the +following: + +7.1 Put your copyright and reserved font names information in the beginning of +the main OFL file (simply use the dedicated placeholders). +7.2 Put your copyright and the OFL references in your various font files (such +as in the copyright, license and description fields) and in your other +components (build scripts, glyph databases, documentation, rendering samples, +etc). Accurate metadata in your font files is beneficial to you as an +increasing number of applications are exposing this information to the user. +For example, clickable links can bring users back to your website and let them +know about other work you have done or services you provide. Depending on the +format of your fonts and sources, you can use template human-readable headers +or machine-readable metadata. +7.3 Write an initial FONTLOG for your font and include it in the release +package. +7.4 Include the OFL license file in your release package. +7.5 We also highly recommend you include the relevant practical documentation +on the license by putting the OFL-FAQ in your package. +7.6 If you wish, you can use the OFL Graphics on your web page. + +That's all. If you have any more questions please get in touch with us. diff --git a/tests/manual/painterpathquickshape/OFL.txt b/tests/manual/painterpathquickshape/OFL.txt new file mode 100644 index 0000000000..8dce0ad901 --- /dev/null +++ b/tests/manual/painterpathquickshape/OFL.txt @@ -0,0 +1,93 @@ +Copyright (c) 2011, Graziano Capelli (air@shweb.it; femtosoft@libero.it). + +This Font Software is licensed under the SIL Open Font License, Version 1.1. +This license is copied below, and is also available with a FAQ at: +http://scripts.sil.org/OFL + + +----------------------------------------------------------- +SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 +----------------------------------------------------------- + +PREAMBLE +The goals of the Open Font License (OFL) are to stimulate worldwide +development of collaborative font projects, to support the font creation +efforts of academic and linguistic communities, and to provide a free and +open framework in which fonts may be shared and improved in partnership +with others. + +The OFL allows the licensed fonts to be used, studied, modified and +redistributed freely as long as they are not sold by themselves. The +fonts, including any derivative works, can be bundled, embedded, +redistributed and/or sold with any software provided that any reserved +names are not used by derivative works. The fonts and derivatives, +however, cannot be released under any other type of license. The +requirement for fonts to remain under this license does not apply +to any document created using the fonts or their derivatives. + +DEFINITIONS +"Font Software" refers to the set of files released by the Copyright +Holder(s) under this license and clearly marked as such. This may +include source files, build scripts and documentation. + +"Reserved Font Name" refers to any names specified as such after the +copyright statement(s). + +"Original Version" refers to the collection of Font Software components as +distributed by the Copyright Holder(s). + +"Modified Version" refers to any derivative made by adding to, deleting, +or substituting -- in part or in whole -- any of the components of the +Original Version, by changing formats or by porting the Font Software to a +new environment. + +"Author" refers to any designer, engineer, programmer, technical +writer or other person who contributed to the Font Software. + +PERMISSION & CONDITIONS +Permission is hereby granted, free of charge, to any person obtaining +a copy of the Font Software, to use, study, copy, merge, embed, modify, +redistribute, and sell modified and unmodified copies of the Font +Software, subject to the following conditions: + +1) Neither the Font Software nor any of its individual components, +in Original or Modified Versions, may be sold by itself. + +2) Original or Modified Versions of the Font Software may be bundled, +redistributed and/or sold with any software, provided that each copy +contains the above copyright notice and this license. These can be +included either as stand-alone text files, human-readable headers or +in the appropriate machine-readable metadata fields within text or +binary files as long as those fields can be easily viewed by the user. + +3) No Modified Version of the Font Software may use the Reserved Font +Name(s) unless explicit written permission is granted by the corresponding +Copyright Holder. This restriction only applies to the primary font name as +presented to the users. + +4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font +Software shall not be used to promote, endorse or advertise any +Modified Version, except to acknowledge the contribution(s) of the +Copyright Holder(s) and the Author(s) or with their explicit written +permission. + +5) The Font Software, modified or unmodified, in part or in whole, +must be distributed entirely under this license, and must not be +distributed under any other license. The requirement for fonts to +remain under this license does not apply to any document created +using the Font Software. + +TERMINATION +This license becomes null and void if any of the above conditions are +not met. + +DISCLAIMER +THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT +OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE +COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL +DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM +OTHER DEALINGS IN THE FONT SOFTWARE. diff --git a/tests/manual/painterpathquickshape/README.md b/tests/manual/painterpathquickshape/README.md new file mode 100644 index 0000000000..8ae04115ab --- /dev/null +++ b/tests/manual/painterpathquickshape/README.md @@ -0,0 +1,7 @@ +SVGs included: + peace_victory.svg: Public Domain by OpenClipart (https://freesvg.org/peace-sign) + hand-print.svg: Public Domain by OpenClipart (https://freesvg.org/palm-print-in-black-and-white) + 1535737773.svg: Public Domain by publicdomainvectors.org (https://freesvg.org/piece-of-cake-4) + +Fonts included: + Graziano.ttf: by Graziano Capelli (https://fontlibrary.org/en/font/graziano), OFL (SIL Open Font License) diff --git a/tests/manual/painterpathquickshape/SimpleShape.qml b/tests/manual/painterpathquickshape/SimpleShape.qml new file mode 100644 index 0000000000..479eafee88 --- /dev/null +++ b/tests/manual/painterpathquickshape/SimpleShape.qml @@ -0,0 +1,117 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +import QtQuick +import QtQuick.Shapes + +ControlledShape { + fillRule: ShapePath.OddEvenFill + delegate: [ + // A triangle + PathPolyline { + id: ppl + path: [ Qt.point(100.0, 100.0), + Qt.point(1250.0, 150.0), + Qt.point(100.0, 1000.0), + Qt.point(100, 100) + ] + }, + + // A very narrow shape with one convex and one concave curve + PathMove { x: 600; y: 1200}, + PathQuad { x: 800; y: 1200; controlX: 700; controlY: 600 }, + PathQuad { x: 600; y: 1200; controlX: 700; controlY: 700 }, + + // A more complex path with editable points + PathMove { x: p1.cx; y: p1.cy }, + PathQuad { x: p2.cx; y: p2.cy; controlX: c1.cx; controlY: c1.cy }, + PathQuad { x: p3.cx; y: p3.cy; controlX: c2.cx; controlY: c2.cy }, + PathQuad { x: p4.cx; y: p4.cy; controlX: c3.cx; controlY: c3.cy }, + PathLine { x: p5.cx; y: p5.cy }, + PathQuad { x: p6.cx; y: p6.cy; controlX: c5.cx; controlY: c5.cy }, + PathQuad { x: p7.cx; y: p7.cy; controlX: c6.cx; controlY: c6.cy }, + PathQuad { x: p8.cx; y: p8.cy; controlX: c7.cx; controlY: c7.cy } + ] + + // Control points for the editable part: + // Curve p1-c1-p2, Curve p2-c2-p3, Curve p3-c3-p4 + // Line p4-p5, Curve p5-c5-p6, Curve p6-c6-p7, Curve p7-c7-p8 + + ControlPoint { + id: p1 + cx: 100 + cy: 1000 + } + ControlPoint { + id: c1 + color: "blue" + cx: 200 + cy: 1500 + } + ControlPoint { + id: p2 + cx: 700 + cy: 1500 + } + ControlPoint { + id: c2 + color: "blue" + cx: 1200 + cy: 1500 + } + ControlPoint { + id: p3 + cx: 1200 + cy: 1000 + } + ControlPoint { + id: c3 + color: "blue" + cx: 1100 + cy: 700 + } + ControlPoint { + id: p4 + cx: 800 + cy: 600 + } + ControlPoint { + id: p5 + cx: 800 + cy: 800 + } + ControlPoint { + id: c5 + color: "blue" + cx: 1000 + cy: 600 + } + ControlPoint { + id: p6 + cx: 1000 + cy: 1000 + } + ControlPoint { + id: c6 + color: "blue" + cx: 1000 + cy: 1300 + } + ControlPoint { + id: p7 + cx: 700 + cy: 1300 + } + ControlPoint { + id: c7 + color: "blue" + cx: 400 + cy: 1300 + } + ControlPoint { + id: p8 + cx: 400 + cy: 1000 + } + +} diff --git a/tests/manual/painterpathquickshape/SmallPolygon.qml b/tests/manual/painterpathquickshape/SmallPolygon.qml new file mode 100644 index 0000000000..ccf4b8e796 --- /dev/null +++ b/tests/manual/painterpathquickshape/SmallPolygon.qml @@ -0,0 +1,49 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +import QtQuick +import QtQuick.Shapes + +ControlledShape { + fillRule: ShapePath.OddEvenFill + + delegate: [ + PathPolyline { + path: [ point1.pt, + point2.pt, + point3.pt, + point4.pt, + point5.pt, + point1.pt + ] + + } + ] + + ControlPoint { + id: point1 + cx: 400 + cy: 900 + } + ControlPoint { + id: point2 + cx: 1500 + cy: 600 + } + ControlPoint { + id: point3 + cx: 2500 + cy: 1100 + } + ControlPoint { + id: point4 + cx: 3000 + cy: 1100 + } + ControlPoint { + id: point5 + cx: 2000 + cy: 1900 + } + +} diff --git a/tests/manual/painterpathquickshape/Squircle.qml b/tests/manual/painterpathquickshape/Squircle.qml new file mode 100644 index 0000000000..4cdbd705a7 --- /dev/null +++ b/tests/manual/painterpathquickshape/Squircle.qml @@ -0,0 +1,60 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +import QtQuick +import QtQuick.Shapes + +ControlledShape { + delegate: [ + PathMove { x: p1.cx; y: p1.cy }, + PathQuad { x: p2.cx; y: p2.cy; controlX: c1.cx; controlY: c1.cy }, + PathQuad { x: p3.cx; y: p3.cy; controlX: c2.cx; controlY: c2.cy }, + PathQuad { x: p4.cx; y: p4.cy; controlX: c3.cx; controlY: c3.cy }, + //PathQuad { x: 600; y: 100; controlX: 100; controlY: 100 }, + PathLine { x: p5.cx; y: p5.cy } + ] + + ControlPoint { + id: p1 + cx: 600 + cy: 100 + } + ControlPoint { + id: c1 + color: "blue" + cx: 1100 + cy: 100 + } + ControlPoint { + id: p2 + cx: 1100 + cy: 600 + } + ControlPoint { + id: c2 + color: "blue" + cx: 1100 + cy: 1100 + } + ControlPoint { + id: p3 + cx: 600 + cy: 1100 + } + ControlPoint { + id: c3 + color: "blue" + cx: 100 + cy: 1100 + } + ControlPoint { + id: p4 + cx: 100 + cy: 600 + } + ControlPoint { + id: p5 + cx: 600 + cy: 330 + } +} diff --git a/tests/manual/painterpathquickshape/SvgShape.qml b/tests/manual/painterpathquickshape/SvgShape.qml new file mode 100644 index 0000000000..83cb605694 --- /dev/null +++ b/tests/manual/painterpathquickshape/SvgShape.qml @@ -0,0 +1,91 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +import QtQuick +import QtQuick.Shapes +import QtQuick.Controls +import QtQuick.Dialogs +import io.qt + +Item { + id: topLevel + property var boundingRect: Qt.rect(0, 0, 100, 100) + + Text { + id: fileNameLabel + x: boundingRect.x + y: boundingRect.bottom * controlPanel.scale + font.pixelSize*2 + text: "Filename: " + pathLoader.source + } + + ToolButton { + anchors.left: fileNameLabel.right + anchors.top: fileNameLabel.top + anchors.leftMargin: 10 + text: "..." + + onClicked: { + fileDialog.visible = true + } + } + + property var children: [] + SvgPathLoader { + id: pathLoader + function reload() + { + for (var j = 0; j < children.length; ++j) { + children[j].destroy() + } + children = [] + + let first = true + let pickOne = controlPanel.subShape + if (pickOne < 0) + console.debug("Creating " + pathLoader.paths.length + " SVG items") + else + console.log("Creating SVG item", pickOne, "out of", pathLoader.paths.length) + for (var i = 0; i < pathLoader.paths.length; ++i) { + if (pickOne >= 0 && pickOne !== i) + continue + var s = pathLoader.paths[i] + var fillColor = pathLoader.fillColors[i] + var obj = Qt.createQmlObject("import QtQuick\nimport QtQuick.Shapes\n ControlledShape { fillColor: \"" + fillColor + "\"; fillRule: ShapePath.WindingFill; delegate: [ PathSvg { path: \"" + s + "\"; } ] }", topLevel, "SvgPathComponent_" + i) + children.push(obj) + if (first) { + topLevel.boundingRect = obj.boundingRect + first = false + } else { + var minX = Math.min(topLevel.boundingRect.x, obj.boundingRect.x) + var minY = Math.min(topLevel.boundingRect.y, obj.boundingRect.y) + var maxX = Math.max(topLevel.boundingRect.x + topLevel.boundingRect.width, + obj.boundingRect.x + obj.boundingRect.width) + var maxY = Math.max(topLevel.boundingRect.y + topLevel.boundingRect.height, + obj.boundingRect.y + obj.boundingRect.height) + + topLevel.boundingRect = Qt.rect(minX, minY, maxX - minX, maxY - minY) + } + } + console.debug("Finished SVG") + } + onPathsChanged: reload() + + Component.onCompleted: { + pathLoader.source = "qrc:/1535737773.svg" + } + } + + Connections { + target: controlPanel + function onSubShapeChanged() { pathLoader.reload() } + } + + FileDialog { + id: fileDialog + title: "Please choose a file" + onAccepted: { + pathLoader.source = fileDialog.selectedFile + fileDialog.visible = false + } + } +} diff --git a/tests/manual/painterpathquickshape/TextShape.qml b/tests/manual/painterpathquickshape/TextShape.qml new file mode 100644 index 0000000000..fa595d3390 --- /dev/null +++ b/tests/manual/painterpathquickshape/TextShape.qml @@ -0,0 +1,31 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +import QtQuick +import QtQuick.Shapes +import QtQuick.Controls +import QtQuick.Dialogs + +ControlledShape { + fillRule: ShapePath.OddEvenFill + delegate: [ + PathText { + text: "foobar" + font: fontDialog.selectedFont + } + ] + + FontDialog { + id: fontDialog + currentFont.family: "Graziano" + currentFont.pixelSize: 500 + } + + Button { + anchors.top: parent.bottom + anchors.left: parent.left + text: "Select font" + onClicked: fontDialog.open() + } + +} diff --git a/tests/manual/painterpathquickshape/arcDirection.qml b/tests/manual/painterpathquickshape/arcDirection.qml new file mode 100644 index 0000000000..d50e60be24 --- /dev/null +++ b/tests/manual/painterpathquickshape/arcDirection.qml @@ -0,0 +1,37 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +import QtQuick +import QtQuick.Shapes + +Item { + Repeater { + anchors.fill: parent + model: 2 + delegate: ControlledShape { + id: delegate + + required property int index + + anchors.fill: parent + fillColor: "transparent" + strokeColor: delegate.index === 0 ? "red" : "blue" + strokeStyle: ShapePath.DashLine + strokeWidth: 4 + + startX: 4 + startY: 4 + + delegate: [ + PathArc { + id: arc + x: 96 + y: 96 + radiusX: 100 + radiusY: 100 + direction: delegate.index === 0 ? PathArc.Clockwise : PathArc.Counterclockwise + } + ] + } + } +} diff --git a/tests/manual/painterpathquickshape/arcRotation.qml b/tests/manual/painterpathquickshape/arcRotation.qml new file mode 100644 index 0000000000..f4f3ab0074 --- /dev/null +++ b/tests/manual/painterpathquickshape/arcRotation.qml @@ -0,0 +1,72 @@ +// Copyright (C) 2021 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +import QtQuick +import QtQuick.Shapes + +Item { + Repeater { + anchors.fill: parent + model: 2 + delegate: ControlledShape { + id: delegate1 + + required property int index + + fillColor: "transparent" + strokeColor: delegate1.index === 0 ? "red" : "blue" + strokeStyle: ShapePath.DashLine + strokeWidth: 4 + + width: 200 + height: 200 + anchors.centerIn: parent + startX: 50 + startY: 100 + + delegate: [ + PathArc { + x: 150 + y: 100 + radiusX: 50 + radiusY: 20 + xAxisRotation: delegate1.index === 0 ? 0 : 45 + } + ] + } + } + + Repeater { + anchors.fill: parent + model: 2 + delegate: ControlledShape { + id: delegate2 + + required property int index + + width: 200 + height: 200 + anchors.centerIn: parent + + fillColor: "transparent" + strokeColor: delegate2.index === 0 ? "red" : "blue" + + startX: 50 + startY: 100 + + delegate: [ + PathArc { + x: 150 + y: 100 + radiusX: 50 + radiusY: 20 + xAxisRotation: delegate2.index === 0 ? 0 : 45 + direction: PathArc.Counterclockwise + } + ] + } + } +} diff --git a/tests/manual/painterpathquickshape/background.png b/tests/manual/painterpathquickshape/background.png new file mode 100644 index 0000000000..138103a4e0 Binary files /dev/null and b/tests/manual/painterpathquickshape/background.png differ diff --git a/tests/manual/painterpathquickshape/capStyles.qml b/tests/manual/painterpathquickshape/capStyles.qml new file mode 100644 index 0000000000..a88edc7efb --- /dev/null +++ b/tests/manual/painterpathquickshape/capStyles.qml @@ -0,0 +1,46 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +import QtQuick +import QtQuick.Shapes + +ControlledShape { + id: capTest + strokeColor: "green" + strokeWidth: 20 + fillColor: "transparent" + + property int capStyleIdx: 0 + readonly property variant styles: [ ShapePath.FlatCap, ShapePath.SquareCap, ShapePath.RoundCap ] + readonly property variant styleTexts: [ qsTr("FlatCap"), qsTr("SquareCap"), qsTr("RoundCap") ] + + capStyle: styles[capStyleIdx] + + startX: 40 + startY: 30 + delegate: [ + PathQuad { + x: 50 + y: 80 + controlX: 0 + controlY: 80 + }, + PathLine { + x: 150 + y: 80 + }, + PathQuad { + x: 160 + y: 30 + controlX: 200 + controlY: 80 + } + ] + + Timer { + interval: 1000 + repeat: true + running: true + onTriggered: capTest.capStyleIdx = (capTest.capStyleIdx + 1) % capTest.styles.length + } +} diff --git a/tests/manual/painterpathquickshape/cubicCurve.qml b/tests/manual/painterpathquickshape/cubicCurve.qml new file mode 100644 index 0000000000..39ded21e60 --- /dev/null +++ b/tests/manual/painterpathquickshape/cubicCurve.qml @@ -0,0 +1,103 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +import QtQuick +import QtQuick.Shapes + +ControlledShape { + id: shape + + strokeWidth: 4 + strokeColor: "black" + + startX: 50 + startY: 100 + delegate: [ + PathCubic { + x: 150 + y: 100 + control1X: cp1.x + control1Y: cp1.y + control2X: cp2.x + control2Y: cp2.y + } + ] + Rectangle { + id: cp1 + color: "red" + width: 10 + height: 10 + SequentialAnimation { + loops: Animation.Infinite + running: true + NumberAnimation { + target: cp1 + property: "x" + from: 0 + to: flickable.width - cp1.width + duration: 5000 + } + NumberAnimation { + target: cp1 + property: "x" + from: flickable.width - cp1.width + to: 0 + duration: 5000 + } + NumberAnimation { + target: cp1 + property: "y" + from: 0 + to: flickable.height - cp1.height + duration: 5000 + } + NumberAnimation { + target: cp1 + property: "y" + from: flickable.height - cp1.height + to: 0 + duration: 5000 + } + } + } + + Rectangle { + id: cp2 + color: "blue" + width: 10 + height: 10 + x: flickable.width - width + SequentialAnimation { + loops: Animation.Infinite + running: true + NumberAnimation { + target: cp2 + property: "y" + from: 0 + to: flickable.height - cp2.height + duration: 5000 + } + NumberAnimation { + target: cp2 + property: "y" + from: flickable.height - cp2.height + to: 0 + duration: 5000 + } + NumberAnimation { + target: cp2 + property: "x" + from: flickable.width - cp2.width + to: 0 + duration: 5000 + } + NumberAnimation { + target: cp2 + property: "x" + from: 0 + to: flickable.width - cp2.width + duration: 5000 + } + } + } +} diff --git a/tests/manual/painterpathquickshape/dashPattern.qml b/tests/manual/painterpathquickshape/dashPattern.qml new file mode 100644 index 0000000000..69fe8c23f3 --- /dev/null +++ b/tests/manual/painterpathquickshape/dashPattern.qml @@ -0,0 +1,36 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +import QtQuick +import QtQuick.Shapes + +ControlledShape { + id: shape + anchors.fill: parent + strokeWidth: 5 + strokeColor: "blue" + strokeStyle: ShapePath.DashLine + //dashPattern: [ 1, 4, 4, 4 ] + fillColor: "lightBlue" + property real xr: 70 + property real yr: 30 + startX: shape.width / 2 - xr + startY: shape.height / 2 - yr + + delegate: [ + PathArc { + x: shape.width / 2 + shape.xr + y: shape.height / 2 + shape.yr + radiusX: shape.xr + radiusY: shape.yr + useLargeArc: true + }, + PathArc { + x: shape.width / 2 - shape.xr + y: shape.height / 2 - shape.yr + radiusX: shape.xr + radiusY: shape.yr + useLargeArc: true + } + ] +} diff --git a/tests/manual/painterpathquickshape/debugpaintitem.cpp b/tests/manual/painterpathquickshape/debugpaintitem.cpp new file mode 100644 index 0000000000..011124a1a8 --- /dev/null +++ b/tests/manual/painterpathquickshape/debugpaintitem.cpp @@ -0,0 +1,170 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +#include "debugpaintitem.h" + +#include +#include + +DebugPaintItem::DebugPaintItem(QQuickItem *item) + : QQuickPaintedItem(item) +{ + connect(this, &DebugPaintItem::shapeChanged, this, &QQuickItem::update); + connect(this, &DebugPaintItem::colorChanged, this, &QQuickItem::update); + connect(this, &DebugPaintItem::pathScaleChanged, this, &QQuickItem::update); + connect(this, &DebugPaintItem::fillRuleChanged, this, &QQuickItem::update); + connect(this, &DebugPaintItem::strokeChanged, this, &QQuickItem::update); +} + +void DebugPaintItem::setShape(QQuickPath *p) +{ + if (p == m_path) + return; + + m_path = p; + emit shapeChanged(); +} + +QQuickPath *DebugPaintItem::shape() const +{ + return m_path; +} + +void DebugPaintItem::handlePathChanged() +{ + update(); +} + +void DebugPaintItem::paint(QPainter *p) +{ + if (!isVisible()) + return; + + if (m_path != nullptr) { + QPainterPath painterPath = m_path->path(); + painterPath.setFillRule(m_fillRule); + + p->scale(m_pathScale, m_pathScale); + p->setRenderHint(QPainter::Antialiasing); + if (m_strokeColor.alpha() > 0) { + QPen pen(m_strokeStyle); + pen.setWidthF(m_strokeWidth); + pen.setCapStyle(m_capStyle); + pen.setJoinStyle(m_joinStyle); + pen.setColor(m_strokeColor); + p->setPen(pen); + } else { + p->setPen(Qt::NoPen); + } + p->setBrush(m_color); + p->drawPath(painterPath); + } +} + +QColor DebugPaintItem::color() const +{ + return m_color; +} + +void DebugPaintItem::setColor(const QColor &color) +{ + if (m_color == color) + return; + + m_color = color; + emit colorChanged(); +} + +void DebugPaintItem::setPathScale(qreal pathScale) +{ + if (qFuzzyCompare(m_pathScale, pathScale)) + return; + + m_pathScale = pathScale; + emit pathScaleChanged(); +} + +qreal DebugPaintItem::pathScale() const +{ + return m_pathScale; +} + +void DebugPaintItem::setFillRule(Qt::FillRule fillRule) +{ + if (m_fillRule == fillRule) + return; + + m_fillRule = fillRule; + emit fillRuleChanged(); +} + +Qt::FillRule DebugPaintItem::fillRule() const +{ + return m_fillRule; +} + +void DebugPaintItem::setStrokeColor(const QColor &strokeColor) +{ + if (m_strokeColor == strokeColor) + return; + m_strokeColor = strokeColor; + emit strokeChanged(); +} + +QColor DebugPaintItem::strokeColor() const +{ + return m_strokeColor; +} + +void DebugPaintItem::setStrokeStyle(Qt::PenStyle strokeStyle) +{ + if (m_strokeStyle == strokeStyle) + return; + + m_strokeStyle = strokeStyle; + emit strokeChanged(); +} + +Qt::PenStyle DebugPaintItem::strokeStyle() const +{ + return m_strokeStyle; +} + +void DebugPaintItem::setJoinStyle(Qt::PenJoinStyle style) +{ + if (m_joinStyle == style) + return; + m_joinStyle = style; + emit strokeChanged(); +} + +Qt::PenJoinStyle DebugPaintItem::joinStyle() const +{ + return m_joinStyle; +} + +void DebugPaintItem::setCapStyle(Qt::PenCapStyle style) +{ + if (m_capStyle == style) + return; + m_capStyle = style; + emit strokeChanged(); +} + +Qt::PenCapStyle DebugPaintItem::capStyle() const +{ + return m_capStyle; +} + +void DebugPaintItem::setStrokeWidth(qreal w) +{ + if (qFuzzyCompare(m_strokeWidth, w)) + return; + m_strokeWidth = w; + emit strokeChanged(); +} + +qreal DebugPaintItem::strokeWidth() const +{ + return m_strokeWidth; +} diff --git a/tests/manual/painterpathquickshape/debugpaintitem.h b/tests/manual/painterpathquickshape/debugpaintitem.h new file mode 100644 index 0000000000..8ab9a89464 --- /dev/null +++ b/tests/manual/painterpathquickshape/debugpaintitem.h @@ -0,0 +1,79 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +#ifndef DEBUGPAINTITEM_H +#define DEBUGPAINTITEM_H + +#include +#include + +class DebugPaintItem : public QQuickPaintedItem +{ + Q_OBJECT + Q_PROPERTY(QQuickPath *shape READ shape WRITE setShape NOTIFY shapeChanged) + Q_PROPERTY(QColor color READ color WRITE setColor NOTIFY colorChanged) + Q_PROPERTY(qreal pathScale READ pathScale WRITE setPathScale NOTIFY pathScaleChanged) + Q_PROPERTY(Qt::FillRule fillRule READ fillRule WRITE setFillRule NOTIFY fillRuleChanged) + + Q_PROPERTY(QColor strokeColor READ strokeColor WRITE setStrokeColor NOTIFY strokeChanged) + Q_PROPERTY(Qt::PenStyle strokeStyle READ strokeStyle WRITE setStrokeStyle NOTIFY strokeChanged) + Q_PROPERTY(Qt::PenJoinStyle joinStyle READ joinStyle WRITE setJoinStyle NOTIFY strokeChanged) + Q_PROPERTY(Qt::PenCapStyle capStyle READ capStyle WRITE setCapStyle NOTIFY strokeChanged) + Q_PROPERTY(qreal strokeWidth READ strokeWidth WRITE setStrokeWidth NOTIFY strokeChanged) +public: + DebugPaintItem(QQuickItem *item = nullptr); + + void setShape(QQuickPath *path); + QQuickPath *shape() const; + + void setColor(const QColor &color); + QColor color() const; + + void setPathScale(qreal pathScale); + qreal pathScale() const; + + void setFillRule(Qt::FillRule filleRule); + Qt::FillRule fillRule() const; + + QColor strokeColor() const; + void setStrokeColor(const QColor &strokeColor); + + Qt::PenStyle strokeStyle() const; + void setStrokeStyle(Qt::PenStyle penStyle); + + Qt::PenJoinStyle joinStyle() const; + void setJoinStyle(Qt::PenJoinStyle style); + + Qt::PenCapStyle capStyle() const; + void setCapStyle(Qt::PenCapStyle style); + + qreal strokeWidth() const; + void setStrokeWidth(qreal w); + +signals: + void shapeChanged(); + void colorChanged(); + void opacityChanged(); + void pathScaleChanged(); + void fillRuleChanged(); + void strokeChanged(); + +public slots: + void handlePathChanged(); + +protected: + void paint(QPainter *p) override; + +private: + QQuickPath *m_path = nullptr; + QColor m_color = Qt::red; + qreal m_pathScale = 1.0; + Qt::FillRule m_fillRule = Qt::WindingFill; + Qt::PenStyle m_strokeStyle = Qt::NoPen; + QColor m_strokeColor = Qt::transparent; + qreal m_strokeWidth = 1.0; + Qt::PenCapStyle m_capStyle = Qt::FlatCap; + Qt::PenJoinStyle m_joinStyle = Qt::MiterJoin; +}; + +#endif // DEBUGPAINTITEM_H diff --git a/tests/manual/painterpathquickshape/debugvisualizationcontroller.cpp b/tests/manual/painterpathquickshape/debugvisualizationcontroller.cpp new file mode 100644 index 0000000000..c16f2ad20d --- /dev/null +++ b/tests/manual/painterpathquickshape/debugvisualizationcontroller.cpp @@ -0,0 +1,46 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +#include "debugvisualizationcontroller.h" +#include + +DebugVisualizationController::DebugVisualizationController(QObject *parent) + : QObject{parent} +{ +} + +bool DebugVisualizationController::showCurves() const +{ + return m_showCurves; +} + +void DebugVisualizationController::setShowCurves(bool newShowCurves) +{ + if (m_showCurves == newShowCurves) + return; + m_showCurves = newShowCurves; + update(); + emit showCurvesChanged(); +} + +bool DebugVisualizationController::showWireframe() const +{ + return m_showWireframe; +} + +void DebugVisualizationController::setWireframe(bool newShowWireframe) +{ + if (m_showWireframe == newShowWireframe) + return; + m_showWireframe = newShowWireframe; + update(); + emit showWireframeChanged(); +} + +void DebugVisualizationController::update() +{ + int flags = (m_showCurves ? QQuickShapeCurveRenderer::DebugCurves : 0) + | (m_showWireframe ? QQuickShapeCurveRenderer::DebugWireframe : 0); + QQuickShapeCurveRenderer::setDebugVisualization(flags); + emit settingsChanged(); +} diff --git a/tests/manual/painterpathquickshape/debugvisualizationcontroller.h b/tests/manual/painterpathquickshape/debugvisualizationcontroller.h new file mode 100644 index 0000000000..5bc02b305c --- /dev/null +++ b/tests/manual/painterpathquickshape/debugvisualizationcontroller.h @@ -0,0 +1,35 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +#ifndef DEBUGVISUALIZATIONCONTROLLER_H +#define DEBUGVISUALIZATIONCONTROLLER_H + +#include + +class DebugVisualizationController : public QObject +{ + Q_OBJECT + Q_PROPERTY(bool showCurves READ showCurves WRITE setShowCurves NOTIFY showCurvesChanged) + Q_PROPERTY(bool showWireframe READ showWireframe WRITE setWireframe NOTIFY showWireframeChanged) + +public: + explicit DebugVisualizationController(QObject *parent = nullptr); + + bool showCurves() const; + void setShowCurves(bool newShowCurves); + + bool showWireframe() const; + void setWireframe(bool newShowWireframe); + +signals: + void showCurvesChanged(); + void showWireframeChanged(); + void settingsChanged(); + +private: + void update(); + bool m_showCurves = false; + bool m_showWireframe = false; +}; + +#endif // DEBUGVISUALIZATIONCONTROLLER_H diff --git a/tests/manual/painterpathquickshape/ellipticalArcs.qml b/tests/manual/painterpathquickshape/ellipticalArcs.qml new file mode 100644 index 0000000000..637da1112f --- /dev/null +++ b/tests/manual/painterpathquickshape/ellipticalArcs.qml @@ -0,0 +1,77 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +import QtQuick +import QtQuick.Shapes + +Item { + ControlledShape { + id: shape + anchors.top: parent.top + anchors.left: parent.left + width: parent.width + height: parent.height / 2 + + startX: 10 + startY: 100 + delegate: [ + PathArc { + relativeX: 50 + y: 100 + radiusX: 25 + radiusY: 25 + }, + PathArc { + relativeX: 50 + y: 100 + radiusX: 25 + radiusY: 35 + }, + PathArc { + relativeX: 50 + y: 100 + radiusX: 25 + radiusY: 60 + }, + PathArc { + relativeX: 50 + y: 100 + radiusX: 50 + radiusY: 120 + } + ] + } + + ControlledShape { + width: parent.width + height: parent.height / 2 + anchors.bottom: parent.bottom + anchors.right: parent.right + + fillColor: "transparent" + strokeColor: "darkBlue" + strokeWidth: 20 + capStyle: ShapePath.RoundCap + + delegate: [ + PathAngleArc { + centerX: 65 + centerY: 95 + radiusX: 45 + radiusY: 45 + startAngle: -180 + SequentialAnimation on sweepAngle { + loops: Animation.Infinite + NumberAnimation { + to: 360 + duration: 2000 + } + NumberAnimation { + to: 0 + duration: 2000 + } + } + } + ] + } +} diff --git a/tests/manual/painterpathquickshape/fillRules.qml b/tests/manual/painterpathquickshape/fillRules.qml new file mode 100644 index 0000000000..0a10185acc --- /dev/null +++ b/tests/manual/painterpathquickshape/fillRules.qml @@ -0,0 +1,44 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +import QtQuick +import QtQuick.Shapes + +ControlledShape { + id: star + strokeColor: "blue" + fillColor: "magenta" + strokeWidth: 2 + delegate: [ + PathMove { + x: 90 + y: 50 + }, + PathLine { + x: 50 + 40 * Math.cos(0.8 * 1 * Math.PI) + y: 50 + 40 * Math.sin(0.8 * 1 * Math.PI) + }, + PathLine { + x: 50 + 40 * Math.cos(0.8 * 2 * Math.PI) + y: 50 + 40 * Math.sin(0.8 * 2 * Math.PI) + }, + PathLine { + x: 50 + 40 * Math.cos(0.8 * 3 * Math.PI) + y: 50 + 40 * Math.sin(0.8 * 3 * Math.PI) + }, + PathLine { + x: 50 + 40 * Math.cos(0.8 * 4 * Math.PI) + y: 50 + 40 * Math.sin(0.8 * 4 * Math.PI) + }, + PathLine { + x: 90 + y: 50 + } + ] + Timer { + interval: 2000 + onTriggered: star.fillRule = (star.fillRule === ShapePath.OddEvenFill ? ShapePath.WindingFill : ShapePath.OddEvenFill) + repeat: true + running: true + } +} diff --git a/tests/manual/painterpathquickshape/gradientSpreadModes.qml b/tests/manual/painterpathquickshape/gradientSpreadModes.qml new file mode 100644 index 0000000000..7cee5e46c1 --- /dev/null +++ b/tests/manual/painterpathquickshape/gradientSpreadModes.qml @@ -0,0 +1,72 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +import QtQuick +import QtQuick.Shapes + +Item { + ControlledShape { + anchors.fill: parent + strokeColor: "transparent" + startX: 10 + startY: 10 + + delegate: [ + PathLine { + relativeX: 180 + relativeY: 0 + }, + PathLine { + relativeX: 0 + relativeY: 180 + }, + PathLine { + relativeX: -180 + relativeY: 0 + }, + PathLine { + relativeX: 0 + relativeY: -180 + } + ] + } + + Timer { + id: spreadTimer + interval: 3000 + running: true + repeat: true + readonly property variant spreads: [ ShapeGradient.PadSpread, ShapeGradient.RepeatSpread, ShapeGradient.ReflectSpread ] + readonly property variant spreadTexts: [ qsTr("PadSpread"), qsTr("RepeatSpread"), qsTr("ReflectSpread") ] + property int spreadIdx: 0 + onTriggered: function() { + spreadIdx = (spreadIdx + 1) % spreads.length + grad.spread = spreads[spreadIdx] + } + } + + ControlledShape { + anchors.fill: parent + strokeColor: "gray" + strokeWidth: 2 + fillColor: "transparent" + delegate: [ + PathMove { + x: 0 + y: 50 + }, + PathLine { + relativeX: 200 + relativeY: 0 + }, + PathMove { + x: 0 + y: 150 + }, + PathLine { + relativeX: 200 + relativeY: 0 + } + ] + } +} diff --git a/tests/manual/painterpathquickshape/hand-print.svg b/tests/manual/painterpathquickshape/hand-print.svg new file mode 100644 index 0000000000..eb8d56060d --- /dev/null +++ b/tests/manual/painterpathquickshape/hand-print.svg @@ -0,0 +1,104 @@ + + + + + + + + + + + image/svg+xml + + + + + Openclipart + + + + + + + + + + + diff --git a/tests/manual/painterpathquickshape/joinStyles.qml b/tests/manual/painterpathquickshape/joinStyles.qml new file mode 100644 index 0000000000..628d2e1397 --- /dev/null +++ b/tests/manual/painterpathquickshape/joinStyles.qml @@ -0,0 +1,40 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +import QtQuick +import QtQuick.Shapes + +ControlledShape { + strokeColor: "black" + strokeWidth: 16 + fillColor: "transparent" + capStyle: ShapePath.RoundCap + + //readonly property variant styles: [ ShapePath.BevelJoin, ShapePath.MiterJoin, ShapePath.RoundJoin ] + //joinStyle: styles[joinStyleIdx] + + property int joinStyleIdx: 0 + readonly property variant styles: [ ShapePath.BevelJoin, ShapePath.MiterJoin, ShapePath.RoundJoin ] + readonly property variant styleTexts: [ qsTr("BevelJoin"), qsTr("MiterJoin"), qsTr("RoundJoin") ] + + startX: 30 + startY: 30 + + delegate: [ + PathLine { + x: 100 + y: 100 + }, + PathLine { + x: 30 + y: 100 + } + ] + +// Timer { +// interval: 1000 +// repeat: true +// running: true +// onTriggered: joinTest.joinStyleIdx = (joinTest.joinStyleIdx + 1) % joinTest.styles.length +// } +} diff --git a/tests/manual/painterpathquickshape/largeOrSmallArc.qml b/tests/manual/painterpathquickshape/largeOrSmallArc.qml new file mode 100644 index 0000000000..7ab1e52920 --- /dev/null +++ b/tests/manual/painterpathquickshape/largeOrSmallArc.qml @@ -0,0 +1,34 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +import QtQuick +import QtQuick.Shapes + +Item { + Repeater { + anchors.fill: parent + model: 2 + delegate: ControlledShape { + id: delegate + required property int index + + anchors.fill: parent + fillColor: "transparent" + strokeColor: delegate.index === 0 ? "red" : "blue" + strokeStyle: ShapePath.DashLine + strokeWidth: 4 + startX: 50 + startY: 100 + + delegate: [ + PathArc { + x: 100 + y: 150 + radiusX: 50 + radiusY: 50 + useLargeArc: delegate.index === 1 + } + ] + } + } +} diff --git a/tests/manual/painterpathquickshape/linearGradient.qml b/tests/manual/painterpathquickshape/linearGradient.qml new file mode 100644 index 0000000000..08d72caee6 --- /dev/null +++ b/tests/manual/painterpathquickshape/linearGradient.qml @@ -0,0 +1,84 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +import QtQuick +import QtQuick.Shapes + +ControlledShape { + strokeWidth: 4 + strokeColor: "red" +// fillGradient: LinearGradient { +// x1: 20 +// y1: 20 +// x2: 180 +// y2: 130 +// GradientStop { +// position: 0 +// color: "blue" +// } +// GradientStop { +// position: 0.2 +// color: "green" +// } +// GradientStop { +// position: 0.4 +// color: "red" +// } +// GradientStop { +// position: 0.6 +// color: "yellow" +// } +// GradientStop { +// position: 1 +// color: "cyan" +// } +// } + fillColor: "blue" // ignored with the gradient set + strokeStyle: ShapePath.DashLine +// dashPattern: [ 1, 4 ] + startX: 20 + startY: 20 + + delegate: [ + PathLine { + x: 180 + y: 130 + }, + PathLine { + x: 20 + y: 130 + }, + PathLine { + x: 20 + y: 20 + } + ] + +// transform: Rotation { +// origin.x: 100 +// origin.y: 50 +// axis { +// x: 0 +// y: 1 +// z: 0 +// } +// SequentialAnimation on angle { +// NumberAnimation { +// from: 0 +// to: 75 +// duration: 2000 +// } +// NumberAnimation { +// from: 75 +// to: -75 +// duration: 4000 +// } +// NumberAnimation { +// from: -75 +// to: 0 +// duration: 2000 +// } +// loops: Animation.Infinite +// } +// } +} diff --git a/tests/manual/painterpathquickshape/main.cpp b/tests/manual/painterpathquickshape/main.cpp new file mode 100644 index 0000000000..5b780b9439 --- /dev/null +++ b/tests/manual/painterpathquickshape/main.cpp @@ -0,0 +1,34 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +#include +#include +#include + +#include "svgpathloader.h" +#include "debugpaintitem.h" +#include "debugvisualizationcontroller.h" + +int main(int argc, char *argv[]) +{ +#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) + QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling); +#endif + QGuiApplication app(argc, argv); + + qmlRegisterType("io.qt", 1, 0, "DebugPaintItem"); + qmlRegisterType("io.qt", 1, 0, "SvgPathLoader"); + qmlRegisterType("io.qt", 1, 0, "DebugVisualizationController"); + + QFontDatabase::addApplicationFont(":/Graziano.ttf"); + QQmlApplicationEngine engine; + const QUrl url(QStringLiteral("qrc:/main.qml")); + QObject::connect(&engine, &QQmlApplicationEngine::objectCreated, + &app, [url](QObject *obj, const QUrl &objUrl) { + if (!obj && url == objUrl) + QCoreApplication::exit(-1); + }, Qt::QueuedConnection); + engine.load(url); + + return app.exec(); +} diff --git a/tests/manual/painterpathquickshape/main.qml b/tests/manual/painterpathquickshape/main.qml new file mode 100644 index 0000000000..45651f870f --- /dev/null +++ b/tests/manual/painterpathquickshape/main.qml @@ -0,0 +1,189 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +import QtQuick +import QtQuick.Window +import QtQuick.Shapes +import QtQuick.Controls +import QtQuick.Layouts + +Window { + width: 1024 + height: 768 + visible: true + title: qsTr("Hello World") + color: "#666" + + ListModel { + id: sampleList + + ListElement { + text: "Small polygon" + source: "SmallPolygon.qml" + } + ListElement { + text: "Text" + source: "TextShape.qml" + } + ListElement { + text: "SVG" + source: "SvgShape.qml" + } + ListElement { + text: "QuadShape" + source: "SimpleShape.qml" + } + ListElement { + text: "Squircle" + source: "Squircle.qml" + } + ListElement { + text: "CubicShape" + source: "CubicShape.qml" + } + ListElement { + text: "Arc Direction" + source: "arcDirection.qml" + } + + ListElement { + text: "Arc Rotation" + source: "arcRotation.qml" + } + + ListElement { + text: "Cap Styles" + source: "capStyles.qml" + } + + ListElement { + text: "Cubic Curve" + source: "cubicCurve.qml" + } + + ListElement { + text: "Dash Pattern" + source: "dashPattern.qml" + } + + ListElement { + text: "Elliptical Arcs" + source: "ellipticalArcs.qml" + } + + ListElement { + text: "Fill rules" + source: "fillRules.qml" + } + + ListElement { + text: "Gradient spread modes" + source: "gradientSpreadModes.qml" + } + + ListElement { + text: "Join styles" + source: "joinStyles.qml" + } + + ListElement { + text: "Large or small arc" + source: "largeOrSmallArc.qml" + } + + ListElement { + text: "Linear gradient" + source: "linearGradient.qml" + } + + ListElement { + text: "Quadratic curve" + source: "quadraticCurve.qml" + } + + ListElement { + text: "Radial gradient" + source: "radialGradient.qml" + } + + ListElement { + text: "Stroke or fill" + source: "strokeOrFill.qml" + } + + ListElement { + text: "Qt! text" + source: "text.qml" + } + + ListElement { + text: "Tiger" + source: "tiger.qml" + } + } + + ComboBox { + id: comboBox + model: sampleList + textRole: "text" + valueRole: "source" + onCurrentValueChanged: { + loader.source = currentValue + } + } + Image { + id: background + anchors.fill: flickable + fillMode: Image.Tile + source: "qrc:/background.png" + smooth: true + } + + Flickable { + id: flickable + clip: true + contentWidth: loader.item ? loader.item.boundingRect.right * controlPanel.scale + controlPanel.pathMargin * 2 : 1 + contentHeight: loader.item ? loader.item.boundingRect.bottom * controlPanel.scale + controlPanel.pathMargin * 2 : 1 + anchors.top: comboBox.bottom + anchors.left: parent.left + anchors.right: parent.right + anchors.bottom: controlPanel.top + + WheelHandler { + onWheel: (event)=> { + let scale = controlPanel.scale + let posX = event.x + let posY = event.y + let xOff = posX - flickable.contentX + let yOff = posY - flickable.contentY + + let pathX = posX / scale + let pathY = posY / scale + + if (event.angleDelta.y > 0) + scale = scale * 1.1 + else + scale = scale / 1.1 + controlPanel.setScale(scale) + + flickable.contentX = pathX * controlPanel.scale - xOff + flickable.contentY = pathY * controlPanel.scale - yOff + flickable.returnToBounds() + } + } + + Loader { + x: controlPanel.pathMargin + y: controlPanel.pathMargin + id: loader + } + } + + ControlPanel { + id: controlPanel + anchors.bottom: parent.bottom + anchors.left: parent.left + anchors.right: parent.right + height: parent.height / 4 + } +} diff --git a/tests/manual/painterpathquickshape/peace_victory.svg b/tests/manual/painterpathquickshape/peace_victory.svg new file mode 100644 index 0000000000..25480cbaee --- /dev/null +++ b/tests/manual/painterpathquickshape/peace_victory.svg @@ -0,0 +1,96 @@ + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + diff --git a/tests/manual/painterpathquickshape/quadraticCurve.qml b/tests/manual/painterpathquickshape/quadraticCurve.qml new file mode 100644 index 0000000000..63303bba91 --- /dev/null +++ b/tests/manual/painterpathquickshape/quadraticCurve.qml @@ -0,0 +1,47 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +import QtQuick +import QtQuick.Shapes + +Item { + ControlledShape { + id: shape + anchors.fill: parent + strokeWidth: 4 + strokeColor: "black" + fillColor: "transparent" + + startX: 50 + startY: 50 + + delegate: [ + PathQuad { + x: 150 + y: 50 + controlX: cp.x + controlY: cp.y + } + ] + } + + Rectangle { + id: cp + color: "red" + width: 10 + height: 10 + SequentialAnimation on x { + loops: Animation.Infinite + NumberAnimation { + from: 0 + to: shape.width - cp.width + duration: 5000 + } + NumberAnimation { + from: shape.width - cp.width + to: 0 + duration: 5000 + } + } + } +} diff --git a/tests/manual/painterpathquickshape/radialGradient.qml b/tests/manual/painterpathquickshape/radialGradient.qml new file mode 100644 index 0000000000..b5dcd4fde5 --- /dev/null +++ b/tests/manual/painterpathquickshape/radialGradient.qml @@ -0,0 +1,106 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +import QtQuick +import QtQuick.Shapes + +ControlledShape { +// fillGradient: RadialGradient { +// centerX: 100 +// centerY: 100 +// centerRadius: 100 +// SequentialAnimation on focalRadius { +// loops: Animation.Infinite +// NumberAnimation { +// from: 1 +// to: 20 +// duration: 2000 +// } +// NumberAnimation { +// from: 20 +// to: 1 +// duration: 2000 +// } +// } +// SequentialAnimation on focalX { +// loops: Animation.Infinite +// NumberAnimation { +// from: 50 +// to: 150 +// duration: 3000 +// } +// NumberAnimation { +// from: 150 +// to: 50 +// duration: 3000 +// } +// } +// SequentialAnimation on focalY { +// loops: Animation.Infinite +// NumberAnimation { +// from: 50 +// to: 150 +// duration: 1000 +// } +// NumberAnimation { +// from: 150 +// to: 50 +// duration: 1000 +// } +// } +// GradientStop { +// position: 0 +// color: "#ffffff" +// } +// GradientStop { +// position: 0.11 +// color: "#f9ffa0" +// } +// GradientStop { +// position: 0.13 +// color: "#f9ff99" +// } +// GradientStop { +// position: 0.14 +// color: "#f3ff86" +// } +// GradientStop { +// position: 0.49 +// color: "#93b353" +// } +// GradientStop { +// position: 0.87 +// color: "#264619" +// } +// GradientStop { +// position: 0.96 +// color: "#0c1306" +// } +// GradientStop { +// position: 1 +// color: "#000000" +// } +// } + + strokeWidth: 4 + strokeColor: "red" + fillColor: "blue" // ignored with the gradient set + strokeStyle: ShapePath.DashLine + //dashPattern: [ 1, 4 ] + startX: 20 + startY: 20 + delegate: [ + PathLine { + x: 180 + y: 130 + }, + PathLine { + x: 20 + y: 130 + }, + PathLine { + x: 20 + y: 20 + } + ] +} diff --git a/tests/manual/painterpathquickshape/strokeOrFill.qml b/tests/manual/painterpathquickshape/strokeOrFill.qml new file mode 100644 index 0000000000..c6a9373729 --- /dev/null +++ b/tests/manual/painterpathquickshape/strokeOrFill.qml @@ -0,0 +1,115 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +import QtQuick +import QtQuick.Shapes + +Item { + ControlledShape { + id: circ1 + anchors.fill: parent + fillColor: "transparent" // stroke only + strokeWidth: 4 + + SequentialAnimation on strokeColor { + loops: Animation.Infinite + ColorAnimation { + from: "black" + to: "yellow" + duration: 5000 + } + ColorAnimation { + from: "yellow" + to: "green" + duration: 5000 + } + ColorAnimation { + from: "green" + to: "black" + duration: 5000 + } + } + + readonly property real r: 60 + startX: circ1.width / 2 - r + startY: circ1.height / 2 - r + + delegate: [ + PathArc { + x: circ1.width / 2 + circ1.r + y: circ1.height / 2 + circ1.r + radiusX: circ1.r + radiusY: circ1.r + useLargeArc: true + }, + PathArc { + x: circ1.width / 2 - circ1.r + y: circ1.height / 2 - circ1.r + radiusX: circ1.r + radiusY: circ1.r + useLargeArc: true + } + ] + } + + ControlledShape { + id: circ2 + anchors.fill: parent + + SequentialAnimation on opacity { + loops: Animation.Infinite + NumberAnimation { + from: 1.0 + to: 0.0 + duration: 5000 + } + NumberAnimation { + from: 0.0 + to: 1.0 + duration: 5000 + } + } + + strokeWidth: -1 // or strokeColor: "transparent" + + SequentialAnimation on fillColor { + loops: Animation.Infinite + ColorAnimation { + from: "gray" + to: "purple" + duration: 3000 + } + ColorAnimation { + from: "purple" + to: "red" + duration: 3000 + } + ColorAnimation { + from: "red" + to: "gray" + duration: 3000 + } + } + + readonly property real r: 40 + startX: circ2.width / 2 - r + startY: circ2.height / 2 - r + + delegate: [ + PathArc { + x: circ2.width / 2 + circ2.r + y: circ2.height / 2 + circ2.r + radiusX: circ2.r + radiusY: circ2.r + useLargeArc: true + }, + PathArc { + x: circ2.width / 2 - circ2.r + y: circ2.height / 2 - circ2.r + radiusX: circ2.r + radiusY: circ2.r + useLargeArc: true + } + ] + } +} diff --git a/tests/manual/painterpathquickshape/svgpathloader.cpp b/tests/manual/painterpathquickshape/svgpathloader.cpp new file mode 100644 index 0000000000..bc4940e478 --- /dev/null +++ b/tests/manual/painterpathquickshape/svgpathloader.cpp @@ -0,0 +1,62 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +#include "svgpathloader.h" + +#include +#include +#include +#include + +SvgPathLoader::SvgPathLoader() +{ + connect(this, &SvgPathLoader::sourceChanged, this, &SvgPathLoader::loadPaths); + + loadPaths(); +} + +void SvgPathLoader::loadPaths() +{ + m_paths.clear(); + m_fillColors.clear(); + if (m_source.isEmpty()) + return; + + QString fileName; + if (m_source.isLocalFile()) + fileName = m_source.toLocalFile(); + else if (m_source.scheme() == QStringLiteral("qrc")) + fileName = QStringLiteral(":/") + m_source.fileName(); + + QFile f(fileName); + if (f.open(QIODevice::ReadOnly)) { + QXmlStreamReader reader(&f); + QString fillColor = QStringLiteral("#ffffff"); + while (!reader.atEnd()) { + reader.readNext(); + QXmlStreamAttributes attrs = reader.attributes(); + if (reader.isStartElement() && attrs.hasAttribute(QStringLiteral("fill"))) + fillColor = attrs.value(QStringLiteral("fill")).toString(); + if (reader.isStartElement() && reader.name() == QStringLiteral("path")) { + m_fillColors.append(fillColor); + if (attrs.hasAttribute(QStringLiteral("d"))) + m_paths.append(attrs.value(QStringLiteral("d")).toString()); + if (attrs.hasAttribute(QStringLiteral("fill"))) { + m_fillColors[m_fillColors.size() - 1] = attrs.value(QStringLiteral("fill")).toString(); + } else if (attrs.hasAttribute(QStringLiteral("style"))) { + QString s = attrs.value(QStringLiteral("style")).toString(); + int idx = s.indexOf(QStringLiteral("fill:")); + if (idx >= 0) { + idx = s.indexOf(QLatin1Char('#'), idx); + if (idx >= 0) + m_fillColors[m_fillColors.size() - 1] = s.mid(idx, 7); + } + } + } + } + } else { + qWarning() << "Can't open file" << fileName; + } + + emit pathsChanged(); +} diff --git a/tests/manual/painterpathquickshape/svgpathloader.h b/tests/manual/painterpathquickshape/svgpathloader.h new file mode 100644 index 0000000000..dc5efcd6ce --- /dev/null +++ b/tests/manual/painterpathquickshape/svgpathloader.h @@ -0,0 +1,57 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +#ifndef SVGPATHLOADER_H +#define SVGPATHLOADER_H + +#include +#include +#include +#include + +class SvgPathLoader : public QObject +{ + Q_OBJECT + Q_PROPERTY(QUrl source READ source WRITE setSource NOTIFY sourceChanged) + Q_PROPERTY(QStringList paths READ paths NOTIFY pathsChanged) + Q_PROPERTY(QStringList fillColors READ fillColors NOTIFY pathsChanged) +public: + SvgPathLoader(); + + QUrl source() const + { + return m_source; + } + void setSource(const QUrl &url) + { + if (url == m_source) + return; + m_source = url; + qDebug() << "Set source" << url; + emit sourceChanged(); + } + + QStringList paths() const + { + return m_paths; + } + + QStringList fillColors() const + { + return m_fillColors; + } + +private slots: + void loadPaths(); + +signals: + void sourceChanged(); + void pathsChanged(); + +private: + QUrl m_source; + QStringList m_paths; + QStringList m_fillColors; +}; + +#endif // SVGPATHLOADER_H diff --git a/tests/manual/painterpathquickshape/text.qml b/tests/manual/painterpathquickshape/text.qml new file mode 100644 index 0000000000..9076f7f193 --- /dev/null +++ b/tests/manual/painterpathquickshape/text.qml @@ -0,0 +1,21 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +import QtQuick +import QtQuick.Shapes + +ControlledShape { + strokeColor: "black" + strokeWidth: 1 + fillColor: "black" + + delegate: [ + PathText { + x: 0 + y: 0 + text: qsTr("Qt!") + font.family: "Arial" + font.pixelSize: 150 + } + ] +} diff --git a/tests/manual/painterpathquickshape/tiger.qml b/tests/manual/painterpathquickshape/tiger.qml new file mode 100644 index 0000000000..95aeaf9676 --- /dev/null +++ b/tests/manual/painterpathquickshape/tiger.qml @@ -0,0 +1,3810 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +import QtQuick +import QtQuick.Shapes + +Item { + property var boundingRect: Qt.rect(-1000, -1000, 4000, 4000) + + ControlledShape { + fillColor: "#ffffff" + strokeColor: "#000000" + strokeWidth: 0.172 + delegate: [PathMove { x: -122.304; y: 84.285 } + , PathCubic { control1X: -122.304; control1Y: 84.285; control2X: -122.203; control2Y: 86.179; x: -123.027; y: 86.16 } + , PathCubic { control1X: -123.851; control1Y: 86.141; control2X: -140.305; control2Y: 38.066; x: -160.833; y: 40.309 } + , PathCubic { control1X: -160.833; control1Y: 40.309; control2X: -143.05; control2Y: 32.956; x: -122.304; y: 84.285 } + ] + } + + ControlledShape { + fillColor: "#ffffff" + strokeColor: "#000000" + strokeWidth: 0.172 + delegate: [PathMove { x: -118.774; y: 81.262 } + , PathCubic { control1X: -118.774; control1Y: 81.262; control2X: -119.323; control2Y: 83.078; x: -120.092; y: 82.779 } + , PathCubic { control1X: -120.86; control1Y: 82.481; control2X: -119.977; control2Y: 31.675; x: -140.043; y: 26.801 } + , PathCubic { control1X: -140.043; control1Y: 26.801; control2X: -120.82; control2Y: 25.937; x: -118.774; y: 81.262 } + ] + } + + ControlledShape { + fillColor: "#ffffff" + strokeColor: "#000000" + strokeWidth: 0.172 + delegate: [PathMove { x: -91.284; y: 123.59 } + , PathCubic { control1X: -91.284; control1Y: 123.59; control2X: -89.648; control2Y: 124.55; x: -90.118; y: 125.227 } + , PathCubic { control1X: -90.589; control1Y: 125.904; control2X: -139.763; control2Y: 113.102; x: -149.218; y: 131.459 } + , PathCubic { control1X: -149.218; control1Y: 131.459; control2X: -145.539; control2Y: 112.572; x: -91.284; y: 123.59 } + ] + } + + ControlledShape { + fillColor: "#ffffff" + strokeColor: "#000000" + strokeWidth: 0.172 + delegate: [PathMove { x: -94.093; y: 133.801 } + , PathCubic { control1X: -94.093; control1Y: 133.801; control2X: -92.237; control2Y: 134.197; x: -92.471; y: 134.988 } + , PathCubic { control1X: -92.704; control1Y: 135.779; control2X: -143.407; control2Y: 139.121; x: -146.597; y: 159.522 } + , PathCubic { control1X: -146.597; control1Y: 159.522; control2X: -149.055; control2Y: 140.437; x: -94.093; y: 133.801 } + ] + } + + ControlledShape { + fillColor: "#ffffff" + strokeColor: "#000000" + strokeWidth: 0.172 + delegate: [PathMove { x: -98.304; y: 128.276 } + , PathCubic { control1X: -98.304; control1Y: 128.276; control2X: -96.526; control2Y: 128.939; x: -96.872; y: 129.687 } + , PathCubic { control1X: -97.218; control1Y: 130.435; control2X: -147.866; control2Y: 126.346; x: -153.998; y: 146.064 } + , PathCubic { control1X: -153.998; control1Y: 146.064; control2X: -153.646; control2Y: 126.825; x: -98.304; y: 128.276 } + ] + } + + ControlledShape { + fillColor: "#ffffff" + strokeColor: "#000000" + strokeWidth: 0.172 + delegate: [PathMove { x: -109.009; y: 110.072 } + , PathCubic { control1X: -109.009; control1Y: 110.072; control2X: -107.701; control2Y: 111.446; x: -108.34; y: 111.967 } + , PathCubic { control1X: -108.979; control1Y: 112.488; control2X: -152.722; control2Y: 86.634; x: -166.869; y: 101.676 } + , PathCubic { control1X: -166.869; control1Y: 101.676; control2X: -158.128; control2Y: 84.533; x: -109.009; y: 110.072 } + ] + } + + ControlledShape { + fillColor: "#ffffff" + strokeColor: "#000000" + strokeWidth: 0.172 + delegate: [PathMove { x: -116.554; y: 114.263 } + , PathCubic { control1X: -116.554; control1Y: 114.263; control2X: -115.098; control2Y: 115.48; x: -115.674; y: 116.071 } + , PathCubic { control1X: -116.25; control1Y: 116.661; control2X: -162.638; control2Y: 95.922; x: -174.992; y: 112.469 } + , PathCubic { control1X: -174.992; control1Y: 112.469; control2X: -168.247; control2Y: 94.447; x: -116.554; y: 114.263 } + ] + } + + ControlledShape { + fillColor: "#ffffff" + strokeColor: "#000000" + strokeWidth: 0.172 + delegate: [PathMove { x: -119.154; y: 118.335 } + , PathCubic { control1X: -119.154; control1Y: 118.335; control2X: -117.546; control2Y: 119.343; x: -118.036; y: 120.006 } + , PathCubic { control1X: -118.526; control1Y: 120.669; control2X: -167.308; control2Y: 106.446; x: -177.291; y: 124.522 } + , PathCubic { control1X: -177.291; control1Y: 124.522; control2X: -173.066; control2Y: 105.749; x: -119.154; y: 118.335 } + ] + } + + ControlledShape { + fillColor: "#ffffff" + strokeColor: "#000000" + strokeWidth: 0.172 + delegate: [PathMove { x: -108.42; y: 118.949 } + , PathCubic { control1X: -108.42; control1Y: 118.949; control2X: -107.298; control2Y: 120.48; x: -107.999; y: 120.915 } + , PathCubic { control1X: -108.7; control1Y: 121.35; control2X: -148.769; control2Y: 90.102; x: -164.727; y: 103.207 } + , PathCubic { control1X: -164.727; control1Y: 103.207; control2X: -153.862; control2Y: 87.326; x: -108.42; y: 118.949 } + ] + } + + ControlledShape { + fillColor: "#ffffff" + strokeColor: "#000000" + strokeWidth: 0.172 + delegate: [PathMove { x: -128.2; y: 90 } + , PathCubic { control1X: -128.2; control1Y: 90; control2X: -127.6; control2Y: 91.8; x: -128.4; y: 92 } + , PathCubic { control1X: -129.2; control1Y: 92.2; control2X: -157.8; control2Y: 50.2; x: -177.001; y: 57.8 } + , PathCubic { control1X: -177.001; control1Y: 57.8; control2X: -161.8; control2Y: 46; x: -128.2; y: 90 } + ] + } + + ControlledShape { + fillColor: "#ffffff" + strokeColor: "#000000" + strokeWidth: 0.172 + delegate: [PathMove { x: -127.505; y: 96.979 } + , PathCubic { control1X: -127.505; control1Y: 96.979; control2X: -126.53; control2Y: 98.608; x: -127.269; y: 98.975 } + , PathCubic { control1X: -128.007; control1Y: 99.343; control2X: -164.992; control2Y: 64.499; x: -182.101; y: 76.061 } + , PathCubic { control1X: -182.101; control1Y: 76.061; control2X: -169.804; control2Y: 61.261; x: -127.505; y: 96.979 } + ] + } + + ControlledShape { + fillColor: "#ffffff" + strokeColor: "#000000" + strokeWidth: 0.172 + delegate: [PathMove { x: -127.62; y: 101.349 } + , PathCubic { control1X: -127.62; control1Y: 101.349; control2X: -126.498; control2Y: 102.88; x: -127.199; y: 103.315 } + , PathCubic { control1X: -127.9; control1Y: 103.749; control2X: -167.969; control2Y: 72.502; x: -183.927; y: 85.607 } + , PathCubic { control1X: -183.927; control1Y: 85.607; control2X: -173.062; control2Y: 69.726; x: -127.62; y: 101.349 } + ] + } + + ControlledShape { + fillColor: "#ffffff" + strokeColor: "#000000" + strokeWidth: 1 + delegate: [PathMove { x: -129.83; y: 103.065 } + , PathCubic { control1X: -129.327; control1Y: 109.113; control2X: -128.339; control2Y: 115.682; x: -126.6; y: 118.801 } + , PathCubic { control1X: -126.6; control1Y: 118.801; control2X: -130.2; control2Y: 131.201; x: -121.4; y: 144.401 } + , PathCubic { control1X: -121.4; control1Y: 144.401; control2X: -121.8; control2Y: 151.601; x: -120.2; y: 154.801 } + , PathCubic { control1X: -120.2; control1Y: 154.801; control2X: -116.2; control2Y: 163.201; x: -111.4; y: 164.001 } + , PathCubic { control1X: -107.516; control1Y: 164.648; control2X: -98.793; control2Y: 167.717; x: -88.932; y: 169.121 } + , PathCubic { control1X: -88.932; control1Y: 169.121; control2X: -71.8; control2Y: 183.201; x: -75; y: 196.001 } + , PathCubic { control1X: -75; control1Y: 196.001; control2X: -75.4; control2Y: 212.401; x: -79; y: 214.001 } + , PathCubic { control1X: -79; control1Y: 214.001; control2X: -67.4; control2Y: 202.801; x: -77; y: 219.601 } + , PathLine { x: -81.4; y: 238.401 } + , PathCubic { control1X: -81.4; control1Y: 238.401; control2X: -55.8; control2Y: 216.801; x: -71.4; y: 235.201 } + , PathLine { x: -81.4; y: 261.201 } + , PathCubic { control1X: -81.4; control1Y: 261.201; control2X: -61.8; control2Y: 242.801; x: -69; y: 251.201 } + , PathLine { x: -72.2; y: 260.001 } + , PathCubic { control1X: -72.2; control1Y: 260.001; control2X: -29; control2Y: 232.801; x: -59.8; y: 262.401 } + , PathCubic { control1X: -59.8; control1Y: 262.401; control2X: -51.8; control2Y: 258.801; x: -47.4; y: 261.601 } + , PathCubic { control1X: -47.4; control1Y: 261.601; control2X: -40.6; control2Y: 260.401; x: -41.4; y: 262.001 } + , PathCubic { control1X: -41.4; control1Y: 262.001; control2X: -62.2; control2Y: 272.401; x: -65.8; y: 290.801 } + , PathCubic { control1X: -65.8; control1Y: 290.801; control2X: -57.4; control2Y: 280.801; x: -60.6; y: 291.601 } + , PathLine { x: -60.2; y: 303.201 } + , PathCubic { control1X: -60.2; control1Y: 303.201; control2X: -56.2; control2Y: 281.601; x: -56.6; y: 319.201 } + , PathCubic { control1X: -56.6; control1Y: 319.201; control2X: -37.4; control2Y: 301.201; x: -49; y: 322.001 } + , PathLine { x: -49; y: 338.801 } + , PathCubic { control1X: -49; control1Y: 338.801; control2X: -33.8; control2Y: 322.401; x: -40.2; y: 335.201 } + , PathCubic { control1X: -40.2; control1Y: 335.201; control2X: -30.2; control2Y: 326.401; x: -34.2; y: 341.601 } + , PathCubic { control1X: -34.2; control1Y: 341.601; control2X: -35; control2Y: 352.001; x: -30.6; y: 340.801 } + , PathCubic { control1X: -30.6; control1Y: 340.801; control2X: -14.6; control2Y: 310.201; x: -20.6; y: 336.401 } + , PathCubic { control1X: -20.6; control1Y: 336.401; control2X: -21.4; control2Y: 355.601; x: -16.6; y: 340.801 } + , PathCubic { control1X: -16.6; control1Y: 340.801; control2X: -16.2; control2Y: 351.201; x: -7; y: 358.401 } + , PathCubic { control1X: -7; control1Y: 358.401; control2X: -8.2; control2Y: 307.601; x: 4.6; y: 343.601 } + , PathLine { x: 8.6; y: 360.001 } + , PathCubic { control1X: 8.6; control1Y: 360.001; control2X: 11.4; control2Y: 350.801; x: 11; y: 345.601 } + , PathCubic { control1X: 11; control1Y: 345.601; control2X: 25.8; control2Y: 329.201; x: 19; y: 353.601 } + , PathCubic { control1X: 19; control1Y: 353.601; control2X: 34.2; control2Y: 330.801; x: 31; y: 344.001 } + , PathCubic { control1X: 31; control1Y: 344.001; control2X: 23.4; control2Y: 360.001; x: 25; y: 364.801 } + , PathCubic { control1X: 25; control1Y: 364.801; control2X: 41.8; control2Y: 330.001; x: 43; y: 328.401 } + , PathCubic { control1X: 43; control1Y: 328.401; control2X: 41; control2Y: 370.802; x: 51.8; y: 334.801 } + , PathCubic { control1X: 51.8; control1Y: 334.801; control2X: 57.4; control2Y: 346.801; x: 54.6; y: 351.201 } + , PathCubic { control1X: 54.6; control1Y: 351.201; control2X: 62.6; control2Y: 343.201; x: 61.8; y: 340.001 } + , PathCubic { control1X: 61.8; control1Y: 340.001; control2X: 66.4; control2Y: 331.801; x: 69.2; y: 345.401 } + , PathCubic { control1X: 69.2; control1Y: 345.401; control2X: 71; control2Y: 354.801; x: 72.6; y: 351.601 } + , PathCubic { control1X: 72.6; control1Y: 351.601; control2X: 76.6; control2Y: 375.602; x: 77.8; y: 352.801 } + , PathCubic { control1X: 77.8; control1Y: 352.801; control2X: 79.4; control2Y: 339.201; x: 72.2; y: 327.601 } + , PathCubic { control1X: 72.2; control1Y: 327.601; control2X: 73; control2Y: 324.401; x: 70.2; y: 320.401 } + , PathCubic { control1X: 70.2; control1Y: 320.401; control2X: 83.8; control2Y: 342.001; x: 76.6; y: 313.201 } + , PathCubic { control1X: 76.6; control1Y: 313.201; control2X: 87.801; control2Y: 321.201; x: 89.001; y: 321.201 } + , PathCubic { control1X: 89.001; control1Y: 321.201; control2X: 75.4; control2Y: 298.001; x: 84.2; y: 302.801 } + , PathCubic { control1X: 84.2; control1Y: 302.801; control2X: 79; control2Y: 292.401; x: 97.001; y: 304.401 } + , PathCubic { control1X: 97.001; control1Y: 304.401; control2X: 81; control2Y: 288.401; x: 98.601; y: 298.001 } + , PathCubic { control1X: 98.601; control1Y: 298.001; control2X: 106.601; control2Y: 304.401; x: 99.001; y: 294.401 } + , PathCubic { control1X: 99.001; control1Y: 294.401; control2X: 84.6; control2Y: 278.401; x: 106.601; y: 296.401 } + , PathCubic { control1X: 106.601; control1Y: 296.401; control2X: 118.201; control2Y: 312.801; x: 119.001; y: 315.601 } + , PathCubic { control1X: 119.001; control1Y: 315.601; control2X: 109.001; control2Y: 286.401; x: 104.601; y: 283.601 } + , PathCubic { control1X: 104.601; control1Y: 283.601; control2X: 113.001; control2Y: 247.201; x: 154.201; y: 262.801 } + , PathCubic { control1X: 154.201; control1Y: 262.801; control2X: 161.001; control2Y: 280.001; x: 165.401; y: 261.601 } + , PathCubic { control1X: 165.401; control1Y: 261.601; control2X: 178.201; control2Y: 255.201; x: 189.401; y: 282.801 } + , PathCubic { control1X: 189.401; control1Y: 282.801; control2X: 193.401; control2Y: 269.201; x: 192.601; y: 266.401 } + , PathCubic { control1X: 192.601; control1Y: 266.401; control2X: 199.401; control2Y: 267.601; x: 198.601; y: 266.401 } + , PathCubic { control1X: 198.601; control1Y: 266.401; control2X: 211.801; control2Y: 270.801; x: 213.001; y: 270.001 } + , PathCubic { control1X: 213.001; control1Y: 270.001; control2X: 219.801; control2Y: 276.801; x: 220.201; y: 273.201 } + , PathCubic { control1X: 220.201; control1Y: 273.201; control2X: 229.401; control2Y: 276.001; x: 227.401; y: 272.401 } + , PathCubic { control1X: 227.401; control1Y: 272.401; control2X: 236.201; control2Y: 288.001; x: 236.601; y: 291.601 } + , PathLine { x: 239.001; y: 277.601 } + , PathLine { x: 241.001; y: 280.401 } + , PathCubic { control1X: 241.001; control1Y: 280.401; control2X: 242.601; control2Y: 272.801; x: 241.801; y: 271.601 } + , PathCubic { control1X: 241.001; control1Y: 270.401; control2X: 261.801; control2Y: 278.401; x: 266.601; y: 299.201 } + , PathLine { x: 268.601; y: 307.601 } + , PathCubic { control1X: 268.601; control1Y: 307.601; control2X: 274.601; control2Y: 292.801; x: 273.001; y: 288.801 } + , PathCubic { control1X: 273.001; control1Y: 288.801; control2X: 278.201; control2Y: 289.601; x: 278.601; y: 294.001 } + , PathCubic { control1X: 278.601; control1Y: 294.001; control2X: 282.601; control2Y: 270.801; x: 277.801; y: 264.801 } + , PathCubic { control1X: 277.801; control1Y: 264.801; control2X: 282.201; control2Y: 264.001; x: 283.401; y: 267.601 } + , PathLine { x: 283.401; y: 260.401 } + , PathCubic { control1X: 283.401; control1Y: 260.401; control2X: 290.601; control2Y: 261.201; x: 290.601; y: 258.801 } + , PathCubic { control1X: 290.601; control1Y: 258.801; control2X: 295.001; control2Y: 254.801; x: 297.001; y: 259.601 } + , PathCubic { control1X: 297.001; control1Y: 259.601; control2X: 284.601; control2Y: 224.401; x: 303.001; y: 243.601 } + , PathCubic { control1X: 303.001; control1Y: 243.601; control2X: 310.201; control2Y: 254.401; x: 306.601; y: 235.601 } + , PathCubic { control1X: 303.001; control1Y: 216.801; control2X: 299.001; control2Y: 215.201; x: 303.801; y: 214.801 } + , PathCubic { control1X: 303.801; control1Y: 214.801; control2X: 304.601; control2Y: 211.201; x: 302.601; y: 209.601 } + , PathCubic { control1X: 300.601; control1Y: 208.001; control2X: 303.801; control2Y: 209.601; x: 303.801; y: 209.601 } + , PathCubic { control1X: 303.801; control1Y: 209.601; control2X: 308.601; control2Y: 213.601; x: 303.401; y: 191.601 } + , PathCubic { control1X: 303.401; control1Y: 191.601; control2X: 309.801; control2Y: 193.201; x: 297.801; y: 164.001 } + , PathCubic { control1X: 297.801; control1Y: 164.001; control2X: 300.601; control2Y: 161.601; x: 296.601; y: 153.201 } + , PathCubic { control1X: 296.601; control1Y: 153.201; control2X: 304.601; control2Y: 157.601; x: 307.401; y: 156.001 } + , PathCubic { control1X: 307.401; control1Y: 156.001; control2X: 307.001; control2Y: 154.401; x: 303.801; y: 150.401 } + , PathCubic { control1X: 303.801; control1Y: 150.401; control2X: 282.201; control2Y: 95.6; x: 302.601; y: 117.601 } + , PathCubic { control1X: 302.601; control1Y: 117.601; control2X: 314.451; control2Y: 131.151; x: 308.051; y: 108.351 } + , PathCubic { control1X: 308.051; control1Y: 108.351; control2X: 298.94; control2Y: 84.341; x: 299.717; y: 80.045 } + , PathLine { x: -129.83; y: 103.065 } + ] + } + + ControlledShape { + fillColor: "#cc7226" + strokeColor: "#000000" + strokeWidth: 1 + delegate: [PathMove { x: 299.717; y: 80.245 } + , PathCubic { control1X: 300.345; control1Y: 80.426; control2X: 302.551; control2Y: 81.55; x: 303.801; y: 83.2 } + , PathCubic { control1X: 303.801; control1Y: 83.2; control2X: 310.601; control2Y: 94; x: 305.401; y: 75.6 } + , PathCubic { control1X: 305.401; control1Y: 75.6; control2X: 296.201; control2Y: 46.8; x: 305.001; y: 58 } + , PathCubic { control1X: 305.001; control1Y: 58; control2X: 311.001; control2Y: 65.2; x: 307.801; y: 51.6 } + , PathCubic { control1X: 303.936; control1Y: 35.173; control2X: 301.401; control2Y: 28.8; x: 301.401; y: 28.8 } + , PathCubic { control1X: 301.401; control1Y: 28.8; control2X: 313.001; control2Y: 33.6; x: 286.201; y: -6 } + , PathLine { x: 295.001; y: -2.4 } + , PathCubic { control1X: 295.001; control1Y: -2.4; control2X: 275.401; control2Y: -42; x: 253.801; y: -47.2 } + , PathLine { x: 245.801; y: -53.2 } + , PathCubic { control1X: 245.801; control1Y: -53.2; control2X: 284.201; control2Y: -91.2; x: 271.401; y: -128 } + , PathCubic { control1X: 271.401; control1Y: -128; control2X: 264.601; control2Y: -133.2; x: 255.001; y: -124 } + , PathCubic { control1X: 255.001; control1Y: -124; control2X: 248.601; control2Y: -119.2; x: 242.601; y: -120.8 } + , PathCubic { control1X: 242.601; control1Y: -120.8; control2X: 211.801; control2Y: -119.6; x: 209.801; y: -119.6 } + , PathCubic { control1X: 207.801; control1Y: -119.6; control2X: 173.001; control2Y: -156.8; x: 107.401; y: -139.2 } + , PathCubic { control1X: 107.401; control1Y: -139.2; control2X: 102.201; control2Y: -137.2; x: 97.801; y: -138.4 } + , PathCubic { control1X: 97.801; control1Y: -138.4; control2X: 79.4; control2Y: -154.4; x: 30.6; y: -131.6 } + , PathCubic { control1X: 30.6; control1Y: -131.6; control2X: 20.6; control2Y: -129.6; x: 19; y: -129.6 } + , PathCubic { control1X: 17.4; control1Y: -129.6; control2X: 14.6; control2Y: -129.6; x: 6.6; y: -123.2 } + , PathCubic { control1X: -1.4; control1Y: -116.8; control2X: -1.8; control2Y: -116; x: -3.8; y: -114.4 } + , PathCubic { control1X: -3.8; control1Y: -114.4; control2X: -20.2; control2Y: -103.2; x: -25; y: -102.4 } + , PathCubic { control1X: -25; control1Y: -102.4; control2X: -36.6; control2Y: -96; x: -41; y: -86 } + , PathLine { x: -44.6; y: -84.8 } + , PathCubic { control1X: -44.6; control1Y: -84.8; control2X: -46.2; control2Y: -77.6; x: -46.6; y: -76.4 } + , PathCubic { control1X: -46.6; control1Y: -76.4; control2X: -51.4; control2Y: -72.8; x: -52.2; y: -67.2 } + , PathCubic { control1X: -52.2; control1Y: -67.2; control2X: -61; control2Y: -61.2; x: -60.6; y: -56.8 } + , PathCubic { control1X: -60.6; control1Y: -56.8; control2X: -62.2; control2Y: -51.6; x: -63; y: -46.8 } + , PathCubic { control1X: -63; control1Y: -46.8; control2X: -70.2; control2Y: -42; x: -69.4; y: -39.2 } + , PathCubic { control1X: -69.4; control1Y: -39.2; control2X: -77; control2Y: -25.2; x: -75.8; y: -18.4 } + , PathCubic { control1X: -75.8; control1Y: -18.4; control2X: -82.2; control2Y: -18.8; x: -85; y: -16.4 } + , PathCubic { control1X: -85; control1Y: -16.4; control2X: -85.8; control2Y: -11.6; x: -87.4; y: -11.2 } + , PathCubic { control1X: -87.4; control1Y: -11.2; control2X: -90.2; control2Y: -10; x: -87.8; y: -6 } + , PathCubic { control1X: -87.8; control1Y: -6; control2X: -89.4; control2Y: -3.2; x: -89.8; y: -1.6 } + , PathCubic { control1X: -89.8; control1Y: -1.6; control2X: -89; control2Y: 1.2; x: -93.4; y: 6.8 } + , PathCubic { control1X: -93.4; control1Y: 6.8; control2X: -99.8; control2Y: 25.6; x: -97.8; y: 30.8 } + , PathCubic { control1X: -97.8; control1Y: 30.8; control2X: -97.4; control2Y: 35.6; x: -100.2; y: 37.2 } + , PathCubic { control1X: -100.2; control1Y: 37.2; control2X: -103.8; control2Y: 36.8; x: -95.4; y: 48.8 } + , PathCubic { control1X: -95.4; control1Y: 48.8; control2X: -94.6; control2Y: 50; x: -97.8; y: 52.4 } + , PathCubic { control1X: -97.8; control1Y: 52.4; control2X: -115; control2Y: 56; x: -117.4; y: 72.4 } + , PathCubic { control1X: -117.4; control1Y: 72.4; control2X: -131; control2Y: 87.2; x: -131; y: 92.4 } + , PathCubic { control1X: -131; control1Y: 94.705; control2X: -130.729; control2Y: 97.852; x: -130.03; y: 102.465 } + , PathCubic { control1X: -130.03; control1Y: 102.465; control2X: -130.6; control2Y: 110.801; x: -103; y: 111.601 } + , PathCubic { control1X: -75.4; control1Y: 112.401; control2X: 299.717; control2Y: 80.245; x: 299.717; y: 80.245 } + ] + } + + ControlledShape { + fillColor: "#cc7226" + strokeWidth: -1 + delegate: [PathMove { x: -115.6; y: 102.6 } + , PathCubic { control1X: -140.6; control1Y: 63.2; control2X: -126.2; control2Y: 119.601; x: -126.2; y: 119.601 } + , PathCubic { control1X: -117.4; control1Y: 154.001; control2X: 12.2; control2Y: 116.401; x: 12.2; y: 116.401 } + , PathCubic { control1X: 12.2; control1Y: 116.401; control2X: 181.001; control2Y: 86; x: 192.201; y: 82 } + , PathCubic { control1X: 203.401; control1Y: 78; control2X: 298.601; control2Y: 84.4; x: 298.601; y: 84.4 } + , PathLine { x: 293.001; y: 67.6 } + , PathCubic { control1X: 228.201; control1Y: 21.2; control2X: 209.001; control2Y: 44.4; x: 195.401; y: 40.4 } + , PathCubic { control1X: 181.801; control1Y: 36.4; control2X: 184.201; control2Y: 46; x: 181.001; y: 46.8 } + , PathCubic { control1X: 177.801; control1Y: 47.6; control2X: 138.601; control2Y: 22.8; x: 132.201; y: 23.6 } + , PathCubic { control1X: 125.801; control1Y: 24.4; control2X: 100.459; control2Y: 0.649; x: 115.401; y: 32.4 } + , PathCubic { control1X: 131.401; control1Y: 66.4; control2X: 57; control2Y: 71.6; x: 40.2; y: 60.4 } + , PathCubic { control1X: 23.4; control1Y: 49.2; control2X: 47.4; control2Y: 78.8; x: 47.4; y: 78.8 } + , PathCubic { control1X: 65.8; control1Y: 98.8; control2X: 31.4; control2Y: 82; x: 31.4; y: 82 } + , PathCubic { control1X: -3; control1Y: 69.2; control2X: -27; control2Y: 94.8; x: -30.2; y: 95.6 } + , PathCubic { control1X: -33.4; control1Y: 96.4; control2X: -38.2; control2Y: 99.6; x: -39; y: 93.2 } + , PathCubic { control1X: -39.8; control1Y: 86.8; control2X: -47.31; control2Y: 70.099; x: -79; y: 96.4 } + , PathCubic { control1X: -99; control1Y: 113.001; control2X: -112.8; control2Y: 91; x: -112.8; y: 91 } + , PathLine { x: -115.6; y: 102.6 } + ] + } + + ControlledShape { + fillColor: "#e87f3a" + strokeWidth: -1 + delegate: [PathMove { x: 133.51; y: 25.346 } + , PathCubic { control1X: 127.11; control1Y: 26.146; control2X: 101.743; control2Y: 2.407; x: 116.71; y: 34.146 } + , PathCubic { control1X: 133.31; control1Y: 69.346; control2X: 58.31; control2Y: 73.346; x: 41.51; y: 62.146 } + , PathCubic { control1X: 24.709; control1Y: 50.946; control2X: 48.71; control2Y: 80.546; x: 48.71; y: 80.546 } + , PathCubic { control1X: 67.11; control1Y: 100.546; control2X: 32.709; control2Y: 83.746; x: 32.709; y: 83.746 } + , PathCubic { control1X: -1.691; control1Y: 70.946; control2X: -25.691; control2Y: 96.546; x: -28.891; y: 97.346 } + , PathCubic { control1X: -32.091; control1Y: 98.146; control2X: -36.891; control2Y: 101.346; x: -37.691; y: 94.946 } + , PathCubic { control1X: -38.491; control1Y: 88.546; control2X: -45.87; control2Y: 72.012; x: -77.691; y: 98.146 } + , PathCubic { control1X: -98.927; control1Y: 115.492; control2X: -112.418; control2Y: 94.037; x: -112.418; y: 94.037 } + , PathLine { x: -115.618; y: 104.146 } + , PathCubic { control1X: -140.618; control1Y: 64.346; control2X: -125.546; control2Y: 122.655; x: -125.546; y: 122.655 } + , PathCubic { control1X: -116.745; control1Y: 157.056; control2X: 13.509; control2Y: 118.146; x: 13.509; y: 118.146 } + , PathCubic { control1X: 13.509; control1Y: 118.146; control2X: 182.31; control2Y: 87.746; x: 193.51; y: 83.746 } + , PathCubic { control1X: 204.71; control1Y: 79.746; control2X: 299.038; control2Y: 86.073; x: 299.038; y: 86.073 } + , PathLine { x: 293.51; y: 68.764 } + , PathCubic { control1X: 228.71; control1Y: 22.364; control2X: 210.31; control2Y: 46.146; x: 196.71; y: 42.146 } + , PathCubic { control1X: 183.11; control1Y: 38.146; control2X: 185.51; control2Y: 47.746; x: 182.31; y: 48.546 } + , PathCubic { control1X: 179.11; control1Y: 49.346; control2X: 139.91; control2Y: 24.546; x: 133.51; y: 25.346 } + ] + } + + ControlledShape { + fillColor: "#ea8c4d" + strokeWidth: -1 + delegate: [PathMove { x: 134.819; y: 27.091 } + , PathCubic { control1X: 128.419; control1Y: 27.891; control2X: 103.685; control2Y: 3.862; x: 118.019; y: 35.891 } + , PathCubic { control1X: 134.219; control1Y: 72.092; control2X: 59.619; control2Y: 75.092; x: 42.819; y: 63.892 } + , PathCubic { control1X: 26.019; control1Y: 52.692; control2X: 50.019; control2Y: 82.292; x: 50.019; y: 82.292 } + , PathCubic { control1X: 68.419; control1Y: 102.292; control2X: 34.019; control2Y: 85.492; x: 34.019; y: 85.492 } + , PathCubic { control1X: -0.381; control1Y: 72.692; control2X: -24.382; control2Y: 98.292; x: -27.582; y: 99.092 } + , PathCubic { control1X: -30.782; control1Y: 99.892; control2X: -35.582; control2Y: 103.092; x: -36.382; y: 96.692 } + , PathCubic { control1X: -37.182; control1Y: 90.292; control2X: -44.43; control2Y: 73.925; x: -76.382; y: 99.892 } + , PathCubic { control1X: -98.855; control1Y: 117.983; control2X: -112.036; control2Y: 97.074; x: -112.036; y: 97.074 } + , PathLine { x: -115.636; y: 105.692 } + , PathCubic { control1X: -139.436; control1Y: 66.692; control2X: -124.891; control2Y: 125.71; x: -124.891; y: 125.71 } + , PathCubic { control1X: -116.091; control1Y: 160.11; control2X: 14.819; control2Y: 119.892; x: 14.819; y: 119.892 } + , PathCubic { control1X: 14.819; control1Y: 119.892; control2X: 183.619; control2Y: 89.492; x: 194.819; y: 85.492 } + , PathCubic { control1X: 206.019; control1Y: 81.492; control2X: 299.474; control2Y: 87.746; x: 299.474; y: 87.746 } + , PathLine { x: 294.02; y: 69.928 } + , PathCubic { control1X: 229.219; control1Y: 23.528; control2X: 211.619; control2Y: 47.891; x: 198.019; y: 43.891 } + , PathCubic { control1X: 184.419; control1Y: 39.891; control2X: 186.819; control2Y: 49.491; x: 183.619; y: 50.292 } + , PathCubic { control1X: 180.419; control1Y: 51.092; control2X: 141.219; control2Y: 26.291; x: 134.819; y: 27.091 } + ] + } + + ControlledShape { + fillColor: "#ec9961" + strokeWidth: -1 + delegate: [PathMove { x: 136.128; y: 28.837 } + , PathCubic { control1X: 129.728; control1Y: 29.637; control2X: 104.999; control2Y: 5.605; x: 119.328; y: 37.637 } + , PathCubic { control1X: 136.128; control1Y: 75.193; control2X: 60.394; control2Y: 76.482; x: 44.128; y: 65.637 } + , PathCubic { control1X: 27.328; control1Y: 54.437; control2X: 51.328; control2Y: 84.037; x: 51.328; y: 84.037 } + , PathCubic { control1X: 69.728; control1Y: 104.037; control2X: 35.328; control2Y: 87.237; x: 35.328; y: 87.237 } + , PathCubic { control1X: 0.928; control1Y: 74.437; control2X: -23.072; control2Y: 100.037; x: -26.272; y: 100.837 } + , PathCubic { control1X: -29.472; control1Y: 101.637; control2X: -34.272; control2Y: 104.837; x: -35.072; y: 98.437 } + , PathCubic { control1X: -35.872; control1Y: 92.037; control2X: -42.989; control2Y: 75.839; x: -75.073; y: 101.637 } + , PathCubic { control1X: -98.782; control1Y: 120.474; control2X: -111.655; control2Y: 100.11; x: -111.655; y: 100.11 } + , PathLine { x: -115.655; y: 107.237 } + , PathCubic { control1X: -137.455; control1Y: 70.437; control2X: -124.236; control2Y: 128.765; x: -124.236; y: 128.765 } + , PathCubic { control1X: -115.436; control1Y: 163.165; control2X: 16.128; control2Y: 121.637; x: 16.128; y: 121.637 } + , PathCubic { control1X: 16.128; control1Y: 121.637; control2X: 184.928; control2Y: 91.237; x: 196.129; y: 87.237 } + , PathCubic { control1X: 207.329; control1Y: 83.237; control2X: 299.911; control2Y: 89.419; x: 299.911; y: 89.419 } + , PathLine { x: 294.529; y: 71.092 } + , PathCubic { control1X: 229.729; control1Y: 24.691; control2X: 212.929; control2Y: 49.637; x: 199.329; y: 45.637 } + , PathCubic { control1X: 185.728; control1Y: 41.637; control2X: 188.128; control2Y: 51.237; x: 184.928; y: 52.037 } + , PathCubic { control1X: 181.728; control1Y: 52.837; control2X: 142.528; control2Y: 28.037; x: 136.128; y: 28.837 } + ] + } + + ControlledShape { + fillColor: "#eea575" + strokeWidth: -1 + delegate: [PathMove { x: 137.438; y: 30.583 } + , PathCubic { control1X: 131.037; control1Y: 31.383; control2X: 106.814; control2Y: 7.129; x: 120.637; y: 39.383 } + , PathCubic { control1X: 137.438; control1Y: 78.583; control2X: 62.237; control2Y: 78.583; x: 45.437; y: 67.383 } + , PathCubic { control1X: 28.637; control1Y: 56.183; control2X: 52.637; control2Y: 85.783; x: 52.637; y: 85.783 } + , PathCubic { control1X: 71.037; control1Y: 105.783; control2X: 36.637; control2Y: 88.983; x: 36.637; y: 88.983 } + , PathCubic { control1X: 2.237; control1Y: 76.183; control2X: -21.763; control2Y: 101.783; x: -24.963; y: 102.583 } + , PathCubic { control1X: -28.163; control1Y: 103.383; control2X: -32.963; control2Y: 106.583; x: -33.763; y: 100.183 } + , PathCubic { control1X: -34.563; control1Y: 93.783; control2X: -41.548; control2Y: 77.752; x: -73.763; y: 103.383 } + , PathCubic { control1X: -98.709; control1Y: 122.965; control2X: -111.273; control2Y: 103.146; x: -111.273; y: 103.146 } + , PathLine { x: -115.673; y: 108.783 } + , PathCubic { control1X: -135.473; control1Y: 73.982; control2X: -123.582; control2Y: 131.819; x: -123.582; y: 131.819 } + , PathCubic { control1X: -114.782; control1Y: 166.22; control2X: 17.437; control2Y: 123.383; x: 17.437; y: 123.383 } + , PathCubic { control1X: 17.437; control1Y: 123.383; control2X: 186.238; control2Y: 92.983; x: 197.438; y: 88.983 } + , PathCubic { control1X: 208.638; control1Y: 84.983; control2X: 300.347; control2Y: 91.092; x: 300.347; y: 91.092 } + , PathLine { x: 295.038; y: 72.255 } + , PathCubic { control1X: 230.238; control1Y: 25.855; control2X: 214.238; control2Y: 51.383; x: 200.638; y: 47.383 } + , PathCubic { control1X: 187.038; control1Y: 43.383; control2X: 189.438; control2Y: 52.983; x: 186.238; y: 53.783 } + , PathCubic { control1X: 183.038; control1Y: 54.583; control2X: 143.838; control2Y: 29.783; x: 137.438; y: 30.583 } + ] + } + + ControlledShape { + fillColor: "#f1b288" + strokeWidth: -1 + delegate: [PathMove { x: 138.747; y: 32.328 } + , PathCubic { control1X: 132.347; control1Y: 33.128; control2X: 106.383; control2Y: 9.677; x: 121.947; y: 41.128 } + , PathCubic { control1X: 141.147; control1Y: 79.928; control2X: 63.546; control2Y: 80.328; x: 46.746; y: 69.128 } + , PathCubic { control1X: 29.946; control1Y: 57.928; control2X: 53.946; control2Y: 87.528; x: 53.946; y: 87.528 } + , PathCubic { control1X: 72.346; control1Y: 107.528; control2X: 37.946; control2Y: 90.728; x: 37.946; y: 90.728 } + , PathCubic { control1X: 3.546; control1Y: 77.928; control2X: -20.454; control2Y: 103.528; x: -23.654; y: 104.328 } + , PathCubic { control1X: -26.854; control1Y: 105.128; control2X: -31.654; control2Y: 108.328; x: -32.454; y: 101.928 } + , PathCubic { control1X: -33.254; control1Y: 95.528; control2X: -40.108; control2Y: 79.665; x: -72.454; y: 105.128 } + , PathCubic { control1X: -98.636; control1Y: 125.456; control2X: -110.891; control2Y: 106.183; x: -110.891; y: 106.183 } + , PathLine { x: -115.691; y: 110.328 } + , PathCubic { control1X: -133.691; control1Y: 77.128; control2X: -122.927; control2Y: 134.874; x: -122.927; y: 134.874 } + , PathCubic { control1X: -114.127; control1Y: 169.274; control2X: 18.746; control2Y: 125.128; x: 18.746; y: 125.128 } + , PathCubic { control1X: 18.746; control1Y: 125.128; control2X: 187.547; control2Y: 94.728; x: 198.747; y: 90.728 } + , PathCubic { control1X: 209.947; control1Y: 86.728; control2X: 300.783; control2Y: 92.764; x: 300.783; y: 92.764 } + , PathLine { x: 295.547; y: 73.419 } + , PathCubic { control1X: 230.747; control1Y: 27.019; control2X: 215.547; control2Y: 53.128; x: 201.947; y: 49.128 } + , PathCubic { control1X: 188.347; control1Y: 45.128; control2X: 190.747; control2Y: 54.728; x: 187.547; y: 55.528 } + , PathCubic { control1X: 184.347; control1Y: 56.328; control2X: 145.147; control2Y: 31.528; x: 138.747; y: 32.328 } + ] + } + + ControlledShape { + fillColor: "#f3bf9c" + strokeWidth: -1 + delegate: [PathMove { x: 140.056; y: 34.073 } + , PathCubic { control1X: 133.655; control1Y: 34.873; control2X: 107.313; control2Y: 11.613; x: 123.255; y: 42.873 } + , PathCubic { control1X: 143.656; control1Y: 82.874; control2X: 64.855; control2Y: 82.074; x: 48.055; y: 70.874 } + , PathCubic { control1X: 31.255; control1Y: 59.674; control2X: 55.255; control2Y: 89.274; x: 55.255; y: 89.274 } + , PathCubic { control1X: 73.655; control1Y: 109.274; control2X: 39.255; control2Y: 92.474; x: 39.255; y: 92.474 } + , PathCubic { control1X: 4.855; control1Y: 79.674; control2X: -19.145; control2Y: 105.274; x: -22.345; y: 106.074 } + , PathCubic { control1X: -25.545; control1Y: 106.874; control2X: -30.345; control2Y: 110.074; x: -31.145; y: 103.674 } + , PathCubic { control1X: -31.945; control1Y: 97.274; control2X: -38.668; control2Y: 81.578; x: -71.145; y: 106.874 } + , PathCubic { control1X: -98.564; control1Y: 127.947; control2X: -110.509; control2Y: 109.219; x: -110.509; y: 109.219 } + , PathLine { x: -115.709; y: 111.874 } + , PathCubic { control1X: -131.709; control1Y: 81.674; control2X: -122.273; control2Y: 137.929; x: -122.273; y: 137.929 } + , PathCubic { control1X: -113.473; control1Y: 172.329; control2X: 20.055; control2Y: 126.874; x: 20.055; y: 126.874 } + , PathCubic { control1X: 20.055; control1Y: 126.874; control2X: 188.856; control2Y: 96.474; x: 200.056; y: 92.474 } + , PathCubic { control1X: 211.256; control1Y: 88.474; control2X: 301.22; control2Y: 94.437; x: 301.22; y: 94.437 } + , PathLine { x: 296.056; y: 74.583 } + , PathCubic { control1X: 231.256; control1Y: 28.183; control2X: 216.856; control2Y: 54.874; x: 203.256; y: 50.874 } + , PathCubic { control1X: 189.656; control1Y: 46.873; control2X: 192.056; control2Y: 56.474; x: 188.856; y: 57.274 } + , PathCubic { control1X: 185.656; control1Y: 58.074; control2X: 146.456; control2Y: 33.273; x: 140.056; y: 34.073 } + ] + } + + ControlledShape { + fillColor: "#f5ccb0" + strokeWidth: -1 + delegate: [PathMove { x: 141.365; y: 35.819 } + , PathCubic { control1X: 134.965; control1Y: 36.619; control2X: 107.523; control2Y: 13.944; x: 124.565; y: 44.619 } + , PathCubic { control1X: 146.565; control1Y: 84.219; control2X: 66.164; control2Y: 83.819; x: 49.364; y: 72.619 } + , PathCubic { control1X: 32.564; control1Y: 61.419; control2X: 56.564; control2Y: 91.019; x: 56.564; y: 91.019 } + , PathCubic { control1X: 74.964; control1Y: 111.019; control2X: 40.564; control2Y: 94.219; x: 40.564; y: 94.219 } + , PathCubic { control1X: 6.164; control1Y: 81.419; control2X: -17.836; control2Y: 107.019; x: -21.036; y: 107.819 } + , PathCubic { control1X: -24.236; control1Y: 108.619; control2X: -29.036; control2Y: 111.819; x: -29.836; y: 105.419 } + , PathCubic { control1X: -30.636; control1Y: 99.019; control2X: -37.227; control2Y: 83.492; x: -69.836; y: 108.619 } + , PathCubic { control1X: -98.491; control1Y: 130.438; control2X: -110.127; control2Y: 112.256; x: -110.127; y: 112.256 } + , PathLine { x: -115.727; y: 113.419 } + , PathCubic { control1X: -130.128; control1Y: 85.019; control2X: -121.618; control2Y: 140.983; x: -121.618; y: 140.983 } + , PathCubic { control1X: -112.818; control1Y: 175.384; control2X: 21.364; control2Y: 128.619; x: 21.364; y: 128.619 } + , PathCubic { control1X: 21.364; control1Y: 128.619; control2X: 190.165; control2Y: 98.219; x: 201.365; y: 94.219 } + , PathCubic { control1X: 212.565; control1Y: 90.219; control2X: 301.656; control2Y: 96.11; x: 301.656; y: 96.11 } + , PathLine { x: 296.565; y: 75.746 } + , PathCubic { control1X: 231.765; control1Y: 29.346; control2X: 218.165; control2Y: 56.619; x: 204.565; y: 52.619 } + , PathCubic { control1X: 190.965; control1Y: 48.619; control2X: 193.365; control2Y: 58.219; x: 190.165; y: 59.019 } + , PathCubic { control1X: 186.965; control1Y: 59.819; control2X: 147.765; control2Y: 35.019; x: 141.365; y: 35.819 } + ] + } + + ControlledShape { + fillColor: "#f8d8c4" + strokeWidth: -1 + delegate: [PathMove { x: 142.674; y: 37.565 } + , PathCubic { control1X: 136.274; control1Y: 38.365; control2X: 108.832; control2Y: 15.689; x: 125.874; y: 46.365 } + , PathCubic { control1X: 147.874; control1Y: 85.965; control2X: 67.474; control2Y: 85.565; x: 50.674; y: 74.365 } + , PathCubic { control1X: 33.874; control1Y: 63.165; control2X: 57.874; control2Y: 92.765; x: 57.874; y: 92.765 } + , PathCubic { control1X: 76.274; control1Y: 112.765; control2X: 41.874; control2Y: 95.965; x: 41.874; y: 95.965 } + , PathCubic { control1X: 7.473; control1Y: 83.165; control2X: -16.527; control2Y: 108.765; x: -19.727; y: 109.565 } + , PathCubic { control1X: -22.927; control1Y: 110.365; control2X: -27.727; control2Y: 113.565; x: -28.527; y: 107.165 } + , PathCubic { control1X: -29.327; control1Y: 100.765; control2X: -35.786; control2Y: 85.405; x: -68.527; y: 110.365 } + , PathCubic { control1X: -98.418; control1Y: 132.929; control2X: -109.745; control2Y: 115.293; x: -109.745; y: 115.293 } + , PathLine { x: -115.745; y: 114.965 } + , PathCubic { control1X: -129.346; control1Y: 88.564; control2X: -120.963; control2Y: 144.038; x: -120.963; y: 144.038 } + , PathCubic { control1X: -112.163; control1Y: 178.438; control2X: 22.673; control2Y: 130.365; x: 22.673; y: 130.365 } + , PathCubic { control1X: 22.673; control1Y: 130.365; control2X: 191.474; control2Y: 99.965; x: 202.674; y: 95.965 } + , PathCubic { control1X: 213.874; control1Y: 91.965; control2X: 302.093; control2Y: 97.783; x: 302.093; y: 97.783 } + , PathLine { x: 297.075; y: 76.91 } + , PathCubic { control1X: 232.274; control1Y: 30.51; control2X: 219.474; control2Y: 58.365; x: 205.874; y: 54.365 } + , PathCubic { control1X: 192.274; control1Y: 50.365; control2X: 194.674; control2Y: 59.965; x: 191.474; y: 60.765 } + , PathCubic { control1X: 188.274; control1Y: 61.565; control2X: 149.074; control2Y: 36.765; x: 142.674; y: 37.565 } + ] + } + + ControlledShape { + fillColor: "#fae5d7" + strokeWidth: -1 + delegate: [PathMove { x: 143.983; y: 39.31 } + , PathCubic { control1X: 137.583; control1Y: 40.11; control2X: 110.529; control2Y: 17.223; x: 127.183; y: 48.11 } + , PathCubic { control1X: 149.183; control1Y: 88.91; control2X: 68.783; control2Y: 87.31; x: 51.983; y: 76.11 } + , PathCubic { control1X: 35.183; control1Y: 64.91; control2X: 59.183; control2Y: 94.51; x: 59.183; y: 94.51 } + , PathCubic { control1X: 77.583; control1Y: 114.51; control2X: 43.183; control2Y: 97.71; x: 43.183; y: 97.71 } + , PathCubic { control1X: 8.783; control1Y: 84.91; control2X: -15.217; control2Y: 110.51; x: -18.417; y: 111.31 } + , PathCubic { control1X: -21.618; control1Y: 112.11; control2X: -26.418; control2Y: 115.31; x: -27.218; y: 108.91 } + , PathCubic { control1X: -28.018; control1Y: 102.51; control2X: -34.346; control2Y: 87.318; x: -67.218; y: 112.11 } + , PathCubic { control1X: -98.345; control1Y: 135.42; control2X: -109.363; control2Y: 118.329; x: -109.363; y: 118.329 } + , PathLine { x: -115.764; y: 116.51 } + , PathCubic { control1X: -128.764; control1Y: 92.51; control2X: -120.309; control2Y: 147.093; x: -120.309; y: 147.093 } + , PathCubic { control1X: -111.509; control1Y: 181.493; control2X: 23.983; control2Y: 132.11; x: 23.983; y: 132.11 } + , PathCubic { control1X: 23.983; control1Y: 132.11; control2X: 192.783; control2Y: 101.71; x: 203.983; y: 97.71 } + , PathCubic { control1X: 215.183; control1Y: 93.71; control2X: 302.529; control2Y: 99.456; x: 302.529; y: 99.456 } + , PathLine { x: 297.583; y: 78.074 } + , PathCubic { control1X: 232.783; control1Y: 31.673; control2X: 220.783; control2Y: 60.11; x: 207.183; y: 56.11 } + , PathCubic { control1X: 193.583; control1Y: 52.11; control2X: 195.983; control2Y: 61.71; x: 192.783; y: 62.51 } + , PathCubic { control1X: 189.583; control1Y: 63.31; control2X: 150.383; control2Y: 38.51; x: 143.983; y: 39.31 } + ] + } + + ControlledShape { + fillColor: "#fcf2eb" + strokeWidth: -1 + delegate: [PathMove { x: 145.292; y: 41.055 } + , PathCubic { control1X: 138.892; control1Y: 41.855; control2X: 112.917; control2Y: 18.411; x: 128.492; y: 49.855 } + , PathCubic { control1X: 149.692; control1Y: 92.656; control2X: 70.092; control2Y: 89.056; x: 53.292; y: 77.856 } + , PathCubic { control1X: 36.492; control1Y: 66.656; control2X: 60.492; control2Y: 96.256; x: 60.492; y: 96.256 } + , PathCubic { control1X: 78.892; control1Y: 116.256; control2X: 44.492; control2Y: 99.456; x: 44.492; y: 99.456 } + , PathCubic { control1X: 10.092; control1Y: 86.656; control2X: -13.908; control2Y: 112.256; x: -17.108; y: 113.056 } + , PathCubic { control1X: -20.308; control1Y: 113.856; control2X: -25.108; control2Y: 117.056; x: -25.908; y: 110.656 } + , PathCubic { control1X: -26.708; control1Y: 104.256; control2X: -32.905; control2Y: 89.232; x: -65.908; y: 113.856 } + , PathCubic { control1X: -98.273; control1Y: 137.911; control2X: -108.982; control2Y: 121.365; x: -108.982; y: 121.365 } + , PathLine { x: -115.782; y: 118.056 } + , PathCubic { control1X: -128.582; control1Y: 94.856; control2X: -119.654; control2Y: 150.147; x: -119.654; y: 150.147 } + , PathCubic { control1X: -110.854; control1Y: 184.547; control2X: 25.292; control2Y: 133.856; x: 25.292; y: 133.856 } + , PathCubic { control1X: 25.292; control1Y: 133.856; control2X: 194.093; control2Y: 103.456; x: 205.293; y: 99.456 } + , PathCubic { control1X: 216.493; control1Y: 95.456; control2X: 302.965; control2Y: 101.128; x: 302.965; y: 101.128 } + , PathLine { x: 298.093; y: 79.237 } + , PathCubic { control1X: 233.292; control1Y: 32.837; control2X: 222.093; control2Y: 61.856; x: 208.493; y: 57.856 } + , PathCubic { control1X: 194.893; control1Y: 53.855; control2X: 197.293; control2Y: 63.456; x: 194.093; y: 64.256 } + , PathCubic { control1X: 190.892; control1Y: 65.056; control2X: 151.692; control2Y: 40.255; x: 145.292; y: 41.055 } + ] + } + + ControlledShape { + fillColor: "#ffffff" + strokeWidth: -1 + delegate: [PathMove { x: -115.8; y: 119.601 } + , PathCubic { control1X: -128.6; control1Y: 97.6; control2X: -119; control2Y: 153.201; x: -119; y: 153.201 } + , PathCubic { control1X: -110.2; control1Y: 187.601; control2X: 26.6; control2Y: 135.601; x: 26.6; y: 135.601 } + , PathCubic { control1X: 26.6; control1Y: 135.601; control2X: 195.401; control2Y: 105.2; x: 206.601; y: 101.2 } + , PathCubic { control1X: 217.801; control1Y: 97.2; control2X: 303.401; control2Y: 102.8; x: 303.401; y: 102.8 } + , PathLine { x: 298.601; y: 80.4 } + , PathCubic { control1X: 233.801; control1Y: 34; control2X: 223.401; control2Y: 63.6; x: 209.801; y: 59.6 } + , PathCubic { control1X: 196.201; control1Y: 55.6; control2X: 198.601; control2Y: 65.2; x: 195.401; y: 66 } + , PathCubic { control1X: 192.201; control1Y: 66.8; control2X: 153.001; control2Y: 42; x: 146.601; y: 42.8 } + , PathCubic { control1X: 140.201; control1Y: 43.6; control2X: 114.981; control2Y: 19.793; x: 129.801; y: 51.6 } + , PathCubic { control1X: 152.028; control1Y: 99.307; control2X: 69.041; control2Y: 89.227; x: 54.6; y: 79.6 } + , PathCubic { control1X: 37.8; control1Y: 68.4; control2X: 61.8; control2Y: 98; x: 61.8; y: 98 } + , PathCubic { control1X: 80.2; control1Y: 118.001; control2X: 45.8; control2Y: 101.2; x: 45.8; y: 101.2 } + , PathCubic { control1X: 11.4; control1Y: 88.4; control2X: -12.6; control2Y: 114.001; x: -15.8; y: 114.801 } + , PathCubic { control1X: -19; control1Y: 115.601; control2X: -23.8; control2Y: 118.801; x: -24.6; y: 112.401 } + , PathCubic { control1X: -25.4; control1Y: 106; control2X: -31.465; control2Y: 91.144; x: -64.6; y: 115.601 } + , PathCubic { control1X: -98.2; control1Y: 140.401; control2X: -108.6; control2Y: 124.401; x: -108.6; y: 124.401 } + , PathLine { x: -115.8; y: 119.601 } + ] + } + + ControlledShape { + fillColor: "#000000" + strokeWidth: -1 + delegate: [PathMove { x: -74.2; y: 149.601 } + , PathCubic { control1X: -74.2; control1Y: 149.601; control2X: -81.4; control2Y: 161.201; x: -60.6; y: 174.401 } + , PathCubic { control1X: -60.6; control1Y: 174.401; control2X: -59.2; control2Y: 175.801; x: -77.2; y: 171.601 } + , PathCubic { control1X: -77.2; control1Y: 171.601; control2X: -83.4; control2Y: 169.601; x: -85; y: 159.201 } + , PathCubic { control1X: -85; control1Y: 159.201; control2X: -89.8; control2Y: 154.801; x: -94.6; y: 149.201 } + , PathCubic { control1X: -99.4; control1Y: 143.601; control2X: -74.2; control2Y: 149.601; x: -74.2; y: 149.601 } + ] + } + + ControlledShape { + fillColor: "#cccccc" + strokeWidth: -1 + delegate: [PathMove { x: 65.8; y: 102 } + , PathCubic { control1X: 65.8; control1Y: 102; control2X: 83.498; control2Y: 128.821; x: 82.9; y: 133.601 } + , PathCubic { control1X: 81.6; control1Y: 144.001; control2X: 81.4; control2Y: 153.601; x: 84.6; y: 157.601 } + , PathCubic { control1X: 87.801; control1Y: 161.601; control2X: 96.601; control2Y: 194.801; x: 96.601; y: 194.801 } + , PathCubic { control1X: 96.601; control1Y: 194.801; control2X: 96.201; control2Y: 196.001; x: 108.601; y: 158.001 } + , PathCubic { control1X: 108.601; control1Y: 158.001; control2X: 120.201; control2Y: 142.001; x: 100.201; y: 123.601 } + , PathCubic { control1X: 100.201; control1Y: 123.601; control2X: 65; control2Y: 94.8; x: 65.8; y: 102 } + ] + } + + ControlledShape { + fillColor: "#000000" + strokeWidth: -1 + delegate: [PathMove { x: -54.2; y: 176.401 } + , PathCubic { control1X: -54.2; control1Y: 176.401; control2X: -43; control2Y: 183.601; x: -57.4; y: 214.801 } + , PathLine { x: -51; y: 212.401 } + , PathCubic { control1X: -51; control1Y: 212.401; control2X: -51.8; control2Y: 223.601; x: -55; y: 226.001 } + , PathLine { x: -47.8; y: 222.801 } + , PathCubic { control1X: -47.8; control1Y: 222.801; control2X: -43; control2Y: 230.801; x: -47; y: 235.601 } + , PathCubic { control1X: -47; control1Y: 235.601; control2X: -30.2; control2Y: 243.601; x: -31; y: 250.001 } + , PathCubic { control1X: -31; control1Y: 250.001; control2X: -24.6; control2Y: 242.001; x: -28.6; y: 235.601 } + , PathCubic { control1X: -32.6; control1Y: 229.201; control2X: -39.8; control2Y: 233.201; x: -39; y: 214.801 } + , PathLine { x: -47.8; y: 218.001 } + , PathCubic { control1X: -47.8; control1Y: 218.001; control2X: -42.2; control2Y: 209.201; x: -42.2; y: 202.801 } + , PathLine { x: -50.2; y: 205.201 } + , PathCubic { control1X: -50.2; control1Y: 205.201; control2X: -34.731; control2Y: 178.623; x: -45.4; y: 177.201 } + , PathCubic { control1X: -51.4; control1Y: 176.401; control2X: -54.2; control2Y: 176.401; x: -54.2; y: 176.401 } + ] + } + + ControlledShape { + fillColor: "#cccccc" + strokeWidth: -1 + delegate: [PathMove { x: -21.8; y: 193.201 } + , PathCubic { control1X: -21.8; control1Y: 193.201; control2X: -19; control2Y: 188.801; x: -21.8; y: 189.601 } + , PathCubic { control1X: -24.6; control1Y: 190.401; control2X: -55.8; control2Y: 205.201; x: -61.8; y: 214.801 } + , PathCubic { control1X: -61.8; control1Y: 214.801; control2X: -27.4; control2Y: 190.401; x: -21.8; y: 193.201 } + ] + } + + ControlledShape { + fillColor: "#cccccc" + strokeWidth: -1 + delegate: [PathMove { x: -11.4; y: 201.201 } + , PathCubic { control1X: -11.4; control1Y: 201.201; control2X: -8.6; control2Y: 196.801; x: -11.4; y: 197.601 } + , PathCubic { control1X: -14.2; control1Y: 198.401; control2X: -45.4; control2Y: 213.201; x: -51.4; y: 222.801 } + , PathCubic { control1X: -51.4; control1Y: 222.801; control2X: -17; control2Y: 198.401; x: -11.4; y: 201.201 } + ] + } + + ControlledShape { + fillColor: "#cccccc" + strokeWidth: -1 + delegate: [PathMove { x: 1.8; y: 186.001 } + , PathCubic { control1X: 1.8; control1Y: 186.001; control2X: 4.6; control2Y: 181.601; x: 1.8; y: 182.401 } + , PathCubic { control1X: -1; control1Y: 183.201; control2X: -32.2; control2Y: 198.001; x: -38.2; y: 207.601 } + , PathCubic { control1X: -38.2; control1Y: 207.601; control2X: -3.8; control2Y: 183.201; x: 1.8; y: 186.001 } + ] + } + + ControlledShape { + fillColor: "#cccccc" + strokeWidth: -1 + delegate: [PathMove { x: -21.4; y: 229.601 } + , PathCubic { control1X: -21.4; control1Y: 229.601; control2X: -21.4; control2Y: 223.601; x: -24.2; y: 224.401 } + , PathCubic { control1X: -27; control1Y: 225.201; control2X: -63; control2Y: 242.801; x: -69; y: 252.401 } + , PathCubic { control1X: -69; control1Y: 252.401; control2X: -27; control2Y: 226.801; x: -21.4; y: 229.601 } + ] + } + + ControlledShape { + fillColor: "#cccccc" + strokeWidth: -1 + delegate: [PathMove { x: -20.2; y: 218.801 } + , PathCubic { control1X: -20.2; control1Y: 218.801; control2X: -19; control2Y: 214.001; x: -21.8; y: 214.801 } + , PathCubic { control1X: -23.8; control1Y: 214.801; control2X: -50.2; control2Y: 226.401; x: -56.2; y: 236.001 } + , PathCubic { control1X: -56.2; control1Y: 236.001; control2X: -26.6; control2Y: 214.401; x: -20.2; y: 218.801 } + ] + } + + ControlledShape { + fillColor: "#cccccc" + strokeWidth: -1 + delegate: [PathMove { x: -34.6; y: 266.401 } + , PathLine { x: -44.6; y: 274.001 } + , PathCubic { control1X: -44.6; control1Y: 274.001; control2X: -34.2; control2Y: 266.401; x: -30.6; y: 267.601 } + , PathCubic { control1X: -30.6; control1Y: 267.601; control2X: -37.4; control2Y: 278.801; x: -38.2; y: 284.001 } + , PathCubic { control1X: -38.2; control1Y: 284.001; control2X: -27.8; control2Y: 271.201; x: -22.2; y: 271.601 } + , PathCubic { control1X: -22.2; control1Y: 271.601; control2X: -14.6; control2Y: 272.001; x: -14.6; y: 282.801 } + , PathCubic { control1X: -14.6; control1Y: 282.801; control2X: -9; control2Y: 272.401; x: -5.8; y: 272.801 } + , PathCubic { control1X: -5.8; control1Y: 272.801; control2X: -4.6; control2Y: 279.201; x: -5.8; y: 286.001 } + , PathCubic { control1X: -5.8; control1Y: 286.001; control2X: -1.8; control2Y: 278.401; x: 2.2; y: 280.001 } + , PathCubic { control1X: 2.2; control1Y: 280.001; control2X: 8.6; control2Y: 278.001; x: 7.8; y: 289.601 } + , PathCubic { control1X: 7.8; control1Y: 289.601; control2X: 7.8; control2Y: 300.001; x: 7; y: 302.801 } + , PathCubic { control1X: 7; control1Y: 302.801; control2X: 12.6; control2Y: 276.401; x: 15; y: 276.001 } + , PathCubic { control1X: 15; control1Y: 276.001; control2X: 23; control2Y: 274.801; x: 27.8; y: 283.601 } + , PathCubic { control1X: 27.8; control1Y: 283.601; control2X: 23.8; control2Y: 276.001; x: 28.6; y: 278.001 } + , PathCubic { control1X: 28.6; control1Y: 278.001; control2X: 39.4; control2Y: 279.601; x: 42.6; y: 286.401 } + , PathCubic { control1X: 42.6; control1Y: 286.401; control2X: 35.8; control2Y: 274.401; x: 41.4; y: 277.601 } + , PathCubic { control1X: 41.4; control1Y: 277.601; control2X: 48.2; control2Y: 277.601; x: 49.4; y: 284.001 } + , PathCubic { control1X: 49.4; control1Y: 284.001; control2X: 57.8; control2Y: 305.201; x: 59.8; y: 306.801 } + , PathCubic { control1X: 59.8; control1Y: 306.801; control2X: 52.2; control2Y: 285.201; x: 53.8; y: 285.201 } + , PathCubic { control1X: 53.8; control1Y: 285.201; control2X: 51.8; control2Y: 273.201; x: 57; y: 288.001 } + , PathCubic { control1X: 57; control1Y: 288.001; control2X: 53.8; control2Y: 274.001; x: 59.4; y: 274.801 } + , PathCubic { control1X: 65; control1Y: 275.601; control2X: 69.4; control2Y: 285.601; x: 77.8; y: 283.201 } + , PathCubic { control1X: 77.8; control1Y: 283.201; control2X: 87.401; control2Y: 288.801; x: 89.401; y: 219.601 } + , PathLine { x: -34.6; y: 266.401 } + ] + } + + ControlledShape { + fillColor: "#000000" + strokeWidth: -1 + delegate: [PathMove { x: -29.8; y: 173.601 } + , PathCubic { control1X: -29.8; control1Y: 173.601; control2X: -15; control2Y: 167.601; x: 25; y: 173.601 } + , PathCubic { control1X: 25; control1Y: 173.601; control2X: 32.2; control2Y: 174.001; x: 39; y: 165.201 } + , PathCubic { control1X: 45.8; control1Y: 156.401; control2X: 72.6; control2Y: 149.201; x: 79; y: 151.201 } + , PathLine { x: 88.601; y: 157.601 } + , PathLine { x: 89.401; y: 158.801 } + , PathCubic { control1X: 89.401; control1Y: 158.801; control2X: 101.801; control2Y: 169.201; x: 102.201; y: 176.801 } + , PathCubic { control1X: 102.601; control1Y: 184.401; control2X: 87.801; control2Y: 232.401; x: 78.2; y: 248.401 } + , PathCubic { control1X: 68.6; control1Y: 264.401; control2X: 59; control2Y: 276.801; x: 39.8; y: 274.401 } + , PathCubic { control1X: 39.8; control1Y: 274.401; control2X: 19; control2Y: 270.401; x: -6.6; y: 274.401 } + , PathCubic { control1X: -6.6; control1Y: 274.401; control2X: -35.8; control2Y: 272.801; x: -38.6; y: 264.801 } + , PathCubic { control1X: -41.4; control1Y: 256.801; control2X: -27.4; control2Y: 241.601; x: -27.4; y: 241.601 } + , PathCubic { control1X: -27.4; control1Y: 241.601; control2X: -23; control2Y: 233.201; x: -24.2; y: 218.801 } + , PathCubic { control1X: -25.4; control1Y: 204.401; control2X: -25; control2Y: 176.401; x: -29.8; y: 173.601 } + ] + } + + ControlledShape { + fillColor: "#e5668c" + strokeWidth: -1 + delegate: [PathMove { x: -7.8; y: 175.601 } + , PathCubic { control1X: 0.6; control1Y: 194.001; control2X: -29; control2Y: 259.201; x: -29; y: 259.201 } + , PathCubic { control1X: -31; control1Y: 260.801; control2X: -16.34; control2Y: 266.846; x: -6.2; y: 264.401 } + , PathCubic { control1X: 4.746; control1Y: 261.763; control2X: 45; control2Y: 266.001; x: 45; y: 266.001 } + , PathCubic { control1X: 68.6; control1Y: 250.401; control2X: 81.4; control2Y: 206.001; x: 81.4; y: 206.001 } + , PathCubic { control1X: 81.4; control1Y: 206.001; control2X: 91.801; control2Y: 182.001; x: 74.2; y: 178.801 } + , PathCubic { control1X: 56.6; control1Y: 175.601; control2X: -7.8; control2Y: 175.601; x: -7.8; y: 175.601 } + ] + } + + ControlledShape { + fillColor: "#b23259" + strokeWidth: -1 + delegate: [PathMove { x: -9.831; y: 206.497 } + , PathCubic { control1X: -6.505; control1Y: 193.707; control2X: -4.921; control2Y: 181.906; x: -7.8; y: 175.601 } + , PathCubic { control1X: -7.8; control1Y: 175.601; control2X: 54.6; control2Y: 182.001; x: 65.8; y: 161.201 } + , PathCubic { control1X: 70.041; control1Y: 153.326; control2X: 84.801; control2Y: 184.001; x: 84.4; y: 193.601 } + , PathCubic { control1X: 84.4; control1Y: 193.601; control2X: 21.4; control2Y: 208.001; x: 6.6; y: 196.801 } + , PathLine { x: -9.831; y: 206.497 } + ] + } + + ControlledShape { + fillColor: "#a5264c" + strokeWidth: -1 + delegate: [PathMove { x: -5.4; y: 222.801 } + , PathCubic { control1X: -5.4; control1Y: 222.801; control2X: -3.4; control2Y: 230.001; x: -5.8; y: 234.001 } + , PathCubic { control1X: -5.8; control1Y: 234.001; control2X: -7.4; control2Y: 234.801; x: -8.6; y: 235.201 } + , PathCubic { control1X: -8.6; control1Y: 235.201; control2X: -7.4; control2Y: 238.801; x: -1.4; y: 240.401 } + , PathCubic { control1X: -1.4; control1Y: 240.401; control2X: 0.6; control2Y: 244.801; x: 3; y: 245.201 } + , PathCubic { control1X: 5.4; control1Y: 245.601; control2X: 10.2; control2Y: 251.201; x: 14.2; y: 250.001 } + , PathCubic { control1X: 18.2; control1Y: 248.801; control2X: 29.4; control2Y: 244.801; x: 29.4; y: 244.801 } + , PathCubic { control1X: 29.4; control1Y: 244.801; control2X: 35; control2Y: 241.601; x: 43.8; y: 245.201 } + , PathCubic { control1X: 43.8; control1Y: 245.201; control2X: 46.175; control2Y: 244.399; x: 46.6; y: 240.401 } + , PathCubic { control1X: 47.1; control1Y: 235.701; control2X: 50.2; control2Y: 232.001; x: 52.2; y: 230.001 } + , PathCubic { control1X: 54.2; control1Y: 228.001; control2X: 63.8; control2Y: 215.201; x: 62.6; y: 214.801 } + , PathCubic { control1X: 61.4; control1Y: 214.401; control2X: -5.4; control2Y: 222.801; x: -5.4; y: 222.801 } + ] + } + + ControlledShape { + fillColor: "#ff727f" + strokeColor: "#000000" + strokeWidth: 1 + delegate: [PathMove { x: -9.8; y: 174.401 } + , PathCubic { control1X: -9.8; control1Y: 174.401; control2X: -12.6; control2Y: 196.801; x: -9.4; y: 205.201 } + , PathCubic { control1X: -6.2; control1Y: 213.601; control2X: -7; control2Y: 215.601; x: -7.8; y: 219.601 } + , PathCubic { control1X: -8.6; control1Y: 223.601; control2X: -4.2; control2Y: 233.601; x: 1.4; y: 239.601 } + , PathLine { x: 13.4; y: 241.201 } + , PathCubic { control1X: 13.4; control1Y: 241.201; control2X: 28.6; control2Y: 237.601; x: 37.8; y: 240.401 } + , PathCubic { control1X: 37.8; control1Y: 240.401; control2X: 46.794; control2Y: 241.744; x: 50.2; y: 226.801 } + , PathCubic { control1X: 50.2; control1Y: 226.801; control2X: 55; control2Y: 220.401; x: 62.2; y: 217.601 } + , PathCubic { control1X: 69.4; control1Y: 214.801; control2X: 76.6; control2Y: 173.201; x: 72.6; y: 165.201 } + , PathCubic { control1X: 68.6; control1Y: 157.201; control2X: 54.2; control2Y: 152.801; x: 38.2; y: 168.401 } + , PathCubic { control1X: 22.2; control1Y: 184.001; control2X: 20.2; control2Y: 167.201; x: -9.8; y: 174.401 } + ] + } + + ControlledShape { + fillColor: "#ffffcc" + strokeColor: "#000000" + strokeWidth: 0.5 + delegate: [PathMove { x: -8.2; y: 249.201 } + , PathCubic { control1X: -8.2; control1Y: 249.201; control2X: -9; control2Y: 247.201; x: -13.4; y: 246.801 } + , PathCubic { control1X: -13.4; control1Y: 246.801; control2X: -35.8; control2Y: 243.201; x: -44.2; y: 230.801 } + , PathCubic { control1X: -44.2; control1Y: 230.801; control2X: -51; control2Y: 225.201; x: -46.6; y: 236.801 } + , PathCubic { control1X: -46.6; control1Y: 236.801; control2X: -36.2; control2Y: 257.201; x: -29.4; y: 260.001 } + , PathCubic { control1X: -29.4; control1Y: 260.001; control2X: -13; control2Y: 264.001; x: -8.2; y: 249.201 } + ] + } + + ControlledShape { + fillColor: "#cc3f4c" + strokeWidth: -1 + delegate: [PathMove { x: 71.742; y: 185.229 } + , PathCubic { control1X: 72.401; control1Y: 177.323; control2X: 74.354; control2Y: 168.709; x: 72.6; y: 165.201 } + , PathCubic { control1X: 66.154; control1Y: 152.307; control2X: 49.181; control2Y: 157.695; x: 38.2; y: 168.401 } + , PathCubic { control1X: 22.2; control1Y: 184.001; control2X: 20.2; control2Y: 167.201; x: -9.8; y: 174.401 } + , PathCubic { control1X: -9.8; control1Y: 174.401; control2X: -11.545; control2Y: 188.364; x: -10.705; y: 198.376 } + , PathCubic { control1X: -10.705; control1Y: 198.376; control2X: 26.6; control2Y: 186.801; x: 27.4; y: 192.401 } + , PathCubic { control1X: 27.4; control1Y: 192.401; control2X: 29; control2Y: 189.201; x: 38.2; y: 189.201 } + , PathCubic { control1X: 47.4; control1Y: 189.201; control2X: 70.142; control2Y: 188.029; x: 71.742; y: 185.229 } + ] + } + + ControlledShape { + fillColor: "transparent" + strokeColor: "#a51926" + strokeWidth: 2 + delegate: [PathMove { x: 28.6; y: 175.201 } + , PathCubic { control1X: 28.6; control1Y: 175.201; control2X: 33.4; control2Y: 180.001; x: 29.8; y: 189.601 } + , PathCubic { control1X: 29.8; control1Y: 189.601; control2X: 15.4; control2Y: 205.601; x: 17.4; y: 219.601 } + ] + } + + ControlledShape { + fillColor: "#ffffcc" + strokeColor: "#000000" + strokeWidth: 0.5 + delegate: [PathMove { x: -19.4; y: 260.001 } + , PathCubic { control1X: -19.4; control1Y: 260.001; control2X: -23.8; control2Y: 247.201; x: -15; y: 254.001 } + , PathCubic { control1X: -15; control1Y: 254.001; control2X: -10.2; control2Y: 256.001; x: -11.4; y: 257.601 } + , PathCubic { control1X: -12.6; control1Y: 259.201; control2X: -18.2; control2Y: 263.201; x: -19.4; y: 260.001 } + ] + } + + ControlledShape { + fillColor: "#ffffcc" + strokeColor: "#000000" + strokeWidth: 0.5 + delegate: [PathMove { x: -14.36; y: 261.201 } + , PathCubic { control1X: -14.36; control1Y: 261.201; control2X: -17.88; control2Y: 250.961; x: -10.84; y: 256.401 } + , PathCubic { control1X: -10.84; control1Y: 256.401; control2X: -6.419; control2Y: 258.849; x: -7.96; y: 259.281 } + , PathCubic { control1X: -12.52; control1Y: 260.561; control2X: -7.96; control2Y: 263.121; x: -14.36; y: 261.201 } + ] + } + + ControlledShape { + fillColor: "#ffffcc" + strokeColor: "#000000" + strokeWidth: 0.5 + delegate: [PathMove { x: -9.56; y: 261.201 } + , PathCubic { control1X: -9.56; control1Y: 261.201; control2X: -13.08; control2Y: 250.961; x: -6.04; y: 256.401 } + , PathCubic { control1X: -6.04; control1Y: 256.401; control2X: -1.665; control2Y: 258.711; x: -3.16; y: 259.281 } + , PathCubic { control1X: -6.52; control1Y: 260.561; control2X: -3.16; control2Y: 263.121; x: -9.56; y: 261.201 } + ] + } + + ControlledShape { + fillColor: "#ffffcc" + strokeColor: "#000000" + strokeWidth: 0.5 + delegate: [PathMove { x: -2.96; y: 261.401 } + , PathCubic { control1X: -2.96; control1Y: 261.401; control2X: -6.48; control2Y: 251.161; x: 0.56; y: 256.601 } + , PathCubic { control1X: 0.56; control1Y: 256.601; control2X: 4.943; control2Y: 258.933; x: 3.441; y: 259.481 } + , PathCubic { control1X: 0.48; control1Y: 260.561; control2X: 3.441; control2Y: 263.321; x: -2.96; y: 261.401 } + ] + } + + ControlledShape { + fillColor: "#ffffcc" + strokeColor: "#000000" + strokeWidth: 0.5 + delegate: [PathMove { x: 3.52; y: 261.321 } + , PathCubic { control1X: 3.52; control1Y: 261.321; control2X: 0; control2Y: 251.081; x: 7.041; y: 256.521 } + , PathCubic { control1X: 7.041; control1Y: 256.521; control2X: 10.881; control2Y: 258.121; x: 9.921; y: 259.401 } + , PathCubic { control1X: 8.961; control1Y: 260.681; control2X: 9.921; control2Y: 263.241; x: 3.52; y: 261.321 } + ] + } + + ControlledShape { + fillColor: "#ffffcc" + strokeColor: "#000000" + strokeWidth: 0.5 + delegate: [PathMove { x: 10.2; y: 262.001 } + , PathCubic { control1X: 10.2; control1Y: 262.001; control2X: 5.4; control2Y: 249.601; x: 14.6; y: 256.001 } + , PathCubic { control1X: 14.6; control1Y: 256.001; control2X: 19.4; control2Y: 258.001; x: 18.2; y: 259.601 } + , PathCubic { control1X: 17; control1Y: 261.201; control2X: 18.2; control2Y: 264.401; x: 10.2; y: 262.001 } + ] + } + + ControlledShape { + fillColor: "transparent" + strokeColor: "#a5264c" + strokeWidth: 2 + delegate: [PathMove { x: -18.2; y: 244.801 } + , PathCubic { control1X: -18.2; control1Y: 244.801; control2X: -5; control2Y: 242.001; x: 1; y: 245.201 } + , PathCubic { control1X: 1; control1Y: 245.201; control2X: 7; control2Y: 246.401; x: 8.2; y: 246.001 } + , PathCubic { control1X: 9.4; control1Y: 245.601; control2X: 12.6; control2Y: 245.201; x: 12.6; y: 245.201 } + ] + } + + ControlledShape { + fillColor: "transparent" + strokeColor: "#a5264c" + strokeWidth: 2 + delegate: [PathMove { x: 15.8; y: 253.601 } + , PathCubic { control1X: 15.8; control1Y: 253.601; control2X: 27.8; control2Y: 240.001; x: 39.8; y: 244.401 } + , PathCubic { control1X: 46.816; control1Y: 246.974; control2X: 45.8; control2Y: 243.601; x: 46.6; y: 240.801 } + , PathCubic { control1X: 47.4; control1Y: 238.001; control2X: 47.6; control2Y: 233.801; x: 52.6; y: 230.801 } + ] + } + + ControlledShape { + fillColor: "#ffffcc" + strokeColor: "#000000" + strokeWidth: 0.5 + delegate: [PathMove { x: 33; y: 237.601 } + , PathCubic { control1X: 33; control1Y: 237.601; control2X: 29; control2Y: 226.801; x: 26.2; y: 239.601 } + , PathCubic { control1X: 23.4; control1Y: 252.401; control2X: 20.2; control2Y: 256.001; x: 18.6; y: 258.801 } + , PathCubic { control1X: 18.6; control1Y: 258.801; control2X: 18.6; control2Y: 264.001; x: 27; y: 263.601 } + , PathCubic { control1X: 27; control1Y: 263.601; control2X: 37.8; control2Y: 263.201; x: 38.2; y: 260.401 } + , PathCubic { control1X: 38.6; control1Y: 257.601; control2X: 37; control2Y: 246.001; x: 33; y: 237.601 } + ] + } + + ControlledShape { + fillColor: "transparent" + strokeColor: "#a5264c" + strokeWidth: 2 + delegate: [PathMove { x: 47; y: 244.801 } + , PathCubic { control1X: 47; control1Y: 244.801; control2X: 50.6; control2Y: 242.401; x: 53; y: 243.601 } + ] + } + + ControlledShape { + fillColor: "transparent" + strokeColor: "#a5264c" + strokeWidth: 2 + delegate: [PathMove { x: 53.5; y: 228.401 } + , PathCubic { control1X: 53.5; control1Y: 228.401; control2X: 56.4; control2Y: 223.501; x: 61.2; y: 222.701 } + ] + } + + ControlledShape { + fillColor: "#b2b2b2" + strokeWidth: -1 + delegate: [PathMove { x: -25.8; y: 265.201 } + , PathCubic { control1X: -25.8; control1Y: 265.201; control2X: -7.8; control2Y: 268.401; x: -3.4; y: 266.801 } + , PathCubic { control1X: -3.4; control1Y: 266.801; control2X: 5.4; control2Y: 266.801; x: -3; y: 268.801 } + , PathCubic { control1X: -3; control1Y: 268.801; control2X: -15.8; control2Y: 268.801; x: -23.8; y: 267.601 } + , PathCubic { control1X: -23.8; control1Y: 267.601; control2X: -35.4; control2Y: 262.001; x: -25.8; y: 265.201 } + ] + } + + ControlledShape { + fillColor: "#ffffcc" + strokeColor: "#000000" + strokeWidth: 0.5 + delegate: [PathMove { x: -11.8; y: 172.001 } + , PathCubic { control1X: -11.8; control1Y: 172.001; control2X: 5.8; control2Y: 172.001; x: 7.8; y: 172.801 } + , PathCubic { control1X: 7.8; control1Y: 172.801; control2X: 15; control2Y: 203.601; x: 11.4; y: 211.201 } + , PathCubic { control1X: 11.4; control1Y: 211.201; control2X: 10.2; control2Y: 214.001; x: 7.4; y: 208.401 } + , PathCubic { control1X: 7.4; control1Y: 208.401; control2X: -11; control2Y: 175.601; x: -14.2; y: 173.601 } + , PathCubic { control1X: -17.4; control1Y: 171.601; control2X: -13; control2Y: 172.001; x: -11.8; y: 172.001 } + ] + } + + ControlledShape { + fillColor: "#ffffcc" + strokeColor: "#000000" + strokeWidth: 0.5 + delegate: [PathMove { x: -88.9; y: 169.301 } + , PathCubic { control1X: -88.9; control1Y: 169.301; control2X: -80; control2Y: 171.001; x: -67.4; y: 173.601 } + , PathCubic { control1X: -67.4; control1Y: 173.601; control2X: -62.6; control2Y: 196.001; x: -59.4; y: 200.801 } + , PathCubic { control1X: -56.2; control1Y: 205.601; control2X: -59.8; control2Y: 205.601; x: -63.4; y: 202.801 } + , PathCubic { control1X: -67; control1Y: 200.001; control2X: -81.8; control2Y: 186.001; x: -83.8; y: 181.601 } + , PathCubic { control1X: -85.8; control1Y: 177.201; control2X: -88.9; control2Y: 169.301; x: -88.9; y: 169.301 } + ] + } + + ControlledShape { + fillColor: "#ffffcc" + strokeColor: "#000000" + strokeWidth: 0.5 + delegate: [PathMove { x: -67.039; y: 173.818 } + , PathCubic { control1X: -67.039; control1Y: 173.818; control2X: -61.239; control2Y: 175.366; x: -60.23; y: 177.581 } + , PathCubic { control1X: -59.222; control1Y: 179.795; control2X: -61.432; control2Y: 183.092; x: -61.432; y: 183.092 } + , PathCubic { control1X: -61.432; control1Y: 183.092; control2X: -62.432; control2Y: 186.397; x: -63.634; y: 184.235 } + , PathCubic { control1X: -64.836; control1Y: 182.072; control2X: -67.708; control2Y: 174.412; x: -67.039; y: 173.818 } + ] + } + + ControlledShape { + fillColor: "#000000" + strokeWidth: -1 + delegate: [PathMove { x: -67; y: 173.601 } + , PathCubic { control1X: -67; control1Y: 173.601; control2X: -63.4; control2Y: 178.801; x: -59.8; y: 178.801 } + , PathCubic { control1X: -56.2; control1Y: 178.801; control2X: -55.818; control2Y: 178.388; x: -53; y: 179.001 } + , PathCubic { control1X: -48.4; control1Y: 180.001; control2X: -48.8; control2Y: 178.001; x: -42.2; y: 179.201 } + , PathCubic { control1X: -39.56; control1Y: 179.681; control2X: -37; control2Y: 178.801; x: -34.2; y: 180.001 } + , PathCubic { control1X: -31.4; control1Y: 181.201; control2X: -28.2; control2Y: 180.401; x: -27; y: 178.401 } + , PathCubic { control1X: -25.8; control1Y: 176.401; control2X: -21; control2Y: 172.201; x: -21; y: 172.201 } + , PathCubic { control1X: -21; control1Y: 172.201; control2X: -33.8; control2Y: 174.001; x: -36.6; y: 174.801 } + , PathCubic { control1X: -36.6; control1Y: 174.801; control2X: -59; control2Y: 176.001; x: -67; y: 173.601 } + ] + } + + ControlledShape { + fillColor: "#ffffcc" + strokeColor: "#000000" + strokeWidth: 0.5 + delegate: [PathMove { x: -22.4; y: 173.801 } + , PathCubic { control1X: -22.4; control1Y: 173.801; control2X: -28.85; control2Y: 177.301; x: -29.25; y: 179.701 } + , PathCubic { control1X: -29.65; control1Y: 182.101; control2X: -24; control2Y: 185.801; x: -24; y: 185.801 } + , PathCubic { control1X: -24; control1Y: 185.801; control2X: -21.25; control2Y: 190.401; x: -20.65; y: 188.001 } + , PathCubic { control1X: -20.05; control1Y: 185.601; control2X: -21.6; control2Y: 174.201; x: -22.4; y: 173.801 } + ] + } + + ControlledShape { + fillColor: "#ffffcc" + strokeColor: "#000000" + strokeWidth: 0.5 + delegate: [PathMove { x: -59.885; y: 179.265 } + , PathCubic { control1X: -59.885; control1Y: 179.265; control2X: -52.878; control2Y: 190.453; x: -52.661; y: 179.242 } + , PathCubic { control1X: -52.661; control1Y: 179.242; control2X: -52.104; control2Y: 177.984; x: -53.864; y: 177.962 } + , PathCubic { control1X: -59.939; control1Y: 177.886; control2X: -58.418; control2Y: 173.784; x: -59.885; y: 179.265 } + ] + } + + ControlledShape { + fillColor: "#ffffcc" + strokeColor: "#000000" + strokeWidth: 0.5 + delegate: [PathMove { x: -52.707; y: 179.514 } + , PathCubic { control1X: -52.707; control1Y: 179.514; control2X: -44.786; control2Y: 190.701; x: -45.422; y: 179.421 } + , PathCubic { control1X: -45.422; control1Y: 179.421; control2X: -45.415; control2Y: 179.089; x: -47.168; y: 178.936 } + , PathCubic { control1X: -51.915; control1Y: 178.522; control2X: -51.57; control2Y: 174.004; x: -52.707; y: 179.514 } + ] + } + + ControlledShape { + fillColor: "#ffffcc" + strokeColor: "#000000" + strokeWidth: 0.5 + delegate: [PathMove { x: -45.494; y: 179.522 } + , PathCubic { control1X: -45.494; control1Y: 179.522; control2X: -37.534; control2Y: 190.15; x: -38.203; y: 180.484 } + , PathCubic { control1X: -38.203; control1Y: 180.484; control2X: -38.084; control2Y: 179.251; x: -39.738; y: 178.95 } + , PathCubic { control1X: -43.63; control1Y: 178.244; control2X: -43.841; control2Y: 174.995; x: -45.494; y: 179.522 } + ] + } + + ControlledShape { + fillColor: "#ffffcc" + strokeColor: "#000000" + strokeWidth: 0.5 + delegate: [PathMove { x: -38.618; y: 179.602 } + , PathCubic { control1X: -38.618; control1Y: 179.602; control2X: -30.718; control2Y: 191.163; x: -30.37; y: 181.382 } + , PathCubic { control1X: -30.37; control1Y: 181.382; control2X: -28.726; control2Y: 180.004; x: -30.472; y: 179.782 } + , PathCubic { control1X: -36.29; control1Y: 179.042; control2X: -35.492; control2Y: 174.588; x: -38.618; y: 179.602 } + ] + } + + ControlledShape { + fillColor: "#e5e5b2" + strokeWidth: -1 + delegate: [PathMove { x: -74.792; y: 183.132 } + , PathLine { x: -82.45; y: 181.601 } + , PathCubic { control1X: -85.05; control1Y: 176.601; control2X: -87.15; control2Y: 170.451; x: -87.15; y: 170.451 } + , PathCubic { control1X: -87.15; control1Y: 170.451; control2X: -80.8; control2Y: 171.451; x: -68.3; y: 174.251 } + , PathCubic { control1X: -68.3; control1Y: 174.251; control2X: -67.424; control2Y: 177.569; x: -65.952; y: 183.364 } + , PathLine { x: -74.792; y: 183.132 } + ] + } + + ControlledShape { + fillColor: "#e5e5b2" + strokeWidth: -1 + delegate: [PathMove { x: -9.724; y: 178.47 } + , PathCubic { control1X: -11.39; control1Y: 175.964; control2X: -12.707; control2Y: 174.206; x: -13.357; y: 173.8 } + , PathCubic { control1X: -16.37; control1Y: 171.917; control2X: -12.227; control2Y: 172.294; x: -11.098; y: 172.294 } + , PathCubic { control1X: -11.098; control1Y: 172.294; control2X: 5.473; control2Y: 172.294; x: 7.356; y: 173.047 } + , PathCubic { control1X: 7.356; control1Y: 173.047; control2X: 7.88; control2Y: 175.289; x: 8.564; y: 178.68 } + , PathCubic { control1X: 8.564; control1Y: 178.68; control2X: -1.524; control2Y: 176.67; x: -9.724; y: 178.47 } + ] + } + + ControlledShape { + fillColor: "#cc7226" + strokeWidth: -1 + delegate: [PathMove { x: 43.88; y: 40.321 } + , PathCubic { control1X: 71.601; control1Y: 44.281; control2X: 97.121; control2Y: 8.641; x: 98.881; y: -1.04 } + , PathCubic { control1X: 100.641; control1Y: -10.72; control2X: 90.521; control2Y: -22.6; x: 90.521; y: -22.6 } + , PathCubic { control1X: 91.841; control1Y: -25.68; control2X: 87.001; control2Y: -39.76; x: 81.721; y: -49 } + , PathCubic { control1X: 76.441; control1Y: -58.24; control2X: 60.54; control2Y: -57.266; x: 43; y: -58.24 } + , PathCubic { control1X: 27.16; control1Y: -59.12; control2X: 8.68; control2Y: -35.8; x: 7.36; y: -34.04 } + , PathCubic { control1X: 6.04; control1Y: -32.28; control2X: 12.2; control2Y: 6.001; x: 13.52; y: 11.721 } + , PathCubic { control1X: 14.84; control1Y: 17.441; control2X: 12.2; control2Y: 43.841; x: 12.2; y: 43.841 } + , PathCubic { control1X: 46.44; control1Y: 34.741; control2X: 16.16; control2Y: 36.361; x: 43.88; y: 40.321 } + ] + } + + ControlledShape { + fillColor: "#ea8e51" + strokeWidth: -1 + delegate: [PathMove { x: 8.088; y: -33.392 } + , PathCubic { control1X: 6.792; control1Y: -31.664; control2X: 12.84; control2Y: 5.921; x: 14.136; y: 11.537 } + , PathCubic { control1X: 15.432; control1Y: 17.153; control2X: 12.84; control2Y: 43.073; x: 12.84; y: 43.073 } + , PathCubic { control1X: 45.512; control1Y: 34.193; control2X: 16.728; control2Y: 35.729; x: 43.944; y: 39.617 } + , PathCubic { control1X: 71.161; control1Y: 43.505; control2X: 96.217; control2Y: 8.513; x: 97.945; y: -0.992 } + , PathCubic { control1X: 99.673; control1Y: -10.496; control2X: 89.737; control2Y: -22.16; x: 89.737; y: -22.16 } + , PathCubic { control1X: 91.033; control1Y: -25.184; control2X: 86.281; control2Y: -39.008; x: 81.097; y: -48.08 } + , PathCubic { control1X: 75.913; control1Y: -57.152; control2X: 60.302; control2Y: -56.195; x: 43.08; y: -57.152 } + , PathCubic { control1X: 27.528; control1Y: -58.016; control2X: 9.384; control2Y: -35.12; x: 8.088; y: -33.392 } + ] + } + + ControlledShape { + fillColor: "#efaa7c" + strokeWidth: -1 + delegate: [PathMove { x: 8.816; y: -32.744 } + , PathCubic { control1X: 7.544; control1Y: -31.048; control2X: 13.48; control2Y: 5.841; x: 14.752; y: 11.353 } + , PathCubic { control1X: 16.024; control1Y: 16.865; control2X: 13.48; control2Y: 42.305; x: 13.48; y: 42.305 } + , PathCubic { control1X: 44.884; control1Y: 33.145; control2X: 17.296; control2Y: 35.097; x: 44.008; y: 38.913 } + , PathCubic { control1X: 70.721; control1Y: 42.729; control2X: 95.313; control2Y: 8.385; x: 97.009; y: -0.944 } + , PathCubic { control1X: 98.705; control1Y: -10.272; control2X: 88.953; control2Y: -21.72; x: 88.953; y: -21.72 } + , PathCubic { control1X: 90.225; control1Y: -24.688; control2X: 85.561; control2Y: -38.256; x: 80.473; y: -47.16 } + , PathCubic { control1X: 75.385; control1Y: -56.064; control2X: 60.063; control2Y: -55.125; x: 43.16; y: -56.064 } + , PathCubic { control1X: 27.896; control1Y: -56.912; control2X: 10.088; control2Y: -34.44; x: 8.816; y: -32.744 } + ] + } + + ControlledShape { + fillColor: "#f4c6a8" + strokeWidth: -1 + delegate: [PathMove { x: 9.544; y: -32.096 } + , PathCubic { control1X: 8.296; control1Y: -30.432; control2X: 14.12; control2Y: 5.761; x: 15.368; y: 11.169 } + , PathCubic { control1X: 16.616; control1Y: 16.577; control2X: 14.12; control2Y: 41.537; x: 14.12; y: 41.537 } + , PathCubic { control1X: 43.556; control1Y: 32.497; control2X: 17.864; control2Y: 34.465; x: 44.072; y: 38.209 } + , PathCubic { control1X: 70.281; control1Y: 41.953; control2X: 94.409; control2Y: 8.257; x: 96.073; y: -0.895 } + , PathCubic { control1X: 97.737; control1Y: -10.048; control2X: 88.169; control2Y: -21.28; x: 88.169; y: -21.28 } + , PathCubic { control1X: 89.417; control1Y: -24.192; control2X: 84.841; control2Y: -37.504; x: 79.849; y: -46.24 } + , PathCubic { control1X: 74.857; control1Y: -54.976; control2X: 59.824; control2Y: -54.055; x: 43.24; y: -54.976 } + , PathCubic { control1X: 28.264; control1Y: -55.808; control2X: 10.792; control2Y: -33.76; x: 9.544; y: -32.096 } + ] + } + + ControlledShape { + fillColor: "#f9e2d3" + strokeWidth: -1 + delegate: [PathMove { x: 10.272; y: -31.448 } + , PathCubic { control1X: 9.048; control1Y: -29.816; control2X: 14.76; control2Y: 5.681; x: 15.984; y: 10.985 } + , PathCubic { control1X: 17.208; control1Y: 16.289; control2X: 14.76; control2Y: 40.769; x: 14.76; y: 40.769 } + , PathCubic { control1X: 42.628; control1Y: 31.849; control2X: 18.432; control2Y: 33.833; x: 44.136; y: 37.505 } + , PathCubic { control1X: 69.841; control1Y: 41.177; control2X: 93.505; control2Y: 8.129; x: 95.137; y: -0.848 } + , PathCubic { control1X: 96.769; control1Y: -9.824; control2X: 87.385; control2Y: -20.84; x: 87.385; y: -20.84 } + , PathCubic { control1X: 88.609; control1Y: -23.696; control2X: 84.121; control2Y: -36.752; x: 79.225; y: -45.32 } + , PathCubic { control1X: 74.329; control1Y: -53.888; control2X: 59.585; control2Y: -52.985; x: 43.32; y: -53.888 } + , PathCubic { control1X: 28.632; control1Y: -54.704; control2X: 11.496; control2Y: -33.08; x: 10.272; y: -31.448 } + ] + } + + ControlledShape { + fillColor: "#ffffff" + strokeWidth: -1 + delegate: [PathMove { x: 44.2; y: 36.8 } + , PathCubic { control1X: 69.4; control1Y: 40.4; control2X: 92.601; control2Y: 8; x: 94.201; y: -0.8 } + , PathCubic { control1X: 95.801; control1Y: -9.6; control2X: 86.601; control2Y: -20.4; x: 86.601; y: -20.4 } + , PathCubic { control1X: 87.801; control1Y: -23.2; control2X: 83.4; control2Y: -36; x: 78.6; y: -44.4 } + , PathCubic { control1X: 73.8; control1Y: -52.8; control2X: 59.346; control2Y: -51.914; x: 43.4; y: -52.8 } + , PathCubic { control1X: 29; control1Y: -53.6; control2X: 12.2; control2Y: -32.4; x: 11; y: -30.8 } + , PathCubic { control1X: 9.8; control1Y: -29.2; control2X: 15.4; control2Y: 5.6; x: 16.6; y: 10.8 } + , PathCubic { control1X: 17.8; control1Y: 16; control2X: 15.4; control2Y: 40; x: 15.4; y: 40 } + , PathCubic { control1X: 40.9; control1Y: 31.4; control2X: 19; control2Y: 33.2; x: 44.2; y: 36.8 } + ] + } + + ControlledShape { + fillColor: "#cccccc" + strokeWidth: -1 + delegate: [PathMove { x: 90.601; y: 2.8 } + , PathCubic { control1X: 90.601; control1Y: 2.8; control2X: 62.8; control2Y: 10.4; x: 51.2; y: 8.8 } + , PathCubic { control1X: 51.2; control1Y: 8.8; control2X: 35.4; control2Y: 2.2; x: 26.6; y: 24 } + , PathCubic { control1X: 26.6; control1Y: 24; control2X: 23; control2Y: 31.2; x: 21; y: 33.2 } + , PathCubic { control1X: 19; control1Y: 35.2; control2X: 90.601; control2Y: 2.8; x: 90.601; y: 2.8 } + ] + } + + ControlledShape { + fillColor: "#000000" + strokeWidth: -1 + delegate: [PathMove { x: 94.401; y: 0.6 } + , PathCubic { control1X: 94.401; control1Y: 0.6; control2X: 65.4; control2Y: 12.8; x: 55.4; y: 12.4 } + , PathCubic { control1X: 55.4; control1Y: 12.4; control2X: 39; control2Y: 7.8; x: 30.6; y: 22.4 } + , PathCubic { control1X: 30.6; control1Y: 22.4; control2X: 22.2; control2Y: 31.6; x: 19; y: 33.2 } + , PathCubic { control1X: 19; control1Y: 33.2; control2X: 18.6; control2Y: 34.8; x: 25; y: 30.8 } + , PathLine { x: 35.4; y: 36 } + , PathCubic { control1X: 35.4; control1Y: 36; control2X: 50.2; control2Y: 45.6; x: 59.8; y: 29.6 } + , PathCubic { control1X: 59.8; control1Y: 29.6; control2X: 63.8; control2Y: 18.4; x: 63.8; y: 16.4 } + , PathCubic { control1X: 63.8; control1Y: 14.4; control2X: 85; control2Y: 8.8; x: 86.601; y: 8.4 } + , PathCubic { control1X: 88.201; control1Y: 8; control2X: 94.801; control2Y: 3.8; x: 94.401; y: 0.6 } + ] + } + + ControlledShape { + fillColor: "#99cc32" + strokeWidth: -1 + delegate: [PathMove { x: 47; y: 36.514 } + , PathCubic { control1X: 40.128; control1Y: 36.514; control2X: 31.755; control2Y: 32.649; x: 31.755; y: 26.4 } + , PathCubic { control1X: 31.755; control1Y: 20.152; control2X: 40.128; control2Y: 13.887; x: 47; y: 13.887 } + , PathCubic { control1X: 53.874; control1Y: 13.887; control2X: 59.446; control2Y: 18.952; x: 59.446; y: 25.2 } + , PathCubic { control1X: 59.446; control1Y: 31.449; control2X: 53.874; control2Y: 36.514; x: 47; y: 36.514 } + ] + } + + ControlledShape { + fillColor: "#659900" + strokeWidth: -1 + delegate: [PathMove { x: 43.377; y: 19.83 } + , PathCubic { control1X: 38.531; control1Y: 20.552; control2X: 33.442; control2Y: 22.055; x: 33.514; y: 21.839 } + , PathCubic { control1X: 35.054; control1Y: 17.22; control2X: 41.415; control2Y: 13.887; x: 47; y: 13.887 } + , PathCubic { control1X: 51.296; control1Y: 13.887; control2X: 55.084; control2Y: 15.865; x: 57.32; y: 18.875 } + , PathCubic { control1X: 57.32; control1Y: 18.875; control2X: 52.004; control2Y: 18.545; x: 43.377; y: 19.83 } + ] + } + + ControlledShape { + fillColor: "#ffffff" + strokeWidth: -1 + delegate: [PathMove { x: 55.4; y: 19.6 } + , PathCubic { control1X: 55.4; control1Y: 19.6; control2X: 51; control2Y: 16.4; x: 51; y: 18.6 } + , PathCubic { control1X: 51; control1Y: 18.6; control2X: 54.6; control2Y: 23; x: 55.4; y: 19.6 } + ] + } + + ControlledShape { + fillColor: "#000000" + strokeWidth: -1 + delegate: [PathMove { x: 45.4; y: 27.726 } + , PathCubic { control1X: 42.901; control1Y: 27.726; control2X: 40.875; control2Y: 25.7; x: 40.875; y: 23.2 } + , PathCubic { control1X: 40.875; control1Y: 20.701; control2X: 42.901; control2Y: 18.675; x: 45.4; y: 18.675 } + , PathCubic { control1X: 47.9; control1Y: 18.675; control2X: 49.926; control2Y: 20.701; x: 49.926; y: 23.2 } + , PathCubic { control1X: 49.926; control1Y: 25.7; control2X: 47.9; control2Y: 27.726; x: 45.4; y: 27.726 } + ] + } + + ControlledShape { + fillColor: "#cc7226" + strokeWidth: -1 + delegate: [PathMove { x: -58.6; y: 14.4 } + , PathCubic { control1X: -58.6; control1Y: 14.4; control2X: -61.8; control2Y: -6.8; x: -59.4; y: -11.2 } + , PathCubic { control1X: -59.4; control1Y: -11.2; control2X: -48.6; control2Y: -21.2; x: -49; y: -24.8 } + , PathCubic { control1X: -49; control1Y: -24.8; control2X: -49.4; control2Y: -42.8; x: -50.6; y: -43.6 } + , PathCubic { control1X: -51.8; control1Y: -44.4; control2X: -59.4; control2Y: -50.4; x: -65.4; y: -44 } + , PathCubic { control1X: -65.4; control1Y: -44; control2X: -75.8; control2Y: -26; x: -75; y: -19.6 } + , PathLine { x: -75; y: -17.6 } + , PathCubic { control1X: -75; control1Y: -17.6; control2X: -82.6; control2Y: -18; x: -84.2; y: -16 } + , PathCubic { control1X: -84.2; control1Y: -16; control2X: -85.4; control2Y: -10.8; x: -86.6; y: -10.4 } + , PathCubic { control1X: -86.6; control1Y: -10.4; control2X: -89.4; control2Y: -8; x: -87.4; y: -5.2 } + , PathCubic { control1X: -87.4; control1Y: -5.2; control2X: -89.4; control2Y: -2.8; x: -89; y: 1.2 } + , PathLine { x: -81.4; y: 5.2 } + , PathCubic { control1X: -81.4; control1Y: 5.2; control2X: -79.4; control2Y: 19.6; x: -68.6; y: 24.8 } + , PathCubic { control1X: -63.764; control1Y: 27.129; control2X: -60.6; control2Y: 20.4; x: -58.6; y: 14.4 } + ] + } + + ControlledShape { + fillColor: "#ffffff" + strokeWidth: -1 + delegate: [PathMove { x: -59.6; y: 12.56 } + , PathCubic { control1X: -59.6; control1Y: 12.56; control2X: -62.48; control2Y: -6.52; x: -60.32; y: -10.48 } + , PathCubic { control1X: -60.32; control1Y: -10.48; control2X: -50.6; control2Y: -19.48; x: -50.96; y: -22.72 } + , PathCubic { control1X: -50.96; control1Y: -22.72; control2X: -51.32; control2Y: -38.92; x: -52.4; y: -39.64 } + , PathCubic { control1X: -53.48; control1Y: -40.36; control2X: -60.32; control2Y: -45.76; x: -65.72; y: -40 } + , PathCubic { control1X: -65.72; control1Y: -40; control2X: -75.08; control2Y: -23.8; x: -74.36; y: -18.04 } + , PathLine { x: -74.36; y: -16.24 } + , PathCubic { control1X: -74.36; control1Y: -16.24; control2X: -81.2; control2Y: -16.6; x: -82.64; y: -14.8 } + , PathCubic { control1X: -82.64; control1Y: -14.8; control2X: -83.72; control2Y: -10.12; x: -84.8; y: -9.76 } + , PathCubic { control1X: -84.8; control1Y: -9.76; control2X: -87.32; control2Y: -7.6; x: -85.52; y: -5.08 } + , PathCubic { control1X: -85.52; control1Y: -5.08; control2X: -87.32; control2Y: -2.92; x: -86.96; y: 0.68 } + , PathLine { x: -80.12; y: 4.28 } + , PathCubic { control1X: -80.12; control1Y: 4.28; control2X: -78.32; control2Y: 17.24; x: -68.6; y: 21.92 } + , PathCubic { control1X: -64.248; control1Y: 24.015; control2X: -61.4; control2Y: 17.96; x: -59.6; y: 12.56 } + ] + } + + ControlledShape { + fillColor: "#eb955c" + strokeWidth: -1 + delegate: [PathMove { x: -51.05; y: -42.61 } + , PathCubic { control1X: -52.14; control1Y: -43.47; control2X: -59.63; control2Y: -49.24; x: -65.48; y: -43 } + , PathCubic { control1X: -65.48; control1Y: -43; control2X: -75.62; control2Y: -25.45; x: -74.84; y: -19.21 } + , PathLine { x: -74.84; y: -17.26 } + , PathCubic { control1X: -74.84; control1Y: -17.26; control2X: -82.25; control2Y: -17.65; x: -83.81; y: -15.7 } + , PathCubic { control1X: -83.81; control1Y: -15.7; control2X: -84.98; control2Y: -10.63; x: -86.15; y: -10.24 } + , PathCubic { control1X: -86.15; control1Y: -10.24; control2X: -88.88; control2Y: -7.9; x: -86.93; y: -5.17 } + , PathCubic { control1X: -86.93; control1Y: -5.17; control2X: -88.88; control2Y: -2.83; x: -88.49; y: 1.07 } + , PathLine { x: -81.08; y: 4.97 } + , PathCubic { control1X: -81.08; control1Y: 4.97; control2X: -79.13; control2Y: 19.01; x: -68.6; y: 24.08 } + , PathCubic { control1X: -63.886; control1Y: 26.35; control2X: -60.8; control2Y: 19.79; x: -58.85; y: 13.94 } + , PathCubic { control1X: -58.85; control1Y: 13.94; control2X: -61.97; control2Y: -6.73; x: -59.63; y: -11.02 } + , PathCubic { control1X: -59.63; control1Y: -11.02; control2X: -49.1; control2Y: -20.77; x: -49.49; y: -24.28 } + , PathCubic { control1X: -49.49; control1Y: -24.28; control2X: -49.88; control2Y: -41.83; x: -51.05; y: -42.61 } + ] + } + + ControlledShape { + fillColor: "#f2b892" + strokeWidth: -1 + delegate: [PathMove { x: -51.5; y: -41.62 } + , PathCubic { control1X: -52.48; control1Y: -42.54; control2X: -59.86; control2Y: -48.08; x: -65.56; y: -42 } + , PathCubic { control1X: -65.56; control1Y: -42; control2X: -75.44; control2Y: -24.9; x: -74.68; y: -18.82 } + , PathLine { x: -74.68; y: -16.92 } + , PathCubic { control1X: -74.68; control1Y: -16.92; control2X: -81.9; control2Y: -17.3; x: -83.42; y: -15.4 } + , PathCubic { control1X: -83.42; control1Y: -15.4; control2X: -84.56; control2Y: -10.46; x: -85.7; y: -10.08 } + , PathCubic { control1X: -85.7; control1Y: -10.08; control2X: -88.36; control2Y: -7.8; x: -86.46; y: -5.14 } + , PathCubic { control1X: -86.46; control1Y: -5.14; control2X: -88.36; control2Y: -2.86; x: -87.98; y: 0.94 } + , PathLine { x: -80.76; y: 4.74 } + , PathCubic { control1X: -80.76; control1Y: 4.74; control2X: -78.86; control2Y: 18.42; x: -68.6; y: 23.36 } + , PathCubic { control1X: -64.006; control1Y: 25.572; control2X: -61; control2Y: 19.18; x: -59.1; y: 13.48 } + , PathCubic { control1X: -59.1; control1Y: 13.48; control2X: -62.14; control2Y: -6.66; x: -59.86; y: -10.84 } + , PathCubic { control1X: -59.86; control1Y: -10.84; control2X: -49.6; control2Y: -20.34; x: -49.98; y: -23.76 } + , PathCubic { control1X: -49.98; control1Y: -23.76; control2X: -50.36; control2Y: -40.86; x: -51.5; y: -41.62 } + ] + } + + ControlledShape { + fillColor: "#f8dcc8" + strokeWidth: -1 + delegate: [PathMove { x: -51.95; y: -40.63 } + , PathCubic { control1X: -52.82; control1Y: -41.61; control2X: -60.09; control2Y: -46.92; x: -65.64; y: -41 } + , PathCubic { control1X: -65.64; control1Y: -41; control2X: -75.26; control2Y: -24.35; x: -74.52; y: -18.43 } + , PathLine { x: -74.52; y: -16.58 } + , PathCubic { control1X: -74.52; control1Y: -16.58; control2X: -81.55; control2Y: -16.95; x: -83.03; y: -15.1 } + , PathCubic { control1X: -83.03; control1Y: -15.1; control2X: -84.14; control2Y: -10.29; x: -85.25; y: -9.92 } + , PathCubic { control1X: -85.25; control1Y: -9.92; control2X: -87.84; control2Y: -7.7; x: -85.99; y: -5.11 } + , PathCubic { control1X: -85.99; control1Y: -5.11; control2X: -87.84; control2Y: -2.89; x: -87.47; y: 0.81 } + , PathLine { x: -80.44; y: 4.51 } + , PathCubic { control1X: -80.44; control1Y: 4.51; control2X: -78.59; control2Y: 17.83; x: -68.6; y: 22.64 } + , PathCubic { control1X: -64.127; control1Y: 24.794; control2X: -61.2; control2Y: 18.57; x: -59.35; y: 13.02 } + , PathCubic { control1X: -59.35; control1Y: 13.02; control2X: -62.31; control2Y: -6.59; x: -60.09; y: -10.66 } + , PathCubic { control1X: -60.09; control1Y: -10.66; control2X: -50.1; control2Y: -19.91; x: -50.47; y: -23.24 } + , PathCubic { control1X: -50.47; control1Y: -23.24; control2X: -50.84; control2Y: -39.89; x: -51.95; y: -40.63 } + ] + } + + ControlledShape { + fillColor: "#ffffff" + strokeWidth: -1 + delegate: [PathMove { x: -59.6; y: 12.46 } + , PathCubic { control1X: -59.6; control1Y: 12.46; control2X: -62.48; control2Y: -6.52; x: -60.32; y: -10.48 } + , PathCubic { control1X: -60.32; control1Y: -10.48; control2X: -50.6; control2Y: -19.48; x: -50.96; y: -22.72 } + , PathCubic { control1X: -50.96; control1Y: -22.72; control2X: -51.32; control2Y: -38.92; x: -52.4; y: -39.64 } + , PathCubic { control1X: -53.16; control1Y: -40.68; control2X: -60.32; control2Y: -45.76; x: -65.72; y: -40 } + , PathCubic { control1X: -65.72; control1Y: -40; control2X: -75.08; control2Y: -23.8; x: -74.36; y: -18.04 } + , PathLine { x: -74.36; y: -16.24 } + , PathCubic { control1X: -74.36; control1Y: -16.24; control2X: -81.2; control2Y: -16.6; x: -82.64; y: -14.8 } + , PathCubic { control1X: -82.64; control1Y: -14.8; control2X: -83.72; control2Y: -10.12; x: -84.8; y: -9.76 } + , PathCubic { control1X: -84.8; control1Y: -9.76; control2X: -87.32; control2Y: -7.6; x: -85.52; y: -5.08 } + , PathCubic { control1X: -85.52; control1Y: -5.08; control2X: -87.32; control2Y: -2.92; x: -86.96; y: 0.68 } + , PathLine { x: -80.12; y: 4.28 } + , PathCubic { control1X: -80.12; control1Y: 4.28; control2X: -78.32; control2Y: 17.24; x: -68.6; y: 21.92 } + , PathCubic { control1X: -64.248; control1Y: 24.015; control2X: -61.4; control2Y: 17.86; x: -59.6; y: 12.46 } + ] + } + + ControlledShape { + fillColor: "#cccccc" + strokeWidth: -1 + delegate: [PathMove { x: -62.7; y: 6.2 } + , PathCubic { control1X: -62.7; control1Y: 6.2; control2X: -84.3; control2Y: -4; x: -85.2; y: -4.8 } + , PathCubic { control1X: -85.2; control1Y: -4.8; control2X: -76.1; control2Y: 3.4; x: -75.3; y: 3.4 } + , PathCubic { control1X: -74.5; control1Y: 3.4; control2X: -62.7; control2Y: 6.2; x: -62.7; y: 6.2 } + ] + } + + ControlledShape { + fillColor: "#000000" + strokeWidth: -1 + delegate: [PathMove { x: -79.8; y: 0 } + , PathCubic { control1X: -79.8; control1Y: 0; control2X: -61.4; control2Y: 3.6; x: -61.4; y: 8 } + , PathCubic { control1X: -61.4; control1Y: 10.912; control2X: -61.643; control2Y: 24.331; x: -67; y: 22.8 } + , PathCubic { control1X: -75.4; control1Y: 20.4; control2X: -71.8; control2Y: 6; x: -79.8; y: 0 } + ] + } + + ControlledShape { + fillColor: "#99cc32" + strokeWidth: -1 + delegate: [PathMove { x: -71.4; y: 3.8 } + , PathCubic { control1X: -71.4; control1Y: 3.8; control2X: -62.422; control2Y: 5.274; x: -61.4; y: 8 } + , PathCubic { control1X: -60.8; control1Y: 9.6; control2X: -60.137; control2Y: 17.908; x: -65.6; y: 19 } + , PathCubic { control1X: -70.152; control1Y: 19.911; control2X: -72.382; control2Y: 9.69; x: -71.4; y: 3.8 } + ] + } + + ControlledShape { + fillColor: "#000000" + strokeWidth: -1 + delegate: [PathMove { x: 14.595; y: 46.349 } + , PathCubic { control1X: 14.098; control1Y: 44.607; control2X: 15.409; control2Y: 44.738; x: 17.2; y: 44.2 } + , PathCubic { control1X: 19.2; control1Y: 43.6; control2X: 31.4; control2Y: 39.8; x: 32.2; y: 37.2 } + , PathCubic { control1X: 33; control1Y: 34.6; control2X: 46.2; control2Y: 39; x: 46.2; y: 39 } + , PathCubic { control1X: 48; control1Y: 39.8; control2X: 52.4; control2Y: 42.4; x: 52.4; y: 42.4 } + , PathCubic { control1X: 57.2; control1Y: 43.6; control2X: 63.8; control2Y: 44; x: 63.8; y: 44 } + , PathCubic { control1X: 66.2; control1Y: 45; control2X: 69.6; control2Y: 47.8; x: 69.6; y: 47.8 } + , PathCubic { control1X: 84.2; control1Y: 58; control2X: 96.601; control2Y: 50.8; x: 96.601; y: 50.8 } + , PathCubic { control1X: 116.601; control1Y: 44.2; control2X: 110.601; control2Y: 27; x: 110.601; y: 27 } + , PathCubic { control1X: 107.601; control1Y: 18; control2X: 110.801; control2Y: 14.6; x: 110.801; y: 14.6 } + , PathCubic { control1X: 111.001; control1Y: 10.8; control2X: 118.201; control2Y: 17.2; x: 118.201; y: 17.2 } + , PathCubic { control1X: 120.801; control1Y: 21.4; control2X: 121.601; control2Y: 26.4; x: 121.601; y: 26.4 } + , PathCubic { control1X: 129.601; control1Y: 37.6; control2X: 126.201; control2Y: 19.8; x: 126.201; y: 19.8 } + , PathCubic { control1X: 126.401; control1Y: 18.8; control2X: 123.601; control2Y: 15.2; x: 123.601; y: 14 } + , PathCubic { control1X: 123.601; control1Y: 12.8; control2X: 121.801; control2Y: 9.4; x: 121.801; y: 9.4 } + , PathCubic { control1X: 118.801; control1Y: 6; control2X: 121.201; control2Y: -1; x: 121.201; y: -1 } + , PathCubic { control1X: 123.001; control1Y: -14.8; control2X: 120.801; control2Y: -13; x: 120.801; y: -13 } + , PathCubic { control1X: 119.601; control1Y: -14.8; control2X: 110.401; control2Y: -4.8; x: 110.401; y: -4.8 } + , PathCubic { control1X: 108.201; control1Y: -1.4; control2X: 102.201; control2Y: 0.2; x: 102.201; y: 0.2 } + , PathCubic { control1X: 99.401; control1Y: 2; control2X: 96.001; control2Y: 0.6; x: 96.001; y: 0.6 } + , PathCubic { control1X: 93.401; control1Y: 0.2; control2X: 87.801; control2Y: 7.2; x: 87.801; y: 7.2 } + , PathCubic { control1X: 90.601; control1Y: 7; control2X: 93.001; control2Y: 11.4; x: 95.401; y: 11.6 } + , PathCubic { control1X: 97.801; control1Y: 11.8; control2X: 99.601; control2Y: 9.2; x: 101.201; y: 8.6 } + , PathCubic { control1X: 102.801; control1Y: 8; control2X: 105.601; control2Y: 13.8; x: 105.601; y: 13.8 } + , PathCubic { control1X: 106.001; control1Y: 16.4; control2X: 100.401; control2Y: 21.2; x: 100.401; y: 21.2 } + , PathCubic { control1X: 100.001; control1Y: 25.8; control2X: 98.401; control2Y: 24.2; x: 98.401; y: 24.2 } + , PathCubic { control1X: 95.401; control1Y: 23.6; control2X: 94.201; control2Y: 27.4; x: 93.201; y: 32 } + , PathCubic { control1X: 92.201; control1Y: 36.6; control2X: 88.001; control2Y: 37; x: 88.001; y: 37 } + , PathCubic { control1X: 86.401; control1Y: 44.4; control2X: 85.2; control2Y: 41.4; x: 85.2; y: 41.4 } + , PathCubic { control1X: 85; control1Y: 35.8; control2X: 79; control2Y: 41.6; x: 79; y: 41.6 } + , PathCubic { control1X: 77.8; control1Y: 43.6; control2X: 73.2; control2Y: 41.4; x: 73.2; y: 41.4 } + , PathCubic { control1X: 66.4; control1Y: 39.4; control2X: 68.8; control2Y: 37.4; x: 68.8; y: 37.4 } + , PathCubic { control1X: 70.6; control1Y: 35.2; control2X: 81.8; control2Y: 37.4; x: 81.8; y: 37.4 } + , PathCubic { control1X: 84; control1Y: 35.8; control2X: 76; control2Y: 31.8; x: 76; y: 31.8 } + , PathCubic { control1X: 75.4; control1Y: 30; control2X: 76.4; control2Y: 25.6; x: 76.4; y: 25.6 } + , PathCubic { control1X: 77.6; control1Y: 22.4; control2X: 84.4; control2Y: 16.8; x: 84.4; y: 16.8 } + , PathCubic { control1X: 93.801; control1Y: 15.6; control2X: 91.001; control2Y: 14; x: 91.001; y: 14 } + , PathCubic { control1X: 84.801; control1Y: 8.8; control2X: 79; control2Y: 16.4; x: 79; y: 16.4 } + , PathCubic { control1X: 76.8; control1Y: 22.6; control2X: 59.4; control2Y: 37.6; x: 59.4; y: 37.6 } + , PathCubic { control1X: 54.6; control1Y: 41; control2X: 57.2; control2Y: 34.2; x: 53.2; y: 37.6 } + , PathCubic { control1X: 49.2; control1Y: 41; control2X: 28.6; control2Y: 32; x: 28.6; y: 32 } + , PathCubic { control1X: 17.038; control1Y: 30.807; control2X: 14.306; control2Y: 46.549; x: 10.777; y: 43.429 } + , PathCubic { control1X: 10.777; control1Y: 43.429; control2X: 16.195; control2Y: 51.949; x: 14.595; y: 46.349 } + ] + } + + ControlledShape { + fillColor: "#000000" + strokeWidth: -1 + delegate: [PathMove { x: 209.401; y: -120 } + , PathCubic { control1X: 209.401; control1Y: -120; control2X: 183.801; control2Y: -112; x: 181.001; y: -93.2 } + , PathCubic { control1X: 181.001; control1Y: -93.2; control2X: 178.601; control2Y: -70.4; x: 199.001; y: -52.8 } + , PathCubic { control1X: 199.001; control1Y: -52.8; control2X: 199.401; control2Y: -46.4; x: 201.401; y: -43.2 } + , PathCubic { control1X: 201.401; control1Y: -43.2; control2X: 199.801; control2Y: -38.4; x: 218.601; y: -46 } + , PathLine { x: 245.801; y: -54.4 } + , PathCubic { control1X: 245.801; control1Y: -54.4; control2X: 252.201; control2Y: -56.8; x: 257.401; y: -65.6 } + , PathCubic { control1X: 262.601; control1Y: -74.4; control2X: 277.801; control2Y: -93.2; x: 274.201; y: -118.4 } + , PathCubic { control1X: 274.201; control1Y: -118.4; control2X: 275.401; control2Y: -129.6; x: 269.401; y: -130 } + , PathCubic { control1X: 269.401; control1Y: -130; control2X: 261.001; control2Y: -131.6; x: 253.801; y: -124 } + , PathCubic { control1X: 253.801; control1Y: -124; control2X: 247.001; control2Y: -120.8; x: 244.601; y: -121.2 } + , PathLine { x: 209.401; y: -120 } + ] + } + + ControlledShape { + fillColor: "#000000" + strokeWidth: -1 + delegate: [PathMove { x: 264.022; y: -120.99 } + , PathCubic { control1X: 264.022; control1Y: -120.99; control2X: 266.122; control2Y: -129.92; x: 261.282; y: -125.08 } + , PathCubic { control1X: 261.282; control1Y: -125.08; control2X: 254.242; control2Y: -119.36; x: 246.761; y: -119.36 } + , PathCubic { control1X: 246.761; control1Y: -119.36; control2X: 232.241; control2Y: -117.16; x: 227.841; y: -103.96 } + , PathCubic { control1X: 227.841; control1Y: -103.96; control2X: 223.881; control2Y: -77.12; x: 231.801; y: -71.4 } + , PathCubic { control1X: 231.801; control1Y: -71.4; control2X: 236.641; control2Y: -63.92; x: 243.681; y: -70.52 } + , PathCubic { control1X: 250.722; control1Y: -77.12; control2X: 266.222; control2Y: -107.35; x: 264.022; y: -120.99 } + ] + } + + ControlledShape { + fillColor: "#323232" + strokeWidth: -1 + delegate: [PathMove { x: 263.648; y: -120.632 } + , PathCubic { control1X: 263.648; control1Y: -120.632; control2X: 265.738; control2Y: -129.376; x: 260.986; y: -124.624 } + , PathCubic { control1X: 260.986; control1Y: -124.624; control2X: 254.074; control2Y: -119.008; x: 246.729; y: -119.008 } + , PathCubic { control1X: 246.729; control1Y: -119.008; control2X: 232.473; control2Y: -116.848; x: 228.153; y: -103.888 } + , PathCubic { control1X: 228.153; control1Y: -103.888; control2X: 224.265; control2Y: -77.536; x: 232.041; y: -71.92 } + , PathCubic { control1X: 232.041; control1Y: -71.92; control2X: 236.793; control2Y: -64.576; x: 243.705; y: -71.056 } + , PathCubic { control1X: 250.618; control1Y: -77.536; control2X: 265.808; control2Y: -107.24; x: 263.648; y: -120.632 } + ] + } + + ControlledShape { + fillColor: "#666666" + strokeWidth: -1 + delegate: [PathMove { x: 263.274; y: -120.274 } + , PathCubic { control1X: 263.274; control1Y: -120.274; control2X: 265.354; control2Y: -128.832; x: 260.69; y: -124.168 } + , PathCubic { control1X: 260.69; control1Y: -124.168; control2X: 253.906; control2Y: -118.656; x: 246.697; y: -118.656 } + , PathCubic { control1X: 246.697; control1Y: -118.656; control2X: 232.705; control2Y: -116.536; x: 228.465; y: -103.816 } + , PathCubic { control1X: 228.465; control1Y: -103.816; control2X: 224.649; control2Y: -77.952; x: 232.281; y: -72.44 } + , PathCubic { control1X: 232.281; control1Y: -72.44; control2X: 236.945; control2Y: -65.232; x: 243.729; y: -71.592 } + , PathCubic { control1X: 250.514; control1Y: -77.952; control2X: 265.394; control2Y: -107.13; x: 263.274; y: -120.274 } + ] + } + + ControlledShape { + fillColor: "#999999" + strokeWidth: -1 + delegate: [PathMove { x: 262.9; y: -119.916 } + , PathCubic { control1X: 262.9; control1Y: -119.916; control2X: 264.97; control2Y: -128.288; x: 260.394; y: -123.712 } + , PathCubic { control1X: 260.394; control1Y: -123.712; control2X: 253.738; control2Y: -118.304; x: 246.665; y: -118.304 } + , PathCubic { control1X: 246.665; control1Y: -118.304; control2X: 232.937; control2Y: -116.224; x: 228.777; y: -103.744 } + , PathCubic { control1X: 228.777; control1Y: -103.744; control2X: 225.033; control2Y: -78.368; x: 232.521; y: -72.96 } + , PathCubic { control1X: 232.521; control1Y: -72.96; control2X: 237.097; control2Y: -65.888; x: 243.753; y: -72.128 } + , PathCubic { control1X: 250.41; control1Y: -78.368; control2X: 264.98; control2Y: -107.02; x: 262.9; y: -119.916 } + ] + } + + ControlledShape { + fillColor: "#cccccc" + strokeWidth: -1 + delegate: [PathMove { x: 262.526; y: -119.558 } + , PathCubic { control1X: 262.526; control1Y: -119.558; control2X: 264.586; control2Y: -127.744; x: 260.098; y: -123.256 } + , PathCubic { control1X: 260.098; control1Y: -123.256; control2X: 253.569; control2Y: -117.952; x: 246.633; y: -117.952 } + , PathCubic { control1X: 246.633; control1Y: -117.952; control2X: 233.169; control2Y: -115.912; x: 229.089; y: -103.672 } + , PathCubic { control1X: 229.089; control1Y: -103.672; control2X: 225.417; control2Y: -78.784; x: 232.761; y: -73.48 } + , PathCubic { control1X: 232.761; control1Y: -73.48; control2X: 237.249; control2Y: -66.544; x: 243.777; y: -72.664 } + , PathCubic { control1X: 250.305; control1Y: -78.784; control2X: 264.566; control2Y: -106.91; x: 262.526; y: -119.558 } + ] + } + + ControlledShape { + fillColor: "#ffffff" + strokeWidth: -1 + delegate: [PathMove { x: 262.151; y: -119.2 } + , PathCubic { control1X: 262.151; control1Y: -119.2; control2X: 264.201; control2Y: -127.2; x: 259.801; y: -122.8 } + , PathCubic { control1X: 259.801; control1Y: -122.8; control2X: 253.401; control2Y: -117.6; x: 246.601; y: -117.6 } + , PathCubic { control1X: 246.601; control1Y: -117.6; control2X: 233.401; control2Y: -115.6; x: 229.401; y: -103.6 } + , PathCubic { control1X: 229.401; control1Y: -103.6; control2X: 225.801; control2Y: -79.2; x: 233.001; y: -74 } + , PathCubic { control1X: 233.001; control1Y: -74; control2X: 237.401; control2Y: -67.2; x: 243.801; y: -73.2 } + , PathCubic { control1X: 250.201; control1Y: -79.2; control2X: 264.151; control2Y: -106.8; x: 262.151; y: -119.2 } + ] + } + + ControlledShape { + fillColor: "#992600" + strokeWidth: -1 + delegate: [PathMove { x: 50.6; y: 84 } + , PathCubic { control1X: 50.6; control1Y: 84; control2X: 30.2; control2Y: 64.8; x: 22.2; y: 64 } + , PathCubic { control1X: 22.2; control1Y: 64; control2X: -12.2; control2Y: 60; x: -27; y: 78 } + , PathCubic { control1X: -27; control1Y: 78; control2X: -9.4; control2Y: 57.6; x: 18.2; y: 63.2 } + , PathCubic { control1X: 18.2; control1Y: 63.2; control2X: -3.4; control2Y: 58.8; x: -15.8; y: 62 } + , PathCubic { control1X: -15.8; control1Y: 62; control2X: -32.6; control2Y: 62; x: -42.2; y: 76 } + , PathLine { x: -45; y: 80.8 } + , PathCubic { control1X: -45; control1Y: 80.8; control2X: -41; control2Y: 66; x: -22.6; y: 60 } + , PathCubic { control1X: -22.6; control1Y: 60; control2X: 0.2; control2Y: 55.2; x: 11; y: 60 } + , PathCubic { control1X: 11; control1Y: 60; control2X: -10.6; control2Y: 53.2; x: -20.6; y: 55.2 } + , PathCubic { control1X: -20.6; control1Y: 55.2; control2X: -51; control2Y: 52.8; x: -63.8; y: 79.2 } + , PathCubic { control1X: -63.8; control1Y: 79.2; control2X: -59.8; control2Y: 64.8; x: -45; y: 57.6 } + , PathCubic { control1X: -45; control1Y: 57.6; control2X: -31.4; control2Y: 48.8; x: -11; y: 51.6 } + , PathCubic { control1X: -11; control1Y: 51.6; control2X: 3.4; control2Y: 54.8; x: 8.6; y: 57.2 } + , PathCubic { control1X: 13.8; control1Y: 59.6; control2X: 12.6; control2Y: 56.8; x: 4.2; y: 52 } + , PathCubic { control1X: 4.2; control1Y: 52; control2X: -1.4; control2Y: 42; x: -15.4; y: 42.4 } + , PathCubic { control1X: -15.4; control1Y: 42.4; control2X: -58.2; control2Y: 46; x: -68.6; y: 58 } + , PathCubic { control1X: -68.6; control1Y: 58; control2X: -55; control2Y: 46.8; x: -44.6; y: 44 } + , PathCubic { control1X: -44.6; control1Y: 44; control2X: -22.2; control2Y: 36; x: -13.8; y: 36.8 } + , PathCubic { control1X: -13.8; control1Y: 36.8; control2X: 11; control2Y: 37.8; x: 18.6; y: 33.8 } + , PathCubic { control1X: 18.6; control1Y: 33.8; control2X: 7.4; control2Y: 38.8; x: 10.6; y: 42 } + , PathCubic { control1X: 13.8; control1Y: 45.2; control2X: 20.6; control2Y: 52.8; x: 20.6; y: 54 } + , PathCubic { control1X: 20.6; control1Y: 55.2; control2X: 44.8; control2Y: 77.3; x: 48.4; y: 81.7 } + , PathLine { x: 50.6; y: 84 } + ] + } + + ControlledShape { + fillColor: "#cccccc" + strokeWidth: -1 + delegate: [PathMove { x: 189; y: 278 } + , PathCubic { control1X: 189; control1Y: 278; control2X: 173.5; control2Y: 241.5; x: 161; y: 232 } + , PathCubic { control1X: 161; control1Y: 232; control2X: 187; control2Y: 248; x: 190.5; y: 266 } + , PathCubic { control1X: 190.5; control1Y: 266; control2X: 190.5; control2Y: 276; x: 189; y: 278 } + ] + } + + ControlledShape { + fillColor: "#cccccc" + strokeWidth: -1 + delegate: [PathMove { x: 236; y: 285.5 } + , PathCubic { control1X: 236; control1Y: 285.5; control2X: 209.5; control2Y: 230.5; x: 191; y: 206.5 } + , PathCubic { control1X: 191; control1Y: 206.5; control2X: 234.5; control2Y: 244; x: 239.5; y: 270.5 } + , PathLine { x: 240; y: 276 } + , PathLine { x: 237; y: 273.5 } + , PathCubic { control1X: 237; control1Y: 273.5; control2X: 236.5; control2Y: 282.5; x: 236; y: 285.5 } + ] + } + + ControlledShape { + fillColor: "#cccccc" + strokeWidth: -1 + delegate: [PathMove { x: 292.5; y: 237 } + , PathCubic { control1X: 292.5; control1Y: 237; control2X: 230; control2Y: 177.5; x: 228.5; y: 175 } + , PathCubic { control1X: 228.5; control1Y: 175; control2X: 289; control2Y: 241; x: 292; y: 248.5 } + , PathCubic { control1X: 292; control1Y: 248.5; control2X: 290; control2Y: 239.5; x: 292.5; y: 237 } + ] + } + + ControlledShape { + fillColor: "#cccccc" + strokeWidth: -1 + delegate: [PathMove { x: 104; y: 280.5 } + , PathCubic { control1X: 104; control1Y: 280.5; control2X: 123.5; control2Y: 228.5; x: 142.5; y: 251 } + , PathCubic { control1X: 142.5; control1Y: 251; control2X: 157.5; control2Y: 261; x: 157; y: 264 } + , PathCubic { control1X: 157; control1Y: 264; control2X: 153; control2Y: 257.5; x: 135; y: 258 } + , PathCubic { control1X: 135; control1Y: 258; control2X: 116; control2Y: 255; x: 104; y: 280.5 } + ] + } + + ControlledShape { + fillColor: "#cccccc" + strokeWidth: -1 + delegate: [PathMove { x: 294.5; y: 153 } + , PathCubic { control1X: 294.5; control1Y: 153; control2X: 249.5; control2Y: 124.5; x: 242; y: 123 } + , PathCubic { control1X: 230.193; control1Y: 120.639; control2X: 291.5; control2Y: 152; x: 296.5; y: 162.5 } + , PathCubic { control1X: 296.5; control1Y: 162.5; control2X: 298.5; control2Y: 160; x: 294.5; y: 153 } + ] + } + + ControlledShape { + fillColor: "#000000" + strokeWidth: -1 + delegate: [PathMove { x: 143.801; y: 259.601 } + , PathCubic { control1X: 143.801; control1Y: 259.601; control2X: 164.201; control2Y: 257.601; x: 171.001; y: 250.801 } + , PathLine { x: 175.401; y: 254.401 } + , PathLine { x: 193.001; y: 216.001 } + , PathLine { x: 196.601; y: 221.201 } + , PathCubic { control1X: 196.601; control1Y: 221.201; control2X: 211.001; control2Y: 206.401; x: 210.201; y: 198.401 } + , PathCubic { control1X: 209.401; control1Y: 190.401; control2X: 223.001; control2Y: 204.401; x: 223.001; y: 204.401 } + , PathCubic { control1X: 223.001; control1Y: 204.401; control2X: 222.201; control2Y: 192.801; x: 229.401; y: 199.601 } + , PathCubic { control1X: 229.401; control1Y: 199.601; control2X: 227.001; control2Y: 184.001; x: 235.401; y: 192.001 } + , PathCubic { control1X: 235.401; control1Y: 192.001; control2X: 224.864; control2Y: 161.844; x: 247.401; y: 187.601 } + , PathCubic { control1X: 253.001; control1Y: 194.001; control2X: 248.601; control2Y: 187.201; x: 248.601; y: 187.201 } + , PathCubic { control1X: 248.601; control1Y: 187.201; control2X: 222.601; control2Y: 139.201; x: 244.201; y: 153.601 } + , PathCubic { control1X: 244.201; control1Y: 153.601; control2X: 246.201; control2Y: 130.801; x: 245.001; y: 126.401 } + , PathCubic { control1X: 243.801; control1Y: 122.001; control2X: 241.801; control2Y: 99.6; x: 237.001; y: 94.4 } + , PathCubic { control1X: 232.201; control1Y: 89.2; control2X: 237.401; control2Y: 87.6; x: 243.001; y: 92.8 } + , PathCubic { control1X: 243.001; control1Y: 92.8; control2X: 231.801; control2Y: 68.8; x: 245.001; y: 80.8 } + , PathCubic { control1X: 245.001; control1Y: 80.8; control2X: 241.401; control2Y: 65.6; x: 237.001; y: 62.8 } + , PathCubic { control1X: 237.001; control1Y: 62.8; control2X: 231.401; control2Y: 45.6; x: 246.601; y: 56.4 } + , PathCubic { control1X: 246.601; control1Y: 56.4; control2X: 242.201; control2Y: 44; x: 239.001; y: 40.8 } + , PathCubic { control1X: 239.001; control1Y: 40.8; control2X: 227.401; control2Y: 13.2; x: 234.601; y: 18 } + , PathLine { x: 239.001; y: 21.6 } + , PathCubic { control1X: 239.001; control1Y: 21.6; control2X: 232.201; control2Y: 7.6; x: 238.601; y: 12 } + , PathCubic { control1X: 245.001; control1Y: 16.4; control2X: 245.001; control2Y: 16; x: 245.001; y: 16 } + , PathCubic { control1X: 245.001; control1Y: 16; control2X: 223.801; control2Y: -17.2; x: 244.201; y: 0.4 } + , PathCubic { control1X: 244.201; control1Y: 0.4; control2X: 236.042; control2Y: -13.518; x: 232.601; y: -20.4 } + , PathCubic { control1X: 232.601; control1Y: -20.4; control2X: 213.801; control2Y: -40.8; x: 228.201; y: -34.4 } + , PathLine { x: 233.001; y: -32.8 } + , PathCubic { control1X: 233.001; control1Y: -32.8; control2X: 224.201; control2Y: -42.8; x: 216.201; y: -44.4 } + , PathCubic { control1X: 208.201; control1Y: -46; control2X: 218.601; control2Y: -52.4; x: 225.001; y: -50.4 } + , PathCubic { control1X: 231.401; control1Y: -48.4; control2X: 247.001; control2Y: -40.8; x: 247.001; y: -40.8 } + , PathCubic { control1X: 247.001; control1Y: -40.8; control2X: 259.801; control2Y: -22; x: 263.801; y: -21.6 } + , PathCubic { control1X: 263.801; control1Y: -21.6; control2X: 243.801; control2Y: -29.2; x: 249.801; y: -21.2 } + , PathCubic { control1X: 249.801; control1Y: -21.2; control2X: 264.201; control2Y: -7.2; x: 257.001; y: -7.6 } + , PathCubic { control1X: 257.001; control1Y: -7.6; control2X: 251.001; control2Y: -0.4; x: 255.801; y: 8.4 } + , PathCubic { control1X: 255.801; control1Y: 8.4; control2X: 237.342; control2Y: -9.991; x: 252.201; y: 15.6 } + , PathLine { x: 259.001; y: 32 } + , PathCubic { control1X: 259.001; control1Y: 32; control2X: 234.601; control2Y: 7.2; x: 245.801; y: 29.2 } + , PathCubic { control1X: 245.801; control1Y: 29.2; control2X: 263.001; control2Y: 52.8; x: 265.001; y: 53.2 } + , PathCubic { control1X: 267.001; control1Y: 53.6; control2X: 271.401; control2Y: 62.4; x: 271.401; y: 62.4 } + , PathLine { x: 267.001; y: 60.4 } + , PathLine { x: 272.201; y: 69.2 } + , PathCubic { control1X: 272.201; control1Y: 69.2; control2X: 261.001; control2Y: 57.2; x: 267.001; y: 70.4 } + , PathLine { x: 272.601; y: 84.8 } + , PathCubic { control1X: 272.601; control1Y: 84.8; control2X: 252.201; control2Y: 62.8; x: 265.801; y: 92.4 } + , PathCubic { control1X: 265.801; control1Y: 92.4; control2X: 249.401; control2Y: 87.2; x: 258.201; y: 104.4 } + , PathCubic { control1X: 258.201; control1Y: 104.4; control2X: 256.601; control2Y: 120.401; x: 257.001; y: 125.601 } + , PathCubic { control1X: 257.401; control1Y: 130.801; control2X: 258.601; control2Y: 159.201; x: 254.201; y: 167.201 } + , PathCubic { control1X: 249.801; control1Y: 175.201; control2X: 260.201; control2Y: 194.401; x: 262.201; y: 198.401 } + , PathCubic { control1X: 264.201; control1Y: 202.401; control2X: 267.801; control2Y: 213.201; x: 259.001; y: 204.001 } + , PathCubic { control1X: 250.201; control1Y: 194.801; control2X: 254.601; control2Y: 200.401; x: 256.601; y: 209.201 } + , PathCubic { control1X: 258.601; control1Y: 218.001; control2X: 264.601; control2Y: 233.601; x: 263.801; y: 239.201 } + , PathCubic { control1X: 263.801; control1Y: 239.201; control2X: 262.601; control2Y: 240.401; x: 259.401; y: 236.801 } + , PathCubic { control1X: 259.401; control1Y: 236.801; control2X: 244.601; control2Y: 214.001; x: 246.201; y: 228.401 } + , PathCubic { control1X: 246.201; control1Y: 228.401; control2X: 245.001; control2Y: 236.401; x: 241.801; y: 245.201 } + , PathCubic { control1X: 241.801; control1Y: 245.201; control2X: 238.601; control2Y: 256.001; x: 238.601; y: 247.201 } + , PathCubic { control1X: 238.601; control1Y: 247.201; control2X: 235.401; control2Y: 230.401; x: 232.601; y: 238.001 } + , PathCubic { control1X: 229.801; control1Y: 245.601; control2X: 226.201; control2Y: 251.601; x: 223.401; y: 254.001 } + , PathCubic { control1X: 220.601; control1Y: 256.401; control2X: 215.401; control2Y: 233.601; x: 214.201; y: 244.001 } + , PathCubic { control1X: 214.201; control1Y: 244.001; control2X: 202.201; control2Y: 231.601; x: 197.401; y: 248.001 } + , PathLine { x: 185.801; y: 264.401 } + , PathCubic { control1X: 185.801; control1Y: 264.401; control2X: 185.401; control2Y: 252.001; x: 184.201; y: 258.001 } + , PathCubic { control1X: 184.201; control1Y: 258.001; control2X: 154.201; control2Y: 264.001; x: 143.801; y: 259.601 } + ] + } + + ControlledShape { + fillColor: "#000000" + strokeWidth: -1 + delegate: [PathMove { x: 109.401; y: -97.2 } + , PathCubic { control1X: 109.401; control1Y: -97.2; control2X: 97.801; control2Y: -105.2; x: 93.801; y: -104.8 } + , PathCubic { control1X: 89.801; control1Y: -104.4; control2X: 121.401; control2Y: -113.6; x: 162.601; y: -86 } + , PathCubic { control1X: 162.601; control1Y: -86; control2X: 167.401; control2Y: -83.2; x: 171.001; y: -83.6 } + , PathCubic { control1X: 171.001; control1Y: -83.6; control2X: 174.201; control2Y: -81.2; x: 171.401; y: -77.6 } + , PathCubic { control1X: 171.401; control1Y: -77.6; control2X: 162.601; control2Y: -68; x: 173.801; y: -56.8 } + , PathCubic { control1X: 173.801; control1Y: -56.8; control2X: 192.201; control2Y: -50; x: 186.601; y: -58.8 } + , PathCubic { control1X: 186.601; control1Y: -58.8; control2X: 197.401; control2Y: -54.8; x: 199.801; y: -50.8 } + , PathCubic { control1X: 202.201; control1Y: -46.8; control2X: 201.001; control2Y: -50.8; x: 201.001; y: -50.8 } + , PathCubic { control1X: 201.001; control1Y: -50.8; control2X: 194.601; control2Y: -58; x: 188.601; y: -63.2 } + , PathCubic { control1X: 188.601; control1Y: -63.2; control2X: 183.401; control2Y: -65.2; x: 180.601; y: -73.6 } + , PathCubic { control1X: 177.801; control1Y: -82; control2X: 175.401; control2Y: -92; x: 179.801; y: -95.2 } + , PathCubic { control1X: 179.801; control1Y: -95.2; control2X: 175.801; control2Y: -90.8; x: 176.601; y: -94.8 } + , PathCubic { control1X: 177.401; control1Y: -98.8; control2X: 181.001; control2Y: -102.4; x: 182.601; y: -102.8 } + , PathCubic { control1X: 184.201; control1Y: -103.2; control2X: 200.601; control2Y: -119; x: 207.401; y: -119.4 } + , PathCubic { control1X: 207.401; control1Y: -119.4; control2X: 198.201; control2Y: -118; x: 195.201; y: -119 } + , PathCubic { control1X: 192.201; control1Y: -120; control2X: 165.601; control2Y: -131.4; x: 159.601; y: -132.6 } + , PathCubic { control1X: 159.601; control1Y: -132.6; control2X: 142.801; control2Y: -139.2; x: 154.801; y: -137.2 } + , PathCubic { control1X: 154.801; control1Y: -137.2; control2X: 190.601; control2Y: -133.4; x: 208.801; y: -120.2 } + , PathCubic { control1X: 208.801; control1Y: -120.2; control2X: 201.601; control2Y: -128.6; x: 183.201; y: -135.6 } + , PathCubic { control1X: 183.201; control1Y: -135.6; control2X: 161.001; control2Y: -148.2; x: 125.801; y: -143.2 } + , PathCubic { control1X: 125.801; control1Y: -143.2; control2X: 108.001; control2Y: -140; x: 100.201; y: -138.2 } + , PathCubic { control1X: 100.201; control1Y: -138.2; control2X: 97.601; control2Y: -138.8; x: 97.001; y: -139.2 } + , PathCubic { control1X: 96.401; control1Y: -139.6; control2X: 84.6; control2Y: -148.6; x: 57; y: -141.6 } + , PathCubic { control1X: 57; control1Y: -141.6; control2X: 40; control2Y: -137; x: 31.4; y: -132.2 } + , PathCubic { control1X: 31.4; control1Y: -132.2; control2X: 16.2; control2Y: -131; x: 12.6; y: -127.8 } + , PathCubic { control1X: 12.6; control1Y: -127.8; control2X: -6; control2Y: -113.2; x: -8; y: -112.4 } + , PathCubic { control1X: -10; control1Y: -111.6; control2X: -21.4; control2Y: -104; x: -22.2; y: -103.6 } + , PathCubic { control1X: -22.2; control1Y: -103.6; control2X: 2.4; control2Y: -110.2; x: 4.8; y: -112.6 } + , PathCubic { control1X: 7.2; control1Y: -115; control2X: 24.6; control2Y: -117.6; x: 27; y: -116.2 } + , PathCubic { control1X: 29.4; control1Y: -114.8; control2X: 37.8; control2Y: -115.4; x: 28.2; y: -114.8 } + , PathCubic { control1X: 28.2; control1Y: -114.8; control2X: 103.801; control2Y: -100; x: 104.601; y: -98 } + , PathCubic { control1X: 105.401; control1Y: -96; control2X: 109.401; control2Y: -97.2; x: 109.401; y: -97.2 } + ] + } + + ControlledShape { + fillColor: "#cc7226" + strokeWidth: -1 + delegate: [PathMove { x: 180.801; y: -106.4 } + , PathCubic { control1X: 180.801; control1Y: -106.4; control2X: 170.601; control2Y: -113.8; x: 168.601; y: -113.8 } + , PathCubic { control1X: 166.601; control1Y: -113.8; control2X: 154.201; control2Y: -124; x: 150.001; y: -123.6 } + , PathCubic { control1X: 145.801; control1Y: -123.2; control2X: 133.601; control2Y: -133.2; x: 106.201; y: -125 } + , PathCubic { control1X: 106.201; control1Y: -125; control2X: 105.601; control2Y: -127; x: 109.201; y: -127.8 } + , PathCubic { control1X: 109.201; control1Y: -127.8; control2X: 115.601; control2Y: -130; x: 116.001; y: -130.6 } + , PathCubic { control1X: 116.001; control1Y: -130.6; control2X: 136.201; control2Y: -134.8; x: 143.401; y: -131.2 } + , PathCubic { control1X: 143.401; control1Y: -131.2; control2X: 152.601; control2Y: -128.6; x: 158.801; y: -122.4 } + , PathCubic { control1X: 158.801; control1Y: -122.4; control2X: 170.001; control2Y: -119.2; x: 173.201; y: -120.2 } + , PathCubic { control1X: 173.201; control1Y: -120.2; control2X: 182.001; control2Y: -118; x: 182.401; y: -116.2 } + , PathCubic { control1X: 182.401; control1Y: -116.2; control2X: 188.201; control2Y: -113.2; x: 186.401; y: -110.6 } + , PathCubic { control1X: 186.401; control1Y: -110.6; control2X: 186.801; control2Y: -109; x: 180.801; y: -106.4 } + ] + } + + ControlledShape { + fillColor: "#cc7226" + strokeWidth: -1 + delegate: [PathMove { x: 168.33; y: -108.509 } + , PathCubic { control1X: 169.137; control1Y: -107.877; control2X: 170.156; control2Y: -107.779; x: 170.761; y: -106.97 } + , PathCubic { control1X: 170.995; control1Y: -106.656; control2X: 170.706; control2Y: -106.33; x: 170.391; y: -106.233 } + , PathCubic { control1X: 169.348; control1Y: -105.916; control2X: 168.292; control2Y: -106.486; x: 167.15; y: -105.898 } + , PathCubic { control1X: 166.748; control1Y: -105.691; control2X: 166.106; control2Y: -105.873; x: 165.553; y: -106.022 } + , PathCubic { control1X: 163.921; control1Y: -106.463; control2X: 162.092; control2Y: -106.488; x: 160.401; y: -105.8 } + , PathCubic { control1X: 158.416; control1Y: -106.929; control2X: 156.056; control2Y: -106.345; x: 153.975; y: -107.346 } + , PathCubic { control1X: 153.917; control1Y: -107.373; control2X: 153.695; control2Y: -107.027; x: 153.621; y: -107.054 } + , PathCubic { control1X: 150.575; control1Y: -108.199; control2X: 146.832; control2Y: -107.916; x: 144.401; y: -110.2 } + , PathCubic { control1X: 141.973; control1Y: -110.612; control2X: 139.616; control2Y: -111.074; x: 137.188; y: -111.754 } + , PathCubic { control1X: 135.37; control1Y: -112.263; control2X: 133.961; control2Y: -113.252; x: 132.341; y: -114.084 } + , PathCubic { control1X: 130.964; control1Y: -114.792; control2X: 129.507; control2Y: -115.314; x: 127.973; y: -115.686 } + , PathCubic { control1X: 126.11; control1Y: -116.138; control2X: 124.279; control2Y: -116.026; x: 122.386; y: -116.546 } + , PathCubic { control1X: 122.293; control1Y: -116.571; control2X: 122.101; control2Y: -116.227; x: 122.019; y: -116.254 } + , PathCubic { control1X: 121.695; control1Y: -116.362; control2X: 121.405; control2Y: -116.945; x: 121.234; y: -116.892 } + , PathCubic { control1X: 119.553; control1Y: -116.37; control2X: 118.065; control2Y: -117.342; x: 116.401; y: -117 } + , PathCubic { control1X: 115.223; control1Y: -118.224; control2X: 113.495; control2Y: -117.979; x: 111.949; y: -118.421 } + , PathCubic { control1X: 108.985; control1Y: -119.269; control2X: 105.831; control2Y: -117.999; x: 102.801; y: -119 } + , PathCubic { control1X: 106.914; control1Y: -120.842; control2X: 111.601; control2Y: -119.61; x: 115.663; y: -121.679 } + , PathCubic { control1X: 117.991; control1Y: -122.865; control2X: 120.653; control2Y: -121.763; x: 123.223; y: -122.523 } + , PathCubic { control1X: 123.71; control1Y: -122.667; control2X: 124.401; control2Y: -122.869; x: 124.801; y: -122.2 } + , PathCubic { control1X: 124.935; control1Y: -122.335; control2X: 125.117; control2Y: -122.574; x: 125.175; y: -122.546 } + , PathCubic { control1X: 127.625; control1Y: -121.389; control2X: 129.94; control2Y: -120.115; x: 132.422; y: -119.049 } + , PathCubic { control1X: 132.763; control1Y: -118.903; control2X: 133.295; control2Y: -119.135; x: 133.547; y: -118.933 } + , PathCubic { control1X: 135.067; control1Y: -117.717; control2X: 137.01; control2Y: -117.82; x: 138.401; y: -116.6 } + , PathCubic { control1X: 140.099; control1Y: -117.102; control2X: 141.892; control2Y: -116.722; x: 143.621; y: -117.346 } + , PathCubic { control1X: 143.698; control1Y: -117.373; control2X: 143.932; control2Y: -117.032; x: 143.965; y: -117.054 } + , PathCubic { control1X: 145.095; control1Y: -117.802; control2X: 146.25; control2Y: -117.531; x: 147.142; y: -117.227 } + , PathCubic { control1X: 147.48; control1Y: -117.112; control2X: 148.143; control2Y: -116.865; x: 148.448; y: -116.791 } + , PathCubic { control1X: 149.574; control1Y: -116.515; control2X: 150.43; control2Y: -116.035; x: 151.609; y: -115.852 } + , PathCubic { control1X: 151.723; control1Y: -115.834; control2X: 151.908; control2Y: -116.174; x: 151.98; y: -116.146 } + , PathCubic { control1X: 153.103; control1Y: -115.708; control2X: 154.145; control2Y: -115.764; x: 154.801; y: -114.6 } + , PathCubic { control1X: 154.936; control1Y: -114.735; control2X: 155.101; control2Y: -114.973; x: 155.183; y: -114.946 } + , PathCubic { control1X: 156.21; control1Y: -114.608; control2X: 156.859; control2Y: -113.853; x: 157.96; y: -113.612 } + , PathCubic { control1X: 158.445; control1Y: -113.506; control2X: 159.057; control2Y: -112.88; x: 159.633; y: -112.704 } + , PathCubic { control1X: 162.025; control1Y: -111.973; control2X: 163.868; control2Y: -110.444; x: 166.062; y: -109.549 } + , PathCubic { control1X: 166.821; control1Y: -109.239; control2X: 167.697; control2Y: -109.005; x: 168.33; y: -108.509 } + ] + } + + ControlledShape { + fillColor: "#cc7226" + strokeWidth: -1 + delegate: [PathMove { x: 91.696; y: -122.739 } + , PathCubic { control1X: 89.178; control1Y: -124.464; control2X: 86.81; control2Y: -125.57; x: 84.368; y: -127.356 } + , PathCubic { control1X: 84.187; control1Y: -127.489; control2X: 83.827; control2Y: -127.319; x: 83.625; y: -127.441 } + , PathCubic { control1X: 82.618; control1Y: -128.05; control2X: 81.73; control2Y: -128.631; x: 80.748; y: -129.327 } + , PathCubic { control1X: 80.209; control1Y: -129.709; control2X: 79.388; control2Y: -129.698; x: 78.88; y: -129.956 } + , PathCubic { control1X: 76.336; control1Y: -131.248; control2X: 73.707; control2Y: -131.806; x: 71.2; y: -133 } + , PathCubic { control1X: 71.882; control1Y: -133.638; control2X: 73.004; control2Y: -133.394; x: 73.6; y: -134.2 } + , PathCubic { control1X: 73.795; control1Y: -133.92; control2X: 74.033; control2Y: -133.636; x: 74.386; y: -133.827 } + , PathCubic { control1X: 76.064; control1Y: -134.731; control2X: 77.914; control2Y: -134.884; x: 79.59; y: -134.794 } + , PathCubic { control1X: 81.294; control1Y: -134.702; control2X: 83.014; control2Y: -134.397; x: 84.789; y: -134.125 } + , PathCubic { control1X: 85.096; control1Y: -134.078; control2X: 85.295; control2Y: -133.555; x: 85.618; y: -133.458 } + , PathCubic { control1X: 87.846; control1Y: -132.795; control2X: 90.235; control2Y: -133.32; x: 92.354; y: -132.482 } + , PathCubic { control1X: 93.945; control1Y: -131.853; control2X: 95.515; control2Y: -131.03; x: 96.754; y: -129.755 } + , PathCubic { control1X: 97.006; control1Y: -129.495; control2X: 96.681; control2Y: -129.194; x: 96.401; y: -129 } + , PathCubic { control1X: 96.789; control1Y: -129.109; control2X: 97.062; control2Y: -128.903; x: 97.173; y: -128.59 } + , PathCubic { control1X: 97.257; control1Y: -128.351; control2X: 97.257; control2Y: -128.049; x: 97.173; y: -127.81 } + , PathCubic { control1X: 97.061; control1Y: -127.498; control2X: 96.782; control2Y: -127.397; x: 96.408; y: -127.346 } + , PathCubic { control1X: 95.001; control1Y: -127.156; control2X: 96.773; control2Y: -128.536; x: 96.073; y: -128.088 } + , PathCubic { control1X: 94.8; control1Y: -127.274; control2X: 95.546; control2Y: -125.868; x: 94.801; y: -124.6 } + , PathCubic { control1X: 94.521; control1Y: -124.794; control2X: 94.291; control2Y: -125.012; x: 94.401; y: -125.4 } + , PathCubic { control1X: 94.635; control1Y: -124.878; control2X: 94.033; control2Y: -124.588; x: 93.865; y: -124.272 } + , PathCubic { control1X: 93.48; control1Y: -123.547; control2X: 92.581; control2Y: -122.132; x: 91.696; y: -122.739 } + ] + } + + ControlledShape { + fillColor: "#cc7226" + strokeWidth: -1 + delegate: [PathMove { x: 59.198; y: -115.391 } + , PathCubic { control1X: 56.044; control1Y: -116.185; control2X: 52.994; control2Y: -116.07; x: 49.978; y: -117.346 } + , PathCubic { control1X: 49.911; control1Y: -117.374; control2X: 49.688; control2Y: -117.027; x: 49.624; y: -117.054 } + , PathCubic { control1X: 48.258; control1Y: -117.648; control2X: 47.34; control2Y: -118.614; x: 46.264; y: -119.66 } + , PathCubic { control1X: 45.351; control1Y: -120.548; control2X: 43.693; control2Y: -120.161; x: 42.419; y: -120.648 } + , PathCubic { control1X: 42.095; control1Y: -120.772; control2X: 41.892; control2Y: -121.284; x: 41.591; y: -121.323 } + , PathCubic { control1X: 40.372; control1Y: -121.48; control2X: 39.445; control2Y: -122.429; x: 38.4; y: -123 } + , PathCubic { control1X: 40.736; control1Y: -123.795; control2X: 43.147; control2Y: -123.764; x: 45.609; y: -124.148 } + , PathCubic { control1X: 45.722; control1Y: -124.166; control2X: 45.867; control2Y: -123.845; x: 46; y: -123.845 } + , PathCubic { control1X: 46.136; control1Y: -123.845; control2X: 46.266; control2Y: -124.066; x: 46.4; y: -124.2 } + , PathCubic { control1X: 46.595; control1Y: -123.92; control2X: 46.897; control2Y: -123.594; x: 47.154; y: -123.848 } + , PathCubic { control1X: 47.702; control1Y: -124.388; control2X: 48.258; control2Y: -124.198; x: 48.798; y: -124.158 } + , PathCubic { control1X: 48.942; control1Y: -124.148; control2X: 49.067; control2Y: -123.845; x: 49.2; y: -123.845 } + , PathCubic { control1X: 49.336; control1Y: -123.845; control2X: 49.467; control2Y: -124.156; x: 49.6; y: -124.156 } + , PathCubic { control1X: 49.736; control1Y: -124.155; control2X: 49.867; control2Y: -123.845; x: 50; y: -123.845 } + , PathCubic { control1X: 50.136; control1Y: -123.845; control2X: 50.266; control2Y: -124.066; x: 50.4; y: -124.2 } + , PathCubic { control1X: 51.092; control1Y: -123.418; control2X: 51.977; control2Y: -123.972; x: 52.799; y: -123.793 } + , PathCubic { control1X: 53.837; control1Y: -123.566; control2X: 54.104; control2Y: -122.418; x: 55.178; y: -122.12 } + , PathCubic { control1X: 59.893; control1Y: -120.816; control2X: 64.03; control2Y: -118.671; x: 68.393; y: -116.584 } + , PathCubic { control1X: 68.7; control1Y: -116.437; control2X: 68.91; control2Y: -116.189; x: 68.8; y: -115.8 } + , PathCubic { control1X: 69.067; control1Y: -115.8; control2X: 69.38; control2Y: -115.888; x: 69.57; y: -115.756 } + , PathCubic { control1X: 70.628; control1Y: -115.024; control2X: 71.669; control2Y: -114.476; x: 72.366; y: -113.378 } + , PathCubic { control1X: 72.582; control1Y: -113.039; control2X: 72.253; control2Y: -112.632; x: 72.02; y: -112.684 } + , PathCubic { control1X: 67.591; control1Y: -113.679; control2X: 63.585; control2Y: -114.287; x: 59.198; y: -115.391 } + ] + } + + ControlledShape { + fillColor: "#cc7226" + strokeWidth: -1 + delegate: [PathMove { x: 45.338; y: -71.179 } + , PathCubic { control1X: 43.746; control1Y: -72.398; control2X: 43.162; control2Y: -74.429; x: 42.034; y: -76.221 } + , PathCubic { control1X: 41.82; control1Y: -76.561; control2X: 42.094; control2Y: -76.875; x: 42.411; y: -76.964 } + , PathCubic { control1X: 42.971; control1Y: -77.123; control2X: 43.514; control2Y: -76.645; x: 43.923; y: -76.443 } + , PathCubic { control1X: 45.668; control1Y: -75.581; control2X: 47.203; control2Y: -74.339; x: 49.2; y: -74.2 } + , PathCubic { control1X: 51.19; control1Y: -71.966; control2X: 55.45; control2Y: -71.581; x: 55.457; y: -68.2 } + , PathCubic { control1X: 55.458; control1Y: -67.341; control2X: 54.03; control2Y: -68.259; x: 53.6; y: -67.4 } + , PathCubic { control1X: 51.149; control1Y: -68.403; control2X: 48.76; control2Y: -68.3; x: 46.38; y: -69.767 } + , PathCubic { control1X: 45.763; control1Y: -70.148; control2X: 46.093; control2Y: -70.601; x: 45.338; y: -71.179 } + ] + } + + ControlledShape { + fillColor: "#cc7226" + strokeWidth: -1 + delegate: [PathMove { x: 17.8; y: -123.756 } + , PathCubic { control1X: 17.935; control1Y: -123.755; control2X: 24.966; control2Y: -123.522; x: 24.949; y: -123.408 } + , PathCubic { control1X: 24.904; control1Y: -123.099; control2X: 17.174; control2Y: -122.05; x: 16.81; y: -122.22 } + , PathCubic { control1X: 16.646; control1Y: -122.296; control2X: 9.134; control2Y: -119.866; x: 9; y: -120 } + , PathCubic { control1X: 9.268; control1Y: -120.135; control2X: 17.534; control2Y: -123.756; x: 17.8; y: -123.756 } + ] + } + + ControlledShape { + fillColor: "#000000" + strokeWidth: -1 + delegate: [PathMove { x: 33.2; y: -114 } + , PathCubic { control1X: 33.2; control1Y: -114; control2X: 18.4; control2Y: -112.2; x: 14; y: -111 } + , PathCubic { control1X: 9.6; control1Y: -109.8; control2X: -9; control2Y: -102.2; x: -12; y: -100.2 } + , PathCubic { control1X: -12; control1Y: -100.2; control2X: -25.4; control2Y: -94.8; x: -42.4; y: -74.8 } + , PathCubic { control1X: -42.4; control1Y: -74.8; control2X: -34.8; control2Y: -78.2; x: -32.6; y: -81 } + , PathCubic { control1X: -32.6; control1Y: -81; control2X: -19; control2Y: -93.6; x: -19.2; y: -91 } + , PathCubic { control1X: -19.2; control1Y: -91; control2X: -7; control2Y: -99.6; x: -7.6; y: -97.4 } + , PathCubic { control1X: -7.6; control1Y: -97.4; control2X: 16.8; control2Y: -108.6; x: 14.8; y: -105.4 } + , PathCubic { control1X: 14.8; control1Y: -105.4; control2X: 36.4; control2Y: -110; x: 35.4; y: -108 } + , PathCubic { control1X: 35.4; control1Y: -108; control2X: 54.2; control2Y: -103.6; x: 51.4; y: -103.4 } + , PathCubic { control1X: 51.4; control1Y: -103.4; control2X: 45.6; control2Y: -102.2; x: 52; y: -98.6 } + , PathCubic { control1X: 52; control1Y: -98.6; control2X: 48.6; control2Y: -94.2; x: 43.2; y: -98.2 } + , PathCubic { control1X: 37.8; control1Y: -102.2; control2X: 40.8; control2Y: -100; x: 35.8; y: -99 } + , PathCubic { control1X: 35.8; control1Y: -99; control2X: 33.2; control2Y: -98.2; x: 28.6; y: -102.2 } + , PathCubic { control1X: 28.6; control1Y: -102.2; control2X: 23; control2Y: -106.8; x: 14.2; y: -103.2 } + , PathCubic { control1X: 14.2; control1Y: -103.2; control2X: -16.4; control2Y: -90.6; x: -18.4; y: -90 } + , PathCubic { control1X: -18.4; control1Y: -90; control2X: -22; control2Y: -87.2; x: -24.4; y: -83.6 } + , PathCubic { control1X: -24.4; control1Y: -83.6; control2X: -30.2; control2Y: -79.2; x: -33.2; y: -77.8 } + , PathCubic { control1X: -33.2; control1Y: -77.8; control2X: -46; control2Y: -66.2; x: -47.2; y: -64.8 } + , PathCubic { control1X: -47.2; control1Y: -64.8; control2X: -50.6; control2Y: -59.6; x: -51.4; y: -59.2 } + , PathCubic { control1X: -51.4; control1Y: -59.2; control2X: -45; control2Y: -63; x: -43; y: -65 } + , PathCubic { control1X: -43; control1Y: -65; control2X: -29; control2Y: -75; x: -23.6; y: -75.8 } + , PathCubic { control1X: -23.6; control1Y: -75.8; control2X: -19.2; control2Y: -78.8; x: -18.4; y: -80.2 } + , PathCubic { control1X: -18.4; control1Y: -80.2; control2X: -4; control2Y: -89.4; x: 0.2; y: -89.4 } + , PathCubic { control1X: 0.2; control1Y: -89.4; control2X: 9.4; control2Y: -84.2; x: 11.8; y: -91.2 } + , PathCubic { control1X: 11.8; control1Y: -91.2; control2X: 17.6; control2Y: -93; x: 23.2; y: -91.8 } + , PathCubic { control1X: 23.2; control1Y: -91.8; control2X: 26.4; control2Y: -94.4; x: 25.6; y: -96.6 } + , PathCubic { control1X: 25.6; control1Y: -96.6; control2X: 27.2; control2Y: -98.4; x: 28.2; y: -94.6 } + , PathCubic { control1X: 28.2; control1Y: -94.6; control2X: 31.6; control2Y: -91; x: 36.4; y: -93 } + , PathCubic { control1X: 36.4; control1Y: -93; control2X: 40.4; control2Y: -93.2; x: 38.4; y: -90.8 } + , PathCubic { control1X: 38.4; control1Y: -90.8; control2X: 34; control2Y: -87; x: 22.2; y: -86.8 } + , PathCubic { control1X: 22.2; control1Y: -86.8; control2X: 9.8; control2Y: -86.2; x: -6.6; y: -78.6 } + , PathCubic { control1X: -6.6; control1Y: -78.6; control2X: -36.4; control2Y: -68.2; x: -45.6; y: -57.8 } + , PathCubic { control1X: -45.6; control1Y: -57.8; control2X: -52; control2Y: -49; x: -57.4; y: -47.8 } + , PathCubic { control1X: -57.4; control1Y: -47.8; control2X: -63.2; control2Y: -47; x: -69.2; y: -39.6 } + , PathCubic { control1X: -69.2; control1Y: -39.6; control2X: -59.4; control2Y: -45.4; x: -50.4; y: -45.4 } + , PathCubic { control1X: -50.4; control1Y: -45.4; control2X: -46.4; control2Y: -47.8; x: -50.2; y: -44.2 } + , PathCubic { control1X: -50.2; control1Y: -44.2; control2X: -53.8; control2Y: -36.6; x: -52.2; y: -31.2 } + , PathCubic { control1X: -52.2; control1Y: -31.2; control2X: -52.8; control2Y: -26; x: -53.6; y: -24.4 } + , PathCubic { control1X: -53.6; control1Y: -24.4; control2X: -61.4; control2Y: -11.6; x: -61.4; y: -9.2 } + , PathCubic { control1X: -61.4; control1Y: -6.8; control2X: -60.2; control2Y: 3; x: -59.8; y: 3.6 } + , PathCubic { control1X: -59.4; control1Y: 4.2; control2X: -60.8; control2Y: 2; x: -57; y: 4.4 } + , PathCubic { control1X: -53.2; control1Y: 6.8; control2X: -50.4; control2Y: 8.4; x: -49.6; y: 11.2 } + , PathCubic { control1X: -48.8; control1Y: 14; control2X: -51.6; control2Y: 5.8; x: -51.8; y: 4 } + , PathCubic { control1X: -52; control1Y: 2.2; control2X: -56.2; control2Y: -5; x: -55.4; y: -7.4 } + , PathCubic { control1X: -55.4; control1Y: -7.4; control2X: -54.4; control2Y: -6.4; x: -53.6; y: -5 } + , PathCubic { control1X: -53.6; control1Y: -5; control2X: -54.2; control2Y: -5.6; x: -53.6; y: -9.2 } + , PathCubic { control1X: -53.6; control1Y: -9.2; control2X: -52.8; control2Y: -14.4; x: -51.4; y: -17.6 } + , PathCubic { control1X: -50; control1Y: -20.8; control2X: -48; control2Y: -24.6; x: -47.6; y: -25.4 } + , PathCubic { control1X: -47.2; control1Y: -26.2; control2X: -47.2; control2Y: -32; x: -45.8; y: -29.4 } + , PathLine { x: -42.4; y: -26.8 } + , PathCubic { control1X: -42.4; control1Y: -26.8; control2X: -45.2; control2Y: -29.4; x: -43; y: -31.6 } + , PathCubic { control1X: -43; control1Y: -31.6; control2X: -44; control2Y: -37.2; x: -42.2; y: -39.8 } + , PathCubic { control1X: -42.2; control1Y: -39.8; control2X: -35.2; control2Y: -48.2; x: -33.6; y: -49.2 } + , PathCubic { control1X: -32; control1Y: -50.2; control2X: -33.4; control2Y: -49.8; x: -33.4; y: -49.8 } + , PathCubic { control1X: -33.4; control1Y: -49.8; control2X: -27.4; control2Y: -54; x: -33.2; y: -52.4 } + , PathCubic { control1X: -33.2; control1Y: -52.4; control2X: -37.2; control2Y: -50.8; x: -40.2; y: -50.8 } + , PathCubic { control1X: -40.2; control1Y: -50.8; control2X: -47.8; control2Y: -48.8; x: -43.8; y: -53 } + , PathCubic { control1X: -39.8; control1Y: -57.2; control2X: -29.8; control2Y: -62.6; x: -26; y: -62.4 } + , PathLine { x: -25.2; y: -60.8 } + , PathLine { x: -14; y: -63.2 } + , PathLine { x: -15.2; y: -62.4 } + , PathCubic { control1X: -15.2; control1Y: -62.4; control2X: -15.4; control2Y: -62.6; x: -11.2; y: -63 } + , PathCubic { control1X: -7; control1Y: -63.4; control2X: -1.2; control2Y: -62; x: 0.2; y: -63.8 } + , PathCubic { control1X: 1.6; control1Y: -65.6; control2X: 5; control2Y: -66.6; x: 4.6; y: -65.2 } + , PathCubic { control1X: 4.2; control1Y: -63.8; control2X: 4; control2Y: -61.8; x: 4; y: -61.8 } + , PathCubic { control1X: 4; control1Y: -61.8; control2X: 9; control2Y: -67.6; x: 8.4; y: -65.4 } + , PathCubic { control1X: 7.8; control1Y: -63.2; control2X: -0.4; control2Y: -58; x: -1.8; y: -51.8 } + , PathLine { x: 8.6; y: -60 } + , PathLine { x: 12.2; y: -63 } + , PathCubic { control1X: 12.2; control1Y: -63; control2X: 15.8; control2Y: -60.8; x: 16; y: -62.4 } + , PathCubic { control1X: 16.2; control1Y: -64; control2X: 20.8; control2Y: -69.8; x: 22; y: -69.6 } + , PathCubic { control1X: 23.2; control1Y: -69.4; control2X: 25.2; control2Y: -72.2; x: 25; y: -69.6 } + , PathCubic { control1X: 24.8; control1Y: -67; control2X: 32.4; control2Y: -61.6; x: 32.4; y: -61.6 } + , PathCubic { control1X: 32.4; control1Y: -61.6; control2X: 35.6; control2Y: -63.4; x: 37; y: -62 } + , PathCubic { control1X: 38.4; control1Y: -60.6; control2X: 42.6; control2Y: -81.8; x: 42.6; y: -81.8 } + , PathLine { x: 67.6; y: -92.4 } + , PathLine { x: 111.201; y: -95.8 } + , PathLine { x: 94.201; y: -102.6 } + , PathLine { x: 33.2; y: -114 } + ] + } + + ControlledShape { + fillColor: "transparent" + strokeColor: "#4c0000" + strokeWidth: 2 + delegate: [PathMove { x: 51.4; y: 85 } + , PathCubic { control1X: 51.4; control1Y: 85; control2X: 36.4; control2Y: 68.2; x: 28; y: 65.6 } + , PathCubic { control1X: 28; control1Y: 65.6; control2X: 14.6; control2Y: 58.8; x: -10; y: 66.6 } + ] + } + + ControlledShape { + fillColor: "transparent" + strokeColor: "#4c0000" + strokeWidth: 2 + delegate: [PathMove { x: 24.8; y: 64.2 } + , PathCubic { control1X: 24.8; control1Y: 64.2; control2X: -0.4; control2Y: 56.2; x: -15.8; y: 60.4 } + , PathCubic { control1X: -15.8; control1Y: 60.4; control2X: -34.2; control2Y: 62.4; x: -42.6; y: 76.2 } + ] + } + + ControlledShape { + fillColor: "transparent" + strokeColor: "#4c0000" + strokeWidth: 2 + delegate: [PathMove { x: 21.2; y: 63 } + , PathCubic { control1X: 21.2; control1Y: 63; control2X: 4.2; control2Y: 55.8; x: -10.6; y: 53.6 } + , PathCubic { control1X: -10.6; control1Y: 53.6; control2X: -27.2; control2Y: 51; x: -43.8; y: 58.2 } + , PathCubic { control1X: -43.8; control1Y: 58.2; control2X: -56; control2Y: 64.2; x: -61.4; y: 74.4 } + ] + } + + ControlledShape { + fillColor: "transparent" + strokeColor: "#4c0000" + strokeWidth: 2 + delegate: [PathMove { x: 22.2; y: 63.4 } + , PathCubic { control1X: 22.2; control1Y: 63.4; control2X: 6.8; control2Y: 52.4; x: 5.8; y: 51 } + , PathCubic { control1X: 5.8; control1Y: 51; control2X: -1.2; control2Y: 40; x: -14.2; y: 39.6 } + , PathCubic { control1X: -14.2; control1Y: 39.6; control2X: -35.6; control2Y: 40.4; x: -52.8; y: 48.4 } + ] + } + + ControlledShape { + fillColor: "#000000" + strokeWidth: -1 + delegate: [PathMove { x: 20.895; y: 54.407 } + , PathCubic { control1X: 22.437; control1Y: 55.87; control2X: 49.4; control2Y: 84.8; x: 49.4; y: 84.8 } + , PathCubic { control1X: 84.6; control1Y: 121.401; control2X: 56.6; control2Y: 87.2; x: 56.6; y: 87.2 } + , PathCubic { control1X: 49; control1Y: 82.4; control2X: 39.8; control2Y: 63.6; x: 39.8; y: 63.6 } + , PathCubic { control1X: 38.6; control1Y: 60.8; control2X: 53.8; control2Y: 70.8; x: 53.8; y: 70.8 } + , PathCubic { control1X: 57.8; control1Y: 71.6; control2X: 71.4; control2Y: 90.8; x: 71.4; y: 90.8 } + , PathCubic { control1X: 64.6; control1Y: 88.4; control2X: 69.4; control2Y: 95.6; x: 69.4; y: 95.6 } + , PathCubic { control1X: 72.2; control1Y: 97.6; control2X: 92.601; control2Y: 113.201; x: 92.601; y: 113.201 } + , PathCubic { control1X: 96.201; control1Y: 117.201; control2X: 100.201; control2Y: 118.801; x: 100.201; y: 118.801 } + , PathCubic { control1X: 114.201; control1Y: 113.601; control2X: 107.801; control2Y: 126.801; x: 107.801; y: 126.801 } + , PathCubic { control1X: 110.201; control1Y: 133.601; control2X: 115.801; control2Y: 122.001; x: 115.801; y: 122.001 } + , PathCubic { control1X: 127.001; control1Y: 105.2; control2X: 110.601; control2Y: 107.601; x: 110.601; y: 107.601 } + , PathCubic { control1X: 80.6; control1Y: 110.401; control2X: 73.8; control2Y: 94.4; x: 73.8; y: 94.4 } + , PathCubic { control1X: 71.4; control1Y: 92; control2X: 80.2; control2Y: 94.4; x: 80.2; y: 94.4 } + , PathCubic { control1X: 88.601; control1Y: 96.4; control2X: 73; control2Y: 82; x: 73; y: 82 } + , PathCubic { control1X: 75.4; control1Y: 82; control2X: 84.6; control2Y: 88.8; x: 84.6; y: 88.8 } + , PathCubic { control1X: 95.001; control1Y: 98; control2X: 97.001; control2Y: 96; x: 97.001; y: 96 } + , PathCubic { control1X: 115.001; control1Y: 87.2; control2X: 125.401; control2Y: 94.8; x: 125.401; y: 94.8 } + , PathCubic { control1X: 127.401; control1Y: 96.4; control2X: 121.801; control2Y: 103.2; x: 123.401; y: 108.401 } + , PathCubic { control1X: 125.001; control1Y: 113.601; control2X: 129.801; control2Y: 126.001; x: 129.801; y: 126.001 } + , PathCubic { control1X: 127.401; control1Y: 127.601; control2X: 127.801; control2Y: 138.401; x: 127.801; y: 138.401 } + , PathCubic { control1X: 144.601; control1Y: 161.601; control2X: 135.001; control2Y: 159.601; x: 135.001; y: 159.601 } + , PathCubic { control1X: 119.401; control1Y: 159.201; control2X: 134.201; control2Y: 166.801; x: 134.201; y: 166.801 } + , PathCubic { control1X: 137.401; control1Y: 168.801; control2X: 146.201; control2Y: 176.001; x: 146.201; y: 176.001 } + , PathCubic { control1X: 143.401; control1Y: 174.801; control2X: 141.801; control2Y: 180.001; x: 141.801; y: 180.001 } + , PathCubic { control1X: 146.601; control1Y: 184.001; control2X: 143.801; control2Y: 188.801; x: 143.801; y: 188.801 } + , PathCubic { control1X: 137.801; control1Y: 190.001; control2X: 136.601; control2Y: 194.001; x: 136.601; y: 194.001 } + , PathCubic { control1X: 143.401; control1Y: 202.001; control2X: 133.401; control2Y: 202.401; x: 133.401; y: 202.401 } + , PathCubic { control1X: 137.001; control1Y: 206.801; control2X: 132.201; control2Y: 218.801; x: 132.201; y: 218.801 } + , PathCubic { control1X: 127.401; control1Y: 218.801; control2X: 121.001; control2Y: 224.401; x: 121.001; y: 224.401 } + , PathCubic { control1X: 123.401; control1Y: 229.201; control2X: 113.001; control2Y: 234.801; x: 113.001; y: 234.801 } + , PathCubic { control1X: 104.601; control1Y: 236.401; control2X: 107.401; control2Y: 243.201; x: 107.401; y: 243.201 } + , PathCubic { control1X: 99.401; control1Y: 249.201; control2X: 97.001; control2Y: 265.201; x: 97.001; y: 265.201 } + , PathCubic { control1X: 96.201; control1Y: 275.601; control2X: 93.801; control2Y: 278.801; x: 99.001; y: 276.801 } + , PathCubic { control1X: 104.201; control1Y: 274.801; control2X: 103.401; control2Y: 262.401; x: 103.401; y: 262.401 } + , PathCubic { control1X: 98.601; control1Y: 246.801; control2X: 141.401; control2Y: 230.801; x: 141.401; y: 230.801 } + , PathCubic { control1X: 145.401; control1Y: 229.201; control2X: 146.201; control2Y: 224.001; x: 146.201; y: 224.001 } + , PathCubic { control1X: 148.201; control1Y: 224.401; control2X: 157.001; control2Y: 232.001; x: 157.001; y: 232.001 } + , PathCubic { control1X: 164.601; control1Y: 243.201; control2X: 165.001; control2Y: 234.001; x: 165.001; y: 234.001 } + , PathCubic { control1X: 166.201; control1Y: 230.401; control2X: 164.601; control2Y: 224.401; x: 164.601; y: 224.401 } + , PathCubic { control1X: 170.601; control1Y: 202.801; control2X: 156.601; control2Y: 196.401; x: 156.601; y: 196.401 } + , PathCubic { control1X: 146.601; control1Y: 162.801; control2X: 160.601; control2Y: 171.201; x: 160.601; y: 171.201 } + , PathCubic { control1X: 163.401; control1Y: 176.801; control2X: 174.201; control2Y: 182.001; x: 174.201; y: 182.001 } + , PathLine { x: 177.801; y: 179.601 } + , PathCubic { control1X: 176.201; control1Y: 174.801; control2X: 184.601; control2Y: 168.801; x: 184.601; y: 168.801 } + , PathCubic { control1X: 187.401; control1Y: 175.201; control2X: 193.401; control2Y: 167.201; x: 193.401; y: 167.201 } + , PathCubic { control1X: 197.001; control1Y: 142.801; control2X: 209.401; control2Y: 157.201; x: 209.401; y: 157.201 } + , PathCubic { control1X: 213.401; control1Y: 158.401; control2X: 214.601; control2Y: 151.601; x: 214.601; y: 151.601 } + , PathCubic { control1X: 218.201; control1Y: 141.201; control2X: 214.601; control2Y: 127.601; x: 214.601; y: 127.601 } + , PathCubic { control1X: 218.201; control1Y: 127.201; control2X: 227.801; control2Y: 133.201; x: 227.801; y: 133.201 } + , PathCubic { control1X: 230.601; control1Y: 129.601; control2X: 221.401; control2Y: 112.801; x: 225.401; y: 115.201 } + , PathCubic { control1X: 229.401; control1Y: 117.601; control2X: 233.801; control2Y: 119.201; x: 233.801; y: 119.201 } + , PathCubic { control1X: 234.601; control1Y: 117.201; control2X: 224.601; control2Y: 104.801; x: 224.601; y: 104.801 } + , PathCubic { control1X: 220.201; control1Y: 102; control2X: 215.001; control2Y: 81.6; x: 215.001; y: 81.6 } + , PathCubic { control1X: 222.201; control1Y: 85.2; control2X: 212.201; control2Y: 70; x: 212.201; y: 70 } + , PathCubic { control1X: 212.201; control1Y: 66.8; control2X: 218.201; control2Y: 55.6; x: 218.201; y: 55.6 } + , PathCubic { control1X: 217.401; control1Y: 48.8; control2X: 218.201; control2Y: 49.2; x: 218.201; y: 49.2 } + , PathCubic { control1X: 221.001; control1Y: 50.4; control2X: 229.001; control2Y: 52; x: 222.201; y: 45.6 } + , PathCubic { control1X: 215.401; control1Y: 39.2; control2X: 223.001; control2Y: 34.4; x: 223.001; y: 34.4 } + , PathCubic { control1X: 227.401; control1Y: 31.6; control2X: 213.801; control2Y: 32; x: 213.801; y: 32 } + , PathCubic { control1X: 208.601; control1Y: 27.6; control2X: 209.001; control2Y: 23.6; x: 209.001; y: 23.6 } + , PathCubic { control1X: 217.001; control1Y: 25.6; control2X: 202.601; control2Y: 11.2; x: 200.201; y: 7.6 } + , PathCubic { control1X: 197.801; control1Y: 4; control2X: 207.401; control2Y: -1.2; x: 207.401; y: -1.2 } + , PathCubic { control1X: 220.601; control1Y: -4.8; control2X: 209.001; control2Y: -8; x: 209.001; y: -8 } + , PathCubic { control1X: 189.401; control1Y: -7.6; control2X: 200.201; control2Y: -18.4; x: 200.201; y: -18.4 } + , PathCubic { control1X: 206.201; control1Y: -18; control2X: 204.601; control2Y: -20.4; x: 204.601; y: -20.4 } + , PathCubic { control1X: 199.401; control1Y: -21.6; control2X: 189.801; control2Y: -28; x: 189.801; y: -28 } + , PathCubic { control1X: 185.801; control1Y: -31.6; control2X: 189.401; control2Y: -30.8; x: 189.401; y: -30.8 } + , PathCubic { control1X: 206.201; control1Y: -29.6; control2X: 177.401; control2Y: -40.8; x: 177.401; y: -40.8 } + , PathCubic { control1X: 185.401; control1Y: -40.8; control2X: 167.401; control2Y: -51.2; x: 167.401; y: -51.2 } + , PathCubic { control1X: 165.401; control1Y: -52.8; control2X: 162.201; control2Y: -60.4; x: 162.201; y: -60.4 } + , PathCubic { control1X: 156.201; control1Y: -65.6; control2X: 151.401; control2Y: -72.4; x: 151.401; y: -72.4 } + , PathCubic { control1X: 151.001; control1Y: -76.8; control2X: 146.201; control2Y: -81.6; x: 146.201; y: -81.6 } + , PathCubic { control1X: 134.601; control1Y: -95.2; control2X: 129.001; control2Y: -94.8; x: 129.001; y: -94.8 } + , PathCubic { control1X: 114.201; control1Y: -98.4; control2X: 109.001; control2Y: -97.6; x: 109.001; y: -97.6 } + , PathLine { x: 56.2; y: -93.2 } + , PathCubic { control1X: 29.8; control1Y: -80.4; control2X: 37.6; control2Y: -59.4; x: 37.6; y: -59.4 } + , PathCubic { control1X: 44; control1Y: -51; control2X: 53.2; control2Y: -54.8; x: 53.2; y: -54.8 } + , PathCubic { control1X: 57.8; control1Y: -61; control2X: 69.4; control2Y: -58.8; x: 69.4; y: -58.8 } + , PathCubic { control1X: 89.801; control1Y: -55.6; control2X: 87.201; control2Y: -59.2; x: 87.201; y: -59.2 } + , PathCubic { control1X: 84.801; control1Y: -63.8; control2X: 68.6; control2Y: -70; x: 68.4; y: -70.6 } + , PathCubic { control1X: 68.2; control1Y: -71.2; control2X: 59.4; control2Y: -74.6; x: 59.4; y: -74.6 } + , PathCubic { control1X: 56.4; control1Y: -75.8; control2X: 52; control2Y: -85; x: 52; y: -85 } + , PathCubic { control1X: 48.8; control1Y: -88.4; control2X: 64.6; control2Y: -82.6; x: 64.6; y: -82.6 } + , PathCubic { control1X: 63.4; control1Y: -81.6; control2X: 70.8; control2Y: -77.6; x: 70.8; y: -77.6 } + , PathCubic { control1X: 88.201; control1Y: -78.6; control2X: 98.801; control2Y: -67.8; x: 98.801; y: -67.8 } + , PathCubic { control1X: 109.601; control1Y: -51.2; control2X: 109.801; control2Y: -59.4; x: 109.801; y: -59.4 } + , PathCubic { control1X: 112.601; control1Y: -68.8; control2X: 100.801; control2Y: -90; x: 100.801; y: -90 } + , PathCubic { control1X: 101.201; control1Y: -92; control2X: 109.401; control2Y: -85.4; x: 109.401; y: -85.4 } + , PathCubic { control1X: 110.801; control1Y: -87.4; control2X: 111.601; control2Y: -81.6; x: 111.601; y: -81.6 } + , PathCubic { control1X: 111.801; control1Y: -79.2; control2X: 115.601; control2Y: -71.2; x: 115.601; y: -71.2 } + , PathCubic { control1X: 118.401; control1Y: -58.2; control2X: 122.001; control2Y: -65.6; x: 122.001; y: -65.6 } + , PathLine { x: 126.601; y: -56.2 } + , PathCubic { control1X: 128.001; control1Y: -53.6; control2X: 122.001; control2Y: -46; x: 122.001; y: -46 } + , PathCubic { control1X: 121.801; control1Y: -43.2; control2X: 122.601; control2Y: -43.4; x: 117.001; y: -35.8 } + , PathCubic { control1X: 111.401; control1Y: -28.2; control2X: 114.801; control2Y: -23.8; x: 114.801; y: -23.8 } + , PathCubic { control1X: 113.401; control1Y: -17.2; control2X: 122.201; control2Y: -17.6; x: 122.201; y: -17.6 } + , PathCubic { control1X: 124.801; control1Y: -15.4; control2X: 128.201; control2Y: -15.4; x: 128.201; y: -15.4 } + , PathCubic { control1X: 130.001; control1Y: -13.4; control2X: 132.401; control2Y: -14; x: 132.401; y: -14 } + , PathCubic { control1X: 134.001; control1Y: -17.8; control2X: 140.201; control2Y: -15.8; x: 140.201; y: -15.8 } + , PathCubic { control1X: 141.601; control1Y: -18.2; control2X: 149.801; control2Y: -18.6; x: 149.801; y: -18.6 } + , PathCubic { control1X: 150.801; control1Y: -21.2; control2X: 151.201; control2Y: -22.8; x: 154.601; y: -23.4 } + , PathCubic { control1X: 158.001; control1Y: -24; control2X: 133.401; control2Y: -67; x: 133.401; y: -67 } + , PathCubic { control1X: 139.801; control1Y: -67.8; control2X: 131.601; control2Y: -80.2; x: 131.601; y: -80.2 } + , PathCubic { control1X: 129.401; control1Y: -86.8; control2X: 140.801; control2Y: -72.2; x: 143.001; y: -70.8 } + , PathCubic { control1X: 145.201; control1Y: -69.4; control2X: 146.201; control2Y: -67.2; x: 144.601; y: -67.4 } + , PathCubic { control1X: 143.001; control1Y: -67.6; control2X: 141.201; control2Y: -65.4; x: 142.601; y: -65.2 } + , PathCubic { control1X: 144.001; control1Y: -65; control2X: 157.001; control2Y: -50; x: 160.401; y: -39.8 } + , PathCubic { control1X: 163.801; control1Y: -29.6; control2X: 169.801; control2Y: -25.6; x: 176.001; y: -19.6 } + , PathCubic { control1X: 182.201; control1Y: -13.6; control2X: 181.401; control2Y: 10.6; x: 181.401; y: 10.6 } + , PathCubic { control1X: 181.001; control1Y: 19.4; control2X: 187.001; control2Y: 30; x: 187.001; y: 30 } + , PathCubic { control1X: 189.001; control1Y: 33.8; control2X: 184.801; control2Y: 52; x: 184.801; y: 52 } + , PathCubic { control1X: 182.801; control1Y: 54.2; control2X: 184.201; control2Y: 55; x: 184.201; y: 55 } + , PathCubic { control1X: 185.201; control1Y: 56.2; control2X: 192.001; control2Y: 69.4; x: 192.001; y: 69.4 } + , PathCubic { control1X: 190.201; control1Y: 69.2; control2X: 193.801; control2Y: 72.8; x: 193.801; y: 72.8 } + , PathCubic { control1X: 199.001; control1Y: 78.8; control2X: 192.601; control2Y: 75.8; x: 192.601; y: 75.8 } + , PathCubic { control1X: 186.601; control1Y: 74.2; control2X: 193.601; control2Y: 84; x: 193.601; y: 84 } + , PathCubic { control1X: 194.801; control1Y: 85.8; control2X: 185.801; control2Y: 81.2; x: 185.801; y: 81.2 } + , PathCubic { control1X: 176.601; control1Y: 80.6; control2X: 188.201; control2Y: 87.8; x: 188.201; y: 87.8 } + , PathCubic { control1X: 196.801; control1Y: 95; control2X: 185.401; control2Y: 90.6; x: 185.401; y: 90.6 } + , PathCubic { control1X: 180.801; control1Y: 88.8; control2X: 184.001; control2Y: 95.6; x: 184.001; y: 95.6 } + , PathCubic { control1X: 187.201; control1Y: 97.2; control2X: 204.401; control2Y: 104.2; x: 204.401; y: 104.2 } + , PathCubic { control1X: 204.801; control1Y: 108.001; control2X: 201.801; control2Y: 113.001; x: 201.801; y: 113.001 } + , PathCubic { control1X: 202.201; control1Y: 117.001; control2X: 200.001; control2Y: 120.401; x: 200.001; y: 120.401 } + , PathCubic { control1X: 198.801; control1Y: 128.601; control2X: 198.201; control2Y: 129.401; x: 198.201; y: 129.401 } + , PathCubic { control1X: 194.001; control1Y: 129.601; control2X: 186.601; control2Y: 143.401; x: 186.601; y: 143.401 } + , PathCubic { control1X: 184.801; control1Y: 146.001; control2X: 174.601; control2Y: 158.001; x: 174.601; y: 158.001 } + , PathCubic { control1X: 172.601; control1Y: 165.001; control2X: 154.601; control2Y: 157.801; x: 154.601; y: 157.801 } + , PathCubic { control1X: 148.001; control1Y: 161.201; control2X: 150.001; control2Y: 157.801; x: 150.001; y: 157.801 } + , PathCubic { control1X: 149.601; control1Y: 155.601; control2X: 154.401; control2Y: 149.601; x: 154.401; y: 149.601 } + , PathCubic { control1X: 161.401; control1Y: 147.001; control2X: 158.801; control2Y: 136.201; x: 158.801; y: 136.201 } + , PathCubic { control1X: 162.801; control1Y: 134.801; control2X: 151.601; control2Y: 132.001; x: 151.801; y: 130.801 } + , PathCubic { control1X: 152.001; control1Y: 129.601; control2X: 157.801; control2Y: 128.201; x: 157.801; y: 128.201 } + , PathCubic { control1X: 165.801; control1Y: 126.201; control2X: 161.401; control2Y: 123.801; x: 161.401; y: 123.801 } + , PathCubic { control1X: 160.801; control1Y: 119.801; control2X: 163.801; control2Y: 114.201; x: 163.801; y: 114.201 } + , PathCubic { control1X: 175.401; control1Y: 113.401; control2X: 163.801; control2Y: 97.2; x: 163.801; y: 97.2 } + , PathCubic { control1X: 153.001; control1Y: 89.6; control2X: 152.001; control2Y: 83.8; x: 152.001; y: 83.8 } + , PathCubic { control1X: 164.601; control1Y: 75.6; control2X: 156.401; control2Y: 63.2; x: 156.601; y: 59.6 } + , PathCubic { control1X: 156.801; control1Y: 56; control2X: 158.001; control2Y: 34.4; x: 158.001; y: 34.4 } + , PathCubic { control1X: 156.001; control1Y: 28.2; control2X: 153.001; control2Y: 14.6; x: 153.001; y: 14.6 } + , PathCubic { control1X: 155.201; control1Y: 9.4; control2X: 162.601; control2Y: -3.2; x: 162.601; y: -3.2 } + , PathCubic { control1X: 165.401; control1Y: -7.4; control2X: 174.201; control2Y: -12.2; x: 172.001; y: -15.2 } + , PathCubic { control1X: 169.801; control1Y: -18.2; control2X: 162.001; control2Y: -16.4; x: 162.001; y: -16.4 } + , PathCubic { control1X: 154.201; control1Y: -17.8; control2X: 154.801; control2Y: -12.6; x: 154.801; y: -12.6 } + , PathCubic { control1X: 153.201; control1Y: -11.6; control2X: 152.401; control2Y: -6.6; x: 152.401; y: -6.6 } + , PathCubic { control1X: 151.68; control1Y: 1.333; control2X: 142.801; control2Y: 7.6; x: 142.801; y: 7.6 } + , PathCubic { control1X: 131.601; control1Y: 13.8; control2X: 140.801; control2Y: 17.8; x: 140.801; y: 17.8 } + , PathCubic { control1X: 146.801; control1Y: 24.4; control2X: 137.001; control2Y: 24.6; x: 137.001; y: 24.6 } + , PathCubic { control1X: 126.001; control1Y: 22.8; control2X: 134.201; control2Y: 33; x: 134.201; y: 33 } + , PathCubic { control1X: 145.001; control1Y: 45.8; control2X: 142.001; control2Y: 48.6; x: 142.001; y: 48.6 } + , PathCubic { control1X: 131.801; control1Y: 49.6; control2X: 144.401; control2Y: 58.8; x: 144.401; y: 58.8 } + , PathCubic { control1X: 144.401; control1Y: 58.8; control2X: 143.601; control2Y: 56.8; x: 143.801; y: 58.6 } + , PathCubic { control1X: 144.001; control1Y: 60.4; control2X: 147.001; control2Y: 64.6; x: 147.801; y: 66.6 } + , PathCubic { control1X: 148.601; control1Y: 68.6; control2X: 144.601; control2Y: 68.8; x: 144.601; y: 68.8 } + , PathCubic { control1X: 145.201; control1Y: 78.4; control2X: 129.801; control2Y: 74.2; x: 129.801; y: 74.2 } + , PathCubic { control1X: 129.801; control1Y: 74.2; control2X: 129.801; control2Y: 74.2; x: 128.201; y: 74.4 } + , PathCubic { control1X: 126.601; control1Y: 74.6; control2X: 115.401; control2Y: 73.8; x: 109.601; y: 71.6 } + , PathCubic { control1X: 103.801; control1Y: 69.4; control2X: 97.001; control2Y: 69.4; x: 97.001; y: 69.4 } + , PathCubic { control1X: 97.001; control1Y: 69.4; control2X: 93.001; control2Y: 71.2; x: 85.4; y: 71 } + , PathCubic { control1X: 77.8; control1Y: 70.8; control2X: 69.8; control2Y: 73.6; x: 69.8; y: 73.6 } + , PathCubic { control1X: 65.4; control1Y: 73.2; control2X: 74; control2Y: 68.8; x: 74.2; y: 69 } + , PathCubic { control1X: 74.4; control1Y: 69.2; control2X: 80; control2Y: 63.6; x: 72; y: 64.2 } + , PathCubic { control1X: 50.203; control1Y: 65.835; control2X: 39.4; control2Y: 55.6; x: 39.4; y: 55.6 } + , PathCubic { control1X: 37.4; control1Y: 54.2; control2X: 34.8; control2Y: 51.4; x: 34.8; y: 51.4 } + , PathCubic { control1X: 24.8; control1Y: 49.4; control2X: 36.2; control2Y: 63.8; x: 36.2; y: 63.8 } + , PathCubic { control1X: 37.4; control1Y: 65.2; control2X: 36; control2Y: 66.2; x: 36; y: 66.2 } + , PathCubic { control1X: 35.2; control1Y: 64.6; control2X: 27.4; control2Y: 59.2; x: 27.4; y: 59.2 } + , PathCubic { control1X: 24.589; control1Y: 58.227; control2X: 23.226; control2Y: 56.893; x: 20.895; y: 54.407 } + ] + } + + ControlledShape { + fillColor: "#4c0000" + strokeWidth: -1 + delegate: [PathMove { x: -3; y: 42.8 } + , PathCubic { control1X: -3; control1Y: 42.8; control2X: 8.6; control2Y: 48.4; x: 11.2; y: 51.2 } + , PathCubic { control1X: 13.8; control1Y: 54; control2X: 27.8; control2Y: 65.4; x: 27.8; y: 65.4 } + , PathCubic { control1X: 27.8; control1Y: 65.4; control2X: 22.4; control2Y: 63.4; x: 19.8; y: 61.6 } + , PathCubic { control1X: 17.2; control1Y: 59.8; control2X: 6.4; control2Y: 51.6; x: 6.4; y: 51.6 } + , PathCubic { control1X: 6.4; control1Y: 51.6; control2X: 2.6; control2Y: 45.6; x: -3; y: 42.8 } + ] + } + + ControlledShape { + fillColor: "#99cc32" + strokeWidth: -1 + delegate: [PathMove { x: -61.009; y: 11.603 } + , PathCubic { control1X: -60.672; control1Y: 11.455; control2X: -61.196; control2Y: 8.743; x: -61.4; y: 8.2 } + , PathCubic { control1X: -62.422; control1Y: 5.474; control2X: -71.4; control2Y: 4; x: -71.4; y: 4 } + , PathCubic { control1X: -71.627; control1Y: 5.365; control2X: -71.682; control2Y: 6.961; x: -71.576; y: 8.599 } + , PathCubic { control1X: -71.576; control1Y: 8.599; control2X: -66.708; control2Y: 14.118; x: -61.009; y: 11.603 } + ] + } + + ControlledShape { + fillColor: "#659900" + strokeWidth: -1 + delegate: [PathMove { x: -61.009; y: 11.403 } + , PathCubic { control1X: -61.458; control1Y: 11.561; control2X: -61.024; control2Y: 8.669; x: -61.2; y: 8.2 } + , PathCubic { control1X: -62.222; control1Y: 5.474; control2X: -71.4; control2Y: 3.9; x: -71.4; y: 3.9 } + , PathCubic { control1X: -71.627; control1Y: 5.265; control2X: -71.682; control2Y: 6.861; x: -71.576; y: 8.499 } + , PathCubic { control1X: -71.576; control1Y: 8.499; control2X: -67.308; control2Y: 13.618; x: -61.009; y: 11.403 } + ] + } + + ControlledShape { + fillColor: "#000000" + strokeWidth: -1 + delegate: [PathMove { x: -65.4; y: 11.546 } + , PathCubic { control1X: -66.025; control1Y: 11.546; control2X: -66.531; control2Y: 10.406; x: -66.531; y: 9 } + , PathCubic { control1X: -66.531; control1Y: 7.595; control2X: -66.025; control2Y: 6.455; x: -65.4; y: 6.455 } + , PathCubic { control1X: -64.775; control1Y: 6.455; control2X: -64.268; control2Y: 7.595; x: -64.268; y: 9 } + , PathCubic { control1X: -64.268; control1Y: 10.406; control2X: -64.775; control2Y: 11.546; x: -65.4; y: 11.546 } + ] + } + + ControlledShape { + fillColor: "#000000" + strokeWidth: -1 + delegate: [PathMove { x: -65.4; y: 9 } + ] + } + + ControlledShape { + fillColor: "#000000" + strokeWidth: -1 + delegate: [PathMove { x: -111; y: 109.601 } + , PathCubic { control1X: -111; control1Y: 109.601; control2X: -116.6; control2Y: 119.601; x: -91.8; y: 113.601 } + , PathCubic { control1X: -91.8; control1Y: 113.601; control2X: -77.8; control2Y: 112.401; x: -75.4; y: 110.001 } + , PathCubic { control1X: -74.2; control1Y: 110.801; control2X: -65.834; control2Y: 113.734; x: -63; y: 114.401 } + , PathCubic { control1X: -56.2; control1Y: 116.001; control2X: -47.8; control2Y: 106; x: -47.8; y: 106 } + , PathCubic { control1X: -47.8; control1Y: 106; control2X: -43.2; control2Y: 95.5; x: -40.4; y: 95.5 } + , PathCubic { control1X: -37.6; control1Y: 95.5; control2X: -40.8; control2Y: 97.1; x: -40.8; y: 97.1 } + , PathCubic { control1X: -40.8; control1Y: 97.1; control2X: -47.4; control2Y: 107.201; x: -47; y: 108.801 } + , PathCubic { control1X: -47; control1Y: 108.801; control2X: -52.2; control2Y: 128.801; x: -68.2; y: 129.601 } + , PathCubic { control1X: -68.2; control1Y: 129.601; control2X: -84.35; control2Y: 130.551; x: -83; y: 136.401 } + , PathCubic { control1X: -83; control1Y: 136.401; control2X: -74.2; control2Y: 134.001; x: -71.8; y: 136.401 } + , PathCubic { control1X: -71.8; control1Y: 136.401; control2X: -61; control2Y: 136.001; x: -69; y: 142.401 } + , PathLine { x: -75.8; y: 154.001 } + , PathCubic { control1X: -75.8; control1Y: 154.001; control2X: -75.66; control2Y: 157.919; x: -85.8; y: 154.401 } + , PathCubic { control1X: -95.6; control1Y: 151.001; control2X: -105.9; control2Y: 138.101; x: -105.9; y: 138.101 } + , PathCubic { control1X: -105.9; control1Y: 138.101; control2X: -121.85; control2Y: 123.551; x: -111; y: 109.601 } + ] + } + + ControlledShape { + fillColor: "#e59999" + strokeWidth: -1 + delegate: [PathMove { x: -112.2; y: 113.601 } + , PathCubic { control1X: -112.2; control1Y: 113.601; control2X: -114.2; control2Y: 123.201; x: -77.4; y: 112.801 } + , PathCubic { control1X: -77.4; control1Y: 112.801; control2X: -73; control2Y: 112.801; x: -70.6; y: 113.601 } + , PathCubic { control1X: -68.2; control1Y: 114.401; control2X: -56.2; control2Y: 117.201; x: -54.2; y: 116.001 } + , PathCubic { control1X: -54.2; control1Y: 116.001; control2X: -61.4; control2Y: 129.601; x: -73; y: 128.001 } + , PathCubic { control1X: -73; control1Y: 128.001; control2X: -86.2; control2Y: 129.601; x: -85.8; y: 134.401 } + , PathCubic { control1X: -85.8; control1Y: 134.401; control2X: -81.8; control2Y: 141.601; x: -77; y: 144.001 } + , PathCubic { control1X: -77; control1Y: 144.001; control2X: -74.2; control2Y: 146.401; x: -74.6; y: 149.601 } + , PathCubic { control1X: -75; control1Y: 152.801; control2X: -77.8; control2Y: 154.401; x: -79.8; y: 155.201 } + , PathCubic { control1X: -81.8; control1Y: 156.001; control2X: -85; control2Y: 152.801; x: -86.6; y: 152.801 } + , PathCubic { control1X: -88.2; control1Y: 152.801; control2X: -96.6; control2Y: 146.401; x: -101; y: 141.601 } + , PathCubic { control1X: -105.4; control1Y: 136.801; control2X: -113.8; control2Y: 124.801; x: -113.4; y: 122.001 } + , PathCubic { control1X: -113; control1Y: 119.201; control2X: -112.2; control2Y: 113.601; x: -112.2; y: 113.601 } + ] + } + + ControlledShape { + fillColor: "#b26565" + strokeWidth: -1 + delegate: [PathMove { x: -109; y: 131.051 } + , PathCubic { control1X: -106.4; control1Y: 135.001; control2X: -103.2; control2Y: 139.201; x: -101; y: 141.601 } + , PathCubic { control1X: -96.6; control1Y: 146.401; control2X: -88.2; control2Y: 152.801; x: -86.6; y: 152.801 } + , PathCubic { control1X: -85; control1Y: 152.801; control2X: -81.8; control2Y: 156.001; x: -79.8; y: 155.201 } + , PathCubic { control1X: -77.8; control1Y: 154.401; control2X: -75; control2Y: 152.801; x: -74.6; y: 149.601 } + , PathCubic { control1X: -74.2; control1Y: 146.401; control2X: -77; control2Y: 144.001; x: -77; y: 144.001 } + , PathCubic { control1X: -80.066; control1Y: 142.468; control2X: -82.806; control2Y: 138.976; x: -84.385; y: 136.653 } + , PathCubic { control1X: -84.385; control1Y: 136.653; control2X: -84.2; control2Y: 139.201; x: -89.4; y: 138.401 } + , PathCubic { control1X: -94.6; control1Y: 137.601; control2X: -99.8; control2Y: 134.801; x: -101.4; y: 131.601 } + , PathCubic { control1X: -103; control1Y: 128.401; control2X: -105.4; control2Y: 126.001; x: -103.8; y: 129.601 } + , PathCubic { control1X: -102.2; control1Y: 133.201; control2X: -99.8; control2Y: 136.801; x: -98.2; y: 137.201 } + , PathCubic { control1X: -96.6; control1Y: 137.601; control2X: -97; control2Y: 138.801; x: -99.4; y: 138.401 } + , PathCubic { control1X: -101.8; control1Y: 138.001; control2X: -104.6; control2Y: 137.601; x: -109; y: 132.401 } + ] + } + + ControlledShape { + fillColor: "#992600" + strokeWidth: -1 + delegate: [PathMove { x: -111.6; y: 110.001 } + , PathCubic { control1X: -111.6; control1Y: 110.001; control2X: -109.8; control2Y: 96.4; x: -108.6; y: 92.4 } + , PathCubic { control1X: -108.6; control1Y: 92.4; control2X: -109.4; control2Y: 85.6; x: -107; y: 81.4 } + , PathCubic { control1X: -104.6; control1Y: 77.2; control2X: -102.6; control2Y: 71; x: -99.6; y: 65.6 } + , PathCubic { control1X: -96.6; control1Y: 60.2; control2X: -96.4; control2Y: 56.2; x: -92.4; y: 54.6 } + , PathCubic { control1X: -88.4; control1Y: 53; control2X: -82.4; control2Y: 44.4; x: -79.6; y: 43.4 } + , PathCubic { control1X: -76.8; control1Y: 42.4; control2X: -77; control2Y: 43.2; x: -77; y: 43.2 } + , PathCubic { control1X: -77; control1Y: 43.2; control2X: -70.2; control2Y: 28.4; x: -56.6; y: 32.4 } + , PathCubic { control1X: -56.6; control1Y: 32.4; control2X: -72.8; control2Y: 29.6; x: -57; y: 20.2 } + , PathCubic { control1X: -57; control1Y: 20.2; control2X: -61.8; control2Y: 21.3; x: -58.5; y: 14.3 } + , PathCubic { control1X: -56.299; control1Y: 9.632; control2X: -56.8; control2Y: 16.4; x: -67.8; y: 28.2 } + , PathCubic { control1X: -67.8; control1Y: 28.2; control2X: -72.8; control2Y: 36.8; x: -78; y: 39.8 } + , PathCubic { control1X: -83.2; control1Y: 42.8; control2X: -95.2; control2Y: 49.8; x: -96.4; y: 53.6 } + , PathCubic { control1X: -97.6; control1Y: 57.4; control2X: -100.8; control2Y: 63.2; x: -102.8; y: 64.8 } + , PathCubic { control1X: -104.8; control1Y: 66.4; control2X: -107.6; control2Y: 70.6; x: -108; y: 74 } + , PathCubic { control1X: -108; control1Y: 74; control2X: -109.2; control2Y: 78; x: -110.6; y: 79.2 } + , PathCubic { control1X: -112; control1Y: 80.4; control2X: -112.2; control2Y: 83.6; x: -112.2; y: 85.6 } + , PathCubic { control1X: -112.2; control1Y: 87.6; control2X: -114.2; control2Y: 90.4; x: -114; y: 92.8 } + , PathCubic { control1X: -114; control1Y: 92.8; control2X: -113.2; control2Y: 111.801; x: -113.6; y: 113.801 } + , PathLine { x: -111.6; y: 110.001 } + ] + } + + ControlledShape { + fillColor: "#ffffff" + strokeWidth: -1 + delegate: [PathMove { x: -120.2; y: 114.601 } + , PathCubic { control1X: -120.2; control1Y: 114.601; control2X: -122.2; control2Y: 113.201; x: -126.6; y: 119.201 } + , PathCubic { control1X: -126.6; control1Y: 119.201; control2X: -119.3; control2Y: 152.201; x: -119.3; y: 153.601 } + , PathCubic { control1X: -119.3; control1Y: 153.601; control2X: -118.2; control2Y: 151.501; x: -119.5; y: 144.301 } + , PathCubic { control1X: -120.8; control1Y: 137.101; control2X: -121.7; control2Y: 124.401; x: -121.7; y: 124.401 } + , PathLine { x: -120.2; y: 114.601 } + ] + } + + ControlledShape { + fillColor: "#992600" + strokeWidth: -1 + delegate: [PathMove { x: -98.6; y: 54 } + , PathCubic { control1X: -98.6; control1Y: 54; control2X: -116.2; control2Y: 57.2; x: -115.8; y: 86.4 } + , PathLine { x: -116.6; y: 111.201 } + , PathCubic { control1X: -116.6; control1Y: 111.201; control2X: -117.8; control2Y: 85.6; x: -119; y: 84 } + , PathCubic { control1X: -120.2; control1Y: 82.4; control2X: -116.2; control2Y: 71.2; x: -119.4; y: 77.2 } + , PathCubic { control1X: -119.4; control1Y: 77.2; control2X: -133.4; control2Y: 91.2; x: -125.4; y: 112.401 } + , PathCubic { control1X: -125.4; control1Y: 112.401; control2X: -123.9; control2Y: 115.701; x: -126.9; y: 111.101 } + , PathCubic { control1X: -126.9; control1Y: 111.101; control2X: -131.5; control2Y: 98.5; x: -130.4; y: 92.1 } + , PathCubic { control1X: -130.4; control1Y: 92.1; control2X: -130.2; control2Y: 89.9; x: -128.3; y: 87.1 } + , PathCubic { control1X: -128.3; control1Y: 87.1; control2X: -119.7; control2Y: 75.4; x: -117; y: 73.1 } + , PathCubic { control1X: -117; control1Y: 73.1; control2X: -115.2; control2Y: 58.7; x: -99.8; y: 53.5 } + , PathCubic { control1X: -99.8; control1Y: 53.5; control2X: -94.1; control2Y: 51.2; x: -98.6; y: 54 } + ] + } + + ControlledShape { + fillColor: "#000000" + strokeWidth: -1 + delegate: [PathMove { x: 40.8; y: -12.2 } + , PathCubic { control1X: 41.46; control1Y: -12.554; control2X: 41.451; control2Y: -13.524; x: 42.031; y: -13.697 } + , PathCubic { control1X: 43.18; control1Y: -14.041; control2X: 43.344; control2Y: -15.108; x: 43.862; y: -15.892 } + , PathCubic { control1X: 44.735; control1Y: -17.211; control2X: 44.928; control2Y: -18.744; x: 45.51; y: -20.235 } + , PathCubic { control1X: 45.782; control1Y: -20.935; control2X: 45.809; control2Y: -21.89; x: 45.496; y: -22.55 } + , PathCubic { control1X: 44.322; control1Y: -25.031; control2X: 43.62; control2Y: -27.48; x: 42.178; y: -29.906 } + , PathCubic { control1X: 41.91; control1Y: -30.356; control2X: 41.648; control2Y: -31.15; x: 41.447; y: -31.748 } + , PathCubic { control1X: 40.984; control1Y: -33.132; control2X: 39.727; control2Y: -34.123; x: 38.867; y: -35.443 } + , PathCubic { control1X: 38.579; control1Y: -35.884; control2X: 39.104; control2Y: -36.809; x: 38.388; y: -36.893 } + , PathCubic { control1X: 37.491; control1Y: -36.998; control2X: 36.042; control2Y: -37.578; x: 35.809; y: -36.552 } + , PathCubic { control1X: 35.221; control1Y: -33.965; control2X: 36.232; control2Y: -31.442; x: 37.2; y: -29 } + , PathCubic { control1X: 36.418; control1Y: -28.308; control2X: 36.752; control2Y: -27.387; x: 36.904; y: -26.62 } + , PathCubic { control1X: 37.614; control1Y: -23.014; control2X: 36.416; control2Y: -19.662; x: 35.655; y: -16.188 } + , PathCubic { control1X: 35.632; control1Y: -16.084; control2X: 35.974; control2Y: -15.886; x: 35.946; y: -15.824 } + , PathCubic { control1X: 34.724; control1Y: -13.138; control2X: 33.272; control2Y: -10.693; x: 31.453; y: -8.312 } + , PathCubic { control1X: 30.695; control1Y: -7.32; control2X: 29.823; control2Y: -6.404; x: 29.326; y: -5.341 } + , PathCubic { control1X: 28.958; control1Y: -4.554; control2X: 28.55; control2Y: -3.588; x: 28.8; y: -2.6 } + , PathCubic { control1X: 25.365; control1Y: 0.18; control2X: 23.115; control2Y: 4.025; x: 20.504; y: 7.871 } + , PathCubic { control1X: 20.042; control1Y: 8.551; control2X: 20.333; control2Y: 9.76; x: 20.884; y: 10.029 } + , PathCubic { control1X: 21.697; control1Y: 10.427; control2X: 22.653; control2Y: 9.403; x: 23.123; y: 8.557 } + , PathCubic { control1X: 23.512; control1Y: 7.859; control2X: 23.865; control2Y: 7.209; x: 24.356; y: 6.566 } + , PathCubic { control1X: 24.489; control1Y: 6.391; control2X: 24.31; control2Y: 5.972; x: 24.445; y: 5.851 } + , PathCubic { control1X: 27.078; control1Y: 3.504; control2X: 28.747; control2Y: 0.568; x: 31.2; y: -1.8 } + , PathCubic { control1X: 33.15; control1Y: -2.129; control2X: 34.687; control2Y: -3.127; x: 36.435; y: -4.14 } + , PathCubic { control1X: 36.743; control1Y: -4.319; control2X: 37.267; control2Y: -4.07; x: 37.557; y: -4.265 } + , PathCubic { control1X: 39.31; control1Y: -5.442; control2X: 39.308; control2Y: -7.478; x: 39.414; y: -9.388 } + , PathCubic { control1X: 39.464; control1Y: -10.272; control2X: 39.66; control2Y: -11.589; x: 40.8; y: -12.2 } + ] + } + + ControlledShape { + fillColor: "#000000" + strokeWidth: -1 + delegate: [PathMove { x: 31.959; y: -16.666 } + , PathCubic { control1X: 32.083; control1Y: -16.743; control2X: 31.928; control2Y: -17.166; x: 32.037; y: -17.382 } + , PathCubic { control1X: 32.199; control1Y: -17.706; control2X: 32.602; control2Y: -17.894; x: 32.764; y: -18.218 } + , PathCubic { control1X: 32.873; control1Y: -18.434; control2X: 32.71; control2Y: -18.814; x: 32.846; y: -18.956 } + , PathCubic { control1X: 35.179; control1Y: -21.403; control2X: 35.436; control2Y: -24.427; x: 34.4; y: -27.4 } + , PathCubic { control1X: 35.424; control1Y: -28.02; control2X: 35.485; control2Y: -29.282; x: 35.06; y: -30.129 } + , PathCubic { control1X: 34.207; control1Y: -31.829; control2X: 34.014; control2Y: -33.755; x: 33.039; y: -35.298 } + , PathCubic { control1X: 32.237; control1Y: -36.567; control2X: 30.659; control2Y: -37.811; x: 29.288; y: -36.508 } + , PathCubic { control1X: 28.867; control1Y: -36.108; control2X: 28.546; control2Y: -35.321; x: 28.824; y: -34.609 } + , PathCubic { control1X: 28.888; control1Y: -34.446; control2X: 29.173; control2Y: -34.3; x: 29.146; y: -34.218 } + , PathCubic { control1X: 29.039; control1Y: -33.894; control2X: 28.493; control2Y: -33.67; x: 28.487; y: -33.398 } + , PathCubic { control1X: 28.457; control1Y: -31.902; control2X: 27.503; control2Y: -30.391; x: 28.133; y: -29.062 } + , PathCubic { control1X: 28.905; control1Y: -27.433; control2X: 29.724; control2Y: -25.576; x: 30.4; y: -23.8 } + , PathCubic { control1X: 29.166; control1Y: -21.684; control2X: 30.199; control2Y: -19.235; x: 28.446; y: -17.358 } + , PathCubic { control1X: 28.31; control1Y: -17.212; control2X: 28.319; control2Y: -16.826; x: 28.441; y: -16.624 } + , PathCubic { control1X: 28.733; control1Y: -16.138; control2X: 29.139; control2Y: -15.732; x: 29.625; y: -15.44 } + , PathCubic { control1X: 29.827; control1Y: -15.319; control2X: 30.175; control2Y: -15.317; x: 30.375; y: -15.441 } + , PathCubic { control1X: 30.953; control1Y: -15.803; control2X: 31.351; control2Y: -16.29; x: 31.959; y: -16.666 } + ] + } + + ControlledShape { + fillColor: "#000000" + strokeWidth: -1 + delegate: [PathMove { x: 94.771; y: -26.977 } + , PathCubic { control1X: 96.16; control1Y: -25.185; control2X: 96.45; control2Y: -22.39; x: 94.401; y: -21 } + , PathCubic { control1X: 94.951; control1Y: -17.691; control2X: 98.302; control2Y: -19.67; x: 100.401; y: -20.2 } + , PathCubic { control1X: 100.292; control1Y: -20.588; control2X: 100.519; control2Y: -20.932; x: 100.802; y: -20.937 } + , PathCubic { control1X: 101.859; control1Y: -20.952; control2X: 102.539; control2Y: -21.984; x: 103.601; y: -21.8 } + , PathCubic { control1X: 104.035; control1Y: -23.357; control2X: 105.673; control2Y: -24.059; x: 106.317; y: -25.439 } + , PathCubic { control1X: 108.043; control1Y: -29.134; control2X: 107.452; control2Y: -33.407; x: 104.868; y: -36.653 } + , PathCubic { control1X: 104.666; control1Y: -36.907; control2X: 104.883; control2Y: -37.424; x: 104.759; y: -37.786 } + , PathCubic { control1X: 104.003; control1Y: -39.997; control2X: 101.935; control2Y: -40.312; x: 100.001; y: -41 } + , PathCubic { control1X: 98.824; control1Y: -44.875; control2X: 98.163; control2Y: -48.906; x: 96.401; y: -52.6 } + , PathCubic { control1X: 94.787; control1Y: -52.85; control2X: 94.089; control2Y: -54.589; x: 92.752; y: -55.309 } + , PathCubic { control1X: 91.419; control1Y: -56.028; control2X: 90.851; control2Y: -54.449; x: 90.892; y: -53.403 } + , PathCubic { control1X: 90.899; control1Y: -53.198; control2X: 91.351; control2Y: -52.974; x: 91.181; y: -52.609 } + , PathCubic { control1X: 91.105; control1Y: -52.445; control2X: 90.845; control2Y: -52.334; x: 90.845; y: -52.2 } + , PathCubic { control1X: 90.846; control1Y: -52.065; control2X: 91.067; control2Y: -51.934; x: 91.201; y: -51.8 } + , PathCubic { control1X: 90.283; control1Y: -50.98; control2X: 88.86; control2Y: -50.503; x: 88.565; y: -49.358 } + , PathCubic { control1X: 87.611; control1Y: -45.648; control2X: 90.184; control2Y: -42.523; x: 91.852; y: -39.322 } + , PathCubic { control1X: 92.443; control1Y: -38.187; control2X: 91.707; control2Y: -36.916; x: 90.947; y: -35.708 } + , PathCubic { control1X: 90.509; control1Y: -35.013; control2X: 90.617; control2Y: -33.886; x: 90.893; y: -33.03 } + , PathCubic { control1X: 91.645; control1Y: -30.699; control2X: 93.236; control2Y: -28.96; x: 94.771; y: -26.977 } + ] + } + + ControlledShape { + fillColor: "#000000" + strokeWidth: -1 + delegate: [PathMove { x: 57.611; y: -8.591 } + , PathCubic { control1X: 56.124; control1Y: -6.74; control2X: 52.712; control2Y: -4.171; x: 55.629; y: -2.243 } + , PathCubic { control1X: 55.823; control1Y: -2.114; control2X: 56.193; control2Y: -2.11; x: 56.366; y: -2.244 } + , PathCubic { control1X: 58.387; control1Y: -3.809; control2X: 60.39; control2Y: -4.712; x: 62.826; y: -5.294 } + , PathCubic { control1X: 62.95; control1Y: -5.323; control2X: 63.224; control2Y: -4.856; x: 63.593; y: -5.017 } + , PathCubic { control1X: 65.206; control1Y: -5.72; control2X: 67.216; control2Y: -5.662; x: 68.4; y: -7 } + , PathCubic { control1X: 72.167; control1Y: -6.776; control2X: 75.732; control2Y: -7.892; x: 79.123; y: -9.2 } + , PathCubic { control1X: 80.284; control1Y: -9.648; control2X: 81.554; control2Y: -10.207; x: 82.755; y: -10.709 } + , PathCubic { control1X: 84.131; control1Y: -11.285; control2X: 85.335; control2Y: -12.213; x: 86.447; y: -13.354 } + , PathCubic { control1X: 86.58; control1Y: -13.49; control2X: 86.934; control2Y: -13.4; x: 87.201; y: -13.4 } + , PathCubic { control1X: 87.161; control1Y: -14.263; control2X: 88.123; control2Y: -14.39; x: 88.37; y: -15.012 } + , PathCubic { control1X: 88.462; control1Y: -15.244; control2X: 88.312; control2Y: -15.64; x: 88.445; y: -15.742 } + , PathCubic { control1X: 90.583; control1Y: -17.372; control2X: 91.503; control2Y: -19.39; x: 90.334; y: -21.767 } + , PathCubic { control1X: 90.049; control1Y: -22.345; control2X: 89.8; control2Y: -22.963; x: 89.234; y: -23.439 } + , PathCubic { control1X: 88.149; control1Y: -24.35; control2X: 87.047; control2Y: -23.496; x: 86; y: -23.8 } + , PathCubic { control1X: 85.841; control1Y: -23.172; control2X: 85.112; control2Y: -23.344; x: 84.726; y: -23.146 } + , PathCubic { control1X: 83.867; control1Y: -22.707; control2X: 82.534; control2Y: -23.292; x: 81.675; y: -22.854 } + , PathCubic { control1X: 80.313; control1Y: -22.159; control2X: 79.072; control2Y: -21.99; x: 77.65; y: -21.613 } + , PathCubic { control1X: 77.338; control1Y: -21.531; control2X: 76.56; control2Y: -21.627; x: 76.4; y: -21 } + , PathCubic { control1X: 76.266; control1Y: -21.134; control2X: 76.118; control2Y: -21.368; x: 76.012; y: -21.346 } + , PathCubic { control1X: 74.104; control1Y: -20.95; control2X: 72.844; control2Y: -20.736; x: 71.543; y: -19.044 } + , PathCubic { control1X: 71.44; control1Y: -18.911; control2X: 70.998; control2Y: -19.09; x: 70.839; y: -18.955 } + , PathCubic { control1X: 69.882; control1Y: -18.147; control2X: 69.477; control2Y: -16.913; x: 68.376; y: -16.241 } + , PathCubic { control1X: 68.175; control1Y: -16.118; control2X: 67.823; control2Y: -16.286; x: 67.629; y: -16.157 } + , PathCubic { control1X: 66.983; control1Y: -15.726; control2X: 66.616; control2Y: -15.085; x: 65.974; y: -14.638 } + , PathCubic { control1X: 65.645; control1Y: -14.409; control2X: 65.245; control2Y: -14.734; x: 65.277; y: -14.99 } + , PathCubic { control1X: 65.522; control1Y: -16.937; control2X: 66.175; control2Y: -18.724; x: 65.6; y: -20.6 } + , PathCubic { control1X: 67.677; control1Y: -23.12; control2X: 70.194; control2Y: -25.069; x: 72; y: -27.8 } + , PathCubic { control1X: 72.015; control1Y: -29.966; control2X: 72.707; control2Y: -32.112; x: 72.594; y: -34.189 } + , PathCubic { control1X: 72.584; control1Y: -34.382; control2X: 72.296; control2Y: -35.115; x: 72.17; y: -35.462 } + , PathCubic { control1X: 71.858; control1Y: -36.316; control2X: 72.764; control2Y: -37.382; x: 71.92; y: -38.106 } + , PathCubic { control1X: 70.516; control1Y: -39.309; control2X: 69.224; control2Y: -38.433; x: 68.4; y: -37 } + , PathCubic { control1X: 66.562; control1Y: -36.61; control2X: 64.496; control2Y: -35.917; x: 62.918; y: -37.151 } + , PathCubic { control1X: 61.911; control1Y: -37.938; control2X: 61.333; control2Y: -38.844; x: 60.534; y: -39.9 } + , PathCubic { control1X: 59.549; control1Y: -41.202; control2X: 59.884; control2Y: -42.638; x: 59.954; y: -44.202 } + , PathCubic { control1X: 59.96; control1Y: -44.33; control2X: 59.645; control2Y: -44.466; x: 59.645; y: -44.6 } + , PathCubic { control1X: 59.646; control1Y: -44.735; control2X: 59.866; control2Y: -44.866; x: 60; y: -45 } + , PathCubic { control1X: 59.294; control1Y: -45.626; control2X: 59.019; control2Y: -46.684; x: 58; y: -47 } + , PathCubic { control1X: 58.305; control1Y: -48.092; control2X: 57.629; control2Y: -48.976; x: 56.758; y: -49.278 } + , PathCubic { control1X: 54.763; control1Y: -49.969; control2X: 53.086; control2Y: -48.057; x: 51.194; y: -47.984 } + , PathCubic { control1X: 50.68; control1Y: -47.965; control2X: 50.213; control2Y: -49.003; x: 49.564; y: -49.328 } + , PathCubic { control1X: 49.132; control1Y: -49.544; control2X: 48.428; control2Y: -49.577; x: 48.066; y: -49.311 } + , PathCubic { control1X: 47.378; control1Y: -48.807; control2X: 46.789; control2Y: -48.693; x: 46.031; y: -48.488 } + , PathCubic { control1X: 44.414; control1Y: -48.052; control2X: 43.136; control2Y: -46.958; x: 41.656; y: -46.103 } + , PathCubic { control1X: 40.171; control1Y: -45.246; control2X: 39.216; control2Y: -43.809; x: 38.136; y: -42.489 } + , PathCubic { control1X: 37.195; control1Y: -41.337; control2X: 37.059; control2Y: -38.923; x: 38.479; y: -38.423 } + , PathCubic { control1X: 40.322; control1Y: -37.773; control2X: 41.626; control2Y: -40.476; x: 43.592; y: -40.15 } + , PathCubic { control1X: 43.904; control1Y: -40.099; control2X: 44.11; control2Y: -39.788; x: 44; y: -39.4 } + , PathCubic { control1X: 44.389; control1Y: -39.291; control2X: 44.607; control2Y: -39.52; x: 44.8; y: -39.8 } + , PathCubic { control1X: 45.658; control1Y: -38.781; control2X: 46.822; control2Y: -38.444; x: 47.76; y: -37.571 } + , PathCubic { control1X: 48.73; control1Y: -36.667; control2X: 50.476; control2Y: -37.085; x: 51.491; y: -36.088 } + , PathCubic { control1X: 53.02; control1Y: -34.586; control2X: 52.461; control2Y: -31.905; x: 54.4; y: -30.6 } + , PathCubic { control1X: 53.814; control1Y: -29.287; control2X: 53.207; control2Y: -28.01; x: 52.872; y: -26.583 } + , PathCubic { control1X: 52.59; control1Y: -25.377; control2X: 53.584; control2Y: -24.18; x: 54.795; y: -24.271 } + , PathCubic { control1X: 56.053; control1Y: -24.365; control2X: 56.315; control2Y: -25.124; x: 56.8; y: -26.2 } + , PathCubic { control1X: 57.067; control1Y: -25.933; control2X: 57.536; control2Y: -25.636; x: 57.495; y: -25.42 } + , PathCubic { control1X: 57.038; control1Y: -23.033; control2X: 56.011; control2Y: -21.04; x: 55.553; y: -18.609 } + , PathCubic { control1X: 55.494; control1Y: -18.292; control2X: 55.189; control2Y: -18.09; x: 54.8; y: -18.2 } + , PathCubic { control1X: 54.332; control1Y: -14.051; control2X: 50.28; control2Y: -11.657; x: 47.735; y: -8.492 } + , PathCubic { control1X: 47.332; control1Y: -7.99; control2X: 47.328; control2Y: -6.741; x: 47.737; y: -6.338 } + , PathCubic { control1X: 49.14; control1Y: -4.951; control2X: 51.1; control2Y: -6.497; x: 52.8; y: -7 } + , PathCubic { control1X: 53.013; control1Y: -8.206; control2X: 53.872; control2Y: -9.148; x: 55.204; y: -9.092 } + , PathCubic { control1X: 55.46; control1Y: -9.082; control2X: 55.695; control2Y: -9.624; x: 56.019; y: -9.754 } + , PathCubic { control1X: 56.367; control1Y: -9.892; control2X: 56.869; control2Y: -9.668; x: 57.155; y: -9.866 } + , PathCubic { control1X: 58.884; control1Y: -11.061; control2X: 60.292; control2Y: -12.167; x: 62.03; y: -13.356 } + , PathCubic { control1X: 62.222; control1Y: -13.487; control2X: 62.566; control2Y: -13.328; x: 62.782; y: -13.436 } + , PathCubic { control1X: 63.107; control1Y: -13.598; control2X: 63.294; control2Y: -13.985; x: 63.617; y: -14.17 } + , PathCubic { control1X: 63.965; control1Y: -14.37; control2X: 64.207; control2Y: -14.08; x: 64.4; y: -13.8 } + , PathCubic { control1X: 63.754; control1Y: -13.451; control2X: 63.75; control2Y: -12.494; x: 63.168; y: -12.292 } + , PathCubic { control1X: 62.393; control1Y: -12.024; control2X: 61.832; control2Y: -11.511; x: 61.158; y: -11.064 } + , PathCubic { control1X: 60.866; control1Y: -10.871; control2X: 60.207; control2Y: -11.119; x: 60.103; y: -10.94 } + , PathCubic { control1X: 59.505; control1Y: -9.912; control2X: 58.321; control2Y: -9.474; x: 57.611; y: -8.591 } + ] + } + + ControlledShape { + fillColor: "#000000" + strokeWidth: -1 + delegate: [PathMove { x: 2.2; y: -58 } + , PathCubic { control1X: 2.2; control1Y: -58; control2X: -7.038; control2Y: -60.872; x: -18.2; y: -35.2 } + , PathCubic { control1X: -18.2; control1Y: -35.2; control2X: -20.6; control2Y: -30; x: -23; y: -28 } + , PathCubic { control1X: -25.4; control1Y: -26; control2X: -36.6; control2Y: -22.4; x: -38.6; y: -18.4 } + , PathLine { x: -49; y: -2.4 } + , PathCubic { control1X: -49; control1Y: -2.4; control2X: -34.2; control2Y: -18.4; x: -31; y: -20.8 } + , PathCubic { control1X: -31; control1Y: -20.8; control2X: -23; control2Y: -29.2; x: -26.2; y: -22.4 } + , PathCubic { control1X: -26.2; control1Y: -22.4; control2X: -40.2; control2Y: -11.6; x: -39; y: -2.4 } + , PathCubic { control1X: -39; control1Y: -2.4; control2X: -44.6; control2Y: 12; x: -45.4; y: 14 } + , PathCubic { control1X: -45.4; control1Y: 14; control2X: -29.4; control2Y: -18; x: -27; y: -19.2 } + , PathCubic { control1X: -24.6; control1Y: -20.4; control2X: -23.4; control2Y: -20.4; x: -24.6; y: -16.8 } + , PathCubic { control1X: -25.8; control1Y: -13.2; control2X: -26.2; control2Y: 3.2; x: -29; y: 5.2 } + , PathCubic { control1X: -29; control1Y: 5.2; control2X: -21; control2Y: -15.2; x: -21.8; y: -18.4 } + , PathCubic { control1X: -21.8; control1Y: -18.4; control2X: -18.6; control2Y: -22; x: -16.2; y: -16.8 } + , PathLine { x: -17.4; y: -0.8 } + , PathLine { x: -13; y: 11.2 } + , PathCubic { control1X: -13; control1Y: 11.2; control2X: -15.4; control2Y: 0; x: -13.8; y: -15.6 } + , PathCubic { control1X: -13.8; control1Y: -15.6; control2X: -15.8; control2Y: -26; x: -11.8; y: -20.4 } + , PathCubic { control1X: -7.8; control1Y: -14.8; control2X: 1.8; control2Y: -8.8; x: 1.8; y: -4 } + , PathCubic { control1X: 1.8; control1Y: -4; control2X: -3.4; control2Y: -21.6; x: -12.6; y: -26.4 } + , PathLine { x: -16.6; y: -20.4 } + , PathLine { x: -17.8; y: -22.4 } + , PathCubic { control1X: -17.8; control1Y: -22.4; control2X: -21.4; control2Y: -23.2; x: -17; y: -30 } + , PathCubic { control1X: -12.6; control1Y: -36.8; control2X: -13; control2Y: -37.6; x: -13; y: -37.6 } + , PathCubic { control1X: -13; control1Y: -37.6; control2X: -6.6; control2Y: -30.4; x: -5; y: -30.4 } + , PathCubic { control1X: -5; control1Y: -30.4; control2X: 8.2; control2Y: -38; x: 9.4; y: -13.6 } + , PathCubic { control1X: 9.4; control1Y: -13.6; control2X: 16.2; control2Y: -28; x: 7; y: -34.8 } + , PathCubic { control1X: 7; control1Y: -34.8; control2X: -7.8; control2Y: -36.8; x: -6.6; y: -42 } + , PathLine { x: 0.6; y: -54.4 } + , PathCubic { control1X: 4.2; control1Y: -59.6; control2X: 2.6; control2Y: -56.8; x: 2.6; y: -56.8 } + ] + } + + ControlledShape { + fillColor: "#000000" + strokeWidth: -1 + delegate: [PathMove { x: -17.8; y: -41.6 } + , PathCubic { control1X: -17.8; control1Y: -41.6; control2X: -30.6; control2Y: -41.6; x: -33.8; y: -36.4 } + , PathLine { x: -41; y: -26.8 } + , PathCubic { control1X: -41; control1Y: -26.8; control2X: -23.8; control2Y: -36.8; x: -19.8; y: -38 } + , PathCubic { control1X: -15.8; control1Y: -39.2; control2X: -17.8; control2Y: -41.6; x: -17.8; y: -41.6 } + ] + } + + ControlledShape { + fillColor: "#000000" + strokeWidth: -1 + delegate: [PathMove { x: -57.8; y: -35.2 } + , PathCubic { control1X: -57.8; control1Y: -35.2; control2X: -59.8; control2Y: -34; x: -60.2; y: -31.2 } + , PathCubic { control1X: -60.6; control1Y: -28.4; control2X: -63; control2Y: -28; x: -62.2; y: -25.2 } + , PathCubic { control1X: -61.4; control1Y: -22.4; control2X: -59.4; control2Y: -20; x: -59.4; y: -24 } + , PathCubic { control1X: -59.4; control1Y: -28; control2X: -57.8; control2Y: -30; x: -57; y: -31.2 } + , PathCubic { control1X: -56.2; control1Y: -32.4; control2X: -54.6; control2Y: -36.8; x: -57.8; y: -35.2 } + ] + } + + ControlledShape { + fillColor: "#000000" + strokeWidth: -1 + delegate: [PathMove { x: -66.6; y: 26 } + , PathCubic { control1X: -66.6; control1Y: 26; control2X: -75; control2Y: 22; x: -78.2; y: 18.4 } + , PathCubic { control1X: -81.4; control1Y: 14.8; control2X: -80.948; control2Y: 19.966; x: -85.8; y: 19.6 } + , PathCubic { control1X: -91.647; control1Y: 19.159; control2X: -90.6; control2Y: 3.2; x: -90.6; y: 3.2 } + , PathLine { x: -94.6; y: 10.8 } + , PathCubic { control1X: -94.6; control1Y: 10.8; control2X: -95.8; control2Y: 25.2; x: -87.8; y: 22.8 } + , PathCubic { control1X: -83.893; control1Y: 21.628; control2X: -82.6; control2Y: 23.2; x: -84.2; y: 24 } + , PathCubic { control1X: -85.8; control1Y: 24.8; control2X: -78.6; control2Y: 25.2; x: -81.4; y: 26.8 } + , PathCubic { control1X: -84.2; control1Y: 28.4; control2X: -69.8; control2Y: 23.2; x: -72.2; y: 33.6 } + , PathLine { x: -66.6; y: 26 } + ] + } + + ControlledShape { + fillColor: "#000000" + strokeWidth: -1 + delegate: [PathMove { x: -79.2; y: 40.4 } + , PathCubic { control1X: -79.2; control1Y: 40.4; control2X: -94.6; control2Y: 44.8; x: -98.2; y: 35.2 } + , PathCubic { control1X: -98.2; control1Y: 35.2; control2X: -103; control2Y: 37.6; x: -100.8; y: 40.6 } + , PathCubic { control1X: -98.6; control1Y: 43.6; control2X: -97.4; control2Y: 44; x: -97.4; y: 44 } + , PathCubic { control1X: -97.4; control1Y: 44; control2X: -92; control2Y: 45.2; x: -92.6; y: 46 } + , PathCubic { control1X: -93.2; control1Y: 46.8; control2X: -95.6; control2Y: 50.2; x: -95.6; y: 50.2 } + , PathCubic { control1X: -95.6; control1Y: 50.2; control2X: -85.4; control2Y: 44.2; x: -79.2; y: 40.4 } + ] + } + + ControlledShape { + fillColor: "#ffffff" + strokeWidth: -1 + delegate: [PathMove { x: 149.201; y: 118.601 } + , PathCubic { control1X: 148.774; control1Y: 120.735; control2X: 147.103; control2Y: 121.536; x: 145.201; y: 122.201 } + , PathCubic { control1X: 143.284; control1Y: 121.243; control2X: 140.686; control2Y: 118.137; x: 138.801; y: 120.201 } + , PathCubic { control1X: 138.327; control1Y: 119.721; control2X: 137.548; control2Y: 119.661; x: 137.204; y: 118.999 } + , PathCubic { control1X: 136.739; control1Y: 118.101; control2X: 137.011; control2Y: 117.055; x: 136.669; y: 116.257 } + , PathCubic { control1X: 136.124; control1Y: 114.985; control2X: 135.415; control2Y: 113.619; x: 135.601; y: 112.201 } + , PathCubic { control1X: 137.407; control1Y: 111.489; control2X: 138.002; control2Y: 109.583; x: 137.528; y: 107.82 } + , PathCubic { control1X: 137.459; control1Y: 107.563; control2X: 137.03; control2Y: 107.366; x: 137.23; y: 107.017 } + , PathCubic { control1X: 137.416; control1Y: 106.694; control2X: 137.734; control2Y: 106.467; x: 138.001; y: 106.2 } + , PathCubic { control1X: 137.866; control1Y: 106.335; control2X: 137.721; control2Y: 106.568; x: 137.61; y: 106.548 } + , PathCubic { control1X: 137; control1Y: 106.442; control2X: 137.124; control2Y: 105.805; x: 137.254; y: 105.418 } + , PathCubic { control1X: 137.839; control1Y: 103.672; control2X: 139.853; control2Y: 103.408; x: 141.201; y: 104.6 } + , PathCubic { control1X: 141.457; control1Y: 104.035; control2X: 141.966; control2Y: 104.229; x: 142.401; y: 104.2 } + , PathCubic { control1X: 142.351; control1Y: 103.621; control2X: 142.759; control2Y: 103.094; x: 142.957; y: 102.674 } + , PathCubic { control1X: 143.475; control1Y: 101.576; control2X: 145.104; control2Y: 102.682; x: 145.901; y: 102.07 } + , PathCubic { control1X: 146.977; control1Y: 101.245; control2X: 148.04; control2Y: 100.546; x: 149.118; y: 101.149 } + , PathCubic { control1X: 150.927; control1Y: 102.162; control2X: 152.636; control2Y: 103.374; x: 153.835; y: 105.115 } + , PathCubic { control1X: 154.41; control1Y: 105.949; control2X: 154.65; control2Y: 107.23; x: 154.592; y: 108.188 } + , PathCubic { control1X: 154.554; control1Y: 108.835; control2X: 153.173; control2Y: 108.483; x: 152.83; y: 109.412 } + , PathCubic { control1X: 152.185; control1Y: 111.16; control2X: 154.016; control2Y: 111.679; x: 154.772; y: 113.017 } + , PathCubic { control1X: 154.97; control1Y: 113.366; control2X: 154.706; control2Y: 113.67; x: 154.391; y: 113.768 } + , PathCubic { control1X: 153.98; control1Y: 113.896; control2X: 153.196; control2Y: 113.707; x: 153.334; y: 114.16 } + , PathCubic { control1X: 154.306; control1Y: 117.353; control2X: 151.55; control2Y: 118.031; x: 149.201; y: 118.601 } + ] + } + + ControlledShape { + fillColor: "#ffffff" + strokeWidth: -1 + delegate: [PathMove { x: 139.6; y: 138.201 } + , PathCubic { control1X: 139.593; control1Y: 136.463; control2X: 137.992; control2Y: 134.707; x: 139.201; y: 133.001 } + , PathCubic { control1X: 139.336; control1Y: 133.135; control2X: 139.467; control2Y: 133.356; x: 139.601; y: 133.356 } + , PathCubic { control1X: 139.736; control1Y: 133.356; control2X: 139.867; control2Y: 133.135; x: 140.001; y: 133.001 } + , PathCubic { control1X: 141.496; control1Y: 135.217; control2X: 145.148; control2Y: 136.145; x: 145.006; y: 138.991 } + , PathCubic { control1X: 144.984; control1Y: 139.438; control2X: 143.897; control2Y: 140.356; x: 144.801; y: 141.001 } + , PathCubic { control1X: 142.988; control1Y: 142.349; control2X: 142.933; control2Y: 144.719; x: 142.001; y: 146.601 } + , PathCubic { control1X: 140.763; control1Y: 146.315; control2X: 139.551; control2Y: 145.952; x: 138.401; y: 145.401 } + , PathCubic { control1X: 138.753; control1Y: 143.915; control2X: 138.636; control2Y: 142.231; x: 139.456; y: 140.911 } + , PathCubic { control1X: 139.89; control1Y: 140.213; control2X: 139.603; control2Y: 139.134; x: 139.6; y: 138.201 } + ] + } + + ControlledShape { + fillColor: "#cccccc" + strokeWidth: -1 + delegate: [PathMove { x: -26.6; y: 129.201 } + , PathCubic { control1X: -26.6; control1Y: 129.201; control2X: -43.458; control2Y: 139.337; x: -29.4; y: 124.001 } + , PathCubic { control1X: -20.6; control1Y: 114.401; control2X: -10.6; control2Y: 108.801; x: -10.6; y: 108.801 } + , PathCubic { control1X: -10.6; control1Y: 108.801; control2X: -0.2; control2Y: 104.4; x: 3.4; y: 103.2 } + , PathCubic { control1X: 7; control1Y: 102; control2X: 22.2; control2Y: 96.8; x: 25.4; y: 96.4 } + , PathCubic { control1X: 28.6; control1Y: 96; control2X: 38.2; control2Y: 92; x: 45; y: 96 } + , PathCubic { control1X: 51.8; control1Y: 100; control2X: 59.8; control2Y: 104.4; x: 59.8; y: 104.4 } + , PathCubic { control1X: 59.8; control1Y: 104.4; control2X: 43.4; control2Y: 96; x: 39.8; y: 98.4 } + , PathCubic { control1X: 36.2; control1Y: 100.8; control2X: 29; control2Y: 100.4; x: 23; y: 103.6 } + , PathCubic { control1X: 23; control1Y: 103.6; control2X: 8.2; control2Y: 108.001; x: 5; y: 110.001 } + , PathCubic { control1X: 1.8; control1Y: 112.001; control2X: -8.6; control2Y: 123.601; x: -10.2; y: 122.801 } + , PathCubic { control1X: -11.8; control1Y: 122.001; control2X: -9.8; control2Y: 121.601; x: -8.6; y: 118.801 } + , PathCubic { control1X: -7.4; control1Y: 116.001; control2X: -9.4; control2Y: 114.401; x: -17.4; y: 120.801 } + , PathCubic { control1X: -25.4; control1Y: 127.201; control2X: -26.6; control2Y: 129.201; x: -26.6; y: 129.201 } + ] + } + + ControlledShape { + fillColor: "#000000" + strokeWidth: -1 + delegate: [PathMove { x: -19.195; y: 123.234 } + , PathCubic { control1X: -19.195; control1Y: 123.234; control2X: -17.785; control2Y: 110.194; x: -9.307; y: 111.859 } + , PathCubic { control1X: -9.307; control1Y: 111.859; control2X: -1.081; control2Y: 107.689; x: 1.641; y: 105.721 } + , PathCubic { control1X: 1.641; control1Y: 105.721; control2X: 9.78; control2Y: 104.019; x: 11.09; y: 103.402 } + , PathCubic { control1X: 29.569; control1Y: 94.702; control2X: 44.288; control2Y: 99.221; x: 44.835; y: 98.101 } + , PathCubic { control1X: 45.381; control1Y: 96.982; control2X: 65.006; control2Y: 104.099; x: 68.615; y: 108.185 } + , PathCubic { control1X: 69.006; control1Y: 108.628; control2X: 58.384; control2Y: 102.588; x: 48.686; y: 100.697 } + , PathCubic { control1X: 40.413; control1Y: 99.083; control2X: 18.811; control2Y: 100.944; x: 7.905; y: 106.48 } + , PathCubic { control1X: 4.932; control1Y: 107.989; control2X: -4.013; control2Y: 113.773; x: -6.544; y: 113.662 } + , PathCubic { control1X: -9.075; control1Y: 113.55; control2X: -19.195; control2Y: 123.234; x: -19.195; y: 123.234 } + ] + } + + ControlledShape { + fillColor: "#cccccc" + strokeWidth: -1 + delegate: [PathMove { x: -23; y: 148.801 } + , PathCubic { control1X: -23; control1Y: 148.801; control2X: -38.2; control2Y: 146.401; x: -21.4; y: 144.801 } + , PathCubic { control1X: -21.4; control1Y: 144.801; control2X: -3.4; control2Y: 142.801; x: 0.6; y: 137.601 } + , PathCubic { control1X: 0.6; control1Y: 137.601; control2X: 14.2; control2Y: 128.401; x: 17; y: 128.001 } + , PathCubic { control1X: 19.8; control1Y: 127.601; control2X: 49.8; control2Y: 120.401; x: 50.2; y: 118.001 } + , PathCubic { control1X: 50.6; control1Y: 115.601; control2X: 56.2; control2Y: 115.601; x: 57.8; y: 116.401 } + , PathCubic { control1X: 59.4; control1Y: 117.201; control2X: 58.6; control2Y: 118.401; x: 55.8; y: 119.201 } + , PathCubic { control1X: 53; control1Y: 120.001; control2X: 21.8; control2Y: 136.401; x: 15.4; y: 137.601 } + , PathCubic { control1X: 9; control1Y: 138.801; control2X: -2.6; control2Y: 146.401; x: -7.4; y: 147.601 } + , PathCubic { control1X: -12.2; control1Y: 148.801; control2X: -23; control2Y: 148.801; x: -23; y: 148.801 } + ] + } + + ControlledShape { + fillColor: "#000000" + strokeWidth: -1 + delegate: [PathMove { x: -3.48; y: 141.403 } + , PathCubic { control1X: -3.48; control1Y: 141.403; control2X: -12.062; control2Y: 140.574; x: -3.461; y: 139.755 } + , PathCubic { control1X: -3.461; control1Y: 139.755; control2X: 5.355; control2Y: 136.331; x: 7.403; y: 133.668 } + , PathCubic { control1X: 7.403; control1Y: 133.668; control2X: 14.367; control2Y: 128.957; x: 15.8; y: 128.753 } + , PathCubic { control1X: 17.234; control1Y: 128.548; control2X: 31.194; control2Y: 124.861; x: 31.399; y: 123.633 } + , PathCubic { control1X: 31.604; control1Y: 122.404; control2X: 65.67; control2Y: 109.823; x: 70.09; y: 113.013 } + , PathCubic { control1X: 73.001; control1Y: 115.114; control2X: 63.1; control2Y: 113.437; x: 53.466; y: 117.847 } + , PathCubic { control1X: 52.111; control1Y: 118.467; control2X: 18.258; control2Y: 133.054; x: 14.981; y: 133.668 } + , PathCubic { control1X: 11.704; control1Y: 134.283; control2X: 5.765; control2Y: 138.174; x: 3.307; y: 138.788 } + , PathCubic { control1X: 0.85; control1Y: 139.403; control2X: -3.48; control2Y: 141.403; x: -3.48; y: 141.403 } + ] + } + + ControlledShape { + fillColor: "#000000" + strokeWidth: -1 + delegate: [PathMove { x: -11.4; y: 143.601 } + , PathCubic { control1X: -11.4; control1Y: 143.601; control2X: -6.2; control2Y: 143.201; x: -7.4; y: 144.801 } + , PathCubic { control1X: -8.6; control1Y: 146.401; control2X: -11; control2Y: 145.601; x: -11; y: 145.601 } + , PathLine { x: -11.4; y: 143.601 } + ] + } + + ControlledShape { + fillColor: "#000000" + strokeWidth: -1 + delegate: [PathMove { x: -18.6; y: 145.201 } + , PathCubic { control1X: -18.6; control1Y: 145.201; control2X: -13.4; control2Y: 144.801; x: -14.6; y: 146.401 } + , PathCubic { control1X: -15.8; control1Y: 148.001; control2X: -18.2; control2Y: 147.201; x: -18.2; y: 147.201 } + , PathLine { x: -18.6; y: 145.201 } + ] + } + + ControlledShape { + fillColor: "#000000" + strokeWidth: -1 + delegate: [PathMove { x: -29; y: 146.801 } + , PathCubic { control1X: -29; control1Y: 146.801; control2X: -23.8; control2Y: 146.401; x: -25; y: 148.001 } + , PathCubic { control1X: -26.2; control1Y: 149.601; control2X: -28.6; control2Y: 148.801; x: -28.6; y: 148.801 } + , PathLine { x: -29; y: 146.801 } + ] + } + + ControlledShape { + fillColor: "#000000" + strokeWidth: -1 + delegate: [PathMove { x: -36.6; y: 147.601 } + , PathCubic { control1X: -36.6; control1Y: 147.601; control2X: -31.4; control2Y: 147.201; x: -32.6; y: 148.801 } + , PathCubic { control1X: -33.8; control1Y: 150.401; control2X: -36.2; control2Y: 149.601; x: -36.2; y: 149.601 } + , PathLine { x: -36.6; y: 147.601 } + ] + } + + ControlledShape { + fillColor: "#000000" + strokeWidth: -1 + delegate: [PathMove { x: 1.8; y: 108.001 } + , PathCubic { control1X: 1.8; control1Y: 108.001; control2X: 6.2; control2Y: 108.001; x: 5; y: 109.601 } + , PathCubic { control1X: 3.8; control1Y: 111.201; control2X: 0.6; control2Y: 110.801; x: 0.6; y: 110.801 } + , PathLine { x: 1.8; y: 108.001 } + ] + } + + ControlledShape { + fillColor: "#000000" + strokeWidth: -1 + delegate: [PathMove { x: -8.2; y: 113.601 } + , PathCubic { control1X: -8.2; control1Y: 113.601; control2X: -1.694; control2Y: 111.46; x: -4.2; y: 114.801 } + , PathCubic { control1X: -5.4; control1Y: 116.401; control2X: -7.8; control2Y: 115.601; x: -7.8; y: 115.601 } + , PathLine { x: -8.2; y: 113.601 } + ] + } + + ControlledShape { + fillColor: "#000000" + strokeWidth: -1 + delegate: [PathMove { x: -19.4; y: 118.401 } + , PathCubic { control1X: -19.4; control1Y: 118.401; control2X: -14.2; control2Y: 118.001; x: -15.4; y: 119.601 } + , PathCubic { control1X: -16.6; control1Y: 121.201; control2X: -19; control2Y: 120.401; x: -19; y: 120.401 } + , PathLine { x: -19.4; y: 118.401 } + ] + } + + ControlledShape { + fillColor: "#000000" + strokeWidth: -1 + delegate: [PathMove { x: -27; y: 124.401 } + , PathCubic { control1X: -27; control1Y: 124.401; control2X: -21.8; control2Y: 124.001; x: -23; y: 125.601 } + , PathCubic { control1X: -24.2; control1Y: 127.201; control2X: -26.6; control2Y: 126.401; x: -26.6; y: 126.401 } + , PathLine { x: -27; y: 124.401 } + ] + } + + ControlledShape { + fillColor: "#000000" + strokeWidth: -1 + delegate: [PathMove { x: -33.8; y: 129.201 } + , PathCubic { control1X: -33.8; control1Y: 129.201; control2X: -28.6; control2Y: 128.801; x: -29.8; y: 130.401 } + , PathCubic { control1X: -31; control1Y: 132.001; control2X: -33.4; control2Y: 131.201; x: -33.4; y: 131.201 } + , PathLine { x: -33.8; y: 129.201 } + ] + } + + ControlledShape { + fillColor: "#000000" + strokeWidth: -1 + delegate: [PathMove { x: 5.282; y: 135.598 } + , PathCubic { control1X: 5.282; control1Y: 135.598; control2X: 12.203; control2Y: 135.066; x: 10.606; y: 137.195 } + , PathCubic { control1X: 9.009; control1Y: 139.325; control2X: 5.814; control2Y: 138.26; x: 5.814; y: 138.26 } + , PathLine { x: 5.282; y: 135.598 } + ] + } + + ControlledShape { + fillColor: "#000000" + strokeWidth: -1 + delegate: [PathMove { x: 15.682; y: 130.798 } + , PathCubic { control1X: 15.682; control1Y: 130.798; control2X: 22.603; control2Y: 130.266; x: 21.006; y: 132.395 } + , PathCubic { control1X: 19.409; control1Y: 134.525; control2X: 16.214; control2Y: 133.46; x: 16.214; y: 133.46 } + , PathLine { x: 15.682; y: 130.798 } + ] + } + + ControlledShape { + fillColor: "#000000" + strokeWidth: -1 + delegate: [PathMove { x: 26.482; y: 126.398 } + , PathCubic { control1X: 26.482; control1Y: 126.398; control2X: 33.403; control2Y: 125.866; x: 31.806; y: 127.995 } + , PathCubic { control1X: 30.209; control1Y: 130.125; control2X: 27.014; control2Y: 129.06; x: 27.014; y: 129.06 } + , PathLine { x: 26.482; y: 126.398 } + ] + } + + ControlledShape { + fillColor: "#000000" + strokeWidth: -1 + delegate: [PathMove { x: 36.882; y: 121.598 } + , PathCubic { control1X: 36.882; control1Y: 121.598; control2X: 43.803; control2Y: 121.066; x: 42.206; y: 123.195 } + , PathCubic { control1X: 40.609; control1Y: 125.325; control2X: 37.414; control2Y: 124.26; x: 37.414; y: 124.26 } + , PathLine { x: 36.882; y: 121.598 } + ] + } + + ControlledShape { + fillColor: "#000000" + strokeWidth: -1 + delegate: [PathMove { x: 9.282; y: 103.598 } + , PathCubic { control1X: 9.282; control1Y: 103.598; control2X: 16.203; control2Y: 103.066; x: 14.606; y: 105.195 } + , PathCubic { control1X: 13.009; control1Y: 107.325; control2X: 9.014; control2Y: 107.06; x: 9.014; y: 107.06 } + , PathLine { x: 9.282; y: 103.598 } + ] + } + + ControlledShape { + fillColor: "#000000" + strokeWidth: -1 + delegate: [PathMove { x: 19.282; y: 100.398 } + , PathCubic { control1X: 19.282; control1Y: 100.398; control2X: 26.203; control2Y: 99.866; x: 24.606; y: 101.995 } + , PathCubic { control1X: 23.009; control1Y: 104.125; control2X: 18.614; control2Y: 103.86; x: 18.614; y: 103.86 } + , PathLine { x: 19.282; y: 100.398 } + ] + } + + ControlledShape { + fillColor: "#000000" + strokeWidth: -1 + delegate: [PathMove { x: -3.4; y: 140.401 } + , PathCubic { control1X: -3.4; control1Y: 140.401; control2X: 1.8; control2Y: 140.001; x: 0.6; y: 141.601 } + , PathCubic { control1X: -0.6; control1Y: 143.201; control2X: -3; control2Y: 142.401; x: -3; y: 142.401 } + , PathLine { x: -3.4; y: 140.401 } + ] + } + + ControlledShape { + fillColor: "#992600" + strokeWidth: -1 + delegate: [PathMove { x: -76.6; y: 41.2 } + , PathCubic { control1X: -76.6; control1Y: 41.2; control2X: -81; control2Y: 50; x: -81.4; y: 53.2 } + , PathCubic { control1X: -81.4; control1Y: 53.2; control2X: -80.6; control2Y: 44.4; x: -79.4; y: 42.4 } + , PathCubic { control1X: -78.2; control1Y: 40.4; control2X: -76.6; control2Y: 41.2; x: -76.6; y: 41.2 } + ] + } + + ControlledShape { + fillColor: "#992600" + strokeWidth: -1 + delegate: [PathMove { x: -95; y: 55.2 } + , PathCubic { control1X: -95; control1Y: 55.2; control2X: -98.2; control2Y: 69.6; x: -97.8; y: 72.4 } + , PathCubic { control1X: -97.8; control1Y: 72.4; control2X: -99; control2Y: 60.8; x: -98.6; y: 59.6 } + , PathCubic { control1X: -98.2; control1Y: 58.4; control2X: -95; control2Y: 55.2; x: -95; y: 55.2 } + ] + } + + ControlledShape { + fillColor: "#cccccc" + strokeWidth: -1 + delegate: [PathMove { x: -74.2; y: -19.4 } + , PathLine { x: -74.4; y: -16.2 } + , PathLine { x: -76.6; y: -16 } + , PathCubic { control1X: -76.6; control1Y: -16; control2X: -62.4; control2Y: -3.4; x: -61.8; y: 4.2 } + , PathCubic { control1X: -61.8; control1Y: 4.2; control2X: -61; control2Y: -4; x: -74.2; y: -19.4 } + ] + } + + ControlledShape { + fillColor: "#000000" + strokeWidth: -1 + delegate: [PathMove { x: -70.216; y: -18.135 } + , PathCubic { control1X: -70.647; control1Y: -18.551; control2X: -70.428; control2Y: -19.296; x: -70.836; y: -19.556 } + , PathCubic { control1X: -71.645; control1Y: -20.072; control2X: -69.538; control2Y: -20.129; x: -69.766; y: -20.845 } + , PathCubic { control1X: -70.149; control1Y: -22.051; control2X: -69.962; control2Y: -22.072; x: -70.084; y: -23.348 } + , PathCubic { control1X: -70.141; control1Y: -23.946; control2X: -69.553; control2Y: -25.486; x: -69.168; y: -25.926 } + , PathCubic { control1X: -67.722; control1Y: -27.578; control2X: -69.046; control2Y: -30.51; x: -67.406; y: -32.061 } + , PathCubic { control1X: -67.102; control1Y: -32.35; control2X: -66.726; control2Y: -32.902; x: -66.441; y: -33.32 } + , PathCubic { control1X: -65.782; control1Y: -34.283; control2X: -64.598; control2Y: -34.771; x: -63.648; y: -35.599 } + , PathCubic { control1X: -63.33; control1Y: -35.875; control2X: -63.531; control2Y: -36.702; x: -62.962; y: -36.61 } + , PathCubic { control1X: -62.248; control1Y: -36.495; control2X: -61.007; control2Y: -36.625; x: -61.052; y: -35.784 } + , PathCubic { control1X: -61.165; control1Y: -33.664; control2X: -62.494; control2Y: -31.944; x: -63.774; y: -30.276 } + , PathCubic { control1X: -63.323; control1Y: -29.572; control2X: -63.781; control2Y: -28.937; x: -64.065; y: -28.38 } + , PathCubic { control1X: -65.4; control1Y: -25.76; control2X: -65.211; control2Y: -22.919; x: -65.385; y: -20.079 } + , PathCubic { control1X: -65.39; control1Y: -19.994; control2X: -65.697; control2Y: -19.916; x: -65.689; y: -19.863 } + , PathCubic { control1X: -65.336; control1Y: -17.528; control2X: -64.752; control2Y: -15.329; x: -63.873; y: -13.1 } + , PathCubic { control1X: -63.507; control1Y: -12.17; control2X: -63.036; control2Y: -11.275; x: -62.886; y: -10.348 } + , PathCubic { control1X: -62.775; control1Y: -9.662; control2X: -62.672; control2Y: -8.829; x: -63.08; y: -8.124 } + , PathCubic { control1X: -61.045; control1Y: -5.234; control2X: -62.354; control2Y: -2.583; x: -61.185; y: 0.948 } + , PathCubic { control1X: -60.978; control1Y: 1.573; control2X: -59.286; control2Y: 3.487; x: -59.749; y: 3.326 } + , PathCubic { control1X: -62.262; control1Y: 2.455; control2X: -62.374; control2Y: 2.057; x: -62.551; y: 1.304 } + , PathCubic { control1X: -62.697; control1Y: 0.681; control2X: -63.027; control2Y: -0.696; x: -63.264; y: -1.298 } + , PathCubic { control1X: -63.328; control1Y: -1.462; control2X: -63.499; control2Y: -3.346; x: -63.577; y: -3.468 } + , PathCubic { control1X: -65.09; control1Y: -5.85; control2X: -63.732; control2Y: -5.674; x: -65.102; y: -8.032 } + , PathCubic { control1X: -66.53; control1Y: -8.712; control2X: -67.496; control2Y: -9.816; x: -68.619; y: -10.978 } + , PathCubic { control1X: -68.817; control1Y: -11.182; control2X: -67.674; control2Y: -11.906; x: -67.855; y: -12.119 } + , PathCubic { control1X: -68.947; control1Y: -13.408; control2X: -70.1; control2Y: -14.175; x: -69.764; y: -15.668 } + , PathCubic { control1X: -69.609; control1Y: -16.358; control2X: -69.472; control2Y: -17.415; x: -70.216; y: -18.135 } + ] + } + + ControlledShape { + fillColor: "#000000" + strokeWidth: -1 + delegate: [PathMove { x: -73.8; y: -16.4 } + , PathCubic { control1X: -73.8; control1Y: -16.4; control2X: -73.4; control2Y: -9.6; x: -71; y: -8 } + , PathCubic { control1X: -68.6; control1Y: -6.4; control2X: -69.8; control2Y: -7.2; x: -73; y: -8.4 } + , PathCubic { control1X: -76.2; control1Y: -9.6; control2X: -75; control2Y: -10.4; x: -75; y: -10.4 } + , PathCubic { control1X: -75; control1Y: -10.4; control2X: -77.8; control2Y: -10; x: -75.4; y: -8 } + , PathCubic { control1X: -73; control1Y: -6; control2X: -69.4; control2Y: -3.6; x: -71; y: -3.6 } + , PathCubic { control1X: -72.6; control1Y: -3.6; control2X: -80.2; control2Y: -7.6; x: -80.2; y: -10.4 } + , PathCubic { control1X: -80.2; control1Y: -13.2; control2X: -81.2; control2Y: -17.3; x: -81.2; y: -17.3 } + , PathCubic { control1X: -81.2; control1Y: -17.3; control2X: -80.1; control2Y: -18.1; x: -75.3; y: -18 } + , PathCubic { control1X: -75.3; control1Y: -18; control2X: -73.9; control2Y: -17.3; x: -73.8; y: -16.4 } + ] + } + + ControlledShape { + fillColor: "#ffffff" + strokeColor: "#000000" + strokeWidth: 0.1 + delegate: [PathMove { x: -74.6; y: 2.2 } + , PathCubic { control1X: -74.6; control1Y: 2.2; control2X: -83.12; control2Y: -0.591; x: -101.6; y: 2.8 } + , PathCubic { control1X: -101.6; control1Y: 2.8; control2X: -92.569; control2Y: 0.722; x: -73.8; y: 3 } + , PathCubic { control1X: -63.5; control1Y: 4.25; control2X: -74.6; control2Y: 2.2; x: -74.6; y: 2.2 } + ] + } + + ControlledShape { + fillColor: "#ffffff" + strokeColor: "#000000" + strokeWidth: 0.1 + delegate: [PathMove { x: -72.502; y: 2.129 } + , PathCubic { control1X: -72.502; control1Y: 2.129; control2X: -80.748; control2Y: -1.389; x: -99.453; y: 0.392 } + , PathCubic { control1X: -99.453; control1Y: 0.392; control2X: -90.275; control2Y: -0.897; x: -71.774; y: 2.995 } + , PathCubic { control1X: -61.62; control1Y: 5.131; control2X: -72.502; control2Y: 2.129; x: -72.502; y: 2.129 } + ] + } + + ControlledShape { + fillColor: "#ffffff" + strokeColor: "#000000" + strokeWidth: 0.1 + delegate: [PathMove { x: -70.714; y: 2.222 } + , PathCubic { control1X: -70.714; control1Y: 2.222; control2X: -78.676; control2Y: -1.899; x: -97.461; y: -1.514 } + , PathCubic { control1X: -97.461; control1Y: -1.514; control2X: -88.213; control2Y: -2.118; x: -70.052; y: 3.14 } + , PathCubic { control1X: -60.086; control1Y: 6.025; control2X: -70.714; control2Y: 2.222; x: -70.714; y: 2.222 } + ] + } + + ControlledShape { + fillColor: "#ffffff" + strokeColor: "#000000" + strokeWidth: 0.1 + delegate: [PathMove { x: -69.444; y: 2.445 } + , PathCubic { control1X: -69.444; control1Y: 2.445; control2X: -76.268; control2Y: -1.862; x: -93.142; y: -2.96 } + , PathCubic { control1X: -93.142; control1Y: -2.96; control2X: -84.803; control2Y: -2.79; x: -68.922; y: 3.319 } + , PathCubic { control1X: -60.206; control1Y: 6.672; control2X: -69.444; control2Y: 2.445; x: -69.444; y: 2.445 } + ] + } + + ControlledShape { + fillColor: "#ffffff" + strokeColor: "#000000" + strokeWidth: 0.1 + delegate: [PathMove { x: 45.84; y: 12.961 } + , PathCubic { control1X: 45.84; control1Y: 12.961; control2X: 44.91; control2Y: 13.605; x: 45.124; y: 12.424 } + , PathCubic { control1X: 45.339; control1Y: 11.243; control2X: 73.547; control2Y: -1.927; x: 77.161; y: -1.677 } + , PathCubic { control1X: 77.161; control1Y: -1.677; control2X: 46.913; control2Y: 11.529; x: 45.84; y: 12.961 } + ] + } + + ControlledShape { + fillColor: "#ffffff" + strokeColor: "#000000" + strokeWidth: 0.1 + delegate: [PathMove { x: 42.446; y: 13.6 } + , PathCubic { control1X: 42.446; control1Y: 13.6; control2X: 41.57; control2Y: 14.315; x: 41.691; y: 13.121 } + , PathCubic { control1X: 41.812; control1Y: 11.927; control2X: 68.899; control2Y: -3.418; x: 72.521; y: -3.452 } + , PathCubic { control1X: 72.521; control1Y: -3.452; control2X: 43.404; control2Y: 12.089; x: 42.446; y: 13.6 } + ] + } + + ControlledShape { + fillColor: "#ffffff" + strokeColor: "#000000" + strokeWidth: 0.1 + delegate: [PathMove { x: 39.16; y: 14.975 } + , PathCubic { control1X: 39.16; control1Y: 14.975; control2X: 38.332; control2Y: 15.747; x: 38.374; y: 14.547 } + , PathCubic { control1X: 38.416; control1Y: 13.348; control2X: 58.233; control2Y: -2.149; x: 68.045; y: -4.023 } + , PathCubic { control1X: 68.045; control1Y: -4.023; control2X: 50.015; control2Y: 4.104; x: 39.16; y: 14.975 } + ] + } + + ControlledShape { + fillColor: "#ffffff" + strokeColor: "#000000" + strokeWidth: 0.1 + delegate: [PathMove { x: 36.284; y: 16.838 } + , PathCubic { control1X: 36.284; control1Y: 16.838; control2X: 35.539; control2Y: 17.532; x: 35.577; y: 16.453 } + , PathCubic { control1X: 35.615; control1Y: 15.373; control2X: 53.449; control2Y: 1.426; x: 62.28; y: -0.26 } + , PathCubic { control1X: 62.28; control1Y: -0.26; control2X: 46.054; control2Y: 7.054; x: 36.284; y: 16.838 } + ] + } + + ControlledShape { + fillColor: "#cccccc" + strokeWidth: -1 + delegate: [PathMove { x: 4.6; y: 164.801 } + , PathCubic { control1X: 4.6; control1Y: 164.801; control2X: -10.6; control2Y: 162.401; x: 6.2; y: 160.801 } + , PathCubic { control1X: 6.2; control1Y: 160.801; control2X: 24.2; control2Y: 158.801; x: 28.2; y: 153.601 } + , PathCubic { control1X: 28.2; control1Y: 153.601; control2X: 41.8; control2Y: 144.401; x: 44.6; y: 144.001 } + , PathCubic { control1X: 47.4; control1Y: 143.601; control2X: 63.8; control2Y: 140.001; x: 64.2; y: 137.601 } + , PathCubic { control1X: 64.6; control1Y: 135.201; control2X: 70.6; control2Y: 132.801; x: 72.2; y: 133.601 } + , PathCubic { control1X: 73.8; control1Y: 134.401; control2X: 73.8; control2Y: 143.601; x: 71; y: 144.401 } + , PathCubic { control1X: 68.2; control1Y: 145.201; control2X: 49.4; control2Y: 152.401; x: 43; y: 153.601 } + , PathCubic { control1X: 36.6; control1Y: 154.801; control2X: 25; control2Y: 162.401; x: 20.2; y: 163.601 } + , PathCubic { control1X: 15.4; control1Y: 164.801; control2X: 4.6; control2Y: 164.801; x: 4.6; y: 164.801 } + ] + } + + ControlledShape { + fillColor: "#000000" + strokeWidth: -1 + delegate: [PathMove { x: 77.6; y: 127.401 } + , PathCubic { control1X: 77.6; control1Y: 127.401; control2X: 74.6; control2Y: 129.001; x: 73.4; y: 131.601 } + , PathCubic { control1X: 73.4; control1Y: 131.601; control2X: 67; control2Y: 142.201; x: 52.8; y: 145.401 } + , PathCubic { control1X: 52.8; control1Y: 145.401; control2X: 29.8; control2Y: 154.401; x: 22; y: 156.401 } + , PathCubic { control1X: 22; control1Y: 156.401; control2X: 8.6; control2Y: 161.401; x: 1.2; y: 160.601 } + , PathCubic { control1X: 1.2; control1Y: 160.601; control2X: -5.8; control2Y: 160.801; x: 0.4; y: 162.401 } + , PathCubic { control1X: 0.4; control1Y: 162.401; control2X: 20.6; control2Y: 160.401; x: 24; y: 158.601 } + , PathCubic { control1X: 24; control1Y: 158.601; control2X: 39.6; control2Y: 153.401; x: 42.6; y: 150.801 } + , PathCubic { control1X: 45.6; control1Y: 148.201; control2X: 63.8; control2Y: 143.201; x: 66; y: 141.201 } + , PathCubic { control1X: 68.2; control1Y: 139.201; control2X: 78; control2Y: 130.801; x: 77.6; y: 127.401 } + ] + } + + ControlledShape { + fillColor: "#000000" + strokeWidth: -1 + delegate: [PathMove { x: 18.882; y: 158.911 } + , PathCubic { control1X: 18.882; control1Y: 158.911; control2X: 24.111; control2Y: 158.685; x: 22.958; y: 160.234 } + , PathCubic { control1X: 21.805; control1Y: 161.784; control2X: 19.357; control2Y: 160.91; x: 19.357; y: 160.91 } + , PathLine { x: 18.882; y: 158.911 } + ] + } + + ControlledShape { + fillColor: "#000000" + strokeWidth: -1 + delegate: [PathMove { x: 11.68; y: 160.263 } + , PathCubic { control1X: 11.68; control1Y: 160.263; control2X: 16.908; control2Y: 160.037; x: 15.756; y: 161.586 } + , PathCubic { control1X: 14.603; control1Y: 163.136; control2X: 12.155; control2Y: 162.263; x: 12.155; y: 162.263 } + , PathLine { x: 11.68; y: 160.263 } + ] + } + + ControlledShape { + fillColor: "#000000" + strokeWidth: -1 + delegate: [PathMove { x: 1.251; y: 161.511 } + , PathCubic { control1X: 1.251; control1Y: 161.511; control2X: 6.48; control2Y: 161.284; x: 5.327; y: 162.834 } + , PathCubic { control1X: 4.174; control1Y: 164.383; control2X: 1.726; control2Y: 163.51; x: 1.726; y: 163.51 } + , PathLine { x: 1.251; y: 161.511 } + ] + } + + ControlledShape { + fillColor: "#000000" + strokeWidth: -1 + delegate: [PathMove { x: -6.383; y: 162.055 } + , PathCubic { control1X: -6.383; control1Y: 162.055; control2X: -1.154; control2Y: 161.829; x: -2.307; y: 163.378 } + , PathCubic { control1X: -3.46; control1Y: 164.928; control2X: -5.908; control2Y: 164.054; x: -5.908; y: 164.054 } + , PathLine { x: -6.383; y: 162.055 } + ] + } + + ControlledShape { + fillColor: "#000000" + strokeWidth: -1 + delegate: [PathMove { x: 35.415; y: 151.513 } + , PathCubic { control1X: 35.415; control1Y: 151.513; control2X: 42.375; control2Y: 151.212; x: 40.84; y: 153.274 } + , PathCubic { control1X: 39.306; control1Y: 155.336; control2X: 36.047; control2Y: 154.174; x: 36.047; y: 154.174 } + , PathLine { x: 35.415; y: 151.513 } + ] + } + + ControlledShape { + fillColor: "#000000" + strokeWidth: -1 + delegate: [PathMove { x: 45.73; y: 147.088 } + , PathCubic { control1X: 45.73; control1Y: 147.088; control2X: 51.689; control2Y: 143.787; x: 51.155; y: 148.849 } + , PathCubic { control1X: 50.885; control1Y: 151.405; control2X: 46.362; control2Y: 149.749; x: 46.362; y: 149.749 } + , PathLine { x: 45.73; y: 147.088 } + ] + } + + ControlledShape { + fillColor: "#000000" + strokeWidth: -1 + delegate: [PathMove { x: 54.862; y: 144.274 } + , PathCubic { control1X: 54.862; control1Y: 144.274; control2X: 62.021; control2Y: 140.573; x: 60.287; y: 146.035 } + , PathCubic { control1X: 59.509; control1Y: 148.485; control2X: 55.493; control2Y: 146.935; x: 55.493; y: 146.935 } + , PathLine { x: 54.862; y: 144.274 } + ] + } + + ControlledShape { + fillColor: "#000000" + strokeWidth: -1 + delegate: [PathMove { x: 64.376; y: 139.449 } + , PathCubic { control1X: 64.376; control1Y: 139.449; control2X: 68.735; control2Y: 134.548; x: 69.801; y: 141.21 } + , PathCubic { control1X: 70.207; control1Y: 143.748; control2X: 65.008; control2Y: 142.11; x: 65.008; y: 142.11 } + , PathLine { x: 64.376; y: 139.449 } + ] + } + + ControlledShape { + fillColor: "#000000" + strokeWidth: -1 + delegate: [PathMove { x: 26.834; y: 155.997 } + , PathCubic { control1X: 26.834; control1Y: 155.997; control2X: 32.062; control2Y: 155.77; x: 30.91; y: 157.32 } + , PathCubic { control1X: 29.757; control1Y: 158.869; control2X: 27.308; control2Y: 157.996; x: 27.308; y: 157.996 } + , PathLine { x: 26.834; y: 155.997 } + ] + } + + ControlledShape { + fillColor: "#ffffff" + strokeColor: "#000000" + strokeWidth: 0.1 + delegate: [PathMove { x: 62.434; y: 34.603 } + , PathCubic { control1X: 62.434; control1Y: 34.603; control2X: 61.708; control2Y: 35.268; x: 61.707; y: 34.197 } + , PathCubic { control1X: 61.707; control1Y: 33.127; control2X: 79.191; control2Y: 19.863; x: 88.034; y: 18.479 } + , PathCubic { control1X: 88.034; control1Y: 18.479; control2X: 71.935; control2Y: 25.208; x: 62.434; y: 34.603 } + ] + } + + ControlledShape { + fillColor: "#000000" + strokeWidth: -1 + delegate: [PathMove { x: 65.4; y: 98.4 } + , PathCubic { control1X: 65.4; control1Y: 98.4; control2X: 87.401; control2Y: 120.801; x: 96.601; y: 124.401 } + , PathCubic { control1X: 96.601; control1Y: 124.401; control2X: 105.801; control2Y: 135.601; x: 101.801; y: 161.601 } + , PathCubic { control1X: 101.801; control1Y: 161.601; control2X: 98.601; control2Y: 169.201; x: 95.401; y: 148.401 } + , PathCubic { control1X: 95.401; control1Y: 148.401; control2X: 98.601; control2Y: 123.201; x: 87.401; y: 139.201 } + , PathCubic { control1X: 87.401; control1Y: 139.201; control2X: 79; control2Y: 129.301; x: 85.4; y: 129.601 } + , PathCubic { control1X: 85.4; control1Y: 129.601; control2X: 88.601; control2Y: 131.601; x: 89.001; y: 130.001 } + , PathCubic { control1X: 89.401; control1Y: 128.401; control2X: 81.4; control2Y: 114.801; x: 64.2; y: 100.4 } + , PathCubic { control1X: 47; control1Y: 86; control2X: 65.4; control2Y: 98.4; x: 65.4; y: 98.4 } + ] + } + + ControlledShape { + fillColor: "#ffffff" + strokeColor: "#000000" + strokeWidth: 0.1 + delegate: [PathMove { x: 7; y: 137.201 } + , PathCubic { control1X: 7; control1Y: 137.201; control2X: 6.8; control2Y: 135.401; x: 8.6; y: 136.201 } + , PathCubic { control1X: 10.4; control1Y: 137.001; control2X: 104.601; control2Y: 143.201; x: 136.201; y: 167.201 } + , PathCubic { control1X: 136.201; control1Y: 167.201; control2X: 91.001; control2Y: 144.001; x: 7; y: 137.201 } + ] + } + + ControlledShape { + fillColor: "#ffffff" + strokeColor: "#000000" + strokeWidth: 0.1 + delegate: [PathMove { x: 17.4; y: 132.801 } + , PathCubic { control1X: 17.4; control1Y: 132.801; control2X: 17.2; control2Y: 131.001; x: 19; y: 131.801 } + , PathCubic { control1X: 20.8; control1Y: 132.601; control2X: 157.401; control2Y: 131.601; x: 181.001; y: 164.001 } + , PathCubic { control1X: 181.001; control1Y: 164.001; control2X: 159.001; control2Y: 138.801; x: 17.4; y: 132.801 } + ] + } + + ControlledShape { + fillColor: "#ffffff" + strokeColor: "#000000" + strokeWidth: 0.1 + delegate: [PathMove { x: 29; y: 128.801 } + , PathCubic { control1X: 29; control1Y: 128.801; control2X: 28.8; control2Y: 127.001; x: 30.6; y: 127.801 } + , PathCubic { control1X: 32.4; control1Y: 128.601; control2X: 205.801; control2Y: 115.601; x: 229.401; y: 148.001 } + , PathCubic { control1X: 229.401; control1Y: 148.001; control2X: 219.801; control2Y: 122.401; x: 29; y: 128.801 } + ] + } + + ControlledShape { + fillColor: "#ffffff" + strokeColor: "#000000" + strokeWidth: 0.1 + delegate: [PathMove { x: 39; y: 124.001 } + , PathCubic { control1X: 39; control1Y: 124.001; control2X: 38.8; control2Y: 122.201; x: 40.6; y: 123.001 } + , PathCubic { control1X: 42.4; control1Y: 123.801; control2X: 164.601; control2Y: 85.2; x: 188.201; y: 117.601 } + , PathCubic { control1X: 188.201; control1Y: 117.601; control2X: 174.801; control2Y: 93; x: 39; y: 124.001 } + ] + } + + ControlledShape { + fillColor: "#ffffff" + strokeColor: "#000000" + strokeWidth: 0.1 + delegate: [PathMove { x: -19; y: 146.801 } + , PathCubic { control1X: -19; control1Y: 146.801; control2X: -19.2; control2Y: 145.001; x: -17.4; y: 145.801 } + , PathCubic { control1X: -15.6; control1Y: 146.601; control2X: 2.2; control2Y: 148.801; x: 4.2; y: 187.601 } + , PathCubic { control1X: 4.2; control1Y: 187.601; control2X: -3; control2Y: 145.601; x: -19; y: 146.801 } + ] + } + + ControlledShape { + fillColor: "#ffffff" + strokeColor: "#000000" + strokeWidth: 0.1 + delegate: [PathMove { x: -27.8; y: 148.401 } + , PathCubic { control1X: -27.8; control1Y: 148.401; control2X: -28; control2Y: 146.601; x: -26.2; y: 147.401 } + , PathCubic { control1X: -24.4; control1Y: 148.201; control2X: -10.2; control2Y: 143.601; x: -13; y: 182.401 } + , PathCubic { control1X: -13; control1Y: 182.401; control2X: -11.8; control2Y: 147.201; x: -27.8; y: 148.401 } + ] + } + + ControlledShape { + fillColor: "#ffffff" + strokeColor: "#000000" + strokeWidth: 0.1 + delegate: [PathMove { x: -35.8; y: 148.801 } + , PathCubic { control1X: -35.8; control1Y: 148.801; control2X: -36; control2Y: 147.001; x: -34.2; y: 147.801 } + , PathCubic { control1X: -32.4; control1Y: 148.601; control2X: -17; control2Y: 149.201; x: -29.4; y: 171.601 } + , PathCubic { control1X: -29.4; control1Y: 171.601; control2X: -19.8; control2Y: 147.601; x: -35.8; y: 148.801 } + ] + } + + ControlledShape { + fillColor: "#ffffff" + strokeColor: "#000000" + strokeWidth: 0.1 + delegate: [PathMove { x: 11.526; y: 104.465 } + , PathCubic { control1X: 11.526; control1Y: 104.465; control2X: 11.082; control2Y: 106.464; x: 12.631; y: 105.247 } + , PathCubic { control1X: 28.699; control1Y: 92.622; control2X: 61.141; control2Y: 33.72; x: 116.826; y: 28.086 } + , PathCubic { control1X: 116.826; control1Y: 28.086; control2X: 78.518; control2Y: 15.976; x: 11.526; y: 104.465 } + ] + } + + ControlledShape { + fillColor: "#ffffff" + strokeColor: "#000000" + strokeWidth: 0.1 + delegate: [PathMove { x: 22.726; y: 102.665 } + , PathCubic { control1X: 22.726; control1Y: 102.665; control2X: 21.363; control2Y: 101.472; x: 23.231; y: 100.847 } + , PathCubic { control1X: 25.099; control1Y: 100.222; control2X: 137.541; control2Y: 27.72; x: 176.826; y: 35.686 } + , PathCubic { control1X: 176.826; control1Y: 35.686; control2X: 149.719; control2Y: 28.176; x: 22.726; y: 102.665 } + ] + } + + ControlledShape { + fillColor: "#ffffff" + strokeColor: "#000000" + strokeWidth: 0.1 + delegate: [PathMove { x: 1.885; y: 108.767 } + , PathCubic { control1X: 1.885; control1Y: 108.767; control2X: 1.376; control2Y: 110.366; x: 3.087; y: 109.39 } + , PathCubic { control1X: 12.062; control1Y: 104.27; control2X: 15.677; control2Y: 47.059; x: 59.254; y: 45.804 } + , PathCubic { control1X: 59.254; control1Y: 45.804; control2X: 26.843; control2Y: 31.09; x: 1.885; y: 108.767 } + ] + } + + ControlledShape { + fillColor: "#ffffff" + strokeColor: "#000000" + strokeWidth: 0.1 + delegate: [PathMove { x: -18.038; y: 119.793 } + , PathCubic { control1X: -18.038; control1Y: 119.793; control2X: -19.115; control2Y: 121.079; x: -17.162; y: 120.825 } + , PathCubic { control1X: -6.916; control1Y: 119.493; control2X: 14.489; control2Y: 78.222; x: 58.928; y: 83.301 } + , PathCubic { control1X: 58.928; control1Y: 83.301; control2X: 26.962; control2Y: 68.955; x: -18.038; y: 119.793 } + ] + } + + ControlledShape { + fillColor: "#ffffff" + strokeColor: "#000000" + strokeWidth: 0.1 + delegate: [PathMove { x: -6.8; y: 113.667 } + , PathCubic { control1X: -6.8; control1Y: 113.667; control2X: -7.611; control2Y: 115.136; x: -5.742; y: 114.511 } + , PathCubic { control1X: 4.057; control1Y: 111.237; control2X: 17.141; control2Y: 66.625; x: 61.729; y: 63.078 } + , PathCubic { control1X: 61.729; control1Y: 63.078; control2X: 27.603; control2Y: 55.135; x: -6.8; y: 113.667 } + ] + } + + ControlledShape { + fillColor: "#ffffff" + strokeColor: "#000000" + strokeWidth: 0.1 + delegate: [PathMove { x: -25.078; y: 124.912 } + , PathCubic { control1X: -25.078; control1Y: 124.912; control2X: -25.951; control2Y: 125.954; x: -24.369; y: 125.748 } + , PathCubic { control1X: -16.07; control1Y: 124.669; control2X: 1.268; control2Y: 91.24; x: 37.264; y: 95.354 } + , PathCubic { control1X: 37.264; control1Y: 95.354; control2X: 11.371; control2Y: 83.734; x: -25.078; y: 124.912 } + ] + } + + ControlledShape { + fillColor: "#ffffff" + strokeColor: "#000000" + strokeWidth: 0.1 + delegate: [PathMove { x: -32.677; y: 130.821 } + , PathCubic { control1X: -32.677; control1Y: 130.821; control2X: -33.682; control2Y: 131.866; x: -32.091; y: 131.748 } + , PathCubic { control1X: -27.923; control1Y: 131.439; control2X: 2.715; control2Y: 98.36; x: 21.183; y: 113.862 } + , PathCubic { control1X: 21.183; control1Y: 113.862; control2X: 9.168; control2Y: 95.139; x: -32.677; y: 130.821 } + ] + } + + ControlledShape { + fillColor: "#ffffff" + strokeColor: "#000000" + strokeWidth: 0.1 + delegate: [PathMove { x: 36.855; y: 98.898 } + , PathCubic { control1X: 36.855; control1Y: 98.898; control2X: 35.654; control2Y: 97.543; x: 37.586; y: 97.158 } + , PathCubic { control1X: 39.518; control1Y: 96.774; control2X: 160.221; control2Y: 39.061; x: 198.184; y: 51.927 } + , PathCubic { control1X: 198.184; control1Y: 51.927; control2X: 172.243; control2Y: 41.053; x: 36.855; y: 98.898 } + ] + } + + ControlledShape { + fillColor: "#ffffff" + strokeColor: "#000000" + strokeWidth: 0.1 + delegate: [PathMove { x: 3.4; y: 163.201 } + , PathCubic { control1X: 3.4; control1Y: 163.201; control2X: 3.2; control2Y: 161.401; x: 5; y: 162.201 } + , PathCubic { control1X: 6.8; control1Y: 163.001; control2X: 22.2; control2Y: 163.601; x: 9.8; y: 186.001 } + , PathCubic { control1X: 9.8; control1Y: 186.001; control2X: 19.4; control2Y: 162.001; x: 3.4; y: 163.201 } + ] + } + + ControlledShape { + fillColor: "#ffffff" + strokeColor: "#000000" + strokeWidth: 0.1 + delegate: [PathMove { x: 13.8; y: 161.601 } + , PathCubic { control1X: 13.8; control1Y: 161.601; control2X: 13.6; control2Y: 159.801; x: 15.4; y: 160.601 } + , PathCubic { control1X: 17.2; control1Y: 161.401; control2X: 35; control2Y: 163.601; x: 37; y: 202.401 } + , PathCubic { control1X: 37; control1Y: 202.401; control2X: 29.8; control2Y: 160.401; x: 13.8; y: 161.601 } + ] + } + + ControlledShape { + fillColor: "#ffffff" + strokeColor: "#000000" + strokeWidth: 0.1 + delegate: [PathMove { x: 20.6; y: 160.001 } + , PathCubic { control1X: 20.6; control1Y: 160.001; control2X: 20.4; control2Y: 158.201; x: 22.2; y: 159.001 } + , PathCubic { control1X: 24; control1Y: 159.801; control2X: 48.6; control2Y: 163.201; x: 72.2; y: 195.601 } + , PathCubic { control1X: 72.2; control1Y: 195.601; control2X: 36.6; control2Y: 158.801; x: 20.6; y: 160.001 } + ] + } + + ControlledShape { + fillColor: "#ffffff" + strokeColor: "#000000" + strokeWidth: 0.1 + delegate: [PathMove { x: 28.225; y: 157.972 } + , PathCubic { control1X: 28.225; control1Y: 157.972; control2X: 27.788; control2Y: 156.214; x: 29.678; y: 156.768 } + , PathCubic { control1X: 31.568; control1Y: 157.322; control2X: 52.002; control2Y: 155.423; x: 90.099; y: 189.599 } + , PathCubic { control1X: 90.099; control1Y: 189.599; control2X: 43.924; control2Y: 154.656; x: 28.225; y: 157.972 } + ] + } + + ControlledShape { + fillColor: "#ffffff" + strokeColor: "#000000" + strokeWidth: 0.1 + delegate: [PathMove { x: 38.625; y: 153.572 } + , PathCubic { control1X: 38.625; control1Y: 153.572; control2X: 38.188; control2Y: 151.814; x: 40.078; y: 152.368 } + , PathCubic { control1X: 41.968; control1Y: 152.922; control2X: 76.802; control2Y: 157.423; x: 128.499; y: 192.399 } + , PathCubic { control1X: 128.499; control1Y: 192.399; control2X: 54.324; control2Y: 150.256; x: 38.625; y: 153.572 } + ] + } + + ControlledShape { + fillColor: "#ffffff" + strokeColor: "#000000" + strokeWidth: 0.1 + delegate: [PathMove { x: -1.8; y: 142.001 } + , PathCubic { control1X: -1.8; control1Y: 142.001; control2X: -2; control2Y: 140.201; x: -0.2; y: 141.001 } + , PathCubic { control1X: 1.6; control1Y: 141.801; control2X: 55; control2Y: 144.401; x: 85.4; y: 171.201 } + , PathCubic { control1X: 85.4; control1Y: 171.201; control2X: 50.499; control2Y: 146.426; x: -1.8; y: 142.001 } + ] + } + + ControlledShape { + fillColor: "#ffffff" + strokeColor: "#000000" + strokeWidth: 0.1 + delegate: [PathMove { x: -11.8; y: 146.001 } + , PathCubic { control1X: -11.8; control1Y: 146.001; control2X: -12; control2Y: 144.201; x: -10.2; y: 145.001 } + , PathCubic { control1X: -8.4; control1Y: 145.801; control2X: 16.2; control2Y: 149.201; x: 39.8; y: 181.601 } + , PathCubic { control1X: 39.8; control1Y: 181.601; control2X: 4.2; control2Y: 144.801; x: -11.8; y: 146.001 } + ] + } + + ControlledShape { + fillColor: "#ffffff" + strokeColor: "#000000" + strokeWidth: 0.1 + delegate: [PathMove { x: 49.503; y: 148.962 } + , PathCubic { control1X: 49.503; control1Y: 148.962; control2X: 48.938; control2Y: 147.241; x: 50.864; y: 147.655 } + , PathCubic { control1X: 52.79; control1Y: 148.068; control2X: 87.86; control2Y: 150.004; x: 141.981; y: 181.098 } + , PathCubic { control1X: 141.981; control1Y: 181.098; control2X: 64.317; control2Y: 146.704; x: 49.503; y: 148.962 } + ] + } + + ControlledShape { + fillColor: "#ffffff" + strokeColor: "#000000" + strokeWidth: 0.1 + delegate: [PathMove { x: 57.903; y: 146.562 } + , PathCubic { control1X: 57.903; control1Y: 146.562; control2X: 57.338; control2Y: 144.841; x: 59.264; y: 145.255 } + , PathCubic { control1X: 61.19; control1Y: 145.668; control2X: 96.26; control2Y: 147.604; x: 150.381; y: 178.698 } + , PathCubic { control1X: 150.381; control1Y: 178.698; control2X: 73.317; control2Y: 143.904; x: 57.903; y: 146.562 } + ] + } + + ControlledShape { + fillColor: "#ffffff" + strokeColor: "#000000" + strokeWidth: 0.1 + delegate: [PathMove { x: 67.503; y: 141.562 } + , PathCubic { control1X: 67.503; control1Y: 141.562; control2X: 66.938; control2Y: 139.841; x: 68.864; y: 140.255 } + , PathCubic { control1X: 70.79; control1Y: 140.668; control2X: 113.86; control2Y: 145.004; x: 203.582; y: 179.298 } + , PathCubic { control1X: 203.582; control1Y: 179.298; control2X: 82.917; control2Y: 138.904; x: 67.503; y: 141.562 } + ] + } + + ControlledShape { + fillColor: "#000000" + strokeWidth: -1 + delegate: [PathMove { x: -43.8; y: 148.401 } + , PathCubic { control1X: -43.8; control1Y: 148.401; control2X: -38.6; control2Y: 148.001; x: -39.8; y: 149.601 } + , PathCubic { control1X: -41; control1Y: 151.201; control2X: -43.4; control2Y: 150.401; x: -43.4; y: 150.401 } + , PathLine { x: -43.8; y: 148.401 } + ] + } + + ControlledShape { + fillColor: "#000000" + strokeWidth: -1 + delegate: [PathMove { x: -13; y: 162.401 } + , PathCubic { control1X: -13; control1Y: 162.401; control2X: -7.8; control2Y: 162.001; x: -9; y: 163.601 } + , PathCubic { control1X: -10.2; control1Y: 165.201; control2X: -12.6; control2Y: 164.401; x: -12.6; y: 164.401 } + , PathLine { x: -13; y: 162.401 } + ] + } + + ControlledShape { + fillColor: "#000000" + strokeWidth: -1 + delegate: [PathMove { x: -21.8; y: 162.001 } + , PathCubic { control1X: -21.8; control1Y: 162.001; control2X: -16.6; control2Y: 161.601; x: -17.8; y: 163.201 } + , PathCubic { control1X: -19; control1Y: 164.801; control2X: -21.4; control2Y: 164.001; x: -21.4; y: 164.001 } + , PathLine { x: -21.8; y: 162.001 } + ] + } + + ControlledShape { + fillColor: "#000000" + strokeWidth: -1 + delegate: [PathMove { x: -117.169; y: 150.182 } + , PathCubic { control1X: -117.169; control1Y: 150.182; control2X: -112.124; control2Y: 151.505; x: -113.782; y: 152.624 } + , PathCubic { control1X: -115.439; control1Y: 153.744; control2X: -117.446; control2Y: 152.202; x: -117.446; y: 152.202 } + , PathLine { x: -117.169; y: 150.182 } + ] + } + + ControlledShape { + fillColor: "#000000" + strokeWidth: -1 + delegate: [PathMove { x: -115.169; y: 140.582 } + , PathCubic { control1X: -115.169; control1Y: 140.582; control2X: -110.124; control2Y: 141.905; x: -111.782; y: 143.024 } + , PathCubic { control1X: -113.439; control1Y: 144.144; control2X: -115.446; control2Y: 142.602; x: -115.446; y: 142.602 } + , PathLine { x: -115.169; y: 140.582 } + ] + } + + ControlledShape { + fillColor: "#000000" + strokeWidth: -1 + delegate: [PathMove { x: -122.369; y: 136.182 } + , PathCubic { control1X: -122.369; control1Y: 136.182; control2X: -117.324; control2Y: 137.505; x: -118.982; y: 138.624 } + , PathCubic { control1X: -120.639; control1Y: 139.744; control2X: -122.646; control2Y: 138.202; x: -122.646; y: 138.202 } + , PathLine { x: -122.369; y: 136.182 } + ] + } + + ControlledShape { + fillColor: "#cccccc" + strokeWidth: -1 + delegate: [PathMove { x: -42.6; y: 211.201 } + , PathCubic { control1X: -42.6; control1Y: 211.201; control2X: -44.2; control2Y: 211.201; x: -48.2; y: 213.201 } + , PathCubic { control1X: -50.2; control1Y: 213.201; control2X: -61.4; control2Y: 216.801; x: -67; y: 226.801 } + , PathCubic { control1X: -67; control1Y: 226.801; control2X: -54.6; control2Y: 217.201; x: -42.6; y: 211.201 } + ] + } + + ControlledShape { + fillColor: "#cccccc" + strokeWidth: -1 + delegate: [PathMove { x: 45.116; y: 303.847 } + , PathCubic { control1X: 45.257; control1Y: 304.105; control2X: 45.312; control2Y: 304.525; x: 45.604; y: 304.542 } + , PathCubic { control1X: 46.262; control1Y: 304.582; control2X: 47.495; control2Y: 304.883; x: 47.37; y: 304.247 } + , PathCubic { control1X: 46.522; control1Y: 299.941; control2X: 45.648; control2Y: 295.004; x: 41.515; y: 293.197 } + , PathCubic { control1X: 40.876; control1Y: 292.918; control2X: 39.434; control2Y: 293.331; x: 39.36; y: 294.215 } + , PathCubic { control1X: 39.233; control1Y: 295.739; control2X: 39.116; control2Y: 297.088; x: 39.425; y: 298.554 } + , PathCubic { control1X: 39.725; control1Y: 299.975; control2X: 41.883; control2Y: 299.985; x: 42.8; y: 298.601 } + , PathCubic { control1X: 43.736; control1Y: 300.273; control2X: 44.168; control2Y: 302.116; x: 45.116; y: 303.847 } + ] + } + + ControlledShape { + fillColor: "#cccccc" + strokeWidth: -1 + delegate: [PathMove { x: 34.038; y: 308.581 } + , PathCubic { control1X: 34.786; control1Y: 309.994; control2X: 34.659; control2Y: 311.853; x: 36.074; y: 312.416 } + , PathCubic { control1X: 36.814; control1Y: 312.71; control2X: 38.664; control2Y: 311.735; x: 38.246; y: 310.661 } + , PathCubic { control1X: 37.444; control1Y: 308.6; control2X: 37.056; control2Y: 306.361; x: 35.667; y: 304.55 } + , PathCubic { control1X: 35.467; control1Y: 304.288; control2X: 35.707; control2Y: 303.755; x: 35.547; y: 303.427 } + , PathCubic { control1X: 34.953; control1Y: 302.207; control2X: 33.808; control2Y: 301.472; x: 32.4; y: 301.801 } + , PathCubic { control1X: 31.285; control1Y: 304.004; control2X: 32.433; control2Y: 306.133; x: 33.955; y: 307.842 } + , PathCubic { control1X: 34.091; control1Y: 307.994; control2X: 33.925; control2Y: 308.37; x: 34.038; y: 308.581 } + ] + } + + ControlledShape { + fillColor: "#cccccc" + strokeWidth: -1 + delegate: [PathMove { x: -5.564; y: 303.391 } + , PathCubic { control1X: -5.672; control1Y: 303.014; control2X: -5.71; control2Y: 302.551; x: -5.545; y: 302.23 } + , PathCubic { control1X: -5.014; control1Y: 301.197; control2X: -4.221; control2Y: 300.075; x: -4.558; y: 299.053 } + , PathCubic { control1X: -4.906; control1Y: 297.997; control2X: -6.022; control2Y: 298.179; x: -6.672; y: 298.748 } + , PathCubic { control1X: -7.807; control1Y: 299.742; control2X: -7.856; control2Y: 301.568; x: -8.547; y: 302.927 } + , PathCubic { control1X: -8.743; control1Y: 303.313; control2X: -8.692; control2Y: 303.886; x: -9.133; y: 304.277 } + , PathCubic { control1X: -9.607; control1Y: 304.698; control2X: -10.047; control2Y: 306.222; x: -9.951; y: 306.793 } + , PathCubic { control1X: -9.898; control1Y: 307.106; control2X: -10.081; control2Y: 317.014; x: -9.859; y: 316.751 } + , PathCubic { control1X: -9.24; control1Y: 316.018; control2X: -6.19; control2Y: 306.284; x: -6.121; y: 305.392 } + , PathCubic { control1X: -6.064; control1Y: 304.661; control2X: -5.332; control2Y: 304.196; x: -5.564; y: 303.391 } + ] + } + + ControlledShape { + fillColor: "#cccccc" + strokeWidth: -1 + delegate: [PathMove { x: -31.202; y: 296.599 } + , PathCubic { control1X: -28.568; control1Y: 294.1; control2X: -25.778; control2Y: 291.139; x: -26.22; y: 287.427 } + , PathCubic { control1X: -26.336; control1Y: 286.451; control2X: -28.111; control2Y: 286.978; x: -28.298; y: 287.824 } + , PathCubic { control1X: -29.1; control1Y: 291.449; control2X: -31.139; control2Y: 294.11; x: -33.707; y: 296.502 } + , PathCubic { control1X: -35.903; control1Y: 298.549; control2X: -37.765; control2Y: 304.893; x: -38; y: 305.401 } + , PathCubic { control1X: -34.303; control1Y: 300.145; control2X: -32.046; control2Y: 297.399; x: -31.202; y: 296.599 } + ] + } + + ControlledShape { + fillColor: "#cccccc" + strokeWidth: -1 + delegate: [PathMove { x: -44.776; y: 290.635 } + , PathCubic { control1X: -44.253; control1Y: 290.265; control2X: -44.555; control2Y: 289.774; x: -44.338; y: 289.442 } + , PathCubic { control1X: -43.385; control1Y: 287.984; control2X: -42.084; control2Y: 286.738; x: -42.066; y: 285 } + , PathCubic { control1X: -42.063; control1Y: 284.723; control2X: -42.441; control2Y: 284.414; x: -42.776; y: 284.638 } + , PathCubic { control1X: -43.053; control1Y: 284.822; control2X: -43.395; control2Y: 284.952; x: -43.503; y: 285.082 } + , PathCubic { control1X: -45.533; control1Y: 287.531; control2X: -46.933; control2Y: 290.202; x: -48.376; y: 293.014 } + , PathCubic { control1X: -48.559; control1Y: 293.371; control2X: -49.703; control2Y: 297.862; x: -49.39; y: 297.973 } + , PathCubic { control1X: -49.151; control1Y: 298.058; control2X: -47.431; control2Y: 293.877; x: -47.221; y: 293.763 } + , PathCubic { control1X: -45.958; control1Y: 293.077; control2X: -45.946; control2Y: 291.462; x: -44.776; y: 290.635 } + ] + } + + ControlledShape { + fillColor: "#cccccc" + strokeWidth: -1 + delegate: [PathMove { x: -28.043; y: 310.179 } + , PathCubic { control1X: -27.599; control1Y: 309.31; control2X: -26.023; control2Y: 308.108; x: -26.136; y: 307.219 } + , PathCubic { control1X: -26.254; control1Y: 306.291; control2X: -25.786; control2Y: 304.848; x: -26.698; y: 305.536 } + , PathCubic { control1X: -27.955; control1Y: 306.484; control2X: -31.404; control2Y: 307.833; x: -31.674; y: 313.641 } + , PathCubic { control1X: -31.7; control1Y: 314.212; control2X: -28.726; control2Y: 311.519; x: -28.043; y: 310.179 } + ] + } + + ControlledShape { + fillColor: "#cccccc" + strokeWidth: -1 + delegate: [PathMove { x: -13.6; y: 293.001 } + , PathCubic { control1X: -13.2; control1Y: 292.333; control2X: -12.492; control2Y: 292.806; x: -12.033; y: 292.543 } + , PathCubic { control1X: -11.385; control1Y: 292.171; control2X: -10.774; control2Y: 291.613; x: -10.482; y: 290.964 } + , PathCubic { control1X: -9.512; control1Y: 288.815; control2X: -7.743; control2Y: 286.995; x: -7.6; y: 284.601 } + , PathCubic { control1X: -9.091; control1Y: 283.196; control2X: -9.77; control2Y: 285.236; x: -10.4; y: 286.201 } + , PathCubic { control1X: -11.723; control1Y: 284.554; control2X: -12.722; control2Y: 286.428; x: -14.022; y: 286.947 } + , PathCubic { control1X: -14.092; control1Y: 286.975; control2X: -14.305; control2Y: 286.628; x: -14.38; y: 286.655 } + , PathCubic { control1X: -15.557; control1Y: 287.095; control2X: -16.237; control2Y: 288.176; x: -17.235; y: 288.957 } + , PathCubic { control1X: -17.406; control1Y: 289.091; control2X: -17.811; control2Y: 288.911; x: -17.958; y: 289.047 } + , PathCubic { control1X: -18.61; control1Y: 289.65; control2X: -19.583; control2Y: 289.975; x: -19.863; y: 290.657 } + , PathCubic { control1X: -20.973; control1Y: 293.364; control2X: -24.113; control2Y: 295.459; x: -26; y: 303.001 } + , PathCubic { control1X: -25.619; control1Y: 303.91; control2X: -21.488; control2Y: 296.359; x: -21.001; y: 295.661 } + , PathCubic { control1X: -20.165; control1Y: 294.465; control2X: -20.047; control2Y: 297.322; x: -18.771; y: 296.656 } + , PathCubic { control1X: -18.72; control1Y: 296.629; control2X: -18.534; control2Y: 296.867; x: -18.4; y: 297.001 } + , PathCubic { control1X: -18.206; control1Y: 296.721; control2X: -17.988; control2Y: 296.492; x: -17.6; y: 296.601 } + , PathCubic { control1X: -17.6; control1Y: 296.201; control2X: -17.734; control2Y: 295.645; x: -17.533; y: 295.486 } + , PathCubic { control1X: -16.296; control1Y: 294.509; control2X: -16.38; control2Y: 293.441; x: -15.6; y: 292.201 } + , PathCubic { control1X: -15.142; control1Y: 292.99; control2X: -14.081; control2Y: 292.271; x: -13.6; y: 293.001 } + ] + } + + ControlledShape { + fillColor: "#cccccc" + strokeWidth: -1 + delegate: [PathMove { x: 46.2; y: 347.401 } + , PathCubic { control1X: 46.2; control1Y: 347.401; control2X: 53.6; control2Y: 327.001; x: 49.2; y: 315.801 } + , PathCubic { control1X: 49.2; control1Y: 315.801; control2X: 60.6; control2Y: 337.401; x: 56; y: 348.601 } + , PathCubic { control1X: 56; control1Y: 348.601; control2X: 55.6; control2Y: 338.201; x: 51.6; y: 333.201 } + , PathCubic { control1X: 51.6; control1Y: 333.201; control2X: 47.6; control2Y: 346.001; x: 46.2; y: 347.401 } + ] + } + + ControlledShape { + fillColor: "#cccccc" + strokeWidth: -1 + delegate: [PathMove { x: 31.4; y: 344.801 } + , PathCubic { control1X: 31.4; control1Y: 344.801; control2X: 36.8; control2Y: 336.001; x: 28.8; y: 317.601 } + , PathCubic { control1X: 28.8; control1Y: 317.601; control2X: 28; control2Y: 338.001; x: 21.2; y: 349.001 } + , PathCubic { control1X: 21.2; control1Y: 349.001; control2X: 35.4; control2Y: 328.801; x: 31.4; y: 344.801 } + ] + } + + ControlledShape { + fillColor: "#cccccc" + strokeWidth: -1 + delegate: [PathMove { x: 21.4; y: 342.801 } + , PathCubic { control1X: 21.4; control1Y: 342.801; control2X: 21.2; control2Y: 322.801; x: 21.6; y: 319.801 } + , PathCubic { control1X: 21.6; control1Y: 319.801; control2X: 17.8; control2Y: 336.401; x: 7.6; y: 346.001 } + , PathCubic { control1X: 7.6; control1Y: 346.001; control2X: 22; control2Y: 334.001; x: 21.4; y: 342.801 } + ] + } + + ControlledShape { + fillColor: "#cccccc" + strokeWidth: -1 + delegate: [PathMove { x: 11.8; y: 310.801 } + , PathCubic { control1X: 11.8; control1Y: 310.801; control2X: 17.8; control2Y: 324.401; x: 7.8; y: 342.801 } + , PathCubic { control1X: 7.8; control1Y: 342.801; control2X: 14.2; control2Y: 330.601; x: 9.4; y: 323.601 } + , PathCubic { control1X: 9.4; control1Y: 323.601; control2X: 12; control2Y: 320.201; x: 11.8; y: 310.801 } + ] + } + + ControlledShape { + fillColor: "#cccccc" + strokeWidth: -1 + delegate: [PathMove { x: -7.4; y: 342.401 } + , PathCubic { control1X: -7.4; control1Y: 342.401; control2X: -8.4; control2Y: 326.801; x: -6.6; y: 324.601 } + , PathCubic { control1X: -6.6; control1Y: 324.601; control2X: -6.4; control2Y: 318.201; x: -6.8; y: 317.201 } + , PathCubic { control1X: -6.8; control1Y: 317.201; control2X: -2.8; control2Y: 311.001; x: -2.6; y: 318.401 } + , PathCubic { control1X: -2.6; control1Y: 318.401; control2X: -1.2; control2Y: 326.201; x: 1.6; y: 330.801 } + , PathCubic { control1X: 1.6; control1Y: 330.801; control2X: 5.2; control2Y: 336.201; x: 5; y: 342.601 } + , PathCubic { control1X: 5; control1Y: 342.601; control2X: -5; control2Y: 312.401; x: -7.4; y: 342.401 } + ] + } + + ControlledShape { + fillColor: "#cccccc" + strokeWidth: -1 + delegate: [PathMove { x: -11; y: 314.801 } + , PathCubic { control1X: -11; control1Y: 314.801; control2X: -17.6; control2Y: 325.601; x: -19.4; y: 344.601 } + , PathCubic { control1X: -19.4; control1Y: 344.601; control2X: -20.8; control2Y: 338.401; x: -17; y: 324.001 } + , PathCubic { control1X: -17; control1Y: 324.001; control2X: -12.8; control2Y: 308.601; x: -11; y: 314.801 } + ] + } + + ControlledShape { + fillColor: "#cccccc" + strokeWidth: -1 + delegate: [PathMove { x: -32.8; y: 334.601 } + , PathCubic { control1X: -32.8; control1Y: 334.601; control2X: -27.8; control2Y: 329.201; x: -26.4; y: 324.201 } + , PathCubic { control1X: -26.4; control1Y: 324.201; control2X: -22.8; control2Y: 308.401; x: -29.2; y: 317.001 } + , PathCubic { control1X: -29.2; control1Y: 317.001; control2X: -29; control2Y: 325.001; x: -37.2; y: 332.401 } + , PathCubic { control1X: -37.2; control1Y: 332.401; control2X: -32.4; control2Y: 330.001; x: -32.8; y: 334.601 } + ] + } + + ControlledShape { + fillColor: "#cccccc" + strokeWidth: -1 + delegate: [PathMove { x: -38.6; y: 329.601 } + , PathCubic { control1X: -38.6; control1Y: 329.601; control2X: -35.2; control2Y: 312.201; x: -34.4; y: 311.401 } + , PathCubic { control1X: -34.4; control1Y: 311.401; control2X: -32.6; control2Y: 308.001; x: -35.4; y: 311.201 } + , PathCubic { control1X: -35.4; control1Y: 311.201; control2X: -44.2; control2Y: 330.401; x: -48.2; y: 337.001 } + , PathCubic { control1X: -48.2; control1Y: 337.001; control2X: -40.2; control2Y: 327.801; x: -38.6; y: 329.601 } + ] + } + + ControlledShape { + fillColor: "#cccccc" + strokeWidth: -1 + delegate: [PathMove { x: -44.4; y: 313.001 } + , PathCubic { control1X: -44.4; control1Y: 313.001; control2X: -32.8; control2Y: 290.601; x: -54.6; y: 316.401 } + , PathCubic { control1X: -54.6; control1Y: 316.401; control2X: -43.6; control2Y: 306.601; x: -44.4; y: 313.001 } + ] + } + + ControlledShape { + fillColor: "#cccccc" + strokeWidth: -1 + delegate: [PathMove { x: -59.8; y: 298.401 } + , PathCubic { control1X: -59.8; control1Y: 298.401; control2X: -55; control2Y: 279.601; x: -52.4; y: 279.801 } + , PathCubic { control1X: -52.4; control1Y: 279.801; control2X: -44.2; control2Y: 270.801; x: -50.8; y: 281.401 } + , PathCubic { control1X: -50.8; control1Y: 281.401; control2X: -56.8; control2Y: 291.001; x: -56.2; y: 300.801 } + , PathCubic { control1X: -56.2; control1Y: 300.801; control2X: -56.8; control2Y: 291.201; x: -59.8; y: 298.401 } + ] + } + + ControlledShape { + fillColor: "#cccccc" + strokeWidth: -1 + delegate: [PathMove { x: 270.5; y: 287 } + , PathCubic { control1X: 270.5; control1Y: 287; control2X: 258.5; control2Y: 277; x: 256; y: 273.5 } + , PathCubic { control1X: 256; control1Y: 273.5; control2X: 269.5; control2Y: 292; x: 269.5; y: 299 } + , PathCubic { control1X: 269.5; control1Y: 299; control2X: 272; control2Y: 291.5; x: 270.5; y: 287 } + ] + } + + ControlledShape { + fillColor: "#cccccc" + strokeWidth: -1 + delegate: [PathMove { x: 276; y: 265 } + , PathCubic { control1X: 276; control1Y: 265; control2X: 255; control2Y: 250; x: 251.5; y: 242.5 } + , PathCubic { control1X: 251.5; control1Y: 242.5; control2X: 278; control2Y: 272; x: 278; y: 276.5 } + , PathCubic { control1X: 278; control1Y: 276.5; control2X: 278.5; control2Y: 267.5; x: 276; y: 265 } + ] + } + + ControlledShape { + fillColor: "#cccccc" + strokeWidth: -1 + delegate: [PathMove { x: 293; y: 111 } + , PathCubic { control1X: 293; control1Y: 111; control2X: 281; control2Y: 103; x: 279.5; y: 105 } + , PathCubic { control1X: 279.5; control1Y: 105; control2X: 290; control2Y: 111.5; x: 292.5; y: 120 } + , PathCubic { control1X: 292.5; control1Y: 120; control2X: 291; control2Y: 111; x: 293; y: 111 } + ] + } + + ControlledShape { + fillColor: "#cccccc" + strokeWidth: -1 + delegate: [PathMove { x: 301.5; y: 191.5 } + , PathLine { x: 284; y: 179.5 } + , PathCubic { control1X: 284; control1Y: 179.5; control2X: 303; control2Y: 196.5; x: 303.5; y: 200.5 } + , PathLine { x: 301.5; y: 191.5 } + ] + } + + ControlledShape { + fillColor: "transparent" + strokeColor: "#000000" + strokeWidth: 1 + delegate: [PathMove { x: -89.25; y: 169 } + , PathLine { x: -67.25; y: 173.75 } + ] + } + + ControlledShape { + fillColor: "transparent" + strokeColor: "#000000" + strokeWidth: 1 + delegate: [PathMove { x: -39; y: 331 } + , PathCubic { control1X: -39; control1Y: 331; control2X: -39.5; control2Y: 327.5; x: -48.5; y: 338 } + ] + } + + ControlledShape { + fillColor: "transparent" + strokeColor: "#000000" + strokeWidth: 1 + delegate: [PathMove { x: -33.5; y: 336 } + , PathCubic { control1X: -33.5; control1Y: 336; control2X: -31.5; control2Y: 329.5; x: -38; y: 334 } + ] + } + + ControlledShape { + fillColor: "transparent" + strokeColor: "#000000" + strokeWidth: 1 + delegate: [PathMove { x: 20.5; y: 344.5 } + , PathCubic { control1X: 20.5; control1Y: 344.5; control2X: 22; control2Y: 333.5; x: 10.5; y: 346.5 } + ] + } +}