@willcgage/module-schematic 0.31.0 → 0.33.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 +48 -1
- package/dist/index.d.ts +48 -1
- package/dist/index.js +105 -2
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.d.cts
CHANGED
|
@@ -438,6 +438,19 @@ declare function sectionBreaksFromSections(doc: {
|
|
|
438
438
|
* a module like One Mile expressible: straight boards with two 24″ curved ones
|
|
439
439
|
* in the middle, which no single module-level geometry can describe (#108).
|
|
440
440
|
* Returns [] when the module has no sections with lengths. */
|
|
441
|
+
/** Where the chained boards finish, and on what heading — computed from the
|
|
442
|
+
* section geometry itself rather than read off the sampled polyline. A curve
|
|
443
|
+
* is sampled in steps, so the last chord lags the true tangent by half a step
|
|
444
|
+
* (a 90° board sampled 12 ways reads 86.25°). That error would land straight
|
|
445
|
+
* in endplate B's heading and throw off face-to-face snapping, so the exact
|
|
446
|
+
* value is accumulated here instead. Null when there are no sections. */
|
|
447
|
+
declare function sectionedEndPose(doc: {
|
|
448
|
+
sections?: SchematicSection[] | null;
|
|
449
|
+
} | null | undefined): {
|
|
450
|
+
x: number;
|
|
451
|
+
y: number;
|
|
452
|
+
heading: number;
|
|
453
|
+
} | null;
|
|
441
454
|
declare function sectionedCenterline(doc: {
|
|
442
455
|
sections?: SchematicSection[] | null;
|
|
443
456
|
} | null | undefined): BenchworkPoint[];
|
|
@@ -455,6 +468,36 @@ declare function sliceCenterline(center: BenchworkPoint[], fromPos: number, toPo
|
|
|
455
468
|
* authored polygon gets one derived from its span, so resizing the board
|
|
456
469
|
* reshapes it instead of leaving a hand-drawn outline stranded. */
|
|
457
470
|
declare function sectionBand(center: BenchworkPoint[], fromPos: number, toPos: number, widthA?: number, widthB?: number, offsetA?: number, offsetB?: number): BenchworkPoint[];
|
|
471
|
+
/** Two sections that physically meet, and how much edge they share. */
|
|
472
|
+
interface SectionAdjacency {
|
|
473
|
+
a: string;
|
|
474
|
+
b: string;
|
|
475
|
+
/** Inches of shared edge — a butt joint across a 24″ board reads ~24. */
|
|
476
|
+
lengthInches: number;
|
|
477
|
+
}
|
|
478
|
+
/**
|
|
479
|
+
* Which sections physically MEET, derived from shared polygon edges rather
|
|
480
|
+
* than list order (#96 phase 2c).
|
|
481
|
+
*
|
|
482
|
+
* Order is the wrong model as soon as a module stops being a row of boards: a
|
|
483
|
+
* peninsula hangs off the BACK of a shallow band over part of its length, so
|
|
484
|
+
* it neighbours a board it isn't next to in any list. Geometry is the only
|
|
485
|
+
* thing that knows.
|
|
486
|
+
*/
|
|
487
|
+
declare function sectionAdjacency(footprints: SectionFootprint[], opts?: {
|
|
488
|
+
gapInches?: number;
|
|
489
|
+
angleDegrees?: number;
|
|
490
|
+
minOverlapInches?: number;
|
|
491
|
+
}): SectionAdjacency[];
|
|
492
|
+
/** The sections each one touches. */
|
|
493
|
+
declare function sectionNeighbours(id: string, adj: SectionAdjacency[]): string[];
|
|
494
|
+
/**
|
|
495
|
+
* Groups of sections that hang together, as connected components. One group
|
|
496
|
+
* means the module is a single piece of bench work; more than one means some
|
|
497
|
+
* board is floating free — an authoring mistake now, and the test #96 phase 3
|
|
498
|
+
* needs before it can say whether dropping a section leaves the rest intact.
|
|
499
|
+
*/
|
|
500
|
+
declare function sectionComponents(ids: string[], adj: SectionAdjacency[]): string[][];
|
|
458
501
|
/** The two endplate faces (the band's flat ends): [A end at widthA, B end at widthB]. */
|
|
459
502
|
declare function endplateFaceSegments(center: BenchworkPoint[], widthA?: number, widthB?: number, offsetA?: number, offsetB?: number): OutlineFace[];
|
|
460
503
|
/**
|
|
@@ -919,6 +962,10 @@ interface ModuleGeometryInput {
|
|
|
919
962
|
/** Half the spacing between the two tracks of a double endplate (Free-mo ≈ 1",
|
|
920
963
|
* Free-moN ≈ 9/16"). */
|
|
921
964
|
trackHalfSpacingInches?: number;
|
|
965
|
+
/** The module's sections. When present they define the module's real shape,
|
|
966
|
+
* so endplate B lands at the end of the CHAINED boards rather than where a
|
|
967
|
+
* single module-level geometry would have put it (#108). */
|
|
968
|
+
sections?: SchematicSection[] | null;
|
|
922
969
|
}
|
|
923
970
|
/** Signed turn a module applies to the through track (CCW/left positive). */
|
|
924
971
|
declare function geometryTurnDegrees(geometryType?: string | null, geometryDegrees?: number | null): number;
|
|
@@ -941,4 +988,4 @@ declare function poseOverridesFromDoc(doc: ModuleSchematicDoc): Record<string, {
|
|
|
941
988
|
heading: number;
|
|
942
989
|
}>;
|
|
943
990
|
|
|
944
|
-
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 SectionFootprint, 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, sectionBand, sectionBreaksFromSections, sectionFootprints, sectionSpans, sectionedCenterline, sliceCenterline, stateToDoc, trackPath };
|
|
991
|
+
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 SectionAdjacency, type SectionFootprint, 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, sectionAdjacency, sectionBand, sectionBreaksFromSections, sectionComponents, sectionFootprints, sectionNeighbours, sectionSpans, sectionedCenterline, sectionedEndPose, sliceCenterline, stateToDoc, trackPath };
|
package/dist/index.d.ts
CHANGED
|
@@ -438,6 +438,19 @@ declare function sectionBreaksFromSections(doc: {
|
|
|
438
438
|
* a module like One Mile expressible: straight boards with two 24″ curved ones
|
|
439
439
|
* in the middle, which no single module-level geometry can describe (#108).
|
|
440
440
|
* Returns [] when the module has no sections with lengths. */
|
|
441
|
+
/** Where the chained boards finish, and on what heading — computed from the
|
|
442
|
+
* section geometry itself rather than read off the sampled polyline. A curve
|
|
443
|
+
* is sampled in steps, so the last chord lags the true tangent by half a step
|
|
444
|
+
* (a 90° board sampled 12 ways reads 86.25°). That error would land straight
|
|
445
|
+
* in endplate B's heading and throw off face-to-face snapping, so the exact
|
|
446
|
+
* value is accumulated here instead. Null when there are no sections. */
|
|
447
|
+
declare function sectionedEndPose(doc: {
|
|
448
|
+
sections?: SchematicSection[] | null;
|
|
449
|
+
} | null | undefined): {
|
|
450
|
+
x: number;
|
|
451
|
+
y: number;
|
|
452
|
+
heading: number;
|
|
453
|
+
} | null;
|
|
441
454
|
declare function sectionedCenterline(doc: {
|
|
442
455
|
sections?: SchematicSection[] | null;
|
|
443
456
|
} | null | undefined): BenchworkPoint[];
|
|
@@ -455,6 +468,36 @@ declare function sliceCenterline(center: BenchworkPoint[], fromPos: number, toPo
|
|
|
455
468
|
* authored polygon gets one derived from its span, so resizing the board
|
|
456
469
|
* reshapes it instead of leaving a hand-drawn outline stranded. */
|
|
457
470
|
declare function sectionBand(center: BenchworkPoint[], fromPos: number, toPos: number, widthA?: number, widthB?: number, offsetA?: number, offsetB?: number): BenchworkPoint[];
|
|
471
|
+
/** Two sections that physically meet, and how much edge they share. */
|
|
472
|
+
interface SectionAdjacency {
|
|
473
|
+
a: string;
|
|
474
|
+
b: string;
|
|
475
|
+
/** Inches of shared edge — a butt joint across a 24″ board reads ~24. */
|
|
476
|
+
lengthInches: number;
|
|
477
|
+
}
|
|
478
|
+
/**
|
|
479
|
+
* Which sections physically MEET, derived from shared polygon edges rather
|
|
480
|
+
* than list order (#96 phase 2c).
|
|
481
|
+
*
|
|
482
|
+
* Order is the wrong model as soon as a module stops being a row of boards: a
|
|
483
|
+
* peninsula hangs off the BACK of a shallow band over part of its length, so
|
|
484
|
+
* it neighbours a board it isn't next to in any list. Geometry is the only
|
|
485
|
+
* thing that knows.
|
|
486
|
+
*/
|
|
487
|
+
declare function sectionAdjacency(footprints: SectionFootprint[], opts?: {
|
|
488
|
+
gapInches?: number;
|
|
489
|
+
angleDegrees?: number;
|
|
490
|
+
minOverlapInches?: number;
|
|
491
|
+
}): SectionAdjacency[];
|
|
492
|
+
/** The sections each one touches. */
|
|
493
|
+
declare function sectionNeighbours(id: string, adj: SectionAdjacency[]): string[];
|
|
494
|
+
/**
|
|
495
|
+
* Groups of sections that hang together, as connected components. One group
|
|
496
|
+
* means the module is a single piece of bench work; more than one means some
|
|
497
|
+
* board is floating free — an authoring mistake now, and the test #96 phase 3
|
|
498
|
+
* needs before it can say whether dropping a section leaves the rest intact.
|
|
499
|
+
*/
|
|
500
|
+
declare function sectionComponents(ids: string[], adj: SectionAdjacency[]): string[][];
|
|
458
501
|
/** The two endplate faces (the band's flat ends): [A end at widthA, B end at widthB]. */
|
|
459
502
|
declare function endplateFaceSegments(center: BenchworkPoint[], widthA?: number, widthB?: number, offsetA?: number, offsetB?: number): OutlineFace[];
|
|
460
503
|
/**
|
|
@@ -919,6 +962,10 @@ interface ModuleGeometryInput {
|
|
|
919
962
|
/** Half the spacing between the two tracks of a double endplate (Free-mo ≈ 1",
|
|
920
963
|
* Free-moN ≈ 9/16"). */
|
|
921
964
|
trackHalfSpacingInches?: number;
|
|
965
|
+
/** The module's sections. When present they define the module's real shape,
|
|
966
|
+
* so endplate B lands at the end of the CHAINED boards rather than where a
|
|
967
|
+
* single module-level geometry would have put it (#108). */
|
|
968
|
+
sections?: SchematicSection[] | null;
|
|
922
969
|
}
|
|
923
970
|
/** Signed turn a module applies to the through track (CCW/left positive). */
|
|
924
971
|
declare function geometryTurnDegrees(geometryType?: string | null, geometryDegrees?: number | null): number;
|
|
@@ -941,4 +988,4 @@ declare function poseOverridesFromDoc(doc: ModuleSchematicDoc): Record<string, {
|
|
|
941
988
|
heading: number;
|
|
942
989
|
}>;
|
|
943
990
|
|
|
944
|
-
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 SectionFootprint, 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, sectionBand, sectionBreaksFromSections, sectionFootprints, sectionSpans, sectionedCenterline, sliceCenterline, stateToDoc, trackPath };
|
|
991
|
+
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 SectionAdjacency, type SectionFootprint, 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, sectionAdjacency, sectionBand, sectionBreaksFromSections, sectionComponents, sectionFootprints, sectionNeighbours, sectionSpans, sectionedCenterline, sectionedEndPose, sliceCenterline, stateToDoc, trackPath };
|
package/dist/index.js
CHANGED
|
@@ -209,6 +209,24 @@ function sectionBreaksFromSections(doc) {
|
|
|
209
209
|
const spans = sectionSpans(doc);
|
|
210
210
|
return spans.slice(0, -1).map((sp) => sp.toPos);
|
|
211
211
|
}
|
|
212
|
+
function sectionedEndPose(doc) {
|
|
213
|
+
const secs = moduleSections(doc).filter(
|
|
214
|
+
(sec) => typeof sec.lengthInches === "number" && sec.lengthInches > 0
|
|
215
|
+
);
|
|
216
|
+
if (!secs.length) return null;
|
|
217
|
+
let ox = 0;
|
|
218
|
+
let oy = 0;
|
|
219
|
+
let heading = 0;
|
|
220
|
+
for (const sec of secs) {
|
|
221
|
+
const local = sectionCenterlineLocal(sec);
|
|
222
|
+
const c = Math.cos(heading * DEG_FP);
|
|
223
|
+
const sn = Math.sin(heading * DEG_FP);
|
|
224
|
+
ox += local.endX * c - local.endY * sn;
|
|
225
|
+
oy += local.endX * sn + local.endY * c;
|
|
226
|
+
heading += local.endHeadingDeg;
|
|
227
|
+
}
|
|
228
|
+
return { x: ox, y: oy, heading };
|
|
229
|
+
}
|
|
212
230
|
function sectionedCenterline(doc) {
|
|
213
231
|
const secs = moduleSections(doc).filter(
|
|
214
232
|
(sec) => typeof sec.lengthInches === "number" && sec.lengthInches > 0
|
|
@@ -325,6 +343,79 @@ function sectionBand(center, fromPos, toPos, widthA = FREEMO_ENDPLATE_WIDTH_RECO
|
|
|
325
343
|
}));
|
|
326
344
|
return [...left, ...right.reverse()];
|
|
327
345
|
}
|
|
346
|
+
function ringEdges(pts) {
|
|
347
|
+
const out = [];
|
|
348
|
+
for (let i = 0; i < pts.length; i++) out.push([pts[i], pts[(i + 1) % pts.length]]);
|
|
349
|
+
return out;
|
|
350
|
+
}
|
|
351
|
+
function sharedEdgeLength(e1, e2, gap, angleDeg) {
|
|
352
|
+
const ux = e1[1].x - e1[0].x;
|
|
353
|
+
const uy = e1[1].y - e1[0].y;
|
|
354
|
+
const ul = Math.hypot(ux, uy);
|
|
355
|
+
const vx = e2[1].x - e2[0].x;
|
|
356
|
+
const vy = e2[1].y - e2[0].y;
|
|
357
|
+
const vl = Math.hypot(vx, vy);
|
|
358
|
+
if (ul < 1e-6 || vl < 1e-6) return 0;
|
|
359
|
+
const dx = ux / ul;
|
|
360
|
+
const dy = uy / ul;
|
|
361
|
+
const cross = Math.abs((dx * vy - dy * vx) / vl);
|
|
362
|
+
if (cross > Math.sin(angleDeg * Math.PI / 180)) return 0;
|
|
363
|
+
const perp = (q) => Math.abs((q.x - e1[0].x) * -dy + (q.y - e1[0].y) * dx);
|
|
364
|
+
if (perp(e2[0]) > gap || perp(e2[1]) > gap) return 0;
|
|
365
|
+
const proj = (q) => (q.x - e1[0].x) * dx + (q.y - e1[0].y) * dy;
|
|
366
|
+
const t1 = proj(e2[0]);
|
|
367
|
+
const t2 = proj(e2[1]);
|
|
368
|
+
return Math.max(0, Math.min(ul, Math.max(t1, t2)) - Math.max(0, Math.min(t1, t2)));
|
|
369
|
+
}
|
|
370
|
+
function sectionAdjacency(footprints, opts) {
|
|
371
|
+
const gap = opts?.gapInches ?? 0.5;
|
|
372
|
+
const angle = opts?.angleDegrees ?? 3;
|
|
373
|
+
const min = opts?.minOverlapInches ?? 1;
|
|
374
|
+
const edges = footprints.map((f) => ringEdges(f.outline));
|
|
375
|
+
const out = [];
|
|
376
|
+
for (let i = 0; i < footprints.length; i++) {
|
|
377
|
+
for (let j = i + 1; j < footprints.length; j++) {
|
|
378
|
+
let total = 0;
|
|
379
|
+
for (const e1 of edges[i])
|
|
380
|
+
for (const e2 of edges[j]) total += sharedEdgeLength(e1, e2, gap, angle);
|
|
381
|
+
if (total >= min)
|
|
382
|
+
out.push({
|
|
383
|
+
a: footprints[i].id,
|
|
384
|
+
b: footprints[j].id,
|
|
385
|
+
lengthInches: Math.round(total * 1e3) / 1e3
|
|
386
|
+
});
|
|
387
|
+
}
|
|
388
|
+
}
|
|
389
|
+
return out;
|
|
390
|
+
}
|
|
391
|
+
function sectionNeighbours(id, adj) {
|
|
392
|
+
return adj.filter((x) => x.a === id || x.b === id).map((x) => x.a === id ? x.b : x.a);
|
|
393
|
+
}
|
|
394
|
+
function sectionComponents(ids, adj) {
|
|
395
|
+
const parent = new Map(ids.map((id) => [id, id]));
|
|
396
|
+
const find = (x) => {
|
|
397
|
+
let r = x;
|
|
398
|
+
while (parent.get(r) !== r) r = parent.get(r);
|
|
399
|
+
while (parent.get(x) !== r) {
|
|
400
|
+
const nx = parent.get(x);
|
|
401
|
+
parent.set(x, r);
|
|
402
|
+
x = nx;
|
|
403
|
+
}
|
|
404
|
+
return r;
|
|
405
|
+
};
|
|
406
|
+
for (const { a, b } of adj) {
|
|
407
|
+
if (!parent.has(a) || !parent.has(b)) continue;
|
|
408
|
+
const ra = find(a);
|
|
409
|
+
const rb = find(b);
|
|
410
|
+
if (ra !== rb) parent.set(ra, rb);
|
|
411
|
+
}
|
|
412
|
+
const groups = /* @__PURE__ */ new Map();
|
|
413
|
+
for (const id of ids) {
|
|
414
|
+
const r = find(id);
|
|
415
|
+
groups.set(r, [...groups.get(r) ?? [], id]);
|
|
416
|
+
}
|
|
417
|
+
return [...groups.values()];
|
|
418
|
+
}
|
|
328
419
|
function endplateFaceSegments(center, widthA = FREEMO_ENDPLATE_WIDTH_RECOMMENDED_INCHES, widthB = FREEMO_ENDPLATE_WIDTH_RECOMMENDED_INCHES, offsetA = 0, offsetB = 0) {
|
|
329
420
|
if (center.length < 2) return [];
|
|
330
421
|
const n = centerlineNormals(center);
|
|
@@ -1208,7 +1299,19 @@ function deriveEndplatePoses(geo) {
|
|
|
1208
1299
|
})
|
|
1209
1300
|
);
|
|
1210
1301
|
const noB = geo.geometryType === "dead_end";
|
|
1211
|
-
|
|
1302
|
+
const end = sectionedEndPose({ sections: geo.sections });
|
|
1303
|
+
if (!noB && end) {
|
|
1304
|
+
poses.push(
|
|
1305
|
+
withOverride({
|
|
1306
|
+
id: "B",
|
|
1307
|
+
x: end.x,
|
|
1308
|
+
y: end.y,
|
|
1309
|
+
heading: norm360(end.heading),
|
|
1310
|
+
trackConfig: cfg(1),
|
|
1311
|
+
trackOffsets: offsetsFor(cfg(1), half)
|
|
1312
|
+
})
|
|
1313
|
+
);
|
|
1314
|
+
} else if (!noB) {
|
|
1212
1315
|
const turn = geometryTurnDegrees(geo.geometryType, geo.geometryDegrees);
|
|
1213
1316
|
let bx;
|
|
1214
1317
|
let by;
|
|
@@ -1269,6 +1372,6 @@ function poseOverridesFromDoc(doc) {
|
|
|
1269
1372
|
return out;
|
|
1270
1373
|
}
|
|
1271
1374
|
|
|
1272
|
-
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, sectionBand, sectionBreaksFromSections, sectionFootprints, sectionSpans, sectionedCenterline, sliceCenterline, stateToDoc, trackPath };
|
|
1375
|
+
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, sectionAdjacency, sectionBand, sectionBreaksFromSections, sectionComponents, sectionFootprints, sectionNeighbours, sectionSpans, sectionedCenterline, sectionedEndPose, sliceCenterline, stateToDoc, trackPath };
|
|
1273
1376
|
//# sourceMappingURL=index.js.map
|
|
1274
1377
|
//# sourceMappingURL=index.js.map
|