@willcgage/module-schematic 0.49.5 → 0.50.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.cjs +46 -19
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +47 -2
- package/dist/index.d.ts +47 -2
- package/dist/index.js +44 -20
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.d.cts
CHANGED
|
@@ -668,7 +668,9 @@ declare const N_CAR_LENGTH_INCHES = 3.3;
|
|
|
668
668
|
/** How many cars fit in a span, from its drawn length — the derived capacity a
|
|
669
669
|
* siding or an industry spot holds (never typed). */
|
|
670
670
|
declare function carCapacity(fromPos: number, toPos: number, carLengthInches?: number): number;
|
|
671
|
-
/** Parse a jsonb value into a schematic doc, or null if it isn't one.
|
|
671
|
+
/** Parse a jsonb value into a schematic doc, or null if it isn't one.
|
|
672
|
+
* EVERY read path goes through here (catalog, module page, my-modules, FD), so
|
|
673
|
+
* it's also where a stored doc gets healed — see healSelfDivergingTurnouts. */
|
|
672
674
|
declare function asModuleSchematic(x: unknown): ModuleSchematicDoc | null;
|
|
673
675
|
interface EditorTrack {
|
|
674
676
|
id: string;
|
|
@@ -1030,6 +1032,49 @@ interface ModuleFeatures {
|
|
|
1030
1032
|
* right-hand throws to the opposite side. `kind` is the source of truth for the
|
|
1031
1033
|
* drawn side (#bug1) — the stored lane's sign is reconciled to match it.
|
|
1032
1034
|
*/
|
|
1035
|
+
/** N-scale track gauge, inches (9 mm). */
|
|
1036
|
+
declare const RAIL_GAUGE_INCHES = 0.354;
|
|
1037
|
+
/** Points → FROG for a #1 frog, inches — a turnout's LEAD, scaled by frog
|
|
1038
|
+
* number. Calibrated COMMERCIALLY rather than on the prototype, since owners lay
|
|
1039
|
+
* real product: an Atlas code 55 #7 measures 3⅜″ points→frog (Steve Branton,
|
|
1040
|
+
* #173) ⇒ 3.375 / 7. (The prototype #7 lead is 62′-1″ ≈ 4.66″ in N; commercial
|
|
1041
|
+
* turnouts are compressed against that.) */
|
|
1042
|
+
declare const TURNOUT_LEAD_INCHES_PER_FROG = 0.482;
|
|
1043
|
+
/** A turnout's CLOSURE — the diverging route's lateral offset from the through
|
|
1044
|
+
* route, from the points (s = 0) to the frog (s = lead) and beyond.
|
|
1045
|
+
*
|
|
1046
|
+
* Anchored on one geometric fact: the FROG is where the two routes' inner rails
|
|
1047
|
+
* cross, which happens where the centre-lines are exactly ONE GAUGE apart. So
|
|
1048
|
+
* `pos` (which means the frog, #132) must be where d = gauge — otherwise the
|
|
1049
|
+
* drawn crossing and the frog marker disagree.
|
|
1050
|
+
*
|
|
1051
|
+
* The curve is `d(s) = α·s + k·s²`, fixed by three conditions:
|
|
1052
|
+
* d(0) = 0 — the points start ON the stock rail
|
|
1053
|
+
* d(lead) = gauge — the frog lands exactly on `pos`
|
|
1054
|
+
* d'(lead) = 1/N — it leaves at the frog angle
|
|
1055
|
+
*
|
|
1056
|
+
* ⚠️ `α` is the SWITCH ANGLE and is deliberately non-zero. A curve leaving
|
|
1057
|
+
* TANGENT to the stock rail only reaches `lead/2N` of lateral by the frog
|
|
1058
|
+
* (0.24″ on a #7) — it could never reach a gauge within a commercial lead; you'd
|
|
1059
|
+
* need `lead = 2·gauge·N` ≈ the prototype figure. Real points leave the stock
|
|
1060
|
+
* rail at a finite angle, which is exactly why a commercial turnout can be
|
|
1061
|
+
* shorter than prototype. Valid while `lead < 2·gauge·N`; α is clamped at 0
|
|
1062
|
+
* otherwise so an absurd lead degrades to a tangent start instead of bending
|
|
1063
|
+
* backwards. */
|
|
1064
|
+
interface TurnoutClosure {
|
|
1065
|
+
/** Lateral offset from the through route at arc length `s` past the points. */
|
|
1066
|
+
offsetAt: (s: number) => number;
|
|
1067
|
+
/** Points→frog, inches. */
|
|
1068
|
+
lead: number;
|
|
1069
|
+
/** Slope at the points (the switch angle), rise over run. */
|
|
1070
|
+
switchSlope: number;
|
|
1071
|
+
/** Slope at and beyond the frog — the frog angle, 1/N. */
|
|
1072
|
+
frogSlope: number;
|
|
1073
|
+
}
|
|
1074
|
+
declare function turnoutClosure(size: number, opts?: {
|
|
1075
|
+
leadInches?: number;
|
|
1076
|
+
gaugeInches?: number;
|
|
1077
|
+
}): TurnoutClosure;
|
|
1033
1078
|
declare function divergeSideForHand(kind: TurnoutKind | undefined, stubDir: number,
|
|
1034
1079
|
/** The turnout is installed the other way round — the points face the far
|
|
1035
1080
|
* direction, which swaps the side the diverging route leaves on. */
|
|
@@ -1188,4 +1233,4 @@ declare function poseOverridesFromDoc(doc: ModuleSchematicDoc): Record<string, {
|
|
|
1188
1233
|
heading: number;
|
|
1189
1234
|
}>;
|
|
1190
1235
|
|
|
1191
|
-
export { type BenchworkPoint, type BranchConnector, 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, FREEMO_ENDPLATE_TRACK_FASCIA_CLEARANCE_INCHES, FREEMO_ENDPLATE_WIDTH_MIN_INCHES, FREEMO_ENDPLATE_WIDTH_RECOMMENDED_INCHES, FREEMO_TRACK_SPACING_INCHES, type GeometryType, type IndustryLabelMode, type IndustrySpot, MAIN2_TRACK_ID, MAIN_TRACK_ID, type ModuleFeatures, type ModuleFootprint, type ModuleFootprintInput, type ModuleGeometryInput, type ModuleSchematicDoc, type ModuleTrackRow, N_CAR_LENGTH_INCHES, N_SCALE_RATIO, type OutlineFace, type ReturnLoopGeometry, type ReturnLoopShape, type SchematicBlock, type SchematicControlPoint, type SchematicCrossing, type SchematicEndplate, type SchematicEndplateTrack, type SchematicIndustry, type SchematicSection, type SchematicSignal, type SchematicTrack, type SchematicTurnout, type SectionAdjacency, type SectionFootprint, type SectionRelativePos, type SignalFacing, type SignalSide, type TrackConfig, type TrackRole, type TurnoutKind, WHOLE_MODULE_SECTION_ID, asModuleSchematic, benchworkBand, benchworkOutline, buildCrossover, buildPassingSiding, buildTransition, carCapacity, checkEndplateWidth, deriveEndplatePoses, divergeSideForHand, docToState, emptyEditorState, endplateFaceSegments, endplateLead, endplateTrackOffsetFor, endplateTrackOffsetInches, endplateWidthInches, fromSectionRelative, geometryTurnDegrees, inchesToScaleFeet, isLoopDoc, isTransitionTurnout, moduleCenterline, moduleFeatures, moduleFootprint, moduleLengthFromSections, moduleSections, nextId, poseNeedsManual, poseOverridesFromDoc, remapPos, returnLoop, sampleBenchworkOutline, samplePath, scaleFeetToInches, sectionAdjacency, sectionBand, sectionBreaksFromSections, sectionComponents, sectionFootprints, sectionNeighbours, sectionSpans, sectionSpansOrWhole, sectionedCenterline, sectionedEndPose, sliceCenterline, stateToDoc, toSectionRelative, trackMeetsEndplateIssues, trackPath };
|
|
1236
|
+
export { type BenchworkPoint, type BranchConnector, 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, FREEMO_ENDPLATE_TRACK_FASCIA_CLEARANCE_INCHES, FREEMO_ENDPLATE_WIDTH_MIN_INCHES, FREEMO_ENDPLATE_WIDTH_RECOMMENDED_INCHES, FREEMO_TRACK_SPACING_INCHES, type GeometryType, type IndustryLabelMode, type IndustrySpot, MAIN2_TRACK_ID, MAIN_TRACK_ID, type ModuleFeatures, type ModuleFootprint, type ModuleFootprintInput, type ModuleGeometryInput, type ModuleSchematicDoc, type ModuleTrackRow, N_CAR_LENGTH_INCHES, N_SCALE_RATIO, type OutlineFace, RAIL_GAUGE_INCHES, type ReturnLoopGeometry, type ReturnLoopShape, type SchematicBlock, type SchematicControlPoint, type SchematicCrossing, type SchematicEndplate, type SchematicEndplateTrack, type SchematicIndustry, type SchematicSection, type SchematicSignal, type SchematicTrack, type SchematicTurnout, type SectionAdjacency, type SectionFootprint, type SectionRelativePos, type SignalFacing, type SignalSide, TURNOUT_LEAD_INCHES_PER_FROG, type TrackConfig, type TrackRole, type TurnoutClosure, type TurnoutKind, WHOLE_MODULE_SECTION_ID, asModuleSchematic, benchworkBand, benchworkOutline, buildCrossover, buildPassingSiding, buildTransition, carCapacity, checkEndplateWidth, deriveEndplatePoses, divergeSideForHand, docToState, emptyEditorState, endplateFaceSegments, endplateLead, endplateTrackOffsetFor, endplateTrackOffsetInches, endplateWidthInches, fromSectionRelative, geometryTurnDegrees, inchesToScaleFeet, isLoopDoc, isTransitionTurnout, moduleCenterline, moduleFeatures, moduleFootprint, moduleLengthFromSections, moduleSections, nextId, poseNeedsManual, poseOverridesFromDoc, remapPos, returnLoop, sampleBenchworkOutline, samplePath, scaleFeetToInches, sectionAdjacency, sectionBand, sectionBreaksFromSections, sectionComponents, sectionFootprints, sectionNeighbours, sectionSpans, sectionSpansOrWhole, sectionedCenterline, sectionedEndPose, sliceCenterline, stateToDoc, toSectionRelative, trackMeetsEndplateIssues, trackPath, turnoutClosure };
|
package/dist/index.d.ts
CHANGED
|
@@ -668,7 +668,9 @@ declare const N_CAR_LENGTH_INCHES = 3.3;
|
|
|
668
668
|
/** How many cars fit in a span, from its drawn length — the derived capacity a
|
|
669
669
|
* siding or an industry spot holds (never typed). */
|
|
670
670
|
declare function carCapacity(fromPos: number, toPos: number, carLengthInches?: number): number;
|
|
671
|
-
/** Parse a jsonb value into a schematic doc, or null if it isn't one.
|
|
671
|
+
/** Parse a jsonb value into a schematic doc, or null if it isn't one.
|
|
672
|
+
* EVERY read path goes through here (catalog, module page, my-modules, FD), so
|
|
673
|
+
* it's also where a stored doc gets healed — see healSelfDivergingTurnouts. */
|
|
672
674
|
declare function asModuleSchematic(x: unknown): ModuleSchematicDoc | null;
|
|
673
675
|
interface EditorTrack {
|
|
674
676
|
id: string;
|
|
@@ -1030,6 +1032,49 @@ interface ModuleFeatures {
|
|
|
1030
1032
|
* right-hand throws to the opposite side. `kind` is the source of truth for the
|
|
1031
1033
|
* drawn side (#bug1) — the stored lane's sign is reconciled to match it.
|
|
1032
1034
|
*/
|
|
1035
|
+
/** N-scale track gauge, inches (9 mm). */
|
|
1036
|
+
declare const RAIL_GAUGE_INCHES = 0.354;
|
|
1037
|
+
/** Points → FROG for a #1 frog, inches — a turnout's LEAD, scaled by frog
|
|
1038
|
+
* number. Calibrated COMMERCIALLY rather than on the prototype, since owners lay
|
|
1039
|
+
* real product: an Atlas code 55 #7 measures 3⅜″ points→frog (Steve Branton,
|
|
1040
|
+
* #173) ⇒ 3.375 / 7. (The prototype #7 lead is 62′-1″ ≈ 4.66″ in N; commercial
|
|
1041
|
+
* turnouts are compressed against that.) */
|
|
1042
|
+
declare const TURNOUT_LEAD_INCHES_PER_FROG = 0.482;
|
|
1043
|
+
/** A turnout's CLOSURE — the diverging route's lateral offset from the through
|
|
1044
|
+
* route, from the points (s = 0) to the frog (s = lead) and beyond.
|
|
1045
|
+
*
|
|
1046
|
+
* Anchored on one geometric fact: the FROG is where the two routes' inner rails
|
|
1047
|
+
* cross, which happens where the centre-lines are exactly ONE GAUGE apart. So
|
|
1048
|
+
* `pos` (which means the frog, #132) must be where d = gauge — otherwise the
|
|
1049
|
+
* drawn crossing and the frog marker disagree.
|
|
1050
|
+
*
|
|
1051
|
+
* The curve is `d(s) = α·s + k·s²`, fixed by three conditions:
|
|
1052
|
+
* d(0) = 0 — the points start ON the stock rail
|
|
1053
|
+
* d(lead) = gauge — the frog lands exactly on `pos`
|
|
1054
|
+
* d'(lead) = 1/N — it leaves at the frog angle
|
|
1055
|
+
*
|
|
1056
|
+
* ⚠️ `α` is the SWITCH ANGLE and is deliberately non-zero. A curve leaving
|
|
1057
|
+
* TANGENT to the stock rail only reaches `lead/2N` of lateral by the frog
|
|
1058
|
+
* (0.24″ on a #7) — it could never reach a gauge within a commercial lead; you'd
|
|
1059
|
+
* need `lead = 2·gauge·N` ≈ the prototype figure. Real points leave the stock
|
|
1060
|
+
* rail at a finite angle, which is exactly why a commercial turnout can be
|
|
1061
|
+
* shorter than prototype. Valid while `lead < 2·gauge·N`; α is clamped at 0
|
|
1062
|
+
* otherwise so an absurd lead degrades to a tangent start instead of bending
|
|
1063
|
+
* backwards. */
|
|
1064
|
+
interface TurnoutClosure {
|
|
1065
|
+
/** Lateral offset from the through route at arc length `s` past the points. */
|
|
1066
|
+
offsetAt: (s: number) => number;
|
|
1067
|
+
/** Points→frog, inches. */
|
|
1068
|
+
lead: number;
|
|
1069
|
+
/** Slope at the points (the switch angle), rise over run. */
|
|
1070
|
+
switchSlope: number;
|
|
1071
|
+
/** Slope at and beyond the frog — the frog angle, 1/N. */
|
|
1072
|
+
frogSlope: number;
|
|
1073
|
+
}
|
|
1074
|
+
declare function turnoutClosure(size: number, opts?: {
|
|
1075
|
+
leadInches?: number;
|
|
1076
|
+
gaugeInches?: number;
|
|
1077
|
+
}): TurnoutClosure;
|
|
1033
1078
|
declare function divergeSideForHand(kind: TurnoutKind | undefined, stubDir: number,
|
|
1034
1079
|
/** The turnout is installed the other way round — the points face the far
|
|
1035
1080
|
* direction, which swaps the side the diverging route leaves on. */
|
|
@@ -1188,4 +1233,4 @@ declare function poseOverridesFromDoc(doc: ModuleSchematicDoc): Record<string, {
|
|
|
1188
1233
|
heading: number;
|
|
1189
1234
|
}>;
|
|
1190
1235
|
|
|
1191
|
-
export { type BenchworkPoint, type BranchConnector, 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, FREEMO_ENDPLATE_TRACK_FASCIA_CLEARANCE_INCHES, FREEMO_ENDPLATE_WIDTH_MIN_INCHES, FREEMO_ENDPLATE_WIDTH_RECOMMENDED_INCHES, FREEMO_TRACK_SPACING_INCHES, type GeometryType, type IndustryLabelMode, type IndustrySpot, MAIN2_TRACK_ID, MAIN_TRACK_ID, type ModuleFeatures, type ModuleFootprint, type ModuleFootprintInput, type ModuleGeometryInput, type ModuleSchematicDoc, type ModuleTrackRow, N_CAR_LENGTH_INCHES, N_SCALE_RATIO, type OutlineFace, type ReturnLoopGeometry, type ReturnLoopShape, type SchematicBlock, type SchematicControlPoint, type SchematicCrossing, type SchematicEndplate, type SchematicEndplateTrack, type SchematicIndustry, type SchematicSection, type SchematicSignal, type SchematicTrack, type SchematicTurnout, type SectionAdjacency, type SectionFootprint, type SectionRelativePos, type SignalFacing, type SignalSide, type TrackConfig, type TrackRole, type TurnoutKind, WHOLE_MODULE_SECTION_ID, asModuleSchematic, benchworkBand, benchworkOutline, buildCrossover, buildPassingSiding, buildTransition, carCapacity, checkEndplateWidth, deriveEndplatePoses, divergeSideForHand, docToState, emptyEditorState, endplateFaceSegments, endplateLead, endplateTrackOffsetFor, endplateTrackOffsetInches, endplateWidthInches, fromSectionRelative, geometryTurnDegrees, inchesToScaleFeet, isLoopDoc, isTransitionTurnout, moduleCenterline, moduleFeatures, moduleFootprint, moduleLengthFromSections, moduleSections, nextId, poseNeedsManual, poseOverridesFromDoc, remapPos, returnLoop, sampleBenchworkOutline, samplePath, scaleFeetToInches, sectionAdjacency, sectionBand, sectionBreaksFromSections, sectionComponents, sectionFootprints, sectionNeighbours, sectionSpans, sectionSpansOrWhole, sectionedCenterline, sectionedEndPose, sliceCenterline, stateToDoc, toSectionRelative, trackMeetsEndplateIssues, trackPath };
|
|
1236
|
+
export { type BenchworkPoint, type BranchConnector, 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, FREEMO_ENDPLATE_TRACK_FASCIA_CLEARANCE_INCHES, FREEMO_ENDPLATE_WIDTH_MIN_INCHES, FREEMO_ENDPLATE_WIDTH_RECOMMENDED_INCHES, FREEMO_TRACK_SPACING_INCHES, type GeometryType, type IndustryLabelMode, type IndustrySpot, MAIN2_TRACK_ID, MAIN_TRACK_ID, type ModuleFeatures, type ModuleFootprint, type ModuleFootprintInput, type ModuleGeometryInput, type ModuleSchematicDoc, type ModuleTrackRow, N_CAR_LENGTH_INCHES, N_SCALE_RATIO, type OutlineFace, RAIL_GAUGE_INCHES, type ReturnLoopGeometry, type ReturnLoopShape, type SchematicBlock, type SchematicControlPoint, type SchematicCrossing, type SchematicEndplate, type SchematicEndplateTrack, type SchematicIndustry, type SchematicSection, type SchematicSignal, type SchematicTrack, type SchematicTurnout, type SectionAdjacency, type SectionFootprint, type SectionRelativePos, type SignalFacing, type SignalSide, TURNOUT_LEAD_INCHES_PER_FROG, type TrackConfig, type TrackRole, type TurnoutClosure, type TurnoutKind, WHOLE_MODULE_SECTION_ID, asModuleSchematic, benchworkBand, benchworkOutline, buildCrossover, buildPassingSiding, buildTransition, carCapacity, checkEndplateWidth, deriveEndplatePoses, divergeSideForHand, docToState, emptyEditorState, endplateFaceSegments, endplateLead, endplateTrackOffsetFor, endplateTrackOffsetInches, endplateWidthInches, fromSectionRelative, geometryTurnDegrees, inchesToScaleFeet, isLoopDoc, isTransitionTurnout, moduleCenterline, moduleFeatures, moduleFootprint, moduleLengthFromSections, moduleSections, nextId, poseNeedsManual, poseOverridesFromDoc, remapPos, returnLoop, sampleBenchworkOutline, samplePath, scaleFeetToInches, sectionAdjacency, sectionBand, sectionBreaksFromSections, sectionComponents, sectionFootprints, sectionNeighbours, sectionSpans, sectionSpansOrWhole, sectionedCenterline, sectionedEndPose, sliceCenterline, stateToDoc, toSectionRelative, trackMeetsEndplateIssues, trackPath, turnoutClosure };
|
package/dist/index.js
CHANGED
|
@@ -539,12 +539,23 @@ function carCapacity(fromPos, toPos, carLengthInches = N_CAR_LENGTH_INCHES) {
|
|
|
539
539
|
if (!(carLengthInches > 0)) return 0;
|
|
540
540
|
return Math.max(0, Math.floor(Math.abs(toPos - fromPos) / carLengthInches));
|
|
541
541
|
}
|
|
542
|
+
function healSelfDivergingTurnouts(turnouts) {
|
|
543
|
+
if (!turnouts?.some((t) => t.onTrack === t.divergeTrack)) return turnouts;
|
|
544
|
+
return turnouts.map((t) => {
|
|
545
|
+
if (t.onTrack !== t.divergeTrack) return t;
|
|
546
|
+
if (t.onTrack === MAIN_TRACK_ID) return { ...t, divergeTrack: MAIN2_TRACK_ID };
|
|
547
|
+
if (t.onTrack === MAIN2_TRACK_ID) return { ...t, divergeTrack: MAIN_TRACK_ID };
|
|
548
|
+
return t;
|
|
549
|
+
});
|
|
550
|
+
}
|
|
542
551
|
function asModuleSchematic(x) {
|
|
543
552
|
if (!x || typeof x !== "object") return null;
|
|
544
553
|
const d = x;
|
|
545
554
|
if (typeof d.version !== "number") return null;
|
|
546
555
|
if (!Array.isArray(d.endplates) || !Array.isArray(d.tracks)) return null;
|
|
547
|
-
|
|
556
|
+
const doc = d;
|
|
557
|
+
const healed = healSelfDivergingTurnouts(doc.turnouts);
|
|
558
|
+
return healed === doc.turnouts ? doc : { ...doc, turnouts: healed };
|
|
548
559
|
}
|
|
549
560
|
function emptyEditorState(lengthInches) {
|
|
550
561
|
return {
|
|
@@ -933,24 +944,19 @@ function docToState(doc, fallbackLength, moduleTracks = []) {
|
|
|
933
944
|
trackB: x.tracks?.[1] ?? MAIN_TRACK_ID
|
|
934
945
|
})),
|
|
935
946
|
extraTracks,
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
...t.size ? { size: t.size } : {},
|
|
950
|
-
...t.curved ? { curved: true } : {},
|
|
951
|
-
...t.flipped ? { flipped: true } : {}
|
|
952
|
-
};
|
|
953
|
-
}),
|
|
947
|
+
// Heal a turnout that diverges into the track it sits on (#172) with the
|
|
948
|
+
// same helper the read path uses, so the editor and every renderer agree.
|
|
949
|
+
turnouts: (healSelfDivergingTurnouts(d.turnouts) ?? []).map((t) => ({
|
|
950
|
+
id: t.id,
|
|
951
|
+
name: t.name ?? "",
|
|
952
|
+
pos: sc(t.pos),
|
|
953
|
+
onTrack: t.onTrack,
|
|
954
|
+
divergeTrack: t.divergeTrack,
|
|
955
|
+
kind: t.kind ?? "right",
|
|
956
|
+
...t.size ? { size: t.size } : {},
|
|
957
|
+
...t.curved ? { curved: true } : {},
|
|
958
|
+
...t.flipped ? { flipped: true } : {}
|
|
959
|
+
})),
|
|
954
960
|
controlPoints: readControlPoints(d, sc),
|
|
955
961
|
industries: (d.industries ?? []).map((ind) => ({
|
|
956
962
|
id: ind.id,
|
|
@@ -1082,6 +1088,24 @@ function buildCrossover(state) {
|
|
|
1082
1088
|
];
|
|
1083
1089
|
return { track, turnouts };
|
|
1084
1090
|
}
|
|
1091
|
+
var RAIL_GAUGE_INCHES = 0.354;
|
|
1092
|
+
var TURNOUT_LEAD_INCHES_PER_FROG = 0.482;
|
|
1093
|
+
function turnoutClosure(size, opts = {}) {
|
|
1094
|
+
const N = size > 0 ? size : 6;
|
|
1095
|
+
const g = opts.gaugeInches ?? RAIL_GAUGE_INCHES;
|
|
1096
|
+
const lead = Math.max(0.01, opts.leadInches ?? N * TURNOUT_LEAD_INCHES_PER_FROG);
|
|
1097
|
+
const frogSlope = 1 / N;
|
|
1098
|
+
const k = (lead / N - g) / (lead * lead);
|
|
1099
|
+
const switchSlope = Math.max(0, frogSlope - 2 * k * lead);
|
|
1100
|
+
return {
|
|
1101
|
+
lead,
|
|
1102
|
+
switchSlope,
|
|
1103
|
+
frogSlope,
|
|
1104
|
+
// Past the frog the route is straight at the frog angle — the closure curve
|
|
1105
|
+
// has done its work, so don't keep bending (that's the tangent Option 1).
|
|
1106
|
+
offsetAt: (s) => s <= lead ? switchSlope * s + k * s * s : g + frogSlope * (s - lead)
|
|
1107
|
+
};
|
|
1108
|
+
}
|
|
1085
1109
|
function divergeSideForHand(kind, stubDir, flipped) {
|
|
1086
1110
|
if (kind !== "left" && kind !== "right") return 0;
|
|
1087
1111
|
const s = (stubDir >= 0 ? 1 : -1) * (flipped ? -1 : 1);
|
|
@@ -1595,6 +1619,6 @@ function poseOverridesFromDoc(doc) {
|
|
|
1595
1619
|
return out;
|
|
1596
1620
|
}
|
|
1597
1621
|
|
|
1598
|
-
export { ENDPLATE_FASCIA_CLEAR_INCHES, ENDPLATE_LEAD_INCHES, 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, WHOLE_MODULE_SECTION_ID, asModuleSchematic, benchworkBand, benchworkOutline, buildCrossover, buildPassingSiding, buildTransition, carCapacity, checkEndplateWidth, deriveEndplatePoses, divergeSideForHand, docToState, emptyEditorState, endplateFaceSegments, endplateLead, endplateTrackOffsetFor, endplateTrackOffsetInches, endplateWidthInches, fromSectionRelative, geometryTurnDegrees, inchesToScaleFeet, isLoopDoc, isTransitionTurnout, moduleCenterline, moduleFeatures, moduleFootprint, moduleLengthFromSections, moduleSections, nextId, poseNeedsManual, poseOverridesFromDoc, remapPos, returnLoop, sampleBenchworkOutline, samplePath, scaleFeetToInches, sectionAdjacency, sectionBand, sectionBreaksFromSections, sectionComponents, sectionFootprints, sectionNeighbours, sectionSpans, sectionSpansOrWhole, sectionedCenterline, sectionedEndPose, sliceCenterline, stateToDoc, toSectionRelative, trackMeetsEndplateIssues, trackPath };
|
|
1622
|
+
export { ENDPLATE_FASCIA_CLEAR_INCHES, ENDPLATE_LEAD_INCHES, FREEMO_ENDPLATE_TRACK_FASCIA_CLEARANCE_INCHES, FREEMO_ENDPLATE_WIDTH_MIN_INCHES, FREEMO_ENDPLATE_WIDTH_RECOMMENDED_INCHES, FREEMO_TRACK_SPACING_INCHES, MAIN2_TRACK_ID, MAIN_TRACK_ID, N_CAR_LENGTH_INCHES, N_SCALE_RATIO, RAIL_GAUGE_INCHES, TURNOUT_LEAD_INCHES_PER_FROG, WHOLE_MODULE_SECTION_ID, asModuleSchematic, benchworkBand, benchworkOutline, buildCrossover, buildPassingSiding, buildTransition, carCapacity, checkEndplateWidth, deriveEndplatePoses, divergeSideForHand, docToState, emptyEditorState, endplateFaceSegments, endplateLead, endplateTrackOffsetFor, endplateTrackOffsetInches, endplateWidthInches, fromSectionRelative, geometryTurnDegrees, inchesToScaleFeet, isLoopDoc, isTransitionTurnout, moduleCenterline, moduleFeatures, moduleFootprint, moduleLengthFromSections, moduleSections, nextId, poseNeedsManual, poseOverridesFromDoc, remapPos, returnLoop, sampleBenchworkOutline, samplePath, scaleFeetToInches, sectionAdjacency, sectionBand, sectionBreaksFromSections, sectionComponents, sectionFootprints, sectionNeighbours, sectionSpans, sectionSpansOrWhole, sectionedCenterline, sectionedEndPose, sliceCenterline, stateToDoc, toSectionRelative, trackMeetsEndplateIssues, trackPath, turnoutClosure };
|
|
1599
1623
|
//# sourceMappingURL=index.js.map
|
|
1600
1624
|
//# sourceMappingURL=index.js.map
|