@willcgage/module-schematic 0.51.0 → 0.53.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 +197 -0
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +171 -6
- package/dist/index.d.ts +171 -6
- package/dist/index.js +190 -1
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.d.cts
CHANGED
|
@@ -1034,12 +1034,177 @@ interface ModuleFeatures {
|
|
|
1034
1034
|
*/
|
|
1035
1035
|
/** N-scale track gauge, inches (9 mm). */
|
|
1036
1036
|
declare const RAIL_GAUGE_INCHES = 0.354;
|
|
1037
|
-
/**
|
|
1038
|
-
*
|
|
1039
|
-
*
|
|
1040
|
-
* #173) ⇒ 3.375 / 7.
|
|
1041
|
-
*
|
|
1037
|
+
/**
|
|
1038
|
+
* ⚠️ **REFUTED AS A GENERAL RULE — LAST RESORT ONLY.** Points→frog for a #1
|
|
1039
|
+
* frog, i.e. a turnout's LEAD if lead were proportional to frog number. From an
|
|
1040
|
+
* Atlas code 55 #7 measuring 3⅜″ (Steve Branton, #173) ⇒ 3.375 / 7.
|
|
1041
|
+
*
|
|
1042
|
+
* It is NOT proportional. Measured lead ÷ N: **#7 = 0.482, #10 = 0.419.** The
|
|
1043
|
+
* rule predicted 4.82″ for the #10; it measures 4³⁄₁₆″ — **13% over**. Atlas
|
|
1044
|
+
* compress the lead on long turnouts, just as they compress overall length.
|
|
1045
|
+
*
|
|
1046
|
+
* Prefer {@link leadInchesForSize}, which returns a real part's measurement when
|
|
1047
|
+
* one exists and only falls back here for sizes nothing in the library covers.
|
|
1048
|
+
* A fallback at N ≥ 10 will read LONG.
|
|
1049
|
+
*
|
|
1050
|
+
* BETTER MODEL (hypothesis, not yet enough data): a roughly CONSTANT SWITCH
|
|
1051
|
+
* ANGLE α — the angle the point rails leave the stock rail at — which is real
|
|
1052
|
+
* prototype practice, points being planed to a standard angle. Inverting
|
|
1053
|
+
* {@link turnoutClosure} gives `lead = 2g / (1/N + α)`, and the two measured
|
|
1054
|
+
* leads agree to 3%: #7 ⇒ α = 0.0669, #10 ⇒ α = 0.0691. If it holds, lead is
|
|
1055
|
+
* whatever falls out of a fixed point angle meeting the frog, not a multiple
|
|
1056
|
+
* of N. DISCRIMINATING TEST: at α ≈ 0.068 the #5's lead is **2.64″**, where
|
|
1057
|
+
* this constant says 2.41″ — measure a physical 2050 to settle it.
|
|
1058
|
+
*/
|
|
1042
1059
|
declare const TURNOUT_LEAD_INCHES_PER_FROG = 0.482;
|
|
1060
|
+
/** Where a dimension came from. `derived` = we computed it from another part or
|
|
1061
|
+
* a rule of thumb; `unverified` = plausible but unconfirmed — treat with care. */
|
|
1062
|
+
type DimensionSource = "manufacturer" | "measured" | "derived" | "unverified";
|
|
1063
|
+
interface PartDimension {
|
|
1064
|
+
inches: number;
|
|
1065
|
+
source: DimensionSource;
|
|
1066
|
+
/** Who measured it / which spec, so it can be re-checked. */
|
|
1067
|
+
note?: string;
|
|
1068
|
+
}
|
|
1069
|
+
/** Same provenance discipline as {@link PartDimension}, for angles. */
|
|
1070
|
+
interface PartAngle {
|
|
1071
|
+
deg: number;
|
|
1072
|
+
source: DimensionSource;
|
|
1073
|
+
note?: string;
|
|
1074
|
+
}
|
|
1075
|
+
interface TrackPart {
|
|
1076
|
+
/** Stable slug, e.g. "atlas-c55-n-7". */
|
|
1077
|
+
id: string;
|
|
1078
|
+
manufacturer: string;
|
|
1079
|
+
/** Product line — "Code 55", "Code 80". */
|
|
1080
|
+
line: string;
|
|
1081
|
+
scale: "N";
|
|
1082
|
+
name: string;
|
|
1083
|
+
kind: "turnout" | "wye" | "curved-turnout" | "crossing";
|
|
1084
|
+
/** Manufacturer part numbers by hand, where the part has a hand. */
|
|
1085
|
+
partNumbers?: {
|
|
1086
|
+
left?: string;
|
|
1087
|
+
right?: string;
|
|
1088
|
+
single?: string;
|
|
1089
|
+
};
|
|
1090
|
+
/** Frog number N (the 1:N ratio). Definitional, so no provenance needed. */
|
|
1091
|
+
frogNumber?: number;
|
|
1092
|
+
/** The angle the part ACTUALLY diverges at, where that's known to differ from
|
|
1093
|
+
* the theoretical `atan(1/N)`. Atlas appear to build to SECTIONAL angles
|
|
1094
|
+
* (multiples of 11.25° = 1/32 turn) rather than true frog ratios, so a "#5" is
|
|
1095
|
+
* 11.25° not 11.31°. ⚠️ Currently below our drawing resolution — 0.06° over a
|
|
1096
|
+
* 6″ turnout is 0.006″ — so nothing uses this yet. Recorded because it's a
|
|
1097
|
+
* real property of the product and will matter if we ever check whether a part
|
|
1098
|
+
* mates with sectional track. */
|
|
1099
|
+
actualAngle?: PartAngle;
|
|
1100
|
+
/** Points → frog. The number that decides where a turnout's throat lands. */
|
|
1101
|
+
lead?: PartDimension;
|
|
1102
|
+
/** End of the tie strip → the point tips. Where the working turnout starts
|
|
1103
|
+
* inside its moulding; the rest of that end is plain approach track. Needed to
|
|
1104
|
+
* fit a part into a space, and by any renderer drawing the real outline. */
|
|
1105
|
+
pointsOffset?: PartDimension;
|
|
1106
|
+
/** End-to-end length of the part. */
|
|
1107
|
+
overallLength?: PartDimension;
|
|
1108
|
+
/** Diverging route radius (straight turnouts). */
|
|
1109
|
+
divergingRadius?: PartDimension;
|
|
1110
|
+
/** Curved turnouts: the two concentric radii. */
|
|
1111
|
+
outerRadius?: PartDimension;
|
|
1112
|
+
innerRadius?: PartDimension;
|
|
1113
|
+
/** Crossing angle, degrees. */
|
|
1114
|
+
crossingAngleDeg?: number;
|
|
1115
|
+
}
|
|
1116
|
+
/** N-scale code 55 rail height, inches — Atlas publish .055″. */
|
|
1117
|
+
declare const CODE55_RAIL_HEIGHT_INCHES = 0.055;
|
|
1118
|
+
/**
|
|
1119
|
+
* Atlas N-scale Code 55 — the Free-moN mainstay.
|
|
1120
|
+
*
|
|
1121
|
+
* ⚠️ Atlas do not publish leads or overall lengths for the straight turnouts.
|
|
1122
|
+
* Everything here traces to physical measurements: Steve Branton's #7 lead
|
|
1123
|
+
* (3⅜″ points→frog, #173) and Will Gage's #5/#7/#10 overall lengths plus the
|
|
1124
|
+
* #10's lead and points offset. The #5's lead and the wye's are still derived.
|
|
1125
|
+
*
|
|
1126
|
+
* NEITHER DIMENSION IS PROPORTIONAL TO FROG NUMBER. Both rules that assumed so
|
|
1127
|
+
* have now been tested and failed:
|
|
1128
|
+
* - **Lead does NOT scale with N.** Measured lead ÷ N: #7 = 0.482, #10 = 0.419.
|
|
1129
|
+
* {@link TURNOUT_LEAD_INCHES_PER_FROG} predicted 4.82″ for the #10 against a
|
|
1130
|
+
* measured 4³⁄₁₆″ — 13% over. See that constant for the constant-switch-angle
|
|
1131
|
+
* model that fits both measurements, and the test that would confirm it.
|
|
1132
|
+
* - ⭐ **Overall length is NOT A FUNCTION OF N AT ALL.** Measured: #5 = 6.00″,
|
|
1133
|
+
* **#7 = 6.00″**, #10 = 8.00″. The #5 and #7 are the SAME LENGTH — Atlas mould
|
|
1134
|
+
* them on a shared 6″ tie strip and vary only the frog angle and where the frog
|
|
1135
|
+
* sits. NEVER derive a length from a frog number; look the part up.
|
|
1136
|
+
*
|
|
1137
|
+
* This is what the frog-number definition actually says, and it's easy to forget:
|
|
1138
|
+
* #N fixes the DIVERGENCE RATE AT THE FROG (1 across per N along). It says
|
|
1139
|
+
* nothing about how much tie strip the manufacturer wraps around it. Angle is
|
|
1140
|
+
* geometry; length is packaging. Hence the lead — where the frog falls inside
|
|
1141
|
+
* that strip — is the only dimension worth chasing.
|
|
1142
|
+
*
|
|
1143
|
+
* (Lengths are whole inches and the #5 diverges at exactly 1/32 of a circle, so
|
|
1144
|
+
* Atlas do seem to work to round numbers — but a prediction that the #7 would
|
|
1145
|
+
* therefore be 7.00″ was tested and FAILED. Round, but not one inch per frog.)
|
|
1146
|
+
*/
|
|
1147
|
+
declare const ATLAS_CODE55_N: TrackPart[];
|
|
1148
|
+
/** Every built-in part, across manufacturers. */
|
|
1149
|
+
declare const BUILT_IN_TRACK_PARTS: TrackPart[];
|
|
1150
|
+
/** Look a part up by its slug. */
|
|
1151
|
+
declare function trackPart(id: string, library?: TrackPart[]): TrackPart | null;
|
|
1152
|
+
/** The closest built-in turnout for a frog number — what a bare `size` maps to
|
|
1153
|
+
* when a turnout names no part. Exact match wins; otherwise the nearest frog. */
|
|
1154
|
+
declare function turnoutPartForSize(size: number, library?: TrackPart[]): TrackPart | null;
|
|
1155
|
+
/** The lead to draw a turnout of this size with — a real part's measurement when
|
|
1156
|
+
* we have one, else the per-frog rule. Keeps `frogLegOf` honest without it
|
|
1157
|
+
* needing to know the library exists. */
|
|
1158
|
+
declare function leadInchesForSize(size: number, library?: TrackPart[]): number;
|
|
1159
|
+
/** One drawn piece of a part's geometry, in the part's own local frame. */
|
|
1160
|
+
type PartSegment = {
|
|
1161
|
+
kind: "straight";
|
|
1162
|
+
x0: number;
|
|
1163
|
+
y0: number;
|
|
1164
|
+
x1: number;
|
|
1165
|
+
y1: number;
|
|
1166
|
+
} | {
|
|
1167
|
+
kind: "curve";
|
|
1168
|
+
radius: number;
|
|
1169
|
+
cx: number;
|
|
1170
|
+
cy: number;
|
|
1171
|
+
startDeg: number;
|
|
1172
|
+
extentDeg: number;
|
|
1173
|
+
};
|
|
1174
|
+
/** A connection point on an imported part: position plus the tangent it faces. */
|
|
1175
|
+
interface PartEnd {
|
|
1176
|
+
x: number;
|
|
1177
|
+
y: number;
|
|
1178
|
+
angleDeg: number;
|
|
1179
|
+
}
|
|
1180
|
+
interface ImportedPart {
|
|
1181
|
+
/** Raw title, tab-separated in the file: manufacturer, name, part number. */
|
|
1182
|
+
title: string;
|
|
1183
|
+
manufacturer?: string;
|
|
1184
|
+
name?: string;
|
|
1185
|
+
partNumber?: string;
|
|
1186
|
+
scale?: string;
|
|
1187
|
+
ends: PartEnd[];
|
|
1188
|
+
segments: PartSegment[];
|
|
1189
|
+
}
|
|
1190
|
+
/**
|
|
1191
|
+
* Parse an XTrkCAD `.xtp` parameter file into parts.
|
|
1192
|
+
*
|
|
1193
|
+
* The format is plain text, record-per-line inside `TURNOUT … END` blocks:
|
|
1194
|
+
* `E x y angle` an endpoint
|
|
1195
|
+
* `S layer width x0 y0 x1 y1` a straight segment
|
|
1196
|
+
* `C layer width radius cx cy a0 ext` a curved segment (radius signed by hand)
|
|
1197
|
+
*
|
|
1198
|
+
* We parse an owner's OWN file — nothing from XTrkCAD is redistributed. Their
|
|
1199
|
+
* geometry is taken as authoritative for part OUTLINES; ⚠️ don't infer frog
|
|
1200
|
+
* positions from it, the shipped Atlas file is internally inconsistent (it puts
|
|
1201
|
+
* the #5's frog further out than the #7's, which is physically impossible).
|
|
1202
|
+
*/
|
|
1203
|
+
declare function parseXtpLibrary(text: string): ImportedPart[];
|
|
1204
|
+
/** Sample an imported part's segments into polylines, in the part's own frame —
|
|
1205
|
+
* XTrkCAD angles run CLOCKWISE FROM NORTH, so a point on a curve is
|
|
1206
|
+
* `(cx + r·sin a, cy + r·cos a)`, not the usual cos/sin. */
|
|
1207
|
+
declare function samplePartSegments(segments: PartSegment[], stepsPerCurve?: number): BenchworkPoint[][];
|
|
1043
1208
|
/** A turnout's CLOSURE — the diverging route's lateral offset from the through
|
|
1044
1209
|
* route, from the points (s = 0) to the frog (s = lead) and beyond.
|
|
1045
1210
|
*
|
|
@@ -1233,4 +1398,4 @@ declare function poseOverridesFromDoc(doc: ModuleSchematicDoc): Record<string, {
|
|
|
1233
1398
|
heading: number;
|
|
1234
1399
|
}>;
|
|
1235
1400
|
|
|
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 };
|
|
1401
|
+
export { ATLAS_CODE55_N, BUILT_IN_TRACK_PARTS, type BenchworkPoint, type BranchConnector, CODE55_RAIL_HEIGHT_INCHES, 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, FREEMO_ENDPLATE_TRACK_FASCIA_CLEARANCE_INCHES, FREEMO_ENDPLATE_WIDTH_MIN_INCHES, FREEMO_ENDPLATE_WIDTH_RECOMMENDED_INCHES, FREEMO_TRACK_SPACING_INCHES, type GeometryType, type ImportedPart, type IndustryLabelMode, type IndustrySpot, MAIN2_TRACK_ID, MAIN_TRACK_ID, type ModuleFeatures, type ModuleFootprint, type ModuleFootprintInput, type ModuleGeometryInput, type ModuleSchematicDoc, type ModuleTrackRow, N_CAR_LENGTH_INCHES, N_SCALE_RATIO, type OutlineFace, type PartAngle, type PartDimension, type PartEnd, type PartSegment, RAIL_GAUGE_INCHES, type ReturnLoopGeometry, type ReturnLoopShape, type SchematicBlock, type SchematicControlPoint, type SchematicCrossing, type SchematicEndplate, type SchematicEndplateTrack, type SchematicIndustry, type SchematicSection, type SchematicSignal, type SchematicTrack, type SchematicTurnout, type SectionAdjacency, type SectionFootprint, type SectionRelativePos, type SignalFacing, type SignalSide, TURNOUT_LEAD_INCHES_PER_FROG, type TrackConfig, type TrackPart, type TrackRole, type TurnoutClosure, type TurnoutKind, WHOLE_MODULE_SECTION_ID, asModuleSchematic, benchworkBand, benchworkOutline, buildCrossover, buildPassingSiding, buildTransition, carCapacity, checkEndplateWidth, deriveEndplatePoses, divergeSideForHand, docToState, emptyEditorState, endplateFaceSegments, endplateLead, endplateTrackOffsetFor, endplateTrackOffsetInches, endplateWidthInches, fromSectionRelative, geometryTurnDegrees, inchesToScaleFeet, isLoopDoc, isTransitionTurnout, leadInchesForSize, moduleCenterline, moduleFeatures, moduleFootprint, moduleLengthFromSections, moduleSections, nextId, parseXtpLibrary, poseNeedsManual, poseOverridesFromDoc, remapPos, returnLoop, sampleBenchworkOutline, samplePartSegments, samplePath, scaleFeetToInches, sectionAdjacency, sectionBand, sectionBreaksFromSections, sectionComponents, sectionFootprints, sectionNeighbours, sectionSpans, sectionSpansOrWhole, sectionedCenterline, sectionedEndPose, sliceCenterline, stateToDoc, toSectionRelative, trackMeetsEndplateIssues, trackPart, trackPath, turnoutClosure, turnoutPartForSize };
|
package/dist/index.d.ts
CHANGED
|
@@ -1034,12 +1034,177 @@ interface ModuleFeatures {
|
|
|
1034
1034
|
*/
|
|
1035
1035
|
/** N-scale track gauge, inches (9 mm). */
|
|
1036
1036
|
declare const RAIL_GAUGE_INCHES = 0.354;
|
|
1037
|
-
/**
|
|
1038
|
-
*
|
|
1039
|
-
*
|
|
1040
|
-
* #173) ⇒ 3.375 / 7.
|
|
1041
|
-
*
|
|
1037
|
+
/**
|
|
1038
|
+
* ⚠️ **REFUTED AS A GENERAL RULE — LAST RESORT ONLY.** Points→frog for a #1
|
|
1039
|
+
* frog, i.e. a turnout's LEAD if lead were proportional to frog number. From an
|
|
1040
|
+
* Atlas code 55 #7 measuring 3⅜″ (Steve Branton, #173) ⇒ 3.375 / 7.
|
|
1041
|
+
*
|
|
1042
|
+
* It is NOT proportional. Measured lead ÷ N: **#7 = 0.482, #10 = 0.419.** The
|
|
1043
|
+
* rule predicted 4.82″ for the #10; it measures 4³⁄₁₆″ — **13% over**. Atlas
|
|
1044
|
+
* compress the lead on long turnouts, just as they compress overall length.
|
|
1045
|
+
*
|
|
1046
|
+
* Prefer {@link leadInchesForSize}, which returns a real part's measurement when
|
|
1047
|
+
* one exists and only falls back here for sizes nothing in the library covers.
|
|
1048
|
+
* A fallback at N ≥ 10 will read LONG.
|
|
1049
|
+
*
|
|
1050
|
+
* BETTER MODEL (hypothesis, not yet enough data): a roughly CONSTANT SWITCH
|
|
1051
|
+
* ANGLE α — the angle the point rails leave the stock rail at — which is real
|
|
1052
|
+
* prototype practice, points being planed to a standard angle. Inverting
|
|
1053
|
+
* {@link turnoutClosure} gives `lead = 2g / (1/N + α)`, and the two measured
|
|
1054
|
+
* leads agree to 3%: #7 ⇒ α = 0.0669, #10 ⇒ α = 0.0691. If it holds, lead is
|
|
1055
|
+
* whatever falls out of a fixed point angle meeting the frog, not a multiple
|
|
1056
|
+
* of N. DISCRIMINATING TEST: at α ≈ 0.068 the #5's lead is **2.64″**, where
|
|
1057
|
+
* this constant says 2.41″ — measure a physical 2050 to settle it.
|
|
1058
|
+
*/
|
|
1042
1059
|
declare const TURNOUT_LEAD_INCHES_PER_FROG = 0.482;
|
|
1060
|
+
/** Where a dimension came from. `derived` = we computed it from another part or
|
|
1061
|
+
* a rule of thumb; `unverified` = plausible but unconfirmed — treat with care. */
|
|
1062
|
+
type DimensionSource = "manufacturer" | "measured" | "derived" | "unverified";
|
|
1063
|
+
interface PartDimension {
|
|
1064
|
+
inches: number;
|
|
1065
|
+
source: DimensionSource;
|
|
1066
|
+
/** Who measured it / which spec, so it can be re-checked. */
|
|
1067
|
+
note?: string;
|
|
1068
|
+
}
|
|
1069
|
+
/** Same provenance discipline as {@link PartDimension}, for angles. */
|
|
1070
|
+
interface PartAngle {
|
|
1071
|
+
deg: number;
|
|
1072
|
+
source: DimensionSource;
|
|
1073
|
+
note?: string;
|
|
1074
|
+
}
|
|
1075
|
+
interface TrackPart {
|
|
1076
|
+
/** Stable slug, e.g. "atlas-c55-n-7". */
|
|
1077
|
+
id: string;
|
|
1078
|
+
manufacturer: string;
|
|
1079
|
+
/** Product line — "Code 55", "Code 80". */
|
|
1080
|
+
line: string;
|
|
1081
|
+
scale: "N";
|
|
1082
|
+
name: string;
|
|
1083
|
+
kind: "turnout" | "wye" | "curved-turnout" | "crossing";
|
|
1084
|
+
/** Manufacturer part numbers by hand, where the part has a hand. */
|
|
1085
|
+
partNumbers?: {
|
|
1086
|
+
left?: string;
|
|
1087
|
+
right?: string;
|
|
1088
|
+
single?: string;
|
|
1089
|
+
};
|
|
1090
|
+
/** Frog number N (the 1:N ratio). Definitional, so no provenance needed. */
|
|
1091
|
+
frogNumber?: number;
|
|
1092
|
+
/** The angle the part ACTUALLY diverges at, where that's known to differ from
|
|
1093
|
+
* the theoretical `atan(1/N)`. Atlas appear to build to SECTIONAL angles
|
|
1094
|
+
* (multiples of 11.25° = 1/32 turn) rather than true frog ratios, so a "#5" is
|
|
1095
|
+
* 11.25° not 11.31°. ⚠️ Currently below our drawing resolution — 0.06° over a
|
|
1096
|
+
* 6″ turnout is 0.006″ — so nothing uses this yet. Recorded because it's a
|
|
1097
|
+
* real property of the product and will matter if we ever check whether a part
|
|
1098
|
+
* mates with sectional track. */
|
|
1099
|
+
actualAngle?: PartAngle;
|
|
1100
|
+
/** Points → frog. The number that decides where a turnout's throat lands. */
|
|
1101
|
+
lead?: PartDimension;
|
|
1102
|
+
/** End of the tie strip → the point tips. Where the working turnout starts
|
|
1103
|
+
* inside its moulding; the rest of that end is plain approach track. Needed to
|
|
1104
|
+
* fit a part into a space, and by any renderer drawing the real outline. */
|
|
1105
|
+
pointsOffset?: PartDimension;
|
|
1106
|
+
/** End-to-end length of the part. */
|
|
1107
|
+
overallLength?: PartDimension;
|
|
1108
|
+
/** Diverging route radius (straight turnouts). */
|
|
1109
|
+
divergingRadius?: PartDimension;
|
|
1110
|
+
/** Curved turnouts: the two concentric radii. */
|
|
1111
|
+
outerRadius?: PartDimension;
|
|
1112
|
+
innerRadius?: PartDimension;
|
|
1113
|
+
/** Crossing angle, degrees. */
|
|
1114
|
+
crossingAngleDeg?: number;
|
|
1115
|
+
}
|
|
1116
|
+
/** N-scale code 55 rail height, inches — Atlas publish .055″. */
|
|
1117
|
+
declare const CODE55_RAIL_HEIGHT_INCHES = 0.055;
|
|
1118
|
+
/**
|
|
1119
|
+
* Atlas N-scale Code 55 — the Free-moN mainstay.
|
|
1120
|
+
*
|
|
1121
|
+
* ⚠️ Atlas do not publish leads or overall lengths for the straight turnouts.
|
|
1122
|
+
* Everything here traces to physical measurements: Steve Branton's #7 lead
|
|
1123
|
+
* (3⅜″ points→frog, #173) and Will Gage's #5/#7/#10 overall lengths plus the
|
|
1124
|
+
* #10's lead and points offset. The #5's lead and the wye's are still derived.
|
|
1125
|
+
*
|
|
1126
|
+
* NEITHER DIMENSION IS PROPORTIONAL TO FROG NUMBER. Both rules that assumed so
|
|
1127
|
+
* have now been tested and failed:
|
|
1128
|
+
* - **Lead does NOT scale with N.** Measured lead ÷ N: #7 = 0.482, #10 = 0.419.
|
|
1129
|
+
* {@link TURNOUT_LEAD_INCHES_PER_FROG} predicted 4.82″ for the #10 against a
|
|
1130
|
+
* measured 4³⁄₁₆″ — 13% over. See that constant for the constant-switch-angle
|
|
1131
|
+
* model that fits both measurements, and the test that would confirm it.
|
|
1132
|
+
* - ⭐ **Overall length is NOT A FUNCTION OF N AT ALL.** Measured: #5 = 6.00″,
|
|
1133
|
+
* **#7 = 6.00″**, #10 = 8.00″. The #5 and #7 are the SAME LENGTH — Atlas mould
|
|
1134
|
+
* them on a shared 6″ tie strip and vary only the frog angle and where the frog
|
|
1135
|
+
* sits. NEVER derive a length from a frog number; look the part up.
|
|
1136
|
+
*
|
|
1137
|
+
* This is what the frog-number definition actually says, and it's easy to forget:
|
|
1138
|
+
* #N fixes the DIVERGENCE RATE AT THE FROG (1 across per N along). It says
|
|
1139
|
+
* nothing about how much tie strip the manufacturer wraps around it. Angle is
|
|
1140
|
+
* geometry; length is packaging. Hence the lead — where the frog falls inside
|
|
1141
|
+
* that strip — is the only dimension worth chasing.
|
|
1142
|
+
*
|
|
1143
|
+
* (Lengths are whole inches and the #5 diverges at exactly 1/32 of a circle, so
|
|
1144
|
+
* Atlas do seem to work to round numbers — but a prediction that the #7 would
|
|
1145
|
+
* therefore be 7.00″ was tested and FAILED. Round, but not one inch per frog.)
|
|
1146
|
+
*/
|
|
1147
|
+
declare const ATLAS_CODE55_N: TrackPart[];
|
|
1148
|
+
/** Every built-in part, across manufacturers. */
|
|
1149
|
+
declare const BUILT_IN_TRACK_PARTS: TrackPart[];
|
|
1150
|
+
/** Look a part up by its slug. */
|
|
1151
|
+
declare function trackPart(id: string, library?: TrackPart[]): TrackPart | null;
|
|
1152
|
+
/** The closest built-in turnout for a frog number — what a bare `size` maps to
|
|
1153
|
+
* when a turnout names no part. Exact match wins; otherwise the nearest frog. */
|
|
1154
|
+
declare function turnoutPartForSize(size: number, library?: TrackPart[]): TrackPart | null;
|
|
1155
|
+
/** The lead to draw a turnout of this size with — a real part's measurement when
|
|
1156
|
+
* we have one, else the per-frog rule. Keeps `frogLegOf` honest without it
|
|
1157
|
+
* needing to know the library exists. */
|
|
1158
|
+
declare function leadInchesForSize(size: number, library?: TrackPart[]): number;
|
|
1159
|
+
/** One drawn piece of a part's geometry, in the part's own local frame. */
|
|
1160
|
+
type PartSegment = {
|
|
1161
|
+
kind: "straight";
|
|
1162
|
+
x0: number;
|
|
1163
|
+
y0: number;
|
|
1164
|
+
x1: number;
|
|
1165
|
+
y1: number;
|
|
1166
|
+
} | {
|
|
1167
|
+
kind: "curve";
|
|
1168
|
+
radius: number;
|
|
1169
|
+
cx: number;
|
|
1170
|
+
cy: number;
|
|
1171
|
+
startDeg: number;
|
|
1172
|
+
extentDeg: number;
|
|
1173
|
+
};
|
|
1174
|
+
/** A connection point on an imported part: position plus the tangent it faces. */
|
|
1175
|
+
interface PartEnd {
|
|
1176
|
+
x: number;
|
|
1177
|
+
y: number;
|
|
1178
|
+
angleDeg: number;
|
|
1179
|
+
}
|
|
1180
|
+
interface ImportedPart {
|
|
1181
|
+
/** Raw title, tab-separated in the file: manufacturer, name, part number. */
|
|
1182
|
+
title: string;
|
|
1183
|
+
manufacturer?: string;
|
|
1184
|
+
name?: string;
|
|
1185
|
+
partNumber?: string;
|
|
1186
|
+
scale?: string;
|
|
1187
|
+
ends: PartEnd[];
|
|
1188
|
+
segments: PartSegment[];
|
|
1189
|
+
}
|
|
1190
|
+
/**
|
|
1191
|
+
* Parse an XTrkCAD `.xtp` parameter file into parts.
|
|
1192
|
+
*
|
|
1193
|
+
* The format is plain text, record-per-line inside `TURNOUT … END` blocks:
|
|
1194
|
+
* `E x y angle` an endpoint
|
|
1195
|
+
* `S layer width x0 y0 x1 y1` a straight segment
|
|
1196
|
+
* `C layer width radius cx cy a0 ext` a curved segment (radius signed by hand)
|
|
1197
|
+
*
|
|
1198
|
+
* We parse an owner's OWN file — nothing from XTrkCAD is redistributed. Their
|
|
1199
|
+
* geometry is taken as authoritative for part OUTLINES; ⚠️ don't infer frog
|
|
1200
|
+
* positions from it, the shipped Atlas file is internally inconsistent (it puts
|
|
1201
|
+
* the #5's frog further out than the #7's, which is physically impossible).
|
|
1202
|
+
*/
|
|
1203
|
+
declare function parseXtpLibrary(text: string): ImportedPart[];
|
|
1204
|
+
/** Sample an imported part's segments into polylines, in the part's own frame —
|
|
1205
|
+
* XTrkCAD angles run CLOCKWISE FROM NORTH, so a point on a curve is
|
|
1206
|
+
* `(cx + r·sin a, cy + r·cos a)`, not the usual cos/sin. */
|
|
1207
|
+
declare function samplePartSegments(segments: PartSegment[], stepsPerCurve?: number): BenchworkPoint[][];
|
|
1043
1208
|
/** A turnout's CLOSURE — the diverging route's lateral offset from the through
|
|
1044
1209
|
* route, from the points (s = 0) to the frog (s = lead) and beyond.
|
|
1045
1210
|
*
|
|
@@ -1233,4 +1398,4 @@ declare function poseOverridesFromDoc(doc: ModuleSchematicDoc): Record<string, {
|
|
|
1233
1398
|
heading: number;
|
|
1234
1399
|
}>;
|
|
1235
1400
|
|
|
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 };
|
|
1401
|
+
export { ATLAS_CODE55_N, BUILT_IN_TRACK_PARTS, type BenchworkPoint, type BranchConnector, CODE55_RAIL_HEIGHT_INCHES, 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, FREEMO_ENDPLATE_TRACK_FASCIA_CLEARANCE_INCHES, FREEMO_ENDPLATE_WIDTH_MIN_INCHES, FREEMO_ENDPLATE_WIDTH_RECOMMENDED_INCHES, FREEMO_TRACK_SPACING_INCHES, type GeometryType, type ImportedPart, type IndustryLabelMode, type IndustrySpot, MAIN2_TRACK_ID, MAIN_TRACK_ID, type ModuleFeatures, type ModuleFootprint, type ModuleFootprintInput, type ModuleGeometryInput, type ModuleSchematicDoc, type ModuleTrackRow, N_CAR_LENGTH_INCHES, N_SCALE_RATIO, type OutlineFace, type PartAngle, type PartDimension, type PartEnd, type PartSegment, RAIL_GAUGE_INCHES, type ReturnLoopGeometry, type ReturnLoopShape, type SchematicBlock, type SchematicControlPoint, type SchematicCrossing, type SchematicEndplate, type SchematicEndplateTrack, type SchematicIndustry, type SchematicSection, type SchematicSignal, type SchematicTrack, type SchematicTurnout, type SectionAdjacency, type SectionFootprint, type SectionRelativePos, type SignalFacing, type SignalSide, TURNOUT_LEAD_INCHES_PER_FROG, type TrackConfig, type TrackPart, type TrackRole, type TurnoutClosure, type TurnoutKind, WHOLE_MODULE_SECTION_ID, asModuleSchematic, benchworkBand, benchworkOutline, buildCrossover, buildPassingSiding, buildTransition, carCapacity, checkEndplateWidth, deriveEndplatePoses, divergeSideForHand, docToState, emptyEditorState, endplateFaceSegments, endplateLead, endplateTrackOffsetFor, endplateTrackOffsetInches, endplateWidthInches, fromSectionRelative, geometryTurnDegrees, inchesToScaleFeet, isLoopDoc, isTransitionTurnout, leadInchesForSize, moduleCenterline, moduleFeatures, moduleFootprint, moduleLengthFromSections, moduleSections, nextId, parseXtpLibrary, poseNeedsManual, poseOverridesFromDoc, remapPos, returnLoop, sampleBenchworkOutline, samplePartSegments, samplePath, scaleFeetToInches, sectionAdjacency, sectionBand, sectionBreaksFromSections, sectionComponents, sectionFootprints, sectionNeighbours, sectionSpans, sectionSpansOrWhole, sectionedCenterline, sectionedEndPose, sliceCenterline, stateToDoc, toSectionRelative, trackMeetsEndplateIssues, trackPart, trackPath, turnoutClosure, turnoutPartForSize };
|
package/dist/index.js
CHANGED
|
@@ -1090,6 +1090,195 @@ function buildCrossover(state) {
|
|
|
1090
1090
|
}
|
|
1091
1091
|
var RAIL_GAUGE_INCHES = 0.354;
|
|
1092
1092
|
var TURNOUT_LEAD_INCHES_PER_FROG = 0.482;
|
|
1093
|
+
var CODE55_RAIL_HEIGHT_INCHES = 0.055;
|
|
1094
|
+
var ATLAS_CODE55_N = [
|
|
1095
|
+
{
|
|
1096
|
+
id: "atlas-c55-n-5",
|
|
1097
|
+
manufacturer: "Atlas",
|
|
1098
|
+
line: "Code 55",
|
|
1099
|
+
scale: "N",
|
|
1100
|
+
name: "#5 Turnout",
|
|
1101
|
+
kind: "turnout",
|
|
1102
|
+
partNumbers: { left: "2050", right: "2051" },
|
|
1103
|
+
frogNumber: 5,
|
|
1104
|
+
lead: {
|
|
1105
|
+
inches: 5 * TURNOUT_LEAD_INCHES_PER_FROG,
|
|
1106
|
+
source: "derived",
|
|
1107
|
+
note: "scaled from the measured #7 by frog number \u2014 not measured, BUT now corroborated: a photo read of Will's physical 2050 puts points\u2192frog at ~2.4\u2033, i.e. 0.48/frog against the #7's 0.482. Replace with a real measurement when someone puts a rule on it."
|
|
1108
|
+
},
|
|
1109
|
+
overallLength: {
|
|
1110
|
+
inches: 6,
|
|
1111
|
+
source: "measured",
|
|
1112
|
+
note: "Will Gage, physical Atlas 2050 (#5 LH), end tie to end tie"
|
|
1113
|
+
},
|
|
1114
|
+
actualAngle: {
|
|
1115
|
+
deg: 11.25,
|
|
1116
|
+
source: "unverified",
|
|
1117
|
+
note: "CORROBORATED: XTrkCAD N-atlasn55.xtp gives 11.250152\xB0, and AnyRail-forum trial-and-error (glakedylan, 2012) gives 11.25\xB0. Theory atan(1/5) = 11.310\xB0. 11.25\xB0 is exactly 1/32 turn \u2014 Atlas appear to build to sectional angles."
|
|
1118
|
+
}
|
|
1119
|
+
},
|
|
1120
|
+
{
|
|
1121
|
+
id: "atlas-c55-n-7",
|
|
1122
|
+
manufacturer: "Atlas",
|
|
1123
|
+
line: "Code 55",
|
|
1124
|
+
scale: "N",
|
|
1125
|
+
name: "#7 Turnout",
|
|
1126
|
+
kind: "turnout",
|
|
1127
|
+
partNumbers: { left: "2052", right: "2053" },
|
|
1128
|
+
frogNumber: 7,
|
|
1129
|
+
lead: {
|
|
1130
|
+
inches: 3.375,
|
|
1131
|
+
source: "measured",
|
|
1132
|
+
note: "Steve Branton, physical Atlas code 55 #7, 3\u215C\u2033 points\u2192frog (#173)"
|
|
1133
|
+
},
|
|
1134
|
+
overallLength: {
|
|
1135
|
+
inches: 6,
|
|
1136
|
+
source: "measured",
|
|
1137
|
+
note: "Will Gage, physical Atlas 2052 (#7), end tie to end tie \u2014 the SAME 6\u2033 as the #5, which is what proves length is not a function of frog number"
|
|
1138
|
+
},
|
|
1139
|
+
actualAngle: {
|
|
1140
|
+
deg: 8.13,
|
|
1141
|
+
source: "unverified",
|
|
1142
|
+
note: "DISPUTED: XTrkCAD .xtp says 8.1818\xB0, AnyRail forum says 8.125\xB0, theory atan(1/7) = 8.130\xB0. The sources disagree, so theory is used here."
|
|
1143
|
+
}
|
|
1144
|
+
},
|
|
1145
|
+
{
|
|
1146
|
+
id: "atlas-c55-n-10",
|
|
1147
|
+
manufacturer: "Atlas",
|
|
1148
|
+
line: "Code 55",
|
|
1149
|
+
scale: "N",
|
|
1150
|
+
name: "#10 Turnout",
|
|
1151
|
+
kind: "turnout",
|
|
1152
|
+
partNumbers: { left: "2054", right: "2055" },
|
|
1153
|
+
frogNumber: 10,
|
|
1154
|
+
lead: {
|
|
1155
|
+
inches: 4.1875,
|
|
1156
|
+
source: "measured",
|
|
1157
|
+
note: "Will Gage, physical Atlas 2054 (#10): points at 9/16\u2033 from the end, frog at 4.75\u2033 \u21D2 4\xB3\u2044\u2081\u2086\u2033. THIS IS THE MEASUREMENT THAT REFUTED the lead-per-frog rule \u2014 it predicted 4.82\u2033, 13% over."
|
|
1158
|
+
},
|
|
1159
|
+
pointsOffset: {
|
|
1160
|
+
inches: 0.5625,
|
|
1161
|
+
source: "measured",
|
|
1162
|
+
note: "Will Gage, physical Atlas 2054 (#10) \u2014 end of tie strip to point tips"
|
|
1163
|
+
},
|
|
1164
|
+
overallLength: {
|
|
1165
|
+
inches: 8,
|
|
1166
|
+
source: "measured",
|
|
1167
|
+
note: "Will Gage, physical Atlas 2054 (#10), end tie to end tie"
|
|
1168
|
+
},
|
|
1169
|
+
actualAngle: {
|
|
1170
|
+
deg: 5.74,
|
|
1171
|
+
source: "unverified",
|
|
1172
|
+
note: "CORROBORATED: XTrkCAD .xtp gives 5.739\xB0, AnyRail forum 5.75\xB0 \u2014 they agree with each other and not with theory atan(1/10) = 5.711\xB0."
|
|
1173
|
+
}
|
|
1174
|
+
},
|
|
1175
|
+
{
|
|
1176
|
+
id: "atlas-c55-n-wye",
|
|
1177
|
+
manufacturer: "Atlas",
|
|
1178
|
+
line: "Code 55",
|
|
1179
|
+
scale: "N",
|
|
1180
|
+
name: "#2.5 Wye",
|
|
1181
|
+
kind: "wye",
|
|
1182
|
+
partNumbers: { single: "2056" },
|
|
1183
|
+
frogNumber: 2.5,
|
|
1184
|
+
lead: {
|
|
1185
|
+
inches: 2.5 * TURNOUT_LEAD_INCHES_PER_FROG,
|
|
1186
|
+
source: "derived",
|
|
1187
|
+
note: "scaled from the measured #7 by frog number \u2014 not measured"
|
|
1188
|
+
}
|
|
1189
|
+
},
|
|
1190
|
+
{
|
|
1191
|
+
id: "atlas-c55-n-curved-21-15",
|
|
1192
|
+
manufacturer: "Atlas",
|
|
1193
|
+
line: "Code 55",
|
|
1194
|
+
scale: "N",
|
|
1195
|
+
name: 'Curved Turnout 21\xBC" / 15"',
|
|
1196
|
+
kind: "curved-turnout",
|
|
1197
|
+
partNumbers: { left: "2058", right: "2059" },
|
|
1198
|
+
outerRadius: { inches: 21.25, source: "manufacturer", note: "Atlas product listing" },
|
|
1199
|
+
innerRadius: { inches: 15, source: "manufacturer", note: "Atlas product listing" }
|
|
1200
|
+
}
|
|
1201
|
+
];
|
|
1202
|
+
var BUILT_IN_TRACK_PARTS = [...ATLAS_CODE55_N];
|
|
1203
|
+
function trackPart(id, library = BUILT_IN_TRACK_PARTS) {
|
|
1204
|
+
return library.find((p) => p.id === id) ?? null;
|
|
1205
|
+
}
|
|
1206
|
+
function turnoutPartForSize(size, library = BUILT_IN_TRACK_PARTS) {
|
|
1207
|
+
const turnouts = library.filter((p) => p.kind === "turnout" && p.frogNumber != null);
|
|
1208
|
+
if (!turnouts.length) return null;
|
|
1209
|
+
return turnouts.reduce(
|
|
1210
|
+
(best, p) => Math.abs(p.frogNumber - size) < Math.abs(best.frogNumber - size) ? p : best
|
|
1211
|
+
);
|
|
1212
|
+
}
|
|
1213
|
+
function leadInchesForSize(size, library = BUILT_IN_TRACK_PARTS) {
|
|
1214
|
+
const part = turnoutPartForSize(size, library);
|
|
1215
|
+
if (part?.lead && part.frogNumber === size) return part.lead.inches;
|
|
1216
|
+
return size * TURNOUT_LEAD_INCHES_PER_FROG;
|
|
1217
|
+
}
|
|
1218
|
+
function parseXtpLibrary(text) {
|
|
1219
|
+
const parts = [];
|
|
1220
|
+
let cur = null;
|
|
1221
|
+
for (const raw of text.split(/\r?\n/)) {
|
|
1222
|
+
const line = raw.trim();
|
|
1223
|
+
if (!line || line.startsWith("#")) continue;
|
|
1224
|
+
if (line.startsWith("TURNOUT")) {
|
|
1225
|
+
const quoted = line.match(/"([^"]*)"/);
|
|
1226
|
+
const scale = line.split(/\s+/)[1];
|
|
1227
|
+
const title = quoted?.[1] ?? "";
|
|
1228
|
+
const bits = title.split(" ").map((s) => s.trim()).filter(Boolean);
|
|
1229
|
+
cur = {
|
|
1230
|
+
title,
|
|
1231
|
+
scale,
|
|
1232
|
+
manufacturer: bits[0],
|
|
1233
|
+
name: bits[1],
|
|
1234
|
+
partNumber: bits[2],
|
|
1235
|
+
ends: [],
|
|
1236
|
+
segments: []
|
|
1237
|
+
};
|
|
1238
|
+
continue;
|
|
1239
|
+
}
|
|
1240
|
+
if (!cur) continue;
|
|
1241
|
+
if (line === "END") {
|
|
1242
|
+
if (cur.ends.length || cur.segments.length) parts.push(cur);
|
|
1243
|
+
cur = null;
|
|
1244
|
+
continue;
|
|
1245
|
+
}
|
|
1246
|
+
const n = line.split(/\s+/);
|
|
1247
|
+
const num = (i) => Number(n[i]);
|
|
1248
|
+
if (n[0] === "E" && n.length >= 4) {
|
|
1249
|
+
cur.ends.push({ x: num(1), y: num(2), angleDeg: num(3) });
|
|
1250
|
+
} else if (n[0] === "S" && n.length >= 7) {
|
|
1251
|
+
cur.segments.push({ kind: "straight", x0: num(3), y0: num(4), x1: num(5), y1: num(6) });
|
|
1252
|
+
} else if (n[0] === "C" && n.length >= 8) {
|
|
1253
|
+
cur.segments.push({
|
|
1254
|
+
kind: "curve",
|
|
1255
|
+
radius: num(3),
|
|
1256
|
+
cx: num(4),
|
|
1257
|
+
cy: num(5),
|
|
1258
|
+
startDeg: num(6),
|
|
1259
|
+
extentDeg: num(7)
|
|
1260
|
+
});
|
|
1261
|
+
}
|
|
1262
|
+
}
|
|
1263
|
+
return parts;
|
|
1264
|
+
}
|
|
1265
|
+
function samplePartSegments(segments, stepsPerCurve = 16) {
|
|
1266
|
+
return segments.map((s) => {
|
|
1267
|
+
if (s.kind === "straight") {
|
|
1268
|
+
return [
|
|
1269
|
+
{ x: s.x0, y: s.y0 },
|
|
1270
|
+
{ x: s.x1, y: s.y1 }
|
|
1271
|
+
];
|
|
1272
|
+
}
|
|
1273
|
+
const r = Math.abs(s.radius);
|
|
1274
|
+
const out = [];
|
|
1275
|
+
for (let i = 0; i <= stepsPerCurve; i++) {
|
|
1276
|
+
const a = (s.startDeg + s.extentDeg * i / stepsPerCurve) * Math.PI / 180;
|
|
1277
|
+
out.push({ x: s.cx + r * Math.sin(a), y: s.cy + r * Math.cos(a) });
|
|
1278
|
+
}
|
|
1279
|
+
return out;
|
|
1280
|
+
});
|
|
1281
|
+
}
|
|
1093
1282
|
function turnoutClosure(size, opts = {}) {
|
|
1094
1283
|
const N = size > 0 ? size : 6;
|
|
1095
1284
|
const g = opts.gaugeInches ?? RAIL_GAUGE_INCHES;
|
|
@@ -1619,6 +1808,6 @@ function poseOverridesFromDoc(doc) {
|
|
|
1619
1808
|
return out;
|
|
1620
1809
|
}
|
|
1621
1810
|
|
|
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 };
|
|
1811
|
+
export { ATLAS_CODE55_N, BUILT_IN_TRACK_PARTS, CODE55_RAIL_HEIGHT_INCHES, 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, leadInchesForSize, moduleCenterline, moduleFeatures, moduleFootprint, moduleLengthFromSections, moduleSections, nextId, parseXtpLibrary, poseNeedsManual, poseOverridesFromDoc, remapPos, returnLoop, sampleBenchworkOutline, samplePartSegments, samplePath, scaleFeetToInches, sectionAdjacency, sectionBand, sectionBreaksFromSections, sectionComponents, sectionFootprints, sectionNeighbours, sectionSpans, sectionSpansOrWhole, sectionedCenterline, sectionedEndPose, sliceCenterline, stateToDoc, toSectionRelative, trackMeetsEndplateIssues, trackPart, trackPath, turnoutClosure, turnoutPartForSize };
|
|
1623
1812
|
//# sourceMappingURL=index.js.map
|
|
1624
1813
|
//# sourceMappingURL=index.js.map
|