@willcgage/module-schematic 0.93.0 → 0.95.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
@@ -2465,6 +2465,27 @@ declare function pieceRoutePaths(piece: TrackPiece, library?: TrackPart[]): {
2465
2465
  y: number;
2466
2466
  }[];
2467
2467
  }[];
2468
+ /**
2469
+ * The HAND of a placed piece — the product an owner would actually buy.
2470
+ *
2471
+ * ⭐ THE MODEL HAS NO HAND, AND THIS IS NOT A CONTRADICTION. Nothing derived
2472
+ * from the graph asks which way a turnout is handed: the side a route leaves on
2473
+ * is simply where the piece is (ADR 0001). But a turnout on a shelf IS a
2474
+ * left-hand or a right-hand product, with its own part number, and an owner
2475
+ * picking one out of a palette is choosing between two things they could own.
2476
+ * So the hand belongs at the point of PURCHASE and PLACEMENT, and nowhere after.
2477
+ *
2478
+ * ⚠️ UNFLIPPED IS THE LEFT-HAND PART. A part's own frame diverges toward +y, and
2479
+ * +y is to the left of the through route looking from the throat — which agrees
2480
+ * with the library: the Atlas #7's `partNumbers.left` (2052) is the one whose
2481
+ * published geometry these dimensions were read from.
2482
+ *
2483
+ * Null for a part with no hand to have — a wye splits symmetrically, which is
2484
+ * exactly why it is sold as one product.
2485
+ */
2486
+ declare function pieceHand(part: TrackPart, flipped?: boolean | null): "left" | "right" | null;
2487
+ /** The part number a placed piece corresponds to — what to order. */
2488
+ declare function piecePartNumber(part: TrackPart, flipped?: boolean | null): string | undefined;
2468
2489
  /** Every part that can be PLACED on a board today, and every one that can only
2469
2490
  * be named — with the reason. The gap list is the parts backlog. */
2470
2491
  declare function partsPlaceable(library?: TrackPart[]): {
@@ -2622,6 +2643,22 @@ interface PieceSnap {
2622
2643
  * inch.
2623
2644
  */
2624
2645
  declare function snapPiece(moving: TrackPiece, others: TrackPiece[], library?: TrackPart[], withinInches?: number): PieceSnap | null;
2646
+ /**
2647
+ * Cut a run of flex to fit BETWEEN two joints — the piece's near end already
2648
+ * where it belongs, its far end brought onto another open joint.
2649
+ *
2650
+ * ⭐ WITHOUT THIS A CROSSOVER CANNOT BE BUILT BY HAND. A piece snaps by ONE end,
2651
+ * and a flex run's length handle drags along its own axis, so the far end can
2652
+ * never be steered onto the turnout opposite: an owner would have to type an
2653
+ * angle and a length to a hundredth of an inch. Cutting a piece to fit between
2654
+ * two fixed points is exactly what a builder does with flex, and it is the last
2655
+ * thing standing between "two mains" and "two mains joined".
2656
+ *
2657
+ * ⚠️ STRAIGHT RUNS ONLY. A bend is already fixed by its radius; asking an arc to
2658
+ * meet a second point as well over-constrains it, and quietly re-bending an
2659
+ * owner's curve to reach something is not a fit, it is a guess.
2660
+ */
2661
+ declare function fitFlexBetween(piece: TrackPiece, others: TrackPiece[], library?: TrackPart[], withinInches?: number): TrackPiece | null;
2625
2662
  /** A route the walk found: a continuous run of pieces a train can travel. */
2626
2663
  interface GraphRoute {
2627
2664
  id: string;
@@ -3049,4 +3086,4 @@ declare function poseOverridesFromDoc(doc: ModuleSchematicDoc): Record<string, {
3049
3086
  heading: number;
3050
3087
  }>;
3051
3088
 
3052
- 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_MAIN_MIN_RADIUS_INCHES, FREEMO_REVERSE_CURVE_STRAIGHT_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, 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, 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 };
3089
+ 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_MAIN_MIN_RADIUS_INCHES, FREEMO_REVERSE_CURVE_STRAIGHT_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, 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, sectionedCenterline, sectionedEndPose, sliceCenterline, snapPiece, spanOverhang, stateToDoc, storedPartToTrackPart, toSectionRelative, trackMeetsEndplateIssues, trackPart, trackPath, turnoutClosure, turnoutFacing, turnoutOccupiedSpan, turnoutPartForSize, usableCapacity, walkTrackGraph };
package/dist/index.d.ts CHANGED
@@ -2465,6 +2465,27 @@ declare function pieceRoutePaths(piece: TrackPiece, library?: TrackPart[]): {
2465
2465
  y: number;
2466
2466
  }[];
2467
2467
  }[];
2468
+ /**
2469
+ * The HAND of a placed piece — the product an owner would actually buy.
2470
+ *
2471
+ * ⭐ THE MODEL HAS NO HAND, AND THIS IS NOT A CONTRADICTION. Nothing derived
2472
+ * from the graph asks which way a turnout is handed: the side a route leaves on
2473
+ * is simply where the piece is (ADR 0001). But a turnout on a shelf IS a
2474
+ * left-hand or a right-hand product, with its own part number, and an owner
2475
+ * picking one out of a palette is choosing between two things they could own.
2476
+ * So the hand belongs at the point of PURCHASE and PLACEMENT, and nowhere after.
2477
+ *
2478
+ * ⚠️ UNFLIPPED IS THE LEFT-HAND PART. A part's own frame diverges toward +y, and
2479
+ * +y is to the left of the through route looking from the throat — which agrees
2480
+ * with the library: the Atlas #7's `partNumbers.left` (2052) is the one whose
2481
+ * published geometry these dimensions were read from.
2482
+ *
2483
+ * Null for a part with no hand to have — a wye splits symmetrically, which is
2484
+ * exactly why it is sold as one product.
2485
+ */
2486
+ declare function pieceHand(part: TrackPart, flipped?: boolean | null): "left" | "right" | null;
2487
+ /** The part number a placed piece corresponds to — what to order. */
2488
+ declare function piecePartNumber(part: TrackPart, flipped?: boolean | null): string | undefined;
2468
2489
  /** Every part that can be PLACED on a board today, and every one that can only
2469
2490
  * be named — with the reason. The gap list is the parts backlog. */
2470
2491
  declare function partsPlaceable(library?: TrackPart[]): {
@@ -2622,6 +2643,22 @@ interface PieceSnap {
2622
2643
  * inch.
2623
2644
  */
2624
2645
  declare function snapPiece(moving: TrackPiece, others: TrackPiece[], library?: TrackPart[], withinInches?: number): PieceSnap | null;
2646
+ /**
2647
+ * Cut a run of flex to fit BETWEEN two joints — the piece's near end already
2648
+ * where it belongs, its far end brought onto another open joint.
2649
+ *
2650
+ * ⭐ WITHOUT THIS A CROSSOVER CANNOT BE BUILT BY HAND. A piece snaps by ONE end,
2651
+ * and a flex run's length handle drags along its own axis, so the far end can
2652
+ * never be steered onto the turnout opposite: an owner would have to type an
2653
+ * angle and a length to a hundredth of an inch. Cutting a piece to fit between
2654
+ * two fixed points is exactly what a builder does with flex, and it is the last
2655
+ * thing standing between "two mains" and "two mains joined".
2656
+ *
2657
+ * ⚠️ STRAIGHT RUNS ONLY. A bend is already fixed by its radius; asking an arc to
2658
+ * meet a second point as well over-constrains it, and quietly re-bending an
2659
+ * owner's curve to reach something is not a fit, it is a guess.
2660
+ */
2661
+ declare function fitFlexBetween(piece: TrackPiece, others: TrackPiece[], library?: TrackPart[], withinInches?: number): TrackPiece | null;
2625
2662
  /** A route the walk found: a continuous run of pieces a train can travel. */
2626
2663
  interface GraphRoute {
2627
2664
  id: string;
@@ -3049,4 +3086,4 @@ declare function poseOverridesFromDoc(doc: ModuleSchematicDoc): Record<string, {
3049
3086
  heading: number;
3050
3087
  }>;
3051
3088
 
3052
- 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_MAIN_MIN_RADIUS_INCHES, FREEMO_REVERSE_CURVE_STRAIGHT_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, 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, 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 };
3089
+ 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_MAIN_MIN_RADIUS_INCHES, FREEMO_REVERSE_CURVE_STRAIGHT_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, 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, sectionedCenterline, sectionedEndPose, sliceCenterline, snapPiece, spanOverhang, stateToDoc, storedPartToTrackPart, toSectionRelative, trackMeetsEndplateIssues, trackPart, trackPath, turnoutClosure, turnoutFacing, turnoutOccupiedSpan, turnoutPartForSize, usableCapacity, walkTrackGraph };
package/dist/index.js CHANGED
@@ -2304,6 +2304,16 @@ function pieceRoutePaths(piece, library = BUILT_IN_TRACK_PARTS) {
2304
2304
  }
2305
2305
  return out;
2306
2306
  }
2307
+ function pieceHand(part, flipped) {
2308
+ const n = part.partNumbers;
2309
+ if (!n?.left || !n?.right) return null;
2310
+ return flipped ? "right" : "left";
2311
+ }
2312
+ function piecePartNumber(part, flipped) {
2313
+ const hand = pieceHand(part, flipped);
2314
+ if (!hand) return part.partNumbers?.single;
2315
+ return part.partNumbers?.[hand];
2316
+ }
2307
2317
  function partsPlaceable(library = BUILT_IN_TRACK_PARTS) {
2308
2318
  const placeable = [];
2309
2319
  const blocked = [];
@@ -2440,6 +2450,33 @@ function snapPiece(moving, others, library = BUILT_IN_TRACK_PARTS, withinInches
2440
2450
  to: best.t.key
2441
2451
  };
2442
2452
  }
2453
+ function fitFlexBetween(piece, others, library = BUILT_IN_TRACK_PARTS, withinInches = 0.5) {
2454
+ const part = library.find((p) => p.id === piece.partId);
2455
+ if (part?.kind !== "flex" || piece.radiusInches) return null;
2456
+ const mine = placedJoints([piece], library);
2457
+ const far = mine.find((j) => j.joint === "b");
2458
+ const near = mine.find((j) => j.joint === "a");
2459
+ if (!far || !near) return null;
2460
+ const graph = buildTrackGraph(others, library);
2461
+ const taken = new Set(graph.connections.flatMap((c) => [c.a, c.b]));
2462
+ let best = null;
2463
+ for (const j of graph.joints) {
2464
+ if (taken.has(j.key)) continue;
2465
+ if (Math.hypot(j.x - near.x, j.y - near.y) <= JOINT_SNAP_INCHES) continue;
2466
+ const d = Math.hypot(j.x - far.x, j.y - far.y);
2467
+ if (d <= withinInches && (!best || d < best.d)) best = { j, d };
2468
+ }
2469
+ if (!best) return null;
2470
+ const dx = best.j.x - piece.x;
2471
+ const dy = best.j.y - piece.y;
2472
+ const len = Math.hypot(dx, dy);
2473
+ if (!(len > 0)) return null;
2474
+ return {
2475
+ ...piece,
2476
+ rotationDeg: norm360(Math.atan2(dy, dx) * 180 / Math.PI),
2477
+ lengthInches: Math.round(len * 1e3) / 1e3
2478
+ };
2479
+ }
2443
2480
  var danglingDivergeWarning = (id) => `the route diverging at ${id} is not connected to anything`;
2444
2481
  var unreachedWarning = (id) => `${id} is not reachable from the endplate \u2014 nothing connects it`;
2445
2482
  function walkTrackGraph(graph, pieces, startAt, library = BUILT_IN_TRACK_PARTS) {
@@ -2633,9 +2670,16 @@ function graphToDoc(pieces, input) {
2633
2670
  const trackIdOf = /* @__PURE__ */ new Map([["main", MAIN_TRACK_ID]]);
2634
2671
  if (twoMains) trackIdOf.set("main2:main", MAIN2_TRACK_ID);
2635
2672
  for (const r of branches) trackIdOf.set(r.id, r.pieces[0]);
2636
- const laneOf = (r) => {
2673
+ const emitted = /* @__PURE__ */ new Set();
2674
+ const uniqueBranches = branches.filter((r) => {
2675
+ const id = trackIdOf.get(r.id);
2676
+ if (emitted.has(id)) return false;
2677
+ emitted.add(id);
2678
+ return true;
2679
+ });
2680
+ const laneOf = (r, among) => {
2637
2681
  const side = Math.sign(r.lateral) || 1;
2638
- const sameSide = branches.filter((x) => (Math.sign(x.lateral) || 1) === side).sort((a, b) => Math.abs(a.lateral) - Math.abs(b.lateral));
2682
+ const sameSide = among.filter((x) => (Math.sign(x.lateral) || 1) === side).sort((a, b) => Math.abs(a.lateral) - Math.abs(b.lateral));
2639
2683
  const taken = side === main2Lane ? 1 : 0;
2640
2684
  return side * (sameSide.indexOf(r) + 1 + taken);
2641
2685
  };
@@ -2661,7 +2705,7 @@ function graphToDoc(pieces, input) {
2661
2705
  }
2662
2706
  ] : []
2663
2707
  ];
2664
- for (const r of branches) {
2708
+ for (const r of uniqueBranches) {
2665
2709
  const id = trackIdOf.get(r.id);
2666
2710
  const meta = input.meta?.[id] ?? {};
2667
2711
  tracks.push({
@@ -2669,7 +2713,7 @@ function graphToDoc(pieces, input) {
2669
2713
  // It runs back into a second turnout, or it doesn't. That is the whole
2670
2714
  // difference between a siding and a spur, and it is read, not declared.
2671
2715
  role: r.endsAt ? "siding" : "spur",
2672
- lane: laneOf(r),
2716
+ lane: laneOf(r, uniqueBranches),
2673
2717
  fromPos: round(Math.min(r.fromPos, r.toPos)),
2674
2718
  toPos: round(Math.max(r.fromPos, r.toPos)),
2675
2719
  ...meta.trackName ? { trackName: meta.trackName } : {},
@@ -2720,6 +2764,12 @@ function graphToDoc(pieces, input) {
2720
2764
  });
2721
2765
  }
2722
2766
  turnouts.sort((a, b) => a.pos - b.pos);
2767
+ for (const t of tracks) {
2768
+ if (t.role === "main") continue;
2769
+ const ends = turnouts.filter((sw) => sw.divergeTrack === t.id);
2770
+ if (ends.length >= 2 && new Set(ends.map((sw) => sw.onTrack)).size >= 2)
2771
+ t.role = "crossover";
2772
+ }
2723
2773
  const place = (a, what) => {
2724
2774
  if (!a) return null;
2725
2775
  const hit = resolveGraphAnchor(a, walk, pieces, library);
@@ -3423,6 +3473,6 @@ function poseOverridesFromDoc(doc) {
3423
3473
  return out;
3424
3474
  }
3425
3475
 
3426
- 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, 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, 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, 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, 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, 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 };
3476
+ 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, 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, 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, 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, sectionedCenterline, sectionedEndPose, sliceCenterline, snapPiece, spanOverhang, stateToDoc, storedPartToTrackPart, toSectionRelative, trackMeetsEndplateIssues, trackPart, trackPath, turnoutClosure, turnoutFacing, turnoutOccupiedSpan, turnoutPartForSize, usableCapacity, walkTrackGraph };
3427
3477
  //# sourceMappingURL=index.js.map
3428
3478
  //# sourceMappingURL=index.js.map