@willcgage/module-schematic 0.47.0 → 0.49.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 +91 -0
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +39 -1
- package/dist/index.d.ts +39 -1
- package/dist/index.js +91 -1
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.d.cts
CHANGED
|
@@ -253,6 +253,10 @@ interface ModuleSchematicDoc {
|
|
|
253
253
|
* +y up). Stored as an open ring; renderers close it. Absent = derive an
|
|
254
254
|
* approximate band from the endplate widths. */
|
|
255
255
|
outline?: BenchworkPoint[];
|
|
256
|
+
/** Benchwork HOLE — an inner boundary punched out of `outline`, making the
|
|
257
|
+
* board a DONUT (a return loop's open middle). Same frame as `outline`; stored
|
|
258
|
+
* as an open ring, renderers close it. Absent = a solid board. */
|
|
259
|
+
outlineInner?: BenchworkPoint[];
|
|
256
260
|
/** Internal section joints — inches from endplate A where the module's boards
|
|
257
261
|
* split into sections. Operationally one unit; these mark construction/transport
|
|
258
262
|
* seams (exempt from the end-interface standards). Empty/absent = one section.
|
|
@@ -405,6 +409,9 @@ interface ModuleFootprintInput {
|
|
|
405
409
|
endplateTrackOffsets?: Record<string, number>;
|
|
406
410
|
/** Authored benchwork outline (module-local inches), or absent for the band. */
|
|
407
411
|
outline?: BenchworkPoint[] | null;
|
|
412
|
+
/** Authored benchwork HOLE — the inner boundary punched out of `outline` to
|
|
413
|
+
* make a donut. Absent/short = a solid board. */
|
|
414
|
+
outlineInner?: BenchworkPoint[] | null;
|
|
408
415
|
/** The module's sections (#96 phase 2). When any carries an outline, the
|
|
409
416
|
* module's footprint is the union of those — `outline` is then ignored. */
|
|
410
417
|
sections?: SchematicSection[] | null;
|
|
@@ -432,6 +439,9 @@ interface ModuleFootprint {
|
|
|
432
439
|
/** Authored outline (arc-sampled closed ring) or null → render the band.
|
|
433
440
|
* Null too when `sectionOutlines` is non-empty — the sections ARE the shape. */
|
|
434
441
|
outline: BenchworkPoint[] | null;
|
|
442
|
+
/** Authored HOLE (arc-sampled closed ring) punched out of `outline` → a donut
|
|
443
|
+
* board. Null = solid. Renderers fill `outline` with `outlineInner` cut out. */
|
|
444
|
+
outlineInner: BenchworkPoint[] | null;
|
|
435
445
|
/** Per-section footprints, arc-sampled (#96 phase 2b). Draw every one: together
|
|
436
446
|
* they are the module's footprint. Empty = this module doesn't use sections,
|
|
437
447
|
* so fall back to `outline ?? band` exactly as before. */
|
|
@@ -793,6 +803,9 @@ interface EditorState {
|
|
|
793
803
|
* (endplate A's track point at the origin, mainline +x, perpendicular +y up).
|
|
794
804
|
* Empty = no authored outline (fall back to the endplate-width band). */
|
|
795
805
|
outline: BenchworkPoint[];
|
|
806
|
+
/** Benchwork HOLE (donut inner boundary), module-local inches. Empty = a solid
|
|
807
|
+
* board. Set by the return-loop generator; punched out of `outline`. */
|
|
808
|
+
outlineInner: BenchworkPoint[];
|
|
796
809
|
/** Internal section joints — inches from endplate A where the boards split
|
|
797
810
|
* into sections. Empty = a single section (#48). */
|
|
798
811
|
sectionBreaks: number[];
|
|
@@ -1042,6 +1055,31 @@ interface EndplatePose {
|
|
|
1042
1055
|
/** True when the pose was hand-entered, not derived (wye/loop/other). */
|
|
1043
1056
|
manual?: boolean;
|
|
1044
1057
|
}
|
|
1058
|
+
type ReturnLoopShape = "circle" | "teardrop" | "offset-teardrop" | "square";
|
|
1059
|
+
/** A computed return-loop: a mainline LEAD to the throat, a WYE that splits into
|
|
1060
|
+
* two legs, and the LOOP track those legs form, closing back at the throat. Pure
|
|
1061
|
+
* geometry, module-local inches, endplate A at the origin, lead heading +x. All
|
|
1062
|
+
* shapes are built so the loop CLOSES exactly at the throat and the two wye legs
|
|
1063
|
+
* diverge symmetrically about the lead — the piece the earlier eyeballed versions
|
|
1064
|
+
* kept getting wrong (#loop). */
|
|
1065
|
+
interface ReturnLoopGeometry {
|
|
1066
|
+
throat: BenchworkPoint;
|
|
1067
|
+
/** The loop track, throat → around → throat (starts and ends at the throat). */
|
|
1068
|
+
loop: BenchworkPoint[];
|
|
1069
|
+
/** The two diverging wye legs at the throat (each throat → its tangent point). */
|
|
1070
|
+
wyeLegs: [BenchworkPoint[], BenchworkPoint[]];
|
|
1071
|
+
/** Half the angle between the two wye legs, degrees (0 = tangent/no wye). */
|
|
1072
|
+
wyeHalfAngleDeg: number;
|
|
1073
|
+
/** Benchwork fascia: outer boundary, and an inner boundary for a donut (empty
|
|
1074
|
+
* when the board is solid). */
|
|
1075
|
+
outlineOuter: BenchworkPoint[];
|
|
1076
|
+
outlineInner: BenchworkPoint[];
|
|
1077
|
+
}
|
|
1078
|
+
declare function returnLoop(shape: ReturnLoopShape, opts: {
|
|
1079
|
+
leadInches: number;
|
|
1080
|
+
radius: number;
|
|
1081
|
+
boardHalfWidth?: number;
|
|
1082
|
+
}): ReturnLoopGeometry;
|
|
1045
1083
|
interface ModuleGeometryInput {
|
|
1046
1084
|
lengthInches: number;
|
|
1047
1085
|
geometryType?: string | null;
|
|
@@ -1141,4 +1179,4 @@ declare function poseOverridesFromDoc(doc: ModuleSchematicDoc): Record<string, {
|
|
|
1141
1179
|
heading: number;
|
|
1142
1180
|
}>;
|
|
1143
1181
|
|
|
1144
|
-
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 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, sampleBenchworkOutline, samplePath, scaleFeetToInches, sectionAdjacency, sectionBand, sectionBreaksFromSections, sectionComponents, sectionFootprints, sectionNeighbours, sectionSpans, sectionSpansOrWhole, sectionedCenterline, sectionedEndPose, sliceCenterline, stateToDoc, toSectionRelative, trackMeetsEndplateIssues, trackPath };
|
|
1182
|
+
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 };
|
package/dist/index.d.ts
CHANGED
|
@@ -253,6 +253,10 @@ interface ModuleSchematicDoc {
|
|
|
253
253
|
* +y up). Stored as an open ring; renderers close it. Absent = derive an
|
|
254
254
|
* approximate band from the endplate widths. */
|
|
255
255
|
outline?: BenchworkPoint[];
|
|
256
|
+
/** Benchwork HOLE — an inner boundary punched out of `outline`, making the
|
|
257
|
+
* board a DONUT (a return loop's open middle). Same frame as `outline`; stored
|
|
258
|
+
* as an open ring, renderers close it. Absent = a solid board. */
|
|
259
|
+
outlineInner?: BenchworkPoint[];
|
|
256
260
|
/** Internal section joints — inches from endplate A where the module's boards
|
|
257
261
|
* split into sections. Operationally one unit; these mark construction/transport
|
|
258
262
|
* seams (exempt from the end-interface standards). Empty/absent = one section.
|
|
@@ -405,6 +409,9 @@ interface ModuleFootprintInput {
|
|
|
405
409
|
endplateTrackOffsets?: Record<string, number>;
|
|
406
410
|
/** Authored benchwork outline (module-local inches), or absent for the band. */
|
|
407
411
|
outline?: BenchworkPoint[] | null;
|
|
412
|
+
/** Authored benchwork HOLE — the inner boundary punched out of `outline` to
|
|
413
|
+
* make a donut. Absent/short = a solid board. */
|
|
414
|
+
outlineInner?: BenchworkPoint[] | null;
|
|
408
415
|
/** The module's sections (#96 phase 2). When any carries an outline, the
|
|
409
416
|
* module's footprint is the union of those — `outline` is then ignored. */
|
|
410
417
|
sections?: SchematicSection[] | null;
|
|
@@ -432,6 +439,9 @@ interface ModuleFootprint {
|
|
|
432
439
|
/** Authored outline (arc-sampled closed ring) or null → render the band.
|
|
433
440
|
* Null too when `sectionOutlines` is non-empty — the sections ARE the shape. */
|
|
434
441
|
outline: BenchworkPoint[] | null;
|
|
442
|
+
/** Authored HOLE (arc-sampled closed ring) punched out of `outline` → a donut
|
|
443
|
+
* board. Null = solid. Renderers fill `outline` with `outlineInner` cut out. */
|
|
444
|
+
outlineInner: BenchworkPoint[] | null;
|
|
435
445
|
/** Per-section footprints, arc-sampled (#96 phase 2b). Draw every one: together
|
|
436
446
|
* they are the module's footprint. Empty = this module doesn't use sections,
|
|
437
447
|
* so fall back to `outline ?? band` exactly as before. */
|
|
@@ -793,6 +803,9 @@ interface EditorState {
|
|
|
793
803
|
* (endplate A's track point at the origin, mainline +x, perpendicular +y up).
|
|
794
804
|
* Empty = no authored outline (fall back to the endplate-width band). */
|
|
795
805
|
outline: BenchworkPoint[];
|
|
806
|
+
/** Benchwork HOLE (donut inner boundary), module-local inches. Empty = a solid
|
|
807
|
+
* board. Set by the return-loop generator; punched out of `outline`. */
|
|
808
|
+
outlineInner: BenchworkPoint[];
|
|
796
809
|
/** Internal section joints — inches from endplate A where the boards split
|
|
797
810
|
* into sections. Empty = a single section (#48). */
|
|
798
811
|
sectionBreaks: number[];
|
|
@@ -1042,6 +1055,31 @@ interface EndplatePose {
|
|
|
1042
1055
|
/** True when the pose was hand-entered, not derived (wye/loop/other). */
|
|
1043
1056
|
manual?: boolean;
|
|
1044
1057
|
}
|
|
1058
|
+
type ReturnLoopShape = "circle" | "teardrop" | "offset-teardrop" | "square";
|
|
1059
|
+
/** A computed return-loop: a mainline LEAD to the throat, a WYE that splits into
|
|
1060
|
+
* two legs, and the LOOP track those legs form, closing back at the throat. Pure
|
|
1061
|
+
* geometry, module-local inches, endplate A at the origin, lead heading +x. All
|
|
1062
|
+
* shapes are built so the loop CLOSES exactly at the throat and the two wye legs
|
|
1063
|
+
* diverge symmetrically about the lead — the piece the earlier eyeballed versions
|
|
1064
|
+
* kept getting wrong (#loop). */
|
|
1065
|
+
interface ReturnLoopGeometry {
|
|
1066
|
+
throat: BenchworkPoint;
|
|
1067
|
+
/** The loop track, throat → around → throat (starts and ends at the throat). */
|
|
1068
|
+
loop: BenchworkPoint[];
|
|
1069
|
+
/** The two diverging wye legs at the throat (each throat → its tangent point). */
|
|
1070
|
+
wyeLegs: [BenchworkPoint[], BenchworkPoint[]];
|
|
1071
|
+
/** Half the angle between the two wye legs, degrees (0 = tangent/no wye). */
|
|
1072
|
+
wyeHalfAngleDeg: number;
|
|
1073
|
+
/** Benchwork fascia: outer boundary, and an inner boundary for a donut (empty
|
|
1074
|
+
* when the board is solid). */
|
|
1075
|
+
outlineOuter: BenchworkPoint[];
|
|
1076
|
+
outlineInner: BenchworkPoint[];
|
|
1077
|
+
}
|
|
1078
|
+
declare function returnLoop(shape: ReturnLoopShape, opts: {
|
|
1079
|
+
leadInches: number;
|
|
1080
|
+
radius: number;
|
|
1081
|
+
boardHalfWidth?: number;
|
|
1082
|
+
}): ReturnLoopGeometry;
|
|
1045
1083
|
interface ModuleGeometryInput {
|
|
1046
1084
|
lengthInches: number;
|
|
1047
1085
|
geometryType?: string | null;
|
|
@@ -1141,4 +1179,4 @@ declare function poseOverridesFromDoc(doc: ModuleSchematicDoc): Record<string, {
|
|
|
1141
1179
|
heading: number;
|
|
1142
1180
|
}>;
|
|
1143
1181
|
|
|
1144
|
-
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 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, sampleBenchworkOutline, samplePath, scaleFeetToInches, sectionAdjacency, sectionBand, sectionBreaksFromSections, sectionComponents, sectionFootprints, sectionNeighbours, sectionSpans, sectionSpansOrWhole, sectionedCenterline, sectionedEndPose, sliceCenterline, stateToDoc, toSectionRelative, trackMeetsEndplateIssues, trackPath };
|
|
1182
|
+
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 };
|
package/dist/index.js
CHANGED
|
@@ -476,6 +476,9 @@ function moduleFootprint(input) {
|
|
|
476
476
|
// endplate A's face (the far face would be a spurious plate at the throat).
|
|
477
477
|
endplateFaces: input.loop ? endplateFaceSegments(centerline, widthA, widthB, offA, offB).slice(0, 1) : endplateFaceSegments(centerline, widthA, widthB, offA, offB),
|
|
478
478
|
outline: sectionOutlines.length || !authored ? null : sampleBenchworkOutline(authored),
|
|
479
|
+
// The donut hole, arc-sampled — only when there's a solid outline to punch it
|
|
480
|
+
// out of (a sectioned module isn't a donut). Renderers cut it from `outline`.
|
|
481
|
+
outlineInner: sectionOutlines.length || !authored || !input.outlineInner || input.outlineInner.length < 3 ? null : sampleBenchworkOutline(input.outlineInner),
|
|
479
482
|
sectionOutlines
|
|
480
483
|
};
|
|
481
484
|
}
|
|
@@ -557,6 +560,7 @@ function emptyEditorState(lengthInches) {
|
|
|
557
560
|
endplateWidths: {},
|
|
558
561
|
endplateTrackOffsets: {},
|
|
559
562
|
outline: [],
|
|
563
|
+
outlineInner: [],
|
|
560
564
|
sectionBreaks: [],
|
|
561
565
|
sections: [],
|
|
562
566
|
controlPoints: [],
|
|
@@ -793,6 +797,8 @@ function stateToDoc(state, recordNumber) {
|
|
|
793
797
|
// Benchwork footprint outline (module-local inches); only when it's a real
|
|
794
798
|
// ring (≥ 3 vertices).
|
|
795
799
|
...state.outline.length >= 3 ? { outline: state.outline } : {},
|
|
800
|
+
// Benchwork hole (donut inner boundary), when it's a real ring.
|
|
801
|
+
...state.outlineInner.length >= 3 ? { outlineInner: state.outlineInner } : {},
|
|
796
802
|
// Internal section joints (inches from A), when the module has more than one.
|
|
797
803
|
...state.sectionBreaks.length ? { sectionBreaks: state.sectionBreaks } : {},
|
|
798
804
|
// Sections as objects — emitted only once the owner has some, so a module
|
|
@@ -884,6 +890,11 @@ function docToState(doc, fallbackLength, moduleTracks = []) {
|
|
|
884
890
|
y: p.y,
|
|
885
891
|
...Number.isFinite(p.bulge) && p.bulge ? { bulge: p.bulge } : {}
|
|
886
892
|
}));
|
|
893
|
+
const outlineInner = (d.outlineInner ?? []).filter((p) => p && Number.isFinite(p.x) && Number.isFinite(p.y)).map((p) => ({
|
|
894
|
+
x: p.x,
|
|
895
|
+
y: p.y,
|
|
896
|
+
...Number.isFinite(p.bulge) && p.bulge ? { bulge: p.bulge } : {}
|
|
897
|
+
}));
|
|
887
898
|
const mainPath = trackPath(d.mainPath) ?? [];
|
|
888
899
|
const main2Track_ = (d.tracks ?? []).find((t) => t.id === MAIN2_TRACK_ID);
|
|
889
900
|
const main2Path = trackPath(d.main2Path ?? main2Track_?.path) ?? [];
|
|
@@ -907,6 +918,7 @@ function docToState(doc, fallbackLength, moduleTracks = []) {
|
|
|
907
918
|
endplateWidths,
|
|
908
919
|
endplateTrackOffsets,
|
|
909
920
|
outline,
|
|
921
|
+
outlineInner,
|
|
910
922
|
sectionBreaks: (d.sectionBreaks ?? []).filter((n) => Number.isFinite(n)).map((n) => sc(n)),
|
|
911
923
|
sections: moduleSections(d),
|
|
912
924
|
mainPath,
|
|
@@ -1320,6 +1332,84 @@ function moduleFeatures(doc) {
|
|
|
1320
1332
|
laneMax: Math.max(...allLanes)
|
|
1321
1333
|
};
|
|
1322
1334
|
}
|
|
1335
|
+
function returnLoop(shape, opts) {
|
|
1336
|
+
const L = Math.max(1, opts.leadInches);
|
|
1337
|
+
const R = Math.max(1, opts.radius);
|
|
1338
|
+
const hw = Math.min(opts.boardHalfWidth ?? 6, R - 1);
|
|
1339
|
+
const T = { x: L, y: 0 };
|
|
1340
|
+
const r2 = (v) => Math.round(v * 100) / 100;
|
|
1341
|
+
const rd = (pts) => pts.map((p) => ({ x: r2(p.x), y: r2(p.y) }));
|
|
1342
|
+
const arc = (cx, cy, r, a0, a12, steps) => {
|
|
1343
|
+
const out = [];
|
|
1344
|
+
for (let i = 0; i <= steps; i++) out.push({ x: cx + r * Math.cos(a0 + (a12 - a0) * i / steps), y: cy + r * Math.sin(a0 + (a12 - a0) * i / steps) });
|
|
1345
|
+
return out;
|
|
1346
|
+
};
|
|
1347
|
+
const D = shape === "circle" || shape === "square" ? R * 1.15 : R * 1.6;
|
|
1348
|
+
const offY = shape === "offset-teardrop" ? R * 0.7 : 0;
|
|
1349
|
+
const C = { x: L + D, y: offY };
|
|
1350
|
+
const dist = Math.hypot(C.x - T.x, C.y - T.y);
|
|
1351
|
+
const ac = Math.acos(Math.min(1, R / dist));
|
|
1352
|
+
const base = Math.atan2(T.y - C.y, T.x - C.x);
|
|
1353
|
+
const a1 = base + ac;
|
|
1354
|
+
const a2 = base - ac;
|
|
1355
|
+
const P1 = { x: C.x + R * Math.cos(a1), y: C.y + R * Math.sin(a1) };
|
|
1356
|
+
const P2 = { x: C.x + R * Math.cos(a2), y: C.y + R * Math.sin(a2) };
|
|
1357
|
+
const loop = [T, P1, ...arc(C.x, C.y, R, a1, a2 + 2 * Math.PI, 64).slice(1), T];
|
|
1358
|
+
const legDir = (P) => Math.atan2(P.y - T.y, P.x - T.x);
|
|
1359
|
+
const half = Math.abs((legDir(P1) - legDir(P2) + Math.PI) % (2 * Math.PI) - Math.PI) / 2;
|
|
1360
|
+
const ringOuter = (rr) => {
|
|
1361
|
+
const xTop = C.x - Math.sqrt(Math.max(0, rr * rr - (hw - C.y) ** 2));
|
|
1362
|
+
const xBot = C.x - Math.sqrt(Math.max(0, rr * rr - (-hw - C.y) ** 2));
|
|
1363
|
+
const tTop = Math.atan2(hw - C.y, xTop - C.x);
|
|
1364
|
+
const tBot = Math.atan2(-hw - C.y, xBot - C.x);
|
|
1365
|
+
return [
|
|
1366
|
+
{ x: 0, y: hw },
|
|
1367
|
+
{ x: xTop, y: hw },
|
|
1368
|
+
...arc(C.x, C.y, rr, tTop, tBot - 2 * Math.PI, 48).slice(1, -1),
|
|
1369
|
+
{ x: xBot, y: -hw },
|
|
1370
|
+
{ x: 0, y: -hw }
|
|
1371
|
+
];
|
|
1372
|
+
};
|
|
1373
|
+
const Ri = R - hw;
|
|
1374
|
+
if (shape === "square") {
|
|
1375
|
+
const xL = C.x - R - hw;
|
|
1376
|
+
const xR = C.x + R + hw;
|
|
1377
|
+
const yT = R + hw;
|
|
1378
|
+
const outerSquare = [
|
|
1379
|
+
{ x: 0, y: hw },
|
|
1380
|
+
{ x: xL, y: hw },
|
|
1381
|
+
{ x: xL, y: yT },
|
|
1382
|
+
{ x: xR, y: yT },
|
|
1383
|
+
{ x: xR, y: -yT },
|
|
1384
|
+
{ x: xL, y: -yT },
|
|
1385
|
+
{ x: xL, y: -hw },
|
|
1386
|
+
{ x: 0, y: -hw }
|
|
1387
|
+
];
|
|
1388
|
+
const iHalf = (R - hw) / Math.SQRT2;
|
|
1389
|
+
const holeSquare = iHalf > 2 ? [
|
|
1390
|
+
{ x: C.x - iHalf, y: iHalf },
|
|
1391
|
+
{ x: C.x + iHalf, y: iHalf },
|
|
1392
|
+
{ x: C.x + iHalf, y: -iHalf },
|
|
1393
|
+
{ x: C.x - iHalf, y: -iHalf }
|
|
1394
|
+
] : [];
|
|
1395
|
+
return {
|
|
1396
|
+
throat: T,
|
|
1397
|
+
loop: rd(loop),
|
|
1398
|
+
wyeLegs: [rd([T, P1]), rd([T, P2])],
|
|
1399
|
+
wyeHalfAngleDeg: r2(half * 180 / Math.PI),
|
|
1400
|
+
outlineOuter: rd(outerSquare),
|
|
1401
|
+
outlineInner: rd(holeSquare)
|
|
1402
|
+
};
|
|
1403
|
+
}
|
|
1404
|
+
return {
|
|
1405
|
+
throat: T,
|
|
1406
|
+
loop: rd(loop),
|
|
1407
|
+
wyeLegs: [rd([T, P1]), rd([T, P2])],
|
|
1408
|
+
wyeHalfAngleDeg: r2(half * 180 / Math.PI),
|
|
1409
|
+
outlineOuter: rd(ringOuter(R + hw)),
|
|
1410
|
+
outlineInner: Ri > 2 ? rd(arc(C.x, C.y, Ri, 0, 2 * Math.PI, 40)) : []
|
|
1411
|
+
};
|
|
1412
|
+
}
|
|
1323
1413
|
function geometryTurnDegrees(geometryType, geometryDegrees) {
|
|
1324
1414
|
switch (geometryType) {
|
|
1325
1415
|
case "corner_45":
|
|
@@ -1491,6 +1581,6 @@ function poseOverridesFromDoc(doc) {
|
|
|
1491
1581
|
return out;
|
|
1492
1582
|
}
|
|
1493
1583
|
|
|
1494
|
-
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, sampleBenchworkOutline, samplePath, scaleFeetToInches, sectionAdjacency, sectionBand, sectionBreaksFromSections, sectionComponents, sectionFootprints, sectionNeighbours, sectionSpans, sectionSpansOrWhole, sectionedCenterline, sectionedEndPose, sliceCenterline, stateToDoc, toSectionRelative, trackMeetsEndplateIssues, trackPath };
|
|
1584
|
+
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 };
|
|
1495
1585
|
//# sourceMappingURL=index.js.map
|
|
1496
1586
|
//# sourceMappingURL=index.js.map
|