@expofp/wayfinding-compiler 0.0.0-experimental.3b0db16 → 0.0.0-stage

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.
Files changed (100) hide show
  1. package/README.md +2 -445
  2. package/package.json +3 -34
  3. package/dist/index.d.ts +0 -14
  4. package/dist/index.js +0 -3
  5. package/dist/lib/attach.d.ts +0 -43
  6. package/dist/lib/attach.js +0 -199
  7. package/dist/lib/axes.d.ts +0 -76
  8. package/dist/lib/axes.js +0 -103
  9. package/dist/lib/blockers.d.ts +0 -134
  10. package/dist/lib/blockers.js +0 -289
  11. package/dist/lib/cell-array.d.ts +0 -14
  12. package/dist/lib/cell-array.js +0 -25
  13. package/dist/lib/compile-layer.d.ts +0 -41
  14. package/dist/lib/compile-layer.js +0 -241
  15. package/dist/lib/compile-wayfinding.d.ts +0 -90
  16. package/dist/lib/compile-wayfinding.js +0 -203
  17. package/dist/lib/connect.d.ts +0 -35
  18. package/dist/lib/connect.js +0 -206
  19. package/dist/lib/diagnostics.d.ts +0 -190
  20. package/dist/lib/diagnostics.js +0 -634
  21. package/dist/lib/doorways.d.ts +0 -78
  22. package/dist/lib/doorways.js +0 -331
  23. package/dist/lib/emit-network.d.ts +0 -47
  24. package/dist/lib/emit-network.js +0 -105
  25. package/dist/lib/emit-v1.d.ts +0 -31
  26. package/dist/lib/emit-v1.js +0 -56
  27. package/dist/lib/entries.d.ts +0 -123
  28. package/dist/lib/entries.js +0 -448
  29. package/dist/lib/floor-grids.d.ts +0 -48
  30. package/dist/lib/floor-grids.js +0 -185
  31. package/dist/lib/framing.d.ts +0 -20
  32. package/dist/lib/framing.js +0 -187
  33. package/dist/lib/geom/boolean-ops.d.ts +0 -21
  34. package/dist/lib/geom/boolean-ops.js +0 -1
  35. package/dist/lib/geom/clipper2-ops.d.ts +0 -7
  36. package/dist/lib/geom/clipper2-ops.js +0 -50
  37. package/dist/lib/geom/footprint.d.ts +0 -26
  38. package/dist/lib/geom/footprint.js +0 -203
  39. package/dist/lib/geom/frames.d.ts +0 -116
  40. package/dist/lib/geom/frames.js +0 -233
  41. package/dist/lib/geom/mesh-outline.d.ts +0 -13
  42. package/dist/lib/geom/mesh-outline.js +0 -90
  43. package/dist/lib/geom/normalize.d.ts +0 -28
  44. package/dist/lib/geom/normalize.js +0 -118
  45. package/dist/lib/geom/point-key.d.ts +0 -16
  46. package/dist/lib/geom/point-key.js +0 -15
  47. package/dist/lib/geom/primitives.d.ts +0 -105
  48. package/dist/lib/geom/primitives.js +0 -184
  49. package/dist/lib/geom/split-crossings.d.ts +0 -14
  50. package/dist/lib/geom/split-crossings.js +0 -171
  51. package/dist/lib/geom/types.d.ts +0 -9
  52. package/dist/lib/geom/types.js +0 -1
  53. package/dist/lib/geom/walls.d.ts +0 -31
  54. package/dist/lib/geom/walls.js +0 -71
  55. package/dist/lib/graph-invariants.d.ts +0 -49
  56. package/dist/lib/graph-invariants.js +0 -129
  57. package/dist/lib/graph-types.d.ts +0 -143
  58. package/dist/lib/graph-types.js +0 -7
  59. package/dist/lib/mask.d.ts +0 -54
  60. package/dist/lib/mask.js +0 -210
  61. package/dist/lib/min-heap.d.ts +0 -24
  62. package/dist/lib/min-heap.js +0 -73
  63. package/dist/lib/network-build.d.ts +0 -21
  64. package/dist/lib/network-build.js +0 -4
  65. package/dist/lib/network-graph.d.ts +0 -80
  66. package/dist/lib/network-graph.js +0 -467
  67. package/dist/lib/network-sizes.d.ts +0 -29
  68. package/dist/lib/network-sizes.js +0 -22
  69. package/dist/lib/network-store.d.ts +0 -173
  70. package/dist/lib/network-store.js +0 -430
  71. package/dist/lib/openings.d.ts +0 -54
  72. package/dist/lib/openings.js +0 -387
  73. package/dist/lib/place-path.d.ts +0 -47
  74. package/dist/lib/place-path.js +0 -346
  75. package/dist/lib/post-passes.d.ts +0 -31
  76. package/dist/lib/post-passes.js +0 -305
  77. package/dist/lib/reachability.d.ts +0 -58
  78. package/dist/lib/reachability.js +0 -220
  79. package/dist/lib/report.d.ts +0 -49
  80. package/dist/lib/report.js +0 -374
  81. package/dist/lib/router.d.ts +0 -111
  82. package/dist/lib/router.js +0 -388
  83. package/dist/lib/runs.d.ts +0 -92
  84. package/dist/lib/runs.js +0 -232
  85. package/dist/lib/scale.d.ts +0 -58
  86. package/dist/lib/scale.js +0 -185
  87. package/dist/lib/steiner-network.d.ts +0 -19
  88. package/dist/lib/steiner-network.js +0 -183
  89. package/dist/lib/strip.d.ts +0 -43
  90. package/dist/lib/strip.js +0 -200
  91. package/dist/lib/stubs.d.ts +0 -84
  92. package/dist/lib/stubs.js +0 -316
  93. package/dist/lib/transitions.d.ts +0 -72
  94. package/dist/lib/transitions.js +0 -191
  95. package/dist/lib/types.d.ts +0 -126
  96. package/dist/lib/types.js +0 -1
  97. package/dist/lib/validate-source.d.ts +0 -41
  98. package/dist/lib/validate-source.js +0 -133
  99. package/dist/lib/walkable.d.ts +0 -104
  100. package/dist/lib/walkable.js +0 -140
package/dist/lib/runs.js DELETED
@@ -1,232 +0,0 @@
1
- // The straight runs of a raster path, and the line each one is laid on: the doorway's middle where
2
- // it passes one, its aisle's axis, else the middle of its corridor.
3
- import { alongAcross } from './axes.js';
4
- import { blockersNear } from './blockers.js';
5
- import { placeIn, toFrame } from './geom/frames.js';
6
- import { snapCm } from './geom/point-key.js';
7
- import { boundsOf } from './geom/primitives.js';
8
- /**
9
- * How far across a run may be pulled onto a line: a tip stands within the clearance of its aisle's
10
- * axis, and a run's cells are centered within half a pitch of what they pass.
11
- * @param cfg Network sizes.
12
- * @returns The distance.
13
- */
14
- export function pullOf(cfg) {
15
- return cfg.clear + cfg.pitch / 2;
16
- }
17
- /**
18
- * A point in a run's coordinates.
19
- * @param run The run.
20
- * @param x Plan x.
21
- * @param y Plan y.
22
- * @returns Along the run, and across it.
23
- */
24
- export function runCoords(run, x, y) {
25
- return alongAcross(run.frame, run.horiz, x, y);
26
- }
27
- /**
28
- * The point at a run's coordinates, on the centimeter grid.
29
- * @param run The run.
30
- * @param along Coordinate along the run.
31
- * @param across Coordinate across it.
32
- * @returns The point.
33
- */
34
- export function onRun(run, along, across) {
35
- return run.horiz ? placeIn(run.frame, along, across) : placeIn(run.frame, across, along);
36
- }
37
- /**
38
- * The run's line at an across coordinate, from its first cell's center to its last's.
39
- * @param run The run.
40
- * @param c The across coordinate.
41
- * @returns The segment.
42
- */
43
- export function runLine(run, c) {
44
- return [...onRun(run, run.lo, c), ...onRun(run, run.hi, c)];
45
- }
46
- /**
47
- * The unit direction the run was walked in.
48
- * @param run The run.
49
- * @returns The direction.
50
- */
51
- export function runDirection(run) {
52
- const [x0, y0, x1, y1] = runLine(run, run.c);
53
- const len = Math.hypot(x1 - x0, y1 - y0) || 1;
54
- return [((x1 - x0) / len) * run.sign, ((y1 - y0) / len) * run.sign];
55
- }
56
- /**
57
- * The straight runs of a path: consecutive cells of one grid stepping the same way.
58
- * @param router The raster router the path was walked on.
59
- * @param cells The path.
60
- * @returns The runs.
61
- */
62
- export function runsOf(router, cells) {
63
- const runs = [];
64
- let cur = null;
65
- for (let i = 0; i + 1 < cells.length; i++) {
66
- const same = router.frameOf(cells[i]) === router.frameOf(cells[i + 1]);
67
- const step = cells[i + 1] - cells[i];
68
- // A grid change is no step: the run ends and the next starts on the other grid.
69
- if (!same || Math.abs(step) === 0) {
70
- cur = null;
71
- continue;
72
- }
73
- if (cur?.step === step)
74
- cur.cells.push(cells[i + 1]);
75
- else {
76
- cur = { grid: router.frameOf(cells[i]), step, cells: [cells[i], cells[i + 1]] };
77
- runs.push(toRun(router, cur.grid, cur.cells));
78
- runs[runs.length - 1].cells = cur.cells;
79
- }
80
- }
81
- // Read again once the cells are complete.
82
- return runs.map((run) => toRun(router, run.frame, run.cells));
83
- }
84
- /**
85
- * A run read from its cells: its frame, where it was walked across, and its reach along.
86
- * @param router The raster router.
87
- * @param frame The frame of the cells' grid.
88
- * @param cells The cells walked.
89
- * @returns The run.
90
- */
91
- function toRun(router, frame, cells) {
92
- const [ux, uy] = router.worldOf(cells[0]);
93
- const [vx, vy] = router.worldOf(cells[cells.length - 1]);
94
- const [u0, v0] = toFrame(frame, ux, uy);
95
- const [u1, v1] = toFrame(frame, vx, vy);
96
- const horiz = Math.abs(u1 - u0) >= Math.abs(v1 - v0);
97
- const walked = snapCm(horiz ? (v0 + v1) / 2 : (u0 + u1) / 2);
98
- const a = horiz ? u0 : v0;
99
- const b = horiz ? u1 : v1;
100
- return {
101
- frame,
102
- horiz,
103
- walked,
104
- c: walked,
105
- lo: Math.min(a, b),
106
- hi: Math.max(a, b),
107
- sign: b >= a ? 1 : -1,
108
- cells,
109
- };
110
- }
111
- /**
112
- * The doorway's middle a run passes through, as an across coordinate.
113
- * @param run The run.
114
- * @param doors The layer's doorways.
115
- * @param cfg Network sizes.
116
- * @param pull How far across a run may be pulled onto a line.
117
- * @returns The coordinate, or null when the run passes no doorway square on.
118
- */
119
- function doorMiddle(run, doors, cfg, pull) {
120
- const seg = runLine(run, run.walked);
121
- const box = [
122
- Math.min(seg[0], seg[2]) - cfg.pitch,
123
- Math.min(seg[1], seg[3]) - cfg.pitch,
124
- Math.max(seg[0], seg[2]) + cfg.pitch,
125
- Math.max(seg[1], seg[3]) + cfg.pitch,
126
- ];
127
- for (const door of doors) {
128
- // The doorway's normal runs along the run, as the raster can tell over the doorway's width.
129
- const width = Math.hypot(door.gap[2] - door.gap[0], door.gap[3] - door.gap[1]);
130
- const [nu, nv] = toFrame(run.frame, door.nx, door.ny);
131
- if ((run.horiz ? Math.abs(nv) : Math.abs(nu)) > cfg.cs / width)
132
- continue;
133
- const [mx, my] = door.mid;
134
- if (mx < box[0] || mx > box[2] || my < box[1] || my > box[3])
135
- continue;
136
- const [at, c] = runCoords(run, mx, my);
137
- if (at < run.lo - cfg.pitch || at > run.hi + cfg.pitch || Math.abs(c - run.walked) > pull)
138
- continue;
139
- return snapCm(c);
140
- }
141
- return null;
142
- }
143
- /**
144
- * The axis of the aisle a run lies in.
145
- * @param run The run.
146
- * @param axes The aisle axes the stub tips define.
147
- * @param pull How far across a run may be pulled onto a line.
148
- * @returns Its across coordinate, or null when the run lies in no aisle.
149
- */
150
- function aisleAxis(run, axes, pull) {
151
- let best = null;
152
- for (const axis of axes) {
153
- if (axis.frame !== run.frame || axis.horiz !== run.horiz)
154
- continue;
155
- if (Math.abs(axis.c - run.walked) > pull)
156
- continue;
157
- if (axis.lo > run.hi || run.lo > axis.hi)
158
- continue;
159
- if (!best || Math.abs(axis.c - run.walked) < Math.abs(best.c - run.walked))
160
- best = axis;
161
- }
162
- return best && snapCm(best.c);
163
- }
164
- /**
165
- * The middle of the corridor a run passes: between the nearest blockers either side along its
166
- * whole length. An open hall wider than the run is long has none.
167
- * @param run The run.
168
- * @param blockers Walls and solid outlines: a corridor's sides.
169
- * @param cfg Network sizes.
170
- * @returns The across coordinate, or null.
171
- */
172
- function corridorMiddle(run, blockers, cfg) {
173
- const reach = run.hi - run.lo;
174
- if (reach < 2 * cfg.clear)
175
- return null;
176
- const box = boundsOf([
177
- onRun(run, run.lo, run.walked - reach),
178
- onRun(run, run.lo, run.walked + reach),
179
- onRun(run, run.hi, run.walked - reach),
180
- onRun(run, run.hi, run.walked + reach),
181
- ]);
182
- let left = -Infinity;
183
- let right = Infinity;
184
- for (const { seg } of blockersNear(blockers, box)) {
185
- const [a0, p0] = runCoords(run, seg[0], seg[1]);
186
- const [a1, p1] = runCoords(run, seg[2], seg[3]);
187
- let from = 0;
188
- let to = 1;
189
- if (a1 === a0) {
190
- if (a0 <= run.lo || a0 >= run.hi)
191
- continue;
192
- }
193
- else {
194
- const t0 = (run.lo - a0) / (a1 - a0);
195
- const t1 = (run.hi - a0) / (a1 - a0);
196
- from = Math.max(0, Math.min(t0, t1));
197
- to = Math.min(1, Math.max(t0, t1));
198
- if (from >= to)
199
- continue;
200
- }
201
- const q0 = p0 + (p1 - p0) * from;
202
- const q1 = p0 + (p1 - p0) * to;
203
- if (Math.max(q0, q1) < run.walked)
204
- left = Math.max(left, Math.max(q0, q1));
205
- else if (Math.min(q0, q1) > run.walked)
206
- right = Math.min(right, Math.min(q0, q1));
207
- }
208
- return right - left > reach ? null : snapCm((left + right) / 2);
209
- }
210
- /**
211
- * Lays a run where its place says: a doorway's middle, its aisle's axis, its corridor's middle,
212
- * else where it was walked.
213
- * @param run The run; `c` is set.
214
- * @param place What the runs are laid on.
215
- * @param cfg Network sizes.
216
- * @param mayLay Whether a line may be laid.
217
- */
218
- export function placeRun(run, place, cfg, mayLay) {
219
- const pull = pullOf(cfg);
220
- for (const c of [
221
- doorMiddle(run, place.doors, cfg, pull),
222
- aisleAxis(run, place.axes, pull),
223
- corridorMiddle(run, place.blockers, cfg),
224
- ]) {
225
- if (c === null)
226
- continue;
227
- if (c === run.walked || mayLay(runLine(run, c))) {
228
- run.c = c;
229
- return;
230
- }
231
- }
232
- }
@@ -1,58 +0,0 @@
1
- import type { RouteNetwork } from './graph-types.js';
2
- import type { TransitionOrphan } from './transitions.js';
3
- import type { SourceDestination, WayfindingSource, WfV1Graph } from './types.js';
4
- import type { WalkableArea } from './walkable.js';
5
- /**
6
- * Decimals of an emitted plan coordinate: two — 1 cm in meters, 3 mm in feet — and more where a plan
7
- * unit is longer than a meter, so a step is never coarser than the centimeter node grid and rounding
8
- * only merges points the network already merged. At most twenty: a double holds no more, and
9
- * `toFixed` throws past a hundred.
10
- * @param unitsPerMeter Plan units per meter.
11
- * @returns The decimals to keep.
12
- */
13
- export declare function planDecimals(unitsPerMeter: number): number;
14
- /**
15
- * The plan in meters. Every threshold is in meters, so the plan is converted once on the way in and once
16
- * on the way out. A plan already in meters is rounded the same way.
17
- * @param source The plan in its own units.
18
- * @param unitsPerMeter Plan coordinates in one meter.
19
- * @returns The same plan with every coordinate in meters.
20
- */
21
- export declare function sourceInMeters(source: WayfindingSource, unitsPerMeter: number): WayfindingSource;
22
- /**
23
- * A walkable area in the other measure. Its `area` stat is a surface, so it moves by the square.
24
- * @param area The artifact to convert.
25
- * @param factor Coordinates in the target measure per coordinate in the current one.
26
- * @param round Round to plan precision — true on the way out.
27
- * @returns The converted artifact.
28
- */
29
- export declare function walkableScaled(area: WalkableArea, factor: number, round: boolean): WalkableArea;
30
- /**
31
- * A route network back in the plan's own units.
32
- * @param network The artifact to convert.
33
- * @param unitsPerMeter Plan coordinates in one meter.
34
- * @returns The converted artifact.
35
- */
36
- export declare function networkInPlanUnits(network: RouteNetwork, unitsPerMeter: number): RouteNetwork;
37
- /**
38
- * The destinations as the stages saw them, back in the plan's own units.
39
- * @param destinations The carved footprints.
40
- * @param unitsPerMeter Plan coordinates in one meter.
41
- * @returns The converted destinations.
42
- */
43
- export declare function destinationsInPlanUnits(destinations: readonly SourceDestination[], unitsPerMeter: number): SourceDestination[];
44
- /**
45
- * Transition stops that never reached the graph, back in plan units.
46
- * @param orphans The stops, in meters.
47
- * @param unitsPerMeter Plan coordinates in one meter.
48
- * @returns The stops in plan coordinates.
49
- */
50
- export declare function orphansInPlanUnits(orphans: readonly TransitionOrphan[], unitsPerMeter: number): readonly TransitionOrphan[];
51
- /**
52
- * The emitted graph back in the plan's own units — what the host snaps its POI to.
53
- * @param graph The graph as compiled, in meters.
54
- * @param unitsPerMeter Plan coordinates in one meter.
55
- * @returns The graph in plan coordinates.
56
- */
57
- export declare function graphInPlanUnits(graph: WfV1Graph, unitsPerMeter: number): WfV1Graph;
58
- //# sourceMappingURL=scale.d.ts.map
package/dist/lib/scale.js DELETED
@@ -1,185 +0,0 @@
1
- /** The most decimals a plan coordinate keeps. */
2
- const MAX_PLAN_DECIMALS = 20;
3
- /**
4
- * Decimals of an emitted plan coordinate: two — 1 cm in meters, 3 mm in feet — and more where a plan
5
- * unit is longer than a meter, so a step is never coarser than the centimeter node grid and rounding
6
- * only merges points the network already merged. At most twenty: a double holds no more, and
7
- * `toFixed` throws past a hundred.
8
- * @param unitsPerMeter Plan units per meter.
9
- * @returns The decimals to keep.
10
- */
11
- export function planDecimals(unitsPerMeter) {
12
- return Math.min(MAX_PLAN_DECIMALS, Math.max(2, Math.ceil(2 - Math.log10(unitsPerMeter))));
13
- }
14
- /**
15
- * Decimals kept in meters — a micron. Rounding off the division's last bits makes the same drawing in feet
16
- * and in meters reach the stages as identical numbers, so boundary comparisons agree.
17
- */
18
- const METER_DECIMALS = 6;
19
- function place(v, factor, round) {
20
- const scaled = v * factor;
21
- return +scaled.toFixed(round ? planDecimals(factor) : METER_DECIMALS);
22
- }
23
- function scaleRing(ring, factor, round) {
24
- return ring.map(([x, y]) => [place(x, factor, round), place(y, factor, round)]);
25
- }
26
- function scaleFlat(flat, factor, round) {
27
- return flat.map((v) => place(v, factor, round));
28
- }
29
- function scaleDestination(d, factor, round) {
30
- return {
31
- ...d,
32
- polygon: scaleRing(d.polygon, factor, round),
33
- ...(d.parts && { parts: d.parts.map((part) => scaleRing(part, factor, round)) }),
34
- ...(d.dock && { dock: scaleRing(d.dock, factor, round) }),
35
- };
36
- }
37
- function scaleLayer(layer, factor) {
38
- return {
39
- ...layer,
40
- walls: layer.walls.map((flat) => scaleFlat(flat, factor, false)),
41
- destinations: layer.destinations.map((d) => scaleDestination(d, factor, false)),
42
- };
43
- }
44
- function scaleTransition(transition, factor) {
45
- return {
46
- ...transition,
47
- points: transition.points.map((point) => ({
48
- ...point,
49
- x: place(point.x, factor, false),
50
- y: place(point.y, factor, false),
51
- })),
52
- };
53
- }
54
- /**
55
- * The plan in meters. Every threshold is in meters, so the plan is converted once on the way in and once
56
- * on the way out. A plan already in meters is rounded the same way.
57
- * @param source The plan in its own units.
58
- * @param unitsPerMeter Plan coordinates in one meter.
59
- * @returns The same plan with every coordinate in meters.
60
- */
61
- export function sourceInMeters(source, unitsPerMeter) {
62
- const factor = 1 / unitsPerMeter;
63
- return {
64
- ...source,
65
- layers: source.layers.map((layer) => scaleLayer(layer, factor)),
66
- ...(source.transitions && {
67
- transitions: source.transitions.map((t) => scaleTransition(t, factor)),
68
- }),
69
- };
70
- }
71
- /**
72
- * A walkable area in the other measure. Its `area` stat is a surface, so it moves by the square.
73
- * @param area The artifact to convert.
74
- * @param factor Coordinates in the target measure per coordinate in the current one.
75
- * @param round Round to plan precision — true on the way out.
76
- * @returns The converted artifact.
77
- */
78
- export function walkableScaled(area, factor, round) {
79
- if (factor === 1)
80
- return area;
81
- const line = (wall) => ({
82
- ...wall,
83
- points: scaleRing([...wall.points], factor, round),
84
- });
85
- return {
86
- rings: area.rings.map((ring) => scaleRing(ring, factor, round)),
87
- outline: area.outline.map((ring) => scaleRing(ring, factor, round)),
88
- wallStrips: area.wallStrips.map((ring) => scaleRing(ring, factor, round)),
89
- wallLines: area.wallLines.map(line),
90
- stats: {
91
- ...area.stats,
92
- area: +(area.stats.area * factor * factor).toFixed(1),
93
- ...(area.stats.areaExact !== undefined && {
94
- areaExact: area.stats.areaExact * factor * factor,
95
- }),
96
- },
97
- };
98
- }
99
- /**
100
- * A route network back in the plan's own units.
101
- * @param network The artifact to convert.
102
- * @param unitsPerMeter Plan coordinates in one meter.
103
- * @returns The converted artifact.
104
- */
105
- export function networkInPlanUnits(network, unitsPerMeter) {
106
- if (unitsPerMeter === 1)
107
- return network;
108
- const at = ([x, y]) => [x * unitsPerMeter, y * unitsPerMeter];
109
- const anatomy = network.anatomy && {
110
- unit: unitsPerMeter,
111
- doors: network.anatomy.doors.map((d) => ({
112
- gap: [at(d.gap[0]), at(d.gap[1])],
113
- mid: at(d.mid),
114
- })),
115
- anchors: network.anatomy.anchors.map((a) => ({ ...a, at: at(a.at) })),
116
- ways: network.anatomy.ways.map((w) => ({ ...w, foot: at(w.foot) })),
117
- whiskers: network.anatomy.whiskers.map((w) => ({ ...w, foot: at(w.foot), tip: at(w.tip) })),
118
- frames: network.anatomy.frames.map((f) => ({ ...f, box: f.box.map(at) })),
119
- loose: network.anatomy.loose,
120
- axes: network.anatomy.axes.map((a) => ({ ...a, from: at(a.from), to: at(a.to) })),
121
- };
122
- const point = ([x, y]) => [
123
- place(x, unitsPerMeter, true),
124
- place(y, unitsPerMeter, true),
125
- ];
126
- return {
127
- ...network,
128
- ...(anatomy && { anatomy }),
129
- paths: network.paths.map((path) => path.map(point)),
130
- docks: network.docks.map((dock) => ({
131
- ...dock,
132
- from: point(dock.from),
133
- to: point(dock.to),
134
- })),
135
- stats: {
136
- ...network.stats,
137
- unreached: network.stats.unreached.map((u) => (u.at ? { ...u, at: point(u.at) } : u)),
138
- },
139
- };
140
- }
141
- /**
142
- * The destinations as the stages saw them, back in the plan's own units.
143
- * @param destinations The carved footprints.
144
- * @param unitsPerMeter Plan coordinates in one meter.
145
- * @returns The converted destinations.
146
- */
147
- export function destinationsInPlanUnits(destinations, unitsPerMeter) {
148
- if (unitsPerMeter === 1)
149
- return [...destinations];
150
- return destinations.map((d) => scaleDestination(d, unitsPerMeter, true));
151
- }
152
- /**
153
- * Transition stops that never reached the graph, back in plan units.
154
- * @param orphans The stops, in meters.
155
- * @param unitsPerMeter Plan coordinates in one meter.
156
- * @returns The stops in plan coordinates.
157
- */
158
- export function orphansInPlanUnits(orphans, unitsPerMeter) {
159
- if (unitsPerMeter === 1)
160
- return orphans;
161
- return orphans.map((orphan) => ({
162
- ...orphan,
163
- x: place(orphan.x, unitsPerMeter, true),
164
- y: place(orphan.y, unitsPerMeter, true),
165
- }));
166
- }
167
- /**
168
- * The emitted graph back in the plan's own units — what the host snaps its POI to.
169
- * @param graph The graph as compiled, in meters.
170
- * @param unitsPerMeter Plan coordinates in one meter.
171
- * @returns The graph in plan coordinates.
172
- */
173
- export function graphInPlanUnits(graph, unitsPerMeter) {
174
- if (unitsPerMeter === 1)
175
- return graph;
176
- const point = (p) => ({
177
- ...p,
178
- x: place(p.x, unitsPerMeter, true),
179
- y: place(p.y, unitsPerMeter, true),
180
- });
181
- return {
182
- lines: graph.lines.map((line) => ({ ...line, p0: point(line.p0), p1: point(line.p1) })),
183
- lineEnds: graph.lineEnds.map(point),
184
- };
185
- }
@@ -1,19 +0,0 @@
1
- import type { Ring } from './geom/types.js';
2
- import { type PreparedWalls } from './geom/walls.js';
3
- import { type RouteNetwork, type RouteNetworkDestination } from './graph-types.js';
4
- import type { Opening } from './openings.js';
5
- /**
6
- * Builds the steiner route network for one layer.
7
- * @param walkableRings The layer's walkable polygon (outer rings + holes).
8
- * @param destinations Footprints the network must reach.
9
- * @param walls Raw wall polylines as flat `[x0, y0, …]` runs, for exact thin-wall blocking.
10
- * @param prepared The same walls already prepared, when the caller has them.
11
- * @param drawn The rest of each destination's drawing; ways in may not cross it.
12
- * @param openings The openings the entrances make: the ways in they own, through the walls.
13
- * @returns Paths, docks and stats for `emit-v1`.
14
- */
15
- export declare function buildSteinerNetwork(walkableRings: readonly Ring[], destinations: readonly RouteNetworkDestination[], walls: readonly (readonly number[])[], prepared?: PreparedWalls, drawn?: readonly {
16
- readonly id: string;
17
- readonly ring: Ring;
18
- }[], openings?: readonly Opening[]): RouteNetwork;
19
- //# sourceMappingURL=steiner-network.d.ts.map
@@ -1,183 +0,0 @@
1
- // The "steiner" route-network generator: the network of a layer, along the directions the plan is
2
- // drawn in. A square stub per open face ends in the middle of the aisle ahead; the tips of an aisle
3
- // define its axis, and the aisle's line is laid through them. One demand growth over an eroded raster
4
- // then joins the pieces and the lone tips across the floor, each path laid on the lines its place
5
- // defines; dangling ends run on to the line ahead, and a link cuts across where the network would
6
- // send a visitor the long way round. Produces the {@link RouteNetwork} for `emit-v1`.
7
- import { attachStubs } from './attach.js';
8
- import { blockersOf, crossesBlocker } from './blockers.js';
9
- import { createConnector } from './connect.js';
10
- import { DOOR_WELD, doorOwners, doorwaySides, findDoorways } from './doorways.js';
11
- import { emitNetwork, turnDoorwayDocks } from './emit-network.js';
12
- import { followTips, tipPoints } from './entries.js';
13
- import { floorGrids, servedFloor } from './floor-grids.js';
14
- import { frameTips } from './framing.js';
15
- import { placeIn } from './geom/frames.js';
16
- import { pointKey } from './geom/point-key.js';
17
- import { boundsOf } from './geom/primitives.js';
18
- import { prepareWalls } from './geom/walls.js';
19
- import { NETWORK_SIZES } from './network-sizes.js';
20
- import { NetworkStore } from './network-store.js';
21
- import { completeLines, dropDeadEnds, shortcutDetours } from './post-passes.js';
22
- import { makeRouter } from './router.js';
23
- import { defineAxes, planStubs, runStubsToAxes } from './stubs.js';
24
- /**
25
- * Builds the steiner route network for one layer.
26
- * @param walkableRings The layer's walkable polygon (outer rings + holes).
27
- * @param destinations Footprints the network must reach.
28
- * @param walls Raw wall polylines as flat `[x0, y0, …]` runs, for exact thin-wall blocking.
29
- * @param prepared The same walls already prepared, when the caller has them.
30
- * @param drawn The rest of each destination's drawing; ways in may not cross it.
31
- * @param openings The openings the entrances make: the ways in they own, through the walls.
32
- * @returns Paths, docks and stats for `emit-v1`.
33
- */
34
- export function buildSteinerNetwork(walkableRings, destinations, walls, prepared, drawn = [], openings = []) {
35
- const cfg = NETWORK_SIZES;
36
- const prep = prepared ?? prepareWalls(walls);
37
- const wallLines = prep.segments;
38
- const doors = findDoorways(prep, destinations);
39
- const owners = doorOwners(doors, destinations, wallLines, cfg);
40
- // An entrance owns its openings and is reached through them alone. An opening on a doorway the
41
- // walls already leave is that doorway; any other stands on the floor past the wall, and the dock
42
- // from it crosses the wall inside the opening's span.
43
- const opened = new Set();
44
- const passes = new Map();
45
- for (const { id, at, span, door } of openings) {
46
- const same = doors.find((d) => Math.hypot(d.mid[0] - at[0], d.mid[1] - at[1]) <= DOOR_WELD);
47
- const flip = same && same.nx * door.nx + same.ny * door.ny < 0 ? -1 : 1;
48
- const sides = owners.get(id) ?? [];
49
- sides.push(same
50
- ? { door: same, nx: same.nx * flip, ny: same.ny * flip }
51
- : { door, nx: door.nx, ny: door.ny });
52
- owners.set(id, sides);
53
- opened.add(id);
54
- passes.set(id, [...(passes.get(id) ?? []), span]);
55
- }
56
- const { rings: floor, dropped: unservedFloorPieces } = servedFloor(walkableRings, destinations, owners);
57
- // Only a doorway's middle stays open, so every pass draws lines through it there.
58
- const doorSides = doorwaySides(doors, cfg);
59
- const { grids, frames, frameOfDest } = floorGrids(floor, doorSides, destinations, wallLines, doors, cfg);
60
- // On-floor tests use the plan's grid.
61
- const ctx = grids[0].ctx;
62
- const router = makeRouter(grids, cfg);
63
- const store = new NetworkStore();
64
- // A doorway's closed sides block stubs and docks like walls.
65
- const blockers = blockersOf(destinations, [...wallLines, ...doorSides], doors, drawn, new Set(owners.keys()), passes);
66
- const destinationOf = new Map(destinations.map((d) => [d.id, d]));
67
- // The floor's own size: nothing on the layer reaches farther.
68
- const floorBounds = boundsOf(floor.flat());
69
- const floorSpan = isFinite(floorBounds[0])
70
- ? Math.hypot(floorBounds[2] - floorBounds[0], floorBounds[3] - floorBounds[1])
71
- : cfg.stub;
72
- /** Why a destination has no stub yet: the furthest stage any of its ways in reached. */
73
- const failed = new Map();
74
- // Reasons from least to most informative; a destination keeps the most informative one.
75
- const REACHED = [
76
- 'no-way-in',
77
- 'no-floor',
78
- 'cut-off',
79
- 'way-in-blocked',
80
- 'detached',
81
- ];
82
- const note = (id, reason, facts) => {
83
- const known = failed.get(id);
84
- if (known === undefined || REACHED.indexOf(reason) > REACHED.indexOf(known.reason))
85
- failed.set(id, { id, reason, ...facts });
86
- };
87
- const build = { cfg, frames, blockers, destinationOf, note };
88
- /* ---- stubs: one per open side of each destination, ending mid-aisle ---- */
89
- const plan = planStubs({ ...build, destinations, owners, ctx, frameOfDest, opened });
90
- const { stubs } = plan;
91
- /* ---- aisle axes: the lines the tips define; each stub runs to its aisle's axis ---- */
92
- // Crossable destinations frame nothing: their stubs join whatever line is there.
93
- const through = new Set(destinations.filter((d) => d.crossable).map((d) => d.id));
94
- const { axes, axisOf } = defineAxes(plan, build, through, floorSpan);
95
- runStubsToAxes(axisOf, build, ctx);
96
- // Where a line may go: on the floor, across no wall or outline, and — once the stubs are attached —
97
- // across no stub short of its tip: a route would walk past that destination and back.
98
- let attached = null;
99
- const mayLay = (seg) => ctx.lineFree(seg[0], seg[1], seg[2], seg[3]) &&
100
- !crossesBlocker(blockers, seg) &&
101
- (attached === null || !crossesBlocker(attached, seg));
102
- /* ---- rows along the axes, perimeters and corners: the block grids ---- */
103
- frameTips(store, stubs.filter((s) => !through.has(s.id)), destinations, mayLay, frames, axisOf, cfg.cs, 2 * cfg.clear);
104
- followTips(stubs, store.node(tipPoints(stubs)));
105
- /* ---- growth: every piece and every lone tip joined across the floor ---- */
106
- const connector = createConnector(store, router, cfg, { frames, axes, doors, blockers, mayLay });
107
- const lone = stubs.filter((s) => !store.hasNode(s.tx, s.ty));
108
- const targets = new Map(lone.map((s) => [s, { at: [s.tx, s.ty], normal: [s.nx, s.ny], frame: s.frame ?? frames[0] }]));
109
- const { untied } = connector.connect([...targets.values()]);
110
- // Anatomy for the debug overlay.
111
- const anatomyDoors = doors.map((d) => ({
112
- gap: [
113
- [d.gap[0], d.gap[1]],
114
- [d.gap[2], d.gap[3]],
115
- ],
116
- mid: [d.mid[0], d.mid[1]],
117
- }));
118
- const anatomyAnchors = [...targets].map(([s, t]) => ({ id: s.id, at: t.at }));
119
- const anatomyWays = [];
120
- for (const [id, list] of plan.ways)
121
- for (const w of list)
122
- anatomyWays.push({ id, kind: w.kind, foot: [w.mx, w.my], normal: [w.nx, w.ny] });
123
- // Each turned grid's outline and who is drawn in its direction; the rest turned alone.
124
- const anatomyFrames = grids.slice(1).map(({ frame, ctx: grid }) => {
125
- const [u0, v0] = [grid.ox, grid.oy];
126
- const [u1, v1] = [grid.ox + grid.w * grid.cs, grid.oy + grid.h * grid.cs];
127
- return {
128
- angle: Math.atan2(frame.sin, frame.cos),
129
- box: [
130
- placeIn(frame, u0, v0),
131
- placeIn(frame, u1, v0),
132
- placeIn(frame, u1, v1),
133
- placeIn(frame, u0, v1),
134
- ],
135
- ids: destinations.filter((d) => frameOfDest(d) === frame).map((d) => d.id),
136
- };
137
- });
138
- const anatomyLoose = destinations.filter((d) => !frameOfDest(d)).map((d) => d.id);
139
- const anatomyAxes = axes.map((axis) => {
140
- const put = (along) => axis.horiz ? placeIn(axis.frame, along, axis.c) : placeIn(axis.frame, axis.c, along);
141
- const angle = Math.atan2(axis.frame.sin, axis.frame.cos) + (axis.horiz ? 0 : Math.PI / 2);
142
- return { angle, from: put(axis.lo), to: put(axis.hi) };
143
- });
144
- /* ---- attach: each stub ends where it first meets the network, or a link takes it there ---- */
145
- const whiskers = attachStubs({ ...build, plan, store, connector, router, mayLay });
146
- followTips(whiskers, store.node(tipPoints(whiskers)));
147
- // A line that ends nowhere serves nothing: growth reached tips whose stubs then met a nearer line.
148
- dropDeadEnds(store, new Set(whiskers.map((w) => pointKey(w.tx, w.ty))));
149
- attached = blockersOf([], whiskers.map((w) => [w.mx, w.my, w.tx, w.ty]), []);
150
- /* ---- dangling ends run on to the line ahead; links cut across detours ---- */
151
- completeLines(store, connector, mayLay, frames);
152
- const shortcuts = shortcutDetours(store, router, connector);
153
- turnDoorwayDocks(whiskers, build, store);
154
- const anatomyWhiskers = whiskers.map((w) => ({
155
- id: w.id,
156
- kind: w.kind,
157
- foot: [w.mx, w.my],
158
- tip: [w.tx, w.ty],
159
- }));
160
- /* ---- emit the RouteNetwork artifact ---- */
161
- const emitted = emitNetwork({ ...build, store, whiskers, destinations, failed, doors });
162
- return {
163
- paths: emitted.paths,
164
- docks: emitted.docks,
165
- anatomy: {
166
- doors: anatomyDoors,
167
- anchors: anatomyAnchors,
168
- ways: anatomyWays,
169
- whiskers: anatomyWhiskers,
170
- frames: anatomyFrames,
171
- loose: anatomyLoose,
172
- axes: anatomyAxes,
173
- },
174
- stats: {
175
- unreached: emitted.unreached,
176
- components: emitted.components,
177
- doorways: doors.length,
178
- shortcuts,
179
- ...(untied > 0 && { untiedPieces: untied }),
180
- ...(unservedFloorPieces > 0 && { unservedFloorPieces }),
181
- },
182
- };
183
- }