Adding improve corner radius.
diff --git a/include/rive/math/vec2d.hpp b/include/rive/math/vec2d.hpp index 21dcdd4..f2e7ec3 100644 --- a/include/rive/math/vec2d.hpp +++ b/include/rive/math/vec2d.hpp
@@ -46,6 +46,7 @@ static Vec2D transformDir(const Vec2D& a, const Mat2D& m); static float dot(Vec2D a, Vec2D b) { return a.x * b.x + a.y * b.y; } + static float cross(Vec2D a, Vec2D b) { return a.x * b.y - a.y * b.x; } static Vec2D scaleAndAdd(Vec2D a, Vec2D b, float scale) { return { a.x + b.x * scale,
diff --git a/src/shapes/path.cpp b/src/shapes/path.cpp index f6090fd..af2b688 100644 --- a/src/shapes/path.cpp +++ b/src/shapes/path.cpp
@@ -1,15 +1,26 @@ #include "rive/shapes/path.hpp" -#include "rive/math/circle_constant.hpp" #include "rive/renderer.hpp" #include "rive/shapes/cubic_vertex.hpp" #include "rive/shapes/cubic_detached_vertex.hpp" #include "rive/shapes/path_vertex.hpp" #include "rive/shapes/shape.hpp" #include "rive/shapes/straight_vertex.hpp" +#include "rive/math/math_types.hpp" #include <cassert> using namespace rive; +/// Compute an ideal control point distance to create a curve of the given +/// radius. +static float +computeIdealControlPointDistance(const Vec2D& toPrev, const Vec2D& toNext, float radius) { + // Get the angle between next and prev + float angle = fabs(atan2(Vec2D::cross(toPrev, toNext), Vec2D::dot(toPrev, toNext))); + + return (4.0f / 3.0f) * tan(math::PI / (2.0f * ((2.0f * math::PI) / angle))) * radius * + (angle < math::PI / 2 ? 1 + cos(angle) : 2.0f - sin(angle)); +} + StatusCode Path::onAddedClean(CoreContext* context) { StatusCode code = Super::onAddedClean(context); if (code != StatusCode::Ok) { @@ -89,13 +100,15 @@ pos; auto toNextLength = toNext.normalizeLength(); - float renderRadius = std::min(toPrevLength, std::min(toNextLength, radius)); + float renderRadius = + std::min(toPrevLength / 2.0f, std::min(toNextLength / 2.0f, radius)); + float idealDistance = computeIdealControlPointDistance(toPrev, toNext, renderRadius); startIn = start = Vec2D::scaleAndAdd(pos, toPrev, renderRadius); commandPath.move(startIn); - Vec2D outPoint = Vec2D::scaleAndAdd(pos, toPrev, icircleConstant * renderRadius); - Vec2D inPoint = Vec2D::scaleAndAdd(pos, toNext, icircleConstant * renderRadius); + Vec2D outPoint = Vec2D::scaleAndAdd(pos, toPrev, renderRadius - idealDistance); + Vec2D inPoint = Vec2D::scaleAndAdd(pos, toNext, renderRadius - idealDistance); out = Vec2D::scaleAndAdd(pos, toNext, renderRadius); commandPath.cubic(outPoint, inPoint, out); prevIsCubic = false; @@ -122,7 +135,10 @@ Vec2D pos = point.renderTranslation(); auto radius = point.radius(); if (radius > 0.0f) { - Vec2D toPrev = out - pos; + auto prev = vertices[i - 1]; + Vec2D toPrev = (prev->is<CubicVertex>() ? prev->as<CubicVertex>()->renderOut() + : prev->renderTranslation()) - + pos; auto toPrevLength = toPrev.normalizeLength(); auto next = vertices[(i + 1) % length]; @@ -132,7 +148,10 @@ pos; auto toNextLength = toNext.normalizeLength(); - float renderRadius = std::min(toPrevLength, std::min(toNextLength, radius)); + float renderRadius = + std::min(toPrevLength / 2.0f, std::min(toNextLength / 2.0f, radius)); + float idealDistance = + computeIdealControlPointDistance(toPrev, toNext, renderRadius); Vec2D translation = Vec2D::scaleAndAdd(pos, toPrev, renderRadius); if (prevIsCubic) { @@ -141,8 +160,8 @@ commandPath.line(translation); } - Vec2D outPoint = Vec2D::scaleAndAdd(pos, toPrev, icircleConstant * renderRadius); - Vec2D inPoint = Vec2D::scaleAndAdd(pos, toNext, icircleConstant * renderRadius); + Vec2D outPoint = Vec2D::scaleAndAdd(pos, toPrev, renderRadius - idealDistance); + Vec2D inPoint = Vec2D::scaleAndAdd(pos, toNext, renderRadius - idealDistance); out = Vec2D::scaleAndAdd(pos, toNext, renderRadius); commandPath.cubic(outPoint, inPoint, out); prevIsCubic = false; @@ -260,11 +279,13 @@ Vec2D toNext = nextPoint - pos; auto toNextLength = toNext.normalizeLength(); - auto renderRadius = - std::min(toPrevLength, std::min(toNextLength, point.radius())); + auto renderRadius = std::min(toPrevLength / 2.0f, + std::min(toNextLength / 2.0f, point.radius())); + float idealDistance = + computeIdealControlPointDistance(toPrev, toNext, renderRadius); Vec2D translation = Vec2D::scaleAndAdd(pos, toPrev, renderRadius); - Vec2D out = Vec2D::scaleAndAdd(pos, toPrev, icircleConstant * renderRadius); + Vec2D out = Vec2D::scaleAndAdd(pos, toPrev, renderRadius - idealDistance); { auto v1 = new DisplayCubicVertex(translation, out, translation); flat->addVertex(v1, transform); @@ -273,7 +294,7 @@ translation = Vec2D::scaleAndAdd(pos, toNext, renderRadius); - Vec2D in = Vec2D::scaleAndAdd(pos, toNext, icircleConstant * renderRadius); + Vec2D in = Vec2D::scaleAndAdd(pos, toNext, renderRadius - idealDistance); auto v2 = new DisplayCubicVertex(in, translation, translation); flat->addVertex(v2, transform); @@ -330,4 +351,4 @@ } } -#endif +#endif \ No newline at end of file