@weasel-js/core 1.7.1 → 1.7.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (41) hide show
  1. package/CHANGELOG.md +47 -0
  2. package/README.md +7 -0
  3. package/dist/{autoPoseDescriptor-BT_vp9FO.d.ts → autoPoseDescriptor-vCdt55xm.d.ts} +1 -1
  4. package/dist/{builtins-BSUfH8nU.d.ts → builtins-C-gRyVRx.d.ts} +7 -3
  5. package/dist/canvas/CursorCoordsHud.js +56 -29
  6. package/dist/canvas/CursorCoordsHud.js.map +1 -1
  7. package/dist/canvas/hud.module.js +3 -3
  8. package/dist/canvas/hud.module.js.map +1 -1
  9. package/dist/clipboard.d.ts +1 -1
  10. package/dist/clone.d.ts +2 -2
  11. package/dist/core/geometry/poseDescriptor.js +10 -15
  12. package/dist/core/geometry/poseDescriptor.js.map +1 -1
  13. package/dist/{cornerHandles-CDcZOV8h.d.ts → cornerHandles-BCrDp_mH.d.ts} +1 -1
  14. package/dist/features/paths/poseDescriptor.js +6 -11
  15. package/dist/features/paths/poseDescriptor.js.map +1 -1
  16. package/dist/index.d.ts +22 -17
  17. package/dist/interactions/actions/defaults/clone.js +64 -53
  18. package/dist/interactions/actions/defaults/clone.js.map +1 -1
  19. package/dist/interactions/actions/defaults/move.js +426 -322
  20. package/dist/interactions/actions/defaults/move.js.map +1 -1
  21. package/dist/interactions/actions/defaults/resize.js +75 -72
  22. package/dist/interactions/actions/defaults/resize.js.map +1 -1
  23. package/dist/interactions/actions/defaults/rotate.js +88 -85
  24. package/dist/interactions/actions/defaults/rotate.js.map +1 -1
  25. package/dist/interactions/actions/planeInput.js +82 -69
  26. package/dist/interactions/actions/planeInput.js.map +1 -1
  27. package/dist/interactions/actions/resize/autoPoseDescriptor.js +0 -1
  28. package/dist/interactions/actions/resize/autoPoseDescriptor.js.map +1 -1
  29. package/dist/{math-CUMxmZzn.d.ts → math-Drrw4ewZ.d.ts} +2 -2
  30. package/dist/math.d.ts +3 -3
  31. package/dist/move.d.ts +4 -4
  32. package/dist/{originProjection-BSn4xiEy.d.ts → originProjection-DEklpDc_.d.ts} +1 -1
  33. package/dist/{poseDescriptor-CjSXMf8X.d.ts → poseDescriptor-D-OH-tu5.d.ts} +0 -4
  34. package/dist/renderer.d.ts +4 -4
  35. package/dist/resize.d.ts +5 -5
  36. package/dist/style.css +2 -0
  37. package/dist/{types-CA54-mAZ.d.ts → types-A37hYkeR.d.ts} +4 -2
  38. package/dist/{types-DSjd6W3n.d.ts → types-B_9g6hMM.d.ts} +1 -1
  39. package/dist/version.js +1 -1
  40. package/package.json +16 -14
  41. package/dist/index.css +0 -2
@@ -6,18 +6,18 @@ import { unionAABB as l } from "../../../core/geometry/unionBounds.js";
6
6
  import { poseDescriptorForNode as u, translatePoseViaDescriptor as d, visualBoundsViaDescriptor as f } from "../../../core/geometry/poseDescriptor.js";
7
7
  import { poseDescriptorOf as p } from "../poseDescriptorDep.js";
8
8
  import { moveGestureAdapter as m } from "../move/gestureAdapter.js";
9
- import { inPlane as h, selectionLayer as g } from "../planeInput.js";
10
- import { defaultCommitAdapter as _ } from "../defaultCommitAdapter.js";
11
- import { scenePoseFrame as v } from "../poseFrame.js";
12
- import { resolveParams as y } from "@weasel-js/routing";
9
+ import { carryPose as h, inPlane as g, invertPlane as _, planeOf as v, selectionLayer as y } from "../planeInput.js";
10
+ import { defaultCommitAdapter as b } from "../defaultCommitAdapter.js";
11
+ import { scenePoseFrame as x } from "../poseFrame.js";
12
+ import { resolveParams as S } from "@weasel-js/routing";
13
13
  //#region src/interactions/actions/defaults/rotate.ts
14
- function b(e, t, n, r, i, a, o) {
14
+ function C(e, t, n, r, i, a, o) {
15
15
  let s = e.withRotation(t, n + r);
16
16
  if (!o) return s;
17
17
  let c = Math.cos(r), l = Math.sin(r), u = i.x - a.x, f = i.y - a.y, p = a.x + u * c - f * l, m = a.y + u * l + f * c;
18
18
  return d(s, p - i.x, m - i.y, e);
19
19
  }
20
- var x = h({
20
+ var w = g({
21
21
  id: "rotate",
22
22
  label: "Rotate",
23
23
  eligible: { capability: "transforms-selection" },
@@ -31,52 +31,52 @@ var x = h({
31
31
  ],
32
32
  invoker: {
33
33
  timing: "ongoing",
34
- start(d, h) {
35
- let g = d.deps.selection, x = d.deps.scene, S = d.deps.applyOps, C = y(h?.params), w = h?.behaviors ?? [];
36
- if (!g || !x) return {};
37
- let T = g.get();
38
- if (T.length === 0) return {};
39
- let E = p(d.deps.poseDescriptor), D = v(x, d.deps.poseComposition), O = /* @__PURE__ */ new Map(), k = /* @__PURE__ */ new Map(), A = /* @__PURE__ */ new Map(), j = /* @__PURE__ */ new Map(), M = [];
40
- for (let e of T) {
41
- let t = x.get(e);
34
+ start(d, g) {
35
+ let y = d.deps.selection, w = d.deps.scene, T = d.deps.applyOps, E = S(g?.params), D = g?.behaviors ?? [];
36
+ if (!y || !w) return {};
37
+ let O = y.get();
38
+ if (O.length === 0) return {};
39
+ let k = p(d.deps.poseDescriptor), A = x(w, d.deps.poseComposition), j = /* @__PURE__ */ new Map(), M = /* @__PURE__ */ new Map(), N = /* @__PURE__ */ new Map(), P = /* @__PURE__ */ new Map(), F = [];
40
+ for (let e of O) {
41
+ let t = w.get(e);
42
42
  if (!t) continue;
43
- let n = u(E, t);
43
+ let n = u(k, t);
44
44
  if (!n.withRotation || n.supportsRotation?.(t.pose) === !1) continue;
45
- O.set(e, t.pose);
46
- let r = D.world(e);
47
- k.set(e, r);
45
+ j.set(e, t.pose);
46
+ let r = h(n, A.world(e), _(v(d.deps.view, t.layer)));
47
+ M.set(e, r);
48
48
  let i = n.getBounds(r);
49
- A.set(e, {
49
+ N.set(e, {
50
50
  x: i.x + i.width / 2,
51
51
  y: i.y + i.height / 2
52
- }), j.set(e, n.getRotation?.(r) ?? 0), M.push(f(r, n));
52
+ }), P.set(e, n.getRotation?.(r) ?? 0), F.push(f(r, n));
53
53
  }
54
- if (O.size === 0) return {};
55
- let N = l(M), P = {
56
- x: N.x + N.width / 2,
57
- y: N.y + N.height / 2
58
- }, F = Math.atan2(d.world.y - P.y, d.world.x - P.x), I = r(d.deps), L = n(d.deps), R = {
59
- ids: T,
60
- scene: x,
61
- descriptor: E,
62
- frame: D,
63
- originPoses: O,
64
- originWorlds: k,
65
- originCenters: A,
66
- originRotations: j,
67
- unionCenter: P,
68
- startPointerAngle: F,
54
+ if (j.size === 0) return {};
55
+ let I = l(F), L = {
56
+ x: I.x + I.width / 2,
57
+ y: I.y + I.height / 2
58
+ }, R = Math.atan2(d.world.y - L.y, d.world.x - L.x), z = r(d.deps), B = d.deps.view, V = n(d.deps), H = {
59
+ ids: O,
60
+ scene: w,
61
+ descriptor: k,
62
+ frame: A,
63
+ originPoses: j,
64
+ originWorlds: M,
65
+ originCenters: N,
66
+ originRotations: P,
67
+ unionCenter: L,
68
+ startPointerAngle: R,
69
69
  currentDelta: 0,
70
- useUnionPivot: T.length > 1 && C?.pivot !== "each",
70
+ useUnionPivot: O.length > 1 && E?.pivot !== "each",
71
71
  previews: /* @__PURE__ */ new Map(),
72
72
  overrideEntries: /* @__PURE__ */ new Map(),
73
- applyOps: S,
74
- lifecycle: a(C, "Rotate", w),
75
- behaviors: w,
73
+ applyOps: T,
74
+ lifecycle: a(E, "Rotate", D),
75
+ behaviors: D,
76
76
  gestureCtx: {
77
- draggedIds: [...O.keys()],
78
- origin: new Map([...k].map(([e, t]) => [e, t])),
79
- current: new Map([...k].map(([e, t]) => [e, t])),
77
+ draggedIds: [...j.keys()],
78
+ origin: new Map([...M].map(([e, t]) => [e, t])),
79
+ current: new Map([...M].map(([e, t]) => [e, t])),
80
80
  snap: null,
81
81
  modifiers: { ...d.modifiers },
82
82
  pointer: {
@@ -85,97 +85,100 @@ var x = h({
85
85
  clientX: 0,
86
86
  clientY: 0
87
87
  },
88
- view: I(),
89
- plane: L(),
90
- adapter: m(x),
88
+ view: z(),
89
+ plane: V(),
90
+ adapter: m(w),
91
91
  scratch: {}
92
92
  }
93
- }, { lifecycle: z } = R, B = (e) => {
93
+ }, { lifecycle: U } = H, W = (e) => {
94
94
  let t = /* @__PURE__ */ new Map();
95
- for (let [n, r] of R.originWorlds) t.set(n, b(R.descriptor, r, R.originRotations.get(n) ?? 0, e, R.originCenters.get(n) ?? {
95
+ for (let [n, r] of H.originWorlds) t.set(n, C(H.descriptor, r, H.originRotations.get(n) ?? 0, e, H.originCenters.get(n) ?? {
96
96
  x: 0,
97
97
  y: 0
98
- }, R.unionCenter, R.useUnionPivot));
98
+ }, H.unionCenter, H.useUnionPivot));
99
99
  return t;
100
- }, V = (e) => {
101
- let t = B(e);
102
- for (let [e, n] of t) R.gestureCtx.current.set(e, n);
100
+ }, G = (e) => {
101
+ let t = W(e);
102
+ for (let [e, n] of t) H.gestureCtx.current.set(e, n);
103
103
  return t;
104
- }, H = (e, t) => {
105
- let n = R.gestureCtx;
104
+ }, K = (e, t) => {
105
+ let n = H.gestureCtx;
106
106
  n.modifiers = { ...e.modifiers }, n.pointer = {
107
107
  worldX: e.world.x,
108
108
  worldY: e.world.y,
109
109
  clientX: 0,
110
110
  clientY: 0
111
- }, n.view = I(), n.plane = L();
112
- let r = R.originWorlds.keys().next().value, i = R.originRotations.get(r) ?? 0, a = t, o = {
113
- pose: V(t).get(r),
111
+ }, n.view = z(), n.plane = V();
112
+ let r = H.originWorlds.keys().next().value, i = H.originRotations.get(r) ?? 0, a = t, o = {
113
+ pose: G(t).get(r),
114
114
  rotation: i + t
115
115
  };
116
- for (let e of R.behaviors) {
116
+ for (let e of H.behaviors) {
117
117
  let t = e.onMove?.(n, o);
118
118
  if (!t || t.pose === void 0) continue;
119
- let r = R.descriptor.getRotation?.(t.pose) ?? 0;
119
+ let r = H.descriptor.getRotation?.(t.pose) ?? 0;
120
120
  a = r - i, o = {
121
121
  pose: t.pose,
122
122
  rotation: r
123
123
  };
124
124
  }
125
125
  return a;
126
- }, U = () => {
126
+ }, q = () => {
127
127
  let e = {
128
- scene: x,
129
- applyOps: R.applyOps,
130
- adapter: _(x, g.adapterMethods)
128
+ scene: w,
129
+ applyOps: H.applyOps,
130
+ adapter: b(w, y.adapterMethods)
131
131
  };
132
- if (R.behaviors.length > 0) {
133
- V(R.currentDelta);
134
- let t = o(R.behaviors, R.gestureCtx);
132
+ if (H.behaviors.length > 0) {
133
+ G(H.currentDelta);
134
+ let t = o(H.behaviors, H.gestureCtx);
135
135
  if (t === null) return !1;
136
- if (t !== void 0) return i(e, z, t), !0;
136
+ if (t !== void 0) return i(e, U, t), !0;
137
137
  }
138
- if (R.currentDelta === 0) return !1;
138
+ if (H.currentDelta === 0) return !1;
139
139
  let t = [];
140
- for (let e of R.ids) {
141
- let n = R.previews.get(e);
140
+ for (let e of H.ids) {
141
+ let n = H.previews.get(e);
142
142
  if (n === void 0) continue;
143
- let r = R.originPoses.get(e);
143
+ let r = H.originPoses.get(e);
144
144
  r !== void 0 && t.push(c({
145
145
  id: e,
146
146
  from: r,
147
147
  to: n,
148
- label: z.label
148
+ label: U.label
149
149
  }));
150
150
  }
151
- return t.length !== 0 && (i(e, z, t), !0);
152
- }, W = (e) => {
153
- if (R.previews.clear(), e !== 0) for (let [t, n] of B(e)) R.previews.set(t, R.frame.local(t, n));
154
- t(R);
151
+ return t.length !== 0 && (i(e, U, t), !0);
152
+ }, J = (e) => {
153
+ if (H.previews.clear(), e !== 0) for (let [t, n] of W(e)) {
154
+ let e = H.scene.get(t)?.layer, r = e === void 0 ? null : v(B, e);
155
+ H.previews.set(t, H.frame.local(t, h(H.descriptor, n, r)));
156
+ }
157
+ t(H);
155
158
  };
156
- for (let e of w) e.onStart?.(R.gestureCtx);
157
- return z.start(R.gestureCtx.draggedIds), {
159
+ for (let e of D) e.onStart?.(H.gestureCtx);
160
+ return U.start(H.gestureCtx.draggedIds), {
158
161
  kind: "rotate",
159
162
  onMove(e) {
160
- let t = Math.atan2(e.world.y - R.unionCenter.y, e.world.x - R.unionCenter.x) - R.startPointerAngle;
161
- R.currentDelta = R.behaviors.length > 0 ? H(e, t) : t, W(R.currentDelta);
163
+ let t = Math.atan2(e.world.y - H.unionCenter.y, e.world.x - H.unionCenter.x) - H.startPointerAngle;
164
+ H.currentDelta = H.behaviors.length > 0 ? K(e, t) : t, J(H.currentDelta);
162
165
  },
163
166
  onEnd(t, n) {
164
167
  let r = !1;
165
168
  try {
166
- n === "commit" ? r = U() : s(R.behaviors, R.gestureCtx);
169
+ n === "commit" ? r = q() : s(H.behaviors, H.gestureCtx);
167
170
  } finally {
168
- e(R), R.previews.clear(), z.end(r);
171
+ e(H), H.previews.clear(), U.end(r);
169
172
  }
170
173
  },
171
- previewIds: () => R.previews.keys(),
172
- previewPose: (e) => R.previews.get(e) ?? null
174
+ previewIds: () => H.previews.keys(),
175
+ previewPose: (e) => H.previews.get(e) ?? null
173
176
  };
174
177
  }
175
178
  },
176
179
  enabled: () => !0
177
- }, g);
180
+ }, y);
178
181
  //#endregion
179
- export { x as rotateAction };
182
+ export { w as rotateAction };
180
183
 
181
184
  //# sourceMappingURL=rotate.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"rotate.js","names":["applyRotationDelta","rotated","d","pose","originRotation","delta","useUnionPivot","cos","sin","ox","originCenter","unionCenter","oy","nx","ny","translatePoseViaDescriptor","rotateAction","inPlane","selection","ctx","scene","applyOps","params","resolveParams","opts","behaviors","ids","descriptor","poseDescriptorOf","frame","scenePoseFrame","originPoses","originWorlds","originCenters","originRotations","visual","id","node","g","poseDescriptorForNode","world","b","visualBoundsViaDescriptor","union","unionAABB","startPointerAngle","readView","gestureViewReader","readPlane","gesturePlaneReader","scratch","\"pivot\"","readGestureLifecycle","p","moveGestureAdapter","turnedWorlds","out","origin","syncGestureCurrent","turned","shapeDelta","gctx","moveCtx","primary","raw","proposed","r","rotation","commitRotate","target","defaultCommitAdapter","reduceBehaviorEnd","commitGestureOps","lifecycle","ops","next","from","createTransformOp","recomputePreviews","committed","reason","runBehaviorCancel","dropPreviewOverrides","selectionLayer"],"sources":["../../../../src/interactions/actions/defaults/rotate.ts"],"sourcesContent":["/**\n * `rotateAction` — ongoing Action descriptor for pointer-driven rotation.\n *\n * ## Status: REAL (unrotated-pivot path)\n *\n * Implements the core rotation math from `useRotate`:\n * - `start`: captures origin poses + AABB centers; computes start pointer\n * angle around the union center of selected nodes.\n * - `onMove`: derives pointer angle delta from start, applies to each node's\n * origin rotation. In union-pivot mode (multi-selection) orbits each item's\n * center around the union center.\n * - `onEnd('commit')`: builds one `createTransformOp` per node (from =\n * pre-mutation origin pose, to = rotated pose) and commits them as a\n * single batch → one undo entry for the whole drag. Routes through the\n * optional `applyOps` dep (consumer history) when present, else\n * `scene.applyBatch` + `defaultCommitAdapter`.\n * - `onEnd('cancel')`: no scene writes (scene never mutated during drag).\n *\n * ## Constraints vs `useRotate`\n *\n * - Reads and writes poses through the `poseDescriptor` dep; a pose whose\n * descriptor has no `withRotation`, or reports `supportsRotation` false, is\n * left alone.\n * - Behaviors (`opts.behaviors`) see the first rotated node's turned pose; a\n * pose one returns from `onMove` sets the rotation for the whole selection.\n * - No overlay rendering — deferred to Phase 7 overlay surface.\n * - Shift-snap (15° quantum) is NOT wired in this phase — omitted deliberately\n * to keep the invoker self-contained (would need to read shift from onMove ctx).\n * TODO: thread shift from InvocationCtx.modifiers.shift into snap logic.\n */\n\nimport { gesturePlaneReader, gestureViewReader } from '../../gestures/shared/screenTolerance';\nimport type { Action } from '@weasel-js/routing';\nimport type { InvocationCtx, OngoingHandle } from '@weasel-js/routing';\nimport { resolveParams } from '@weasel-js/routing';\nimport type { Scene, NodeId } from 'core/scene/types';\nimport { syncPreviewOverrides, dropPreviewOverrides } from '../previewOverrides';\nimport type { Op } from 'core/ops/types';\nimport { createTransformOp } from 'core/ops/transform';\nimport { defaultCommitAdapter } from '../defaultCommitAdapter';\nimport type { SelectionApi } from 'core/selection/useSelection';\nimport { unionAABB } from 'core/geometry/unionBounds';\nimport type { Bounds } from 'core/viewport/fitViewToBounds';\nimport { poseDescriptorOf } from '../poseDescriptorDep';\nimport {\n poseDescriptorForNode,\n translatePoseViaDescriptor,\n visualBoundsViaDescriptor,\n type PoseDescriptor,\n} from '../resize/geometry';\nimport { scenePoseFrame, type PoseFrame } from '../poseFrame';\nimport { commitGestureOps, readGestureLifecycle, reduceBehaviorEnd, runBehaviorCancel, type GestureLifecycle } from '../gestureLifecycle';\nimport { moveGestureAdapter } from '../move/gestureAdapter';\nimport type { GestureContext, RotateBehavior, RotateProposed } from '../../gestures/types';\nimport { inPlane, selectionLayer } from '../planeInput';\n\n// ---------------------------------------------------------------------------\n// Internal helpers\n// ---------------------------------------------------------------------------\n\n/** Apply a rotation delta (radians) to a pose, optionally orbiting the item's\n * center around a shared union center. */\nfunction applyRotationDelta(\n d: PoseDescriptor<unknown>,\n pose: unknown,\n originRotation: number,\n delta: number,\n originCenter: { x: number; y: number },\n unionCenter: { x: number; y: number },\n useUnionPivot: boolean,\n): unknown {\n const rotated = d.withRotation!(pose, originRotation + delta);\n if (!useUnionPivot) return rotated;\n const cos = Math.cos(delta);\n const sin = Math.sin(delta);\n const ox = originCenter.x - unionCenter.x;\n const oy = originCenter.y - unionCenter.y;\n const nx = unionCenter.x + ox * cos - oy * sin;\n const ny = unionCenter.y + ox * sin + oy * cos;\n return translatePoseViaDescriptor(rotated, nx - originCenter.x, ny - originCenter.y, d);\n}\n\n// ---------------------------------------------------------------------------\n// Internal scratch\n// ---------------------------------------------------------------------------\n\ninterface RotateScratch {\n ids: NodeId[];\n scene: Scene<unknown, string, unknown>;\n descriptor: PoseDescriptor<unknown>;\n /** World reads and local writes over the scene's composition strategy. */\n frame: PoseFrame<unknown>;\n /** Origin pose for each selected node as stored — the `from` of its op. */\n originPoses: Map<NodeId, unknown>;\n /** Origin pose for each selected node in world — what the gesture turns. */\n originWorlds: Map<NodeId, unknown>;\n /** AABB center for each selected node (from origin pose). */\n originCenters: Map<NodeId, { x: number; y: number }>;\n /** Per-node origin rotation (radians). */\n originRotations: Map<NodeId, number>;\n /** Union center of all selected nodes — the rotation pivot. */\n unionCenter: { x: number; y: number };\n /** Pointer angle around unionCenter at drag start. */\n startPointerAngle: number;\n /** Running delta — updated each onMove, applied once at commit. */\n currentDelta: number;\n /** Use union pivot (multi-select) vs each item's own center. */\n useUnionPivot: boolean;\n /** In-flight preview poses keyed by node id. */\n previews: Map<NodeId, unknown>;\n overrideEntries: Map<NodeId, { pose: unknown }>;\n /** Optional consumer commit hook captured at gesture start. When present,\n * ops-based commits route through it (consumer history) instead of\n * `scene.applyBatch`. Undefined → fall back to `scene.applyBatch`. */\n applyOps?: (ops: Op[], label: string) => void;\n lifecycle: GestureLifecycle;\n behaviors: RotateBehavior<unknown>[];\n /** Reused gesture context handed to behaviors across the drag. */\n gestureCtx: GestureContext<unknown>;\n}\n\n// ---------------------------------------------------------------------------\n// Descriptor\n// ---------------------------------------------------------------------------\n\n/**\n * @experimental\n * Static descriptor for the `rotate` Action.\n *\n * Requires dep-schema entries: `selection`, `scene`.\n *\n * Implements the unrotated-pivot rotation path from `useRotate`.\n *\n * @see useRotate — the React hook this descriptor mirrors for the rect case.\n */\nexport const rotateAction: Action & { requires: string[] } = inPlane({\n id: 'rotate',\n label: 'Rotate',\n // No default binding. It used to be a bare `{ kind: 'drag' }`, which made\n // any drag no active tool claimed rotate a non-empty selection. The rotation\n // handle binds it (`selectionTransformBindings`, the `transform` preset).\n eligible: { capability: 'transforms-selection' },\n requires: ['selection', 'scene', 'applyOps', 'poseDescriptor', 'poseComposition', 'view'],\n invoker: {\n timing: 'ongoing',\n start(ctx: InvocationCtx, opts): OngoingHandle {\n const selection = ctx.deps.selection as SelectionApi | undefined;\n const scene = ctx.deps.scene as Scene<unknown, string, unknown> | undefined;\n const applyOps = ctx.deps.applyOps as ((ops: Op[], label: string) => void) | undefined;\n const params = resolveParams(opts?.params);\n const behaviors = (opts?.behaviors ?? []) as RotateBehavior<unknown>[];\n\n if (!selection || !scene) return {};\n\n const ids = selection.get() as NodeId[];\n if (ids.length === 0) return {};\n\n const descriptor = poseDescriptorOf(ctx.deps.poseDescriptor);\n // The pivot the pointer orbits is a world point, so every pose the\n // gesture turns is a world pose. Each turned result is stored back in\n // the node's own parent frame at preview and commit alike.\n const frame = scenePoseFrame(scene, ctx.deps.poseComposition);\n const originPoses = new Map<NodeId, unknown>();\n const originWorlds = new Map<NodeId, unknown>();\n const originCenters = new Map<NodeId, { x: number; y: number }>();\n const originRotations = new Map<NodeId, number>();\n const visual: Bounds[] = [];\n\n for (const id of ids) {\n const node = scene.get(id);\n if (!node) continue;\n const g = poseDescriptorForNode(descriptor, node);\n if (!g.withRotation || g.supportsRotation?.(node.pose) === false) continue;\n originPoses.set(id, node.pose);\n // The pivot is a world point, so every measurement the gesture orbits\n // is taken from the world pose, not the stored one.\n const world = frame.world(id);\n originWorlds.set(id, world);\n const b = g.getBounds(world);\n originCenters.set(id, { x: b.x + b.width / 2, y: b.y + b.height / 2 });\n originRotations.set(id, g.getRotation?.(world) ?? 0);\n visual.push(visualBoundsViaDescriptor(world, g));\n }\n\n if (originPoses.size === 0) return {};\n\n const union = unionAABB(visual)!;\n const unionCenter = { x: union.x + union.width / 2, y: union.y + union.height / 2 };\n const startPointerAngle = Math.atan2(\n ctx.world.y - unionCenter.y,\n ctx.world.x - unionCenter.x,\n );\n\n const readView = gestureViewReader(ctx.deps);\n\n const readPlane = gesturePlaneReader(ctx.deps);\n const scratch: RotateScratch = {\n ids,\n scene,\n descriptor,\n frame,\n originPoses,\n originWorlds,\n originCenters,\n originRotations,\n unionCenter,\n startPointerAngle,\n currentDelta: 0,\n useUnionPivot: ids.length > 1 && params?.['pivot'] !== 'each',\n previews: new Map<NodeId, unknown>(),\n overrideEntries: new Map<NodeId, { pose: unknown }>(),\n applyOps,\n lifecycle: readGestureLifecycle(params, 'Rotate', behaviors),\n behaviors,\n gestureCtx: {\n draggedIds: [...originPoses.keys()] as string[],\n origin: new Map([...originWorlds].map(([id, p]) => [id as string, p])),\n current: new Map([...originWorlds].map(([id, p]) => [id as string, p])),\n snap: null,\n modifiers: { ...ctx.modifiers },\n pointer: { worldX: ctx.world.x, worldY: ctx.world.y, clientX: 0, clientY: 0 },\n view: readView(),\n plane: readPlane(),\n adapter: moveGestureAdapter(scene) as unknown as GestureContext<unknown>['adapter'],\n scratch: {},\n },\n };\n const { lifecycle } = scratch;\n\n /** Each rotated node's world pose turned by `delta`. */\n const turnedWorlds = (delta: number): Map<NodeId, unknown> => {\n const out = new Map<NodeId, unknown>();\n for (const [id, origin] of scratch.originWorlds) {\n out.set(id, applyRotationDelta(\n scratch.descriptor,\n origin,\n scratch.originRotations.get(id) ?? 0,\n delta,\n scratch.originCenters.get(id) ?? { x: 0, y: 0 },\n scratch.unionCenter,\n scratch.useUnionPivot,\n ));\n }\n return out;\n };\n\n const syncGestureCurrent = (delta: number): Map<NodeId, unknown> => {\n const turned = turnedWorlds(delta);\n for (const [id, pose] of turned) scratch.gestureCtx.current.set(id as string, pose);\n return turned;\n };\n\n /** The pointer's delta as the behaviors reshape it. */\n const shapeDelta = (moveCtx: InvocationCtx, raw: number): number => {\n const gctx = scratch.gestureCtx;\n gctx.modifiers = { ...moveCtx.modifiers };\n gctx.pointer = { worldX: moveCtx.world.x, worldY: moveCtx.world.y, clientX: 0, clientY: 0 };\n gctx.view = readView();\n gctx.plane = readPlane();\n const primary = scratch.originWorlds.keys().next().value as NodeId;\n const originRotation = scratch.originRotations.get(primary) ?? 0;\n let delta = raw;\n let proposed: RotateProposed<unknown> = {\n pose: syncGestureCurrent(raw).get(primary),\n rotation: originRotation + raw,\n };\n for (const b of scratch.behaviors) {\n const r = b.onMove?.(gctx, proposed);\n if (!r || r.pose === undefined) continue;\n const rotation = scratch.descriptor.getRotation?.(r.pose) ?? 0;\n delta = rotation - originRotation;\n proposed = { pose: r.pose, rotation };\n }\n return delta;\n };\n\n // Returns whether anything reached the document.\n const commitRotate = (): boolean => {\n const target = {\n scene,\n applyOps: scratch.applyOps,\n adapter: defaultCommitAdapter(scene, selection.adapterMethods),\n };\n if (scratch.behaviors.length > 0) {\n syncGestureCurrent(scratch.currentDelta);\n const r = reduceBehaviorEnd(scratch.behaviors, scratch.gestureCtx);\n if (r === null) return false;\n if (r !== undefined) {\n commitGestureOps(target, lifecycle, r);\n return true;\n }\n }\n if (scratch.currentDelta === 0) return false;\n // One transform op per node: `from` is the origin pose captured at drag\n // start, `to` the rotated preview. A single batch, so one undo entry.\n const ops: Op[] = [];\n for (const id of scratch.ids) {\n const next = scratch.previews.get(id);\n if (next === undefined) continue;\n const from = scratch.originPoses.get(id);\n if (from === undefined) continue;\n ops.push(createTransformOp<unknown>({\n id: id as string,\n from,\n to: next,\n label: lifecycle.label,\n }));\n }\n if (ops.length === 0) return false;\n commitGestureOps(target, lifecycle, ops);\n return true;\n };\n\n const recomputePreviews = (delta: number) => {\n scratch.previews.clear();\n if (delta !== 0) {\n for (const [id, turned] of turnedWorlds(delta)) {\n scratch.previews.set(id, scratch.frame.local(id, turned));\n }\n }\n syncPreviewOverrides(scratch);\n };\n\n for (const b of behaviors) b.onStart?.(scratch.gestureCtx);\n lifecycle.start(scratch.gestureCtx.draggedIds);\n\n return {\n kind: 'rotate',\n onMove(moveCtx: InvocationCtx): void {\n const pointerAngle = Math.atan2(\n moveCtx.world.y - scratch.unionCenter.y,\n moveCtx.world.x - scratch.unionCenter.x,\n );\n const raw = pointerAngle - scratch.startPointerAngle;\n scratch.currentDelta = scratch.behaviors.length > 0 ? shapeDelta(moveCtx, raw) : raw;\n recomputePreviews(scratch.currentDelta);\n },\n onEnd(_endCtx: InvocationCtx, reason: 'commit' | 'cancel'): void {\n let committed = false;\n try {\n if (reason === 'commit') committed = commitRotate();\n else runBehaviorCancel(scratch.behaviors, scratch.gestureCtx);\n } finally {\n dropPreviewOverrides(scratch);\n scratch.previews.clear();\n lifecycle.end(committed);\n }\n },\n previewIds: () => scratch.previews.keys(),\n previewPose: (id: string) => scratch.previews.get(id as NodeId) ?? null,\n };\n },\n },\n enabled: () => true,\n}, selectionLayer);\n"],"mappings":"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"}
1
+ {"version":3,"file":"rotate.js","names":["applyRotationDelta","rotated","d","pose","originRotation","delta","useUnionPivot","cos","sin","ox","originCenter","unionCenter","oy","nx","ny","translatePoseViaDescriptor","rotateAction","inPlane","selection","ctx","scene","applyOps","params","resolveParams","opts","behaviors","ids","descriptor","poseDescriptorOf","frame","scenePoseFrame","originPoses","originWorlds","originCenters","originRotations","visual","id","node","g","poseDescriptorForNode","world","carryPose","invertPlane","planeOf","b","visualBoundsViaDescriptor","union","unionAABB","startPointerAngle","readView","gestureViewReader","view","readPlane","gesturePlaneReader","scratch","\"pivot\"","readGestureLifecycle","p","moveGestureAdapter","turnedWorlds","out","origin","syncGestureCurrent","turned","shapeDelta","gctx","moveCtx","primary","raw","proposed","r","rotation","commitRotate","target","defaultCommitAdapter","reduceBehaviorEnd","commitGestureOps","lifecycle","ops","next","from","createTransformOp","recomputePreviews","layer","own","committed","reason","runBehaviorCancel","dropPreviewOverrides","selectionLayer"],"sources":["../../../../src/interactions/actions/defaults/rotate.ts"],"sourcesContent":["/**\n * `rotateAction` — ongoing Action descriptor for pointer-driven rotation.\n *\n * ## Status: REAL (unrotated-pivot path)\n *\n * Implements the core rotation math from `useRotate`:\n * - `start`: captures origin poses + AABB centers; computes start pointer\n * angle around the union center of selected nodes.\n * - `onMove`: derives pointer angle delta from start, applies to each node's\n * origin rotation. In union-pivot mode (multi-selection) orbits each item's\n * center around the union center.\n * - `onEnd('commit')`: builds one `createTransformOp` per node (from =\n * pre-mutation origin pose, to = rotated pose) and commits them as a\n * single batch → one undo entry for the whole drag. Routes through the\n * optional `applyOps` dep (consumer history) when present, else\n * `scene.applyBatch` + `defaultCommitAdapter`.\n * - `onEnd('cancel')`: no scene writes (scene never mutated during drag).\n *\n * ## Constraints vs `useRotate`\n *\n * - Reads and writes poses through the `poseDescriptor` dep; a pose whose\n * descriptor has no `withRotation`, or reports `supportsRotation` false, is\n * left alone.\n * - Behaviors (`opts.behaviors`) see the first rotated node's turned pose; a\n * pose one returns from `onMove` sets the rotation for the whole selection.\n * - No overlay rendering — deferred to Phase 7 overlay surface.\n * - Shift-snap (15° quantum) is NOT wired in this phase — omitted deliberately\n * to keep the invoker self-contained (would need to read shift from onMove ctx).\n * TODO: thread shift from InvocationCtx.modifiers.shift into snap logic.\n */\n\nimport { gesturePlaneReader, gestureViewReader } from '../../gestures/shared/screenTolerance';\nimport type { Action } from '@weasel-js/routing';\nimport type { InvocationCtx, OngoingHandle } from '@weasel-js/routing';\nimport { resolveParams } from '@weasel-js/routing';\nimport type { Scene, NodeId } from 'core/scene/types';\nimport { syncPreviewOverrides, dropPreviewOverrides } from '../previewOverrides';\nimport type { Op } from 'core/ops/types';\nimport { createTransformOp } from 'core/ops/transform';\nimport { defaultCommitAdapter } from '../defaultCommitAdapter';\nimport type { SelectionApi } from 'core/selection/useSelection';\nimport { unionAABB } from 'core/geometry/unionBounds';\nimport type { Bounds } from 'core/viewport/fitViewToBounds';\nimport { poseDescriptorOf } from '../poseDescriptorDep';\nimport {\n poseDescriptorForNode,\n translatePoseViaDescriptor,\n visualBoundsViaDescriptor,\n type PoseDescriptor,\n} from '../resize/geometry';\nimport { scenePoseFrame, type PoseFrame } from '../poseFrame';\nimport { commitGestureOps, readGestureLifecycle, reduceBehaviorEnd, runBehaviorCancel, type GestureLifecycle } from '../gestureLifecycle';\nimport { moveGestureAdapter } from '../move/gestureAdapter';\nimport type { GestureContext, RotateBehavior, RotateProposed } from '../../gestures/types';\nimport { carryPose, invertPlane, inPlane, planeOf, selectionLayer } from '../planeInput';\n\n// ---------------------------------------------------------------------------\n// Internal helpers\n// ---------------------------------------------------------------------------\n\n/** Apply a rotation delta (radians) to a pose, optionally orbiting the item's\n * center around a shared union center. */\nfunction applyRotationDelta(\n d: PoseDescriptor<unknown>,\n pose: unknown,\n originRotation: number,\n delta: number,\n originCenter: { x: number; y: number },\n unionCenter: { x: number; y: number },\n useUnionPivot: boolean,\n): unknown {\n const rotated = d.withRotation!(pose, originRotation + delta);\n if (!useUnionPivot) return rotated;\n const cos = Math.cos(delta);\n const sin = Math.sin(delta);\n const ox = originCenter.x - unionCenter.x;\n const oy = originCenter.y - unionCenter.y;\n const nx = unionCenter.x + ox * cos - oy * sin;\n const ny = unionCenter.y + ox * sin + oy * cos;\n return translatePoseViaDescriptor(rotated, nx - originCenter.x, ny - originCenter.y, d);\n}\n\n// ---------------------------------------------------------------------------\n// Internal scratch\n// ---------------------------------------------------------------------------\n\ninterface RotateScratch {\n ids: NodeId[];\n scene: Scene<unknown, string, unknown>;\n descriptor: PoseDescriptor<unknown>;\n /** World reads and local writes over the scene's composition strategy. */\n frame: PoseFrame<unknown>;\n /** Origin pose for each selected node as stored — the `from` of its op. */\n originPoses: Map<NodeId, unknown>;\n /** Origin pose for each selected node in world — what the gesture turns. */\n originWorlds: Map<NodeId, unknown>;\n /** AABB center for each selected node (from origin pose). */\n originCenters: Map<NodeId, { x: number; y: number }>;\n /** Per-node origin rotation (radians). */\n originRotations: Map<NodeId, number>;\n /** Union center of all selected nodes — the rotation pivot. */\n unionCenter: { x: number; y: number };\n /** Pointer angle around unionCenter at drag start. */\n startPointerAngle: number;\n /** Running delta — updated each onMove, applied once at commit. */\n currentDelta: number;\n /** Use union pivot (multi-select) vs each item's own center. */\n useUnionPivot: boolean;\n /** In-flight preview poses keyed by node id. */\n previews: Map<NodeId, unknown>;\n overrideEntries: Map<NodeId, { pose: unknown }>;\n /** Optional consumer commit hook captured at gesture start. When present,\n * ops-based commits route through it (consumer history) instead of\n * `scene.applyBatch`. Undefined → fall back to `scene.applyBatch`. */\n applyOps?: (ops: Op[], label: string) => void;\n lifecycle: GestureLifecycle;\n behaviors: RotateBehavior<unknown>[];\n /** Reused gesture context handed to behaviors across the drag. */\n gestureCtx: GestureContext<unknown>;\n}\n\n// ---------------------------------------------------------------------------\n// Descriptor\n// ---------------------------------------------------------------------------\n\n/**\n * @experimental\n * Static descriptor for the `rotate` Action.\n *\n * Requires dep-schema entries: `selection`, `scene`.\n *\n * Implements the unrotated-pivot rotation path from `useRotate`.\n *\n * @see useRotate — the React hook this descriptor mirrors for the rect case.\n */\nexport const rotateAction: Action & { requires: string[] } = inPlane({\n id: 'rotate',\n label: 'Rotate',\n // No default binding. It used to be a bare `{ kind: 'drag' }`, which made\n // any drag no active tool claimed rotate a non-empty selection. The rotation\n // handle binds it (`selectionTransformBindings`, the `transform` preset).\n eligible: { capability: 'transforms-selection' },\n requires: ['selection', 'scene', 'applyOps', 'poseDescriptor', 'poseComposition', 'view'],\n invoker: {\n timing: 'ongoing',\n start(ctx: InvocationCtx, opts): OngoingHandle {\n const selection = ctx.deps.selection as SelectionApi | undefined;\n const scene = ctx.deps.scene as Scene<unknown, string, unknown> | undefined;\n const applyOps = ctx.deps.applyOps as ((ops: Op[], label: string) => void) | undefined;\n const params = resolveParams(opts?.params);\n const behaviors = (opts?.behaviors ?? []) as RotateBehavior<unknown>[];\n\n if (!selection || !scene) return {};\n\n const ids = selection.get() as NodeId[];\n if (ids.length === 0) return {};\n\n const descriptor = poseDescriptorOf(ctx.deps.poseDescriptor);\n // The pivot the pointer orbits is a world point, so every pose the\n // gesture turns is a world pose. Each turned result is stored back in\n // the node's own parent frame at preview and commit alike.\n const frame = scenePoseFrame(scene, ctx.deps.poseComposition);\n const originPoses = new Map<NodeId, unknown>();\n const originWorlds = new Map<NodeId, unknown>();\n const originCenters = new Map<NodeId, { x: number; y: number }>();\n const originRotations = new Map<NodeId, number>();\n const visual: Bounds[] = [];\n\n for (const id of ids) {\n const node = scene.get(id);\n if (!node) continue;\n const g = poseDescriptorForNode(descriptor, node);\n if (!g.withRotation || g.supportsRotation?.(node.pose) === false) continue;\n originPoses.set(id, node.pose);\n // The pivot is a world point, so every measurement the gesture orbits\n // is taken from the world pose, not the stored one — carried into the\n // plane the edit is measured in, where the pointer's angle is read.\n const world = carryPose(g, frame.world(id), invertPlane(planeOf(ctx.deps.view, node.layer)));\n originWorlds.set(id, world);\n const b = g.getBounds(world);\n originCenters.set(id, { x: b.x + b.width / 2, y: b.y + b.height / 2 });\n originRotations.set(id, g.getRotation?.(world) ?? 0);\n visual.push(visualBoundsViaDescriptor(world, g));\n }\n\n if (originPoses.size === 0) return {};\n\n const union = unionAABB(visual)!;\n const unionCenter = { x: union.x + union.width / 2, y: union.y + union.height / 2 };\n const startPointerAngle = Math.atan2(\n ctx.world.y - unionCenter.y,\n ctx.world.x - unionCenter.x,\n );\n\n const readView = gestureViewReader(ctx.deps);\n const view = ctx.deps.view;\n\n const readPlane = gesturePlaneReader(ctx.deps);\n const scratch: RotateScratch = {\n ids,\n scene,\n descriptor,\n frame,\n originPoses,\n originWorlds,\n originCenters,\n originRotations,\n unionCenter,\n startPointerAngle,\n currentDelta: 0,\n useUnionPivot: ids.length > 1 && params?.['pivot'] !== 'each',\n previews: new Map<NodeId, unknown>(),\n overrideEntries: new Map<NodeId, { pose: unknown }>(),\n applyOps,\n lifecycle: readGestureLifecycle(params, 'Rotate', behaviors),\n behaviors,\n gestureCtx: {\n draggedIds: [...originPoses.keys()] as string[],\n origin: new Map([...originWorlds].map(([id, p]) => [id as string, p])),\n current: new Map([...originWorlds].map(([id, p]) => [id as string, p])),\n snap: null,\n modifiers: { ...ctx.modifiers },\n pointer: { worldX: ctx.world.x, worldY: ctx.world.y, clientX: 0, clientY: 0 },\n view: readView(),\n plane: readPlane(),\n adapter: moveGestureAdapter(scene) as unknown as GestureContext<unknown>['adapter'],\n scratch: {},\n },\n };\n const { lifecycle } = scratch;\n\n /** Each rotated node's world pose turned by `delta`. */\n const turnedWorlds = (delta: number): Map<NodeId, unknown> => {\n const out = new Map<NodeId, unknown>();\n for (const [id, origin] of scratch.originWorlds) {\n out.set(id, applyRotationDelta(\n scratch.descriptor,\n origin,\n scratch.originRotations.get(id) ?? 0,\n delta,\n scratch.originCenters.get(id) ?? { x: 0, y: 0 },\n scratch.unionCenter,\n scratch.useUnionPivot,\n ));\n }\n return out;\n };\n\n const syncGestureCurrent = (delta: number): Map<NodeId, unknown> => {\n const turned = turnedWorlds(delta);\n for (const [id, pose] of turned) scratch.gestureCtx.current.set(id as string, pose);\n return turned;\n };\n\n /** The pointer's delta as the behaviors reshape it. */\n const shapeDelta = (moveCtx: InvocationCtx, raw: number): number => {\n const gctx = scratch.gestureCtx;\n gctx.modifiers = { ...moveCtx.modifiers };\n gctx.pointer = { worldX: moveCtx.world.x, worldY: moveCtx.world.y, clientX: 0, clientY: 0 };\n gctx.view = readView();\n gctx.plane = readPlane();\n const primary = scratch.originWorlds.keys().next().value as NodeId;\n const originRotation = scratch.originRotations.get(primary) ?? 0;\n let delta = raw;\n let proposed: RotateProposed<unknown> = {\n pose: syncGestureCurrent(raw).get(primary),\n rotation: originRotation + raw,\n };\n for (const b of scratch.behaviors) {\n const r = b.onMove?.(gctx, proposed);\n if (!r || r.pose === undefined) continue;\n const rotation = scratch.descriptor.getRotation?.(r.pose) ?? 0;\n delta = rotation - originRotation;\n proposed = { pose: r.pose, rotation };\n }\n return delta;\n };\n\n // Returns whether anything reached the document.\n const commitRotate = (): boolean => {\n const target = {\n scene,\n applyOps: scratch.applyOps,\n adapter: defaultCommitAdapter(scene, selection.adapterMethods),\n };\n if (scratch.behaviors.length > 0) {\n syncGestureCurrent(scratch.currentDelta);\n const r = reduceBehaviorEnd(scratch.behaviors, scratch.gestureCtx);\n if (r === null) return false;\n if (r !== undefined) {\n commitGestureOps(target, lifecycle, r);\n return true;\n }\n }\n if (scratch.currentDelta === 0) return false;\n // One transform op per node: `from` is the origin pose captured at drag\n // start, `to` the rotated preview. A single batch, so one undo entry.\n const ops: Op[] = [];\n for (const id of scratch.ids) {\n const next = scratch.previews.get(id);\n if (next === undefined) continue;\n const from = scratch.originPoses.get(id);\n if (from === undefined) continue;\n ops.push(createTransformOp<unknown>({\n id: id as string,\n from,\n to: next,\n label: lifecycle.label,\n }));\n }\n if (ops.length === 0) return false;\n commitGestureOps(target, lifecycle, ops);\n return true;\n };\n\n const recomputePreviews = (delta: number) => {\n scratch.previews.clear();\n if (delta !== 0) {\n for (const [id, turned] of turnedWorlds(delta)) {\n const layer = scratch.scene.get(id)?.layer;\n const own = layer === undefined ? null : planeOf(view, layer);\n scratch.previews.set(id, scratch.frame.local(id, carryPose(scratch.descriptor, turned, own)));\n }\n }\n syncPreviewOverrides(scratch);\n };\n\n for (const b of behaviors) b.onStart?.(scratch.gestureCtx);\n lifecycle.start(scratch.gestureCtx.draggedIds);\n\n return {\n kind: 'rotate',\n onMove(moveCtx: InvocationCtx): void {\n const pointerAngle = Math.atan2(\n moveCtx.world.y - scratch.unionCenter.y,\n moveCtx.world.x - scratch.unionCenter.x,\n );\n const raw = pointerAngle - scratch.startPointerAngle;\n scratch.currentDelta = scratch.behaviors.length > 0 ? shapeDelta(moveCtx, raw) : raw;\n recomputePreviews(scratch.currentDelta);\n },\n onEnd(_endCtx: InvocationCtx, reason: 'commit' | 'cancel'): void {\n let committed = false;\n try {\n if (reason === 'commit') committed = commitRotate();\n else runBehaviorCancel(scratch.behaviors, scratch.gestureCtx);\n } finally {\n dropPreviewOverrides(scratch);\n scratch.previews.clear();\n lifecycle.end(committed);\n }\n },\n previewIds: () => scratch.previews.keys(),\n previewPose: (id: string) => scratch.previews.get(id as NodeId) ?? null,\n };\n },\n },\n enabled: () => true,\n}, selectionLayer);\n"],"mappings":"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"}
@@ -1,17 +1,28 @@
1
- import { deriveParallaxView as e, fromPlane as t, planeMap as n, rectFromPlane as r, toPlane as i } from "../../core/viewport/parallax.js";
2
- import { asNodeId as a } from "../../core/scene/types.js";
1
+ import { deriveParallaxView as e, fromPlane as t, planeMap as n, planeToPlane as r, rectFromPlane as i, rectToPlane as a, toPlane as o } from "../../core/viewport/parallax.js";
2
+ import { asNodeId as s } from "../../core/scene/types.js";
3
3
  //#region src/interactions/actions/planeInput.ts
4
- var o = (e, t) => t ? e.scene?.get(a(t))?.layer : void 0, s = (e, t) => {
4
+ var c = (e, t) => t ? e.scene?.get(s(t))?.layer : void 0, l = (e, t) => {
5
5
  let n = e.selection;
6
6
  for (let r of [...t?.targetIds ?? [], ...n?.get() ?? []]) {
7
- let t = o(e, r);
7
+ let t = c(e, r);
8
8
  if (t !== void 0) return t;
9
9
  }
10
- }, c = (e) => e.insert?.layer?.(), l = (e) => o(e, e.editAnchors?.editingId);
11
- function u(e, t) {
10
+ }, u = (e) => e.insert?.layer?.(), d = (e) => c(e, e.editAnchors?.editingId);
11
+ function f(e, t) {
12
+ return e?.planeOf?.(t) ?? null;
13
+ }
14
+ function p(e) {
15
+ return e && r(e, null);
16
+ }
17
+ function m(e, t, n) {
18
+ if (n === null) return t;
19
+ let r = e.getBounds(t);
20
+ return e.remapBounds(t, r, a(n, r));
21
+ }
22
+ function h(e, t) {
12
23
  let n = e.invoker;
13
24
  if (!n) return e;
14
- let r = e.requires ?? [], a = [.../* @__PURE__ */ new Set([
25
+ let r = e.requires ?? [], i = [.../* @__PURE__ */ new Set([
15
26
  ...r,
16
27
  "scene",
17
28
  "selection",
@@ -19,41 +30,39 @@ function u(e, t) {
19
30
  ])];
20
31
  return n.timing === "immediate" ? {
21
32
  ...e,
22
- requires: a,
33
+ requires: i,
23
34
  invoker: {
24
35
  timing: "immediate",
25
- run(e, a) {
26
- let o = d(e, t(e), r, null);
27
- if (o === null) return n.run(e, a);
28
- let s = a?.worldX, c = a?.worldY, l = typeof s == "number" && typeof c == "number" ? i(o.map(), {
36
+ run(e, i) {
37
+ let a = g(e, t(e), r, null);
38
+ if (a === null) return n.run(e, i);
39
+ let s = i?.worldX, c = i?.worldY, l = typeof s == "number" && typeof c == "number" ? o(a.map(), {
29
40
  x: s,
30
41
  y: c
31
42
  }) : null;
32
- n.run(o.deps, l ? {
33
- ...a,
43
+ n.run(a.deps, l ? {
44
+ ...i,
34
45
  worldX: l.x,
35
46
  worldY: l.y
36
- } : a);
47
+ } : i);
37
48
  }
38
49
  }
39
50
  } : {
40
51
  ...e,
41
- requires: a,
52
+ requires: i,
42
53
  invoker: {
43
54
  timing: "ongoing",
44
55
  start(e, i) {
45
- let a = d(e.deps, t(e.deps, e.drag?.affordance), r, e.drag?.start ?? null);
46
- return a === null ? n.start(e, i) : p(n.start(a.carry(e), i), a);
56
+ let a = g(e.deps, t(e.deps, e.drag?.affordance), r, e.drag?.start ?? null);
57
+ return a === null ? n.start(e, i) : v(n.start(a.carry(e), i), a);
47
58
  }
48
59
  }
49
60
  };
50
61
  }
51
- function d(r, a, o, s) {
52
- let c = r.scene, l = r.view;
53
- if (a === void 0 || !c || !l) return null;
54
- let u = () => c.layers.find((e) => e.id === a)?.parallax;
55
- if (u() === void 0) return null;
56
- let d = {
62
+ function g(i, a, s, c) {
63
+ let l = i.scene, u = i.view;
64
+ if (a === void 0 || !l || !u || !l.layers.some((e) => e.parallax !== void 0)) return null;
65
+ let d = (e) => l.layers.find((t) => t.id === e)?.parallax, f = () => d(a), p = {
57
66
  scale: {
58
67
  x: 1,
59
68
  y: 1
@@ -62,99 +71,103 @@ function d(r, a, o, s) {
62
71
  x: 0,
63
72
  y: 0
64
73
  }
65
- }, p = () => {
66
- let e = u();
67
- return e ? n(l.get(), e) : d;
68
- }, m = Object.defineProperties(Object.create(l), {
74
+ }, m = () => {
75
+ let e = f();
76
+ return e ? n(u.get(), e) : p;
77
+ }, h = Object.defineProperties(Object.create(u), {
69
78
  get: { value: () => {
70
- let t = u(), n = l.get();
79
+ let t = f(), n = u.get();
71
80
  return t ? e(n, t) : n;
72
81
  } },
73
- plane: { value: p }
74
- }), h = Object.create(r), g = (e, t) => Object.defineProperty(h, e, {
82
+ plane: { value: m },
83
+ planeOf: { value: (e) => {
84
+ let t = f(), i = d(e), a = u.get();
85
+ return r(t ? n(a, t) : null, i ? n(a, i) : null);
86
+ } }
87
+ }), g = Object.create(i), v = (e, t) => Object.defineProperty(g, e, {
75
88
  value: t,
76
89
  enumerable: !0
77
90
  });
78
- g("view", m);
79
- let _ = o.includes("nodeAtPoint") ? r.nodeAtPoint : void 0;
80
- _ && g("nodeAtPoint", ((e, n) => _(t(p(), e), n)));
81
- let v = o.includes("snap") ? r.snap : void 0;
82
- v && g("snap", { point: (e) => i(p(), v.point(t(p(), e))) });
83
- let y = s ? i(p(), s) : null, b = f();
91
+ v("view", h);
92
+ let y = s.includes("nodeAtPoint") ? i.nodeAtPoint : void 0;
93
+ y && v("nodeAtPoint", ((e, n) => y(t(m(), e), n)));
94
+ let b = s.includes("snap") ? i.snap : void 0;
95
+ b && v("snap", { point: (e) => o(m(), b.point(t(m(), e))) });
96
+ let x = c ? o(m(), c) : null, S = _();
84
97
  return {
85
- map: p,
86
- deps: h,
98
+ map: m,
99
+ deps: g,
87
100
  carry(e) {
88
- let t = p(), n = {
101
+ let t = m(), n = {
89
102
  ...e,
90
- world: i(t, e.world)
103
+ world: o(t, e.world)
91
104
  };
92
105
  if (e.drag) {
93
- let r = y ?? i(t, e.drag.start), a = i(t, e.drag.current), o = e.drag.affordance;
106
+ let r = x ?? o(t, e.drag.start), i = o(t, e.drag.current), a = e.drag.affordance;
94
107
  n.drag = {
95
108
  ...e.drag,
96
109
  start: r,
97
- current: a,
110
+ current: i,
98
111
  delta: {
99
- x: a.x - r.x,
100
- y: a.y - r.y
112
+ x: i.x - r.x,
113
+ y: i.y - r.y
101
114
  },
102
- ...e.drag.points ? { points: b(e.drag.points, t) } : {},
103
- ...o?.fixedPoint ? { affordance: {
104
- ...o,
105
- fixedPoint: i(t, o.fixedPoint)
115
+ ...e.drag.points ? { points: S(e.drag.points, t) } : {},
116
+ ...a?.fixedPoint ? { affordance: {
117
+ ...a,
118
+ fixedPoint: o(t, a.fixedPoint)
106
119
  } } : {}
107
120
  };
108
121
  }
109
- return e.deps && (n.deps = h), n;
122
+ return e.deps && (n.deps = g), n;
110
123
  }
111
124
  };
112
125
  }
113
- function f() {
126
+ function _() {
114
127
  let e = null, t = null, n = [];
115
- return (r, a) => {
116
- let o = t !== null && t.scale.x === a.scale.x && t.scale.y === a.scale.y && t.offset.x === a.offset.x && t.offset.y === a.offset.y;
117
- (r !== e || !o || r.length < n.length) && (e = r, n = []), t = a;
128
+ return (r, i) => {
129
+ let a = t !== null && t.scale.x === i.scale.x && t.scale.y === i.scale.y && t.offset.x === i.offset.x && t.offset.y === i.offset.y;
130
+ (r !== e || !a || r.length < n.length) && (e = r, n = []), t = i;
118
131
  for (let e = n.length; e < r.length; e++) n.push({
119
132
  ...r[e],
120
- ...i(a, r[e])
133
+ ...o(i, r[e])
121
134
  });
122
135
  return n.slice();
123
136
  };
124
137
  }
125
- function p(e, t) {
138
+ function v(e, t) {
126
139
  let n = { ...e };
127
140
  return e.onMove && (n.onMove = (n) => e.onMove(t.carry(n))), e.onEnd && (n.onEnd = (n, r) => e.onEnd(t.carry(n), r)), e.overlay && (n.overlay = () => {
128
141
  let n = e.overlay();
129
- return n && m(n, t.map());
142
+ return n && y(n, t.map());
130
143
  }), n;
131
144
  }
132
- function m(e, n) {
133
- let i = (e) => t(n, e);
145
+ function y(e, n) {
146
+ let r = (e) => t(n, e);
134
147
  switch (e.kind) {
135
148
  case "marquee": return {
136
149
  ...e,
137
- start: i(e.start),
138
- current: i(e.current)
150
+ start: r(e.start),
151
+ current: r(e.current)
139
152
  };
140
153
  case "lasso": return {
141
154
  ...e,
142
- vertices: e.vertices.map(i),
143
- current: i(e.current)
155
+ vertices: e.vertices.map(r),
156
+ current: r(e.current)
144
157
  };
145
158
  case "polyline": return {
146
159
  ...e,
147
- points: e.points.map(i)
160
+ points: e.points.map(r)
148
161
  };
149
162
  case "insertPreview": return {
150
163
  ...e,
151
- bounds: r(n, e.bounds),
152
- extras: h(e.extras, n),
153
- ...e.anchorPoint ? { anchorPoint: i(e.anchorPoint) } : {}
164
+ bounds: i(n, e.bounds),
165
+ extras: b(e.extras, n),
166
+ ...e.anchorPoint ? { anchorPoint: r(e.anchorPoint) } : {}
154
167
  };
155
168
  }
156
169
  }
157
- function h(e, n) {
170
+ function b(e, n) {
158
171
  if (typeof e != "object" || !e) return e;
159
172
  let r = e, i = (e) => t(n, e), a = (e) => typeof e == "number" ? e / ((n.scale.x + n.scale.y) / 2) : e, o = { ...r };
160
173
  return r.a && r.b && (o.a = i(r.a), o.b = i(r.b)), r.center && (o.center = i(r.center)), "radius" in r && (o.radius = a(r.radius)), "outerRadius" in r && (o.outerRadius = a(r.outerRadius)), Array.isArray(r.samples) && (o.samples = r.samples.map((e) => ({
@@ -163,6 +176,6 @@ function h(e, n) {
163
176
  }))), o;
164
177
  }
165
178
  //#endregion
166
- export { l as editingLayer, u as inPlane, c as insertLayer, s as selectionLayer };
179
+ export { m as carryPose, d as editingLayer, h as inPlane, u as insertLayer, p as invertPlane, f as planeOf, l as selectionLayer };
167
180
 
168
181
  //# sourceMappingURL=planeInput.js.map