jysBlack2
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

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));
}