@weasel-js/guides 1.13.0-rc.0 → 1.14.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,16 +1,92 @@
1
1
  import { useState, useRef, useCallback, useEffect } from 'react';
2
- import { resolveAxes, useLatest, viewportWorldRect, pxExtent, tickedSegment, measuredSegmentCommands, AUTO_POSE_DESCRIPTOR, visualBoundsViaDescriptor, rectFromPlane, scenePoseFrame, unionAABB, createTransformOp, sceneSharedUnitScopes, asNodeId, sceneUnitScopes, screenToWorld, nodeGates, resolveStrokeWidth, resolveScoped } from '@weasel-js/core';
3
- import { PATH_M, PATH_L } from '@weasel-js/geom';
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';
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 pointToGridCell(point, spacing, unitSystem, origin = { x: 0, y: 0 }) {
10
- const s = resolveAxes(spacing, unitSystem);
9
+ function sceneGridRegions(scene, root, outlineOf) {
10
+ const frames = sceneBoxFrames(scene);
11
+ const poses = scenePoseFrame(scene);
12
+ const boxOutline = (id, frame) => {
13
+ const { width, height } = poses.world(id);
14
+ return [[0, 0], [width, 0], [width, height], [0, height]].map(([x, y]) => {
15
+ const [wx, wy] = applyToPoint(frame, x, y);
16
+ return { x: wx, y: wy };
17
+ });
18
+ };
19
+ const children = /* @__PURE__ */ new Map();
20
+ for (const id of scene.nodes.keys()) {
21
+ if (scene.get(id)?.units === void 0) continue;
22
+ const parent = scopeOwner(scene, id);
23
+ children.set(parent, [...children.get(parent) ?? [], id]);
24
+ }
25
+ const build = (parent) => {
26
+ const out = [];
27
+ for (const id of children.get(parent) ?? []) {
28
+ const frame = frames(id);
29
+ const outline = outlineOf ? outlineOf(id) : frame && boxOutline(id, frame);
30
+ if (!outline) continue;
31
+ const inner = build(id);
32
+ out.push({
33
+ outline,
34
+ chain: sceneUnitScopes(scene, id, root, { inclusive: true }),
35
+ ...inner.length ? { regions: inner } : {},
36
+ ...frame ? { frame } : {}
37
+ });
38
+ }
39
+ return out;
40
+ };
41
+ return build(null);
42
+ }
43
+ function scopeOwner(scene, id, inclusive = false) {
44
+ 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;
46
+ }
47
+ return null;
48
+ }
49
+ function regionAt(regions, point) {
50
+ for (const r of regions ?? []) {
51
+ const flat = r.outline.flatMap((p) => [p.x, p.y]);
52
+ if (pointInPolygon(flat, point.x, point.y)) return regionAt(r.regions, point) ?? r;
53
+ }
54
+ return null;
55
+ }
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
+
77
+ // src/grid/pointToGridCell.ts
78
+ 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 };
85
+ }
11
86
  return {
12
87
  col: Math.floor((point.x - origin.x) / s.x),
13
- row: Math.floor((point.y - origin.y) / s.y)
88
+ row: Math.floor((point.y - origin.y) / s.y),
89
+ ...scoped ? { pitch: s } : {}
14
90
  };
15
91
  }
16
92
 
@@ -18,7 +94,7 @@ function pointToGridCell(point, spacing, unitSystem, origin = { x: 0, y: 0 }) {
18
94
  function cellsEqual(a, b) {
19
95
  if (a === b) return true;
20
96
  if (a === null || b === null) return false;
21
- return a.col === b.col && a.row === b.row;
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]));
22
98
  }
23
99
  function useGridCellHover(opts) {
24
100
  const [cell, setCell] = useState(null);
@@ -42,7 +118,8 @@ function useGridCellHover(opts) {
42
118
  const sx = e.clientX - rect.left;
43
119
  const sy = e.clientY - rect.top;
44
120
  const [wx, wy] = screenToWorld(sx, sy, o.view());
45
- update(pointToGridCell({ x: wx, y: wy }, o.spacing, o.unitSystem, o.origin));
121
+ const scoped = o.scopes ? { scopes: o.scopes, ...o.regions ? { regions: o.regions() } : {} } : void 0;
122
+ update(pointToGridCell({ x: wx, y: wy }, o.spacing, o.unitSystem, o.origin, scoped));
46
123
  };
47
124
  const onLeave = () => update(null);
48
125
  el.addEventListener("pointermove", onMove);
@@ -56,6 +133,66 @@ function useGridCellHover(opts) {
56
133
  }, [enabled, opts.ref, optsRef]);
57
134
  return { cell, getCell };
58
135
  }
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
59
196
  var DEFAULT_LINE = {
60
197
  paint: { kind: "solid", color: "rgba(255, 255, 255, 0.08)" },
61
198
  width: 1
@@ -68,72 +205,85 @@ var DEFAULT_SUB = {
68
205
  paint: { kind: "solid", color: "rgba(255, 255, 255, 0.04)" },
69
206
  width: 1
70
207
  };
71
- var MIN_SCREEN_SPACING = 4;
72
208
  function createGridLayer(opts) {
73
- const line = opts.style?.line ?? DEFAULT_LINE;
74
- const accent = opts.style?.accent ?? DEFAULT_ACCENT;
75
- const sub = opts.style?.sub ?? DEFAULT_SUB;
209
+ const styles = {
210
+ line: opts.style?.line ?? DEFAULT_LINE,
211
+ accent: opts.style?.accent ?? DEFAULT_ACCENT,
212
+ sub: opts.style?.sub ?? DEFAULT_SUB
213
+ };
76
214
  return {
77
215
  id: "grid",
78
216
  label: "Grid",
79
217
  draw: (_data, view, dims) => {
80
- const { accentEvery, subdivisions } = opts;
81
- const spacing = resolveAxes(opts.spacing, opts.unitSystem);
82
- const b = opts.bounds ? opts.bounds(view, dims) : onLattice(viewportWorldRect(view, dims), spacing, accentEvery && accentEvery > 0 ? accentEvery : 1);
83
- if (!(b.width > 0 && b.height > 0 && Number.isFinite(b.width) && Number.isFinite(b.height))) return [];
84
- const x0 = b.x;
85
- const y0 = b.y;
86
- const x1 = b.x + b.width;
87
- const y1 = b.y + b.height;
88
- const px = pxExtent(1, view.scale);
89
- const children = [];
90
- const pushSegment = (sx, sy, ex, ey, s) => {
91
- const path = {
92
- kind: "polygon",
93
- commands: new Uint8Array([PATH_M, PATH_L]),
94
- coords: new Float32Array([sx, sy, ex, ey]),
95
- fillRule: "nonzero"
96
- };
97
- const across = sx === ex ? px.x : px.y;
98
- children.push({
99
- kind: "path",
100
- path,
101
- stroke: { ...s, width: resolveStrokeWidth(s.width ?? 1, 1) * across }
102
- });
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 });
222
+ 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
+ const anchor = opts.bounds ? { x: b.x, y: b.y } : { x: 0, y: 0 };
227
+ const area = opts.bounds ? b : shown;
228
+ const children = [{ kind: "group", clip: withHoles(rectOutline(b), regions), children: root }];
229
+ 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));
232
+ if (lines.length) children.push({ kind: "group", clip: withHoles(r.outline, r.regions ?? []), children: lines });
233
+ for (const inner of r.regions ?? []) visit(inner);
103
234
  };
104
- const family = (axis, step, skipEvery, s) => {
105
- if (!(step * Math.abs(view.scale[axis]) >= MIN_SCREEN_SPACING)) return;
106
- const [from, to] = axis === "x" ? [x0, x1] : [y0, y1];
107
- for (let k = 0; from + k * step <= to + 1e-9; k++) {
108
- if (skipEvery > 0 && k % skipEvery === 0) continue;
109
- const at = from + k * step;
110
- if (axis === "x") pushSegment(at, y0, at, y1, s);
111
- else pushSegment(x0, at, x1, at, s);
112
- }
113
- };
114
- const accentOn = accentEvery !== void 0 && accentEvery > 0;
115
- for (const axis of ["x", "y"]) {
116
- if (subdivisions && subdivisions > 1) family(axis, spacing[axis] / subdivisions, subdivisions, sub);
117
- }
118
- for (const axis of ["x", "y"]) family(axis, spacing[axis], accentOn ? accentEvery : 0, line);
119
- if (accentOn) {
120
- for (const axis of ["x", "y"]) family(axis, spacing[axis] * accentEvery, 0, accent);
121
- }
235
+ for (const r of regions) visit(r);
122
236
  return children;
123
237
  }
124
238
  };
125
239
  }
126
- function onLattice(r, spacing, accentEvery) {
127
- const px = spacing.x * accentEvery;
128
- const py = spacing.y * accentEvery;
129
- const x = Math.floor(r.x / px) * px;
130
- const y = Math.floor(r.y / py) * py;
131
- return {
132
- x,
133
- y,
134
- width: Math.ceil((r.x + r.width) / px) * px - x,
135
- height: Math.ceil((r.y + r.height) / py) * py - y
136
- };
240
+ function rectOutline(r) {
241
+ return [
242
+ { x: r.x, y: r.y },
243
+ { x: r.x + r.width, y: r.y },
244
+ { x: r.x + r.width, y: r.y + r.height },
245
+ { x: r.x, y: r.y + r.height }
246
+ ];
247
+ }
248
+ function withHoles(outline, holes) {
249
+ const commands = [];
250
+ const coords = [];
251
+ for (const contour of [outline, ...holes.map((h) => h.outline)]) {
252
+ contour.forEach((p, i) => {
253
+ commands.push(i === 0 ? PATH_M : PATH_L);
254
+ coords.push(p.x, p.y);
255
+ });
256
+ commands.push(PATH_Z);
257
+ }
258
+ return { kind: "polygon", commands: new Uint8Array(commands), coords: new Float32Array(coords), fillRule: "evenodd" };
259
+ }
260
+ function regionLattice(r, area, draw) {
261
+ if (!r.frame) {
262
+ const box2 = intersect(outlineBox(r.outline), area);
263
+ return hasArea(box2) ? draw(box2) : [];
264
+ }
265
+ const back = invert(r.frame);
266
+ if (!back) return [];
267
+ const local = (ps) => outlineBox(ps.map((p) => {
268
+ const [x, y] = applyToPoint(back, p.x, p.y);
269
+ return { x, y };
270
+ }));
271
+ const box = intersect(local(r.outline), local(rectOutline(area)));
272
+ if (!hasArea(box)) return [];
273
+ const lines = draw(box, r.frame);
274
+ return lines.length ? [{ kind: "group", transform: mat3.fromAffine(r.frame), children: lines }] : [];
275
+ }
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 };
137
287
  }
138
288
  var DEFAULT_FILL = { paint: { kind: "solid", color: "rgba(127,176,105,0.15)" } };
139
289
  function createCellHighlightLayer(opts) {
@@ -144,8 +294,8 @@ function createCellHighlightLayer(opts) {
144
294
  draw: () => {
145
295
  const cell = opts.getCell();
146
296
  if (!cell) return [];
147
- const spacing = resolveAxes(opts.spacing, opts.unitSystem);
148
- const o = opts.origin ? opts.origin() : { x: 0, y: 0 };
297
+ const spacing = cell.pitch ?? resolveAxes(opts.spacing, opts.unitSystem);
298
+ const o = cell.frame || !opts.origin ? { x: 0, y: 0 } : opts.origin();
149
299
  const x = o.x + cell.col * spacing.x;
150
300
  const y = o.y + cell.row * spacing.y;
151
301
  const children = [
@@ -155,26 +305,112 @@ function createCellHighlightLayer(opts) {
155
305
  fill
156
306
  }
157
307
  ];
158
- return children;
308
+ return cell.frame ? [{ kind: "group", transform: mat3.fromAffine(cell.frame), children }] : children;
159
309
  }
160
310
  };
161
311
  }
162
- function span(v, chain) {
163
- const zero = typeof v === "number" ? 0 : { ...v, value: 0 };
164
- return resolveScoped(v, chain) - resolveScoped(zero, chain);
312
+
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;
165
316
  }
166
- function scopedGridPitch(spacing, scopes, chain) {
167
- const inner = chain.length > 1 ? chain[chain.length - 1] : null;
168
- const v = inner && scopes.spacing ? scopes.spacing(inner) : spacing;
169
- const on = (axis) => {
170
- const a = typeof v === "object" && "x" in v ? v[axis] : v;
171
- try {
172
- return span(a, chain);
173
- } catch (e) {
174
- throw new Error(`${axis} axis: ${e instanceof Error ? e.message : String(e)}`);
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];
321
+ }
322
+
323
+ // src/sources/lines.ts
324
+ function axisLine(axis, offset, near) {
325
+ return axis === "x" ? { at: { x: offset, y: near.y }, dirs: [{ x: 0, y: 1 }] } : { at: { x: near.x, y: offset }, dirs: [{ x: 1, y: 0 }] };
326
+ }
327
+ function frameLine(frame, axis, offset, near) {
328
+ const on = axis === "x" ? { x: offset, y: near.y } : { x: near.x, y: offset };
329
+ const at = frame.toWorld(on);
330
+ const tip = frame.toWorld(axis === "x" ? { x: offset, y: near.y + 1 } : { x: near.x + 1, y: offset });
331
+ return { at, dirs: [{ x: tip.x - at.x, y: tip.y - at.y }] };
332
+ }
333
+ function landedShift(q, delta) {
334
+ return { x: delta.x - q.delta0.x, y: delta.y - q.delta0.y };
335
+ }
336
+
337
+ // src/sources/grid.ts
338
+ function gridSnapSource(opts) {
339
+ const { scopes } = opts;
340
+ const rootPitch = gridPitch(opts.spacing, opts.unitSystem, scopes);
341
+ const latticeFor = (q) => {
342
+ const scene = q.scene;
343
+ if (!scopes || !scene) return { pitch: rootPitch };
344
+ const within = (owner) => owner === null ? void 0 : q.frameOf?.(owner);
345
+ 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
+ };
175
353
  }
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
+ };
360
+ };
361
+ 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
+ return {
369
+ id: "grid",
370
+ tier: "fallback",
371
+ reachPx: Infinity,
372
+ roles: ["origin", "pointer"],
373
+ *collect(q) {
374
+ const { pitch, frame } = latticeFor(q);
375
+ 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) };
383
+ }
384
+ }
385
+ }
386
+ };
387
+ }
388
+ function* framedLines(probe, at, pitch, frame, plane) {
389
+ const local = frame.toLocal(at);
390
+ 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
+ }
397
+ }
398
+ }
399
+
400
+ // src/grid/preset.ts
401
+ function createGridPreset(opts) {
402
+ const { id = "grid", snap = {}, ...layer } = opts;
403
+ const { spacing, unitSystem, scopes } = layer;
404
+ return {
405
+ id,
406
+ layers: { grid: { layer: createGridLayer(layer) } },
407
+ snapSources: snap === false ? [] : [gridSnapSource({
408
+ spacing,
409
+ ...unitSystem === void 0 ? {} : { unitSystem },
410
+ ...scopes === void 0 ? {} : { scopes },
411
+ ...snap
412
+ })]
176
413
  };
177
- return { x: on("x"), y: on("y") };
178
414
  }
179
415
  function useGuides(initial = []) {
180
416
  const [guides, setGuides] = useState(initial);
@@ -445,57 +681,6 @@ function siblingBounds(scene, ids, opts = {}) {
445
681
  return worldBoundsOf(scene, siblingIds(scene, ids), opts);
446
682
  }
447
683
 
448
- // src/strategies/onPlane.ts
449
- function guideOffsetOn(g, plane) {
450
- return plane ? g.offset * plane.scale[g.axis] + plane.offset[g.axis] : g.offset;
451
- }
452
- function roundToGridOn(value, axis, spacing, plane) {
453
- if (!plane) return Math.round(value / spacing) * spacing;
454
- const camera = (value - plane.offset[axis]) / plane.scale[axis];
455
- return Math.round(camera / spacing) * spacing * plane.scale[axis] + plane.offset[axis];
456
- }
457
-
458
- // src/sources/lines.ts
459
- function axisLine(axis, offset, near) {
460
- return axis === "x" ? { at: { x: offset, y: near.y }, dirs: [{ x: 0, y: 1 }] } : { at: { x: near.x, y: offset }, dirs: [{ x: 1, y: 0 }] };
461
- }
462
- function landedShift(q, delta) {
463
- return { x: delta.x - q.delta0.x, y: delta.y - q.delta0.y };
464
- }
465
-
466
- // src/sources/grid.ts
467
- function gridSnapSource(opts) {
468
- const { scopes } = opts;
469
- const rootPitch = scopes ? scopedGridPitch(opts.spacing, scopes, [scopes.root]) : resolveAxes(opts.spacing, opts.unitSystem);
470
- const pitchFor = (q) => {
471
- if (!scopes || !q.scene) return rootPitch;
472
- if (q.ids?.length) return scopedGridPitch(opts.spacing, scopes, sceneSharedUnitScopes(q.scene, q.ids.map(asNodeId), scopes.root));
473
- if (q.parent === void 0) return rootPitch;
474
- return scopedGridPitch(opts.spacing, scopes, sceneUnitScopes(q.scene, asNodeId(q.parent), scopes.root, { inclusive: true }));
475
- };
476
- const o = opts.origin ?? { x: 0, y: 0 };
477
- const lines = (v, axis, pitch, plane) => {
478
- const s = plane ? plane.scale[axis] : 1;
479
- const near = roundToGridOn(v - o[axis] * s, axis, pitch[axis], plane) + o[axis] * s;
480
- const step = pitch[axis] * s;
481
- return [near, near + (v >= near ? step : -step)];
482
- };
483
- return {
484
- id: "grid",
485
- tier: "fallback",
486
- reachPx: Infinity,
487
- roles: ["origin", "pointer"],
488
- *collect(q) {
489
- const pitch = pitchFor(q);
490
- for (const p of q.probes) {
491
- for (const axis of ["x", "y"]) {
492
- for (const at of lines(p.at[axis], axis, pitch, q.plane ?? null)) yield { probe: p.id, locus: axisLine(axis, at, p.at) };
493
- }
494
- }
495
- }
496
- };
497
- }
498
-
499
684
  // src/sources/guides.ts
500
685
  function guideSnapSource(getGuides) {
501
686
  return {
@@ -746,11 +931,11 @@ function distanceReadout(opts = {}) {
746
931
  const { selection, scene, nodeAtPoint, poseDescriptor, snapReadout } = deps;
747
932
  if (snapReadout !== void 0) store = snapReadout.store;
748
933
  const ids = selection?.get() ?? [];
749
- if (p?.show !== true || p.worldX === void 0 || p.worldY === void 0 || scene === void 0 || nodeAtPoint === void 0 || ids.length === 0) {
934
+ if (p?.show !== true || p.world === void 0 || scene === void 0 || nodeAtPoint === void 0 || ids.length === 0) {
750
935
  show(EMPTY);
751
936
  return;
752
937
  }
753
- const hit = nodeAtPoint({ x: p.worldX, y: p.worldY }, subtreeOf(scene, ids));
938
+ const hit = nodeAtPoint(p.world, subtreeOf(scene, ids));
754
939
  const from = worldUnionOf(scene, ids, { poseDescriptor });
755
940
  const [to] = hit === null ? [] : worldBoundsOf(scene, [hit], { poseDescriptor });
756
941
  show(from === null || to === void 0 ? EMPTY : measureDistances(from, to));
@@ -1574,6 +1759,6 @@ function ringSlots(opts = {}) {
1574
1759
  };
1575
1760
  }
1576
1761
 
1577
- export { alignmentSnapSource, cellAt, containedThenNearest, createCellHighlightLayer, createGridLayer, createGuidesLayer, createSpacingHintsPreset, deriveAlignmentGuides, distanceReadout, freeform, gridSlots, gridSnapSource, guideSnapSource, matchSpacing, measureDistances, measureGaps, nearest, nearestWithin, none, pack, perContainer, pointToGridCell, quadtree, ringSlots, roundToCell, rowSlots, scopedGridPitch, sequenceLayout, siblingBounds, slots, snapPoint, spacingSnapSource, tileGrid, useGridCellHover, useGuides, worldBoundsOf, worldUnionOf };
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 };
1578
1763
  //# sourceMappingURL=index.js.map
1579
1764
  //# sourceMappingURL=index.js.map