@willcgage/module-schematic 0.95.1 → 0.96.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 CHANGED
@@ -1703,11 +1703,22 @@ var FAST_TRACKS_N_ME55_CROSSOVERS = [
1703
1703
  };
1704
1704
  });
1705
1705
  var DEFAULT_FLEX_PART_ID = "atlas-c55-n-flex";
1706
+ var GENERIC_END_OF_TRACK = [
1707
+ {
1708
+ id: "generic-bumper",
1709
+ manufacturer: "Generic",
1710
+ line: "N scale",
1711
+ scale: "N",
1712
+ name: "Bumper",
1713
+ kind: "bumper"
1714
+ }
1715
+ ];
1706
1716
  var BUILT_IN_TRACK_PARTS = [
1707
1717
  ...ATLAS_CODE55_N,
1708
1718
  ...FAST_TRACKS_N_ME55,
1709
1719
  ...FAST_TRACKS_N_ME55_CROSSOVERS,
1710
- ...FLEX_TRACK_PARTS
1720
+ ...FLEX_TRACK_PARTS,
1721
+ ...GENERIC_END_OF_TRACK
1711
1722
  ];
1712
1723
  function flexParts(library = BUILT_IN_TRACK_PARTS) {
1713
1724
  return library.filter((p) => p.kind === "flex");
@@ -2162,6 +2173,7 @@ function turnoutClosure(size, opts = {}) {
2162
2173
  }
2163
2174
  function partGeometryGap(part) {
2164
2175
  if (part.kind === "flex") return null;
2176
+ if (part.kind === "bumper") return null;
2165
2177
  if (part.kind === "crossover")
2166
2178
  return "a crossover fixture builds one HALF of the assembly (piecesPerAssembly), so its published lengths describe a piece, not the finished part \u2014 the geometry of the whole crossover is not yet derivable from them";
2167
2179
  if (part.kind === "crossing") return "crossing geometry is not modelled yet";
@@ -2175,6 +2187,14 @@ function partGeometryGap(part) {
2175
2187
  }
2176
2188
  function partGeometry(part, library = BUILT_IN_TRACK_PARTS) {
2177
2189
  if (partGeometryGap(part)) return null;
2190
+ if (part.kind === "bumper") {
2191
+ return {
2192
+ joints: [{ id: "a", role: "throat", x: 0, y: 0, angleDeg: 180 }],
2193
+ routes: [],
2194
+ source: part.overallLength?.source ?? "unverified",
2195
+ divergingEndMeasured: false
2196
+ };
2197
+ }
2178
2198
  if (part.kind === "flex") {
2179
2199
  return {
2180
2200
  joints: [
@@ -2265,6 +2285,12 @@ function pieceRoutePaths(piece, library = BUILT_IN_TRACK_PARTS) {
2265
2285
  return { x: piece.x + x * c - ly * s, y: piece.y + x * s + ly * c };
2266
2286
  };
2267
2287
  const out = [];
2288
+ if (part.kind === "bumper") {
2289
+ const at2 = joints[0];
2290
+ if (!at2) return out;
2291
+ const len = part.overallLength?.inches ?? BUMPER_DRAWN_INCHES;
2292
+ return [{ route: ["a", "a"], points: [{ x: at2.x, y: at2.y }, place(len, 0)] }];
2293
+ }
2268
2294
  for (const route of geo.routes) {
2269
2295
  const a = at(route[0]);
2270
2296
  const b = at(route[1]);
@@ -2328,6 +2354,7 @@ function partsPlaceable(library = BUILT_IN_TRACK_PARTS) {
2328
2354
  return { placeable, blocked };
2329
2355
  }
2330
2356
  var JOINT_SNAP_INCHES = 0.01;
2357
+ var BUMPER_DRAWN_INCHES = 0.75;
2331
2358
  function flexRunEnd(lengthInches, radiusInches) {
2332
2359
  const L = lengthInches;
2333
2360
  const R = radiusInches;
@@ -2519,6 +2546,7 @@ function walkTrackGraph(graph, pieces, startAt, library = BUILT_IN_TRACK_PARTS)
2519
2546
  toPos: startPos,
2520
2547
  bornAt,
2521
2548
  endsAt: null,
2549
+ closedBy: null,
2522
2550
  pieces: [],
2523
2551
  spans: [],
2524
2552
  lateral: 0
@@ -2533,7 +2561,11 @@ function walkTrackGraph(graph, pieces, startAt, library = BUILT_IN_TRACK_PARTS)
2533
2561
  const geo = geoOf(here.piece);
2534
2562
  if (!geo) break;
2535
2563
  const opts = geo.routes.filter((r) => r.includes(here.joint));
2536
- if (!opts.length) break;
2564
+ if (!opts.length) {
2565
+ route.pieces.push(here.piece);
2566
+ route.closedBy = here.piece;
2567
+ break;
2568
+ }
2537
2569
  const pick = opts.find((r) => r.includes("through")) ?? opts[0];
2538
2570
  const exitJoint = pick[0] === here.joint ? pick[1] : pick[0];
2539
2571
  const exit = byKey.get(`${here.piece}.${exitJoint}`);
@@ -2691,7 +2723,10 @@ function graphToDoc(pieces, input) {
2691
2723
  role: "main",
2692
2724
  lane: 0,
2693
2725
  from: epA?.id ?? "A",
2694
- ...epB ? { to: epB.id } : {}
2726
+ ...epB ? { to: epB.id } : {},
2727
+ // A main can be closed too — that is a pocket module, where the track
2728
+ // runs in and stops rather than crossing to a second endplate.
2729
+ ...main.closedBy ? { bumperAt: "to" } : {}
2695
2730
  },
2696
2731
  ...twoMains ? [
2697
2732
  {
@@ -2718,6 +2753,11 @@ function graphToDoc(pieces, input) {
2718
2753
  lane: laneOf(r, uniqueBranches),
2719
2754
  fromPos: round(Math.min(r.fromPos, r.toPos)),
2720
2755
  toPos: round(Math.max(r.fromPos, r.toPos)),
2756
+ // ⚠️ ALWAYS THE `to` END, and not because of an assumption: a branch's
2757
+ // positions are ARC LENGTH FROM ITS THROAT, so they only ever grow — a
2758
+ // spur running physically back toward endplate A still ends at the larger
2759
+ // number. The closed end is the far one from the turnout, by definition.
2760
+ ...r.closedBy ? { bumperAt: "to" } : {},
2721
2761
  ...meta.trackName ? { trackName: meta.trackName } : {},
2722
2762
  ...meta.capacityFeet != null ? { capacityFeet: meta.capacityFeet } : {},
2723
2763
  ...meta.moduleTrackId != null ? { moduleTrackId: meta.moduleTrackId } : {}
@@ -3477,6 +3517,7 @@ function poseOverridesFromDoc(doc) {
3477
3517
 
3478
3518
  exports.ATLAS_CODE55_N = ATLAS_CODE55_N;
3479
3519
  exports.BUILT_IN_TRACK_PARTS = BUILT_IN_TRACK_PARTS;
3520
+ exports.BUMPER_DRAWN_INCHES = BUMPER_DRAWN_INCHES;
3480
3521
  exports.CLEARANCE_SPACING_INCHES = CLEARANCE_SPACING_INCHES;
3481
3522
  exports.CODE55_RAIL_HEIGHT_INCHES = CODE55_RAIL_HEIGHT_INCHES;
3482
3523
  exports.DEFAULT_FLEX_PART_ID = DEFAULT_FLEX_PART_ID;
@@ -3491,6 +3532,7 @@ exports.FREEMO_ENDPLATE_WIDTH_RECOMMENDED_INCHES = FREEMO_ENDPLATE_WIDTH_RECOMME
3491
3532
  exports.FREEMO_MAIN_MIN_RADIUS_INCHES = FREEMO_MAIN_MIN_RADIUS_INCHES;
3492
3533
  exports.FREEMO_REVERSE_CURVE_STRAIGHT_INCHES = FREEMO_REVERSE_CURVE_STRAIGHT_INCHES;
3493
3534
  exports.FREEMO_TRACK_SPACING_INCHES = FREEMO_TRACK_SPACING_INCHES;
3535
+ exports.GENERIC_END_OF_TRACK = GENERIC_END_OF_TRACK;
3494
3536
  exports.JOINT_SNAP_INCHES = JOINT_SNAP_INCHES;
3495
3537
  exports.MAIN2_TRACK_ID = MAIN2_TRACK_ID;
3496
3538
  exports.MAIN_TRACK_ID = MAIN_TRACK_ID;