@willcgage/module-schematic 0.74.0 → 0.76.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
@@ -131,6 +131,10 @@ interface SchematicTrack {
131
131
  * product's maximum piece length. Present = these are the ONLY joints, so an
132
132
  * owner's deliberate cut survives a change elsewhere on the module. */
133
133
  flexCuts?: number[] | null;
134
+ /** The owner's MEASURED usable length, real inches (#20) — for what the
135
+ * drawing can't know: a bumper post short of the drawn end, a structure
136
+ * fouling the track. Absent = derive it from the clearance points (#19). */
137
+ measuredUsableInches?: number | null;
134
138
  }
135
139
  interface SchematicTurnout {
136
140
  id: string;
@@ -362,6 +366,35 @@ interface SchematicSection {
362
366
  geometryDegrees?: number | null;
363
367
  /** Lateral jog, for offset sections. */
364
368
  geometryOffsetInches?: number | null;
369
+ /** This board's WEST end (the A side) — see {@link SectionEnd}. */
370
+ endA?: SectionEnd | null;
371
+ /** This board's EAST end (the B side). */
372
+ endB?: SectionEnd | null;
373
+ }
374
+ /**
375
+ * One end of a board (#130).
376
+ *
377
+ * Owner's question was *"how would I update my section joints to be endplates
378
+ * from within MR?"* — and until now nothing could say it: a section had a
379
+ * length, a shape and an outline, but nothing about its ends.
380
+ *
381
+ * ⚠️ **There is deliberately no "this is an endplate" flag.** An owner could
382
+ * tick it wrongly and the registry would then tell Free-Dispatcher that two
383
+ * boards will physically mate when they won't. The geometry decides — describe
384
+ * the end and {@link assessSectionEnd} works out whether it conforms.
385
+ *
386
+ * Absent means **not described**, which is an ordinary internal joint, not a
387
+ * failing endplate: the standard exempts internal boundaries from the
388
+ * end-interface rules (#96).
389
+ */
390
+ interface SectionEnd {
391
+ /** What this end presents. `"none"` = a closed end (a bumper). */
392
+ config?: TrackConfig | "none" | null;
393
+ /** Face width, inches. Absent = the recommended default. */
394
+ widthInches?: number | null;
395
+ /** Main 1's signed distance from the face centre — the standard's own
396
+ * framing, same as an endplate's (see {@link endplateTrackOffsetInches}). */
397
+ trackOffsetInches?: number | null;
365
398
  }
366
399
  /** The authored benchwork outline, or null when a module hasn't drawn one
367
400
  * (renderers then fall back to a band derived from the endplate widths). A
@@ -771,6 +804,130 @@ declare function scaleFeetToInches(feet: number, ratio?: number): number;
771
804
  * ~3.0″; ~3.3″ over the couplers — the real spacing a cut of cars takes. The
772
805
  * single constant every repo reads so a track's car count matches everywhere. */
773
806
  declare const N_CAR_LENGTH_INCHES = 3.3;
807
+ /**
808
+ * How far the two routes must separate before a car on one clears equipment on
809
+ * the other — the CLEARANCE POINT's defining distance (#19).
810
+ *
811
+ * ⭐ Deliberately the **Free-moN track spacing**, not a second number. §2.0 uses
812
+ * 1.125″ as the distance at which two parallel tracks coexist, so "a car here
813
+ * clears a car there" is the same statement the standard already makes. It's
814
+ * 15 scale feet centre to centre, inside the prototype's usual 13–15 ft range.
815
+ *
816
+ * The alternative — a separate prototype constant — would leave the app holding
817
+ * two spacings that disagree about what "clear" means.
818
+ */
819
+ declare const CLEARANCE_SPACING_INCHES = 1.125;
820
+ /**
821
+ * How far past the FROG a turnout's diverging route reaches the clearance point
822
+ * (#19) — the point from which usable capacity is measured.
823
+ *
824
+ * Not a measurement and not a guess: it's solved from the closure the drawing
825
+ * already uses, by asking where `offsetAt` first reaches
826
+ * {@link CLEARANCE_SPACING_INCHES}. So it follows the part's real lead, and a
827
+ * better-measured part moves it automatically.
828
+ *
829
+ * Returns the distance PAST THE FROG because `pos` means the frog (#132) — add
830
+ * it to the turnout's position, in the direction the turnout faces.
831
+ */
832
+ declare function clearancePointPastFrogInches(size: number, library?: TrackPart[], clearanceInches?: number): number;
833
+ /** A track's usable capacity, measured to the clearance-point standard (#19). */
834
+ interface UsableCapacity {
835
+ /** The stretch a car can actually stand on, inches along the run. */
836
+ fromPos: number;
837
+ toPos: number;
838
+ /** Its length — what capacity is computed from. */
839
+ usableInches: number;
840
+ /** The run's full drawn length, for comparison. */
841
+ drawnInches: number;
842
+ /** How much each end gives up to its governing turnout's clearance point. */
843
+ givenUpInches: number;
844
+ /** Cars that fit in the usable length. */
845
+ cars: number;
846
+ /** Usable length as scale feet. */
847
+ scaleFeet: number;
848
+ }
849
+ /**
850
+ * A track's USABLE capacity — measured from the governing turnout's clearance
851
+ * point, not from the rail ends (#19/#20).
852
+ *
853
+ * A spur runs clearance point → end of track; a siding, clearance point →
854
+ * clearance point. The difference is not small: FMN-0040's 70″ passing siding
855
+ * has two #7s on it, and loses 5.4″ at each end — 21 cars drawn, 17 usable.
856
+ *
857
+ * `measuredUsableInches` is the owner's override for what the drawing can't
858
+ * know — a bumper post short of the drawn end, a structure fouling the track.
859
+ * Given, it wins outright and the clearance points aren't applied to it: they
860
+ * measured the usable length itself.
861
+ */
862
+ declare function usableCapacity(input: {
863
+ fromPos: number;
864
+ toPos: number;
865
+ /** Turnouts that DIVERGE ONTO this track — the ones that govern its ends. */
866
+ governing?: {
867
+ pos: number;
868
+ size?: number | null;
869
+ }[] | null;
870
+ /** The owner's measured usable length, inches. Wins when present. */
871
+ measuredUsableInches?: number | null;
872
+ library?: TrackPart[];
873
+ carLengthInches?: number;
874
+ }): UsableCapacity;
875
+ /** What a board's end is, once the geometry has been asked (#130). */
876
+ interface SectionEndAssessment {
877
+ /** Has the owner described this end at all? Undescribed is an ordinary
878
+ * internal joint — exempt from the end-interface rules (#96) — not a failure. */
879
+ described: boolean;
880
+ /** Does it meet the standard's end-interface rules? Only a described end can. */
881
+ conforming: boolean;
882
+ /** Why it doesn't, when it doesn't. Empty for a conforming or undescribed end. */
883
+ issues: EndplateWidthIssue[];
884
+ /** The resolved values the assessment was made against. */
885
+ config: TrackConfig | "none";
886
+ widthInches: number;
887
+ trackOffsetInches: number;
888
+ }
889
+ /**
890
+ * Is this end of a board a standard endplate? (#130)
891
+ *
892
+ * ⭐ **Derived, never declared.** The issue is emphatic and the reason is good:
893
+ * a checkbox could be ticked wrongly, and the registry would then promise
894
+ * Free-Dispatcher a mating surface that doesn't exist. So the answer comes from
895
+ * the same rules {@link checkEndplateWidth} already applies to a module's own
896
+ * plates — §1.1's 12″ minimum, and §2.0's 4″ fascia clearance and 1.125″ pair.
897
+ *
898
+ * Two of the standard's conditions aren't checked because they can't be false
899
+ * here: track crosses the face **perpendicular** and a double end's two tracks
900
+ * sit **1.125″ apart** are both true by construction in this model. Inventing
901
+ * fields for them would be inventing ways to be wrong.
902
+ *
903
+ * An end with no config is **undescribed**, which is the ordinary case — most
904
+ * joints inside a module are just joints.
905
+ */
906
+ declare function assessSectionEnd(end: SectionEnd | null | undefined, opts?: {
907
+ main2Below?: boolean;
908
+ }): SectionEndAssessment;
909
+ /** Two board ends meeting at a joint, and whether that joint is a standard
910
+ * interface (#130). */
911
+ interface SectionJointAssessment {
912
+ west: SectionEndAssessment;
913
+ east: SectionEndAssessment;
914
+ /** Both ends conform AND present the same number of tracks — so these two
915
+ * boards could be separated and each used against any other module. */
916
+ standardInterface: boolean;
917
+ /** Plain-language reason it isn't one, or null when it is. */
918
+ reason: string | null;
919
+ }
920
+ /**
921
+ * Assess a joint — the two board ends that meet there (#130).
922
+ *
923
+ * ⚠️ Track COUNT is the only compatibility rule. Two conforming ends of
924
+ * differing face WIDTH still mate: the standard lets plates differ in width and
925
+ * be offset, so long as the track lines up. That's a deliberate non-check — see
926
+ * the note on endplate width authoring.
927
+ */
928
+ declare function assessSectionJoint(west: SectionEnd | null | undefined, east: SectionEnd | null | undefined, opts?: {
929
+ main2Below?: boolean;
930
+ }): SectionJointAssessment;
774
931
  /** How much of a car-spot span has no rail under it (#194). */
775
932
  interface SpanOverhang {
776
933
  /** Inches the span runs past the track's near end (0 = it starts on track). */
@@ -822,6 +979,8 @@ interface EditorTrack {
822
979
  /** Authored 2-D path (module-local inches) — a bent/rotated spur's real
823
980
  * shape. Absent = derive from the main + lane (#2d-track). */
824
981
  path?: BenchworkPoint[];
982
+ /** Measured usable length, real inches (#20). Absent = derived (#19). */
983
+ measuredUsableInches?: number;
825
984
  }
826
985
  /** A module_tracks row as loaded for the editor. */
827
986
  interface ModuleTrackRow {
@@ -2051,4 +2210,4 @@ declare function poseOverridesFromDoc(doc: ModuleSchematicDoc): Record<string, {
2051
2210
  heading: number;
2052
2211
  }>;
2053
2212
 
2054
- export { ATLAS_CODE55_N, BUILT_IN_TRACK_PARTS, type BenchworkPoint, type BranchConnector, 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 EndplatePose, type EndplateTrackIssue, type EndplateWidthIssue, 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 ImportedPart, type IndustryLabelMode, type IndustrySpot, 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, type PartAngle, type PartDimension, type PartEnd, type PartExtent, type PartSegment, 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 SectionFootprint, type SectionRelativePos, type SignalFacing, type SignalSide, type SpanOverhang, type StoredTrackPart, TIE_HALF_LENGTH_INCHES, TURNOUT_LEAD_INCHES_PER_FROG, type TrackConfig, type TrackPart, type TrackRole, type TurnoutClosure, type TurnoutKind, WHOLE_MODULE_SECTION_ID, asModuleSchematic, benchworkBand, benchworkOutline, buildCrossover, buildPassingSiding, buildTransition, carCapacity, checkEndplateWidth, deriveEndplatePoses, divergeSideForHand, docToState, emptyEditorState, endplateCentreOffsetInches, endplateFaceSegments, endplateLead, endplateTrackOffsetInches, endplateWidthInches, flexPartFor, flexParts, flexPieces, flexUsage, frogCasting, frogNumberFromName, fromSectionRelative, geometryTurnDegrees, hasNoFarEndplate, importedPartToTrackPart, inchesToScaleFeet, isLoopDoc, isTransitionTurnout, leadInchesForSize, maxFlexPieceInches, mergeImportedParts, mergeStoredParts, moduleCenterline, moduleFeatures, moduleFootprint, moduleLengthFromSections, moduleSections, nextId, parseXtpLibrary, partExtent, partExtentForSize, partOutlineAtFrog, pastFrogInchesForSize, pathLengthInches, 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 };
2213
+ 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 EndplatePose, type EndplateTrackIssue, type EndplateWidthIssue, 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 ImportedPart, type IndustryLabelMode, type IndustrySpot, 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, type PartAngle, type PartDimension, type PartEnd, type PartExtent, type PartSegment, 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 TrackPart, type TrackRole, type TurnoutClosure, type TurnoutKind, type UsableCapacity, WHOLE_MODULE_SECTION_ID, asModuleSchematic, assessSectionEnd, assessSectionJoint, benchworkBand, benchworkOutline, buildCrossover, buildPassingSiding, buildTransition, carCapacity, checkEndplateWidth, clearancePointPastFrogInches, deriveEndplatePoses, divergeSideForHand, docToState, emptyEditorState, endplateCentreOffsetInches, endplateFaceSegments, endplateLead, endplateTrackOffsetInches, endplateWidthInches, flexPartFor, flexParts, flexPieces, flexUsage, frogCasting, frogNumberFromName, fromSectionRelative, geometryTurnDegrees, hasNoFarEndplate, importedPartToTrackPart, inchesToScaleFeet, isLoopDoc, isTransitionTurnout, leadInchesForSize, maxFlexPieceInches, mergeImportedParts, mergeStoredParts, moduleCenterline, moduleFeatures, moduleFootprint, moduleLengthFromSections, moduleSections, nextId, parseXtpLibrary, partExtent, partExtentForSize, partOutlineAtFrog, pastFrogInchesForSize, pathLengthInches, 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 };
package/dist/index.d.ts CHANGED
@@ -131,6 +131,10 @@ interface SchematicTrack {
131
131
  * product's maximum piece length. Present = these are the ONLY joints, so an
132
132
  * owner's deliberate cut survives a change elsewhere on the module. */
133
133
  flexCuts?: number[] | null;
134
+ /** The owner's MEASURED usable length, real inches (#20) — for what the
135
+ * drawing can't know: a bumper post short of the drawn end, a structure
136
+ * fouling the track. Absent = derive it from the clearance points (#19). */
137
+ measuredUsableInches?: number | null;
134
138
  }
135
139
  interface SchematicTurnout {
136
140
  id: string;
@@ -362,6 +366,35 @@ interface SchematicSection {
362
366
  geometryDegrees?: number | null;
363
367
  /** Lateral jog, for offset sections. */
364
368
  geometryOffsetInches?: number | null;
369
+ /** This board's WEST end (the A side) — see {@link SectionEnd}. */
370
+ endA?: SectionEnd | null;
371
+ /** This board's EAST end (the B side). */
372
+ endB?: SectionEnd | null;
373
+ }
374
+ /**
375
+ * One end of a board (#130).
376
+ *
377
+ * Owner's question was *"how would I update my section joints to be endplates
378
+ * from within MR?"* — and until now nothing could say it: a section had a
379
+ * length, a shape and an outline, but nothing about its ends.
380
+ *
381
+ * ⚠️ **There is deliberately no "this is an endplate" flag.** An owner could
382
+ * tick it wrongly and the registry would then tell Free-Dispatcher that two
383
+ * boards will physically mate when they won't. The geometry decides — describe
384
+ * the end and {@link assessSectionEnd} works out whether it conforms.
385
+ *
386
+ * Absent means **not described**, which is an ordinary internal joint, not a
387
+ * failing endplate: the standard exempts internal boundaries from the
388
+ * end-interface rules (#96).
389
+ */
390
+ interface SectionEnd {
391
+ /** What this end presents. `"none"` = a closed end (a bumper). */
392
+ config?: TrackConfig | "none" | null;
393
+ /** Face width, inches. Absent = the recommended default. */
394
+ widthInches?: number | null;
395
+ /** Main 1's signed distance from the face centre — the standard's own
396
+ * framing, same as an endplate's (see {@link endplateTrackOffsetInches}). */
397
+ trackOffsetInches?: number | null;
365
398
  }
366
399
  /** The authored benchwork outline, or null when a module hasn't drawn one
367
400
  * (renderers then fall back to a band derived from the endplate widths). A
@@ -771,6 +804,130 @@ declare function scaleFeetToInches(feet: number, ratio?: number): number;
771
804
  * ~3.0″; ~3.3″ over the couplers — the real spacing a cut of cars takes. The
772
805
  * single constant every repo reads so a track's car count matches everywhere. */
773
806
  declare const N_CAR_LENGTH_INCHES = 3.3;
807
+ /**
808
+ * How far the two routes must separate before a car on one clears equipment on
809
+ * the other — the CLEARANCE POINT's defining distance (#19).
810
+ *
811
+ * ⭐ Deliberately the **Free-moN track spacing**, not a second number. §2.0 uses
812
+ * 1.125″ as the distance at which two parallel tracks coexist, so "a car here
813
+ * clears a car there" is the same statement the standard already makes. It's
814
+ * 15 scale feet centre to centre, inside the prototype's usual 13–15 ft range.
815
+ *
816
+ * The alternative — a separate prototype constant — would leave the app holding
817
+ * two spacings that disagree about what "clear" means.
818
+ */
819
+ declare const CLEARANCE_SPACING_INCHES = 1.125;
820
+ /**
821
+ * How far past the FROG a turnout's diverging route reaches the clearance point
822
+ * (#19) — the point from which usable capacity is measured.
823
+ *
824
+ * Not a measurement and not a guess: it's solved from the closure the drawing
825
+ * already uses, by asking where `offsetAt` first reaches
826
+ * {@link CLEARANCE_SPACING_INCHES}. So it follows the part's real lead, and a
827
+ * better-measured part moves it automatically.
828
+ *
829
+ * Returns the distance PAST THE FROG because `pos` means the frog (#132) — add
830
+ * it to the turnout's position, in the direction the turnout faces.
831
+ */
832
+ declare function clearancePointPastFrogInches(size: number, library?: TrackPart[], clearanceInches?: number): number;
833
+ /** A track's usable capacity, measured to the clearance-point standard (#19). */
834
+ interface UsableCapacity {
835
+ /** The stretch a car can actually stand on, inches along the run. */
836
+ fromPos: number;
837
+ toPos: number;
838
+ /** Its length — what capacity is computed from. */
839
+ usableInches: number;
840
+ /** The run's full drawn length, for comparison. */
841
+ drawnInches: number;
842
+ /** How much each end gives up to its governing turnout's clearance point. */
843
+ givenUpInches: number;
844
+ /** Cars that fit in the usable length. */
845
+ cars: number;
846
+ /** Usable length as scale feet. */
847
+ scaleFeet: number;
848
+ }
849
+ /**
850
+ * A track's USABLE capacity — measured from the governing turnout's clearance
851
+ * point, not from the rail ends (#19/#20).
852
+ *
853
+ * A spur runs clearance point → end of track; a siding, clearance point →
854
+ * clearance point. The difference is not small: FMN-0040's 70″ passing siding
855
+ * has two #7s on it, and loses 5.4″ at each end — 21 cars drawn, 17 usable.
856
+ *
857
+ * `measuredUsableInches` is the owner's override for what the drawing can't
858
+ * know — a bumper post short of the drawn end, a structure fouling the track.
859
+ * Given, it wins outright and the clearance points aren't applied to it: they
860
+ * measured the usable length itself.
861
+ */
862
+ declare function usableCapacity(input: {
863
+ fromPos: number;
864
+ toPos: number;
865
+ /** Turnouts that DIVERGE ONTO this track — the ones that govern its ends. */
866
+ governing?: {
867
+ pos: number;
868
+ size?: number | null;
869
+ }[] | null;
870
+ /** The owner's measured usable length, inches. Wins when present. */
871
+ measuredUsableInches?: number | null;
872
+ library?: TrackPart[];
873
+ carLengthInches?: number;
874
+ }): UsableCapacity;
875
+ /** What a board's end is, once the geometry has been asked (#130). */
876
+ interface SectionEndAssessment {
877
+ /** Has the owner described this end at all? Undescribed is an ordinary
878
+ * internal joint — exempt from the end-interface rules (#96) — not a failure. */
879
+ described: boolean;
880
+ /** Does it meet the standard's end-interface rules? Only a described end can. */
881
+ conforming: boolean;
882
+ /** Why it doesn't, when it doesn't. Empty for a conforming or undescribed end. */
883
+ issues: EndplateWidthIssue[];
884
+ /** The resolved values the assessment was made against. */
885
+ config: TrackConfig | "none";
886
+ widthInches: number;
887
+ trackOffsetInches: number;
888
+ }
889
+ /**
890
+ * Is this end of a board a standard endplate? (#130)
891
+ *
892
+ * ⭐ **Derived, never declared.** The issue is emphatic and the reason is good:
893
+ * a checkbox could be ticked wrongly, and the registry would then promise
894
+ * Free-Dispatcher a mating surface that doesn't exist. So the answer comes from
895
+ * the same rules {@link checkEndplateWidth} already applies to a module's own
896
+ * plates — §1.1's 12″ minimum, and §2.0's 4″ fascia clearance and 1.125″ pair.
897
+ *
898
+ * Two of the standard's conditions aren't checked because they can't be false
899
+ * here: track crosses the face **perpendicular** and a double end's two tracks
900
+ * sit **1.125″ apart** are both true by construction in this model. Inventing
901
+ * fields for them would be inventing ways to be wrong.
902
+ *
903
+ * An end with no config is **undescribed**, which is the ordinary case — most
904
+ * joints inside a module are just joints.
905
+ */
906
+ declare function assessSectionEnd(end: SectionEnd | null | undefined, opts?: {
907
+ main2Below?: boolean;
908
+ }): SectionEndAssessment;
909
+ /** Two board ends meeting at a joint, and whether that joint is a standard
910
+ * interface (#130). */
911
+ interface SectionJointAssessment {
912
+ west: SectionEndAssessment;
913
+ east: SectionEndAssessment;
914
+ /** Both ends conform AND present the same number of tracks — so these two
915
+ * boards could be separated and each used against any other module. */
916
+ standardInterface: boolean;
917
+ /** Plain-language reason it isn't one, or null when it is. */
918
+ reason: string | null;
919
+ }
920
+ /**
921
+ * Assess a joint — the two board ends that meet there (#130).
922
+ *
923
+ * ⚠️ Track COUNT is the only compatibility rule. Two conforming ends of
924
+ * differing face WIDTH still mate: the standard lets plates differ in width and
925
+ * be offset, so long as the track lines up. That's a deliberate non-check — see
926
+ * the note on endplate width authoring.
927
+ */
928
+ declare function assessSectionJoint(west: SectionEnd | null | undefined, east: SectionEnd | null | undefined, opts?: {
929
+ main2Below?: boolean;
930
+ }): SectionJointAssessment;
774
931
  /** How much of a car-spot span has no rail under it (#194). */
775
932
  interface SpanOverhang {
776
933
  /** Inches the span runs past the track's near end (0 = it starts on track). */
@@ -822,6 +979,8 @@ interface EditorTrack {
822
979
  /** Authored 2-D path (module-local inches) — a bent/rotated spur's real
823
980
  * shape. Absent = derive from the main + lane (#2d-track). */
824
981
  path?: BenchworkPoint[];
982
+ /** Measured usable length, real inches (#20). Absent = derived (#19). */
983
+ measuredUsableInches?: number;
825
984
  }
826
985
  /** A module_tracks row as loaded for the editor. */
827
986
  interface ModuleTrackRow {
@@ -2051,4 +2210,4 @@ declare function poseOverridesFromDoc(doc: ModuleSchematicDoc): Record<string, {
2051
2210
  heading: number;
2052
2211
  }>;
2053
2212
 
2054
- export { ATLAS_CODE55_N, BUILT_IN_TRACK_PARTS, type BenchworkPoint, type BranchConnector, 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 EndplatePose, type EndplateTrackIssue, type EndplateWidthIssue, 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 ImportedPart, type IndustryLabelMode, type IndustrySpot, 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, type PartAngle, type PartDimension, type PartEnd, type PartExtent, type PartSegment, 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 SectionFootprint, type SectionRelativePos, type SignalFacing, type SignalSide, type SpanOverhang, type StoredTrackPart, TIE_HALF_LENGTH_INCHES, TURNOUT_LEAD_INCHES_PER_FROG, type TrackConfig, type TrackPart, type TrackRole, type TurnoutClosure, type TurnoutKind, WHOLE_MODULE_SECTION_ID, asModuleSchematic, benchworkBand, benchworkOutline, buildCrossover, buildPassingSiding, buildTransition, carCapacity, checkEndplateWidth, deriveEndplatePoses, divergeSideForHand, docToState, emptyEditorState, endplateCentreOffsetInches, endplateFaceSegments, endplateLead, endplateTrackOffsetInches, endplateWidthInches, flexPartFor, flexParts, flexPieces, flexUsage, frogCasting, frogNumberFromName, fromSectionRelative, geometryTurnDegrees, hasNoFarEndplate, importedPartToTrackPart, inchesToScaleFeet, isLoopDoc, isTransitionTurnout, leadInchesForSize, maxFlexPieceInches, mergeImportedParts, mergeStoredParts, moduleCenterline, moduleFeatures, moduleFootprint, moduleLengthFromSections, moduleSections, nextId, parseXtpLibrary, partExtent, partExtentForSize, partOutlineAtFrog, pastFrogInchesForSize, pathLengthInches, 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 };
2213
+ 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 EndplatePose, type EndplateTrackIssue, type EndplateWidthIssue, 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 ImportedPart, type IndustryLabelMode, type IndustrySpot, 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, type PartAngle, type PartDimension, type PartEnd, type PartExtent, type PartSegment, 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 TrackPart, type TrackRole, type TurnoutClosure, type TurnoutKind, type UsableCapacity, WHOLE_MODULE_SECTION_ID, asModuleSchematic, assessSectionEnd, assessSectionJoint, benchworkBand, benchworkOutline, buildCrossover, buildPassingSiding, buildTransition, carCapacity, checkEndplateWidth, clearancePointPastFrogInches, deriveEndplatePoses, divergeSideForHand, docToState, emptyEditorState, endplateCentreOffsetInches, endplateFaceSegments, endplateLead, endplateTrackOffsetInches, endplateWidthInches, flexPartFor, flexParts, flexPieces, flexUsage, frogCasting, frogNumberFromName, fromSectionRelative, geometryTurnDegrees, hasNoFarEndplate, importedPartToTrackPart, inchesToScaleFeet, isLoopDoc, isTransitionTurnout, leadInchesForSize, maxFlexPieceInches, mergeImportedParts, mergeStoredParts, moduleCenterline, moduleFeatures, moduleFootprint, moduleLengthFromSections, moduleSections, nextId, parseXtpLibrary, partExtent, partExtentForSize, partOutlineAtFrog, pastFrogInchesForSize, pathLengthInches, 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 };
package/dist/index.js CHANGED
@@ -29,10 +29,28 @@ function moduleSections(doc) {
29
29
  ...typeof len === "number" && Number.isFinite(len) && len > 0 ? { lengthInches: len } : {},
30
30
  ...sec.geometryType ? { geometryType: sec.geometryType } : {},
31
31
  ...typeof deg === "number" && Number.isFinite(deg) ? { geometryDegrees: deg } : {},
32
- ...typeof off === "number" && Number.isFinite(off) ? { geometryOffsetInches: off } : {}
32
+ ...typeof off === "number" && Number.isFinite(off) ? { geometryOffsetInches: off } : {},
33
+ // The board's two ends (#130). Written only when actually described —
34
+ // an absent end is an ordinary internal joint, and an empty object
35
+ // would claim the owner had said something about it.
36
+ ...sectionEnd(sec.endA) ? { endA: sectionEnd(sec.endA) } : {},
37
+ ...sectionEnd(sec.endB) ? { endB: sectionEnd(sec.endB) } : {}
33
38
  };
34
39
  });
35
40
  }
41
+ function sectionEnd(end) {
42
+ if (!end) return null;
43
+ const config = end.config === "single" || end.config === "double" || end.config === "none" ? end.config : null;
44
+ const w = end.widthInches;
45
+ const o = end.trackOffsetInches;
46
+ const out = {
47
+ ...config ? { config } : {},
48
+ ...typeof w === "number" && Number.isFinite(w) && w > 0 ? { widthInches: w } : {},
49
+ // Signed, and 0 is meaningful ("explicitly centred", #93) — keep any finite.
50
+ ...typeof o === "number" && Number.isFinite(o) ? { trackOffsetInches: o } : {}
51
+ };
52
+ return Object.keys(out).length ? out : null;
53
+ }
36
54
  function sectionFootprints(doc, derive) {
37
55
  const spans = derive ? sectionSpans(doc) : [];
38
56
  const spanOf = new Map(spans.map((sp) => [sp.id, sp]));
@@ -575,6 +593,92 @@ function scaleFeetToInches(feet, ratio = N_SCALE_RATIO) {
575
593
  return feet * 12 / ratio;
576
594
  }
577
595
  var N_CAR_LENGTH_INCHES = 3.3;
596
+ var CLEARANCE_SPACING_INCHES = FREEMO_TRACK_SPACING_INCHES;
597
+ function clearancePointPastFrogInches(size, library = BUILT_IN_TRACK_PARTS, clearanceInches = CLEARANCE_SPACING_INCHES) {
598
+ const N = size > 0 ? size : 6;
599
+ const lead = leadInchesForSize(N, library);
600
+ const cl = turnoutClosure(N, { leadInches: lead });
601
+ let lo = 0;
602
+ let hi = Math.max(4 * lead, 4 * clearanceInches * N);
603
+ if (cl.offsetAt(hi) < clearanceInches) return hi - lead;
604
+ for (let i = 0; i < 60; i++) {
605
+ const mid = (lo + hi) / 2;
606
+ if (cl.offsetAt(mid) < clearanceInches) lo = mid;
607
+ else hi = mid;
608
+ }
609
+ return Math.max(0, hi - lead);
610
+ }
611
+ function usableCapacity(input) {
612
+ const lo = Math.min(input.fromPos, input.toPos);
613
+ const hi = Math.max(input.fromPos, input.toPos);
614
+ const drawn = hi - lo;
615
+ const carLen = input.carLengthInches ?? N_CAR_LENGTH_INCHES;
616
+ if (typeof input.measuredUsableInches === "number" && input.measuredUsableInches >= 0) {
617
+ const m = input.measuredUsableInches;
618
+ return {
619
+ fromPos: lo,
620
+ toPos: lo + m,
621
+ usableInches: m,
622
+ drawnInches: drawn,
623
+ givenUpInches: Math.max(0, drawn - m),
624
+ cars: carCapacity(0, m, carLen),
625
+ scaleFeet: Math.round(inchesToScaleFeet(m))
626
+ };
627
+ }
628
+ let a = lo;
629
+ let b = hi;
630
+ for (const sw of input.governing ?? []) {
631
+ const c = clearancePointPastFrogInches(sw.size ?? 6, input.library);
632
+ if (Math.abs(sw.pos - lo) <= Math.abs(sw.pos - hi)) a = Math.max(a, lo + c);
633
+ else b = Math.min(b, hi - c);
634
+ }
635
+ const usable = Math.max(0, b - a);
636
+ return {
637
+ fromPos: a,
638
+ toPos: Math.max(a, b),
639
+ usableInches: usable,
640
+ drawnInches: drawn,
641
+ givenUpInches: Math.max(0, drawn - usable),
642
+ cars: carCapacity(0, usable, carLen),
643
+ scaleFeet: Math.round(inchesToScaleFeet(usable))
644
+ };
645
+ }
646
+ function assessSectionEnd(end, opts = {}) {
647
+ const config = end?.config ?? "none";
648
+ const widthInches = endplateWidthInches({ widthInches: end?.widthInches });
649
+ const trackOffsetInches = endplateTrackOffsetInches(
650
+ end?.trackOffsetInches,
651
+ config,
652
+ opts.main2Below
653
+ );
654
+ const described = config === "single" || config === "double";
655
+ if (!described) {
656
+ return { described: false, conforming: false, issues: [], config, widthInches, trackOffsetInches };
657
+ }
658
+ const issues = checkEndplateWidth({
659
+ widthInches: end?.widthInches,
660
+ config,
661
+ trackOffsetInches: end?.trackOffsetInches,
662
+ main2Below: opts.main2Below
663
+ });
664
+ return { described: true, conforming: issues.length === 0, issues, config, widthInches, trackOffsetInches };
665
+ }
666
+ function assessSectionJoint(west, east, opts = {}) {
667
+ const w = assessSectionEnd(west, opts);
668
+ const e = assessSectionEnd(east, opts);
669
+ let reason = null;
670
+ if (!w.described || !e.described) {
671
+ reason = "an internal joint \u2014 neither end has been described as an interface";
672
+ if (w.described !== e.described)
673
+ reason = `only the ${w.described ? "west" : "east"} side is described as an endplate`;
674
+ } else if (!w.conforming || !e.conforming) {
675
+ const bad = !w.conforming ? w : e;
676
+ reason = bad.issues[0]?.message ?? "an end doesn't meet the standard";
677
+ } else if (w.config !== e.config) {
678
+ reason = `${w.config} meets ${e.config} \u2014 the track counts differ`;
679
+ }
680
+ return { west: w, east: e, standardInterface: reason === null, reason };
681
+ }
578
682
  function spanOverhang(input) {
579
683
  const lo = Math.min(input.fromPos, input.toPos);
580
684
  const hi = Math.max(input.fromPos, input.toPos);
@@ -825,7 +929,17 @@ function stateToDoc(state, recordNumber) {
825
929
  toPos: t.toPos,
826
930
  moduleTrackId: t.moduleTrackId,
827
931
  trackName: t.trackName || void 0,
828
- capacityFeet: Math.round(inchesToScaleFeet(Math.abs(t.toPos - t.fromPos))),
932
+ // ⚠️ USABLE capacity, measured from the governing turnouts' CLEARANCE
933
+ // POINTS (#19) — not the drawn rail-to-rail length, which counts track
934
+ // a car can't stand on without fouling the route it diverged from.
935
+ // FMN-0040's 70″ siding is 21 cars drawn and 17 usable.
936
+ capacityFeet: usableCapacity({
937
+ fromPos: t.fromPos,
938
+ toPos: t.toPos,
939
+ governing: state.turnouts.filter((sw) => sw.divergeTrack === t.id),
940
+ measuredUsableInches: t.measuredUsableInches
941
+ }).scaleFeet,
942
+ ...typeof t.measuredUsableInches === "number" && t.measuredUsableInches >= 0 ? { measuredUsableInches: t.measuredUsableInches } : {},
829
943
  ...state.loop && t.inLoop ? { inLoop: true } : {},
830
944
  ...t.path && t.path.length >= 2 ? { path: t.path } : {}
831
945
  }))
@@ -924,7 +1038,10 @@ function docToState(doc, fallbackLength, moduleTracks = []) {
924
1038
  trackName: t.trackName ?? nameOf(moduleTrackId),
925
1039
  ...t.inLoop ? { inLoop: true } : {},
926
1040
  // Authored path kept as-drawn (a physical shape, not rescaled with length).
927
- ...trackPath(t.path) ? { path: trackPath(t.path) } : {}
1041
+ ...trackPath(t.path) ? { path: trackPath(t.path) } : {},
1042
+ // A MEASURED length is a real-world fact about the physical track, so it
1043
+ // rescales with the module exactly as its positions do (#20).
1044
+ ...typeof t.measuredUsableInches === "number" && Number.isFinite(t.measuredUsableInches) ? { measuredUsableInches: sc(t.measuredUsableInches) } : {}
928
1045
  });
929
1046
  }
930
1047
  }
@@ -2360,6 +2477,6 @@ function poseOverridesFromDoc(doc) {
2360
2477
  return out;
2361
2478
  }
2362
2479
 
2363
- export { ATLAS_CODE55_N, BUILT_IN_TRACK_PARTS, CODE55_RAIL_HEIGHT_INCHES, DEFAULT_FLEX_PART_ID, ENDPLATE_FASCIA_CLEAR_INCHES, ENDPLATE_LEAD_INCHES, FLEX_TRACK_PARTS, FREEMO_ENDPLATE_TRACK_FASCIA_CLEARANCE_INCHES, FREEMO_ENDPLATE_WIDTH_MIN_INCHES, FREEMO_ENDPLATE_WIDTH_RECOMMENDED_INCHES, FREEMO_TRACK_SPACING_INCHES, MAIN2_TRACK_ID, MAIN_TRACK_ID, N_CAR_LENGTH_INCHES, N_SCALE_RATIO, RAIL_GAUGE_INCHES, TIE_HALF_LENGTH_INCHES, TURNOUT_LEAD_INCHES_PER_FROG, WHOLE_MODULE_SECTION_ID, asModuleSchematic, benchworkBand, benchworkOutline, buildCrossover, buildPassingSiding, buildTransition, carCapacity, checkEndplateWidth, deriveEndplatePoses, divergeSideForHand, docToState, emptyEditorState, endplateCentreOffsetInches, endplateFaceSegments, endplateLead, endplateTrackOffsetInches, endplateWidthInches, flexPartFor, flexParts, flexPieces, flexUsage, frogCasting, frogNumberFromName, fromSectionRelative, geometryTurnDegrees, hasNoFarEndplate, importedPartToTrackPart, inchesToScaleFeet, isLoopDoc, isTransitionTurnout, leadInchesForSize, maxFlexPieceInches, mergeImportedParts, mergeStoredParts, moduleCenterline, moduleFeatures, moduleFootprint, moduleLengthFromSections, moduleSections, nextId, parseXtpLibrary, partExtent, partExtentForSize, partOutlineAtFrog, pastFrogInchesForSize, pathLengthInches, 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 };
2480
+ export { ATLAS_CODE55_N, BUILT_IN_TRACK_PARTS, CLEARANCE_SPACING_INCHES, CODE55_RAIL_HEIGHT_INCHES, DEFAULT_FLEX_PART_ID, ENDPLATE_FASCIA_CLEAR_INCHES, ENDPLATE_LEAD_INCHES, FLEX_TRACK_PARTS, FREEMO_ENDPLATE_TRACK_FASCIA_CLEARANCE_INCHES, FREEMO_ENDPLATE_WIDTH_MIN_INCHES, FREEMO_ENDPLATE_WIDTH_RECOMMENDED_INCHES, FREEMO_TRACK_SPACING_INCHES, MAIN2_TRACK_ID, MAIN_TRACK_ID, N_CAR_LENGTH_INCHES, N_SCALE_RATIO, RAIL_GAUGE_INCHES, TIE_HALF_LENGTH_INCHES, TURNOUT_LEAD_INCHES_PER_FROG, WHOLE_MODULE_SECTION_ID, asModuleSchematic, assessSectionEnd, assessSectionJoint, benchworkBand, benchworkOutline, buildCrossover, buildPassingSiding, buildTransition, carCapacity, checkEndplateWidth, clearancePointPastFrogInches, deriveEndplatePoses, divergeSideForHand, docToState, emptyEditorState, endplateCentreOffsetInches, endplateFaceSegments, endplateLead, endplateTrackOffsetInches, endplateWidthInches, flexPartFor, flexParts, flexPieces, flexUsage, frogCasting, frogNumberFromName, fromSectionRelative, geometryTurnDegrees, hasNoFarEndplate, importedPartToTrackPart, inchesToScaleFeet, isLoopDoc, isTransitionTurnout, leadInchesForSize, maxFlexPieceInches, mergeImportedParts, mergeStoredParts, moduleCenterline, moduleFeatures, moduleFootprint, moduleLengthFromSections, moduleSections, nextId, parseXtpLibrary, partExtent, partExtentForSize, partOutlineAtFrog, pastFrogInchesForSize, pathLengthInches, 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 };
2364
2481
  //# sourceMappingURL=index.js.map
2365
2482
  //# sourceMappingURL=index.js.map