@expofp/wayfinding-compiler 0.0.0-bootstrap.0 → 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 (110) 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/centering.d.ts +0 -14
  14. package/dist/lib/centering.js +0 -88
  15. package/dist/lib/compile-layer.d.ts +0 -41
  16. package/dist/lib/compile-layer.js +0 -241
  17. package/dist/lib/compile-wayfinding.d.ts +0 -90
  18. package/dist/lib/compile-wayfinding.js +0 -203
  19. package/dist/lib/connect.d.ts +0 -35
  20. package/dist/lib/connect.js +0 -206
  21. package/dist/lib/diagnostics.d.ts +0 -190
  22. package/dist/lib/diagnostics.js +0 -634
  23. package/dist/lib/doorways.d.ts +0 -78
  24. package/dist/lib/doorways.js +0 -331
  25. package/dist/lib/emit-network.d.ts +0 -47
  26. package/dist/lib/emit-network.js +0 -105
  27. package/dist/lib/emit-v1.d.ts +0 -31
  28. package/dist/lib/emit-v1.js +0 -56
  29. package/dist/lib/entries.d.ts +0 -123
  30. package/dist/lib/entries.js +0 -448
  31. package/dist/lib/floor-grids.d.ts +0 -48
  32. package/dist/lib/floor-grids.js +0 -185
  33. package/dist/lib/framing.d.ts +0 -20
  34. package/dist/lib/framing.js +0 -187
  35. package/dist/lib/geom/boolean-ops.d.ts +0 -21
  36. package/dist/lib/geom/boolean-ops.js +0 -1
  37. package/dist/lib/geom/clipper2-ops.d.ts +0 -7
  38. package/dist/lib/geom/clipper2-ops.js +0 -50
  39. package/dist/lib/geom/footprint.d.ts +0 -26
  40. package/dist/lib/geom/footprint.js +0 -203
  41. package/dist/lib/geom/frames.d.ts +0 -116
  42. package/dist/lib/geom/frames.js +0 -233
  43. package/dist/lib/geom/mesh-outline.d.ts +0 -13
  44. package/dist/lib/geom/mesh-outline.js +0 -90
  45. package/dist/lib/geom/normalize.d.ts +0 -28
  46. package/dist/lib/geom/normalize.js +0 -118
  47. package/dist/lib/geom/point-key.d.ts +0 -16
  48. package/dist/lib/geom/point-key.js +0 -15
  49. package/dist/lib/geom/primitives.d.ts +0 -105
  50. package/dist/lib/geom/primitives.js +0 -184
  51. package/dist/lib/geom/split-crossings.d.ts +0 -14
  52. package/dist/lib/geom/split-crossings.js +0 -171
  53. package/dist/lib/geom/types.d.ts +0 -9
  54. package/dist/lib/geom/types.js +0 -1
  55. package/dist/lib/geom/walls.d.ts +0 -31
  56. package/dist/lib/geom/walls.js +0 -71
  57. package/dist/lib/graph-invariants.d.ts +0 -49
  58. package/dist/lib/graph-invariants.js +0 -129
  59. package/dist/lib/graph-types.d.ts +0 -143
  60. package/dist/lib/graph-types.js +0 -7
  61. package/dist/lib/grid-network.d.ts +0 -20
  62. package/dist/lib/grid-network.js +0 -349
  63. package/dist/lib/lattice-map.d.ts +0 -19
  64. package/dist/lib/lattice-map.js +0 -92
  65. package/dist/lib/lattice-network.d.ts +0 -32
  66. package/dist/lib/lattice-network.js +0 -289
  67. package/dist/lib/mask.d.ts +0 -54
  68. package/dist/lib/mask.js +0 -210
  69. package/dist/lib/min-heap.d.ts +0 -24
  70. package/dist/lib/min-heap.js +0 -73
  71. package/dist/lib/network-build.d.ts +0 -21
  72. package/dist/lib/network-build.js +0 -4
  73. package/dist/lib/network-graph.d.ts +0 -80
  74. package/dist/lib/network-graph.js +0 -467
  75. package/dist/lib/network-setup.d.ts +0 -50
  76. package/dist/lib/network-setup.js +0 -76
  77. package/dist/lib/network-sizes.d.ts +0 -29
  78. package/dist/lib/network-sizes.js +0 -22
  79. package/dist/lib/network-store.d.ts +0 -173
  80. package/dist/lib/network-store.js +0 -430
  81. package/dist/lib/openings.d.ts +0 -54
  82. package/dist/lib/openings.js +0 -387
  83. package/dist/lib/place-path.d.ts +0 -47
  84. package/dist/lib/place-path.js +0 -346
  85. package/dist/lib/post-passes.d.ts +0 -31
  86. package/dist/lib/post-passes.js +0 -305
  87. package/dist/lib/reachability.d.ts +0 -58
  88. package/dist/lib/reachability.js +0 -220
  89. package/dist/lib/report.d.ts +0 -49
  90. package/dist/lib/report.js +0 -374
  91. package/dist/lib/router.d.ts +0 -111
  92. package/dist/lib/router.js +0 -388
  93. package/dist/lib/runs.d.ts +0 -92
  94. package/dist/lib/runs.js +0 -232
  95. package/dist/lib/scale.d.ts +0 -58
  96. package/dist/lib/scale.js +0 -185
  97. package/dist/lib/steiner-network.d.ts +0 -19
  98. package/dist/lib/steiner-network.js +0 -183
  99. package/dist/lib/strip.d.ts +0 -43
  100. package/dist/lib/strip.js +0 -200
  101. package/dist/lib/stubs.d.ts +0 -84
  102. package/dist/lib/stubs.js +0 -316
  103. package/dist/lib/transitions.d.ts +0 -72
  104. package/dist/lib/transitions.js +0 -191
  105. package/dist/lib/types.d.ts +0 -126
  106. package/dist/lib/types.js +0 -1
  107. package/dist/lib/validate-source.d.ts +0 -41
  108. package/dist/lib/validate-source.js +0 -133
  109. package/dist/lib/walkable.d.ts +0 -104
  110. package/dist/lib/walkable.js +0 -140
@@ -1,78 +0,0 @@
1
- import type { Ring, Segment, Vec2 } from './geom/types.js';
2
- import type { PreparedWalls } from './geom/walls.js';
3
- import { type RouteNetworkDestination } from './graph-types.js';
4
- import { type NetworkSizes } from './network-sizes.js';
5
- /** How close two readings of one doorway may lie: its middles, or a jamb and a wall end. */
6
- export declare const DOOR_WELD = 0.12;
7
- /** How far off a wall's line a piece may lie and still continue that wall. */
8
- export declare const ON_WALL_LINE = 0.12;
9
- /** A gap in a wall, with the wall's normal — the way through it. */
10
- export interface Doorway {
11
- /** The gap, jamb to jamb. */
12
- readonly gap: Segment;
13
- /** The middle of the gap. */
14
- readonly mid: Vec2;
15
- /** Normal of the wall the gap is in, x. */
16
- readonly nx: number;
17
- /** Normal of the wall the gap is in, y. */
18
- readonly ny: number;
19
- /** The room's outline, when the gap is the way into a room drawn as one polyline. */
20
- readonly room?: Ring;
21
- }
22
- /**
23
- * The closed sides of the doorways: only `MIN_PASS` plus one cell either side of the middle stays open,
24
- * the rest is closed like the wall. A doorway no wider than that is all middle.
25
- * @param doors The doorways.
26
- * @param cfg Network sizes.
27
- * @returns Each wider doorway's two closed sides, jamb to the edge of its middle.
28
- */
29
- export declare function doorwaySides(doors: readonly Doorway[], cfg: NetworkSizes): Segment[];
30
- /**
31
- * The doorways a plan draws as gaps in its walls, at any width.
32
- *
33
- * From each free wall end, the first wall piece ahead on the same line is the other jamb. A gap is dropped
34
- * when a wall or a non-crossable destination's outline crosses it — unless the gap opens into that
35
- * destination (see `doorOwners`). An open run turning more than half a turn is a room, and the gap between
36
- * its ends is its door.
37
- * @param walls The layer's walls, read once.
38
- * @param destinations The layer's destinations.
39
- * @returns One doorway per gap found.
40
- */
41
- export declare function findDoorways(walls: PreparedWalls, destinations?: readonly RouteNetworkDestination[]): Doorway[];
42
- /**
43
- * How far a ray travels before it meets one of `blockers`, and which one it met.
44
- * @param from Where the ray starts.
45
- * @param dx Ray direction x.
46
- * @param dy Ray direction y.
47
- * @param blockers What it may meet.
48
- * @returns The distance and the blocker's index, or null.
49
- */
50
- export declare function rayHit(from: Vec2, dx: number, dy: number, blockers: readonly Segment[]): {
51
- at: number;
52
- index: number;
53
- } | null;
54
- /** One side of a doorway that opens straight into a destination, with the outward normal of that side. */
55
- export interface DoorSide {
56
- /** The doorway. */
57
- readonly door: Doorway;
58
- /** Normal pointing out of the destination, x. */
59
- readonly nx: number;
60
- /** Normal pointing out of the destination, y. */
61
- readonly ny: number;
62
- }
63
- /**
64
- * Which destinations each doorway opens straight into, by id — the first case that applies:
65
- * - the gap's middle is on a destination's outline;
66
- * - the middle is inside a footprint that overshoots its wall — it opens where the footprint ends sooner;
67
- * - the gap is a room's way in — the room's destination, when it holds one that is not walked through
68
- * (an exit, a stair or a pad beside it does not count): a room holding more is a hall, and its door
69
- * a way into the space the network reaches, not into each of them;
70
- * - else the first outline straight in, closer than `cfg.clear` and with no wall before it.
71
- * @param doors Every doorway on the layer.
72
- * @param destinations Every destination on the layer.
73
- * @param walls The layer's wall segments.
74
- * @param cfg Network sizes.
75
- * @returns The doorway sides opening into each destination.
76
- */
77
- export declare function doorOwners(doors: readonly Doorway[], destinations: readonly RouteNetworkDestination[], walls: readonly Segment[], cfg: NetworkSizes): Map<string, DoorSide[]>;
78
- //# sourceMappingURL=doorways.d.ts.map
@@ -1,331 +0,0 @@
1
- // Doorways: gaps in the walls, and the destinations each one opens into.
2
- import { blockersNear, blockersOf } from './blockers.js';
3
- import { boundsOf, onOutline, pointInRing, pointSegmentDistance, rayVsSegment, segmentsCross, signedArea, } from './geom/primitives.js';
4
- import { NODE_SNAP } from './graph-types.js';
5
- import { MIN_PASS } from './network-sizes.js';
6
- import { WALL_HALF_WIDTH } from './walkable.js';
7
- /** How close two readings of one doorway may lie: its middles, or a jamb and a wall end. */
8
- export const DOOR_WELD = 0.12;
9
- /** How far off a wall's line a piece may lie and still continue that wall. */
10
- export const ON_WALL_LINE = 0.12;
11
- /**
12
- * Whether a wall crosses the open span of a gap: the gap inset by a wall half-width at each jamb, one wall
13
- * thick across. A jamb's own wall touches only the span's end, so it never counts.
14
- * @param gap The gap, jamb to jamb.
15
- * @param walls Wall segments.
16
- * @returns True when something is drawn across the gap.
17
- */
18
- function spanBlocked(gap, walls) {
19
- const width = Math.hypot(gap[2] - gap[0], gap[3] - gap[1]);
20
- const lo = WALL_HALF_WIDTH;
21
- const hi = width - WALL_HALF_WIDTH;
22
- if (hi <= lo)
23
- return false;
24
- const ux = (gap[2] - gap[0]) / width;
25
- const uy = (gap[3] - gap[1]) / width;
26
- return walls.some(([x0, y0, x1, y1]) => {
27
- // The segment in the gap's own frame: how far along it runs, and how far to the side.
28
- const a0 = (x0 - gap[0]) * ux + (y0 - gap[1]) * uy;
29
- const a1 = (x1 - gap[0]) * ux + (y1 - gap[1]) * uy;
30
- const o0 = -(x0 - gap[0]) * uy + (y0 - gap[1]) * ux;
31
- const o1 = -(x1 - gap[0]) * uy + (y1 - gap[1]) * ux;
32
- let t0 = 0;
33
- let t1 = 1;
34
- // Clipped to the span's rectangle a side at a time; anything left crosses the gap.
35
- const clip = (p, q) => {
36
- if (Math.abs(p) < 1e-12)
37
- return q >= 0;
38
- const r = q / p;
39
- if (p < 0)
40
- t0 = Math.max(t0, r);
41
- else
42
- t1 = Math.min(t1, r);
43
- return t0 < t1;
44
- };
45
- return (clip(a0 - a1, a0 - lo) &&
46
- clip(a1 - a0, hi - a0) &&
47
- clip(o0 - o1, o0 + WALL_HALF_WIDTH) &&
48
- clip(o1 - o0, WALL_HALF_WIDTH - o0));
49
- });
50
- }
51
- /**
52
- * How far a wall run turns: the signed angles at its vertices, summed.
53
- * @param points The run's vertices.
54
- * @returns The total turn in radians; positive counter-clockwise.
55
- */
56
- function turnOf(points) {
57
- let turn = 0;
58
- for (let i = 1; i + 1 < points.length; i++) {
59
- const [ax, ay] = points[i - 1];
60
- const [bx, by] = points[i];
61
- const [cx, cy] = points[i + 1];
62
- turn += Math.atan2((bx - ax) * (cy - by) - (by - ay) * (cx - bx), (bx - ax) * (cx - bx) + (by - ay) * (cy - by));
63
- }
64
- return turn;
65
- }
66
- /**
67
- * The closed sides of the doorways: only `MIN_PASS` plus one cell either side of the middle stays open,
68
- * the rest is closed like the wall. A doorway no wider than that is all middle.
69
- * @param doors The doorways.
70
- * @param cfg Network sizes.
71
- * @returns Each wider doorway's two closed sides, jamb to the edge of its middle.
72
- */
73
- export function doorwaySides(doors, cfg) {
74
- const middle = MIN_PASS + cfg.cs;
75
- const sides = [];
76
- for (const door of doors) {
77
- const [ax, ay, bx, by] = door.gap;
78
- const width = Math.hypot(bx - ax, by - ay);
79
- if (width <= 2 * middle)
80
- continue;
81
- const ux = (bx - ax) / width;
82
- const uy = (by - ay) / width;
83
- const [mx, my] = door.mid;
84
- sides.push([ax, ay, mx - ux * middle, my - uy * middle], [mx + ux * middle, my + uy * middle, bx, by]);
85
- }
86
- return sides;
87
- }
88
- /**
89
- * Whether every vertex of a destination's outline is inside a room's ring.
90
- * @param d The destination.
91
- * @param room The room's ring.
92
- * @returns True when it stands inside.
93
- */
94
- function standsIn(d, room) {
95
- return d.polygon.every((p) => pointInRing(room, p));
96
- }
97
- /**
98
- * The doorways a plan draws as gaps in its walls, at any width.
99
- *
100
- * From each free wall end, the first wall piece ahead on the same line is the other jamb. A gap is dropped
101
- * when a wall or a non-crossable destination's outline crosses it — unless the gap opens into that
102
- * destination (see `doorOwners`). An open run turning more than half a turn is a room, and the gap between
103
- * its ends is its door.
104
- * @param walls The layer's walls, read once.
105
- * @param destinations The layer's destinations.
106
- * @returns One doorway per gap found.
107
- */
108
- export function findDoorways(walls, destinations = []) {
109
- const segs = walls.segments;
110
- const solid = blockersOf(destinations, [], []);
111
- const byId = new Map();
112
- for (const d of destinations)
113
- byId.set(d.id, [...(byId.get(d.id) ?? []), d]);
114
- /**
115
- * Whether a non-crossable destination's outline crosses the gap, other than one the gap opens into.
116
- * @param gap The gap, jamb to jamb.
117
- * @param room The room the gap closes, if any.
118
- * @returns True when such an outline crosses it.
119
- */
120
- const standsAcross = (gap, room) => {
121
- const [ax, ay, bx, by] = gap;
122
- const mx = (ax + bx) / 2;
123
- const my = (ay + by) / 2;
124
- // A footprint over the middle that also reaches a jamb has overshot its own wall: the gap is its door.
125
- const opensInto = (d) => onOutline(d.polygon, mx, my, NODE_SNAP) ||
126
- (room !== undefined && standsIn(d, room)) ||
127
- (pointInRing(d.polygon, [mx, my]) &&
128
- [
129
- [ax, ay],
130
- [bx, by],
131
- ].some(([x, y]) => pointInRing(d.polygon, [x, y]) || onOutline(d.polygon, x, y, NODE_SNAP)));
132
- return blockersNear(solid, boundsOf([
133
- [ax, ay],
134
- [bx, by],
135
- ])).some(({ seg, owner }) => segmentsCross(gap, seg) && !(byId.get(owner ?? '') ?? []).some(opensInto));
136
- };
137
- const doorways = [];
138
- const add = (gap, nx, ny, room) => {
139
- if (spanBlocked(gap, segs) || standsAcross(gap, room))
140
- return;
141
- const mid = [+((gap[0] + gap[2]) / 2).toFixed(2), +((gap[1] + gap[3]) / 2).toFixed(2)];
142
- // The same door read from both of its jambs is one door.
143
- if (doorways.some((d) => Math.hypot(d.mid[0] - mid[0], d.mid[1] - mid[1]) <= DOOR_WELD))
144
- return;
145
- doorways.push({ gap, mid, nx, ny, ...(room && { room }) });
146
- };
147
- // Straight doors: a free end of an open run, closed by the next piece on its own line.
148
- for (const run of walls.runs) {
149
- if (run.closed || run.points.length < 2)
150
- continue;
151
- for (const [tip, inward] of [
152
- [run.points[0], run.points[1]],
153
- [run.points[run.points.length - 1], run.points[run.points.length - 2]],
154
- ]) {
155
- const len = Math.hypot(tip[0] - inward[0], tip[1] - inward[1]) || 1;
156
- const ox = (tip[0] - inward[0]) / len;
157
- const oy = (tip[1] - inward[1]) / len;
158
- let gap = Infinity;
159
- for (const [x0, y0, x1, y1] of segs) {
160
- // Only pieces on this wall's own line count; a ray misses collinear pieces, so project instead.
161
- const off0 = -(x0 - tip[0]) * oy + (y0 - tip[1]) * ox;
162
- const off1 = -(x1 - tip[0]) * oy + (y1 - tip[1]) * ox;
163
- if (Math.abs(off0) > ON_WALL_LINE || Math.abs(off1) > ON_WALL_LINE)
164
- continue;
165
- const along0 = (x0 - tip[0]) * ox + (y0 - tip[1]) * oy;
166
- const along1 = (x1 - tip[0]) * ox + (y1 - tip[1]) * oy;
167
- const near = Math.min(along0, along1);
168
- const far = Math.max(along0, along1);
169
- if (far <= 1e-6)
170
- continue;
171
- gap = Math.min(gap, Math.max(near, 0));
172
- }
173
- if (!isFinite(gap) || gap <= 1e-6)
174
- continue;
175
- add([tip[0], tip[1], tip[0] + ox * gap, tip[1] + oy * gap], -oy, ox);
176
- }
177
- }
178
- // Room doors: an open run turning more than half a turn encloses a room, and the gap between its ends is
179
- // the way in; its ends need not line up. A bent wall (a quarter turn) or a three-sided screen (a half)
180
- // stays open. Read after the straight doors: a gap already found takes the room, a jamb end is skipped.
181
- const straight = doorways.length;
182
- const isJamb = (p) => doorways
183
- .slice(0, straight)
184
- .some(({ gap }) => Math.hypot(gap[0] - p[0], gap[1] - p[1]) <= DOOR_WELD ||
185
- Math.hypot(gap[2] - p[0], gap[3] - p[1]) <= DOOR_WELD);
186
- for (const run of walls.runs) {
187
- if (run.closed || run.points.length < 4)
188
- continue;
189
- const first = run.points[0];
190
- const last = run.points[run.points.length - 1];
191
- const dx = first[0] - last[0];
192
- const dy = first[1] - last[1];
193
- const width = Math.hypot(dx, dy);
194
- if (width < 1e-6 || Math.abs(turnOf(run.points)) <= Math.PI + 1e-9)
195
- continue;
196
- const mid = [
197
- +((first[0] + last[0]) / 2).toFixed(2),
198
- +((first[1] + last[1]) / 2).toFixed(2),
199
- ];
200
- const same = doorways.findIndex((d) => Math.hypot(d.mid[0] - mid[0], d.mid[1] - mid[1]) <= DOOR_WELD);
201
- if (same >= 0)
202
- doorways[same] = { ...doorways[same], room: run.points };
203
- else if (!isJamb(first) && !isJamb(last))
204
- add([last[0], last[1], first[0], first[1]], -dy / width, dx / width, run.points);
205
- }
206
- return doorways;
207
- }
208
- /**
209
- * How far a ray travels before it meets one of `blockers`, and which one it met.
210
- * @param from Where the ray starts.
211
- * @param dx Ray direction x.
212
- * @param dy Ray direction y.
213
- * @param blockers What it may meet.
214
- * @returns The distance and the blocker's index, or null.
215
- */
216
- export function rayHit(from, dx, dy, blockers) {
217
- let nearest = Infinity;
218
- let index = -1;
219
- const hit = [0, 0];
220
- blockers.forEach(([x0, y0, x1, y1], i) => {
221
- if (!rayVsSegment(hit, from[0], from[1], dx, dy, x0, y0, x1, y1))
222
- return;
223
- const [t, u] = hit;
224
- if (t > 1e-6 && u >= -1e-9 && u <= 1 + 1e-9 && t < nearest) {
225
- nearest = t;
226
- index = i;
227
- }
228
- });
229
- return index < 0 ? null : { at: nearest, index };
230
- }
231
- /**
232
- * Which destinations each doorway opens straight into, by id — the first case that applies:
233
- * - the gap's middle is on a destination's outline;
234
- * - the middle is inside a footprint that overshoots its wall — it opens where the footprint ends sooner;
235
- * - the gap is a room's way in — the room's destination, when it holds one that is not walked through
236
- * (an exit, a stair or a pad beside it does not count): a room holding more is a hall, and its door
237
- * a way into the space the network reaches, not into each of them;
238
- * - else the first outline straight in, closer than `cfg.clear` and with no wall before it.
239
- * @param doors Every doorway on the layer.
240
- * @param destinations Every destination on the layer.
241
- * @param walls The layer's wall segments.
242
- * @param cfg Network sizes.
243
- * @returns The doorway sides opening into each destination.
244
- */
245
- export function doorOwners(doors, destinations, walls, cfg) {
246
- // A room's way in opens into the one destination standing in it; the outline rules read non-crossable
247
- // ones only.
248
- const outlined = destinations.filter((d) => d.polygon.length >= 3);
249
- const solid = outlined.filter((d) => !d.crossable);
250
- const edges = [];
251
- const edgeOwner = [];
252
- const edgeInward = [];
253
- for (const d of solid) {
254
- const ccw = signedArea(d.polygon) > 0;
255
- for (let i = 0; i < d.polygon.length; i++) {
256
- const a = d.polygon[i];
257
- const b = d.polygon[(i + 1) % d.polygon.length];
258
- const len = Math.hypot(b[0] - a[0], b[1] - a[1]) || 1;
259
- // Left of a→b is inside a counter-clockwise ring.
260
- const left = [-(b[1] - a[1]) / len, (b[0] - a[0]) / len];
261
- edges.push([a[0], a[1], b[0], b[1]]);
262
- edgeOwner.push(d.id);
263
- edgeInward.push(ccw ? left : [-left[0], -left[1]]);
264
- }
265
- }
266
- const owners = new Map();
267
- const claim = (id, door, nx, ny) => {
268
- const sides = owners.get(id) ?? [];
269
- if (!sides.some((side) => side.door === door && side.nx === nx && side.ny === ny))
270
- sides.push({ door, nx, ny });
271
- owners.set(id, sides);
272
- };
273
- for (const door of doors) {
274
- let onOutline = false;
275
- for (let e = 0; e < edges.length; e++) {
276
- const [x0, y0, x1, y1] = edges[e];
277
- if (pointSegmentDistance(door.mid[0], door.mid[1], x0, y0, x1, y1) > NODE_SNAP)
278
- continue;
279
- const inward = edgeInward[e];
280
- const s = Math.sign(-(door.nx * inward[0] + door.ny * inward[1]));
281
- if (!s)
282
- continue;
283
- claim(edgeOwner[e], door, door.nx * s, door.ny * s);
284
- onOutline = true;
285
- }
286
- if (onOutline)
287
- continue;
288
- const host = solid.find((d) => pointInRing(d.polygon, door.mid));
289
- if (host) {
290
- const own = host.polygon.map((a, i) => {
291
- const b = host.polygon[(i + 1) % host.polygon.length];
292
- return [a[0], a[1], b[0], b[1]];
293
- });
294
- const ahead = rayHit(door.mid, door.nx, door.ny, own)?.at ?? Infinity;
295
- const behind = rayHit(door.mid, -door.nx, -door.ny, own)?.at ?? Infinity;
296
- const s = ahead <= behind ? 1 : -1;
297
- claim(host.id, door, door.nx * s, door.ny * s);
298
- continue;
299
- }
300
- if (door.room) {
301
- const room = door.room;
302
- // The gap closes the room from its last vertex to its first; outward is right of it in a CCW ring.
303
- const out = signedArea(room) > 0 ? 1 : -1;
304
- const [ax, ay] = room[room.length - 1];
305
- const [bx, by] = room[0];
306
- const len = Math.hypot(bx - ax, by - ay);
307
- const nx = (out * (by - ay)) / len;
308
- const ny = (out * -(bx - ax)) / len;
309
- const inside = outlined.filter((d) => standsIn(d, room));
310
- // Destinations walked through — an exit, a stair, a transition's pad — make no room a hall.
311
- const solidInside = inside.filter((d) => !d.crossable);
312
- const owner = solidInside.length === 1 ? solidInside[0] : inside.length === 1 ? inside[0] : null;
313
- if (owner)
314
- claim(owner.id, door, nx, ny);
315
- if (inside.length)
316
- continue;
317
- }
318
- for (const s of [1, -1]) {
319
- const nx = door.nx * s;
320
- const ny = door.ny * s;
321
- const hit = rayHit(door.mid, -nx, -ny, edges);
322
- if (!hit || hit.at >= cfg.clear)
323
- continue;
324
- const wall = rayHit(door.mid, -nx, -ny, walls);
325
- if (wall && wall.at < hit.at)
326
- continue;
327
- claim(edgeOwner[hit.index], door, nx, ny);
328
- }
329
- }
330
- return owners;
331
- }
@@ -1,47 +0,0 @@
1
- import type { Doorway } from './doorways.js';
2
- import { type Whisker } from './entries.js';
3
- import type { Vec2 } from './geom/types.js';
4
- import { type RouteNetwork, type RouteNetworkDestination, type RouteNetworkDock, type Unreached } from './graph-types.js';
5
- import type { NetworkBuild } from './network-build.js';
6
- import type { NetworkStore } from './network-store.js';
7
- /**
8
- * Turns each doorway dock that ends toward a side of its ring: the stub runs to the doorway's middle
9
- * as a line of the network, and the dock turns inside from there.
10
- * @param whiskers The attached stubs; a turned one's tip moves to its foot.
11
- * @param build The layer as the build reads it.
12
- * @param store The network; extended in place.
13
- */
14
- export declare function turnDoorwayDocks(whiskers: readonly Whisker[], build: NetworkBuild, store: NetworkStore): void;
15
- /** What the emit reads. */
16
- interface Emitting extends NetworkBuild {
17
- /** The finished network. */
18
- readonly store: NetworkStore;
19
- /** The attached stubs; a tip merged by the noding moves with it. */
20
- readonly whiskers: readonly Whisker[];
21
- /** The layer's destinations. */
22
- readonly destinations: readonly RouteNetworkDestination[];
23
- /** Why each destination without a whisker has none, as the stages noted it. */
24
- readonly failed: ReadonlyMap<string, Unreached>;
25
- /** The layer's doorways: a line through one keeps its place. */
26
- readonly doors: readonly Doorway[];
27
- }
28
- /** The emitted network: its paths and docks, and what it leaves unreached. */
29
- interface EmittedNetwork {
30
- /** The polylines of the pieces kept. */
31
- readonly paths: Vec2[][];
32
- /** One dock per whisker on a kept piece. */
33
- readonly docks: RouteNetworkDock[];
34
- /** Destinations without a dock, with the reason. */
35
- readonly unreached: RouteNetwork['stats']['unreached'];
36
- /** Connected pieces of the network, kept or not. */
37
- readonly components: number;
38
- }
39
- /**
40
- * Nodes the network, docks every whisker on it, and keeps the pieces that serve: routes turn only at
41
- * shared vertices, so crossings become vertices and ends within `NODE_SNAP` merge.
42
- * @param emitting What the emit reads.
43
- * @returns The paths, the docks, the unreached destinations and the piece count.
44
- */
45
- export declare function emitNetwork(emitting: Emitting): EmittedNetwork;
46
- export {};
47
- //# sourceMappingURL=emit-network.d.ts.map
@@ -1,105 +0,0 @@
1
- // Emit: the finished network as the {@link RouteNetwork} artifact. A doorway dock that turns for a
2
- // side of its ring gets a line of the network from its tip to the doorway's middle and leaves from
3
- // there; every meeting becomes a vertex; the docks are drawn from the tips into the destinations. Of
4
- // the network's pieces, the one with most docks is emitted, and each further piece only when it
5
- // docks a destination no emitted piece does. A straight run is emitted as one line through every
6
- // meeting on it, so an aisle is one line and what meets it ends on its span.
7
- import { crossesBlocker } from './blockers.js';
8
- import { dockFor, followTips, tipPoints, turnsInDoorway } from './entries.js';
9
- import { pointKey } from './geom/point-key.js';
10
- import { segmentsCross } from './geom/primitives.js';
11
- import { splitCrossings } from './geom/split-crossings.js';
12
- import { NODE_SNAP, } from './graph-types.js';
13
- import { chainSegments, chainsFromEdges, components, graphOf, joinStraightRuns, } from './network-graph.js';
14
- /**
15
- * Turns each doorway dock that ends toward a side of its ring: the stub runs to the doorway's middle
16
- * as a line of the network, and the dock turns inside from there.
17
- * @param whiskers The attached stubs; a turned one's tip moves to its foot.
18
- * @param build The layer as the build reads it.
19
- * @param store The network; extended in place.
20
- */
21
- export function turnDoorwayDocks(whiskers, build, store) {
22
- const { destinationOf, blockers } = build;
23
- for (const w of whiskers) {
24
- const d = destinationOf.get(w.id);
25
- if (!d || !turnsInDoorway(d, w, [w.tx, w.ty], w.to) || !dockFor(d, w, [w.tx, w.ty], blockers))
26
- continue;
27
- store.add(w.tx, w.ty, w.mx, w.my);
28
- w.tx = w.mx;
29
- w.ty = w.my;
30
- }
31
- }
32
- /**
33
- * Nodes the network, docks every whisker on it, and keeps the pieces that serve: routes turn only at
34
- * shared vertices, so crossings become vertices and ends within `NODE_SNAP` merge.
35
- * @param emitting What the emit reads.
36
- * @returns The paths, the docks, the unreached destinations and the piece count.
37
- */
38
- export function emitNetwork(emitting) {
39
- const { store, whiskers, destinations, destinationOf, blockers, failed, note } = emitting;
40
- /** Docking-ring sides already taken, per destination: one stub per side. */
41
- const usedEdges = new Map();
42
- const joined = new Map();
43
- const pieces = splitCrossings(store.toArray(), NODE_SNAP, tipPoints(whiskers), joined);
44
- followTips(whiskers, joined);
45
- const chains = chainsFromEdges(pieces, new Set(whiskers.map((w) => pointKey(w.tx, w.ty))));
46
- const graph = graphOf(chainSegments(chains));
47
- const comp = components(graph);
48
- const lawful = [];
49
- for (const wsk of whiskers) {
50
- const n = graph.nodeId.get(pointKey(wsk.tx, wsk.ty));
51
- // A later pass moved the tip's line.
52
- if (n === undefined) {
53
- note(wsk.id, 'detached', { at: [wsk.tx, wsk.ty], by: 'not-a-vertex' });
54
- continue;
55
- }
56
- const dest = destinationOf.get(wsk.id);
57
- // Recheck with the final foot and tip.
58
- const stub = dest
59
- ? dockFor(dest, { ...wsk, kind: wsk.kind }, [wsk.tx, wsk.ty], blockers, usedEdges.get(wsk.id))
60
- : null;
61
- // No lawful dock here; unreached unless another whisker docks.
62
- if (!stub) {
63
- note(wsk.id, 'detached', { at: [wsk.tx, wsk.ty], by: 'dock-unlawful' });
64
- continue;
65
- }
66
- if (stub.edge !== undefined)
67
- usedEdges.set(wsk.id, (usedEdges.get(wsk.id) ?? new Set()).add(stub.edge));
68
- // The ring side only shares sides out; the emit carries just the dock's ends.
69
- lawful.push({ dock: { id: wsk.id, from: [wsk.tx, wsk.ty], to: stub.to }, piece: comp[n] });
70
- }
71
- // The pieces emitted: most docks first, each next piece only when it docks a destination no
72
- // kept piece does — a piece the floor cuts off serves what nothing else can, and nothing else.
73
- const dockCount = new Map();
74
- for (const { piece } of lawful)
75
- dockCount.set(piece, (dockCount.get(piece) ?? 0) + 1);
76
- const serving = new Set();
77
- const docked = new Set();
78
- for (const [piece] of [...dockCount.entries()].sort((a, b) => b[1] - a[1] || a[0] - b[0])) {
79
- const ids = lawful.filter((l) => l.piece === piece).map((l) => l.dock.id);
80
- if (!ids.some((id) => !docked.has(id)))
81
- continue;
82
- serving.add(piece);
83
- for (const id of ids)
84
- docked.add(id);
85
- }
86
- const runs = joinStraightRuns(chains.filter((c) => serving.has(comp[graph.nodeId.get(pointKey(c.pts[0][0], c.pts[0][1]))])), {
87
- frames: emitting.frames,
88
- // Free floor: across no wall or stand, and through no doorway, where a line stays at the middle.
89
- free: (seg) => !crossesBlocker(blockers, seg) && !emitting.doors.some((d) => segmentsCross(seg, d.gap)),
90
- lane: emitting.cfg.stub,
91
- });
92
- const paths = runs.chains.map((c) => c.pts.map((p) => [p[0], p[1]]));
93
- // A dock whose tip stood on a chord now starts where the tip moved to.
94
- const docks = lawful
95
- .filter((l) => serving.has(l.piece))
96
- .map(({ dock }) => {
97
- const to = runs.moved.get(pointKey(dock.from[0], dock.from[1]));
98
- return to ? { ...dock, from: to } : dock;
99
- });
100
- // Unreached: no dock left for any of its ways in.
101
- const unreached = destinations
102
- .filter((d) => !docked.has(d.id))
103
- .map((d) => failed.get(d.id) ?? { id: d.id, reason: 'detached' });
104
- return { paths, docks, unreached, components: new Set(comp).size };
105
- }
@@ -1,31 +0,0 @@
1
- import type { RouteNetwork } from './graph-types.js';
2
- import type { WfV1Graph, WfV1Line, WfV1Point } from './types.js';
3
- /**
4
- * A v1 graph point on the network's centimeter grid.
5
- * @param x Plan x.
6
- * @param y Plan y.
7
- * @param layer Layer id.
8
- * @returns The point.
9
- */
10
- export declare function point(x: number, y: number, layer: string): WfV1Point;
11
- /**
12
- * A plain walking edge of the v1 graph.
13
- * @param p0 One end.
14
- * @param p1 The other end.
15
- * @returns The edge.
16
- */
17
- export declare function line(p0: WfV1Point, p1: WfV1Point): WfV1Line;
18
- /**
19
- * Appends one layer's route network to `out` as v1 edges — one per path segment and per dock — plus
20
- * a `lineEnd` at each dock's inner end, the point a destination is reached at.
21
- * @param network The layer's route network.
22
- * @param layer Layer id carried onto every point.
23
- * @param out The graph accumulated across layers; mutated.
24
- * @returns What collapsed on the centimeter grid: segments skipped, and the docks whose line is
25
- * lost while their `lineEnd` still stands.
26
- */
27
- export declare function emitLayerV1(network: RouteNetwork, layer: string, out: WfV1Graph): {
28
- collapsedSegments: number;
29
- collapsedDocks: string[];
30
- };
31
- //# sourceMappingURL=emit-v1.d.ts.map
@@ -1,56 +0,0 @@
1
- import { snapCm } from './geom/point-key.js';
2
- /**
3
- * A v1 graph point on the network's centimeter grid.
4
- * @param x Plan x.
5
- * @param y Plan y.
6
- * @param layer Layer id.
7
- * @returns The point.
8
- */
9
- export function point(x, y, layer) {
10
- return { x: snapCm(x), y: snapCm(y), layer };
11
- }
12
- function sameNode(a, b) {
13
- return a.x === b.x && a.y === b.y;
14
- }
15
- /**
16
- * A plain walking edge of the v1 graph.
17
- * @param p0 One end.
18
- * @param p1 The other end.
19
- * @returns The edge.
20
- */
21
- export function line(p0, p1) {
22
- return { p0, p1, virtual: false, unaccessible: false, unidirection: false, weight: 0 };
23
- }
24
- /**
25
- * Appends one layer's route network to `out` as v1 edges — one per path segment and per dock — plus
26
- * a `lineEnd` at each dock's inner end, the point a destination is reached at.
27
- * @param network The layer's route network.
28
- * @param layer Layer id carried onto every point.
29
- * @param out The graph accumulated across layers; mutated.
30
- * @returns What collapsed on the centimeter grid: segments skipped, and the docks whose line is
31
- * lost while their `lineEnd` still stands.
32
- */
33
- export function emitLayerV1(network, layer, out) {
34
- let collapsedSegments = 0;
35
- const collapsedDocks = [];
36
- for (const path of network.paths) {
37
- for (let i = 0; i + 1 < path.length; i++) {
38
- const p0 = point(path[i][0], path[i][1], layer);
39
- const p1 = point(path[i + 1][0], path[i + 1][1], layer);
40
- if (!sameNode(p0, p1))
41
- out.lines.push(line(p0, p1));
42
- else
43
- collapsedSegments++;
44
- }
45
- }
46
- for (const dock of network.docks) {
47
- const from = point(dock.from[0], dock.from[1], layer);
48
- const to = point(dock.to[0], dock.to[1], layer);
49
- if (!sameNode(from, to))
50
- out.lines.push(line(from, to));
51
- else
52
- collapsedDocks.push(dock.id);
53
- out.lineEnds.push(to);
54
- }
55
- return { collapsedSegments, collapsedDocks };
56
- }