mapshaper 0.7.30 → 0.7.32
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/mapshaper.js +106 -15
- package/package.json +1 -1
- package/www/mapshaper.js +106 -15
package/mapshaper.js
CHANGED
|
@@ -32938,6 +32938,10 @@ ${svg}
|
|
|
32938
32938
|
describe: 'shrinkage-correction (default 1; 0 = none, >1 exaggerates bends)',
|
|
32939
32939
|
type: 'number'
|
|
32940
32940
|
})
|
|
32941
|
+
.option('max-bend-angle', {
|
|
32942
|
+
describe: 'max bend between output segments in degrees (default is 8)',
|
|
32943
|
+
type: 'number'
|
|
32944
|
+
})
|
|
32941
32945
|
.option('planar', {
|
|
32942
32946
|
// describe: 'smooth decimal degree coords in 2D space (default is spherical)',
|
|
32943
32947
|
type: 'flag'
|
|
@@ -57238,7 +57242,16 @@ ${svg}
|
|
|
57238
57242
|
// distance D) segments the line into runs that each fit within the detail
|
|
57239
57243
|
// scale and bounds every candidate chord to <= D, so cuts stay local.
|
|
57240
57244
|
//
|
|
57241
|
-
// 2.
|
|
57245
|
+
// 2. MERGE survivors that hide a slicing chord. A spike with long bare flanks
|
|
57246
|
+
// parks its base vertices as survivors (each flank chord alone exceeds D), so
|
|
57247
|
+
// the short chord that closes the excursion sits between non-adjacent
|
|
57248
|
+
// survivors. Where a near-degenerate closing chord (a needle returning close
|
|
57249
|
+
// to its base: chord <= MERGE_CHORD_FRACTION * D, tortuosity >= threshold)
|
|
57250
|
+
// exists within an arc-length window, widen the run so the next phase can
|
|
57251
|
+
// slice it. Restricting to short closing chords keeps the merge from sweeping
|
|
57252
|
+
// a wide excursion across neighbouring geometry and introducing a crossing.
|
|
57253
|
+
//
|
|
57254
|
+
// 3. COMMIT selectively. For each run of removed vertices between two survivors,
|
|
57242
57255
|
// compare the original sub-path length to the chord across it (tortuosity).
|
|
57243
57256
|
// Collapse the run to its chord only when it is convoluted (tortuosity >=
|
|
57244
57257
|
// threshold) -- a jetty, fjord or crinkle. Otherwise restore the run's
|
|
@@ -57256,6 +57269,16 @@ ${svg}
|
|
|
57256
57269
|
// topology nodes stay put and the operation is topology-safe like -simplify.
|
|
57257
57270
|
var DEFAULT_WEIGHTING = 0.7;
|
|
57258
57271
|
var DEFAULT_TORTUOSITY = 2;
|
|
57272
|
+
// How far (in detail-distances of arc length) the survivor-merge pass looks ahead
|
|
57273
|
+
// for a chord that closes a convoluted excursion. Bounds the pass to O(n) and
|
|
57274
|
+
// caps how long a thin spike it can slice in one merge.
|
|
57275
|
+
var MERGE_WINDOW_FACTOR = 12;
|
|
57276
|
+
// The survivor-merge pass only fires when the closing chord is this fraction of D
|
|
57277
|
+
// or shorter: a near-degenerate needle that returns close to its base can be
|
|
57278
|
+
// sliced safely, but collapsing a wider excursion (chord approaching D) sweeps a
|
|
57279
|
+
// long span across neighbouring geometry and can introduce a crossing. cutRun,
|
|
57280
|
+
// which spans only adjacent survivors, is not constrained this way.
|
|
57281
|
+
var MERGE_CHORD_FRACTION = 0.5;
|
|
57259
57282
|
|
|
57260
57283
|
function collapseArcDetail(xx, yy, opts) {
|
|
57261
57284
|
var n = xx.length;
|
|
@@ -57378,6 +57401,47 @@ ${svg}
|
|
|
57378
57401
|
}
|
|
57379
57402
|
}
|
|
57380
57403
|
|
|
57404
|
+
// Survivor-merge pass. A thin spike with long bare flanks (e.g. a fjord wall or
|
|
57405
|
+
// a dredged channel) forces its base vertices to be parked as survivors -- each
|
|
57406
|
+
// flank chord on its own exceeds D -- so the short chord that actually closes
|
|
57407
|
+
// the excursion is hidden between *non-adjacent* survivors and the per-run
|
|
57408
|
+
// cutRun above never sees it. Walk the survivor chain and, where a later
|
|
57409
|
+
// survivor closes a convoluted excursion with a short chord (<=
|
|
57410
|
+
// MERGE_CHORD_FRACTION * D, tortuosity >= T) within an arc-length window,
|
|
57411
|
+
// splice out the spanned survivors so the run boundary widens and cutRun slices
|
|
57412
|
+
// the spike off. Restricting to short closing chords keeps the merge from
|
|
57413
|
+
// sweeping a wide excursion across neighbouring geometry. This reuses cumLen and the
|
|
57414
|
+
// peel's linked list; it does not re-run the Visvalingam peel. Only non-adjacent
|
|
57415
|
+
// convoluted survivors are merged, so gentle runs (and all existing behaviour)
|
|
57416
|
+
// are untouched.
|
|
57417
|
+
var window = MERGE_WINDOW_FACTOR * D;
|
|
57418
|
+
var mergeChordSq = Dsq * MERGE_CHORD_FRACTION * MERGE_CHORD_FRACTION;
|
|
57419
|
+
var s = 0;
|
|
57420
|
+
while (s !== n - 1) {
|
|
57421
|
+
var mergeJ = -1;
|
|
57422
|
+
var mergeTort = T;
|
|
57423
|
+
var u = next[s];
|
|
57424
|
+
while (cumLen[u] - cumLen[s] <= window) {
|
|
57425
|
+
if (chordSq(s, u) <= mergeChordSq) {
|
|
57426
|
+
var ud = dist(s, u);
|
|
57427
|
+
var utort = ud > 0 ? (cumLen[u] - cumLen[s]) / ud : Infinity;
|
|
57428
|
+
if (utort > mergeTort) {
|
|
57429
|
+
mergeTort = utort;
|
|
57430
|
+
mergeJ = u;
|
|
57431
|
+
}
|
|
57432
|
+
}
|
|
57433
|
+
if (u === n - 1) break;
|
|
57434
|
+
u = next[u];
|
|
57435
|
+
}
|
|
57436
|
+
if (mergeJ > next[s]) {
|
|
57437
|
+
next[s] = mergeJ;
|
|
57438
|
+
prev[mergeJ] = s;
|
|
57439
|
+
s = mergeJ;
|
|
57440
|
+
} else {
|
|
57441
|
+
s = next[s];
|
|
57442
|
+
}
|
|
57443
|
+
}
|
|
57444
|
+
|
|
57381
57445
|
outX.push(xx[0]);
|
|
57382
57446
|
outY.push(yy[0]);
|
|
57383
57447
|
var a = 0;
|
|
@@ -62527,20 +62591,25 @@ ${svg}
|
|
|
62527
62591
|
// chord * accumulatedTurn / 8 estimates the bow of a circular arc, and we also
|
|
62528
62592
|
// cut when it exceeds a fraction of the tolerance. Both tests are O(1) per dense
|
|
62529
62593
|
// vertex, so the pass stays O(n).
|
|
62530
|
-
var DENSE_STEP_FACTOR = 0.033; // dense sampling step = tolerance * this
|
|
62531
|
-
// resolve the sharpest
|
|
62532
|
-
// ~ the kernel scale)
|
|
62533
|
-
// angle filter can reach
|
|
62534
|
-
//
|
|
62535
|
-
// kept well under
|
|
62536
|
-
//
|
|
62537
|
-
|
|
62594
|
+
var DENSE_STEP_FACTOR = 0.033; // dense sampling step = tolerance * this at the
|
|
62595
|
+
// default bend angle. Must resolve the sharpest
|
|
62596
|
+
// smoothed feature (radius ~ the kernel scale)
|
|
62597
|
+
// finely enough that the angle filter can reach
|
|
62598
|
+
// the bend-angle joins: one dense segment turns
|
|
62599
|
+
// ~ this/0.4 radians there, kept well under the
|
|
62600
|
+
// bend angle so the discrete accumulation barely
|
|
62601
|
+
// overshoots the threshold. For a smaller-than-
|
|
62602
|
+
// default bend angle the step is refined in
|
|
62603
|
+
// proportion so the threshold stays reachable.
|
|
62604
|
+
var DEFAULT_BEND_ANGLE = 8 * Math.PI / 180; // keep a vertex after this much accumulated
|
|
62605
|
+
// turn (max turn between consecutive output
|
|
62606
|
+
// segments); user-overridable via max-bend-angle
|
|
62538
62607
|
var DEVIATION_FACTOR = 0.1; // sagitta guard: also cut a gentle bend that bows
|
|
62539
62608
|
// more than tolerance * this from its chord
|
|
62540
62609
|
|
|
62541
62610
|
// Smooth a single arc's coordinates.
|
|
62542
62611
|
// @xx, @yy: coordinate arrays (may be typed-array subarrays) for one arc.
|
|
62543
|
-
// @opts: {tolerance, method, spherical, closed, keepCorners}
|
|
62612
|
+
// @opts: {tolerance, method, spherical, closed, keepCorners, gain, maxBendAngle}
|
|
62544
62613
|
// Returns {xx: [], yy: []} with the smoothed coordinates. Endpoints of open
|
|
62545
62614
|
// arcs are preserved exactly (so shared topology nodes stay put); closed arcs
|
|
62546
62615
|
// are smoothed cyclically and returned closed (first point repeated at the end).
|
|
@@ -62555,6 +62624,16 @@ ${svg}
|
|
|
62555
62624
|
return g >= 0 ? g : 0;
|
|
62556
62625
|
}
|
|
62557
62626
|
|
|
62627
|
+
// Resolve the output bend-angle threshold from the user option (in degrees) to
|
|
62628
|
+
// radians. It caps the turn between consecutive output segments: a larger angle
|
|
62629
|
+
// keeps fewer vertices (coarser joins), a smaller one keeps more (smoother joins).
|
|
62630
|
+
// Non-positive or missing values fall back to the default.
|
|
62631
|
+
function resolveBendAngle(opts) {
|
|
62632
|
+
var deg = opts.maxBendAngle;
|
|
62633
|
+
if (deg === undefined || deg === null || !(deg > 0)) return DEFAULT_BEND_ANGLE;
|
|
62634
|
+
return deg * Math.PI / 180;
|
|
62635
|
+
}
|
|
62636
|
+
|
|
62558
62637
|
function smoothArcCoords(xx, yy, opts) {
|
|
62559
62638
|
var n = xx.length;
|
|
62560
62639
|
var origX = toArray(xx);
|
|
@@ -62567,12 +62646,18 @@ ${svg}
|
|
|
62567
62646
|
var closed = !!opts.closed;
|
|
62568
62647
|
var spherical = !!opts.spherical;
|
|
62569
62648
|
var keepCorners = !!opts.keepCorners;
|
|
62649
|
+
var bendAngle = resolveBendAngle(opts);
|
|
62570
62650
|
var ctx = {
|
|
62571
62651
|
tol: tol,
|
|
62572
62652
|
method: method,
|
|
62573
62653
|
spherical: spherical,
|
|
62574
62654
|
keepCorners: keepCorners,
|
|
62575
62655
|
gain: resolveGain(opts),
|
|
62656
|
+
bendAngle: bendAngle,
|
|
62657
|
+
// Refine the dense step for a smaller-than-default bend angle so one dense
|
|
62658
|
+
// segment still turns well under the threshold; never coarsen it beyond the
|
|
62659
|
+
// default (the angle filter alone thins the output for larger angles).
|
|
62660
|
+
denseStep: tol * DENSE_STEP_FACTOR * Math.min(1, bendAngle / DEFAULT_BEND_ANGLE),
|
|
62576
62661
|
radius: tol * WINDOW_RADIUS_FACTOR,
|
|
62577
62662
|
scale: (method == 'gaussian' ? GAUSSIAN_SIGMA_FACTOR : PAEK_SCALE_FACTOR) * tol
|
|
62578
62663
|
};
|
|
@@ -62804,7 +62889,7 @@ ${svg}
|
|
|
62804
62889
|
// "Output resampling" note above). Step 1 evaluates the smoother at a uniform
|
|
62805
62890
|
// dense step; step 2 makes one forward pass keeping the endpoints plus every
|
|
62806
62891
|
// interior vertex where the accumulated turn since the last kept vertex reaches
|
|
62807
|
-
//
|
|
62892
|
+
// the bend-angle threshold, or where the sagitta guard trips. @inputCount bounds the dense
|
|
62808
62893
|
// sampling (and thus the output). Returns one array per channel, ordered by
|
|
62809
62894
|
// increasing arc length, including both endpoints.
|
|
62810
62895
|
function sampleSmoothedCurve(src, a, b, closed, ctx, inputCount) {
|
|
@@ -62813,7 +62898,7 @@ ${svg}
|
|
|
62813
62898
|
var maxPoints = Math.max(inputCount, MIN_CLOSED_SEGMENTS) * MAX_OUTPUT_FACTOR;
|
|
62814
62899
|
|
|
62815
62900
|
// 1. dense uniform sampling of the smoothed curve
|
|
62816
|
-
var nDense = Math.ceil(span /
|
|
62901
|
+
var nDense = Math.ceil(span / ctx.denseStep) + 1;
|
|
62817
62902
|
if (nDense < MIN_CLOSED_SEGMENTS + 1) nDense = MIN_CLOSED_SEGMENTS + 1;
|
|
62818
62903
|
if (nDense > maxPoints) nDense = maxPoints;
|
|
62819
62904
|
if (nDense < 2) nDense = 2;
|
|
@@ -62823,7 +62908,7 @@ ${svg}
|
|
|
62823
62908
|
}
|
|
62824
62909
|
|
|
62825
62910
|
// 2. one-pass bend-angle filter
|
|
62826
|
-
var theta =
|
|
62911
|
+
var theta = ctx.bendAngle;
|
|
62827
62912
|
var epsDev = ctx.tol * DEVIATION_FACTOR;
|
|
62828
62913
|
var out = [];
|
|
62829
62914
|
for (var c = 0; c < K; c++) out.push([]);
|
|
@@ -63120,6 +63205,10 @@ ${svg}
|
|
|
63120
63205
|
if (opts.gain !== undefined && opts.gain !== null && !(opts.gain >= 0)) {
|
|
63121
63206
|
stop$1('Expected gain to be a number >= 0');
|
|
63122
63207
|
}
|
|
63208
|
+
if (opts.max_bend_angle !== undefined && opts.max_bend_angle !== null &&
|
|
63209
|
+
!(opts.max_bend_angle > 0)) {
|
|
63210
|
+
stop$1('Expected max-bend-angle to be a number > 0 (degrees)');
|
|
63211
|
+
}
|
|
63123
63212
|
var implicitlySmoothedNames = getImplicitlyTargetedLayerNames(dataset, targetLayers, layerHasPaths);
|
|
63124
63213
|
|
|
63125
63214
|
// Smoothing rewrites coordinates, so lock in any pending (non-destructive)
|
|
@@ -63150,7 +63239,8 @@ ${svg}
|
|
|
63150
63239
|
method: method,
|
|
63151
63240
|
spherical: spherical,
|
|
63152
63241
|
keepCorners: !!opts.keep_corners,
|
|
63153
|
-
gain: opts.gain
|
|
63242
|
+
gain: opts.gain,
|
|
63243
|
+
maxBendAngle: opts.max_bend_angle
|
|
63154
63244
|
});
|
|
63155
63245
|
|
|
63156
63246
|
if (implicitlySmoothedNames.length > 0) {
|
|
@@ -63177,6 +63267,7 @@ ${svg}
|
|
|
63177
63267
|
spherical: opts.spherical,
|
|
63178
63268
|
keepCorners: opts.keepCorners,
|
|
63179
63269
|
gain: opts.gain,
|
|
63270
|
+
maxBendAngle: opts.maxBendAngle,
|
|
63180
63271
|
closed: arcs.arcIsClosed(arcId)
|
|
63181
63272
|
});
|
|
63182
63273
|
nn.push(res.xx.length);
|
|
@@ -64993,7 +65084,7 @@ ${svg}
|
|
|
64993
65084
|
return name == 'rectangle' || name == 'rectangles' || name == 'filter' && opts.cleanup;
|
|
64994
65085
|
}
|
|
64995
65086
|
|
|
64996
|
-
var version = "0.7.
|
|
65087
|
+
var version = "0.7.32";
|
|
64997
65088
|
|
|
64998
65089
|
// Parse command line args into commands and run them
|
|
64999
65090
|
// Function takes an optional Node-style callback. A Promise is returned if no callback is given.
|
package/package.json
CHANGED
package/www/mapshaper.js
CHANGED
|
@@ -32938,6 +32938,10 @@ ${svg}
|
|
|
32938
32938
|
describe: 'shrinkage-correction (default 1; 0 = none, >1 exaggerates bends)',
|
|
32939
32939
|
type: 'number'
|
|
32940
32940
|
})
|
|
32941
|
+
.option('max-bend-angle', {
|
|
32942
|
+
describe: 'max bend between output segments in degrees (default is 8)',
|
|
32943
|
+
type: 'number'
|
|
32944
|
+
})
|
|
32941
32945
|
.option('planar', {
|
|
32942
32946
|
// describe: 'smooth decimal degree coords in 2D space (default is spherical)',
|
|
32943
32947
|
type: 'flag'
|
|
@@ -57238,7 +57242,16 @@ ${svg}
|
|
|
57238
57242
|
// distance D) segments the line into runs that each fit within the detail
|
|
57239
57243
|
// scale and bounds every candidate chord to <= D, so cuts stay local.
|
|
57240
57244
|
//
|
|
57241
|
-
// 2.
|
|
57245
|
+
// 2. MERGE survivors that hide a slicing chord. A spike with long bare flanks
|
|
57246
|
+
// parks its base vertices as survivors (each flank chord alone exceeds D), so
|
|
57247
|
+
// the short chord that closes the excursion sits between non-adjacent
|
|
57248
|
+
// survivors. Where a near-degenerate closing chord (a needle returning close
|
|
57249
|
+
// to its base: chord <= MERGE_CHORD_FRACTION * D, tortuosity >= threshold)
|
|
57250
|
+
// exists within an arc-length window, widen the run so the next phase can
|
|
57251
|
+
// slice it. Restricting to short closing chords keeps the merge from sweeping
|
|
57252
|
+
// a wide excursion across neighbouring geometry and introducing a crossing.
|
|
57253
|
+
//
|
|
57254
|
+
// 3. COMMIT selectively. For each run of removed vertices between two survivors,
|
|
57242
57255
|
// compare the original sub-path length to the chord across it (tortuosity).
|
|
57243
57256
|
// Collapse the run to its chord only when it is convoluted (tortuosity >=
|
|
57244
57257
|
// threshold) -- a jetty, fjord or crinkle. Otherwise restore the run's
|
|
@@ -57256,6 +57269,16 @@ ${svg}
|
|
|
57256
57269
|
// topology nodes stay put and the operation is topology-safe like -simplify.
|
|
57257
57270
|
var DEFAULT_WEIGHTING = 0.7;
|
|
57258
57271
|
var DEFAULT_TORTUOSITY = 2;
|
|
57272
|
+
// How far (in detail-distances of arc length) the survivor-merge pass looks ahead
|
|
57273
|
+
// for a chord that closes a convoluted excursion. Bounds the pass to O(n) and
|
|
57274
|
+
// caps how long a thin spike it can slice in one merge.
|
|
57275
|
+
var MERGE_WINDOW_FACTOR = 12;
|
|
57276
|
+
// The survivor-merge pass only fires when the closing chord is this fraction of D
|
|
57277
|
+
// or shorter: a near-degenerate needle that returns close to its base can be
|
|
57278
|
+
// sliced safely, but collapsing a wider excursion (chord approaching D) sweeps a
|
|
57279
|
+
// long span across neighbouring geometry and can introduce a crossing. cutRun,
|
|
57280
|
+
// which spans only adjacent survivors, is not constrained this way.
|
|
57281
|
+
var MERGE_CHORD_FRACTION = 0.5;
|
|
57259
57282
|
|
|
57260
57283
|
function collapseArcDetail(xx, yy, opts) {
|
|
57261
57284
|
var n = xx.length;
|
|
@@ -57378,6 +57401,47 @@ ${svg}
|
|
|
57378
57401
|
}
|
|
57379
57402
|
}
|
|
57380
57403
|
|
|
57404
|
+
// Survivor-merge pass. A thin spike with long bare flanks (e.g. a fjord wall or
|
|
57405
|
+
// a dredged channel) forces its base vertices to be parked as survivors -- each
|
|
57406
|
+
// flank chord on its own exceeds D -- so the short chord that actually closes
|
|
57407
|
+
// the excursion is hidden between *non-adjacent* survivors and the per-run
|
|
57408
|
+
// cutRun above never sees it. Walk the survivor chain and, where a later
|
|
57409
|
+
// survivor closes a convoluted excursion with a short chord (<=
|
|
57410
|
+
// MERGE_CHORD_FRACTION * D, tortuosity >= T) within an arc-length window,
|
|
57411
|
+
// splice out the spanned survivors so the run boundary widens and cutRun slices
|
|
57412
|
+
// the spike off. Restricting to short closing chords keeps the merge from
|
|
57413
|
+
// sweeping a wide excursion across neighbouring geometry. This reuses cumLen and the
|
|
57414
|
+
// peel's linked list; it does not re-run the Visvalingam peel. Only non-adjacent
|
|
57415
|
+
// convoluted survivors are merged, so gentle runs (and all existing behaviour)
|
|
57416
|
+
// are untouched.
|
|
57417
|
+
var window = MERGE_WINDOW_FACTOR * D;
|
|
57418
|
+
var mergeChordSq = Dsq * MERGE_CHORD_FRACTION * MERGE_CHORD_FRACTION;
|
|
57419
|
+
var s = 0;
|
|
57420
|
+
while (s !== n - 1) {
|
|
57421
|
+
var mergeJ = -1;
|
|
57422
|
+
var mergeTort = T;
|
|
57423
|
+
var u = next[s];
|
|
57424
|
+
while (cumLen[u] - cumLen[s] <= window) {
|
|
57425
|
+
if (chordSq(s, u) <= mergeChordSq) {
|
|
57426
|
+
var ud = dist(s, u);
|
|
57427
|
+
var utort = ud > 0 ? (cumLen[u] - cumLen[s]) / ud : Infinity;
|
|
57428
|
+
if (utort > mergeTort) {
|
|
57429
|
+
mergeTort = utort;
|
|
57430
|
+
mergeJ = u;
|
|
57431
|
+
}
|
|
57432
|
+
}
|
|
57433
|
+
if (u === n - 1) break;
|
|
57434
|
+
u = next[u];
|
|
57435
|
+
}
|
|
57436
|
+
if (mergeJ > next[s]) {
|
|
57437
|
+
next[s] = mergeJ;
|
|
57438
|
+
prev[mergeJ] = s;
|
|
57439
|
+
s = mergeJ;
|
|
57440
|
+
} else {
|
|
57441
|
+
s = next[s];
|
|
57442
|
+
}
|
|
57443
|
+
}
|
|
57444
|
+
|
|
57381
57445
|
outX.push(xx[0]);
|
|
57382
57446
|
outY.push(yy[0]);
|
|
57383
57447
|
var a = 0;
|
|
@@ -62527,20 +62591,25 @@ ${svg}
|
|
|
62527
62591
|
// chord * accumulatedTurn / 8 estimates the bow of a circular arc, and we also
|
|
62528
62592
|
// cut when it exceeds a fraction of the tolerance. Both tests are O(1) per dense
|
|
62529
62593
|
// vertex, so the pass stays O(n).
|
|
62530
|
-
var DENSE_STEP_FACTOR = 0.033; // dense sampling step = tolerance * this
|
|
62531
|
-
// resolve the sharpest
|
|
62532
|
-
// ~ the kernel scale)
|
|
62533
|
-
// angle filter can reach
|
|
62534
|
-
//
|
|
62535
|
-
// kept well under
|
|
62536
|
-
//
|
|
62537
|
-
|
|
62594
|
+
var DENSE_STEP_FACTOR = 0.033; // dense sampling step = tolerance * this at the
|
|
62595
|
+
// default bend angle. Must resolve the sharpest
|
|
62596
|
+
// smoothed feature (radius ~ the kernel scale)
|
|
62597
|
+
// finely enough that the angle filter can reach
|
|
62598
|
+
// the bend-angle joins: one dense segment turns
|
|
62599
|
+
// ~ this/0.4 radians there, kept well under the
|
|
62600
|
+
// bend angle so the discrete accumulation barely
|
|
62601
|
+
// overshoots the threshold. For a smaller-than-
|
|
62602
|
+
// default bend angle the step is refined in
|
|
62603
|
+
// proportion so the threshold stays reachable.
|
|
62604
|
+
var DEFAULT_BEND_ANGLE = 8 * Math.PI / 180; // keep a vertex after this much accumulated
|
|
62605
|
+
// turn (max turn between consecutive output
|
|
62606
|
+
// segments); user-overridable via max-bend-angle
|
|
62538
62607
|
var DEVIATION_FACTOR = 0.1; // sagitta guard: also cut a gentle bend that bows
|
|
62539
62608
|
// more than tolerance * this from its chord
|
|
62540
62609
|
|
|
62541
62610
|
// Smooth a single arc's coordinates.
|
|
62542
62611
|
// @xx, @yy: coordinate arrays (may be typed-array subarrays) for one arc.
|
|
62543
|
-
// @opts: {tolerance, method, spherical, closed, keepCorners}
|
|
62612
|
+
// @opts: {tolerance, method, spherical, closed, keepCorners, gain, maxBendAngle}
|
|
62544
62613
|
// Returns {xx: [], yy: []} with the smoothed coordinates. Endpoints of open
|
|
62545
62614
|
// arcs are preserved exactly (so shared topology nodes stay put); closed arcs
|
|
62546
62615
|
// are smoothed cyclically and returned closed (first point repeated at the end).
|
|
@@ -62555,6 +62624,16 @@ ${svg}
|
|
|
62555
62624
|
return g >= 0 ? g : 0;
|
|
62556
62625
|
}
|
|
62557
62626
|
|
|
62627
|
+
// Resolve the output bend-angle threshold from the user option (in degrees) to
|
|
62628
|
+
// radians. It caps the turn between consecutive output segments: a larger angle
|
|
62629
|
+
// keeps fewer vertices (coarser joins), a smaller one keeps more (smoother joins).
|
|
62630
|
+
// Non-positive or missing values fall back to the default.
|
|
62631
|
+
function resolveBendAngle(opts) {
|
|
62632
|
+
var deg = opts.maxBendAngle;
|
|
62633
|
+
if (deg === undefined || deg === null || !(deg > 0)) return DEFAULT_BEND_ANGLE;
|
|
62634
|
+
return deg * Math.PI / 180;
|
|
62635
|
+
}
|
|
62636
|
+
|
|
62558
62637
|
function smoothArcCoords(xx, yy, opts) {
|
|
62559
62638
|
var n = xx.length;
|
|
62560
62639
|
var origX = toArray(xx);
|
|
@@ -62567,12 +62646,18 @@ ${svg}
|
|
|
62567
62646
|
var closed = !!opts.closed;
|
|
62568
62647
|
var spherical = !!opts.spherical;
|
|
62569
62648
|
var keepCorners = !!opts.keepCorners;
|
|
62649
|
+
var bendAngle = resolveBendAngle(opts);
|
|
62570
62650
|
var ctx = {
|
|
62571
62651
|
tol: tol,
|
|
62572
62652
|
method: method,
|
|
62573
62653
|
spherical: spherical,
|
|
62574
62654
|
keepCorners: keepCorners,
|
|
62575
62655
|
gain: resolveGain(opts),
|
|
62656
|
+
bendAngle: bendAngle,
|
|
62657
|
+
// Refine the dense step for a smaller-than-default bend angle so one dense
|
|
62658
|
+
// segment still turns well under the threshold; never coarsen it beyond the
|
|
62659
|
+
// default (the angle filter alone thins the output for larger angles).
|
|
62660
|
+
denseStep: tol * DENSE_STEP_FACTOR * Math.min(1, bendAngle / DEFAULT_BEND_ANGLE),
|
|
62576
62661
|
radius: tol * WINDOW_RADIUS_FACTOR,
|
|
62577
62662
|
scale: (method == 'gaussian' ? GAUSSIAN_SIGMA_FACTOR : PAEK_SCALE_FACTOR) * tol
|
|
62578
62663
|
};
|
|
@@ -62804,7 +62889,7 @@ ${svg}
|
|
|
62804
62889
|
// "Output resampling" note above). Step 1 evaluates the smoother at a uniform
|
|
62805
62890
|
// dense step; step 2 makes one forward pass keeping the endpoints plus every
|
|
62806
62891
|
// interior vertex where the accumulated turn since the last kept vertex reaches
|
|
62807
|
-
//
|
|
62892
|
+
// the bend-angle threshold, or where the sagitta guard trips. @inputCount bounds the dense
|
|
62808
62893
|
// sampling (and thus the output). Returns one array per channel, ordered by
|
|
62809
62894
|
// increasing arc length, including both endpoints.
|
|
62810
62895
|
function sampleSmoothedCurve(src, a, b, closed, ctx, inputCount) {
|
|
@@ -62813,7 +62898,7 @@ ${svg}
|
|
|
62813
62898
|
var maxPoints = Math.max(inputCount, MIN_CLOSED_SEGMENTS) * MAX_OUTPUT_FACTOR;
|
|
62814
62899
|
|
|
62815
62900
|
// 1. dense uniform sampling of the smoothed curve
|
|
62816
|
-
var nDense = Math.ceil(span /
|
|
62901
|
+
var nDense = Math.ceil(span / ctx.denseStep) + 1;
|
|
62817
62902
|
if (nDense < MIN_CLOSED_SEGMENTS + 1) nDense = MIN_CLOSED_SEGMENTS + 1;
|
|
62818
62903
|
if (nDense > maxPoints) nDense = maxPoints;
|
|
62819
62904
|
if (nDense < 2) nDense = 2;
|
|
@@ -62823,7 +62908,7 @@ ${svg}
|
|
|
62823
62908
|
}
|
|
62824
62909
|
|
|
62825
62910
|
// 2. one-pass bend-angle filter
|
|
62826
|
-
var theta =
|
|
62911
|
+
var theta = ctx.bendAngle;
|
|
62827
62912
|
var epsDev = ctx.tol * DEVIATION_FACTOR;
|
|
62828
62913
|
var out = [];
|
|
62829
62914
|
for (var c = 0; c < K; c++) out.push([]);
|
|
@@ -63120,6 +63205,10 @@ ${svg}
|
|
|
63120
63205
|
if (opts.gain !== undefined && opts.gain !== null && !(opts.gain >= 0)) {
|
|
63121
63206
|
stop$1('Expected gain to be a number >= 0');
|
|
63122
63207
|
}
|
|
63208
|
+
if (opts.max_bend_angle !== undefined && opts.max_bend_angle !== null &&
|
|
63209
|
+
!(opts.max_bend_angle > 0)) {
|
|
63210
|
+
stop$1('Expected max-bend-angle to be a number > 0 (degrees)');
|
|
63211
|
+
}
|
|
63123
63212
|
var implicitlySmoothedNames = getImplicitlyTargetedLayerNames(dataset, targetLayers, layerHasPaths);
|
|
63124
63213
|
|
|
63125
63214
|
// Smoothing rewrites coordinates, so lock in any pending (non-destructive)
|
|
@@ -63150,7 +63239,8 @@ ${svg}
|
|
|
63150
63239
|
method: method,
|
|
63151
63240
|
spherical: spherical,
|
|
63152
63241
|
keepCorners: !!opts.keep_corners,
|
|
63153
|
-
gain: opts.gain
|
|
63242
|
+
gain: opts.gain,
|
|
63243
|
+
maxBendAngle: opts.max_bend_angle
|
|
63154
63244
|
});
|
|
63155
63245
|
|
|
63156
63246
|
if (implicitlySmoothedNames.length > 0) {
|
|
@@ -63177,6 +63267,7 @@ ${svg}
|
|
|
63177
63267
|
spherical: opts.spherical,
|
|
63178
63268
|
keepCorners: opts.keepCorners,
|
|
63179
63269
|
gain: opts.gain,
|
|
63270
|
+
maxBendAngle: opts.maxBendAngle,
|
|
63180
63271
|
closed: arcs.arcIsClosed(arcId)
|
|
63181
63272
|
});
|
|
63182
63273
|
nn.push(res.xx.length);
|
|
@@ -64993,7 +65084,7 @@ ${svg}
|
|
|
64993
65084
|
return name == 'rectangle' || name == 'rectangles' || name == 'filter' && opts.cleanup;
|
|
64994
65085
|
}
|
|
64995
65086
|
|
|
64996
|
-
var version = "0.7.
|
|
65087
|
+
var version = "0.7.32";
|
|
64997
65088
|
|
|
64998
65089
|
// Parse command line args into commands and run them
|
|
64999
65090
|
// Function takes an optional Node-style callback. A Promise is returned if no callback is given.
|