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