@willcgage/module-schematic 0.97.1 → 0.98.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
@@ -677,6 +677,12 @@ interface ModuleFootprintInput {
677
677
  /** A balloon / return loop — the centre-line turns back on itself, so its far
678
678
  * end is the THROAT, not an endplate; only endplate A's face is emitted (#loop). */
679
679
  loop?: boolean;
680
+ /**
681
+ * The document already places track along a main (see {@link
682
+ * moduleCenterline}). Set from the doc's own `tracks`; without it a module
683
+ * whose track was authored before it had a geometry draws as a bare board.
684
+ */
685
+ hasPlacedTrack?: boolean;
680
686
  /** Axial endplate configs (A first, then B), same as
681
687
  * {@link ModuleGeometryInput.endplateConfigs}. `"none"` at B means the module
682
688
  * presents no far endplate, so no face is emitted there (#184/#191). */
@@ -2744,6 +2750,35 @@ declare function snapPiece(moving: TrackPiece, others: TrackPiece[], library?: T
2744
2750
  * owner's curve to reach something is not a fit, it is a guess.
2745
2751
  */
2746
2752
  declare function fitFlexBetween(piece: TrackPiece, others: TrackPiece[], library?: TrackPart[], withinInches?: number): TrackPiece | null;
2753
+ /** A run cut in two to let a piece in. */
2754
+ interface RunInsertion {
2755
+ /** The whole set: the host shortened, the new piece, and the remainder. */
2756
+ pieces: TrackPiece[];
2757
+ /** The run that was cut, and the piece that went into it. */
2758
+ hostId: string;
2759
+ insertedId: string;
2760
+ }
2761
+ /**
2762
+ * Drop a piece ONTO a run and cut the run to take it.
2763
+ *
2764
+ * ⭐ Will, 2026-07-27: *"When I drop a turnout on the track, it should cut the
2765
+ * track there and automatically snap to the new joints."* Snapping alone cannot
2766
+ * do this: the middle of a run has no open joint, so there is nothing to snap
2767
+ * TO — the joints have to be made by cutting, which is exactly what a builder
2768
+ * does with a razor saw.
2769
+ *
2770
+ * ⚠️ ONLY FLEX CAN BE CUT. A turnout or a sectional piece is a moulding: you
2771
+ * cannot saw one in half and have two of anything. Dropping onto one is refused
2772
+ * rather than fudged.
2773
+ *
2774
+ * ⚠️ AND THE PIECE HAS TO FIT. It consumes length along the run — a #7 is six
2775
+ * inches of it — so an insertion that would run off the end is refused instead
2776
+ * of silently producing a remainder of negative length.
2777
+ */
2778
+ declare function insertIntoRun(pieces: TrackPiece[], fresh: TrackPiece, at: {
2779
+ x: number;
2780
+ y: number;
2781
+ }, library?: TrackPart[], withinInches?: number): RunInsertion | null;
2747
2782
  /** A route the walk found: a continuous run of pieces a train can travel. */
2748
2783
  interface GraphRoute {
2749
2784
  id: string;
@@ -3174,4 +3209,4 @@ declare function poseOverridesFromDoc(doc: ModuleSchematicDoc): Record<string, {
3174
3209
  heading: number;
3175
3210
  }>;
3176
3211
 
3177
- export { ATLAS_CODE55_N, BUILT_IN_TRACK_PARTS, BUMPER_DRAWN_INCHES, 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_MAIN_MIN_RADIUS_INCHES, FREEMO_REVERSE_CURVE_STRAIGHT_INCHES, FREEMO_TRACK_SPACING_INCHES, type FlexPiece, type FlexPieceEnd, type FrogCasting, GENERIC_END_OF_TRACK, 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, TRACK_PART_KINDS, 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, fitFlexBetween, flexPartFor, flexParts, flexPieces, flexRunEnd, 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, pieceHand, piecePartNumber, pieceRoutePaths, placedJoints, poseNeedsManual, poseOverridesFromDoc, remapPos, resizeFlexPiece, resolveGraphAnchor, returnLoop, sampleBenchworkOutline, samplePartSegments, samplePath, scaleFeetToInches, sectionAdjacency, sectionBand, sectionBreaksFromSections, sectionComponents, sectionFootprints, sectionNeighbours, sectionSpans, sectionSpansOrWhole, sectionalArcInches, sectionedCenterline, sectionedEndPose, sliceCenterline, snapPiece, spanOverhang, stateToDoc, storedPartToTrackPart, toSectionRelative, trackMeetsEndplateIssues, trackPart, trackPath, turnoutClosure, turnoutFacing, turnoutOccupiedSpan, turnoutPartForSize, usableCapacity, walkTrackGraph };
3212
+ export { ATLAS_CODE55_N, BUILT_IN_TRACK_PARTS, BUMPER_DRAWN_INCHES, 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_MAIN_MIN_RADIUS_INCHES, FREEMO_REVERSE_CURVE_STRAIGHT_INCHES, FREEMO_TRACK_SPACING_INCHES, type FlexPiece, type FlexPieceEnd, type FrogCasting, GENERIC_END_OF_TRACK, 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 RunInsertion, 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, TRACK_PART_KINDS, 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, fitFlexBetween, flexPartFor, flexParts, flexPieces, flexRunEnd, flexUsage, frogCasting, frogNumberFromName, fromSectionRelative, geometryTurnDegrees, graphToDoc, hasNoFarEndplate, importedPartToTrackPart, inchesToScaleFeet, insertIntoRun, isLoopDoc, isTransitionTurnout, laneOffsetAt, leadInchesForSize, maxFlexPieceInches, mergeImportedParts, mergeStoredParts, moduleCenterline, moduleFeatures, moduleFootprint, moduleLengthFromSections, moduleSections, nextId, parseXtpLibrary, partExtent, partExtentForSize, partGeometry, partGeometryGap, partOutlineAtFrog, partsPlaceable, pastFrogInchesForSize, pathLengthInches, pieceHand, piecePartNumber, pieceRoutePaths, placedJoints, poseNeedsManual, poseOverridesFromDoc, remapPos, resizeFlexPiece, resolveGraphAnchor, returnLoop, sampleBenchworkOutline, samplePartSegments, samplePath, scaleFeetToInches, sectionAdjacency, sectionBand, sectionBreaksFromSections, sectionComponents, sectionFootprints, sectionNeighbours, sectionSpans, sectionSpansOrWhole, sectionalArcInches, sectionedCenterline, sectionedEndPose, sliceCenterline, snapPiece, spanOverhang, stateToDoc, storedPartToTrackPart, toSectionRelative, trackMeetsEndplateIssues, trackPart, trackPath, turnoutClosure, turnoutFacing, turnoutOccupiedSpan, turnoutPartForSize, usableCapacity, walkTrackGraph };
package/dist/index.d.ts CHANGED
@@ -677,6 +677,12 @@ interface ModuleFootprintInput {
677
677
  /** A balloon / return loop — the centre-line turns back on itself, so its far
678
678
  * end is the THROAT, not an endplate; only endplate A's face is emitted (#loop). */
679
679
  loop?: boolean;
680
+ /**
681
+ * The document already places track along a main (see {@link
682
+ * moduleCenterline}). Set from the doc's own `tracks`; without it a module
683
+ * whose track was authored before it had a geometry draws as a bare board.
684
+ */
685
+ hasPlacedTrack?: boolean;
680
686
  /** Axial endplate configs (A first, then B), same as
681
687
  * {@link ModuleGeometryInput.endplateConfigs}. `"none"` at B means the module
682
688
  * presents no far endplate, so no face is emitted there (#184/#191). */
@@ -2744,6 +2750,35 @@ declare function snapPiece(moving: TrackPiece, others: TrackPiece[], library?: T
2744
2750
  * owner's curve to reach something is not a fit, it is a guess.
2745
2751
  */
2746
2752
  declare function fitFlexBetween(piece: TrackPiece, others: TrackPiece[], library?: TrackPart[], withinInches?: number): TrackPiece | null;
2753
+ /** A run cut in two to let a piece in. */
2754
+ interface RunInsertion {
2755
+ /** The whole set: the host shortened, the new piece, and the remainder. */
2756
+ pieces: TrackPiece[];
2757
+ /** The run that was cut, and the piece that went into it. */
2758
+ hostId: string;
2759
+ insertedId: string;
2760
+ }
2761
+ /**
2762
+ * Drop a piece ONTO a run and cut the run to take it.
2763
+ *
2764
+ * ⭐ Will, 2026-07-27: *"When I drop a turnout on the track, it should cut the
2765
+ * track there and automatically snap to the new joints."* Snapping alone cannot
2766
+ * do this: the middle of a run has no open joint, so there is nothing to snap
2767
+ * TO — the joints have to be made by cutting, which is exactly what a builder
2768
+ * does with a razor saw.
2769
+ *
2770
+ * ⚠️ ONLY FLEX CAN BE CUT. A turnout or a sectional piece is a moulding: you
2771
+ * cannot saw one in half and have two of anything. Dropping onto one is refused
2772
+ * rather than fudged.
2773
+ *
2774
+ * ⚠️ AND THE PIECE HAS TO FIT. It consumes length along the run — a #7 is six
2775
+ * inches of it — so an insertion that would run off the end is refused instead
2776
+ * of silently producing a remainder of negative length.
2777
+ */
2778
+ declare function insertIntoRun(pieces: TrackPiece[], fresh: TrackPiece, at: {
2779
+ x: number;
2780
+ y: number;
2781
+ }, library?: TrackPart[], withinInches?: number): RunInsertion | null;
2747
2782
  /** A route the walk found: a continuous run of pieces a train can travel. */
2748
2783
  interface GraphRoute {
2749
2784
  id: string;
@@ -3174,4 +3209,4 @@ declare function poseOverridesFromDoc(doc: ModuleSchematicDoc): Record<string, {
3174
3209
  heading: number;
3175
3210
  }>;
3176
3211
 
3177
- export { ATLAS_CODE55_N, BUILT_IN_TRACK_PARTS, BUMPER_DRAWN_INCHES, 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_MAIN_MIN_RADIUS_INCHES, FREEMO_REVERSE_CURVE_STRAIGHT_INCHES, FREEMO_TRACK_SPACING_INCHES, type FlexPiece, type FlexPieceEnd, type FrogCasting, GENERIC_END_OF_TRACK, 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, TRACK_PART_KINDS, 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, fitFlexBetween, flexPartFor, flexParts, flexPieces, flexRunEnd, 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, pieceHand, piecePartNumber, pieceRoutePaths, placedJoints, poseNeedsManual, poseOverridesFromDoc, remapPos, resizeFlexPiece, resolveGraphAnchor, returnLoop, sampleBenchworkOutline, samplePartSegments, samplePath, scaleFeetToInches, sectionAdjacency, sectionBand, sectionBreaksFromSections, sectionComponents, sectionFootprints, sectionNeighbours, sectionSpans, sectionSpansOrWhole, sectionalArcInches, sectionedCenterline, sectionedEndPose, sliceCenterline, snapPiece, spanOverhang, stateToDoc, storedPartToTrackPart, toSectionRelative, trackMeetsEndplateIssues, trackPart, trackPath, turnoutClosure, turnoutFacing, turnoutOccupiedSpan, turnoutPartForSize, usableCapacity, walkTrackGraph };
3212
+ export { ATLAS_CODE55_N, BUILT_IN_TRACK_PARTS, BUMPER_DRAWN_INCHES, 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_MAIN_MIN_RADIUS_INCHES, FREEMO_REVERSE_CURVE_STRAIGHT_INCHES, FREEMO_TRACK_SPACING_INCHES, type FlexPiece, type FlexPieceEnd, type FrogCasting, GENERIC_END_OF_TRACK, 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 RunInsertion, 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, TRACK_PART_KINDS, 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, fitFlexBetween, flexPartFor, flexParts, flexPieces, flexRunEnd, flexUsage, frogCasting, frogNumberFromName, fromSectionRelative, geometryTurnDegrees, graphToDoc, hasNoFarEndplate, importedPartToTrackPart, inchesToScaleFeet, insertIntoRun, isLoopDoc, isTransitionTurnout, laneOffsetAt, leadInchesForSize, maxFlexPieceInches, mergeImportedParts, mergeStoredParts, moduleCenterline, moduleFeatures, moduleFootprint, moduleLengthFromSections, moduleSections, nextId, parseXtpLibrary, partExtent, partExtentForSize, partGeometry, partGeometryGap, partOutlineAtFrog, partsPlaceable, pastFrogInchesForSize, pathLengthInches, pieceHand, piecePartNumber, pieceRoutePaths, placedJoints, poseNeedsManual, poseOverridesFromDoc, remapPos, resizeFlexPiece, resolveGraphAnchor, returnLoop, sampleBenchworkOutline, samplePartSegments, samplePath, scaleFeetToInches, sectionAdjacency, sectionBand, sectionBreaksFromSections, sectionComponents, sectionFootprints, sectionNeighbours, sectionSpans, sectionSpansOrWhole, sectionalArcInches, sectionedCenterline, sectionedEndPose, sliceCenterline, snapPiece, spanOverhang, stateToDoc, storedPartToTrackPart, toSectionRelative, trackMeetsEndplateIssues, trackPart, trackPath, turnoutClosure, turnoutFacing, turnoutOccupiedSpan, turnoutPartForSize, usableCapacity, walkTrackGraph };
package/dist/index.js CHANGED
@@ -227,7 +227,7 @@ function moduleCenterline(input) {
227
227
  if (drawn) return samplePath(drawn);
228
228
  const chained = sectionedCenterline(input);
229
229
  if (chained.length >= 2) return chained;
230
- if (!input.geometryType) return [];
230
+ if (!input.geometryType && !input.hasPlacedTrack) return [];
231
231
  const L = input.lengthInches > 0 ? input.lengthInches : 24;
232
232
  const gt = input.geometryType;
233
233
  if (gt === "dead_end") return [{ x: 0, y: 0 }];
@@ -2551,6 +2551,83 @@ function fitFlexBetween(piece, others, library = BUILT_IN_TRACK_PARTS, withinInc
2551
2551
  lengthInches: Math.round(len * 1e3) / 1e3
2552
2552
  };
2553
2553
  }
2554
+ function insertIntoRun(pieces, fresh, at, library = BUILT_IN_TRACK_PARTS, withinInches = 1) {
2555
+ let host = null;
2556
+ let bestD = Infinity;
2557
+ for (const piece of pieces) {
2558
+ const part = library.find((x) => x.id === piece.partId);
2559
+ if (part?.kind !== "flex") continue;
2560
+ for (const { points } of pieceRoutePaths(piece, library))
2561
+ for (let i = 1; i < points.length; i++) {
2562
+ const d = distanceToSegment(at, points[i - 1], points[i]);
2563
+ if (d < bestD) {
2564
+ bestD = d;
2565
+ host = piece;
2566
+ }
2567
+ }
2568
+ }
2569
+ if (!host || bestD > withinInches) return null;
2570
+ const L = host.lengthInches ?? 0;
2571
+ const R = host.radiusInches;
2572
+ const steps = 200;
2573
+ let s = 0;
2574
+ let closest = Infinity;
2575
+ for (let i = 0; i <= steps; i++) {
2576
+ const t = L * i / steps;
2577
+ const local = flexRunEnd(t, R);
2578
+ const world = placeLocal(host, local.x, local.y);
2579
+ const d = Math.hypot(world.x - at.x, world.y - at.y);
2580
+ if (d < closest) {
2581
+ closest = d;
2582
+ s = t;
2583
+ }
2584
+ }
2585
+ const cutLocal = flexRunEnd(s, R);
2586
+ const cut = placeLocal(host, cutLocal.x, cutLocal.y);
2587
+ const heading = norm360(host.rotationDeg + (host.flipped ? -cutLocal.headingDeg : cutLocal.headingDeg));
2588
+ const placed = { ...fresh, x: cut.x, y: cut.y, rotationDeg: heading };
2589
+ const js = placedJoints([placed], library);
2590
+ const entry = js.find((j) => j.joint === "throat" || j.joint === "a");
2591
+ const exit = js.find((j) => j.joint === "through" || j.joint === "b");
2592
+ if (!entry || !exit) return null;
2593
+ const body = Math.hypot(exit.x - entry.x, exit.y - entry.y);
2594
+ const rest = L - s - body;
2595
+ if (s < 0 || rest < 0) return null;
2596
+ const ids = new Set(pieces.map((p) => p.id));
2597
+ let n = 1;
2598
+ while (ids.has(`${host.id}b${n}`)) n += 1;
2599
+ const tail = {
2600
+ ...host,
2601
+ id: `${host.id}b${n}`,
2602
+ x: exit.x,
2603
+ y: exit.y,
2604
+ rotationDeg: exit.headingDeg,
2605
+ lengthInches: rest
2606
+ };
2607
+ const out = pieces.map((p) => p.id === host.id ? { ...p, lengthInches: s } : p);
2608
+ const kept = out.filter((p) => p.id !== host.id || s > 0);
2609
+ return {
2610
+ pieces: [...kept, placed, ...rest > 0 ? [tail] : []],
2611
+ hostId: host.id,
2612
+ insertedId: placed.id
2613
+ };
2614
+ }
2615
+ function placeLocal(piece, x, y) {
2616
+ const rad = piece.rotationDeg * Math.PI / 180;
2617
+ const ly = piece.flipped ? -y : y;
2618
+ return {
2619
+ x: piece.x + x * Math.cos(rad) - ly * Math.sin(rad),
2620
+ y: piece.y + x * Math.sin(rad) + ly * Math.cos(rad)
2621
+ };
2622
+ }
2623
+ function distanceToSegment(p, a, b) {
2624
+ const vx = b.x - a.x;
2625
+ const vy = b.y - a.y;
2626
+ const len2 = vx * vx + vy * vy;
2627
+ if (len2 === 0) return Math.hypot(p.x - a.x, p.y - a.y);
2628
+ const t = Math.max(0, Math.min(1, ((p.x - a.x) * vx + (p.y - a.y) * vy) / len2));
2629
+ return Math.hypot(p.x - (a.x + t * vx), p.y - (a.y + t * vy));
2630
+ }
2554
2631
  var danglingDivergeWarning = (id) => `the route diverging at ${id} is not connected to anything`;
2555
2632
  var unreachedWarning = (id) => `${id} is not reachable from the endplate \u2014 nothing connects it`;
2556
2633
  function walkTrackGraph(graph, pieces, startAt, library = BUILT_IN_TRACK_PARTS) {
@@ -3561,6 +3638,6 @@ function poseOverridesFromDoc(doc) {
3561
3638
  return out;
3562
3639
  }
3563
3640
 
3564
- export { ATLAS_CODE55_N, BUILT_IN_TRACK_PARTS, BUMPER_DRAWN_INCHES, CLEARANCE_SPACING_INCHES, CODE55_RAIL_HEIGHT_INCHES, DEFAULT_FLEX_PART_ID, ENDPLATE_FASCIA_CLEAR_INCHES, ENDPLATE_LEAD_INCHES, 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_MAIN_MIN_RADIUS_INCHES, FREEMO_REVERSE_CURVE_STRAIGHT_INCHES, FREEMO_TRACK_SPACING_INCHES, GENERIC_END_OF_TRACK, JOINT_SNAP_INCHES, MAIN2_TRACK_ID, MAIN_TRACK_ID, N_CAR_LENGTH_INCHES, N_SCALE_RATIO, PINCH_EASE_INCHES, RAIL_GAUGE_INCHES, TIE_HALF_LENGTH_INCHES, TRACK_PART_KINDS, TURNOUT_LEAD_INCHES_PER_FROG, 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, fitFlexBetween, flexPartFor, flexParts, flexPieces, flexRunEnd, 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, pieceHand, piecePartNumber, pieceRoutePaths, placedJoints, poseNeedsManual, poseOverridesFromDoc, remapPos, resizeFlexPiece, resolveGraphAnchor, returnLoop, sampleBenchworkOutline, samplePartSegments, samplePath, scaleFeetToInches, sectionAdjacency, sectionBand, sectionBreaksFromSections, sectionComponents, sectionFootprints, sectionNeighbours, sectionSpans, sectionSpansOrWhole, sectionalArcInches, sectionedCenterline, sectionedEndPose, sliceCenterline, snapPiece, spanOverhang, stateToDoc, storedPartToTrackPart, toSectionRelative, trackMeetsEndplateIssues, trackPart, trackPath, turnoutClosure, turnoutFacing, turnoutOccupiedSpan, turnoutPartForSize, usableCapacity, walkTrackGraph };
3641
+ export { ATLAS_CODE55_N, BUILT_IN_TRACK_PARTS, BUMPER_DRAWN_INCHES, CLEARANCE_SPACING_INCHES, CODE55_RAIL_HEIGHT_INCHES, DEFAULT_FLEX_PART_ID, ENDPLATE_FASCIA_CLEAR_INCHES, ENDPLATE_LEAD_INCHES, 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_MAIN_MIN_RADIUS_INCHES, FREEMO_REVERSE_CURVE_STRAIGHT_INCHES, FREEMO_TRACK_SPACING_INCHES, GENERIC_END_OF_TRACK, JOINT_SNAP_INCHES, MAIN2_TRACK_ID, MAIN_TRACK_ID, N_CAR_LENGTH_INCHES, N_SCALE_RATIO, PINCH_EASE_INCHES, RAIL_GAUGE_INCHES, TIE_HALF_LENGTH_INCHES, TRACK_PART_KINDS, TURNOUT_LEAD_INCHES_PER_FROG, 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, fitFlexBetween, flexPartFor, flexParts, flexPieces, flexRunEnd, flexUsage, frogCasting, frogNumberFromName, fromSectionRelative, geometryTurnDegrees, graphToDoc, hasNoFarEndplate, importedPartToTrackPart, inchesToScaleFeet, insertIntoRun, isLoopDoc, isTransitionTurnout, laneOffsetAt, leadInchesForSize, maxFlexPieceInches, mergeImportedParts, mergeStoredParts, moduleCenterline, moduleFeatures, moduleFootprint, moduleLengthFromSections, moduleSections, nextId, parseXtpLibrary, partExtent, partExtentForSize, partGeometry, partGeometryGap, partOutlineAtFrog, partsPlaceable, pastFrogInchesForSize, pathLengthInches, pieceHand, piecePartNumber, pieceRoutePaths, placedJoints, poseNeedsManual, poseOverridesFromDoc, remapPos, resizeFlexPiece, resolveGraphAnchor, returnLoop, sampleBenchworkOutline, samplePartSegments, samplePath, scaleFeetToInches, sectionAdjacency, sectionBand, sectionBreaksFromSections, sectionComponents, sectionFootprints, sectionNeighbours, sectionSpans, sectionSpansOrWhole, sectionalArcInches, sectionedCenterline, sectionedEndPose, sliceCenterline, snapPiece, spanOverhang, stateToDoc, storedPartToTrackPart, toSectionRelative, trackMeetsEndplateIssues, trackPart, trackPath, turnoutClosure, turnoutFacing, turnoutOccupiedSpan, turnoutPartForSize, usableCapacity, walkTrackGraph };
3565
3642
  //# sourceMappingURL=index.js.map
3566
3643
  //# sourceMappingURL=index.js.map