mapshaper 0.7.63 → 0.7.65
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 +81 -10
- package/package.json +1 -1
- package/www/mapshaper-gui.js +55 -9
- package/www/mapshaper.js +81 -10
package/mapshaper.js
CHANGED
|
@@ -27981,6 +27981,12 @@
|
|
|
27981
27981
|
// invariant under translation, rotation and scaling (Hobby 1986), which is
|
|
27982
27982
|
// what lets the tool fit in display coordinates while storing knots in map
|
|
27983
27983
|
// coordinates.
|
|
27984
|
+
//
|
|
27985
|
+
// Left alone, the method bulges: a hard turn followed by a much shorter
|
|
27986
|
+
// segment throws the long segment into a wide arc, several times the length of
|
|
27987
|
+
// the short one. Two limits below hold that in, MAX_HANDLE and
|
|
27988
|
+
// MAX_CHORD_RATIO. Both are inert on a path whose chords are of comparable
|
|
27989
|
+
// length, so an ordinary label path is fitted exactly as Hobby describes.
|
|
27984
27990
|
|
|
27985
27991
|
// Curl controls how the curve behaves at the two ends of a run. At 0 it
|
|
27986
27992
|
// approaches a straight line there; at 1 (Metafont's default) it approaches a
|
|
@@ -28068,6 +28074,24 @@
|
|
|
28068
28074
|
VEL_B = 1 / 16,
|
|
28069
28075
|
VEL_C = (3 - Math.sqrt(5)) / 2;
|
|
28070
28076
|
|
|
28077
|
+
// Longest a control point may sit from its knot, as a fraction of the chord
|
|
28078
|
+
// length that handleLimit() works out for it. See "Bulge" in the design doc: a
|
|
28079
|
+
// hard turn followed by a short segment otherwise throws the long segment into
|
|
28080
|
+
// a wide arc, which during editing is most of the time, because a short
|
|
28081
|
+
// segment is what a half-dragged knot leaves behind.
|
|
28082
|
+
//
|
|
28083
|
+
// The value is a floor, not a preference. Four points on a circle want 0.3905,
|
|
28084
|
+
// which is the roundest any sensible path asks for, so a limit just above it
|
|
28085
|
+
// never touches a curve that was not already misbehaving.
|
|
28086
|
+
var MAX_HANDLE = 0.4;
|
|
28087
|
+
|
|
28088
|
+
// How much longer one chord may be than its neighbour *for the purposes of the
|
|
28089
|
+
// solve*. Hobby equalizes mock curvature across each knot, and a short chord
|
|
28090
|
+
// carries a lot of curvature, so without this a short segment dictates a wide
|
|
28091
|
+
// arc to a long one. Capping the ratio limits how far that reaches without
|
|
28092
|
+
// moving any knot or touching the curve where the chords are even.
|
|
28093
|
+
var MAX_CHORD_RATIO = 1.5;
|
|
28094
|
+
|
|
28071
28095
|
// Fits a run of knots, returning a cubic per interval.
|
|
28072
28096
|
function fitRun(pts) {
|
|
28073
28097
|
var n = pts.length - 1, // segment count
|
|
@@ -28084,16 +28108,54 @@
|
|
|
28084
28108
|
psi.push(0);
|
|
28085
28109
|
for (i = 1; i < n; i++) psi.push(wrapAngle(om[i] - om[i - 1]));
|
|
28086
28110
|
|
|
28087
|
-
theta = solveDepartureAngles(dd, psi, n);
|
|
28111
|
+
theta = solveDepartureAngles(evenOutChords(dd, n), psi, n);
|
|
28088
28112
|
phi = getArrivalAngles(theta, psi, n);
|
|
28089
28113
|
|
|
28090
28114
|
for (i = 0; i < n; i++) {
|
|
28091
28115
|
segments.push(buildSegment(pts[i], pts[i + 1], dd[i], om[i],
|
|
28092
|
-
theta[i], phi[i + 1]
|
|
28116
|
+
theta[i], phi[i + 1],
|
|
28117
|
+
handleLimit(dd[i], i > 0 ? dd[i - 1] : Infinity),
|
|
28118
|
+
handleLimit(dd[i], i < n - 1 ? dd[i + 1] : Infinity)));
|
|
28093
28119
|
}
|
|
28094
28120
|
return segments;
|
|
28095
28121
|
}
|
|
28096
28122
|
|
|
28123
|
+
// Longest a handle may be at one end of a segment whose own chord is @own and
|
|
28124
|
+
// whose neighbour across that knot is @neighbor. (At the first and last knot
|
|
28125
|
+
// of a run there is no neighbour, and the segment's own chord is the measure.)
|
|
28126
|
+
//
|
|
28127
|
+
// The scale is the geometric mean of the two, rather than the shorter of them.
|
|
28128
|
+
// Measuring against the shorter alone holds the bulge down just as well, but
|
|
28129
|
+
// it makes a long segment meeting a short one spend nearly all of its shape at
|
|
28130
|
+
// its far end: on a 490-unit arm meeting a 50-unit leg the two handles came
|
|
28131
|
+
// out 9.8:1, so the arm ran almost straight and then hooked hard into the
|
|
28132
|
+
// knot, and the curvature either side of that knot differed by a factor of 14.
|
|
28133
|
+
// The geometric mean gives the long segment a handle in proportion to how
|
|
28134
|
+
// lopsided the pair actually is, which brings the same case to 3.1:1 and the
|
|
28135
|
+
// curvature to within a factor of 1.4 -- about what Hobby's mock curvature
|
|
28136
|
+
// leaves on an ordinary path anyway -- for two points of extra bulge.
|
|
28137
|
+
function handleLimit(own, neighbor) {
|
|
28138
|
+
return MAX_HANDLE * Math.sqrt(own * Math.min(own, neighbor));
|
|
28139
|
+
}
|
|
28140
|
+
|
|
28141
|
+
// The chord lengths the solve sees, with the ratio between neighbours held to
|
|
28142
|
+
// MAX_CHORD_RATIO. Each length becomes the smallest that any chord permits it
|
|
28143
|
+
// to be, allowing for a factor of MAX_CHORD_RATIO per step -- a forward pass
|
|
28144
|
+
// and a backward pass compute that exactly, so this needs no iteration.
|
|
28145
|
+
//
|
|
28146
|
+
// Only the tangent directions are affected. The curve is still built on the
|
|
28147
|
+
// true chord lengths.
|
|
28148
|
+
function evenOutChords(dd, n) {
|
|
28149
|
+
var out = dd.concat(), i;
|
|
28150
|
+
for (i = 1; i < n; i++) {
|
|
28151
|
+
if (out[i] > out[i - 1] * MAX_CHORD_RATIO) out[i] = out[i - 1] * MAX_CHORD_RATIO;
|
|
28152
|
+
}
|
|
28153
|
+
for (i = n - 2; i >= 0; i--) {
|
|
28154
|
+
if (out[i] > out[i + 1] * MAX_CHORD_RATIO) out[i] = out[i + 1] * MAX_CHORD_RATIO;
|
|
28155
|
+
}
|
|
28156
|
+
return out;
|
|
28157
|
+
}
|
|
28158
|
+
|
|
28097
28159
|
// The tridiagonal system of Hobby's mock-curvature conditions, in the
|
|
28098
28160
|
// departure angle at each knot measured from the chord leaving it. Written for
|
|
28099
28161
|
// unit tension throughout; the general form carries a tension per knot, which
|
|
@@ -28137,21 +28199,30 @@
|
|
|
28137
28199
|
|
|
28138
28200
|
// Turns a pair of angles into a cubic, by way of Hobby's velocity functions --
|
|
28139
28201
|
// how far along each tangent the control point sits, as a multiple of a third
|
|
28140
|
-
// of the chord.
|
|
28141
|
-
|
|
28202
|
+
// of the chord -- with each distance held to the caller's limit.
|
|
28203
|
+
//
|
|
28204
|
+
// Shortening a handle leaves its direction alone, so the tangent the two
|
|
28205
|
+
// segments share at a knot is untouched and the curve stays smooth through it.
|
|
28206
|
+
// What it gives up is Hobby's mock-curvature match across that knot, and only
|
|
28207
|
+
// at a knot lopsided enough for the limit to bite.
|
|
28208
|
+
function buildSegment(p0, p1, d, om, theta, phi, maxOut, maxIn) {
|
|
28142
28209
|
var alpha = VEL_A * (Math.sin(theta) - VEL_B * Math.sin(phi)) *
|
|
28143
28210
|
(Math.sin(phi) - VEL_B * Math.sin(theta)) *
|
|
28144
28211
|
(Math.cos(theta) - Math.cos(phi)),
|
|
28145
28212
|
rho = (2 + alpha) /
|
|
28146
28213
|
(1 + (1 - VEL_C) * Math.cos(theta) + VEL_C * Math.cos(phi)),
|
|
28147
28214
|
sigma = (2 - alpha) /
|
|
28148
|
-
(1 + (1 - VEL_C) * Math.cos(phi) + VEL_C * Math.cos(theta))
|
|
28215
|
+
(1 + (1 - VEL_C) * Math.cos(phi) + VEL_C * Math.cos(theta)),
|
|
28216
|
+
out = d * rho / 3,
|
|
28217
|
+
arr = d * sigma / 3;
|
|
28218
|
+
if (out > maxOut) out = maxOut;
|
|
28219
|
+
if (arr > maxIn) arr = maxIn;
|
|
28149
28220
|
return {
|
|
28150
28221
|
p0: p0,
|
|
28151
|
-
c1: [p0[0] +
|
|
28152
|
-
p0[1] +
|
|
28153
|
-
c2: [p1[0] -
|
|
28154
|
-
p1[1] -
|
|
28222
|
+
c1: [p0[0] + out * Math.cos(theta + om),
|
|
28223
|
+
p0[1] + out * Math.sin(theta + om)],
|
|
28224
|
+
c2: [p1[0] - arr * Math.cos(om - phi),
|
|
28225
|
+
p1[1] - arr * Math.sin(om - phi)],
|
|
28155
28226
|
p3: p1
|
|
28156
28227
|
};
|
|
28157
28228
|
}
|
|
@@ -82718,7 +82789,7 @@ ${svg}
|
|
|
82718
82789
|
return name == 'rectangle' || name == 'rectangles' || name == 'filter' && opts.cleanup;
|
|
82719
82790
|
}
|
|
82720
82791
|
|
|
82721
|
-
var version = "0.7.
|
|
82792
|
+
var version = "0.7.65";
|
|
82722
82793
|
|
|
82723
82794
|
// Parse command line args into commands and run them
|
|
82724
82795
|
// Function takes an optional Node-style callback. A Promise is returned if no callback is given.
|
package/package.json
CHANGED
package/www/mapshaper-gui.js
CHANGED
|
@@ -16663,10 +16663,15 @@
|
|
|
16663
16663
|
// aligned: whether it carries a label-align (see below)
|
|
16664
16664
|
// delta: {dx, dy} -- how far the pointer has moved, in the same space.
|
|
16665
16665
|
//
|
|
16666
|
-
// Returns
|
|
16667
|
-
//
|
|
16668
|
-
//
|
|
16669
|
-
//
|
|
16666
|
+
// Returns two descriptions of the same placement:
|
|
16667
|
+
//
|
|
16668
|
+
// dx, dy, text-anchor: what the drag writes to the record. The three have to
|
|
16669
|
+
// be written together: applying dx without its text-anchor moves the text
|
|
16670
|
+
// by half its own width or by all of it.
|
|
16671
|
+
// x, anchor: where the text sits and how it is justified while it is drawn,
|
|
16672
|
+
// which is what the preview writes onto the rendered label. The same as
|
|
16673
|
+
// dx and text-anchor except on an aligned label, whose stored offset is
|
|
16674
|
+
// measured against a justification it is not drawn with.
|
|
16670
16675
|
function getOffsetDragValues(start, delta) {
|
|
16671
16676
|
var width = start.width > 0 ? start.width : 0;
|
|
16672
16677
|
var drawnAnchor = normalizeAnchor(start.anchor);
|
|
@@ -16685,12 +16690,18 @@
|
|
|
16685
16690
|
// if the alignment were later removed.
|
|
16686
16691
|
var anchor = start.aligned ? 'start' :
|
|
16687
16692
|
width > 0 ? getAnchorForCentre(left + width / 2, width) : drawnAnchor;
|
|
16693
|
+
// An aligned label goes on being drawn with the anchor its alignment gives
|
|
16694
|
+
// it, whatever the drag writes: the alignment is unchanged, so the renderer
|
|
16695
|
+
// will justify it the same way afterwards. Drawing it as 'start' instead
|
|
16696
|
+
// would slide the text right by half its width or by all of it for the
|
|
16697
|
+
// length of the drag, and drop it back on release.
|
|
16698
|
+
var drawn = start.aligned ? drawnAnchor : anchor;
|
|
16688
16699
|
return {
|
|
16689
16700
|
dx: round$1(left + anchorOffsets[anchor] * width),
|
|
16690
16701
|
dy: round$1(start.dy + delta.dy),
|
|
16691
16702
|
'text-anchor': anchor,
|
|
16692
|
-
x: round$1(
|
|
16693
|
-
|
|
16703
|
+
x: round$1(left + anchorOffsets[drawn] * width),
|
|
16704
|
+
anchor: drawn
|
|
16694
16705
|
};
|
|
16695
16706
|
}
|
|
16696
16707
|
|
|
@@ -17070,8 +17081,12 @@
|
|
|
17070
17081
|
applyInlineCss(cssInput.node().value.trim());
|
|
17071
17082
|
});
|
|
17072
17083
|
|
|
17084
|
+
// The caption for the grid and the drag-mode toggle below it, and what
|
|
17085
|
+
// gives Fixed|Draggable something to be an adjective of. "Position" read as
|
|
17086
|
+
// the label's own position -- which a drag moves in either mode -- and made
|
|
17087
|
+
// Fixed sound like a lock on a label that is still perfectly draggable.
|
|
17073
17088
|
var positionRow = El('label').addClass('label-style-row').appendTo(textSection);
|
|
17074
|
-
El('span').appendTo(positionRow).text('
|
|
17089
|
+
El('span').appendTo(positionRow).text('Offset from anchor');
|
|
17075
17090
|
|
|
17076
17091
|
// Whether the label has a symbol is one question and which symbol it has is
|
|
17077
17092
|
// another, so the first is a switch on the section's heading rather than a
|
|
@@ -25332,6 +25347,9 @@
|
|
|
25332
25347
|
// See docs/development/label-tool-design.md.
|
|
25333
25348
|
|
|
25334
25349
|
var SVG_NS$2 = 'http://www.w3.org/2000/svg';
|
|
25350
|
+
// How far outside its text a label's outline is drawn. Exported because the
|
|
25351
|
+
// tool grabs a label by the same box: what looks like the object is what takes
|
|
25352
|
+
// a drag on it.
|
|
25335
25353
|
var BOX_PADDING = 3;
|
|
25336
25354
|
var ANCHOR_RADIUS = 3.5;
|
|
25337
25355
|
var KNOT_RADIUS = 3;
|
|
@@ -26524,7 +26542,10 @@
|
|
|
26524
26542
|
// ended.
|
|
26525
26543
|
setMultilineAttribute(nodes.text, 'x', o.values.x);
|
|
26526
26544
|
nodes.text.setAttribute('y', o.values.dy);
|
|
26527
|
-
|
|
26545
|
+
// The anchor the label is drawn with, not the one the drag writes: on an
|
|
26546
|
+
// aligned label the two differ, and the stored one belongs with the stored
|
|
26547
|
+
// dx rather than with the x being previewed here.
|
|
26548
|
+
nodes.text.setAttribute('text-anchor', o.values.anchor);
|
|
26528
26549
|
// The cue is drawn around the text rather than moved with it, so it has to
|
|
26529
26550
|
// be rebuilt to follow -- one getBBox on one label per mouse move.
|
|
26530
26551
|
selection.refresh(true);
|
|
@@ -26679,9 +26700,34 @@
|
|
|
26679
26700
|
hoverTextId = id > -1 && (!hoverHandle || glyphsOutrankHandle(id)) ? id : -1;
|
|
26680
26701
|
}
|
|
26681
26702
|
|
|
26703
|
+
// The exception is about the pointer being on the glyphs, not about the mode:
|
|
26704
|
+
// an offset label's anchor is out from under its text, and taking the handle
|
|
26705
|
+
// away there left it with no anchor handle at all -- dragging the symbol of a
|
|
26706
|
+
// label positioned ne slid its text instead of moving the label.
|
|
26682
26707
|
function glyphsOutrankHandle(id) {
|
|
26683
26708
|
return hoverHandle.id == id && labelTextIsDraggable(gui) &&
|
|
26684
|
-
!isPathLabel(hit.getHitTarget(), id);
|
|
26709
|
+
!isPathLabel(hit.getHitTarget(), id) && pointerIsOverText(id);
|
|
26710
|
+
}
|
|
26711
|
+
|
|
26712
|
+
// Whether the pointer is within the box drawn around a label's text -- the
|
|
26713
|
+
// outline the selection cue draws, padding and all, so that the thing on
|
|
26714
|
+
// screen that says "this is the object" is the thing that takes the drag.
|
|
26715
|
+
//
|
|
26716
|
+
// From the last hover rather than from an event, because this is asked from
|
|
26717
|
+
// the hit control's 'change' as well, which arrives without a position; the
|
|
26718
|
+
// pointer has not moved since the hover that preceded it.
|
|
26719
|
+
function pointerIsOverText(id) {
|
|
26720
|
+
var target = hit.getHitTarget();
|
|
26721
|
+
var shapes = target && getDisplayShapes(target);
|
|
26722
|
+
var shp = shapes && shapes[id];
|
|
26723
|
+
var nodes = findLabelNodes(target, id);
|
|
26724
|
+
var box = nodes && !nodes.textPath ? measureNode(nodes.text) : null;
|
|
26725
|
+
var pix = hoverPoint && ext.translateCoords(hoverPoint[0], hoverPoint[1]);
|
|
26726
|
+
var p;
|
|
26727
|
+
if (!box || !pix || !shp) return false;
|
|
26728
|
+
p = getLabelSpacePoint(shp[0], {x: pix[0], y: pix[1]});
|
|
26729
|
+
return p.x >= box.x - BOX_PADDING && p.x <= box.x + box.width + BOX_PADDING &&
|
|
26730
|
+
p.y >= box.y - BOX_PADDING && p.y <= box.y + box.height + BOX_PADDING;
|
|
26685
26731
|
}
|
|
26686
26732
|
|
|
26687
26733
|
// The label a drag on the glyphs would act on, or -1. Only a selected label
|
package/www/mapshaper.js
CHANGED
|
@@ -27981,6 +27981,12 @@
|
|
|
27981
27981
|
// invariant under translation, rotation and scaling (Hobby 1986), which is
|
|
27982
27982
|
// what lets the tool fit in display coordinates while storing knots in map
|
|
27983
27983
|
// coordinates.
|
|
27984
|
+
//
|
|
27985
|
+
// Left alone, the method bulges: a hard turn followed by a much shorter
|
|
27986
|
+
// segment throws the long segment into a wide arc, several times the length of
|
|
27987
|
+
// the short one. Two limits below hold that in, MAX_HANDLE and
|
|
27988
|
+
// MAX_CHORD_RATIO. Both are inert on a path whose chords are of comparable
|
|
27989
|
+
// length, so an ordinary label path is fitted exactly as Hobby describes.
|
|
27984
27990
|
|
|
27985
27991
|
// Curl controls how the curve behaves at the two ends of a run. At 0 it
|
|
27986
27992
|
// approaches a straight line there; at 1 (Metafont's default) it approaches a
|
|
@@ -28068,6 +28074,24 @@
|
|
|
28068
28074
|
VEL_B = 1 / 16,
|
|
28069
28075
|
VEL_C = (3 - Math.sqrt(5)) / 2;
|
|
28070
28076
|
|
|
28077
|
+
// Longest a control point may sit from its knot, as a fraction of the chord
|
|
28078
|
+
// length that handleLimit() works out for it. See "Bulge" in the design doc: a
|
|
28079
|
+
// hard turn followed by a short segment otherwise throws the long segment into
|
|
28080
|
+
// a wide arc, which during editing is most of the time, because a short
|
|
28081
|
+
// segment is what a half-dragged knot leaves behind.
|
|
28082
|
+
//
|
|
28083
|
+
// The value is a floor, not a preference. Four points on a circle want 0.3905,
|
|
28084
|
+
// which is the roundest any sensible path asks for, so a limit just above it
|
|
28085
|
+
// never touches a curve that was not already misbehaving.
|
|
28086
|
+
var MAX_HANDLE = 0.4;
|
|
28087
|
+
|
|
28088
|
+
// How much longer one chord may be than its neighbour *for the purposes of the
|
|
28089
|
+
// solve*. Hobby equalizes mock curvature across each knot, and a short chord
|
|
28090
|
+
// carries a lot of curvature, so without this a short segment dictates a wide
|
|
28091
|
+
// arc to a long one. Capping the ratio limits how far that reaches without
|
|
28092
|
+
// moving any knot or touching the curve where the chords are even.
|
|
28093
|
+
var MAX_CHORD_RATIO = 1.5;
|
|
28094
|
+
|
|
28071
28095
|
// Fits a run of knots, returning a cubic per interval.
|
|
28072
28096
|
function fitRun(pts) {
|
|
28073
28097
|
var n = pts.length - 1, // segment count
|
|
@@ -28084,16 +28108,54 @@
|
|
|
28084
28108
|
psi.push(0);
|
|
28085
28109
|
for (i = 1; i < n; i++) psi.push(wrapAngle(om[i] - om[i - 1]));
|
|
28086
28110
|
|
|
28087
|
-
theta = solveDepartureAngles(dd, psi, n);
|
|
28111
|
+
theta = solveDepartureAngles(evenOutChords(dd, n), psi, n);
|
|
28088
28112
|
phi = getArrivalAngles(theta, psi, n);
|
|
28089
28113
|
|
|
28090
28114
|
for (i = 0; i < n; i++) {
|
|
28091
28115
|
segments.push(buildSegment(pts[i], pts[i + 1], dd[i], om[i],
|
|
28092
|
-
theta[i], phi[i + 1]
|
|
28116
|
+
theta[i], phi[i + 1],
|
|
28117
|
+
handleLimit(dd[i], i > 0 ? dd[i - 1] : Infinity),
|
|
28118
|
+
handleLimit(dd[i], i < n - 1 ? dd[i + 1] : Infinity)));
|
|
28093
28119
|
}
|
|
28094
28120
|
return segments;
|
|
28095
28121
|
}
|
|
28096
28122
|
|
|
28123
|
+
// Longest a handle may be at one end of a segment whose own chord is @own and
|
|
28124
|
+
// whose neighbour across that knot is @neighbor. (At the first and last knot
|
|
28125
|
+
// of a run there is no neighbour, and the segment's own chord is the measure.)
|
|
28126
|
+
//
|
|
28127
|
+
// The scale is the geometric mean of the two, rather than the shorter of them.
|
|
28128
|
+
// Measuring against the shorter alone holds the bulge down just as well, but
|
|
28129
|
+
// it makes a long segment meeting a short one spend nearly all of its shape at
|
|
28130
|
+
// its far end: on a 490-unit arm meeting a 50-unit leg the two handles came
|
|
28131
|
+
// out 9.8:1, so the arm ran almost straight and then hooked hard into the
|
|
28132
|
+
// knot, and the curvature either side of that knot differed by a factor of 14.
|
|
28133
|
+
// The geometric mean gives the long segment a handle in proportion to how
|
|
28134
|
+
// lopsided the pair actually is, which brings the same case to 3.1:1 and the
|
|
28135
|
+
// curvature to within a factor of 1.4 -- about what Hobby's mock curvature
|
|
28136
|
+
// leaves on an ordinary path anyway -- for two points of extra bulge.
|
|
28137
|
+
function handleLimit(own, neighbor) {
|
|
28138
|
+
return MAX_HANDLE * Math.sqrt(own * Math.min(own, neighbor));
|
|
28139
|
+
}
|
|
28140
|
+
|
|
28141
|
+
// The chord lengths the solve sees, with the ratio between neighbours held to
|
|
28142
|
+
// MAX_CHORD_RATIO. Each length becomes the smallest that any chord permits it
|
|
28143
|
+
// to be, allowing for a factor of MAX_CHORD_RATIO per step -- a forward pass
|
|
28144
|
+
// and a backward pass compute that exactly, so this needs no iteration.
|
|
28145
|
+
//
|
|
28146
|
+
// Only the tangent directions are affected. The curve is still built on the
|
|
28147
|
+
// true chord lengths.
|
|
28148
|
+
function evenOutChords(dd, n) {
|
|
28149
|
+
var out = dd.concat(), i;
|
|
28150
|
+
for (i = 1; i < n; i++) {
|
|
28151
|
+
if (out[i] > out[i - 1] * MAX_CHORD_RATIO) out[i] = out[i - 1] * MAX_CHORD_RATIO;
|
|
28152
|
+
}
|
|
28153
|
+
for (i = n - 2; i >= 0; i--) {
|
|
28154
|
+
if (out[i] > out[i + 1] * MAX_CHORD_RATIO) out[i] = out[i + 1] * MAX_CHORD_RATIO;
|
|
28155
|
+
}
|
|
28156
|
+
return out;
|
|
28157
|
+
}
|
|
28158
|
+
|
|
28097
28159
|
// The tridiagonal system of Hobby's mock-curvature conditions, in the
|
|
28098
28160
|
// departure angle at each knot measured from the chord leaving it. Written for
|
|
28099
28161
|
// unit tension throughout; the general form carries a tension per knot, which
|
|
@@ -28137,21 +28199,30 @@
|
|
|
28137
28199
|
|
|
28138
28200
|
// Turns a pair of angles into a cubic, by way of Hobby's velocity functions --
|
|
28139
28201
|
// how far along each tangent the control point sits, as a multiple of a third
|
|
28140
|
-
// of the chord.
|
|
28141
|
-
|
|
28202
|
+
// of the chord -- with each distance held to the caller's limit.
|
|
28203
|
+
//
|
|
28204
|
+
// Shortening a handle leaves its direction alone, so the tangent the two
|
|
28205
|
+
// segments share at a knot is untouched and the curve stays smooth through it.
|
|
28206
|
+
// What it gives up is Hobby's mock-curvature match across that knot, and only
|
|
28207
|
+
// at a knot lopsided enough for the limit to bite.
|
|
28208
|
+
function buildSegment(p0, p1, d, om, theta, phi, maxOut, maxIn) {
|
|
28142
28209
|
var alpha = VEL_A * (Math.sin(theta) - VEL_B * Math.sin(phi)) *
|
|
28143
28210
|
(Math.sin(phi) - VEL_B * Math.sin(theta)) *
|
|
28144
28211
|
(Math.cos(theta) - Math.cos(phi)),
|
|
28145
28212
|
rho = (2 + alpha) /
|
|
28146
28213
|
(1 + (1 - VEL_C) * Math.cos(theta) + VEL_C * Math.cos(phi)),
|
|
28147
28214
|
sigma = (2 - alpha) /
|
|
28148
|
-
(1 + (1 - VEL_C) * Math.cos(phi) + VEL_C * Math.cos(theta))
|
|
28215
|
+
(1 + (1 - VEL_C) * Math.cos(phi) + VEL_C * Math.cos(theta)),
|
|
28216
|
+
out = d * rho / 3,
|
|
28217
|
+
arr = d * sigma / 3;
|
|
28218
|
+
if (out > maxOut) out = maxOut;
|
|
28219
|
+
if (arr > maxIn) arr = maxIn;
|
|
28149
28220
|
return {
|
|
28150
28221
|
p0: p0,
|
|
28151
|
-
c1: [p0[0] +
|
|
28152
|
-
p0[1] +
|
|
28153
|
-
c2: [p1[0] -
|
|
28154
|
-
p1[1] -
|
|
28222
|
+
c1: [p0[0] + out * Math.cos(theta + om),
|
|
28223
|
+
p0[1] + out * Math.sin(theta + om)],
|
|
28224
|
+
c2: [p1[0] - arr * Math.cos(om - phi),
|
|
28225
|
+
p1[1] - arr * Math.sin(om - phi)],
|
|
28155
28226
|
p3: p1
|
|
28156
28227
|
};
|
|
28157
28228
|
}
|
|
@@ -82718,7 +82789,7 @@ ${svg}
|
|
|
82718
82789
|
return name == 'rectangle' || name == 'rectangles' || name == 'filter' && opts.cleanup;
|
|
82719
82790
|
}
|
|
82720
82791
|
|
|
82721
|
-
var version = "0.7.
|
|
82792
|
+
var version = "0.7.65";
|
|
82722
82793
|
|
|
82723
82794
|
// Parse command line args into commands and run them
|
|
82724
82795
|
// Function takes an optional Node-style callback. A Promise is returned if no callback is given.
|