UNPKG

@antv/util

Version:

> AntV 底层依赖的工具库,不建议在自己业务中使用。

288 lines 9.01 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.arcToCubic = arcToCubic; var rotate_vector_1 = require("../util/rotate-vector"); /** * Converts A (arc-to) segments to C (cubic-bezier-to). * * For more information of where this math came from visit: * http://www.w3.org/TR/SVG11/implnote.html#ArcImplementationNotes */ function arcToCubic(X1, Y1, RX, RY, angle, LAF, SF, X2, Y2, recursive) { var x1 = X1; var y1 = Y1; var rx = RX; var ry = RY; var x2 = X2; var y2 = Y2; // for more information of where this Math came from visit: // http://www.w3.org/TR/SVG11/implnote.html#ArcImplementationNotes var d120 = (Math.PI * 120) / 180; var rad = (Math.PI / 180) * (+angle || 0); /** @type {number[]} */ var res = []; var xy; var f1; var f2; var cx; var cy; if (!recursive) { xy = (0, rotate_vector_1.rotateVector)(x1, y1, -rad); x1 = xy.x; y1 = xy.y; xy = (0, rotate_vector_1.rotateVector)(x2, y2, -rad); x2 = xy.x; y2 = xy.y; var x = (x1 - x2) / 2; var y = (y1 - y2) / 2; var h = (x * x) / (rx * rx) + (y * y) / (ry * ry); if (h > 1) { h = Math.sqrt(h); rx *= h; ry *= h; } var rx2 = rx * rx; var ry2 = ry * ry; var k = (LAF === SF ? -1 : 1) * Math.sqrt(Math.abs((rx2 * ry2 - rx2 * y * y - ry2 * x * x) / (rx2 * y * y + ry2 * x * x))); cx = (k * rx * y) / ry + (x1 + x2) / 2; cy = (k * -ry * x) / rx + (y1 + y2) / 2; // eslint-disable-next-line no-bitwise -- Impossible to satisfy no-bitwise f1 = Math.asin(((((y1 - cy) / ry) * Math.pow(10, 9)) >> 0) / Math.pow(10, 9)); // eslint-disable-next-line no-bitwise -- Impossible to satisfy no-bitwise f2 = Math.asin(((((y2 - cy) / ry) * Math.pow(10, 9)) >> 0) / Math.pow(10, 9)); f1 = x1 < cx ? Math.PI - f1 : f1; f2 = x2 < cx ? Math.PI - f2 : f2; if (f1 < 0) f1 = Math.PI * 2 + f1; if (f2 < 0) f2 = Math.PI * 2 + f2; if (SF && f1 > f2) { f1 -= Math.PI * 2; } if (!SF && f2 > f1) { f2 -= Math.PI * 2; } } else { f1 = recursive[0], f2 = recursive[1], cx = recursive[2], cy = recursive[3]; } var df = f2 - f1; if (Math.abs(df) > d120) { var f2old = f2; var x2old = x2; var y2old = y2; f2 = f1 + d120 * (SF && f2 > f1 ? 1 : -1); x2 = cx + rx * Math.cos(f2); y2 = cy + ry * Math.sin(f2); res = arcToCubic(x2, y2, rx, ry, angle, 0, SF, x2old, y2old, [f2, f2old, cx, cy]); } df = f2 - f1; var c1 = Math.cos(f1); var s1 = Math.sin(f1); var c2 = Math.cos(f2); var s2 = Math.sin(f2); var t = Math.tan(df / 4); var hx = (4 / 3) * rx * t; var hy = (4 / 3) * ry * t; var m1 = [x1, y1]; var m2 = [x1 + hx * s1, y1 - hy * c1]; var m3 = [x2 + hx * s2, y2 - hy * c2]; var m4 = [x2, y2]; m2[0] = 2 * m1[0] - m2[0]; m2[1] = 2 * m1[1] - m2[1]; if (recursive) { return m2.concat(m3, m4, res); // return [...m2, ...m3, ...m4, ...res]; } res = m2.concat(m3, m4, res); // res = [...m2, ...m3, ...m4, ...res]; var newres = []; for (var i = 0, ii = res.length; i < ii; i += 1) { newres[i] = i % 2 ? (0, rotate_vector_1.rotateVector)(res[i - 1], res[i], rad).y : (0, rotate_vector_1.rotateVector)(res[i], res[i + 1], rad).x; } return newres; } // const TAU = Math.PI * 2; // const mapToEllipse = ( // { x, y }: { x: number; y: number }, // rx: number, // ry: number, // cosphi: number, // sinphi: number, // centerx: number, // centery: number, // ) => { // x *= rx; // y *= ry; // const xp = cosphi * x - sinphi * y; // const yp = sinphi * x + cosphi * y; // return { // x: xp + centerx, // y: yp + centery, // }; // }; // const approxUnitArc = (ang1: number, ang2: number) => { // // If 90 degree circular arc, use a constant // // as derived from http://spencermortensen.com/articles/bezier-circle // const a = // ang2 === 1.5707963267948966 // ? 0.551915024494 // : ang2 === -1.5707963267948966 // ? -0.551915024494 // : (4 / 3) * Math.tan(ang2 / 4); // const x1 = Math.cos(ang1); // const y1 = Math.sin(ang1); // const x2 = Math.cos(ang1 + ang2); // const y2 = Math.sin(ang1 + ang2); // return [ // { // x: x1 - y1 * a, // y: y1 + x1 * a, // }, // { // x: x2 + y2 * a, // y: y2 - x2 * a, // }, // { // x: x2, // y: y2, // }, // ]; // }; // const vectorAngle = (ux: number, uy: number, vx: number, vy: number) => { // const sign = ux * vy - uy * vx < 0 ? -1 : 1; // let dot = ux * vx + uy * vy; // if (dot > 1) { // dot = 1; // } // if (dot < -1) { // dot = -1; // } // return sign * Math.acos(dot); // }; // const getArcCenter = ( // px: any, // py: any, // cx: any, // cy: any, // rx: number, // ry: number, // largeArcFlag: number, // sweepFlag: number, // sinphi: number, // cosphi: number, // pxp: number, // pyp: number, // ) => { // const rxsq = Math.pow(rx, 2); // const rysq = Math.pow(ry, 2); // const pxpsq = Math.pow(pxp, 2); // const pypsq = Math.pow(pyp, 2); // let radicant = rxsq * rysq - rxsq * pypsq - rysq * pxpsq; // if (radicant < 0) { // radicant = 0; // } // radicant /= rxsq * pypsq + rysq * pxpsq; // radicant = Math.sqrt(radicant) * (largeArcFlag === sweepFlag ? -1 : 1); // const centerxp = ((radicant * rx) / ry) * pyp; // const centeryp = ((radicant * -ry) / rx) * pxp; // const centerx = cosphi * centerxp - sinphi * centeryp + (px + cx) / 2; // const centery = sinphi * centerxp + cosphi * centeryp + (py + cy) / 2; // const vx1 = (pxp - centerxp) / rx; // const vy1 = (pyp - centeryp) / ry; // const vx2 = (-pxp - centerxp) / rx; // const vy2 = (-pyp - centeryp) / ry; // const ang1 = vectorAngle(1, 0, vx1, vy1); // let ang2 = vectorAngle(vx1, vy1, vx2, vy2); // if (sweepFlag === 0 && ang2 > 0) { // ang2 -= TAU; // } // if (sweepFlag === 1 && ang2 < 0) { // ang2 += TAU; // } // return [centerx, centery, ang1, ang2]; // }; // const arcToBezier = ({ px, py, cx, cy, rx, ry, xAxisRotation = 0, largeArcFlag = 0, sweepFlag = 0 }) => { // const curves = []; // if (rx === 0 || ry === 0) { // return [{ x1: 0, y1: 0, x2: 0, y2: 0, x: cx, y: cy }]; // } // const sinphi = Math.sin((xAxisRotation * TAU) / 360); // const cosphi = Math.cos((xAxisRotation * TAU) / 360); // const pxp = (cosphi * (px - cx)) / 2 + (sinphi * (py - cy)) / 2; // const pyp = (-sinphi * (px - cx)) / 2 + (cosphi * (py - cy)) / 2; // if (pxp === 0 && pyp === 0) { // return [{ x1: 0, y1: 0, x2: 0, y2: 0, x: cx, y: cy }]; // } // rx = Math.abs(rx); // ry = Math.abs(ry); // const lambda = Math.pow(pxp, 2) / Math.pow(rx, 2) + Math.pow(pyp, 2) / Math.pow(ry, 2); // if (lambda > 1) { // rx *= Math.sqrt(lambda); // ry *= Math.sqrt(lambda); // } // let [centerx, centery, ang1, ang2] = getArcCenter( // px, // py, // cx, // cy, // rx, // ry, // largeArcFlag, // sweepFlag, // sinphi, // cosphi, // pxp, // pyp, // ); // // If 'ang2' == 90.0000000001, then `ratio` will evaluate to // // 1.0000000001. This causes `segments` to be greater than one, which is an // // unecessary split, and adds extra points to the bezier curve. To alleviate // // this issue, we round to 1.0 when the ratio is close to 1.0. // let ratio = Math.abs(ang2) / (TAU / 4); // if (Math.abs(1.0 - ratio) < 0.0000001) { // ratio = 1.0; // } // const segments = Math.max(Math.ceil(ratio), 1); // ang2 /= segments; // for (let i = 0; i < segments; i++) { // curves.push(approxUnitArc(ang1, ang2)); // ang1 += ang2; // } // return curves.map((curve) => { // const { x: x1, y: y1 } = mapToEllipse(curve[0], rx, ry, cosphi, sinphi, centerx, centery); // const { x: x2, y: y2 } = mapToEllipse(curve[1], rx, ry, cosphi, sinphi, centerx, centery); // const { x, y } = mapToEllipse(curve[2], rx, ry, cosphi, sinphi, centerx, centery); // return { x1, y1, x2, y2, x, y }; // }); // }; // export function arcToCubic( // x1: number, // y1: number, // rx: number, // ry: number, // angle: number, // LAF: number, // SF: number, // x2: number, // y2: number, // ) { // const curves = arcToBezier({ // px: x1, // py: y1, // cx: x2, // cy: y2, // rx, // ry, // xAxisRotation: angle, // largeArcFlag: LAF, // sweepFlag: SF, // }); // return curves.reduce((prev, cur) => { // const { x1, y1, x2, y2, x, y } = cur; // prev.push(x1, y1, x2, y2, x, y); // return prev; // }, [] as number[]); // } //# sourceMappingURL=arc-2-cubic.js.map