@willcgage/module-schematic 0.93.0 → 0.95.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
@@ -2306,6 +2306,16 @@ function pieceRoutePaths(piece, library = BUILT_IN_TRACK_PARTS) {
2306
2306
  }
2307
2307
  return out;
2308
2308
  }
2309
+ function pieceHand(part, flipped) {
2310
+ const n = part.partNumbers;
2311
+ if (!n?.left || !n?.right) return null;
2312
+ return flipped ? "right" : "left";
2313
+ }
2314
+ function piecePartNumber(part, flipped) {
2315
+ const hand = pieceHand(part, flipped);
2316
+ if (!hand) return part.partNumbers?.single;
2317
+ return part.partNumbers?.[hand];
2318
+ }
2309
2319
  function partsPlaceable(library = BUILT_IN_TRACK_PARTS) {
2310
2320
  const placeable = [];
2311
2321
  const blocked = [];
@@ -2442,6 +2452,33 @@ function snapPiece(moving, others, library = BUILT_IN_TRACK_PARTS, withinInches
2442
2452
  to: best.t.key
2443
2453
  };
2444
2454
  }
2455
+ function fitFlexBetween(piece, others, library = BUILT_IN_TRACK_PARTS, withinInches = 0.5) {
2456
+ const part = library.find((p) => p.id === piece.partId);
2457
+ if (part?.kind !== "flex" || piece.radiusInches) return null;
2458
+ const mine = placedJoints([piece], library);
2459
+ const far = mine.find((j) => j.joint === "b");
2460
+ const near = mine.find((j) => j.joint === "a");
2461
+ if (!far || !near) return null;
2462
+ const graph = buildTrackGraph(others, library);
2463
+ const taken = new Set(graph.connections.flatMap((c) => [c.a, c.b]));
2464
+ let best = null;
2465
+ for (const j of graph.joints) {
2466
+ if (taken.has(j.key)) continue;
2467
+ if (Math.hypot(j.x - near.x, j.y - near.y) <= JOINT_SNAP_INCHES) continue;
2468
+ const d = Math.hypot(j.x - far.x, j.y - far.y);
2469
+ if (d <= withinInches && (!best || d < best.d)) best = { j, d };
2470
+ }
2471
+ if (!best) return null;
2472
+ const dx = best.j.x - piece.x;
2473
+ const dy = best.j.y - piece.y;
2474
+ const len = Math.hypot(dx, dy);
2475
+ if (!(len > 0)) return null;
2476
+ return {
2477
+ ...piece,
2478
+ rotationDeg: norm360(Math.atan2(dy, dx) * 180 / Math.PI),
2479
+ lengthInches: Math.round(len * 1e3) / 1e3
2480
+ };
2481
+ }
2445
2482
  var danglingDivergeWarning = (id) => `the route diverging at ${id} is not connected to anything`;
2446
2483
  var unreachedWarning = (id) => `${id} is not reachable from the endplate \u2014 nothing connects it`;
2447
2484
  function walkTrackGraph(graph, pieces, startAt, library = BUILT_IN_TRACK_PARTS) {
@@ -2635,9 +2672,16 @@ function graphToDoc(pieces, input) {
2635
2672
  const trackIdOf = /* @__PURE__ */ new Map([["main", MAIN_TRACK_ID]]);
2636
2673
  if (twoMains) trackIdOf.set("main2:main", MAIN2_TRACK_ID);
2637
2674
  for (const r of branches) trackIdOf.set(r.id, r.pieces[0]);
2638
- const laneOf = (r) => {
2675
+ const emitted = /* @__PURE__ */ new Set();
2676
+ const uniqueBranches = branches.filter((r) => {
2677
+ const id = trackIdOf.get(r.id);
2678
+ if (emitted.has(id)) return false;
2679
+ emitted.add(id);
2680
+ return true;
2681
+ });
2682
+ const laneOf = (r, among) => {
2639
2683
  const side = Math.sign(r.lateral) || 1;
2640
- const sameSide = branches.filter((x) => (Math.sign(x.lateral) || 1) === side).sort((a, b) => Math.abs(a.lateral) - Math.abs(b.lateral));
2684
+ const sameSide = among.filter((x) => (Math.sign(x.lateral) || 1) === side).sort((a, b) => Math.abs(a.lateral) - Math.abs(b.lateral));
2641
2685
  const taken = side === main2Lane ? 1 : 0;
2642
2686
  return side * (sameSide.indexOf(r) + 1 + taken);
2643
2687
  };
@@ -2663,7 +2707,7 @@ function graphToDoc(pieces, input) {
2663
2707
  }
2664
2708
  ] : []
2665
2709
  ];
2666
- for (const r of branches) {
2710
+ for (const r of uniqueBranches) {
2667
2711
  const id = trackIdOf.get(r.id);
2668
2712
  const meta = input.meta?.[id] ?? {};
2669
2713
  tracks.push({
@@ -2671,7 +2715,7 @@ function graphToDoc(pieces, input) {
2671
2715
  // It runs back into a second turnout, or it doesn't. That is the whole
2672
2716
  // difference between a siding and a spur, and it is read, not declared.
2673
2717
  role: r.endsAt ? "siding" : "spur",
2674
- lane: laneOf(r),
2718
+ lane: laneOf(r, uniqueBranches),
2675
2719
  fromPos: round(Math.min(r.fromPos, r.toPos)),
2676
2720
  toPos: round(Math.max(r.fromPos, r.toPos)),
2677
2721
  ...meta.trackName ? { trackName: meta.trackName } : {},
@@ -2722,6 +2766,12 @@ function graphToDoc(pieces, input) {
2722
2766
  });
2723
2767
  }
2724
2768
  turnouts.sort((a, b) => a.pos - b.pos);
2769
+ for (const t of tracks) {
2770
+ if (t.role === "main") continue;
2771
+ const ends = turnouts.filter((sw) => sw.divergeTrack === t.id);
2772
+ if (ends.length >= 2 && new Set(ends.map((sw) => sw.onTrack)).size >= 2)
2773
+ t.role = "crossover";
2774
+ }
2725
2775
  const place = (a, what) => {
2726
2776
  if (!a) return null;
2727
2777
  const hit = resolveGraphAnchor(a, walk, pieces, library);
@@ -3475,6 +3525,7 @@ exports.endplateFaceSegments = endplateFaceSegments;
3475
3525
  exports.endplateLead = endplateLead;
3476
3526
  exports.endplateTrackOffsetInches = endplateTrackOffsetInches;
3477
3527
  exports.endplateWidthInches = endplateWidthInches;
3528
+ exports.fitFlexBetween = fitFlexBetween;
3478
3529
  exports.flexPartFor = flexPartFor;
3479
3530
  exports.flexParts = flexParts;
3480
3531
  exports.flexPieces = flexPieces;
@@ -3510,6 +3561,8 @@ exports.partOutlineAtFrog = partOutlineAtFrog;
3510
3561
  exports.partsPlaceable = partsPlaceable;
3511
3562
  exports.pastFrogInchesForSize = pastFrogInchesForSize;
3512
3563
  exports.pathLengthInches = pathLengthInches;
3564
+ exports.pieceHand = pieceHand;
3565
+ exports.piecePartNumber = piecePartNumber;
3513
3566
  exports.pieceRoutePaths = pieceRoutePaths;
3514
3567
  exports.placedJoints = placedJoints;
3515
3568
  exports.poseNeedsManual = poseNeedsManual;