@willcgage/module-schematic 0.12.0 → 0.13.1
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 +139 -8
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +43 -1
- package/dist/index.d.ts +43 -1
- package/dist/index.js +138 -9
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.d.cts
CHANGED
|
@@ -280,6 +280,16 @@ declare function buildPassingSiding(state: EditorState): {
|
|
|
280
280
|
turnouts: EditorTurnout[];
|
|
281
281
|
controlPoints: EditorControlPoint[];
|
|
282
282
|
};
|
|
283
|
+
/**
|
|
284
|
+
* Build a crossover as one unit: a short connector track between Main 1 and
|
|
285
|
+
* Main 2, with a turnout on each main. The two turnouts sit on different lanes,
|
|
286
|
+
* so the feature resolver draws it as a diagonal (not a lane-paralleling siding)
|
|
287
|
+
* — this is what connects the two dots (#bug2). Needs a double-track module.
|
|
288
|
+
*/
|
|
289
|
+
declare function buildCrossover(state: EditorState): {
|
|
290
|
+
track: EditorTrack;
|
|
291
|
+
turnouts: EditorTurnout[];
|
|
292
|
+
} | null;
|
|
283
293
|
interface DrawTrack {
|
|
284
294
|
id: string;
|
|
285
295
|
role: TrackRole;
|
|
@@ -291,6 +301,13 @@ interface DrawTrack {
|
|
|
291
301
|
* of the diverge diagonal. A spur off Main 2 starts at lane 1, not lane 0;
|
|
292
302
|
* without this, renderers draw what looks like a crossover. */
|
|
293
303
|
divergesFromLane: number;
|
|
304
|
+
/** The end that meets the main at its turnout (throat) and the far stub end,
|
|
305
|
+
* as fractions of length. Direction-preserving: unlike fromFrac/toFrac (always
|
|
306
|
+
* sorted West→East), these keep WHICH end joins, so an east-facing spur draws
|
|
307
|
+
* its throat on the east. A siding meets the main at both ends — there
|
|
308
|
+
* throat=fromFrac, stub=toFrac (the fields are only meaningful for spurs). */
|
|
309
|
+
throatFrac: number;
|
|
310
|
+
stubFrac: number;
|
|
294
311
|
/** Inside the balloon of a loop module (#165). */
|
|
295
312
|
inLoop: boolean;
|
|
296
313
|
}
|
|
@@ -324,6 +341,18 @@ interface DrawCrossing {
|
|
|
324
341
|
laneA: number;
|
|
325
342
|
laneB: number;
|
|
326
343
|
}
|
|
344
|
+
/** A crossover — a connector track joining two parallel mains through a turnout
|
|
345
|
+
* on each. Draw a straight diagonal between the two turnout points (unlike a
|
|
346
|
+
* siding, which parallels a lane between its turnouts). Detected structurally:
|
|
347
|
+
* a track whose turnouts sit on two different lanes. */
|
|
348
|
+
interface DrawCrossover {
|
|
349
|
+
id: string;
|
|
350
|
+
name: string;
|
|
351
|
+
fromPosFrac: number;
|
|
352
|
+
fromLane: number;
|
|
353
|
+
toPosFrac: number;
|
|
354
|
+
toLane: number;
|
|
355
|
+
}
|
|
327
356
|
/** A branch endplate — draw a connector stub + arrow + label off the given
|
|
328
357
|
* side (the CATS/US&S off-band idiom, #170). */
|
|
329
358
|
interface BranchConnector {
|
|
@@ -352,6 +381,8 @@ interface ModuleFeatures {
|
|
|
352
381
|
signals: DrawSignal[];
|
|
353
382
|
/** Grade crossings / diamonds (#170). */
|
|
354
383
|
crossings: DrawCrossing[];
|
|
384
|
+
/** Crossovers — connector tracks between two mains (drawn as a diagonal). */
|
|
385
|
+
crossovers: DrawCrossover[];
|
|
355
386
|
/** Branch endplates — junction connectors off the module (#170). */
|
|
356
387
|
branchConnectors: BranchConnector[];
|
|
357
388
|
/** Main 2's extent when it doesn't run the full module — a single↔double
|
|
@@ -366,6 +397,17 @@ interface ModuleFeatures {
|
|
|
366
397
|
laneMin: number;
|
|
367
398
|
laneMax: number;
|
|
368
399
|
}
|
|
400
|
+
/**
|
|
401
|
+
* Which side of the main a diverging track draws on, given the turnout's HAND
|
|
402
|
+
* and the direction the track body runs along the main from the turnout
|
|
403
|
+
* (`stubDir`: +1 = the body extends east / toward B, −1 = west / toward A).
|
|
404
|
+
* Returns +1 (above the main) or −1 (below); 0 for a wye or unset hand (keep the
|
|
405
|
+
* authored side). A left-hand turnout throws its route to the same side its body
|
|
406
|
+
* runs (facing the frog, left is the inside of a body running that way); a
|
|
407
|
+
* right-hand throws to the opposite side. `kind` is the source of truth for the
|
|
408
|
+
* drawn side (#bug1) — the stored lane's sign is reconciled to match it.
|
|
409
|
+
*/
|
|
410
|
+
declare function divergeSideForHand(kind: TurnoutKind | undefined, stubDir: number): -1 | 0 | 1;
|
|
369
411
|
/**
|
|
370
412
|
* Resolve a schematic doc into positioned drawables. `pos` (inches) becomes a
|
|
371
413
|
* fraction of the module length; endplate A = 0, B = length; turnouts sit at
|
|
@@ -437,4 +479,4 @@ declare function poseOverridesFromDoc(doc: ModuleSchematicDoc): Record<string, {
|
|
|
437
479
|
heading: number;
|
|
438
480
|
}>;
|
|
439
481
|
|
|
440
|
-
export { type BranchConnector, type DrawCrossing, type DrawSignal, type DrawTrack, type DrawTurnout, type EditorBranch, type EditorControlPoint, type EditorCpSignal, type EditorCrossing, type EditorState, type EditorTrack, type EditorTurnout, type EndplateBConfig, type EndplatePose, type GeometryType, MAIN2_TRACK_ID, MAIN_TRACK_ID, type ModuleFeatures, type ModuleGeometryInput, type ModuleSchematicDoc, type ModuleTrackRow, N_SCALE_RATIO, type SchematicBlock, type SchematicControlPoint, type SchematicCrossing, type SchematicEndplate, type SchematicEndplateTrack, type SchematicSignal, type SchematicTrack, type SchematicTurnout, type SignalFacing, type SignalSide, type TrackConfig, type TrackRole, type TurnoutKind, asModuleSchematic, buildPassingSiding, buildTransition, deriveEndplatePoses, docToState, emptyEditorState, geometryTurnDegrees, inchesToScaleFeet, isLoopDoc, moduleFeatures, nextId, poseNeedsManual, poseOverridesFromDoc, scaleFeetToInches, stateToDoc };
|
|
482
|
+
export { type BranchConnector, type DrawCrossing, type DrawCrossover, type DrawSignal, type DrawTrack, type DrawTurnout, type EditorBranch, type EditorControlPoint, type EditorCpSignal, type EditorCrossing, type EditorState, type EditorTrack, type EditorTurnout, type EndplateBConfig, type EndplatePose, type GeometryType, MAIN2_TRACK_ID, MAIN_TRACK_ID, type ModuleFeatures, type ModuleGeometryInput, type ModuleSchematicDoc, type ModuleTrackRow, N_SCALE_RATIO, type SchematicBlock, type SchematicControlPoint, type SchematicCrossing, type SchematicEndplate, type SchematicEndplateTrack, type SchematicSignal, type SchematicTrack, type SchematicTurnout, type SignalFacing, type SignalSide, type TrackConfig, type TrackRole, type TurnoutKind, asModuleSchematic, buildCrossover, buildPassingSiding, buildTransition, deriveEndplatePoses, divergeSideForHand, docToState, emptyEditorState, geometryTurnDegrees, inchesToScaleFeet, isLoopDoc, moduleFeatures, nextId, poseNeedsManual, poseOverridesFromDoc, scaleFeetToInches, stateToDoc };
|
package/dist/index.d.ts
CHANGED
|
@@ -280,6 +280,16 @@ declare function buildPassingSiding(state: EditorState): {
|
|
|
280
280
|
turnouts: EditorTurnout[];
|
|
281
281
|
controlPoints: EditorControlPoint[];
|
|
282
282
|
};
|
|
283
|
+
/**
|
|
284
|
+
* Build a crossover as one unit: a short connector track between Main 1 and
|
|
285
|
+
* Main 2, with a turnout on each main. The two turnouts sit on different lanes,
|
|
286
|
+
* so the feature resolver draws it as a diagonal (not a lane-paralleling siding)
|
|
287
|
+
* — this is what connects the two dots (#bug2). Needs a double-track module.
|
|
288
|
+
*/
|
|
289
|
+
declare function buildCrossover(state: EditorState): {
|
|
290
|
+
track: EditorTrack;
|
|
291
|
+
turnouts: EditorTurnout[];
|
|
292
|
+
} | null;
|
|
283
293
|
interface DrawTrack {
|
|
284
294
|
id: string;
|
|
285
295
|
role: TrackRole;
|
|
@@ -291,6 +301,13 @@ interface DrawTrack {
|
|
|
291
301
|
* of the diverge diagonal. A spur off Main 2 starts at lane 1, not lane 0;
|
|
292
302
|
* without this, renderers draw what looks like a crossover. */
|
|
293
303
|
divergesFromLane: number;
|
|
304
|
+
/** The end that meets the main at its turnout (throat) and the far stub end,
|
|
305
|
+
* as fractions of length. Direction-preserving: unlike fromFrac/toFrac (always
|
|
306
|
+
* sorted West→East), these keep WHICH end joins, so an east-facing spur draws
|
|
307
|
+
* its throat on the east. A siding meets the main at both ends — there
|
|
308
|
+
* throat=fromFrac, stub=toFrac (the fields are only meaningful for spurs). */
|
|
309
|
+
throatFrac: number;
|
|
310
|
+
stubFrac: number;
|
|
294
311
|
/** Inside the balloon of a loop module (#165). */
|
|
295
312
|
inLoop: boolean;
|
|
296
313
|
}
|
|
@@ -324,6 +341,18 @@ interface DrawCrossing {
|
|
|
324
341
|
laneA: number;
|
|
325
342
|
laneB: number;
|
|
326
343
|
}
|
|
344
|
+
/** A crossover — a connector track joining two parallel mains through a turnout
|
|
345
|
+
* on each. Draw a straight diagonal between the two turnout points (unlike a
|
|
346
|
+
* siding, which parallels a lane between its turnouts). Detected structurally:
|
|
347
|
+
* a track whose turnouts sit on two different lanes. */
|
|
348
|
+
interface DrawCrossover {
|
|
349
|
+
id: string;
|
|
350
|
+
name: string;
|
|
351
|
+
fromPosFrac: number;
|
|
352
|
+
fromLane: number;
|
|
353
|
+
toPosFrac: number;
|
|
354
|
+
toLane: number;
|
|
355
|
+
}
|
|
327
356
|
/** A branch endplate — draw a connector stub + arrow + label off the given
|
|
328
357
|
* side (the CATS/US&S off-band idiom, #170). */
|
|
329
358
|
interface BranchConnector {
|
|
@@ -352,6 +381,8 @@ interface ModuleFeatures {
|
|
|
352
381
|
signals: DrawSignal[];
|
|
353
382
|
/** Grade crossings / diamonds (#170). */
|
|
354
383
|
crossings: DrawCrossing[];
|
|
384
|
+
/** Crossovers — connector tracks between two mains (drawn as a diagonal). */
|
|
385
|
+
crossovers: DrawCrossover[];
|
|
355
386
|
/** Branch endplates — junction connectors off the module (#170). */
|
|
356
387
|
branchConnectors: BranchConnector[];
|
|
357
388
|
/** Main 2's extent when it doesn't run the full module — a single↔double
|
|
@@ -366,6 +397,17 @@ interface ModuleFeatures {
|
|
|
366
397
|
laneMin: number;
|
|
367
398
|
laneMax: number;
|
|
368
399
|
}
|
|
400
|
+
/**
|
|
401
|
+
* Which side of the main a diverging track draws on, given the turnout's HAND
|
|
402
|
+
* and the direction the track body runs along the main from the turnout
|
|
403
|
+
* (`stubDir`: +1 = the body extends east / toward B, −1 = west / toward A).
|
|
404
|
+
* Returns +1 (above the main) or −1 (below); 0 for a wye or unset hand (keep the
|
|
405
|
+
* authored side). A left-hand turnout throws its route to the same side its body
|
|
406
|
+
* runs (facing the frog, left is the inside of a body running that way); a
|
|
407
|
+
* right-hand throws to the opposite side. `kind` is the source of truth for the
|
|
408
|
+
* drawn side (#bug1) — the stored lane's sign is reconciled to match it.
|
|
409
|
+
*/
|
|
410
|
+
declare function divergeSideForHand(kind: TurnoutKind | undefined, stubDir: number): -1 | 0 | 1;
|
|
369
411
|
/**
|
|
370
412
|
* Resolve a schematic doc into positioned drawables. `pos` (inches) becomes a
|
|
371
413
|
* fraction of the module length; endplate A = 0, B = length; turnouts sit at
|
|
@@ -437,4 +479,4 @@ declare function poseOverridesFromDoc(doc: ModuleSchematicDoc): Record<string, {
|
|
|
437
479
|
heading: number;
|
|
438
480
|
}>;
|
|
439
481
|
|
|
440
|
-
export { type BranchConnector, type DrawCrossing, type DrawSignal, type DrawTrack, type DrawTurnout, type EditorBranch, type EditorControlPoint, type EditorCpSignal, type EditorCrossing, type EditorState, type EditorTrack, type EditorTurnout, type EndplateBConfig, type EndplatePose, type GeometryType, MAIN2_TRACK_ID, MAIN_TRACK_ID, type ModuleFeatures, type ModuleGeometryInput, type ModuleSchematicDoc, type ModuleTrackRow, N_SCALE_RATIO, type SchematicBlock, type SchematicControlPoint, type SchematicCrossing, type SchematicEndplate, type SchematicEndplateTrack, type SchematicSignal, type SchematicTrack, type SchematicTurnout, type SignalFacing, type SignalSide, type TrackConfig, type TrackRole, type TurnoutKind, asModuleSchematic, buildPassingSiding, buildTransition, deriveEndplatePoses, docToState, emptyEditorState, geometryTurnDegrees, inchesToScaleFeet, isLoopDoc, moduleFeatures, nextId, poseNeedsManual, poseOverridesFromDoc, scaleFeetToInches, stateToDoc };
|
|
482
|
+
export { type BranchConnector, type DrawCrossing, type DrawCrossover, type DrawSignal, type DrawTrack, type DrawTurnout, type EditorBranch, type EditorControlPoint, type EditorCpSignal, type EditorCrossing, type EditorState, type EditorTrack, type EditorTurnout, type EndplateBConfig, type EndplatePose, type GeometryType, MAIN2_TRACK_ID, MAIN_TRACK_ID, type ModuleFeatures, type ModuleGeometryInput, type ModuleSchematicDoc, type ModuleTrackRow, N_SCALE_RATIO, type SchematicBlock, type SchematicControlPoint, type SchematicCrossing, type SchematicEndplate, type SchematicEndplateTrack, type SchematicSignal, type SchematicTrack, type SchematicTurnout, type SignalFacing, type SignalSide, type TrackConfig, type TrackRole, type TurnoutKind, asModuleSchematic, buildCrossover, buildPassingSiding, buildTransition, deriveEndplatePoses, divergeSideForHand, docToState, emptyEditorState, geometryTurnDegrees, inchesToScaleFeet, isLoopDoc, moduleFeatures, nextId, poseNeedsManual, poseOverridesFromDoc, scaleFeetToInches, stateToDoc };
|
package/dist/index.js
CHANGED
|
@@ -61,7 +61,9 @@ function buildTransition(state) {
|
|
|
61
61
|
pos,
|
|
62
62
|
onTrack: MAIN_TRACK_ID,
|
|
63
63
|
divergeTrack: MAIN2_TRACK_ID,
|
|
64
|
-
|
|
64
|
+
// Main 2 sits above (lane 1); the throw that runs toward the double end is
|
|
65
|
+
// left when that end is east (single at A), right when it's west (#bug1).
|
|
66
|
+
kind: aDouble ? "right" : "left"
|
|
65
67
|
};
|
|
66
68
|
const cpId = nextId("cp", state.controlPoints.map((c) => c.id));
|
|
67
69
|
const sig = (facing) => ({
|
|
@@ -347,8 +349,8 @@ function buildPassingSiding(state) {
|
|
|
347
349
|
const swW = nextId("sw", swIds);
|
|
348
350
|
const swE = nextId("sw", [...swIds, swW]);
|
|
349
351
|
const turnouts = [
|
|
350
|
-
{ id: swW, name: "West Siding", pos: fromPos, onTrack: MAIN_TRACK_ID, divergeTrack: sidId, kind: "
|
|
351
|
-
{ id: swE, name: "East Siding", pos: toPos, onTrack: MAIN_TRACK_ID, divergeTrack: sidId, kind: "
|
|
352
|
+
{ id: swW, name: "West Siding", pos: fromPos, onTrack: MAIN_TRACK_ID, divergeTrack: sidId, kind: "left" },
|
|
353
|
+
{ id: swE, name: "East Siding", pos: toPos, onTrack: MAIN_TRACK_ID, divergeTrack: sidId, kind: "right" }
|
|
352
354
|
];
|
|
353
355
|
const cpIds = state.controlPoints.map((c) => c.id);
|
|
354
356
|
const cpW = nextId("cp", cpIds);
|
|
@@ -367,6 +369,39 @@ function buildPassingSiding(state) {
|
|
|
367
369
|
];
|
|
368
370
|
return { track, turnouts, controlPoints };
|
|
369
371
|
}
|
|
372
|
+
function buildCrossover(state) {
|
|
373
|
+
const hasSecond = state.configA === "double" || state.configB === "double";
|
|
374
|
+
if (state.loop || !hasSecond) return null;
|
|
375
|
+
const len = state.lengthInches > 0 ? state.lengthInches : 24;
|
|
376
|
+
const mid = Math.round(len / 2);
|
|
377
|
+
const gap = Math.max(3, Math.round(len * 0.04));
|
|
378
|
+
const w = Math.round(mid - gap / 2);
|
|
379
|
+
const e = Math.round(mid + gap / 2);
|
|
380
|
+
const trackIds = [MAIN_TRACK_ID, MAIN2_TRACK_ID, ...state.extraTracks.map((t) => t.id)];
|
|
381
|
+
const xoId = nextId("xo", trackIds);
|
|
382
|
+
const track = {
|
|
383
|
+
id: xoId,
|
|
384
|
+
role: "crossover",
|
|
385
|
+
lane: 1,
|
|
386
|
+
fromPos: w,
|
|
387
|
+
toPos: e,
|
|
388
|
+
moduleTrackId: null,
|
|
389
|
+
trackName: "Crossover"
|
|
390
|
+
};
|
|
391
|
+
const swIds = state.turnouts.map((t) => t.id);
|
|
392
|
+
const sw1 = nextId("sw", swIds);
|
|
393
|
+
const sw2 = nextId("sw", [...swIds, sw1]);
|
|
394
|
+
const turnouts = [
|
|
395
|
+
{ id: sw1, name: "Crossover", pos: w, onTrack: MAIN_TRACK_ID, divergeTrack: xoId, kind: "left" },
|
|
396
|
+
{ id: sw2, name: "Crossover", pos: e, onTrack: MAIN2_TRACK_ID, divergeTrack: xoId, kind: "right" }
|
|
397
|
+
];
|
|
398
|
+
return { track, turnouts };
|
|
399
|
+
}
|
|
400
|
+
function divergeSideForHand(kind, stubDir) {
|
|
401
|
+
if (kind !== "left" && kind !== "right") return 0;
|
|
402
|
+
const s = stubDir >= 0 ? 1 : -1;
|
|
403
|
+
return kind === "left" ? s : -s;
|
|
404
|
+
}
|
|
370
405
|
function moduleFeatures(doc) {
|
|
371
406
|
const len = doc.lengthInches && doc.lengthInches > 0 ? doc.lengthInches : Math.max(
|
|
372
407
|
1,
|
|
@@ -393,23 +428,115 @@ function moduleFeatures(doc) {
|
|
|
393
428
|
const sw = (doc.turnouts ?? []).find((t) => t.divergeTrack === trackId);
|
|
394
429
|
return sw ? trackLane.get(sw.onTrack) ?? 0 : 0;
|
|
395
430
|
};
|
|
431
|
+
const turnoutsByTrack = /* @__PURE__ */ new Map();
|
|
432
|
+
for (const sw of doc.turnouts ?? []) {
|
|
433
|
+
const arr = turnoutsByTrack.get(sw.divergeTrack) ?? [];
|
|
434
|
+
arr.push(sw);
|
|
435
|
+
turnoutsByTrack.set(sw.divergeTrack, arr);
|
|
436
|
+
}
|
|
437
|
+
const isCrossover = (trackId) => {
|
|
438
|
+
const sws = turnoutsByTrack.get(trackId) ?? [];
|
|
439
|
+
if (sws.length < 2) return false;
|
|
440
|
+
return new Set(sws.map((s) => trackLane.get(s.onTrack) ?? 0)).size >= 2;
|
|
441
|
+
};
|
|
442
|
+
const extentOf = (t) => {
|
|
443
|
+
const from = t.fromPos ?? posOf(t.from);
|
|
444
|
+
const to = t.toPos ?? posOf(t.to);
|
|
445
|
+
return from == null || to == null ? null : [from, to];
|
|
446
|
+
};
|
|
447
|
+
const trackById = new Map(doc.tracks.map((t) => [t.id, t]));
|
|
448
|
+
const resolvedLanes = /* @__PURE__ */ new Map();
|
|
449
|
+
const resolving = /* @__PURE__ */ new Set();
|
|
450
|
+
const resolveLane = (id) => {
|
|
451
|
+
const trk = trackById.get(id);
|
|
452
|
+
if (!trk) return 0;
|
|
453
|
+
if (trk.role === "main") return trk.lane;
|
|
454
|
+
if (resolvedLanes.has(id)) return resolvedLanes.get(id);
|
|
455
|
+
if (resolving.has(id)) return trk.lane;
|
|
456
|
+
resolving.add(id);
|
|
457
|
+
let lane = trk.lane;
|
|
458
|
+
const sw = turnoutsByTrack.get(id)?.[0];
|
|
459
|
+
const ext = extentOf(trk);
|
|
460
|
+
if (sw && (sw.kind === "left" || sw.kind === "right") && !isCrossover(id) && ext) {
|
|
461
|
+
const parentLane = resolveLane(sw.onTrack);
|
|
462
|
+
let sign;
|
|
463
|
+
if (parentLane === 0) {
|
|
464
|
+
const [from, to] = ext;
|
|
465
|
+
const far = Math.abs(to - sw.pos) >= Math.abs(from - sw.pos) ? to : from;
|
|
466
|
+
const s = divergeSideForHand(sw.kind, far - sw.pos);
|
|
467
|
+
sign = s !== 0 ? s : Math.sign(trk.lane) || 1;
|
|
468
|
+
} else {
|
|
469
|
+
sign = Math.sign(parentLane) || 1;
|
|
470
|
+
}
|
|
471
|
+
lane = sign * Math.abs(trk.lane);
|
|
472
|
+
}
|
|
473
|
+
resolving.delete(id);
|
|
474
|
+
resolvedLanes.set(id, lane);
|
|
475
|
+
return lane;
|
|
476
|
+
};
|
|
477
|
+
for (const t of doc.tracks) {
|
|
478
|
+
if (t.role === "main") continue;
|
|
479
|
+
trackLane.set(t.id, resolveLane(t.id));
|
|
480
|
+
}
|
|
396
481
|
const extraTracks = [];
|
|
397
482
|
for (const t of doc.tracks) {
|
|
398
483
|
if (t.role === "main") continue;
|
|
399
|
-
|
|
400
|
-
const
|
|
401
|
-
if (
|
|
484
|
+
if (isCrossover(t.id)) continue;
|
|
485
|
+
const ext = extentOf(t);
|
|
486
|
+
if (!ext) continue;
|
|
487
|
+
const [from, to] = ext;
|
|
488
|
+
const sw = turnoutsByTrack.get(t.id)?.[0];
|
|
489
|
+
const throatAtTo = sw != null && Math.abs(to - sw.pos) < Math.abs(from - sw.pos);
|
|
490
|
+
const throat = throatAtTo ? to : from;
|
|
491
|
+
const stub = throatAtTo ? from : to;
|
|
402
492
|
extraTracks.push({
|
|
403
493
|
id: t.id,
|
|
404
494
|
role: t.role,
|
|
405
|
-
lane: t.lane,
|
|
495
|
+
lane: trackLane.get(t.id) ?? t.lane,
|
|
406
496
|
fromFrac: clampFrac(Math.min(from, to)),
|
|
407
497
|
toFrac: clampFrac(Math.max(from, to)),
|
|
498
|
+
throatFrac: clampFrac(throat),
|
|
499
|
+
stubFrac: clampFrac(stub),
|
|
408
500
|
capacityFeet: t.capacityFeet ?? null,
|
|
409
501
|
divergesFromLane: divergeOrigin(t.id),
|
|
410
502
|
inLoop: t.inLoop === true
|
|
411
503
|
});
|
|
412
504
|
}
|
|
505
|
+
const crossovers = [];
|
|
506
|
+
for (const t of doc.tracks) {
|
|
507
|
+
if (!isCrossover(t.id)) continue;
|
|
508
|
+
const [s1, s2] = turnoutsByTrack.get(t.id);
|
|
509
|
+
crossovers.push({
|
|
510
|
+
id: t.id,
|
|
511
|
+
name: t.trackName ?? "",
|
|
512
|
+
fromPosFrac: clampFrac(s1.pos),
|
|
513
|
+
fromLane: trackLane.get(s1.onTrack) ?? 0,
|
|
514
|
+
toPosFrac: clampFrac(s2.pos),
|
|
515
|
+
toLane: trackLane.get(s2.onTrack) ?? 1
|
|
516
|
+
});
|
|
517
|
+
}
|
|
518
|
+
const isMainId = (id) => !!id && trackById.get(id)?.role === "main";
|
|
519
|
+
const m2m = (doc.turnouts ?? []).filter(
|
|
520
|
+
(sw) => isMainId(sw.onTrack) && isMainId(sw.divergeTrack) && (trackLane.get(sw.onTrack) ?? 0) !== (trackLane.get(sw.divergeTrack) ?? 1)
|
|
521
|
+
);
|
|
522
|
+
const usedLegs = /* @__PURE__ */ new Set();
|
|
523
|
+
for (const t1 of m2m) {
|
|
524
|
+
if (usedLegs.has(t1.id)) continue;
|
|
525
|
+
usedLegs.add(t1.id);
|
|
526
|
+
const t2 = m2m.find(
|
|
527
|
+
(x) => !usedLegs.has(x.id) && x.onTrack === t1.divergeTrack && x.divergeTrack === t1.onTrack
|
|
528
|
+
);
|
|
529
|
+
if (t2) usedLegs.add(t2.id);
|
|
530
|
+
const end = t2 ?? t1;
|
|
531
|
+
crossovers.push({
|
|
532
|
+
id: t2 ? `${t1.id}-${t2.id}` : t1.id,
|
|
533
|
+
name: t1.name ?? t2?.name ?? "",
|
|
534
|
+
fromPosFrac: clampFrac(t1.pos),
|
|
535
|
+
fromLane: trackLane.get(t1.onTrack) ?? 0,
|
|
536
|
+
toPosFrac: clampFrac(end === t1 ? t1.pos : end.pos),
|
|
537
|
+
toLane: trackLane.get(t1.divergeTrack) ?? 1
|
|
538
|
+
});
|
|
539
|
+
}
|
|
413
540
|
const turnouts = (doc.turnouts ?? []).map((t) => ({
|
|
414
541
|
id: t.id,
|
|
415
542
|
name: t.name ?? "",
|
|
@@ -455,7 +582,8 @@ function moduleFeatures(doc) {
|
|
|
455
582
|
doubleMain ? 1 : 0,
|
|
456
583
|
...extraTracks.map((t) => t.lane),
|
|
457
584
|
...signals.map((s) => s.lane),
|
|
458
|
-
...crossings.flatMap((x) => [x.laneA, x.laneB])
|
|
585
|
+
...crossings.flatMap((x) => [x.laneA, x.laneB]),
|
|
586
|
+
...crossovers.flatMap((x) => [x.fromLane, x.toLane])
|
|
459
587
|
];
|
|
460
588
|
const loop = isLoopDoc(doc);
|
|
461
589
|
const main2 = doc.tracks.find((t) => t.id === MAIN2_TRACK_ID);
|
|
@@ -475,6 +603,7 @@ function moduleFeatures(doc) {
|
|
|
475
603
|
turnouts,
|
|
476
604
|
signals,
|
|
477
605
|
crossings,
|
|
606
|
+
crossovers,
|
|
478
607
|
branchConnectors,
|
|
479
608
|
laneMin: Math.min(...allLanes),
|
|
480
609
|
laneMax: Math.max(...allLanes)
|
|
@@ -578,6 +707,6 @@ function poseOverridesFromDoc(doc) {
|
|
|
578
707
|
return out;
|
|
579
708
|
}
|
|
580
709
|
|
|
581
|
-
export { MAIN2_TRACK_ID, MAIN_TRACK_ID, N_SCALE_RATIO, asModuleSchematic, buildPassingSiding, buildTransition, deriveEndplatePoses, docToState, emptyEditorState, geometryTurnDegrees, inchesToScaleFeet, isLoopDoc, moduleFeatures, nextId, poseNeedsManual, poseOverridesFromDoc, scaleFeetToInches, stateToDoc };
|
|
710
|
+
export { MAIN2_TRACK_ID, MAIN_TRACK_ID, N_SCALE_RATIO, asModuleSchematic, buildCrossover, buildPassingSiding, buildTransition, deriveEndplatePoses, divergeSideForHand, docToState, emptyEditorState, geometryTurnDegrees, inchesToScaleFeet, isLoopDoc, moduleFeatures, nextId, poseNeedsManual, poseOverridesFromDoc, scaleFeetToInches, stateToDoc };
|
|
582
711
|
//# sourceMappingURL=index.js.map
|
|
583
712
|
//# sourceMappingURL=index.js.map
|