@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.
- 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/centering.d.ts +0 -14
- package/dist/lib/centering.js +0 -88
- 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/grid-network.d.ts +0 -20
- package/dist/lib/grid-network.js +0 -349
- package/dist/lib/lattice-map.d.ts +0 -19
- package/dist/lib/lattice-map.js +0 -92
- package/dist/lib/lattice-network.d.ts +0 -32
- package/dist/lib/lattice-network.js +0 -289
- 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-setup.d.ts +0 -50
- package/dist/lib/network-setup.js +0 -76
- 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/grid-network.js
DELETED
|
@@ -1,349 +0,0 @@
|
|
|
1
|
-
// The "grid" route-network generator: one tree of cheapest paths over the plan's raster. A stub per
|
|
2
|
-
// open face ends on a router cell; a single growing search then joins the tips, nearest first, each
|
|
3
|
-
// path laid along the cell centers and free to walk from then on. What shapes the lines is the cost
|
|
4
|
-
// of a cell alone — a corridor's middle row is the cheapest, a turn costs a length — and a staircase
|
|
5
|
-
// along one corridor is cut to a straight line. On request, links are cut into the tree where it
|
|
6
|
-
// sends a route the long way round. Produces the {@link RouteNetwork} for `emit-v1`.
|
|
7
|
-
import { crossesBlocker } from './blockers.js';
|
|
8
|
-
import { createConnector } from './connect.js';
|
|
9
|
-
import { emitNetwork, turnDoorwayDocks } from './emit-network.js';
|
|
10
|
-
import { dockFor, followTips, tipPoints } from './entries.js';
|
|
11
|
-
import { floorGrids } from './floor-grids.js';
|
|
12
|
-
import { POINT_EPS, pointKey } from './geom/point-key.js';
|
|
13
|
-
import { pointSegmentDistance } from './geom/primitives.js';
|
|
14
|
-
import { chainSegments, chainsFromEdges } from './network-graph.js';
|
|
15
|
-
import { setUpNetwork } from './network-setup.js';
|
|
16
|
-
import { MIN_PASS } from './network-sizes.js';
|
|
17
|
-
import { NetworkStore } from './network-store.js';
|
|
18
|
-
import { shortcutDetours } from './post-passes.js';
|
|
19
|
-
import { makeRouter } from './router.js';
|
|
20
|
-
import { planStubs, sideOf } from './stubs.js';
|
|
21
|
-
/** What a cell at a corridor's wall costs over one on its middle row, as a share of its length. */
|
|
22
|
-
const CENTERING = 1;
|
|
23
|
-
/** The sine of the angle under which a stub counts as running along a line rather than across it. */
|
|
24
|
-
const ALONG = 0.2;
|
|
25
|
-
/**
|
|
26
|
-
* Cuts each staircase to a straight line: along a chain, the farthest vertex a free straight line
|
|
27
|
-
* reaches while every vertex between stays within a lane of it. A stub's tip on the staircase moves
|
|
28
|
-
* along the stub onto the line, so a corridor between two meetings is one line however many stubs
|
|
29
|
-
* end on it; where a dock from there would be unlawful the tip stays, and the line ends at it. A
|
|
30
|
-
* corner with legs longer than the lane stays a corner.
|
|
31
|
-
* @param store The network, every meeting a node; rebuilt in place.
|
|
32
|
-
* @param whiskers The attached stubs; a tip on a cut staircase moves onto the line.
|
|
33
|
-
* @param lane How far off the straight line a staircase's vertices may stand.
|
|
34
|
-
* @param free Whether a straight line may be laid.
|
|
35
|
-
* @param lawful Whether a stub may dock from a point.
|
|
36
|
-
*/
|
|
37
|
-
function cutStairs(store, whiskers, lane, free, lawful) {
|
|
38
|
-
const tipsAt = new Map();
|
|
39
|
-
for (const stub of whiskers) {
|
|
40
|
-
const key = pointKey(stub.tx, stub.ty);
|
|
41
|
-
tipsAt.set(key, [...(tipsAt.get(key) ?? []), stub]);
|
|
42
|
-
}
|
|
43
|
-
const chains = chainsFromEdges(store.toArray(), new Set(tipsAt.keys()));
|
|
44
|
-
for (const chain of chains) {
|
|
45
|
-
const { pts } = chain;
|
|
46
|
-
const kept = [pts[0]];
|
|
47
|
-
/**
|
|
48
|
-
* Reads the vertices between two of the chain's as a staircase.
|
|
49
|
-
* @param from Index of the vertex the line starts at.
|
|
50
|
-
* @param to Index of the vertex it ends at.
|
|
51
|
-
* @returns The tips between them moved onto the line, or null when a line may not replace them.
|
|
52
|
-
*/
|
|
53
|
-
const stairs = (from, to) => {
|
|
54
|
-
const [ax, ay] = pts[from];
|
|
55
|
-
const [bx, by] = pts[to];
|
|
56
|
-
const dx = bx - ax;
|
|
57
|
-
const dy = by - ay;
|
|
58
|
-
const length = Math.hypot(dx, dy);
|
|
59
|
-
const moved = [];
|
|
60
|
-
// A chain that closes on itself has no line between its ends.
|
|
61
|
-
if (length < POINT_EPS)
|
|
62
|
-
return null;
|
|
63
|
-
for (let k = from + 1; k < to; k++)
|
|
64
|
-
if (pointSegmentDistance(pts[k][0], pts[k][1], ax, ay, bx, by) > lane)
|
|
65
|
-
return null;
|
|
66
|
-
for (let k = from + 1; k < to; k++) {
|
|
67
|
-
const [px, py] = pts[k];
|
|
68
|
-
for (const stub of tipsAt.get(pointKey(px, py)) ?? []) {
|
|
69
|
-
// Where the stub's own line meets the straight line; a stub running along the line keeps
|
|
70
|
-
// its place along it.
|
|
71
|
-
const cross = dx * stub.ny - dy * stub.nx;
|
|
72
|
-
const along = Math.abs(cross) > ALONG * length
|
|
73
|
-
? ((stub.mx - ax) * stub.ny - (stub.my - ay) * stub.nx) / cross
|
|
74
|
-
: ((px - ax) * dx + (py - ay) * dy) / (length * length);
|
|
75
|
-
const at = [+(ax + along * dx).toFixed(2), +(ay + along * dy).toFixed(2)];
|
|
76
|
-
const run = (at[0] - stub.mx) * stub.nx + (at[1] - stub.my) * stub.ny;
|
|
77
|
-
if (along < 0 || along > 1 || run < MIN_PASS || !lawful(stub, at))
|
|
78
|
-
return null;
|
|
79
|
-
moved.push({ stub, along, at });
|
|
80
|
-
}
|
|
81
|
-
}
|
|
82
|
-
return free([ax, ay, bx, by]) ? moved : null;
|
|
83
|
-
};
|
|
84
|
-
// The edge to the next vertex is the network's own: it stays as it is.
|
|
85
|
-
const cut = (from, to) => to > from + 1 ? stairs(from, to) : [];
|
|
86
|
-
let from = 0;
|
|
87
|
-
while (from < pts.length - 1) {
|
|
88
|
-
let to = pts.length - 1;
|
|
89
|
-
let moved = cut(from, to);
|
|
90
|
-
while (!moved)
|
|
91
|
-
moved = cut(from, --to);
|
|
92
|
-
for (const { stub, at } of moved.sort((a, b) => a.along - b.along)) {
|
|
93
|
-
[stub.tx, stub.ty] = at;
|
|
94
|
-
kept.push([at[0], at[1]]);
|
|
95
|
-
}
|
|
96
|
-
kept.push(pts[to]);
|
|
97
|
-
from = to;
|
|
98
|
-
}
|
|
99
|
-
chain.pts = kept;
|
|
100
|
-
}
|
|
101
|
-
store.reset(chainSegments(chains));
|
|
102
|
-
}
|
|
103
|
-
/**
|
|
104
|
-
* Builds the grid route network for one layer.
|
|
105
|
-
* @param walkableRings The layer's walkable polygon (outer rings + holes).
|
|
106
|
-
* @param destinations Footprints the network must reach.
|
|
107
|
-
* @param walls Raw wall polylines as flat `[x0, y0, …]` runs, for exact thin-wall blocking.
|
|
108
|
-
* @param prepared The same walls already prepared, when the caller has them.
|
|
109
|
-
* @param drawn The rest of each destination's drawing; ways in may not cross it.
|
|
110
|
-
* @param openings The openings the entrances make: the ways in they own, through the walls.
|
|
111
|
-
* @param shortcuts Whether links are cut into the tree where it sends a route the long way round.
|
|
112
|
-
* @returns Paths, docks and stats for `emit-v1`.
|
|
113
|
-
*/
|
|
114
|
-
export function buildGridNetwork(walkableRings, destinations, walls, prepared, drawn = [], openings = [], shortcuts = false) {
|
|
115
|
-
const { cfg, wallLines, doors, owners, opened, floor, unservedFloorPieces, doorSides, blockers, destinationOf, failed, note, } = setUpNetwork(walkableRings, destinations, walls, prepared, drawn, openings);
|
|
116
|
-
// One grid, the plan's: a hall turned its own way is walked on it too.
|
|
117
|
-
const { grids, frames, frameOfDest } = floorGrids(floor, doorSides, destinations, wallLines, doors, cfg, false);
|
|
118
|
-
const ctx = grids[0].ctx;
|
|
119
|
-
const router = makeRouter(grids, cfg, CENTERING);
|
|
120
|
-
const store = new NetworkStore();
|
|
121
|
-
const build = { cfg, frames, blockers, destinationOf, note };
|
|
122
|
-
const mayLay = (seg) => ctx.lineFree(seg[0], seg[1], seg[2], seg[3]) && !crossesBlocker(blockers, seg);
|
|
123
|
-
/* ---- stubs: one per open side of each destination, as the steiner network plans them ---- */
|
|
124
|
-
const plan = planStubs({ ...build, destinations, owners, ctx, frameOfDest, opened });
|
|
125
|
-
const whiskers = [];
|
|
126
|
-
const anchors = [];
|
|
127
|
-
/** Sides of each destination a whisker already serves. */
|
|
128
|
-
const servedSides = new Map();
|
|
129
|
-
const attach = (stub) => {
|
|
130
|
-
whiskers.push(stub);
|
|
131
|
-
if (stub.kind === 'face')
|
|
132
|
-
servedSides.set(stub.id, (servedSides.get(stub.id) ?? new Set()).add(sideOf(stub.way)));
|
|
133
|
-
};
|
|
134
|
-
/**
|
|
135
|
-
* Moves a stub's tip onto a router cell: the planned tip's, or one a step nearer or farther along
|
|
136
|
-
* the stub — the cheapest to stand on from which the dock is lawful, the planned one among equals.
|
|
137
|
-
* Where the floor around the planned tip is too tight to walk, the nearest such cell along the
|
|
138
|
-
* stub, toward the foot first.
|
|
139
|
-
* @param stub The stub; its tip moves to the cell's center.
|
|
140
|
-
* @returns The cell, or -1 when the stub has none (noted).
|
|
141
|
-
*/
|
|
142
|
-
const seat = (stub) => {
|
|
143
|
-
const destination = destinationOf.get(stub.id);
|
|
144
|
-
const farthest = Math.min(stub.way.depth - MIN_PASS, stub.reach + 2 * cfg.stub);
|
|
145
|
-
let onFloor = false;
|
|
146
|
-
/**
|
|
147
|
-
* The cell a number of steps along the stub from its planned tip, when a dock from it is lawful.
|
|
148
|
-
* @param steps Cells along the stub, toward the foot when negative.
|
|
149
|
-
* @returns The cell, or -1.
|
|
150
|
-
*/
|
|
151
|
-
const cellAt = (steps) => {
|
|
152
|
-
const run = steps * router.pitch;
|
|
153
|
-
if (stub.reach + run < MIN_PASS || stub.reach + run > farthest)
|
|
154
|
-
return -1;
|
|
155
|
-
const cell = router.cellIn(stub.tx + stub.nx * run, stub.ty + stub.ny * run, frames[0]);
|
|
156
|
-
if (cell < 0 || !router.ok[cell])
|
|
157
|
-
return -1;
|
|
158
|
-
onFloor = true;
|
|
159
|
-
return dockFor(destination, stub.way, router.worldOf(cell), blockers) ? cell : -1;
|
|
160
|
-
};
|
|
161
|
-
let best = cellAt(0);
|
|
162
|
-
for (const steps of [-1, 1]) {
|
|
163
|
-
const cell = cellAt(steps);
|
|
164
|
-
if (cell >= 0 && (best < 0 || router.stepCost[cell] < router.stepCost[best]))
|
|
165
|
-
best = cell;
|
|
166
|
-
}
|
|
167
|
-
for (const sign of [-1, 1])
|
|
168
|
-
for (let steps = 2; best < 0 && Math.abs(steps * router.pitch) <= farthest; steps++)
|
|
169
|
-
best = cellAt(sign * steps);
|
|
170
|
-
if (best < 0) {
|
|
171
|
-
note(stub.id, onFloor ? 'way-in-blocked' : 'no-floor', { at: [stub.tx, stub.ty] });
|
|
172
|
-
return -1;
|
|
173
|
-
}
|
|
174
|
-
[stub.tx, stub.ty] = router.worldOf(best);
|
|
175
|
-
anchors.push({ id: stub.id, at: [stub.tx, stub.ty] });
|
|
176
|
-
return best;
|
|
177
|
-
};
|
|
178
|
-
const around = Math.round(cfg.stub / router.pitch);
|
|
179
|
-
/**
|
|
180
|
-
* How much floor lies around a cell: the walkable cells within a stub's length of it.
|
|
181
|
-
* @param cell The cell.
|
|
182
|
-
* @returns The count.
|
|
183
|
-
*/
|
|
184
|
-
const roomAround = (cell) => {
|
|
185
|
-
const [x, y] = router.worldOf(cell);
|
|
186
|
-
let room = 0;
|
|
187
|
-
for (let dy = -around; dy <= around; dy++)
|
|
188
|
-
for (let dx = -around; dx <= around; dx++) {
|
|
189
|
-
const near = router.cellIn(x + dx * router.pitch, y + dy * router.pitch, frames[0]);
|
|
190
|
-
if (near >= 0 && router.ok[near])
|
|
191
|
-
room++;
|
|
192
|
-
}
|
|
193
|
-
return room;
|
|
194
|
-
};
|
|
195
|
-
/* ---- the tree: every tip joined to the network laid so far, nearest first ---- */
|
|
196
|
-
/** 1 for each cell a laid path passes. */
|
|
197
|
-
const laid = new Uint8Array(router.cells);
|
|
198
|
-
const network = [];
|
|
199
|
-
/** Stubs whose tip stands on floor no other tip shares. */
|
|
200
|
-
const stranded = [];
|
|
201
|
-
let trees = 0;
|
|
202
|
-
/**
|
|
203
|
-
* One growth over the stubs: each tip is joined by the cheapest path from the network laid so
|
|
204
|
-
* far; with no network, or floor it cannot reach, a tree starts from the roomiest tip left.
|
|
205
|
-
* @param stubs The stubs to join.
|
|
206
|
-
* @returns The stubs that could not be: no cell to stand on, or alone on their floor.
|
|
207
|
-
*/
|
|
208
|
-
const grow = (stubs) => {
|
|
209
|
-
const group = new Int32Array(router.cells).fill(-1);
|
|
210
|
-
const targets = [];
|
|
211
|
-
const lost = [];
|
|
212
|
-
for (const stub of stubs) {
|
|
213
|
-
const cell = seat(stub);
|
|
214
|
-
if (cell < 0) {
|
|
215
|
-
lost.push(stub);
|
|
216
|
-
continue;
|
|
217
|
-
}
|
|
218
|
-
// Already on a laid line: the tip becomes a node of it.
|
|
219
|
-
if (laid[cell]) {
|
|
220
|
-
store.splitAt(store.segmentAt(stub.tx, stub.ty), stub.tx, stub.ty);
|
|
221
|
-
attach(stub);
|
|
222
|
-
continue;
|
|
223
|
-
}
|
|
224
|
-
if (group[cell] < 0) {
|
|
225
|
-
group[cell] = targets.length;
|
|
226
|
-
targets.push({ cell, stubs: [], room: roomAround(cell) });
|
|
227
|
-
}
|
|
228
|
-
targets[group[cell]].stubs.push(stub);
|
|
229
|
-
}
|
|
230
|
-
// Trees start from the tip with most floor around it; the stubs' own order among equals.
|
|
231
|
-
const order = targets
|
|
232
|
-
.map((_, index) => index)
|
|
233
|
-
.sort((a, b) => targets[b].room - targets[a].room || a - b);
|
|
234
|
-
/** The target a tree started from, by its cell, while nothing has joined it. */
|
|
235
|
-
const lone = new Map();
|
|
236
|
-
let next = 0;
|
|
237
|
-
const reseed = () => {
|
|
238
|
-
while (next < order.length && group[targets[order[next]].cell] < 0)
|
|
239
|
-
next++;
|
|
240
|
-
if (next >= order.length)
|
|
241
|
-
return null;
|
|
242
|
-
const { cell } = targets[order[next]];
|
|
243
|
-
group[cell] = -1;
|
|
244
|
-
lone.set(cell, order[next]);
|
|
245
|
-
return [cell];
|
|
246
|
-
};
|
|
247
|
-
router.grow(network.length ? network : (reseed() ?? []), group, reseed, (cells, reached) => {
|
|
248
|
-
const root = cells[cells.length - 1];
|
|
249
|
-
const seed = lone.get(root);
|
|
250
|
-
// The first path of a tree: the tip it started from is on the network now.
|
|
251
|
-
if (seed !== undefined) {
|
|
252
|
-
lone.delete(root);
|
|
253
|
-
trees++;
|
|
254
|
-
targets[seed].stubs.forEach(attach);
|
|
255
|
-
}
|
|
256
|
-
for (const [x0, y0, x1, y1] of router.segmentsAlong(cells))
|
|
257
|
-
store.add(x0, y0, x1, y1);
|
|
258
|
-
for (const cell of cells) {
|
|
259
|
-
if (laid[cell])
|
|
260
|
-
continue;
|
|
261
|
-
laid[cell] = 1;
|
|
262
|
-
network.push(cell);
|
|
263
|
-
}
|
|
264
|
-
targets[reached].stubs.forEach(attach);
|
|
265
|
-
return [];
|
|
266
|
-
});
|
|
267
|
-
for (const target of lone.values())
|
|
268
|
-
for (const stub of targets[target].stubs) {
|
|
269
|
-
note(stub.id, 'cut-off', { at: [stub.tx, stub.ty] });
|
|
270
|
-
stranded.push(stub);
|
|
271
|
-
lost.push(stub);
|
|
272
|
-
}
|
|
273
|
-
return lost;
|
|
274
|
-
};
|
|
275
|
-
let pending = plan.stubs;
|
|
276
|
-
// A stub that could not be joined is retried from its destination's next way in.
|
|
277
|
-
while (pending.length)
|
|
278
|
-
pending = grow(pending).flatMap((stub) => plan.retry(stub, servedSides, whiskers) ?? []);
|
|
279
|
-
// No network at all (a plan with one destination): a stub's own run is its line.
|
|
280
|
-
if (!store.size) {
|
|
281
|
-
const own = stranded.find((stub) => mayLay([stub.mx, stub.my, stub.tx, stub.ty]));
|
|
282
|
-
if (own) {
|
|
283
|
-
store.add(own.mx, own.my, own.tx, own.ty);
|
|
284
|
-
attach(own);
|
|
285
|
-
}
|
|
286
|
-
}
|
|
287
|
-
followTips(whiskers, store.node(tipPoints(whiskers)));
|
|
288
|
-
/* ---- shortcuts: links across where the tree sends a route the long way round ---- */
|
|
289
|
-
let linked = 0;
|
|
290
|
-
if (shortcuts) {
|
|
291
|
-
const connector = createConnector(store, router, cfg, {
|
|
292
|
-
frames,
|
|
293
|
-
axes: [],
|
|
294
|
-
doors,
|
|
295
|
-
blockers,
|
|
296
|
-
mayLay,
|
|
297
|
-
});
|
|
298
|
-
const tree = store.toArray();
|
|
299
|
-
// Through the connector, so a link ends on the line that claims its last cell.
|
|
300
|
-
store.reset([]);
|
|
301
|
-
for (const seg of tree)
|
|
302
|
-
connector.add(seg);
|
|
303
|
-
linked = shortcutDetours(store, router, connector);
|
|
304
|
-
followTips(whiskers, store.node(tipPoints(whiskers)));
|
|
305
|
-
}
|
|
306
|
-
/* ---- straighten: every meeting a node, then each staircase one line ---- */
|
|
307
|
-
cutStairs(store, whiskers, cfg.stub / 2,
|
|
308
|
-
// Over cells the router walks, so the line keeps a person's clearance.
|
|
309
|
-
(seg) => mayLay(seg) && router.cellsAlong(...seg).every((cell) => router.ok[cell] === 1), (stub, at) => dockFor(destinationOf.get(stub.id), stub.way, at, blockers) !== null);
|
|
310
|
-
turnDoorwayDocks(whiskers, build, store);
|
|
311
|
-
/* ---- emit the RouteNetwork artifact ---- */
|
|
312
|
-
const emitted = emitNetwork({ ...build, store, whiskers, destinations, failed, doors });
|
|
313
|
-
const ways = [];
|
|
314
|
-
for (const [id, list] of plan.ways)
|
|
315
|
-
for (const way of list)
|
|
316
|
-
ways.push({ id, kind: way.kind, foot: [way.mx, way.my], normal: [way.nx, way.ny] });
|
|
317
|
-
return {
|
|
318
|
-
paths: emitted.paths,
|
|
319
|
-
docks: emitted.docks,
|
|
320
|
-
anatomy: {
|
|
321
|
-
doors: doors.map((door) => ({
|
|
322
|
-
gap: [
|
|
323
|
-
[door.gap[0], door.gap[1]],
|
|
324
|
-
[door.gap[2], door.gap[3]],
|
|
325
|
-
],
|
|
326
|
-
mid: [door.mid[0], door.mid[1]],
|
|
327
|
-
})),
|
|
328
|
-
anchors,
|
|
329
|
-
ways,
|
|
330
|
-
whiskers: whiskers.map((stub) => ({
|
|
331
|
-
id: stub.id,
|
|
332
|
-
kind: stub.kind,
|
|
333
|
-
foot: [stub.mx, stub.my],
|
|
334
|
-
tip: [stub.tx, stub.ty],
|
|
335
|
-
})),
|
|
336
|
-
frames: [],
|
|
337
|
-
loose: [],
|
|
338
|
-
axes: [],
|
|
339
|
-
},
|
|
340
|
-
stats: {
|
|
341
|
-
unreached: emitted.unreached,
|
|
342
|
-
components: emitted.components,
|
|
343
|
-
doorways: doors.length,
|
|
344
|
-
shortcuts: linked,
|
|
345
|
-
...(trees > 1 && { untiedPieces: trees - 1 }),
|
|
346
|
-
...(unservedFloorPieces > 0 && { unservedFloorPieces }),
|
|
347
|
-
},
|
|
348
|
-
};
|
|
349
|
-
}
|
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
import type { Lattice, RouteNetworkDock } from './graph-types.js';
|
|
2
|
-
import type { WfGridFloor } from './types.js';
|
|
3
|
-
/**
|
|
4
|
-
* Bytes packed as runs: the value, then the run's length as a little-endian base-128 number, and
|
|
5
|
-
* the whole as base64. A floor's map is long stretches of one value.
|
|
6
|
-
* @param bytes The bytes.
|
|
7
|
-
* @returns The packed text.
|
|
8
|
-
*/
|
|
9
|
-
export declare function packRuns(bytes: Uint8Array): string;
|
|
10
|
-
/**
|
|
11
|
-
* One floor's lattice and docks as the map handed to the runtime, in plan units.
|
|
12
|
-
* @param layer The floor's layer id.
|
|
13
|
-
* @param lattice The lattice, in meters.
|
|
14
|
-
* @param docks The docks leaving it, in meters, in the lattice's own order.
|
|
15
|
-
* @param unitsPerMeter Plan coordinates in one meter.
|
|
16
|
-
* @returns The floor's map.
|
|
17
|
-
*/
|
|
18
|
-
export declare function latticeMap(layer: string, lattice: Lattice, docks: readonly RouteNetworkDock[], unitsPerMeter: number): WfGridFloor;
|
|
19
|
-
//# sourceMappingURL=lattice-map.d.ts.map
|
package/dist/lib/lattice-map.js
DELETED
|
@@ -1,92 +0,0 @@
|
|
|
1
|
-
// The lattice as it is handed out: a map of each floor in the plan's own units — where the grid
|
|
2
|
-
// stands, how its cells are spaced, a byte of links per cell — and the docks that leave it.
|
|
3
|
-
import { PLAN_DECIMALS } from './scale.js';
|
|
4
|
-
/** How near a blocker a route starts to pay more to walk, in meters: a wide aisle's half. */
|
|
5
|
-
const NEAR = 1.5;
|
|
6
|
-
const ALPHABET = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/';
|
|
7
|
-
/**
|
|
8
|
-
* Bytes as base64 text.
|
|
9
|
-
* @param bytes The bytes.
|
|
10
|
-
* @returns The text, padded.
|
|
11
|
-
*/
|
|
12
|
-
function toBase64(bytes) {
|
|
13
|
-
const chunks = [];
|
|
14
|
-
let chunk = '';
|
|
15
|
-
for (let i = 0; i < bytes.length; i += 3) {
|
|
16
|
-
const triple = (bytes[i] << 16) | ((bytes[i + 1] ?? 0) << 8) | (bytes[i + 2] ?? 0);
|
|
17
|
-
chunk +=
|
|
18
|
-
ALPHABET[triple >> 18] +
|
|
19
|
-
ALPHABET[(triple >> 12) & 63] +
|
|
20
|
-
(i + 1 < bytes.length ? ALPHABET[(triple >> 6) & 63] : '=') +
|
|
21
|
-
(i + 2 < bytes.length ? ALPHABET[triple & 63] : '=');
|
|
22
|
-
// In pieces: one string grown a character at a time over a whole floor is slow to build.
|
|
23
|
-
if (chunk.length >= 8192) {
|
|
24
|
-
chunks.push(chunk);
|
|
25
|
-
chunk = '';
|
|
26
|
-
}
|
|
27
|
-
}
|
|
28
|
-
return chunks.join('') + chunk;
|
|
29
|
-
}
|
|
30
|
-
/**
|
|
31
|
-
* Bytes packed as runs: the value, then the run's length as a little-endian base-128 number, and
|
|
32
|
-
* the whole as base64. A floor's map is long stretches of one value.
|
|
33
|
-
* @param bytes The bytes.
|
|
34
|
-
* @returns The packed text.
|
|
35
|
-
*/
|
|
36
|
-
export function packRuns(bytes) {
|
|
37
|
-
const runs = [];
|
|
38
|
-
for (let start = 0; start < bytes.length;) {
|
|
39
|
-
let end = start + 1;
|
|
40
|
-
while (end < bytes.length && bytes[end] === bytes[start])
|
|
41
|
-
end++;
|
|
42
|
-
runs.push(bytes[start]);
|
|
43
|
-
for (let length = end - start;; length >>>= 7) {
|
|
44
|
-
if (length < 128) {
|
|
45
|
-
runs.push(length);
|
|
46
|
-
break;
|
|
47
|
-
}
|
|
48
|
-
runs.push((length & 127) | 128);
|
|
49
|
-
}
|
|
50
|
-
start = end;
|
|
51
|
-
}
|
|
52
|
-
return toBase64(Uint8Array.from(runs));
|
|
53
|
-
}
|
|
54
|
-
/**
|
|
55
|
-
* One floor's lattice and docks as the map handed to the runtime, in plan units.
|
|
56
|
-
* @param layer The floor's layer id.
|
|
57
|
-
* @param lattice The lattice, in meters.
|
|
58
|
-
* @param docks The docks leaving it, in meters, in the lattice's own order.
|
|
59
|
-
* @param unitsPerMeter Plan coordinates in one meter.
|
|
60
|
-
* @returns The floor's map.
|
|
61
|
-
*/
|
|
62
|
-
export function latticeMap(layer, lattice, docks, unitsPerMeter) {
|
|
63
|
-
const at = (v) => +(v * unitsPerMeter).toFixed(PLAN_DECIMALS);
|
|
64
|
-
const nodeX = (cell) => lattice.origin[0] + (cell % lattice.width) * lattice.pitch;
|
|
65
|
-
const nodeY = (cell) => lattice.origin[1] + Math.floor(cell / lattice.width) * lattice.pitch;
|
|
66
|
-
const turns = [];
|
|
67
|
-
docks.forEach((dock, index) => {
|
|
68
|
-
const cell = lattice.dockCells[index];
|
|
69
|
-
// A dock that leaves from elsewhere than its node turns there on its way: a doorway's middle.
|
|
70
|
-
if (at(dock.from[0]) !== at(nodeX(cell)) || at(dock.from[1]) !== at(nodeY(cell)))
|
|
71
|
-
turns.push(index, at(dock.from[0]), at(dock.from[1]));
|
|
72
|
-
});
|
|
73
|
-
return {
|
|
74
|
-
layer,
|
|
75
|
-
x: at(lattice.origin[0]),
|
|
76
|
-
y: at(lattice.origin[1]),
|
|
77
|
-
pitch: +(lattice.pitch * unitsPerMeter).toFixed(6),
|
|
78
|
-
width: lattice.width,
|
|
79
|
-
height: lattice.height,
|
|
80
|
-
links: packRuns(lattice.links),
|
|
81
|
-
near: +(NEAR * unitsPerMeter).toFixed(6),
|
|
82
|
-
...(lattice.sparseFrom !== undefined && {
|
|
83
|
-
sparseFrom: +(lattice.sparseFrom * unitsPerMeter).toFixed(6),
|
|
84
|
-
}),
|
|
85
|
-
docks: docks.flatMap((dock, index) => [
|
|
86
|
-
at(dock.to[0]),
|
|
87
|
-
at(dock.to[1]),
|
|
88
|
-
lattice.dockCells[index],
|
|
89
|
-
]),
|
|
90
|
-
...(turns.length && { turns }),
|
|
91
|
-
};
|
|
92
|
-
}
|
|
@@ -1,32 +0,0 @@
|
|
|
1
|
-
import type { Ring } from './geom/types.js';
|
|
2
|
-
import type { PreparedWalls } from './geom/walls.js';
|
|
3
|
-
import { type NetworkVariant, type RouteNetwork, type RouteNetworkDestination } from './graph-types.js';
|
|
4
|
-
import type { Opening } from './openings.js';
|
|
5
|
-
/** How a lattice is spaced. */
|
|
6
|
-
export interface LatticeSpacing {
|
|
7
|
-
/** Distance between neighboring nodes, in meters. */
|
|
8
|
-
readonly pitch: number;
|
|
9
|
-
/**
|
|
10
|
-
* How far from the nearest cell nobody can stand in open floor begins, in meters: there the
|
|
11
|
-
* reader stands its nodes two cells apart. Absent, a node stands in every cell.
|
|
12
|
-
*/
|
|
13
|
-
readonly sparseFrom?: number;
|
|
14
|
-
}
|
|
15
|
-
/** The spacing of each lattice network. */
|
|
16
|
-
export declare const LATTICE_SPACING: Readonly<Record<Exclude<NetworkVariant, 'steiner'>, LatticeSpacing>>;
|
|
17
|
-
/**
|
|
18
|
-
* Builds the lattice route network for one layer.
|
|
19
|
-
* @param walkableRings The layer's walkable polygon (outer rings + holes).
|
|
20
|
-
* @param destinations Footprints the network must reach.
|
|
21
|
-
* @param walls Raw wall polylines as flat `[x0, y0, …]` runs, for exact thin-wall blocking.
|
|
22
|
-
* @param prepared The same walls already prepared, when the caller has them.
|
|
23
|
-
* @param drawn The rest of each destination's drawing; ways in may not cross it.
|
|
24
|
-
* @param openings The openings the entrances make: the ways in they own, through the walls.
|
|
25
|
-
* @param spacing How the lattice is spaced.
|
|
26
|
-
* @returns The docks, the lattice they stand on, and stats.
|
|
27
|
-
*/
|
|
28
|
-
export declare function buildLatticeNetwork(walkableRings: readonly Ring[], destinations: readonly RouteNetworkDestination[], walls: readonly (readonly number[])[], prepared: PreparedWalls | undefined, drawn: readonly {
|
|
29
|
-
readonly id: string;
|
|
30
|
-
readonly ring: Ring;
|
|
31
|
-
}[], openings: readonly Opening[], spacing: LatticeSpacing): RouteNetwork;
|
|
32
|
-
//# sourceMappingURL=lattice-network.d.ts.map
|