@willcgage/module-schematic 0.26.0 → 0.28.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 +72 -13
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +73 -3
- package/dist/index.d.ts +73 -3
- package/dist/index.js +68 -14
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
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,79 @@ 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
|
|
153
|
-
const
|
|
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
|
|
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, authoredTrackOffset) {
|
|
190
|
+
const v = -endplateTrackOffsetInches(authoredTrackOffset, config);
|
|
191
|
+
return v === 0 ? 0 : v;
|
|
192
|
+
}
|
|
193
|
+
function endplateTrackOffsetInches(authored, config) {
|
|
194
|
+
if (typeof authored === "number" && Number.isFinite(authored)) return authored;
|
|
195
|
+
return config === "double" ? -FREEMO_TRACK_SPACING_INCHES / 2 : 0;
|
|
196
|
+
}
|
|
197
|
+
function checkEndplateWidth(input) {
|
|
198
|
+
const width = endplateWidthInches(input);
|
|
199
|
+
const issues = [];
|
|
200
|
+
if (width < FREEMO_ENDPLATE_WIDTH_MIN_INCHES) {
|
|
201
|
+
issues.push({
|
|
202
|
+
code: "narrow",
|
|
203
|
+
message: `Endplate is ${round2(width)}\u2033 wide \u2014 the standard requires at least ${FREEMO_ENDPLATE_WIDTH_MIN_INCHES}\u2033.`,
|
|
204
|
+
requiredInches: FREEMO_ENDPLATE_WIDTH_MIN_INCHES
|
|
205
|
+
});
|
|
206
|
+
}
|
|
207
|
+
const off = input.trackOffsetInches ?? 0;
|
|
208
|
+
const centres = input.config === "double" ? [off - FREEMO_TRACK_SPACING_INCHES / 2, off + FREEMO_TRACK_SPACING_INCHES / 2] : [off];
|
|
209
|
+
const worst = Math.max(...centres.map((c) => Math.abs(c)));
|
|
210
|
+
const clearance = width / 2 - worst;
|
|
211
|
+
if (clearance < FREEMO_ENDPLATE_TRACK_FASCIA_CLEARANCE_INCHES) {
|
|
212
|
+
const required = 2 * (worst + FREEMO_ENDPLATE_TRACK_FASCIA_CLEARANCE_INCHES);
|
|
213
|
+
issues.push({
|
|
214
|
+
code: "clearance",
|
|
215
|
+
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." : "."),
|
|
216
|
+
requiredInches: required
|
|
217
|
+
});
|
|
218
|
+
}
|
|
219
|
+
return issues;
|
|
220
|
+
}
|
|
221
|
+
var round2 = (n) => Math.round(n * 100) / 100;
|
|
178
222
|
function endplateWidthFor(widths, id) {
|
|
179
223
|
const w = widths?.[id];
|
|
180
224
|
return typeof w === "number" && w > 0 ? w : FREEMO_ENDPLATE_WIDTH_RECOMMENDED_INCHES;
|
|
@@ -217,6 +261,7 @@ function emptyEditorState(lengthInches) {
|
|
|
217
261
|
branches: [],
|
|
218
262
|
poseOverrides: {},
|
|
219
263
|
endplateWidths: {},
|
|
264
|
+
endplateTrackOffsets: {},
|
|
220
265
|
outline: [],
|
|
221
266
|
sectionBreaks: [],
|
|
222
267
|
controlPoints: [],
|
|
@@ -299,10 +344,14 @@ function withPoses(endplates, overrides) {
|
|
|
299
344
|
(e) => overrides[e.id] ? { ...e, pose: overrides[e.id] } : e
|
|
300
345
|
);
|
|
301
346
|
}
|
|
302
|
-
function withWidths(endplates, widths) {
|
|
347
|
+
function withWidths(endplates, widths, offsets = {}) {
|
|
303
348
|
return endplates.map((e) => {
|
|
304
349
|
const w = widths[e.id];
|
|
305
|
-
|
|
350
|
+
const o = offsets[e.id];
|
|
351
|
+
let out = e;
|
|
352
|
+
if (typeof w === "number" && w > 0) out = { ...out, widthInches: w };
|
|
353
|
+
if (typeof o === "number" && Number.isFinite(o)) out = { ...out, trackOffsetInches: o };
|
|
354
|
+
return out;
|
|
306
355
|
});
|
|
307
356
|
}
|
|
308
357
|
function stateToDoc(state, recordNumber) {
|
|
@@ -351,7 +400,8 @@ function stateToDoc(state, recordNumber) {
|
|
|
351
400
|
],
|
|
352
401
|
state.poseOverrides
|
|
353
402
|
),
|
|
354
|
-
state.endplateWidths
|
|
403
|
+
state.endplateWidths,
|
|
404
|
+
state.endplateTrackOffsets
|
|
355
405
|
),
|
|
356
406
|
tracks: [
|
|
357
407
|
state.loop ? (
|
|
@@ -510,9 +560,12 @@ function docToState(doc, fallbackLength, moduleTracks = []) {
|
|
|
510
560
|
);
|
|
511
561
|
const poseOverrides = poseOverridesFromDoc(d);
|
|
512
562
|
const endplateWidths = {};
|
|
563
|
+
const endplateTrackOffsets = {};
|
|
513
564
|
for (const e of d.endplates ?? []) {
|
|
514
565
|
if (typeof e.widthInches === "number" && e.widthInches > 0)
|
|
515
566
|
endplateWidths[e.id] = e.widthInches;
|
|
567
|
+
if (typeof e.trackOffsetInches === "number" && Number.isFinite(e.trackOffsetInches))
|
|
568
|
+
endplateTrackOffsets[e.id] = e.trackOffsetInches;
|
|
516
569
|
}
|
|
517
570
|
const outline = (d.outline ?? []).filter((p) => p && Number.isFinite(p.x) && Number.isFinite(p.y)).map((p) => ({
|
|
518
571
|
x: p.x,
|
|
@@ -536,6 +589,7 @@ function docToState(doc, fallbackLength, moduleTracks = []) {
|
|
|
536
589
|
})),
|
|
537
590
|
poseOverrides,
|
|
538
591
|
endplateWidths,
|
|
592
|
+
endplateTrackOffsets,
|
|
539
593
|
outline,
|
|
540
594
|
sectionBreaks: (d.sectionBreaks ?? []).filter((n) => Number.isFinite(n)).map((n) => sc(n)),
|
|
541
595
|
mainPath,
|
|
@@ -1039,8 +1093,10 @@ function poseOverridesFromDoc(doc) {
|
|
|
1039
1093
|
return out;
|
|
1040
1094
|
}
|
|
1041
1095
|
|
|
1096
|
+
exports.FREEMO_ENDPLATE_TRACK_FASCIA_CLEARANCE_INCHES = FREEMO_ENDPLATE_TRACK_FASCIA_CLEARANCE_INCHES;
|
|
1042
1097
|
exports.FREEMO_ENDPLATE_WIDTH_MIN_INCHES = FREEMO_ENDPLATE_WIDTH_MIN_INCHES;
|
|
1043
1098
|
exports.FREEMO_ENDPLATE_WIDTH_RECOMMENDED_INCHES = FREEMO_ENDPLATE_WIDTH_RECOMMENDED_INCHES;
|
|
1099
|
+
exports.FREEMO_TRACK_SPACING_INCHES = FREEMO_TRACK_SPACING_INCHES;
|
|
1044
1100
|
exports.MAIN2_TRACK_ID = MAIN2_TRACK_ID;
|
|
1045
1101
|
exports.MAIN_TRACK_ID = MAIN_TRACK_ID;
|
|
1046
1102
|
exports.N_CAR_LENGTH_INCHES = N_CAR_LENGTH_INCHES;
|
|
@@ -1052,11 +1108,14 @@ exports.buildCrossover = buildCrossover;
|
|
|
1052
1108
|
exports.buildPassingSiding = buildPassingSiding;
|
|
1053
1109
|
exports.buildTransition = buildTransition;
|
|
1054
1110
|
exports.carCapacity = carCapacity;
|
|
1111
|
+
exports.checkEndplateWidth = checkEndplateWidth;
|
|
1055
1112
|
exports.deriveEndplatePoses = deriveEndplatePoses;
|
|
1056
1113
|
exports.divergeSideForHand = divergeSideForHand;
|
|
1057
1114
|
exports.docToState = docToState;
|
|
1058
1115
|
exports.emptyEditorState = emptyEditorState;
|
|
1059
1116
|
exports.endplateFaceSegments = endplateFaceSegments;
|
|
1117
|
+
exports.endplateTrackOffsetFor = endplateTrackOffsetFor;
|
|
1118
|
+
exports.endplateTrackOffsetInches = endplateTrackOffsetInches;
|
|
1060
1119
|
exports.endplateWidthInches = endplateWidthInches;
|
|
1061
1120
|
exports.geometryTurnDegrees = geometryTurnDegrees;
|
|
1062
1121
|
exports.inchesToScaleFeet = inchesToScaleFeet;
|