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.
- package/README.md +12 -10
- package/dist/Core.d.ts +126 -98
- package/dist/Core.d.ts.map +1 -1
- package/dist/Core.js +610 -655
- package/dist/Core.js.map +1 -1
- package/dist/Engine.d.ts +12 -17
- package/dist/Engine.d.ts.map +1 -1
- package/dist/Engine.js +70 -80
- package/dist/Engine.js.map +1 -1
- package/dist/Minkowski.d.ts +6 -6
- package/dist/Minkowski.d.ts.map +1 -1
- package/dist/Minkowski.js +92 -99
- package/dist/Minkowski.js.map +1 -1
- package/dist/Offset.d.ts.map +1 -1
- package/dist/Offset.js +6 -3
- package/dist/Offset.js.map +1 -1
- package/dist/RectClip.d.ts +1 -0
- package/dist/RectClip.d.ts.map +1 -1
- package/dist/RectClip.js +130 -93
- package/dist/RectClip.js.map +1 -1
- package/dist/Triangulation.d.ts.map +1 -1
- package/dist/Triangulation.js +21 -23
- package/dist/Triangulation.js.map +1 -1
- package/dist/clipper2.min.mjs +7 -0
- package/dist/clipper2.min.mjs.map +1 -0
- package/dist/index.d.ts +3 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/package.json +10 -2
- package/src/Core.ts +617 -583
- package/src/Engine.ts +70 -73
- package/src/Minkowski.ts +102 -105
- package/src/Offset.ts +6 -3
- package/src/RectClip.ts +63 -114
- package/src/Triangulation.ts +20 -22
- package/src/index.ts +14 -12
- package/dist/OutPtPool.d.ts +0 -12
- package/dist/OutPtPool.d.ts.map +0 -1
- package/dist/OutPtPool.js +0 -40
- package/dist/OutPtPool.js.map +0 -1
- package/dist/VertexPool.d.ts +0 -12
- package/dist/VertexPool.d.ts.map +0 -1
- package/dist/VertexPool.js +0 -31
- 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
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
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
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
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
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
return
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
const
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
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
|
-
|
|
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
|
|
300
|
-
}
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
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 (
|
|
379
|
-
|
|
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
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
};
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
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
|
-
|
|
448
|
-
|
|
449
|
-
|
|
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
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
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
|
-
|
|
514
|
-
|
|
447
|
+
else {
|
|
448
|
+
while (i < end && polygon[i].y > pt.y)
|
|
449
|
+
i++;
|
|
515
450
|
}
|
|
516
|
-
if (i ===
|
|
517
|
-
|
|
518
|
-
const
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
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
|
-
|
|
549
|
-
|
|
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
|
-
|
|
558
|
-
|
|
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
|
-
|
|
561
|
-
|
|
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
|
-
|
|
572
|
-
|
|
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
|
-
|
|
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
|
-
|
|
586
|
-
function equals(a, b) {
|
|
584
|
+
},
|
|
585
|
+
equals(a, b) {
|
|
587
586
|
return a.x === b.x && a.y === b.y;
|
|
588
|
-
}
|
|
589
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
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
|
-
|
|
626
|
-
function fromPoint64(pt) {
|
|
618
|
+
},
|
|
619
|
+
fromPoint64(pt) {
|
|
627
620
|
return { x: pt.x, y: pt.y, z: pt.z || 0 };
|
|
628
|
-
}
|
|
629
|
-
|
|
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
|
-
|
|
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
|
-
|
|
639
|
-
function negate(pt) {
|
|
628
|
+
},
|
|
629
|
+
negate(pt) {
|
|
640
630
|
pt.x = -pt.x;
|
|
641
631
|
pt.y = -pt.y;
|
|
642
|
-
}
|
|
643
|
-
|
|
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
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
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
|
-
|
|
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
|
-
|
|
668
|
-
function width(rect) {
|
|
652
|
+
},
|
|
653
|
+
width(rect) {
|
|
669
654
|
return rect.right - rect.left;
|
|
670
|
-
}
|
|
671
|
-
|
|
672
|
-
function height(rect) {
|
|
655
|
+
},
|
|
656
|
+
height(rect) {
|
|
673
657
|
return rect.bottom - rect.top;
|
|
674
|
-
}
|
|
675
|
-
|
|
676
|
-
function isEmpty(rect) {
|
|
658
|
+
},
|
|
659
|
+
isEmpty(rect) {
|
|
677
660
|
return rect.bottom <= rect.top || rect.right <= rect.left;
|
|
678
|
-
}
|
|
679
|
-
|
|
680
|
-
function isValid(rect) {
|
|
661
|
+
},
|
|
662
|
+
isValid(rect) {
|
|
681
663
|
return rect.left < Number.MAX_SAFE_INTEGER;
|
|
682
|
-
}
|
|
683
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
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
|
-
|
|
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
|
-
|
|
738
|
-
function width(rect) {
|
|
710
|
+
},
|
|
711
|
+
width(rect) {
|
|
739
712
|
return rect.right - rect.left;
|
|
740
|
-
}
|
|
741
|
-
|
|
742
|
-
function height(rect) {
|
|
713
|
+
},
|
|
714
|
+
height(rect) {
|
|
743
715
|
return rect.bottom - rect.top;
|
|
744
|
-
}
|
|
745
|
-
|
|
746
|
-
function isEmpty(rect) {
|
|
716
|
+
},
|
|
717
|
+
isEmpty(rect) {
|
|
747
718
|
return rect.bottom <= rect.top || rect.right <= rect.left;
|
|
748
|
-
}
|
|
749
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
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
|
-
|
|
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
|
-
|
|
803
|
-
function reverse64(path) {
|
|
764
|
+
},
|
|
765
|
+
reverse64(path) {
|
|
804
766
|
return [...path].reverse();
|
|
805
|
-
}
|
|
806
|
-
|
|
807
|
-
function reverseD(path) {
|
|
767
|
+
},
|
|
768
|
+
reverseD(path) {
|
|
808
769
|
return [...path].reverse();
|
|
809
|
-
}
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
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
|
-
|
|
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
|
-
|
|
830
|
-
function reverse64(paths) {
|
|
786
|
+
},
|
|
787
|
+
reverse64(paths) {
|
|
831
788
|
return paths.map(path => PathUtils.reverse64(path));
|
|
832
|
-
}
|
|
833
|
-
|
|
834
|
-
function reverseD(paths) {
|
|
789
|
+
},
|
|
790
|
+
reverseD(paths) {
|
|
835
791
|
return paths.map(path => PathUtils.reverseD(path));
|
|
836
|
-
}
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
export const
|
|
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
|