first commit
This commit is contained in:
306
node_modules/lottie-web/player/js/utils/shapes/OffsetPathModifier.js
generated
vendored
Normal file
306
node_modules/lottie-web/player/js/utils/shapes/OffsetPathModifier.js
generated
vendored
Normal file
@@ -0,0 +1,306 @@
|
||||
import {
|
||||
roundCorner,
|
||||
} from '../common';
|
||||
import {
|
||||
extendPrototype,
|
||||
} from '../functionExtensions';
|
||||
import PropertyFactory from '../PropertyFactory';
|
||||
import shapePool from '../pooling/shape_pool';
|
||||
import {
|
||||
ShapeModifier,
|
||||
} from './ShapeModifiers';
|
||||
import {
|
||||
PolynomialBezier,
|
||||
polarOffset,
|
||||
lineIntersection,
|
||||
pointDistance,
|
||||
pointEqual,
|
||||
floatEqual,
|
||||
} from '../PolynomialBezier';
|
||||
|
||||
function linearOffset(p1, p2, amount) {
|
||||
var angle = Math.atan2(p2[0] - p1[0], p2[1] - p1[1]);
|
||||
return [
|
||||
polarOffset(p1, angle, amount),
|
||||
polarOffset(p2, angle, amount),
|
||||
];
|
||||
}
|
||||
|
||||
function offsetSegment(segment, amount) {
|
||||
var p0; var p1a; var p1b; var p2b; var p2a; var
|
||||
p3;
|
||||
var e;
|
||||
e = linearOffset(segment.points[0], segment.points[1], amount);
|
||||
p0 = e[0];
|
||||
p1a = e[1];
|
||||
e = linearOffset(segment.points[1], segment.points[2], amount);
|
||||
p1b = e[0];
|
||||
p2b = e[1];
|
||||
e = linearOffset(segment.points[2], segment.points[3], amount);
|
||||
p2a = e[0];
|
||||
p3 = e[1];
|
||||
var p1 = lineIntersection(p0, p1a, p1b, p2b);
|
||||
if (p1 === null) p1 = p1a;
|
||||
var p2 = lineIntersection(p2a, p3, p1b, p2b);
|
||||
if (p2 === null) p2 = p2a;
|
||||
|
||||
return new PolynomialBezier(p0, p1, p2, p3);
|
||||
}
|
||||
|
||||
function joinLines(outputBezier, seg1, seg2, lineJoin, miterLimit) {
|
||||
var p0 = seg1.points[3];
|
||||
var p1 = seg2.points[0];
|
||||
|
||||
// Bevel
|
||||
if (lineJoin === 3) return p0;
|
||||
|
||||
// Connected, they don't need a joint
|
||||
if (pointEqual(p0, p1)) return p0;
|
||||
|
||||
// Round
|
||||
if (lineJoin === 2) {
|
||||
var angleOut = -seg1.tangentAngle(1);
|
||||
var angleIn = -seg2.tangentAngle(0) + Math.PI;
|
||||
var center = lineIntersection(
|
||||
p0,
|
||||
polarOffset(p0, angleOut + Math.PI / 2, 100),
|
||||
p1,
|
||||
polarOffset(p1, angleOut + Math.PI / 2, 100)
|
||||
);
|
||||
var radius = center ? pointDistance(center, p0) : pointDistance(p0, p1) / 2;
|
||||
|
||||
var tan = polarOffset(p0, angleOut, 2 * radius * roundCorner);
|
||||
outputBezier.setXYAt(tan[0], tan[1], 'o', outputBezier.length() - 1);
|
||||
|
||||
tan = polarOffset(p1, angleIn, 2 * radius * roundCorner);
|
||||
outputBezier.setTripleAt(p1[0], p1[1], p1[0], p1[1], tan[0], tan[1], outputBezier.length());
|
||||
|
||||
return p1;
|
||||
}
|
||||
|
||||
// Miter
|
||||
var t0 = pointEqual(p0, seg1.points[2]) ? seg1.points[0] : seg1.points[2];
|
||||
var t1 = pointEqual(p1, seg2.points[1]) ? seg2.points[3] : seg2.points[1];
|
||||
var intersection = lineIntersection(t0, p0, p1, t1);
|
||||
if (intersection && pointDistance(intersection, p0) < miterLimit) {
|
||||
outputBezier.setTripleAt(
|
||||
intersection[0],
|
||||
intersection[1],
|
||||
intersection[0],
|
||||
intersection[1],
|
||||
intersection[0],
|
||||
intersection[1],
|
||||
outputBezier.length()
|
||||
);
|
||||
return intersection;
|
||||
}
|
||||
|
||||
return p0;
|
||||
}
|
||||
|
||||
function getIntersection(a, b) {
|
||||
const intersect = a.intersections(b);
|
||||
|
||||
if (intersect.length && floatEqual(intersect[0][0], 1)) intersect.shift();
|
||||
|
||||
if (intersect.length) return intersect[0];
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
function pruneSegmentIntersection(a, b) {
|
||||
var outa = a.slice();
|
||||
var outb = b.slice();
|
||||
var intersect = getIntersection(a[a.length - 1], b[0]);
|
||||
if (intersect) {
|
||||
outa[a.length - 1] = a[a.length - 1].split(intersect[0])[0];
|
||||
outb[0] = b[0].split(intersect[1])[1];
|
||||
}
|
||||
if (a.length > 1 && b.length > 1) {
|
||||
intersect = getIntersection(a[0], b[b.length - 1]);
|
||||
if (intersect) {
|
||||
return [
|
||||
[a[0].split(intersect[0])[0]],
|
||||
[b[b.length - 1].split(intersect[1])[1]],
|
||||
];
|
||||
}
|
||||
}
|
||||
return [outa, outb];
|
||||
}
|
||||
|
||||
function pruneIntersections(segments) {
|
||||
var e;
|
||||
for (var i = 1; i < segments.length; i += 1) {
|
||||
e = pruneSegmentIntersection(segments[i - 1], segments[i]);
|
||||
segments[i - 1] = e[0];
|
||||
segments[i] = e[1];
|
||||
}
|
||||
|
||||
if (segments.length > 1) {
|
||||
e = pruneSegmentIntersection(segments[segments.length - 1], segments[0]);
|
||||
segments[segments.length - 1] = e[0];
|
||||
segments[0] = e[1];
|
||||
}
|
||||
|
||||
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);
|
||||
OffsetPathModifier.prototype.initModifierProperties = function (elem, data) {
|
||||
this.getValue = this.processKeys;
|
||||
this.amount = PropertyFactory.getProp(elem, data.a, 0, null, this);
|
||||
this.miterLimit = PropertyFactory.getProp(elem, data.ml, 0, null, this);
|
||||
this.lineJoin = data.lj;
|
||||
this._isAnimated = this.amount.effectsSequence.length !== 0;
|
||||
};
|
||||
|
||||
OffsetPathModifier.prototype.processPath = function (inputBezier, amount, lineJoin, miterLimit) {
|
||||
var outputBezier = shapePool.newElement();
|
||||
outputBezier.c = inputBezier.c;
|
||||
var count = inputBezier.length();
|
||||
if (!inputBezier.c) {
|
||||
count -= 1;
|
||||
}
|
||||
var i; var j; var segment;
|
||||
var multiSegments = [];
|
||||
|
||||
for (i = 0; i < count; i += 1) {
|
||||
segment = PolynomialBezier.shapeSegment(inputBezier, i);
|
||||
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));
|
||||
}
|
||||
}
|
||||
|
||||
multiSegments = pruneIntersections(multiSegments);
|
||||
|
||||
// Add bezier segments to the output and apply line joints
|
||||
var lastPoint = null;
|
||||
var lastSeg = null;
|
||||
|
||||
for (i = 0; i < multiSegments.length; i += 1) {
|
||||
var multiSegment = multiSegments[i];
|
||||
|
||||
if (lastSeg) lastPoint = joinLines(outputBezier, lastSeg, multiSegment[0], lineJoin, miterLimit);
|
||||
|
||||
lastSeg = multiSegment[multiSegment.length - 1];
|
||||
|
||||
for (j = 0; j < multiSegment.length; j += 1) {
|
||||
segment = multiSegment[j];
|
||||
|
||||
if (lastPoint && pointEqual(segment.points[0], lastPoint)) {
|
||||
outputBezier.setXYAt(segment.points[1][0], segment.points[1][1], 'o', outputBezier.length() - 1);
|
||||
} else {
|
||||
outputBezier.setTripleAt(
|
||||
segment.points[0][0],
|
||||
segment.points[0][1],
|
||||
segment.points[1][0],
|
||||
segment.points[1][1],
|
||||
segment.points[0][0],
|
||||
segment.points[0][1],
|
||||
outputBezier.length()
|
||||
);
|
||||
}
|
||||
|
||||
outputBezier.setTripleAt(
|
||||
segment.points[3][0],
|
||||
segment.points[3][1],
|
||||
segment.points[3][0],
|
||||
segment.points[3][1],
|
||||
segment.points[2][0],
|
||||
segment.points[2][1],
|
||||
outputBezier.length()
|
||||
);
|
||||
|
||||
lastPoint = segment.points[3];
|
||||
}
|
||||
}
|
||||
|
||||
if (multiSegments.length) joinLines(outputBezier, lastSeg, multiSegments[0][0], lineJoin, miterLimit);
|
||||
|
||||
return outputBezier;
|
||||
};
|
||||
|
||||
OffsetPathModifier.prototype.processShapes = function (_isFirstFrame) {
|
||||
var shapePaths;
|
||||
var i;
|
||||
var len = this.shapes.length;
|
||||
var j;
|
||||
var jLen;
|
||||
var amount = this.amount.v;
|
||||
var miterLimit = this.miterLimit.v;
|
||||
var lineJoin = this.lineJoin;
|
||||
|
||||
if (amount !== 0) {
|
||||
var shapeData;
|
||||
var localShapeCollection;
|
||||
for (i = 0; i < len; i += 1) {
|
||||
shapeData = this.shapes[i];
|
||||
localShapeCollection = shapeData.localShapeCollection;
|
||||
if (!(!shapeData.shape._mdf && !this._mdf && !_isFirstFrame)) {
|
||||
localShapeCollection.releaseShapes();
|
||||
shapeData.shape._mdf = true;
|
||||
shapePaths = shapeData.shape.paths.shapes;
|
||||
jLen = shapeData.shape.paths._length;
|
||||
for (j = 0; j < jLen; j += 1) {
|
||||
localShapeCollection.addShape(this.processPath(shapePaths[j], amount, lineJoin, miterLimit));
|
||||
}
|
||||
}
|
||||
shapeData.shape.paths = shapeData.localShapeCollection;
|
||||
}
|
||||
}
|
||||
if (!this.dynamicProperties.length) {
|
||||
this._mdf = false;
|
||||
}
|
||||
};
|
||||
|
||||
export default OffsetPathModifier;
|
Reference in New Issue
Block a user