@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/floor-grids.js
DELETED
|
@@ -1,185 +0,0 @@
|
|
|
1
|
-
// The floor a layer's network grows over: the walkable pieces a route can serve, and one router grid
|
|
2
|
-
// per direction the destinations are drawn in — the plan's over the whole floor, a turned direction's
|
|
3
|
-
// over the halls turned its way.
|
|
4
|
-
import { rayHit } from './doorways.js';
|
|
5
|
-
import { frameRunning, framesOf, PLAN, toFrame, turnedInto } from './geom/frames.js';
|
|
6
|
-
import { boundsOf, boxesOverlap, outerRingsOf, pointInRing, signedArea, vertexMean, } from './geom/primitives.js';
|
|
7
|
-
import { NODE_SNAP } from './graph-types.js';
|
|
8
|
-
import { buildMaskCtx, openDoorMiddle } from './mask.js';
|
|
9
|
-
import { MIN_PASS } from './network-sizes.js';
|
|
10
|
-
import { standsOn, WALL_HALF_WIDTH } from './walkable.js';
|
|
11
|
-
/**
|
|
12
|
-
* The floor pieces a route can serve: an outer ring (with its holes) is kept when a destination or
|
|
13
|
-
* transition stop stands in it, or a doorway into a destination opens onto it.
|
|
14
|
-
* @param rings Walkable outer rings and holes (holes wound the other way).
|
|
15
|
-
* @param destinations The layer's destinations, transition stops included.
|
|
16
|
-
* @param owners Doorway sides by the destination they open into.
|
|
17
|
-
* @returns The rings kept, and how many outer rings were left out.
|
|
18
|
-
*/
|
|
19
|
-
export function servedFloor(rings, destinations, owners) {
|
|
20
|
-
const pieces = outerRingsOf(rings);
|
|
21
|
-
const reach = WALL_HALF_WIDTH + MIN_PASS;
|
|
22
|
-
const atDoors = [];
|
|
23
|
-
for (const sides of owners.values())
|
|
24
|
-
for (const { door } of sides)
|
|
25
|
-
for (const s of [1, -1])
|
|
26
|
-
atDoors.push([door.mid[0] + door.nx * s * reach, door.mid[1] + door.ny * s * reach]);
|
|
27
|
-
// A destination stands on a piece within `NODE_SNAP` of it at most: boxes apart rule the pair out
|
|
28
|
-
// before the piece's contour is walked.
|
|
29
|
-
const standing = destinations.map((d) => {
|
|
30
|
-
const ring = d.dock ?? d.polygon;
|
|
31
|
-
const [x0, y0, x1, y1] = boundsOf(ring);
|
|
32
|
-
return { ring, box: [x0 - NODE_SNAP, y0 - NODE_SNAP, x1 + NODE_SNAP, y1 + NODE_SNAP] };
|
|
33
|
-
});
|
|
34
|
-
const served = new Set(pieces.filter((piece) => {
|
|
35
|
-
const pieceBox = boundsOf(piece);
|
|
36
|
-
return (standing.some(({ ring, box }) => boxesOverlap(pieceBox, box) && standsOn(piece, ring)) ||
|
|
37
|
-
atDoors.some((p) => pointInRing(piece, p)));
|
|
38
|
-
}));
|
|
39
|
-
const kept = rings.filter((ring) => {
|
|
40
|
-
if (pieces.includes(ring))
|
|
41
|
-
return served.has(ring);
|
|
42
|
-
// A hole belongs to the smallest piece around it.
|
|
43
|
-
const around = pieces
|
|
44
|
-
.filter((piece) => pointInRing(piece, ring[0]))
|
|
45
|
-
.sort((a, b) => Math.abs(signedArea(a)) - Math.abs(signedArea(b)))[0];
|
|
46
|
-
return around !== undefined && served.has(around);
|
|
47
|
-
});
|
|
48
|
-
return { rings: kept, dropped: pieces.length - served.size };
|
|
49
|
-
}
|
|
50
|
-
/**
|
|
51
|
-
* Groups rings whose bounds, grown by `reach`, meet.
|
|
52
|
-
* @param rings The rings.
|
|
53
|
-
* @param reach Growth of each ring's box.
|
|
54
|
-
* @returns Each group's box and its ring indices.
|
|
55
|
-
*/
|
|
56
|
-
function groupedBoxes(rings, reach) {
|
|
57
|
-
const groups = [];
|
|
58
|
-
rings.forEach((ring, index) => {
|
|
59
|
-
const [x0, y0, x1, y1] = boundsOf(ring);
|
|
60
|
-
let box = [x0 - reach, y0 - reach, x1 + reach, y1 + reach];
|
|
61
|
-
let members = [index];
|
|
62
|
-
let merged = true;
|
|
63
|
-
// Merge until stable: the grown box may meet groups it missed before.
|
|
64
|
-
while (merged) {
|
|
65
|
-
merged = false;
|
|
66
|
-
for (let i = groups.length - 1; i >= 0; i--)
|
|
67
|
-
if (boxesOverlap(box, groups[i].box)) {
|
|
68
|
-
const g = groups.splice(i, 1)[0];
|
|
69
|
-
box = [
|
|
70
|
-
Math.min(box[0], g.box[0]),
|
|
71
|
-
Math.min(box[1], g.box[1]),
|
|
72
|
-
Math.max(box[2], g.box[2]),
|
|
73
|
-
Math.max(box[3], g.box[3]),
|
|
74
|
-
];
|
|
75
|
-
members = [...members, ...g.members];
|
|
76
|
-
merged = true;
|
|
77
|
-
}
|
|
78
|
-
}
|
|
79
|
-
groups.push({ box, members });
|
|
80
|
-
});
|
|
81
|
-
return groups;
|
|
82
|
-
}
|
|
83
|
-
/**
|
|
84
|
-
* Whether a group stands in a hall turned its way: of the walls seen from its stands — the first wall
|
|
85
|
-
* along each of the direction's four ways from each stand's middle, however far — more length runs along
|
|
86
|
-
* its direction than along the plan's axes, so the long walls that make the hall outweigh a column or a
|
|
87
|
-
* jamb. Each wall is read as the raster can tell it over its length; one too short to tell the two apart
|
|
88
|
-
* counts for neither.
|
|
89
|
-
* @param stands The group's rings.
|
|
90
|
-
* @param frame The direction.
|
|
91
|
-
* @param walls The layer's wall segments.
|
|
92
|
-
* @param cfg Network sizes.
|
|
93
|
-
* @returns True for a turned hall.
|
|
94
|
-
*/
|
|
95
|
-
function turnedHall(stands, frame, walls, cfg) {
|
|
96
|
-
const ways = [
|
|
97
|
-
[frame.cos, frame.sin],
|
|
98
|
-
[-frame.cos, -frame.sin],
|
|
99
|
-
[-frame.sin, frame.cos],
|
|
100
|
-
[frame.sin, -frame.cos],
|
|
101
|
-
];
|
|
102
|
-
let own = 0;
|
|
103
|
-
let square = 0;
|
|
104
|
-
for (const ring of stands) {
|
|
105
|
-
const middle = vertexMean(ring);
|
|
106
|
-
for (const [dx, dy] of ways) {
|
|
107
|
-
const met = rayHit(middle, dx, dy, walls);
|
|
108
|
-
if (!met)
|
|
109
|
-
continue;
|
|
110
|
-
const [ax, ay, bx, by] = walls[met.index];
|
|
111
|
-
const [wx, wy] = [bx - ax, by - ay];
|
|
112
|
-
const length = Math.hypot(wx, wy);
|
|
113
|
-
const along = frameRunning([frame], wx, wy, cfg.cs, length) === 0;
|
|
114
|
-
const axial = frameRunning([PLAN], wx, wy, cfg.cs, length) === 0;
|
|
115
|
-
if (along && !axial)
|
|
116
|
-
own += length;
|
|
117
|
-
else if (axial && !along)
|
|
118
|
-
square += length;
|
|
119
|
-
}
|
|
120
|
-
}
|
|
121
|
-
return own > square;
|
|
122
|
-
}
|
|
123
|
-
/**
|
|
124
|
-
* One grid per direction the destinations are drawn in, walked as one raster, so a turned hall's
|
|
125
|
-
* aisles run straight on its own grid. The plan's grid covers the whole floor; a turned direction's
|
|
126
|
-
* grid covers its groups of two or more destinations whose reaches (a stub around each) meet and
|
|
127
|
-
* which stand in a hall turned their way. A lone turned stand, or stands turned in a hall drawn
|
|
128
|
-
* square, get no grid. Every doorway's throat is passable at its middle on every grid, however
|
|
129
|
-
* narrow the gap: routes and lines pass there, and only there; the jambs stay closed.
|
|
130
|
-
* @param floor The floor pieces a route can serve.
|
|
131
|
-
* @param doorSides The closed sides of the doorways, masked like walls.
|
|
132
|
-
* @param destinations The layer's destinations.
|
|
133
|
-
* @param wallLines The layer's wall segments.
|
|
134
|
-
* @param doors The layer's doorways.
|
|
135
|
-
* @param cfg Network sizes.
|
|
136
|
-
* @returns The grids, their directions, and each destination's direction.
|
|
137
|
-
*/
|
|
138
|
-
export function floorGrids(floor, doorSides, destinations, wallLines, doors, cfg) {
|
|
139
|
-
// Masked like walls: a two-point ring fills nothing but closes every cell it crosses.
|
|
140
|
-
const masked = [
|
|
141
|
-
...floor,
|
|
142
|
-
...doorSides.map(([x0, y0, x1, y1]) => [
|
|
143
|
-
[x0, y0],
|
|
144
|
-
[x1, y1],
|
|
145
|
-
]),
|
|
146
|
-
];
|
|
147
|
-
const directions = framesOf(destinations.map((d) => d.dock ?? d.polygon), cfg.cs);
|
|
148
|
-
// Each destination's direction. One without a frame is reached on the plan's grid, gets no
|
|
149
|
-
// framing, and holds every line at its tips.
|
|
150
|
-
const drawnIn = new Map();
|
|
151
|
-
const grids = [{ frame: directions[0].frame, ctx: buildMaskCtx(masked, cfg) }];
|
|
152
|
-
for (const ring of directions[0].rings)
|
|
153
|
-
drawnIn.set(ring, directions[0].frame);
|
|
154
|
-
for (const { frame, rings } of directions.slice(1)) {
|
|
155
|
-
const groups = groupedBoxes(turnedInto(rings, frame), cfg.stub).filter((group) => group.members.length >= 2 &&
|
|
156
|
-
turnedHall(group.members.map((i) => rings[i]), frame, wallLines, cfg));
|
|
157
|
-
if (!groups.length)
|
|
158
|
-
continue;
|
|
159
|
-
// One grid over all the direction's groups, so aisles between halls turned alike follow it too.
|
|
160
|
-
const box = groups.reduce((b, { box: g }) => [
|
|
161
|
-
Math.min(b[0], g[0]),
|
|
162
|
-
Math.min(b[1], g[1]),
|
|
163
|
-
Math.max(b[2], g[2]),
|
|
164
|
-
Math.max(b[3], g[3]),
|
|
165
|
-
], groups[0].box);
|
|
166
|
-
grids.push({ frame, ctx: buildMaskCtx(turnedInto(masked, frame), cfg, box) });
|
|
167
|
-
for (const group of groups)
|
|
168
|
-
for (const i of group.members)
|
|
169
|
-
drawnIn.set(rings[i], frame);
|
|
170
|
-
}
|
|
171
|
-
const frameOfDest = (d) => drawnIn.get(d.dock ?? d.polygon);
|
|
172
|
-
// The directions that got a grid; every pass works in these.
|
|
173
|
-
const frames = [...new Set(grids.map((grid) => grid.frame))];
|
|
174
|
-
// Every doorway's throat is passable at its middle, however narrow the gap: routes and lines pass
|
|
175
|
-
// there, and only there. The jambs stay closed.
|
|
176
|
-
for (const door of doors) {
|
|
177
|
-
const width = Math.hypot(door.gap[2] - door.gap[0], door.gap[3] - door.gap[1]);
|
|
178
|
-
const throat = Math.min(MIN_PASS + cfg.cs, width / 2 - WALL_HALF_WIDTH);
|
|
179
|
-
if (throat <= 0)
|
|
180
|
-
continue;
|
|
181
|
-
for (const { frame, ctx: grid } of grids)
|
|
182
|
-
openDoorMiddle(grid, ...toFrame(frame, door.mid[0], door.mid[1]), throat);
|
|
183
|
-
}
|
|
184
|
-
return { grids, frames, frameOfDest };
|
|
185
|
-
}
|
package/dist/lib/framing.d.ts
DELETED
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
import { type AisleAxis } from './axes.js';
|
|
2
|
-
import { type Frame } from './geom/frames.js';
|
|
3
|
-
import type { Segment } from './geom/types.js';
|
|
4
|
-
import { type RouteNetworkDestination } from './graph-types.js';
|
|
5
|
-
import type { NetworkStore } from './network-store.js';
|
|
6
|
-
import type { Stub } from './stubs.js';
|
|
7
|
-
/**
|
|
8
|
-
* Framing, per direction: the row along each aisle's axis through its tips, and each destination's
|
|
9
|
-
* tips chained into a perimeter (destinations with two or more stubs).
|
|
10
|
-
* @param store The network; extended in place.
|
|
11
|
-
* @param stubs The stubs of destinations that get framing.
|
|
12
|
-
* @param destinations Their footprints, for the perimeter's angular order.
|
|
13
|
-
* @param mayLay Whether a link may be laid.
|
|
14
|
-
* @param frames The directions the layer is drawn in.
|
|
15
|
-
* @param axisOf Each stub's aisle axis.
|
|
16
|
-
* @param cs The raster cell: what it cannot tell apart lies on one line.
|
|
17
|
-
* @param snap A corridor's width: an aisle's axis nearer than that to a rung's line joins its stubs itself.
|
|
18
|
-
*/
|
|
19
|
-
export declare function frameTips(store: NetworkStore, stubs: readonly Stub[], destinations: readonly RouteNetworkDestination[], mayLay: (seg: Segment) => boolean, frames: readonly Frame[], axisOf: ReadonlyMap<Stub, AisleAxis>, cs: number, snap: number): void;
|
|
20
|
-
//# sourceMappingURL=framing.d.ts.map
|
package/dist/lib/framing.js
DELETED
|
@@ -1,187 +0,0 @@
|
|
|
1
|
-
// Framing: the row along each aisle's axis through its tips, the rungs between stubs facing each
|
|
2
|
-
// other across an aisle, and each destination's tips chained into a perimeter with square corners.
|
|
3
|
-
import { aisleAxes, tipInFrame } from './axes.js';
|
|
4
|
-
import { placeIn, toFrame } from './geom/frames.js';
|
|
5
|
-
import { vertexMean } from './geom/primitives.js';
|
|
6
|
-
import { NODE_SNAP } from './graph-types.js';
|
|
7
|
-
/**
|
|
8
|
-
* Framing, per direction: the row along each aisle's axis through its tips, and each destination's
|
|
9
|
-
* tips chained into a perimeter (destinations with two or more stubs).
|
|
10
|
-
* @param store The network; extended in place.
|
|
11
|
-
* @param stubs The stubs of destinations that get framing.
|
|
12
|
-
* @param destinations Their footprints, for the perimeter's angular order.
|
|
13
|
-
* @param mayLay Whether a link may be laid.
|
|
14
|
-
* @param frames The directions the layer is drawn in.
|
|
15
|
-
* @param axisOf Each stub's aisle axis.
|
|
16
|
-
* @param cs The raster cell: what it cannot tell apart lies on one line.
|
|
17
|
-
* @param snap A corridor's width: an aisle's axis nearer than that to a rung's line joins its stubs itself.
|
|
18
|
-
*/
|
|
19
|
-
export function frameTips(store, stubs, destinations, mayLay, frames, axisOf, cs, snap) {
|
|
20
|
-
for (const frame of frames)
|
|
21
|
-
frameTipsIn(store, stubs.filter((s) => s.frame === frame), destinations, mayLay, frame, axisOf, cs, snap);
|
|
22
|
-
}
|
|
23
|
-
/**
|
|
24
|
-
* {@link frameTips} in one frame: coordinates are read and links laid along its axes.
|
|
25
|
-
* @param store The network; extended in place.
|
|
26
|
-
* @param stubs The stubs of this direction.
|
|
27
|
-
* @param destinations Their footprints.
|
|
28
|
-
* @param mayLay Whether a link may be laid.
|
|
29
|
-
* @param frame The frame.
|
|
30
|
-
* @param axisOf Each stub's aisle axis.
|
|
31
|
-
* @param cs The raster cell: what it cannot tell apart lies on one line.
|
|
32
|
-
* @param snap A corridor's width: an aisle's axis nearer than that to a rung's line joins its stubs itself.
|
|
33
|
-
*/
|
|
34
|
-
function frameTipsIn(store, stubs, destinations, mayLay, frame, axisOf, cs, snap) {
|
|
35
|
-
const read = (x, y) => toFrame(frame, x, y);
|
|
36
|
-
const axes = [...new Set(axisOf.values())].filter((axis) => axis.frame === frame);
|
|
37
|
-
const place = (u, v) => placeIn(frame, u, v);
|
|
38
|
-
const free = (u0, v0, u1, v1) => mayLay([...place(u0, v0), ...place(u1, v1)]);
|
|
39
|
-
const addLink = (u0, v0, u1, v1) => {
|
|
40
|
-
if (Math.hypot(u1 - u0, v1 - v0) < 0.3)
|
|
41
|
-
return true;
|
|
42
|
-
if (!free(u0, v0, u1, v1))
|
|
43
|
-
return false;
|
|
44
|
-
store.add(...place(u0, v0), ...place(u1, v1));
|
|
45
|
-
return true;
|
|
46
|
-
};
|
|
47
|
-
// Rows: each aisle's line through its tips, laid between neighbors along it.
|
|
48
|
-
const byAxis = new Map();
|
|
49
|
-
/** Along span each axis's row was laid over: tips joined later widen it. */
|
|
50
|
-
const laidSpan = new Map();
|
|
51
|
-
for (const s of stubs) {
|
|
52
|
-
const axis = axisOf.get(s);
|
|
53
|
-
if (!axis)
|
|
54
|
-
continue;
|
|
55
|
-
const list = byAxis.get(axis);
|
|
56
|
-
if (list)
|
|
57
|
-
list.push(s);
|
|
58
|
-
else
|
|
59
|
-
byAxis.set(axis, [s]);
|
|
60
|
-
}
|
|
61
|
-
for (const [axis, list] of byAxis) {
|
|
62
|
-
const pts = list
|
|
63
|
-
.map((s) => {
|
|
64
|
-
const [u, v] = read(s.tx, s.ty);
|
|
65
|
-
return axis.horiz ? { along: u, c: v } : { along: v, c: u };
|
|
66
|
-
})
|
|
67
|
-
.sort((p, q) => p.along - q.along);
|
|
68
|
-
const at = (along, c) => axis.horiz ? [along, c] : [c, along];
|
|
69
|
-
if (pts.length >= 2)
|
|
70
|
-
laidSpan.set(axis, [pts[0].along, pts[pts.length - 1].along]);
|
|
71
|
-
for (let i = 0; i + 1 < pts.length; i++) {
|
|
72
|
-
const a = pts[i];
|
|
73
|
-
const b = pts[i + 1];
|
|
74
|
-
const gap = b.along - a.along;
|
|
75
|
-
if (gap <= NODE_SNAP)
|
|
76
|
-
continue;
|
|
77
|
-
if (!addLink(...at(a.along, axis.c), ...at(b.along, axis.c)))
|
|
78
|
-
continue;
|
|
79
|
-
// A tip that could not reach the axis gets a leg to it.
|
|
80
|
-
if (Math.abs(a.c - axis.c) > NODE_SNAP)
|
|
81
|
-
addLink(...at(a.along, a.c), ...at(a.along, axis.c));
|
|
82
|
-
if (Math.abs(b.c - axis.c) > NODE_SNAP)
|
|
83
|
-
addLink(...at(b.along, b.c), ...at(b.along, axis.c));
|
|
84
|
-
}
|
|
85
|
-
}
|
|
86
|
-
// Rungs: stubs that face each other on one line across an aisle are joined straight — the shortest
|
|
87
|
-
// way between the two stands, and the line both stubs run along. An aisle's axis running their way
|
|
88
|
-
// within a corridor's width hosts the rung: it lays onto that line (legs bring the tips over), and
|
|
89
|
-
// only the parts its row has not laid yet — one line per aisle, never a second one beside it.
|
|
90
|
-
const rungs = new Map();
|
|
91
|
-
for (const s of stubs) {
|
|
92
|
-
const { horiz, c, along } = tipInFrame(frame, s.tx, s.ty, s.nx, s.ny);
|
|
93
|
-
const [nu, nv] = read(s.nx, s.ny);
|
|
94
|
-
const list = rungs.get(!horiz) ?? [];
|
|
95
|
-
// On the line along the stub: across is where the stub stands, along is where its tip is.
|
|
96
|
-
list.push({ stub: s, c: along, along: c, out: Math.sign(horiz ? nv : nu) });
|
|
97
|
-
rungs.set(!horiz, list);
|
|
98
|
-
}
|
|
99
|
-
for (const [horiz, tips] of rungs)
|
|
100
|
-
for (const line of aisleAxes(tips, cs, 0, () => 0)) {
|
|
101
|
-
const at = (along, c) => (horiz ? [along, c] : [c, along]);
|
|
102
|
-
const hosts = axes
|
|
103
|
-
.filter((axis) => axis.horiz === horiz && Math.abs(axis.c - line.c) <= snap)
|
|
104
|
-
.sort((p, q) => Math.abs(p.c - line.c) - Math.abs(q.c - line.c));
|
|
105
|
-
const host = hosts[0];
|
|
106
|
-
const c = host ? host.c : line.c;
|
|
107
|
-
for (let i = 0; i + 1 < line.tips.length; i++) {
|
|
108
|
-
const a = line.tips[i];
|
|
109
|
-
const b = line.tips[i + 1];
|
|
110
|
-
const gap = b.along - a.along;
|
|
111
|
-
// Facing: each stub points at the other.
|
|
112
|
-
if (a.out <= 0 || b.out >= 0 || gap <= NODE_SNAP)
|
|
113
|
-
continue;
|
|
114
|
-
const row = host && laidSpan.get(host);
|
|
115
|
-
// The parts the host's row has not laid yet.
|
|
116
|
-
const parts = [];
|
|
117
|
-
if (row && row[0] < b.along && a.along < row[1]) {
|
|
118
|
-
if (a.along < row[0])
|
|
119
|
-
parts.push([a.along, row[0]]);
|
|
120
|
-
if (row[1] < b.along)
|
|
121
|
-
parts.push([row[1], b.along]);
|
|
122
|
-
}
|
|
123
|
-
else
|
|
124
|
-
parts.push([a.along, b.along]);
|
|
125
|
-
let laid = true;
|
|
126
|
-
for (const [from, to] of parts)
|
|
127
|
-
if (to - from > NODE_SNAP)
|
|
128
|
-
laid = addLink(...at(from, c), ...at(to, c)) && laid;
|
|
129
|
-
if (!laid)
|
|
130
|
-
continue;
|
|
131
|
-
if (host)
|
|
132
|
-
laidSpan.set(host, [
|
|
133
|
-
Math.min(row ? row[0] : a.along, a.along),
|
|
134
|
-
Math.max(row ? row[1] : b.along, b.along),
|
|
135
|
-
]);
|
|
136
|
-
if (Math.abs(a.c - c) > NODE_SNAP)
|
|
137
|
-
addLink(...at(a.along, a.c), ...at(a.along, c));
|
|
138
|
-
if (Math.abs(b.c - c) > NODE_SNAP)
|
|
139
|
-
addLink(...at(b.along, b.c), ...at(b.along, c));
|
|
140
|
-
}
|
|
141
|
-
}
|
|
142
|
-
// Perimeter: chain each destination's tips in angular order, with axis L-corners.
|
|
143
|
-
const byPoi = new Map();
|
|
144
|
-
for (const w of stubs) {
|
|
145
|
-
const [qx, qy] = read(w.tx, w.ty);
|
|
146
|
-
// Direction from the face normal: a short stub's rounded run would be zero.
|
|
147
|
-
const [nu, nv] = read(w.nx, w.ny);
|
|
148
|
-
if (!byPoi.has(w.id))
|
|
149
|
-
byPoi.set(w.id, []);
|
|
150
|
-
byPoi.get(w.id).push({
|
|
151
|
-
qx,
|
|
152
|
-
qy,
|
|
153
|
-
nx: Math.round(nu),
|
|
154
|
-
ny: Math.round(nv),
|
|
155
|
-
});
|
|
156
|
-
}
|
|
157
|
-
const destById = new Map(destinations.map((d) => [d.id, d]));
|
|
158
|
-
for (const [id, tips] of byPoi) {
|
|
159
|
-
if (tips.length < 2)
|
|
160
|
-
continue;
|
|
161
|
-
const d = destById.get(id);
|
|
162
|
-
if (!d)
|
|
163
|
-
continue;
|
|
164
|
-
const [cx, cy] = read(...vertexMean(d.polygon));
|
|
165
|
-
tips.sort((a, b) => Math.atan2(a.qy - cy, a.qx - cx) - Math.atan2(b.qy - cy, b.qx - cx));
|
|
166
|
-
for (let i = 0; i < tips.length; i++) {
|
|
167
|
-
if (tips.length === 2 && i === 1)
|
|
168
|
-
break;
|
|
169
|
-
const a = tips[i];
|
|
170
|
-
const b = tips[(i + 1) % tips.length];
|
|
171
|
-
if (Math.abs(a.qx - b.qx) <= cs || Math.abs(a.qy - b.qy) <= cs) {
|
|
172
|
-
addLink(a.qx, a.qy, b.qx, b.qy);
|
|
173
|
-
continue;
|
|
174
|
-
}
|
|
175
|
-
const hTip = a.ny !== 0 ? a : b.ny !== 0 ? b : null;
|
|
176
|
-
const vTip = a.nx !== 0 ? a : b.nx !== 0 ? b : null;
|
|
177
|
-
if (!hTip || !vTip || hTip === vTip)
|
|
178
|
-
continue;
|
|
179
|
-
if (!free(hTip.qx, hTip.qy, vTip.qx, hTip.qy))
|
|
180
|
-
continue;
|
|
181
|
-
if (!free(vTip.qx, hTip.qy, vTip.qx, vTip.qy))
|
|
182
|
-
continue;
|
|
183
|
-
addLink(hTip.qx, hTip.qy, vTip.qx, hTip.qy);
|
|
184
|
-
addLink(vTip.qx, hTip.qy, vTip.qx, vTip.qy);
|
|
185
|
-
}
|
|
186
|
-
}
|
|
187
|
-
}
|
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
import type { Ring } from './types.js';
|
|
2
|
-
/**
|
|
3
|
-
* Polygon boolean operations the compiler needs, so the backend is one swappable adapter. Rings are in
|
|
4
|
-
* meters; the adapter owns any integer scaling.
|
|
5
|
-
*/
|
|
6
|
-
export interface BooleanOps {
|
|
7
|
-
/** Union of all rings into non-overlapping outers and holes (NonZero). Feed positively wound rings. */
|
|
8
|
-
union(rings: readonly Ring[]): Ring[];
|
|
9
|
-
/** `subject − clip` (NonZero). */
|
|
10
|
-
difference(subject: readonly Ring[], clip: readonly Ring[]): Ring[];
|
|
11
|
-
/** `subject ∩ clip` (NonZero). */
|
|
12
|
-
intersect(subject: readonly Ring[], clip: readonly Ring[]): Ring[];
|
|
13
|
-
/** Net area in m²: holes, wound the other way, subtract. */
|
|
14
|
-
area(rings: readonly Ring[]): number;
|
|
15
|
-
/**
|
|
16
|
-
* One rectangle per segment of an open polyline, at the given half-width, un-unioned: small
|
|
17
|
-
* strips keep the nearby-wall lookup by bounding box tight, and the caller unions all walls once.
|
|
18
|
-
*/
|
|
19
|
-
offsetSegments(polyline: Ring, halfWidth: number): Ring[];
|
|
20
|
-
}
|
|
21
|
-
//# sourceMappingURL=boolean-ops.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
import type { BooleanOps } from './boolean-ops.js';
|
|
2
|
-
/**
|
|
3
|
-
* {@link BooleanOps} on clipper2-ts — the only module importing it.
|
|
4
|
-
* @returns A {@link BooleanOps} backed by clipper2-ts.
|
|
5
|
-
*/
|
|
6
|
-
export declare function createClipper2Ops(): BooleanOps;
|
|
7
|
-
//# sourceMappingURL=clipper2-ops.d.ts.map
|
|
@@ -1,50 +0,0 @@
|
|
|
1
|
-
import { areaPaths, difference, EndType, FillRule, inflatePaths, intersect, JoinType, union, } from 'clipper2-ts';
|
|
2
|
-
/** Clipper2 works on integers: 1 mm precision at meter scale. */
|
|
3
|
-
const SCALE = 1000;
|
|
4
|
-
function toPoint([x, y]) {
|
|
5
|
-
return { x: Math.round(x * SCALE), y: Math.round(y * SCALE) };
|
|
6
|
-
}
|
|
7
|
-
function toPath(ring) {
|
|
8
|
-
return ring.map(toPoint);
|
|
9
|
-
}
|
|
10
|
-
function toPaths(rings) {
|
|
11
|
-
return rings.map(toPath);
|
|
12
|
-
}
|
|
13
|
-
function fromPath(path) {
|
|
14
|
-
return path.map((pt) => [pt.x / SCALE, pt.y / SCALE]);
|
|
15
|
-
}
|
|
16
|
-
function fromPaths(paths) {
|
|
17
|
-
return paths.map(fromPath);
|
|
18
|
-
}
|
|
19
|
-
/**
|
|
20
|
-
* {@link BooleanOps} on clipper2-ts — the only module importing it.
|
|
21
|
-
* @returns A {@link BooleanOps} backed by clipper2-ts.
|
|
22
|
-
*/
|
|
23
|
-
export function createClipper2Ops() {
|
|
24
|
-
return {
|
|
25
|
-
union(rings) {
|
|
26
|
-
if (rings.length === 0)
|
|
27
|
-
return [];
|
|
28
|
-
return fromPaths(union(toPaths(rings), FillRule.NonZero));
|
|
29
|
-
},
|
|
30
|
-
difference(subject, clip) {
|
|
31
|
-
return fromPaths(difference(toPaths(subject), toPaths(clip), FillRule.NonZero));
|
|
32
|
-
},
|
|
33
|
-
intersect(subject, clip) {
|
|
34
|
-
return fromPaths(intersect(toPaths(subject), toPaths(clip), FillRule.NonZero));
|
|
35
|
-
},
|
|
36
|
-
area(rings) {
|
|
37
|
-
return Math.abs(areaPaths(toPaths(rings))) / (SCALE * SCALE);
|
|
38
|
-
},
|
|
39
|
-
offsetSegments(polyline, halfWidth) {
|
|
40
|
-
const delta = halfWidth * SCALE;
|
|
41
|
-
const quads = [];
|
|
42
|
-
for (let i = 0; i + 1 < polyline.length; i++) {
|
|
43
|
-
const seg = inflatePaths(toPaths([[polyline[i], polyline[i + 1]]]), delta, JoinType.Miter, EndType.Square);
|
|
44
|
-
for (const quad of seg)
|
|
45
|
-
quads.push(fromPath(quad));
|
|
46
|
-
}
|
|
47
|
-
return quads;
|
|
48
|
-
},
|
|
49
|
-
};
|
|
50
|
-
}
|
|
@@ -1,26 +0,0 @@
|
|
|
1
|
-
import type { BooleanOps } from './boolean-ops.js';
|
|
2
|
-
import type { Ring } from './types.js';
|
|
3
|
-
/**
|
|
4
|
-
* How a footprint's ring was read off its drawn pieces: traced exactly, taken as the convex hull the
|
|
5
|
-
* pieces fill, as a short axis-aligned trace, as their box (`bounds-oversized` when the mesh is too
|
|
6
|
-
* fine to trace at all), or as the dock alone when the pieces enclose nothing.
|
|
7
|
-
*/
|
|
8
|
-
export type FootprintShape = 'traced' | 'hull' | 'rectilinear' | 'bounds' | 'bounds-oversized' | 'dock';
|
|
9
|
-
/**
|
|
10
|
-
* The ring a route arrives at — the drawing joined with the dock rectangle — and the other drawn blocks.
|
|
11
|
-
*
|
|
12
|
-
* A piece identical to the dock is skipped: its shared edges would cancel out of the mesh outline. When
|
|
13
|
-
* the drawing falls into blocks, the ring is the block the dock stands in; the rest come back in `drawn`
|
|
14
|
-
* for the caller to carve.
|
|
15
|
-
* @param parts The raw pieces, positioned, in meters.
|
|
16
|
-
* @param ops Boolean backend.
|
|
17
|
-
* @param dock The rectangle routes arrive at, if any.
|
|
18
|
-
* @returns The ring to carve, how it was read, and the drawn blocks it does not cover (only with a
|
|
19
|
-
* dock).
|
|
20
|
-
*/
|
|
21
|
-
export declare function footprintOf(parts: readonly Ring[], ops: BooleanOps, dock?: Ring): {
|
|
22
|
-
ring: Ring | null;
|
|
23
|
-
how?: FootprintShape;
|
|
24
|
-
drawn: Ring[];
|
|
25
|
-
};
|
|
26
|
-
//# sourceMappingURL=footprint.d.ts.map
|