@willcgage/module-schematic 0.28.1 → 0.30.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 +108 -1
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +108 -3
- package/dist/index.d.ts +108 -3
- package/dist/index.js +103 -2
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -17,6 +17,31 @@ function benchworkOutline(doc) {
|
|
|
17
17
|
}));
|
|
18
18
|
return pts.length >= 3 ? pts : null;
|
|
19
19
|
}
|
|
20
|
+
function moduleSections(doc) {
|
|
21
|
+
return (doc?.sections ?? []).filter((sec) => sec && typeof sec.id === "string" && sec.id !== "").map((sec) => {
|
|
22
|
+
const outline = benchworkOutline({ outline: sec.outline });
|
|
23
|
+
const name = typeof sec.name === "string" ? sec.name.trim() : "";
|
|
24
|
+
const len = sec.lengthInches;
|
|
25
|
+
const deg = sec.geometryDegrees;
|
|
26
|
+
const off = sec.geometryOffsetInches;
|
|
27
|
+
return {
|
|
28
|
+
id: sec.id,
|
|
29
|
+
...name ? { name } : {},
|
|
30
|
+
...outline ? { outline } : {},
|
|
31
|
+
...typeof len === "number" && Number.isFinite(len) && len > 0 ? { lengthInches: len } : {},
|
|
32
|
+
...sec.geometryType ? { geometryType: sec.geometryType } : {},
|
|
33
|
+
...typeof deg === "number" && Number.isFinite(deg) ? { geometryDegrees: deg } : {},
|
|
34
|
+
...typeof off === "number" && Number.isFinite(off) ? { geometryOffsetInches: off } : {}
|
|
35
|
+
};
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
function sectionFootprints(doc) {
|
|
39
|
+
return moduleSections(doc).filter((sec) => sec.outline).map((sec) => ({
|
|
40
|
+
id: sec.id,
|
|
41
|
+
...sec.name ? { name: sec.name } : {},
|
|
42
|
+
outline: sampleBenchworkOutline(sec.outline)
|
|
43
|
+
}));
|
|
44
|
+
}
|
|
20
45
|
function sampleBenchworkOutline(pts, segsPerArc = 20) {
|
|
21
46
|
const n = pts.length;
|
|
22
47
|
if (n < 2) return pts.map((p) => ({ x: p.x, y: p.y }));
|
|
@@ -110,6 +135,8 @@ var DEG_FP = Math.PI / 180;
|
|
|
110
135
|
function moduleCenterline(input) {
|
|
111
136
|
const drawn = trackPath(input.mainPath);
|
|
112
137
|
if (drawn) return samplePath(drawn);
|
|
138
|
+
const chained = sectionedCenterline(input);
|
|
139
|
+
if (chained.length >= 2) return chained;
|
|
113
140
|
if (!input.geometryType) return [];
|
|
114
141
|
const L = input.lengthInches > 0 ? input.lengthInches : 24;
|
|
115
142
|
const gt = input.geometryType;
|
|
@@ -127,6 +154,73 @@ function moduleCenterline(input) {
|
|
|
127
154
|
}
|
|
128
155
|
return pts;
|
|
129
156
|
}
|
|
157
|
+
function sectionCenterlineLocal(sec) {
|
|
158
|
+
const L = typeof sec.lengthInches === "number" && sec.lengthInches > 0 ? sec.lengthInches : 0;
|
|
159
|
+
const gt = sec.geometryType || "straight";
|
|
160
|
+
if (L <= 0) return { points: [{ x: 0, y: 0 }], endX: 0, endY: 0, endHeadingDeg: 0 };
|
|
161
|
+
if (gt === "offset") {
|
|
162
|
+
const dy = sec.geometryOffsetInches ?? 0;
|
|
163
|
+
return { points: [{ x: 0, y: 0 }, { x: L, y: dy }], endX: L, endY: dy, endHeadingDeg: 0 };
|
|
164
|
+
}
|
|
165
|
+
const turn = gt === "corner_45" ? 45 : gt === "corner_90" ? 90 : gt === "curve" ? sec.geometryDegrees ?? 0 : 0;
|
|
166
|
+
if (turn === 0) return { points: [{ x: 0, y: 0 }, { x: L, y: 0 }], endX: L, endY: 0, endHeadingDeg: 0 };
|
|
167
|
+
const t = turn * DEG_FP;
|
|
168
|
+
const r = L / t;
|
|
169
|
+
const steps = 12;
|
|
170
|
+
const points = [];
|
|
171
|
+
for (let i = 0; i <= steps; i++) {
|
|
172
|
+
const a = t * i / steps;
|
|
173
|
+
points.push({ x: r * Math.sin(a), y: r * (1 - Math.cos(a)) });
|
|
174
|
+
}
|
|
175
|
+
const last = points[points.length - 1];
|
|
176
|
+
return { points, endX: last.x, endY: last.y, endHeadingDeg: turn };
|
|
177
|
+
}
|
|
178
|
+
function moduleLengthFromSections(doc) {
|
|
179
|
+
const secs = moduleSections(doc).filter(
|
|
180
|
+
(sec) => typeof sec.lengthInches === "number" && sec.lengthInches > 0
|
|
181
|
+
);
|
|
182
|
+
if (!secs.length) return null;
|
|
183
|
+
return secs.reduce((a, sec) => a + sec.lengthInches, 0);
|
|
184
|
+
}
|
|
185
|
+
function sectionSpans(doc) {
|
|
186
|
+
let acc = 0;
|
|
187
|
+
const out = [];
|
|
188
|
+
for (const sec of moduleSections(doc)) {
|
|
189
|
+
const L = typeof sec.lengthInches === "number" && sec.lengthInches > 0 ? sec.lengthInches : 0;
|
|
190
|
+
if (L <= 0) continue;
|
|
191
|
+
out.push({ id: sec.id, ...sec.name ? { name: sec.name } : {}, fromPos: acc, toPos: acc + L });
|
|
192
|
+
acc += L;
|
|
193
|
+
}
|
|
194
|
+
return out;
|
|
195
|
+
}
|
|
196
|
+
function sectionBreaksFromSections(doc) {
|
|
197
|
+
const spans = sectionSpans(doc);
|
|
198
|
+
return spans.slice(0, -1).map((sp) => sp.toPos);
|
|
199
|
+
}
|
|
200
|
+
function sectionedCenterline(doc) {
|
|
201
|
+
const secs = moduleSections(doc).filter(
|
|
202
|
+
(sec) => typeof sec.lengthInches === "number" && sec.lengthInches > 0
|
|
203
|
+
);
|
|
204
|
+
if (!secs.length) return [];
|
|
205
|
+
const out = [];
|
|
206
|
+
let ox = 0;
|
|
207
|
+
let oy = 0;
|
|
208
|
+
let heading = 0;
|
|
209
|
+
for (const sec of secs) {
|
|
210
|
+
const local = sectionCenterlineLocal(sec);
|
|
211
|
+
const c = Math.cos(heading * DEG_FP);
|
|
212
|
+
const sn = Math.sin(heading * DEG_FP);
|
|
213
|
+
for (let i = 0; i < local.points.length; i++) {
|
|
214
|
+
if (i === 0 && out.length) continue;
|
|
215
|
+
const p = local.points[i];
|
|
216
|
+
out.push({ x: ox + p.x * c - p.y * sn, y: oy + p.x * sn + p.y * c });
|
|
217
|
+
}
|
|
218
|
+
ox += local.endX * c - local.endY * sn;
|
|
219
|
+
oy += local.endX * sn + local.endY * c;
|
|
220
|
+
heading += local.endHeadingDeg;
|
|
221
|
+
}
|
|
222
|
+
return out;
|
|
223
|
+
}
|
|
130
224
|
function centerlineNormals(center) {
|
|
131
225
|
return center.map((_, i) => {
|
|
132
226
|
const a = center[Math.max(0, i - 1)];
|
|
@@ -179,11 +273,13 @@ function moduleFootprint(input) {
|
|
|
179
273
|
const authored = benchworkOutline(input);
|
|
180
274
|
const offA = input.endplateTrackOffsets?.["A"] ?? 0;
|
|
181
275
|
const offB = input.endplateTrackOffsets?.["B"] ?? 0;
|
|
276
|
+
const sectionOutlines = sectionFootprints(input);
|
|
182
277
|
return {
|
|
183
278
|
centerline,
|
|
184
279
|
band: benchworkBand(centerline, widthA, widthB, offA, offB),
|
|
185
280
|
endplateFaces: endplateFaceSegments(centerline, widthA, widthB, offA, offB),
|
|
186
|
-
outline: authored ? sampleBenchworkOutline(authored)
|
|
281
|
+
outline: sectionOutlines.length || !authored ? null : sampleBenchworkOutline(authored),
|
|
282
|
+
sectionOutlines
|
|
187
283
|
};
|
|
188
284
|
}
|
|
189
285
|
function endplateTrackOffsetFor(config, authoredTrackOffset) {
|
|
@@ -264,6 +360,7 @@ function emptyEditorState(lengthInches) {
|
|
|
264
360
|
endplateTrackOffsets: {},
|
|
265
361
|
outline: [],
|
|
266
362
|
sectionBreaks: [],
|
|
363
|
+
sections: [],
|
|
267
364
|
controlPoints: [],
|
|
268
365
|
industries: [],
|
|
269
366
|
mainPath: []
|
|
@@ -487,6 +584,9 @@ function stateToDoc(state, recordNumber) {
|
|
|
487
584
|
...state.outline.length >= 3 ? { outline: state.outline } : {},
|
|
488
585
|
// Internal section joints (inches from A), when the module has more than one.
|
|
489
586
|
...state.sectionBreaks.length ? { sectionBreaks: state.sectionBreaks } : {},
|
|
587
|
+
// Sections as objects — emitted only once the owner has some, so a module
|
|
588
|
+
// that never used them keeps exactly the doc it had before (#96 phase 2).
|
|
589
|
+
...state.sections.length ? { sections: moduleSections({ sections: state.sections }) } : {},
|
|
490
590
|
// Authored mainline path (module-local inches); only when it's a real path.
|
|
491
591
|
...state.mainPath.length >= 2 ? { mainPath: state.mainPath } : {}
|
|
492
592
|
};
|
|
@@ -592,6 +692,7 @@ function docToState(doc, fallbackLength, moduleTracks = []) {
|
|
|
592
692
|
endplateTrackOffsets,
|
|
593
693
|
outline,
|
|
594
694
|
sectionBreaks: (d.sectionBreaks ?? []).filter((n) => Number.isFinite(n)).map((n) => sc(n)),
|
|
695
|
+
sections: moduleSections(d),
|
|
595
696
|
mainPath,
|
|
596
697
|
crossings: (d.crossings ?? []).map((x) => ({
|
|
597
698
|
id: x.id,
|
|
@@ -1124,12 +1225,18 @@ exports.isTransitionTurnout = isTransitionTurnout;
|
|
|
1124
1225
|
exports.moduleCenterline = moduleCenterline;
|
|
1125
1226
|
exports.moduleFeatures = moduleFeatures;
|
|
1126
1227
|
exports.moduleFootprint = moduleFootprint;
|
|
1228
|
+
exports.moduleLengthFromSections = moduleLengthFromSections;
|
|
1229
|
+
exports.moduleSections = moduleSections;
|
|
1127
1230
|
exports.nextId = nextId;
|
|
1128
1231
|
exports.poseNeedsManual = poseNeedsManual;
|
|
1129
1232
|
exports.poseOverridesFromDoc = poseOverridesFromDoc;
|
|
1130
1233
|
exports.sampleBenchworkOutline = sampleBenchworkOutline;
|
|
1131
1234
|
exports.samplePath = samplePath;
|
|
1132
1235
|
exports.scaleFeetToInches = scaleFeetToInches;
|
|
1236
|
+
exports.sectionBreaksFromSections = sectionBreaksFromSections;
|
|
1237
|
+
exports.sectionFootprints = sectionFootprints;
|
|
1238
|
+
exports.sectionSpans = sectionSpans;
|
|
1239
|
+
exports.sectionedCenterline = sectionedCenterline;
|
|
1133
1240
|
exports.stateToDoc = stateToDoc;
|
|
1134
1241
|
exports.trackPath = trackPath;
|
|
1135
1242
|
//# sourceMappingURL=index.cjs.map
|