mapshaper 0.6.108 → 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 +1782 -516
- package/package.json +2 -1
- 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 +200 -25
- package/www/mapshaper.js +1782 -516
- 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,41 +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
|
-
|
|
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);
|
|
3201
4196
|
}
|
|
4197
|
+
if (!p) return null;
|
|
3202
4198
|
|
|
3203
4199
|
// Snap p to a vertex if very close to one
|
|
3204
4200
|
// This avoids tiny segments caused by T-intersection overshoots and prevents
|
|
@@ -3214,9 +4210,98 @@
|
|
|
3214
4210
|
return p;
|
|
3215
4211
|
}
|
|
3216
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
|
+
|
|
3217
4300
|
function testEndpointHit(epsSq, ax, ay, bx, by, cx, cy, dx, dy) {
|
|
3218
|
-
return distanceSq(ax, ay, cx, cy) <= epsSq ||
|
|
3219
|
-
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;
|
|
3220
4305
|
}
|
|
3221
4306
|
|
|
3222
4307
|
function findPointSegTouches(epsSq, ax, ay, bx, by, cx, cy, dx, dy) {
|
|
@@ -3227,6 +4312,9 @@
|
|
|
3227
4312
|
collectPointSegTouch(touches, epsSq, dx, dy, ax, ay, bx, by);
|
|
3228
4313
|
if (touches.length === 0) return null;
|
|
3229
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))
|
|
3230
4318
|
// Geometrically, more than two touch intersections can not occur.
|
|
3231
4319
|
// Is it possible that fp rounding or a bug might result in >2 touches?
|
|
3232
4320
|
debug('Intersection detection error');
|
|
@@ -3243,10 +4331,10 @@
|
|
|
3243
4331
|
var pa = distanceSq(ax, ay, px, py);
|
|
3244
4332
|
var pb = distanceSq(bx, by, px, py);
|
|
3245
4333
|
if (pa <= epsSq || pb <= epsSq) return; // ignore endpoint hits
|
|
4334
|
+
// console.log("Dist:", Math.sqrt(pab), "eps:", Math.sqrt(epsSq), "p:", px, py)
|
|
3246
4335
|
arr.push(px, py); // T intersection at P and AB
|
|
3247
4336
|
}
|
|
3248
4337
|
|
|
3249
|
-
|
|
3250
4338
|
// Used by mapshaper-undershoots.js
|
|
3251
4339
|
// TODO: make more robust, make sure result is compatible with segmentIntersection()
|
|
3252
4340
|
// (rounding errors currently must be handled downstream)
|
|
@@ -3295,16 +4383,55 @@
|
|
|
3295
4383
|
// when a segment is vertical or horizontal. This has caused problems when
|
|
3296
4384
|
// repeatedly applying bbox clipping along the same segment
|
|
3297
4385
|
var x = p[0],
|
|
3298
|
-
y = p[1]
|
|
4386
|
+
y = p[1],
|
|
4387
|
+
s1out = false,
|
|
4388
|
+
s2out = false;
|
|
4389
|
+
|
|
3299
4390
|
// assumes that segment ranges intersect
|
|
3300
|
-
|
|
3301
|
-
|
|
3302
|
-
|
|
3303
|
-
|
|
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
|
+
}
|
|
3304
4418
|
p[0] = x;
|
|
3305
4419
|
p[1] = y;
|
|
3306
4420
|
}
|
|
3307
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
|
+
|
|
3308
4435
|
// a: coordinate of point
|
|
3309
4436
|
// b: endpoint coordinate of segment
|
|
3310
4437
|
// c: other endpoint of segment
|
|
@@ -3320,16 +4447,13 @@
|
|
|
3320
4447
|
return out;
|
|
3321
4448
|
}
|
|
3322
4449
|
|
|
3323
|
-
function
|
|
3324
|
-
var lim;
|
|
3325
|
-
|
|
3326
|
-
|
|
3327
|
-
|
|
3328
|
-
debug("[clampToCloseRange()] large clamping interval", a, b, c);
|
|
3329
|
-
}
|
|
3330
|
-
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);
|
|
3331
4455
|
}
|
|
3332
|
-
return
|
|
4456
|
+
return lim;
|
|
3333
4457
|
}
|
|
3334
4458
|
|
|
3335
4459
|
// Determinant of matrix
|
|
@@ -3339,6 +4463,10 @@
|
|
|
3339
4463
|
return a * d - b * c;
|
|
3340
4464
|
}
|
|
3341
4465
|
|
|
4466
|
+
function determinant2D_big(a, b, c, d) {
|
|
4467
|
+
return a.times(d).minus(b.times(c));
|
|
4468
|
+
}
|
|
4469
|
+
|
|
3342
4470
|
// returns a positive value if the points a, b, and c are arranged in
|
|
3343
4471
|
// counterclockwise order, a negative value if the points are in clockwise
|
|
3344
4472
|
// order, and zero if the points are collinear.
|
|
@@ -3347,16 +4475,51 @@
|
|
|
3347
4475
|
return determinant2D(ax - cx, ay - cy, bx - cx, by - cy);
|
|
3348
4476
|
}
|
|
3349
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
|
+
|
|
3350
4494
|
// Source: Sedgewick, _Algorithms in C_
|
|
3351
4495
|
// (Other functions were tried that were more sensitive to floating point errors
|
|
3352
4496
|
// than this function)
|
|
3353
|
-
function
|
|
4497
|
+
function segmentHit_fast(ax, ay, bx, by, cx, cy, dx, dy) {
|
|
3354
4498
|
return orient2D(ax, ay, bx, by, cx, cy) *
|
|
3355
4499
|
orient2D(ax, ay, bx, by, dx, dy) <= 0 &&
|
|
3356
4500
|
orient2D(cx, cy, dx, dy, ax, ay) *
|
|
3357
4501
|
orient2D(cx, cy, dx, dy, bx, by) <= 0;
|
|
3358
4502
|
}
|
|
3359
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
|
+
|
|
3360
4523
|
// Useful for determining if a segment that intersects another segment is
|
|
3361
4524
|
// entering or leaving an enclosed buffer area
|
|
3362
4525
|
// returns -1 if angle of p1p2 -> p3p4 is counter-clockwise (left turn)
|
|
@@ -3381,7 +4544,11 @@
|
|
|
3381
4544
|
__proto__: null,
|
|
3382
4545
|
findClosestPointOnSeg: findClosestPointOnSeg,
|
|
3383
4546
|
orient2D: orient2D,
|
|
3384
|
-
|
|
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,
|
|
3385
4552
|
segmentIntersection: segmentIntersection,
|
|
3386
4553
|
segmentTurn: segmentTurn
|
|
3387
4554
|
});
|
|
@@ -3691,7 +4858,7 @@
|
|
|
3691
4858
|
// editPart: callback function
|
|
3692
4859
|
function editShapes(shapes, editPart) {
|
|
3693
4860
|
for (var i=0, n=shapes.length; i<n; i++) {
|
|
3694
|
-
shapes[i] = editShapeParts(shapes[i], editPart);
|
|
4861
|
+
shapes[i] = editShapeParts(shapes[i], editPart, i);
|
|
3695
4862
|
}
|
|
3696
4863
|
}
|
|
3697
4864
|
|
|
@@ -3699,8 +4866,9 @@
|
|
|
3699
4866
|
// @cb: function(part, i, parts)
|
|
3700
4867
|
// If @cb returns an array, it replaces the existing value
|
|
3701
4868
|
// If @cb returns null, the path is removed from the feature
|
|
4869
|
+
// @shpId: (optional) id of shape
|
|
3702
4870
|
//
|
|
3703
|
-
function editShapeParts(parts, cb) {
|
|
4871
|
+
function editShapeParts(parts, cb, shpId) {
|
|
3704
4872
|
if (!parts) return null; // null geometry not edited
|
|
3705
4873
|
if (!utils.isArray(parts)) error("Expected an array, received:", parts);
|
|
3706
4874
|
var nulls = 0,
|
|
@@ -3708,7 +4876,7 @@
|
|
|
3708
4876
|
retn;
|
|
3709
4877
|
|
|
3710
4878
|
for (var i=0; i<n; i++) {
|
|
3711
|
-
retn = cb(parts[i], i, parts);
|
|
4879
|
+
retn = cb(parts[i], i, parts, shpId);
|
|
3712
4880
|
if (retn === null) {
|
|
3713
4881
|
nulls++;
|
|
3714
4882
|
parts[i] = null;
|
|
@@ -5755,7 +6923,7 @@
|
|
|
5755
6923
|
//
|
|
5756
6924
|
this.forEach2 = function(cb) {
|
|
5757
6925
|
for (var arcId=0, n=this.size(); arcId<n; arcId++) {
|
|
5758
|
-
cb(_ii[arcId], _nn[arcId], _xx, _yy, _zz
|
|
6926
|
+
cb(arcId, _ii[arcId], _nn[arcId], _xx, _yy, _zz);
|
|
5759
6927
|
}
|
|
5760
6928
|
};
|
|
5761
6929
|
|
|
@@ -5890,6 +7058,27 @@
|
|
|
5890
7058
|
};
|
|
5891
7059
|
|
|
5892
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) {
|
|
5893
7082
|
var iter = this.getArcIter(arcId);
|
|
5894
7083
|
var i = 0,
|
|
5895
7084
|
x, y;
|
|
@@ -6396,10 +7585,10 @@
|
|
|
6396
7585
|
// Visit each point in the path, up to but not including the last point
|
|
6397
7586
|
for (var i = start; i < end; i++) {
|
|
6398
7587
|
if (pointIsArcEndpoint(i)) {
|
|
6399
|
-
if (firstNodeId
|
|
6400
|
-
arcIds.push(addEdge(arcStartId, i));
|
|
6401
|
-
} else {
|
|
7588
|
+
if (firstNodeId == -1) {
|
|
6402
7589
|
firstNodeId = i;
|
|
7590
|
+
} else {
|
|
7591
|
+
arcIds.push(addEdge(arcStartId, i));
|
|
6403
7592
|
}
|
|
6404
7593
|
arcStartId = i;
|
|
6405
7594
|
}
|
|
@@ -6990,7 +8179,7 @@
|
|
|
6990
8179
|
yy2 = new Float64Array(n),
|
|
6991
8180
|
ids2 = new Int32Array(n);
|
|
6992
8181
|
|
|
6993
|
-
arcs.forEach2(function(i, n, xx, yy, zz
|
|
8182
|
+
arcs.forEach2(function(arcId, i, n, xx, yy, zz) {
|
|
6994
8183
|
var start = i,
|
|
6995
8184
|
end = i + n - 1,
|
|
6996
8185
|
start2 = arcId * 2,
|
|
@@ -7041,7 +8230,7 @@
|
|
|
7041
8230
|
layers.forEach(function(lyr) {
|
|
7042
8231
|
// modify copies of the original shapes; original shapes should be unmodified
|
|
7043
8232
|
// (need to test this)
|
|
7044
|
-
lyr.shapes = lyr.shapes.map(function(shape) {
|
|
8233
|
+
lyr.shapes = lyr.shapes.map(function(shape, i) {
|
|
7045
8234
|
return editShapeParts(shape && shape.concat(), translatePath);
|
|
7046
8235
|
});
|
|
7047
8236
|
});
|
|
@@ -12583,11 +13772,161 @@
|
|
|
12583
13772
|
stringifyAsNDJSON: stringifyAsNDJSON
|
|
12584
13773
|
});
|
|
12585
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
|
+
|
|
12586
13915
|
function isValidArc(arcId, arcs) {
|
|
12587
13916
|
// check for arcs with no vertices
|
|
12588
13917
|
// TODO: also check for other kinds of degenerate arcs
|
|
12589
13918
|
// (e.g. collapsed arcs consisting of identical points)
|
|
12590
|
-
|
|
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;
|
|
12591
13930
|
}
|
|
12592
13931
|
|
|
12593
13932
|
// Return id of rightmost connected arc in relation to @fromArcId
|
|
@@ -12624,6 +13963,7 @@
|
|
|
12624
13963
|
candId = ids[j];
|
|
12625
13964
|
if (!isValidArc(candId, arcs)) {
|
|
12626
13965
|
// skip empty arcs
|
|
13966
|
+
debug('skipping one arc:', candId, 'out of:', ids.length, ids);
|
|
12627
13967
|
continue;
|
|
12628
13968
|
}
|
|
12629
13969
|
icand = arcs.indexOfVertex(candId, -2);
|
|
@@ -12634,6 +13974,10 @@
|
|
|
12634
13974
|
continue;
|
|
12635
13975
|
}
|
|
12636
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
|
+
}
|
|
12637
13981
|
if (code == 2) {
|
|
12638
13982
|
ito = icand;
|
|
12639
13983
|
toArcId = candId;
|
|
@@ -12643,26 +13987,22 @@
|
|
|
12643
13987
|
|
|
12644
13988
|
if (toArcId == fromArcId) {
|
|
12645
13989
|
// This shouldn't occur, assuming that other arcs are present
|
|
12646
|
-
error("Pathfinder error");
|
|
13990
|
+
error("Pathfinder error", toArcId, fromArcId);
|
|
12647
13991
|
}
|
|
12648
13992
|
return toArcId;
|
|
12649
13993
|
}
|
|
12650
13994
|
|
|
12651
|
-
// TODO: consider using simpler internal.chooseRighthandPath2()
|
|
12652
13995
|
// Returns 1 if node->a, return 2 if node->b, else return 0
|
|
12653
13996
|
// TODO: better handling of identical angles (better -- avoid creating them)
|
|
12654
13997
|
function chooseRighthandPath(fromX, fromY, nodeX, nodeY, ax, ay, bx, by) {
|
|
12655
13998
|
var angleA = geom.signedAngle(fromX, fromY, nodeX, nodeY, ax, ay);
|
|
12656
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;
|
|
12657
14003
|
var code;
|
|
12658
14004
|
if (angleA <= 0 || angleB <= 0) {
|
|
12659
14005
|
debug("[chooseRighthandPath()] 0 angle(s):", angleA, angleB);
|
|
12660
|
-
if (angleA <= 0) {
|
|
12661
|
-
debug(' A orient2D:', geom.orient2D(fromX, fromY, nodeX, nodeY, ax, ay));
|
|
12662
|
-
}
|
|
12663
|
-
if (angleB <= 0) {
|
|
12664
|
-
debug(' B orient2D:', geom.orient2D(fromX, fromY, nodeX, nodeY, bx, by));
|
|
12665
|
-
}
|
|
12666
14006
|
// TODO: test against "from" segment
|
|
12667
14007
|
if (angleA > 0) {
|
|
12668
14008
|
code = 1;
|
|
@@ -12671,23 +14011,24 @@
|
|
|
12671
14011
|
} else {
|
|
12672
14012
|
code = 0;
|
|
12673
14013
|
}
|
|
12674
|
-
} else if (angleA < angleB) {
|
|
14014
|
+
} else if (angleA < angleB - smallAngle) {
|
|
12675
14015
|
code = 1;
|
|
12676
|
-
} else if (angleB < angleA) {
|
|
14016
|
+
} else if (angleB < angleA - smallAngle) {
|
|
12677
14017
|
code = 2;
|
|
12678
14018
|
} else if (isNaN(angleA) || isNaN(angleB)) {
|
|
12679
14019
|
// probably a duplicate point, which should not occur
|
|
12680
14020
|
error('Invalid node geometry');
|
|
12681
14021
|
} else {
|
|
12682
|
-
//
|
|
12683
|
-
code =
|
|
12684
|
-
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);
|
|
12685
14025
|
}
|
|
12686
14026
|
return code;
|
|
12687
14027
|
}
|
|
12688
14028
|
|
|
12689
|
-
function
|
|
12690
|
-
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);
|
|
12691
14032
|
var code;
|
|
12692
14033
|
if (orient > 0) {
|
|
12693
14034
|
code = 2;
|
|
@@ -12701,7 +14042,7 @@
|
|
|
12701
14042
|
|
|
12702
14043
|
var PathfinderUtils = /*#__PURE__*/Object.freeze({
|
|
12703
14044
|
__proto__: null,
|
|
12704
|
-
|
|
14045
|
+
chooseBetweenClosePaths: chooseBetweenClosePaths,
|
|
12705
14046
|
getRightmostArc: getRightmostArc
|
|
12706
14047
|
});
|
|
12707
14048
|
|
|
@@ -13226,7 +14567,7 @@
|
|
|
13226
14567
|
}
|
|
13227
14568
|
|
|
13228
14569
|
// test two candidate segments for intersection
|
|
13229
|
-
hit =
|
|
14570
|
+
hit = segmentIntersection(s1p1x, s1p1y, s1p2x, s1p2y,
|
|
13230
14571
|
s2p1x, s2p1y, s2p2x, s2p2y, tolerance);
|
|
13231
14572
|
if (hit) {
|
|
13232
14573
|
seg1 = [s1p1, s1p2];
|
|
@@ -13284,146 +14625,6 @@
|
|
|
13284
14625
|
sortIntersections: sortIntersections
|
|
13285
14626
|
});
|
|
13286
14627
|
|
|
13287
|
-
function findNearestVertices(p, shp, arcs) {
|
|
13288
|
-
var p2 = findNearestVertex(p[0], p[1], shp, arcs);
|
|
13289
|
-
return findVertexIds(p2.x, p2.y, arcs);
|
|
13290
|
-
}
|
|
13291
|
-
|
|
13292
|
-
function snapVerticesToPoint(ids, p, arcs) {
|
|
13293
|
-
var data = arcs.getVertexData();
|
|
13294
|
-
ids.forEach(function(idx) {
|
|
13295
|
-
setVertexCoords(p[0], p[1], idx, arcs);
|
|
13296
|
-
arcs.updateArcBounds(findArcIdFromVertexId(idx, data.ii));
|
|
13297
|
-
});
|
|
13298
|
-
}
|
|
13299
|
-
|
|
13300
|
-
|
|
13301
|
-
// p: point to snap
|
|
13302
|
-
// ids: ids of nearby vertices, possibly including an arc endpoint
|
|
13303
|
-
function snapPointToArcEndpoint(p, ids, arcs) {
|
|
13304
|
-
var p2, dx, dy;
|
|
13305
|
-
ids.forEach(function(idx) {
|
|
13306
|
-
if (vertexIsArcStart(idx, arcs)) {
|
|
13307
|
-
p2 = getVertexCoords(idx + 1, arcs);
|
|
13308
|
-
} else if (vertexIsArcEnd(idx, arcs)) {
|
|
13309
|
-
p2 = getVertexCoords(idx - 1, arcs);
|
|
13310
|
-
}
|
|
13311
|
-
});
|
|
13312
|
-
if (!p2) return;
|
|
13313
|
-
dx = p2[0] - p[0];
|
|
13314
|
-
dy = p2[1] - p[1];
|
|
13315
|
-
if (Math.abs(dx) > Math.abs(dy)) {
|
|
13316
|
-
p[1] = p2[1]; // snap y coord
|
|
13317
|
-
} else {
|
|
13318
|
-
p[0] = p2[0];
|
|
13319
|
-
}
|
|
13320
|
-
}
|
|
13321
|
-
|
|
13322
|
-
// Find ids of vertices with identical coordinates to x,y in an ArcCollection
|
|
13323
|
-
// Caveat: does not exclude vertices that are not visible at the
|
|
13324
|
-
// current level of simplification.
|
|
13325
|
-
function findVertexIds(x, y, arcs) {
|
|
13326
|
-
var data = arcs.getVertexData(),
|
|
13327
|
-
xx = data.xx,
|
|
13328
|
-
yy = data.yy,
|
|
13329
|
-
ids = [];
|
|
13330
|
-
for (var i=0, n=xx.length; i<n; i++) {
|
|
13331
|
-
if (xx[i] == x && yy[i] == y) ids.push(i);
|
|
13332
|
-
}
|
|
13333
|
-
return ids;
|
|
13334
|
-
}
|
|
13335
|
-
|
|
13336
|
-
function getVertexCoords(i, arcs) {
|
|
13337
|
-
var data = arcs.getVertexData();
|
|
13338
|
-
return [data.xx[i], data.yy[i]];
|
|
13339
|
-
}
|
|
13340
|
-
|
|
13341
|
-
function vertexIsArcEnd(idx, arcs) {
|
|
13342
|
-
// Test whether the vertex at index @idx is the endpoint of an arc
|
|
13343
|
-
var data = arcs.getVertexData(),
|
|
13344
|
-
ii = data.ii,
|
|
13345
|
-
nn = data.nn;
|
|
13346
|
-
for (var j=0, n=ii.length; j<n; j++) {
|
|
13347
|
-
if (idx === ii[j] + nn[j] - 1) return true;
|
|
13348
|
-
}
|
|
13349
|
-
return false;
|
|
13350
|
-
}
|
|
13351
|
-
|
|
13352
|
-
function vertexIsArcEndpoint(idx, arcs) {
|
|
13353
|
-
return vertexIsArcStart(idx, arcs) || vertexIsArcEnd(idx, arcs);
|
|
13354
|
-
}
|
|
13355
|
-
|
|
13356
|
-
function vertexIsArcStart(idx, arcs) {
|
|
13357
|
-
var ii = arcs.getVertexData().ii;
|
|
13358
|
-
for (var j=0, n=ii.length; j<n; j++) {
|
|
13359
|
-
if (idx === ii[j]) return true;
|
|
13360
|
-
}
|
|
13361
|
-
return false;
|
|
13362
|
-
}
|
|
13363
|
-
|
|
13364
|
-
function getArcStartCoords(arcId, arcs) {
|
|
13365
|
-
var coords = getArcEndpointCoords(arcId, arcs);
|
|
13366
|
-
return coords[0];
|
|
13367
|
-
}
|
|
13368
|
-
|
|
13369
|
-
function getArcEndCoords(arcId, arcs) {
|
|
13370
|
-
var coords = getArcEndpointCoords(arcId, arcs);
|
|
13371
|
-
return coords[1];
|
|
13372
|
-
}
|
|
13373
|
-
|
|
13374
|
-
function getArcEndpointCoords(arcId, arcs) {
|
|
13375
|
-
if (arcId < 0) {
|
|
13376
|
-
return getArcEndpointCoords(~arcId, arcs).reverse();
|
|
13377
|
-
}
|
|
13378
|
-
var data = arcs.getVertexData();
|
|
13379
|
-
var i = data.ii[arcId];
|
|
13380
|
-
var n = data.nn[arcId];
|
|
13381
|
-
var a = [data.xx[i], data.yy[i]];
|
|
13382
|
-
var b = [data.xx[i + n - 1], data.yy[i + n - 1]];
|
|
13383
|
-
return [a, b];
|
|
13384
|
-
}
|
|
13385
|
-
|
|
13386
|
-
function setVertexCoords(x, y, i, arcs) {
|
|
13387
|
-
var data = arcs.getVertexData();
|
|
13388
|
-
data.xx[i] = x;
|
|
13389
|
-
data.yy[i] = y;
|
|
13390
|
-
}
|
|
13391
|
-
|
|
13392
|
-
function findNearestVertex(x, y, shp, arcs, spherical) {
|
|
13393
|
-
var calcLen = spherical ? geom.greatCircleDistance : geom.distance2D,
|
|
13394
|
-
minLen = Infinity,
|
|
13395
|
-
minX, minY, dist, iter;
|
|
13396
|
-
for (var i=0; i<shp.length; i++) {
|
|
13397
|
-
iter = arcs.getShapeIter(shp[i]);
|
|
13398
|
-
while (iter.hasNext()) {
|
|
13399
|
-
dist = calcLen(x, y, iter.x, iter.y);
|
|
13400
|
-
if (dist < minLen) {
|
|
13401
|
-
minLen = dist;
|
|
13402
|
-
minX = iter.x;
|
|
13403
|
-
minY = iter.y;
|
|
13404
|
-
}
|
|
13405
|
-
}
|
|
13406
|
-
}
|
|
13407
|
-
return minLen < Infinity ? {x: minX, y: minY} : null;
|
|
13408
|
-
}
|
|
13409
|
-
|
|
13410
|
-
var VertexUtils = /*#__PURE__*/Object.freeze({
|
|
13411
|
-
__proto__: null,
|
|
13412
|
-
findNearestVertex: findNearestVertex,
|
|
13413
|
-
findNearestVertices: findNearestVertices,
|
|
13414
|
-
findVertexIds: findVertexIds,
|
|
13415
|
-
getArcEndCoords: getArcEndCoords,
|
|
13416
|
-
getArcEndpointCoords: getArcEndpointCoords,
|
|
13417
|
-
getArcStartCoords: getArcStartCoords,
|
|
13418
|
-
getVertexCoords: getVertexCoords,
|
|
13419
|
-
setVertexCoords: setVertexCoords,
|
|
13420
|
-
snapPointToArcEndpoint: snapPointToArcEndpoint,
|
|
13421
|
-
snapVerticesToPoint: snapVerticesToPoint,
|
|
13422
|
-
vertexIsArcEnd: vertexIsArcEnd,
|
|
13423
|
-
vertexIsArcEndpoint: vertexIsArcEndpoint,
|
|
13424
|
-
vertexIsArcStart: vertexIsArcStart
|
|
13425
|
-
});
|
|
13426
|
-
|
|
13427
14628
|
// arcs: ArcCollection containing original coordinates
|
|
13428
14629
|
function getRepairFunction(arcs) {
|
|
13429
14630
|
var arcsOrig = arcs.getCopy();
|
|
@@ -13855,6 +15056,9 @@
|
|
|
13855
15056
|
getSliverTest(dataset.arcs, threshold, sliverControl) :
|
|
13856
15057
|
getMinAreaTest(threshold, dataset);
|
|
13857
15058
|
var label = getSliverLabel(getAreaLabel(threshold, crs), sliverControl > 0);
|
|
15059
|
+
if (opts.keep_shapes) {
|
|
15060
|
+
filter = keepShapes(filter);
|
|
15061
|
+
}
|
|
13858
15062
|
return {
|
|
13859
15063
|
threshold: threshold,
|
|
13860
15064
|
filter: filter,
|
|
@@ -13862,6 +15066,23 @@
|
|
|
13862
15066
|
};
|
|
13863
15067
|
}
|
|
13864
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
|
+
|
|
13865
15086
|
function getSliverLabel(areaStr, variable) {
|
|
13866
15087
|
if (variable) {
|
|
13867
15088
|
areaStr = areaStr.replace(' ', '+ ') + ' variable';
|
|
@@ -16790,6 +18011,7 @@
|
|
|
16790
18011
|
arcs.flatten();
|
|
16791
18012
|
|
|
16792
18013
|
var changed = snapAndCut(dataset, snapDist);
|
|
18014
|
+
|
|
16793
18015
|
// Detect topology again if coordinates have changed
|
|
16794
18016
|
if (changed || opts.rebuild_topology) {
|
|
16795
18017
|
buildTopology(dataset);
|
|
@@ -16811,9 +18033,10 @@
|
|
|
16811
18033
|
|
|
16812
18034
|
function snapAndCut(dataset, snapDist) {
|
|
16813
18035
|
var arcs = dataset.arcs;
|
|
16814
|
-
var cutOpts = snapDist > 0 ? {} : {tolerance: 0};
|
|
18036
|
+
var cutOpts = snapDist > 0 ? {tolerance: snapDist} : {tolerance: 0};
|
|
16815
18037
|
var coordsHaveChanged = false;
|
|
16816
18038
|
var snapCount, dupeCount, cutCount;
|
|
18039
|
+
var maxPasses = 4, passCount = 0;
|
|
16817
18040
|
snapCount = snapCoordsByInterval(arcs, snapDist);
|
|
16818
18041
|
dupeCount = arcs.dedupCoords();
|
|
16819
18042
|
|
|
@@ -16825,22 +18048,28 @@
|
|
|
16825
18048
|
|
|
16826
18049
|
// cut arcs at points where segments intersect
|
|
16827
18050
|
cutCount = cutPathsAtIntersections(dataset, cutOpts);
|
|
18051
|
+
passCount++;
|
|
16828
18052
|
if (cutCount > 0 || snapCount > 0 || dupeCount > 0) {
|
|
16829
18053
|
coordsHaveChanged = true;
|
|
16830
18054
|
}
|
|
18055
|
+
|
|
16831
18056
|
// perform a second snap + cut pass if needed
|
|
16832
|
-
|
|
16833
|
-
|
|
18057
|
+
while (cutCount > 0 && passCount < maxPasses) {
|
|
18058
|
+
passCount++;
|
|
16834
18059
|
snapCount = snapCoordsByInterval(arcs, snapDist);
|
|
16835
|
-
arcs.dedupCoords();
|
|
16836
|
-
|
|
18060
|
+
dupeCount = arcs.dedupCoords();
|
|
18061
|
+
// cutCount = 0;
|
|
18062
|
+
if (snapCount > 0 || cutCount > 0) {
|
|
16837
18063
|
cutCount = cutPathsAtIntersections(dataset, cutOpts);
|
|
16838
|
-
|
|
16839
|
-
if (cutCount > 0) {
|
|
16840
|
-
arcs.dedupCoords(); // need to do this here?
|
|
16841
|
-
debug('Second-pass vertices added:', cutCount, 'consider third pass?');
|
|
18064
|
+
debug("[snapAndCut] pass:", passCount, "snaps:", snapCount, "dupes:", dupeCount, "cuts:", cutCount);
|
|
16842
18065
|
}
|
|
16843
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
|
+
|
|
16844
18073
|
return coordsHaveChanged;
|
|
16845
18074
|
}
|
|
16846
18075
|
|
|
@@ -16876,17 +18105,6 @@
|
|
|
16876
18105
|
}
|
|
16877
18106
|
}
|
|
16878
18107
|
|
|
16879
|
-
// Divides a collection of arcs at points where arc paths cross each other
|
|
16880
|
-
// Returns array for remapping arc ids
|
|
16881
|
-
function divideArcs(arcs, opts) {
|
|
16882
|
-
var points = findClippingPoints(arcs, opts);
|
|
16883
|
-
// TODO: avoid the following if no points need to be added
|
|
16884
|
-
var map = insertCutPoints(points, arcs);
|
|
16885
|
-
// segment-point intersections currently create duplicate points
|
|
16886
|
-
// TODO: consider dedup in a later cleanup pass?
|
|
16887
|
-
// arcs.dedupCoords();
|
|
16888
|
-
return map;
|
|
16889
|
-
}
|
|
16890
18108
|
|
|
16891
18109
|
function cutPathsAtIntersections(dataset, opts) {
|
|
16892
18110
|
var n = dataset.arcs.getPointCount();
|
|
@@ -16905,6 +18123,25 @@
|
|
|
16905
18123
|
});
|
|
16906
18124
|
}
|
|
16907
18125
|
|
|
18126
|
+
|
|
18127
|
+
// Divides a collection of arcs at points where arc paths cross each other
|
|
18128
|
+
// Returns array for remapping arc ids
|
|
18129
|
+
function divideArcs(arcs, opts) {
|
|
18130
|
+
var points = findClippingPoints(arcs, opts);
|
|
18131
|
+
// TODO: avoid the following if no points need to be added
|
|
18132
|
+
var map = insertCutPoints(points, arcs);
|
|
18133
|
+
// segment-point intersections currently create duplicate points
|
|
18134
|
+
// TODO: consider dedup in a later cleanup pass?
|
|
18135
|
+
// arcs.dedupCoords();
|
|
18136
|
+
return map;
|
|
18137
|
+
}
|
|
18138
|
+
|
|
18139
|
+
function findClippingPoints(arcs, opts) {
|
|
18140
|
+
var intersections = findSegmentIntersections(arcs, opts),
|
|
18141
|
+
data = arcs.getVertexData();
|
|
18142
|
+
return convertIntersectionsToCutPoints(intersections, data.xx, data.yy);
|
|
18143
|
+
}
|
|
18144
|
+
|
|
16908
18145
|
// Inserts array of cutting points into an ArcCollection
|
|
16909
18146
|
// Returns array for remapping arc ids
|
|
16910
18147
|
function insertCutPoints(unfilteredPoints, arcs) {
|
|
@@ -17017,32 +18254,26 @@
|
|
|
17017
18254
|
function filterSortedCutPoints(points, arcs) {
|
|
17018
18255
|
var filtered = [],
|
|
17019
18256
|
pointId = 0;
|
|
17020
|
-
arcs.forEach2(function(i, n, xx, yy) {
|
|
17021
|
-
var j = i + n - 1,
|
|
17022
|
-
|
|
17023
|
-
|
|
17024
|
-
|
|
17025
|
-
|
|
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],
|
|
17026
18263
|
p, pp;
|
|
17027
18264
|
|
|
17028
18265
|
while (pointId < points.length && points[pointId].i <= j) {
|
|
17029
18266
|
p = points[pointId];
|
|
18267
|
+
pointId++;
|
|
17030
18268
|
pp = filtered[filtered.length - 1]; // previous point
|
|
17031
|
-
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 {
|
|
17032
18270
|
filtered.push(p);
|
|
17033
18271
|
}
|
|
17034
|
-
pointId++;
|
|
17035
18272
|
}
|
|
17036
18273
|
});
|
|
17037
18274
|
return filtered;
|
|
17038
18275
|
}
|
|
17039
18276
|
|
|
17040
|
-
function findClippingPoints(arcs, opts) {
|
|
17041
|
-
var intersections = findSegmentIntersections(arcs, opts),
|
|
17042
|
-
data = arcs.getVertexData();
|
|
17043
|
-
return convertIntersectionsToCutPoints(intersections, data.xx, data.yy);
|
|
17044
|
-
}
|
|
17045
|
-
|
|
17046
18277
|
var IntersectionCuts = /*#__PURE__*/Object.freeze({
|
|
17047
18278
|
__proto__: null,
|
|
17048
18279
|
addIntersectionCuts: addIntersectionCuts,
|
|
@@ -18772,203 +20003,6 @@
|
|
|
18772
20003
|
catalog.getDatasets().push(o);
|
|
18773
20004
|
}
|
|
18774
20005
|
|
|
18775
|
-
// Apply rotation, scale and/or shift to some or all of the features in a dataset
|
|
18776
|
-
//
|
|
18777
|
-
cmd.affine = function(targetLayers, dataset, opts) {
|
|
18778
|
-
// Need to separate the targeted shapes from any other shapes that share
|
|
18779
|
-
// the same topology. So we duplicate any arcs that are shared by the targeted
|
|
18780
|
-
// shapes and their topological neighbors and remap arc references in the
|
|
18781
|
-
// neighbors to point to the copies.
|
|
18782
|
-
// TODO: explore alternative: if some arcs are shared between transformed and
|
|
18783
|
-
// non-transformed shapes, first remove topology, then tranform, then rebuild topology
|
|
18784
|
-
//
|
|
18785
|
-
var rotateArg = opts.rotate || 0;
|
|
18786
|
-
var scaleArg = opts.scale || 1;
|
|
18787
|
-
var shiftArg = opts.shift ? convertIntervalPair(opts.shift, getDatasetCRS(dataset)) : [0, 0];
|
|
18788
|
-
var arcs = dataset.arcs;
|
|
18789
|
-
var targetShapes = [];
|
|
18790
|
-
var otherShapes = [];
|
|
18791
|
-
var targetPoints = [];
|
|
18792
|
-
var targetFlags, otherFlags, transform;
|
|
18793
|
-
dataset.layers.filter(layerHasGeometry).forEach(function(lyr) {
|
|
18794
|
-
var hits = [],
|
|
18795
|
-
misses = [],
|
|
18796
|
-
test;
|
|
18797
|
-
if (targetLayers.indexOf(lyr) == -1) {
|
|
18798
|
-
misses = lyr.shapes;
|
|
18799
|
-
} else if (opts.where) {
|
|
18800
|
-
test = compileFeatureExpression(opts.where, lyr, dataset.arcs);
|
|
18801
|
-
lyr.shapes.forEach(function(shp, i) {
|
|
18802
|
-
(test(i) ? hits : misses).push(shp);
|
|
18803
|
-
});
|
|
18804
|
-
} else {
|
|
18805
|
-
hits = lyr.shapes;
|
|
18806
|
-
}
|
|
18807
|
-
if (lyr.geometry_type == 'point') {
|
|
18808
|
-
targetPoints = targetPoints.concat(hits);
|
|
18809
|
-
} else {
|
|
18810
|
-
targetShapes = targetShapes.concat(hits);
|
|
18811
|
-
otherShapes = otherShapes.concat(misses);
|
|
18812
|
-
}
|
|
18813
|
-
});
|
|
18814
|
-
var anchorArg = getAffineAnchor({arcs: dataset.arcs, layers: [{
|
|
18815
|
-
geometry_type: 'point', shapes: targetPoints}, {geometry_type: 'polyline',
|
|
18816
|
-
shapes: targetShapes}]}, opts);
|
|
18817
|
-
transform = getAffineTransform(rotateArg, scaleArg, shiftArg, anchorArg);
|
|
18818
|
-
if (opts.fit_bbox) {
|
|
18819
|
-
transform = getFitBoxTransform(opts.fit_bbox, targetPoints, targetShapes, arcs);
|
|
18820
|
-
}
|
|
18821
|
-
if (targetShapes.length > 0) {
|
|
18822
|
-
targetFlags = new Uint8Array(arcs.size());
|
|
18823
|
-
otherFlags = new Uint8Array(arcs.size());
|
|
18824
|
-
countArcsInShapes(targetShapes, targetFlags);
|
|
18825
|
-
if (otherShapes.length > 0) {
|
|
18826
|
-
countArcsInShapes(otherShapes, otherFlags);
|
|
18827
|
-
applyArrayMask(otherFlags, targetFlags);
|
|
18828
|
-
dataset.arcs = duplicateSelectedArcs(otherShapes, arcs, otherFlags);
|
|
18829
|
-
}
|
|
18830
|
-
dataset.arcs.transformPoints(function(x, y, arcId) {
|
|
18831
|
-
if (arcId < targetFlags.length && targetFlags[arcId] > 0) {
|
|
18832
|
-
return transform(x, y);
|
|
18833
|
-
}
|
|
18834
|
-
});
|
|
18835
|
-
}
|
|
18836
|
-
forEachPoint(targetPoints, function(p) {
|
|
18837
|
-
var p2 = transform(p[0], p[1]);
|
|
18838
|
-
p[0] = p2[0];
|
|
18839
|
-
p[1] = p2[1];
|
|
18840
|
-
});
|
|
18841
|
-
};
|
|
18842
|
-
|
|
18843
|
-
function getAffineAnchor(dataset, opts) {
|
|
18844
|
-
var anchor, bounds;
|
|
18845
|
-
if (opts.anchor) {
|
|
18846
|
-
anchor = opts.anchor;
|
|
18847
|
-
} else {
|
|
18848
|
-
// get bounds of selected shapes to calculate center of rotation/scale
|
|
18849
|
-
bounds = getDatasetBounds(dataset);
|
|
18850
|
-
anchor = [bounds.centerX(), bounds.centerY()];
|
|
18851
|
-
}
|
|
18852
|
-
return anchor;
|
|
18853
|
-
}
|
|
18854
|
-
|
|
18855
|
-
// TODO: handle problems with unprojected datasets
|
|
18856
|
-
// option 1: don't allow affine transformation of unprojected data
|
|
18857
|
-
// option 2: error if transformed data exceeds valid coordinate range
|
|
18858
|
-
// source: http://mathworld.wolfram.com/AffineTransformation.html
|
|
18859
|
-
function getAffineTransform(rotation, scale, shift, anchor) {
|
|
18860
|
-
var angle = rotation * Math.PI / 180;
|
|
18861
|
-
var a = scale * Math.cos(angle);
|
|
18862
|
-
var b = -scale * Math.sin(angle);
|
|
18863
|
-
return function(x, y) {
|
|
18864
|
-
var x2 = a * (x - anchor[0]) - b * (y - anchor[1]) + shift[0] + anchor[0];
|
|
18865
|
-
var y2 = b * (x - anchor[0]) + a * (y - anchor[1]) + shift[1] + anchor[1];
|
|
18866
|
-
return [x2, y2];
|
|
18867
|
-
};
|
|
18868
|
-
}
|
|
18869
|
-
|
|
18870
|
-
function getFitBoxTransform(bbox, points, shapes, arcs) {
|
|
18871
|
-
var dataset = {
|
|
18872
|
-
arcs: arcs,
|
|
18873
|
-
layers: []
|
|
18874
|
-
};
|
|
18875
|
-
if (points && points.length) {
|
|
18876
|
-
dataset.layers.push({
|
|
18877
|
-
geometry_type: 'point',
|
|
18878
|
-
shapes: points
|
|
18879
|
-
});
|
|
18880
|
-
}
|
|
18881
|
-
if (shapes && shapes.length) {
|
|
18882
|
-
dataset.layers.push({
|
|
18883
|
-
geometry_type: 'polyline',
|
|
18884
|
-
shapes: shapes
|
|
18885
|
-
});
|
|
18886
|
-
}
|
|
18887
|
-
var frame = calcFrameData(dataset, {fit_bbox: bbox});
|
|
18888
|
-
var fromBounds = new Bounds(frame.bbox);
|
|
18889
|
-
var toBounds = new Bounds(frame.bbox2);
|
|
18890
|
-
var fwd = fromBounds.getTransform(toBounds, false);
|
|
18891
|
-
return function(x, y) {
|
|
18892
|
-
return fwd.transform(x, y);
|
|
18893
|
-
};
|
|
18894
|
-
}
|
|
18895
|
-
|
|
18896
|
-
function applyArrayMask(destArr, maskArr) {
|
|
18897
|
-
for (var i=0, n=destArr.length; i<n; i++) {
|
|
18898
|
-
if (maskArr[i] === 0) destArr[i] = 0;
|
|
18899
|
-
}
|
|
18900
|
-
}
|
|
18901
|
-
|
|
18902
|
-
function duplicateSelectedArcs(shapes, arcs, flags) {
|
|
18903
|
-
var arcCount = 0;
|
|
18904
|
-
var vertexCount = 0;
|
|
18905
|
-
var data = arcs.getVertexData();
|
|
18906
|
-
var xx = [], yy = [], nn = [], map = [], n;
|
|
18907
|
-
for (var i=0, len=flags.length; i<len; i++) {
|
|
18908
|
-
if (flags[i] > 0) {
|
|
18909
|
-
map[i] = arcs.size() + arcCount;
|
|
18910
|
-
n = data.nn[i];
|
|
18911
|
-
utils.copyElements(data.xx, data.ii[i], xx, vertexCount, n);
|
|
18912
|
-
utils.copyElements(data.yy, data.ii[i], yy, vertexCount, n);
|
|
18913
|
-
nn.push(n);
|
|
18914
|
-
vertexCount += n;
|
|
18915
|
-
arcCount++;
|
|
18916
|
-
}
|
|
18917
|
-
}
|
|
18918
|
-
forEachArcId(shapes, function(id) {
|
|
18919
|
-
var absId = absArcId(id);
|
|
18920
|
-
if (flags[absId] > 0) {
|
|
18921
|
-
return id < 0 ? ~map[absId] : map[absId];
|
|
18922
|
-
}
|
|
18923
|
-
});
|
|
18924
|
-
return mergeArcs([arcs, new ArcCollection(nn, xx, yy)]);
|
|
18925
|
-
}
|
|
18926
|
-
|
|
18927
|
-
var roundCoord$2 = getRoundingFunction(0.01);
|
|
18928
|
-
|
|
18929
|
-
function stringifyVertex(p) {
|
|
18930
|
-
return ' ' + roundCoord$2(p[0]) + ' ' + roundCoord$2(p[1]);
|
|
18931
|
-
}
|
|
18932
|
-
|
|
18933
|
-
function isCubicCtrl(p) {
|
|
18934
|
-
return p.length > 2 && p[2] == 'C';
|
|
18935
|
-
}
|
|
18936
|
-
|
|
18937
|
-
function stringifyPolygonCoords(coords) {
|
|
18938
|
-
var parts = [];
|
|
18939
|
-
for (var i=0; i<coords.length; i++) {
|
|
18940
|
-
parts.push(stringifyLineStringCoords(coords[i]) + ' Z');
|
|
18941
|
-
}
|
|
18942
|
-
return parts.length > 0 ? parts.join(' ') : '';
|
|
18943
|
-
}
|
|
18944
|
-
|
|
18945
|
-
function stringifyLineStringCoords(coords) {
|
|
18946
|
-
if (coords.length === 0) return '';
|
|
18947
|
-
var d = 'M';
|
|
18948
|
-
var fromCurve = false;
|
|
18949
|
-
var p, i, n;
|
|
18950
|
-
for (i=0, n=coords.length; i<n; i++) {
|
|
18951
|
-
p = coords[i];
|
|
18952
|
-
if (isCubicCtrl(p)) {
|
|
18953
|
-
// TODO: add defensive check
|
|
18954
|
-
d += ' C' + stringifyVertex(p) + stringifyVertex(coords[++i]) + stringifyVertex(coords[++i]);
|
|
18955
|
-
fromCurve = true;
|
|
18956
|
-
} else if (fromCurve) {
|
|
18957
|
-
d += ' L' + stringifyVertex(p);
|
|
18958
|
-
fromCurve = false;
|
|
18959
|
-
} else {
|
|
18960
|
-
d += stringifyVertex(p);
|
|
18961
|
-
}
|
|
18962
|
-
}
|
|
18963
|
-
return d;
|
|
18964
|
-
}
|
|
18965
|
-
|
|
18966
|
-
var SvgPathUtils = /*#__PURE__*/Object.freeze({
|
|
18967
|
-
__proto__: null,
|
|
18968
|
-
stringifyLineStringCoords: stringifyLineStringCoords,
|
|
18969
|
-
stringifyPolygonCoords: stringifyPolygonCoords
|
|
18970
|
-
});
|
|
18971
|
-
|
|
18972
20006
|
/* example patterns
|
|
18973
20007
|
hatches 1px black 1px red 1px white
|
|
18974
20008
|
1px black 1px red 1px white // same as above (hatches is default)
|
|
@@ -19557,7 +20591,7 @@
|
|
|
19557
20591
|
}
|
|
19558
20592
|
|
|
19559
20593
|
function importLineString(coords) {
|
|
19560
|
-
var d = stringifyLineStringCoords(coords);
|
|
20594
|
+
var d = stringifyLineStringCoords$1(coords);
|
|
19561
20595
|
return {
|
|
19562
20596
|
tag: 'path',
|
|
19563
20597
|
properties: {d: d}
|
|
@@ -19565,7 +20599,7 @@
|
|
|
19565
20599
|
}
|
|
19566
20600
|
|
|
19567
20601
|
function importMultiLineString(coords) {
|
|
19568
|
-
var d = coords.map(stringifyLineStringCoords).join(' ');
|
|
20602
|
+
var d = coords.map(stringifyLineStringCoords$1).join(' ');
|
|
19569
20603
|
return {
|
|
19570
20604
|
tag: 'path',
|
|
19571
20605
|
properties: {d: d}
|
|
@@ -19587,7 +20621,7 @@
|
|
|
19587
20621
|
var o = {
|
|
19588
20622
|
tag: 'path',
|
|
19589
20623
|
properties: {
|
|
19590
|
-
d: stringifyPolygonCoords(coords)
|
|
20624
|
+
d: stringifyPolygonCoords$1(coords)
|
|
19591
20625
|
}
|
|
19592
20626
|
};
|
|
19593
20627
|
if (coords.length > 1) {
|
|
@@ -19596,6 +20630,23 @@
|
|
|
19596
20630
|
return o;
|
|
19597
20631
|
}
|
|
19598
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
|
+
|
|
19599
20650
|
var GeojsonToSvg = /*#__PURE__*/Object.freeze({
|
|
19600
20651
|
__proto__: null,
|
|
19601
20652
|
flattenMultiPolygonCoords: flattenMultiPolygonCoords,
|
|
@@ -20485,7 +21536,7 @@
|
|
|
20485
21536
|
opts = Object.assign({invert_y: true, margin: "1"}, opts);
|
|
20486
21537
|
frame = getFrameData(dataset, opts);
|
|
20487
21538
|
fitDatasetToFrame(dataset, frame);
|
|
20488
|
-
setCoordinatePrecision(dataset, opts.precision || 0.
|
|
21539
|
+
setCoordinatePrecision(dataset, opts.precision || 0.01);
|
|
20489
21540
|
|
|
20490
21541
|
// error if one or more svg_data fields are not present in any layers
|
|
20491
21542
|
if (opts.svg_data) validateSvgDataFields(dataset.layers, opts.svg_data);
|
|
@@ -22241,7 +23292,7 @@ ${svg}
|
|
|
22241
23292
|
if (opts.presimplify) {
|
|
22242
23293
|
fromZ = getPresimplifyFunction(bounds.width());
|
|
22243
23294
|
}
|
|
22244
|
-
arcs.forEach2(function(i, n, xx, yy, zz) {
|
|
23295
|
+
arcs.forEach2(function(arcId, i, n, xx, yy, zz) {
|
|
22245
23296
|
var arc = [], p;
|
|
22246
23297
|
for (var j=i + n; i<j; i++) {
|
|
22247
23298
|
p = [xx[i], yy[i]];
|
|
@@ -24837,6 +25888,11 @@ ${svg}
|
|
|
24837
25888
|
.option('sliver-control', sliverControlOpt)
|
|
24838
25889
|
.option('snap-interval', snapIntervalOpt)
|
|
24839
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
|
+
// })
|
|
24840
25896
|
.option('allow-overlaps', {
|
|
24841
25897
|
describe: 'allow polygons to overlap (disables gap fill)',
|
|
24842
25898
|
type: 'flag'
|
|
@@ -24888,6 +25944,10 @@ ${svg}
|
|
|
24888
25944
|
})
|
|
24889
25945
|
.option('name', nameOpt)
|
|
24890
25946
|
.option('no-snap', noSnapOpt)
|
|
25947
|
+
.option('no-cleanup', {
|
|
25948
|
+
// for debugging - no documentation
|
|
25949
|
+
type: 'flag'
|
|
25950
|
+
})
|
|
24891
25951
|
.option('target', targetOpt)
|
|
24892
25952
|
.option('no-replace', noReplaceOpt);
|
|
24893
25953
|
|
|
@@ -25123,6 +26183,10 @@ ${svg}
|
|
|
25123
26183
|
.option('bbox', bboxOpt)
|
|
25124
26184
|
.option('name', nameOpt)
|
|
25125
26185
|
.option('no-snap', noSnapOpt)
|
|
26186
|
+
.option('no-cleanup', {
|
|
26187
|
+
// for debugging - no documentation
|
|
26188
|
+
type: 'flag'
|
|
26189
|
+
})
|
|
25126
26190
|
.option('target', targetOpt)
|
|
25127
26191
|
.option('no-replace', noReplaceOpt);
|
|
25128
26192
|
|
|
@@ -25233,6 +26297,10 @@ ${svg}
|
|
|
25233
26297
|
type: 'flag',
|
|
25234
26298
|
describe: 'delete features with null geometry'
|
|
25235
26299
|
})
|
|
26300
|
+
.option('keep-shapes', {
|
|
26301
|
+
type: 'flag',
|
|
26302
|
+
describe: 'protect sliver polygons from complete removal'
|
|
26303
|
+
})
|
|
25236
26304
|
.option('target', targetOpt);
|
|
25237
26305
|
|
|
25238
26306
|
parser.command('graticule')
|
|
@@ -25440,6 +26508,8 @@ ${svg}
|
|
|
25440
26508
|
.option('calc', calcOpt)
|
|
25441
26509
|
.option('name', nameOpt)
|
|
25442
26510
|
.option('target', targetOpt)
|
|
26511
|
+
.option('snap-interval', snapIntervalOpt)
|
|
26512
|
+
.option('no-snap', noSnapOpt)
|
|
25443
26513
|
.option('no-replace', noReplaceOpt);
|
|
25444
26514
|
|
|
25445
26515
|
parser.command('point-grid')
|
|
@@ -29835,6 +30905,158 @@ ${svg}
|
|
|
29835
30905
|
stop('Unable to import coordinates');
|
|
29836
30906
|
}
|
|
29837
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
|
+
|
|
29838
31060
|
const epsilon = 1.1102230246251565e-16;
|
|
29839
31061
|
const splitter = 134217729;
|
|
29840
31062
|
const resulterrbound = (3 + 8 * epsilon) * epsilon;
|
|
@@ -30095,8 +31317,6 @@ ${svg}
|
|
|
30095
31317
|
const detright = (ax - cx) * (by - cy);
|
|
30096
31318
|
const det = detleft - detright;
|
|
30097
31319
|
|
|
30098
|
-
if (detleft === 0 || detright === 0 || (detleft > 0) !== (detright > 0)) return det;
|
|
30099
|
-
|
|
30100
31320
|
const detsum = Math.abs(detleft + detright);
|
|
30101
31321
|
if (Math.abs(det) >= ccwerrboundA * detsum) return det;
|
|
30102
31322
|
|
|
@@ -34870,7 +36090,7 @@ ${svg}
|
|
|
34870
36090
|
var ringTest = filterData.filter;
|
|
34871
36091
|
var removed = 0;
|
|
34872
36092
|
var pathFilter = function(path, i, paths) {
|
|
34873
|
-
if (ringTest(path)) {
|
|
36093
|
+
if (ringTest(path, i, paths)) {
|
|
34874
36094
|
removed++;
|
|
34875
36095
|
return null;
|
|
34876
36096
|
}
|
|
@@ -35010,6 +36230,7 @@ ${svg}
|
|
|
35010
36230
|
// or overlapping rings. TODO: try to optimize or remove it for all cases
|
|
35011
36231
|
|
|
35012
36232
|
// skipping shape cleanup when using the experimental fast bbox clipping option
|
|
36233
|
+
// if (!opts.bbox2 && !opts.no_cleanup) {
|
|
35013
36234
|
if (!opts.bbox2) {
|
|
35014
36235
|
// clean each target polygon by dissolving its rings
|
|
35015
36236
|
targetShapes = targetShapes.map(dissolvePolygon);
|
|
@@ -35506,7 +36727,7 @@ ${svg}
|
|
|
35506
36727
|
} else {
|
|
35507
36728
|
nodes = new NodeCollection(mergedDataset.arcs);
|
|
35508
36729
|
}
|
|
35509
|
-
|
|
36730
|
+
|
|
35510
36731
|
return clipLayersByLayer(targetLayers, clipLyr, nodes, type, opts);
|
|
35511
36732
|
}
|
|
35512
36733
|
|
|
@@ -40498,7 +41719,7 @@ ${svg}
|
|
|
40498
41719
|
|
|
40499
41720
|
function createMeridianPart(x, ymin, ymax) {
|
|
40500
41721
|
var coords = densifyPathByInterval([[x, ymin], [x, ymax]], precision);
|
|
40501
|
-
meridians.push(graticuleFeature(coords, {type: 'meridian', value: roundCoord$
|
|
41722
|
+
meridians.push(graticuleFeature(coords, {type: 'meridian', value: roundCoord$2(x)}));
|
|
40502
41723
|
}
|
|
40503
41724
|
|
|
40504
41725
|
function createParallel(y) {
|
|
@@ -40508,7 +41729,7 @@ ${svg}
|
|
|
40508
41729
|
}
|
|
40509
41730
|
|
|
40510
41731
|
// remove tiny offsets
|
|
40511
|
-
function roundCoord$
|
|
41732
|
+
function roundCoord$2(x) {
|
|
40512
41733
|
return +x.toFixed(3) || 0;
|
|
40513
41734
|
}
|
|
40514
41735
|
|
|
@@ -44607,7 +45828,7 @@ ${svg}
|
|
|
44607
45828
|
});
|
|
44608
45829
|
};
|
|
44609
45830
|
|
|
44610
|
-
var roundCoord = getRoundingFunction(0.01);
|
|
45831
|
+
var roundCoord$1 = getRoundingFunction(0.01);
|
|
44611
45832
|
|
|
44612
45833
|
function getSymbolFillColor(d) {
|
|
44613
45834
|
return d.fill || 'magenta';
|
|
@@ -44658,8 +45879,8 @@ ${svg}
|
|
|
44658
45879
|
|
|
44659
45880
|
function roundCoordsForSVG(coords) {
|
|
44660
45881
|
forEachSymbolCoord(coords, function(p) {
|
|
44661
|
-
p[0] = roundCoord(p[0]);
|
|
44662
|
-
p[1] = roundCoord(p[1]);
|
|
45882
|
+
p[0] = roundCoord$1(p[0]);
|
|
45883
|
+
p[1] = roundCoord$1(p[1]);
|
|
44663
45884
|
});
|
|
44664
45885
|
}
|
|
44665
45886
|
|
|
@@ -45384,7 +46605,7 @@ ${svg}
|
|
|
45384
46605
|
// Filter arcs based on an array of thresholds
|
|
45385
46606
|
function applyArcThresholds(arcs, thresholds) {
|
|
45386
46607
|
arcs.getVertexData().zz;
|
|
45387
|
-
arcs.forEach2(function(start, n, xx, yy, zz
|
|
46608
|
+
arcs.forEach2(function(arcId, start, n, xx, yy, zz) {
|
|
45388
46609
|
var arcZ = thresholds[arcId];
|
|
45389
46610
|
var z;
|
|
45390
46611
|
for (var i=1; i<n-1; i++) {
|
|
@@ -46038,7 +47259,7 @@ ${svg}
|
|
|
46038
47259
|
});
|
|
46039
47260
|
}
|
|
46040
47261
|
|
|
46041
|
-
var version = "0.6.
|
|
47262
|
+
var version = "0.6.110";
|
|
46042
47263
|
|
|
46043
47264
|
// Parse command line args into commands and run them
|
|
46044
47265
|
// Function takes an optional Node-style callback. A Promise is returned if no callback is given.
|
|
@@ -46370,6 +47591,51 @@ ${svg}
|
|
|
46370
47591
|
testCommands: testCommands
|
|
46371
47592
|
});
|
|
46372
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
|
+
|
|
46373
47639
|
// Return an array containing points from a path iterator, clipped to a bounding box
|
|
46374
47640
|
// Currently using this function for clipping styled polygons in the GUI to speed up layer rendering.
|
|
46375
47641
|
// Artifacts along the edges make this unsuitable for clipping datasets
|