@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,123 +0,0 @@
1
- import { type Blockers } from './blockers.js';
2
- import type { DoorSide } from './doorways.js';
3
- import type { Frame } from './geom/frames.js';
4
- import type { Vec2 } from './geom/types.js';
5
- import { type RouteNetworkDestination } from './graph-types.js';
6
- import { type NetworkSizes } from './network-sizes.js';
7
- /** A stub grown from one face: the foot it leaves from and the tip it reaches. */
8
- export interface Whisker {
9
- /** Destination id. */
10
- readonly id: string;
11
- /** Way-in kind it grew from. */
12
- readonly kind: WayIn['kind'];
13
- /** Foot x, on the face. */
14
- readonly mx: number;
15
- /** Foot y. */
16
- readonly my: number;
17
- /** Tip x. */
18
- tx: number;
19
- /** Tip y. */
20
- ty: number;
21
- /** Unit normal out of the destination, x. */
22
- readonly nx: number;
23
- /** Normal y. */
24
- readonly ny: number;
25
- /** Dock end, from {@link WayIn.to}. */
26
- readonly to: Vec2;
27
- /** Dock side, from {@link WayIn.edge}. */
28
- readonly edge?: number;
29
- /** Direction the destination is drawn in; none for a stand set at its own angle. */
30
- readonly frame?: Frame;
31
- }
32
- /**
33
- * The tips of whiskers.
34
- * @param list The whiskers.
35
- * @returns One point per whisker.
36
- */
37
- export declare function tipPoints(list: readonly Whisker[]): Vec2[];
38
- /**
39
- * Moves tips whose vertex was merged into another by noding.
40
- * @param list The whiskers.
41
- * @param moved New position by old tip key.
42
- */
43
- export declare function followTips(list: readonly Whisker[], moved: ReadonlyMap<string, Vec2>): void;
44
- /** One way into a destination: where its stub leaves and which way. */
45
- export interface WayIn {
46
- /** A side of the outline or dock ring, or one of its doorways. */
47
- readonly kind: 'face' | 'door';
48
- /** Foot x: where a dock side's normal meets the outline, or the doorway's middle. */
49
- readonly mx: number;
50
- /** Foot y. */
51
- readonly my: number;
52
- /** Unit normal out of the destination, x. */
53
- readonly nx: number;
54
- /** Normal y. */
55
- readonly ny: number;
56
- /** Free run from the foot along the normal; `Infinity` when nothing stands ahead. */
57
- readonly depth: number;
58
- /**
59
- * How far the aisle in front of the face reaches: to what stands across half its width or more (see
60
- * {@link frontDepth}). Absent when nothing stands across.
61
- */
62
- readonly front?: number;
63
- /** The destinations standing across the aisle, when something does. */
64
- readonly across?: readonly string[];
65
- /** The face's width: its side's length. */
66
- readonly width: number;
67
- /** Dock end, inside the dock ring (the outline when there is none). */
68
- readonly to: Vec2;
69
- /** Ring side the dock ends on. */
70
- readonly edge?: number;
71
- }
72
- /**
73
- * Destinations whose outline the way in's ray meets within `reach`, before any wall. The own outline is
74
- * ignored.
75
- * @param way The way in, at its foot, pointing out.
76
- * @param ownId Destination the way in belongs to.
77
- * @param blockers What the ray may meet.
78
- * @param reach How far ahead to look.
79
- * @returns The ids, each once.
80
- */
81
- export declare function standingBefore(way: Pick<WayIn, 'mx' | 'my' | 'nx' | 'ny'>, ownId: string, blockers: Blockers, reach: number): string[];
82
- /**
83
- * Stub length with open floor ahead: half the free run, at most `cfg.stub`.
84
- * @param way The way in.
85
- * @param cfg Network sizes.
86
- * @returns The length.
87
- */
88
- export declare function stubLength(way: WayIn, cfg: NetworkSizes): number;
89
- /**
90
- * Every way into a destination: dock-ring sides (or outline edges) with room in front, then its doorways.
91
- * @param d The destination.
92
- * @param owners Doorway sides by the destination they open into.
93
- * @param blockers What a run out of it may not cross.
94
- * @param cfg Network sizes.
95
- * @param onFloor Whether a point is walkable floor.
96
- * @returns The ways in, faces first.
97
- */
98
- export declare function waysInto(d: RouteNetworkDestination, owners: ReadonlyMap<string, readonly DoorSide[]>, blockers: Blockers, cfg: NetworkSizes, onFloor?: (x: number, y: number) => boolean): WayIn[];
99
- /**
100
- * Whether a doorway dock turns at the doorway's middle: its end is off the tip–doorway line, and the
101
- * doorway stands clear of the destination's outline.
102
- * @param d The destination.
103
- * @param way The way in.
104
- * @param tip Where the dock leaves from.
105
- * @param to Where it ends.
106
- * @returns True when the dock turns at the doorway's middle.
107
- */
108
- export declare function turnsInDoorway(d: RouteNetworkDestination, way: Pick<WayIn, 'kind' | 'mx' | 'my'>, tip: Vec2, to: Vec2): boolean;
109
- /**
110
- * The way in's dock, when lawful: its end lies inside the ring the destination is reached through, and the run crosses no
111
- * wall, no other outline and, from a face, no doorway gap.
112
- * @param d The destination.
113
- * @param way The way in.
114
- * @param tip The stub's tip.
115
- * @param blockers What the run may not cross.
116
- * @param taken Ring sides already docked by this destination's other stubs.
117
- * @returns The dock end and ring side, or null.
118
- */
119
- export declare function dockFor(d: RouteNetworkDestination, way: Pick<WayIn, 'kind' | 'mx' | 'my' | 'nx' | 'ny' | 'to' | 'edge'>, tip: Vec2, blockers: Blockers, taken?: ReadonlySet<number>): {
120
- to: Vec2;
121
- edge?: number;
122
- } | null;
123
- //# sourceMappingURL=entries.d.ts.map
@@ -1,448 +0,0 @@
1
- // Ways into a destination: the faces and doorways a stub leaves from, and where its dock ends.
2
- import { blockerGrid, BlockerKind, freeRun } from './blockers.js';
3
- import { pointKey } from './geom/point-key.js';
4
- import { onOutline, pointInRing, pointSegmentDistance, rayVsSegment, ringEdges, segmentsCross, signedArea, vertexMean, } from './geom/primitives.js';
5
- import { NODE_SNAP } from './graph-types.js';
6
- import { MIN_PASS } from './network-sizes.js';
7
- import { freeRunOf, frontDepth } from './strip.js';
8
- import { WALL_HALF_WIDTH } from './walkable.js';
9
- /**
10
- * The tips of whiskers.
11
- * @param list The whiskers.
12
- * @returns One point per whisker.
13
- */
14
- export function tipPoints(list) {
15
- return list.map((w) => [w.tx, w.ty]);
16
- }
17
- /**
18
- * Moves tips whose vertex was merged into another by noding.
19
- * @param list The whiskers.
20
- * @param moved New position by old tip key.
21
- */
22
- export function followTips(list, moved) {
23
- for (const w of list) {
24
- const to = moved.get(pointKey(w.tx, w.ty));
25
- if (to)
26
- [w.tx, w.ty] = to;
27
- }
28
- }
29
- /**
30
- * Where a dock through a doorway ends going in square: just past where the doorway's inward normal
31
- * enters the dock ring.
32
- * @param ring Convex ring to land in.
33
- * @param mx Doorway point x.
34
- * @param my Doorway point y.
35
- * @param nx Doorway normal x, out of the destination.
36
- * @param ny Doorway normal y, out of the destination.
37
- * @returns The dock end, or null when the normal misses the ring.
38
- */
39
- function dockTipOnNormal(ring, mx, my, nx, ny) {
40
- // Ray into the destination: p(t) = m - n·t. Crossings behind the start tell whether it is already in
41
- // the ring.
42
- const hits = [];
43
- const hit = [0, 0];
44
- for (let i = 0; i < ring.length; i++) {
45
- const a = ring[i];
46
- const b = ring[(i + 1) % ring.length];
47
- // An edge running along the ray is never crossed by it.
48
- if (!rayVsSegment(hit, mx, my, -nx, -ny, a[0], a[1], b[0], b[1]))
49
- continue;
50
- const [t, s] = hit;
51
- if (s >= 0 && s <= 1)
52
- hits.push(t);
53
- }
54
- // A ray through a vertex crosses both edges that meet there, at one distance: that is one crossing.
55
- const once = hits.sort((a, b) => a - b).filter((t, i, all) => i === 0 || t - all[i - 1] > 1e-9);
56
- const ahead = once.filter((t) => t > NODE_SNAP);
57
- if (!ahead.length)
58
- return null;
59
- // A ring drawn as the footprint or wider holds the doorway already: the stub only has to go far enough
60
- // in to read as inside. Otherwise it enters at the first crossing.
61
- const inRing = once.some((t) => Math.abs(t) <= NODE_SNAP) || once.filter((t) => t < -NODE_SNAP).length % 2;
62
- const near = inRing ? 0 : ahead[0];
63
- // Where the ray leaves the ring (the far side, for a convex ring).
64
- const far = (inRing ? ahead[0] : ahead[1]) ?? near;
65
- // Just past the near edge: clear of a wall drawn along it, so containment never reads the tip as on the
66
- // boundary, and short of crossing the footprint. Half the span when the ring is shallower.
67
- const t = Math.min(near + WALL_HALF_WIDTH * 2, (near + far) / 2);
68
- return [+(mx - nx * t).toFixed(2), +(my - ny * t).toFixed(2)];
69
- }
70
- /**
71
- * Whether a segment crosses a wall where a pass spans it: an entrance's dock goes through the wall
72
- * inside its opening and nowhere else.
73
- * @param leg The segment.
74
- * @param wall The wall it crosses.
75
- * @param passes The spans it may cross.
76
- * @returns True when the crossing lies on one of the passes.
77
- */
78
- function crossesAtPass(leg, wall, passes) {
79
- const hit = [0, 0];
80
- if (!rayVsSegment(hit, leg[0], leg[1], leg[2] - leg[0], leg[3] - leg[1], ...wall))
81
- return false;
82
- const x = leg[0] + (leg[2] - leg[0]) * hit[0];
83
- const y = leg[1] + (leg[3] - leg[1]) * hit[0];
84
- return passes.some((pass) => pointSegmentDistance(x, y, ...pass) <= WALL_HALF_WIDTH);
85
- }
86
- /**
87
- * Whether a stub stays within its destination: it crosses no wall but through a pass of its own, and
88
- * crosses the outline at most once (a concave footprint would otherwise be left and re-entered
89
- * through its notch).
90
- * @param d The destination; none to skip the outline check.
91
- * @param chain The stub as emitted, from the whisker tip inward.
92
- * @param grid The blockers' index.
93
- * @param passes Wall spans the destination's dock may cross: an entrance's openings.
94
- * @returns True when the stub is clear.
95
- */
96
- function stubIsClear(d, chain, grid, passes = []) {
97
- const poly = d?.polygon ?? [];
98
- let outline = 0;
99
- for (let k = 0; k + 1 < chain.length; k++) {
100
- const leg = [chain[k][0], chain[k][1], chain[k + 1][0], chain[k + 1][1]];
101
- if (grid.crossing(leg, BlockerKind.Wall).some((wall) => !crossesAtPass(leg, wall, passes)))
102
- return false;
103
- for (let i = 0; i < poly.length; i++) {
104
- const a = poly[i];
105
- const b = poly[(i + 1) % poly.length];
106
- if (segmentsCross(leg, [a[0], a[1], b[0], b[1]]))
107
- outline++;
108
- }
109
- }
110
- return outline <= 1;
111
- }
112
- /**
113
- * Destinations whose outline the way in's ray meets within `reach`, before any wall. The own outline is
114
- * ignored.
115
- * @param way The way in, at its foot, pointing out.
116
- * @param ownId Destination the way in belongs to.
117
- * @param blockers What the ray may meet.
118
- * @param reach How far ahead to look.
119
- * @returns The ids, each once.
120
- */
121
- export function standingBefore(way, ownId, blockers, reach) {
122
- const hit = [0, 0];
123
- const along = (seg, from) => {
124
- if (!rayVsSegment(hit, way.mx, way.my, way.nx, way.ny, seg[0], seg[1], seg[2], seg[3]))
125
- return Infinity;
126
- const [t, u] = hit;
127
- return u < -1e-9 || u > 1 + 1e-9 || t <= from ? Infinity : Math.max(0, t);
128
- };
129
- // Nearest wall along the ray: anything past it is not in the way.
130
- let wall = reach;
131
- for (const seg of blockers.walls)
132
- wall = Math.min(wall, along(seg, -NODE_SNAP));
133
- const found = new Set();
134
- for (const poi of blockers.solid) {
135
- if (poi.id === ownId)
136
- continue;
137
- for (const edge of poi.edges)
138
- if (along(edge, -NODE_SNAP) <= wall)
139
- found.add(poi.id);
140
- }
141
- return [...found];
142
- }
143
- /**
144
- * Stub length with open floor ahead: half the free run, at most `cfg.stub`.
145
- * @param way The way in.
146
- * @param cfg Network sizes.
147
- * @returns The length.
148
- */
149
- export function stubLength(way, cfg) {
150
- return Math.min(cfg.stub, way.depth / 2);
151
- }
152
- /**
153
- * Ways in laid from a ring (the dock ring, or the outline when there is none): the dock ends at each
154
- * side's middle and the side's outward normal carries it to the outline, where the foot stands — dock end,
155
- * foot and tip on one line. A side is dropped when that line crosses a wall or another outline, or the
156
- * outline is blocked where the line leaves it.
157
- * @param d The destination.
158
- * @param dock Ring the ways in are laid from.
159
- * @param blockers What stands around it.
160
- * @param minSide Shorter sides hold no stub.
161
- * @param freeMiddle Use the middle of a side's free part rather than of its length.
162
- * @returns The ways in, at most one per side.
163
- */
164
- function dockFaces(d, dock, blockers, minSide, freeMiddle) {
165
- const ccw = signedArea(dock) > 0;
166
- // A crossable destination is floor up to its dock ring, so the ring is its outline.
167
- const poly = d.crossable ? dock : d.polygon;
168
- const out = [];
169
- const hit = [0, 0];
170
- for (const [index, edge] of ringEdges(dock).entries()) {
171
- const ex = edge[2] - edge[0];
172
- const ey = edge[3] - edge[1];
173
- const el = Math.hypot(ex, ey);
174
- if (!el || el < minSide)
175
- continue;
176
- const s = ccw ? 1 : -1;
177
- const nx = (s * ey) / el;
178
- const ny = (s * -ex) / el;
179
- // On the own outline, enter a face half taken by a neighbor where it is still open.
180
- const free = freeMiddle ? freeRunOf(edge, [-nx, -ny], blockers, d.id) : [0, 1];
181
- const open = (free[1] - free[0]) * el >= 2 * MIN_PASS ? free : [0, 1];
182
- const along = (open[0] + open[1]) / 2;
183
- const on = [edge[0] + ex * along, edge[1] + ey * along];
184
- const inset = Math.min(WALL_HALF_WIDTH * 2, acrossRing(dock, on, [-nx, -ny], index) / 2);
185
- const to = [+(on[0] - nx * inset).toFixed(2), +(on[1] - ny * inset).toFixed(2)];
186
- // Where the normal leaves the footprint and through which edge; a side on or outside the outline has
187
- // its foot on itself.
188
- const hits = [];
189
- for (let i = 0; i < poly.length; i++) {
190
- const a = poly[i];
191
- const b = poly[(i + 1) % poly.length];
192
- if (!rayVsSegment(hit, on[0], on[1], nx, ny, a[0], a[1], b[0], b[1]))
193
- continue;
194
- const [t, u] = hit;
195
- if (u >= 0 && u <= 1 && t >= -NODE_SNAP)
196
- hits.push({ t: Math.max(0, t), i });
197
- }
198
- hits.sort((p, q) => p.t - q.t);
199
- const inside = pointInRing(poly, on) || (hits.length > 0 && hits[0].t <= NODE_SNAP);
200
- const leave = inside ? hits[0] : undefined;
201
- if (inside && !leave)
202
- continue;
203
- const t = leave?.t ?? 0;
204
- const mx = +(on[0] + nx * t).toFixed(2);
205
- const my = +(on[1] + ny * t).toFixed(2);
206
- const body = [to[0], to[1], mx, my];
207
- const grid = blockerGrid(blockers);
208
- if (grid.crosses(body, BlockerKind.Wall))
209
- continue;
210
- // A dock side drawn past the footprint counts only when no wall separates it from the destination.
211
- if (!inside) {
212
- const middle = vertexMean(d.polygon);
213
- const across = [to[0], to[1], middle[0], middle[1]];
214
- if (grid.crosses(across, BlockerKind.Wall))
215
- continue;
216
- }
217
- if (grid.crosses(body, BlockerKind.Solid, d.id))
218
- continue;
219
- // A person's width of outline around the foot must be open.
220
- const side = leave
221
- ? [poly[leave.i][0], poly[leave.i][1], ...poly[(leave.i + 1) % poly.length]]
222
- : edge;
223
- const sx = side[2] - side[0];
224
- const sy = side[3] - side[1];
225
- const sl = Math.hypot(sx, sy);
226
- const at = ((mx - side[0]) * sx + (my - side[1]) * sy) / (sl * sl);
227
- const lo = Math.max(0, at - MIN_PASS / sl);
228
- const hi = Math.min(1, at + MIN_PASS / sl);
229
- const window = [
230
- side[0] + sx * lo,
231
- side[1] + sy * lo,
232
- side[0] + sx * hi,
233
- side[1] + sy * hi,
234
- ];
235
- const wl = Math.hypot(window[2] - window[0], window[3] - window[1]);
236
- const seen = wl ? freeRunOf(window, [-nx, -ny], blockers, d.id) : [0, 0];
237
- if (seen[1] - seen[0] < 1 - 1e-6)
238
- continue;
239
- const depth = freeRun(mx, my, nx, ny, d.id, d.crossable ? 'skip' : 'ahead', true, blockers);
240
- const { depth: front, across } = frontDepth(side, [nx, ny], blockers, d.id, depth);
241
- out.push({
242
- kind: 'face',
243
- mx,
244
- my,
245
- nx,
246
- ny,
247
- depth,
248
- ...(isFinite(front) && { front, across }),
249
- width: sl,
250
- to,
251
- edge: index,
252
- });
253
- }
254
- return out;
255
- }
256
- /**
257
- * A dock end on the ring side facing a point, just inside: the nearest point (a person's width clear of
258
- * the corners) or the side's middle.
259
- * @param ring The dock ring.
260
- * @param from The point faced.
261
- * @param nearest Nearest point rather than the middle.
262
- * @returns The dock end, or null when no side faces the point.
263
- */
264
- function sideFacing(ring, from, nearest) {
265
- const ccw = signedArea(ring) > 0;
266
- let best = null;
267
- for (const [index, edge] of ringEdges(ring).entries()) {
268
- const ex = edge[2] - edge[0];
269
- const ey = edge[3] - edge[1];
270
- const el = Math.hypot(ex, ey);
271
- if (!el)
272
- continue;
273
- const s = ccw ? 1 : -1;
274
- const nx = (s * ey) / el;
275
- const ny = (s * -ex) / el;
276
- const margin = nearest ? Math.min(el / 2, MIN_PASS) : el / 2;
277
- const along = Math.min(el - margin, Math.max(margin, ((from[0] - edge[0]) * ex + (from[1] - edge[1]) * ey) / el));
278
- const on = [edge[0] + (ex * along) / el, edge[1] + (ey * along) / el];
279
- const score = (from[0] - on[0]) * nx + (from[1] - on[1]) * ny;
280
- if (score <= 0 || (best && score <= best.score))
281
- continue;
282
- const inset = Math.min(WALL_HALF_WIDTH * 2, acrossRing(ring, on, [-nx, -ny], index) / 2);
283
- best = { score, to: [+(on[0] - nx * inset).toFixed(2), +(on[1] - ny * inset).toFixed(2)] };
284
- }
285
- return best?.to ?? null;
286
- }
287
- /**
288
- * Where a dock through a doorway ends: square along its normal when that enters the ring; otherwise on the
289
- * side facing the doorway — at the nearest point when lawful, else at the middle.
290
- * @param dock The dock ring.
291
- * @param from Doorway point the dock leaves from.
292
- * @param nx Doorway normal x, out of the destination.
293
- * @param ny Doorway normal y, out of the destination.
294
- * @param lawful Whether a dock may run to a point.
295
- * @returns The dock end, or null when no side faces the doorway.
296
- */
297
- function doorDockEnd(dock, from, nx, ny, lawful) {
298
- const square = dockTipOnNormal(dock, from[0], from[1], nx, ny);
299
- if (square)
300
- return square;
301
- const near = sideFacing(dock, from, true);
302
- return near && lawful(near) ? near : sideFacing(dock, from, false);
303
- }
304
- /**
305
- * Every way into a destination: dock-ring sides (or outline edges) with room in front, then its doorways.
306
- * @param d The destination.
307
- * @param owners Doorway sides by the destination they open into.
308
- * @param blockers What a run out of it may not cross.
309
- * @param cfg Network sizes.
310
- * @param onFloor Whether a point is walkable floor.
311
- * @returns The ways in, faces first.
312
- */
313
- export function waysInto(d, owners, blockers, cfg, onFloor = () => true) {
314
- const out = [];
315
- // Faces are read once; the strict and fallback passes below only filter them. Without a dock ring the
316
- // outline is used, and its short sides (a curve's) hold no stub.
317
- const ring = d.dock && d.dock.length >= 3 ? d.dock : d.polygon;
318
- const own = ring !== d.dock;
319
- const faces = ring.length >= 3 ? dockFaces(d, ring, blockers, own ? cfg.pitch : 0, own) : [];
320
- if (faces.length) {
321
- // An endless run may just be off the walkable area: check the floor where the stub would stand.
322
- const standsOnFloor = (way) => {
323
- const run = stubLength(way, cfg);
324
- return onFloor(way.mx + way.nx * run, way.my + way.ny * run);
325
- };
326
- const collect = (minDepth) => faces.filter((way) => way.depth >= minDepth && standsOnFloor(way));
327
- // Strict first (nothing within `open` ahead); fall back to `clear` for a tightly packed destination.
328
- const strict = collect(cfg.open);
329
- out.push(...(strict.length ? strict : collect(cfg.clear)));
330
- }
331
- // Doorways: the own outline does not stop the run (a footprint over its own wall is an overshot carve).
332
- for (const { door, nx, ny } of owners.get(d.id) ?? []) {
333
- // The nearest point only suits a dock that turns in the doorway.
334
- const lawful = (at) => {
335
- const way = { kind: 'door', mx: door.mid[0], my: door.mid[1], nx, ny, to: at };
336
- const tip = [door.mid[0] + nx, door.mid[1] + ny];
337
- return turnsInDoorway(d, way, tip, at) && dockFor(d, way, tip, blockers) !== null;
338
- };
339
- const to = ring.length >= 3 ? doorDockEnd(ring, door.mid, nx, ny, lawful) : null;
340
- if (!to)
341
- continue;
342
- const depth = freeRun(door.mid[0], door.mid[1], nx, ny, d.id, 'skip', false, blockers);
343
- // The aisle ahead is read over the doorway's width; the face is the footprint side it opens from.
344
- const { depth: front, across } = frontDepth(door.gap, [nx, ny], blockers, d.id, depth);
345
- const sides = ringEdges(d.polygon);
346
- const side = sides[edgeNearest(sides, door.mid)];
347
- out.push({
348
- kind: 'door',
349
- mx: door.mid[0],
350
- my: door.mid[1],
351
- nx,
352
- ny,
353
- depth,
354
- ...(isFinite(front) && { front, across }),
355
- width: Math.hypot(side[2] - side[0], side[3] - side[1]),
356
- to,
357
- edge: edgeNearest(ringEdges(ring), to),
358
- });
359
- }
360
- return out;
361
- }
362
- /**
363
- * How far a ring reaches straight in from a point on one of its sides.
364
- * @param ring The ring.
365
- * @param from The point, on `side`.
366
- * @param inward The side's inward unit normal.
367
- * @param side Side the point stands on; not counted.
368
- * @returns Distance to the nearest other side; 0 when none is met.
369
- */
370
- function acrossRing(ring, from, inward, side) {
371
- const hit = [0, 0];
372
- let near = Infinity;
373
- for (let i = 0; i < ring.length; i++) {
374
- if (i === side)
375
- continue;
376
- const a = ring[i];
377
- const b = ring[(i + 1) % ring.length];
378
- if (!rayVsSegment(hit, from[0], from[1], inward[0], inward[1], a[0], a[1], b[0], b[1]))
379
- continue;
380
- const [t, u] = hit;
381
- if (u >= 0 && u <= 1 && t > NODE_SNAP && t < near)
382
- near = t;
383
- }
384
- return isFinite(near) ? near : 0;
385
- }
386
- /**
387
- * The ring edge nearest a point.
388
- * @param edges The ring's edges.
389
- * @param at The point.
390
- * @returns Its index.
391
- */
392
- function edgeNearest(edges, at) {
393
- let best = 0;
394
- let near = Infinity;
395
- for (const [i, e] of edges.entries()) {
396
- const d = pointSegmentDistance(at[0], at[1], e[0], e[1], e[2], e[3]);
397
- if (d < near) {
398
- near = d;
399
- best = i;
400
- }
401
- }
402
- return best;
403
- }
404
- /**
405
- * Whether a doorway dock turns at the doorway's middle: its end is off the tip–doorway line, and the
406
- * doorway stands clear of the destination's outline.
407
- * @param d The destination.
408
- * @param way The way in.
409
- * @param tip Where the dock leaves from.
410
- * @param to Where it ends.
411
- * @returns True when the dock turns at the doorway's middle.
412
- */
413
- export function turnsInDoorway(d, way, tip, to) {
414
- return (way.kind === 'door' &&
415
- pointSegmentDistance(way.mx, way.my, tip[0], tip[1], to[0], to[1]) > NODE_SNAP &&
416
- !pointInRing(d.polygon, [way.mx, way.my]) &&
417
- !onOutline(d.polygon, way.mx, way.my, NODE_SNAP));
418
- }
419
- /**
420
- * The way in's dock, when lawful: its end lies inside the ring the destination is reached through, and the run crosses no
421
- * wall, no other outline and, from a face, no doorway gap.
422
- * @param d The destination.
423
- * @param way The way in.
424
- * @param tip The stub's tip.
425
- * @param blockers What the run may not cross.
426
- * @param taken Ring sides already docked by this destination's other stubs.
427
- * @returns The dock end and ring side, or null.
428
- */
429
- export function dockFor(d, way, tip, blockers, taken) {
430
- const ring = d.dock ?? d.polygon;
431
- const to = way.to;
432
- // One face stub per ring side; doorways are exempt.
433
- if (ring.length < 3 || (way.kind === 'face' && way.edge !== undefined && taken?.has(way.edge)))
434
- return null;
435
- if (!pointInRing(ring, to))
436
- return null;
437
- const run = [tip[0], tip[1], to[0], to[1]];
438
- // A turning doorway dock runs through the doorway's middle first.
439
- const chain = turnsInDoorway(d, way, tip, to) ? [tip, [way.mx, way.my], to] : [tip, to];
440
- const grid = blockerGrid(blockers);
441
- if (!stubIsClear(d.crossable ? undefined : d, chain, grid, blockers.passes?.get(d.id)))
442
- return null;
443
- if (chain.slice(1).some((p, k) => grid.crosses([...chain[k], ...p], BlockerKind.Solid, d.id)))
444
- return null;
445
- if (way.kind === 'face' && grid.crosses(run, BlockerKind.Gap))
446
- return null;
447
- return { to, edge: way.edge };
448
- }
@@ -1,48 +0,0 @@
1
- import { type DoorSide, type Doorway } from './doorways.js';
2
- import { type Frame } from './geom/frames.js';
3
- import type { Ring, Segment } from './geom/types.js';
4
- import { type RouteNetworkDestination } from './graph-types.js';
5
- import { type NetworkSizes } from './network-sizes.js';
6
- import type { Grid } from './router.js';
7
- /**
8
- * The floor pieces a route can serve: an outer ring (with its holes) is kept when a destination or
9
- * transition stop stands in it, or a doorway into a destination opens onto it.
10
- * @param rings Walkable outer rings and holes (holes wound the other way).
11
- * @param destinations The layer's destinations, transition stops included.
12
- * @param owners Doorway sides by the destination they open into.
13
- * @returns The rings kept, and how many outer rings were left out.
14
- */
15
- export declare function servedFloor(rings: readonly Ring[], destinations: readonly RouteNetworkDestination[], owners: ReadonlyMap<string, readonly DoorSide[]>): {
16
- rings: Ring[];
17
- dropped: number;
18
- };
19
- /** The grids a layer's network is walked on, and the direction each destination is drawn in. */
20
- interface FloorGrids {
21
- /** One grid per direction that got one, the plan's first. */
22
- readonly grids: Grid[];
23
- /** The directions that got a grid; every pass works in these. */
24
- readonly frames: Frame[];
25
- /**
26
- * The direction a destination is drawn in; none for one reached on the plan's grid, which gets no
27
- * framing and holds every line at its tips.
28
- */
29
- readonly frameOfDest: (d: RouteNetworkDestination) => Frame | undefined;
30
- }
31
- /**
32
- * One grid per direction the destinations are drawn in, walked as one raster, so a turned hall's
33
- * aisles run straight on its own grid. The plan's grid covers the whole floor; a turned direction's
34
- * grid covers its groups of two or more destinations whose reaches (a stub around each) meet and
35
- * which stand in a hall turned their way. A lone turned stand, or stands turned in a hall drawn
36
- * square, get no grid. Every doorway's throat is passable at its middle on every grid, however
37
- * narrow the gap: routes and lines pass there, and only there; the jambs stay closed.
38
- * @param floor The floor pieces a route can serve.
39
- * @param doorSides The closed sides of the doorways, masked like walls.
40
- * @param destinations The layer's destinations.
41
- * @param wallLines The layer's wall segments.
42
- * @param doors The layer's doorways.
43
- * @param cfg Network sizes.
44
- * @returns The grids, their directions, and each destination's direction.
45
- */
46
- export declare function floorGrids(floor: readonly Ring[], doorSides: readonly Segment[], destinations: readonly RouteNetworkDestination[], wallLines: readonly Segment[], doors: readonly Doorway[], cfg: NetworkSizes): FloorGrids;
47
- export {};
48
- //# sourceMappingURL=floor-grids.d.ts.map