@willcgage/module-schematic 0.86.0 → 0.90.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
@@ -242,6 +242,9 @@ interface SchematicTurnout {
242
242
  interface SchematicSignal {
243
243
  id: string;
244
244
  pos: number;
245
+ /** Hold this signal by the piece it stands beside (ADR 0001). See
246
+ * {@link GraphAnchor} — a mast is planted next to a particular bit of rail. */
247
+ anchor?: GraphAnchor | null;
245
248
  /** Track the signal governs; absent = the primary main (lane 0). */
246
249
  track?: string;
247
250
  facing?: SignalFacing;
@@ -295,11 +298,40 @@ type IndustryLabelMode = "none" | "cars" | "inches";
295
298
  */
296
299
  /** One car-spot span of an industry on a track — an industry may have several
297
300
  * (a house track serving one customer across multiple spot tracks, #54). */
301
+ /**
302
+ * ⭐ A PLACE ON THE TRACK, HELD BY THE PIECE THAT IS THERE (ADR 0001).
303
+ *
304
+ * `pos` — inches from endplate A — is a fine way to *read* where something is
305
+ * and a poor way to *hold* it: it is a number about the whole module, so every
306
+ * edit anywhere upstream silently changes what it means. A car spot belongs to
307
+ * the rail it is beside. Anchored to the piece, moving that piece takes the
308
+ * industry with it and moving anything else leaves it alone, which is what an
309
+ * owner dragging track expects to happen.
310
+ *
311
+ * Present = `pos` / `fromPos` / `toPos` are DERIVED from this by
312
+ * {@link graphToDoc}. Absent = the authored numbers stand, exactly as before —
313
+ * nothing migrates (ADR 0001).
314
+ */
315
+ interface GraphAnchor {
316
+ /** The {@link TrackPiece} it sits on. */
317
+ piece: string;
318
+ /**
319
+ * Inches along that piece from its OWN origin end — joint `a` on flex, the
320
+ * throat on a turnout, and in every case the part's x=0.
321
+ *
322
+ * ⚠️ Deliberately NOT "from the end the walk came in by". Which end that is
323
+ * depends on where the module's endplate A happens to be, so the same spot
324
+ * beside the same rail would mean two different numbers on two modules.
325
+ */
326
+ atInches: number;
327
+ }
298
328
  interface IndustrySpot {
299
329
  track: string;
300
330
  fromPos: number;
301
331
  toPos: number;
302
332
  side?: SignalSide;
333
+ /** Hold this spot by the piece it is beside (ADR 0001). See {@link GraphAnchor}. */
334
+ anchor?: GraphAnchor | null;
303
335
  }
304
336
  interface SchematicIndustry {
305
337
  id: string;
@@ -312,6 +344,10 @@ interface SchematicIndustry {
312
344
  /** The primary car-spot span along `track`, inches from endplate A. */
313
345
  fromPos: number;
314
346
  toPos: number;
347
+ /** Hold this span by the piece it is beside (ADR 0001). See {@link GraphAnchor}.
348
+ * The span's LENGTH stays authored — a dock face is as long as it is built;
349
+ * only where it begins is read off the track. */
350
+ anchor?: GraphAnchor | null;
315
351
  /** Extra car-spot spans on other tracks — the industry's house-track spots. */
316
352
  spots?: IndustrySpot[];
317
353
  /** Which side of the track the building + label sit on. */
@@ -392,6 +428,27 @@ interface ModuleSchematicDoc {
392
428
  * shape; absent = derive it as a lane offset from Main 1. Physical view only
393
429
  * (#131). */
394
430
  main2Path?: BenchworkPoint[] | null;
431
+ /**
432
+ * ⭐⭐ THE AUTHORED TRACK, when this module was drawn as PIECES (ADR 0001).
433
+ *
434
+ * Present = this is the source, and `tracks`, `turnouts` and every anchored
435
+ * feature's position are DERIVED from it by {@link deriveGraphDoc} — they are
436
+ * still written into the document so that everything downstream, Free-
437
+ * Dispatcher included, reads an ordinary document and is never told which way
438
+ * the module was authored.
439
+ *
440
+ * Absent = the module is authored the way it always was, and nothing about it
441
+ * changes. No document is converted (ADR 0001): converting one would mean
442
+ * inventing the leads, frogs and radii nobody measured.
443
+ */
444
+ graph?: {
445
+ pieces: TrackPiece[];
446
+ /** Where the main starts: the joint endplate A's track arrives at. */
447
+ startAt: {
448
+ piece: string;
449
+ joint: string;
450
+ };
451
+ } | null;
395
452
  }
396
453
  /** A benchwork-outline vertex, module-local inches. The edge from this vertex
397
454
  * to the NEXT one is a straight line, unless `bulge` is set — then it's a
@@ -1122,6 +1179,8 @@ interface EditorIndustry {
1122
1179
  carTypes: string[];
1123
1180
  /** freemon_industries row (single source of truth), or null for a new one. */
1124
1181
  moduleIndustryId: number | null;
1182
+ /** The piece this span is held by (ADR 0001). See {@link GraphAnchor}. */
1183
+ anchor?: GraphAnchor | null;
1125
1184
  }
1126
1185
  /** A 3rd+ endplate — a branch/junction connection off the module (#170).
1127
1186
  * A module may have several (e.g. a set carrying a second railroad through:
@@ -1216,6 +1275,10 @@ interface EditorState {
1216
1275
  mainPath: BenchworkPoint[];
1217
1276
  /** Authored Main 2 centre-line (double-track only) — empty = lane offset (#131). */
1218
1277
  main2Path: BenchworkPoint[];
1278
+ /** ⭐ The track as PLACED PIECES (ADR 0001), when the owner drew it that way.
1279
+ * Absent = authored the way it always was, and nothing changes. See
1280
+ * {@link ModuleSchematicDoc.graph} and {@link deriveGraphDoc}. */
1281
+ graph?: ModuleSchematicDoc["graph"];
1219
1282
  }
1220
1283
  /** Build the empty editor state for a module of the given length. */
1221
1284
  declare function emptyEditorState(lengthInches: number): EditorState;
@@ -2360,6 +2423,27 @@ declare function partGeometryGap(part: TrackPart): string | null;
2360
2423
  * which you got.
2361
2424
  */
2362
2425
  declare function partGeometry(part: TrackPart, library?: TrackPart[]): PartGeometry | null;
2426
+ /**
2427
+ * The SHAPE of every route through a placed piece, in MODULE inches — the
2428
+ * polyline a renderer draws.
2429
+ *
2430
+ * ⭐ The shape belongs to the PART, so it lives here rather than in either app's
2431
+ * canvas. A diverging route drawn as a straight chord from throat to diverging
2432
+ * end cuts the corner the closure actually turns; every renderer would have to
2433
+ * rebuild that curve, and they would drift.
2434
+ *
2435
+ * ⚠️ Each polyline ENDS EXACTLY ON ITS JOINTS, by construction — the samples in
2436
+ * between follow the closure, but the ends are the joint positions themselves.
2437
+ * Track that stops short of its own joint would draw a gap at a connection that
2438
+ * the graph considers made.
2439
+ */
2440
+ declare function pieceRoutePaths(piece: TrackPiece, library?: TrackPart[]): {
2441
+ route: [string, string];
2442
+ points: {
2443
+ x: number;
2444
+ y: number;
2445
+ }[];
2446
+ }[];
2363
2447
  /** Every part that can be PLACED on a board today, and every one that can only
2364
2448
  * be named — with the reason. The gap list is the parts backlog. */
2365
2449
  declare function partsPlaceable(library?: TrackPart[]): {
@@ -2446,6 +2530,45 @@ declare function placedJoints(pieces: TrackPiece[], library?: TrackPart[]): Plac
2446
2530
  * part carrying three joints of its own.
2447
2531
  */
2448
2532
  declare function buildTrackGraph(pieces: TrackPiece[], library?: TrackPart[], snapInches?: number): TrackGraph;
2533
+ /** A piece as the route crosses it — what an anchored feature resolves against. */
2534
+ interface RouteSpan {
2535
+ piece: string;
2536
+ /** The joints the route entered and left this piece by. Tell you which way
2537
+ * round the piece's own measurements run against the route's. */
2538
+ entryJoint: string;
2539
+ exitJoint: string;
2540
+ /** Inches from endplate A at the entry joint, and at the one it left by. */
2541
+ fromPos: number;
2542
+ toPos: number;
2543
+ }
2544
+ /** A piece moved into place against an open joint. */
2545
+ interface PieceSnap {
2546
+ /** The piece, rotated and translated so the two joints coincide. */
2547
+ piece: TrackPiece;
2548
+ /** The moving joint's key, and the open joint it was brought onto. */
2549
+ from: string;
2550
+ to: string;
2551
+ }
2552
+ /**
2553
+ * Bring a piece being dragged onto the nearest OPEN joint of the others.
2554
+ *
2555
+ * ⛔ **ONLY OPEN JOINTS ARE CANDIDATES, and that is the ADR's standing rule
2556
+ * enforced where it can still be obeyed.** A joint already holding a connection
2557
+ * is not offered, so an owner cannot stack a third rail end on a joint at all —
2558
+ * rather than stacking it and having {@link buildTrackGraph} refuse all three
2559
+ * afterwards, with a piece silently outside the layout until they notice.
2560
+ *
2561
+ * The piece is ROTATED as well as moved: two ends meet when they are in the same
2562
+ * place and facing opposite ways, so the rotation falls out of the joint it is
2563
+ * brought to. That is what makes laying a curve out of straight pieces possible
2564
+ * without typing an angle.
2565
+ *
2566
+ * `withinInches` is a GRAB radius — how near counts as "meant it" — and is a
2567
+ * different thing from {@link JOINT_SNAP_INCHES}, which is how close two joints
2568
+ * must be to BE connected. This one is generous; that one is a hundredth of an
2569
+ * inch.
2570
+ */
2571
+ declare function snapPiece(moving: TrackPiece, others: TrackPiece[], library?: TrackPart[], withinInches?: number): PieceSnap | null;
2449
2572
  /** A route the walk found: a continuous run of pieces a train can travel. */
2450
2573
  interface GraphRoute {
2451
2574
  id: string;
@@ -2457,6 +2580,8 @@ interface GraphRoute {
2457
2580
  /** The turnout it runs back into. Set = a SIDING; null = it dead-ends. */
2458
2581
  endsAt: string | null;
2459
2582
  pieces: string[];
2583
+ /** The same pieces, each with where it starts and ends along the route. */
2584
+ spans: RouteSpan[];
2460
2585
  /** Furthest lateral offset reached — what gives a lane its side. */
2461
2586
  lateral: number;
2462
2587
  }
@@ -2488,6 +2613,94 @@ declare function walkTrackGraph(graph: TrackGraph, pieces: TrackPiece[], startAt
2488
2613
  piece: string;
2489
2614
  joint: string;
2490
2615
  }, library?: TrackPart[]): GraphWalk;
2616
+ /** Where an anchored feature turned out to be. */
2617
+ interface ResolvedAnchor {
2618
+ /** Inches from endplate A — the number the document and the dispatcher read. */
2619
+ pos: number;
2620
+ /** The route the anchored piece is part of. */
2621
+ routeId: string;
2622
+ /** The piece's own measuring direction runs BACKWARD along the route, so
2623
+ * increasing {@link GraphAnchor.atInches} moves toward endplate A. */
2624
+ reversed: boolean;
2625
+ }
2626
+ /**
2627
+ * Turn a {@link GraphAnchor} into a position along the module.
2628
+ *
2629
+ * The piece is measured from its own origin end, and the walk knows both ends
2630
+ * of it, so whichever end the route arrived by, the sum comes out the same.
2631
+ * Returns null when the anchored piece is not on any route — a spur nobody has
2632
+ * connected yet — which is a thing to report, not to guess at.
2633
+ */
2634
+ declare function resolveGraphAnchor(anchor: GraphAnchor, walk: GraphWalk, pieces: TrackPiece[], library?: TrackPart[]): ResolvedAnchor | null;
2635
+ /** What the graph cannot know, and therefore never invents. */
2636
+ interface GraphDocInput {
2637
+ /** Where the main begins: the joint endplate A's track arrives at. */
2638
+ startAt: {
2639
+ piece: string;
2640
+ joint: string;
2641
+ };
2642
+ /**
2643
+ * The rest of the document — module id, endplate identities, the benchwork,
2644
+ * industries and signals. Merged UNDERNEATH the derived keys, so the graph
2645
+ * wins on length, tracks and turnouts and on nothing else.
2646
+ *
2647
+ * ⏸️ Industries, signals and control points are PASSED THROUGH untouched.
2648
+ * Where they live in a graph is deliberately still open (ADR 0001 defers it
2649
+ * to persistence); carrying them is honest, re-deriving them would be a guess.
2650
+ */
2651
+ base?: Partial<ModuleSchematicDoc>;
2652
+ /**
2653
+ * Owner metadata for a run, keyed by the piece the run STARTS at — the piece
2654
+ * they select and name. Held here rather than on {@link TrackPiece} because
2655
+ * it describes the whole run, not the one piece.
2656
+ */
2657
+ meta?: Record<string, {
2658
+ trackName?: string;
2659
+ capacityFeet?: number | null;
2660
+ moduleTrackId?: number | null;
2661
+ }>;
2662
+ library?: TrackPart[];
2663
+ }
2664
+ interface GraphDocResult {
2665
+ doc: ModuleSchematicDoc;
2666
+ graph: TrackGraph;
2667
+ walk: GraphWalk;
2668
+ /** The walk's warnings, plus anything the emission itself had to leave out. */
2669
+ warnings: string[];
2670
+ }
2671
+ /**
2672
+ * Derive a `ModuleSchematicDoc` from placed pieces.
2673
+ *
2674
+ * ⭐ **NO HAND IS EMITTED.** A turnout's `kind` is left unset on purpose. The
2675
+ * graph knows which side the diverging route is on — it is where the piece IS —
2676
+ * so the lane carries the side and `moduleFeatures` keeps the lane it is given
2677
+ * (`resolveLane` only overrides the sign for a stated left/right). Emitting a
2678
+ * hand as well would be a second source for one fact, which is the ~120 lines
2679
+ * of reconciliation this model exists to remove.
2680
+ *
2681
+ * ⚠️ ONE MAIN. The walk starts at one endplate, so a double-track module's
2682
+ * second main is not reached — it surfaces in `warnings` as unreachable pieces
2683
+ * rather than vanishing quietly. Emitting Main 2 needs a second start point and
2684
+ * is not built yet.
2685
+ */
2686
+ declare function graphToDoc(pieces: TrackPiece[], input: GraphDocInput): GraphDocResult;
2687
+ /**
2688
+ * Bring a document up to date with the track its owner drew.
2689
+ *
2690
+ * A document with no `graph` is returned UNTOUCHED — that is the ADR's
2691
+ * no-migration rule in one line, and it is why every module authored before this
2692
+ * keeps behaving exactly as it did. With a graph, the tracks and turnouts are
2693
+ * re-read off the pieces and written back into the ordinary document keys, so
2694
+ * the module is stored in a form every existing reader already understands.
2695
+ *
2696
+ * ⭐ Owners' names, capacities and `module_tracks` links are carried across from
2697
+ * the tracks already in the document, keyed by id. Re-deriving must never cost
2698
+ * an owner the name they gave a siding.
2699
+ */
2700
+ declare function deriveGraphDoc(doc: ModuleSchematicDoc, library?: TrackPart[]): {
2701
+ doc: ModuleSchematicDoc;
2702
+ warnings: string[];
2703
+ };
2491
2704
  /** A frog casting's parts, in TURNOUT-LOCAL inches: `x` = distance past the
2492
2705
  * points, `y` = lateral offset from the through route. */
2493
2706
  interface FrogCasting {
@@ -2769,4 +2982,4 @@ declare function poseOverridesFromDoc(doc: ModuleSchematicDoc): Record<string, {
2769
2982
  heading: number;
2770
2983
  }>;
2771
2984
 
2772
- export { ATLAS_CODE55_N, BUILT_IN_TRACK_PARTS, type BenchworkPoint, type BranchConnector, CLEARANCE_SPACING_INCHES, CODE55_RAIL_HEIGHT_INCHES, DEFAULT_FLEX_PART_ID, type DimensionSource, type DrawCrossing, type DrawCrossover, type DrawIndustry, type DrawSignal, type DrawTrack, type DrawTurnout, ENDPLATE_FASCIA_CLEAR_INCHES, ENDPLATE_LEAD_INCHES, type EditorBranch, type EditorControlPoint, type EditorCpSignal, type EditorCrossing, type EditorIndustry, type EditorState, type EditorTrack, type EditorTurnout, type EndplateBConfig, type EndplateEdge, type EndplateEdgePose, type EndplatePose, type EndplateTrackIssue, type EndplateWidthIssue, FAST_TRACKS_N_ME55, FAST_TRACKS_N_ME55_CROSSOVERS, FLEX_TRACK_PARTS, FREEMO_ENDPLATE_TRACK_FASCIA_CLEARANCE_INCHES, FREEMO_ENDPLATE_WIDTH_MIN_INCHES, FREEMO_ENDPLATE_WIDTH_RECOMMENDED_INCHES, FREEMO_TRACK_SPACING_INCHES, type FlexPiece, type FlexPieceEnd, type FrogCasting, type GeometryType, type GraphConflict, type GraphConnection, type GraphRoute, type GraphTurnout, type GraphWalk, type ImportedPart, type IndustryLabelMode, type IndustrySpot, JOINT_SNAP_INCHES, type LanePinch, MAIN2_TRACK_ID, MAIN_TRACK_ID, type ModuleFeatures, type ModuleFootprint, type ModuleFootprintInput, type ModuleGeometryInput, type ModuleSchematicDoc, type ModuleTrackRow, N_CAR_LENGTH_INCHES, N_SCALE_RATIO, type OccupiedSpan, type OutlineFace, PINCH_EASE_INCHES, type PartAngle, type PartDimension, type PartEnd, type PartExtent, type PartGeometry, type PartJoint, type PartJointRole, type PartSegment, type PlacedJoint, RAIL_GAUGE_INCHES, type ReturnLoopGeometry, type ReturnLoopShape, type SchematicBlock, type SchematicControlPoint, type SchematicCrossing, type SchematicEndplate, type SchematicEndplateTrack, type SchematicIndustry, type SchematicSection, type SchematicSignal, type SchematicTrack, type SchematicTurnout, type SectionAdjacency, type SectionEnd, type SectionEndAssessment, type SectionFootprint, type SectionJointAssessment, type SectionRelativePos, type SignalFacing, type SignalSide, type SpanOverhang, type StoredTrackPart, TIE_HALF_LENGTH_INCHES, TURNOUT_LEAD_INCHES_PER_FROG, type TrackConfig, type TrackGraph, type TrackPart, type TrackPiece, type TrackRole, type TurnoutClosure, type TurnoutKind, type UsableCapacity, WHOLE_MODULE_SECTION_ID, asModuleSchematic, assessSectionEnd, assessSectionJoint, benchworkBand, benchworkOutline, buildCrossover, buildPassingSiding, buildTrackGraph, buildTransition, carCapacity, checkEndplateWidth, clearancePointPastFrogInches, crossoverPinches, deriveEndplatePoses, divergeSideForHand, docToState, emptyEditorState, endplateCentreOffsetInches, endplateEdgePose, endplateFaceSegments, endplateLead, endplateTrackOffsetInches, endplateWidthInches, flexPartFor, flexParts, flexPieces, flexUsage, frogCasting, frogNumberFromName, fromSectionRelative, geometryTurnDegrees, hasNoFarEndplate, importedPartToTrackPart, inchesToScaleFeet, isLoopDoc, isTransitionTurnout, laneOffsetAt, leadInchesForSize, maxFlexPieceInches, mergeImportedParts, mergeStoredParts, moduleCenterline, moduleFeatures, moduleFootprint, moduleLengthFromSections, moduleSections, nextId, parseXtpLibrary, partExtent, partExtentForSize, partGeometry, partGeometryGap, partOutlineAtFrog, partsPlaceable, pastFrogInchesForSize, pathLengthInches, placedJoints, poseNeedsManual, poseOverridesFromDoc, remapPos, resizeFlexPiece, returnLoop, sampleBenchworkOutline, samplePartSegments, samplePath, scaleFeetToInches, sectionAdjacency, sectionBand, sectionBreaksFromSections, sectionComponents, sectionFootprints, sectionNeighbours, sectionSpans, sectionSpansOrWhole, sectionedCenterline, sectionedEndPose, sliceCenterline, spanOverhang, stateToDoc, storedPartToTrackPart, toSectionRelative, trackMeetsEndplateIssues, trackPart, trackPath, turnoutClosure, turnoutFacing, turnoutOccupiedSpan, turnoutPartForSize, usableCapacity, walkTrackGraph };
2985
+ export { ATLAS_CODE55_N, BUILT_IN_TRACK_PARTS, type BenchworkPoint, type BranchConnector, CLEARANCE_SPACING_INCHES, CODE55_RAIL_HEIGHT_INCHES, DEFAULT_FLEX_PART_ID, type DimensionSource, type DrawCrossing, type DrawCrossover, type DrawIndustry, type DrawSignal, type DrawTrack, type DrawTurnout, ENDPLATE_FASCIA_CLEAR_INCHES, ENDPLATE_LEAD_INCHES, type EditorBranch, type EditorControlPoint, type EditorCpSignal, type EditorCrossing, type EditorIndustry, type EditorState, type EditorTrack, type EditorTurnout, type EndplateBConfig, type EndplateEdge, type EndplateEdgePose, type EndplatePose, type EndplateTrackIssue, type EndplateWidthIssue, FAST_TRACKS_N_ME55, FAST_TRACKS_N_ME55_CROSSOVERS, FLEX_TRACK_PARTS, FREEMO_ENDPLATE_TRACK_FASCIA_CLEARANCE_INCHES, FREEMO_ENDPLATE_WIDTH_MIN_INCHES, FREEMO_ENDPLATE_WIDTH_RECOMMENDED_INCHES, FREEMO_TRACK_SPACING_INCHES, type FlexPiece, type FlexPieceEnd, type FrogCasting, type GeometryType, type GraphAnchor, type GraphConflict, type GraphConnection, type GraphDocInput, type GraphDocResult, type GraphRoute, type GraphTurnout, type GraphWalk, type ImportedPart, type IndustryLabelMode, type IndustrySpot, JOINT_SNAP_INCHES, type LanePinch, MAIN2_TRACK_ID, MAIN_TRACK_ID, type ModuleFeatures, type ModuleFootprint, type ModuleFootprintInput, type ModuleGeometryInput, type ModuleSchematicDoc, type ModuleTrackRow, N_CAR_LENGTH_INCHES, N_SCALE_RATIO, type OccupiedSpan, type OutlineFace, PINCH_EASE_INCHES, type PartAngle, type PartDimension, type PartEnd, type PartExtent, type PartGeometry, type PartJoint, type PartJointRole, type PartSegment, type PieceSnap, type PlacedJoint, RAIL_GAUGE_INCHES, type ResolvedAnchor, type ReturnLoopGeometry, type ReturnLoopShape, type RouteSpan, type SchematicBlock, type SchematicControlPoint, type SchematicCrossing, type SchematicEndplate, type SchematicEndplateTrack, type SchematicIndustry, type SchematicSection, type SchematicSignal, type SchematicTrack, type SchematicTurnout, type SectionAdjacency, type SectionEnd, type SectionEndAssessment, type SectionFootprint, type SectionJointAssessment, type SectionRelativePos, type SignalFacing, type SignalSide, type SpanOverhang, type StoredTrackPart, TIE_HALF_LENGTH_INCHES, TURNOUT_LEAD_INCHES_PER_FROG, type TrackConfig, type TrackGraph, type TrackPart, type TrackPiece, type TrackRole, type TurnoutClosure, type TurnoutKind, type UsableCapacity, WHOLE_MODULE_SECTION_ID, asModuleSchematic, assessSectionEnd, assessSectionJoint, benchworkBand, benchworkOutline, buildCrossover, buildPassingSiding, buildTrackGraph, buildTransition, carCapacity, checkEndplateWidth, clearancePointPastFrogInches, crossoverPinches, deriveEndplatePoses, deriveGraphDoc, divergeSideForHand, docToState, emptyEditorState, endplateCentreOffsetInches, endplateEdgePose, endplateFaceSegments, endplateLead, endplateTrackOffsetInches, endplateWidthInches, flexPartFor, flexParts, flexPieces, flexUsage, frogCasting, frogNumberFromName, fromSectionRelative, geometryTurnDegrees, graphToDoc, hasNoFarEndplate, importedPartToTrackPart, inchesToScaleFeet, isLoopDoc, isTransitionTurnout, laneOffsetAt, leadInchesForSize, maxFlexPieceInches, mergeImportedParts, mergeStoredParts, moduleCenterline, moduleFeatures, moduleFootprint, moduleLengthFromSections, moduleSections, nextId, parseXtpLibrary, partExtent, partExtentForSize, partGeometry, partGeometryGap, partOutlineAtFrog, partsPlaceable, pastFrogInchesForSize, pathLengthInches, pieceRoutePaths, placedJoints, poseNeedsManual, poseOverridesFromDoc, remapPos, resizeFlexPiece, resolveGraphAnchor, returnLoop, sampleBenchworkOutline, samplePartSegments, samplePath, scaleFeetToInches, sectionAdjacency, sectionBand, sectionBreaksFromSections, sectionComponents, sectionFootprints, sectionNeighbours, sectionSpans, sectionSpansOrWhole, sectionedCenterline, sectionedEndPose, sliceCenterline, snapPiece, spanOverhang, stateToDoc, storedPartToTrackPart, toSectionRelative, trackMeetsEndplateIssues, trackPart, trackPath, turnoutClosure, turnoutFacing, turnoutOccupiedSpan, turnoutPartForSize, usableCapacity, walkTrackGraph };
package/dist/index.d.ts CHANGED
@@ -242,6 +242,9 @@ interface SchematicTurnout {
242
242
  interface SchematicSignal {
243
243
  id: string;
244
244
  pos: number;
245
+ /** Hold this signal by the piece it stands beside (ADR 0001). See
246
+ * {@link GraphAnchor} — a mast is planted next to a particular bit of rail. */
247
+ anchor?: GraphAnchor | null;
245
248
  /** Track the signal governs; absent = the primary main (lane 0). */
246
249
  track?: string;
247
250
  facing?: SignalFacing;
@@ -295,11 +298,40 @@ type IndustryLabelMode = "none" | "cars" | "inches";
295
298
  */
296
299
  /** One car-spot span of an industry on a track — an industry may have several
297
300
  * (a house track serving one customer across multiple spot tracks, #54). */
301
+ /**
302
+ * ⭐ A PLACE ON THE TRACK, HELD BY THE PIECE THAT IS THERE (ADR 0001).
303
+ *
304
+ * `pos` — inches from endplate A — is a fine way to *read* where something is
305
+ * and a poor way to *hold* it: it is a number about the whole module, so every
306
+ * edit anywhere upstream silently changes what it means. A car spot belongs to
307
+ * the rail it is beside. Anchored to the piece, moving that piece takes the
308
+ * industry with it and moving anything else leaves it alone, which is what an
309
+ * owner dragging track expects to happen.
310
+ *
311
+ * Present = `pos` / `fromPos` / `toPos` are DERIVED from this by
312
+ * {@link graphToDoc}. Absent = the authored numbers stand, exactly as before —
313
+ * nothing migrates (ADR 0001).
314
+ */
315
+ interface GraphAnchor {
316
+ /** The {@link TrackPiece} it sits on. */
317
+ piece: string;
318
+ /**
319
+ * Inches along that piece from its OWN origin end — joint `a` on flex, the
320
+ * throat on a turnout, and in every case the part's x=0.
321
+ *
322
+ * ⚠️ Deliberately NOT "from the end the walk came in by". Which end that is
323
+ * depends on where the module's endplate A happens to be, so the same spot
324
+ * beside the same rail would mean two different numbers on two modules.
325
+ */
326
+ atInches: number;
327
+ }
298
328
  interface IndustrySpot {
299
329
  track: string;
300
330
  fromPos: number;
301
331
  toPos: number;
302
332
  side?: SignalSide;
333
+ /** Hold this spot by the piece it is beside (ADR 0001). See {@link GraphAnchor}. */
334
+ anchor?: GraphAnchor | null;
303
335
  }
304
336
  interface SchematicIndustry {
305
337
  id: string;
@@ -312,6 +344,10 @@ interface SchematicIndustry {
312
344
  /** The primary car-spot span along `track`, inches from endplate A. */
313
345
  fromPos: number;
314
346
  toPos: number;
347
+ /** Hold this span by the piece it is beside (ADR 0001). See {@link GraphAnchor}.
348
+ * The span's LENGTH stays authored — a dock face is as long as it is built;
349
+ * only where it begins is read off the track. */
350
+ anchor?: GraphAnchor | null;
315
351
  /** Extra car-spot spans on other tracks — the industry's house-track spots. */
316
352
  spots?: IndustrySpot[];
317
353
  /** Which side of the track the building + label sit on. */
@@ -392,6 +428,27 @@ interface ModuleSchematicDoc {
392
428
  * shape; absent = derive it as a lane offset from Main 1. Physical view only
393
429
  * (#131). */
394
430
  main2Path?: BenchworkPoint[] | null;
431
+ /**
432
+ * ⭐⭐ THE AUTHORED TRACK, when this module was drawn as PIECES (ADR 0001).
433
+ *
434
+ * Present = this is the source, and `tracks`, `turnouts` and every anchored
435
+ * feature's position are DERIVED from it by {@link deriveGraphDoc} — they are
436
+ * still written into the document so that everything downstream, Free-
437
+ * Dispatcher included, reads an ordinary document and is never told which way
438
+ * the module was authored.
439
+ *
440
+ * Absent = the module is authored the way it always was, and nothing about it
441
+ * changes. No document is converted (ADR 0001): converting one would mean
442
+ * inventing the leads, frogs and radii nobody measured.
443
+ */
444
+ graph?: {
445
+ pieces: TrackPiece[];
446
+ /** Where the main starts: the joint endplate A's track arrives at. */
447
+ startAt: {
448
+ piece: string;
449
+ joint: string;
450
+ };
451
+ } | null;
395
452
  }
396
453
  /** A benchwork-outline vertex, module-local inches. The edge from this vertex
397
454
  * to the NEXT one is a straight line, unless `bulge` is set — then it's a
@@ -1122,6 +1179,8 @@ interface EditorIndustry {
1122
1179
  carTypes: string[];
1123
1180
  /** freemon_industries row (single source of truth), or null for a new one. */
1124
1181
  moduleIndustryId: number | null;
1182
+ /** The piece this span is held by (ADR 0001). See {@link GraphAnchor}. */
1183
+ anchor?: GraphAnchor | null;
1125
1184
  }
1126
1185
  /** A 3rd+ endplate — a branch/junction connection off the module (#170).
1127
1186
  * A module may have several (e.g. a set carrying a second railroad through:
@@ -1216,6 +1275,10 @@ interface EditorState {
1216
1275
  mainPath: BenchworkPoint[];
1217
1276
  /** Authored Main 2 centre-line (double-track only) — empty = lane offset (#131). */
1218
1277
  main2Path: BenchworkPoint[];
1278
+ /** ⭐ The track as PLACED PIECES (ADR 0001), when the owner drew it that way.
1279
+ * Absent = authored the way it always was, and nothing changes. See
1280
+ * {@link ModuleSchematicDoc.graph} and {@link deriveGraphDoc}. */
1281
+ graph?: ModuleSchematicDoc["graph"];
1219
1282
  }
1220
1283
  /** Build the empty editor state for a module of the given length. */
1221
1284
  declare function emptyEditorState(lengthInches: number): EditorState;
@@ -2360,6 +2423,27 @@ declare function partGeometryGap(part: TrackPart): string | null;
2360
2423
  * which you got.
2361
2424
  */
2362
2425
  declare function partGeometry(part: TrackPart, library?: TrackPart[]): PartGeometry | null;
2426
+ /**
2427
+ * The SHAPE of every route through a placed piece, in MODULE inches — the
2428
+ * polyline a renderer draws.
2429
+ *
2430
+ * ⭐ The shape belongs to the PART, so it lives here rather than in either app's
2431
+ * canvas. A diverging route drawn as a straight chord from throat to diverging
2432
+ * end cuts the corner the closure actually turns; every renderer would have to
2433
+ * rebuild that curve, and they would drift.
2434
+ *
2435
+ * ⚠️ Each polyline ENDS EXACTLY ON ITS JOINTS, by construction — the samples in
2436
+ * between follow the closure, but the ends are the joint positions themselves.
2437
+ * Track that stops short of its own joint would draw a gap at a connection that
2438
+ * the graph considers made.
2439
+ */
2440
+ declare function pieceRoutePaths(piece: TrackPiece, library?: TrackPart[]): {
2441
+ route: [string, string];
2442
+ points: {
2443
+ x: number;
2444
+ y: number;
2445
+ }[];
2446
+ }[];
2363
2447
  /** Every part that can be PLACED on a board today, and every one that can only
2364
2448
  * be named — with the reason. The gap list is the parts backlog. */
2365
2449
  declare function partsPlaceable(library?: TrackPart[]): {
@@ -2446,6 +2530,45 @@ declare function placedJoints(pieces: TrackPiece[], library?: TrackPart[]): Plac
2446
2530
  * part carrying three joints of its own.
2447
2531
  */
2448
2532
  declare function buildTrackGraph(pieces: TrackPiece[], library?: TrackPart[], snapInches?: number): TrackGraph;
2533
+ /** A piece as the route crosses it — what an anchored feature resolves against. */
2534
+ interface RouteSpan {
2535
+ piece: string;
2536
+ /** The joints the route entered and left this piece by. Tell you which way
2537
+ * round the piece's own measurements run against the route's. */
2538
+ entryJoint: string;
2539
+ exitJoint: string;
2540
+ /** Inches from endplate A at the entry joint, and at the one it left by. */
2541
+ fromPos: number;
2542
+ toPos: number;
2543
+ }
2544
+ /** A piece moved into place against an open joint. */
2545
+ interface PieceSnap {
2546
+ /** The piece, rotated and translated so the two joints coincide. */
2547
+ piece: TrackPiece;
2548
+ /** The moving joint's key, and the open joint it was brought onto. */
2549
+ from: string;
2550
+ to: string;
2551
+ }
2552
+ /**
2553
+ * Bring a piece being dragged onto the nearest OPEN joint of the others.
2554
+ *
2555
+ * ⛔ **ONLY OPEN JOINTS ARE CANDIDATES, and that is the ADR's standing rule
2556
+ * enforced where it can still be obeyed.** A joint already holding a connection
2557
+ * is not offered, so an owner cannot stack a third rail end on a joint at all —
2558
+ * rather than stacking it and having {@link buildTrackGraph} refuse all three
2559
+ * afterwards, with a piece silently outside the layout until they notice.
2560
+ *
2561
+ * The piece is ROTATED as well as moved: two ends meet when they are in the same
2562
+ * place and facing opposite ways, so the rotation falls out of the joint it is
2563
+ * brought to. That is what makes laying a curve out of straight pieces possible
2564
+ * without typing an angle.
2565
+ *
2566
+ * `withinInches` is a GRAB radius — how near counts as "meant it" — and is a
2567
+ * different thing from {@link JOINT_SNAP_INCHES}, which is how close two joints
2568
+ * must be to BE connected. This one is generous; that one is a hundredth of an
2569
+ * inch.
2570
+ */
2571
+ declare function snapPiece(moving: TrackPiece, others: TrackPiece[], library?: TrackPart[], withinInches?: number): PieceSnap | null;
2449
2572
  /** A route the walk found: a continuous run of pieces a train can travel. */
2450
2573
  interface GraphRoute {
2451
2574
  id: string;
@@ -2457,6 +2580,8 @@ interface GraphRoute {
2457
2580
  /** The turnout it runs back into. Set = a SIDING; null = it dead-ends. */
2458
2581
  endsAt: string | null;
2459
2582
  pieces: string[];
2583
+ /** The same pieces, each with where it starts and ends along the route. */
2584
+ spans: RouteSpan[];
2460
2585
  /** Furthest lateral offset reached — what gives a lane its side. */
2461
2586
  lateral: number;
2462
2587
  }
@@ -2488,6 +2613,94 @@ declare function walkTrackGraph(graph: TrackGraph, pieces: TrackPiece[], startAt
2488
2613
  piece: string;
2489
2614
  joint: string;
2490
2615
  }, library?: TrackPart[]): GraphWalk;
2616
+ /** Where an anchored feature turned out to be. */
2617
+ interface ResolvedAnchor {
2618
+ /** Inches from endplate A — the number the document and the dispatcher read. */
2619
+ pos: number;
2620
+ /** The route the anchored piece is part of. */
2621
+ routeId: string;
2622
+ /** The piece's own measuring direction runs BACKWARD along the route, so
2623
+ * increasing {@link GraphAnchor.atInches} moves toward endplate A. */
2624
+ reversed: boolean;
2625
+ }
2626
+ /**
2627
+ * Turn a {@link GraphAnchor} into a position along the module.
2628
+ *
2629
+ * The piece is measured from its own origin end, and the walk knows both ends
2630
+ * of it, so whichever end the route arrived by, the sum comes out the same.
2631
+ * Returns null when the anchored piece is not on any route — a spur nobody has
2632
+ * connected yet — which is a thing to report, not to guess at.
2633
+ */
2634
+ declare function resolveGraphAnchor(anchor: GraphAnchor, walk: GraphWalk, pieces: TrackPiece[], library?: TrackPart[]): ResolvedAnchor | null;
2635
+ /** What the graph cannot know, and therefore never invents. */
2636
+ interface GraphDocInput {
2637
+ /** Where the main begins: the joint endplate A's track arrives at. */
2638
+ startAt: {
2639
+ piece: string;
2640
+ joint: string;
2641
+ };
2642
+ /**
2643
+ * The rest of the document — module id, endplate identities, the benchwork,
2644
+ * industries and signals. Merged UNDERNEATH the derived keys, so the graph
2645
+ * wins on length, tracks and turnouts and on nothing else.
2646
+ *
2647
+ * ⏸️ Industries, signals and control points are PASSED THROUGH untouched.
2648
+ * Where they live in a graph is deliberately still open (ADR 0001 defers it
2649
+ * to persistence); carrying them is honest, re-deriving them would be a guess.
2650
+ */
2651
+ base?: Partial<ModuleSchematicDoc>;
2652
+ /**
2653
+ * Owner metadata for a run, keyed by the piece the run STARTS at — the piece
2654
+ * they select and name. Held here rather than on {@link TrackPiece} because
2655
+ * it describes the whole run, not the one piece.
2656
+ */
2657
+ meta?: Record<string, {
2658
+ trackName?: string;
2659
+ capacityFeet?: number | null;
2660
+ moduleTrackId?: number | null;
2661
+ }>;
2662
+ library?: TrackPart[];
2663
+ }
2664
+ interface GraphDocResult {
2665
+ doc: ModuleSchematicDoc;
2666
+ graph: TrackGraph;
2667
+ walk: GraphWalk;
2668
+ /** The walk's warnings, plus anything the emission itself had to leave out. */
2669
+ warnings: string[];
2670
+ }
2671
+ /**
2672
+ * Derive a `ModuleSchematicDoc` from placed pieces.
2673
+ *
2674
+ * ⭐ **NO HAND IS EMITTED.** A turnout's `kind` is left unset on purpose. The
2675
+ * graph knows which side the diverging route is on — it is where the piece IS —
2676
+ * so the lane carries the side and `moduleFeatures` keeps the lane it is given
2677
+ * (`resolveLane` only overrides the sign for a stated left/right). Emitting a
2678
+ * hand as well would be a second source for one fact, which is the ~120 lines
2679
+ * of reconciliation this model exists to remove.
2680
+ *
2681
+ * ⚠️ ONE MAIN. The walk starts at one endplate, so a double-track module's
2682
+ * second main is not reached — it surfaces in `warnings` as unreachable pieces
2683
+ * rather than vanishing quietly. Emitting Main 2 needs a second start point and
2684
+ * is not built yet.
2685
+ */
2686
+ declare function graphToDoc(pieces: TrackPiece[], input: GraphDocInput): GraphDocResult;
2687
+ /**
2688
+ * Bring a document up to date with the track its owner drew.
2689
+ *
2690
+ * A document with no `graph` is returned UNTOUCHED — that is the ADR's
2691
+ * no-migration rule in one line, and it is why every module authored before this
2692
+ * keeps behaving exactly as it did. With a graph, the tracks and turnouts are
2693
+ * re-read off the pieces and written back into the ordinary document keys, so
2694
+ * the module is stored in a form every existing reader already understands.
2695
+ *
2696
+ * ⭐ Owners' names, capacities and `module_tracks` links are carried across from
2697
+ * the tracks already in the document, keyed by id. Re-deriving must never cost
2698
+ * an owner the name they gave a siding.
2699
+ */
2700
+ declare function deriveGraphDoc(doc: ModuleSchematicDoc, library?: TrackPart[]): {
2701
+ doc: ModuleSchematicDoc;
2702
+ warnings: string[];
2703
+ };
2491
2704
  /** A frog casting's parts, in TURNOUT-LOCAL inches: `x` = distance past the
2492
2705
  * points, `y` = lateral offset from the through route. */
2493
2706
  interface FrogCasting {
@@ -2769,4 +2982,4 @@ declare function poseOverridesFromDoc(doc: ModuleSchematicDoc): Record<string, {
2769
2982
  heading: number;
2770
2983
  }>;
2771
2984
 
2772
- export { ATLAS_CODE55_N, BUILT_IN_TRACK_PARTS, type BenchworkPoint, type BranchConnector, CLEARANCE_SPACING_INCHES, CODE55_RAIL_HEIGHT_INCHES, DEFAULT_FLEX_PART_ID, type DimensionSource, type DrawCrossing, type DrawCrossover, type DrawIndustry, type DrawSignal, type DrawTrack, type DrawTurnout, ENDPLATE_FASCIA_CLEAR_INCHES, ENDPLATE_LEAD_INCHES, type EditorBranch, type EditorControlPoint, type EditorCpSignal, type EditorCrossing, type EditorIndustry, type EditorState, type EditorTrack, type EditorTurnout, type EndplateBConfig, type EndplateEdge, type EndplateEdgePose, type EndplatePose, type EndplateTrackIssue, type EndplateWidthIssue, FAST_TRACKS_N_ME55, FAST_TRACKS_N_ME55_CROSSOVERS, FLEX_TRACK_PARTS, FREEMO_ENDPLATE_TRACK_FASCIA_CLEARANCE_INCHES, FREEMO_ENDPLATE_WIDTH_MIN_INCHES, FREEMO_ENDPLATE_WIDTH_RECOMMENDED_INCHES, FREEMO_TRACK_SPACING_INCHES, type FlexPiece, type FlexPieceEnd, type FrogCasting, type GeometryType, type GraphConflict, type GraphConnection, type GraphRoute, type GraphTurnout, type GraphWalk, type ImportedPart, type IndustryLabelMode, type IndustrySpot, JOINT_SNAP_INCHES, type LanePinch, MAIN2_TRACK_ID, MAIN_TRACK_ID, type ModuleFeatures, type ModuleFootprint, type ModuleFootprintInput, type ModuleGeometryInput, type ModuleSchematicDoc, type ModuleTrackRow, N_CAR_LENGTH_INCHES, N_SCALE_RATIO, type OccupiedSpan, type OutlineFace, PINCH_EASE_INCHES, type PartAngle, type PartDimension, type PartEnd, type PartExtent, type PartGeometry, type PartJoint, type PartJointRole, type PartSegment, type PlacedJoint, RAIL_GAUGE_INCHES, type ReturnLoopGeometry, type ReturnLoopShape, type SchematicBlock, type SchematicControlPoint, type SchematicCrossing, type SchematicEndplate, type SchematicEndplateTrack, type SchematicIndustry, type SchematicSection, type SchematicSignal, type SchematicTrack, type SchematicTurnout, type SectionAdjacency, type SectionEnd, type SectionEndAssessment, type SectionFootprint, type SectionJointAssessment, type SectionRelativePos, type SignalFacing, type SignalSide, type SpanOverhang, type StoredTrackPart, TIE_HALF_LENGTH_INCHES, TURNOUT_LEAD_INCHES_PER_FROG, type TrackConfig, type TrackGraph, type TrackPart, type TrackPiece, type TrackRole, type TurnoutClosure, type TurnoutKind, type UsableCapacity, WHOLE_MODULE_SECTION_ID, asModuleSchematic, assessSectionEnd, assessSectionJoint, benchworkBand, benchworkOutline, buildCrossover, buildPassingSiding, buildTrackGraph, buildTransition, carCapacity, checkEndplateWidth, clearancePointPastFrogInches, crossoverPinches, deriveEndplatePoses, divergeSideForHand, docToState, emptyEditorState, endplateCentreOffsetInches, endplateEdgePose, endplateFaceSegments, endplateLead, endplateTrackOffsetInches, endplateWidthInches, flexPartFor, flexParts, flexPieces, flexUsage, frogCasting, frogNumberFromName, fromSectionRelative, geometryTurnDegrees, hasNoFarEndplate, importedPartToTrackPart, inchesToScaleFeet, isLoopDoc, isTransitionTurnout, laneOffsetAt, leadInchesForSize, maxFlexPieceInches, mergeImportedParts, mergeStoredParts, moduleCenterline, moduleFeatures, moduleFootprint, moduleLengthFromSections, moduleSections, nextId, parseXtpLibrary, partExtent, partExtentForSize, partGeometry, partGeometryGap, partOutlineAtFrog, partsPlaceable, pastFrogInchesForSize, pathLengthInches, placedJoints, poseNeedsManual, poseOverridesFromDoc, remapPos, resizeFlexPiece, returnLoop, sampleBenchworkOutline, samplePartSegments, samplePath, scaleFeetToInches, sectionAdjacency, sectionBand, sectionBreaksFromSections, sectionComponents, sectionFootprints, sectionNeighbours, sectionSpans, sectionSpansOrWhole, sectionedCenterline, sectionedEndPose, sliceCenterline, spanOverhang, stateToDoc, storedPartToTrackPart, toSectionRelative, trackMeetsEndplateIssues, trackPart, trackPath, turnoutClosure, turnoutFacing, turnoutOccupiedSpan, turnoutPartForSize, usableCapacity, walkTrackGraph };
2985
+ export { ATLAS_CODE55_N, BUILT_IN_TRACK_PARTS, type BenchworkPoint, type BranchConnector, CLEARANCE_SPACING_INCHES, CODE55_RAIL_HEIGHT_INCHES, DEFAULT_FLEX_PART_ID, type DimensionSource, type DrawCrossing, type DrawCrossover, type DrawIndustry, type DrawSignal, type DrawTrack, type DrawTurnout, ENDPLATE_FASCIA_CLEAR_INCHES, ENDPLATE_LEAD_INCHES, type EditorBranch, type EditorControlPoint, type EditorCpSignal, type EditorCrossing, type EditorIndustry, type EditorState, type EditorTrack, type EditorTurnout, type EndplateBConfig, type EndplateEdge, type EndplateEdgePose, type EndplatePose, type EndplateTrackIssue, type EndplateWidthIssue, FAST_TRACKS_N_ME55, FAST_TRACKS_N_ME55_CROSSOVERS, FLEX_TRACK_PARTS, FREEMO_ENDPLATE_TRACK_FASCIA_CLEARANCE_INCHES, FREEMO_ENDPLATE_WIDTH_MIN_INCHES, FREEMO_ENDPLATE_WIDTH_RECOMMENDED_INCHES, FREEMO_TRACK_SPACING_INCHES, type FlexPiece, type FlexPieceEnd, type FrogCasting, type GeometryType, type GraphAnchor, type GraphConflict, type GraphConnection, type GraphDocInput, type GraphDocResult, type GraphRoute, type GraphTurnout, type GraphWalk, type ImportedPart, type IndustryLabelMode, type IndustrySpot, JOINT_SNAP_INCHES, type LanePinch, MAIN2_TRACK_ID, MAIN_TRACK_ID, type ModuleFeatures, type ModuleFootprint, type ModuleFootprintInput, type ModuleGeometryInput, type ModuleSchematicDoc, type ModuleTrackRow, N_CAR_LENGTH_INCHES, N_SCALE_RATIO, type OccupiedSpan, type OutlineFace, PINCH_EASE_INCHES, type PartAngle, type PartDimension, type PartEnd, type PartExtent, type PartGeometry, type PartJoint, type PartJointRole, type PartSegment, type PieceSnap, type PlacedJoint, RAIL_GAUGE_INCHES, type ResolvedAnchor, type ReturnLoopGeometry, type ReturnLoopShape, type RouteSpan, type SchematicBlock, type SchematicControlPoint, type SchematicCrossing, type SchematicEndplate, type SchematicEndplateTrack, type SchematicIndustry, type SchematicSection, type SchematicSignal, type SchematicTrack, type SchematicTurnout, type SectionAdjacency, type SectionEnd, type SectionEndAssessment, type SectionFootprint, type SectionJointAssessment, type SectionRelativePos, type SignalFacing, type SignalSide, type SpanOverhang, type StoredTrackPart, TIE_HALF_LENGTH_INCHES, TURNOUT_LEAD_INCHES_PER_FROG, type TrackConfig, type TrackGraph, type TrackPart, type TrackPiece, type TrackRole, type TurnoutClosure, type TurnoutKind, type UsableCapacity, WHOLE_MODULE_SECTION_ID, asModuleSchematic, assessSectionEnd, assessSectionJoint, benchworkBand, benchworkOutline, buildCrossover, buildPassingSiding, buildTrackGraph, buildTransition, carCapacity, checkEndplateWidth, clearancePointPastFrogInches, crossoverPinches, deriveEndplatePoses, deriveGraphDoc, divergeSideForHand, docToState, emptyEditorState, endplateCentreOffsetInches, endplateEdgePose, endplateFaceSegments, endplateLead, endplateTrackOffsetInches, endplateWidthInches, flexPartFor, flexParts, flexPieces, flexUsage, frogCasting, frogNumberFromName, fromSectionRelative, geometryTurnDegrees, graphToDoc, hasNoFarEndplate, importedPartToTrackPart, inchesToScaleFeet, isLoopDoc, isTransitionTurnout, laneOffsetAt, leadInchesForSize, maxFlexPieceInches, mergeImportedParts, mergeStoredParts, moduleCenterline, moduleFeatures, moduleFootprint, moduleLengthFromSections, moduleSections, nextId, parseXtpLibrary, partExtent, partExtentForSize, partGeometry, partGeometryGap, partOutlineAtFrog, partsPlaceable, pastFrogInchesForSize, pathLengthInches, pieceRoutePaths, placedJoints, poseNeedsManual, poseOverridesFromDoc, remapPos, resizeFlexPiece, resolveGraphAnchor, returnLoop, sampleBenchworkOutline, samplePartSegments, samplePath, scaleFeetToInches, sectionAdjacency, sectionBand, sectionBreaksFromSections, sectionComponents, sectionFootprints, sectionNeighbours, sectionSpans, sectionSpansOrWhole, sectionedCenterline, sectionedEndPose, sliceCenterline, snapPiece, spanOverhang, stateToDoc, storedPartToTrackPart, toSectionRelative, trackMeetsEndplateIssues, trackPart, trackPath, turnoutClosure, turnoutFacing, turnoutOccupiedSpan, turnoutPartForSize, usableCapacity, walkTrackGraph };