@emasoft/svg-matrix 1.0.30 → 1.0.31
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/bin/svg-matrix.js +310 -61
- package/bin/svglinter.cjs +102 -3
- package/bin/svgm.js +236 -27
- package/package.json +1 -1
- package/src/animation-optimization.js +137 -17
- package/src/animation-references.js +123 -6
- package/src/arc-length.js +213 -4
- package/src/bezier-analysis.js +217 -21
- package/src/bezier-intersections.js +275 -12
- package/src/browser-verify.js +237 -4
- package/src/clip-path-resolver.js +168 -0
- package/src/convert-path-data.js +479 -28
- package/src/css-specificity.js +73 -10
- package/src/douglas-peucker.js +219 -2
- package/src/flatten-pipeline.js +284 -26
- package/src/geometry-to-path.js +250 -25
- package/src/gjk-collision.js +236 -33
- package/src/index.js +261 -3
- package/src/inkscape-support.js +86 -28
- package/src/logger.js +48 -3
- package/src/marker-resolver.js +278 -74
- package/src/mask-resolver.js +265 -66
- package/src/matrix.js +44 -5
- package/src/mesh-gradient.js +352 -102
- package/src/off-canvas-detection.js +382 -13
- package/src/path-analysis.js +192 -18
- package/src/path-data-plugins.js +309 -5
- package/src/path-optimization.js +129 -5
- package/src/path-simplification.js +188 -32
- package/src/pattern-resolver.js +454 -106
- package/src/polygon-clip.js +324 -1
- package/src/svg-boolean-ops.js +226 -9
- package/src/svg-collections.js +7 -5
- package/src/svg-flatten.js +386 -62
- package/src/svg-parser.js +179 -8
- package/src/svg-rendering-context.js +235 -6
- package/src/svg-toolbox.js +45 -8
- package/src/svg2-polyfills.js +40 -10
- package/src/transform-decomposition.js +258 -32
- package/src/transform-optimization.js +259 -13
- package/src/transforms2d.js +82 -9
- package/src/transforms3d.js +62 -10
- package/src/use-symbol-resolver.js +286 -42
- package/src/vector.js +64 -8
- package/src/verification.js +392 -1
package/src/svg-boolean-ops.js
CHANGED
|
@@ -59,6 +59,15 @@ export function pointInPolygonWithRule(
|
|
|
59
59
|
polygon,
|
|
60
60
|
fillRule = FillRule.NONZERO,
|
|
61
61
|
) {
|
|
62
|
+
if (!pt || typeof pt !== "object") {
|
|
63
|
+
throw new Error("pointInPolygonWithRule: pt must be an object with x, y properties");
|
|
64
|
+
}
|
|
65
|
+
if (pt.x === undefined || pt.x === null || pt.y === undefined || pt.y === null) {
|
|
66
|
+
throw new Error("pointInPolygonWithRule: pt must have x and y properties");
|
|
67
|
+
}
|
|
68
|
+
if (!Array.isArray(polygon)) {
|
|
69
|
+
throw new Error("pointInPolygonWithRule: polygon must be an array");
|
|
70
|
+
}
|
|
62
71
|
const n = polygon.length;
|
|
63
72
|
if (n < 3) return -1;
|
|
64
73
|
|
|
@@ -109,6 +118,14 @@ export function pointInPolygonWithRule(
|
|
|
109
118
|
* @returns {boolean} True if point is on the segment
|
|
110
119
|
*/
|
|
111
120
|
function pointOnSegment(pt, a, b) {
|
|
121
|
+
if (!pt || !a || !b) {
|
|
122
|
+
throw new Error("pointOnSegment: pt, a, and b must be defined");
|
|
123
|
+
}
|
|
124
|
+
if (pt.x === undefined || pt.x === null || pt.y === undefined || pt.y === null ||
|
|
125
|
+
a.x === undefined || a.x === null || a.y === undefined || a.y === null ||
|
|
126
|
+
b.x === undefined || b.x === null || b.y === undefined || b.y === null) {
|
|
127
|
+
throw new Error("pointOnSegment: all points must have x and y properties");
|
|
128
|
+
}
|
|
112
129
|
const crossVal = cross(a, b, pt);
|
|
113
130
|
if (crossVal.abs().gt(EPSILON)) {
|
|
114
131
|
return false;
|
|
@@ -138,10 +155,26 @@ function pointOnSegment(pt, a, b) {
|
|
|
138
155
|
* @returns {Array} Polygon vertices (or path with curves for rounded corners)
|
|
139
156
|
*/
|
|
140
157
|
export function rectToPolygon(rect) {
|
|
158
|
+
if (!rect || typeof rect !== "object") {
|
|
159
|
+
throw new Error("rectToPolygon: rect must be an object");
|
|
160
|
+
}
|
|
161
|
+
if (rect.width === undefined || rect.width === null) {
|
|
162
|
+
throw new Error("rectToPolygon: rect must have width property");
|
|
163
|
+
}
|
|
164
|
+
if (rect.height === undefined || rect.height === null) {
|
|
165
|
+
throw new Error("rectToPolygon: rect must have height property");
|
|
166
|
+
}
|
|
141
167
|
const x = D(rect.x || 0);
|
|
142
168
|
const y = D(rect.y || 0);
|
|
143
169
|
const w = D(rect.width);
|
|
144
170
|
const h = D(rect.height);
|
|
171
|
+
|
|
172
|
+
if (w.lte(0)) {
|
|
173
|
+
throw new Error("rectToPolygon: width must be positive");
|
|
174
|
+
}
|
|
175
|
+
if (h.lte(0)) {
|
|
176
|
+
throw new Error("rectToPolygon: height must be positive");
|
|
177
|
+
}
|
|
145
178
|
const rx = D(rect.rx || 0);
|
|
146
179
|
const ry = D(rect.ry || rx); // ry defaults to rx if not specified
|
|
147
180
|
|
|
@@ -230,10 +263,23 @@ export function rectToPolygon(rect) {
|
|
|
230
263
|
* @returns {Array} Polygon vertices
|
|
231
264
|
*/
|
|
232
265
|
export function circleToPolygon(circle, segments = 32) {
|
|
266
|
+
if (!circle || typeof circle !== "object") {
|
|
267
|
+
throw new Error("circleToPolygon: circle must be an object");
|
|
268
|
+
}
|
|
269
|
+
if (circle.r === undefined || circle.r === null) {
|
|
270
|
+
throw new Error("circleToPolygon: circle must have r property");
|
|
271
|
+
}
|
|
272
|
+
if (typeof segments !== "number" || segments <= 0 || !Number.isFinite(segments)) {
|
|
273
|
+
throw new Error("circleToPolygon: segments must be a positive finite number");
|
|
274
|
+
}
|
|
233
275
|
const cx = D(circle.cx || 0);
|
|
234
276
|
const cy = D(circle.cy || 0);
|
|
235
277
|
const r = D(circle.r);
|
|
236
278
|
|
|
279
|
+
if (r.lte(0)) {
|
|
280
|
+
throw new Error("circleToPolygon: radius must be positive");
|
|
281
|
+
}
|
|
282
|
+
|
|
237
283
|
const vertices = [];
|
|
238
284
|
for (let i = 0; i < segments; i++) {
|
|
239
285
|
const angle = (2 * Math.PI * i) / segments;
|
|
@@ -256,11 +302,30 @@ export function circleToPolygon(circle, segments = 32) {
|
|
|
256
302
|
* @returns {Array} Polygon vertices
|
|
257
303
|
*/
|
|
258
304
|
export function ellipseToPolygon(ellipse, segments = 32) {
|
|
305
|
+
if (!ellipse || typeof ellipse !== "object") {
|
|
306
|
+
throw new Error("ellipseToPolygon: ellipse must be an object");
|
|
307
|
+
}
|
|
308
|
+
if (ellipse.rx === undefined || ellipse.rx === null) {
|
|
309
|
+
throw new Error("ellipseToPolygon: ellipse must have rx property");
|
|
310
|
+
}
|
|
311
|
+
if (ellipse.ry === undefined || ellipse.ry === null) {
|
|
312
|
+
throw new Error("ellipseToPolygon: ellipse must have ry property");
|
|
313
|
+
}
|
|
314
|
+
if (typeof segments !== "number" || segments <= 0 || !Number.isFinite(segments)) {
|
|
315
|
+
throw new Error("ellipseToPolygon: segments must be a positive finite number");
|
|
316
|
+
}
|
|
259
317
|
const cx = D(ellipse.cx || 0);
|
|
260
318
|
const cy = D(ellipse.cy || 0);
|
|
261
319
|
const rx = D(ellipse.rx);
|
|
262
320
|
const ry = D(ellipse.ry);
|
|
263
321
|
|
|
322
|
+
if (rx.lte(0)) {
|
|
323
|
+
throw new Error("ellipseToPolygon: rx must be positive");
|
|
324
|
+
}
|
|
325
|
+
if (ry.lte(0)) {
|
|
326
|
+
throw new Error("ellipseToPolygon: ry must be positive");
|
|
327
|
+
}
|
|
328
|
+
|
|
264
329
|
const vertices = [];
|
|
265
330
|
for (let i = 0; i < segments; i++) {
|
|
266
331
|
const angle = (2 * Math.PI * i) / segments;
|
|
@@ -283,12 +348,37 @@ export function ellipseToPolygon(ellipse, segments = 32) {
|
|
|
283
348
|
* @returns {Array} Polygon vertices representing stroked line
|
|
284
349
|
*/
|
|
285
350
|
export function lineToPolygon(line, stroke = { width: 1, linecap: "butt" }) {
|
|
351
|
+
if (!line || typeof line !== "object") {
|
|
352
|
+
throw new Error("lineToPolygon: line must be an object");
|
|
353
|
+
}
|
|
354
|
+
if (line.x1 === undefined || line.x1 === null) {
|
|
355
|
+
throw new Error("lineToPolygon: line must have x1 property");
|
|
356
|
+
}
|
|
357
|
+
if (line.y1 === undefined || line.y1 === null) {
|
|
358
|
+
throw new Error("lineToPolygon: line must have y1 property");
|
|
359
|
+
}
|
|
360
|
+
if (line.x2 === undefined || line.x2 === null) {
|
|
361
|
+
throw new Error("lineToPolygon: line must have x2 property");
|
|
362
|
+
}
|
|
363
|
+
if (line.y2 === undefined || line.y2 === null) {
|
|
364
|
+
throw new Error("lineToPolygon: line must have y2 property");
|
|
365
|
+
}
|
|
366
|
+
if (!stroke || typeof stroke !== "object") {
|
|
367
|
+
throw new Error("lineToPolygon: stroke must be an object");
|
|
368
|
+
}
|
|
369
|
+
if (stroke.width === undefined || stroke.width === null) {
|
|
370
|
+
throw new Error("lineToPolygon: stroke must have width property");
|
|
371
|
+
}
|
|
286
372
|
const x1 = D(line.x1);
|
|
287
373
|
const y1 = D(line.y1);
|
|
288
374
|
const x2 = D(line.x2);
|
|
289
375
|
const y2 = D(line.y2);
|
|
290
376
|
const halfWidth = D(stroke.width).div(2);
|
|
291
377
|
|
|
378
|
+
if (halfWidth.lte(0)) {
|
|
379
|
+
throw new Error("lineToPolygon: stroke width must be positive");
|
|
380
|
+
}
|
|
381
|
+
|
|
292
382
|
// Direction vector
|
|
293
383
|
const dx = x2.minus(x1);
|
|
294
384
|
const dy = y2.minus(y1);
|
|
@@ -365,15 +455,46 @@ export function lineToPolygon(line, stroke = { width: 1, linecap: "butt" }) {
|
|
|
365
455
|
* @returns {Array} Polygon vertices
|
|
366
456
|
*/
|
|
367
457
|
export function svgPolygonToPolygon(points) {
|
|
458
|
+
if (!points) {
|
|
459
|
+
throw new Error("svgPolygonToPolygon: points must be defined");
|
|
460
|
+
}
|
|
461
|
+
|
|
368
462
|
if (Array.isArray(points)) {
|
|
369
|
-
|
|
463
|
+
if (points.length === 0) {
|
|
464
|
+
throw new Error("svgPolygonToPolygon: points array cannot be empty");
|
|
465
|
+
}
|
|
466
|
+
return points.map((p) => {
|
|
467
|
+
if (!p || typeof p !== "object" || p.x === undefined || p.y === undefined) {
|
|
468
|
+
throw new Error("svgPolygonToPolygon: each point must have x and y properties");
|
|
469
|
+
}
|
|
470
|
+
return point(p.x, p.y);
|
|
471
|
+
});
|
|
472
|
+
}
|
|
473
|
+
|
|
474
|
+
if (typeof points !== "string") {
|
|
475
|
+
throw new Error("svgPolygonToPolygon: points must be a string or array");
|
|
370
476
|
}
|
|
371
477
|
|
|
372
478
|
// Parse SVG points string
|
|
373
|
-
const
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
479
|
+
const trimmed = points.trim();
|
|
480
|
+
if (trimmed === "") {
|
|
481
|
+
throw new Error("svgPolygonToPolygon: points string cannot be empty");
|
|
482
|
+
}
|
|
483
|
+
|
|
484
|
+
const coords = trimmed.split(/[\s,]+/).map(Number);
|
|
485
|
+
|
|
486
|
+
if (coords.length < 2) {
|
|
487
|
+
throw new Error("svgPolygonToPolygon: points must contain at least one coordinate pair");
|
|
488
|
+
}
|
|
489
|
+
|
|
490
|
+
if (coords.length % 2 !== 0) {
|
|
491
|
+
throw new Error("svgPolygonToPolygon: points must contain an even number of coordinates");
|
|
492
|
+
}
|
|
493
|
+
|
|
494
|
+
if (coords.some((c) => !Number.isFinite(c))) {
|
|
495
|
+
throw new Error("svgPolygonToPolygon: all coordinates must be finite numbers");
|
|
496
|
+
}
|
|
497
|
+
|
|
377
498
|
const vertices = [];
|
|
378
499
|
for (let i = 0; i < coords.length; i += 2) {
|
|
379
500
|
vertices.push(point(coords[i], coords[i + 1]));
|
|
@@ -397,7 +518,21 @@ export function svgPolygonToPolygon(points) {
|
|
|
397
518
|
* @returns {Object} {outer: Array, inner: Array} offset polygons
|
|
398
519
|
*/
|
|
399
520
|
export function offsetPolygon(polygon, distance, options = {}) {
|
|
521
|
+
if (!Array.isArray(polygon)) {
|
|
522
|
+
throw new Error("offsetPolygon: polygon must be an array");
|
|
523
|
+
}
|
|
524
|
+
if (distance === undefined || distance === null) {
|
|
525
|
+
throw new Error("offsetPolygon: distance must be defined");
|
|
526
|
+
}
|
|
527
|
+
if (!options || typeof options !== "object") {
|
|
528
|
+
throw new Error("offsetPolygon: options must be an object");
|
|
529
|
+
}
|
|
530
|
+
|
|
400
531
|
const dist = D(distance);
|
|
532
|
+
if (dist.lte(0)) {
|
|
533
|
+
throw new Error("offsetPolygon: distance must be positive");
|
|
534
|
+
}
|
|
535
|
+
|
|
401
536
|
const linejoin = options.linejoin || "miter";
|
|
402
537
|
const miterLimit = D(options.miterLimit || 4);
|
|
403
538
|
|
|
@@ -545,7 +680,21 @@ export function offsetPolygon(polygon, distance, options = {}) {
|
|
|
545
680
|
* @returns {Array} Polygon representing the stroke area
|
|
546
681
|
*/
|
|
547
682
|
export function strokeToFilledPolygon(polygon, strokeProps) {
|
|
548
|
-
|
|
683
|
+
if (!Array.isArray(polygon)) {
|
|
684
|
+
throw new Error("strokeToFilledPolygon: polygon must be an array");
|
|
685
|
+
}
|
|
686
|
+
if (!strokeProps || typeof strokeProps !== "object") {
|
|
687
|
+
throw new Error("strokeToFilledPolygon: strokeProps must be an object");
|
|
688
|
+
}
|
|
689
|
+
if (strokeProps.width === undefined || strokeProps.width === null) {
|
|
690
|
+
throw new Error("strokeToFilledPolygon: strokeProps must have width property");
|
|
691
|
+
}
|
|
692
|
+
|
|
693
|
+
const halfWidth = D(strokeProps.width).div(2);
|
|
694
|
+
if (halfWidth.lte(0)) {
|
|
695
|
+
throw new Error("strokeToFilledPolygon: width must be positive");
|
|
696
|
+
}
|
|
697
|
+
|
|
549
698
|
const offset = offsetPolygon(polygon, halfWidth, strokeProps);
|
|
550
699
|
|
|
551
700
|
// The stroke area is the outer path with the inner path as a hole
|
|
@@ -567,10 +716,25 @@ export function strokeToFilledPolygon(polygon, strokeProps) {
|
|
|
567
716
|
* @returns {Array<Array>} Array of polygon segments
|
|
568
717
|
*/
|
|
569
718
|
export function applyDashArray(polygon, dashArray, dashOffset = 0) {
|
|
719
|
+
if (!Array.isArray(polygon)) {
|
|
720
|
+
throw new Error("applyDashArray: polygon must be an array");
|
|
721
|
+
}
|
|
722
|
+
if (dashOffset === undefined || dashOffset === null) {
|
|
723
|
+
throw new Error("applyDashArray: dashOffset must be defined");
|
|
724
|
+
}
|
|
725
|
+
|
|
570
726
|
if (!dashArray || dashArray.length === 0) {
|
|
571
727
|
return [polygon];
|
|
572
728
|
}
|
|
573
729
|
|
|
730
|
+
if (!Array.isArray(dashArray)) {
|
|
731
|
+
throw new Error("applyDashArray: dashArray must be an array");
|
|
732
|
+
}
|
|
733
|
+
|
|
734
|
+
if (dashArray.some((d) => typeof d !== "number" || !Number.isFinite(d) || d < 0)) {
|
|
735
|
+
throw new Error("applyDashArray: all dash values must be non-negative finite numbers");
|
|
736
|
+
}
|
|
737
|
+
|
|
574
738
|
// Normalize dash array (must have even length)
|
|
575
739
|
const dashes =
|
|
576
740
|
dashArray.length % 2 === 0
|
|
@@ -680,9 +844,19 @@ export function applyDashArray(polygon, dashArray, dashOffset = 0) {
|
|
|
680
844
|
*/
|
|
681
845
|
export class SVGRegion {
|
|
682
846
|
constructor(options = {}) {
|
|
847
|
+
if (!options || typeof options !== "object") {
|
|
848
|
+
throw new Error("SVGRegion constructor: options must be an object");
|
|
849
|
+
}
|
|
683
850
|
this.fillPolygons = options.fillPolygons || []; // Array of polygons
|
|
684
851
|
this.fillRule = options.fillRule || FillRule.NONZERO;
|
|
685
852
|
this.strokePolygons = options.strokePolygons || []; // Array of stroked regions
|
|
853
|
+
|
|
854
|
+
if (!Array.isArray(this.fillPolygons)) {
|
|
855
|
+
throw new Error("SVGRegion constructor: fillPolygons must be an array");
|
|
856
|
+
}
|
|
857
|
+
if (!Array.isArray(this.strokePolygons)) {
|
|
858
|
+
throw new Error("SVGRegion constructor: strokePolygons must be an array");
|
|
859
|
+
}
|
|
686
860
|
}
|
|
687
861
|
|
|
688
862
|
/**
|
|
@@ -694,6 +868,16 @@ export class SVGRegion {
|
|
|
694
868
|
* @returns {SVGRegion}
|
|
695
869
|
*/
|
|
696
870
|
static fromElement(type, props, style = {}) {
|
|
871
|
+
if (!type || typeof type !== "string") {
|
|
872
|
+
throw new Error("SVGRegion.fromElement: type must be a non-empty string");
|
|
873
|
+
}
|
|
874
|
+
if (!props || typeof props !== "object") {
|
|
875
|
+
throw new Error("SVGRegion.fromElement: props must be an object");
|
|
876
|
+
}
|
|
877
|
+
if (!style || typeof style !== "object") {
|
|
878
|
+
throw new Error("SVGRegion.fromElement: style must be an object");
|
|
879
|
+
}
|
|
880
|
+
|
|
697
881
|
let polygon;
|
|
698
882
|
|
|
699
883
|
switch (type) {
|
|
@@ -727,7 +911,7 @@ export class SVGRegion {
|
|
|
727
911
|
}
|
|
728
912
|
|
|
729
913
|
// Add stroke region if element has stroke
|
|
730
|
-
if (style.stroke !== "none" && style.strokeWidth > 0) {
|
|
914
|
+
if (style.stroke !== "none" && typeof style.strokeWidth === "number" && style.strokeWidth > 0) {
|
|
731
915
|
const sourcePolygon =
|
|
732
916
|
type === "line"
|
|
733
917
|
? lineToPolygon(props, {
|
|
@@ -740,7 +924,7 @@ export class SVGRegion {
|
|
|
740
924
|
let strokePolygons;
|
|
741
925
|
|
|
742
926
|
// Apply dash array if present
|
|
743
|
-
if (style.strokeDasharray && style.strokeDasharray.length > 0) {
|
|
927
|
+
if (Array.isArray(style.strokeDasharray) && style.strokeDasharray.length > 0) {
|
|
744
928
|
const dashedSegments = applyDashArray(
|
|
745
929
|
sourcePolygon,
|
|
746
930
|
style.strokeDasharray,
|
|
@@ -786,6 +970,13 @@ export class SVGRegion {
|
|
|
786
970
|
* @returns {boolean}
|
|
787
971
|
*/
|
|
788
972
|
containsPoint(pt) {
|
|
973
|
+
if (!pt || typeof pt !== "object") {
|
|
974
|
+
throw new Error("SVGRegion.containsPoint: pt must be an object");
|
|
975
|
+
}
|
|
976
|
+
if (pt.x === undefined || pt.x === null || pt.y === undefined || pt.y === null) {
|
|
977
|
+
throw new Error("SVGRegion.containsPoint: pt must have x and y properties");
|
|
978
|
+
}
|
|
979
|
+
|
|
789
980
|
// Check fill polygons with fill rule
|
|
790
981
|
for (const poly of this.fillPolygons) {
|
|
791
982
|
if (pointInPolygonWithRule(pt, poly, this.fillRule) >= 0) {
|
|
@@ -816,6 +1007,13 @@ export class SVGRegion {
|
|
|
816
1007
|
* @returns {SVGRegion} Intersection region
|
|
817
1008
|
*/
|
|
818
1009
|
export function regionIntersection(regionA, regionB) {
|
|
1010
|
+
if (!regionA || !(regionA instanceof SVGRegion)) {
|
|
1011
|
+
throw new Error("regionIntersection: regionA must be an SVGRegion instance");
|
|
1012
|
+
}
|
|
1013
|
+
if (!regionB || !(regionB instanceof SVGRegion)) {
|
|
1014
|
+
throw new Error("regionIntersection: regionB must be an SVGRegion instance");
|
|
1015
|
+
}
|
|
1016
|
+
|
|
819
1017
|
const resultPolygons = [];
|
|
820
1018
|
|
|
821
1019
|
const polygonsA = regionA.getAllPolygons();
|
|
@@ -846,7 +1044,12 @@ export function regionIntersection(regionA, regionB) {
|
|
|
846
1044
|
* @returns {SVGRegion} Union region
|
|
847
1045
|
*/
|
|
848
1046
|
export function regionUnion(regionA, regionB) {
|
|
849
|
-
|
|
1047
|
+
if (!regionA || !(regionA instanceof SVGRegion)) {
|
|
1048
|
+
throw new Error("regionUnion: regionA must be an SVGRegion instance");
|
|
1049
|
+
}
|
|
1050
|
+
if (!regionB || !(regionB instanceof SVGRegion)) {
|
|
1051
|
+
throw new Error("regionUnion: regionB must be an SVGRegion instance");
|
|
1052
|
+
}
|
|
850
1053
|
|
|
851
1054
|
const polygonsA = regionA.getAllPolygons();
|
|
852
1055
|
const polygonsB = regionB.getAllPolygons();
|
|
@@ -899,6 +1102,13 @@ export function regionUnion(regionA, regionB) {
|
|
|
899
1102
|
* @returns {SVGRegion} Difference region
|
|
900
1103
|
*/
|
|
901
1104
|
export function regionDifference(regionA, regionB) {
|
|
1105
|
+
if (!regionA || !(regionA instanceof SVGRegion)) {
|
|
1106
|
+
throw new Error("regionDifference: regionA must be an SVGRegion instance");
|
|
1107
|
+
}
|
|
1108
|
+
if (!regionB || !(regionB instanceof SVGRegion)) {
|
|
1109
|
+
throw new Error("regionDifference: regionB must be an SVGRegion instance");
|
|
1110
|
+
}
|
|
1111
|
+
|
|
902
1112
|
let resultPolygons = regionA.getAllPolygons().map((p) => [...p]);
|
|
903
1113
|
|
|
904
1114
|
const polygonsB = regionB.getAllPolygons();
|
|
@@ -933,6 +1143,13 @@ export function regionDifference(regionA, regionB) {
|
|
|
933
1143
|
* @returns {SVGRegion} XOR region
|
|
934
1144
|
*/
|
|
935
1145
|
export function regionXOR(regionA, regionB) {
|
|
1146
|
+
if (!regionA || !(regionA instanceof SVGRegion)) {
|
|
1147
|
+
throw new Error("regionXOR: regionA must be an SVGRegion instance");
|
|
1148
|
+
}
|
|
1149
|
+
if (!regionB || !(regionB instanceof SVGRegion)) {
|
|
1150
|
+
throw new Error("regionXOR: regionB must be an SVGRegion instance");
|
|
1151
|
+
}
|
|
1152
|
+
|
|
936
1153
|
const diffAB = regionDifference(regionA, regionB);
|
|
937
1154
|
const diffBA = regionDifference(regionB, regionA);
|
|
938
1155
|
|
package/src/svg-collections.js
CHANGED
|
@@ -173,6 +173,11 @@ export const editorNamespaces = new Set([
|
|
|
173
173
|
'http://www.w3.org/1999/02/22-rdf-syntax-ns#',
|
|
174
174
|
'http://www.serif.com/',
|
|
175
175
|
'http://www.vector.evaxdesign.sk',
|
|
176
|
+
// Additional editor namespaces
|
|
177
|
+
'http://www.corel.com/coreldraw/svg',
|
|
178
|
+
'http://gravit.io/ns',
|
|
179
|
+
'http://serif.com/affinity',
|
|
180
|
+
'http://canva.com/ns',
|
|
176
181
|
]);
|
|
177
182
|
|
|
178
183
|
// ============================================================================
|
|
@@ -921,6 +926,8 @@ export const pseudoClasses = {
|
|
|
921
926
|
|
|
922
927
|
/**
|
|
923
928
|
* Additional editor namespaces beyond the base set
|
|
929
|
+
* Note: These are now integrated directly into editorNamespaces above
|
|
930
|
+
* to avoid post-export mutation issues. This export is kept for backwards compatibility.
|
|
924
931
|
*/
|
|
925
932
|
export const additionalEditorNamespaces = new Set([
|
|
926
933
|
// CorelDraw
|
|
@@ -932,8 +939,3 @@ export const additionalEditorNamespaces = new Set([
|
|
|
932
939
|
// Canva
|
|
933
940
|
'http://canva.com/ns',
|
|
934
941
|
]);
|
|
935
|
-
|
|
936
|
-
// Merge additional namespaces into main set at initialization (avoid post-export mutation)
|
|
937
|
-
for (const ns of additionalEditorNamespaces) {
|
|
938
|
-
editorNamespaces.add(ns);
|
|
939
|
-
}
|