Skip to content

Commit

Permalink
Properly handle offset path for open shapes
Browse files Browse the repository at this point in the history
  • Loading branch information
mbasaglia committed Jul 27, 2022
1 parent 31f8d1e commit 9dee1a1
Show file tree
Hide file tree
Showing 2 changed files with 56 additions and 35 deletions.
5 changes: 5 additions & 0 deletions player/js/utils/PolynomialBezier.js
Original file line number Diff line number Diff line change
Expand Up @@ -199,6 +199,11 @@ PolynomialBezier.shapeSegment = function (shapePath, index) {
return new PolynomialBezier(shapePath.v[index], shapePath.o[index], shapePath.i[nextIndex], shapePath.v[nextIndex], true);
};

PolynomialBezier.shapeSegmentInverted = function (shapePath, index) {
var nextIndex = (index + 1) % shapePath.length();
return new PolynomialBezier(shapePath.v[nextIndex], shapePath.i[nextIndex], shapePath.o[index], shapePath.v[index], true);
};

function crossProduct(a, b) {
return [
a[1] * b[2] - a[2] * b[1],
Expand Down
86 changes: 51 additions & 35 deletions player/js/utils/shapes/OffsetPathModifier.js
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,49 @@ function pruneIntersections(segments) {
return segments;
}

function offsetSegmentSplit(segment, amount) {
/*
We split each bezier segment into smaller pieces based
on inflection points, this ensures the control point
polygon is convex.
(A cubic bezier can have none, one, or two inflection points)
*/
var flex = segment.inflectionPoints();
var left;
var right;
var split;
var mid;

if (flex.length === 0) {
return [offsetSegment(segment, amount)];
}

if (flex.length === 1 || floatEqual(flex[1], 1)) {
split = segment.split(flex[0]);
left = split[0];
right = split[1];

return [
offsetSegment(left, amount),
offsetSegment(right, amount),
];
}

split = segment.split(flex[0]);
left = split[0];
var t = (flex[1] - flex[0]) / (1 - flex[0]);
split = split[1].split(t);
mid = split[0];
right = split[1];

return [
offsetSegment(left, amount),
offsetSegment(mid, amount),
offsetSegment(right, amount),
];
}

function OffsetPathModifier() {}

extendPrototype([ShapeModifier], OffsetPathModifier);
Expand All @@ -163,45 +206,18 @@ OffsetPathModifier.prototype.processPath = function (inputBezier, amount, lineJo
if (!inputBezier.c) {
count -= 1;
}
var left; var right; var mid; var split;
var i; var j; var segment;
var multiSegments = [];

for (i = 0; i < count; i += 1) {
segment = PolynomialBezier.shapeSegment(inputBezier, i);
/*
We split each bezier segment into smaller pieces based
on inflection points, this ensures the control point
polygon is convex.
(A cubic bezier can have none, one, or two inflection points)
*/
var flex = segment.inflectionPoints();

if (flex.length === 0) {
multiSegments.push([offsetSegment(segment, amount)]);
} else if (flex.length === 1 || floatEqual(flex[1], 1)) {
split = segment.split(flex[0]);
left = split[0];
right = split[1];

multiSegments.push([
offsetSegment(left, amount),
offsetSegment(right, amount),
]);
} else {
split = segment.split(flex[0]);
left = split[0];
var t = (flex[1] - flex[0]) / (1 - flex[0]);
split = split[1].split(t);
mid = split[0];
right = split[1];

multiSegments.push([
offsetSegment(left, amount),
offsetSegment(mid, amount),
offsetSegment(right, amount),
]);
multiSegments.push(offsetSegmentSplit(segment, amount));
}

if (!inputBezier.c) {
for (i = count - 1; i >= 0; i -= 1) {
segment = PolynomialBezier.shapeSegmentInverted(inputBezier, i);
multiSegments.push(offsetSegmentSplit(segment, amount));
}
}

Expand Down Expand Up @@ -249,7 +265,7 @@ OffsetPathModifier.prototype.processPath = function (inputBezier, amount, lineJo
}
}

if (inputBezier.c && multiSegments.length) joinLines(outputBezier, lastSeg, multiSegments[0][0], lineJoin, miterLimit);
if (multiSegments.length) joinLines(outputBezier, lastSeg, multiSegments[0][0], lineJoin, miterLimit);

return outputBezier;
};
Expand Down

0 comments on commit 9dee1a1

Please sign in to comment.