@willcgage/module-schematic 0.17.0 → 0.19.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 -5
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +109 -2
- package/dist/index.d.ts +109 -2
- package/dist/index.js +105 -6
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -45,6 +45,45 @@ function sampleBenchworkOutline(pts, segsPerArc = 20) {
|
|
|
45
45
|
}
|
|
46
46
|
return out;
|
|
47
47
|
}
|
|
48
|
+
function samplePath(pts, segsPerArc = 20) {
|
|
49
|
+
const n = pts.length;
|
|
50
|
+
if (n < 2) return pts.map((p) => ({ x: p.x, y: p.y }));
|
|
51
|
+
const out = [];
|
|
52
|
+
for (let i = 0; i < n - 1; i++) {
|
|
53
|
+
const p0 = pts[i];
|
|
54
|
+
const p1 = pts[i + 1];
|
|
55
|
+
out.push({ x: p0.x, y: p0.y });
|
|
56
|
+
const bulge = p0.bulge ?? 0;
|
|
57
|
+
if (!bulge) continue;
|
|
58
|
+
const dx = p1.x - p0.x;
|
|
59
|
+
const dy = p1.y - p0.y;
|
|
60
|
+
const c = Math.hypot(dx, dy);
|
|
61
|
+
if (c < 1e-6) continue;
|
|
62
|
+
const nx = -dy / c;
|
|
63
|
+
const ny = dx / c;
|
|
64
|
+
const mid = { x: (p0.x + p1.x) / 2 + nx * bulge, y: (p0.y + p1.y) / 2 + ny * bulge };
|
|
65
|
+
const circ = circleThrough(p0, mid, p1);
|
|
66
|
+
if (!circ) continue;
|
|
67
|
+
const a0 = Math.atan2(p0.y - circ.cy, p0.x - circ.cx);
|
|
68
|
+
const am = Math.atan2(mid.y - circ.cy, mid.x - circ.cx);
|
|
69
|
+
const a1 = Math.atan2(p1.y - circ.cy, p1.x - circ.cx);
|
|
70
|
+
const sweep = arcSweep(a0, a1, am);
|
|
71
|
+
for (let s = 1; s < segsPerArc; s++) {
|
|
72
|
+
const a = a0 + sweep * s / segsPerArc;
|
|
73
|
+
out.push({ x: circ.cx + circ.r * Math.cos(a), y: circ.cy + circ.r * Math.sin(a) });
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
out.push({ x: pts[n - 1].x, y: pts[n - 1].y });
|
|
77
|
+
return out;
|
|
78
|
+
}
|
|
79
|
+
function trackPath(path) {
|
|
80
|
+
const pts = (path ?? []).filter((p) => p && Number.isFinite(p.x) && Number.isFinite(p.y)).map((p) => ({
|
|
81
|
+
x: p.x,
|
|
82
|
+
y: p.y,
|
|
83
|
+
...Number.isFinite(p.bulge) && p.bulge ? { bulge: p.bulge } : {}
|
|
84
|
+
}));
|
|
85
|
+
return pts.length >= 2 ? pts : null;
|
|
86
|
+
}
|
|
48
87
|
function circleThrough(a, b, c) {
|
|
49
88
|
const d = 2 * (a.x * (b.y - c.y) + b.x * (c.y - a.y) + c.x * (a.y - b.y));
|
|
50
89
|
if (Math.abs(d) < 1e-9) return null;
|
|
@@ -67,6 +106,8 @@ function arcSweep(a0, a1, am) {
|
|
|
67
106
|
}
|
|
68
107
|
var DEG_FP = Math.PI / 180;
|
|
69
108
|
function moduleCenterline(input) {
|
|
109
|
+
const drawn = trackPath(input.mainPath);
|
|
110
|
+
if (drawn) return samplePath(drawn);
|
|
70
111
|
const L = input.lengthInches > 0 ? input.lengthInches : 24;
|
|
71
112
|
const gt = input.geometryType;
|
|
72
113
|
if (gt === "dead_end") return [{ x: 0, y: 0 }];
|
|
@@ -149,6 +190,11 @@ function inchesToScaleFeet(inches, ratio = N_SCALE_RATIO) {
|
|
|
149
190
|
function scaleFeetToInches(feet, ratio = N_SCALE_RATIO) {
|
|
150
191
|
return feet * 12 / ratio;
|
|
151
192
|
}
|
|
193
|
+
var N_CAR_LENGTH_INCHES = 3.3;
|
|
194
|
+
function carCapacity(fromPos, toPos, carLengthInches = N_CAR_LENGTH_INCHES) {
|
|
195
|
+
if (!(carLengthInches > 0)) return 0;
|
|
196
|
+
return Math.max(0, Math.floor(Math.abs(toPos - fromPos) / carLengthInches));
|
|
197
|
+
}
|
|
152
198
|
function asModuleSchematic(x) {
|
|
153
199
|
if (!x || typeof x !== "object") return null;
|
|
154
200
|
const d = x;
|
|
@@ -170,7 +216,9 @@ function emptyEditorState(lengthInches) {
|
|
|
170
216
|
poseOverrides: {},
|
|
171
217
|
endplateWidths: {},
|
|
172
218
|
outline: [],
|
|
173
|
-
controlPoints: []
|
|
219
|
+
controlPoints: [],
|
|
220
|
+
industries: [],
|
|
221
|
+
mainPath: []
|
|
174
222
|
};
|
|
175
223
|
}
|
|
176
224
|
function isTransitionTurnout(t) {
|
|
@@ -307,7 +355,8 @@ function stateToDoc(state, recordNumber) {
|
|
|
307
355
|
moduleTrackId: t.moduleTrackId,
|
|
308
356
|
trackName: t.trackName || void 0,
|
|
309
357
|
capacityFeet: Math.round(inchesToScaleFeet(Math.abs(t.toPos - t.fromPos))),
|
|
310
|
-
...state.loop && t.inLoop ? { inLoop: true } : {}
|
|
358
|
+
...state.loop && t.inLoop ? { inLoop: true } : {},
|
|
359
|
+
...t.path && t.path.length >= 2 ? { path: t.path } : {}
|
|
311
360
|
}))
|
|
312
361
|
],
|
|
313
362
|
turnouts: state.turnouts.map((t) => ({
|
|
@@ -340,9 +389,26 @@ function stateToDoc(state, recordNumber) {
|
|
|
340
389
|
side: s.side
|
|
341
390
|
}))
|
|
342
391
|
})),
|
|
392
|
+
// Industries — car-spot spans on a track; only when any are authored.
|
|
393
|
+
...state.industries.length > 0 ? {
|
|
394
|
+
industries: state.industries.map((ind) => ({
|
|
395
|
+
id: ind.id,
|
|
396
|
+
name: ind.name,
|
|
397
|
+
...ind.type ? { type: ind.type } : {},
|
|
398
|
+
track: ind.track,
|
|
399
|
+
fromPos: ind.fromPos,
|
|
400
|
+
toPos: ind.toPos,
|
|
401
|
+
side: ind.side,
|
|
402
|
+
...ind.labelMode && ind.labelMode !== "none" ? { labelMode: ind.labelMode } : {},
|
|
403
|
+
...ind.carTypes.length ? { carTypes: ind.carTypes } : {},
|
|
404
|
+
moduleIndustryId: ind.moduleIndustryId
|
|
405
|
+
}))
|
|
406
|
+
} : {},
|
|
343
407
|
// Benchwork footprint outline (module-local inches); only when it's a real
|
|
344
408
|
// ring (≥ 3 vertices).
|
|
345
|
-
...state.outline.length >= 3 ? { outline: state.outline } : {}
|
|
409
|
+
...state.outline.length >= 3 ? { outline: state.outline } : {},
|
|
410
|
+
// Authored mainline path (module-local inches); only when it's a real path.
|
|
411
|
+
...state.mainPath.length >= 2 ? { mainPath: state.mainPath } : {}
|
|
346
412
|
};
|
|
347
413
|
}
|
|
348
414
|
function docToState(doc, fallbackLength, moduleTracks = []) {
|
|
@@ -372,7 +438,9 @@ function docToState(doc, fallbackLength, moduleTracks = []) {
|
|
|
372
438
|
toPos: t.toPos != null ? sc(t.toPos) : len,
|
|
373
439
|
moduleTrackId,
|
|
374
440
|
trackName: t.trackName ?? nameOf(moduleTrackId),
|
|
375
|
-
...t.inLoop ? { inLoop: true } : {}
|
|
441
|
+
...t.inLoop ? { inLoop: true } : {},
|
|
442
|
+
// Authored path kept as-drawn (a physical shape, not rescaled with length).
|
|
443
|
+
...trackPath(t.path) ? { path: trackPath(t.path) } : {}
|
|
376
444
|
});
|
|
377
445
|
}
|
|
378
446
|
}
|
|
@@ -421,6 +489,7 @@ function docToState(doc, fallbackLength, moduleTracks = []) {
|
|
|
421
489
|
y: p.y,
|
|
422
490
|
...Number.isFinite(p.bulge) && p.bulge ? { bulge: p.bulge } : {}
|
|
423
491
|
}));
|
|
492
|
+
const mainPath = trackPath(d.mainPath) ?? [];
|
|
424
493
|
return {
|
|
425
494
|
lengthInches: len,
|
|
426
495
|
loop,
|
|
@@ -437,6 +506,7 @@ function docToState(doc, fallbackLength, moduleTracks = []) {
|
|
|
437
506
|
poseOverrides,
|
|
438
507
|
endplateWidths,
|
|
439
508
|
outline,
|
|
509
|
+
mainPath,
|
|
440
510
|
crossings: (d.crossings ?? []).map((x) => ({
|
|
441
511
|
id: x.id,
|
|
442
512
|
name: x.name ?? "",
|
|
@@ -453,7 +523,19 @@ function docToState(doc, fallbackLength, moduleTracks = []) {
|
|
|
453
523
|
divergeTrack: t.divergeTrack,
|
|
454
524
|
kind: t.kind ?? "right"
|
|
455
525
|
})),
|
|
456
|
-
controlPoints: readControlPoints(d, sc)
|
|
526
|
+
controlPoints: readControlPoints(d, sc),
|
|
527
|
+
industries: (d.industries ?? []).map((ind) => ({
|
|
528
|
+
id: ind.id,
|
|
529
|
+
name: ind.name ?? "",
|
|
530
|
+
type: ind.type ?? "",
|
|
531
|
+
track: ind.track,
|
|
532
|
+
fromPos: sc(ind.fromPos ?? 0),
|
|
533
|
+
toPos: ind.toPos != null ? sc(ind.toPos) : len,
|
|
534
|
+
side: ind.side ?? "above",
|
|
535
|
+
labelMode: ind.labelMode ?? "none",
|
|
536
|
+
carTypes: Array.isArray(ind.carTypes) ? ind.carTypes : [],
|
|
537
|
+
moduleIndustryId: ind.moduleIndustryId ?? null
|
|
538
|
+
}))
|
|
457
539
|
};
|
|
458
540
|
}
|
|
459
541
|
function readControlPoints(d, sc = (p) => p) {
|
|
@@ -752,6 +834,22 @@ function moduleFeatures(doc) {
|
|
|
752
834
|
posFrac: clampFrac(e.at.pos),
|
|
753
835
|
side: e.at.side === "down" ? "down" : "up"
|
|
754
836
|
}));
|
|
837
|
+
const industries = (doc.industries ?? []).map((ind) => {
|
|
838
|
+
const from = ind.fromPos ?? 0;
|
|
839
|
+
const to = ind.toPos ?? len;
|
|
840
|
+
return {
|
|
841
|
+
id: ind.id,
|
|
842
|
+
name: ind.name ?? "",
|
|
843
|
+
type: ind.type ?? null,
|
|
844
|
+
fromFrac: clampFrac(Math.min(from, to)),
|
|
845
|
+
toFrac: clampFrac(Math.max(from, to)),
|
|
846
|
+
lane: trackLane.get(ind.track) ?? 0,
|
|
847
|
+
side: ind.side ?? "above",
|
|
848
|
+
labelMode: ind.labelMode ?? "none",
|
|
849
|
+
cars: carCapacity(from, to),
|
|
850
|
+
carTypes: Array.isArray(ind.carTypes) ? ind.carTypes : []
|
|
851
|
+
};
|
|
852
|
+
});
|
|
755
853
|
const allLanes = [
|
|
756
854
|
0,
|
|
757
855
|
doubleMain ? 1 : 0,
|
|
@@ -792,6 +890,7 @@ function moduleFeatures(doc) {
|
|
|
792
890
|
crossings,
|
|
793
891
|
crossovers,
|
|
794
892
|
branchConnectors,
|
|
893
|
+
industries,
|
|
795
894
|
laneMin: Math.min(...allLanes),
|
|
796
895
|
laneMax: Math.max(...allLanes)
|
|
797
896
|
};
|
|
@@ -898,6 +997,7 @@ exports.FREEMO_ENDPLATE_WIDTH_MIN_INCHES = FREEMO_ENDPLATE_WIDTH_MIN_INCHES;
|
|
|
898
997
|
exports.FREEMO_ENDPLATE_WIDTH_RECOMMENDED_INCHES = FREEMO_ENDPLATE_WIDTH_RECOMMENDED_INCHES;
|
|
899
998
|
exports.MAIN2_TRACK_ID = MAIN2_TRACK_ID;
|
|
900
999
|
exports.MAIN_TRACK_ID = MAIN_TRACK_ID;
|
|
1000
|
+
exports.N_CAR_LENGTH_INCHES = N_CAR_LENGTH_INCHES;
|
|
901
1001
|
exports.N_SCALE_RATIO = N_SCALE_RATIO;
|
|
902
1002
|
exports.asModuleSchematic = asModuleSchematic;
|
|
903
1003
|
exports.benchworkBand = benchworkBand;
|
|
@@ -905,6 +1005,7 @@ exports.benchworkOutline = benchworkOutline;
|
|
|
905
1005
|
exports.buildCrossover = buildCrossover;
|
|
906
1006
|
exports.buildPassingSiding = buildPassingSiding;
|
|
907
1007
|
exports.buildTransition = buildTransition;
|
|
1008
|
+
exports.carCapacity = carCapacity;
|
|
908
1009
|
exports.deriveEndplatePoses = deriveEndplatePoses;
|
|
909
1010
|
exports.divergeSideForHand = divergeSideForHand;
|
|
910
1011
|
exports.docToState = docToState;
|
|
@@ -922,7 +1023,9 @@ exports.nextId = nextId;
|
|
|
922
1023
|
exports.poseNeedsManual = poseNeedsManual;
|
|
923
1024
|
exports.poseOverridesFromDoc = poseOverridesFromDoc;
|
|
924
1025
|
exports.sampleBenchworkOutline = sampleBenchworkOutline;
|
|
1026
|
+
exports.samplePath = samplePath;
|
|
925
1027
|
exports.scaleFeetToInches = scaleFeetToInches;
|
|
926
1028
|
exports.stateToDoc = stateToDoc;
|
|
1029
|
+
exports.trackPath = trackPath;
|
|
927
1030
|
//# sourceMappingURL=index.cjs.map
|
|
928
1031
|
//# sourceMappingURL=index.cjs.map
|