mirror of
https://github.com/thorvg/thorvg.git
synced 2025-06-21 07:22:23 +00:00
lottie: ensure proper shape closure
Some checks are pending
Android / build_x86_64 (push) Waiting to run
Android / build_aarch64 (push) Waiting to run
iOS / build_x86_64 (push) Waiting to run
iOS / build_arm64 (push) Waiting to run
macOS / build (push) Waiting to run
macOS / compact_test (push) Waiting to run
macOS / unit_test (push) Waiting to run
Ubuntu / build (push) Waiting to run
Ubuntu / compact_test (push) Waiting to run
Ubuntu / unit_test (push) Waiting to run
Windows / build (push) Waiting to run
Windows / compact_test (push) Waiting to run
Windows / unit_test (push) Waiting to run
Some checks are pending
Android / build_x86_64 (push) Waiting to run
Android / build_aarch64 (push) Waiting to run
iOS / build_x86_64 (push) Waiting to run
iOS / build_arm64 (push) Waiting to run
macOS / build (push) Waiting to run
macOS / compact_test (push) Waiting to run
macOS / unit_test (push) Waiting to run
Ubuntu / build (push) Waiting to run
Ubuntu / compact_test (push) Waiting to run
Ubuntu / unit_test (push) Waiting to run
Windows / build (push) Waiting to run
Windows / compact_test (push) Waiting to run
Windows / unit_test (push) Waiting to run
- Ensured proper closure of star and polygon shapes. The start and end points now match - in cases with degenerate bezier curves, the second-to-last point is also aligned. Proper shape closure is necessary for modifiers like offset (future pucker bloat). If the start and end points aren’t equal (within the comparison function’s precision), the shape will be closed with a straight line - and during offsetting, that line will become visible, even though it’s not intended. - Use of the internal _zero() function for point equality check in modifiers algs led to incorrect results when p2.x or p2.y was zero (division by zero). The intent was to treat nearly identical points as equal, but this approach was flawed - at the modifier stage, it’s no longer possible to tell if small gaps are intentional or just due to limited numerical precision (as seen for example in the difference between the start and end points of star/polygon shapes).
This commit is contained in:
parent
e20562c8c2
commit
2679880bc3
2 changed files with 14 additions and 10 deletions
|
@ -488,6 +488,13 @@ void LottieBuilder::updatePath(LottieGroup* parent, LottieObject** child, float
|
|||
}
|
||||
|
||||
|
||||
static void _close(Array<Point>& pts, const Point& p, bool round)
|
||||
{
|
||||
if (round && tvg::zero(pts.last() - pts[pts.count - 2])) pts[pts.count - 2] = p;
|
||||
pts.last() = p;
|
||||
}
|
||||
|
||||
|
||||
void LottieBuilder::updateStar(LottiePolyStar* star, float frameNo, Matrix* transform, Shape* merging, RenderContext* ctx, Tween& tween, LottieExpressions* exps)
|
||||
{
|
||||
static constexpr auto POLYSTAR_MAGIC_NUMBER = 0.47829f / 0.28f;
|
||||
|
@ -595,6 +602,8 @@ void LottieBuilder::updateStar(LottiePolyStar* star, float frameNo, Matrix* tran
|
|||
angle += dTheta * direction;
|
||||
longSegment = !longSegment;
|
||||
}
|
||||
//ensure proper shape closure - important for modifiers that behave differently for degenerate (linear) vs curved cubics
|
||||
_close(SHAPE(shape)->rs.path.pts, in, hasRoundness);
|
||||
shape->close();
|
||||
|
||||
if (ctx->modifier) ctx->modifier->modifyPolystar(SHAPE(shape)->rs.path, SHAPE(merging)->rs.path, outerRoundness, hasRoundness);
|
||||
|
@ -663,6 +672,8 @@ void LottieBuilder::updatePolygon(LottieGroup* parent, LottiePolyStar* star, flo
|
|||
}
|
||||
angle += anglePerPoint * direction;
|
||||
}
|
||||
//ensure proper shape closure - important for modifiers that behave differently for degenerate (linear) vs curved cubics
|
||||
_close(SHAPE(shape)->rs.path.pts, in, hasRoundness);
|
||||
shape->close();
|
||||
|
||||
if (ctx->modifier) ctx->modifier->modifyPolystar(SHAPE(shape)->rs.path, SHAPE(merging)->rs.path, 0.0f, false);
|
||||
|
|
|
@ -52,16 +52,9 @@ static void _roundCorner(Array<PathCommand>& cmds, Array<Point>& pts, Point& pre
|
|||
}
|
||||
|
||||
|
||||
static bool _zero(Point& p1, Point& p2)
|
||||
{
|
||||
constexpr float epsilon = 1e-3f;
|
||||
return fabsf(p1.x / p2.x - 1.0f) < epsilon && fabsf(p1.y / p2.y - 1.0f) < epsilon;
|
||||
}
|
||||
|
||||
|
||||
static bool _intersect(Line& line1, Line& line2, Point& intersection, bool& inside)
|
||||
{
|
||||
if (_zero(line1.pt2, line2.pt1)) {
|
||||
if (tvg::zero(line1.pt2 - line2.pt1)) {
|
||||
intersection = line1.pt2;
|
||||
inside = true;
|
||||
return true;
|
||||
|
@ -168,7 +161,7 @@ void LottieOffsetModifier::line(RenderPath& out, PathCommand* inCmds, uint32_t i
|
|||
Line nextLine = state.firstLine;
|
||||
if (inCmds[curCmd + 1] == PathCommand::LineTo) nextLine = _offset(inPts[curPt + degenerated], inPts[curPt + 1 + degenerated], offset);
|
||||
else if (inCmds[curCmd + 1] == PathCommand::CubicTo) nextLine = _offset(inPts[curPt + 1 + degenerated], inPts[curPt + 2 + degenerated], offset);
|
||||
else if (inCmds[curCmd + 1] == PathCommand::Close && !_zero(inPts[curPt + degenerated], inPts[state.movetoInIndex + degenerated]))
|
||||
else if (inCmds[curCmd + 1] == PathCommand::Close && !tvg::zero(inPts[curPt + degenerated] - inPts[state.movetoInIndex + degenerated]))
|
||||
nextLine = _offset(inPts[curPt + degenerated], inPts[state.movetoInIndex + degenerated], offset);
|
||||
|
||||
corner(out, state.line, nextLine, state.movetoOutIndex, inCmds[curCmd + 1] == PathCommand::Close);
|
||||
|
@ -386,7 +379,7 @@ bool LottieOffsetModifier::modifyPath(PathCommand* inCmds, uint32_t inCmdsCnt, P
|
|||
iPt += 3;
|
||||
}
|
||||
else {
|
||||
if (!_zero(inPts[iPt - 1], inPts[state.movetoInIndex])) {
|
||||
if (!tvg::zero(inPts[iPt - 1] - inPts[state.movetoInIndex])) {
|
||||
out.cmds.push(PathCommand::LineTo);
|
||||
corner(out, state.line, state.firstLine, state.movetoOutIndex, true);
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue