@antv/path-util
Version:
A common util collection for antv projects
136 lines (129 loc) • 3.6 kB
text/typescript
import parsePathString from './parse-path-string';
const REGEX_MD = /[a-z]/;
function toSymmetry(p, c) { // 点对称
return [
c[0] + (c[0] - p[0]),
c[1] + (c[1] - p[1]),
];
}
export default function pathToAbsolute(pathString: string) {
const pathArray = parsePathString(pathString);
if (!pathArray || !pathArray.length) {
return [
[ 'M', 0, 0 ],
];
}
let needProcess = false; // 如果存在小写的命令或者 V,H,T,S 则需要处理
for (let i = 0; i < pathArray.length; i++) {
const cmd = pathArray[i][0];
// 如果存在相对位置的命令,则中断返回
if (REGEX_MD.test(cmd) || [ 'V', 'H', 'T', 'S' ].indexOf(cmd) >= 0) {
needProcess = true;
break;
}
}
// 如果不存在相对命令,则直接返回
// 如果在业务上都写绝对路径,这种方式最快,仅做了一次检测
if (!needProcess) {
return pathArray;
}
const res = [];
let x = 0;
let y = 0;
let mx = 0;
let my = 0;
let start = 0;
let pa0;
let dots;
const first = pathArray[0];
if (first[0] === 'M' || first[0] === 'm') {
x = +first[1];
y = +first[2];
mx = x;
my = y;
start++;
res[0] = [ 'M', x, y ];
}
for (let i = start, ii = pathArray.length; i < ii; i++) {
const pa = pathArray[i];
const preParams = res[i - 1]; // 取前一个已经处理后的节点,否则会出现问题
let r = [];
const cmd = pa[0];
const upCmd = cmd.toUpperCase();
if (cmd !== upCmd) {
r[0] = upCmd;
switch (upCmd) {
case 'A':
r[1] = pa[1];
r[2] = pa[2];
r[3] = pa[3];
r[4] = pa[4];
r[5] = pa[5];
r[6] = +pa[6] + x;
r[7] = +pa[7] + y;
break;
case 'V':
r[1] = +pa[1] + y;
break;
case 'H':
r[1] = +pa[1] + x;
break;
case 'M':
mx = +pa[1] + x;
my = +pa[2] + y;
r[1] = mx;
r[2] = my;
break; // for lint
default:
for (let j = 1, jj = pa.length; j < jj; j++) {
r[j] = +pa[j] + ((j % 2) ? x : y);
}
}
} else { // 如果本来已经大写,则不处理
r = pathArray[i];
}
// 需要在外面统一做,同时处理 V,H,S,T 等特殊指令
switch (upCmd) {
case 'Z':
x = +mx;
y = +my;
break;
case 'H':
x = r[1];
r = [ 'L', x, y ];
break;
case 'V':
y = r[1];
r = [ 'L', x, y ];
break;
case 'T':
x = r[1];
y = r[2];
// 以 x, y 为中心的,上一个控制点的对称点
// 需要假设上一个节点的命令为 Q
const symetricT = toSymmetry([ preParams[1], preParams[2] ], [ preParams[3], preParams[4] ]);
r = [ 'Q', symetricT[0], symetricT[1], x, y ];
break;
case 'S':
x = r[r.length - 2];
y = r[r.length - 1];
// 以 x,y 为中心,取上一个控制点,
// 需要假设上一个线段为 C 或者 S
const length = preParams.length;
const symetricS = toSymmetry(
[ preParams[length - 4], preParams[length - 3] ],
[ preParams[length - 2], preParams[length - 1] ]);
r = [ 'C', symetricS[0], symetricS[1], r[1], r[2], x, y ];
break;
case 'M':
mx = r[r.length - 2];
my = r[r.length - 1];
break; // for lint
default:
x = r[r.length - 2];
y = r[r.length - 1];
}
res.push(r);
}
return res;
}