| #include "rive/shapes/metrics_path.hpp" | |
| #include "rive/renderer.hpp" | |
| #include "rive/math/raw_path.hpp" | |
| #include "rive/math/contour_measure.hpp" | |
| #include <iostream> | |
| using namespace rive; | |
| void MetricsPath::rewind() | |
| { | |
| for (auto ptr : m_Paths) | |
| { | |
| delete ptr; | |
| } | |
| m_Paths.clear(); | |
| m_Contour.reset(nullptr); | |
| m_RawPath.rewind(); | |
| m_ComputedLengthTransform = Mat2D(); | |
| m_ComputedLength = 0; | |
| } | |
| MetricsPath::~MetricsPath() { rewind(); } | |
| void MetricsPath::addPath(CommandPath* path, const Mat2D& transform) | |
| { | |
| MetricsPath* metricsPath = static_cast<MetricsPath*>(path); | |
| m_ComputedLength += metricsPath->computeLength(transform); | |
| // We need to copy the data to avoid contention between multiple uses of the same path | |
| // for example when the same path is added as localPath and worldPath | |
| auto metricsPathCopy = new OnlyMetricsPath(); | |
| metricsPathCopy->m_Contour = metricsPath->m_Contour; | |
| metricsPathCopy->m_RawPath = metricsPath->m_RawPath; | |
| metricsPathCopy->m_ComputedLength = metricsPath->m_ComputedLength; | |
| m_Paths.emplace_back(metricsPathCopy); | |
| } | |
| RawPath::Iter MetricsPath::addToRawPath(RawPath& rawPath, const Mat2D& transform) const | |
| { | |
| return rawPath.addPath(m_RawPath, &transform); | |
| } | |
| void MetricsPath::moveTo(float x, float y) | |
| { | |
| assert(m_RawPath.points().size() == 0); | |
| m_RawPath.move({x, y}); | |
| } | |
| void MetricsPath::lineTo(float x, float y) { m_RawPath.line({x, y}); } | |
| void MetricsPath::cubicTo(float ox, float oy, float ix, float iy, float x, float y) | |
| { | |
| m_RawPath.cubic({ox, oy}, {ix, iy}, {x, y}); | |
| } | |
| void MetricsPath::close() | |
| { | |
| // Should we pass the close() to our m_RawPath ??? | |
| } | |
| float MetricsPath::computeLength(const Mat2D& transform) | |
| { | |
| // Only compute if our pre-computed length is not valid | |
| if (!m_Contour || transform != m_ComputedLengthTransform) | |
| { | |
| m_ComputedLengthTransform = transform; | |
| m_Contour = ContourMeasureIter(m_RawPath * transform).next(); | |
| m_ComputedLength = m_Contour ? m_Contour->length() : 0; | |
| } | |
| return m_ComputedLength; | |
| } | |
| void MetricsPath::trim(float startLength, float endLength, bool moveTo, RawPath* result) | |
| { | |
| assert(endLength >= startLength); | |
| if (!m_Paths.empty()) | |
| { | |
| m_Paths.front()->trim(startLength, endLength, moveTo, result); | |
| return; | |
| } | |
| if (!m_Contour) | |
| { | |
| // All the contours were 0 length, so there's nothing to segment. | |
| return; | |
| } | |
| // TODO: if we can change the signature of MetricsPath and/or trim() to speak native | |
| // rawpaths, we wouldn't need this temporary copy (since ContourMeasure speaks | |
| // native rawpaths). | |
| RawPath tmp; | |
| m_Contour->getSegment(startLength, endLength, result, moveTo); | |
| } | |
| RenderMetricsPath::RenderMetricsPath(rcp<RenderPath> path) : m_RenderPath(std::move(path)) {} | |
| void RenderMetricsPath::addPath(CommandPath* path, const Mat2D& transform) | |
| { | |
| MetricsPath::addPath(path, transform); | |
| m_RenderPath->addPath(path->renderPath(), transform); | |
| } | |
| void RenderMetricsPath::rewind() | |
| { | |
| MetricsPath::rewind(); | |
| m_RenderPath->rewind(); | |
| } | |
| void RenderMetricsPath::moveTo(float x, float y) | |
| { | |
| MetricsPath::moveTo(x, y); | |
| m_RenderPath->moveTo(x, y); | |
| } | |
| void RenderMetricsPath::lineTo(float x, float y) | |
| { | |
| MetricsPath::lineTo(x, y); | |
| m_RenderPath->lineTo(x, y); | |
| } | |
| void RenderMetricsPath::cubicTo(float ox, float oy, float ix, float iy, float x, float y) | |
| { | |
| MetricsPath::cubicTo(ox, oy, ix, iy, x, y); | |
| m_RenderPath->cubicTo(ox, oy, ix, iy, x, y); | |
| } | |
| void RenderMetricsPath::close() | |
| { | |
| MetricsPath::close(); | |
| m_RenderPath->close(); | |
| } | |
| void RenderMetricsPath::fillRule(FillRule value) { m_RenderPath->fillRule(value); } |