@willcgage/module-schematic 0.14.0 → 0.16.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 +71 -1
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +37 -1
- package/dist/index.d.ts +37 -1
- package/dist/index.js +70 -2
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.d.cts
CHANGED
|
@@ -156,9 +156,41 @@ interface ModuleSchematicDoc {
|
|
|
156
156
|
/** Grade crossings / diamonds (#170). */
|
|
157
157
|
crossings?: SchematicCrossing[];
|
|
158
158
|
controlPoints?: SchematicControlPoint[];
|
|
159
|
+
/** Benchwork FOOTPRINT outline — the module's physical board shape as a
|
|
160
|
+
* polygon in module-local inches, in the same frame as the endplate poses
|
|
161
|
+
* (endplate A's track point at the origin, the mainline along +x, perpendicular
|
|
162
|
+
* +y up). Stored as an open ring; renderers close it. Absent = derive an
|
|
163
|
+
* approximate band from the endplate widths. */
|
|
164
|
+
outline?: BenchworkPoint[];
|
|
159
165
|
/** @deprecated pre-grouping flat signals; read for back-compat. */
|
|
160
166
|
signals?: SchematicSignal[];
|
|
161
167
|
}
|
|
168
|
+
/** A benchwork-outline vertex, module-local inches. The edge from this vertex
|
|
169
|
+
* to the NEXT one is a straight line, unless `bulge` is set — then it's a
|
|
170
|
+
* circular arc whose midpoint is offset `bulge` inches (signed: + bows to the
|
|
171
|
+
* left of the P→next direction) perpendicular from the chord. */
|
|
172
|
+
interface BenchworkPoint {
|
|
173
|
+
x: number;
|
|
174
|
+
y: number;
|
|
175
|
+
bulge?: number;
|
|
176
|
+
}
|
|
177
|
+
/** The authored benchwork outline, or null when a module hasn't drawn one
|
|
178
|
+
* (renderers then fall back to a band derived from the endplate widths). A
|
|
179
|
+
* valid outline needs at least 3 points. Normalises each vertex to {x, y, bulge?}. */
|
|
180
|
+
declare function benchworkOutline(doc: {
|
|
181
|
+
outline?: BenchworkPoint[] | null;
|
|
182
|
+
} | null | undefined): BenchworkPoint[] | null;
|
|
183
|
+
/**
|
|
184
|
+
* Expand a benchwork outline (whose edges may be arcs) into a dense closed
|
|
185
|
+
* polyline for rendering — the SAME sampling both the Repository preview and
|
|
186
|
+
* Free-Dispatcher use, so a curve looks identical in both. Straight edges emit
|
|
187
|
+
* just their start vertex; a bulged edge emits `segsPerArc` points along the
|
|
188
|
+
* circular arc through the two endpoints and the bulged midpoint.
|
|
189
|
+
*/
|
|
190
|
+
declare function sampleBenchworkOutline(pts: BenchworkPoint[], segsPerArc?: number): {
|
|
191
|
+
x: number;
|
|
192
|
+
y: number;
|
|
193
|
+
}[];
|
|
162
194
|
/** Whether a doc is a single-endplate turnback (explicit flag or one endplate). */
|
|
163
195
|
declare function isLoopDoc(doc: ModuleSchematicDoc): boolean;
|
|
164
196
|
declare const MAIN_TRACK_ID = "main";
|
|
@@ -263,6 +295,10 @@ interface EditorState {
|
|
|
263
295
|
/** Authored endplate face widths by endplate id, inches (Free-moN 12″ min,
|
|
264
296
|
* 24″ recommended). Absent id = the recommended default. */
|
|
265
297
|
endplateWidths: Record<string, number>;
|
|
298
|
+
/** Benchwork footprint outline — polygon vertices in module-local inches
|
|
299
|
+
* (endplate A's track point at the origin, mainline +x, perpendicular +y up).
|
|
300
|
+
* Empty = no authored outline (fall back to the endplate-width band). */
|
|
301
|
+
outline: BenchworkPoint[];
|
|
266
302
|
controlPoints: EditorControlPoint[];
|
|
267
303
|
}
|
|
268
304
|
/** Build the empty editor state for a module of the given length. */
|
|
@@ -523,4 +559,4 @@ declare function poseOverridesFromDoc(doc: ModuleSchematicDoc): Record<string, {
|
|
|
523
559
|
heading: number;
|
|
524
560
|
}>;
|
|
525
561
|
|
|
526
|
-
export { type BranchConnector, type DrawCrossing, type DrawCrossover, type DrawSignal, type DrawTrack, type DrawTurnout, type EditorBranch, type EditorControlPoint, type EditorCpSignal, type EditorCrossing, type EditorState, type EditorTrack, type EditorTurnout, type EndplateBConfig, type EndplatePose, FREEMO_ENDPLATE_WIDTH_MIN_INCHES, FREEMO_ENDPLATE_WIDTH_RECOMMENDED_INCHES, type GeometryType, MAIN2_TRACK_ID, MAIN_TRACK_ID, type ModuleFeatures, type ModuleGeometryInput, type ModuleSchematicDoc, type ModuleTrackRow, N_SCALE_RATIO, type SchematicBlock, type SchematicControlPoint, type SchematicCrossing, type SchematicEndplate, type SchematicEndplateTrack, type SchematicSignal, type SchematicTrack, type SchematicTurnout, type SignalFacing, type SignalSide, type TrackConfig, type TrackRole, type TurnoutKind, asModuleSchematic, buildCrossover, buildPassingSiding, buildTransition, deriveEndplatePoses, divergeSideForHand, docToState, emptyEditorState, endplateWidthInches, geometryTurnDegrees, inchesToScaleFeet, isLoopDoc, isTransitionTurnout, moduleFeatures, nextId, poseNeedsManual, poseOverridesFromDoc, scaleFeetToInches, stateToDoc };
|
|
562
|
+
export { type BenchworkPoint, type BranchConnector, type DrawCrossing, type DrawCrossover, type DrawSignal, type DrawTrack, type DrawTurnout, type EditorBranch, type EditorControlPoint, type EditorCpSignal, type EditorCrossing, type EditorState, type EditorTrack, type EditorTurnout, type EndplateBConfig, type EndplatePose, FREEMO_ENDPLATE_WIDTH_MIN_INCHES, FREEMO_ENDPLATE_WIDTH_RECOMMENDED_INCHES, type GeometryType, MAIN2_TRACK_ID, MAIN_TRACK_ID, type ModuleFeatures, type ModuleGeometryInput, type ModuleSchematicDoc, type ModuleTrackRow, N_SCALE_RATIO, type SchematicBlock, type SchematicControlPoint, type SchematicCrossing, type SchematicEndplate, type SchematicEndplateTrack, type SchematicSignal, type SchematicTrack, type SchematicTurnout, type SignalFacing, type SignalSide, type TrackConfig, type TrackRole, type TurnoutKind, asModuleSchematic, benchworkOutline, buildCrossover, buildPassingSiding, buildTransition, deriveEndplatePoses, divergeSideForHand, docToState, emptyEditorState, endplateWidthInches, geometryTurnDegrees, inchesToScaleFeet, isLoopDoc, isTransitionTurnout, moduleFeatures, nextId, poseNeedsManual, poseOverridesFromDoc, sampleBenchworkOutline, scaleFeetToInches, stateToDoc };
|
package/dist/index.d.ts
CHANGED
|
@@ -156,9 +156,41 @@ interface ModuleSchematicDoc {
|
|
|
156
156
|
/** Grade crossings / diamonds (#170). */
|
|
157
157
|
crossings?: SchematicCrossing[];
|
|
158
158
|
controlPoints?: SchematicControlPoint[];
|
|
159
|
+
/** Benchwork FOOTPRINT outline — the module's physical board shape as a
|
|
160
|
+
* polygon in module-local inches, in the same frame as the endplate poses
|
|
161
|
+
* (endplate A's track point at the origin, the mainline along +x, perpendicular
|
|
162
|
+
* +y up). Stored as an open ring; renderers close it. Absent = derive an
|
|
163
|
+
* approximate band from the endplate widths. */
|
|
164
|
+
outline?: BenchworkPoint[];
|
|
159
165
|
/** @deprecated pre-grouping flat signals; read for back-compat. */
|
|
160
166
|
signals?: SchematicSignal[];
|
|
161
167
|
}
|
|
168
|
+
/** A benchwork-outline vertex, module-local inches. The edge from this vertex
|
|
169
|
+
* to the NEXT one is a straight line, unless `bulge` is set — then it's a
|
|
170
|
+
* circular arc whose midpoint is offset `bulge` inches (signed: + bows to the
|
|
171
|
+
* left of the P→next direction) perpendicular from the chord. */
|
|
172
|
+
interface BenchworkPoint {
|
|
173
|
+
x: number;
|
|
174
|
+
y: number;
|
|
175
|
+
bulge?: number;
|
|
176
|
+
}
|
|
177
|
+
/** The authored benchwork outline, or null when a module hasn't drawn one
|
|
178
|
+
* (renderers then fall back to a band derived from the endplate widths). A
|
|
179
|
+
* valid outline needs at least 3 points. Normalises each vertex to {x, y, bulge?}. */
|
|
180
|
+
declare function benchworkOutline(doc: {
|
|
181
|
+
outline?: BenchworkPoint[] | null;
|
|
182
|
+
} | null | undefined): BenchworkPoint[] | null;
|
|
183
|
+
/**
|
|
184
|
+
* Expand a benchwork outline (whose edges may be arcs) into a dense closed
|
|
185
|
+
* polyline for rendering — the SAME sampling both the Repository preview and
|
|
186
|
+
* Free-Dispatcher use, so a curve looks identical in both. Straight edges emit
|
|
187
|
+
* just their start vertex; a bulged edge emits `segsPerArc` points along the
|
|
188
|
+
* circular arc through the two endpoints and the bulged midpoint.
|
|
189
|
+
*/
|
|
190
|
+
declare function sampleBenchworkOutline(pts: BenchworkPoint[], segsPerArc?: number): {
|
|
191
|
+
x: number;
|
|
192
|
+
y: number;
|
|
193
|
+
}[];
|
|
162
194
|
/** Whether a doc is a single-endplate turnback (explicit flag or one endplate). */
|
|
163
195
|
declare function isLoopDoc(doc: ModuleSchematicDoc): boolean;
|
|
164
196
|
declare const MAIN_TRACK_ID = "main";
|
|
@@ -263,6 +295,10 @@ interface EditorState {
|
|
|
263
295
|
/** Authored endplate face widths by endplate id, inches (Free-moN 12″ min,
|
|
264
296
|
* 24″ recommended). Absent id = the recommended default. */
|
|
265
297
|
endplateWidths: Record<string, number>;
|
|
298
|
+
/** Benchwork footprint outline — polygon vertices in module-local inches
|
|
299
|
+
* (endplate A's track point at the origin, mainline +x, perpendicular +y up).
|
|
300
|
+
* Empty = no authored outline (fall back to the endplate-width band). */
|
|
301
|
+
outline: BenchworkPoint[];
|
|
266
302
|
controlPoints: EditorControlPoint[];
|
|
267
303
|
}
|
|
268
304
|
/** Build the empty editor state for a module of the given length. */
|
|
@@ -523,4 +559,4 @@ declare function poseOverridesFromDoc(doc: ModuleSchematicDoc): Record<string, {
|
|
|
523
559
|
heading: number;
|
|
524
560
|
}>;
|
|
525
561
|
|
|
526
|
-
export { type BranchConnector, type DrawCrossing, type DrawCrossover, type DrawSignal, type DrawTrack, type DrawTurnout, type EditorBranch, type EditorControlPoint, type EditorCpSignal, type EditorCrossing, type EditorState, type EditorTrack, type EditorTurnout, type EndplateBConfig, type EndplatePose, FREEMO_ENDPLATE_WIDTH_MIN_INCHES, FREEMO_ENDPLATE_WIDTH_RECOMMENDED_INCHES, type GeometryType, MAIN2_TRACK_ID, MAIN_TRACK_ID, type ModuleFeatures, type ModuleGeometryInput, type ModuleSchematicDoc, type ModuleTrackRow, N_SCALE_RATIO, type SchematicBlock, type SchematicControlPoint, type SchematicCrossing, type SchematicEndplate, type SchematicEndplateTrack, type SchematicSignal, type SchematicTrack, type SchematicTurnout, type SignalFacing, type SignalSide, type TrackConfig, type TrackRole, type TurnoutKind, asModuleSchematic, buildCrossover, buildPassingSiding, buildTransition, deriveEndplatePoses, divergeSideForHand, docToState, emptyEditorState, endplateWidthInches, geometryTurnDegrees, inchesToScaleFeet, isLoopDoc, isTransitionTurnout, moduleFeatures, nextId, poseNeedsManual, poseOverridesFromDoc, scaleFeetToInches, stateToDoc };
|
|
562
|
+
export { type BenchworkPoint, type BranchConnector, type DrawCrossing, type DrawCrossover, type DrawSignal, type DrawTrack, type DrawTurnout, type EditorBranch, type EditorControlPoint, type EditorCpSignal, type EditorCrossing, type EditorState, type EditorTrack, type EditorTurnout, type EndplateBConfig, type EndplatePose, FREEMO_ENDPLATE_WIDTH_MIN_INCHES, FREEMO_ENDPLATE_WIDTH_RECOMMENDED_INCHES, type GeometryType, MAIN2_TRACK_ID, MAIN_TRACK_ID, type ModuleFeatures, type ModuleGeometryInput, type ModuleSchematicDoc, type ModuleTrackRow, N_SCALE_RATIO, type SchematicBlock, type SchematicControlPoint, type SchematicCrossing, type SchematicEndplate, type SchematicEndplateTrack, type SchematicSignal, type SchematicTrack, type SchematicTurnout, type SignalFacing, type SignalSide, type TrackConfig, type TrackRole, type TurnoutKind, asModuleSchematic, benchworkOutline, buildCrossover, buildPassingSiding, buildTransition, deriveEndplatePoses, divergeSideForHand, docToState, emptyEditorState, endplateWidthInches, geometryTurnDegrees, inchesToScaleFeet, isLoopDoc, isTransitionTurnout, moduleFeatures, nextId, poseNeedsManual, poseOverridesFromDoc, sampleBenchworkOutline, scaleFeetToInches, stateToDoc };
|
package/dist/index.js
CHANGED
|
@@ -5,6 +5,64 @@ function endplateWidthInches(ep) {
|
|
|
5
5
|
const w = ep?.widthInches;
|
|
6
6
|
return typeof w === "number" && w > 0 ? w : FREEMO_ENDPLATE_WIDTH_RECOMMENDED_INCHES;
|
|
7
7
|
}
|
|
8
|
+
function benchworkOutline(doc) {
|
|
9
|
+
const pts = (doc?.outline ?? []).filter((p) => p && Number.isFinite(p.x) && Number.isFinite(p.y)).map((p) => ({
|
|
10
|
+
x: p.x,
|
|
11
|
+
y: p.y,
|
|
12
|
+
...Number.isFinite(p.bulge) && p.bulge ? { bulge: p.bulge } : {}
|
|
13
|
+
}));
|
|
14
|
+
return pts.length >= 3 ? pts : null;
|
|
15
|
+
}
|
|
16
|
+
function sampleBenchworkOutline(pts, segsPerArc = 20) {
|
|
17
|
+
const n = pts.length;
|
|
18
|
+
if (n < 2) return pts.map((p) => ({ x: p.x, y: p.y }));
|
|
19
|
+
const out = [];
|
|
20
|
+
for (let i = 0; i < n; i++) {
|
|
21
|
+
const p0 = pts[i];
|
|
22
|
+
const p1 = pts[(i + 1) % n];
|
|
23
|
+
out.push({ x: p0.x, y: p0.y });
|
|
24
|
+
const bulge = p0.bulge ?? 0;
|
|
25
|
+
if (!bulge) continue;
|
|
26
|
+
const dx = p1.x - p0.x;
|
|
27
|
+
const dy = p1.y - p0.y;
|
|
28
|
+
const c = Math.hypot(dx, dy);
|
|
29
|
+
if (c < 1e-6) continue;
|
|
30
|
+
const nx = -dy / c;
|
|
31
|
+
const ny = dx / c;
|
|
32
|
+
const mid = { x: (p0.x + p1.x) / 2 + nx * bulge, y: (p0.y + p1.y) / 2 + ny * bulge };
|
|
33
|
+
const circ = circleThrough(p0, mid, p1);
|
|
34
|
+
if (!circ) continue;
|
|
35
|
+
const a0 = Math.atan2(p0.y - circ.cy, p0.x - circ.cx);
|
|
36
|
+
const am = Math.atan2(mid.y - circ.cy, mid.x - circ.cx);
|
|
37
|
+
const a1 = Math.atan2(p1.y - circ.cy, p1.x - circ.cx);
|
|
38
|
+
const sweep = arcSweep(a0, a1, am);
|
|
39
|
+
for (let s = 1; s < segsPerArc; s++) {
|
|
40
|
+
const a = a0 + sweep * s / segsPerArc;
|
|
41
|
+
out.push({ x: circ.cx + circ.r * Math.cos(a), y: circ.cy + circ.r * Math.sin(a) });
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
return out;
|
|
45
|
+
}
|
|
46
|
+
function circleThrough(a, b, c) {
|
|
47
|
+
const d = 2 * (a.x * (b.y - c.y) + b.x * (c.y - a.y) + c.x * (a.y - b.y));
|
|
48
|
+
if (Math.abs(d) < 1e-9) return null;
|
|
49
|
+
const a2 = a.x * a.x + a.y * a.y;
|
|
50
|
+
const b2 = b.x * b.x + b.y * b.y;
|
|
51
|
+
const c2 = c.x * c.x + c.y * c.y;
|
|
52
|
+
const cx = (a2 * (b.y - c.y) + b2 * (c.y - a.y) + c2 * (a.y - b.y)) / d;
|
|
53
|
+
const cy = (a2 * (c.x - b.x) + b2 * (a.x - c.x) + c2 * (b.x - a.x)) / d;
|
|
54
|
+
return { cx, cy, r: Math.hypot(a.x - cx, a.y - cy) };
|
|
55
|
+
}
|
|
56
|
+
function arcSweep(a0, a1, am) {
|
|
57
|
+
const norm = (x) => {
|
|
58
|
+
let v = (x - a0) % (2 * Math.PI);
|
|
59
|
+
if (v < 0) v += 2 * Math.PI;
|
|
60
|
+
return v;
|
|
61
|
+
};
|
|
62
|
+
const m = norm(am);
|
|
63
|
+
const one = norm(a1);
|
|
64
|
+
return m <= one ? one : one - 2 * Math.PI;
|
|
65
|
+
}
|
|
8
66
|
function isLoopDoc(doc) {
|
|
9
67
|
return doc.loop === true || doc.endplates.length === 1;
|
|
10
68
|
}
|
|
@@ -37,6 +95,7 @@ function emptyEditorState(lengthInches) {
|
|
|
37
95
|
branches: [],
|
|
38
96
|
poseOverrides: {},
|
|
39
97
|
endplateWidths: {},
|
|
98
|
+
outline: [],
|
|
40
99
|
controlPoints: []
|
|
41
100
|
};
|
|
42
101
|
}
|
|
@@ -206,7 +265,10 @@ function stateToDoc(state, recordNumber) {
|
|
|
206
265
|
kind: "mast",
|
|
207
266
|
side: s.side
|
|
208
267
|
}))
|
|
209
|
-
}))
|
|
268
|
+
})),
|
|
269
|
+
// Benchwork footprint outline (module-local inches); only when it's a real
|
|
270
|
+
// ring (≥ 3 vertices).
|
|
271
|
+
...state.outline.length >= 3 ? { outline: state.outline } : {}
|
|
210
272
|
};
|
|
211
273
|
}
|
|
212
274
|
function docToState(doc, fallbackLength, moduleTracks = []) {
|
|
@@ -280,6 +342,11 @@ function docToState(doc, fallbackLength, moduleTracks = []) {
|
|
|
280
342
|
if (typeof e.widthInches === "number" && e.widthInches > 0)
|
|
281
343
|
endplateWidths[e.id] = e.widthInches;
|
|
282
344
|
}
|
|
345
|
+
const outline = (d.outline ?? []).filter((p) => p && Number.isFinite(p.x) && Number.isFinite(p.y)).map((p) => ({
|
|
346
|
+
x: p.x,
|
|
347
|
+
y: p.y,
|
|
348
|
+
...Number.isFinite(p.bulge) && p.bulge ? { bulge: p.bulge } : {}
|
|
349
|
+
}));
|
|
283
350
|
return {
|
|
284
351
|
lengthInches: len,
|
|
285
352
|
loop,
|
|
@@ -295,6 +362,7 @@ function docToState(doc, fallbackLength, moduleTracks = []) {
|
|
|
295
362
|
})),
|
|
296
363
|
poseOverrides,
|
|
297
364
|
endplateWidths,
|
|
365
|
+
outline,
|
|
298
366
|
crossings: (d.crossings ?? []).map((x) => ({
|
|
299
367
|
id: x.id,
|
|
300
368
|
name: x.name ?? "",
|
|
@@ -752,6 +820,6 @@ function poseOverridesFromDoc(doc) {
|
|
|
752
820
|
return out;
|
|
753
821
|
}
|
|
754
822
|
|
|
755
|
-
export { FREEMO_ENDPLATE_WIDTH_MIN_INCHES, FREEMO_ENDPLATE_WIDTH_RECOMMENDED_INCHES, MAIN2_TRACK_ID, MAIN_TRACK_ID, N_SCALE_RATIO, asModuleSchematic, buildCrossover, buildPassingSiding, buildTransition, deriveEndplatePoses, divergeSideForHand, docToState, emptyEditorState, endplateWidthInches, geometryTurnDegrees, inchesToScaleFeet, isLoopDoc, isTransitionTurnout, moduleFeatures, nextId, poseNeedsManual, poseOverridesFromDoc, scaleFeetToInches, stateToDoc };
|
|
823
|
+
export { FREEMO_ENDPLATE_WIDTH_MIN_INCHES, FREEMO_ENDPLATE_WIDTH_RECOMMENDED_INCHES, MAIN2_TRACK_ID, MAIN_TRACK_ID, N_SCALE_RATIO, asModuleSchematic, benchworkOutline, buildCrossover, buildPassingSiding, buildTransition, deriveEndplatePoses, divergeSideForHand, docToState, emptyEditorState, endplateWidthInches, geometryTurnDegrees, inchesToScaleFeet, isLoopDoc, isTransitionTurnout, moduleFeatures, nextId, poseNeedsManual, poseOverridesFromDoc, sampleBenchworkOutline, scaleFeetToInches, stateToDoc };
|
|
756
824
|
//# sourceMappingURL=index.js.map
|
|
757
825
|
//# sourceMappingURL=index.js.map
|