Adding more logic for beveling of contoured stroke.
diff --git a/src/contour_stroke.cpp b/src/contour_stroke.cpp index 0657d6e..1f0e342 100644 --- a/src/contour_stroke.cpp +++ b/src/contour_stroke.cpp
@@ -5,6 +5,8 @@ using namespace rive; +static const int subdivisionArcLength = 4.0f; + void ContourStroke::extrude(const ContourRenderPath* renderPath, bool isClosed, StrokeJoin join, @@ -14,8 +16,8 @@ m_TriangleStrip.clear(); const std::vector<Vec2D>& points = renderPath->contourVertices(); - auto size = points.size(); - if (size < 2) + auto pointCount = points.size(); + if (pointCount < 2) { return; } @@ -31,5 +33,156 @@ lastDiffNormalized[0] * strokeWidth); Vec2D::add(lastA, lastPoint, perpendicularStrokeDiff); Vec2D::subtract(lastB, lastPoint, perpendicularStrokeDiff); + + if (!isClosed) + { + switch (cap) + { + case StrokeCap::square: + { + Vec2D squareA, squareB; + Vec2D strokeDiff = Vec2D(lastDiffNormalized[0] * strokeWidth, + lastDiffNormalized[1] * strokeWidth); + Vec2D::subtract(squareA, lastA, strokeDiff); + Vec2D::subtract(squareB, lastB, strokeDiff); + m_TriangleStrip.push_back(squareA); + m_TriangleStrip.push_back(squareB); + break; + } + case StrokeCap::round: + { + Vec2D capDirection = + Vec2D(-lastDiffNormalized[1], lastDiffNormalized[0]); + float arcLength = std::abs(M_PI * strokeWidth); + int steps = (int)std::ceil(arcLength / subdivisionArcLength); + float angleTo = std::atan2(capDirection[1], capDirection[0]); + float inc = M_PI / steps; + float angle = angleTo; + // make sure to draw the full cap due triangle strip + for (int j = 0; j <= steps; j++) + { + m_TriangleStrip.push_back(lastPoint); + m_TriangleStrip.push_back( + Vec2D(lastPoint[0] + std::cos(angle) * strokeWidth, + lastPoint[1] + std::sin(angle) * strokeWidth)); + angle += inc; + } + break; + } + default: + break; + } + } + m_TriangleStrip.push_back(lastA); + m_TriangleStrip.push_back(lastB); + + std::size_t adjustedPointCount = isClosed ? pointCount + 1 : pointCount; + + for (std::size_t i = 1; i < adjustedPointCount; i++) + { + const Vec2D& point = points[i % pointCount]; + Vec2D diff, diffNormalized, next; + float length; + if (i < adjustedPointCount - 1 || isClosed) + { + Vec2D::subtract(diff, (next = points[(i + 1) % pointCount]), point); + length = Vec2D::length(diff); + Vec2D::scale(diffNormalized, diff, 1.0f / length); + } + else + { + diff = lastDiff; + next = point; + length = lastLength; + diffNormalized = lastDiffNormalized; + } + + // perpendicular dx + float pdx0 = -lastDiffNormalized[1]; + float pdy0 = lastDiffNormalized[0]; + float pdx1 = -diffNormalized[1]; + float pdy1 = diffNormalized[0]; + + // Compute bisector without a normalization by averaging perpendicular + // diffs. + Vec2D bisector((pdx0 + pdx1) * 0.5f, (pdy0 + pdy1) * 0.5f); + float cross = diff[0] * lastDiff[1] - lastDiff[0] * diff[1]; + + float dot = Vec2D::dot(bisector, bisector); + + float lengthLimit = std::min(length, lastLength); + bool bevelInner = false; + bool bevel = join == StrokeJoin::miter ? dot < 0.1f : dot < 0.999f; + + // Scale bisector to match stroke size. + if (dot > 0.000001f) + { + float scale = 1.0f / dot * strokeWidth; + float limit = lengthLimit / strokeWidth; + if (dot * limit * limit < 1.0f) + { + bevelInner = true; + } + bisector[0] *= scale; + bisector[1] *= scale; + } + else + { + bisector[0] *= strokeWidth; + bisector[1] *= strokeWidth; + } + + if (!bevel) + { + Vec2D c, d; + Vec2D::add(c, point, bisector); + Vec2D::subtract(d, point, bisector); + + if (!bevelInner) + { + // Normal mitered edge. + m_TriangleStrip.push_back(c); + m_TriangleStrip.push_back(d); + } + else if (cross <= 0) + { + // Overlap the inner (in this case right) edge (sometimes called + // miter inner). + Vec2D c1, c2; + Vec2D::add(c1, + point, + Vec2D(lastDiffNormalized[1] * -strokeWidth, + lastDiffNormalized[0] * strokeWidth)); + Vec2D::add(c2, + point, + Vec2D(diffNormalized[1] * -strokeWidth, + diffNormalized[0] * strokeWidth)); + + m_TriangleStrip.push_back(c1); + m_TriangleStrip.push_back(d); + m_TriangleStrip.push_back(c2); + m_TriangleStrip.push_back(d); + } + else + { + // Overlap the inner (in this case left) edge (sometimes called + // miter inner). + Vec2D d1, d2; + Vec2D::subtract(d1, + point, + Vec2D(lastDiffNormalized[1] * -strokeWidth, + lastDiffNormalized[0] * strokeWidth)); + Vec2D::subtract(d2, + point, + Vec2D(diffNormalized[1] * -strokeWidth, + diffNormalized[0] * strokeWidth)); + + m_TriangleStrip.push_back(c); + m_TriangleStrip.push_back(d1); + m_TriangleStrip.push_back(c); + m_TriangleStrip.push_back(d2); + } + } + } } #endif \ No newline at end of file