mapshaper 0.6.109 → 0.6.110
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/mapshaper-gui +3 -2
- package/mapshaper.js +1761 -501
- package/package.json +2 -2
- package/www/assets/SourceSans3-VariableFont_wght.ttf +0 -0
- package/www/elements.css +1 -1
- package/www/index.html +45 -51
- package/www/mapshaper-gui.js +186 -19
- package/www/mapshaper.js +1761 -501
- package/www/page.css +21 -23
- package/www/assets/SourceSansPro-Regular.woff +0 -0
- package/www/assets/SourceSansPro-Semibold.woff +0 -0
package/mapshaper.js
CHANGED
|
@@ -1328,13 +1328,21 @@
|
|
|
1328
1328
|
|
|
1329
1329
|
function verbose() {
|
|
1330
1330
|
// verbose can be set globally with the -verbose command or separately for each command
|
|
1331
|
-
if (
|
|
1331
|
+
if (useVerbose()) {
|
|
1332
1332
|
message.apply(null, arguments);
|
|
1333
1333
|
}
|
|
1334
1334
|
}
|
|
1335
1335
|
|
|
1336
|
+
function useVerbose() {
|
|
1337
|
+
return getStashedVar('VERBOSE');
|
|
1338
|
+
}
|
|
1339
|
+
|
|
1340
|
+
function useDebug() {
|
|
1341
|
+
return getStashedVar('DEBUG');
|
|
1342
|
+
}
|
|
1343
|
+
|
|
1336
1344
|
function debug() {
|
|
1337
|
-
if (
|
|
1345
|
+
if (useDebug()) {
|
|
1338
1346
|
logArgs(arguments);
|
|
1339
1347
|
}
|
|
1340
1348
|
}
|
|
@@ -1458,6 +1466,8 @@
|
|
|
1458
1466
|
setLoggingFunctions: setLoggingFunctions,
|
|
1459
1467
|
stop: stop,
|
|
1460
1468
|
truncateString: truncateString,
|
|
1469
|
+
useDebug: useDebug,
|
|
1470
|
+
useVerbose: useVerbose,
|
|
1461
1471
|
verbose: verbose,
|
|
1462
1472
|
warn: warn
|
|
1463
1473
|
});
|
|
@@ -2265,44 +2275,6 @@
|
|
|
2265
2275
|
return dist * R$1;
|
|
2266
2276
|
}
|
|
2267
2277
|
|
|
2268
|
-
// TODO: make this safe for small angles
|
|
2269
|
-
function innerAngle(ax, ay, bx, by, cx, cy) {
|
|
2270
|
-
var ab = distance2D(ax, ay, bx, by),
|
|
2271
|
-
bc = distance2D(bx, by, cx, cy),
|
|
2272
|
-
theta, dotp;
|
|
2273
|
-
if (ab === 0 || bc === 0) {
|
|
2274
|
-
theta = 0;
|
|
2275
|
-
} else {
|
|
2276
|
-
dotp = ((ax - bx) * (cx - bx) + (ay - by) * (cy - by)) / (ab * bc);
|
|
2277
|
-
if (dotp >= 1 - 1e-14) {
|
|
2278
|
-
theta = 0;
|
|
2279
|
-
} else if (dotp <= -1 + 1e-14) {
|
|
2280
|
-
theta = Math.PI;
|
|
2281
|
-
} else {
|
|
2282
|
-
theta = Math.acos(dotp); // consider using other formula at small dp
|
|
2283
|
-
}
|
|
2284
|
-
}
|
|
2285
|
-
return theta;
|
|
2286
|
-
}
|
|
2287
|
-
|
|
2288
|
-
function innerAngle3D(ax, ay, az, bx, by, bz, cx, cy, cz) {
|
|
2289
|
-
var ab = distance3D(ax, ay, az, bx, by, bz),
|
|
2290
|
-
bc = distance3D(bx, by, bz, cx, cy, cz),
|
|
2291
|
-
theta, dotp;
|
|
2292
|
-
if (ab === 0 || bc === 0) {
|
|
2293
|
-
theta = 0;
|
|
2294
|
-
} else {
|
|
2295
|
-
dotp = ((ax - bx) * (cx - bx) + (ay - by) * (cy - by) + (az - bz) * (cz - bz)) / (ab * bc);
|
|
2296
|
-
if (dotp >= 1) {
|
|
2297
|
-
theta = 0;
|
|
2298
|
-
} else if (dotp <= -1) {
|
|
2299
|
-
theta = Math.PI;
|
|
2300
|
-
} else {
|
|
2301
|
-
theta = Math.acos(dotp); // consider using other formula at small dp
|
|
2302
|
-
}
|
|
2303
|
-
}
|
|
2304
|
-
return theta;
|
|
2305
|
-
}
|
|
2306
2278
|
|
|
2307
2279
|
function triangleArea(ax, ay, bx, by, cx, cy) {
|
|
2308
2280
|
var area = Math.abs(((ay - cy) * (bx - cx) + (by - cy) * (cx - ax)) / 2);
|
|
@@ -2391,36 +2363,10 @@
|
|
|
2391
2363
|
return distanceSq(px, py, ax + t * (bx - ax), ay + t * (by - ay));
|
|
2392
2364
|
}
|
|
2393
2365
|
|
|
2394
|
-
|
|
2395
|
-
// internal.reversePathCoords = function(arr, start, len) {
|
|
2396
|
-
// var i = start,
|
|
2397
|
-
// j = start + len - 1,
|
|
2398
|
-
// tmp;
|
|
2399
|
-
// while (i < j) {
|
|
2400
|
-
// tmp = arr[i];
|
|
2401
|
-
// arr[i] = arr[j];
|
|
2402
|
-
// arr[j] = tmp;
|
|
2403
|
-
// i++;
|
|
2404
|
-
// j--;
|
|
2405
|
-
// }
|
|
2406
|
-
// };
|
|
2407
|
-
|
|
2408
|
-
// merge B into A
|
|
2409
|
-
// function mergeBounds(a, b) {
|
|
2410
|
-
// if (b[0] < a[0]) a[0] = b[0];
|
|
2411
|
-
// if (b[1] < a[1]) a[1] = b[1];
|
|
2412
|
-
// if (b[2] > a[2]) a[2] = b[2];
|
|
2413
|
-
// if (b[3] > a[3]) a[3] = b[3];
|
|
2414
|
-
// }
|
|
2415
|
-
|
|
2416
2366
|
function containsBounds(a, b) {
|
|
2417
2367
|
return a[0] <= b[0] && a[2] >= b[2] && a[1] <= b[1] && a[3] >= b[3];
|
|
2418
2368
|
}
|
|
2419
2369
|
|
|
2420
|
-
// function boundsArea(b) {
|
|
2421
|
-
// return (b[2] - b[0]) * (b[3] - b[1]);
|
|
2422
|
-
// }
|
|
2423
|
-
|
|
2424
2370
|
var Geom = /*#__PURE__*/Object.freeze({
|
|
2425
2371
|
__proto__: null,
|
|
2426
2372
|
D2R: D2R,
|
|
@@ -2438,9 +2384,7 @@
|
|
|
2438
2384
|
distanceSq: distanceSq,
|
|
2439
2385
|
distanceSq3D: distanceSq3D,
|
|
2440
2386
|
greatCircleDistance: greatCircleDistance,
|
|
2441
|
-
innerAngle: innerAngle,
|
|
2442
2387
|
innerAngle2: innerAngle2,
|
|
2443
|
-
innerAngle3D: innerAngle3D,
|
|
2444
2388
|
lngLatToXYZ: lngLatToXYZ,
|
|
2445
2389
|
pointSegDistSq: pointSegDistSq,
|
|
2446
2390
|
pointSegDistSq2: pointSegDistSq2,
|
|
@@ -2962,7 +2906,22 @@
|
|
|
2962
2906
|
// @coords: Array of relevant coordinates (e.g. bbox coordinates of vertex coordinates
|
|
2963
2907
|
// of two intersecting segments).
|
|
2964
2908
|
//
|
|
2909
|
+
|
|
2910
|
+
// Updated the original function with a smaller interval... which works
|
|
2911
|
+
// better on a (limited) set of real-world sample data
|
|
2912
|
+
// (less likely to create erroneous output)
|
|
2965
2913
|
function getHighPrecisionSnapInterval(coords) {
|
|
2914
|
+
var n = Math.max.apply(null, coords.map(Math.abs));
|
|
2915
|
+
var ceil = n <= 1 ? 1 : 2 ** Math.ceil(Math.log2(n));
|
|
2916
|
+
// console.log(ceil < ceil + ceil / 2 ** 51) // true
|
|
2917
|
+
// console.log(ceil < ceil + ceil / 2 ** 52) // true
|
|
2918
|
+
// console.log(ceil < ceil + ceil / 2 ** 53) // false
|
|
2919
|
+
var interval = ceil / 2 ** 51;
|
|
2920
|
+
// console.log('interval:', interval)
|
|
2921
|
+
return interval;
|
|
2922
|
+
}
|
|
2923
|
+
|
|
2924
|
+
function getHighPrecisionSnapInterval_old(coords) {
|
|
2966
2925
|
var maxCoord = Math.max.apply(null, coords.map(Math.abs));
|
|
2967
2926
|
return maxCoord * 1e-14;
|
|
2968
2927
|
}
|
|
@@ -3148,11 +3107,1044 @@
|
|
|
3148
3107
|
getCoordinateIds: getCoordinateIds,
|
|
3149
3108
|
getEndpointIds: getEndpointIds,
|
|
3150
3109
|
getHighPrecisionSnapInterval: getHighPrecisionSnapInterval,
|
|
3110
|
+
getHighPrecisionSnapInterval_old: getHighPrecisionSnapInterval_old,
|
|
3151
3111
|
snapCoords: snapCoords,
|
|
3152
3112
|
snapCoordsByInterval: snapCoordsByInterval,
|
|
3153
3113
|
snapEndpointsByInterval: snapEndpointsByInterval
|
|
3154
3114
|
});
|
|
3155
3115
|
|
|
3116
|
+
/*
|
|
3117
|
+
* big.js v7.0.1
|
|
3118
|
+
* A small, fast, easy-to-use library for arbitrary-precision decimal arithmetic.
|
|
3119
|
+
* Copyright (c) 2025 Michael Mclaughlin
|
|
3120
|
+
* https://github.com/MikeMcl/big.js/LICENCE.md
|
|
3121
|
+
*/
|
|
3122
|
+
|
|
3123
|
+
|
|
3124
|
+
/************************************** EDITABLE DEFAULTS *****************************************/
|
|
3125
|
+
|
|
3126
|
+
|
|
3127
|
+
// The default values below must be integers within the stated ranges.
|
|
3128
|
+
|
|
3129
|
+
/*
|
|
3130
|
+
* The maximum number of decimal places (DP) of the results of operations involving division:
|
|
3131
|
+
* div and sqrt, and pow with negative exponents.
|
|
3132
|
+
*/
|
|
3133
|
+
var DP = 20, // 0 to MAX_DP
|
|
3134
|
+
|
|
3135
|
+
/*
|
|
3136
|
+
* The rounding mode (RM) used when rounding to the above decimal places.
|
|
3137
|
+
*
|
|
3138
|
+
* 0 Towards zero (i.e. truncate, no rounding). (ROUND_DOWN)
|
|
3139
|
+
* 1 To nearest neighbour. If equidistant, round up. (ROUND_HALF_UP)
|
|
3140
|
+
* 2 To nearest neighbour. If equidistant, to even. (ROUND_HALF_EVEN)
|
|
3141
|
+
* 3 Away from zero. (ROUND_UP)
|
|
3142
|
+
*/
|
|
3143
|
+
RM = 1, // 0, 1, 2 or 3
|
|
3144
|
+
|
|
3145
|
+
// The maximum value of DP and Big.DP.
|
|
3146
|
+
MAX_DP = 1E6, // 0 to 1000000
|
|
3147
|
+
|
|
3148
|
+
// The maximum magnitude of the exponent argument to the pow method.
|
|
3149
|
+
MAX_POWER = 1E6, // 1 to 1000000
|
|
3150
|
+
|
|
3151
|
+
/*
|
|
3152
|
+
* The negative exponent (NE) at and beneath which toString returns exponential notation.
|
|
3153
|
+
* (JavaScript numbers: -7)
|
|
3154
|
+
* -1000000 is the minimum recommended exponent value of a Big.
|
|
3155
|
+
*/
|
|
3156
|
+
NE = -7, // 0 to -1000000
|
|
3157
|
+
|
|
3158
|
+
/*
|
|
3159
|
+
* The positive exponent (PE) at and above which toString returns exponential notation.
|
|
3160
|
+
* (JavaScript numbers: 21)
|
|
3161
|
+
* 1000000 is the maximum recommended exponent value of a Big, but this limit is not enforced.
|
|
3162
|
+
*/
|
|
3163
|
+
PE = 21, // 0 to 1000000
|
|
3164
|
+
|
|
3165
|
+
/*
|
|
3166
|
+
* When true, an error will be thrown if a primitive number is passed to the Big constructor,
|
|
3167
|
+
* or if valueOf is called, or if toNumber is called on a Big which cannot be converted to a
|
|
3168
|
+
* primitive number without a loss of precision.
|
|
3169
|
+
*/
|
|
3170
|
+
STRICT = false, // true or false
|
|
3171
|
+
|
|
3172
|
+
|
|
3173
|
+
/**************************************************************************************************/
|
|
3174
|
+
|
|
3175
|
+
|
|
3176
|
+
// Error messages.
|
|
3177
|
+
NAME = '[big.js] ',
|
|
3178
|
+
INVALID = NAME + 'Invalid ',
|
|
3179
|
+
INVALID_DP = INVALID + 'decimal places',
|
|
3180
|
+
INVALID_RM = INVALID + 'rounding mode',
|
|
3181
|
+
DIV_BY_ZERO = NAME + 'Division by zero',
|
|
3182
|
+
|
|
3183
|
+
// The shared prototype object.
|
|
3184
|
+
P = {},
|
|
3185
|
+
UNDEFINED = void 0,
|
|
3186
|
+
NUMERIC = /^-?(\d+(\.\d*)?|\.\d+)(e[+-]?\d+)?$/i;
|
|
3187
|
+
|
|
3188
|
+
|
|
3189
|
+
/*
|
|
3190
|
+
* Create and return a Big constructor.
|
|
3191
|
+
*/
|
|
3192
|
+
function _Big_() {
|
|
3193
|
+
|
|
3194
|
+
/*
|
|
3195
|
+
* The Big constructor and exported function.
|
|
3196
|
+
* Create and return a new instance of a Big number object.
|
|
3197
|
+
*
|
|
3198
|
+
* n {number|string|Big} A numeric value.
|
|
3199
|
+
*/
|
|
3200
|
+
function Big(n) {
|
|
3201
|
+
var x = this;
|
|
3202
|
+
|
|
3203
|
+
// Enable constructor usage without new.
|
|
3204
|
+
if (!(x instanceof Big)) {
|
|
3205
|
+
return n === UNDEFINED && arguments.length === 0 ? _Big_() : new Big(n);
|
|
3206
|
+
}
|
|
3207
|
+
|
|
3208
|
+
|
|
3209
|
+
// Duplicate.
|
|
3210
|
+
if (n instanceof Big) {
|
|
3211
|
+
x.s = n.s;
|
|
3212
|
+
x.e = n.e;
|
|
3213
|
+
x.c = n.c.slice();
|
|
3214
|
+
} else {
|
|
3215
|
+
if (typeof n !== 'string') {
|
|
3216
|
+
if (Big.strict === true && typeof n !== 'bigint') {
|
|
3217
|
+
throw TypeError(INVALID + 'value');
|
|
3218
|
+
}
|
|
3219
|
+
|
|
3220
|
+
// Minus zero?
|
|
3221
|
+
n = n === 0 && 1 / n < 0 ? '-0' : String(n);
|
|
3222
|
+
}
|
|
3223
|
+
|
|
3224
|
+
parse(x, n);
|
|
3225
|
+
}
|
|
3226
|
+
|
|
3227
|
+
// Retain a reference to this Big constructor.
|
|
3228
|
+
// Shadow Big.prototype.constructor which points to Object.
|
|
3229
|
+
x.constructor = Big;
|
|
3230
|
+
}
|
|
3231
|
+
|
|
3232
|
+
Big.prototype = P;
|
|
3233
|
+
Big.DP = DP;
|
|
3234
|
+
Big.RM = RM;
|
|
3235
|
+
Big.NE = NE;
|
|
3236
|
+
Big.PE = PE;
|
|
3237
|
+
Big.strict = STRICT;
|
|
3238
|
+
Big.roundDown = 0;
|
|
3239
|
+
Big.roundHalfUp = 1;
|
|
3240
|
+
Big.roundHalfEven = 2;
|
|
3241
|
+
Big.roundUp = 3;
|
|
3242
|
+
|
|
3243
|
+
return Big;
|
|
3244
|
+
}
|
|
3245
|
+
|
|
3246
|
+
|
|
3247
|
+
/*
|
|
3248
|
+
* Parse the number or string value passed to a Big constructor.
|
|
3249
|
+
*
|
|
3250
|
+
* x {Big} A Big number instance.
|
|
3251
|
+
* n {number|string} A numeric value.
|
|
3252
|
+
*/
|
|
3253
|
+
function parse(x, n) {
|
|
3254
|
+
var e, i, nl;
|
|
3255
|
+
|
|
3256
|
+
if (!NUMERIC.test(n)) {
|
|
3257
|
+
throw Error(INVALID + 'number');
|
|
3258
|
+
}
|
|
3259
|
+
|
|
3260
|
+
// Determine sign.
|
|
3261
|
+
x.s = n.charAt(0) == '-' ? (n = n.slice(1), -1) : 1;
|
|
3262
|
+
|
|
3263
|
+
// Decimal point?
|
|
3264
|
+
if ((e = n.indexOf('.')) > -1) n = n.replace('.', '');
|
|
3265
|
+
|
|
3266
|
+
// Exponential form?
|
|
3267
|
+
if ((i = n.search(/e/i)) > 0) {
|
|
3268
|
+
|
|
3269
|
+
// Determine exponent.
|
|
3270
|
+
if (e < 0) e = i;
|
|
3271
|
+
e += +n.slice(i + 1);
|
|
3272
|
+
n = n.substring(0, i);
|
|
3273
|
+
} else if (e < 0) {
|
|
3274
|
+
|
|
3275
|
+
// Integer.
|
|
3276
|
+
e = n.length;
|
|
3277
|
+
}
|
|
3278
|
+
|
|
3279
|
+
nl = n.length;
|
|
3280
|
+
|
|
3281
|
+
// Determine leading zeros.
|
|
3282
|
+
for (i = 0; i < nl && n.charAt(i) == '0';) ++i;
|
|
3283
|
+
|
|
3284
|
+
if (i == nl) {
|
|
3285
|
+
|
|
3286
|
+
// Zero.
|
|
3287
|
+
x.c = [x.e = 0];
|
|
3288
|
+
} else {
|
|
3289
|
+
|
|
3290
|
+
// Determine trailing zeros.
|
|
3291
|
+
for (; nl > 0 && n.charAt(--nl) == '0';);
|
|
3292
|
+
x.e = e - i - 1;
|
|
3293
|
+
x.c = [];
|
|
3294
|
+
|
|
3295
|
+
// Convert string to array of digits without leading/trailing zeros.
|
|
3296
|
+
for (e = 0; i <= nl;) x.c[e++] = +n.charAt(i++);
|
|
3297
|
+
}
|
|
3298
|
+
|
|
3299
|
+
return x;
|
|
3300
|
+
}
|
|
3301
|
+
|
|
3302
|
+
|
|
3303
|
+
/*
|
|
3304
|
+
* Round Big x to a maximum of sd significant digits using rounding mode rm.
|
|
3305
|
+
*
|
|
3306
|
+
* x {Big} The Big to round.
|
|
3307
|
+
* sd {number} Significant digits: integer, 0 to MAX_DP inclusive.
|
|
3308
|
+
* rm {number} Rounding mode: 0 (down), 1 (half-up), 2 (half-even) or 3 (up).
|
|
3309
|
+
* [more] {boolean} Whether the result of division was truncated.
|
|
3310
|
+
*/
|
|
3311
|
+
function round(x, sd, rm, more) {
|
|
3312
|
+
var xc = x.c;
|
|
3313
|
+
|
|
3314
|
+
if (rm === UNDEFINED) rm = x.constructor.RM;
|
|
3315
|
+
if (rm !== 0 && rm !== 1 && rm !== 2 && rm !== 3) {
|
|
3316
|
+
throw Error(INVALID_RM);
|
|
3317
|
+
}
|
|
3318
|
+
|
|
3319
|
+
if (sd < 1) {
|
|
3320
|
+
more =
|
|
3321
|
+
rm === 3 && (more || !!xc[0]) || sd === 0 && (
|
|
3322
|
+
rm === 1 && xc[0] >= 5 ||
|
|
3323
|
+
rm === 2 && (xc[0] > 5 || xc[0] === 5 && (more || xc[1] !== UNDEFINED))
|
|
3324
|
+
);
|
|
3325
|
+
|
|
3326
|
+
xc.length = 1;
|
|
3327
|
+
|
|
3328
|
+
if (more) {
|
|
3329
|
+
|
|
3330
|
+
// 1, 0.1, 0.01, 0.001, 0.0001 etc.
|
|
3331
|
+
x.e = x.e - sd + 1;
|
|
3332
|
+
xc[0] = 1;
|
|
3333
|
+
} else {
|
|
3334
|
+
|
|
3335
|
+
// Zero.
|
|
3336
|
+
xc[0] = x.e = 0;
|
|
3337
|
+
}
|
|
3338
|
+
} else if (sd < xc.length) {
|
|
3339
|
+
|
|
3340
|
+
// xc[sd] is the digit after the digit that may be rounded up.
|
|
3341
|
+
more =
|
|
3342
|
+
rm === 1 && xc[sd] >= 5 ||
|
|
3343
|
+
rm === 2 && (xc[sd] > 5 || xc[sd] === 5 &&
|
|
3344
|
+
(more || xc[sd + 1] !== UNDEFINED || xc[sd - 1] & 1)) ||
|
|
3345
|
+
rm === 3 && (more || !!xc[0]);
|
|
3346
|
+
|
|
3347
|
+
// Remove any digits after the required precision.
|
|
3348
|
+
xc.length = sd;
|
|
3349
|
+
|
|
3350
|
+
// Round up?
|
|
3351
|
+
if (more) {
|
|
3352
|
+
|
|
3353
|
+
// Rounding up may mean the previous digit has to be rounded up.
|
|
3354
|
+
for (; ++xc[--sd] > 9;) {
|
|
3355
|
+
xc[sd] = 0;
|
|
3356
|
+
if (sd === 0) {
|
|
3357
|
+
++x.e;
|
|
3358
|
+
xc.unshift(1);
|
|
3359
|
+
break;
|
|
3360
|
+
}
|
|
3361
|
+
}
|
|
3362
|
+
}
|
|
3363
|
+
|
|
3364
|
+
// Remove trailing zeros.
|
|
3365
|
+
for (sd = xc.length; !xc[--sd];) xc.pop();
|
|
3366
|
+
}
|
|
3367
|
+
|
|
3368
|
+
return x;
|
|
3369
|
+
}
|
|
3370
|
+
|
|
3371
|
+
|
|
3372
|
+
/*
|
|
3373
|
+
* Return a string representing the value of Big x in normal or exponential notation.
|
|
3374
|
+
* Handles P.toExponential, P.toFixed, P.toJSON, P.toPrecision, P.toString and P.valueOf.
|
|
3375
|
+
*/
|
|
3376
|
+
function stringify$1(x, doExponential, isNonzero) {
|
|
3377
|
+
var e = x.e,
|
|
3378
|
+
s = x.c.join(''),
|
|
3379
|
+
n = s.length;
|
|
3380
|
+
|
|
3381
|
+
// Exponential notation?
|
|
3382
|
+
if (doExponential) {
|
|
3383
|
+
s = s.charAt(0) + (n > 1 ? '.' + s.slice(1) : '') + (e < 0 ? 'e' : 'e+') + e;
|
|
3384
|
+
|
|
3385
|
+
// Normal notation.
|
|
3386
|
+
} else if (e < 0) {
|
|
3387
|
+
for (; ++e;) s = '0' + s;
|
|
3388
|
+
s = '0.' + s;
|
|
3389
|
+
} else if (e > 0) {
|
|
3390
|
+
if (++e > n) {
|
|
3391
|
+
for (e -= n; e--;) s += '0';
|
|
3392
|
+
} else if (e < n) {
|
|
3393
|
+
s = s.slice(0, e) + '.' + s.slice(e);
|
|
3394
|
+
}
|
|
3395
|
+
} else if (n > 1) {
|
|
3396
|
+
s = s.charAt(0) + '.' + s.slice(1);
|
|
3397
|
+
}
|
|
3398
|
+
|
|
3399
|
+
return x.s < 0 && isNonzero ? '-' + s : s;
|
|
3400
|
+
}
|
|
3401
|
+
|
|
3402
|
+
|
|
3403
|
+
// Prototype/instance methods
|
|
3404
|
+
|
|
3405
|
+
|
|
3406
|
+
/*
|
|
3407
|
+
* Return a new Big whose value is the absolute value of this Big.
|
|
3408
|
+
*/
|
|
3409
|
+
P.abs = function () {
|
|
3410
|
+
var x = new this.constructor(this);
|
|
3411
|
+
x.s = 1;
|
|
3412
|
+
return x;
|
|
3413
|
+
};
|
|
3414
|
+
|
|
3415
|
+
|
|
3416
|
+
/*
|
|
3417
|
+
* Return 1 if the value of this Big is greater than the value of Big y,
|
|
3418
|
+
* -1 if the value of this Big is less than the value of Big y, or
|
|
3419
|
+
* 0 if they have the same value.
|
|
3420
|
+
*/
|
|
3421
|
+
P.cmp = function (y) {
|
|
3422
|
+
var isneg,
|
|
3423
|
+
x = this,
|
|
3424
|
+
xc = x.c,
|
|
3425
|
+
yc = (y = new x.constructor(y)).c,
|
|
3426
|
+
i = x.s,
|
|
3427
|
+
j = y.s,
|
|
3428
|
+
k = x.e,
|
|
3429
|
+
l = y.e;
|
|
3430
|
+
|
|
3431
|
+
// Either zero?
|
|
3432
|
+
if (!xc[0] || !yc[0]) return !xc[0] ? !yc[0] ? 0 : -j : i;
|
|
3433
|
+
|
|
3434
|
+
// Signs differ?
|
|
3435
|
+
if (i != j) return i;
|
|
3436
|
+
|
|
3437
|
+
isneg = i < 0;
|
|
3438
|
+
|
|
3439
|
+
// Compare exponents.
|
|
3440
|
+
if (k != l) return k > l ^ isneg ? 1 : -1;
|
|
3441
|
+
|
|
3442
|
+
j = (k = xc.length) < (l = yc.length) ? k : l;
|
|
3443
|
+
|
|
3444
|
+
// Compare digit by digit.
|
|
3445
|
+
for (i = -1; ++i < j;) {
|
|
3446
|
+
if (xc[i] != yc[i]) return xc[i] > yc[i] ^ isneg ? 1 : -1;
|
|
3447
|
+
}
|
|
3448
|
+
|
|
3449
|
+
// Compare lengths.
|
|
3450
|
+
return k == l ? 0 : k > l ^ isneg ? 1 : -1;
|
|
3451
|
+
};
|
|
3452
|
+
|
|
3453
|
+
|
|
3454
|
+
/*
|
|
3455
|
+
* Return a new Big whose value is the value of this Big divided by the value of Big y, rounded,
|
|
3456
|
+
* if necessary, to a maximum of Big.DP decimal places using rounding mode Big.RM.
|
|
3457
|
+
*/
|
|
3458
|
+
P.div = function (y) {
|
|
3459
|
+
var x = this,
|
|
3460
|
+
Big = x.constructor,
|
|
3461
|
+
a = x.c, // dividend
|
|
3462
|
+
b = (y = new Big(y)).c, // divisor
|
|
3463
|
+
k = x.s == y.s ? 1 : -1,
|
|
3464
|
+
dp = Big.DP;
|
|
3465
|
+
|
|
3466
|
+
if (dp !== ~~dp || dp < 0 || dp > MAX_DP) {
|
|
3467
|
+
throw Error(INVALID_DP);
|
|
3468
|
+
}
|
|
3469
|
+
|
|
3470
|
+
// Divisor is zero?
|
|
3471
|
+
if (!b[0]) {
|
|
3472
|
+
throw Error(DIV_BY_ZERO);
|
|
3473
|
+
}
|
|
3474
|
+
|
|
3475
|
+
// Dividend is 0? Return +-0.
|
|
3476
|
+
if (!a[0]) {
|
|
3477
|
+
y.s = k;
|
|
3478
|
+
y.c = [y.e = 0];
|
|
3479
|
+
return y;
|
|
3480
|
+
}
|
|
3481
|
+
|
|
3482
|
+
var bl, bt, n, cmp, ri,
|
|
3483
|
+
bz = b.slice(),
|
|
3484
|
+
ai = bl = b.length,
|
|
3485
|
+
al = a.length,
|
|
3486
|
+
r = a.slice(0, bl), // remainder
|
|
3487
|
+
rl = r.length,
|
|
3488
|
+
q = y, // quotient
|
|
3489
|
+
qc = q.c = [],
|
|
3490
|
+
qi = 0,
|
|
3491
|
+
p = dp + (q.e = x.e - y.e) + 1; // precision of the result
|
|
3492
|
+
|
|
3493
|
+
q.s = k;
|
|
3494
|
+
k = p < 0 ? 0 : p;
|
|
3495
|
+
|
|
3496
|
+
// Create version of divisor with leading zero.
|
|
3497
|
+
bz.unshift(0);
|
|
3498
|
+
|
|
3499
|
+
// Add zeros to make remainder as long as divisor.
|
|
3500
|
+
for (; rl++ < bl;) r.push(0);
|
|
3501
|
+
|
|
3502
|
+
do {
|
|
3503
|
+
|
|
3504
|
+
// n is how many times the divisor goes into current remainder.
|
|
3505
|
+
for (n = 0; n < 10; n++) {
|
|
3506
|
+
|
|
3507
|
+
// Compare divisor and remainder.
|
|
3508
|
+
if (bl != (rl = r.length)) {
|
|
3509
|
+
cmp = bl > rl ? 1 : -1;
|
|
3510
|
+
} else {
|
|
3511
|
+
for (ri = -1, cmp = 0; ++ri < bl;) {
|
|
3512
|
+
if (b[ri] != r[ri]) {
|
|
3513
|
+
cmp = b[ri] > r[ri] ? 1 : -1;
|
|
3514
|
+
break;
|
|
3515
|
+
}
|
|
3516
|
+
}
|
|
3517
|
+
}
|
|
3518
|
+
|
|
3519
|
+
// If divisor < remainder, subtract divisor from remainder.
|
|
3520
|
+
if (cmp < 0) {
|
|
3521
|
+
|
|
3522
|
+
// Remainder can't be more than 1 digit longer than divisor.
|
|
3523
|
+
// Equalise lengths using divisor with extra leading zero?
|
|
3524
|
+
for (bt = rl == bl ? b : bz; rl;) {
|
|
3525
|
+
if (r[--rl] < bt[rl]) {
|
|
3526
|
+
ri = rl;
|
|
3527
|
+
for (; ri && !r[--ri];) r[ri] = 9;
|
|
3528
|
+
--r[ri];
|
|
3529
|
+
r[rl] += 10;
|
|
3530
|
+
}
|
|
3531
|
+
r[rl] -= bt[rl];
|
|
3532
|
+
}
|
|
3533
|
+
|
|
3534
|
+
for (; !r[0];) r.shift();
|
|
3535
|
+
} else {
|
|
3536
|
+
break;
|
|
3537
|
+
}
|
|
3538
|
+
}
|
|
3539
|
+
|
|
3540
|
+
// Add the digit n to the result array.
|
|
3541
|
+
qc[qi++] = cmp ? n : ++n;
|
|
3542
|
+
|
|
3543
|
+
// Update the remainder.
|
|
3544
|
+
if (r[0] && cmp) r[rl] = a[ai] || 0;
|
|
3545
|
+
else r = [a[ai]];
|
|
3546
|
+
|
|
3547
|
+
} while ((ai++ < al || r[0] !== UNDEFINED) && k--);
|
|
3548
|
+
|
|
3549
|
+
// Leading zero? Do not remove if result is simply zero (qi == 1).
|
|
3550
|
+
if (!qc[0] && qi != 1) {
|
|
3551
|
+
|
|
3552
|
+
// There can't be more than one zero.
|
|
3553
|
+
qc.shift();
|
|
3554
|
+
q.e--;
|
|
3555
|
+
p--;
|
|
3556
|
+
}
|
|
3557
|
+
|
|
3558
|
+
// Round?
|
|
3559
|
+
if (qi > p) round(q, p, Big.RM, r[0] !== UNDEFINED);
|
|
3560
|
+
|
|
3561
|
+
return q;
|
|
3562
|
+
};
|
|
3563
|
+
|
|
3564
|
+
|
|
3565
|
+
/*
|
|
3566
|
+
* Return true if the value of this Big is equal to the value of Big y, otherwise return false.
|
|
3567
|
+
*/
|
|
3568
|
+
P.eq = function (y) {
|
|
3569
|
+
return this.cmp(y) === 0;
|
|
3570
|
+
};
|
|
3571
|
+
|
|
3572
|
+
|
|
3573
|
+
/*
|
|
3574
|
+
* Return true if the value of this Big is greater than the value of Big y, otherwise return
|
|
3575
|
+
* false.
|
|
3576
|
+
*/
|
|
3577
|
+
P.gt = function (y) {
|
|
3578
|
+
return this.cmp(y) > 0;
|
|
3579
|
+
};
|
|
3580
|
+
|
|
3581
|
+
|
|
3582
|
+
/*
|
|
3583
|
+
* Return true if the value of this Big is greater than or equal to the value of Big y, otherwise
|
|
3584
|
+
* return false.
|
|
3585
|
+
*/
|
|
3586
|
+
P.gte = function (y) {
|
|
3587
|
+
return this.cmp(y) > -1;
|
|
3588
|
+
};
|
|
3589
|
+
|
|
3590
|
+
|
|
3591
|
+
/*
|
|
3592
|
+
* Return true if the value of this Big is less than the value of Big y, otherwise return false.
|
|
3593
|
+
*/
|
|
3594
|
+
P.lt = function (y) {
|
|
3595
|
+
return this.cmp(y) < 0;
|
|
3596
|
+
};
|
|
3597
|
+
|
|
3598
|
+
|
|
3599
|
+
/*
|
|
3600
|
+
* Return true if the value of this Big is less than or equal to the value of Big y, otherwise
|
|
3601
|
+
* return false.
|
|
3602
|
+
*/
|
|
3603
|
+
P.lte = function (y) {
|
|
3604
|
+
return this.cmp(y) < 1;
|
|
3605
|
+
};
|
|
3606
|
+
|
|
3607
|
+
|
|
3608
|
+
/*
|
|
3609
|
+
* Return a new Big whose value is the value of this Big minus the value of Big y.
|
|
3610
|
+
*/
|
|
3611
|
+
P.minus = P.sub = function (y) {
|
|
3612
|
+
var i, j, t, xlty,
|
|
3613
|
+
x = this,
|
|
3614
|
+
Big = x.constructor,
|
|
3615
|
+
a = x.s,
|
|
3616
|
+
b = (y = new Big(y)).s;
|
|
3617
|
+
|
|
3618
|
+
// Signs differ?
|
|
3619
|
+
if (a != b) {
|
|
3620
|
+
y.s = -b;
|
|
3621
|
+
return x.plus(y);
|
|
3622
|
+
}
|
|
3623
|
+
|
|
3624
|
+
var xc = x.c.slice(),
|
|
3625
|
+
xe = x.e,
|
|
3626
|
+
yc = y.c,
|
|
3627
|
+
ye = y.e;
|
|
3628
|
+
|
|
3629
|
+
// Either zero?
|
|
3630
|
+
if (!xc[0] || !yc[0]) {
|
|
3631
|
+
if (yc[0]) {
|
|
3632
|
+
y.s = -b;
|
|
3633
|
+
} else if (xc[0]) {
|
|
3634
|
+
y = new Big(x);
|
|
3635
|
+
} else {
|
|
3636
|
+
y.s = 1;
|
|
3637
|
+
}
|
|
3638
|
+
return y;
|
|
3639
|
+
}
|
|
3640
|
+
|
|
3641
|
+
// Determine which is the bigger number. Prepend zeros to equalise exponents.
|
|
3642
|
+
if (a = xe - ye) {
|
|
3643
|
+
|
|
3644
|
+
if (xlty = a < 0) {
|
|
3645
|
+
a = -a;
|
|
3646
|
+
t = xc;
|
|
3647
|
+
} else {
|
|
3648
|
+
ye = xe;
|
|
3649
|
+
t = yc;
|
|
3650
|
+
}
|
|
3651
|
+
|
|
3652
|
+
t.reverse();
|
|
3653
|
+
for (b = a; b--;) t.push(0);
|
|
3654
|
+
t.reverse();
|
|
3655
|
+
} else {
|
|
3656
|
+
|
|
3657
|
+
// Exponents equal. Check digit by digit.
|
|
3658
|
+
j = ((xlty = xc.length < yc.length) ? xc : yc).length;
|
|
3659
|
+
|
|
3660
|
+
for (a = b = 0; b < j; b++) {
|
|
3661
|
+
if (xc[b] != yc[b]) {
|
|
3662
|
+
xlty = xc[b] < yc[b];
|
|
3663
|
+
break;
|
|
3664
|
+
}
|
|
3665
|
+
}
|
|
3666
|
+
}
|
|
3667
|
+
|
|
3668
|
+
// x < y? Point xc to the array of the bigger number.
|
|
3669
|
+
if (xlty) {
|
|
3670
|
+
t = xc;
|
|
3671
|
+
xc = yc;
|
|
3672
|
+
yc = t;
|
|
3673
|
+
y.s = -y.s;
|
|
3674
|
+
}
|
|
3675
|
+
|
|
3676
|
+
/*
|
|
3677
|
+
* Append zeros to xc if shorter. No need to add zeros to yc if shorter as subtraction only
|
|
3678
|
+
* needs to start at yc.length.
|
|
3679
|
+
*/
|
|
3680
|
+
if ((b = (j = yc.length) - (i = xc.length)) > 0) for (; b--;) xc[i++] = 0;
|
|
3681
|
+
|
|
3682
|
+
// Subtract yc from xc.
|
|
3683
|
+
for (b = i; j > a;) {
|
|
3684
|
+
if (xc[--j] < yc[j]) {
|
|
3685
|
+
for (i = j; i && !xc[--i];) xc[i] = 9;
|
|
3686
|
+
--xc[i];
|
|
3687
|
+
xc[j] += 10;
|
|
3688
|
+
}
|
|
3689
|
+
|
|
3690
|
+
xc[j] -= yc[j];
|
|
3691
|
+
}
|
|
3692
|
+
|
|
3693
|
+
// Remove trailing zeros.
|
|
3694
|
+
for (; xc[--b] === 0;) xc.pop();
|
|
3695
|
+
|
|
3696
|
+
// Remove leading zeros and adjust exponent accordingly.
|
|
3697
|
+
for (; xc[0] === 0;) {
|
|
3698
|
+
xc.shift();
|
|
3699
|
+
--ye;
|
|
3700
|
+
}
|
|
3701
|
+
|
|
3702
|
+
if (!xc[0]) {
|
|
3703
|
+
|
|
3704
|
+
// n - n = +0
|
|
3705
|
+
y.s = 1;
|
|
3706
|
+
|
|
3707
|
+
// Result must be zero.
|
|
3708
|
+
xc = [ye = 0];
|
|
3709
|
+
}
|
|
3710
|
+
|
|
3711
|
+
y.c = xc;
|
|
3712
|
+
y.e = ye;
|
|
3713
|
+
|
|
3714
|
+
return y;
|
|
3715
|
+
};
|
|
3716
|
+
|
|
3717
|
+
|
|
3718
|
+
/*
|
|
3719
|
+
* Return a new Big whose value is the value of this Big modulo the value of Big y.
|
|
3720
|
+
*/
|
|
3721
|
+
P.mod = function (y) {
|
|
3722
|
+
var ygtx,
|
|
3723
|
+
x = this,
|
|
3724
|
+
Big = x.constructor,
|
|
3725
|
+
a = x.s,
|
|
3726
|
+
b = (y = new Big(y)).s;
|
|
3727
|
+
|
|
3728
|
+
if (!y.c[0]) {
|
|
3729
|
+
throw Error(DIV_BY_ZERO);
|
|
3730
|
+
}
|
|
3731
|
+
|
|
3732
|
+
x.s = y.s = 1;
|
|
3733
|
+
ygtx = y.cmp(x) == 1;
|
|
3734
|
+
x.s = a;
|
|
3735
|
+
y.s = b;
|
|
3736
|
+
|
|
3737
|
+
if (ygtx) return new Big(x);
|
|
3738
|
+
|
|
3739
|
+
a = Big.DP;
|
|
3740
|
+
b = Big.RM;
|
|
3741
|
+
Big.DP = Big.RM = 0;
|
|
3742
|
+
x = x.div(y);
|
|
3743
|
+
Big.DP = a;
|
|
3744
|
+
Big.RM = b;
|
|
3745
|
+
|
|
3746
|
+
return this.minus(x.times(y));
|
|
3747
|
+
};
|
|
3748
|
+
|
|
3749
|
+
|
|
3750
|
+
/*
|
|
3751
|
+
* Return a new Big whose value is the value of this Big negated.
|
|
3752
|
+
*/
|
|
3753
|
+
P.neg = function () {
|
|
3754
|
+
var x = new this.constructor(this);
|
|
3755
|
+
x.s = -x.s;
|
|
3756
|
+
return x;
|
|
3757
|
+
};
|
|
3758
|
+
|
|
3759
|
+
|
|
3760
|
+
/*
|
|
3761
|
+
* Return a new Big whose value is the value of this Big plus the value of Big y.
|
|
3762
|
+
*/
|
|
3763
|
+
P.plus = P.add = function (y) {
|
|
3764
|
+
var e, k, t,
|
|
3765
|
+
x = this,
|
|
3766
|
+
Big = x.constructor;
|
|
3767
|
+
|
|
3768
|
+
y = new Big(y);
|
|
3769
|
+
|
|
3770
|
+
// Signs differ?
|
|
3771
|
+
if (x.s != y.s) {
|
|
3772
|
+
y.s = -y.s;
|
|
3773
|
+
return x.minus(y);
|
|
3774
|
+
}
|
|
3775
|
+
|
|
3776
|
+
var xe = x.e,
|
|
3777
|
+
xc = x.c,
|
|
3778
|
+
ye = y.e,
|
|
3779
|
+
yc = y.c;
|
|
3780
|
+
|
|
3781
|
+
// Either zero?
|
|
3782
|
+
if (!xc[0] || !yc[0]) {
|
|
3783
|
+
if (!yc[0]) {
|
|
3784
|
+
if (xc[0]) {
|
|
3785
|
+
y = new Big(x);
|
|
3786
|
+
} else {
|
|
3787
|
+
y.s = x.s;
|
|
3788
|
+
}
|
|
3789
|
+
}
|
|
3790
|
+
return y;
|
|
3791
|
+
}
|
|
3792
|
+
|
|
3793
|
+
xc = xc.slice();
|
|
3794
|
+
|
|
3795
|
+
// Prepend zeros to equalise exponents.
|
|
3796
|
+
// Note: reverse faster than unshifts.
|
|
3797
|
+
if (e = xe - ye) {
|
|
3798
|
+
if (e > 0) {
|
|
3799
|
+
ye = xe;
|
|
3800
|
+
t = yc;
|
|
3801
|
+
} else {
|
|
3802
|
+
e = -e;
|
|
3803
|
+
t = xc;
|
|
3804
|
+
}
|
|
3805
|
+
|
|
3806
|
+
t.reverse();
|
|
3807
|
+
for (; e--;) t.push(0);
|
|
3808
|
+
t.reverse();
|
|
3809
|
+
}
|
|
3810
|
+
|
|
3811
|
+
// Point xc to the longer array.
|
|
3812
|
+
if (xc.length - yc.length < 0) {
|
|
3813
|
+
t = yc;
|
|
3814
|
+
yc = xc;
|
|
3815
|
+
xc = t;
|
|
3816
|
+
}
|
|
3817
|
+
|
|
3818
|
+
e = yc.length;
|
|
3819
|
+
|
|
3820
|
+
// Only start adding at yc.length - 1 as the further digits of xc can be left as they are.
|
|
3821
|
+
for (k = 0; e; xc[e] %= 10) k = (xc[--e] = xc[e] + yc[e] + k) / 10 | 0;
|
|
3822
|
+
|
|
3823
|
+
// No need to check for zero, as +x + +y != 0 && -x + -y != 0
|
|
3824
|
+
|
|
3825
|
+
if (k) {
|
|
3826
|
+
xc.unshift(k);
|
|
3827
|
+
++ye;
|
|
3828
|
+
}
|
|
3829
|
+
|
|
3830
|
+
// Remove trailing zeros.
|
|
3831
|
+
for (e = xc.length; xc[--e] === 0;) xc.pop();
|
|
3832
|
+
|
|
3833
|
+
y.c = xc;
|
|
3834
|
+
y.e = ye;
|
|
3835
|
+
|
|
3836
|
+
return y;
|
|
3837
|
+
};
|
|
3838
|
+
|
|
3839
|
+
|
|
3840
|
+
/*
|
|
3841
|
+
* Return a Big whose value is the value of this Big raised to the power n.
|
|
3842
|
+
* If n is negative, round to a maximum of Big.DP decimal places using rounding
|
|
3843
|
+
* mode Big.RM.
|
|
3844
|
+
*
|
|
3845
|
+
* n {number} Integer, -MAX_POWER to MAX_POWER inclusive.
|
|
3846
|
+
*/
|
|
3847
|
+
P.pow = function (n) {
|
|
3848
|
+
var x = this,
|
|
3849
|
+
one = new x.constructor('1'),
|
|
3850
|
+
y = one,
|
|
3851
|
+
isneg = n < 0;
|
|
3852
|
+
|
|
3853
|
+
if (n !== ~~n || n < -MAX_POWER || n > MAX_POWER) {
|
|
3854
|
+
throw Error(INVALID + 'exponent');
|
|
3855
|
+
}
|
|
3856
|
+
|
|
3857
|
+
if (isneg) n = -n;
|
|
3858
|
+
|
|
3859
|
+
for (;;) {
|
|
3860
|
+
if (n & 1) y = y.times(x);
|
|
3861
|
+
n >>= 1;
|
|
3862
|
+
if (!n) break;
|
|
3863
|
+
x = x.times(x);
|
|
3864
|
+
}
|
|
3865
|
+
|
|
3866
|
+
return isneg ? one.div(y) : y;
|
|
3867
|
+
};
|
|
3868
|
+
|
|
3869
|
+
|
|
3870
|
+
/*
|
|
3871
|
+
* Return a new Big whose value is the value of this Big rounded to a maximum precision of sd
|
|
3872
|
+
* significant digits using rounding mode rm, or Big.RM if rm is not specified.
|
|
3873
|
+
*
|
|
3874
|
+
* sd {number} Significant digits: integer, 1 to MAX_DP inclusive.
|
|
3875
|
+
* rm? {number} Rounding mode: 0 (down), 1 (half-up), 2 (half-even) or 3 (up).
|
|
3876
|
+
*/
|
|
3877
|
+
P.prec = function (sd, rm) {
|
|
3878
|
+
if (sd !== ~~sd || sd < 1 || sd > MAX_DP) {
|
|
3879
|
+
throw Error(INVALID + 'precision');
|
|
3880
|
+
}
|
|
3881
|
+
return round(new this.constructor(this), sd, rm);
|
|
3882
|
+
};
|
|
3883
|
+
|
|
3884
|
+
|
|
3885
|
+
/*
|
|
3886
|
+
* Return a new Big whose value is the value of this Big rounded to a maximum of dp decimal places
|
|
3887
|
+
* using rounding mode rm, or Big.RM if rm is not specified.
|
|
3888
|
+
* If dp is negative, round to an integer which is a multiple of 10**-dp.
|
|
3889
|
+
* If dp is not specified, round to 0 decimal places.
|
|
3890
|
+
*
|
|
3891
|
+
* dp? {number} Integer, -MAX_DP to MAX_DP inclusive.
|
|
3892
|
+
* rm? {number} Rounding mode: 0 (down), 1 (half-up), 2 (half-even) or 3 (up).
|
|
3893
|
+
*/
|
|
3894
|
+
P.round = function (dp, rm) {
|
|
3895
|
+
if (dp === UNDEFINED) dp = 0;
|
|
3896
|
+
else if (dp !== ~~dp || dp < -MAX_DP || dp > MAX_DP) {
|
|
3897
|
+
throw Error(INVALID_DP);
|
|
3898
|
+
}
|
|
3899
|
+
return round(new this.constructor(this), dp + this.e + 1, rm);
|
|
3900
|
+
};
|
|
3901
|
+
|
|
3902
|
+
|
|
3903
|
+
/*
|
|
3904
|
+
* Return a new Big whose value is the square root of the value of this Big, rounded, if
|
|
3905
|
+
* necessary, to a maximum of Big.DP decimal places using rounding mode Big.RM.
|
|
3906
|
+
*/
|
|
3907
|
+
P.sqrt = function () {
|
|
3908
|
+
var r, c, t,
|
|
3909
|
+
x = this,
|
|
3910
|
+
Big = x.constructor,
|
|
3911
|
+
s = x.s,
|
|
3912
|
+
e = x.e,
|
|
3913
|
+
half = new Big('0.5');
|
|
3914
|
+
|
|
3915
|
+
// Zero?
|
|
3916
|
+
if (!x.c[0]) return new Big(x);
|
|
3917
|
+
|
|
3918
|
+
// Negative?
|
|
3919
|
+
if (s < 0) {
|
|
3920
|
+
throw Error(NAME + 'No square root');
|
|
3921
|
+
}
|
|
3922
|
+
|
|
3923
|
+
// Estimate.
|
|
3924
|
+
s = Math.sqrt(+stringify$1(x, true, true));
|
|
3925
|
+
|
|
3926
|
+
// Math.sqrt underflow/overflow?
|
|
3927
|
+
// Re-estimate: pass x coefficient to Math.sqrt as integer, then adjust the result exponent.
|
|
3928
|
+
if (s === 0 || s === 1 / 0) {
|
|
3929
|
+
c = x.c.join('');
|
|
3930
|
+
if (!(c.length + e & 1)) c += '0';
|
|
3931
|
+
s = Math.sqrt(c);
|
|
3932
|
+
e = ((e + 1) / 2 | 0) - (e < 0 || e & 1);
|
|
3933
|
+
r = new Big((s == 1 / 0 ? '5e' : (s = s.toExponential()).slice(0, s.indexOf('e') + 1)) + e);
|
|
3934
|
+
} else {
|
|
3935
|
+
r = new Big(s + '');
|
|
3936
|
+
}
|
|
3937
|
+
|
|
3938
|
+
e = r.e + (Big.DP += 4);
|
|
3939
|
+
|
|
3940
|
+
// Newton-Raphson iteration.
|
|
3941
|
+
do {
|
|
3942
|
+
t = r;
|
|
3943
|
+
r = half.times(t.plus(x.div(t)));
|
|
3944
|
+
} while (t.c.slice(0, e).join('') !== r.c.slice(0, e).join(''));
|
|
3945
|
+
|
|
3946
|
+
return round(r, (Big.DP -= 4) + r.e + 1, Big.RM);
|
|
3947
|
+
};
|
|
3948
|
+
|
|
3949
|
+
|
|
3950
|
+
/*
|
|
3951
|
+
* Return a new Big whose value is the value of this Big times the value of Big y.
|
|
3952
|
+
*/
|
|
3953
|
+
P.times = P.mul = function (y) {
|
|
3954
|
+
var c,
|
|
3955
|
+
x = this,
|
|
3956
|
+
Big = x.constructor,
|
|
3957
|
+
xc = x.c,
|
|
3958
|
+
yc = (y = new Big(y)).c,
|
|
3959
|
+
a = xc.length,
|
|
3960
|
+
b = yc.length,
|
|
3961
|
+
i = x.e,
|
|
3962
|
+
j = y.e;
|
|
3963
|
+
|
|
3964
|
+
// Determine sign of result.
|
|
3965
|
+
y.s = x.s == y.s ? 1 : -1;
|
|
3966
|
+
|
|
3967
|
+
// Return signed 0 if either 0.
|
|
3968
|
+
if (!xc[0] || !yc[0]) {
|
|
3969
|
+
y.c = [y.e = 0];
|
|
3970
|
+
return y;
|
|
3971
|
+
}
|
|
3972
|
+
|
|
3973
|
+
// Initialise exponent of result as x.e + y.e.
|
|
3974
|
+
y.e = i + j;
|
|
3975
|
+
|
|
3976
|
+
// If array xc has fewer digits than yc, swap xc and yc, and lengths.
|
|
3977
|
+
if (a < b) {
|
|
3978
|
+
c = xc;
|
|
3979
|
+
xc = yc;
|
|
3980
|
+
yc = c;
|
|
3981
|
+
j = a;
|
|
3982
|
+
a = b;
|
|
3983
|
+
b = j;
|
|
3984
|
+
}
|
|
3985
|
+
|
|
3986
|
+
// Initialise coefficient array of result with zeros.
|
|
3987
|
+
for (c = new Array(j = a + b); j--;) c[j] = 0;
|
|
3988
|
+
|
|
3989
|
+
// Multiply.
|
|
3990
|
+
|
|
3991
|
+
// i is initially xc.length.
|
|
3992
|
+
for (i = b; i--;) {
|
|
3993
|
+
b = 0;
|
|
3994
|
+
|
|
3995
|
+
// a is yc.length.
|
|
3996
|
+
for (j = a + i; j > i;) {
|
|
3997
|
+
|
|
3998
|
+
// Current sum of products at this digit position, plus carry.
|
|
3999
|
+
b = c[j] + yc[i] * xc[j - i - 1] + b;
|
|
4000
|
+
c[j--] = b % 10;
|
|
4001
|
+
|
|
4002
|
+
// carry
|
|
4003
|
+
b = b / 10 | 0;
|
|
4004
|
+
}
|
|
4005
|
+
|
|
4006
|
+
c[j] = b;
|
|
4007
|
+
}
|
|
4008
|
+
|
|
4009
|
+
// Increment result exponent if there is a final carry, otherwise remove leading zero.
|
|
4010
|
+
if (b) ++y.e;
|
|
4011
|
+
else c.shift();
|
|
4012
|
+
|
|
4013
|
+
// Remove trailing zeros.
|
|
4014
|
+
for (i = c.length; !c[--i];) c.pop();
|
|
4015
|
+
y.c = c;
|
|
4016
|
+
|
|
4017
|
+
return y;
|
|
4018
|
+
};
|
|
4019
|
+
|
|
4020
|
+
|
|
4021
|
+
/*
|
|
4022
|
+
* Return a string representing the value of this Big in exponential notation rounded to dp fixed
|
|
4023
|
+
* decimal places using rounding mode rm, or Big.RM if rm is not specified.
|
|
4024
|
+
*
|
|
4025
|
+
* dp? {number} Decimal places: integer, 0 to MAX_DP inclusive.
|
|
4026
|
+
* rm? {number} Rounding mode: 0 (down), 1 (half-up), 2 (half-even) or 3 (up).
|
|
4027
|
+
*/
|
|
4028
|
+
P.toExponential = function (dp, rm) {
|
|
4029
|
+
var x = this,
|
|
4030
|
+
n = x.c[0];
|
|
4031
|
+
|
|
4032
|
+
if (dp !== UNDEFINED) {
|
|
4033
|
+
if (dp !== ~~dp || dp < 0 || dp > MAX_DP) {
|
|
4034
|
+
throw Error(INVALID_DP);
|
|
4035
|
+
}
|
|
4036
|
+
x = round(new x.constructor(x), ++dp, rm);
|
|
4037
|
+
for (; x.c.length < dp;) x.c.push(0);
|
|
4038
|
+
}
|
|
4039
|
+
|
|
4040
|
+
return stringify$1(x, true, !!n);
|
|
4041
|
+
};
|
|
4042
|
+
|
|
4043
|
+
|
|
4044
|
+
/*
|
|
4045
|
+
* Return a string representing the value of this Big in normal notation rounded to dp fixed
|
|
4046
|
+
* decimal places using rounding mode rm, or Big.RM if rm is not specified.
|
|
4047
|
+
*
|
|
4048
|
+
* dp? {number} Decimal places: integer, 0 to MAX_DP inclusive.
|
|
4049
|
+
* rm? {number} Rounding mode: 0 (down), 1 (half-up), 2 (half-even) or 3 (up).
|
|
4050
|
+
*
|
|
4051
|
+
* (-0).toFixed(0) is '0', but (-0.1).toFixed(0) is '-0'.
|
|
4052
|
+
* (-0).toFixed(1) is '0.0', but (-0.01).toFixed(1) is '-0.0'.
|
|
4053
|
+
*/
|
|
4054
|
+
P.toFixed = function (dp, rm) {
|
|
4055
|
+
var x = this,
|
|
4056
|
+
n = x.c[0];
|
|
4057
|
+
|
|
4058
|
+
if (dp !== UNDEFINED) {
|
|
4059
|
+
if (dp !== ~~dp || dp < 0 || dp > MAX_DP) {
|
|
4060
|
+
throw Error(INVALID_DP);
|
|
4061
|
+
}
|
|
4062
|
+
x = round(new x.constructor(x), dp + x.e + 1, rm);
|
|
4063
|
+
|
|
4064
|
+
// x.e may have changed if the value is rounded up.
|
|
4065
|
+
for (dp = dp + x.e + 1; x.c.length < dp;) x.c.push(0);
|
|
4066
|
+
}
|
|
4067
|
+
|
|
4068
|
+
return stringify$1(x, false, !!n);
|
|
4069
|
+
};
|
|
4070
|
+
|
|
4071
|
+
|
|
4072
|
+
/*
|
|
4073
|
+
* Return a string representing the value of this Big.
|
|
4074
|
+
* Return exponential notation if this Big has a positive exponent equal to or greater than
|
|
4075
|
+
* Big.PE, or a negative exponent equal to or less than Big.NE.
|
|
4076
|
+
* Omit the sign for negative zero.
|
|
4077
|
+
*/
|
|
4078
|
+
P.toJSON = P.toString = function () {
|
|
4079
|
+
var x = this,
|
|
4080
|
+
Big = x.constructor;
|
|
4081
|
+
return stringify$1(x, x.e <= Big.NE || x.e >= Big.PE, !!x.c[0]);
|
|
4082
|
+
};
|
|
4083
|
+
|
|
4084
|
+
if (typeof Symbol !== "undefined") {
|
|
4085
|
+
P[Symbol.for('nodejs.util.inspect.custom')] = P.toJSON;
|
|
4086
|
+
}
|
|
4087
|
+
|
|
4088
|
+
|
|
4089
|
+
/*
|
|
4090
|
+
* Return the value of this Big as a primitive number.
|
|
4091
|
+
*/
|
|
4092
|
+
P.toNumber = function () {
|
|
4093
|
+
var n = +stringify$1(this, true, true);
|
|
4094
|
+
if (this.constructor.strict === true && !this.eq(n.toString())) {
|
|
4095
|
+
throw Error(NAME + 'Imprecise conversion');
|
|
4096
|
+
}
|
|
4097
|
+
return n;
|
|
4098
|
+
};
|
|
4099
|
+
|
|
4100
|
+
|
|
4101
|
+
/*
|
|
4102
|
+
* Return a string representing the value of this Big rounded to sd significant digits using
|
|
4103
|
+
* rounding mode rm, or Big.RM if rm is not specified.
|
|
4104
|
+
* Use exponential notation if sd is less than the number of digits necessary to represent
|
|
4105
|
+
* the integer part of the value in normal notation.
|
|
4106
|
+
*
|
|
4107
|
+
* sd {number} Significant digits: integer, 1 to MAX_DP inclusive.
|
|
4108
|
+
* rm? {number} Rounding mode: 0 (down), 1 (half-up), 2 (half-even) or 3 (up).
|
|
4109
|
+
*/
|
|
4110
|
+
P.toPrecision = function (sd, rm) {
|
|
4111
|
+
var x = this,
|
|
4112
|
+
Big = x.constructor,
|
|
4113
|
+
n = x.c[0];
|
|
4114
|
+
|
|
4115
|
+
if (sd !== UNDEFINED) {
|
|
4116
|
+
if (sd !== ~~sd || sd < 1 || sd > MAX_DP) {
|
|
4117
|
+
throw Error(INVALID + 'precision');
|
|
4118
|
+
}
|
|
4119
|
+
x = round(new Big(x), sd, rm);
|
|
4120
|
+
for (; x.c.length < sd;) x.c.push(0);
|
|
4121
|
+
}
|
|
4122
|
+
|
|
4123
|
+
return stringify$1(x, sd <= x.e || x.e <= Big.NE || x.e >= Big.PE, !!n);
|
|
4124
|
+
};
|
|
4125
|
+
|
|
4126
|
+
|
|
4127
|
+
/*
|
|
4128
|
+
* Return a string representing the value of this Big.
|
|
4129
|
+
* Return exponential notation if this Big has a positive exponent equal to or greater than
|
|
4130
|
+
* Big.PE, or a negative exponent equal to or less than Big.NE.
|
|
4131
|
+
* Include the sign for negative zero.
|
|
4132
|
+
*/
|
|
4133
|
+
P.valueOf = function () {
|
|
4134
|
+
var x = this,
|
|
4135
|
+
Big = x.constructor;
|
|
4136
|
+
if (Big.strict === true) {
|
|
4137
|
+
throw Error(NAME + 'valueOf disallowed');
|
|
4138
|
+
}
|
|
4139
|
+
return stringify$1(x, x.e <= Big.NE || x.e >= Big.PE, true);
|
|
4140
|
+
};
|
|
4141
|
+
|
|
4142
|
+
|
|
4143
|
+
// Export
|
|
4144
|
+
|
|
4145
|
+
|
|
4146
|
+
var Big = _Big_();
|
|
4147
|
+
|
|
3156
4148
|
// Find the intersection between two 2D segments
|
|
3157
4149
|
// Returns 0, 1 or 2 [x, y] locations as null, [x, y], or [x1, y1, x2, y2]
|
|
3158
4150
|
// Special cases:
|
|
@@ -3164,42 +4156,45 @@
|
|
|
3164
4156
|
function segmentIntersection(ax, ay, bx, by, cx, cy, dx, dy, epsArg) {
|
|
3165
4157
|
// Use a small tolerance interval, so collinear segments and T-intersections
|
|
3166
4158
|
// are detected (floating point rounding often causes exact functions to fail)
|
|
3167
|
-
var eps = epsArg
|
|
4159
|
+
var eps = epsArg > 0 ? epsArg :
|
|
3168
4160
|
getHighPrecisionSnapInterval([ax, ay, bx, by, cx, cy, dx, dy]);
|
|
3169
4161
|
var epsSq = eps * eps;
|
|
3170
4162
|
var touches, cross;
|
|
4163
|
+
|
|
3171
4164
|
// Detect 0, 1 or 2 'touch' intersections, where a vertex of one segment
|
|
3172
4165
|
// is very close to the other segment's linear portion.
|
|
3173
4166
|
// One touch indicates either a T-intersection or two overlapping collinear
|
|
3174
4167
|
// segments that share an endpoint. Two touches indicates overlapping
|
|
3175
4168
|
// collinear segments that do not share an endpoint.
|
|
3176
4169
|
touches = findPointSegTouches(epsSq, ax, ay, bx, by, cx, cy, dx, dy);
|
|
3177
|
-
// if (touches) return touches;
|
|
3178
4170
|
// Ignore endpoint-only intersections
|
|
3179
4171
|
if (!touches && testEndpointHit(epsSq, ax, ay, bx, by, cx, cy, dx, dy)) {
|
|
3180
4172
|
return null;
|
|
3181
4173
|
}
|
|
3182
4174
|
// Detect cross intersection
|
|
3183
|
-
|
|
4175
|
+
// (TODO: consider cross intersections that are also endpoint hits)
|
|
4176
|
+
if (!touches) {
|
|
4177
|
+
cross = findCrossIntersection(ax, ay, bx, by, cx, cy, dx, dy, eps);
|
|
4178
|
+
}
|
|
3184
4179
|
return touches || cross || null;
|
|
3185
4180
|
}
|
|
3186
4181
|
|
|
3187
4182
|
|
|
3188
|
-
// Find the intersection point of two segments that cross each other,
|
|
3189
|
-
// or return null if the segments do not cross.
|
|
3190
|
-
// Assumes endpoint intersections have already been detected
|
|
3191
4183
|
function findCrossIntersection(ax, ay, bx, by, cx, cy, dx, dy, eps) {
|
|
3192
|
-
|
|
3193
|
-
|
|
3194
|
-
|
|
3195
|
-
|
|
3196
|
-
|
|
3197
|
-
|
|
3198
|
-
|
|
3199
|
-
|
|
3200
|
-
|
|
3201
|
-
|
|
4184
|
+
var p;
|
|
4185
|
+
if (eps > 0 && !segmentHit_fast(ax, ay, bx, by, cx, cy, dx, dy)) return null;
|
|
4186
|
+
else if (eps === 0 && !segmentHit_big(ax, ay, bx, by, cx, cy, dx, dy)) return null;
|
|
4187
|
+
|
|
4188
|
+
// in a typical layer with many intersections, robust is preferred in
|
|
4189
|
+
// most (>90%) segment intersections in order to keep the positional
|
|
4190
|
+
// error within a small interval (e.g. 50% of eps)
|
|
4191
|
+
//
|
|
4192
|
+
if (useRobustCross(ax, ay, bx, by, cx, cy, dx, dy)) {
|
|
4193
|
+
p = findCrossIntersection_robust(ax, ay, bx, by, cx, cy, dx, dy);
|
|
4194
|
+
} else {
|
|
4195
|
+
p = findCrossIntersection_fast(ax, ay, bx, by, cx, cy, dx, dy);
|
|
3202
4196
|
}
|
|
4197
|
+
if (!p) return null;
|
|
3203
4198
|
|
|
3204
4199
|
// Snap p to a vertex if very close to one
|
|
3205
4200
|
// This avoids tiny segments caused by T-intersection overshoots and prevents
|
|
@@ -3215,9 +4210,98 @@
|
|
|
3215
4210
|
return p;
|
|
3216
4211
|
}
|
|
3217
4212
|
|
|
4213
|
+
// Find the intersection point of two segments that cross each other,
|
|
4214
|
+
// or return null if the segments do not cross.
|
|
4215
|
+
// Assumes endpoint intersections have already been detected
|
|
4216
|
+
function findCrossIntersection_robust(ax, ay, bx, by, cx, cy, dx, dy) {
|
|
4217
|
+
var ax_big = Big(ax);
|
|
4218
|
+
var ay_big = Big(ay);
|
|
4219
|
+
var bx_big = Big(bx);
|
|
4220
|
+
var by_big = Big(by);
|
|
4221
|
+
var cx_big = Big(cx);
|
|
4222
|
+
var cy_big = Big(cy);
|
|
4223
|
+
var dx_big = Big(dx);
|
|
4224
|
+
var dy_big = Big(dy);
|
|
4225
|
+
var v1x = bx_big.minus(ax_big);
|
|
4226
|
+
var v1y = by_big.minus(ay_big);
|
|
4227
|
+
var den_big = determinant2D_big(v1x, v1y, dx_big.minus(cx), dy_big.minus(cy));
|
|
4228
|
+
if (den_big.eq(0)) {
|
|
4229
|
+
debug("DIV0 error (should have been caught upstream)");
|
|
4230
|
+
// console.log("hit?", segmentHit_big(ax, ay, bx, by, cx, cy, dx, dy))
|
|
4231
|
+
// console.log('Seg 1', getSegFeature(ax, ay, bx, by, true))
|
|
4232
|
+
// console.log('Seg 2', getSegFeature(cx, cy, dx, dy, false))
|
|
4233
|
+
return null;
|
|
4234
|
+
}
|
|
4235
|
+
// perform division using regular math, which does not reduce overall
|
|
4236
|
+
// precision in test data (big.js division is very slow)
|
|
4237
|
+
// tests show identical result to:
|
|
4238
|
+
// orient2D_big(cx_big, cy_big, dx_big, dy_big, ax_big, ay_big).div(den_big)
|
|
4239
|
+
var m = orient2D_big(cx_big, cy_big, dx_big, dy_big, ax_big,
|
|
4240
|
+
ay_big).toNumber() / den_big.toNumber();
|
|
4241
|
+
var m_big = Big(m);
|
|
4242
|
+
var x_big = ax_big.plus(m_big.times(v1x).round(16));
|
|
4243
|
+
var y_big = ay_big.plus(m_big.times(v1y).round(16));
|
|
4244
|
+
var p = [x_big.toNumber(), y_big.toNumber()];
|
|
4245
|
+
return p;
|
|
4246
|
+
}
|
|
4247
|
+
|
|
4248
|
+
function findCrossIntersection_fast(ax, ay, bx, by, cx, cy, dx, dy) {
|
|
4249
|
+
var den = determinant2D(bx - ax, by - ay, dx - cx, dy - cy);
|
|
4250
|
+
var m = orient2D(cx, cy, dx, dy, ax, ay) / den;
|
|
4251
|
+
var p = [ax + m * (bx - ax), ay + m * (by - ay)];
|
|
4252
|
+
if (Math.abs(den) < 1e-25) {
|
|
4253
|
+
// changed from 1e-18 to 1e-25 (see geom ex1)
|
|
4254
|
+
// assume that collinear and near-collinear segment intersections have been
|
|
4255
|
+
// accounted for already.
|
|
4256
|
+
// TODO: is this really true?
|
|
4257
|
+
return null;
|
|
4258
|
+
}
|
|
4259
|
+
return p;
|
|
4260
|
+
}
|
|
4261
|
+
|
|
4262
|
+
function useRobustCross(ax, ay, bx, by, cx, cy, dx, dy) {
|
|
4263
|
+
// angle and seg length ratio thresholds were found by comparing
|
|
4264
|
+
// fast and robust outputs on sample data
|
|
4265
|
+
if (innerAngle(ax, ay, bx, by, cx, cy, dx, dy) < 0.1) return true;
|
|
4266
|
+
var len1 = distance2D(ax, ay, bx, by);
|
|
4267
|
+
var len2 = distance2D(cx, cy, dx, dy);
|
|
4268
|
+
var ratio = len1 < len2 ? len1 / len2 : len2 / len1 || 0;
|
|
4269
|
+
if (ratio < 0.001) return true;
|
|
4270
|
+
return false;
|
|
4271
|
+
}
|
|
4272
|
+
|
|
4273
|
+
// Returns smaller unsigned angle between two segments
|
|
4274
|
+
function innerAngle(ax, ay, bx, by, cx, cy, dx, dy) {
|
|
4275
|
+
var v1x = bx - ax;
|
|
4276
|
+
var v1y = by - ay;
|
|
4277
|
+
var v2x = dx - cx;
|
|
4278
|
+
var v2y = dy - cy;
|
|
4279
|
+
var dot = v1x * v2x + v1y * v2y;
|
|
4280
|
+
var mag1Sq = v1x * v1x + v1y * v1y;
|
|
4281
|
+
var mag2Sq = v2x * v2x + v2y * v2y;
|
|
4282
|
+
if (mag1Sq === 0 || mag2Sq === 0) {
|
|
4283
|
+
return 0;
|
|
4284
|
+
}
|
|
4285
|
+
var cosTheta = dot / Math.sqrt(mag1Sq * mag2Sq);
|
|
4286
|
+
var theta;
|
|
4287
|
+
if (cosTheta > 1 - 1e-14) {
|
|
4288
|
+
theta = 0;
|
|
4289
|
+
} else if (cosTheta < -1 + 1e-14) {
|
|
4290
|
+
theta = Math.PI;
|
|
4291
|
+
} else {
|
|
4292
|
+
theta = Math.acos(cosTheta);
|
|
4293
|
+
}
|
|
4294
|
+
if (theta >= Math.PI / 2) {
|
|
4295
|
+
theta = Math.PI - theta;
|
|
4296
|
+
}
|
|
4297
|
+
return theta;
|
|
4298
|
+
}
|
|
4299
|
+
|
|
3218
4300
|
function testEndpointHit(epsSq, ax, ay, bx, by, cx, cy, dx, dy) {
|
|
3219
|
-
return distanceSq(ax, ay, cx, cy) <= epsSq ||
|
|
3220
|
-
distanceSq(
|
|
4301
|
+
return distanceSq(ax, ay, cx, cy) <= epsSq ||
|
|
4302
|
+
distanceSq(ax, ay, dx, dy) <= epsSq ||
|
|
4303
|
+
distanceSq(bx, by, cx, cy) <= epsSq ||
|
|
4304
|
+
distanceSq(bx, by, dx, dy) <= epsSq;
|
|
3221
4305
|
}
|
|
3222
4306
|
|
|
3223
4307
|
function findPointSegTouches(epsSq, ax, ay, bx, by, cx, cy, dx, dy) {
|
|
@@ -3228,6 +4312,9 @@
|
|
|
3228
4312
|
collectPointSegTouch(touches, epsSq, dx, dy, ax, ay, bx, by);
|
|
3229
4313
|
if (touches.length === 0) return null;
|
|
3230
4314
|
if (touches.length > 4) {
|
|
4315
|
+
// console.log('XX', touches.length)
|
|
4316
|
+
// console.log('Seg 1', getSegFeature(ax, ay, bx, by, true))
|
|
4317
|
+
// console.log('Seg 2', getSegFeature(cx, cy, dx, dy, false))
|
|
3231
4318
|
// Geometrically, more than two touch intersections can not occur.
|
|
3232
4319
|
// Is it possible that fp rounding or a bug might result in >2 touches?
|
|
3233
4320
|
debug('Intersection detection error');
|
|
@@ -3244,10 +4331,10 @@
|
|
|
3244
4331
|
var pa = distanceSq(ax, ay, px, py);
|
|
3245
4332
|
var pb = distanceSq(bx, by, px, py);
|
|
3246
4333
|
if (pa <= epsSq || pb <= epsSq) return; // ignore endpoint hits
|
|
4334
|
+
// console.log("Dist:", Math.sqrt(pab), "eps:", Math.sqrt(epsSq), "p:", px, py)
|
|
3247
4335
|
arr.push(px, py); // T intersection at P and AB
|
|
3248
4336
|
}
|
|
3249
4337
|
|
|
3250
|
-
|
|
3251
4338
|
// Used by mapshaper-undershoots.js
|
|
3252
4339
|
// TODO: make more robust, make sure result is compatible with segmentIntersection()
|
|
3253
4340
|
// (rounding errors currently must be handled downstream)
|
|
@@ -3296,16 +4383,55 @@
|
|
|
3296
4383
|
// when a segment is vertical or horizontal. This has caused problems when
|
|
3297
4384
|
// repeatedly applying bbox clipping along the same segment
|
|
3298
4385
|
var x = p[0],
|
|
3299
|
-
y = p[1]
|
|
4386
|
+
y = p[1],
|
|
4387
|
+
s1out = false,
|
|
4388
|
+
s2out = false;
|
|
4389
|
+
|
|
3300
4390
|
// assumes that segment ranges intersect
|
|
3301
|
-
|
|
3302
|
-
|
|
3303
|
-
|
|
3304
|
-
|
|
4391
|
+
if (outsideRange(x, ax, bx)) {
|
|
4392
|
+
x = clampToClosestEndpoint(x, ax, bx);
|
|
4393
|
+
s1out = true;
|
|
4394
|
+
}
|
|
4395
|
+
if (outsideRange(x, cx, dx)) {
|
|
4396
|
+
x = clampToClosestEndpoint(x, cx, dx);
|
|
4397
|
+
s2out = true;
|
|
4398
|
+
}
|
|
4399
|
+
if (outsideRange(y, ay, by)) {
|
|
4400
|
+
y = clampToClosestEndpoint(y, ay, by);
|
|
4401
|
+
s1out = true;
|
|
4402
|
+
}
|
|
4403
|
+
if (outsideRange(y, cy, dy)) {
|
|
4404
|
+
y = clampToClosestEndpoint(y, cy, dy);
|
|
4405
|
+
s2out = true;
|
|
4406
|
+
}
|
|
4407
|
+
if ((s1out || s2out)) {
|
|
4408
|
+
debug('Clamping a segment intersection point');
|
|
4409
|
+
// console.log("angle:", innerAngle(ax, ay, bx, by, cx, cy, dx, dy))
|
|
4410
|
+
// console.log('Feature 1', getSegFeature(ax, ay, bx, by, s1out));
|
|
4411
|
+
// console.log('Feature 2', getSegFeature(cx, cy, dx, dy, s2out));
|
|
4412
|
+
// console.log('Point:', JSON.stringify({
|
|
4413
|
+
// type: 'Feature',
|
|
4414
|
+
// properties: {fill: 'red'},
|
|
4415
|
+
// geometry: {type: 'Point', coordinates: [p[0], p[1]]}
|
|
4416
|
+
// }));
|
|
4417
|
+
}
|
|
3305
4418
|
p[0] = x;
|
|
3306
4419
|
p[1] = y;
|
|
3307
4420
|
}
|
|
3308
4421
|
|
|
4422
|
+
// function getSegFeature(x1, y1, x2, y2, hot) {
|
|
4423
|
+
// return JSON.stringify({
|
|
4424
|
+
// type: "Feature",
|
|
4425
|
+
// properties: {
|
|
4426
|
+
// stroke: hot ? "orange" : "blue"
|
|
4427
|
+
// },
|
|
4428
|
+
// geometry: {
|
|
4429
|
+
// type: "LineString",
|
|
4430
|
+
// coordinates: [[x1, y1], [x2, y2]]
|
|
4431
|
+
// }
|
|
4432
|
+
// });
|
|
4433
|
+
// }
|
|
4434
|
+
|
|
3309
4435
|
// a: coordinate of point
|
|
3310
4436
|
// b: endpoint coordinate of segment
|
|
3311
4437
|
// c: other endpoint of segment
|
|
@@ -3321,16 +4447,13 @@
|
|
|
3321
4447
|
return out;
|
|
3322
4448
|
}
|
|
3323
4449
|
|
|
3324
|
-
function
|
|
3325
|
-
var lim;
|
|
3326
|
-
|
|
3327
|
-
|
|
3328
|
-
|
|
3329
|
-
debug("[clampToCloseRange()] large clamping interval", a, b, c);
|
|
3330
|
-
}
|
|
3331
|
-
a = lim;
|
|
4450
|
+
function clampToClosestEndpoint(a, b, c) {
|
|
4451
|
+
var lim = Math.abs(a - b) < Math.abs(a - c) ? b : c;
|
|
4452
|
+
var interval = Math.abs(a - lim);
|
|
4453
|
+
if (interval > 1e-15) {
|
|
4454
|
+
debug("[clampToClosestEndpoint()] large clamping interval:", interval);
|
|
3332
4455
|
}
|
|
3333
|
-
return
|
|
4456
|
+
return lim;
|
|
3334
4457
|
}
|
|
3335
4458
|
|
|
3336
4459
|
// Determinant of matrix
|
|
@@ -3340,6 +4463,10 @@
|
|
|
3340
4463
|
return a * d - b * c;
|
|
3341
4464
|
}
|
|
3342
4465
|
|
|
4466
|
+
function determinant2D_big(a, b, c, d) {
|
|
4467
|
+
return a.times(d).minus(b.times(c));
|
|
4468
|
+
}
|
|
4469
|
+
|
|
3343
4470
|
// returns a positive value if the points a, b, and c are arranged in
|
|
3344
4471
|
// counterclockwise order, a negative value if the points are in clockwise
|
|
3345
4472
|
// order, and zero if the points are collinear.
|
|
@@ -3348,16 +4475,51 @@
|
|
|
3348
4475
|
return determinant2D(ax - cx, ay - cy, bx - cx, by - cy);
|
|
3349
4476
|
}
|
|
3350
4477
|
|
|
4478
|
+
function orient2D_big(ax, ay, bx, by, cx, cy) {
|
|
4479
|
+
var a = (ax.minus(cx)).times(by.minus(cy));
|
|
4480
|
+
var b = (ay.minus(cy)).times(bx.minus(cx));
|
|
4481
|
+
return a.minus(b);
|
|
4482
|
+
}
|
|
4483
|
+
|
|
4484
|
+
function orient2D_big2(ax, ay, bx, by, cx, cy) {
|
|
4485
|
+
return orient2D_big(Big(ax), Big(ay), Big(bx), Big(by), Big(cx), Big(cy));
|
|
4486
|
+
}
|
|
4487
|
+
|
|
4488
|
+
|
|
4489
|
+
// export function orient2D_v2(ax, ay, bx, by, cx, cy) {
|
|
4490
|
+
// return -orient2D_robust(ax, ay, bx, by, cx, cy);
|
|
4491
|
+
// }
|
|
4492
|
+
|
|
4493
|
+
|
|
3351
4494
|
// Source: Sedgewick, _Algorithms in C_
|
|
3352
4495
|
// (Other functions were tried that were more sensitive to floating point errors
|
|
3353
4496
|
// than this function)
|
|
3354
|
-
function
|
|
4497
|
+
function segmentHit_fast(ax, ay, bx, by, cx, cy, dx, dy) {
|
|
3355
4498
|
return orient2D(ax, ay, bx, by, cx, cy) *
|
|
3356
4499
|
orient2D(ax, ay, bx, by, dx, dy) <= 0 &&
|
|
3357
4500
|
orient2D(cx, cy, dx, dy, ax, ay) *
|
|
3358
4501
|
orient2D(cx, cy, dx, dy, bx, by) <= 0;
|
|
3359
4502
|
}
|
|
3360
4503
|
|
|
4504
|
+
function segmentHit_big(ax, ay, bx, by, cx, cy, dx, dy) {
|
|
4505
|
+
return orient2D_big(ax, ay, bx, by, cx, cy).times(
|
|
4506
|
+
orient2D_big(ax, ay, bx, by, dx, dy)).lte(0) &&
|
|
4507
|
+
orient2D_big(cx, cy, dx, dy, ax, ay).times(
|
|
4508
|
+
orient2D_big(cx, cy, dx, dy, bx, by)).lte(0);
|
|
4509
|
+
}
|
|
4510
|
+
|
|
4511
|
+
function segmentHit_big2(ax, ay, bx, by, cx, cy, dx, dy) {
|
|
4512
|
+
return segmentHit_big(Big(ax), Big(ay), Big(bx), Big(by),
|
|
4513
|
+
Big(cx), Big(cy), Big(dx), Big(dy));
|
|
4514
|
+
}
|
|
4515
|
+
|
|
4516
|
+
// export function segmentHit_robust(ax, ay, bx, by, cx, cy, dx, dy) {
|
|
4517
|
+
// return -orient2D_robust(ax, ay, bx, by, cx, cy) *
|
|
4518
|
+
// -orient2D_robust(ax, ay, bx, by, dx, dy) <= 0 &&
|
|
4519
|
+
// -orient2D_robust(cx, cy, dx, dy, ax, ay) *
|
|
4520
|
+
// -orient2D_robust(cx, cy, dx, dy, bx, by) <= 0;
|
|
4521
|
+
// }
|
|
4522
|
+
|
|
3361
4523
|
// Useful for determining if a segment that intersects another segment is
|
|
3362
4524
|
// entering or leaving an enclosed buffer area
|
|
3363
4525
|
// returns -1 if angle of p1p2 -> p3p4 is counter-clockwise (left turn)
|
|
@@ -3382,7 +4544,11 @@
|
|
|
3382
4544
|
__proto__: null,
|
|
3383
4545
|
findClosestPointOnSeg: findClosestPointOnSeg,
|
|
3384
4546
|
orient2D: orient2D,
|
|
3385
|
-
|
|
4547
|
+
orient2D_big: orient2D_big,
|
|
4548
|
+
orient2D_big2: orient2D_big2,
|
|
4549
|
+
segmentHit_big: segmentHit_big,
|
|
4550
|
+
segmentHit_big2: segmentHit_big2,
|
|
4551
|
+
segmentHit_fast: segmentHit_fast,
|
|
3386
4552
|
segmentIntersection: segmentIntersection,
|
|
3387
4553
|
segmentTurn: segmentTurn
|
|
3388
4554
|
});
|
|
@@ -3692,7 +4858,7 @@
|
|
|
3692
4858
|
// editPart: callback function
|
|
3693
4859
|
function editShapes(shapes, editPart) {
|
|
3694
4860
|
for (var i=0, n=shapes.length; i<n; i++) {
|
|
3695
|
-
shapes[i] = editShapeParts(shapes[i], editPart);
|
|
4861
|
+
shapes[i] = editShapeParts(shapes[i], editPart, i);
|
|
3696
4862
|
}
|
|
3697
4863
|
}
|
|
3698
4864
|
|
|
@@ -3700,8 +4866,9 @@
|
|
|
3700
4866
|
// @cb: function(part, i, parts)
|
|
3701
4867
|
// If @cb returns an array, it replaces the existing value
|
|
3702
4868
|
// If @cb returns null, the path is removed from the feature
|
|
4869
|
+
// @shpId: (optional) id of shape
|
|
3703
4870
|
//
|
|
3704
|
-
function editShapeParts(parts, cb) {
|
|
4871
|
+
function editShapeParts(parts, cb, shpId) {
|
|
3705
4872
|
if (!parts) return null; // null geometry not edited
|
|
3706
4873
|
if (!utils.isArray(parts)) error("Expected an array, received:", parts);
|
|
3707
4874
|
var nulls = 0,
|
|
@@ -3709,7 +4876,7 @@
|
|
|
3709
4876
|
retn;
|
|
3710
4877
|
|
|
3711
4878
|
for (var i=0; i<n; i++) {
|
|
3712
|
-
retn = cb(parts[i], i, parts);
|
|
4879
|
+
retn = cb(parts[i], i, parts, shpId);
|
|
3713
4880
|
if (retn === null) {
|
|
3714
4881
|
nulls++;
|
|
3715
4882
|
parts[i] = null;
|
|
@@ -5756,7 +6923,7 @@
|
|
|
5756
6923
|
//
|
|
5757
6924
|
this.forEach2 = function(cb) {
|
|
5758
6925
|
for (var arcId=0, n=this.size(); arcId<n; arcId++) {
|
|
5759
|
-
cb(_ii[arcId], _nn[arcId], _xx, _yy, _zz
|
|
6926
|
+
cb(arcId, _ii[arcId], _nn[arcId], _xx, _yy, _zz);
|
|
5760
6927
|
}
|
|
5761
6928
|
};
|
|
5762
6929
|
|
|
@@ -5891,6 +7058,27 @@
|
|
|
5891
7058
|
};
|
|
5892
7059
|
|
|
5893
7060
|
this.arcIsDegenerate = function(arcId) {
|
|
7061
|
+
return this.arcHasZeroLength(arcId) || this.arcIsSpike(arcId);
|
|
7062
|
+
};
|
|
7063
|
+
|
|
7064
|
+
this.arcIsSpike = function(arcId) {
|
|
7065
|
+
var iter = this.getArcIter(arcId);
|
|
7066
|
+
var x0, y0;
|
|
7067
|
+
if (iter.hasNext()) {
|
|
7068
|
+
x0 = iter.x;
|
|
7069
|
+
y0 = iter.y;
|
|
7070
|
+
}
|
|
7071
|
+
iter.hasNext(); // ignore second point
|
|
7072
|
+
if (iter.hasNext()) {
|
|
7073
|
+
if (iter.x == x0 && iter.y == y0 && !iter.hasNext()) {
|
|
7074
|
+
// three-vertex arc, first two are the same
|
|
7075
|
+
return true;
|
|
7076
|
+
}
|
|
7077
|
+
}
|
|
7078
|
+
return false;
|
|
7079
|
+
};
|
|
7080
|
+
|
|
7081
|
+
this.arcHasZeroLength = function(arcId) {
|
|
5894
7082
|
var iter = this.getArcIter(arcId);
|
|
5895
7083
|
var i = 0,
|
|
5896
7084
|
x, y;
|
|
@@ -6397,10 +7585,10 @@
|
|
|
6397
7585
|
// Visit each point in the path, up to but not including the last point
|
|
6398
7586
|
for (var i = start; i < end; i++) {
|
|
6399
7587
|
if (pointIsArcEndpoint(i)) {
|
|
6400
|
-
if (firstNodeId
|
|
6401
|
-
arcIds.push(addEdge(arcStartId, i));
|
|
6402
|
-
} else {
|
|
7588
|
+
if (firstNodeId == -1) {
|
|
6403
7589
|
firstNodeId = i;
|
|
7590
|
+
} else {
|
|
7591
|
+
arcIds.push(addEdge(arcStartId, i));
|
|
6404
7592
|
}
|
|
6405
7593
|
arcStartId = i;
|
|
6406
7594
|
}
|
|
@@ -6991,7 +8179,7 @@
|
|
|
6991
8179
|
yy2 = new Float64Array(n),
|
|
6992
8180
|
ids2 = new Int32Array(n);
|
|
6993
8181
|
|
|
6994
|
-
arcs.forEach2(function(i, n, xx, yy, zz
|
|
8182
|
+
arcs.forEach2(function(arcId, i, n, xx, yy, zz) {
|
|
6995
8183
|
var start = i,
|
|
6996
8184
|
end = i + n - 1,
|
|
6997
8185
|
start2 = arcId * 2,
|
|
@@ -7042,7 +8230,7 @@
|
|
|
7042
8230
|
layers.forEach(function(lyr) {
|
|
7043
8231
|
// modify copies of the original shapes; original shapes should be unmodified
|
|
7044
8232
|
// (need to test this)
|
|
7045
|
-
lyr.shapes = lyr.shapes.map(function(shape) {
|
|
8233
|
+
lyr.shapes = lyr.shapes.map(function(shape, i) {
|
|
7046
8234
|
return editShapeParts(shape && shape.concat(), translatePath);
|
|
7047
8235
|
});
|
|
7048
8236
|
});
|
|
@@ -12584,11 +13772,161 @@
|
|
|
12584
13772
|
stringifyAsNDJSON: stringifyAsNDJSON
|
|
12585
13773
|
});
|
|
12586
13774
|
|
|
13775
|
+
function findNearestVertices(p, shp, arcs) {
|
|
13776
|
+
var p2 = findNearestVertex(p[0], p[1], shp, arcs);
|
|
13777
|
+
return findVertexIds(p2.x, p2.y, arcs);
|
|
13778
|
+
}
|
|
13779
|
+
|
|
13780
|
+
function snapVerticesToPoint(ids, p, arcs) {
|
|
13781
|
+
var data = arcs.getVertexData();
|
|
13782
|
+
ids.forEach(function(idx) {
|
|
13783
|
+
setVertexCoords(p[0], p[1], idx, arcs);
|
|
13784
|
+
arcs.updateArcBounds(findArcIdFromVertexId(idx, data.ii));
|
|
13785
|
+
});
|
|
13786
|
+
}
|
|
13787
|
+
|
|
13788
|
+
|
|
13789
|
+
// p: point to snap
|
|
13790
|
+
// ids: ids of nearby vertices, possibly including an arc endpoint
|
|
13791
|
+
function snapPointToArcEndpoint(p, ids, arcs) {
|
|
13792
|
+
var p2, dx, dy;
|
|
13793
|
+
ids.forEach(function(idx) {
|
|
13794
|
+
if (vertexIsArcStart(idx, arcs)) {
|
|
13795
|
+
p2 = getVertexCoords(idx + 1, arcs);
|
|
13796
|
+
} else if (vertexIsArcEnd(idx, arcs)) {
|
|
13797
|
+
p2 = getVertexCoords(idx - 1, arcs);
|
|
13798
|
+
}
|
|
13799
|
+
});
|
|
13800
|
+
if (!p2) return;
|
|
13801
|
+
dx = p2[0] - p[0];
|
|
13802
|
+
dy = p2[1] - p[1];
|
|
13803
|
+
if (Math.abs(dx) > Math.abs(dy)) {
|
|
13804
|
+
p[1] = p2[1]; // snap y coord
|
|
13805
|
+
} else {
|
|
13806
|
+
p[0] = p2[0];
|
|
13807
|
+
}
|
|
13808
|
+
}
|
|
13809
|
+
|
|
13810
|
+
// Find ids of vertices with identical coordinates to x,y in an ArcCollection
|
|
13811
|
+
// Caveat: does not exclude vertices that are not visible at the
|
|
13812
|
+
// current level of simplification.
|
|
13813
|
+
function findVertexIds(x, y, arcs) {
|
|
13814
|
+
var data = arcs.getVertexData(),
|
|
13815
|
+
xx = data.xx,
|
|
13816
|
+
yy = data.yy,
|
|
13817
|
+
ids = [];
|
|
13818
|
+
for (var i=0, n=xx.length; i<n; i++) {
|
|
13819
|
+
if (xx[i] == x && yy[i] == y) ids.push(i);
|
|
13820
|
+
}
|
|
13821
|
+
return ids;
|
|
13822
|
+
}
|
|
13823
|
+
|
|
13824
|
+
function getVertexCoords(i, arcs) {
|
|
13825
|
+
var data = arcs.getVertexData();
|
|
13826
|
+
return [data.xx[i], data.yy[i]];
|
|
13827
|
+
}
|
|
13828
|
+
|
|
13829
|
+
function vertexIsArcEnd(idx, arcs) {
|
|
13830
|
+
// Test whether the vertex at index @idx is the endpoint of an arc
|
|
13831
|
+
var data = arcs.getVertexData(),
|
|
13832
|
+
ii = data.ii,
|
|
13833
|
+
nn = data.nn;
|
|
13834
|
+
for (var j=0, n=ii.length; j<n; j++) {
|
|
13835
|
+
if (idx === ii[j] + nn[j] - 1) return true;
|
|
13836
|
+
}
|
|
13837
|
+
return false;
|
|
13838
|
+
}
|
|
13839
|
+
|
|
13840
|
+
function vertexIsArcEndpoint(idx, arcs) {
|
|
13841
|
+
return vertexIsArcStart(idx, arcs) || vertexIsArcEnd(idx, arcs);
|
|
13842
|
+
}
|
|
13843
|
+
|
|
13844
|
+
function vertexIsArcStart(idx, arcs) {
|
|
13845
|
+
var ii = arcs.getVertexData().ii;
|
|
13846
|
+
for (var j=0, n=ii.length; j<n; j++) {
|
|
13847
|
+
if (idx === ii[j]) return true;
|
|
13848
|
+
}
|
|
13849
|
+
return false;
|
|
13850
|
+
}
|
|
13851
|
+
|
|
13852
|
+
function getArcStartCoords(arcId, arcs) {
|
|
13853
|
+
var coords = getArcEndpointCoords(arcId, arcs);
|
|
13854
|
+
return coords[0];
|
|
13855
|
+
}
|
|
13856
|
+
|
|
13857
|
+
function getArcEndCoords(arcId, arcs) {
|
|
13858
|
+
var coords = getArcEndpointCoords(arcId, arcs);
|
|
13859
|
+
return coords[1];
|
|
13860
|
+
}
|
|
13861
|
+
|
|
13862
|
+
function getArcEndpointCoords(arcId, arcs) {
|
|
13863
|
+
if (arcId < 0) {
|
|
13864
|
+
return getArcEndpointCoords(~arcId, arcs).reverse();
|
|
13865
|
+
}
|
|
13866
|
+
var data = arcs.getVertexData();
|
|
13867
|
+
var i = data.ii[arcId];
|
|
13868
|
+
var n = data.nn[arcId];
|
|
13869
|
+
var a = [data.xx[i], data.yy[i]];
|
|
13870
|
+
var b = [data.xx[i + n - 1], data.yy[i + n - 1]];
|
|
13871
|
+
return [a, b];
|
|
13872
|
+
}
|
|
13873
|
+
|
|
13874
|
+
function setVertexCoords(x, y, i, arcs) {
|
|
13875
|
+
var data = arcs.getVertexData();
|
|
13876
|
+
data.xx[i] = x;
|
|
13877
|
+
data.yy[i] = y;
|
|
13878
|
+
}
|
|
13879
|
+
|
|
13880
|
+
function findNearestVertex(x, y, shp, arcs, spherical) {
|
|
13881
|
+
var calcLen = spherical ? geom.greatCircleDistance : geom.distance2D,
|
|
13882
|
+
minLen = Infinity,
|
|
13883
|
+
minX, minY, dist, iter;
|
|
13884
|
+
for (var i=0; i<shp.length; i++) {
|
|
13885
|
+
iter = arcs.getShapeIter(shp[i]);
|
|
13886
|
+
while (iter.hasNext()) {
|
|
13887
|
+
dist = calcLen(x, y, iter.x, iter.y);
|
|
13888
|
+
if (dist < minLen) {
|
|
13889
|
+
minLen = dist;
|
|
13890
|
+
minX = iter.x;
|
|
13891
|
+
minY = iter.y;
|
|
13892
|
+
}
|
|
13893
|
+
}
|
|
13894
|
+
}
|
|
13895
|
+
return minLen < Infinity ? {x: minX, y: minY} : null;
|
|
13896
|
+
}
|
|
13897
|
+
|
|
13898
|
+
var VertexUtils = /*#__PURE__*/Object.freeze({
|
|
13899
|
+
__proto__: null,
|
|
13900
|
+
findNearestVertex: findNearestVertex,
|
|
13901
|
+
findNearestVertices: findNearestVertices,
|
|
13902
|
+
findVertexIds: findVertexIds,
|
|
13903
|
+
getArcEndCoords: getArcEndCoords,
|
|
13904
|
+
getArcEndpointCoords: getArcEndpointCoords,
|
|
13905
|
+
getArcStartCoords: getArcStartCoords,
|
|
13906
|
+
getVertexCoords: getVertexCoords,
|
|
13907
|
+
setVertexCoords: setVertexCoords,
|
|
13908
|
+
snapPointToArcEndpoint: snapPointToArcEndpoint,
|
|
13909
|
+
snapVerticesToPoint: snapVerticesToPoint,
|
|
13910
|
+
vertexIsArcEnd: vertexIsArcEnd,
|
|
13911
|
+
vertexIsArcEndpoint: vertexIsArcEndpoint,
|
|
13912
|
+
vertexIsArcStart: vertexIsArcStart
|
|
13913
|
+
});
|
|
13914
|
+
|
|
12587
13915
|
function isValidArc(arcId, arcs) {
|
|
12588
13916
|
// check for arcs with no vertices
|
|
12589
13917
|
// TODO: also check for other kinds of degenerate arcs
|
|
12590
13918
|
// (e.g. collapsed arcs consisting of identical points)
|
|
12591
|
-
|
|
13919
|
+
var len = arcs.getArcLength(arcId);
|
|
13920
|
+
if (len >= 2 === false) {
|
|
13921
|
+
return false;
|
|
13922
|
+
}
|
|
13923
|
+
// if (len <=3) {
|
|
13924
|
+
// var endpoints = getArcEndpointCoords(arcId, arcs);
|
|
13925
|
+
// if (pointsAreEqual(endpoints[0], endpoints[1])) {
|
|
13926
|
+
// return false;
|
|
13927
|
+
// }
|
|
13928
|
+
// }
|
|
13929
|
+
return true;
|
|
12592
13930
|
}
|
|
12593
13931
|
|
|
12594
13932
|
// Return id of rightmost connected arc in relation to @fromArcId
|
|
@@ -12625,6 +13963,7 @@
|
|
|
12625
13963
|
candId = ids[j];
|
|
12626
13964
|
if (!isValidArc(candId, arcs)) {
|
|
12627
13965
|
// skip empty arcs
|
|
13966
|
+
debug('skipping one arc:', candId, 'out of:', ids.length, ids);
|
|
12628
13967
|
continue;
|
|
12629
13968
|
}
|
|
12630
13969
|
icand = arcs.indexOfVertex(candId, -2);
|
|
@@ -12635,6 +13974,10 @@
|
|
|
12635
13974
|
continue;
|
|
12636
13975
|
}
|
|
12637
13976
|
code = chooseRighthandPath(fromX, fromY, nodeX, nodeY, xx[ito], yy[ito], xx[icand], yy[icand]);
|
|
13977
|
+
|
|
13978
|
+
if (xx[ito] == xx[icand] && yy[ito] == yy[icand]) {
|
|
13979
|
+
debug("Pathfinder warning: duplicate segments: i:", ito, "j:", icand, "ids:", [fromArcId].concat(ids));
|
|
13980
|
+
}
|
|
12638
13981
|
if (code == 2) {
|
|
12639
13982
|
ito = icand;
|
|
12640
13983
|
toArcId = candId;
|
|
@@ -12644,26 +13987,22 @@
|
|
|
12644
13987
|
|
|
12645
13988
|
if (toArcId == fromArcId) {
|
|
12646
13989
|
// This shouldn't occur, assuming that other arcs are present
|
|
12647
|
-
error("Pathfinder error");
|
|
13990
|
+
error("Pathfinder error", toArcId, fromArcId);
|
|
12648
13991
|
}
|
|
12649
13992
|
return toArcId;
|
|
12650
13993
|
}
|
|
12651
13994
|
|
|
12652
|
-
// TODO: consider using simpler internal.chooseRighthandPath2()
|
|
12653
13995
|
// Returns 1 if node->a, return 2 if node->b, else return 0
|
|
12654
13996
|
// TODO: better handling of identical angles (better -- avoid creating them)
|
|
12655
13997
|
function chooseRighthandPath(fromX, fromY, nodeX, nodeY, ax, ay, bx, by) {
|
|
12656
13998
|
var angleA = geom.signedAngle(fromX, fromY, nodeX, nodeY, ax, ay);
|
|
12657
13999
|
var angleB = geom.signedAngle(fromX, fromY, nodeX, nodeY, bx, by);
|
|
14000
|
+
// should use arbitrary precision math to evaluate angles smaller than this
|
|
14001
|
+
// (all observed errors caused by fp rounding occured with smaller angles than this)
|
|
14002
|
+
var smallAngle = 0.001;
|
|
12658
14003
|
var code;
|
|
12659
14004
|
if (angleA <= 0 || angleB <= 0) {
|
|
12660
14005
|
debug("[chooseRighthandPath()] 0 angle(s):", angleA, angleB);
|
|
12661
|
-
if (angleA <= 0) {
|
|
12662
|
-
debug(' A orient2D:', geom.orient2D(fromX, fromY, nodeX, nodeY, ax, ay));
|
|
12663
|
-
}
|
|
12664
|
-
if (angleB <= 0) {
|
|
12665
|
-
debug(' B orient2D:', geom.orient2D(fromX, fromY, nodeX, nodeY, bx, by));
|
|
12666
|
-
}
|
|
12667
14006
|
// TODO: test against "from" segment
|
|
12668
14007
|
if (angleA > 0) {
|
|
12669
14008
|
code = 1;
|
|
@@ -12672,23 +14011,24 @@
|
|
|
12672
14011
|
} else {
|
|
12673
14012
|
code = 0;
|
|
12674
14013
|
}
|
|
12675
|
-
} else if (angleA < angleB) {
|
|
14014
|
+
} else if (angleA < angleB - smallAngle) {
|
|
12676
14015
|
code = 1;
|
|
12677
|
-
} else if (angleB < angleA) {
|
|
14016
|
+
} else if (angleB < angleA - smallAngle) {
|
|
12678
14017
|
code = 2;
|
|
12679
14018
|
} else if (isNaN(angleA) || isNaN(angleB)) {
|
|
12680
14019
|
// probably a duplicate point, which should not occur
|
|
12681
14020
|
error('Invalid node geometry');
|
|
12682
14021
|
} else {
|
|
12683
|
-
//
|
|
12684
|
-
code =
|
|
12685
|
-
debug('[chooseRighthandPath()] equal angles:', angleA, '
|
|
14022
|
+
// Close-to-equal or equal angles: use more exact test.
|
|
14023
|
+
code = chooseBetweenClosePaths(nodeX, nodeY, ax, ay, bx, by);
|
|
14024
|
+
// debug('[chooseRighthandPath()] close-to-equal angles:', Math.abs(angleA) - Math.abs(angleB), 'hi-res code:', code);
|
|
12686
14025
|
}
|
|
12687
14026
|
return code;
|
|
12688
14027
|
}
|
|
12689
14028
|
|
|
12690
|
-
function
|
|
12691
|
-
var orient =
|
|
14029
|
+
function chooseBetweenClosePaths(nodeX, nodeY, ax, ay, bx, by) {
|
|
14030
|
+
// var orient = orient2D_big2(ax, ay, 0, 0, bx, by);
|
|
14031
|
+
var orient = orient2D_big2(ax, ay, nodeX, nodeY, bx, by);
|
|
12692
14032
|
var code;
|
|
12693
14033
|
if (orient > 0) {
|
|
12694
14034
|
code = 2;
|
|
@@ -12702,7 +14042,7 @@
|
|
|
12702
14042
|
|
|
12703
14043
|
var PathfinderUtils = /*#__PURE__*/Object.freeze({
|
|
12704
14044
|
__proto__: null,
|
|
12705
|
-
|
|
14045
|
+
chooseBetweenClosePaths: chooseBetweenClosePaths,
|
|
12706
14046
|
getRightmostArc: getRightmostArc
|
|
12707
14047
|
});
|
|
12708
14048
|
|
|
@@ -13227,7 +14567,7 @@
|
|
|
13227
14567
|
}
|
|
13228
14568
|
|
|
13229
14569
|
// test two candidate segments for intersection
|
|
13230
|
-
hit =
|
|
14570
|
+
hit = segmentIntersection(s1p1x, s1p1y, s1p2x, s1p2y,
|
|
13231
14571
|
s2p1x, s2p1y, s2p2x, s2p2y, tolerance);
|
|
13232
14572
|
if (hit) {
|
|
13233
14573
|
seg1 = [s1p1, s1p2];
|
|
@@ -13285,146 +14625,6 @@
|
|
|
13285
14625
|
sortIntersections: sortIntersections
|
|
13286
14626
|
});
|
|
13287
14627
|
|
|
13288
|
-
function findNearestVertices(p, shp, arcs) {
|
|
13289
|
-
var p2 = findNearestVertex(p[0], p[1], shp, arcs);
|
|
13290
|
-
return findVertexIds(p2.x, p2.y, arcs);
|
|
13291
|
-
}
|
|
13292
|
-
|
|
13293
|
-
function snapVerticesToPoint(ids, p, arcs) {
|
|
13294
|
-
var data = arcs.getVertexData();
|
|
13295
|
-
ids.forEach(function(idx) {
|
|
13296
|
-
setVertexCoords(p[0], p[1], idx, arcs);
|
|
13297
|
-
arcs.updateArcBounds(findArcIdFromVertexId(idx, data.ii));
|
|
13298
|
-
});
|
|
13299
|
-
}
|
|
13300
|
-
|
|
13301
|
-
|
|
13302
|
-
// p: point to snap
|
|
13303
|
-
// ids: ids of nearby vertices, possibly including an arc endpoint
|
|
13304
|
-
function snapPointToArcEndpoint(p, ids, arcs) {
|
|
13305
|
-
var p2, dx, dy;
|
|
13306
|
-
ids.forEach(function(idx) {
|
|
13307
|
-
if (vertexIsArcStart(idx, arcs)) {
|
|
13308
|
-
p2 = getVertexCoords(idx + 1, arcs);
|
|
13309
|
-
} else if (vertexIsArcEnd(idx, arcs)) {
|
|
13310
|
-
p2 = getVertexCoords(idx - 1, arcs);
|
|
13311
|
-
}
|
|
13312
|
-
});
|
|
13313
|
-
if (!p2) return;
|
|
13314
|
-
dx = p2[0] - p[0];
|
|
13315
|
-
dy = p2[1] - p[1];
|
|
13316
|
-
if (Math.abs(dx) > Math.abs(dy)) {
|
|
13317
|
-
p[1] = p2[1]; // snap y coord
|
|
13318
|
-
} else {
|
|
13319
|
-
p[0] = p2[0];
|
|
13320
|
-
}
|
|
13321
|
-
}
|
|
13322
|
-
|
|
13323
|
-
// Find ids of vertices with identical coordinates to x,y in an ArcCollection
|
|
13324
|
-
// Caveat: does not exclude vertices that are not visible at the
|
|
13325
|
-
// current level of simplification.
|
|
13326
|
-
function findVertexIds(x, y, arcs) {
|
|
13327
|
-
var data = arcs.getVertexData(),
|
|
13328
|
-
xx = data.xx,
|
|
13329
|
-
yy = data.yy,
|
|
13330
|
-
ids = [];
|
|
13331
|
-
for (var i=0, n=xx.length; i<n; i++) {
|
|
13332
|
-
if (xx[i] == x && yy[i] == y) ids.push(i);
|
|
13333
|
-
}
|
|
13334
|
-
return ids;
|
|
13335
|
-
}
|
|
13336
|
-
|
|
13337
|
-
function getVertexCoords(i, arcs) {
|
|
13338
|
-
var data = arcs.getVertexData();
|
|
13339
|
-
return [data.xx[i], data.yy[i]];
|
|
13340
|
-
}
|
|
13341
|
-
|
|
13342
|
-
function vertexIsArcEnd(idx, arcs) {
|
|
13343
|
-
// Test whether the vertex at index @idx is the endpoint of an arc
|
|
13344
|
-
var data = arcs.getVertexData(),
|
|
13345
|
-
ii = data.ii,
|
|
13346
|
-
nn = data.nn;
|
|
13347
|
-
for (var j=0, n=ii.length; j<n; j++) {
|
|
13348
|
-
if (idx === ii[j] + nn[j] - 1) return true;
|
|
13349
|
-
}
|
|
13350
|
-
return false;
|
|
13351
|
-
}
|
|
13352
|
-
|
|
13353
|
-
function vertexIsArcEndpoint(idx, arcs) {
|
|
13354
|
-
return vertexIsArcStart(idx, arcs) || vertexIsArcEnd(idx, arcs);
|
|
13355
|
-
}
|
|
13356
|
-
|
|
13357
|
-
function vertexIsArcStart(idx, arcs) {
|
|
13358
|
-
var ii = arcs.getVertexData().ii;
|
|
13359
|
-
for (var j=0, n=ii.length; j<n; j++) {
|
|
13360
|
-
if (idx === ii[j]) return true;
|
|
13361
|
-
}
|
|
13362
|
-
return false;
|
|
13363
|
-
}
|
|
13364
|
-
|
|
13365
|
-
function getArcStartCoords(arcId, arcs) {
|
|
13366
|
-
var coords = getArcEndpointCoords(arcId, arcs);
|
|
13367
|
-
return coords[0];
|
|
13368
|
-
}
|
|
13369
|
-
|
|
13370
|
-
function getArcEndCoords(arcId, arcs) {
|
|
13371
|
-
var coords = getArcEndpointCoords(arcId, arcs);
|
|
13372
|
-
return coords[1];
|
|
13373
|
-
}
|
|
13374
|
-
|
|
13375
|
-
function getArcEndpointCoords(arcId, arcs) {
|
|
13376
|
-
if (arcId < 0) {
|
|
13377
|
-
return getArcEndpointCoords(~arcId, arcs).reverse();
|
|
13378
|
-
}
|
|
13379
|
-
var data = arcs.getVertexData();
|
|
13380
|
-
var i = data.ii[arcId];
|
|
13381
|
-
var n = data.nn[arcId];
|
|
13382
|
-
var a = [data.xx[i], data.yy[i]];
|
|
13383
|
-
var b = [data.xx[i + n - 1], data.yy[i + n - 1]];
|
|
13384
|
-
return [a, b];
|
|
13385
|
-
}
|
|
13386
|
-
|
|
13387
|
-
function setVertexCoords(x, y, i, arcs) {
|
|
13388
|
-
var data = arcs.getVertexData();
|
|
13389
|
-
data.xx[i] = x;
|
|
13390
|
-
data.yy[i] = y;
|
|
13391
|
-
}
|
|
13392
|
-
|
|
13393
|
-
function findNearestVertex(x, y, shp, arcs, spherical) {
|
|
13394
|
-
var calcLen = spherical ? geom.greatCircleDistance : geom.distance2D,
|
|
13395
|
-
minLen = Infinity,
|
|
13396
|
-
minX, minY, dist, iter;
|
|
13397
|
-
for (var i=0; i<shp.length; i++) {
|
|
13398
|
-
iter = arcs.getShapeIter(shp[i]);
|
|
13399
|
-
while (iter.hasNext()) {
|
|
13400
|
-
dist = calcLen(x, y, iter.x, iter.y);
|
|
13401
|
-
if (dist < minLen) {
|
|
13402
|
-
minLen = dist;
|
|
13403
|
-
minX = iter.x;
|
|
13404
|
-
minY = iter.y;
|
|
13405
|
-
}
|
|
13406
|
-
}
|
|
13407
|
-
}
|
|
13408
|
-
return minLen < Infinity ? {x: minX, y: minY} : null;
|
|
13409
|
-
}
|
|
13410
|
-
|
|
13411
|
-
var VertexUtils = /*#__PURE__*/Object.freeze({
|
|
13412
|
-
__proto__: null,
|
|
13413
|
-
findNearestVertex: findNearestVertex,
|
|
13414
|
-
findNearestVertices: findNearestVertices,
|
|
13415
|
-
findVertexIds: findVertexIds,
|
|
13416
|
-
getArcEndCoords: getArcEndCoords,
|
|
13417
|
-
getArcEndpointCoords: getArcEndpointCoords,
|
|
13418
|
-
getArcStartCoords: getArcStartCoords,
|
|
13419
|
-
getVertexCoords: getVertexCoords,
|
|
13420
|
-
setVertexCoords: setVertexCoords,
|
|
13421
|
-
snapPointToArcEndpoint: snapPointToArcEndpoint,
|
|
13422
|
-
snapVerticesToPoint: snapVerticesToPoint,
|
|
13423
|
-
vertexIsArcEnd: vertexIsArcEnd,
|
|
13424
|
-
vertexIsArcEndpoint: vertexIsArcEndpoint,
|
|
13425
|
-
vertexIsArcStart: vertexIsArcStart
|
|
13426
|
-
});
|
|
13427
|
-
|
|
13428
14628
|
// arcs: ArcCollection containing original coordinates
|
|
13429
14629
|
function getRepairFunction(arcs) {
|
|
13430
14630
|
var arcsOrig = arcs.getCopy();
|
|
@@ -13856,6 +15056,9 @@
|
|
|
13856
15056
|
getSliverTest(dataset.arcs, threshold, sliverControl) :
|
|
13857
15057
|
getMinAreaTest(threshold, dataset);
|
|
13858
15058
|
var label = getSliverLabel(getAreaLabel(threshold, crs), sliverControl > 0);
|
|
15059
|
+
if (opts.keep_shapes) {
|
|
15060
|
+
filter = keepShapes(filter);
|
|
15061
|
+
}
|
|
13859
15062
|
return {
|
|
13860
15063
|
threshold: threshold,
|
|
13861
15064
|
filter: filter,
|
|
@@ -13863,6 +15066,23 @@
|
|
|
13863
15066
|
};
|
|
13864
15067
|
}
|
|
13865
15068
|
|
|
15069
|
+
// wrap path filter in a function that ensures at least one part of every shape
|
|
15070
|
+
// is retained.
|
|
15071
|
+
function keepShapes(filter) {
|
|
15072
|
+
var flags;
|
|
15073
|
+
return function(path, pathId, paths) {
|
|
15074
|
+
// console.log("keepShapes()", path, pathId, paths)
|
|
15075
|
+
if (pathId === 0) {
|
|
15076
|
+
flags = paths.map(path => filter(path));
|
|
15077
|
+
}
|
|
15078
|
+
if (flags.length > 0 && flags.every(Boolean)) {
|
|
15079
|
+
// kludge ... assumes that the first path is a valid ring (e.g. not a)
|
|
15080
|
+
flags[0] = false;
|
|
15081
|
+
}
|
|
15082
|
+
return flags[pathId];
|
|
15083
|
+
};
|
|
15084
|
+
}
|
|
15085
|
+
|
|
13866
15086
|
function getSliverLabel(areaStr, variable) {
|
|
13867
15087
|
if (variable) {
|
|
13868
15088
|
areaStr = areaStr.replace(' ', '+ ') + ' variable';
|
|
@@ -16813,9 +18033,10 @@
|
|
|
16813
18033
|
|
|
16814
18034
|
function snapAndCut(dataset, snapDist) {
|
|
16815
18035
|
var arcs = dataset.arcs;
|
|
16816
|
-
var cutOpts = snapDist > 0 ? {} : {tolerance: 0};
|
|
18036
|
+
var cutOpts = snapDist > 0 ? {tolerance: snapDist} : {tolerance: 0};
|
|
16817
18037
|
var coordsHaveChanged = false;
|
|
16818
18038
|
var snapCount, dupeCount, cutCount;
|
|
18039
|
+
var maxPasses = 4, passCount = 0;
|
|
16819
18040
|
snapCount = snapCoordsByInterval(arcs, snapDist);
|
|
16820
18041
|
dupeCount = arcs.dedupCoords();
|
|
16821
18042
|
|
|
@@ -16827,23 +18048,28 @@
|
|
|
16827
18048
|
|
|
16828
18049
|
// cut arcs at points where segments intersect
|
|
16829
18050
|
cutCount = cutPathsAtIntersections(dataset, cutOpts);
|
|
18051
|
+
passCount++;
|
|
16830
18052
|
if (cutCount > 0 || snapCount > 0 || dupeCount > 0) {
|
|
16831
18053
|
coordsHaveChanged = true;
|
|
16832
18054
|
}
|
|
16833
18055
|
|
|
16834
18056
|
// perform a second snap + cut pass if needed
|
|
16835
|
-
|
|
16836
|
-
|
|
18057
|
+
while (cutCount > 0 && passCount < maxPasses) {
|
|
18058
|
+
passCount++;
|
|
16837
18059
|
snapCount = snapCoordsByInterval(arcs, snapDist);
|
|
16838
|
-
arcs.dedupCoords();
|
|
16839
|
-
|
|
18060
|
+
dupeCount = arcs.dedupCoords();
|
|
18061
|
+
// cutCount = 0;
|
|
18062
|
+
if (snapCount > 0 || cutCount > 0) {
|
|
16840
18063
|
cutCount = cutPathsAtIntersections(dataset, cutOpts);
|
|
16841
|
-
|
|
16842
|
-
if (cutCount > 0) {
|
|
16843
|
-
arcs.dedupCoords(); // need to do this here?
|
|
16844
|
-
debug('Second-pass vertices added:', cutCount, 'consider third pass?');
|
|
18064
|
+
debug("[snapAndCut] pass:", passCount, "snaps:", snapCount, "dupes:", dupeCount, "cuts:", cutCount);
|
|
16845
18065
|
}
|
|
16846
18066
|
}
|
|
18067
|
+
|
|
18068
|
+
// if (cutCount > 0) {
|
|
18069
|
+
// arcs.dedupCoords(); // need to do this here?
|
|
18070
|
+
// debug('Second-pass vertices added:', cutCount, 'consider third pass?');
|
|
18071
|
+
// }
|
|
18072
|
+
|
|
16847
18073
|
return coordsHaveChanged;
|
|
16848
18074
|
}
|
|
16849
18075
|
|
|
@@ -17028,21 +18254,21 @@
|
|
|
17028
18254
|
function filterSortedCutPoints(points, arcs) {
|
|
17029
18255
|
var filtered = [],
|
|
17030
18256
|
pointId = 0;
|
|
17031
|
-
arcs.forEach2(function(i, n, xx, yy) {
|
|
17032
|
-
var j = i + n - 1,
|
|
17033
|
-
|
|
17034
|
-
|
|
17035
|
-
|
|
17036
|
-
|
|
18257
|
+
arcs.forEach2(function(arcId, i, n, xx, yy) {
|
|
18258
|
+
var j = i + n - 1, // idx of second endpoint
|
|
18259
|
+
x1 = xx[i],
|
|
18260
|
+
y1 = yy[i],
|
|
18261
|
+
x2 = xx[j],
|
|
18262
|
+
y2 = yy[j],
|
|
17037
18263
|
p, pp;
|
|
17038
18264
|
|
|
17039
18265
|
while (pointId < points.length && points[pointId].i <= j) {
|
|
17040
18266
|
p = points[pointId];
|
|
18267
|
+
pointId++;
|
|
17041
18268
|
pp = filtered[filtered.length - 1]; // previous point
|
|
17042
|
-
if (p.x ==
|
|
18269
|
+
if (p.x == x1 && p.y == y1 && p.i == i || p.x == x2 && p.y == y2 && p.i >= j - 1) ; else if (pp && pp.x == p.x && pp.y == p.y && pp.i == p.i) ; else {
|
|
17043
18270
|
filtered.push(p);
|
|
17044
18271
|
}
|
|
17045
|
-
pointId++;
|
|
17046
18272
|
}
|
|
17047
18273
|
});
|
|
17048
18274
|
return filtered;
|
|
@@ -18777,203 +20003,6 @@
|
|
|
18777
20003
|
catalog.getDatasets().push(o);
|
|
18778
20004
|
}
|
|
18779
20005
|
|
|
18780
|
-
// Apply rotation, scale and/or shift to some or all of the features in a dataset
|
|
18781
|
-
//
|
|
18782
|
-
cmd.affine = function(targetLayers, dataset, opts) {
|
|
18783
|
-
// Need to separate the targeted shapes from any other shapes that share
|
|
18784
|
-
// the same topology. So we duplicate any arcs that are shared by the targeted
|
|
18785
|
-
// shapes and their topological neighbors and remap arc references in the
|
|
18786
|
-
// neighbors to point to the copies.
|
|
18787
|
-
// TODO: explore alternative: if some arcs are shared between transformed and
|
|
18788
|
-
// non-transformed shapes, first remove topology, then tranform, then rebuild topology
|
|
18789
|
-
//
|
|
18790
|
-
var rotateArg = opts.rotate || 0;
|
|
18791
|
-
var scaleArg = opts.scale || 1;
|
|
18792
|
-
var shiftArg = opts.shift ? convertIntervalPair(opts.shift, getDatasetCRS(dataset)) : [0, 0];
|
|
18793
|
-
var arcs = dataset.arcs;
|
|
18794
|
-
var targetShapes = [];
|
|
18795
|
-
var otherShapes = [];
|
|
18796
|
-
var targetPoints = [];
|
|
18797
|
-
var targetFlags, otherFlags, transform;
|
|
18798
|
-
dataset.layers.filter(layerHasGeometry).forEach(function(lyr) {
|
|
18799
|
-
var hits = [],
|
|
18800
|
-
misses = [],
|
|
18801
|
-
test;
|
|
18802
|
-
if (targetLayers.indexOf(lyr) == -1) {
|
|
18803
|
-
misses = lyr.shapes;
|
|
18804
|
-
} else if (opts.where) {
|
|
18805
|
-
test = compileFeatureExpression(opts.where, lyr, dataset.arcs);
|
|
18806
|
-
lyr.shapes.forEach(function(shp, i) {
|
|
18807
|
-
(test(i) ? hits : misses).push(shp);
|
|
18808
|
-
});
|
|
18809
|
-
} else {
|
|
18810
|
-
hits = lyr.shapes;
|
|
18811
|
-
}
|
|
18812
|
-
if (lyr.geometry_type == 'point') {
|
|
18813
|
-
targetPoints = targetPoints.concat(hits);
|
|
18814
|
-
} else {
|
|
18815
|
-
targetShapes = targetShapes.concat(hits);
|
|
18816
|
-
otherShapes = otherShapes.concat(misses);
|
|
18817
|
-
}
|
|
18818
|
-
});
|
|
18819
|
-
var anchorArg = getAffineAnchor({arcs: dataset.arcs, layers: [{
|
|
18820
|
-
geometry_type: 'point', shapes: targetPoints}, {geometry_type: 'polyline',
|
|
18821
|
-
shapes: targetShapes}]}, opts);
|
|
18822
|
-
transform = getAffineTransform(rotateArg, scaleArg, shiftArg, anchorArg);
|
|
18823
|
-
if (opts.fit_bbox) {
|
|
18824
|
-
transform = getFitBoxTransform(opts.fit_bbox, targetPoints, targetShapes, arcs);
|
|
18825
|
-
}
|
|
18826
|
-
if (targetShapes.length > 0) {
|
|
18827
|
-
targetFlags = new Uint8Array(arcs.size());
|
|
18828
|
-
otherFlags = new Uint8Array(arcs.size());
|
|
18829
|
-
countArcsInShapes(targetShapes, targetFlags);
|
|
18830
|
-
if (otherShapes.length > 0) {
|
|
18831
|
-
countArcsInShapes(otherShapes, otherFlags);
|
|
18832
|
-
applyArrayMask(otherFlags, targetFlags);
|
|
18833
|
-
dataset.arcs = duplicateSelectedArcs(otherShapes, arcs, otherFlags);
|
|
18834
|
-
}
|
|
18835
|
-
dataset.arcs.transformPoints(function(x, y, arcId) {
|
|
18836
|
-
if (arcId < targetFlags.length && targetFlags[arcId] > 0) {
|
|
18837
|
-
return transform(x, y);
|
|
18838
|
-
}
|
|
18839
|
-
});
|
|
18840
|
-
}
|
|
18841
|
-
forEachPoint(targetPoints, function(p) {
|
|
18842
|
-
var p2 = transform(p[0], p[1]);
|
|
18843
|
-
p[0] = p2[0];
|
|
18844
|
-
p[1] = p2[1];
|
|
18845
|
-
});
|
|
18846
|
-
};
|
|
18847
|
-
|
|
18848
|
-
function getAffineAnchor(dataset, opts) {
|
|
18849
|
-
var anchor, bounds;
|
|
18850
|
-
if (opts.anchor) {
|
|
18851
|
-
anchor = opts.anchor;
|
|
18852
|
-
} else {
|
|
18853
|
-
// get bounds of selected shapes to calculate center of rotation/scale
|
|
18854
|
-
bounds = getDatasetBounds(dataset);
|
|
18855
|
-
anchor = [bounds.centerX(), bounds.centerY()];
|
|
18856
|
-
}
|
|
18857
|
-
return anchor;
|
|
18858
|
-
}
|
|
18859
|
-
|
|
18860
|
-
// TODO: handle problems with unprojected datasets
|
|
18861
|
-
// option 1: don't allow affine transformation of unprojected data
|
|
18862
|
-
// option 2: error if transformed data exceeds valid coordinate range
|
|
18863
|
-
// source: http://mathworld.wolfram.com/AffineTransformation.html
|
|
18864
|
-
function getAffineTransform(rotation, scale, shift, anchor) {
|
|
18865
|
-
var angle = rotation * Math.PI / 180;
|
|
18866
|
-
var a = scale * Math.cos(angle);
|
|
18867
|
-
var b = -scale * Math.sin(angle);
|
|
18868
|
-
return function(x, y) {
|
|
18869
|
-
var x2 = a * (x - anchor[0]) - b * (y - anchor[1]) + shift[0] + anchor[0];
|
|
18870
|
-
var y2 = b * (x - anchor[0]) + a * (y - anchor[1]) + shift[1] + anchor[1];
|
|
18871
|
-
return [x2, y2];
|
|
18872
|
-
};
|
|
18873
|
-
}
|
|
18874
|
-
|
|
18875
|
-
function getFitBoxTransform(bbox, points, shapes, arcs) {
|
|
18876
|
-
var dataset = {
|
|
18877
|
-
arcs: arcs,
|
|
18878
|
-
layers: []
|
|
18879
|
-
};
|
|
18880
|
-
if (points && points.length) {
|
|
18881
|
-
dataset.layers.push({
|
|
18882
|
-
geometry_type: 'point',
|
|
18883
|
-
shapes: points
|
|
18884
|
-
});
|
|
18885
|
-
}
|
|
18886
|
-
if (shapes && shapes.length) {
|
|
18887
|
-
dataset.layers.push({
|
|
18888
|
-
geometry_type: 'polyline',
|
|
18889
|
-
shapes: shapes
|
|
18890
|
-
});
|
|
18891
|
-
}
|
|
18892
|
-
var frame = calcFrameData(dataset, {fit_bbox: bbox});
|
|
18893
|
-
var fromBounds = new Bounds(frame.bbox);
|
|
18894
|
-
var toBounds = new Bounds(frame.bbox2);
|
|
18895
|
-
var fwd = fromBounds.getTransform(toBounds, false);
|
|
18896
|
-
return function(x, y) {
|
|
18897
|
-
return fwd.transform(x, y);
|
|
18898
|
-
};
|
|
18899
|
-
}
|
|
18900
|
-
|
|
18901
|
-
function applyArrayMask(destArr, maskArr) {
|
|
18902
|
-
for (var i=0, n=destArr.length; i<n; i++) {
|
|
18903
|
-
if (maskArr[i] === 0) destArr[i] = 0;
|
|
18904
|
-
}
|
|
18905
|
-
}
|
|
18906
|
-
|
|
18907
|
-
function duplicateSelectedArcs(shapes, arcs, flags) {
|
|
18908
|
-
var arcCount = 0;
|
|
18909
|
-
var vertexCount = 0;
|
|
18910
|
-
var data = arcs.getVertexData();
|
|
18911
|
-
var xx = [], yy = [], nn = [], map = [], n;
|
|
18912
|
-
for (var i=0, len=flags.length; i<len; i++) {
|
|
18913
|
-
if (flags[i] > 0) {
|
|
18914
|
-
map[i] = arcs.size() + arcCount;
|
|
18915
|
-
n = data.nn[i];
|
|
18916
|
-
utils.copyElements(data.xx, data.ii[i], xx, vertexCount, n);
|
|
18917
|
-
utils.copyElements(data.yy, data.ii[i], yy, vertexCount, n);
|
|
18918
|
-
nn.push(n);
|
|
18919
|
-
vertexCount += n;
|
|
18920
|
-
arcCount++;
|
|
18921
|
-
}
|
|
18922
|
-
}
|
|
18923
|
-
forEachArcId(shapes, function(id) {
|
|
18924
|
-
var absId = absArcId(id);
|
|
18925
|
-
if (flags[absId] > 0) {
|
|
18926
|
-
return id < 0 ? ~map[absId] : map[absId];
|
|
18927
|
-
}
|
|
18928
|
-
});
|
|
18929
|
-
return mergeArcs([arcs, new ArcCollection(nn, xx, yy)]);
|
|
18930
|
-
}
|
|
18931
|
-
|
|
18932
|
-
var roundCoord$2 = getRoundingFunction(0.01);
|
|
18933
|
-
|
|
18934
|
-
function stringifyVertex(p) {
|
|
18935
|
-
return ' ' + roundCoord$2(p[0]) + ' ' + roundCoord$2(p[1]);
|
|
18936
|
-
}
|
|
18937
|
-
|
|
18938
|
-
function isCubicCtrl(p) {
|
|
18939
|
-
return p.length > 2 && p[2] == 'C';
|
|
18940
|
-
}
|
|
18941
|
-
|
|
18942
|
-
function stringifyPolygonCoords(coords) {
|
|
18943
|
-
var parts = [];
|
|
18944
|
-
for (var i=0; i<coords.length; i++) {
|
|
18945
|
-
parts.push(stringifyLineStringCoords(coords[i]) + ' Z');
|
|
18946
|
-
}
|
|
18947
|
-
return parts.length > 0 ? parts.join(' ') : '';
|
|
18948
|
-
}
|
|
18949
|
-
|
|
18950
|
-
function stringifyLineStringCoords(coords) {
|
|
18951
|
-
if (coords.length === 0) return '';
|
|
18952
|
-
var d = 'M';
|
|
18953
|
-
var fromCurve = false;
|
|
18954
|
-
var p, i, n;
|
|
18955
|
-
for (i=0, n=coords.length; i<n; i++) {
|
|
18956
|
-
p = coords[i];
|
|
18957
|
-
if (isCubicCtrl(p)) {
|
|
18958
|
-
// TODO: add defensive check
|
|
18959
|
-
d += ' C' + stringifyVertex(p) + stringifyVertex(coords[++i]) + stringifyVertex(coords[++i]);
|
|
18960
|
-
fromCurve = true;
|
|
18961
|
-
} else if (fromCurve) {
|
|
18962
|
-
d += ' L' + stringifyVertex(p);
|
|
18963
|
-
fromCurve = false;
|
|
18964
|
-
} else {
|
|
18965
|
-
d += stringifyVertex(p);
|
|
18966
|
-
}
|
|
18967
|
-
}
|
|
18968
|
-
return d;
|
|
18969
|
-
}
|
|
18970
|
-
|
|
18971
|
-
var SvgPathUtils = /*#__PURE__*/Object.freeze({
|
|
18972
|
-
__proto__: null,
|
|
18973
|
-
stringifyLineStringCoords: stringifyLineStringCoords,
|
|
18974
|
-
stringifyPolygonCoords: stringifyPolygonCoords
|
|
18975
|
-
});
|
|
18976
|
-
|
|
18977
20006
|
/* example patterns
|
|
18978
20007
|
hatches 1px black 1px red 1px white
|
|
18979
20008
|
1px black 1px red 1px white // same as above (hatches is default)
|
|
@@ -19562,7 +20591,7 @@
|
|
|
19562
20591
|
}
|
|
19563
20592
|
|
|
19564
20593
|
function importLineString(coords) {
|
|
19565
|
-
var d = stringifyLineStringCoords(coords);
|
|
20594
|
+
var d = stringifyLineStringCoords$1(coords);
|
|
19566
20595
|
return {
|
|
19567
20596
|
tag: 'path',
|
|
19568
20597
|
properties: {d: d}
|
|
@@ -19570,7 +20599,7 @@
|
|
|
19570
20599
|
}
|
|
19571
20600
|
|
|
19572
20601
|
function importMultiLineString(coords) {
|
|
19573
|
-
var d = coords.map(stringifyLineStringCoords).join(' ');
|
|
20602
|
+
var d = coords.map(stringifyLineStringCoords$1).join(' ');
|
|
19574
20603
|
return {
|
|
19575
20604
|
tag: 'path',
|
|
19576
20605
|
properties: {d: d}
|
|
@@ -19592,7 +20621,7 @@
|
|
|
19592
20621
|
var o = {
|
|
19593
20622
|
tag: 'path',
|
|
19594
20623
|
properties: {
|
|
19595
|
-
d: stringifyPolygonCoords(coords)
|
|
20624
|
+
d: stringifyPolygonCoords$1(coords)
|
|
19596
20625
|
}
|
|
19597
20626
|
};
|
|
19598
20627
|
if (coords.length > 1) {
|
|
@@ -19601,6 +20630,23 @@
|
|
|
19601
20630
|
return o;
|
|
19602
20631
|
}
|
|
19603
20632
|
|
|
20633
|
+
function stringifyPolygonCoords$1(coords) {
|
|
20634
|
+
var parts = [];
|
|
20635
|
+
for (var i=0; i<coords.length; i++) {
|
|
20636
|
+
parts.push(stringifyLineStringCoords$1(coords[i]) + ' Z');
|
|
20637
|
+
}
|
|
20638
|
+
return parts.length > 0 ? parts.join(' ') : '';
|
|
20639
|
+
}
|
|
20640
|
+
|
|
20641
|
+
function stringifyLineStringCoords$1(coords) {
|
|
20642
|
+
if (coords.length === 0) return '';
|
|
20643
|
+
var d = 'M';
|
|
20644
|
+
for (var i=0, n=coords.length; i<n; i++) {
|
|
20645
|
+
d += ' ' + coords[i][0] + ' ' + coords[i][1];
|
|
20646
|
+
}
|
|
20647
|
+
return d;
|
|
20648
|
+
}
|
|
20649
|
+
|
|
19604
20650
|
var GeojsonToSvg = /*#__PURE__*/Object.freeze({
|
|
19605
20651
|
__proto__: null,
|
|
19606
20652
|
flattenMultiPolygonCoords: flattenMultiPolygonCoords,
|
|
@@ -20490,7 +21536,7 @@
|
|
|
20490
21536
|
opts = Object.assign({invert_y: true, margin: "1"}, opts);
|
|
20491
21537
|
frame = getFrameData(dataset, opts);
|
|
20492
21538
|
fitDatasetToFrame(dataset, frame);
|
|
20493
|
-
setCoordinatePrecision(dataset, opts.precision || 0.
|
|
21539
|
+
setCoordinatePrecision(dataset, opts.precision || 0.01);
|
|
20494
21540
|
|
|
20495
21541
|
// error if one or more svg_data fields are not present in any layers
|
|
20496
21542
|
if (opts.svg_data) validateSvgDataFields(dataset.layers, opts.svg_data);
|
|
@@ -22246,7 +23292,7 @@ ${svg}
|
|
|
22246
23292
|
if (opts.presimplify) {
|
|
22247
23293
|
fromZ = getPresimplifyFunction(bounds.width());
|
|
22248
23294
|
}
|
|
22249
|
-
arcs.forEach2(function(i, n, xx, yy, zz) {
|
|
23295
|
+
arcs.forEach2(function(arcId, i, n, xx, yy, zz) {
|
|
22250
23296
|
var arc = [], p;
|
|
22251
23297
|
for (var j=i + n; i<j; i++) {
|
|
22252
23298
|
p = [xx[i], yy[i]];
|
|
@@ -24842,6 +25888,11 @@ ${svg}
|
|
|
24842
25888
|
.option('sliver-control', sliverControlOpt)
|
|
24843
25889
|
.option('snap-interval', snapIntervalOpt)
|
|
24844
25890
|
.option('no-snap', noSnapOpt)
|
|
25891
|
+
// // not useful in -clean -- clean removes gaps, not slivers
|
|
25892
|
+
// .option('keep-shapes', {
|
|
25893
|
+
// type: 'flag',
|
|
25894
|
+
// describe: 'protect sliver polygons from complete removal'
|
|
25895
|
+
// })
|
|
24845
25896
|
.option('allow-overlaps', {
|
|
24846
25897
|
describe: 'allow polygons to overlap (disables gap fill)',
|
|
24847
25898
|
type: 'flag'
|
|
@@ -24893,6 +25944,10 @@ ${svg}
|
|
|
24893
25944
|
})
|
|
24894
25945
|
.option('name', nameOpt)
|
|
24895
25946
|
.option('no-snap', noSnapOpt)
|
|
25947
|
+
.option('no-cleanup', {
|
|
25948
|
+
// for debugging - no documentation
|
|
25949
|
+
type: 'flag'
|
|
25950
|
+
})
|
|
24896
25951
|
.option('target', targetOpt)
|
|
24897
25952
|
.option('no-replace', noReplaceOpt);
|
|
24898
25953
|
|
|
@@ -25128,6 +26183,10 @@ ${svg}
|
|
|
25128
26183
|
.option('bbox', bboxOpt)
|
|
25129
26184
|
.option('name', nameOpt)
|
|
25130
26185
|
.option('no-snap', noSnapOpt)
|
|
26186
|
+
.option('no-cleanup', {
|
|
26187
|
+
// for debugging - no documentation
|
|
26188
|
+
type: 'flag'
|
|
26189
|
+
})
|
|
25131
26190
|
.option('target', targetOpt)
|
|
25132
26191
|
.option('no-replace', noReplaceOpt);
|
|
25133
26192
|
|
|
@@ -25238,6 +26297,10 @@ ${svg}
|
|
|
25238
26297
|
type: 'flag',
|
|
25239
26298
|
describe: 'delete features with null geometry'
|
|
25240
26299
|
})
|
|
26300
|
+
.option('keep-shapes', {
|
|
26301
|
+
type: 'flag',
|
|
26302
|
+
describe: 'protect sliver polygons from complete removal'
|
|
26303
|
+
})
|
|
25241
26304
|
.option('target', targetOpt);
|
|
25242
26305
|
|
|
25243
26306
|
parser.command('graticule')
|
|
@@ -25445,9 +26508,8 @@ ${svg}
|
|
|
25445
26508
|
.option('calc', calcOpt)
|
|
25446
26509
|
.option('name', nameOpt)
|
|
25447
26510
|
.option('target', targetOpt)
|
|
25448
|
-
.option('
|
|
25449
|
-
|
|
25450
|
-
})
|
|
26511
|
+
.option('snap-interval', snapIntervalOpt)
|
|
26512
|
+
.option('no-snap', noSnapOpt)
|
|
25451
26513
|
.option('no-replace', noReplaceOpt);
|
|
25452
26514
|
|
|
25453
26515
|
parser.command('point-grid')
|
|
@@ -29843,6 +30905,158 @@ ${svg}
|
|
|
29843
30905
|
stop('Unable to import coordinates');
|
|
29844
30906
|
}
|
|
29845
30907
|
|
|
30908
|
+
// Apply rotation, scale and/or shift to some or all of the features in a dataset
|
|
30909
|
+
//
|
|
30910
|
+
cmd.affine = function(targetLayers, dataset, opts) {
|
|
30911
|
+
// Need to separate the targeted shapes from any other shapes that share
|
|
30912
|
+
// the same topology. So we duplicate any arcs that are shared by the targeted
|
|
30913
|
+
// shapes and their topological neighbors and remap arc references in the
|
|
30914
|
+
// neighbors to point to the copies.
|
|
30915
|
+
// TODO: explore alternative: if some arcs are shared between transformed and
|
|
30916
|
+
// non-transformed shapes, first remove topology, then tranform, then rebuild topology
|
|
30917
|
+
//
|
|
30918
|
+
var rotateArg = opts.rotate || 0;
|
|
30919
|
+
var scaleArg = opts.scale || 1;
|
|
30920
|
+
var shiftArg = opts.shift ? convertIntervalPair(opts.shift, getDatasetCRS(dataset)) : [0, 0];
|
|
30921
|
+
var arcs = dataset.arcs;
|
|
30922
|
+
var targetShapes = [];
|
|
30923
|
+
var otherShapes = [];
|
|
30924
|
+
var targetPoints = [];
|
|
30925
|
+
var targetFlags, otherFlags, transform;
|
|
30926
|
+
dataset.layers.filter(layerHasGeometry).forEach(function(lyr) {
|
|
30927
|
+
var hits = [],
|
|
30928
|
+
misses = [],
|
|
30929
|
+
test;
|
|
30930
|
+
if (targetLayers.indexOf(lyr) == -1) {
|
|
30931
|
+
misses = lyr.shapes;
|
|
30932
|
+
} else if (opts.where) {
|
|
30933
|
+
test = compileFeatureExpression(opts.where, lyr, dataset.arcs);
|
|
30934
|
+
lyr.shapes.forEach(function(shp, i) {
|
|
30935
|
+
(test(i) ? hits : misses).push(shp);
|
|
30936
|
+
});
|
|
30937
|
+
} else {
|
|
30938
|
+
hits = lyr.shapes;
|
|
30939
|
+
}
|
|
30940
|
+
if (lyr.geometry_type == 'point') {
|
|
30941
|
+
targetPoints = targetPoints.concat(hits);
|
|
30942
|
+
} else {
|
|
30943
|
+
targetShapes = targetShapes.concat(hits);
|
|
30944
|
+
otherShapes = otherShapes.concat(misses);
|
|
30945
|
+
}
|
|
30946
|
+
});
|
|
30947
|
+
var anchorArg = getAffineAnchor({arcs: dataset.arcs, layers: [{
|
|
30948
|
+
geometry_type: 'point', shapes: targetPoints}, {geometry_type: 'polyline',
|
|
30949
|
+
shapes: targetShapes}]}, opts);
|
|
30950
|
+
transform = getAffineTransform(rotateArg, scaleArg, shiftArg, anchorArg);
|
|
30951
|
+
if (opts.fit_bbox) {
|
|
30952
|
+
transform = getFitBoxTransform(opts.fit_bbox, targetPoints, targetShapes, arcs);
|
|
30953
|
+
}
|
|
30954
|
+
if (targetShapes.length > 0) {
|
|
30955
|
+
targetFlags = new Uint8Array(arcs.size());
|
|
30956
|
+
otherFlags = new Uint8Array(arcs.size());
|
|
30957
|
+
countArcsInShapes(targetShapes, targetFlags);
|
|
30958
|
+
if (otherShapes.length > 0) {
|
|
30959
|
+
countArcsInShapes(otherShapes, otherFlags);
|
|
30960
|
+
applyArrayMask(otherFlags, targetFlags);
|
|
30961
|
+
dataset.arcs = duplicateSelectedArcs(otherShapes, arcs, otherFlags);
|
|
30962
|
+
}
|
|
30963
|
+
dataset.arcs.transformPoints(function(x, y, arcId) {
|
|
30964
|
+
if (arcId < targetFlags.length && targetFlags[arcId] > 0) {
|
|
30965
|
+
return transform(x, y);
|
|
30966
|
+
}
|
|
30967
|
+
});
|
|
30968
|
+
}
|
|
30969
|
+
forEachPoint(targetPoints, function(p) {
|
|
30970
|
+
var p2 = transform(p[0], p[1]);
|
|
30971
|
+
p[0] = p2[0];
|
|
30972
|
+
p[1] = p2[1];
|
|
30973
|
+
});
|
|
30974
|
+
};
|
|
30975
|
+
|
|
30976
|
+
function getAffineAnchor(dataset, opts) {
|
|
30977
|
+
var anchor, bounds;
|
|
30978
|
+
if (opts.anchor) {
|
|
30979
|
+
anchor = opts.anchor;
|
|
30980
|
+
} else {
|
|
30981
|
+
// get bounds of selected shapes to calculate center of rotation/scale
|
|
30982
|
+
bounds = getDatasetBounds(dataset);
|
|
30983
|
+
anchor = [bounds.centerX(), bounds.centerY()];
|
|
30984
|
+
}
|
|
30985
|
+
return anchor;
|
|
30986
|
+
}
|
|
30987
|
+
|
|
30988
|
+
// TODO: handle problems with unprojected datasets
|
|
30989
|
+
// option 1: don't allow affine transformation of unprojected data
|
|
30990
|
+
// option 2: error if transformed data exceeds valid coordinate range
|
|
30991
|
+
// source: http://mathworld.wolfram.com/AffineTransformation.html
|
|
30992
|
+
function getAffineTransform(rotation, scale, shift, anchor) {
|
|
30993
|
+
var angle = rotation * Math.PI / 180;
|
|
30994
|
+
var a = scale * Math.cos(angle);
|
|
30995
|
+
var b = -scale * Math.sin(angle);
|
|
30996
|
+
return function(x, y) {
|
|
30997
|
+
var x2 = a * (x - anchor[0]) - b * (y - anchor[1]) + shift[0] + anchor[0];
|
|
30998
|
+
var y2 = b * (x - anchor[0]) + a * (y - anchor[1]) + shift[1] + anchor[1];
|
|
30999
|
+
return [x2, y2];
|
|
31000
|
+
};
|
|
31001
|
+
}
|
|
31002
|
+
|
|
31003
|
+
function getFitBoxTransform(bbox, points, shapes, arcs) {
|
|
31004
|
+
var dataset = {
|
|
31005
|
+
arcs: arcs,
|
|
31006
|
+
layers: []
|
|
31007
|
+
};
|
|
31008
|
+
if (points && points.length) {
|
|
31009
|
+
dataset.layers.push({
|
|
31010
|
+
geometry_type: 'point',
|
|
31011
|
+
shapes: points
|
|
31012
|
+
});
|
|
31013
|
+
}
|
|
31014
|
+
if (shapes && shapes.length) {
|
|
31015
|
+
dataset.layers.push({
|
|
31016
|
+
geometry_type: 'polyline',
|
|
31017
|
+
shapes: shapes
|
|
31018
|
+
});
|
|
31019
|
+
}
|
|
31020
|
+
var frame = calcFrameData(dataset, {fit_bbox: bbox});
|
|
31021
|
+
var fromBounds = new Bounds(frame.bbox);
|
|
31022
|
+
var toBounds = new Bounds(frame.bbox2);
|
|
31023
|
+
var fwd = fromBounds.getTransform(toBounds, false);
|
|
31024
|
+
return function(x, y) {
|
|
31025
|
+
return fwd.transform(x, y);
|
|
31026
|
+
};
|
|
31027
|
+
}
|
|
31028
|
+
|
|
31029
|
+
function applyArrayMask(destArr, maskArr) {
|
|
31030
|
+
for (var i=0, n=destArr.length; i<n; i++) {
|
|
31031
|
+
if (maskArr[i] === 0) destArr[i] = 0;
|
|
31032
|
+
}
|
|
31033
|
+
}
|
|
31034
|
+
|
|
31035
|
+
function duplicateSelectedArcs(shapes, arcs, flags) {
|
|
31036
|
+
var arcCount = 0;
|
|
31037
|
+
var vertexCount = 0;
|
|
31038
|
+
var data = arcs.getVertexData();
|
|
31039
|
+
var xx = [], yy = [], nn = [], map = [], n;
|
|
31040
|
+
for (var i=0, len=flags.length; i<len; i++) {
|
|
31041
|
+
if (flags[i] > 0) {
|
|
31042
|
+
map[i] = arcs.size() + arcCount;
|
|
31043
|
+
n = data.nn[i];
|
|
31044
|
+
utils.copyElements(data.xx, data.ii[i], xx, vertexCount, n);
|
|
31045
|
+
utils.copyElements(data.yy, data.ii[i], yy, vertexCount, n);
|
|
31046
|
+
nn.push(n);
|
|
31047
|
+
vertexCount += n;
|
|
31048
|
+
arcCount++;
|
|
31049
|
+
}
|
|
31050
|
+
}
|
|
31051
|
+
forEachArcId(shapes, function(id) {
|
|
31052
|
+
var absId = absArcId(id);
|
|
31053
|
+
if (flags[absId] > 0) {
|
|
31054
|
+
return id < 0 ? ~map[absId] : map[absId];
|
|
31055
|
+
}
|
|
31056
|
+
});
|
|
31057
|
+
return mergeArcs([arcs, new ArcCollection(nn, xx, yy)]);
|
|
31058
|
+
}
|
|
31059
|
+
|
|
29846
31060
|
const epsilon = 1.1102230246251565e-16;
|
|
29847
31061
|
const splitter = 134217729;
|
|
29848
31062
|
const resulterrbound = (3 + 8 * epsilon) * epsilon;
|
|
@@ -34876,7 +36090,7 @@ ${svg}
|
|
|
34876
36090
|
var ringTest = filterData.filter;
|
|
34877
36091
|
var removed = 0;
|
|
34878
36092
|
var pathFilter = function(path, i, paths) {
|
|
34879
|
-
if (ringTest(path)) {
|
|
36093
|
+
if (ringTest(path, i, paths)) {
|
|
34880
36094
|
removed++;
|
|
34881
36095
|
return null;
|
|
34882
36096
|
}
|
|
@@ -35016,6 +36230,7 @@ ${svg}
|
|
|
35016
36230
|
// or overlapping rings. TODO: try to optimize or remove it for all cases
|
|
35017
36231
|
|
|
35018
36232
|
// skipping shape cleanup when using the experimental fast bbox clipping option
|
|
36233
|
+
// if (!opts.bbox2 && !opts.no_cleanup) {
|
|
35019
36234
|
if (!opts.bbox2) {
|
|
35020
36235
|
// clean each target polygon by dissolving its rings
|
|
35021
36236
|
targetShapes = targetShapes.map(dissolvePolygon);
|
|
@@ -35512,7 +36727,7 @@ ${svg}
|
|
|
35512
36727
|
} else {
|
|
35513
36728
|
nodes = new NodeCollection(mergedDataset.arcs);
|
|
35514
36729
|
}
|
|
35515
|
-
|
|
36730
|
+
|
|
35516
36731
|
return clipLayersByLayer(targetLayers, clipLyr, nodes, type, opts);
|
|
35517
36732
|
}
|
|
35518
36733
|
|
|
@@ -40504,7 +41719,7 @@ ${svg}
|
|
|
40504
41719
|
|
|
40505
41720
|
function createMeridianPart(x, ymin, ymax) {
|
|
40506
41721
|
var coords = densifyPathByInterval([[x, ymin], [x, ymax]], precision);
|
|
40507
|
-
meridians.push(graticuleFeature(coords, {type: 'meridian', value: roundCoord$
|
|
41722
|
+
meridians.push(graticuleFeature(coords, {type: 'meridian', value: roundCoord$2(x)}));
|
|
40508
41723
|
}
|
|
40509
41724
|
|
|
40510
41725
|
function createParallel(y) {
|
|
@@ -40514,7 +41729,7 @@ ${svg}
|
|
|
40514
41729
|
}
|
|
40515
41730
|
|
|
40516
41731
|
// remove tiny offsets
|
|
40517
|
-
function roundCoord$
|
|
41732
|
+
function roundCoord$2(x) {
|
|
40518
41733
|
return +x.toFixed(3) || 0;
|
|
40519
41734
|
}
|
|
40520
41735
|
|
|
@@ -44613,7 +45828,7 @@ ${svg}
|
|
|
44613
45828
|
});
|
|
44614
45829
|
};
|
|
44615
45830
|
|
|
44616
|
-
var roundCoord = getRoundingFunction(0.01);
|
|
45831
|
+
var roundCoord$1 = getRoundingFunction(0.01);
|
|
44617
45832
|
|
|
44618
45833
|
function getSymbolFillColor(d) {
|
|
44619
45834
|
return d.fill || 'magenta';
|
|
@@ -44664,8 +45879,8 @@ ${svg}
|
|
|
44664
45879
|
|
|
44665
45880
|
function roundCoordsForSVG(coords) {
|
|
44666
45881
|
forEachSymbolCoord(coords, function(p) {
|
|
44667
|
-
p[0] = roundCoord(p[0]);
|
|
44668
|
-
p[1] = roundCoord(p[1]);
|
|
45882
|
+
p[0] = roundCoord$1(p[0]);
|
|
45883
|
+
p[1] = roundCoord$1(p[1]);
|
|
44669
45884
|
});
|
|
44670
45885
|
}
|
|
44671
45886
|
|
|
@@ -45390,7 +46605,7 @@ ${svg}
|
|
|
45390
46605
|
// Filter arcs based on an array of thresholds
|
|
45391
46606
|
function applyArcThresholds(arcs, thresholds) {
|
|
45392
46607
|
arcs.getVertexData().zz;
|
|
45393
|
-
arcs.forEach2(function(start, n, xx, yy, zz
|
|
46608
|
+
arcs.forEach2(function(arcId, start, n, xx, yy, zz) {
|
|
45394
46609
|
var arcZ = thresholds[arcId];
|
|
45395
46610
|
var z;
|
|
45396
46611
|
for (var i=1; i<n-1; i++) {
|
|
@@ -46044,7 +47259,7 @@ ${svg}
|
|
|
46044
47259
|
});
|
|
46045
47260
|
}
|
|
46046
47261
|
|
|
46047
|
-
var version = "0.6.
|
|
47262
|
+
var version = "0.6.110";
|
|
46048
47263
|
|
|
46049
47264
|
// Parse command line args into commands and run them
|
|
46050
47265
|
// Function takes an optional Node-style callback. A Promise is returned if no callback is given.
|
|
@@ -46376,6 +47591,51 @@ ${svg}
|
|
|
46376
47591
|
testCommands: testCommands
|
|
46377
47592
|
});
|
|
46378
47593
|
|
|
47594
|
+
var roundCoord = getRoundingFunction(0.01);
|
|
47595
|
+
|
|
47596
|
+
function stringifyVertex(p) {
|
|
47597
|
+
return ' ' + roundCoord(p[0]) + ' ' + roundCoord(p[1]);
|
|
47598
|
+
}
|
|
47599
|
+
|
|
47600
|
+
function isCubicCtrl(p) {
|
|
47601
|
+
return p.length > 2 && p[2] == 'C';
|
|
47602
|
+
}
|
|
47603
|
+
|
|
47604
|
+
function stringifyPolygonCoords(coords) {
|
|
47605
|
+
var parts = [];
|
|
47606
|
+
for (var i=0; i<coords.length; i++) {
|
|
47607
|
+
parts.push(stringifyLineStringCoords(coords[i]) + ' Z');
|
|
47608
|
+
}
|
|
47609
|
+
return parts.length > 0 ? parts.join(' ') : '';
|
|
47610
|
+
}
|
|
47611
|
+
|
|
47612
|
+
function stringifyLineStringCoords(coords) {
|
|
47613
|
+
if (coords.length === 0) return '';
|
|
47614
|
+
var d = 'M';
|
|
47615
|
+
var fromCurve = false;
|
|
47616
|
+
var p, i, n;
|
|
47617
|
+
for (i=0, n=coords.length; i<n; i++) {
|
|
47618
|
+
p = coords[i];
|
|
47619
|
+
if (isCubicCtrl(p)) {
|
|
47620
|
+
// TODO: add defensive check
|
|
47621
|
+
d += ' C' + stringifyVertex(p) + stringifyVertex(coords[++i]) + stringifyVertex(coords[++i]);
|
|
47622
|
+
fromCurve = true;
|
|
47623
|
+
} else if (fromCurve) {
|
|
47624
|
+
d += ' L' + stringifyVertex(p);
|
|
47625
|
+
fromCurve = false;
|
|
47626
|
+
} else {
|
|
47627
|
+
d += stringifyVertex(p);
|
|
47628
|
+
}
|
|
47629
|
+
}
|
|
47630
|
+
return d;
|
|
47631
|
+
}
|
|
47632
|
+
|
|
47633
|
+
var SvgPathUtils = /*#__PURE__*/Object.freeze({
|
|
47634
|
+
__proto__: null,
|
|
47635
|
+
stringifyLineStringCoords: stringifyLineStringCoords,
|
|
47636
|
+
stringifyPolygonCoords: stringifyPolygonCoords
|
|
47637
|
+
});
|
|
47638
|
+
|
|
46379
47639
|
// Return an array containing points from a path iterator, clipped to a bounding box
|
|
46380
47640
|
// Currently using this function for clipping styled polygons in the GUI to speed up layer rendering.
|
|
46381
47641
|
// Artifacts along the edges make this unsuitable for clipping datasets
|