@willcgage/module-schematic 0.83.2 → 0.84.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
@@ -2302,6 +2302,62 @@ declare function turnoutClosure(size: number, opts?: {
2302
2302
  * around 25″ on a #7 — clear of the Free-moN 22″ minimum. */
2303
2303
  easeInches?: number;
2304
2304
  }): TurnoutClosure;
2305
+ /** Which end of a part this is. A graph connects joints; a walk uses `routes`. */
2306
+ type PartJointRole = "throat" | "through" | "diverge" | "divergeB";
2307
+ /** One end of a part, in part-local inches: `x` along the through route from
2308
+ * the tie end, `y` lateral toward the diverging side, `angleDeg` the OUTWARD
2309
+ * tangent (0 = +x). Named, unlike {@link PartEnd}, because a graph has to know
2310
+ * a throat from a frog. */
2311
+ interface PartJoint extends PartEnd {
2312
+ id: string;
2313
+ role: PartJointRole;
2314
+ }
2315
+ interface PartGeometry {
2316
+ joints: PartJoint[];
2317
+ /** Joint id pairs a train can run between. A turnout has two routes sharing
2318
+ * its throat; flex has one; a double crossover has four. */
2319
+ routes: [string, string][];
2320
+ /** The weakest provenance among the dimensions used — so a caller can tell a
2321
+ * placed-from-measurement part from a placed-from-a-catalogue-figure one. */
2322
+ source: DimensionSource;
2323
+ /** True when the diverging end came from a measured {@link
2324
+ * TrackPart.divergingLength} rather than from assuming the diverging rail
2325
+ * leaves the moulding at the same place the through route does. */
2326
+ divergingEndMeasured: boolean;
2327
+ }
2328
+ /**
2329
+ * Why a part has no derivable geometry — null when it has.
2330
+ *
2331
+ * Worth its own function because "we cannot place this yet" is a fact an owner
2332
+ * should see in the picker, not a silent absence. It is also the parts-library
2333
+ * backlog in machine-readable form: every string this returns is a measurement
2334
+ * someone could take.
2335
+ */
2336
+ declare function partGeometryGap(part: TrackPart): string | null;
2337
+ /**
2338
+ * A part's joints and routes, in its own frame.
2339
+ *
2340
+ * The through route runs along +x from the tie end; the diverging side is +y.
2341
+ * A placed piece is this, transformed — which is the whole point: the geometry
2342
+ * belongs to the PART, and placement is a rotation and a translation.
2343
+ *
2344
+ * ⚠️ THE DIVERGING END IS MEASURED WHERE WE HAVE A MEASUREMENT. `divergingLength`
2345
+ * (frog → end of the diverging rail, along the rail) says exactly where that end
2346
+ * is. Without it we fall back to assuming the diverging rail leaves the moulding
2347
+ * at the same x as the through route — which is what `partExtent` has always
2348
+ * assumed, and is an assumption, not a reading. `divergingEndMeasured` says
2349
+ * which you got.
2350
+ */
2351
+ declare function partGeometry(part: TrackPart, library?: TrackPart[]): PartGeometry | null;
2352
+ /** Every part that can be PLACED on a board today, and every one that can only
2353
+ * be named — with the reason. The gap list is the parts backlog. */
2354
+ declare function partsPlaceable(library?: TrackPart[]): {
2355
+ placeable: TrackPart[];
2356
+ blocked: {
2357
+ part: TrackPart;
2358
+ why: string;
2359
+ }[];
2360
+ };
2305
2361
  /** A frog casting's parts, in TURNOUT-LOCAL inches: `x` = distance past the
2306
2362
  * points, `y` = lateral offset from the through route. */
2307
2363
  interface FrogCasting {
@@ -2508,4 +2564,4 @@ declare function poseOverridesFromDoc(doc: ModuleSchematicDoc): Record<string, {
2508
2564
  heading: number;
2509
2565
  }>;
2510
2566
 
2511
- 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, FAST_TRACKS_N_ME55, FAST_TRACKS_N_ME55_CROSSOVERS, FLEX_TRACK_PARTS, FREEMO_ENDPLATE_TRACK_FASCIA_CLEARANCE_INCHES, FREEMO_ENDPLATE_WIDTH_MIN_INCHES, FREEMO_ENDPLATE_WIDTH_RECOMMENDED_INCHES, FREEMO_TRACK_SPACING_INCHES, type FlexPiece, type FlexPieceEnd, type FrogCasting, type GeometryType, type ImportedPart, type IndustryLabelMode, type IndustrySpot, 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 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, crossoverPinches, deriveEndplatePoses, divergeSideForHand, docToState, emptyEditorState, endplateCentreOffsetInches, endplateFaceSegments, endplateLead, endplateTrackOffsetInches, endplateWidthInches, flexPartFor, flexParts, flexPieces, flexUsage, frogCasting, frogNumberFromName, fromSectionRelative, geometryTurnDegrees, hasNoFarEndplate, importedPartToTrackPart, inchesToScaleFeet, isLoopDoc, isTransitionTurnout, laneOffsetAt, leadInchesForSize, maxFlexPieceInches, mergeImportedParts, mergeStoredParts, moduleCenterline, moduleFeatures, moduleFootprint, moduleLengthFromSections, moduleSections, nextId, parseXtpLibrary, partExtent, partExtentForSize, 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 };
2567
+ 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, FAST_TRACKS_N_ME55, FAST_TRACKS_N_ME55_CROSSOVERS, FLEX_TRACK_PARTS, FREEMO_ENDPLATE_TRACK_FASCIA_CLEARANCE_INCHES, FREEMO_ENDPLATE_WIDTH_MIN_INCHES, FREEMO_ENDPLATE_WIDTH_RECOMMENDED_INCHES, FREEMO_TRACK_SPACING_INCHES, type FlexPiece, type FlexPieceEnd, type FrogCasting, type GeometryType, type ImportedPart, type IndustryLabelMode, type IndustrySpot, 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, 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, crossoverPinches, deriveEndplatePoses, divergeSideForHand, docToState, emptyEditorState, endplateCentreOffsetInches, endplateFaceSegments, endplateLead, endplateTrackOffsetInches, endplateWidthInches, flexPartFor, flexParts, flexPieces, flexUsage, frogCasting, frogNumberFromName, fromSectionRelative, geometryTurnDegrees, hasNoFarEndplate, importedPartToTrackPart, inchesToScaleFeet, isLoopDoc, isTransitionTurnout, laneOffsetAt, leadInchesForSize, maxFlexPieceInches, mergeImportedParts, mergeStoredParts, moduleCenterline, moduleFeatures, moduleFootprint, moduleLengthFromSections, moduleSections, nextId, parseXtpLibrary, partExtent, partExtentForSize, partGeometry, partGeometryGap, partOutlineAtFrog, partsPlaceable, pastFrogInchesForSize, pathLengthInches, 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
@@ -2302,6 +2302,62 @@ declare function turnoutClosure(size: number, opts?: {
2302
2302
  * around 25″ on a #7 — clear of the Free-moN 22″ minimum. */
2303
2303
  easeInches?: number;
2304
2304
  }): TurnoutClosure;
2305
+ /** Which end of a part this is. A graph connects joints; a walk uses `routes`. */
2306
+ type PartJointRole = "throat" | "through" | "diverge" | "divergeB";
2307
+ /** One end of a part, in part-local inches: `x` along the through route from
2308
+ * the tie end, `y` lateral toward the diverging side, `angleDeg` the OUTWARD
2309
+ * tangent (0 = +x). Named, unlike {@link PartEnd}, because a graph has to know
2310
+ * a throat from a frog. */
2311
+ interface PartJoint extends PartEnd {
2312
+ id: string;
2313
+ role: PartJointRole;
2314
+ }
2315
+ interface PartGeometry {
2316
+ joints: PartJoint[];
2317
+ /** Joint id pairs a train can run between. A turnout has two routes sharing
2318
+ * its throat; flex has one; a double crossover has four. */
2319
+ routes: [string, string][];
2320
+ /** The weakest provenance among the dimensions used — so a caller can tell a
2321
+ * placed-from-measurement part from a placed-from-a-catalogue-figure one. */
2322
+ source: DimensionSource;
2323
+ /** True when the diverging end came from a measured {@link
2324
+ * TrackPart.divergingLength} rather than from assuming the diverging rail
2325
+ * leaves the moulding at the same place the through route does. */
2326
+ divergingEndMeasured: boolean;
2327
+ }
2328
+ /**
2329
+ * Why a part has no derivable geometry — null when it has.
2330
+ *
2331
+ * Worth its own function because "we cannot place this yet" is a fact an owner
2332
+ * should see in the picker, not a silent absence. It is also the parts-library
2333
+ * backlog in machine-readable form: every string this returns is a measurement
2334
+ * someone could take.
2335
+ */
2336
+ declare function partGeometryGap(part: TrackPart): string | null;
2337
+ /**
2338
+ * A part's joints and routes, in its own frame.
2339
+ *
2340
+ * The through route runs along +x from the tie end; the diverging side is +y.
2341
+ * A placed piece is this, transformed — which is the whole point: the geometry
2342
+ * belongs to the PART, and placement is a rotation and a translation.
2343
+ *
2344
+ * ⚠️ THE DIVERGING END IS MEASURED WHERE WE HAVE A MEASUREMENT. `divergingLength`
2345
+ * (frog → end of the diverging rail, along the rail) says exactly where that end
2346
+ * is. Without it we fall back to assuming the diverging rail leaves the moulding
2347
+ * at the same x as the through route — which is what `partExtent` has always
2348
+ * assumed, and is an assumption, not a reading. `divergingEndMeasured` says
2349
+ * which you got.
2350
+ */
2351
+ declare function partGeometry(part: TrackPart, library?: TrackPart[]): PartGeometry | null;
2352
+ /** Every part that can be PLACED on a board today, and every one that can only
2353
+ * be named — with the reason. The gap list is the parts backlog. */
2354
+ declare function partsPlaceable(library?: TrackPart[]): {
2355
+ placeable: TrackPart[];
2356
+ blocked: {
2357
+ part: TrackPart;
2358
+ why: string;
2359
+ }[];
2360
+ };
2305
2361
  /** A frog casting's parts, in TURNOUT-LOCAL inches: `x` = distance past the
2306
2362
  * points, `y` = lateral offset from the through route. */
2307
2363
  interface FrogCasting {
@@ -2508,4 +2564,4 @@ declare function poseOverridesFromDoc(doc: ModuleSchematicDoc): Record<string, {
2508
2564
  heading: number;
2509
2565
  }>;
2510
2566
 
2511
- 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, FAST_TRACKS_N_ME55, FAST_TRACKS_N_ME55_CROSSOVERS, FLEX_TRACK_PARTS, FREEMO_ENDPLATE_TRACK_FASCIA_CLEARANCE_INCHES, FREEMO_ENDPLATE_WIDTH_MIN_INCHES, FREEMO_ENDPLATE_WIDTH_RECOMMENDED_INCHES, FREEMO_TRACK_SPACING_INCHES, type FlexPiece, type FlexPieceEnd, type FrogCasting, type GeometryType, type ImportedPart, type IndustryLabelMode, type IndustrySpot, 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 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, crossoverPinches, deriveEndplatePoses, divergeSideForHand, docToState, emptyEditorState, endplateCentreOffsetInches, endplateFaceSegments, endplateLead, endplateTrackOffsetInches, endplateWidthInches, flexPartFor, flexParts, flexPieces, flexUsage, frogCasting, frogNumberFromName, fromSectionRelative, geometryTurnDegrees, hasNoFarEndplate, importedPartToTrackPart, inchesToScaleFeet, isLoopDoc, isTransitionTurnout, laneOffsetAt, leadInchesForSize, maxFlexPieceInches, mergeImportedParts, mergeStoredParts, moduleCenterline, moduleFeatures, moduleFootprint, moduleLengthFromSections, moduleSections, nextId, parseXtpLibrary, partExtent, partExtentForSize, 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 };
2567
+ 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, FAST_TRACKS_N_ME55, FAST_TRACKS_N_ME55_CROSSOVERS, FLEX_TRACK_PARTS, FREEMO_ENDPLATE_TRACK_FASCIA_CLEARANCE_INCHES, FREEMO_ENDPLATE_WIDTH_MIN_INCHES, FREEMO_ENDPLATE_WIDTH_RECOMMENDED_INCHES, FREEMO_TRACK_SPACING_INCHES, type FlexPiece, type FlexPieceEnd, type FrogCasting, type GeometryType, type ImportedPart, type IndustryLabelMode, type IndustrySpot, 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, 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, crossoverPinches, deriveEndplatePoses, divergeSideForHand, docToState, emptyEditorState, endplateCentreOffsetInches, endplateFaceSegments, endplateLead, endplateTrackOffsetInches, endplateWidthInches, flexPartFor, flexParts, flexPieces, flexUsage, frogCasting, frogNumberFromName, fromSectionRelative, geometryTurnDegrees, hasNoFarEndplate, importedPartToTrackPart, inchesToScaleFeet, isLoopDoc, isTransitionTurnout, laneOffsetAt, leadInchesForSize, maxFlexPieceInches, mergeImportedParts, mergeStoredParts, moduleCenterline, moduleFeatures, moduleFootprint, moduleLengthFromSections, moduleSections, nextId, parseXtpLibrary, partExtent, partExtentForSize, partGeometry, partGeometryGap, partOutlineAtFrog, partsPlaceable, pastFrogInchesForSize, pathLengthInches, 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
@@ -2126,6 +2126,107 @@ function turnoutClosure(size, opts = {}) {
2126
2126
  }
2127
2127
  };
2128
2128
  }
2129
+ function partGeometryGap(part) {
2130
+ if (part.kind === "flex") return null;
2131
+ if (part.kind === "crossover")
2132
+ return "a crossover fixture builds one HALF of the assembly (piecesPerAssembly), so its published lengths describe a piece, not the finished part \u2014 the geometry of the whole crossover is not yet derivable from them";
2133
+ if (part.kind === "crossing") return "crossing geometry is not modelled yet";
2134
+ if (part.kind === "curved-turnout")
2135
+ return "a curved turnout needs both radii AND its points/frog landmarks; only the radii are published";
2136
+ if (!part.pointsOffset)
2137
+ return "no points offset \u2014 without it there is nowhere for the diverging route to begin";
2138
+ if (!part.overallLength) return "no overall length \u2014 the part has no end to put a joint on";
2139
+ if (part.frogNumber == null) return "no frog number \u2014 the diverging angle is unknown";
2140
+ return null;
2141
+ }
2142
+ function partGeometry(part, library = BUILT_IN_TRACK_PARTS) {
2143
+ if (partGeometryGap(part)) return null;
2144
+ if (part.kind === "flex") {
2145
+ return {
2146
+ joints: [
2147
+ { id: "a", role: "throat", x: 0, y: 0, angleDeg: 180 },
2148
+ { id: "b", role: "through", x: 0, y: 0, angleDeg: 0 }
2149
+ ],
2150
+ routes: [["a", "b"]],
2151
+ source: "derived",
2152
+ divergingEndMeasured: false
2153
+ };
2154
+ }
2155
+ const N = part.frogNumber;
2156
+ const points = part.pointsOffset;
2157
+ const overall = part.overallLength;
2158
+ const frog = part.frogOffset;
2159
+ const lead = part.lead?.inches ?? (frog ? frog.inches - points.inches : void 0);
2160
+ if (lead == null || !(lead > 0)) return null;
2161
+ const isWye = part.kind === "wye";
2162
+ const effN = isWye ? N * 2 : N;
2163
+ const closure = turnoutClosure(effN, { leadInches: lead });
2164
+ const divergingEnd = () => {
2165
+ const frogX = frog ? frog.inches : points.inches + lead;
2166
+ const slope = closure.frogSlope;
2167
+ const dir = 1 / Math.hypot(1, slope);
2168
+ if (part.divergingLength) {
2169
+ const L = part.divergingLength.inches;
2170
+ return {
2171
+ x: frogX + L * dir,
2172
+ y: closure.offsetAt(frogX - points.inches) + L * slope * dir,
2173
+ measured: true
2174
+ };
2175
+ }
2176
+ return {
2177
+ x: overall.inches,
2178
+ y: closure.offsetAt(overall.inches - points.inches),
2179
+ measured: false
2180
+ };
2181
+ };
2182
+ const weakest = (...ds) => {
2183
+ const rank = ["measured", "manufacturer", "derived", "unverified"];
2184
+ let worst = 0;
2185
+ for (const d of ds) if (d) worst = Math.max(worst, rank.indexOf(d.source));
2186
+ return rank[worst];
2187
+ };
2188
+ const de = divergingEnd();
2189
+ const legAngle = Math.atan(closure.frogSlope) * 180 / Math.PI;
2190
+ if (isWye) {
2191
+ return {
2192
+ joints: [
2193
+ { id: "throat", role: "throat", x: 0, y: 0, angleDeg: 180 },
2194
+ { id: "legA", role: "diverge", x: de.x, y: de.y, angleDeg: legAngle },
2195
+ { id: "legB", role: "divergeB", x: de.x, y: -de.y, angleDeg: -legAngle }
2196
+ ],
2197
+ routes: [
2198
+ ["throat", "legA"],
2199
+ ["throat", "legB"]
2200
+ ],
2201
+ source: weakest(points, overall, frog, part.divergingLength),
2202
+ divergingEndMeasured: de.measured
2203
+ };
2204
+ }
2205
+ return {
2206
+ joints: [
2207
+ { id: "throat", role: "throat", x: 0, y: 0, angleDeg: 180 },
2208
+ { id: "through", role: "through", x: overall.inches, y: 0, angleDeg: 0 },
2209
+ { id: "diverge", role: "diverge", x: de.x, y: de.y, angleDeg: legAngle }
2210
+ ],
2211
+ routes: [
2212
+ ["throat", "through"],
2213
+ ["throat", "diverge"]
2214
+ ],
2215
+ source: weakest(points, overall, frog, part.divergingLength),
2216
+ divergingEndMeasured: de.measured
2217
+ };
2218
+ }
2219
+ function partsPlaceable(library = BUILT_IN_TRACK_PARTS) {
2220
+ const placeable = [];
2221
+ const blocked = [];
2222
+ for (const p of library) {
2223
+ const why = partGeometryGap(p);
2224
+ if (why) blocked.push({ part: p, why });
2225
+ else if (partGeometry(p, library)) placeable.push(p);
2226
+ else blocked.push({ part: p, why: "dimensions present but inconsistent" });
2227
+ }
2228
+ return { placeable, blocked };
2229
+ }
2129
2230
  function frogCasting(cl, opts = {}) {
2130
2231
  const g = opts.gaugeInches ?? RAIL_GAUGE_INCHES;
2131
2232
  const w = opts.reachInches ?? g * 1.5;
@@ -2705,6 +2806,6 @@ function poseOverridesFromDoc(doc) {
2705
2806
  return out;
2706
2807
  }
2707
2808
 
2708
- 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_TRACK_SPACING_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, buildTransition, carCapacity, checkEndplateWidth, clearancePointPastFrogInches, crossoverPinches, deriveEndplatePoses, divergeSideForHand, docToState, emptyEditorState, endplateCentreOffsetInches, endplateFaceSegments, endplateLead, endplateTrackOffsetInches, endplateWidthInches, flexPartFor, flexParts, flexPieces, flexUsage, frogCasting, frogNumberFromName, fromSectionRelative, geometryTurnDegrees, hasNoFarEndplate, importedPartToTrackPart, inchesToScaleFeet, isLoopDoc, isTransitionTurnout, laneOffsetAt, leadInchesForSize, maxFlexPieceInches, mergeImportedParts, mergeStoredParts, moduleCenterline, moduleFeatures, moduleFootprint, moduleLengthFromSections, moduleSections, nextId, parseXtpLibrary, partExtent, partExtentForSize, 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 };
2809
+ 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_TRACK_SPACING_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, buildTransition, carCapacity, checkEndplateWidth, clearancePointPastFrogInches, crossoverPinches, deriveEndplatePoses, divergeSideForHand, docToState, emptyEditorState, endplateCentreOffsetInches, endplateFaceSegments, endplateLead, endplateTrackOffsetInches, endplateWidthInches, flexPartFor, flexParts, flexPieces, flexUsage, frogCasting, frogNumberFromName, fromSectionRelative, geometryTurnDegrees, hasNoFarEndplate, importedPartToTrackPart, inchesToScaleFeet, isLoopDoc, isTransitionTurnout, laneOffsetAt, leadInchesForSize, maxFlexPieceInches, mergeImportedParts, mergeStoredParts, moduleCenterline, moduleFeatures, moduleFootprint, moduleLengthFromSections, moduleSections, nextId, parseXtpLibrary, partExtent, partExtentForSize, partGeometry, partGeometryGap, partOutlineAtFrog, partsPlaceable, pastFrogInchesForSize, pathLengthInches, 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 };
2709
2810
  //# sourceMappingURL=index.js.map
2710
2811
  //# sourceMappingURL=index.js.map