@weasel-js/guides 1.14.1 → 1.15.0

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/dist/index.js CHANGED
@@ -1,12 +1,197 @@
1
1
  import { useState, useRef, useCallback, useEffect } from 'react';
2
- import { sceneBoxFrames, scenePoseFrame, resolveAxes, useLatest, viewportWorldRect, tickedSegment, measuredSegmentCommands, AUTO_POSE_DESCRIPTOR, visualBoundsViaDescriptor, rectFromPlane, unionAABB, createTransformOp, asNodeId, sceneSharedUnitScopes, sceneUnitScopes, screenToWorld, nodeGates, resolveScoped, resolveStrokeWidth } from '@weasel-js/core';
3
- import { pointInPolygon, invert, applyToPoint, mat3, pxExtent, PATH_M, PATH_L, PATH_Z, pxExtentInFrame } from '@weasel-js/geom';
2
+ import { sceneBoxFrames, scenePoseFrame, useLatest, viewportWorldRect, tickedSegment, measuredSegmentCommands, AUTO_POSE_DESCRIPTOR, visualBoundsViaDescriptor, rectFromPlane, unionAABB, createTransformOp, asNodeId, sceneUnitScopes, screenToWorld, nodeGates, resolveStrokeWidth, resolveUnit, axisUnits, resolveScoped } from '@weasel-js/core';
3
+ import { pointInPolygon, invert, mat3, applyToPoint, pxExtent, PATH_M, PATH_L, PATH_Z, pxExtentInFrame } from '@weasel-js/geom';
4
4
 
5
5
  // src/grid/roundToCell.ts
6
6
  function roundToCell(value, cellSize) {
7
7
  return Math.round(value / cellSize) * cellSize || 0;
8
8
  }
9
- function sceneGridRegions(scene, root, outlineOf) {
9
+
10
+ // src/grid/axes.ts
11
+ function isGridLength(v) {
12
+ return typeof v === "number" || typeof v === "object" && v !== null && "value" in v;
13
+ }
14
+ function resolveGridLines(spacing, read) {
15
+ const on = (axis) => {
16
+ const a = isGridLength(spacing) ? spacing : spacing[axis];
17
+ if (a === void 0) return null;
18
+ try {
19
+ return resolveAxis(a, (v) => read(v, axis));
20
+ } catch (e) {
21
+ throw new Error(`${axis} axis: ${e instanceof Error ? e.message : String(e)}`);
22
+ }
23
+ };
24
+ return { x: on("x"), y: on("y") };
25
+ }
26
+ function resolveAxis(a, read) {
27
+ if (isGridLength(a)) return periodic(read(a), 0, -Infinity, Infinity);
28
+ if ("at" in a) {
29
+ const at = [...new Set(a.at.map(read))].filter(Number.isFinite).sort((p, q) => p - q);
30
+ return at.length ? { at } : null;
31
+ }
32
+ return periodic(
33
+ read(a.pitch),
34
+ a.offset === void 0 ? 0 : read(a.offset),
35
+ a.from === void 0 ? -Infinity : read(a.from),
36
+ a.to === void 0 ? Infinity : read(a.to)
37
+ );
38
+ }
39
+ function periodic(pitch, offset, from, to) {
40
+ return pitch > 0 && Number.isFinite(pitch) && from <= to ? { pitch, offset, from, to } : null;
41
+ }
42
+ var EPS = 1e-9;
43
+ function indexRange(a) {
44
+ return [Math.ceil((a.from - a.offset) / a.pitch - EPS), Math.floor((a.to - a.offset) / a.pitch + EPS)];
45
+ }
46
+ function linesNear(a, v) {
47
+ let pair;
48
+ if ("at" in a) {
49
+ const i = Math.min(Math.max(0, lastAtOrBefore(a.at, v)), Math.max(0, a.at.length - 2));
50
+ pair = a.at.slice(i, i + 2);
51
+ } else {
52
+ const [lo3, hi3] = indexRange(a);
53
+ if (lo3 > hi3) return [];
54
+ const k = Math.min(Math.max(Math.floor((v - a.offset) / a.pitch), lo3), Math.max(lo3, hi3 - 1));
55
+ pair = k + 1 <= hi3 ? [a.offset + k * a.pitch, a.offset + (k + 1) * a.pitch] : [a.offset + k * a.pitch];
56
+ }
57
+ return pair.length === 2 && v - pair[0] >= pair[1] - v ? [pair[1], pair[0]] : pair;
58
+ }
59
+ function nearestLine(a, v) {
60
+ let best = null;
61
+ for (const l of linesNear(a, v)) if (best === null || Math.abs(l - v) < Math.abs(best - v)) best = l;
62
+ return best;
63
+ }
64
+ function linesIn(a, lo3, hi3) {
65
+ if ("at" in a) return a.at.filter((l) => l >= lo3 - EPS && l <= hi3 + EPS);
66
+ const [k0, k1] = indexRange({ ...a, from: Math.max(a.from, lo3), to: Math.min(a.to, hi3) });
67
+ const out = [];
68
+ for (let k = k0; k <= k1; k++) out.push(a.offset + k * a.pitch);
69
+ return out;
70
+ }
71
+ function cellOf(a, v) {
72
+ if (!a) return { index: 0, lo: -Infinity, hi: Infinity };
73
+ if ("at" in a) {
74
+ const i = lastAtOrBefore(a.at, v);
75
+ return i >= 0 && i < a.at.length - 1 ? { index: i, lo: a.at[i], hi: a.at[i + 1] } : null;
76
+ }
77
+ const [lo3, hi3] = indexRange(a);
78
+ const k = Math.floor((v - a.offset) / a.pitch);
79
+ if (k < lo3 || k >= hi3) return null;
80
+ return { index: k, lo: a.offset + k * a.pitch, hi: a.offset + (k + 1) * a.pitch };
81
+ }
82
+ function mapLines(a, scale, shift) {
83
+ const m = (p) => p * scale + shift;
84
+ if ("at" in a) return { at: a.at.map(m).sort((p, q) => p - q) };
85
+ const [from, to] = [m(a.from), m(a.to)].sort((p, q) => p - q);
86
+ return { pitch: Math.abs(a.pitch * scale), offset: m(a.offset), from: Number.isNaN(from) ? -Infinity : from, to: Number.isNaN(to) ? Infinity : to };
87
+ }
88
+ function lastAtOrBefore(at, v) {
89
+ let lo3 = 0;
90
+ let hi3 = at.length - 1;
91
+ let found = -1;
92
+ while (lo3 <= hi3) {
93
+ const mid2 = lo3 + hi3 >> 1;
94
+ if (at[mid2] <= v + EPS) {
95
+ found = mid2;
96
+ lo3 = mid2 + 1;
97
+ } else hi3 = mid2 - 1;
98
+ }
99
+ return found;
100
+ }
101
+ var MIN_SCREEN_SPACING = 4;
102
+ function hasArea(r) {
103
+ return r.width > 0 && r.height > 0 && Number.isFinite(r.width) && Number.isFinite(r.height);
104
+ }
105
+ var other = (axis) => axis === "x" ? "y" : "x";
106
+ function lattice(b, lines, shape, view, styles, frame) {
107
+ const px = frame ? pxExtentInFrame(1, view.scale, frame) : pxExtent(1, view.scale);
108
+ const span2 = { x: [b.x, b.x + b.width], y: [b.y, b.y + b.height] };
109
+ const children = [];
110
+ const across = (axis) => {
111
+ const a = lines[axis];
112
+ const [lo3, hi3] = span2[axis];
113
+ return a && "pitch" in a ? [Math.max(lo3, a.from), Math.min(hi3, a.to)] : [lo3, hi3];
114
+ };
115
+ const draw = (axis, at, s) => {
116
+ const [lo3, hi3] = across(other(axis));
117
+ if (!(hi3 > lo3)) return;
118
+ const width = resolveStrokeWidth(s.width ?? 1, 1) * px[axis];
119
+ for (const p of at) {
120
+ const coords = axis === "x" ? [p, lo3, p, hi3] : [lo3, p, hi3, p];
121
+ const path = {
122
+ kind: "polygon",
123
+ commands: new Uint8Array([PATH_M, PATH_L]),
124
+ coords: new Float32Array(coords),
125
+ fillRule: "nonzero"
126
+ };
127
+ children.push({ kind: "path", path, stroke: { ...s, width } });
128
+ }
129
+ };
130
+ const periodic2 = (axis, a, step, skipEvery, s) => {
131
+ if (!(step / px[axis] >= MIN_SCREEN_SPACING)) return;
132
+ const at = linesIn({ ...a, pitch: step }, ...span2[axis]);
133
+ draw(axis, skipEvery > 0 ? at.filter((p) => Math.round((p - a.offset) / step) % skipEvery !== 0) : at, s);
134
+ };
135
+ const { accentEvery, subdivisions } = shape;
136
+ const accentOn = accentEvery !== void 0 && accentEvery > 0;
137
+ const passes = [
138
+ // Sub-lines first so cell lines paint on top, accents last of all.
139
+ (axis, a) => {
140
+ if (subdivisions && subdivisions > 1) periodic2(axis, a, a.pitch / subdivisions, subdivisions, styles.sub);
141
+ },
142
+ (axis, a) => periodic2(axis, a, a.pitch, accentOn ? accentEvery : 0, styles.line),
143
+ (axis, a) => {
144
+ if (accentOn) periodic2(axis, a, a.pitch * accentEvery, 0, styles.accent);
145
+ }
146
+ ];
147
+ for (const pass of passes) {
148
+ for (const axis of ["x", "y"]) {
149
+ const a = lines[axis];
150
+ if (a && "pitch" in a) pass(axis, a);
151
+ }
152
+ }
153
+ for (const axis of ["x", "y"]) {
154
+ const a = lines[axis];
155
+ if (a && "at" in a) draw(axis, thinned(linesIn(a, ...span2[axis]), px[axis] * MIN_SCREEN_SPACING), styles.line);
156
+ }
157
+ return children;
158
+ }
159
+ function thinned(at, gap) {
160
+ const out = [];
161
+ for (const p of at) if (out.length === 0 || p - out[out.length - 1] >= gap) out.push(p);
162
+ return out;
163
+ }
164
+ function outlineBox(outline) {
165
+ const xs = outline.map((p) => p.x);
166
+ const ys = outline.map((p) => p.y);
167
+ const x = Math.min(...xs);
168
+ const y = Math.min(...ys);
169
+ return { x, y, width: Math.max(...xs) - x, height: Math.max(...ys) - y };
170
+ }
171
+ function intersect(a, b) {
172
+ const x = Math.max(a.x, b.x);
173
+ const y = Math.max(a.y, b.y);
174
+ return { x, y, width: Math.min(end(a.x, a.width), end(b.x, b.width)) - x, height: Math.min(end(a.y, a.height), end(b.y, b.height)) - y };
175
+ }
176
+ var end = (start, size) => size === Infinity ? Infinity : start + size;
177
+ function span(v, chain) {
178
+ const zero = typeof v === "number" ? 0 : { ...v, value: 0 };
179
+ return resolveScoped(v, chain) - resolveScoped(zero, chain);
180
+ }
181
+ function gridLines(spacing, unitSystem, scopes, chain) {
182
+ if (!scopes) return resolveGridLines(spacing, (v, axis) => resolveUnit(v, axisUnits(unitSystem, axis)));
183
+ const at = chain ?? [scopes.root];
184
+ return resolveGridLines(spacing, (v) => span(v, at));
185
+ }
186
+ function scopeSpacing(spacing, scopes, chain) {
187
+ const inner = chain && chain.length > 1 ? chain[chain.length - 1] : null;
188
+ return inner && scopes?.spacing ? scopes.spacing(inner) : spacing;
189
+ }
190
+ function containerLines(own, spacing, unitSystem, scopes, chain) {
191
+ if (own === null) return null;
192
+ return gridLines(own ?? scopeSpacing(spacing, scopes, chain), unitSystem, scopes, chain);
193
+ }
194
+ function sceneGridRegions(scene, owners, outlineOf) {
10
195
  const frames = sceneBoxFrames(scene);
11
196
  const poses = scenePoseFrame(scene);
12
197
  const boxOutline = (id, frame) => {
@@ -18,8 +203,8 @@ function sceneGridRegions(scene, root, outlineOf) {
18
203
  };
19
204
  const children = /* @__PURE__ */ new Map();
20
205
  for (const id of scene.nodes.keys()) {
21
- if (scene.get(id)?.units === void 0) continue;
22
- const parent = scopeOwner(scene, id);
206
+ if (!ownsGrid(scene, id, owners)) continue;
207
+ const parent = gridOwner(scene, id, owners);
23
208
  children.set(parent, [...children.get(parent) ?? [], id]);
24
209
  }
25
210
  const build = (parent) => {
@@ -29,9 +214,11 @@ function sceneGridRegions(scene, root, outlineOf) {
29
214
  const outline = outlineOf ? outlineOf(id) : frame && boxOutline(id, frame);
30
215
  if (!outline) continue;
31
216
  const inner = build(id);
217
+ const own = owners.gridOf?.(id);
32
218
  out.push({
33
219
  outline,
34
- chain: sceneUnitScopes(scene, id, root, { inclusive: true }),
220
+ ...owners.root ? { chain: sceneUnitScopes(scene, id, owners.root, { inclusive: true }) } : {},
221
+ ...own !== void 0 ? { spacing: own } : {},
35
222
  ...inner.length ? { regions: inner } : {},
36
223
  ...frame ? { frame } : {}
37
224
  });
@@ -40,9 +227,12 @@ function sceneGridRegions(scene, root, outlineOf) {
40
227
  };
41
228
  return build(null);
42
229
  }
43
- function scopeOwner(scene, id, inclusive = false) {
230
+ function ownsGrid(scene, id, owners) {
231
+ return owners.gridOf?.(id) !== void 0 || owners.root !== void 0 && scene.get(id)?.units !== void 0;
232
+ }
233
+ function gridOwner(scene, id, owners, inclusive = false) {
44
234
  for (let at = inclusive ? id : scene.get(id)?.parent ?? null; at !== null; at = scene.get(at)?.parent ?? null) {
45
- if (scene.get(at)?.units !== void 0) return at;
235
+ if (ownsGrid(scene, at, owners)) return at;
46
236
  }
47
237
  return null;
48
238
  }
@@ -53,48 +243,41 @@ function regionAt(regions, point) {
53
243
  }
54
244
  return null;
55
245
  }
56
- function span(v, chain) {
57
- const zero = typeof v === "number" ? 0 : { ...v, value: 0 };
58
- return resolveScoped(v, chain) - resolveScoped(zero, chain);
59
- }
60
- function scopedGridPitch(spacing, scopes, chain) {
61
- const inner = chain.length > 1 ? chain[chain.length - 1] : null;
62
- const v = inner && scopes.spacing ? scopes.spacing(inner) : spacing;
63
- const on = (axis) => {
64
- const a = typeof v === "object" && "x" in v ? v[axis] : v;
65
- try {
66
- return span(a, chain);
67
- } catch (e) {
68
- throw new Error(`${axis} axis: ${e instanceof Error ? e.message : String(e)}`);
69
- }
70
- };
71
- return { x: on("x"), y: on("y") };
72
- }
73
- function gridPitch(spacing, unitSystem, scopes, chain) {
74
- return scopes ? scopedGridPitch(spacing, scopes, chain ?? [scopes.root]) : resolveAxes(spacing, unitSystem);
75
- }
76
246
 
77
247
  // src/grid/pointToGridCell.ts
78
248
  function pointToGridCell(point, spacing, unitSystem, origin = { x: 0, y: 0 }, scoped) {
79
- const region = scoped ? regionAt(scoped.regions, point) : null;
80
- const s = gridPitch(spacing, unitSystem, scoped?.scopes, region?.chain);
81
- const back = region?.frame && invert(region.frame);
82
- if (region?.frame && back) {
83
- const [x, y] = applyToPoint(back, point.x, point.y);
84
- return { col: Math.floor(x / s.x), row: Math.floor(y / s.y), pitch: s, frame: region.frame };
249
+ const region = regionAt(scoped?.regions, point);
250
+ if (!region) {
251
+ const cell2 = cellIn(gridLines(spacing, unitSystem, scoped?.scopes), point.x - origin.x, point.y - origin.y);
252
+ return cell2 && { ...cell2, box: { ...cell2.box, x: cell2.box.x + origin.x, y: cell2.box.y + origin.y } };
85
253
  }
86
- return {
87
- col: Math.floor((point.x - origin.x) / s.x),
88
- row: Math.floor((point.y - origin.y) / s.y),
89
- ...scoped ? { pitch: s } : {}
254
+ const lines = containerLines(region.spacing, spacing, unitSystem, scoped?.scopes, region.chain);
255
+ if (!lines) return null;
256
+ const back = region.frame && invert(region.frame);
257
+ if (region.frame && !back) return null;
258
+ const local = (p) => {
259
+ if (!back) return p;
260
+ const [x, y] = applyToPoint(back, p.x, p.y);
261
+ return { x, y };
90
262
  };
263
+ const at = local(point);
264
+ const cell = cellIn(lines, at.x, at.y, outlineBox(region.outline.map(local)));
265
+ return cell && (region.frame ? { ...cell, frame: region.frame } : cell);
266
+ }
267
+ function cellIn(lines, x, y, within) {
268
+ const cx = cellOf(lines.x, x);
269
+ const cy = cellOf(lines.y, y);
270
+ if (!cx || !cy) return null;
271
+ const [x0, x1] = within && !lines.x ? [within.x, within.x + within.width] : [cx.lo, cx.hi];
272
+ const [y0, y1] = within && !lines.y ? [within.y, within.y + within.height] : [cy.lo, cy.hi];
273
+ return { col: cx.index, row: cy.index, box: { x: x0, y: y0, width: x1 - x0, height: y1 - y0 } };
91
274
  }
92
275
 
93
276
  // src/grid/useGridCellHover.ts
94
277
  function cellsEqual(a, b) {
95
278
  if (a === b) return true;
96
279
  if (a === null || b === null) return false;
97
- return a.col === b.col && a.row === b.row && a.pitch?.x === b.pitch?.x && a.pitch?.y === b.pitch?.y && (a.frame === b.frame || !!a.frame && !!b.frame && a.frame.every((v, i) => v === b.frame[i]));
280
+ return a.col === b.col && a.row === b.row && a.box.x === b.box.x && a.box.y === b.box.y && a.box.width === b.box.width && a.box.height === b.box.height && (a.frame === b.frame || !!a.frame && !!b.frame && a.frame.every((v, i) => v === b.frame[i]));
98
281
  }
99
282
  function useGridCellHover(opts) {
100
283
  const [cell, setCell] = useState(null);
@@ -118,7 +301,7 @@ function useGridCellHover(opts) {
118
301
  const sx = e.clientX - rect.left;
119
302
  const sy = e.clientY - rect.top;
120
303
  const [wx, wy] = screenToWorld(sx, sy, o.view());
121
- const scoped = o.scopes ? { scopes: o.scopes, ...o.regions ? { regions: o.regions() } : {} } : void 0;
304
+ const scoped = { ...o.scopes ? { scopes: o.scopes } : {}, ...o.regions ? { regions: o.regions() } : {} };
122
305
  update(pointToGridCell({ x: wx, y: wy }, o.spacing, o.unitSystem, o.origin, scoped));
123
306
  };
124
307
  const onLeave = () => update(null);
@@ -133,66 +316,6 @@ function useGridCellHover(opts) {
133
316
  }, [enabled, opts.ref, optsRef]);
134
317
  return { cell, getCell };
135
318
  }
136
- var MIN_SCREEN_SPACING = 4;
137
- function hasArea(r) {
138
- return r.width > 0 && r.height > 0 && Number.isFinite(r.width) && Number.isFinite(r.height);
139
- }
140
- function lattice(b, spacing, shape, view, styles, frame) {
141
- const { accentEvery, subdivisions } = shape;
142
- const x0 = b.x;
143
- const y0 = b.y;
144
- const x1 = b.x + b.width;
145
- const y1 = b.y + b.height;
146
- const px = frame ? pxExtentInFrame(1, view.scale, frame) : pxExtent(1, view.scale);
147
- const children = [];
148
- const pushSegment = (sx, sy, ex, ey, s) => {
149
- const path = {
150
- kind: "polygon",
151
- commands: new Uint8Array([PATH_M, PATH_L]),
152
- coords: new Float32Array([sx, sy, ex, ey]),
153
- fillRule: "nonzero"
154
- };
155
- const across = sx === ex ? px.x : px.y;
156
- children.push({
157
- kind: "path",
158
- path,
159
- stroke: { ...s, width: resolveStrokeWidth(s.width ?? 1, 1) * across }
160
- });
161
- };
162
- const family = (axis, step, skipEvery, s) => {
163
- if (!(step / px[axis] >= MIN_SCREEN_SPACING)) return;
164
- const [from, to] = axis === "x" ? [x0, x1] : [y0, y1];
165
- for (let k = 0; from + k * step <= to + 1e-9; k++) {
166
- if (skipEvery > 0 && k % skipEvery === 0) continue;
167
- const at = from + k * step;
168
- if (axis === "x") pushSegment(at, y0, at, y1, s);
169
- else pushSegment(x0, at, x1, at, s);
170
- }
171
- };
172
- const accentOn = accentEvery !== void 0 && accentEvery > 0;
173
- for (const axis of ["x", "y"]) {
174
- if (subdivisions && subdivisions > 1) family(axis, spacing[axis] / subdivisions, subdivisions, styles.sub);
175
- }
176
- for (const axis of ["x", "y"]) family(axis, spacing[axis], accentOn ? accentEvery : 0, styles.line);
177
- if (accentOn) {
178
- for (const axis of ["x", "y"]) family(axis, spacing[axis] * accentEvery, 0, styles.accent);
179
- }
180
- return children;
181
- }
182
- function onLattice(r, spacing, accentEvery, anchor) {
183
- const px = spacing.x * accentEvery;
184
- const py = spacing.y * accentEvery;
185
- const x = anchor.x + Math.floor((r.x - anchor.x) / px) * px;
186
- const y = anchor.y + Math.floor((r.y - anchor.y) / py) * py;
187
- return {
188
- x,
189
- y,
190
- width: anchor.x + Math.ceil((r.x + r.width - anchor.x) / px) * px - x,
191
- height: anchor.y + Math.ceil((r.y + r.height - anchor.y) / py) * py - y
192
- };
193
- }
194
-
195
- // src/grid/layer.ts
196
319
  var DEFAULT_LINE = {
197
320
  paint: { kind: "solid", color: "rgba(255, 255, 255, 0.08)" },
198
321
  width: 1
@@ -215,20 +338,16 @@ function createGridLayer(opts) {
215
338
  id: "grid",
216
339
  label: "Grid",
217
340
  draw: (_data, view, dims) => {
218
- const every = opts.accentEvery && opts.accentEvery > 0 ? opts.accentEvery : 1;
219
- const spacing = gridPitch(opts.spacing, opts.unitSystem, opts.scopes);
220
- const shown = viewportWorldRect(view, dims);
221
- const b = opts.bounds ? opts.bounds(view, dims) : onLattice(shown, spacing, every, { x: 0, y: 0 });
341
+ const b = opts.bounds ? opts.bounds(view, dims) : padded(viewportWorldRect(view, dims), view);
222
342
  if (!hasArea(b)) return [];
223
- const root = lattice(b, spacing, opts, view, styles);
224
- const regions = opts.scopes && opts.regions ? opts.regions() : [];
225
- if (regions.length === 0) return root;
226
343
  const anchor = opts.bounds ? { x: b.x, y: b.y } : { x: 0, y: 0 };
227
- const area = opts.bounds ? b : shown;
344
+ const root = lattice(b, placed(gridLines(opts.spacing, opts.unitSystem, opts.scopes), anchor), opts, view, styles);
345
+ const regions = opts.regions?.() ?? [];
346
+ if (regions.length === 0) return root;
228
347
  const children = [{ kind: "group", clip: withHoles(rectOutline(b), regions), children: root }];
229
348
  const visit = (r) => {
230
- const pitch = gridPitch(opts.spacing, opts.unitSystem, opts.scopes, r.chain);
231
- const lines = regionLattice(r, area, (box, frame) => lattice(onLattice(box, pitch, every, frame ? { x: 0, y: 0 } : anchor), pitch, opts, view, styles, frame));
349
+ const own = containerLines(r.spacing, opts.spacing, opts.unitSystem, opts.scopes, r.chain);
350
+ const lines = own ? regionLattice(r, b, (box, frame) => lattice(box, placed(own, frame ? { x: 0, y: 0 } : anchor), opts, view, styles, frame)) : [];
232
351
  if (lines.length) children.push({ kind: "group", clip: withHoles(r.outline, r.regions ?? []), children: lines });
233
352
  for (const inner of r.regions ?? []) visit(inner);
234
353
  };
@@ -237,6 +356,14 @@ function createGridLayer(opts) {
237
356
  }
238
357
  };
239
358
  }
359
+ function padded(r, view) {
360
+ const px = 1 / Math.abs(view.scale.x);
361
+ const py = 1 / Math.abs(view.scale.y);
362
+ return { x: r.x - px, y: r.y - py, width: r.width + 2 * px, height: r.height + 2 * py };
363
+ }
364
+ function placed(lines, anchor) {
365
+ return { x: lines.x && mapLines(lines.x, 1, anchor.x), y: lines.y && mapLines(lines.y, 1, anchor.y) };
366
+ }
240
367
  function rectOutline(r) {
241
368
  return [
242
369
  { x: r.x, y: r.y },
@@ -273,51 +400,36 @@ function regionLattice(r, area, draw) {
273
400
  const lines = draw(box, r.frame);
274
401
  return lines.length ? [{ kind: "group", transform: mat3.fromAffine(r.frame), children: lines }] : [];
275
402
  }
276
- function outlineBox(outline) {
277
- const xs = outline.map((p) => p.x);
278
- const ys = outline.map((p) => p.y);
279
- const x = Math.min(...xs);
280
- const y = Math.min(...ys);
281
- return { x, y, width: Math.max(...xs) - x, height: Math.max(...ys) - y };
282
- }
283
- function intersect(a, b) {
284
- const x = Math.max(a.x, b.x);
285
- const y = Math.max(a.y, b.y);
286
- return { x, y, width: Math.min(a.x + a.width, b.x + b.width) - x, height: Math.min(a.y + a.height, b.y + b.height) - y };
287
- }
288
403
  var DEFAULT_FILL = { paint: { kind: "solid", color: "rgba(127,176,105,0.15)" } };
289
404
  function createCellHighlightLayer(opts) {
290
405
  const fill = opts.fill ?? DEFAULT_FILL;
291
406
  return {
292
407
  id: "cell-highlight",
293
408
  label: "Cell highlight",
294
- draw: () => {
409
+ draw: (_data, view, dims) => {
295
410
  const cell = opts.getCell();
296
411
  if (!cell) return [];
297
- const spacing = cell.pitch ?? resolveAxes(opts.spacing, opts.unitSystem);
298
- const o = cell.frame || !opts.origin ? { x: 0, y: 0 } : opts.origin();
299
- const x = o.x + cell.col * spacing.x;
300
- const y = o.y + cell.row * spacing.y;
301
- const children = [
302
- {
303
- kind: "path",
304
- path: { kind: "rect", x, y, width: spacing.x, height: spacing.y },
305
- fill
306
- }
307
- ];
412
+ const box = cell.frame ? cell.box : intersect(cell.box, viewportWorldRect(view, dims));
413
+ if (!hasArea(box)) return [];
414
+ const children = [{ kind: "path", path: { kind: "rect", ...box }, fill }];
308
415
  return cell.frame ? [{ kind: "group", transform: mat3.fromAffine(cell.frame), children }] : children;
309
416
  }
310
417
  };
311
418
  }
312
419
 
313
- // src/strategies/onPlane.ts
314
- function guideOffsetOn(g, plane) {
315
- return plane ? g.offset * plane.scale[g.axis] + plane.offset[g.axis] : g.offset;
316
- }
317
- function roundToGridOn(value, axis, spacing, plane) {
318
- if (!plane) return Math.round(value / spacing) * spacing;
319
- const camera = (value - plane.offset[axis]) / plane.scale[axis];
320
- return Math.round(camera / spacing) * spacing * plane.scale[axis] + plane.offset[axis];
420
+ // src/grid/fit.ts
421
+ function gridFit(points, lines, tolerance = 1e-6) {
422
+ const misses = [];
423
+ points.forEach((p, index) => {
424
+ for (const axis of ["x", "y"]) {
425
+ const a = lines[axis];
426
+ if (!a) continue;
427
+ const near = nearestLine(a, p[axis]);
428
+ const distance = near === null ? Infinity : Math.abs(p[axis] - near);
429
+ if (distance > tolerance) misses.push({ index, axis, distance });
430
+ }
431
+ });
432
+ return { fits: misses.length === 0, misses };
321
433
  }
322
434
 
323
435
  // src/sources/lines.ts
@@ -333,67 +445,89 @@ function frameLine(frame, axis, offset, near) {
333
445
  function landedShift(q, delta) {
334
446
  return { x: delta.x - q.delta0.x, y: delta.y - q.delta0.y };
335
447
  }
448
+ function sideLine(near, rate, by) {
449
+ const k = by / (rate.x * rate.x + rate.y * rate.y);
450
+ return { at: { x: near.x + rate.x * k, y: near.y + rate.y * k }, dirs: [{ x: -rate.y, y: rate.x }] };
451
+ }
452
+ function landedBox(q, delta) {
453
+ if (q.boxAt) return q.boxAt(delta);
454
+ if (!q.box) return null;
455
+ const shift = landedShift(q, delta);
456
+ return { ...q.box, x: q.box.x + shift.x, y: q.box.y + shift.y };
457
+ }
336
458
 
337
459
  // src/sources/grid.ts
460
+ var AXES = ["x", "y"];
338
461
  function gridSnapSource(opts) {
339
- const { scopes } = opts;
340
- const rootPitch = gridPitch(opts.spacing, opts.unitSystem, scopes);
462
+ const { scopes, gridOf } = opts;
463
+ const owners = { ...scopes ? { root: scopes.root } : {}, ...gridOf ? { gridOf } : {} };
464
+ const rootLines = gridLines(opts.spacing, opts.unitSystem, scopes);
465
+ const latticeOf = (q, owner) => {
466
+ if (owner === null) return { lines: rootLines };
467
+ const chain = scopes && q.scene ? sceneUnitScopes(q.scene, owner, scopes.root, { inclusive: true }) : void 0;
468
+ return {
469
+ lines: containerLines(gridOf?.(owner), opts.spacing, opts.unitSystem, scopes, chain),
470
+ frame: q.frameOf?.(owner)
471
+ };
472
+ };
341
473
  const latticeFor = (q) => {
342
474
  const scene = q.scene;
343
- if (!scopes || !scene) return { pitch: rootPitch };
344
- const within = (owner) => owner === null ? void 0 : q.frameOf?.(owner);
475
+ if (!scopes && !gridOf || !scene) return { lines: rootLines };
345
476
  if (q.ids?.length) {
346
- const ids = q.ids.map(asNodeId);
347
- const owners = new Set(ids.map((id) => scopeOwner(scene, id)));
348
- return {
349
- pitch: scopedGridPitch(opts.spacing, scopes, sceneSharedUnitScopes(scene, ids, scopes.root)),
350
- // Nodes in two containers share no one lattice, whatever their scopes.
351
- frame: owners.size === 1 ? within([...owners][0]) : void 0
352
- };
477
+ const held = [...new Set(q.ids.map((id) => gridOwner(scene, asNodeId(id), owners)))];
478
+ if (held.length === 1) return latticeOf(q, held[0]);
479
+ const each = held.map((owner) => JSON.stringify(latticeOf(q, owner).lines));
480
+ return { lines: each.every((l) => l === each[0]) ? latticeOf(q, held[0]).lines : null };
353
481
  }
354
- if (q.parent === void 0) return { pitch: rootPitch };
355
- const parent = asNodeId(q.parent);
356
- return {
357
- pitch: scopedGridPitch(opts.spacing, scopes, sceneUnitScopes(scene, parent, scopes.root, { inclusive: true })),
358
- frame: within(scopeOwner(scene, parent, true))
359
- };
482
+ if (q.parent === void 0) return { lines: rootLines };
483
+ return latticeOf(q, gridOwner(scene, asNodeId(q.parent), owners, true));
360
484
  };
361
485
  const o = opts.origin ?? { x: 0, y: 0 };
362
- const lines = (v, axis, pitch, plane) => {
363
- const s = plane ? plane.scale[axis] : 1;
364
- const near = roundToGridOn(v - o[axis] * s, axis, pitch[axis], plane) + o[axis] * s;
365
- const step = pitch[axis] * s;
366
- return [near, near + (v >= near ? step : -step)];
367
- };
368
486
  return {
369
- id: "grid",
487
+ id: opts.id ?? "grid",
370
488
  tier: "fallback",
371
489
  reachPx: Infinity,
372
- roles: ["origin", "pointer"],
490
+ roles: ["box", "origin", "pointer", "anchor"],
373
491
  *collect(q) {
374
- const { pitch, frame } = latticeFor(q);
492
+ const { lines, frame } = latticeFor(q);
493
+ if (!lines) return;
375
494
  const plane = q.plane ?? null;
376
- for (const p of q.probes) {
377
- if (frame) {
378
- yield* framedLines(p.id, p.at, pitch, frame, plane);
379
- continue;
380
- }
381
- for (const axis of ["x", "y"]) {
382
- for (const at of lines(p.at[axis], axis, pitch, plane)) yield { probe: p.id, locus: axisLine(axis, at, p.at) };
495
+ const scale = (axis) => plane ? plane.scale[axis] : 1;
496
+ const anchored = q.probes.some((p) => p.role === "anchor");
497
+ const skip = frame && turns(frame) ? "box" : "origin";
498
+ const probes = q.probes.filter((p) => anchored ? p.role === "anchor" || p.role === "pointer" : p.role !== skip);
499
+ if (frame) {
500
+ const local = { x: lines.x && mapLines(lines.x, scale("x"), 0), y: lines.y && mapLines(lines.y, scale("y"), 0) };
501
+ for (const p of probes) yield* framedLines(p.id, p.at, local, frame);
502
+ return;
503
+ }
504
+ const shift = (axis) => o[axis] * scale(axis) + (plane ? plane.offset[axis] : 0);
505
+ const world = { x: lines.x && mapLines(lines.x, scale("x"), shift("x")), y: lines.y && mapLines(lines.y, scale("y"), shift("y")) };
506
+ for (const p of probes) {
507
+ for (const axis of AXES) {
508
+ const a = world[axis];
509
+ if (a) for (const at of linesNear(a, p.at[axis])) yield { probe: p.id, locus: axisLine(axis, at, p.at) };
383
510
  }
384
511
  }
385
512
  }
386
513
  };
387
514
  }
388
- function* framedLines(probe, at, pitch, frame, plane) {
515
+ function turns(frame) {
516
+ const o = frame.toWorld({ x: 0, y: 0 });
517
+ const ex = frame.toWorld({ x: 1, y: 0 });
518
+ const ey = frame.toWorld({ x: 0, y: 1 });
519
+ const along = (a, axis) => {
520
+ const d = { x: a.x - o.x, y: a.y - o.y };
521
+ return axis === "x" ? Math.abs(d.y) <= 1e-9 * Math.abs(d.x) : Math.abs(d.x) <= 1e-9 * Math.abs(d.y);
522
+ };
523
+ return !(along(ex, "x") && along(ey, "y") || along(ex, "y") && along(ey, "x"));
524
+ }
525
+ function* framedLines(probe, at, lines, frame) {
389
526
  const local = frame.toLocal(at);
390
527
  if (!local) return;
391
- for (const axis of ["x", "y"]) {
392
- const step = pitch[axis] * (plane ? plane.scale[axis] : 1);
393
- const near = Math.round(local[axis] / step) * step;
394
- for (const offset of [near, near + (local[axis] >= near ? step : -step)]) {
395
- yield { probe, locus: frameLine(frame, axis, offset, local) };
396
- }
528
+ for (const axis of AXES) {
529
+ const a = lines[axis];
530
+ if (a) for (const offset of linesNear(a, local[axis])) yield { probe, locus: frameLine(frame, axis, offset, local) };
397
531
  }
398
532
  }
399
533
 
@@ -405,6 +539,7 @@ function createGridPreset(opts) {
405
539
  id,
406
540
  layers: { grid: { layer: createGridLayer(layer) } },
407
541
  snapSources: snap === false ? [] : [gridSnapSource({
542
+ id,
408
543
  spacing,
409
544
  ...unitSystem === void 0 ? {} : { unitSystem },
410
545
  ...scopes === void 0 ? {} : { scopes },
@@ -482,7 +617,7 @@ function createGuidesLayer(opts) {
482
617
  }
483
618
  };
484
619
  }
485
- var EPS = 1e-3;
620
+ var EPS2 = 1e-3;
486
621
  function deriveAlignmentGuides(targets, opts = {}) {
487
622
  const d = opts.poseDescriptor ?? AUTO_POSE_DESCRIPTOR;
488
623
  const edges = opts.edges ?? true;
@@ -491,7 +626,7 @@ function deriveAlignmentGuides(targets, opts = {}) {
491
626
  const seenY = /* @__PURE__ */ new Map();
492
627
  const add = (axis, offset, b) => {
493
628
  const seen = axis === "x" ? seenX : seenY;
494
- const key = Math.round(offset / EPS);
629
+ const key = Math.round(offset / EPS2);
495
630
  const min = axis === "x" ? b.y : b.x;
496
631
  const max = min + (axis === "x" ? b.height : b.width);
497
632
  const prev = seen.get(key);
@@ -523,14 +658,14 @@ function deriveAlignmentGuides(targets, opts = {}) {
523
658
  }
524
659
 
525
660
  // src/guides/alignment/spacing.ts
526
- var EPS2 = 1e-6;
661
+ var EPS3 = 1e-6;
527
662
  var lo = (b, a) => a === "x" ? b.x : b.y;
528
663
  var hi = (b, a) => a === "x" ? b.x + b.width : b.y + b.height;
529
664
  var cross = (a) => a === "x" ? "y" : "x";
530
665
  function overlap(a, b, axis) {
531
666
  const min = Math.max(lo(a, axis), lo(b, axis));
532
667
  const max = Math.min(hi(a, axis), hi(b, axis));
533
- return max - min > EPS2 ? [min, max] : null;
668
+ return max - min > EPS3 ? [min, max] : null;
534
669
  }
535
670
  function gapBetween(a, b, axis) {
536
671
  const shared = overlap(a, b, cross(axis));
@@ -539,7 +674,7 @@ function gapBetween(a, b, axis) {
539
674
  }
540
675
  function hitsStrip(g, b, span2) {
541
676
  const c = cross(g.axis);
542
- return lo(b, g.axis) < g.max - EPS2 && hi(b, g.axis) > g.min + EPS2 && lo(b, c) < span2[1] - EPS2 && hi(b, c) > span2[0] + EPS2;
677
+ return lo(b, g.axis) < g.max - EPS3 && hi(b, g.axis) > g.min + EPS3 && lo(b, c) < span2[1] - EPS3 && hi(b, c) > span2[0] + EPS3;
543
678
  }
544
679
  function measureGaps(targets, axis) {
545
680
  return measureGapSpans(targets, axis).map((m) => m.gap);
@@ -549,7 +684,7 @@ function measureGapSpans(targets, axis) {
549
684
  for (const a of targets) {
550
685
  let next = null;
551
686
  for (const c of targets) {
552
- if (c === a || lo(c, axis) < hi(a, axis) + EPS2) continue;
687
+ if (c === a || lo(c, axis) < hi(a, axis) + EPS3) continue;
553
688
  if (overlap(a, c, cross(axis)) === null) continue;
554
689
  if (next === null || lo(c, axis) < lo(next, axis)) next = c;
555
690
  }
@@ -576,14 +711,14 @@ function spacingOffers(b, axis, edge, targets, refs) {
576
711
  const sizes = refs.map((g) => g.max - g.min);
577
712
  const out = [];
578
713
  const offer = (delta, gap) => {
579
- if (gap > EPS2) out.push({ delta, size: gap });
714
+ if (gap > EPS3) out.push({ delta, size: gap });
580
715
  };
581
716
  const L = left === null ? null : hi(left, axis);
582
717
  const R = right === null ? null : lo(right, axis);
583
718
  if (edge === "both") {
584
719
  for (const g of sizes) {
585
- if (L !== null && (R === null || L + g + size <= R + EPS2)) offer(L + g - bLo, g);
586
- if (R !== null && (L === null || R - g - size >= L - EPS2)) offer(R - g - size - bLo, g);
720
+ if (L !== null && (R === null || L + g + size <= R + EPS3)) offer(L + g - bLo, g);
721
+ if (R !== null && (L === null || R - g - size >= L - EPS3)) offer(R - g - size - bLo, g);
587
722
  }
588
723
  if (L !== null && R !== null) {
589
724
  const g = (R - L - size) / 2;
@@ -591,10 +726,10 @@ function spacingOffers(b, axis, edge, targets, refs) {
591
726
  }
592
727
  } else if (edge === "max" && R !== null) {
593
728
  const own = L !== null ? [bLo - L] : [];
594
- for (const g of [...sizes, ...own]) if (R - g > bLo + EPS2) offer(R - g - bHi, g);
729
+ for (const g of [...sizes, ...own]) if (R - g > bLo + EPS3) offer(R - g - bHi, g);
595
730
  } else if (edge === "min" && L !== null) {
596
731
  const own = R !== null ? [R - bHi] : [];
597
- for (const g of [...sizes, ...own]) if (L + g < bHi - EPS2) offer(L + g - bLo, g);
732
+ for (const g of [...sizes, ...own]) if (L + g < bHi - EPS3) offer(L + g - bLo, g);
598
733
  }
599
734
  return out;
600
735
  }
@@ -609,8 +744,8 @@ function bestAxis(b, axis, edge, targets, refs, tol) {
609
744
  function equalGaps(b, axis, size, targets, refs) {
610
745
  const eq = (g) => Math.abs(g.max - g.min - size) < 1e-3;
611
746
  const out = refs.filter(eq);
612
- const near = targets.filter((t) => hi(t, axis) > lo(b, axis) - size - EPS2 && lo(t, axis) < hi(b, axis) + size + EPS2);
613
- const own = measureGaps([...near, b], axis).filter((g) => (Math.abs(g.max - lo(b, axis)) < EPS2 || Math.abs(g.min - hi(b, axis)) < EPS2) && eq(g));
747
+ const near = targets.filter((t) => hi(t, axis) > lo(b, axis) - size - EPS3 && lo(t, axis) < hi(b, axis) + size + EPS3);
748
+ const own = measureGaps([...near, b], axis).filter((g) => (Math.abs(g.max - lo(b, axis)) < EPS3 || Math.abs(g.min - hi(b, axis)) < EPS3) && eq(g));
614
749
  return [...out, ...own];
615
750
  }
616
751
  function referenceGaps(bounds, measured) {
@@ -681,11 +816,16 @@ function siblingBounds(scene, ids, opts = {}) {
681
816
  return worldBoundsOf(scene, siblingIds(scene, ids), opts);
682
817
  }
683
818
 
819
+ // src/strategies/onPlane.ts
820
+ function guideOffsetOn(g, plane) {
821
+ return plane ? g.offset * plane.scale[g.axis] + plane.offset[g.axis] : g.offset;
822
+ }
823
+
684
824
  // src/sources/guides.ts
685
825
  function guideSnapSource(getGuides) {
686
826
  return {
687
827
  id: "guides",
688
- roles: ["origin", "pointer"],
828
+ roles: ["box", "pointer"],
689
829
  *collect(q) {
690
830
  const guides = getGuides();
691
831
  if (guides.length === 0) return;
@@ -775,7 +915,6 @@ function alignmentSnapSource(opts = {}) {
775
915
  const indexes = linesFor(q);
776
916
  if (indexes.length === 0) return;
777
917
  const reach = q.reachWorld(q.reachPx);
778
- const box = q.box ?? null;
779
918
  const probes = q.probes.some((p) => p.role === "box") ? q.probes.filter((p) => p.role === "box") : q.probes;
780
919
  for (const p of probes) {
781
920
  for (const axis of ["x", "y"]) {
@@ -784,7 +923,7 @@ function alignmentSnapSource(opts = {}) {
784
923
  yield {
785
924
  probe: p.id,
786
925
  locus: axisLine(axis, g.offset, p.at),
787
- mark: (delta) => lineMark(g, extentAcross(p, box, axis, landedShift(q, delta), q.edges), opts.color)
926
+ mark: (delta) => lineMark(g, extentAcross(q, p, axis, delta), opts.color)
788
927
  };
789
928
  }
790
929
  }
@@ -793,15 +932,25 @@ function alignmentSnapSource(opts = {}) {
793
932
  }
794
933
  };
795
934
  }
796
- function extentAcross(p, box, axis, shift, edges = { x: "both", y: "both" }) {
935
+ function extentAcross(q, p, axis, delta) {
797
936
  const c = cross2(axis);
798
- if (p.role !== "box" || box === null) return [p.at[c] + shift[c], p.at[c] + shift[c]];
799
- const landed = applyEdges(box, shift.x, shift.y, edges);
937
+ const landed = p.role === "box" ? landedBox(q, delta) : null;
938
+ if (landed === null) {
939
+ const shift = landedShift(q, delta);
940
+ return [p.at[c] + shift[c], p.at[c] + shift[c]];
941
+ }
800
942
  const lo3 = c === "x" ? landed.x : landed.y;
801
943
  return [lo3, lo3 + (c === "x" ? landed.width : landed.height)];
802
944
  }
803
945
 
804
946
  // src/sources/spacing.ts
947
+ var TRANSLATE = [
948
+ { axis: "x", edge: "both", rate: { x: 1, y: 0 } },
949
+ { axis: "y", edge: "both", rate: { x: 0, y: 1 } }
950
+ ];
951
+ function movingSides(q) {
952
+ return q.sides ? q.sides.map((s) => ({ axis: s.axis, edge: s.side, rate: s.rate })) : TRANSLATE;
953
+ }
805
954
  var measureAll = (boxes) => ({ boxes, x: measureGapSpans(boxes, "x"), y: measureGapSpans(boxes, "y") });
806
955
  function gapMark(g, color) {
807
956
  const ends = g.axis === "x" ? { from: { x: g.min, y: g.at }, to: { x: g.max, y: g.at } } : { from: { x: g.at, y: g.min }, to: { x: g.at, y: g.max } };
@@ -820,23 +969,20 @@ function spacingSnapSource(opts = {}) {
820
969
  const measured = opts.getTargets ? measureAll(opts.getTargets()) : fromScene(q);
821
970
  if (!box || !probe || !measured) return;
822
971
  const reach = q.reachWorld(q.reachPx);
823
- const edges = q.edges ?? { x: "both", y: "both" };
824
- for (const axis of ["x", "y"]) {
825
- const edge = edges[axis];
826
- if (edge === null) continue;
827
- const refs = referenceGaps(box, measured[axis]);
828
- const seen = /* @__PURE__ */ new Set();
829
- for (const o of spacingOffers(box, axis, edge, measured.boxes, refs)) {
830
- const key = `${o.delta.toFixed(6)}:${o.size.toFixed(6)}`;
831
- if (Math.abs(o.delta) > reach || seen.has(key)) continue;
972
+ const refs = { x: referenceGaps(box, measured.x), y: referenceGaps(box, measured.y) };
973
+ const seen = /* @__PURE__ */ new Set();
974
+ for (const { axis, edge, rate } of movingSides(q)) {
975
+ const speed = Math.hypot(rate.x, rate.y);
976
+ for (const o of spacingOffers(box, axis, edge, measured.boxes, refs[axis])) {
977
+ const key = `${axis}:${edge}:${o.delta.toFixed(6)}:${o.size.toFixed(6)}`;
978
+ if (Math.abs(o.delta) / speed > reach || seen.has(key)) continue;
832
979
  seen.add(key);
833
980
  yield {
834
981
  probe: probe.id,
835
- locus: axisLine(axis, probe.at[axis] + o.delta, probe.at),
982
+ locus: sideLine(probe.at, rate, o.delta),
836
983
  mark: (delta) => {
837
- const shift = landedShift(q, delta);
838
- const landed = applyEdges(box, shift.x, shift.y, edges);
839
- return equalGaps(landed, axis, o.size, measured.boxes, refs).map((g) => gapMark(g, opts.color));
984
+ const landed = landedBox(q, delta) ?? box;
985
+ return equalGaps(landed, axis, o.size, measured.boxes, refs[axis]).map((g) => gapMark(g, opts.color));
840
986
  }
841
987
  };
842
988
  }
@@ -846,7 +992,7 @@ function spacingSnapSource(opts = {}) {
846
992
  }
847
993
 
848
994
  // src/spacingHints/distance.ts
849
- var EPS3 = 1e-6;
995
+ var EPS4 = 1e-6;
850
996
  var lo2 = (b, a) => a === "x" ? b.x : b.y;
851
997
  var hi2 = (b, a) => a === "x" ? b.x + b.width : b.y + b.height;
852
998
  var mid = (b, a) => (lo2(b, a) + hi2(b, a)) / 2;
@@ -860,15 +1006,15 @@ function measureAxis(from, to, axis, out) {
860
1006
  const c = cross3(axis);
861
1007
  const sharedMin = Math.max(lo2(from, c), lo2(to, c));
862
1008
  const sharedMax = Math.min(hi2(from, c), hi2(to, c));
863
- const shares = sharedMax - sharedMin > EPS3;
1009
+ const shares = sharedMax - sharedMin > EPS4;
864
1010
  const push = (min, max, at2) => {
865
- if (max - min > EPS3) out.gaps.push({ axis, min, max, at: at2 });
1011
+ if (max - min > EPS4) out.gaps.push({ axis, min, max, at: at2 });
866
1012
  };
867
- const before = hi2(from, axis) <= lo2(to, axis) + EPS3;
868
- const after = hi2(to, axis) <= lo2(from, axis) + EPS3;
1013
+ const before = hi2(from, axis) <= lo2(to, axis) + EPS4;
1014
+ const after = hi2(to, axis) <= lo2(from, axis) + EPS4;
869
1015
  if (before || after) {
870
1016
  const [min, max] = before ? [hi2(from, axis), lo2(to, axis)] : [hi2(to, axis), lo2(from, axis)];
871
- if (max - min <= EPS3) return;
1017
+ if (max - min <= EPS4) return;
872
1018
  const at2 = shares ? (sharedMin + sharedMax) / 2 : mid(from, c);
873
1019
  push(min, max, at2);
874
1020
  if (!shares) {
@@ -879,7 +1025,7 @@ function measureAxis(from, to, axis, out) {
879
1025
  return;
880
1026
  }
881
1027
  if (!shares) return;
882
- const [inner, outer] = lo2(from, axis) >= lo2(to, axis) - EPS3 && hi2(from, axis) <= hi2(to, axis) + EPS3 ? [from, to] : lo2(to, axis) >= lo2(from, axis) - EPS3 && hi2(to, axis) <= hi2(from, axis) + EPS3 ? [to, from] : [null, null];
1028
+ const [inner, outer] = lo2(from, axis) >= lo2(to, axis) - EPS4 && hi2(from, axis) <= hi2(to, axis) + EPS4 ? [from, to] : lo2(to, axis) >= lo2(from, axis) - EPS4 && hi2(to, axis) <= hi2(from, axis) + EPS4 ? [to, from] : [null, null];
883
1029
  if (inner === null || outer === null) return;
884
1030
  const at = mid(inner, c);
885
1031
  push(lo2(outer, axis), lo2(inner, axis), at);
@@ -1759,6 +1905,6 @@ function ringSlots(opts = {}) {
1759
1905
  };
1760
1906
  }
1761
1907
 
1762
- export { alignmentSnapSource, cellAt, containedThenNearest, createCellHighlightLayer, createGridLayer, createGridPreset, createGuidesLayer, createSpacingHintsPreset, deriveAlignmentGuides, distanceReadout, freeform, gridPitch, gridSlots, gridSnapSource, guideSnapSource, matchSpacing, measureDistances, measureGaps, nearest, nearestWithin, none, pack, perContainer, pointToGridCell, quadtree, regionAt, ringSlots, roundToCell, rowSlots, sceneGridRegions, scopedGridPitch, sequenceLayout, siblingBounds, slots, snapPoint, spacingSnapSource, tileGrid, useGridCellHover, useGuides, worldBoundsOf, worldUnionOf };
1908
+ export { alignmentSnapSource, cellAt, cellOf, containedThenNearest, containerLines, createCellHighlightLayer, createGridLayer, createGridPreset, createGuidesLayer, createSpacingHintsPreset, deriveAlignmentGuides, distanceReadout, freeform, gridFit, gridLines, gridOwner, gridSlots, gridSnapSource, guideSnapSource, linesIn, linesNear, mapLines, matchSpacing, measureDistances, measureGaps, nearest, nearestLine, nearestWithin, none, pack, perContainer, pointToGridCell, quadtree, regionAt, resolveGridLines, ringSlots, roundToCell, rowSlots, sceneGridRegions, sequenceLayout, siblingBounds, slots, snapPoint, spacingSnapSource, tileGrid, useGridCellHover, useGuides, worldBoundsOf, worldUnionOf };
1763
1909
  //# sourceMappingURL=index.js.map
1764
1910
  //# sourceMappingURL=index.js.map