@weasel-js/guides 1.9.2 → 1.9.4
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/{chunk-5BCCIJY3.js → chunk-JETW4ZA3.js} +6 -8
- package/dist/chunk-JETW4ZA3.js.map +1 -0
- package/dist/chunk-NIEZTECL.js +45 -0
- package/dist/chunk-NIEZTECL.js.map +1 -0
- package/dist/index.d.ts +28 -12
- package/dist/index.js +65 -24
- package/dist/index.js.map +1 -1
- package/dist/insert.js +12 -31
- package/dist/insert.js.map +1 -1
- package/dist/move.js +2 -2
- package/dist/resize.js +10 -32
- package/dist/resize.js.map +1 -1
- package/package.json +3 -3
- package/dist/chunk-5BCCIJY3.js.map +0 -1
- package/dist/chunk-GEDAPTOM.js +0 -48
- package/dist/chunk-GEDAPTOM.js.map +0 -1
package/dist/insert.js
CHANGED
|
@@ -1,49 +1,30 @@
|
|
|
1
|
-
import { DEFAULT_GUIDE_TOLERANCE_PX } from './chunk-
|
|
1
|
+
import { DEFAULT_GUIDE_TOLERANCE_PX, roundToGridOn, nearestGuideDelta } from './chunk-NIEZTECL.js';
|
|
2
2
|
import { screenTolerance } from '@weasel-js/core';
|
|
3
3
|
|
|
4
4
|
// src/behaviors/insert/snapToGrid.ts
|
|
5
5
|
function snapToGrid(args) {
|
|
6
6
|
const { spacing, bypassKey } = args;
|
|
7
|
-
const round = (
|
|
7
|
+
const round = (p, plane) => ({
|
|
8
|
+
x: roundToGridOn(p.x, "x", spacing, plane),
|
|
9
|
+
y: roundToGridOn(p.y, "y", spacing, plane)
|
|
10
|
+
});
|
|
8
11
|
return {
|
|
9
12
|
onStart(ctx) {
|
|
10
13
|
if (bypassKey && ctx.modifiers[bypassKey]) return;
|
|
11
14
|
const id = ctx.draggedIds[0];
|
|
12
15
|
const o = ctx.origin.get(id);
|
|
13
16
|
if (!o) return;
|
|
14
|
-
ctx.origin.set(id,
|
|
17
|
+
ctx.origin.set(id, round(o, ctx.plane));
|
|
15
18
|
},
|
|
16
19
|
onMove(ctx, { current }) {
|
|
17
20
|
if (bypassKey && ctx.modifiers[bypassKey]) return;
|
|
18
|
-
return { current:
|
|
21
|
+
return { current: round(current, ctx.plane) };
|
|
19
22
|
}
|
|
20
23
|
};
|
|
21
24
|
}
|
|
22
|
-
function snapPoint(p, guides, tol) {
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
let bestDy = 0;
|
|
26
|
-
let bestAbsDy = Infinity;
|
|
27
|
-
for (const g of guides) {
|
|
28
|
-
if (g.axis === "x") {
|
|
29
|
-
const d = g.offset - p.x;
|
|
30
|
-
const ad = Math.abs(d);
|
|
31
|
-
if (ad <= tol.x && ad < bestAbsDx) {
|
|
32
|
-
bestAbsDx = ad;
|
|
33
|
-
bestDx = d;
|
|
34
|
-
}
|
|
35
|
-
} else {
|
|
36
|
-
const d = g.offset - p.y;
|
|
37
|
-
const ad = Math.abs(d);
|
|
38
|
-
if (ad <= tol.y && ad < bestAbsDy) {
|
|
39
|
-
bestAbsDy = ad;
|
|
40
|
-
bestDy = d;
|
|
41
|
-
}
|
|
42
|
-
}
|
|
43
|
-
}
|
|
44
|
-
if (bestAbsDx === Infinity && bestAbsDy === Infinity) return null;
|
|
45
|
-
const dx = bestAbsDx === Infinity ? 0 : bestDx;
|
|
46
|
-
const dy = bestAbsDy === Infinity ? 0 : bestDy;
|
|
25
|
+
function snapPoint(p, guides, tol, plane) {
|
|
26
|
+
const dx = nearestGuideDelta(guides, "x", p.x, tol.x, plane) ?? 0;
|
|
27
|
+
const dy = nearestGuideDelta(guides, "y", p.y, tol.y, plane) ?? 0;
|
|
47
28
|
if (dx === 0 && dy === 0) return null;
|
|
48
29
|
return { x: p.x + dx, y: p.y + dy };
|
|
49
30
|
}
|
|
@@ -58,14 +39,14 @@ function snapToGuides(args) {
|
|
|
58
39
|
const id = ctx.draggedIds[0];
|
|
59
40
|
const o = ctx.origin.get(id);
|
|
60
41
|
if (!o) return;
|
|
61
|
-
const snapped = snapPoint(o, guides, screenTolerance(tolerance, ctx));
|
|
42
|
+
const snapped = snapPoint(o, guides, screenTolerance(tolerance, ctx), ctx.plane);
|
|
62
43
|
if (snapped) ctx.origin.set(id, snapped);
|
|
63
44
|
},
|
|
64
45
|
onMove(ctx, { current }) {
|
|
65
46
|
if (bypassKey && ctx.modifiers[bypassKey]) return;
|
|
66
47
|
const guides = args.getGuides();
|
|
67
48
|
if (guides.length === 0) return;
|
|
68
|
-
const snapped = snapPoint(current, guides, screenTolerance(tolerance, ctx));
|
|
49
|
+
const snapped = snapPoint(current, guides, screenTolerance(tolerance, ctx), ctx.plane);
|
|
69
50
|
if (!snapped) return;
|
|
70
51
|
return { current: snapped };
|
|
71
52
|
}
|
package/dist/insert.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/behaviors/insert/snapToGrid.ts","../src/behaviors/insert/snapToGuides.ts"],"names":[],"mappings":";;;;
|
|
1
|
+
{"version":3,"sources":["../src/behaviors/insert/snapToGrid.ts","../src/behaviors/insert/snapToGuides.ts"],"names":[],"mappings":"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","file":"insert.js","sourcesContent":["import type {\n GestureContext,\n InsertBehavior,\n InsertPoint,\n ModifierState,\n} from '@weasel-js/core';\nimport { roundToGridOn } from '../../strategies/onPlane';\n\ntype ModKey = keyof ModifierState;\n\n/** Insert behavior that rounds the insertion point to a grid, so a shape\n * drawn freehand lands on grid intersections. Hold `bypassKey` to place\n * freely. */\nexport function snapToGrid<TPose>(args: {\n spacing: number;\n bypassKey?: ModKey;\n}): InsertBehavior<TPose> {\n const { spacing, bypassKey } = args;\n const round = (p: InsertPoint, plane: GestureContext<unknown>['plane']): InsertPoint => ({\n x: roundToGridOn(p.x, 'x', spacing, plane),\n y: roundToGridOn(p.y, 'y', spacing, plane),\n });\n return {\n onStart(ctx) {\n if (bypassKey && ctx.modifiers[bypassKey]) return;\n const id = ctx.draggedIds[0];\n const o = ctx.origin.get(id) as unknown as InsertPoint | undefined;\n if (!o) return;\n ctx.origin.set(id, round(o, ctx.plane) as unknown as TPose);\n },\n onMove(ctx, { current }) {\n if (bypassKey && ctx.modifiers[bypassKey]) return;\n return { current: round(current, ctx.plane) };\n },\n };\n}\n","import {\n type GestureContext,\n type InsertBehavior,\n type InsertPoint,\n type ModifierState,\n screenTolerance,\n} from '@weasel-js/core';\nimport type { Guide } from '../../guides/types';\nimport { DEFAULT_GUIDE_TOLERANCE_PX } from '../../strategies/guides';\nimport { nearestGuideDelta } from '../../strategies/onPlane';\n\ntype ModKey = keyof ModifierState;\n\n/** Options for the insert-flavored `snapToGuides`. */\nexport interface SnapToGuidesInsertArgs {\n /** Stable getter into the live guide list (typically from `useGuides`). */\n getGuides: () => readonly Guide[];\n /** Snap tolerance in screen px, read through the gesture's view. */\n tolerance?: number;\n /** Modifier key that bypasses snapping while held. */\n bypassKey?: ModKey;\n}\n\nfunction snapPoint(\n p: InsertPoint,\n guides: readonly Guide[],\n tol: { x: number; y: number },\n plane: GestureContext<unknown>['plane'],\n): InsertPoint | null {\n const dx = nearestGuideDelta(guides, 'x', p.x, tol.x, plane) ?? 0;\n const dy = nearestGuideDelta(guides, 'y', p.y, tol.y, plane) ?? 0;\n if (dx === 0 && dy === 0) return null;\n return { x: p.x + dx, y: p.y + dy };\n}\n\n/**\n * Insert behavior that snaps the start anchor (on gesture start) and the\n * live current point (during drag) to the nearest guide on each axis when\n * within `tolerance`. Pair with `useGuides` and `createGuidesLayer`.\n */\nexport function snapToGuides<TPose>(\n args: SnapToGuidesInsertArgs,\n): InsertBehavior<TPose> {\n const tolerance = args.tolerance ?? DEFAULT_GUIDE_TOLERANCE_PX;\n const bypassKey = args.bypassKey;\n\n return {\n onStart(ctx) {\n if (bypassKey && ctx.modifiers[bypassKey]) return;\n const guides = args.getGuides();\n if (guides.length === 0) return;\n const id = ctx.draggedIds[0];\n const o = ctx.origin.get(id) as unknown as InsertPoint | undefined;\n if (!o) return;\n const snapped = snapPoint(o, guides, screenTolerance(tolerance, ctx), ctx.plane);\n if (snapped) ctx.origin.set(id, snapped as unknown as TPose);\n },\n onMove(ctx, { current }) {\n if (bypassKey && ctx.modifiers[bypassKey]) return;\n const guides = args.getGuides();\n if (guides.length === 0) return;\n const snapped = snapPoint(current, guides, screenTolerance(tolerance, ctx), ctx.plane);\n if (!snapped) return;\n return { current: snapped };\n },\n };\n}\n"]}
|
package/dist/move.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { gridSnapStrategy } from './chunk-
|
|
2
|
-
import { guideSnapStrategy } from './chunk-
|
|
1
|
+
import { gridSnapStrategy } from './chunk-JETW4ZA3.js';
|
|
2
|
+
import { guideSnapStrategy } from './chunk-NIEZTECL.js';
|
|
3
3
|
import { snap } from '@weasel-js/core';
|
|
4
4
|
|
|
5
5
|
function snapToGrid(args) {
|
package/dist/resize.js
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
export { pointSnapToGrid } from './chunk-B25KFI3I.js';
|
|
2
|
-
import { DEFAULT_GUIDE_TOLERANCE_PX } from './chunk-
|
|
2
|
+
import { DEFAULT_GUIDE_TOLERANCE_PX, nearestGuideDelta, roundToGridOn } from './chunk-NIEZTECL.js';
|
|
3
3
|
import { screenTolerance } from '@weasel-js/core';
|
|
4
4
|
|
|
5
5
|
// src/behaviors/resize/snapToGrid.ts
|
|
6
6
|
function snapToGrid(args) {
|
|
7
7
|
const { spacing, bypassKey, suspendBelowDim = true } = args;
|
|
8
|
-
const round = (v) => Math.round(v / spacing) * spacing;
|
|
9
8
|
return {
|
|
10
9
|
onMove(ctx, { pose, anchor }) {
|
|
11
10
|
if (bypassKey && ctx.modifiers[bypassKey]) return;
|
|
11
|
+
const round = (v, axis) => roundToGridOn(v, axis, spacing, ctx.plane);
|
|
12
12
|
const origin = ctx.origin.get(ctx.draggedIds[0]);
|
|
13
13
|
const subX = suspendBelowDim && origin.width < spacing;
|
|
14
14
|
const subY = suspendBelowDim && origin.height < spacing;
|
|
@@ -16,11 +16,11 @@ function snapToGrid(args) {
|
|
|
16
16
|
let changed = false;
|
|
17
17
|
if (anchor.x !== "free" && !subX) {
|
|
18
18
|
if (anchor.x === "min") {
|
|
19
|
-
const east = round(x + width);
|
|
19
|
+
const east = round(x + width, "x");
|
|
20
20
|
width = east - x;
|
|
21
21
|
} else {
|
|
22
22
|
const right = origin.x + origin.width;
|
|
23
|
-
const newX = round(x);
|
|
23
|
+
const newX = round(x, "x");
|
|
24
24
|
width = right - newX;
|
|
25
25
|
x = newX;
|
|
26
26
|
}
|
|
@@ -28,11 +28,11 @@ function snapToGrid(args) {
|
|
|
28
28
|
}
|
|
29
29
|
if (anchor.y !== "free" && !subY) {
|
|
30
30
|
if (anchor.y === "min") {
|
|
31
|
-
const south = round(y + height);
|
|
31
|
+
const south = round(y + height, "y");
|
|
32
32
|
height = south - y;
|
|
33
33
|
} else {
|
|
34
34
|
const bottom = origin.y + origin.height;
|
|
35
|
-
const newY = round(y);
|
|
35
|
+
const newY = round(y, "y");
|
|
36
36
|
height = bottom - newY;
|
|
37
37
|
y = newY;
|
|
38
38
|
}
|
|
@@ -55,19 +55,8 @@ function snapToGuides(args) {
|
|
|
55
55
|
let { x, y, width, height } = pose;
|
|
56
56
|
let changed = false;
|
|
57
57
|
if (anchor.x !== "free") {
|
|
58
|
-
const
|
|
59
|
-
|
|
60
|
-
let bestAbs = Infinity;
|
|
61
|
-
for (const g of guides) {
|
|
62
|
-
if (g.axis !== "x") continue;
|
|
63
|
-
const d = g.offset - movingX;
|
|
64
|
-
const ad = Math.abs(d);
|
|
65
|
-
if (ad <= tol.x && ad < bestAbs) {
|
|
66
|
-
bestAbs = ad;
|
|
67
|
-
bestD = d;
|
|
68
|
-
}
|
|
69
|
-
}
|
|
70
|
-
if (bestAbs !== Infinity && bestD !== 0) {
|
|
58
|
+
const bestD = nearestGuideDelta(guides, "x", anchor.x === "min" ? x + width : x, tol.x, ctx.plane);
|
|
59
|
+
if (bestD !== null && bestD !== 0) {
|
|
71
60
|
if (anchor.x === "min") {
|
|
72
61
|
width = width + bestD;
|
|
73
62
|
} else {
|
|
@@ -78,19 +67,8 @@ function snapToGuides(args) {
|
|
|
78
67
|
}
|
|
79
68
|
}
|
|
80
69
|
if (anchor.y !== "free") {
|
|
81
|
-
const
|
|
82
|
-
|
|
83
|
-
let bestAbs = Infinity;
|
|
84
|
-
for (const g of guides) {
|
|
85
|
-
if (g.axis !== "y") continue;
|
|
86
|
-
const d = g.offset - movingY;
|
|
87
|
-
const ad = Math.abs(d);
|
|
88
|
-
if (ad <= tol.y && ad < bestAbs) {
|
|
89
|
-
bestAbs = ad;
|
|
90
|
-
bestD = d;
|
|
91
|
-
}
|
|
92
|
-
}
|
|
93
|
-
if (bestAbs !== Infinity && bestD !== 0) {
|
|
70
|
+
const bestD = nearestGuideDelta(guides, "y", anchor.y === "min" ? y + height : y, tol.y, ctx.plane);
|
|
71
|
+
if (bestD !== null && bestD !== 0) {
|
|
94
72
|
if (anchor.y === "min") {
|
|
95
73
|
height = height + bestD;
|
|
96
74
|
} else {
|
package/dist/resize.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/behaviors/resize/snapToGrid.ts","../src/behaviors/resize/snapToGuides.ts"],"names":[],"mappings":";;;;;
|
|
1
|
+
{"version":3,"sources":["../src/behaviors/resize/snapToGrid.ts","../src/behaviors/resize/snapToGuides.ts"],"names":[],"mappings":"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","file":"resize.js","sourcesContent":["import type { ModifierState, BoundsConstraint, Bounds } from '@weasel-js/core';\nimport { roundToGridOn } from '../../strategies/onPlane';\n\ntype ModKey = keyof ModifierState;\n\n/** Resize constraint that snaps the moving edges to a grid, leaving the\n * anchored ones where they are. By default a node already smaller than one\n * grid cell on an axis is left unsnapped on that axis, so it stays resizable\n * at sub-grid sizes. */\nexport function snapToGrid<TPose extends Bounds>(args: {\n spacing: number;\n bypassKey?: ModKey;\n suspendBelowDim?: boolean;\n}): BoundsConstraint<TPose> {\n const { spacing, bypassKey, suspendBelowDim = true } = args;\n\n return {\n onMove(ctx, { pose, anchor }) {\n if (bypassKey && ctx.modifiers[bypassKey]) return;\n const round = (v: number, axis: 'x' | 'y') => roundToGridOn(v, axis, spacing, ctx.plane);\n const origin = ctx.origin.get(ctx.draggedIds[0])!;\n const subX = suspendBelowDim && origin.width < spacing;\n const subY = suspendBelowDim && origin.height < spacing;\n\n let { x, y, width, height } = pose;\n let changed = false;\n\n if (anchor.x !== 'free' && !subX) {\n if (anchor.x === 'min') {\n // East edge moves; west (x) stays.\n const east = round(x + width, 'x');\n width = east - x;\n } else {\n // West edge moves; east (x+width) stays.\n const right = origin.x + origin.width;\n const newX = round(x, 'x');\n width = right - newX;\n x = newX;\n }\n changed = true;\n }\n if (anchor.y !== 'free' && !subY) {\n if (anchor.y === 'min') {\n const south = round(y + height, 'y');\n height = south - y;\n } else {\n const bottom = origin.y + origin.height;\n const newY = round(y, 'y');\n height = bottom - newY;\n y = newY;\n }\n changed = true;\n }\n if (!changed) return;\n return { pose: { ...pose, x, y, width, height } };\n },\n };\n}\n","import {\n type ModifierState,\n type BoundsConstraint,\n screenTolerance,\n type Bounds,\n} from '@weasel-js/core';\nimport type { Guide } from '../../guides/types';\nimport { DEFAULT_GUIDE_TOLERANCE_PX } from '../../strategies/guides';\nimport { nearestGuideDelta } from '../../strategies/onPlane';\n\ntype ModKey = keyof ModifierState;\n\n/** Options for the resize-flavored `snapToGuides`. */\nexport interface SnapToGuidesResizeArgs {\n /** Stable getter into the live guide list (typically from `useGuides`). */\n getGuides: () => readonly Guide[];\n /** Snap tolerance in screen px, read through the gesture's view. */\n tolerance?: number;\n /** Modifier key that bypasses snapping while held. */\n bypassKey?: ModKey;\n}\n\n/**\n * Resize behavior that snaps the active edge of the dragged rect to the\n * nearest guide on the corresponding axis when within `tolerance`. The\n * \"free\" axis is left untouched. The fixed (anchor) edge stays pinned;\n * only the moving edge snaps.\n *\n * On the X axis, vertical guides (`axis: 'x'`) constrain the moving vertical\n * edge — east when `anchor.x === 'min'` (west pinned), west when `'max'`.\n * Likewise on Y for horizontal guides.\n */\nexport function snapToGuides<TPose extends Bounds>(\n args: SnapToGuidesResizeArgs,\n): BoundsConstraint<TPose> {\n const tolerance = args.tolerance ?? DEFAULT_GUIDE_TOLERANCE_PX;\n const bypassKey = args.bypassKey;\n\n return {\n onMove(ctx, { pose, anchor }) {\n if (bypassKey && ctx.modifiers[bypassKey]) return;\n const guides = args.getGuides();\n if (guides.length === 0) return;\n\n // Per axis: a vertical guide is matched by a horizontal distance, so it\n // answers to `scale.x` alone.\n const tol = screenTolerance(tolerance, ctx);\n\n let { x, y, width, height } = pose;\n let changed = false;\n\n // X-axis: snap the moving vertical edge to the nearest 'x' guide.\n if (anchor.x !== 'free') {\n const bestD = nearestGuideDelta(guides, 'x', anchor.x === 'min' ? x + width : x, tol.x, ctx.plane);\n if (bestD !== null && bestD !== 0) {\n if (anchor.x === 'min') {\n // East edge moves; west (x) stays.\n width = width + bestD;\n } else {\n // West edge moves; east (x+width) stays.\n x = x + bestD;\n width = width - bestD;\n }\n changed = true;\n }\n }\n\n // Y-axis: snap the moving horizontal edge to the nearest 'y' guide.\n if (anchor.y !== 'free') {\n const bestD = nearestGuideDelta(guides, 'y', anchor.y === 'min' ? y + height : y, tol.y, ctx.plane);\n if (bestD !== null && bestD !== 0) {\n if (anchor.y === 'min') {\n height = height + bestD;\n } else {\n y = y + bestD;\n height = height - bestD;\n }\n changed = true;\n }\n }\n\n if (!changed) return;\n return { pose: { ...pose, x, y, width, height } };\n },\n };\n}\n"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@weasel-js/guides",
|
|
3
|
-
"version": "1.9.
|
|
3
|
+
"version": "1.9.4",
|
|
4
4
|
"description": "Placement aids for weasel: grids, alignment guides, snapping to them, and container layout strategies, built on core's snap and behavior seams.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -29,8 +29,8 @@
|
|
|
29
29
|
},
|
|
30
30
|
"peerDependencies": {
|
|
31
31
|
"react": ">=18",
|
|
32
|
-
"@weasel-js/core": "1.9.
|
|
33
|
-
"@weasel-js/geom": "1.9.
|
|
32
|
+
"@weasel-js/core": "1.9.4",
|
|
33
|
+
"@weasel-js/geom": "1.9.4"
|
|
34
34
|
},
|
|
35
35
|
"author": "orochi235",
|
|
36
36
|
"homepage": "https://orochi235.github.io/weasel/",
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/strategies/grid.ts"],"names":[],"mappings":";;;AAiCO,SAAS,gBAAA,CACd,SACA,GAAA,EACqB;AACrB,EAAA,MAAM,MAAA,GACJ,OAAO,GAAA,KAAQ,QAAA,IACf,GAAA,KAAQ,IAAA,KACP,QAAA,IAAY,GAAA,IAAO,OAAA,IAAW,GAAA,IAAO,YAAA,IAAgB,GAAA,CAAA,IACtD,EAAE,MAAA,IAAU,GAAA,CAAA;AACd,EAAA,MAAM,OAAA,GAAU,SACX,GAAA,GACD,IAAA;AACJ,EAAA,MAAM,UAAA,GAAa,OAAA,GAAU,OAAA,CAAQ,UAAA,GAAc,GAAA;AACnD,EAAA,MAAM,IAAA,GAAgC,OAAA,IAAW,OAAA,CAAQ,MAAA,GACrD,QAAQ,MAAA,GACP,sBAAA;AACL,EAAA,MAAM,KAAA,GAA+B,OAAA,GAAU,OAAA,CAAQ,KAAA,GAAQ,MAAA;AAC/D,EAAA,MAAM,CAAA,GAAI,WAAA,CAAY,OAAA,EAAS,UAAU,CAAA;AACzC,EAAA,OAAO;AAAA,IACL,IAAA,CAAK,MAAM,GAAA,EAAK;AACd,MAAA,MAAM,CAAA,GAAI,IAAA,CAAK,SAAA,CAAU,IAAI,CAAA;AAG7B,MAAA,MAAM,CAAA,GAAI,KAAK,KAAA,IAAS,IAAA;AACxB,MAAA,MAAM,CAAA,GAAI,CAAA,GAAI,SAAA,CAAU,CAAA,EAAG,CAAC,CAAA,GAAI,CAAA;AAChC,MAAA,MAAM,OAAO,EAAE,CAAA,EAAG,IAAA,CAAK,KAAA,CAAM,EAAE,CAAA,GAAI,CAAC,CAAA,GAAI,CAAA,EAAG,GAAG,IAAA,CAAK,KAAA,CAAM,EAAE,CAAA,GAAI,CAAC,IAAI,CAAA,EAAE;AACtE,MAAA,MAAM,EAAE,CAAA,EAAG,EAAA,EAAI,CAAA,EAAG,EAAA,KAAO,CAAA,GAAI,OAAA,CAAQ,CAAA,EAAG,IAAI,CAAA,GAAI,IAAA;AAChD,MAAA,KAAA,EAAO,oBAAoB,EAAE,CAAA,EAAG,IAAI,CAAA,EAAG,EAAA,IAAM,IAAI,CAAA;AACjD,MAAA,OAAO,IAAA,CAAK,UAAU,IAAA,EAAM,EAAA,GAAK,EAAE,CAAA,EAAG,EAAA,GAAK,EAAE,CAAC,CAAA;AAAA,IAChD;AAAA,GACF;AACF;AAmBO,SAAS,eAAA,CACd,KAAA,EACA,OAAA,EACA,UAAA,EACA,MAAA,GAAmC,EAAE,CAAA,EAAG,CAAA,EAAG,CAAA,EAAG,CAAA,EAAE,EAClB;AAC9B,EAAA,MAAM,CAAA,GAAI,WAAA,CAAY,OAAA,EAAS,UAAU,CAAA;AACzC,EAAA,OAAO;AAAA,IACL,KAAK,IAAA,CAAK,KAAA,CAAA,CAAO,MAAM,CAAA,GAAI,MAAA,CAAO,KAAK,CAAC,CAAA;AAAA,IACxC,KAAK,IAAA,CAAK,KAAA,CAAA,CAAO,MAAM,CAAA,GAAI,MAAA,CAAO,KAAK,CAAC;AAAA,GAC1C;AACF","file":"chunk-5BCCIJY3.js","sourcesContent":["import {\n AUTO_ORIGIN_PROJECTION,\n fromPlane,\n resolveUnit,\n toPlane,\n type DebugSink,\n type OriginProjection,\n type SnapStrategy,\n type UnitSystem,\n type UnitValue,\n} from '@weasel-js/core';\n\n/** Snap-strategy that rounds the pose's origin to the nearest multiple of\n * `spacing` (resolved through `unitSystem`). For non-rect TPose pass an\n * `OriginProjection` so the strategy knows how to read/write the origin. */\nexport function gridSnapStrategy<TPose>(\n spacing: UnitValue,\n unitSystem?: UnitSystem,\n): SnapStrategy<TPose>;\n/** As above, additionally reporting each candidate to a debug sink. */\nexport function gridSnapStrategy<TPose>(\n spacing: UnitValue,\n opts: { unitSystem?: UnitSystem; debug?: DebugSink },\n): SnapStrategy<TPose>;\n/** As above, for a `TPose` that is not a rect: `origin` tells the strategy how\n * to read and write the pose's origin. */\nexport function gridSnapStrategy<TPose>(\n spacing: UnitValue,\n opts: { unitSystem?: UnitSystem; origin: OriginProjection<TPose>; debug?: DebugSink },\n): SnapStrategy<TPose>;\n/** Snap-strategy that rounds the pose's origin to the nearest multiple of\n * `spacing` (resolved through `unitSystem`). For non-rect TPose pass an\n * `OriginProjection` so the strategy knows how to read/write the origin. */\nexport function gridSnapStrategy<TPose>(\n spacing: UnitValue,\n arg?: UnitSystem | { unitSystem?: UnitSystem; origin?: OriginProjection<TPose>; debug?: DebugSink },\n): SnapStrategy<TPose> {\n const isOpts =\n typeof arg === 'object' &&\n arg !== null &&\n ('origin' in arg || 'debug' in arg || 'unitSystem' in arg) &&\n !('base' in arg);\n const optsArg = isOpts\n ? (arg as { unitSystem?: UnitSystem; origin?: OriginProjection<TPose>; debug?: DebugSink })\n : null;\n const unitSystem = optsArg ? optsArg.unitSystem : (arg as UnitSystem | undefined);\n const proj: OriginProjection<TPose> = optsArg && optsArg.origin\n ? optsArg.origin\n : (AUTO_ORIGIN_PROJECTION as unknown as OriginProjection<TPose>);\n const debug: DebugSink | undefined = optsArg ? optsArg.debug : undefined;\n const c = resolveUnit(spacing, unitSystem);\n return {\n snap(pose, ctx) {\n const o = proj.getOrigin(pose);\n // The grid is the camera's: on a plane, round where the origin lies in\n // the camera's world and carry the lattice point back.\n const m = ctx?.plane ?? null;\n const w = m ? fromPlane(m, o) : o;\n const cell = { x: Math.round(w.x / c) * c, y: Math.round(w.y / c) * c };\n const { x: sx, y: sy } = m ? toPlane(m, cell) : cell;\n debug?.recordSnapCandidate({ x: sx, y: sy }, true);\n return proj.translate(pose, sx - o.x, sy - o.y);\n },\n };\n}\n\n/**\n * Compute the integer cell `{col, row}` that contains `point`, given a grid\n * `spacing` and optional `origin` and `unitSystem`. Pair with\n * `createCellHighlightLayer` (its `getCell` callback) to draw a snap-target\n * preview that uses the same spacing as `gridSnapStrategy` — so the visual\n * and behavioral grids stay in lockstep:\n *\n * const SPACING = 20;\n * const snap = gridSnapStrategy(SPACING);\n * // visual grid:\n * createGridLayer({ spacing: SPACING, bounds: () => ... });\n * // hover-preview overlay:\n * createCellHighlightLayer({\n * spacing: SPACING,\n * getCell: () => hoverPoint && pointToGridCell(hoverPoint, SPACING),\n * });\n */\nexport function pointToGridCell(\n point: { x: number; y: number },\n spacing: UnitValue,\n unitSystem?: UnitSystem,\n origin: { x: number; y: number } = { x: 0, y: 0 },\n): { col: number; row: number } {\n const s = resolveUnit(spacing, unitSystem);\n return {\n col: Math.floor((point.x - origin.x) / s),\n row: Math.floor((point.y - origin.y) / s),\n };\n}\n"]}
|
package/dist/chunk-GEDAPTOM.js
DELETED
|
@@ -1,48 +0,0 @@
|
|
|
1
|
-
import { AUTO_ORIGIN_PROJECTION, screenTolerance } from '@weasel-js/core';
|
|
2
|
-
|
|
3
|
-
// src/strategies/guides.ts
|
|
4
|
-
var DEFAULT_GUIDE_TOLERANCE_PX = 6;
|
|
5
|
-
function guideSnapStrategy(getGuides, options = {}) {
|
|
6
|
-
const tolerance = options.tolerance ?? DEFAULT_GUIDE_TOLERANCE_PX;
|
|
7
|
-
const proj = options.origin ?? AUTO_ORIGIN_PROJECTION;
|
|
8
|
-
return {
|
|
9
|
-
snap(pose, ctx) {
|
|
10
|
-
const guides = getGuides();
|
|
11
|
-
if (guides.length === 0) return null;
|
|
12
|
-
const tol = screenTolerance(tolerance, ctx);
|
|
13
|
-
const o = proj.getOrigin(pose);
|
|
14
|
-
let bestDx = 0;
|
|
15
|
-
let bestAbsDx = Infinity;
|
|
16
|
-
let bestDy = 0;
|
|
17
|
-
let bestAbsDy = Infinity;
|
|
18
|
-
const m = ctx.plane ?? null;
|
|
19
|
-
const at = (g) => m === null ? g.offset : g.axis === "x" ? g.offset * m.scale.x + m.offset.x : g.offset * m.scale.y + m.offset.y;
|
|
20
|
-
for (const g of guides) {
|
|
21
|
-
if (g.axis === "x") {
|
|
22
|
-
const d = at(g) - o.x;
|
|
23
|
-
const ad = Math.abs(d);
|
|
24
|
-
if (ad <= tol.x && ad < bestAbsDx) {
|
|
25
|
-
bestAbsDx = ad;
|
|
26
|
-
bestDx = d;
|
|
27
|
-
}
|
|
28
|
-
} else {
|
|
29
|
-
const d = at(g) - o.y;
|
|
30
|
-
const ad = Math.abs(d);
|
|
31
|
-
if (ad <= tol.y && ad < bestAbsDy) {
|
|
32
|
-
bestAbsDy = ad;
|
|
33
|
-
bestDy = d;
|
|
34
|
-
}
|
|
35
|
-
}
|
|
36
|
-
}
|
|
37
|
-
if (bestAbsDx === Infinity && bestAbsDy === Infinity) return null;
|
|
38
|
-
const dx = bestAbsDx === Infinity ? 0 : bestDx;
|
|
39
|
-
const dy = bestAbsDy === Infinity ? 0 : bestDy;
|
|
40
|
-
if (dx === 0 && dy === 0) return null;
|
|
41
|
-
return proj.translate(pose, dx, dy);
|
|
42
|
-
}
|
|
43
|
-
};
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
export { DEFAULT_GUIDE_TOLERANCE_PX, guideSnapStrategy };
|
|
47
|
-
//# sourceMappingURL=chunk-GEDAPTOM.js.map
|
|
48
|
-
//# sourceMappingURL=chunk-GEDAPTOM.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/strategies/guides.ts"],"names":[],"mappings":";;;AASO,IAAM,0BAAA,GAA6B;AA0CnC,SAAS,iBAAA,CACd,SAAA,EACA,OAAA,GAAmC,EAAC,EACf;AACrB,EAAA,MAAM,SAAA,GAAY,QAAQ,SAAA,IAAa,0BAAA;AACvC,EAAA,MAAM,IAAA,GACJ,QAAQ,MAAA,IAAW,sBAAA;AAErB,EAAA,OAAO;AAAA,IACL,IAAA,CAAK,MAAM,GAAA,EAAK;AACd,MAAA,MAAM,SAAS,SAAA,EAAU;AACzB,MAAA,IAAI,MAAA,CAAO,MAAA,KAAW,CAAA,EAAG,OAAO,IAAA;AAIhC,MAAA,MAAM,GAAA,GAAM,eAAA,CAAgB,SAAA,EAAW,GAAG,CAAA;AAE1C,MAAA,MAAM,CAAA,GAAI,IAAA,CAAK,SAAA,CAAU,IAAI,CAAA;AAE7B,MAAA,IAAI,MAAA,GAAS,CAAA;AACb,MAAA,IAAI,SAAA,GAAY,QAAA;AAChB,MAAA,IAAI,MAAA,GAAS,CAAA;AACb,MAAA,IAAI,SAAA,GAAY,QAAA;AAIhB,MAAA,MAAM,CAAA,GAAI,IAAI,KAAA,IAAS,IAAA;AACvB,MAAA,MAAM,EAAA,GAAK,CAAC,CAAA,KAAc,CAAA,KAAM,IAAA,GAAO,EAAE,MAAA,GACrC,CAAA,CAAE,IAAA,KAAS,GAAA,GAAM,CAAA,CAAE,MAAA,GAAS,EAAE,KAAA,CAAM,CAAA,GAAI,CAAA,CAAE,MAAA,CAAO,CAAA,GACjD,CAAA,CAAE,SAAS,CAAA,CAAE,KAAA,CAAM,CAAA,GAAI,CAAA,CAAE,MAAA,CAAO,CAAA;AAEpC,MAAA,KAAA,MAAW,KAAK,MAAA,EAAQ;AACtB,QAAA,IAAI,CAAA,CAAE,SAAS,GAAA,EAAK;AAClB,UAAA,MAAM,CAAA,GAAI,EAAA,CAAG,CAAC,CAAA,GAAI,CAAA,CAAE,CAAA;AACpB,UAAA,MAAM,EAAA,GAAK,IAAA,CAAK,GAAA,CAAI,CAAC,CAAA;AACrB,UAAA,IAAI,EAAA,IAAM,GAAA,CAAI,CAAA,IAAK,EAAA,GAAK,SAAA,EAAW;AACjC,YAAA,SAAA,GAAY,EAAA;AACZ,YAAA,MAAA,GAAS,CAAA;AAAA,UACX;AAAA,QACF,CAAA,MAAO;AACL,UAAA,MAAM,CAAA,GAAI,EAAA,CAAG,CAAC,CAAA,GAAI,CAAA,CAAE,CAAA;AACpB,UAAA,MAAM,EAAA,GAAK,IAAA,CAAK,GAAA,CAAI,CAAC,CAAA;AACrB,UAAA,IAAI,EAAA,IAAM,GAAA,CAAI,CAAA,IAAK,EAAA,GAAK,SAAA,EAAW;AACjC,YAAA,SAAA,GAAY,EAAA;AACZ,YAAA,MAAA,GAAS,CAAA;AAAA,UACX;AAAA,QACF;AAAA,MACF;AAEA,MAAA,IAAI,SAAA,KAAc,QAAA,IAAY,SAAA,KAAc,QAAA,EAAU,OAAO,IAAA;AAC7D,MAAA,MAAM,EAAA,GAAK,SAAA,KAAc,QAAA,GAAW,CAAA,GAAI,MAAA;AACxC,MAAA,MAAM,EAAA,GAAK,SAAA,KAAc,QAAA,GAAW,CAAA,GAAI,MAAA;AACxC,MAAA,IAAI,EAAA,KAAO,CAAA,IAAK,EAAA,KAAO,CAAA,EAAG,OAAO,IAAA;AACjC,MAAA,OAAO,IAAA,CAAK,SAAA,CAAU,IAAA,EAAM,EAAA,EAAI,EAAE,CAAA;AAAA,IACpC;AAAA,GACF;AACF","file":"chunk-GEDAPTOM.js","sourcesContent":["import {\n type SnapStrategy,\n screenTolerance,\n type OriginProjection,\n AUTO_ORIGIN_PROJECTION,\n} from '@weasel-js/core';\nimport type { Guide } from '../guides/types';\n\n/** Default snap tolerance (screen pixels). */\nexport const DEFAULT_GUIDE_TOLERANCE_PX = 6;\n\n/** Options for `guideSnapStrategy`. */\nexport interface GuideSnapOptions<TPose> {\n /**\n * Tolerance in screen pixels, divided by the gesture view's scale so the\n * trigger zone stays the same on screen at every zoom. Defaults to\n * `DEFAULT_GUIDE_TOLERANCE_PX`.\n */\n tolerance?: number;\n /**\n * Projection used when `TPose` doesn't expose `{x, y}` directly (Path,\n * polygon, etc.) — same contract as `gridSnapStrategy`. Defaults to the\n * rect-pose identity projection.\n */\n origin?: OriginProjection<TPose>;\n}\n\n/**\n * Snap-strategy that snaps the pose's origin to the nearest guide line on\n * each axis when within tolerance. Each axis is considered independently:\n * a vertical guide (`axis: 'x'`) constrains X, a horizontal guide\n * (`axis: 'y'`) constrains Y. When multiple guides are within range on the\n * same axis, the closest wins.\n *\n * Pair with `useGuides` for storage and `createGuidesLayer` for rendering.\n *\n * **Composition:** combining with `gridSnapStrategy` is left to the\n * consumer in v1 — typically by picking one based on a modifier key, or\n * by stacking two move/resize/insert behaviors in priority order.\n */\nexport function guideSnapStrategy<TPose extends { x: number; y: number }>(\n getGuides: () => readonly Guide[],\n options?: Omit<GuideSnapOptions<TPose>, 'origin'>,\n): SnapStrategy<TPose>;\n/** As above, for a `TPose` that is not a rect: `origin` tells the strategy how\n * to read and write the pose's origin. */\nexport function guideSnapStrategy<TPose>(\n getGuides: () => readonly Guide[],\n options: GuideSnapOptions<TPose> & { origin: OriginProjection<TPose> },\n): SnapStrategy<TPose>;\n/** Snap a pose's origin to the nearest guide line within tolerance, per axis. */\nexport function guideSnapStrategy<TPose>(\n getGuides: () => readonly Guide[],\n options: GuideSnapOptions<TPose> = {},\n): SnapStrategy<TPose> {\n const tolerance = options.tolerance ?? DEFAULT_GUIDE_TOLERANCE_PX;\n const proj: OriginProjection<TPose> =\n options.origin ?? (AUTO_ORIGIN_PROJECTION as unknown as OriginProjection<TPose>);\n\n return {\n snap(pose, ctx) {\n const guides = getGuides();\n if (guides.length === 0) return null;\n\n // Per axis: a guide on the x axis is matched by a horizontal distance,\n // so it answers to `scale.x` alone.\n const tol = screenTolerance(tolerance, ctx);\n\n const o = proj.getOrigin(pose);\n\n let bestDx = 0;\n let bestAbsDx = Infinity;\n let bestDy = 0;\n let bestAbsDy = Infinity;\n\n // Guides are drawn by the camera; on a plane each is carried into the\n // pose's world, which on one axis is a scale and an offset.\n const m = ctx.plane ?? null;\n const at = (g: Guide) => (m === null ? g.offset\n : g.axis === 'x' ? g.offset * m.scale.x + m.offset.x\n : g.offset * m.scale.y + m.offset.y);\n\n for (const g of guides) {\n if (g.axis === 'x') {\n const d = at(g) - o.x;\n const ad = Math.abs(d);\n if (ad <= tol.x && ad < bestAbsDx) {\n bestAbsDx = ad;\n bestDx = d;\n }\n } else {\n const d = at(g) - o.y;\n const ad = Math.abs(d);\n if (ad <= tol.y && ad < bestAbsDy) {\n bestAbsDy = ad;\n bestDy = d;\n }\n }\n }\n\n if (bestAbsDx === Infinity && bestAbsDy === Infinity) return null;\n const dx = bestAbsDx === Infinity ? 0 : bestDx;\n const dy = bestAbsDy === Infinity ? 0 : bestDy;\n if (dx === 0 && dy === 0) return null;\n return proj.translate(pose, dx, dy);\n },\n };\n}\n"]}
|