@willcgage/module-schematic 0.26.0 → 0.27.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
@@ -3,6 +3,8 @@
3
3
  // src/index.ts
4
4
  var FREEMO_ENDPLATE_WIDTH_MIN_INCHES = 12;
5
5
  var FREEMO_ENDPLATE_WIDTH_RECOMMENDED_INCHES = 24;
6
+ var FREEMO_TRACK_SPACING_INCHES = 1.125;
7
+ var FREEMO_ENDPLATE_TRACK_FASCIA_CLEARANCE_INCHES = 4;
6
8
  function endplateWidthInches(ep) {
7
9
  const w = ep?.widthInches;
8
10
  return typeof w === "number" && w > 0 ? w : FREEMO_ENDPLATE_WIDTH_RECOMMENDED_INCHES;
@@ -144,37 +146,74 @@ function centerlineFractions(center) {
144
146
  const total = cum[cum.length - 1] || 1;
145
147
  return cum.map((d) => d / total);
146
148
  }
147
- function benchworkBand(center, widthA = FREEMO_ENDPLATE_WIDTH_RECOMMENDED_INCHES, widthB = FREEMO_ENDPLATE_WIDTH_RECOMMENDED_INCHES) {
149
+ function benchworkBand(center, widthA = FREEMO_ENDPLATE_WIDTH_RECOMMENDED_INCHES, widthB = FREEMO_ENDPLATE_WIDTH_RECOMMENDED_INCHES, offsetA = 0, offsetB = 0) {
148
150
  if (center.length < 2) return [];
149
151
  const n = centerlineNormals(center);
150
152
  const f = centerlineFractions(center);
151
153
  const half = (i) => (widthA * (1 - f[i]) + widthB * f[i]) / 2;
152
- const left = center.map((p, i) => ({ x: p.x + n[i].x * half(i), y: p.y + n[i].y * half(i) }));
153
- const right = center.map((p, i) => ({ x: p.x - n[i].x * half(i), y: p.y - n[i].y * half(i) }));
154
+ const off = (i) => offsetA * (1 - f[i]) + offsetB * f[i];
155
+ const left = center.map((p, i) => ({
156
+ x: p.x + n[i].x * (off(i) + half(i)),
157
+ y: p.y + n[i].y * (off(i) + half(i))
158
+ }));
159
+ const right = center.map((p, i) => ({
160
+ x: p.x + n[i].x * (off(i) - half(i)),
161
+ y: p.y + n[i].y * (off(i) - half(i))
162
+ }));
154
163
  return [...left, ...right.reverse()];
155
164
  }
156
- function endplateFaceSegments(center, widthA = FREEMO_ENDPLATE_WIDTH_RECOMMENDED_INCHES, widthB = FREEMO_ENDPLATE_WIDTH_RECOMMENDED_INCHES) {
165
+ function endplateFaceSegments(center, widthA = FREEMO_ENDPLATE_WIDTH_RECOMMENDED_INCHES, widthB = FREEMO_ENDPLATE_WIDTH_RECOMMENDED_INCHES, offsetA = 0, offsetB = 0) {
157
166
  if (center.length < 2) return [];
158
167
  const n = centerlineNormals(center);
159
- const face = (i, w) => ({
160
- p1: { x: center[i].x + n[i].x * (w / 2), y: center[i].y + n[i].y * (w / 2) },
161
- p2: { x: center[i].x - n[i].x * (w / 2), y: center[i].y - n[i].y * (w / 2) },
168
+ const face = (i, w, o) => ({
169
+ p1: { x: center[i].x + n[i].x * (o + w / 2), y: center[i].y + n[i].y * (o + w / 2) },
170
+ p2: { x: center[i].x + n[i].x * (o - w / 2), y: center[i].y + n[i].y * (o - w / 2) },
162
171
  mid: { x: center[i].x, y: center[i].y }
163
172
  });
164
- return [face(0, widthA), face(center.length - 1, widthB)];
173
+ return [face(0, widthA, offsetA), face(center.length - 1, widthB, offsetB)];
165
174
  }
166
175
  function moduleFootprint(input) {
167
176
  const centerline = moduleCenterline(input);
168
177
  const widthA = endplateWidthFor(input.endplateWidths, "A");
169
178
  const widthB = endplateWidthFor(input.endplateWidths, "B");
170
179
  const authored = benchworkOutline(input);
180
+ const offA = input.endplateTrackOffsets?.["A"] ?? 0;
181
+ const offB = input.endplateTrackOffsets?.["B"] ?? 0;
171
182
  return {
172
183
  centerline,
173
- band: benchworkBand(centerline, widthA, widthB),
174
- endplateFaces: endplateFaceSegments(centerline, widthA, widthB),
184
+ band: benchworkBand(centerline, widthA, widthB, offA, offB),
185
+ endplateFaces: endplateFaceSegments(centerline, widthA, widthB, offA, offB),
175
186
  outline: authored ? sampleBenchworkOutline(authored) : null
176
187
  };
177
188
  }
189
+ function endplateTrackOffsetFor(config) {
190
+ return config === "double" ? FREEMO_TRACK_SPACING_INCHES / 2 : 0;
191
+ }
192
+ function checkEndplateWidth(input) {
193
+ const width = endplateWidthInches(input);
194
+ const issues = [];
195
+ if (width < FREEMO_ENDPLATE_WIDTH_MIN_INCHES) {
196
+ issues.push({
197
+ code: "narrow",
198
+ message: `Endplate is ${round2(width)}\u2033 wide \u2014 the standard requires at least ${FREEMO_ENDPLATE_WIDTH_MIN_INCHES}\u2033.`,
199
+ requiredInches: FREEMO_ENDPLATE_WIDTH_MIN_INCHES
200
+ });
201
+ }
202
+ const off = input.trackOffsetInches ?? 0;
203
+ const centres = input.config === "double" ? [off - FREEMO_TRACK_SPACING_INCHES / 2, off + FREEMO_TRACK_SPACING_INCHES / 2] : [off];
204
+ const worst = Math.max(...centres.map((c) => Math.abs(c)));
205
+ const clearance = width / 2 - worst;
206
+ if (clearance < FREEMO_ENDPLATE_TRACK_FASCIA_CLEARANCE_INCHES) {
207
+ const required = 2 * (worst + FREEMO_ENDPLATE_TRACK_FASCIA_CLEARANCE_INCHES);
208
+ issues.push({
209
+ code: "clearance",
210
+ message: `Track sits ${round2(clearance)}\u2033 from the fascia \u2014 the standard requires at least ${FREEMO_ENDPLATE_TRACK_FASCIA_CLEARANCE_INCHES}\u2033. Widen this end to ${round2(required)}\u2033` + (off !== 0 ? " or move the track back toward the centre." : "."),
211
+ requiredInches: required
212
+ });
213
+ }
214
+ return issues;
215
+ }
216
+ var round2 = (n) => Math.round(n * 100) / 100;
178
217
  function endplateWidthFor(widths, id) {
179
218
  const w = widths?.[id];
180
219
  return typeof w === "number" && w > 0 ? w : FREEMO_ENDPLATE_WIDTH_RECOMMENDED_INCHES;
@@ -1039,8 +1078,10 @@ function poseOverridesFromDoc(doc) {
1039
1078
  return out;
1040
1079
  }
1041
1080
 
1081
+ exports.FREEMO_ENDPLATE_TRACK_FASCIA_CLEARANCE_INCHES = FREEMO_ENDPLATE_TRACK_FASCIA_CLEARANCE_INCHES;
1042
1082
  exports.FREEMO_ENDPLATE_WIDTH_MIN_INCHES = FREEMO_ENDPLATE_WIDTH_MIN_INCHES;
1043
1083
  exports.FREEMO_ENDPLATE_WIDTH_RECOMMENDED_INCHES = FREEMO_ENDPLATE_WIDTH_RECOMMENDED_INCHES;
1084
+ exports.FREEMO_TRACK_SPACING_INCHES = FREEMO_TRACK_SPACING_INCHES;
1044
1085
  exports.MAIN2_TRACK_ID = MAIN2_TRACK_ID;
1045
1086
  exports.MAIN_TRACK_ID = MAIN_TRACK_ID;
1046
1087
  exports.N_CAR_LENGTH_INCHES = N_CAR_LENGTH_INCHES;
@@ -1052,11 +1093,13 @@ exports.buildCrossover = buildCrossover;
1052
1093
  exports.buildPassingSiding = buildPassingSiding;
1053
1094
  exports.buildTransition = buildTransition;
1054
1095
  exports.carCapacity = carCapacity;
1096
+ exports.checkEndplateWidth = checkEndplateWidth;
1055
1097
  exports.deriveEndplatePoses = deriveEndplatePoses;
1056
1098
  exports.divergeSideForHand = divergeSideForHand;
1057
1099
  exports.docToState = docToState;
1058
1100
  exports.emptyEditorState = emptyEditorState;
1059
1101
  exports.endplateFaceSegments = endplateFaceSegments;
1102
+ exports.endplateTrackOffsetFor = endplateTrackOffsetFor;
1060
1103
  exports.endplateWidthInches = endplateWidthInches;
1061
1104
  exports.geometryTurnDegrees = geometryTurnDegrees;
1062
1105
  exports.inchesToScaleFeet = inchesToScaleFeet;