143 lines
5.9 KiB
TypeScript
143 lines
5.9 KiB
TypeScript
import getArcParams from './get-arc-params';
|
|||
|
|
import { isSamePoint } from './get-arc-params';
|
||
|
|
import parsePath from './parse-path';
|
||
|
|
|
||
|
|
// 点对称
|
||
|
|
function toSymmetry(point, center) {
|
||
|
|
return [ center[0] + (center[0] - point[0]), center[1] + (center[1] - point[1]) ];
|
||
|
|
}
|
||
|
|
|
||
|
|
export default function getSegments(path) {
|
||
|
|
path = parsePath(path);
|
||
|
|
const segments = [];
|
||
|
|
let currentPoint = null; // 当前图形
|
||
|
|
let nextParams = null; // 下一节点的 path 参数
|
||
|
|
let startMovePoint = null; // 开始 M 的点,可能会有多个
|
||
|
|
let lastStartMovePointIndex = 0; // 最近一个开始点 M 的索引
|
||
|
|
const count = path.length;
|
||
|
|
for (let i = 0; i < count; i++) {
|
||
|
|
const params = path[i];
|
||
|
|
nextParams = path[i + 1];
|
||
|
|
const command = params[0];
|
||
|
|
// 数学定义上的参数,便于后面的计算
|
||
|
|
const segment = {
|
||
|
|
command,
|
||
|
|
prePoint: currentPoint,
|
||
|
|
params,
|
||
|
|
startTangent: null,
|
||
|
|
endTangent: null,
|
||
|
|
};
|
||
|
|
switch (command) {
|
||
|
|
case 'M':
|
||
|
|
startMovePoint = [ params[1], params[2] ];
|
||
|
|
lastStartMovePointIndex = i;
|
||
|
|
break;
|
||
|
|
case 'A':
|
||
|
|
const arcParams = getArcParams(currentPoint, params);
|
||
|
|
segment['arcParams'] = arcParams;
|
||
|
|
break;
|
||
|
|
default:
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
if (command === 'Z') {
|
||
|
|
// 有了 Z 后,当前节点从开始 M 的点开始
|
||
|
|
currentPoint = startMovePoint;
|
||
|
|
// 如果当前点的命令为 Z,相当于当前点为最近一个 M 点,则下一个点直接指向最近一个 M 点的下一个点
|
||
|
|
nextParams = path[lastStartMovePointIndex + 1];
|
||
|
|
} else {
|
||
|
|
const len = params.length;
|
||
|
|
currentPoint = [ params[len - 2], params[len - 1] ];
|
||
|
|
}
|
||
|
|
if (nextParams && nextParams[0] === 'Z') {
|
||
|
|
// 如果下一个点的命令为 Z,则下一个点直接指向最近一个 M 点
|
||
|
|
nextParams = path[lastStartMovePointIndex];
|
||
|
|
if (segments[lastStartMovePointIndex]) {
|
||
|
|
// 如果下一个点的命令为 Z,则最近一个 M 点的前一个点为当前点
|
||
|
|
segments[lastStartMovePointIndex].prePoint = currentPoint;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
segment['currentPoint'] = currentPoint;
|
||
|
|
// 如果当前点与最近一个 M 点相同,则最近一个 M 点的前一个点为当前点的前一个点
|
||
|
|
if (
|
||
|
|
segments[lastStartMovePointIndex] &&
|
||
|
|
isSamePoint(currentPoint, segments[lastStartMovePointIndex].currentPoint)
|
||
|
|
) {
|
||
|
|
segments[lastStartMovePointIndex].prePoint = segment.prePoint;
|
||
|
|
}
|
||
|
|
const nextPoint = nextParams ? [ nextParams[nextParams.length - 2], nextParams[nextParams.length - 1] ] : null;
|
||
|
|
segment['nextPoint'] = nextPoint;
|
||
|
|
// Add startTangent and endTangent
|
||
|
|
const { prePoint } = segment;
|
||
|
|
if ([ 'L', 'H', 'V' ].includes(command)) {
|
||
|
|
segment.startTangent = [ prePoint[0] - currentPoint[0], prePoint[1] - currentPoint[1] ];
|
||
|
|
segment.endTangent = [ currentPoint[0] - prePoint[0], currentPoint[1] - prePoint[1] ];
|
||
|
|
} else if (command === 'Q') {
|
||
|
|
// 二次贝塞尔曲线只有一个控制点
|
||
|
|
const cp = [ params[1], params[2] ];
|
||
|
|
// 二次贝塞尔曲线的终点为 currentPoint
|
||
|
|
segment.startTangent = [ prePoint[0] - cp[0], prePoint[1] - cp[1] ];
|
||
|
|
segment.endTangent = [ currentPoint[0] - cp[0], currentPoint[1] - cp[1] ];
|
||
|
|
} else if (command === 'T') {
|
||
|
|
const preSegment = segments[i - 1];
|
||
|
|
const cp = toSymmetry(preSegment.currentPoint, prePoint);
|
||
|
|
if (preSegment.command === 'Q') {
|
||
|
|
segment.command = 'Q';
|
||
|
|
segment.startTangent = [ prePoint[0] - cp[0], prePoint[1] - cp[1] ];
|
||
|
|
segment.endTangent = [ currentPoint[0] - cp[0], currentPoint[1] - cp[1] ];
|
||
|
|
} else {
|
||
|
|
segment.command = 'TL';
|
||
|
|
segment.startTangent = [ prePoint[0] - currentPoint[0], prePoint[1] - currentPoint[1] ];
|
||
|
|
segment.endTangent = [ currentPoint[0] - prePoint[0], currentPoint[1] - prePoint[1] ];
|
||
|
|
}
|
||
|
|
} else if (command === 'C') {
|
||
|
|
// 三次贝塞尔曲线有两个控制点
|
||
|
|
const cp1 = [ params[1], params[2] ];
|
||
|
|
const cp2 = [ params[3], params[4] ];
|
||
|
|
segment.startTangent = [ prePoint[0] - cp1[0], prePoint[1] - cp1[1] ];
|
||
|
|
segment.endTangent = [ currentPoint[0] - cp2[0], currentPoint[1] - cp2[1] ];
|
||
|
|
|
||
|
|
// horizontal line, eg. ['C', 100, 100, 100, 100, 200, 200]
|
||
|
|
if (segment.startTangent[0] === 0 && segment.startTangent[1] === 0) {
|
||
|
|
segment.startTangent = [cp1[0] - cp2[0], cp1[1] - cp2[1]];
|
||
|
|
}
|
||
|
|
if (segment.endTangent[0] === 0 && segment.endTangent[1] === 0) {
|
||
|
|
segment.endTangent = [cp2[0] - cp1[0], cp2[1] - cp1[1]];
|
||
|
|
}
|
||
|
|
} else if (command === 'S') {
|
||
|
|
const preSegment = segments[i - 1];
|
||
|
|
const cp1 = toSymmetry(preSegment.currentPoint, prePoint);
|
||
|
|
const cp2 = [ params[1], params[2] ];
|
||
|
|
if (preSegment.command === 'C') {
|
||
|
|
segment.command = 'C'; // 将 S 命令变换为 C 命令
|
||
|
|
segment.startTangent = [ prePoint[0] - cp1[0], prePoint[1] - cp1[1] ];
|
||
|
|
segment.endTangent = [ currentPoint[0] - cp2[0], currentPoint[1] - cp2[1] ];
|
||
|
|
} else {
|
||
|
|
segment.command = 'SQ'; // 将 S 命令变换为 SQ 命令
|
||
|
|
segment.startTangent = [ prePoint[0] - cp2[0], prePoint[1] - cp2[1] ];
|
||
|
|
segment.endTangent = [ currentPoint[0] - cp2[0], currentPoint[1] - cp2[1] ];
|
||
|
|
}
|
||
|
|
} else if (command === 'A') {
|
||
|
|
let d = 0.001;
|
||
|
|
const {
|
||
|
|
cx = 0,
|
||
|
|
cy = 0,
|
||
|
|
rx = 0,
|
||
|
|
ry = 0,
|
||
|
|
sweepFlag = 0,
|
||
|
|
startAngle = 0,
|
||
|
|
endAngle = 0,
|
||
|
|
} = segment['arcParams'] || {};
|
||
|
|
if (sweepFlag === 0) {
|
||
|
|
d *= -1;
|
||
|
|
}
|
||
|
|
const dx1 = rx * Math.cos(startAngle - d) + cx;
|
||
|
|
const dy1 = ry * Math.sin(startAngle - d) + cy;
|
||
|
|
segment.startTangent = [ dx1 - startMovePoint[0], dy1 - startMovePoint[1] ];
|
||
|
|
const dx2 = rx * Math.cos(startAngle + endAngle + d) + cx;
|
||
|
|
const dy2 = ry * Math.sin(startAngle + endAngle - d) + cy;
|
||
|
|
segment.endTangent = [ prePoint[0] - dx2, prePoint[1] - dy2 ];
|
||
|
|
}
|
||
|
|
segments.push(segment);
|
||
|
|
}
|
||
|
|
return segments;
|
||
|
|
}
|