@willcgage/module-schematic 0.26.0 → 0.28.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 +72 -13
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +73 -3
- package/dist/index.d.ts +73 -3
- package/dist/index.js +68 -14
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.d.cts
CHANGED
|
@@ -48,10 +48,29 @@ interface SchematicEndplate {
|
|
|
48
48
|
* recommended. Absent = the recommended default (modules may differ end to
|
|
49
49
|
* end, e.g. a transition). */
|
|
50
50
|
widthInches?: number | null;
|
|
51
|
+
/** Where this end's PRIMARY track (Main 1) crosses, as a signed distance from
|
|
52
|
+
* the plate's CENTRE in inches — the standard's own framing ("each track
|
|
53
|
+
* 0.5625 inches from the center of the endplate"). §2.0 requires only that
|
|
54
|
+
* every track stay ≥4″ from either fascia; centring is a recommendation the
|
|
55
|
+
* 20220628 revision relaxed, so an offset is legal — a transition SECTION
|
|
56
|
+
* commonly offsets its single-track end so the through main lines up with one
|
|
57
|
+
* of the two tracks at its double end. Absent = the recommended default
|
|
58
|
+
* (single centred; double straddling at ∓ half the track spacing). */
|
|
59
|
+
trackOffsetInches?: number | null;
|
|
51
60
|
}
|
|
52
61
|
/** Free-moN endplate face width, inches — the connection interface size. */
|
|
53
62
|
declare const FREEMO_ENDPLATE_WIDTH_MIN_INCHES = 12;
|
|
63
|
+
/** NB: our own default, NOT from the standard — §1.1 states only the 12″ minimum
|
|
64
|
+
* ("Endplates shall be 6 inches high and a minimum 12 inches wide"). 24″ is
|
|
65
|
+
* simply a common real-world width. Don't present it as required. */
|
|
54
66
|
declare const FREEMO_ENDPLATE_WIDTH_RECOMMENDED_INCHES = 24;
|
|
67
|
+
/** Free-moN §2.0 **standard**: "Double track endplates must have a track spacing
|
|
68
|
+
* of 1.125 inches (1 1/8 inches). Track spacing shall be measured along the
|
|
69
|
+
* track center line." The one definition both apps read. */
|
|
70
|
+
declare const FREEMO_TRACK_SPACING_INCHES = 1.125;
|
|
71
|
+
/** Free-moN §2.0 **standard**: track crossing an endplate must be "not less than
|
|
72
|
+
* 4 inches from either fascia" (and perpendicular, straight and level for 4″). */
|
|
73
|
+
declare const FREEMO_ENDPLATE_TRACK_FASCIA_CLEARANCE_INCHES = 4;
|
|
55
74
|
/**
|
|
56
75
|
* The authored face width for an endplate, or the recommended default when a
|
|
57
76
|
* module hasn't authored one. The single source of truth both apps read so a
|
|
@@ -277,6 +296,13 @@ interface ModuleFootprintInput {
|
|
|
277
296
|
geometryOffsetInches?: number | null;
|
|
278
297
|
/** Authored endplate face widths by id ("A"/"B"…), inches; default recommended. */
|
|
279
298
|
endplateWidths?: Record<string, number>;
|
|
299
|
+
/** Where each endplate's CENTRE sits relative to the main centre-line at that
|
|
300
|
+
* end, inches (signed, along the +normal). Free-moN puts a **double**-track
|
|
301
|
+
* plate's two tracks 9/16″ either side of its centre, so with Main 1 on the
|
|
302
|
+
* centre-line the plate centre is half a track spacing up — pass
|
|
303
|
+
* `FREEMO_TRACK_SPACING_INCHES / 2`. Single track crosses at the centre ⇒ 0
|
|
304
|
+
* (the default), and an off-centre track is a signed value. */
|
|
305
|
+
endplateTrackOffsets?: Record<string, number>;
|
|
280
306
|
/** Authored benchwork outline (module-local inches), or absent for the band. */
|
|
281
307
|
outline?: BenchworkPoint[] | null;
|
|
282
308
|
/** Authored mainline centre-line (module-local inches, open path with arcs).
|
|
@@ -305,15 +331,56 @@ interface ModuleFootprint {
|
|
|
305
331
|
* centre-line is derived from the geometry fields (length + type/degrees/offset). */
|
|
306
332
|
declare function moduleCenterline(input: ModuleFootprintInput): BenchworkPoint[];
|
|
307
333
|
/** Benchwork band: the centre-line offset ±half-width, tapering widthA→widthB. */
|
|
308
|
-
declare function benchworkBand(center: BenchworkPoint[], widthA?: number, widthB?: number): BenchworkPoint[];
|
|
334
|
+
declare function benchworkBand(center: BenchworkPoint[], widthA?: number, widthB?: number, offsetA?: number, offsetB?: number): BenchworkPoint[];
|
|
309
335
|
/** The two endplate faces (the band's flat ends): [A end at widthA, B end at widthB]. */
|
|
310
|
-
declare function endplateFaceSegments(center: BenchworkPoint[], widthA?: number, widthB?: number): OutlineFace[];
|
|
336
|
+
declare function endplateFaceSegments(center: BenchworkPoint[], widthA?: number, widthB?: number, offsetA?: number, offsetB?: number): OutlineFace[];
|
|
311
337
|
/**
|
|
312
338
|
* The full single-module physical footprint: centre-line + derived band +
|
|
313
339
|
* endplate faces + the authored outline (arc-sampled), all in module-local
|
|
314
340
|
* inches. Renderers draw `outline ?? band`.
|
|
315
341
|
*/
|
|
316
342
|
declare function moduleFootprint(input: ModuleFootprintInput): ModuleFootprint;
|
|
343
|
+
/**
|
|
344
|
+
* The offset from the main centre-line to an endplate's CENTRE, inches — the
|
|
345
|
+
* Free-moN geometry. A **double**-track end carries its two tracks 9/16″ either
|
|
346
|
+
* side of the plate centre (§2.0 RP), and Main 1 sits on the centre-line, so the
|
|
347
|
+
* plate centre is half a track spacing up. A single track crosses at the centre.
|
|
348
|
+
*/
|
|
349
|
+
declare function endplateTrackOffsetFor(config: TrackConfig | "none" | undefined, authoredTrackOffset?: number | null): number;
|
|
350
|
+
/**
|
|
351
|
+
* Where an endplate's PRIMARY track (Main 1) crosses, as a signed distance from
|
|
352
|
+
* the plate's CENTRE — the standard's own framing. Authored value wins; absent
|
|
353
|
+
* falls back to the §2.0 recommendations: a single track centred (0), a double
|
|
354
|
+
* straddling so its two tracks land ∓ half the track spacing (Main 1 low).
|
|
355
|
+
*/
|
|
356
|
+
declare function endplateTrackOffsetInches(authored: number | null | undefined, config: TrackConfig | "none" | undefined): number;
|
|
357
|
+
/** A Free-moN conformance problem with an endplate's width/track placement. */
|
|
358
|
+
interface EndplateWidthIssue {
|
|
359
|
+
/** "narrow" = below the 12″ minimum; "clearance" = a track too near a fascia. */
|
|
360
|
+
code: "narrow" | "clearance";
|
|
361
|
+
/** Plain-language problem, for the author. */
|
|
362
|
+
message: string;
|
|
363
|
+
/** The width that would satisfy this rule, inches. */
|
|
364
|
+
requiredInches: number;
|
|
365
|
+
}
|
|
366
|
+
/**
|
|
367
|
+
* Check an endplate against the two Free-moN **standards** that bound its width:
|
|
368
|
+
*
|
|
369
|
+
* - §1.1 "Endplates shall be 6 inches high and a **minimum 12 inches wide**."
|
|
370
|
+
* - §2.0 "At the endplate, track shall cross near center on the width, **not less
|
|
371
|
+
* than 4 inches from either fascia**."
|
|
372
|
+
*
|
|
373
|
+
* With the tracks centred (the §2.0 recommendation) a double-track end needs
|
|
374
|
+
* 4 + 1.125 + 4 = 9.125″ for clearance alone, so the 12″ minimum governs — but an
|
|
375
|
+
* **off-centre** track can breach the 4″ rule on a plate that is otherwise wide
|
|
376
|
+
* enough, which is why both are checked. `trackOffsetInches` is the signed
|
|
377
|
+
* distance from the plate's centre to the main's crossing point (0 = centred).
|
|
378
|
+
*/
|
|
379
|
+
declare function checkEndplateWidth(input: {
|
|
380
|
+
widthInches?: number | null;
|
|
381
|
+
config?: TrackConfig | "none" | null;
|
|
382
|
+
trackOffsetInches?: number | null;
|
|
383
|
+
}): EndplateWidthIssue[];
|
|
317
384
|
/** Whether a doc is a single-endplate turnback (explicit flag or one endplate). */
|
|
318
385
|
declare function isLoopDoc(doc: ModuleSchematicDoc): boolean;
|
|
319
386
|
declare const MAIN_TRACK_ID = "main";
|
|
@@ -454,6 +521,9 @@ interface EditorState {
|
|
|
454
521
|
/** Authored endplate face widths by endplate id, inches (Free-moN 12″ min,
|
|
455
522
|
* 24″ recommended). Absent id = the recommended default. */
|
|
456
523
|
endplateWidths: Record<string, number>;
|
|
524
|
+
/** Authored per-endplate TRACK offsets by id — the primary track's signed
|
|
525
|
+
* distance from the plate centre, inches. Absent id = the §2.0 default. */
|
|
526
|
+
endplateTrackOffsets: Record<string, number>;
|
|
457
527
|
/** Benchwork footprint outline — polygon vertices in module-local inches
|
|
458
528
|
* (endplate A's track point at the origin, mainline +x, perpendicular +y up).
|
|
459
529
|
* Empty = no authored outline (fall back to the endplate-width band). */
|
|
@@ -745,4 +815,4 @@ declare function poseOverridesFromDoc(doc: ModuleSchematicDoc): Record<string, {
|
|
|
745
815
|
heading: number;
|
|
746
816
|
}>;
|
|
747
817
|
|
|
748
|
-
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 };
|
|
818
|
+
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, endplateTrackOffsetInches, endplateWidthInches, geometryTurnDegrees, inchesToScaleFeet, isLoopDoc, isTransitionTurnout, moduleCenterline, moduleFeatures, moduleFootprint, nextId, poseNeedsManual, poseOverridesFromDoc, sampleBenchworkOutline, samplePath, scaleFeetToInches, stateToDoc, trackPath };
|
package/dist/index.d.ts
CHANGED
|
@@ -48,10 +48,29 @@ interface SchematicEndplate {
|
|
|
48
48
|
* recommended. Absent = the recommended default (modules may differ end to
|
|
49
49
|
* end, e.g. a transition). */
|
|
50
50
|
widthInches?: number | null;
|
|
51
|
+
/** Where this end's PRIMARY track (Main 1) crosses, as a signed distance from
|
|
52
|
+
* the plate's CENTRE in inches — the standard's own framing ("each track
|
|
53
|
+
* 0.5625 inches from the center of the endplate"). §2.0 requires only that
|
|
54
|
+
* every track stay ≥4″ from either fascia; centring is a recommendation the
|
|
55
|
+
* 20220628 revision relaxed, so an offset is legal — a transition SECTION
|
|
56
|
+
* commonly offsets its single-track end so the through main lines up with one
|
|
57
|
+
* of the two tracks at its double end. Absent = the recommended default
|
|
58
|
+
* (single centred; double straddling at ∓ half the track spacing). */
|
|
59
|
+
trackOffsetInches?: number | null;
|
|
51
60
|
}
|
|
52
61
|
/** Free-moN endplate face width, inches — the connection interface size. */
|
|
53
62
|
declare const FREEMO_ENDPLATE_WIDTH_MIN_INCHES = 12;
|
|
63
|
+
/** NB: our own default, NOT from the standard — §1.1 states only the 12″ minimum
|
|
64
|
+
* ("Endplates shall be 6 inches high and a minimum 12 inches wide"). 24″ is
|
|
65
|
+
* simply a common real-world width. Don't present it as required. */
|
|
54
66
|
declare const FREEMO_ENDPLATE_WIDTH_RECOMMENDED_INCHES = 24;
|
|
67
|
+
/** Free-moN §2.0 **standard**: "Double track endplates must have a track spacing
|
|
68
|
+
* of 1.125 inches (1 1/8 inches). Track spacing shall be measured along the
|
|
69
|
+
* track center line." The one definition both apps read. */
|
|
70
|
+
declare const FREEMO_TRACK_SPACING_INCHES = 1.125;
|
|
71
|
+
/** Free-moN §2.0 **standard**: track crossing an endplate must be "not less than
|
|
72
|
+
* 4 inches from either fascia" (and perpendicular, straight and level for 4″). */
|
|
73
|
+
declare const FREEMO_ENDPLATE_TRACK_FASCIA_CLEARANCE_INCHES = 4;
|
|
55
74
|
/**
|
|
56
75
|
* The authored face width for an endplate, or the recommended default when a
|
|
57
76
|
* module hasn't authored one. The single source of truth both apps read so a
|
|
@@ -277,6 +296,13 @@ interface ModuleFootprintInput {
|
|
|
277
296
|
geometryOffsetInches?: number | null;
|
|
278
297
|
/** Authored endplate face widths by id ("A"/"B"…), inches; default recommended. */
|
|
279
298
|
endplateWidths?: Record<string, number>;
|
|
299
|
+
/** Where each endplate's CENTRE sits relative to the main centre-line at that
|
|
300
|
+
* end, inches (signed, along the +normal). Free-moN puts a **double**-track
|
|
301
|
+
* plate's two tracks 9/16″ either side of its centre, so with Main 1 on the
|
|
302
|
+
* centre-line the plate centre is half a track spacing up — pass
|
|
303
|
+
* `FREEMO_TRACK_SPACING_INCHES / 2`. Single track crosses at the centre ⇒ 0
|
|
304
|
+
* (the default), and an off-centre track is a signed value. */
|
|
305
|
+
endplateTrackOffsets?: Record<string, number>;
|
|
280
306
|
/** Authored benchwork outline (module-local inches), or absent for the band. */
|
|
281
307
|
outline?: BenchworkPoint[] | null;
|
|
282
308
|
/** Authored mainline centre-line (module-local inches, open path with arcs).
|
|
@@ -305,15 +331,56 @@ interface ModuleFootprint {
|
|
|
305
331
|
* centre-line is derived from the geometry fields (length + type/degrees/offset). */
|
|
306
332
|
declare function moduleCenterline(input: ModuleFootprintInput): BenchworkPoint[];
|
|
307
333
|
/** Benchwork band: the centre-line offset ±half-width, tapering widthA→widthB. */
|
|
308
|
-
declare function benchworkBand(center: BenchworkPoint[], widthA?: number, widthB?: number): BenchworkPoint[];
|
|
334
|
+
declare function benchworkBand(center: BenchworkPoint[], widthA?: number, widthB?: number, offsetA?: number, offsetB?: number): BenchworkPoint[];
|
|
309
335
|
/** The two endplate faces (the band's flat ends): [A end at widthA, B end at widthB]. */
|
|
310
|
-
declare function endplateFaceSegments(center: BenchworkPoint[], widthA?: number, widthB?: number): OutlineFace[];
|
|
336
|
+
declare function endplateFaceSegments(center: BenchworkPoint[], widthA?: number, widthB?: number, offsetA?: number, offsetB?: number): OutlineFace[];
|
|
311
337
|
/**
|
|
312
338
|
* The full single-module physical footprint: centre-line + derived band +
|
|
313
339
|
* endplate faces + the authored outline (arc-sampled), all in module-local
|
|
314
340
|
* inches. Renderers draw `outline ?? band`.
|
|
315
341
|
*/
|
|
316
342
|
declare function moduleFootprint(input: ModuleFootprintInput): ModuleFootprint;
|
|
343
|
+
/**
|
|
344
|
+
* The offset from the main centre-line to an endplate's CENTRE, inches — the
|
|
345
|
+
* Free-moN geometry. A **double**-track end carries its two tracks 9/16″ either
|
|
346
|
+
* side of the plate centre (§2.0 RP), and Main 1 sits on the centre-line, so the
|
|
347
|
+
* plate centre is half a track spacing up. A single track crosses at the centre.
|
|
348
|
+
*/
|
|
349
|
+
declare function endplateTrackOffsetFor(config: TrackConfig | "none" | undefined, authoredTrackOffset?: number | null): number;
|
|
350
|
+
/**
|
|
351
|
+
* Where an endplate's PRIMARY track (Main 1) crosses, as a signed distance from
|
|
352
|
+
* the plate's CENTRE — the standard's own framing. Authored value wins; absent
|
|
353
|
+
* falls back to the §2.0 recommendations: a single track centred (0), a double
|
|
354
|
+
* straddling so its two tracks land ∓ half the track spacing (Main 1 low).
|
|
355
|
+
*/
|
|
356
|
+
declare function endplateTrackOffsetInches(authored: number | null | undefined, config: TrackConfig | "none" | undefined): number;
|
|
357
|
+
/** A Free-moN conformance problem with an endplate's width/track placement. */
|
|
358
|
+
interface EndplateWidthIssue {
|
|
359
|
+
/** "narrow" = below the 12″ minimum; "clearance" = a track too near a fascia. */
|
|
360
|
+
code: "narrow" | "clearance";
|
|
361
|
+
/** Plain-language problem, for the author. */
|
|
362
|
+
message: string;
|
|
363
|
+
/** The width that would satisfy this rule, inches. */
|
|
364
|
+
requiredInches: number;
|
|
365
|
+
}
|
|
366
|
+
/**
|
|
367
|
+
* Check an endplate against the two Free-moN **standards** that bound its width:
|
|
368
|
+
*
|
|
369
|
+
* - §1.1 "Endplates shall be 6 inches high and a **minimum 12 inches wide**."
|
|
370
|
+
* - §2.0 "At the endplate, track shall cross near center on the width, **not less
|
|
371
|
+
* than 4 inches from either fascia**."
|
|
372
|
+
*
|
|
373
|
+
* With the tracks centred (the §2.0 recommendation) a double-track end needs
|
|
374
|
+
* 4 + 1.125 + 4 = 9.125″ for clearance alone, so the 12″ minimum governs — but an
|
|
375
|
+
* **off-centre** track can breach the 4″ rule on a plate that is otherwise wide
|
|
376
|
+
* enough, which is why both are checked. `trackOffsetInches` is the signed
|
|
377
|
+
* distance from the plate's centre to the main's crossing point (0 = centred).
|
|
378
|
+
*/
|
|
379
|
+
declare function checkEndplateWidth(input: {
|
|
380
|
+
widthInches?: number | null;
|
|
381
|
+
config?: TrackConfig | "none" | null;
|
|
382
|
+
trackOffsetInches?: number | null;
|
|
383
|
+
}): EndplateWidthIssue[];
|
|
317
384
|
/** Whether a doc is a single-endplate turnback (explicit flag or one endplate). */
|
|
318
385
|
declare function isLoopDoc(doc: ModuleSchematicDoc): boolean;
|
|
319
386
|
declare const MAIN_TRACK_ID = "main";
|
|
@@ -454,6 +521,9 @@ interface EditorState {
|
|
|
454
521
|
/** Authored endplate face widths by endplate id, inches (Free-moN 12″ min,
|
|
455
522
|
* 24″ recommended). Absent id = the recommended default. */
|
|
456
523
|
endplateWidths: Record<string, number>;
|
|
524
|
+
/** Authored per-endplate TRACK offsets by id — the primary track's signed
|
|
525
|
+
* distance from the plate centre, inches. Absent id = the §2.0 default. */
|
|
526
|
+
endplateTrackOffsets: Record<string, number>;
|
|
457
527
|
/** Benchwork footprint outline — polygon vertices in module-local inches
|
|
458
528
|
* (endplate A's track point at the origin, mainline +x, perpendicular +y up).
|
|
459
529
|
* Empty = no authored outline (fall back to the endplate-width band). */
|
|
@@ -745,4 +815,4 @@ declare function poseOverridesFromDoc(doc: ModuleSchematicDoc): Record<string, {
|
|
|
745
815
|
heading: number;
|
|
746
816
|
}>;
|
|
747
817
|
|
|
748
|
-
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 };
|
|
818
|
+
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, endplateTrackOffsetInches, 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,79 @@ 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, authoredTrackOffset) {
|
|
188
|
+
const v = -endplateTrackOffsetInches(authoredTrackOffset, config);
|
|
189
|
+
return v === 0 ? 0 : v;
|
|
190
|
+
}
|
|
191
|
+
function endplateTrackOffsetInches(authored, config) {
|
|
192
|
+
if (typeof authored === "number" && Number.isFinite(authored)) return authored;
|
|
193
|
+
return config === "double" ? -FREEMO_TRACK_SPACING_INCHES / 2 : 0;
|
|
194
|
+
}
|
|
195
|
+
function checkEndplateWidth(input) {
|
|
196
|
+
const width = endplateWidthInches(input);
|
|
197
|
+
const issues = [];
|
|
198
|
+
if (width < FREEMO_ENDPLATE_WIDTH_MIN_INCHES) {
|
|
199
|
+
issues.push({
|
|
200
|
+
code: "narrow",
|
|
201
|
+
message: `Endplate is ${round2(width)}\u2033 wide \u2014 the standard requires at least ${FREEMO_ENDPLATE_WIDTH_MIN_INCHES}\u2033.`,
|
|
202
|
+
requiredInches: FREEMO_ENDPLATE_WIDTH_MIN_INCHES
|
|
203
|
+
});
|
|
204
|
+
}
|
|
205
|
+
const off = input.trackOffsetInches ?? 0;
|
|
206
|
+
const centres = input.config === "double" ? [off - FREEMO_TRACK_SPACING_INCHES / 2, off + FREEMO_TRACK_SPACING_INCHES / 2] : [off];
|
|
207
|
+
const worst = Math.max(...centres.map((c) => Math.abs(c)));
|
|
208
|
+
const clearance = width / 2 - worst;
|
|
209
|
+
if (clearance < FREEMO_ENDPLATE_TRACK_FASCIA_CLEARANCE_INCHES) {
|
|
210
|
+
const required = 2 * (worst + FREEMO_ENDPLATE_TRACK_FASCIA_CLEARANCE_INCHES);
|
|
211
|
+
issues.push({
|
|
212
|
+
code: "clearance",
|
|
213
|
+
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." : "."),
|
|
214
|
+
requiredInches: required
|
|
215
|
+
});
|
|
216
|
+
}
|
|
217
|
+
return issues;
|
|
218
|
+
}
|
|
219
|
+
var round2 = (n) => Math.round(n * 100) / 100;
|
|
176
220
|
function endplateWidthFor(widths, id) {
|
|
177
221
|
const w = widths?.[id];
|
|
178
222
|
return typeof w === "number" && w > 0 ? w : FREEMO_ENDPLATE_WIDTH_RECOMMENDED_INCHES;
|
|
@@ -215,6 +259,7 @@ function emptyEditorState(lengthInches) {
|
|
|
215
259
|
branches: [],
|
|
216
260
|
poseOverrides: {},
|
|
217
261
|
endplateWidths: {},
|
|
262
|
+
endplateTrackOffsets: {},
|
|
218
263
|
outline: [],
|
|
219
264
|
sectionBreaks: [],
|
|
220
265
|
controlPoints: [],
|
|
@@ -297,10 +342,14 @@ function withPoses(endplates, overrides) {
|
|
|
297
342
|
(e) => overrides[e.id] ? { ...e, pose: overrides[e.id] } : e
|
|
298
343
|
);
|
|
299
344
|
}
|
|
300
|
-
function withWidths(endplates, widths) {
|
|
345
|
+
function withWidths(endplates, widths, offsets = {}) {
|
|
301
346
|
return endplates.map((e) => {
|
|
302
347
|
const w = widths[e.id];
|
|
303
|
-
|
|
348
|
+
const o = offsets[e.id];
|
|
349
|
+
let out = e;
|
|
350
|
+
if (typeof w === "number" && w > 0) out = { ...out, widthInches: w };
|
|
351
|
+
if (typeof o === "number" && Number.isFinite(o)) out = { ...out, trackOffsetInches: o };
|
|
352
|
+
return out;
|
|
304
353
|
});
|
|
305
354
|
}
|
|
306
355
|
function stateToDoc(state, recordNumber) {
|
|
@@ -349,7 +398,8 @@ function stateToDoc(state, recordNumber) {
|
|
|
349
398
|
],
|
|
350
399
|
state.poseOverrides
|
|
351
400
|
),
|
|
352
|
-
state.endplateWidths
|
|
401
|
+
state.endplateWidths,
|
|
402
|
+
state.endplateTrackOffsets
|
|
353
403
|
),
|
|
354
404
|
tracks: [
|
|
355
405
|
state.loop ? (
|
|
@@ -508,9 +558,12 @@ function docToState(doc, fallbackLength, moduleTracks = []) {
|
|
|
508
558
|
);
|
|
509
559
|
const poseOverrides = poseOverridesFromDoc(d);
|
|
510
560
|
const endplateWidths = {};
|
|
561
|
+
const endplateTrackOffsets = {};
|
|
511
562
|
for (const e of d.endplates ?? []) {
|
|
512
563
|
if (typeof e.widthInches === "number" && e.widthInches > 0)
|
|
513
564
|
endplateWidths[e.id] = e.widthInches;
|
|
565
|
+
if (typeof e.trackOffsetInches === "number" && Number.isFinite(e.trackOffsetInches))
|
|
566
|
+
endplateTrackOffsets[e.id] = e.trackOffsetInches;
|
|
514
567
|
}
|
|
515
568
|
const outline = (d.outline ?? []).filter((p) => p && Number.isFinite(p.x) && Number.isFinite(p.y)).map((p) => ({
|
|
516
569
|
x: p.x,
|
|
@@ -534,6 +587,7 @@ function docToState(doc, fallbackLength, moduleTracks = []) {
|
|
|
534
587
|
})),
|
|
535
588
|
poseOverrides,
|
|
536
589
|
endplateWidths,
|
|
590
|
+
endplateTrackOffsets,
|
|
537
591
|
outline,
|
|
538
592
|
sectionBreaks: (d.sectionBreaks ?? []).filter((n) => Number.isFinite(n)).map((n) => sc(n)),
|
|
539
593
|
mainPath,
|
|
@@ -1037,6 +1091,6 @@ function poseOverridesFromDoc(doc) {
|
|
|
1037
1091
|
return out;
|
|
1038
1092
|
}
|
|
1039
1093
|
|
|
1040
|
-
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 };
|
|
1094
|
+
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, endplateTrackOffsetInches, endplateWidthInches, geometryTurnDegrees, inchesToScaleFeet, isLoopDoc, isTransitionTurnout, moduleCenterline, moduleFeatures, moduleFootprint, nextId, poseNeedsManual, poseOverridesFromDoc, sampleBenchworkOutline, samplePath, scaleFeetToInches, stateToDoc, trackPath };
|
|
1041
1095
|
//# sourceMappingURL=index.js.map
|
|
1042
1096
|
//# sourceMappingURL=index.js.map
|