@weasel-js/core 1.11.0 → 1.11.1

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 (365) hide show
  1. package/CHANGELOG.md +427 -0
  2. package/README.md +1 -1
  3. package/dist/animation/index.js +3 -3
  4. package/dist/animation/rig/bindRig.js +1 -1
  5. package/dist/animation/rig/rigNode.js.map +1 -1
  6. package/dist/barrel/canvas.js +7 -3
  7. package/dist/barrel/interactions.js +5 -4
  8. package/dist/barrel/paths.js +3 -3
  9. package/dist/barrel/renderer.js +3 -2
  10. package/dist/barrel/routing.js +6 -11
  11. package/dist/barrel/scene.js +8 -5
  12. package/dist/barrel/viewport.js +4 -3
  13. package/dist/canvas/Canvas.js +3 -1
  14. package/dist/canvas/Canvas.js.map +1 -1
  15. package/dist/canvas/SceneCanvas/SceneCanvasInput.js +17 -3
  16. package/dist/canvas/SceneCanvas/SceneCanvasInput.js.map +1 -1
  17. package/dist/canvas/SceneCanvas/builtinToolSets.js +30 -0
  18. package/dist/canvas/SceneCanvas/builtinToolSets.js.map +1 -0
  19. package/dist/canvas/SceneCanvas/canvasPassThrough.js +1 -0
  20. package/dist/canvas/SceneCanvas/canvasPassThrough.js.map +1 -1
  21. package/dist/canvas/SceneCanvas/coordTrace.js +5 -16
  22. package/dist/canvas/SceneCanvas/coordTrace.js.map +1 -1
  23. package/dist/canvas/SceneCanvas/defaultNodeProperties.js +5 -0
  24. package/dist/canvas/SceneCanvas/defaultNodeProperties.js.map +1 -1
  25. package/dist/canvas/SceneCanvas/features.js +45 -5
  26. package/dist/canvas/SceneCanvas/features.js.map +1 -1
  27. package/dist/canvas/SceneCanvas/props.js +1 -11
  28. package/dist/canvas/SceneCanvas/props.js.map +1 -1
  29. package/dist/canvas/SceneCanvas/toolRegistry.js +8 -4
  30. package/dist/canvas/SceneCanvas/toolRegistry.js.map +1 -1
  31. package/dist/canvas/SceneCanvas/useActionsPropResolver.js +1 -11
  32. package/dist/canvas/SceneCanvas/useActionsPropResolver.js.map +1 -1
  33. package/dist/canvas/SceneCanvas/useBuiltinShapeTools.js.map +1 -1
  34. package/dist/canvas/SceneCanvas/useBuiltinTools.js +2 -9
  35. package/dist/canvas/SceneCanvas/useBuiltinTools.js.map +1 -1
  36. package/dist/canvas/SceneCanvas/useCanvasDevtoolsRoot.js +93 -0
  37. package/dist/canvas/SceneCanvas/useCanvasDevtoolsRoot.js.map +1 -0
  38. package/dist/canvas/SceneCanvas/useCanvasSelection.js +7 -2
  39. package/dist/canvas/SceneCanvas/useCanvasSelection.js.map +1 -1
  40. package/dist/canvas/SceneCanvas/useCanvasTools.js +8 -8
  41. package/dist/canvas/SceneCanvas/useCanvasTools.js.map +1 -1
  42. package/dist/canvas/SceneCanvas/useNodeKinds.js +1 -1
  43. package/dist/canvas/SceneCanvas/useNodeKinds.js.map +1 -1
  44. package/dist/canvas/SceneCanvas/useSceneCanvasLayers.js +13 -2
  45. package/dist/canvas/SceneCanvas/useSceneCanvasLayers.js.map +1 -1
  46. package/dist/canvas/SceneCanvas/useSceneCanvasTools.js +1 -3
  47. package/dist/canvas/SceneCanvas/useSceneCanvasTools.js.map +1 -1
  48. package/dist/canvas/SceneCanvas/useSceneChrome.js +6 -7
  49. package/dist/canvas/SceneCanvas/useSceneChrome.js.map +1 -1
  50. package/dist/canvas/SceneCanvas/useSceneGestureDispatcher.js +5 -3
  51. package/dist/canvas/SceneCanvas/useSceneGestureDispatcher.js.map +1 -1
  52. package/dist/canvas/SceneCanvas/useScenePicking.js +2 -3
  53. package/dist/canvas/SceneCanvas/useScenePicking.js.map +1 -1
  54. package/dist/canvas/SceneCanvas/useSceneSelectTool.js +2 -1
  55. package/dist/canvas/SceneCanvas/useSceneSelectTool.js.map +1 -1
  56. package/dist/canvas/SceneCanvas/useSceneStandardActions.js +3 -4
  57. package/dist/canvas/SceneCanvas/useSceneStandardActions.js.map +1 -1
  58. package/dist/canvas/SceneCanvas.js +16 -14
  59. package/dist/canvas/SceneCanvas.js.map +1 -1
  60. package/dist/canvas/SurfaceCanvas.js +5 -22
  61. package/dist/canvas/SurfaceCanvas.js.map +1 -1
  62. package/dist/canvas/canvasRenderer.js +9 -5
  63. package/dist/canvas/canvasRenderer.js.map +1 -1
  64. package/dist/canvas/contextAttributes.js +14 -0
  65. package/dist/canvas/contextAttributes.js.map +1 -0
  66. package/dist/canvas/deps/editAnchors.js +9 -113
  67. package/dist/canvas/deps/editAnchors.js.map +1 -1
  68. package/dist/canvas/deps/insert.js +4 -2
  69. package/dist/canvas/deps/insert.js.map +1 -1
  70. package/dist/canvas/deps/pathEditCommits.js +193 -0
  71. package/dist/canvas/deps/pathEditCommits.js.map +1 -0
  72. package/dist/canvas/deps/snap.js +12 -9
  73. package/dist/canvas/deps/snap.js.map +1 -1
  74. package/dist/canvas/deps/transformFollowers.js +20 -0
  75. package/dist/canvas/deps/transformFollowers.js.map +1 -0
  76. package/dist/canvas/derivedPath.js +20 -1
  77. package/dist/canvas/derivedPath.js.map +1 -1
  78. package/dist/canvas/drawnBounds.js +29 -3
  79. package/dist/canvas/drawnBounds.js.map +1 -1
  80. package/dist/canvas/nodeShapes/derivedPainter.js +9 -1
  81. package/dist/canvas/nodeShapes/derivedPainter.js.map +1 -1
  82. package/dist/canvas/nodeShapes/pathPainter.js +3 -2
  83. package/dist/canvas/nodeShapes/pathPainter.js.map +1 -1
  84. package/dist/canvas/nodeShapes/shapePainter.js +1 -1
  85. package/dist/canvas/overlayRoleStrokes.js +12 -0
  86. package/dist/canvas/overlayRoleStrokes.js.map +1 -1
  87. package/dist/canvas/pathOverlay.js +27 -7
  88. package/dist/canvas/pathOverlay.js.map +1 -1
  89. package/dist/canvas/renderDebugSnapshot.js +1 -0
  90. package/dist/canvas/renderDebugSnapshot.js.map +1 -1
  91. package/dist/canvas/renderSceneToPixels.js +6 -5
  92. package/dist/canvas/renderSceneToPixels.js.map +1 -1
  93. package/dist/canvas/sceneAdapter.js.map +1 -1
  94. package/dist/canvas/useCanvasPaint.js +2 -1
  95. package/dist/canvas/useCanvasPaint.js.map +1 -1
  96. package/dist/canvas/useInFlightBounds.js +52 -0
  97. package/dist/canvas/useInFlightBounds.js.map +1 -0
  98. package/dist/canvas/useViewHelpers.js +7 -9
  99. package/dist/canvas/useViewHelpers.js.map +1 -1
  100. package/dist/clipboard.d.ts +3 -2
  101. package/dist/{clone-DZDrfIlU.d.ts → clone-C3e0OTuF.d.ts} +8 -21
  102. package/dist/clone.d.ts +4 -3
  103. package/dist/core/geometry/autoPoseDescriptor.js +1 -1
  104. package/dist/core/ops/setDependsOn.js +37 -0
  105. package/dist/core/ops/setDependsOn.js.map +1 -0
  106. package/dist/core/ops/writtenBy.js +53 -0
  107. package/dist/core/ops/writtenBy.js.map +1 -0
  108. package/dist/core/pages/fitViewToPage.js +12 -1
  109. package/dist/core/pages/fitViewToPage.js.map +1 -1
  110. package/dist/core/pages/pageEdits.js +164 -0
  111. package/dist/core/pages/pageEdits.js.map +1 -0
  112. package/dist/core/pages/pages.js +30 -1
  113. package/dist/core/pages/pages.js.map +1 -1
  114. package/dist/core/scene/NodeRouting.js +8 -3
  115. package/dist/core/scene/NodeRouting.js.map +1 -1
  116. package/dist/core/scene/arrivalWindow.js.map +1 -1
  117. package/dist/core/scene/carryDescendants.js +51 -0
  118. package/dist/core/scene/carryDescendants.js.map +1 -0
  119. package/dist/core/scene/docValues.js +57 -0
  120. package/dist/core/scene/docValues.js.map +1 -0
  121. package/dist/core/scene/editPass.js +111 -0
  122. package/dist/core/scene/editPass.js.map +1 -0
  123. package/dist/core/scene/effectivePose.js +7 -1
  124. package/dist/core/scene/effectivePose.js.map +1 -1
  125. package/dist/core/scene/fieldEdits.js +4 -1
  126. package/dist/core/scene/fieldEdits.js.map +1 -1
  127. package/dist/core/scene/historyFacade.js +1 -0
  128. package/dist/core/scene/historyFacade.js.map +1 -1
  129. package/dist/core/scene/layoutPass.js +28 -3
  130. package/dist/core/scene/layoutPass.js.map +1 -1
  131. package/dist/core/scene/recorder.js +28 -19
  132. package/dist/core/scene/recorder.js.map +1 -1
  133. package/dist/core/scene/scene.js +15 -9
  134. package/dist/core/scene/scene.js.map +1 -1
  135. package/dist/core/scene/sceneChanges.js +13 -0
  136. package/dist/core/scene/sceneChanges.js.map +1 -0
  137. package/dist/core/scene/sceneEdits.js +8 -1
  138. package/dist/core/scene/sceneEdits.js.map +1 -1
  139. package/dist/core/scene/sceneMutators.js +1 -0
  140. package/dist/core/scene/sceneMutators.js.map +1 -1
  141. package/dist/core/scene/sceneRecording.js +20 -3
  142. package/dist/core/scene/sceneRecording.js.map +1 -1
  143. package/dist/core/scene/sceneSelection.js +4 -1
  144. package/dist/core/scene/sceneSelection.js.map +1 -1
  145. package/dist/core/scene/sceneState.js +11 -5
  146. package/dist/core/scene/sceneState.js.map +1 -1
  147. package/dist/core/scene/serialization.js +5 -3
  148. package/dist/core/scene/serialization.js.map +1 -1
  149. package/dist/core/scene/types.js.map +1 -1
  150. package/dist/core/scene/unitScopes.js +19 -1
  151. package/dist/core/scene/unitScopes.js.map +1 -1
  152. package/dist/core/selection/usePublishSceneSelection.js +6 -12
  153. package/dist/core/selection/usePublishSceneSelection.js.map +1 -1
  154. package/dist/core/shapeKinds.js +25 -11
  155. package/dist/core/shapeKinds.js.map +1 -1
  156. package/dist/{cornerHandles-Camw_yeu.d.ts → cornerHandles-BHuoMs1o.d.ts} +1 -1
  157. package/dist/debug/createDebugOverlayLayer.js +16 -111
  158. package/dist/debug/createDebugOverlayLayer.js.map +1 -1
  159. package/dist/debug/createDebugSink.js +2 -1
  160. package/dist/debug/createDebugSink.js.map +1 -1
  161. package/dist/debug/defaultTheme.js +4 -2
  162. package/dist/debug/defaultTheme.js.map +1 -1
  163. package/dist/debug/features.js +17 -0
  164. package/dist/debug/features.js.map +1 -0
  165. package/dist/debug/overlayPanels.js +117 -0
  166. package/dist/debug/overlayPanels.js.map +1 -0
  167. package/dist/debug/parseDebugFlags.js +4 -13
  168. package/dist/debug/parseDebugFlags.js.map +1 -1
  169. package/dist/{depSchema-Cgfu_G9s.d.ts → depSchema-BpZNOv7q.d.ts} +114 -892
  170. package/dist/devtools/handlers.js +112 -0
  171. package/dist/devtools/handlers.js.map +1 -0
  172. package/dist/devtools/history.js +71 -0
  173. package/dist/devtools/history.js.map +1 -0
  174. package/dist/devtools/hook.js +74 -0
  175. package/dist/devtools/hook.js.map +1 -0
  176. package/dist/devtools/label.js +25 -0
  177. package/dist/devtools/label.js.map +1 -0
  178. package/dist/devtools/notices.js +58 -0
  179. package/dist/devtools/notices.js.map +1 -0
  180. package/dist/devtools/protocol.js +7 -0
  181. package/dist/devtools/protocol.js.map +1 -0
  182. package/dist/devtools/registry.js +90 -0
  183. package/dist/devtools/registry.js.map +1 -0
  184. package/dist/devtools/root.js +80 -0
  185. package/dist/devtools/root.js.map +1 -0
  186. package/dist/devtools/serialize.js +86 -0
  187. package/dist/devtools/serialize.js.map +1 -0
  188. package/dist/devtools/session.js +111 -0
  189. package/dist/devtools/session.js.map +1 -0
  190. package/dist/devtools/transport.js +67 -0
  191. package/dist/devtools/transport.js.map +1 -0
  192. package/dist/devtools/useDevtoolsRoot.js +81 -0
  193. package/dist/devtools/useDevtoolsRoot.js.map +1 -0
  194. package/dist/devtools-protocol.d.ts +435 -0
  195. package/dist/devtools-protocol.js +7 -0
  196. package/dist/devtools.d.ts +8 -0
  197. package/dist/devtools.js +12 -0
  198. package/dist/devtools.js.map +1 -0
  199. package/dist/features/chrome-caps/useHoverTracking.js +16 -10
  200. package/dist/features/chrome-caps/useHoverTracking.js.map +1 -1
  201. package/dist/features/ingestion/contentHandlers.js +25 -2
  202. package/dist/features/ingestion/contentHandlers.js.map +1 -1
  203. package/dist/features/measure/measureCommands.js +7 -5
  204. package/dist/features/measure/measureCommands.js.map +1 -1
  205. package/dist/features/paths/index.js +2 -2
  206. package/dist/features/paths/pathEditExtension.js.map +1 -1
  207. package/dist/features/paths/pathSnap/pathSnapSource.js +5 -5
  208. package/dist/features/paths/pathSnap/pathSnapSource.js.map +1 -1
  209. package/dist/features/paths/pathSnap/sceneIndex.js +10 -3
  210. package/dist/features/paths/pathSnap/sceneIndex.js.map +1 -1
  211. package/dist/features/selection/selectionPose.js +4 -1
  212. package/dist/features/selection/selectionPose.js.map +1 -1
  213. package/dist/features/snap/gather.js +9 -1
  214. package/dist/features/snap/gather.js.map +1 -1
  215. package/dist/features/snap/lazySession.js +17 -0
  216. package/dist/features/snap/lazySession.js.map +1 -0
  217. package/dist/features/snap/marks.js.map +1 -1
  218. package/dist/features/snap/probes2d.js +27 -19
  219. package/dist/features/snap/probes2d.js.map +1 -1
  220. package/dist/features/snap/readoutLayer.js.map +1 -1
  221. package/dist/features/snap/roles.js +2 -1
  222. package/dist/features/snap/roles.js.map +1 -1
  223. package/dist/features/snap/rotationSource.js +25 -8
  224. package/dist/features/snap/rotationSource.js.map +1 -1
  225. package/dist/features/snap/sceneSession2d.js +6 -4
  226. package/dist/features/snap/sceneSession2d.js.map +1 -1
  227. package/dist/features/snap/session2d.js +37 -23
  228. package/dist/features/snap/session2d.js.map +1 -1
  229. package/dist/features/snap/spanSnap.js +5 -2
  230. package/dist/features/snap/spanSnap.js.map +1 -1
  231. package/dist/icons.table.js +1 -1
  232. package/dist/icons.table.js.map +1 -1
  233. package/dist/icons.table.json +0 -10
  234. package/dist/index.d.ts +701 -696
  235. package/dist/index.js +35 -29
  236. package/dist/interactions/actions/align/action.js +6 -4
  237. package/dist/interactions/actions/align/action.js.map +1 -1
  238. package/dist/interactions/actions/align/align.js.map +1 -1
  239. package/dist/interactions/actions/booleans/action.js +7 -1
  240. package/dist/interactions/actions/booleans/action.js.map +1 -1
  241. package/dist/interactions/actions/clone/action.js +1 -1
  242. package/dist/interactions/actions/clone/behaviors.js +4 -4
  243. package/dist/interactions/actions/clone/behaviors.js.map +1 -1
  244. package/dist/interactions/actions/defaults/bendSegment.js +1 -1
  245. package/dist/interactions/actions/defaults/bendSegment.js.map +1 -1
  246. package/dist/interactions/actions/defaults/createPaintAction.js +1 -1
  247. package/dist/interactions/actions/defaults/delete.js +48 -39
  248. package/dist/interactions/actions/defaults/delete.js.map +1 -1
  249. package/dist/interactions/actions/defaults/duplicate.js +1 -1
  250. package/dist/interactions/actions/defaults/group.js +2 -2
  251. package/dist/interactions/actions/defaults/ingest.js +7 -5
  252. package/dist/interactions/actions/defaults/ingest.js.map +1 -1
  253. package/dist/interactions/actions/defaults/nudge.js +13 -38
  254. package/dist/interactions/actions/defaults/nudge.js.map +1 -1
  255. package/dist/interactions/actions/distribute/action.js +6 -4
  256. package/dist/interactions/actions/distribute/action.js.map +1 -1
  257. package/dist/interactions/actions/drawnBoxes.js +2 -1
  258. package/dist/interactions/actions/drawnBoxes.js.map +1 -1
  259. package/dist/interactions/actions/edit-anchors/action.js +3 -6
  260. package/dist/interactions/actions/edit-anchors/action.js.map +1 -1
  261. package/dist/interactions/actions/edit-anchors/preview.js +41 -8
  262. package/dist/interactions/actions/edit-anchors/preview.js.map +1 -1
  263. package/dist/interactions/actions/flip/action.js +6 -4
  264. package/dist/interactions/actions/flip/action.js.map +1 -1
  265. package/dist/interactions/actions/gestureLifecycle.js +5 -2
  266. package/dist/interactions/actions/gestureLifecycle.js.map +1 -1
  267. package/dist/interactions/actions/insert/action.js +1 -2
  268. package/dist/interactions/actions/insert/action.js.map +1 -1
  269. package/dist/interactions/actions/insert/commit.js +1 -1
  270. package/dist/interactions/actions/insert/parent.js +11 -0
  271. package/dist/interactions/actions/insert/parent.js.map +1 -0
  272. package/dist/interactions/actions/insert/placeAction.js +7 -2
  273. package/dist/interactions/actions/insert/placeAction.js.map +1 -1
  274. package/dist/interactions/actions/insert/session.js +5 -1
  275. package/dist/interactions/actions/insert/session.js.map +1 -1
  276. package/dist/interactions/actions/move/action.js +8 -4
  277. package/dist/interactions/actions/move/action.js.map +1 -1
  278. package/dist/interactions/actions/move/behaviors/layoutDrop.js +0 -1
  279. package/dist/interactions/actions/move/behaviors/layoutDrop.js.map +1 -1
  280. package/dist/interactions/actions/move/behaviors/snapToContainer.js +1 -1
  281. package/dist/interactions/actions/move/capture.js +1 -1
  282. package/dist/interactions/actions/move/commit/index.js +0 -2
  283. package/dist/interactions/actions/move/commit/index.js.map +1 -1
  284. package/dist/interactions/actions/move/commit/layoutDrop.js +2 -2
  285. package/dist/interactions/actions/move/commit/layoutRelease.js +2 -2
  286. package/dist/interactions/actions/move/commit/reparent.js +11 -5
  287. package/dist/interactions/actions/move/commit/reparent.js.map +1 -1
  288. package/dist/interactions/actions/move/frame.js +4 -10
  289. package/dist/interactions/actions/move/frame.js.map +1 -1
  290. package/dist/interactions/actions/move/landedDescendants.js +20 -17
  291. package/dist/interactions/actions/move/landedDescendants.js.map +1 -1
  292. package/dist/interactions/actions/move/layout/container.js +2 -1
  293. package/dist/interactions/actions/move/layout/container.js.map +1 -1
  294. package/dist/interactions/actions/move/layout/dragged.js +1 -1
  295. package/dist/interactions/actions/move/layout/dropPass.js +18 -1
  296. package/dist/interactions/actions/move/layout/dropPass.js.map +1 -1
  297. package/dist/interactions/actions/move/layout/exits.js +5 -5
  298. package/dist/interactions/actions/move/layout/exits.js.map +1 -1
  299. package/dist/interactions/actions/move/session.js +14 -3
  300. package/dist/interactions/actions/move/session.js.map +1 -1
  301. package/dist/interactions/actions/move/translate.js +8 -37
  302. package/dist/interactions/actions/move/translate.js.map +1 -1
  303. package/dist/interactions/actions/moveNodes.js +87 -0
  304. package/dist/interactions/actions/moveNodes.js.map +1 -0
  305. package/dist/interactions/actions/planeInput.js.map +1 -1
  306. package/dist/interactions/actions/resize/action.js +83 -72
  307. package/dist/interactions/actions/resize/action.js.map +1 -1
  308. package/dist/interactions/actions/resize/commit.js +2 -1
  309. package/dist/interactions/actions/resize/commit.js.map +1 -1
  310. package/dist/interactions/actions/resize/session.js +2 -0
  311. package/dist/interactions/actions/resize/session.js.map +1 -1
  312. package/dist/interactions/actions/resolvePreviews.js +3 -3
  313. package/dist/interactions/actions/resolvePreviews.js.map +1 -1
  314. package/dist/interactions/actions/rotate/action.js +7 -4
  315. package/dist/interactions/actions/rotate/action.js.map +1 -1
  316. package/dist/interactions/actions/rotate/commit.js +2 -1
  317. package/dist/interactions/actions/rotate/commit.js.map +1 -1
  318. package/dist/interactions/actions/rotate/session.js +2 -0
  319. package/dist/interactions/actions/rotate/session.js.map +1 -1
  320. package/dist/interactions/actions/selectionSubtree.js +13 -2
  321. package/dist/interactions/actions/selectionSubtree.js.map +1 -1
  322. package/dist/interactions/actions/testUtils.js.map +1 -1
  323. package/dist/interactions/actions/transform-again/action.js +5 -6
  324. package/dist/interactions/actions/transform-again/action.js.map +1 -1
  325. package/dist/interactions/actions/transformFinish.js +106 -0
  326. package/dist/interactions/actions/transformFinish.js.map +1 -0
  327. package/dist/kitSettingsSchema.js +0 -4
  328. package/dist/kitSettingsSchema.js.map +1 -1
  329. package/dist/move.d.ts +5 -4
  330. package/dist/{options-p9GZMXbY.d.ts → options-DBxEryYq.d.ts} +1 -1
  331. package/dist/resize.d.ts +4 -3
  332. package/dist/routing.d.ts +1 -1
  333. package/dist/routing.js +2 -2
  334. package/dist/test-hook/createTestHook.js +1 -1
  335. package/dist/test-hook/createTestHook.js.map +1 -1
  336. package/dist/test-hook/useTestHook.js +2 -1
  337. package/dist/test-hook/useTestHook.js.map +1 -1
  338. package/dist/test-seams.d.ts +9 -5
  339. package/dist/tools/builtin/pen/penHandle.js +3 -5
  340. package/dist/tools/builtin/pen/penHandle.js.map +1 -1
  341. package/dist/tools/index.js +26 -0
  342. package/dist/{types-CHVXGfYm.d.ts → types-7puAuVD2.d.ts} +3 -262
  343. package/dist/types-BN9hbdMI.d.ts +184 -0
  344. package/dist/types-CniVctmk.d.ts +949 -0
  345. package/dist/types-oFeQ5VjV.d.ts +264 -0
  346. package/dist/version.js +1 -1
  347. package/package.json +35 -25
  348. package/dist/canvas/SceneCanvas/useBuiltinRigTools.js +0 -65
  349. package/dist/canvas/SceneCanvas/useBuiltinRigTools.js.map +0 -1
  350. package/dist/tools/builtin/bone/boneActions.js +0 -156
  351. package/dist/tools/builtin/bone/boneActions.js.map +0 -1
  352. package/dist/tools/builtin/bone/boneOverlay.js +0 -71
  353. package/dist/tools/builtin/bone/boneOverlay.js.map +0 -1
  354. package/dist/tools/builtin/bone/placedRig.js +0 -28
  355. package/dist/tools/builtin/bone/placedRig.js.map +0 -1
  356. package/dist/tools/builtin/bone/rigEditState.js +0 -30
  357. package/dist/tools/builtin/bone/rigEditState.js.map +0 -1
  358. package/dist/tools/builtin/bone/useBoneTool.js +0 -125
  359. package/dist/tools/builtin/bone/useBoneTool.js.map +0 -1
  360. package/dist/tools/builtin/weight/useWeightTool.js +0 -101
  361. package/dist/tools/builtin/weight/useWeightTool.js.map +0 -1
  362. package/dist/tools/builtin/weight/weightActions.js +0 -140
  363. package/dist/tools/builtin/weight/weightActions.js.map +0 -1
  364. package/dist/tools/builtin/weight/weightOverlay.js +0 -78
  365. package/dist/tools/builtin/weight/weightOverlay.js.map +0 -1
@@ -1 +1 @@
1
- {"version":3,"file":"landedDescendants.js","names":[],"sources":["../../../../src/interactions/actions/move/landedDescendants.ts"],"sourcesContent":["import type { NodeId, Scene } from 'core/scene/types';\nimport type { Op } from 'core/ops/types';\nimport { createTransformOp } from 'core/ops/transform';\nimport { translatePoseViaDescriptor, type PoseDescriptor } from 'core/geometry/poseDescriptor';\nimport { subtreeIds } from '../selectionSubtree';\n\n/** What a move gesture captured at its start, as this pass reads it. */\nexport interface LandedDescendantsInput {\n scene: Scene<unknown, string, unknown>;\n projection: PoseDescriptor<unknown>;\n /** The dragged roots. */\n roots: readonly NodeId[];\n /** Start pose of every root and descendant. */\n startPoses: ReadonlyMap<NodeId, unknown>;\n /** Descendants stored in a moving node's frame, which that node carries. */\n carried: ReadonlySet<NodeId>;\n label: string;\n}\n\n/**\n * Ops that bring each dragged root's descendants along to wherever `ops` put\n * the root. A layout drop or a behavior places the root itself, somewhere the\n * drag delta does not describe, so each descendant moves by its root's landed\n * pose less its start pose. A descendant `ops` already writes, or that a\n * moving frame carries, is left alone.\n */\nexport function landedDescendantOps(input: LandedDescendantsInput, ops: readonly Op[]): Op[] {\n const { scene, projection, startPoses, carried, label } = input;\n const landed = new Map<string, unknown>();\n for (const op of ops) {\n if (op.name !== 'transform') continue;\n const a = op.args as { id: string; to: unknown };\n landed.set(a.id, a.to);\n }\n const out: Op[] = [];\n for (const root of input.roots) {\n const to = landed.get(root);\n const start = startPoses.get(root);\n if (to === undefined || start === undefined) continue;\n const a = projection.getBounds(start);\n const b = projection.getBounds(to);\n const dx = b.x - a.x;\n const dy = b.y - a.y;\n if (dx === 0 && dy === 0) continue;\n for (const id of subtreeIds(scene, root)) {\n if (id === root || landed.has(id) || carried.has(id)) continue;\n const node = scene.get(id);\n if (!node) continue;\n out.push(createTransformOp<unknown>({\n id,\n from: node.pose,\n to: translatePoseViaDescriptor(node.pose, dx, dy, projection),\n label,\n }));\n }\n }\n return out;\n}\n"],"mappings":";;;;;;;;;;;;AA0BA,SAAgB,oBAAoB,OAA+B,KAA0B;CAC3F,MAAM,EAAE,OAAO,YAAY,YAAY,SAAS,UAAU;CAC1D,MAAM,yBAAS,IAAI,IAAqB;CACxC,KAAK,MAAM,MAAM,KAAK;EACpB,IAAI,GAAG,SAAS,aAAa;EAC7B,MAAM,IAAI,GAAG;EACb,OAAO,IAAI,EAAE,IAAI,EAAE,EAAE;CACvB;CACA,MAAM,MAAY,CAAC;CACnB,KAAK,MAAM,QAAQ,MAAM,OAAO;EAC9B,MAAM,KAAK,OAAO,IAAI,IAAI;EAC1B,MAAM,QAAQ,WAAW,IAAI,IAAI;EACjC,IAAI,OAAO,UAAa,UAAU,QAAW;EAC7C,MAAM,IAAI,WAAW,UAAU,KAAK;EACpC,MAAM,IAAI,WAAW,UAAU,EAAE;EACjC,MAAM,KAAK,EAAE,IAAI,EAAE;EACnB,MAAM,KAAK,EAAE,IAAI,EAAE;EACnB,IAAI,OAAO,KAAK,OAAO,GAAG;EAC1B,KAAK,MAAM,MAAM,WAAW,OAAO,IAAI,GAAG;GACxC,IAAI,OAAO,QAAQ,OAAO,IAAI,EAAE,KAAK,QAAQ,IAAI,EAAE,GAAG;GACtD,MAAM,OAAO,MAAM,IAAI,EAAE;GACzB,IAAI,CAAC,MAAM;GACX,IAAI,KAAK,kBAA2B;IAClC;IACA,MAAM,KAAK;IACX,IAAI,2BAA2B,KAAK,MAAM,IAAI,IAAI,UAAU;IAC5D;GACF,CAAC,CAAC;EACJ;CACF;CACA,OAAO;AACT"}
1
+ {"version":3,"file":"landedDescendants.js","names":[],"sources":["../../../../src/interactions/actions/move/landedDescendants.ts"],"sourcesContent":["import type { NodeId, Scene } from 'core/scene/types';\nimport type { Op } from 'core/ops/types';\nimport { createTransformOp } from 'core/ops/transform';\nimport { translatePoseViaDescriptor, type PoseDescriptor } from 'core/geometry/poseDescriptor';\nimport { carriedDescendants, sceneCarrySource } from 'core/scene/carryDescendants';\n\n/** What a move gesture captured at its start, as this pass reads it. */\nexport interface LandedDescendantsInput {\n scene: Scene<unknown, string, unknown>;\n projection: PoseDescriptor<unknown>;\n /** Start pose of every root and descendant. */\n startPoses: ReadonlyMap<NodeId, unknown>;\n label: string;\n}\n\n/**\n * Ops that bring along the descendants of every node `ops` transforms. A\n * layout drop or a behavior places a dragged root somewhere the drag delta\n * does not describe, and a layout shifts siblings to make room, so each\n * descendant moves by its nearest transformed ancestor's landed pose less\n * that ancestor's start pose. A descendant `ops` already writes, or that a\n * moving frame carries, is left alone.\n */\nexport function landedDescendantOps(input: LandedDescendantsInput, ops: readonly Op[]): Op[] {\n const { scene, projection, startPoses, label } = input;\n const landed = new Map<NodeId, { from: unknown; to: unknown }>();\n for (const op of ops) {\n if (op.name !== 'transform') continue;\n const a = op.args as { id: string; from: unknown; to: unknown };\n const id = a.id as NodeId;\n landed.set(id, { from: landed.get(id)?.from ?? startPoses.get(id) ?? a.from, to: a.to });\n }\n const source = sceneCarrySource(scene);\n const out: Op[] = [];\n for (const [root, { from, to }] of landed) {\n if (from === undefined || to === undefined) continue;\n const a = projection.getBounds(from);\n const b = projection.getBounds(to);\n const dx = b.x - a.x;\n const dy = b.y - a.y;\n if (dx === 0 && dy === 0) continue;\n const carried = carriedDescendants(\n source,\n root,\n (pose) => translatePoseViaDescriptor(pose, dx, dy, projection),\n (id) => landed.has(id),\n );\n for (const [id, pose] of carried) {\n const node = scene.get(id);\n if (node) out.push(createTransformOp<unknown>({ id, from: node.pose, to: pose, label }));\n }\n }\n return out;\n}\n"],"mappings":";;;;;;;;;;;;;AAuBA,SAAgB,oBAAoB,OAA+B,KAA0B;CAC3F,MAAM,EAAE,OAAO,YAAY,YAAY,UAAU;CACjD,MAAM,yBAAS,IAAI,IAA4C;CAC/D,KAAK,MAAM,MAAM,KAAK;EACpB,IAAI,GAAG,SAAS,aAAa;EAC7B,MAAM,IAAI,GAAG;EACb,MAAM,KAAK,EAAE;EACb,OAAO,IAAI,IAAI;GAAE,MAAM,OAAO,IAAI,EAAE,CAAC,EAAE,QAAQ,WAAW,IAAI,EAAE,KAAK,EAAE;GAAM,IAAI,EAAE;EAAG,CAAC;CACzF;CACA,MAAM,SAAS,iBAAiB,KAAK;CACrC,MAAM,MAAY,CAAC;CACnB,KAAK,MAAM,CAAC,MAAM,EAAE,MAAM,SAAS,QAAQ;EACzC,IAAI,SAAS,UAAa,OAAO,QAAW;EAC5C,MAAM,IAAI,WAAW,UAAU,IAAI;EACnC,MAAM,IAAI,WAAW,UAAU,EAAE;EACjC,MAAM,KAAK,EAAE,IAAI,EAAE;EACnB,MAAM,KAAK,EAAE,IAAI,EAAE;EACnB,IAAI,OAAO,KAAK,OAAO,GAAG;EAC1B,MAAM,UAAU,mBACd,QACA,OACC,SAAS,2BAA2B,MAAM,IAAI,IAAI,UAAU,IAC5D,OAAO,OAAO,IAAI,EAAE,CACvB;EACA,KAAK,MAAM,CAAC,IAAI,SAAS,SAAS;GAChC,MAAM,OAAO,MAAM,IAAI,EAAE;GACzB,IAAI,MAAM,IAAI,KAAK,kBAA2B;IAAE;IAAI,MAAM,KAAK;IAAM,IAAI;IAAM;GAAM,CAAC,CAAC;EACzF;CACF;CACA,OAAO;AACT"}
@@ -1,8 +1,8 @@
1
1
  import { toPlane } from "../../../../core/viewport/parallax.js";
2
2
  import { composeWorldPose } from "../../../../core/scene/composeFrame.js";
3
3
  import { poseDescriptorForNode } from "../../../../core/geometry/poseDescriptor.js";
4
- import { planeOf } from "../../planeInput.js";
5
4
  import { scenePoseAdapter } from "../../../../core/scene/scenePoseAdapter.js";
5
+ import { planeOf } from "../../planeInput.js";
6
6
  import { carryDragged } from "./dragged.js";
7
7
  import { pointInPath } from "@weasel-js/geom";
8
8
 
@@ -80,6 +80,7 @@ function pickContainer(session, dragged, center) {
80
80
  const plane = planeOf(session.view, node.layer);
81
81
  const at = plane ? toPlane(plane, center) : center;
82
82
  if (!testInside(id, worldPose, worldAABB, layout, at)) return;
83
+ if (!session.admits(id)) return;
83
84
  if (layout.acceptsDrop) {
84
85
  const arg = {
85
86
  id,
@@ -1 +1 @@
1
- {"version":3,"file":"container.js","names":[],"sources":["../../../../../src/interactions/actions/move/layout/container.ts"],"sourcesContent":["import { pointInPath } from '@weasel-js/geom';\nimport type { NodeId, Scene } from 'core/scene/types';\nimport { poseDescriptorForNode } from 'core/geometry/poseDescriptor';\nimport { toPlane, type PlaneMap } from 'core/viewport/parallax';\nimport { composeWorldPose } from 'core/scene/composePose';\nimport { scenePoseAdapter } from 'core/scene/scenePoseAdapter';\nimport type { DropRegion, LayoutContainer, LayoutStrategy } from '../../../../layout/types';\nimport type { LayoutDep } from '../../depSchema';\nimport { planeOf } from '../../planeInput';\nimport type { MoveSession } from '../session';\nimport { carryDragged, type DraggedChild } from './dragged';\n\nexport interface Bounds4 { x: number; y: number; width: number; height: number }\nexport function pointInBounds(b: Bounds4, at: { x: number; y: number }): boolean {\n return at.x >= b.x && at.x < b.x + b.width && at.y >= b.y && at.y < b.y + b.height;\n}\n\ntype Layout = LayoutStrategy<unknown>;\nexport interface Candidate {\n id: NodeId;\n bounds: Bounds4;\n layout: LayoutStrategy<unknown>;\n zPath: number[];\n depth: number;\n /** The edited plane into this container's. */\n plane: PlaneMap | null;\n}\n\n/** Whether a world point falls in a drop region, which is authored relative to\n * the top-left of the container's world bounds. */\nfunction regionContains(\n region: DropRegion,\n bounds: { x: number; y: number },\n point: { x: number; y: number },\n): boolean {\n const local = { x: point.x - bounds.x, y: point.y - bounds.y };\n return typeof region === 'function'\n ? region(local)\n : pointInPath(region, local.x, local.y);\n}\n\nfunction chooseContainer(\n scene: Scene<unknown, string, unknown>,\n mode: NonNullable<LayoutDep['dropTarget']>,\n candidates: readonly Candidate[],\n): Candidate | null {\n let dest: Candidate | null = null;\n if (mode === 'topmost') {\n // Painted last wins, in the scene's own render order — which crosses\n // subtrees and layers, where a sibling-index path cannot.\n const byId = new Map<NodeId, Candidate>(candidates.map((c) => [c.id, c]));\n for (const id of scene.renderOrder()) dest = byId.get(id) ?? dest;\n } else {\n // Deepest wins; sibling-index path breaks ties (higher z = later index).\n for (const c of candidates) {\n if (dest === null) { dest = c; continue; }\n if (c.depth > dest.depth) { dest = c; continue; }\n if (c.depth < dest.depth) continue;\n let cAfter = false;\n for (let i = 0; i < c.zPath.length; i++) {\n if (c.zPath[i] > dest.zPath[i]) { cAfter = true; break; }\n if (c.zPath[i] < dest.zPath[i]) { cAfter = false; break; }\n }\n if (cAfter) dest = c;\n }\n }\n return dest;\n}\n\n/** The layout container that takes the drop at `center`, or null. */\nexport function pickContainer(\n session: MoveSession,\n dragged: readonly DraggedChild[],\n center: { x: number; y: number },\n): Candidate | null {\n const layoutDep = session.layout;\n if (!layoutDep) return null;\n const scene = session.scene;\n const pc = session.pc;\n const poseAdapter = scenePoseAdapter(scene);\n const d = session.projection;\n const draggedIds = new Set<NodeId>(dragged.map((c) => c.id));\n const candidates: Candidate[] = [];\n const mode = layoutDep.dropTarget ?? 'innermost';\n const testInside = (\n id: NodeId, cPose: unknown, aabb: Bounds4, layout: Layout, at: { x: number; y: number },\n ): boolean => {\n const region = layout.dropRegion?.({ id: id as string, bounds: aabb }) ?? null;\n if (region) return regionContains(region, aabb, at);\n if (mode === 'region') return false;\n if (layout.contains) return layout.contains(cPose, at);\n return pointInBounds(aabb, at);\n };\n const consider = (id: NodeId, zPath: number[]): void => {\n const layout = layoutDep.getLayout(id as string);\n if (!layout) return;\n const node = scene.get(id);\n if (!node) return;\n const worldPose = composeWorldPose(poseAdapter, id as string, pc.compose);\n const worldAABB = poseDescriptorForNode(d, node).getBounds(worldPose);\n const plane = planeOf(session.view, node.layer);\n const at = plane ? toPlane(plane, center) : center;\n if (!testInside(id, worldPose, worldAABB, layout, at)) return;\n if (layout.acceptsDrop) {\n const arg: LayoutContainer = { id: id as string, bounds: worldAABB };\n for (const c of dragged) if (!layout.acceptsDrop(arg, carryDragged(session, c, plane).arg)) return;\n }\n candidates.push({\n id,\n bounds: worldAABB,\n layout,\n zPath,\n depth: zPath.length,\n plane,\n });\n };\n const walk = (parentId: NodeId | null, parentPath: number[]): void => {\n const childIds = parentId === null ? scene.roots : scene.childrenOf(parentId);\n for (let i = 0; i < childIds.length; i++) {\n // A dragged root's subtree goes with it: dropping into one of its own\n // descendants would reparent it under itself.\n if (draggedIds.has(childIds[i])) continue;\n const childPath = [...parentPath, i];\n consider(childIds[i], childPath);\n walk(childIds[i], childPath);\n }\n };\n walk(null, []);\n\n return chooseContainer(scene, mode, candidates);\n}\n"],"mappings":";;;;;;;;;AAaA,SAAgB,cAAc,GAAY,IAAuC;CAC/E,OAAO,GAAG,KAAK,EAAE,KAAK,GAAG,IAAI,EAAE,IAAI,EAAE,SAAS,GAAG,KAAK,EAAE,KAAK,GAAG,IAAI,EAAE,IAAI,EAAE;AAC9E;;;AAeA,SAAS,eACP,QACA,QACA,OACS;CACT,MAAM,QAAQ;EAAE,GAAG,MAAM,IAAI,OAAO;EAAG,GAAG,MAAM,IAAI,OAAO;CAAE;CAC7D,OAAO,OAAO,WAAW,aACrB,OAAO,KAAK,IACZ,YAAY,QAAQ,MAAM,GAAG,MAAM,CAAC;AAC1C;AAEA,SAAS,gBACP,OACA,MACA,YACkB;CAClB,IAAI,OAAyB;CAC7B,IAAI,SAAS,WAAW;EAGtB,MAAM,OAAO,IAAI,IAAuB,WAAW,KAAK,MAAM,CAAC,EAAE,IAAI,CAAC,CAAC,CAAC;EACxE,KAAK,MAAM,MAAM,MAAM,YAAY,GAAG,OAAO,KAAK,IAAI,EAAE,KAAK;CAC/D,OAEE,KAAK,MAAM,KAAK,YAAY;EAC1B,IAAI,SAAS,MAAM;GAAE,OAAO;GAAG;EAAU;EACzC,IAAI,EAAE,QAAQ,KAAK,OAAO;GAAE,OAAO;GAAG;EAAU;EAChD,IAAI,EAAE,QAAQ,KAAK,OAAO;EAC1B,IAAI,SAAS;EACb,KAAK,IAAI,IAAI,GAAG,IAAI,EAAE,MAAM,QAAQ,KAAK;GACvC,IAAI,EAAE,MAAM,KAAK,KAAK,MAAM,IAAI;IAAE,SAAS;IAAM;GAAO;GACxD,IAAI,EAAE,MAAM,KAAK,KAAK,MAAM,IAAI;IAAE,SAAS;IAAO;GAAO;EAC3D;EACA,IAAI,QAAQ,OAAO;CACrB;CAEF,OAAO;AACT;;AAGA,SAAgB,cACd,SACA,SACA,QACkB;CAClB,MAAM,YAAY,QAAQ;CAC1B,IAAI,CAAC,WAAW,OAAO;CACvB,MAAM,QAAQ,QAAQ;CACtB,MAAM,KAAK,QAAQ;CACnB,MAAM,cAAc,iBAAiB,KAAK;CAC1C,MAAM,IAAI,QAAQ;CAClB,MAAM,aAAa,IAAI,IAAY,QAAQ,KAAK,MAAM,EAAE,EAAE,CAAC;CAC3D,MAAM,aAA0B,CAAC;CACjC,MAAM,OAAO,UAAU,cAAc;CACrC,MAAM,cACJ,IAAY,OAAgB,MAAe,QAAgB,OAC/C;EACZ,MAAM,SAAS,OAAO,aAAa;GAAM;GAAc,QAAQ;EAAK,CAAC,KAAK;EAC1E,IAAI,QAAQ,OAAO,eAAe,QAAQ,MAAM,EAAE;EAClD,IAAI,SAAS,UAAU,OAAO;EAC9B,IAAI,OAAO,UAAU,OAAO,OAAO,SAAS,OAAO,EAAE;EACrD,OAAO,cAAc,MAAM,EAAE;CAC/B;CACA,MAAM,YAAY,IAAY,UAA0B;EACtD,MAAM,SAAS,UAAU,UAAU,EAAY;EAC/C,IAAI,CAAC,QAAQ;EACb,MAAM,OAAO,MAAM,IAAI,EAAE;EACzB,IAAI,CAAC,MAAM;EACX,MAAM,YAAY,iBAAiB,aAAa,IAAc,GAAG,OAAO;EACxE,MAAM,YAAY,sBAAsB,GAAG,IAAI,CAAC,CAAC,UAAU,SAAS;EACpE,MAAM,QAAQ,QAAQ,QAAQ,MAAM,KAAK,KAAK;EAC9C,MAAM,KAAK,QAAQ,QAAQ,OAAO,MAAM,IAAI;EAC5C,IAAI,CAAC,WAAW,IAAI,WAAW,WAAW,QAAQ,EAAE,GAAG;EACvD,IAAI,OAAO,aAAa;GACtB,MAAM,MAAuB;IAAM;IAAc,QAAQ;GAAU;GACnE,KAAK,MAAM,KAAK,SAAS,IAAI,CAAC,OAAO,YAAY,KAAK,aAAa,SAAS,GAAG,KAAK,CAAC,CAAC,GAAG,GAAG;EAC9F;EACA,WAAW,KAAK;GACd;GACA,QAAQ;GACR;GACA;GACA,OAAO,MAAM;GACb;EACF,CAAC;CACH;CACA,MAAM,QAAQ,UAAyB,eAA+B;EACpE,MAAM,WAAW,aAAa,OAAO,MAAM,QAAQ,MAAM,WAAW,QAAQ;EAC5E,KAAK,IAAI,IAAI,GAAG,IAAI,SAAS,QAAQ,KAAK;GAGxC,IAAI,WAAW,IAAI,SAAS,EAAE,GAAG;GACjC,MAAM,YAAY,CAAC,GAAG,YAAY,CAAC;GACnC,SAAS,SAAS,IAAI,SAAS;GAC/B,KAAK,SAAS,IAAI,SAAS;EAC7B;CACF;CACA,KAAK,MAAM,CAAC,CAAC;CAEb,OAAO,gBAAgB,OAAO,MAAM,UAAU;AAChD"}
1
+ {"version":3,"file":"container.js","names":[],"sources":["../../../../../src/interactions/actions/move/layout/container.ts"],"sourcesContent":["import { pointInPath } from '@weasel-js/geom';\nimport type { NodeId, Scene } from 'core/scene/types';\nimport { poseDescriptorForNode } from 'core/geometry/poseDescriptor';\nimport { toPlane, type PlaneMap } from 'core/viewport/parallax';\nimport { composeWorldPose } from 'core/scene/composePose';\nimport { scenePoseAdapter } from 'core/scene/scenePoseAdapter';\nimport type { DropRegion, LayoutContainer, LayoutStrategy } from '../../../../layout/types';\nimport type { LayoutDep } from '../../depSchema';\nimport { planeOf } from '../../planeInput';\nimport type { MoveSession } from '../session';\nimport { carryDragged, type DraggedChild } from './dragged';\n\nexport interface Bounds4 { x: number; y: number; width: number; height: number }\nexport function pointInBounds(b: Bounds4, at: { x: number; y: number }): boolean {\n return at.x >= b.x && at.x < b.x + b.width && at.y >= b.y && at.y < b.y + b.height;\n}\n\ntype Layout = LayoutStrategy<unknown>;\nexport interface Candidate {\n id: NodeId;\n bounds: Bounds4;\n layout: LayoutStrategy<unknown>;\n zPath: number[];\n depth: number;\n /** The edited plane into this container's. */\n plane: PlaneMap | null;\n}\n\n/** Whether a world point falls in a drop region, which is authored relative to\n * the top-left of the container's world bounds. */\nfunction regionContains(\n region: DropRegion,\n bounds: { x: number; y: number },\n point: { x: number; y: number },\n): boolean {\n const local = { x: point.x - bounds.x, y: point.y - bounds.y };\n return typeof region === 'function'\n ? region(local)\n : pointInPath(region, local.x, local.y);\n}\n\nfunction chooseContainer(\n scene: Scene<unknown, string, unknown>,\n mode: NonNullable<LayoutDep['dropTarget']>,\n candidates: readonly Candidate[],\n): Candidate | null {\n let dest: Candidate | null = null;\n if (mode === 'topmost') {\n // Painted last wins, in the scene's own render order — which crosses\n // subtrees and layers, where a sibling-index path cannot.\n const byId = new Map<NodeId, Candidate>(candidates.map((c) => [c.id, c]));\n for (const id of scene.renderOrder()) dest = byId.get(id) ?? dest;\n } else {\n // Deepest wins; sibling-index path breaks ties (higher z = later index).\n for (const c of candidates) {\n if (dest === null) { dest = c; continue; }\n if (c.depth > dest.depth) { dest = c; continue; }\n if (c.depth < dest.depth) continue;\n let cAfter = false;\n for (let i = 0; i < c.zPath.length; i++) {\n if (c.zPath[i] > dest.zPath[i]) { cAfter = true; break; }\n if (c.zPath[i] < dest.zPath[i]) { cAfter = false; break; }\n }\n if (cAfter) dest = c;\n }\n }\n return dest;\n}\n\n/** The layout container that takes the drop at `center`, or null. */\nexport function pickContainer(\n session: MoveSession,\n dragged: readonly DraggedChild[],\n center: { x: number; y: number },\n): Candidate | null {\n const layoutDep = session.layout;\n if (!layoutDep) return null;\n const scene = session.scene;\n const pc = session.pc;\n const poseAdapter = scenePoseAdapter(scene);\n const d = session.projection;\n const draggedIds = new Set<NodeId>(dragged.map((c) => c.id));\n const candidates: Candidate[] = [];\n const mode = layoutDep.dropTarget ?? 'innermost';\n const testInside = (\n id: NodeId, cPose: unknown, aabb: Bounds4, layout: Layout, at: { x: number; y: number },\n ): boolean => {\n const region = layout.dropRegion?.({ id: id as string, bounds: aabb }) ?? null;\n if (region) return regionContains(region, aabb, at);\n if (mode === 'region') return false;\n if (layout.contains) return layout.contains(cPose, at);\n return pointInBounds(aabb, at);\n };\n const consider = (id: NodeId, zPath: number[]): void => {\n const layout = layoutDep.getLayout(id as string);\n if (!layout) return;\n const node = scene.get(id);\n if (!node) return;\n const worldPose = composeWorldPose(poseAdapter, id as string, pc.compose);\n const worldAABB = poseDescriptorForNode(d, node).getBounds(worldPose);\n const plane = planeOf(session.view, node.layer);\n const at = plane ? toPlane(plane, center) : center;\n if (!testInside(id, worldPose, worldAABB, layout, at)) return;\n if (!session.admits(id)) return;\n if (layout.acceptsDrop) {\n const arg: LayoutContainer = { id: id as string, bounds: worldAABB };\n for (const c of dragged) if (!layout.acceptsDrop(arg, carryDragged(session, c, plane).arg)) return;\n }\n candidates.push({\n id,\n bounds: worldAABB,\n layout,\n zPath,\n depth: zPath.length,\n plane,\n });\n };\n const walk = (parentId: NodeId | null, parentPath: number[]): void => {\n const childIds = parentId === null ? scene.roots : scene.childrenOf(parentId);\n for (let i = 0; i < childIds.length; i++) {\n // A dragged root's subtree goes with it: dropping into one of its own\n // descendants would reparent it under itself.\n if (draggedIds.has(childIds[i])) continue;\n const childPath = [...parentPath, i];\n consider(childIds[i], childPath);\n walk(childIds[i], childPath);\n }\n };\n walk(null, []);\n\n return chooseContainer(scene, mode, candidates);\n}\n"],"mappings":"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"}
@@ -1,8 +1,8 @@
1
1
  import { toPlane } from "../../../../core/viewport/parallax.js";
2
2
  import { composeWorldPose } from "../../../../core/scene/composeFrame.js";
3
3
  import { poseDescriptorForNode, translatePoseViaDescriptor } from "../../../../core/geometry/poseDescriptor.js";
4
- import { carryPose, invertPlane, planeOf } from "../../planeInput.js";
5
4
  import { scenePoseAdapter } from "../../../../core/scene/scenePoseAdapter.js";
5
+ import { carryPose, invertPlane, planeOf } from "../../planeInput.js";
6
6
  import { grabbedPoseAdapter, planeDelta } from "../delta.js";
7
7
 
8
8
  //#region src/interactions/actions/move/layout/dragged.ts
@@ -1,7 +1,9 @@
1
1
  import { toPlane } from "../../../../core/viewport/parallax.js";
2
2
  import { composeWorldPose, rebaseLocalPose } from "../../../../core/scene/composeFrame.js";
3
- import { asNodeId } from "../../../../core/scene/types.js";
3
+ import { translatePoseViaDescriptor } from "../../../../core/geometry/poseDescriptor.js";
4
4
  import { scenePoseAdapter } from "../../../../core/scene/scenePoseAdapter.js";
5
+ import { carriedDescendants, sceneCarrySource } from "../../../../core/scene/carryDescendants.js";
6
+ import { asNodeId } from "../../../../core/scene/types.js";
5
7
  import { carryDragged, gatherDragged, selectionCenter } from "./dragged.js";
6
8
  import { pickContainer } from "./container.js";
7
9
 
@@ -105,6 +107,16 @@ function sourceReflow(session, dragged, destId) {
105
107
  }
106
108
  return sourceReflow;
107
109
  }
110
+ /** The stored poses that bring `id`'s descendants along to `world`, where a reflow shows it. */
111
+ function reflowCarry(session, id, world, moving) {
112
+ const { scene, pc, projection: d } = session;
113
+ const a = d.getBounds(composeWorldPose(scenePoseAdapter(scene), id, pc.compose));
114
+ const b = d.getBounds(world);
115
+ const dx = b.x - a.x;
116
+ const dy = b.y - a.y;
117
+ if (dx === 0 && dy === 0) return /* @__PURE__ */ new Map();
118
+ return carriedDescendants(sceneCarrySource(scene), id, (pose) => translatePoseViaDescriptor(pose, dx, dy, d), moving);
119
+ }
108
120
  /** One frame's layout result, or null with no layout dep or nothing dragged. */
109
121
  function runDropPass(session, delta, pointer) {
110
122
  if (!session.layout) return null;
@@ -146,6 +158,11 @@ function runDropPass(session, delta, pointer) {
146
158
  reflow.set(asNodeId(cid), rebaseLocalPose(poseAdapter, pose, parent, pc.compose, pc.decompose));
147
159
  reflowIds.add(asNodeId(cid));
148
160
  }
161
+ const draggedIds = new Set(dragged.map((c) => c.id));
162
+ for (const [cid, pose] of [...placed.destReflow, ...leftovers]) for (const [id, carried] of reflowCarry(session, asNodeId(cid), pose, (d) => reflow.has(d) || draggedIds.has(d))) {
163
+ reflow.set(id, carried);
164
+ reflowIds.add(id);
165
+ }
149
166
  return {
150
167
  dropCenter,
151
168
  pass: {
@@ -1 +1 @@
1
- {"version":3,"file":"dropPass.js","names":[],"sources":["../../../../../src/interactions/actions/move/layout/dropPass.ts"],"sourcesContent":["import { asNodeId, type NodeId } from 'core/scene/types';\nimport { toPlane } from 'core/viewport/parallax';\nimport { composeWorldPose, rebaseLocalPose } from 'core/scene/composePose';\nimport { scenePoseAdapter } from 'core/scene/scenePoseAdapter';\nimport type { LayoutChild, LayoutContainer } from '../../../../layout/types';\nimport type { MoveSession } from '../session';\nimport { pickContainer, type Candidate } from './container';\nimport { carryDragged, gatherDragged, selectionCenter, type DraggedChild } from './dragged';\nimport type { DropPass, LayoutPlacement } from './types';\n\n/** Dragged ids grouped by the container they are leaving, skipping any that\n * stay put (`exclude` is the destination) or that had no container. */\nexport function groupBySourceContainer(\n dragged: ReadonlyArray<{ id: NodeId; sourceContainerId: string | null }>,\n exclude: string | null,\n): Map<string, Set<NodeId>> {\n const out = new Map<string, Set<NodeId>>();\n for (const d of dragged) {\n const src = d.sourceContainerId;\n if (!src || src === exclude) continue;\n let set = out.get(src);\n if (!set) { set = new Set<NodeId>(); out.set(src, set); }\n set.add(d.id);\n }\n return out;\n}\n\n/** Place each dragged child in `dest` in turn. Null when the snap rejects\n * any of them: a container that cannot take every member takes none. */\nfunction placeAll(\n session: MoveSession,\n dest: Candidate,\n container: LayoutContainer,\n dragged: readonly DraggedChild[],\n probeOffset: { x: number; y: number },\n): { placements: LayoutPlacement[]; destReflow: Map<NodeId, unknown> } | null {\n const { scene, pc } = session;\n const poseAdapter = scenePoseAdapter(scene);\n const draggedIds = new Set<NodeId>(dragged.map((c) => c.id));\n const layout = dest.layout;\n // The container state the placements run against, advanced by each one.\n const working: LayoutChild<unknown>[] = scene.childrenOf(dest.id).map((cid) => ({\n id: cid as string,\n pose: composeWorldPose(poseAdapter, cid as string, pc.compose),\n }));\n const setWorking = (id: string, pose: unknown): void => {\n const at = working.findIndex((c) => c.id === id);\n if (at >= 0) working[at] = { id, pose };\n else working.push({ id, pose });\n };\n\n const placements: LayoutPlacement[] = [];\n const destReflow = new Map<NodeId, unknown>();\n const destPlane = dest.plane;\n for (const edited of dragged) {\n const dc = carryDragged(session, edited, destPlane);\n const children = working.map((c) => ({ ...c }));\n const targets = layout.getDropTargets(container, children, dc.arg);\n const probe = { x: edited.center.x + probeOffset.x, y: edited.center.y + probeOffset.y };\n const target = layout.snap.pickTarget(targets, destPlane ? toPlane(destPlane, probe) : probe);\n // All-or-nothing: a container that cannot take every member takes none\n // rather than splitting the selection between two homes.\n if (!target) return null;\n placements.push({ dragged: dc.arg, children, target });\n const poses = layout.reflowPoses(container, children, dc.arg, target);\n for (const [cid, pose] of poses) {\n setWorking(cid, pose);\n if (!draggedIds.has(asNodeId(cid))) destReflow.set(asNodeId(cid), pose);\n }\n setWorking(dc.arg.id, poses.get(dc.arg.id) ?? target.pose);\n }\n return { placements, destReflow };\n}\n\n/** World poses of the leftovers that moved in each container the selection left. */\nfunction sourceReflow(\n session: MoveSession, dragged: readonly DraggedChild[], destId: string,\n): Map<string, unknown> {\n const { scene, pc, projection: d } = session;\n const poseAdapter = scenePoseAdapter(scene);\n // Source reflow (cross-container) → fold changed leftovers into previews.\n // One pass per container the selection left behind, with everything that\n // left it withdrawn at once.\n const sourceReflow = new Map<string, unknown>();\n const layoutDep = session.layout;\n if (!layoutDep) return sourceReflow;\n for (const [srcId, leaving] of groupBySourceContainer(dragged.map((c) => ({\n id: c.id,\n sourceContainerId: c.arg.sourceContainerId,\n })), destId)) {\n const srcLayout = layoutDep.getLayout(srcId);\n const srcNode = scene.get(asNodeId(srcId));\n if (!srcLayout || !srcNode) continue;\n const srcContainer: LayoutContainer = {\n id: srcId,\n bounds: d.getBounds(composeWorldPose(poseAdapter, srcId, pc.compose)),\n };\n const srcChildren: LayoutChild<unknown>[] = scene.childrenOf(asNodeId(srcId))\n .filter((cid) => !leaving.has(cid))\n .map((cid) => ({\n id: cid as string,\n pose: composeWorldPose(poseAdapter, cid as string, pc.compose),\n }));\n for (const [cid, pose] of srcLayout.childPoses(srcContainer, srcChildren)) {\n const cur = d.getBounds(composeWorldPose(poseAdapter, cid, pc.compose));\n const next = d.getBounds(pose);\n const same = cur.x === next.x && cur.y === next.y\n && cur.width === next.width && cur.height === next.height;\n if (same) continue;\n sourceReflow.set(cid, pose); // WORLD — rebased at each consumption point\n }\n }\n return sourceReflow;\n}\n\n/** One frame's layout result, or null with no layout dep or nothing dragged. */\nexport function runDropPass(\n session: MoveSession,\n delta: { dx: number; dy: number },\n pointer: { x: number; y: number },\n): DropPass | null {\n if (!session.layout) return null;\n const { scene, pc } = session;\n const poseAdapter = scenePoseAdapter(scene);\n const dragged = gatherDragged(session, delta);\n if (dragged.length === 0) return null;\n const dropCenter = selectionCenter(session, dragged);\n // Each child is snapped at its own position, displaced by however far the\n // pointer sits from that center — the group keeps its shape, and a lone\n // child is still probed at the pointer itself.\n const probeOffset = {\n x: pointer.x - dropCenter.x,\n y: pointer.y - dropCenter.y,\n };\n\n const dest = pickContainer(session, dragged, dropCenter);\n if (!dest) return { dropCenter, pass: null, reflow: new Map() };\n\n const container: LayoutContainer = { id: dest.id as string, bounds: dest.bounds };\n const placed = placeAll(session, dest, container, dragged, probeOffset);\n if (!placed) return { dropCenter, pass: null, reflow: new Map() };\n\n // Destination reflow → fold into previews (the dragged children keep their\n // pointer-following ghosts). `reflowPoses` returns world; previews are\n // local, so rebase to the child's current parent frame first.\n const reflow = new Map<NodeId, unknown>();\n const reflowIds = new Set<NodeId>();\n for (const [cid, pose] of placed.destReflow) {\n const parent = scene.get(cid)?.parent ?? null;\n reflow.set(cid, rebaseLocalPose(poseAdapter, pose, parent, pc.compose, pc.decompose));\n reflowIds.add(cid);\n }\n\n const leftovers = sourceReflow(session, dragged, container.id);\n for (const [cid, pose] of leftovers) {\n const parent = scene.get(asNodeId(cid))?.parent ?? null;\n reflow.set(asNodeId(cid), rebaseLocalPose(poseAdapter, pose, parent, pc.compose, pc.decompose));\n reflowIds.add(asNodeId(cid));\n }\n\n return {\n dropCenter,\n pass: { layout: dest.layout, container, placements: placed.placements, sourceReflow: leftovers, reflowIds },\n reflow,\n };\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"dropPass.js","names":[],"sources":["../../../../../src/interactions/actions/move/layout/dropPass.ts"],"sourcesContent":["import { asNodeId, type NodeId } from 'core/scene/types';\nimport { toPlane } from 'core/viewport/parallax';\nimport { composeWorldPose, rebaseLocalPose } from 'core/scene/composePose';\nimport { scenePoseAdapter } from 'core/scene/scenePoseAdapter';\nimport { carriedDescendants, sceneCarrySource } from 'core/scene/carryDescendants';\nimport { translatePoseViaDescriptor } from 'core/geometry/poseDescriptor';\nimport type { LayoutChild, LayoutContainer } from '../../../../layout/types';\nimport type { MoveSession } from '../session';\nimport { pickContainer, type Candidate } from './container';\nimport { carryDragged, gatherDragged, selectionCenter, type DraggedChild } from './dragged';\nimport type { DropPass, LayoutPlacement } from './types';\n\n/** Dragged ids grouped by the container they are leaving, skipping any that\n * stay put (`exclude` is the destination) or that had no container. */\nexport function groupBySourceContainer(\n dragged: ReadonlyArray<{ id: NodeId; sourceContainerId: string | null }>,\n exclude: string | null,\n): Map<string, Set<NodeId>> {\n const out = new Map<string, Set<NodeId>>();\n for (const d of dragged) {\n const src = d.sourceContainerId;\n if (!src || src === exclude) continue;\n let set = out.get(src);\n if (!set) { set = new Set<NodeId>(); out.set(src, set); }\n set.add(d.id);\n }\n return out;\n}\n\n/** Place each dragged child in `dest` in turn. Null when the snap rejects\n * any of them: a container that cannot take every member takes none. */\nfunction placeAll(\n session: MoveSession,\n dest: Candidate,\n container: LayoutContainer,\n dragged: readonly DraggedChild[],\n probeOffset: { x: number; y: number },\n): { placements: LayoutPlacement[]; destReflow: Map<NodeId, unknown> } | null {\n const { scene, pc } = session;\n const poseAdapter = scenePoseAdapter(scene);\n const draggedIds = new Set<NodeId>(dragged.map((c) => c.id));\n const layout = dest.layout;\n // The container state the placements run against, advanced by each one.\n const working: LayoutChild<unknown>[] = scene.childrenOf(dest.id).map((cid) => ({\n id: cid as string,\n pose: composeWorldPose(poseAdapter, cid as string, pc.compose),\n }));\n const setWorking = (id: string, pose: unknown): void => {\n const at = working.findIndex((c) => c.id === id);\n if (at >= 0) working[at] = { id, pose };\n else working.push({ id, pose });\n };\n\n const placements: LayoutPlacement[] = [];\n const destReflow = new Map<NodeId, unknown>();\n const destPlane = dest.plane;\n for (const edited of dragged) {\n const dc = carryDragged(session, edited, destPlane);\n const children = working.map((c) => ({ ...c }));\n const targets = layout.getDropTargets(container, children, dc.arg);\n const probe = { x: edited.center.x + probeOffset.x, y: edited.center.y + probeOffset.y };\n const target = layout.snap.pickTarget(targets, destPlane ? toPlane(destPlane, probe) : probe);\n // All-or-nothing: a container that cannot take every member takes none\n // rather than splitting the selection between two homes.\n if (!target) return null;\n placements.push({ dragged: dc.arg, children, target });\n const poses = layout.reflowPoses(container, children, dc.arg, target);\n for (const [cid, pose] of poses) {\n setWorking(cid, pose);\n if (!draggedIds.has(asNodeId(cid))) destReflow.set(asNodeId(cid), pose);\n }\n setWorking(dc.arg.id, poses.get(dc.arg.id) ?? target.pose);\n }\n return { placements, destReflow };\n}\n\n/** World poses of the leftovers that moved in each container the selection left. */\nfunction sourceReflow(\n session: MoveSession, dragged: readonly DraggedChild[], destId: string,\n): Map<string, unknown> {\n const { scene, pc, projection: d } = session;\n const poseAdapter = scenePoseAdapter(scene);\n // Source reflow (cross-container) → fold changed leftovers into previews.\n // One pass per container the selection left behind, with everything that\n // left it withdrawn at once.\n const sourceReflow = new Map<string, unknown>();\n const layoutDep = session.layout;\n if (!layoutDep) return sourceReflow;\n for (const [srcId, leaving] of groupBySourceContainer(dragged.map((c) => ({\n id: c.id,\n sourceContainerId: c.arg.sourceContainerId,\n })), destId)) {\n const srcLayout = layoutDep.getLayout(srcId);\n const srcNode = scene.get(asNodeId(srcId));\n if (!srcLayout || !srcNode) continue;\n const srcContainer: LayoutContainer = {\n id: srcId,\n bounds: d.getBounds(composeWorldPose(poseAdapter, srcId, pc.compose)),\n };\n const srcChildren: LayoutChild<unknown>[] = scene.childrenOf(asNodeId(srcId))\n .filter((cid) => !leaving.has(cid))\n .map((cid) => ({\n id: cid as string,\n pose: composeWorldPose(poseAdapter, cid as string, pc.compose),\n }));\n for (const [cid, pose] of srcLayout.childPoses(srcContainer, srcChildren)) {\n const cur = d.getBounds(composeWorldPose(poseAdapter, cid, pc.compose));\n const next = d.getBounds(pose);\n const same = cur.x === next.x && cur.y === next.y\n && cur.width === next.width && cur.height === next.height;\n if (same) continue;\n sourceReflow.set(cid, pose); // WORLD — rebased at each consumption point\n }\n }\n return sourceReflow;\n}\n\n/** The stored poses that bring `id`'s descendants along to `world`, where a reflow shows it. */\nfunction reflowCarry(\n session: MoveSession, id: NodeId, world: unknown, moving: (id: NodeId) => boolean,\n): Map<NodeId, unknown> {\n const { scene, pc, projection: d } = session;\n const a = d.getBounds(composeWorldPose(scenePoseAdapter(scene), id as string, pc.compose));\n const b = d.getBounds(world);\n const dx = b.x - a.x;\n const dy = b.y - a.y;\n if (dx === 0 && dy === 0) return new Map();\n return carriedDescendants(sceneCarrySource(scene), id, (pose) => translatePoseViaDescriptor(pose, dx, dy, d), moving);\n}\n\n/** One frame's layout result, or null with no layout dep or nothing dragged. */\nexport function runDropPass(\n session: MoveSession,\n delta: { dx: number; dy: number },\n pointer: { x: number; y: number },\n): DropPass | null {\n if (!session.layout) return null;\n const { scene, pc } = session;\n const poseAdapter = scenePoseAdapter(scene);\n const dragged = gatherDragged(session, delta);\n if (dragged.length === 0) return null;\n const dropCenter = selectionCenter(session, dragged);\n // Each child is snapped at its own position, displaced by however far the\n // pointer sits from that center — the group keeps its shape, and a lone\n // child is still probed at the pointer itself.\n const probeOffset = {\n x: pointer.x - dropCenter.x,\n y: pointer.y - dropCenter.y,\n };\n\n const dest = pickContainer(session, dragged, dropCenter);\n if (!dest) return { dropCenter, pass: null, reflow: new Map() };\n\n const container: LayoutContainer = { id: dest.id as string, bounds: dest.bounds };\n const placed = placeAll(session, dest, container, dragged, probeOffset);\n if (!placed) return { dropCenter, pass: null, reflow: new Map() };\n\n // Destination reflow → fold into previews (the dragged children keep their\n // pointer-following ghosts). `reflowPoses` returns world; previews are\n // local, so rebase to the child's current parent frame first.\n const reflow = new Map<NodeId, unknown>();\n const reflowIds = new Set<NodeId>();\n for (const [cid, pose] of placed.destReflow) {\n const parent = scene.get(cid)?.parent ?? null;\n reflow.set(cid, rebaseLocalPose(poseAdapter, pose, parent, pc.compose, pc.decompose));\n reflowIds.add(cid);\n }\n\n const leftovers = sourceReflow(session, dragged, container.id);\n for (const [cid, pose] of leftovers) {\n const parent = scene.get(asNodeId(cid))?.parent ?? null;\n reflow.set(asNodeId(cid), rebaseLocalPose(poseAdapter, pose, parent, pc.compose, pc.decompose));\n reflowIds.add(asNodeId(cid));\n }\n const draggedIds = new Set<NodeId>(dragged.map((c) => c.id));\n for (const [cid, pose] of [...placed.destReflow, ...leftovers] as [string, unknown][]) {\n for (const [id, carried] of reflowCarry(session, asNodeId(cid), pose, (d) => reflow.has(d) || draggedIds.has(d))) {\n reflow.set(id, carried);\n reflowIds.add(id);\n }\n }\n\n return {\n dropCenter,\n pass: { layout: dest.layout, container, placements: placed.placements, sourceReflow: leftovers, reflowIds },\n reflow,\n };\n}\n"],"mappings":"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"}
@@ -1,17 +1,17 @@
1
1
  import { toPlane } from "../../../../core/viewport/parallax.js";
2
2
  import { composeWorldPose } from "../../../../core/scene/composeFrame.js";
3
3
  import { poseDescriptorForNode } from "../../../../core/geometry/poseDescriptor.js";
4
- import { planeOf } from "../../planeInput.js";
5
4
  import { scenePoseAdapter } from "../../../../core/scene/scenePoseAdapter.js";
5
+ import { planeOf } from "../../planeInput.js";
6
6
  import { pointInBounds } from "./container.js";
7
7
 
8
8
  //#region src/interactions/actions/move/layout/exits.ts
9
9
  /** Where each dragged root that leaves its layout container on release lands.
10
10
  * A root leaves when the drop center falls outside the body of the layout
11
11
  * container holding it, and lands under the innermost plain container beneath
12
- * the drop — a layout container there has already declined it — or at the top
13
- * level of its layer. A root whose parent has no layout is not this pass's to
14
- * move; `reparentOnDrop` decides for those. */
12
+ * the drop that admits it (`canDropInto`) — a layout container there has
13
+ * already declined it — or at the top level of its layer. A root whose parent
14
+ * has no layout is not this pass's to move; `reparentOnDrop` decides for those. */
15
15
  function resolveLayoutExits(session, dropCenter, ids) {
16
16
  const out = /* @__PURE__ */ new Map();
17
17
  const layoutDep = session.layout;
@@ -39,7 +39,7 @@ function resolveLayoutExits(session, dropCenter, ids) {
39
39
  if (excluded.has(id)) continue;
40
40
  const node = scene.get(id);
41
41
  if (!node || node.kind !== "container") continue;
42
- if (node.layer === layer && depth >= bestDepth && layoutDep.getLayout(id) === null && holds(id, null)) {
42
+ if (node.layer === layer && depth >= bestDepth && layoutDep.getLayout(id) === null && holds(id, null) && session.admits(id)) {
43
43
  best = id;
44
44
  bestDepth = depth;
45
45
  }
@@ -1 +1 @@
1
- {"version":3,"file":"exits.js","names":[],"sources":["../../../../../src/interactions/actions/move/layout/exits.ts"],"sourcesContent":["import type { NodeId } from 'core/scene/types';\nimport { poseDescriptorForNode } from 'core/geometry/poseDescriptor';\nimport { toPlane } from 'core/viewport/parallax';\nimport { composeWorldPose } from 'core/scene/composePose';\nimport { scenePoseAdapter } from 'core/scene/scenePoseAdapter';\nimport type { LayoutStrategy } from '../../../../layout/types';\nimport { planeOf } from '../../planeInput';\nimport type { MoveSession } from '../session';\nimport { pointInBounds } from './container';\n\n/** Where each dragged root that leaves its layout container on release lands.\n * A root leaves when the drop center falls outside the body of the layout\n * container holding it, and lands under the innermost plain container beneath\n * the drop — a layout container there has already declined it — or at the top\n * level of its layer. A root whose parent has no layout is not this pass's to\n * move; `reparentOnDrop` decides for those. */\nexport function resolveLayoutExits(\n session: MoveSession, dropCenter: { x: number; y: number } | null, ids: Iterable<NodeId>,\n): Map<NodeId, NodeId | null> {\n const out = new Map<NodeId, NodeId | null>();\n const layoutDep = session.layout;\n const center = dropCenter;\n if (!layoutDep || !center) return out;\n const { scene, pc, projection: d } = session;\n const poseAdapter = scenePoseAdapter(scene);\n const excluded = new Set<NodeId>([...session.ids, ...session.cascadeIds]);\n const holds = (id: NodeId, layout: LayoutStrategy<unknown> | null): boolean => {\n const node = scene.get(id)!;\n const plane = planeOf(session.view, node.layer);\n const at = plane ? toPlane(plane, center) : center;\n const pose = composeWorldPose(poseAdapter, id as string, pc.compose);\n if (layout?.contains) return layout.contains(pose, at);\n const b = poseDescriptorForNode(d, node).getBounds(pose);\n return pointInBounds(b, at);\n };\n const landings = new Map<string, NodeId | null>();\n const landingOn = (layer: string): NodeId | null => {\n if (landings.has(layer)) return landings.get(layer)!;\n // Deepest wins; among equals, the later sibling (painted over the other).\n let best: NodeId | null = null;\n let bestDepth = -1;\n const walk = (parentId: NodeId | null, depth: number): void => {\n for (const id of parentId === null ? scene.roots : scene.childrenOf(parentId)) {\n if (excluded.has(id)) continue;\n const node = scene.get(id);\n if (!node || node.kind !== 'container') continue;\n if (node.layer === layer && depth >= bestDepth\n && layoutDep.getLayout(id as string) === null && holds(id, null)) {\n best = id;\n bestDepth = depth;\n }\n walk(id, depth + 1);\n }\n };\n walk(null, 0);\n landings.set(layer, best);\n return best;\n };\n for (const id of ids) {\n const node = scene.get(id);\n const parent = node?.parent ?? null;\n if (!node || parent === null) continue;\n const layout = layoutDep.getLayout(parent as string);\n if (!layout || holds(parent, layout)) continue;\n out.set(id, landingOn(node.layer));\n }\n return out;\n}\n"],"mappings":";;;;;;;;;;;;;;AAgBA,SAAgB,mBACd,SAAsB,YAA6C,KACvC;CAC5B,MAAM,sBAAM,IAAI,IAA2B;CAC3C,MAAM,YAAY,QAAQ;CAC1B,MAAM,SAAS;CACf,IAAI,CAAC,aAAa,CAAC,QAAQ,OAAO;CAClC,MAAM,EAAE,OAAO,IAAI,YAAY,MAAM;CACrC,MAAM,cAAc,iBAAiB,KAAK;CAC1C,MAAM,2BAAW,IAAI,IAAY,CAAC,GAAG,QAAQ,KAAK,GAAG,QAAQ,UAAU,CAAC;CACxE,MAAM,SAAS,IAAY,WAAoD;EAC7E,MAAM,OAAO,MAAM,IAAI,EAAE;EACzB,MAAM,QAAQ,QAAQ,QAAQ,MAAM,KAAK,KAAK;EAC9C,MAAM,KAAK,QAAQ,QAAQ,OAAO,MAAM,IAAI;EAC5C,MAAM,OAAO,iBAAiB,aAAa,IAAc,GAAG,OAAO;EACnE,IAAI,QAAQ,UAAU,OAAO,OAAO,SAAS,MAAM,EAAE;EACrD,MAAM,IAAI,sBAAsB,GAAG,IAAI,CAAC,CAAC,UAAU,IAAI;EACvD,OAAO,cAAc,GAAG,EAAE;CAC5B;CACA,MAAM,2BAAW,IAAI,IAA2B;CAChD,MAAM,aAAa,UAAiC;EAClD,IAAI,SAAS,IAAI,KAAK,GAAG,OAAO,SAAS,IAAI,KAAK;EAElD,IAAI,OAAsB;EAC1B,IAAI,YAAY;EAChB,MAAM,QAAQ,UAAyB,UAAwB;GAC7D,KAAK,MAAM,MAAM,aAAa,OAAO,MAAM,QAAQ,MAAM,WAAW,QAAQ,GAAG;IAC7E,IAAI,SAAS,IAAI,EAAE,GAAG;IACtB,MAAM,OAAO,MAAM,IAAI,EAAE;IACzB,IAAI,CAAC,QAAQ,KAAK,SAAS,aAAa;IACxC,IAAI,KAAK,UAAU,SAAS,SAAS,aAChC,UAAU,UAAU,EAAY,MAAM,QAAQ,MAAM,IAAI,IAAI,GAAG;KAClE,OAAO;KACP,YAAY;IACd;IACA,KAAK,IAAI,QAAQ,CAAC;GACpB;EACF;EACA,KAAK,MAAM,CAAC;EACZ,SAAS,IAAI,OAAO,IAAI;EACxB,OAAO;CACT;CACA,KAAK,MAAM,MAAM,KAAK;EACpB,MAAM,OAAO,MAAM,IAAI,EAAE;EACzB,MAAM,SAAS,MAAM,UAAU;EAC/B,IAAI,CAAC,QAAQ,WAAW,MAAM;EAC9B,MAAM,SAAS,UAAU,UAAU,MAAgB;EACnD,IAAI,CAAC,UAAU,MAAM,QAAQ,MAAM,GAAG;EACtC,IAAI,IAAI,IAAI,UAAU,KAAK,KAAK,CAAC;CACnC;CACA,OAAO;AACT"}
1
+ {"version":3,"file":"exits.js","names":[],"sources":["../../../../../src/interactions/actions/move/layout/exits.ts"],"sourcesContent":["import type { NodeId } from 'core/scene/types';\nimport { poseDescriptorForNode } from 'core/geometry/poseDescriptor';\nimport { toPlane } from 'core/viewport/parallax';\nimport { composeWorldPose } from 'core/scene/composePose';\nimport { scenePoseAdapter } from 'core/scene/scenePoseAdapter';\nimport type { LayoutStrategy } from '../../../../layout/types';\nimport { planeOf } from '../../planeInput';\nimport type { MoveSession } from '../session';\nimport { pointInBounds } from './container';\n\n/** Where each dragged root that leaves its layout container on release lands.\n * A root leaves when the drop center falls outside the body of the layout\n * container holding it, and lands under the innermost plain container beneath\n * the drop that admits it (`canDropInto`) — a layout container there has\n * already declined it — or at the top level of its layer. A root whose parent\n * has no layout is not this pass's to move; `reparentOnDrop` decides for those. */\nexport function resolveLayoutExits(\n session: MoveSession, dropCenter: { x: number; y: number } | null, ids: Iterable<NodeId>,\n): Map<NodeId, NodeId | null> {\n const out = new Map<NodeId, NodeId | null>();\n const layoutDep = session.layout;\n const center = dropCenter;\n if (!layoutDep || !center) return out;\n const { scene, pc, projection: d } = session;\n const poseAdapter = scenePoseAdapter(scene);\n const excluded = new Set<NodeId>([...session.ids, ...session.cascadeIds]);\n const holds = (id: NodeId, layout: LayoutStrategy<unknown> | null): boolean => {\n const node = scene.get(id)!;\n const plane = planeOf(session.view, node.layer);\n const at = plane ? toPlane(plane, center) : center;\n const pose = composeWorldPose(poseAdapter, id as string, pc.compose);\n if (layout?.contains) return layout.contains(pose, at);\n const b = poseDescriptorForNode(d, node).getBounds(pose);\n return pointInBounds(b, at);\n };\n const landings = new Map<string, NodeId | null>();\n const landingOn = (layer: string): NodeId | null => {\n if (landings.has(layer)) return landings.get(layer)!;\n // Deepest wins; among equals, the later sibling (painted over the other).\n let best: NodeId | null = null;\n let bestDepth = -1;\n const walk = (parentId: NodeId | null, depth: number): void => {\n for (const id of parentId === null ? scene.roots : scene.childrenOf(parentId)) {\n if (excluded.has(id)) continue;\n const node = scene.get(id);\n if (!node || node.kind !== 'container') continue;\n if (node.layer === layer && depth >= bestDepth\n && layoutDep.getLayout(id as string) === null && holds(id, null) && session.admits(id)) {\n best = id;\n bestDepth = depth;\n }\n walk(id, depth + 1);\n }\n };\n walk(null, 0);\n landings.set(layer, best);\n return best;\n };\n for (const id of ids) {\n const node = scene.get(id);\n const parent = node?.parent ?? null;\n if (!node || parent === null) continue;\n const layout = layoutDep.getLayout(parent as string);\n if (!layout || holds(parent, layout)) continue;\n out.set(id, landingOn(node.layer));\n }\n return out;\n}\n"],"mappings":";;;;;;;;;;;;;;AAgBA,SAAgB,mBACd,SAAsB,YAA6C,KACvC;CAC5B,MAAM,sBAAM,IAAI,IAA2B;CAC3C,MAAM,YAAY,QAAQ;CAC1B,MAAM,SAAS;CACf,IAAI,CAAC,aAAa,CAAC,QAAQ,OAAO;CAClC,MAAM,EAAE,OAAO,IAAI,YAAY,MAAM;CACrC,MAAM,cAAc,iBAAiB,KAAK;CAC1C,MAAM,2BAAW,IAAI,IAAY,CAAC,GAAG,QAAQ,KAAK,GAAG,QAAQ,UAAU,CAAC;CACxE,MAAM,SAAS,IAAY,WAAoD;EAC7E,MAAM,OAAO,MAAM,IAAI,EAAE;EACzB,MAAM,QAAQ,QAAQ,QAAQ,MAAM,KAAK,KAAK;EAC9C,MAAM,KAAK,QAAQ,QAAQ,OAAO,MAAM,IAAI;EAC5C,MAAM,OAAO,iBAAiB,aAAa,IAAc,GAAG,OAAO;EACnE,IAAI,QAAQ,UAAU,OAAO,OAAO,SAAS,MAAM,EAAE;EACrD,MAAM,IAAI,sBAAsB,GAAG,IAAI,CAAC,CAAC,UAAU,IAAI;EACvD,OAAO,cAAc,GAAG,EAAE;CAC5B;CACA,MAAM,2BAAW,IAAI,IAA2B;CAChD,MAAM,aAAa,UAAiC;EAClD,IAAI,SAAS,IAAI,KAAK,GAAG,OAAO,SAAS,IAAI,KAAK;EAElD,IAAI,OAAsB;EAC1B,IAAI,YAAY;EAChB,MAAM,QAAQ,UAAyB,UAAwB;GAC7D,KAAK,MAAM,MAAM,aAAa,OAAO,MAAM,QAAQ,MAAM,WAAW,QAAQ,GAAG;IAC7E,IAAI,SAAS,IAAI,EAAE,GAAG;IACtB,MAAM,OAAO,MAAM,IAAI,EAAE;IACzB,IAAI,CAAC,QAAQ,KAAK,SAAS,aAAa;IACxC,IAAI,KAAK,UAAU,SAAS,SAAS,aAChC,UAAU,UAAU,EAAY,MAAM,QAAQ,MAAM,IAAI,IAAI,KAAK,QAAQ,OAAO,EAAE,GAAG;KACxF,OAAO;KACP,YAAY;IACd;IACA,KAAK,IAAI,QAAQ,CAAC;GACpB;EACF;EACA,KAAK,MAAM,CAAC;EACZ,SAAS,IAAI,OAAO,IAAI;EACxB,OAAO;CACT;CACA,KAAK,MAAM,MAAM,KAAK;EACpB,MAAM,OAAO,MAAM,IAAI,EAAE;EACzB,MAAM,SAAS,MAAM,UAAU;EAC/B,IAAI,CAAC,QAAQ,WAAW,MAAM;EAC9B,MAAM,SAAS,UAAU,UAAU,MAAgB;EACnD,IAAI,CAAC,UAAU,MAAM,QAAQ,MAAM,GAAG;EACtC,IAAI,IAAI,IAAI,UAAU,KAAK,KAAK,CAAC;CACnC;CACA,OAAO;AACT"}
@@ -1,4 +1,5 @@
1
1
  import { nodeDescriptors, poseDescriptorOf } from "../poseDescriptorDep.js";
2
+ import { transformFinisher } from "../transformFinish.js";
2
3
  import { commitGestureOps, readGestureLifecycle } from "../gestureLifecycle.js";
3
4
  import { gestureReaders } from "../../gestures/shared/screenTolerance.js";
4
5
  import { moveSources } from "../selectionSubtree.js";
@@ -6,9 +7,15 @@ import { layoutDrop } from "./behaviors/layoutDrop.js";
6
7
  import { reparentOnDrop } from "./behaviors/reparentOnDrop.js";
7
8
  import { buildGestureContext, captureFrames, capturePlaneRefs, captureStartPoses } from "./capture.js";
8
9
  import { moveGestureAdapter } from "./gestureAdapter.js";
10
+ import { lazySnapSession } from "../../../features/snap/lazySession.js";
9
11
  import { DRAG_THRESHOLD_PX, isPathEditPart, resolveParams } from "@weasel-js/routing";
10
12
 
11
13
  //#region src/interactions/actions/move/session.ts
14
+ function admitsOf(canDropInto, ids) {
15
+ if (canDropInto === void 0) return () => true;
16
+ const roots = ids;
17
+ return (parent) => canDropInto(parent, roots);
18
+ }
12
19
  /** Capture a drag at press. Null when there is nothing to move. Runs each
13
20
  * behavior's `onStart`. `frame` reads the drag's latest frame, for the
14
21
  * behaviors appended to the binding's own. */
@@ -19,6 +26,7 @@ function beginMove(ctx, opts, frame) {
19
26
  const layout = ctx.deps.layout;
20
27
  const applyOps = ctx.deps.applyOps;
21
28
  const geometryProjection = ctx.deps.geometryProjection;
29
+ const finish = transformFinisher(ctx.deps);
22
30
  if (!selection || !scene) return null;
23
31
  const pc = scene.composition;
24
32
  if (isPathEditPart(ctx.drag?.affordance)) return null;
@@ -44,7 +52,8 @@ function beginMove(ctx, opts, frame) {
44
52
  const adapter = moveGestureAdapter(scene, (ops, _label, through) => commitGestureOps({
45
53
  scene,
46
54
  applyOps,
47
- adapter: through
55
+ adapter: through,
56
+ finish
48
57
  }, lifecycle, ops), selection.adapterMethods);
49
58
  const readers = gestureReaders(ctx.deps);
50
59
  const gestureCtx = buildGestureContext(ctx, scene, ids, startPoses, adapter, readers);
@@ -68,17 +77,19 @@ function beginMove(ctx, opts, frame) {
68
77
  layout,
69
78
  pc,
70
79
  applyOps,
80
+ finish,
71
81
  geometryProjection,
72
82
  lifecycle,
73
83
  nodeAtPoint: ctx.deps.nodeAtPoint,
84
+ admits: admitsOf(ctx.deps.canDropInto, ids),
74
85
  reparentOnDrop: () => resolveParams(opts?.params)?.["reparentOnDrop"] ?? "off",
75
86
  dragThresholdPx,
76
- snap: ctx.deps.snap?.begin({
87
+ snap: lazySnapSession(ctx.deps.snap, {
77
88
  gesture: "move",
78
89
  ids,
79
90
  press,
80
91
  constrain: "axis"
81
- }) ?? null,
92
+ }),
82
93
  press
83
94
  };
84
95
  return {
@@ -1 +1 @@
1
- {"version":3,"file":"session.js","names":[],"sources":["../../../../src/interactions/actions/move/session.ts"],"sourcesContent":["import type { BindingOpts, InvocationCtx } from '@weasel-js/routing';\nimport { DRAG_THRESHOLD_PX, isPathEditPart, resolveParams } from '@weasel-js/routing';\nimport type { Op } from 'core/ops/types';\nimport type { NodeId, Scene } from 'core/scene/types';\nimport type { PoseDescriptor } from 'core/geometry/poseDescriptor';\nimport type { PoseComposition } from 'core/scene/composePose';\nimport { gestureReaders, type GestureReaders } from '../../gestures/shared/screenTolerance';\nimport type { GestureContext, MoveBehavior } from '../../gestures/types';\nimport type { LayoutDep, NodeAtPointDep } from '../depSchema';\nimport type { GeometryProjection } from '../geometryProjection';\nimport { commitGestureOps, readGestureLifecycle, type GestureLifecycle } from '../gestureLifecycle';\nimport { nodeDescriptors, poseDescriptorOf } from '../poseDescriptorDep';\nimport { moveSources } from '../selectionSubtree';\nimport { layoutDrop } from './behaviors/layoutDrop';\nimport { reparentOnDrop } from './behaviors/reparentOnDrop';\nimport { buildGestureContext, captureFrames, capturePlaneRefs, captureStartPoses } from './capture';\nimport { moveGestureAdapter, type MoveGestureAdapter } from './gestureAdapter';\nimport type { SnapSession } from '../snapDep';\n\n/** Allowed values for the `reparentOnDrop` binding param. `'off'` (the\n * default) preserves the legacy translate-only commit. `'top'` lands the\n * moved node at the top of the container under the cursor. `'above'`\n * lands it immediately above the hit sibling in z-order (falls back to\n * `'top'` semantics when the hit is itself a container or when the hit\n * and moved node share no parent). */\nexport type ReparentOnDrop = 'off' | 'top' | 'above';\n\n/** What a move captured at press. Nothing here changes during the drag. */\nexport interface MoveSession {\n readonly scene: Scene<unknown, string, unknown>;\n /** The drag roots. */\n readonly ids: readonly NodeId[];\n /** Descendants of the roots, previewed along with them. */\n readonly cascadeIds: readonly NodeId[];\n /** Start pose of every root and descendant. */\n readonly startPoses: ReadonlyMap<NodeId, unknown>;\n /** Ids whose frame is itself moving, so they ride it and are not translated. */\n readonly carried: ReadonlySet<NodeId>;\n /** For each moved id stored under a frame: its world pose and the frame's, at press. */\n readonly frames: ReadonlyMap<NodeId, { world: unknown; frameWorld: unknown }>;\n /** Ids grabbed mid-glide, with how far the pose shown stood from the document's. */\n readonly caught: ReadonlyMap<NodeId, { dx: number; dy: number }>;\n /** Per layer on another plane than the edited one: a point of its first root, in both. */\n readonly planeRefs: ReadonlyMap<string, { own: { x: number; y: number }; edited: { x: number; y: number } }>;\n readonly view: unknown;\n readonly projection: PoseDescriptor<unknown>;\n readonly descriptorOf: (id: NodeId | string) => PoseDescriptor<unknown>;\n readonly pc: PoseComposition<unknown>;\n /** The binding's behaviors, then the ones the action adds: a layout drop\n * when the `layout` dep is present, and reparent-on-drop. */\n readonly behaviors: readonly MoveBehavior<unknown>[];\n /** Reused across the drag; behaviors mutate its `scratch`. */\n readonly gestureCtx: GestureContext<unknown>;\n readonly adapter: MoveGestureAdapter<unknown>;\n readonly layout: LayoutDep | undefined;\n readonly applyOps?: (ops: Op[], label: string) => void;\n readonly geometryProjection?: GeometryProjection;\n readonly lifecycle: GestureLifecycle;\n readonly nodeAtPoint: NodeAtPointDep | undefined;\n /** Read at release: a binding's params may be a thunk. */\n readonly reparentOnDrop: () => ReparentOnDrop;\n readonly dragThresholdPx: number;\n /** The drag's snapping; null when the `snap` dep runs no sessions. */\n readonly snap: SnapSession | null;\n /** The press, in the coordinates the drag's delta is measured in. */\n readonly press: { x: number; y: number };\n}\n\n/** What one `onMove` produced. Replaced, not mutated, by the next. */\nexport interface MoveFrame {\n readonly delta: { dx: number; dy: number };\n /** Stored-frame pose of every displaced id, roots and descendants, and of\n * any other id a behavior posed. */\n readonly previews: Map<NodeId, unknown>;\n /** Ids the behaviors asked to have painted at full opacity. */\n readonly opaque: ReadonlySet<NodeId>;\n}\n\n/** What the behaviors the action builds itself read of the drag they belong\n * to. Neither is readable from `onStart`, which runs before the session\n * exists. */\nexport interface MoveGesture {\n session(): MoveSession;\n /** The latest frame: the previous one while an `onMove` is running. */\n frame(): MoveFrame;\n}\n\n/** Capture a drag at press. Null when there is nothing to move. Runs each\n * behavior's `onStart`. `frame` reads the drag's latest frame, for the\n * behaviors appended to the binding's own. */\nexport function beginMove(\n ctx: InvocationCtx,\n opts: BindingOpts | undefined,\n frame: () => MoveFrame,\n): { session: MoveSession; readers: GestureReaders } | null {\n const selection = ctx.deps.selection;\n const scene = ctx.deps.scene;\n const projection = poseDescriptorOf(ctx.deps.poseDescriptor);\n const layout = ctx.deps.layout;\n const applyOps = ctx.deps.applyOps;\n const geometryProjection = ctx.deps.geometryProjection;\n\n if (!selection || !scene) return null;\n const pc = scene.composition;\n // Anchors, segments, and balance grips sit on the selected body, and this binding ties the\n // path-edit drags' on specificity; with no mode registry to filter move out, declining is what\n // lets them through.\n if (isPathEditPart(ctx.drag?.affordance)) return null;\n\n const params = resolveParams(opts?.params);\n const expandIds = params?.['expandIds'] as ((ids: string[]) => string[]) | undefined;\n const selected = [...selection.get()] as string[];\n const ids = moveSources(scene, expandIds ? expandIds(selected) : selected) as NodeId[];\n if (ids.length === 0) return null;\n\n const reflow = layout?.reflow ?? null;\n const { startPoses, cascadeIds, caught } = captureStartPoses(scene, ids, reflow, projection);\n const { carried, frames } = captureFrames(scene, ids, startPoses);\n\n // Everything a move does beyond translating is a behavior: the binding's\n // own, then the layout drop, then reparent-on-drop. On release the first to\n // answer owns the commit, so each preempts the ones after it.\n const gesture: MoveGesture = { session: () => session, frame };\n const behaviors: MoveBehavior<unknown>[] = [\n ...((opts?.behaviors ?? []) as MoveBehavior<unknown>[]),\n ...(layout ? [layoutDrop(gesture)] : []),\n reparentOnDrop(gesture),\n ];\n const dragThresholdPx = (params?.['dragThresholdPx'] as number | undefined)\n ?? ctx.deps.kitSettings?.get('gestures.dragPx') ?? DRAG_THRESHOLD_PX;\n const lifecycle = readGestureLifecycle(params, 'Move', behaviors);\n // A behavior that commits after the gesture has ended, a coast coming to\n // rest, lands its batch the way the gesture's own commit does.\n const adapter = moveGestureAdapter<unknown>(scene, (ops, _label, through) =>\n commitGestureOps({ scene, applyOps, adapter: through }, lifecycle, ops), selection.adapterMethods);\n const readers = gestureReaders(ctx.deps);\n const gestureCtx = buildGestureContext(ctx, scene, ids, startPoses, adapter, readers);\n for (const b of behaviors) b.onStart?.(gestureCtx);\n\n const press = ctx.drag?.start ?? ctx.world;\n const session: MoveSession = {\n startPoses,\n ids,\n cascadeIds,\n carried,\n frames,\n scene,\n view: ctx.deps.view,\n planeRefs: capturePlaneRefs(scene, ids, ctx.deps.view, frames, startPoses, projection),\n caught,\n projection,\n descriptorOf: nodeDescriptors(scene, projection),\n behaviors,\n gestureCtx,\n adapter,\n layout,\n pc,\n applyOps,\n geometryProjection,\n lifecycle,\n nodeAtPoint: ctx.deps.nodeAtPoint,\n reparentOnDrop: () => (resolveParams(opts?.params)?.['reparentOnDrop'] as ReparentOnDrop | undefined) ?? 'off',\n dragThresholdPx,\n snap: ctx.deps.snap?.begin({ gesture: 'move', ids, press, constrain: 'axis' }) ?? null,\n press,\n };\n return { session, readers };\n}\n"],"mappings":";;;;;;;;;;;;;;AA0FA,SAAgB,UACd,KACA,MACA,OAC0D;CAC1D,MAAM,YAAY,IAAI,KAAK;CAC3B,MAAM,QAAQ,IAAI,KAAK;CACvB,MAAM,aAAa,iBAAiB,IAAI,KAAK,cAAc;CAC3D,MAAM,SAAS,IAAI,KAAK;CACxB,MAAM,WAAW,IAAI,KAAK;CAC1B,MAAM,qBAAqB,IAAI,KAAK;CAEpC,IAAI,CAAC,aAAa,CAAC,OAAO,OAAO;CACjC,MAAM,KAAK,MAAM;CAIjB,IAAI,eAAe,IAAI,MAAM,UAAU,GAAG,OAAO;CAEjD,MAAM,SAAS,cAAc,MAAM,MAAM;CACzC,MAAM,YAAY,SAAS;CAC3B,MAAM,WAAW,CAAC,GAAG,UAAU,IAAI,CAAC;CACpC,MAAM,MAAM,YAAY,OAAO,YAAY,UAAU,QAAQ,IAAI,QAAQ;CACzE,IAAI,IAAI,WAAW,GAAG,OAAO;CAE7B,MAAM,SAAS,QAAQ,UAAU;CACjC,MAAM,EAAE,YAAY,YAAY,WAAW,kBAAkB,OAAO,KAAK,QAAQ,UAAU;CAC3F,MAAM,EAAE,SAAS,WAAW,cAAc,OAAO,KAAK,UAAU;CAKhE,MAAM,UAAuB;EAAE,eAAe;EAAS;CAAM;CAC7D,MAAM,YAAqC;EACzC,GAAK,MAAM,aAAa,CAAC;EACzB,GAAI,SAAS,CAAC,WAAW,OAAO,CAAC,IAAI,CAAC;EACtC,eAAe,OAAO;CACxB;CACA,MAAM,kBAAmB,SAAS,sBAC7B,IAAI,KAAK,aAAa,IAAI,iBAAiB,KAAK;CACrD,MAAM,YAAY,qBAAqB,QAAQ,QAAQ,SAAS;CAGhE,MAAM,UAAU,mBAA4B,QAAQ,KAAK,QAAQ,YAC/D,iBAAiB;EAAE;EAAO;EAAU,SAAS;CAAQ,GAAG,WAAW,GAAG,GAAG,UAAU,cAAc;CACnG,MAAM,UAAU,eAAe,IAAI,IAAI;CACvC,MAAM,aAAa,oBAAoB,KAAK,OAAO,KAAK,YAAY,SAAS,OAAO;CACpF,KAAK,MAAM,KAAK,WAAW,EAAE,UAAU,UAAU;CAEjD,MAAM,QAAQ,IAAI,MAAM,SAAS,IAAI;CACrC,MAAM,UAAuB;EAC3B;EACA;EACA;EACA;EACA;EACA;EACA,MAAM,IAAI,KAAK;EACf,WAAW,iBAAiB,OAAO,KAAK,IAAI,KAAK,MAAM,QAAQ,YAAY,UAAU;EACrF;EACA;EACA,cAAc,gBAAgB,OAAO,UAAU;EAC/C;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA,aAAa,IAAI,KAAK;EACtB,sBAAuB,cAAc,MAAM,MAAM,CAAC,GAAG,qBAAoD;EACzG;EACA,MAAM,IAAI,KAAK,MAAM,MAAM;GAAE,SAAS;GAAQ;GAAK;GAAO,WAAW;EAAO,CAAC,KAAK;EAClF;CACF;CACA,OAAO;EAAE;EAAS;CAAQ;AAC5B"}
1
+ {"version":3,"file":"session.js","names":[],"sources":["../../../../src/interactions/actions/move/session.ts"],"sourcesContent":["import type { BindingOpts, InvocationCtx } from '@weasel-js/routing';\nimport { DRAG_THRESHOLD_PX, isPathEditPart, resolveParams } from '@weasel-js/routing';\nimport type { Op } from 'core/ops/types';\nimport type { NodeId, Scene } from 'core/scene/types';\nimport type { PoseDescriptor } from 'core/geometry/poseDescriptor';\nimport type { PoseComposition } from 'core/scene/composePose';\nimport { gestureReaders, type GestureReaders } from '../../gestures/shared/screenTolerance';\nimport type { GestureContext, MoveBehavior } from '../../gestures/types';\nimport type { CanDropIntoDep, LayoutDep, NodeAtPointDep } from '../depSchema';\nimport type { GeometryProjection } from '../geometryProjection';\nimport { commitGestureOps, readGestureLifecycle, type GestureLifecycle } from '../gestureLifecycle';\nimport { transformFinisher, type TransformFinisher } from '../transformFinish';\nimport { nodeDescriptors, poseDescriptorOf } from '../poseDescriptorDep';\nimport { moveSources } from '../selectionSubtree';\nimport { layoutDrop } from './behaviors/layoutDrop';\nimport { reparentOnDrop } from './behaviors/reparentOnDrop';\nimport { buildGestureContext, captureFrames, capturePlaneRefs, captureStartPoses } from './capture';\nimport { moveGestureAdapter, type MoveGestureAdapter } from './gestureAdapter';\nimport type { SnapSession } from '../snapDep';\nimport { lazySnapSession } from 'features/snap/lazySession';\n\n/** Allowed values for the `reparentOnDrop` binding param. `'off'` (the\n * default) preserves the legacy translate-only commit. `'top'` lands the\n * moved node at the top of the container under the cursor. `'above'`\n * lands it immediately above the hit sibling in z-order (falls back to\n * `'top'` semantics when the hit is itself a container or when the hit\n * and moved node share no parent). */\nexport type ReparentOnDrop = 'off' | 'top' | 'above';\n\n/** What a move captured at press. Nothing here changes during the drag. */\nexport interface MoveSession {\n readonly scene: Scene<unknown, string, unknown>;\n /** The drag roots. */\n readonly ids: readonly NodeId[];\n /** Descendants of the roots, previewed along with them. */\n readonly cascadeIds: readonly NodeId[];\n /** Start pose of every root and descendant. */\n readonly startPoses: ReadonlyMap<NodeId, unknown>;\n /** Ids whose frame is itself moving, so they ride it and are not translated. */\n readonly carried: ReadonlySet<NodeId>;\n /** For each moved id stored under a frame: its world pose and the frame's, at press. */\n readonly frames: ReadonlyMap<NodeId, { world: unknown; frameWorld: unknown }>;\n /** Ids grabbed mid-glide, with how far the pose shown stood from the document's. */\n readonly caught: ReadonlyMap<NodeId, { dx: number; dy: number }>;\n /** Per layer on another plane than the edited one: a point of its first root, in both. */\n readonly planeRefs: ReadonlyMap<string, { own: { x: number; y: number }; edited: { x: number; y: number } }>;\n readonly view: unknown;\n readonly projection: PoseDescriptor<unknown>;\n readonly descriptorOf: (id: NodeId | string) => PoseDescriptor<unknown>;\n readonly pc: PoseComposition<unknown>;\n /** The binding's behaviors, then the ones the action adds: a layout drop\n * when the `layout` dep is present, and reparent-on-drop. */\n readonly behaviors: readonly MoveBehavior<unknown>[];\n /** Reused across the drag; behaviors mutate its `scratch`. */\n readonly gestureCtx: GestureContext<unknown>;\n readonly adapter: MoveGestureAdapter<unknown>;\n readonly layout: LayoutDep | undefined;\n readonly applyOps?: (ops: Op[], label: string) => void;\n /** Finishes the committed batch with the other nodes that change with it. */\n readonly finish?: TransformFinisher;\n readonly geometryProjection?: GeometryProjection;\n readonly lifecycle: GestureLifecycle;\n readonly nodeAtPoint: NodeAtPointDep | undefined;\n /** Whether a container admits the dragged roots; every one does when absent. */\n readonly admits: (parent: NodeId) => boolean;\n /** Read at release: a binding's params may be a thunk. */\n readonly reparentOnDrop: () => ReparentOnDrop;\n readonly dragThresholdPx: number;\n /** The drag's snapping, begun once the drag passes its threshold; null\n * with no `snap` dep. */\n readonly snap: SnapSession | null;\n /** The press, in the coordinates the drag's delta is measured in. */\n readonly press: { x: number; y: number };\n}\n\n/** What one `onMove` produced. Replaced, not mutated, by the next. */\nexport interface MoveFrame {\n readonly delta: { dx: number; dy: number };\n /** Stored-frame pose of every displaced id, roots and descendants, and of\n * any other id a behavior posed. */\n readonly previews: Map<NodeId, unknown>;\n /** Ids the behaviors asked to have painted at full opacity. */\n readonly opaque: ReadonlySet<NodeId>;\n}\n\n/** What the behaviors the action builds itself read of the drag they belong\n * to. Neither is readable from `onStart`, which runs before the session\n * exists. */\nexport interface MoveGesture {\n session(): MoveSession;\n /** The latest frame: the previous one while an `onMove` is running. */\n frame(): MoveFrame;\n}\n\nfunction admitsOf(\n canDropInto: CanDropIntoDep | undefined,\n ids: readonly NodeId[],\n): (parent: NodeId) => boolean {\n if (canDropInto === undefined) return () => true;\n const roots = ids as readonly string[];\n return (parent) => canDropInto(parent as string, roots);\n}\n\n/** Capture a drag at press. Null when there is nothing to move. Runs each\n * behavior's `onStart`. `frame` reads the drag's latest frame, for the\n * behaviors appended to the binding's own. */\nexport function beginMove(\n ctx: InvocationCtx,\n opts: BindingOpts | undefined,\n frame: () => MoveFrame,\n): { session: MoveSession; readers: GestureReaders } | null {\n const selection = ctx.deps.selection;\n const scene = ctx.deps.scene;\n const projection = poseDescriptorOf(ctx.deps.poseDescriptor);\n const layout = ctx.deps.layout;\n const applyOps = ctx.deps.applyOps;\n const geometryProjection = ctx.deps.geometryProjection;\n const finish = transformFinisher(ctx.deps);\n\n if (!selection || !scene) return null;\n const pc = scene.composition;\n // Anchors, segments, and balance grips sit on the selected body, and this binding ties the\n // path-edit drags' on specificity; with no mode registry to filter move out, declining is what\n // lets them through.\n if (isPathEditPart(ctx.drag?.affordance)) return null;\n\n const params = resolveParams(opts?.params);\n const expandIds = params?.['expandIds'] as ((ids: string[]) => string[]) | undefined;\n const selected = [...selection.get()] as string[];\n const ids = moveSources(scene, expandIds ? expandIds(selected) : selected) as NodeId[];\n if (ids.length === 0) return null;\n\n const reflow = layout?.reflow ?? null;\n const { startPoses, cascadeIds, caught } = captureStartPoses(scene, ids, reflow, projection);\n const { carried, frames } = captureFrames(scene, ids, startPoses);\n\n // Everything a move does beyond translating is a behavior: the binding's\n // own, then the layout drop, then reparent-on-drop. On release the first to\n // answer owns the commit, so each preempts the ones after it.\n const gesture: MoveGesture = { session: () => session, frame };\n const behaviors: MoveBehavior<unknown>[] = [\n ...((opts?.behaviors ?? []) as MoveBehavior<unknown>[]),\n ...(layout ? [layoutDrop(gesture)] : []),\n reparentOnDrop(gesture),\n ];\n const dragThresholdPx = (params?.['dragThresholdPx'] as number | undefined)\n ?? ctx.deps.kitSettings?.get('gestures.dragPx') ?? DRAG_THRESHOLD_PX;\n const lifecycle = readGestureLifecycle(params, 'Move', behaviors);\n // A behavior that commits after the gesture has ended, a coast coming to\n // rest, lands its batch the way the gesture's own commit does.\n const adapter = moveGestureAdapter<unknown>(scene, (ops, _label, through) =>\n commitGestureOps({ scene, applyOps, adapter: through, finish }, lifecycle, ops), selection.adapterMethods);\n const readers = gestureReaders(ctx.deps);\n const gestureCtx = buildGestureContext(ctx, scene, ids, startPoses, adapter, readers);\n for (const b of behaviors) b.onStart?.(gestureCtx);\n\n const press = ctx.drag?.start ?? ctx.world;\n const session: MoveSession = {\n startPoses,\n ids,\n cascadeIds,\n carried,\n frames,\n scene,\n view: ctx.deps.view,\n planeRefs: capturePlaneRefs(scene, ids, ctx.deps.view, frames, startPoses, projection),\n caught,\n projection,\n descriptorOf: nodeDescriptors(scene, projection),\n behaviors,\n gestureCtx,\n adapter,\n layout,\n pc,\n applyOps,\n finish,\n geometryProjection,\n lifecycle,\n nodeAtPoint: ctx.deps.nodeAtPoint,\n admits: admitsOf(ctx.deps.canDropInto, ids),\n reparentOnDrop: () => (resolveParams(opts?.params)?.['reparentOnDrop'] as ReparentOnDrop | undefined) ?? 'off',\n dragThresholdPx,\n snap: lazySnapSession(ctx.deps.snap, { gesture: 'move', ids, press, constrain: 'axis' }),\n press,\n };\n return { session, readers };\n}\n"],"mappings":";;;;;;;;;;;;;AA8FA,SAAS,SACP,aACA,KAC6B;CAC7B,IAAI,gBAAgB,QAAW,aAAa;CAC5C,MAAM,QAAQ;CACd,QAAQ,WAAW,YAAY,QAAkB,KAAK;AACxD;;;;AAKA,SAAgB,UACd,KACA,MACA,OAC0D;CAC1D,MAAM,YAAY,IAAI,KAAK;CAC3B,MAAM,QAAQ,IAAI,KAAK;CACvB,MAAM,aAAa,iBAAiB,IAAI,KAAK,cAAc;CAC3D,MAAM,SAAS,IAAI,KAAK;CACxB,MAAM,WAAW,IAAI,KAAK;CAC1B,MAAM,qBAAqB,IAAI,KAAK;CACpC,MAAM,SAAS,kBAAkB,IAAI,IAAI;CAEzC,IAAI,CAAC,aAAa,CAAC,OAAO,OAAO;CACjC,MAAM,KAAK,MAAM;CAIjB,IAAI,eAAe,IAAI,MAAM,UAAU,GAAG,OAAO;CAEjD,MAAM,SAAS,cAAc,MAAM,MAAM;CACzC,MAAM,YAAY,SAAS;CAC3B,MAAM,WAAW,CAAC,GAAG,UAAU,IAAI,CAAC;CACpC,MAAM,MAAM,YAAY,OAAO,YAAY,UAAU,QAAQ,IAAI,QAAQ;CACzE,IAAI,IAAI,WAAW,GAAG,OAAO;CAE7B,MAAM,SAAS,QAAQ,UAAU;CACjC,MAAM,EAAE,YAAY,YAAY,WAAW,kBAAkB,OAAO,KAAK,QAAQ,UAAU;CAC3F,MAAM,EAAE,SAAS,WAAW,cAAc,OAAO,KAAK,UAAU;CAKhE,MAAM,UAAuB;EAAE,eAAe;EAAS;CAAM;CAC7D,MAAM,YAAqC;EACzC,GAAK,MAAM,aAAa,CAAC;EACzB,GAAI,SAAS,CAAC,WAAW,OAAO,CAAC,IAAI,CAAC;EACtC,eAAe,OAAO;CACxB;CACA,MAAM,kBAAmB,SAAS,sBAC7B,IAAI,KAAK,aAAa,IAAI,iBAAiB,KAAK;CACrD,MAAM,YAAY,qBAAqB,QAAQ,QAAQ,SAAS;CAGhE,MAAM,UAAU,mBAA4B,QAAQ,KAAK,QAAQ,YAC/D,iBAAiB;EAAE;EAAO;EAAU,SAAS;EAAS;CAAO,GAAG,WAAW,GAAG,GAAG,UAAU,cAAc;CAC3G,MAAM,UAAU,eAAe,IAAI,IAAI;CACvC,MAAM,aAAa,oBAAoB,KAAK,OAAO,KAAK,YAAY,SAAS,OAAO;CACpF,KAAK,MAAM,KAAK,WAAW,EAAE,UAAU,UAAU;CAEjD,MAAM,QAAQ,IAAI,MAAM,SAAS,IAAI;CACrC,MAAM,UAAuB;EAC3B;EACA;EACA;EACA;EACA;EACA;EACA,MAAM,IAAI,KAAK;EACf,WAAW,iBAAiB,OAAO,KAAK,IAAI,KAAK,MAAM,QAAQ,YAAY,UAAU;EACrF;EACA;EACA,cAAc,gBAAgB,OAAO,UAAU;EAC/C;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA,aAAa,IAAI,KAAK;EACtB,QAAQ,SAAS,IAAI,KAAK,aAAa,GAAG;EAC1C,sBAAuB,cAAc,MAAM,MAAM,CAAC,GAAG,qBAAoD;EACzG;EACA,MAAM,gBAAgB,IAAI,KAAK,MAAM;GAAE,SAAS;GAAQ;GAAK;GAAO,WAAW;EAAO,CAAC;EACvF;CACF;CACA,OAAO;EAAE;EAAS;CAAQ;AAC5B"}
@@ -1,48 +1,19 @@
1
- import { translatePoseViaDescriptor } from "../../../core/geometry/poseDescriptor.js";
2
- import { createTransformOp } from "../../../core/ops/transform.js";
3
1
  import { localDelta } from "./delta.js";
4
- import { geometryDataOp } from "../geometryProjection.js";
2
+ import { translateNodeOps } from "../moveNodes.js";
5
3
 
6
4
  //#region src/interactions/actions/move/translate.ts
7
5
  /** The default translate commit: one transform op per id, plus the data-held
8
6
  * geometry op when the `geometryProjection` seam is wired. */
9
7
  function translateOps(session, ids, delta) {
10
- const ops = [];
11
- for (const id of ids) {
12
- if (!session.startPoses.has(id) || session.carried.has(id)) continue;
13
- const node = session.scene.get(id);
14
- if (!node) continue;
8
+ return translateNodeOps(session.scene, ids, (id) => {
9
+ if (!session.startPoses.has(id) || session.carried.has(id)) return null;
15
10
  const l = localDelta(session, id, delta.dx, delta.dy);
16
11
  const c = session.caught.get(id);
17
- if (c) {
18
- l.dx += c.dx;
19
- l.dy += c.dy;
20
- }
21
- const m = [
22
- 1,
23
- 0,
24
- 0,
25
- 1,
26
- l.dx,
27
- l.dy
28
- ];
29
- const authored = node.pose;
30
- ops.push(createTransformOp({
31
- id,
32
- from: authored,
33
- to: translatePoseViaDescriptor(authored, l.dx, l.dy, session.descriptorOf(id)),
34
- label: session.lifecycle.label
35
- }));
36
- if (session.geometryProjection) {
37
- const dataOp = geometryDataOp(session.geometryProjection, {
38
- id,
39
- data: node.data,
40
- pose: authored
41
- }, m, session.lifecycle.label);
42
- if (dataOp) ops.push(dataOp);
43
- }
44
- }
45
- return ops;
12
+ return c ? {
13
+ dx: l.dx + c.dx,
14
+ dy: l.dy + c.dy
15
+ } : l;
16
+ }, session.descriptorOf, session.geometryProjection, session.lifecycle.label);
46
17
  }
47
18
  /** The default translate commit for the dragged roots among `roots` and the
48
19
  * descendants that follow them. Anything else in `roots` is ignored. */
@@ -1 +1 @@
1
- {"version":3,"file":"translate.js","names":[],"sources":["../../../../src/interactions/actions/move/translate.ts"],"sourcesContent":["import type { Affine } from '@weasel-js/geom';\nimport { translatePoseViaDescriptor } from 'core/geometry/poseDescriptor';\nimport { createTransformOp } from 'core/ops/transform';\nimport type { Op } from 'core/ops/types';\nimport type { NodeId } from 'core/scene/types';\nimport { geometryDataOp } from '../geometryProjection';\nimport { localDelta } from './delta';\nimport type { MoveSession } from './session';\n\n/** The default translate commit: one transform op per id, plus the data-held\n * geometry op when the `geometryProjection` seam is wired. */\nexport function translateOps(session: MoveSession, ids: Iterable<NodeId>, delta: { dx: number; dy: number }): Op[] {\n const ops: Op[] = [];\n for (const id of ids) {\n if (!session.startPoses.has(id) || session.carried.has(id)) continue;\n const node = session.scene.get(id);\n if (!node) continue;\n const l = localDelta(session, id, delta.dx, delta.dy);\n const c = session.caught.get(id);\n if (c) { l.dx += c.dx; l.dy += c.dy; }\n const m: Affine = [1, 0, 0, 1, l.dx, l.dy];\n // The authored pose, not the captured one: a derived node's captured pose\n // is its derivation, and writing that back would lose the placeholder undo\n // restores.\n const authored = node.pose;\n ops.push(createTransformOp<unknown>({\n id: id as string,\n from: authored,\n to: translatePoseViaDescriptor(authored, l.dx, l.dy, session.descriptorOf(id)),\n label: session.lifecycle.label,\n }));\n if (session.geometryProjection) {\n const dataOp = geometryDataOp(\n session.geometryProjection,\n { id: id as string, data: node.data, pose: authored },\n m,\n session.lifecycle.label,\n );\n if (dataOp) ops.push(dataOp);\n }\n }\n return ops;\n}\n\n/** The default translate commit for the dragged roots among `roots` and the\n * descendants that follow them. Anything else in `roots` is ignored. */\nexport function rootTranslateOps(session: MoveSession, roots: Iterable<string>, delta: { dx: number; dy: number }): Op[] {\n const wanted = new Set<string>(roots);\n const others = new Set<NodeId>(session.ids.filter((id) => !wanted.has(id)));\n return translateOps(session, unreleasedIds(session, others), delta);\n}\n\n/** Selected roots no source layout claimed on release, plus the descendants\n * that follow them — the part of the selection the translate commit owns. */\nexport function unreleasedIds(session: MoveSession, released: Set<NodeId>): NodeId[] {\n const selected = new Set<NodeId>(session.ids);\n const out: NodeId[] = session.ids.filter((id) => !released.has(id));\n for (const id of session.cascadeIds) {\n let cur = session.scene.get(id)?.parent ?? null;\n let claimed = false;\n while (cur) {\n if (released.has(cur)) { claimed = true; break; }\n if (selected.has(cur)) break;\n cur = session.scene.get(cur)?.parent ?? null;\n }\n if (!claimed) out.push(id);\n }\n return out;\n}\n"],"mappings":";;;;;;;;AAWA,SAAgB,aAAa,SAAsB,KAAuB,OAAyC;CACjH,MAAM,MAAY,CAAC;CACnB,KAAK,MAAM,MAAM,KAAK;EACpB,IAAI,CAAC,QAAQ,WAAW,IAAI,EAAE,KAAK,QAAQ,QAAQ,IAAI,EAAE,GAAG;EAC5D,MAAM,OAAO,QAAQ,MAAM,IAAI,EAAE;EACjC,IAAI,CAAC,MAAM;EACX,MAAM,IAAI,WAAW,SAAS,IAAI,MAAM,IAAI,MAAM,EAAE;EACpD,MAAM,IAAI,QAAQ,OAAO,IAAI,EAAE;EAC/B,IAAI,GAAG;GAAE,EAAE,MAAM,EAAE;GAAI,EAAE,MAAM,EAAE;EAAI;EACrC,MAAM,IAAY;GAAC;GAAG;GAAG;GAAG;GAAG,EAAE;GAAI,EAAE;EAAE;EAIzC,MAAM,WAAW,KAAK;EACtB,IAAI,KAAK,kBAA2B;GAC9B;GACJ,MAAM;GACN,IAAI,2BAA2B,UAAU,EAAE,IAAI,EAAE,IAAI,QAAQ,aAAa,EAAE,CAAC;GAC7E,OAAO,QAAQ,UAAU;EAC3B,CAAC,CAAC;EACF,IAAI,QAAQ,oBAAoB;GAC9B,MAAM,SAAS,eACb,QAAQ,oBACR;IAAM;IAAc,MAAM,KAAK;IAAM,MAAM;GAAS,GACpD,GACA,QAAQ,UAAU,KACpB;GACA,IAAI,QAAQ,IAAI,KAAK,MAAM;EAC7B;CACF;CACA,OAAO;AACT;;;AAIA,SAAgB,iBAAiB,SAAsB,OAAyB,OAAyC;CACvH,MAAM,SAAS,IAAI,IAAY,KAAK;CAEpC,OAAO,aAAa,SAAS,cAAc,SAAS,IADjC,IAAY,QAAQ,IAAI,QAAQ,OAAO,CAAC,OAAO,IAAI,EAAE,CAAC,CACrB,CAAM,GAAG,KAAK;AACpE;;;AAIA,SAAgB,cAAc,SAAsB,UAAiC;CACnF,MAAM,WAAW,IAAI,IAAY,QAAQ,GAAG;CAC5C,MAAM,MAAgB,QAAQ,IAAI,QAAQ,OAAO,CAAC,SAAS,IAAI,EAAE,CAAC;CAClE,KAAK,MAAM,MAAM,QAAQ,YAAY;EACnC,IAAI,MAAM,QAAQ,MAAM,IAAI,EAAE,CAAC,EAAE,UAAU;EAC3C,IAAI,UAAU;EACd,OAAO,KAAK;GACV,IAAI,SAAS,IAAI,GAAG,GAAG;IAAE,UAAU;IAAM;GAAO;GAChD,IAAI,SAAS,IAAI,GAAG,GAAG;GACvB,MAAM,QAAQ,MAAM,IAAI,GAAG,CAAC,EAAE,UAAU;EAC1C;EACA,IAAI,CAAC,SAAS,IAAI,KAAK,EAAE;CAC3B;CACA,OAAO;AACT"}
1
+ {"version":3,"file":"translate.js","names":[],"sources":["../../../../src/interactions/actions/move/translate.ts"],"sourcesContent":["import type { Op } from 'core/ops/types';\nimport type { NodeId } from 'core/scene/types';\nimport { translateNodeOps } from '../moveNodes';\nimport { localDelta } from './delta';\nimport type { MoveSession } from './session';\n\n/** The default translate commit: one transform op per id, plus the data-held\n * geometry op when the `geometryProjection` seam is wired. */\nexport function translateOps(session: MoveSession, ids: Iterable<NodeId>, delta: { dx: number; dy: number }): Op[] {\n return translateNodeOps(\n session.scene,\n ids,\n (id) => {\n if (!session.startPoses.has(id) || session.carried.has(id)) return null;\n const l = localDelta(session, id, delta.dx, delta.dy);\n const c = session.caught.get(id);\n return c ? { dx: l.dx + c.dx, dy: l.dy + c.dy } : l;\n },\n session.descriptorOf,\n session.geometryProjection,\n session.lifecycle.label,\n );\n}\n\n/** The default translate commit for the dragged roots among `roots` and the\n * descendants that follow them. Anything else in `roots` is ignored. */\nexport function rootTranslateOps(session: MoveSession, roots: Iterable<string>, delta: { dx: number; dy: number }): Op[] {\n const wanted = new Set<string>(roots);\n const others = new Set<NodeId>(session.ids.filter((id) => !wanted.has(id)));\n return translateOps(session, unreleasedIds(session, others), delta);\n}\n\n/** Selected roots no source layout claimed on release, plus the descendants\n * that follow them — the part of the selection the translate commit owns. */\nexport function unreleasedIds(session: MoveSession, released: Set<NodeId>): NodeId[] {\n const selected = new Set<NodeId>(session.ids);\n const out: NodeId[] = session.ids.filter((id) => !released.has(id));\n for (const id of session.cascadeIds) {\n let cur = session.scene.get(id)?.parent ?? null;\n let claimed = false;\n while (cur) {\n if (released.has(cur)) { claimed = true; break; }\n if (selected.has(cur)) break;\n cur = session.scene.get(cur)?.parent ?? null;\n }\n if (!claimed) out.push(id);\n }\n return out;\n}\n"],"mappings":";;;;;;AAQA,SAAgB,aAAa,SAAsB,KAAuB,OAAyC;CACjH,OAAO,iBACL,QAAQ,OACR,MACC,OAAO;EACN,IAAI,CAAC,QAAQ,WAAW,IAAI,EAAE,KAAK,QAAQ,QAAQ,IAAI,EAAE,GAAG,OAAO;EACnE,MAAM,IAAI,WAAW,SAAS,IAAI,MAAM,IAAI,MAAM,EAAE;EACpD,MAAM,IAAI,QAAQ,OAAO,IAAI,EAAE;EAC/B,OAAO,IAAI;GAAE,IAAI,EAAE,KAAK,EAAE;GAAI,IAAI,EAAE,KAAK,EAAE;EAAG,IAAI;CACpD,GACA,QAAQ,cACR,QAAQ,oBACR,QAAQ,UAAU,KACpB;AACF;;;AAIA,SAAgB,iBAAiB,SAAsB,OAAyB,OAAyC;CACvH,MAAM,SAAS,IAAI,IAAY,KAAK;CAEpC,OAAO,aAAa,SAAS,cAAc,SAAS,IADjC,IAAY,QAAQ,IAAI,QAAQ,OAAO,CAAC,OAAO,IAAI,EAAE,CAAC,CACrB,CAAM,GAAG,KAAK;AACpE;;;AAIA,SAAgB,cAAc,SAAsB,UAAiC;CACnF,MAAM,WAAW,IAAI,IAAY,QAAQ,GAAG;CAC5C,MAAM,MAAgB,QAAQ,IAAI,QAAQ,OAAO,CAAC,SAAS,IAAI,EAAE,CAAC;CAClE,KAAK,MAAM,MAAM,QAAQ,YAAY;EACnC,IAAI,MAAM,QAAQ,MAAM,IAAI,EAAE,CAAC,EAAE,UAAU;EAC3C,IAAI,UAAU;EACd,OAAO,KAAK;GACV,IAAI,SAAS,IAAI,GAAG,GAAG;IAAE,UAAU;IAAM;GAAO;GAChD,IAAI,SAAS,IAAI,GAAG,GAAG;GACvB,MAAM,QAAQ,MAAM,IAAI,GAAG,CAAC,EAAE,UAAU;EAC1C;EACA,IAAI,CAAC,SAAS,IAAI,KAAK,EAAE;CAC3B;CACA,OAAO;AACT"}
@@ -0,0 +1,87 @@
1
+ import { translatePoseViaDescriptor } from "../../core/geometry/poseDescriptor.js";
2
+ import { scenePoseFrame } from "./poseFrame.js";
3
+ import { nodeDescriptors, poseDescriptorOf } from "./poseDescriptorDep.js";
4
+ import { commitTransformOps } from "./transformFinish.js";
5
+ import { createTransformOp } from "../../core/ops/transform.js";
6
+ import { moveSources, storedDelta, translateTargets } from "./selectionSubtree.js";
7
+ import { geometryDataOp } from "./geometryProjection.js";
8
+
9
+ //#region src/interactions/actions/moveNodes.ts
10
+ /** The deps {@link moveNodes} reads; an action calling it declares these. */
11
+ var MOVE_NODES_REQUIRES = [
12
+ "scene",
13
+ "selection",
14
+ "applyOps",
15
+ "poseDescriptor",
16
+ "geometryProjection",
17
+ "transformFollowers"
18
+ ];
19
+ /**
20
+ * One transform op per id, translating its authored pose by the stored-frame
21
+ * delta `deltaOf` gives it, plus its data-held geometry's op when a
22
+ * `geometryProjection` is wired. An id `deltaOf` answers null for is skipped.
23
+ * Every translate commit — a drag, a nudge, {@link moveNodes} — builds its ops here.
24
+ */
25
+ function translateNodeOps(scene, ids, deltaOf, descriptorOf, geometryProjection, label) {
26
+ const ops = [];
27
+ for (const id of ids) {
28
+ const node = scene.get(id);
29
+ if (!node) continue;
30
+ const l = deltaOf(id);
31
+ if (!l) continue;
32
+ const from = node.pose;
33
+ ops.push(createTransformOp({
34
+ id,
35
+ from,
36
+ to: translatePoseViaDescriptor(from, l.dx, l.dy, descriptorOf(id)),
37
+ label
38
+ }));
39
+ const m = [
40
+ 1,
41
+ 0,
42
+ 0,
43
+ 1,
44
+ l.dx,
45
+ l.dy
46
+ ];
47
+ const dataOp = geometryDataOp(geometryProjection, {
48
+ id,
49
+ data: node.data,
50
+ pose: from
51
+ }, m, label);
52
+ if (dataOp) ops.push(dataOp);
53
+ }
54
+ return ops;
55
+ }
56
+ /**
57
+ * The ops that move `ids` by `delta` the way a drag of them commits: each id
58
+ * that is not itself derived (a derived one is moved through what it derives
59
+ * from) and every descendant not carried by a moving frame, each translated in
60
+ * the frame it is stored in. `delta` is in the coordinates a gesture's points
61
+ * are — world on a 2D canvas — and the `poseDescriptor` dep turns it into each
62
+ * pose's translation, so a 3D host's descriptor lands it on the node's plane.
63
+ */
64
+ function moveNodesOps(scene, ids, delta, deps, label = "Move") {
65
+ const { dx, dy } = delta;
66
+ if (dx === 0 && dy === 0) return [];
67
+ const descriptorOf = nodeDescriptors(scene, poseDescriptorOf(deps?.poseDescriptor));
68
+ const frame = scenePoseFrame(scene);
69
+ return translateNodeOps(scene, translateTargets(scene, frame, moveSources(scene, ids)), (id) => storedDelta(frame, descriptorOf(id), id, dx, dy), descriptorOf, deps?.geometryProjection, label);
70
+ }
71
+ /**
72
+ * Move `ids` by `delta` the way a drag of them commits ({@link moveNodesOps}),
73
+ * finished by the `transformFollowers` and committed as one undo entry through
74
+ * `applyOps`, else the scene. Inside an open `scene.batch` it joins that
75
+ * batch's entry. Returns whether anything moved. A caller running as an
76
+ * action declares {@link MOVE_NODES_REQUIRES}.
77
+ */
78
+ function moveNodes(deps, ids, delta, label = "Move") {
79
+ const scene = deps.scene;
80
+ if (!scene) return false;
81
+ const ops = moveNodesOps(scene, ids, delta, deps, label);
82
+ return ops.length > 0 && commitTransformOps(deps, ops, label);
83
+ }
84
+
85
+ //#endregion
86
+ export { MOVE_NODES_REQUIRES, moveNodes, moveNodesOps, translateNodeOps };
87
+ //# sourceMappingURL=moveNodes.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"moveNodes.js","names":[],"sources":["../../../src/interactions/actions/moveNodes.ts"],"sourcesContent":["import type { ActionDeps } from '@weasel-js/routing';\nimport type { Affine } from '@weasel-js/geom';\nimport { translatePoseViaDescriptor, type PoseDescriptor } from 'core/geometry/poseDescriptor';\nimport { createTransformOp } from 'core/ops/transform';\nimport type { Op } from 'core/ops/types';\nimport type { NodeId, Scene } from 'core/scene/types';\nimport { geometryDataOp, type GeometryProjection } from './geometryProjection';\nimport { nodeDescriptors, poseDescriptorOf } from './poseDescriptorDep';\nimport { scenePoseFrame } from './poseFrame';\nimport { moveSources, storedDelta, translateTargets } from './selectionSubtree';\nimport { commitTransformOps } from './transformFinish';\n\ntype AnyScene = Scene<unknown, string, unknown>;\n\n/** The deps {@link moveNodes} reads; an action calling it declares these. */\nexport const MOVE_NODES_REQUIRES = [\n 'scene', 'selection', 'applyOps', 'poseDescriptor', 'geometryProjection', 'transformFollowers',\n] as const;\n\n/**\n * One transform op per id, translating its authored pose by the stored-frame\n * delta `deltaOf` gives it, plus its data-held geometry's op when a\n * `geometryProjection` is wired. An id `deltaOf` answers null for is skipped.\n * Every translate commit — a drag, a nudge, {@link moveNodes} — builds its ops here.\n */\nexport function translateNodeOps(\n scene: AnyScene,\n ids: Iterable<NodeId>,\n deltaOf: (id: NodeId) => { dx: number; dy: number } | null,\n descriptorOf: (id: NodeId) => PoseDescriptor<unknown>,\n geometryProjection: GeometryProjection | undefined,\n label: string,\n): Op[] {\n const ops: Op[] = [];\n for (const id of ids) {\n const node = scene.get(id);\n if (!node) continue;\n const l = deltaOf(id);\n if (!l) continue;\n // The authored pose, not the effective one: a derived node's effective\n // pose is its derivation, and writing that back would lose the\n // placeholder undo restores.\n const from = node.pose;\n ops.push(createTransformOp<unknown>({\n id: id as string,\n from,\n to: translatePoseViaDescriptor(from, l.dx, l.dy, descriptorOf(id)),\n label,\n }));\n const m: Affine = [1, 0, 0, 1, l.dx, l.dy];\n const dataOp = geometryDataOp(geometryProjection, { id: id as string, data: node.data, pose: from }, m, label);\n if (dataOp) ops.push(dataOp);\n }\n return ops;\n}\n\n/**\n * The ops that move `ids` by `delta` the way a drag of them commits: each id\n * that is not itself derived (a derived one is moved through what it derives\n * from) and every descendant not carried by a moving frame, each translated in\n * the frame it is stored in. `delta` is in the coordinates a gesture's points\n * are — world on a 2D canvas — and the `poseDescriptor` dep turns it into each\n * pose's translation, so a 3D host's descriptor lands it on the node's plane.\n */\nexport function moveNodesOps(\n scene: AnyScene,\n ids: readonly string[],\n delta: { dx: number; dy: number },\n deps?: Pick<ActionDeps, 'poseDescriptor' | 'geometryProjection'>,\n label = 'Move',\n): Op[] {\n const { dx, dy } = delta;\n if (dx === 0 && dy === 0) return [];\n const descriptorOf = nodeDescriptors(scene, poseDescriptorOf(deps?.poseDescriptor));\n const frame = scenePoseFrame(scene);\n const targets = translateTargets(scene, frame, moveSources(scene, ids));\n return translateNodeOps(\n scene,\n targets,\n (id) => storedDelta(frame, descriptorOf(id), id, dx, dy),\n descriptorOf,\n deps?.geometryProjection,\n label,\n );\n}\n\n/**\n * Move `ids` by `delta` the way a drag of them commits ({@link moveNodesOps}),\n * finished by the `transformFollowers` and committed as one undo entry through\n * `applyOps`, else the scene. Inside an open `scene.batch` it joins that\n * batch's entry. Returns whether anything moved. A caller running as an\n * action declares {@link MOVE_NODES_REQUIRES}.\n */\nexport function moveNodes(\n deps: ActionDeps,\n ids: readonly string[],\n delta: { dx: number; dy: number },\n label = 'Move',\n): boolean {\n const scene = deps.scene as AnyScene | undefined;\n if (!scene) return false;\n const ops = moveNodesOps(scene, ids, delta, deps, label);\n return ops.length > 0 && commitTransformOps(deps, ops, label);\n}\n"],"mappings":";;;;;;;;;;AAeA,IAAa,sBAAsB;CACjC;CAAS;CAAa;CAAY;CAAkB;CAAsB;AAC5E;;;;;;;AAQA,SAAgB,iBACd,OACA,KACA,SACA,cACA,oBACA,OACM;CACN,MAAM,MAAY,CAAC;CACnB,KAAK,MAAM,MAAM,KAAK;EACpB,MAAM,OAAO,MAAM,IAAI,EAAE;EACzB,IAAI,CAAC,MAAM;EACX,MAAM,IAAI,QAAQ,EAAE;EACpB,IAAI,CAAC,GAAG;EAIR,MAAM,OAAO,KAAK;EAClB,IAAI,KAAK,kBAA2B;GAC9B;GACJ;GACA,IAAI,2BAA2B,MAAM,EAAE,IAAI,EAAE,IAAI,aAAa,EAAE,CAAC;GACjE;EACF,CAAC,CAAC;EACF,MAAM,IAAY;GAAC;GAAG;GAAG;GAAG;GAAG,EAAE;GAAI,EAAE;EAAE;EACzC,MAAM,SAAS,eAAe,oBAAoB;GAAM;GAAc,MAAM,KAAK;GAAM,MAAM;EAAK,GAAG,GAAG,KAAK;EAC7G,IAAI,QAAQ,IAAI,KAAK,MAAM;CAC7B;CACA,OAAO;AACT;;;;;;;;;AAUA,SAAgB,aACd,OACA,KACA,OACA,MACA,QAAQ,QACF;CACN,MAAM,EAAE,IAAI,OAAO;CACnB,IAAI,OAAO,KAAK,OAAO,GAAG,OAAO,CAAC;CAClC,MAAM,eAAe,gBAAgB,OAAO,iBAAiB,MAAM,cAAc,CAAC;CAClF,MAAM,QAAQ,eAAe,KAAK;CAElC,OAAO,iBACL,OAFc,iBAAiB,OAAO,OAAO,YAAY,OAAO,GAAG,CAGnE,IACC,OAAO,YAAY,OAAO,aAAa,EAAE,GAAG,IAAI,IAAI,EAAE,GACvD,cACA,MAAM,oBACN,KACF;AACF;;;;;;;;AASA,SAAgB,UACd,MACA,KACA,OACA,QAAQ,QACC;CACT,MAAM,QAAQ,KAAK;CACnB,IAAI,CAAC,OAAO,OAAO;CACnB,MAAM,MAAM,aAAa,OAAO,KAAK,OAAO,MAAM,KAAK;CACvD,OAAO,IAAI,SAAS,KAAK,mBAAmB,MAAM,KAAK,KAAK;AAC9D"}