@willcgage/module-schematic 0.82.0 → 0.83.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
@@ -5,6 +5,46 @@ var FREEMO_ENDPLATE_WIDTH_MIN_INCHES = 12;
5
5
  var FREEMO_ENDPLATE_WIDTH_RECOMMENDED_INCHES = 24;
6
6
  var FREEMO_TRACK_SPACING_INCHES = 1.125;
7
7
  var FREEMO_ENDPLATE_TRACK_FASCIA_CLEARANCE_INCHES = 4;
8
+ var PINCH_EASE_INCHES = 3;
9
+ function laneOffsetAt(lane, pos, pinches, spacingInches = FREEMO_TRACK_SPACING_INCHES) {
10
+ const base = (lane ?? 0) * spacingInches;
11
+ if (!lane || !pinches?.length) return base;
12
+ const sign = Math.sign(lane);
13
+ const smooth = (t) => t * t * (3 - 2 * t);
14
+ let off = base;
15
+ for (const p of pinches) {
16
+ if (p.lane !== lane) continue;
17
+ const a = Math.min(p.fromPos, p.toPos);
18
+ const b = Math.max(p.fromPos, p.toPos);
19
+ const target = base - sign * (spacingInches * Math.abs(lane) - p.spacingInches);
20
+ const e = PINCH_EASE_INCHES;
21
+ let t = null;
22
+ if (pos >= a && pos <= b) t = 1;
23
+ else if (pos > a - e && pos < a) t = smooth((pos - (a - e)) / e);
24
+ else if (pos > b && pos < b + e) t = smooth(1 - (pos - b) / e);
25
+ if (t == null) continue;
26
+ const candidate = base + (target - base) * t;
27
+ if (Math.abs(candidate - base) > Math.abs(off - base)) off = candidate;
28
+ }
29
+ return off;
30
+ }
31
+ function crossoverPinches(tracks, library = BUILT_IN_TRACK_PARTS, spacingInches = FREEMO_TRACK_SPACING_INCHES) {
32
+ const out = [];
33
+ for (const t of tracks) {
34
+ if (t.role !== "crossover" || !t.crossoverPartId) continue;
35
+ const part = library.find((p) => p.id === t.crossoverPartId);
36
+ const s = part?.trackSpacing?.inches;
37
+ if (typeof s !== "number" || !Number.isFinite(s) || s <= 0) continue;
38
+ if (Math.abs(s - spacingInches) < 1e-9) continue;
39
+ const lane = t.lane ?? 1;
40
+ if (!lane) continue;
41
+ const a = t.fromPos ?? 0;
42
+ const b = t.toPos ?? 0;
43
+ if (!Number.isFinite(a) || !Number.isFinite(b) || Math.abs(b - a) < 1e-9) continue;
44
+ out.push({ lane, fromPos: Math.min(a, b), toPos: Math.max(a, b), spacingInches: s });
45
+ }
46
+ return out;
47
+ }
8
48
  function endplateWidthInches(ep) {
9
49
  const w = ep?.widthInches;
10
50
  return typeof w === "number" && w > 0 ? w : FREEMO_ENDPLATE_WIDTH_RECOMMENDED_INCHES;
@@ -943,6 +983,7 @@ function stateToDoc(state, recordNumber) {
943
983
  }).scaleFeet,
944
984
  ...typeof t.measuredUsableInches === "number" && t.measuredUsableInches >= 0 ? { measuredUsableInches: t.measuredUsableInches } : {},
945
985
  ...state.loop && t.inLoop ? { inLoop: true } : {},
986
+ ...t.crossoverPartId ? { crossoverPartId: t.crossoverPartId } : {},
946
987
  ...t.path && t.path.length >= 2 ? { path: t.path } : {}
947
988
  }))
948
989
  ]),
@@ -1039,6 +1080,9 @@ function docToState(doc, fallbackLength, moduleTracks = []) {
1039
1080
  moduleTrackId,
1040
1081
  trackName: t.trackName ?? nameOf(moduleTrackId),
1041
1082
  ...t.inLoop ? { inLoop: true } : {},
1083
+ // The crossover product this connector was built from — what makes the
1084
+ // physical view draw the pair at the spacing it was really built to.
1085
+ ...typeof t.crossoverPartId === "string" && t.crossoverPartId ? { crossoverPartId: t.crossoverPartId } : {},
1042
1086
  // Authored path kept as-drawn (a physical shape, not rescaled with length).
1043
1087
  ...trackPath(t.path) ? { path: trackPath(t.path) } : {},
1044
1088
  // A MEASURED length is a real-world fact about the physical track, so it
@@ -2672,6 +2716,7 @@ exports.MAIN2_TRACK_ID = MAIN2_TRACK_ID;
2672
2716
  exports.MAIN_TRACK_ID = MAIN_TRACK_ID;
2673
2717
  exports.N_CAR_LENGTH_INCHES = N_CAR_LENGTH_INCHES;
2674
2718
  exports.N_SCALE_RATIO = N_SCALE_RATIO;
2719
+ exports.PINCH_EASE_INCHES = PINCH_EASE_INCHES;
2675
2720
  exports.RAIL_GAUGE_INCHES = RAIL_GAUGE_INCHES;
2676
2721
  exports.TIE_HALF_LENGTH_INCHES = TIE_HALF_LENGTH_INCHES;
2677
2722
  exports.TURNOUT_LEAD_INCHES_PER_FROG = TURNOUT_LEAD_INCHES_PER_FROG;
@@ -2687,6 +2732,7 @@ exports.buildTransition = buildTransition;
2687
2732
  exports.carCapacity = carCapacity;
2688
2733
  exports.checkEndplateWidth = checkEndplateWidth;
2689
2734
  exports.clearancePointPastFrogInches = clearancePointPastFrogInches;
2735
+ exports.crossoverPinches = crossoverPinches;
2690
2736
  exports.deriveEndplatePoses = deriveEndplatePoses;
2691
2737
  exports.divergeSideForHand = divergeSideForHand;
2692
2738
  exports.docToState = docToState;
@@ -2709,6 +2755,7 @@ exports.importedPartToTrackPart = importedPartToTrackPart;
2709
2755
  exports.inchesToScaleFeet = inchesToScaleFeet;
2710
2756
  exports.isLoopDoc = isLoopDoc;
2711
2757
  exports.isTransitionTurnout = isTransitionTurnout;
2758
+ exports.laneOffsetAt = laneOffsetAt;
2712
2759
  exports.leadInchesForSize = leadInchesForSize;
2713
2760
  exports.maxFlexPieceInches = maxFlexPieceInches;
2714
2761
  exports.mergeImportedParts = mergeImportedParts;