@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.
Files changed (100) hide show
  1. package/README.md +2 -445
  2. package/package.json +3 -34
  3. package/dist/index.d.ts +0 -14
  4. package/dist/index.js +0 -3
  5. package/dist/lib/attach.d.ts +0 -43
  6. package/dist/lib/attach.js +0 -199
  7. package/dist/lib/axes.d.ts +0 -76
  8. package/dist/lib/axes.js +0 -103
  9. package/dist/lib/blockers.d.ts +0 -134
  10. package/dist/lib/blockers.js +0 -289
  11. package/dist/lib/cell-array.d.ts +0 -14
  12. package/dist/lib/cell-array.js +0 -25
  13. package/dist/lib/compile-layer.d.ts +0 -41
  14. package/dist/lib/compile-layer.js +0 -241
  15. package/dist/lib/compile-wayfinding.d.ts +0 -90
  16. package/dist/lib/compile-wayfinding.js +0 -203
  17. package/dist/lib/connect.d.ts +0 -35
  18. package/dist/lib/connect.js +0 -206
  19. package/dist/lib/diagnostics.d.ts +0 -190
  20. package/dist/lib/diagnostics.js +0 -634
  21. package/dist/lib/doorways.d.ts +0 -78
  22. package/dist/lib/doorways.js +0 -331
  23. package/dist/lib/emit-network.d.ts +0 -47
  24. package/dist/lib/emit-network.js +0 -105
  25. package/dist/lib/emit-v1.d.ts +0 -31
  26. package/dist/lib/emit-v1.js +0 -56
  27. package/dist/lib/entries.d.ts +0 -123
  28. package/dist/lib/entries.js +0 -448
  29. package/dist/lib/floor-grids.d.ts +0 -48
  30. package/dist/lib/floor-grids.js +0 -185
  31. package/dist/lib/framing.d.ts +0 -20
  32. package/dist/lib/framing.js +0 -187
  33. package/dist/lib/geom/boolean-ops.d.ts +0 -21
  34. package/dist/lib/geom/boolean-ops.js +0 -1
  35. package/dist/lib/geom/clipper2-ops.d.ts +0 -7
  36. package/dist/lib/geom/clipper2-ops.js +0 -50
  37. package/dist/lib/geom/footprint.d.ts +0 -26
  38. package/dist/lib/geom/footprint.js +0 -203
  39. package/dist/lib/geom/frames.d.ts +0 -116
  40. package/dist/lib/geom/frames.js +0 -233
  41. package/dist/lib/geom/mesh-outline.d.ts +0 -13
  42. package/dist/lib/geom/mesh-outline.js +0 -90
  43. package/dist/lib/geom/normalize.d.ts +0 -28
  44. package/dist/lib/geom/normalize.js +0 -118
  45. package/dist/lib/geom/point-key.d.ts +0 -16
  46. package/dist/lib/geom/point-key.js +0 -15
  47. package/dist/lib/geom/primitives.d.ts +0 -105
  48. package/dist/lib/geom/primitives.js +0 -184
  49. package/dist/lib/geom/split-crossings.d.ts +0 -14
  50. package/dist/lib/geom/split-crossings.js +0 -171
  51. package/dist/lib/geom/types.d.ts +0 -9
  52. package/dist/lib/geom/types.js +0 -1
  53. package/dist/lib/geom/walls.d.ts +0 -31
  54. package/dist/lib/geom/walls.js +0 -71
  55. package/dist/lib/graph-invariants.d.ts +0 -49
  56. package/dist/lib/graph-invariants.js +0 -129
  57. package/dist/lib/graph-types.d.ts +0 -143
  58. package/dist/lib/graph-types.js +0 -7
  59. package/dist/lib/mask.d.ts +0 -54
  60. package/dist/lib/mask.js +0 -210
  61. package/dist/lib/min-heap.d.ts +0 -24
  62. package/dist/lib/min-heap.js +0 -73
  63. package/dist/lib/network-build.d.ts +0 -21
  64. package/dist/lib/network-build.js +0 -4
  65. package/dist/lib/network-graph.d.ts +0 -80
  66. package/dist/lib/network-graph.js +0 -467
  67. package/dist/lib/network-sizes.d.ts +0 -29
  68. package/dist/lib/network-sizes.js +0 -22
  69. package/dist/lib/network-store.d.ts +0 -173
  70. package/dist/lib/network-store.js +0 -430
  71. package/dist/lib/openings.d.ts +0 -54
  72. package/dist/lib/openings.js +0 -387
  73. package/dist/lib/place-path.d.ts +0 -47
  74. package/dist/lib/place-path.js +0 -346
  75. package/dist/lib/post-passes.d.ts +0 -31
  76. package/dist/lib/post-passes.js +0 -305
  77. package/dist/lib/reachability.d.ts +0 -58
  78. package/dist/lib/reachability.js +0 -220
  79. package/dist/lib/report.d.ts +0 -49
  80. package/dist/lib/report.js +0 -374
  81. package/dist/lib/router.d.ts +0 -111
  82. package/dist/lib/router.js +0 -388
  83. package/dist/lib/runs.d.ts +0 -92
  84. package/dist/lib/runs.js +0 -232
  85. package/dist/lib/scale.d.ts +0 -58
  86. package/dist/lib/scale.js +0 -185
  87. package/dist/lib/steiner-network.d.ts +0 -19
  88. package/dist/lib/steiner-network.js +0 -183
  89. package/dist/lib/strip.d.ts +0 -43
  90. package/dist/lib/strip.js +0 -200
  91. package/dist/lib/stubs.d.ts +0 -84
  92. package/dist/lib/stubs.js +0 -316
  93. package/dist/lib/transitions.d.ts +0 -72
  94. package/dist/lib/transitions.js +0 -191
  95. package/dist/lib/types.d.ts +0 -126
  96. package/dist/lib/types.js +0 -1
  97. package/dist/lib/validate-source.d.ts +0 -41
  98. package/dist/lib/validate-source.js +0 -133
  99. package/dist/lib/walkable.d.ts +0 -104
  100. package/dist/lib/walkable.js +0 -140
@@ -1,118 +0,0 @@
1
- import { POINT_EPS } from './point-key.js';
2
- import { pointSegmentDistance } from './primitives.js';
3
- /**
4
- * Distance between two vertices.
5
- * @param a One vertex.
6
- * @param b The other.
7
- * @returns The distance.
8
- */
9
- function gap(a, b) {
10
- return Math.hypot(a[0] - b[0], a[1] - b[1]);
11
- }
12
- /**
13
- * The step parity that carries the repeated joint vertices, or `-1` for a plain polyline.
14
- * @param verts Raw vertices.
15
- * @param eps Coincidence tolerance.
16
- * @returns `0`, `1`, or `-1`.
17
- */
18
- function jointParity(verts, eps) {
19
- const glued = [0, 0];
20
- const total = [0, 0];
21
- for (let k = 0; k + 1 < verts.length; k++) {
22
- total[k % 2]++;
23
- if (gap(verts[k], verts[k + 1]) <= eps)
24
- glued[k % 2]++;
25
- }
26
- // The parity glued more often, by share: a vertex written three times glues both parities once, and
27
- // only the joints' parity is glued at every step.
28
- const parity = glued[0] * total[1] >= glued[1] * total[0] ? 0 : 1;
29
- // The joint parity is glued at least half the time; otherwise every step is a real segment.
30
- return glued[parity] * 2 >= total[parity] && glued[parity] > 0 ? parity : -1;
31
- }
32
- /**
33
- * Dedups a run's vertices and detects whether it closes.
34
- * @param verts Raw vertices.
35
- * @param eps Merge tolerance.
36
- * @returns The run, without an explicit closing vertex.
37
- */
38
- function cleanRun(verts, eps) {
39
- const points = [];
40
- for (const [x, y] of verts) {
41
- const last = points[points.length - 1];
42
- if (last && gap([x, y], last) <= eps)
43
- continue;
44
- points.push([x, y]);
45
- }
46
- const closed = points.length > 2 && gap(points[0], points[points.length - 1]) <= eps;
47
- if (closed)
48
- points.pop();
49
- return { points, closed };
50
- }
51
- /**
52
- * Splits a raw `walls` entry into the continuous runs it describes.
53
- *
54
- * The export stores one segment per vertex pair, joined by repeating the shared vertex; where a pair does
55
- * not touch the next, a new run starts elsewhere. Read as one polyline, each such gap would become a
56
- * phantom wall. The joint parity is read off the entry, since exports double from either vertex.
57
- * @param flat Flat `[x0, y0, x1, y1, …]` coordinates.
58
- * @param eps Merge tolerance for coincident vertices.
59
- * @returns The deduplicated runs; runs of fewer than two vertices are dropped.
60
- */
61
- export function cleanPolylineRuns(flat, eps = POINT_EPS) {
62
- const verts = [];
63
- for (let i = 0; i + 1 < flat.length; i += 2)
64
- verts.push([flat[i], flat[i + 1]]);
65
- if (verts.length < 2)
66
- return [];
67
- const joint = jointParity(verts, eps);
68
- const runs = [];
69
- let start = 0;
70
- for (let k = 0; k + 1 < verts.length; k++) {
71
- if (k % 2 !== joint || gap(verts[k], verts[k + 1]) <= eps)
72
- continue;
73
- runs.push(cleanRun(verts.slice(start, k + 1), eps));
74
- start = k + 1;
75
- }
76
- runs.push(cleanRun(verts.slice(start), eps));
77
- return runs.filter((run) => run.points.length >= 2);
78
- }
79
- /**
80
- * Normalizes a ring: drops coincident vertices (the closing one included) and vertices within `eps` of
81
- * the segment between their neighbors. Every surviving vertex splits a face, and each face grows a stub.
82
- * @param ring The ring, open or explicitly closed.
83
- * @param eps Tolerance for coincidence and collinearity.
84
- * @returns The ring, implicitly closed.
85
- */
86
- export function cleanRing(ring, eps = POINT_EPS) {
87
- const pts = [];
88
- for (const [x, y] of ring) {
89
- const last = pts[pts.length - 1];
90
- if (last && gap([x, y], last) <= eps)
91
- continue;
92
- pts.push([x, y]);
93
- }
94
- while (pts.length > 1) {
95
- const a = pts[0];
96
- const b = pts[pts.length - 1];
97
- if (gap(a, b) > eps)
98
- break;
99
- pts.pop();
100
- }
101
- // Removing one vertex can expose the next, so sweep until stable.
102
- let changed = true;
103
- while (changed && pts.length > 3) {
104
- changed = false;
105
- for (let i = 0; i < pts.length && pts.length > 3; i++) {
106
- const n = pts.length;
107
- const prev = pts[(i + n - 1) % n];
108
- const next = pts[(i + 1) % n];
109
- const [x, y] = pts[i];
110
- if (pointSegmentDistance(x, y, prev[0], prev[1], next[0], next[1]) > eps)
111
- continue;
112
- pts.splice(i, 1);
113
- changed = true;
114
- i--;
115
- }
116
- }
117
- return pts;
118
- }
@@ -1,16 +0,0 @@
1
- /** The resolution the route network tells points apart by, in meters. */
2
- export declare const POINT_EPS = 0.01;
3
- /**
4
- * One coordinate on the centimeter grid.
5
- * @param v Coordinate in meters.
6
- * @returns The coordinate rounded to the centimeter.
7
- */
8
- export declare const snapCm: (v: number) => number;
9
- /**
10
- * A point's key throughout the compiler: its centimeter cell.
11
- * @param x Point x in meters.
12
- * @param y Point y in meters.
13
- * @returns The key.
14
- */
15
- export declare const pointKey: (x: number, y: number) => string;
16
- //# sourceMappingURL=point-key.d.ts.map
@@ -1,15 +0,0 @@
1
- /** The resolution the route network tells points apart by, in meters. */
2
- export const POINT_EPS = 0.01;
3
- /**
4
- * One coordinate on the centimeter grid.
5
- * @param v Coordinate in meters.
6
- * @returns The coordinate rounded to the centimeter.
7
- */
8
- export const snapCm = (v) => Math.round(v * 100) / 100;
9
- /**
10
- * A point's key throughout the compiler: its centimeter cell.
11
- * @param x Point x in meters.
12
- * @param y Point y in meters.
13
- * @returns The key.
14
- */
15
- export const pointKey = (x, y) => `${Math.round(x * 100)}:${Math.round(y * 100)}`;
@@ -1,105 +0,0 @@
1
- import type { Box, Ring, Segment, Vec2 } from './types.js';
2
- /**
3
- * Signed area of a ring (shoelace).
4
- * @param ring The ring to measure.
5
- * @returns The area, positive when the ring winds counter-clockwise.
6
- */
7
- export declare function signedArea(ring: Ring): number;
8
- /**
9
- * The ring wound positive, as the boolean backend's NonZero fill needs.
10
- * @param ring The ring.
11
- * @returns The ring itself when already positive, else a reversed copy.
12
- */
13
- export declare function wound(ring: Ring): Ring;
14
- /**
15
- * The outer rings of a boolean result, told from its holes by their winding: outers wind like the
16
- * largest ring, holes the other way.
17
- * @param rings Outer rings and holes, as the boolean backend returns them.
18
- * @returns The outer rings, in input order.
19
- */
20
- export declare function outerRingsOf(rings: readonly Ring[]): Ring[];
21
- /**
22
- * Even-odd ray-cast point-in-ring test. A point on the boundary may land on either side.
23
- * @param ring The ring to test against.
24
- * @param point The point to locate.
25
- * @returns True when the ring contains the point.
26
- */
27
- export declare function pointInRing(ring: Ring, point: Vec2): boolean;
28
- /**
29
- * Axis-aligned bounds of a set of points.
30
- * @param points The points.
31
- * @returns `[minX, minY, maxX, maxY]`; `[Infinity, Infinity, -Infinity, -Infinity]` for no points.
32
- */
33
- export declare function boundsOf(points: Iterable<Vec2>): Box;
34
- /**
35
- * Whether two bounds overlap, touching included.
36
- * @param a One box.
37
- * @param b The other.
38
- * @returns True when they overlap.
39
- */
40
- export declare function boxesOverlap(a: Readonly<Box>, b: Readonly<Box>): boolean;
41
- /**
42
- * Mean of a ring's vertices — inside it for a convex ring.
43
- * @param ring The ring.
44
- * @returns The mean point.
45
- */
46
- export declare function vertexMean(ring: Ring): Vec2;
47
- /**
48
- * Where along a segment a point projects.
49
- * @param px Point x.
50
- * @param py Point y.
51
- * @param x0 Segment start x.
52
- * @param y0 Segment start y.
53
- * @param x1 Segment end x.
54
- * @param y1 Segment end y.
55
- * @returns The share of the segment's length from its start, clamped to [0, 1]; 0 for a zero-length segment.
56
- */
57
- export declare function segmentParam(px: number, py: number, x0: number, y0: number, x1: number, y1: number): number;
58
- /**
59
- * Distance from a point to a segment.
60
- * @param px Point x.
61
- * @param py Point y.
62
- * @param x0 Segment start x.
63
- * @param y0 Segment start y.
64
- * @param x1 Segment end x.
65
- * @param y1 Segment end y.
66
- * @returns The distance to the nearest point of the segment.
67
- */
68
- export declare function pointSegmentDistance(px: number, py: number, x0: number, y0: number, x1: number, y1: number): number;
69
- /**
70
- * A ray `o + d·t` against the line through a segment `a + e·u`; the caller bounds `t` and `u`.
71
- * @param out Receives `[t, u]` when the lines are not parallel.
72
- * @param ox Ray origin x.
73
- * @param oy Ray origin y.
74
- * @param dx Ray direction x.
75
- * @param dy Ray direction y.
76
- * @param x0 Segment start x.
77
- * @param y0 Segment start y.
78
- * @param x1 Segment end x.
79
- * @param y1 Segment end y.
80
- * @returns False when the ray runs parallel to the segment.
81
- */
82
- export declare function rayVsSegment(out: [number, number], ox: number, oy: number, dx: number, dy: number, x0: number, y0: number, x1: number, y1: number): boolean;
83
- /**
84
- * Whether two segments properly cross; touching or collinear is not a crossing.
85
- * @param seg One segment.
86
- * @param blocker The other.
87
- * @returns True when each has the other's ends strictly on opposite sides.
88
- */
89
- export declare function segmentsCross(seg: Segment, blocker: Segment): boolean;
90
- /**
91
- * A ring's edges as segments.
92
- * @param ring The ring.
93
- * @returns One segment per edge, closing edge included.
94
- */
95
- export declare function ringEdges(ring: Ring): Segment[];
96
- /**
97
- * Whether a point lies on a ring's outline.
98
- * @param ring The ring.
99
- * @param x Point x.
100
- * @param y Point y.
101
- * @param eps Tolerance.
102
- * @returns True when some edge is within `eps`.
103
- */
104
- export declare function onOutline(ring: Ring, x: number, y: number, eps: number): boolean;
105
- //# sourceMappingURL=primitives.d.ts.map
@@ -1,184 +0,0 @@
1
- // Planar primitives shared by the stages, on `[x, y]` tuples.
2
- /**
3
- * Signed area of a ring (shoelace).
4
- * @param ring The ring to measure.
5
- * @returns The area, positive when the ring winds counter-clockwise.
6
- */
7
- export function signedArea(ring) {
8
- let sum = 0;
9
- for (let i = 0; i < ring.length; i++) {
10
- const [x1, y1] = ring[i];
11
- const [x2, y2] = ring[(i + 1) % ring.length];
12
- sum += x1 * y2 - x2 * y1;
13
- }
14
- return sum / 2;
15
- }
16
- /**
17
- * The ring wound positive, as the boolean backend's NonZero fill needs.
18
- * @param ring The ring.
19
- * @returns The ring itself when already positive, else a reversed copy.
20
- */
21
- export function wound(ring) {
22
- return signedArea(ring) < 0 ? [...ring].reverse() : ring;
23
- }
24
- /**
25
- * The outer rings of a boolean result, told from its holes by their winding: outers wind like the
26
- * largest ring, holes the other way.
27
- * @param rings Outer rings and holes, as the boolean backend returns them.
28
- * @returns The outer rings, in input order.
29
- */
30
- export function outerRingsOf(rings) {
31
- if (!rings.length)
32
- return [];
33
- const areas = rings.map(signedArea);
34
- const outer = Math.sign(areas.reduce((a, b) => (Math.abs(b) > Math.abs(a) ? b : a)));
35
- return rings.filter((_, i) => Math.sign(areas[i]) === outer);
36
- }
37
- /**
38
- * Even-odd ray-cast point-in-ring test. A point on the boundary may land on either side.
39
- * @param ring The ring to test against.
40
- * @param point The point to locate.
41
- * @returns True when the ring contains the point.
42
- */
43
- export function pointInRing(ring, point) {
44
- const [x, y] = point;
45
- let inside = false;
46
- for (let i = 0, j = ring.length - 1; i < ring.length; j = i++) {
47
- const [xi, yi] = ring[i];
48
- const [xj, yj] = ring[j];
49
- if (yi > y !== yj > y && x < ((xj - xi) * (y - yi)) / (yj - yi) + xi)
50
- inside = !inside;
51
- }
52
- return inside;
53
- }
54
- /**
55
- * Axis-aligned bounds of a set of points.
56
- * @param points The points.
57
- * @returns `[minX, minY, maxX, maxY]`; `[Infinity, Infinity, -Infinity, -Infinity]` for no points.
58
- */
59
- export function boundsOf(points) {
60
- let minX = Infinity;
61
- let minY = Infinity;
62
- let maxX = -Infinity;
63
- let maxY = -Infinity;
64
- for (const [x, y] of points) {
65
- minX = Math.min(minX, x);
66
- minY = Math.min(minY, y);
67
- maxX = Math.max(maxX, x);
68
- maxY = Math.max(maxY, y);
69
- }
70
- return [minX, minY, maxX, maxY];
71
- }
72
- /**
73
- * Whether two bounds overlap, touching included.
74
- * @param a One box.
75
- * @param b The other.
76
- * @returns True when they overlap.
77
- */
78
- export function boxesOverlap(a, b) {
79
- return a[0] <= b[2] && b[0] <= a[2] && a[1] <= b[3] && b[1] <= a[3];
80
- }
81
- /**
82
- * Mean of a ring's vertices — inside it for a convex ring.
83
- * @param ring The ring.
84
- * @returns The mean point.
85
- */
86
- export function vertexMean(ring) {
87
- let cx = 0;
88
- let cy = 0;
89
- for (const [x, y] of ring) {
90
- cx += x / ring.length;
91
- cy += y / ring.length;
92
- }
93
- return [cx, cy];
94
- }
95
- /**
96
- * Where along a segment a point projects.
97
- * @param px Point x.
98
- * @param py Point y.
99
- * @param x0 Segment start x.
100
- * @param y0 Segment start y.
101
- * @param x1 Segment end x.
102
- * @param y1 Segment end y.
103
- * @returns The share of the segment's length from its start, clamped to [0, 1]; 0 for a zero-length segment.
104
- */
105
- export function segmentParam(px, py, x0, y0, x1, y1) {
106
- const dx = x1 - x0;
107
- const dy = y1 - y0;
108
- const ll = dx * dx + dy * dy;
109
- return ll ? Math.max(0, Math.min(1, ((px - x0) * dx + (py - y0) * dy) / ll)) : 0;
110
- }
111
- /**
112
- * Distance from a point to a segment.
113
- * @param px Point x.
114
- * @param py Point y.
115
- * @param x0 Segment start x.
116
- * @param y0 Segment start y.
117
- * @param x1 Segment end x.
118
- * @param y1 Segment end y.
119
- * @returns The distance to the nearest point of the segment.
120
- */
121
- export function pointSegmentDistance(px, py, x0, y0, x1, y1) {
122
- const t = segmentParam(px, py, x0, y0, x1, y1);
123
- return Math.hypot(x0 + t * (x1 - x0) - px, y0 + t * (y1 - y0) - py);
124
- }
125
- /**
126
- * A ray `o + d·t` against the line through a segment `a + e·u`; the caller bounds `t` and `u`.
127
- * @param out Receives `[t, u]` when the lines are not parallel.
128
- * @param ox Ray origin x.
129
- * @param oy Ray origin y.
130
- * @param dx Ray direction x.
131
- * @param dy Ray direction y.
132
- * @param x0 Segment start x.
133
- * @param y0 Segment start y.
134
- * @param x1 Segment end x.
135
- * @param y1 Segment end y.
136
- * @returns False when the ray runs parallel to the segment.
137
- */
138
- export function rayVsSegment(out, ox, oy, dx, dy, x0, y0, x1, y1) {
139
- const ex = x1 - x0;
140
- const ey = y1 - y0;
141
- const det = dx * ey - dy * ex;
142
- if (Math.abs(det) < 1e-9)
143
- return false;
144
- out[0] = ((x0 - ox) * ey - (y0 - oy) * ex) / det;
145
- out[1] = ((x0 - ox) * dy - (y0 - oy) * dx) / det;
146
- return true;
147
- }
148
- /**
149
- * Whether two segments properly cross; touching or collinear is not a crossing.
150
- * @param seg One segment.
151
- * @param blocker The other.
152
- * @returns True when each has the other's ends strictly on opposite sides.
153
- */
154
- export function segmentsCross(seg, blocker) {
155
- const side = (ax, ay, bx, by, cx, cy) => Math.sign((bx - ax) * (cy - ay) - (by - ay) * (cx - ax));
156
- return (side(seg[0], seg[1], seg[2], seg[3], blocker[0], blocker[1]) *
157
- side(seg[0], seg[1], seg[2], seg[3], blocker[2], blocker[3]) <
158
- 0 &&
159
- side(blocker[0], blocker[1], blocker[2], blocker[3], seg[0], seg[1]) *
160
- side(blocker[0], blocker[1], blocker[2], blocker[3], seg[2], seg[3]) <
161
- 0);
162
- }
163
- /**
164
- * A ring's edges as segments.
165
- * @param ring The ring.
166
- * @returns One segment per edge, closing edge included.
167
- */
168
- export function ringEdges(ring) {
169
- return ring.map((a, i) => {
170
- const b = ring[(i + 1) % ring.length];
171
- return [a[0], a[1], b[0], b[1]];
172
- });
173
- }
174
- /**
175
- * Whether a point lies on a ring's outline.
176
- * @param ring The ring.
177
- * @param x Point x.
178
- * @param y Point y.
179
- * @param eps Tolerance.
180
- * @returns True when some edge is within `eps`.
181
- */
182
- export function onOutline(ring, x, y, eps) {
183
- return ringEdges(ring).some((edge) => pointSegmentDistance(x, y, ...edge) <= eps);
184
- }
@@ -1,14 +0,0 @@
1
- import type { Segment, Vec2 } from './types.js';
2
- /**
3
- * Cuts segments wherever they meet away from their ends, so every meeting is a shared vertex.
4
- *
5
- * Near ends are welded first; each meeting point is then computed once and cut into every segment it lies
6
- * inside, so both sides end on identical coordinates. Swept by left edge.
7
- * @param input The segments, in meters.
8
- * @param eps Tolerance for lying on a segment or at its end.
9
- * @param fixed Vertices found elsewhere by exact coordinates; near ends join them, not the reverse.
10
- * @param moved Receives each fixed vertex that merged into another, by key.
11
- * @returns The distinct pieces.
12
- */
13
- export declare function splitCrossings(input: readonly Segment[], eps?: number, fixed?: readonly Vec2[], moved?: Map<string, Vec2>): Segment[];
14
- //# sourceMappingURL=split-crossings.d.ts.map
@@ -1,171 +0,0 @@
1
- import { POINT_EPS, pointKey } from './point-key.js';
2
- import { pointSegmentDistance, segmentParam } from './primitives.js';
3
- const minX = (s) => Math.min(s[0], s[2]);
4
- /**
5
- * Distance from a point to a segment.
6
- * @param s The segment.
7
- * @param p The point.
8
- * @returns The distance.
9
- */
10
- function distanceTo(s, p) {
11
- return pointSegmentDistance(p[0], p[1], s[0], s[1], s[2], s[3]);
12
- }
13
- /**
14
- * Whether a point lies on a segment clear of both its ends.
15
- * @param s The segment.
16
- * @param p The point.
17
- * @param eps Tolerance.
18
- * @returns True for a point the segment has to be cut at.
19
- */
20
- function inside(s, p, eps) {
21
- // Within `eps` of an end is that end, as in the weld.
22
- return (distanceTo(s, p) < eps &&
23
- Math.hypot(p[0] - s[0], p[1] - s[1]) >= eps &&
24
- Math.hypot(p[0] - s[2], p[1] - s[3]) >= eps);
25
- }
26
- /**
27
- * Where two segments meet: an end of one on the other, and their crossing. A crossing within `eps` of an
28
- * end snaps to that end, so both sides cut at an existing vertex.
29
- * @param a One segment.
30
- * @param b The other.
31
- * @param eps Tolerance.
32
- * @returns Candidate cut points.
33
- */
34
- function meetings(a, b, eps) {
35
- const ends = [
36
- [a[0], a[1]],
37
- [a[2], a[3]],
38
- [b[0], b[1]],
39
- [b[2], b[3]],
40
- ];
41
- const points = ends.filter((end, index) => distanceTo(index < 2 ? b : a, end) < eps);
42
- const rx = a[2] - a[0];
43
- const ry = a[3] - a[1];
44
- const sx = b[2] - b[0];
45
- const sy = b[3] - b[1];
46
- const denom = rx * sy - ry * sx;
47
- if (Math.abs(denom) < 1e-12)
48
- return points;
49
- const qx = b[0] - a[0];
50
- const qy = b[1] - a[1];
51
- const t = (qx * sy - qy * sx) / denom;
52
- const u = (qx * ry - qy * rx) / denom;
53
- if (t <= 0 || t >= 1 || u <= 0 || u >= 1)
54
- return points;
55
- const cross = [+(a[0] + t * rx).toFixed(2), +(a[1] + t * ry).toFixed(2)];
56
- const nearest = ends.reduce((p, q) => Math.hypot(q[0] - cross[0], q[1] - cross[1]) < Math.hypot(p[0] - cross[0], p[1] - cross[1])
57
- ? q
58
- : p);
59
- points.push(Math.hypot(nearest[0] - cross[0], nearest[1] - cross[1]) < eps ? nearest : cross);
60
- return points;
61
- }
62
- /**
63
- * Joins ends closer than `eps` into one vertex: a fixed point if one is near, else the first end met.
64
- * Fixed points closer than `eps` merge into the first of them.
65
- * @param segments The segments.
66
- * @param eps Join distance.
67
- * @param fixed Points that never move.
68
- * @param moved Receives each fixed point that merged into another, by key.
69
- * @returns The welded segments, degenerate ones dropped.
70
- */
71
- function weldEnds(segments, eps, fixed, moved) {
72
- const buckets = new Map();
73
- const cell = (v) => Math.floor(v / eps);
74
- const register = (point) => {
75
- const key = `${cell(point[0])}:${cell(point[1])}`;
76
- const bucket = buckets.get(key) ?? [];
77
- bucket.push(point);
78
- buckets.set(key, bucket);
79
- return point;
80
- };
81
- const near = (x, y) => {
82
- const cx = cell(x);
83
- const cy = cell(y);
84
- let best = null;
85
- let bestDistance = eps;
86
- for (let dx = -1; dx <= 1; dx++)
87
- for (let dy = -1; dy <= 1; dy++)
88
- for (const known of buckets.get(`${cx + dx}:${cy + dy}`) ?? []) {
89
- const distance = Math.hypot(known[0] - x, known[1] - y);
90
- if (distance < bestDistance) {
91
- best = known;
92
- bestDistance = distance;
93
- }
94
- }
95
- return best;
96
- };
97
- const weld = (x, y) => near(x, y) ?? register([x, y]);
98
- for (const point of fixed) {
99
- const joined = near(point[0], point[1]);
100
- if (!joined)
101
- register(point);
102
- else if (joined[0] !== point[0] || joined[1] !== point[1])
103
- moved?.set(pointKey(point[0], point[1]), joined);
104
- }
105
- const welded = [];
106
- for (const s of segments) {
107
- const from = weld(s[0], s[1]);
108
- const to = weld(s[2], s[3]);
109
- if (from !== to)
110
- welded.push([from[0], from[1], to[0], to[1]]);
111
- }
112
- return welded;
113
- }
114
- /**
115
- * Cuts segments wherever they meet away from their ends, so every meeting is a shared vertex.
116
- *
117
- * Near ends are welded first; each meeting point is then computed once and cut into every segment it lies
118
- * inside, so both sides end on identical coordinates. Swept by left edge.
119
- * @param input The segments, in meters.
120
- * @param eps Tolerance for lying on a segment or at its end.
121
- * @param fixed Vertices found elsewhere by exact coordinates; near ends join them, not the reverse.
122
- * @param moved Receives each fixed vertex that merged into another, by key.
123
- * @returns The distinct pieces.
124
- */
125
- export function splitCrossings(input, eps = POINT_EPS, fixed = [], moved) {
126
- const segments = weldEnds(input, eps, fixed, moved);
127
- const cuts = segments.map(() => []);
128
- const order = segments.map((_, i) => i).sort((i, j) => minX(segments[i]) - minX(segments[j]));
129
- for (let oi = 0; oi < order.length; oi++) {
130
- const a = segments[order[oi]];
131
- const right = Math.max(a[0], a[2]) + eps;
132
- const top = Math.max(a[1], a[3]) + eps;
133
- const bottom = Math.min(a[1], a[3]) - eps;
134
- for (let oj = oi + 1; oj < order.length; oj++) {
135
- const b = segments[order[oj]];
136
- if (minX(b) > right)
137
- break;
138
- if (Math.min(b[1], b[3]) > top || Math.max(b[1], b[3]) < bottom)
139
- continue;
140
- for (const point of meetings(a, b, eps)) {
141
- if (inside(a, point, eps))
142
- cuts[order[oi]].push(point);
143
- if (inside(b, point, eps))
144
- cuts[order[oj]].push(point);
145
- }
146
- }
147
- }
148
- const pieces = [];
149
- const seen = new Set();
150
- const add = (p, q) => {
151
- const from = pointKey(p[0], p[1]);
152
- const to = pointKey(q[0], q[1]);
153
- const key = from < to ? `${from}|${to}` : `${to}|${from}`;
154
- if (from === to || seen.has(key))
155
- return;
156
- seen.add(key);
157
- pieces.push([p[0], p[1], q[0], q[1]]);
158
- };
159
- segments.forEach((s, i) => {
160
- const along = cuts[i]
161
- .map((point) => ({ point, t: segmentParam(point[0], point[1], s[0], s[1], s[2], s[3]) }))
162
- .sort((p, q) => p.t - q.t);
163
- let from = [s[0], s[1]];
164
- for (const { point } of along) {
165
- add(from, point);
166
- from = point;
167
- }
168
- add(from, [s[2], s[3]]);
169
- });
170
- return pieces;
171
- }
@@ -1,9 +0,0 @@
1
- /** A point as an `[x, y]` tuple, which keeps the hot geometry loops allocation-light. */
2
- export type Vec2 = readonly [number, number];
3
- /** A polygon ring, implicitly closed (the first vertex is not repeated). */
4
- export type Ring = readonly Vec2[];
5
- /** A line segment as `[x0, y0, x1, y1]`. */
6
- export type Segment = [number, number, number, number];
7
- /** Axis-aligned bounds as `[minX, minY, maxX, maxY]`. */
8
- export type Box = [number, number, number, number];
9
- //# sourceMappingURL=types.d.ts.map
@@ -1 +0,0 @@
1
- export {};
@@ -1,31 +0,0 @@
1
- import { type WallLine } from './normalize.js';
2
- import type { Segment } from './types.js';
3
- /**
4
- * A raw `walls` entry the cleaning changed: it split into runs, cleaned to nothing (`runs === 0`
5
- * with something drawn), or held an odd trailing coordinate.
6
- */
7
- export interface WallEntryFact {
8
- /** Index of the entry in the raw `walls` array. */
9
- readonly entry: number;
10
- /** How many continuous runs the entry split into; 0 when it cleaned away whole. */
11
- readonly runs: number;
12
- /** True when the entry's flat array holds an odd coordinate count, so the last number is dropped. */
13
- readonly oddTail: boolean;
14
- }
15
- /** A layer's walls, read once: the cleaned runs and their segments. */
16
- export interface PreparedWalls {
17
- /** All runs, each in canonical direction, sorted. */
18
- readonly runs: readonly WallLine[];
19
- /** One segment per wall piece (closing pieces included), in run order. */
20
- readonly segments: readonly Segment[];
21
- /** The entries the cleaning changed, in entry order. */
22
- readonly entries: readonly WallEntryFact[];
23
- }
24
- /**
25
- * Reads a layer's raw walls once for every stage. Runs are canonicalized and sorted, so wall order and
26
- * drawing direction do not change the network.
27
- * @param walls Raw walls as flat `[x0, y0, …]` runs.
28
- * @returns The runs and their segments.
29
- */
30
- export declare function prepareWalls(walls: readonly (readonly number[])[]): PreparedWalls;
31
- //# sourceMappingURL=walls.d.ts.map