@willcgage/module-schematic 0.25.0 → 0.27.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 +64 -16
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +63 -3
- package/dist/index.d.ts +63 -3
- package/dist/index.js +61 -17
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.d.cts
CHANGED
|
@@ -51,7 +51,17 @@ interface SchematicEndplate {
|
|
|
51
51
|
}
|
|
52
52
|
/** Free-moN endplate face width, inches — the connection interface size. */
|
|
53
53
|
declare const FREEMO_ENDPLATE_WIDTH_MIN_INCHES = 12;
|
|
54
|
+
/** NB: our own default, NOT from the standard — §1.1 states only the 12″ minimum
|
|
55
|
+
* ("Endplates shall be 6 inches high and a minimum 12 inches wide"). 24″ is
|
|
56
|
+
* simply a common real-world width. Don't present it as required. */
|
|
54
57
|
declare const FREEMO_ENDPLATE_WIDTH_RECOMMENDED_INCHES = 24;
|
|
58
|
+
/** Free-moN §2.0 **standard**: "Double track endplates must have a track spacing
|
|
59
|
+
* of 1.125 inches (1 1/8 inches). Track spacing shall be measured along the
|
|
60
|
+
* track center line." The one definition both apps read. */
|
|
61
|
+
declare const FREEMO_TRACK_SPACING_INCHES = 1.125;
|
|
62
|
+
/** Free-moN §2.0 **standard**: track crossing an endplate must be "not less than
|
|
63
|
+
* 4 inches from either fascia" (and perpendicular, straight and level for 4″). */
|
|
64
|
+
declare const FREEMO_ENDPLATE_TRACK_FASCIA_CLEARANCE_INCHES = 4;
|
|
55
65
|
/**
|
|
56
66
|
* The authored face width for an endplate, or the recommended default when a
|
|
57
67
|
* module hasn't authored one. The single source of truth both apps read so a
|
|
@@ -201,6 +211,10 @@ interface ModuleSchematicDoc {
|
|
|
201
211
|
* tracks unrolled as a ladder past the throat — the default when inLoop
|
|
202
212
|
* tracks exist), "geometric" (drawn balloon, AL&E-style). */
|
|
203
213
|
loopRender?: "bulb" | "fan" | "geometric";
|
|
214
|
+
/** The two mains' positions are swapped: Main 1 draws above (lane 1), Main 2
|
|
215
|
+
* on the centre line (lane 0). Absent/false = the default (Main 1 below).
|
|
216
|
+
* Identities are unchanged — only which lane each is drawn in (#FMN-0043). */
|
|
217
|
+
mainsSwapped?: boolean;
|
|
204
218
|
endplates: SchematicEndplate[];
|
|
205
219
|
tracks: SchematicTrack[];
|
|
206
220
|
turnouts?: SchematicTurnout[];
|
|
@@ -273,6 +287,13 @@ interface ModuleFootprintInput {
|
|
|
273
287
|
geometryOffsetInches?: number | null;
|
|
274
288
|
/** Authored endplate face widths by id ("A"/"B"…), inches; default recommended. */
|
|
275
289
|
endplateWidths?: Record<string, number>;
|
|
290
|
+
/** Where each endplate's CENTRE sits relative to the main centre-line at that
|
|
291
|
+
* end, inches (signed, along the +normal). Free-moN puts a **double**-track
|
|
292
|
+
* plate's two tracks 9/16″ either side of its centre, so with Main 1 on the
|
|
293
|
+
* centre-line the plate centre is half a track spacing up — pass
|
|
294
|
+
* `FREEMO_TRACK_SPACING_INCHES / 2`. Single track crosses at the centre ⇒ 0
|
|
295
|
+
* (the default), and an off-centre track is a signed value. */
|
|
296
|
+
endplateTrackOffsets?: Record<string, number>;
|
|
276
297
|
/** Authored benchwork outline (module-local inches), or absent for the band. */
|
|
277
298
|
outline?: BenchworkPoint[] | null;
|
|
278
299
|
/** Authored mainline centre-line (module-local inches, open path with arcs).
|
|
@@ -301,15 +322,49 @@ interface ModuleFootprint {
|
|
|
301
322
|
* centre-line is derived from the geometry fields (length + type/degrees/offset). */
|
|
302
323
|
declare function moduleCenterline(input: ModuleFootprintInput): BenchworkPoint[];
|
|
303
324
|
/** Benchwork band: the centre-line offset ±half-width, tapering widthA→widthB. */
|
|
304
|
-
declare function benchworkBand(center: BenchworkPoint[], widthA?: number, widthB?: number): BenchworkPoint[];
|
|
325
|
+
declare function benchworkBand(center: BenchworkPoint[], widthA?: number, widthB?: number, offsetA?: number, offsetB?: number): BenchworkPoint[];
|
|
305
326
|
/** The two endplate faces (the band's flat ends): [A end at widthA, B end at widthB]. */
|
|
306
|
-
declare function endplateFaceSegments(center: BenchworkPoint[], widthA?: number, widthB?: number): OutlineFace[];
|
|
327
|
+
declare function endplateFaceSegments(center: BenchworkPoint[], widthA?: number, widthB?: number, offsetA?: number, offsetB?: number): OutlineFace[];
|
|
307
328
|
/**
|
|
308
329
|
* The full single-module physical footprint: centre-line + derived band +
|
|
309
330
|
* endplate faces + the authored outline (arc-sampled), all in module-local
|
|
310
331
|
* inches. Renderers draw `outline ?? band`.
|
|
311
332
|
*/
|
|
312
333
|
declare function moduleFootprint(input: ModuleFootprintInput): ModuleFootprint;
|
|
334
|
+
/**
|
|
335
|
+
* The offset from the main centre-line to an endplate's CENTRE, inches — the
|
|
336
|
+
* Free-moN geometry. A **double**-track end carries its two tracks 9/16″ either
|
|
337
|
+
* side of the plate centre (§2.0 RP), and Main 1 sits on the centre-line, so the
|
|
338
|
+
* plate centre is half a track spacing up. A single track crosses at the centre.
|
|
339
|
+
*/
|
|
340
|
+
declare function endplateTrackOffsetFor(config: TrackConfig | "none" | undefined): number;
|
|
341
|
+
/** A Free-moN conformance problem with an endplate's width/track placement. */
|
|
342
|
+
interface EndplateWidthIssue {
|
|
343
|
+
/** "narrow" = below the 12″ minimum; "clearance" = a track too near a fascia. */
|
|
344
|
+
code: "narrow" | "clearance";
|
|
345
|
+
/** Plain-language problem, for the author. */
|
|
346
|
+
message: string;
|
|
347
|
+
/** The width that would satisfy this rule, inches. */
|
|
348
|
+
requiredInches: number;
|
|
349
|
+
}
|
|
350
|
+
/**
|
|
351
|
+
* Check an endplate against the two Free-moN **standards** that bound its width:
|
|
352
|
+
*
|
|
353
|
+
* - §1.1 "Endplates shall be 6 inches high and a **minimum 12 inches wide**."
|
|
354
|
+
* - §2.0 "At the endplate, track shall cross near center on the width, **not less
|
|
355
|
+
* than 4 inches from either fascia**."
|
|
356
|
+
*
|
|
357
|
+
* With the tracks centred (the §2.0 recommendation) a double-track end needs
|
|
358
|
+
* 4 + 1.125 + 4 = 9.125″ for clearance alone, so the 12″ minimum governs — but an
|
|
359
|
+
* **off-centre** track can breach the 4″ rule on a plate that is otherwise wide
|
|
360
|
+
* enough, which is why both are checked. `trackOffsetInches` is the signed
|
|
361
|
+
* distance from the plate's centre to the main's crossing point (0 = centred).
|
|
362
|
+
*/
|
|
363
|
+
declare function checkEndplateWidth(input: {
|
|
364
|
+
widthInches?: number | null;
|
|
365
|
+
config?: TrackConfig | "none" | null;
|
|
366
|
+
trackOffsetInches?: number | null;
|
|
367
|
+
}): EndplateWidthIssue[];
|
|
313
368
|
/** Whether a doc is a single-endplate turnback (explicit flag or one endplate). */
|
|
314
369
|
declare function isLoopDoc(doc: ModuleSchematicDoc): boolean;
|
|
315
370
|
declare const MAIN_TRACK_ID = "main";
|
|
@@ -429,6 +484,11 @@ interface EditorState {
|
|
|
429
484
|
loopReturn: "same" | "main2";
|
|
430
485
|
configA: TrackConfig;
|
|
431
486
|
configB: EndplateBConfig;
|
|
487
|
+
/** Swap the two mains' POSITIONS: Main 1 draws above (lane 1) and Main 2 on
|
|
488
|
+
* the centre line (lane 0). The module decides which physical track is which
|
|
489
|
+
* main — on some modules the upper track is the through/primary main
|
|
490
|
+
* (#FMN-0043). Identities and references are unchanged; only the lanes swap. */
|
|
491
|
+
mainsSwapped: boolean;
|
|
432
492
|
extraTracks: EditorTrack[];
|
|
433
493
|
turnouts: EditorTurnout[];
|
|
434
494
|
/** Grade crossings / diamonds (#170). */
|
|
@@ -736,4 +796,4 @@ declare function poseOverridesFromDoc(doc: ModuleSchematicDoc): Record<string, {
|
|
|
736
796
|
heading: number;
|
|
737
797
|
}>;
|
|
738
798
|
|
|
739
|
-
export { type BenchworkPoint, type BranchConnector, type DrawCrossing, type DrawCrossover, type DrawIndustry, type DrawSignal, type DrawTrack, type DrawTurnout, type EditorBranch, type EditorControlPoint, type EditorCpSignal, type EditorCrossing, type EditorIndustry, type EditorState, type EditorTrack, type EditorTurnout, type EndplateBConfig, type EndplatePose, FREEMO_ENDPLATE_WIDTH_MIN_INCHES, FREEMO_ENDPLATE_WIDTH_RECOMMENDED_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 SchematicSignal, type SchematicTrack, type SchematicTurnout, type SignalFacing, type SignalSide, type TrackConfig, type TrackRole, type TurnoutKind, asModuleSchematic, benchworkBand, benchworkOutline, buildCrossover, buildPassingSiding, buildTransition, carCapacity, deriveEndplatePoses, divergeSideForHand, docToState, emptyEditorState, endplateFaceSegments, endplateWidthInches, geometryTurnDegrees, inchesToScaleFeet, isLoopDoc, isTransitionTurnout, moduleCenterline, moduleFeatures, moduleFootprint, nextId, poseNeedsManual, poseOverridesFromDoc, sampleBenchworkOutline, samplePath, scaleFeetToInches, stateToDoc, trackPath };
|
|
799
|
+
export { type BenchworkPoint, type BranchConnector, type DrawCrossing, type DrawCrossover, type DrawIndustry, type DrawSignal, type DrawTrack, type DrawTurnout, type EditorBranch, type EditorControlPoint, type EditorCpSignal, type EditorCrossing, type EditorIndustry, type EditorState, type EditorTrack, type EditorTurnout, type EndplateBConfig, type EndplatePose, 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 SchematicSignal, type SchematicTrack, type SchematicTurnout, type SignalFacing, type SignalSide, type TrackConfig, type TrackRole, type TurnoutKind, asModuleSchematic, benchworkBand, benchworkOutline, buildCrossover, buildPassingSiding, buildTransition, carCapacity, checkEndplateWidth, deriveEndplatePoses, divergeSideForHand, docToState, emptyEditorState, endplateFaceSegments, endplateTrackOffsetFor, endplateWidthInches, geometryTurnDegrees, inchesToScaleFeet, isLoopDoc, isTransitionTurnout, moduleCenterline, moduleFeatures, moduleFootprint, nextId, poseNeedsManual, poseOverridesFromDoc, sampleBenchworkOutline, samplePath, scaleFeetToInches, stateToDoc, trackPath };
|
package/dist/index.d.ts
CHANGED
|
@@ -51,7 +51,17 @@ interface SchematicEndplate {
|
|
|
51
51
|
}
|
|
52
52
|
/** Free-moN endplate face width, inches — the connection interface size. */
|
|
53
53
|
declare const FREEMO_ENDPLATE_WIDTH_MIN_INCHES = 12;
|
|
54
|
+
/** NB: our own default, NOT from the standard — §1.1 states only the 12″ minimum
|
|
55
|
+
* ("Endplates shall be 6 inches high and a minimum 12 inches wide"). 24″ is
|
|
56
|
+
* simply a common real-world width. Don't present it as required. */
|
|
54
57
|
declare const FREEMO_ENDPLATE_WIDTH_RECOMMENDED_INCHES = 24;
|
|
58
|
+
/** Free-moN §2.0 **standard**: "Double track endplates must have a track spacing
|
|
59
|
+
* of 1.125 inches (1 1/8 inches). Track spacing shall be measured along the
|
|
60
|
+
* track center line." The one definition both apps read. */
|
|
61
|
+
declare const FREEMO_TRACK_SPACING_INCHES = 1.125;
|
|
62
|
+
/** Free-moN §2.0 **standard**: track crossing an endplate must be "not less than
|
|
63
|
+
* 4 inches from either fascia" (and perpendicular, straight and level for 4″). */
|
|
64
|
+
declare const FREEMO_ENDPLATE_TRACK_FASCIA_CLEARANCE_INCHES = 4;
|
|
55
65
|
/**
|
|
56
66
|
* The authored face width for an endplate, or the recommended default when a
|
|
57
67
|
* module hasn't authored one. The single source of truth both apps read so a
|
|
@@ -201,6 +211,10 @@ interface ModuleSchematicDoc {
|
|
|
201
211
|
* tracks unrolled as a ladder past the throat — the default when inLoop
|
|
202
212
|
* tracks exist), "geometric" (drawn balloon, AL&E-style). */
|
|
203
213
|
loopRender?: "bulb" | "fan" | "geometric";
|
|
214
|
+
/** The two mains' positions are swapped: Main 1 draws above (lane 1), Main 2
|
|
215
|
+
* on the centre line (lane 0). Absent/false = the default (Main 1 below).
|
|
216
|
+
* Identities are unchanged — only which lane each is drawn in (#FMN-0043). */
|
|
217
|
+
mainsSwapped?: boolean;
|
|
204
218
|
endplates: SchematicEndplate[];
|
|
205
219
|
tracks: SchematicTrack[];
|
|
206
220
|
turnouts?: SchematicTurnout[];
|
|
@@ -273,6 +287,13 @@ interface ModuleFootprintInput {
|
|
|
273
287
|
geometryOffsetInches?: number | null;
|
|
274
288
|
/** Authored endplate face widths by id ("A"/"B"…), inches; default recommended. */
|
|
275
289
|
endplateWidths?: Record<string, number>;
|
|
290
|
+
/** Where each endplate's CENTRE sits relative to the main centre-line at that
|
|
291
|
+
* end, inches (signed, along the +normal). Free-moN puts a **double**-track
|
|
292
|
+
* plate's two tracks 9/16″ either side of its centre, so with Main 1 on the
|
|
293
|
+
* centre-line the plate centre is half a track spacing up — pass
|
|
294
|
+
* `FREEMO_TRACK_SPACING_INCHES / 2`. Single track crosses at the centre ⇒ 0
|
|
295
|
+
* (the default), and an off-centre track is a signed value. */
|
|
296
|
+
endplateTrackOffsets?: Record<string, number>;
|
|
276
297
|
/** Authored benchwork outline (module-local inches), or absent for the band. */
|
|
277
298
|
outline?: BenchworkPoint[] | null;
|
|
278
299
|
/** Authored mainline centre-line (module-local inches, open path with arcs).
|
|
@@ -301,15 +322,49 @@ interface ModuleFootprint {
|
|
|
301
322
|
* centre-line is derived from the geometry fields (length + type/degrees/offset). */
|
|
302
323
|
declare function moduleCenterline(input: ModuleFootprintInput): BenchworkPoint[];
|
|
303
324
|
/** Benchwork band: the centre-line offset ±half-width, tapering widthA→widthB. */
|
|
304
|
-
declare function benchworkBand(center: BenchworkPoint[], widthA?: number, widthB?: number): BenchworkPoint[];
|
|
325
|
+
declare function benchworkBand(center: BenchworkPoint[], widthA?: number, widthB?: number, offsetA?: number, offsetB?: number): BenchworkPoint[];
|
|
305
326
|
/** The two endplate faces (the band's flat ends): [A end at widthA, B end at widthB]. */
|
|
306
|
-
declare function endplateFaceSegments(center: BenchworkPoint[], widthA?: number, widthB?: number): OutlineFace[];
|
|
327
|
+
declare function endplateFaceSegments(center: BenchworkPoint[], widthA?: number, widthB?: number, offsetA?: number, offsetB?: number): OutlineFace[];
|
|
307
328
|
/**
|
|
308
329
|
* The full single-module physical footprint: centre-line + derived band +
|
|
309
330
|
* endplate faces + the authored outline (arc-sampled), all in module-local
|
|
310
331
|
* inches. Renderers draw `outline ?? band`.
|
|
311
332
|
*/
|
|
312
333
|
declare function moduleFootprint(input: ModuleFootprintInput): ModuleFootprint;
|
|
334
|
+
/**
|
|
335
|
+
* The offset from the main centre-line to an endplate's CENTRE, inches — the
|
|
336
|
+
* Free-moN geometry. A **double**-track end carries its two tracks 9/16″ either
|
|
337
|
+
* side of the plate centre (§2.0 RP), and Main 1 sits on the centre-line, so the
|
|
338
|
+
* plate centre is half a track spacing up. A single track crosses at the centre.
|
|
339
|
+
*/
|
|
340
|
+
declare function endplateTrackOffsetFor(config: TrackConfig | "none" | undefined): number;
|
|
341
|
+
/** A Free-moN conformance problem with an endplate's width/track placement. */
|
|
342
|
+
interface EndplateWidthIssue {
|
|
343
|
+
/** "narrow" = below the 12″ minimum; "clearance" = a track too near a fascia. */
|
|
344
|
+
code: "narrow" | "clearance";
|
|
345
|
+
/** Plain-language problem, for the author. */
|
|
346
|
+
message: string;
|
|
347
|
+
/** The width that would satisfy this rule, inches. */
|
|
348
|
+
requiredInches: number;
|
|
349
|
+
}
|
|
350
|
+
/**
|
|
351
|
+
* Check an endplate against the two Free-moN **standards** that bound its width:
|
|
352
|
+
*
|
|
353
|
+
* - §1.1 "Endplates shall be 6 inches high and a **minimum 12 inches wide**."
|
|
354
|
+
* - §2.0 "At the endplate, track shall cross near center on the width, **not less
|
|
355
|
+
* than 4 inches from either fascia**."
|
|
356
|
+
*
|
|
357
|
+
* With the tracks centred (the §2.0 recommendation) a double-track end needs
|
|
358
|
+
* 4 + 1.125 + 4 = 9.125″ for clearance alone, so the 12″ minimum governs — but an
|
|
359
|
+
* **off-centre** track can breach the 4″ rule on a plate that is otherwise wide
|
|
360
|
+
* enough, which is why both are checked. `trackOffsetInches` is the signed
|
|
361
|
+
* distance from the plate's centre to the main's crossing point (0 = centred).
|
|
362
|
+
*/
|
|
363
|
+
declare function checkEndplateWidth(input: {
|
|
364
|
+
widthInches?: number | null;
|
|
365
|
+
config?: TrackConfig | "none" | null;
|
|
366
|
+
trackOffsetInches?: number | null;
|
|
367
|
+
}): EndplateWidthIssue[];
|
|
313
368
|
/** Whether a doc is a single-endplate turnback (explicit flag or one endplate). */
|
|
314
369
|
declare function isLoopDoc(doc: ModuleSchematicDoc): boolean;
|
|
315
370
|
declare const MAIN_TRACK_ID = "main";
|
|
@@ -429,6 +484,11 @@ interface EditorState {
|
|
|
429
484
|
loopReturn: "same" | "main2";
|
|
430
485
|
configA: TrackConfig;
|
|
431
486
|
configB: EndplateBConfig;
|
|
487
|
+
/** Swap the two mains' POSITIONS: Main 1 draws above (lane 1) and Main 2 on
|
|
488
|
+
* the centre line (lane 0). The module decides which physical track is which
|
|
489
|
+
* main — on some modules the upper track is the through/primary main
|
|
490
|
+
* (#FMN-0043). Identities and references are unchanged; only the lanes swap. */
|
|
491
|
+
mainsSwapped: boolean;
|
|
432
492
|
extraTracks: EditorTrack[];
|
|
433
493
|
turnouts: EditorTurnout[];
|
|
434
494
|
/** Grade crossings / diamonds (#170). */
|
|
@@ -736,4 +796,4 @@ declare function poseOverridesFromDoc(doc: ModuleSchematicDoc): Record<string, {
|
|
|
736
796
|
heading: number;
|
|
737
797
|
}>;
|
|
738
798
|
|
|
739
|
-
export { type BenchworkPoint, type BranchConnector, type DrawCrossing, type DrawCrossover, type DrawIndustry, type DrawSignal, type DrawTrack, type DrawTurnout, type EditorBranch, type EditorControlPoint, type EditorCpSignal, type EditorCrossing, type EditorIndustry, type EditorState, type EditorTrack, type EditorTurnout, type EndplateBConfig, type EndplatePose, FREEMO_ENDPLATE_WIDTH_MIN_INCHES, FREEMO_ENDPLATE_WIDTH_RECOMMENDED_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 SchematicSignal, type SchematicTrack, type SchematicTurnout, type SignalFacing, type SignalSide, type TrackConfig, type TrackRole, type TurnoutKind, asModuleSchematic, benchworkBand, benchworkOutline, buildCrossover, buildPassingSiding, buildTransition, carCapacity, deriveEndplatePoses, divergeSideForHand, docToState, emptyEditorState, endplateFaceSegments, endplateWidthInches, geometryTurnDegrees, inchesToScaleFeet, isLoopDoc, isTransitionTurnout, moduleCenterline, moduleFeatures, moduleFootprint, nextId, poseNeedsManual, poseOverridesFromDoc, sampleBenchworkOutline, samplePath, scaleFeetToInches, stateToDoc, trackPath };
|
|
799
|
+
export { type BenchworkPoint, type BranchConnector, type DrawCrossing, type DrawCrossover, type DrawIndustry, type DrawSignal, type DrawTrack, type DrawTurnout, type EditorBranch, type EditorControlPoint, type EditorCpSignal, type EditorCrossing, type EditorIndustry, type EditorState, type EditorTrack, type EditorTurnout, type EndplateBConfig, type EndplatePose, 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 SchematicSignal, type SchematicTrack, type SchematicTurnout, type SignalFacing, type SignalSide, type TrackConfig, type TrackRole, type TurnoutKind, asModuleSchematic, benchworkBand, benchworkOutline, buildCrossover, buildPassingSiding, buildTransition, carCapacity, checkEndplateWidth, deriveEndplatePoses, divergeSideForHand, docToState, emptyEditorState, endplateFaceSegments, endplateTrackOffsetFor, endplateWidthInches, geometryTurnDegrees, inchesToScaleFeet, isLoopDoc, isTransitionTurnout, moduleCenterline, moduleFeatures, moduleFootprint, nextId, poseNeedsManual, poseOverridesFromDoc, sampleBenchworkOutline, samplePath, scaleFeetToInches, stateToDoc, trackPath };
|
package/dist/index.js
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
// src/index.ts
|
|
2
2
|
var FREEMO_ENDPLATE_WIDTH_MIN_INCHES = 12;
|
|
3
3
|
var FREEMO_ENDPLATE_WIDTH_RECOMMENDED_INCHES = 24;
|
|
4
|
+
var FREEMO_TRACK_SPACING_INCHES = 1.125;
|
|
5
|
+
var FREEMO_ENDPLATE_TRACK_FASCIA_CLEARANCE_INCHES = 4;
|
|
4
6
|
function endplateWidthInches(ep) {
|
|
5
7
|
const w = ep?.widthInches;
|
|
6
8
|
return typeof w === "number" && w > 0 ? w : FREEMO_ENDPLATE_WIDTH_RECOMMENDED_INCHES;
|
|
@@ -142,37 +144,74 @@ function centerlineFractions(center) {
|
|
|
142
144
|
const total = cum[cum.length - 1] || 1;
|
|
143
145
|
return cum.map((d) => d / total);
|
|
144
146
|
}
|
|
145
|
-
function benchworkBand(center, widthA = FREEMO_ENDPLATE_WIDTH_RECOMMENDED_INCHES, widthB = FREEMO_ENDPLATE_WIDTH_RECOMMENDED_INCHES) {
|
|
147
|
+
function benchworkBand(center, widthA = FREEMO_ENDPLATE_WIDTH_RECOMMENDED_INCHES, widthB = FREEMO_ENDPLATE_WIDTH_RECOMMENDED_INCHES, offsetA = 0, offsetB = 0) {
|
|
146
148
|
if (center.length < 2) return [];
|
|
147
149
|
const n = centerlineNormals(center);
|
|
148
150
|
const f = centerlineFractions(center);
|
|
149
151
|
const half = (i) => (widthA * (1 - f[i]) + widthB * f[i]) / 2;
|
|
150
|
-
const
|
|
151
|
-
const
|
|
152
|
+
const off = (i) => offsetA * (1 - f[i]) + offsetB * f[i];
|
|
153
|
+
const left = center.map((p, i) => ({
|
|
154
|
+
x: p.x + n[i].x * (off(i) + half(i)),
|
|
155
|
+
y: p.y + n[i].y * (off(i) + half(i))
|
|
156
|
+
}));
|
|
157
|
+
const right = center.map((p, i) => ({
|
|
158
|
+
x: p.x + n[i].x * (off(i) - half(i)),
|
|
159
|
+
y: p.y + n[i].y * (off(i) - half(i))
|
|
160
|
+
}));
|
|
152
161
|
return [...left, ...right.reverse()];
|
|
153
162
|
}
|
|
154
|
-
function endplateFaceSegments(center, widthA = FREEMO_ENDPLATE_WIDTH_RECOMMENDED_INCHES, widthB = FREEMO_ENDPLATE_WIDTH_RECOMMENDED_INCHES) {
|
|
163
|
+
function endplateFaceSegments(center, widthA = FREEMO_ENDPLATE_WIDTH_RECOMMENDED_INCHES, widthB = FREEMO_ENDPLATE_WIDTH_RECOMMENDED_INCHES, offsetA = 0, offsetB = 0) {
|
|
155
164
|
if (center.length < 2) return [];
|
|
156
165
|
const n = centerlineNormals(center);
|
|
157
|
-
const face = (i, w) => ({
|
|
158
|
-
p1: { x: center[i].x + n[i].x * (w / 2), y: center[i].y + n[i].y * (w / 2) },
|
|
159
|
-
p2: { x: center[i].x
|
|
166
|
+
const face = (i, w, o) => ({
|
|
167
|
+
p1: { x: center[i].x + n[i].x * (o + w / 2), y: center[i].y + n[i].y * (o + w / 2) },
|
|
168
|
+
p2: { x: center[i].x + n[i].x * (o - w / 2), y: center[i].y + n[i].y * (o - w / 2) },
|
|
160
169
|
mid: { x: center[i].x, y: center[i].y }
|
|
161
170
|
});
|
|
162
|
-
return [face(0, widthA), face(center.length - 1, widthB)];
|
|
171
|
+
return [face(0, widthA, offsetA), face(center.length - 1, widthB, offsetB)];
|
|
163
172
|
}
|
|
164
173
|
function moduleFootprint(input) {
|
|
165
174
|
const centerline = moduleCenterline(input);
|
|
166
175
|
const widthA = endplateWidthFor(input.endplateWidths, "A");
|
|
167
176
|
const widthB = endplateWidthFor(input.endplateWidths, "B");
|
|
168
177
|
const authored = benchworkOutline(input);
|
|
178
|
+
const offA = input.endplateTrackOffsets?.["A"] ?? 0;
|
|
179
|
+
const offB = input.endplateTrackOffsets?.["B"] ?? 0;
|
|
169
180
|
return {
|
|
170
181
|
centerline,
|
|
171
|
-
band: benchworkBand(centerline, widthA, widthB),
|
|
172
|
-
endplateFaces: endplateFaceSegments(centerline, widthA, widthB),
|
|
182
|
+
band: benchworkBand(centerline, widthA, widthB, offA, offB),
|
|
183
|
+
endplateFaces: endplateFaceSegments(centerline, widthA, widthB, offA, offB),
|
|
173
184
|
outline: authored ? sampleBenchworkOutline(authored) : null
|
|
174
185
|
};
|
|
175
186
|
}
|
|
187
|
+
function endplateTrackOffsetFor(config) {
|
|
188
|
+
return config === "double" ? FREEMO_TRACK_SPACING_INCHES / 2 : 0;
|
|
189
|
+
}
|
|
190
|
+
function checkEndplateWidth(input) {
|
|
191
|
+
const width = endplateWidthInches(input);
|
|
192
|
+
const issues = [];
|
|
193
|
+
if (width < FREEMO_ENDPLATE_WIDTH_MIN_INCHES) {
|
|
194
|
+
issues.push({
|
|
195
|
+
code: "narrow",
|
|
196
|
+
message: `Endplate is ${round2(width)}\u2033 wide \u2014 the standard requires at least ${FREEMO_ENDPLATE_WIDTH_MIN_INCHES}\u2033.`,
|
|
197
|
+
requiredInches: FREEMO_ENDPLATE_WIDTH_MIN_INCHES
|
|
198
|
+
});
|
|
199
|
+
}
|
|
200
|
+
const off = input.trackOffsetInches ?? 0;
|
|
201
|
+
const centres = input.config === "double" ? [off - FREEMO_TRACK_SPACING_INCHES / 2, off + FREEMO_TRACK_SPACING_INCHES / 2] : [off];
|
|
202
|
+
const worst = Math.max(...centres.map((c) => Math.abs(c)));
|
|
203
|
+
const clearance = width / 2 - worst;
|
|
204
|
+
if (clearance < FREEMO_ENDPLATE_TRACK_FASCIA_CLEARANCE_INCHES) {
|
|
205
|
+
const required = 2 * (worst + FREEMO_ENDPLATE_TRACK_FASCIA_CLEARANCE_INCHES);
|
|
206
|
+
issues.push({
|
|
207
|
+
code: "clearance",
|
|
208
|
+
message: `Track sits ${round2(clearance)}\u2033 from the fascia \u2014 the standard requires at least ${FREEMO_ENDPLATE_TRACK_FASCIA_CLEARANCE_INCHES}\u2033. Widen this end to ${round2(required)}\u2033` + (off !== 0 ? " or move the track back toward the centre." : "."),
|
|
209
|
+
requiredInches: required
|
|
210
|
+
});
|
|
211
|
+
}
|
|
212
|
+
return issues;
|
|
213
|
+
}
|
|
214
|
+
var round2 = (n) => Math.round(n * 100) / 100;
|
|
176
215
|
function endplateWidthFor(widths, id) {
|
|
177
216
|
const w = widths?.[id];
|
|
178
217
|
return typeof w === "number" && w > 0 ? w : FREEMO_ENDPLATE_WIDTH_RECOMMENDED_INCHES;
|
|
@@ -208,6 +247,7 @@ function emptyEditorState(lengthInches) {
|
|
|
208
247
|
loopReturn: "same",
|
|
209
248
|
configA: "single",
|
|
210
249
|
configB: "single",
|
|
250
|
+
mainsSwapped: false,
|
|
211
251
|
extraTracks: [],
|
|
212
252
|
turnouts: [],
|
|
213
253
|
crossings: [],
|
|
@@ -228,29 +268,31 @@ function main1Track(state) {
|
|
|
228
268
|
const bothDouble = state.configA === "double" && state.configB === "double";
|
|
229
269
|
const isDouble = state.configA === "double" || state.configB === "double";
|
|
230
270
|
const sw = state.turnouts.find(isTransitionTurnout);
|
|
271
|
+
const lane = state.mainsSwapped && isDouble ? 1 : 0;
|
|
231
272
|
if (!isDouble || bothDouble || !sw || sw.onTrack !== MAIN2_TRACK_ID) {
|
|
232
|
-
return { id: MAIN_TRACK_ID, role: "main", lane
|
|
273
|
+
return { id: MAIN_TRACK_ID, role: "main", lane, from: "A", to: "B" };
|
|
233
274
|
}
|
|
234
275
|
return state.configA === "double" ? (
|
|
235
276
|
// Double at A: Main 1 runs from A and ends at the turnout.
|
|
236
|
-
{ id: MAIN_TRACK_ID, role: "main", lane
|
|
277
|
+
{ id: MAIN_TRACK_ID, role: "main", lane, fromPos: 0, toPos: sw.pos }
|
|
237
278
|
) : (
|
|
238
279
|
// Double at B: Main 1 begins at the turnout and runs to B.
|
|
239
|
-
{ id: MAIN_TRACK_ID, role: "main", lane
|
|
280
|
+
{ id: MAIN_TRACK_ID, role: "main", lane, fromPos: sw.pos, toPos: state.lengthInches }
|
|
240
281
|
);
|
|
241
282
|
}
|
|
242
283
|
function main2Track(state) {
|
|
243
284
|
const bothDouble = state.configA === "double" && state.configB === "double";
|
|
244
285
|
const sw = state.turnouts.find(isTransitionTurnout);
|
|
286
|
+
const lane = state.mainsSwapped ? 0 : 1;
|
|
245
287
|
if (bothDouble || !sw || sw.divergeTrack !== MAIN2_TRACK_ID) {
|
|
246
|
-
return { id: MAIN2_TRACK_ID, role: "main", lane
|
|
288
|
+
return { id: MAIN2_TRACK_ID, role: "main", lane, from: "A", to: "B" };
|
|
247
289
|
}
|
|
248
290
|
return state.configA === "double" ? (
|
|
249
291
|
// Double at A: Main 2 runs from A and ends at the turnout.
|
|
250
|
-
{ id: MAIN2_TRACK_ID, role: "main", lane
|
|
292
|
+
{ id: MAIN2_TRACK_ID, role: "main", lane, fromPos: 0, toPos: sw.pos }
|
|
251
293
|
) : (
|
|
252
294
|
// Double at B: Main 2 begins at the turnout and runs to B.
|
|
253
|
-
{ id: MAIN2_TRACK_ID, role: "main", lane
|
|
295
|
+
{ id: MAIN2_TRACK_ID, role: "main", lane, fromPos: sw.pos, toPos: state.lengthInches }
|
|
254
296
|
);
|
|
255
297
|
}
|
|
256
298
|
function buildTransition(state) {
|
|
@@ -307,6 +349,7 @@ function stateToDoc(state, recordNumber) {
|
|
|
307
349
|
lengthInches: state.lengthInches,
|
|
308
350
|
...state.loop ? { loop: true } : {},
|
|
309
351
|
...state.loop && state.loopReturn === "main2" ? { loopReturn: "main2" } : {},
|
|
352
|
+
...state.mainsSwapped ? { mainsSwapped: true } : {},
|
|
310
353
|
endplates: withWidths(
|
|
311
354
|
withPoses(
|
|
312
355
|
[
|
|
@@ -518,6 +561,7 @@ function docToState(doc, fallbackLength, moduleTracks = []) {
|
|
|
518
561
|
lengthInches: len,
|
|
519
562
|
loop,
|
|
520
563
|
loopReturn: loop && d.loopReturn === "main2" ? "main2" : "same",
|
|
564
|
+
mainsSwapped: d.mainsSwapped === true,
|
|
521
565
|
configA: configOf("A"),
|
|
522
566
|
// On a loop, a missing B means pure turnback; present = interchange loop.
|
|
523
567
|
configB: loop && !hasB ? "none" : configOf("B"),
|
|
@@ -1032,6 +1076,6 @@ function poseOverridesFromDoc(doc) {
|
|
|
1032
1076
|
return out;
|
|
1033
1077
|
}
|
|
1034
1078
|
|
|
1035
|
-
export { FREEMO_ENDPLATE_WIDTH_MIN_INCHES, FREEMO_ENDPLATE_WIDTH_RECOMMENDED_INCHES, MAIN2_TRACK_ID, MAIN_TRACK_ID, N_CAR_LENGTH_INCHES, N_SCALE_RATIO, asModuleSchematic, benchworkBand, benchworkOutline, buildCrossover, buildPassingSiding, buildTransition, carCapacity, deriveEndplatePoses, divergeSideForHand, docToState, emptyEditorState, endplateFaceSegments, endplateWidthInches, geometryTurnDegrees, inchesToScaleFeet, isLoopDoc, isTransitionTurnout, moduleCenterline, moduleFeatures, moduleFootprint, nextId, poseNeedsManual, poseOverridesFromDoc, sampleBenchworkOutline, samplePath, scaleFeetToInches, stateToDoc, trackPath };
|
|
1079
|
+
export { 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, asModuleSchematic, benchworkBand, benchworkOutline, buildCrossover, buildPassingSiding, buildTransition, carCapacity, checkEndplateWidth, deriveEndplatePoses, divergeSideForHand, docToState, emptyEditorState, endplateFaceSegments, endplateTrackOffsetFor, endplateWidthInches, geometryTurnDegrees, inchesToScaleFeet, isLoopDoc, isTransitionTurnout, moduleCenterline, moduleFeatures, moduleFootprint, nextId, poseNeedsManual, poseOverridesFromDoc, sampleBenchworkOutline, samplePath, scaleFeetToInches, stateToDoc, trackPath };
|
|
1036
1080
|
//# sourceMappingURL=index.js.map
|
|
1037
1081
|
//# sourceMappingURL=index.js.map
|