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.
Files changed (3) hide show
  1. package/mapshaper.js +106 -15
  2. package/package.json +1 -1
  3. 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. COMMIT selectively. For each run of removed vertices between two survivors,
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. Must
62531
- // resolve the sharpest smoothed feature (radius
62532
- // ~ the kernel scale) finely enough that the
62533
- // angle filter can reach BEND_ANGLE joins: one
62534
- // dense segment turns ~ this/0.4 radians there,
62535
- // kept well under BEND_ANGLE so the discrete
62536
- // accumulation barely overshoots the threshold.
62537
- var BEND_ANGLE = 8 * Math.PI / 180; // keep a vertex after this much accumulated turn
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
- // BEND_ANGLE, or where the sagitta guard trips. @inputCount bounds the dense
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 / (ctx.tol * DENSE_STEP_FACTOR)) + 1;
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 = BEND_ANGLE;
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.30";
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mapshaper",
3
- "version": "0.7.30",
3
+ "version": "0.7.32",
4
4
  "description": "A tool for editing geospatial data for mapping and GIS.",
5
5
  "keywords": [
6
6
  "shapefile",
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. COMMIT selectively. For each run of removed vertices between two survivors,
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. Must
62531
- // resolve the sharpest smoothed feature (radius
62532
- // ~ the kernel scale) finely enough that the
62533
- // angle filter can reach BEND_ANGLE joins: one
62534
- // dense segment turns ~ this/0.4 radians there,
62535
- // kept well under BEND_ANGLE so the discrete
62536
- // accumulation barely overshoots the threshold.
62537
- var BEND_ANGLE = 8 * Math.PI / 180; // keep a vertex after this much accumulated turn
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
- // BEND_ANGLE, or where the sagitta guard trips. @inputCount bounds the dense
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 / (ctx.tol * DENSE_STEP_FACTOR)) + 1;
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 = BEND_ANGLE;
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.30";
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.