@willcgage/module-schematic 0.11.0 → 0.13.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.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
  }
@@ -311,6 +328,10 @@ interface DrawSignal {
311
328
  /** Owning control point's id, when the signal came from a CP group — lets a
312
329
  * renderer join the drawn signal back to interlocking-level state (aspects). */
313
330
  cp?: string;
331
+ /** 0-based rank among signals that would otherwise land on the exact same
332
+ * spot (same lane + side + position). Renderers offset each further from the
333
+ * track by `stack` so a control point's signals never overlap. */
334
+ stack: number;
314
335
  }
315
336
  /** A grade crossing / diamond — draw an X spanning the two lanes (#170). */
316
337
  interface DrawCrossing {
@@ -320,6 +341,18 @@ interface DrawCrossing {
320
341
  laneA: number;
321
342
  laneB: number;
322
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
+ }
323
356
  /** A branch endplate — draw a connector stub + arrow + label off the given
324
357
  * side (the CATS/US&S off-band idiom, #170). */
325
358
  interface BranchConnector {
@@ -348,6 +381,8 @@ interface ModuleFeatures {
348
381
  signals: DrawSignal[];
349
382
  /** Grade crossings / diamonds (#170). */
350
383
  crossings: DrawCrossing[];
384
+ /** Crossovers — connector tracks between two mains (drawn as a diagonal). */
385
+ crossovers: DrawCrossover[];
351
386
  /** Branch endplates — junction connectors off the module (#170). */
352
387
  branchConnectors: BranchConnector[];
353
388
  /** Main 2's extent when it doesn't run the full module — a single↔double
@@ -362,6 +397,17 @@ interface ModuleFeatures {
362
397
  laneMin: number;
363
398
  laneMax: number;
364
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;
365
411
  /**
366
412
  * Resolve a schematic doc into positioned drawables. `pos` (inches) becomes a
367
413
  * fraction of the module length; endplate A = 0, B = length; turnouts sit at
@@ -433,4 +479,4 @@ declare function poseOverridesFromDoc(doc: ModuleSchematicDoc): Record<string, {
433
479
  heading: number;
434
480
  }>;
435
481
 
436
- 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
  }
@@ -311,6 +328,10 @@ interface DrawSignal {
311
328
  /** Owning control point's id, when the signal came from a CP group — lets a
312
329
  * renderer join the drawn signal back to interlocking-level state (aspects). */
313
330
  cp?: string;
331
+ /** 0-based rank among signals that would otherwise land on the exact same
332
+ * spot (same lane + side + position). Renderers offset each further from the
333
+ * track by `stack` so a control point's signals never overlap. */
334
+ stack: number;
314
335
  }
315
336
  /** A grade crossing / diamond — draw an X spanning the two lanes (#170). */
316
337
  interface DrawCrossing {
@@ -320,6 +341,18 @@ interface DrawCrossing {
320
341
  laneA: number;
321
342
  laneB: number;
322
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
+ }
323
356
  /** A branch endplate — draw a connector stub + arrow + label off the given
324
357
  * side (the CATS/US&S off-band idiom, #170). */
325
358
  interface BranchConnector {
@@ -348,6 +381,8 @@ interface ModuleFeatures {
348
381
  signals: DrawSignal[];
349
382
  /** Grade crossings / diamonds (#170). */
350
383
  crossings: DrawCrossing[];
384
+ /** Crossovers — connector tracks between two mains (drawn as a diagonal). */
385
+ crossovers: DrawCrossover[];
351
386
  /** Branch endplates — junction connectors off the module (#170). */
352
387
  branchConnectors: BranchConnector[];
353
388
  /** Main 2's extent when it doesn't run the full module — a single↔double
@@ -362,6 +397,17 @@ interface ModuleFeatures {
362
397
  laneMin: number;
363
398
  laneMax: number;
364
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;
365
411
  /**
366
412
  * Resolve a schematic doc into positioned drawables. `pos` (inches) becomes a
367
413
  * fraction of the module length; endplate A = 0, B = length; turnouts sit at
@@ -433,4 +479,4 @@ declare function poseOverridesFromDoc(doc: ModuleSchematicDoc): Record<string, {
433
479
  heading: number;
434
480
  }>;
435
481
 
436
- 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
- kind: aDouble ? "left" : "right"
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: "right" },
351
- { id: swE, name: "East Siding", pos: toPos, onTrack: MAIN_TRACK_ID, divergeTrack: sidId, kind: "left" }
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,70 @@ 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
+ for (const t of doc.tracks) {
448
+ if (t.role === "main" || isCrossover(t.id)) continue;
449
+ const sw = turnoutsByTrack.get(t.id)?.[0];
450
+ if (!sw) continue;
451
+ const ext = extentOf(t);
452
+ if (!ext) continue;
453
+ const [from, to] = ext;
454
+ const far = Math.abs(to - sw.pos) >= Math.abs(from - sw.pos) ? to : from;
455
+ const side = divergeSideForHand(sw.kind, far - sw.pos);
456
+ if (side !== 0) trackLane.set(t.id, side * Math.abs(t.lane));
457
+ }
396
458
  const extraTracks = [];
397
459
  for (const t of doc.tracks) {
398
460
  if (t.role === "main") continue;
399
- const from = t.fromPos ?? posOf(t.from);
400
- const to = t.toPos ?? posOf(t.to);
401
- if (from == null || to == null) continue;
461
+ if (isCrossover(t.id)) continue;
462
+ const ext = extentOf(t);
463
+ if (!ext) continue;
464
+ const [from, to] = ext;
465
+ const sw = turnoutsByTrack.get(t.id)?.[0];
466
+ const throatAtTo = sw != null && Math.abs(to - sw.pos) < Math.abs(from - sw.pos);
467
+ const throat = throatAtTo ? to : from;
468
+ const stub = throatAtTo ? from : to;
402
469
  extraTracks.push({
403
470
  id: t.id,
404
471
  role: t.role,
405
- lane: t.lane,
472
+ lane: trackLane.get(t.id) ?? t.lane,
406
473
  fromFrac: clampFrac(Math.min(from, to)),
407
474
  toFrac: clampFrac(Math.max(from, to)),
475
+ throatFrac: clampFrac(throat),
476
+ stubFrac: clampFrac(stub),
408
477
  capacityFeet: t.capacityFeet ?? null,
409
478
  divergesFromLane: divergeOrigin(t.id),
410
479
  inLoop: t.inLoop === true
411
480
  });
412
481
  }
482
+ const crossovers = [];
483
+ for (const t of doc.tracks) {
484
+ if (!isCrossover(t.id)) continue;
485
+ const [s1, s2] = turnoutsByTrack.get(t.id);
486
+ crossovers.push({
487
+ id: t.id,
488
+ name: t.trackName ?? "",
489
+ fromPosFrac: clampFrac(s1.pos),
490
+ fromLane: trackLane.get(s1.onTrack) ?? 0,
491
+ toPosFrac: clampFrac(s2.pos),
492
+ toLane: trackLane.get(s2.onTrack) ?? 1
493
+ });
494
+ }
413
495
  const turnouts = (doc.turnouts ?? []).map((t) => ({
414
496
  id: t.id,
415
497
  name: t.name ?? "",
@@ -424,11 +506,19 @@ function moduleFeatures(doc) {
424
506
  lane: s.track ? trackLane.get(s.track) ?? 0 : 0,
425
507
  facing: s.facing ?? "AtoB",
426
508
  side: s.side === "below" ? "below" : "above",
427
- ...cp ? { cp } : {}
509
+ ...cp ? { cp } : {},
510
+ stack: 0
428
511
  });
429
512
  const signals = Array.isArray(doc.controlPoints) ? doc.controlPoints.flatMap(
430
513
  (c) => (c.signals ?? []).map((s) => drawSignal(s, c.name ?? "", c.id))
431
514
  ) : (doc.signals ?? []).map((s) => drawSignal(s, s.name ?? ""));
515
+ const stackCount = /* @__PURE__ */ new Map();
516
+ for (const s of signals) {
517
+ const key = `${s.lane}|${s.side}|${Math.round(s.posFrac * 1e3)}`;
518
+ const n = stackCount.get(key) ?? 0;
519
+ s.stack = n;
520
+ stackCount.set(key, n + 1);
521
+ }
432
522
  const crossings = (doc.crossings ?? []).map((x) => ({
433
523
  id: x.id,
434
524
  name: x.name ?? "",
@@ -447,7 +537,8 @@ function moduleFeatures(doc) {
447
537
  doubleMain ? 1 : 0,
448
538
  ...extraTracks.map((t) => t.lane),
449
539
  ...signals.map((s) => s.lane),
450
- ...crossings.flatMap((x) => [x.laneA, x.laneB])
540
+ ...crossings.flatMap((x) => [x.laneA, x.laneB]),
541
+ ...crossovers.flatMap((x) => [x.fromLane, x.toLane])
451
542
  ];
452
543
  const loop = isLoopDoc(doc);
453
544
  const main2 = doc.tracks.find((t) => t.id === MAIN2_TRACK_ID);
@@ -467,6 +558,7 @@ function moduleFeatures(doc) {
467
558
  turnouts,
468
559
  signals,
469
560
  crossings,
561
+ crossovers,
470
562
  branchConnectors,
471
563
  laneMin: Math.min(...allLanes),
472
564
  laneMax: Math.max(...allLanes)
@@ -570,6 +662,6 @@ function poseOverridesFromDoc(doc) {
570
662
  return out;
571
663
  }
572
664
 
573
- 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 };
665
+ 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 };
574
666
  //# sourceMappingURL=index.js.map
575
667
  //# sourceMappingURL=index.js.map