clipper2-ts 2.0.1-7 → 2.0.1-9

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.
Files changed (45) hide show
  1. package/README.md +12 -10
  2. package/dist/Core.d.ts +126 -98
  3. package/dist/Core.d.ts.map +1 -1
  4. package/dist/Core.js +610 -655
  5. package/dist/Core.js.map +1 -1
  6. package/dist/Engine.d.ts +12 -17
  7. package/dist/Engine.d.ts.map +1 -1
  8. package/dist/Engine.js +70 -80
  9. package/dist/Engine.js.map +1 -1
  10. package/dist/Minkowski.d.ts +6 -6
  11. package/dist/Minkowski.d.ts.map +1 -1
  12. package/dist/Minkowski.js +92 -99
  13. package/dist/Minkowski.js.map +1 -1
  14. package/dist/Offset.d.ts.map +1 -1
  15. package/dist/Offset.js +6 -3
  16. package/dist/Offset.js.map +1 -1
  17. package/dist/RectClip.d.ts +1 -0
  18. package/dist/RectClip.d.ts.map +1 -1
  19. package/dist/RectClip.js +130 -93
  20. package/dist/RectClip.js.map +1 -1
  21. package/dist/Triangulation.d.ts.map +1 -1
  22. package/dist/Triangulation.js +21 -23
  23. package/dist/Triangulation.js.map +1 -1
  24. package/dist/clipper2.min.mjs +7 -0
  25. package/dist/clipper2.min.mjs.map +1 -0
  26. package/dist/index.d.ts +3 -3
  27. package/dist/index.d.ts.map +1 -1
  28. package/dist/index.js +1 -1
  29. package/dist/index.js.map +1 -1
  30. package/package.json +10 -2
  31. package/src/Core.ts +617 -583
  32. package/src/Engine.ts +70 -73
  33. package/src/Minkowski.ts +102 -105
  34. package/src/Offset.ts +6 -3
  35. package/src/RectClip.ts +63 -114
  36. package/src/Triangulation.ts +20 -22
  37. package/src/index.ts +14 -12
  38. package/dist/OutPtPool.d.ts +0 -12
  39. package/dist/OutPtPool.d.ts.map +0 -1
  40. package/dist/OutPtPool.js +0 -40
  41. package/dist/OutPtPool.js.map +0 -1
  42. package/dist/VertexPool.d.ts +0 -12
  43. package/dist/VertexPool.d.ts.map +0 -1
  44. package/dist/VertexPool.js +0 -31
  45. package/dist/VertexPool.js.map +0 -1
package/dist/Core.js CHANGED
@@ -37,557 +37,555 @@ export var PointInPolygonResult;
37
37
  PointInPolygonResult[PointInPolygonResult["IsInside"] = 1] = "IsInside";
38
38
  PointInPolygonResult[PointInPolygonResult["IsOutside"] = 2] = "IsOutside";
39
39
  })(PointInPolygonResult || (PointInPolygonResult = {}));
40
- export var InternalClipper;
41
- (function (InternalClipper) {
42
- InternalClipper.MaxInt64 = 9223372036854775807n;
43
- InternalClipper.MaxCoord = Number(InternalClipper.MaxInt64 / 4n);
44
- InternalClipper.max_coord = InternalClipper.MaxCoord;
45
- InternalClipper.min_coord = -InternalClipper.MaxCoord;
46
- InternalClipper.Invalid64 = Number(InternalClipper.MaxInt64);
47
- InternalClipper.floatingPointTolerance = 1E-12;
48
- InternalClipper.defaultMinimumEdgeLength = 0.1;
49
- const maxSafeInteger = Number.MAX_SAFE_INTEGER;
50
- const maxDeltaForSafeProduct = Math.floor(Math.sqrt(maxSafeInteger));
51
- InternalClipper.maxCoordForSafeAreaProduct = Math.floor(maxDeltaForSafeProduct / 2);
52
- // Bound for |a|,|b|,|c|,|d| so cross^2 and denom stay safe
53
- InternalClipper.maxCoordForSafeCrossSq = Math.floor(Math.sqrt(Math.sqrt(maxSafeInteger / 4)));
54
- function maxSafeCoordinateForScale(scale) {
55
- if (!Number.isFinite(scale)) {
56
- throw new RangeError("Scale must be a finite number");
57
- }
58
- const absScale = Math.abs(scale);
59
- if (absScale === 0)
60
- return Number.POSITIVE_INFINITY;
61
- return maxSafeInteger / absScale;
62
- }
63
- InternalClipper.maxSafeCoordinateForScale = maxSafeCoordinateForScale;
64
- function checkSafeScaleValue(value, maxAbs, context) {
65
- if (!Number.isFinite(value) || Math.abs(value) > maxAbs) {
66
- throw new RangeError(`Scaled coordinate exceeds Number.MAX_SAFE_INTEGER in ${context}`);
67
- }
68
- }
69
- InternalClipper.checkSafeScaleValue = checkSafeScaleValue;
70
- function ensureSafeInteger(value, context) {
71
- if (!Number.isFinite(value) || Math.abs(value) > maxSafeInteger) {
72
- throw new RangeError(`Coordinate exceeds Number.MAX_SAFE_INTEGER in ${context}`);
73
- }
74
- }
75
- InternalClipper.ensureSafeInteger = ensureSafeInteger;
76
- function isSafeProduct(a, b) {
77
- if (!Number.isSafeInteger(a) || !Number.isSafeInteger(b))
78
- return false;
79
- if (a === 0 || b === 0)
80
- return true;
81
- return Math.abs(a) <= maxSafeInteger / Math.abs(b);
82
- }
83
- function isSafeSum(a, b) {
84
- return Math.abs(a) + Math.abs(b) <= maxSafeInteger;
85
- }
86
- function safeMultiplyDifference(a, b, c, d) {
87
- if (isSafeProduct(a, b) && isSafeProduct(c, d)) {
88
- const prod1 = a * b;
89
- const prod2 = c * d;
90
- if (isSafeSum(prod1, prod2)) {
91
- return prod1 - prod2;
92
- }
93
- }
94
- if (Number.isSafeInteger(a) && Number.isSafeInteger(b) &&
95
- Number.isSafeInteger(c) && Number.isSafeInteger(d)) {
96
- return Number((BigInt(a) * BigInt(b)) - (BigInt(c) * BigInt(d)));
97
- }
40
+ // InternalClipper - converted from namespace to plain const object to avoid
41
+ // the IIFE wrapper that tsc emits for namespaces. All functions are defined at
42
+ // module level so internal cross-calls (e.g. isCollinear -> productsAreEqual)
43
+ // are direct calls without property lookup overhead. The exported object bundles
44
+ // them for external callers that use InternalClipper.foo() syntax.
45
+ // --- private helpers (module-level, not exported) ---
46
+ const maxSafeInteger = Number.MAX_SAFE_INTEGER;
47
+ const maxDeltaForSafeProduct = Math.floor(Math.sqrt(maxSafeInteger));
48
+ function isSafeProduct(a, b) {
49
+ if (!Number.isSafeInteger(a) || !Number.isSafeInteger(b))
50
+ return false;
51
+ if (a === 0 || b === 0)
52
+ return true;
53
+ return Math.abs(a) <= maxSafeInteger / Math.abs(b);
54
+ }
55
+ function isSafeSum(a, b) {
56
+ return Math.abs(a) + Math.abs(b) <= maxSafeInteger;
57
+ }
58
+ function safeMultiplyDifference(a, b, c, d) {
59
+ if (isSafeProduct(a, b) && isSafeProduct(c, d)) {
60
+ const prod1 = a * b;
61
+ const prod2 = c * d;
62
+ if (isSafeSum(prod1, prod2)) {
63
+ return prod1 - prod2;
64
+ }
65
+ }
66
+ if (Number.isSafeInteger(a) && Number.isSafeInteger(b) &&
67
+ Number.isSafeInteger(c) && Number.isSafeInteger(d)) {
68
+ return Number((BigInt(a) * BigInt(b)) - (BigInt(c) * BigInt(d)));
69
+ }
70
+ return (a * b) - (c * d);
71
+ }
72
+ function safeMultiplySum(a, b, c, d) {
73
+ if (isSafeProduct(a, b) && isSafeProduct(c, d)) {
74
+ const prod1 = a * b;
75
+ const prod2 = c * d;
76
+ if (isSafeSum(prod1, prod2)) {
77
+ return prod1 + prod2;
78
+ }
79
+ }
80
+ if (Number.isSafeInteger(a) && Number.isSafeInteger(b) &&
81
+ Number.isSafeInteger(c) && Number.isSafeInteger(d)) {
82
+ return Number((BigInt(a) * BigInt(b)) + (BigInt(c) * BigInt(d)));
83
+ }
84
+ return (a * b) + (c * d);
85
+ }
86
+ // --- public constants (module-level for cross-referencing) ---
87
+ const IC_MaxInt64 = 9223372036854775807n;
88
+ const IC_MaxCoord = Number(IC_MaxInt64 / 4n);
89
+ const IC_Invalid64 = Number(IC_MaxInt64);
90
+ const IC_floatingPointTolerance = 1E-12;
91
+ const IC_defaultMinimumEdgeLength = 0.1;
92
+ const IC_maxCoordForSafeAreaProduct = Math.floor(maxDeltaForSafeProduct / 2);
93
+ // Bound for |a|,|b|,|c|,|d| so cross^2 and denom stay safe
94
+ const IC_maxCoordForSafeCrossSq = Math.floor(Math.sqrt(Math.sqrt(maxSafeInteger / 4)));
95
+ // --- public functions (module-level for direct internal calls) ---
96
+ function maxSafeCoordinateForScale(scale) {
97
+ if (!Number.isFinite(scale)) {
98
+ throw new RangeError("Scale must be a finite number");
99
+ }
100
+ const absScale = Math.abs(scale);
101
+ if (absScale === 0)
102
+ return Number.POSITIVE_INFINITY;
103
+ return maxSafeInteger / absScale;
104
+ }
105
+ function checkSafeScaleValue(value, maxAbs, context) {
106
+ if (!Number.isFinite(value) || Math.abs(value) > maxAbs) {
107
+ throw new RangeError(`Scaled coordinate exceeds Number.MAX_SAFE_INTEGER in ${context}`);
108
+ }
109
+ }
110
+ function ensureSafeInteger(value, context) {
111
+ if (!Number.isFinite(value) || Math.abs(value) > maxSafeInteger) {
112
+ throw new RangeError(`Coordinate exceeds Number.MAX_SAFE_INTEGER in ${context}`);
113
+ }
114
+ }
115
+ function crossProduct(pt1, pt2, pt3) {
116
+ const a = pt2.x - pt1.x;
117
+ const b = pt3.y - pt2.y;
118
+ const c = pt2.y - pt1.y;
119
+ const d = pt3.x - pt2.x;
120
+ // Fast path for small coordinates
121
+ if (Math.abs(a) < maxDeltaForSafeProduct && Math.abs(b) < maxDeltaForSafeProduct &&
122
+ Math.abs(c) < maxDeltaForSafeProduct && Math.abs(d) < maxDeltaForSafeProduct) {
98
123
  return (a * b) - (c * d);
99
124
  }
100
- function safeMultiplySum(a, b, c, d) {
101
- if (isSafeProduct(a, b) && isSafeProduct(c, d)) {
102
- const prod1 = a * b;
103
- const prod2 = c * d;
104
- if (isSafeSum(prod1, prod2)) {
105
- return prod1 + prod2;
106
- }
107
- }
108
- if (Number.isSafeInteger(a) && Number.isSafeInteger(b) &&
109
- Number.isSafeInteger(c) && Number.isSafeInteger(d)) {
110
- return Number((BigInt(a) * BigInt(b)) + (BigInt(c) * BigInt(d)));
111
- }
125
+ return safeMultiplyDifference(a, b, c, d);
126
+ }
127
+ function crossProductSign(pt1, pt2, pt3) {
128
+ const a = pt2.x - pt1.x;
129
+ const b = pt3.y - pt2.y;
130
+ const c = pt2.y - pt1.y;
131
+ const d = pt3.x - pt2.x;
132
+ // Fast check for safe integer range
133
+ // Using Math.abs inline allows short-circuiting
134
+ if (Math.abs(a) < maxDeltaForSafeProduct && Math.abs(b) < maxDeltaForSafeProduct &&
135
+ Math.abs(c) < maxDeltaForSafeProduct && Math.abs(d) < maxDeltaForSafeProduct) {
136
+ const prod1 = a * b;
137
+ const prod2 = c * d;
138
+ return (prod1 > prod2) ? 1 : (prod1 < prod2) ? -1 : 0;
139
+ }
140
+ if (!Number.isSafeInteger(a) || !Number.isSafeInteger(b) ||
141
+ !Number.isSafeInteger(c) || !Number.isSafeInteger(d)) {
142
+ const prod1 = a * b;
143
+ const prod2 = c * d;
144
+ return (prod1 > prod2) ? 1 : (prod1 < prod2) ? -1 : 0;
145
+ }
146
+ const bigProd1 = BigInt(a) * BigInt(b);
147
+ const bigProd2 = BigInt(c) * BigInt(d);
148
+ if (bigProd1 === bigProd2)
149
+ return 0;
150
+ return (bigProd1 > bigProd2) ? 1 : -1;
151
+ }
152
+ function checkPrecision(precision) {
153
+ if (precision < -8 || precision > 8) {
154
+ throw new Error("Error: Precision is out of range.");
155
+ }
156
+ }
157
+ function isAlmostZero(value) {
158
+ return Math.abs(value) <= IC_floatingPointTolerance;
159
+ }
160
+ function triSign(x) {
161
+ return (x < 0) ? -1 : (x > 0) ? 1 : 0;
162
+ }
163
+ function multiplyUInt64(a, b) {
164
+ // Fix: a and b might be larger than 2^32, so don't use >>> 0
165
+ const aBig = BigInt(a);
166
+ const bBig = BigInt(b);
167
+ const res = aBig * bBig;
168
+ return {
169
+ lo64: res & 0xffffffffffffffffn,
170
+ hi64: res >> 64n
171
+ };
172
+ }
173
+ // returns true if (and only if) a * b == c * d
174
+ function productsAreEqual(a, b, c, d) {
175
+ const absA = Math.abs(a);
176
+ const absB = Math.abs(b);
177
+ const absC = Math.abs(c);
178
+ const absD = Math.abs(d);
179
+ // Fast path for safe integer range (covers all typical coordinates)
180
+ if (absA < maxDeltaForSafeProduct && absB < maxDeltaForSafeProduct &&
181
+ absC < maxDeltaForSafeProduct && absD < maxDeltaForSafeProduct) {
182
+ return a * b === c * d;
183
+ }
184
+ const signAb = (a < 0 ? -1 : (a > 0 ? 1 : 0)) * (b < 0 ? -1 : (b > 0 ? 1 : 0));
185
+ const signCd = (c < 0 ? -1 : (c > 0 ? 1 : 0)) * (d < 0 ? -1 : (d > 0 ? 1 : 0));
186
+ if (signAb !== signCd)
187
+ return false;
188
+ if (signAb === 0)
189
+ return true;
190
+ if (!Number.isSafeInteger(absA) || !Number.isSafeInteger(absB) ||
191
+ !Number.isSafeInteger(absC) || !Number.isSafeInteger(absD)) {
192
+ return a * b === c * d;
193
+ }
194
+ const bigA = BigInt(absA);
195
+ const bigB = BigInt(absB);
196
+ const bigC = BigInt(absC);
197
+ const bigD = BigInt(absD);
198
+ return (bigA * bigB) === (bigC * bigD);
199
+ }
200
+ function isCollinear(pt1, sharedPt, pt2) {
201
+ const a = sharedPt.x - pt1.x;
202
+ const b = pt2.y - sharedPt.y;
203
+ const c = sharedPt.y - pt1.y;
204
+ const d = pt2.x - sharedPt.x;
205
+ // When checking for collinearity with very large coordinate values
206
+ // then ProductsAreEqual is more accurate than using CrossProduct.
207
+ return productsAreEqual(a, b, c, d);
208
+ }
209
+ function dotProduct(pt1, pt2, pt3) {
210
+ const a = pt2.x - pt1.x;
211
+ const b = pt3.x - pt2.x;
212
+ const c = pt2.y - pt1.y;
213
+ const d = pt3.y - pt2.y;
214
+ // Fast path for small coordinates
215
+ if (Math.abs(a) < maxDeltaForSafeProduct && Math.abs(b) < maxDeltaForSafeProduct &&
216
+ Math.abs(c) < maxDeltaForSafeProduct && Math.abs(d) < maxDeltaForSafeProduct) {
112
217
  return (a * b) + (c * d);
113
218
  }
114
- function crossProduct(pt1, pt2, pt3) {
115
- const a = pt2.x - pt1.x;
116
- const b = pt3.y - pt2.y;
117
- const c = pt2.y - pt1.y;
118
- const d = pt3.x - pt2.x;
119
- // Fast path for small coordinates
120
- if (Math.abs(a) < maxDeltaForSafeProduct && Math.abs(b) < maxDeltaForSafeProduct &&
121
- Math.abs(c) < maxDeltaForSafeProduct && Math.abs(d) < maxDeltaForSafeProduct) {
122
- return (a * b) - (c * d);
123
- }
124
- return safeMultiplyDifference(a, b, c, d);
125
- }
126
- InternalClipper.crossProduct = crossProduct;
127
- function crossProductSign(pt1, pt2, pt3) {
128
- const a = pt2.x - pt1.x;
129
- const b = pt3.y - pt2.y;
130
- const c = pt2.y - pt1.y;
131
- const d = pt3.x - pt2.x;
132
- // Fast check for safe integer range
133
- // Using Math.abs inline allows short-circuiting
134
- if (Math.abs(a) < maxDeltaForSafeProduct && Math.abs(b) < maxDeltaForSafeProduct &&
135
- Math.abs(c) < maxDeltaForSafeProduct && Math.abs(d) < maxDeltaForSafeProduct) {
136
- const prod1 = a * b;
137
- const prod2 = c * d;
138
- return (prod1 > prod2) ? 1 : (prod1 < prod2) ? -1 : 0;
139
- }
140
- if (!Number.isSafeInteger(a) || !Number.isSafeInteger(b) ||
141
- !Number.isSafeInteger(c) || !Number.isSafeInteger(d)) {
142
- const prod1 = a * b;
143
- const prod2 = c * d;
144
- return (prod1 > prod2) ? 1 : (prod1 < prod2) ? -1 : 0;
145
- }
146
- const bigProd1 = BigInt(a) * BigInt(b);
147
- const bigProd2 = BigInt(c) * BigInt(d);
148
- if (bigProd1 === bigProd2)
149
- return 0;
150
- return (bigProd1 > bigProd2) ? 1 : -1;
151
- }
152
- InternalClipper.crossProductSign = crossProductSign;
153
- function checkPrecision(precision) {
154
- if (precision < -8 || precision > 8) {
155
- throw new Error("Error: Precision is out of range.");
156
- }
157
- }
158
- InternalClipper.checkPrecision = checkPrecision;
159
- function isAlmostZero(value) {
160
- return Math.abs(value) <= InternalClipper.floatingPointTolerance;
161
- }
162
- InternalClipper.isAlmostZero = isAlmostZero;
163
- function triSign(x) {
164
- return (x < 0) ? -1 : (x > 0) ? 1 : 0;
165
- }
166
- InternalClipper.triSign = triSign;
167
- function multiplyUInt64(a, b) {
168
- // Fix: a and b might be larger than 2^32, so don't use >>> 0
169
- const aBig = BigInt(a);
170
- const bBig = BigInt(b);
171
- const res = aBig * bBig;
172
- return {
173
- lo64: res & 0xffffffffffffffffn,
174
- hi64: res >> 64n
175
- };
176
- }
177
- InternalClipper.multiplyUInt64 = multiplyUInt64;
178
- // returns true if (and only if) a * b == c * d
179
- function productsAreEqual(a, b, c, d) {
180
- const absA = Math.abs(a);
181
- const absB = Math.abs(b);
182
- const absC = Math.abs(c);
183
- const absD = Math.abs(d);
184
- // Fast path for typical coordinates
185
- if (absA < 46341 && absB < 46341 && absC < 46341 && absD < 46341) {
186
- return a * b === c * d;
187
- }
188
- // Extended fast path for safe integer range
189
- if (absA < maxDeltaForSafeProduct && absB < maxDeltaForSafeProduct &&
190
- absC < maxDeltaForSafeProduct && absD < maxDeltaForSafeProduct) {
191
- return a * b === c * d;
192
- }
193
- const signAb = (a < 0 ? -1 : (a > 0 ? 1 : 0)) * (b < 0 ? -1 : (b > 0 ? 1 : 0));
194
- const signCd = (c < 0 ? -1 : (c > 0 ? 1 : 0)) * (d < 0 ? -1 : (d > 0 ? 1 : 0));
195
- if (signAb !== signCd)
196
- return false;
197
- if (signAb === 0)
198
- return true;
199
- if (!Number.isSafeInteger(absA) || !Number.isSafeInteger(absB) ||
200
- !Number.isSafeInteger(absC) || !Number.isSafeInteger(absD)) {
201
- return a * b === c * d;
202
- }
203
- const bigA = BigInt(absA);
204
- const bigB = BigInt(absB);
205
- const bigC = BigInt(absC);
206
- const bigD = BigInt(absD);
207
- return (bigA * bigB) === (bigC * bigD);
208
- }
209
- InternalClipper.productsAreEqual = productsAreEqual;
210
- function isCollinear(pt1, sharedPt, pt2) {
211
- const a = sharedPt.x - pt1.x;
212
- const b = pt2.y - sharedPt.y;
213
- const c = sharedPt.y - pt1.y;
214
- const d = pt2.x - sharedPt.x;
215
- // When checking for collinearity with very large coordinate values
216
- // then ProductsAreEqual is more accurate than using CrossProduct.
217
- return productsAreEqual(a, b, c, d);
218
- }
219
- InternalClipper.isCollinear = isCollinear;
220
- function dotProduct(pt1, pt2, pt3) {
221
- const a = pt2.x - pt1.x;
222
- const b = pt3.x - pt2.x;
223
- const c = pt2.y - pt1.y;
224
- const d = pt3.y - pt2.y;
225
- // Fast path for small coordinates
226
- if (Math.abs(a) < maxDeltaForSafeProduct && Math.abs(b) < maxDeltaForSafeProduct &&
227
- Math.abs(c) < maxDeltaForSafeProduct && Math.abs(d) < maxDeltaForSafeProduct) {
228
- return (a * b) + (c * d);
229
- }
230
- return safeMultiplySum(a, b, c, d);
231
- }
232
- InternalClipper.dotProduct = dotProduct;
233
- function dotProductSign(pt1, pt2, pt3) {
234
- const a = pt2.x - pt1.x;
235
- const b = pt3.x - pt2.x;
236
- const c = pt2.y - pt1.y;
237
- const d = pt3.y - pt2.y;
238
- if (Math.abs(a) < maxDeltaForSafeProduct && Math.abs(b) < maxDeltaForSafeProduct &&
239
- Math.abs(c) < maxDeltaForSafeProduct && Math.abs(d) < maxDeltaForSafeProduct) {
240
- const sum = (a * b) + (c * d);
241
- return sum > 0 ? 1 : (sum < 0 ? -1 : 0);
242
- }
243
- if (!Number.isSafeInteger(a) || !Number.isSafeInteger(b) ||
244
- !Number.isSafeInteger(c) || !Number.isSafeInteger(d)) {
245
- const sum = (a * b) + (c * d);
246
- return sum > 0 ? 1 : (sum < 0 ? -1 : 0);
247
- }
248
- const bigSum = (BigInt(a) * BigInt(b)) + (BigInt(c) * BigInt(d));
249
- if (bigSum === 0n)
250
- return 0;
251
- return bigSum > 0n ? 1 : -1;
252
- }
253
- InternalClipper.dotProductSign = dotProductSign;
254
- function area(path) {
255
- // https://en.wikipedia.org/wiki/Shoelace_formula
256
- const cnt = path.length;
257
- if (cnt < 3)
258
- return 0.0;
259
- // Fast path when coords are small enough that (y1+y2)*(x1-x2) won't overflow
260
- // maxCoordForSafeAreaProduct is floor(sqrt(Number.MAX_SAFE_INTEGER) / 2)
261
- let allSmall = true;
262
- for (let i = 0; i < cnt && allSmall; i++) {
263
- const pt = path[i];
264
- if (Math.abs(pt.x) >= InternalClipper.maxCoordForSafeAreaProduct ||
265
- Math.abs(pt.y) >= InternalClipper.maxCoordForSafeAreaProduct) {
266
- allSmall = false;
267
- }
268
- }
269
- let prevPt = path[cnt - 1];
270
- if (allSmall) {
271
- // Fast path - no overflow checks needed
272
- let total = 0.0;
273
- for (const pt of path) {
274
- total += (prevPt.y + pt.y) * (prevPt.x - pt.x);
275
- prevPt = pt;
276
- }
277
- return total * 0.5;
278
- }
279
- // Safe path - use BigInt for accumulation
280
- let totalBig = 0n;
219
+ return safeMultiplySum(a, b, c, d);
220
+ }
221
+ function dotProductSign(pt1, pt2, pt3) {
222
+ const a = pt2.x - pt1.x;
223
+ const b = pt3.x - pt2.x;
224
+ const c = pt2.y - pt1.y;
225
+ const d = pt3.y - pt2.y;
226
+ if (Math.abs(a) < maxDeltaForSafeProduct && Math.abs(b) < maxDeltaForSafeProduct &&
227
+ Math.abs(c) < maxDeltaForSafeProduct && Math.abs(d) < maxDeltaForSafeProduct) {
228
+ const sum = (a * b) + (c * d);
229
+ return sum > 0 ? 1 : (sum < 0 ? -1 : 0);
230
+ }
231
+ if (!Number.isSafeInteger(a) || !Number.isSafeInteger(b) ||
232
+ !Number.isSafeInteger(c) || !Number.isSafeInteger(d)) {
233
+ const sum = (a * b) + (c * d);
234
+ return sum > 0 ? 1 : (sum < 0 ? -1 : 0);
235
+ }
236
+ const bigSum = (BigInt(a) * BigInt(b)) + (BigInt(c) * BigInt(d));
237
+ if (bigSum === 0n)
238
+ return 0;
239
+ return bigSum > 0n ? 1 : -1;
240
+ }
241
+ function icArea(path) {
242
+ // https://en.wikipedia.org/wiki/Shoelace_formula
243
+ const cnt = path.length;
244
+ if (cnt < 3)
245
+ return 0.0;
246
+ // Fast path when coords are small enough that (y1+y2)*(x1-x2) won't overflow
247
+ // maxCoordForSafeAreaProduct is floor(sqrt(Number.MAX_SAFE_INTEGER) / 2)
248
+ let allSmall = true;
249
+ for (let i = 0; i < cnt && allSmall; i++) {
250
+ const pt = path[i];
251
+ if (Math.abs(pt.x) >= IC_maxCoordForSafeAreaProduct ||
252
+ Math.abs(pt.y) >= IC_maxCoordForSafeAreaProduct) {
253
+ allSmall = false;
254
+ }
255
+ }
256
+ let prevPt = path[cnt - 1];
257
+ if (allSmall) {
258
+ // Fast path - no overflow checks needed
259
+ let total = 0.0;
281
260
  for (const pt of path) {
282
- const sum = prevPt.y + pt.y;
283
- const diff = prevPt.x - pt.x;
284
- if (Number.isSafeInteger(sum) && Number.isSafeInteger(diff)) {
285
- totalBig += BigInt(sum) * BigInt(diff);
286
- }
287
- else if (Number.isSafeInteger(prevPt.y) && Number.isSafeInteger(pt.y) &&
288
- Number.isSafeInteger(prevPt.x) && Number.isSafeInteger(pt.x)) {
289
- const sumBig = BigInt(prevPt.y) + BigInt(pt.y);
290
- const diffBig = BigInt(prevPt.x) - BigInt(pt.x);
291
- totalBig += sumBig * diffBig;
292
- }
293
- else {
294
- // Coordinates not safe integers - fall back to float
295
- totalBig += BigInt(Math.round(sum * diff));
296
- }
261
+ total += (prevPt.y + pt.y) * (prevPt.x - pt.x);
297
262
  prevPt = pt;
298
263
  }
299
- return Number(totalBig) * 0.5;
300
- }
301
- InternalClipper.area = area;
302
- function crossProductD(vec1, vec2) {
303
- return (vec1.y * vec2.x - vec2.y * vec1.x);
304
- }
305
- InternalClipper.crossProductD = crossProductD;
306
- function dotProductD(vec1, vec2) {
307
- return (vec1.x * vec2.x + vec1.y * vec2.y);
308
- }
309
- InternalClipper.dotProductD = dotProductD;
310
- // Banker's rounding (round half to even) to match C# MidpointRounding.ToEven
311
- function roundToEven(value) {
312
- // Use the built-in behavior that's closer to C# MidpointRounding.ToEven
313
- // JavaScript's Math.round actually implements "round half away from zero"
314
- // but for most practical cases, the difference is minimal
315
- const floor = Math.floor(value);
316
- const diff = value - floor;
317
- if (Math.abs(diff - 0.5) < 1e-10) {
318
- // Exactly halfway - round to even
319
- return floor % 2 === 0 ? floor : floor + 1;
320
- }
321
- return Math.round(value);
322
- }
323
- InternalClipper.roundToEven = roundToEven;
324
- function checkCastInt64(val) {
325
- if ((val >= InternalClipper.max_coord) || (val <= InternalClipper.min_coord))
326
- return InternalClipper.Invalid64;
327
- return Math.round(val);
328
- }
329
- InternalClipper.checkCastInt64 = checkCastInt64;
330
- // GetLineIntersectPt - a 'true' result is non-parallel. The 'ip' will also
331
- // be constrained to seg1. However, it's possible that 'ip' won't be inside
332
- // seg2, even when 'ip' hasn't been constrained (ie 'ip' is inside seg1).
333
- function getLineIntersectPt(ln1a, ln1b, ln2a, ln2b) {
334
- const dy1 = (ln1b.y - ln1a.y);
335
- const dx1 = (ln1b.x - ln1a.x);
336
- const dy2 = (ln2b.y - ln2a.y);
337
- const dx2 = (ln2b.x - ln2a.x);
338
- const det = safeMultiplyDifference(dy1, dx2, dy2, dx1);
339
- if (det === 0.0) {
340
- return { intersects: false, point: { x: 0, y: 0 } };
341
- }
342
- const t = safeMultiplyDifference((ln1a.x - ln2a.x), dy2, (ln1a.y - ln2a.y), dx2) / det;
343
- let ip;
344
- if (t <= 0.0) {
345
- // Create a copy to avoid mutating original.
346
- ip = { x: ln1a.x, y: ln1a.y };
347
- }
348
- else if (t >= 1.0) {
349
- ip = { x: ln1b.x, y: ln1b.y };
350
- }
351
- else {
352
- // avoid using constructor (and rounding too) as they affect performance
353
- // Use Math.trunc to match C# (long) cast behavior which truncates towards zero
354
- const rawX = ln1a.x + t * dx1;
355
- const rawY = ln1a.y + t * dy1;
356
- ip = {
357
- x: Math.trunc(rawX),
358
- y: Math.trunc(rawY)
359
- };
360
- }
361
- return { intersects: true, point: ip };
362
- }
363
- InternalClipper.getLineIntersectPt = getLineIntersectPt;
364
- function getLineIntersectPtD(ln1a, ln1b, ln2a, ln2b) {
365
- const dy1 = ln1b.y - ln1a.y;
366
- const dx1 = ln1b.x - ln1a.x;
367
- const dy2 = ln2b.y - ln2a.y;
368
- const dx2 = ln2b.x - ln2a.x;
369
- const det = dy1 * dx2 - dy2 * dx1;
370
- if (det === 0.0) {
371
- return { success: false, ip: { x: 0, y: 0, z: 0 } };
372
- }
373
- const t = ((ln1a.x - ln2a.x) * dy2 - (ln1a.y - ln2a.y) * dx2) / det;
374
- let ip;
375
- if (t <= 0.0) {
376
- ip = { ...ln1a, z: 0 };
264
+ return total * 0.5;
265
+ }
266
+ // Safe path - use BigInt for accumulation
267
+ let totalBig = 0n;
268
+ for (const pt of path) {
269
+ const sum = prevPt.y + pt.y;
270
+ const diff = prevPt.x - pt.x;
271
+ if (Number.isSafeInteger(sum) && Number.isSafeInteger(diff)) {
272
+ totalBig += BigInt(sum) * BigInt(diff);
377
273
  }
378
- else if (t >= 1.0) {
379
- ip = { ...ln1b, z: 0 };
274
+ else if (Number.isSafeInteger(prevPt.y) && Number.isSafeInteger(pt.y) &&
275
+ Number.isSafeInteger(prevPt.x) && Number.isSafeInteger(pt.x)) {
276
+ const sumBig = BigInt(prevPt.y) + BigInt(pt.y);
277
+ const diffBig = BigInt(prevPt.x) - BigInt(pt.x);
278
+ totalBig += sumBig * diffBig;
380
279
  }
381
280
  else {
382
- ip = {
383
- x: ln1a.x + t * dx1,
384
- y: ln1a.y + t * dy1,
385
- z: 0
386
- };
387
- }
388
- return { success: true, ip };
389
- }
390
- InternalClipper.getLineIntersectPtD = getLineIntersectPtD;
391
- function segsIntersect(seg1a, seg1b, seg2a, seg2b, inclusive = false) {
392
- if (!inclusive) {
393
- // Match C# fast path - use cross product multiplication
394
- // This avoids floating point equality checks (safer than === 0)
395
- const s1 = crossProductSign(seg1a, seg2a, seg2b);
396
- const s2 = crossProductSign(seg1b, seg2a, seg2b);
397
- const s3 = crossProductSign(seg2a, seg1a, seg1b);
398
- const s4 = crossProductSign(seg2b, seg1a, seg1b);
399
- return (s1 !== 0 && s2 !== 0 && s1 !== s2) &&
400
- (s3 !== 0 && s4 !== 0 && s3 !== s4);
401
- }
402
- // Inclusive case - match C# implementation
403
- const res1 = crossProductSign(seg1a, seg2a, seg2b);
404
- const res2 = crossProductSign(seg1b, seg2a, seg2b);
405
- if (res1 !== 0 && res1 === res2)
406
- return false;
407
- const res3 = crossProductSign(seg2a, seg1a, seg1b);
408
- const res4 = crossProductSign(seg2b, seg1a, seg1b);
409
- if (res3 !== 0 && res3 === res4)
410
- return false;
411
- // ensure NOT collinear
412
- return (res1 !== 0 || res2 !== 0 || res3 !== 0 || res4 !== 0);
413
- }
414
- InternalClipper.segsIntersect = segsIntersect;
415
- function getBounds(path) {
416
- if (path.length === 0)
417
- return { left: 0, top: 0, right: 0, bottom: 0 };
418
- const result = {
419
- left: Number.MAX_SAFE_INTEGER,
420
- top: Number.MAX_SAFE_INTEGER,
421
- right: Number.MIN_SAFE_INTEGER,
422
- bottom: Number.MIN_SAFE_INTEGER
423
- };
424
- for (const pt of path) {
425
- if (pt.x < result.left)
426
- result.left = pt.x;
427
- if (pt.x > result.right)
428
- result.right = pt.x;
429
- if (pt.y < result.top)
430
- result.top = pt.y;
431
- if (pt.y > result.bottom)
432
- result.bottom = pt.y;
433
- }
434
- return result.left === Number.MAX_SAFE_INTEGER ?
435
- { left: 0, top: 0, right: 0, bottom: 0 } : result;
436
- }
437
- InternalClipper.getBounds = getBounds;
438
- function getClosestPtOnSegment(offPt, seg1, seg2) {
439
- if (seg1.x === seg2.x && seg1.y === seg2.y)
440
- return { x: seg1.x, y: seg1.y, z: 0 }; // Return copy, not reference
441
- const dx = (seg2.x - seg1.x);
442
- const dy = (seg2.y - seg1.y);
443
- const q = safeMultiplySum((offPt.x - seg1.x), dx, (offPt.y - seg1.y), dy) /
444
- safeMultiplySum(dx, dx, dy, dy);
445
- const qClamped = q < 0 ? 0 : (q > 1 ? 1 : q);
281
+ // Coordinates not safe integers - fall back to float
282
+ totalBig += BigInt(Math.round(sum * diff));
283
+ }
284
+ prevPt = pt;
285
+ }
286
+ return Number(totalBig) * 0.5;
287
+ }
288
+ function crossProductD(vec1, vec2) {
289
+ return (vec1.y * vec2.x - vec2.y * vec1.x);
290
+ }
291
+ function dotProductD(vec1, vec2) {
292
+ return (vec1.x * vec2.x + vec1.y * vec2.y);
293
+ }
294
+ // Banker's rounding (round half to even) to match C# MidpointRounding.ToEven.
295
+ function roundToEven(value) {
296
+ const r = Math.round(value);
297
+ if (value === r - 0.5 && (r & 1) !== 0)
298
+ return r - 1;
299
+ return r;
300
+ }
301
+ function checkCastInt64(val) {
302
+ if ((val >= IC_MaxCoord) || (val <= -IC_MaxCoord))
303
+ return IC_Invalid64;
304
+ return Math.round(val);
305
+ }
306
+ // GetLineIntersectPt - returns the intersection point if non-parallel, or null.
307
+ // The point will be constrained to seg1. However, it's possible that the point
308
+ // won't be inside seg2, even when it hasn't been constrained (ie inside seg1).
309
+ // Returns Point64 | null to avoid allocating a wrapper object on every call.
310
+ function getLineIntersectPt(ln1a, ln1b, ln2a, ln2b) {
311
+ const dy1 = (ln1b.y - ln1a.y);
312
+ const dx1 = (ln1b.x - ln1a.x);
313
+ const dy2 = (ln2b.y - ln2a.y);
314
+ const dx2 = (ln2b.x - ln2a.x);
315
+ const det = safeMultiplyDifference(dy1, dx2, dy2, dx1);
316
+ if (det === 0.0) {
317
+ return null;
318
+ }
319
+ const t = safeMultiplyDifference((ln1a.x - ln2a.x), dy2, (ln1a.y - ln2a.y), dx2) / det;
320
+ if (t <= 0.0) {
321
+ // Create a copy to avoid mutating original.
322
+ return { x: ln1a.x, y: ln1a.y };
323
+ }
324
+ else if (t >= 1.0) {
325
+ return { x: ln1b.x, y: ln1b.y };
326
+ }
327
+ else {
328
+ // avoid using constructor (and rounding too) as they affect performance
329
+ // Use Math.trunc to match C# (long) cast behavior which truncates towards zero
446
330
  return {
447
- // use Math.round to match the C# MidpointRounding.ToEven behavior
448
- x: Math.round(seg1.x + qClamped * dx),
449
- y: Math.round(seg1.y + qClamped * dy),
331
+ x: Math.trunc(ln1a.x + t * dx1),
332
+ y: Math.trunc(ln1a.y + t * dy1)
333
+ };
334
+ }
335
+ }
336
+ function getLineIntersectPtD(ln1a, ln1b, ln2a, ln2b) {
337
+ const dy1 = ln1b.y - ln1a.y;
338
+ const dx1 = ln1b.x - ln1a.x;
339
+ const dy2 = ln2b.y - ln2a.y;
340
+ const dx2 = ln2b.x - ln2a.x;
341
+ const det = dy1 * dx2 - dy2 * dx1;
342
+ if (det === 0.0) {
343
+ return { success: false, ip: { x: 0, y: 0, z: 0 } };
344
+ }
345
+ const t = ((ln1a.x - ln2a.x) * dy2 - (ln1a.y - ln2a.y) * dx2) / det;
346
+ let ip;
347
+ if (t <= 0.0) {
348
+ ip = { ...ln1a, z: 0 };
349
+ }
350
+ else if (t >= 1.0) {
351
+ ip = { ...ln1b, z: 0 };
352
+ }
353
+ else {
354
+ ip = {
355
+ x: ln1a.x + t * dx1,
356
+ y: ln1a.y + t * dy1,
450
357
  z: 0
451
358
  };
452
359
  }
453
- InternalClipper.getClosestPtOnSegment = getClosestPtOnSegment;
454
- function pointInPolygon(pt, polygon) {
455
- const len = polygon.length;
456
- let start = 0;
457
- if (len < 3)
458
- return PointInPolygonResult.IsOutside;
459
- while (start < len && polygon[start].y === pt.y)
460
- start++;
461
- if (start === len)
462
- return PointInPolygonResult.IsOutside;
463
- let isAbove = polygon[start].y < pt.y;
464
- const startingAbove = isAbove;
465
- let val = 0;
466
- let i = start + 1;
467
- let end = len;
468
- while (true) {
469
- if (i === end) {
470
- if (end === 0 || start === 0)
471
- break;
472
- end = start;
473
- i = 0;
474
- }
475
- if (isAbove) {
476
- while (i < end && polygon[i].y < pt.y)
477
- i++;
478
- }
479
- else {
480
- while (i < end && polygon[i].y > pt.y)
481
- i++;
482
- }
483
- if (i === end)
484
- continue;
485
- const curr = polygon[i];
486
- const prev = i > 0 ? polygon[i - 1] : polygon[len - 1];
487
- if (curr.y === pt.y) {
488
- if (curr.x === pt.x || (curr.y === prev.y &&
489
- ((pt.x < prev.x) !== (pt.x < curr.x)))) {
490
- return PointInPolygonResult.IsOn;
491
- }
360
+ return { success: true, ip };
361
+ }
362
+ function segsIntersect(seg1a, seg1b, seg2a, seg2b, inclusive = false) {
363
+ if (!inclusive) {
364
+ // Match C# fast path - use cross product multiplication
365
+ // This avoids floating point equality checks (safer than === 0)
366
+ const s1 = crossProductSign(seg1a, seg2a, seg2b);
367
+ const s2 = crossProductSign(seg1b, seg2a, seg2b);
368
+ const s3 = crossProductSign(seg2a, seg1a, seg1b);
369
+ const s4 = crossProductSign(seg2b, seg1a, seg1b);
370
+ return (s1 !== 0 && s2 !== 0 && s1 !== s2) &&
371
+ (s3 !== 0 && s4 !== 0 && s3 !== s4);
372
+ }
373
+ // Inclusive case - match C# implementation
374
+ const res1 = crossProductSign(seg1a, seg2a, seg2b);
375
+ const res2 = crossProductSign(seg1b, seg2a, seg2b);
376
+ if (res1 !== 0 && res1 === res2)
377
+ return false;
378
+ const res3 = crossProductSign(seg2a, seg1a, seg1b);
379
+ const res4 = crossProductSign(seg2b, seg1a, seg1b);
380
+ if (res3 !== 0 && res3 === res4)
381
+ return false;
382
+ // ensure NOT collinear
383
+ return (res1 !== 0 || res2 !== 0 || res3 !== 0 || res4 !== 0);
384
+ }
385
+ function icGetBounds(path) {
386
+ if (path.length === 0)
387
+ return { left: 0, top: 0, right: 0, bottom: 0 };
388
+ const result = {
389
+ left: Number.MAX_SAFE_INTEGER,
390
+ top: Number.MAX_SAFE_INTEGER,
391
+ right: Number.MIN_SAFE_INTEGER,
392
+ bottom: Number.MIN_SAFE_INTEGER
393
+ };
394
+ for (const pt of path) {
395
+ if (pt.x < result.left)
396
+ result.left = pt.x;
397
+ if (pt.x > result.right)
398
+ result.right = pt.x;
399
+ if (pt.y < result.top)
400
+ result.top = pt.y;
401
+ if (pt.y > result.bottom)
402
+ result.bottom = pt.y;
403
+ }
404
+ return result.left === Number.MAX_SAFE_INTEGER ?
405
+ { left: 0, top: 0, right: 0, bottom: 0 } : result;
406
+ }
407
+ function getClosestPtOnSegment(offPt, seg1, seg2) {
408
+ if (seg1.x === seg2.x && seg1.y === seg2.y)
409
+ return { x: seg1.x, y: seg1.y, z: 0 }; // Return copy, not reference
410
+ const dx = (seg2.x - seg1.x);
411
+ const dy = (seg2.y - seg1.y);
412
+ const q = safeMultiplySum((offPt.x - seg1.x), dx, (offPt.y - seg1.y), dy) /
413
+ safeMultiplySum(dx, dx, dy, dy);
414
+ const qClamped = q < 0 ? 0 : (q > 1 ? 1 : q);
415
+ return {
416
+ // use Math.round to match the C# MidpointRounding.ToEven behavior
417
+ x: Math.round(seg1.x + qClamped * dx),
418
+ y: Math.round(seg1.y + qClamped * dy),
419
+ z: 0
420
+ };
421
+ }
422
+ function icPointInPolygon(pt, polygon) {
423
+ const len = polygon.length;
424
+ let start = 0;
425
+ if (len < 3)
426
+ return PointInPolygonResult.IsOutside;
427
+ while (start < len && polygon[start].y === pt.y)
428
+ start++;
429
+ if (start === len)
430
+ return PointInPolygonResult.IsOutside;
431
+ let isAbove = polygon[start].y < pt.y;
432
+ const startingAbove = isAbove;
433
+ let val = 0;
434
+ let i = start + 1;
435
+ let end = len;
436
+ while (true) {
437
+ if (i === end) {
438
+ if (end === 0 || start === 0)
439
+ break;
440
+ end = start;
441
+ i = 0;
442
+ }
443
+ if (isAbove) {
444
+ while (i < end && polygon[i].y < pt.y)
492
445
  i++;
493
- if (i === start)
494
- break;
495
- continue;
496
- }
497
- if (pt.x < curr.x && pt.x < prev.x) {
498
- // we're only interested in edges crossing on the left
499
- }
500
- else if (pt.x > prev.x && pt.x > curr.x) {
501
- val = 1 - val; // toggle val
502
- }
503
- else {
504
- const cps = crossProductSign(prev, curr, pt);
505
- if (cps === 0)
506
- return PointInPolygonResult.IsOn;
507
- if ((cps < 0) === isAbove)
508
- val = 1 - val;
509
- }
510
- isAbove = !isAbove;
511
- i++;
512
446
  }
513
- if (isAbove === startingAbove) {
514
- return val === 0 ? PointInPolygonResult.IsOutside : PointInPolygonResult.IsInside;
447
+ else {
448
+ while (i < end && polygon[i].y > pt.y)
449
+ i++;
515
450
  }
516
- if (i === len)
517
- i = 0;
518
- const cps = i === 0 ?
519
- crossProductSign(polygon[len - 1], polygon[0], pt) :
520
- crossProductSign(polygon[i - 1], polygon[i], pt);
521
- if (cps === 0)
522
- return PointInPolygonResult.IsOn;
523
- if ((cps < 0) === isAbove)
524
- val = 1 - val;
525
- return val === 0 ? PointInPolygonResult.IsOutside : PointInPolygonResult.IsInside;
526
- }
527
- InternalClipper.pointInPolygon = pointInPolygon;
528
- function path2ContainsPath1(path1, path2) {
529
- // we need to make some accommodation for rounding errors
530
- // so we won't jump if the first vertex is found outside
531
- let pip = PointInPolygonResult.IsOn;
532
- for (const pt of path1) {
533
- switch (pointInPolygon(pt, path2)) {
534
- case PointInPolygonResult.IsOutside:
535
- if (pip === PointInPolygonResult.IsOutside)
536
- return false;
537
- pip = PointInPolygonResult.IsOutside;
538
- break;
539
- case PointInPolygonResult.IsInside:
540
- if (pip === PointInPolygonResult.IsInside)
541
- return true;
542
- pip = PointInPolygonResult.IsInside;
543
- break;
544
- default:
545
- break;
451
+ if (i === end)
452
+ continue;
453
+ const curr = polygon[i];
454
+ const prev = i > 0 ? polygon[i - 1] : polygon[len - 1];
455
+ if (curr.y === pt.y) {
456
+ if (curr.x === pt.x || (curr.y === prev.y &&
457
+ ((pt.x < prev.x) !== (pt.x < curr.x)))) {
458
+ return PointInPolygonResult.IsOn;
546
459
  }
460
+ i++;
461
+ if (i === start)
462
+ break;
463
+ continue;
464
+ }
465
+ if (pt.x < curr.x && pt.x < prev.x) {
466
+ // we're only interested in edges crossing on the left
547
467
  }
548
- // since path1's location is still equivocal, check its midpoint
549
- const mp = getBounds(path1);
550
- let midX, midY;
551
- if (Number.isSafeInteger(mp.left) && Number.isSafeInteger(mp.right) &&
552
- Math.abs(mp.left) + Math.abs(mp.right) > Number.MAX_SAFE_INTEGER) {
553
- midX = Number((BigInt(mp.left) + BigInt(mp.right)) / 2n);
554
- midY = Number((BigInt(mp.top) + BigInt(mp.bottom)) / 2n);
468
+ else if (pt.x > prev.x && pt.x > curr.x) {
469
+ val = 1 - val; // toggle val
555
470
  }
556
471
  else {
557
- midX = Math.round((mp.left + mp.right) / 2);
558
- midY = Math.round((mp.top + mp.bottom) / 2);
472
+ const cps = crossProductSign(prev, curr, pt);
473
+ if (cps === 0)
474
+ return PointInPolygonResult.IsOn;
475
+ if ((cps < 0) === isAbove)
476
+ val = 1 - val;
559
477
  }
560
- const midPt = { x: midX, y: midY };
561
- return pointInPolygon(midPt, path2) !== PointInPolygonResult.IsOutside;
562
- }
563
- InternalClipper.path2ContainsPath1 = path2ContainsPath1;
564
- })(InternalClipper || (InternalClipper = {}));
565
- // Point64 utility functions
566
- export var Point64Utils;
567
- (function (Point64Utils) {
568
- function create(x = 0, y = 0, z = 0) {
569
- return { x: Math.round(x), y: Math.round(y), z };
478
+ isAbove = !isAbove;
479
+ i++;
570
480
  }
571
- Point64Utils.create = create;
572
- function fromPointD(pt) {
481
+ if (isAbove === startingAbove) {
482
+ return val === 0 ? PointInPolygonResult.IsOutside : PointInPolygonResult.IsInside;
483
+ }
484
+ if (i === len)
485
+ i = 0;
486
+ const cps = i === 0 ?
487
+ crossProductSign(polygon[len - 1], polygon[0], pt) :
488
+ crossProductSign(polygon[i - 1], polygon[i], pt);
489
+ if (cps === 0)
490
+ return PointInPolygonResult.IsOn;
491
+ if ((cps < 0) === isAbove)
492
+ val = 1 - val;
493
+ return val === 0 ? PointInPolygonResult.IsOutside : PointInPolygonResult.IsInside;
494
+ }
495
+ function path2ContainsPath1(path1, path2) {
496
+ // we need to make some accommodation for rounding errors
497
+ // so we won't jump if the first vertex is found outside
498
+ let pip = PointInPolygonResult.IsOn;
499
+ for (const pt of path1) {
500
+ switch (icPointInPolygon(pt, path2)) {
501
+ case PointInPolygonResult.IsOutside:
502
+ if (pip === PointInPolygonResult.IsOutside)
503
+ return false;
504
+ pip = PointInPolygonResult.IsOutside;
505
+ break;
506
+ case PointInPolygonResult.IsInside:
507
+ if (pip === PointInPolygonResult.IsInside)
508
+ return true;
509
+ pip = PointInPolygonResult.IsInside;
510
+ break;
511
+ default:
512
+ break;
513
+ }
514
+ }
515
+ // since path1's location is still equivocal, check its midpoint
516
+ const mp = icGetBounds(path1);
517
+ let midX, midY;
518
+ if (Number.isSafeInteger(mp.left) && Number.isSafeInteger(mp.right) &&
519
+ Math.abs(mp.left) + Math.abs(mp.right) > Number.MAX_SAFE_INTEGER) {
520
+ midX = Number((BigInt(mp.left) + BigInt(mp.right)) / 2n);
521
+ midY = Number((BigInt(mp.top) + BigInt(mp.bottom)) / 2n);
522
+ }
523
+ else {
524
+ midX = Math.round((mp.left + mp.right) / 2);
525
+ midY = Math.round((mp.top + mp.bottom) / 2);
526
+ }
527
+ const midPt = { x: midX, y: midY };
528
+ return icPointInPolygon(midPt, path2) !== PointInPolygonResult.IsOutside;
529
+ }
530
+ // Plain const object replaces the namespace. External callers use InternalClipper.foo()
531
+ // unchanged; tsc emits a simple object literal instead of an IIFE.
532
+ export const InternalClipper = {
533
+ MaxInt64: IC_MaxInt64,
534
+ MaxCoord: IC_MaxCoord,
535
+ max_coord: IC_MaxCoord,
536
+ min_coord: -IC_MaxCoord,
537
+ Invalid64: IC_Invalid64,
538
+ floatingPointTolerance: IC_floatingPointTolerance,
539
+ defaultMinimumEdgeLength: IC_defaultMinimumEdgeLength,
540
+ maxCoordForSafeAreaProduct: IC_maxCoordForSafeAreaProduct,
541
+ maxCoordForSafeCrossSq: IC_maxCoordForSafeCrossSq,
542
+ maxSafeCoordinateForScale,
543
+ checkSafeScaleValue,
544
+ ensureSafeInteger,
545
+ crossProduct,
546
+ crossProductSign,
547
+ checkPrecision,
548
+ isAlmostZero,
549
+ triSign,
550
+ multiplyUInt64,
551
+ productsAreEqual,
552
+ isCollinear,
553
+ dotProduct,
554
+ dotProductSign,
555
+ area: icArea,
556
+ crossProductD,
557
+ dotProductD,
558
+ roundToEven,
559
+ checkCastInt64,
560
+ getLineIntersectPt,
561
+ getLineIntersectPtD,
562
+ segsIntersect,
563
+ getBounds: icGetBounds,
564
+ getClosestPtOnSegment,
565
+ pointInPolygon: icPointInPolygon,
566
+ path2ContainsPath1,
567
+ };
568
+ // Point64 utility functions (plain object, avoids namespace IIFE)
569
+ export const Point64Utils = {
570
+ create(x = 0, y = 0, z = 0) {
571
+ return { x: Math.round(x), y: Math.round(y), z };
572
+ },
573
+ fromPointD(pt) {
573
574
  InternalClipper.ensureSafeInteger(pt.x, "Point64Utils.fromPointD");
574
575
  InternalClipper.ensureSafeInteger(pt.y, "Point64Utils.fromPointD");
575
576
  return { x: Math.round(pt.x), y: Math.round(pt.y), z: pt.z || 0 };
576
- }
577
- Point64Utils.fromPointD = fromPointD;
578
- function scale(pt, scale) {
577
+ },
578
+ scale(pt, scale) {
579
579
  return {
580
580
  x: Math.round(pt.x * scale),
581
581
  y: Math.round(pt.y * scale),
582
582
  z: pt.z || 0
583
583
  };
584
- }
585
- Point64Utils.scale = scale;
586
- function equals(a, b) {
584
+ },
585
+ equals(a, b) {
587
586
  return a.x === b.x && a.y === b.y;
588
- }
589
- Point64Utils.equals = equals;
590
- function add(a, b) {
587
+ },
588
+ add(a, b) {
591
589
  if (Number.isSafeInteger(a.x) && Number.isSafeInteger(b.x) &&
592
590
  Number.isSafeInteger(a.y) && Number.isSafeInteger(b.y)) {
593
591
  const sumX = a.x + b.x;
@@ -602,86 +600,69 @@ export var Point64Utils;
602
600
  };
603
601
  }
604
602
  return { x: a.x + b.x, y: a.y + b.y, z: 0 };
605
- }
606
- Point64Utils.add = add;
607
- function subtract(a, b) {
603
+ },
604
+ subtract(a, b) {
608
605
  return { x: a.x - b.x, y: a.y - b.y, z: 0 };
609
- }
610
- Point64Utils.subtract = subtract;
611
- function toString(pt) {
606
+ },
607
+ toString(pt) {
612
608
  if (pt.z !== undefined && pt.z !== 0) {
613
609
  return `${pt.x},${pt.y},${pt.z} `;
614
610
  }
615
611
  return `${pt.x},${pt.y} `;
616
- }
617
- Point64Utils.toString = toString;
618
- })(Point64Utils || (Point64Utils = {}));
619
- // PointD utility functions
620
- export var PointDUtils;
621
- (function (PointDUtils) {
622
- function create(x = 0, y = 0, z = 0) {
612
+ },
613
+ };
614
+ // PointD utility functions (plain object, avoids namespace IIFE)
615
+ export const PointDUtils = {
616
+ create(x = 0, y = 0, z = 0) {
623
617
  return { x, y, z };
624
- }
625
- PointDUtils.create = create;
626
- function fromPoint64(pt) {
618
+ },
619
+ fromPoint64(pt) {
627
620
  return { x: pt.x, y: pt.y, z: pt.z || 0 };
628
- }
629
- PointDUtils.fromPoint64 = fromPoint64;
630
- function scale(pt, scale) {
621
+ },
622
+ scale(pt, scale) {
631
623
  return { x: pt.x * scale, y: pt.y * scale, z: pt.z || 0 };
632
- }
633
- PointDUtils.scale = scale;
634
- function equals(a, b) {
624
+ },
625
+ equals(a, b) {
635
626
  return InternalClipper.isAlmostZero(a.x - b.x) &&
636
627
  InternalClipper.isAlmostZero(a.y - b.y);
637
- }
638
- PointDUtils.equals = equals;
639
- function negate(pt) {
628
+ },
629
+ negate(pt) {
640
630
  pt.x = -pt.x;
641
631
  pt.y = -pt.y;
642
- }
643
- PointDUtils.negate = negate;
644
- function toString(pt, precision = 2) {
632
+ },
633
+ toString(pt, precision = 2) {
645
634
  if (pt.z !== undefined && pt.z !== 0) {
646
635
  return `${pt.x.toFixed(precision)},${pt.y.toFixed(precision)},${pt.z}`;
647
636
  }
648
637
  return `${pt.x.toFixed(precision)},${pt.y.toFixed(precision)}`;
649
- }
650
- PointDUtils.toString = toString;
651
- })(PointDUtils || (PointDUtils = {}));
652
- // Rect64 utility functions
653
- export var Rect64Utils;
654
- (function (Rect64Utils) {
655
- function create(l = 0, t = 0, r = 0, b = 0) {
638
+ },
639
+ };
640
+ // Rect64 utility functions (plain object, avoids namespace IIFE)
641
+ export const Rect64Utils = {
642
+ create(l = 0, t = 0, r = 0, b = 0) {
656
643
  return { left: l, top: t, right: r, bottom: b };
657
- }
658
- Rect64Utils.create = create;
659
- function createInvalid() {
644
+ },
645
+ createInvalid() {
660
646
  return {
661
647
  left: Number.MAX_SAFE_INTEGER,
662
648
  top: Number.MAX_SAFE_INTEGER,
663
649
  right: Number.MIN_SAFE_INTEGER,
664
650
  bottom: Number.MIN_SAFE_INTEGER
665
651
  };
666
- }
667
- Rect64Utils.createInvalid = createInvalid;
668
- function width(rect) {
652
+ },
653
+ width(rect) {
669
654
  return rect.right - rect.left;
670
- }
671
- Rect64Utils.width = width;
672
- function height(rect) {
655
+ },
656
+ height(rect) {
673
657
  return rect.bottom - rect.top;
674
- }
675
- Rect64Utils.height = height;
676
- function isEmpty(rect) {
658
+ },
659
+ isEmpty(rect) {
677
660
  return rect.bottom <= rect.top || rect.right <= rect.left;
678
- }
679
- Rect64Utils.isEmpty = isEmpty;
680
- function isValid(rect) {
661
+ },
662
+ isValid(rect) {
681
663
  return rect.left < Number.MAX_SAFE_INTEGER;
682
- }
683
- Rect64Utils.isValid = isValid;
684
- function midPoint(rect) {
664
+ },
665
+ midPoint(rect) {
685
666
  if (Number.isSafeInteger(rect.left) && Number.isSafeInteger(rect.right) &&
686
667
  Math.abs(rect.left) + Math.abs(rect.right) > Number.MAX_SAFE_INTEGER) {
687
668
  const midX = Number((BigInt(rect.left) + BigInt(rect.right)) / 2n);
@@ -692,105 +673,87 @@ export var Rect64Utils;
692
673
  x: Math.round((rect.left + rect.right) / 2),
693
674
  y: Math.round((rect.top + rect.bottom) / 2)
694
675
  };
695
- }
696
- Rect64Utils.midPoint = midPoint;
697
- function contains(rect, pt) {
676
+ },
677
+ contains(rect, pt) {
698
678
  return pt.x > rect.left && pt.x < rect.right &&
699
679
  pt.y > rect.top && pt.y < rect.bottom;
700
- }
701
- Rect64Utils.contains = contains;
702
- function containsRect(rect, rec) {
680
+ },
681
+ containsRect(rect, rec) {
703
682
  return rec.left >= rect.left && rec.right <= rect.right &&
704
683
  rec.top >= rect.top && rec.bottom <= rect.bottom;
705
- }
706
- Rect64Utils.containsRect = containsRect;
707
- function intersects(rect, rec) {
684
+ },
685
+ intersects(rect, rec) {
708
686
  return (Math.max(rect.left, rec.left) <= Math.min(rect.right, rec.right)) &&
709
687
  (Math.max(rect.top, rec.top) <= Math.min(rect.bottom, rec.bottom));
710
- }
711
- Rect64Utils.intersects = intersects;
712
- function asPath(rect) {
688
+ },
689
+ asPath(rect) {
713
690
  return [
714
691
  { x: rect.left, y: rect.top, z: 0 },
715
692
  { x: rect.right, y: rect.top, z: 0 },
716
693
  { x: rect.right, y: rect.bottom, z: 0 },
717
694
  { x: rect.left, y: rect.bottom, z: 0 }
718
695
  ];
719
- }
720
- Rect64Utils.asPath = asPath;
721
- })(Rect64Utils || (Rect64Utils = {}));
722
- // RectD utility functions
723
- export var RectDUtils;
724
- (function (RectDUtils) {
725
- function create(l = 0, t = 0, r = 0, b = 0) {
696
+ },
697
+ };
698
+ // RectD utility functions (plain object, avoids namespace IIFE)
699
+ export const RectDUtils = {
700
+ create(l = 0, t = 0, r = 0, b = 0) {
726
701
  return { left: l, top: t, right: r, bottom: b };
727
- }
728
- RectDUtils.create = create;
729
- function createInvalid() {
702
+ },
703
+ createInvalid() {
730
704
  return {
731
705
  left: Number.MAX_VALUE,
732
706
  top: Number.MAX_VALUE,
733
707
  right: -Number.MAX_VALUE,
734
708
  bottom: -Number.MAX_VALUE
735
709
  };
736
- }
737
- RectDUtils.createInvalid = createInvalid;
738
- function width(rect) {
710
+ },
711
+ width(rect) {
739
712
  return rect.right - rect.left;
740
- }
741
- RectDUtils.width = width;
742
- function height(rect) {
713
+ },
714
+ height(rect) {
743
715
  return rect.bottom - rect.top;
744
- }
745
- RectDUtils.height = height;
746
- function isEmpty(rect) {
716
+ },
717
+ isEmpty(rect) {
747
718
  return rect.bottom <= rect.top || rect.right <= rect.left;
748
- }
749
- RectDUtils.isEmpty = isEmpty;
750
- function midPoint(rect) {
719
+ },
720
+ midPoint(rect) {
751
721
  return {
752
722
  x: (rect.left + rect.right) / 2,
753
723
  y: (rect.top + rect.bottom) / 2
754
724
  };
755
- }
756
- RectDUtils.midPoint = midPoint;
757
- function contains(rect, pt) {
725
+ },
726
+ contains(rect, pt) {
758
727
  return pt.x > rect.left && pt.x < rect.right &&
759
728
  pt.y > rect.top && pt.y < rect.bottom;
760
- }
761
- RectDUtils.contains = contains;
762
- function containsRect(rect, rec) {
729
+ },
730
+ containsRect(rect, rec) {
763
731
  return rec.left >= rect.left && rec.right <= rect.right &&
764
732
  rec.top >= rect.top && rec.bottom <= rect.bottom;
765
- }
766
- RectDUtils.containsRect = containsRect;
767
- function intersects(rect, rec) {
733
+ },
734
+ intersects(rect, rec) {
768
735
  return (Math.max(rect.left, rec.left) < Math.min(rect.right, rec.right)) &&
769
736
  (Math.max(rect.top, rec.top) < Math.min(rect.bottom, rec.bottom));
770
- }
771
- RectDUtils.intersects = intersects;
772
- function asPath(rect) {
737
+ },
738
+ asPath(rect) {
773
739
  return [
774
740
  { x: rect.left, y: rect.top, z: 0 },
775
741
  { x: rect.right, y: rect.top, z: 0 },
776
742
  { x: rect.right, y: rect.bottom, z: 0 },
777
743
  { x: rect.left, y: rect.bottom, z: 0 }
778
744
  ];
779
- }
780
- RectDUtils.asPath = asPath;
781
- })(RectDUtils || (RectDUtils = {}));
782
- // Path utility functions
783
- export var PathUtils;
784
- (function (PathUtils) {
785
- function toString64(path) {
745
+ },
746
+ };
747
+ // Path utility functions (plain object, avoids namespace IIFE)
748
+ export const PathUtils = {
749
+ toString64(path) {
786
750
  let result = "";
787
751
  for (const pt of path) {
788
752
  result += Point64Utils.toString(pt);
789
753
  }
790
754
  return result + '\n';
791
- }
792
- PathUtils.toString64 = toString64;
793
- function toStringD(path, precision = 2) {
755
+ },
756
+ toStringD(path, precision = 2) {
794
757
  let result = "";
795
758
  for (const pt of path) {
796
759
  result += PointDUtils.toString(pt, precision) + ", ";
@@ -798,45 +761,37 @@ export var PathUtils;
798
761
  if (result !== "")
799
762
  result = result.slice(0, -2);
800
763
  return result;
801
- }
802
- PathUtils.toStringD = toStringD;
803
- function reverse64(path) {
764
+ },
765
+ reverse64(path) {
804
766
  return [...path].reverse();
805
- }
806
- PathUtils.reverse64 = reverse64;
807
- function reverseD(path) {
767
+ },
768
+ reverseD(path) {
808
769
  return [...path].reverse();
809
- }
810
- PathUtils.reverseD = reverseD;
811
- })(PathUtils || (PathUtils = {}));
812
- export var PathsUtils;
813
- (function (PathsUtils) {
814
- function toString64(paths) {
770
+ },
771
+ };
772
+ export const PathsUtils = {
773
+ toString64(paths) {
815
774
  let result = "";
816
775
  for (const path of paths) {
817
776
  result += PathUtils.toString64(path);
818
777
  }
819
778
  return result;
820
- }
821
- PathsUtils.toString64 = toString64;
822
- function toStringD(paths, precision = 2) {
779
+ },
780
+ toStringD(paths, precision = 2) {
823
781
  let result = "";
824
782
  for (const path of paths) {
825
783
  result += PathUtils.toStringD(path, precision) + "\n";
826
784
  }
827
785
  return result;
828
- }
829
- PathsUtils.toStringD = toStringD;
830
- function reverse64(paths) {
786
+ },
787
+ reverse64(paths) {
831
788
  return paths.map(path => PathUtils.reverse64(path));
832
- }
833
- PathsUtils.reverse64 = reverse64;
834
- function reverseD(paths) {
789
+ },
790
+ reverseD(paths) {
835
791
  return paths.map(path => PathUtils.reverseD(path));
836
- }
837
- PathsUtils.reverseD = reverseD;
838
- })(PathsUtils || (PathsUtils = {}));
839
- // Constants
840
- export const InvalidRect64 = Rect64Utils.createInvalid();
841
- export const InvalidRectD = RectDUtils.createInvalid();
792
+ },
793
+ };
794
+ // Constants (frozen to prevent accidental mutation of shared singletons)
795
+ export const InvalidRect64 = Object.freeze(Rect64Utils.createInvalid());
796
+ export const InvalidRectD = Object.freeze(RectDUtils.createInvalid());
842
797
  //# sourceMappingURL=Core.js.map