1 | ;
|
2 | Object.defineProperty(exports, "__esModule", { value: true });
|
3 | exports.arcToCubic = void 0;
|
4 | var rotate_vector_1 = require("../util/rotate-vector");
|
5 | /**
|
6 | * Converts A (arc-to) segments to C (cubic-bezier-to).
|
7 | *
|
8 | * For more information of where this math came from visit:
|
9 | * http://www.w3.org/TR/SVG11/implnote.html#ArcImplementationNotes
|
10 | */
|
11 | function arcToCubic(X1, Y1, RX, RY, angle, LAF, SF, X2, Y2, recursive) {
|
12 | var x1 = X1;
|
13 | var y1 = Y1;
|
14 | var rx = RX;
|
15 | var ry = RY;
|
16 | var x2 = X2;
|
17 | var y2 = Y2;
|
18 | // for more information of where this Math came from visit:
|
19 | // http://www.w3.org/TR/SVG11/implnote.html#ArcImplementationNotes
|
20 | var d120 = (Math.PI * 120) / 180;
|
21 | var rad = (Math.PI / 180) * (+angle || 0);
|
22 | /** @type {number[]} */
|
23 | var res = [];
|
24 | var xy;
|
25 | var f1;
|
26 | var f2;
|
27 | var cx;
|
28 | var cy;
|
29 | if (!recursive) {
|
30 | xy = (0, rotate_vector_1.rotateVector)(x1, y1, -rad);
|
31 | x1 = xy.x;
|
32 | y1 = xy.y;
|
33 | xy = (0, rotate_vector_1.rotateVector)(x2, y2, -rad);
|
34 | x2 = xy.x;
|
35 | y2 = xy.y;
|
36 | var x = (x1 - x2) / 2;
|
37 | var y = (y1 - y2) / 2;
|
38 | var h = (x * x) / (rx * rx) + (y * y) / (ry * ry);
|
39 | if (h > 1) {
|
40 | h = Math.sqrt(h);
|
41 | rx *= h;
|
42 | ry *= h;
|
43 | }
|
44 | var rx2 = rx * rx;
|
45 | var ry2 = ry * ry;
|
46 | var k = (LAF === SF ? -1 : 1) *
|
47 | Math.sqrt(Math.abs((rx2 * ry2 - rx2 * y * y - ry2 * x * x) / (rx2 * y * y + ry2 * x * x)));
|
48 | cx = (k * rx * y) / ry + (x1 + x2) / 2;
|
49 | cy = (k * -ry * x) / rx + (y1 + y2) / 2;
|
50 | // eslint-disable-next-line no-bitwise -- Impossible to satisfy no-bitwise
|
51 | f1 = Math.asin(((((y1 - cy) / ry) * Math.pow(10, 9)) >> 0) / Math.pow(10, 9));
|
52 | // eslint-disable-next-line no-bitwise -- Impossible to satisfy no-bitwise
|
53 | f2 = Math.asin(((((y2 - cy) / ry) * Math.pow(10, 9)) >> 0) / Math.pow(10, 9));
|
54 | f1 = x1 < cx ? Math.PI - f1 : f1;
|
55 | f2 = x2 < cx ? Math.PI - f2 : f2;
|
56 | if (f1 < 0)
|
57 | f1 = Math.PI * 2 + f1;
|
58 | if (f2 < 0)
|
59 | f2 = Math.PI * 2 + f2;
|
60 | if (SF && f1 > f2) {
|
61 | f1 -= Math.PI * 2;
|
62 | }
|
63 | if (!SF && f2 > f1) {
|
64 | f2 -= Math.PI * 2;
|
65 | }
|
66 | }
|
67 | else {
|
68 | f1 = recursive[0], f2 = recursive[1], cx = recursive[2], cy = recursive[3];
|
69 | }
|
70 | var df = f2 - f1;
|
71 | if (Math.abs(df) > d120) {
|
72 | var f2old = f2;
|
73 | var x2old = x2;
|
74 | var y2old = y2;
|
75 | f2 = f1 + d120 * (SF && f2 > f1 ? 1 : -1);
|
76 | x2 = cx + rx * Math.cos(f2);
|
77 | y2 = cy + ry * Math.sin(f2);
|
78 | res = arcToCubic(x2, y2, rx, ry, angle, 0, SF, x2old, y2old, [f2, f2old, cx, cy]);
|
79 | }
|
80 | df = f2 - f1;
|
81 | var c1 = Math.cos(f1);
|
82 | var s1 = Math.sin(f1);
|
83 | var c2 = Math.cos(f2);
|
84 | var s2 = Math.sin(f2);
|
85 | var t = Math.tan(df / 4);
|
86 | var hx = (4 / 3) * rx * t;
|
87 | var hy = (4 / 3) * ry * t;
|
88 | var m1 = [x1, y1];
|
89 | var m2 = [x1 + hx * s1, y1 - hy * c1];
|
90 | var m3 = [x2 + hx * s2, y2 - hy * c2];
|
91 | var m4 = [x2, y2];
|
92 | m2[0] = 2 * m1[0] - m2[0];
|
93 | m2[1] = 2 * m1[1] - m2[1];
|
94 | if (recursive) {
|
95 | return m2.concat(m3, m4, res);
|
96 | // return [...m2, ...m3, ...m4, ...res];
|
97 | }
|
98 | res = m2.concat(m3, m4, res);
|
99 | // res = [...m2, ...m3, ...m4, ...res];
|
100 | var newres = [];
|
101 | for (var i = 0, ii = res.length; i < ii; i += 1) {
|
102 | 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;
|
103 | }
|
104 | return newres;
|
105 | }
|
106 | exports.arcToCubic = arcToCubic;
|
107 | // const TAU = Math.PI * 2;
|
108 | // const mapToEllipse = (
|
109 | // { x, y }: { x: number; y: number },
|
110 | // rx: number,
|
111 | // ry: number,
|
112 | // cosphi: number,
|
113 | // sinphi: number,
|
114 | // centerx: number,
|
115 | // centery: number,
|
116 | // ) => {
|
117 | // x *= rx;
|
118 | // y *= ry;
|
119 | // const xp = cosphi * x - sinphi * y;
|
120 | // const yp = sinphi * x + cosphi * y;
|
121 | // return {
|
122 | // x: xp + centerx,
|
123 | // y: yp + centery,
|
124 | // };
|
125 | // };
|
126 | // const approxUnitArc = (ang1: number, ang2: number) => {
|
127 | // // If 90 degree circular arc, use a constant
|
128 | // // as derived from http://spencermortensen.com/articles/bezier-circle
|
129 | // const a =
|
130 | // ang2 === 1.5707963267948966
|
131 | // ? 0.551915024494
|
132 | // : ang2 === -1.5707963267948966
|
133 | // ? -0.551915024494
|
134 | // : (4 / 3) * Math.tan(ang2 / 4);
|
135 | // const x1 = Math.cos(ang1);
|
136 | // const y1 = Math.sin(ang1);
|
137 | // const x2 = Math.cos(ang1 + ang2);
|
138 | // const y2 = Math.sin(ang1 + ang2);
|
139 | // return [
|
140 | // {
|
141 | // x: x1 - y1 * a,
|
142 | // y: y1 + x1 * a,
|
143 | // },
|
144 | // {
|
145 | // x: x2 + y2 * a,
|
146 | // y: y2 - x2 * a,
|
147 | // },
|
148 | // {
|
149 | // x: x2,
|
150 | // y: y2,
|
151 | // },
|
152 | // ];
|
153 | // };
|
154 | // const vectorAngle = (ux: number, uy: number, vx: number, vy: number) => {
|
155 | // const sign = ux * vy - uy * vx < 0 ? -1 : 1;
|
156 | // let dot = ux * vx + uy * vy;
|
157 | // if (dot > 1) {
|
158 | // dot = 1;
|
159 | // }
|
160 | // if (dot < -1) {
|
161 | // dot = -1;
|
162 | // }
|
163 | // return sign * Math.acos(dot);
|
164 | // };
|
165 | // const getArcCenter = (
|
166 | // px: any,
|
167 | // py: any,
|
168 | // cx: any,
|
169 | // cy: any,
|
170 | // rx: number,
|
171 | // ry: number,
|
172 | // largeArcFlag: number,
|
173 | // sweepFlag: number,
|
174 | // sinphi: number,
|
175 | // cosphi: number,
|
176 | // pxp: number,
|
177 | // pyp: number,
|
178 | // ) => {
|
179 | // const rxsq = Math.pow(rx, 2);
|
180 | // const rysq = Math.pow(ry, 2);
|
181 | // const pxpsq = Math.pow(pxp, 2);
|
182 | // const pypsq = Math.pow(pyp, 2);
|
183 | // let radicant = rxsq * rysq - rxsq * pypsq - rysq * pxpsq;
|
184 | // if (radicant < 0) {
|
185 | // radicant = 0;
|
186 | // }
|
187 | // radicant /= rxsq * pypsq + rysq * pxpsq;
|
188 | // radicant = Math.sqrt(radicant) * (largeArcFlag === sweepFlag ? -1 : 1);
|
189 | // const centerxp = ((radicant * rx) / ry) * pyp;
|
190 | // const centeryp = ((radicant * -ry) / rx) * pxp;
|
191 | // const centerx = cosphi * centerxp - sinphi * centeryp + (px + cx) / 2;
|
192 | // const centery = sinphi * centerxp + cosphi * centeryp + (py + cy) / 2;
|
193 | // const vx1 = (pxp - centerxp) / rx;
|
194 | // const vy1 = (pyp - centeryp) / ry;
|
195 | // const vx2 = (-pxp - centerxp) / rx;
|
196 | // const vy2 = (-pyp - centeryp) / ry;
|
197 | // const ang1 = vectorAngle(1, 0, vx1, vy1);
|
198 | // let ang2 = vectorAngle(vx1, vy1, vx2, vy2);
|
199 | // if (sweepFlag === 0 && ang2 > 0) {
|
200 | // ang2 -= TAU;
|
201 | // }
|
202 | // if (sweepFlag === 1 && ang2 < 0) {
|
203 | // ang2 += TAU;
|
204 | // }
|
205 | // return [centerx, centery, ang1, ang2];
|
206 | // };
|
207 | // const arcToBezier = ({ px, py, cx, cy, rx, ry, xAxisRotation = 0, largeArcFlag = 0, sweepFlag = 0 }) => {
|
208 | // const curves = [];
|
209 | // if (rx === 0 || ry === 0) {
|
210 | // return [{ x1: 0, y1: 0, x2: 0, y2: 0, x: cx, y: cy }];
|
211 | // }
|
212 | // const sinphi = Math.sin((xAxisRotation * TAU) / 360);
|
213 | // const cosphi = Math.cos((xAxisRotation * TAU) / 360);
|
214 | // const pxp = (cosphi * (px - cx)) / 2 + (sinphi * (py - cy)) / 2;
|
215 | // const pyp = (-sinphi * (px - cx)) / 2 + (cosphi * (py - cy)) / 2;
|
216 | // if (pxp === 0 && pyp === 0) {
|
217 | // return [{ x1: 0, y1: 0, x2: 0, y2: 0, x: cx, y: cy }];
|
218 | // }
|
219 | // rx = Math.abs(rx);
|
220 | // ry = Math.abs(ry);
|
221 | // const lambda = Math.pow(pxp, 2) / Math.pow(rx, 2) + Math.pow(pyp, 2) / Math.pow(ry, 2);
|
222 | // if (lambda > 1) {
|
223 | // rx *= Math.sqrt(lambda);
|
224 | // ry *= Math.sqrt(lambda);
|
225 | // }
|
226 | // let [centerx, centery, ang1, ang2] = getArcCenter(
|
227 | // px,
|
228 | // py,
|
229 | // cx,
|
230 | // cy,
|
231 | // rx,
|
232 | // ry,
|
233 | // largeArcFlag,
|
234 | // sweepFlag,
|
235 | // sinphi,
|
236 | // cosphi,
|
237 | // pxp,
|
238 | // pyp,
|
239 | // );
|
240 | // // If 'ang2' == 90.0000000001, then `ratio` will evaluate to
|
241 | // // 1.0000000001. This causes `segments` to be greater than one, which is an
|
242 | // // unecessary split, and adds extra points to the bezier curve. To alleviate
|
243 | // // this issue, we round to 1.0 when the ratio is close to 1.0.
|
244 | // let ratio = Math.abs(ang2) / (TAU / 4);
|
245 | // if (Math.abs(1.0 - ratio) < 0.0000001) {
|
246 | // ratio = 1.0;
|
247 | // }
|
248 | // const segments = Math.max(Math.ceil(ratio), 1);
|
249 | // ang2 /= segments;
|
250 | // for (let i = 0; i < segments; i++) {
|
251 | // curves.push(approxUnitArc(ang1, ang2));
|
252 | // ang1 += ang2;
|
253 | // }
|
254 | // return curves.map((curve) => {
|
255 | // const { x: x1, y: y1 } = mapToEllipse(curve[0], rx, ry, cosphi, sinphi, centerx, centery);
|
256 | // const { x: x2, y: y2 } = mapToEllipse(curve[1], rx, ry, cosphi, sinphi, centerx, centery);
|
257 | // const { x, y } = mapToEllipse(curve[2], rx, ry, cosphi, sinphi, centerx, centery);
|
258 | // return { x1, y1, x2, y2, x, y };
|
259 | // });
|
260 | // };
|
261 | // export function arcToCubic(
|
262 | // x1: number,
|
263 | // y1: number,
|
264 | // rx: number,
|
265 | // ry: number,
|
266 | // angle: number,
|
267 | // LAF: number,
|
268 | // SF: number,
|
269 | // x2: number,
|
270 | // y2: number,
|
271 | // ) {
|
272 | // const curves = arcToBezier({
|
273 | // px: x1,
|
274 | // py: y1,
|
275 | // cx: x2,
|
276 | // cy: y2,
|
277 | // rx,
|
278 | // ry,
|
279 | // xAxisRotation: angle,
|
280 | // largeArcFlag: LAF,
|
281 | // sweepFlag: SF,
|
282 | // });
|
283 | // return curves.reduce((prev, cur) => {
|
284 | // const { x1, y1, x2, y2, x, y } = cur;
|
285 | // prev.push(x1, y1, x2, y2, x, y);
|
286 | // return prev;
|
287 | // }, [] as number[]);
|
288 | // }
|
289 | //# sourceMappingURL=arc-2-cubic.js.map |
\ | No newline at end of file |