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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (488) hide show
  1. package/CHANGELOG.md +275 -0
  2. package/dist/WeaselProvider.js +1 -1
  3. package/dist/affordances/pathAnchors.js +10 -6
  4. package/dist/affordances/pathAnchors.js.map +1 -1
  5. package/dist/animation/poseTarget.js +46 -15
  6. package/dist/animation/poseTarget.js.map +1 -1
  7. package/dist/animation/poseTransition.js +3 -3
  8. package/dist/animation/poseTransition.js.map +1 -1
  9. package/dist/animation/reflow.js +3 -6
  10. package/dist/animation/reflow.js.map +1 -1
  11. package/dist/animation/rig/bindRig.js +2 -2
  12. package/dist/animation/rig/bindRig.js.map +1 -1
  13. package/dist/animation/rig/skinBinding.js +2 -2
  14. package/dist/animation/rig/skinBinding.js.map +1 -1
  15. package/dist/barrel/canvas.js +5 -1
  16. package/dist/barrel/chrome.js +3 -1
  17. package/dist/barrel/paint.js +4 -4
  18. package/dist/barrel/paths.js +9 -4
  19. package/dist/barrel/renderer.js +3 -2
  20. package/dist/barrel/routing.js +4 -4
  21. package/dist/barrel/scene.js +7 -1
  22. package/dist/barrel/text.js +2 -2
  23. package/dist/barrel/viewport.js +6 -2
  24. package/dist/canvas/Canvas.js +2 -1
  25. package/dist/canvas/Canvas.js.map +1 -1
  26. package/dist/canvas/CanvasView.js +23 -34
  27. package/dist/canvas/CanvasView.js.map +1 -1
  28. package/dist/canvas/ModalityHud.js +1 -1
  29. package/dist/canvas/NodeShape.js +5 -1
  30. package/dist/canvas/NodeShape.js.map +1 -1
  31. package/dist/canvas/SceneCanvas/ActionsProviderIfRoot.js +1 -1
  32. package/dist/canvas/SceneCanvas/DepRegistryProviderIfRoot.js +1 -1
  33. package/dist/canvas/SceneCanvas/PointerProviderIfRoot.js +1 -1
  34. package/dist/canvas/SceneCanvas/SceneCanvasInput.js +2 -0
  35. package/dist/canvas/SceneCanvas/SceneCanvasInput.js.map +1 -1
  36. package/dist/canvas/SceneCanvas/SurfaceDeclarations.js +23 -0
  37. package/dist/canvas/SceneCanvas/SurfaceDeclarations.js.map +1 -0
  38. package/dist/canvas/SceneCanvas/canvasPassThrough.js +2 -1
  39. package/dist/canvas/SceneCanvas/canvasPassThrough.js.map +1 -1
  40. package/dist/canvas/SceneCanvas/defaultNodeRouting.js +2 -2
  41. package/dist/canvas/SceneCanvas/defaultNodeRouting.js.map +1 -1
  42. package/dist/canvas/SceneCanvas/props.js.map +1 -1
  43. package/dist/canvas/SceneCanvas/scenePointQuery.js +27 -5
  44. package/dist/canvas/SceneCanvas/scenePointQuery.js.map +1 -1
  45. package/dist/canvas/SceneCanvas/useActionTunings.js +6 -1
  46. package/dist/canvas/SceneCanvas/useActionTunings.js.map +1 -1
  47. package/dist/canvas/SceneCanvas/useCanvasDevtoolsRoot.js +1 -0
  48. package/dist/canvas/SceneCanvas/useCanvasDevtoolsRoot.js.map +1 -1
  49. package/dist/canvas/SceneCanvas/useCanvasTools.js +10 -2
  50. package/dist/canvas/SceneCanvas/useCanvasTools.js.map +1 -1
  51. package/dist/canvas/SceneCanvas/usePreviewGhostLayer.js +25 -19
  52. package/dist/canvas/SceneCanvas/usePreviewGhostLayer.js.map +1 -1
  53. package/dist/canvas/SceneCanvas/useSceneCanvasHandle.js +9 -3
  54. package/dist/canvas/SceneCanvas/useSceneCanvasHandle.js.map +1 -1
  55. package/dist/canvas/SceneCanvas/useSceneCanvasLayers.js +17 -2
  56. package/dist/canvas/SceneCanvas/useSceneCanvasLayers.js.map +1 -1
  57. package/dist/canvas/SceneCanvas/useSceneCanvasTools.js +2 -0
  58. package/dist/canvas/SceneCanvas/useSceneCanvasTools.js.map +1 -1
  59. package/dist/canvas/SceneCanvas/useSceneGestureDispatcher.js +19 -4
  60. package/dist/canvas/SceneCanvas/useSceneGestureDispatcher.js.map +1 -1
  61. package/dist/canvas/SceneCanvas/useScenePicking.js +31 -6
  62. package/dist/canvas/SceneCanvas/useScenePicking.js.map +1 -1
  63. package/dist/canvas/SceneCanvas/useSceneSelectTool.js +4 -5
  64. package/dist/canvas/SceneCanvas/useSceneSelectTool.js.map +1 -1
  65. package/dist/canvas/SceneCanvas/useSceneStandardActions.js +7 -4
  66. package/dist/canvas/SceneCanvas/useSceneStandardActions.js.map +1 -1
  67. package/dist/canvas/SceneCanvas.js +30 -12
  68. package/dist/canvas/SceneCanvas.js.map +1 -1
  69. package/dist/canvas/ViewMarker.js +35 -0
  70. package/dist/canvas/ViewMarker.js.map +1 -0
  71. package/dist/canvas/accentColor.js +26 -3
  72. package/dist/canvas/accentColor.js.map +1 -1
  73. package/dist/canvas/buildSceneTree.js +14 -20
  74. package/dist/canvas/buildSceneTree.js.map +1 -1
  75. package/dist/canvas/copiesAbove.js +1 -1
  76. package/dist/canvas/copiesAbove.js.map +1 -1
  77. package/dist/canvas/defaultDrawOne.js +11 -6
  78. package/dist/canvas/defaultDrawOne.js.map +1 -1
  79. package/dist/canvas/deps/areaSelect.js +5 -2
  80. package/dist/canvas/deps/areaSelect.js.map +1 -1
  81. package/dist/canvas/deps/booleans.js +3 -2
  82. package/dist/canvas/deps/booleans.js.map +1 -1
  83. package/dist/canvas/deps/enteredGroup.js +81 -0
  84. package/dist/canvas/deps/enteredGroup.js.map +1 -0
  85. package/dist/canvas/deps/hitTestArea.js +28 -16
  86. package/dist/canvas/deps/hitTestArea.js.map +1 -1
  87. package/dist/canvas/deps/insert.js +10 -38
  88. package/dist/canvas/deps/insert.js.map +1 -1
  89. package/dist/canvas/deps/insertSite.js +39 -0
  90. package/dist/canvas/deps/insertSite.js.map +1 -0
  91. package/dist/canvas/deps/lassoSelect.js +6 -3
  92. package/dist/canvas/deps/lassoSelect.js.map +1 -1
  93. package/dist/canvas/deps/pathEditCommits.js +3 -7
  94. package/dist/canvas/deps/pathEditCommits.js.map +1 -1
  95. package/dist/canvas/deps/pathLeaf.js +1 -1
  96. package/dist/canvas/deps/pathLeaf.js.map +1 -1
  97. package/dist/canvas/deps/slice.js +2 -1
  98. package/dist/canvas/deps/slice.js.map +1 -1
  99. package/dist/canvas/derivedPath.js +14 -6
  100. package/dist/canvas/derivedPath.js.map +1 -1
  101. package/dist/canvas/doubleClickTarget.js +9 -11
  102. package/dist/canvas/doubleClickTarget.js.map +1 -1
  103. package/dist/canvas/drawnBounds.js +26 -9
  104. package/dist/canvas/drawnBounds.js.map +1 -1
  105. package/dist/canvas/inheritedPaints.js +55 -0
  106. package/dist/canvas/inheritedPaints.js.map +1 -0
  107. package/dist/canvas/nodeShapes/derivedPainter.js +15 -16
  108. package/dist/canvas/nodeShapes/derivedPainter.js.map +1 -1
  109. package/dist/canvas/nodeShapes/nodePaint.js +7 -34
  110. package/dist/canvas/nodeShapes/nodePaint.js.map +1 -1
  111. package/dist/canvas/nodeShapes/pathPainter.js +7 -7
  112. package/dist/canvas/nodeShapes/pathPainter.js.map +1 -1
  113. package/dist/canvas/nodeShapes/rectFallbackPainter.js +4 -5
  114. package/dist/canvas/nodeShapes/rectFallbackPainter.js.map +1 -1
  115. package/dist/canvas/nodeShapes/shapePainter.js +9 -9
  116. package/dist/canvas/nodeShapes/shapePainter.js.map +1 -1
  117. package/dist/canvas/nodeShapes/textPainter.js +13 -12
  118. package/dist/canvas/nodeShapes/textPainter.js.map +1 -1
  119. package/dist/canvas/ownClip.js +24 -0
  120. package/dist/canvas/ownClip.js.map +1 -0
  121. package/dist/canvas/paintCull.js.map +1 -1
  122. package/dist/canvas/pickIndex/nodeBroadBox.js +86 -0
  123. package/dist/canvas/pickIndex/nodeBroadBox.js.map +1 -0
  124. package/dist/canvas/pickIndex/scenePickIndex.js +208 -0
  125. package/dist/canvas/pickIndex/scenePickIndex.js.map +1 -0
  126. package/dist/canvas/pickReach.js +3 -3
  127. package/dist/canvas/pickReach.js.map +1 -1
  128. package/dist/canvas/pickWalk.js +17 -91
  129. package/dist/canvas/pickWalk.js.map +1 -1
  130. package/dist/canvas/poseCascade.js +1 -1
  131. package/dist/canvas/poseCascade.js.map +1 -1
  132. package/dist/canvas/publishPreviews.js +97 -0
  133. package/dist/canvas/publishPreviews.js.map +1 -0
  134. package/dist/canvas/renderSceneToPixels.js +1 -1
  135. package/dist/canvas/renderSceneToPixels.js.map +1 -1
  136. package/dist/canvas/sceneAdapter.js +7 -5
  137. package/dist/canvas/sceneAdapter.js.map +1 -1
  138. package/dist/canvas/sceneClipboard.js +17 -10
  139. package/dist/canvas/sceneClipboard.js.map +1 -1
  140. package/dist/canvas/scenePickReads.js +37 -0
  141. package/dist/canvas/scenePickReads.js.map +1 -0
  142. package/dist/canvas/scenePickSource.js +74 -0
  143. package/dist/canvas/scenePickSource.js.map +1 -0
  144. package/dist/canvas/sceneSlotWiring.js +9 -3
  145. package/dist/canvas/sceneSlotWiring.js.map +1 -1
  146. package/dist/canvas/sceneViewRender.js +9 -4
  147. package/dist/canvas/sceneViewRender.js.map +1 -1
  148. package/dist/canvas/shownNodes.js +15 -0
  149. package/dist/canvas/shownNodes.js.map +1 -0
  150. package/dist/canvas/surfaceContribution.js +12 -8
  151. package/dist/canvas/surfaceContribution.js.map +1 -1
  152. package/dist/canvas/useCanvasView.js +4 -2
  153. package/dist/canvas/useCanvasView.js.map +1 -1
  154. package/dist/canvas/useLayerStack.js +6 -2
  155. package/dist/canvas/useLayerStack.js.map +1 -1
  156. package/dist/canvas/useSceneAdapter.js +5 -3
  157. package/dist/canvas/useSceneAdapter.js.map +1 -1
  158. package/dist/canvas/useViewAim.js +121 -0
  159. package/dist/canvas/useViewAim.js.map +1 -0
  160. package/dist/canvas/useViewNesting.js +3 -2
  161. package/dist/canvas/useViewNesting.js.map +1 -1
  162. package/dist/canvas/useViewRegistrySurface.js +4 -2
  163. package/dist/canvas/useViewRegistrySurface.js.map +1 -1
  164. package/dist/canvas/viewMarkers.js +63 -0
  165. package/dist/canvas/viewMarkers.js.map +1 -0
  166. package/dist/canvas/viewRegistry.js +27 -1
  167. package/dist/canvas/viewRegistry.js.map +1 -1
  168. package/dist/clipboard.d.ts +2 -2
  169. package/dist/{clone-C3e0OTuF.d.ts → clone-qb2e1F5p.d.ts} +2 -2
  170. package/dist/clone.d.ts +3 -3
  171. package/dist/core/isPlainObject.js +2 -2
  172. package/dist/core/layers/layerGroup.js.map +1 -1
  173. package/dist/core/pages/pageEdits.js +2 -3
  174. package/dist/core/pages/pageEdits.js.map +1 -1
  175. package/dist/core/paint/meshKindLazy.js +59 -2
  176. package/dist/core/paint/meshKindLazy.js.map +1 -1
  177. package/dist/core/scene/NodeRouting.js.map +1 -1
  178. package/dist/core/scene/arrivalWindow.js +1 -1
  179. package/dist/core/scene/arrivalWindow.js.map +1 -1
  180. package/dist/core/scene/clipNothing.js +10 -0
  181. package/dist/core/scene/clipNothing.js.map +1 -0
  182. package/dist/core/scene/declaredFieldSetters.js +44 -0
  183. package/dist/core/scene/declaredFieldSetters.js.map +1 -0
  184. package/dist/core/scene/depMemo.js +14 -3
  185. package/dist/core/scene/depMemo.js.map +1 -1
  186. package/dist/core/scene/dependents.js +9 -1
  187. package/dist/core/scene/dependents.js.map +1 -1
  188. package/dist/core/scene/derivedParts.js +10 -11
  189. package/dist/core/scene/derivedParts.js.map +1 -1
  190. package/dist/core/scene/documentOf.js +43 -0
  191. package/dist/core/scene/documentOf.js.map +1 -0
  192. package/dist/core/scene/effectivePose.js +44 -15
  193. package/dist/core/scene/effectivePose.js.map +1 -1
  194. package/dist/core/scene/fieldEdits.js +17 -14
  195. package/dist/core/scene/fieldEdits.js.map +1 -1
  196. package/dist/core/scene/fontReads.js +37 -0
  197. package/dist/core/scene/fontReads.js.map +1 -0
  198. package/dist/core/scene/historyForm.js +4 -4
  199. package/dist/core/scene/historyForm.js.map +1 -1
  200. package/dist/core/scene/inheritedPaint.js +97 -0
  201. package/dist/core/scene/inheritedPaint.js.map +1 -0
  202. package/dist/core/scene/kitOps.js +3 -23
  203. package/dist/core/scene/kitOps.js.map +1 -1
  204. package/dist/core/scene/kitRegistry.js +5 -0
  205. package/dist/core/scene/kitRegistry.js.map +1 -1
  206. package/dist/core/scene/layoutPass.js +1 -2
  207. package/dist/core/scene/layoutPass.js.map +1 -1
  208. package/dist/core/scene/nodeAsShown.js +72 -0
  209. package/dist/core/scene/nodeAsShown.js.map +1 -0
  210. package/dist/core/scene/nodeFnFields.js +2 -10
  211. package/dist/core/scene/nodeFnFields.js.map +1 -1
  212. package/dist/core/scene/nodeMemo.js +21 -8
  213. package/dist/core/scene/nodeMemo.js.map +1 -1
  214. package/dist/core/scene/nodeStore.js +2 -4
  215. package/dist/core/scene/nodeStore.js.map +1 -1
  216. package/dist/core/scene/pathEffects/blend.js.map +1 -1
  217. package/dist/core/scene/pathEffects/derive.js +10 -4
  218. package/dist/core/scene/pathEffects/derive.js.map +1 -1
  219. package/dist/core/scene/pathEffects/effects.js +3 -1
  220. package/dist/core/scene/pathEffects/effects.js.map +1 -1
  221. package/dist/core/scene/pathEffects/foundHidden.js +35 -0
  222. package/dist/core/scene/pathEffects/foundHidden.js.map +1 -0
  223. package/dist/core/scene/pathEffects/textAlong.js +95 -0
  224. package/dist/core/scene/pathEffects/textAlong.js.map +1 -0
  225. package/dist/core/scene/plyStacks.js +89 -0
  226. package/dist/core/scene/plyStacks.js.map +1 -0
  227. package/dist/core/scene/poseHold.js +42 -24
  228. package/dist/core/scene/poseHold.js.map +1 -1
  229. package/dist/core/scene/poseOverrides.js +35 -38
  230. package/dist/core/scene/poseOverrides.js.map +1 -1
  231. package/dist/core/scene/posePlies.js +59 -0
  232. package/dist/core/scene/posePlies.js.map +1 -0
  233. package/dist/core/scene/reconcileSpecs.js +6 -15
  234. package/dist/core/scene/reconcileSpecs.js.map +1 -1
  235. package/dist/core/scene/renderOrder.js +10 -4
  236. package/dist/core/scene/renderOrder.js.map +1 -1
  237. package/dist/core/scene/sameValue.js +17 -0
  238. package/dist/core/scene/sameValue.js.map +1 -0
  239. package/dist/core/scene/scene.js +3 -3
  240. package/dist/core/scene/scene.js.map +1 -1
  241. package/dist/core/scene/sceneEdits.js +8 -0
  242. package/dist/core/scene/sceneEdits.js.map +1 -1
  243. package/dist/core/scene/sceneState.js +1 -2
  244. package/dist/core/scene/sceneState.js.map +1 -1
  245. package/dist/core/scene/serialization.js +45 -9
  246. package/dist/core/scene/serialization.js.map +1 -1
  247. package/dist/core/scene/staleReplay.js +5 -2
  248. package/dist/core/scene/staleReplay.js.map +1 -1
  249. package/dist/core/scene/statedFields.js +4 -2
  250. package/dist/core/scene/statedFields.js.map +1 -1
  251. package/dist/core/scene/treeEdits.js +2 -4
  252. package/dist/core/scene/treeEdits.js.map +1 -1
  253. package/dist/core/scene/treeOrder.js +3 -1
  254. package/dist/core/scene/treeOrder.js.map +1 -1
  255. package/dist/core/scene/treeReads.js +1 -2
  256. package/dist/core/scene/treeReads.js.map +1 -1
  257. package/dist/core/scene/treeShape.js +13 -0
  258. package/dist/core/scene/treeShape.js.map +1 -0
  259. package/dist/core/scene/types.js.map +1 -1
  260. package/dist/core/scene/useScene.js +1 -1
  261. package/dist/core/scene/useScene.js.map +1 -1
  262. package/dist/{cornerHandles-BHuoMs1o.d.ts → cornerHandles-CNCb7aTY.d.ts} +3 -2
  263. package/dist/{depSchema-BpZNOv7q.d.ts → depSchema-B1RS-IJx.d.ts} +39 -18
  264. package/dist/devtools/handlers.js +1 -1
  265. package/dist/devtools/handlers.js.map +1 -1
  266. package/dist/devtools/protocol.js.map +1 -1
  267. package/dist/devtools/registry.js +4 -4
  268. package/dist/devtools/registry.js.map +1 -1
  269. package/dist/devtools-protocol.d.ts +4 -3
  270. package/dist/devtools.d.ts +3 -2
  271. package/dist/features/measure/measureContribution.js +4 -2
  272. package/dist/features/measure/measureContribution.js.map +1 -1
  273. package/dist/features/measure/measureHit.js +5 -3
  274. package/dist/features/measure/measureHit.js.map +1 -1
  275. package/dist/features/measure/measurement.js +1 -1
  276. package/dist/features/measure/measurement.js.map +1 -1
  277. package/dist/features/modes/modeLayers.js +14 -7
  278. package/dist/features/modes/modeLayers.js.map +1 -1
  279. package/dist/features/modes/modeTriggers.js +10 -6
  280. package/dist/features/modes/modeTriggers.js.map +1 -1
  281. package/dist/features/pathEffects/effectSources.js +69 -0
  282. package/dist/features/pathEffects/effectSources.js.map +1 -0
  283. package/dist/features/pathEffects/readSource.js +18 -2
  284. package/dist/features/pathEffects/readSource.js.map +1 -1
  285. package/dist/features/pathEffects/textItems.js +53 -0
  286. package/dist/features/pathEffects/textItems.js.map +1 -0
  287. package/dist/features/pathEffects/useEffectSourcesLayer.js +53 -0
  288. package/dist/features/pathEffects/useEffectSourcesLayer.js.map +1 -0
  289. package/dist/features/paths/editablePath.js +1 -1
  290. package/dist/features/paths/editablePath.js.map +1 -1
  291. package/dist/features/paths/pathEditState.js +5 -2
  292. package/dist/features/paths/pathEditState.js.map +1 -1
  293. package/dist/features/paths/pathSnap/sceneIndex.js +1 -1
  294. package/dist/features/paths/pathSnap/sceneIndex.js.map +1 -1
  295. package/dist/features/selection/enteredGroupFrame.js +65 -0
  296. package/dist/features/selection/enteredGroupFrame.js.map +1 -0
  297. package/dist/features/selection/enteredGroupScope.js +44 -0
  298. package/dist/features/selection/enteredGroupScope.js.map +1 -0
  299. package/dist/features/text/sceneTextDrafts.js +64 -0
  300. package/dist/features/text/sceneTextDrafts.js.map +1 -0
  301. package/dist/features/text/textHitAt.js +34 -3
  302. package/dist/features/text/textHitAt.js.map +1 -1
  303. package/dist/features/text/useSceneTextEdit.js +23 -19
  304. package/dist/features/text/useSceneTextEdit.js.map +1 -1
  305. package/dist/features/viewports/aim.js +105 -0
  306. package/dist/features/viewports/aim.js.map +1 -0
  307. package/dist/features/viewports/viewMarker.js +82 -0
  308. package/dist/features/viewports/viewMarker.js.map +1 -0
  309. package/dist/features/viewports/viewMarkerDrag.js +55 -0
  310. package/dist/features/viewports/viewMarkerDrag.js.map +1 -0
  311. package/dist/index.d.ts +709 -258
  312. package/dist/index.js +33 -12
  313. package/dist/interactions/actions/admitsChild.js +11 -0
  314. package/dist/interactions/actions/admitsChild.js.map +1 -0
  315. package/dist/interactions/actions/booleans/booleans.js +2 -1
  316. package/dist/interactions/actions/booleans/booleans.js.map +1 -1
  317. package/dist/interactions/actions/booleans/operands.js +6 -5
  318. package/dist/interactions/actions/booleans/operands.js.map +1 -1
  319. package/dist/interactions/actions/clipboard/action.js +27 -17
  320. package/dist/interactions/actions/clipboard/action.js.map +1 -1
  321. package/dist/interactions/actions/defaults/anchorEditing.js +44 -23
  322. package/dist/interactions/actions/defaults/anchorEditing.js.map +1 -1
  323. package/dist/interactions/actions/defaults/anchorJoin.js +12 -6
  324. package/dist/interactions/actions/defaults/anchorJoin.js.map +1 -1
  325. package/dist/interactions/actions/defaults/bendSegment.js +7 -4
  326. package/dist/interactions/actions/defaults/bendSegment.js.map +1 -1
  327. package/dist/interactions/actions/defaults/cancelGesture.js +11 -8
  328. package/dist/interactions/actions/defaults/cancelGesture.js.map +1 -1
  329. package/dist/interactions/actions/defaults/contextMenu.js +33 -11
  330. package/dist/interactions/actions/defaults/contextMenu.js.map +1 -1
  331. package/dist/interactions/actions/defaults/createPaintAction.js +5 -2
  332. package/dist/interactions/actions/defaults/createPaintAction.js.map +1 -1
  333. package/dist/interactions/actions/defaults/delete.js +28 -14
  334. package/dist/interactions/actions/defaults/delete.js.map +1 -1
  335. package/dist/interactions/actions/defaults/duplicate.js +11 -8
  336. package/dist/interactions/actions/defaults/duplicate.js.map +1 -1
  337. package/dist/interactions/actions/defaults/enterGroup.js +88 -0
  338. package/dist/interactions/actions/defaults/enterGroup.js.map +1 -0
  339. package/dist/interactions/actions/defaults/enterPathEdit.js +20 -10
  340. package/dist/interactions/actions/defaults/enterPathEdit.js.map +1 -1
  341. package/dist/interactions/actions/defaults/enterTextEdit.js +1 -1
  342. package/dist/interactions/actions/defaults/enterTextEdit.js.map +1 -1
  343. package/dist/interactions/actions/defaults/escape.js +11 -8
  344. package/dist/interactions/actions/defaults/escape.js.map +1 -1
  345. package/dist/interactions/actions/defaults/exitPathEdit.js +7 -4
  346. package/dist/interactions/actions/defaults/exitPathEdit.js.map +1 -1
  347. package/dist/interactions/actions/defaults/group.js +26 -20
  348. package/dist/interactions/actions/defaults/group.js.map +1 -1
  349. package/dist/interactions/actions/defaults/ingest.js +10 -4
  350. package/dist/interactions/actions/defaults/ingest.js.map +1 -1
  351. package/dist/interactions/actions/defaults/insertPathAnchor.js +7 -4
  352. package/dist/interactions/actions/defaults/insertPathAnchor.js.map +1 -1
  353. package/dist/interactions/actions/defaults/nudge.js +8 -2
  354. package/dist/interactions/actions/defaults/nudge.js.map +1 -1
  355. package/dist/interactions/actions/defaults/pathEditTools.js +11 -8
  356. package/dist/interactions/actions/defaults/pathEditTools.js.map +1 -1
  357. package/dist/interactions/actions/defaults/pinchZoom.js +7 -4
  358. package/dist/interactions/actions/defaults/pinchZoom.js.map +1 -1
  359. package/dist/interactions/actions/defaults/reorder.js +24 -6
  360. package/dist/interactions/actions/defaults/reorder.js.map +1 -1
  361. package/dist/interactions/actions/defaults/selectAll.js +15 -8
  362. package/dist/interactions/actions/defaults/selectAll.js.map +1 -1
  363. package/dist/interactions/actions/defaults/selectDeep.js +35 -0
  364. package/dist/interactions/actions/defaults/selectDeep.js.map +1 -0
  365. package/dist/interactions/actions/defaults/setFill.js +7 -3
  366. package/dist/interactions/actions/defaults/setFill.js.map +1 -1
  367. package/dist/interactions/actions/defaults/setFillOpacity.js +1 -1
  368. package/dist/interactions/actions/defaults/setStroke.js +12 -11
  369. package/dist/interactions/actions/defaults/setStroke.js.map +1 -1
  370. package/dist/interactions/actions/defaults/setStrokeOpacity.js +1 -1
  371. package/dist/interactions/actions/defaults/toolActivate.js +7 -1
  372. package/dist/interactions/actions/defaults/toolActivate.js.map +1 -1
  373. package/dist/interactions/actions/defaults/toolResetToDefault.js +7 -4
  374. package/dist/interactions/actions/defaults/toolResetToDefault.js.map +1 -1
  375. package/dist/interactions/actions/defaults/undoRedo.js +19 -13
  376. package/dist/interactions/actions/defaults/undoRedo.js.map +1 -1
  377. package/dist/interactions/actions/defaults/viewportWheelPan.js +16 -7
  378. package/dist/interactions/actions/defaults/viewportWheelPan.js.map +1 -1
  379. package/dist/interactions/actions/defaults/viewportZoom.js +24 -6
  380. package/dist/interactions/actions/defaults/viewportZoom.js.map +1 -1
  381. package/dist/interactions/actions/drawnBoxes.js +2 -1
  382. package/dist/interactions/actions/drawnBoxes.js.map +1 -1
  383. package/dist/interactions/actions/edit-anchors/action.js +7 -4
  384. package/dist/interactions/actions/edit-anchors/action.js.map +1 -1
  385. package/dist/interactions/actions/flip/action.js +8 -2
  386. package/dist/interactions/actions/flip/action.js.map +1 -1
  387. package/dist/interactions/actions/insert/action.js +1 -0
  388. package/dist/interactions/actions/insert/action.js.map +1 -1
  389. package/dist/interactions/actions/insert/parent.js +7 -5
  390. package/dist/interactions/actions/insert/parent.js.map +1 -1
  391. package/dist/interactions/actions/insert/placeAction.js +3 -2
  392. package/dist/interactions/actions/insert/placeAction.js.map +1 -1
  393. package/dist/interactions/actions/insert/repositionAction.js +2 -1
  394. package/dist/interactions/actions/insert/repositionAction.js.map +1 -1
  395. package/dist/interactions/actions/insert/session.js +2 -2
  396. package/dist/interactions/actions/insert/session.js.map +1 -1
  397. package/dist/interactions/actions/move/action.js +7 -4
  398. package/dist/interactions/actions/move/action.js.map +1 -1
  399. package/dist/interactions/actions/move/commit/reparent.js +1 -1
  400. package/dist/interactions/actions/move/commit/reparent.js.map +1 -1
  401. package/dist/interactions/actions/move/gestureAdapter.js +6 -7
  402. package/dist/interactions/actions/move/gestureAdapter.js.map +1 -1
  403. package/dist/interactions/actions/move/layout/exits.js +1 -1
  404. package/dist/interactions/actions/move/layout/exits.js.map +1 -1
  405. package/dist/interactions/actions/move/session.js +4 -4
  406. package/dist/interactions/actions/move/session.js.map +1 -1
  407. package/dist/interactions/actions/outlines/createOutlinesAction.js +8 -5
  408. package/dist/interactions/actions/outlines/createOutlinesAction.js.map +1 -1
  409. package/dist/interactions/actions/outlines/outlineStroke.js +5 -3
  410. package/dist/interactions/actions/outlines/outlineStroke.js.map +1 -1
  411. package/dist/interactions/actions/pathCommands/cutJoin.js +8 -5
  412. package/dist/interactions/actions/pathCommands/cutJoin.js.map +1 -1
  413. package/dist/interactions/actions/pathCommands/pathNodes.js +14 -5
  414. package/dist/interactions/actions/pathCommands/pathNodes.js.map +1 -1
  415. package/dist/interactions/actions/resize/cornerHandles.js +2 -1
  416. package/dist/interactions/actions/resize/cornerHandles.js.map +1 -1
  417. package/dist/interactions/actions/selectionSubtree.js +12 -7
  418. package/dist/interactions/actions/selectionSubtree.js.map +1 -1
  419. package/dist/interactions/actions/transform-again/action.js +8 -5
  420. package/dist/interactions/actions/transform-again/action.js.map +1 -1
  421. package/dist/interactions/gestures/thresholdDrag/thresholdDrag.js +14 -2
  422. package/dist/interactions/gestures/thresholdDrag/thresholdDrag.js.map +1 -1
  423. package/dist/json.d.ts +4 -0
  424. package/dist/json.js +3 -0
  425. package/dist/kitSettingsSchema.js +8 -1
  426. package/dist/kitSettingsSchema.js.map +1 -1
  427. package/dist/{math-CrJULaZI.d.ts → math-C_elShP_.d.ts} +7 -1
  428. package/dist/math.d.ts +1 -1
  429. package/dist/math.js +2 -1
  430. package/dist/move.d.ts +4 -4
  431. package/dist/{options-DBxEryYq.d.ts → options-ClEm3SEl.d.ts} +1 -1
  432. package/dist/resize.d.ts +4 -4
  433. package/dist/test-seams.d.ts +6 -6
  434. package/dist/tools/builtin/ellipse/useEllipseTool.js +1 -0
  435. package/dist/tools/builtin/ellipse/useEllipseTool.js.map +1 -1
  436. package/dist/tools/builtin/eyedropper/useEyedropperTool.js +2 -1
  437. package/dist/tools/builtin/eyedropper/useEyedropperTool.js.map +1 -1
  438. package/dist/tools/builtin/hand/dragPanContribution.js +4 -1
  439. package/dist/tools/builtin/hand/dragPanContribution.js.map +1 -1
  440. package/dist/tools/builtin/hand/useHandTool.js +7 -4
  441. package/dist/tools/builtin/hand/useHandTool.js.map +1 -1
  442. package/dist/tools/builtin/image/useImageTool.js +1 -0
  443. package/dist/tools/builtin/image/useImageTool.js.map +1 -1
  444. package/dist/tools/builtin/lasso/useLassoTool.js +1 -0
  445. package/dist/tools/builtin/lasso/useLassoTool.js.map +1 -1
  446. package/dist/tools/builtin/line/useLineTool.js +1 -0
  447. package/dist/tools/builtin/line/useLineTool.js.map +1 -1
  448. package/dist/tools/builtin/measure/useMeasureTool.js +12 -5
  449. package/dist/tools/builtin/measure/useMeasureTool.js.map +1 -1
  450. package/dist/tools/builtin/pen/usePenTool.js +14 -6
  451. package/dist/tools/builtin/pen/usePenTool.js.map +1 -1
  452. package/dist/tools/builtin/pencil/usePencilTool.js +1 -0
  453. package/dist/tools/builtin/pencil/usePencilTool.js.map +1 -1
  454. package/dist/tools/builtin/polygon/usePolygonTool.js +13 -4
  455. package/dist/tools/builtin/polygon/usePolygonTool.js.map +1 -1
  456. package/dist/tools/builtin/pressTarget.js +22 -0
  457. package/dist/tools/builtin/pressTarget.js.map +1 -0
  458. package/dist/tools/builtin/rect/useRectTool.js +1 -0
  459. package/dist/tools/builtin/rect/useRectTool.js.map +1 -1
  460. package/dist/tools/builtin/select/adapterPointQuery.js +6 -5
  461. package/dist/tools/builtin/select/adapterPointQuery.js.map +1 -1
  462. package/dist/tools/builtin/select/collapseDeferred.js +40 -0
  463. package/dist/tools/builtin/select/collapseDeferred.js.map +1 -0
  464. package/dist/tools/builtin/select/groupEntry.js +82 -0
  465. package/dist/tools/builtin/select/groupEntry.js.map +1 -0
  466. package/dist/tools/builtin/select/pressDecision.js +32 -0
  467. package/dist/tools/builtin/select/pressDecision.js.map +1 -0
  468. package/dist/tools/builtin/select/selectionContributions.js +22 -13
  469. package/dist/tools/builtin/select/selectionContributions.js.map +1 -1
  470. package/dist/tools/builtin/select/useSelectTool.js +74 -55
  471. package/dist/tools/builtin/select/useSelectTool.js.map +1 -1
  472. package/dist/tools/builtin/slice/useSliceTool.js +22 -8
  473. package/dist/tools/builtin/slice/useSliceTool.js.map +1 -1
  474. package/dist/tools/builtin/star/useStarTool.js +13 -4
  475. package/dist/tools/builtin/star/useStarTool.js.map +1 -1
  476. package/dist/tools/builtin/sticky/stickyNode.js +4 -1
  477. package/dist/tools/builtin/sticky/stickyNode.js.map +1 -1
  478. package/dist/tools/builtin/sticky/useStickyTool.js +10 -5
  479. package/dist/tools/builtin/sticky/useStickyTool.js.map +1 -1
  480. package/dist/tools/builtin/text/useTextTool.js +3 -1
  481. package/dist/tools/builtin/text/useTextTool.js.map +1 -1
  482. package/dist/{types-7puAuVD2.d.ts → types-49uYJN3j.d.ts} +1 -1
  483. package/dist/{types-CniVctmk.d.ts → types-BEdRIhNc.d.ts} +272 -109
  484. package/dist/{types-oFeQ5VjV.d.ts → types-p2alu-R1.d.ts} +1 -1
  485. package/dist/util/paint.js +1 -3
  486. package/dist/util/paint.js.map +1 -1
  487. package/dist/version.js +1 -1
  488. package/package.json +29 -25
@@ -1 +1 @@
1
- {"version":3,"file":"derivedPainter.js","names":[],"sources":["../../../src/canvas/nodeShapes/derivedPainter.ts"],"sourcesContent":["import { boundsOfPath, type Path } from '@weasel-js/geom';\nimport type { Stroke, Fill } from '@weasel-js/paint';\nimport type { NodeInkCtx, NodeShapeEntry } from 'canvas/NodeShape';\nimport type { Bounds } from 'core/viewport/fitViewToBounds';\nimport { commandFill, commandStroke } from '@weasel-js/renderer';\nimport type { DrawCommand } from '@weasel-js/renderer';\nimport { DEFAULT_SHAPE_FILL } from '../../util/paint';\nimport { derivedPartsOf, type DerivedPart } from 'core/scene/derivedParts';\nimport { derivesPath } from './memo';\nimport { strokedBounds } from './strokedBounds';\nimport {\n inkReach, withAnimatedVertexColors, resolveNodeFill, resolveNodeStroke, type PaintedFill, type PaintedStroke,\n} from './nodePaint';\n\ntype PaintData = { fill?: Fill | null; stroke?: Stroke | null; vertexColors?: number[] };\n\nfunction pathCommand(\n path: Path,\n declared: PaintedFill | null,\n strokeSpec: PaintedStroke | null,\n vertexColors: number[] | undefined,\n): DrawCommand {\n const box = boundsOfPath(path);\n const fill = declared && commandFill(declared, box);\n const stroke = strokeSpec && commandStroke(strokeSpec, box);\n return {\n kind: 'path',\n path,\n ...(fill ? { fill } : {}),\n ...(fill && vertexColors ? { vertexColors } : {}),\n ...(stroke ? { stroke } : {}),\n };\n}\n\n/** The stroked box of the path on `ctx`, or `null` with no path or no stroke. */\nexport function derivedDrawnBounds(node: { data?: unknown }, ctx: NodeInkCtx | undefined): Bounds | null {\n const path = ctx?.derivedPath;\n const stroke = resolveNodeStroke((node.data as PaintData | null)?.stroke);\n return path == null || stroke === null ? null : strokedBounds(path, stroke, ctx?.scale ?? 1, ctx?.screen);\n}\n\n/** One part's command: its own paint where it names one, else the node's. */\nfunction partCommand(part: DerivedPart, d: PaintData | null): DrawCommand {\n const strokeSpec = resolveNodeStroke(part.stroke === undefined ? d?.stroke : part.stroke);\n const declared = resolveNodeFill(\n part.fill === undefined ? d?.fill : part.fill,\n strokeSpec === null ? DEFAULT_SHAPE_FILL : null,\n );\n return pathCommand(part.path, declared, strokeSpec, undefined);\n}\n\n/**\n * Built-in painter for nodes whose geometry is computed from other nodes'\n * poses. The path itself arrives on the paint context — see\n * {@link NodePaintCtx.derivedPath}.\n *\n * Fill and stroke resolve as `kit:path`'s do, but against the **derived path's\n * own bounds**, not the pose. `kit:path` projects its path into the pose frame\n * first, so there the two boxes are the same one; here the path is already\n * absolute and the pose is typically a zero-sized placeholder, which would\n * collapse a box-relative gradient onto a point.\n *\n * Not memoized on {@link PAINT_SLOT} on purpose: that key is `(node, pose,\n * data)` and cannot see `ctx.derivedPath`, so it would serve one caller's\n * commands to a caller that passed a different path.\n *\n * Rotation still comes from the pose — `wrapNodeOutput` turns the output about\n * the pose AABB's center, which for a zero-sized pose is its origin.\n *\n * A path made by `pathFromParts` paints one command per part, each with the\n * part's own fill and stroke where it names them.\n */\nexport const DERIVED_PAINTER: NodeShapeEntry = {\n id: 'kit:derived',\n // `dependsOn` alone is not enough: the two fields are independent optionals,\n // and `kit:add` restores a node with its `dependsOn` intact when its\n // `derivePath` key is missing from the registry. Claiming that node would paint\n // nothing where the authored appearance is what the warning promises.\n matches: derivesPath,\n paint: (node, _pose, ctx) => {\n const path = ctx?.derivedPath;\n if (path == null) return [];\n const d = node.data as PaintData | null;\n const parts = derivedPartsOf(path);\n if (parts !== undefined) return parts.map((part) => partCommand(part, d));\n const strokeSpec = resolveNodeStroke(d?.stroke);\n const declared = resolveNodeFill(d?.fill, strokeSpec === null ? DEFAULT_SHAPE_FILL : null);\n return withAnimatedVertexColors([pathCommand(path, declared, strokeSpec, d?.vertexColors)], ctx);\n },\n // The derived path *is* the silhouette, and it is already absolute — the\n // pose is a placeholder, so nothing here can be recovered from it. Without\n // a `ctx` this reports null, which `shapeCoversPoint` reads as \"no\n // opinion\": picking then falls back to that zero-sized pose.\n silhouette: (_node, _pose, ctx) => ctx?.derivedPath ?? null,\n drawnBounds: (node, _pose, ctx) => derivedDrawnBounds(node, ctx),\n ink: (node, _pose, ctx) => {\n const d = node.data as { fill?: Fill | null; stroke?: Stroke | null } | null;\n const stroke = resolveNodeStroke(d?.stroke);\n return {\n filled: resolveNodeFill(d?.fill, stroke === null ? DEFAULT_SHAPE_FILL : null) !== null,\n ...inkReach(stroke, ctx?.scale),\n };\n },\n};\n"],"mappings":";;;;;;;;;AAgBA,SAAS,YACP,MACA,UACA,YACA,cACa;CACb,MAAM,MAAM,aAAa,IAAI;CAC7B,MAAM,OAAO,YAAY,YAAY,UAAU,GAAG;CAClD,MAAM,SAAS,cAAc,cAAc,YAAY,GAAG;CAC1D,OAAO;EACL,MAAM;EACN;EACA,GAAI,OAAO,EAAE,KAAK,IAAI,CAAC;EACvB,GAAI,QAAQ,eAAe,EAAE,aAAa,IAAI,CAAC;EAC/C,GAAI,SAAS,EAAE,OAAO,IAAI,CAAC;CAC7B;AACF;;AAGA,SAAgB,mBAAmB,MAA0B,KAA4C;CACvG,MAAM,OAAO,KAAK;CAClB,MAAM,SAAS,kBAAmB,KAAK,MAA2B,MAAM;CACxE,OAAO,QAAQ,QAAQ,WAAW,OAAO,OAAO,cAAc,MAAM,QAAQ,KAAK,SAAS,GAAG,KAAK,MAAM;AAC1G;;AAGA,SAAS,YAAY,MAAmB,GAAkC;CACxE,MAAM,aAAa,kBAAkB,KAAK,WAAW,SAAY,GAAG,SAAS,KAAK,MAAM;CACxF,MAAM,WAAW,gBACf,KAAK,SAAS,SAAY,GAAG,OAAO,KAAK,MACzC,eAAe,OAAO,qBAAqB,IAC7C;CACA,OAAO,YAAY,KAAK,MAAM,UAAU,YAAY,MAAS;AAC/D;;;;;;;;;;;;;;;;;;;;;;AAuBA,IAAa,kBAAkC;CAC7C,IAAI;CAKJ,SAAS;CACT,QAAQ,MAAM,OAAO,QAAQ;EAC3B,MAAM,OAAO,KAAK;EAClB,IAAI,QAAQ,MAAM,OAAO,CAAC;EAC1B,MAAM,IAAI,KAAK;EACf,MAAM,QAAQ,eAAe,IAAI;EACjC,IAAI,UAAU,QAAW,OAAO,MAAM,KAAK,SAAS,YAAY,MAAM,CAAC,CAAC;EACxE,MAAM,aAAa,kBAAkB,GAAG,MAAM;EAC9C,MAAM,WAAW,gBAAgB,GAAG,MAAM,eAAe,OAAO,qBAAqB,IAAI;EACzF,OAAO,yBAAyB,CAAC,YAAY,MAAM,UAAU,YAAY,GAAG,YAAY,CAAC,GAAG,GAAG;CACjG;CAKA,aAAa,OAAO,OAAO,QAAQ,KAAK,eAAe;CACvD,cAAc,MAAM,OAAO,QAAQ,mBAAmB,MAAM,GAAG;CAC/D,MAAM,MAAM,OAAO,QAAQ;EACzB,MAAM,IAAI,KAAK;EACf,MAAM,SAAS,kBAAkB,GAAG,MAAM;EAC1C,OAAO;GACL,QAAQ,gBAAgB,GAAG,MAAM,WAAW,OAAO,qBAAqB,IAAI,MAAM;GAClF,GAAG,SAAS,QAAQ,KAAK,KAAK;EAChC;CACF;AACF"}
1
+ {"version":3,"file":"derivedPainter.js","names":[],"sources":["../../../src/canvas/nodeShapes/derivedPainter.ts"],"sourcesContent":["import { boundsOfPath, type Path } from '@weasel-js/geom';\nimport type { Stroke, Fill, Inherit } from '@weasel-js/paint';\nimport type { NodeInkCtx, NodeShapeEntry } from 'canvas/NodeShape';\nimport type { Bounds } from 'core/viewport/fitViewToBounds';\nimport { commandFill, commandStroke } from '@weasel-js/renderer';\nimport type { DrawCommand } from '@weasel-js/renderer';\nimport { derivedPartsOf, resolvePartPaint, type DerivedPart } from 'core/scene/derivedParts';\nimport { derivesPath } from './memo';\nimport type { InheritedPaint } from 'core/scene/inheritedPaint';\nimport { strokedBounds } from './strokedBounds';\nimport { inkReach, withAnimatedVertexColors } from './nodePaint';\nimport { resolveFill, resolveStroke, type PaintedFill, type PaintedStroke } from '@weasel-js/paint';\n\ntype PaintData = { fill?: Fill | Inherit | null; stroke?: Stroke | Inherit | null; vertexColors?: number[] };\n\nfunction pathCommand(\n path: Path,\n declared: PaintedFill | null,\n strokeSpec: PaintedStroke | null,\n vertexColors: number[] | undefined,\n): DrawCommand {\n const box = boundsOfPath(path);\n const fill = declared && commandFill(declared, box);\n const stroke = strokeSpec && commandStroke(strokeSpec, box);\n return {\n kind: 'path',\n path,\n ...(fill ? { fill } : {}),\n ...(fill && vertexColors ? { vertexColors } : {}),\n ...(stroke ? { stroke } : {}),\n };\n}\n\n/** The stroked box of the path on `ctx`, or `null` with no path or no stroke. */\nexport function derivedDrawnBounds(node: { data?: unknown }, ctx: NodeInkCtx | undefined): Bounds | null {\n const path = ctx?.derivedPath;\n const stroke = resolveStroke((node.data as PaintData | null)?.stroke, ctx?.inherited?.stroke);\n return path == null || stroke === null ? null : strokedBounds(path, stroke, ctx?.scale ?? 1, ctx?.screen);\n}\n\n/** One part's command, in its own paint; `INHERIT` takes the node's. */\nfunction partCommand(part: DerivedPart, d: PaintData | null, inherited: InheritedPaint | undefined): DrawCommand {\n const { fill, stroke } = resolvePartPaint(part, d, inherited);\n return pathCommand(part.path, fill, stroke, undefined);\n}\n\n/**\n * Built-in painter for nodes whose geometry is computed from other nodes'\n * poses. The path itself arrives on the paint context — see\n * {@link NodePaintCtx.derivedPath}.\n *\n * Fill and stroke resolve as `kit:path`'s do, but against the **derived path's\n * own bounds**, not the pose. `kit:path` projects its path into the pose frame\n * first, so there the two boxes are the same one; here the path is already\n * absolute and the pose is typically a zero-sized placeholder, which would\n * collapse a box-relative gradient onto a point.\n *\n * Not memoized on {@link PAINT_SLOT} on purpose: that key is `(node, pose,\n * data)` and cannot see `ctx.derivedPath`, so it would serve one caller's\n * commands to a caller that passed a different path.\n *\n * Rotation still comes from the pose — `wrapNodeOutput` turns the output about\n * the pose AABB's center, which for a zero-sized pose is its origin.\n *\n * A path made by `pathFromParts` paints one command per part, each in the\n * part's own fill and stroke.\n */\nexport const DERIVED_PAINTER: NodeShapeEntry = {\n id: 'kit:derived',\n // `dependsOn` alone is not enough: the two fields are independent optionals,\n // and `kit:add` restores a node with its `dependsOn` intact when its\n // `derivePath` key is missing from the registry. Claiming that node would paint\n // nothing where the authored appearance is what the warning promises.\n matches: derivesPath,\n paint: (node, _pose, ctx) => {\n const path = ctx?.derivedPath;\n if (path == null) return [];\n const d = node.data as PaintData | null;\n const parts = derivedPartsOf(path);\n if (parts !== undefined) return parts.map((part) => partCommand(part, d, ctx?.inherited));\n const strokeSpec = resolveStroke(d?.stroke, ctx?.inherited?.stroke);\n const declared = resolveFill(d?.fill, ctx?.inherited?.fill);\n return withAnimatedVertexColors([pathCommand(path, declared, strokeSpec, d?.vertexColors)], ctx);\n },\n // The derived path *is* the silhouette, and it is already absolute — the\n // pose is a placeholder, so nothing here can be recovered from it. Without\n // a `ctx` this reports null, which `shapeCoversPoint` reads as \"no\n // opinion\": picking then falls back to that zero-sized pose.\n silhouette: (_node, _pose, ctx) => ctx?.derivedPath ?? null,\n drawnBounds: (node, _pose, ctx) => derivedDrawnBounds(node, ctx),\n ink: (node, _pose, ctx) => {\n const d = node.data as PaintData | null;\n const stroke = resolveStroke(d?.stroke, ctx?.inherited?.stroke);\n return {\n filled: resolveFill(d?.fill, ctx?.inherited?.fill) !== null,\n ...inkReach(stroke, ctx?.scale),\n };\n },\n};\n"],"mappings":";;;;;;;;;AAeA,SAAS,YACP,MACA,UACA,YACA,cACa;CACb,MAAM,MAAM,aAAa,IAAI;CAC7B,MAAM,OAAO,YAAY,YAAY,UAAU,GAAG;CAClD,MAAM,SAAS,cAAc,cAAc,YAAY,GAAG;CAC1D,OAAO;EACL,MAAM;EACN;EACA,GAAI,OAAO,EAAE,KAAK,IAAI,CAAC;EACvB,GAAI,QAAQ,eAAe,EAAE,aAAa,IAAI,CAAC;EAC/C,GAAI,SAAS,EAAE,OAAO,IAAI,CAAC;CAC7B;AACF;;AAGA,SAAgB,mBAAmB,MAA0B,KAA4C;CACvG,MAAM,OAAO,KAAK;CAClB,MAAM,SAAS,cAAe,KAAK,MAA2B,QAAQ,KAAK,WAAW,MAAM;CAC5F,OAAO,QAAQ,QAAQ,WAAW,OAAO,OAAO,cAAc,MAAM,QAAQ,KAAK,SAAS,GAAG,KAAK,MAAM;AAC1G;;AAGA,SAAS,YAAY,MAAmB,GAAqB,WAAoD;CAC/G,MAAM,EAAE,MAAM,WAAW,iBAAiB,MAAM,GAAG,SAAS;CAC5D,OAAO,YAAY,KAAK,MAAM,MAAM,QAAQ,MAAS;AACvD;;;;;;;;;;;;;;;;;;;;;;AAuBA,IAAa,kBAAkC;CAC7C,IAAI;CAKJ,SAAS;CACT,QAAQ,MAAM,OAAO,QAAQ;EAC3B,MAAM,OAAO,KAAK;EAClB,IAAI,QAAQ,MAAM,OAAO,CAAC;EAC1B,MAAM,IAAI,KAAK;EACf,MAAM,QAAQ,eAAe,IAAI;EACjC,IAAI,UAAU,QAAW,OAAO,MAAM,KAAK,SAAS,YAAY,MAAM,GAAG,KAAK,SAAS,CAAC;EACxF,MAAM,aAAa,cAAc,GAAG,QAAQ,KAAK,WAAW,MAAM;EAClE,MAAM,WAAW,YAAY,GAAG,MAAM,KAAK,WAAW,IAAI;EAC1D,OAAO,yBAAyB,CAAC,YAAY,MAAM,UAAU,YAAY,GAAG,YAAY,CAAC,GAAG,GAAG;CACjG;CAKA,aAAa,OAAO,OAAO,QAAQ,KAAK,eAAe;CACvD,cAAc,MAAM,OAAO,QAAQ,mBAAmB,MAAM,GAAG;CAC/D,MAAM,MAAM,OAAO,QAAQ;EACzB,MAAM,IAAI,KAAK;EACf,MAAM,SAAS,cAAc,GAAG,QAAQ,KAAK,WAAW,MAAM;EAC9D,OAAO;GACL,QAAQ,YAAY,GAAG,MAAM,KAAK,WAAW,IAAI,MAAM;GACvD,GAAG,SAAS,QAAQ,KAAK,KAAK;EAChC;CACF;AACF"}
@@ -1,37 +1,11 @@
1
1
  import { bumpNodeMemoGeneration } from "../../core/scene/nodeMemo.js";
2
- import { DEFAULT_SHAPE_FILL } from "../../util/paint.js";
3
2
  import { paintDrawKindRegistry } from "@weasel-js/renderer";
4
3
  import { countPathAnchors } from "@weasel-js/geom";
4
+ import { resolveFill, resolveStroke } from "@weasel-js/paint";
5
5
  import { markerReach, resolveStrokeWidth, widestStrokeWidth } from "@weasel-js/stroke";
6
6
 
7
7
  //#region src/canvas/nodeShapes/nodePaint.ts
8
8
  paintDrawKindRegistry.subscribe(bumpNodeMemoGeneration);
9
- /**
10
- * What a node's `data.fill` means to the built-in painters.
11
- *
12
- * Its {@link Paint} is used as-is — which is how a gradient or a pattern
13
- * reaches the renderer without a consumer registering a painter of its own.
14
- * `null` is an explicit "no fill", and so is a fill carrying no `paint`;
15
- * `undefined` takes the painter's fallback. Write the paint with
16
- * {@link solid} rather than by hand when all you have is a color.
17
- */
18
- function resolveNodeFill(fill, fallback) {
19
- const declared = fill === void 0 ? fallback : fill;
20
- return declared?.paint == null ? null : declared;
21
- }
22
- /** The stroke a built-in painter should emit, or `null` for "no stroke".
23
- * `data.stroke` is a whole {@link Stroke} — width, cap, join, dash, miter
24
- * limit, and align all reach the renderer. {@link strokeOf} builds one from a
25
- * color.
26
- *
27
- * A stroke carrying no `paint` reads as no stroke. The type requires `paint`,
28
- * but a document is data, and a stroke assembled a field at a time arrives
29
- * here without one; painting it throws out of the painter and takes the whole
30
- * frame with it. */
31
- function resolveNodeStroke(stroke) {
32
- if (stroke?.paint == null) return null;
33
- return stroke;
34
- }
35
9
  /** Per-side grab reach for a resolved stroke: world units for what the
36
10
  * renderer builds in world, screen pixels for what it builds on screen — a
37
11
  * `{ px }` width, and each head by its own size's units. */
@@ -130,12 +104,11 @@ function withAnimatedVertexColors(cmds, ctx) {
130
104
  }, ...cmds.slice(1)];
131
105
  }
132
106
  /** {@link PathPaint} of a node that keeps its paint in `data.fill` and
133
- * `data.stroke`, as the built-in path painter draws it: a stroke with no
134
- * `paint` is none, and an undeclared fill is the default shape fill unless
135
- * the node is stroked. */
136
- function pathPaintOf(data) {
137
- const stroke = resolveNodeStroke(data.stroke);
138
- const fill = resolveNodeFill(data.fill, stroke !== null ? null : DEFAULT_SHAPE_FILL);
107
+ * `data.stroke`, as the built-in path painter draws it: a stroke or a fill
108
+ * with no `paint` is none, and an `'inherit'` one is `inherited`'s. */
109
+ function pathPaintOf(data, inherited) {
110
+ const stroke = resolveStroke(data.stroke, inherited?.stroke);
111
+ const fill = resolveFill(data.fill, inherited?.fill);
139
112
  return stroke !== null ? {
140
113
  fill,
141
114
  stroke
@@ -143,5 +116,5 @@ function pathPaintOf(data) {
143
116
  }
144
117
 
145
118
  //#endregion
146
- export { inkReach, pathPaintOf, resolveNodeFill, resolveNodeStroke, strokeOutset, strokeOutsetPerAxis, withAnimatedVertexColors };
119
+ export { inkReach, pathPaintOf, strokeOutset, strokeOutsetPerAxis, withAnimatedVertexColors };
147
120
  //# sourceMappingURL=nodePaint.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"nodePaint.js","names":[],"sources":["../../../src/canvas/nodeShapes/nodePaint.ts"],"sourcesContent":["import type { Paint, Stroke, Fill } from '@weasel-js/paint';\nimport type { NodeInk, NodeInkCtx, NodePaintCtx } from 'canvas/NodeShape';\nimport { countPathAnchors } from '@weasel-js/geom';\nimport { markerReach } from '@weasel-js/stroke';\nimport { resolveStrokeWidth, widestStrokeWidth } from '@weasel-js/stroke';\nimport type { DrawCommand, PathDrawCommand } from '@weasel-js/renderer';\nimport { paintDrawKindRegistry } from '@weasel-js/renderer';\nimport { bumpNodeMemoGeneration } from 'core/scene/nodeMemo';\nimport { DEFAULT_SHAPE_FILL } from '../../util/paint';\n\n// The painters memoize a node's paint and resolve a fill's frame inside the\n// memo, so the kind set is ambient state that memo cannot see change.\npaintDrawKindRegistry.subscribe(bumpNodeMemoGeneration);\n\n/**\n * What a node's `data.fill` means to the built-in painters.\n *\n * Its {@link Paint} is used as-is — which is how a gradient or a pattern\n * reaches the renderer without a consumer registering a painter of its own.\n * `null` is an explicit \"no fill\", and so is a fill carrying no `paint`;\n * `undefined` takes the painter's fallback. Write the paint with\n * {@link solid} rather than by hand when all you have is a color.\n */\nexport function resolveNodeFill(\n fill: Fill | null | undefined,\n fallback: Fill | null,\n): PaintedFill | null {\n const declared = fill === undefined ? fallback : fill;\n return declared?.paint == null ? null : (declared as PaintedFill);\n}\n\n/** A fill that paints — what everything downstream of `resolveNodeFill`\n * works with. */\nexport type PaintedFill = Fill & { paint: Paint };\n\n/** The stroke a built-in painter should emit, or `null` for \"no stroke\".\n * `data.stroke` is a whole {@link Stroke} — width, cap, join, dash, miter\n * limit, and align all reach the renderer. {@link strokeOf} builds one from a\n * color.\n *\n * A stroke carrying no `paint` reads as no stroke. The type requires `paint`,\n * but a document is data, and a stroke assembled a field at a time arrives\n * here without one; painting it throws out of the painter and takes the whole\n * frame with it. */\nexport function resolveNodeStroke(stroke: Stroke | null | undefined): PaintedStroke | null {\n if (stroke?.paint == null) return null;\n return stroke as PaintedStroke;\n}\n\n/** A stroke that paints — what everything downstream of `resolveNodeStroke`\n * works with. */\nexport type PaintedStroke = Stroke & { paint: Paint };\n\n/** Per-side grab reach for a resolved stroke: world units for what the\n * renderer builds in world, screen pixels for what it builds on screen — a\n * `{ px }` width, and each head by its own size's units. */\nexport function inkReach(\n stroke: Stroke | null,\n scale: number | undefined,\n): Pick<NodeInk, 'outset' | 'inset' | 'outsetPx' | 'insetPx'> {\n if (stroke === null) return { outset: 0, inset: 0 };\n // A marker paints past the path's own end, and the kit's rule is that\n // visible chrome is hittable — so the reach has to cover it.\n const heads = markerReach(stroke);\n const sides = (width: number, headReach: number) => {\n switch (stroke.align ?? 'center') {\n case 'inner': return { outset: headReach, inset: width };\n case 'outer': return { outset: width + headReach, inset: 0 };\n default: return { outset: width / 2 + headReach, inset: width / 2 };\n }\n };\n const width = stroke.width ?? 1;\n // Per-vertex widths are world units even on a `{ px }` stroke.\n const worldWidth = widestStrokeWidth(typeof width === 'object' ? 0 : resolveStrokeWidth(width, scale ?? 1), stroke.vertexWidths);\n const world = sides(worldWidth, heads.world);\n if (typeof width !== 'object' && !(heads.px > 0)) return world;\n const px = sides(typeof width === 'object' ? width.px : 0, heads.px);\n return { outset: world.outset, inset: world.inset, outsetPx: px.outset, insetPx: px.inset };\n}\n\n/** How far `stroke` reaches outside its outline on every side, world units at\n * `scale`: {@link inkReach}'s outset with its screen part resolved. */\nexport function strokeOutset(stroke: Stroke | null, scale: number): number {\n const r = inkReach(stroke, scale);\n return r.outset + (r.outsetPx ?? 0) / (scale > 0 ? scale : 1);\n}\n\n/** {@link strokeOutset} per axis of the frame `ctx.screen` maps from: `x` past\n * the left and right sides, `y` past the top and bottom. A `{ px }` reach\n * is a disk on screen, and its extent along each axis of that frame is the\n * support of the disk pulled back through the map. */\nexport function strokeOutsetPerAxis(stroke: Stroke | null, ctx: NodeInkCtx | undefined): { x: number; y: number } {\n const scale = ctx?.scale ?? 1;\n const screen = ctx?.screen;\n if (!screen) {\n const r = strokeOutset(stroke, scale);\n return { x: r, y: r };\n }\n const r = inkReach(stroke, scale);\n const px = r.outsetPx ?? 0;\n if (px === 0) return { x: r.outset, y: r.outset };\n const [a, b, c, d] = screen;\n const det = Math.abs(a * d - b * c);\n if (!(det > 0)) return { x: r.outset, y: r.outset };\n return { x: r.outset + (px * Math.hypot(c, d)) / det, y: r.outset + (px * Math.hypot(a, b)) / det };\n}\n\n/** Apply {@link NodePaintCtx.vertexColors} to a path painter's body — its first\n * command. Applied outside the paint memo, whose key\n * cannot see an override: `cmds` comes back as-is when nothing changes, and\n * otherwise as a copy, since a memoized list is not ours to edit. Colors whose\n * length does not match the path's anchors are dropped. */\nexport function withAnimatedVertexColors(cmds: DrawCommand[], ctx: NodePaintCtx | undefined): DrawCommand[] {\n const resolve = ctx?.vertexColors;\n const body = cmds[0];\n if (resolve === undefined || body?.kind !== 'path') return cmds;\n const expected = 4 * countPathAnchors(body.path);\n const fill = body.fill ? resolve('fill', body.vertexColors) : undefined;\n const stroke = body.stroke ? resolve('stroke', body.stroke.vertexColors) : undefined;\n const fillChanged = fill?.length === expected && fill !== body.vertexColors;\n const strokeChanged = stroke?.length === expected && stroke !== body.stroke?.vertexColors;\n if (!fillChanged && !strokeChanged) return cmds;\n const next: PathDrawCommand = {\n ...body,\n ...(fillChanged ? { vertexColors: fill as number[] } : {}),\n ...(strokeChanged ? { stroke: { ...body.stroke!, vertexColors: stroke as number[] } } : {}),\n };\n return [next, ...cmds.slice(1)];\n}\n\n/** The paint a path node wears, resolved: what an action that turns ink into\n * geometry reads from a node and tells a consumer to put on a result. */\nexport interface PathPaint {\n /** `null` is no fill. */\n fill: Fill | null;\n /** Absent for no stroke. */\n stroke?: Stroke;\n}\n\n/** {@link PathPaint} of a node that keeps its paint in `data.fill` and\n * `data.stroke`, as the built-in path painter draws it: a stroke with no\n * `paint` is none, and an undeclared fill is the default shape fill unless\n * the node is stroked. */\nexport function pathPaintOf(data: { fill?: Fill | null; stroke?: Stroke | null }): PathPaint {\n const stroke = resolveNodeStroke(data.stroke);\n const fill = resolveNodeFill(data.fill, stroke !== null ? null : DEFAULT_SHAPE_FILL);\n return stroke !== null ? { fill, stroke } : { fill };\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"nodePaint.js","names":[],"sources":["../../../src/canvas/nodeShapes/nodePaint.ts"],"sourcesContent":["import type { Stroke, Fill, Inherit } from '@weasel-js/paint';\nimport { resolveFill, resolveStroke } from '@weasel-js/paint';\nimport type { NodeInk, NodeInkCtx, NodePaintCtx } from 'canvas/NodeShape';\nimport { countPathAnchors } from '@weasel-js/geom';\nimport { markerReach } from '@weasel-js/stroke';\nimport { resolveStrokeWidth, widestStrokeWidth } from '@weasel-js/stroke';\nimport type { DrawCommand, PathDrawCommand } from '@weasel-js/renderer';\nimport { paintDrawKindRegistry } from '@weasel-js/renderer';\nimport { bumpNodeMemoGeneration } from 'core/scene/nodeMemo';\nimport type { InheritedPaint } from 'core/scene/inheritedPaint';\n\n// The painters memoize a node's paint and resolve a fill's frame inside the\n// memo, so the kind set is ambient state that memo cannot see change.\npaintDrawKindRegistry.subscribe(bumpNodeMemoGeneration);\n\n/** Per-side grab reach for a resolved stroke: world units for what the\n * renderer builds in world, screen pixels for what it builds on screen — a\n * `{ px }` width, and each head by its own size's units. */\nexport function inkReach(\n stroke: Stroke | null,\n scale: number | undefined,\n): Pick<NodeInk, 'outset' | 'inset' | 'outsetPx' | 'insetPx'> {\n if (stroke === null) return { outset: 0, inset: 0 };\n // A marker paints past the path's own end, and the kit's rule is that\n // visible chrome is hittable — so the reach has to cover it.\n const heads = markerReach(stroke);\n const sides = (width: number, headReach: number) => {\n switch (stroke.align ?? 'center') {\n case 'inner': return { outset: headReach, inset: width };\n case 'outer': return { outset: width + headReach, inset: 0 };\n default: return { outset: width / 2 + headReach, inset: width / 2 };\n }\n };\n const width = stroke.width ?? 1;\n // Per-vertex widths are world units even on a `{ px }` stroke.\n const worldWidth = widestStrokeWidth(typeof width === 'object' ? 0 : resolveStrokeWidth(width, scale ?? 1), stroke.vertexWidths);\n const world = sides(worldWidth, heads.world);\n if (typeof width !== 'object' && !(heads.px > 0)) return world;\n const px = sides(typeof width === 'object' ? width.px : 0, heads.px);\n return { outset: world.outset, inset: world.inset, outsetPx: px.outset, insetPx: px.inset };\n}\n\n/** How far `stroke` reaches outside its outline on every side, world units at\n * `scale`: {@link inkReach}'s outset with its screen part resolved. */\nexport function strokeOutset(stroke: Stroke | null, scale: number): number {\n const r = inkReach(stroke, scale);\n return r.outset + (r.outsetPx ?? 0) / (scale > 0 ? scale : 1);\n}\n\n/** {@link strokeOutset} per axis of the frame `ctx.screen` maps from: `x` past\n * the left and right sides, `y` past the top and bottom. A `{ px }` reach\n * is a disk on screen, and its extent along each axis of that frame is the\n * support of the disk pulled back through the map. */\nexport function strokeOutsetPerAxis(stroke: Stroke | null, ctx: NodeInkCtx | undefined): { x: number; y: number } {\n const scale = ctx?.scale ?? 1;\n const screen = ctx?.screen;\n if (!screen) {\n const r = strokeOutset(stroke, scale);\n return { x: r, y: r };\n }\n const r = inkReach(stroke, scale);\n const px = r.outsetPx ?? 0;\n if (px === 0) return { x: r.outset, y: r.outset };\n const [a, b, c, d] = screen;\n const det = Math.abs(a * d - b * c);\n if (!(det > 0)) return { x: r.outset, y: r.outset };\n return { x: r.outset + (px * Math.hypot(c, d)) / det, y: r.outset + (px * Math.hypot(a, b)) / det };\n}\n\n/** Apply {@link NodePaintCtx.vertexColors} to a path painter's body — its first\n * command. Applied outside the paint memo, whose key\n * cannot see an override: `cmds` comes back as-is when nothing changes, and\n * otherwise as a copy, since a memoized list is not ours to edit. Colors whose\n * length does not match the path's anchors are dropped. */\nexport function withAnimatedVertexColors(cmds: DrawCommand[], ctx: NodePaintCtx | undefined): DrawCommand[] {\n const resolve = ctx?.vertexColors;\n const body = cmds[0];\n if (resolve === undefined || body?.kind !== 'path') return cmds;\n const expected = 4 * countPathAnchors(body.path);\n const fill = body.fill ? resolve('fill', body.vertexColors) : undefined;\n const stroke = body.stroke ? resolve('stroke', body.stroke.vertexColors) : undefined;\n const fillChanged = fill?.length === expected && fill !== body.vertexColors;\n const strokeChanged = stroke?.length === expected && stroke !== body.stroke?.vertexColors;\n if (!fillChanged && !strokeChanged) return cmds;\n const next: PathDrawCommand = {\n ...body,\n ...(fillChanged ? { vertexColors: fill as number[] } : {}),\n ...(strokeChanged ? { stroke: { ...body.stroke!, vertexColors: stroke as number[] } } : {}),\n };\n return [next, ...cmds.slice(1)];\n}\n\n/** The paint a path node wears, resolved: what an action that turns ink into\n * geometry reads from a node and tells a consumer to put on a result. */\nexport interface PathPaint {\n /** `null` is no fill. */\n fill: Fill | null;\n /** Absent for no stroke. */\n stroke?: Stroke;\n}\n\n/** {@link PathPaint} of a node that keeps its paint in `data.fill` and\n * `data.stroke`, as the built-in path painter draws it: a stroke or a fill\n * with no `paint` is none, and an `'inherit'` one is `inherited`'s. */\nexport function pathPaintOf(\n data: { fill?: Fill | Inherit | null; stroke?: Stroke | Inherit | null },\n inherited?: InheritedPaint,\n): PathPaint {\n const stroke = resolveStroke(data.stroke, inherited?.stroke);\n const fill = resolveFill(data.fill, inherited?.fill);\n return stroke !== null ? { fill, stroke } : { fill };\n}\n"],"mappings":"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"}
@@ -1,11 +1,12 @@
1
1
  import { nodeMemo } from "../../core/scene/nodeMemo.js";
2
2
  import { PAINT_SLOT } from "./memo.js";
3
- import { inkReach, pathPaintOf, resolveNodeStroke, withAnimatedVertexColors } from "./nodePaint.js";
3
+ import { inkReach, pathPaintOf, withAnimatedVertexColors } from "./nodePaint.js";
4
4
  import { strokedPoseBox } from "./strokedBounds.js";
5
5
  import { DERIVED_PAINTER, derivedDrawnBounds } from "./derivedPainter.js";
6
6
  import { paintsPose, poseBoxBounds } from "./poseBox.js";
7
7
  import { commandFill, commandStroke } from "@weasel-js/renderer";
8
8
  import { pathInPoseFrame } from "@weasel-js/geom";
9
+ import { resolveStroke } from "@weasel-js/paint";
9
10
 
10
11
  //#region src/canvas/nodeShapes/pathPainter.ts
11
12
  var PATH_PAINTER = {
@@ -17,7 +18,7 @@ var PATH_PAINTER = {
17
18
  paint: (node, pose, ctx) => ctx?.derivedPath != null ? DERIVED_PAINTER.paint(node, pose, ctx) : withAnimatedVertexColors(nodeMemo(node, PAINT_SLOT, pose, () => {
18
19
  const d = node.data;
19
20
  const projected = pathInPoseFrame(d.path, pose);
20
- const { fill: declared, stroke: strokeSpec } = pathPaintOf(d);
21
+ const { fill: declared, stroke: strokeSpec } = pathPaintOf(d, ctx?.inherited);
21
22
  const fill = declared && commandFill(declared, pose);
22
23
  const stroke = strokeSpec && commandStroke(strokeSpec, pose);
23
24
  return [{
@@ -27,25 +28,24 @@ var PATH_PAINTER = {
27
28
  ...fill && d.vertexColors ? { vertexColors: d.vertexColors } : {},
28
29
  ...stroke ? { stroke } : {}
29
30
  }];
30
- }), ctx),
31
+ }, ctx?.inherited), ctx),
31
32
  silhouette: (node, pose, ctx) => {
32
33
  if (ctx?.derivedPath != null) return ctx.derivedPath;
33
34
  const d = node.data;
34
35
  return pathInPoseFrame(d.path, pose);
35
36
  },
36
37
  ink: (node, _pose, ctx) => {
37
- const d = node.data;
38
- const { fill, stroke } = pathPaintOf(d);
38
+ const { fill, stroke } = pathPaintOf(node.data, ctx?.inherited);
39
39
  return {
40
40
  filled: fill !== null,
41
41
  ...inkReach(stroke ?? null, ctx?.scale)
42
42
  };
43
43
  },
44
- bounds: (node, pose, ctx) => poseBoxBounds(pose, resolveNodeStroke(node.data.stroke), ctx),
44
+ bounds: (node, pose, ctx) => poseBoxBounds(pose, resolveStroke(node.data.stroke, ctx?.inherited?.stroke), ctx),
45
45
  drawnBounds: (node, pose, ctx) => {
46
46
  if (ctx?.derivedPath != null) return derivedDrawnBounds(node, ctx);
47
47
  const d = node.data;
48
- return strokedPoseBox(node, pose, resolveNodeStroke(d.stroke), ctx, (at) => pathInPoseFrame(d.path, at));
48
+ return strokedPoseBox(node, pose, resolveStroke(d.stroke, ctx?.inherited?.stroke), ctx, (at) => pathInPoseFrame(d.path, at));
49
49
  }
50
50
  };
51
51
 
@@ -1 +1 @@
1
- {"version":3,"file":"pathPainter.js","names":[],"sources":["../../../src/canvas/nodeShapes/pathPainter.ts"],"sourcesContent":["import type {\n Path\n} from '@weasel-js/geom';\nimport type { Stroke, Fill } from '@weasel-js/paint';\nimport type { NodeShapeEntry } from 'canvas/NodeShape';\nimport { nodeMemo } from 'core/scene/nodeMemo';\nimport type { RectPose } from 'core/scene/types';\nimport { pathInPoseFrame } from '@weasel-js/geom';\nimport { commandFill, commandStroke } from '@weasel-js/renderer';\nimport type { DrawCommand } from '@weasel-js/renderer';\nimport { DERIVED_PAINTER, derivedDrawnBounds } from './derivedPainter';\nimport { PAINT_SLOT } from './memo';\nimport { inkReach, pathPaintOf, withAnimatedVertexColors, resolveNodeStroke } from './nodePaint';\nimport { paintsPose, poseBoxBounds } from './poseBox';\nimport { strokedPoseBox } from './strokedBounds';\n\n// ─── Built-in painters ─────────────────────────────────────────────────\nexport const PATH_PAINTER: NodeShapeEntry<unknown, RectPose> = {\n id: 'kit:path',\n matches: (node) => {\n if (!paintsPose(node.pose)) return false;\n const d = node.data as { path?: Path } | null;\n return d?.path != null;\n },\n // Memoized: a pure function of `(data, pose)`, and `pathInPoseFrame`\n // allocates on every call but the identity (`kind: 'rect'`) branch. See\n // PAINT_SLOT.\n paint: (node, pose, ctx) => ctx?.derivedPath != null\n // A pose override's path stands in for `data.path`, already where it is drawn.\n ? DERIVED_PAINTER.paint(node, pose, ctx)\n : withAnimatedVertexColors(nodeMemo(node, PAINT_SLOT, pose, () => {\n const d = node.data as {\n path: Path;\n fill?: Fill | null;\n stroke?: Stroke | null;\n vertexColors?: number[];\n };\n const projected = pathInPoseFrame(d.path, pose);\n const { fill: declared, stroke: strokeSpec } = pathPaintOf(d);\n // The pose is baked into `projected` rather than emitted as a transform,\n // so a box-relative gradient has to be baked onto the same box here or\n // it would arrive in the renderer referring to a frame that never exists.\n const fill = declared && commandFill(declared, pose);\n const stroke = strokeSpec && commandStroke(strokeSpec, pose);\n const cmd: DrawCommand = {\n kind: 'path',\n path: projected,\n ...(fill ? { fill } : {}),\n ...(fill && d.vertexColors ? { vertexColors: d.vertexColors } : {}),\n ...(stroke ? { stroke } : {}),\n };\n return [cmd];\n }), ctx),\n silhouette: (node, pose, ctx) => {\n if (ctx?.derivedPath != null) return ctx.derivedPath;\n const d = node.data as { path: Path };\n return pathInPoseFrame(d.path, pose);\n },\n ink: (node, _pose, ctx) => {\n const d = node.data as {\n fill?: Fill | null;\n stroke?: Stroke | null;\n };\n const { fill, stroke } = pathPaintOf(d);\n return { filled: fill !== null, ...inkReach(stroke ?? null, ctx?.scale) };\n },\n // `pathInPoseFrame` fits the path's own bounds to the pose box.\n bounds: (node, pose, ctx) =>\n poseBoxBounds(pose, resolveNodeStroke((node.data as { stroke?: Stroke | null }).stroke), ctx),\n drawnBounds: (node, pose, ctx) => {\n if (ctx?.derivedPath != null) return derivedDrawnBounds(node, ctx);\n const d = node.data as { path: Path; stroke?: Stroke | null };\n return strokedPoseBox(node, pose, resolveNodeStroke(d.stroke), ctx, (at) => pathInPoseFrame(d.path, at));\n },\n};\n"],"mappings":";;;;;;;;;;AAiBA,IAAa,eAAkD;CAC7D,IAAI;CACJ,UAAU,SAAS;EACjB,IAAI,CAAC,WAAW,KAAK,IAAI,GAAG,OAAO;EAEnC,OADU,KAAK,MACL,QAAQ;CACpB;CAIA,QAAQ,MAAM,MAAM,QAAQ,KAAK,eAAe,OAE5C,gBAAgB,MAAM,MAAM,MAAM,GAAG,IACrC,yBAAyB,SAAS,MAAM,YAAY,YAAY;EAClE,MAAM,IAAI,KAAK;EAMf,MAAM,YAAY,gBAAgB,EAAE,MAAM,IAAI;EAC9C,MAAM,EAAE,MAAM,UAAU,QAAQ,eAAe,YAAY,CAAC;EAI5D,MAAM,OAAO,YAAY,YAAY,UAAU,IAAI;EACnD,MAAM,SAAS,cAAc,cAAc,YAAY,IAAI;EAQ3D,OAAO,CAAC;GANN,MAAM;GACN,MAAM;GACN,GAAI,OAAO,EAAE,KAAK,IAAI,CAAC;GACvB,GAAI,QAAQ,EAAE,eAAe,EAAE,cAAc,EAAE,aAAa,IAAI,CAAC;GACjE,GAAI,SAAS,EAAE,OAAO,IAAI,CAAC;EAErB,CAAG;CACb,CAAC,GAAG,GAAG;CACP,aAAa,MAAM,MAAM,QAAQ;EAC/B,IAAI,KAAK,eAAe,MAAM,OAAO,IAAI;EACzC,MAAM,IAAI,KAAK;EACf,OAAO,gBAAgB,EAAE,MAAM,IAAI;CACrC;CACA,MAAM,MAAM,OAAO,QAAQ;EACzB,MAAM,IAAI,KAAK;EAIf,MAAM,EAAE,MAAM,WAAW,YAAY,CAAC;EACtC,OAAO;GAAE,QAAQ,SAAS;GAAM,GAAG,SAAS,UAAU,MAAM,KAAK,KAAK;EAAE;CAC1E;CAEA,SAAS,MAAM,MAAM,QACnB,cAAc,MAAM,kBAAmB,KAAK,KAAoC,MAAM,GAAG,GAAG;CAC9F,cAAc,MAAM,MAAM,QAAQ;EAChC,IAAI,KAAK,eAAe,MAAM,OAAO,mBAAmB,MAAM,GAAG;EACjE,MAAM,IAAI,KAAK;EACf,OAAO,eAAe,MAAM,MAAM,kBAAkB,EAAE,MAAM,GAAG,MAAM,OAAO,gBAAgB,EAAE,MAAM,EAAE,CAAC;CACzG;AACF"}
1
+ {"version":3,"file":"pathPainter.js","names":[],"sources":["../../../src/canvas/nodeShapes/pathPainter.ts"],"sourcesContent":["import type {\n Path\n} from '@weasel-js/geom';\nimport type { Stroke, Fill, Inherit } from '@weasel-js/paint';\nimport type { NodeShapeEntry } from 'canvas/NodeShape';\nimport { nodeMemo } from 'core/scene/nodeMemo';\nimport type { RectPose } from 'core/scene/types';\nimport { pathInPoseFrame } from '@weasel-js/geom';\nimport { commandFill, commandStroke } from '@weasel-js/renderer';\nimport type { DrawCommand } from '@weasel-js/renderer';\nimport { DERIVED_PAINTER, derivedDrawnBounds } from './derivedPainter';\nimport { PAINT_SLOT } from './memo';\nimport { inkReach, pathPaintOf, withAnimatedVertexColors } from './nodePaint';\nimport { resolveStroke } from '@weasel-js/paint';\nimport { paintsPose, poseBoxBounds } from './poseBox';\nimport { strokedPoseBox } from './strokedBounds';\n\ntype PathData = { fill?: Fill | Inherit | null; stroke?: Stroke | Inherit | null; vertexColors?: number[] };\n\n// ─── Built-in painters ─────────────────────────────────────────────────\nexport const PATH_PAINTER: NodeShapeEntry<unknown, RectPose> = {\n id: 'kit:path',\n matches: (node) => {\n if (!paintsPose(node.pose)) return false;\n const d = node.data as { path?: Path } | null;\n return d?.path != null;\n },\n // Memoized: a pure function of `(data, pose)`, and `pathInPoseFrame`\n // allocates on every call but the identity (`kind: 'rect'`) branch. See\n // PAINT_SLOT.\n paint: (node, pose, ctx) => ctx?.derivedPath != null\n // A pose override's path stands in for `data.path`, already where it is drawn.\n ? DERIVED_PAINTER.paint(node, pose, ctx)\n : withAnimatedVertexColors(nodeMemo(node, PAINT_SLOT, pose, () => {\n const d = node.data as PathData & { path: Path };\n const projected = pathInPoseFrame(d.path, pose);\n const { fill: declared, stroke: strokeSpec } = pathPaintOf(d, ctx?.inherited);\n // The pose is baked into `projected` rather than emitted as a transform,\n // so a box-relative gradient has to be baked onto the same box here or\n // it would arrive in the renderer referring to a frame that never exists.\n const fill = declared && commandFill(declared, pose);\n const stroke = strokeSpec && commandStroke(strokeSpec, pose);\n const cmd: DrawCommand = {\n kind: 'path',\n path: projected,\n ...(fill ? { fill } : {}),\n ...(fill && d.vertexColors ? { vertexColors: d.vertexColors } : {}),\n ...(stroke ? { stroke } : {}),\n };\n return [cmd];\n }, ctx?.inherited), ctx),\n silhouette: (node, pose, ctx) => {\n if (ctx?.derivedPath != null) return ctx.derivedPath;\n const d = node.data as { path: Path };\n return pathInPoseFrame(d.path, pose);\n },\n ink: (node, _pose, ctx) => {\n const { fill, stroke } = pathPaintOf(node.data as PathData, ctx?.inherited);\n return { filled: fill !== null, ...inkReach(stroke ?? null, ctx?.scale) };\n },\n // `pathInPoseFrame` fits the path's own bounds to the pose box.\n bounds: (node, pose, ctx) =>\n poseBoxBounds(pose, resolveStroke((node.data as PathData).stroke, ctx?.inherited?.stroke), ctx),\n drawnBounds: (node, pose, ctx) => {\n if (ctx?.derivedPath != null) return derivedDrawnBounds(node, ctx);\n const d = node.data as PathData & { path: Path };\n return strokedPoseBox(node, pose, resolveStroke(d.stroke, ctx?.inherited?.stroke), ctx, (at) => pathInPoseFrame(d.path, at));\n },\n};\n"],"mappings":";;;;;;;;;;;AAoBA,IAAa,eAAkD;CAC7D,IAAI;CACJ,UAAU,SAAS;EACjB,IAAI,CAAC,WAAW,KAAK,IAAI,GAAG,OAAO;EAEnC,OADU,KAAK,MACL,QAAQ;CACpB;CAIA,QAAQ,MAAM,MAAM,QAAQ,KAAK,eAAe,OAE5C,gBAAgB,MAAM,MAAM,MAAM,GAAG,IACrC,yBAAyB,SAAS,MAAM,YAAY,YAAY;EAClE,MAAM,IAAI,KAAK;EACf,MAAM,YAAY,gBAAgB,EAAE,MAAM,IAAI;EAC9C,MAAM,EAAE,MAAM,UAAU,QAAQ,eAAe,YAAY,GAAG,KAAK,SAAS;EAI5E,MAAM,OAAO,YAAY,YAAY,UAAU,IAAI;EACnD,MAAM,SAAS,cAAc,cAAc,YAAY,IAAI;EAQ3D,OAAO,CAAC;GANN,MAAM;GACN,MAAM;GACN,GAAI,OAAO,EAAE,KAAK,IAAI,CAAC;GACvB,GAAI,QAAQ,EAAE,eAAe,EAAE,cAAc,EAAE,aAAa,IAAI,CAAC;GACjE,GAAI,SAAS,EAAE,OAAO,IAAI,CAAC;EAErB,CAAG;CACb,GAAG,KAAK,SAAS,GAAG,GAAG;CACvB,aAAa,MAAM,MAAM,QAAQ;EAC/B,IAAI,KAAK,eAAe,MAAM,OAAO,IAAI;EACzC,MAAM,IAAI,KAAK;EACf,OAAO,gBAAgB,EAAE,MAAM,IAAI;CACrC;CACA,MAAM,MAAM,OAAO,QAAQ;EACzB,MAAM,EAAE,MAAM,WAAW,YAAY,KAAK,MAAkB,KAAK,SAAS;EAC1E,OAAO;GAAE,QAAQ,SAAS;GAAM,GAAG,SAAS,UAAU,MAAM,KAAK,KAAK;EAAE;CAC1E;CAEA,SAAS,MAAM,MAAM,QACnB,cAAc,MAAM,cAAe,KAAK,KAAkB,QAAQ,KAAK,WAAW,MAAM,GAAG,GAAG;CAChG,cAAc,MAAM,MAAM,QAAQ;EAChC,IAAI,KAAK,eAAe,MAAM,OAAO,mBAAmB,MAAM,GAAG;EACjE,MAAM,IAAI,KAAK;EACf,OAAO,eAAe,MAAM,MAAM,cAAc,EAAE,QAAQ,KAAK,WAAW,MAAM,GAAG,MAAM,OAAO,gBAAgB,EAAE,MAAM,EAAE,CAAC;CAC7H;AACF"}
@@ -1,16 +1,15 @@
1
- import { DEFAULT_SHAPE_FILL } from "../../util/paint.js";
2
- import { resolveNodeFill } from "./nodePaint.js";
3
1
  import { paintsPose, poseBoxBounds } from "./poseBox.js";
4
2
  import { commandFill } from "@weasel-js/renderer";
3
+ import { resolveFill } from "@weasel-js/paint";
5
4
 
6
5
  //#region src/canvas/nodeShapes/rectFallbackPainter.ts
7
6
  var RECT_FALLBACK_PAINTER = {
8
7
  id: "kit:rect-fallback",
9
- matches: (node) => paintsPose(node.pose),
10
- paint: (node, pose) => {
8
+ matches: (node) => node.kind !== "group" && paintsPose(node.pose),
9
+ paint: (node, pose, ctx) => {
11
10
  const d = node.data;
12
11
  const p = pose;
13
- const declared = resolveNodeFill(d?.fill, DEFAULT_SHAPE_FILL);
12
+ const declared = resolveFill(d?.fill, ctx?.inherited?.fill);
14
13
  const fill = declared && commandFill(declared, p);
15
14
  return [{
16
15
  kind: "path",
@@ -1 +1 @@
1
- {"version":3,"file":"rectFallbackPainter.js","names":[],"sources":["../../../src/canvas/nodeShapes/rectFallbackPainter.ts"],"sourcesContent":["import type { Fill } from '@weasel-js/paint';\nimport type { NodeShapeEntry } from 'canvas/NodeShape';\nimport type { RectPose } from 'core/scene/types';\nimport { commandFill } from '@weasel-js/renderer';\nimport { DEFAULT_SHAPE_FILL } from '../../util/paint';\nimport { resolveNodeFill } from './nodePaint';\nimport { paintsPose, poseBoxBounds } from './poseBox';\n\n// ─── Built-in painters ─────────────────────────────────────────────────\nexport const RECT_FALLBACK_PAINTER: NodeShapeEntry<unknown, RectPose> = {\n // Last-resort painter — matches any rect-posed node, so it must be\n // registered last within `'normal'`. Consumers who want a different\n // fallback should register their own painter at `'high'` priority and let\n // this one never fire (or unregister it explicitly).\n id: 'kit:rect-fallback',\n matches: (node) => paintsPose(node.pose),\n paint: (node, pose) => {\n const d = node.data as { fill?: Fill | null } | null;\n const p = pose;\n const declared = resolveNodeFill(d?.fill, DEFAULT_SHAPE_FILL);\n const fill = declared && commandFill(declared, p);\n return [{\n kind: 'path',\n path: { kind: 'rect', x: p.x, y: p.y, width: p.width, height: p.height },\n ...(fill ? { fill } : {}),\n }];\n },\n silhouette: (_node, pose) => {\n const p = pose;\n return { kind: 'rect', x: p.x, y: p.y, width: p.width, height: p.height };\n },\n bounds: (_node, pose) => poseBoxBounds(pose, null, undefined),\n};\n"],"mappings":";;;;;;AASA,IAAa,wBAA2D;CAKtE,IAAI;CACJ,UAAU,SAAS,WAAW,KAAK,IAAI;CACvC,QAAQ,MAAM,SAAS;EACrB,MAAM,IAAI,KAAK;EACf,MAAM,IAAI;EACV,MAAM,WAAW,gBAAgB,GAAG,MAAM,kBAAkB;EAC5D,MAAM,OAAO,YAAY,YAAY,UAAU,CAAC;EAChD,OAAO,CAAC;GACN,MAAM;GACN,MAAM;IAAE,MAAM;IAAQ,GAAG,EAAE;IAAG,GAAG,EAAE;IAAG,OAAO,EAAE;IAAO,QAAQ,EAAE;GAAO;GACvE,GAAI,OAAO,EAAE,KAAK,IAAI,CAAC;EACzB,CAAC;CACH;CACA,aAAa,OAAO,SAAS;EAC3B,MAAM,IAAI;EACV,OAAO;GAAE,MAAM;GAAQ,GAAG,EAAE;GAAG,GAAG,EAAE;GAAG,OAAO,EAAE;GAAO,QAAQ,EAAE;EAAO;CAC1E;CACA,SAAS,OAAO,SAAS,cAAc,MAAM,MAAM,MAAS;AAC9D"}
1
+ {"version":3,"file":"rectFallbackPainter.js","names":[],"sources":["../../../src/canvas/nodeShapes/rectFallbackPainter.ts"],"sourcesContent":["import type { Fill, Inherit } from '@weasel-js/paint';\nimport type { NodeShapeEntry } from 'canvas/NodeShape';\nimport type { RectPose } from 'core/scene/types';\nimport { commandFill } from '@weasel-js/renderer';\nimport { resolveFill } from '@weasel-js/paint';\nimport { paintsPose, poseBoxBounds } from './poseBox';\n\n// ─── Built-in painters ─────────────────────────────────────────────────\nexport const RECT_FALLBACK_PAINTER: NodeShapeEntry<unknown, RectPose> = {\n // Last-resort painter — matches any rect-posed mark, so it must be\n // registered last within `'normal'`.\n // Consumers who want a different\n // fallback should register their own painter at `'high'` priority and let\n // this one never fire (or unregister it explicitly).\n id: 'kit:rect-fallback',\n matches: (node) => node.kind !== 'group' && paintsPose(node.pose),\n paint: (node, pose, ctx) => {\n const d = node.data as { fill?: Fill | Inherit | null } | null;\n const p = pose;\n const declared = resolveFill(d?.fill, ctx?.inherited?.fill);\n const fill = declared && commandFill(declared, p);\n return [{\n kind: 'path',\n path: { kind: 'rect', x: p.x, y: p.y, width: p.width, height: p.height },\n ...(fill ? { fill } : {}),\n }];\n },\n silhouette: (_node, pose) => {\n const p = pose;\n return { kind: 'rect', x: p.x, y: p.y, width: p.width, height: p.height };\n },\n bounds: (_node, pose) => poseBoxBounds(pose, null, undefined),\n};\n"],"mappings":";;;;;AAQA,IAAa,wBAA2D;CAMtE,IAAI;CACJ,UAAU,SAAS,KAAK,SAAS,WAAW,WAAW,KAAK,IAAI;CAChE,QAAQ,MAAM,MAAM,QAAQ;EAC1B,MAAM,IAAI,KAAK;EACf,MAAM,IAAI;EACV,MAAM,WAAW,YAAY,GAAG,MAAM,KAAK,WAAW,IAAI;EAC1D,MAAM,OAAO,YAAY,YAAY,UAAU,CAAC;EAChD,OAAO,CAAC;GACN,MAAM;GACN,MAAM;IAAE,MAAM;IAAQ,GAAG,EAAE;IAAG,GAAG,EAAE;IAAG,OAAO,EAAE;IAAO,QAAQ,EAAE;GAAO;GACvE,GAAI,OAAO,EAAE,KAAK,IAAI,CAAC;EACzB,CAAC;CACH;CACA,aAAa,OAAO,SAAS;EAC3B,MAAM,IAAI;EACV,OAAO;GAAE,MAAM;GAAQ,GAAG,EAAE;GAAG,GAAG,EAAE;GAAG,OAAO,EAAE;GAAO,QAAQ,EAAE;EAAO;CAC1E;CACA,SAAS,OAAO,SAAS,cAAc,MAAM,MAAM,MAAS;AAC9D"}
@@ -1,11 +1,11 @@
1
1
  import { nodeMemo } from "../../core/scene/nodeMemo.js";
2
- import { DEFAULT_SHAPE_FILL } from "../../util/paint.js";
3
2
  import { PAINT_SLOT } from "./memo.js";
4
- import { inkReach, resolveNodeFill, resolveNodeStroke, withAnimatedVertexColors } from "./nodePaint.js";
3
+ import { inkReach, withAnimatedVertexColors } from "./nodePaint.js";
5
4
  import { outsetPoseBox, strokedPoseBox } from "./strokedBounds.js";
6
5
  import { paintsPose, poseBoxBounds } from "./poseBox.js";
7
6
  import { commandFill, commandStroke } from "@weasel-js/renderer";
8
7
  import { ellipsePath, regularPolygonPath, starPath } from "@weasel-js/geom";
8
+ import { resolveFill, resolveStroke } from "@weasel-js/paint";
9
9
 
10
10
  //#region src/canvas/nodeShapes/shapePainter.ts
11
11
  /** Built-in shape dispatcher — matches when `data.shape` names a kit-known
@@ -22,9 +22,9 @@ var SHAPE_PAINTER = {
22
22
  paint: (node, pose, ctx) => withAnimatedVertexColors(nodeMemo(node, PAINT_SLOT, pose, () => {
23
23
  const d = node.data;
24
24
  const path = pathForShape(d, pose);
25
- const declaredFill = resolveNodeFill(d.fill, DEFAULT_SHAPE_FILL);
25
+ const declaredFill = resolveFill(d.fill, ctx?.inherited?.fill);
26
26
  const shapeFill = declaredFill && commandFill(declaredFill, pose);
27
- const strokeSpec = resolveNodeStroke(d.stroke);
27
+ const strokeSpec = resolveStroke(d.stroke, ctx?.inherited?.stroke);
28
28
  const stroke = strokeSpec && commandStroke(strokeSpec, pose);
29
29
  return [{
30
30
  kind: "path",
@@ -32,7 +32,7 @@ var SHAPE_PAINTER = {
32
32
  ...shapeFill ? { fill: shapeFill } : {},
33
33
  ...stroke ? { stroke } : {}
34
34
  }];
35
- }), ctx),
35
+ }, ctx?.inherited), ctx),
36
36
  silhouette: (node, pose) => {
37
37
  const d = node.data;
38
38
  return pathForShape(d, pose);
@@ -40,14 +40,14 @@ var SHAPE_PAINTER = {
40
40
  ink: (node, _pose, ctx) => {
41
41
  const d = node.data;
42
42
  return {
43
- filled: resolveNodeFill(d.fill, DEFAULT_SHAPE_FILL) !== null,
44
- ...inkReach(resolveNodeStroke(d.stroke), ctx?.scale)
43
+ filled: resolveFill(d.fill, ctx?.inherited?.fill) !== null,
44
+ ...inkReach(resolveStroke(d.stroke, ctx?.inherited?.stroke), ctx?.scale)
45
45
  };
46
46
  },
47
- bounds: (node, pose, ctx) => poseBoxBounds(pose, resolveNodeStroke(node.data.stroke), ctx),
47
+ bounds: (node, pose, ctx) => poseBoxBounds(pose, resolveStroke(node.data.stroke, ctx?.inherited?.stroke), ctx),
48
48
  drawnBounds: (node, pose, ctx) => {
49
49
  const d = node.data;
50
- const stroke = resolveNodeStroke(d.stroke);
50
+ const stroke = resolveStroke(d.stroke, ctx?.inherited?.stroke);
51
51
  return d.shape === "polygon" || d.shape === "star" ? strokedPoseBox(node, pose, stroke, ctx, (at) => pathForShape(d, at)) : outsetPoseBox(pose, stroke, ctx);
52
52
  }
53
53
  };
@@ -1 +1 @@
1
- {"version":3,"file":"shapePainter.js","names":[],"sources":["../../../src/canvas/nodeShapes/shapePainter.ts"],"sourcesContent":["import {\n ellipsePath,\n regularPolygonPath,\n starPath,\n type Path\n} from '@weasel-js/geom';\nimport type { Stroke, Fill } from '@weasel-js/paint';\nimport type { NodeShapeEntry } from 'canvas/NodeShape';\nimport { nodeMemo } from 'core/scene/nodeMemo';\nimport type { RectPose } from 'core/scene/types';\nimport { commandFill, commandStroke } from '@weasel-js/renderer';\nimport { DEFAULT_SHAPE_FILL } from '../../util/paint';\nimport { PAINT_SLOT } from './memo';\nimport { inkReach, withAnimatedVertexColors, resolveNodeFill, resolveNodeStroke } from './nodePaint';\nimport { paintsPose, poseBoxBounds } from './poseBox';\nimport { outsetPoseBox, strokedPoseBox } from './strokedBounds';\n\n// ─── Built-in painters ─────────────────────────────────────────────────\n/** Built-in shape dispatcher — matches when `data.shape` names a kit-known\n * shape kind. Computes both paint and silhouette from the pose so consumer\n * scenes can declare clipping/rendering directly in JSON without\n * registering a custom painter. */\nexport const SHAPE_PAINTER: NodeShapeEntry<unknown, RectPose> = {\n id: 'kit:shape',\n matches: (node) => {\n if (!paintsPose(node.pose)) return false;\n const s = (node.data as { shape?: string } | null)?.shape;\n return s != null && SHAPE_KINDS.has(s);\n },\n // Memoized: a pure function of `(data, pose)`, and `pathForShape` builds a\n // fresh `Uint8Array` + `Float32Array` for every ellipse, polygon and star.\n // See PAINT_SLOT.\n paint: (node, pose, ctx) => withAnimatedVertexColors(nodeMemo(node, PAINT_SLOT, pose, () => {\n const d = node.data as { shape: string; fill?: Fill | null; stroke?: Stroke | null; sides?: number; points?: number };\n const path = pathForShape(d, pose);\n const declaredFill = resolveNodeFill(d.fill, DEFAULT_SHAPE_FILL);\n const shapeFill = declaredFill && commandFill(declaredFill, pose);\n const strokeSpec = resolveNodeStroke(d.stroke);\n const stroke = strokeSpec && commandStroke(strokeSpec, pose);\n return [{\n kind: 'path',\n path,\n ...(shapeFill ? { fill: shapeFill } : {}),\n ...(stroke ? { stroke } : {}),\n }];\n }), ctx),\n silhouette: (node, pose) => {\n const d = node.data as { shape: string; sides?: number; points?: number };\n return pathForShape(d, pose);\n },\n // `paint` above emits the default fill when none is declared, so a shape\n // is filled unless it declares `fill: null` — unlike `kit:path`, it has no\n // implicit stroke-only form.\n ink: (node, _pose, ctx) => {\n const d = node.data as { fill?: Fill | null; stroke?: Stroke | null };\n return {\n filled: resolveNodeFill(d.fill, DEFAULT_SHAPE_FILL) !== null,\n ...inkReach(resolveNodeStroke(d.stroke), ctx?.scale),\n };\n },\n // Every shape kind is inscribed in the pose box.\n bounds: (node, pose, ctx) =>\n poseBoxBounds(pose, resolveNodeStroke((node.data as { stroke?: Stroke | null }).stroke), ctx),\n // A rect's or an ellipse's stroke reaches its box evenly; a polygon's or a\n // star's corners are sharper than a right angle, so their miters are measured.\n drawnBounds: (node, pose, ctx) => {\n const d = node.data as { shape: string; stroke?: Stroke | null; sides?: number; points?: number };\n const stroke = resolveNodeStroke(d.stroke);\n return d.shape === 'polygon' || d.shape === 'star'\n ? strokedPoseBox(node, pose, stroke, ctx, (at) => pathForShape(d, at))\n : outsetPoseBox(pose, stroke, ctx);\n },\n};\n\nexport const SHAPE_KINDS = new Set(['rect', 'ellipse', 'polygon', 'star']);\n\nexport function pathForShape(\n d: { shape: string; sides?: number; points?: number },\n p: RectPose,\n): Path {\n switch (d.shape) {\n case 'ellipse':\n return ellipsePath(p);\n case 'polygon': {\n const cx = p.x + p.width / 2;\n const cy = p.y + p.height / 2;\n const r = Math.min(p.width, p.height) / 2;\n return regularPolygonPath({ x: cx, y: cy }, r, d.sides ?? 6);\n }\n case 'star': {\n const cx = p.x + p.width / 2;\n const cy = p.y + p.height / 2;\n const r = Math.min(p.width, p.height) / 2;\n return starPath({ x: cx, y: cy }, r, d.points ?? 5);\n }\n case 'rect':\n default:\n return { kind: 'rect', x: p.x, y: p.y, width: p.width, height: p.height };\n }\n}\n"],"mappings":";;;;;;;;;;;;;;AAsBA,IAAa,gBAAmD;CAC9D,IAAI;CACJ,UAAU,SAAS;EACjB,IAAI,CAAC,WAAW,KAAK,IAAI,GAAG,OAAO;EACnC,MAAM,IAAK,KAAK,MAAoC;EACpD,OAAO,KAAK,QAAQ,YAAY,IAAI,CAAC;CACvC;CAIA,QAAQ,MAAM,MAAM,QAAQ,yBAAyB,SAAS,MAAM,YAAY,YAAY;EAC1F,MAAM,IAAI,KAAK;EACf,MAAM,OAAO,aAAa,GAAG,IAAI;EACjC,MAAM,eAAe,gBAAgB,EAAE,MAAM,kBAAkB;EAC/D,MAAM,YAAY,gBAAgB,YAAY,cAAc,IAAI;EAChE,MAAM,aAAa,kBAAkB,EAAE,MAAM;EAC7C,MAAM,SAAS,cAAc,cAAc,YAAY,IAAI;EAC3D,OAAO,CAAC;GACN,MAAM;GACN;GACA,GAAI,YAAY,EAAE,MAAM,UAAU,IAAI,CAAC;GACvC,GAAI,SAAS,EAAE,OAAO,IAAI,CAAC;EAC7B,CAAC;CACH,CAAC,GAAG,GAAG;CACP,aAAa,MAAM,SAAS;EAC1B,MAAM,IAAI,KAAK;EACf,OAAO,aAAa,GAAG,IAAI;CAC7B;CAIA,MAAM,MAAM,OAAO,QAAQ;EACzB,MAAM,IAAI,KAAK;EACf,OAAO;GACL,QAAQ,gBAAgB,EAAE,MAAM,kBAAkB,MAAM;GACxD,GAAG,SAAS,kBAAkB,EAAE,MAAM,GAAG,KAAK,KAAK;EACrD;CACF;CAEA,SAAS,MAAM,MAAM,QACnB,cAAc,MAAM,kBAAmB,KAAK,KAAoC,MAAM,GAAG,GAAG;CAG9F,cAAc,MAAM,MAAM,QAAQ;EAChC,MAAM,IAAI,KAAK;EACf,MAAM,SAAS,kBAAkB,EAAE,MAAM;EACzC,OAAO,EAAE,UAAU,aAAa,EAAE,UAAU,SACxC,eAAe,MAAM,MAAM,QAAQ,MAAM,OAAO,aAAa,GAAG,EAAE,CAAC,IACnE,cAAc,MAAM,QAAQ,GAAG;CACrC;AACF;AAEA,IAAa,8BAAc,IAAI,IAAI;CAAC;CAAQ;CAAW;CAAW;AAAM,CAAC;AAEzE,SAAgB,aACd,GACA,GACM;CACN,QAAQ,EAAE,OAAV;EACE,KAAK,WACH,OAAO,YAAY,CAAC;EACtB,KAAK,WAAW;GACd,MAAM,KAAK,EAAE,IAAI,EAAE,QAAQ;GAC3B,MAAM,KAAK,EAAE,IAAI,EAAE,SAAS;GAC5B,MAAM,IAAI,KAAK,IAAI,EAAE,OAAO,EAAE,MAAM,IAAI;GACxC,OAAO,mBAAmB;IAAE,GAAG;IAAI,GAAG;GAAG,GAAG,GAAG,EAAE,SAAS,CAAC;EAC7D;EACA,KAAK,QAAQ;GACX,MAAM,KAAK,EAAE,IAAI,EAAE,QAAQ;GAC3B,MAAM,KAAK,EAAE,IAAI,EAAE,SAAS;GAC5B,MAAM,IAAI,KAAK,IAAI,EAAE,OAAO,EAAE,MAAM,IAAI;GACxC,OAAO,SAAS;IAAE,GAAG;IAAI,GAAG;GAAG,GAAG,GAAG,EAAE,UAAU,CAAC;EACpD;EAEA,SACE,OAAO;GAAE,MAAM;GAAQ,GAAG,EAAE;GAAG,GAAG,EAAE;GAAG,OAAO,EAAE;GAAO,QAAQ,EAAE;EAAO;CAC5E;AACF"}
1
+ {"version":3,"file":"shapePainter.js","names":[],"sources":["../../../src/canvas/nodeShapes/shapePainter.ts"],"sourcesContent":["import {\n ellipsePath,\n regularPolygonPath,\n starPath,\n type Path\n} from '@weasel-js/geom';\nimport type { Stroke, Fill, Inherit } from '@weasel-js/paint';\nimport type { NodeShapeEntry } from 'canvas/NodeShape';\nimport { nodeMemo } from 'core/scene/nodeMemo';\nimport type { RectPose } from 'core/scene/types';\nimport { commandFill, commandStroke } from '@weasel-js/renderer';\nimport { PAINT_SLOT } from './memo';\nimport { inkReach, withAnimatedVertexColors } from './nodePaint';\nimport { resolveFill, resolveStroke } from '@weasel-js/paint';\nimport { paintsPose, poseBoxBounds } from './poseBox';\nimport { outsetPoseBox, strokedPoseBox } from './strokedBounds';\n\ntype ShapeData = {\n shape: string;\n fill?: Fill | Inherit | null;\n stroke?: Stroke | Inherit | null;\n sides?: number;\n points?: number;\n};\n\n// ─── Built-in painters ─────────────────────────────────────────────────\n/** Built-in shape dispatcher — matches when `data.shape` names a kit-known\n * shape kind. Computes both paint and silhouette from the pose so consumer\n * scenes can declare clipping/rendering directly in JSON without\n * registering a custom painter. */\nexport const SHAPE_PAINTER: NodeShapeEntry<unknown, RectPose> = {\n id: 'kit:shape',\n matches: (node) => {\n if (!paintsPose(node.pose)) return false;\n const s = (node.data as { shape?: string } | null)?.shape;\n return s != null && SHAPE_KINDS.has(s);\n },\n // Memoized: a pure function of `(data, pose)`, and `pathForShape` builds a\n // fresh `Uint8Array` + `Float32Array` for every ellipse, polygon and star.\n // See PAINT_SLOT.\n paint: (node, pose, ctx) => withAnimatedVertexColors(nodeMemo(node, PAINT_SLOT, pose, () => {\n const d = node.data as ShapeData;\n const path = pathForShape(d, pose);\n const declaredFill = resolveFill(d.fill, ctx?.inherited?.fill);\n const shapeFill = declaredFill && commandFill(declaredFill, pose);\n const strokeSpec = resolveStroke(d.stroke, ctx?.inherited?.stroke);\n const stroke = strokeSpec && commandStroke(strokeSpec, pose);\n return [{\n kind: 'path',\n path,\n ...(shapeFill ? { fill: shapeFill } : {}),\n ...(stroke ? { stroke } : {}),\n }];\n }, ctx?.inherited), ctx),\n silhouette: (node, pose) => {\n const d = node.data as { shape: string; sides?: number; points?: number };\n return pathForShape(d, pose);\n },\n ink: (node, _pose, ctx) => {\n const d = node.data as ShapeData;\n return {\n filled: resolveFill(d.fill, ctx?.inherited?.fill) !== null,\n ...inkReach(resolveStroke(d.stroke, ctx?.inherited?.stroke), ctx?.scale),\n };\n },\n // Every shape kind is inscribed in the pose box.\n bounds: (node, pose, ctx) =>\n poseBoxBounds(pose, resolveStroke((node.data as ShapeData).stroke, ctx?.inherited?.stroke), ctx),\n // A rect's or an ellipse's stroke reaches its box evenly; a polygon's or a\n // star's corners are sharper than a right angle, so their miters are measured.\n drawnBounds: (node, pose, ctx) => {\n const d = node.data as ShapeData;\n const stroke = resolveStroke(d.stroke, ctx?.inherited?.stroke);\n return d.shape === 'polygon' || d.shape === 'star'\n ? strokedPoseBox(node, pose, stroke, ctx, (at) => pathForShape(d, at))\n : outsetPoseBox(pose, stroke, ctx);\n },\n};\n\nexport const SHAPE_KINDS = new Set(['rect', 'ellipse', 'polygon', 'star']);\n\nexport function pathForShape(\n d: { shape: string; sides?: number; points?: number },\n p: RectPose,\n): Path {\n switch (d.shape) {\n case 'ellipse':\n return ellipsePath(p);\n case 'polygon': {\n const cx = p.x + p.width / 2;\n const cy = p.y + p.height / 2;\n const r = Math.min(p.width, p.height) / 2;\n return regularPolygonPath({ x: cx, y: cy }, r, d.sides ?? 6);\n }\n case 'star': {\n const cx = p.x + p.width / 2;\n const cy = p.y + p.height / 2;\n const r = Math.min(p.width, p.height) / 2;\n return starPath({ x: cx, y: cy }, r, d.points ?? 5);\n }\n case 'rect':\n default:\n return { kind: 'rect', x: p.x, y: p.y, width: p.width, height: p.height };\n }\n}\n"],"mappings":";;;;;;;;;;;;;;AA8BA,IAAa,gBAAmD;CAC9D,IAAI;CACJ,UAAU,SAAS;EACjB,IAAI,CAAC,WAAW,KAAK,IAAI,GAAG,OAAO;EACnC,MAAM,IAAK,KAAK,MAAoC;EACpD,OAAO,KAAK,QAAQ,YAAY,IAAI,CAAC;CACvC;CAIA,QAAQ,MAAM,MAAM,QAAQ,yBAAyB,SAAS,MAAM,YAAY,YAAY;EAC1F,MAAM,IAAI,KAAK;EACf,MAAM,OAAO,aAAa,GAAG,IAAI;EACjC,MAAM,eAAe,YAAY,EAAE,MAAM,KAAK,WAAW,IAAI;EAC7D,MAAM,YAAY,gBAAgB,YAAY,cAAc,IAAI;EAChE,MAAM,aAAa,cAAc,EAAE,QAAQ,KAAK,WAAW,MAAM;EACjE,MAAM,SAAS,cAAc,cAAc,YAAY,IAAI;EAC3D,OAAO,CAAC;GACN,MAAM;GACN;GACA,GAAI,YAAY,EAAE,MAAM,UAAU,IAAI,CAAC;GACvC,GAAI,SAAS,EAAE,OAAO,IAAI,CAAC;EAC7B,CAAC;CACH,GAAG,KAAK,SAAS,GAAG,GAAG;CACvB,aAAa,MAAM,SAAS;EAC1B,MAAM,IAAI,KAAK;EACf,OAAO,aAAa,GAAG,IAAI;CAC7B;CACA,MAAM,MAAM,OAAO,QAAQ;EACzB,MAAM,IAAI,KAAK;EACf,OAAO;GACL,QAAQ,YAAY,EAAE,MAAM,KAAK,WAAW,IAAI,MAAM;GACtD,GAAG,SAAS,cAAc,EAAE,QAAQ,KAAK,WAAW,MAAM,GAAG,KAAK,KAAK;EACzE;CACF;CAEA,SAAS,MAAM,MAAM,QACnB,cAAc,MAAM,cAAe,KAAK,KAAmB,QAAQ,KAAK,WAAW,MAAM,GAAG,GAAG;CAGjG,cAAc,MAAM,MAAM,QAAQ;EAChC,MAAM,IAAI,KAAK;EACf,MAAM,SAAS,cAAc,EAAE,QAAQ,KAAK,WAAW,MAAM;EAC7D,OAAO,EAAE,UAAU,aAAa,EAAE,UAAU,SACxC,eAAe,MAAM,MAAM,QAAQ,MAAM,OAAO,aAAa,GAAG,EAAE,CAAC,IACnE,cAAc,MAAM,QAAQ,GAAG;CACrC;AACF;AAEA,IAAa,8BAAc,IAAI,IAAI;CAAC;CAAQ;CAAW;CAAW;AAAM,CAAC;AAEzE,SAAgB,aACd,GACA,GACM;CACN,QAAQ,EAAE,OAAV;EACE,KAAK,WACH,OAAO,YAAY,CAAC;EACtB,KAAK,WAAW;GACd,MAAM,KAAK,EAAE,IAAI,EAAE,QAAQ;GAC3B,MAAM,KAAK,EAAE,IAAI,EAAE,SAAS;GAC5B,MAAM,IAAI,KAAK,IAAI,EAAE,OAAO,EAAE,MAAM,IAAI;GACxC,OAAO,mBAAmB;IAAE,GAAG;IAAI,GAAG;GAAG,GAAG,GAAG,EAAE,SAAS,CAAC;EAC7D;EACA,KAAK,QAAQ;GACX,MAAM,KAAK,EAAE,IAAI,EAAE,QAAQ;GAC3B,MAAM,KAAK,EAAE,IAAI,EAAE,SAAS;GAC5B,MAAM,IAAI,KAAK,IAAI,EAAE,OAAO,EAAE,MAAM,IAAI;GACxC,OAAO,SAAS;IAAE,GAAG;IAAI,GAAG;GAAG,GAAG,GAAG,EAAE,UAAU,CAAC;EACpD;EAEA,SACE,OAAO;GAAE,MAAM;GAAQ,GAAG,EAAE;GAAG,GAAG,EAAE;GAAG,OAAO,EAAE;GAAO,QAAQ,EAAE;EAAO;CAC5E;AACF"}
@@ -1,9 +1,10 @@
1
1
  import { nodeMemo } from "../../core/scene/nodeMemo.js";
2
2
  import { PAINT_SLOT } from "./memo.js";
3
- import { inkReach, resolveNodeFill, resolveNodeStroke, strokeOutsetPerAxis } from "./nodePaint.js";
3
+ import { inkReach, strokeOutsetPerAxis } from "./nodePaint.js";
4
4
  import { paintsPose } from "./poseBox.js";
5
5
  import { commandFill, textCommandFromPose } from "@weasel-js/renderer";
6
6
  import { PATH_L, PATH_M, PATH_Z } from "@weasel-js/geom";
7
+ import { INHERIT, resolveFill, resolveStroke } from "@weasel-js/paint";
7
8
  import { textLineBoxes } from "@weasel-js/text";
8
9
 
9
10
  //#region src/canvas/nodeShapes/textPainter.ts
@@ -39,14 +40,14 @@ function textBoxOf(d, p) {
39
40
  * {@link textBoxOf} insets. The stroke goes through the same resolver every
40
41
  * other node kind's does, so one carrying no paint is dropped here rather
41
42
  * than throwing in the renderer. */
42
- function textPoseOf(d, p) {
43
+ function textPoseOf(d, p, inherited) {
43
44
  return {
44
45
  ...textBoxOf(d, p),
45
46
  text: d.text,
46
47
  runs: d.runs,
47
48
  style: d.style,
48
- fill: d.fill,
49
- stroke: resolveNodeStroke(d.stroke) ?? void 0,
49
+ fill: d.fill === INHERIT ? resolveFill(d.fill, inherited?.fill) : d.fill,
50
+ stroke: resolveStroke(d.stroke, inherited?.stroke) ?? void 0,
50
51
  verticalAlign: d.verticalAlign
51
52
  };
52
53
  }
@@ -57,11 +58,11 @@ var TEXT_PAINTER = {
57
58
  if (!paintsPose(node.pose)) return false;
58
59
  return node.data?.text != null;
59
60
  },
60
- paint: (node, pose) => nodeMemo(node, PAINT_SLOT, pose, () => {
61
+ paint: (node, pose, ctx) => nodeMemo(node, PAINT_SLOT, pose, () => {
61
62
  const d = node.data;
62
- const laid = textCommandFromPose(textPoseOf(d, pose));
63
+ const laid = textCommandFromPose(textPoseOf(d, pose, ctx?.inherited));
63
64
  const text = d.overflow === "clip" ? clippedToBox(laid, pose) : laid;
64
- const bg = resolveNodeFill(d.background, null);
65
+ const bg = resolveFill(d.background);
65
66
  if (!bg) return [text];
66
67
  const fill = commandFill(bg, pose);
67
68
  return [{
@@ -75,10 +76,10 @@ var TEXT_PAINTER = {
75
76
  },
76
77
  ...fill ? { fill } : {}
77
78
  }, text];
78
- }),
79
+ }, ctx?.inherited),
79
80
  silhouette: (node, pose) => {
80
81
  const d = node.data;
81
- if (resolveNodeFill(d.background, null)) return {
82
+ if (resolveFill(d.background)) return {
82
83
  kind: "rect",
83
84
  x: pose.x,
84
85
  y: pose.y,
@@ -94,13 +95,13 @@ var TEXT_PAINTER = {
94
95
  const d = node.data;
95
96
  return {
96
97
  filled: true,
97
- ...inkReach(resolveNodeStroke(d.stroke), ctx?.scale)
98
+ ...inkReach(resolveStroke(d.stroke, ctx?.inherited?.stroke), ctx?.scale)
98
99
  };
99
100
  },
100
101
  drawnBounds: (node, pose, ctx) => {
101
102
  const d = node.data;
102
- const stroke = resolveNodeStroke(d.stroke);
103
- return stroke ? strokedLinesBox(pose, textLineBoxes(textPoseOf(d, pose)), strokeOutsetPerAxis(stroke, ctx)) : null;
103
+ const stroke = resolveStroke(d.stroke, ctx?.inherited?.stroke);
104
+ return stroke ? strokedLinesBox(pose, textLineBoxes(textPoseOf(d, pose, ctx?.inherited)), strokeOutsetPerAxis(stroke, ctx)) : null;
104
105
  }
105
106
  };
106
107
  /** Each of `rects` cut to `box`, leaving out any it does not overlap. */
@@ -1 +1 @@
1
- {"version":3,"file":"textPainter.js","names":[],"sources":["../../../src/canvas/nodeShapes/textPainter.ts"],"sourcesContent":["import {\n PATH_L,\n PATH_M,\n PATH_Z,\n type PolygonPath,\n type Rect,\n} from '@weasel-js/geom';\nimport type { Stroke, Fill } from '@weasel-js/paint';\nimport type { StyledRun, TextPose, TextStyle, TextVerticalAlign } from '@weasel-js/text';\nimport { textLineBoxes } from '@weasel-js/text';\nimport type { NodeShapeEntry } from 'canvas/NodeShape';\nimport { nodeMemo } from 'core/scene/nodeMemo';\nimport type { RectPose } from 'core/scene/types';\nimport { commandFill, textCommandFromPose, type DrawCommand } from '@weasel-js/renderer';\nimport { PAINT_SLOT } from './memo';\nimport { inkReach, resolveNodeFill, resolveNodeStroke, strokeOutsetPerAxis } from './nodePaint';\nimport { paintsPose } from './poseBox';\n\n/** The `data` a `kit:text` node carries. */\nexport interface TextNodeData {\n text: string;\n style?: TextStyle;\n runs?: readonly StyledRun[];\n fill?: Fill | null;\n stroke?: Stroke | null;\n verticalAlign?: TextVerticalAlign;\n /** Paint for the whole pose box, drawn under the glyphs. `fill` is the\n * glyphs' own paint, so a box behind them needs a key of its own. */\n background?: Fill | null;\n /** World units between the pose box and the box the text lays out in, on\n * every side. */\n padding?: number;\n /** What happens to text that runs past the pose box: `'visible'` (the\n * default) draws it there, `'clip'` cuts it at the box's edge. */\n overflow?: TextOverflow;\n}\n\n/** How a text node treats lines that run past its box. */\nexport type TextOverflow = 'visible' | 'clip';\n\ntype Box = { x: number; y: number; width: number; height: number };\n\n/** `cmd` cut to `box`: a group whose clip is the box as a rect, which the\n * renderer cuts on the CPU when the frame keeps it axis-aligned. */\nexport function clippedToBox(cmd: DrawCommand, box: Box): DrawCommand {\n return {\n kind: 'group',\n clip: { kind: 'rect', x: box.x, y: box.y, width: box.width, height: box.height },\n children: [cmd],\n };\n}\n\n/** The box a text node's lines lay out in: its pose box, inset by\n * `data.padding` and never past its center. */\nexport function textBoxOf(\n d: Pick<TextNodeData, 'padding'>,\n p: RectPose,\n): { x: number; y: number; width: number; height: number } {\n const pad = Math.max(0, d.padding ?? 0);\n const px = Math.min(pad, p.width / 2);\n const py = Math.min(pad, p.height / 2);\n return { x: p.x + px, y: p.y + py, width: p.width - 2 * px, height: p.height - 2 * py };\n}\n\n/** A `kit:text` node as the `TextPose` every text consumer reads, in the box\n * {@link textBoxOf} insets. The stroke goes through the same resolver every\n * other node kind's does, so one carrying no paint is dropped here rather\n * than throwing in the renderer. */\nexport function textPoseOf(d: TextNodeData, p: RectPose): TextPose {\n return {\n ...textBoxOf(d, p),\n text: d.text,\n runs: d.runs as StyledRun[] | undefined,\n style: d.style,\n fill: d.fill,\n stroke: resolveNodeStroke(d.stroke) ?? undefined,\n verticalAlign: d.verticalAlign,\n };\n}\n\nexport const TEXT_PAINTER: NodeShapeEntry<unknown, RectPose> = {\n id: 'kit:text',\n snapsToBox: true,\n matches: (node) => {\n if (!paintsPose(node.pose)) return false;\n const d = node.data as { text?: string } | null;\n return d?.text != null;\n },\n // Memoized because the renderer's layout cache keys on the `ResolvedRun[]` array\n // identity: a fresh runs array per frame would leave that cache missing on every\n // draw. Both resolvers are pure style merging — no font reads — so `(data, pose)` is\n // the whole key. See PAINT_SLOT.\n //\n // `y` is the top of the first line box, not a baseline — see `TextDrawCommand.y`.\n paint: (node, pose) => nodeMemo(node, PAINT_SLOT, pose, (): DrawCommand[] => {\n const d = node.data as TextNodeData;\n const laid = textCommandFromPose(textPoseOf(d, pose));\n const text = d.overflow === 'clip' ? clippedToBox(laid, pose) : laid;\n const bg = resolveNodeFill(d.background, null);\n if (!bg) return [text];\n const fill = commandFill(bg, pose);\n return [\n { kind: 'path', path: { kind: 'rect', x: pose.x, y: pose.y, width: pose.width, height: pose.height }, ...(fill ? { fill } : {}) },\n text,\n ];\n }),\n // The pose is a layout box, not a bounding box: the union of the line boxes is what\n // the node actually covers, so picking, lasso, and clipping stop grabbing blank space.\n //\n // `null` rather than an empty path when there are no non-blank lines: a caller\n // reading \"no silhouette\" falls back to the pose rect, which keeps an empty text\n // node pickable.\n //\n // A background is ink across the whole box, so a node with one is its box.\n // A clipped node's lines are cut to the box, as they are drawn.\n silhouette: (node, pose) => {\n const d = node.data as TextNodeData;\n if (resolveNodeFill(d.background, null)) {\n return { kind: 'rect', x: pose.x, y: pose.y, width: pose.width, height: pose.height };\n }\n const lines = textLineBoxes(textPoseOf(d, pose));\n const boxes = d.overflow === 'clip' ? cutRects(lines, pose) : lines;\n if (boxes.length === 0) return null;\n return rectsToPath(boxes);\n },\n // `filled` is unconditional, `data.fill: null` included: the silhouette above is line\n // boxes, not glyph ink, so reporting outline-only text as unfilled would leave it\n // grabbable within a stroke width of a box edge and nowhere near the letters.\n ink: (node, _pose, ctx) => {\n const d = node.data as { stroke?: Stroke | null };\n return { filled: true, ...inkReach(resolveNodeStroke(d.stroke), ctx?.scale) };\n },\n drawnBounds: (node, pose, ctx) => {\n const d = node.data as TextNodeData;\n const stroke = resolveNodeStroke(d.stroke);\n return stroke ? strokedLinesBox(pose, textLineBoxes(textPoseOf(d, pose)), strokeOutsetPerAxis(stroke, ctx)) : null;\n },\n};\n\n/** Each of `rects` cut to `box`, leaving out any it does not overlap. */\nfunction cutRects(rects: readonly Box[], box: Box): Box[] {\n const out: Box[] = [];\n for (const r of rects) {\n const x0 = Math.max(r.x, box.x);\n const y0 = Math.max(r.y, box.y);\n const x1 = Math.min(r.x + r.width, box.x + box.width);\n const y1 = Math.min(r.y + r.height, box.y + box.height);\n if (x1 > x0 && y1 > y0) out.push({ x: x0, y: y0, width: x1 - x0, height: y1 - y0 });\n }\n return out;\n}\n\n/**\n * The layout box grown where a stroked line reaches it: each line box, cut to\n * the layout box, then outset by the stroke's reach. A line that overflows the\n * layout box is left to it — the layout box is what a text node claims.\n */\nfunction strokedLinesBox(pose: RectPose, lines: readonly Rect[], reach: { x: number; y: number }): Rect | null {\n if (!(reach.x > 0 || reach.y > 0)) return null;\n let x0 = pose.x, y0 = pose.y, x1 = pose.x + pose.width, y1 = pose.y + pose.height;\n for (const l of lines) {\n const lx0 = Math.max(l.x, pose.x), ly0 = Math.max(l.y, pose.y);\n const lx1 = Math.min(l.x + l.width, pose.x + pose.width), ly1 = Math.min(l.y + l.height, pose.y + pose.height);\n if (lx1 <= lx0 || ly1 <= ly0) continue;\n x0 = Math.min(x0, lx0 - reach.x); y0 = Math.min(y0, ly0 - reach.y);\n x1 = Math.max(x1, lx1 + reach.x); y1 = Math.max(y1, ly1 + reach.y);\n }\n return { x: x0, y: y0, width: x1 - x0, height: y1 - y0 };\n}\n\n/**\n * Union of axis-aligned rects as one multi-contour `PolygonPath`.\n *\n * Every contour winds the same way, so `'nonzero'` reads overlapping rects as\n * covered rather than punching a hole where two lines happen to overlap —\n * which they do the moment `lineHeight` drops below 1.\n */\nexport function rectsToPath(rects: readonly { x: number; y: number; width: number; height: number }[]): PolygonPath {\n const commands = new Uint8Array(rects.length * 6);\n const coords = new Float32Array(rects.length * 10);\n let ci = 0;\n let cmd = 0;\n for (const r of rects) {\n const { x, y, width: w, height: h } = r;\n commands[cmd++] = PATH_M; coords[ci++] = x; coords[ci++] = y;\n commands[cmd++] = PATH_L; coords[ci++] = x + w; coords[ci++] = y;\n commands[cmd++] = PATH_L; coords[ci++] = x + w; coords[ci++] = y + h;\n commands[cmd++] = PATH_L; coords[ci++] = x; coords[ci++] = y + h;\n commands[cmd++] = PATH_L; coords[ci++] = x; coords[ci++] = y;\n commands[cmd++] = PATH_Z;\n }\n return { kind: 'polygon', commands, coords, fillRule: 'nonzero' };\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"textPainter.js","names":[],"sources":["../../../src/canvas/nodeShapes/textPainter.ts"],"sourcesContent":["import {\n PATH_L,\n PATH_M,\n PATH_Z,\n type PolygonPath,\n type Rect,\n} from '@weasel-js/geom';\nimport type { Stroke, Fill, Inherit } from '@weasel-js/paint';\nimport type { StyledRun, TextPose, TextStyle, TextVerticalAlign } from '@weasel-js/text';\nimport { textLineBoxes } from '@weasel-js/text';\nimport type { NodeShapeEntry } from 'canvas/NodeShape';\nimport { nodeMemo } from 'core/scene/nodeMemo';\nimport type { RectPose } from 'core/scene/types';\nimport { commandFill, textCommandFromPose, type DrawCommand } from '@weasel-js/renderer';\nimport { PAINT_SLOT } from './memo';\nimport { inkReach, strokeOutsetPerAxis } from './nodePaint';\nimport { INHERIT, resolveFill, resolveStroke } from '@weasel-js/paint';\nimport type { InheritedPaint } from 'core/scene/inheritedPaint';\nimport { paintsPose } from './poseBox';\n\n/** The `data` a `kit:text` node carries. */\nexport interface TextNodeData {\n text: string;\n style?: TextStyle;\n runs?: readonly StyledRun[];\n fill?: Fill | Inherit | null;\n stroke?: Stroke | Inherit | null;\n verticalAlign?: TextVerticalAlign;\n /** Paint for the whole pose box, drawn under the glyphs. `fill` is the\n * glyphs' own paint, so a box behind them needs a key of its own. */\n background?: Fill | null;\n /** World units between the pose box and the box the text lays out in, on\n * every side. */\n padding?: number;\n /** What happens to text that runs past the pose box: `'visible'` (the\n * default) draws it there, `'clip'` cuts it at the box's edge. */\n overflow?: TextOverflow;\n}\n\n/** How a text node treats lines that run past its box. */\nexport type TextOverflow = 'visible' | 'clip';\n\ntype Box = { x: number; y: number; width: number; height: number };\n\n/** `cmd` cut to `box`: a group whose clip is the box as a rect, which the\n * renderer cuts on the CPU when the frame keeps it axis-aligned. */\nexport function clippedToBox(cmd: DrawCommand, box: Box): DrawCommand {\n return {\n kind: 'group',\n clip: { kind: 'rect', x: box.x, y: box.y, width: box.width, height: box.height },\n children: [cmd],\n };\n}\n\n/** The box a text node's lines lay out in: its pose box, inset by\n * `data.padding` and never past its center. */\nexport function textBoxOf(\n d: Pick<TextNodeData, 'padding'>,\n p: RectPose,\n): { x: number; y: number; width: number; height: number } {\n const pad = Math.max(0, d.padding ?? 0);\n const px = Math.min(pad, p.width / 2);\n const py = Math.min(pad, p.height / 2);\n return { x: p.x + px, y: p.y + py, width: p.width - 2 * px, height: p.height - 2 * py };\n}\n\n/** A `kit:text` node as the `TextPose` every text consumer reads, in the box\n * {@link textBoxOf} insets. The stroke goes through the same resolver every\n * other node kind's does, so one carrying no paint is dropped here rather\n * than throwing in the renderer. */\nexport function textPoseOf(d: TextNodeData, p: RectPose, inherited?: InheritedPaint): TextPose {\n return {\n ...textBoxOf(d, p),\n text: d.text,\n runs: d.runs as StyledRun[] | undefined,\n style: d.style,\n fill: d.fill === INHERIT ? resolveFill(d.fill, inherited?.fill) : d.fill,\n stroke: resolveStroke(d.stroke, inherited?.stroke) ?? undefined,\n verticalAlign: d.verticalAlign,\n };\n}\n\nexport const TEXT_PAINTER: NodeShapeEntry<unknown, RectPose> = {\n id: 'kit:text',\n snapsToBox: true,\n matches: (node) => {\n if (!paintsPose(node.pose)) return false;\n const d = node.data as { text?: string } | null;\n return d?.text != null;\n },\n // Memoized because the renderer's layout cache keys on the `ResolvedRun[]` array\n // identity: a fresh runs array per frame would leave that cache missing on every\n // draw. Both resolvers are pure style merging — no font reads — so `(data, pose)` is\n // the whole key. See PAINT_SLOT.\n //\n // `y` is the top of the first line box, not a baseline — see `TextDrawCommand.y`.\n paint: (node, pose, ctx) => nodeMemo(node, PAINT_SLOT, pose, (): DrawCommand[] => {\n const d = node.data as TextNodeData;\n const laid = textCommandFromPose(textPoseOf(d, pose, ctx?.inherited));\n const text = d.overflow === 'clip' ? clippedToBox(laid, pose) : laid;\n const bg = resolveFill(d.background);\n if (!bg) return [text];\n const fill = commandFill(bg, pose);\n return [\n { kind: 'path', path: { kind: 'rect', x: pose.x, y: pose.y, width: pose.width, height: pose.height }, ...(fill ? { fill } : {}) },\n text,\n ];\n }, ctx?.inherited),\n // The pose is a layout box, not a bounding box: the union of the line boxes is what\n // the node actually covers, so picking, lasso, and clipping stop grabbing blank space.\n //\n // `null` rather than an empty path when there are no non-blank lines: a caller\n // reading \"no silhouette\" falls back to the pose rect, which keeps an empty text\n // node pickable.\n //\n // A background is ink across the whole box, so a node with one is its box.\n // A clipped node's lines are cut to the box, as they are drawn.\n silhouette: (node, pose) => {\n const d = node.data as TextNodeData;\n if (resolveFill(d.background)) {\n return { kind: 'rect', x: pose.x, y: pose.y, width: pose.width, height: pose.height };\n }\n const lines = textLineBoxes(textPoseOf(d, pose));\n const boxes = d.overflow === 'clip' ? cutRects(lines, pose) : lines;\n if (boxes.length === 0) return null;\n return rectsToPath(boxes);\n },\n // `filled` is unconditional, `data.fill: null` included: the silhouette above is line\n // boxes, not glyph ink, so reporting outline-only text as unfilled would leave it\n // grabbable within a stroke width of a box edge and nowhere near the letters.\n ink: (node, _pose, ctx) => {\n const d = node.data as TextNodeData;\n return { filled: true, ...inkReach(resolveStroke(d.stroke, ctx?.inherited?.stroke), ctx?.scale) };\n },\n drawnBounds: (node, pose, ctx) => {\n const d = node.data as TextNodeData;\n const stroke = resolveStroke(d.stroke, ctx?.inherited?.stroke);\n return stroke ? strokedLinesBox(pose, textLineBoxes(textPoseOf(d, pose, ctx?.inherited)), strokeOutsetPerAxis(stroke, ctx)) : null;\n },\n};\n\n/** Each of `rects` cut to `box`, leaving out any it does not overlap. */\nfunction cutRects(rects: readonly Box[], box: Box): Box[] {\n const out: Box[] = [];\n for (const r of rects) {\n const x0 = Math.max(r.x, box.x);\n const y0 = Math.max(r.y, box.y);\n const x1 = Math.min(r.x + r.width, box.x + box.width);\n const y1 = Math.min(r.y + r.height, box.y + box.height);\n if (x1 > x0 && y1 > y0) out.push({ x: x0, y: y0, width: x1 - x0, height: y1 - y0 });\n }\n return out;\n}\n\n/**\n * The layout box grown where a stroked line reaches it: each line box, cut to\n * the layout box, then outset by the stroke's reach. A line that overflows the\n * layout box is left to it — the layout box is what a text node claims.\n */\nfunction strokedLinesBox(pose: RectPose, lines: readonly Rect[], reach: { x: number; y: number }): Rect | null {\n if (!(reach.x > 0 || reach.y > 0)) return null;\n let x0 = pose.x, y0 = pose.y, x1 = pose.x + pose.width, y1 = pose.y + pose.height;\n for (const l of lines) {\n const lx0 = Math.max(l.x, pose.x), ly0 = Math.max(l.y, pose.y);\n const lx1 = Math.min(l.x + l.width, pose.x + pose.width), ly1 = Math.min(l.y + l.height, pose.y + pose.height);\n if (lx1 <= lx0 || ly1 <= ly0) continue;\n x0 = Math.min(x0, lx0 - reach.x); y0 = Math.min(y0, ly0 - reach.y);\n x1 = Math.max(x1, lx1 + reach.x); y1 = Math.max(y1, ly1 + reach.y);\n }\n return { x: x0, y: y0, width: x1 - x0, height: y1 - y0 };\n}\n\n/**\n * Union of axis-aligned rects as one multi-contour `PolygonPath`.\n *\n * Every contour winds the same way, so `'nonzero'` reads overlapping rects as\n * covered rather than punching a hole where two lines happen to overlap —\n * which they do the moment `lineHeight` drops below 1.\n */\nexport function rectsToPath(rects: readonly { x: number; y: number; width: number; height: number }[]): PolygonPath {\n const commands = new Uint8Array(rects.length * 6);\n const coords = new Float32Array(rects.length * 10);\n let ci = 0;\n let cmd = 0;\n for (const r of rects) {\n const { x, y, width: w, height: h } = r;\n commands[cmd++] = PATH_M; coords[ci++] = x; coords[ci++] = y;\n commands[cmd++] = PATH_L; coords[ci++] = x + w; coords[ci++] = y;\n commands[cmd++] = PATH_L; coords[ci++] = x + w; coords[ci++] = y + h;\n commands[cmd++] = PATH_L; coords[ci++] = x; coords[ci++] = y + h;\n commands[cmd++] = PATH_L; coords[ci++] = x; coords[ci++] = y;\n commands[cmd++] = PATH_Z;\n }\n return { kind: 'polygon', commands, coords, fillRule: 'nonzero' };\n}\n"],"mappings":"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"}
@@ -0,0 +1,24 @@
1
+ import { findShapeSilhouette } from "./NodeShape.js";
2
+
3
+ //#region src/canvas/ownClip.ts
4
+ /**
5
+ * What a node declaring `clip: 'outline'` clips its children to, in world
6
+ * coordinates: the silhouette of whichever painter claims it, the rect
7
+ * fallback's box included, or `null` when none does. `derived` supplies a
8
+ * derived path when there is one.
9
+ */
10
+ function clipOutline(node, pose, derived) {
11
+ return findShapeSilhouette(node, pose, { derivedPath: derived?.() ?? null });
12
+ }
13
+ /** The clip a node imposes on its children, or null when it imposes none:
14
+ * its `clipFromPose` when it has one, else its {@link clipOutline} when it
15
+ * declares `clip: 'outline'`. */
16
+ function ownClipOf(node, pose, derived) {
17
+ if (typeof node.clipFromPose === "function") return node.clipFromPose(pose);
18
+ if (node.clip !== "outline") return null;
19
+ return clipOutline(node, pose, derived);
20
+ }
21
+
22
+ //#endregion
23
+ export { clipOutline, ownClipOf };
24
+ //# sourceMappingURL=ownClip.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ownClip.js","names":[],"sources":["../../src/canvas/ownClip.ts"],"sourcesContent":["import type { Path } from '@weasel-js/geom';\nimport { findShapeSilhouette } from 'canvas/NodeShape';\nimport type { ClipFromPoseFn, Node, NodeClip } from 'core/scene/types';\n\n/** What {@link ownClipOf} reads of a node. */\nexport interface ClipBearing<TPose> {\n clipFromPose?: ClipFromPoseFn<TPose>;\n clip?: NodeClip;\n}\n\n/**\n * What a node declaring `clip: 'outline'` clips its children to, in world\n * coordinates: the silhouette of whichever painter claims it, the rect\n * fallback's box included, or `null` when none does. `derived` supplies a\n * derived path when there is one.\n */\nexport function clipOutline<TPose>(\n node: Node<unknown, string, TPose>,\n pose: TPose,\n derived?: () => Path | null | undefined,\n): Path | null {\n return findShapeSilhouette(node, pose, { derivedPath: derived?.() ?? null });\n}\n\n/** The clip a node imposes on its children, or null when it imposes none:\n * its `clipFromPose` when it has one, else its {@link clipOutline} when it\n * declares `clip: 'outline'`. */\nexport function ownClipOf<TPose>(\n node: ClipBearing<TPose>,\n pose: TPose,\n derived?: () => Path | null | undefined,\n): Path | null {\n if (typeof node.clipFromPose === 'function') return node.clipFromPose(pose);\n if (node.clip !== 'outline') return null;\n return clipOutline(node as unknown as Node<unknown, string, TPose>, pose, derived);\n}\n"],"mappings":";;;;;;;;;AAgBA,SAAgB,YACd,MACA,MACA,SACa;CACb,OAAO,oBAAoB,MAAM,MAAM,EAAE,aAAa,UAAU,KAAK,KAAK,CAAC;AAC7E;;;;AAKA,SAAgB,UACd,MACA,MACA,SACa;CACb,IAAI,OAAO,KAAK,iBAAiB,YAAY,OAAO,KAAK,aAAa,IAAI;CAC1E,IAAI,KAAK,SAAS,WAAW,OAAO;CACpC,OAAO,YAAY,MAAiD,MAAM,OAAO;AACnF"}
@@ -1 +1 @@
1
- {"version":3,"file":"paintCull.js","names":["viewToMat3","mat3"],"sources":["../../src/canvas/paintCull.ts"],"sourcesContent":["import type { View } from 'core/viewport/view';\nimport type { Bounds } from 'core/viewport/fitViewToBounds';\nimport { poseFrameOf } from '@weasel-js/geom';\nimport { boundsMissRect, type CullRect } from '@weasel-js/renderer';\nimport { mat3, viewToMat3 } from '@weasel-js/geom';\n\n/** Where a node's paint can reach, before its pose frame — the scene\n * slot's `paintBounds`. `null` means unknown, and the node is painted. */\nexport type PaintBoundsFn<TNode, TPose> = (node: TNode, pose: TPose, view: View) => Bounds | null;\n\n/**\n * A predicate true for the nodes whose paint cannot reach `rect` (screen\n * pixels) under `view`, by `paintBounds` taken through the pose frame\n * (`poseFrameOf`) `wrapNodeOutput` applies and then the view. A node `paintBounds` cannot\n * bound is never culled.\n */\nexport function paintMissesView<TNode, TPose>(\n paintBounds: PaintBoundsFn<TNode, TPose>,\n view: View,\n rect: CullRect,\n): (node: TNode, pose: TPose, layerView?: View) => boolean {\n // `layerView` is a parallax plane's own view; planes are few, so one slot\n // of memo covers a walk that meets them layer by layer.\n let last = view;\n let toScreen = viewToMat3(view);\n return (node, pose, layerView = view) => {\n if (layerView !== last) { last = layerView; toScreen = viewToMat3(layerView); }\n const b = paintBounds(node, pose, layerView);\n if (!b) return false;\n const frame = poseFrameOf(pose);\n const m = frame ? mat3.multiply(toScreen, mat3.fromAffine(frame)) : toScreen;\n return boundsMissRect(b, m, rect);\n };\n}\n"],"mappings":";;;;;;;;;;AAgBA,SAAgB,gBACd,aACA,MACA,MACyD;CAGzD,IAAI,OAAO;CACX,IAAI,WAAWA,aAAW,IAAI;CAC9B,QAAQ,MAAM,MAAM,YAAY,SAAS;EACvC,IAAI,cAAc,MAAM;GAAE,OAAO;GAAW,WAAWA,aAAW,SAAS;EAAG;EAC9E,MAAM,IAAI,YAAY,MAAM,MAAM,SAAS;EAC3C,IAAI,CAAC,GAAG,OAAO;EACf,MAAM,QAAQ,YAAY,IAAI;EAC9B,MAAM,IAAI,QAAQC,OAAK,SAAS,UAAUA,OAAK,WAAW,KAAK,CAAC,IAAI;EACpE,OAAO,eAAe,GAAG,GAAG,IAAI;CAClC;AACF"}
1
+ {"version":3,"file":"paintCull.js","names":["viewToMat3","mat3"],"sources":["../../src/canvas/paintCull.ts"],"sourcesContent":["import type { View } from 'core/viewport/view';\nimport type { Bounds } from 'core/viewport/fitViewToBounds';\nimport { poseFrameOf } from '@weasel-js/geom';\nimport { boundsMissRect, type CullRect } from '@weasel-js/renderer';\nimport { mat3, viewToMat3 } from '@weasel-js/geom';\nimport type { NodePaintCtx } from './nodeShapeTypes';\n\n/** Where a node's paint can reach, before its pose frame — the scene\n * slot's `paintBounds`. `null` means unknown, and the node is painted.\n * `ctx.inherited` is what its `'inherit'` fill and stroke take, as `drawOne` gets it. */\nexport type PaintBoundsFn<TNode, TPose> = (\n node: TNode, pose: TPose, view: View, ctx?: Pick<NodePaintCtx, 'inherited'>,\n) => Bounds | null;\n\n/**\n * A predicate true for the nodes whose paint cannot reach `rect` (screen\n * pixels) under `view`, by `paintBounds` taken through the pose frame\n * (`poseFrameOf`) `wrapNodeOutput` applies and then the view. A node `paintBounds` cannot\n * bound is never culled.\n */\nexport function paintMissesView<TNode, TPose>(\n paintBounds: PaintBoundsFn<TNode, TPose>,\n view: View,\n rect: CullRect,\n): (node: TNode, pose: TPose, layerView?: View) => boolean {\n // `layerView` is a parallax plane's own view; planes are few, so one slot\n // of memo covers a walk that meets them layer by layer.\n let last = view;\n let toScreen = viewToMat3(view);\n return (node, pose, layerView = view) => {\n if (layerView !== last) { last = layerView; toScreen = viewToMat3(layerView); }\n const b = paintBounds(node, pose, layerView);\n if (!b) return false;\n const frame = poseFrameOf(pose);\n const m = frame ? mat3.multiply(toScreen, mat3.fromAffine(frame)) : toScreen;\n return boundsMissRect(b, m, rect);\n };\n}\n"],"mappings":";;;;;;;;;;AAoBA,SAAgB,gBACd,aACA,MACA,MACyD;CAGzD,IAAI,OAAO;CACX,IAAI,WAAWA,aAAW,IAAI;CAC9B,QAAQ,MAAM,MAAM,YAAY,SAAS;EACvC,IAAI,cAAc,MAAM;GAAE,OAAO;GAAW,WAAWA,aAAW,SAAS;EAAG;EAC9E,MAAM,IAAI,YAAY,MAAM,MAAM,SAAS;EAC3C,IAAI,CAAC,GAAG,OAAO;EACf,MAAM,QAAQ,YAAY,IAAI;EAC9B,MAAM,IAAI,QAAQC,OAAK,SAAS,UAAUA,OAAK,WAAW,KAAK,CAAC,IAAI;EACpE,OAAO,eAAe,GAAG,GAAG,IAAI;CAClC;AACF"}