@weasel-js/core 1.11.1 → 1.13.0-rc.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (488) hide show
  1. package/CHANGELOG.md +275 -0
  2. package/dist/WeaselProvider.js +1 -1
  3. package/dist/affordances/pathAnchors.js +10 -6
  4. package/dist/affordances/pathAnchors.js.map +1 -1
  5. package/dist/animation/poseTarget.js +46 -15
  6. package/dist/animation/poseTarget.js.map +1 -1
  7. package/dist/animation/poseTransition.js +3 -3
  8. package/dist/animation/poseTransition.js.map +1 -1
  9. package/dist/animation/reflow.js +3 -6
  10. package/dist/animation/reflow.js.map +1 -1
  11. package/dist/animation/rig/bindRig.js +2 -2
  12. package/dist/animation/rig/bindRig.js.map +1 -1
  13. package/dist/animation/rig/skinBinding.js +2 -2
  14. package/dist/animation/rig/skinBinding.js.map +1 -1
  15. package/dist/barrel/canvas.js +5 -1
  16. package/dist/barrel/chrome.js +3 -1
  17. package/dist/barrel/paint.js +4 -4
  18. package/dist/barrel/paths.js +9 -4
  19. package/dist/barrel/renderer.js +3 -2
  20. package/dist/barrel/routing.js +4 -4
  21. package/dist/barrel/scene.js +7 -1
  22. package/dist/barrel/text.js +2 -2
  23. package/dist/barrel/viewport.js +6 -2
  24. package/dist/canvas/Canvas.js +2 -1
  25. package/dist/canvas/Canvas.js.map +1 -1
  26. package/dist/canvas/CanvasView.js +23 -34
  27. package/dist/canvas/CanvasView.js.map +1 -1
  28. package/dist/canvas/ModalityHud.js +1 -1
  29. package/dist/canvas/NodeShape.js +5 -1
  30. package/dist/canvas/NodeShape.js.map +1 -1
  31. package/dist/canvas/SceneCanvas/ActionsProviderIfRoot.js +1 -1
  32. package/dist/canvas/SceneCanvas/DepRegistryProviderIfRoot.js +1 -1
  33. package/dist/canvas/SceneCanvas/PointerProviderIfRoot.js +1 -1
  34. package/dist/canvas/SceneCanvas/SceneCanvasInput.js +2 -0
  35. package/dist/canvas/SceneCanvas/SceneCanvasInput.js.map +1 -1
  36. package/dist/canvas/SceneCanvas/SurfaceDeclarations.js +23 -0
  37. package/dist/canvas/SceneCanvas/SurfaceDeclarations.js.map +1 -0
  38. package/dist/canvas/SceneCanvas/canvasPassThrough.js +2 -1
  39. package/dist/canvas/SceneCanvas/canvasPassThrough.js.map +1 -1
  40. package/dist/canvas/SceneCanvas/defaultNodeRouting.js +2 -2
  41. package/dist/canvas/SceneCanvas/defaultNodeRouting.js.map +1 -1
  42. package/dist/canvas/SceneCanvas/props.js.map +1 -1
  43. package/dist/canvas/SceneCanvas/scenePointQuery.js +27 -5
  44. package/dist/canvas/SceneCanvas/scenePointQuery.js.map +1 -1
  45. package/dist/canvas/SceneCanvas/useActionTunings.js +6 -1
  46. package/dist/canvas/SceneCanvas/useActionTunings.js.map +1 -1
  47. package/dist/canvas/SceneCanvas/useCanvasDevtoolsRoot.js +1 -0
  48. package/dist/canvas/SceneCanvas/useCanvasDevtoolsRoot.js.map +1 -1
  49. package/dist/canvas/SceneCanvas/useCanvasTools.js +10 -2
  50. package/dist/canvas/SceneCanvas/useCanvasTools.js.map +1 -1
  51. package/dist/canvas/SceneCanvas/usePreviewGhostLayer.js +25 -19
  52. package/dist/canvas/SceneCanvas/usePreviewGhostLayer.js.map +1 -1
  53. package/dist/canvas/SceneCanvas/useSceneCanvasHandle.js +9 -3
  54. package/dist/canvas/SceneCanvas/useSceneCanvasHandle.js.map +1 -1
  55. package/dist/canvas/SceneCanvas/useSceneCanvasLayers.js +17 -2
  56. package/dist/canvas/SceneCanvas/useSceneCanvasLayers.js.map +1 -1
  57. package/dist/canvas/SceneCanvas/useSceneCanvasTools.js +2 -0
  58. package/dist/canvas/SceneCanvas/useSceneCanvasTools.js.map +1 -1
  59. package/dist/canvas/SceneCanvas/useSceneGestureDispatcher.js +19 -4
  60. package/dist/canvas/SceneCanvas/useSceneGestureDispatcher.js.map +1 -1
  61. package/dist/canvas/SceneCanvas/useScenePicking.js +31 -6
  62. package/dist/canvas/SceneCanvas/useScenePicking.js.map +1 -1
  63. package/dist/canvas/SceneCanvas/useSceneSelectTool.js +4 -5
  64. package/dist/canvas/SceneCanvas/useSceneSelectTool.js.map +1 -1
  65. package/dist/canvas/SceneCanvas/useSceneStandardActions.js +7 -4
  66. package/dist/canvas/SceneCanvas/useSceneStandardActions.js.map +1 -1
  67. package/dist/canvas/SceneCanvas.js +30 -12
  68. package/dist/canvas/SceneCanvas.js.map +1 -1
  69. package/dist/canvas/ViewMarker.js +35 -0
  70. package/dist/canvas/ViewMarker.js.map +1 -0
  71. package/dist/canvas/accentColor.js +26 -3
  72. package/dist/canvas/accentColor.js.map +1 -1
  73. package/dist/canvas/buildSceneTree.js +14 -20
  74. package/dist/canvas/buildSceneTree.js.map +1 -1
  75. package/dist/canvas/copiesAbove.js +1 -1
  76. package/dist/canvas/copiesAbove.js.map +1 -1
  77. package/dist/canvas/defaultDrawOne.js +11 -6
  78. package/dist/canvas/defaultDrawOne.js.map +1 -1
  79. package/dist/canvas/deps/areaSelect.js +5 -2
  80. package/dist/canvas/deps/areaSelect.js.map +1 -1
  81. package/dist/canvas/deps/booleans.js +3 -2
  82. package/dist/canvas/deps/booleans.js.map +1 -1
  83. package/dist/canvas/deps/enteredGroup.js +81 -0
  84. package/dist/canvas/deps/enteredGroup.js.map +1 -0
  85. package/dist/canvas/deps/hitTestArea.js +28 -16
  86. package/dist/canvas/deps/hitTestArea.js.map +1 -1
  87. package/dist/canvas/deps/insert.js +10 -38
  88. package/dist/canvas/deps/insert.js.map +1 -1
  89. package/dist/canvas/deps/insertSite.js +39 -0
  90. package/dist/canvas/deps/insertSite.js.map +1 -0
  91. package/dist/canvas/deps/lassoSelect.js +6 -3
  92. package/dist/canvas/deps/lassoSelect.js.map +1 -1
  93. package/dist/canvas/deps/pathEditCommits.js +3 -7
  94. package/dist/canvas/deps/pathEditCommits.js.map +1 -1
  95. package/dist/canvas/deps/pathLeaf.js +1 -1
  96. package/dist/canvas/deps/pathLeaf.js.map +1 -1
  97. package/dist/canvas/deps/slice.js +2 -1
  98. package/dist/canvas/deps/slice.js.map +1 -1
  99. package/dist/canvas/derivedPath.js +14 -6
  100. package/dist/canvas/derivedPath.js.map +1 -1
  101. package/dist/canvas/doubleClickTarget.js +9 -11
  102. package/dist/canvas/doubleClickTarget.js.map +1 -1
  103. package/dist/canvas/drawnBounds.js +26 -9
  104. package/dist/canvas/drawnBounds.js.map +1 -1
  105. package/dist/canvas/inheritedPaints.js +55 -0
  106. package/dist/canvas/inheritedPaints.js.map +1 -0
  107. package/dist/canvas/nodeShapes/derivedPainter.js +15 -16
  108. package/dist/canvas/nodeShapes/derivedPainter.js.map +1 -1
  109. package/dist/canvas/nodeShapes/nodePaint.js +7 -34
  110. package/dist/canvas/nodeShapes/nodePaint.js.map +1 -1
  111. package/dist/canvas/nodeShapes/pathPainter.js +7 -7
  112. package/dist/canvas/nodeShapes/pathPainter.js.map +1 -1
  113. package/dist/canvas/nodeShapes/rectFallbackPainter.js +4 -5
  114. package/dist/canvas/nodeShapes/rectFallbackPainter.js.map +1 -1
  115. package/dist/canvas/nodeShapes/shapePainter.js +9 -9
  116. package/dist/canvas/nodeShapes/shapePainter.js.map +1 -1
  117. package/dist/canvas/nodeShapes/textPainter.js +13 -12
  118. package/dist/canvas/nodeShapes/textPainter.js.map +1 -1
  119. package/dist/canvas/ownClip.js +24 -0
  120. package/dist/canvas/ownClip.js.map +1 -0
  121. package/dist/canvas/paintCull.js.map +1 -1
  122. package/dist/canvas/pickIndex/nodeBroadBox.js +86 -0
  123. package/dist/canvas/pickIndex/nodeBroadBox.js.map +1 -0
  124. package/dist/canvas/pickIndex/scenePickIndex.js +208 -0
  125. package/dist/canvas/pickIndex/scenePickIndex.js.map +1 -0
  126. package/dist/canvas/pickReach.js +3 -3
  127. package/dist/canvas/pickReach.js.map +1 -1
  128. package/dist/canvas/pickWalk.js +17 -91
  129. package/dist/canvas/pickWalk.js.map +1 -1
  130. package/dist/canvas/poseCascade.js +1 -1
  131. package/dist/canvas/poseCascade.js.map +1 -1
  132. package/dist/canvas/publishPreviews.js +97 -0
  133. package/dist/canvas/publishPreviews.js.map +1 -0
  134. package/dist/canvas/renderSceneToPixels.js +1 -1
  135. package/dist/canvas/renderSceneToPixels.js.map +1 -1
  136. package/dist/canvas/sceneAdapter.js +7 -5
  137. package/dist/canvas/sceneAdapter.js.map +1 -1
  138. package/dist/canvas/sceneClipboard.js +17 -10
  139. package/dist/canvas/sceneClipboard.js.map +1 -1
  140. package/dist/canvas/scenePickReads.js +37 -0
  141. package/dist/canvas/scenePickReads.js.map +1 -0
  142. package/dist/canvas/scenePickSource.js +74 -0
  143. package/dist/canvas/scenePickSource.js.map +1 -0
  144. package/dist/canvas/sceneSlotWiring.js +9 -3
  145. package/dist/canvas/sceneSlotWiring.js.map +1 -1
  146. package/dist/canvas/sceneViewRender.js +9 -4
  147. package/dist/canvas/sceneViewRender.js.map +1 -1
  148. package/dist/canvas/shownNodes.js +15 -0
  149. package/dist/canvas/shownNodes.js.map +1 -0
  150. package/dist/canvas/surfaceContribution.js +12 -8
  151. package/dist/canvas/surfaceContribution.js.map +1 -1
  152. package/dist/canvas/useCanvasView.js +4 -2
  153. package/dist/canvas/useCanvasView.js.map +1 -1
  154. package/dist/canvas/useLayerStack.js +6 -2
  155. package/dist/canvas/useLayerStack.js.map +1 -1
  156. package/dist/canvas/useSceneAdapter.js +5 -3
  157. package/dist/canvas/useSceneAdapter.js.map +1 -1
  158. package/dist/canvas/useViewAim.js +121 -0
  159. package/dist/canvas/useViewAim.js.map +1 -0
  160. package/dist/canvas/useViewNesting.js +3 -2
  161. package/dist/canvas/useViewNesting.js.map +1 -1
  162. package/dist/canvas/useViewRegistrySurface.js +4 -2
  163. package/dist/canvas/useViewRegistrySurface.js.map +1 -1
  164. package/dist/canvas/viewMarkers.js +63 -0
  165. package/dist/canvas/viewMarkers.js.map +1 -0
  166. package/dist/canvas/viewRegistry.js +27 -1
  167. package/dist/canvas/viewRegistry.js.map +1 -1
  168. package/dist/clipboard.d.ts +2 -2
  169. package/dist/{clone-C3e0OTuF.d.ts → clone-qb2e1F5p.d.ts} +2 -2
  170. package/dist/clone.d.ts +3 -3
  171. package/dist/core/isPlainObject.js +2 -2
  172. package/dist/core/layers/layerGroup.js.map +1 -1
  173. package/dist/core/pages/pageEdits.js +2 -3
  174. package/dist/core/pages/pageEdits.js.map +1 -1
  175. package/dist/core/paint/meshKindLazy.js +59 -2
  176. package/dist/core/paint/meshKindLazy.js.map +1 -1
  177. package/dist/core/scene/NodeRouting.js.map +1 -1
  178. package/dist/core/scene/arrivalWindow.js +1 -1
  179. package/dist/core/scene/arrivalWindow.js.map +1 -1
  180. package/dist/core/scene/clipNothing.js +10 -0
  181. package/dist/core/scene/clipNothing.js.map +1 -0
  182. package/dist/core/scene/declaredFieldSetters.js +44 -0
  183. package/dist/core/scene/declaredFieldSetters.js.map +1 -0
  184. package/dist/core/scene/depMemo.js +14 -3
  185. package/dist/core/scene/depMemo.js.map +1 -1
  186. package/dist/core/scene/dependents.js +9 -1
  187. package/dist/core/scene/dependents.js.map +1 -1
  188. package/dist/core/scene/derivedParts.js +10 -11
  189. package/dist/core/scene/derivedParts.js.map +1 -1
  190. package/dist/core/scene/documentOf.js +43 -0
  191. package/dist/core/scene/documentOf.js.map +1 -0
  192. package/dist/core/scene/effectivePose.js +44 -15
  193. package/dist/core/scene/effectivePose.js.map +1 -1
  194. package/dist/core/scene/fieldEdits.js +17 -14
  195. package/dist/core/scene/fieldEdits.js.map +1 -1
  196. package/dist/core/scene/fontReads.js +37 -0
  197. package/dist/core/scene/fontReads.js.map +1 -0
  198. package/dist/core/scene/historyForm.js +4 -4
  199. package/dist/core/scene/historyForm.js.map +1 -1
  200. package/dist/core/scene/inheritedPaint.js +97 -0
  201. package/dist/core/scene/inheritedPaint.js.map +1 -0
  202. package/dist/core/scene/kitOps.js +3 -23
  203. package/dist/core/scene/kitOps.js.map +1 -1
  204. package/dist/core/scene/kitRegistry.js +5 -0
  205. package/dist/core/scene/kitRegistry.js.map +1 -1
  206. package/dist/core/scene/layoutPass.js +1 -2
  207. package/dist/core/scene/layoutPass.js.map +1 -1
  208. package/dist/core/scene/nodeAsShown.js +72 -0
  209. package/dist/core/scene/nodeAsShown.js.map +1 -0
  210. package/dist/core/scene/nodeFnFields.js +2 -10
  211. package/dist/core/scene/nodeFnFields.js.map +1 -1
  212. package/dist/core/scene/nodeMemo.js +21 -8
  213. package/dist/core/scene/nodeMemo.js.map +1 -1
  214. package/dist/core/scene/nodeStore.js +2 -4
  215. package/dist/core/scene/nodeStore.js.map +1 -1
  216. package/dist/core/scene/pathEffects/blend.js.map +1 -1
  217. package/dist/core/scene/pathEffects/derive.js +10 -4
  218. package/dist/core/scene/pathEffects/derive.js.map +1 -1
  219. package/dist/core/scene/pathEffects/effects.js +3 -1
  220. package/dist/core/scene/pathEffects/effects.js.map +1 -1
  221. package/dist/core/scene/pathEffects/foundHidden.js +35 -0
  222. package/dist/core/scene/pathEffects/foundHidden.js.map +1 -0
  223. package/dist/core/scene/pathEffects/textAlong.js +95 -0
  224. package/dist/core/scene/pathEffects/textAlong.js.map +1 -0
  225. package/dist/core/scene/plyStacks.js +89 -0
  226. package/dist/core/scene/plyStacks.js.map +1 -0
  227. package/dist/core/scene/poseHold.js +42 -24
  228. package/dist/core/scene/poseHold.js.map +1 -1
  229. package/dist/core/scene/poseOverrides.js +35 -38
  230. package/dist/core/scene/poseOverrides.js.map +1 -1
  231. package/dist/core/scene/posePlies.js +59 -0
  232. package/dist/core/scene/posePlies.js.map +1 -0
  233. package/dist/core/scene/reconcileSpecs.js +6 -15
  234. package/dist/core/scene/reconcileSpecs.js.map +1 -1
  235. package/dist/core/scene/renderOrder.js +10 -4
  236. package/dist/core/scene/renderOrder.js.map +1 -1
  237. package/dist/core/scene/sameValue.js +17 -0
  238. package/dist/core/scene/sameValue.js.map +1 -0
  239. package/dist/core/scene/scene.js +3 -3
  240. package/dist/core/scene/scene.js.map +1 -1
  241. package/dist/core/scene/sceneEdits.js +8 -0
  242. package/dist/core/scene/sceneEdits.js.map +1 -1
  243. package/dist/core/scene/sceneState.js +1 -2
  244. package/dist/core/scene/sceneState.js.map +1 -1
  245. package/dist/core/scene/serialization.js +45 -9
  246. package/dist/core/scene/serialization.js.map +1 -1
  247. package/dist/core/scene/staleReplay.js +5 -2
  248. package/dist/core/scene/staleReplay.js.map +1 -1
  249. package/dist/core/scene/statedFields.js +4 -2
  250. package/dist/core/scene/statedFields.js.map +1 -1
  251. package/dist/core/scene/treeEdits.js +2 -4
  252. package/dist/core/scene/treeEdits.js.map +1 -1
  253. package/dist/core/scene/treeOrder.js +3 -1
  254. package/dist/core/scene/treeOrder.js.map +1 -1
  255. package/dist/core/scene/treeReads.js +1 -2
  256. package/dist/core/scene/treeReads.js.map +1 -1
  257. package/dist/core/scene/treeShape.js +13 -0
  258. package/dist/core/scene/treeShape.js.map +1 -0
  259. package/dist/core/scene/types.js.map +1 -1
  260. package/dist/core/scene/useScene.js +1 -1
  261. package/dist/core/scene/useScene.js.map +1 -1
  262. package/dist/{cornerHandles-BHuoMs1o.d.ts → cornerHandles-CNCb7aTY.d.ts} +3 -2
  263. package/dist/{depSchema-BpZNOv7q.d.ts → depSchema-B1RS-IJx.d.ts} +39 -18
  264. package/dist/devtools/handlers.js +1 -1
  265. package/dist/devtools/handlers.js.map +1 -1
  266. package/dist/devtools/protocol.js.map +1 -1
  267. package/dist/devtools/registry.js +4 -4
  268. package/dist/devtools/registry.js.map +1 -1
  269. package/dist/devtools-protocol.d.ts +4 -3
  270. package/dist/devtools.d.ts +3 -2
  271. package/dist/features/measure/measureContribution.js +4 -2
  272. package/dist/features/measure/measureContribution.js.map +1 -1
  273. package/dist/features/measure/measureHit.js +5 -3
  274. package/dist/features/measure/measureHit.js.map +1 -1
  275. package/dist/features/measure/measurement.js +1 -1
  276. package/dist/features/measure/measurement.js.map +1 -1
  277. package/dist/features/modes/modeLayers.js +14 -7
  278. package/dist/features/modes/modeLayers.js.map +1 -1
  279. package/dist/features/modes/modeTriggers.js +10 -6
  280. package/dist/features/modes/modeTriggers.js.map +1 -1
  281. package/dist/features/pathEffects/effectSources.js +69 -0
  282. package/dist/features/pathEffects/effectSources.js.map +1 -0
  283. package/dist/features/pathEffects/readSource.js +18 -2
  284. package/dist/features/pathEffects/readSource.js.map +1 -1
  285. package/dist/features/pathEffects/textItems.js +53 -0
  286. package/dist/features/pathEffects/textItems.js.map +1 -0
  287. package/dist/features/pathEffects/useEffectSourcesLayer.js +53 -0
  288. package/dist/features/pathEffects/useEffectSourcesLayer.js.map +1 -0
  289. package/dist/features/paths/editablePath.js +1 -1
  290. package/dist/features/paths/editablePath.js.map +1 -1
  291. package/dist/features/paths/pathEditState.js +5 -2
  292. package/dist/features/paths/pathEditState.js.map +1 -1
  293. package/dist/features/paths/pathSnap/sceneIndex.js +1 -1
  294. package/dist/features/paths/pathSnap/sceneIndex.js.map +1 -1
  295. package/dist/features/selection/enteredGroupFrame.js +65 -0
  296. package/dist/features/selection/enteredGroupFrame.js.map +1 -0
  297. package/dist/features/selection/enteredGroupScope.js +44 -0
  298. package/dist/features/selection/enteredGroupScope.js.map +1 -0
  299. package/dist/features/text/sceneTextDrafts.js +64 -0
  300. package/dist/features/text/sceneTextDrafts.js.map +1 -0
  301. package/dist/features/text/textHitAt.js +34 -3
  302. package/dist/features/text/textHitAt.js.map +1 -1
  303. package/dist/features/text/useSceneTextEdit.js +23 -19
  304. package/dist/features/text/useSceneTextEdit.js.map +1 -1
  305. package/dist/features/viewports/aim.js +105 -0
  306. package/dist/features/viewports/aim.js.map +1 -0
  307. package/dist/features/viewports/viewMarker.js +82 -0
  308. package/dist/features/viewports/viewMarker.js.map +1 -0
  309. package/dist/features/viewports/viewMarkerDrag.js +55 -0
  310. package/dist/features/viewports/viewMarkerDrag.js.map +1 -0
  311. package/dist/index.d.ts +709 -258
  312. package/dist/index.js +33 -12
  313. package/dist/interactions/actions/admitsChild.js +11 -0
  314. package/dist/interactions/actions/admitsChild.js.map +1 -0
  315. package/dist/interactions/actions/booleans/booleans.js +2 -1
  316. package/dist/interactions/actions/booleans/booleans.js.map +1 -1
  317. package/dist/interactions/actions/booleans/operands.js +6 -5
  318. package/dist/interactions/actions/booleans/operands.js.map +1 -1
  319. package/dist/interactions/actions/clipboard/action.js +27 -17
  320. package/dist/interactions/actions/clipboard/action.js.map +1 -1
  321. package/dist/interactions/actions/defaults/anchorEditing.js +44 -23
  322. package/dist/interactions/actions/defaults/anchorEditing.js.map +1 -1
  323. package/dist/interactions/actions/defaults/anchorJoin.js +12 -6
  324. package/dist/interactions/actions/defaults/anchorJoin.js.map +1 -1
  325. package/dist/interactions/actions/defaults/bendSegment.js +7 -4
  326. package/dist/interactions/actions/defaults/bendSegment.js.map +1 -1
  327. package/dist/interactions/actions/defaults/cancelGesture.js +11 -8
  328. package/dist/interactions/actions/defaults/cancelGesture.js.map +1 -1
  329. package/dist/interactions/actions/defaults/contextMenu.js +33 -11
  330. package/dist/interactions/actions/defaults/contextMenu.js.map +1 -1
  331. package/dist/interactions/actions/defaults/createPaintAction.js +5 -2
  332. package/dist/interactions/actions/defaults/createPaintAction.js.map +1 -1
  333. package/dist/interactions/actions/defaults/delete.js +28 -14
  334. package/dist/interactions/actions/defaults/delete.js.map +1 -1
  335. package/dist/interactions/actions/defaults/duplicate.js +11 -8
  336. package/dist/interactions/actions/defaults/duplicate.js.map +1 -1
  337. package/dist/interactions/actions/defaults/enterGroup.js +88 -0
  338. package/dist/interactions/actions/defaults/enterGroup.js.map +1 -0
  339. package/dist/interactions/actions/defaults/enterPathEdit.js +20 -10
  340. package/dist/interactions/actions/defaults/enterPathEdit.js.map +1 -1
  341. package/dist/interactions/actions/defaults/enterTextEdit.js +1 -1
  342. package/dist/interactions/actions/defaults/enterTextEdit.js.map +1 -1
  343. package/dist/interactions/actions/defaults/escape.js +11 -8
  344. package/dist/interactions/actions/defaults/escape.js.map +1 -1
  345. package/dist/interactions/actions/defaults/exitPathEdit.js +7 -4
  346. package/dist/interactions/actions/defaults/exitPathEdit.js.map +1 -1
  347. package/dist/interactions/actions/defaults/group.js +26 -20
  348. package/dist/interactions/actions/defaults/group.js.map +1 -1
  349. package/dist/interactions/actions/defaults/ingest.js +10 -4
  350. package/dist/interactions/actions/defaults/ingest.js.map +1 -1
  351. package/dist/interactions/actions/defaults/insertPathAnchor.js +7 -4
  352. package/dist/interactions/actions/defaults/insertPathAnchor.js.map +1 -1
  353. package/dist/interactions/actions/defaults/nudge.js +8 -2
  354. package/dist/interactions/actions/defaults/nudge.js.map +1 -1
  355. package/dist/interactions/actions/defaults/pathEditTools.js +11 -8
  356. package/dist/interactions/actions/defaults/pathEditTools.js.map +1 -1
  357. package/dist/interactions/actions/defaults/pinchZoom.js +7 -4
  358. package/dist/interactions/actions/defaults/pinchZoom.js.map +1 -1
  359. package/dist/interactions/actions/defaults/reorder.js +24 -6
  360. package/dist/interactions/actions/defaults/reorder.js.map +1 -1
  361. package/dist/interactions/actions/defaults/selectAll.js +15 -8
  362. package/dist/interactions/actions/defaults/selectAll.js.map +1 -1
  363. package/dist/interactions/actions/defaults/selectDeep.js +35 -0
  364. package/dist/interactions/actions/defaults/selectDeep.js.map +1 -0
  365. package/dist/interactions/actions/defaults/setFill.js +7 -3
  366. package/dist/interactions/actions/defaults/setFill.js.map +1 -1
  367. package/dist/interactions/actions/defaults/setFillOpacity.js +1 -1
  368. package/dist/interactions/actions/defaults/setStroke.js +12 -11
  369. package/dist/interactions/actions/defaults/setStroke.js.map +1 -1
  370. package/dist/interactions/actions/defaults/setStrokeOpacity.js +1 -1
  371. package/dist/interactions/actions/defaults/toolActivate.js +7 -1
  372. package/dist/interactions/actions/defaults/toolActivate.js.map +1 -1
  373. package/dist/interactions/actions/defaults/toolResetToDefault.js +7 -4
  374. package/dist/interactions/actions/defaults/toolResetToDefault.js.map +1 -1
  375. package/dist/interactions/actions/defaults/undoRedo.js +19 -13
  376. package/dist/interactions/actions/defaults/undoRedo.js.map +1 -1
  377. package/dist/interactions/actions/defaults/viewportWheelPan.js +16 -7
  378. package/dist/interactions/actions/defaults/viewportWheelPan.js.map +1 -1
  379. package/dist/interactions/actions/defaults/viewportZoom.js +24 -6
  380. package/dist/interactions/actions/defaults/viewportZoom.js.map +1 -1
  381. package/dist/interactions/actions/drawnBoxes.js +2 -1
  382. package/dist/interactions/actions/drawnBoxes.js.map +1 -1
  383. package/dist/interactions/actions/edit-anchors/action.js +7 -4
  384. package/dist/interactions/actions/edit-anchors/action.js.map +1 -1
  385. package/dist/interactions/actions/flip/action.js +8 -2
  386. package/dist/interactions/actions/flip/action.js.map +1 -1
  387. package/dist/interactions/actions/insert/action.js +1 -0
  388. package/dist/interactions/actions/insert/action.js.map +1 -1
  389. package/dist/interactions/actions/insert/parent.js +7 -5
  390. package/dist/interactions/actions/insert/parent.js.map +1 -1
  391. package/dist/interactions/actions/insert/placeAction.js +3 -2
  392. package/dist/interactions/actions/insert/placeAction.js.map +1 -1
  393. package/dist/interactions/actions/insert/repositionAction.js +2 -1
  394. package/dist/interactions/actions/insert/repositionAction.js.map +1 -1
  395. package/dist/interactions/actions/insert/session.js +2 -2
  396. package/dist/interactions/actions/insert/session.js.map +1 -1
  397. package/dist/interactions/actions/move/action.js +7 -4
  398. package/dist/interactions/actions/move/action.js.map +1 -1
  399. package/dist/interactions/actions/move/commit/reparent.js +1 -1
  400. package/dist/interactions/actions/move/commit/reparent.js.map +1 -1
  401. package/dist/interactions/actions/move/gestureAdapter.js +6 -7
  402. package/dist/interactions/actions/move/gestureAdapter.js.map +1 -1
  403. package/dist/interactions/actions/move/layout/exits.js +1 -1
  404. package/dist/interactions/actions/move/layout/exits.js.map +1 -1
  405. package/dist/interactions/actions/move/session.js +4 -4
  406. package/dist/interactions/actions/move/session.js.map +1 -1
  407. package/dist/interactions/actions/outlines/createOutlinesAction.js +8 -5
  408. package/dist/interactions/actions/outlines/createOutlinesAction.js.map +1 -1
  409. package/dist/interactions/actions/outlines/outlineStroke.js +5 -3
  410. package/dist/interactions/actions/outlines/outlineStroke.js.map +1 -1
  411. package/dist/interactions/actions/pathCommands/cutJoin.js +8 -5
  412. package/dist/interactions/actions/pathCommands/cutJoin.js.map +1 -1
  413. package/dist/interactions/actions/pathCommands/pathNodes.js +14 -5
  414. package/dist/interactions/actions/pathCommands/pathNodes.js.map +1 -1
  415. package/dist/interactions/actions/resize/cornerHandles.js +2 -1
  416. package/dist/interactions/actions/resize/cornerHandles.js.map +1 -1
  417. package/dist/interactions/actions/selectionSubtree.js +12 -7
  418. package/dist/interactions/actions/selectionSubtree.js.map +1 -1
  419. package/dist/interactions/actions/transform-again/action.js +8 -5
  420. package/dist/interactions/actions/transform-again/action.js.map +1 -1
  421. package/dist/interactions/gestures/thresholdDrag/thresholdDrag.js +14 -2
  422. package/dist/interactions/gestures/thresholdDrag/thresholdDrag.js.map +1 -1
  423. package/dist/json.d.ts +4 -0
  424. package/dist/json.js +3 -0
  425. package/dist/kitSettingsSchema.js +8 -1
  426. package/dist/kitSettingsSchema.js.map +1 -1
  427. package/dist/{math-CrJULaZI.d.ts → math-C_elShP_.d.ts} +7 -1
  428. package/dist/math.d.ts +1 -1
  429. package/dist/math.js +2 -1
  430. package/dist/move.d.ts +4 -4
  431. package/dist/{options-DBxEryYq.d.ts → options-ClEm3SEl.d.ts} +1 -1
  432. package/dist/resize.d.ts +4 -4
  433. package/dist/test-seams.d.ts +6 -6
  434. package/dist/tools/builtin/ellipse/useEllipseTool.js +1 -0
  435. package/dist/tools/builtin/ellipse/useEllipseTool.js.map +1 -1
  436. package/dist/tools/builtin/eyedropper/useEyedropperTool.js +2 -1
  437. package/dist/tools/builtin/eyedropper/useEyedropperTool.js.map +1 -1
  438. package/dist/tools/builtin/hand/dragPanContribution.js +4 -1
  439. package/dist/tools/builtin/hand/dragPanContribution.js.map +1 -1
  440. package/dist/tools/builtin/hand/useHandTool.js +7 -4
  441. package/dist/tools/builtin/hand/useHandTool.js.map +1 -1
  442. package/dist/tools/builtin/image/useImageTool.js +1 -0
  443. package/dist/tools/builtin/image/useImageTool.js.map +1 -1
  444. package/dist/tools/builtin/lasso/useLassoTool.js +1 -0
  445. package/dist/tools/builtin/lasso/useLassoTool.js.map +1 -1
  446. package/dist/tools/builtin/line/useLineTool.js +1 -0
  447. package/dist/tools/builtin/line/useLineTool.js.map +1 -1
  448. package/dist/tools/builtin/measure/useMeasureTool.js +12 -5
  449. package/dist/tools/builtin/measure/useMeasureTool.js.map +1 -1
  450. package/dist/tools/builtin/pen/usePenTool.js +14 -6
  451. package/dist/tools/builtin/pen/usePenTool.js.map +1 -1
  452. package/dist/tools/builtin/pencil/usePencilTool.js +1 -0
  453. package/dist/tools/builtin/pencil/usePencilTool.js.map +1 -1
  454. package/dist/tools/builtin/polygon/usePolygonTool.js +13 -4
  455. package/dist/tools/builtin/polygon/usePolygonTool.js.map +1 -1
  456. package/dist/tools/builtin/pressTarget.js +22 -0
  457. package/dist/tools/builtin/pressTarget.js.map +1 -0
  458. package/dist/tools/builtin/rect/useRectTool.js +1 -0
  459. package/dist/tools/builtin/rect/useRectTool.js.map +1 -1
  460. package/dist/tools/builtin/select/adapterPointQuery.js +6 -5
  461. package/dist/tools/builtin/select/adapterPointQuery.js.map +1 -1
  462. package/dist/tools/builtin/select/collapseDeferred.js +40 -0
  463. package/dist/tools/builtin/select/collapseDeferred.js.map +1 -0
  464. package/dist/tools/builtin/select/groupEntry.js +82 -0
  465. package/dist/tools/builtin/select/groupEntry.js.map +1 -0
  466. package/dist/tools/builtin/select/pressDecision.js +32 -0
  467. package/dist/tools/builtin/select/pressDecision.js.map +1 -0
  468. package/dist/tools/builtin/select/selectionContributions.js +22 -13
  469. package/dist/tools/builtin/select/selectionContributions.js.map +1 -1
  470. package/dist/tools/builtin/select/useSelectTool.js +74 -55
  471. package/dist/tools/builtin/select/useSelectTool.js.map +1 -1
  472. package/dist/tools/builtin/slice/useSliceTool.js +22 -8
  473. package/dist/tools/builtin/slice/useSliceTool.js.map +1 -1
  474. package/dist/tools/builtin/star/useStarTool.js +13 -4
  475. package/dist/tools/builtin/star/useStarTool.js.map +1 -1
  476. package/dist/tools/builtin/sticky/stickyNode.js +4 -1
  477. package/dist/tools/builtin/sticky/stickyNode.js.map +1 -1
  478. package/dist/tools/builtin/sticky/useStickyTool.js +10 -5
  479. package/dist/tools/builtin/sticky/useStickyTool.js.map +1 -1
  480. package/dist/tools/builtin/text/useTextTool.js +3 -1
  481. package/dist/tools/builtin/text/useTextTool.js.map +1 -1
  482. package/dist/{types-7puAuVD2.d.ts → types-49uYJN3j.d.ts} +1 -1
  483. package/dist/{types-CniVctmk.d.ts → types-BEdRIhNc.d.ts} +272 -109
  484. package/dist/{types-oFeQ5VjV.d.ts → types-p2alu-R1.d.ts} +1 -1
  485. package/dist/util/paint.js +1 -3
  486. package/dist/util/paint.js.map +1 -1
  487. package/dist/version.js +1 -1
  488. package/package.json +29 -25
@@ -1 +1 @@
1
- {"version":3,"file":"pathEditCommits.js","names":[],"sources":["../../../src/canvas/deps/pathEditCommits.ts"],"sourcesContent":["/**\n * What the `editAnchors` dep commits: an edit finished by every path-edit\n * extension, the other nodes the extensions change with it, and the same for\n * a whole-node transform's batch.\n */\nimport type { PointOrigin, PolygonPath } from '@weasel-js/geom';\nimport type { Op } from 'core/ops/types';\nimport { createTransformOp } from 'core/ops/transform';\nimport { createSetDataOp } from 'core/ops/setData';\nimport { createDeleteOp } from 'core/ops/delete';\nimport { writtenBy } from 'core/ops/writtenBy';\nimport type { TransformFollower, TransformPreviewNode } from 'interactions/actions/transformFinish';\nimport type { NodeId, Scene } from 'core/scene/types';\nimport { pathEditPathOf, storeEditablePath } from 'features/paths/editablePath';\nimport { withoutShapeGenerator } from 'features/paths/shapeGenerator';\nimport {\n withoutGeometryBoundData, type PathEditExtension, type PathEditFinishCtx, type PathEditOther, type PathTransformFinishCtx,\n} from 'features/paths/pathEditExtension';\n\ntype AnyScene = Scene<unknown, string, unknown>;\nexport type EditableNode = { pose: unknown; data: unknown };\n\n/** What {@link finishEdit} answers: the edited path, the data set beside it, and the other nodes it changes, by id. */\nexport interface Finished {\n path: PolygonPath;\n data: Record<string, unknown> | null;\n others: Map<string, PathEditOther>;\n}\n\n/** The nodes that list `id` in their `dependsOn`, directly or through others. */\nfunction dependentsOf(sc: AnyScene, id: string): readonly string[] {\n if (!sc.get(id as NodeId)) return [];\n return sc.removalClosure([id as NodeId]).filter((d) => d !== id && !sc.ancestorsOf(d).includes(id as NodeId));\n}\n\n/** The scene as an extension's `finishEdit` reads it. */\nexport function finishCtx(sc: AnyScene, id: string, extensions: readonly PathEditExtension[]): PathEditFinishCtx {\n return {\n id,\n node(nid) {\n const node = sc.get(nid as NodeId);\n return node ? { pose: node.pose, data: node.data, dependsOn: node.dependsOn } : null;\n },\n path: (nid) => pathEditPathOf(sc.get(nid as NodeId) as EditableNode | undefined, extensions),\n dependents: (nid) => dependentsOf(sc, nid),\n };\n}\n\n/** `o` laid over what `others` already holds for its node. */\nfunction addOther(others: Map<string, PathEditOther>, o: PathEditOther): void {\n const had = others.get(o.id);\n others.set(o.id, {\n id: o.id,\n path: o.path ?? had?.path,\n data: o.data || had?.data ? { ...had?.data, ...o.data } : undefined,\n remove: o.remove || had?.remove,\n });\n}\n\n/** `worldPath` finished by each of `extensions` in turn, the data they set\n * beside it, and the other nodes they change; null when one cannot finish it. */\nexport function finishEdit(\n extensions: readonly PathEditExtension[],\n node: EditableNode,\n worldPath: PolygonPath,\n origins: readonly (PointOrigin | null)[] | undefined,\n ctx: PathEditFinishCtx,\n): Finished | null {\n let path = worldPath;\n let data: Record<string, unknown> | null = null;\n const others = new Map<string, PathEditOther>();\n for (const ext of extensions) {\n if (!ext.finishEdit) continue;\n const done = ext.finishEdit(node, path, origins, ctx);\n if (!done) return null;\n path = done.path;\n if (done.data) data = { ...(data ?? {}), ...done.data };\n for (const o of done.others ?? []) addOther(others, o);\n }\n return { path, data, others };\n}\n\n/** The ops that commit `others`, each from its node as committed. */\nexport function otherOps(\n sc: AnyScene,\n others: ReadonlyMap<string, PathEditOther>,\n label: string,\n extensions: readonly PathEditExtension[],\n): Op[] {\n return [...others.values()].flatMap((o) => {\n const node = sc.get(o.id as NodeId) as EditableNode | undefined;\n if (!node) return [];\n if (o.remove) return [createDeleteOp({ node: sc.get(o.id as NodeId)!, label })];\n if (o.path) return reshapeOps(o.id, node, o.path, label, o.data ?? null, extensions);\n return o.data ? [createSetDataOp({ id: o.id, from: node.data, to: { ...(node.data as object), ...o.data }, label })] : [];\n });\n}\n\n/** The ops that store `worldPath` where `node` keeps its polygon, with `patch` laid over its data. */\nexport function reshapeOps(\n id: string,\n node: EditableNode,\n worldPath: PolygonPath,\n label: string,\n patch: Record<string, unknown> | null,\n extensions: readonly PathEditExtension[],\n): Op[] {\n const next = storeEditablePath(node, worldPath, extensions);\n if (!next) return [];\n const reshape = createTransformOp({ id, from: node.pose, to: next.pose, label });\n // A path edited by hand is no longer what its generator or warp makes: it becomes plain geometry.\n // Its skin and constraints stay, renumbered by the caller's patch where it has one, and the\n // form it was drawn from as storing it left it.\n const data = withoutGeometryBoundData(\n next.data === node.data ? node.data : withoutShapeGenerator(next.data), ['skin', 'constraints', 'representation'],\n );\n if (data === node.data && !patch) return [reshape];\n return [reshape, createSetDataOp({ id, from: node.data, to: { ...(data as object), ...patch }, label })];\n}\n\n/** The other nodes `extensions` change along with a transform that leaves the nodes in `after` as given; null when one refuses it. */\nfunction transformOthers(\n sc: AnyScene,\n extensions: readonly PathEditExtension[],\n after: ReadonlyMap<string, EditableNode>,\n): Map<string, PathEditOther> | null {\n const others = new Map<string, PathEditOther>();\n const finishing = extensions.filter((e) => e.finishTransform);\n if (finishing.length === 0 || after.size === 0) return others;\n const nodeAfter = (id: string) => {\n const node = sc.get(id as NodeId);\n if (!node) return null;\n const written = after.get(id);\n return { pose: written?.pose ?? node.pose, data: written?.data ?? node.data, dependsOn: node.dependsOn };\n };\n const ctx: PathTransformFinishCtx = {\n ids: [...after.keys()],\n node: nodeAfter,\n path: (id) => pathEditPathOf(nodeAfter(id) ?? undefined, extensions),\n before: (id) => pathEditPathOf(sc.get(id as NodeId) as EditableNode | undefined, extensions),\n dependents: (id) => dependentsOf(sc, id),\n };\n for (const ext of finishing) {\n const done = ext.finishTransform!(ctx);\n if (!done) return null;\n for (const o of done) if (!after.has(o.id)) addOther(others, o);\n }\n return others;\n}\n\n/** `ops`, a whole-node transform's batch, with the ops `extensions` add for the other nodes it changes; null when one refuses it. */\nexport function transformOps(\n sc: AnyScene,\n extensions: readonly PathEditExtension[],\n ops: readonly Op[],\n label: string,\n): Op[] | null {\n const after = extensions.some((e) => e.finishTransform) ? writtenBy(sc, ops) : null;\n if (!after || after.size === 0) return [...ops];\n const others = transformOthers(sc, extensions, after);\n return others && [...ops, ...otherOps(sc, others, label, extensions)];\n}\n\n/**\n * The other nodes `extensions` change along with a transform frame that\n * poses the nodes in `poses` as given, each as it would be stored — what\n * {@link transformOps} would commit for that frame, read the same way. Empty\n * when nothing changes with it, or an extension refuses it.\n */\nexport function transformPreview(\n sc: AnyScene,\n extensions: readonly PathEditExtension[],\n poses: ReadonlyMap<string, unknown>,\n): TransformPreviewNode[] {\n if (poses.size === 0 || !extensions.some((e) => e.finishTransform)) return [];\n const after = new Map<string, EditableNode>();\n for (const [id, pose] of poses) {\n const node = sc.get(id as NodeId);\n if (node) after.set(id, { pose, data: node.data });\n }\n const others = transformOthers(sc, extensions, after);\n return [...(others?.values() ?? [])].flatMap((o) => {\n const node = sc.get(o.id as NodeId) as EditableNode | undefined;\n const stored = node && o.path && !o.remove ? storeEditablePath(node, o.path, extensions) : null;\n if (!node || !stored) return [];\n return [stored.data === node.data ? { id: o.id, pose: stored.pose } : { id: o.id, pose: stored.pose, data: stored.data }];\n });\n}\n\n/** The transform follower `extensions` make: each one's `finishTransform`, as\n * {@link transformOps} and {@link transformPreview} read it. Null when none\n * has one, so a transform runs no follower for them. */\nexport function pathEditFollower(sc: AnyScene, extensions: readonly PathEditExtension[]): TransformFollower | null {\n if (!extensions.some((e) => e.finishTransform)) return null;\n return {\n finish: (ops, label) => transformOps(sc, extensions, ops, label),\n preview: (poses) => transformPreview(sc, extensions, poses),\n };\n}\n"],"mappings":";;;;;;;;;;AA8BA,SAAS,aAAa,IAAc,IAA+B;CACjE,IAAI,CAAC,GAAG,IAAI,EAAY,GAAG,OAAO,CAAC;CACnC,OAAO,GAAG,eAAe,CAAC,EAAY,CAAC,CAAC,CAAC,QAAQ,MAAM,MAAM,MAAM,CAAC,GAAG,YAAY,CAAC,CAAC,CAAC,SAAS,EAAY,CAAC;AAC9G;;AAGA,SAAgB,UAAU,IAAc,IAAY,YAA6D;CAC/G,OAAO;EACL;EACA,KAAK,KAAK;GACR,MAAM,OAAO,GAAG,IAAI,GAAa;GACjC,OAAO,OAAO;IAAE,MAAM,KAAK;IAAM,MAAM,KAAK;IAAM,WAAW,KAAK;GAAU,IAAI;EAClF;EACA,OAAO,QAAQ,eAAe,GAAG,IAAI,GAAa,GAA+B,UAAU;EAC3F,aAAa,QAAQ,aAAa,IAAI,GAAG;CAC3C;AACF;;AAGA,SAAS,SAAS,QAAoC,GAAwB;CAC5E,MAAM,MAAM,OAAO,IAAI,EAAE,EAAE;CAC3B,OAAO,IAAI,EAAE,IAAI;EACf,IAAI,EAAE;EACN,MAAM,EAAE,QAAQ,KAAK;EACrB,MAAM,EAAE,QAAQ,KAAK,OAAO;GAAE,GAAG,KAAK;GAAM,GAAG,EAAE;EAAK,IAAI;EAC1D,QAAQ,EAAE,UAAU,KAAK;CAC3B,CAAC;AACH;;;AAIA,SAAgB,WACd,YACA,MACA,WACA,SACA,KACiB;CACjB,IAAI,OAAO;CACX,IAAI,OAAuC;CAC3C,MAAM,yBAAS,IAAI,IAA2B;CAC9C,KAAK,MAAM,OAAO,YAAY;EAC5B,IAAI,CAAC,IAAI,YAAY;EACrB,MAAM,OAAO,IAAI,WAAW,MAAM,MAAM,SAAS,GAAG;EACpD,IAAI,CAAC,MAAM,OAAO;EAClB,OAAO,KAAK;EACZ,IAAI,KAAK,MAAM,OAAO;GAAE,GAAI,QAAQ,CAAC;GAAI,GAAG,KAAK;EAAK;EACtD,KAAK,MAAM,KAAK,KAAK,UAAU,CAAC,GAAG,SAAS,QAAQ,CAAC;CACvD;CACA,OAAO;EAAE;EAAM;EAAM;CAAO;AAC9B;;AAGA,SAAgB,SACd,IACA,QACA,OACA,YACM;CACN,OAAO,CAAC,GAAG,OAAO,OAAO,CAAC,CAAC,CAAC,SAAS,MAAM;EACzC,MAAM,OAAO,GAAG,IAAI,EAAE,EAAY;EAClC,IAAI,CAAC,MAAM,OAAO,CAAC;EACnB,IAAI,EAAE,QAAQ,OAAO,CAAC,eAAe;GAAE,MAAM,GAAG,IAAI,EAAE,EAAY;GAAI;EAAM,CAAC,CAAC;EAC9E,IAAI,EAAE,MAAM,OAAO,WAAW,EAAE,IAAI,MAAM,EAAE,MAAM,OAAO,EAAE,QAAQ,MAAM,UAAU;EACnF,OAAO,EAAE,OAAO,CAAC,gBAAgB;GAAE,IAAI,EAAE;GAAI,MAAM,KAAK;GAAM,IAAI;IAAE,GAAI,KAAK;IAAiB,GAAG,EAAE;GAAK;GAAG;EAAM,CAAC,CAAC,IAAI,CAAC;CAC1H,CAAC;AACH;;AAGA,SAAgB,WACd,IACA,MACA,WACA,OACA,OACA,YACM;CACN,MAAM,OAAO,kBAAkB,MAAM,WAAW,UAAU;CAC1D,IAAI,CAAC,MAAM,OAAO,CAAC;CACnB,MAAM,UAAU,kBAAkB;EAAE;EAAI,MAAM,KAAK;EAAM,IAAI,KAAK;EAAM;CAAM,CAAC;CAI/E,MAAM,OAAO,yBACX,KAAK,SAAS,KAAK,OAAO,KAAK,OAAO,sBAAsB,KAAK,IAAI,GAAG;EAAC;EAAQ;EAAe;CAAgB,CAClH;CACA,IAAI,SAAS,KAAK,QAAQ,CAAC,OAAO,OAAO,CAAC,OAAO;CACjD,OAAO,CAAC,SAAS,gBAAgB;EAAE;EAAI,MAAM,KAAK;EAAM,IAAI;GAAE,GAAI;GAAiB,GAAG;EAAM;EAAG;CAAM,CAAC,CAAC;AACzG;;AAGA,SAAS,gBACP,IACA,YACA,OACmC;CACnC,MAAM,yBAAS,IAAI,IAA2B;CAC9C,MAAM,YAAY,WAAW,QAAQ,MAAM,EAAE,eAAe;CAC5D,IAAI,UAAU,WAAW,KAAK,MAAM,SAAS,GAAG,OAAO;CACvD,MAAM,aAAa,OAAe;EAChC,MAAM,OAAO,GAAG,IAAI,EAAY;EAChC,IAAI,CAAC,MAAM,OAAO;EAClB,MAAM,UAAU,MAAM,IAAI,EAAE;EAC5B,OAAO;GAAE,MAAM,SAAS,QAAQ,KAAK;GAAM,MAAM,SAAS,QAAQ,KAAK;GAAM,WAAW,KAAK;EAAU;CACzG;CACA,MAAM,MAA8B;EAClC,KAAK,CAAC,GAAG,MAAM,KAAK,CAAC;EACrB,MAAM;EACN,OAAO,OAAO,eAAe,UAAU,EAAE,KAAK,QAAW,UAAU;EACnE,SAAS,OAAO,eAAe,GAAG,IAAI,EAAY,GAA+B,UAAU;EAC3F,aAAa,OAAO,aAAa,IAAI,EAAE;CACzC;CACA,KAAK,MAAM,OAAO,WAAW;EAC3B,MAAM,OAAO,IAAI,gBAAiB,GAAG;EACrC,IAAI,CAAC,MAAM,OAAO;EAClB,KAAK,MAAM,KAAK,MAAM,IAAI,CAAC,MAAM,IAAI,EAAE,EAAE,GAAG,SAAS,QAAQ,CAAC;CAChE;CACA,OAAO;AACT;;AAGA,SAAgB,aACd,IACA,YACA,KACA,OACa;CACb,MAAM,QAAQ,WAAW,MAAM,MAAM,EAAE,eAAe,IAAI,UAAU,IAAI,GAAG,IAAI;CAC/E,IAAI,CAAC,SAAS,MAAM,SAAS,GAAG,OAAO,CAAC,GAAG,GAAG;CAC9C,MAAM,SAAS,gBAAgB,IAAI,YAAY,KAAK;CACpD,OAAO,UAAU,CAAC,GAAG,KAAK,GAAG,SAAS,IAAI,QAAQ,OAAO,UAAU,CAAC;AACtE;;;;;;;AAQA,SAAgB,iBACd,IACA,YACA,OACwB;CACxB,IAAI,MAAM,SAAS,KAAK,CAAC,WAAW,MAAM,MAAM,EAAE,eAAe,GAAG,OAAO,CAAC;CAC5E,MAAM,wBAAQ,IAAI,IAA0B;CAC5C,KAAK,MAAM,CAAC,IAAI,SAAS,OAAO;EAC9B,MAAM,OAAO,GAAG,IAAI,EAAY;EAChC,IAAI,MAAM,MAAM,IAAI,IAAI;GAAE;GAAM,MAAM,KAAK;EAAK,CAAC;CACnD;CAEA,OAAO,CAAC,GADO,gBAAgB,IAAI,YAAY,KACnC,CAAA,EAAQ,OAAO,KAAK,CAAC,CAAE,CAAC,CAAC,SAAS,MAAM;EAClD,MAAM,OAAO,GAAG,IAAI,EAAE,EAAY;EAClC,MAAM,SAAS,QAAQ,EAAE,QAAQ,CAAC,EAAE,SAAS,kBAAkB,MAAM,EAAE,MAAM,UAAU,IAAI;EAC3F,IAAI,CAAC,QAAQ,CAAC,QAAQ,OAAO,CAAC;EAC9B,OAAO,CAAC,OAAO,SAAS,KAAK,OAAO;GAAE,IAAI,EAAE;GAAI,MAAM,OAAO;EAAK,IAAI;GAAE,IAAI,EAAE;GAAI,MAAM,OAAO;GAAM,MAAM,OAAO;EAAK,CAAC;CAC1H,CAAC;AACH;;;;AAKA,SAAgB,iBAAiB,IAAc,YAAoE;CACjH,IAAI,CAAC,WAAW,MAAM,MAAM,EAAE,eAAe,GAAG,OAAO;CACvD,OAAO;EACL,SAAS,KAAK,UAAU,aAAa,IAAI,YAAY,KAAK,KAAK;EAC/D,UAAU,UAAU,iBAAiB,IAAI,YAAY,KAAK;CAC5D;AACF"}
1
+ {"version":3,"file":"pathEditCommits.js","names":[],"sources":["../../../src/canvas/deps/pathEditCommits.ts"],"sourcesContent":["/**\n * What the `editAnchors` dep commits: an edit finished by every path-edit\n * extension, the other nodes the extensions change with it, and the same for\n * a whole-node transform's batch.\n */\nimport type { PointOrigin, PolygonPath } from '@weasel-js/geom';\nimport type { Op } from 'core/ops/types';\nimport { createTransformOp } from 'core/ops/transform';\nimport { createSetDataOp } from 'core/ops/setData';\nimport { createDeleteOp } from 'core/ops/delete';\nimport { writtenBy } from 'core/ops/writtenBy';\nimport type { TransformFollower, TransformPreviewNode } from 'interactions/actions/transformFinish';\nimport type { NodeId, Scene } from 'core/scene/types';\nimport { sceneDependentsOf } from 'core/scene/dependents';\nimport { pathEditPathOf, storeEditablePath } from 'features/paths/editablePath';\nimport { withoutShapeGenerator } from 'features/paths/shapeGenerator';\nimport {\n withoutGeometryBoundData, type PathEditExtension, type PathEditFinishCtx, type PathEditOther, type PathTransformFinishCtx,\n} from 'features/paths/pathEditExtension';\n\ntype AnyScene = Scene<unknown, string, unknown>;\nexport type EditableNode = { pose: unknown; data: unknown };\n\n/** What {@link finishEdit} answers: the edited path, the data set beside it, and the other nodes it changes, by id. */\nexport interface Finished {\n path: PolygonPath;\n data: Record<string, unknown> | null;\n others: Map<string, PathEditOther>;\n}\n\n/** The scene as an extension's `finishEdit` reads it. */\nexport function finishCtx(sc: AnyScene, id: string, extensions: readonly PathEditExtension[]): PathEditFinishCtx {\n return {\n id,\n node(nid) {\n const node = sc.get(nid as NodeId);\n return node ? { pose: node.pose, data: node.data, dependsOn: node.dependsOn } : null;\n },\n path: (nid) => pathEditPathOf(sc.get(nid as NodeId) as EditableNode | undefined, extensions),\n dependents: (nid) => sceneDependentsOf(sc, nid as NodeId),\n };\n}\n\n/** `o` laid over what `others` already holds for its node. */\nfunction addOther(others: Map<string, PathEditOther>, o: PathEditOther): void {\n const had = others.get(o.id);\n others.set(o.id, {\n id: o.id,\n path: o.path ?? had?.path,\n data: o.data || had?.data ? { ...had?.data, ...o.data } : undefined,\n remove: o.remove || had?.remove,\n });\n}\n\n/** `worldPath` finished by each of `extensions` in turn, the data they set\n * beside it, and the other nodes they change; null when one cannot finish it. */\nexport function finishEdit(\n extensions: readonly PathEditExtension[],\n node: EditableNode,\n worldPath: PolygonPath,\n origins: readonly (PointOrigin | null)[] | undefined,\n ctx: PathEditFinishCtx,\n): Finished | null {\n let path = worldPath;\n let data: Record<string, unknown> | null = null;\n const others = new Map<string, PathEditOther>();\n for (const ext of extensions) {\n if (!ext.finishEdit) continue;\n const done = ext.finishEdit(node, path, origins, ctx);\n if (!done) return null;\n path = done.path;\n if (done.data) data = { ...(data ?? {}), ...done.data };\n for (const o of done.others ?? []) addOther(others, o);\n }\n return { path, data, others };\n}\n\n/** The ops that commit `others`, each from its node as committed. */\nexport function otherOps(\n sc: AnyScene,\n others: ReadonlyMap<string, PathEditOther>,\n label: string,\n extensions: readonly PathEditExtension[],\n): Op[] {\n return [...others.values()].flatMap((o) => {\n const node = sc.get(o.id as NodeId) as EditableNode | undefined;\n if (!node) return [];\n if (o.remove) return [createDeleteOp({ node: sc.get(o.id as NodeId)!, label })];\n if (o.path) return reshapeOps(o.id, node, o.path, label, o.data ?? null, extensions);\n return o.data ? [createSetDataOp({ id: o.id, from: node.data, to: { ...(node.data as object), ...o.data }, label })] : [];\n });\n}\n\n/** The ops that store `worldPath` where `node` keeps its polygon, with `patch` laid over its data. */\nexport function reshapeOps(\n id: string,\n node: EditableNode,\n worldPath: PolygonPath,\n label: string,\n patch: Record<string, unknown> | null,\n extensions: readonly PathEditExtension[],\n): Op[] {\n const next = storeEditablePath(node, worldPath, extensions);\n if (!next) return [];\n const reshape = createTransformOp({ id, from: node.pose, to: next.pose, label });\n // A path edited by hand is no longer what its generator or warp makes: it becomes plain geometry.\n // Its skin and constraints stay, renumbered by the caller's patch where it has one, and the\n // form it was drawn from as storing it left it.\n const data = withoutGeometryBoundData(\n next.data === node.data ? node.data : withoutShapeGenerator(next.data), ['skin', 'constraints', 'representation'],\n );\n if (data === node.data && !patch) return [reshape];\n return [reshape, createSetDataOp({ id, from: node.data, to: { ...(data as object), ...patch }, label })];\n}\n\n/** The other nodes `extensions` change along with a transform that leaves the nodes in `after` as given; null when one refuses it. */\nfunction transformOthers(\n sc: AnyScene,\n extensions: readonly PathEditExtension[],\n after: ReadonlyMap<string, EditableNode>,\n): Map<string, PathEditOther> | null {\n const others = new Map<string, PathEditOther>();\n const finishing = extensions.filter((e) => e.finishTransform);\n if (finishing.length === 0 || after.size === 0) return others;\n const nodeAfter = (id: string) => {\n const node = sc.get(id as NodeId);\n if (!node) return null;\n const written = after.get(id);\n return { pose: written?.pose ?? node.pose, data: written?.data ?? node.data, dependsOn: node.dependsOn };\n };\n const ctx: PathTransformFinishCtx = {\n ids: [...after.keys()],\n node: nodeAfter,\n path: (id) => pathEditPathOf(nodeAfter(id) ?? undefined, extensions),\n before: (id) => pathEditPathOf(sc.get(id as NodeId) as EditableNode | undefined, extensions),\n dependents: (id) => sceneDependentsOf(sc, id as NodeId),\n };\n for (const ext of finishing) {\n const done = ext.finishTransform!(ctx);\n if (!done) return null;\n for (const o of done) if (!after.has(o.id)) addOther(others, o);\n }\n return others;\n}\n\n/** `ops`, a whole-node transform's batch, with the ops `extensions` add for the other nodes it changes; null when one refuses it. */\nexport function transformOps(\n sc: AnyScene,\n extensions: readonly PathEditExtension[],\n ops: readonly Op[],\n label: string,\n): Op[] | null {\n const after = extensions.some((e) => e.finishTransform) ? writtenBy(sc, ops) : null;\n if (!after || after.size === 0) return [...ops];\n const others = transformOthers(sc, extensions, after);\n return others && [...ops, ...otherOps(sc, others, label, extensions)];\n}\n\n/**\n * The other nodes `extensions` change along with a transform frame that\n * poses the nodes in `poses` as given, each as it would be stored — what\n * {@link transformOps} would commit for that frame, read the same way. Empty\n * when nothing changes with it, or an extension refuses it.\n */\nexport function transformPreview(\n sc: AnyScene,\n extensions: readonly PathEditExtension[],\n poses: ReadonlyMap<string, unknown>,\n): TransformPreviewNode[] {\n if (poses.size === 0 || !extensions.some((e) => e.finishTransform)) return [];\n const after = new Map<string, EditableNode>();\n for (const [id, pose] of poses) {\n const node = sc.get(id as NodeId);\n if (node) after.set(id, { pose, data: node.data });\n }\n const others = transformOthers(sc, extensions, after);\n return [...(others?.values() ?? [])].flatMap((o) => {\n const node = sc.get(o.id as NodeId) as EditableNode | undefined;\n const stored = node && o.path && !o.remove ? storeEditablePath(node, o.path, extensions) : null;\n if (!node || !stored) return [];\n return [stored.data === node.data ? { id: o.id, pose: stored.pose } : { id: o.id, pose: stored.pose, data: stored.data }];\n });\n}\n\n/** The transform follower `extensions` make: each one's `finishTransform`, as\n * {@link transformOps} and {@link transformPreview} read it. Null when none\n * has one, so a transform runs no follower for them. */\nexport function pathEditFollower(sc: AnyScene, extensions: readonly PathEditExtension[]): TransformFollower | null {\n if (!extensions.some((e) => e.finishTransform)) return null;\n return {\n finish: (ops, label) => transformOps(sc, extensions, ops, label),\n preview: (poses) => transformPreview(sc, extensions, poses),\n };\n}\n"],"mappings":"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"}
@@ -55,7 +55,7 @@ function storePiece({ world, g, box }, piece) {
55
55
  /** The path a leaf stores, or null for one that is not a free path leaf: a
56
56
  * group, a locked leaf, one whose geometry derives from other nodes. */
57
57
  function leafPath(scene, node) {
58
- if (node.kind !== "leaf" || node.derivePath || node.derivePose) return null;
58
+ if (node.kind !== "mark" || node.derivePath || node.derivePose) return null;
59
59
  if (scene.isLocked(node.id)) return null;
60
60
  const path = node.data?.path;
61
61
  return path && (path.kind === "rect" || path.kind === "polygon") ? path : null;
@@ -1 +1 @@
1
- {"version":3,"file":"pathLeaf.js","names":[],"sources":["../../../src/canvas/deps/pathLeaf.ts"],"sourcesContent":["/**\n * Path leaves as the kit's own path deps read and write them: geometry in\n * `data.path`, drawn through the box its pose describes.\n */\nimport type { Path } from '@weasel-js/geom';\nimport { poseLinearOf, worldEditToStorage, type PathInWorldPose } from '@weasel-js/geom';\nimport type { PoseDescriptor } from 'core/geometry/poseDescriptor';\nimport type { Node, Scene } from 'core/scene/types';\n\n/** A path leaf's world pose, and the box, rotation, and transform its path is drawn through. */\nexport interface DrawnPose {\n world: unknown;\n g: PoseDescriptor<unknown>;\n box: PathInWorldPose;\n}\n\n/** How `g` draws a leaf at `world`, or null for a pose it reads no finite box from. */\nexport function drawnPose(g: PoseDescriptor<unknown>, world: unknown): DrawnPose | null {\n const b = g.getBounds(world);\n if (!b || ![b.x, b.y, b.width, b.height].every(Number.isFinite)) return null;\n const rotation = g.getRotation?.(world) ?? 0;\n const transform = poseLinearOf(world) ?? undefined;\n return { world, g, box: { x: b.x, y: b.y, width: b.width, height: b.height, rotation, transform } };\n}\n\n/** A world-space piece as a world pose of the source's kind plus the path it draws. */\nexport function storePiece({ world, g, box }: DrawnPose, piece: Path): { pose: unknown; path: Path } {\n const keepsRotation = !!g.withRotation && g.supportsRotation?.(world) !== false;\n const rotation = box.rotation ?? 0;\n if (rotation !== 0 && !keepsRotation) {\n const baked = worldEditToStorage({ ...box, rotation: 0 }, piece);\n return { pose: g.fromBounds(baked.pose, world), path: baked.path };\n }\n const { pose: b, path } = worldEditToStorage(box, piece);\n const dst = { x: b.x, y: b.y, width: b.width, height: b.height };\n const pose = g.remapBounds(world, g.getBounds(world), dst);\n return { pose: rotation !== 0 ? g.withRotation!(pose, rotation) : pose, path };\n}\n\n/** The path a leaf stores, or null for one that is not a free path leaf: a\n * group, a locked leaf, one whose geometry derives from other nodes. */\nexport function leafPath(scene: Scene<unknown, string, unknown>, node: Node<unknown, string, unknown>): Path | null {\n if (node.kind !== 'leaf' || node.derivePath || node.derivePose) return null;\n if (scene.isLocked(node.id)) return null;\n const path = (node.data as { path?: unknown } | null)?.path as Path | undefined;\n return path && (path.kind === 'rect' || path.kind === 'polygon') ? path : null;\n}\n"],"mappings":";;;;AAiBA,SAAgB,UAAU,GAA4B,OAAkC;CACtF,MAAM,IAAI,EAAE,UAAU,KAAK;CAC3B,IAAI,CAAC,KAAK,CAAC;EAAC,EAAE;EAAG,EAAE;EAAG,EAAE;EAAO,EAAE;CAAM,CAAC,CAAC,MAAM,OAAO,QAAQ,GAAG,OAAO;CACxE,MAAM,WAAW,EAAE,cAAc,KAAK,KAAK;CAC3C,MAAM,YAAY,aAAa,KAAK,KAAK;CACzC,OAAO;EAAE;EAAO;EAAG,KAAK;GAAE,GAAG,EAAE;GAAG,GAAG,EAAE;GAAG,OAAO,EAAE;GAAO,QAAQ,EAAE;GAAQ;GAAU;EAAU;CAAE;AACpG;;AAGA,SAAgB,WAAW,EAAE,OAAO,GAAG,OAAkB,OAA4C;CACnG,MAAM,gBAAgB,CAAC,CAAC,EAAE,gBAAgB,EAAE,mBAAmB,KAAK,MAAM;CAC1E,MAAM,WAAW,IAAI,YAAY;CACjC,IAAI,aAAa,KAAK,CAAC,eAAe;EACpC,MAAM,QAAQ,mBAAmB;GAAE,GAAG;GAAK,UAAU;EAAE,GAAG,KAAK;EAC/D,OAAO;GAAE,MAAM,EAAE,WAAW,MAAM,MAAM,KAAK;GAAG,MAAM,MAAM;EAAK;CACnE;CACA,MAAM,EAAE,MAAM,GAAG,SAAS,mBAAmB,KAAK,KAAK;CACvD,MAAM,MAAM;EAAE,GAAG,EAAE;EAAG,GAAG,EAAE;EAAG,OAAO,EAAE;EAAO,QAAQ,EAAE;CAAO;CAC/D,MAAM,OAAO,EAAE,YAAY,OAAO,EAAE,UAAU,KAAK,GAAG,GAAG;CACzD,OAAO;EAAE,MAAM,aAAa,IAAI,EAAE,aAAc,MAAM,QAAQ,IAAI;EAAM;CAAK;AAC/E;;;AAIA,SAAgB,SAAS,OAAwC,MAAmD;CAClH,IAAI,KAAK,SAAS,UAAU,KAAK,cAAc,KAAK,YAAY,OAAO;CACvE,IAAI,MAAM,SAAS,KAAK,EAAE,GAAG,OAAO;CACpC,MAAM,OAAQ,KAAK,MAAoC;CACvD,OAAO,SAAS,KAAK,SAAS,UAAU,KAAK,SAAS,aAAa,OAAO;AAC5E"}
1
+ {"version":3,"file":"pathLeaf.js","names":[],"sources":["../../../src/canvas/deps/pathLeaf.ts"],"sourcesContent":["/**\n * Path leaves as the kit's own path deps read and write them: geometry in\n * `data.path`, drawn through the box its pose describes.\n */\nimport type { Path } from '@weasel-js/geom';\nimport { poseLinearOf, worldEditToStorage, type PathInWorldPose } from '@weasel-js/geom';\nimport type { PoseDescriptor } from 'core/geometry/poseDescriptor';\nimport type { Node, Scene } from 'core/scene/types';\n\n/** A path leaf's world pose, and the box, rotation, and transform its path is drawn through. */\nexport interface DrawnPose {\n world: unknown;\n g: PoseDescriptor<unknown>;\n box: PathInWorldPose;\n}\n\n/** How `g` draws a leaf at `world`, or null for a pose it reads no finite box from. */\nexport function drawnPose(g: PoseDescriptor<unknown>, world: unknown): DrawnPose | null {\n const b = g.getBounds(world);\n if (!b || ![b.x, b.y, b.width, b.height].every(Number.isFinite)) return null;\n const rotation = g.getRotation?.(world) ?? 0;\n const transform = poseLinearOf(world) ?? undefined;\n return { world, g, box: { x: b.x, y: b.y, width: b.width, height: b.height, rotation, transform } };\n}\n\n/** A world-space piece as a world pose of the source's kind plus the path it draws. */\nexport function storePiece({ world, g, box }: DrawnPose, piece: Path): { pose: unknown; path: Path } {\n const keepsRotation = !!g.withRotation && g.supportsRotation?.(world) !== false;\n const rotation = box.rotation ?? 0;\n if (rotation !== 0 && !keepsRotation) {\n const baked = worldEditToStorage({ ...box, rotation: 0 }, piece);\n return { pose: g.fromBounds(baked.pose, world), path: baked.path };\n }\n const { pose: b, path } = worldEditToStorage(box, piece);\n const dst = { x: b.x, y: b.y, width: b.width, height: b.height };\n const pose = g.remapBounds(world, g.getBounds(world), dst);\n return { pose: rotation !== 0 ? g.withRotation!(pose, rotation) : pose, path };\n}\n\n/** The path a leaf stores, or null for one that is not a free path leaf: a\n * group, a locked leaf, one whose geometry derives from other nodes. */\nexport function leafPath(scene: Scene<unknown, string, unknown>, node: Node<unknown, string, unknown>): Path | null {\n if (node.kind !== 'mark' || node.derivePath || node.derivePose) return null;\n if (scene.isLocked(node.id)) return null;\n const path = (node.data as { path?: unknown } | null)?.path as Path | undefined;\n return path && (path.kind === 'rect' || path.kind === 'polygon') ? path : null;\n}\n"],"mappings":";;;;AAiBA,SAAgB,UAAU,GAA4B,OAAkC;CACtF,MAAM,IAAI,EAAE,UAAU,KAAK;CAC3B,IAAI,CAAC,KAAK,CAAC;EAAC,EAAE;EAAG,EAAE;EAAG,EAAE;EAAO,EAAE;CAAM,CAAC,CAAC,MAAM,OAAO,QAAQ,GAAG,OAAO;CACxE,MAAM,WAAW,EAAE,cAAc,KAAK,KAAK;CAC3C,MAAM,YAAY,aAAa,KAAK,KAAK;CACzC,OAAO;EAAE;EAAO;EAAG,KAAK;GAAE,GAAG,EAAE;GAAG,GAAG,EAAE;GAAG,OAAO,EAAE;GAAO,QAAQ,EAAE;GAAQ;GAAU;EAAU;CAAE;AACpG;;AAGA,SAAgB,WAAW,EAAE,OAAO,GAAG,OAAkB,OAA4C;CACnG,MAAM,gBAAgB,CAAC,CAAC,EAAE,gBAAgB,EAAE,mBAAmB,KAAK,MAAM;CAC1E,MAAM,WAAW,IAAI,YAAY;CACjC,IAAI,aAAa,KAAK,CAAC,eAAe;EACpC,MAAM,QAAQ,mBAAmB;GAAE,GAAG;GAAK,UAAU;EAAE,GAAG,KAAK;EAC/D,OAAO;GAAE,MAAM,EAAE,WAAW,MAAM,MAAM,KAAK;GAAG,MAAM,MAAM;EAAK;CACnE;CACA,MAAM,EAAE,MAAM,GAAG,SAAS,mBAAmB,KAAK,KAAK;CACvD,MAAM,MAAM;EAAE,GAAG,EAAE;EAAG,GAAG,EAAE;EAAG,OAAO,EAAE;EAAO,QAAQ,EAAE;CAAO;CAC/D,MAAM,OAAO,EAAE,YAAY,OAAO,EAAE,UAAU,KAAK,GAAG,GAAG;CACzD,OAAO;EAAE,MAAM,aAAa,IAAI,EAAE,aAAc,MAAM,QAAQ,IAAI;EAAM;CAAK;AAC/E;;;AAIA,SAAgB,SAAS,OAAwC,MAAmD;CAClH,IAAI,KAAK,SAAS,UAAU,KAAK,cAAc,KAAK,YAAY,OAAO;CACvE,IAAI,MAAM,SAAS,KAAK,EAAE,GAAG,OAAO;CACpC,MAAM,OAAQ,KAAK,MAAoC;CACvD,OAAO,SAAS,KAAK,SAAS,UAAU,KAAK,SAAS,aAAa,OAAO;AAC5E"}
@@ -1,3 +1,4 @@
1
+ import { documentOf } from "../../core/scene/documentOf.js";
1
2
  import { poseDescriptorForNode } from "../../core/geometry/poseDescriptor.js";
2
3
  import { AUTO_POSE_DESCRIPTOR } from "../../core/geometry/autoPoseDescriptor.js";
3
4
  import { scenePoseFrame } from "../../interactions/actions/poseFrame.js";
@@ -39,7 +40,7 @@ function useSliceDepSource(scene, selection, adapter, poseDescriptor) {
39
40
  const adapterRef = useLatest(adapter);
40
41
  const descriptorRef = useLatest(poseDescriptor);
41
42
  useDepSource("slice", () => ({ commit(cut) {
42
- const sc = sceneRef.current;
43
+ const sc = documentOf(sceneRef.current);
43
44
  const frame = scenePoseFrame(sc);
44
45
  const descriptor = descriptorRef.current ?? AUTO_POSE_DESCRIPTOR;
45
46
  const leaves = [];
@@ -1 +1 @@
1
- {"version":3,"file":"slice.js","names":[],"sources":["../../../src/canvas/deps/slice.ts"],"sourcesContent":["/**\n * The `slice` dep `sliceAction` and `useSliceTool` commit their cuts through.\n *\n * `<SceneCanvas>` publishes `useSliceDepSource` over its own scene. A consumer\n * replaces it with `useSliceDep` (or `useDepSource('slice', …)`) from under\n * the canvas; the newest registration wins, and a canvas child registers\n * after the canvas's own.\n */\nimport { useDepSource } from '@weasel-js/routing/react';\nimport { useLatest } from '@weasel-js/react';\nimport type { SliceDep } from 'interactions/actions/depSchema';\nimport type { Scene, NodeId, Node } from 'core/scene/types';\nimport type { SelectionApi } from 'core/selection/useSelection';\nimport type { Op } from 'core/ops/types';\nimport { AUTO_POSE_DESCRIPTOR } from 'core/geometry/autoPoseDescriptor';\nimport { poseDescriptorForNode, type PoseDescriptor } from 'core/geometry/poseDescriptor';\nimport { pathInWorld } from '@weasel-js/geom';\nimport { drawnPose, leafPath, storePiece, type DrawnPose } from './pathLeaf';\nimport { scenePoseFrame } from 'interactions/actions/poseFrame';\nimport { computeSliceOps, type SliceLeaf } from 'interactions/actions/defaults/sliceOps';\n\n/** Publish how a slice (knife cut) is performed, so the `slice` action can\n * run against the consumer's geometry. */\nexport function useSliceDep(dep: SliceDep): void {\n const depRef = useLatest(dep);\n\n useDepSource('slice', (): SliceDep => depRef.current);\n}\n\n/** What the `slice` dep writes its batch through. An adapter satisfies it. */\nexport interface OpsApplier {\n applyOps(ops: Op[], label?: string): void;\n}\n\n/**\n * The kit `slice` dep: cut every leaf the cut crosses and swap it for its\n * pieces in one undoable batch.\n *\n * A leaf is cut when its `data.path` is a `Path` and `poseDescriptor` can read\n * its world pose as a box and a rotation; a leaf on a locked layer, or one\n * whose geometry derives from other nodes, is passed over. A piece keeps the\n * source's rotation in its pose when the descriptor can write one\n * (`withRotation`), and has the rotation baked into its path otherwise. The\n * pieces of a selected leaf take its place in the selection.\n */\nexport function useSliceDepSource(\n scene: Scene<unknown, string, unknown>,\n selection: SelectionApi,\n adapter: OpsApplier,\n poseDescriptor?: PoseDescriptor<unknown>,\n): void {\n const sceneRef = useLatest(scene);\n const selectionRef = useLatest(selection);\n const adapterRef = useLatest(adapter);\n const descriptorRef = useLatest(poseDescriptor);\n\n useDepSource('slice', (): SliceDep => ({\n commit(cut) {\n const sc = sceneRef.current;\n const frame = scenePoseFrame(sc);\n const descriptor = descriptorRef.current ?? AUTO_POSE_DESCRIPTOR;\n const leaves: SliceLeaf<Node<unknown, string, unknown>>[] = [];\n const drawnAt = new Map<string, DrawnPose>();\n for (const node of sc.renderOrderNodes()) {\n if (sc.isHidden(node.id)) continue;\n const path = leafPath(sc, node);\n if (!path) continue;\n const drawn = drawnPose(poseDescriptorForNode(descriptor, node), frame.world(node.id));\n if (!drawn) continue;\n drawnAt.set(node.id, drawn);\n leaves.push({ node, index: siblingIndex(sc, node), worldPath: pathInWorld(path, drawn.box) });\n }\n\n const sel = selectionRef.current;\n const { ops, nextSelection } = computeSliceOps({\n leaves,\n cut,\n nextId: () => sc.mintId(),\n selection: sel.get(),\n placePiece: (leaf, piece) => {\n const stored = storePiece(drawnAt.get(leaf.node.id)!, piece);\n return { pose: frame.local(leaf.node.id, stored.pose), path: stored.path };\n },\n });\n if (ops.length === 0) return;\n adapterRef.current.applyOps(ops, 'Slice');\n if (nextSelection) sel.set(nextSelection as NodeId[]);\n },\n }));\n}\n\nfunction siblingIndex(scene: Scene<unknown, string, unknown>, node: Node<unknown, string, unknown>): number {\n const siblings = node.parent == null ? scene.roots : scene.childrenOf(node.parent);\n return siblings.indexOf(node.id);\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAuBA,SAAgB,YAAY,KAAqB;CAC/C,MAAM,SAAS,UAAU,GAAG;CAE5B,aAAa,eAAyB,OAAO,OAAO;AACtD;;;;;;;;;;;;AAkBA,SAAgB,kBACd,OACA,WACA,SACA,gBACM;CACN,MAAM,WAAW,UAAU,KAAK;CAChC,MAAM,eAAe,UAAU,SAAS;CACxC,MAAM,aAAa,UAAU,OAAO;CACpC,MAAM,gBAAgB,UAAU,cAAc;CAE9C,aAAa,gBAA0B,EACrC,OAAO,KAAK;EACV,MAAM,KAAK,SAAS;EACpB,MAAM,QAAQ,eAAe,EAAE;EAC/B,MAAM,aAAa,cAAc,WAAW;EAC5C,MAAM,SAAsD,CAAC;EAC7D,MAAM,0BAAU,IAAI,IAAuB;EAC3C,KAAK,MAAM,QAAQ,GAAG,iBAAiB,GAAG;GACxC,IAAI,GAAG,SAAS,KAAK,EAAE,GAAG;GAC1B,MAAM,OAAO,SAAS,IAAI,IAAI;GAC9B,IAAI,CAAC,MAAM;GACX,MAAM,QAAQ,UAAU,sBAAsB,YAAY,IAAI,GAAG,MAAM,MAAM,KAAK,EAAE,CAAC;GACrF,IAAI,CAAC,OAAO;GACZ,QAAQ,IAAI,KAAK,IAAI,KAAK;GAC1B,OAAO,KAAK;IAAE;IAAM,OAAO,aAAa,IAAI,IAAI;IAAG,WAAW,YAAY,MAAM,MAAM,GAAG;GAAE,CAAC;EAC9F;EAEA,MAAM,MAAM,aAAa;EACzB,MAAM,EAAE,KAAK,kBAAkB,gBAAgB;GAC7C;GACA;GACA,cAAc,GAAG,OAAO;GACxB,WAAW,IAAI,IAAI;GACnB,aAAa,MAAM,UAAU;IAC3B,MAAM,SAAS,WAAW,QAAQ,IAAI,KAAK,KAAK,EAAE,GAAI,KAAK;IAC3D,OAAO;KAAE,MAAM,MAAM,MAAM,KAAK,KAAK,IAAI,OAAO,IAAI;KAAG,MAAM,OAAO;IAAK;GAC3E;EACF,CAAC;EACD,IAAI,IAAI,WAAW,GAAG;EACtB,WAAW,QAAQ,SAAS,KAAK,OAAO;EACxC,IAAI,eAAe,IAAI,IAAI,aAAyB;CACtD,EACF,EAAE;AACJ;AAEA,SAAS,aAAa,OAAwC,MAA8C;CAE1G,QADiB,KAAK,UAAU,OAAO,MAAM,QAAQ,MAAM,WAAW,KAAK,MAAM,EAC1E,CAAS,QAAQ,KAAK,EAAE;AACjC"}
1
+ {"version":3,"file":"slice.js","names":[],"sources":["../../../src/canvas/deps/slice.ts"],"sourcesContent":["/**\n * The `slice` dep `sliceAction` and `useSliceTool` commit their cuts through.\n *\n * `<SceneCanvas>` publishes `useSliceDepSource` over its own scene. A consumer\n * replaces it with `useSliceDep` (or `useDepSource('slice', …)`) from under\n * the canvas; the newest registration wins, and a canvas child registers\n * after the canvas's own.\n */\nimport { useDepSource } from '@weasel-js/routing/react';\nimport { useLatest } from '@weasel-js/react';\nimport type { SliceDep } from 'interactions/actions/depSchema';\nimport type { Scene, NodeId, Node } from 'core/scene/types';\nimport type { SelectionApi } from 'core/selection/useSelection';\nimport type { Op } from 'core/ops/types';\nimport { AUTO_POSE_DESCRIPTOR } from 'core/geometry/autoPoseDescriptor';\nimport { poseDescriptorForNode, type PoseDescriptor } from 'core/geometry/poseDescriptor';\nimport { pathInWorld } from '@weasel-js/geom';\nimport { drawnPose, leafPath, storePiece, type DrawnPose } from './pathLeaf';\nimport { scenePoseFrame } from 'interactions/actions/poseFrame';\nimport { documentOf } from 'core/scene/documentOf';\nimport { computeSliceOps, type SliceLeaf } from 'interactions/actions/defaults/sliceOps';\n\n/** Publish how a slice (knife cut) is performed, so the `slice` action can\n * run against the consumer's geometry. */\nexport function useSliceDep(dep: SliceDep): void {\n const depRef = useLatest(dep);\n\n useDepSource('slice', (): SliceDep => depRef.current);\n}\n\n/** What the `slice` dep writes its batch through. An adapter satisfies it. */\nexport interface OpsApplier {\n applyOps(ops: Op[], label?: string): void;\n}\n\n/**\n * The kit `slice` dep: cut every leaf the cut crosses and swap it for its\n * pieces in one undoable batch.\n *\n * A leaf is cut when its `data.path` is a `Path` and `poseDescriptor` can read\n * its world pose as a box and a rotation; a leaf on a locked layer, or one\n * whose geometry derives from other nodes, is passed over. A piece keeps the\n * source's rotation in its pose when the descriptor can write one\n * (`withRotation`), and has the rotation baked into its path otherwise. The\n * pieces of a selected leaf take its place in the selection.\n */\nexport function useSliceDepSource(\n scene: Scene<unknown, string, unknown>,\n selection: SelectionApi,\n adapter: OpsApplier,\n poseDescriptor?: PoseDescriptor<unknown>,\n): void {\n const sceneRef = useLatest(scene);\n const selectionRef = useLatest(selection);\n const adapterRef = useLatest(adapter);\n const descriptorRef = useLatest(poseDescriptor);\n\n useDepSource('slice', (): SliceDep => ({\n commit(cut) {\n // A commit cuts what the document holds, not what an open gesture or edit is showing.\n const sc = documentOf(sceneRef.current);\n const frame = scenePoseFrame(sc);\n const descriptor = descriptorRef.current ?? AUTO_POSE_DESCRIPTOR;\n const leaves: SliceLeaf<Node<unknown, string, unknown>>[] = [];\n const drawnAt = new Map<string, DrawnPose>();\n for (const node of sc.renderOrderNodes()) {\n if (sc.isHidden(node.id)) continue;\n const path = leafPath(sc, node);\n if (!path) continue;\n const drawn = drawnPose(poseDescriptorForNode(descriptor, node), frame.world(node.id));\n if (!drawn) continue;\n drawnAt.set(node.id, drawn);\n leaves.push({ node, index: siblingIndex(sc, node), worldPath: pathInWorld(path, drawn.box) });\n }\n\n const sel = selectionRef.current;\n const { ops, nextSelection } = computeSliceOps({\n leaves,\n cut,\n nextId: () => sc.mintId(),\n selection: sel.get(),\n placePiece: (leaf, piece) => {\n const stored = storePiece(drawnAt.get(leaf.node.id)!, piece);\n return { pose: frame.local(leaf.node.id, stored.pose), path: stored.path };\n },\n });\n if (ops.length === 0) return;\n adapterRef.current.applyOps(ops, 'Slice');\n if (nextSelection) sel.set(nextSelection as NodeId[]);\n },\n }));\n}\n\nfunction siblingIndex(scene: Scene<unknown, string, unknown>, node: Node<unknown, string, unknown>): number {\n const siblings = node.parent == null ? scene.roots : scene.childrenOf(node.parent);\n return siblings.indexOf(node.id);\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;AAwBA,SAAgB,YAAY,KAAqB;CAC/C,MAAM,SAAS,UAAU,GAAG;CAE5B,aAAa,eAAyB,OAAO,OAAO;AACtD;;;;;;;;;;;;AAkBA,SAAgB,kBACd,OACA,WACA,SACA,gBACM;CACN,MAAM,WAAW,UAAU,KAAK;CAChC,MAAM,eAAe,UAAU,SAAS;CACxC,MAAM,aAAa,UAAU,OAAO;CACpC,MAAM,gBAAgB,UAAU,cAAc;CAE9C,aAAa,gBAA0B,EACrC,OAAO,KAAK;EAEV,MAAM,KAAK,WAAW,SAAS,OAAO;EACtC,MAAM,QAAQ,eAAe,EAAE;EAC/B,MAAM,aAAa,cAAc,WAAW;EAC5C,MAAM,SAAsD,CAAC;EAC7D,MAAM,0BAAU,IAAI,IAAuB;EAC3C,KAAK,MAAM,QAAQ,GAAG,iBAAiB,GAAG;GACxC,IAAI,GAAG,SAAS,KAAK,EAAE,GAAG;GAC1B,MAAM,OAAO,SAAS,IAAI,IAAI;GAC9B,IAAI,CAAC,MAAM;GACX,MAAM,QAAQ,UAAU,sBAAsB,YAAY,IAAI,GAAG,MAAM,MAAM,KAAK,EAAE,CAAC;GACrF,IAAI,CAAC,OAAO;GACZ,QAAQ,IAAI,KAAK,IAAI,KAAK;GAC1B,OAAO,KAAK;IAAE;IAAM,OAAO,aAAa,IAAI,IAAI;IAAG,WAAW,YAAY,MAAM,MAAM,GAAG;GAAE,CAAC;EAC9F;EAEA,MAAM,MAAM,aAAa;EACzB,MAAM,EAAE,KAAK,kBAAkB,gBAAgB;GAC7C;GACA;GACA,cAAc,GAAG,OAAO;GACxB,WAAW,IAAI,IAAI;GACnB,aAAa,MAAM,UAAU;IAC3B,MAAM,SAAS,WAAW,QAAQ,IAAI,KAAK,KAAK,EAAE,GAAI,KAAK;IAC3D,OAAO;KAAE,MAAM,MAAM,MAAM,KAAK,KAAK,IAAI,OAAO,IAAI;KAAG,MAAM,OAAO;IAAK;GAC3E;EACF,CAAC;EACD,IAAI,IAAI,WAAW,GAAG;EACtB,WAAW,QAAQ,SAAS,KAAK,OAAO;EACxC,IAAI,eAAe,IAAI,IAAI,aAAyB;CACtD,EACF,EAAE;AACJ;AAEA,SAAS,aAAa,OAAwC,MAA8C;CAE1G,QADiB,KAAK,UAAU,OAAO,MAAM,QAAQ,MAAM,WAAW,KAAK,MAAM,EAC1E,CAAS,QAAQ,KAAK,EAAE;AACjC"}
@@ -2,15 +2,18 @@ import { composeWorldPose, composesFrames } from "../core/scene/composeFrame.js"
2
2
  import { carryBoxPath, carryPathBy, pathAsPainted } from "../core/scene/depFrame.js";
3
3
  import { createNodeMemoScope } from "../core/scene/nodeMemo.js";
4
4
  import { resolveDerivedPath } from "../core/scene/derivedPath.js";
5
+ import { inheritedPaintIn } from "../core/scene/inheritedPaint.js";
6
+ import { nodeAsShown, shownLookup } from "../core/scene/nodeAsShown.js";
5
7
  import { definesFrame, derivedDepOf, effectivePose } from "../core/scene/effectivePose.js";
6
8
  import { asNodeId } from "../core/scene/types.js";
7
9
  import { multiply, poseFrameInverseOf, poseFrameOf } from "@weasel-js/geom";
8
10
 
9
11
  //#region src/canvas/derivedPath.ts
10
- /** `node` as the scene shows it, its `pose` an override's or a derived one: the stored node
11
- * itself where the two agree. */
12
- function shownNode(scene, node) {
13
- const pose = effectivePose(scene, node);
12
+ /** `node` as the scene shows it, its `pose` an override's or a derived one and its `data` an
13
+ * override's: the stored node itself where they agree. */
14
+ function shownNode(scene, stored) {
15
+ const node = nodeAsShown(scene.overrides, stored);
16
+ const pose = effectivePose(scene, stored);
14
17
  return pose === node.pose ? node : {
15
18
  ...node,
16
19
  pose
@@ -74,9 +77,10 @@ function sceneDepLookup(scene, toPose) {
74
77
  slot,
75
78
  shown: sceneOverridePathOf(scene, toPose),
76
79
  childrenOf: (id) => scene.childrenOf(id),
80
+ scene: shownLookup(scene),
77
81
  lookup: (id) => {
78
82
  const node = scene.get(id);
79
- return node === void 0 ? void 0 : new SlotDep(node, ctx);
83
+ return node === void 0 ? void 0 : new SlotDep(nodeAsShown(scene.overrides, node), ctx);
80
84
  }
81
85
  };
82
86
  return ctx.lookup;
@@ -97,6 +101,9 @@ var SlotDep = class {
97
101
  if (shown !== void 0) return pathAsPainted(shown, this.ctx.local(this.node), this.pose);
98
102
  return resolveDerivedPath(this.node, this.ctx.lookup, this.ctx.childrenOf, this.ctx.slot);
99
103
  }
104
+ get inherited() {
105
+ return inheritedPaintIn(this.ctx.scene, this.node);
106
+ }
100
107
  };
101
108
  /** Whether something outside `node`'s data says what path it shows: it derives one, or a
102
109
  * pose override carries one. The gate both scene walks check before resolving a path. */
@@ -181,8 +188,9 @@ function drawnBoundsScene(scene) {
181
188
  let held = drawnScenes.get(scene);
182
189
  if (held === void 0) {
183
190
  const pathOf = sceneDerivedPathOf(scene);
191
+ const shown = shownLookup(scene);
184
192
  held = {
185
- get: (id) => scene.get(id),
193
+ get: (id) => shown.get(id),
186
194
  childrenOf: (id) => scene.childrenOf(id),
187
195
  derivedPathOf: (id) => {
188
196
  const node = scene.get(id);
@@ -1 +1 @@
1
- {"version":3,"file":"derivedPath.js","names":[],"sources":["../../src/canvas/derivedPath.ts"],"sourcesContent":["/**\n * The render walk's half of derived paths: resolving one against a scene, and\n * handing it to the painter.\n *\n * The resolver itself lives in `core/scene/derivedPath.ts`, beside\n * `derivedPose` — a *pose* can derive from a dependency's path, so\n * `effectivePose` has to be able to reach it.\n */\nimport type { DerivedDep, Node, NodeId, Scene } from 'core/scene/types';\nimport { multiply, poseFrameInverseOf, poseFrameOf, type Path } from '@weasel-js/geom';\nimport { asNodeId } from 'core/scene/types';\nimport { definesFrame, derivedDepOf, effectivePose } from 'core/scene/effectivePose';\nimport { composesFrames, composeWorldPose, type PoseAdapter } from 'core/scene/composeFrame';\nimport { resolveDerivedPath } from 'core/scene/derivedPath';\nimport { createNodeMemoScope, type NodeMemoScope } from 'core/scene/nodeMemo';\nimport { carryBoxPath, carryPathBy, pathAsPainted } from 'core/scene/depFrame';\nimport type { SceneViewDrawOne } from './NodeShape';\nimport type { DrawnBoundsScene } from './drawnBounds';\n\nexport { resolveDerivedPath } from 'core/scene/derivedPath';\n\ntype ToPose<TData, TLayer extends string, TPose> = (node: Node<TData, TLayer, TPose>) => TPose;\n\n/** `node` as the scene shows it, its `pose` an override's or a derived one: the stored node\n * itself where the two agree. */\nfunction shownNode<TData, TLayer extends string, TPose>(\n scene: Scene<TData, TLayer, TPose>,\n node: Node<TData, TLayer, TPose>,\n): Node<TData, TLayer, TPose> {\n const pose = effectivePose(scene, node);\n return pose === node.pose ? node : { ...node, pose };\n}\n\n/** A slot's `toPose`, handed each node as the scene shows it, so a view drawn through one\n * shows a drag in flight and a rig's motion as every other view does. */\nexport function sceneShownToPose<TData, TLayer extends string, TPose>(\n scene: Scene<TData, TLayer, TPose>,\n toPose: ToPose<TData, TLayer, TPose>,\n): ToPose<TData, TLayer, TPose> {\n return (node) => toPose(shownNode(scene, node));\n}\n\n/**\n * `(node) => the path its pose override shows it by`, in the frame the reader paints in: as\n * the override has it, or under a `toPose` carried from the box the scene shows the node at\n * to the box `toPose` answers.\n */\nfunction sceneOverridePathOf<TData, TLayer extends string, TPose>(\n scene: Scene<TData, TLayer, TPose>,\n toPose?: ToPose<TData, TLayer, TPose>,\n): (node: Node<TData, TLayer, TPose>) => Path | undefined {\n if (toPose === undefined) return (node) => scene.overrides.read(node.id)?.path;\n return (node) => {\n const path = scene.overrides.read(node.id)?.path;\n if (path === undefined) return undefined;\n const shown = shownNode(scene, node);\n return carryBoxPath(path, shown.pose, toPose(shown));\n };\n}\n\n/**\n * `(node) => the pose it is painted at`: the slot's `toPose` of the node as the scene shows\n * it if it has one, else the scene's effective pose, folded through the parent chain when\n * the scene's composition makes a stored pose local to its container.\n */\nexport function scenePaintedPoseOf<TData, TLayer extends string, TPose>(\n scene: Scene<TData, TLayer, TPose>,\n toPose?: (node: Node<TData, TLayer, TPose>) => TPose,\n): (node: Node<TData, TLayer, TPose>) => TPose {\n const local = toPose\n ? sceneShownToPose(scene, toPose)\n : (node: Node<TData, TLayer, TPose>) => effectivePose(scene, node);\n if (!composesFrames(scene.composition)) return local;\n const chain: PoseAdapter<TPose> = {\n getPose: (id) => {\n const n = scene.get(asNodeId(id));\n return n ? local(n) : (undefined as unknown as TPose);\n },\n getParent: (id) => scene.get(asNodeId(id))?.parent ?? null,\n definesFrame: (id) => {\n const n = scene.get(asNodeId(id));\n return n === undefined || definesFrame(n);\n },\n };\n return (node) => composeWorldPose(chain, node.id, scene.composition.compose);\n}\n\n/**\n * `(id) => that node, the pose it is painted at, and the path it derives` —\n * resolved against the scene, so a dependency that is itself derived resolves\n * before it is read.\n *\n * A derived edge is drawn from these coordinates and must meet the nodes it\n * connects, so this reads the pose each one is painted at: see\n * {@link scenePaintedPoseOf}.\n */\nexport function sceneDepLookup<TData, TLayer extends string, TPose>(\n scene: Scene<TData, TLayer, TPose>,\n toPose?: (node: Node<TData, TLayer, TPose>) => TPose,\n): (id: NodeId) => DerivedDep<TPose> | undefined {\n const slot = memoSlotFor(toPose, composesFrames(scene.composition));\n if (slot === undefined) return (id) => derivedDepOf(scene, id);\n const ctx: SlotLookup<TPose> = {\n at: scenePaintedPoseOf(scene, toPose) as (node: unknown) => TPose,\n local: (toPose\n ? sceneShownToPose(scene, toPose)\n : (node: Node<TData, TLayer, TPose>) => effectivePose(scene, node)) as (node: unknown) => TPose,\n slot,\n shown: sceneOverridePathOf(scene, toPose) as (node: unknown) => Path | undefined,\n childrenOf: (id: NodeId) => scene.childrenOf(id),\n lookup: (id: NodeId) => {\n const node = scene.get(id);\n return node === undefined ? undefined : new SlotDep(node, ctx);\n },\n };\n return ctx.lookup;\n}\n\ninterface SlotLookup<TPose> {\n at: (node: unknown) => TPose;\n /** The pose the node is shown at in its own frame: the box its override path is drawn in. */\n local: (node: unknown) => TPose;\n slot: string | NodeMemoScope;\n /** The path a pose override shows a node by, where it has one. */\n shown: (node: unknown) => Path | undefined;\n childrenOf: (id: NodeId) => readonly NodeId[];\n lookup: (id: NodeId) => DerivedDep<TPose> | undefined;\n}\n\n/** A class, not an object literal with a `get path()` — see `SceneDep` in\n * `core/scene/effectivePose.ts` for why. */\nclass SlotDep<TPose> implements DerivedDep<TPose> {\n readonly node: Node<unknown, string, TPose>;\n readonly pose: TPose;\n\n constructor(posed: unknown, private readonly ctx: SlotLookup<TPose>) {\n this.node = posed as Node<unknown, string, TPose>;\n this.pose = ctx.at(posed);\n }\n\n get path(): Path | null {\n const shown = this.ctx.shown(this.node);\n if (shown !== undefined) return pathAsPainted(shown, this.ctx.local(this.node), this.pose);\n return resolveDerivedPath(\n this.node as never, this.ctx.lookup, this.ctx.childrenOf, this.ctx.slot,\n );\n }\n}\n\n/** Whether something outside `node`'s data says what path it shows: it derives one, or a\n * pose override carries one. The gate both scene walks check before resolving a path. */\nexport function showsComputedPath<TData, TLayer extends string, TPose>(\n scene: Scene<TData, TLayer, TPose>,\n node: { id: NodeId; dependsOn?: readonly NodeId[] | 'children' },\n): boolean {\n return node.dependsOn !== undefined || scene.overrides.read(node.id)?.path !== undefined;\n}\n\n/**\n * `(node) => the path it shows in place of its data's`: a pose override's `path`, else the\n * one it derives, read through {@link sceneDepLookup}. A reader painting through a `toPose`\n * is answered the override's path carried into the box its `toPose` answers.\n */\nexport function sceneDerivedPathOf<TData, TLayer extends string, TPose>(\n scene: Scene<TData, TLayer, TPose>,\n toPose?: (node: Node<TData, TLayer, TPose>) => TPose,\n): (node: Node<TData, TLayer, TPose>) => Path | null {\n const depOf = sceneDepLookup(scene, toPose);\n const slot = memoSlotFor(toPose, composesFrames(scene.composition));\n const childrenOf = (id: NodeId): readonly NodeId[] => scene.childrenOf(id);\n const derived = (node: Node<TData, TLayer, TPose>): Path | null =>\n resolveDerivedPath(node, depOf, childrenOf, slot);\n const overridePathOf = sceneOverridePathOf(scene, toPose);\n return (node) => overridePathOf(node) ?? derived(node);\n}\n\nconst toPoseScopes = new WeakMap<object, { local?: NodeMemoScope; world?: NodeMemoScope }>();\n\n/** Where one way of reading poses memoizes its paths, or `undefined` for the\n * scene's own slot: two readers painting one scene at different poses must\n * not serve each other's paths. Every reader composing stored poses shares a\n * slot; a `toPose` gets a scope that is collected with the function. */\nfunction memoSlotFor(\n toPose: object | undefined,\n composes: boolean,\n): string | NodeMemoScope | undefined {\n const frame = composes ? 'world' : 'local';\n if (toPose === undefined) return frame === 'world' ? 'kit:derivedPath:world' : undefined;\n let scopes = toPoseScopes.get(toPose);\n if (scopes === undefined) {\n scopes = {};\n toPoseScopes.set(toPose, scopes);\n }\n return (scopes[frame] ??= createNodeMemoScope());\n}\n\n/**\n * Wrap a `drawOne` so a node that derives its geometry, or whose pose override\n * carries a path, arrives at the painter with the resolved path. The painter has no scene handle and so cannot read\n * the dependencies' poses itself; both scene walks wrap here instead.\n *\n * A node that derives nothing passes the caller's `ctx` through untouched —\n * this runs per node per frame, and an added `{ derivedPath: null }` would be\n * an allocation per node for a field no painter reads.\n */\nexport function withDerivedPaths<TData, TLayer extends string, TPose>(\n scene: Scene<TData, TLayer, TPose>,\n drawOne: SceneViewDrawOne<TData, TLayer, TPose>,\n toPose?: (node: Node<TData, TLayer, TPose>) => TPose,\n): SceneViewDrawOne<TData, TLayer, TPose> {\n const derivedPathOf = sceneDerivedPathOf(scene, toPose);\n const ownPoseOf = composesFrames(scene.composition)\n ? (toPose ? sceneShownToPose(scene, toPose) : (node: Node<TData, TLayer, TPose>) => effectivePose(scene, node))\n : null;\n return (node, pose, view, ctx) => {\n if (!showsComputedPath(scene, node)) return drawOne(node, pose, view, ctx);\n let path = derivedPathOf(node);\n if (path !== null && ownPoseOf !== null && scene.overrides.read(node.id)?.path === undefined) {\n path = withoutInheritedFrame(path, pose, ownPoseOf(node));\n }\n return drawOne(node, pose, view, { ...ctx, derivedPath: path });\n };\n}\n\n/**\n * A derived path is drawn from its dependencies' painted poses, so it is already where it\n * shows; but the pose rotation the walk wraps every node in is `painted`'s, which carries its\n * containers' turns as well as its own. Carried back through what the containers add, the\n * wrap turns it by its own frame alone, as it does a root's.\n */\nfunction withoutInheritedFrame(path: Path, painted: unknown, own: unknown): Path {\n const undo = poseFrameInverseOf(painted);\n if (undo === null) return path;\n const box = painted as { x: number; y: number; width: number; height: number };\n const o = own as { rotation?: number; transform?: unknown } | null;\n const redo = poseFrameOf({ x: box.x, y: box.y, width: box.width, height: box.height, rotation: o?.rotation, transform: o?.transform });\n return carryPathBy(path, redo === null ? undo : multiply(undo, redo));\n}\n\nconst drawnScenes = new WeakMap<object, DrawnBoundsScene>();\n\n/** `scene` read as `drawnBoundsOf` reads one, each node that shows a computed\n * path answering it. One object per scene, so it can key a memo. */\nexport function drawnBoundsScene<TData, TLayer extends string, TPose>(scene: Scene<TData, TLayer, TPose>): DrawnBoundsScene {\n let held = drawnScenes.get(scene);\n if (held === undefined) {\n const pathOf = sceneDerivedPathOf(scene);\n held = {\n get: (id) => scene.get(id) as never,\n childrenOf: (id) => scene.childrenOf(id),\n derivedPathOf: (id) => {\n const node = scene.get(id);\n return node !== undefined && showsComputedPath(scene, node) ? pathOf(node) : null;\n },\n };\n drawnScenes.set(scene, held);\n }\n return held;\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"derivedPath.js","names":[],"sources":["../../src/canvas/derivedPath.ts"],"sourcesContent":["/**\n * The render walk's half of derived paths: resolving one against a scene, and\n * handing it to the painter.\n *\n * The resolver itself lives in `core/scene/derivedPath.ts`, beside\n * `derivedPose` — a *pose* can derive from a dependency's path, so\n * `effectivePose` has to be able to reach it.\n */\nimport type { DerivedDep, Node, NodeId, Scene } from 'core/scene/types';\nimport { multiply, poseFrameInverseOf, poseFrameOf, type Path } from '@weasel-js/geom';\nimport { asNodeId } from 'core/scene/types';\nimport { definesFrame, derivedDepOf, effectivePose } from 'core/scene/effectivePose';\nimport { composesFrames, composeWorldPose, type PoseAdapter } from 'core/scene/composeFrame';\nimport { resolveDerivedPath } from 'core/scene/derivedPath';\nimport { inheritedPaintIn, type InheritedPaint } from 'core/scene/inheritedPaint';\nimport { createNodeMemoScope, type NodeMemoScope } from 'core/scene/nodeMemo';\nimport { carryBoxPath, carryPathBy, pathAsPainted } from 'core/scene/depFrame';\nimport type { SceneViewDrawOne } from './NodeShape';\nimport type { DrawnBoundsScene } from './drawnBounds';\nimport { nodeAsShown, shownLookup } from 'core/scene/nodeAsShown';\n\nexport { resolveDerivedPath } from 'core/scene/derivedPath';\n\ntype ToPose<TData, TLayer extends string, TPose> = (node: Node<TData, TLayer, TPose>) => TPose;\n\n/** `node` as the scene shows it, its `pose` an override's or a derived one and its `data` an\n * override's: the stored node itself where they agree. */\nfunction shownNode<TData, TLayer extends string, TPose>(\n scene: Scene<TData, TLayer, TPose>,\n stored: Node<TData, TLayer, TPose>,\n): Node<TData, TLayer, TPose> {\n const node = nodeAsShown(scene.overrides, stored);\n const pose = effectivePose(scene, stored);\n return pose === node.pose ? node : { ...node, pose };\n}\n\n/** A slot's `toPose`, handed each node as the scene shows it, so a view drawn through one\n * shows a drag in flight and a rig's motion as every other view does. */\nexport function sceneShownToPose<TData, TLayer extends string, TPose>(\n scene: Scene<TData, TLayer, TPose>,\n toPose: ToPose<TData, TLayer, TPose>,\n): ToPose<TData, TLayer, TPose> {\n return (node) => toPose(shownNode(scene, node));\n}\n\n/**\n * `(node) => the path its pose override shows it by`, in the frame the reader paints in: as\n * the override has it, or under a `toPose` carried from the box the scene shows the node at\n * to the box `toPose` answers.\n */\nfunction sceneOverridePathOf<TData, TLayer extends string, TPose>(\n scene: Scene<TData, TLayer, TPose>,\n toPose?: ToPose<TData, TLayer, TPose>,\n): (node: Node<TData, TLayer, TPose>) => Path | undefined {\n if (toPose === undefined) return (node) => scene.overrides.read(node.id)?.path;\n return (node) => {\n const path = scene.overrides.read(node.id)?.path;\n if (path === undefined) return undefined;\n const shown = shownNode(scene, node);\n return carryBoxPath(path, shown.pose, toPose(shown));\n };\n}\n\n/**\n * `(node) => the pose it is painted at`: the slot's `toPose` of the node as the scene shows\n * it if it has one, else the scene's effective pose, folded through the parent chain when\n * the scene's composition makes a stored pose local to its container.\n */\nexport function scenePaintedPoseOf<TData, TLayer extends string, TPose>(\n scene: Scene<TData, TLayer, TPose>,\n toPose?: (node: Node<TData, TLayer, TPose>) => TPose,\n): (node: Node<TData, TLayer, TPose>) => TPose {\n const local = toPose\n ? sceneShownToPose(scene, toPose)\n : (node: Node<TData, TLayer, TPose>) => effectivePose(scene, node);\n if (!composesFrames(scene.composition)) return local;\n const chain: PoseAdapter<TPose> = {\n getPose: (id) => {\n const n = scene.get(asNodeId(id));\n return n ? local(n) : (undefined as unknown as TPose);\n },\n getParent: (id) => scene.get(asNodeId(id))?.parent ?? null,\n definesFrame: (id) => {\n const n = scene.get(asNodeId(id));\n return n === undefined || definesFrame(n);\n },\n };\n return (node) => composeWorldPose(chain, node.id, scene.composition.compose);\n}\n\n/**\n * `(id) => that node, the pose it is painted at, and the path it derives` —\n * resolved against the scene, so a dependency that is itself derived resolves\n * before it is read.\n *\n * A derived edge is drawn from these coordinates and must meet the nodes it\n * connects, so this reads the pose each one is painted at: see\n * {@link scenePaintedPoseOf}.\n */\nexport function sceneDepLookup<TData, TLayer extends string, TPose>(\n scene: Scene<TData, TLayer, TPose>,\n toPose?: (node: Node<TData, TLayer, TPose>) => TPose,\n): (id: NodeId) => DerivedDep<TPose> | undefined {\n const slot = memoSlotFor(toPose, composesFrames(scene.composition));\n if (slot === undefined) return (id) => derivedDepOf(scene, id);\n const ctx: SlotLookup<TPose> = {\n at: scenePaintedPoseOf(scene, toPose) as (node: unknown) => TPose,\n local: (toPose\n ? sceneShownToPose(scene, toPose)\n : (node: Node<TData, TLayer, TPose>) => effectivePose(scene, node)) as (node: unknown) => TPose,\n slot,\n shown: sceneOverridePathOf(scene, toPose) as (node: unknown) => Path | undefined,\n childrenOf: (id: NodeId) => scene.childrenOf(id),\n scene: shownLookup(scene) as never,\n lookup: (id: NodeId) => {\n const node = scene.get(id);\n return node === undefined ? undefined : new SlotDep(nodeAsShown(scene.overrides, node), ctx);\n },\n };\n return ctx.lookup;\n}\n\ninterface SlotLookup<TPose> {\n at: (node: unknown) => TPose;\n /** The pose the node is shown at in its own frame: the box its override path is drawn in. */\n local: (node: unknown) => TPose;\n slot: string | NodeMemoScope;\n /** The path a pose override shows a node by, where it has one. */\n shown: (node: unknown) => Path | undefined;\n childrenOf: (id: NodeId) => readonly NodeId[];\n lookup: (id: NodeId) => DerivedDep<TPose> | undefined;\n /** Where a node's `'inherit'` paint is looked up. */\n scene: { get(id: NodeId): Node<unknown, string, TPose> | undefined };\n}\n\n/** A class, not an object literal with a `get path()` — see `SceneDep` in\n * `core/scene/effectivePose.ts` for why. */\nclass SlotDep<TPose> implements DerivedDep<TPose> {\n readonly node: Node<unknown, string, TPose>;\n readonly pose: TPose;\n\n constructor(posed: unknown, private readonly ctx: SlotLookup<TPose>) {\n this.node = posed as Node<unknown, string, TPose>;\n this.pose = ctx.at(posed);\n }\n\n get path(): Path | null {\n const shown = this.ctx.shown(this.node);\n if (shown !== undefined) return pathAsPainted(shown, this.ctx.local(this.node), this.pose);\n return resolveDerivedPath(\n this.node as never, this.ctx.lookup, this.ctx.childrenOf, this.ctx.slot,\n );\n }\n\n get inherited(): InheritedPaint | undefined {\n return inheritedPaintIn(this.ctx.scene, this.node);\n }\n}\n\n/** Whether something outside `node`'s data says what path it shows: it derives one, or a\n * pose override carries one. The gate both scene walks check before resolving a path. */\nexport function showsComputedPath<TData, TLayer extends string, TPose>(\n scene: Scene<TData, TLayer, TPose>,\n node: { id: NodeId; dependsOn?: readonly NodeId[] | 'children' },\n): boolean {\n return node.dependsOn !== undefined || scene.overrides.read(node.id)?.path !== undefined;\n}\n\n/**\n * `(node) => the path it shows in place of its data's`: a pose override's `path`, else the\n * one it derives, read through {@link sceneDepLookup}. A reader painting through a `toPose`\n * is answered the override's path carried into the box its `toPose` answers.\n */\nexport function sceneDerivedPathOf<TData, TLayer extends string, TPose>(\n scene: Scene<TData, TLayer, TPose>,\n toPose?: (node: Node<TData, TLayer, TPose>) => TPose,\n): (node: Node<TData, TLayer, TPose>) => Path | null {\n const depOf = sceneDepLookup(scene, toPose);\n const slot = memoSlotFor(toPose, composesFrames(scene.composition));\n const childrenOf = (id: NodeId): readonly NodeId[] => scene.childrenOf(id);\n const derived = (node: Node<TData, TLayer, TPose>): Path | null =>\n resolveDerivedPath(node, depOf, childrenOf, slot);\n const overridePathOf = sceneOverridePathOf(scene, toPose);\n return (node) => overridePathOf(node) ?? derived(node);\n}\n\nconst toPoseScopes = new WeakMap<object, { local?: NodeMemoScope; world?: NodeMemoScope }>();\n\n/** Where one way of reading poses memoizes its paths, or `undefined` for the\n * scene's own slot: two readers painting one scene at different poses must\n * not serve each other's paths. Every reader composing stored poses shares a\n * slot; a `toPose` gets a scope that is collected with the function. */\nfunction memoSlotFor(\n toPose: object | undefined,\n composes: boolean,\n): string | NodeMemoScope | undefined {\n const frame = composes ? 'world' : 'local';\n if (toPose === undefined) return frame === 'world' ? 'kit:derivedPath:world' : undefined;\n let scopes = toPoseScopes.get(toPose);\n if (scopes === undefined) {\n scopes = {};\n toPoseScopes.set(toPose, scopes);\n }\n return (scopes[frame] ??= createNodeMemoScope());\n}\n\n/**\n * Wrap a `drawOne` so a node that derives its geometry, or whose pose override\n * carries a path, arrives at the painter with the resolved path. The painter has no scene handle and so cannot read\n * the dependencies' poses itself; both scene walks wrap here instead.\n *\n * A node that derives nothing passes the caller's `ctx` through untouched —\n * this runs per node per frame, and an added `{ derivedPath: null }` would be\n * an allocation per node for a field no painter reads.\n */\nexport function withDerivedPaths<TData, TLayer extends string, TPose>(\n scene: Scene<TData, TLayer, TPose>,\n drawOne: SceneViewDrawOne<TData, TLayer, TPose>,\n toPose?: (node: Node<TData, TLayer, TPose>) => TPose,\n): SceneViewDrawOne<TData, TLayer, TPose> {\n const derivedPathOf = sceneDerivedPathOf(scene, toPose);\n const ownPoseOf = composesFrames(scene.composition)\n ? (toPose ? sceneShownToPose(scene, toPose) : (node: Node<TData, TLayer, TPose>) => effectivePose(scene, node))\n : null;\n return (node, pose, view, ctx) => {\n if (!showsComputedPath(scene, node)) return drawOne(node, pose, view, ctx);\n let path = derivedPathOf(node);\n if (path !== null && ownPoseOf !== null && scene.overrides.read(node.id)?.path === undefined) {\n path = withoutInheritedFrame(path, pose, ownPoseOf(node));\n }\n return drawOne(node, pose, view, { ...ctx, derivedPath: path });\n };\n}\n\n/**\n * A derived path is drawn from its dependencies' painted poses, so it is already where it\n * shows; but the pose rotation the walk wraps every node in is `painted`'s, which carries its\n * containers' turns as well as its own. Carried back through what the containers add, the\n * wrap turns it by its own frame alone, as it does a root's.\n */\nfunction withoutInheritedFrame(path: Path, painted: unknown, own: unknown): Path {\n const undo = poseFrameInverseOf(painted);\n if (undo === null) return path;\n const box = painted as { x: number; y: number; width: number; height: number };\n const o = own as { rotation?: number; transform?: unknown } | null;\n const redo = poseFrameOf({ x: box.x, y: box.y, width: box.width, height: box.height, rotation: o?.rotation, transform: o?.transform });\n return carryPathBy(path, redo === null ? undo : multiply(undo, redo));\n}\n\nconst drawnScenes = new WeakMap<object, DrawnBoundsScene>();\n\n/** `scene` read as `drawnBoundsOf` reads one, each node that shows a computed\n * path answering it. One object per scene, so it can key a memo. */\nexport function drawnBoundsScene<TData, TLayer extends string, TPose>(scene: Scene<TData, TLayer, TPose>): DrawnBoundsScene {\n let held = drawnScenes.get(scene);\n if (held === undefined) {\n const pathOf = sceneDerivedPathOf(scene);\n const shown = shownLookup(scene);\n held = {\n get: (id) => shown.get(id) as never,\n childrenOf: (id) => scene.childrenOf(id),\n derivedPathOf: (id) => {\n const node = scene.get(id);\n return node !== undefined && showsComputedPath(scene, node) ? pathOf(node) : null;\n },\n };\n drawnScenes.set(scene, held);\n }\n return held;\n}\n"],"mappings":"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"}
@@ -1,18 +1,16 @@
1
+ import { pressTarget } from "../tools/builtin/pressTarget.js";
2
+
1
3
  //#region src/canvas/doubleClickTarget.ts
2
4
  /**
3
- * The node a double-click on `hitId` names: the outermost group on the way up from it, stopping
4
- * below `scope`, or `hitId` itself where no group stands between them. So a double-click on a
5
- * grouped member names the group, and each one inside an isolated group names the next group in.
5
+ * The node a double-click on `hitId` names: what a press there would select, taken inside
6
+ * `scope` (`pressTarget`). So a double-click on a grouped member names the outermost group
7
+ * above it, and each one inside the entered group names the next group in.
6
8
  */
7
9
  function doubleClickTarget(hitId, tree) {
8
- const { parentOf, kindOf, scope } = tree;
9
- let named = hitId;
10
- let id = hitId;
11
- for (let depth = 0; id !== null && id !== scope && depth < 1e4; depth++) {
12
- if (kindOf(id) === "group") named = id;
13
- id = parentOf(id);
14
- }
15
- return named;
10
+ return pressTarget({
11
+ getParent: tree.parentOf,
12
+ getNode: (id) => ({ kind: tree.kindOf(id) })
13
+ }, hitId, tree.scope);
16
14
  }
17
15
 
18
16
  //#endregion
@@ -1 +1 @@
1
- {"version":3,"file":"doubleClickTarget.js","names":[],"sources":["../../src/canvas/doubleClickTarget.ts"],"sourcesContent":["/** How {@link doubleClickTarget} reads the tree. */\nexport interface DoubleClickTree {\n parentOf: (id: string) => string | null;\n /** A node's routing kind; a node routed `'group'` is one a double-click can name. */\n kindOf: (id: string) => string | undefined;\n /** The group isolation is working inside, or null at the scene root. */\n scope: string | null;\n}\n\n/**\n * The node a double-click on `hitId` names: the outermost group on the way up from it, stopping\n * below `scope`, or `hitId` itself where no group stands between them. So a double-click on a\n * grouped member names the group, and each one inside an isolated group names the next group in.\n */\nexport function doubleClickTarget(hitId: string, tree: DoubleClickTree): string {\n const { parentOf, kindOf, scope } = tree;\n let named = hitId;\n let id: string | null = hitId;\n // Capped so a cyclic parent chain cannot hang the pick.\n for (let depth = 0; id !== null && id !== scope && depth < 10_000; depth++) {\n if (kindOf(id) === 'group') named = id;\n id = parentOf(id);\n }\n return named;\n}\n"],"mappings":";;;;;;AAcA,SAAgB,kBAAkB,OAAe,MAA+B;CAC9E,MAAM,EAAE,UAAU,QAAQ,UAAU;CACpC,IAAI,QAAQ;CACZ,IAAI,KAAoB;CAExB,KAAK,IAAI,QAAQ,GAAG,OAAO,QAAQ,OAAO,SAAS,QAAQ,KAAQ,SAAS;EAC1E,IAAI,OAAO,EAAE,MAAM,SAAS,QAAQ;EACpC,KAAK,SAAS,EAAE;CAClB;CACA,OAAO;AACT"}
1
+ {"version":3,"file":"doubleClickTarget.js","names":[],"sources":["../../src/canvas/doubleClickTarget.ts"],"sourcesContent":["import { pressTarget } from '../tools/builtin/pressTarget';\n\n/** How {@link doubleClickTarget} reads the tree. */\nexport interface DoubleClickTree {\n parentOf: (id: string) => string | null;\n /** A node's routing kind; a node routed `'group'` is one a double-click can name. */\n kindOf: (id: string) => string | undefined;\n /** The entered group, or null at the scene root. */\n scope: string | null;\n}\n\n/**\n * The node a double-click on `hitId` names: what a press there would select, taken inside\n * `scope` (`pressTarget`). So a double-click on a grouped member names the outermost group\n * above it, and each one inside the entered group names the next group in.\n */\nexport function doubleClickTarget(hitId: string, tree: DoubleClickTree): string {\n return pressTarget(\n { getParent: tree.parentOf, getNode: (id) => ({ kind: tree.kindOf(id) }) },\n hitId,\n tree.scope,\n );\n}\n"],"mappings":";;;;;;;;AAgBA,SAAgB,kBAAkB,OAAe,MAA+B;CAC9E,OAAO,YACL;EAAE,WAAW,KAAK;EAAU,UAAU,QAAQ,EAAE,MAAM,KAAK,OAAO,EAAE,EAAE;CAAG,GACzE,OACA,KAAK,KACP;AACF"}
@@ -1,3 +1,4 @@
1
+ import { inheritedPaintIn } from "../core/scene/inheritedPaint.js";
1
2
  import { axisAlignedBounds, unionBounds } from "../core/geometry/unionBounds.js";
2
3
  import { framedBounds, poseDescriptorForNode } from "../core/geometry/poseDescriptor.js";
3
4
  import { AUTO_POSE_DESCRIPTOR } from "../core/geometry/autoPoseDescriptor.js";
@@ -17,8 +18,9 @@ import { boundsOfPath, meanScale } from "@weasel-js/geom";
17
18
  * The box of `id` drawn at `pose`, stroke included, in the frame the pose is
18
19
  * drawn in: a turned node's box comes back turned with it. A node with no
19
20
  * stroke, a painter that declares no {@link NodeShapeEntry.drawnBounds}, or
20
- * `includeStroke: false` answers `framedBounds` unchanged. A container's box
21
- * reaches as far as its members' boxes do. A node showing a derived path is
21
+ * `includeStroke: false` answers `framedBounds` unchanged. A group's box
22
+ * reaches as far as its members' boxes do; a mark's, as far as its own ink and its
23
+ * children's do. A node showing a derived path is
22
24
  * boxed around that path, and its placeholder pose plays no part.
23
25
  */
24
26
  function drawnBoundsOf(scene, id, pose, opts = {}) {
@@ -39,10 +41,18 @@ function inkExtentsOf(scene, id, pose, opts = {}) {
39
41
  width: own.width,
40
42
  height: own.height
41
43
  };
42
- const members = node.kind === "container" ? scene.childrenOf?.(id) ?? [] : [];
43
- if (members.length > 0) return membersExtents(scene, members, own, opts);
44
- const drawn = findShapeDrawnBounds(node, pose, inkCtx(opts.scale, own));
45
- return drawn === null ? NO_INK_EXTENTS : inkExtentsBetween(normalized(unframed), drawn);
44
+ const members = scene.childrenOf?.(id) ?? [];
45
+ const reached = members.length > 0 ? membersExtents(scene, members, own, opts) : NO_INK_EXTENTS;
46
+ if (node.kind === "group" && members.length > 0) return reached;
47
+ const drawn = findShapeDrawnBounds(node, pose, withInherited(scene, node, inkCtx(opts.scale, own)));
48
+ if (drawn === null) return reached;
49
+ const ink = inkExtentsBetween(normalized(unframed), drawn);
50
+ return {
51
+ left: Math.max(ink.left, reached.left),
52
+ top: Math.max(ink.top, reached.top),
53
+ right: Math.max(ink.right, reached.right),
54
+ bottom: Math.max(ink.bottom, reached.bottom)
55
+ };
46
56
  }
47
57
  /** The box of a node drawn along `path`: the path's own, grown by the stroke
48
58
  * its painter reports. */
@@ -57,17 +67,17 @@ function derivedBoundsOf(scene, id, pose, path, opts) {
57
67
  const node = scene.get(id);
58
68
  if (opts.includeStroke === false || !node) return own;
59
69
  const drawn = findShapeDrawnBounds(node, pose, {
60
- ...inkCtx(opts.scale, {
70
+ ...withInherited(scene, node, inkCtx(opts.scale, {
61
71
  x: 0,
62
72
  y: 0,
63
73
  width: 0,
64
74
  height: 0
65
- }),
75
+ })),
66
76
  derivedPath: path
67
77
  });
68
78
  return drawn === null ? own : unionBounds([own, drawn]) ?? own;
69
79
  }
70
- /** A container's extents: its members' drawn boxes, taken into its own frame,
80
+ /** A parent's member extents: its members' drawn boxes, taken into its own frame,
71
81
  * past its own box. */
72
82
  function membersExtents(scene, members, own, opts) {
73
83
  const xf = targetTransformOf(own);
@@ -128,6 +138,13 @@ function inkCtx(scale, own) {
128
138
  ]
129
139
  };
130
140
  }
141
+ function withInherited(scene, node, ctx) {
142
+ const inherited = inheritedPaintIn(scene, node);
143
+ return inherited === void 0 ? ctx : {
144
+ ...ctx,
145
+ inherited
146
+ };
147
+ }
131
148
  function normalized(b) {
132
149
  return {
133
150
  x: Math.min(b.x, b.x + b.width),
@@ -1 +1 @@
1
- {"version":3,"file":"drawnBounds.js","names":[],"sources":["../../src/canvas/drawnBounds.ts"],"sourcesContent":["/**\n * The box of a node as drawn — its pose box with the stroke included — which\n * is what selection chrome, resize, rotate, and alignment sit on. Places that\n * store geometry (a group's derived pose, booleans, export) keep reading the\n * pose box through `framedBounds`.\n */\nimport { framedBounds, poseDescriptorForNode, type PoseDescriptor } from 'core/geometry/poseDescriptor';\nimport {\n inkExtentsBetween,\n NO_INK_EXTENTS,\n outsetBox,\n type InkExtents,\n} from 'core/geometry/inkExtents';\nimport { targetCorners, targetTransformOf, worldToLocal } from 'core/geometry/targetFrame';\nimport { axisAlignedBounds, unionBounds } from 'core/geometry/unionBounds';\nimport type { Node, NodeId } from 'core/scene/types';\nimport type { Bounds } from 'core/viewport/fitViewToBounds';\nimport { boundsOfPath, meanScale, type Path, type Scale2 } from '@weasel-js/geom';\nimport { AUTO_POSE_DESCRIPTOR } from 'core/geometry/autoPoseDescriptor';\nimport { findNodeShape, type NodeInkCtx } from './NodeShape';\n\n/** The scene reads {@link drawnBoundsOf} makes. `childrenOf` absent: every\n * node is a leaf. `derivedPathOf` absent: every node is measured by its pose,\n * a path-derived one included — `drawnBoundsScene` supplies it for a `Scene`. */\nexport interface DrawnBoundsScene {\n get(id: NodeId): Node<unknown, string, unknown> | undefined;\n childrenOf?(id: NodeId): readonly NodeId[];\n /** The path a node shows in place of its pose's, or `null` for one that shows its own. */\n derivedPathOf?(id: NodeId): Path | null;\n}\n\n/** How {@link drawnBoundsOf} reads a node. */\nexport interface DrawnBoundsOptions {\n /** How poses are read. Default `AUTO_POSE_DESCRIPTOR`. */\n descriptor?: PoseDescriptor<unknown>;\n /** A member's world pose, for boxing a container around its members.\n * Default: its pose as stored, which is world unless containers compose. */\n worldPoseOf?: (id: NodeId) => unknown;\n /** `false` answers the pose box alone. Default `true`. */\n includeStroke?: boolean;\n /** View scale, which a screen-pixel stroke width is resolved at. Default 1. */\n scale?: number | Scale2;\n}\n\n/**\n * The box of `id` drawn at `pose`, stroke included, in the frame the pose is\n * drawn in: a turned node's box comes back turned with it. A node with no\n * stroke, a painter that declares no {@link NodeShapeEntry.drawnBounds}, or\n * `includeStroke: false` answers `framedBounds` unchanged. A container's box\n * reaches as far as its members' boxes do. A node showing a derived path is\n * boxed around that path, and its placeholder pose plays no part.\n */\nexport function drawnBoundsOf(\n scene: DrawnBoundsScene,\n id: NodeId,\n pose: unknown,\n opts: DrawnBoundsOptions = {},\n): Bounds {\n const derived = scene.derivedPathOf?.(id) ?? null;\n if (derived !== null) return derivedBoundsOf(scene, id, pose, derived, opts);\n return outsetBox(framedBounds(pose, descriptorFor(scene, id, opts)), inkExtentsOf(scene, id, pose, opts));\n}\n\n/** How far `id`'s ink at `pose` reaches past its pose box on each side, in\n * the box's own frame: what {@link drawnBoundsOf} grows the pose box by. */\nexport function inkExtentsOf(\n scene: DrawnBoundsScene,\n id: NodeId,\n pose: unknown,\n opts: DrawnBoundsOptions = {},\n): InkExtents {\n if (opts.includeStroke === false) return NO_INK_EXTENTS;\n const node = scene.get(id);\n if (!node) return NO_INK_EXTENTS;\n const own = framedBounds(pose, descriptorFor(scene, id, opts));\n const unframed = { x: own.x, y: own.y, width: own.width, height: own.height };\n const members = node.kind === 'container' ? scene.childrenOf?.(id) ?? [] : [];\n if (members.length > 0) return membersExtents(scene, members, own, opts);\n const drawn = findShapeDrawnBounds(node, pose, inkCtx(opts.scale, own));\n return drawn === null ? NO_INK_EXTENTS : inkExtentsBetween(normalized(unframed), drawn);\n}\n\n/** The box of a node drawn along `path`: the path's own, grown by the stroke\n * its painter reports. */\nfunction derivedBoundsOf(scene: DrawnBoundsScene, id: NodeId, pose: unknown, path: Path, opts: DrawnBoundsOptions): Bounds {\n const { x, y, width, height } = boundsOfPath(path);\n const own = { x, y, width, height };\n const node = scene.get(id);\n if (opts.includeStroke === false || !node) return own;\n const ctx = { ...inkCtx(opts.scale, { x: 0, y: 0, width: 0, height: 0 }), derivedPath: path };\n const drawn = findShapeDrawnBounds(node, pose, ctx);\n return drawn === null ? own : unionBounds([own, drawn]) ?? own;\n}\n\n/** A container's extents: its members' drawn boxes, taken into its own frame,\n * past its own box. */\nfunction membersExtents(\n scene: DrawnBoundsScene,\n members: readonly NodeId[],\n own: Bounds,\n opts: DrawnBoundsOptions,\n): InkExtents {\n const xf = targetTransformOf(own);\n const worldPoseOf = opts.worldPoseOf ?? ((m: NodeId) => scene.get(m)?.pose);\n let x0 = Infinity, y0 = Infinity, x1 = -Infinity, y1 = -Infinity;\n for (const m of members) {\n const box = drawnBoundsOf(scene, m, worldPoseOf(m), opts);\n for (const c of targetCorners(box, targetTransformOf(box))) {\n const p = worldToLocal(xf, c.x, c.y);\n if (!p) continue;\n x0 = Math.min(x0, p.x); y0 = Math.min(y0, p.y);\n x1 = Math.max(x1, p.x); y1 = Math.max(y1, p.y);\n }\n }\n if (x0 === Infinity) return NO_INK_EXTENTS;\n return inkExtentsBetween(normalized(own), { x: x0, y: y0, width: x1 - x0, height: y1 - y0 });\n}\n\n/** {@link drawnBoundsOf} as an axis-aligned box: what a turned node covers. */\nexport function drawnAABBOf(\n scene: DrawnBoundsScene,\n id: NodeId,\n pose: unknown,\n opts: DrawnBoundsOptions = {},\n): Bounds {\n return axisAlignedBounds(drawnBoundsOf(scene, id, pose, opts));\n}\n\n/** The painter's {@link NodeShapeEntry.drawnBounds} for `node`, or `null`\n * when no painter matches or the one that does declares none. */\nexport function findShapeDrawnBounds<TData, TPose>(\n node: Node<TData, string, TPose>,\n pose: TPose,\n ctx?: NodeInkCtx,\n): Bounds | null {\n return findNodeShape(node)?.drawnBounds?.(node, pose, ctx) ?? null;\n}\n\nfunction descriptorFor(scene: DrawnBoundsScene, id: NodeId, opts: DrawnBoundsOptions): PoseDescriptor<unknown> {\n const d = opts.descriptor ?? AUTO_POSE_DESCRIPTOR;\n const node = scene.get(id);\n return node ? poseDescriptorForNode(d, node) : d;\n}\n\n/** A per-axis view scale also carries the map from `own`'s frame to screen,\n * so a `{ px }` stroke is measured there rather than at the mean. */\nfunction inkCtx(scale: number | Scale2 | undefined, own: Bounds): NodeInkCtx | undefined {\n if (scale === undefined) return undefined;\n if (typeof scale === 'number') return { scale };\n const xf = targetTransformOf(own);\n const [a, b, c, d] = xf.identity ? [1, 0, 0, 1] : xf.toWorld;\n return { scale: meanScale(scale), screen: [scale.x * a, scale.y * b, scale.x * c, scale.y * d] };\n}\n\nfunction normalized(b: Bounds): Bounds {\n return {\n x: Math.min(b.x, b.x + b.width),\n y: Math.min(b.y, b.y + b.height),\n width: Math.abs(b.width),\n height: Math.abs(b.height),\n };\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"drawnBounds.js","names":[],"sources":["../../src/canvas/drawnBounds.ts"],"sourcesContent":["/**\n * The box of a node as drawn — its pose box with the stroke included — which\n * is what selection chrome, resize, rotate, and alignment sit on. Places that\n * store geometry (a group's derived pose, booleans, export) keep reading the\n * pose box through `framedBounds`.\n */\nimport { inheritedPaintIn } from './inheritedPaints';\nimport { framedBounds, poseDescriptorForNode, type PoseDescriptor } from 'core/geometry/poseDescriptor';\nimport {\n inkExtentsBetween,\n NO_INK_EXTENTS,\n outsetBox,\n type InkExtents,\n} from 'core/geometry/inkExtents';\nimport { targetCorners, targetTransformOf, worldToLocal } from 'core/geometry/targetFrame';\nimport { axisAlignedBounds, unionBounds } from 'core/geometry/unionBounds';\nimport type { Node, NodeId } from 'core/scene/types';\nimport type { Bounds } from 'core/viewport/fitViewToBounds';\nimport { boundsOfPath, meanScale, type Path, type Scale2 } from '@weasel-js/geom';\nimport { AUTO_POSE_DESCRIPTOR } from 'core/geometry/autoPoseDescriptor';\nimport { findNodeShape, type NodeInkCtx } from './NodeShape';\n\n/** The scene reads {@link drawnBoundsOf} makes. `childrenOf` absent: every\n * node is a leaf. `derivedPathOf` absent: every node is measured by its pose,\n * a path-derived one included — `drawnBoundsScene` supplies it for a `Scene`. */\nexport interface DrawnBoundsScene {\n get(id: NodeId): Node<unknown, string, unknown> | undefined;\n childrenOf?(id: NodeId): readonly NodeId[];\n /** The path a node shows in place of its pose's, or `null` for one that shows its own. */\n derivedPathOf?(id: NodeId): Path | null;\n}\n\n/** How {@link drawnBoundsOf} reads a node. */\nexport interface DrawnBoundsOptions {\n /** How poses are read. Default `AUTO_POSE_DESCRIPTOR`. */\n descriptor?: PoseDescriptor<unknown>;\n /** A member's world pose, for boxing a container around its members.\n * Default: its pose as stored, which is world unless containers compose. */\n worldPoseOf?: (id: NodeId) => unknown;\n /** `false` answers the pose box alone. Default `true`. */\n includeStroke?: boolean;\n /** View scale, which a screen-pixel stroke width is resolved at. Default 1. */\n scale?: number | Scale2;\n}\n\n/**\n * The box of `id` drawn at `pose`, stroke included, in the frame the pose is\n * drawn in: a turned node's box comes back turned with it. A node with no\n * stroke, a painter that declares no {@link NodeShapeEntry.drawnBounds}, or\n * `includeStroke: false` answers `framedBounds` unchanged. A group's box\n * reaches as far as its members' boxes do; a mark's, as far as its own ink and its\n * children's do. A node showing a derived path is\n * boxed around that path, and its placeholder pose plays no part.\n */\nexport function drawnBoundsOf(\n scene: DrawnBoundsScene,\n id: NodeId,\n pose: unknown,\n opts: DrawnBoundsOptions = {},\n): Bounds {\n const derived = scene.derivedPathOf?.(id) ?? null;\n if (derived !== null) return derivedBoundsOf(scene, id, pose, derived, opts);\n return outsetBox(framedBounds(pose, descriptorFor(scene, id, opts)), inkExtentsOf(scene, id, pose, opts));\n}\n\n/** How far `id`'s ink at `pose` reaches past its pose box on each side, in\n * the box's own frame: what {@link drawnBoundsOf} grows the pose box by. */\nexport function inkExtentsOf(\n scene: DrawnBoundsScene,\n id: NodeId,\n pose: unknown,\n opts: DrawnBoundsOptions = {},\n): InkExtents {\n if (opts.includeStroke === false) return NO_INK_EXTENTS;\n const node = scene.get(id);\n if (!node) return NO_INK_EXTENTS;\n const own = framedBounds(pose, descriptorFor(scene, id, opts));\n const unframed = { x: own.x, y: own.y, width: own.width, height: own.height };\n const members = scene.childrenOf?.(id) ?? [];\n const reached = members.length > 0 ? membersExtents(scene, members, own, opts) : NO_INK_EXTENTS;\n if (node.kind === 'group' && members.length > 0) return reached;\n const drawn = findShapeDrawnBounds(node, pose, withInherited(scene, node, inkCtx(opts.scale, own)));\n if (drawn === null) return reached;\n const ink = inkExtentsBetween(normalized(unframed), drawn);\n return {\n left: Math.max(ink.left, reached.left),\n top: Math.max(ink.top, reached.top),\n right: Math.max(ink.right, reached.right),\n bottom: Math.max(ink.bottom, reached.bottom),\n };\n}\n\n/** The box of a node drawn along `path`: the path's own, grown by the stroke\n * its painter reports. */\nfunction derivedBoundsOf(scene: DrawnBoundsScene, id: NodeId, pose: unknown, path: Path, opts: DrawnBoundsOptions): Bounds {\n const { x, y, width, height } = boundsOfPath(path);\n const own = { x, y, width, height };\n const node = scene.get(id);\n if (opts.includeStroke === false || !node) return own;\n const ctx = { ...withInherited(scene, node, inkCtx(opts.scale, { x: 0, y: 0, width: 0, height: 0 })), derivedPath: path };\n const drawn = findShapeDrawnBounds(node, pose, ctx);\n return drawn === null ? own : unionBounds([own, drawn]) ?? own;\n}\n\n/** A parent's member extents: its members' drawn boxes, taken into its own frame,\n * past its own box. */\nfunction membersExtents(\n scene: DrawnBoundsScene,\n members: readonly NodeId[],\n own: Bounds,\n opts: DrawnBoundsOptions,\n): InkExtents {\n const xf = targetTransformOf(own);\n const worldPoseOf = opts.worldPoseOf ?? ((m: NodeId) => scene.get(m)?.pose);\n let x0 = Infinity, y0 = Infinity, x1 = -Infinity, y1 = -Infinity;\n for (const m of members) {\n const box = drawnBoundsOf(scene, m, worldPoseOf(m), opts);\n for (const c of targetCorners(box, targetTransformOf(box))) {\n const p = worldToLocal(xf, c.x, c.y);\n if (!p) continue;\n x0 = Math.min(x0, p.x); y0 = Math.min(y0, p.y);\n x1 = Math.max(x1, p.x); y1 = Math.max(y1, p.y);\n }\n }\n if (x0 === Infinity) return NO_INK_EXTENTS;\n return inkExtentsBetween(normalized(own), { x: x0, y: y0, width: x1 - x0, height: y1 - y0 });\n}\n\n/** {@link drawnBoundsOf} as an axis-aligned box: what a turned node covers. */\nexport function drawnAABBOf(\n scene: DrawnBoundsScene,\n id: NodeId,\n pose: unknown,\n opts: DrawnBoundsOptions = {},\n): Bounds {\n return axisAlignedBounds(drawnBoundsOf(scene, id, pose, opts));\n}\n\n/** The painter's {@link NodeShapeEntry.drawnBounds} for `node`, or `null`\n * when no painter matches or the one that does declares none. */\nexport function findShapeDrawnBounds<TData, TPose>(\n node: Node<TData, string, TPose>,\n pose: TPose,\n ctx?: NodeInkCtx,\n): Bounds | null {\n return findNodeShape(node)?.drawnBounds?.(node, pose, ctx) ?? null;\n}\n\nfunction descriptorFor(scene: DrawnBoundsScene, id: NodeId, opts: DrawnBoundsOptions): PoseDescriptor<unknown> {\n const d = opts.descriptor ?? AUTO_POSE_DESCRIPTOR;\n const node = scene.get(id);\n return node ? poseDescriptorForNode(d, node) : d;\n}\n\n/** A per-axis view scale also carries the map from `own`'s frame to screen,\n * so a `{ px }` stroke is measured there rather than at the mean. */\nfunction inkCtx(scale: number | Scale2 | undefined, own: Bounds): NodeInkCtx | undefined {\n if (scale === undefined) return undefined;\n if (typeof scale === 'number') return { scale };\n const xf = targetTransformOf(own);\n const [a, b, c, d] = xf.identity ? [1, 0, 0, 1] : xf.toWorld;\n return { scale: meanScale(scale), screen: [scale.x * a, scale.y * b, scale.x * c, scale.y * d] };\n}\n\nfunction withInherited(\n scene: DrawnBoundsScene,\n node: Node<unknown, string, unknown>,\n ctx: NodeInkCtx | undefined,\n): NodeInkCtx | undefined {\n const inherited = inheritedPaintIn(scene, node);\n return inherited === undefined ? ctx : { ...ctx, inherited };\n}\n\nfunction normalized(b: Bounds): Bounds {\n return {\n x: Math.min(b.x, b.x + b.width),\n y: Math.min(b.y, b.y + b.height),\n width: Math.abs(b.width),\n height: Math.abs(b.height),\n };\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;AAsDA,SAAgB,cACd,OACA,IACA,MACA,OAA2B,CAAC,GACpB;CACR,MAAM,UAAU,MAAM,gBAAgB,EAAE,KAAK;CAC7C,IAAI,YAAY,MAAM,OAAO,gBAAgB,OAAO,IAAI,MAAM,SAAS,IAAI;CAC3E,OAAO,UAAU,aAAa,MAAM,cAAc,OAAO,IAAI,IAAI,CAAC,GAAG,aAAa,OAAO,IAAI,MAAM,IAAI,CAAC;AAC1G;;;AAIA,SAAgB,aACd,OACA,IACA,MACA,OAA2B,CAAC,GAChB;CACZ,IAAI,KAAK,kBAAkB,OAAO,OAAO;CACzC,MAAM,OAAO,MAAM,IAAI,EAAE;CACzB,IAAI,CAAC,MAAM,OAAO;CAClB,MAAM,MAAM,aAAa,MAAM,cAAc,OAAO,IAAI,IAAI,CAAC;CAC7D,MAAM,WAAW;EAAE,GAAG,IAAI;EAAG,GAAG,IAAI;EAAG,OAAO,IAAI;EAAO,QAAQ,IAAI;CAAO;CAC5E,MAAM,UAAU,MAAM,aAAa,EAAE,KAAK,CAAC;CAC3C,MAAM,UAAU,QAAQ,SAAS,IAAI,eAAe,OAAO,SAAS,KAAK,IAAI,IAAI;CACjF,IAAI,KAAK,SAAS,WAAW,QAAQ,SAAS,GAAG,OAAO;CACxD,MAAM,QAAQ,qBAAqB,MAAM,MAAM,cAAc,OAAO,MAAM,OAAO,KAAK,OAAO,GAAG,CAAC,CAAC;CAClG,IAAI,UAAU,MAAM,OAAO;CAC3B,MAAM,MAAM,kBAAkB,WAAW,QAAQ,GAAG,KAAK;CACzD,OAAO;EACL,MAAM,KAAK,IAAI,IAAI,MAAM,QAAQ,IAAI;EACrC,KAAK,KAAK,IAAI,IAAI,KAAK,QAAQ,GAAG;EAClC,OAAO,KAAK,IAAI,IAAI,OAAO,QAAQ,KAAK;EACxC,QAAQ,KAAK,IAAI,IAAI,QAAQ,QAAQ,MAAM;CAC7C;AACF;;;AAIA,SAAS,gBAAgB,OAAyB,IAAY,MAAe,MAAY,MAAkC;CACzH,MAAM,EAAE,GAAG,GAAG,OAAO,WAAW,aAAa,IAAI;CACjD,MAAM,MAAM;EAAE;EAAG;EAAG;EAAO;CAAO;CAClC,MAAM,OAAO,MAAM,IAAI,EAAE;CACzB,IAAI,KAAK,kBAAkB,SAAS,CAAC,MAAM,OAAO;CAElD,MAAM,QAAQ,qBAAqB,MAAM,MAAM;EADjC,GAAG,cAAc,OAAO,MAAM,OAAO,KAAK,OAAO;GAAE,GAAG;GAAG,GAAG;GAAG,OAAO;GAAG,QAAQ;EAAE,CAAC,CAAC;EAAG,aAAa;CACpE,CAAG;CAClD,OAAO,UAAU,OAAO,MAAM,YAAY,CAAC,KAAK,KAAK,CAAC,KAAK;AAC7D;;;AAIA,SAAS,eACP,OACA,SACA,KACA,MACY;CACZ,MAAM,KAAK,kBAAkB,GAAG;CAChC,MAAM,cAAc,KAAK,iBAAiB,MAAc,MAAM,IAAI,CAAC,CAAC,EAAE;CACtE,IAAI,KAAK,UAAU,KAAK,UAAU,KAAK,WAAW,KAAK;CACvD,KAAK,MAAM,KAAK,SAAS;EACvB,MAAM,MAAM,cAAc,OAAO,GAAG,YAAY,CAAC,GAAG,IAAI;EACxD,KAAK,MAAM,KAAK,cAAc,KAAK,kBAAkB,GAAG,CAAC,GAAG;GAC1D,MAAM,IAAI,aAAa,IAAI,EAAE,GAAG,EAAE,CAAC;GACnC,IAAI,CAAC,GAAG;GACR,KAAK,KAAK,IAAI,IAAI,EAAE,CAAC;GAAG,KAAK,KAAK,IAAI,IAAI,EAAE,CAAC;GAC7C,KAAK,KAAK,IAAI,IAAI,EAAE,CAAC;GAAG,KAAK,KAAK,IAAI,IAAI,EAAE,CAAC;EAC/C;CACF;CACA,IAAI,OAAO,UAAU,OAAO;CAC5B,OAAO,kBAAkB,WAAW,GAAG,GAAG;EAAE,GAAG;EAAI,GAAG;EAAI,OAAO,KAAK;EAAI,QAAQ,KAAK;CAAG,CAAC;AAC7F;;AAGA,SAAgB,YACd,OACA,IACA,MACA,OAA2B,CAAC,GACpB;CACR,OAAO,kBAAkB,cAAc,OAAO,IAAI,MAAM,IAAI,CAAC;AAC/D;;;AAIA,SAAgB,qBACd,MACA,MACA,KACe;CACf,OAAO,cAAc,IAAI,CAAC,EAAE,cAAc,MAAM,MAAM,GAAG,KAAK;AAChE;AAEA,SAAS,cAAc,OAAyB,IAAY,MAAmD;CAC7G,MAAM,IAAI,KAAK,cAAc;CAC7B,MAAM,OAAO,MAAM,IAAI,EAAE;CACzB,OAAO,OAAO,sBAAsB,GAAG,IAAI,IAAI;AACjD;;;AAIA,SAAS,OAAO,OAAoC,KAAqC;CACvF,IAAI,UAAU,QAAW,OAAO;CAChC,IAAI,OAAO,UAAU,UAAU,OAAO,EAAE,MAAM;CAC9C,MAAM,KAAK,kBAAkB,GAAG;CAChC,MAAM,CAAC,GAAG,GAAG,GAAG,KAAK,GAAG,WAAW;EAAC;EAAG;EAAG;EAAG;CAAC,IAAI,GAAG;CACrD,OAAO;EAAE,OAAO,UAAU,KAAK;EAAG,QAAQ;GAAC,MAAM,IAAI;GAAG,MAAM,IAAI;GAAG,MAAM,IAAI;GAAG,MAAM,IAAI;EAAC;CAAE;AACjG;AAEA,SAAS,cACP,OACA,MACA,KACwB;CACxB,MAAM,YAAY,iBAAiB,OAAO,IAAI;CAC9C,OAAO,cAAc,SAAY,MAAM;EAAE,GAAG;EAAK;CAAU;AAC7D;AAEA,SAAS,WAAW,GAAmB;CACrC,OAAO;EACL,GAAG,KAAK,IAAI,EAAE,GAAG,EAAE,IAAI,EAAE,KAAK;EAC9B,GAAG,KAAK,IAAI,EAAE,GAAG,EAAE,IAAI,EAAE,MAAM;EAC/B,OAAO,KAAK,IAAI,EAAE,KAAK;EACvB,QAAQ,KAAK,IAAI,EAAE,MAAM;CAC3B;AACF"}
@@ -0,0 +1,55 @@
1
+ import { inheritedPaintIn, inheritedPaintOf, sceneAncestors } from "../core/scene/inheritedPaint.js";
2
+ import { INHERIT } from "@weasel-js/paint";
3
+
4
+ //#region src/canvas/inheritedPaints.ts
5
+ var INHERITS_BOTH = { data: {
6
+ fill: INHERIT,
7
+ stroke: INHERIT
8
+ } };
9
+ /** What an `'inherit'` fill and stroke on `node` would take from its
10
+ * containers in `scene`, whatever `node` holds now — what an editor offering
11
+ * the choice shows it would paint. */
12
+ function paintAbove(scene, node) {
13
+ return inheritedPaintOf(INHERITS_BOTH, sceneAncestors((id) => scene.get(id), node));
14
+ }
15
+ /** {@link inheritedPaintIn} bound to `scene`. */
16
+ function sceneInheritedPaintOf(scene) {
17
+ return (node) => inheritedPaintIn(scene, node);
18
+ }
19
+ /**
20
+ * Wrap a `drawOne` so a node whose fill or stroke is `'inherit'` arrives at
21
+ * the painter with its containers' paint on `ctx.inherited`, as
22
+ * `withDerivedPaths` does for a derived path. Every other node, and one whose
23
+ * caller already says what it inherits — a ghost whose containers are mid-edit —
24
+ * passes the caller's `ctx` through untouched.
25
+ */
26
+ function withInheritedPaints(scene, drawOne) {
27
+ const inheritedOf = sceneInheritedPaintOf(scene);
28
+ return (node, pose, view, ctx) => {
29
+ const inherited = ctx?.inherited ?? inheritedOf(node);
30
+ return inherited === void 0 ? drawOne(node, pose, view, ctx) : drawOne(node, pose, view, {
31
+ ...ctx,
32
+ inherited
33
+ });
34
+ };
35
+ }
36
+ /**
37
+ * {@link withInheritedPaints} for a cull's `paintBounds`: a node whose stroke
38
+ * is `'inherit'` is measured with its containers' stroke on `ctx.inherited`,
39
+ * unless its caller already says what it inherits. For a consumer culling its
40
+ * own scene walk with `defaultPaintBounds`, which cannot bound such a node alone.
41
+ */
42
+ function withInheritedPaintBounds(scene, paintBounds) {
43
+ const inheritedOf = sceneInheritedPaintOf(scene);
44
+ return (node, pose, view, ctx) => {
45
+ const inherited = ctx?.inherited ?? inheritedOf(node);
46
+ return paintBounds(node, pose, view, inherited === void 0 ? ctx : {
47
+ ...ctx,
48
+ inherited
49
+ });
50
+ };
51
+ }
52
+
53
+ //#endregion
54
+ export { inheritedPaintIn, paintAbove, sceneInheritedPaintOf, withInheritedPaintBounds, withInheritedPaints };
55
+ //# sourceMappingURL=inheritedPaints.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"inheritedPaints.js","names":[],"sources":["../../src/canvas/inheritedPaints.ts"],"sourcesContent":["import { INHERIT } from '@weasel-js/paint';\nimport type { Node, NodeId } from 'core/scene/types';\nimport { inheritedPaintIn, inheritedPaintOf, sceneAncestors, type InheritedPaint } from 'core/scene/inheritedPaint';\nimport type { SceneViewDrawOne } from './nodeShapeTypes';\nimport type { PaintBoundsFn } from './paintCull';\n\n/** The part of a scene the lookup reads. */\ntype ParentLookup<TNode> = { get(id: NodeId): TNode | undefined };\n\ntype PaintedNode = { data?: unknown; parent?: NodeId | null };\n\nexport { inheritedPaintIn };\n\nconst INHERITS_BOTH = { data: { fill: INHERIT, stroke: INHERIT } };\n\n/** What an `'inherit'` fill and stroke on `node` would take from its\n * containers in `scene`, whatever `node` holds now — what an editor offering\n * the choice shows it would paint. */\nexport function paintAbove<TNode extends PaintedNode>(scene: ParentLookup<TNode>, node: TNode): InheritedPaint {\n return inheritedPaintOf(INHERITS_BOTH, sceneAncestors((id: never) => scene.get(id), node));\n}\n\n/** {@link inheritedPaintIn} bound to `scene`. */\nexport function sceneInheritedPaintOf<TNode extends PaintedNode>(\n scene: ParentLookup<TNode>,\n): (node: TNode) => InheritedPaint | undefined {\n return (node) => inheritedPaintIn(scene, node);\n}\n\n/**\n * Wrap a `drawOne` so a node whose fill or stroke is `'inherit'` arrives at\n * the painter with its containers' paint on `ctx.inherited`, as\n * `withDerivedPaths` does for a derived path. Every other node, and one whose\n * caller already says what it inherits — a ghost whose containers are mid-edit —\n * passes the caller's `ctx` through untouched.\n */\nexport function withInheritedPaints<TData, TLayer extends string, TPose>(\n scene: ParentLookup<Node<TData, TLayer, TPose>>,\n drawOne: SceneViewDrawOne<TData, TLayer, TPose>,\n): SceneViewDrawOne<TData, TLayer, TPose> {\n const inheritedOf = sceneInheritedPaintOf(scene);\n return (node, pose, view, ctx) => {\n const inherited = ctx?.inherited ?? inheritedOf(node);\n return inherited === undefined ? drawOne(node, pose, view, ctx) : drawOne(node, pose, view, { ...ctx, inherited });\n };\n}\n\n/**\n * {@link withInheritedPaints} for a cull's `paintBounds`: a node whose stroke\n * is `'inherit'` is measured with its containers' stroke on `ctx.inherited`,\n * unless its caller already says what it inherits. For a consumer culling its\n * own scene walk with `defaultPaintBounds`, which cannot bound such a node alone.\n */\nexport function withInheritedPaintBounds<TData, TLayer extends string, TPose>(\n scene: ParentLookup<Node<TData, TLayer, TPose>>,\n paintBounds: PaintBoundsFn<Node<TData, TLayer, TPose>, TPose>,\n): PaintBoundsFn<Node<TData, TLayer, TPose>, TPose> {\n const inheritedOf = sceneInheritedPaintOf(scene);\n return (node, pose, view, ctx) => {\n const inherited = ctx?.inherited ?? inheritedOf(node);\n return paintBounds(node, pose, view, inherited === undefined ? ctx : { ...ctx, inherited });\n };\n}\n"],"mappings":";;;;AAaA,IAAM,gBAAgB,EAAE,MAAM;CAAE,MAAM;CAAS,QAAQ;AAAQ,EAAE;;;;AAKjE,SAAgB,WAAsC,OAA4B,MAA6B;CAC7G,OAAO,iBAAiB,eAAe,gBAAgB,OAAc,MAAM,IAAI,EAAE,GAAG,IAAI,CAAC;AAC3F;;AAGA,SAAgB,sBACd,OAC6C;CAC7C,QAAQ,SAAS,iBAAiB,OAAO,IAAI;AAC/C;;;;;;;;AASA,SAAgB,oBACd,OACA,SACwC;CACxC,MAAM,cAAc,sBAAsB,KAAK;CAC/C,QAAQ,MAAM,MAAM,MAAM,QAAQ;EAChC,MAAM,YAAY,KAAK,aAAa,YAAY,IAAI;EACpD,OAAO,cAAc,SAAY,QAAQ,MAAM,MAAM,MAAM,GAAG,IAAI,QAAQ,MAAM,MAAM,MAAM;GAAE,GAAG;GAAK;EAAU,CAAC;CACnH;AACF;;;;;;;AAQA,SAAgB,yBACd,OACA,aACkD;CAClD,MAAM,cAAc,sBAAsB,KAAK;CAC/C,QAAQ,MAAM,MAAM,MAAM,QAAQ;EAChC,MAAM,YAAY,KAAK,aAAa,YAAY,IAAI;EACpD,OAAO,YAAY,MAAM,MAAM,MAAM,cAAc,SAAY,MAAM;GAAE,GAAG;GAAK;EAAU,CAAC;CAC5F;AACF"}
@@ -1,10 +1,10 @@
1
- import { derivedPartsOf } from "../../core/scene/derivedParts.js";
2
- import { DEFAULT_SHAPE_FILL } from "../../util/paint.js";
1
+ import { derivedPartsOf, resolvePartPaint } from "../../core/scene/derivedParts.js";
3
2
  import { derivesPath } from "./memo.js";
4
- import { inkReach, resolveNodeFill, resolveNodeStroke, withAnimatedVertexColors } from "./nodePaint.js";
3
+ import { inkReach, withAnimatedVertexColors } from "./nodePaint.js";
5
4
  import { strokedBounds } from "./strokedBounds.js";
6
5
  import { commandFill, commandStroke } from "@weasel-js/renderer";
7
6
  import { boundsOfPath } from "@weasel-js/geom";
7
+ import { resolveFill, resolveStroke } from "@weasel-js/paint";
8
8
 
9
9
  //#region src/canvas/nodeShapes/derivedPainter.ts
10
10
  function pathCommand(path, declared, strokeSpec, vertexColors) {
@@ -22,14 +22,13 @@ function pathCommand(path, declared, strokeSpec, vertexColors) {
22
22
  /** The stroked box of the path on `ctx`, or `null` with no path or no stroke. */
23
23
  function derivedDrawnBounds(node, ctx) {
24
24
  const path = ctx?.derivedPath;
25
- const stroke = resolveNodeStroke(node.data?.stroke);
25
+ const stroke = resolveStroke(node.data?.stroke, ctx?.inherited?.stroke);
26
26
  return path == null || stroke === null ? null : strokedBounds(path, stroke, ctx?.scale ?? 1, ctx?.screen);
27
27
  }
28
- /** One part's command: its own paint where it names one, else the node's. */
29
- function partCommand(part, d) {
30
- const strokeSpec = resolveNodeStroke(part.stroke === void 0 ? d?.stroke : part.stroke);
31
- const declared = resolveNodeFill(part.fill === void 0 ? d?.fill : part.fill, strokeSpec === null ? DEFAULT_SHAPE_FILL : null);
32
- return pathCommand(part.path, declared, strokeSpec, void 0);
28
+ /** One part's command, in its own paint; `INHERIT` takes the node's. */
29
+ function partCommand(part, d, inherited) {
30
+ const { fill, stroke } = resolvePartPaint(part, d, inherited);
31
+ return pathCommand(part.path, fill, stroke, void 0);
33
32
  }
34
33
  /**
35
34
  * Built-in painter for nodes whose geometry is computed from other nodes'
@@ -49,8 +48,8 @@ function partCommand(part, d) {
49
48
  * Rotation still comes from the pose — `wrapNodeOutput` turns the output about
50
49
  * the pose AABB's center, which for a zero-sized pose is its origin.
51
50
  *
52
- * A path made by `pathFromParts` paints one command per part, each with the
53
- * part's own fill and stroke where it names them.
51
+ * A path made by `pathFromParts` paints one command per part, each in the
52
+ * part's own fill and stroke.
54
53
  */
55
54
  var DERIVED_PAINTER = {
56
55
  id: "kit:derived",
@@ -60,18 +59,18 @@ var DERIVED_PAINTER = {
60
59
  if (path == null) return [];
61
60
  const d = node.data;
62
61
  const parts = derivedPartsOf(path);
63
- if (parts !== void 0) return parts.map((part) => partCommand(part, d));
64
- const strokeSpec = resolveNodeStroke(d?.stroke);
65
- const declared = resolveNodeFill(d?.fill, strokeSpec === null ? DEFAULT_SHAPE_FILL : null);
62
+ if (parts !== void 0) return parts.map((part) => partCommand(part, d, ctx?.inherited));
63
+ const strokeSpec = resolveStroke(d?.stroke, ctx?.inherited?.stroke);
64
+ const declared = resolveFill(d?.fill, ctx?.inherited?.fill);
66
65
  return withAnimatedVertexColors([pathCommand(path, declared, strokeSpec, d?.vertexColors)], ctx);
67
66
  },
68
67
  silhouette: (_node, _pose, ctx) => ctx?.derivedPath ?? null,
69
68
  drawnBounds: (node, _pose, ctx) => derivedDrawnBounds(node, ctx),
70
69
  ink: (node, _pose, ctx) => {
71
70
  const d = node.data;
72
- const stroke = resolveNodeStroke(d?.stroke);
71
+ const stroke = resolveStroke(d?.stroke, ctx?.inherited?.stroke);
73
72
  return {
74
- filled: resolveNodeFill(d?.fill, stroke === null ? DEFAULT_SHAPE_FILL : null) !== null,
73
+ filled: resolveFill(d?.fill, ctx?.inherited?.fill) !== null,
75
74
  ...inkReach(stroke, ctx?.scale)
76
75
  };
77
76
  }