You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
701 lines
25 KiB
701 lines
25 KiB
import PathProxy from '../core/PathProxy';
|
|
import { cubicSubdivide } from '../core/curve';
|
|
import { defaults, assert, noop, clone } from '../core/util';
|
|
import { lerp } from '../core/vector';
|
|
import Rect from '../graphic/shape/Rect';
|
|
import Sector from '../graphic/shape/Sector';
|
|
var CMD = PathProxy.CMD;
|
|
var PI2 = Math.PI * 2;
|
|
var PROP_XY = ['x', 'y'];
|
|
var PROP_WH = ['width', 'height'];
|
|
var tmpArr = [];
|
|
function aroundEqual(a, b) {
|
|
return Math.abs(a - b) < 1e-5;
|
|
}
|
|
export function pathToBezierCurves(path) {
|
|
var data = path.data;
|
|
var len = path.len();
|
|
var bezierArray = [];
|
|
var currentSubpath;
|
|
var xi = 0;
|
|
var yi = 0;
|
|
var x0 = 0;
|
|
var y0 = 0;
|
|
function createNewSubpath(x, y) {
|
|
if (currentSubpath && currentSubpath.length > 2) {
|
|
bezierArray.push(currentSubpath);
|
|
}
|
|
currentSubpath = [x, y];
|
|
}
|
|
function addLine(x0, y0, x1, y1) {
|
|
if (!(aroundEqual(x0, x1) && aroundEqual(y0, y1))) {
|
|
currentSubpath.push(x0, y0, x1, y1, x1, y1);
|
|
}
|
|
}
|
|
function addArc(startAngle, endAngle, cx, cy, rx, ry) {
|
|
var delta = Math.abs(endAngle - startAngle);
|
|
var len = Math.tan(delta / 4) * 4 / 3;
|
|
var dir = endAngle < startAngle ? -1 : 1;
|
|
var c1 = Math.cos(startAngle);
|
|
var s1 = Math.sin(startAngle);
|
|
var c2 = Math.cos(endAngle);
|
|
var s2 = Math.sin(endAngle);
|
|
var x1 = c1 * rx + cx;
|
|
var y1 = s1 * ry + cy;
|
|
var x4 = c2 * rx + cx;
|
|
var y4 = s2 * ry + cy;
|
|
var hx = rx * len * dir;
|
|
var hy = ry * len * dir;
|
|
currentSubpath.push(x1 - hx * s1, y1 + hy * c1, x4 + hx * s2, y4 - hy * c2, x4, y4);
|
|
}
|
|
var x1;
|
|
var y1;
|
|
var x2;
|
|
var y2;
|
|
for (var i = 0; i < len;) {
|
|
var cmd = data[i++];
|
|
var isFirst = i === 1;
|
|
if (isFirst) {
|
|
xi = data[i];
|
|
yi = data[i + 1];
|
|
x0 = xi;
|
|
y0 = yi;
|
|
if (cmd === CMD.L || cmd === CMD.C || cmd === CMD.Q) {
|
|
currentSubpath = [x0, y0];
|
|
}
|
|
}
|
|
switch (cmd) {
|
|
case CMD.M:
|
|
xi = x0 = data[i++];
|
|
yi = y0 = data[i++];
|
|
createNewSubpath(x0, y0);
|
|
break;
|
|
case CMD.L:
|
|
x1 = data[i++];
|
|
y1 = data[i++];
|
|
addLine(xi, yi, x1, y1);
|
|
xi = x1;
|
|
yi = y1;
|
|
break;
|
|
case CMD.C:
|
|
currentSubpath.push(data[i++], data[i++], data[i++], data[i++], xi = data[i++], yi = data[i++]);
|
|
break;
|
|
case CMD.Q:
|
|
x1 = data[i++];
|
|
y1 = data[i++];
|
|
x2 = data[i++];
|
|
y2 = data[i++];
|
|
currentSubpath.push(xi + 2 / 3 * (x1 - xi), yi + 2 / 3 * (y1 - yi), x2 + 2 / 3 * (x1 - x2), y2 + 2 / 3 * (y1 - y2), x2, y2);
|
|
xi = x2;
|
|
yi = y2;
|
|
break;
|
|
case CMD.A:
|
|
var cx = data[i++];
|
|
var cy = data[i++];
|
|
var rx = data[i++];
|
|
var ry = data[i++];
|
|
var startAngle = data[i++];
|
|
var endAngle = data[i++] + startAngle;
|
|
i += 1;
|
|
var anticlockwise = !data[i++];
|
|
x1 = Math.cos(startAngle) * rx + cx;
|
|
y1 = Math.sin(startAngle) * ry + cy;
|
|
if (isFirst) {
|
|
x0 = x1;
|
|
y0 = y1;
|
|
createNewSubpath(x0, y0);
|
|
}
|
|
else {
|
|
addLine(xi, yi, x1, y1);
|
|
}
|
|
xi = Math.cos(endAngle) * rx + cx;
|
|
yi = Math.sin(endAngle) * ry + cy;
|
|
var step = (anticlockwise ? -1 : 1) * Math.PI / 2;
|
|
for (var angle = startAngle; anticlockwise ? angle > endAngle : angle < endAngle; angle += step) {
|
|
var nextAngle = anticlockwise ? Math.max(angle + step, endAngle)
|
|
: Math.min(angle + step, endAngle);
|
|
addArc(angle, nextAngle, cx, cy, rx, ry);
|
|
}
|
|
break;
|
|
case CMD.R:
|
|
x0 = xi = data[i++];
|
|
y0 = yi = data[i++];
|
|
x1 = x0 + data[i++];
|
|
y1 = y0 + data[i++];
|
|
createNewSubpath(x1, y0);
|
|
addLine(x1, y0, x1, y1);
|
|
addLine(x1, y1, x0, y1);
|
|
addLine(x0, y1, x0, y0);
|
|
addLine(x0, y0, x1, y0);
|
|
break;
|
|
case CMD.Z:
|
|
currentSubpath && addLine(xi, yi, x0, y0);
|
|
xi = x0;
|
|
yi = y0;
|
|
break;
|
|
}
|
|
}
|
|
if (currentSubpath && currentSubpath.length > 2) {
|
|
bezierArray.push(currentSubpath);
|
|
}
|
|
return bezierArray;
|
|
}
|
|
function alignSubpath(subpath1, subpath2) {
|
|
var len1 = subpath1.length;
|
|
var len2 = subpath2.length;
|
|
if (len1 === len2) {
|
|
return [subpath1, subpath2];
|
|
}
|
|
var shorterPath = len1 < len2 ? subpath1 : subpath2;
|
|
var shorterLen = Math.min(len1, len2);
|
|
var diff = Math.abs(len2 - len1) / 6;
|
|
var shorterBezierCount = (shorterLen - 2) / 6;
|
|
var eachCurveSubDivCount = Math.ceil(diff / shorterBezierCount) + 1;
|
|
var newSubpath = [shorterPath[0], shorterPath[1]];
|
|
var remained = diff;
|
|
var tmpSegX = [];
|
|
var tmpSegY = [];
|
|
for (var i = 2; i < shorterLen;) {
|
|
var x0 = shorterPath[i - 2];
|
|
var y0 = shorterPath[i - 1];
|
|
var x1 = shorterPath[i++];
|
|
var y1 = shorterPath[i++];
|
|
var x2 = shorterPath[i++];
|
|
var y2 = shorterPath[i++];
|
|
var x3 = shorterPath[i++];
|
|
var y3 = shorterPath[i++];
|
|
if (remained <= 0) {
|
|
newSubpath.push(x1, y1, x2, y2, x3, y3);
|
|
continue;
|
|
}
|
|
var actualSubDivCount = Math.min(remained, eachCurveSubDivCount - 1) + 1;
|
|
for (var k = 1; k <= actualSubDivCount; k++) {
|
|
var p = k / actualSubDivCount;
|
|
cubicSubdivide(x0, x1, x2, x3, p, tmpSegX);
|
|
cubicSubdivide(y0, y1, y2, y3, p, tmpSegY);
|
|
x0 = tmpSegX[3];
|
|
y0 = tmpSegY[3];
|
|
newSubpath.push(tmpSegX[1], tmpSegY[1], tmpSegX[2], tmpSegY[2], x0, y0);
|
|
x1 = tmpSegX[5];
|
|
y1 = tmpSegY[5];
|
|
x2 = tmpSegX[6];
|
|
y2 = tmpSegY[6];
|
|
}
|
|
remained -= actualSubDivCount - 1;
|
|
}
|
|
return shorterPath === subpath1 ? [newSubpath, subpath2] : [subpath1, newSubpath];
|
|
}
|
|
function createSubpath(lastSubpathSubpath, otherSubpath) {
|
|
var len = lastSubpathSubpath.length;
|
|
var lastX = lastSubpathSubpath[len - 2];
|
|
var lastY = lastSubpathSubpath[len - 1];
|
|
var newSubpath = [];
|
|
for (var i = 0; i < otherSubpath.length;) {
|
|
newSubpath[i++] = lastX;
|
|
newSubpath[i++] = lastY;
|
|
}
|
|
return newSubpath;
|
|
}
|
|
export function alignBezierCurves(array1, array2) {
|
|
var _a;
|
|
var lastSubpath1;
|
|
var lastSubpath2;
|
|
var newArray1 = [];
|
|
var newArray2 = [];
|
|
for (var i = 0; i < Math.max(array1.length, array2.length); i++) {
|
|
var subpath1 = array1[i];
|
|
var subpath2 = array2[i];
|
|
var newSubpath1 = void 0;
|
|
var newSubpath2 = void 0;
|
|
if (!subpath1) {
|
|
newSubpath1 = createSubpath(lastSubpath1 || subpath2, subpath2);
|
|
newSubpath2 = subpath2;
|
|
}
|
|
else if (!subpath2) {
|
|
newSubpath2 = createSubpath(lastSubpath2 || subpath1, subpath1);
|
|
newSubpath1 = subpath1;
|
|
}
|
|
else {
|
|
_a = alignSubpath(subpath1, subpath2), newSubpath1 = _a[0], newSubpath2 = _a[1];
|
|
lastSubpath1 = newSubpath1;
|
|
lastSubpath2 = newSubpath2;
|
|
}
|
|
newArray1.push(newSubpath1);
|
|
newArray2.push(newSubpath2);
|
|
}
|
|
return [newArray1, newArray2];
|
|
}
|
|
export function centroid(array) {
|
|
var signedArea = 0;
|
|
var cx = 0;
|
|
var cy = 0;
|
|
var len = array.length;
|
|
for (var i = 0, j = len - 2; i < len; j = i, i += 2) {
|
|
var x0 = array[j];
|
|
var y0 = array[j + 1];
|
|
var x1 = array[i];
|
|
var y1 = array[i + 1];
|
|
var a = x0 * y1 - x1 * y0;
|
|
signedArea += a;
|
|
cx += (x0 + x1) * a;
|
|
cy += (y0 + y1) * a;
|
|
}
|
|
if (signedArea === 0) {
|
|
return [array[0] || 0, array[1] || 0];
|
|
}
|
|
return [cx / signedArea / 3, cy / signedArea / 3, signedArea];
|
|
}
|
|
function findBestRingOffset(fromSubBeziers, toSubBeziers, fromCp, toCp) {
|
|
var bezierCount = (fromSubBeziers.length - 2) / 6;
|
|
var bestScore = Infinity;
|
|
var bestOffset = 0;
|
|
var len = fromSubBeziers.length;
|
|
var len2 = len - 2;
|
|
for (var offset = 0; offset < bezierCount; offset++) {
|
|
var cursorOffset = offset * 6;
|
|
var score = 0;
|
|
for (var k = 0; k < len; k += 2) {
|
|
var idx = k === 0 ? cursorOffset : ((cursorOffset + k - 2) % len2 + 2);
|
|
var x0 = fromSubBeziers[idx] - fromCp[0];
|
|
var y0 = fromSubBeziers[idx + 1] - fromCp[1];
|
|
var x1 = toSubBeziers[k] - toCp[0];
|
|
var y1 = toSubBeziers[k + 1] - toCp[1];
|
|
var dx = x1 - x0;
|
|
var dy = y1 - y0;
|
|
score += dx * dx + dy * dy;
|
|
}
|
|
if (score < bestScore) {
|
|
bestScore = score;
|
|
bestOffset = offset;
|
|
}
|
|
}
|
|
return bestOffset;
|
|
}
|
|
function reverse(array) {
|
|
var newArr = [];
|
|
var len = array.length;
|
|
for (var i = 0; i < len; i += 2) {
|
|
newArr[i] = array[len - i - 2];
|
|
newArr[i + 1] = array[len - i - 1];
|
|
}
|
|
return newArr;
|
|
}
|
|
function findBestMorphingRotation(fromArr, toArr, searchAngleIteration, searchAngleRange) {
|
|
var result = [];
|
|
var fromNeedsReverse;
|
|
for (var i = 0; i < fromArr.length; i++) {
|
|
var fromSubpathBezier = fromArr[i];
|
|
var toSubpathBezier = toArr[i];
|
|
var fromCp = centroid(fromSubpathBezier);
|
|
var toCp = centroid(toSubpathBezier);
|
|
if (fromNeedsReverse == null) {
|
|
fromNeedsReverse = fromCp[2] < 0 !== toCp[2] < 0;
|
|
}
|
|
var newFromSubpathBezier = [];
|
|
var newToSubpathBezier = [];
|
|
var bestAngle = 0;
|
|
var bestScore = Infinity;
|
|
var tmpArr_1 = [];
|
|
var len = fromSubpathBezier.length;
|
|
if (fromNeedsReverse) {
|
|
fromSubpathBezier = reverse(fromSubpathBezier);
|
|
}
|
|
var offset = findBestRingOffset(fromSubpathBezier, toSubpathBezier, fromCp, toCp) * 6;
|
|
var len2 = len - 2;
|
|
for (var k = 0; k < len2; k += 2) {
|
|
var idx = (offset + k) % len2 + 2;
|
|
newFromSubpathBezier[k + 2] = fromSubpathBezier[idx] - fromCp[0];
|
|
newFromSubpathBezier[k + 3] = fromSubpathBezier[idx + 1] - fromCp[1];
|
|
}
|
|
newFromSubpathBezier[0] = fromSubpathBezier[offset] - fromCp[0];
|
|
newFromSubpathBezier[1] = fromSubpathBezier[offset + 1] - fromCp[1];
|
|
if (searchAngleIteration > 0) {
|
|
var step = searchAngleRange / searchAngleIteration;
|
|
for (var angle = -searchAngleRange / 2; angle <= searchAngleRange / 2; angle += step) {
|
|
var sa = Math.sin(angle);
|
|
var ca = Math.cos(angle);
|
|
var score = 0;
|
|
for (var k = 0; k < fromSubpathBezier.length; k += 2) {
|
|
var x0 = newFromSubpathBezier[k];
|
|
var y0 = newFromSubpathBezier[k + 1];
|
|
var x1 = toSubpathBezier[k] - toCp[0];
|
|
var y1 = toSubpathBezier[k + 1] - toCp[1];
|
|
var newX1 = x1 * ca - y1 * sa;
|
|
var newY1 = x1 * sa + y1 * ca;
|
|
tmpArr_1[k] = newX1;
|
|
tmpArr_1[k + 1] = newY1;
|
|
var dx = newX1 - x0;
|
|
var dy = newY1 - y0;
|
|
score += dx * dx + dy * dy;
|
|
}
|
|
if (score < bestScore) {
|
|
bestScore = score;
|
|
bestAngle = angle;
|
|
for (var m = 0; m < tmpArr_1.length; m++) {
|
|
newToSubpathBezier[m] = tmpArr_1[m];
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
for (var i_1 = 0; i_1 < len; i_1 += 2) {
|
|
newToSubpathBezier[i_1] = toSubpathBezier[i_1] - toCp[0];
|
|
newToSubpathBezier[i_1 + 1] = toSubpathBezier[i_1 + 1] - toCp[1];
|
|
}
|
|
}
|
|
result.push({
|
|
from: newFromSubpathBezier,
|
|
to: newToSubpathBezier,
|
|
fromCp: fromCp,
|
|
toCp: toCp,
|
|
rotation: -bestAngle
|
|
});
|
|
}
|
|
return result;
|
|
}
|
|
export function morphPath(fromPath, toPath, animationOpts) {
|
|
var fromPathProxy;
|
|
var toPathProxy;
|
|
if (!fromPath || !toPath) {
|
|
return toPath;
|
|
}
|
|
!fromPath.path && fromPath.createPathProxy();
|
|
fromPathProxy = fromPath.path;
|
|
fromPathProxy.beginPath();
|
|
fromPath.buildPath(fromPathProxy, fromPath.shape);
|
|
!toPath.path && toPath.createPathProxy();
|
|
toPathProxy = toPath.path;
|
|
toPathProxy === fromPathProxy && (toPathProxy = new PathProxy(false));
|
|
toPathProxy.beginPath();
|
|
if (isIndividualMorphingPath(toPath)) {
|
|
toPath.__oldBuildPath(toPathProxy, toPath.shape);
|
|
}
|
|
else {
|
|
toPath.buildPath(toPathProxy, toPath.shape);
|
|
}
|
|
var _a = alignBezierCurves(pathToBezierCurves(fromPathProxy), pathToBezierCurves(toPathProxy)), fromBezierCurves = _a[0], toBezierCurves = _a[1];
|
|
var morphingData = findBestMorphingRotation(fromBezierCurves, toBezierCurves, 10, Math.PI);
|
|
becomeIndividualMorphingPath(toPath, morphingData, 0);
|
|
var oldDone = animationOpts && animationOpts.done;
|
|
var oldAborted = animationOpts && animationOpts.aborted;
|
|
var oldDuring = animationOpts && animationOpts.during;
|
|
toPath.animateTo({
|
|
__morphT: 1
|
|
}, defaults({
|
|
during: function (p) {
|
|
toPath.dirtyShape();
|
|
oldDuring && oldDuring(p);
|
|
},
|
|
done: function () {
|
|
restoreIndividualMorphingPath(toPath);
|
|
toPath.createPathProxy();
|
|
toPath.dirtyShape();
|
|
oldDone && oldDone();
|
|
},
|
|
aborted: function () {
|
|
oldAborted && oldAborted();
|
|
}
|
|
}, animationOpts));
|
|
return toPath;
|
|
}
|
|
function morphingPathBuildPath(path) {
|
|
var morphingData = this.__morphingData;
|
|
var t = this.__morphT;
|
|
var onet = 1 - t;
|
|
var newCp = [];
|
|
for (var i = 0; i < morphingData.length; i++) {
|
|
var item = morphingData[i];
|
|
var from = item.from;
|
|
var to = item.to;
|
|
var angle = item.rotation * t;
|
|
var fromCp = item.fromCp;
|
|
var toCp = item.toCp;
|
|
var sa = Math.sin(angle);
|
|
var ca = Math.cos(angle);
|
|
lerp(newCp, fromCp, toCp, t);
|
|
for (var m = 0; m < from.length; m += 2) {
|
|
var x0 = from[m];
|
|
var y0 = from[m + 1];
|
|
var x1 = to[m];
|
|
var y1 = to[m + 1];
|
|
var x = x0 * onet + x1 * t;
|
|
var y = y0 * onet + y1 * t;
|
|
tmpArr[m] = (x * ca - y * sa) + newCp[0];
|
|
tmpArr[m + 1] = (x * sa + y * ca) + newCp[1];
|
|
}
|
|
for (var m = 0; m < from.length;) {
|
|
if (m === 0) {
|
|
path.moveTo(tmpArr[m++], tmpArr[m++]);
|
|
}
|
|
path.bezierCurveTo(tmpArr[m++], tmpArr[m++], tmpArr[m++], tmpArr[m++], tmpArr[m++], tmpArr[m++]);
|
|
}
|
|
}
|
|
}
|
|
;
|
|
function becomeIndividualMorphingPath(path, morphingData, morphT) {
|
|
if (isIndividualMorphingPath(path)) {
|
|
updateIndividualMorphingPath(path, morphingData, morphT);
|
|
return;
|
|
}
|
|
var morphingPath = path;
|
|
morphingPath.__oldBuildPath = morphingPath.buildPath;
|
|
morphingPath.buildPath = morphingPathBuildPath;
|
|
updateIndividualMorphingPath(morphingPath, morphingData, morphT);
|
|
}
|
|
function updateIndividualMorphingPath(morphingPath, morphingData, morphT) {
|
|
morphingPath.__morphingData = morphingData;
|
|
morphingPath.__morphT = morphT;
|
|
}
|
|
function restoreIndividualMorphingPath(path) {
|
|
if (isIndividualMorphingPath(path)) {
|
|
path.buildPath = path.__oldBuildPath;
|
|
path.__oldBuildPath = path.__morphingData = null;
|
|
}
|
|
}
|
|
function isIndividualMorphingPath(path) {
|
|
return path.__oldBuildPath != null;
|
|
}
|
|
export function isCombiningPath(path) {
|
|
return !!path.__combiningSubList;
|
|
}
|
|
export function isInAnyMorphing(path) {
|
|
return isIndividualMorphingPath(path) || isCombiningPath(path);
|
|
}
|
|
export function combine(fromPathList, toPath, animationOpts, copyPropsIfDivided) {
|
|
var fromIndividuals = [];
|
|
var separateCount = 0;
|
|
for (var i = 0; i < fromPathList.length; i++) {
|
|
var fromPath = fromPathList[i];
|
|
if (isCombiningPath(fromPath)) {
|
|
var fromCombiningSubList = fromPath.__combiningSubList;
|
|
for (var j = 0; j < fromCombiningSubList.length; j++) {
|
|
fromIndividuals.push(fromCombiningSubList[j]);
|
|
}
|
|
separateCount += fromCombiningSubList.length;
|
|
}
|
|
else {
|
|
fromIndividuals.push(fromPath);
|
|
separateCount++;
|
|
}
|
|
}
|
|
if (!separateCount) {
|
|
return;
|
|
}
|
|
var dividingMethod = animationOpts ? animationOpts.dividingMethod : null;
|
|
var toPathSplittedList = divideShape(toPath, separateCount, dividingMethod);
|
|
assert(toPathSplittedList.length === separateCount);
|
|
var oldDone = animationOpts && animationOpts.done;
|
|
var oldAborted = animationOpts && animationOpts.aborted;
|
|
var oldDuring = animationOpts && animationOpts.during;
|
|
var doneCount = 0;
|
|
var abortedCalled = false;
|
|
var morphAnimationOpts = defaults({
|
|
during: function (p) {
|
|
oldDuring && oldDuring(p);
|
|
},
|
|
done: function () {
|
|
doneCount++;
|
|
if (doneCount === toPathSplittedList.length) {
|
|
restoreCombiningPath(toPath);
|
|
oldDone && oldDone();
|
|
}
|
|
},
|
|
aborted: function () {
|
|
if (!abortedCalled) {
|
|
abortedCalled = true;
|
|
oldAborted && oldAborted();
|
|
}
|
|
}
|
|
}, animationOpts);
|
|
for (var i = 0; i < separateCount; i++) {
|
|
var from = fromIndividuals[i];
|
|
var to = toPathSplittedList[i];
|
|
copyPropsIfDivided && copyPropsIfDivided(toPath, to, true);
|
|
morphPath(from, to, morphAnimationOpts);
|
|
}
|
|
becomeCombiningPath(toPath, toPathSplittedList);
|
|
return {
|
|
fromIndividuals: fromIndividuals,
|
|
toIndividuals: toPathSplittedList,
|
|
count: separateCount
|
|
};
|
|
}
|
|
function becomeCombiningPath(path, combiningSubList) {
|
|
if (isCombiningPath(path)) {
|
|
updateCombiningPathSubList(path, combiningSubList);
|
|
return;
|
|
}
|
|
var combiningPath = path;
|
|
updateCombiningPathSubList(combiningPath, combiningSubList);
|
|
combiningPath.__oldAddSelfToZr = path.addSelfToZr;
|
|
combiningPath.__oldRemoveSelfFromZr = path.removeSelfFromZr;
|
|
combiningPath.addSelfToZr = combiningAddSelfToZr;
|
|
combiningPath.removeSelfFromZr = combiningRemoveSelfFromZr;
|
|
combiningPath.__oldBuildPath = combiningPath.buildPath;
|
|
combiningPath.buildPath = noop;
|
|
combiningPath.childrenRef = combiningChildrenRef;
|
|
}
|
|
function restoreCombiningPath(path) {
|
|
if (!isCombiningPath(path)) {
|
|
return;
|
|
}
|
|
var combiningPath = path;
|
|
updateCombiningPathSubList(combiningPath, null);
|
|
combiningPath.addSelfToZr = combiningPath.__oldAddSelfToZr;
|
|
combiningPath.removeSelfFromZr = combiningPath.__oldRemoveSelfFromZr;
|
|
combiningPath.buildPath = combiningPath.__oldBuildPath;
|
|
combiningPath.childrenRef =
|
|
combiningPath.__combiningSubList =
|
|
combiningPath.__oldAddSelfToZr =
|
|
combiningPath.__oldRemoveSelfFromZr =
|
|
combiningPath.__oldBuildPath = null;
|
|
}
|
|
function updateCombiningPathSubList(combiningPath, combiningSubList) {
|
|
if (combiningPath.__combiningSubList !== combiningSubList) {
|
|
combiningPathSubListAddRemoveWithZr(combiningPath, 'removeSelfFromZr');
|
|
combiningPath.__combiningSubList = combiningSubList;
|
|
if (combiningSubList) {
|
|
for (var i = 0; i < combiningSubList.length; i++) {
|
|
combiningSubList[i].parent = combiningPath;
|
|
}
|
|
}
|
|
combiningPathSubListAddRemoveWithZr(combiningPath, 'addSelfToZr');
|
|
}
|
|
}
|
|
function combiningAddSelfToZr(zr) {
|
|
this.__oldAddSelfToZr(zr);
|
|
combiningPathSubListAddRemoveWithZr(this, 'addSelfToZr');
|
|
}
|
|
function combiningPathSubListAddRemoveWithZr(path, method) {
|
|
var combiningSubList = path.__combiningSubList;
|
|
var zr = path.__zr;
|
|
if (combiningSubList && zr) {
|
|
for (var i = 0; i < combiningSubList.length; i++) {
|
|
var child = combiningSubList[i];
|
|
child[method](zr);
|
|
}
|
|
}
|
|
}
|
|
function combiningRemoveSelfFromZr(zr) {
|
|
this.__oldRemoveSelfFromZr(zr);
|
|
var combiningSubList = this.__combiningSubList;
|
|
for (var i = 0; i < combiningSubList.length; i++) {
|
|
var child = combiningSubList[i];
|
|
child.removeSelfFromZr(zr);
|
|
}
|
|
}
|
|
function combiningChildrenRef() {
|
|
return this.__combiningSubList;
|
|
}
|
|
export function separate(fromPath, toPathList, animationOpts, copyPropsIfDivided) {
|
|
var toPathListLen = toPathList.length;
|
|
var fromPathList;
|
|
var dividingMethod = animationOpts ? animationOpts.dividingMethod : null;
|
|
var copyProps = false;
|
|
if (isCombiningPath(fromPath)) {
|
|
var fromCombiningSubList = fromPath.__combiningSubList;
|
|
if (fromCombiningSubList.length === toPathListLen) {
|
|
fromPathList = fromCombiningSubList;
|
|
}
|
|
else {
|
|
fromPathList = divideShape(fromPath, toPathListLen, dividingMethod);
|
|
copyProps = true;
|
|
}
|
|
}
|
|
else {
|
|
fromPathList = divideShape(fromPath, toPathListLen, dividingMethod);
|
|
copyProps = true;
|
|
}
|
|
assert(fromPathList.length === toPathListLen);
|
|
for (var i = 0; i < toPathListLen; i++) {
|
|
if (copyProps && copyPropsIfDivided) {
|
|
copyPropsIfDivided(fromPath, fromPathList[i], false);
|
|
}
|
|
morphPath(fromPathList[i], toPathList[i], animationOpts);
|
|
}
|
|
return {
|
|
fromIndividuals: fromPathList,
|
|
toIndividuals: toPathList,
|
|
count: toPathListLen
|
|
};
|
|
}
|
|
function divideShape(path, separateCount, dividingMethod) {
|
|
return dividingMethod === 'duplicate'
|
|
? duplicateShape(path, separateCount)
|
|
: splitShape(path, separateCount);
|
|
}
|
|
function splitShape(path, separateCount) {
|
|
var resultPaths = [];
|
|
if (separateCount <= 0) {
|
|
return resultPaths;
|
|
}
|
|
if (separateCount === 1) {
|
|
return duplicateShape(path, separateCount);
|
|
}
|
|
if (path instanceof Rect) {
|
|
var toPathShape = path.shape;
|
|
var splitPropIdx = toPathShape.height > toPathShape.width ? 1 : 0;
|
|
var propWH = PROP_WH[splitPropIdx];
|
|
var propXY = PROP_XY[splitPropIdx];
|
|
var subWH = toPathShape[propWH] / separateCount;
|
|
var xyCurr = toPathShape[propXY];
|
|
for (var i = 0; i < separateCount; i++, xyCurr += subWH) {
|
|
var subShape = {
|
|
x: toPathShape.x,
|
|
y: toPathShape.y,
|
|
width: toPathShape.width,
|
|
height: toPathShape.height
|
|
};
|
|
subShape[propXY] = xyCurr;
|
|
subShape[propWH] = i < separateCount - 1
|
|
? subWH
|
|
: toPathShape[propXY] + toPathShape[propWH] - xyCurr;
|
|
var splitted = new Rect({ shape: subShape });
|
|
resultPaths.push(splitted);
|
|
}
|
|
}
|
|
else if (path instanceof Sector) {
|
|
var toPathShape = path.shape;
|
|
var clockwise = toPathShape.clockwise;
|
|
var startAngle = toPathShape.startAngle;
|
|
var endAngle = toPathShape.endAngle;
|
|
var endAngleNormalized = normalizeRadian(startAngle, toPathShape.endAngle, clockwise);
|
|
var step = (endAngleNormalized - startAngle) / separateCount;
|
|
var angleCurr = startAngle;
|
|
for (var i = 0; i < separateCount; i++, angleCurr += step) {
|
|
var splitted = new Sector({
|
|
shape: {
|
|
cx: toPathShape.cx,
|
|
cy: toPathShape.cy,
|
|
r: toPathShape.r,
|
|
r0: toPathShape.r0,
|
|
clockwise: clockwise,
|
|
startAngle: angleCurr,
|
|
endAngle: i === separateCount - 1 ? endAngle : angleCurr + step
|
|
}
|
|
});
|
|
resultPaths.push(splitted);
|
|
}
|
|
}
|
|
else {
|
|
return duplicateShape(path, separateCount);
|
|
}
|
|
return resultPaths;
|
|
}
|
|
function duplicateShape(path, separateCount) {
|
|
var resultPaths = [];
|
|
if (separateCount <= 0) {
|
|
return resultPaths;
|
|
}
|
|
var ctor = path.constructor;
|
|
for (var i = 0; i < separateCount; i++) {
|
|
var sub = new ctor({
|
|
shape: clone(path.shape)
|
|
});
|
|
resultPaths.push(sub);
|
|
}
|
|
return resultPaths;
|
|
}
|
|
function normalizeRadian(start, end, clockwise) {
|
|
return end + PI2 * (Math[clockwise ? 'ceil' : 'floor']((start - end) / PI2));
|
|
}
|
|
|