@weasel-js/core 1.10.0 → 1.10.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (327) hide show
  1. package/CHANGELOG.md +168 -0
  2. package/README.md +33 -0
  3. package/dist/affordances/composeAffordanceLayer.js +21 -21
  4. package/dist/affordances/composeAffordanceLayer.js.map +1 -1
  5. package/dist/affordances/hitAffordanceRegions.js +48 -32
  6. package/dist/affordances/hitAffordanceRegions.js.map +1 -1
  7. package/dist/affordances/pathAnchors.js +50 -28
  8. package/dist/affordances/pathAnchors.js.map +1 -1
  9. package/dist/affordances/rotateCursor.js +12 -0
  10. package/dist/affordances/rotateCursor.js.map +1 -0
  11. package/dist/affordances/rotationHandle.js +40 -43
  12. package/dist/affordances/rotationHandle.js.map +1 -1
  13. package/dist/animation/rig/rigNode.js +9 -3
  14. package/dist/animation/rig/rigNode.js.map +1 -1
  15. package/dist/animation/rig/rigSeat.js +46 -13
  16. package/dist/animation/rig/rigSeat.js.map +1 -1
  17. package/dist/animation/rig/skinBinding.js +22 -14
  18. package/dist/animation/rig/skinBinding.js.map +1 -1
  19. package/dist/barrel/canvas.js +29 -28
  20. package/dist/barrel/interactions.js +33 -32
  21. package/dist/barrel/paths.js +11 -10
  22. package/dist/barrel/routing.js +49 -48
  23. package/dist/barrel/scene.js +10 -10
  24. package/dist/barrel/viewport.js +24 -21
  25. package/dist/canvas/Canvas.js +6 -17
  26. package/dist/canvas/Canvas.js.map +1 -1
  27. package/dist/canvas/CanvasView.js +34 -37
  28. package/dist/canvas/CanvasView.js.map +1 -1
  29. package/dist/canvas/CursorCoordsHud.js +3 -3
  30. package/dist/canvas/CursorCoordsHud.js.map +1 -1
  31. package/dist/canvas/DrawCanvas.js +3 -3
  32. package/dist/canvas/DrawCanvas.js.map +1 -1
  33. package/dist/canvas/SceneCanvas/PointerProviderIfRoot.js +24 -23
  34. package/dist/canvas/SceneCanvas/PointerProviderIfRoot.js.map +1 -1
  35. package/dist/canvas/SceneCanvas/StandardActionsRegistrar.js +40 -49
  36. package/dist/canvas/SceneCanvas/StandardActionsRegistrar.js.map +1 -1
  37. package/dist/canvas/SceneCanvas/defaultNodeProperties.js +297 -259
  38. package/dist/canvas/SceneCanvas/defaultNodeProperties.js.map +1 -1
  39. package/dist/canvas/SceneCanvas/features.js +3 -24
  40. package/dist/canvas/SceneCanvas/features.js.map +1 -1
  41. package/dist/canvas/SceneCanvas/insertPreviewCommands.js +97 -0
  42. package/dist/canvas/SceneCanvas/insertPreviewCommands.js.map +1 -0
  43. package/dist/canvas/SceneCanvas/poseGeometry.js.map +1 -1
  44. package/dist/canvas/SceneCanvas/props.js.map +1 -1
  45. package/dist/canvas/SceneCanvas/scenePointQuery.js +50 -0
  46. package/dist/canvas/SceneCanvas/scenePointQuery.js.map +1 -0
  47. package/dist/canvas/SceneCanvas/shapeGeneratorProperties.js +52 -0
  48. package/dist/canvas/SceneCanvas/shapeGeneratorProperties.js.map +1 -0
  49. package/dist/canvas/SceneCanvas/useActionTunings.js +17 -0
  50. package/dist/canvas/SceneCanvas/useActionTunings.js.map +1 -0
  51. package/dist/canvas/SceneCanvas/useCanvasCamera.js +37 -29
  52. package/dist/canvas/SceneCanvas/useCanvasCamera.js.map +1 -1
  53. package/dist/canvas/SceneCanvas/useCanvasDispatcher.js +11 -11
  54. package/dist/canvas/SceneCanvas/useCanvasDispatcher.js.map +1 -1
  55. package/dist/canvas/SceneCanvas/useDispatcherOverlayLayer.js +45 -109
  56. package/dist/canvas/SceneCanvas/useDispatcherOverlayLayer.js.map +1 -1
  57. package/dist/canvas/SceneCanvas/usePreviewGhostLayer.js +49 -41
  58. package/dist/canvas/SceneCanvas/usePreviewGhostLayer.js.map +1 -1
  59. package/dist/canvas/SceneCanvas/useSceneSelectTool.js +64 -96
  60. package/dist/canvas/SceneCanvas/useSceneSelectTool.js.map +1 -1
  61. package/dist/canvas/SceneCanvas/worldPointAt.js +3 -4
  62. package/dist/canvas/SceneCanvas/worldPointAt.js.map +1 -1
  63. package/dist/canvas/SceneCanvas.js +77 -80
  64. package/dist/canvas/SceneCanvas.js.map +1 -1
  65. package/dist/canvas/SceneViewCanvas.js +3 -3
  66. package/dist/canvas/SceneViewCanvas.js.map +1 -1
  67. package/dist/canvas/SurfaceCanvas.js +40 -0
  68. package/dist/canvas/SurfaceCanvas.js.map +1 -0
  69. package/dist/canvas/adapterDerivedPath.js +13 -13
  70. package/dist/canvas/adapterDerivedPath.js.map +1 -1
  71. package/dist/canvas/adapterPickSource.js +34 -0
  72. package/dist/canvas/adapterPickSource.js.map +1 -0
  73. package/dist/canvas/affordanceAt.js +1 -1
  74. package/dist/canvas/affordanceAt.js.map +1 -1
  75. package/dist/canvas/buildSceneLayer.js.map +1 -1
  76. package/dist/canvas/buildSceneTree.js +56 -34
  77. package/dist/canvas/buildSceneTree.js.map +1 -1
  78. package/dist/canvas/copiesAbove.js +27 -0
  79. package/dist/canvas/copiesAbove.js.map +1 -0
  80. package/dist/canvas/defaultDrawOne.js +24 -23
  81. package/dist/canvas/defaultDrawOne.js.map +1 -1
  82. package/dist/canvas/deps/editAnchors.js +45 -50
  83. package/dist/canvas/deps/editAnchors.js.map +1 -1
  84. package/dist/canvas/deps/hitTestArea.js +30 -15
  85. package/dist/canvas/deps/hitTestArea.js.map +1 -1
  86. package/dist/canvas/deps/insert.js +134 -100
  87. package/dist/canvas/deps/insert.js.map +1 -1
  88. package/dist/canvas/deps/layout.js +9 -15
  89. package/dist/canvas/deps/layout.js.map +1 -1
  90. package/dist/canvas/derivedPath.js +53 -29
  91. package/dist/canvas/derivedPath.js.map +1 -1
  92. package/dist/canvas/insertPreviewExtent.js +33 -31
  93. package/dist/canvas/insertPreviewExtent.js.map +1 -1
  94. package/dist/canvas/overChildren.js +29 -0
  95. package/dist/canvas/overChildren.js.map +1 -0
  96. package/dist/canvas/paintedCursorLayer.js.map +1 -1
  97. package/dist/canvas/pickReach.js +23 -0
  98. package/dist/canvas/pickReach.js.map +1 -0
  99. package/dist/canvas/pickWalk.js +51 -80
  100. package/dist/canvas/pickWalk.js.map +1 -1
  101. package/dist/canvas/sceneAdapter.js +14 -35
  102. package/dist/canvas/sceneAdapter.js.map +1 -1
  103. package/dist/canvas/sceneSlotWiring.js +11 -10
  104. package/dist/canvas/sceneSlotWiring.js.map +1 -1
  105. package/dist/canvas/sceneViewRender.js +38 -44
  106. package/dist/canvas/sceneViewRender.js.map +1 -1
  107. package/dist/canvas/useCanvasView.js +42 -44
  108. package/dist/canvas/useCanvasView.js.map +1 -1
  109. package/dist/canvas/useSceneAdapter.js +22 -0
  110. package/dist/canvas/useSceneAdapter.js.map +1 -0
  111. package/dist/canvas/useSurfaceInput.js +13 -16
  112. package/dist/canvas/useSurfaceInput.js.map +1 -1
  113. package/dist/canvas/viewRegistry.js +31 -23
  114. package/dist/canvas/viewRegistry.js.map +1 -1
  115. package/dist/canvas/wrapNodeOutput.js +7 -2
  116. package/dist/canvas/wrapNodeOutput.js.map +1 -1
  117. package/dist/clipboard.d.ts +1 -1
  118. package/dist/{clone-2a5hVbBd.d.ts → clone-C0eADtS0.d.ts} +13 -13
  119. package/dist/clone.d.ts +2 -2
  120. package/dist/core/geometry/poseDescriptor.js +49 -23
  121. package/dist/core/geometry/poseDescriptor.js.map +1 -1
  122. package/dist/core/geometry/regionHit.js +2 -2
  123. package/dist/core/geometry/regionHit.js.map +1 -1
  124. package/dist/core/geometry/targetFrame.js.map +1 -1
  125. package/dist/core/layers/children.js +1 -3
  126. package/dist/core/layers/children.js.map +1 -1
  127. package/dist/core/ops/create.js +2 -2
  128. package/dist/core/ops/create.js.map +1 -1
  129. package/dist/core/ops/delete.js +19 -23
  130. package/dist/core/ops/delete.js.map +1 -1
  131. package/dist/core/ops/reorder/index.js +3 -7
  132. package/dist/core/ops/reorder/index.js.map +1 -1
  133. package/dist/core/ops/reorder/setChildOrder.js +2 -4
  134. package/dist/core/ops/reorder/setChildOrder.js.map +1 -1
  135. package/dist/core/ops/reparent.js +0 -1
  136. package/dist/core/ops/reparent.js.map +1 -1
  137. package/dist/core/ops/slot.js +3 -6
  138. package/dist/core/ops/slot.js.map +1 -1
  139. package/dist/core/pages/fitViewToPage.js +15 -0
  140. package/dist/core/pages/fitViewToPage.js.map +1 -0
  141. package/dist/core/pages/pages.js +65 -0
  142. package/dist/core/pages/pages.js.map +1 -0
  143. package/dist/core/pages/pagesLayer.js +27 -0
  144. package/dist/core/pages/pagesLayer.js.map +1 -0
  145. package/dist/core/paint/meshKindLazy.js +2 -2
  146. package/dist/core/paint/meshKindLazy.js.map +1 -1
  147. package/dist/core/scene/NodeProperties.js.map +1 -1
  148. package/dist/core/scene/composeFrame.js.map +1 -1
  149. package/dist/core/scene/composePose.js +58 -20
  150. package/dist/core/scene/composePose.js.map +1 -1
  151. package/dist/core/scene/effectivePose.js +5 -1
  152. package/dist/core/scene/effectivePose.js.map +1 -1
  153. package/dist/core/scene/historyFacade.js +19 -13
  154. package/dist/core/scene/historyFacade.js.map +1 -1
  155. package/dist/core/scene/nodeFnFields.js +7 -0
  156. package/dist/core/scene/nodeFnFields.js.map +1 -1
  157. package/dist/core/scene/outerOps.js +2 -1
  158. package/dist/core/scene/outerOps.js.map +1 -1
  159. package/dist/core/scene/reconcileSpecs.js +1 -1
  160. package/dist/core/scene/reconcileSpecs.js.map +1 -1
  161. package/dist/core/scene/sceneRecording.js +2 -1
  162. package/dist/core/scene/sceneRecording.js.map +1 -1
  163. package/dist/core/scene/types.js.map +1 -1
  164. package/dist/core/viewport/screenSlop.js.map +1 -1
  165. package/dist/core/viewport/useDecayLoop.js +28 -30
  166. package/dist/core/viewport/useDecayLoop.js.map +1 -1
  167. package/dist/core/viewport/useNodeOverlayFrame.js.map +1 -1
  168. package/dist/core/viewport/useViewAnimation.js +70 -69
  169. package/dist/core/viewport/useViewAnimation.js.map +1 -1
  170. package/dist/core/viewport/view.js +8 -5
  171. package/dist/core/viewport/view.js.map +1 -1
  172. package/dist/core/viewport/zoomAt.js +11 -2
  173. package/dist/core/viewport/zoomAt.js.map +1 -1
  174. package/dist/{cornerHandles-BEshc4z3.d.ts → cornerHandles-BSKbyuvQ.d.ts} +1 -1
  175. package/dist/features/paths/editablePath.js +24 -8
  176. package/dist/features/paths/editablePath.js.map +1 -1
  177. package/dist/features/paths/index.js +4 -4
  178. package/dist/features/paths/shapeGenerator.js +89 -0
  179. package/dist/features/paths/shapeGenerator.js.map +1 -0
  180. package/dist/features/paths/usePathEdit.js +13 -13
  181. package/dist/features/poseRun/usePoseRun.js +25 -25
  182. package/dist/features/poseRun/usePoseRun.js.map +1 -1
  183. package/dist/features/text/useSceneTextEdit.js +82 -64
  184. package/dist/features/text/useSceneTextEdit.js.map +1 -1
  185. package/dist/features/viewports/viewResolver.js +28 -24
  186. package/dist/features/viewports/viewResolver.js.map +1 -1
  187. package/dist/icons/SliceIcon.js +24 -0
  188. package/dist/icons/SliceIcon.js.map +1 -0
  189. package/dist/index.d.ts +956 -581
  190. package/dist/index.js +260 -251
  191. package/dist/interactions/actions/align/align.js +9 -9
  192. package/dist/interactions/actions/align/align.js.map +1 -1
  193. package/dist/interactions/actions/booleans/booleans.js +18 -18
  194. package/dist/interactions/actions/booleans/booleans.js.map +1 -1
  195. package/dist/interactions/actions/booleans/operands.js.map +1 -1
  196. package/dist/interactions/actions/clone/action.js +35 -30
  197. package/dist/interactions/actions/clone/action.js.map +1 -1
  198. package/dist/interactions/actions/defaults/anchorEditing.js +56 -59
  199. package/dist/interactions/actions/defaults/anchorEditing.js.map +1 -1
  200. package/dist/interactions/actions/defaults/anchorHandles.js +42 -0
  201. package/dist/interactions/actions/defaults/anchorHandles.js.map +1 -0
  202. package/dist/interactions/actions/defaults/anchorJoin.js +92 -0
  203. package/dist/interactions/actions/defaults/anchorJoin.js.map +1 -0
  204. package/dist/interactions/actions/defaults/bendSegment.js +46 -0
  205. package/dist/interactions/actions/defaults/bendSegment.js.map +1 -0
  206. package/dist/interactions/actions/defaults/copySubtree.js +1 -1
  207. package/dist/interactions/actions/defaults/copySubtree.js.map +1 -1
  208. package/dist/interactions/actions/defaults/duplicate.js +32 -17
  209. package/dist/interactions/actions/defaults/duplicate.js.map +1 -1
  210. package/dist/interactions/actions/defaults/enterPathEdit.js +19 -7
  211. package/dist/interactions/actions/defaults/enterPathEdit.js.map +1 -1
  212. package/dist/interactions/actions/defaults/icons/anchorIcons.js +75 -0
  213. package/dist/interactions/actions/defaults/icons/anchorIcons.js.map +1 -0
  214. package/dist/interactions/actions/defaults/icons/editIcons.js +35 -8
  215. package/dist/interactions/actions/defaults/icons/editIcons.js.map +1 -1
  216. package/dist/interactions/actions/defaults/insertPathAnchor.js +16 -31
  217. package/dist/interactions/actions/defaults/insertPathAnchor.js.map +1 -1
  218. package/dist/interactions/actions/defaults/nudge.js +28 -23
  219. package/dist/interactions/actions/defaults/nudge.js.map +1 -1
  220. package/dist/interactions/actions/defaults/pinchZoom.js +29 -29
  221. package/dist/interactions/actions/defaults/pinchZoom.js.map +1 -1
  222. package/dist/interactions/actions/defaults/segmentUnder.js +22 -0
  223. package/dist/interactions/actions/defaults/segmentUnder.js.map +1 -0
  224. package/dist/interactions/actions/defaults/viewportZoom.js +82 -79
  225. package/dist/interactions/actions/defaults/viewportZoom.js.map +1 -1
  226. package/dist/interactions/actions/distribute/distribute.js +8 -8
  227. package/dist/interactions/actions/distribute/distribute.js.map +1 -1
  228. package/dist/interactions/actions/edit-anchors/action.js +58 -55
  229. package/dist/interactions/actions/edit-anchors/action.js.map +1 -1
  230. package/dist/interactions/actions/edit-anchors/preview.js +26 -0
  231. package/dist/interactions/actions/edit-anchors/preview.js.map +1 -0
  232. package/dist/interactions/actions/edit-anchors/symmetry.js +11 -0
  233. package/dist/interactions/actions/edit-anchors/symmetry.js.map +1 -0
  234. package/dist/interactions/actions/flip/action.js.map +1 -1
  235. package/dist/interactions/actions/geometryProjection.js.map +1 -1
  236. package/dist/interactions/actions/insert/action.js +37 -30
  237. package/dist/interactions/actions/insert/action.js.map +1 -1
  238. package/dist/interactions/actions/insert/frame.js +9 -2
  239. package/dist/interactions/actions/insert/frame.js.map +1 -1
  240. package/dist/interactions/actions/insert/repositionAction.js +31 -0
  241. package/dist/interactions/actions/insert/repositionAction.js.map +1 -0
  242. package/dist/interactions/actions/lasso-select/action.js +32 -32
  243. package/dist/interactions/actions/move/action.js +29 -25
  244. package/dist/interactions/actions/move/action.js.map +1 -1
  245. package/dist/interactions/actions/move/behaviors/snapToContainer.js +12 -6
  246. package/dist/interactions/actions/move/behaviors/snapToContainer.js.map +1 -1
  247. package/dist/interactions/actions/move/capture.js +32 -23
  248. package/dist/interactions/actions/move/capture.js.map +1 -1
  249. package/dist/interactions/actions/move/frame.js +2 -14
  250. package/dist/interactions/actions/move/frame.js.map +1 -1
  251. package/dist/interactions/actions/move/session.js +1 -1
  252. package/dist/interactions/actions/move/session.js.map +1 -1
  253. package/dist/interactions/actions/move/translate.js.map +1 -1
  254. package/dist/interactions/actions/outlines/createOutlines.js.map +1 -1
  255. package/dist/interactions/actions/outlines/replaceWithPaths.js +13 -13
  256. package/dist/interactions/actions/outlines/replaceWithPaths.js.map +1 -1
  257. package/dist/interactions/actions/outlines/strokeRegion.js.map +1 -1
  258. package/dist/interactions/actions/pathCommands/cutJoin.js +102 -0
  259. package/dist/interactions/actions/pathCommands/cutJoin.js.map +1 -0
  260. package/dist/interactions/actions/pathCommands/livePathEdit.js +64 -0
  261. package/dist/interactions/actions/pathCommands/livePathEdit.js.map +1 -0
  262. package/dist/interactions/actions/pathCommands/pathNodes.js +99 -0
  263. package/dist/interactions/actions/pathCommands/pathNodes.js.map +1 -0
  264. package/dist/interactions/actions/pathCommands/simplifyOffset.js +42 -0
  265. package/dist/interactions/actions/pathCommands/simplifyOffset.js.map +1 -0
  266. package/dist/interactions/actions/resize/action.js +31 -20
  267. package/dist/interactions/actions/resize/action.js.map +1 -1
  268. package/dist/interactions/actions/resize/commit.js.map +1 -1
  269. package/dist/interactions/actions/resize/frame.js +29 -15
  270. package/dist/interactions/actions/resize/frame.js.map +1 -1
  271. package/dist/interactions/actions/resize/replay.js +47 -0
  272. package/dist/interactions/actions/resize/replay.js.map +1 -0
  273. package/dist/interactions/actions/resize/session.js +49 -35
  274. package/dist/interactions/actions/resize/session.js.map +1 -1
  275. package/dist/interactions/actions/resolveOverlays.js +2 -1
  276. package/dist/interactions/actions/resolveOverlays.js.map +1 -1
  277. package/dist/interactions/actions/rotate/action.js +40 -25
  278. package/dist/interactions/actions/rotate/action.js.map +1 -1
  279. package/dist/interactions/actions/rotate/frame.js +1 -1
  280. package/dist/interactions/actions/rotate/frame.js.map +1 -1
  281. package/dist/interactions/actions/rotate/replay.js +22 -0
  282. package/dist/interactions/actions/rotate/replay.js.map +1 -0
  283. package/dist/interactions/actions/rotate/session.js +42 -32
  284. package/dist/interactions/actions/rotate/session.js.map +1 -1
  285. package/dist/interactions/actions/transform-again/action.js +63 -0
  286. package/dist/interactions/actions/transform-again/action.js.map +1 -0
  287. package/dist/interactions/actions/transform-again/memory.js +35 -0
  288. package/dist/interactions/actions/transform-again/memory.js.map +1 -0
  289. package/dist/interactions/actions/useStandardActions.js +101 -83
  290. package/dist/interactions/actions/useStandardActions.js.map +1 -1
  291. package/dist/move.d.ts +4 -4
  292. package/dist/{originProjection-PPT88jZv.d.ts → originProjection-CoNVpITV.d.ts} +1 -1
  293. package/dist/resize.d.ts +3 -3
  294. package/dist/style.css +1 -1
  295. package/dist/tools/builtin/ellipse/useEllipseTool.js +16 -15
  296. package/dist/tools/builtin/ellipse/useEllipseTool.js.map +1 -1
  297. package/dist/tools/builtin/line/useLineTool.js +16 -15
  298. package/dist/tools/builtin/line/useLineTool.js.map +1 -1
  299. package/dist/tools/builtin/pen/usePenTool.js +111 -111
  300. package/dist/tools/builtin/pen/usePenTool.js.map +1 -1
  301. package/dist/tools/builtin/pickTopMostHit.js +11 -14
  302. package/dist/tools/builtin/pickTopMostHit.js.map +1 -1
  303. package/dist/tools/builtin/polygon/usePolygonTool.js +17 -15
  304. package/dist/tools/builtin/polygon/usePolygonTool.js.map +1 -1
  305. package/dist/tools/builtin/rect/useRectTool.js +15 -14
  306. package/dist/tools/builtin/rect/useRectTool.js.map +1 -1
  307. package/dist/tools/builtin/select/adapterPointQuery.js +22 -18
  308. package/dist/tools/builtin/select/adapterPointQuery.js.map +1 -1
  309. package/dist/tools/builtin/select/selectionContributions.js +1 -1
  310. package/dist/tools/builtin/select/selectionContributions.js.map +1 -1
  311. package/dist/tools/builtin/select/useSelectTool.js +2 -1
  312. package/dist/tools/builtin/select/useSelectTool.js.map +1 -1
  313. package/dist/tools/builtin/slice/useSliceTool.js +42 -40
  314. package/dist/tools/builtin/slice/useSliceTool.js.map +1 -1
  315. package/dist/tools/builtin/star/useStarTool.js +18 -16
  316. package/dist/tools/builtin/star/useStarTool.js.map +1 -1
  317. package/dist/tools/builtin/text/useTextTool.js +2 -2
  318. package/dist/tools/builtin/text/useTextTool.js.map +1 -1
  319. package/dist/{types-CIKDwlsP.d.ts → types-DBbbCy0i.d.ts} +22 -15
  320. package/dist/version.js +1 -1
  321. package/package.json +22 -22
  322. package/dist/canvas/surface.module.js +0 -6
  323. package/dist/canvas/surface.module.js.map +0 -1
  324. package/dist/canvas/surfaceBox.js +0 -16
  325. package/dist/canvas/surfaceBox.js.map +0 -1
  326. package/dist/layout/arrivals.js +0 -33
  327. package/dist/layout/arrivals.js.map +0 -1
@@ -1,72 +1,66 @@
1
- import { composesFrames as e } from "../core/scene/composeFrame.js";
2
- import { effectivePose as t } from "../core/scene/effectivePose.js";
3
- import { paintedSceneLayers as n } from "./sceneLayerPaint.js";
4
- import { paintCanvas as r } from "./canvasRenderer.js";
5
- import { wrapNodeOutput as i } from "./wrapNodeOutput.js";
6
- import { sceneDerivedPathOf as a, withDerivedPaths as o } from "./derivedPath.js";
7
- import { buildSceneTree as s } from "./buildSceneTree.js";
8
- import { withColorOverrides as c } from "./colorOverrides.js";
9
- import { paintMissesView as l } from "./paintCull.js";
10
- import { paintBoundsFor as u } from "./defaultDrawOne.js";
11
- import { cullDrawCommands as d } from "@weasel-js/renderer";
12
- import { viewToMat3 as f } from "@weasel-js/geom";
1
+ import { paintedSceneLayers as e } from "./sceneLayerPaint.js";
2
+ import { paintCanvas as t } from "./canvasRenderer.js";
3
+ import { wrapNodeOutput as n } from "./wrapNodeOutput.js";
4
+ import { sceneDerivedPathOf as r, withDerivedPaths as i } from "./derivedPath.js";
5
+ import { buildSceneTree as a } from "./buildSceneTree.js";
6
+ import { sceneToAdapter as o } from "./sceneAdapter.js";
7
+ import { withColorOverrides as s } from "./colorOverrides.js";
8
+ import { paintMissesView as c } from "./paintCull.js";
9
+ import { paintBoundsFor as l } from "./defaultDrawOne.js";
10
+ import { cullDrawCommands as u } from "@weasel-js/renderer";
11
+ import { viewToMat3 as d } from "@weasel-js/geom";
13
12
  //#region src/canvas/sceneViewRender.ts
14
- function p(e, r = {}) {
13
+ function f(t, n = {}) {
14
+ let { getNode: r, getChildren: i, getPose: a, composePose: s } = o(t);
15
15
  return {
16
- getLayers: () => n(e.layers, r).map((e) => ({
16
+ getLayers: () => e(t.layers, n).map((e) => ({
17
17
  id: e.id,
18
18
  visible: e.visible,
19
19
  parallax: e.parallax
20
20
  })),
21
- getNode: (t) => e.get(t),
22
- getChildren: (t) => {
23
- if (t === null) return e.roots;
24
- let n = e.get(t);
25
- return n && n.kind === "container" ? n.children : [];
26
- },
27
- getPose: (n) => t(e, e.get(n))
21
+ getNode: r,
22
+ getChildren: i,
23
+ getPose: a,
24
+ ...s ? { composePose: s } : {}
28
25
  };
29
26
  }
30
- function m(t, n, r, u, m, h, g, _) {
31
- let v = o(t, r), y = g ? c(g, v) : v, b = a(t), x = (e, n, r) => {
32
- let a = t.overrides.read(e.id)?.alpha ?? 1;
33
- return i(y(e, n, r), n, (m ? m(e.id) : 1) * a);
34
- }, S = e(t.composition) ? {
35
- ...p(t, h),
36
- composePose: t.composition.compose
37
- } : p(t, h), C = _ ? {
27
+ function p(e, t, o, l, p, m, h, g) {
28
+ let _ = i(e, o), v = h ? s(h, _) : _, y = r(e), b = (t, r, i) => {
29
+ let a = e.overrides.read(t.id)?.alpha ?? 1;
30
+ return n(v(t, r, i), r, (p ? p(t.id) : 1) * a);
31
+ }, x = f(e, m), S = g ? {
38
32
  x: 0,
39
33
  y: 0,
40
- width: _.width,
41
- height: _.height
42
- } : void 0, w = C && _?.paintBounds ? l(_.paintBounds, n, C) : void 0, T = s(S, x, n, void 0, b, w), E = C ? d(T, f(n), C) : T;
43
- if (u && u.length > 0) for (let e of u) E.push(e);
34
+ width: g.width,
35
+ height: g.height
36
+ } : void 0, C = S && g?.paintBounds ? c(g.paintBounds, t, S) : void 0, w = a(x, b, t, void 0, y, C), T = S ? u(w, d(t), S) : w;
37
+ if (l && l.length > 0) for (let e of l) T.push(e);
44
38
  return [{
45
39
  kind: "group",
46
- transform: f(n),
47
- children: E
40
+ transform: d(t),
41
+ children: T
48
42
  }];
49
43
  }
50
- function h(e) {
51
- let { scene: t, view: n, width: r, height: i, drawOne: a, extraCommands: o, alphaFor: s, layerVisibility: c, layerOrder: l, colorOverrides: d, cull: f } = e;
52
- return m(t, n, a, o, s, {
44
+ function m(e) {
45
+ let { scene: t, view: n, width: r, height: i, drawOne: a, extraCommands: o, alphaFor: s, layerVisibility: c, layerOrder: u, colorOverrides: d, cull: f } = e;
46
+ return p(t, n, a, o, s, {
53
47
  layerVisibility: c,
54
- layerOrder: l
48
+ layerOrder: u
55
49
  }, d, f ? {
56
50
  width: r,
57
51
  height: i,
58
- paintBounds: e.paintBounds ?? u(a)
52
+ paintBounds: e.paintBounds ?? l(a)
59
53
  } : void 0);
60
54
  }
61
- function g(e) {
62
- let { canvas: t, view: n, width: i, height: a, dpr: o } = e;
63
- r(t, h(e), n, {
55
+ function h(e) {
56
+ let { canvas: n, view: r, width: i, height: a, dpr: o } = e;
57
+ t(n, m(e), r, {
64
58
  width: i,
65
59
  height: a,
66
60
  dpr: o
67
61
  });
68
62
  }
69
63
  //#endregion
70
- export { m as buildSceneViewCommands, g as renderSceneToCanvas, h as sceneViewFrame };
64
+ export { p as buildSceneViewCommands, h as renderSceneToCanvas, m as sceneViewFrame };
71
65
 
72
66
  //# sourceMappingURL=sceneViewRender.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"sceneViewRender.js","names":["viewToMat3","viewToMat3$1","sceneAsHierarchy","paintedSceneLayers","scene","layers","l","id","parentId","node","effectivePose","buildSceneViewCommands","derivedOnly","withDerivedPaths","drawOne","derived","colorOverrides","withColorOverrides","derivedPathOf","sceneDerivedPathOf","wrappedDrawOne","overrideAlpha","wrapNodeOutput","pose","v","alphaFor","hierarchy","composesFrames","cullRect","cull","culled","paintMissesView","view","tree","buildSceneTree","children","cullDrawCommands","extraCommands","cmd","sceneViewFrame","args","paintBoundsFor","renderSceneToCanvas","canvas"],"sources":["../../src/canvas/sceneViewRender.ts"],"sourcesContent":["/**\n * `renderSceneToCanvas` — substrate for detached, read-only scene views\n * (`<SceneViewCanvas>` / `<MinimapCanvas>`). Given a `<canvas>`, a `Scene`,\n * a `View`, and a per-node `drawOne`, render one frame.\n */\n\nimport type { WeaselRenderer } from '@weasel-js/renderer';\nimport { cachedCanvasRenderer, paintCanvas } from './canvasRenderer';\nimport { composesFrames } from 'core/scene/composePose';\nimport { viewToMat3 } from '@weasel-js/geom';\nimport type { DrawCommand } from '@weasel-js/renderer';\nimport type { View } from '../core/viewport/view';\nimport type { Node, NodeId, Scene } from '../core/scene/types';\nimport { wrapNodeOutput } from './wrapNodeOutput';\nimport { buildSceneTree, type HierarchicalAdapter } from './buildSceneTree';\nimport { effectivePose } from 'core/scene/effectivePose';\nimport { withDerivedPaths, sceneDerivedPathOf } from './derivedPath';\nimport { withColorOverrides } from './colorOverrides';\nimport type { ColorOverrideRegistry } from '@weasel-js/animator';\nimport type { SceneViewDrawOne } from './NodeShape';\nimport { paintedSceneLayers } from './sceneLayerPaint';\nimport { cullDrawCommands } from '@weasel-js/renderer';\nimport { paintMissesView, type PaintBoundsFn } from './paintCull';\nimport { paintBoundsFor } from './defaultDrawOne';\n\nexport type { SceneViewDrawOne } from './NodeShape';\n\n/**\n * Which scene layers a detached view paints, on top of each layer's own\n * `visible` flag. Keyed the way `<SceneCanvas>` keys its scene layers —\n * `scene:<layerId>` — so the map a main canvas takes serves its minimap too.\n */\nexport interface SceneViewLayers {\n /** `false` hides that layer in this view. It cannot show one the scene hides. */\n layerVisibility?: Record<string, boolean>;\n /** Paint order, bottom first. A listed order is the whole list: a scene\n * layer left out of it is not painted. */\n layerOrder?: readonly string[];\n}\n\n/** The view size `buildSceneViewCommands` culls to, and how it bounds a\n * node's paint before painting it. */\nexport interface SceneViewCull<TData, TLayer extends string, TPose> {\n width: number;\n height: number;\n paintBounds?: PaintBoundsFn<Node<TData, TLayer, TPose>, TPose>;\n}\n\n/** What to draw into an existing canvas: the scene, the view, and the same\n * painting hooks `<SceneCanvas>` takes. */\nexport interface RenderSceneToCanvasArgs<TData, TLayer extends string, TPose> {\n /** The DOM canvas to paint into. The first call on a given canvas creates\n * its renderer and later calls reuse it;\n * `releaseCanvasRenderer(canvas)` frees it. */\n canvas: HTMLCanvasElement;\n scene: Scene<TData, TLayer, TPose>;\n /** Camera view applied to scene and `extraCommands` alike. */\n view: View;\n /** CSS-pixel size. The drawing buffer is set to `width × dpr` and\n * `height × dpr`; the element's CSS size is the caller's to set. */\n width: number;\n height: number;\n drawOne: SceneViewDrawOne<TData, TLayer, TPose>;\n /** Commands appended after the scene-node output, wrapped in the same view\n * transform as scene commands. */\n extraCommands?: DrawCommand[];\n /** Per-id alpha multiplier, mirroring `<SceneCanvas>`'s scene-slot\n * `alphaFor`. Defaults to `() => 1`. */\n alphaFor?: (id: string) => number;\n /** Per-view layer visibility. See {@link SceneViewLayers}. */\n layerVisibility?: SceneViewLayers['layerVisibility'];\n /** Per-view layer order. See {@link SceneViewLayers}. */\n layerOrder?: SceneViewLayers['layerOrder'];\n /** Animated vertex colors to paint, typically an animator's\n * `colorOverrides` — see `NodePaintCtx.vertexColors`. */\n colorOverrides?: ColorOverrideRegistry;\n /** Skip what cannot reach the `width × height` view: nodes `paintBounds`\n * places outside it are not painted, and scene commands that cannot reach\n * it are dropped before the renderer — the scene slot's `cull`. Off by\n * default. `extraCommands` are never culled. */\n cull?: boolean;\n /** Where `drawOne`'s output for a node can reach, as on the scene slot.\n * Defaults to `defaultPaintBounds` when `drawOne` is `defaultDrawOne`. */\n paintBounds?: PaintBoundsFn<Node<TData, TLayer, TPose>, TPose>;\n /** Device-pixel ratio. Defaults to `window.devicePixelRatio || 1` when\n * available, otherwise 1. */\n dpr?: number;\n}\n\n/** Test seam: the renderer `renderSceneToCanvas` created for a canvas. Not\n * re-exported from the barrel. */\nexport function __getCachedRendererForTest(\n canvas: HTMLCanvasElement,\n): WeaselRenderer | undefined {\n return cachedCanvasRenderer(canvas);\n}\n\n/**\n * Present a `Scene` as the reading surface `buildSceneTree` walks. Built\n * inline rather than via `sceneToAdapter` so this module stays free of React\n * — `renderSceneToPixels` runs headless.\n */\nfunction sceneAsHierarchy<TData, TLayer extends string, TPose>(\n scene: Scene<TData, TLayer, TPose>,\n layers: SceneViewLayers = {},\n): HierarchicalAdapter<Node<TData, TLayer, TPose>, TPose> {\n return {\n getLayers: () =>\n paintedSceneLayers(scene.layers, layers)\n .map((l) => ({ id: l.id, visible: l.visible, parallax: l.parallax })),\n getNode: (id) => scene.get(id as NodeId),\n getChildren: (parentId) => {\n if (parentId === null) return scene.roots as readonly string[];\n const node = scene.get(parentId as NodeId);\n return node && node.kind === 'container' ? (node.children as readonly string[]) : [];\n },\n getPose: (id) => effectivePose(scene, scene.get(id as NodeId)!),\n } as HierarchicalAdapter<Node<TData, TLayer, TPose>, TPose>;\n}\n\n/**\n * Build the DrawCommand list that `renderSceneToCanvas` would dispatch, in\n * screen space. Pure — no GL, no DOM. Walks the scene through `buildSceneTree`,\n * the same walk `<SceneCanvas>` uses, which is what keeps hidden layers\n * unpainted and container clips applied here.\n *\n * Each node's commands go through `wrapNodeOutput` — pose rotation, then the\n * `alphaFor` multiplier — so a `drawOne` that rotates its own output rotates\n * twice; emit unrotated geometry and let the pose drive it.\n *\n * Output is one group per **visible** layer, in scene-layer order (or\n * `layers.layerOrder`), each holding one group per node on that layer.\n */\nexport function buildSceneViewCommands<TData, TLayer extends string, TPose>(\n scene: Scene<TData, TLayer, TPose>,\n view: View,\n drawOne: SceneViewDrawOne<TData, TLayer, TPose>,\n extraCommands?: ReadonlyArray<DrawCommand>,\n alphaFor?: (id: string) => number,\n /** This view's own layer visibility and order. */\n layers?: SceneViewLayers,\n /** Animated vertex colors to paint, typically an animator's `colorOverrides`. */\n colorOverrides?: ColorOverrideRegistry,\n /** Cull the scene to this CSS-pixel view size — see\n * `RenderSceneToCanvasArgs.cull`. Omit to paint every node. */\n cull?: SceneViewCull<TData, TLayer, TPose>,\n): DrawCommand[] {\n const derivedOnly = withDerivedPaths(scene, drawOne);\n const derived = colorOverrides ? withColorOverrides(colorOverrides, derivedOnly) : derivedOnly;\n const derivedPathOf = sceneDerivedPathOf(scene);\n const wrappedDrawOne = (\n node: Node<TData, TLayer, TPose>,\n pose: TPose,\n v: View,\n ): DrawCommand[] => {\n const overrideAlpha = scene.overrides.read(node.id as NodeId)?.alpha ?? 1;\n return wrapNodeOutput(\n derived(node, pose, v),\n pose,\n (alphaFor ? alphaFor(node.id) : 1) * overrideAlpha,\n );\n };\n\n const hierarchy = composesFrames(scene.composition)\n ? { ...sceneAsHierarchy(scene, layers), composePose: scene.composition.compose }\n : sceneAsHierarchy(scene, layers);\n\n const cullRect = cull ? { x: 0, y: 0, width: cull.width, height: cull.height } : undefined;\n const culled = cullRect && cull?.paintBounds ? paintMissesView(cull.paintBounds, view, cullRect) : undefined;\n const tree: DrawCommand[] = buildSceneTree(\n hierarchy as Parameters<typeof buildSceneTree>[0],\n wrappedDrawOne as unknown as Parameters<typeof buildSceneTree>[1],\n view,\n undefined,\n derivedPathOf as unknown as Parameters<typeof buildSceneTree>[4],\n culled as Parameters<typeof buildSceneTree>[5],\n );\n const children = cullRect ? cullDrawCommands(tree, viewToMat3(view), cullRect) : tree;\n if (extraCommands && extraCommands.length > 0) {\n for (const cmd of extraCommands) children.push(cmd);\n }\n return [{\n kind: 'group',\n transform: viewToMat3(view),\n children,\n }];\n}\n\n/**\n * The commands `renderSceneToCanvas` paints for one frame — everything in\n * `args` but the canvas. `<SceneViewCanvas>` paints these through its own\n * renderer lease.\n */\nexport function sceneViewFrame<TData, TLayer extends string, TPose>(\n args: Omit<RenderSceneToCanvasArgs<TData, TLayer, TPose>, 'canvas'>,\n): DrawCommand[] {\n const {\n scene, view, width, height, drawOne, extraCommands, alphaFor, layerVisibility, layerOrder,\n colorOverrides, cull,\n } = args;\n return buildSceneViewCommands(\n scene, view, drawOne, extraCommands, alphaFor, { layerVisibility, layerOrder }, colorOverrides,\n cull ? { width, height, paintBounds: args.paintBounds ?? paintBoundsFor(drawOne) } : undefined,\n );\n}\n\n/**\n * Render one frame of `scene` at `view` into `canvas`. Where no WebGL2\n * context is available (jsdom) the call paints nothing. The canvas's renderer\n * lives until `releaseCanvasRenderer(canvas)`.\n */\nexport function renderSceneToCanvas<TData, TLayer extends string, TPose>(\n args: RenderSceneToCanvasArgs<TData, TLayer, TPose>,\n): void {\n const { canvas, view, width, height, dpr } = args;\n paintCanvas(canvas, sceneViewFrame(args), view, { width, height, dpr });\n}\n"],"mappings":";;;;;;;;;;;;;AAsGA,SAASE,EACP,GACA,IAA0B,CAAC,GAC6B;CACxD,OAAO;EACL,iBACEC,EAAmBC,EAAM,QAAQC,CAAM,CAAC,CACrC,KAAK,OAAO;GAAE,IAAIC,EAAE;GAAI,SAASA,EAAE;GAAS,UAAUA,EAAE;EAAS,EAAE;EACxE,UAAU,MAAOF,EAAM,IAAIG,CAAY;EACvC,cAAc,MAAa;GACzB,IAAIC,MAAa,MAAM,OAAOJ,EAAM;GACpC,IAAMK,IAAOL,EAAM,IAAII,CAAkB;GACzC,OAAOC,KAAQA,EAAK,SAAS,cAAeA,EAAK,WAAiC,CAAC;EACrF;EACA,UAAU,MAAOC,EAAcN,GAAOA,EAAM,IAAIG,CAAY,CAAE;CAChE;AACF;AAeA,SAAgBI,EACd,GACA,GACA,GACA,GACA,GAEA,GAEA,GAGA,GACe;CACf,IAAMC,IAAcC,EAAiBT,GAAOU,CAAO,GAC7CC,IAAUC,IAAiBC,EAAmBD,GAAgBJ,CAAW,IAAIA,GAC7EM,IAAgBC,EAAmBf,CAAK,GACxCgB,KACJ,GACA,GACA,MACkB;EAClB,IAAMC,IAAgBjB,EAAM,UAAU,KAAKK,EAAK,EAAY,CAAC,EAAE,SAAS;EACxE,OAAOa,EACLP,EAAQN,GAAMc,GAAMC,CAAC,GACrBD,IACCE,IAAWA,EAAShB,EAAK,EAAE,IAAI,KAAKY,CACvC;CACF,GAEMK,IAAYC,EAAevB,EAAM,WAAW,IAC9C;EAAE,GAAGF,EAAiBE,GAAOC,CAAM;EAAG,aAAaD,EAAM,YAAY;CAAQ,IAC7EF,EAAiBE,GAAOC,CAAM,GAE5BuB,IAAWC,IAAO;EAAE,GAAG;EAAG,GAAG;EAAG,OAAOA,EAAK;EAAO,QAAQA,EAAK;CAAO,IAAI,KAAA,GAC3EC,IAASF,KAAYC,GAAM,cAAcE,EAAgBF,EAAK,aAAaG,GAAMJ,CAAQ,IAAI,KAAA,GAC7FK,IAAsBC,EAC1BR,GACAN,GACAY,GACA,KAAA,GACAd,GACAY,CACF,GACMK,IAAWP,IAAWQ,EAAiBH,GAAMjC,EAAWgC,CAAI,GAAGJ,CAAQ,IAAIK;CACjF,IAAII,KAAiBA,EAAc,SAAS,GAC1C,KAAK,IAAMC,KAAOD,GAAe,EAAS,KAAKC,CAAG;CAEpD,OAAO,CAAC;EACN,MAAM;EACN,WAAWtC,EAAWgC,CAAI;EAC1B;CACF,CAAC;AACH;AAOA,SAAgBO,EACd,GACe;CACf,IAAM,EACJ,UAAO,SAAM,UAAO,WAAQ,YAAS,kBAAe,aAAU,oBAAiB,eAC/E,mBAAgB,YACdC;CACJ,OAAO7B,EACLP,GAAO4B,GAAMlB,GAASuB,GAAeZ,GAAU;EAAE;EAAiB;CAAW,GAAGT,GAChFa,IAAO;EAAE;EAAO;EAAQ,aAAaW,EAAK,eAAeC,EAAe3B,CAAO;CAAE,IAAI,KAAA,CACvF;AACF;AAOA,SAAgB4B,EACd,GACM;CACN,IAAM,EAAE,WAAQ,SAAM,UAAO,WAAQ,WAAQF;CAC7C,EAAYG,GAAQJ,EAAeC,CAAI,GAAGR,GAAM;EAAE;EAAO;EAAQ;CAAI,CAAC;AACxE"}
1
+ {"version":3,"file":"sceneViewRender.js","names":["viewToMat3","viewToMat3$1","sceneAsHierarchy","sceneToAdapter","scene","paintedSceneLayers","layers","l","composePose","buildSceneViewCommands","derivedOnly","withDerivedPaths","drawOne","derived","colorOverrides","withColorOverrides","derivedPathOf","sceneDerivedPathOf","wrappedDrawOne","overrideAlpha","node","wrapNodeOutput","pose","v","alphaFor","hierarchy","cullRect","cull","culled","paintMissesView","view","tree","buildSceneTree","children","cullDrawCommands","extraCommands","cmd","sceneViewFrame","args","paintBoundsFor","renderSceneToCanvas","canvas"],"sources":["../../src/canvas/sceneViewRender.ts"],"sourcesContent":["/**\n * `renderSceneToCanvas` — substrate for detached, read-only scene views\n * (`<SceneViewCanvas>` / `<MinimapCanvas>`). Given a `<canvas>`, a `Scene`,\n * a `View`, and a per-node `drawOne`, render one frame.\n */\n\nimport type { WeaselRenderer } from '@weasel-js/renderer';\nimport { cachedCanvasRenderer, paintCanvas } from './canvasRenderer';\nimport { viewToMat3 } from '@weasel-js/geom';\nimport type { DrawCommand } from '@weasel-js/renderer';\nimport type { View } from '../core/viewport/view';\nimport type { Node, NodeId, Scene } from '../core/scene/types';\nimport { wrapNodeOutput } from './wrapNodeOutput';\nimport { buildSceneTree, type HierarchicalAdapter } from './buildSceneTree';\nimport { sceneToAdapter } from './sceneAdapter';\nimport { withDerivedPaths, sceneDerivedPathOf } from './derivedPath';\nimport { withColorOverrides } from './colorOverrides';\nimport type { ColorOverrideRegistry } from '@weasel-js/animator';\nimport type { SceneViewDrawOne } from './NodeShape';\nimport { paintedSceneLayers } from './sceneLayerPaint';\nimport { cullDrawCommands } from '@weasel-js/renderer';\nimport { paintMissesView, type PaintBoundsFn } from './paintCull';\nimport { paintBoundsFor } from './defaultDrawOne';\n\nexport type { SceneViewDrawOne } from './NodeShape';\n\n/**\n * Which scene layers a detached view paints, on top of each layer's own\n * `visible` flag. Keyed the way `<SceneCanvas>` keys its scene layers —\n * `scene:<layerId>` — so the map a main canvas takes serves its minimap too.\n */\nexport interface SceneViewLayers {\n /** `false` hides that layer in this view. It cannot show one the scene hides. */\n layerVisibility?: Record<string, boolean>;\n /** Paint order, bottom first. A listed order is the whole list: a scene\n * layer left out of it is not painted. */\n layerOrder?: readonly string[];\n}\n\n/** The view size `buildSceneViewCommands` culls to, and how it bounds a\n * node's paint before painting it. */\nexport interface SceneViewCull<TData, TLayer extends string, TPose> {\n width: number;\n height: number;\n paintBounds?: PaintBoundsFn<Node<TData, TLayer, TPose>, TPose>;\n}\n\n/** What to draw into an existing canvas: the scene, the view, and the same\n * painting hooks `<SceneCanvas>` takes. */\nexport interface RenderSceneToCanvasArgs<TData, TLayer extends string, TPose> {\n /** The DOM canvas to paint into. The first call on a given canvas creates\n * its renderer and later calls reuse it;\n * `releaseCanvasRenderer(canvas)` frees it. */\n canvas: HTMLCanvasElement;\n scene: Scene<TData, TLayer, TPose>;\n /** Camera view applied to scene and `extraCommands` alike. */\n view: View;\n /** CSS-pixel size. The drawing buffer is set to `width × dpr` and\n * `height × dpr`; the element's CSS size is the caller's to set. */\n width: number;\n height: number;\n drawOne: SceneViewDrawOne<TData, TLayer, TPose>;\n /** Commands appended after the scene-node output, wrapped in the same view\n * transform as scene commands. */\n extraCommands?: DrawCommand[];\n /** Per-id alpha multiplier, mirroring `<SceneCanvas>`'s scene-slot\n * `alphaFor`. Defaults to `() => 1`. */\n alphaFor?: (id: string) => number;\n /** Per-view layer visibility. See {@link SceneViewLayers}. */\n layerVisibility?: SceneViewLayers['layerVisibility'];\n /** Per-view layer order. See {@link SceneViewLayers}. */\n layerOrder?: SceneViewLayers['layerOrder'];\n /** Animated vertex colors to paint, typically an animator's\n * `colorOverrides` — see `NodePaintCtx.vertexColors`. */\n colorOverrides?: ColorOverrideRegistry;\n /** Skip what cannot reach the `width × height` view: nodes `paintBounds`\n * places outside it are not painted, and scene commands that cannot reach\n * it are dropped before the renderer — the scene slot's `cull`. Off by\n * default. `extraCommands` are never culled. */\n cull?: boolean;\n /** Where `drawOne`'s output for a node can reach, as on the scene slot.\n * Defaults to `defaultPaintBounds` when `drawOne` is `defaultDrawOne`. */\n paintBounds?: PaintBoundsFn<Node<TData, TLayer, TPose>, TPose>;\n /** Device-pixel ratio. Defaults to `window.devicePixelRatio || 1` when\n * available, otherwise 1. */\n dpr?: number;\n}\n\n/** Test seam: the renderer `renderSceneToCanvas` created for a canvas. Not\n * re-exported from the barrel. */\nexport function __getCachedRendererForTest(\n canvas: HTMLCanvasElement,\n): WeaselRenderer | undefined {\n return cachedCanvasRenderer(canvas);\n}\n\n/**\n * Present a `Scene` as the reading surface `buildSceneTree` walks: the scene\n * adapter's own reads, under this view's layer list.\n */\nfunction sceneAsHierarchy<TData, TLayer extends string, TPose>(\n scene: Scene<TData, TLayer, TPose>,\n layers: SceneViewLayers = {},\n): HierarchicalAdapter<Node<TData, TLayer, TPose>, TPose> {\n const { getNode, getChildren, getPose, composePose } = sceneToAdapter(scene);\n return {\n getLayers: () =>\n paintedSceneLayers(scene.layers, layers)\n .map((l) => ({ id: l.id, visible: l.visible, parallax: l.parallax })),\n getNode,\n getChildren,\n getPose,\n ...(composePose ? { composePose } : {}),\n };\n}\n\n/**\n * Build the DrawCommand list that `renderSceneToCanvas` would dispatch, in\n * screen space. Pure — no GL, no DOM. Walks the scene through `buildSceneTree`,\n * the same walk `<SceneCanvas>` uses, which is what keeps hidden layers\n * unpainted and container clips applied here.\n *\n * Each node's commands go through `wrapNodeOutput` — pose rotation, then the\n * `alphaFor` multiplier — so a `drawOne` that rotates its own output rotates\n * twice; emit unrotated geometry and let the pose drive it.\n *\n * Output is one group per **visible** layer, in scene-layer order (or\n * `layers.layerOrder`), each holding one group per node on that layer.\n */\nexport function buildSceneViewCommands<TData, TLayer extends string, TPose>(\n scene: Scene<TData, TLayer, TPose>,\n view: View,\n drawOne: SceneViewDrawOne<TData, TLayer, TPose>,\n extraCommands?: ReadonlyArray<DrawCommand>,\n alphaFor?: (id: string) => number,\n /** This view's own layer visibility and order. */\n layers?: SceneViewLayers,\n /** Animated vertex colors to paint, typically an animator's `colorOverrides`. */\n colorOverrides?: ColorOverrideRegistry,\n /** Cull the scene to this CSS-pixel view size — see\n * `RenderSceneToCanvasArgs.cull`. Omit to paint every node. */\n cull?: SceneViewCull<TData, TLayer, TPose>,\n): DrawCommand[] {\n const derivedOnly = withDerivedPaths(scene, drawOne);\n const derived = colorOverrides ? withColorOverrides(colorOverrides, derivedOnly) : derivedOnly;\n const derivedPathOf = sceneDerivedPathOf(scene);\n const wrappedDrawOne = (\n node: Node<TData, TLayer, TPose>,\n pose: TPose,\n v: View,\n ): DrawCommand[] => {\n const overrideAlpha = scene.overrides.read(node.id as NodeId)?.alpha ?? 1;\n return wrapNodeOutput(\n derived(node, pose, v),\n pose,\n (alphaFor ? alphaFor(node.id) : 1) * overrideAlpha,\n );\n };\n\n const hierarchy = sceneAsHierarchy(scene, layers);\n\n const cullRect = cull ? { x: 0, y: 0, width: cull.width, height: cull.height } : undefined;\n const culled = cullRect && cull?.paintBounds ? paintMissesView(cull.paintBounds, view, cullRect) : undefined;\n const tree: DrawCommand[] = buildSceneTree(\n hierarchy as Parameters<typeof buildSceneTree>[0],\n wrappedDrawOne as unknown as Parameters<typeof buildSceneTree>[1],\n view,\n undefined,\n derivedPathOf as unknown as Parameters<typeof buildSceneTree>[4],\n culled as Parameters<typeof buildSceneTree>[5],\n );\n const children = cullRect ? cullDrawCommands(tree, viewToMat3(view), cullRect) : tree;\n if (extraCommands && extraCommands.length > 0) {\n for (const cmd of extraCommands) children.push(cmd);\n }\n return [{\n kind: 'group',\n transform: viewToMat3(view),\n children,\n }];\n}\n\n/**\n * The commands `renderSceneToCanvas` paints for one frame — everything in\n * `args` but the canvas. `<SceneViewCanvas>` paints these through its own\n * renderer lease.\n */\nexport function sceneViewFrame<TData, TLayer extends string, TPose>(\n args: Omit<RenderSceneToCanvasArgs<TData, TLayer, TPose>, 'canvas'>,\n): DrawCommand[] {\n const {\n scene, view, width, height, drawOne, extraCommands, alphaFor, layerVisibility, layerOrder,\n colorOverrides, cull,\n } = args;\n return buildSceneViewCommands(\n scene, view, drawOne, extraCommands, alphaFor, { layerVisibility, layerOrder }, colorOverrides,\n cull ? { width, height, paintBounds: args.paintBounds ?? paintBoundsFor(drawOne) } : undefined,\n );\n}\n\n/**\n * Render one frame of `scene` at `view` into `canvas`. Where no WebGL2\n * context is available (jsdom) the call paints nothing. The canvas's renderer\n * lives until `releaseCanvasRenderer(canvas)`.\n */\nexport function renderSceneToCanvas<TData, TLayer extends string, TPose>(\n args: RenderSceneToCanvasArgs<TData, TLayer, TPose>,\n): void {\n const { canvas, view, width, height, dpr } = args;\n paintCanvas(canvas, sceneViewFrame(args), view, { width, height, dpr });\n}\n"],"mappings":";;;;;;;;;;;;AAoGA,SAASE,EACP,GACA,IAA0B,CAAC,GAC6B;CACxD,IAAM,EAAE,YAAS,gBAAa,YAAS,mBAAgBC,EAAeC,CAAK;CAC3E,OAAO;EACL,iBACEC,EAAmBD,EAAM,QAAQE,CAAM,CAAC,CACrC,KAAK,OAAO;GAAE,IAAIC,EAAE;GAAI,SAASA,EAAE;GAAS,UAAUA,EAAE;EAAS,EAAE;EACxE;EACA;EACA;EACA,GAAIC,IAAc,EAAE,eAAY,IAAI,CAAC;CACvC;AACF;AAeA,SAAgBC,EACd,GACA,GACA,GACA,GACA,GAEA,GAEA,GAGA,GACe;CACf,IAAMC,IAAcC,EAAiBP,GAAOQ,CAAO,GAC7CC,IAAUC,IAAiBC,EAAmBD,GAAgBJ,CAAW,IAAIA,GAC7EM,IAAgBC,EAAmBb,CAAK,GACxCc,KACJ,GACA,GACA,MACkB;EAClB,IAAMC,IAAgBf,EAAM,UAAU,KAAKgB,EAAK,EAAY,CAAC,EAAE,SAAS;EACxE,OAAOC,EACLR,EAAQO,GAAME,GAAMC,CAAC,GACrBD,IACCE,IAAWA,EAASJ,EAAK,EAAE,IAAI,KAAKD,CACvC;CACF,GAEMM,IAAYvB,EAAiBE,GAAOE,CAAM,GAE1CoB,IAAWC,IAAO;EAAE,GAAG;EAAG,GAAG;EAAG,OAAOA,EAAK;EAAO,QAAQA,EAAK;CAAO,IAAI,KAAA,GAC3EC,IAASF,KAAYC,GAAM,cAAcE,EAAgBF,EAAK,aAAaG,GAAMJ,CAAQ,IAAI,KAAA,GAC7FK,IAAsBC,EAC1BP,GACAP,GACAY,GACA,KAAA,GACAd,GACAY,CACF,GACMK,IAAWP,IAAWQ,EAAiBH,GAAM/B,EAAW8B,CAAI,GAAGJ,CAAQ,IAAIK;CACjF,IAAII,KAAiBA,EAAc,SAAS,GAC1C,KAAK,IAAMC,KAAOD,GAAe,EAAS,KAAKC,CAAG;CAEpD,OAAO,CAAC;EACN,MAAM;EACN,WAAWpC,EAAW8B,CAAI;EAC1B;CACF,CAAC;AACH;AAOA,SAAgBO,EACd,GACe;CACf,IAAM,EACJ,UAAO,SAAM,UAAO,WAAQ,YAAS,kBAAe,aAAU,oBAAiB,eAC/E,mBAAgB,YACdC;CACJ,OAAO7B,EACLL,GAAO0B,GAAMlB,GAASuB,GAAeX,GAAU;EAAE;EAAiB;CAAW,GAAGV,GAChFa,IAAO;EAAE;EAAO;EAAQ,aAAaW,EAAK,eAAeC,EAAe3B,CAAO;CAAE,IAAI,KAAA,CACvF;AACF;AAOA,SAAgB4B,EACd,GACM;CACN,IAAM,EAAE,WAAQ,SAAM,UAAO,WAAQ,WAAQF;CAC7C,EAAYG,GAAQJ,EAAeC,CAAI,GAAGR,GAAM;EAAE;EAAO;EAAQ;CAAI,CAAC;AACxE"}
@@ -1,9 +1,9 @@
1
- import { normalizeView as e } from "../core/viewport/view.js";
2
- import { clampView as t } from "../core/viewport/clampView.js";
3
- import { useCallback as n, useRef as r } from "react";
4
- import { useLatest as i } from "@weasel-js/react";
1
+ import { normalizeView as e, sameView as t } from "../core/viewport/view.js";
2
+ import { clampView as n } from "../core/viewport/clampView.js";
3
+ import { useCallback as r, useRef as i } from "react";
4
+ import { useLatest as a } from "@weasel-js/react";
5
5
  //#region src/canvas/useCanvasView.ts
6
- var a = {
6
+ var o = {
7
7
  x: 0,
8
8
  y: 0,
9
9
  scale: {
@@ -11,57 +11,55 @@ var a = {
11
11
  y: 1
12
12
  }
13
13
  };
14
- function o(o) {
15
- let { view: s, defaultView: c, onViewChange: l, viewBounds: u, dims: d, requestRedraw: f } = o, p = typeof s == "function" ? s : void 0, m = s === void 0 || p ? void 0 : e(s), h = r(m ?? e(c ?? a)), g = r(/* @__PURE__ */ new Set()), _ = i({
16
- controlled: s !== void 0,
17
- derived: p,
18
- onViewChange: l,
19
- viewBounds: u,
20
- dims: d,
21
- requestRedraw: f
22
- }), v = r(!1), y = n(() => {
23
- let t = _.current.derived;
24
- return t ? e(t()) : h.current;
25
- }, [_]), b = n((n, r) => {
26
- let { controlled: i, derived: a, onViewChange: o, viewBounds: s, dims: c, requestRedraw: l } = _.current, u = e(n), d = s ? t(u, s, c()) : u;
14
+ function s(s) {
15
+ let { view: c, defaultView: l, onViewChange: u, viewBounds: d, dims: f, requestRedraw: p } = s, m = typeof c == "function" ? c : void 0, h = c === void 0 || m ? void 0 : e(c), g = i(h ?? e(l ?? o)), _ = i(/* @__PURE__ */ new Set()), v = a({
16
+ controlled: c !== void 0,
17
+ derived: m,
18
+ onViewChange: u,
19
+ viewBounds: d,
20
+ dims: f,
21
+ requestRedraw: p
22
+ }), y = i(!1), b = r(() => {
23
+ let t = v.current.derived;
24
+ return t ? e(t()) : g.current;
25
+ }, [v]), x = r((t, r) => {
26
+ let { controlled: i, derived: a, onViewChange: o, viewBounds: s, dims: c, requestRedraw: l } = v.current, u = e(t), d = s ? n(u, s, c()) : u;
27
27
  if (i) {
28
- r && !v.current && (v.current = !0, console.warn(o ? "[weasel] setView not applied locally: this canvas is controlled by its `view` prop. The value was forwarded to `onViewChange` — update the prop from there, or drop it to take the imperative path." : "[weasel] setView ignored: this canvas is controlled by its `view` prop and has no `onViewChange`, so the value has nowhere to go. Update the prop, or drop it to take the imperative path.")), o?.(d), a && l();
28
+ r && !y.current && (y.current = !0, console.warn(o ? "[weasel] setView not applied locally: this canvas is controlled by its `view` prop. The value was forwarded to `onViewChange` — update the prop from there, or drop it to take the imperative path." : "[weasel] setView ignored: this canvas is controlled by its `view` prop and has no `onViewChange`, so the value has nowhere to go. Update the prop, or drop it to take the imperative path.")), o?.(d), a && l();
29
29
  return;
30
30
  }
31
- h.current = d;
32
- for (let e of g.current) e(d);
31
+ g.current = d;
32
+ for (let e of _.current) e(d);
33
33
  o?.(d), l();
34
- }, [_]), x = n((e) => {
35
- b(typeof e == "function" ? e(y()) : e, !0);
36
- }, [b, y]), S = n((e) => {
37
- b(e, !1);
38
- }, [b]), C = n((e) => (g.current.add(e), () => {
39
- g.current.delete(e);
40
- }), []), w = r(!1);
34
+ }, [v]), S = r((e) => {
35
+ x(typeof e == "function" ? e(b()) : e, !0);
36
+ }, [x, b]), C = r((e) => {
37
+ x(e, !1);
38
+ }, [x]), w = r((e) => (_.current.add(e), () => {
39
+ _.current.delete(e);
40
+ }), []), T = i(!1);
41
41
  return {
42
- viewRef: h,
43
- controlled: m,
44
- setView: x,
45
- setViewFromInput: S,
46
- getView: y,
47
- subscribeView: C,
42
+ viewRef: g,
43
+ controlled: h,
44
+ setView: S,
45
+ setViewFromInput: C,
46
+ getView: b,
47
+ subscribeView: w,
48
48
  adoptControlled: () => {
49
- if (!m) return;
50
- let e = h.current;
51
- w.current ||= e.x !== m.x || e.y !== m.y || e.scale.x !== m.scale.x || e.scale.y !== m.scale.y, h.current = m;
49
+ h && (T.current ||= !t(g.current, h), g.current = h);
52
50
  },
53
- announceControlled: n(() => {
54
- if (w.current) {
55
- w.current = !1;
56
- for (let e of g.current) e(h.current);
51
+ announceControlled: r(() => {
52
+ if (T.current) {
53
+ T.current = !1;
54
+ for (let e of _.current) e(g.current);
57
55
  }
58
56
  }, []),
59
- clearSubscribers: n(() => {
60
- g.current.clear();
57
+ clearSubscribers: r(() => {
58
+ _.current.clear();
61
59
  }, [])
62
60
  };
63
61
  }
64
62
  //#endregion
65
- export { o as useCanvasView };
63
+ export { s as useCanvasView };
66
64
 
67
65
  //# sourceMappingURL=useCanvasView.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"useCanvasView.js","names":["IDENTITY","useCanvasView","viewProp","opts","derived","controlledView","normalizeView","viewRef","useRef","defaultView","viewSubsRef","live","useLatest","controlledWarnedRef","getView","useCallback","thunk","write","forward","bounds","size","redraw","valid","next","clamped","clampView","controlled","imperative","fn","setView","setViewFromInput","subscribeView","controlledMovedRef","was"],"sources":["../../src/canvas/useCanvasView.ts"],"sourcesContent":["/**\n * A camera: held in a ref when uncontrolled, taken from the `view` option when\n * controlled, with one write path and one subscriber list over both.\n * `<SceneCanvas>` makes the one its `<Canvas>` looks through, and each\n * `<CanvasView>` makes its own.\n */\nimport { useCallback, useRef, type MutableRefObject } from 'react';\nimport { useLatest } from '@weasel-js/react';\nimport type { Dims } from 'core/layers/render';\nimport type { Bounds } from 'core/viewport/fitViewToBounds';\nimport { clampView } from 'core/viewport/clampView';\nimport { normalizeView, type View } from 'core/viewport/view';\n\n/** The view props a canvas takes for its camera. */\nexport interface CameraProps {\n /** Controlled viewport. When supplied, the canvas does not own the value —\n * the consumer must supply `onViewChange` and re-render with the new\n * view. See `View` JSDoc for the camera-position convention. */\n view?: View;\n /** Initial viewport for the uncontrolled path. Default `{x:0, y:0}`. */\n defaultView?: View;\n /** Fires whenever the viewport changes — in both controlled and\n * uncontrolled modes. */\n onViewChange?: (next: View) => void;\n /**\n * Optional world-space rect that constrains pan: every write to the view passes through\n * `clampView` first, keeping the visible rect inside `viewBounds`. An axis where the\n * visible rect is larger than the bounds is centered. Has no effect on `scale`.\n */\n viewBounds?: { x: number; y: number; width: number; height: number };\n}\n\n/** Inputs to {@link useCanvasView}: the camera's view props and what a write\n * needs from whatever the camera looks through. */\nexport interface CanvasViewOptions {\n /** Supplied, the camera is controlled. A thunk is a camera derived from\n * something else, read fresh at every {@link CanvasViewState.getView}. */\n view: View | (() => View) | undefined;\n defaultView: View | undefined;\n onViewChange: ((next: View) => void) | undefined;\n viewBounds: Bounds | undefined;\n /** The size the camera looks through, which `viewBounds` clamps against. */\n dims: () => Dims;\n requestRedraw: () => void;\n}\n\n/** What `useCanvasView` hands back. The owner decides when in a commit\n * the two controlled steps run, since they have to sit among its own effects. */\nexport interface CanvasViewState {\n /** The view in effect. Written by a write when uncontrolled and by\n * `adoptControlled` when controlled. A camera derived by a thunk is not\n * kept here: read `getView`. */\n viewRef: MutableRefObject<View>;\n /** This render's controlled view, absent when the camera is uncontrolled or\n * derived. A new object every render, so compare its numbers. */\n controlled: View | undefined;\n /** The imperative write. Controlled, it is forwarded to `onViewChange`\n * instead of applied, and says so once per mount. */\n setView: (next: View | ((current: View) => View)) => void;\n /** The write input makes. Controlled, forwarding it to `onViewChange` is how\n * the owner hears of a pan at all, so nothing is warned. */\n setViewFromInput: (next: View) => void;\n getView: () => View;\n subscribeView: (fn: (v: View) => void) => () => void;\n /** Take this render's controlled view into `viewRef`. Call from an insertion effect. */\n adoptControlled: () => void;\n /** Tell subscribers of a controlled view that moved since the last call.\n * A controlled view reaches the ref with no write, so this is where they\n * hear of it. Call from a layout effect. */\n announceControlled: () => void;\n /** Drop every subscriber, on unmount. */\n clearSubscribers: () => void;\n}\n\nconst IDENTITY: View = { x: 0, y: 0, scale: { x: 1, y: 1 } };\n\nexport function useCanvasView(opts: CanvasViewOptions): CanvasViewState {\n const { view: viewProp, defaultView, onViewChange, viewBounds, dims, requestRedraw } = opts;\n const derived = typeof viewProp === 'function' ? viewProp : undefined;\n const controlledView = viewProp === undefined || derived ? undefined : normalizeView(viewProp as View);\n // The uncontrolled view lives in a ref, not `useState`, so a camera moving\n // at 60 Hz costs no React render; DOM that mirrors it subscribes instead.\n const viewRef = useRef<View>(controlledView ?? normalizeView(defaultView ?? IDENTITY));\n const viewSubsRef = useRef<Set<(v: View) => void>>(new Set());\n const live = useLatest({\n controlled: viewProp !== undefined, derived, onViewChange, viewBounds, dims, requestRedraw,\n });\n const controlledWarnedRef = useRef(false);\n\n const getView = useCallback((): View => {\n const thunk = live.current.derived;\n return thunk ? normalizeView(thunk()) : viewRef.current;\n }, [live]);\n\n const write = useCallback((next: View, imperative: boolean): void => {\n const {\n controlled, derived, onViewChange: forward, viewBounds: bounds, dims: size, requestRedraw: redraw,\n } = live.current;\n const valid = normalizeView(next);\n const clamped = bounds ? clampView(valid, bounds, size()) : valid;\n if (controlled) {\n // The prop is the authority; writing the ref would put pixels and props\n // out of step with nothing to reconcile them.\n if (imperative && !controlledWarnedRef.current) {\n controlledWarnedRef.current = true;\n console.warn(forward\n ? '[weasel] setView not applied locally: this canvas is controlled by its `view` prop. The value was forwarded to `onViewChange` — update the prop from there, or drop it to take the imperative path.'\n : '[weasel] setView ignored: this canvas is controlled by its `view` prop and has no `onViewChange`, so the value has nowhere to go. Update the prop, or drop it to take the imperative path.');\n }\n forward?.(clamped);\n // A derived camera has no prop to change, so nothing else repaints it.\n if (derived) redraw();\n return;\n }\n viewRef.current = clamped;\n for (const fn of viewSubsRef.current) fn(clamped);\n forward?.(clamped);\n redraw();\n }, [live]);\n\n const setView = useCallback((next: View | ((current: View) => View)) => {\n write(typeof next === 'function' ? next(getView()) : next, true);\n }, [write, getView]);\n const setViewFromInput = useCallback((next: View) => { write(next, false); }, [write]);\n\n const subscribeView = useCallback((fn: (v: View) => void) => {\n viewSubsRef.current.add(fn);\n return () => { viewSubsRef.current.delete(fn); };\n }, []);\n\n const controlledMovedRef = useRef(false);\n // A closure over this render's prop, not a ref read: the owner calls it\n // from the same render's insertion effect.\n const adoptControlled = (): void => {\n if (!controlledView) return;\n const was = viewRef.current;\n controlledMovedRef.current ||= was.x !== controlledView.x || was.y !== controlledView.y\n || was.scale.x !== controlledView.scale.x || was.scale.y !== controlledView.scale.y;\n viewRef.current = controlledView;\n };\n const announceControlled = useCallback(() => {\n if (!controlledMovedRef.current) return;\n controlledMovedRef.current = false;\n for (const fn of viewSubsRef.current) fn(viewRef.current);\n }, []);\n const clearSubscribers = useCallback(() => { viewSubsRef.current.clear(); }, []);\n\n return {\n viewRef, controlled: controlledView, setView, setViewFromInput, getView, subscribeView,\n adoptControlled, announceControlled, clearSubscribers,\n };\n}\n"],"mappings":";;;;;AA0EA,IAAMA,IAAiB;CAAE,GAAG;CAAG,GAAG;CAAG,OAAO;EAAE,GAAG;EAAG,GAAG;CAAE;AAAE;AAE3D,SAAgBC,EAAc,GAA0C;CACtE,IAAM,EAAE,MAAMC,GAAU,gBAAa,iBAAc,eAAY,SAAM,qBAAkBC,GACjFC,IAAU,OAAOF,KAAa,aAAaA,IAAW,KAAA,GACtDG,IAAiBH,MAAa,KAAA,KAAaE,IAAU,KAAA,IAAYE,EAAcJ,CAAgB,GAG/FK,IAAUC,EAAaH,KAAkBC,EAAcG,KAAeT,CAAQ,CAAC,GAC/EU,IAAcF,kBAA+B,IAAI,IAAI,CAAC,GACtDG,IAAOC,EAAU;EACrB,YAAYV,MAAa,KAAA;EAAW;EAAS;EAAc;EAAY;EAAM;CAC/E,CAAC,GACKW,IAAsBL,EAAO,EAAK,GAElCM,IAAUC,QAAwB;EACtC,IAAMC,IAAQL,EAAK,QAAQ;EAC3B,OAAOK,IAAQV,EAAcU,EAAM,CAAC,IAAIT,EAAQ;CAClD,GAAG,CAACI,CAAI,CAAC,GAEHM,IAAQF,GAAa,GAAY,MAA8B;EACnE,IAAM,EACJ,eAAY,YAAS,cAAcG,GAAS,YAAYC,GAAQ,MAAMC,GAAM,eAAeC,MACzFV,EAAK,SACHW,IAAQhB,EAAciB,CAAI,GAC1BC,IAAUL,IAASM,EAAUH,GAAOH,GAAQC,EAAK,CAAC,IAAIE;EAC5D,IAAII,GAAY;GAWd,AARIC,KAAc,CAACd,EAAoB,YACrC,EAAoB,UAAU,IAC9B,QAAQ,KAAKK,IACT,wMACA,4LAA4L,IAElMA,IAAUM,CAAO,GAEbpB,KAASiB,EAAO;GACpB;EACF;EACA,EAAQ,UAAUG;EAClB,KAAK,IAAMI,KAAMlB,EAAY,SAAS,EAAGc,CAAO;EAEhD,AADAN,IAAUM,CAAO,GACjBH,EAAO;CACT,GAAG,CAACV,CAAI,CAAC,GAEHkB,IAAUd,GAAa,MAA2C;EACtE,EAAM,OAAOQ,KAAS,aAAaA,EAAKT,EAAQ,CAAC,IAAIS,GAAM,EAAI;CACjE,GAAG,CAACN,GAAOH,CAAO,CAAC,GACbgB,IAAmBf,GAAa,MAAe;EAAE,EAAMQ,GAAM,EAAK;CAAG,GAAG,CAACN,CAAK,CAAC,GAE/Ec,IAAgBhB,GAAa,OACjCL,EAAY,QAAQ,IAAIkB,CAAE,SACb;EAAE,EAAY,QAAQ,OAAOA,CAAE;CAAG,IAC9C,CAAC,CAAC,GAECI,IAAqBxB,EAAO,EAAK;CAiBvC,OAAO;EACL;EAAS,YAAYH;EAAgB;EAAS;EAAkB;EAAS;EACzE,uBAhBkC;GAClC,IAAI,CAACA,GAAgB;GACrB,IAAM4B,IAAM1B,EAAQ;GAGpB,AAFA,EAAmB,YAAY0B,EAAI,MAAM5B,EAAe,KAAK4B,EAAI,MAAM5B,EAAe,KACjF4B,EAAI,MAAM,MAAM5B,EAAe,MAAM,KAAK4B,EAAI,MAAM,MAAM5B,EAAe,MAAM,GACpF,EAAQ,UAAUA;EACpB;EAUmB,oBATQU,QAAkB;GAC3C,IAAKiB,EAAmB,SACxB;MAAmB,UAAU;IAC7B,KAAK,IAAMJ,KAAMlB,EAAY,SAAS,EAAGH,EAAQ,OAAO;GAD3B;EAE/B,GAAG,CAAC,CAKe;EAAoB,kBAJdQ,QAAkB;GAAE,EAAY,QAAQ,MAAM;EAAG,GAAG,CAAC,CAIvC;CACvC;AACF"}
1
+ {"version":3,"file":"useCanvasView.js","names":["IDENTITY","useCanvasView","viewProp","opts","derived","controlledView","normalizeView","viewRef","useRef","defaultView","viewSubsRef","live","useLatest","controlledWarnedRef","getView","useCallback","thunk","write","forward","bounds","size","redraw","valid","next","clamped","clampView","controlled","imperative","fn","setView","setViewFromInput","subscribeView","controlledMovedRef","sameView"],"sources":["../../src/canvas/useCanvasView.ts"],"sourcesContent":["/**\n * A camera: held in a ref when uncontrolled, taken from the `view` option when\n * controlled, with one write path and one subscriber list over both.\n * `<SceneCanvas>` makes the one its `<Canvas>` looks through, and each\n * `<CanvasView>` makes its own.\n */\nimport { useCallback, useRef, type MutableRefObject } from 'react';\nimport { useLatest } from '@weasel-js/react';\nimport type { Dims } from 'core/layers/render';\nimport type { Bounds } from 'core/viewport/fitViewToBounds';\nimport { clampView } from 'core/viewport/clampView';\nimport { normalizeView, sameView, type View } from 'core/viewport/view';\n\n/** The view props a canvas takes for its camera. */\nexport interface CameraProps {\n /** Controlled viewport. When supplied, the canvas does not own the value —\n * the consumer must supply `onViewChange` and re-render with the new\n * view. See `View` JSDoc for the camera-position convention. */\n view?: View;\n /** Initial viewport for the uncontrolled path. Default `{x:0, y:0}`. */\n defaultView?: View;\n /** Fires whenever the viewport changes — in both controlled and\n * uncontrolled modes. */\n onViewChange?: (next: View) => void;\n /**\n * Optional world-space rect that constrains pan: every write to the view passes through\n * `clampView` first, keeping the visible rect inside `viewBounds`. An axis where the\n * visible rect is larger than the bounds is centered. Has no effect on `scale`.\n */\n viewBounds?: { x: number; y: number; width: number; height: number };\n}\n\n/** Inputs to {@link useCanvasView}: the camera's view props and what a write\n * needs from whatever the camera looks through. */\nexport interface CanvasViewOptions {\n /** Supplied, the camera is controlled. A thunk is a camera derived from\n * something else, read fresh at every {@link CanvasViewState.getView}. */\n view: View | (() => View) | undefined;\n defaultView: View | undefined;\n onViewChange: ((next: View) => void) | undefined;\n viewBounds: Bounds | undefined;\n /** The size the camera looks through, which `viewBounds` clamps against. */\n dims: () => Dims;\n requestRedraw: () => void;\n}\n\n/** What `useCanvasView` hands back. The owner decides when in a commit\n * the two controlled steps run, since they have to sit among its own effects. */\nexport interface CanvasViewState {\n /** The view in effect. Written by a write when uncontrolled and by\n * `adoptControlled` when controlled. A camera derived by a thunk is not\n * kept here: read `getView`. */\n viewRef: MutableRefObject<View>;\n /** This render's controlled view, absent when the camera is uncontrolled or\n * derived. A new object every render, so compare its numbers. */\n controlled: View | undefined;\n /** The imperative write. Controlled, it is forwarded to `onViewChange`\n * instead of applied, and says so once per mount. */\n setView: (next: View | ((current: View) => View)) => void;\n /** The write input makes. Controlled, forwarding it to `onViewChange` is how\n * the owner hears of a pan at all, so nothing is warned. */\n setViewFromInput: (next: View) => void;\n getView: () => View;\n subscribeView: (fn: (v: View) => void) => () => void;\n /** Take this render's controlled view into `viewRef`. Call from an insertion effect. */\n adoptControlled: () => void;\n /** Tell subscribers of a controlled view that moved since the last call.\n * A controlled view reaches the ref with no write, so this is where they\n * hear of it. Call from a layout effect. */\n announceControlled: () => void;\n /** Drop every subscriber, on unmount. */\n clearSubscribers: () => void;\n}\n\nconst IDENTITY: View = { x: 0, y: 0, scale: { x: 1, y: 1 } };\n\nexport function useCanvasView(opts: CanvasViewOptions): CanvasViewState {\n const { view: viewProp, defaultView, onViewChange, viewBounds, dims, requestRedraw } = opts;\n const derived = typeof viewProp === 'function' ? viewProp : undefined;\n const controlledView = viewProp === undefined || derived ? undefined : normalizeView(viewProp as View);\n // The uncontrolled view lives in a ref, not `useState`, so a camera moving\n // at 60 Hz costs no React render; DOM that mirrors it subscribes instead.\n const viewRef = useRef<View>(controlledView ?? normalizeView(defaultView ?? IDENTITY));\n const viewSubsRef = useRef<Set<(v: View) => void>>(new Set());\n const live = useLatest({\n controlled: viewProp !== undefined, derived, onViewChange, viewBounds, dims, requestRedraw,\n });\n const controlledWarnedRef = useRef(false);\n\n const getView = useCallback((): View => {\n const thunk = live.current.derived;\n return thunk ? normalizeView(thunk()) : viewRef.current;\n }, [live]);\n\n const write = useCallback((next: View, imperative: boolean): void => {\n const {\n controlled, derived, onViewChange: forward, viewBounds: bounds, dims: size, requestRedraw: redraw,\n } = live.current;\n const valid = normalizeView(next);\n const clamped = bounds ? clampView(valid, bounds, size()) : valid;\n if (controlled) {\n // The prop is the authority; writing the ref would put pixels and props\n // out of step with nothing to reconcile them.\n if (imperative && !controlledWarnedRef.current) {\n controlledWarnedRef.current = true;\n console.warn(forward\n ? '[weasel] setView not applied locally: this canvas is controlled by its `view` prop. The value was forwarded to `onViewChange` — update the prop from there, or drop it to take the imperative path.'\n : '[weasel] setView ignored: this canvas is controlled by its `view` prop and has no `onViewChange`, so the value has nowhere to go. Update the prop, or drop it to take the imperative path.');\n }\n forward?.(clamped);\n // A derived camera has no prop to change, so nothing else repaints it.\n if (derived) redraw();\n return;\n }\n viewRef.current = clamped;\n for (const fn of viewSubsRef.current) fn(clamped);\n forward?.(clamped);\n redraw();\n }, [live]);\n\n const setView = useCallback((next: View | ((current: View) => View)) => {\n write(typeof next === 'function' ? next(getView()) : next, true);\n }, [write, getView]);\n const setViewFromInput = useCallback((next: View) => { write(next, false); }, [write]);\n\n const subscribeView = useCallback((fn: (v: View) => void) => {\n viewSubsRef.current.add(fn);\n return () => { viewSubsRef.current.delete(fn); };\n }, []);\n\n const controlledMovedRef = useRef(false);\n // A closure over this render's prop, not a ref read: the owner calls it\n // from the same render's insertion effect.\n const adoptControlled = (): void => {\n if (!controlledView) return;\n controlledMovedRef.current ||= !sameView(viewRef.current, controlledView);\n viewRef.current = controlledView;\n };\n const announceControlled = useCallback(() => {\n if (!controlledMovedRef.current) return;\n controlledMovedRef.current = false;\n for (const fn of viewSubsRef.current) fn(viewRef.current);\n }, []);\n const clearSubscribers = useCallback(() => { viewSubsRef.current.clear(); }, []);\n\n return {\n viewRef, controlled: controlledView, setView, setViewFromInput, getView, subscribeView,\n adoptControlled, announceControlled, clearSubscribers,\n };\n}\n"],"mappings":";;;;;AA0EA,IAAMA,IAAiB;CAAE,GAAG;CAAG,GAAG;CAAG,OAAO;EAAE,GAAG;EAAG,GAAG;CAAE;AAAE;AAE3D,SAAgBC,EAAc,GAA0C;CACtE,IAAM,EAAE,MAAMC,GAAU,gBAAa,iBAAc,eAAY,SAAM,qBAAkBC,GACjFC,IAAU,OAAOF,KAAa,aAAaA,IAAW,KAAA,GACtDG,IAAiBH,MAAa,KAAA,KAAaE,IAAU,KAAA,IAAYE,EAAcJ,CAAgB,GAG/FK,IAAUC,EAAaH,KAAkBC,EAAcG,KAAeT,CAAQ,CAAC,GAC/EU,IAAcF,kBAA+B,IAAI,IAAI,CAAC,GACtDG,IAAOC,EAAU;EACrB,YAAYV,MAAa,KAAA;EAAW;EAAS;EAAc;EAAY;EAAM;CAC/E,CAAC,GACKW,IAAsBL,EAAO,EAAK,GAElCM,IAAUC,QAAwB;EACtC,IAAMC,IAAQL,EAAK,QAAQ;EAC3B,OAAOK,IAAQV,EAAcU,EAAM,CAAC,IAAIT,EAAQ;CAClD,GAAG,CAACI,CAAI,CAAC,GAEHM,IAAQF,GAAa,GAAY,MAA8B;EACnE,IAAM,EACJ,eAAY,YAAS,cAAcG,GAAS,YAAYC,GAAQ,MAAMC,GAAM,eAAeC,MACzFV,EAAK,SACHW,IAAQhB,EAAciB,CAAI,GAC1BC,IAAUL,IAASM,EAAUH,GAAOH,GAAQC,EAAK,CAAC,IAAIE;EAC5D,IAAII,GAAY;GAWd,AARIC,KAAc,CAACd,EAAoB,YACrC,EAAoB,UAAU,IAC9B,QAAQ,KAAKK,IACT,wMACA,4LAA4L,IAElMA,IAAUM,CAAO,GAEbpB,KAASiB,EAAO;GACpB;EACF;EACA,EAAQ,UAAUG;EAClB,KAAK,IAAMI,KAAMlB,EAAY,SAAS,EAAGc,CAAO;EAEhD,AADAN,IAAUM,CAAO,GACjBH,EAAO;CACT,GAAG,CAACV,CAAI,CAAC,GAEHkB,IAAUd,GAAa,MAA2C;EACtE,EAAM,OAAOQ,KAAS,aAAaA,EAAKT,EAAQ,CAAC,IAAIS,GAAM,EAAI;CACjE,GAAG,CAACN,GAAOH,CAAO,CAAC,GACbgB,IAAmBf,GAAa,MAAe;EAAE,EAAMQ,GAAM,EAAK;CAAG,GAAG,CAACN,CAAK,CAAC,GAE/Ec,IAAgBhB,GAAa,OACjCL,EAAY,QAAQ,IAAIkB,CAAE,SACb;EAAE,EAAY,QAAQ,OAAOA,CAAE;CAAG,IAC9C,CAAC,CAAC,GAECI,IAAqBxB,EAAO,EAAK;CAevC,OAAO;EACL;EAAS,YAAYH;EAAgB;EAAS;EAAkB;EAAS;EACzE,uBAdkC;GAClC,AAAKA,MACL,EAAmB,YAAY,CAAC4B,EAAS1B,EAAQ,SAASF,CAAc,GACxE,EAAQ,UAAUA;EACpB;EAUmB,oBATQU,QAAkB;GAC3C,IAAKiB,EAAmB,SACxB;MAAmB,UAAU;IAC7B,KAAK,IAAMJ,KAAMlB,EAAY,SAAS,EAAGH,EAAQ,OAAO;GAD3B;EAE/B,GAAG,CAAC,CAKe;EAAoB,kBAJdQ,QAAkB;GAAE,EAAY,QAAQ,MAAM;EAAG,GAAG,CAAC,CAIvC;CACvC;AACF"}
@@ -0,0 +1,22 @@
1
+ import { sceneToAdapter as e } from "./sceneAdapter.js";
2
+ import { useMemo as t } from "react";
3
+ //#region src/canvas/useSceneAdapter.ts
4
+ function n(n, r = {}) {
5
+ let { selection: i, commitInsert: a, insertLayer: o, poseDescriptor: s } = r;
6
+ return t(() => e(n, {
7
+ selection: i,
8
+ commitInsert: a,
9
+ insertLayer: o,
10
+ poseDescriptor: s
11
+ }), [
12
+ n,
13
+ i,
14
+ a,
15
+ o,
16
+ s
17
+ ]);
18
+ }
19
+ //#endregion
20
+ export { n as useSceneAdapter };
21
+
22
+ //# sourceMappingURL=useSceneAdapter.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useSceneAdapter.js","names":["useSceneAdapter","options","useMemo","sceneToAdapter","scene","selection","commitInsert","insertLayer","poseDescriptor"],"sources":["../../src/canvas/useSceneAdapter.ts"],"sourcesContent":["import { useMemo } from 'react';\nimport type { Scene } from 'core/scene/types';\nimport {\n sceneToAdapter,\n type SceneCanvasAdapter,\n type SceneToAdapterOptions,\n} from './sceneAdapter';\n\n/**\n * React-hook wrapper around `sceneToAdapter`. Memoizes on every option, so\n * the adapter keeps its identity across renders unless one of them changes.\n */\nexport function useSceneAdapter<TData, TLayer extends string, TPose>(\n scene: Scene<TData, TLayer, TPose>,\n options: SceneToAdapterOptions<TData, TLayer, TPose> = {},\n): SceneCanvasAdapter<TData, TLayer, TPose> {\n const {\n selection, commitInsert, insertLayer, poseDescriptor,\n } = options;\n return useMemo(\n () => sceneToAdapter(scene, {\n selection, commitInsert, insertLayer, poseDescriptor,\n }),\n [scene, selection, commitInsert, insertLayer, poseDescriptor],\n );\n}\n"],"mappings":";;;AAYA,SAAgBA,EACd,GACA,IAAuD,CAAC,GACd;CAC1C,IAAM,EACJ,cAAW,iBAAc,gBAAa,sBACpCC;CACJ,OAAOC,QACCC,EAAeC,GAAO;EAC1B;EAAW;EAAc;EAAa;CACxC,CAAC,GACD;EAACA;EAAOC;EAAWC;EAAcC;EAAaC;CAAc,CAC9D;AACF"}
@@ -1,42 +1,39 @@
1
1
  import { useLayoutEffect as e, useRef as t } from "react";
2
2
  import { useLatest as n } from "@weasel-js/react";
3
+ import { heldPointers as r } from "@weasel-js/gestures";
3
4
  //#region src/canvas/useSurfaceInput.ts
4
- function r(r, a) {
5
- let o = n(a), s = t(null);
5
+ function i(r, i) {
6
+ let o = n(i), s = t(null);
6
7
  e(() => {
7
8
  let e = r.current;
8
9
  s.current?.element !== e && (s.current?.detach(), s.current = e ? {
9
10
  element: e,
10
- detach: i(e, o)
11
+ detach: a(e, o)
11
12
  } : null);
12
13
  }), e(() => () => {
13
14
  s.current?.detach(), s.current = null;
14
15
  }, []);
15
16
  }
16
- function i(e, t) {
17
- let n = !1, r = {
17
+ function a(e, t) {
18
+ let n = r(e), i = {
18
19
  pointerdown: () => {
19
- t.current.focusOnPress && e.focus(), n = !0;
20
+ t.current.focusOnPress && e.focus();
20
21
  },
21
22
  pointermove: (r) => {
22
- n || t.current.onHover(e, r);
23
- },
24
- pointerup: () => {
25
- n = !1;
26
- },
27
- pointerleave: () => {
28
- n = !1, t.current.onLeave();
23
+ n.count() === 0 && t.current.onHover(e, r);
29
24
  },
25
+ pointerleave: () => t.current.onLeave(),
30
26
  contextmenu: (e) => e.preventDefault(),
31
27
  focus: (e) => t.current.onFocus?.(e),
32
28
  blur: (e) => t.current.onBlur?.(e)
33
29
  };
34
- for (let [t, n] of Object.entries(r)) e.addEventListener(t, n);
30
+ for (let [t, n] of Object.entries(i)) e.addEventListener(t, n);
35
31
  return () => {
36
- for (let [t, n] of Object.entries(r)) e.removeEventListener(t, n);
32
+ for (let [t, n] of Object.entries(i)) e.removeEventListener(t, n);
33
+ n.release();
37
34
  };
38
35
  }
39
36
  //#endregion
40
- export { r as useSurfaceInput };
37
+ export { i as useSurfaceInput };
41
38
 
42
39
  //# sourceMappingURL=useSurfaceInput.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"useSurfaceInput.js","names":["useSurfaceInput","live","useLatest","handlers","attached","useRef","useLayoutEffect","element","elementRef","listen","pressed","listeners","event","type","fn"],"sources":["../../src/canvas/useSurfaceInput.ts"],"sourcesContent":["import { useLayoutEffect, useRef } from 'react';\nimport { useLatest } from '@weasel-js/react';\n\n/** What a surface does with the input its element receives. */\nexport interface SurfaceInputHandlers {\n /** Focus the element on a press, so keys reach it without a click first. */\n focusOnPress: boolean;\n onFocus?: (event: FocusEvent) => void;\n onBlur?: (event: FocusEvent) => void;\n /** The pointer moved over the element with no button held. */\n onHover(element: HTMLElement, event: PointerEvent): void;\n /** The pointer left the element. */\n onLeave(): void;\n}\n\n/**\n * Listen on whichever element `elementRef` holds, moving the listeners when\n * it holds another. Handlers are read at event time, so a new set never\n * re-attaches.\n *\n * Tracks one fact about the pointer, whether a button is down, because hover\n * stands down for as long as one is. Everything else about a press is the\n * gesture dispatcher's, which listens on this same element.\n *\n * Suppresses the browser's context menu: right-click is a gesture surface\n * tools may consume.\n */\nexport function useSurfaceInput(\n elementRef: { readonly current: HTMLElement | null },\n handlers: SurfaceInputHandlers,\n): void {\n const live = useLatest(handlers);\n const attached = useRef<{ element: HTMLElement; detach: () => void } | null>(null);\n\n useLayoutEffect(() => {\n const element = elementRef.current;\n if (attached.current?.element === element) return;\n attached.current?.detach();\n attached.current = element ? { element, detach: listen(element, live) } : null;\n });\n useLayoutEffect(() => () => {\n attached.current?.detach();\n attached.current = null;\n }, []);\n}\n\nfunction listen(element: HTMLElement, live: { readonly current: SurfaceInputHandlers }): () => void {\n let pressed = false;\n const listeners: Record<string, (event: Event) => void> = {\n pointerdown: () => {\n if (live.current.focusOnPress) element.focus();\n pressed = true;\n },\n pointermove: (event) => {\n if (!pressed) live.current.onHover(element, event as PointerEvent);\n },\n pointerup: () => { pressed = false; },\n pointerleave: () => {\n pressed = false;\n live.current.onLeave();\n },\n contextmenu: (event) => event.preventDefault(),\n focus: (event) => live.current.onFocus?.(event as FocusEvent),\n blur: (event) => live.current.onBlur?.(event as FocusEvent),\n };\n for (const [type, fn] of Object.entries(listeners)) element.addEventListener(type, fn);\n return () => {\n for (const [type, fn] of Object.entries(listeners)) element.removeEventListener(type, fn);\n };\n}\n"],"mappings":";;;AA2BA,SAAgBA,EACd,GACA,GACM;CACN,IAAMC,IAAOC,EAAUC,CAAQ,GACzBC,IAAWC,EAA4D,IAAI;CAQjF,AANAC,QAAsB;EACpB,IAAMC,IAAUC,EAAW;EAC3B,AAAIJ,EAAS,SAAS,YAAYG,MAClCH,EAAS,SAAS,OAAO,GACzB,EAAS,UAAUG,IAAU;GAAE;GAAS,QAAQE,EAAOF,GAASN,CAAI;EAAE,IAAI;CAC5E,CAAC,GACDK,cAA4B;EAE1B,AADAF,EAAS,SAAS,OAAO,GACzB,EAAS,UAAU;CACrB,GAAG,CAAC,CAAC;AACP;AAEA,SAASK,EAAO,GAAsB,GAA8D;CAClG,IAAIC,IAAU,IACRC,IAAoD;EACxD,mBAAmB;GAEjB,AADIV,EAAK,QAAQ,gBAAcM,EAAQ,MAAM,GAC7C,IAAU;EACZ;EACA,cAAc,MAAU;GACtB,AAAKG,KAAST,EAAK,QAAQ,QAAQM,GAASK,CAAqB;EACnE;EACA,iBAAiB;GAAE,IAAU;EAAO;EACpC,oBAAoB;GAElB,AADA,IAAU,IACVX,EAAK,QAAQ,QAAQ;EACvB;EACA,cAAc,MAAUW,EAAM,eAAe;EAC7C,QAAQ,MAAUX,EAAK,QAAQ,UAAUW,CAAmB;EAC5D,OAAO,MAAUX,EAAK,QAAQ,SAASW,CAAmB;CAC5D;CACA,KAAK,IAAM,CAACC,GAAMC,MAAO,OAAO,QAAQH,CAAS,GAAG,EAAQ,iBAAiBE,GAAMC,CAAE;CACrF,aAAa;EACX,KAAK,IAAM,CAACD,GAAMC,MAAO,OAAO,QAAQH,CAAS,GAAG,EAAQ,oBAAoBE,GAAMC,CAAE;CAC1F;AACF"}
1
+ {"version":3,"file":"useSurfaceInput.js","names":["useSurfaceInput","live","useLatest","handlers","attached","useRef","useLayoutEffect","element","elementRef","listen","held","heldPointers","listeners","event","type","fn"],"sources":["../../src/canvas/useSurfaceInput.ts"],"sourcesContent":["import { useLayoutEffect, useRef } from 'react';\nimport { heldPointers } from '@weasel-js/gestures';\nimport { useLatest } from '@weasel-js/react';\n\n/** What a surface does with the input its element receives. */\nexport interface SurfaceInputHandlers {\n /** Focus the element on a press, so keys reach it without a click first. */\n focusOnPress: boolean;\n onFocus?: (event: FocusEvent) => void;\n onBlur?: (event: FocusEvent) => void;\n /** The pointer moved over the element with no button held. */\n onHover(element: HTMLElement, event: PointerEvent): void;\n /** The pointer left the element. */\n onLeave(): void;\n}\n\n/**\n * Listen on whichever element `elementRef` holds, moving the listeners when\n * it holds another. Handlers are read at event time, so a new set never\n * re-attaches.\n *\n * Hover stands down while a pointer is held on the element, which\n * `heldPointers` answers for this and the gesture dispatcher alike. Everything\n * else about a press is the dispatcher's, which listens on this same element.\n *\n * Suppresses the browser's context menu: right-click is a gesture surface\n * tools may consume.\n */\nexport function useSurfaceInput(\n elementRef: { readonly current: HTMLElement | null },\n handlers: SurfaceInputHandlers,\n): void {\n const live = useLatest(handlers);\n const attached = useRef<{ element: HTMLElement; detach: () => void } | null>(null);\n\n useLayoutEffect(() => {\n const element = elementRef.current;\n if (attached.current?.element === element) return;\n attached.current?.detach();\n attached.current = element ? { element, detach: listen(element, live) } : null;\n });\n useLayoutEffect(() => () => {\n attached.current?.detach();\n attached.current = null;\n }, []);\n}\n\nfunction listen(element: HTMLElement, live: { readonly current: SurfaceInputHandlers }): () => void {\n const held = heldPointers(element);\n const listeners: Record<string, (event: Event) => void> = {\n pointerdown: () => {\n if (live.current.focusOnPress) element.focus();\n },\n pointermove: (event) => {\n if (held.count() === 0) live.current.onHover(element, event as PointerEvent);\n },\n pointerleave: () => live.current.onLeave(),\n contextmenu: (event) => event.preventDefault(),\n focus: (event) => live.current.onFocus?.(event as FocusEvent),\n blur: (event) => live.current.onBlur?.(event as FocusEvent),\n };\n for (const [type, fn] of Object.entries(listeners)) element.addEventListener(type, fn);\n return () => {\n for (const [type, fn] of Object.entries(listeners)) element.removeEventListener(type, fn);\n held.release();\n };\n}\n"],"mappings":";;;;AA4BA,SAAgBA,EACd,GACA,GACM;CACN,IAAMC,IAAOC,EAAUC,CAAQ,GACzBC,IAAWC,EAA4D,IAAI;CAQjF,AANAC,QAAsB;EACpB,IAAMC,IAAUC,EAAW;EAC3B,AAAIJ,EAAS,SAAS,YAAYG,MAClCH,EAAS,SAAS,OAAO,GACzB,EAAS,UAAUG,IAAU;GAAE;GAAS,QAAQE,EAAOF,GAASN,CAAI;EAAE,IAAI;CAC5E,CAAC,GACDK,cAA4B;EAE1B,AADAF,EAAS,SAAS,OAAO,GACzB,EAAS,UAAU;CACrB,GAAG,CAAC,CAAC;AACP;AAEA,SAASK,EAAO,GAAsB,GAA8D;CAClG,IAAMC,IAAOC,EAAaJ,CAAO,GAC3BK,IAAoD;EACxD,mBAAmB;GACjB,AAAIX,EAAK,QAAQ,gBAAcM,EAAQ,MAAM;EAC/C;EACA,cAAc,MAAU;GACtB,AAAIG,EAAK,MAAM,MAAM,KAAGT,EAAK,QAAQ,QAAQM,GAASM,CAAqB;EAC7E;EACA,oBAAoBZ,EAAK,QAAQ,QAAQ;EACzC,cAAc,MAAUY,EAAM,eAAe;EAC7C,QAAQ,MAAUZ,EAAK,QAAQ,UAAUY,CAAmB;EAC5D,OAAO,MAAUZ,EAAK,QAAQ,SAASY,CAAmB;CAC5D;CACA,KAAK,IAAM,CAACC,GAAMC,MAAO,OAAO,QAAQH,CAAS,GAAG,EAAQ,iBAAiBE,GAAMC,CAAE;CACrF,aAAa;EACX,KAAK,IAAM,CAACD,GAAMC,MAAO,OAAO,QAAQH,CAAS,GAAG,EAAQ,oBAAoBE,GAAMC,CAAE;EACxF,EAAK,QAAQ;CACf;AACF"}
@@ -3,49 +3,57 @@ import { createContext as t, useContext as n, useMemo as r, useRef as i } from "
3
3
  import { createEmitter as a } from "@weasel-js/registry";
4
4
  import { jsx as o } from "react/jsx-runtime";
5
5
  //#region src/canvas/viewRegistry.tsx
6
- var s = t(null);
7
- function c({ children: t }) {
8
- let n = i(/* @__PURE__ */ new Map()), c = i(0), l = i(0), f = i(null), p = r(() => {
6
+ function s(e, t, n) {
7
+ if (!e) return {
8
+ x: t,
9
+ y: n
10
+ };
11
+ let [r, i] = e.clientToWorld(t, n), a = e.view();
12
+ return {
13
+ x: (r - a.x) * a.scale.x,
14
+ y: (i - a.y) * a.scale.y
15
+ };
16
+ }
17
+ var c = t(null);
18
+ function l({ children: t }) {
19
+ let n = i(/* @__PURE__ */ new Map()), l = i(0), u = i(0), p = i(null), m = r(() => {
9
20
  let t = a(), r = () => {
10
- l.current++, t.emit();
21
+ u.current++, t.emit();
11
22
  }, i = () => [...n.current.keys()].sort((e, t) => e.order - t.order || n.current.get(e) - n.current.get(t));
12
23
  return {
13
24
  resolver: e({
14
25
  views: () => {
15
- let e = f.current, t = e?.view() ?? d, n = e?.dims() ?? u;
26
+ let e = p.current, t = e?.view() ?? f, n = e?.dims() ?? d;
16
27
  return i().filter((e) => e.interactive).map((e) => e.layer.resolvable(t, n));
17
28
  },
18
- root: () => f.current?.view() ?? d,
19
- canvasOrigin: () => f.current?.origin() ?? {
20
- left: 0,
21
- top: 0
22
- },
23
- occluded: (e, t) => f.current?.claimsAbove(e, t) ?? !1
29
+ root: () => p.current?.view() ?? f,
30
+ toLocal: (e, t) => s(p.current, e, t),
31
+ occluded: (e, t) => p.current?.claimsAbove(e, t) ?? !1
24
32
  }),
25
- register: (e) => (n.current.set(e, c.current++), r(), () => {
33
+ register: (e) => (n.current.set(e, l.current++), r(), () => {
26
34
  n.current.delete(e), r();
27
35
  }),
28
36
  list: i,
29
- getVersion: () => l.current,
37
+ getVersion: () => u.current,
30
38
  subscribe: t.subscribe,
31
- surface: () => f.current,
32
- attachSurface: (e) => (f.current = e, () => {
33
- f.current === e && (f.current = null);
39
+ surface: () => p.current,
40
+ attachSurface: (e) => (p.current = e, () => {
41
+ p.current === e && (p.current = null);
34
42
  })
35
43
  };
36
44
  }, []);
37
- return /* @__PURE__ */ o(s.Provider, {
38
- value: p,
45
+ return /* @__PURE__ */ o(c.Provider, {
46
+ value: m,
39
47
  children: t
40
48
  });
41
49
  }
42
- function l() {
43
- return n(s);
50
+ function u() {
51
+ return n(c);
44
52
  }
45
- var u = {
53
+ var d = {
46
54
  width: 0,
47
55
  height: 0
48
- }, d = {
56
+ }, f = {
49
57
  x: 0,
50
58
  y: 0,
51
59
  scale: {
@@ -54,6 +62,6 @@ var u = {
54
62
  }
55
63
  };
56
64
  //#endregion
57
- export { d as IDENTITY_VIEW, u as UNMEASURED_DIMS, c as ViewRegistryProvider, l as useOptionalViewRegistry };
65
+ export { f as IDENTITY_VIEW, d as UNMEASURED_DIMS, l as ViewRegistryProvider, s as surfaceLocal, u as useOptionalViewRegistry };
58
66
 
59
67
  //# sourceMappingURL=viewRegistry.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"viewRegistry.js","names":["ViewRegistryContext","createContext","ViewRegistryProvider","entriesRef","useRef","seqRef","versionRef","surfaceRef","registry","useMemo","listeners","createEmitter","changed","ordered","a","b","createViewResolver","surface","view","IDENTITY_VIEW","dims","UNMEASURED_DIMS","r","x","y","reg","handle","useOptionalViewRegistry","useContext"],"sources":["../../src/canvas/viewRegistry.tsx"],"sourcesContent":["/**\n * The seam between a canvas surface and the views drawn on it.\n *\n * Traffic runs both ways: the surface attaches a handle onto its frame and its\n * layer stack, and each view registers what it contributes back — the viewport\n * node it paints into and the record the gesture dispatcher routes its events\n * to. Both sides are thunks, read at draw and dispatch time, so neither can\n * serve the other a frame that has already moved.\n */\nimport { createContext, useContext, useMemo, useRef, type ReactNode } from 'react';\nimport type { Dims, RenderLayer } from 'core/layers/render';\nimport type { LayerHit } from 'affordances/types';\nimport type { ChromeState } from 'core/selection/chromeState';\nimport type { View } from 'core/viewport/view';\nimport type { ViewportLayer } from 'features/viewports/viewportLayer';\nimport type { DispatcherViewTarget } from '@weasel-js/routing/internal';\nimport { createViewResolver, type ViewResolver } from 'features/viewports/viewResolver';\nimport { createEmitter } from '@weasel-js/registry';\n\n/**\n * What a view needs from the surface hosting it.\n *\n * @internal Attached by the surface, read by `<CanvasView>`.\n */\nexport interface SurfaceHandle {\n /** Client-space origin of the canvas element. A rect fact: it knows nothing\n * of a `clientToWorld` override, so convert with {@link clientToWorld}. */\n origin(): { left: number; top: number };\n /** A client point in the surface's own world — the conversion the surface\n * itself makes, its `clientToWorld` override included. */\n clientToWorld(clientX: number, clientY: number): [number, number];\n /** The outer camera. */\n view(): View;\n /** The canvas's CSS-pixel size. */\n dims(): Dims;\n /** The stack a view paints through its own camera, unless it narrows it. */\n layers(): readonly RenderLayer<unknown>[];\n requestRedraw(): void;\n /** The canvas's own view chrome — its selection, its overlay-aware bounds.\n * What a `<CanvasView>` answers for itself, answered for view zero. */\n chromeState(): ChromeState;\n /** Hit-test the externally registered layers at a world point, against the\n * frame and draw envelope of whichever view is asking. The public\n * `hitTestExtras` is this with the canvas's own frame and envelope. */\n hitTestExtras(\n worldX: number,\n worldY: number,\n view: View,\n dims: Dims,\n data: unknown,\n /** Consider only these layers — the ones the asking view paints. A layer\n * asked on a frame it was not drawn in answers for the wrong pixels. */\n only?: readonly RenderLayer<unknown>[],\n ): { layerId: string; hit: LayerHit } | null;\n /** Whether a registered layer claims a canvas-local point on the surface's\n * own frame. Registered layers paint over every view, so such a point is\n * theirs even inside a view's rect. */\n claimsAbove(x: number, y: number): boolean;\n}\n\n/**\n * What one view contributes to the surface hosting it.\n *\n * @internal Written by `<CanvasView>`, read by the surface. Consumers declare\n * a view, not a registration.\n */\nexport interface ViewRegistration {\n id: string;\n /**\n * Paint and hit order, low to high. Views declared as props take their array\n * index; a view mounted as a child takes `Infinity`, so it lands after them\n * however React happens to order the mount effects.\n */\n order: number;\n /** The viewport node this view paints into. Its `resolvable` is what routes\n * input here. */\n layer: ViewportLayer<unknown>;\n /** `false` keeps input over the view on the canvas beneath it. */\n interactive: boolean;\n /** `false` leaves `layer` for a host to draw — a HUD window's interior — so\n * the surface does not paint it too. */\n paint: boolean;\n /** Everything about dispatching to this view except its id. */\n target: Omit<DispatcherViewTarget, 'id'>;\n /** Whether a scene layer reaches the screen in this view. For input that\n * reaches a view without a dispatch — hover — so it judges this view's\n * paint rather than the surface's. */\n layerIsPainted(layerId: string): boolean;\n}\n\n/** @internal */\nexport interface ViewRegistry {\n /** Returns an unregister. */\n register(reg: ViewRegistration): () => void;\n /** Live registrations in paint order. */\n list(): readonly ViewRegistration[];\n /** Bumped whenever the list changes. Pair with {@link ViewRegistry.subscribe}\n * for `useSyncExternalStore`. */\n getVersion(): number;\n subscribe(fn: () => void): () => void;\n /** Which view a client point belongs to, sticky for a captured pointer.\n * One per surface: every caller that routes input — the dispatcher, pinch,\n * hover — must agree about where a point landed. */\n readonly resolver: ViewResolver;\n /** The hosting surface, or `null` before one attaches. */\n surface(): SurfaceHandle | null;\n /** Returns a detach, which leaves a handle attached since alone. */\n attachSurface(handle: SurfaceHandle): () => void;\n}\n\nconst ViewRegistryContext = createContext<ViewRegistry | null>(null);\n\n/** Mounts the registry for one surface. `<SceneCanvas>` mounts one; a view\n * declaring itself as a child must be inside it. */\nexport function ViewRegistryProvider({ children }: { children: ReactNode }) {\n const entriesRef = useRef(new Map<ViewRegistration, number>());\n const seqRef = useRef(0);\n const versionRef = useRef(0);\n const surfaceRef = useRef<SurfaceHandle | null>(null);\n\n const registry = useMemo<ViewRegistry>(() => {\n const listeners = createEmitter();\n const changed = (): void => {\n versionRef.current++;\n listeners.emit();\n };\n const ordered = (): ViewRegistration[] => [...entriesRef.current.keys()].sort((a, b) => (\n a.order - b.order || entriesRef.current.get(a)! - entriesRef.current.get(b)!\n ));\n const resolver = createViewResolver({\n views: () => {\n const surface = surfaceRef.current;\n const view = surface?.view() ?? IDENTITY_VIEW;\n const dims = surface?.dims() ?? UNMEASURED_DIMS;\n return ordered().filter((r) => r.interactive).map((r) => r.layer.resolvable(view, dims));\n },\n root: () => surfaceRef.current?.view() ?? IDENTITY_VIEW,\n canvasOrigin: () => surfaceRef.current?.origin() ?? { left: 0, top: 0 },\n occluded: (x, y) => surfaceRef.current?.claimsAbove(x, y) ?? false,\n });\n return {\n resolver,\n register: (reg) => {\n entriesRef.current.set(reg, seqRef.current++);\n changed();\n return () => { entriesRef.current.delete(reg); changed(); };\n },\n list: ordered,\n getVersion: () => versionRef.current,\n subscribe: listeners.subscribe,\n surface: () => surfaceRef.current,\n attachSurface: (handle) => {\n surfaceRef.current = handle;\n return () => { if (surfaceRef.current === handle) surfaceRef.current = null; };\n },\n };\n }, []);\n\n return <ViewRegistryContext.Provider value={registry}>{children}</ViewRegistryContext.Provider>;\n}\n\n/** The surface's view registry, or `null` outside one. */\nexport function useOptionalViewRegistry(): ViewRegistry | null {\n return useContext(ViewRegistryContext);\n}\n\n/** The `Dims` a surface reports before it has measured itself. */\nexport const UNMEASURED_DIMS: Dims = { width: 0, height: 0 };\n\n/** The camera a view resolves against before its surface attaches. */\nexport const IDENTITY_VIEW: View = { x: 0, y: 0, scale: { x: 1, y: 1 } };\n"],"mappings":";;;;;AA8GA,IAAMA,IAAsBC,EAAmC,IAAI;AAInE,SAAgBC,EAAqB,EAAE,eAAqC;CAC1E,IAAMC,IAAaC,kBAAO,IAAI,IAA8B,CAAC,GACvDC,IAASD,EAAO,CAAC,GACjBE,IAAaF,EAAO,CAAC,GACrBG,IAAaH,EAA6B,IAAI,GAE9CI,IAAWC,QAA4B;EAC3C,IAAMC,IAAYC,EAAc,GAC1BC,UAAsB;GAE1B,AADA,EAAW,WACXF,EAAU,KAAK;EACjB,GACMG,UAAoC,CAAC,GAAGV,EAAW,QAAQ,KAAK,CAAC,CAAC,CAAC,MAAM,GAAG,MAChFW,EAAE,QAAQC,EAAE,SAASZ,EAAW,QAAQ,IAAIW,CAAC,IAAKX,EAAW,QAAQ,IAAIY,CAAC,CAC3E;EAYD,OAAO;GACL,UAZeC,EAAmB;IAClC,aAAa;KACX,IAAMC,IAAUV,EAAW,SACrBW,IAAOD,GAAS,KAAK,KAAKE,GAC1BC,IAAOH,GAAS,KAAK,KAAKI;KAChC,OAAOR,EAAQ,CAAC,CAAC,QAAQ,MAAMS,EAAE,WAAW,CAAC,CAAC,KAAK,MAAMA,EAAE,MAAM,WAAWJ,GAAME,CAAI,CAAC;IACzF;IACA,YAAYb,EAAW,SAAS,KAAK,KAAKY;IAC1C,oBAAoBZ,EAAW,SAAS,OAAO,KAAK;KAAE,MAAM;KAAG,KAAK;IAAE;IACtE,WAAW,GAAG,MAAMA,EAAW,SAAS,YAAYgB,GAAGC,CAAC,KAAK;GAC/D,CAEE;GACA,WAAW,OACTrB,EAAW,QAAQ,IAAIsB,GAAK,EAAO,SAAS,GAC5Cb,EAAQ,SACK;IAAkC,AAAhCT,EAAW,QAAQ,OAAOsB,CAAG,GAAGb,EAAQ;GAAG;GAE5D,MAAMC;GACN,kBAAkBP,EAAW;GAC7B,WAAWI,EAAU;GACrB,eAAeH,EAAW;GAC1B,gBAAgB,OACd,EAAW,UAAUmB,SACR;IAAE,AAAInB,EAAW,YAAYmB,MAAQ,EAAW,UAAU;GAAM;EAEjF;CACF,GAAG,CAAC,CAAC;CAEL,OAAO,kBAAC1B,EAAoB,UAArB;EAA8B,OAAOQ;EAAW;CAAuC,CAAA;AAChG;AAGA,SAAgBmB,IAA+C;CAC7D,OAAOC,EAAW5B,CAAmB;AACvC;AAGA,IAAaqB,IAAwB;CAAE,OAAO;CAAG,QAAQ;AAAE,GAG9CF,IAAsB;CAAE,GAAG;CAAG,GAAG;CAAG,OAAO;EAAE,GAAG;EAAG,GAAG;CAAE;AAAE"}
1
+ {"version":3,"file":"viewRegistry.js","names":["surfaceLocal","surface","clientX","clientY","worldX","worldY","view","ViewRegistryContext","createContext","ViewRegistryProvider","entriesRef","useRef","seqRef","versionRef","surfaceRef","registry","useMemo","listeners","createEmitter","changed","ordered","a","b","createViewResolver","IDENTITY_VIEW","dims","UNMEASURED_DIMS","r","x","y","reg","handle","useOptionalViewRegistry","useContext"],"sources":["../../src/canvas/viewRegistry.tsx"],"sourcesContent":["/**\n * The seam between a canvas surface and the views drawn on it.\n *\n * Traffic runs both ways: the surface attaches a handle onto its frame and its\n * layer stack, and each view registers what it contributes back — the viewport\n * node it paints into and the record the gesture dispatcher routes its events\n * to. Both sides are thunks, read at draw and dispatch time, so neither can\n * serve the other a frame that has already moved.\n */\nimport { createContext, useContext, useMemo, useRef, type ReactNode } from 'react';\nimport type { Dims, RenderLayer } from 'core/layers/render';\nimport type { LayerHit } from 'affordances/types';\nimport type { ChromeState } from 'core/selection/chromeState';\nimport type { View } from 'core/viewport/view';\nimport type { ViewportLayer } from 'features/viewports/viewportLayer';\nimport type { DispatcherViewTarget } from '@weasel-js/routing/internal';\nimport { createViewResolver, type ViewResolver } from 'features/viewports/viewResolver';\nimport { createEmitter } from '@weasel-js/registry';\n\n/**\n * What a view needs from the surface hosting it.\n *\n * @internal Attached by the surface, read by `<CanvasView>`.\n */\nexport interface SurfaceHandle {\n /** A client point in the surface's own world — the conversion the surface\n * itself makes, its `clientToWorld` override included. */\n clientToWorld(clientX: number, clientY: number): [number, number];\n /** The outer camera. */\n view(): View;\n /** The canvas's CSS-pixel size. */\n dims(): Dims;\n /** The stack a view paints through its own camera, unless it narrows it. */\n layers(): readonly RenderLayer<unknown>[];\n requestRedraw(): void;\n /** The canvas's own view chrome — its selection, its overlay-aware bounds.\n * What a `<CanvasView>` answers for itself, answered for view zero. */\n chromeState(): ChromeState;\n /** Hit-test the externally registered layers at a world point, against the\n * frame and draw envelope of whichever view is asking. The public\n * `hitTestExtras` is this with the canvas's own frame and envelope. */\n hitTestExtras(\n worldX: number,\n worldY: number,\n view: View,\n dims: Dims,\n data: unknown,\n /** Consider only these layers — the ones the asking view paints. A layer\n * asked on a frame it was not drawn in answers for the wrong pixels. */\n only?: readonly RenderLayer<unknown>[],\n ): { layerId: string; hit: LayerHit } | null;\n /** Whether a registered layer claims a canvas-local point on the surface's\n * own frame. Registered layers paint over every view, so such a point is\n * theirs even inside a view's rect. */\n claimsAbove(x: number, y: number): boolean;\n}\n\n/**\n * A client point in the surface's CSS pixels, from its top-left: the surface's\n * own world point put back through its camera, so a `clientToWorld` override\n * reaches every view declared on it.\n *\n * @internal\n */\nexport function surfaceLocal(\n surface: Pick<SurfaceHandle, 'clientToWorld' | 'view'> | null,\n clientX: number,\n clientY: number,\n): { x: number; y: number } {\n if (!surface) return { x: clientX, y: clientY };\n const [worldX, worldY] = surface.clientToWorld(clientX, clientY);\n const view = surface.view();\n return { x: (worldX - view.x) * view.scale.x, y: (worldY - view.y) * view.scale.y };\n}\n\n/**\n * What one view contributes to the surface hosting it.\n *\n * @internal Written by `<CanvasView>`, read by the surface. Consumers declare\n * a view, not a registration.\n */\nexport interface ViewRegistration {\n id: string;\n /**\n * Paint and hit order, low to high. Views declared as props take their array\n * index; a view mounted as a child takes `Infinity`, so it lands after them\n * however React happens to order the mount effects.\n */\n order: number;\n /** The viewport node this view paints into. Its `resolvable` is what routes\n * input here. */\n layer: ViewportLayer<unknown>;\n /** `false` keeps input over the view on the canvas beneath it. */\n interactive: boolean;\n /** `false` leaves `layer` for a host to draw — a HUD window's interior — so\n * the surface does not paint it too. */\n paint: boolean;\n /** Everything about dispatching to this view except its id. */\n target: Omit<DispatcherViewTarget, 'id'>;\n /** Whether a scene layer reaches the screen in this view. For input that\n * reaches a view without a dispatch — hover — so it judges this view's\n * paint rather than the surface's. */\n layerIsPainted(layerId: string): boolean;\n}\n\n/** @internal */\nexport interface ViewRegistry {\n /** Returns an unregister. */\n register(reg: ViewRegistration): () => void;\n /** Live registrations in paint order. */\n list(): readonly ViewRegistration[];\n /** Bumped whenever the list changes. Pair with {@link ViewRegistry.subscribe}\n * for `useSyncExternalStore`. */\n getVersion(): number;\n subscribe(fn: () => void): () => void;\n /** Which view a client point belongs to, sticky for a captured pointer.\n * One per surface: every caller that routes input — the dispatcher, pinch,\n * hover — must agree about where a point landed. */\n readonly resolver: ViewResolver;\n /** The hosting surface, or `null` before one attaches. */\n surface(): SurfaceHandle | null;\n /** Returns a detach, which leaves a handle attached since alone. */\n attachSurface(handle: SurfaceHandle): () => void;\n}\n\nconst ViewRegistryContext = createContext<ViewRegistry | null>(null);\n\n/** Mounts the registry for one surface. `<SceneCanvas>` mounts one; a view\n * declaring itself as a child must be inside it. */\nexport function ViewRegistryProvider({ children }: { children: ReactNode }) {\n const entriesRef = useRef(new Map<ViewRegistration, number>());\n const seqRef = useRef(0);\n const versionRef = useRef(0);\n const surfaceRef = useRef<SurfaceHandle | null>(null);\n\n const registry = useMemo<ViewRegistry>(() => {\n const listeners = createEmitter();\n const changed = (): void => {\n versionRef.current++;\n listeners.emit();\n };\n const ordered = (): ViewRegistration[] => [...entriesRef.current.keys()].sort((a, b) => (\n a.order - b.order || entriesRef.current.get(a)! - entriesRef.current.get(b)!\n ));\n const resolver = createViewResolver({\n views: () => {\n const surface = surfaceRef.current;\n const view = surface?.view() ?? IDENTITY_VIEW;\n const dims = surface?.dims() ?? UNMEASURED_DIMS;\n return ordered().filter((r) => r.interactive).map((r) => r.layer.resolvable(view, dims));\n },\n root: () => surfaceRef.current?.view() ?? IDENTITY_VIEW,\n toLocal: (clientX, clientY) => surfaceLocal(surfaceRef.current, clientX, clientY),\n occluded: (x, y) => surfaceRef.current?.claimsAbove(x, y) ?? false,\n });\n return {\n resolver,\n register: (reg) => {\n entriesRef.current.set(reg, seqRef.current++);\n changed();\n return () => { entriesRef.current.delete(reg); changed(); };\n },\n list: ordered,\n getVersion: () => versionRef.current,\n subscribe: listeners.subscribe,\n surface: () => surfaceRef.current,\n attachSurface: (handle) => {\n surfaceRef.current = handle;\n return () => { if (surfaceRef.current === handle) surfaceRef.current = null; };\n },\n };\n }, []);\n\n return <ViewRegistryContext.Provider value={registry}>{children}</ViewRegistryContext.Provider>;\n}\n\n/** The surface's view registry, or `null` outside one. */\nexport function useOptionalViewRegistry(): ViewRegistry | null {\n return useContext(ViewRegistryContext);\n}\n\n/** The `Dims` a surface reports before it has measured itself. */\nexport const UNMEASURED_DIMS: Dims = { width: 0, height: 0 };\n\n/** The camera a view resolves against before its surface attaches. */\nexport const IDENTITY_VIEW: View = { x: 0, y: 0, scale: { x: 1, y: 1 } };\n"],"mappings":";;;;;AAgEA,SAAgBA,EACd,GACA,GACA,GAC0B;CAC1B,IAAI,CAACC,GAAS,OAAO;EAAE,GAAGC;EAAS,GAAGC;CAAQ;CAC9C,IAAM,CAACC,GAAQC,KAAUJ,EAAQ,cAAcC,GAASC,CAAO,GACzDG,IAAOL,EAAQ,KAAK;CAC1B,OAAO;EAAE,IAAIG,IAASE,EAAK,KAAKA,EAAK,MAAM;EAAG,IAAID,IAASC,EAAK,KAAKA,EAAK,MAAM;CAAE;AACpF;AAoDA,IAAMC,IAAsBC,EAAmC,IAAI;AAInE,SAAgBC,EAAqB,EAAE,eAAqC;CAC1E,IAAMC,IAAaC,kBAAO,IAAI,IAA8B,CAAC,GACvDC,IAASD,EAAO,CAAC,GACjBE,IAAaF,EAAO,CAAC,GACrBG,IAAaH,EAA6B,IAAI,GAE9CI,IAAWC,QAA4B;EAC3C,IAAMC,IAAYC,EAAc,GAC1BC,UAAsB;GAE1B,AADA,EAAW,WACXF,EAAU,KAAK;EACjB,GACMG,UAAoC,CAAC,GAAGV,EAAW,QAAQ,KAAK,CAAC,CAAC,CAAC,MAAM,GAAG,MAChFW,EAAE,QAAQC,EAAE,SAASZ,EAAW,QAAQ,IAAIW,CAAC,IAAKX,EAAW,QAAQ,IAAIY,CAAC,CAC3E;EAYD,OAAO;GACL,UAZeC,EAAmB;IAClC,aAAa;KACX,IAAMtB,IAAUa,EAAW,SACrBR,IAAOL,GAAS,KAAK,KAAKuB,GAC1BC,IAAOxB,GAAS,KAAK,KAAKyB;KAChC,OAAON,EAAQ,CAAC,CAAC,QAAQ,MAAMO,EAAE,WAAW,CAAC,CAAC,KAAK,MAAMA,EAAE,MAAM,WAAWrB,GAAMmB,CAAI,CAAC;IACzF;IACA,YAAYX,EAAW,SAAS,KAAK,KAAKU;IAC1C,UAAU,GAAS,MAAYxB,EAAac,EAAW,SAASZ,GAASC,CAAO;IAChF,WAAW,GAAG,MAAMW,EAAW,SAAS,YAAYc,GAAGC,CAAC,KAAK;GAC/D,CAEE;GACA,WAAW,OACTnB,EAAW,QAAQ,IAAIoB,GAAK,EAAO,SAAS,GAC5CX,EAAQ,SACK;IAAkC,AAAhCT,EAAW,QAAQ,OAAOoB,CAAG,GAAGX,EAAQ;GAAG;GAE5D,MAAMC;GACN,kBAAkBP,EAAW;GAC7B,WAAWI,EAAU;GACrB,eAAeH,EAAW;GAC1B,gBAAgB,OACd,EAAW,UAAUiB,SACR;IAAE,AAAIjB,EAAW,YAAYiB,MAAQ,EAAW,UAAU;GAAM;EAEjF;CACF,GAAG,CAAC,CAAC;CAEL,OAAO,kBAACxB,EAAoB,UAArB;EAA8B,OAAOQ;EAAW;CAAuC,CAAA;AAChG;AAGA,SAAgBiB,IAA+C;CAC7D,OAAOC,EAAW1B,CAAmB;AACvC;AAGA,IAAamB,IAAwB;CAAE,OAAO;CAAG,QAAQ;AAAE,GAG9CF,IAAsB;CAAE,GAAG;CAAG,GAAG;CAAG,OAAO;EAAE,GAAG;EAAG,GAAG;CAAE;AAAE"}
@@ -1,6 +1,11 @@
1
1
  import { wrapWithPoseRotation as e } from "./poseRotation.js";
2
+ import { overChildren as t, splitOverChildren as n } from "./overChildren.js";
2
3
  //#region src/canvas/wrapNodeOutput.ts
3
- function t(t, n, r) {
4
+ function r(e, r, a) {
5
+ let { under: o, over: s } = n(e), c = i(o, r, a);
6
+ return s.length > 0 ? [...c, t(i(s, r, a))] : c;
7
+ }
8
+ function i(t, n, r) {
4
9
  let i = e(t, n);
5
10
  return r !== 1 && i.length > 0 ? [{
6
11
  kind: "group",
@@ -9,6 +14,6 @@ function t(t, n, r) {
9
14
  }] : i;
10
15
  }
11
16
  //#endregion
12
- export { t as wrapNodeOutput };
17
+ export { r as wrapNodeOutput };
13
18
 
14
19
  //# sourceMappingURL=wrapNodeOutput.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"wrapNodeOutput.js","names":["wrapNodeOutput","rotated","wrapWithPoseRotation","cmds","pose","alpha"],"sources":["../../src/canvas/wrapNodeOutput.ts"],"sourcesContent":["/**\n * The per-node wraps a scene render applies on top of whatever `drawOne`\n * returned — pose rotation, then a per-id alpha multiplier. They sit outside\n * `drawOne` on purpose: consumer-supplied painters get rotation and dimming\n * without having to reimplement either.\n *\n * Shared by the main canvas (`buildSceneLayer`) and the detached renders\n * (`buildSceneViewCommands`) so a scene painted off-screen matches the same\n * scene painted on it.\n */\nimport type { DrawCommand } from '@weasel-js/renderer';\nimport { wrapWithPoseRotation } from './poseRotation';\n\n/** Rotate `cmds` about the pose's AABB center and multiply by `alpha`.\n * Both wraps are skipped when they would be no-ops (zero/absent rotation,\n * alpha exactly 1, empty command list), so an upright opaque node emits the\n * painter's commands untouched. Alpha nests OUTSIDE rotation. */\nexport function wrapNodeOutput(\n cmds: DrawCommand[],\n pose: unknown,\n alpha: number,\n): DrawCommand[] {\n const rotated = wrapWithPoseRotation(cmds, pose);\n if (alpha !== 1 && rotated.length > 0) {\n return [{ kind: 'group', alpha, children: rotated }];\n }\n return rotated;\n}\n"],"mappings":";;AAiBA,SAAgBA,EACd,GACA,GACA,GACe;CACf,IAAMC,IAAUC,EAAqBC,GAAMC,CAAI;CAI/C,OAHIC,MAAU,KAAKJ,EAAQ,SAAS,IAC3B,CAAC;EAAE,MAAM;EAAS;EAAO,UAAUA;CAAQ,CAAC,IAE9CA;AACT"}
1
+ {"version":3,"file":"wrapNodeOutput.js","names":["wrapNodeOutput","splitOverChildren","cmds","wrapped","wrapPart","under","pose","alpha","over","overChildren","rotated","wrapWithPoseRotation"],"sources":["../../src/canvas/wrapNodeOutput.ts"],"sourcesContent":["/**\n * The per-node wraps a scene render applies on top of whatever `drawOne`\n * returned — pose rotation, then a per-id alpha multiplier. They sit outside\n * `drawOne` on purpose: consumer-supplied painters get rotation and dimming\n * without having to reimplement either.\n *\n * Shared by the main canvas (`buildSceneLayer`) and the detached renders\n * (`buildSceneViewCommands`) so a scene painted off-screen matches the same\n * scene painted on it.\n */\nimport type { DrawCommand } from '@weasel-js/renderer';\nimport { wrapWithPoseRotation } from './poseRotation';\nimport { overChildren, splitOverChildren } from './overChildren';\n\n/** Rotate `cmds` about the pose's AABB center and multiply by `alpha`.\n * Both wraps are skipped when they would be no-ops (zero/absent rotation,\n * alpha exactly 1, empty command list), so an upright opaque node emits the\n * painter's commands untouched. Alpha nests OUTSIDE rotation. */\nexport function wrapNodeOutput(\n cmds: DrawCommand[],\n pose: unknown,\n alpha: number,\n): DrawCommand[] {\n const { under, over } = splitOverChildren(cmds);\n const wrapped = wrapPart(under, pose, alpha);\n // What a painter asked to draw over the node's children is wrapped apart, so it is still over them.\n return over.length > 0 ? [...wrapped, overChildren(wrapPart(over, pose, alpha))] : wrapped;\n}\n\nfunction wrapPart(cmds: DrawCommand[], pose: unknown, alpha: number): DrawCommand[] {\n const rotated = wrapWithPoseRotation(cmds, pose);\n if (alpha !== 1 && rotated.length > 0) {\n return [{ kind: 'group', alpha, children: rotated }];\n }\n return rotated;\n}\n"],"mappings":";;;AAkBA,SAAgBA,EACd,GACA,GACA,GACe;CACf,IAAM,EAAE,UAAO,YAASC,EAAkBC,CAAI,GACxCC,IAAUC,EAASC,GAAOC,GAAMC,CAAK;CAE3C,OAAOC,EAAK,SAAS,IAAI,CAAC,GAAGL,GAASM,EAAaL,EAASI,GAAMF,GAAMC,CAAK,CAAC,CAAC,IAAIJ;AACrF;AAEA,SAASC,EAAS,GAAqB,GAAe,GAA8B;CAClF,IAAMM,IAAUC,EAAqBT,GAAMI,CAAI;CAI/C,OAHIC,MAAU,KAAKG,EAAQ,SAAS,IAC3B,CAAC;EAAE,MAAM;EAAS;EAAO,UAAUA;CAAQ,CAAC,IAE9CA;AACT"}