@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.
- package/README.md +2 -445
- package/package.json +3 -34
- package/dist/index.d.ts +0 -14
- package/dist/index.js +0 -3
- package/dist/lib/attach.d.ts +0 -43
- package/dist/lib/attach.js +0 -199
- package/dist/lib/axes.d.ts +0 -76
- package/dist/lib/axes.js +0 -103
- package/dist/lib/blockers.d.ts +0 -134
- package/dist/lib/blockers.js +0 -289
- package/dist/lib/cell-array.d.ts +0 -14
- package/dist/lib/cell-array.js +0 -25
- package/dist/lib/compile-layer.d.ts +0 -41
- package/dist/lib/compile-layer.js +0 -241
- package/dist/lib/compile-wayfinding.d.ts +0 -90
- package/dist/lib/compile-wayfinding.js +0 -203
- package/dist/lib/connect.d.ts +0 -35
- package/dist/lib/connect.js +0 -206
- package/dist/lib/diagnostics.d.ts +0 -190
- package/dist/lib/diagnostics.js +0 -634
- package/dist/lib/doorways.d.ts +0 -78
- package/dist/lib/doorways.js +0 -331
- package/dist/lib/emit-network.d.ts +0 -47
- package/dist/lib/emit-network.js +0 -105
- package/dist/lib/emit-v1.d.ts +0 -31
- package/dist/lib/emit-v1.js +0 -56
- package/dist/lib/entries.d.ts +0 -123
- package/dist/lib/entries.js +0 -448
- package/dist/lib/floor-grids.d.ts +0 -48
- package/dist/lib/floor-grids.js +0 -185
- package/dist/lib/framing.d.ts +0 -20
- package/dist/lib/framing.js +0 -187
- package/dist/lib/geom/boolean-ops.d.ts +0 -21
- package/dist/lib/geom/boolean-ops.js +0 -1
- package/dist/lib/geom/clipper2-ops.d.ts +0 -7
- package/dist/lib/geom/clipper2-ops.js +0 -50
- package/dist/lib/geom/footprint.d.ts +0 -26
- package/dist/lib/geom/footprint.js +0 -203
- package/dist/lib/geom/frames.d.ts +0 -116
- package/dist/lib/geom/frames.js +0 -233
- package/dist/lib/geom/mesh-outline.d.ts +0 -13
- package/dist/lib/geom/mesh-outline.js +0 -90
- package/dist/lib/geom/normalize.d.ts +0 -28
- package/dist/lib/geom/normalize.js +0 -118
- package/dist/lib/geom/point-key.d.ts +0 -16
- package/dist/lib/geom/point-key.js +0 -15
- package/dist/lib/geom/primitives.d.ts +0 -105
- package/dist/lib/geom/primitives.js +0 -184
- package/dist/lib/geom/split-crossings.d.ts +0 -14
- package/dist/lib/geom/split-crossings.js +0 -171
- package/dist/lib/geom/types.d.ts +0 -9
- package/dist/lib/geom/types.js +0 -1
- package/dist/lib/geom/walls.d.ts +0 -31
- package/dist/lib/geom/walls.js +0 -71
- package/dist/lib/graph-invariants.d.ts +0 -49
- package/dist/lib/graph-invariants.js +0 -129
- package/dist/lib/graph-types.d.ts +0 -143
- package/dist/lib/graph-types.js +0 -7
- package/dist/lib/mask.d.ts +0 -54
- package/dist/lib/mask.js +0 -210
- package/dist/lib/min-heap.d.ts +0 -24
- package/dist/lib/min-heap.js +0 -73
- package/dist/lib/network-build.d.ts +0 -21
- package/dist/lib/network-build.js +0 -4
- package/dist/lib/network-graph.d.ts +0 -80
- package/dist/lib/network-graph.js +0 -467
- package/dist/lib/network-sizes.d.ts +0 -29
- package/dist/lib/network-sizes.js +0 -22
- package/dist/lib/network-store.d.ts +0 -173
- package/dist/lib/network-store.js +0 -430
- package/dist/lib/openings.d.ts +0 -54
- package/dist/lib/openings.js +0 -387
- package/dist/lib/place-path.d.ts +0 -47
- package/dist/lib/place-path.js +0 -346
- package/dist/lib/post-passes.d.ts +0 -31
- package/dist/lib/post-passes.js +0 -305
- package/dist/lib/reachability.d.ts +0 -58
- package/dist/lib/reachability.js +0 -220
- package/dist/lib/report.d.ts +0 -49
- package/dist/lib/report.js +0 -374
- package/dist/lib/router.d.ts +0 -111
- package/dist/lib/router.js +0 -388
- package/dist/lib/runs.d.ts +0 -92
- package/dist/lib/runs.js +0 -232
- package/dist/lib/scale.d.ts +0 -58
- package/dist/lib/scale.js +0 -185
- package/dist/lib/steiner-network.d.ts +0 -19
- package/dist/lib/steiner-network.js +0 -183
- package/dist/lib/strip.d.ts +0 -43
- package/dist/lib/strip.js +0 -200
- package/dist/lib/stubs.d.ts +0 -84
- package/dist/lib/stubs.js +0 -316
- package/dist/lib/transitions.d.ts +0 -72
- package/dist/lib/transitions.js +0 -191
- package/dist/lib/types.d.ts +0 -126
- package/dist/lib/types.js +0 -1
- package/dist/lib/validate-source.d.ts +0 -41
- package/dist/lib/validate-source.js +0 -133
- package/dist/lib/walkable.d.ts +0 -104
- package/dist/lib/walkable.js +0 -140
package/dist/lib/strip.d.ts
DELETED
|
@@ -1,43 +0,0 @@
|
|
|
1
|
-
import { type Blockers } from './blockers.js';
|
|
2
|
-
import type { Segment, Vec2 } from './geom/types.js';
|
|
3
|
-
/**
|
|
4
|
-
* The longest stretch of an edge with a person's width of free strip outside it, computed exactly from
|
|
5
|
-
* the blockers crossing that strip. The destination's own outline is ignored.
|
|
6
|
-
* @param edge The edge.
|
|
7
|
-
* @param inward Its inward unit normal.
|
|
8
|
-
* @param blockers What stands around it.
|
|
9
|
-
* @param ownId Destination the edge belongs to.
|
|
10
|
-
* @returns `[from, to]` in `[0, 1]`; empty when nothing is free.
|
|
11
|
-
*/
|
|
12
|
-
export declare function freeRunOf(edge: Segment, inward: Vec2, blockers: Blockers, ownId: string): [number, number];
|
|
13
|
-
/**
|
|
14
|
-
* How far the aisle in front of an edge reaches: the height, along the outward normal, at which what
|
|
15
|
-
* stands across — other destinations' outlines, and wall lines (their doorways included) that a
|
|
16
|
-
* destination entered through doorways lines within a person's width behind them, a walled booth's or
|
|
17
|
-
* a room row's — covers half the edge's width or more, each counted at its nearest point. A room
|
|
18
|
-
* corner or a gap in the row across before a part of the edge does not set the aisle, and a hall wall
|
|
19
|
-
* has no row across, whatever stands with its back to it.
|
|
20
|
-
* @param edge The edge.
|
|
21
|
-
* @param outward Its outward unit normal.
|
|
22
|
-
* @param blockers What stands around it.
|
|
23
|
-
* @param ownId Destination the edge belongs to.
|
|
24
|
-
* @param limit How far in front to look; the strip ends there.
|
|
25
|
-
* @returns The height and the destinations across, or `Infinity` and none when nothing stands across
|
|
26
|
-
* half the edge within `limit`.
|
|
27
|
-
*/
|
|
28
|
-
export declare function frontDepth(edge: Segment, outward: Vec2, blockers: Blockers, ownId: string, limit: number): {
|
|
29
|
-
depth: number;
|
|
30
|
-
across: string[];
|
|
31
|
-
};
|
|
32
|
-
/**
|
|
33
|
-
* How far the strip in front of an edge is free: to the nearest wall, outline or doorway gap before any
|
|
34
|
-
* part of it.
|
|
35
|
-
* @param edge The edge.
|
|
36
|
-
* @param outward Its outward unit normal.
|
|
37
|
-
* @param blockers What stands around it.
|
|
38
|
-
* @param ownId Destination the edge belongs to; empty for none.
|
|
39
|
-
* @param limit How far in front to look.
|
|
40
|
-
* @returns The height, or `Infinity` when the strip is free within `limit`.
|
|
41
|
-
*/
|
|
42
|
-
export declare function stripDepth(edge: Segment, outward: Vec2, blockers: Blockers, ownId: string, limit: number): number;
|
|
43
|
-
//# sourceMappingURL=strip.d.ts.map
|
package/dist/lib/strip.js
DELETED
|
@@ -1,200 +0,0 @@
|
|
|
1
|
-
// The strip in front of an edge: how far it is free, and what stands across it.
|
|
2
|
-
import { blockerGrid, BlockerKind } from './blockers.js';
|
|
3
|
-
import { POINT_EPS } from './geom/point-key.js';
|
|
4
|
-
import { boundsOf } from './geom/primitives.js';
|
|
5
|
-
import { NODE_SNAP } from './graph-types.js';
|
|
6
|
-
import { MIN_PASS } from './network-sizes.js';
|
|
7
|
-
/**
|
|
8
|
-
* The longest stretch of an edge with a person's width of free strip outside it, computed exactly from
|
|
9
|
-
* the blockers crossing that strip. The destination's own outline is ignored.
|
|
10
|
-
* @param edge The edge.
|
|
11
|
-
* @param inward Its inward unit normal.
|
|
12
|
-
* @param blockers What stands around it.
|
|
13
|
-
* @param ownId Destination the edge belongs to.
|
|
14
|
-
* @returns `[from, to]` in `[0, 1]`; empty when nothing is free.
|
|
15
|
-
*/
|
|
16
|
-
export function freeRunOf(edge, inward, blockers, ownId) {
|
|
17
|
-
const ex = edge[2] - edge[0];
|
|
18
|
-
const ey = edge[3] - edge[1];
|
|
19
|
-
const span = ex * ex + ey * ey;
|
|
20
|
-
if (!span)
|
|
21
|
-
return [0, 0];
|
|
22
|
-
const reach = 2 * MIN_PASS;
|
|
23
|
-
// Stretches of the edge (as fractions) shaded by a blocker.
|
|
24
|
-
const shaded = [];
|
|
25
|
-
// Only blockers within the strip's box can shade the edge.
|
|
26
|
-
const bx0 = Math.min(edge[0], edge[2]) - reach;
|
|
27
|
-
const bx1 = Math.max(edge[0], edge[2]) + reach;
|
|
28
|
-
const by0 = Math.min(edge[1], edge[3]) - reach;
|
|
29
|
-
const by1 = Math.max(edge[1], edge[3]) + reach;
|
|
30
|
-
const shade = (seg) => {
|
|
31
|
-
const ta = ((seg[0] - edge[0]) * ex + (seg[1] - edge[1]) * ey) / span;
|
|
32
|
-
const tb = ((seg[2] - edge[0]) * ex + (seg[3] - edge[1]) * ey) / span;
|
|
33
|
-
const ha = -((seg[0] - edge[0]) * inward[0] + (seg[1] - edge[1]) * inward[1]);
|
|
34
|
-
const hb = -((seg[2] - edge[0]) * inward[0] + (seg[3] - edge[1]) * inward[1]);
|
|
35
|
-
if ((ha < 0 && hb < 0) || (ha > reach && hb > reach))
|
|
36
|
-
return;
|
|
37
|
-
let ua = ta;
|
|
38
|
-
let ub = tb;
|
|
39
|
-
// Clip the blocker to the strip.
|
|
40
|
-
if (ha !== hb) {
|
|
41
|
-
const at = (h) => ta + ((tb - ta) * (h - ha)) / (hb - ha);
|
|
42
|
-
ua = at(Math.max(0, Math.min(ha, hb)));
|
|
43
|
-
ub = at(Math.min(reach, Math.max(ha, hb)));
|
|
44
|
-
}
|
|
45
|
-
const lo = Math.max(0, Math.min(ua, ub));
|
|
46
|
-
const hi = Math.min(1, Math.max(ua, ub));
|
|
47
|
-
if (hi > lo)
|
|
48
|
-
shaded.push([lo, hi]);
|
|
49
|
-
};
|
|
50
|
-
const grid = blockerGrid(blockers);
|
|
51
|
-
for (const i of grid.near(bx0, by0, bx1, by1))
|
|
52
|
-
if (grid.from[i] === BlockerKind.Wall ||
|
|
53
|
-
(grid.from[i] === BlockerKind.Solid && grid.owner[i] !== ownId))
|
|
54
|
-
shade(grid.segs[i]);
|
|
55
|
-
shaded.sort((a, b) => a[0] - b[0]);
|
|
56
|
-
let walked = 0;
|
|
57
|
-
let best = [0, 0];
|
|
58
|
-
for (const [lo, hi] of shaded) {
|
|
59
|
-
if (lo > walked && lo - walked > best[1] - best[0])
|
|
60
|
-
best = [walked, lo];
|
|
61
|
-
walked = Math.max(walked, hi);
|
|
62
|
-
}
|
|
63
|
-
if (1 - walked > best[1] - best[0])
|
|
64
|
-
best = [walked, 1];
|
|
65
|
-
return best;
|
|
66
|
-
}
|
|
67
|
-
/**
|
|
68
|
-
* The blockers in the strip in front of an edge, each as the share of the edge's width it stands before
|
|
69
|
-
* and its nearest height along the outward normal, nearest first. Exact, from the segments; the edge's
|
|
70
|
-
* own destination's outline, and anything on the edge or behind it, is left out.
|
|
71
|
-
* @param edge The edge.
|
|
72
|
-
* @param outward Its outward unit normal.
|
|
73
|
-
* @param blockers What stands around it.
|
|
74
|
-
* @param ownId Destination the edge belongs to.
|
|
75
|
-
* @param far How far in front the strip reaches.
|
|
76
|
-
* @returns The parts.
|
|
77
|
-
*/
|
|
78
|
-
function stripParts(edge, outward, blockers, ownId, far) {
|
|
79
|
-
const ex = edge[2] - edge[0];
|
|
80
|
-
const ey = edge[3] - edge[1];
|
|
81
|
-
const span = ex * ex + ey * ey;
|
|
82
|
-
if (!span || !(far > 0))
|
|
83
|
-
return [];
|
|
84
|
-
const reach = Math.min(far, 1e6);
|
|
85
|
-
const box = boundsOf([
|
|
86
|
-
[edge[0], edge[1]],
|
|
87
|
-
[edge[2], edge[3]],
|
|
88
|
-
[edge[0] + outward[0] * reach, edge[1] + outward[1] * reach],
|
|
89
|
-
[edge[2] + outward[0] * reach, edge[3] + outward[1] * reach],
|
|
90
|
-
]);
|
|
91
|
-
const grid = blockerGrid(blockers);
|
|
92
|
-
const parts = [];
|
|
93
|
-
for (const i of grid.near(...box)) {
|
|
94
|
-
if (grid.from[i] === BlockerKind.Solid && grid.owner[i] === ownId)
|
|
95
|
-
continue;
|
|
96
|
-
const seg = grid.segs[i];
|
|
97
|
-
// Along the edge (as a share of its length) and in front of it (as a height), at both ends.
|
|
98
|
-
const ta = ((seg[0] - edge[0]) * ex + (seg[1] - edge[1]) * ey) / span;
|
|
99
|
-
const tb = ((seg[2] - edge[0]) * ex + (seg[3] - edge[1]) * ey) / span;
|
|
100
|
-
const ha = (seg[0] - edge[0]) * outward[0] + (seg[1] - edge[1]) * outward[1];
|
|
101
|
-
const hb = (seg[2] - edge[0]) * outward[0] + (seg[3] - edge[1]) * outward[1];
|
|
102
|
-
// Square to the edge, it stands before none of its width.
|
|
103
|
-
if (ta === tb)
|
|
104
|
-
continue;
|
|
105
|
-
// The share of the edge the segment stands before, and the segment's own parameters there:
|
|
106
|
-
// heights are read over that visible part alone.
|
|
107
|
-
const s0 = Math.max(0, Math.min(ta, tb));
|
|
108
|
-
const s1 = Math.min(1, Math.max(ta, tb));
|
|
109
|
-
if (s0 >= s1)
|
|
110
|
-
continue;
|
|
111
|
-
const at = (t) => (t - ta) / (tb - ta);
|
|
112
|
-
const h0 = ha + (hb - ha) * at(s0);
|
|
113
|
-
const h1 = ha + (hb - ha) * at(s1);
|
|
114
|
-
const low = Math.min(h0, h1);
|
|
115
|
-
// On the edge or behind it, or reaching down to it: the edge of a neighbor, not a stand ahead.
|
|
116
|
-
if (low <= NODE_SNAP)
|
|
117
|
-
continue;
|
|
118
|
-
parts.push({ s0, s1, h: low, from: grid.from[i], owner: grid.owner[i] });
|
|
119
|
-
}
|
|
120
|
-
return parts.sort((a, b) => a.h - b.h);
|
|
121
|
-
}
|
|
122
|
-
/**
|
|
123
|
-
* How far the aisle in front of an edge reaches: the height, along the outward normal, at which what
|
|
124
|
-
* stands across — other destinations' outlines, and wall lines (their doorways included) that a
|
|
125
|
-
* destination entered through doorways lines within a person's width behind them, a walled booth's or
|
|
126
|
-
* a room row's — covers half the edge's width or more, each counted at its nearest point. A room
|
|
127
|
-
* corner or a gap in the row across before a part of the edge does not set the aisle, and a hall wall
|
|
128
|
-
* has no row across, whatever stands with its back to it.
|
|
129
|
-
* @param edge The edge.
|
|
130
|
-
* @param outward Its outward unit normal.
|
|
131
|
-
* @param blockers What stands around it.
|
|
132
|
-
* @param ownId Destination the edge belongs to.
|
|
133
|
-
* @param limit How far in front to look; the strip ends there.
|
|
134
|
-
* @returns The height and the destinations across, or `Infinity` and none when nothing stands across
|
|
135
|
-
* half the edge within `limit`.
|
|
136
|
-
*/
|
|
137
|
-
export function frontDepth(edge, outward, blockers, ownId, limit) {
|
|
138
|
-
const lining = 2 * MIN_PASS;
|
|
139
|
-
const parts = stripParts(edge, outward, blockers, ownId, limit + lining);
|
|
140
|
-
const liner = (wall) => parts.find((p) => p.from === BlockerKind.Solid &&
|
|
141
|
-
blockers.walled.has(p.owner) &&
|
|
142
|
-
p.h >= wall.h &&
|
|
143
|
-
p.h - wall.h <= lining &&
|
|
144
|
-
p.s0 < wall.s1 &&
|
|
145
|
-
wall.s0 < p.s1);
|
|
146
|
-
const across = [];
|
|
147
|
-
for (const p of parts) {
|
|
148
|
-
// The wall the free run stopped at lies exactly at the limit: a hair of float noise may not
|
|
149
|
-
// hide the aisle's own far side.
|
|
150
|
-
if (p.h > limit + POINT_EPS)
|
|
151
|
-
continue;
|
|
152
|
-
if (p.from === BlockerKind.Solid)
|
|
153
|
-
across.push(p);
|
|
154
|
-
else {
|
|
155
|
-
// A wall line and its doorways: the far side of a corridor of rooms.
|
|
156
|
-
const room = liner(p);
|
|
157
|
-
if (room)
|
|
158
|
-
across.push({ ...p, owner: room.owner });
|
|
159
|
-
}
|
|
160
|
-
}
|
|
161
|
-
// Nearest first, until what stands across covers half the edge.
|
|
162
|
-
const covered = [];
|
|
163
|
-
const owners = new Set();
|
|
164
|
-
for (const part of across) {
|
|
165
|
-
owners.add(part.owner);
|
|
166
|
-
const kept = [];
|
|
167
|
-
let lo = part.s0;
|
|
168
|
-
let hi = part.s1;
|
|
169
|
-
let total = 0;
|
|
170
|
-
for (const span of covered) {
|
|
171
|
-
if (span[1] < lo || span[0] > hi) {
|
|
172
|
-
kept.push(span);
|
|
173
|
-
total += span[1] - span[0];
|
|
174
|
-
}
|
|
175
|
-
else {
|
|
176
|
-
lo = Math.min(lo, span[0]);
|
|
177
|
-
hi = Math.max(hi, span[1]);
|
|
178
|
-
}
|
|
179
|
-
}
|
|
180
|
-
kept.push([lo, hi]);
|
|
181
|
-
covered.splice(0, covered.length, ...kept);
|
|
182
|
-
if (total + hi - lo >= 0.5)
|
|
183
|
-
return { depth: part.h, across: [...owners] };
|
|
184
|
-
}
|
|
185
|
-
return { depth: Infinity, across: [] };
|
|
186
|
-
}
|
|
187
|
-
/**
|
|
188
|
-
* How far the strip in front of an edge is free: to the nearest wall, outline or doorway gap before any
|
|
189
|
-
* part of it.
|
|
190
|
-
* @param edge The edge.
|
|
191
|
-
* @param outward Its outward unit normal.
|
|
192
|
-
* @param blockers What stands around it.
|
|
193
|
-
* @param ownId Destination the edge belongs to; empty for none.
|
|
194
|
-
* @param limit How far in front to look.
|
|
195
|
-
* @returns The height, or `Infinity` when the strip is free within `limit`.
|
|
196
|
-
*/
|
|
197
|
-
export function stripDepth(edge, outward, blockers, ownId, limit) {
|
|
198
|
-
const [nearest] = stripParts(edge, outward, blockers, ownId, limit);
|
|
199
|
-
return nearest ? nearest.h : Infinity;
|
|
200
|
-
}
|
package/dist/lib/stubs.d.ts
DELETED
|
@@ -1,84 +0,0 @@
|
|
|
1
|
-
import { type AisleAxis } from './axes.js';
|
|
2
|
-
import type { DoorSide } from './doorways.js';
|
|
3
|
-
import { type WayIn, type Whisker } from './entries.js';
|
|
4
|
-
import { type Frame } from './geom/frames.js';
|
|
5
|
-
import type { RouteNetworkDestination } from './graph-types.js';
|
|
6
|
-
import type { MaskCtx } from './mask.js';
|
|
7
|
-
import type { NetworkBuild } from './network-build.js';
|
|
8
|
-
/** A stub planned for a way in: a whisker before it is attached, with the way it grew from. */
|
|
9
|
-
export interface Stub extends Whisker {
|
|
10
|
-
/** The way in. */
|
|
11
|
-
readonly way: WayIn;
|
|
12
|
-
/** How far the stub runs from its foot: to its aisle's axis, else to the middle of the aisle. */
|
|
13
|
-
reach: number;
|
|
14
|
-
/** Half the aisle's width ahead, when the face stands on an aisle: where the stub first ends. */
|
|
15
|
-
readonly middle?: number;
|
|
16
|
-
}
|
|
17
|
-
/** What planning the stubs reads. */
|
|
18
|
-
interface StubPlanning extends NetworkBuild {
|
|
19
|
-
/** The layer's destinations. */
|
|
20
|
-
readonly destinations: readonly RouteNetworkDestination[];
|
|
21
|
-
/** Doorway sides by the destination they open into. */
|
|
22
|
-
readonly owners: ReadonlyMap<string, readonly DoorSide[]>;
|
|
23
|
-
/** The plan's mask: whether a point is on the floor. */
|
|
24
|
-
readonly ctx: MaskCtx;
|
|
25
|
-
/** The direction a destination is drawn in, when it has one. */
|
|
26
|
-
readonly frameOfDest: (d: RouteNetworkDestination) => Frame | undefined;
|
|
27
|
-
/** Entrances with openings: reached through their doorways alone, never from a face. */
|
|
28
|
-
readonly opened: ReadonlySet<string>;
|
|
29
|
-
}
|
|
30
|
-
/** The stubs planned, and the planning a retry goes on from once a stub fails to attach. */
|
|
31
|
-
export interface StubPlan {
|
|
32
|
-
/** Every destination's ways in, by id. */
|
|
33
|
-
readonly ways: ReadonlyMap<string, readonly WayIn[]>;
|
|
34
|
-
/** The stubs planned, in destination order. */
|
|
35
|
-
readonly stubs: Stub[];
|
|
36
|
-
/**
|
|
37
|
-
* Whether a stub across the aisle may run that far: within the stretch its row's pitch allows,
|
|
38
|
-
* or its face's width when it has no row.
|
|
39
|
-
*/
|
|
40
|
-
readonly mayRunAcross: (run: number, way: WayIn) => boolean;
|
|
41
|
-
/**
|
|
42
|
-
* The stub of the next way to try once one failed to attach: the destination's ranked ways go on
|
|
43
|
-
* where the planning stopped, a face only onto a side no whisker serves.
|
|
44
|
-
*/
|
|
45
|
-
readonly retry: (failed: Stub, servedSides: ReadonlyMap<string, ReadonlySet<string>>, whiskers: readonly Whisker[]) => Stub | null;
|
|
46
|
-
}
|
|
47
|
-
/**
|
|
48
|
-
* The side of its destination a way in leaves from, by its normal's main axis.
|
|
49
|
-
* @param way The way in.
|
|
50
|
-
* @returns The side's key.
|
|
51
|
-
*/
|
|
52
|
-
export declare function sideOf(way: WayIn): string;
|
|
53
|
-
/**
|
|
54
|
-
* Plans one stub per open side of each destination, ending mid-aisle: doorways first (a stub from
|
|
55
|
-
* another face would cross the wall), then the most open face; one face stub per side, and a
|
|
56
|
-
* terminal destination gets only its best way in.
|
|
57
|
-
* @param planning What the planning reads.
|
|
58
|
-
* @returns The stubs and the plan a retry goes on from.
|
|
59
|
-
*/
|
|
60
|
-
export declare function planStubs(planning: StubPlanning): StubPlan;
|
|
61
|
-
/**
|
|
62
|
-
* The aisle axes the stubs define, per direction and orientation: the stubs that end mid-aisle
|
|
63
|
-
* define the aisles' lines first; a stub with open floor ahead runs to such a line ahead of it when
|
|
64
|
-
* no corridor fits between the two or its row may run that far, and the rest line up among
|
|
65
|
-
* themselves.
|
|
66
|
-
* @param plan The stubs planned.
|
|
67
|
-
* @param build The layer as the build reads it.
|
|
68
|
-
* @param through Destinations people walk through: they frame nothing.
|
|
69
|
-
* @param floorSpan The floor's own size: nothing on the layer reaches farther.
|
|
70
|
-
* @returns The axes, and each stub's axis.
|
|
71
|
-
*/
|
|
72
|
-
export declare function defineAxes(plan: StubPlan, build: NetworkBuild, through: ReadonlySet<string>, floorSpan: number): {
|
|
73
|
-
axes: AisleAxis[];
|
|
74
|
-
axisOf: Map<Stub, AisleAxis>;
|
|
75
|
-
};
|
|
76
|
-
/**
|
|
77
|
-
* Runs each stub out to its aisle's axis when its dock stays lawful, over floor only.
|
|
78
|
-
* @param axisOf Each stub's axis.
|
|
79
|
-
* @param build The layer as the build reads it.
|
|
80
|
-
* @param ctx The plan's mask: whether a point is on the floor.
|
|
81
|
-
*/
|
|
82
|
-
export declare function runStubsToAxes(axisOf: ReadonlyMap<Stub, AisleAxis>, build: NetworkBuild, ctx: MaskCtx): void;
|
|
83
|
-
export {};
|
|
84
|
-
//# sourceMappingURL=stubs.d.ts.map
|
package/dist/lib/stubs.js
DELETED
|
@@ -1,316 +0,0 @@
|
|
|
1
|
-
// The stubs: a whisker planned for each way into a destination, before it is attached to the
|
|
2
|
-
// network. A stub ends in the middle of the aisle ahead, where the stubs from both sides meet on one
|
|
3
|
-
// line, when the rows on both sides may run that far; else it is half the free run and its own
|
|
4
|
-
// length at most. The tips that end mid-aisle then define the aisles' axes, and each stub runs out
|
|
5
|
-
// to its aisle's axis.
|
|
6
|
-
import { aisleAxes, tipInFrame } from './axes.js';
|
|
7
|
-
import { dockFor, stubLength, waysInto } from './entries.js';
|
|
8
|
-
import { toFrame } from './geom/frames.js';
|
|
9
|
-
import { POINT_EPS } from './geom/point-key.js';
|
|
10
|
-
import { MAX_STRETCH, MIN_PASS } from './network-sizes.js';
|
|
11
|
-
import { stripDepth } from './strip.js';
|
|
12
|
-
/**
|
|
13
|
-
* The side of its destination a way in leaves from, by its normal's main axis.
|
|
14
|
-
* @param way The way in.
|
|
15
|
-
* @returns The side's key.
|
|
16
|
-
*/
|
|
17
|
-
export function sideOf(way) {
|
|
18
|
-
return Math.abs(way.nx) >= Math.abs(way.ny) ? `x${Math.sign(way.nx)}` : `y${Math.sign(way.ny)}`;
|
|
19
|
-
}
|
|
20
|
-
/**
|
|
21
|
-
* Plans one stub per open side of each destination, ending mid-aisle: doorways first (a stub from
|
|
22
|
-
* another face would cross the wall), then the most open face; one face stub per side, and a
|
|
23
|
-
* terminal destination gets only its best way in.
|
|
24
|
-
* @param planning What the planning reads.
|
|
25
|
-
* @returns The stubs and the plan a retry goes on from.
|
|
26
|
-
*/
|
|
27
|
-
export function planStubs(planning) {
|
|
28
|
-
const { destinations, destinationOf, owners, blockers, ctx, cfg, frames, frameOfDest, opened, note, } = planning;
|
|
29
|
-
const ways = new Map(destinations.map((d) => [
|
|
30
|
-
d.id,
|
|
31
|
-
waysInto(d, owners, blockers, cfg, (x, y) => ctx.freeAt(x, y)).filter((way) => way.kind === 'door' || !opened.has(d.id)),
|
|
32
|
-
]));
|
|
33
|
-
const stubs = [];
|
|
34
|
-
// Each way in's row: the ways of one direction whose aisle MIDDLES lie on one line — the stands
|
|
35
|
-
// along one side of one aisle, however their fronts step (staggered fronts share the middle, the
|
|
36
|
-
// next aisle's stands lie a stand and an aisle away). Two at least: a lone face is no row. The
|
|
37
|
-
// pitch is the median distance between neighbors along: the walk between two of them.
|
|
38
|
-
const pitchOf = new Map();
|
|
39
|
-
{
|
|
40
|
-
const lines = new Map();
|
|
41
|
-
for (const d of destinations) {
|
|
42
|
-
const frame = frameOfDest(d) ?? frames[0];
|
|
43
|
-
const sides = new Set();
|
|
44
|
-
for (const way of ways.get(d.id) ?? []) {
|
|
45
|
-
if (way.front === undefined)
|
|
46
|
-
continue;
|
|
47
|
-
const { horiz, c, along } = tipInFrame(frame, way.mx, way.my, way.nx, way.ny);
|
|
48
|
-
const [nu, nv] = toFrame(frame, way.nx, way.ny);
|
|
49
|
-
const sign = Math.sign(horiz ? nv : nu);
|
|
50
|
-
const key = `${frames.indexOf(frame)}|${horiz}|${sign}`;
|
|
51
|
-
// One foot per destination and side: a doorway beside a face is the same neighbor.
|
|
52
|
-
if (sides.has(key))
|
|
53
|
-
continue;
|
|
54
|
-
sides.add(key);
|
|
55
|
-
lines.set(key, [...(lines.get(key) ?? []), { way, c: c + (sign * way.front) / 2, along }]);
|
|
56
|
-
}
|
|
57
|
-
}
|
|
58
|
-
for (const feet of lines.values())
|
|
59
|
-
for (const row of aisleAxes(feet, 2 * cfg.clear, 0, () => 0)) {
|
|
60
|
-
const gaps = row.tips
|
|
61
|
-
.slice(1)
|
|
62
|
-
.map((foot, i) => foot.along - row.tips[i].along)
|
|
63
|
-
.sort((a, b) => a - b);
|
|
64
|
-
const pitch = gaps[Math.floor((gaps.length - 1) / 2)];
|
|
65
|
-
if (pitch !== undefined)
|
|
66
|
-
for (const { way } of row.tips)
|
|
67
|
-
pitchOf.set(way, pitch);
|
|
68
|
-
}
|
|
69
|
-
}
|
|
70
|
-
// A stub may run past its own length to the aisle's line ahead while it is still a stub — at
|
|
71
|
-
// most MAX_STRETCH more of its own length — and while the detour that adds for two neighbors
|
|
72
|
-
// along the row, out and back, stays within MAX_STRETCH of the walk along their fronts: the
|
|
73
|
-
// row's pitch. A lone face has no row to share a line with; the side ACROSS the aisle answers
|
|
74
|
-
// by its face alone when it has no row — its row is not what is asked.
|
|
75
|
-
const fits = (run, pitch) => run <= (1 + MAX_STRETCH) * cfg.stub && 2 * (run - cfg.stub) <= (MAX_STRETCH - 1) * pitch;
|
|
76
|
-
const mayRun = (run, way) => {
|
|
77
|
-
const pitch = pitchOf.get(way);
|
|
78
|
-
return pitch !== undefined && fits(run, pitch);
|
|
79
|
-
};
|
|
80
|
-
const mayRunAcross = (run, way) => fits(run, pitchOf.get(way) ?? way.width);
|
|
81
|
-
/** Each destination's ways in, best first; the attach loop retries down this order. */
|
|
82
|
-
const rankedOf = new Map();
|
|
83
|
-
/** Ways already planned or found unlawful; a retry never returns to one. */
|
|
84
|
-
const tried = new Set();
|
|
85
|
-
/**
|
|
86
|
-
* Plans the stub of one way in. It ends in the middle of the aisle ahead, where the stubs from
|
|
87
|
-
* both sides meet on one line, when the rows on both sides may run that far — one line serves the
|
|
88
|
-
* aisle or none does; wider floor ahead is open floor, and an aisle narrower than a person is
|
|
89
|
-
* none: its own length at most.
|
|
90
|
-
* @param d The destination.
|
|
91
|
-
* @param way Its way in.
|
|
92
|
-
* @returns The stub, or null when the way in is unlawful (noted).
|
|
93
|
-
*/
|
|
94
|
-
const planStub = (d, way) => {
|
|
95
|
-
const tipAt = (run) => [
|
|
96
|
-
+(way.mx + way.nx * run).toFixed(2),
|
|
97
|
-
+(way.my + way.ny * run).toFixed(2),
|
|
98
|
-
];
|
|
99
|
-
const facing = (id) => ways.get(id)?.find((other) => other.nx * way.nx + other.ny * way.ny < 0);
|
|
100
|
-
const middle = way.front !== undefined &&
|
|
101
|
-
way.front >= 2 * MIN_PASS &&
|
|
102
|
-
mayRun(way.front / 2, way) &&
|
|
103
|
-
(way.across ?? []).every((id) => {
|
|
104
|
-
const other = facing(id);
|
|
105
|
-
return other !== undefined && mayRunAcross(way.front / 2, other);
|
|
106
|
-
})
|
|
107
|
-
? +(way.front / 2).toFixed(2)
|
|
108
|
-
: undefined;
|
|
109
|
-
const onAisle = middle !== undefined &&
|
|
110
|
-
middle <= way.depth - MIN_PASS &&
|
|
111
|
-
ctx.freeAt(...tipAt(middle)) &&
|
|
112
|
-
dockFor(d, way, tipAt(middle), blockers) !== null;
|
|
113
|
-
const reach = onAisle ? middle : stubLength(way, cfg);
|
|
114
|
-
const tip = tipAt(reach);
|
|
115
|
-
// Under half a person of room ahead, or no lawful dock: no way in. The noted point is where
|
|
116
|
-
// the way really ends: the blocker in front when there is no room, else the tip.
|
|
117
|
-
if (way.depth - MIN_PASS <= 0 || (!onAisle && !dockFor(d, way, tip, blockers))) {
|
|
118
|
-
note(d.id, 'way-in-blocked', {
|
|
119
|
-
at: way.depth - MIN_PASS <= 0 ? tipAt(Math.max(way.depth, 0)) : tip,
|
|
120
|
-
});
|
|
121
|
-
return null;
|
|
122
|
-
}
|
|
123
|
-
return {
|
|
124
|
-
id: d.id,
|
|
125
|
-
kind: way.kind,
|
|
126
|
-
mx: way.mx,
|
|
127
|
-
my: way.my,
|
|
128
|
-
tx: tip[0],
|
|
129
|
-
ty: tip[1],
|
|
130
|
-
nx: way.nx,
|
|
131
|
-
ny: way.ny,
|
|
132
|
-
to: way.to,
|
|
133
|
-
edge: way.edge,
|
|
134
|
-
frame: frameOfDest(d),
|
|
135
|
-
way,
|
|
136
|
-
reach,
|
|
137
|
-
...(onAisle && { middle }),
|
|
138
|
-
};
|
|
139
|
-
};
|
|
140
|
-
for (const d of destinations) {
|
|
141
|
-
const list = ways.get(d.id) ?? [];
|
|
142
|
-
if (!list.length) {
|
|
143
|
-
note(d.id, 'no-way-in');
|
|
144
|
-
continue;
|
|
145
|
-
}
|
|
146
|
-
// Doorways first (a stub from another face would cross the wall), then the most open face.
|
|
147
|
-
const ranked = list
|
|
148
|
-
.map((way, order) => ({ way, order }))
|
|
149
|
-
.sort((p, q) => Number(q.way.kind !== 'face') - Number(p.way.kind !== 'face') ||
|
|
150
|
-
q.way.depth - p.way.depth ||
|
|
151
|
-
p.order - q.order)
|
|
152
|
-
.map(({ way }) => way);
|
|
153
|
-
rankedOf.set(d.id, ranked);
|
|
154
|
-
// One face stub per side (the best-ranked claims it): a many-edged shape gets at most four.
|
|
155
|
-
const claimed = new Set();
|
|
156
|
-
for (const way of ranked) {
|
|
157
|
-
// Doorways never compete for a side.
|
|
158
|
-
if (way.kind === 'face' && claimed.has(sideOf(way)))
|
|
159
|
-
continue;
|
|
160
|
-
tried.add(way);
|
|
161
|
-
const stub = planStub(d, way);
|
|
162
|
-
if (!stub)
|
|
163
|
-
continue;
|
|
164
|
-
stubs.push(stub);
|
|
165
|
-
if (way.kind === 'face')
|
|
166
|
-
claimed.add(sideOf(way));
|
|
167
|
-
// A terminal destination gets only its best way in.
|
|
168
|
-
if (d.terminal)
|
|
169
|
-
break;
|
|
170
|
-
}
|
|
171
|
-
}
|
|
172
|
-
/**
|
|
173
|
-
* The stub of the next way to try once one failed to attach: the destination's ranked ways go on
|
|
174
|
-
* where the planning stopped, a face only onto a side no whisker serves.
|
|
175
|
-
* @param failed The stub that could not attach.
|
|
176
|
-
* @param servedSides Sides of each destination a whisker already serves.
|
|
177
|
-
* @param whiskers The stubs attached so far.
|
|
178
|
-
* @returns The next stub, or null when no way in is left.
|
|
179
|
-
*/
|
|
180
|
-
const retry = (failed, servedSides, whiskers) => {
|
|
181
|
-
const d = destinationOf.get(failed.id);
|
|
182
|
-
if (d.terminal && whiskers.some((w) => w.id === d.id))
|
|
183
|
-
return null;
|
|
184
|
-
for (const way of rankedOf.get(d.id) ?? []) {
|
|
185
|
-
if (tried.has(way))
|
|
186
|
-
continue;
|
|
187
|
-
if (way.kind === 'face' && servedSides.get(d.id)?.has(sideOf(way)))
|
|
188
|
-
continue;
|
|
189
|
-
tried.add(way);
|
|
190
|
-
const stub = planStub(d, way);
|
|
191
|
-
if (stub)
|
|
192
|
-
return stub;
|
|
193
|
-
}
|
|
194
|
-
return null;
|
|
195
|
-
};
|
|
196
|
-
return { ways, stubs, mayRunAcross, retry };
|
|
197
|
-
}
|
|
198
|
-
/**
|
|
199
|
-
* The aisle axes the stubs define, per direction and orientation: the stubs that end mid-aisle
|
|
200
|
-
* define the aisles' lines first; a stub with open floor ahead runs to such a line ahead of it when
|
|
201
|
-
* no corridor fits between the two or its row may run that far, and the rest line up among
|
|
202
|
-
* themselves.
|
|
203
|
-
* @param plan The stubs planned.
|
|
204
|
-
* @param build The layer as the build reads it.
|
|
205
|
-
* @param through Destinations people walk through: they frame nothing.
|
|
206
|
-
* @param floorSpan The floor's own size: nothing on the layer reaches farther.
|
|
207
|
-
* @returns The axes, and each stub's axis.
|
|
208
|
-
*/
|
|
209
|
-
export function defineAxes(plan, build, through, floorSpan) {
|
|
210
|
-
const { stubs, mayRunAcross } = plan;
|
|
211
|
-
const { frames, blockers, cfg } = build;
|
|
212
|
-
const axes = [];
|
|
213
|
-
const axisOf = new Map();
|
|
214
|
-
const groups = new Map();
|
|
215
|
-
for (const stub of stubs) {
|
|
216
|
-
if (!stub.frame || through.has(stub.id))
|
|
217
|
-
continue;
|
|
218
|
-
const { horiz, c, along } = tipInFrame(stub.frame, stub.tx, stub.ty, stub.nx, stub.ny);
|
|
219
|
-
const key = `${frames.indexOf(stub.frame)}|${horiz}`;
|
|
220
|
-
const group = groups.get(key) ?? { frame: stub.frame, horiz, tips: [] };
|
|
221
|
-
// Where the tip's line may lie across: a comfortable clearance from its face and from what
|
|
222
|
-
// stands across; in an aisle too narrow for that, where the tip is.
|
|
223
|
-
const [nu, nv] = toFrame(stub.frame, stub.nx, stub.ny);
|
|
224
|
-
const out = Math.sign(horiz ? nv : nu);
|
|
225
|
-
const foot = tipInFrame(stub.frame, stub.mx, stub.my, stub.nx, stub.ny).c;
|
|
226
|
-
const room = stub.way.depth - cfg.clear;
|
|
227
|
-
const may = room >= cfg.clear
|
|
228
|
-
? [
|
|
229
|
-
Math.min(foot + out * cfg.clear, foot + out * room),
|
|
230
|
-
Math.max(foot + out * cfg.clear, foot + out * room),
|
|
231
|
-
]
|
|
232
|
-
: [c, c];
|
|
233
|
-
group.tips.push({ stub, c, along, may });
|
|
234
|
-
groups.set(key, group);
|
|
235
|
-
}
|
|
236
|
-
for (const { frame, horiz, tips } of groups.values()) {
|
|
237
|
-
// How far a person walks along the row's line from a tip: the line reaches that far, and no
|
|
238
|
-
// farther than the floor. The search window grows only as needed — the answer is the same,
|
|
239
|
-
// a rotated strip's bounding box stays small.
|
|
240
|
-
const [dx, dy] = horiz ? [frame.cos, frame.sin] : [-frame.sin, frame.cos];
|
|
241
|
-
const reach = (tip, sign) => {
|
|
242
|
-
const strip = [
|
|
243
|
-
tip.stub.tx + dy * MIN_PASS,
|
|
244
|
-
tip.stub.ty - dx * MIN_PASS,
|
|
245
|
-
tip.stub.tx - dy * MIN_PASS,
|
|
246
|
-
tip.stub.ty + dx * MIN_PASS,
|
|
247
|
-
];
|
|
248
|
-
for (let window = 8 * cfg.stub;; window *= 4) {
|
|
249
|
-
const met = stripDepth(strip, [sign * dx, sign * dy], blockers, '', Math.min(window, floorSpan));
|
|
250
|
-
if (isFinite(met))
|
|
251
|
-
return met;
|
|
252
|
-
if (window >= floorSpan)
|
|
253
|
-
return floorSpan;
|
|
254
|
-
}
|
|
255
|
-
};
|
|
256
|
-
const settle = (list) => aisleAxes(list, cfg.cs, 2 * cfg.clear, reach).map((axis) => {
|
|
257
|
-
const full = { frame, horiz, c: axis.c, lo: axis.lo, hi: axis.hi };
|
|
258
|
-
axes.push(full);
|
|
259
|
-
for (const { stub } of axis.tips)
|
|
260
|
-
axisOf.set(stub, full);
|
|
261
|
-
return full;
|
|
262
|
-
});
|
|
263
|
-
// The aisles' lines come from the stubs that end mid-aisle. A stub with open floor ahead runs
|
|
264
|
-
// to such a line ahead of it when no corridor fits between the two, or within the bound its
|
|
265
|
-
// own face gives (it has no row of its own to ask); the rest line up among themselves.
|
|
266
|
-
const lines = settle(tips.filter((tip) => tip.stub.middle !== undefined));
|
|
267
|
-
const loose = [];
|
|
268
|
-
for (const tip of tips) {
|
|
269
|
-
if (tip.stub.middle !== undefined)
|
|
270
|
-
continue;
|
|
271
|
-
const [nu, nv] = toFrame(frame, tip.stub.nx, tip.stub.ny);
|
|
272
|
-
const sign = Math.sign(horiz ? nv : nu);
|
|
273
|
-
let best = null;
|
|
274
|
-
for (const axis of lines) {
|
|
275
|
-
if (axis.lo > tip.along || axis.hi < tip.along)
|
|
276
|
-
continue;
|
|
277
|
-
if (axis.c < tip.may[0] || axis.c > tip.may[1])
|
|
278
|
-
continue;
|
|
279
|
-
const move = (axis.c - tip.c) * sign;
|
|
280
|
-
const run = tip.stub.reach + move;
|
|
281
|
-
if (run < MIN_PASS || (Math.abs(move) > 2 * cfg.clear && !mayRunAcross(run, tip.stub.way)))
|
|
282
|
-
continue;
|
|
283
|
-
if (!best || Math.abs(move) < Math.abs(best.move))
|
|
284
|
-
best = { axis, move };
|
|
285
|
-
}
|
|
286
|
-
if (best)
|
|
287
|
-
axisOf.set(tip.stub, best.axis);
|
|
288
|
-
else
|
|
289
|
-
loose.push(tip);
|
|
290
|
-
}
|
|
291
|
-
settle(loose);
|
|
292
|
-
}
|
|
293
|
-
return { axes, axisOf };
|
|
294
|
-
}
|
|
295
|
-
/**
|
|
296
|
-
* Runs each stub out to its aisle's axis when its dock stays lawful, over floor only.
|
|
297
|
-
* @param axisOf Each stub's axis.
|
|
298
|
-
* @param build The layer as the build reads it.
|
|
299
|
-
* @param ctx The plan's mask: whether a point is on the floor.
|
|
300
|
-
*/
|
|
301
|
-
export function runStubsToAxes(axisOf, build, ctx) {
|
|
302
|
-
const { destinationOf, blockers } = build;
|
|
303
|
-
for (const [stub, axis] of axisOf) {
|
|
304
|
-
const d = destinationOf.get(stub.id);
|
|
305
|
-
const [nu, nv] = toFrame(axis.frame, stub.nx, stub.ny);
|
|
306
|
-
const foot = tipInFrame(axis.frame, stub.mx, stub.my, stub.nx, stub.ny).c;
|
|
307
|
-
const t = +((axis.c - foot) / (axis.horiz ? nv : nu)).toFixed(2);
|
|
308
|
-
if (Math.abs(t - stub.reach) <= POINT_EPS || t < MIN_PASS || t > stub.way.depth - MIN_PASS)
|
|
309
|
-
continue;
|
|
310
|
-
const tip = [+(stub.mx + stub.nx * t).toFixed(2), +(stub.my + stub.ny * t).toFixed(2)];
|
|
311
|
-
if (!ctx.freeAt(tip[0], tip[1]) || !dockFor(d, stub.way, tip, blockers))
|
|
312
|
-
continue;
|
|
313
|
-
stub.reach = t;
|
|
314
|
-
[stub.tx, stub.ty] = tip;
|
|
315
|
-
}
|
|
316
|
-
}
|