| #include "rive/tess/segmented_contour.hpp" |
| #include "rive/math/raw_path.hpp" |
| #include "rive/math/cubic_utilities.hpp" |
| |
| using namespace rive; |
| |
| SegmentedContour::SegmentedContour(float threshold) : |
| m_bounds(AABB::forExpansion()), |
| m_threshold(threshold), |
| m_thresholdSquared(threshold * threshold) |
| {} |
| |
| float SegmentedContour::threshold() const { return m_threshold; } |
| void SegmentedContour::threshold(float value) |
| { |
| m_threshold = value; |
| m_thresholdSquared = value * value; |
| } |
| const AABB& SegmentedContour::bounds() const { return m_bounds; } |
| void SegmentedContour::addVertex(Vec2D vertex) |
| { |
| m_contourPoints.push_back(vertex); |
| AABB::expandTo(m_bounds, vertex); |
| } |
| |
| const std::size_t SegmentedContour::contourSize() const { return m_contourPoints.size(); } |
| |
| const Span<const Vec2D> SegmentedContour::contourPoints(uint32_t endOffset) const |
| { |
| assert(endOffset <= m_contourPoints.size()); |
| return Span<const Vec2D>(m_contourPoints.data(), m_contourPoints.size() - endOffset); |
| } |
| |
| void SegmentedContour::segmentCubic(const Vec2D& from, |
| const Vec2D& fromOut, |
| const Vec2D& toIn, |
| const Vec2D& to, |
| float t1, |
| float t2) |
| { |
| if (CubicUtilities::shouldSplitCubic(from, fromOut, toIn, to, m_threshold)) |
| { |
| float halfT = (t1 + t2) / 2.0f; |
| |
| Vec2D hull[6]; |
| CubicUtilities::computeHull(from, fromOut, toIn, to, 0.5f, hull); |
| |
| segmentCubic(from, hull[0], hull[3], hull[5], t1, halfT); |
| |
| segmentCubic(hull[5], hull[4], hull[2], to, halfT, t2); |
| } |
| else |
| { |
| if (Vec2D::distanceSquared(from, to) > m_thresholdSquared) |
| { |
| addVertex(Vec2D(CubicUtilities::cubicAt(t2, from.x, fromOut.x, toIn.x, to.x), |
| CubicUtilities::cubicAt(t2, from.y, fromOut.y, toIn.y, to.y))); |
| } |
| } |
| } |
| |
| void SegmentedContour::contour(const RawPath& rawPath, const Mat2D& transform) |
| { |
| m_contourPoints.clear(); |
| |
| // Possible perf consideration: could add second path that doesn't transform |
| // if transform is the identity. |
| for (auto tuple : rawPath) |
| { |
| PathVerb verb = std::get<0>(tuple); |
| const Vec2D* pts = std::get<1>(tuple); |
| |
| switch (verb) |
| { |
| case PathVerb::move: |
| addVertex(transform * pts[0]); |
| break; |
| case PathVerb::line: |
| addVertex(transform * pts[1]); |
| break; |
| case PathVerb::cubic: |
| segmentCubic(transform * pts[0], |
| transform * pts[1], |
| transform * pts[2], |
| transform * pts[3], |
| 0.0f, |
| 1.0f); |
| break; |
| case PathVerb::close: |
| break; |
| case PathVerb::quad: |
| // TODO: not currently used by render paths, however might be |
| // necessary for fonts. |
| break; |
| } |
| } |
| } |