@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/flatten-pipeline.js
CHANGED
|
@@ -84,6 +84,19 @@ const DEFAULT_OPTIONS = {
|
|
|
84
84
|
* @returns {{svg: string, stats: Object}} Flattened SVG and statistics
|
|
85
85
|
*/
|
|
86
86
|
export function flattenSVG(svgString, options = {}) {
|
|
87
|
+
// Validate required parameters
|
|
88
|
+
if (svgString === null || svgString === undefined) {
|
|
89
|
+
throw new Error("flattenSVG: svgString parameter is required");
|
|
90
|
+
}
|
|
91
|
+
if (typeof svgString !== "string") {
|
|
92
|
+
throw new Error(
|
|
93
|
+
`flattenSVG: svgString must be a string, got ${typeof svgString}`,
|
|
94
|
+
);
|
|
95
|
+
}
|
|
96
|
+
if (svgString.trim().length === 0) {
|
|
97
|
+
throw new Error("flattenSVG: svgString cannot be empty");
|
|
98
|
+
}
|
|
99
|
+
|
|
87
100
|
const opts = { ...DEFAULT_OPTIONS, ...options };
|
|
88
101
|
const stats = {
|
|
89
102
|
useResolved: 0,
|
|
@@ -260,6 +273,17 @@ function containsPreserveElements(el) {
|
|
|
260
273
|
}
|
|
261
274
|
|
|
262
275
|
function resolveAllUseElements(root, defsMap, opts) {
|
|
276
|
+
// Validate parameters
|
|
277
|
+
if (!root || !root.getElementsByTagName) {
|
|
278
|
+
throw new Error("resolveAllUseElements: invalid root element");
|
|
279
|
+
}
|
|
280
|
+
if (!defsMap || !(defsMap instanceof Map)) {
|
|
281
|
+
throw new Error("resolveAllUseElements: defsMap must be a Map");
|
|
282
|
+
}
|
|
283
|
+
if (!opts || typeof opts !== "object") {
|
|
284
|
+
throw new Error("resolveAllUseElements: opts must be an object");
|
|
285
|
+
}
|
|
286
|
+
|
|
263
287
|
const errors = [];
|
|
264
288
|
let count = 0;
|
|
265
289
|
|
|
@@ -306,6 +330,11 @@ function resolveAllUseElements(root, defsMap, opts) {
|
|
|
306
330
|
// Get use element positioning and transform
|
|
307
331
|
const useX = parseFloat(useEl.getAttribute("x") || "0");
|
|
308
332
|
const useY = parseFloat(useEl.getAttribute("y") || "0");
|
|
333
|
+
// Validate parseFloat results to prevent NaN propagation
|
|
334
|
+
if (isNaN(useX) || isNaN(useY)) {
|
|
335
|
+
errors.push(`use: invalid x/y coordinates for element #${refId}`);
|
|
336
|
+
continue;
|
|
337
|
+
}
|
|
309
338
|
const useTransform = useEl.getAttribute("transform") || "";
|
|
310
339
|
|
|
311
340
|
// Build combined transform: use transform + translation from x/y
|
|
@@ -436,6 +465,17 @@ function resolveAllUseElements(root, defsMap, opts) {
|
|
|
436
465
|
* @private
|
|
437
466
|
*/
|
|
438
467
|
function resolveAllMarkers(root, defsMap, opts) {
|
|
468
|
+
// Validate parameters
|
|
469
|
+
if (!root || !root.getElementsByTagName) {
|
|
470
|
+
throw new Error("resolveAllMarkers: invalid root element");
|
|
471
|
+
}
|
|
472
|
+
if (!defsMap || !(defsMap instanceof Map)) {
|
|
473
|
+
throw new Error("resolveAllMarkers: defsMap must be a Map");
|
|
474
|
+
}
|
|
475
|
+
if (!opts || typeof opts !== "object") {
|
|
476
|
+
throw new Error("resolveAllMarkers: opts must be an object");
|
|
477
|
+
}
|
|
478
|
+
|
|
439
479
|
const errors = [];
|
|
440
480
|
let count = 0;
|
|
441
481
|
|
|
@@ -517,6 +557,17 @@ function resolveAllMarkers(root, defsMap, opts) {
|
|
|
517
557
|
* @private
|
|
518
558
|
*/
|
|
519
559
|
function resolveAllPatterns(root, defsMap, opts) {
|
|
560
|
+
// Validate parameters
|
|
561
|
+
if (!root || !root.getElementsByTagName) {
|
|
562
|
+
throw new Error("resolveAllPatterns: invalid root element");
|
|
563
|
+
}
|
|
564
|
+
if (!defsMap || !(defsMap instanceof Map)) {
|
|
565
|
+
throw new Error("resolveAllPatterns: defsMap must be a Map");
|
|
566
|
+
}
|
|
567
|
+
if (!opts || typeof opts !== "object") {
|
|
568
|
+
throw new Error("resolveAllPatterns: opts must be an object");
|
|
569
|
+
}
|
|
570
|
+
|
|
520
571
|
const errors = [];
|
|
521
572
|
let count = 0;
|
|
522
573
|
|
|
@@ -617,6 +668,17 @@ function resolveAllPatterns(root, defsMap, opts) {
|
|
|
617
668
|
* @private
|
|
618
669
|
*/
|
|
619
670
|
function resolveAllMasks(root, defsMap, opts) {
|
|
671
|
+
// Validate parameters
|
|
672
|
+
if (!root || !root.getElementsByTagName) {
|
|
673
|
+
throw new Error("resolveAllMasks: invalid root element");
|
|
674
|
+
}
|
|
675
|
+
if (!defsMap || !(defsMap instanceof Map)) {
|
|
676
|
+
throw new Error("resolveAllMasks: defsMap must be a Map");
|
|
677
|
+
}
|
|
678
|
+
if (!opts || typeof opts !== "object") {
|
|
679
|
+
throw new Error("resolveAllMasks: opts must be an object");
|
|
680
|
+
}
|
|
681
|
+
|
|
620
682
|
const errors = [];
|
|
621
683
|
let count = 0;
|
|
622
684
|
|
|
@@ -718,6 +780,20 @@ function resolveAllMasks(root, defsMap, opts) {
|
|
|
718
780
|
* @private
|
|
719
781
|
*/
|
|
720
782
|
function applyAllClipPaths(root, defsMap, opts, stats) {
|
|
783
|
+
// Validate parameters
|
|
784
|
+
if (!root || !root.getElementsByTagName) {
|
|
785
|
+
throw new Error("applyAllClipPaths: invalid root element");
|
|
786
|
+
}
|
|
787
|
+
if (!defsMap || !(defsMap instanceof Map)) {
|
|
788
|
+
throw new Error("applyAllClipPaths: defsMap must be a Map");
|
|
789
|
+
}
|
|
790
|
+
if (!opts || typeof opts !== "object") {
|
|
791
|
+
throw new Error("applyAllClipPaths: opts must be an object");
|
|
792
|
+
}
|
|
793
|
+
if (!stats || typeof stats !== "object") {
|
|
794
|
+
throw new Error("applyAllClipPaths: stats must be an object");
|
|
795
|
+
}
|
|
796
|
+
|
|
721
797
|
const errors = [];
|
|
722
798
|
let count = 0;
|
|
723
799
|
|
|
@@ -768,12 +844,14 @@ function applyAllClipPaths(root, defsMap, opts, stats) {
|
|
|
768
844
|
|
|
769
845
|
// Get clip path data from clipPath element's children
|
|
770
846
|
let clipPathData = "";
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
847
|
+
if (clipPathEl.children && clipPathEl.children.length > 0) {
|
|
848
|
+
for (const child of clipPathEl.children) {
|
|
849
|
+
// Use tagName check instead of instanceof
|
|
850
|
+
if (child && child.tagName) {
|
|
851
|
+
const childPath = getElementPathData(child, opts.precision);
|
|
852
|
+
if (childPath) {
|
|
853
|
+
clipPathData += (clipPathData ? " " : "") + childPath;
|
|
854
|
+
}
|
|
777
855
|
}
|
|
778
856
|
}
|
|
779
857
|
}
|
|
@@ -914,6 +992,17 @@ function applyAllClipPaths(root, defsMap, opts, stats) {
|
|
|
914
992
|
* @private
|
|
915
993
|
*/
|
|
916
994
|
function flattenAllTransforms(root, opts, stats) {
|
|
995
|
+
// Validate parameters
|
|
996
|
+
if (!root || !root.getElementsByTagName) {
|
|
997
|
+
throw new Error("flattenAllTransforms: invalid root element");
|
|
998
|
+
}
|
|
999
|
+
if (!opts || typeof opts !== "object") {
|
|
1000
|
+
throw new Error("flattenAllTransforms: opts must be an object");
|
|
1001
|
+
}
|
|
1002
|
+
if (!stats || typeof stats !== "object") {
|
|
1003
|
+
throw new Error("flattenAllTransforms: stats must be an object");
|
|
1004
|
+
}
|
|
1005
|
+
|
|
917
1006
|
const errors = [];
|
|
918
1007
|
let count = 0;
|
|
919
1008
|
|
|
@@ -1028,6 +1117,20 @@ function flattenAllTransforms(root, opts, stats) {
|
|
|
1028
1117
|
* @private
|
|
1029
1118
|
*/
|
|
1030
1119
|
function propagateTransformToChildren(group, ctm, opts, stats) {
|
|
1120
|
+
// Validate parameters
|
|
1121
|
+
if (!group || !group.children) {
|
|
1122
|
+
throw new Error("propagateTransformToChildren: invalid group element");
|
|
1123
|
+
}
|
|
1124
|
+
if (!ctm || !ctm.data) {
|
|
1125
|
+
throw new Error("propagateTransformToChildren: invalid matrix");
|
|
1126
|
+
}
|
|
1127
|
+
if (!opts || typeof opts !== "object") {
|
|
1128
|
+
throw new Error("propagateTransformToChildren: opts must be an object");
|
|
1129
|
+
}
|
|
1130
|
+
if (!stats || typeof stats !== "object") {
|
|
1131
|
+
throw new Error("propagateTransformToChildren: stats must be an object");
|
|
1132
|
+
}
|
|
1133
|
+
|
|
1031
1134
|
for (const child of [...group.children]) {
|
|
1032
1135
|
// Use tagName check instead of instanceof
|
|
1033
1136
|
if (!(child && child.tagName)) continue;
|
|
@@ -1107,6 +1210,17 @@ function propagateTransformToChildren(group, ctm, opts, stats) {
|
|
|
1107
1210
|
* @private
|
|
1108
1211
|
*/
|
|
1109
1212
|
function bakeAllGradientTransforms(root, opts, stats) {
|
|
1213
|
+
// Validate parameters
|
|
1214
|
+
if (!root || !root.getElementsByTagName) {
|
|
1215
|
+
throw new Error("bakeAllGradientTransforms: invalid root element");
|
|
1216
|
+
}
|
|
1217
|
+
if (!opts || typeof opts !== "object") {
|
|
1218
|
+
throw new Error("bakeAllGradientTransforms: opts must be an object");
|
|
1219
|
+
}
|
|
1220
|
+
if (!stats || typeof stats !== "object") {
|
|
1221
|
+
throw new Error("bakeAllGradientTransforms: stats must be an object");
|
|
1222
|
+
}
|
|
1223
|
+
|
|
1110
1224
|
const errors = [];
|
|
1111
1225
|
let count = 0;
|
|
1112
1226
|
|
|
@@ -1125,6 +1239,14 @@ function bakeAllGradientTransforms(root, opts, stats) {
|
|
|
1125
1239
|
const x2 = parseFloat(grad.getAttribute("x2") || "1");
|
|
1126
1240
|
const y2 = parseFloat(grad.getAttribute("y2") || "0");
|
|
1127
1241
|
|
|
1242
|
+
// Validate parseFloat results to prevent NaN propagation
|
|
1243
|
+
if (isNaN(x1) || isNaN(y1) || isNaN(x2) || isNaN(y2)) {
|
|
1244
|
+
errors.push(
|
|
1245
|
+
`linearGradient: invalid coordinate values for gradient ${grad.getAttribute("id") || "unknown"}`,
|
|
1246
|
+
);
|
|
1247
|
+
continue;
|
|
1248
|
+
}
|
|
1249
|
+
|
|
1128
1250
|
const [tx1, ty1] = Transforms2D.applyTransform(ctm, x1, y1);
|
|
1129
1251
|
const [tx2, ty2] = Transforms2D.applyTransform(ctm, x2, y2);
|
|
1130
1252
|
|
|
@@ -1178,13 +1300,33 @@ function bakeAllGradientTransforms(root, opts, stats) {
|
|
|
1178
1300
|
const fy = parseFloat(grad.getAttribute("fy") || cy.toString());
|
|
1179
1301
|
const r = parseFloat(grad.getAttribute("r") || "0.5");
|
|
1180
1302
|
|
|
1303
|
+
// Validate parseFloat results to prevent NaN propagation
|
|
1304
|
+
if (isNaN(cx) || isNaN(cy) || isNaN(fx) || isNaN(fy) || isNaN(r)) {
|
|
1305
|
+
errors.push(
|
|
1306
|
+
`radialGradient: invalid coordinate/radius values for gradient ${grad.getAttribute("id") || "unknown"}`,
|
|
1307
|
+
);
|
|
1308
|
+
continue;
|
|
1309
|
+
}
|
|
1310
|
+
if (r <= 0) {
|
|
1311
|
+
errors.push(
|
|
1312
|
+
`radialGradient: radius must be positive for gradient ${grad.getAttribute("id") || "unknown"}`,
|
|
1313
|
+
);
|
|
1314
|
+
continue;
|
|
1315
|
+
}
|
|
1316
|
+
|
|
1181
1317
|
const [tcx, tcy] = Transforms2D.applyTransform(ctm, cx, cy);
|
|
1182
1318
|
const [tfx, tfy] = Transforms2D.applyTransform(ctm, fx, fy);
|
|
1183
1319
|
|
|
1184
1320
|
// Scale radius by average scale factor
|
|
1185
|
-
const
|
|
1186
|
-
|
|
1187
|
-
)
|
|
1321
|
+
const scaleProduct =
|
|
1322
|
+
ctm.data[0][0].toNumber() * ctm.data[1][1].toNumber();
|
|
1323
|
+
if (Math.abs(scaleProduct) < 1e-10) {
|
|
1324
|
+
errors.push(
|
|
1325
|
+
`radialGradient: matrix determinant too close to zero (degenerate transform)`,
|
|
1326
|
+
);
|
|
1327
|
+
continue;
|
|
1328
|
+
}
|
|
1329
|
+
const scale = Math.sqrt(Math.abs(scaleProduct));
|
|
1188
1330
|
const tr = r * scale;
|
|
1189
1331
|
|
|
1190
1332
|
grad.setAttribute("cx", tcx.toFixed(opts.precision));
|
|
@@ -1220,10 +1362,19 @@ function bakeAllGradientTransforms(root, opts, stats) {
|
|
|
1220
1362
|
* @private
|
|
1221
1363
|
*/
|
|
1222
1364
|
function collectAllReferences(root) {
|
|
1365
|
+
// Validate parameter
|
|
1366
|
+
if (!root || !root.getElementsByTagName) {
|
|
1367
|
+
throw new Error("collectAllReferences: invalid root element");
|
|
1368
|
+
}
|
|
1369
|
+
|
|
1223
1370
|
const usedIds = new Set();
|
|
1224
1371
|
|
|
1225
1372
|
// Collect references from an element and all its children
|
|
1226
1373
|
const collectReferences = (el) => {
|
|
1374
|
+
// Validate element parameter
|
|
1375
|
+
if (!el || !el.getAttributeNames) {
|
|
1376
|
+
return;
|
|
1377
|
+
}
|
|
1227
1378
|
// Check for <style> elements and parse their CSS content for url(#id) references
|
|
1228
1379
|
// This is CRITICAL for SVG 2.0 features like shape-inside: url(#textShape)
|
|
1229
1380
|
if (el.tagName && el.tagName.toLowerCase() === "style") {
|
|
@@ -1289,10 +1440,23 @@ function collectAllReferences(root) {
|
|
|
1289
1440
|
* @private
|
|
1290
1441
|
*/
|
|
1291
1442
|
function removeUnusedDefinitions(root, referencedIds) {
|
|
1443
|
+
// Validate parameters
|
|
1444
|
+
if (!root || !root.getElementsByTagName) {
|
|
1445
|
+
throw new Error("removeUnusedDefinitions: invalid root element");
|
|
1446
|
+
}
|
|
1447
|
+
if (!referencedIds || !(referencedIds instanceof Set)) {
|
|
1448
|
+
throw new Error("removeUnusedDefinitions: referencedIds must be a Set");
|
|
1449
|
+
}
|
|
1450
|
+
|
|
1292
1451
|
let count = 0;
|
|
1293
1452
|
|
|
1294
1453
|
// Check if an element or any of its descendants has a referenced ID
|
|
1295
1454
|
function hasReferencedDescendant(el) {
|
|
1455
|
+
// Validate element
|
|
1456
|
+
if (!el || !el.getAttribute) {
|
|
1457
|
+
return false;
|
|
1458
|
+
}
|
|
1459
|
+
|
|
1296
1460
|
// Check self
|
|
1297
1461
|
const id = el.getAttribute("id");
|
|
1298
1462
|
if (id && referencedIds.has(id)) {
|
|
@@ -1346,6 +1510,16 @@ function removeUnusedDefinitions(root, referencedIds) {
|
|
|
1346
1510
|
* @private
|
|
1347
1511
|
*/
|
|
1348
1512
|
function getElementPathData(el, precision) {
|
|
1513
|
+
// Validate parameters
|
|
1514
|
+
if (!el || !el.tagName) {
|
|
1515
|
+
return null;
|
|
1516
|
+
}
|
|
1517
|
+
if (typeof precision !== "number" || isNaN(precision) || precision < 0) {
|
|
1518
|
+
throw new Error(
|
|
1519
|
+
`getElementPathData: precision must be a non-negative number, got ${precision}`,
|
|
1520
|
+
);
|
|
1521
|
+
}
|
|
1522
|
+
|
|
1349
1523
|
const tagName = el.tagName.toLowerCase();
|
|
1350
1524
|
|
|
1351
1525
|
if (tagName === "path") {
|
|
@@ -1355,21 +1529,33 @@ function getElementPathData(el, precision) {
|
|
|
1355
1529
|
// Use GeometryToPath for shape conversion
|
|
1356
1530
|
const getAttr = (name, def = 0) => {
|
|
1357
1531
|
const val = el.getAttribute(name);
|
|
1358
|
-
|
|
1532
|
+
if (val === null) return def;
|
|
1533
|
+
const parsed = parseFloat(val);
|
|
1534
|
+
// Validate parseFloat result to prevent NaN propagation
|
|
1535
|
+
if (isNaN(parsed)) {
|
|
1536
|
+
throw new Error(
|
|
1537
|
+
`getElementPathData: invalid numeric value "${val}" for attribute "${name}"`,
|
|
1538
|
+
);
|
|
1539
|
+
}
|
|
1540
|
+
return parsed;
|
|
1359
1541
|
};
|
|
1360
1542
|
|
|
1361
1543
|
switch (tagName) {
|
|
1362
|
-
case "rect":
|
|
1544
|
+
case "rect": {
|
|
1545
|
+
const ry = el.getAttribute("ry");
|
|
1546
|
+
// ry can be null (not specified), or a numeric value including 0
|
|
1547
|
+
const ryValue = ry !== null ? getAttr("ry") : null;
|
|
1363
1548
|
return GeometryToPath.rectToPathData(
|
|
1364
1549
|
getAttr("x"),
|
|
1365
1550
|
getAttr("y"),
|
|
1366
1551
|
getAttr("width"),
|
|
1367
1552
|
getAttr("height"),
|
|
1368
1553
|
getAttr("rx"),
|
|
1369
|
-
|
|
1554
|
+
ryValue,
|
|
1370
1555
|
false,
|
|
1371
1556
|
precision,
|
|
1372
1557
|
);
|
|
1558
|
+
}
|
|
1373
1559
|
case "circle":
|
|
1374
1560
|
return GeometryToPath.circleToPathData(
|
|
1375
1561
|
getAttr("cx"),
|
|
@@ -1417,6 +1603,11 @@ function getElementPathData(el, precision) {
|
|
|
1417
1603
|
* @private
|
|
1418
1604
|
*/
|
|
1419
1605
|
function getElementBBox(el) {
|
|
1606
|
+
// Validate parameter
|
|
1607
|
+
if (!el || !el.tagName) {
|
|
1608
|
+
return null;
|
|
1609
|
+
}
|
|
1610
|
+
|
|
1420
1611
|
const pathData = getElementPathData(el, 6);
|
|
1421
1612
|
if (!pathData) return null;
|
|
1422
1613
|
|
|
@@ -1489,6 +1680,11 @@ function getElementBBox(el) {
|
|
|
1489
1680
|
* - Visual effects (opacity, paint-order, vector-effect)
|
|
1490
1681
|
*/
|
|
1491
1682
|
function extractPresentationAttrs(el) {
|
|
1683
|
+
// Validate parameter
|
|
1684
|
+
if (!el || !el.getAttribute) {
|
|
1685
|
+
return {};
|
|
1686
|
+
}
|
|
1687
|
+
|
|
1492
1688
|
const presentationAttrs = [
|
|
1493
1689
|
// Stroke properties
|
|
1494
1690
|
"stroke",
|
|
@@ -1580,6 +1776,11 @@ function extractPresentationAttrs(el) {
|
|
|
1580
1776
|
* attribute instead.
|
|
1581
1777
|
*/
|
|
1582
1778
|
function getShapeSpecificAttrs(tagName) {
|
|
1779
|
+
// Validate parameter
|
|
1780
|
+
if (!tagName || typeof tagName !== "string") {
|
|
1781
|
+
return [];
|
|
1782
|
+
}
|
|
1783
|
+
|
|
1583
1784
|
const attrs = {
|
|
1584
1785
|
rect: ["x", "y", "width", "height", "rx", "ry"],
|
|
1585
1786
|
circle: ["cx", "cy", "r"],
|
|
@@ -1610,12 +1811,42 @@ function getShapeSpecificAttrs(tagName) {
|
|
|
1610
1811
|
* @private
|
|
1611
1812
|
*/
|
|
1612
1813
|
function matrixToTransform(matrix) {
|
|
1814
|
+
// Validate parameter
|
|
1815
|
+
if (!matrix || !matrix.data || !Array.isArray(matrix.data)) {
|
|
1816
|
+
throw new Error("matrixToTransform: invalid matrix");
|
|
1817
|
+
}
|
|
1818
|
+
if (
|
|
1819
|
+
!matrix.data[0] ||
|
|
1820
|
+
!matrix.data[1] ||
|
|
1821
|
+
!matrix.data[0][0] ||
|
|
1822
|
+
!matrix.data[0][1] ||
|
|
1823
|
+
!matrix.data[0][2] ||
|
|
1824
|
+
!matrix.data[1][0] ||
|
|
1825
|
+
!matrix.data[1][1] ||
|
|
1826
|
+
!matrix.data[1][2]
|
|
1827
|
+
) {
|
|
1828
|
+
throw new Error("matrixToTransform: matrix is missing required elements");
|
|
1829
|
+
}
|
|
1830
|
+
|
|
1613
1831
|
const a = matrix.data[0][0].toNumber();
|
|
1614
1832
|
const b = matrix.data[1][0].toNumber();
|
|
1615
1833
|
const c = matrix.data[0][1].toNumber();
|
|
1616
1834
|
const d = matrix.data[1][1].toNumber();
|
|
1617
1835
|
const e = matrix.data[0][2].toNumber();
|
|
1618
1836
|
const f = matrix.data[1][2].toNumber();
|
|
1837
|
+
|
|
1838
|
+
// Validate all values are finite numbers
|
|
1839
|
+
if (
|
|
1840
|
+
!isFinite(a) ||
|
|
1841
|
+
!isFinite(b) ||
|
|
1842
|
+
!isFinite(c) ||
|
|
1843
|
+
!isFinite(d) ||
|
|
1844
|
+
!isFinite(e) ||
|
|
1845
|
+
!isFinite(f)
|
|
1846
|
+
) {
|
|
1847
|
+
throw new Error("matrixToTransform: matrix contains non-finite values");
|
|
1848
|
+
}
|
|
1849
|
+
|
|
1619
1850
|
return `matrix(${a} ${b} ${c} ${d} ${e} ${f})`;
|
|
1620
1851
|
}
|
|
1621
1852
|
|
|
@@ -1690,14 +1921,27 @@ function intersectPolygons(subject, clip) {
|
|
|
1690
1921
|
* @private
|
|
1691
1922
|
*/
|
|
1692
1923
|
function isInsideEdge(point, edgeStart, edgeEnd) {
|
|
1693
|
-
|
|
1694
|
-
|
|
1924
|
+
// Validate parameters
|
|
1925
|
+
if (!point || (point.x === undefined && point.y === undefined)) {
|
|
1926
|
+
throw new Error("isInsideEdge: invalid point");
|
|
1927
|
+
}
|
|
1928
|
+
if (!edgeStart || (edgeStart.x === undefined && edgeStart.y === undefined)) {
|
|
1929
|
+
throw new Error("isInsideEdge: invalid edgeStart");
|
|
1930
|
+
}
|
|
1931
|
+
if (!edgeEnd || (edgeEnd.x === undefined && edgeEnd.y === undefined)) {
|
|
1932
|
+
throw new Error("isInsideEdge: invalid edgeEnd");
|
|
1933
|
+
}
|
|
1934
|
+
|
|
1935
|
+
const px = point.x instanceof Decimal ? point.x.toNumber() : point.x || 0;
|
|
1936
|
+
const py = point.y instanceof Decimal ? point.y.toNumber() : point.y || 0;
|
|
1695
1937
|
const sx =
|
|
1696
|
-
edgeStart.x instanceof Decimal ? edgeStart.x.toNumber() : edgeStart.x;
|
|
1938
|
+
edgeStart.x instanceof Decimal ? edgeStart.x.toNumber() : edgeStart.x || 0;
|
|
1697
1939
|
const sy =
|
|
1698
|
-
edgeStart.y instanceof Decimal ? edgeStart.y.toNumber() : edgeStart.y;
|
|
1699
|
-
const ex =
|
|
1700
|
-
|
|
1940
|
+
edgeStart.y instanceof Decimal ? edgeStart.y.toNumber() : edgeStart.y || 0;
|
|
1941
|
+
const ex =
|
|
1942
|
+
edgeEnd.x instanceof Decimal ? edgeEnd.x.toNumber() : edgeEnd.x || 0;
|
|
1943
|
+
const ey =
|
|
1944
|
+
edgeEnd.y instanceof Decimal ? edgeEnd.y.toNumber() : edgeEnd.y || 0;
|
|
1701
1945
|
|
|
1702
1946
|
return (ex - sx) * (py - sy) - (ey - sy) * (px - sx) >= 0;
|
|
1703
1947
|
}
|
|
@@ -1716,14 +1960,28 @@ function isInsideEdge(point, edgeStart, edgeEnd) {
|
|
|
1716
1960
|
* @returns {Object} Intersection point with x, y as Decimal
|
|
1717
1961
|
*/
|
|
1718
1962
|
function lineIntersect(p1, p2, p3, p4) {
|
|
1719
|
-
|
|
1720
|
-
|
|
1721
|
-
|
|
1722
|
-
|
|
1723
|
-
|
|
1724
|
-
|
|
1725
|
-
|
|
1726
|
-
|
|
1963
|
+
// Validate parameters
|
|
1964
|
+
if (!p1 || (p1.x === undefined && p1.y === undefined)) {
|
|
1965
|
+
throw new Error("lineIntersect: invalid p1");
|
|
1966
|
+
}
|
|
1967
|
+
if (!p2 || (p2.x === undefined && p2.y === undefined)) {
|
|
1968
|
+
throw new Error("lineIntersect: invalid p2");
|
|
1969
|
+
}
|
|
1970
|
+
if (!p3 || (p3.x === undefined && p3.y === undefined)) {
|
|
1971
|
+
throw new Error("lineIntersect: invalid p3");
|
|
1972
|
+
}
|
|
1973
|
+
if (!p4 || (p4.x === undefined && p4.y === undefined)) {
|
|
1974
|
+
throw new Error("lineIntersect: invalid p4");
|
|
1975
|
+
}
|
|
1976
|
+
|
|
1977
|
+
const x1 = p1.x instanceof Decimal ? p1.x.toNumber() : p1.x || 0;
|
|
1978
|
+
const y1 = p1.y instanceof Decimal ? p1.y.toNumber() : p1.y || 0;
|
|
1979
|
+
const x2 = p2.x instanceof Decimal ? p2.x.toNumber() : p2.x || 0;
|
|
1980
|
+
const y2 = p2.y instanceof Decimal ? p2.y.toNumber() : p2.y || 0;
|
|
1981
|
+
const x3 = p3.x instanceof Decimal ? p3.x.toNumber() : p3.x || 0;
|
|
1982
|
+
const y3 = p3.y instanceof Decimal ? p3.y.toNumber() : p3.y || 0;
|
|
1983
|
+
const x4 = p4.x instanceof Decimal ? p4.x.toNumber() : p4.x || 0;
|
|
1984
|
+
const y4 = p4.y instanceof Decimal ? p4.y.toNumber() : p4.y || 0;
|
|
1727
1985
|
|
|
1728
1986
|
const denom = (x1 - x2) * (y3 - y4) - (y1 - y2) * (x3 - x4);
|
|
1729
1987
|
|