123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306 |
- 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;
|