@willcgage/module-schematic 0.28.1 → 0.30.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 +108 -1
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +108 -3
- package/dist/index.d.ts +108 -3
- package/dist/index.js +103 -2
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.d.cts
CHANGED
|
@@ -240,8 +240,22 @@ interface ModuleSchematicDoc {
|
|
|
240
240
|
outline?: BenchworkPoint[];
|
|
241
241
|
/** Internal section joints — inches from endplate A where the module's boards
|
|
242
242
|
* split into sections. Operationally one unit; these mark construction/transport
|
|
243
|
-
* seams (exempt from the end-interface standards). Empty/absent = one section.
|
|
243
|
+
* seams (exempt from the end-interface standards). Empty/absent = one section.
|
|
244
|
+
* Describes only sections that are full-depth SLICES; a section with a shape
|
|
245
|
+
* of its own lives in `sections` below. */
|
|
244
246
|
sectionBreaks?: number[];
|
|
247
|
+
/** The module's sections as real objects — named, each with a bench-work
|
|
248
|
+
* outline of its own (#96 phase 2). A module is a kit: the same sections can
|
|
249
|
+
* be set up in different combinations, so its footprint is the UNION of the
|
|
250
|
+
* sections present rather than an independently authored shape.
|
|
251
|
+
*
|
|
252
|
+
* Needed because sections are not slices. Real modules hang a deep section
|
|
253
|
+
* off the BACK of a shallow main band — a peninsula carrying an industry —
|
|
254
|
+
* which no single position along the main can describe.
|
|
255
|
+
*
|
|
256
|
+
* Absent = the module keeps using its own `outline` exactly as before; this
|
|
257
|
+
* is purely additive and nothing migrates on read. */
|
|
258
|
+
sections?: SchematicSection[];
|
|
245
259
|
/** @deprecated pre-grouping flat signals; read for back-compat. */
|
|
246
260
|
signals?: SchematicSignal[];
|
|
247
261
|
/** Authored mainline centre-line (module-local inches, open path with arcs).
|
|
@@ -258,12 +272,56 @@ interface BenchworkPoint {
|
|
|
258
272
|
y: number;
|
|
259
273
|
bulge?: number;
|
|
260
274
|
}
|
|
275
|
+
/** One bench-work section of a module (#96 phase 2). */
|
|
276
|
+
interface SchematicSection {
|
|
277
|
+
id: string;
|
|
278
|
+
/** What the owner calls this board — "west transition", "double #3". */
|
|
279
|
+
name?: string | null;
|
|
280
|
+
/** This section's own footprint polygon, module-local inches, same frame as
|
|
281
|
+
* the module outline. Absent = the section has no shape of its own yet (it's
|
|
282
|
+
* described only by the joints in `sectionBreaks`). */
|
|
283
|
+
outline?: BenchworkPoint[] | null;
|
|
284
|
+
/** How far this board runs along the main, inches. The module's length is the
|
|
285
|
+
* SUM of these — it isn't authored separately (#108). */
|
|
286
|
+
lengthInches?: number | null;
|
|
287
|
+
/** This board's own shape: straight | curve | corner_45 | corner_90 | offset |
|
|
288
|
+
* dead_end. Geometry belongs to the SECTION, not the module — a module like
|
|
289
|
+
* One Mile is 384″ of mostly-straight boards with two 24″ CURVED sections in
|
|
290
|
+
* the middle, which no single module-level geometry can describe (#108).
|
|
291
|
+
* Absent = straight. */
|
|
292
|
+
geometryType?: string | null;
|
|
293
|
+
/** Degrees turned, for curve/corner sections. */
|
|
294
|
+
geometryDegrees?: number | null;
|
|
295
|
+
/** Lateral jog, for offset sections. */
|
|
296
|
+
geometryOffsetInches?: number | null;
|
|
297
|
+
}
|
|
261
298
|
/** The authored benchwork outline, or null when a module hasn't drawn one
|
|
262
299
|
* (renderers then fall back to a band derived from the endplate widths). A
|
|
263
300
|
* valid outline needs at least 3 points. Normalises each vertex to {x, y, bulge?}. */
|
|
264
301
|
declare function benchworkOutline(doc: {
|
|
265
302
|
outline?: BenchworkPoint[] | null;
|
|
266
303
|
} | null | undefined): BenchworkPoint[] | null;
|
|
304
|
+
/** The module's sections, normalised — id required, name trimmed away when
|
|
305
|
+
* blank, outline kept only when it's a usable polygon (#96 phase 2). */
|
|
306
|
+
declare function moduleSections(doc: {
|
|
307
|
+
sections?: SchematicSection[] | null;
|
|
308
|
+
} | null | undefined): SchematicSection[];
|
|
309
|
+
/** Every section outline that's actually a shape, arc-sampled for drawing.
|
|
310
|
+
* Drawing all of them IS the module's footprint — the union of its sections.
|
|
311
|
+
* No polygon boolean is computed: a renderer painting each ring gives the same
|
|
312
|
+
* picture, and an approximate union would be worse than none. If something
|
|
313
|
+
* ever needs a single ring (an export, a collision test), that's the point to
|
|
314
|
+
* bring in real clipping. */
|
|
315
|
+
declare function sectionFootprints(doc: {
|
|
316
|
+
sections?: SchematicSection[] | null;
|
|
317
|
+
} | null | undefined): {
|
|
318
|
+
id: string;
|
|
319
|
+
name?: string;
|
|
320
|
+
outline: {
|
|
321
|
+
x: number;
|
|
322
|
+
y: number;
|
|
323
|
+
}[];
|
|
324
|
+
}[];
|
|
267
325
|
/**
|
|
268
326
|
* Expand a benchwork outline (whose edges may be arcs) into a dense closed
|
|
269
327
|
* polyline for rendering — the SAME sampling both the Repository preview and
|
|
@@ -305,6 +363,9 @@ interface ModuleFootprintInput {
|
|
|
305
363
|
endplateTrackOffsets?: Record<string, number>;
|
|
306
364
|
/** Authored benchwork outline (module-local inches), or absent for the band. */
|
|
307
365
|
outline?: BenchworkPoint[] | null;
|
|
366
|
+
/** The module's sections (#96 phase 2). When any carries an outline, the
|
|
367
|
+
* module's footprint is the union of those — `outline` is then ignored. */
|
|
368
|
+
sections?: SchematicSection[] | null;
|
|
308
369
|
/** Authored mainline centre-line (module-local inches, open path with arcs).
|
|
309
370
|
* When present it wins over the geometry-derived centre-line — the owner drew
|
|
310
371
|
* the real shape (#2d-track, physical view only). */
|
|
@@ -323,13 +384,54 @@ interface ModuleFootprint {
|
|
|
323
384
|
band: BenchworkPoint[];
|
|
324
385
|
/** Endplate faces: [A end, B end]. */
|
|
325
386
|
endplateFaces: OutlineFace[];
|
|
326
|
-
/** Authored outline (arc-sampled closed ring) or null → render the band.
|
|
387
|
+
/** Authored outline (arc-sampled closed ring) or null → render the band.
|
|
388
|
+
* Null too when `sectionOutlines` is non-empty — the sections ARE the shape. */
|
|
327
389
|
outline: BenchworkPoint[] | null;
|
|
390
|
+
/** Per-section footprints, arc-sampled (#96 phase 2). Draw every one: together
|
|
391
|
+
* they are the module's footprint. Empty = this module doesn't use sections,
|
|
392
|
+
* so fall back to `outline ?? band` exactly as before. */
|
|
393
|
+
sectionOutlines: {
|
|
394
|
+
id: string;
|
|
395
|
+
name?: string;
|
|
396
|
+
outline: {
|
|
397
|
+
x: number;
|
|
398
|
+
y: number;
|
|
399
|
+
}[];
|
|
400
|
+
}[];
|
|
328
401
|
}
|
|
329
402
|
/** Module-local main track centre-line (A→B), sampling arcs for curves/corners.
|
|
330
403
|
* An authored `mainPath` wins — the owner drew the real shape; otherwise the
|
|
331
404
|
* centre-line is derived from the geometry fields (length + type/degrees/offset). */
|
|
332
405
|
declare function moduleCenterline(input: ModuleFootprintInput): BenchworkPoint[];
|
|
406
|
+
/** The module's length as the SUM of its sections, or null when it has none
|
|
407
|
+
* (then the authored module length still speaks). */
|
|
408
|
+
declare function moduleLengthFromSections(doc: {
|
|
409
|
+
sections?: SchematicSection[] | null;
|
|
410
|
+
} | null | undefined): number | null;
|
|
411
|
+
/** Where each section starts and ends along the main, inches from endplate A.
|
|
412
|
+
* This is what `sectionBreaks` used to author by hand — now derived, so a
|
|
413
|
+
* length is just a number you type and nothing steals from its neighbour. */
|
|
414
|
+
declare function sectionSpans(doc: {
|
|
415
|
+
sections?: SchematicSection[] | null;
|
|
416
|
+
} | null | undefined): {
|
|
417
|
+
id: string;
|
|
418
|
+
name?: string;
|
|
419
|
+
fromPos: number;
|
|
420
|
+
toPos: number;
|
|
421
|
+
}[];
|
|
422
|
+
/** The joints implied by the sections — the interior boundaries, in inches from
|
|
423
|
+
* endplate A. Replaces the authored `sectionBreaks` for a sectioned module. */
|
|
424
|
+
declare function sectionBreaksFromSections(doc: {
|
|
425
|
+
sections?: SchematicSection[] | null;
|
|
426
|
+
} | null | undefined): number[];
|
|
427
|
+
/** The module's centre-line built by CHAINING its sections — each board starts
|
|
428
|
+
* where the previous one ended, at the heading it ended on. This is what makes
|
|
429
|
+
* a module like One Mile expressible: straight boards with two 24″ curved ones
|
|
430
|
+
* in the middle, which no single module-level geometry can describe (#108).
|
|
431
|
+
* Returns [] when the module has no sections with lengths. */
|
|
432
|
+
declare function sectionedCenterline(doc: {
|
|
433
|
+
sections?: SchematicSection[] | null;
|
|
434
|
+
} | null | undefined): BenchworkPoint[];
|
|
333
435
|
/** Benchwork band: the centre-line offset ±half-width, tapering widthA→widthB. */
|
|
334
436
|
declare function benchworkBand(center: BenchworkPoint[], widthA?: number, widthB?: number, offsetA?: number, offsetB?: number): BenchworkPoint[];
|
|
335
437
|
/** The two endplate faces (the band's flat ends): [A end at widthA, B end at widthB]. */
|
|
@@ -531,6 +633,9 @@ interface EditorState {
|
|
|
531
633
|
/** Internal section joints — inches from endplate A where the boards split
|
|
532
634
|
* into sections. Empty = a single section (#48). */
|
|
533
635
|
sectionBreaks: number[];
|
|
636
|
+
/** The module's sections as named objects, each optionally carrying its own
|
|
637
|
+
* outline (#96 phase 2). Empty = fall back to `outline` + `sectionBreaks`. */
|
|
638
|
+
sections: SchematicSection[];
|
|
534
639
|
controlPoints: EditorControlPoint[];
|
|
535
640
|
/** Rail-served industries — car-spot spans on a track (#industries). */
|
|
536
641
|
industries: EditorIndustry[];
|
|
@@ -815,4 +920,4 @@ declare function poseOverridesFromDoc(doc: ModuleSchematicDoc): Record<string, {
|
|
|
815
920
|
heading: number;
|
|
816
921
|
}>;
|
|
817
922
|
|
|
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 };
|
|
923
|
+
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 SchematicSection, 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, moduleLengthFromSections, moduleSections, nextId, poseNeedsManual, poseOverridesFromDoc, sampleBenchworkOutline, samplePath, scaleFeetToInches, sectionBreaksFromSections, sectionFootprints, sectionSpans, sectionedCenterline, stateToDoc, trackPath };
|
package/dist/index.d.ts
CHANGED
|
@@ -240,8 +240,22 @@ interface ModuleSchematicDoc {
|
|
|
240
240
|
outline?: BenchworkPoint[];
|
|
241
241
|
/** Internal section joints — inches from endplate A where the module's boards
|
|
242
242
|
* split into sections. Operationally one unit; these mark construction/transport
|
|
243
|
-
* seams (exempt from the end-interface standards). Empty/absent = one section.
|
|
243
|
+
* seams (exempt from the end-interface standards). Empty/absent = one section.
|
|
244
|
+
* Describes only sections that are full-depth SLICES; a section with a shape
|
|
245
|
+
* of its own lives in `sections` below. */
|
|
244
246
|
sectionBreaks?: number[];
|
|
247
|
+
/** The module's sections as real objects — named, each with a bench-work
|
|
248
|
+
* outline of its own (#96 phase 2). A module is a kit: the same sections can
|
|
249
|
+
* be set up in different combinations, so its footprint is the UNION of the
|
|
250
|
+
* sections present rather than an independently authored shape.
|
|
251
|
+
*
|
|
252
|
+
* Needed because sections are not slices. Real modules hang a deep section
|
|
253
|
+
* off the BACK of a shallow main band — a peninsula carrying an industry —
|
|
254
|
+
* which no single position along the main can describe.
|
|
255
|
+
*
|
|
256
|
+
* Absent = the module keeps using its own `outline` exactly as before; this
|
|
257
|
+
* is purely additive and nothing migrates on read. */
|
|
258
|
+
sections?: SchematicSection[];
|
|
245
259
|
/** @deprecated pre-grouping flat signals; read for back-compat. */
|
|
246
260
|
signals?: SchematicSignal[];
|
|
247
261
|
/** Authored mainline centre-line (module-local inches, open path with arcs).
|
|
@@ -258,12 +272,56 @@ interface BenchworkPoint {
|
|
|
258
272
|
y: number;
|
|
259
273
|
bulge?: number;
|
|
260
274
|
}
|
|
275
|
+
/** One bench-work section of a module (#96 phase 2). */
|
|
276
|
+
interface SchematicSection {
|
|
277
|
+
id: string;
|
|
278
|
+
/** What the owner calls this board — "west transition", "double #3". */
|
|
279
|
+
name?: string | null;
|
|
280
|
+
/** This section's own footprint polygon, module-local inches, same frame as
|
|
281
|
+
* the module outline. Absent = the section has no shape of its own yet (it's
|
|
282
|
+
* described only by the joints in `sectionBreaks`). */
|
|
283
|
+
outline?: BenchworkPoint[] | null;
|
|
284
|
+
/** How far this board runs along the main, inches. The module's length is the
|
|
285
|
+
* SUM of these — it isn't authored separately (#108). */
|
|
286
|
+
lengthInches?: number | null;
|
|
287
|
+
/** This board's own shape: straight | curve | corner_45 | corner_90 | offset |
|
|
288
|
+
* dead_end. Geometry belongs to the SECTION, not the module — a module like
|
|
289
|
+
* One Mile is 384″ of mostly-straight boards with two 24″ CURVED sections in
|
|
290
|
+
* the middle, which no single module-level geometry can describe (#108).
|
|
291
|
+
* Absent = straight. */
|
|
292
|
+
geometryType?: string | null;
|
|
293
|
+
/** Degrees turned, for curve/corner sections. */
|
|
294
|
+
geometryDegrees?: number | null;
|
|
295
|
+
/** Lateral jog, for offset sections. */
|
|
296
|
+
geometryOffsetInches?: number | null;
|
|
297
|
+
}
|
|
261
298
|
/** The authored benchwork outline, or null when a module hasn't drawn one
|
|
262
299
|
* (renderers then fall back to a band derived from the endplate widths). A
|
|
263
300
|
* valid outline needs at least 3 points. Normalises each vertex to {x, y, bulge?}. */
|
|
264
301
|
declare function benchworkOutline(doc: {
|
|
265
302
|
outline?: BenchworkPoint[] | null;
|
|
266
303
|
} | null | undefined): BenchworkPoint[] | null;
|
|
304
|
+
/** The module's sections, normalised — id required, name trimmed away when
|
|
305
|
+
* blank, outline kept only when it's a usable polygon (#96 phase 2). */
|
|
306
|
+
declare function moduleSections(doc: {
|
|
307
|
+
sections?: SchematicSection[] | null;
|
|
308
|
+
} | null | undefined): SchematicSection[];
|
|
309
|
+
/** Every section outline that's actually a shape, arc-sampled for drawing.
|
|
310
|
+
* Drawing all of them IS the module's footprint — the union of its sections.
|
|
311
|
+
* No polygon boolean is computed: a renderer painting each ring gives the same
|
|
312
|
+
* picture, and an approximate union would be worse than none. If something
|
|
313
|
+
* ever needs a single ring (an export, a collision test), that's the point to
|
|
314
|
+
* bring in real clipping. */
|
|
315
|
+
declare function sectionFootprints(doc: {
|
|
316
|
+
sections?: SchematicSection[] | null;
|
|
317
|
+
} | null | undefined): {
|
|
318
|
+
id: string;
|
|
319
|
+
name?: string;
|
|
320
|
+
outline: {
|
|
321
|
+
x: number;
|
|
322
|
+
y: number;
|
|
323
|
+
}[];
|
|
324
|
+
}[];
|
|
267
325
|
/**
|
|
268
326
|
* Expand a benchwork outline (whose edges may be arcs) into a dense closed
|
|
269
327
|
* polyline for rendering — the SAME sampling both the Repository preview and
|
|
@@ -305,6 +363,9 @@ interface ModuleFootprintInput {
|
|
|
305
363
|
endplateTrackOffsets?: Record<string, number>;
|
|
306
364
|
/** Authored benchwork outline (module-local inches), or absent for the band. */
|
|
307
365
|
outline?: BenchworkPoint[] | null;
|
|
366
|
+
/** The module's sections (#96 phase 2). When any carries an outline, the
|
|
367
|
+
* module's footprint is the union of those — `outline` is then ignored. */
|
|
368
|
+
sections?: SchematicSection[] | null;
|
|
308
369
|
/** Authored mainline centre-line (module-local inches, open path with arcs).
|
|
309
370
|
* When present it wins over the geometry-derived centre-line — the owner drew
|
|
310
371
|
* the real shape (#2d-track, physical view only). */
|
|
@@ -323,13 +384,54 @@ interface ModuleFootprint {
|
|
|
323
384
|
band: BenchworkPoint[];
|
|
324
385
|
/** Endplate faces: [A end, B end]. */
|
|
325
386
|
endplateFaces: OutlineFace[];
|
|
326
|
-
/** Authored outline (arc-sampled closed ring) or null → render the band.
|
|
387
|
+
/** Authored outline (arc-sampled closed ring) or null → render the band.
|
|
388
|
+
* Null too when `sectionOutlines` is non-empty — the sections ARE the shape. */
|
|
327
389
|
outline: BenchworkPoint[] | null;
|
|
390
|
+
/** Per-section footprints, arc-sampled (#96 phase 2). Draw every one: together
|
|
391
|
+
* they are the module's footprint. Empty = this module doesn't use sections,
|
|
392
|
+
* so fall back to `outline ?? band` exactly as before. */
|
|
393
|
+
sectionOutlines: {
|
|
394
|
+
id: string;
|
|
395
|
+
name?: string;
|
|
396
|
+
outline: {
|
|
397
|
+
x: number;
|
|
398
|
+
y: number;
|
|
399
|
+
}[];
|
|
400
|
+
}[];
|
|
328
401
|
}
|
|
329
402
|
/** Module-local main track centre-line (A→B), sampling arcs for curves/corners.
|
|
330
403
|
* An authored `mainPath` wins — the owner drew the real shape; otherwise the
|
|
331
404
|
* centre-line is derived from the geometry fields (length + type/degrees/offset). */
|
|
332
405
|
declare function moduleCenterline(input: ModuleFootprintInput): BenchworkPoint[];
|
|
406
|
+
/** The module's length as the SUM of its sections, or null when it has none
|
|
407
|
+
* (then the authored module length still speaks). */
|
|
408
|
+
declare function moduleLengthFromSections(doc: {
|
|
409
|
+
sections?: SchematicSection[] | null;
|
|
410
|
+
} | null | undefined): number | null;
|
|
411
|
+
/** Where each section starts and ends along the main, inches from endplate A.
|
|
412
|
+
* This is what `sectionBreaks` used to author by hand — now derived, so a
|
|
413
|
+
* length is just a number you type and nothing steals from its neighbour. */
|
|
414
|
+
declare function sectionSpans(doc: {
|
|
415
|
+
sections?: SchematicSection[] | null;
|
|
416
|
+
} | null | undefined): {
|
|
417
|
+
id: string;
|
|
418
|
+
name?: string;
|
|
419
|
+
fromPos: number;
|
|
420
|
+
toPos: number;
|
|
421
|
+
}[];
|
|
422
|
+
/** The joints implied by the sections — the interior boundaries, in inches from
|
|
423
|
+
* endplate A. Replaces the authored `sectionBreaks` for a sectioned module. */
|
|
424
|
+
declare function sectionBreaksFromSections(doc: {
|
|
425
|
+
sections?: SchematicSection[] | null;
|
|
426
|
+
} | null | undefined): number[];
|
|
427
|
+
/** The module's centre-line built by CHAINING its sections — each board starts
|
|
428
|
+
* where the previous one ended, at the heading it ended on. This is what makes
|
|
429
|
+
* a module like One Mile expressible: straight boards with two 24″ curved ones
|
|
430
|
+
* in the middle, which no single module-level geometry can describe (#108).
|
|
431
|
+
* Returns [] when the module has no sections with lengths. */
|
|
432
|
+
declare function sectionedCenterline(doc: {
|
|
433
|
+
sections?: SchematicSection[] | null;
|
|
434
|
+
} | null | undefined): BenchworkPoint[];
|
|
333
435
|
/** Benchwork band: the centre-line offset ±half-width, tapering widthA→widthB. */
|
|
334
436
|
declare function benchworkBand(center: BenchworkPoint[], widthA?: number, widthB?: number, offsetA?: number, offsetB?: number): BenchworkPoint[];
|
|
335
437
|
/** The two endplate faces (the band's flat ends): [A end at widthA, B end at widthB]. */
|
|
@@ -531,6 +633,9 @@ interface EditorState {
|
|
|
531
633
|
/** Internal section joints — inches from endplate A where the boards split
|
|
532
634
|
* into sections. Empty = a single section (#48). */
|
|
533
635
|
sectionBreaks: number[];
|
|
636
|
+
/** The module's sections as named objects, each optionally carrying its own
|
|
637
|
+
* outline (#96 phase 2). Empty = fall back to `outline` + `sectionBreaks`. */
|
|
638
|
+
sections: SchematicSection[];
|
|
534
639
|
controlPoints: EditorControlPoint[];
|
|
535
640
|
/** Rail-served industries — car-spot spans on a track (#industries). */
|
|
536
641
|
industries: EditorIndustry[];
|
|
@@ -815,4 +920,4 @@ declare function poseOverridesFromDoc(doc: ModuleSchematicDoc): Record<string, {
|
|
|
815
920
|
heading: number;
|
|
816
921
|
}>;
|
|
817
922
|
|
|
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 };
|
|
923
|
+
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 SchematicSection, 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, moduleLengthFromSections, moduleSections, nextId, poseNeedsManual, poseOverridesFromDoc, sampleBenchworkOutline, samplePath, scaleFeetToInches, sectionBreaksFromSections, sectionFootprints, sectionSpans, sectionedCenterline, stateToDoc, trackPath };
|
package/dist/index.js
CHANGED
|
@@ -15,6 +15,31 @@ function benchworkOutline(doc) {
|
|
|
15
15
|
}));
|
|
16
16
|
return pts.length >= 3 ? pts : null;
|
|
17
17
|
}
|
|
18
|
+
function moduleSections(doc) {
|
|
19
|
+
return (doc?.sections ?? []).filter((sec) => sec && typeof sec.id === "string" && sec.id !== "").map((sec) => {
|
|
20
|
+
const outline = benchworkOutline({ outline: sec.outline });
|
|
21
|
+
const name = typeof sec.name === "string" ? sec.name.trim() : "";
|
|
22
|
+
const len = sec.lengthInches;
|
|
23
|
+
const deg = sec.geometryDegrees;
|
|
24
|
+
const off = sec.geometryOffsetInches;
|
|
25
|
+
return {
|
|
26
|
+
id: sec.id,
|
|
27
|
+
...name ? { name } : {},
|
|
28
|
+
...outline ? { outline } : {},
|
|
29
|
+
...typeof len === "number" && Number.isFinite(len) && len > 0 ? { lengthInches: len } : {},
|
|
30
|
+
...sec.geometryType ? { geometryType: sec.geometryType } : {},
|
|
31
|
+
...typeof deg === "number" && Number.isFinite(deg) ? { geometryDegrees: deg } : {},
|
|
32
|
+
...typeof off === "number" && Number.isFinite(off) ? { geometryOffsetInches: off } : {}
|
|
33
|
+
};
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
function sectionFootprints(doc) {
|
|
37
|
+
return moduleSections(doc).filter((sec) => sec.outline).map((sec) => ({
|
|
38
|
+
id: sec.id,
|
|
39
|
+
...sec.name ? { name: sec.name } : {},
|
|
40
|
+
outline: sampleBenchworkOutline(sec.outline)
|
|
41
|
+
}));
|
|
42
|
+
}
|
|
18
43
|
function sampleBenchworkOutline(pts, segsPerArc = 20) {
|
|
19
44
|
const n = pts.length;
|
|
20
45
|
if (n < 2) return pts.map((p) => ({ x: p.x, y: p.y }));
|
|
@@ -108,6 +133,8 @@ var DEG_FP = Math.PI / 180;
|
|
|
108
133
|
function moduleCenterline(input) {
|
|
109
134
|
const drawn = trackPath(input.mainPath);
|
|
110
135
|
if (drawn) return samplePath(drawn);
|
|
136
|
+
const chained = sectionedCenterline(input);
|
|
137
|
+
if (chained.length >= 2) return chained;
|
|
111
138
|
if (!input.geometryType) return [];
|
|
112
139
|
const L = input.lengthInches > 0 ? input.lengthInches : 24;
|
|
113
140
|
const gt = input.geometryType;
|
|
@@ -125,6 +152,73 @@ function moduleCenterline(input) {
|
|
|
125
152
|
}
|
|
126
153
|
return pts;
|
|
127
154
|
}
|
|
155
|
+
function sectionCenterlineLocal(sec) {
|
|
156
|
+
const L = typeof sec.lengthInches === "number" && sec.lengthInches > 0 ? sec.lengthInches : 0;
|
|
157
|
+
const gt = sec.geometryType || "straight";
|
|
158
|
+
if (L <= 0) return { points: [{ x: 0, y: 0 }], endX: 0, endY: 0, endHeadingDeg: 0 };
|
|
159
|
+
if (gt === "offset") {
|
|
160
|
+
const dy = sec.geometryOffsetInches ?? 0;
|
|
161
|
+
return { points: [{ x: 0, y: 0 }, { x: L, y: dy }], endX: L, endY: dy, endHeadingDeg: 0 };
|
|
162
|
+
}
|
|
163
|
+
const turn = gt === "corner_45" ? 45 : gt === "corner_90" ? 90 : gt === "curve" ? sec.geometryDegrees ?? 0 : 0;
|
|
164
|
+
if (turn === 0) return { points: [{ x: 0, y: 0 }, { x: L, y: 0 }], endX: L, endY: 0, endHeadingDeg: 0 };
|
|
165
|
+
const t = turn * DEG_FP;
|
|
166
|
+
const r = L / t;
|
|
167
|
+
const steps = 12;
|
|
168
|
+
const points = [];
|
|
169
|
+
for (let i = 0; i <= steps; i++) {
|
|
170
|
+
const a = t * i / steps;
|
|
171
|
+
points.push({ x: r * Math.sin(a), y: r * (1 - Math.cos(a)) });
|
|
172
|
+
}
|
|
173
|
+
const last = points[points.length - 1];
|
|
174
|
+
return { points, endX: last.x, endY: last.y, endHeadingDeg: turn };
|
|
175
|
+
}
|
|
176
|
+
function moduleLengthFromSections(doc) {
|
|
177
|
+
const secs = moduleSections(doc).filter(
|
|
178
|
+
(sec) => typeof sec.lengthInches === "number" && sec.lengthInches > 0
|
|
179
|
+
);
|
|
180
|
+
if (!secs.length) return null;
|
|
181
|
+
return secs.reduce((a, sec) => a + sec.lengthInches, 0);
|
|
182
|
+
}
|
|
183
|
+
function sectionSpans(doc) {
|
|
184
|
+
let acc = 0;
|
|
185
|
+
const out = [];
|
|
186
|
+
for (const sec of moduleSections(doc)) {
|
|
187
|
+
const L = typeof sec.lengthInches === "number" && sec.lengthInches > 0 ? sec.lengthInches : 0;
|
|
188
|
+
if (L <= 0) continue;
|
|
189
|
+
out.push({ id: sec.id, ...sec.name ? { name: sec.name } : {}, fromPos: acc, toPos: acc + L });
|
|
190
|
+
acc += L;
|
|
191
|
+
}
|
|
192
|
+
return out;
|
|
193
|
+
}
|
|
194
|
+
function sectionBreaksFromSections(doc) {
|
|
195
|
+
const spans = sectionSpans(doc);
|
|
196
|
+
return spans.slice(0, -1).map((sp) => sp.toPos);
|
|
197
|
+
}
|
|
198
|
+
function sectionedCenterline(doc) {
|
|
199
|
+
const secs = moduleSections(doc).filter(
|
|
200
|
+
(sec) => typeof sec.lengthInches === "number" && sec.lengthInches > 0
|
|
201
|
+
);
|
|
202
|
+
if (!secs.length) return [];
|
|
203
|
+
const out = [];
|
|
204
|
+
let ox = 0;
|
|
205
|
+
let oy = 0;
|
|
206
|
+
let heading = 0;
|
|
207
|
+
for (const sec of secs) {
|
|
208
|
+
const local = sectionCenterlineLocal(sec);
|
|
209
|
+
const c = Math.cos(heading * DEG_FP);
|
|
210
|
+
const sn = Math.sin(heading * DEG_FP);
|
|
211
|
+
for (let i = 0; i < local.points.length; i++) {
|
|
212
|
+
if (i === 0 && out.length) continue;
|
|
213
|
+
const p = local.points[i];
|
|
214
|
+
out.push({ x: ox + p.x * c - p.y * sn, y: oy + p.x * sn + p.y * c });
|
|
215
|
+
}
|
|
216
|
+
ox += local.endX * c - local.endY * sn;
|
|
217
|
+
oy += local.endX * sn + local.endY * c;
|
|
218
|
+
heading += local.endHeadingDeg;
|
|
219
|
+
}
|
|
220
|
+
return out;
|
|
221
|
+
}
|
|
128
222
|
function centerlineNormals(center) {
|
|
129
223
|
return center.map((_, i) => {
|
|
130
224
|
const a = center[Math.max(0, i - 1)];
|
|
@@ -177,11 +271,13 @@ function moduleFootprint(input) {
|
|
|
177
271
|
const authored = benchworkOutline(input);
|
|
178
272
|
const offA = input.endplateTrackOffsets?.["A"] ?? 0;
|
|
179
273
|
const offB = input.endplateTrackOffsets?.["B"] ?? 0;
|
|
274
|
+
const sectionOutlines = sectionFootprints(input);
|
|
180
275
|
return {
|
|
181
276
|
centerline,
|
|
182
277
|
band: benchworkBand(centerline, widthA, widthB, offA, offB),
|
|
183
278
|
endplateFaces: endplateFaceSegments(centerline, widthA, widthB, offA, offB),
|
|
184
|
-
outline: authored ? sampleBenchworkOutline(authored)
|
|
279
|
+
outline: sectionOutlines.length || !authored ? null : sampleBenchworkOutline(authored),
|
|
280
|
+
sectionOutlines
|
|
185
281
|
};
|
|
186
282
|
}
|
|
187
283
|
function endplateTrackOffsetFor(config, authoredTrackOffset) {
|
|
@@ -262,6 +358,7 @@ function emptyEditorState(lengthInches) {
|
|
|
262
358
|
endplateTrackOffsets: {},
|
|
263
359
|
outline: [],
|
|
264
360
|
sectionBreaks: [],
|
|
361
|
+
sections: [],
|
|
265
362
|
controlPoints: [],
|
|
266
363
|
industries: [],
|
|
267
364
|
mainPath: []
|
|
@@ -485,6 +582,9 @@ function stateToDoc(state, recordNumber) {
|
|
|
485
582
|
...state.outline.length >= 3 ? { outline: state.outline } : {},
|
|
486
583
|
// Internal section joints (inches from A), when the module has more than one.
|
|
487
584
|
...state.sectionBreaks.length ? { sectionBreaks: state.sectionBreaks } : {},
|
|
585
|
+
// Sections as objects — emitted only once the owner has some, so a module
|
|
586
|
+
// that never used them keeps exactly the doc it had before (#96 phase 2).
|
|
587
|
+
...state.sections.length ? { sections: moduleSections({ sections: state.sections }) } : {},
|
|
488
588
|
// Authored mainline path (module-local inches); only when it's a real path.
|
|
489
589
|
...state.mainPath.length >= 2 ? { mainPath: state.mainPath } : {}
|
|
490
590
|
};
|
|
@@ -590,6 +690,7 @@ function docToState(doc, fallbackLength, moduleTracks = []) {
|
|
|
590
690
|
endplateTrackOffsets,
|
|
591
691
|
outline,
|
|
592
692
|
sectionBreaks: (d.sectionBreaks ?? []).filter((n) => Number.isFinite(n)).map((n) => sc(n)),
|
|
693
|
+
sections: moduleSections(d),
|
|
593
694
|
mainPath,
|
|
594
695
|
crossings: (d.crossings ?? []).map((x) => ({
|
|
595
696
|
id: x.id,
|
|
@@ -1091,6 +1192,6 @@ function poseOverridesFromDoc(doc) {
|
|
|
1091
1192
|
return out;
|
|
1092
1193
|
}
|
|
1093
1194
|
|
|
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 };
|
|
1195
|
+
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, moduleLengthFromSections, moduleSections, nextId, poseNeedsManual, poseOverridesFromDoc, sampleBenchworkOutline, samplePath, scaleFeetToInches, sectionBreaksFromSections, sectionFootprints, sectionSpans, sectionedCenterline, stateToDoc, trackPath };
|
|
1095
1196
|
//# sourceMappingURL=index.js.map
|
|
1096
1197
|
//# sourceMappingURL=index.js.map
|