modern-path2d 1.4.16 → 1.5.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.mjs CHANGED
@@ -43,9 +43,10 @@ function setCanvasContext(ctx, style) {
43
43
  }
44
44
 
45
45
  class Vector2 {
46
- constructor(x = 0, y = 0) {
47
- this.x = x;
48
- this.y = y;
46
+ constructor(_x = 0, _y = 0, _onUpdate) {
47
+ this._x = _x;
48
+ this._y = _y;
49
+ this._onUpdate = _onUpdate;
49
50
  }
50
51
  static get MAX() {
51
52
  return new Vector2(Infinity, Infinity);
@@ -53,141 +54,173 @@ class Vector2 {
53
54
  static get MIN() {
54
55
  return new Vector2(-Infinity, -Infinity);
55
56
  }
56
- get array() {
57
- return [this.x, this.y];
57
+ static lerp(a, b, t) {
58
+ return new Vector2(b.x, b.y).clone().sub(a).multiply(t).add(a);
58
59
  }
59
- finite() {
60
- this.x = Number.isFinite(this.x) ? this.x : 0;
61
- this.y = Number.isFinite(this.y) ? this.y : 0;
62
- return this;
60
+ get width() {
61
+ return this.x;
63
62
  }
64
- set(x, y) {
65
- this.x = x;
66
- this.y = y;
67
- return this;
63
+ set width(val) {
64
+ this.x = val;
68
65
  }
69
- add(vec) {
70
- this.x += vec.x;
71
- this.y += vec.y;
72
- return this;
66
+ get height() {
67
+ return this.y;
73
68
  }
74
- sub(vec) {
75
- this.x -= vec.x;
76
- this.y -= vec.y;
77
- return this;
69
+ set height(val) {
70
+ this.y = val;
78
71
  }
79
- multiply(vec) {
80
- this.x *= vec.x;
81
- this.y *= vec.y;
82
- return this;
72
+ get left() {
73
+ return this.x;
83
74
  }
84
- divide(vec) {
85
- this.x /= vec.x;
86
- this.y /= vec.y;
87
- return this;
75
+ set left(val) {
76
+ this.x = val;
88
77
  }
89
- dot(vec) {
90
- return this.x * vec.x + this.y * vec.y;
78
+ get top() {
79
+ return this.y;
91
80
  }
92
- cross(vec) {
93
- return this.x * vec.y - this.y * vec.x;
81
+ set top(val) {
82
+ this.y = val;
94
83
  }
95
- rotate(a, target = { x: 0, y: 0 }) {
96
- const rotation = -a / 180 * Math.PI;
97
- const x = this.x - target.x;
98
- const y = -(this.y - target.y);
99
- const sin = Math.sin(rotation);
100
- const cos = Math.cos(rotation);
101
- this.set(
102
- target.x + (x * cos - y * sin),
103
- target.y - (x * sin + y * cos)
104
- );
84
+ get x() {
85
+ return this._x;
86
+ }
87
+ set x(value) {
88
+ if (this._x !== value) {
89
+ this._x = value;
90
+ this._onUpdate?.(this);
91
+ }
92
+ }
93
+ get y() {
94
+ return this._y;
95
+ }
96
+ set y(value) {
97
+ if (this._y !== value) {
98
+ this._y = value;
99
+ this._onUpdate?.(this);
100
+ }
101
+ }
102
+ set(x = 0, y = x) {
103
+ if (this._x !== x || this._y !== y) {
104
+ this._x = x;
105
+ this._y = y;
106
+ this._onUpdate?.(this);
107
+ }
105
108
  return this;
106
109
  }
107
- distanceTo(vec) {
108
- return Math.sqrt(this.distanceToSquared(vec));
110
+ add(p) {
111
+ return this.set(this._x + p.x, this._y + p.y);
109
112
  }
110
- distanceToSquared(vec) {
111
- const dx = this.x - vec.x;
112
- const dy = this.y - vec.y;
113
- return dx * dx + dy * dy;
113
+ sub(p) {
114
+ return this.set(this._x - p.x, this._y - p.y);
114
115
  }
115
- lengthSquared() {
116
- return this.x * this.x + this.y * this.y;
116
+ subVectors(a, b) {
117
+ return this.set(a.x - b.x, a.y - b.y);
117
118
  }
118
- length() {
119
- return Math.sqrt(this.lengthSquared());
119
+ multiply(x = 0, y = x) {
120
+ return this.set(this._x * x, this._y * y);
120
121
  }
121
- scale(sx, sy = sx, target = { x: 0, y: 0 }) {
122
- const x = sx < 0 ? target.x - this.x + target.x : this.x;
123
- const y = sy < 0 ? target.y - this.y + target.y : this.y;
124
- this.x = x * Math.abs(sx);
125
- this.y = y * Math.abs(sy);
126
- return this;
122
+ divide(x = 0, y = x) {
123
+ return this.set(this._x / x, this._y / y);
127
124
  }
128
- skew(ax, ay = 0, target = { x: 0, y: 0 }) {
129
- const dx = this.x - target.x;
130
- const dy = this.y - target.y;
131
- this.x = target.x + (dx + Math.tan(ax) * dy);
132
- this.y = target.y + (dy + Math.tan(ay) * dx);
133
- return this;
125
+ cross(p) {
126
+ return this._x * p.y - this._y * p.x;
134
127
  }
135
- min(...vecs) {
136
- this.x = Math.min(this.x, ...vecs.map((v) => v.x));
137
- this.y = Math.min(this.y, ...vecs.map((v) => v.y));
138
- return this;
128
+ dot(p) {
129
+ return this._x * p.x + this._y * p.y;
139
130
  }
140
- max(...vecs) {
141
- this.x = Math.max(this.x, ...vecs.map((v) => v.x));
142
- this.y = Math.max(this.y, ...vecs.map((v) => v.y));
143
- return this;
131
+ rotate(rad, origin = { x: 0, y: 0 }) {
132
+ const { x, y } = this;
133
+ const cos = Math.cos(rad);
134
+ const sin = Math.sin(rad);
135
+ return this.set(
136
+ (x - origin.x) * cos - (y - origin.y) * sin + origin.x,
137
+ (x - origin.x) * sin + (y - origin.y) * cos + origin.y
138
+ );
144
139
  }
145
- normalize() {
146
- return this.scale(1 / (this.length() || 1));
140
+ getLength() {
141
+ const { x, y } = this;
142
+ return Math.sqrt(x * x + y * y);
147
143
  }
148
- addVectors(a, b) {
149
- this.x = a.x + b.x;
150
- this.y = a.y + b.y;
151
- return this;
144
+ getAngle() {
145
+ return Math.atan2(-this.x, -this.y) + Math.PI;
152
146
  }
153
- subVectors(a, b) {
154
- this.x = a.x - b.x;
155
- this.y = a.y - b.y;
156
- return this;
147
+ distanceTo(p) {
148
+ return Math.hypot(p.x - this.x, p.y - this.y);
157
149
  }
158
- multiplyVectors(a, b) {
159
- this.x = a.x * b.x;
160
- this.y = a.y * b.y;
150
+ normalize() {
151
+ const scalar = 1 / (this.getLength() || 1);
152
+ this.set(this.x * scalar, this.y * scalar);
161
153
  return this;
162
154
  }
163
- divideVectors(a, b) {
164
- this.x = a.x / b.x;
165
- this.y = a.y / b.y;
155
+ copyFrom(p) {
156
+ if (this._x !== p.x || this._y !== p.y) {
157
+ this._x = p.x;
158
+ this._y = p.y;
159
+ this._onUpdate?.(this);
160
+ }
166
161
  return this;
167
162
  }
168
- lerpVectors(v1, v2, alpha) {
169
- this.x = v1.x + (v2.x - v1.x) * alpha;
170
- this.y = v1.y + (v2.y - v1.y) * alpha;
171
- return this;
163
+ copyTo(p) {
164
+ p.set(this._x, this._y);
165
+ return p;
172
166
  }
173
167
  equals(vec) {
174
- return this.x === vec.x && this.y === vec.y;
175
- }
176
- applyMatrix3(m) {
177
- const x = this.x;
178
- const y = this.y;
179
- const e = m.elements;
180
- this.x = e[0] * x + e[3] * y + e[6];
181
- this.y = e[1] * x + e[4] * y + e[7];
182
- return this;
168
+ return this._x === vec.x && this._y === vec.y;
183
169
  }
184
- copy(vec) {
185
- this.x = vec.x;
186
- this.y = vec.y;
187
- return this;
170
+ get array() {
171
+ return [this.x, this.y];
188
172
  }
189
- clone() {
190
- return new Vector2(this.x, this.y);
173
+ finite() {
174
+ return this.set(
175
+ Number.isFinite(this._x) ? this._x : 0,
176
+ Number.isFinite(this._y) ? this._y : 0
177
+ );
178
+ }
179
+ lengthSquared() {
180
+ return this._x * this._x + this._y * this._y;
181
+ }
182
+ length() {
183
+ return Math.sqrt(this.lengthSquared());
184
+ }
185
+ scale(sx, sy = sx, origin = { x: 0, y: 0 }) {
186
+ const x = sx < 0 ? origin.x - this._x + origin.x : this._x;
187
+ const y = sy < 0 ? origin.y - this._y + origin.y : this._y;
188
+ return this.set(
189
+ x * Math.abs(sx),
190
+ y * Math.abs(sy)
191
+ );
192
+ }
193
+ skew(ax, ay = 0, origin = { x: 0, y: 0 }) {
194
+ const dx = this._x - origin.x;
195
+ const dy = this._y - origin.y;
196
+ return this.set(
197
+ origin.x + (dx + Math.tan(ax) * dy),
198
+ origin.y + (dy + Math.tan(ay) * dx)
199
+ );
200
+ }
201
+ clampMin(...pList) {
202
+ return this.set(
203
+ Math.min(this._x, ...pList.map((v) => v.x)),
204
+ Math.min(this._y, ...pList.map((v) => v.y))
205
+ );
206
+ }
207
+ clampMax(...pList) {
208
+ return this.set(
209
+ Math.max(this.x, ...pList.map((v) => v.x)),
210
+ Math.max(this.y, ...pList.map((v) => v.y))
211
+ );
212
+ }
213
+ clone(_onUpdate) {
214
+ return new Vector2(this._x, this._y, _onUpdate ?? this._onUpdate);
215
+ }
216
+ toJSON() {
217
+ return {
218
+ x: this._x,
219
+ y: this._y
220
+ };
221
+ }
222
+ destroy() {
223
+ this._onUpdate = void 0;
191
224
  }
192
225
  }
193
226
 
@@ -263,2017 +296,2198 @@ class BoundingBox {
263
296
  }
264
297
  }
265
298
 
266
- class Matrix3 {
267
- elements = [];
268
- constructor(n11 = 1, n12 = 0, n13 = 0, n21 = 0, n22 = 1, n23 = 0, n31 = 0, n32 = 0, n33 = 1) {
269
- this.set(n11, n12, n13, n21, n22, n23, n31, n32, n33);
270
- }
271
- set(n11, n12, n13, n21, n22, n23, n31, n32, n33) {
272
- const te = this.elements;
273
- te[0] = n11;
274
- te[1] = n21;
275
- te[2] = n31;
276
- te[3] = n12;
277
- te[4] = n22;
278
- te[5] = n32;
279
- te[6] = n13;
280
- te[7] = n23;
281
- te[8] = n33;
282
- return this;
283
- }
284
- identity() {
285
- this.set(
286
- 1,
287
- 0,
288
- 0,
289
- 0,
290
- 1,
291
- 0,
292
- 0,
293
- 0,
294
- 1
299
+ function catmullRom(t, p0, p1, p2, p3) {
300
+ const v0 = (p2 - p0) * 0.5;
301
+ const v1 = (p3 - p1) * 0.5;
302
+ const t2 = t * t;
303
+ const t3 = t * t2;
304
+ return (2 * p1 - 2 * p2 + v0 + v1) * t3 + (-3 * p1 + 3 * p2 - 2 * v0 - v1) * t2 + v0 * t + p1;
305
+ }
306
+
307
+ const PI = Math.PI;
308
+ const PI_2 = PI * 2;
309
+ function toKebabCase(str) {
310
+ return str.replace(/[^a-z0-9]/gi, "-").replace(/\B([A-Z])/g, "-$1").toLowerCase();
311
+ }
312
+ function getIntersectionPoint(p1, p2, q1, q2) {
313
+ const r = p2.clone().sub(p1);
314
+ const s = q2.clone().sub(q1);
315
+ const q1p1 = q1.clone().sub(p1);
316
+ const crossRS = r.cross(s);
317
+ if (crossRS === 0) {
318
+ return new Vector2(
319
+ (p1.x + q1.x) / 2,
320
+ (p1.y + q1.y) / 2
295
321
  );
296
- return this;
297
- }
298
- copy(m) {
299
- const te = this.elements;
300
- const me = m.elements;
301
- te[0] = me[0];
302
- te[1] = me[1];
303
- te[2] = me[2];
304
- te[3] = me[3];
305
- te[4] = me[4];
306
- te[5] = me[5];
307
- te[6] = me[6];
308
- te[7] = me[7];
309
- te[8] = me[8];
310
- return this;
311
- }
312
- multiply(m) {
313
- return this.multiplyMatrices(this, m);
314
- }
315
- premultiply(m) {
316
- return this.multiplyMatrices(m, this);
317
- }
318
- multiplyMatrices(a, b) {
319
- const ae = a.elements;
320
- const be = b.elements;
321
- const te = this.elements;
322
- const a11 = ae[0];
323
- const a12 = ae[3];
324
- const a13 = ae[6];
325
- const a21 = ae[1];
326
- const a22 = ae[4];
327
- const a23 = ae[7];
328
- const a31 = ae[2];
329
- const a32 = ae[5];
330
- const a33 = ae[8];
331
- const b11 = be[0];
332
- const b12 = be[3];
333
- const b13 = be[6];
334
- const b21 = be[1];
335
- const b22 = be[4];
336
- const b23 = be[7];
337
- const b31 = be[2];
338
- const b32 = be[5];
339
- const b33 = be[8];
340
- te[0] = a11 * b11 + a12 * b21 + a13 * b31;
341
- te[3] = a11 * b12 + a12 * b22 + a13 * b32;
342
- te[6] = a11 * b13 + a12 * b23 + a13 * b33;
343
- te[1] = a21 * b11 + a22 * b21 + a23 * b31;
344
- te[4] = a21 * b12 + a22 * b22 + a23 * b32;
345
- te[7] = a21 * b13 + a22 * b23 + a23 * b33;
346
- te[2] = a31 * b11 + a32 * b21 + a33 * b31;
347
- te[5] = a31 * b12 + a32 * b22 + a33 * b32;
348
- te[8] = a31 * b13 + a32 * b23 + a33 * b33;
349
- return this;
350
- }
351
- invert() {
352
- const te = this.elements;
353
- const n11 = te[0];
354
- const n21 = te[1];
355
- const n31 = te[2];
356
- const n12 = te[3];
357
- const n22 = te[4];
358
- const n32 = te[5];
359
- const n13 = te[6];
360
- const n23 = te[7];
361
- const n33 = te[8];
362
- const t11 = n33 * n22 - n32 * n23;
363
- const t12 = n32 * n13 - n33 * n12;
364
- const t13 = n23 * n12 - n22 * n13;
365
- const det = n11 * t11 + n21 * t12 + n31 * t13;
366
- if (det === 0)
367
- return this.set(0, 0, 0, 0, 0, 0, 0, 0, 0);
368
- const detInv = 1 / det;
369
- te[0] = t11 * detInv;
370
- te[1] = (n31 * n23 - n33 * n21) * detInv;
371
- te[2] = (n32 * n21 - n31 * n22) * detInv;
372
- te[3] = t12 * detInv;
373
- te[4] = (n33 * n11 - n31 * n13) * detInv;
374
- te[5] = (n31 * n12 - n32 * n11) * detInv;
375
- te[6] = t13 * detInv;
376
- te[7] = (n21 * n13 - n23 * n11) * detInv;
377
- te[8] = (n22 * n11 - n21 * n12) * detInv;
378
- return this;
379
- }
380
- transpose() {
381
- let tmp;
382
- const m = this.elements;
383
- tmp = m[1];
384
- m[1] = m[3];
385
- m[3] = tmp;
386
- tmp = m[2];
387
- m[2] = m[6];
388
- m[6] = tmp;
389
- tmp = m[5];
390
- m[5] = m[7];
391
- m[7] = tmp;
392
- return this;
393
- }
394
- scale(sx, sy) {
395
- this.premultiply(_m3.makeScale(sx, sy));
396
- return this;
397
- }
398
- rotate(theta) {
399
- this.premultiply(_m3.makeRotation(-theta));
400
- return this;
401
- }
402
- translate(tx, ty) {
403
- this.premultiply(_m3.makeTranslation(tx, ty));
404
- return this;
405
322
  }
406
- makeTranslation(x, y) {
407
- this.set(
408
- 1,
409
- 0,
410
- x,
411
- 0,
412
- 1,
413
- y,
414
- 0,
415
- 0,
416
- 1
323
+ const t = q1p1.cross(s) / crossRS;
324
+ if (Math.abs(t) > 1) {
325
+ return new Vector2(
326
+ (p1.x + q1.x) / 2,
327
+ (p1.y + q1.y) / 2
417
328
  );
418
- return this;
419
329
  }
420
- makeRotation(theta) {
421
- const c = Math.cos(theta);
422
- const s = Math.sin(theta);
423
- this.set(
424
- c,
425
- -s,
426
- 0,
427
- s,
428
- c,
429
- 0,
430
- 0,
431
- 0,
432
- 1
433
- );
434
- return this;
330
+ return new Vector2(
331
+ p1.x + t * r.x,
332
+ p1.y + t * r.y
333
+ );
334
+ }
335
+
336
+ const FUNCTIONS_RE = /([\w-]+)\((.+?)\)/g;
337
+ const ARGS_RE = /[^,]+/g;
338
+ const ARG_RE = /([-e.\d]+)(.*)/;
339
+ function parseCssFunctions(propertyValue, context = {}) {
340
+ const functions = [];
341
+ let match;
342
+ while ((match = FUNCTIONS_RE.exec(propertyValue)) !== null) {
343
+ const [, name, value] = match;
344
+ if (name) {
345
+ functions.push({
346
+ name,
347
+ args: parseCssArgs(name, value, context)
348
+ });
349
+ }
435
350
  }
436
- makeScale(x, y) {
437
- this.set(
438
- x,
439
- 0,
440
- 0,
441
- 0,
442
- y,
443
- 0,
444
- 0,
445
- 0,
446
- 1
351
+ return functions;
352
+ }
353
+ function parseCssArgs(name, value, context = {}) {
354
+ const values = [];
355
+ let match;
356
+ let i = 0;
357
+ while ((match = ARGS_RE.exec(value)) !== null) {
358
+ values.push(
359
+ parseCssArg(name, match[0], {
360
+ ...context,
361
+ index: i++
362
+ })
447
363
  );
448
- return this;
449
364
  }
450
- fromArray(array, offset = 0) {
451
- for (let i = 0; i < 9; i++) {
452
- this.elements[i] = array[i + offset];
453
- }
454
- return this;
365
+ return values;
366
+ }
367
+ function parseCssArg(name, value, context = {}) {
368
+ const { width = 1, height = 1, index = 0 } = context;
369
+ const matched = value.match(ARG_RE);
370
+ const result = {
371
+ unit: matched?.[2] ?? null,
372
+ value,
373
+ intValue: Number(matched?.[1]),
374
+ normalizedIntValue: 0,
375
+ normalizedDefaultIntValue: 0
376
+ };
377
+ switch (name) {
378
+ case "scale":
379
+ case "scaleX":
380
+ case "scaleY":
381
+ case "scale3d":
382
+ result.normalizedDefaultIntValue = 1;
383
+ break;
455
384
  }
456
- clone() {
457
- return new this.constructor().fromArray(this.elements);
385
+ switch (result.unit) {
386
+ case "%":
387
+ result.normalizedIntValue = result.intValue / 100;
388
+ break;
389
+ case "rad":
390
+ result.normalizedIntValue = result.intValue / PI_2;
391
+ break;
392
+ case "deg":
393
+ result.normalizedIntValue = result.intValue / 360;
394
+ break;
395
+ case "px":
396
+ switch (index) {
397
+ case 0:
398
+ result.normalizedIntValue = result.intValue / width;
399
+ break;
400
+ case 1:
401
+ result.normalizedIntValue = result.intValue / height;
402
+ break;
403
+ }
404
+ break;
405
+ case "turn":
406
+ case "em":
407
+ // div fontSize
408
+ case "rem":
409
+ // div fontSize
410
+ default:
411
+ result.normalizedIntValue = result.intValue;
412
+ break;
458
413
  }
414
+ return result;
459
415
  }
460
- const _m3 = /* @__PURE__ */ new Matrix3();
461
416
 
462
- function svgAngle(ux, uy, vx, vy) {
463
- const dot = ux * vx + uy * vy;
464
- const len = Math.sqrt(ux * ux + uy * uy) * Math.sqrt(vx * vx + vy * vy);
465
- let ang = Math.acos(Math.max(-1, Math.min(1, dot / len)));
466
- if (ux * vy - uy * vx < 0)
467
- ang = -ang;
468
- return ang;
417
+ function cubicBezierP0(t, p) {
418
+ const k = 1 - t;
419
+ return k * k * k * p;
469
420
  }
470
- function parseArcCommand(path, rx, ry, xAxisRotation, largeArcFlag, sweepFlag, start, end) {
471
- if (rx === 0 || ry === 0) {
472
- path.lineTo(end.x, end.y);
473
- return;
474
- }
475
- xAxisRotation = xAxisRotation * Math.PI / 180;
476
- rx = Math.abs(rx);
477
- ry = Math.abs(ry);
478
- const dx2 = (start.x - end.x) / 2;
479
- const dy2 = (start.y - end.y) / 2;
480
- const x1p = Math.cos(xAxisRotation) * dx2 + Math.sin(xAxisRotation) * dy2;
481
- const y1p = -Math.sin(xAxisRotation) * dx2 + Math.cos(xAxisRotation) * dy2;
482
- let rxs = rx * rx;
483
- let rys = ry * ry;
484
- const x1ps = x1p * x1p;
485
- const y1ps = y1p * y1p;
486
- const cr = x1ps / rxs + y1ps / rys;
487
- if (cr > 1) {
488
- const s = Math.sqrt(cr);
489
- rx = s * rx;
490
- ry = s * ry;
491
- rxs = rx * rx;
492
- rys = ry * ry;
421
+ function cubicBezierP1(t, p) {
422
+ const k = 1 - t;
423
+ return 3 * k * k * t * p;
424
+ }
425
+ function cubicBezierP2(t, p) {
426
+ return 3 * (1 - t) * t * t * p;
427
+ }
428
+ function cubicBezierP3(t, p) {
429
+ return t * t * t * p;
430
+ }
431
+ function cubicBezier(t, p0, p1, p2, p3) {
432
+ return cubicBezierP0(t, p0) + cubicBezierP1(t, p1) + cubicBezierP2(t, p2) + cubicBezierP3(t, p3);
433
+ }
434
+
435
+ function fillTriangulate(pointArray, options = {}) {
436
+ let {
437
+ vertices = [],
438
+ indices = [],
439
+ holes = [],
440
+ verticesStride = 2,
441
+ verticesOffset = vertices.length / verticesStride,
442
+ indicesOffset = indices.length
443
+ } = options;
444
+ const triangles = earcut(pointArray, holes, 2);
445
+ if (triangles.length) {
446
+ for (let i = 0; i < triangles.length; i += 3) {
447
+ indices[indicesOffset++] = triangles[i] + verticesOffset;
448
+ indices[indicesOffset++] = triangles[i + 1] + verticesOffset;
449
+ indices[indicesOffset++] = triangles[i + 2] + verticesOffset;
450
+ }
451
+ let index = verticesOffset * verticesStride;
452
+ for (let i = 0; i < pointArray.length; i += 2) {
453
+ vertices[index] = pointArray[i];
454
+ vertices[index + 1] = pointArray[i + 1];
455
+ index += verticesStride;
456
+ }
493
457
  }
494
- const dq = rxs * y1ps + rys * x1ps;
495
- const pq = (rxs * rys - dq) / dq;
496
- let q = Math.sqrt(Math.max(0, pq));
497
- if (largeArcFlag === sweepFlag)
498
- q = -q;
499
- const cxp = q * rx * y1p / ry;
500
- const cyp = -q * ry * x1p / rx;
501
- const cx = Math.cos(xAxisRotation) * cxp - Math.sin(xAxisRotation) * cyp + (start.x + end.x) / 2;
502
- const cy = Math.sin(xAxisRotation) * cxp + Math.cos(xAxisRotation) * cyp + (start.y + end.y) / 2;
503
- const theta = svgAngle(1, 0, (x1p - cxp) / rx, (y1p - cyp) / ry);
504
- const delta = svgAngle((x1p - cxp) / rx, (y1p - cyp) / ry, (-x1p - cxp) / rx, (-y1p - cyp) / ry) % (Math.PI * 2);
505
- path.ellipse(cx, cy, rx, ry, xAxisRotation, theta, theta + delta, sweepFlag === 0);
458
+ return {
459
+ vertices,
460
+ indices
461
+ };
506
462
  }
507
463
 
508
- const RE$3 = {
509
- WHITESPACE: /[ \t\r\n]/,
510
- DIGIT: /\d/,
511
- SIGN: /[-+]/,
512
- POINT: /\./,
513
- COMMA: /,/,
514
- EXP: /e/i,
515
- FLAGS: /[01]/
516
- };
517
- function parsePathDataArgs(input, flags, stride = 0) {
518
- const SEP = 0;
519
- const INT = 1;
520
- const FLOAT = 2;
521
- const EXP = 3;
522
- let state = SEP;
523
- let seenComma = true;
524
- let number = "";
525
- let exponent = "";
526
- const result = [];
527
- function throwSyntaxError(current2, i, partial) {
528
- const error = new SyntaxError(`Unexpected character "${current2}" at index ${i}.`);
529
- error.partial = partial;
530
- throw error;
531
- }
532
- function newNumber() {
533
- if (number !== "") {
534
- if (exponent === "")
535
- result.push(Number(number));
536
- else result.push(Number(number) * 10 ** Number(exponent));
537
- }
538
- number = "";
539
- exponent = "";
540
- }
541
- let current;
542
- const length = input.length;
543
- for (let i = 0; i < length; i++) {
544
- current = input[i];
545
- if (Array.isArray(flags) && flags.includes(result.length % stride) && RE$3.FLAGS.test(current)) {
546
- state = INT;
547
- number = current;
548
- newNumber();
549
- continue;
550
- }
551
- if (state === SEP) {
552
- if (RE$3.WHITESPACE.test(current)) {
553
- continue;
554
- }
555
- if (RE$3.DIGIT.test(current) || RE$3.SIGN.test(current)) {
556
- state = INT;
557
- number = current;
558
- continue;
559
- }
560
- if (RE$3.POINT.test(current)) {
561
- state = FLOAT;
562
- number = current;
563
- continue;
564
- }
565
- if (RE$3.COMMA.test(current)) {
566
- if (seenComma) {
567
- throwSyntaxError(current, i, result);
464
+ const RECURSION_LIMIT$1 = 8;
465
+ const FLT_EPSILON$1 = 11920929e-14;
466
+ const PATH_DISTANCE_EPSILON$1 = 1;
467
+ function getAdaptiveCubicBezierCurvePoints(sX, sY, x1, y1, x2, y2, x, y, smoothness = 0.5, points = []) {
468
+ const scale = 1;
469
+ const smoothing = Math.min(
470
+ 0.99,
471
+ // a value of 1.0 actually inverts smoothing, so we cap it at 0.99
472
+ Math.max(0, smoothness)
473
+ );
474
+ let distanceTolerance = (PATH_DISTANCE_EPSILON$1 - smoothing) / scale;
475
+ distanceTolerance *= distanceTolerance;
476
+ recursive$1(sX, sY, x1, y1, x2, y2, x, y, points, distanceTolerance, 0);
477
+ points.push(x, y);
478
+ return points;
479
+ }
480
+ function recursive$1(x1, y1, x2, y2, x3, y3, x4, y4, points, distanceTolerance, level) {
481
+ if (level > RECURSION_LIMIT$1)
482
+ return;
483
+ const x12 = (x1 + x2) / 2;
484
+ const y12 = (y1 + y2) / 2;
485
+ const x23 = (x2 + x3) / 2;
486
+ const y23 = (y2 + y3) / 2;
487
+ const x34 = (x3 + x4) / 2;
488
+ const y34 = (y3 + y4) / 2;
489
+ const x123 = (x12 + x23) / 2;
490
+ const y123 = (y12 + y23) / 2;
491
+ const x234 = (x23 + x34) / 2;
492
+ const y234 = (y23 + y34) / 2;
493
+ const x1234 = (x123 + x234) / 2;
494
+ const y1234 = (y123 + y234) / 2;
495
+ if (level > 0) {
496
+ let dx = x4 - x1;
497
+ let dy = y4 - y1;
498
+ const d2 = Math.abs((x2 - x4) * dy - (y2 - y4) * dx);
499
+ const d3 = Math.abs((x3 - x4) * dy - (y3 - y4) * dx);
500
+ if (d2 > FLT_EPSILON$1 && d3 > FLT_EPSILON$1) {
501
+ if ((d2 + d3) * (d2 + d3) <= distanceTolerance * (dx * dx + dy * dy)) {
502
+ {
503
+ points.push(x1234, y1234);
504
+ return;
568
505
  }
569
- seenComma = true;
570
- }
571
- }
572
- if (state === INT) {
573
- if (RE$3.DIGIT.test(current)) {
574
- number += current;
575
- continue;
576
- }
577
- if (RE$3.POINT.test(current)) {
578
- number += current;
579
- state = FLOAT;
580
- continue;
581
- }
582
- if (RE$3.EXP.test(current)) {
583
- state = EXP;
584
- continue;
585
- }
586
- if (RE$3.SIGN.test(current) && number.length === 1 && RE$3.SIGN.test(number[0])) {
587
- throwSyntaxError(current, i, result);
588
506
  }
589
- }
590
- if (state === FLOAT) {
591
- if (RE$3.DIGIT.test(current)) {
592
- number += current;
593
- continue;
507
+ } else if (d2 > FLT_EPSILON$1) {
508
+ if (d2 * d2 <= distanceTolerance * (dx * dx + dy * dy)) {
509
+ {
510
+ points.push(x1234, y1234);
511
+ return;
512
+ }
594
513
  }
595
- if (RE$3.EXP.test(current)) {
596
- state = EXP;
597
- continue;
514
+ } else if (d3 > FLT_EPSILON$1) {
515
+ if (d3 * d3 <= distanceTolerance * (dx * dx + dy * dy)) {
516
+ {
517
+ points.push(x1234, y1234);
518
+ return;
519
+ }
598
520
  }
599
- if (RE$3.POINT.test(current) && number[number.length - 1] === ".") {
600
- throwSyntaxError(current, i, result);
521
+ } else {
522
+ dx = x1234 - (x1 + x4) / 2;
523
+ dy = y1234 - (y1 + y4) / 2;
524
+ if (dx * dx + dy * dy <= distanceTolerance) {
525
+ points.push(x1234, y1234);
526
+ return;
601
527
  }
602
528
  }
603
- if (state === EXP) {
604
- if (RE$3.DIGIT.test(current)) {
605
- exponent += current;
606
- continue;
607
- }
608
- if (RE$3.SIGN.test(current)) {
609
- if (exponent === "") {
610
- exponent += current;
611
- continue;
612
- }
613
- if (exponent.length === 1 && RE$3.SIGN.test(exponent)) {
614
- throwSyntaxError(current, i, result);
615
- }
529
+ }
530
+ recursive$1(x1, y1, x12, y12, x123, y123, x1234, y1234, points, distanceTolerance, level + 1);
531
+ recursive$1(x1234, y1234, x234, y234, x34, y34, x4, y4, points, distanceTolerance, level + 1);
532
+ }
533
+
534
+ const RECURSION_LIMIT = 8;
535
+ const FLT_EPSILON = 11920929e-14;
536
+ const PATH_DISTANCE_EPSILON = 1;
537
+ function getAdaptiveQuadraticBezierCurvePoints(sX, sY, x1, y1, x, y, smoothness = 0.5, points = []) {
538
+ const scale = 1;
539
+ const smoothing = Math.min(
540
+ 0.99,
541
+ // a value of 1.0 actually inverts smoothing, so we cap it at 0.99
542
+ Math.max(0, smoothness)
543
+ );
544
+ let distanceTolerance = (PATH_DISTANCE_EPSILON - smoothing) / scale;
545
+ distanceTolerance *= distanceTolerance;
546
+ recursive(points, sX, sY, x1, y1, x, y, distanceTolerance, 0);
547
+ points.push(x, y);
548
+ return points;
549
+ }
550
+ function recursive(points, x1, y1, x2, y2, x3, y3, distanceTolerance, level) {
551
+ if (level > RECURSION_LIMIT)
552
+ return;
553
+ const x12 = (x1 + x2) / 2;
554
+ const y12 = (y1 + y2) / 2;
555
+ const x23 = (x2 + x3) / 2;
556
+ const y23 = (y2 + y3) / 2;
557
+ const x123 = (x12 + x23) / 2;
558
+ const y123 = (y12 + y23) / 2;
559
+ let dx = x3 - x1;
560
+ let dy = y3 - y1;
561
+ const d = Math.abs((x2 - x3) * dy - (y2 - y3) * dx);
562
+ if (d > FLT_EPSILON) {
563
+ if (d * d <= distanceTolerance * (dx * dx + dy * dy)) {
564
+ {
565
+ points.push(x123, y123);
566
+ return;
616
567
  }
617
568
  }
618
- if (RE$3.WHITESPACE.test(current)) {
619
- newNumber();
620
- state = SEP;
621
- seenComma = false;
622
- } else if (RE$3.COMMA.test(current)) {
623
- newNumber();
624
- state = SEP;
625
- seenComma = true;
626
- } else if (RE$3.SIGN.test(current)) {
627
- newNumber();
628
- state = INT;
629
- number = current;
630
- } else if (RE$3.POINT.test(current)) {
631
- newNumber();
632
- state = FLOAT;
633
- number = current;
634
- } else {
635
- throwSyntaxError(current, i, result);
569
+ } else {
570
+ dx = x123 - (x1 + x3) / 2;
571
+ dy = y123 - (y1 + y3) / 2;
572
+ if (dx * dx + dy * dy <= distanceTolerance) {
573
+ points.push(x123, y123);
574
+ return;
636
575
  }
637
576
  }
638
- newNumber();
639
- return result;
577
+ recursive(points, x1, y1, x12, y12, x123, y123, distanceTolerance, level + 1);
578
+ recursive(points, x123, y123, x23, y23, x3, y3, distanceTolerance, level + 1);
640
579
  }
641
580
 
642
- function getReflection(a, b) {
643
- return a - (b - a);
581
+ function getDirectedArea(vertices) {
582
+ let area = 0;
583
+ const n = vertices.length;
584
+ for (let i = 0; i < n; i += 2) {
585
+ const x0 = vertices[i];
586
+ const y0 = vertices[i + 1];
587
+ const x1 = vertices[(i + 2) % (n - 1)];
588
+ const y1 = vertices[(i + 3) % n];
589
+ area += x0 * y1 - x1 * y0;
590
+ }
591
+ return area / 2;
644
592
  }
645
- function svgPathCommandsAddToPath2D(commands, path) {
646
- const current = new Vector2();
647
- const control = new Vector2();
648
- for (let i = 0, l = commands.length; i < l; i++) {
649
- const cmd = commands[i];
650
- if (cmd.type === "m" || cmd.type === "M") {
651
- if (cmd.type === "m") {
652
- current.add(cmd);
653
- } else {
654
- current.copy(cmd);
655
- }
656
- path.moveTo(current.x, current.y);
657
- control.copy(current);
658
- } else if (cmd.type === "h" || cmd.type === "H") {
659
- if (cmd.type === "h") {
660
- current.x += cmd.x;
661
- } else {
662
- current.x = cmd.x;
663
- }
664
- path.lineTo(current.x, current.y);
665
- control.copy(current);
666
- } else if (cmd.type === "v" || cmd.type === "V") {
667
- if (cmd.type === "v") {
668
- current.y += cmd.y;
669
- } else {
670
- current.y = cmd.y;
593
+
594
+ function cross(ax, ay, bx, by, cx, cy) {
595
+ return (bx - ax) * (cy - ay) - (by - ay) * (cx - ax);
596
+ }
597
+ function windingNumber(px, py, polygon) {
598
+ const polygonLen = polygon.length;
599
+ let wn = 0;
600
+ for (let i = 0, j = polygonLen - 2; i < polygonLen; j = i, i += 2) {
601
+ const xi = polygon[i];
602
+ const yi = polygon[i + 1];
603
+ const xj = polygon[j];
604
+ const yj = polygon[j + 1];
605
+ if (yi <= py) {
606
+ if (yj > py && cross(xj, yj, xi, yi, px, py) > 0)
607
+ wn++;
608
+ } else {
609
+ if (yj <= py && cross(xj, yj, xi, yi, px, py) < 0)
610
+ wn--;
611
+ }
612
+ }
613
+ return wn;
614
+ }
615
+ function distance(p1, p2) {
616
+ const dx = p2[0] - p1[0];
617
+ const dy = p2[1] - p1[1];
618
+ return Math.sqrt(dx * dx + dy * dy);
619
+ }
620
+ function nonzeroFillRule(paths) {
621
+ const results = paths.map((_, i) => ({ index: i }));
622
+ const testPointsGroups = paths.map((path) => {
623
+ const len = path.length;
624
+ if (!len) {
625
+ return [];
626
+ }
627
+ let xMinYAuto = [Number.MAX_SAFE_INTEGER, 0];
628
+ let xAutoYMin = [0, Number.MAX_SAFE_INTEGER];
629
+ let xMaxYAuto = [Number.MIN_SAFE_INTEGER, 0];
630
+ let xAutoYMax = [0, Number.MIN_SAFE_INTEGER];
631
+ for (let i = 0; i < len; i += 2) {
632
+ const x = path[i];
633
+ const y = path[i + 1];
634
+ if (xMinYAuto[0] > x) {
635
+ xMinYAuto = [x, y];
671
636
  }
672
- path.lineTo(current.x, current.y);
673
- control.copy(current);
674
- } else if (cmd.type === "l" || cmd.type === "L") {
675
- if (cmd.type === "l") {
676
- current.add(cmd);
677
- } else {
678
- current.copy(cmd);
637
+ if (xAutoYMin[1] > y) {
638
+ xAutoYMin = [x, y];
679
639
  }
680
- path.lineTo(current.x, current.y);
681
- control.copy(current);
682
- } else if (cmd.type === "c" || cmd.type === "C") {
683
- if (cmd.type === "c") {
684
- path.bezierCurveTo(
685
- current.x + cmd.x1,
686
- current.y + cmd.y1,
687
- current.x + cmd.x2,
688
- current.y + cmd.y2,
689
- current.x + cmd.x,
690
- current.y + cmd.y
691
- );
692
- control.x = current.x + cmd.x2;
693
- control.y = current.y + cmd.y2;
694
- current.add(cmd);
695
- } else {
696
- path.bezierCurveTo(
697
- cmd.x1,
698
- cmd.y1,
699
- cmd.x2,
700
- cmd.y2,
701
- cmd.x,
702
- cmd.y
703
- );
704
- control.x = cmd.x2;
705
- control.y = cmd.y2;
706
- current.copy(cmd);
640
+ if (xMaxYAuto[0] < x) {
641
+ xMaxYAuto = [x, y];
707
642
  }
708
- } else if (cmd.type === "s" || cmd.type === "S") {
709
- if (cmd.type === "s") {
710
- path.bezierCurveTo(
711
- getReflection(current.x, control.x),
712
- getReflection(current.y, control.y),
713
- current.x + cmd.x2,
714
- current.y + cmd.y2,
715
- current.x + cmd.x,
716
- current.y + cmd.y
717
- );
718
- control.x = current.x + cmd.x2;
719
- control.y = current.y + cmd.y2;
720
- current.add(cmd);
721
- } else {
722
- path.bezierCurveTo(
723
- getReflection(current.x, control.x),
724
- getReflection(current.y, control.y),
725
- cmd.x2,
726
- cmd.y2,
727
- cmd.x,
728
- cmd.y
729
- );
730
- control.x = cmd.x2;
731
- control.y = cmd.y2;
732
- current.copy(cmd);
643
+ if (xAutoYMax[1] < y) {
644
+ xAutoYMax = [x, y];
733
645
  }
734
- } else if (cmd.type === "q" || cmd.type === "Q") {
735
- if (cmd.type === "q") {
736
- path.quadraticCurveTo(
737
- current.x + cmd.x1,
738
- current.y + cmd.y1,
739
- current.x + cmd.x,
740
- current.y + cmd.y
741
- );
742
- control.x = current.x + cmd.x1;
743
- control.y = current.y + cmd.y1;
744
- current.add(cmd);
745
- } else {
746
- path.quadraticCurveTo(
747
- cmd.x1,
748
- cmd.y1,
749
- cmd.x,
750
- cmd.y
751
- );
752
- control.x = cmd.x1;
753
- control.y = cmd.y1;
754
- current.copy(cmd);
646
+ }
647
+ const mid = [
648
+ (xMinYAuto[0] + xMaxYAuto[0]) / 2,
649
+ (xAutoYMin[1] + xAutoYMax[1]) / 2
650
+ ];
651
+ let xMidYMinDx;
652
+ let xMidYMaxDx;
653
+ let xMidYMin;
654
+ let xMidYMax;
655
+ let xMinYMidDy;
656
+ let xMaxYMidDy;
657
+ let xMinYMid;
658
+ let xMaxYMid;
659
+ for (let i = 0; i < len; i += 2) {
660
+ const x = path[i];
661
+ const y = path[i + 1];
662
+ const _dx = Math.abs(x - mid[0]);
663
+ const _dy = Math.abs(y - mid[1]);
664
+ if (y < mid[1] && (!xMidYMinDx || _dx < xMidYMinDx)) {
665
+ xMidYMinDx = _dx;
666
+ xMidYMin = [x, y];
755
667
  }
756
- } else if (cmd.type === "t" || cmd.type === "T") {
757
- const rx = getReflection(current.x, control.x);
758
- const ry = getReflection(current.y, control.y);
759
- control.x = rx;
760
- control.y = ry;
761
- if (cmd.type === "t") {
762
- path.quadraticCurveTo(
763
- rx,
764
- ry,
765
- current.x + cmd.x,
766
- current.y + cmd.y
767
- );
768
- current.add(cmd);
769
- } else {
770
- path.quadraticCurveTo(
771
- rx,
772
- ry,
773
- cmd.x,
774
- cmd.y
775
- );
776
- current.copy(cmd);
668
+ if (y > mid[1] && (!xMidYMaxDx || _dx < xMidYMaxDx)) {
669
+ xMidYMaxDx = _dx;
670
+ xMidYMax = [x, y];
777
671
  }
778
- } else if (cmd.type === "a" || cmd.type === "A") {
779
- const start = current.clone();
780
- if (cmd.type === "a") {
781
- if (cmd.x === 0 && cmd.y === 0)
782
- continue;
783
- current.add(cmd);
784
- } else {
785
- if (current.equals(cmd))
786
- continue;
787
- current.copy(cmd);
672
+ if (x < mid[0] && (!xMinYMidDy || _dy < xMinYMidDy)) {
673
+ xMinYMidDy = _dy;
674
+ xMinYMid = [x, y];
788
675
  }
789
- control.copy(current);
790
- parseArcCommand(
791
- path,
792
- cmd.rx,
793
- cmd.ry,
794
- cmd.angle,
795
- cmd.largeArcFlag,
796
- cmd.sweepFlag,
797
- start,
798
- current
799
- );
800
- } else if (cmd.type === "z" || cmd.type === "Z") {
801
- if (path.startPoint) {
802
- current.copy(path.startPoint);
676
+ if (x > mid[0] && (!xMaxYMidDy || _dy < xMaxYMidDy)) {
677
+ xMaxYMidDy = _dy;
678
+ xMaxYMid = [x, y];
803
679
  }
804
- path.closePath();
805
- } else {
806
- console.warn("Unsupported commands", cmd);
680
+ }
681
+ return [
682
+ xMinYAuto,
683
+ xAutoYMin,
684
+ xMaxYAuto,
685
+ xAutoYMax,
686
+ xMidYMin,
687
+ xMidYMax,
688
+ xMinYMid,
689
+ xMaxYMid
690
+ ].filter(Boolean);
691
+ });
692
+ for (let i = 0, len = paths.length; i < len; i++) {
693
+ const _results = [];
694
+ const testPoints = testPointsGroups[i];
695
+ for (let j = 0; j < len; j++) {
696
+ if (i === j)
697
+ continue;
698
+ const wnMap = {};
699
+ const wnList = [];
700
+ for (let p = 0, pLen = testPoints.length; p < pLen; p++) {
701
+ const [x, y] = testPoints[p];
702
+ const winding = windingNumber(x, y, paths[j]);
703
+ wnMap[winding] = (wnMap[winding] ?? 0) + 1;
704
+ wnList.push(winding);
705
+ }
706
+ if (wnList.filter((v) => v !== 0).length > wnList.filter((v) => v === 0).length) {
707
+ _results.push({
708
+ index: i,
709
+ parentIndex: j,
710
+ winding: Number(
711
+ Array.from(Object.entries(wnMap)).sort((a, b) => b[1] - a[1])?.[0]?.[0] ?? 0
712
+ ),
713
+ dist: distance(testPointsGroups[i][0], testPointsGroups[j][0])
714
+ });
715
+ }
716
+ }
717
+ if (_results.reduce((total, item) => total + item.winding, 0) !== 0) {
718
+ _results.sort((a, b) => a.dist - b.dist);
719
+ results[i] = _results[0];
807
720
  }
808
721
  }
722
+ return results;
809
723
  }
810
724
 
811
- function svgPathCommandsToData(commands) {
812
- let first;
813
- let prev;
814
- const data = [];
815
- for (let i = 0, len = commands.length; i < len; i++) {
816
- const cmd = commands[i];
817
- switch (cmd.type) {
818
- case "m":
819
- case "M":
820
- if (cmd.x.toFixed(4) === prev?.x.toFixed(4) && cmd.y.toFixed(4) === prev?.y.toFixed(4)) {
821
- continue;
822
- }
823
- data.push(`${cmd.type} ${cmd.x} ${cmd.y}`);
824
- prev = { x: cmd.x, y: cmd.y };
825
- first = { x: cmd.x, y: cmd.y };
826
- break;
827
- case "h":
828
- case "H":
829
- data.push(`${cmd.type} ${cmd.x}`);
830
- prev = { x: cmd.x, y: prev?.y ?? 0 };
831
- break;
832
- case "v":
833
- case "V":
834
- data.push(`${cmd.type} ${cmd.y}`);
835
- prev = { x: prev?.x ?? 0, y: cmd.y };
836
- break;
837
- case "l":
838
- case "L":
839
- data.push(`${cmd.type} ${cmd.x} ${cmd.y}`);
840
- prev = { x: cmd.x, y: cmd.y };
841
- break;
842
- case "c":
843
- case "C":
844
- data.push(`${cmd.type} ${cmd.x1} ${cmd.y1} ${cmd.x2} ${cmd.y2} ${cmd.x} ${cmd.y}`);
845
- prev = { x: cmd.x, y: cmd.y };
846
- break;
847
- case "s":
848
- case "S":
849
- data.push(`${cmd.type} ${cmd.x2} ${cmd.y2} ${cmd.x} ${cmd.y}`);
850
- prev = { x: cmd.x, y: cmd.y };
851
- break;
852
- case "q":
853
- case "Q":
854
- data.push(`${cmd.type} ${cmd.x1} ${cmd.y1} ${cmd.x} ${cmd.y}`);
855
- prev = { x: cmd.x, y: cmd.y };
856
- break;
857
- case "t":
858
- case "T":
859
- data.push(`${cmd.type} ${cmd.x} ${cmd.y}`);
860
- prev = { x: cmd.x, y: cmd.y };
861
- break;
862
- case "a":
863
- case "A":
864
- data.push(`${cmd.type} ${cmd.rx} ${cmd.ry} ${cmd.angle} ${cmd.largeArcFlag} ${cmd.sweepFlag} ${cmd.x} ${cmd.y}`);
865
- prev = { x: cmd.x, y: cmd.y };
866
- break;
867
- case "z":
868
- case "Z":
869
- data.push(cmd.type);
870
- if (first) {
871
- prev = { x: first.x, y: first.y };
872
- }
873
- break;
874
- }
875
- }
876
- return data.join(" ");
725
+ function quadraticBezierP0(t, p) {
726
+ const k = 1 - t;
727
+ return k * k * p;
728
+ }
729
+ function quadraticBezierP1(t, p) {
730
+ return 2 * (1 - t) * t * p;
731
+ }
732
+ function quadraticBezierP2(t, p) {
733
+ return t * t * p;
734
+ }
735
+ function quadraticBezier(t, p0, p1, p2) {
736
+ return quadraticBezierP0(t, p0) + quadraticBezierP1(t, p1) + quadraticBezierP2(t, p2);
877
737
  }
878
738
 
879
- const RE$2 = /[a-df-z][^a-df-z]*/gi;
880
- function svgPathDataToCommands(data) {
881
- const commands = [];
882
- const matched = data.match(RE$2);
883
- if (!matched) {
884
- return commands;
739
+ const closePointEps = 1e-4;
740
+ const curveEps = 1e-4;
741
+ function strokeTriangulate(points, options = {}) {
742
+ const {
743
+ vertices = [],
744
+ indices = [],
745
+ lineStyle = {
746
+ alignment: 0.5,
747
+ cap: "butt",
748
+ join: "miter",
749
+ width: 1,
750
+ miterLimit: 10
751
+ },
752
+ flipAlignment = false,
753
+ closed = true
754
+ } = options;
755
+ const eps = closePointEps;
756
+ if (points.length === 0) {
757
+ return { vertices, indices };
885
758
  }
886
- for (let i = 0, len = matched.length; i < len; i++) {
887
- const command = matched[i];
888
- const type = command.charAt(0);
889
- const data2 = command.slice(1).trim();
890
- let args;
891
- switch (type) {
892
- case "m":
893
- case "M":
894
- args = parsePathDataArgs(data2);
895
- for (let i2 = 0, len2 = args.length; i2 < len2; i2 += 2) {
896
- if (i2 === 0) {
897
- commands.push({ type, x: args[i2], y: args[i2 + 1] });
898
- } else {
899
- commands.push({ type: type === "m" ? "l" : "L", x: args[i2], y: args[i2 + 1] });
900
- }
901
- }
902
- break;
903
- case "h":
904
- case "H":
905
- args = parsePathDataArgs(data2);
906
- for (let i2 = 0, len2 = args.length; i2 < len2; i2++) {
907
- commands.push({ type, x: args[i2] });
908
- }
909
- break;
910
- case "v":
911
- case "V":
912
- args = parsePathDataArgs(data2);
913
- for (let i2 = 0, len2 = args.length; i2 < len2; i2++) {
914
- commands.push({ type, y: args[i2] });
915
- }
916
- break;
917
- case "l":
918
- case "L":
919
- args = parsePathDataArgs(data2);
920
- for (let i2 = 0, len2 = args.length; i2 < len2; i2 += 2) {
921
- commands.push({ type, x: args[i2], y: args[i2 + 1] });
922
- }
923
- break;
924
- case "c":
925
- case "C":
926
- args = parsePathDataArgs(data2);
927
- for (let i2 = 0, len2 = args.length; i2 < len2; i2 += 6) {
928
- commands.push({
929
- type,
930
- x1: args[i2],
931
- y1: args[i2 + 1],
932
- x2: args[i2 + 2],
933
- y2: args[i2 + 3],
934
- x: args[i2 + 4],
935
- y: args[i2 + 5]
936
- });
937
- }
938
- break;
939
- case "s":
940
- case "S":
941
- args = parsePathDataArgs(data2);
942
- for (let i2 = 0, len2 = args.length; i2 < len2; i2 += 4) {
943
- commands.push({
944
- type,
945
- x2: args[i2],
946
- y2: args[i2 + 1],
947
- x: args[i2 + 2],
948
- y: args[i2 + 3]
949
- });
950
- }
951
- break;
952
- case "q":
953
- case "Q":
954
- args = parsePathDataArgs(data2);
955
- for (let i2 = 0, len2 = args.length; i2 < len2; i2 += 4) {
956
- commands.push({
957
- type,
958
- x1: args[i2],
959
- y1: args[i2 + 1],
960
- x: args[i2 + 2],
961
- y: args[i2 + 3]
962
- });
963
- }
964
- break;
965
- case "t":
966
- case "T":
967
- args = parsePathDataArgs(data2);
968
- for (let i2 = 0, len2 = args.length; i2 < len2; i2 += 2) {
969
- commands.push({
970
- type,
971
- x: args[i2],
972
- y: args[i2 + 1]
973
- });
974
- }
975
- break;
976
- case "a":
977
- case "A":
978
- args = parsePathDataArgs(data2, [3, 4], 7);
979
- for (let i2 = 0, len2 = args.length; i2 < len2; i2 += 7) {
980
- commands.push({
981
- type,
982
- rx: args[i2],
983
- ry: args[i2 + 1],
984
- angle: args[i2 + 2],
985
- largeArcFlag: args[i2 + 3],
986
- sweepFlag: args[i2 + 4],
987
- x: args[i2 + 5],
988
- y: args[i2 + 6]
989
- });
990
- }
991
- break;
992
- case "z":
993
- case "Z":
994
- commands.push({
995
- type
996
- });
997
- break;
998
- default:
999
- console.warn(command);
1000
- }
759
+ const style = lineStyle;
760
+ let alignment = style.alignment;
761
+ if (lineStyle.alignment !== 0.5) {
762
+ let orientation = getOrientationOfPoints(points);
763
+ if (flipAlignment)
764
+ orientation *= -1;
765
+ alignment = (alignment - 0.5) * orientation + 0.5;
1001
766
  }
1002
- return commands;
1003
- }
1004
-
1005
- const dataUri = "data:image/svg+xml;";
1006
- const base64DataUri = `${dataUri}base64,`;
1007
- const utf8DataUri = `${dataUri}charset=utf8,`;
1008
- function svgToDom(svg) {
1009
- if (typeof svg === "string") {
1010
- let xml;
1011
- if (svg.startsWith(base64DataUri)) {
1012
- svg = svg.substring(base64DataUri.length, svg.length);
1013
- xml = atob(svg);
1014
- } else if (svg.startsWith(utf8DataUri)) {
1015
- svg = svg.substring(utf8DataUri.length, svg.length);
1016
- xml = decodeURIComponent(svg);
1017
- } else {
1018
- xml = svg;
1019
- }
1020
- const doc = new DOMParser().parseFromString(xml, "text/xml");
1021
- const error = doc.querySelector("parsererror");
1022
- if (error) {
1023
- throw new Error(`${error.textContent ?? "parser error"}
1024
- ${xml}`);
767
+ const firstPoint = { x: points[0], y: points[1] };
768
+ const lastPoint = { x: points[points.length - 2], y: points[points.length - 1] };
769
+ const closedShape = closed;
770
+ const closedPath = Math.abs(firstPoint.x - lastPoint.x) < eps && Math.abs(firstPoint.y - lastPoint.y) < eps;
771
+ if (closedShape) {
772
+ points = points.slice();
773
+ if (closedPath) {
774
+ points.pop();
775
+ points.pop();
776
+ lastPoint.x = points[points.length - 2];
777
+ lastPoint.y = points[points.length - 1];
1025
778
  }
1026
- return doc.documentElement;
1027
- } else {
1028
- return svg;
779
+ const midPointX = (firstPoint.x + lastPoint.x) * 0.5;
780
+ const midPointY = (lastPoint.y + firstPoint.y) * 0.5;
781
+ points.unshift(midPointX, midPointY);
782
+ points.push(midPointX, midPointY);
1029
783
  }
1030
- }
1031
-
1032
- const defaultUnit = "px";
1033
- const defaultDPI = 90;
1034
- const units = ["mm", "cm", "in", "pt", "pc", "px"];
1035
- const unitConversion = {
1036
- mm: {
1037
- mm: 1,
1038
- cm: 0.1,
1039
- in: 1 / 25.4,
1040
- pt: 72 / 25.4,
1041
- pc: 6 / 25.4,
1042
- px: -1
1043
- },
1044
- cm: {
1045
- mm: 10,
1046
- cm: 1,
1047
- in: 1 / 2.54,
1048
- pt: 72 / 2.54,
1049
- pc: 6 / 2.54,
1050
- px: -1
1051
- },
1052
- in: {
1053
- mm: 25.4,
1054
- cm: 2.54,
1055
- in: 1,
1056
- pt: 72,
1057
- pc: 6,
1058
- px: -1
1059
- },
1060
- pt: {
1061
- mm: 25.4 / 72,
1062
- cm: 2.54 / 72,
1063
- in: 1 / 72,
1064
- pt: 1,
1065
- pc: 6 / 72,
1066
- px: -1
1067
- },
1068
- pc: {
1069
- mm: 25.4 / 6,
1070
- cm: 2.54 / 6,
1071
- in: 1 / 6,
1072
- pt: 72 / 6,
1073
- pc: 1,
1074
- px: -1
1075
- },
1076
- px: {
1077
- px: 1
1078
- }
1079
- };
1080
- function parseFloatWithUnits(string) {
1081
- let theUnit = "px";
1082
- if (typeof string === "string") {
1083
- for (let i = 0, n = units.length; i < n; i++) {
1084
- const u = units[i];
1085
- if (string.endsWith(u)) {
1086
- theUnit = u;
1087
- string = string.substring(0, string.length - u.length);
1088
- break;
1089
- }
1090
- }
1091
- }
1092
- let scale;
1093
- {
1094
- scale = unitConversion[theUnit][defaultUnit];
1095
- if (scale < 0) {
1096
- scale = unitConversion[theUnit].in * defaultDPI;
784
+ const verts = vertices;
785
+ const length = points.length / 2;
786
+ let indexCount = points.length;
787
+ const indexStart = verts.length / 2;
788
+ const width = style.width / 2;
789
+ const widthSquared = width * width;
790
+ const miterLimitSquared = style.miterLimit * style.miterLimit;
791
+ let x0 = points[0];
792
+ let y0 = points[1];
793
+ let x1 = points[2];
794
+ let y1 = points[3];
795
+ let x2 = 0;
796
+ let y2 = 0;
797
+ let perpX = -(y0 - y1);
798
+ let perpY = x0 - x1;
799
+ let perp1x = 0;
800
+ let perp1y = 0;
801
+ let dist = Math.sqrt(perpX * perpX + perpY * perpY);
802
+ perpX /= dist;
803
+ perpY /= dist;
804
+ perpX *= width;
805
+ perpY *= width;
806
+ const ratio = alignment;
807
+ const innerWeight = (1 - ratio) * 2;
808
+ const outerWeight = ratio * 2;
809
+ if (!closedShape) {
810
+ if (style.cap === "round") {
811
+ indexCount += round(
812
+ x0 - perpX * (innerWeight - outerWeight) * 0.5,
813
+ y0 - perpY * (innerWeight - outerWeight) * 0.5,
814
+ x0 - perpX * innerWeight,
815
+ y0 - perpY * innerWeight,
816
+ x0 + perpX * outerWeight,
817
+ y0 + perpY * outerWeight,
818
+ verts,
819
+ true
820
+ ) + 2;
821
+ } else if (style.cap === "square") {
822
+ indexCount += square(x0, y0, perpX, perpY, innerWeight, outerWeight, true, verts);
1097
823
  }
1098
824
  }
1099
- return scale * Number.parseFloat(string);
1100
- }
1101
-
1102
- const tempTransform0$1 = new Matrix3();
1103
- const tempTransform1$1 = new Matrix3();
1104
- const tempTransform2$1 = new Matrix3();
1105
- const tempTransform3 = new Matrix3();
1106
- function getNodeTransform(node, currentTransform, transformStack) {
1107
- if (!(node.hasAttribute("transform") || node.nodeName === "use" && (node.hasAttribute("x") || node.hasAttribute("y")))) {
1108
- return null;
1109
- }
1110
- const transform = parseNodeTransform(node);
1111
- if (transformStack.length > 0) {
1112
- transform.premultiply(transformStack[transformStack.length - 1]);
1113
- }
1114
- currentTransform.copy(transform);
1115
- transformStack.push(transform);
1116
- return transform;
1117
- }
1118
- function parseNodeTransform(node) {
1119
- const transform = new Matrix3();
1120
- const currentTransform = tempTransform0$1;
1121
- if (node.nodeName === "use" && (node.hasAttribute("x") || node.hasAttribute("y"))) {
1122
- transform.translate(
1123
- parseFloatWithUnits(node.getAttribute("x")),
1124
- parseFloatWithUnits(node.getAttribute("y"))
1125
- );
1126
- }
1127
- if (node.hasAttribute("transform")) {
1128
- const transformsTexts = node.getAttribute("transform").split(")");
1129
- for (let tIndex = transformsTexts.length - 1; tIndex >= 0; tIndex--) {
1130
- const transformText = transformsTexts[tIndex].trim();
1131
- if (transformText === "")
1132
- continue;
1133
- const openParPos = transformText.indexOf("(");
1134
- const closeParPos = transformText.length;
1135
- if (openParPos > 0 && openParPos < closeParPos) {
1136
- const transformType = transformText.slice(0, openParPos);
1137
- const array = parsePathDataArgs(transformText.slice(openParPos + 1));
1138
- currentTransform.identity();
1139
- switch (transformType) {
1140
- case "translate":
1141
- if (array.length >= 1) {
1142
- const tx = array[0];
1143
- let ty = 0;
1144
- if (array.length >= 2) {
1145
- ty = array[1];
1146
- }
1147
- currentTransform.translate(tx, ty);
1148
- }
1149
- break;
1150
- case "rotate":
1151
- if (array.length >= 1) {
1152
- let angle = 0;
1153
- let cx = 0;
1154
- let cy = 0;
1155
- angle = array[0] * Math.PI / 180;
1156
- if (array.length >= 3) {
1157
- cx = array[1];
1158
- cy = array[2];
1159
- }
1160
- tempTransform1$1.makeTranslation(-cx, -cy);
1161
- tempTransform2$1.makeRotation(angle);
1162
- tempTransform3.multiplyMatrices(tempTransform2$1, tempTransform1$1);
1163
- tempTransform1$1.makeTranslation(cx, cy);
1164
- currentTransform.multiplyMatrices(tempTransform1$1, tempTransform3);
1165
- }
1166
- break;
1167
- case "scale":
1168
- if (array.length >= 1) {
1169
- currentTransform.scale(
1170
- array[0],
1171
- array[1] ?? array[0]
1172
- );
1173
- }
1174
- break;
1175
- case "skewX":
1176
- if (array.length === 1) {
1177
- currentTransform.set(
1178
- 1,
1179
- Math.tan(array[0] * Math.PI / 180),
1180
- 0,
1181
- 0,
1182
- 1,
1183
- 0,
1184
- 0,
1185
- 0,
1186
- 1
1187
- );
1188
- }
1189
- break;
1190
- case "skewY":
1191
- if (array.length === 1) {
1192
- currentTransform.set(
1193
- 1,
1194
- 0,
1195
- 0,
1196
- Math.tan(array[0] * Math.PI / 180),
1197
- 1,
1198
- 0,
1199
- 0,
1200
- 0,
1201
- 1
1202
- );
1203
- }
1204
- break;
1205
- case "matrix":
1206
- if (array.length === 6) {
1207
- currentTransform.set(
1208
- array[0],
1209
- array[2],
1210
- array[4],
1211
- array[1],
1212
- array[3],
1213
- array[5],
1214
- 0,
1215
- 0,
1216
- 1
1217
- );
1218
- }
1219
- break;
825
+ verts.push(
826
+ x0 - perpX * innerWeight,
827
+ y0 - perpY * innerWeight
828
+ );
829
+ verts.push(
830
+ x0 + perpX * outerWeight,
831
+ y0 + perpY * outerWeight
832
+ );
833
+ for (let i = 1; i < length - 1; ++i) {
834
+ x0 = points[(i - 1) * 2];
835
+ y0 = points[(i - 1) * 2 + 1];
836
+ x1 = points[i * 2];
837
+ y1 = points[i * 2 + 1];
838
+ x2 = points[(i + 1) * 2];
839
+ y2 = points[(i + 1) * 2 + 1];
840
+ perpX = -(y0 - y1);
841
+ perpY = x0 - x1;
842
+ dist = Math.sqrt(perpX * perpX + perpY * perpY);
843
+ perpX /= dist;
844
+ perpY /= dist;
845
+ perpX *= width;
846
+ perpY *= width;
847
+ perp1x = -(y1 - y2);
848
+ perp1y = x1 - x2;
849
+ dist = Math.sqrt(perp1x * perp1x + perp1y * perp1y);
850
+ perp1x /= dist;
851
+ perp1y /= dist;
852
+ perp1x *= width;
853
+ perp1y *= width;
854
+ const dx0 = x1 - x0;
855
+ const dy0 = y0 - y1;
856
+ const dx1 = x1 - x2;
857
+ const dy1 = y2 - y1;
858
+ const dot = dx0 * dx1 + dy0 * dy1;
859
+ const cross = dy0 * dx1 - dy1 * dx0;
860
+ const clockwise = cross < 0;
861
+ if (Math.abs(cross) < 1e-3 * Math.abs(dot)) {
862
+ verts.push(
863
+ x1 - perpX * innerWeight,
864
+ y1 - perpY * innerWeight
865
+ );
866
+ verts.push(
867
+ x1 + perpX * outerWeight,
868
+ y1 + perpY * outerWeight
869
+ );
870
+ if (dot >= 0) {
871
+ if (style.join === "round") {
872
+ indexCount += round(
873
+ x1,
874
+ y1,
875
+ x1 - perpX * innerWeight,
876
+ y1 - perpY * innerWeight,
877
+ x1 - perp1x * innerWeight,
878
+ y1 - perp1y * innerWeight,
879
+ verts,
880
+ false
881
+ ) + 4;
882
+ } else {
883
+ indexCount += 2;
1220
884
  }
885
+ verts.push(
886
+ x1 - perp1x * outerWeight,
887
+ y1 - perp1y * outerWeight
888
+ );
889
+ verts.push(
890
+ x1 + perp1x * innerWeight,
891
+ y1 + perp1y * innerWeight
892
+ );
1221
893
  }
1222
- transform.premultiply(currentTransform);
1223
- }
1224
- }
1225
- return transform;
1226
- }
1227
-
1228
- function parseCircleNode(node) {
1229
- return new Path2D().arc(
1230
- parseFloatWithUnits(node.getAttribute("cx") || 0),
1231
- parseFloatWithUnits(node.getAttribute("cy") || 0),
1232
- parseFloatWithUnits(node.getAttribute("r") || 0),
1233
- 0,
1234
- Math.PI * 2
1235
- );
1236
- }
1237
-
1238
- function parseCSSStylesheet(node, stylesheets) {
1239
- if (!node.sheet || !node.sheet.cssRules || !node.sheet.cssRules.length)
1240
- return;
1241
- for (let i = 0; i < node.sheet.cssRules.length; i++) {
1242
- const stylesheet = node.sheet.cssRules[i];
1243
- if (stylesheet.type !== 1)
1244
894
  continue;
1245
- const selectorList = stylesheet.selectorText.split(/,/g).filter(Boolean).map((i2) => i2.trim());
1246
- const definitions = {};
1247
- for (let len = stylesheet.style.length, i2 = 0; i2 < len; i2++) {
1248
- const name = stylesheet.style.item(i2);
1249
- definitions[name] = stylesheet.style.getPropertyValue(name);
1250
- }
1251
- for (let j = 0; j < selectorList.length; j++) {
1252
- stylesheets[selectorList[j]] = Object.assign(
1253
- stylesheets[selectorList[j]] || {},
1254
- { ...definitions }
1255
- );
1256
895
  }
1257
- }
1258
- }
1259
-
1260
- function parseEllipseNode(node) {
1261
- return new Path2D().ellipse(
1262
- parseFloatWithUnits(node.getAttribute("cx") || 0),
1263
- parseFloatWithUnits(node.getAttribute("cy") || 0),
1264
- parseFloatWithUnits(node.getAttribute("rx") || 0),
1265
- parseFloatWithUnits(node.getAttribute("ry") || 0),
1266
- 0,
1267
- 0,
1268
- Math.PI * 2
1269
- );
1270
- }
1271
-
1272
- function parseLineNode(node) {
1273
- return new Path2D().moveTo(
1274
- parseFloatWithUnits(node.getAttribute("x1") || 0),
1275
- parseFloatWithUnits(node.getAttribute("y1") || 0)
1276
- ).lineTo(
1277
- parseFloatWithUnits(node.getAttribute("x2") || 0),
1278
- parseFloatWithUnits(node.getAttribute("y2") || 0)
1279
- );
1280
- }
1281
-
1282
- function parsePathNode(node) {
1283
- const path = new Path2D();
1284
- const d = node.getAttribute("d");
1285
- if (!d || d === "none")
1286
- return null;
1287
- path.addData(d);
1288
- return path;
1289
- }
1290
-
1291
- const RE$1 = /([+-]?(?:\d+(?:\.\d+)?|\.\d+)(?:e[+-]?\d+)?)(?:,|\s)([+-]?\d*\.?\d+(?:e[+-]?\d+)?)/g;
1292
- function parsePolygonNode(node) {
1293
- const path = new Path2D();
1294
- let index = 0;
1295
- node.getAttribute("points")?.replace(RE$1, (match, a, b) => {
1296
- const x = parseFloatWithUnits(a);
1297
- const y = parseFloatWithUnits(b);
1298
- if (index === 0) {
1299
- path.moveTo(x, y);
1300
- } else {
1301
- path.lineTo(x, y);
1302
- }
1303
- index++;
1304
- return match;
1305
- });
1306
- path.currentCurve.autoClose = true;
1307
- return path;
1308
- }
1309
-
1310
- const RE = /([+-]?(?:\d+(?:\.\d+)?|\.\d+)(?:e[+-]?\d+)?)(?:,|\s)([+-]?\d*\.?\d+(?:e[+-]?\d+)?)/g;
1311
- function parsePolylineNode(node) {
1312
- const path = new Path2D();
1313
- let index = 0;
1314
- node.getAttribute("points")?.replace(RE, (match, a, b) => {
1315
- const x = parseFloatWithUnits(a);
1316
- const y = parseFloatWithUnits(b);
1317
- if (index === 0) {
1318
- path.moveTo(x, y);
896
+ const c1 = (-perpX + x0) * (-perpY + y1) - (-perpX + x1) * (-perpY + y0);
897
+ const c2 = (-perp1x + x2) * (-perp1y + y1) - (-perp1x + x1) * (-perp1y + y2);
898
+ const px = (dx0 * c2 - dx1 * c1) / cross;
899
+ const py = (dy1 * c1 - dy0 * c2) / cross;
900
+ const pDist = (px - x1) * (px - x1) + (py - y1) * (py - y1);
901
+ const imx = x1 + (px - x1) * innerWeight;
902
+ const imy = y1 + (py - y1) * innerWeight;
903
+ const omx = x1 - (px - x1) * outerWeight;
904
+ const omy = y1 - (py - y1) * outerWeight;
905
+ const smallerInsideSegmentSq = Math.min(dx0 * dx0 + dy0 * dy0, dx1 * dx1 + dy1 * dy1);
906
+ const insideWeight = clockwise ? innerWeight : outerWeight;
907
+ const smallerInsideDiagonalSq = smallerInsideSegmentSq + insideWeight * insideWeight * widthSquared;
908
+ const insideMiterOk = pDist <= smallerInsideDiagonalSq;
909
+ if (insideMiterOk) {
910
+ if (style.join === "bevel" || pDist / widthSquared > miterLimitSquared) {
911
+ if (clockwise) {
912
+ verts.push(imx, imy);
913
+ verts.push(x1 + perpX * outerWeight, y1 + perpY * outerWeight);
914
+ verts.push(imx, imy);
915
+ verts.push(x1 + perp1x * outerWeight, y1 + perp1y * outerWeight);
916
+ } else {
917
+ verts.push(x1 - perpX * innerWeight, y1 - perpY * innerWeight);
918
+ verts.push(omx, omy);
919
+ verts.push(x1 - perp1x * innerWeight, y1 - perp1y * innerWeight);
920
+ verts.push(omx, omy);
921
+ }
922
+ indexCount += 2;
923
+ } else if (style.join === "round") {
924
+ if (clockwise) {
925
+ verts.push(imx, imy);
926
+ verts.push(x1 + perpX * outerWeight, y1 + perpY * outerWeight);
927
+ indexCount += round(
928
+ x1,
929
+ y1,
930
+ x1 + perpX * outerWeight,
931
+ y1 + perpY * outerWeight,
932
+ x1 + perp1x * outerWeight,
933
+ y1 + perp1y * outerWeight,
934
+ verts,
935
+ true
936
+ ) + 4;
937
+ verts.push(imx, imy);
938
+ verts.push(x1 + perp1x * outerWeight, y1 + perp1y * outerWeight);
939
+ } else {
940
+ verts.push(x1 - perpX * innerWeight, y1 - perpY * innerWeight);
941
+ verts.push(omx, omy);
942
+ indexCount += round(
943
+ x1,
944
+ y1,
945
+ x1 - perpX * innerWeight,
946
+ y1 - perpY * innerWeight,
947
+ x1 - perp1x * innerWeight,
948
+ y1 - perp1y * innerWeight,
949
+ verts,
950
+ false
951
+ ) + 4;
952
+ verts.push(x1 - perp1x * innerWeight, y1 - perp1y * innerWeight);
953
+ verts.push(omx, omy);
954
+ }
955
+ } else {
956
+ verts.push(imx, imy);
957
+ verts.push(omx, omy);
958
+ }
1319
959
  } else {
1320
- path.lineTo(x, y);
960
+ verts.push(x1 - perpX * innerWeight, y1 - perpY * innerWeight);
961
+ verts.push(x1 + perpX * outerWeight, y1 + perpY * outerWeight);
962
+ if (style.join === "round") {
963
+ if (clockwise) {
964
+ indexCount += round(
965
+ x1,
966
+ y1,
967
+ x1 + perpX * outerWeight,
968
+ y1 + perpY * outerWeight,
969
+ x1 + perp1x * outerWeight,
970
+ y1 + perp1y * outerWeight,
971
+ verts,
972
+ true
973
+ ) + 2;
974
+ } else {
975
+ indexCount += round(
976
+ x1,
977
+ y1,
978
+ x1 - perpX * innerWeight,
979
+ y1 - perpY * innerWeight,
980
+ x1 - perp1x * innerWeight,
981
+ y1 - perp1y * innerWeight,
982
+ verts,
983
+ false
984
+ ) + 2;
985
+ }
986
+ } else if (style.join === "miter" && pDist / widthSquared <= miterLimitSquared) {
987
+ if (clockwise) {
988
+ verts.push(omx, omy);
989
+ verts.push(omx, omy);
990
+ } else {
991
+ verts.push(imx, imy);
992
+ verts.push(imx, imy);
993
+ }
994
+ indexCount += 2;
995
+ }
996
+ verts.push(x1 - perp1x * innerWeight, y1 - perp1y * innerWeight);
997
+ verts.push(x1 + perp1x * outerWeight, y1 + perp1y * outerWeight);
998
+ indexCount += 2;
1321
999
  }
1322
- index++;
1323
- return match;
1324
- });
1325
- path.currentCurve.autoClose = false;
1326
- return path;
1327
- }
1328
-
1329
- function parseRectNode(node) {
1330
- const x = parseFloatWithUnits(node.getAttribute("x") || 0);
1331
- const y = parseFloatWithUnits(node.getAttribute("y") || 0);
1332
- const rx = parseFloatWithUnits(node.getAttribute("rx") || node.getAttribute("ry") || 0);
1333
- const ry = parseFloatWithUnits(node.getAttribute("ry") || node.getAttribute("rx") || 0);
1334
- const w = parseFloatWithUnits(node.getAttribute("width"));
1335
- const h = parseFloatWithUnits(node.getAttribute("height"));
1336
- const bci = 1 - 0.551915024494;
1337
- const path = new Path2D();
1338
- path.moveTo(x + rx, y);
1339
- path.lineTo(x + w - rx, y);
1340
- if (rx !== 0 || ry !== 0) {
1341
- path.bezierCurveTo(
1342
- x + w - rx * bci,
1343
- y,
1344
- x + w,
1345
- y + ry * bci,
1346
- x + w,
1347
- y + ry
1348
- );
1349
- }
1350
- path.lineTo(x + w, y + h - ry);
1351
- if (rx !== 0 || ry !== 0) {
1352
- path.bezierCurveTo(
1353
- x + w,
1354
- y + h - ry * bci,
1355
- x + w - rx * bci,
1356
- y + h,
1357
- x + w - rx,
1358
- y + h
1359
- );
1360
1000
  }
1361
- path.lineTo(x + rx, y + h);
1362
- if (rx !== 0 || ry !== 0) {
1363
- path.bezierCurveTo(
1364
- x + rx * bci,
1365
- y + h,
1366
- x,
1367
- y + h - ry * bci,
1368
- x,
1369
- y + h - ry
1370
- );
1001
+ x0 = points[(length - 2) * 2];
1002
+ y0 = points[(length - 2) * 2 + 1];
1003
+ x1 = points[(length - 1) * 2];
1004
+ y1 = points[(length - 1) * 2 + 1];
1005
+ perpX = -(y0 - y1);
1006
+ perpY = x0 - x1;
1007
+ dist = Math.sqrt(perpX * perpX + perpY * perpY);
1008
+ perpX /= dist;
1009
+ perpY /= dist;
1010
+ perpX *= width;
1011
+ perpY *= width;
1012
+ verts.push(x1 - perpX * innerWeight, y1 - perpY * innerWeight);
1013
+ verts.push(x1 + perpX * outerWeight, y1 + perpY * outerWeight);
1014
+ if (!closedShape) {
1015
+ if (style.cap === "round") {
1016
+ indexCount += round(
1017
+ x1 - perpX * (innerWeight - outerWeight) * 0.5,
1018
+ y1 - perpY * (innerWeight - outerWeight) * 0.5,
1019
+ x1 - perpX * innerWeight,
1020
+ y1 - perpY * innerWeight,
1021
+ x1 + perpX * outerWeight,
1022
+ y1 + perpY * outerWeight,
1023
+ verts,
1024
+ false
1025
+ ) + 2;
1026
+ } else if (style.cap === "square") {
1027
+ indexCount += square(x1, y1, perpX, perpY, innerWeight, outerWeight, false, verts);
1028
+ }
1371
1029
  }
1372
- path.lineTo(x, y + ry);
1373
- if (rx !== 0 || ry !== 0) {
1374
- path.bezierCurveTo(x, y + ry * bci, x + rx * bci, y, x + rx, y);
1030
+ const eps2 = curveEps * curveEps;
1031
+ for (let i = indexStart; i < indexCount + indexStart - 2; ++i) {
1032
+ x0 = verts[i * 2];
1033
+ y0 = verts[i * 2 + 1];
1034
+ x1 = verts[(i + 1) * 2];
1035
+ y1 = verts[(i + 1) * 2 + 1];
1036
+ x2 = verts[(i + 2) * 2];
1037
+ y2 = verts[(i + 2) * 2 + 1];
1038
+ if (Math.abs(x0 * (y1 - y2) + x1 * (y2 - y0) + x2 * (y0 - y1)) < eps2) {
1039
+ continue;
1040
+ }
1041
+ indices.push(i, i + 1, i + 2);
1375
1042
  }
1376
- return path;
1043
+ return {
1044
+ vertices,
1045
+ indices
1046
+ };
1377
1047
  }
1378
-
1379
- function parseStyle(node, style, stylesheets) {
1380
- style = Object.assign({}, style);
1381
- let stylesheetStyles = {};
1382
- if (node.hasAttribute("class")) {
1383
- const classSelectors = node.getAttribute("class").split(/\s/).filter(Boolean).map((i) => i.trim());
1384
- for (let i = 0; i < classSelectors.length; i++) {
1385
- stylesheetStyles = Object.assign(stylesheetStyles, stylesheets[`.${classSelectors[i]}`]);
1386
- }
1048
+ function getOrientationOfPoints(points) {
1049
+ const m = points.length;
1050
+ if (m < 6) {
1051
+ return 1;
1387
1052
  }
1388
- if (node.hasAttribute("id")) {
1389
- stylesheetStyles = Object.assign(stylesheetStyles, stylesheets[`#${node.getAttribute("id")}`]);
1053
+ let area = 0;
1054
+ for (let i = 0, x1 = points[m - 2], y1 = points[m - 1]; i < m; i += 2) {
1055
+ const x2 = points[i];
1056
+ const y2 = points[i + 1];
1057
+ area += (x2 - x1) * (y2 + y1);
1058
+ x1 = x2;
1059
+ y1 = y2;
1390
1060
  }
1391
- for (let len = node.style.length, i = 0; i < len; i++) {
1392
- const name = node.style.item(i);
1393
- const value = node.style.getPropertyValue(name);
1394
- style[name] = value;
1395
- stylesheetStyles[name] = value;
1061
+ if (area < 0) {
1062
+ return -1;
1396
1063
  }
1397
- function addStyle(svgName, jsName, adjustFunction = copy) {
1398
- if (node.hasAttribute(svgName))
1399
- style[jsName] = adjustFunction(node.getAttribute(svgName));
1400
- if (stylesheetStyles[svgName])
1401
- style[jsName] = adjustFunction(stylesheetStyles[svgName]);
1402
- }
1403
- function copy(v) {
1404
- if (v.startsWith("url"))
1405
- console.warn("url access in attributes is not implemented.");
1406
- return v;
1407
- }
1408
- function clamp(v) {
1409
- return Math.max(0, Math.min(1, parseFloatWithUnits(v)));
1064
+ return 1;
1065
+ }
1066
+ function square(x, y, nx, ny, innerWeight, outerWeight, clockwise, verts) {
1067
+ const ix = x - nx * innerWeight;
1068
+ const iy = y - ny * innerWeight;
1069
+ const ox = x + nx * outerWeight;
1070
+ const oy = y + ny * outerWeight;
1071
+ let exx;
1072
+ let eyy;
1073
+ if (clockwise) {
1074
+ exx = ny;
1075
+ eyy = -nx;
1076
+ } else {
1077
+ exx = -ny;
1078
+ eyy = nx;
1410
1079
  }
1411
- function positive(v) {
1412
- return Math.max(0, parseFloatWithUnits(v));
1080
+ const eix = ix + exx;
1081
+ const eiy = iy + eyy;
1082
+ const eox = ox + exx;
1083
+ const eoy = oy + eyy;
1084
+ verts.push(eix, eiy);
1085
+ verts.push(eox, eoy);
1086
+ return 2;
1087
+ }
1088
+ function round(cx, cy, sx, sy, ex, ey, verts, clockwise) {
1089
+ const cx2p0x = sx - cx;
1090
+ const cy2p0y = sy - cy;
1091
+ let angle0 = Math.atan2(cx2p0x, cy2p0y);
1092
+ let angle1 = Math.atan2(ex - cx, ey - cy);
1093
+ if (clockwise && angle0 < angle1) {
1094
+ angle0 += Math.PI * 2;
1095
+ } else if (!clockwise && angle0 > angle1) {
1096
+ angle1 += Math.PI * 2;
1413
1097
  }
1414
- function array(v) {
1415
- return v.split(" ").filter((v2) => v2 !== "").map((v2) => parseFloatWithUnits(v2));
1098
+ let startAngle = angle0;
1099
+ const angleDiff = angle1 - angle0;
1100
+ const absAngleDiff = Math.abs(angleDiff);
1101
+ const radius = Math.sqrt(cx2p0x * cx2p0x + cy2p0y * cy2p0y);
1102
+ const segCount = (15 * absAngleDiff * Math.sqrt(radius) / Math.PI >> 0) + 1;
1103
+ const angleInc = angleDiff / segCount;
1104
+ startAngle += angleInc;
1105
+ if (clockwise) {
1106
+ verts.push(cx, cy);
1107
+ verts.push(sx, sy);
1108
+ for (let i = 1, angle = startAngle; i < segCount; i++, angle += angleInc) {
1109
+ verts.push(cx, cy);
1110
+ verts.push(cx + Math.sin(angle) * radius, cy + Math.cos(angle) * radius);
1111
+ }
1112
+ verts.push(cx, cy);
1113
+ verts.push(ex, ey);
1114
+ } else {
1115
+ verts.push(sx, sy);
1116
+ verts.push(cx, cy);
1117
+ for (let i = 1, angle = startAngle; i < segCount; i++, angle += angleInc) {
1118
+ verts.push(cx + Math.sin(angle) * radius, cy + Math.cos(angle) * radius);
1119
+ verts.push(cx, cy);
1120
+ }
1121
+ verts.push(ex, ey);
1122
+ verts.push(cx, cy);
1416
1123
  }
1417
- addStyle("fill", "fill");
1418
- addStyle("fill-opacity", "fillOpacity", clamp);
1419
- addStyle("fill-rule", "fillRule");
1420
- addStyle("opacity", "opacity", clamp);
1421
- addStyle("stroke", "stroke");
1422
- addStyle("stroke-opacity", "strokeOpacity", clamp);
1423
- addStyle("stroke-width", "strokeWidth", positive);
1424
- addStyle("stroke-linecap", "strokeLinecap");
1425
- addStyle("stroke-linejoin", "strokeLinejoin");
1426
- addStyle("stroke-miterlimit", "strokeMiterlimit", positive);
1427
- addStyle("stroke-dasharray", "strokeDasharray", array);
1428
- addStyle("stroke-dashoffset", "strokeDashoffset", parseFloatWithUnits);
1429
- addStyle("visibility", "visibility");
1430
- return style;
1124
+ return segCount * 2;
1431
1125
  }
1432
1126
 
1433
- function parseNode(node, style, paths = [], stylesheets = {}) {
1434
- if (node.nodeType !== 1)
1435
- return paths;
1436
- let isDefsNode = false;
1437
- let path = null;
1438
- let _style = { ...style };
1439
- switch (node.nodeName) {
1440
- case "svg":
1441
- _style = parseStyle(node, _style, stylesheets);
1442
- break;
1443
- case "style":
1444
- parseCSSStylesheet(node, stylesheets);
1445
- break;
1446
- case "g":
1447
- _style = parseStyle(node, _style, stylesheets);
1448
- break;
1449
- case "path":
1450
- _style = parseStyle(node, _style, stylesheets);
1451
- if (node.hasAttribute("d"))
1452
- path = parsePathNode(node);
1453
- break;
1454
- case "rect":
1455
- _style = parseStyle(node, _style, stylesheets);
1456
- path = parseRectNode(node);
1457
- break;
1458
- case "polygon":
1459
- _style = parseStyle(node, _style, stylesheets);
1460
- path = parsePolygonNode(node);
1461
- break;
1462
- case "polyline":
1463
- _style = parseStyle(node, _style, stylesheets);
1464
- path = parsePolylineNode(node);
1465
- break;
1466
- case "circle":
1467
- _style = parseStyle(node, _style, stylesheets);
1468
- path = parseCircleNode(node);
1469
- break;
1470
- case "ellipse":
1471
- _style = parseStyle(node, _style, stylesheets);
1472
- path = parseEllipseNode(node);
1473
- break;
1474
- case "line":
1475
- _style = parseStyle(node, _style, stylesheets);
1476
- path = parseLineNode(node);
1477
- break;
1478
- case "defs":
1479
- isDefsNode = true;
1480
- break;
1481
- case "use": {
1482
- _style = parseStyle(node, _style, stylesheets);
1483
- const href = node.getAttributeNS("http://www.w3.org/1999/xlink", "href") || node.getAttribute("href") || "";
1484
- const usedNodeId = href.substring(1);
1485
- const usedNode = node.viewportElement?.getElementById(usedNodeId);
1486
- if (usedNode) {
1487
- parseNode(usedNode, _style, paths, stylesheets);
1488
- } else {
1489
- console.warn(`'use node' references non-existent node id: ${usedNodeId}`);
1490
- }
1491
- break;
1127
+ class Transform2D {
1128
+ constructor(a = 1, b = 0, c = 0, d = 1, tx = 0, ty = 0) {
1129
+ this.a = a;
1130
+ this.b = b;
1131
+ this.c = c;
1132
+ this.d = d;
1133
+ this.tx = tx;
1134
+ this.ty = ty;
1135
+ }
1136
+ _array;
1137
+ set(a, b, c, d, tx, ty) {
1138
+ this.a = a;
1139
+ this.b = b;
1140
+ this.c = c;
1141
+ this.d = d;
1142
+ this.tx = tx;
1143
+ this.ty = ty;
1144
+ return this;
1145
+ }
1146
+ append(t2d) {
1147
+ const a1 = this.a;
1148
+ const b1 = this.b;
1149
+ const c1 = this.c;
1150
+ const d1 = this.d;
1151
+ this.a = t2d.a * a1 + t2d.b * c1;
1152
+ this.b = t2d.a * b1 + t2d.b * d1;
1153
+ this.c = t2d.c * a1 + t2d.d * c1;
1154
+ this.d = t2d.c * b1 + t2d.d * d1;
1155
+ this.tx = t2d.tx * a1 + t2d.ty * c1 + this.tx;
1156
+ this.ty = t2d.tx * b1 + t2d.ty * d1 + this.ty;
1157
+ return this;
1158
+ }
1159
+ appendFrom(a, b) {
1160
+ const a1 = a.a;
1161
+ const b1 = a.b;
1162
+ const c1 = a.c;
1163
+ const d1 = a.d;
1164
+ const tx = a.tx;
1165
+ const ty = a.ty;
1166
+ const a2 = b.a;
1167
+ const b2 = b.b;
1168
+ const c2 = b.c;
1169
+ const d2 = b.d;
1170
+ this.a = a1 * a2 + b1 * c2;
1171
+ this.b = a1 * b2 + b1 * d2;
1172
+ this.c = c1 * a2 + d1 * c2;
1173
+ this.d = c1 * b2 + d1 * d2;
1174
+ this.tx = tx * a2 + ty * c2 + b.tx;
1175
+ this.ty = tx * b2 + ty * d2 + b.ty;
1176
+ return this;
1177
+ }
1178
+ setTransform(x, y, pivotX, pivotY, scaleX, scaleY, rotation, skewX, skewY) {
1179
+ this.a = Math.cos(rotation + skewY) * scaleX;
1180
+ this.b = Math.sin(rotation + skewY) * scaleX;
1181
+ this.c = -Math.sin(rotation - skewX) * scaleY;
1182
+ this.d = Math.cos(rotation - skewX) * scaleY;
1183
+ this.tx = x - (pivotX * this.a + pivotY * this.c);
1184
+ this.ty = y - (pivotX * this.b + pivotY * this.d);
1185
+ return this;
1186
+ }
1187
+ prepend(t2d) {
1188
+ const tx1 = this.tx;
1189
+ if (t2d.a !== 1 || t2d.b !== 0 || t2d.c !== 0 || t2d.d !== 1) {
1190
+ const a1 = this.a;
1191
+ const c1 = this.c;
1192
+ this.a = a1 * t2d.a + this.b * t2d.c;
1193
+ this.b = a1 * t2d.b + this.b * t2d.d;
1194
+ this.c = c1 * t2d.a + this.d * t2d.c;
1195
+ this.d = c1 * t2d.b + this.d * t2d.d;
1492
1196
  }
1493
- default:
1494
- console.warn(node);
1495
- break;
1197
+ this.tx = tx1 * t2d.a + this.ty * t2d.c + t2d.tx;
1198
+ this.ty = tx1 * t2d.b + this.ty * t2d.d + t2d.ty;
1199
+ return this;
1496
1200
  }
1497
- if (_style.display === "none") {
1498
- return paths;
1201
+ _skewX;
1202
+ _skewY;
1203
+ _skewXTan = 1;
1204
+ _skewYTan = 1;
1205
+ skewX(x) {
1206
+ return this.skew(x, 0);
1499
1207
  }
1500
- const currentTransform = new Matrix3();
1501
- const transformStack = [];
1502
- const transform = getNodeTransform(node, currentTransform, transformStack);
1503
- if (path) {
1504
- path.applyTransform(currentTransform);
1505
- paths.push(path);
1506
- path.style = { ..._style };
1208
+ skewY(y) {
1209
+ return this.skew(0, y);
1507
1210
  }
1508
- const childNodes = node.childNodes;
1509
- for (let i = 0, len = childNodes.length; i < len; i++) {
1510
- const node2 = childNodes[i];
1511
- if (isDefsNode && node2.nodeName !== "style" && node2.nodeName !== "defs")
1512
- continue;
1513
- parseNode(node2, _style, paths, stylesheets);
1211
+ skew(x, y) {
1212
+ if (x !== this._skewX) {
1213
+ this._skewX = x;
1214
+ this._skewXTan = Math.tan(x);
1215
+ }
1216
+ if (y !== this._skewY) {
1217
+ this._skewY = y;
1218
+ this._skewYTan = Math.tan(y);
1219
+ }
1220
+ this.b *= this._skewXTan;
1221
+ this.c *= this._skewYTan;
1222
+ return this;
1514
1223
  }
1515
- if (transform) {
1516
- transformStack.pop();
1517
- if (transformStack.length > 0) {
1518
- currentTransform.copy(transformStack[transformStack.length - 1]);
1224
+ translateX(x) {
1225
+ return this.translate(x, 0);
1226
+ }
1227
+ translateY(y) {
1228
+ return this.translate(0, y);
1229
+ }
1230
+ translateZ(z) {
1231
+ return this.translate(0, 0, z);
1232
+ }
1233
+ translate3d(x, y, z) {
1234
+ return this.translate(x, y, z);
1235
+ }
1236
+ translate(x, y, _z = 0) {
1237
+ this.tx += x;
1238
+ this.ty += y;
1239
+ return this;
1240
+ }
1241
+ scaleX(x) {
1242
+ return this.scale(x, 1);
1243
+ }
1244
+ scaleY(y) {
1245
+ return this.scale(1, y);
1246
+ }
1247
+ scale3d(x, y, z = 1) {
1248
+ return this.scale(x, y, z);
1249
+ }
1250
+ scale(x, y, _z = 1) {
1251
+ this.a *= x;
1252
+ this.d *= y;
1253
+ this.c *= x;
1254
+ this.b *= y;
1255
+ this.tx *= x;
1256
+ this.ty *= y;
1257
+ return this;
1258
+ }
1259
+ rotateX(x) {
1260
+ return this.scaleY(this._rotateToScale(x));
1261
+ }
1262
+ rotateY(y) {
1263
+ return this.scaleX(this._rotateToScale(y));
1264
+ }
1265
+ rotateZ(z) {
1266
+ return this.rotate(z);
1267
+ }
1268
+ rotate(angle) {
1269
+ const cos = Math.cos(angle);
1270
+ const sin = Math.sin(angle);
1271
+ const a1 = this.a;
1272
+ const c1 = this.c;
1273
+ const tx1 = this.tx;
1274
+ this.a = a1 * cos - this.b * sin;
1275
+ this.b = a1 * sin + this.b * cos;
1276
+ this.c = c1 * cos - this.d * sin;
1277
+ this.d = c1 * sin + this.d * cos;
1278
+ this.tx = tx1 * cos - this.ty * sin;
1279
+ this.ty = tx1 * sin + this.ty * cos;
1280
+ return this;
1281
+ }
1282
+ rotate3d(x, y, z, rad) {
1283
+ const [rx, ry, rz] = this._rotate3d(x, y, z, rad);
1284
+ rx && this.rotateX(rx);
1285
+ ry && this.rotateY(ry);
1286
+ rz && this.rotateZ(rz);
1287
+ return this;
1288
+ }
1289
+ _rotateToScale(rad) {
1290
+ const val = rad / PI_2;
1291
+ return val <= 0.5 ? val * -4 + 1 : (val - 1) * 4 + 1;
1292
+ }
1293
+ _rotate3d(x, y, z, rad) {
1294
+ if (x === 1 && y === 0 && z === 0) {
1295
+ return [rad, 0, 0];
1296
+ } else if (x === 0 && y === 1 && z === 0) {
1297
+ return [0, rad, 0];
1298
+ } else if (x === 0 && y === 0) {
1299
+ return [0, 0, rad];
1519
1300
  } else {
1520
- currentTransform.identity();
1301
+ const cos = Math.cos(rad);
1302
+ const sin = Math.sin(rad);
1303
+ const m11 = cos + x * x * (1 - cos);
1304
+ const m12 = x * y * (1 - cos) - z * sin;
1305
+ const m13 = x * z * (1 - cos) + y * sin;
1306
+ const m22 = cos + y * y * (1 - cos);
1307
+ const m23 = y * z * (1 - cos) - x * sin;
1308
+ const m33 = cos + z * z * (1 - cos);
1309
+ const rotateX = -Math.atan2(-m23, m22);
1310
+ const rotateY = -Math.atan2(m13, Math.sqrt(m23 * m23 + m33 * m33));
1311
+ const rotateZ = -Math.atan2(-m12, m11);
1312
+ return [rotateX, rotateY, rotateZ];
1521
1313
  }
1522
1314
  }
1523
- return paths;
1315
+ decompose(pivot = { x: 0, y: 0 }, output = {
1316
+ position: { x: 0, y: 0 },
1317
+ scale: { x: 0, y: 0 },
1318
+ skew: { x: 0, y: 0 },
1319
+ rotation: 0
1320
+ }) {
1321
+ const { a, b, c, d, tx, ty } = this;
1322
+ const skewX = -Math.atan2(-c, d);
1323
+ const skewY = Math.atan2(b, a);
1324
+ const delta = Math.abs(skewX + skewY);
1325
+ if (delta < 1e-5 || Math.abs(PI_2 - delta) < 1e-5) {
1326
+ output.rotation = skewY;
1327
+ output.skew.x = output.skew.y = 0;
1328
+ } else {
1329
+ output.rotation = 0;
1330
+ output.skew.x = skewX;
1331
+ output.skew.y = skewY;
1332
+ }
1333
+ output.scale.x = Math.sqrt(a * a + b * b);
1334
+ output.scale.y = Math.sqrt(c * c + d * d);
1335
+ output.position.x = tx + (pivot.x * a + pivot.y * c);
1336
+ output.position.y = ty + (pivot.x * b + pivot.y * d);
1337
+ return output;
1338
+ }
1339
+ apply(pos, newPos) {
1340
+ newPos = newPos || new Vector2();
1341
+ const { x, y } = pos;
1342
+ newPos.x = this.a * x + this.c * y + this.tx;
1343
+ newPos.y = this.b * x + this.d * y + this.ty;
1344
+ return newPos;
1345
+ }
1346
+ affineInvert() {
1347
+ const a1 = this.a;
1348
+ const b1 = this.b;
1349
+ const c1 = this.c;
1350
+ const d1 = this.d;
1351
+ const tx1 = this.tx;
1352
+ const n = a1 * d1 - b1 * c1;
1353
+ this.a = d1 / n;
1354
+ this.b = -b1 / n;
1355
+ this.c = -c1 / n;
1356
+ this.d = a1 / n;
1357
+ this.tx = (c1 * this.ty - d1 * tx1) / n;
1358
+ this.ty = -(a1 * this.ty - b1 * tx1) / n;
1359
+ return this;
1360
+ }
1361
+ affineInverse() {
1362
+ return this.clone().affineInvert();
1363
+ }
1364
+ applyAffineInverse(pos, newPos) {
1365
+ newPos = newPos || new Vector2();
1366
+ const { a, b, c, d, tx, ty } = this;
1367
+ const id = 1 / (a * d + c * -b);
1368
+ const x = pos.x;
1369
+ const y = pos.y;
1370
+ newPos.x = d * id * x + -c * id * y + (ty * c - tx * d) * id;
1371
+ newPos.y = a * id * y + -b * id * x + (-ty * a + tx * b) * id;
1372
+ return newPos;
1373
+ }
1374
+ identity() {
1375
+ this.a = 1;
1376
+ this.b = 0;
1377
+ this.c = 0;
1378
+ this.d = 1;
1379
+ this.tx = 0;
1380
+ this.ty = 0;
1381
+ return this;
1382
+ }
1383
+ isIdentity() {
1384
+ const { a, b, c, d, tx, ty } = this;
1385
+ return a === 1 && b === 0 && c === 0 && d === 1 && tx === 0 && ty === 0;
1386
+ }
1387
+ copyTo(t2d) {
1388
+ t2d.a = this.a;
1389
+ t2d.b = this.b;
1390
+ t2d.c = this.c;
1391
+ t2d.d = this.d;
1392
+ t2d.tx = this.tx;
1393
+ t2d.ty = this.ty;
1394
+ return t2d;
1395
+ }
1396
+ copyFrom(t2d) {
1397
+ this.a = t2d.a;
1398
+ this.b = t2d.b;
1399
+ this.c = t2d.c;
1400
+ this.d = t2d.d;
1401
+ this.tx = t2d.tx;
1402
+ this.ty = t2d.ty;
1403
+ return this;
1404
+ }
1405
+ equals(t2d) {
1406
+ return t2d.a === this.a && t2d.b === this.b && t2d.c === this.c && t2d.d === this.d && t2d.tx === this.tx && t2d.ty === this.ty;
1407
+ }
1408
+ appendCssTransform(cssTransform, ctx = {}) {
1409
+ const { width = 1, height = 1 } = ctx;
1410
+ const output = new Transform2D();
1411
+ parseCssFunctions(cssTransform, { width, height }).reverse().forEach(({ name, args }) => {
1412
+ const values = args.map((arg) => arg.normalizedIntValue);
1413
+ switch (name) {
1414
+ case "translate":
1415
+ output.translate(values[0] * width, (values[1] ?? values[0]) * height);
1416
+ break;
1417
+ case "translateX":
1418
+ output.translateX(values[0] * width);
1419
+ break;
1420
+ case "translateY":
1421
+ output.translateY(values[0] * height);
1422
+ break;
1423
+ case "translateZ":
1424
+ output.translateZ(values[0]);
1425
+ break;
1426
+ case "translate3d":
1427
+ output.translate3d(
1428
+ values[0] * width,
1429
+ (values[1] ?? values[0]) * height,
1430
+ values[2] ?? values[1] ?? values[0]
1431
+ );
1432
+ break;
1433
+ case "scale":
1434
+ output.scale(values[0], values[1] ?? values[0]);
1435
+ break;
1436
+ case "scaleX":
1437
+ output.scaleX(values[0]);
1438
+ break;
1439
+ case "scaleY":
1440
+ output.scaleY(values[0]);
1441
+ break;
1442
+ case "scale3d":
1443
+ output.scale3d(values[0], values[1] ?? values[0], values[2] ?? values[1] ?? values[0]);
1444
+ break;
1445
+ case "rotate":
1446
+ output.rotate(values[0] * PI_2);
1447
+ break;
1448
+ case "rotateX":
1449
+ output.rotateX(values[0] * PI_2);
1450
+ break;
1451
+ case "rotateY":
1452
+ output.rotateY(values[0] * PI_2);
1453
+ break;
1454
+ case "rotateZ":
1455
+ output.rotateZ(values[0] * PI_2);
1456
+ break;
1457
+ case "rotate3d":
1458
+ output.rotate3d(
1459
+ values[0] * PI_2,
1460
+ (values[1] ?? values[0]) * PI_2,
1461
+ (values[2] ?? values[1] ?? values[0]) * PI_2,
1462
+ (values[3] ?? values[2] ?? values[1] ?? values[0]) * PI_2
1463
+ );
1464
+ break;
1465
+ case "skew":
1466
+ output.skew(values[0], values[0] ?? values[1]);
1467
+ break;
1468
+ case "skewX":
1469
+ output.skewX(values[0]);
1470
+ break;
1471
+ case "skewY":
1472
+ output.skewY(values[0]);
1473
+ break;
1474
+ case "matrix":
1475
+ output.set(values[0], values[1], values[2], values[3], values[4], values[5]);
1476
+ break;
1477
+ }
1478
+ });
1479
+ this.prepend(output);
1480
+ return this;
1481
+ }
1482
+ clone() {
1483
+ return new Transform2D(this.a, this.b, this.c, this.d, this.tx, this.ty);
1484
+ }
1485
+ toArray(transpose, out) {
1486
+ if (!this._array) {
1487
+ this._array = new Float32Array(9);
1488
+ }
1489
+ const array = out || this._array;
1490
+ if (transpose) {
1491
+ array[0] = this.a;
1492
+ array[1] = this.b;
1493
+ array[2] = 0;
1494
+ array[3] = this.c;
1495
+ array[4] = this.d;
1496
+ array[5] = 0;
1497
+ array[6] = this.tx;
1498
+ array[7] = this.ty;
1499
+ array[8] = 1;
1500
+ } else {
1501
+ array[0] = this.a;
1502
+ array[1] = this.c;
1503
+ array[2] = this.tx;
1504
+ array[3] = this.b;
1505
+ array[4] = this.d;
1506
+ array[5] = this.ty;
1507
+ array[6] = 0;
1508
+ array[7] = 0;
1509
+ array[8] = 1;
1510
+ }
1511
+ return array;
1512
+ }
1513
+ toString() {
1514
+ return `[Transform2D a=${this.a} b=${this.b} c=${this.c} d=${this.d} tx=${this.tx} ty=${this.ty}]`;
1515
+ }
1516
+ destroy() {
1517
+ this._array = void 0;
1518
+ }
1524
1519
  }
1525
1520
 
1526
- function svgToPath2DSet(svg) {
1527
- const dom = svgToDom(svg);
1528
- return new Path2DSet(
1529
- parseNode(dom, {}),
1530
- dom.getAttribute("viewBox")?.trim().split(" ").map((v) => Number(v))
1531
- );
1521
+ function svgAngle(ux, uy, vx, vy) {
1522
+ const dot = ux * vx + uy * vy;
1523
+ const len = Math.sqrt(ux * ux + uy * uy) * Math.sqrt(vx * vx + vy * vy);
1524
+ let ang = Math.acos(Math.max(-1, Math.min(1, dot / len)));
1525
+ if (ux * vy - uy * vx < 0)
1526
+ ang = -ang;
1527
+ return ang;
1532
1528
  }
1533
-
1534
- function catmullRom(t, p0, p1, p2, p3) {
1535
- const v0 = (p2 - p0) * 0.5;
1536
- const v1 = (p3 - p1) * 0.5;
1537
- const t2 = t * t;
1538
- const t3 = t * t2;
1539
- return (2 * p1 - 2 * p2 + v0 + v1) * t3 + (-3 * p1 + 3 * p2 - 2 * v0 - v1) * t2 + v0 * t + p1;
1540
- }
1541
-
1542
- function cubicBezierP0(t, p) {
1543
- const k = 1 - t;
1544
- return k * k * k * p;
1545
- }
1546
- function cubicBezierP1(t, p) {
1547
- const k = 1 - t;
1548
- return 3 * k * k * t * p;
1549
- }
1550
- function cubicBezierP2(t, p) {
1551
- return 3 * (1 - t) * t * t * p;
1552
- }
1553
- function cubicBezierP3(t, p) {
1554
- return t * t * t * p;
1555
- }
1556
- function cubicBezier(t, p0, p1, p2, p3) {
1557
- return cubicBezierP0(t, p0) + cubicBezierP1(t, p1) + cubicBezierP2(t, p2) + cubicBezierP3(t, p3);
1529
+ function parseArcCommand(path, rx, ry, xAxisRotation, largeArcFlag, sweepFlag, start, end) {
1530
+ if (rx === 0 || ry === 0) {
1531
+ path.lineTo(end.x, end.y);
1532
+ return;
1533
+ }
1534
+ xAxisRotation = xAxisRotation * Math.PI / 180;
1535
+ rx = Math.abs(rx);
1536
+ ry = Math.abs(ry);
1537
+ const dx2 = (start.x - end.x) / 2;
1538
+ const dy2 = (start.y - end.y) / 2;
1539
+ const x1p = Math.cos(xAxisRotation) * dx2 + Math.sin(xAxisRotation) * dy2;
1540
+ const y1p = -Math.sin(xAxisRotation) * dx2 + Math.cos(xAxisRotation) * dy2;
1541
+ let rxs = rx * rx;
1542
+ let rys = ry * ry;
1543
+ const x1ps = x1p * x1p;
1544
+ const y1ps = y1p * y1p;
1545
+ const cr = x1ps / rxs + y1ps / rys;
1546
+ if (cr > 1) {
1547
+ const s = Math.sqrt(cr);
1548
+ rx = s * rx;
1549
+ ry = s * ry;
1550
+ rxs = rx * rx;
1551
+ rys = ry * ry;
1552
+ }
1553
+ const dq = rxs * y1ps + rys * x1ps;
1554
+ const pq = (rxs * rys - dq) / dq;
1555
+ let q = Math.sqrt(Math.max(0, pq));
1556
+ if (largeArcFlag === sweepFlag)
1557
+ q = -q;
1558
+ const cxp = q * rx * y1p / ry;
1559
+ const cyp = -q * ry * x1p / rx;
1560
+ const cx = Math.cos(xAxisRotation) * cxp - Math.sin(xAxisRotation) * cyp + (start.x + end.x) / 2;
1561
+ const cy = Math.sin(xAxisRotation) * cxp + Math.cos(xAxisRotation) * cyp + (start.y + end.y) / 2;
1562
+ const theta = svgAngle(1, 0, (x1p - cxp) / rx, (y1p - cyp) / ry);
1563
+ const delta = svgAngle((x1p - cxp) / rx, (y1p - cyp) / ry, (-x1p - cxp) / rx, (-y1p - cyp) / ry) % (Math.PI * 2);
1564
+ path.ellipse(cx, cy, rx, ry, xAxisRotation, theta, theta + delta, sweepFlag === 0);
1558
1565
  }
1559
1566
 
1560
- function fillTriangulate(pointArray, options = {}) {
1561
- let {
1562
- vertices = [],
1563
- indices = [],
1564
- holes = [],
1565
- verticesStride = 2,
1566
- verticesOffset = vertices.length / verticesStride,
1567
- indicesOffset = indices.length
1568
- } = options;
1569
- const triangles = earcut(pointArray, holes, 2);
1570
- if (triangles.length) {
1571
- for (let i = 0; i < triangles.length; i += 3) {
1572
- indices[indicesOffset++] = triangles[i] + verticesOffset;
1573
- indices[indicesOffset++] = triangles[i + 1] + verticesOffset;
1574
- indices[indicesOffset++] = triangles[i + 2] + verticesOffset;
1575
- }
1576
- let index = verticesOffset * verticesStride;
1577
- for (let i = 0; i < pointArray.length; i += 2) {
1578
- vertices[index] = pointArray[i];
1579
- vertices[index + 1] = pointArray[i + 1];
1580
- index += verticesStride;
1567
+ const RE$3 = {
1568
+ WHITESPACE: /[ \t\r\n]/,
1569
+ DIGIT: /\d/,
1570
+ SIGN: /[-+]/,
1571
+ POINT: /\./,
1572
+ COMMA: /,/,
1573
+ EXP: /e/i,
1574
+ FLAGS: /[01]/
1575
+ };
1576
+ function parsePathDataArgs(input, flags, stride = 0) {
1577
+ const SEP = 0;
1578
+ const INT = 1;
1579
+ const FLOAT = 2;
1580
+ const EXP = 3;
1581
+ let state = SEP;
1582
+ let seenComma = true;
1583
+ let number = "";
1584
+ let exponent = "";
1585
+ const result = [];
1586
+ function throwSyntaxError(current2, i, partial) {
1587
+ const error = new SyntaxError(`Unexpected character "${current2}" at index ${i}.`);
1588
+ error.partial = partial;
1589
+ throw error;
1590
+ }
1591
+ function newNumber() {
1592
+ if (number !== "") {
1593
+ if (exponent === "")
1594
+ result.push(Number(number));
1595
+ else result.push(Number(number) * 10 ** Number(exponent));
1581
1596
  }
1597
+ number = "";
1598
+ exponent = "";
1582
1599
  }
1583
- return {
1584
- vertices,
1585
- indices
1586
- };
1587
- }
1588
-
1589
- const RECURSION_LIMIT$1 = 8;
1590
- const FLT_EPSILON$1 = 11920929e-14;
1591
- const PATH_DISTANCE_EPSILON$1 = 1;
1592
- function getAdaptiveCubicBezierCurvePoints(sX, sY, x1, y1, x2, y2, x, y, smoothness = 0.5, points = []) {
1593
- const scale = 1;
1594
- const smoothing = Math.min(
1595
- 0.99,
1596
- // a value of 1.0 actually inverts smoothing, so we cap it at 0.99
1597
- Math.max(0, smoothness)
1598
- );
1599
- let distanceTolerance = (PATH_DISTANCE_EPSILON$1 - smoothing) / scale;
1600
- distanceTolerance *= distanceTolerance;
1601
- recursive$1(sX, sY, x1, y1, x2, y2, x, y, points, distanceTolerance, 0);
1602
- points.push(x, y);
1603
- return points;
1604
- }
1605
- function recursive$1(x1, y1, x2, y2, x3, y3, x4, y4, points, distanceTolerance, level) {
1606
- if (level > RECURSION_LIMIT$1)
1607
- return;
1608
- const x12 = (x1 + x2) / 2;
1609
- const y12 = (y1 + y2) / 2;
1610
- const x23 = (x2 + x3) / 2;
1611
- const y23 = (y2 + y3) / 2;
1612
- const x34 = (x3 + x4) / 2;
1613
- const y34 = (y3 + y4) / 2;
1614
- const x123 = (x12 + x23) / 2;
1615
- const y123 = (y12 + y23) / 2;
1616
- const x234 = (x23 + x34) / 2;
1617
- const y234 = (y23 + y34) / 2;
1618
- const x1234 = (x123 + x234) / 2;
1619
- const y1234 = (y123 + y234) / 2;
1620
- if (level > 0) {
1621
- let dx = x4 - x1;
1622
- let dy = y4 - y1;
1623
- const d2 = Math.abs((x2 - x4) * dy - (y2 - y4) * dx);
1624
- const d3 = Math.abs((x3 - x4) * dy - (y3 - y4) * dx);
1625
- if (d2 > FLT_EPSILON$1 && d3 > FLT_EPSILON$1) {
1626
- if ((d2 + d3) * (d2 + d3) <= distanceTolerance * (dx * dx + dy * dy)) {
1627
- {
1628
- points.push(x1234, y1234);
1629
- return;
1630
- }
1600
+ let current;
1601
+ const length = input.length;
1602
+ for (let i = 0; i < length; i++) {
1603
+ current = input[i];
1604
+ if (Array.isArray(flags) && flags.includes(result.length % stride) && RE$3.FLAGS.test(current)) {
1605
+ state = INT;
1606
+ number = current;
1607
+ newNumber();
1608
+ continue;
1609
+ }
1610
+ if (state === SEP) {
1611
+ if (RE$3.WHITESPACE.test(current)) {
1612
+ continue;
1631
1613
  }
1632
- } else if (d2 > FLT_EPSILON$1) {
1633
- if (d2 * d2 <= distanceTolerance * (dx * dx + dy * dy)) {
1634
- {
1635
- points.push(x1234, y1234);
1636
- return;
1637
- }
1614
+ if (RE$3.DIGIT.test(current) || RE$3.SIGN.test(current)) {
1615
+ state = INT;
1616
+ number = current;
1617
+ continue;
1638
1618
  }
1639
- } else if (d3 > FLT_EPSILON$1) {
1640
- if (d3 * d3 <= distanceTolerance * (dx * dx + dy * dy)) {
1641
- {
1642
- points.push(x1234, y1234);
1643
- return;
1644
- }
1619
+ if (RE$3.POINT.test(current)) {
1620
+ state = FLOAT;
1621
+ number = current;
1622
+ continue;
1645
1623
  }
1646
- } else {
1647
- dx = x1234 - (x1 + x4) / 2;
1648
- dy = y1234 - (y1 + y4) / 2;
1649
- if (dx * dx + dy * dy <= distanceTolerance) {
1650
- points.push(x1234, y1234);
1651
- return;
1624
+ if (RE$3.COMMA.test(current)) {
1625
+ if (seenComma) {
1626
+ throwSyntaxError(current, i, result);
1627
+ }
1628
+ seenComma = true;
1652
1629
  }
1653
1630
  }
1654
- }
1655
- recursive$1(x1, y1, x12, y12, x123, y123, x1234, y1234, points, distanceTolerance, level + 1);
1656
- recursive$1(x1234, y1234, x234, y234, x34, y34, x4, y4, points, distanceTolerance, level + 1);
1657
- }
1658
-
1659
- const RECURSION_LIMIT = 8;
1660
- const FLT_EPSILON = 11920929e-14;
1661
- const PATH_DISTANCE_EPSILON = 1;
1662
- function getAdaptiveQuadraticBezierCurvePoints(sX, sY, x1, y1, x, y, smoothness = 0.5, points = []) {
1663
- const scale = 1;
1664
- const smoothing = Math.min(
1665
- 0.99,
1666
- // a value of 1.0 actually inverts smoothing, so we cap it at 0.99
1667
- Math.max(0, smoothness)
1668
- );
1669
- let distanceTolerance = (PATH_DISTANCE_EPSILON - smoothing) / scale;
1670
- distanceTolerance *= distanceTolerance;
1671
- recursive(points, sX, sY, x1, y1, x, y, distanceTolerance, 0);
1672
- points.push(x, y);
1673
- return points;
1674
- }
1675
- function recursive(points, x1, y1, x2, y2, x3, y3, distanceTolerance, level) {
1676
- if (level > RECURSION_LIMIT)
1677
- return;
1678
- const x12 = (x1 + x2) / 2;
1679
- const y12 = (y1 + y2) / 2;
1680
- const x23 = (x2 + x3) / 2;
1681
- const y23 = (y2 + y3) / 2;
1682
- const x123 = (x12 + x23) / 2;
1683
- const y123 = (y12 + y23) / 2;
1684
- let dx = x3 - x1;
1685
- let dy = y3 - y1;
1686
- const d = Math.abs((x2 - x3) * dy - (y2 - y3) * dx);
1687
- if (d > FLT_EPSILON) {
1688
- if (d * d <= distanceTolerance * (dx * dx + dy * dy)) {
1689
- {
1690
- points.push(x123, y123);
1691
- return;
1631
+ if (state === INT) {
1632
+ if (RE$3.DIGIT.test(current)) {
1633
+ number += current;
1634
+ continue;
1635
+ }
1636
+ if (RE$3.POINT.test(current)) {
1637
+ number += current;
1638
+ state = FLOAT;
1639
+ continue;
1640
+ }
1641
+ if (RE$3.EXP.test(current)) {
1642
+ state = EXP;
1643
+ continue;
1644
+ }
1645
+ if (RE$3.SIGN.test(current) && number.length === 1 && RE$3.SIGN.test(number[0])) {
1646
+ throwSyntaxError(current, i, result);
1692
1647
  }
1693
1648
  }
1694
- } else {
1695
- dx = x123 - (x1 + x3) / 2;
1696
- dy = y123 - (y1 + y3) / 2;
1697
- if (dx * dx + dy * dy <= distanceTolerance) {
1698
- points.push(x123, y123);
1699
- return;
1700
- }
1701
- }
1702
- recursive(points, x1, y1, x12, y12, x123, y123, distanceTolerance, level + 1);
1703
- recursive(points, x123, y123, x23, y23, x3, y3, distanceTolerance, level + 1);
1704
- }
1705
-
1706
- function getDirectedArea(vertices) {
1707
- let area = 0;
1708
- const n = vertices.length;
1709
- for (let i = 0; i < n; i += 2) {
1710
- const x0 = vertices[i];
1711
- const y0 = vertices[i + 1];
1712
- const x1 = vertices[(i + 2) % (n - 1)];
1713
- const y1 = vertices[(i + 3) % n];
1714
- area += x0 * y1 - x1 * y0;
1715
- }
1716
- return area / 2;
1717
- }
1718
-
1719
- function toKebabCase(str) {
1720
- return str.replace(/[^a-z0-9]/gi, "-").replace(/\B([A-Z])/g, "-$1").toLowerCase();
1721
- }
1722
- function getIntersectionPoint(p1, p2, q1, q2) {
1723
- const r = p2.clone().sub(p1);
1724
- const s = q2.clone().sub(q1);
1725
- const q1p1 = q1.clone().sub(p1);
1726
- const crossRS = r.cross(s);
1727
- if (crossRS === 0) {
1728
- return new Vector2(
1729
- (p1.x + q1.x) / 2,
1730
- (p1.y + q1.y) / 2
1731
- );
1732
- }
1733
- const t = q1p1.cross(s) / crossRS;
1734
- if (Math.abs(t) > 1) {
1735
- return new Vector2(
1736
- (p1.x + q1.x) / 2,
1737
- (p1.y + q1.y) / 2
1738
- );
1739
- }
1740
- return new Vector2(
1741
- p1.x + t * r.x,
1742
- p1.y + t * r.y
1743
- );
1744
- }
1745
-
1746
- function cross(ax, ay, bx, by, cx, cy) {
1747
- return (bx - ax) * (cy - ay) - (by - ay) * (cx - ax);
1748
- }
1749
- function windingNumber(px, py, polygon) {
1750
- const polygonLen = polygon.length;
1751
- let wn = 0;
1752
- for (let i = 0, j = polygonLen - 2; i < polygonLen; j = i, i += 2) {
1753
- const xi = polygon[i];
1754
- const yi = polygon[i + 1];
1755
- const xj = polygon[j];
1756
- const yj = polygon[j + 1];
1757
- if (yi <= py) {
1758
- if (yj > py && cross(xj, yj, xi, yi, px, py) > 0)
1759
- wn++;
1760
- } else {
1761
- if (yj <= py && cross(xj, yj, xi, yi, px, py) < 0)
1762
- wn--;
1763
- }
1764
- }
1765
- return wn;
1766
- }
1767
- function distance(p1, p2) {
1768
- const dx = p2[0] - p1[0];
1769
- const dy = p2[1] - p1[1];
1770
- return Math.sqrt(dx * dx + dy * dy);
1771
- }
1772
- function nonzeroFillRule(paths) {
1773
- const results = paths.map((_, i) => ({ index: i }));
1774
- const testPointsGroups = paths.map((path) => {
1775
- const len = path.length;
1776
- if (!len) {
1777
- return [];
1778
- }
1779
- let xMinYAuto = [Number.MAX_SAFE_INTEGER, 0];
1780
- let xAutoYMin = [0, Number.MAX_SAFE_INTEGER];
1781
- let xMaxYAuto = [Number.MIN_SAFE_INTEGER, 0];
1782
- let xAutoYMax = [0, Number.MIN_SAFE_INTEGER];
1783
- for (let i = 0; i < len; i += 2) {
1784
- const x = path[i];
1785
- const y = path[i + 1];
1786
- if (xMinYAuto[0] > x) {
1787
- xMinYAuto = [x, y];
1788
- }
1789
- if (xAutoYMin[1] > y) {
1790
- xAutoYMin = [x, y];
1791
- }
1792
- if (xMaxYAuto[0] < x) {
1793
- xMaxYAuto = [x, y];
1794
- }
1795
- if (xAutoYMax[1] < y) {
1796
- xAutoYMax = [x, y];
1797
- }
1798
- }
1799
- const mid = [
1800
- (xMinYAuto[0] + xMaxYAuto[0]) / 2,
1801
- (xAutoYMin[1] + xAutoYMax[1]) / 2
1802
- ];
1803
- let xMidYMinDx;
1804
- let xMidYMaxDx;
1805
- let xMidYMin;
1806
- let xMidYMax;
1807
- let xMinYMidDy;
1808
- let xMaxYMidDy;
1809
- let xMinYMid;
1810
- let xMaxYMid;
1811
- for (let i = 0; i < len; i += 2) {
1812
- const x = path[i];
1813
- const y = path[i + 1];
1814
- const _dx = Math.abs(x - mid[0]);
1815
- const _dy = Math.abs(y - mid[1]);
1816
- if (y < mid[1] && (!xMidYMinDx || _dx < xMidYMinDx)) {
1817
- xMidYMinDx = _dx;
1818
- xMidYMin = [x, y];
1819
- }
1820
- if (y > mid[1] && (!xMidYMaxDx || _dx < xMidYMaxDx)) {
1821
- xMidYMaxDx = _dx;
1822
- xMidYMax = [x, y];
1649
+ if (state === FLOAT) {
1650
+ if (RE$3.DIGIT.test(current)) {
1651
+ number += current;
1652
+ continue;
1823
1653
  }
1824
- if (x < mid[0] && (!xMinYMidDy || _dy < xMinYMidDy)) {
1825
- xMinYMidDy = _dy;
1826
- xMinYMid = [x, y];
1654
+ if (RE$3.EXP.test(current)) {
1655
+ state = EXP;
1656
+ continue;
1827
1657
  }
1828
- if (x > mid[0] && (!xMaxYMidDy || _dy < xMaxYMidDy)) {
1829
- xMaxYMidDy = _dy;
1830
- xMaxYMid = [x, y];
1658
+ if (RE$3.POINT.test(current) && number[number.length - 1] === ".") {
1659
+ throwSyntaxError(current, i, result);
1831
1660
  }
1832
1661
  }
1833
- return [
1834
- xMinYAuto,
1835
- xAutoYMin,
1836
- xMaxYAuto,
1837
- xAutoYMax,
1838
- xMidYMin,
1839
- xMidYMax,
1840
- xMinYMid,
1841
- xMaxYMid
1842
- ].filter(Boolean);
1843
- });
1844
- for (let i = 0, len = paths.length; i < len; i++) {
1845
- const _results = [];
1846
- const testPoints = testPointsGroups[i];
1847
- for (let j = 0; j < len; j++) {
1848
- if (i === j)
1662
+ if (state === EXP) {
1663
+ if (RE$3.DIGIT.test(current)) {
1664
+ exponent += current;
1849
1665
  continue;
1850
- const wnMap = {};
1851
- const wnList = [];
1852
- for (let p = 0, pLen = testPoints.length; p < pLen; p++) {
1853
- const [x, y] = testPoints[p];
1854
- const winding = windingNumber(x, y, paths[j]);
1855
- wnMap[winding] = (wnMap[winding] ?? 0) + 1;
1856
- wnList.push(winding);
1857
1666
  }
1858
- if (wnList.filter((v) => v !== 0).length > wnList.filter((v) => v === 0).length) {
1859
- _results.push({
1860
- index: i,
1861
- parentIndex: j,
1862
- winding: Number(
1863
- Array.from(Object.entries(wnMap)).sort((a, b) => b[1] - a[1])?.[0]?.[0] ?? 0
1864
- ),
1865
- dist: distance(testPointsGroups[i][0], testPointsGroups[j][0])
1866
- });
1667
+ if (RE$3.SIGN.test(current)) {
1668
+ if (exponent === "") {
1669
+ exponent += current;
1670
+ continue;
1671
+ }
1672
+ if (exponent.length === 1 && RE$3.SIGN.test(exponent)) {
1673
+ throwSyntaxError(current, i, result);
1674
+ }
1867
1675
  }
1868
1676
  }
1869
- if (_results.reduce((total, item) => total + item.winding, 0) !== 0) {
1870
- _results.sort((a, b) => a.dist - b.dist);
1871
- results[i] = _results[0];
1677
+ if (RE$3.WHITESPACE.test(current)) {
1678
+ newNumber();
1679
+ state = SEP;
1680
+ seenComma = false;
1681
+ } else if (RE$3.COMMA.test(current)) {
1682
+ newNumber();
1683
+ state = SEP;
1684
+ seenComma = true;
1685
+ } else if (RE$3.SIGN.test(current)) {
1686
+ newNumber();
1687
+ state = INT;
1688
+ number = current;
1689
+ } else if (RE$3.POINT.test(current)) {
1690
+ newNumber();
1691
+ state = FLOAT;
1692
+ number = current;
1693
+ } else {
1694
+ throwSyntaxError(current, i, result);
1872
1695
  }
1873
1696
  }
1874
- return results;
1697
+ newNumber();
1698
+ return result;
1875
1699
  }
1876
1700
 
1877
- function quadraticBezierP0(t, p) {
1878
- const k = 1 - t;
1879
- return k * k * p;
1880
- }
1881
- function quadraticBezierP1(t, p) {
1882
- return 2 * (1 - t) * t * p;
1883
- }
1884
- function quadraticBezierP2(t, p) {
1885
- return t * t * p;
1886
- }
1887
- function quadraticBezier(t, p0, p1, p2) {
1888
- return quadraticBezierP0(t, p0) + quadraticBezierP1(t, p1) + quadraticBezierP2(t, p2);
1701
+ function getReflection(a, b) {
1702
+ return a - (b - a);
1889
1703
  }
1890
-
1891
- const closePointEps = 1e-4;
1892
- const curveEps = 1e-4;
1893
- function strokeTriangulate(points, options = {}) {
1894
- const {
1895
- vertices = [],
1896
- indices = [],
1897
- lineStyle = {
1898
- alignment: 0.5,
1899
- cap: "butt",
1900
- join: "miter",
1901
- width: 1,
1902
- miterLimit: 10
1903
- },
1904
- flipAlignment = false,
1905
- closed = true
1906
- } = options;
1907
- const eps = closePointEps;
1908
- if (points.length === 0) {
1909
- return { vertices, indices };
1910
- }
1911
- const style = lineStyle;
1912
- let alignment = style.alignment;
1913
- if (lineStyle.alignment !== 0.5) {
1914
- let orientation = getOrientationOfPoints(points);
1915
- if (flipAlignment)
1916
- orientation *= -1;
1917
- alignment = (alignment - 0.5) * orientation + 0.5;
1918
- }
1919
- const firstPoint = { x: points[0], y: points[1] };
1920
- const lastPoint = { x: points[points.length - 2], y: points[points.length - 1] };
1921
- const closedShape = closed;
1922
- const closedPath = Math.abs(firstPoint.x - lastPoint.x) < eps && Math.abs(firstPoint.y - lastPoint.y) < eps;
1923
- if (closedShape) {
1924
- points = points.slice();
1925
- if (closedPath) {
1926
- points.pop();
1927
- points.pop();
1928
- lastPoint.x = points[points.length - 2];
1929
- lastPoint.y = points[points.length - 1];
1704
+ function svgPathCommandsAddToPath2D(commands, path) {
1705
+ const current = new Vector2();
1706
+ const control = new Vector2();
1707
+ for (let i = 0, l = commands.length; i < l; i++) {
1708
+ const cmd = commands[i];
1709
+ if (cmd.type === "m" || cmd.type === "M") {
1710
+ if (cmd.type === "m") {
1711
+ current.add(cmd);
1712
+ } else {
1713
+ current.copyFrom(cmd);
1714
+ }
1715
+ path.moveTo(current.x, current.y);
1716
+ control.copyFrom(current);
1717
+ } else if (cmd.type === "h" || cmd.type === "H") {
1718
+ if (cmd.type === "h") {
1719
+ current.x += cmd.x;
1720
+ } else {
1721
+ current.x = cmd.x;
1722
+ }
1723
+ path.lineTo(current.x, current.y);
1724
+ control.copyFrom(current);
1725
+ } else if (cmd.type === "v" || cmd.type === "V") {
1726
+ if (cmd.type === "v") {
1727
+ current.y += cmd.y;
1728
+ } else {
1729
+ current.y = cmd.y;
1730
+ }
1731
+ path.lineTo(current.x, current.y);
1732
+ control.copyFrom(current);
1733
+ } else if (cmd.type === "l" || cmd.type === "L") {
1734
+ if (cmd.type === "l") {
1735
+ current.add(cmd);
1736
+ } else {
1737
+ current.copyFrom(cmd);
1738
+ }
1739
+ path.lineTo(current.x, current.y);
1740
+ control.copyFrom(current);
1741
+ } else if (cmd.type === "c" || cmd.type === "C") {
1742
+ if (cmd.type === "c") {
1743
+ path.bezierCurveTo(
1744
+ current.x + cmd.x1,
1745
+ current.y + cmd.y1,
1746
+ current.x + cmd.x2,
1747
+ current.y + cmd.y2,
1748
+ current.x + cmd.x,
1749
+ current.y + cmd.y
1750
+ );
1751
+ control.x = current.x + cmd.x2;
1752
+ control.y = current.y + cmd.y2;
1753
+ current.add(cmd);
1754
+ } else {
1755
+ path.bezierCurveTo(
1756
+ cmd.x1,
1757
+ cmd.y1,
1758
+ cmd.x2,
1759
+ cmd.y2,
1760
+ cmd.x,
1761
+ cmd.y
1762
+ );
1763
+ control.x = cmd.x2;
1764
+ control.y = cmd.y2;
1765
+ current.copyFrom(cmd);
1766
+ }
1767
+ } else if (cmd.type === "s" || cmd.type === "S") {
1768
+ if (cmd.type === "s") {
1769
+ path.bezierCurveTo(
1770
+ getReflection(current.x, control.x),
1771
+ getReflection(current.y, control.y),
1772
+ current.x + cmd.x2,
1773
+ current.y + cmd.y2,
1774
+ current.x + cmd.x,
1775
+ current.y + cmd.y
1776
+ );
1777
+ control.x = current.x + cmd.x2;
1778
+ control.y = current.y + cmd.y2;
1779
+ current.add(cmd);
1780
+ } else {
1781
+ path.bezierCurveTo(
1782
+ getReflection(current.x, control.x),
1783
+ getReflection(current.y, control.y),
1784
+ cmd.x2,
1785
+ cmd.y2,
1786
+ cmd.x,
1787
+ cmd.y
1788
+ );
1789
+ control.x = cmd.x2;
1790
+ control.y = cmd.y2;
1791
+ current.copyFrom(cmd);
1792
+ }
1793
+ } else if (cmd.type === "q" || cmd.type === "Q") {
1794
+ if (cmd.type === "q") {
1795
+ path.quadraticCurveTo(
1796
+ current.x + cmd.x1,
1797
+ current.y + cmd.y1,
1798
+ current.x + cmd.x,
1799
+ current.y + cmd.y
1800
+ );
1801
+ control.x = current.x + cmd.x1;
1802
+ control.y = current.y + cmd.y1;
1803
+ current.add(cmd);
1804
+ } else {
1805
+ path.quadraticCurveTo(
1806
+ cmd.x1,
1807
+ cmd.y1,
1808
+ cmd.x,
1809
+ cmd.y
1810
+ );
1811
+ control.x = cmd.x1;
1812
+ control.y = cmd.y1;
1813
+ current.copyFrom(cmd);
1814
+ }
1815
+ } else if (cmd.type === "t" || cmd.type === "T") {
1816
+ const rx = getReflection(current.x, control.x);
1817
+ const ry = getReflection(current.y, control.y);
1818
+ control.x = rx;
1819
+ control.y = ry;
1820
+ if (cmd.type === "t") {
1821
+ path.quadraticCurveTo(
1822
+ rx,
1823
+ ry,
1824
+ current.x + cmd.x,
1825
+ current.y + cmd.y
1826
+ );
1827
+ current.add(cmd);
1828
+ } else {
1829
+ path.quadraticCurveTo(
1830
+ rx,
1831
+ ry,
1832
+ cmd.x,
1833
+ cmd.y
1834
+ );
1835
+ current.copyFrom(cmd);
1836
+ }
1837
+ } else if (cmd.type === "a" || cmd.type === "A") {
1838
+ const start = current.clone();
1839
+ if (cmd.type === "a") {
1840
+ if (cmd.x === 0 && cmd.y === 0)
1841
+ continue;
1842
+ current.add(cmd);
1843
+ } else {
1844
+ if (current.equals(cmd))
1845
+ continue;
1846
+ current.copyFrom(cmd);
1847
+ }
1848
+ control.copyFrom(current);
1849
+ parseArcCommand(
1850
+ path,
1851
+ cmd.rx,
1852
+ cmd.ry,
1853
+ cmd.angle,
1854
+ cmd.largeArcFlag,
1855
+ cmd.sweepFlag,
1856
+ start,
1857
+ current
1858
+ );
1859
+ } else if (cmd.type === "z" || cmd.type === "Z") {
1860
+ if (path.startPoint) {
1861
+ current.copyFrom(path.startPoint);
1862
+ }
1863
+ path.closePath();
1864
+ } else {
1865
+ console.warn("Unsupported commands", cmd);
1866
+ }
1867
+ }
1868
+ }
1869
+
1870
+ function svgPathCommandsToData(commands) {
1871
+ let first;
1872
+ let prev;
1873
+ const data = [];
1874
+ for (let i = 0, len = commands.length; i < len; i++) {
1875
+ const cmd = commands[i];
1876
+ switch (cmd.type) {
1877
+ case "m":
1878
+ case "M":
1879
+ if (cmd.x.toFixed(4) === prev?.x.toFixed(4) && cmd.y.toFixed(4) === prev?.y.toFixed(4)) {
1880
+ continue;
1881
+ }
1882
+ data.push(`${cmd.type} ${cmd.x} ${cmd.y}`);
1883
+ prev = { x: cmd.x, y: cmd.y };
1884
+ first = { x: cmd.x, y: cmd.y };
1885
+ break;
1886
+ case "h":
1887
+ case "H":
1888
+ data.push(`${cmd.type} ${cmd.x}`);
1889
+ prev = { x: cmd.x, y: prev?.y ?? 0 };
1890
+ break;
1891
+ case "v":
1892
+ case "V":
1893
+ data.push(`${cmd.type} ${cmd.y}`);
1894
+ prev = { x: prev?.x ?? 0, y: cmd.y };
1895
+ break;
1896
+ case "l":
1897
+ case "L":
1898
+ data.push(`${cmd.type} ${cmd.x} ${cmd.y}`);
1899
+ prev = { x: cmd.x, y: cmd.y };
1900
+ break;
1901
+ case "c":
1902
+ case "C":
1903
+ data.push(`${cmd.type} ${cmd.x1} ${cmd.y1} ${cmd.x2} ${cmd.y2} ${cmd.x} ${cmd.y}`);
1904
+ prev = { x: cmd.x, y: cmd.y };
1905
+ break;
1906
+ case "s":
1907
+ case "S":
1908
+ data.push(`${cmd.type} ${cmd.x2} ${cmd.y2} ${cmd.x} ${cmd.y}`);
1909
+ prev = { x: cmd.x, y: cmd.y };
1910
+ break;
1911
+ case "q":
1912
+ case "Q":
1913
+ data.push(`${cmd.type} ${cmd.x1} ${cmd.y1} ${cmd.x} ${cmd.y}`);
1914
+ prev = { x: cmd.x, y: cmd.y };
1915
+ break;
1916
+ case "t":
1917
+ case "T":
1918
+ data.push(`${cmd.type} ${cmd.x} ${cmd.y}`);
1919
+ prev = { x: cmd.x, y: cmd.y };
1920
+ break;
1921
+ case "a":
1922
+ case "A":
1923
+ data.push(`${cmd.type} ${cmd.rx} ${cmd.ry} ${cmd.angle} ${cmd.largeArcFlag} ${cmd.sweepFlag} ${cmd.x} ${cmd.y}`);
1924
+ prev = { x: cmd.x, y: cmd.y };
1925
+ break;
1926
+ case "z":
1927
+ case "Z":
1928
+ data.push(cmd.type);
1929
+ if (first) {
1930
+ prev = { x: first.x, y: first.y };
1931
+ }
1932
+ break;
1933
+ }
1934
+ }
1935
+ return data.join(" ");
1936
+ }
1937
+
1938
+ const RE$2 = /[a-df-z][^a-df-z]*/gi;
1939
+ function svgPathDataToCommands(data) {
1940
+ const commands = [];
1941
+ const matched = data.match(RE$2);
1942
+ if (!matched) {
1943
+ return commands;
1944
+ }
1945
+ for (let i = 0, len = matched.length; i < len; i++) {
1946
+ const command = matched[i];
1947
+ const type = command.charAt(0);
1948
+ const data2 = command.slice(1).trim();
1949
+ let args;
1950
+ switch (type) {
1951
+ case "m":
1952
+ case "M":
1953
+ args = parsePathDataArgs(data2);
1954
+ for (let i2 = 0, len2 = args.length; i2 < len2; i2 += 2) {
1955
+ if (i2 === 0) {
1956
+ commands.push({ type, x: args[i2], y: args[i2 + 1] });
1957
+ } else {
1958
+ commands.push({ type: type === "m" ? "l" : "L", x: args[i2], y: args[i2 + 1] });
1959
+ }
1960
+ }
1961
+ break;
1962
+ case "h":
1963
+ case "H":
1964
+ args = parsePathDataArgs(data2);
1965
+ for (let i2 = 0, len2 = args.length; i2 < len2; i2++) {
1966
+ commands.push({ type, x: args[i2] });
1967
+ }
1968
+ break;
1969
+ case "v":
1970
+ case "V":
1971
+ args = parsePathDataArgs(data2);
1972
+ for (let i2 = 0, len2 = args.length; i2 < len2; i2++) {
1973
+ commands.push({ type, y: args[i2] });
1974
+ }
1975
+ break;
1976
+ case "l":
1977
+ case "L":
1978
+ args = parsePathDataArgs(data2);
1979
+ for (let i2 = 0, len2 = args.length; i2 < len2; i2 += 2) {
1980
+ commands.push({ type, x: args[i2], y: args[i2 + 1] });
1981
+ }
1982
+ break;
1983
+ case "c":
1984
+ case "C":
1985
+ args = parsePathDataArgs(data2);
1986
+ for (let i2 = 0, len2 = args.length; i2 < len2; i2 += 6) {
1987
+ commands.push({
1988
+ type,
1989
+ x1: args[i2],
1990
+ y1: args[i2 + 1],
1991
+ x2: args[i2 + 2],
1992
+ y2: args[i2 + 3],
1993
+ x: args[i2 + 4],
1994
+ y: args[i2 + 5]
1995
+ });
1996
+ }
1997
+ break;
1998
+ case "s":
1999
+ case "S":
2000
+ args = parsePathDataArgs(data2);
2001
+ for (let i2 = 0, len2 = args.length; i2 < len2; i2 += 4) {
2002
+ commands.push({
2003
+ type,
2004
+ x2: args[i2],
2005
+ y2: args[i2 + 1],
2006
+ x: args[i2 + 2],
2007
+ y: args[i2 + 3]
2008
+ });
2009
+ }
2010
+ break;
2011
+ case "q":
2012
+ case "Q":
2013
+ args = parsePathDataArgs(data2);
2014
+ for (let i2 = 0, len2 = args.length; i2 < len2; i2 += 4) {
2015
+ commands.push({
2016
+ type,
2017
+ x1: args[i2],
2018
+ y1: args[i2 + 1],
2019
+ x: args[i2 + 2],
2020
+ y: args[i2 + 3]
2021
+ });
2022
+ }
2023
+ break;
2024
+ case "t":
2025
+ case "T":
2026
+ args = parsePathDataArgs(data2);
2027
+ for (let i2 = 0, len2 = args.length; i2 < len2; i2 += 2) {
2028
+ commands.push({
2029
+ type,
2030
+ x: args[i2],
2031
+ y: args[i2 + 1]
2032
+ });
2033
+ }
2034
+ break;
2035
+ case "a":
2036
+ case "A":
2037
+ args = parsePathDataArgs(data2, [3, 4], 7);
2038
+ for (let i2 = 0, len2 = args.length; i2 < len2; i2 += 7) {
2039
+ commands.push({
2040
+ type,
2041
+ rx: args[i2],
2042
+ ry: args[i2 + 1],
2043
+ angle: args[i2 + 2],
2044
+ largeArcFlag: args[i2 + 3],
2045
+ sweepFlag: args[i2 + 4],
2046
+ x: args[i2 + 5],
2047
+ y: args[i2 + 6]
2048
+ });
2049
+ }
2050
+ break;
2051
+ case "z":
2052
+ case "Z":
2053
+ commands.push({
2054
+ type
2055
+ });
2056
+ break;
2057
+ default:
2058
+ console.warn(command);
1930
2059
  }
1931
- const midPointX = (firstPoint.x + lastPoint.x) * 0.5;
1932
- const midPointY = (lastPoint.y + firstPoint.y) * 0.5;
1933
- points.unshift(midPointX, midPointY);
1934
- points.push(midPointX, midPointY);
1935
2060
  }
1936
- const verts = vertices;
1937
- const length = points.length / 2;
1938
- let indexCount = points.length;
1939
- const indexStart = verts.length / 2;
1940
- const width = style.width / 2;
1941
- const widthSquared = width * width;
1942
- const miterLimitSquared = style.miterLimit * style.miterLimit;
1943
- let x0 = points[0];
1944
- let y0 = points[1];
1945
- let x1 = points[2];
1946
- let y1 = points[3];
1947
- let x2 = 0;
1948
- let y2 = 0;
1949
- let perpX = -(y0 - y1);
1950
- let perpY = x0 - x1;
1951
- let perp1x = 0;
1952
- let perp1y = 0;
1953
- let dist = Math.sqrt(perpX * perpX + perpY * perpY);
1954
- perpX /= dist;
1955
- perpY /= dist;
1956
- perpX *= width;
1957
- perpY *= width;
1958
- const ratio = alignment;
1959
- const innerWeight = (1 - ratio) * 2;
1960
- const outerWeight = ratio * 2;
1961
- if (!closedShape) {
1962
- if (style.cap === "round") {
1963
- indexCount += round(
1964
- x0 - perpX * (innerWeight - outerWeight) * 0.5,
1965
- y0 - perpY * (innerWeight - outerWeight) * 0.5,
1966
- x0 - perpX * innerWeight,
1967
- y0 - perpY * innerWeight,
1968
- x0 + perpX * outerWeight,
1969
- y0 + perpY * outerWeight,
1970
- verts,
1971
- true
1972
- ) + 2;
1973
- } else if (style.cap === "square") {
1974
- indexCount += square(x0, y0, perpX, perpY, innerWeight, outerWeight, true, verts);
2061
+ return commands;
2062
+ }
2063
+
2064
+ const dataUri = "data:image/svg+xml;";
2065
+ const base64DataUri = `${dataUri}base64,`;
2066
+ const utf8DataUri = `${dataUri}charset=utf8,`;
2067
+ function svgToDom(svg) {
2068
+ if (typeof svg === "string") {
2069
+ let xml;
2070
+ if (svg.startsWith(base64DataUri)) {
2071
+ svg = svg.substring(base64DataUri.length, svg.length);
2072
+ xml = atob(svg);
2073
+ } else if (svg.startsWith(utf8DataUri)) {
2074
+ svg = svg.substring(utf8DataUri.length, svg.length);
2075
+ xml = decodeURIComponent(svg);
2076
+ } else {
2077
+ xml = svg;
2078
+ }
2079
+ const doc = new DOMParser().parseFromString(xml, "text/xml");
2080
+ const error = doc.querySelector("parsererror");
2081
+ if (error) {
2082
+ throw new Error(`${error.textContent ?? "parser error"}
2083
+ ${xml}`);
1975
2084
  }
2085
+ return doc.documentElement;
2086
+ } else {
2087
+ return svg;
1976
2088
  }
1977
- verts.push(
1978
- x0 - perpX * innerWeight,
1979
- y0 - perpY * innerWeight
1980
- );
1981
- verts.push(
1982
- x0 + perpX * outerWeight,
1983
- y0 + perpY * outerWeight
1984
- );
1985
- for (let i = 1; i < length - 1; ++i) {
1986
- x0 = points[(i - 1) * 2];
1987
- y0 = points[(i - 1) * 2 + 1];
1988
- x1 = points[i * 2];
1989
- y1 = points[i * 2 + 1];
1990
- x2 = points[(i + 1) * 2];
1991
- y2 = points[(i + 1) * 2 + 1];
1992
- perpX = -(y0 - y1);
1993
- perpY = x0 - x1;
1994
- dist = Math.sqrt(perpX * perpX + perpY * perpY);
1995
- perpX /= dist;
1996
- perpY /= dist;
1997
- perpX *= width;
1998
- perpY *= width;
1999
- perp1x = -(y1 - y2);
2000
- perp1y = x1 - x2;
2001
- dist = Math.sqrt(perp1x * perp1x + perp1y * perp1y);
2002
- perp1x /= dist;
2003
- perp1y /= dist;
2004
- perp1x *= width;
2005
- perp1y *= width;
2006
- const dx0 = x1 - x0;
2007
- const dy0 = y0 - y1;
2008
- const dx1 = x1 - x2;
2009
- const dy1 = y2 - y1;
2010
- const dot = dx0 * dx1 + dy0 * dy1;
2011
- const cross = dy0 * dx1 - dy1 * dx0;
2012
- const clockwise = cross < 0;
2013
- if (Math.abs(cross) < 1e-3 * Math.abs(dot)) {
2014
- verts.push(
2015
- x1 - perpX * innerWeight,
2016
- y1 - perpY * innerWeight
2017
- );
2018
- verts.push(
2019
- x1 + perpX * outerWeight,
2020
- y1 + perpY * outerWeight
2021
- );
2022
- if (dot >= 0) {
2023
- if (style.join === "round") {
2024
- indexCount += round(
2025
- x1,
2026
- y1,
2027
- x1 - perpX * innerWeight,
2028
- y1 - perpY * innerWeight,
2029
- x1 - perp1x * innerWeight,
2030
- y1 - perp1y * innerWeight,
2031
- verts,
2032
- false
2033
- ) + 4;
2034
- } else {
2035
- indexCount += 2;
2036
- }
2037
- verts.push(
2038
- x1 - perp1x * outerWeight,
2039
- y1 - perp1y * outerWeight
2040
- );
2041
- verts.push(
2042
- x1 + perp1x * innerWeight,
2043
- y1 + perp1y * innerWeight
2044
- );
2089
+ }
2090
+
2091
+ const defaultUnit = "px";
2092
+ const defaultDPI = 90;
2093
+ const units = ["mm", "cm", "in", "pt", "pc", "px"];
2094
+ const unitConversion = {
2095
+ mm: {
2096
+ mm: 1,
2097
+ cm: 0.1,
2098
+ in: 1 / 25.4,
2099
+ pt: 72 / 25.4,
2100
+ pc: 6 / 25.4,
2101
+ px: -1
2102
+ },
2103
+ cm: {
2104
+ mm: 10,
2105
+ cm: 1,
2106
+ in: 1 / 2.54,
2107
+ pt: 72 / 2.54,
2108
+ pc: 6 / 2.54,
2109
+ px: -1
2110
+ },
2111
+ in: {
2112
+ mm: 25.4,
2113
+ cm: 2.54,
2114
+ in: 1,
2115
+ pt: 72,
2116
+ pc: 6,
2117
+ px: -1
2118
+ },
2119
+ pt: {
2120
+ mm: 25.4 / 72,
2121
+ cm: 2.54 / 72,
2122
+ in: 1 / 72,
2123
+ pt: 1,
2124
+ pc: 6 / 72,
2125
+ px: -1
2126
+ },
2127
+ pc: {
2128
+ mm: 25.4 / 6,
2129
+ cm: 2.54 / 6,
2130
+ in: 1 / 6,
2131
+ pt: 72 / 6,
2132
+ pc: 1,
2133
+ px: -1
2134
+ },
2135
+ px: {
2136
+ px: 1
2137
+ }
2138
+ };
2139
+ function parseFloatWithUnits(string) {
2140
+ let theUnit = "px";
2141
+ if (typeof string === "string") {
2142
+ for (let i = 0, n = units.length; i < n; i++) {
2143
+ const u = units[i];
2144
+ if (string.endsWith(u)) {
2145
+ theUnit = u;
2146
+ string = string.substring(0, string.length - u.length);
2147
+ break;
2045
2148
  }
2149
+ }
2150
+ }
2151
+ let scale;
2152
+ {
2153
+ scale = unitConversion[theUnit][defaultUnit];
2154
+ if (scale < 0) {
2155
+ scale = unitConversion[theUnit].in * defaultDPI;
2156
+ }
2157
+ }
2158
+ return scale * Number.parseFloat(string);
2159
+ }
2160
+
2161
+ function getNodeTransform(node, currentTransform, transformStack) {
2162
+ if (!(node.hasAttribute("transform") || node.nodeName === "use" && (node.hasAttribute("x") || node.hasAttribute("y")))) {
2163
+ return null;
2164
+ }
2165
+ const transform = parseNodeTransform(node);
2166
+ if (transformStack.length > 0) {
2167
+ transform.prepend(transformStack[transformStack.length - 1]);
2168
+ }
2169
+ currentTransform.copyFrom(transform);
2170
+ transformStack.push(transform);
2171
+ return transform;
2172
+ }
2173
+ function parseNodeTransform(node) {
2174
+ const transform = new Transform2D();
2175
+ if (node.nodeName === "use" && (node.hasAttribute("x") || node.hasAttribute("y"))) {
2176
+ transform.translate(
2177
+ parseFloatWithUnits(node.getAttribute("x")),
2178
+ parseFloatWithUnits(node.getAttribute("y"))
2179
+ );
2180
+ }
2181
+ if (node.hasAttribute("transform")) {
2182
+ transform.appendCssTransform(node.getAttribute("transform"));
2183
+ }
2184
+ return transform;
2185
+ }
2186
+
2187
+ function parseCircleNode(node) {
2188
+ return new Path2D().arc(
2189
+ parseFloatWithUnits(node.getAttribute("cx") || 0),
2190
+ parseFloatWithUnits(node.getAttribute("cy") || 0),
2191
+ parseFloatWithUnits(node.getAttribute("r") || 0),
2192
+ 0,
2193
+ Math.PI * 2
2194
+ );
2195
+ }
2196
+
2197
+ function parseCSSStylesheet(node, stylesheets) {
2198
+ if (!node.sheet || !node.sheet.cssRules || !node.sheet.cssRules.length)
2199
+ return;
2200
+ for (let i = 0; i < node.sheet.cssRules.length; i++) {
2201
+ const stylesheet = node.sheet.cssRules[i];
2202
+ if (stylesheet.type !== 1)
2046
2203
  continue;
2204
+ const selectorList = stylesheet.selectorText.split(/,/g).filter(Boolean).map((i2) => i2.trim());
2205
+ const definitions = {};
2206
+ for (let len = stylesheet.style.length, i2 = 0; i2 < len; i2++) {
2207
+ const name = stylesheet.style.item(i2);
2208
+ definitions[name] = stylesheet.style.getPropertyValue(name);
2047
2209
  }
2048
- const c1 = (-perpX + x0) * (-perpY + y1) - (-perpX + x1) * (-perpY + y0);
2049
- const c2 = (-perp1x + x2) * (-perp1y + y1) - (-perp1x + x1) * (-perp1y + y2);
2050
- const px = (dx0 * c2 - dx1 * c1) / cross;
2051
- const py = (dy1 * c1 - dy0 * c2) / cross;
2052
- const pDist = (px - x1) * (px - x1) + (py - y1) * (py - y1);
2053
- const imx = x1 + (px - x1) * innerWeight;
2054
- const imy = y1 + (py - y1) * innerWeight;
2055
- const omx = x1 - (px - x1) * outerWeight;
2056
- const omy = y1 - (py - y1) * outerWeight;
2057
- const smallerInsideSegmentSq = Math.min(dx0 * dx0 + dy0 * dy0, dx1 * dx1 + dy1 * dy1);
2058
- const insideWeight = clockwise ? innerWeight : outerWeight;
2059
- const smallerInsideDiagonalSq = smallerInsideSegmentSq + insideWeight * insideWeight * widthSquared;
2060
- const insideMiterOk = pDist <= smallerInsideDiagonalSq;
2061
- if (insideMiterOk) {
2062
- if (style.join === "bevel" || pDist / widthSquared > miterLimitSquared) {
2063
- if (clockwise) {
2064
- verts.push(imx, imy);
2065
- verts.push(x1 + perpX * outerWeight, y1 + perpY * outerWeight);
2066
- verts.push(imx, imy);
2067
- verts.push(x1 + perp1x * outerWeight, y1 + perp1y * outerWeight);
2068
- } else {
2069
- verts.push(x1 - perpX * innerWeight, y1 - perpY * innerWeight);
2070
- verts.push(omx, omy);
2071
- verts.push(x1 - perp1x * innerWeight, y1 - perp1y * innerWeight);
2072
- verts.push(omx, omy);
2073
- }
2074
- indexCount += 2;
2075
- } else if (style.join === "round") {
2076
- if (clockwise) {
2077
- verts.push(imx, imy);
2078
- verts.push(x1 + perpX * outerWeight, y1 + perpY * outerWeight);
2079
- indexCount += round(
2080
- x1,
2081
- y1,
2082
- x1 + perpX * outerWeight,
2083
- y1 + perpY * outerWeight,
2084
- x1 + perp1x * outerWeight,
2085
- y1 + perp1y * outerWeight,
2086
- verts,
2087
- true
2088
- ) + 4;
2089
- verts.push(imx, imy);
2090
- verts.push(x1 + perp1x * outerWeight, y1 + perp1y * outerWeight);
2091
- } else {
2092
- verts.push(x1 - perpX * innerWeight, y1 - perpY * innerWeight);
2093
- verts.push(omx, omy);
2094
- indexCount += round(
2095
- x1,
2096
- y1,
2097
- x1 - perpX * innerWeight,
2098
- y1 - perpY * innerWeight,
2099
- x1 - perp1x * innerWeight,
2100
- y1 - perp1y * innerWeight,
2101
- verts,
2102
- false
2103
- ) + 4;
2104
- verts.push(x1 - perp1x * innerWeight, y1 - perp1y * innerWeight);
2105
- verts.push(omx, omy);
2106
- }
2107
- } else {
2108
- verts.push(imx, imy);
2109
- verts.push(omx, omy);
2110
- }
2111
- } else {
2112
- verts.push(x1 - perpX * innerWeight, y1 - perpY * innerWeight);
2113
- verts.push(x1 + perpX * outerWeight, y1 + perpY * outerWeight);
2114
- if (style.join === "round") {
2115
- if (clockwise) {
2116
- indexCount += round(
2117
- x1,
2118
- y1,
2119
- x1 + perpX * outerWeight,
2120
- y1 + perpY * outerWeight,
2121
- x1 + perp1x * outerWeight,
2122
- y1 + perp1y * outerWeight,
2123
- verts,
2124
- true
2125
- ) + 2;
2126
- } else {
2127
- indexCount += round(
2128
- x1,
2129
- y1,
2130
- x1 - perpX * innerWeight,
2131
- y1 - perpY * innerWeight,
2132
- x1 - perp1x * innerWeight,
2133
- y1 - perp1y * innerWeight,
2134
- verts,
2135
- false
2136
- ) + 2;
2137
- }
2138
- } else if (style.join === "miter" && pDist / widthSquared <= miterLimitSquared) {
2139
- if (clockwise) {
2140
- verts.push(omx, omy);
2141
- verts.push(omx, omy);
2142
- } else {
2143
- verts.push(imx, imy);
2144
- verts.push(imx, imy);
2145
- }
2146
- indexCount += 2;
2147
- }
2148
- verts.push(x1 - perp1x * innerWeight, y1 - perp1y * innerWeight);
2149
- verts.push(x1 + perp1x * outerWeight, y1 + perp1y * outerWeight);
2150
- indexCount += 2;
2210
+ for (let j = 0; j < selectorList.length; j++) {
2211
+ stylesheets[selectorList[j]] = Object.assign(
2212
+ stylesheets[selectorList[j]] || {},
2213
+ { ...definitions }
2214
+ );
2151
2215
  }
2152
2216
  }
2153
- x0 = points[(length - 2) * 2];
2154
- y0 = points[(length - 2) * 2 + 1];
2155
- x1 = points[(length - 1) * 2];
2156
- y1 = points[(length - 1) * 2 + 1];
2157
- perpX = -(y0 - y1);
2158
- perpY = x0 - x1;
2159
- dist = Math.sqrt(perpX * perpX + perpY * perpY);
2160
- perpX /= dist;
2161
- perpY /= dist;
2162
- perpX *= width;
2163
- perpY *= width;
2164
- verts.push(x1 - perpX * innerWeight, y1 - perpY * innerWeight);
2165
- verts.push(x1 + perpX * outerWeight, y1 + perpY * outerWeight);
2166
- if (!closedShape) {
2167
- if (style.cap === "round") {
2168
- indexCount += round(
2169
- x1 - perpX * (innerWeight - outerWeight) * 0.5,
2170
- y1 - perpY * (innerWeight - outerWeight) * 0.5,
2171
- x1 - perpX * innerWeight,
2172
- y1 - perpY * innerWeight,
2173
- x1 + perpX * outerWeight,
2174
- y1 + perpY * outerWeight,
2175
- verts,
2176
- false
2177
- ) + 2;
2178
- } else if (style.cap === "square") {
2179
- indexCount += square(x1, y1, perpX, perpY, innerWeight, outerWeight, false, verts);
2217
+ }
2218
+
2219
+ function parseEllipseNode(node) {
2220
+ return new Path2D().ellipse(
2221
+ parseFloatWithUnits(node.getAttribute("cx") || 0),
2222
+ parseFloatWithUnits(node.getAttribute("cy") || 0),
2223
+ parseFloatWithUnits(node.getAttribute("rx") || 0),
2224
+ parseFloatWithUnits(node.getAttribute("ry") || 0),
2225
+ 0,
2226
+ 0,
2227
+ Math.PI * 2
2228
+ );
2229
+ }
2230
+
2231
+ function parseLineNode(node) {
2232
+ return new Path2D().moveTo(
2233
+ parseFloatWithUnits(node.getAttribute("x1") || 0),
2234
+ parseFloatWithUnits(node.getAttribute("y1") || 0)
2235
+ ).lineTo(
2236
+ parseFloatWithUnits(node.getAttribute("x2") || 0),
2237
+ parseFloatWithUnits(node.getAttribute("y2") || 0)
2238
+ );
2239
+ }
2240
+
2241
+ function parsePathNode(node) {
2242
+ const path = new Path2D();
2243
+ const d = node.getAttribute("d");
2244
+ if (!d || d === "none")
2245
+ return null;
2246
+ path.addData(d);
2247
+ return path;
2248
+ }
2249
+
2250
+ const RE$1 = /([+-]?(?:\d+(?:\.\d+)?|\.\d+)(?:e[+-]?\d+)?)(?:,|\s)([+-]?\d*\.?\d+(?:e[+-]?\d+)?)/g;
2251
+ function parsePolygonNode(node) {
2252
+ const path = new Path2D();
2253
+ let index = 0;
2254
+ node.getAttribute("points")?.replace(RE$1, (match, a, b) => {
2255
+ const x = parseFloatWithUnits(a);
2256
+ const y = parseFloatWithUnits(b);
2257
+ if (index === 0) {
2258
+ path.moveTo(x, y);
2259
+ } else {
2260
+ path.lineTo(x, y);
2261
+ }
2262
+ index++;
2263
+ return match;
2264
+ });
2265
+ path.currentCurve.autoClose = true;
2266
+ return path;
2267
+ }
2268
+
2269
+ const RE = /([+-]?(?:\d+(?:\.\d+)?|\.\d+)(?:e[+-]?\d+)?)(?:,|\s)([+-]?\d*\.?\d+(?:e[+-]?\d+)?)/g;
2270
+ function parsePolylineNode(node) {
2271
+ const path = new Path2D();
2272
+ let index = 0;
2273
+ node.getAttribute("points")?.replace(RE, (match, a, b) => {
2274
+ const x = parseFloatWithUnits(a);
2275
+ const y = parseFloatWithUnits(b);
2276
+ if (index === 0) {
2277
+ path.moveTo(x, y);
2278
+ } else {
2279
+ path.lineTo(x, y);
2180
2280
  }
2281
+ index++;
2282
+ return match;
2283
+ });
2284
+ path.currentCurve.autoClose = false;
2285
+ return path;
2286
+ }
2287
+
2288
+ function parseRectNode(node) {
2289
+ const x = parseFloatWithUnits(node.getAttribute("x") || 0);
2290
+ const y = parseFloatWithUnits(node.getAttribute("y") || 0);
2291
+ const rx = parseFloatWithUnits(node.getAttribute("rx") || node.getAttribute("ry") || 0);
2292
+ const ry = parseFloatWithUnits(node.getAttribute("ry") || node.getAttribute("rx") || 0);
2293
+ const w = parseFloatWithUnits(node.getAttribute("width"));
2294
+ const h = parseFloatWithUnits(node.getAttribute("height"));
2295
+ const bci = 1 - 0.551915024494;
2296
+ const path = new Path2D();
2297
+ path.moveTo(x + rx, y);
2298
+ path.lineTo(x + w - rx, y);
2299
+ if (rx !== 0 || ry !== 0) {
2300
+ path.bezierCurveTo(
2301
+ x + w - rx * bci,
2302
+ y,
2303
+ x + w,
2304
+ y + ry * bci,
2305
+ x + w,
2306
+ y + ry
2307
+ );
2308
+ }
2309
+ path.lineTo(x + w, y + h - ry);
2310
+ if (rx !== 0 || ry !== 0) {
2311
+ path.bezierCurveTo(
2312
+ x + w,
2313
+ y + h - ry * bci,
2314
+ x + w - rx * bci,
2315
+ y + h,
2316
+ x + w - rx,
2317
+ y + h
2318
+ );
2319
+ }
2320
+ path.lineTo(x + rx, y + h);
2321
+ if (rx !== 0 || ry !== 0) {
2322
+ path.bezierCurveTo(
2323
+ x + rx * bci,
2324
+ y + h,
2325
+ x,
2326
+ y + h - ry * bci,
2327
+ x,
2328
+ y + h - ry
2329
+ );
2181
2330
  }
2182
- const eps2 = curveEps * curveEps;
2183
- for (let i = indexStart; i < indexCount + indexStart - 2; ++i) {
2184
- x0 = verts[i * 2];
2185
- y0 = verts[i * 2 + 1];
2186
- x1 = verts[(i + 1) * 2];
2187
- y1 = verts[(i + 1) * 2 + 1];
2188
- x2 = verts[(i + 2) * 2];
2189
- y2 = verts[(i + 2) * 2 + 1];
2190
- if (Math.abs(x0 * (y1 - y2) + x1 * (y2 - y0) + x2 * (y0 - y1)) < eps2) {
2191
- continue;
2192
- }
2193
- indices.push(i, i + 1, i + 2);
2331
+ path.lineTo(x, y + ry);
2332
+ if (rx !== 0 || ry !== 0) {
2333
+ path.bezierCurveTo(x, y + ry * bci, x + rx * bci, y, x + rx, y);
2194
2334
  }
2195
- return {
2196
- vertices,
2197
- indices
2198
- };
2335
+ return path;
2199
2336
  }
2200
- function getOrientationOfPoints(points) {
2201
- const m = points.length;
2202
- if (m < 6) {
2203
- return 1;
2337
+
2338
+ function parseStyle(node, style, stylesheets) {
2339
+ style = Object.assign({}, style);
2340
+ let stylesheetStyles = {};
2341
+ if (node.hasAttribute("class")) {
2342
+ const classSelectors = node.getAttribute("class").split(/\s/).filter(Boolean).map((i) => i.trim());
2343
+ for (let i = 0; i < classSelectors.length; i++) {
2344
+ stylesheetStyles = Object.assign(stylesheetStyles, stylesheets[`.${classSelectors[i]}`]);
2345
+ }
2204
2346
  }
2205
- let area = 0;
2206
- for (let i = 0, x1 = points[m - 2], y1 = points[m - 1]; i < m; i += 2) {
2207
- const x2 = points[i];
2208
- const y2 = points[i + 1];
2209
- area += (x2 - x1) * (y2 + y1);
2210
- x1 = x2;
2211
- y1 = y2;
2347
+ if (node.hasAttribute("id")) {
2348
+ stylesheetStyles = Object.assign(stylesheetStyles, stylesheets[`#${node.getAttribute("id")}`]);
2212
2349
  }
2213
- if (area < 0) {
2214
- return -1;
2350
+ for (let len = node.style.length, i = 0; i < len; i++) {
2351
+ const name = node.style.item(i);
2352
+ const value = node.style.getPropertyValue(name);
2353
+ style[name] = value;
2354
+ stylesheetStyles[name] = value;
2215
2355
  }
2216
- return 1;
2217
- }
2218
- function square(x, y, nx, ny, innerWeight, outerWeight, clockwise, verts) {
2219
- const ix = x - nx * innerWeight;
2220
- const iy = y - ny * innerWeight;
2221
- const ox = x + nx * outerWeight;
2222
- const oy = y + ny * outerWeight;
2223
- let exx;
2224
- let eyy;
2225
- if (clockwise) {
2226
- exx = ny;
2227
- eyy = -nx;
2228
- } else {
2229
- exx = -ny;
2230
- eyy = nx;
2356
+ function addStyle(svgName, jsName, adjustFunction = copy) {
2357
+ if (node.hasAttribute(svgName))
2358
+ style[jsName] = adjustFunction(node.getAttribute(svgName));
2359
+ if (stylesheetStyles[svgName])
2360
+ style[jsName] = adjustFunction(stylesheetStyles[svgName]);
2231
2361
  }
2232
- const eix = ix + exx;
2233
- const eiy = iy + eyy;
2234
- const eox = ox + exx;
2235
- const eoy = oy + eyy;
2236
- verts.push(eix, eiy);
2237
- verts.push(eox, eoy);
2238
- return 2;
2239
- }
2240
- function round(cx, cy, sx, sy, ex, ey, verts, clockwise) {
2241
- const cx2p0x = sx - cx;
2242
- const cy2p0y = sy - cy;
2243
- let angle0 = Math.atan2(cx2p0x, cy2p0y);
2244
- let angle1 = Math.atan2(ex - cx, ey - cy);
2245
- if (clockwise && angle0 < angle1) {
2246
- angle0 += Math.PI * 2;
2247
- } else if (!clockwise && angle0 > angle1) {
2248
- angle1 += Math.PI * 2;
2362
+ function copy(v) {
2363
+ if (v.startsWith("url"))
2364
+ console.warn("url access in attributes is not implemented.");
2365
+ return v;
2249
2366
  }
2250
- let startAngle = angle0;
2251
- const angleDiff = angle1 - angle0;
2252
- const absAngleDiff = Math.abs(angleDiff);
2253
- const radius = Math.sqrt(cx2p0x * cx2p0x + cy2p0y * cy2p0y);
2254
- const segCount = (15 * absAngleDiff * Math.sqrt(radius) / Math.PI >> 0) + 1;
2255
- const angleInc = angleDiff / segCount;
2256
- startAngle += angleInc;
2257
- if (clockwise) {
2258
- verts.push(cx, cy);
2259
- verts.push(sx, sy);
2260
- for (let i = 1, angle = startAngle; i < segCount; i++, angle += angleInc) {
2261
- verts.push(cx, cy);
2262
- verts.push(cx + Math.sin(angle) * radius, cy + Math.cos(angle) * radius);
2367
+ function clamp(v) {
2368
+ return Math.max(0, Math.min(1, parseFloatWithUnits(v)));
2369
+ }
2370
+ function positive(v) {
2371
+ return Math.max(0, parseFloatWithUnits(v));
2372
+ }
2373
+ function array(v) {
2374
+ return v.split(" ").filter((v2) => v2 !== "").map((v2) => parseFloatWithUnits(v2));
2375
+ }
2376
+ addStyle("fill", "fill");
2377
+ addStyle("fill-opacity", "fillOpacity", clamp);
2378
+ addStyle("fill-rule", "fillRule");
2379
+ addStyle("opacity", "opacity", clamp);
2380
+ addStyle("stroke", "stroke");
2381
+ addStyle("stroke-opacity", "strokeOpacity", clamp);
2382
+ addStyle("stroke-width", "strokeWidth", positive);
2383
+ addStyle("stroke-linecap", "strokeLinecap");
2384
+ addStyle("stroke-linejoin", "strokeLinejoin");
2385
+ addStyle("stroke-miterlimit", "strokeMiterlimit", positive);
2386
+ addStyle("stroke-dasharray", "strokeDasharray", array);
2387
+ addStyle("stroke-dashoffset", "strokeDashoffset", parseFloatWithUnits);
2388
+ addStyle("visibility", "visibility");
2389
+ return style;
2390
+ }
2391
+
2392
+ function parseNode(node, style, paths = [], stylesheets = {}) {
2393
+ if (node.nodeType !== 1)
2394
+ return paths;
2395
+ let isDefsNode = false;
2396
+ let path = null;
2397
+ let _style = { ...style };
2398
+ switch (node.nodeName) {
2399
+ case "svg":
2400
+ _style = parseStyle(node, _style, stylesheets);
2401
+ break;
2402
+ case "style":
2403
+ parseCSSStylesheet(node, stylesheets);
2404
+ break;
2405
+ case "g":
2406
+ _style = parseStyle(node, _style, stylesheets);
2407
+ break;
2408
+ case "path":
2409
+ _style = parseStyle(node, _style, stylesheets);
2410
+ if (node.hasAttribute("d"))
2411
+ path = parsePathNode(node);
2412
+ break;
2413
+ case "rect":
2414
+ _style = parseStyle(node, _style, stylesheets);
2415
+ path = parseRectNode(node);
2416
+ break;
2417
+ case "polygon":
2418
+ _style = parseStyle(node, _style, stylesheets);
2419
+ path = parsePolygonNode(node);
2420
+ break;
2421
+ case "polyline":
2422
+ _style = parseStyle(node, _style, stylesheets);
2423
+ path = parsePolylineNode(node);
2424
+ break;
2425
+ case "circle":
2426
+ _style = parseStyle(node, _style, stylesheets);
2427
+ path = parseCircleNode(node);
2428
+ break;
2429
+ case "ellipse":
2430
+ _style = parseStyle(node, _style, stylesheets);
2431
+ path = parseEllipseNode(node);
2432
+ break;
2433
+ case "line":
2434
+ _style = parseStyle(node, _style, stylesheets);
2435
+ path = parseLineNode(node);
2436
+ break;
2437
+ case "defs":
2438
+ isDefsNode = true;
2439
+ break;
2440
+ case "use": {
2441
+ _style = parseStyle(node, _style, stylesheets);
2442
+ const href = node.getAttributeNS("http://www.w3.org/1999/xlink", "href") || node.getAttribute("href") || "";
2443
+ const usedNodeId = href.substring(1);
2444
+ const usedNode = node.viewportElement?.getElementById(usedNodeId);
2445
+ if (usedNode) {
2446
+ parseNode(usedNode, _style, paths, stylesheets);
2447
+ } else {
2448
+ console.warn(`'use node' references non-existent node id: ${usedNodeId}`);
2449
+ }
2450
+ break;
2263
2451
  }
2264
- verts.push(cx, cy);
2265
- verts.push(ex, ey);
2266
- } else {
2267
- verts.push(sx, sy);
2268
- verts.push(cx, cy);
2269
- for (let i = 1, angle = startAngle; i < segCount; i++, angle += angleInc) {
2270
- verts.push(cx + Math.sin(angle) * radius, cy + Math.cos(angle) * radius);
2271
- verts.push(cx, cy);
2452
+ default:
2453
+ console.warn(node);
2454
+ break;
2455
+ }
2456
+ if (_style.display === "none") {
2457
+ return paths;
2458
+ }
2459
+ const currentTransform = new Transform2D();
2460
+ const transformStack = [];
2461
+ const transform = getNodeTransform(node, currentTransform, transformStack);
2462
+ if (path) {
2463
+ path.applyTransform(currentTransform);
2464
+ paths.push(path);
2465
+ path.style = { ..._style };
2466
+ }
2467
+ const childNodes = node.childNodes;
2468
+ for (let i = 0, len = childNodes.length; i < len; i++) {
2469
+ const node2 = childNodes[i];
2470
+ if (isDefsNode && node2.nodeName !== "style" && node2.nodeName !== "defs")
2471
+ continue;
2472
+ parseNode(node2, _style, paths, stylesheets);
2473
+ }
2474
+ if (transform) {
2475
+ transformStack.pop();
2476
+ if (transformStack.length > 0) {
2477
+ currentTransform.copyFrom(transformStack[transformStack.length - 1]);
2478
+ } else {
2479
+ currentTransform.identity();
2272
2480
  }
2273
- verts.push(ex, ey);
2274
- verts.push(cx, cy);
2275
2481
  }
2276
- return segCount * 2;
2482
+ return paths;
2483
+ }
2484
+
2485
+ function svgToPath2DSet(svg) {
2486
+ const dom = svgToDom(svg);
2487
+ return new Path2DSet(
2488
+ parseNode(dom, {}),
2489
+ dom.getAttribute("viewBox")?.trim().split(" ").map((v) => Number(v))
2490
+ );
2277
2491
  }
2278
2492
 
2279
2493
  class Curve {
@@ -2294,7 +2508,7 @@ class Curve {
2294
2508
  if (isFunction) {
2295
2509
  transform(p);
2296
2510
  } else {
2297
- p.applyMatrix3(transform);
2511
+ transform.apply(p, p);
2298
2512
  }
2299
2513
  });
2300
2514
  return this;
@@ -2409,7 +2623,7 @@ class Curve {
2409
2623
  const delta = 1e-4;
2410
2624
  const t1 = Math.max(0, t - delta);
2411
2625
  const t2 = Math.min(1, t + delta);
2412
- return output.copy(this.getPoint(t2).sub(this.getPoint(t1)).normalize());
2626
+ return output.copyFrom(this.getPoint(t2).sub(this.getPoint(t1)).normalize());
2413
2627
  }
2414
2628
  getTangentAt(u, output) {
2415
2629
  return this.getTangent(this.getUToTMapping(u), output);
@@ -2444,8 +2658,8 @@ class Curve {
2444
2658
  const potins = this.getPoints();
2445
2659
  for (let i = 0, len = potins.length; i < len; i++) {
2446
2660
  const p = potins[i];
2447
- min.min(p);
2448
- max.max(p);
2661
+ min.clampMin(p);
2662
+ max.clampMax(p);
2449
2663
  }
2450
2664
  return { min: min.finite(), max: max.finite() };
2451
2665
  }
@@ -2497,18 +2711,18 @@ class Curve {
2497
2711
  });
2498
2712
  return this;
2499
2713
  }
2500
- copy(source) {
2714
+ copyFrom(source) {
2501
2715
  this.arcLengthDivision = source.arcLengthDivision;
2502
2716
  return this;
2503
2717
  }
2504
2718
  clone() {
2505
- return new this.constructor().copy(this);
2719
+ return new this.constructor().copyFrom(this);
2506
2720
  }
2507
2721
  }
2508
2722
 
2509
- const tempTransform0 = new Matrix3();
2510
- const tempTransform1 = new Matrix3();
2511
- const tempTransform2 = new Matrix3();
2723
+ const tempTransform0 = new Transform2D();
2724
+ const tempTransform1 = new Transform2D();
2725
+ const tempTransform2 = new Transform2D();
2512
2726
  const tempV2 = new Vector2();
2513
2727
  class RoundCurve extends Curve {
2514
2728
  constructor(_center = new Vector2(), _radius = new Vector2(), _diff = new Vector2(), rotate = 0, startAngle = 0, endAngle = Math.PI * 2, clockwise = false) {
@@ -2627,15 +2841,15 @@ class RoundCurve extends Curve {
2627
2841
  );
2628
2842
  return this;
2629
2843
  }
2630
- applyTransform(matrix) {
2844
+ applyTransform(transform) {
2631
2845
  tempV2.set(this.cx, this.cy);
2632
- tempV2.applyMatrix3(matrix);
2846
+ transform.apply(tempV2, tempV2);
2633
2847
  this.cx = tempV2.x;
2634
2848
  this.cy = tempV2.y;
2635
- if (isTransformSkewed(matrix)) {
2636
- transfEllipseGeneric(this, matrix);
2849
+ if (isTransformSkewed(transform)) {
2850
+ transfEllipseGeneric(this, transform);
2637
2851
  } else {
2638
- transfEllipseNoSkew(this, matrix);
2852
+ transfEllipseNoSkew(this, transform);
2639
2853
  }
2640
2854
  return this;
2641
2855
  }
@@ -2762,8 +2976,8 @@ class RoundCurve extends Curve {
2762
2976
  }
2763
2977
  return this._getAdaptiveVerticesByArc(output);
2764
2978
  }
2765
- copy(source) {
2766
- super.copy(source);
2979
+ copyFrom(source) {
2980
+ super.copyFrom(source);
2767
2981
  this.cx = source.cx;
2768
2982
  this.cy = source.cy;
2769
2983
  this.rx = source.rx;
@@ -2784,24 +2998,17 @@ function transfEllipseGeneric(curve, m) {
2784
2998
  const sinTheta = Math.sin(curve.rotate);
2785
2999
  const v1 = new Vector2(a * cosTheta, a * sinTheta);
2786
3000
  const v2 = new Vector2(-b * sinTheta, b * cosTheta);
2787
- const f1 = v1.applyMatrix3(m);
2788
- const f2 = v2.applyMatrix3(m);
2789
- const mF = tempTransform0.set(
2790
- f1.x,
2791
- f2.x,
2792
- 0,
2793
- f1.y,
2794
- f2.y,
2795
- 0,
2796
- 0,
2797
- 0,
2798
- 1
2799
- );
2800
- const mFInv = tempTransform1.copy(mF).invert();
2801
- const mFInvT = tempTransform2.copy(mFInv).transpose();
2802
- const mQ = mFInvT.multiply(mFInv);
2803
- const mQe = mQ.elements;
2804
- const ed = eigenDecomposition(mQe[0], mQe[1], mQe[4]);
3001
+ const f1x = m.a * v1.x + m.c * v1.y;
3002
+ const f1y = m.b * v1.x + m.d * v1.y;
3003
+ const f2x = m.a * v2.x + m.c * v2.y;
3004
+ const f2y = m.b * v2.x + m.d * v2.y;
3005
+ const mF = tempTransform0.set(f1x, f1y, f2x, f2y, 0, 0);
3006
+ const mFInv = tempTransform1.copyFrom(mF).affineInvert();
3007
+ const { a: ia, b: ib, c: ic, d: id } = mFInv;
3008
+ const qA = ia * ia + ib * ib;
3009
+ const qB = ic * ia + id * ib;
3010
+ const qD = ic * ic + id * id;
3011
+ const ed = eigenDecomposition(qA, qB, qD);
2805
3012
  const rt1sqrt = Math.sqrt(ed.rt1);
2806
3013
  const rt2sqrt = Math.sqrt(ed.rt2);
2807
3014
  curve.rx = 1 / rt1sqrt;
@@ -2813,27 +3020,21 @@ function transfEllipseGeneric(curve, m) {
2813
3020
  rt1sqrt,
2814
3021
  0,
2815
3022
  0,
2816
- 0,
2817
3023
  rt2sqrt,
2818
3024
  0,
2819
- 0,
2820
- 0,
2821
- 1
3025
+ 0
2822
3026
  );
2823
3027
  const mRT = tempTransform2.set(
2824
3028
  ed.cs,
2825
3029
  ed.sn,
2826
- 0,
2827
3030
  -ed.sn,
2828
3031
  ed.cs,
2829
3032
  0,
2830
- 0,
2831
- 0,
2832
- 1
3033
+ 0
2833
3034
  );
2834
- const mDRF = mDsqrt.multiply(mRT).multiply(mF);
3035
+ const mDRF = mDsqrt.append(mRT).append(mF);
2835
3036
  const transformAngle = (phi) => {
2836
- const { x: cosR, y: sinR } = new Vector2(Math.cos(phi), Math.sin(phi)).applyMatrix3(mDRF);
3037
+ const { x: cosR, y: sinR } = mDRF.apply({ x: Math.cos(phi), y: Math.sin(phi) });
2837
3038
  return Math.atan2(sinR, cosR);
2838
3039
  };
2839
3040
  curve.startAngle = transformAngle(curve.startAngle);
@@ -2844,11 +3045,10 @@ function transfEllipseGeneric(curve, m) {
2844
3045
  }
2845
3046
  }
2846
3047
  function transfEllipseNoSkew(curve, m) {
2847
- const sx = getTransformScaleX(m);
2848
- const sy = getTransformScaleY(m);
2849
- curve.rx *= sx;
2850
- curve.ry *= sy;
2851
- const theta = sx > Number.EPSILON ? Math.atan2(m.elements[1], m.elements[0]) : Math.atan2(-m.elements[3], m.elements[4]);
3048
+ const { scale } = m.decompose();
3049
+ curve.rx *= scale.x;
3050
+ curve.ry *= scale.y;
3051
+ const theta = scale.x > Number.EPSILON ? Math.atan2(m.b, m.a) : Math.atan2(-m.c, m.d);
2852
3052
  curve.rotate += theta;
2853
3053
  if (isTransformFlipped(m)) {
2854
3054
  curve.startAngle *= -1;
@@ -2857,25 +3057,14 @@ function transfEllipseNoSkew(curve, m) {
2857
3057
  }
2858
3058
  }
2859
3059
  function isTransformFlipped(m) {
2860
- const te = m.elements;
2861
- return te[0] * te[4] - te[1] * te[3] < 0;
3060
+ return m.a * m.d - m.c * m.b < 0;
2862
3061
  }
2863
3062
  function isTransformSkewed(m) {
2864
- const te = m.elements;
2865
- const basisDot = te[0] * te[3] + te[1] * te[4];
3063
+ const basisDot = m.a * m.c + m.b * m.d;
2866
3064
  if (basisDot === 0)
2867
3065
  return false;
2868
- const sx = getTransformScaleX(m);
2869
- const sy = getTransformScaleY(m);
2870
- return Math.abs(basisDot / (sx * sy)) > Number.EPSILON;
2871
- }
2872
- function getTransformScaleX(m) {
2873
- const te = m.elements;
2874
- return Math.sqrt(te[0] * te[0] + te[1] * te[1]);
2875
- }
2876
- function getTransformScaleY(m) {
2877
- const te = m.elements;
2878
- return Math.sqrt(te[3] * te[3] + te[4] * te[4]);
3066
+ const { scale } = m.decompose();
3067
+ return Math.abs(basisDot / (scale.x * scale.y)) > Number.EPSILON;
2879
3068
  }
2880
3069
  function eigenDecomposition(A, B, C) {
2881
3070
  let rt1, rt2, cs, sn, t;
@@ -2957,9 +3146,9 @@ class LineCurve extends Curve {
2957
3146
  }
2958
3147
  getPoint(t, output = new Vector2()) {
2959
3148
  if (t === 1) {
2960
- output.copy(this.p2);
3149
+ output.copyFrom(this.p2);
2961
3150
  } else {
2962
- output.copy(this.p2).sub(this.p1).scale(t).add(this.p1);
3151
+ output.copyFrom(this.p2).sub(this.p1).scale(t).add(this.p1);
2963
3152
  }
2964
3153
  return output;
2965
3154
  }
@@ -3025,10 +3214,10 @@ class LineCurve extends Curve {
3025
3214
  ctx.lineTo(p2.x, p2.y);
3026
3215
  return this;
3027
3216
  }
3028
- copy(source) {
3029
- super.copy(source);
3030
- this.p1.copy(source.p1);
3031
- this.p2.copy(source.p2);
3217
+ copyFrom(source) {
3218
+ super.copyFrom(source);
3219
+ this.p1.copyFrom(source.p1);
3220
+ this.p2.copyFrom(source.p2);
3032
3221
  return this;
3033
3222
  }
3034
3223
  }
@@ -3167,8 +3356,8 @@ class CompositeCurve extends Curve {
3167
3356
  this.curves.forEach((curve) => curve.drawTo(ctx));
3168
3357
  return this;
3169
3358
  }
3170
- copy(source) {
3171
- super.copy(source);
3359
+ copyFrom(source) {
3360
+ super.copyFrom(source);
3172
3361
  this.curves = source.curves.map((curve) => curve.clone());
3173
3362
  return this;
3174
3363
  }
@@ -3265,12 +3454,12 @@ class CubicBezierCurve extends Curve {
3265
3454
  ctx.bezierCurveTo(cp1.x, cp1.y, cp2.x, cp2.y, p2.x, p2.y);
3266
3455
  return this;
3267
3456
  }
3268
- copy(source) {
3269
- super.copy(source);
3270
- this.p1.copy(source.p1);
3271
- this.cp1.copy(source.cp1);
3272
- this.cp2.copy(source.cp2);
3273
- this.p2.copy(source.p2);
3457
+ copyFrom(source) {
3458
+ super.copyFrom(source);
3459
+ this.p1.copyFrom(source.p1);
3460
+ this.cp1.copyFrom(source.cp1);
3461
+ this.cp2.copyFrom(source.cp2);
3462
+ this.p2.copyFrom(source.p2);
3274
3463
  return this;
3275
3464
  }
3276
3465
  }
@@ -3339,8 +3528,8 @@ class EquilateralPloygonCurve extends PloygonCurve {
3339
3528
  this.curves = curves;
3340
3529
  return this;
3341
3530
  }
3342
- copy(source) {
3343
- super.copy(source);
3531
+ copyFrom(source) {
3532
+ super.copyFrom(source);
3344
3533
  this.cx = source.cx;
3345
3534
  this.cy = source.cy;
3346
3535
  this.radius = source.radius;
@@ -3412,11 +3601,11 @@ class QuadraticBezierCurve extends Curve {
3412
3601
  ctx.quadraticCurveTo(cp.x, cp.y, p2.x, p2.y);
3413
3602
  return this;
3414
3603
  }
3415
- copy(source) {
3416
- super.copy(source);
3417
- this.p1.copy(source.p1);
3418
- this.cp.copy(source.cp);
3419
- this.p2.copy(source.p2);
3604
+ copyFrom(source) {
3605
+ super.copyFrom(source);
3606
+ this.p1.copyFrom(source.p1);
3607
+ this.cp.copyFrom(source.cp);
3608
+ this.p2.copyFrom(source.p2);
3420
3609
  return this;
3421
3610
  }
3422
3611
  }
@@ -3463,8 +3652,8 @@ class RectangleCurve extends PloygonCurve {
3463
3652
  y + height
3464
3653
  ];
3465
3654
  }
3466
- copy(source) {
3467
- super.copy(source);
3655
+ copyFrom(source) {
3656
+ super.copyFrom(source);
3468
3657
  this.x = source.x;
3469
3658
  this.y = source.y;
3470
3659
  this.width = source.width;
@@ -3502,8 +3691,8 @@ class RoundRectangleCurve extends RoundCurve {
3502
3691
  ctx.roundRect(x, y, width, height, radius);
3503
3692
  return this;
3504
3693
  }
3505
- copy(source) {
3506
- super.copy(source);
3694
+ copyFrom(source) {
3695
+ super.copyFrom(source);
3507
3696
  this.x = source.x;
3508
3697
  this.y = source.y;
3509
3698
  this.width = source.width;
@@ -3537,8 +3726,8 @@ class SplineCurve extends Curve {
3537
3726
  getControlPointRefs() {
3538
3727
  return this.points;
3539
3728
  }
3540
- copy(source) {
3541
- super.copy(source);
3729
+ copyFrom(source) {
3730
+ super.copyFrom(source);
3542
3731
  this.points = [];
3543
3732
  for (let i = 0, len = source.points.length; i < len; i++) {
3544
3733
  this.points.push(source.points[i].clone());
@@ -3621,7 +3810,7 @@ class CurvePath extends CompositeCurve {
3621
3810
  const end = this.currentPoint;
3622
3811
  if (end && !start.equals(end)) {
3623
3812
  this.curves.push(new LineCurve(end.clone(), start.clone()));
3624
- end.copy(start);
3813
+ end.copyFrom(start);
3625
3814
  }
3626
3815
  this.startPoint = void 0;
3627
3816
  }
@@ -3762,8 +3951,8 @@ class CurvePath extends CompositeCurve {
3762
3951
  }
3763
3952
  return this;
3764
3953
  }
3765
- copy(source) {
3766
- super.copy(source);
3954
+ copyFrom(source) {
3955
+ super.copyFrom(source);
3767
3956
  this.autoClose = source.autoClose;
3768
3957
  this.currentPoint = source.currentPoint?.clone();
3769
3958
  return this;
@@ -3957,8 +4146,8 @@ class Path2D extends CompositeCurve {
3957
4146
  pointsB[1] ?? pointsB[0]
3958
4147
  );
3959
4148
  if (point) {
3960
- pointsA[pointsA.length - 1].copy(point);
3961
- pointsB[0].copy(point);
4149
+ pointsA[pointsA.length - 1].copyFrom(point);
4150
+ pointsB[0].copyFrom(point);
3962
4151
  }
3963
4152
  });
3964
4153
  });
@@ -3989,8 +4178,8 @@ class Path2D extends CompositeCurve {
3989
4178
  point.clone().add({ x: -halfStrokeWidth, y: -halfStrokeWidth })
3990
4179
  );
3991
4180
  }
3992
- min.min(...points);
3993
- max.max(...points);
4181
+ min.clampMin(...points);
4182
+ max.clampMax(...points);
3994
4183
  }
3995
4184
  }
3996
4185
  });
@@ -4128,8 +4317,8 @@ class Path2D extends CompositeCurve {
4128
4317
  }
4129
4318
  return `<path d="${this.toData()}" style="${cssText}"></path>`;
4130
4319
  }
4131
- copy(source) {
4132
- super.copy(source);
4320
+ copyFrom(source) {
4321
+ super.copyFrom(source);
4133
4322
  this.currentCurve = source.currentCurve.clone();
4134
4323
  this.style = { ...source.style };
4135
4324
  return this;
@@ -4296,4 +4485,4 @@ function applyFFD(point, grid, width = grid.width, height = grid.height) {
4296
4485
  point.set(x, y);
4297
4486
  }
4298
4487
 
4299
- export { ArcCurve, BoundingBox, CompositeCurve, CubicBezierCurve, Curve, CurvePath, EllipseCurve, EquilateralPloygonCurve, FFDControlGrid, LineCurve, Matrix3, Path2D, Path2DSet, PloygonCurve, QuadraticBezierCurve, RectangleCurve, RoundRectangleCurve, SplineCurve, Vector2, applyFFD, catmullRom, cubicBezier, drawPoint, fillTriangulate, getAdaptiveCubicBezierCurvePoints, getAdaptiveQuadraticBezierCurvePoints, getDirectedArea, getIntersectionPoint, nonzeroFillRule, parseArcCommand, parsePathDataArgs, quadraticBezier, setCanvasContext, strokeTriangulate, svgPathCommandsAddToPath2D, svgPathCommandsToData, svgPathDataToCommands, svgToDom, svgToPath2DSet, toKebabCase };
4488
+ export { ArcCurve, BoundingBox, CompositeCurve, CubicBezierCurve, Curve, CurvePath, EllipseCurve, EquilateralPloygonCurve, FFDControlGrid, LineCurve, PI, PI_2, Path2D, Path2DSet, PloygonCurve, QuadraticBezierCurve, RectangleCurve, RoundRectangleCurve, SplineCurve, Transform2D, Vector2, applyFFD, catmullRom, cubicBezier, drawPoint, fillTriangulate, getAdaptiveCubicBezierCurvePoints, getAdaptiveQuadraticBezierCurvePoints, getDirectedArea, getIntersectionPoint, nonzeroFillRule, parseArcCommand, parseCssArg, parseCssArgs, parseCssFunctions, parsePathDataArgs, quadraticBezier, setCanvasContext, strokeTriangulate, svgPathCommandsAddToPath2D, svgPathCommandsToData, svgPathDataToCommands, svgToDom, svgToPath2DSet, toKebabCase };