diff options
author | Benau <Benau@users.noreply.github.com> | 2021-08-25 04:32:50 +0800 |
---|---|---|
committer | GitHub <noreply@github.com> | 2021-08-24 22:32:50 +0200 |
commit | 53cafa9f3d0c8be33821fc7338b1da97e91d9cc6 (patch) | |
tree | 964a225219099a1a1c282e27913767da588191b4 /vendor/github.com/Benau/go_rlottie/vector_vpath.cpp | |
parent | d4195deb3a6305c49c50ff30e8af978c7f1bdd92 (diff) | |
download | matterbridge-msglm-53cafa9f3d0c8be33821fc7338b1da97e91d9cc6.tar.gz matterbridge-msglm-53cafa9f3d0c8be33821fc7338b1da97e91d9cc6.tar.bz2 matterbridge-msglm-53cafa9f3d0c8be33821fc7338b1da97e91d9cc6.zip |
Convert .tgs with go libraries (and cgo) (telegram) (#1569)
This commit adds support for go/cgo tgs conversion when building with the -tags `cgo`
The default binaries are still "pure" go and uses the old way of converting.
* Move lottie_convert.py conversion code to its own file
* Add optional libtgsconverter
* Update vendor
* Apply suggestions from code review
* Update bridge/helper/libtgsconverter.go
Co-authored-by: Wim <wim@42.be>
Diffstat (limited to 'vendor/github.com/Benau/go_rlottie/vector_vpath.cpp')
-rw-r--r-- | vendor/github.com/Benau/go_rlottie/vector_vpath.cpp | 709 |
1 files changed, 709 insertions, 0 deletions
diff --git a/vendor/github.com/Benau/go_rlottie/vector_vpath.cpp b/vendor/github.com/Benau/go_rlottie/vector_vpath.cpp new file mode 100644 index 00000000..470c6d42 --- /dev/null +++ b/vendor/github.com/Benau/go_rlottie/vector_vpath.cpp @@ -0,0 +1,709 @@ +/* + * Copyright (c) 2020 Samsung Electronics Co., Ltd. All rights reserved. + + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#include "vector_vpath.h" +#include <cassert> +#include <iterator> +#include <vector> +#include "vector_vbezier.h" +#include "vector_vdebug.h" +#include "vector_vline.h" +#include "vector_vrect.h" + +V_BEGIN_NAMESPACE + +void VPath::VPathData::transform(const VMatrix &m) +{ + for (auto &i : m_points) { + i = m.map(i); + } + mLengthDirty = true; +} + +float VPath::VPathData::length() const +{ + if (!mLengthDirty) return mLength; + + mLengthDirty = false; + mLength = 0.0; + + size_t i = 0; + for (auto e : m_elements) { + switch (e) { + case VPath::Element::MoveTo: + i++; + break; + case VPath::Element::LineTo: { + mLength += VLine(m_points[i - 1], m_points[i]).length(); + i++; + break; + } + case VPath::Element::CubicTo: { + mLength += VBezier::fromPoints(m_points[i - 1], m_points[i], + m_points[i + 1], m_points[i + 2]) + .length(); + i += 3; + break; + } + case VPath::Element::Close: + break; + } + } + + return mLength; +} + +void VPath::VPathData::checkNewSegment() +{ + if (mNewSegment) { + moveTo(0, 0); + mNewSegment = false; + } +} + +void VPath::VPathData::moveTo(float x, float y) +{ + mStartPoint = {x, y}; + mNewSegment = false; + m_elements.emplace_back(VPath::Element::MoveTo); + m_points.emplace_back(x, y); + m_segments++; + mLengthDirty = true; +} + +void VPath::VPathData::lineTo(float x, float y) +{ + checkNewSegment(); + m_elements.emplace_back(VPath::Element::LineTo); + m_points.emplace_back(x, y); + mLengthDirty = true; +} + +void VPath::VPathData::cubicTo(float cx1, float cy1, float cx2, float cy2, + float ex, float ey) +{ + checkNewSegment(); + m_elements.emplace_back(VPath::Element::CubicTo); + m_points.emplace_back(cx1, cy1); + m_points.emplace_back(cx2, cy2); + m_points.emplace_back(ex, ey); + mLengthDirty = true; +} + +void VPath::VPathData::close() +{ + if (empty()) return; + + const VPointF &lastPt = m_points.back(); + if (!fuzzyCompare(mStartPoint, lastPt)) { + lineTo(mStartPoint.x(), mStartPoint.y()); + } + m_elements.push_back(VPath::Element::Close); + mNewSegment = true; + mLengthDirty = true; +} + +void VPath::VPathData::reset() +{ + if (empty()) return; + + m_elements.clear(); + m_points.clear(); + m_segments = 0; + mLength = 0; + mLengthDirty = false; +} + +size_t VPath::VPathData::segments() const +{ + return m_segments; +} + +void VPath::VPathData::reserve(size_t pts, size_t elms) +{ + if (m_points.capacity() < m_points.size() + pts) + m_points.reserve(m_points.size() + pts); + if (m_elements.capacity() < m_elements.size() + elms) + m_elements.reserve(m_elements.size() + elms); +} + +static VPointF curvesForArc(const VRectF &, float, float, VPointF *, size_t *); +static constexpr float PATH_KAPPA = 0.5522847498f; +static constexpr float K_PI = 3.141592f; + +void VPath::VPathData::arcTo(const VRectF &rect, float startAngle, + float sweepLength, bool forceMoveTo) +{ + size_t point_count = 0; + VPointF pts[15]; + VPointF curve_start = + curvesForArc(rect, startAngle, sweepLength, pts, &point_count); + + reserve(point_count + 1, point_count / 3 + 1); + if (empty() || forceMoveTo) { + moveTo(curve_start.x(), curve_start.y()); + } else { + lineTo(curve_start.x(), curve_start.y()); + } + for (size_t i = 0; i < point_count; i += 3) { + cubicTo(pts[i].x(), pts[i].y(), pts[i + 1].x(), pts[i + 1].y(), + pts[i + 2].x(), pts[i + 2].y()); + } +} + +void VPath::VPathData::addCircle(float cx, float cy, float radius, + VPath::Direction dir) +{ + addOval(VRectF(cx - radius, cy - radius, 2 * radius, 2 * radius), dir); +} + +void VPath::VPathData::addOval(const VRectF &rect, VPath::Direction dir) +{ + if (rect.empty()) return; + + float x = rect.x(); + float y = rect.y(); + + float w = rect.width(); + float w2 = rect.width() / 2; + float w2k = w2 * PATH_KAPPA; + + float h = rect.height(); + float h2 = rect.height() / 2; + float h2k = h2 * PATH_KAPPA; + + reserve(13, 6); // 1Move + 4Cubic + 1Close + if (dir == VPath::Direction::CW) { + // moveto 12 o'clock. + moveTo(x + w2, y); + // 12 -> 3 o'clock + cubicTo(x + w2 + w2k, y, x + w, y + h2 - h2k, x + w, y + h2); + // 3 -> 6 o'clock + cubicTo(x + w, y + h2 + h2k, x + w2 + w2k, y + h, x + w2, y + h); + // 6 -> 9 o'clock + cubicTo(x + w2 - w2k, y + h, x, y + h2 + h2k, x, y + h2); + // 9 -> 12 o'clock + cubicTo(x, y + h2 - h2k, x + w2 - w2k, y, x + w2, y); + } else { + // moveto 12 o'clock. + moveTo(x + w2, y); + // 12 -> 9 o'clock + cubicTo(x + w2 - w2k, y, x, y + h2 - h2k, x, y + h2); + // 9 -> 6 o'clock + cubicTo(x, y + h2 + h2k, x + w2 - w2k, y + h, x + w2, y + h); + // 6 -> 3 o'clock + cubicTo(x + w2 + w2k, y + h, x + w, y + h2 + h2k, x + w, y + h2); + // 3 -> 12 o'clock + cubicTo(x + w, y + h2 - h2k, x + w2 + w2k, y, x + w2, y); + } + close(); +} + +void VPath::VPathData::addRect(const VRectF &rect, VPath::Direction dir) +{ + float x = rect.x(); + float y = rect.y(); + float w = rect.width(); + float h = rect.height(); + + if (vCompare(w, 0.f) && vCompare(h, 0.f)) return; + + reserve(5, 6); // 1Move + 4Line + 1Close + if (dir == VPath::Direction::CW) { + moveTo(x + w, y); + lineTo(x + w, y + h); + lineTo(x, y + h); + lineTo(x, y); + close(); + } else { + moveTo(x + w, y); + lineTo(x, y); + lineTo(x, y + h); + lineTo(x + w, y + h); + close(); + } +} + +void VPath::VPathData::addRoundRect(const VRectF &rect, float roundness, + VPath::Direction dir) +{ + if (2 * roundness > rect.width()) roundness = rect.width() / 2.0f; + if (2 * roundness > rect.height()) roundness = rect.height() / 2.0f; + addRoundRect(rect, roundness, roundness, dir); +} + +void VPath::VPathData::addRoundRect(const VRectF &rect, float rx, float ry, + VPath::Direction dir) +{ + if (vCompare(rx, 0.f) || vCompare(ry, 0.f)) { + addRect(rect, dir); + return; + } + + float x = rect.x(); + float y = rect.y(); + float w = rect.width(); + float h = rect.height(); + // clamp the rx and ry radius value. + rx = 2 * rx; + ry = 2 * ry; + if (rx > w) rx = w; + if (ry > h) ry = h; + + reserve(17, 10); // 1Move + 4Cubic + 1Close + if (dir == VPath::Direction::CW) { + moveTo(x + w, y + ry / 2.f); + arcTo(VRectF(x + w - rx, y + h - ry, rx, ry), 0, -90, false); + arcTo(VRectF(x, y + h - ry, rx, ry), -90, -90, false); + arcTo(VRectF(x, y, rx, ry), -180, -90, false); + arcTo(VRectF(x + w - rx, y, rx, ry), -270, -90, false); + close(); + } else { + moveTo(x + w, y + ry / 2.f); + arcTo(VRectF(x + w - rx, y, rx, ry), 0, 90, false); + arcTo(VRectF(x, y, rx, ry), 90, 90, false); + arcTo(VRectF(x, y + h - ry, rx, ry), 180, 90, false); + arcTo(VRectF(x + w - rx, y + h - ry, rx, ry), 270, 90, false); + close(); + } +} + +static float tForArcAngle(float angle); +void findEllipseCoords(const VRectF &r, float angle, float length, + VPointF *startPoint, VPointF *endPoint) +{ + if (r.empty()) { + if (startPoint) *startPoint = VPointF(); + if (endPoint) *endPoint = VPointF(); + return; + } + + float w2 = r.width() / 2; + float h2 = r.height() / 2; + + float angles[2] = {angle, angle + length}; + VPointF *points[2] = {startPoint, endPoint}; + + for (int i = 0; i < 2; ++i) { + if (!points[i]) continue; + + float theta = angles[i] - 360 * floorf(angles[i] / 360); + float t = theta / 90; + // truncate + int quadrant = int(t); + t -= quadrant; + + t = tForArcAngle(90 * t); + + // swap x and y? + if (quadrant & 1) t = 1 - t; + + float a, b, c, d; + VBezier::coefficients(t, a, b, c, d); + VPointF p(a + b + c * PATH_KAPPA, d + c + b * PATH_KAPPA); + + // left quadrants + if (quadrant == 1 || quadrant == 2) p.rx() = -p.x(); + + // top quadrants + if (quadrant == 0 || quadrant == 1) p.ry() = -p.y(); + + *points[i] = r.center() + VPointF(w2 * p.x(), h2 * p.y()); + } +} + +static float tForArcAngle(float angle) +{ + float radians, cos_angle, sin_angle, tc, ts, t; + + if (vCompare(angle, 0.f)) return 0; + if (vCompare(angle, 90.0f)) return 1; + + radians = (angle / 180) * K_PI; + + cos_angle = cosf(radians); + sin_angle = sinf(radians); + + // initial guess + tc = angle / 90; + + // do some iterations of newton's method to approximate cos_angle + // finds the zero of the function b.pointAt(tc).x() - cos_angle + tc -= ((((2 - 3 * PATH_KAPPA) * tc + 3 * (PATH_KAPPA - 1)) * tc) * tc + 1 - + cos_angle) // value + / (((6 - 9 * PATH_KAPPA) * tc + 6 * (PATH_KAPPA - 1)) * + tc); // derivative + tc -= ((((2 - 3 * PATH_KAPPA) * tc + 3 * (PATH_KAPPA - 1)) * tc) * tc + 1 - + cos_angle) // value + / (((6 - 9 * PATH_KAPPA) * tc + 6 * (PATH_KAPPA - 1)) * + tc); // derivative + + // initial guess + ts = tc; + // do some iterations of newton's method to approximate sin_angle + // finds the zero of the function b.pointAt(tc).y() - sin_angle + ts -= ((((3 * PATH_KAPPA - 2) * ts - 6 * PATH_KAPPA + 3) * ts + + 3 * PATH_KAPPA) * + ts - + sin_angle) / + (((9 * PATH_KAPPA - 6) * ts + 12 * PATH_KAPPA - 6) * ts + + 3 * PATH_KAPPA); + ts -= ((((3 * PATH_KAPPA - 2) * ts - 6 * PATH_KAPPA + 3) * ts + + 3 * PATH_KAPPA) * + ts - + sin_angle) / + (((9 * PATH_KAPPA - 6) * ts + 12 * PATH_KAPPA - 6) * ts + + 3 * PATH_KAPPA); + + // use the average of the t that best approximates cos_angle + // and the t that best approximates sin_angle + t = 0.5f * (tc + ts); + return t; +} + +// The return value is the starting point of the arc +static VPointF curvesForArc(const VRectF &rect, float startAngle, + float sweepLength, VPointF *curves, + size_t *point_count) +{ + if (rect.empty()) { + return {}; + } + + float x = rect.x(); + float y = rect.y(); + + float w = rect.width(); + float w2 = rect.width() / 2; + float w2k = w2 * PATH_KAPPA; + + float h = rect.height(); + float h2 = rect.height() / 2; + float h2k = h2 * PATH_KAPPA; + + VPointF points[16] = { + // start point + VPointF(x + w, y + h2), + + // 0 -> 270 degrees + VPointF(x + w, y + h2 + h2k), VPointF(x + w2 + w2k, y + h), + VPointF(x + w2, y + h), + + // 270 -> 180 degrees + VPointF(x + w2 - w2k, y + h), VPointF(x, y + h2 + h2k), + VPointF(x, y + h2), + + // 180 -> 90 degrees + VPointF(x, y + h2 - h2k), VPointF(x + w2 - w2k, y), VPointF(x + w2, y), + + // 90 -> 0 degrees + VPointF(x + w2 + w2k, y), VPointF(x + w, y + h2 - h2k), + VPointF(x + w, y + h2)}; + + if (sweepLength > 360) + sweepLength = 360; + else if (sweepLength < -360) + sweepLength = -360; + + // Special case fast paths + if (startAngle == 0.0f) { + if (vCompare(sweepLength, 360)) { + for (int i = 11; i >= 0; --i) curves[(*point_count)++] = points[i]; + return points[12]; + } else if (vCompare(sweepLength, -360)) { + for (int i = 1; i <= 12; ++i) curves[(*point_count)++] = points[i]; + return points[0]; + } + } + + int startSegment = int(floorf(startAngle / 90.0f)); + int endSegment = int(floorf((startAngle + sweepLength) / 90.0f)); + + float startT = (startAngle - startSegment * 90) / 90; + float endT = (startAngle + sweepLength - endSegment * 90) / 90; + + int delta = sweepLength > 0 ? 1 : -1; + if (delta < 0) { + startT = 1 - startT; + endT = 1 - endT; + } + + // avoid empty start segment + if (vIsZero(startT - float(1))) { + startT = 0; + startSegment += delta; + } + + // avoid empty end segment + if (vIsZero(endT)) { + endT = 1; + endSegment -= delta; + } + + startT = tForArcAngle(startT * 90); + endT = tForArcAngle(endT * 90); + + const bool splitAtStart = !vIsZero(startT); + const bool splitAtEnd = !vIsZero(endT - float(1)); + + const int end = endSegment + delta; + + // empty arc? + if (startSegment == end) { + const int quadrant = 3 - ((startSegment % 4) + 4) % 4; + const int j = 3 * quadrant; + return delta > 0 ? points[j + 3] : points[j]; + } + + VPointF startPoint, endPoint; + findEllipseCoords(rect, startAngle, sweepLength, &startPoint, &endPoint); + + for (int i = startSegment; i != end; i += delta) { + const int quadrant = 3 - ((i % 4) + 4) % 4; + const int j = 3 * quadrant; + + VBezier b; + if (delta > 0) + b = VBezier::fromPoints(points[j + 3], points[j + 2], points[j + 1], + points[j]); + else + b = VBezier::fromPoints(points[j], points[j + 1], points[j + 2], + points[j + 3]); + + // empty arc? + if (startSegment == endSegment && vCompare(startT, endT)) + return startPoint; + + if (i == startSegment) { + if (i == endSegment && splitAtEnd) + b = b.onInterval(startT, endT); + else if (splitAtStart) + b = b.onInterval(startT, 1); + } else if (i == endSegment && splitAtEnd) { + b = b.onInterval(0, endT); + } + + // push control points + curves[(*point_count)++] = b.pt2(); + curves[(*point_count)++] = b.pt3(); + curves[(*point_count)++] = b.pt4(); + } + + curves[*(point_count)-1] = endPoint; + + return startPoint; +} + +void VPath::VPathData::addPolystar(float points, float innerRadius, + float outerRadius, float innerRoundness, + float outerRoundness, float startAngle, + float cx, float cy, VPath::Direction dir) +{ + const static float POLYSTAR_MAGIC_NUMBER = 0.47829f / 0.28f; + float currentAngle = (startAngle - 90.0f) * K_PI / 180.0f; + float x; + float y; + float partialPointRadius = 0; + float anglePerPoint = (2.0f * K_PI / points); + float halfAnglePerPoint = anglePerPoint / 2.0f; + float partialPointAmount = points - floorf(points); + bool longSegment = false; + size_t numPoints = size_t(ceilf(points) * 2); + float angleDir = ((dir == VPath::Direction::CW) ? 1.0f : -1.0f); + bool hasRoundness = false; + + innerRoundness /= 100.0f; + outerRoundness /= 100.0f; + + if (!vCompare(partialPointAmount, 0)) { + currentAngle += + halfAnglePerPoint * (1.0f - partialPointAmount) * angleDir; + } + + if (!vCompare(partialPointAmount, 0)) { + partialPointRadius = + innerRadius + partialPointAmount * (outerRadius - innerRadius); + x = partialPointRadius * cosf(currentAngle); + y = partialPointRadius * sinf(currentAngle); + currentAngle += anglePerPoint * partialPointAmount / 2.0f * angleDir; + } else { + x = outerRadius * cosf(currentAngle); + y = outerRadius * sinf(currentAngle); + currentAngle += halfAnglePerPoint * angleDir; + } + + if (vIsZero(innerRoundness) && vIsZero(outerRoundness)) { + reserve(numPoints + 2, numPoints + 3); + } else { + reserve(numPoints * 3 + 2, numPoints + 3); + hasRoundness = true; + } + + moveTo(x + cx, y + cy); + + for (size_t i = 0; i < numPoints; i++) { + float radius = longSegment ? outerRadius : innerRadius; + float dTheta = halfAnglePerPoint; + if (!vIsZero(partialPointRadius) && i == numPoints - 2) { + dTheta = anglePerPoint * partialPointAmount / 2.0f; + } + if (!vIsZero(partialPointRadius) && i == numPoints - 1) { + radius = partialPointRadius; + } + float previousX = x; + float previousY = y; + x = radius * cosf(currentAngle); + y = radius * sinf(currentAngle); + + if (hasRoundness) { + float cp1Theta = + (atan2f(previousY, previousX) - K_PI / 2.0f * angleDir); + float cp1Dx = cosf(cp1Theta); + float cp1Dy = sinf(cp1Theta); + float cp2Theta = (atan2f(y, x) - K_PI / 2.0f * angleDir); + float cp2Dx = cosf(cp2Theta); + float cp2Dy = sinf(cp2Theta); + + float cp1Roundness = longSegment ? innerRoundness : outerRoundness; + float cp2Roundness = longSegment ? outerRoundness : innerRoundness; + float cp1Radius = longSegment ? innerRadius : outerRadius; + float cp2Radius = longSegment ? outerRadius : innerRadius; + + float cp1x = cp1Radius * cp1Roundness * POLYSTAR_MAGIC_NUMBER * + cp1Dx / points; + float cp1y = cp1Radius * cp1Roundness * POLYSTAR_MAGIC_NUMBER * + cp1Dy / points; + float cp2x = cp2Radius * cp2Roundness * POLYSTAR_MAGIC_NUMBER * + cp2Dx / points; + float cp2y = cp2Radius * cp2Roundness * POLYSTAR_MAGIC_NUMBER * + cp2Dy / points; + + if (!vIsZero(partialPointAmount) && + ((i == 0) || (i == numPoints - 1))) { + cp1x *= partialPointAmount; + cp1y *= partialPointAmount; + cp2x *= partialPointAmount; + cp2y *= partialPointAmount; + } + + cubicTo(previousX - cp1x + cx, previousY - cp1y + cy, x + cp2x + cx, + y + cp2y + cy, x + cx, y + cy); + } else { + lineTo(x + cx, y + cy); + } + + currentAngle += dTheta * angleDir; + longSegment = !longSegment; + } + + close(); +} + +void VPath::VPathData::addPolygon(float points, float radius, float roundness, + float startAngle, float cx, float cy, + VPath::Direction dir) +{ + // TODO: Need to support floating point number for number of points + const static float POLYGON_MAGIC_NUMBER = 0.25; + float currentAngle = (startAngle - 90.0f) * K_PI / 180.0f; + float x; + float y; + float anglePerPoint = 2.0f * K_PI / floorf(points); + size_t numPoints = size_t(floorf(points)); + float angleDir = ((dir == VPath::Direction::CW) ? 1.0f : -1.0f); + bool hasRoundness = false; + + roundness /= 100.0f; + + currentAngle = (currentAngle - 90.0f) * K_PI / 180.0f; + x = radius * cosf(currentAngle); + y = radius * sinf(currentAngle); + currentAngle += anglePerPoint * angleDir; + + if (vIsZero(roundness)) { + reserve(numPoints + 2, numPoints + 3); + } else { + reserve(numPoints * 3 + 2, numPoints + 3); + hasRoundness = true; + } + + moveTo(x + cx, y + cy); + + for (size_t i = 0; i < numPoints; i++) { + float previousX = x; + float previousY = y; + x = (radius * cosf(currentAngle)); + y = (radius * sinf(currentAngle)); + + if (hasRoundness) { + float cp1Theta = + (atan2f(previousY, previousX) - K_PI / 2.0f * angleDir); + float cp1Dx = cosf(cp1Theta); + float cp1Dy = sinf(cp1Theta); + float cp2Theta = atan2f(y, x) - K_PI / 2.0f * angleDir; + float cp2Dx = cosf(cp2Theta); + float cp2Dy = sinf(cp2Theta); + + float cp1x = radius * roundness * POLYGON_MAGIC_NUMBER * cp1Dx; + float cp1y = radius * roundness * POLYGON_MAGIC_NUMBER * cp1Dy; + float cp2x = radius * roundness * POLYGON_MAGIC_NUMBER * cp2Dx; + float cp2y = radius * roundness * POLYGON_MAGIC_NUMBER * cp2Dy; + + cubicTo(previousX - cp1x + cx, previousY - cp1y + cy, x + cp2x + cx, + y + cp2y + cy, x, y); + } else { + lineTo(x + cx, y + cy); + } + + currentAngle += anglePerPoint * angleDir; + } + + close(); +} + +void VPath::VPathData::addPath(const VPathData &path, const VMatrix *m) +{ + size_t segment = path.segments(); + + // make sure enough memory available + if (m_points.capacity() < m_points.size() + path.m_points.size()) + m_points.reserve(m_points.size() + path.m_points.size()); + + if (m_elements.capacity() < m_elements.size() + path.m_elements.size()) + m_elements.reserve(m_elements.size() + path.m_elements.size()); + + if (m) { + for (const auto &i : path.m_points) { + m_points.push_back(m->map(i)); + } + } else { + std::copy(path.m_points.begin(), path.m_points.end(), + back_inserter(m_points)); + } + + std::copy(path.m_elements.begin(), path.m_elements.end(), + back_inserter(m_elements)); + + m_segments += segment; + mLengthDirty = true; +} + +V_END_NAMESPACE |