@weasel-js/geom 1.11.0 → 1.12.0
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/dist/booleans/index.d.ts +3 -1
- package/dist/booleans/index.js +138 -11
- package/dist/booleans/index.js.map +1 -1
- package/dist/{chunk-J2O2B54M.js → chunk-2PKPKU4P.js} +178 -174
- package/dist/chunk-2PKPKU4P.js.map +1 -0
- package/dist/{chunk-HGF5OVEN.js → chunk-STGSBIOL.js} +29 -6
- package/dist/chunk-STGSBIOL.js.map +1 -0
- package/dist/{chunk-SASMXDBE.js → chunk-UGE57OTX.js} +83 -35
- package/dist/chunk-UGE57OTX.js.map +1 -0
- package/dist/{chunk-6XSJY3TC.js → chunk-VHXOCDZ2.js} +3 -3
- package/dist/{chunk-6XSJY3TC.js.map → chunk-VHXOCDZ2.js.map} +1 -1
- package/dist/{chunk-7KLJZFBY.js → chunk-YPWFGFQX.js} +3 -3
- package/dist/{chunk-7KLJZFBY.js.map → chunk-YPWFGFQX.js.map} +1 -1
- package/dist/index.d.ts +261 -42
- package/dist/index.js +786 -124
- package/dist/index.js.map +1 -1
- package/dist/nd/index.d.ts +2 -1
- package/dist/nd/index.js +44 -29
- package/dist/nd/index.js.map +1 -1
- package/dist/tessellate/index.js +3 -3
- package/dist/warp/index.d.ts +7 -5
- package/dist/warp/index.js +145 -13
- package/dist/warp/index.js.map +1 -1
- package/package.json +1 -1
- package/dist/chunk-HGF5OVEN.js.map +0 -1
- package/dist/chunk-J2O2B54M.js.map +0 -1
- package/dist/chunk-SASMXDBE.js.map +0 -1
package/dist/index.js
CHANGED
|
@@ -1,21 +1,21 @@
|
|
|
1
|
-
import { carryCorners, evalSeg, parseSubpaths, keepCorners, anchorRange, subSeg, writePath, derivSeg } from './chunk-
|
|
2
|
-
export { CORNER_RESTORE_TOLERANCE, cornerTraces, keepCorners, restoreCorners } from './chunk-
|
|
1
|
+
import { carryCorners, evalSeg, parseSubpaths, keepCorners, anchorRange, subSeg, writePath, derivSeg } from './chunk-YPWFGFQX.js';
|
|
2
|
+
export { CORNER_RESTORE_TOLERANCE, cornerTraces, keepCorners, restoreCorners } from './chunk-YPWFGFQX.js';
|
|
3
3
|
import { PORT_REACH, portControls, portCurvePoints } from './chunk-YQADYJJ7.js';
|
|
4
4
|
export { PORT_REACH } from './chunk-YQADYJJ7.js';
|
|
5
5
|
import { cubicDerivativeAt, cubicCurvatureAt } from './chunk-UI3FUWXM.js';
|
|
6
6
|
export { SPIRO_KINDS, combTeeth, cubicCurvatureAt, cubicCurvatureSource, cubicDerivativeAt, curvatureComb, eulerAngleAt, eulerCurvatureAt, eulerCurvatureDerivativesAt, eulerPointAt, fitEulerSegment, isSpiroKind, placeEulerSegment, solveSpiro, spiroContoursFromControl, spiroContoursOf, spiroControlPath, spiroToPath } from './chunk-UI3FUWXM.js';
|
|
7
|
-
import { extractPolylines } from './chunk-
|
|
8
|
-
import { boundsOfPath, fitPointRun, keepCorner } from './chunk-
|
|
9
|
-
export { boundsOfPath } from './chunk-
|
|
7
|
+
import { extractPolylines } from './chunk-VHXOCDZ2.js';
|
|
8
|
+
import { boundsOfPath, fitPointRun, keepCorner } from './chunk-STGSBIOL.js';
|
|
9
|
+
export { boundsOfPath } from './chunk-STGSBIOL.js';
|
|
10
10
|
import { countPathAnchors, pathToAnchors, anchorsToPath, isAnchorSmooth, segmentEnds, isStraightSegment, nearestSegmentT } from './chunk-Q66NA2FU.js';
|
|
11
11
|
export { anchorsToPath, countPathAnchors, isAnchorSmooth, isStraightSegment, nearestSegmentT, pathToAnchors, segmentEnds } from './chunk-Q66NA2FU.js';
|
|
12
|
-
import {
|
|
12
|
+
import { nearestOnLine, nearestOnPath, nearestOnQuadratic, nearestOnCubic } from './chunk-CE7IY7TB.js';
|
|
13
13
|
export { nearestOnCubic, nearestOnLine, nearestOnPath, nearestOnQuadratic } from './chunk-CE7IY7TB.js';
|
|
14
|
-
import {
|
|
15
|
-
export { CORNER_FIT_TOLERANCE, CORNER_KEYWORDS, DEFAULT_FLATTEN_TOLERANCE, cornerCenter, cornerCuts, cornerFrameAt, cornerFrames, cornerKeywordOf, cornerShapeK, cornerShapePieces, cornerShapePoint, defaultOpenFill, fillsOpenSubpaths, fitCubicWithTangents, flattenCubic, flattenCubicWithArcLen, flattenQuadratic, flattenQuadraticWithArcLen, isCornerShape, nextCornerShape, pointInPolygon, pointSegmentDist2, remapPerAnchor, resolveCorners, resolveCornersWithSources, schneiderFit, segmentsCross, setDefaultOpenFill } from './chunk-
|
|
16
|
-
import { SINGULAR_RATIO } from './chunk-755Y4YZS.js';
|
|
14
|
+
import { resolveCorners, pointInPolygon, cornerCuts, fillsOpenSubpaths, schneiderFit, fitCubicWithTangents, flattenCubic, pointSegmentDist2, flattenQuadratic, DEFAULT_FLATTEN_TOLERANCE, segmentsCross } from './chunk-2PKPKU4P.js';
|
|
15
|
+
export { CORNER_FIT_TOLERANCE, CORNER_KEYWORDS, DEFAULT_FLATTEN_TOLERANCE, cornerCenter, cornerCuts, cornerFrameAt, cornerFrames, cornerKeywordOf, cornerShapeK, cornerShapePieces, cornerShapePoint, defaultOpenFill, fillsOpenSubpaths, fitCubicWithTangents, flattenCubic, flattenCubicWithArcLen, flattenQuadratic, flattenQuadraticWithArcLen, isCornerShape, nextCornerShape, pointInPolygon, pointSegmentDist2, remapPerAnchor, resolveCorners, resolveCornersWithSources, schneiderFit, segmentsCross, setDefaultOpenFill } from './chunk-2PKPKU4P.js';
|
|
16
|
+
import { SINGULAR_RATIO, clamp } from './chunk-755Y4YZS.js';
|
|
17
17
|
export { EPS, approxEq, clamp, cross, dot, len2, sign, sub } from './chunk-755Y4YZS.js';
|
|
18
|
-
import {
|
|
18
|
+
import { PATH_M, PATH_L, PATH_C, PATH_Q, PATH_Z, PathBuilder, withCorner, scaleCorners, forEachSegment, pathCommandCoordCount, withoutCorners, elevateQuadraticToCubic, cubicEvalAt, cubicPointAt, splitCubicAtT, cubicAxisExtremaTs, rectToContour, splitCubicAt } from './chunk-B53BERKG.js';
|
|
19
19
|
export { PATH_C, PATH_CMD_LENGTHS, PATH_COMMANDS, PATH_L, PATH_M, PATH_Q, PATH_Z, PathBuilder, asPolygonPath, boundsOfCoords, boxContainsBox, boxContainsPoint, compactCorners, cornerAt, cornerSpecAt, cubicAxisExtremaTs, cubicBounds, cubicEvalAt, cubicPointAt, elevateQuadraticToCubic, ellipsePath, forEachSegment, hasCorners, lerpCorners, linePath, pathCommandCoordCount, polygonFromPoints, polylineFromPoints, quadraticEvalAt, rectPath, rectToContour, regularPolygonPath, scaleCorners, splitCubicAt, splitCubicAtT, splitLineAt, splitQuadraticAt, starPath, unionBox, withCorner, withoutCorners } from './chunk-B53BERKG.js';
|
|
20
20
|
|
|
21
21
|
// src/view.ts
|
|
@@ -82,6 +82,516 @@ function transformCoords(coords, m) {
|
|
|
82
82
|
return out;
|
|
83
83
|
}
|
|
84
84
|
|
|
85
|
+
// src/track/shape.ts
|
|
86
|
+
function lineShape(from, to) {
|
|
87
|
+
const dx2 = to.x - from.x;
|
|
88
|
+
const dy2 = to.y - from.y;
|
|
89
|
+
const length2 = Math.hypot(dx2, dy2);
|
|
90
|
+
const tangent = length2 === 0 ? { x: 1, y: 0 } : { x: dx2 / length2, y: dy2 / length2 };
|
|
91
|
+
const normal2 = { x: tangent.y, y: -tangent.x };
|
|
92
|
+
return {
|
|
93
|
+
kind: "line",
|
|
94
|
+
length: length2,
|
|
95
|
+
closed: false,
|
|
96
|
+
at(f) {
|
|
97
|
+
const t = clamp(f, 0, 1);
|
|
98
|
+
return {
|
|
99
|
+
point: { x: from.x + t * dx2, y: from.y + t * dy2 },
|
|
100
|
+
tangent: { ...tangent },
|
|
101
|
+
normal: { ...normal2 }
|
|
102
|
+
};
|
|
103
|
+
},
|
|
104
|
+
nearest(p, options) {
|
|
105
|
+
const n = nearestOnLine(p.x, p.y, from.x, from.y, to.x, to.y);
|
|
106
|
+
if (!options?.open || length2 === 0) return { f: n.t, dist: n.dist };
|
|
107
|
+
const f = ((p.x - from.x) * dx2 + (p.y - from.y) * dy2) / (length2 * length2);
|
|
108
|
+
return { f, dist: n.dist };
|
|
109
|
+
}
|
|
110
|
+
};
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
// src/track/arc.ts
|
|
114
|
+
var RAD = Math.PI / 180;
|
|
115
|
+
var mod360 = (a) => (a % 360 + 360) % 360;
|
|
116
|
+
function arcShape(spec) {
|
|
117
|
+
const { center, radius, start } = spec;
|
|
118
|
+
const sign2 = spec.direction === "ccw" ? -1 : 1;
|
|
119
|
+
const raw = mod360(sign2 * (spec.end - start));
|
|
120
|
+
const sweep = raw === 0 ? 360 : raw;
|
|
121
|
+
const length2 = radius * sweep * RAD;
|
|
122
|
+
const at2 = (f) => {
|
|
123
|
+
const t = clamp(f, 0, 1);
|
|
124
|
+
const theta = (start + sign2 * t * sweep) * RAD;
|
|
125
|
+
const s = Math.sin(theta);
|
|
126
|
+
const c = Math.cos(theta);
|
|
127
|
+
return {
|
|
128
|
+
point: { x: center.x + radius * s, y: center.y - radius * c },
|
|
129
|
+
tangent: { x: sign2 * c, y: sign2 * s },
|
|
130
|
+
normal: { x: s, y: -c }
|
|
131
|
+
};
|
|
132
|
+
};
|
|
133
|
+
const shape = {
|
|
134
|
+
kind: "arc",
|
|
135
|
+
length: length2,
|
|
136
|
+
closed: raw === 0,
|
|
137
|
+
at: at2,
|
|
138
|
+
nearest(p, options) {
|
|
139
|
+
const dx2 = p.x - center.x;
|
|
140
|
+
const dy2 = p.y - center.y;
|
|
141
|
+
const angle = dx2 === 0 && dy2 === 0 ? 0 : Math.atan2(dx2, -dy2) / RAD;
|
|
142
|
+
const offset2 = mod360(sign2 * (angle - start));
|
|
143
|
+
let f;
|
|
144
|
+
if (offset2 <= sweep) f = offset2 / sweep;
|
|
145
|
+
else f = 360 - offset2 < offset2 - sweep ? 0 : 1;
|
|
146
|
+
const q = at2(f).point;
|
|
147
|
+
const dist = Math.hypot(p.x - q.x, p.y - q.y);
|
|
148
|
+
if (!options?.open) return { f, dist };
|
|
149
|
+
const mid = (options.about ?? 0.5) * sweep;
|
|
150
|
+
const unwrapped = mid + mod360(offset2 - mid + 180) - 180;
|
|
151
|
+
return { f: unwrapped / sweep, dist };
|
|
152
|
+
}
|
|
153
|
+
};
|
|
154
|
+
Object.defineProperty(shape, "arc", { value: spec, enumerable: false });
|
|
155
|
+
return shape;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
// src/track/ellipse.ts
|
|
159
|
+
var RAD2 = Math.PI / 180;
|
|
160
|
+
var mod3602 = (a) => (a % 360 + 360) % 360;
|
|
161
|
+
var PIECES = 128;
|
|
162
|
+
var PANELS = 4;
|
|
163
|
+
var RING = 256;
|
|
164
|
+
var pointAt = ({ center, a, b, t0, sign: sign2 }, u) => {
|
|
165
|
+
const t = (t0 + sign2 * u) * RAD2;
|
|
166
|
+
return { x: center.x + a * Math.sin(t), y: center.y - b * Math.cos(t) };
|
|
167
|
+
};
|
|
168
|
+
var speed = ({ a, b, t0, sign: sign2 }, u) => {
|
|
169
|
+
const t = (t0 + sign2 * u) * RAD2;
|
|
170
|
+
return Math.hypot(a * Math.cos(t), b * Math.sin(t)) * RAD2;
|
|
171
|
+
};
|
|
172
|
+
function lengthTable(e, sweep) {
|
|
173
|
+
const simpson = (u0, u1) => {
|
|
174
|
+
const h = (u1 - u0) / PANELS;
|
|
175
|
+
let sum = speed(e, u0) + speed(e, u1);
|
|
176
|
+
for (let i = 1; i < PANELS; i++) sum += speed(e, u0 + i * h) * (i % 2 ? 4 : 2);
|
|
177
|
+
return sum * h / 3;
|
|
178
|
+
};
|
|
179
|
+
const du = sweep / PIECES;
|
|
180
|
+
const table = [0];
|
|
181
|
+
for (let i = 0; i < PIECES; i++) table.push(table[i] + simpson(i * du, (i + 1) * du));
|
|
182
|
+
const length2 = table[PIECES];
|
|
183
|
+
const lengthTo = (u) => {
|
|
184
|
+
if (u >= 0 && u <= sweep) {
|
|
185
|
+
const i = Math.min(PIECES - 1, Math.floor(u / du));
|
|
186
|
+
return table[i] + simpson(i * du, u);
|
|
187
|
+
}
|
|
188
|
+
const n = Math.max(1, Math.ceil(Math.abs(u) / du));
|
|
189
|
+
let s = 0;
|
|
190
|
+
for (let i = 0; i < n; i++) s += simpson(u * i / n, u * (i + 1) / n);
|
|
191
|
+
return s;
|
|
192
|
+
};
|
|
193
|
+
const offsetAt = (f) => {
|
|
194
|
+
if (length2 === 0) return f * sweep;
|
|
195
|
+
const s = f * length2;
|
|
196
|
+
let lo = 0;
|
|
197
|
+
let hi = PIECES;
|
|
198
|
+
while (hi - lo > 1) {
|
|
199
|
+
const mid = lo + hi >> 1;
|
|
200
|
+
if (table[mid] <= s) lo = mid;
|
|
201
|
+
else hi = mid;
|
|
202
|
+
}
|
|
203
|
+
const piece = table[lo + 1] - table[lo];
|
|
204
|
+
let u = (lo + (piece === 0 ? 0 : (s - table[lo]) / piece)) * du;
|
|
205
|
+
for (let i = 0; i < 3; i++) {
|
|
206
|
+
const v = speed(e, u);
|
|
207
|
+
if (!(v > 0)) break;
|
|
208
|
+
u -= (lengthTo(u) - s) / v;
|
|
209
|
+
}
|
|
210
|
+
return u;
|
|
211
|
+
};
|
|
212
|
+
return { length: length2, lengthTo, offsetAt };
|
|
213
|
+
}
|
|
214
|
+
var dist2 = (e, p, u) => {
|
|
215
|
+
const q = pointAt(e, u);
|
|
216
|
+
return (p.x - q.x) ** 2 + (p.y - q.y) ** 2;
|
|
217
|
+
};
|
|
218
|
+
function footBetween(e, p, lo, hi, n, late) {
|
|
219
|
+
let best = lo;
|
|
220
|
+
let bestD = dist2(e, p, lo);
|
|
221
|
+
const step = (hi - lo) / n;
|
|
222
|
+
for (let i = 1; i <= n; i++) {
|
|
223
|
+
const v = i === n ? hi : lo + i * step;
|
|
224
|
+
const d = dist2(e, p, v);
|
|
225
|
+
if (late ? d <= bestD * (1 + 1e-12) : d < bestD * (1 - 1e-12)) [best, bestD] = [v, d];
|
|
226
|
+
}
|
|
227
|
+
const { a, b, t0, sign: sign2 } = e;
|
|
228
|
+
const dx2 = p.x - e.center.x;
|
|
229
|
+
const dy2 = p.y - e.center.y;
|
|
230
|
+
let u = best;
|
|
231
|
+
for (let i = 0; i < 12; i++) {
|
|
232
|
+
const t = (t0 + sign2 * u) * RAD2;
|
|
233
|
+
const s = Math.sin(t);
|
|
234
|
+
const c = Math.cos(t);
|
|
235
|
+
const g = (a * a - b * b) * s * c - dx2 * a * c - dy2 * b * s;
|
|
236
|
+
const g1 = (a * a - b * b) * (c * c - s * s) + dx2 * a * s - dy2 * b * c;
|
|
237
|
+
if (!(g1 > 0)) break;
|
|
238
|
+
const next = clamp(u - sign2 * g / g1 / RAD2, Math.max(lo, best - step), Math.min(hi, best + step));
|
|
239
|
+
if (Math.abs(next - u) < 1e-12) break;
|
|
240
|
+
u = next;
|
|
241
|
+
}
|
|
242
|
+
return dist2(e, p, u) <= bestD ? u : best;
|
|
243
|
+
}
|
|
244
|
+
function ellipseShape(spec) {
|
|
245
|
+
const { center, radiusX: a, radiusY: b } = spec;
|
|
246
|
+
const sign2 = spec.direction === "ccw" ? -1 : 1;
|
|
247
|
+
const param = (seenDeg) => Math.atan2(b * Math.sin(seenDeg * RAD2), a * Math.cos(seenDeg * RAD2)) / RAD2;
|
|
248
|
+
const e = { center, a, b, t0: param(spec.start), sign: sign2 };
|
|
249
|
+
const raw = mod3602(sign2 * (param(spec.end) - e.t0));
|
|
250
|
+
const sweep = raw === 0 ? 360 : raw;
|
|
251
|
+
const { length: length2, lengthTo, offsetAt } = lengthTable(e, sweep);
|
|
252
|
+
const at2 = (f) => {
|
|
253
|
+
const u = offsetAt(clamp(f, 0, 1));
|
|
254
|
+
const t = (e.t0 + sign2 * u) * RAD2;
|
|
255
|
+
const s = Math.sin(t);
|
|
256
|
+
const c = Math.cos(t);
|
|
257
|
+
const k = Math.hypot(a * c, b * s);
|
|
258
|
+
const tangent = k === 0 ? { x: sign2 * c, y: sign2 * s } : { x: sign2 * a * c / k, y: sign2 * b * s / k };
|
|
259
|
+
const normal2 = k === 0 ? { x: s, y: -c } : { x: b * s / k, y: -a * c / k };
|
|
260
|
+
return { point: pointAt(e, u), tangent, normal: normal2 };
|
|
261
|
+
};
|
|
262
|
+
const shape = {
|
|
263
|
+
kind: "ellipse",
|
|
264
|
+
length: length2,
|
|
265
|
+
closed: raw === 0,
|
|
266
|
+
at: at2,
|
|
267
|
+
nearest(p, options) {
|
|
268
|
+
const u = footBetween(e, p, 0, sweep, PIECES, true);
|
|
269
|
+
const f = length2 === 0 ? 0 : clamp(lengthTo(u) / length2, 0, 1);
|
|
270
|
+
const dist = Math.sqrt(dist2(e, p, u));
|
|
271
|
+
if (!options?.open || length2 === 0) return { f, dist };
|
|
272
|
+
const mid = offsetAt(options.about ?? 0.5);
|
|
273
|
+
return { f: lengthTo(footBetween(e, p, mid - 180, mid + 180, RING, false)) / length2, dist };
|
|
274
|
+
}
|
|
275
|
+
};
|
|
276
|
+
Object.defineProperty(shape, "ellipse", { value: spec, enumerable: false });
|
|
277
|
+
return shape;
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
// src/paths/pathMeasure.ts
|
|
281
|
+
function measurePath(path, opts = {}) {
|
|
282
|
+
const polylines = extractPolylines(
|
|
283
|
+
path,
|
|
284
|
+
opts.flattenTolerance === void 0 ? {} : { flattenTolerance: opts.flattenTolerance }
|
|
285
|
+
);
|
|
286
|
+
const segments = [];
|
|
287
|
+
let total = 0;
|
|
288
|
+
let first;
|
|
289
|
+
let contours = 0;
|
|
290
|
+
let lastClosed = false;
|
|
291
|
+
for (const pl of polylines) {
|
|
292
|
+
const count = pl.points.length / 2;
|
|
293
|
+
if (count === 0) continue;
|
|
294
|
+
contours++;
|
|
295
|
+
lastClosed = pl.closed;
|
|
296
|
+
first ??= { x: pl.points[0], y: pl.points[1] };
|
|
297
|
+
const startIndex = segments.length;
|
|
298
|
+
const last = pl.closed ? count : count - 1;
|
|
299
|
+
for (let i = 0; i < last; i++) {
|
|
300
|
+
const j = (i + 1) % count;
|
|
301
|
+
const ax = pl.points[i * 2];
|
|
302
|
+
const ay = pl.points[i * 2 + 1];
|
|
303
|
+
const bx = pl.points[j * 2];
|
|
304
|
+
const by = pl.points[j * 2 + 1];
|
|
305
|
+
const len = Math.hypot(bx - ax, by - ay);
|
|
306
|
+
if (len === 0) continue;
|
|
307
|
+
const tx = (bx - ax) / len;
|
|
308
|
+
const ty = (by - ay) / len;
|
|
309
|
+
segments.push({ ax, ay, bx, by, start: total, len, tx, ty, t0x: tx, t0y: ty, t1x: tx, t1y: ty });
|
|
310
|
+
total += len;
|
|
311
|
+
}
|
|
312
|
+
smoothVertexHeadings(segments, startIndex, pl.closed, opts.keepCorners ? opts.flattenTolerance ?? 0.5 : null);
|
|
313
|
+
}
|
|
314
|
+
if (first === void 0) return null;
|
|
315
|
+
const origin = first;
|
|
316
|
+
const closed = contours === 1 && lastClosed;
|
|
317
|
+
if (segments.length === 0) {
|
|
318
|
+
return {
|
|
319
|
+
length: 0,
|
|
320
|
+
closed,
|
|
321
|
+
at: () => ({ point: { ...origin }, tangent: { x: 1, y: 0 } }),
|
|
322
|
+
nearest: (p) => ({
|
|
323
|
+
s: 0,
|
|
324
|
+
dist: Math.hypot(p.x - origin.x, p.y - origin.y),
|
|
325
|
+
point: { ...origin },
|
|
326
|
+
tangent: { x: 1, y: 0 }
|
|
327
|
+
})
|
|
328
|
+
};
|
|
329
|
+
}
|
|
330
|
+
return {
|
|
331
|
+
length: total,
|
|
332
|
+
closed,
|
|
333
|
+
at(s, smooth = false) {
|
|
334
|
+
const target = Math.min(Math.max(s, 0), total);
|
|
335
|
+
const seg = segments[segmentAt(segments, target)];
|
|
336
|
+
const u = Math.min(Math.max((target - seg.start) / seg.len, 0), 1);
|
|
337
|
+
const point = { x: seg.ax + (seg.bx - seg.ax) * u, y: seg.ay + (seg.by - seg.ay) * u };
|
|
338
|
+
if (!smooth) return { point, tangent: { x: seg.tx, y: seg.ty } };
|
|
339
|
+
const x = seg.t0x + (seg.t1x - seg.t0x) * u;
|
|
340
|
+
const y = seg.t0y + (seg.t1y - seg.t0y) * u;
|
|
341
|
+
const l = Math.hypot(x, y);
|
|
342
|
+
return { point, tangent: l === 0 ? { x: seg.tx, y: seg.ty } : { x: x / l, y: y / l } };
|
|
343
|
+
},
|
|
344
|
+
nearest(p) {
|
|
345
|
+
let best = segments[0];
|
|
346
|
+
let bestU = 0;
|
|
347
|
+
let bestDist = Infinity;
|
|
348
|
+
for (const seg of segments) {
|
|
349
|
+
const u = Math.min(Math.max(((p.x - seg.ax) * seg.tx + (p.y - seg.ay) * seg.ty) / seg.len, 0), 1);
|
|
350
|
+
const d = Math.hypot(p.x - (seg.ax + (seg.bx - seg.ax) * u), p.y - (seg.ay + (seg.by - seg.ay) * u));
|
|
351
|
+
if (d < bestDist) {
|
|
352
|
+
best = seg;
|
|
353
|
+
bestU = u;
|
|
354
|
+
bestDist = d;
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
return {
|
|
358
|
+
s: best.start + best.len * bestU,
|
|
359
|
+
dist: bestDist,
|
|
360
|
+
point: { x: best.ax + (best.bx - best.ax) * bestU, y: best.ay + (best.by - best.ay) * bestU },
|
|
361
|
+
tangent: { x: best.tx, y: best.ty }
|
|
362
|
+
};
|
|
363
|
+
}
|
|
364
|
+
};
|
|
365
|
+
}
|
|
366
|
+
function smoothVertexHeadings(segments, from, closed, cornerTolerance) {
|
|
367
|
+
const n = segments.length - from;
|
|
368
|
+
if (n < 2) return;
|
|
369
|
+
const pairs = closed ? n : n - 1;
|
|
370
|
+
for (let k = 0; k < pairs; k++) {
|
|
371
|
+
const a = segments[from + k];
|
|
372
|
+
const b = segments[from + (k + 1) % n];
|
|
373
|
+
if (cornerTolerance !== null && isCorner(a, b, cornerTolerance)) continue;
|
|
374
|
+
let x = a.tx + b.tx;
|
|
375
|
+
let y = a.ty + b.ty;
|
|
376
|
+
const l = Math.hypot(x, y);
|
|
377
|
+
if (l < 1e-9) continue;
|
|
378
|
+
x /= l;
|
|
379
|
+
y /= l;
|
|
380
|
+
a.t1x = x;
|
|
381
|
+
a.t1y = y;
|
|
382
|
+
b.t0x = x;
|
|
383
|
+
b.t0y = y;
|
|
384
|
+
}
|
|
385
|
+
}
|
|
386
|
+
function isCorner(a, b, tolerance) {
|
|
387
|
+
const turn = Math.acos(Math.min(1, Math.max(-1, a.tx * b.tx + a.ty * b.ty)));
|
|
388
|
+
return turn > 16 * tolerance / Math.min(a.len, b.len);
|
|
389
|
+
}
|
|
390
|
+
function segmentAt(segments, s) {
|
|
391
|
+
let lo = 0;
|
|
392
|
+
let hi = segments.length - 1;
|
|
393
|
+
while (lo < hi) {
|
|
394
|
+
const mid = lo + hi + 1 >> 1;
|
|
395
|
+
if (segments[mid].start <= s) lo = mid;
|
|
396
|
+
else hi = mid - 1;
|
|
397
|
+
}
|
|
398
|
+
return lo;
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
// src/track/path.ts
|
|
402
|
+
function pathShape(path, options = {}) {
|
|
403
|
+
const measure2 = measurePath(path, { ...options, keepCorners: true });
|
|
404
|
+
if (!measure2) return null;
|
|
405
|
+
const { length: length2, closed } = measure2;
|
|
406
|
+
const shape = {
|
|
407
|
+
kind: "path",
|
|
408
|
+
length: length2,
|
|
409
|
+
closed,
|
|
410
|
+
at(f) {
|
|
411
|
+
const { point, tangent } = measure2.at(clamp(f, 0, 1) * length2, true);
|
|
412
|
+
return { point, tangent, normal: { x: tangent.y, y: -tangent.x } };
|
|
413
|
+
},
|
|
414
|
+
nearest(p, opts) {
|
|
415
|
+
const n = measure2.nearest(p);
|
|
416
|
+
const f = length2 === 0 ? 0 : n.s / length2;
|
|
417
|
+
if (!opts?.open || !closed || length2 === 0) return { f, dist: n.dist };
|
|
418
|
+
const about = opts.about ?? 0.5;
|
|
419
|
+
const unwrapped = [f - 1, f + 1].reduce((best, g) => Math.abs(g - about) < Math.abs(best - about) ? g : best, f);
|
|
420
|
+
return { f: unwrapped, dist: n.dist };
|
|
421
|
+
}
|
|
422
|
+
};
|
|
423
|
+
Object.defineProperty(shape, "path", { value: path, enumerable: false });
|
|
424
|
+
return shape;
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
// src/track/run.ts
|
|
428
|
+
function trackRun(shape, from = 0, to = 1, options = {}) {
|
|
429
|
+
const { side = "auto", about = (from + to) / 2 } = options;
|
|
430
|
+
const mid = shape.at(about);
|
|
431
|
+
const flipped = side === "auto" ? mid.normal.y > 1e-9 : side === "flipped";
|
|
432
|
+
const sgn = flipped ? -1 : 1;
|
|
433
|
+
const reading = (n) => ({ x: -(sgn * n.y), y: sgn * n.x });
|
|
434
|
+
const r0 = reading(mid.normal);
|
|
435
|
+
const reversed3 = r0.x * mid.tangent.x + r0.y * mid.tangent.y < 0;
|
|
436
|
+
const span = to - from;
|
|
437
|
+
const lo = Math.min(from, to);
|
|
438
|
+
const hi = Math.max(from, to);
|
|
439
|
+
const toF = (g) => reversed3 ? to - g * span : from + g * span;
|
|
440
|
+
const at2 = (g) => {
|
|
441
|
+
const s = shape.at(toF(clamp(g, 0, 1)));
|
|
442
|
+
const dir = reading(s.normal);
|
|
443
|
+
const angle = Math.atan2(dir.y, dir.x) * 180 / Math.PI;
|
|
444
|
+
return {
|
|
445
|
+
point: s.point,
|
|
446
|
+
tangent: dir,
|
|
447
|
+
normal: { x: sgn * s.normal.x, y: sgn * s.normal.y },
|
|
448
|
+
angle: angle === 0 ? 0 : angle === -180 ? 180 : angle
|
|
449
|
+
};
|
|
450
|
+
};
|
|
451
|
+
const toG = (f) => span === 0 ? 0 : reversed3 ? (to - f) / span : (f - from) / span;
|
|
452
|
+
const nearest = (p, options2) => {
|
|
453
|
+
const f = clamp(shape.nearest(p).f, lo, hi);
|
|
454
|
+
const q = shape.at(f).point;
|
|
455
|
+
const dist = Math.hypot(p.x - q.x, p.y - q.y);
|
|
456
|
+
if (!options2?.open) return { f: toG(f), dist };
|
|
457
|
+
return { f: toG(shape.nearest(p, { open: true, about: toF(options2.about ?? 0.5) }).f), dist };
|
|
458
|
+
};
|
|
459
|
+
return {
|
|
460
|
+
kind: shape.kind,
|
|
461
|
+
length: shape.length * Math.abs(span),
|
|
462
|
+
closed: shape.closed && Math.abs(span) === 1,
|
|
463
|
+
shape,
|
|
464
|
+
from,
|
|
465
|
+
to,
|
|
466
|
+
flipped,
|
|
467
|
+
reversed: reversed3,
|
|
468
|
+
at: at2,
|
|
469
|
+
nearest
|
|
470
|
+
};
|
|
471
|
+
}
|
|
472
|
+
|
|
473
|
+
// src/paths/pathToD.ts
|
|
474
|
+
var num = (n) => Number.isFinite(n) ? String(Math.round(n * 1e6) / 1e6) : "0";
|
|
475
|
+
function pathToD(given) {
|
|
476
|
+
const path = resolveCorners(given);
|
|
477
|
+
if (path.kind === "rect") {
|
|
478
|
+
const { x, y, width, height } = path;
|
|
479
|
+
return `M${num(x)} ${num(y)}h${num(width)}v${num(height)}h${num(-width)}Z`;
|
|
480
|
+
}
|
|
481
|
+
const cmds = path.commands;
|
|
482
|
+
const xs = path.coords;
|
|
483
|
+
const out = [];
|
|
484
|
+
let ci = 0;
|
|
485
|
+
for (let i = 0; i < cmds.length; i++) {
|
|
486
|
+
const c = cmds[i];
|
|
487
|
+
if (c === PATH_M) {
|
|
488
|
+
out.push(`M${num(xs[ci])} ${num(xs[ci + 1])}`);
|
|
489
|
+
ci += 2;
|
|
490
|
+
} else if (c === PATH_L) {
|
|
491
|
+
out.push(`L${num(xs[ci])} ${num(xs[ci + 1])}`);
|
|
492
|
+
ci += 2;
|
|
493
|
+
} else if (c === PATH_C) {
|
|
494
|
+
out.push(
|
|
495
|
+
`C${num(xs[ci])} ${num(xs[ci + 1])} ${num(xs[ci + 2])} ${num(xs[ci + 3])} ${num(xs[ci + 4])} ${num(xs[ci + 5])}`
|
|
496
|
+
);
|
|
497
|
+
ci += 6;
|
|
498
|
+
} else if (c === PATH_Q) {
|
|
499
|
+
out.push(`Q${num(xs[ci])} ${num(xs[ci + 1])} ${num(xs[ci + 2])} ${num(xs[ci + 3])}`);
|
|
500
|
+
ci += 4;
|
|
501
|
+
} else if (c === PATH_Z) {
|
|
502
|
+
out.push("Z");
|
|
503
|
+
}
|
|
504
|
+
}
|
|
505
|
+
return out.join("");
|
|
506
|
+
}
|
|
507
|
+
|
|
508
|
+
// src/track/pathData.ts
|
|
509
|
+
var SAMPLES = 64;
|
|
510
|
+
var num2 = (v) => {
|
|
511
|
+
const r = Math.round(v * 1e4) / 1e4;
|
|
512
|
+
return String(r === 0 ? 0 : r);
|
|
513
|
+
};
|
|
514
|
+
var pt = (p) => `${num2(p.x)} ${num2(p.y)}`;
|
|
515
|
+
var isArc = (s) => s.kind === "arc" && "arc" in s;
|
|
516
|
+
var isPath = (s) => s.kind === "path" && "path" in s;
|
|
517
|
+
var isEllipse = (s) => s.kind === "ellipse" && "ellipse" in s;
|
|
518
|
+
function arcsD(a, b, m, rx, ry, turn, sweep) {
|
|
519
|
+
const r = `${num2(rx)} ${num2(ry)}`;
|
|
520
|
+
if (turn >= 2 * Math.PI - 1e-9) return `M ${pt(a)} A ${r} 0 0 ${sweep} ${pt(m())} A ${r} 0 0 ${sweep} ${pt(b)}`;
|
|
521
|
+
return `M ${pt(a)} A ${r} 0 ${turn > Math.PI + 1e-9 ? 1 : 0} ${sweep} ${pt(b)}`;
|
|
522
|
+
}
|
|
523
|
+
function trackShapeD(shape, from = 0, to = 1) {
|
|
524
|
+
if (isPath(shape) && from === 0 && to === 1) return pathToD(shape.path);
|
|
525
|
+
const a = shape.at(from).point;
|
|
526
|
+
const b = shape.at(to).point;
|
|
527
|
+
if (shape.kind === "line") return `M ${pt(a)} L ${pt(b)}`;
|
|
528
|
+
const m = () => shape.at((from + to) / 2).point;
|
|
529
|
+
if (isArc(shape)) {
|
|
530
|
+
const { radius, direction } = shape.arc;
|
|
531
|
+
const turn = shape.length * Math.abs(to - from) / radius;
|
|
532
|
+
return arcsD(a, b, m, radius, radius, turn, direction === "ccw" === to < from ? 1 : 0);
|
|
533
|
+
}
|
|
534
|
+
if (isEllipse(shape)) {
|
|
535
|
+
const { radiusX, radiusY, direction, center } = shape.ellipse;
|
|
536
|
+
const param = (p) => Math.atan2((p.x - center.x) / radiusX, (center.y - p.y) / radiusY);
|
|
537
|
+
const sign2 = direction === "ccw" ? -1 : 1;
|
|
538
|
+
const whole = (shape.ellipse.start - shape.ellipse.end) % 360 === 0 && Math.abs(to - from) >= 1 - 1e-12;
|
|
539
|
+
const raw = (sign2 * Math.sign(to - from) * (param(b) - param(a)) + 4 * Math.PI) % (2 * Math.PI);
|
|
540
|
+
return arcsD(a, b, m, radiusX, radiusY, whole ? 2 * Math.PI : raw, direction === "ccw" === to < from ? 1 : 0);
|
|
541
|
+
}
|
|
542
|
+
let d = `M ${pt(a)}`;
|
|
543
|
+
for (let i = 1; i <= SAMPLES; i++)
|
|
544
|
+
d += ` L ${pt(shape.at(from + (to - from) * i / SAMPLES).point)}`;
|
|
545
|
+
return d;
|
|
546
|
+
}
|
|
547
|
+
|
|
548
|
+
// src/track/place.ts
|
|
549
|
+
var clipOf = (lo, hi, start, end, hair = 0) => lo >= start - hair && hi <= end + hair ? "none" : hi <= start + hair || lo >= end - hair ? "past" : "part";
|
|
550
|
+
function runPlaces(lengths, gap, room, align, offset2, at2) {
|
|
551
|
+
const total = runLength(lengths, gap);
|
|
552
|
+
const fits = at2 === void 0 && total <= room;
|
|
553
|
+
const lead = align === "start" ? 0 : align === "end" ? total : total / 2;
|
|
554
|
+
let lo = at2 !== void 0 ? at2 - offset2 - lead : !fits ? -offset2 : align === "start" ? 0 : align === "end" ? room - total : (room - total) / 2;
|
|
555
|
+
return lengths.map((length2) => {
|
|
556
|
+
const start = lo;
|
|
557
|
+
const hi = start + length2;
|
|
558
|
+
lo = hi + gap;
|
|
559
|
+
return { center: start + length2 / 2, clipped: fits ? "none" : clipOf(start, hi, 0, room) };
|
|
560
|
+
});
|
|
561
|
+
}
|
|
562
|
+
function runLength(lengths, gap) {
|
|
563
|
+
return lengths.reduce((sum, length2, i) => sum + length2 + (i > 0 ? gap : 0), 0);
|
|
564
|
+
}
|
|
565
|
+
function runStationAt(run2, distance, shift = 0) {
|
|
566
|
+
const { length: length2 } = run2;
|
|
567
|
+
const place = run2.at(length2 === 0 ? 0 : distance / length2);
|
|
568
|
+
const past = distance < 0 ? distance : distance > length2 ? distance - length2 : 0;
|
|
569
|
+
const { point, tangent, normal: normal2 } = place;
|
|
570
|
+
return {
|
|
571
|
+
...place,
|
|
572
|
+
point: { x: point.x + past * tangent.x + shift * normal2.x, y: point.y + past * tangent.y + shift * normal2.y }
|
|
573
|
+
};
|
|
574
|
+
}
|
|
575
|
+
function placeAlongRun(run2, lengths, options = {}) {
|
|
576
|
+
const { gap = 0, align = "center", offset: offset2 = 0, at: at2, shift = 0, wrap = false, past = "extend", order = "reading" } = options;
|
|
577
|
+
const { length: length2 } = run2;
|
|
578
|
+
const mirror = order === "track" && run2.reversed;
|
|
579
|
+
const spots = runPlaces(lengths, gap, length2, align, offset2, at2);
|
|
580
|
+
const lap = wrap && run2.closed && spots.length > 0 ? wrapWindow(spots[0], lengths[0], length2) : null;
|
|
581
|
+
return spots.map((spot, i) => {
|
|
582
|
+
const half = lengths[i] / 2;
|
|
583
|
+
const clipped = lap ? clipOf(spot.center - half, spot.center + half, lap.start, lap.end, lap.hair) : spot.clipped;
|
|
584
|
+
let distance = spot.center;
|
|
585
|
+
if (past === "hold" && clipped === "past") distance = clamp(distance, lap?.start ?? 0, lap?.end ?? length2);
|
|
586
|
+
if (lap && length2 > 0) distance = (distance % length2 + length2) % length2;
|
|
587
|
+
return { ...runStationAt(run2, mirror ? length2 - distance : distance, shift), center: spot.center, clipped };
|
|
588
|
+
});
|
|
589
|
+
}
|
|
590
|
+
function wrapWindow(first, firstLength, length2) {
|
|
591
|
+
const start = first.center - firstLength / 2;
|
|
592
|
+
return { start, end: start + length2, hair: 1e-9 * Math.max(1, length2) };
|
|
593
|
+
}
|
|
594
|
+
|
|
85
595
|
// src/portCurve2.ts
|
|
86
596
|
function portControls2(ax, ay, bx, by, outNormal, inNormal, reach = PORT_REACH) {
|
|
87
597
|
const [c1, c2] = portControls([ax, ay], [bx, by], outNormal, inNormal, reach);
|
|
@@ -270,7 +780,7 @@ function pointInPath(path, x, y, opts = {}) {
|
|
|
270
780
|
return x >= path.x && x <= path.x + path.width && y >= path.y && y <= path.y + path.height;
|
|
271
781
|
}
|
|
272
782
|
let crossings = 0;
|
|
273
|
-
let
|
|
783
|
+
let winding2 = 0;
|
|
274
784
|
for (const sub3 of closedSubpaths(path, opts)) {
|
|
275
785
|
const n = sub3.length;
|
|
276
786
|
for (let i = 0; i < n; i += 2) {
|
|
@@ -280,10 +790,10 @@ function pointInPath(path, x, y, opts = {}) {
|
|
|
280
790
|
const ax = sub3[i], bx = sub3[j];
|
|
281
791
|
if (x >= ax + (y - ay) / (by - ay) * (bx - ax)) continue;
|
|
282
792
|
crossings++;
|
|
283
|
-
|
|
793
|
+
winding2 += ay <= y ? 1 : -1;
|
|
284
794
|
}
|
|
285
795
|
}
|
|
286
|
-
return path.fillRule === "evenodd" ? (crossings & 1) === 1 :
|
|
796
|
+
return path.fillRule === "evenodd" ? (crossings & 1) === 1 : winding2 !== 0;
|
|
287
797
|
}
|
|
288
798
|
function strokeHitTest(path, x, y, threshold, opts = {}) {
|
|
289
799
|
const slop = opts.slop;
|
|
@@ -620,7 +1130,7 @@ function splitSubpaths(path) {
|
|
|
620
1130
|
let coordStart = 0;
|
|
621
1131
|
let anchorStart = 0;
|
|
622
1132
|
let anchors = 0;
|
|
623
|
-
const
|
|
1133
|
+
const emit2 = (end, coordEnd) => {
|
|
624
1134
|
const sub3 = { ...bare, commands: commands.slice(cmdStart, end), coords: coords.slice(coordStart, coordEnd) };
|
|
625
1135
|
const count = anchors - anchorStart;
|
|
626
1136
|
out.push(path.corners ? carryCorners(path, sub3, anchorRange(anchorStart, count)) : sub3);
|
|
@@ -628,13 +1138,13 @@ function splitSubpaths(path) {
|
|
|
628
1138
|
};
|
|
629
1139
|
forEachSegment(commands, coords, (cmd, ci, _px, _py, i) => {
|
|
630
1140
|
if (cmd === PATH_M && i > cmdStart) {
|
|
631
|
-
|
|
1141
|
+
emit2(i, ci);
|
|
632
1142
|
cmdStart = i;
|
|
633
1143
|
coordStart = ci;
|
|
634
1144
|
}
|
|
635
1145
|
if (cmd !== PATH_Z) anchors++;
|
|
636
1146
|
});
|
|
637
|
-
|
|
1147
|
+
emit2(commands.length, coords.length);
|
|
638
1148
|
return out;
|
|
639
1149
|
}
|
|
640
1150
|
|
|
@@ -812,8 +1322,8 @@ function reversePath(path) {
|
|
|
812
1322
|
anchor++;
|
|
813
1323
|
});
|
|
814
1324
|
flush(false);
|
|
815
|
-
const
|
|
816
|
-
return path.corners ? carryCorners(path,
|
|
1325
|
+
const reversed3 = { ...path, commands: new Uint8Array(cmds), coords: new Float32Array(out) };
|
|
1326
|
+
return path.corners ? carryCorners(path, reversed3, sources) : reversed3;
|
|
817
1327
|
}
|
|
818
1328
|
|
|
819
1329
|
// src/paths/pathFromD.ts
|
|
@@ -1074,9 +1584,9 @@ function arcToCubics(x1, y1, x2, y2, rx, ry, xAxisRotationDeg, largeArc, sweep,
|
|
|
1074
1584
|
rys = ryFixed * ryFixed;
|
|
1075
1585
|
}
|
|
1076
1586
|
const sign2 = largeArc === sweep ? -1 : 1;
|
|
1077
|
-
const
|
|
1587
|
+
const num3 = rxs * rys - rxs * y1ps - rys * x1ps;
|
|
1078
1588
|
const den = rxs * y1ps + rys * x1ps;
|
|
1079
|
-
const factor = sign2 * Math.sqrt(Math.max(0,
|
|
1589
|
+
const factor = sign2 * Math.sqrt(Math.max(0, num3 / den));
|
|
1080
1590
|
const cxp = factor * (rxFixed * y1p) / ryFixed;
|
|
1081
1591
|
const cyp = factor * -(ryFixed * x1p) / rxFixed;
|
|
1082
1592
|
const cx = cosPhi * cxp - sinPhi * cyp + (x1 + x2) / 2;
|
|
@@ -1124,92 +1634,6 @@ function arcToCubics(x1, y1, x2, y2, rx, ry, xAxisRotationDeg, largeArc, sweep,
|
|
|
1124
1634
|
}
|
|
1125
1635
|
}
|
|
1126
1636
|
|
|
1127
|
-
// src/paths/pathMeasure.ts
|
|
1128
|
-
function measurePath(path, opts = {}) {
|
|
1129
|
-
const polylines = extractPolylines(
|
|
1130
|
-
path,
|
|
1131
|
-
opts.flattenTolerance === void 0 ? {} : { flattenTolerance: opts.flattenTolerance }
|
|
1132
|
-
);
|
|
1133
|
-
const segments = [];
|
|
1134
|
-
let total = 0;
|
|
1135
|
-
let first;
|
|
1136
|
-
let contours = 0;
|
|
1137
|
-
let lastClosed = false;
|
|
1138
|
-
for (const pl of polylines) {
|
|
1139
|
-
const count = pl.points.length / 2;
|
|
1140
|
-
if (count === 0) continue;
|
|
1141
|
-
contours++;
|
|
1142
|
-
lastClosed = pl.closed;
|
|
1143
|
-
first ??= { x: pl.points[0], y: pl.points[1] };
|
|
1144
|
-
const startIndex = segments.length;
|
|
1145
|
-
const last = pl.closed ? count : count - 1;
|
|
1146
|
-
for (let i = 0; i < last; i++) {
|
|
1147
|
-
const j = (i + 1) % count;
|
|
1148
|
-
const ax = pl.points[i * 2];
|
|
1149
|
-
const ay = pl.points[i * 2 + 1];
|
|
1150
|
-
const bx = pl.points[j * 2];
|
|
1151
|
-
const by = pl.points[j * 2 + 1];
|
|
1152
|
-
const len = Math.hypot(bx - ax, by - ay);
|
|
1153
|
-
if (len === 0) continue;
|
|
1154
|
-
const tx = (bx - ax) / len;
|
|
1155
|
-
const ty = (by - ay) / len;
|
|
1156
|
-
segments.push({ ax, ay, bx, by, start: total, len, tx, ty, t0x: tx, t0y: ty, t1x: tx, t1y: ty });
|
|
1157
|
-
total += len;
|
|
1158
|
-
}
|
|
1159
|
-
smoothVertexHeadings(segments, startIndex, pl.closed);
|
|
1160
|
-
}
|
|
1161
|
-
if (first === void 0) return null;
|
|
1162
|
-
const origin = first;
|
|
1163
|
-
const closed = contours === 1 && lastClosed;
|
|
1164
|
-
if (segments.length === 0) {
|
|
1165
|
-
return { length: 0, closed, at: () => ({ point: { ...origin }, tangent: { x: 1, y: 0 } }) };
|
|
1166
|
-
}
|
|
1167
|
-
return {
|
|
1168
|
-
length: total,
|
|
1169
|
-
closed,
|
|
1170
|
-
at(s, smooth = false) {
|
|
1171
|
-
const target = Math.min(Math.max(s, 0), total);
|
|
1172
|
-
const seg = segments[segmentAt(segments, target)];
|
|
1173
|
-
const u = Math.min(Math.max((target - seg.start) / seg.len, 0), 1);
|
|
1174
|
-
const point = { x: seg.ax + (seg.bx - seg.ax) * u, y: seg.ay + (seg.by - seg.ay) * u };
|
|
1175
|
-
if (!smooth) return { point, tangent: { x: seg.tx, y: seg.ty } };
|
|
1176
|
-
const x = seg.t0x + (seg.t1x - seg.t0x) * u;
|
|
1177
|
-
const y = seg.t0y + (seg.t1y - seg.t0y) * u;
|
|
1178
|
-
const l = Math.hypot(x, y);
|
|
1179
|
-
return { point, tangent: l === 0 ? { x: seg.tx, y: seg.ty } : { x: x / l, y: y / l } };
|
|
1180
|
-
}
|
|
1181
|
-
};
|
|
1182
|
-
}
|
|
1183
|
-
function smoothVertexHeadings(segments, from, closed) {
|
|
1184
|
-
const n = segments.length - from;
|
|
1185
|
-
if (n < 2) return;
|
|
1186
|
-
const pairs = closed ? n : n - 1;
|
|
1187
|
-
for (let k = 0; k < pairs; k++) {
|
|
1188
|
-
const a = segments[from + k];
|
|
1189
|
-
const b = segments[from + (k + 1) % n];
|
|
1190
|
-
let x = a.tx + b.tx;
|
|
1191
|
-
let y = a.ty + b.ty;
|
|
1192
|
-
const l = Math.hypot(x, y);
|
|
1193
|
-
if (l < 1e-9) continue;
|
|
1194
|
-
x /= l;
|
|
1195
|
-
y /= l;
|
|
1196
|
-
a.t1x = x;
|
|
1197
|
-
a.t1y = y;
|
|
1198
|
-
b.t0x = x;
|
|
1199
|
-
b.t0y = y;
|
|
1200
|
-
}
|
|
1201
|
-
}
|
|
1202
|
-
function segmentAt(segments, s) {
|
|
1203
|
-
let lo = 0;
|
|
1204
|
-
let hi = segments.length - 1;
|
|
1205
|
-
while (lo < hi) {
|
|
1206
|
-
const mid = lo + hi + 1 >> 1;
|
|
1207
|
-
if (segments[mid].start <= s) lo = mid;
|
|
1208
|
-
else hi = mid - 1;
|
|
1209
|
-
}
|
|
1210
|
-
return lo;
|
|
1211
|
-
}
|
|
1212
|
-
|
|
1213
1637
|
// src/paths/pathAt.ts
|
|
1214
1638
|
function pointAlongPath(path, t, opts = {}) {
|
|
1215
1639
|
const measure2 = measurePath(path, opts);
|
|
@@ -1381,13 +1805,13 @@ function matchContours(a, b, opts) {
|
|
|
1381
1805
|
const offset2 = Math.round(opts.startOffset ?? 0);
|
|
1382
1806
|
const directions = opts.reverse !== void 0 ? [opts.reverse] : auto ? [false, true] : [false];
|
|
1383
1807
|
let best = null;
|
|
1384
|
-
for (const
|
|
1385
|
-
const run3 =
|
|
1808
|
+
for (const reversed3 of directions) {
|
|
1809
|
+
const run3 = reversed3 ? reverseContour(y) : y;
|
|
1386
1810
|
const turns = closed && auto ? run3.segs.length : 1;
|
|
1387
1811
|
for (let k = 0; k < turns; k++) {
|
|
1388
1812
|
const [ma, mb] = equalize(x, rotate2(run3, k));
|
|
1389
1813
|
const score = auto ? correspondence(ma, mb) : 0;
|
|
1390
|
-
if (best === null || score < best.score - 1e-9) best = { score, a: ma, b: mb, reversed:
|
|
1814
|
+
if (best === null || score < best.score - 1e-9) best = { score, a: ma, b: mb, reversed: reversed3, turn: k };
|
|
1391
1815
|
}
|
|
1392
1816
|
}
|
|
1393
1817
|
const chosen = best;
|
|
@@ -1771,17 +2195,17 @@ var GL_W2 = [
|
|
|
1771
2195
|
0.2223810344533745,
|
|
1772
2196
|
0.1012285362903763
|
|
1773
2197
|
];
|
|
1774
|
-
var
|
|
2198
|
+
var speed2 = (s, t) => {
|
|
1775
2199
|
const d = derivSeg(s, t);
|
|
1776
2200
|
return Math.hypot(d.x, d.y);
|
|
1777
2201
|
};
|
|
1778
2202
|
function segmentLength(s, a = 0, b = 1) {
|
|
1779
|
-
if (s.length === 2) return
|
|
2203
|
+
if (s.length === 2) return speed2(s, 0) * (b - a);
|
|
1780
2204
|
const panels = 8, h = (b - a) / panels;
|
|
1781
2205
|
let sum = 0;
|
|
1782
2206
|
for (let k = 0; k < panels; k++) {
|
|
1783
2207
|
const mid = a + (k + 0.5) * h;
|
|
1784
|
-
for (let i = 0; i < GL_X.length; i++) sum += GL_W2[i] *
|
|
2208
|
+
for (let i = 0; i < GL_X.length; i++) sum += GL_W2[i] * speed2(s, mid + GL_X[i] * h / 2);
|
|
1785
2209
|
}
|
|
1786
2210
|
return sum * h / 2;
|
|
1787
2211
|
}
|
|
@@ -1796,7 +2220,7 @@ function segmentTAtLength(s, length2) {
|
|
|
1796
2220
|
if (Math.abs(err) < 1e-10 * Math.max(1, total)) break;
|
|
1797
2221
|
if (err > 0) hi = t;
|
|
1798
2222
|
else lo = t;
|
|
1799
|
-
const v =
|
|
2223
|
+
const v = speed2(s, t);
|
|
1800
2224
|
let next = v > 0 ? t - err / v : NaN;
|
|
1801
2225
|
if (!(next > lo && next < hi)) next = (lo + hi) / 2;
|
|
1802
2226
|
t = next;
|
|
@@ -2803,7 +3227,7 @@ function breaksOf(sub3, closed, cosCorner) {
|
|
|
2803
3227
|
const n = sub3.length;
|
|
2804
3228
|
const breaks = /* @__PURE__ */ new Set();
|
|
2805
3229
|
for (let i = 0; i < n; i++) {
|
|
2806
|
-
if (
|
|
3230
|
+
if (isCorner2(sub3, i, closed, cosCorner)) breaks.add(i);
|
|
2807
3231
|
if (isLongStraight(sub3, i, closed)) {
|
|
2808
3232
|
breaks.add(i);
|
|
2809
3233
|
breaks.add((i + 1) % n);
|
|
@@ -2823,7 +3247,7 @@ function fitStops(sub3, stops, tol, out, closing) {
|
|
|
2823
3247
|
if (!last) keepCorner(out[out.length - 1], sub3[stops[k + 1] % n]);
|
|
2824
3248
|
}
|
|
2825
3249
|
}
|
|
2826
|
-
function
|
|
3250
|
+
function isCorner2(sub3, i, closed, cosCorner) {
|
|
2827
3251
|
const n = sub3.length;
|
|
2828
3252
|
const a = sub3[i];
|
|
2829
3253
|
const prev = sub3[(i - 1 + n) % n];
|
|
@@ -2880,7 +3304,7 @@ function unit(p) {
|
|
|
2880
3304
|
|
|
2881
3305
|
// src/paths/growRegion.ts
|
|
2882
3306
|
var MAX_DEPTH2 = 6;
|
|
2883
|
-
var
|
|
3307
|
+
var SAMPLES2 = [0.2, 0.35, 0.5, 0.65, 0.8];
|
|
2884
3308
|
function growRegion(path, distance, opts = {}) {
|
|
2885
3309
|
const b = new PathBuilder();
|
|
2886
3310
|
if (!(distance > 0)) return b.build();
|
|
@@ -3053,7 +3477,7 @@ function offsetSeg(seg, ctx, depth) {
|
|
|
3053
3477
|
}
|
|
3054
3478
|
function offsetError(c, q, ctx) {
|
|
3055
3479
|
let worst2 = 0;
|
|
3056
|
-
for (const t of
|
|
3480
|
+
for (const t of SAMPLES2) {
|
|
3057
3481
|
const [dx2, dy2] = derivative(c, t);
|
|
3058
3482
|
const len = Math.hypot(dx2, dy2);
|
|
3059
3483
|
if (len < 1e-12) continue;
|
|
@@ -3110,6 +3534,243 @@ function emitContour(b, grown) {
|
|
|
3110
3534
|
b.close();
|
|
3111
3535
|
}
|
|
3112
3536
|
|
|
3537
|
+
// src/paths/segmentCrossings.ts
|
|
3538
|
+
var STEPS = 16;
|
|
3539
|
+
var END2 = 1e-7;
|
|
3540
|
+
function segmentCrossings(segs) {
|
|
3541
|
+
const flat = segs.map((s) => flatten2(s));
|
|
3542
|
+
const boxes = flat.map(boxOf2);
|
|
3543
|
+
const out = [];
|
|
3544
|
+
for (let a = 0; a < segs.length; a++) {
|
|
3545
|
+
for (let b = a + 1; b < segs.length; b++) {
|
|
3546
|
+
if (!overlaps(boxes[a], boxes[b])) continue;
|
|
3547
|
+
const pa = flat[a], pb = flat[b];
|
|
3548
|
+
for (let i = 0; i + 1 < pa.length; i++) {
|
|
3549
|
+
for (let j = 0; j + 1 < pb.length; j++) {
|
|
3550
|
+
const hit = linesCross(pa[i], pa[i + 1], pb[j], pb[j + 1]);
|
|
3551
|
+
if (hit === null) continue;
|
|
3552
|
+
const guess = [(i + hit[0]) / (pa.length - 1), (j + hit[1]) / (pb.length - 1)];
|
|
3553
|
+
const [s, t] = segs[a].line && segs[b].line ? guess : refine2(segs[a].c, segs[b].c, guess);
|
|
3554
|
+
if (atEnd(s) && atEnd(t)) continue;
|
|
3555
|
+
if (out.some((x) => x.a === a && x.b === b && Math.abs(x.s - s) < 1e-6 && Math.abs(x.t - t) < 1e-6)) continue;
|
|
3556
|
+
out.push({ a, b, s, t });
|
|
3557
|
+
}
|
|
3558
|
+
}
|
|
3559
|
+
}
|
|
3560
|
+
}
|
|
3561
|
+
return out;
|
|
3562
|
+
}
|
|
3563
|
+
var atEnd = (t) => t < END2 || t > 1 - END2;
|
|
3564
|
+
function flatten2(seg) {
|
|
3565
|
+
if (seg.line) return [[seg.c[0], seg.c[1]], [seg.c[6], seg.c[7]]];
|
|
3566
|
+
const out = [];
|
|
3567
|
+
for (let i = 0; i <= STEPS; i++) out.push(cubicEvalAt(...seg.c, i / STEPS));
|
|
3568
|
+
return out;
|
|
3569
|
+
}
|
|
3570
|
+
function boxOf2(pts) {
|
|
3571
|
+
const b = [Infinity, Infinity, -Infinity, -Infinity];
|
|
3572
|
+
for (const [x, y] of pts) {
|
|
3573
|
+
b[0] = Math.min(b[0], x);
|
|
3574
|
+
b[1] = Math.min(b[1], y);
|
|
3575
|
+
b[2] = Math.max(b[2], x);
|
|
3576
|
+
b[3] = Math.max(b[3], y);
|
|
3577
|
+
}
|
|
3578
|
+
return b;
|
|
3579
|
+
}
|
|
3580
|
+
var overlaps = (p, q) => p[0] <= q[2] && q[0] <= p[2] && p[1] <= q[3] && q[1] <= p[3];
|
|
3581
|
+
function linesCross(a, b, c, d) {
|
|
3582
|
+
const rx = b[0] - a[0], ry = b[1] - a[1], sx = d[0] - c[0], sy = d[1] - c[1];
|
|
3583
|
+
const den = rx * sy - ry * sx;
|
|
3584
|
+
if (Math.abs(den) < 1e-18) return null;
|
|
3585
|
+
const u = ((c[0] - a[0]) * sy - (c[1] - a[1]) * sx) / den;
|
|
3586
|
+
const w = ((c[0] - a[0]) * ry - (c[1] - a[1]) * rx) / den;
|
|
3587
|
+
return u >= 0 && u <= 1 && w >= 0 && w <= 1 ? [u, w] : null;
|
|
3588
|
+
}
|
|
3589
|
+
function refine2(a, b, guess) {
|
|
3590
|
+
const gap = (s2, t2) => {
|
|
3591
|
+
const p = cubicEvalAt(...a, s2), q = cubicEvalAt(...b, t2);
|
|
3592
|
+
return [p[0] - q[0], p[1] - q[1]];
|
|
3593
|
+
};
|
|
3594
|
+
let [s, t] = guess;
|
|
3595
|
+
let f = gap(s, t);
|
|
3596
|
+
const start = Math.hypot(...f);
|
|
3597
|
+
for (let k = 0; k < 8 && Math.hypot(...f) > 1e-13; k++) {
|
|
3598
|
+
const da = derivative2(a, s), db = derivative2(b, t);
|
|
3599
|
+
const det = -da[0] * db[1] + db[0] * da[1];
|
|
3600
|
+
if (Math.abs(det) < 1e-18) break;
|
|
3601
|
+
s = Math.min(1, Math.max(0, s - (-db[1] * f[0] + db[0] * f[1]) / det));
|
|
3602
|
+
t = Math.min(1, Math.max(0, t - (-da[1] * f[0] + da[0] * f[1]) / det));
|
|
3603
|
+
f = gap(s, t);
|
|
3604
|
+
}
|
|
3605
|
+
return Math.hypot(...f) <= start ? [s, t] : guess;
|
|
3606
|
+
}
|
|
3607
|
+
function derivative2(c, t) {
|
|
3608
|
+
const u = 1 - t;
|
|
3609
|
+
const a = 3 * u * u, b = 6 * u * t, e = 3 * t * t;
|
|
3610
|
+
return [
|
|
3611
|
+
a * (c[2] - c[0]) + b * (c[4] - c[2]) + e * (c[6] - c[4]),
|
|
3612
|
+
a * (c[3] - c[1]) + b * (c[5] - c[3]) + e * (c[7] - c[5])
|
|
3613
|
+
];
|
|
3614
|
+
}
|
|
3615
|
+
|
|
3616
|
+
// src/paths/mergeRegion.ts
|
|
3617
|
+
function mergeRegion(path, opts = {}) {
|
|
3618
|
+
const tol = opts.tolerance ?? 0.01;
|
|
3619
|
+
const contours = pathToCubicContours(path).filter((c) => c.segs.length > 0);
|
|
3620
|
+
const segs = contours.flatMap((c) => closeContour(c.segs, tol * 1e-3));
|
|
3621
|
+
const fillLeft = pathSignedArea(path) < 0;
|
|
3622
|
+
const cuts = segs.map(() => []);
|
|
3623
|
+
for (const x of segmentCrossings(segs)) {
|
|
3624
|
+
const p = crossingPoint(segs[x.a], x.s, segs[x.b], x.t);
|
|
3625
|
+
cuts[x.a].push({ t: x.s, p });
|
|
3626
|
+
cuts[x.b].push({ t: x.t, p });
|
|
3627
|
+
}
|
|
3628
|
+
const rings = flattenAll(segs, tol / 4);
|
|
3629
|
+
const pieces = [];
|
|
3630
|
+
let changed = false;
|
|
3631
|
+
segs.forEach((seg, i) => {
|
|
3632
|
+
const parts = cutSegment(seg, cuts[i]);
|
|
3633
|
+
if (parts.length > 1) changed = true;
|
|
3634
|
+
for (const part of parts) {
|
|
3635
|
+
const side = filledSide(part, rings, tol);
|
|
3636
|
+
if (side === null) changed = true;
|
|
3637
|
+
else if (side === fillLeft) pieces.push(part);
|
|
3638
|
+
else {
|
|
3639
|
+
changed = true;
|
|
3640
|
+
pieces.push(reversed2(part));
|
|
3641
|
+
}
|
|
3642
|
+
}
|
|
3643
|
+
});
|
|
3644
|
+
const b = new PathBuilder();
|
|
3645
|
+
if (!changed) {
|
|
3646
|
+
for (const c of contours) emit(b, closeContour(c.segs, tol * 1e-3));
|
|
3647
|
+
return b.build();
|
|
3648
|
+
}
|
|
3649
|
+
for (const ring of chain(pieces)) emit(b, ring);
|
|
3650
|
+
return b.build();
|
|
3651
|
+
}
|
|
3652
|
+
function closeContour(given, snap2) {
|
|
3653
|
+
const short = (s) => s.line && Math.hypot(s.c[6] - s.c[0], s.c[7] - s.c[1]) <= snap2;
|
|
3654
|
+
const segs = given.filter((s) => !short(s));
|
|
3655
|
+
if (segs.length === 0) return [];
|
|
3656
|
+
for (let i = 0; i < segs.length; i++) {
|
|
3657
|
+
const prev = segs[(i + segs.length - 1) % segs.length].c, s = segs[i];
|
|
3658
|
+
if (prev[6] !== s.c[0] || prev[7] !== s.c[1]) {
|
|
3659
|
+
if (Math.hypot(prev[6] - s.c[0], prev[7] - s.c[1]) <= snap2) segs[i] = moveEnds(s, [prev[6], prev[7]], [s.c[6], s.c[7]]);
|
|
3660
|
+
else if (i === 0) segs.push(lineSegment(prev[6], prev[7], s.c[0], s.c[1]));
|
|
3661
|
+
}
|
|
3662
|
+
}
|
|
3663
|
+
return segs;
|
|
3664
|
+
}
|
|
3665
|
+
function crossingPoint(a, s, b, t) {
|
|
3666
|
+
const end = (seg, u) => u <= 1e-7 ? [seg.c[0], seg.c[1]] : u >= 1 - 1e-7 ? [seg.c[6], seg.c[7]] : null;
|
|
3667
|
+
const fixed = end(a, s) ?? end(b, t);
|
|
3668
|
+
if (fixed) return fixed;
|
|
3669
|
+
const p = cubicEvalAt(...a.c, s), q = cubicEvalAt(...b.c, t);
|
|
3670
|
+
return [(p[0] + q[0]) / 2, (p[1] + q[1]) / 2];
|
|
3671
|
+
}
|
|
3672
|
+
function cutSegment(seg, cuts) {
|
|
3673
|
+
const inner = cuts.filter((c) => c.t > 1e-7 && c.t < 1 - 1e-7).sort((x, y) => x.t - y.t);
|
|
3674
|
+
const out = [];
|
|
3675
|
+
let rest = seg, from = 0;
|
|
3676
|
+
let start = [seg.c[0], seg.c[1]];
|
|
3677
|
+
for (const cut of inner) {
|
|
3678
|
+
if (cut.t - from < 1e-9) continue;
|
|
3679
|
+
const [l, r] = splitSegment(rest, (cut.t - from) / (1 - from));
|
|
3680
|
+
out.push(moveEnds(l, start, cut.p));
|
|
3681
|
+
rest = r;
|
|
3682
|
+
from = cut.t;
|
|
3683
|
+
start = cut.p;
|
|
3684
|
+
}
|
|
3685
|
+
out.push(moveEnds(rest, start, [seg.c[6], seg.c[7]]));
|
|
3686
|
+
return out;
|
|
3687
|
+
}
|
|
3688
|
+
function moveEnds(s, a, b) {
|
|
3689
|
+
const c = s.c;
|
|
3690
|
+
return {
|
|
3691
|
+
c: [a[0], a[1], c[2] + a[0] - c[0], c[3] + a[1] - c[1], c[4] + b[0] - c[6], c[5] + b[1] - c[7], b[0], b[1]],
|
|
3692
|
+
line: s.line
|
|
3693
|
+
};
|
|
3694
|
+
}
|
|
3695
|
+
function reversed2(s) {
|
|
3696
|
+
const c = s.c;
|
|
3697
|
+
return { c: [c[6], c[7], c[4], c[5], c[2], c[3], c[0], c[1]], line: s.line };
|
|
3698
|
+
}
|
|
3699
|
+
function flattenAll(segs, tol) {
|
|
3700
|
+
const rings = [];
|
|
3701
|
+
let ring = [];
|
|
3702
|
+
for (const { c, line } of segs) {
|
|
3703
|
+
if (ring.length === 0) ring.push(c[0], c[1]);
|
|
3704
|
+
else if (ring[ring.length - 2] !== c[0] || ring[ring.length - 1] !== c[1]) {
|
|
3705
|
+
rings.push(ring);
|
|
3706
|
+
ring = [c[0], c[1]];
|
|
3707
|
+
}
|
|
3708
|
+
if (line) ring.push(c[6], c[7]);
|
|
3709
|
+
else flattenCubic(...c, tol, ring);
|
|
3710
|
+
}
|
|
3711
|
+
if (ring.length > 0) rings.push(ring);
|
|
3712
|
+
return rings;
|
|
3713
|
+
}
|
|
3714
|
+
function winding(rings, x, y) {
|
|
3715
|
+
let w = 0;
|
|
3716
|
+
for (const r of rings) {
|
|
3717
|
+
for (let i = 0; i + 3 < r.length; i += 2) {
|
|
3718
|
+
const ay = r[i + 1], by = r[i + 3];
|
|
3719
|
+
if (ay > y === by > y) continue;
|
|
3720
|
+
const ax = r[i], bx = r[i + 2];
|
|
3721
|
+
if (x >= ax + (y - ay) / (by - ay) * (bx - ax)) continue;
|
|
3722
|
+
w += ay <= y ? 1 : -1;
|
|
3723
|
+
}
|
|
3724
|
+
}
|
|
3725
|
+
return w;
|
|
3726
|
+
}
|
|
3727
|
+
function filledSide(seg, rings, tol) {
|
|
3728
|
+
const c = seg.c;
|
|
3729
|
+
const [x, y] = cubicEvalAt(...c, 0.5);
|
|
3730
|
+
const dx2 = c[6] + c[4] - c[2] - c[0], dy2 = c[7] + c[5] - c[3] - c[1];
|
|
3731
|
+
const len = Math.hypot(dx2, dy2);
|
|
3732
|
+
if (len < 1e-12) return null;
|
|
3733
|
+
const nx = dy2 / len * tol, ny = -dx2 / len * tol;
|
|
3734
|
+
const left = winding(rings, x + nx, y + ny) !== 0, right = winding(rings, x - nx, y - ny) !== 0;
|
|
3735
|
+
return left === right ? null : left;
|
|
3736
|
+
}
|
|
3737
|
+
function chain(pieces) {
|
|
3738
|
+
const key = (x, y) => `${x},${y}`;
|
|
3739
|
+
const from = /* @__PURE__ */ new Map();
|
|
3740
|
+
pieces.forEach((p, i) => {
|
|
3741
|
+
const k = key(p.c[0], p.c[1]);
|
|
3742
|
+
const list = from.get(k);
|
|
3743
|
+
if (list) list.push(i);
|
|
3744
|
+
else from.set(k, [i]);
|
|
3745
|
+
});
|
|
3746
|
+
const used = new Uint8Array(pieces.length);
|
|
3747
|
+
const rings = [];
|
|
3748
|
+
for (let i = 0; i < pieces.length; i++) {
|
|
3749
|
+
if (used[i]) continue;
|
|
3750
|
+
const ring = [];
|
|
3751
|
+
let at2 = i;
|
|
3752
|
+
while (at2 !== void 0 && !used[at2]) {
|
|
3753
|
+
used[at2] = 1;
|
|
3754
|
+
const p = pieces[at2];
|
|
3755
|
+
ring.push(p);
|
|
3756
|
+
at2 = from.get(key(p.c[6], p.c[7]))?.find((j) => !used[j]);
|
|
3757
|
+
}
|
|
3758
|
+
rings.push(ring);
|
|
3759
|
+
}
|
|
3760
|
+
return rings;
|
|
3761
|
+
}
|
|
3762
|
+
function emit(b, ring) {
|
|
3763
|
+
if (ring.length === 0) return;
|
|
3764
|
+
const [x0, y0] = ring[0].c;
|
|
3765
|
+
b.moveTo(x0, y0);
|
|
3766
|
+
for (const [i, { c, line }] of ring.entries()) {
|
|
3767
|
+
if (line && i === ring.length - 1 && c[6] === x0 && c[7] === y0) break;
|
|
3768
|
+
if (line) b.lineTo(c[6], c[7]);
|
|
3769
|
+
else b.curveTo(c[2], c[3], c[4], c[5], c[6], c[7]);
|
|
3770
|
+
}
|
|
3771
|
+
b.close();
|
|
3772
|
+
}
|
|
3773
|
+
|
|
3113
3774
|
// src/paths/simplifyAnchors.ts
|
|
3114
3775
|
function simplifyAnchorsAt(set, flatIndexes, tolerance, opts = {}) {
|
|
3115
3776
|
const bySub = /* @__PURE__ */ new Map();
|
|
@@ -3164,7 +3825,7 @@ var unit3 = (x, y) => {
|
|
|
3164
3825
|
const len = Math.hypot(x, y);
|
|
3165
3826
|
return len === 0 ? { x: 0, y: 0 } : { x: x / len, y: y / len };
|
|
3166
3827
|
};
|
|
3167
|
-
function
|
|
3828
|
+
function derivative3(seg, t) {
|
|
3168
3829
|
const u = 1 - t;
|
|
3169
3830
|
const { a, c1, c2, b } = seg;
|
|
3170
3831
|
return {
|
|
@@ -3174,8 +3835,8 @@ function derivative2(seg, t) {
|
|
|
3174
3835
|
}
|
|
3175
3836
|
function headingOn(seg, t) {
|
|
3176
3837
|
if (seg.straight) return unit3(seg.b.x - seg.a.x, seg.b.y - seg.a.y);
|
|
3177
|
-
let d =
|
|
3178
|
-
if (Math.hypot(d.x, d.y) < 1e-9) d =
|
|
3838
|
+
let d = derivative3(seg, t);
|
|
3839
|
+
if (Math.hypot(d.x, d.y) < 1e-9) d = derivative3(seg, t < 0.5 ? t + 1e-3 : t - 1e-3);
|
|
3179
3840
|
const h = unit3(d.x, d.y);
|
|
3180
3841
|
return h.x === 0 && h.y === 0 ? unit3(seg.b.x - seg.a.x, seg.b.y - seg.a.y) : h;
|
|
3181
3842
|
}
|
|
@@ -4338,7 +4999,7 @@ function smudgePath(path, opts) {
|
|
|
4338
4999
|
if (!(radius > 0) || strength === 0 || delta.x === 0 && delta.y === 0) return path;
|
|
4339
5000
|
const detail = opts.detail ?? radius / 3;
|
|
4340
5001
|
return editAnchorSet(path, (set) => {
|
|
4341
|
-
|
|
5002
|
+
refine3(set, center, radius, detail);
|
|
4342
5003
|
let touched = false;
|
|
4343
5004
|
let flat = 0;
|
|
4344
5005
|
for (const sub3 of set.anchors) {
|
|
@@ -4358,7 +5019,7 @@ function weight(p, c, radius) {
|
|
|
4358
5019
|
const u2 = ((p.x - c.x) ** 2 + (p.y - c.y) ** 2) / (radius * radius);
|
|
4359
5020
|
return u2 >= 1 ? 0 : (1 - u2) ** 2;
|
|
4360
5021
|
}
|
|
4361
|
-
function
|
|
5022
|
+
function refine3(set, c, radius, detail) {
|
|
4362
5023
|
if (!Number.isFinite(detail) || detail <= 0) return;
|
|
4363
5024
|
let added = 0;
|
|
4364
5025
|
for (let sub3 = 0; sub3 < set.anchors.length; sub3++) {
|
|
@@ -5579,7 +6240,8 @@ function constrainPathsEdit(items, links = []) {
|
|
|
5579
6240
|
const sliding = new Set(kept.flatMap((c) => c.kind === "onPath" ? [c.anchor] : []));
|
|
5580
6241
|
const still = (i) => {
|
|
5581
6242
|
const { item } = itemOf(i, nowStart);
|
|
5582
|
-
|
|
6243
|
+
const it = items[item];
|
|
6244
|
+
return items.length > 1 && it.before === it.after && !it.yields && !sliding.has(i);
|
|
5583
6245
|
};
|
|
5584
6246
|
const held = nowAll.flatMap((p, i) => {
|
|
5585
6247
|
const r = reference[i];
|
|
@@ -6001,6 +6663,6 @@ function typedArrayReplacer(_key, value) {
|
|
|
6001
6663
|
return tagTypedArray(value) ?? value;
|
|
6002
6664
|
}
|
|
6003
6665
|
|
|
6004
|
-
export { ANCHOR_FIELDS, CONSTRAINT_TOLERANCE, EASINGS, PATH_SNAP_KINDS, SPRING_PRESETS, TYPED_ARRAY_CONSTRUCTORS, ZOOM_FLOOR, addExtremaAnchors, anchorAt, anchorCount, anchorOriginsOf, anchorSymmetries, anchorSymmetry, anchorsBefore, anchorsInRect, anchorsUnderBrush, anchorsWhere, applyToPoint, averageAnchorsAt, balanceFactorToward, bendAlongPath, bendSegment, blendPaths, boxToBox, circlePath, clampRectWithin, closestOpenEnds, composePath, conflictingConstraints, constrainPathEdit, constrainPathsEdit, constraintAnchors, constraintApplies, constraintFits, convertSegments, cornerAnchors, createPathSnapIndex, cubicBezierEasing, cubicContoursToPath, decomposePath, deleteAnchorsAt, directionMarks, easeIn, easeInBack, easeInBounce, easeInCirc, easeInCubic, easeInElastic, easeInExpo, easeInOut, easeInOutBack, easeInOutBounce, easeInOutCirc, easeInOutCubic, easeInOutElastic, easeInOutExpo, easeInOutQuad, easeInOutQuart, easeInOutQuint, easeInOutSine, easeInQuad, easeInQuart, easeInQuint, easeInSine, easeOut, easeOutBack, easeOutBounce, easeOutCirc, easeOutCubic, easeOutElastic, easeOutExpo, easeOutQuad, easeOutQuart, easeOutQuint, easeOutSine, editAnchorSet, editAnchorSetTracked, equalizeSegmentHandles, everyNthAnchor, extendToMeet, findPathSnap, findPathSnaps, fitAffine, fitCubicThroughDeletion, flatAnchorIndex, flatIndexesAfterReverse, growAnchorSelection, growRegion, handleAlongItsAngle, handleAtItsLength, handleBalances, harmonizeAnchors, hasOpenSubpath, identity, insertAnchorOnSegment, intersectSegments, invert, invertAnchorSelection, isSegmentStraight, joinAnchorsAt, joinSubpathEnds, lengthToward, lengthenOpenEnd, lengthenPathEnd, linear, locateAnchor, makeHandlesSymmetric, mapConstraintAnchors, mat3, meanScale, measureAnchorPath, measurePath, moveHandleSymmetric, moveHandleTo, multiply, nearestPathAnchor, normalizeZoom, offsetCornerTraces, openEndAt, openEndRay, openPathEnds, openSubpathAt, pathAnchorPoints, pathAwayFromAnchors, pathContainsPoint, pathContainsPolygon, pathContainsRect, pathCrossings, pathDistanceToPoint, pathEndAnchorIndex, pathEndFrame, pathFromD, pathInPoseFrame, pathInWorld, pathIntersectsPolygon, pathIntersectsRect, pathSegments, pathSignedArea, pathThroughPoseFrame, pathToCubicContours, placeRect, pointAlongPath, pointInPath, polygonContainsPath, polygonIntersectsPath, portControls2 as portControls, portCurvePoints2 as portCurvePoints, poseFrameInverseOf, poseFrameOf, poseLinearOf, poseRotationOf, prepareBlend, pxExtent, readAnchorFields, rectMarkerPath, remapAnchorValues, remapConstraints, remapPointValues, repeatAlongPath, resamplePath, resolveEasing, retractHandles, reverseAnchors, reversePath, reverseSubpaths, reviveTypedArrays, rotate, rotateAboutPoint, rotatePathAround, roundRectPath, scale, scaleDelta, scalePathToBounds, scaleSegmentHandles, screenAngleOf, segmentAt2 as segmentAt, segmentBetween, segmentExtremaTs, segmentsBetweenAnchors, setSubpathStart, shrinkAnchorSelection, simplifyAnchorsAt, simplifyPath, simplifySpan, simplifySubpath, smoothAnchors, smudgePath, softFalloff, softSelectionBand, softSelectionWeights, solveConstraints, solvePathConstraints, splitByClosure, splitSubpaths, squarePath, standoff, stationsAlongPath, strokeHitTest, subpathEndAt, subpathsOfAnchors, tagTypedArray, tagTypedArrays, transformCoords, transformPath, translate, translateAnchorBy, translateAnchorsSymmetric, translatePath, trimPathAt, typedArrayReplacer, unionBoundsPath, unsatisfiedConstraints, untagTypedArray, viewToMat3, withAnchorPoints, withinPxBox, withinPxRadius, worldEditToStorage, writeAnchorField };
|
|
6666
|
+
export { ANCHOR_FIELDS, CONSTRAINT_TOLERANCE, EASINGS, PATH_SNAP_KINDS, SPRING_PRESETS, TYPED_ARRAY_CONSTRUCTORS, ZOOM_FLOOR, addExtremaAnchors, anchorAt, anchorCount, anchorOriginsOf, anchorSymmetries, anchorSymmetry, anchorsBefore, anchorsInRect, anchorsUnderBrush, anchorsWhere, applyToPoint, arcShape, averageAnchorsAt, balanceFactorToward, bendAlongPath, bendSegment, blendPaths, boxToBox, circlePath, clampRectWithin, closestOpenEnds, composePath, conflictingConstraints, constrainPathEdit, constrainPathsEdit, constraintAnchors, constraintApplies, constraintFits, convertSegments, cornerAnchors, createPathSnapIndex, cubicBezierEasing, cubicContoursToPath, decomposePath, deleteAnchorsAt, directionMarks, easeIn, easeInBack, easeInBounce, easeInCirc, easeInCubic, easeInElastic, easeInExpo, easeInOut, easeInOutBack, easeInOutBounce, easeInOutCirc, easeInOutCubic, easeInOutElastic, easeInOutExpo, easeInOutQuad, easeInOutQuart, easeInOutQuint, easeInOutSine, easeInQuad, easeInQuart, easeInQuint, easeInSine, easeOut, easeOutBack, easeOutBounce, easeOutCirc, easeOutCubic, easeOutElastic, easeOutExpo, easeOutQuad, easeOutQuart, easeOutQuint, easeOutSine, editAnchorSet, editAnchorSetTracked, ellipseShape, equalizeSegmentHandles, everyNthAnchor, extendToMeet, findPathSnap, findPathSnaps, fitAffine, fitCubicThroughDeletion, flatAnchorIndex, flatIndexesAfterReverse, growAnchorSelection, growRegion, handleAlongItsAngle, handleAtItsLength, handleBalances, harmonizeAnchors, hasOpenSubpath, identity, insertAnchorOnSegment, intersectSegments, invert, invertAnchorSelection, isSegmentStraight, joinAnchorsAt, joinSubpathEnds, lengthToward, lengthenOpenEnd, lengthenPathEnd, lineShape, linear, locateAnchor, makeHandlesSymmetric, mapConstraintAnchors, mat3, meanScale, measureAnchorPath, measurePath, mergeRegion, moveHandleSymmetric, moveHandleTo, multiply, nearestPathAnchor, normalizeZoom, offsetCornerTraces, openEndAt, openEndRay, openPathEnds, openSubpathAt, pathAnchorPoints, pathAwayFromAnchors, pathContainsPoint, pathContainsPolygon, pathContainsRect, pathCrossings, pathDistanceToPoint, pathEndAnchorIndex, pathEndFrame, pathFromD, pathInPoseFrame, pathInWorld, pathIntersectsPolygon, pathIntersectsRect, pathSegments, pathShape, pathSignedArea, pathThroughPoseFrame, pathToCubicContours, pathToD, placeAlongRun, placeRect, pointAlongPath, pointInPath, polygonContainsPath, polygonIntersectsPath, portControls2 as portControls, portCurvePoints2 as portCurvePoints, poseFrameInverseOf, poseFrameOf, poseLinearOf, poseRotationOf, prepareBlend, pxExtent, readAnchorFields, rectMarkerPath, remapAnchorValues, remapConstraints, remapPointValues, repeatAlongPath, resamplePath, resolveEasing, retractHandles, reverseAnchors, reversePath, reverseSubpaths, reviveTypedArrays, rotate, rotateAboutPoint, rotatePathAround, roundRectPath, runLength, runPlaces, runStationAt, scale, scaleDelta, scalePathToBounds, scaleSegmentHandles, screenAngleOf, segmentAt2 as segmentAt, segmentBetween, segmentExtremaTs, segmentsBetweenAnchors, setSubpathStart, shrinkAnchorSelection, simplifyAnchorsAt, simplifyPath, simplifySpan, simplifySubpath, smoothAnchors, smudgePath, softFalloff, softSelectionBand, softSelectionWeights, solveConstraints, solvePathConstraints, splitByClosure, splitSubpaths, squarePath, standoff, stationsAlongPath, strokeHitTest, subpathEndAt, subpathsOfAnchors, tagTypedArray, tagTypedArrays, trackRun, trackShapeD, transformCoords, transformPath, translate, translateAnchorBy, translateAnchorsSymmetric, translatePath, trimPathAt, typedArrayReplacer, unionBoundsPath, unsatisfiedConstraints, untagTypedArray, viewToMat3, withAnchorPoints, withinPxBox, withinPxRadius, worldEditToStorage, writeAnchorField };
|
|
6005
6667
|
//# sourceMappingURL=index.js.map
|
|
6006
6668
|
//# sourceMappingURL=index.js.map
|