@willcgage/module-schematic 0.13.3 → 0.15.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
@@ -1,6 +1,18 @@
1
1
  'use strict';
2
2
 
3
3
  // src/index.ts
4
+ var FREEMO_ENDPLATE_WIDTH_MIN_INCHES = 12;
5
+ var FREEMO_ENDPLATE_WIDTH_RECOMMENDED_INCHES = 24;
6
+ function endplateWidthInches(ep) {
7
+ const w = ep?.widthInches;
8
+ return typeof w === "number" && w > 0 ? w : FREEMO_ENDPLATE_WIDTH_RECOMMENDED_INCHES;
9
+ }
10
+ function benchworkOutline(doc) {
11
+ const pts = (doc?.outline ?? []).filter(
12
+ (p) => p && Number.isFinite(p.x) && Number.isFinite(p.y)
13
+ );
14
+ return pts.length >= 3 ? pts : null;
15
+ }
4
16
  function isLoopDoc(doc) {
5
17
  return doc.loop === true || doc.endplates.length === 1;
6
18
  }
@@ -32,6 +44,8 @@ function emptyEditorState(lengthInches) {
32
44
  crossings: [],
33
45
  branches: [],
34
46
  poseOverrides: {},
47
+ endplateWidths: {},
48
+ outline: [],
35
49
  controlPoints: []
36
50
  };
37
51
  }
@@ -93,6 +107,12 @@ function withPoses(endplates, overrides) {
93
107
  (e) => overrides[e.id] ? { ...e, pose: overrides[e.id] } : e
94
108
  );
95
109
  }
110
+ function withWidths(endplates, widths) {
111
+ return endplates.map((e) => {
112
+ const w = widths[e.id];
113
+ return typeof w === "number" && w > 0 ? { ...e, widthInches: w } : e;
114
+ });
115
+ }
96
116
  function stateToDoc(state, recordNumber) {
97
117
  return {
98
118
  version: 1,
@@ -100,42 +120,45 @@ function stateToDoc(state, recordNumber) {
100
120
  lengthInches: state.lengthInches,
101
121
  ...state.loop ? { loop: true } : {},
102
122
  ...state.loop && state.loopReturn === "main2" ? { loopReturn: "main2" } : {},
103
- endplates: withPoses(
104
- [
105
- ...state.loop ? (
106
- // Balloon loop: A is the entry. A standard endplate B on the balloon
107
- // makes it an INTERCHANGE (second route connects at the loop, e.g.
108
- // Seaford); configB "none" makes it a pure turnback.
109
- [
110
- { id: "A", label: "Entry", tracks: [{ trackId: MAIN_TRACK_ID, lane: 0, config: state.configA }] },
111
- ...state.configB !== "none" ? [{ id: "B", label: "Interchange", tracks: [{ trackId: MAIN_TRACK_ID, lane: 0, config: state.configB }] }] : []
112
- ]
113
- ) : [
114
- { id: "A", label: "West", tracks: [{ trackId: MAIN_TRACK_ID, lane: 0, config: state.configA }] },
115
- // Non-loop modules always have a real B ("none" never applies).
116
- {
117
- id: "B",
118
- label: "East",
119
- tracks: [
120
- {
121
- trackId: MAIN_TRACK_ID,
122
- lane: 0,
123
- config: state.configB === "none" ? "single" : state.configB
124
- }
123
+ endplates: withWidths(
124
+ withPoses(
125
+ [
126
+ ...state.loop ? (
127
+ // Balloon loop: A is the entry. A standard endplate B on the balloon
128
+ // makes it an INTERCHANGE (second route connects at the loop, e.g.
129
+ // Seaford); configB "none" makes it a pure turnback.
130
+ [
131
+ { id: "A", label: "Entry", tracks: [{ trackId: MAIN_TRACK_ID, lane: 0, config: state.configA }] },
132
+ ...state.configB !== "none" ? [{ id: "B", label: "Interchange", tracks: [{ trackId: MAIN_TRACK_ID, lane: 0, config: state.configB }] }] : []
125
133
  ]
126
- }
134
+ ) : [
135
+ { id: "A", label: "West", tracks: [{ trackId: MAIN_TRACK_ID, lane: 0, config: state.configA }] },
136
+ // Non-loop modules always have a real B ("none" never applies).
137
+ {
138
+ id: "B",
139
+ label: "East",
140
+ tracks: [
141
+ {
142
+ trackId: MAIN_TRACK_ID,
143
+ lane: 0,
144
+ config: state.configB === "none" ? "single" : state.configB
145
+ }
146
+ ]
147
+ }
148
+ ],
149
+ // Branch endplates C, D, … — junction connections at pos, off one side
150
+ // (#170). A set can carry several (e.g. a second railroad through).
151
+ ...state.branches.map((b, i) => ({
152
+ id: String.fromCharCode(67 + i),
153
+ // C, D, E…
154
+ label: b.label || `Branch ${i + 1}`,
155
+ tracks: [{ trackId: MAIN_TRACK_ID, lane: 0, config: b.config }],
156
+ at: { pos: b.pos, side: b.side }
157
+ }))
127
158
  ],
128
- // Branch endplates C, D, … — junction connections at pos, off one side
129
- // (#170). A set can carry several (e.g. a second railroad through).
130
- ...state.branches.map((b, i) => ({
131
- id: String.fromCharCode(67 + i),
132
- // C, D, E…
133
- label: b.label || `Branch ${i + 1}`,
134
- tracks: [{ trackId: MAIN_TRACK_ID, lane: 0, config: b.config }],
135
- at: { pos: b.pos, side: b.side }
136
- }))
137
- ],
138
- state.poseOverrides
159
+ state.poseOverrides
160
+ ),
161
+ state.endplateWidths
139
162
  ),
140
163
  tracks: [
141
164
  state.loop ? (
@@ -192,7 +215,10 @@ function stateToDoc(state, recordNumber) {
192
215
  kind: "mast",
193
216
  side: s.side
194
217
  }))
195
- }))
218
+ })),
219
+ // Benchwork footprint outline (module-local inches); only when it's a real
220
+ // ring (≥ 3 vertices).
221
+ ...state.outline.length >= 3 ? { outline: state.outline } : {}
196
222
  };
197
223
  }
198
224
  function docToState(doc, fallbackLength, moduleTracks = []) {
@@ -261,6 +287,14 @@ function docToState(doc, fallbackLength, moduleTracks = []) {
261
287
  (e) => e.id !== "A" && e.id !== "B" && e.at
262
288
  );
263
289
  const poseOverrides = poseOverridesFromDoc(d);
290
+ const endplateWidths = {};
291
+ for (const e of d.endplates ?? []) {
292
+ if (typeof e.widthInches === "number" && e.widthInches > 0)
293
+ endplateWidths[e.id] = e.widthInches;
294
+ }
295
+ const outline = (d.outline ?? []).filter(
296
+ (p) => p && Number.isFinite(p.x) && Number.isFinite(p.y)
297
+ );
264
298
  return {
265
299
  lengthInches: len,
266
300
  loop,
@@ -275,6 +309,8 @@ function docToState(doc, fallbackLength, moduleTracks = []) {
275
309
  config: ep.tracks?.[0]?.config === "double" ? "double" : "single"
276
310
  })),
277
311
  poseOverrides,
312
+ endplateWidths,
313
+ outline,
278
314
  crossings: (d.crossings ?? []).map((x) => ({
279
315
  id: x.id,
280
316
  name: x.name ?? "",
@@ -732,10 +768,13 @@ function poseOverridesFromDoc(doc) {
732
768
  return out;
733
769
  }
734
770
 
771
+ exports.FREEMO_ENDPLATE_WIDTH_MIN_INCHES = FREEMO_ENDPLATE_WIDTH_MIN_INCHES;
772
+ exports.FREEMO_ENDPLATE_WIDTH_RECOMMENDED_INCHES = FREEMO_ENDPLATE_WIDTH_RECOMMENDED_INCHES;
735
773
  exports.MAIN2_TRACK_ID = MAIN2_TRACK_ID;
736
774
  exports.MAIN_TRACK_ID = MAIN_TRACK_ID;
737
775
  exports.N_SCALE_RATIO = N_SCALE_RATIO;
738
776
  exports.asModuleSchematic = asModuleSchematic;
777
+ exports.benchworkOutline = benchworkOutline;
739
778
  exports.buildCrossover = buildCrossover;
740
779
  exports.buildPassingSiding = buildPassingSiding;
741
780
  exports.buildTransition = buildTransition;
@@ -743,6 +782,7 @@ exports.deriveEndplatePoses = deriveEndplatePoses;
743
782
  exports.divergeSideForHand = divergeSideForHand;
744
783
  exports.docToState = docToState;
745
784
  exports.emptyEditorState = emptyEditorState;
785
+ exports.endplateWidthInches = endplateWidthInches;
746
786
  exports.geometryTurnDegrees = geometryTurnDegrees;
747
787
  exports.inchesToScaleFeet = inchesToScaleFeet;
748
788
  exports.isLoopDoc = isLoopDoc;