@willcgage/module-schematic 0.86.0 → 0.90.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/index.cjs +262 -3
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +214 -1
- package/dist/index.d.ts +214 -1
- package/dist/index.js +258 -4
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -1046,6 +1046,10 @@ function stateToDoc(state, recordNumber) {
|
|
|
1046
1046
|
track: ind.track,
|
|
1047
1047
|
fromPos: ind.fromPos,
|
|
1048
1048
|
toPos: ind.toPos,
|
|
1049
|
+
// Carried, not interpreted. An anchor is what HOLDS the span (ADR
|
|
1050
|
+
// 0001); dropping it here would quietly convert an anchored
|
|
1051
|
+
// industry back into a typed number the next time anyone saved.
|
|
1052
|
+
...ind.anchor ? { anchor: ind.anchor } : {},
|
|
1049
1053
|
...ind.spots?.length ? { spots: ind.spots } : {},
|
|
1050
1054
|
side: ind.side,
|
|
1051
1055
|
...ind.labelMode && ind.labelMode !== "none" ? { labelMode: ind.labelMode } : {},
|
|
@@ -1065,7 +1069,11 @@ function stateToDoc(state, recordNumber) {
|
|
|
1065
1069
|
...state.sections.length ? { sections: moduleSections({ sections: state.sections }) } : {},
|
|
1066
1070
|
// Authored mainline path (module-local inches); only when it's a real path.
|
|
1067
1071
|
...state.mainPath.length >= 2 ? { mainPath: state.mainPath } : {},
|
|
1068
|
-
...state.main2Path.length >= 2 ? { main2Path: state.main2Path } : {}
|
|
1072
|
+
...state.main2Path.length >= 2 ? { main2Path: state.main2Path } : {},
|
|
1073
|
+
// The pieces the owner drew, carried verbatim (ADR 0001). This function
|
|
1074
|
+
// stays dumb about them on purpose: deriving here would put the derivation
|
|
1075
|
+
// in every save path in both apps. `deriveGraphDoc` is the one place.
|
|
1076
|
+
...state.graph?.pieces?.length ? { graph: state.graph } : {}
|
|
1069
1077
|
};
|
|
1070
1078
|
}
|
|
1071
1079
|
function docToState(doc, fallbackLength, moduleTracks = []) {
|
|
@@ -1210,6 +1218,10 @@ function docToState(doc, fallbackLength, moduleTracks = []) {
|
|
|
1210
1218
|
sections: moduleSections(d),
|
|
1211
1219
|
mainPath,
|
|
1212
1220
|
main2Path,
|
|
1221
|
+
// ⚠️ NOT scaled by `sc()` like every position above it. A piece is placed in
|
|
1222
|
+
// real inches on the board; stretching the module's length does not move it,
|
|
1223
|
+
// and the length is DERIVED from the pieces anyway when a graph is present.
|
|
1224
|
+
...d.graph?.pieces?.length ? { graph: d.graph } : {},
|
|
1213
1225
|
crossings: (d.crossings ?? []).map((x) => ({
|
|
1214
1226
|
id: x.id,
|
|
1215
1227
|
name: x.name ?? "",
|
|
@@ -1244,12 +1256,14 @@ function docToState(doc, fallbackLength, moduleTracks = []) {
|
|
|
1244
1256
|
track: s.track,
|
|
1245
1257
|
fromPos: sc(s.fromPos ?? 0),
|
|
1246
1258
|
toPos: s.toPos != null ? sc(s.toPos) : len,
|
|
1247
|
-
...s.side ? { side: s.side } : {}
|
|
1259
|
+
...s.side ? { side: s.side } : {},
|
|
1260
|
+
...s.anchor ? { anchor: s.anchor } : {}
|
|
1248
1261
|
})),
|
|
1249
1262
|
side: ind.side ?? "above",
|
|
1250
1263
|
labelMode: ind.labelMode ?? "none",
|
|
1251
1264
|
carTypes: Array.isArray(ind.carTypes) ? ind.carTypes : [],
|
|
1252
|
-
moduleIndustryId: ind.moduleIndustryId ?? null
|
|
1265
|
+
moduleIndustryId: ind.moduleIndustryId ?? null,
|
|
1266
|
+
...ind.anchor ? { anchor: ind.anchor } : {}
|
|
1253
1267
|
}))
|
|
1254
1268
|
};
|
|
1255
1269
|
}
|
|
@@ -2234,6 +2248,54 @@ function partGeometry(part, library = BUILT_IN_TRACK_PARTS) {
|
|
|
2234
2248
|
divergingEndMeasured: de.measured
|
|
2235
2249
|
};
|
|
2236
2250
|
}
|
|
2251
|
+
function pieceRoutePaths(piece, library = BUILT_IN_TRACK_PARTS) {
|
|
2252
|
+
const part = library.find((p) => p.id === piece.partId);
|
|
2253
|
+
if (!part) return [];
|
|
2254
|
+
const geo = partGeometry(part, library);
|
|
2255
|
+
if (!geo) return [];
|
|
2256
|
+
const joints = placedJoints([piece], library);
|
|
2257
|
+
const at = (id) => joints.find((j) => j.joint === id);
|
|
2258
|
+
const RAD = Math.PI / 180;
|
|
2259
|
+
const c = Math.cos(piece.rotationDeg * RAD);
|
|
2260
|
+
const s = Math.sin(piece.rotationDeg * RAD);
|
|
2261
|
+
const place = (x, y) => {
|
|
2262
|
+
const ly = piece.flipped ? -y : y;
|
|
2263
|
+
return { x: piece.x + x * c - ly * s, y: piece.y + x * s + ly * c };
|
|
2264
|
+
};
|
|
2265
|
+
const out = [];
|
|
2266
|
+
for (const route of geo.routes) {
|
|
2267
|
+
const a = at(route[0]);
|
|
2268
|
+
const b = at(route[1]);
|
|
2269
|
+
if (!a || !b) continue;
|
|
2270
|
+
const ends = [{ x: a.x, y: a.y }, { x: b.x, y: b.y }];
|
|
2271
|
+
const diverging = route.some((r) => r === "diverge" || r === "legA" || r === "legB");
|
|
2272
|
+
if (!diverging || part.kind === "flex") {
|
|
2273
|
+
out.push({ route, points: ends });
|
|
2274
|
+
continue;
|
|
2275
|
+
}
|
|
2276
|
+
const lead = part.lead?.inches ?? 0;
|
|
2277
|
+
const pts0 = part.pointsOffset?.inches ?? 0;
|
|
2278
|
+
const far = geo.joints.find((j) => j.id === route[1]) ?? geo.joints.find((j) => j.id === route[0]);
|
|
2279
|
+
if (!far || !(lead > 0)) {
|
|
2280
|
+
out.push({ route, points: ends });
|
|
2281
|
+
continue;
|
|
2282
|
+
}
|
|
2283
|
+
const isWye = part.kind === "wye";
|
|
2284
|
+
const closure = turnoutClosure(isWye ? part.frogNumber * 2 : part.frogNumber, {
|
|
2285
|
+
leadInches: lead
|
|
2286
|
+
});
|
|
2287
|
+
const mirror = route.includes("legB") ? -1 : 1;
|
|
2288
|
+
const pts = [place(0, 0), place(pts0, 0)];
|
|
2289
|
+
const steps = 12;
|
|
2290
|
+
for (let i = 1; i <= steps; i++) {
|
|
2291
|
+
const x = pts0 + (far.x - pts0) * i / steps;
|
|
2292
|
+
pts.push(place(x, mirror * closure.offsetAt(x - pts0)));
|
|
2293
|
+
}
|
|
2294
|
+
pts[pts.length - 1] = { x: b.x, y: b.y };
|
|
2295
|
+
out.push({ route, points: route[0] === "throat" ? pts : pts.slice().reverse() });
|
|
2296
|
+
}
|
|
2297
|
+
return out;
|
|
2298
|
+
}
|
|
2237
2299
|
function partsPlaceable(library = BUILT_IN_TRACK_PARTS) {
|
|
2238
2300
|
const placeable = [];
|
|
2239
2301
|
const blocked = [];
|
|
@@ -2318,6 +2380,36 @@ function buildTrackGraph(pieces, library = BUILT_IN_TRACK_PARTS, snapInches = JO
|
|
|
2318
2380
|
}
|
|
2319
2381
|
return { joints, connections, open, conflicts, unplaceable };
|
|
2320
2382
|
}
|
|
2383
|
+
function snapPiece(moving, others, library = BUILT_IN_TRACK_PARTS, withinInches = 0.5) {
|
|
2384
|
+
const mine = placedJoints([moving], library);
|
|
2385
|
+
if (!mine.length) return null;
|
|
2386
|
+
const graph = buildTrackGraph(others, library);
|
|
2387
|
+
const taken = new Set(graph.connections.flatMap((c2) => [c2.a, c2.b]));
|
|
2388
|
+
const open = graph.joints.filter((j) => !taken.has(j.key));
|
|
2389
|
+
let best = null;
|
|
2390
|
+
for (const m of mine)
|
|
2391
|
+
for (const t of open) {
|
|
2392
|
+
const d = Math.hypot(t.x - m.x, t.y - m.y);
|
|
2393
|
+
if (d <= withinInches && (!best || d < best.d)) best = { m, t, d };
|
|
2394
|
+
}
|
|
2395
|
+
if (!best) return null;
|
|
2396
|
+
const RAD = Math.PI / 180;
|
|
2397
|
+
const dRot = norm360(best.t.headingDeg + 180 - best.m.headingDeg);
|
|
2398
|
+
const c = Math.cos(dRot * RAD);
|
|
2399
|
+
const s = Math.sin(dRot * RAD);
|
|
2400
|
+
const ox = moving.x - best.m.x;
|
|
2401
|
+
const oy = moving.y - best.m.y;
|
|
2402
|
+
return {
|
|
2403
|
+
piece: {
|
|
2404
|
+
...moving,
|
|
2405
|
+
rotationDeg: norm360(moving.rotationDeg + dRot),
|
|
2406
|
+
x: best.t.x + ox * c - oy * s,
|
|
2407
|
+
y: best.t.y + ox * s + oy * c
|
|
2408
|
+
},
|
|
2409
|
+
from: best.m.key,
|
|
2410
|
+
to: best.t.key
|
|
2411
|
+
};
|
|
2412
|
+
}
|
|
2321
2413
|
function walkTrackGraph(graph, pieces, startAt, library = BUILT_IN_TRACK_PARTS) {
|
|
2322
2414
|
const byKey = new Map(graph.joints.map((j) => [j.key, j]));
|
|
2323
2415
|
const pieceById = new Map(pieces.map((p) => [p.id, p]));
|
|
@@ -2348,6 +2440,7 @@ function walkTrackGraph(graph, pieces, startAt, library = BUILT_IN_TRACK_PARTS)
|
|
|
2348
2440
|
bornAt,
|
|
2349
2441
|
endsAt: null,
|
|
2350
2442
|
pieces: [],
|
|
2443
|
+
spans: [],
|
|
2351
2444
|
lateral: 0
|
|
2352
2445
|
};
|
|
2353
2446
|
let cur = startKey;
|
|
@@ -2405,7 +2498,15 @@ function walkTrackGraph(graph, pieces, startAt, library = BUILT_IN_TRACK_PARTS)
|
|
|
2405
2498
|
for (const j of graph.joints)
|
|
2406
2499
|
if (j.piece === here.piece && Math.abs(j.y) > Math.abs(route.lateral))
|
|
2407
2500
|
route.lateral = j.y;
|
|
2501
|
+
const entered = pos;
|
|
2408
2502
|
pos += gap(here, exit);
|
|
2503
|
+
route.spans.push({
|
|
2504
|
+
piece: here.piece,
|
|
2505
|
+
entryJoint: here.joint,
|
|
2506
|
+
exitJoint,
|
|
2507
|
+
fromPos: entered,
|
|
2508
|
+
toPos: pos
|
|
2509
|
+
});
|
|
2409
2510
|
route.toPos = pos;
|
|
2410
2511
|
const next = exit ? link.get(exit.key) : void 0;
|
|
2411
2512
|
if (!next) break;
|
|
@@ -2422,6 +2523,12 @@ function walkTrackGraph(graph, pieces, startAt, library = BUILT_IN_TRACK_PARTS)
|
|
|
2422
2523
|
warnings.push(`the route diverging at ${b.from} is not connected to anything`);
|
|
2423
2524
|
continue;
|
|
2424
2525
|
}
|
|
2526
|
+
const already = routes.find((r2) => r2.pieces.includes(byKey.get(start).piece));
|
|
2527
|
+
if (already) {
|
|
2528
|
+
const swFar = turnouts.find((t) => t.id === b.from);
|
|
2529
|
+
if (swFar && !swFar.divergeRoute) swFar.divergeRoute = already.id;
|
|
2530
|
+
continue;
|
|
2531
|
+
}
|
|
2425
2532
|
n += 1;
|
|
2426
2533
|
const r = walk(start, b.at + b.skew, `route${n}`, b.from);
|
|
2427
2534
|
r.fromPos = b.at;
|
|
@@ -2436,6 +2543,153 @@ function walkTrackGraph(graph, pieces, startAt, library = BUILT_IN_TRACK_PARTS)
|
|
|
2436
2543
|
for (const c of graph.conflicts) warnings.push(c.reason);
|
|
2437
2544
|
return { routes, turnouts, warnings };
|
|
2438
2545
|
}
|
|
2546
|
+
function resolveGraphAnchor(anchor, walk, pieces, library = BUILT_IN_TRACK_PARTS) {
|
|
2547
|
+
const piece = pieces.find((p) => p.id === anchor.piece);
|
|
2548
|
+
const part = piece ? library.find((x) => x.id === piece.partId) : void 0;
|
|
2549
|
+
const origin = part?.kind === "flex" ? "a" : "throat";
|
|
2550
|
+
for (const r of walk.routes) {
|
|
2551
|
+
const span = r.spans.find((s) => s.piece === anchor.piece);
|
|
2552
|
+
if (!span) continue;
|
|
2553
|
+
if (span.entryJoint === origin)
|
|
2554
|
+
return { pos: span.fromPos + anchor.atInches, routeId: r.id, reversed: false };
|
|
2555
|
+
if (span.exitJoint === origin)
|
|
2556
|
+
return { pos: span.toPos - anchor.atInches, routeId: r.id, reversed: true };
|
|
2557
|
+
return null;
|
|
2558
|
+
}
|
|
2559
|
+
return null;
|
|
2560
|
+
}
|
|
2561
|
+
function graphToDoc(pieces, input) {
|
|
2562
|
+
const library = input.library ?? BUILT_IN_TRACK_PARTS;
|
|
2563
|
+
const graph = buildTrackGraph(pieces, library);
|
|
2564
|
+
const walk = walkTrackGraph(graph, pieces, input.startAt, library);
|
|
2565
|
+
const warnings = [...walk.warnings];
|
|
2566
|
+
const round = (n) => Math.round(n * 100) / 100;
|
|
2567
|
+
const base = input.base ?? {};
|
|
2568
|
+
const endplates = base.endplates && base.endplates.length ? base.endplates : [{ id: "A", label: "West" }, { id: "B", label: "East" }];
|
|
2569
|
+
const epA = endplates[0];
|
|
2570
|
+
const epB = endplates[1];
|
|
2571
|
+
const main = walk.routes.find((r) => r.id === "main");
|
|
2572
|
+
const lengthInches = round(main.toPos);
|
|
2573
|
+
const branches = walk.routes.filter((r) => r.id !== "main" && r.pieces.length);
|
|
2574
|
+
const trackIdOf = /* @__PURE__ */ new Map([["main", MAIN_TRACK_ID]]);
|
|
2575
|
+
for (const r of branches) trackIdOf.set(r.id, r.pieces[0]);
|
|
2576
|
+
const laneOf = (r) => {
|
|
2577
|
+
const side = Math.sign(r.lateral) || 1;
|
|
2578
|
+
const sameSide = branches.filter((x) => (Math.sign(x.lateral) || 1) === side).sort((a, b) => Math.abs(a.lateral) - Math.abs(b.lateral));
|
|
2579
|
+
return side * (sameSide.indexOf(r) + 1);
|
|
2580
|
+
};
|
|
2581
|
+
const tracks = [
|
|
2582
|
+
{
|
|
2583
|
+
id: MAIN_TRACK_ID,
|
|
2584
|
+
role: "main",
|
|
2585
|
+
lane: 0,
|
|
2586
|
+
from: epA?.id ?? "A",
|
|
2587
|
+
...epB ? { to: epB.id } : {}
|
|
2588
|
+
}
|
|
2589
|
+
];
|
|
2590
|
+
for (const r of branches) {
|
|
2591
|
+
const id = trackIdOf.get(r.id);
|
|
2592
|
+
const meta = input.meta?.[id] ?? {};
|
|
2593
|
+
tracks.push({
|
|
2594
|
+
id,
|
|
2595
|
+
// It runs back into a second turnout, or it doesn't. That is the whole
|
|
2596
|
+
// difference between a siding and a spur, and it is read, not declared.
|
|
2597
|
+
role: r.endsAt ? "siding" : "spur",
|
|
2598
|
+
lane: laneOf(r),
|
|
2599
|
+
fromPos: round(Math.min(r.fromPos, r.toPos)),
|
|
2600
|
+
toPos: round(Math.max(r.fromPos, r.toPos)),
|
|
2601
|
+
...meta.trackName ? { trackName: meta.trackName } : {},
|
|
2602
|
+
...meta.capacityFeet != null ? { capacityFeet: meta.capacityFeet } : {},
|
|
2603
|
+
...meta.moduleTrackId != null ? { moduleTrackId: meta.moduleTrackId } : {}
|
|
2604
|
+
});
|
|
2605
|
+
}
|
|
2606
|
+
const pieceById = new Map(pieces.map((p) => [p.id, p]));
|
|
2607
|
+
const turnouts = [];
|
|
2608
|
+
for (const t of walk.turnouts) {
|
|
2609
|
+
const diverge = t.divergeRoute ? trackIdOf.get(t.divergeRoute) : void 0;
|
|
2610
|
+
if (!diverge) {
|
|
2611
|
+
warnings.push(
|
|
2612
|
+
`${t.id} is placed but its diverging route goes nowhere, so it is not in the operations view`
|
|
2613
|
+
);
|
|
2614
|
+
continue;
|
|
2615
|
+
}
|
|
2616
|
+
const piece = pieceById.get(t.id);
|
|
2617
|
+
const part = piece ? library.find((p) => p.id === piece.partId) : void 0;
|
|
2618
|
+
turnouts.push({
|
|
2619
|
+
id: t.id,
|
|
2620
|
+
pos: round(t.pos),
|
|
2621
|
+
onTrack: trackIdOf.get(t.onRoute) ?? MAIN_TRACK_ID,
|
|
2622
|
+
divergeTrack: diverge,
|
|
2623
|
+
...piece?.name ? { name: piece.name } : {},
|
|
2624
|
+
...part?.frogNumber != null ? { size: part.frogNumber } : {},
|
|
2625
|
+
...part ? { partId: part.id } : {}
|
|
2626
|
+
});
|
|
2627
|
+
}
|
|
2628
|
+
turnouts.sort((a, b) => a.pos - b.pos);
|
|
2629
|
+
const place = (a, what) => {
|
|
2630
|
+
if (!a) return null;
|
|
2631
|
+
const hit = resolveGraphAnchor(a, walk, pieces, library);
|
|
2632
|
+
if (!hit) {
|
|
2633
|
+
warnings.push(
|
|
2634
|
+
`${what} is anchored to ${a.piece}, which is not on any route \u2014 its authored position stands`
|
|
2635
|
+
);
|
|
2636
|
+
return null;
|
|
2637
|
+
}
|
|
2638
|
+
return hit;
|
|
2639
|
+
};
|
|
2640
|
+
const spanAt = (hit, from, to) => {
|
|
2641
|
+
const len = Math.abs(to - from);
|
|
2642
|
+
return hit.reversed ? [round(hit.pos - len), round(hit.pos)] : [round(hit.pos), round(hit.pos + len)];
|
|
2643
|
+
};
|
|
2644
|
+
const industries = base.industries?.map((ind) => {
|
|
2645
|
+
const hit = place(ind.anchor, `industry "${ind.name}"`);
|
|
2646
|
+
const spots = ind.spots?.map((s) => {
|
|
2647
|
+
const sh = place(s.anchor, `a spot of industry "${ind.name}"`);
|
|
2648
|
+
if (!sh) return s;
|
|
2649
|
+
const [from, to] = spanAt(sh, s.fromPos, s.toPos);
|
|
2650
|
+
return { ...s, track: trackIdOf.get(sh.routeId) ?? s.track, fromPos: from, toPos: to };
|
|
2651
|
+
});
|
|
2652
|
+
if (!hit) return spots ? { ...ind, spots } : ind;
|
|
2653
|
+
const [fromPos, toPos] = spanAt(hit, ind.fromPos, ind.toPos);
|
|
2654
|
+
return {
|
|
2655
|
+
...ind,
|
|
2656
|
+
track: trackIdOf.get(hit.routeId) ?? ind.track,
|
|
2657
|
+
fromPos,
|
|
2658
|
+
toPos,
|
|
2659
|
+
...spots ? { spots } : {}
|
|
2660
|
+
};
|
|
2661
|
+
});
|
|
2662
|
+
const signals = base.signals?.map((sig) => {
|
|
2663
|
+
const hit = place(sig.anchor, `signal "${sig.name ?? sig.id}"`);
|
|
2664
|
+
return hit ? { ...sig, pos: round(hit.pos), track: trackIdOf.get(hit.routeId) ?? sig.track } : sig;
|
|
2665
|
+
});
|
|
2666
|
+
const doc = {
|
|
2667
|
+
version: 1,
|
|
2668
|
+
...base,
|
|
2669
|
+
lengthInches,
|
|
2670
|
+
endplates,
|
|
2671
|
+
tracks,
|
|
2672
|
+
turnouts,
|
|
2673
|
+
...industries ? { industries } : {},
|
|
2674
|
+
...signals ? { signals } : {}
|
|
2675
|
+
};
|
|
2676
|
+
return { doc, graph, walk, warnings };
|
|
2677
|
+
}
|
|
2678
|
+
function deriveGraphDoc(doc, library = BUILT_IN_TRACK_PARTS) {
|
|
2679
|
+
const g = doc.graph;
|
|
2680
|
+
if (!g || !g.pieces?.length || !g.startAt) return { doc, warnings: [] };
|
|
2681
|
+
const meta = {};
|
|
2682
|
+
for (const t of doc.tracks ?? []) {
|
|
2683
|
+
if (t.role === "main") continue;
|
|
2684
|
+
meta[t.id] = {
|
|
2685
|
+
...t.trackName ? { trackName: t.trackName } : {},
|
|
2686
|
+
...t.capacityFeet != null ? { capacityFeet: t.capacityFeet } : {},
|
|
2687
|
+
...t.moduleTrackId != null ? { moduleTrackId: t.moduleTrackId } : {}
|
|
2688
|
+
};
|
|
2689
|
+
}
|
|
2690
|
+
const out = graphToDoc(g.pieces, { startAt: g.startAt, base: doc, meta, library });
|
|
2691
|
+
return { doc: out.doc, warnings: out.warnings };
|
|
2692
|
+
}
|
|
2439
2693
|
function frogCasting(cl, opts = {}) {
|
|
2440
2694
|
const g = opts.gaugeInches ?? RAIL_GAUGE_INCHES;
|
|
2441
2695
|
const w = opts.reachInches ?? g * 1.5;
|
|
@@ -3107,6 +3361,7 @@ exports.checkEndplateWidth = checkEndplateWidth;
|
|
|
3107
3361
|
exports.clearancePointPastFrogInches = clearancePointPastFrogInches;
|
|
3108
3362
|
exports.crossoverPinches = crossoverPinches;
|
|
3109
3363
|
exports.deriveEndplatePoses = deriveEndplatePoses;
|
|
3364
|
+
exports.deriveGraphDoc = deriveGraphDoc;
|
|
3110
3365
|
exports.divergeSideForHand = divergeSideForHand;
|
|
3111
3366
|
exports.docToState = docToState;
|
|
3112
3367
|
exports.emptyEditorState = emptyEditorState;
|
|
@@ -3124,6 +3379,7 @@ exports.frogCasting = frogCasting;
|
|
|
3124
3379
|
exports.frogNumberFromName = frogNumberFromName;
|
|
3125
3380
|
exports.fromSectionRelative = fromSectionRelative;
|
|
3126
3381
|
exports.geometryTurnDegrees = geometryTurnDegrees;
|
|
3382
|
+
exports.graphToDoc = graphToDoc;
|
|
3127
3383
|
exports.hasNoFarEndplate = hasNoFarEndplate;
|
|
3128
3384
|
exports.importedPartToTrackPart = importedPartToTrackPart;
|
|
3129
3385
|
exports.inchesToScaleFeet = inchesToScaleFeet;
|
|
@@ -3149,11 +3405,13 @@ exports.partOutlineAtFrog = partOutlineAtFrog;
|
|
|
3149
3405
|
exports.partsPlaceable = partsPlaceable;
|
|
3150
3406
|
exports.pastFrogInchesForSize = pastFrogInchesForSize;
|
|
3151
3407
|
exports.pathLengthInches = pathLengthInches;
|
|
3408
|
+
exports.pieceRoutePaths = pieceRoutePaths;
|
|
3152
3409
|
exports.placedJoints = placedJoints;
|
|
3153
3410
|
exports.poseNeedsManual = poseNeedsManual;
|
|
3154
3411
|
exports.poseOverridesFromDoc = poseOverridesFromDoc;
|
|
3155
3412
|
exports.remapPos = remapPos;
|
|
3156
3413
|
exports.resizeFlexPiece = resizeFlexPiece;
|
|
3414
|
+
exports.resolveGraphAnchor = resolveGraphAnchor;
|
|
3157
3415
|
exports.returnLoop = returnLoop;
|
|
3158
3416
|
exports.sampleBenchworkOutline = sampleBenchworkOutline;
|
|
3159
3417
|
exports.samplePartSegments = samplePartSegments;
|
|
@@ -3170,6 +3428,7 @@ exports.sectionSpansOrWhole = sectionSpansOrWhole;
|
|
|
3170
3428
|
exports.sectionedCenterline = sectionedCenterline;
|
|
3171
3429
|
exports.sectionedEndPose = sectionedEndPose;
|
|
3172
3430
|
exports.sliceCenterline = sliceCenterline;
|
|
3431
|
+
exports.snapPiece = snapPiece;
|
|
3173
3432
|
exports.spanOverhang = spanOverhang;
|
|
3174
3433
|
exports.stateToDoc = stateToDoc;
|
|
3175
3434
|
exports.storedPartToTrackPart = storedPartToTrackPart;
|