@lightningjs/renderer 2.12.1 → 2.13.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 (329) hide show
  1. package/COPYING +1 -0
  2. package/LICENSE +202 -202
  3. package/NOTICE +3 -3
  4. package/README.md +147 -147
  5. package/dist/src/core/CoreNode.d.ts +10 -0
  6. package/dist/src/core/CoreNode.js +90 -14
  7. package/dist/src/core/CoreNode.js.map +1 -1
  8. package/dist/src/core/CoreTextureManager.d.ts +13 -7
  9. package/dist/src/core/CoreTextureManager.js +92 -105
  10. package/dist/src/core/CoreTextureManager.js.map +1 -1
  11. package/dist/src/core/Stage.d.ts +4 -2
  12. package/dist/src/core/Stage.js +23 -7
  13. package/dist/src/core/Stage.js.map +1 -1
  14. package/dist/src/core/TextureMemoryManager.d.ts +16 -6
  15. package/dist/src/core/TextureMemoryManager.js +75 -17
  16. package/dist/src/core/TextureMemoryManager.js.map +1 -1
  17. package/dist/src/core/lib/ImageWorker.d.ts +1 -1
  18. package/dist/src/core/lib/ImageWorker.js +13 -11
  19. package/dist/src/core/lib/ImageWorker.js.map +1 -1
  20. package/dist/src/core/lib/validateImageBitmap.d.ts +6 -0
  21. package/dist/src/core/lib/validateImageBitmap.js +68 -0
  22. package/dist/src/core/lib/validateImageBitmap.js.map +1 -0
  23. package/dist/src/core/platform.js +3 -3
  24. package/dist/src/core/platform.js.map +1 -1
  25. package/dist/src/core/renderers/webgl/WebGlCoreCtxTexture.js +7 -12
  26. package/dist/src/core/renderers/webgl/WebGlCoreCtxTexture.js.map +1 -1
  27. package/dist/src/core/renderers/webgl/shaders/DefaultShader.js +45 -45
  28. package/dist/src/core/renderers/webgl/shaders/DefaultShaderBatched.js +61 -61
  29. package/dist/src/core/renderers/webgl/shaders/DynamicShader.js +93 -93
  30. package/dist/src/core/renderers/webgl/shaders/RoundedRectangle.js +63 -63
  31. package/dist/src/core/renderers/webgl/shaders/SdfShader.js +62 -62
  32. package/dist/src/core/renderers/webgl/shaders/effects/BorderBottomEffect.js +15 -15
  33. package/dist/src/core/renderers/webgl/shaders/effects/BorderEffect.js +6 -6
  34. package/dist/src/core/renderers/webgl/shaders/effects/BorderLeftEffect.js +15 -15
  35. package/dist/src/core/renderers/webgl/shaders/effects/BorderRightEffect.js +15 -15
  36. package/dist/src/core/renderers/webgl/shaders/effects/BorderTopEffect.js +15 -15
  37. package/dist/src/core/renderers/webgl/shaders/effects/FadeOutEffect.js +42 -42
  38. package/dist/src/core/renderers/webgl/shaders/effects/GlitchEffect.js +44 -44
  39. package/dist/src/core/renderers/webgl/shaders/effects/GrayscaleEffect.js +3 -3
  40. package/dist/src/core/renderers/webgl/shaders/effects/HolePunchEffect.js +22 -22
  41. package/dist/src/core/renderers/webgl/shaders/effects/LinearGradientEffect.js +28 -28
  42. package/dist/src/core/renderers/webgl/shaders/effects/RadialGradientEffect.js +10 -10
  43. package/dist/src/core/renderers/webgl/shaders/effects/RadialProgressEffect.js +37 -37
  44. package/dist/src/core/renderers/webgl/shaders/effects/RadiusEffect.js +19 -19
  45. package/dist/src/core/textures/ImageTexture.js +10 -16
  46. package/dist/src/core/textures/ImageTexture.js.map +1 -1
  47. package/dist/src/main-api/Inspector.js +16 -2
  48. package/dist/src/main-api/Inspector.js.map +1 -1
  49. package/dist/src/main-api/Renderer.d.ts +27 -1
  50. package/dist/src/main-api/Renderer.js +8 -3
  51. package/dist/src/main-api/Renderer.js.map +1 -1
  52. package/dist/tsconfig.dist.tsbuildinfo +1 -1
  53. package/exports/canvas.ts +39 -39
  54. package/exports/index.ts +89 -89
  55. package/exports/inspector.ts +24 -24
  56. package/exports/utils.ts +44 -44
  57. package/exports/webgl.ts +38 -38
  58. package/package.json +1 -2
  59. package/scripts/please-use-pnpm.js +13 -13
  60. package/src/common/CommonTypes.ts +146 -146
  61. package/src/common/EventEmitter.ts +77 -77
  62. package/src/common/IAnimationController.ts +92 -92
  63. package/src/common/IEventEmitter.ts +28 -28
  64. package/src/core/CoreNode.test.ts +202 -199
  65. package/src/core/CoreNode.ts +2455 -2341
  66. package/src/core/CoreShaderManager.ts +292 -292
  67. package/src/core/CoreTextNode.ts +455 -455
  68. package/src/core/CoreTextureManager.ts +597 -596
  69. package/src/core/Stage.ts +743 -722
  70. package/src/core/TextureMemoryManager.ts +395 -314
  71. package/src/core/animations/AnimationManager.ts +38 -38
  72. package/src/core/animations/CoreAnimation.ts +340 -340
  73. package/src/core/animations/CoreAnimationController.ts +157 -157
  74. package/src/core/lib/ContextSpy.ts +41 -41
  75. package/src/core/lib/ImageWorker.ts +279 -271
  76. package/src/core/lib/Matrix3d.ts +244 -244
  77. package/src/core/lib/RenderCoords.ts +86 -86
  78. package/src/core/lib/WebGlContextWrapper.ts +1332 -1332
  79. package/src/core/lib/textureCompression.ts +152 -152
  80. package/src/core/lib/textureSvg.ts +78 -78
  81. package/src/core/lib/utils.ts +310 -310
  82. package/src/core/lib/validateImageBitmap.ts +76 -0
  83. package/src/core/platform.ts +63 -61
  84. package/src/core/renderers/CoreContextTexture.ts +43 -43
  85. package/src/core/renderers/CoreRenderOp.ts +22 -22
  86. package/src/core/renderers/CoreRenderer.ts +115 -115
  87. package/src/core/renderers/CoreShader.ts +41 -41
  88. package/src/core/renderers/canvas/CanvasCoreRenderer.ts +375 -375
  89. package/src/core/renderers/canvas/CanvasCoreTexture.ts +153 -153
  90. package/src/core/renderers/canvas/internal/C2DShaderUtils.ts +231 -231
  91. package/src/core/renderers/canvas/internal/ColorUtils.ts +69 -69
  92. package/src/core/renderers/canvas/shaders/UnsupportedShader.ts +48 -48
  93. package/src/core/renderers/webgl/WebGlCoreCtxRenderTexture.ts +86 -86
  94. package/src/core/renderers/webgl/WebGlCoreCtxSubTexture.ts +50 -50
  95. package/src/core/renderers/webgl/WebGlCoreCtxTexture.ts +301 -303
  96. package/src/core/renderers/webgl/WebGlCoreRenderOp.ts +125 -125
  97. package/src/core/renderers/webgl/WebGlCoreRenderer.ts +815 -815
  98. package/src/core/renderers/webgl/WebGlCoreShader.ts +362 -362
  99. package/src/core/renderers/webgl/internal/BufferCollection.ts +54 -54
  100. package/src/core/renderers/webgl/internal/RendererUtils.ts +155 -155
  101. package/src/core/renderers/webgl/internal/ShaderUtils.ts +143 -143
  102. package/src/core/renderers/webgl/internal/WebGlUtils.ts +35 -35
  103. package/src/core/renderers/webgl/shaders/DefaultShader.ts +93 -93
  104. package/src/core/renderers/webgl/shaders/DefaultShaderBatched.ts +132 -132
  105. package/src/core/renderers/webgl/shaders/DynamicShader.ts +580 -580
  106. package/src/core/renderers/webgl/shaders/RoundedRectangle.ts +167 -167
  107. package/src/core/renderers/webgl/shaders/SdfShader.ts +204 -204
  108. package/src/core/renderers/webgl/shaders/effects/BorderBottomEffect.ts +101 -101
  109. package/src/core/renderers/webgl/shaders/effects/BorderEffect.ts +87 -87
  110. package/src/core/renderers/webgl/shaders/effects/BorderLeftEffect.ts +101 -101
  111. package/src/core/renderers/webgl/shaders/effects/BorderRightEffect.ts +101 -101
  112. package/src/core/renderers/webgl/shaders/effects/BorderTopEffect.ts +101 -101
  113. package/src/core/renderers/webgl/shaders/effects/EffectUtils.ts +159 -159
  114. package/src/core/renderers/webgl/shaders/effects/FadeOutEffect.ts +127 -127
  115. package/src/core/renderers/webgl/shaders/effects/GlitchEffect.ts +148 -148
  116. package/src/core/renderers/webgl/shaders/effects/GrayscaleEffect.ts +67 -67
  117. package/src/core/renderers/webgl/shaders/effects/HolePunchEffect.ts +157 -157
  118. package/src/core/renderers/webgl/shaders/effects/LinearGradientEffect.ts +171 -171
  119. package/src/core/renderers/webgl/shaders/effects/RadialGradientEffect.ts +168 -168
  120. package/src/core/renderers/webgl/shaders/effects/RadialProgressEffect.ts +187 -187
  121. package/src/core/renderers/webgl/shaders/effects/RadiusEffect.ts +110 -110
  122. package/src/core/renderers/webgl/shaders/effects/ShaderEffect.ts +196 -196
  123. package/src/core/text-rendering/TextRenderingUtils.ts +36 -36
  124. package/src/core/text-rendering/TextTextureRendererUtils.ts +263 -263
  125. package/src/core/text-rendering/TrFontManager.ts +183 -183
  126. package/src/core/text-rendering/font-face-types/SdfTrFontFace/SdfTrFontFace.ts +176 -176
  127. package/src/core/text-rendering/font-face-types/SdfTrFontFace/internal/FontShaper.ts +139 -139
  128. package/src/core/text-rendering/font-face-types/SdfTrFontFace/internal/SdfFontShaper.test.ts +173 -173
  129. package/src/core/text-rendering/font-face-types/SdfTrFontFace/internal/SdfFontShaper.ts +171 -171
  130. package/src/core/text-rendering/font-face-types/TrFontFace.ts +187 -187
  131. package/src/core/text-rendering/font-face-types/WebTrFontFace.ts +94 -94
  132. package/src/core/text-rendering/font-face-types/utils.ts +39 -39
  133. package/src/core/text-rendering/renderers/CanvasTextRenderer.ts +509 -509
  134. package/src/core/text-rendering/renderers/LightningTextTextureRenderer.ts +808 -808
  135. package/src/core/text-rendering/renderers/SdfTextRenderer/SdfTextRenderer.ts +853 -853
  136. package/src/core/text-rendering/renderers/SdfTextRenderer/internal/PeekableGenerator.test.ts +48 -48
  137. package/src/core/text-rendering/renderers/SdfTextRenderer/internal/PeekableGenerator.ts +66 -66
  138. package/src/core/text-rendering/renderers/SdfTextRenderer/internal/SpecialCodepoints.ts +52 -52
  139. package/src/core/text-rendering/renderers/SdfTextRenderer/internal/constants.ts +32 -32
  140. package/src/core/text-rendering/renderers/SdfTextRenderer/internal/getStartConditions.ts +117 -117
  141. package/src/core/text-rendering/renderers/SdfTextRenderer/internal/getUnicodeCodepoints.test.ts +133 -133
  142. package/src/core/text-rendering/renderers/SdfTextRenderer/internal/getUnicodeCodepoints.ts +38 -38
  143. package/src/core/text-rendering/renderers/SdfTextRenderer/internal/layoutText.ts +408 -408
  144. package/src/core/text-rendering/renderers/SdfTextRenderer/internal/measureText.test.ts +49 -49
  145. package/src/core/text-rendering/renderers/SdfTextRenderer/internal/measureText.ts +52 -52
  146. package/src/core/text-rendering/renderers/SdfTextRenderer/internal/setRenderWindow.test.ts +205 -205
  147. package/src/core/text-rendering/renderers/SdfTextRenderer/internal/setRenderWindow.ts +93 -93
  148. package/src/core/text-rendering/renderers/SdfTextRenderer/internal/util.ts +40 -40
  149. package/src/core/text-rendering/renderers/TextRenderer.ts +557 -557
  150. package/src/core/textures/ColorTexture.ts +102 -102
  151. package/src/core/textures/ImageTexture.ts +376 -382
  152. package/src/core/textures/NoiseTexture.ts +104 -104
  153. package/src/core/textures/RenderTexture.ts +85 -85
  154. package/src/core/textures/SubTexture.ts +205 -205
  155. package/src/core/textures/Texture.ts +337 -337
  156. package/src/core/utils.ts +227 -227
  157. package/src/env.d.ts +7 -7
  158. package/src/main-api/DynamicShaderController.ts +104 -104
  159. package/src/main-api/INode.ts +101 -101
  160. package/src/main-api/Inspector.ts +522 -505
  161. package/src/main-api/Renderer.ts +751 -720
  162. package/src/main-api/ShaderController.ts +80 -80
  163. package/src/main-api/utils.ts +45 -45
  164. package/src/utils.ts +248 -248
  165. package/dist/exports/core-api.d.ts +0 -74
  166. package/dist/exports/core-api.js +0 -96
  167. package/dist/exports/core-api.js.map +0 -1
  168. package/dist/exports/main-api.d.ts +0 -30
  169. package/dist/exports/main-api.js +0 -45
  170. package/dist/exports/main-api.js.map +0 -1
  171. package/dist/src/core/CoreExtension.d.ts +0 -12
  172. package/dist/src/core/CoreExtension.js +0 -29
  173. package/dist/src/core/CoreExtension.js.map +0 -1
  174. package/dist/src/core/CoreStuff.d.ts +0 -1
  175. package/dist/src/core/CoreStuff.js +0 -138
  176. package/dist/src/core/CoreStuff.js.map +0 -1
  177. package/dist/src/core/CoreTexturizer.d.ts +0 -14
  178. package/dist/src/core/CoreTexturizer.js +0 -47
  179. package/dist/src/core/CoreTexturizer.js.map +0 -1
  180. package/dist/src/core/LngNode.d.ts +0 -736
  181. package/dist/src/core/LngNode.js +0 -1174
  182. package/dist/src/core/LngNode.js.map +0 -1
  183. package/dist/src/core/Matrix2DContext.d.ts +0 -15
  184. package/dist/src/core/Matrix2DContext.js +0 -45
  185. package/dist/src/core/Matrix2DContext.js.map +0 -1
  186. package/dist/src/core/ShaderNode.d.ts +0 -10
  187. package/dist/src/core/ShaderNode.js +0 -30
  188. package/dist/src/core/ShaderNode.js.map +0 -1
  189. package/dist/src/core/TextNode.d.ts +0 -103
  190. package/dist/src/core/TextNode.js +0 -331
  191. package/dist/src/core/TextNode.js.map +0 -1
  192. package/dist/src/core/lib/Coords.d.ts +0 -14
  193. package/dist/src/core/lib/Coords.js +0 -55
  194. package/dist/src/core/lib/Coords.js.map +0 -1
  195. package/dist/src/core/lib/glm/common.d.ts +0 -162
  196. package/dist/src/core/lib/glm/common.js +0 -81
  197. package/dist/src/core/lib/glm/common.js.map +0 -1
  198. package/dist/src/core/lib/glm/index.d.ts +0 -11
  199. package/dist/src/core/lib/glm/index.js +0 -30
  200. package/dist/src/core/lib/glm/index.js.map +0 -1
  201. package/dist/src/core/lib/glm/mat2.d.ts +0 -219
  202. package/dist/src/core/lib/glm/mat2.js +0 -396
  203. package/dist/src/core/lib/glm/mat2.js.map +0 -1
  204. package/dist/src/core/lib/glm/mat2d.d.ts +0 -237
  205. package/dist/src/core/lib/glm/mat2d.js +0 -442
  206. package/dist/src/core/lib/glm/mat2d.js.map +0 -1
  207. package/dist/src/core/lib/glm/mat3.d.ts +0 -283
  208. package/dist/src/core/lib/glm/mat3.js +0 -680
  209. package/dist/src/core/lib/glm/mat3.js.map +0 -1
  210. package/dist/src/core/lib/glm/mat4.d.ts +0 -550
  211. package/dist/src/core/lib/glm/mat4.js +0 -1802
  212. package/dist/src/core/lib/glm/mat4.js.map +0 -1
  213. package/dist/src/core/lib/glm/quat.d.ts +0 -363
  214. package/dist/src/core/lib/glm/quat.js +0 -693
  215. package/dist/src/core/lib/glm/quat.js.map +0 -1
  216. package/dist/src/core/lib/glm/quat2.d.ts +0 -356
  217. package/dist/src/core/lib/glm/quat2.js +0 -754
  218. package/dist/src/core/lib/glm/quat2.js.map +0 -1
  219. package/dist/src/core/lib/glm/vec2.d.ts +0 -365
  220. package/dist/src/core/lib/glm/vec2.js +0 -569
  221. package/dist/src/core/lib/glm/vec2.js.map +0 -1
  222. package/dist/src/core/lib/glm/vec3.d.ts +0 -406
  223. package/dist/src/core/lib/glm/vec3.js +0 -720
  224. package/dist/src/core/lib/glm/vec3.js.map +0 -1
  225. package/dist/src/core/lib/glm/vec4.d.ts +0 -330
  226. package/dist/src/core/lib/glm/vec4.js +0 -608
  227. package/dist/src/core/lib/glm/vec4.js.map +0 -1
  228. package/dist/src/core/renderers/CoreShaderManager.d.ts +0 -19
  229. package/dist/src/core/renderers/CoreShaderManager.js +0 -33
  230. package/dist/src/core/renderers/CoreShaderManager.js.map +0 -1
  231. package/dist/src/core/renderers/webgl/WebGlCoreShaderManager.d.ts +0 -27
  232. package/dist/src/core/renderers/webgl/WebGlCoreShaderManager.js +0 -82
  233. package/dist/src/core/renderers/webgl/WebGlCoreShaderManager.js.map +0 -1
  234. package/dist/src/core/renderers/webgl/WebGlCoreShaderProgram.d.ts +0 -11
  235. package/dist/src/core/renderers/webgl/WebGlCoreShaderProgram.js +0 -34
  236. package/dist/src/core/renderers/webgl/WebGlCoreShaderProgram.js.map +0 -1
  237. package/dist/src/core/scene/Scene.d.ts +0 -59
  238. package/dist/src/core/scene/Scene.js +0 -106
  239. package/dist/src/core/scene/Scene.js.map +0 -1
  240. package/dist/src/core/text-rendering/renderers/SdfTextRenderer/internal/makeRenderWindow.d.ts +0 -20
  241. package/dist/src/core/text-rendering/renderers/SdfTextRenderer/internal/makeRenderWindow.js +0 -55
  242. package/dist/src/core/text-rendering/renderers/SdfTextRenderer/internal/makeRenderWindow.js.map +0 -1
  243. package/dist/src/main-api/ICoreDriver.d.ts +0 -27
  244. package/dist/src/main-api/ICoreDriver.js +0 -20
  245. package/dist/src/main-api/ICoreDriver.js.map +0 -1
  246. package/dist/src/main-api/IRenderDriver.d.ts +0 -20
  247. package/dist/src/main-api/IRenderDriver.js +0 -20
  248. package/dist/src/main-api/IRenderDriver.js.map +0 -1
  249. package/dist/src/main-api/IShaderController.d.ts +0 -14
  250. package/dist/src/main-api/IShaderController.js +0 -30
  251. package/dist/src/main-api/IShaderController.js.map +0 -1
  252. package/dist/src/main-api/IShaderNode.d.ts +0 -17
  253. package/dist/src/main-api/IShaderNode.js +0 -19
  254. package/dist/src/main-api/IShaderNode.js.map +0 -1
  255. package/dist/src/main-api/RendererMain.d.ts +0 -375
  256. package/dist/src/main-api/RendererMain.js +0 -365
  257. package/dist/src/main-api/RendererMain.js.map +0 -1
  258. package/dist/src/main-api/texture-usage-trackers/FinalizationRegistryTextureUsageTracker.d.ts +0 -9
  259. package/dist/src/main-api/texture-usage-trackers/FinalizationRegistryTextureUsageTracker.js +0 -38
  260. package/dist/src/main-api/texture-usage-trackers/FinalizationRegistryTextureUsageTracker.js.map +0 -1
  261. package/dist/src/main-api/texture-usage-trackers/ManualCountTextureUsageTracker.d.ts +0 -56
  262. package/dist/src/main-api/texture-usage-trackers/ManualCountTextureUsageTracker.js +0 -101
  263. package/dist/src/main-api/texture-usage-trackers/ManualCountTextureUsageTracker.js.map +0 -1
  264. package/dist/src/main-api/texture-usage-trackers/TextureUsageTracker.d.ts +0 -32
  265. package/dist/src/main-api/texture-usage-trackers/TextureUsageTracker.js +0 -28
  266. package/dist/src/main-api/texture-usage-trackers/TextureUsageTracker.js.map +0 -1
  267. package/dist/src/render-drivers/main/MainCoreDriver.d.ts +0 -24
  268. package/dist/src/render-drivers/main/MainCoreDriver.js +0 -118
  269. package/dist/src/render-drivers/main/MainCoreDriver.js.map +0 -1
  270. package/dist/src/render-drivers/main/MainOnlyNode.d.ts +0 -99
  271. package/dist/src/render-drivers/main/MainOnlyNode.js +0 -396
  272. package/dist/src/render-drivers/main/MainOnlyNode.js.map +0 -1
  273. package/dist/src/render-drivers/main/MainOnlyShaderController.d.ts +0 -6
  274. package/dist/src/render-drivers/main/MainOnlyShaderController.js +0 -15
  275. package/dist/src/render-drivers/main/MainOnlyShaderController.js.map +0 -1
  276. package/dist/src/render-drivers/main/MainOnlyShaderNode.d.ts +0 -7
  277. package/dist/src/render-drivers/main/MainOnlyShaderNode.js +0 -34
  278. package/dist/src/render-drivers/main/MainOnlyShaderNode.js.map +0 -1
  279. package/dist/src/render-drivers/main/MainOnlyTextNode.d.ts +0 -47
  280. package/dist/src/render-drivers/main/MainOnlyTextNode.js +0 -205
  281. package/dist/src/render-drivers/main/MainOnlyTextNode.js.map +0 -1
  282. package/dist/src/render-drivers/main/MainRenderDriver.d.ts +0 -17
  283. package/dist/src/render-drivers/main/MainRenderDriver.js +0 -88
  284. package/dist/src/render-drivers/main/MainRenderDriver.js.map +0 -1
  285. package/dist/src/render-drivers/threadx/NodeStruct.d.ts +0 -90
  286. package/dist/src/render-drivers/threadx/NodeStruct.js +0 -281
  287. package/dist/src/render-drivers/threadx/NodeStruct.js.map +0 -1
  288. package/dist/src/render-drivers/threadx/SharedNode.d.ts +0 -39
  289. package/dist/src/render-drivers/threadx/SharedNode.js +0 -60
  290. package/dist/src/render-drivers/threadx/SharedNode.js.map +0 -1
  291. package/dist/src/render-drivers/threadx/TextNodeStruct.d.ts +0 -44
  292. package/dist/src/render-drivers/threadx/TextNodeStruct.js +0 -201
  293. package/dist/src/render-drivers/threadx/TextNodeStruct.js.map +0 -1
  294. package/dist/src/render-drivers/threadx/ThreadXCoreDriver.d.ts +0 -28
  295. package/dist/src/render-drivers/threadx/ThreadXCoreDriver.js +0 -234
  296. package/dist/src/render-drivers/threadx/ThreadXCoreDriver.js.map +0 -1
  297. package/dist/src/render-drivers/threadx/ThreadXMainAnimationController.d.ts +0 -20
  298. package/dist/src/render-drivers/threadx/ThreadXMainAnimationController.js +0 -84
  299. package/dist/src/render-drivers/threadx/ThreadXMainAnimationController.js.map +0 -1
  300. package/dist/src/render-drivers/threadx/ThreadXMainNode.d.ts +0 -44
  301. package/dist/src/render-drivers/threadx/ThreadXMainNode.js +0 -154
  302. package/dist/src/render-drivers/threadx/ThreadXMainNode.js.map +0 -1
  303. package/dist/src/render-drivers/threadx/ThreadXMainShaderController.d.ts +0 -6
  304. package/dist/src/render-drivers/threadx/ThreadXMainShaderController.js +0 -16
  305. package/dist/src/render-drivers/threadx/ThreadXMainShaderController.js.map +0 -1
  306. package/dist/src/render-drivers/threadx/ThreadXMainShaderNode.d.ts +0 -7
  307. package/dist/src/render-drivers/threadx/ThreadXMainShaderNode.js +0 -15
  308. package/dist/src/render-drivers/threadx/ThreadXMainShaderNode.js.map +0 -1
  309. package/dist/src/render-drivers/threadx/ThreadXMainTextNode.d.ts +0 -28
  310. package/dist/src/render-drivers/threadx/ThreadXMainTextNode.js +0 -55
  311. package/dist/src/render-drivers/threadx/ThreadXMainTextNode.js.map +0 -1
  312. package/dist/src/render-drivers/threadx/ThreadXRenderDriver.d.ts +0 -21
  313. package/dist/src/render-drivers/threadx/ThreadXRenderDriver.js +0 -198
  314. package/dist/src/render-drivers/threadx/ThreadXRenderDriver.js.map +0 -1
  315. package/dist/src/render-drivers/threadx/ThreadXRendererMessage.d.ts +0 -70
  316. package/dist/src/render-drivers/threadx/ThreadXRendererMessage.js +0 -32
  317. package/dist/src/render-drivers/threadx/ThreadXRendererMessage.js.map +0 -1
  318. package/dist/src/render-drivers/threadx/worker/ThreadXRendererNode.d.ts +0 -19
  319. package/dist/src/render-drivers/threadx/worker/ThreadXRendererNode.js +0 -177
  320. package/dist/src/render-drivers/threadx/worker/ThreadXRendererNode.js.map +0 -1
  321. package/dist/src/render-drivers/threadx/worker/ThreadXRendererTextNode.d.ts +0 -27
  322. package/dist/src/render-drivers/threadx/worker/ThreadXRendererTextNode.js +0 -108
  323. package/dist/src/render-drivers/threadx/worker/ThreadXRendererTextNode.js.map +0 -1
  324. package/dist/src/render-drivers/threadx/worker/renderer.d.ts +0 -1
  325. package/dist/src/render-drivers/threadx/worker/renderer.js +0 -145
  326. package/dist/src/render-drivers/threadx/worker/renderer.js.map +0 -1
  327. package/dist/src/render-drivers/utils.d.ts +0 -12
  328. package/dist/src/render-drivers/utils.js +0 -69
  329. package/dist/src/render-drivers/utils.js.map +0 -1
@@ -1,2341 +1,2455 @@
1
- /*
2
- * If not stated otherwise in this file or this component's LICENSE file the
3
- * following copyright and licenses apply:
4
- *
5
- * Copyright 2023 Comcast Cable Communications Management, LLC.
6
- *
7
- * Licensed under the Apache License, Version 2.0 (the License);
8
- * you may not use this file except in compliance with the License.
9
- * You may obtain a copy of the License at
10
- *
11
- * http://www.apache.org/licenses/LICENSE-2.0
12
- *
13
- * Unless required by applicable law or agreed to in writing, software
14
- * distributed under the License is distributed on an "AS IS" BASIS,
15
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16
- * See the License for the specific language governing permissions and
17
- * limitations under the License.
18
- */
19
-
20
- import {
21
- assertTruthy,
22
- getNewId,
23
- isProductionEnvironment,
24
- mergeColorAlphaPremultiplied,
25
- } from '../utils.js';
26
- import type { TextureOptions } from './CoreTextureManager.js';
27
- import type { CoreRenderer } from './renderers/CoreRenderer.js';
28
- import type { Stage } from './Stage.js';
29
- import {
30
- type Texture,
31
- type TextureFailedEventHandler,
32
- type TextureFreedEventHandler,
33
- type TextureLoadedEventHandler,
34
- } from './textures/Texture.js';
35
- import type {
36
- Dimensions,
37
- NodeTextureFailedPayload,
38
- NodeTextureFreedPayload,
39
- NodeTextureLoadedPayload,
40
- } from '../common/CommonTypes.js';
41
- import { EventEmitter } from '../common/EventEmitter.js';
42
- import {
43
- copyRect,
44
- intersectRect,
45
- type Bound,
46
- type RectWithValid,
47
- createBound,
48
- boundInsideBound,
49
- boundLargeThanBound,
50
- createPreloadBounds,
51
- } from './lib/utils.js';
52
- import { Matrix3d } from './lib/Matrix3d.js';
53
- import { RenderCoords } from './lib/RenderCoords.js';
54
- import type { AnimationSettings } from './animations/CoreAnimation.js';
55
- import type { IAnimationController } from '../common/IAnimationController.js';
56
- import { CoreAnimation } from './animations/CoreAnimation.js';
57
- import { CoreAnimationController } from './animations/CoreAnimationController.js';
58
- import type { BaseShaderController } from '../main-api/ShaderController.js';
59
-
60
- export enum CoreNodeRenderState {
61
- Init = 0,
62
- OutOfBounds = 2,
63
- InBounds = 4,
64
- InViewport = 8,
65
- }
66
-
67
- const CoreNodeRenderStateMap: Map<CoreNodeRenderState, string> = new Map();
68
- CoreNodeRenderStateMap.set(CoreNodeRenderState.Init, 'init');
69
- CoreNodeRenderStateMap.set(CoreNodeRenderState.OutOfBounds, 'outOfBounds');
70
- CoreNodeRenderStateMap.set(CoreNodeRenderState.InBounds, 'inBounds');
71
- CoreNodeRenderStateMap.set(CoreNodeRenderState.InViewport, 'inViewport');
72
-
73
- export enum UpdateType {
74
- /**
75
- * Child updates
76
- */
77
- Children = 1,
78
-
79
- /**
80
- * Scale/Rotate transform update
81
- *
82
- * @remarks
83
- * CoreNode Properties Updated:
84
- * - `scaleRotateTransform`
85
- */
86
- ScaleRotate = 2,
87
-
88
- /**
89
- * Translate transform update (x/y/width/height/pivot/mount)
90
- *
91
- * @remarks
92
- * CoreNode Properties Updated:
93
- * - `localTransform`
94
- */
95
- Local = 4,
96
-
97
- /**
98
- * Global Transform update
99
- *
100
- * @remarks
101
- * CoreNode Properties Updated:
102
- * - `globalTransform`
103
- * - `renderCoords`
104
- * - `renderBound`
105
- */
106
- Global = 8,
107
-
108
- /**
109
- * Clipping rect update
110
- *
111
- * @remarks
112
- * CoreNode Properties Updated:
113
- * - `clippingRect`
114
- */
115
- Clipping = 16,
116
-
117
- /**
118
- * Calculated ZIndex update
119
- *
120
- * @remarks
121
- * CoreNode Properties Updated:
122
- * - `calcZIndex`
123
- */
124
- CalculatedZIndex = 32,
125
-
126
- /**
127
- * Z-Index Sorted Children update
128
- *
129
- * @remarks
130
- * CoreNode Properties Updated:
131
- * - `children` (sorts children by their `calcZIndex`)
132
- */
133
- ZIndexSortedChildren = 64,
134
-
135
- /**
136
- * Premultiplied Colors update
137
- *
138
- * @remarks
139
- * CoreNode Properties Updated:
140
- * - `premultipliedColorTl`
141
- * - `premultipliedColorTr`
142
- * - `premultipliedColorBl`
143
- * - `premultipliedColorBr`
144
- */
145
- PremultipliedColors = 128,
146
-
147
- /**
148
- * World Alpha update
149
- *
150
- * @remarks
151
- * CoreNode Properties Updated:
152
- * - `worldAlpha` = `parent.worldAlpha` * `alpha`
153
- */
154
- WorldAlpha = 256,
155
-
156
- /**
157
- * Render State update
158
- *
159
- * @remarks
160
- * CoreNode Properties Updated:
161
- * - `renderState`
162
- */
163
- RenderState = 512,
164
-
165
- /**
166
- * Is Renderable update
167
- *
168
- * @remarks
169
- * CoreNode Properties Updated:
170
- * - `isRenderable`
171
- */
172
- IsRenderable = 1024,
173
-
174
- /**
175
- * Render Texture update
176
- */
177
- RenderTexture = 2048,
178
-
179
- /**
180
- * Track if parent has render texture
181
- */
182
- ParentRenderTexture = 4096,
183
-
184
- /**
185
- * Render Bounds update
186
- */
187
- RenderBounds = 8192,
188
-
189
- /**
190
- * None
191
- */
192
- None = 0,
193
-
194
- /**
195
- * All
196
- */
197
- All = 14335,
198
- }
199
-
200
- /**
201
- * A custom data map which can be stored on an CoreNode
202
- *
203
- * @remarks
204
- * This is a map of key-value pairs that can be stored on an INode. It is used
205
- * to store custom data that can be used by the application.
206
- * The data stored can only be of type string, number or boolean.
207
- */
208
- export type CustomDataMap = {
209
- [key: string]: string | number | boolean | undefined;
210
- };
211
-
212
- /**
213
- * Writable properties of a Node.
214
- */
215
- export interface CoreNodeProps {
216
- /**
217
- * The x coordinate of the Node's Mount Point.
218
- *
219
- * @remarks
220
- * See {@link mountX} and {@link mountY} for more information about setting
221
- * the Mount Point.
222
- *
223
- * @default `0`
224
- */
225
- x: number;
226
- /**
227
- * The y coordinate of the Node's Mount Point.
228
- *
229
- * @remarks
230
- * See {@link mountX} and {@link mountY} for more information about setting
231
- * the Mount Point.
232
- *
233
- * @default `0`
234
- */
235
- y: number;
236
- /**
237
- * The width of the Node.
238
- *
239
- * @default `0`
240
- */
241
- width: number;
242
- /**
243
- * The height of the Node.
244
- *
245
- * @default `0`
246
- */
247
- height: number;
248
- /**
249
- * The alpha opacity of the Node.
250
- *
251
- * @remarks
252
- * The alpha value is a number between 0 and 1, where 0 is fully transparent
253
- * and 1 is fully opaque.
254
- *
255
- * @default `1`
256
- */
257
- alpha: number;
258
- /**
259
- * Autosize mode
260
- *
261
- * @remarks
262
- * When enabled, when a texture is loaded into the Node, the Node will
263
- * automatically resize to the dimensions of the texture.
264
- *
265
- * Text Nodes are always autosized based on their text content regardless
266
- * of this mode setting.
267
- *
268
- * @default `false`
269
- */
270
- autosize: boolean;
271
- /**
272
- * Clipping Mode
273
- *
274
- * @remarks
275
- * Enable Clipping Mode when you want to prevent the drawing of a Node and
276
- * its descendants from overflowing outside of the Node's x/y/width/height
277
- * bounds.
278
- *
279
- * For WebGL, clipping is implemented using the high-performance WebGL
280
- * operation scissor. As a consequence, clipping does not work for
281
- * non-rectangular areas. So, if the element is rotated
282
- * (by itself or by any of its ancestors), clipping will not work as intended.
283
- *
284
- * TODO: Add support for non-rectangular clipping either automatically or
285
- * via Render-To-Texture.
286
- *
287
- * @default `false`
288
- */
289
- clipping: boolean;
290
- /**
291
- * The color of the Node.
292
- *
293
- * @remarks
294
- * The color value is a number in the format 0xRRGGBBAA, where RR is the red
295
- * component, GG is the green component, BB is the blue component, and AA is
296
- * the alpha component.
297
- *
298
- * Gradient colors may be set by setting the different color sub-properties:
299
- * {@link colorTop}, {@link colorBottom}, {@link colorLeft}, {@link colorRight},
300
- * {@link colorTl}, {@link colorTr}, {@link colorBr}, {@link colorBl} accordingly.
301
- *
302
- * @default `0xffffffff` (opaque white)
303
- */
304
- color: number;
305
- /**
306
- * The color of the top edge of the Node for gradient rendering.
307
- *
308
- * @remarks
309
- * See {@link color} for more information about color values and gradient
310
- * rendering.
311
- */
312
- colorTop: number;
313
- /**
314
- * The color of the bottom edge of the Node for gradient rendering.
315
- *
316
- * @remarks
317
- * See {@link color} for more information about color values and gradient
318
- * rendering.
319
- */
320
- colorBottom: number;
321
- /**
322
- * The color of the left edge of the Node for gradient rendering.
323
- *
324
- * @remarks
325
- * See {@link color} for more information about color values and gradient
326
- * rendering.
327
- */
328
- colorLeft: number;
329
- /**
330
- * The color of the right edge of the Node for gradient rendering.
331
- *
332
- * @remarks
333
- * See {@link color} for more information about color values and gradient
334
- * rendering.
335
- */
336
- colorRight: number;
337
- /**
338
- * The color of the top-left corner of the Node for gradient rendering.
339
- *
340
- * @remarks
341
- * See {@link color} for more information about color values and gradient
342
- * rendering.
343
- */
344
- colorTl: number;
345
- /**
346
- * The color of the top-right corner of the Node for gradient rendering.
347
- *
348
- * @remarks
349
- * See {@link color} for more information about color values and gradient
350
- * rendering.
351
- */
352
- colorTr: number;
353
- /**
354
- * The color of the bottom-right corner of the Node for gradient rendering.
355
- *
356
- * @remarks
357
- * See {@link color} for more information about color values and gradient
358
- * rendering.
359
- */
360
- colorBr: number;
361
- /**
362
- * The color of the bottom-left corner of the Node for gradient rendering.
363
- *
364
- * @remarks
365
- * See {@link color} for more information about color values and gradient
366
- * rendering.
367
- */
368
- colorBl: number;
369
- /**
370
- * The Node's parent Node.
371
- *
372
- * @remarks
373
- * The value `null` indicates that the Node has no parent. This may either be
374
- * because the Node is the root Node of the scene graph, or because the Node
375
- * has been removed from the scene graph.
376
- *
377
- * In order to make sure that a Node can be rendered on the screen, it must
378
- * be added to the scene graph by setting it's parent property to a Node that
379
- * is already in the scene graph such as the root Node.
380
- *
381
- * @default `null`
382
- */
383
- parent: CoreNode | null;
384
- /**
385
- * The Node's z-index.
386
- *
387
- * @remarks
388
- * TBD
389
- */
390
- zIndex: number;
391
- /**
392
- * The Node's Texture.
393
- *
394
- * @remarks
395
- * The `texture` defines a rasterized image that is contained within the
396
- * {@link width} and {@link height} dimensions of the Node. If null, the
397
- * Node will use an opaque white {@link ColorTexture} when being drawn, which
398
- * essentially enables colors (including gradients) to be drawn.
399
- *
400
- * If set, by default, the texture will be drawn, as is, stretched to the
401
- * dimensions of the Node. This behavior can be modified by setting the TBD
402
- * and TBD properties.
403
- *
404
- * To create a Texture in order to set it on this property, call
405
- * {@link RendererMain.createTexture}.
406
- *
407
- * If the {@link src} is set on a Node, the Node will use the
408
- * {@link ImageTexture} by default and the Node will simply load the image at
409
- * the specified URL.
410
- *
411
- * Note: If this is a Text Node, the Texture will be managed by the Node's
412
- * {@link TextRenderer} and should not be set explicitly.
413
- */
414
- texture: Texture | null;
415
-
416
- /**
417
- * [Deprecated]: Prevents the texture from being cleaned up when the Node is removed
418
- *
419
- * @remarks
420
- * Please use the `preventCleanup` property on {@link TextureOptions} instead.
421
- *
422
- * @default false
423
- */
424
- preventCleanup: boolean;
425
- /**
426
- * Options to associate with the Node's Texture
427
- */
428
- textureOptions: TextureOptions;
429
-
430
- /**
431
- * The Node's shader
432
- *
433
- * @remarks
434
- * The `shader` defines a {@link Shader} used to draw the Node. By default,
435
- * the Default Shader is used which simply draws the defined {@link texture}
436
- * or {@link color}(s) within the Node without any special effects.
437
- *
438
- * To create a Shader in order to set it on this property, call
439
- * {@link RendererMain.createShader}.
440
- *
441
- * Note: If this is a Text Node, the Shader will be managed by the Node's
442
- * {@link TextRenderer} and should not be set explicitly.
443
- */
444
- shader: BaseShaderController;
445
- /**
446
- * Image URL
447
- *
448
- * @remarks
449
- * When set, the Node's {@link texture} is automatically set to an
450
- * {@link ImageTexture} using the source image URL provided (with all other
451
- * settings being defaults)
452
- */
453
- src: string | null;
454
- zIndexLocked: number;
455
- /**
456
- * Scale to render the Node at
457
- *
458
- * @remarks
459
- * The scale value multiplies the provided {@link width} and {@link height}
460
- * of the Node around the Node's Pivot Point (defined by the {@link pivot}
461
- * props).
462
- *
463
- * Behind the scenes, setting this property sets both the {@link scaleX} and
464
- * {@link scaleY} props to the same value.
465
- *
466
- * NOTE: When the scaleX and scaleY props are explicitly set to different values,
467
- * this property returns `null`. Setting `null` on this property will have no
468
- * effect.
469
- *
470
- * @default 1.0
471
- */
472
- scale: number | null;
473
- /**
474
- * Scale to render the Node at (X-Axis)
475
- *
476
- * @remarks
477
- * The scaleX value multiplies the provided {@link width} of the Node around
478
- * the Node's Pivot Point (defined by the {@link pivot} props).
479
- *
480
- * @default 1.0
481
- */
482
- scaleX: number;
483
- /**
484
- * Scale to render the Node at (Y-Axis)
485
- *
486
- * @remarks
487
- * The scaleY value multiplies the provided {@link height} of the Node around
488
- * the Node's Pivot Point (defined by the {@link pivot} props).
489
- *
490
- * @default 1.0
491
- */
492
- scaleY: number;
493
- /**
494
- * Combined position of the Node's Mount Point
495
- *
496
- * @remarks
497
- * The value can be any number between `0.0` and `1.0`:
498
- * - `0.0` defines the Mount Point at the top-left corner of the Node.
499
- * - `0.5` defines it at the center of the Node.
500
- * - `1.0` defines it at the bottom-right corner of the node.
501
- *
502
- * Use the {@link mountX} and {@link mountY} props seperately for more control
503
- * of the Mount Point.
504
- *
505
- * When assigned, the same value is also passed to both the {@link mountX} and
506
- * {@link mountY} props.
507
- *
508
- * @default 0 (top-left)
509
- */
510
- mount: number;
511
- /**
512
- * X position of the Node's Mount Point
513
- *
514
- * @remarks
515
- * The value can be any number between `0.0` and `1.0`:
516
- * - `0.0` defines the Mount Point's X position as the left-most edge of the
517
- * Node
518
- * - `0.5` defines it as the horizontal center of the Node
519
- * - `1.0` defines it as the right-most edge of the Node.
520
- *
521
- * The combination of {@link mountX} and {@link mountY} define the Mount Point
522
- *
523
- * @default 0 (left-most edge)
524
- */
525
- mountX: number;
526
- /**
527
- * Y position of the Node's Mount Point
528
- *
529
- * @remarks
530
- * The value can be any number between `0.0` and `1.0`:
531
- * - `0.0` defines the Mount Point's Y position as the top-most edge of the
532
- * Node
533
- * - `0.5` defines it as the vertical center of the Node
534
- * - `1.0` defines it as the bottom-most edge of the Node.
535
- *
536
- * The combination of {@link mountX} and {@link mountY} define the Mount Point
537
- *
538
- * @default 0 (top-most edge)
539
- */
540
- mountY: number;
541
- /**
542
- * Combined position of the Node's Pivot Point
543
- *
544
- * @remarks
545
- * The value can be any number between `0.0` and `1.0`:
546
- * - `0.0` defines the Pivot Point at the top-left corner of the Node.
547
- * - `0.5` defines it at the center of the Node.
548
- * - `1.0` defines it at the bottom-right corner of the node.
549
- *
550
- * Use the {@link pivotX} and {@link pivotY} props seperately for more control
551
- * of the Pivot Point.
552
- *
553
- * When assigned, the same value is also passed to both the {@link pivotX} and
554
- * {@link pivotY} props.
555
- *
556
- * @default 0.5 (center)
557
- */
558
- pivot: number;
559
- /**
560
- * X position of the Node's Pivot Point
561
- *
562
- * @remarks
563
- * The value can be any number between `0.0` and `1.0`:
564
- * - `0.0` defines the Pivot Point's X position as the left-most edge of the
565
- * Node
566
- * - `0.5` defines it as the horizontal center of the Node
567
- * - `1.0` defines it as the right-most edge of the Node.
568
- *
569
- * The combination of {@link pivotX} and {@link pivotY} define the Pivot Point
570
- *
571
- * @default 0.5 (centered on x-axis)
572
- */
573
- pivotX: number;
574
- /**
575
- * Y position of the Node's Pivot Point
576
- *
577
- * @remarks
578
- * The value can be any number between `0.0` and `1.0`:
579
- * - `0.0` defines the Pivot Point's Y position as the top-most edge of the
580
- * Node
581
- * - `0.5` defines it as the vertical center of the Node
582
- * - `1.0` defines it as the bottom-most edge of the Node.
583
- *
584
- * The combination of {@link pivotX} and {@link pivotY} define the Pivot Point
585
- *
586
- * @default 0.5 (centered on y-axis)
587
- */
588
- pivotY: number;
589
- /**
590
- * Rotation of the Node (in Radians)
591
- *
592
- * @remarks
593
- * Sets the amount to rotate the Node by around it's Pivot Point (defined by
594
- * the {@link pivot} props). Positive values rotate the Node clockwise, while
595
- * negative values rotate it counter-clockwise.
596
- *
597
- * Example values:
598
- * - `-Math.PI / 2`: 90 degree rotation counter-clockwise
599
- * - `0`: No rotation
600
- * - `Math.PI / 2`: 90 degree rotation clockwise
601
- * - `Math.PI`: 180 degree rotation clockwise
602
- * - `3 * Math.PI / 2`: 270 degree rotation clockwise
603
- * - `2 * Math.PI`: 360 rotation clockwise
604
- */
605
- rotation: number;
606
-
607
- /**
608
- * Whether the Node is rendered to a texture
609
- *
610
- * @remarks
611
- * TBD
612
- *
613
- * @default false
614
- */
615
- rtt: boolean;
616
-
617
- /**
618
- * Node data element for custom data storage (optional)
619
- *
620
- * @remarks
621
- * This property is used to store custom data on the Node as a key/value data store.
622
- * Data values are limited to string, numbers, booleans. Strings will be truncated
623
- * to a 2048 character limit for performance reasons.
624
- *
625
- * This is not a data storage mechanism for large amounts of data please use a
626
- * dedicated data storage mechanism for that.
627
- *
628
- * The custom data will be reflected in the inspector as part of `data-*` attributes
629
- *
630
- * @default `undefined`
631
- */
632
- data?: CustomDataMap;
633
-
634
- /**
635
- * Image Type to explicitly set the image type that is being loaded
636
- *
637
- * @remarks
638
- * This property must be used with a `src` that points at an image. In some cases
639
- * the extension doesn't provide a reliable representation of the image type. In such
640
- * cases set the ImageType explicitly.
641
- *
642
- * `regular` is used for normal images such as png, jpg, etc
643
- * `compressed` is used for ETC1/ETC2 compressed images with a PVR or KTX container
644
- * `svg` is used for scalable vector graphics
645
- *
646
- * @default `undefined`
647
- */
648
- imageType?: 'regular' | 'compressed' | 'svg' | null;
649
-
650
- /**
651
- * She width of the rectangle from which the Image Texture will be extracted.
652
- * This value can be negative. If not provided, the image's source natural
653
- * width will be used.
654
- */
655
- srcWidth?: number;
656
- /**
657
- * The height of the rectangle from which the Image Texture will be extracted.
658
- * This value can be negative. If not provided, the image's source natural
659
- * height will be used.
660
- */
661
- srcHeight?: number;
662
- /**
663
- * The x coordinate of the reference point of the rectangle from which the Texture
664
- * will be extracted. `width` and `height` are provided. And only works when
665
- * createImageBitmap is available. Only works when createImageBitmap is supported on the browser.
666
- */
667
- srcX?: number;
668
- /**
669
- * The y coordinate of the reference point of the rectangle from which the Texture
670
- * will be extracted. Only used when source `srcWidth` width and `srcHeight` height
671
- * are provided. Only works when createImageBitmap is supported on the browser.
672
- */
673
- srcY?: number;
674
- /**
675
- * By enabling Strict bounds the renderer will not process & render child nodes of a node that is out of the visible area
676
- *
677
- * @remarks
678
- * When enabled out of bound nodes, i.e. nodes that are out of the visible area, will
679
- * **NOT** have their children processed and renderer anymore. This means the children of a out of bound
680
- * node will not receive update processing such as positioning updates and will not be drawn on screen.
681
- * As such the rest of the branch of the update tree that sits below this node will not be processed anymore
682
- *
683
- * This is a big performance gain but may be disabled in cases where the width of the parent node is
684
- * unknown and the render must process the child nodes regardless of the viewport status of the parent node
685
- *
686
- * @default false
687
- */
688
- strictBounds: boolean;
689
- }
690
-
691
- /**
692
- * Grab all the number properties of type T
693
- */
694
- type NumberProps<T> = {
695
- [Key in keyof T as NonNullable<T[Key]> extends number ? Key : never]: number;
696
- };
697
-
698
- /**
699
- * Properties of a Node used by the animate() function
700
- */
701
- export interface CoreNodeAnimateProps extends NumberProps<CoreNodeProps> {
702
- /**
703
- * Shader properties to animate
704
- */
705
- shaderProps: Record<string, number>;
706
- // TODO: textureProps: Record<string, number>;
707
- }
708
-
709
- /**
710
- * A visual Node in the Renderer scene graph.
711
- *
712
- * @remarks
713
- * CoreNode is an internally used class that represents a Renderer Node in the
714
- * scene graph. See INode.ts for the public APIs exposed to Renderer users
715
- * that include generic types for Shaders.
716
- */
717
- export class CoreNode extends EventEmitter {
718
- readonly children: CoreNode[] = [];
719
- protected _id: number = getNewId();
720
- readonly props: CoreNodeProps;
721
-
722
- public updateType = UpdateType.All;
723
- public childUpdateType = UpdateType.None;
724
-
725
- public globalTransform?: Matrix3d;
726
- public scaleRotateTransform?: Matrix3d;
727
- public localTransform?: Matrix3d;
728
- public renderCoords?: RenderCoords;
729
- public renderBound?: Bound;
730
- public strictBound?: Bound;
731
- public preloadBound?: Bound;
732
- public clippingRect: RectWithValid = {
733
- x: 0,
734
- y: 0,
735
- width: 0,
736
- height: 0,
737
- valid: false,
738
- };
739
- public isRenderable = false;
740
- public renderState: CoreNodeRenderState = CoreNodeRenderState.Init;
741
-
742
- public worldAlpha = 1;
743
- public premultipliedColorTl = 0;
744
- public premultipliedColorTr = 0;
745
- public premultipliedColorBl = 0;
746
- public premultipliedColorBr = 0;
747
- public calcZIndex = 0;
748
- public hasRTTupdates = false;
749
- public parentHasRenderTexture = false;
750
- public rttParent: CoreNode | null = null;
751
-
752
- constructor(readonly stage: Stage, props: CoreNodeProps) {
753
- super();
754
-
755
- this.props = {
756
- ...props,
757
- parent: null,
758
- texture: null,
759
- src: null,
760
- rtt: false,
761
- };
762
-
763
- // Assign props to instance
764
- this.parent = props.parent;
765
- this.texture = props.texture;
766
- this.src = props.src;
767
- this.rtt = props.rtt;
768
-
769
- this.setUpdateType(
770
- UpdateType.ScaleRotate |
771
- UpdateType.Local |
772
- UpdateType.RenderBounds |
773
- UpdateType.RenderState,
774
- );
775
-
776
- if (isProductionEnvironment() === false && props.preventCleanup === true) {
777
- console.warn(
778
- 'CoreNode.preventCleanup: Is deprecated and will be removed in upcoming release, please use textureOptions.preventCleanup instead',
779
- );
780
- }
781
-
782
- // if the default texture isn't loaded yet, wait for it to load
783
- // this only happens when the node is created before the stage is ready
784
- if (
785
- this.stage.defaultTexture &&
786
- this.stage.defaultTexture.state !== 'loaded'
787
- ) {
788
- this.stage.defaultTexture.once('loaded', () => {
789
- this.setUpdateType(UpdateType.IsRenderable);
790
- });
791
- }
792
- }
793
-
794
- //#region Textures
795
- loadTexture(): void {
796
- const { texture } = this.props;
797
- assertTruthy(texture);
798
-
799
- // If texture is already loaded / failed, trigger loaded event manually
800
- // so that users get a consistent event experience.
801
- // We do this in a microtask to allow listeners to be attached in the same
802
- // synchronous task after calling loadTexture()
803
- queueMicrotask(() => {
804
- if (this.textureOptions.preload === true) {
805
- this.stage.txManager.loadTexture(texture);
806
- }
807
-
808
- texture.preventCleanup =
809
- this.props.textureOptions?.preventCleanup ?? false;
810
- texture.on('loaded', this.onTextureLoaded);
811
- texture.on('failed', this.onTextureFailed);
812
- texture.on('freed', this.onTextureFreed);
813
-
814
- // If the parent is a render texture, the initial texture status
815
- // will be set to freed until the texture is processed by the
816
- // Render RTT nodes. So we only need to listen fo changes and
817
- // no need to check the texture.state until we restructure how
818
- // textures are being processed.
819
- if (this.parentHasRenderTexture) {
820
- this.notifyParentRTTOfUpdate();
821
- return;
822
- }
823
-
824
- if (texture.state === 'loaded') {
825
- assertTruthy(texture.dimensions);
826
- this.onTextureLoaded(texture, texture.dimensions);
827
- } else if (texture.state === 'failed') {
828
- assertTruthy(texture.error);
829
- this.onTextureFailed(texture, texture.error);
830
- } else if (texture.state === 'freed') {
831
- this.onTextureFreed(texture);
832
- }
833
- });
834
- }
835
-
836
- unloadTexture(): void {
837
- if (this.texture !== null) {
838
- this.texture.off('loaded', this.onTextureLoaded);
839
- this.texture.off('failed', this.onTextureFailed);
840
- this.texture.off('freed', this.onTextureFreed);
841
- this.texture.setRenderableOwner(this, false);
842
- }
843
- }
844
-
845
- autosizeNode(dimensions: Dimensions) {
846
- if (this.autosize) {
847
- this.width = dimensions.width;
848
- this.height = dimensions.height;
849
- }
850
- }
851
-
852
- private onTextureLoaded: TextureLoadedEventHandler = (_, dimensions) => {
853
- this.autosizeNode(dimensions);
854
- this.setUpdateType(UpdateType.IsRenderable);
855
-
856
- // Texture was loaded. In case the RAF loop has already stopped, we request
857
- // a render to ensure the texture is rendered.
858
- this.stage.requestRender();
859
-
860
- // If parent has a render texture, flag that we need to update
861
- if (this.parentHasRenderTexture) {
862
- this.notifyParentRTTOfUpdate();
863
- }
864
-
865
- // ignore 1x1 pixel textures
866
- if (dimensions.width > 1 && dimensions.height > 1) {
867
- this.emit('loaded', {
868
- type: 'texture',
869
- dimensions,
870
- } satisfies NodeTextureLoadedPayload);
871
- }
872
-
873
- // Trigger a local update if the texture is loaded and the resizeMode is 'contain'
874
- if (this.props.textureOptions?.resizeMode?.type === 'contain') {
875
- this.setUpdateType(UpdateType.Local);
876
- }
877
- };
878
-
879
- private onTextureFailed: TextureFailedEventHandler = (_, error) => {
880
- this.setUpdateType(UpdateType.IsRenderable);
881
-
882
- // If parent has a render texture, flag that we need to update
883
- if (this.parentHasRenderTexture) {
884
- this.notifyParentRTTOfUpdate();
885
- }
886
-
887
- this.emit('failed', {
888
- type: 'texture',
889
- error,
890
- } satisfies NodeTextureFailedPayload);
891
- };
892
-
893
- private onTextureFreed: TextureFreedEventHandler = () => {
894
- this.setUpdateType(UpdateType.IsRenderable);
895
-
896
- // If parent has a render texture, flag that we need to update
897
- if (this.parentHasRenderTexture) {
898
- this.notifyParentRTTOfUpdate();
899
- }
900
-
901
- this.emit('freed', {
902
- type: 'texture',
903
- } satisfies NodeTextureFreedPayload);
904
- };
905
- //#endregion Textures
906
-
907
- /**
908
- * Change types types is used to determine the scope of the changes being applied
909
- *
910
- * @remarks
911
- * See {@link UpdateType} for more information on each type
912
- *
913
- * @param type
914
- */
915
- setUpdateType(type: UpdateType): void {
916
- this.updateType |= type;
917
-
918
- const parent = this.props.parent;
919
- if (!parent) return;
920
-
921
- if ((parent.updateType & UpdateType.Children) === 0) {
922
- // Inform the parent if it doesn’t already have a child update
923
- parent.setUpdateType(UpdateType.Children);
924
- }
925
- }
926
-
927
- sortChildren() {
928
- this.children.sort((a, b) => a.calcZIndex - b.calcZIndex);
929
- }
930
-
931
- updateScaleRotateTransform() {
932
- const { rotation, scaleX, scaleY } = this.props;
933
-
934
- // optimize simple translation cases
935
- if (rotation === 0 && scaleX === 1 && scaleY === 1) {
936
- this.scaleRotateTransform = undefined;
937
- return;
938
- }
939
-
940
- this.scaleRotateTransform = Matrix3d.rotate(
941
- rotation,
942
- this.scaleRotateTransform,
943
- ).scale(scaleX, scaleY);
944
- }
945
-
946
- updateLocalTransform() {
947
- const { x, y, width, height } = this.props;
948
- const mountTranslateX = this.props.mountX * width;
949
- const mountTranslateY = this.props.mountY * height;
950
-
951
- if (this.scaleRotateTransform) {
952
- const pivotTranslateX = this.props.pivotX * width;
953
- const pivotTranslateY = this.props.pivotY * height;
954
-
955
- this.localTransform = Matrix3d.translate(
956
- x - mountTranslateX + pivotTranslateX,
957
- y - mountTranslateY + pivotTranslateY,
958
- this.localTransform,
959
- )
960
- .multiply(this.scaleRotateTransform)
961
- .translate(-pivotTranslateX, -pivotTranslateY);
962
- } else {
963
- this.localTransform = Matrix3d.translate(
964
- x - mountTranslateX,
965
- y - mountTranslateY,
966
- this.localTransform,
967
- );
968
- }
969
-
970
- // Handle 'contain' resize mode
971
- const texture = this.props.texture;
972
- if (
973
- texture &&
974
- texture.dimensions &&
975
- this.props.textureOptions?.resizeMode?.type === 'contain'
976
- ) {
977
- let resizeModeScaleX = 1;
978
- let resizeModeScaleY = 1;
979
- let extraX = 0;
980
- let extraY = 0;
981
- const { width: tw, height: th } = texture.dimensions;
982
- const txAspectRatio = tw / th;
983
- const nodeAspectRatio = width / height;
984
- if (txAspectRatio > nodeAspectRatio) {
985
- // Texture is wider than node
986
- // Center the node vertically (shift down by extraY)
987
- // Scale the node vertically to maintain original aspect ratio
988
- const scaleX = width / tw;
989
- const scaledTxHeight = th * scaleX;
990
- extraY = (height - scaledTxHeight) / 2;
991
- resizeModeScaleY = scaledTxHeight / height;
992
- } else {
993
- // Texture is taller than node (or equal)
994
- // Center the node horizontally (shift right by extraX)
995
- // Scale the node horizontally to maintain original aspect ratio
996
- const scaleY = height / th;
997
- const scaledTxWidth = tw * scaleY;
998
- extraX = (width - scaledTxWidth) / 2;
999
- resizeModeScaleX = scaledTxWidth / width;
1000
- }
1001
-
1002
- // Apply the extra translation and scale to the local transform
1003
- this.localTransform
1004
- .translate(extraX, extraY)
1005
- .scale(resizeModeScaleX, resizeModeScaleY);
1006
- }
1007
-
1008
- this.setUpdateType(UpdateType.Global);
1009
- }
1010
-
1011
- /**
1012
- * @todo: test for correct calculation flag
1013
- * @param delta
1014
- */
1015
- update(delta: number, parentClippingRect: RectWithValid): void {
1016
- if (this.updateType & UpdateType.ScaleRotate) {
1017
- this.updateScaleRotateTransform();
1018
- this.setUpdateType(UpdateType.Local);
1019
- }
1020
-
1021
- if (this.updateType & UpdateType.Local) {
1022
- this.updateLocalTransform();
1023
- this.setUpdateType(UpdateType.Global);
1024
- }
1025
-
1026
- const parent = this.props.parent;
1027
- let renderState = null;
1028
-
1029
- // Handle specific RTT updates at this node level
1030
- if (this.updateType & UpdateType.RenderTexture && this.rtt) {
1031
- this.hasRTTupdates = true;
1032
- }
1033
-
1034
- if (this.updateType & UpdateType.Global) {
1035
- assertTruthy(this.localTransform);
1036
-
1037
- this.globalTransform = Matrix3d.copy(
1038
- parent?.globalTransform || this.localTransform,
1039
- this.globalTransform,
1040
- );
1041
-
1042
- if (this.parentHasRenderTexture && this.props.parent?.rtt) {
1043
- this.globalTransform = Matrix3d.identity();
1044
- }
1045
-
1046
- if (parent) {
1047
- this.globalTransform.multiply(this.localTransform);
1048
- }
1049
-
1050
- this.calculateRenderCoords();
1051
- this.updateBoundingRect();
1052
-
1053
- this.setUpdateType(UpdateType.RenderState | UpdateType.Children);
1054
- this.childUpdateType |= UpdateType.Global;
1055
-
1056
- if (this.clipping === true) {
1057
- this.setUpdateType(UpdateType.Clipping | UpdateType.RenderBounds);
1058
- this.childUpdateType |= UpdateType.RenderBounds;
1059
- }
1060
- }
1061
-
1062
- if (this.updateType & UpdateType.RenderBounds) {
1063
- this.createRenderBounds();
1064
- this.setUpdateType(UpdateType.RenderState);
1065
- this.setUpdateType(UpdateType.Children);
1066
- }
1067
-
1068
- if (this.updateType & UpdateType.RenderState) {
1069
- renderState = this.checkRenderBounds();
1070
- this.setUpdateType(UpdateType.IsRenderable);
1071
-
1072
- // if we're not going out of bounds, update the render state
1073
- // this is done so the update loop can finish before we mark a node
1074
- // as out of bounds
1075
- if (renderState !== CoreNodeRenderState.OutOfBounds) {
1076
- this.updateRenderState(renderState);
1077
- }
1078
- }
1079
-
1080
- if (this.updateType & UpdateType.WorldAlpha) {
1081
- if (parent) {
1082
- this.worldAlpha = parent.worldAlpha * this.props.alpha;
1083
- } else {
1084
- this.worldAlpha = this.props.alpha;
1085
- }
1086
- this.setUpdateType(
1087
- UpdateType.Children |
1088
- UpdateType.PremultipliedColors |
1089
- UpdateType.IsRenderable,
1090
- );
1091
- this.childUpdateType |= UpdateType.WorldAlpha;
1092
- }
1093
-
1094
- if (this.updateType & UpdateType.IsRenderable) {
1095
- this.updateIsRenderable();
1096
- }
1097
-
1098
- if (this.updateType & UpdateType.Clipping) {
1099
- this.calculateClippingRect(parentClippingRect);
1100
- this.setUpdateType(UpdateType.Children);
1101
-
1102
- this.childUpdateType |= UpdateType.Clipping;
1103
- this.childUpdateType |= UpdateType.RenderBounds;
1104
- }
1105
-
1106
- if (this.updateType & UpdateType.PremultipliedColors) {
1107
- this.premultipliedColorTl = mergeColorAlphaPremultiplied(
1108
- this.props.colorTl,
1109
- this.worldAlpha,
1110
- true,
1111
- );
1112
-
1113
- // If all the colors are the same just sent them all to the same value
1114
- if (
1115
- this.props.colorTl === this.props.colorTr &&
1116
- this.props.colorBl === this.props.colorBr &&
1117
- this.props.colorTl === this.props.colorBl
1118
- ) {
1119
- this.premultipliedColorTr =
1120
- this.premultipliedColorBl =
1121
- this.premultipliedColorBr =
1122
- this.premultipliedColorTl;
1123
- } else {
1124
- this.premultipliedColorTr = mergeColorAlphaPremultiplied(
1125
- this.props.colorTr,
1126
- this.worldAlpha,
1127
- true,
1128
- );
1129
- this.premultipliedColorBl = mergeColorAlphaPremultiplied(
1130
- this.props.colorBl,
1131
- this.worldAlpha,
1132
- true,
1133
- );
1134
- this.premultipliedColorBr = mergeColorAlphaPremultiplied(
1135
- this.props.colorBr,
1136
- this.worldAlpha,
1137
- true,
1138
- );
1139
- }
1140
- }
1141
-
1142
- // No need to update zIndex if there is no parent
1143
- if (parent !== null && this.updateType & UpdateType.CalculatedZIndex) {
1144
- this.calculateZIndex();
1145
- // Tell parent to re-sort children
1146
- parent.setUpdateType(UpdateType.ZIndexSortedChildren);
1147
- }
1148
-
1149
- if (
1150
- this.props.strictBounds === true &&
1151
- this.renderState === CoreNodeRenderState.OutOfBounds
1152
- ) {
1153
- return;
1154
- }
1155
-
1156
- if (this.updateType & UpdateType.Children && this.children.length > 0) {
1157
- for (let i = 0, length = this.children.length; i < length; i++) {
1158
- const child = this.children[i] as CoreNode;
1159
-
1160
- child.setUpdateType(this.childUpdateType);
1161
-
1162
- if (child.updateType === 0) {
1163
- continue;
1164
- }
1165
-
1166
- let childClippingRect = this.clippingRect;
1167
- if (this.rtt === true) {
1168
- childClippingRect = {
1169
- x: 0,
1170
- y: 0,
1171
- width: 0,
1172
- height: 0,
1173
- valid: false,
1174
- };
1175
- }
1176
-
1177
- child.update(delta, childClippingRect);
1178
- }
1179
- }
1180
-
1181
- // If the node has an RTT parent and requires a texture re-render, inform the RTT parent
1182
- // if (this.parentHasRenderTexture && this.updateType & UpdateType.RenderTexture) {
1183
- // @TODO have a more scoped down updateType for RTT updates
1184
- if (this.parentHasRenderTexture && this.updateType > 0) {
1185
- this.notifyParentRTTOfUpdate();
1186
- }
1187
-
1188
- // Sorting children MUST happen after children have been updated so
1189
- // that they have the oppotunity to update their calculated zIndex.
1190
- if (this.updateType & UpdateType.ZIndexSortedChildren) {
1191
- // reorder z-index
1192
- this.sortChildren();
1193
- }
1194
-
1195
- // If we're out of bounds, apply the render state now
1196
- // this is done so nodes can finish their entire update loop before
1197
- // being marked as out of bounds
1198
- if (renderState === CoreNodeRenderState.OutOfBounds) {
1199
- this.updateRenderState(renderState);
1200
- this.updateIsRenderable();
1201
-
1202
- if (
1203
- this.rtt === true &&
1204
- renderState === CoreNodeRenderState.OutOfBounds
1205
- ) {
1206
- // notify children that we are going out of bounds
1207
- // we have to do this now before we stop processing the render tree
1208
- this.notifyChildrenRTTOfUpdate(renderState);
1209
- // this.childUpdateType |= UpdateType.RenderState;
1210
- }
1211
- }
1212
-
1213
- // reset update type
1214
- this.updateType = 0;
1215
- this.childUpdateType = 0;
1216
- }
1217
-
1218
- private findParentRTTNode(): CoreNode | null {
1219
- let rttNode: CoreNode | null = this.parent;
1220
- while (rttNode && !rttNode.rtt) {
1221
- rttNode = rttNode.parent;
1222
- }
1223
- return rttNode;
1224
- }
1225
-
1226
- private getRTTParentRenderState(): CoreNodeRenderState | null {
1227
- const rttNode = this.rttParent || this.findParentRTTNode();
1228
- if (!rttNode) {
1229
- return null;
1230
- }
1231
-
1232
- return rttNode.renderState;
1233
- }
1234
-
1235
- private notifyChildrenRTTOfUpdate(renderState: CoreNodeRenderState) {
1236
- for (const child of this.children) {
1237
- // force child to update render state
1238
- child.updateRenderState(renderState);
1239
- child.updateIsRenderable();
1240
- child.notifyChildrenRTTOfUpdate(renderState);
1241
- }
1242
- }
1243
-
1244
- private notifyParentRTTOfUpdate() {
1245
- if (this.parent === null) {
1246
- return;
1247
- }
1248
-
1249
- const rttNode = this.rttParent || this.findParentRTTNode();
1250
- if (!rttNode) {
1251
- return;
1252
- }
1253
-
1254
- // If an RTT node is found, mark it for re-rendering
1255
- rttNode.hasRTTupdates = true;
1256
- rttNode.setUpdateType(UpdateType.RenderTexture);
1257
-
1258
- // if rttNode is nested, also make it update its RTT parent
1259
- if (rttNode.parentHasRenderTexture === true) {
1260
- rttNode.notifyParentRTTOfUpdate();
1261
- }
1262
- }
1263
-
1264
- checkRenderBounds(): CoreNodeRenderState {
1265
- assertTruthy(this.renderBound);
1266
- assertTruthy(this.strictBound);
1267
- assertTruthy(this.preloadBound);
1268
-
1269
- // if we are part of a parent render texture, we're always in bounds
1270
- if (this.parentHasRenderTexture === true) {
1271
- return this.getRTTParentRenderState() || CoreNodeRenderState.OutOfBounds;
1272
- }
1273
-
1274
- if (boundInsideBound(this.renderBound, this.strictBound)) {
1275
- return CoreNodeRenderState.InViewport;
1276
- }
1277
-
1278
- if (boundInsideBound(this.renderBound, this.preloadBound)) {
1279
- return CoreNodeRenderState.InBounds;
1280
- }
1281
-
1282
- // check if we're larger then our parent, we're definitely in the viewport
1283
- if (boundLargeThanBound(this.renderBound, this.strictBound)) {
1284
- return CoreNodeRenderState.InViewport;
1285
- }
1286
-
1287
- // check if we dont have dimensions, take our parent's render state
1288
- if (
1289
- this.parent !== null &&
1290
- (this.props.width === 0 || this.props.height === 0)
1291
- ) {
1292
- return this.parent.renderState;
1293
- }
1294
-
1295
- return CoreNodeRenderState.OutOfBounds;
1296
- }
1297
-
1298
- updateBoundingRect() {
1299
- const { renderCoords, globalTransform: transform } = this;
1300
- assertTruthy(transform);
1301
- assertTruthy(renderCoords);
1302
-
1303
- const { tb, tc } = transform;
1304
- const { x1, y1, x3, y3 } = renderCoords;
1305
- if (tb === 0 || tc === 0) {
1306
- this.renderBound = createBound(x1, y1, x3, y3, this.renderBound);
1307
- } else {
1308
- const { x2, x4, y2, y4 } = renderCoords;
1309
- this.renderBound = createBound(
1310
- Math.min(x1, x2, x3, x4),
1311
- Math.min(y1, y2, y3, y4),
1312
- Math.max(x1, x2, x3, x4),
1313
- Math.max(y1, y2, y3, y4),
1314
- this.renderBound,
1315
- );
1316
- }
1317
- }
1318
-
1319
- createRenderBounds(): void {
1320
- assertTruthy(this.stage);
1321
-
1322
- if (this.parent !== null && this.parent.strictBound !== undefined) {
1323
- // we have a parent with a valid bound, copy it
1324
- const parentBound = this.parent.strictBound;
1325
- this.strictBound = createBound(
1326
- parentBound.x1,
1327
- parentBound.y1,
1328
- parentBound.x2,
1329
- parentBound.y2,
1330
- );
1331
-
1332
- this.preloadBound = createPreloadBounds(
1333
- this.strictBound,
1334
- this.stage.boundsMargin,
1335
- );
1336
- } else {
1337
- // no parent or parent does not have a bound, take the stage boundaries
1338
- this.strictBound = this.stage.strictBound;
1339
- this.preloadBound = this.stage.preloadBound;
1340
- }
1341
-
1342
- // if clipping is disabled, we're done
1343
- if (this.props.clipping === false) {
1344
- return;
1345
- }
1346
-
1347
- // only create local clipping bounds if node itself is in bounds
1348
- // this can only be done if we have a render bound already
1349
- if (this.renderBound === undefined) {
1350
- return;
1351
- }
1352
-
1353
- // if we're out of bounds, we're done
1354
- if (boundInsideBound(this.renderBound, this.strictBound) === false) {
1355
- return;
1356
- }
1357
-
1358
- // clipping is enabled and we are in bounds create our own bounds
1359
- const { x, y, width, height } = this.props;
1360
- const { tx, ty } = this.globalTransform || {};
1361
- const _x = tx ?? x;
1362
- const _y = ty ?? y;
1363
- this.strictBound = createBound(
1364
- _x,
1365
- _y,
1366
- _x + width,
1367
- _y + height,
1368
- this.strictBound,
1369
- );
1370
-
1371
- this.preloadBound = createPreloadBounds(
1372
- this.strictBound,
1373
- this.stage.boundsMargin,
1374
- );
1375
- }
1376
-
1377
- updateRenderState(renderState: CoreNodeRenderState) {
1378
- if (renderState === this.renderState) {
1379
- return;
1380
- }
1381
-
1382
- const previous = this.renderState;
1383
- this.renderState = renderState;
1384
- const event = CoreNodeRenderStateMap.get(renderState);
1385
- assertTruthy(event);
1386
- this.emit(event, {
1387
- previous,
1388
- current: renderState,
1389
- });
1390
- }
1391
-
1392
- /**
1393
- * Updates the `isRenderable` property based on various conditions.
1394
- */
1395
- updateIsRenderable() {
1396
- let newIsRenderable = false;
1397
- let needsTextureOwnership = false;
1398
-
1399
- // If the node is out of bounds or has an alpha of 0, it is not renderable
1400
- if (this.checkBasicRenderability() === false) {
1401
- this.updateTextureOwnership(false);
1402
- this.setRenderable(false);
1403
- return;
1404
- }
1405
-
1406
- if (this.texture !== null) {
1407
- needsTextureOwnership = true;
1408
-
1409
- // we're only renderable if the texture state is loaded
1410
- newIsRenderable = this.texture.state === 'loaded';
1411
- } else if (
1412
- (this.hasShader() || this.hasColorProperties() === true) &&
1413
- this.hasDimensions() === true
1414
- ) {
1415
- // This mean we have dimensions and a color set, so we can render a ColorTexture
1416
- if (
1417
- this.stage.defaultTexture &&
1418
- this.stage.defaultTexture.state === 'loaded'
1419
- ) {
1420
- newIsRenderable = true;
1421
- }
1422
- }
1423
-
1424
- this.updateTextureOwnership(needsTextureOwnership);
1425
- this.setRenderable(newIsRenderable);
1426
- }
1427
-
1428
- /**
1429
- * Checks if the node is renderable based on world alpha, dimensions and out of bounds status.
1430
- */
1431
- checkBasicRenderability(): boolean {
1432
- if (this.worldAlpha === 0 || this.isOutOfBounds() === true) {
1433
- return false;
1434
- } else {
1435
- return true;
1436
- }
1437
- }
1438
-
1439
- /**
1440
- * Sets the renderable state and triggers changes if necessary.
1441
- * @param isRenderable - The new renderable state
1442
- */
1443
- setRenderable(isRenderable: boolean) {
1444
- this.isRenderable = isRenderable;
1445
- }
1446
-
1447
- /**
1448
- * Changes the renderable state of the node.
1449
- */
1450
- updateTextureOwnership(isRenderable: boolean) {
1451
- this.texture?.setRenderableOwner(this, isRenderable);
1452
- }
1453
-
1454
- /**
1455
- * Checks if the node is out of the viewport bounds.
1456
- */
1457
- isOutOfBounds(): boolean {
1458
- return this.renderState <= CoreNodeRenderState.OutOfBounds;
1459
- }
1460
-
1461
- /**
1462
- * Checks if the node has dimensions (width/height)
1463
- */
1464
- hasDimensions(): boolean {
1465
- return this.props.width !== 0 && this.props.height !== 0;
1466
- }
1467
-
1468
- /**
1469
- * Checks if the node has any color properties set.
1470
- */
1471
- hasColorProperties(): boolean {
1472
- return (
1473
- this.props.color !== 0 ||
1474
- this.props.colorTop !== 0 ||
1475
- this.props.colorBottom !== 0 ||
1476
- this.props.colorLeft !== 0 ||
1477
- this.props.colorRight !== 0 ||
1478
- this.props.colorTl !== 0 ||
1479
- this.props.colorTr !== 0 ||
1480
- this.props.colorBl !== 0 ||
1481
- this.props.colorBr !== 0
1482
- );
1483
- }
1484
-
1485
- hasShader(): boolean {
1486
- return this.props.shader !== null;
1487
- }
1488
-
1489
- calculateRenderCoords() {
1490
- const { width, height, globalTransform: transform } = this;
1491
- assertTruthy(transform);
1492
- const { tx, ty, ta, tb, tc, td } = transform;
1493
- if (tb === 0 && tc === 0) {
1494
- const minX = tx;
1495
- const maxX = tx + width * ta;
1496
-
1497
- const minY = ty;
1498
- const maxY = ty + height * td;
1499
- this.renderCoords = RenderCoords.translate(
1500
- //top-left
1501
- minX,
1502
- minY,
1503
- //top-right
1504
- maxX,
1505
- minY,
1506
- //bottom-right
1507
- maxX,
1508
- maxY,
1509
- //bottom-left
1510
- minX,
1511
- maxY,
1512
- this.renderCoords,
1513
- );
1514
- } else {
1515
- this.renderCoords = RenderCoords.translate(
1516
- //top-left
1517
- tx,
1518
- ty,
1519
- //top-right
1520
- tx + width * ta,
1521
- ty + width * tc,
1522
- //bottom-right
1523
- tx + width * ta + height * tb,
1524
- ty + width * tc + height * td,
1525
- //bottom-left
1526
- tx + height * tb,
1527
- ty + height * td,
1528
- this.renderCoords,
1529
- );
1530
- }
1531
- }
1532
-
1533
- /**
1534
- * This function calculates the clipping rectangle for a node.
1535
- *
1536
- * The function then checks if the node is rotated. If the node requires clipping and is not rotated, a new clipping rectangle is created based on the node's global transform and dimensions.
1537
- * If a parent clipping rectangle exists, it is intersected with the node's clipping rectangle (if it exists), or replaces the node's clipping rectangle.
1538
- *
1539
- * Finally, the node's parentClippingRect and clippingRect properties are updated.
1540
- */
1541
- calculateClippingRect(parentClippingRect: RectWithValid) {
1542
- assertTruthy(this.globalTransform);
1543
- const { clippingRect, props, globalTransform: gt } = this;
1544
- const { clipping } = props;
1545
-
1546
- const isRotated = gt.tb !== 0 || gt.tc !== 0;
1547
-
1548
- if (clipping === true && isRotated === false) {
1549
- clippingRect.x = gt.tx;
1550
- clippingRect.y = gt.ty;
1551
- clippingRect.width = this.width * gt.ta;
1552
- clippingRect.height = this.height * gt.td;
1553
- clippingRect.valid = true;
1554
- } else {
1555
- clippingRect.valid = false;
1556
- }
1557
-
1558
- if (parentClippingRect.valid === true && clippingRect.valid === true) {
1559
- // Intersect parent clipping rect with node clipping rect
1560
- intersectRect(parentClippingRect, clippingRect, clippingRect);
1561
- } else if (parentClippingRect.valid === true) {
1562
- // Copy parent clipping rect
1563
- copyRect(parentClippingRect, clippingRect);
1564
- clippingRect.valid = true;
1565
- }
1566
- }
1567
-
1568
- calculateZIndex(): void {
1569
- const props = this.props;
1570
- const z = props.zIndex || 0;
1571
- const p = props.parent?.zIndex || 0;
1572
-
1573
- let zIndex = z;
1574
- if (props.parent?.zIndexLocked) {
1575
- zIndex = z < p ? z : p;
1576
- }
1577
- this.calcZIndex = zIndex;
1578
- }
1579
-
1580
- /**
1581
- * Destroy the node and cleanup all resources
1582
- */
1583
- destroy(): void {
1584
- this.unloadTexture();
1585
-
1586
- this.clippingRect.valid = false;
1587
- this.isRenderable = false;
1588
-
1589
- this.renderCoords = undefined;
1590
- this.renderBound = undefined;
1591
- this.strictBound = undefined;
1592
- this.preloadBound = undefined;
1593
- this.globalTransform = undefined;
1594
- this.scaleRotateTransform = undefined;
1595
- this.localTransform = undefined;
1596
-
1597
- this.props.texture = null;
1598
- this.props.shader = this.stage.defShaderCtr;
1599
-
1600
- while (this.children.length > 0) {
1601
- this.children[0]?.destroy();
1602
- }
1603
-
1604
- // This very action will also remove the node from the parent's children array
1605
- this.parent = null;
1606
-
1607
- if (this.rtt) {
1608
- this.stage.renderer.removeRTTNode(this);
1609
- }
1610
-
1611
- this.removeAllListeners();
1612
- }
1613
-
1614
- renderQuads(renderer: CoreRenderer): void {
1615
- // Prevent quad rendering if parent has a render texture
1616
- // and renderer is not currently rendering to a texture
1617
- if (this.parentHasRenderTexture) {
1618
- if (!renderer.renderToTextureActive) {
1619
- return;
1620
- }
1621
- // Prevent quad rendering if parent render texture is not the active render texture
1622
- if (this.parentRenderTexture !== renderer.activeRttNode) {
1623
- return;
1624
- }
1625
- }
1626
-
1627
- assertTruthy(this.globalTransform);
1628
- assertTruthy(this.renderCoords);
1629
-
1630
- // add to list of renderables to be sorted before rendering
1631
- renderer.addQuad({
1632
- width: this.props.width,
1633
- height: this.props.height,
1634
- colorTl: this.premultipliedColorTl,
1635
- colorTr: this.premultipliedColorTr,
1636
- colorBl: this.premultipliedColorBl,
1637
- colorBr: this.premultipliedColorBr,
1638
- // if we do not have a texture, use the default texture
1639
- // this assumes any renderable node is either a distinct texture or a ColorTexture
1640
- texture: this.texture || this.stage.defaultTexture,
1641
- textureOptions: this.textureOptions,
1642
- zIndex: this.zIndex,
1643
- shader: this.shader.shader,
1644
- shaderProps: this.shader.getResolvedProps(),
1645
- alpha: this.worldAlpha,
1646
- clippingRect: this.clippingRect,
1647
- tx: this.globalTransform.tx,
1648
- ty: this.globalTransform.ty,
1649
- ta: this.globalTransform.ta,
1650
- tb: this.globalTransform.tb,
1651
- tc: this.globalTransform.tc,
1652
- td: this.globalTransform.td,
1653
- renderCoords: this.renderCoords,
1654
- rtt: this.rtt,
1655
- parentHasRenderTexture: this.parentHasRenderTexture,
1656
- framebufferDimensions: this.framebufferDimensions,
1657
- });
1658
- }
1659
-
1660
- //#region Properties
1661
- get id(): number {
1662
- return this._id;
1663
- }
1664
-
1665
- get data(): CustomDataMap | undefined {
1666
- return this.props.data;
1667
- }
1668
-
1669
- set data(d: CustomDataMap | undefined) {
1670
- this.props.data = d;
1671
- }
1672
-
1673
- get x(): number {
1674
- return this.props.x;
1675
- }
1676
-
1677
- set x(value: number) {
1678
- if (this.props.x !== value) {
1679
- this.props.x = value;
1680
- this.setUpdateType(UpdateType.Local);
1681
- }
1682
- }
1683
-
1684
- get absX(): number {
1685
- return (
1686
- this.props.x +
1687
- -this.props.width * this.props.mountX +
1688
- (this.props.parent?.absX || this.props.parent?.globalTransform?.tx || 0)
1689
- );
1690
- }
1691
-
1692
- get absY(): number {
1693
- return (
1694
- this.props.y +
1695
- -this.props.height * this.props.mountY +
1696
- (this.props.parent?.absY ?? 0)
1697
- );
1698
- }
1699
-
1700
- get y(): number {
1701
- return this.props.y;
1702
- }
1703
-
1704
- set y(value: number) {
1705
- if (this.props.y !== value) {
1706
- this.props.y = value;
1707
- this.setUpdateType(UpdateType.Local);
1708
- }
1709
- }
1710
-
1711
- get width(): number {
1712
- return this.props.width;
1713
- }
1714
-
1715
- set width(value: number) {
1716
- if (this.props.width !== value) {
1717
- this.props.width = value;
1718
- this.setUpdateType(UpdateType.Local);
1719
-
1720
- if (this.props.rtt) {
1721
- this.texture = this.stage.txManager.createTexture('RenderTexture', {
1722
- width: this.width,
1723
- height: this.height,
1724
- });
1725
-
1726
- this.setUpdateType(UpdateType.RenderTexture);
1727
- }
1728
- }
1729
- }
1730
-
1731
- get height(): number {
1732
- return this.props.height;
1733
- }
1734
-
1735
- set height(value: number) {
1736
- if (this.props.height !== value) {
1737
- this.props.height = value;
1738
- this.setUpdateType(UpdateType.Local);
1739
-
1740
- if (this.props.rtt) {
1741
- this.texture = this.stage.txManager.createTexture('RenderTexture', {
1742
- width: this.width,
1743
- height: this.height,
1744
- });
1745
-
1746
- this.setUpdateType(UpdateType.RenderTexture);
1747
- }
1748
- }
1749
- }
1750
-
1751
- get scale(): number {
1752
- // The CoreNode `scale` property is only used by Animations.
1753
- // Unlike INode, `null` should never be possibility for Animations.
1754
- return this.scaleX;
1755
- }
1756
-
1757
- set scale(value: number) {
1758
- // The CoreNode `scale` property is only used by Animations.
1759
- // Unlike INode, `null` should never be possibility for Animations.
1760
- this.scaleX = value;
1761
- this.scaleY = value;
1762
- }
1763
-
1764
- get scaleX(): number {
1765
- return this.props.scaleX;
1766
- }
1767
-
1768
- set scaleX(value: number) {
1769
- if (this.props.scaleX !== value) {
1770
- this.props.scaleX = value;
1771
- this.setUpdateType(UpdateType.ScaleRotate);
1772
- }
1773
- }
1774
-
1775
- get scaleY(): number {
1776
- return this.props.scaleY;
1777
- }
1778
-
1779
- set scaleY(value: number) {
1780
- if (this.props.scaleY !== value) {
1781
- this.props.scaleY = value;
1782
- this.setUpdateType(UpdateType.ScaleRotate);
1783
- }
1784
- }
1785
-
1786
- get mount(): number {
1787
- return this.props.mount;
1788
- }
1789
-
1790
- set mount(value: number) {
1791
- if (this.props.mountX !== value || this.props.mountY !== value) {
1792
- this.props.mountX = value;
1793
- this.props.mountY = value;
1794
- this.props.mount = value;
1795
- this.setUpdateType(UpdateType.Local);
1796
- }
1797
- }
1798
-
1799
- get mountX(): number {
1800
- return this.props.mountX;
1801
- }
1802
-
1803
- set mountX(value: number) {
1804
- if (this.props.mountX !== value) {
1805
- this.props.mountX = value;
1806
- this.setUpdateType(UpdateType.Local);
1807
- }
1808
- }
1809
-
1810
- get mountY(): number {
1811
- return this.props.mountY;
1812
- }
1813
-
1814
- set mountY(value: number) {
1815
- if (this.props.mountY !== value) {
1816
- this.props.mountY = value;
1817
- this.setUpdateType(UpdateType.Local);
1818
- }
1819
- }
1820
-
1821
- get pivot(): number {
1822
- return this.props.pivot;
1823
- }
1824
-
1825
- set pivot(value: number) {
1826
- if (this.props.pivotX !== value || this.props.pivotY !== value) {
1827
- this.props.pivotX = value;
1828
- this.props.pivotY = value;
1829
- this.props.pivot = value;
1830
- this.setUpdateType(UpdateType.Local);
1831
- }
1832
- }
1833
-
1834
- get pivotX(): number {
1835
- return this.props.pivotX;
1836
- }
1837
-
1838
- set pivotX(value: number) {
1839
- if (this.props.pivotX !== value) {
1840
- this.props.pivotX = value;
1841
- this.setUpdateType(UpdateType.Local);
1842
- }
1843
- }
1844
-
1845
- get pivotY(): number {
1846
- return this.props.pivotY;
1847
- }
1848
-
1849
- set pivotY(value: number) {
1850
- if (this.props.pivotY !== value) {
1851
- this.props.pivotY = value;
1852
- this.setUpdateType(UpdateType.Local);
1853
- }
1854
- }
1855
-
1856
- get rotation(): number {
1857
- return this.props.rotation;
1858
- }
1859
-
1860
- set rotation(value: number) {
1861
- if (this.props.rotation !== value) {
1862
- this.props.rotation = value;
1863
- this.setUpdateType(UpdateType.ScaleRotate);
1864
- }
1865
- }
1866
-
1867
- get alpha(): number {
1868
- return this.props.alpha;
1869
- }
1870
-
1871
- set alpha(value: number) {
1872
- this.props.alpha = value;
1873
- this.setUpdateType(
1874
- UpdateType.PremultipliedColors |
1875
- UpdateType.WorldAlpha |
1876
- UpdateType.Children |
1877
- UpdateType.IsRenderable,
1878
- );
1879
- this.childUpdateType |= UpdateType.WorldAlpha;
1880
- }
1881
-
1882
- get autosize(): boolean {
1883
- return this.props.autosize;
1884
- }
1885
-
1886
- set autosize(value: boolean) {
1887
- this.props.autosize = value;
1888
- }
1889
-
1890
- get clipping(): boolean {
1891
- return this.props.clipping;
1892
- }
1893
-
1894
- set clipping(value: boolean) {
1895
- this.props.clipping = value;
1896
- this.setUpdateType(
1897
- UpdateType.Clipping | UpdateType.RenderBounds | UpdateType.Children,
1898
- );
1899
- this.childUpdateType |= UpdateType.Global | UpdateType.Clipping;
1900
- }
1901
-
1902
- get color(): number {
1903
- return this.props.color;
1904
- }
1905
-
1906
- set color(value: number) {
1907
- this.colorTop = value;
1908
- this.colorBottom = value;
1909
- this.colorLeft = value;
1910
- this.colorRight = value;
1911
- this.props.color = value;
1912
-
1913
- this.setUpdateType(UpdateType.PremultipliedColors);
1914
- }
1915
-
1916
- get colorTop(): number {
1917
- return this.props.colorTop;
1918
- }
1919
-
1920
- set colorTop(value: number) {
1921
- if (this.props.colorTl !== value || this.props.colorTr !== value) {
1922
- this.colorTl = value;
1923
- this.colorTr = value;
1924
- }
1925
- this.props.colorTop = value;
1926
- this.setUpdateType(UpdateType.PremultipliedColors);
1927
- }
1928
-
1929
- get colorBottom(): number {
1930
- return this.props.colorBottom;
1931
- }
1932
-
1933
- set colorBottom(value: number) {
1934
- if (this.props.colorBl !== value || this.props.colorBr !== value) {
1935
- this.colorBl = value;
1936
- this.colorBr = value;
1937
- }
1938
- this.props.colorBottom = value;
1939
- this.setUpdateType(UpdateType.PremultipliedColors);
1940
- }
1941
-
1942
- get colorLeft(): number {
1943
- return this.props.colorLeft;
1944
- }
1945
-
1946
- set colorLeft(value: number) {
1947
- if (this.props.colorTl !== value || this.props.colorBl !== value) {
1948
- this.colorTl = value;
1949
- this.colorBl = value;
1950
- }
1951
- this.props.colorLeft = value;
1952
- this.setUpdateType(UpdateType.PremultipliedColors);
1953
- }
1954
-
1955
- get colorRight(): number {
1956
- return this.props.colorRight;
1957
- }
1958
-
1959
- set colorRight(value: number) {
1960
- if (this.props.colorTr !== value || this.props.colorBr !== value) {
1961
- this.colorTr = value;
1962
- this.colorBr = value;
1963
- }
1964
- this.props.colorRight = value;
1965
- this.setUpdateType(UpdateType.PremultipliedColors);
1966
- }
1967
-
1968
- get colorTl(): number {
1969
- return this.props.colorTl;
1970
- }
1971
-
1972
- set colorTl(value: number) {
1973
- this.props.colorTl = value;
1974
- this.setUpdateType(UpdateType.PremultipliedColors);
1975
- }
1976
-
1977
- get colorTr(): number {
1978
- return this.props.colorTr;
1979
- }
1980
-
1981
- set colorTr(value: number) {
1982
- this.props.colorTr = value;
1983
- this.setUpdateType(UpdateType.PremultipliedColors);
1984
- }
1985
-
1986
- get colorBl(): number {
1987
- return this.props.colorBl;
1988
- }
1989
-
1990
- set colorBl(value: number) {
1991
- this.props.colorBl = value;
1992
- this.setUpdateType(UpdateType.PremultipliedColors);
1993
- }
1994
-
1995
- get colorBr(): number {
1996
- return this.props.colorBr;
1997
- }
1998
-
1999
- set colorBr(value: number) {
2000
- this.props.colorBr = value;
2001
- this.setUpdateType(UpdateType.PremultipliedColors);
2002
- }
2003
-
2004
- // we're only interested in parent zIndex to test
2005
- // if we should use node zIndex is higher then parent zIndex
2006
- get zIndexLocked(): number {
2007
- return this.props.zIndexLocked || 0;
2008
- }
2009
-
2010
- set zIndexLocked(value: number) {
2011
- this.props.zIndexLocked = value;
2012
- this.setUpdateType(UpdateType.CalculatedZIndex | UpdateType.Children);
2013
- for (let i = 0, length = this.children.length; i < length; i++) {
2014
- this.children[i]!.setUpdateType(UpdateType.CalculatedZIndex);
2015
- }
2016
- }
2017
-
2018
- get zIndex(): number {
2019
- return this.props.zIndex;
2020
- }
2021
-
2022
- set zIndex(value: number) {
2023
- this.props.zIndex = value;
2024
- this.setUpdateType(UpdateType.CalculatedZIndex | UpdateType.Children);
2025
- for (let i = 0, length = this.children.length; i < length; i++) {
2026
- this.children[i]!.setUpdateType(UpdateType.CalculatedZIndex);
2027
- }
2028
- }
2029
-
2030
- get parent(): CoreNode | null {
2031
- return this.props.parent;
2032
- }
2033
-
2034
- set parent(newParent: CoreNode | null) {
2035
- const oldParent = this.props.parent;
2036
- if (oldParent === newParent) {
2037
- return;
2038
- }
2039
- this.props.parent = newParent;
2040
- if (oldParent) {
2041
- const index = oldParent.children.indexOf(this);
2042
- assertTruthy(
2043
- index !== -1,
2044
- "CoreNode.parent: Node not found in old parent's children!",
2045
- );
2046
- oldParent.children.splice(index, 1);
2047
- oldParent.setUpdateType(
2048
- UpdateType.Children | UpdateType.ZIndexSortedChildren,
2049
- );
2050
- }
2051
- if (newParent) {
2052
- newParent.children.push(this);
2053
- // Since this node has a new parent, to be safe, have it do a full update.
2054
- this.setUpdateType(UpdateType.All);
2055
- // Tell parent that it's children need to be updated and sorted.
2056
- newParent.setUpdateType(
2057
- UpdateType.Children | UpdateType.ZIndexSortedChildren,
2058
- );
2059
-
2060
- // If the new parent has an RTT enabled, apply RTT inheritance
2061
- if (newParent.rtt || newParent.parentHasRenderTexture) {
2062
- this.applyRTTInheritance(newParent);
2063
- }
2064
- }
2065
- this.updateScaleRotateTransform();
2066
-
2067
- // fetch render bounds from parent
2068
- this.setUpdateType(UpdateType.RenderBounds | UpdateType.Children);
2069
- }
2070
-
2071
- get preventCleanup(): boolean {
2072
- return this.props.textureOptions.preventCleanup || false;
2073
- }
2074
-
2075
- set preventCleanup(value: boolean) {
2076
- if (isProductionEnvironment() === false) {
2077
- console.warn(
2078
- 'CoreNode.preventCleanup: Is deprecated and will be removed in upcoming release, please use textureOptions.preventCleanup instead',
2079
- );
2080
- }
2081
-
2082
- this.props.textureOptions.preventCleanup = value;
2083
- }
2084
-
2085
- get rtt(): boolean {
2086
- return this.props.rtt;
2087
- }
2088
-
2089
- set rtt(value: boolean) {
2090
- if (this.props.rtt === value) {
2091
- return;
2092
- }
2093
- this.props.rtt = value;
2094
-
2095
- if (value === true) {
2096
- this.initRenderTexture();
2097
- this.markChildrenWithRTT();
2098
- } else {
2099
- this.cleanupRenderTexture();
2100
- }
2101
-
2102
- this.setUpdateType(UpdateType.RenderTexture);
2103
-
2104
- if (this.parentHasRenderTexture === true) {
2105
- this.notifyParentRTTOfUpdate();
2106
- }
2107
- }
2108
- private initRenderTexture() {
2109
- this.texture = this.stage.txManager.createTexture('RenderTexture', {
2110
- width: this.width,
2111
- height: this.height,
2112
- });
2113
-
2114
- this.stage.renderer.renderToTexture(this);
2115
- }
2116
-
2117
- private cleanupRenderTexture() {
2118
- this.unloadTexture();
2119
- this.clearRTTInheritance();
2120
-
2121
- this.hasRTTupdates = false;
2122
- this.texture = null;
2123
- }
2124
-
2125
- private markChildrenWithRTT(node: CoreNode | null = null) {
2126
- const parent = node || this;
2127
-
2128
- for (const child of parent.children) {
2129
- child.setUpdateType(UpdateType.All);
2130
- child.parentHasRenderTexture = true;
2131
- child.markChildrenWithRTT();
2132
- }
2133
- }
2134
-
2135
- // Apply RTT inheritance when a node has an RTT-enabled parent
2136
- private applyRTTInheritance(parent: CoreNode) {
2137
- if (parent.rtt) {
2138
- // Only the RTT node should be added to `renderToTexture`
2139
- parent.setUpdateType(UpdateType.RenderTexture);
2140
- }
2141
-
2142
- // Propagate `parentHasRenderTexture` downwards
2143
- this.markChildrenWithRTT(parent);
2144
- }
2145
-
2146
- // Clear RTT inheritance when detaching from an RTT chain
2147
- private clearRTTInheritance() {
2148
- // if this node is RTT itself stop the propagation important for nested RTT nodes
2149
- // for the initial RTT node this is already handled in `set rtt`
2150
- if (this.rtt) {
2151
- return;
2152
- }
2153
-
2154
- for (const child of this.children) {
2155
- // force child to update everything as the RTT inheritance has changed
2156
- child.parentHasRenderTexture = false;
2157
- child.rttParent = null;
2158
- child.setUpdateType(UpdateType.All);
2159
- child.clearRTTInheritance();
2160
- }
2161
- }
2162
-
2163
- get shader(): BaseShaderController {
2164
- return this.props.shader;
2165
- }
2166
-
2167
- set shader(value: BaseShaderController) {
2168
- if (this.props.shader === value) {
2169
- return;
2170
- }
2171
-
2172
- this.props.shader = value;
2173
-
2174
- this.setUpdateType(UpdateType.IsRenderable);
2175
- }
2176
-
2177
- get src(): string | null {
2178
- return this.props.src;
2179
- }
2180
-
2181
- set src(imageUrl: string | null) {
2182
- if (this.props.src === imageUrl) {
2183
- return;
2184
- }
2185
-
2186
- this.props.src = imageUrl;
2187
-
2188
- if (!imageUrl) {
2189
- this.texture = null;
2190
- return;
2191
- }
2192
-
2193
- this.texture = this.stage.txManager.createTexture('ImageTexture', {
2194
- src: imageUrl,
2195
- width: this.props.width,
2196
- height: this.props.height,
2197
- type: this.props.imageType,
2198
- sx: this.props.srcX,
2199
- sy: this.props.srcY,
2200
- sw: this.props.srcWidth,
2201
- sh: this.props.srcHeight,
2202
- });
2203
- }
2204
-
2205
- set imageType(type: 'regular' | 'compressed' | 'svg' | null) {
2206
- if (this.props.imageType === type) {
2207
- return;
2208
- }
2209
-
2210
- this.props.imageType = type;
2211
- }
2212
-
2213
- get imageType() {
2214
- return this.props.imageType || null;
2215
- }
2216
-
2217
- get srcHeight(): number | undefined {
2218
- return this.props.srcHeight;
2219
- }
2220
-
2221
- set srcHeight(value: number) {
2222
- this.props.srcHeight = value;
2223
- }
2224
-
2225
- get srcWidth(): number | undefined {
2226
- return this.props.srcWidth;
2227
- }
2228
-
2229
- set srcWidth(value: number) {
2230
- this.props.srcWidth = value;
2231
- }
2232
-
2233
- get srcX(): number | undefined {
2234
- return this.props.srcX;
2235
- }
2236
-
2237
- set srcX(value: number) {
2238
- this.props.srcX = value;
2239
- }
2240
-
2241
- get srcY(): number | undefined {
2242
- return this.props.srcY;
2243
- }
2244
-
2245
- set srcY(value: number) {
2246
- this.props.srcY = value;
2247
- }
2248
-
2249
- /**
2250
- * Returns the framebuffer dimensions of the node.
2251
- * If the node has a render texture, the dimensions are the same as the node's dimensions.
2252
- * If the node does not have a render texture, the dimensions are inherited from the parent.
2253
- * If the node parent has a render texture and the node is a render texture, the nodes dimensions are used.
2254
- */
2255
- get framebufferDimensions(): Dimensions {
2256
- if (this.parentHasRenderTexture && !this.rtt && this.parent) {
2257
- return this.parent.framebufferDimensions;
2258
- }
2259
- return { width: this.width, height: this.height };
2260
- }
2261
-
2262
- /**
2263
- * Returns the parent render texture node if it exists.
2264
- */
2265
- get parentRenderTexture(): CoreNode | null {
2266
- let parent = this.parent;
2267
- while (parent) {
2268
- if (parent.rtt) {
2269
- return parent;
2270
- }
2271
- parent = parent.parent;
2272
- }
2273
- return null;
2274
- }
2275
-
2276
- get texture(): Texture | null {
2277
- return this.props.texture;
2278
- }
2279
-
2280
- set texture(value: Texture | null) {
2281
- if (this.props.texture === value) {
2282
- return;
2283
- }
2284
-
2285
- const oldTexture = this.props.texture;
2286
- if (oldTexture) {
2287
- oldTexture.setRenderableOwner(this, false);
2288
- this.unloadTexture();
2289
- }
2290
-
2291
- this.props.texture = value;
2292
- if (value !== null) {
2293
- value.setRenderableOwner(this, this.isRenderable);
2294
- this.loadTexture();
2295
- }
2296
-
2297
- this.setUpdateType(UpdateType.IsRenderable);
2298
- }
2299
-
2300
- set textureOptions(value: TextureOptions) {
2301
- this.props.textureOptions = value;
2302
- }
2303
-
2304
- get textureOptions(): TextureOptions {
2305
- return this.props.textureOptions;
2306
- }
2307
-
2308
- get strictBounds(): boolean {
2309
- return this.props.strictBounds;
2310
- }
2311
-
2312
- set strictBounds(v) {
2313
- if (v === this.props.strictBounds) {
2314
- return;
2315
- }
2316
-
2317
- this.props.strictBounds = v;
2318
- this.setUpdateType(UpdateType.RenderBounds | UpdateType.Children);
2319
- this.childUpdateType |= UpdateType.RenderBounds | UpdateType.Children;
2320
- }
2321
-
2322
- animate(
2323
- props: Partial<CoreNodeAnimateProps>,
2324
- settings: Partial<AnimationSettings>,
2325
- ): IAnimationController {
2326
- const animation = new CoreAnimation(this, props, settings);
2327
-
2328
- const controller = new CoreAnimationController(
2329
- this.stage.animationManager,
2330
- animation,
2331
- );
2332
-
2333
- return controller;
2334
- }
2335
-
2336
- flush() {
2337
- // no-op
2338
- }
2339
-
2340
- //#endregion Properties
2341
- }
1
+ /*
2
+ * If not stated otherwise in this file or this component's LICENSE file the
3
+ * following copyright and licenses apply:
4
+ *
5
+ * Copyright 2023 Comcast Cable Communications Management, LLC.
6
+ *
7
+ * Licensed under the Apache License, Version 2.0 (the License);
8
+ * you may not use this file except in compliance with the License.
9
+ * You may obtain a copy of the License at
10
+ *
11
+ * http://www.apache.org/licenses/LICENSE-2.0
12
+ *
13
+ * Unless required by applicable law or agreed to in writing, software
14
+ * distributed under the License is distributed on an "AS IS" BASIS,
15
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16
+ * See the License for the specific language governing permissions and
17
+ * limitations under the License.
18
+ */
19
+
20
+ import {
21
+ assertTruthy,
22
+ getNewId,
23
+ isProductionEnvironment,
24
+ mergeColorAlphaPremultiplied,
25
+ } from '../utils.js';
26
+ import type { TextureOptions } from './CoreTextureManager.js';
27
+ import type { CoreRenderer } from './renderers/CoreRenderer.js';
28
+ import type { Stage } from './Stage.js';
29
+ import {
30
+ type Texture,
31
+ type TextureFailedEventHandler,
32
+ type TextureFreedEventHandler,
33
+ type TextureLoadedEventHandler,
34
+ } from './textures/Texture.js';
35
+ import type {
36
+ Dimensions,
37
+ NodeTextureFailedPayload,
38
+ NodeTextureFreedPayload,
39
+ NodeTextureLoadedPayload,
40
+ } from '../common/CommonTypes.js';
41
+ import { EventEmitter } from '../common/EventEmitter.js';
42
+ import {
43
+ copyRect,
44
+ intersectRect,
45
+ type Bound,
46
+ type RectWithValid,
47
+ createBound,
48
+ boundInsideBound,
49
+ boundLargeThanBound,
50
+ createPreloadBounds,
51
+ } from './lib/utils.js';
52
+ import { Matrix3d } from './lib/Matrix3d.js';
53
+ import { RenderCoords } from './lib/RenderCoords.js';
54
+ import type { AnimationSettings } from './animations/CoreAnimation.js';
55
+ import type { IAnimationController } from '../common/IAnimationController.js';
56
+ import { CoreAnimation } from './animations/CoreAnimation.js';
57
+ import { CoreAnimationController } from './animations/CoreAnimationController.js';
58
+ import type { BaseShaderController } from '../main-api/ShaderController.js';
59
+
60
+ export enum CoreNodeRenderState {
61
+ Init = 0,
62
+ OutOfBounds = 2,
63
+ InBounds = 4,
64
+ InViewport = 8,
65
+ }
66
+
67
+ const CoreNodeRenderStateMap: Map<CoreNodeRenderState, string> = new Map();
68
+ CoreNodeRenderStateMap.set(CoreNodeRenderState.Init, 'init');
69
+ CoreNodeRenderStateMap.set(CoreNodeRenderState.OutOfBounds, 'outOfBounds');
70
+ CoreNodeRenderStateMap.set(CoreNodeRenderState.InBounds, 'inBounds');
71
+ CoreNodeRenderStateMap.set(CoreNodeRenderState.InViewport, 'inViewport');
72
+
73
+ export enum UpdateType {
74
+ /**
75
+ * Child updates
76
+ */
77
+ Children = 1,
78
+
79
+ /**
80
+ * Scale/Rotate transform update
81
+ *
82
+ * @remarks
83
+ * CoreNode Properties Updated:
84
+ * - `scaleRotateTransform`
85
+ */
86
+ ScaleRotate = 2,
87
+
88
+ /**
89
+ * Translate transform update (x/y/width/height/pivot/mount)
90
+ *
91
+ * @remarks
92
+ * CoreNode Properties Updated:
93
+ * - `localTransform`
94
+ */
95
+ Local = 4,
96
+
97
+ /**
98
+ * Global Transform update
99
+ *
100
+ * @remarks
101
+ * CoreNode Properties Updated:
102
+ * - `globalTransform`
103
+ * - `renderCoords`
104
+ * - `renderBound`
105
+ */
106
+ Global = 8,
107
+
108
+ /**
109
+ * Clipping rect update
110
+ *
111
+ * @remarks
112
+ * CoreNode Properties Updated:
113
+ * - `clippingRect`
114
+ */
115
+ Clipping = 16,
116
+
117
+ /**
118
+ * Calculated ZIndex update
119
+ *
120
+ * @remarks
121
+ * CoreNode Properties Updated:
122
+ * - `calcZIndex`
123
+ */
124
+ CalculatedZIndex = 32,
125
+
126
+ /**
127
+ * Z-Index Sorted Children update
128
+ *
129
+ * @remarks
130
+ * CoreNode Properties Updated:
131
+ * - `children` (sorts children by their `calcZIndex`)
132
+ */
133
+ ZIndexSortedChildren = 64,
134
+
135
+ /**
136
+ * Premultiplied Colors update
137
+ *
138
+ * @remarks
139
+ * CoreNode Properties Updated:
140
+ * - `premultipliedColorTl`
141
+ * - `premultipliedColorTr`
142
+ * - `premultipliedColorBl`
143
+ * - `premultipliedColorBr`
144
+ */
145
+ PremultipliedColors = 128,
146
+
147
+ /**
148
+ * World Alpha update
149
+ *
150
+ * @remarks
151
+ * CoreNode Properties Updated:
152
+ * - `worldAlpha` = `parent.worldAlpha` * `alpha`
153
+ */
154
+ WorldAlpha = 256,
155
+
156
+ /**
157
+ * Render State update
158
+ *
159
+ * @remarks
160
+ * CoreNode Properties Updated:
161
+ * - `renderState`
162
+ */
163
+ RenderState = 512,
164
+
165
+ /**
166
+ * Is Renderable update
167
+ *
168
+ * @remarks
169
+ * CoreNode Properties Updated:
170
+ * - `isRenderable`
171
+ */
172
+ IsRenderable = 1024,
173
+
174
+ /**
175
+ * Render Texture update
176
+ */
177
+ RenderTexture = 2048,
178
+
179
+ /**
180
+ * Track if parent has render texture
181
+ */
182
+ ParentRenderTexture = 4096,
183
+
184
+ /**
185
+ * Render Bounds update
186
+ */
187
+ RenderBounds = 8192,
188
+
189
+ /**
190
+ * None
191
+ */
192
+ None = 0,
193
+
194
+ /**
195
+ * All
196
+ */
197
+ All = 14335,
198
+ }
199
+
200
+ /**
201
+ * A custom data map which can be stored on an CoreNode
202
+ *
203
+ * @remarks
204
+ * This is a map of key-value pairs that can be stored on an INode. It is used
205
+ * to store custom data that can be used by the application.
206
+ * The data stored can only be of type string, number or boolean.
207
+ */
208
+ export type CustomDataMap = {
209
+ [key: string]: string | number | boolean | undefined;
210
+ };
211
+
212
+ /**
213
+ * Writable properties of a Node.
214
+ */
215
+ export interface CoreNodeProps {
216
+ /**
217
+ * The x coordinate of the Node's Mount Point.
218
+ *
219
+ * @remarks
220
+ * See {@link mountX} and {@link mountY} for more information about setting
221
+ * the Mount Point.
222
+ *
223
+ * @default `0`
224
+ */
225
+ x: number;
226
+ /**
227
+ * The y coordinate of the Node's Mount Point.
228
+ *
229
+ * @remarks
230
+ * See {@link mountX} and {@link mountY} for more information about setting
231
+ * the Mount Point.
232
+ *
233
+ * @default `0`
234
+ */
235
+ y: number;
236
+ /**
237
+ * The width of the Node.
238
+ *
239
+ * @default `0`
240
+ */
241
+ width: number;
242
+ /**
243
+ * The height of the Node.
244
+ *
245
+ * @default `0`
246
+ */
247
+ height: number;
248
+ /**
249
+ * The alpha opacity of the Node.
250
+ *
251
+ * @remarks
252
+ * The alpha value is a number between 0 and 1, where 0 is fully transparent
253
+ * and 1 is fully opaque.
254
+ *
255
+ * @default `1`
256
+ */
257
+ alpha: number;
258
+ /**
259
+ * Autosize mode
260
+ *
261
+ * @remarks
262
+ * When enabled, when a texture is loaded into the Node, the Node will
263
+ * automatically resize to the dimensions of the texture.
264
+ *
265
+ * Text Nodes are always autosized based on their text content regardless
266
+ * of this mode setting.
267
+ *
268
+ * @default `false`
269
+ */
270
+ autosize: boolean;
271
+ /**
272
+ * Margin around the Node's bounds for preloading
273
+ *
274
+ * @default `null`
275
+ */
276
+ boundsMargin: number | [number, number, number, number] | null;
277
+ /**
278
+ * Clipping Mode
279
+ *
280
+ * @remarks
281
+ * Enable Clipping Mode when you want to prevent the drawing of a Node and
282
+ * its descendants from overflowing outside of the Node's x/y/width/height
283
+ * bounds.
284
+ *
285
+ * For WebGL, clipping is implemented using the high-performance WebGL
286
+ * operation scissor. As a consequence, clipping does not work for
287
+ * non-rectangular areas. So, if the element is rotated
288
+ * (by itself or by any of its ancestors), clipping will not work as intended.
289
+ *
290
+ * TODO: Add support for non-rectangular clipping either automatically or
291
+ * via Render-To-Texture.
292
+ *
293
+ * @default `false`
294
+ */
295
+ clipping: boolean;
296
+ /**
297
+ * The color of the Node.
298
+ *
299
+ * @remarks
300
+ * The color value is a number in the format 0xRRGGBBAA, where RR is the red
301
+ * component, GG is the green component, BB is the blue component, and AA is
302
+ * the alpha component.
303
+ *
304
+ * Gradient colors may be set by setting the different color sub-properties:
305
+ * {@link colorTop}, {@link colorBottom}, {@link colorLeft}, {@link colorRight},
306
+ * {@link colorTl}, {@link colorTr}, {@link colorBr}, {@link colorBl} accordingly.
307
+ *
308
+ * @default `0xffffffff` (opaque white)
309
+ */
310
+ color: number;
311
+ /**
312
+ * The color of the top edge of the Node for gradient rendering.
313
+ *
314
+ * @remarks
315
+ * See {@link color} for more information about color values and gradient
316
+ * rendering.
317
+ */
318
+ colorTop: number;
319
+ /**
320
+ * The color of the bottom edge of the Node for gradient rendering.
321
+ *
322
+ * @remarks
323
+ * See {@link color} for more information about color values and gradient
324
+ * rendering.
325
+ */
326
+ colorBottom: number;
327
+ /**
328
+ * The color of the left edge of the Node for gradient rendering.
329
+ *
330
+ * @remarks
331
+ * See {@link color} for more information about color values and gradient
332
+ * rendering.
333
+ */
334
+ colorLeft: number;
335
+ /**
336
+ * The color of the right edge of the Node for gradient rendering.
337
+ *
338
+ * @remarks
339
+ * See {@link color} for more information about color values and gradient
340
+ * rendering.
341
+ */
342
+ colorRight: number;
343
+ /**
344
+ * The color of the top-left corner of the Node for gradient rendering.
345
+ *
346
+ * @remarks
347
+ * See {@link color} for more information about color values and gradient
348
+ * rendering.
349
+ */
350
+ colorTl: number;
351
+ /**
352
+ * The color of the top-right corner of the Node for gradient rendering.
353
+ *
354
+ * @remarks
355
+ * See {@link color} for more information about color values and gradient
356
+ * rendering.
357
+ */
358
+ colorTr: number;
359
+ /**
360
+ * The color of the bottom-right corner of the Node for gradient rendering.
361
+ *
362
+ * @remarks
363
+ * See {@link color} for more information about color values and gradient
364
+ * rendering.
365
+ */
366
+ colorBr: number;
367
+ /**
368
+ * The color of the bottom-left corner of the Node for gradient rendering.
369
+ *
370
+ * @remarks
371
+ * See {@link color} for more information about color values and gradient
372
+ * rendering.
373
+ */
374
+ colorBl: number;
375
+ /**
376
+ * The Node's parent Node.
377
+ *
378
+ * @remarks
379
+ * The value `null` indicates that the Node has no parent. This may either be
380
+ * because the Node is the root Node of the scene graph, or because the Node
381
+ * has been removed from the scene graph.
382
+ *
383
+ * In order to make sure that a Node can be rendered on the screen, it must
384
+ * be added to the scene graph by setting it's parent property to a Node that
385
+ * is already in the scene graph such as the root Node.
386
+ *
387
+ * @default `null`
388
+ */
389
+ parent: CoreNode | null;
390
+ /**
391
+ * The Node's z-index.
392
+ *
393
+ * @remarks
394
+ * TBD
395
+ */
396
+ zIndex: number;
397
+ /**
398
+ * The Node's Texture.
399
+ *
400
+ * @remarks
401
+ * The `texture` defines a rasterized image that is contained within the
402
+ * {@link width} and {@link height} dimensions of the Node. If null, the
403
+ * Node will use an opaque white {@link ColorTexture} when being drawn, which
404
+ * essentially enables colors (including gradients) to be drawn.
405
+ *
406
+ * If set, by default, the texture will be drawn, as is, stretched to the
407
+ * dimensions of the Node. This behavior can be modified by setting the TBD
408
+ * and TBD properties.
409
+ *
410
+ * To create a Texture in order to set it on this property, call
411
+ * {@link RendererMain.createTexture}.
412
+ *
413
+ * If the {@link src} is set on a Node, the Node will use the
414
+ * {@link ImageTexture} by default and the Node will simply load the image at
415
+ * the specified URL.
416
+ *
417
+ * Note: If this is a Text Node, the Texture will be managed by the Node's
418
+ * {@link TextRenderer} and should not be set explicitly.
419
+ */
420
+ texture: Texture | null;
421
+
422
+ /**
423
+ * [Deprecated]: Prevents the texture from being cleaned up when the Node is removed
424
+ *
425
+ * @remarks
426
+ * Please use the `preventCleanup` property on {@link TextureOptions} instead.
427
+ *
428
+ * @default false
429
+ */
430
+ preventCleanup: boolean;
431
+ /**
432
+ * Options to associate with the Node's Texture
433
+ */
434
+ textureOptions: TextureOptions;
435
+
436
+ /**
437
+ * The Node's shader
438
+ *
439
+ * @remarks
440
+ * The `shader` defines a {@link Shader} used to draw the Node. By default,
441
+ * the Default Shader is used which simply draws the defined {@link texture}
442
+ * or {@link color}(s) within the Node without any special effects.
443
+ *
444
+ * To create a Shader in order to set it on this property, call
445
+ * {@link RendererMain.createShader}.
446
+ *
447
+ * Note: If this is a Text Node, the Shader will be managed by the Node's
448
+ * {@link TextRenderer} and should not be set explicitly.
449
+ */
450
+ shader: BaseShaderController;
451
+ /**
452
+ * Image URL
453
+ *
454
+ * @remarks
455
+ * When set, the Node's {@link texture} is automatically set to an
456
+ * {@link ImageTexture} using the source image URL provided (with all other
457
+ * settings being defaults)
458
+ */
459
+ src: string | null;
460
+ zIndexLocked: number;
461
+ /**
462
+ * Scale to render the Node at
463
+ *
464
+ * @remarks
465
+ * The scale value multiplies the provided {@link width} and {@link height}
466
+ * of the Node around the Node's Pivot Point (defined by the {@link pivot}
467
+ * props).
468
+ *
469
+ * Behind the scenes, setting this property sets both the {@link scaleX} and
470
+ * {@link scaleY} props to the same value.
471
+ *
472
+ * NOTE: When the scaleX and scaleY props are explicitly set to different values,
473
+ * this property returns `null`. Setting `null` on this property will have no
474
+ * effect.
475
+ *
476
+ * @default 1.0
477
+ */
478
+ scale: number | null;
479
+ /**
480
+ * Scale to render the Node at (X-Axis)
481
+ *
482
+ * @remarks
483
+ * The scaleX value multiplies the provided {@link width} of the Node around
484
+ * the Node's Pivot Point (defined by the {@link pivot} props).
485
+ *
486
+ * @default 1.0
487
+ */
488
+ scaleX: number;
489
+ /**
490
+ * Scale to render the Node at (Y-Axis)
491
+ *
492
+ * @remarks
493
+ * The scaleY value multiplies the provided {@link height} of the Node around
494
+ * the Node's Pivot Point (defined by the {@link pivot} props).
495
+ *
496
+ * @default 1.0
497
+ */
498
+ scaleY: number;
499
+ /**
500
+ * Combined position of the Node's Mount Point
501
+ *
502
+ * @remarks
503
+ * The value can be any number between `0.0` and `1.0`:
504
+ * - `0.0` defines the Mount Point at the top-left corner of the Node.
505
+ * - `0.5` defines it at the center of the Node.
506
+ * - `1.0` defines it at the bottom-right corner of the node.
507
+ *
508
+ * Use the {@link mountX} and {@link mountY} props seperately for more control
509
+ * of the Mount Point.
510
+ *
511
+ * When assigned, the same value is also passed to both the {@link mountX} and
512
+ * {@link mountY} props.
513
+ *
514
+ * @default 0 (top-left)
515
+ */
516
+ mount: number;
517
+ /**
518
+ * X position of the Node's Mount Point
519
+ *
520
+ * @remarks
521
+ * The value can be any number between `0.0` and `1.0`:
522
+ * - `0.0` defines the Mount Point's X position as the left-most edge of the
523
+ * Node
524
+ * - `0.5` defines it as the horizontal center of the Node
525
+ * - `1.0` defines it as the right-most edge of the Node.
526
+ *
527
+ * The combination of {@link mountX} and {@link mountY} define the Mount Point
528
+ *
529
+ * @default 0 (left-most edge)
530
+ */
531
+ mountX: number;
532
+ /**
533
+ * Y position of the Node's Mount Point
534
+ *
535
+ * @remarks
536
+ * The value can be any number between `0.0` and `1.0`:
537
+ * - `0.0` defines the Mount Point's Y position as the top-most edge of the
538
+ * Node
539
+ * - `0.5` defines it as the vertical center of the Node
540
+ * - `1.0` defines it as the bottom-most edge of the Node.
541
+ *
542
+ * The combination of {@link mountX} and {@link mountY} define the Mount Point
543
+ *
544
+ * @default 0 (top-most edge)
545
+ */
546
+ mountY: number;
547
+ /**
548
+ * Combined position of the Node's Pivot Point
549
+ *
550
+ * @remarks
551
+ * The value can be any number between `0.0` and `1.0`:
552
+ * - `0.0` defines the Pivot Point at the top-left corner of the Node.
553
+ * - `0.5` defines it at the center of the Node.
554
+ * - `1.0` defines it at the bottom-right corner of the node.
555
+ *
556
+ * Use the {@link pivotX} and {@link pivotY} props seperately for more control
557
+ * of the Pivot Point.
558
+ *
559
+ * When assigned, the same value is also passed to both the {@link pivotX} and
560
+ * {@link pivotY} props.
561
+ *
562
+ * @default 0.5 (center)
563
+ */
564
+ pivot: number;
565
+ /**
566
+ * X position of the Node's Pivot Point
567
+ *
568
+ * @remarks
569
+ * The value can be any number between `0.0` and `1.0`:
570
+ * - `0.0` defines the Pivot Point's X position as the left-most edge of the
571
+ * Node
572
+ * - `0.5` defines it as the horizontal center of the Node
573
+ * - `1.0` defines it as the right-most edge of the Node.
574
+ *
575
+ * The combination of {@link pivotX} and {@link pivotY} define the Pivot Point
576
+ *
577
+ * @default 0.5 (centered on x-axis)
578
+ */
579
+ pivotX: number;
580
+ /**
581
+ * Y position of the Node's Pivot Point
582
+ *
583
+ * @remarks
584
+ * The value can be any number between `0.0` and `1.0`:
585
+ * - `0.0` defines the Pivot Point's Y position as the top-most edge of the
586
+ * Node
587
+ * - `0.5` defines it as the vertical center of the Node
588
+ * - `1.0` defines it as the bottom-most edge of the Node.
589
+ *
590
+ * The combination of {@link pivotX} and {@link pivotY} define the Pivot Point
591
+ *
592
+ * @default 0.5 (centered on y-axis)
593
+ */
594
+ pivotY: number;
595
+ /**
596
+ * Rotation of the Node (in Radians)
597
+ *
598
+ * @remarks
599
+ * Sets the amount to rotate the Node by around it's Pivot Point (defined by
600
+ * the {@link pivot} props). Positive values rotate the Node clockwise, while
601
+ * negative values rotate it counter-clockwise.
602
+ *
603
+ * Example values:
604
+ * - `-Math.PI / 2`: 90 degree rotation counter-clockwise
605
+ * - `0`: No rotation
606
+ * - `Math.PI / 2`: 90 degree rotation clockwise
607
+ * - `Math.PI`: 180 degree rotation clockwise
608
+ * - `3 * Math.PI / 2`: 270 degree rotation clockwise
609
+ * - `2 * Math.PI`: 360 rotation clockwise
610
+ */
611
+ rotation: number;
612
+
613
+ /**
614
+ * Whether the Node is rendered to a texture
615
+ *
616
+ * @remarks
617
+ * TBD
618
+ *
619
+ * @default false
620
+ */
621
+ rtt: boolean;
622
+
623
+ /**
624
+ * Node data element for custom data storage (optional)
625
+ *
626
+ * @remarks
627
+ * This property is used to store custom data on the Node as a key/value data store.
628
+ * Data values are limited to string, numbers, booleans. Strings will be truncated
629
+ * to a 2048 character limit for performance reasons.
630
+ *
631
+ * This is not a data storage mechanism for large amounts of data please use a
632
+ * dedicated data storage mechanism for that.
633
+ *
634
+ * The custom data will be reflected in the inspector as part of `data-*` attributes
635
+ *
636
+ * @default `undefined`
637
+ */
638
+ data?: CustomDataMap;
639
+
640
+ /**
641
+ * Image Type to explicitly set the image type that is being loaded
642
+ *
643
+ * @remarks
644
+ * This property must be used with a `src` that points at an image. In some cases
645
+ * the extension doesn't provide a reliable representation of the image type. In such
646
+ * cases set the ImageType explicitly.
647
+ *
648
+ * `regular` is used for normal images such as png, jpg, etc
649
+ * `compressed` is used for ETC1/ETC2 compressed images with a PVR or KTX container
650
+ * `svg` is used for scalable vector graphics
651
+ *
652
+ * @default `undefined`
653
+ */
654
+ imageType?: 'regular' | 'compressed' | 'svg' | null;
655
+
656
+ /**
657
+ * She width of the rectangle from which the Image Texture will be extracted.
658
+ * This value can be negative. If not provided, the image's source natural
659
+ * width will be used.
660
+ */
661
+ srcWidth?: number;
662
+ /**
663
+ * The height of the rectangle from which the Image Texture will be extracted.
664
+ * This value can be negative. If not provided, the image's source natural
665
+ * height will be used.
666
+ */
667
+ srcHeight?: number;
668
+ /**
669
+ * The x coordinate of the reference point of the rectangle from which the Texture
670
+ * will be extracted. `width` and `height` are provided. And only works when
671
+ * createImageBitmap is available. Only works when createImageBitmap is supported on the browser.
672
+ */
673
+ srcX?: number;
674
+ /**
675
+ * The y coordinate of the reference point of the rectangle from which the Texture
676
+ * will be extracted. Only used when source `srcWidth` width and `srcHeight` height
677
+ * are provided. Only works when createImageBitmap is supported on the browser.
678
+ */
679
+ srcY?: number;
680
+ /**
681
+ * By enabling Strict bounds the renderer will not process & render child nodes of a node that is out of the visible area
682
+ *
683
+ * @remarks
684
+ * When enabled out of bound nodes, i.e. nodes that are out of the visible area, will
685
+ * **NOT** have their children processed and renderer anymore. This means the children of a out of bound
686
+ * node will not receive update processing such as positioning updates and will not be drawn on screen.
687
+ * As such the rest of the branch of the update tree that sits below this node will not be processed anymore
688
+ *
689
+ * This is a big performance gain but may be disabled in cases where the width of the parent node is
690
+ * unknown and the render must process the child nodes regardless of the viewport status of the parent node
691
+ *
692
+ * @default false
693
+ */
694
+ strictBounds: boolean;
695
+ }
696
+
697
+ /**
698
+ * Grab all the number properties of type T
699
+ */
700
+ type NumberProps<T> = {
701
+ [Key in keyof T as NonNullable<T[Key]> extends number ? Key : never]: number;
702
+ };
703
+
704
+ /**
705
+ * Properties of a Node used by the animate() function
706
+ */
707
+ export interface CoreNodeAnimateProps extends NumberProps<CoreNodeProps> {
708
+ /**
709
+ * Shader properties to animate
710
+ */
711
+ shaderProps: Record<string, number>;
712
+ // TODO: textureProps: Record<string, number>;
713
+ }
714
+
715
+ /**
716
+ * A visual Node in the Renderer scene graph.
717
+ *
718
+ * @remarks
719
+ * CoreNode is an internally used class that represents a Renderer Node in the
720
+ * scene graph. See INode.ts for the public APIs exposed to Renderer users
721
+ * that include generic types for Shaders.
722
+ */
723
+ export class CoreNode extends EventEmitter {
724
+ readonly children: CoreNode[] = [];
725
+ protected _id: number = getNewId();
726
+ readonly props: CoreNodeProps;
727
+
728
+ public updateType = UpdateType.All;
729
+ public childUpdateType = UpdateType.None;
730
+
731
+ public globalTransform?: Matrix3d;
732
+ public scaleRotateTransform?: Matrix3d;
733
+ public localTransform?: Matrix3d;
734
+ public sceneGlobalTransform?: Matrix3d;
735
+ public renderCoords?: RenderCoords;
736
+ public sceneRenderCoords?: RenderCoords;
737
+ public renderBound?: Bound;
738
+ public strictBound?: Bound;
739
+ public preloadBound?: Bound;
740
+ public clippingRect: RectWithValid = {
741
+ x: 0,
742
+ y: 0,
743
+ width: 0,
744
+ height: 0,
745
+ valid: false,
746
+ };
747
+ public isRenderable = false;
748
+ public renderState: CoreNodeRenderState = CoreNodeRenderState.Init;
749
+
750
+ public worldAlpha = 1;
751
+ public premultipliedColorTl = 0;
752
+ public premultipliedColorTr = 0;
753
+ public premultipliedColorBl = 0;
754
+ public premultipliedColorBr = 0;
755
+ public calcZIndex = 0;
756
+ public hasRTTupdates = false;
757
+ public parentHasRenderTexture = false;
758
+ public rttParent: CoreNode | null = null;
759
+
760
+ constructor(readonly stage: Stage, props: CoreNodeProps) {
761
+ super();
762
+
763
+ this.props = {
764
+ ...props,
765
+ parent: null,
766
+ texture: null,
767
+ src: null,
768
+ rtt: false,
769
+ };
770
+
771
+ // Assign props to instance
772
+ this.parent = props.parent;
773
+ this.texture = props.texture;
774
+ this.src = props.src;
775
+ this.rtt = props.rtt;
776
+
777
+ if (props.boundsMargin) {
778
+ this.boundsMargin = Array.isArray(props.boundsMargin)
779
+ ? props.boundsMargin
780
+ : [
781
+ props.boundsMargin,
782
+ props.boundsMargin,
783
+ props.boundsMargin,
784
+ props.boundsMargin,
785
+ ];
786
+ }
787
+
788
+ this.setUpdateType(
789
+ UpdateType.ScaleRotate |
790
+ UpdateType.Local |
791
+ UpdateType.RenderBounds |
792
+ UpdateType.RenderState,
793
+ );
794
+
795
+ if (isProductionEnvironment() === false && props.preventCleanup === true) {
796
+ console.warn(
797
+ 'CoreNode.preventCleanup: Is deprecated and will be removed in upcoming release, please use textureOptions.preventCleanup instead',
798
+ );
799
+ }
800
+
801
+ // if the default texture isn't loaded yet, wait for it to load
802
+ // this only happens when the node is created before the stage is ready
803
+ if (
804
+ this.stage.defaultTexture &&
805
+ this.stage.defaultTexture.state !== 'loaded'
806
+ ) {
807
+ this.stage.defaultTexture.once('loaded', () => {
808
+ this.setUpdateType(UpdateType.IsRenderable);
809
+ });
810
+ }
811
+ }
812
+
813
+ //#region Textures
814
+ loadTexture(): void {
815
+ const { texture } = this.props;
816
+ assertTruthy(texture);
817
+
818
+ // If texture is already loaded / failed, trigger loaded event manually
819
+ // so that users get a consistent event experience.
820
+ // We do this in a microtask to allow listeners to be attached in the same
821
+ // synchronous task after calling loadTexture()
822
+ queueMicrotask(() => {
823
+ if (this.textureOptions.preload === true) {
824
+ this.stage.txManager.loadTexture(texture);
825
+ }
826
+
827
+ texture.preventCleanup =
828
+ this.props.textureOptions?.preventCleanup ?? false;
829
+ texture.on('loaded', this.onTextureLoaded);
830
+ texture.on('failed', this.onTextureFailed);
831
+ texture.on('freed', this.onTextureFreed);
832
+
833
+ // If the parent is a render texture, the initial texture status
834
+ // will be set to freed until the texture is processed by the
835
+ // Render RTT nodes. So we only need to listen fo changes and
836
+ // no need to check the texture.state until we restructure how
837
+ // textures are being processed.
838
+ if (this.parentHasRenderTexture) {
839
+ this.notifyParentRTTOfUpdate();
840
+ return;
841
+ }
842
+
843
+ if (texture.state === 'loaded') {
844
+ assertTruthy(texture.dimensions);
845
+ this.onTextureLoaded(texture, texture.dimensions);
846
+ } else if (texture.state === 'failed') {
847
+ assertTruthy(texture.error);
848
+ this.onTextureFailed(texture, texture.error);
849
+ } else if (texture.state === 'freed') {
850
+ this.onTextureFreed(texture);
851
+ }
852
+ });
853
+ }
854
+
855
+ unloadTexture(): void {
856
+ if (this.texture !== null) {
857
+ this.texture.off('loaded', this.onTextureLoaded);
858
+ this.texture.off('failed', this.onTextureFailed);
859
+ this.texture.off('freed', this.onTextureFreed);
860
+ this.texture.setRenderableOwner(this, false);
861
+ }
862
+ }
863
+
864
+ autosizeNode(dimensions: Dimensions) {
865
+ if (this.autosize) {
866
+ this.width = dimensions.width;
867
+ this.height = dimensions.height;
868
+ }
869
+ }
870
+
871
+ private onTextureLoaded: TextureLoadedEventHandler = (_, dimensions) => {
872
+ this.autosizeNode(dimensions);
873
+ this.setUpdateType(UpdateType.IsRenderable);
874
+
875
+ // Texture was loaded. In case the RAF loop has already stopped, we request
876
+ // a render to ensure the texture is rendered.
877
+ this.stage.requestRender();
878
+
879
+ // If parent has a render texture, flag that we need to update
880
+ if (this.parentHasRenderTexture) {
881
+ this.notifyParentRTTOfUpdate();
882
+ }
883
+
884
+ // ignore 1x1 pixel textures
885
+ if (dimensions.width > 1 && dimensions.height > 1) {
886
+ this.emit('loaded', {
887
+ type: 'texture',
888
+ dimensions,
889
+ } satisfies NodeTextureLoadedPayload);
890
+ }
891
+
892
+ // Trigger a local update if the texture is loaded and the resizeMode is 'contain'
893
+ if (this.props.textureOptions?.resizeMode?.type === 'contain') {
894
+ this.setUpdateType(UpdateType.Local);
895
+ }
896
+ };
897
+
898
+ private onTextureFailed: TextureFailedEventHandler = (_, error) => {
899
+ this.setUpdateType(UpdateType.IsRenderable);
900
+
901
+ // If parent has a render texture, flag that we need to update
902
+ if (this.parentHasRenderTexture) {
903
+ this.notifyParentRTTOfUpdate();
904
+ }
905
+
906
+ this.emit('failed', {
907
+ type: 'texture',
908
+ error,
909
+ } satisfies NodeTextureFailedPayload);
910
+ };
911
+
912
+ private onTextureFreed: TextureFreedEventHandler = () => {
913
+ this.setUpdateType(UpdateType.IsRenderable);
914
+
915
+ // If parent has a render texture, flag that we need to update
916
+ if (this.parentHasRenderTexture) {
917
+ this.notifyParentRTTOfUpdate();
918
+ }
919
+
920
+ this.emit('freed', {
921
+ type: 'texture',
922
+ } satisfies NodeTextureFreedPayload);
923
+ };
924
+ //#endregion Textures
925
+
926
+ /**
927
+ * Change types types is used to determine the scope of the changes being applied
928
+ *
929
+ * @remarks
930
+ * See {@link UpdateType} for more information on each type
931
+ *
932
+ * @param type
933
+ */
934
+ setUpdateType(type: UpdateType): void {
935
+ this.updateType |= type;
936
+
937
+ const parent = this.props.parent;
938
+ if (!parent) return;
939
+
940
+ if ((parent.updateType & UpdateType.Children) === 0) {
941
+ // Inform the parent if it doesn’t already have a child update
942
+ parent.setUpdateType(UpdateType.Children);
943
+ }
944
+ }
945
+
946
+ sortChildren() {
947
+ this.children.sort((a, b) => a.calcZIndex - b.calcZIndex);
948
+ }
949
+
950
+ updateScaleRotateTransform() {
951
+ const { rotation, scaleX, scaleY } = this.props;
952
+
953
+ // optimize simple translation cases
954
+ if (rotation === 0 && scaleX === 1 && scaleY === 1) {
955
+ this.scaleRotateTransform = undefined;
956
+ return;
957
+ }
958
+
959
+ this.scaleRotateTransform = Matrix3d.rotate(
960
+ rotation,
961
+ this.scaleRotateTransform,
962
+ ).scale(scaleX, scaleY);
963
+ }
964
+
965
+ updateLocalTransform() {
966
+ const { x, y, width, height } = this.props;
967
+ const mountTranslateX = this.props.mountX * width;
968
+ const mountTranslateY = this.props.mountY * height;
969
+
970
+ if (this.scaleRotateTransform) {
971
+ const pivotTranslateX = this.props.pivotX * width;
972
+ const pivotTranslateY = this.props.pivotY * height;
973
+
974
+ this.localTransform = Matrix3d.translate(
975
+ x - mountTranslateX + pivotTranslateX,
976
+ y - mountTranslateY + pivotTranslateY,
977
+ this.localTransform,
978
+ )
979
+ .multiply(this.scaleRotateTransform)
980
+ .translate(-pivotTranslateX, -pivotTranslateY);
981
+ } else {
982
+ this.localTransform = Matrix3d.translate(
983
+ x - mountTranslateX,
984
+ y - mountTranslateY,
985
+ this.localTransform,
986
+ );
987
+ }
988
+
989
+ // Handle 'contain' resize mode
990
+ const texture = this.props.texture;
991
+ if (
992
+ texture &&
993
+ texture.dimensions &&
994
+ this.props.textureOptions?.resizeMode?.type === 'contain'
995
+ ) {
996
+ let resizeModeScaleX = 1;
997
+ let resizeModeScaleY = 1;
998
+ let extraX = 0;
999
+ let extraY = 0;
1000
+ const { width: tw, height: th } = texture.dimensions;
1001
+ const txAspectRatio = tw / th;
1002
+ const nodeAspectRatio = width / height;
1003
+ if (txAspectRatio > nodeAspectRatio) {
1004
+ // Texture is wider than node
1005
+ // Center the node vertically (shift down by extraY)
1006
+ // Scale the node vertically to maintain original aspect ratio
1007
+ const scaleX = width / tw;
1008
+ const scaledTxHeight = th * scaleX;
1009
+ extraY = (height - scaledTxHeight) / 2;
1010
+ resizeModeScaleY = scaledTxHeight / height;
1011
+ } else {
1012
+ // Texture is taller than node (or equal)
1013
+ // Center the node horizontally (shift right by extraX)
1014
+ // Scale the node horizontally to maintain original aspect ratio
1015
+ const scaleY = height / th;
1016
+ const scaledTxWidth = tw * scaleY;
1017
+ extraX = (width - scaledTxWidth) / 2;
1018
+ resizeModeScaleX = scaledTxWidth / width;
1019
+ }
1020
+
1021
+ // Apply the extra translation and scale to the local transform
1022
+ this.localTransform
1023
+ .translate(extraX, extraY)
1024
+ .scale(resizeModeScaleX, resizeModeScaleY);
1025
+ }
1026
+
1027
+ this.setUpdateType(UpdateType.Global);
1028
+ }
1029
+
1030
+ /**
1031
+ * @todo: test for correct calculation flag
1032
+ * @param delta
1033
+ */
1034
+ update(delta: number, parentClippingRect: RectWithValid): void {
1035
+ if (this.updateType & UpdateType.ScaleRotate) {
1036
+ this.updateScaleRotateTransform();
1037
+ this.setUpdateType(UpdateType.Local);
1038
+ }
1039
+
1040
+ if (this.updateType & UpdateType.Local) {
1041
+ this.updateLocalTransform();
1042
+ this.setUpdateType(UpdateType.Global);
1043
+ }
1044
+
1045
+ const parent = this.props.parent;
1046
+ let renderState = null;
1047
+
1048
+ // Handle specific RTT updates at this node level
1049
+ if (this.updateType & UpdateType.RenderTexture && this.rtt) {
1050
+ this.hasRTTupdates = true;
1051
+ }
1052
+
1053
+ if (this.updateType & UpdateType.Global) {
1054
+ assertTruthy(this.localTransform);
1055
+
1056
+ if (this.parentHasRenderTexture === true && parent?.rtt === true) {
1057
+ // we are at the start of the RTT chain, so we need to reset the globalTransform
1058
+ // for correct RTT rendering
1059
+ this.globalTransform = Matrix3d.identity();
1060
+
1061
+ // Maintain a full scene global transform for bounds detection
1062
+ this.sceneGlobalTransform = Matrix3d.copy(
1063
+ parent?.globalTransform || Matrix3d.identity(),
1064
+ ).multiply(this.localTransform);
1065
+ } else if (
1066
+ this.parentHasRenderTexture === true &&
1067
+ parent?.rtt === false
1068
+ ) {
1069
+ // we're part of an RTT chain but our parent is not the main RTT node
1070
+ // so we need to propogate the sceneGlobalTransform of the parent
1071
+ // to maintain a full scene global transform for bounds detection
1072
+ this.sceneGlobalTransform = Matrix3d.copy(
1073
+ parent?.sceneGlobalTransform || this.localTransform,
1074
+ ).multiply(this.localTransform);
1075
+
1076
+ this.globalTransform = Matrix3d.copy(
1077
+ parent?.globalTransform || this.localTransform,
1078
+ this.globalTransform,
1079
+ );
1080
+ } else {
1081
+ this.globalTransform = Matrix3d.copy(
1082
+ parent?.globalTransform || this.localTransform,
1083
+ this.globalTransform,
1084
+ );
1085
+ }
1086
+
1087
+ if (parent !== null) {
1088
+ this.globalTransform.multiply(this.localTransform);
1089
+ }
1090
+ this.calculateRenderCoords();
1091
+ this.updateBoundingRect();
1092
+
1093
+ this.setUpdateType(UpdateType.RenderState | UpdateType.Children);
1094
+ this.childUpdateType |= UpdateType.Global;
1095
+
1096
+ if (this.clipping === true) {
1097
+ this.setUpdateType(UpdateType.Clipping | UpdateType.RenderBounds);
1098
+ this.childUpdateType |= UpdateType.RenderBounds;
1099
+ }
1100
+ }
1101
+
1102
+ if (this.updateType & UpdateType.RenderBounds) {
1103
+ this.createRenderBounds();
1104
+ this.setUpdateType(UpdateType.RenderState);
1105
+ this.setUpdateType(UpdateType.Children);
1106
+
1107
+ this.childUpdateType |= UpdateType.RenderBounds;
1108
+ }
1109
+
1110
+ if (this.updateType & UpdateType.RenderState) {
1111
+ renderState = this.checkRenderBounds();
1112
+ this.setUpdateType(UpdateType.IsRenderable);
1113
+
1114
+ // if we're not going out of bounds, update the render state
1115
+ // this is done so the update loop can finish before we mark a node
1116
+ // as out of bounds
1117
+ if (renderState !== CoreNodeRenderState.OutOfBounds) {
1118
+ this.updateRenderState(renderState);
1119
+ }
1120
+ }
1121
+
1122
+ if (this.updateType & UpdateType.WorldAlpha) {
1123
+ if (parent) {
1124
+ this.worldAlpha = parent.worldAlpha * this.props.alpha;
1125
+ } else {
1126
+ this.worldAlpha = this.props.alpha;
1127
+ }
1128
+ this.setUpdateType(
1129
+ UpdateType.Children |
1130
+ UpdateType.PremultipliedColors |
1131
+ UpdateType.IsRenderable,
1132
+ );
1133
+ this.childUpdateType |= UpdateType.WorldAlpha;
1134
+ }
1135
+
1136
+ if (this.updateType & UpdateType.IsRenderable) {
1137
+ this.updateIsRenderable();
1138
+ }
1139
+
1140
+ if (this.updateType & UpdateType.Clipping) {
1141
+ this.calculateClippingRect(parentClippingRect);
1142
+ this.setUpdateType(UpdateType.Children);
1143
+
1144
+ this.childUpdateType |= UpdateType.Clipping;
1145
+ this.childUpdateType |= UpdateType.RenderBounds;
1146
+ }
1147
+
1148
+ if (this.updateType & UpdateType.PremultipliedColors) {
1149
+ this.premultipliedColorTl = mergeColorAlphaPremultiplied(
1150
+ this.props.colorTl,
1151
+ this.worldAlpha,
1152
+ true,
1153
+ );
1154
+
1155
+ // If all the colors are the same just sent them all to the same value
1156
+ if (
1157
+ this.props.colorTl === this.props.colorTr &&
1158
+ this.props.colorBl === this.props.colorBr &&
1159
+ this.props.colorTl === this.props.colorBl
1160
+ ) {
1161
+ this.premultipliedColorTr =
1162
+ this.premultipliedColorBl =
1163
+ this.premultipliedColorBr =
1164
+ this.premultipliedColorTl;
1165
+ } else {
1166
+ this.premultipliedColorTr = mergeColorAlphaPremultiplied(
1167
+ this.props.colorTr,
1168
+ this.worldAlpha,
1169
+ true,
1170
+ );
1171
+ this.premultipliedColorBl = mergeColorAlphaPremultiplied(
1172
+ this.props.colorBl,
1173
+ this.worldAlpha,
1174
+ true,
1175
+ );
1176
+ this.premultipliedColorBr = mergeColorAlphaPremultiplied(
1177
+ this.props.colorBr,
1178
+ this.worldAlpha,
1179
+ true,
1180
+ );
1181
+ }
1182
+ }
1183
+
1184
+ // No need to update zIndex if there is no parent
1185
+ if (parent !== null && this.updateType & UpdateType.CalculatedZIndex) {
1186
+ this.calculateZIndex();
1187
+ // Tell parent to re-sort children
1188
+ parent.setUpdateType(UpdateType.ZIndexSortedChildren);
1189
+ }
1190
+
1191
+ if (
1192
+ this.props.strictBounds === true &&
1193
+ this.renderState === CoreNodeRenderState.OutOfBounds
1194
+ ) {
1195
+ this.updateType &= ~UpdateType.RenderBounds; // remove render bounds update
1196
+ return;
1197
+ }
1198
+
1199
+ if (this.updateType & UpdateType.Children && this.children.length > 0) {
1200
+ for (let i = 0, length = this.children.length; i < length; i++) {
1201
+ const child = this.children[i] as CoreNode;
1202
+
1203
+ child.setUpdateType(this.childUpdateType);
1204
+
1205
+ if (child.updateType === 0) {
1206
+ continue;
1207
+ }
1208
+
1209
+ let childClippingRect = this.clippingRect;
1210
+ if (this.rtt === true) {
1211
+ childClippingRect = {
1212
+ x: 0,
1213
+ y: 0,
1214
+ width: 0,
1215
+ height: 0,
1216
+ valid: false,
1217
+ };
1218
+ }
1219
+
1220
+ child.update(delta, childClippingRect);
1221
+ }
1222
+ }
1223
+
1224
+ // If the node has an RTT parent and requires a texture re-render, inform the RTT parent
1225
+ // if (this.parentHasRenderTexture && this.updateType & UpdateType.RenderTexture) {
1226
+ // @TODO have a more scoped down updateType for RTT updates
1227
+ if (this.parentHasRenderTexture && this.updateType > 0) {
1228
+ this.notifyParentRTTOfUpdate();
1229
+ }
1230
+
1231
+ // Sorting children MUST happen after children have been updated so
1232
+ // that they have the oppotunity to update their calculated zIndex.
1233
+ if (this.updateType & UpdateType.ZIndexSortedChildren) {
1234
+ // reorder z-index
1235
+ this.sortChildren();
1236
+ }
1237
+
1238
+ // If we're out of bounds, apply the render state now
1239
+ // this is done so nodes can finish their entire update loop before
1240
+ // being marked as out of bounds
1241
+ if (renderState === CoreNodeRenderState.OutOfBounds) {
1242
+ this.updateRenderState(renderState);
1243
+ this.updateIsRenderable();
1244
+
1245
+ if (
1246
+ this.rtt === true &&
1247
+ renderState === CoreNodeRenderState.OutOfBounds
1248
+ ) {
1249
+ // notify children that we are going out of bounds
1250
+ // we have to do this now before we stop processing the render tree
1251
+ this.notifyChildrenRTTOfUpdate(renderState);
1252
+ // this.childUpdateType |= UpdateType.RenderState;
1253
+ }
1254
+ }
1255
+
1256
+ // reset update type
1257
+ this.updateType = 0;
1258
+ this.childUpdateType = 0;
1259
+ }
1260
+
1261
+ private findParentRTTNode(): CoreNode | null {
1262
+ let rttNode: CoreNode | null = this.parent;
1263
+ while (rttNode && !rttNode.rtt) {
1264
+ rttNode = rttNode.parent;
1265
+ }
1266
+ return rttNode;
1267
+ }
1268
+
1269
+ private getRTTParentRenderState(): CoreNodeRenderState | null {
1270
+ const rttNode = this.rttParent || this.findParentRTTNode();
1271
+ if (!rttNode) {
1272
+ return null;
1273
+ }
1274
+
1275
+ return rttNode.renderState;
1276
+ }
1277
+
1278
+ private notifyChildrenRTTOfUpdate(renderState: CoreNodeRenderState) {
1279
+ for (const child of this.children) {
1280
+ // force child to update render state
1281
+ child.updateRenderState(renderState);
1282
+ child.updateIsRenderable();
1283
+ child.notifyChildrenRTTOfUpdate(renderState);
1284
+ }
1285
+ }
1286
+
1287
+ private notifyParentRTTOfUpdate() {
1288
+ if (this.parent === null) {
1289
+ return;
1290
+ }
1291
+
1292
+ const rttNode = this.rttParent || this.findParentRTTNode();
1293
+ if (!rttNode) {
1294
+ return;
1295
+ }
1296
+
1297
+ // If an RTT node is found, mark it for re-rendering
1298
+ rttNode.hasRTTupdates = true;
1299
+ rttNode.setUpdateType(UpdateType.RenderTexture);
1300
+
1301
+ // if rttNode is nested, also make it update its RTT parent
1302
+ if (rttNode.parentHasRenderTexture === true) {
1303
+ rttNode.notifyParentRTTOfUpdate();
1304
+ }
1305
+ }
1306
+
1307
+ checkRenderBounds(): CoreNodeRenderState {
1308
+ assertTruthy(this.renderBound);
1309
+ assertTruthy(this.strictBound);
1310
+ assertTruthy(this.preloadBound);
1311
+
1312
+ if (boundInsideBound(this.renderBound, this.strictBound)) {
1313
+ return CoreNodeRenderState.InViewport;
1314
+ }
1315
+
1316
+ if (boundInsideBound(this.renderBound, this.preloadBound)) {
1317
+ return CoreNodeRenderState.InBounds;
1318
+ }
1319
+
1320
+ // check if we're larger then our parent, we're definitely in the viewport
1321
+ if (boundLargeThanBound(this.renderBound, this.strictBound)) {
1322
+ return CoreNodeRenderState.InViewport;
1323
+ }
1324
+
1325
+ // check if we dont have dimensions, take our parent's render state
1326
+ if (
1327
+ this.parent !== null &&
1328
+ (this.props.width === 0 || this.props.height === 0)
1329
+ ) {
1330
+ return this.parent.renderState;
1331
+ }
1332
+
1333
+ return CoreNodeRenderState.OutOfBounds;
1334
+ }
1335
+
1336
+ updateBoundingRect() {
1337
+ const transform = this.sceneGlobalTransform || this.globalTransform;
1338
+ const renderCoords = this.sceneRenderCoords || this.renderCoords;
1339
+
1340
+ assertTruthy(transform);
1341
+ assertTruthy(renderCoords);
1342
+
1343
+ const { tb, tc } = transform;
1344
+ const { x1, y1, x3, y3 } = renderCoords;
1345
+ if (tb === 0 || tc === 0) {
1346
+ this.renderBound = createBound(x1, y1, x3, y3, this.renderBound);
1347
+ } else {
1348
+ const { x2, x4, y2, y4 } = renderCoords;
1349
+ this.renderBound = createBound(
1350
+ Math.min(x1, x2, x3, x4),
1351
+ Math.min(y1, y2, y3, y4),
1352
+ Math.max(x1, x2, x3, x4),
1353
+ Math.max(y1, y2, y3, y4),
1354
+ this.renderBound,
1355
+ );
1356
+ }
1357
+ }
1358
+
1359
+ createRenderBounds(): void {
1360
+ assertTruthy(this.stage);
1361
+
1362
+ if (this.parent !== null && this.parent.strictBound !== undefined) {
1363
+ // we have a parent with a valid bound, copy it
1364
+ const parentBound = this.parent.strictBound;
1365
+ this.strictBound = createBound(
1366
+ parentBound.x1,
1367
+ parentBound.y1,
1368
+ parentBound.x2,
1369
+ parentBound.y2,
1370
+ );
1371
+
1372
+ this.preloadBound = createPreloadBounds(
1373
+ this.strictBound,
1374
+ this.boundsMargin as [number, number, number, number],
1375
+ );
1376
+ } else {
1377
+ // no parent or parent does not have a bound, take the stage boundaries
1378
+ this.strictBound = this.stage.strictBound;
1379
+ this.preloadBound = this.stage.preloadBound;
1380
+ }
1381
+
1382
+ // if clipping is disabled, we're done
1383
+ if (this.props.clipping === false) {
1384
+ return;
1385
+ }
1386
+
1387
+ // only create local clipping bounds if node itself is in bounds
1388
+ // this can only be done if we have a render bound already
1389
+ if (this.renderBound === undefined) {
1390
+ return;
1391
+ }
1392
+
1393
+ // if we're out of bounds, we're done
1394
+ if (boundInsideBound(this.renderBound, this.strictBound) === false) {
1395
+ return;
1396
+ }
1397
+
1398
+ // clipping is enabled and we are in bounds create our own bounds
1399
+ const { x, y, width, height } = this.props;
1400
+
1401
+ // Pick the global transform if available, otherwise use the local transform
1402
+ // global transform is only available if the node in an RTT chain
1403
+ const { tx, ty } = this.sceneGlobalTransform || this.globalTransform || {};
1404
+ const _x = tx ?? x;
1405
+ const _y = ty ?? y;
1406
+ this.strictBound = createBound(
1407
+ _x,
1408
+ _y,
1409
+ _x + width,
1410
+ _y + height,
1411
+ this.strictBound,
1412
+ );
1413
+
1414
+ this.preloadBound = createPreloadBounds(
1415
+ this.strictBound,
1416
+ this.boundsMargin as [number, number, number, number],
1417
+ );
1418
+ }
1419
+
1420
+ updateRenderState(renderState: CoreNodeRenderState) {
1421
+ if (renderState === this.renderState) {
1422
+ return;
1423
+ }
1424
+
1425
+ const previous = this.renderState;
1426
+ this.renderState = renderState;
1427
+ const event = CoreNodeRenderStateMap.get(renderState);
1428
+ assertTruthy(event);
1429
+ this.emit(event, {
1430
+ previous,
1431
+ current: renderState,
1432
+ });
1433
+ }
1434
+
1435
+ /**
1436
+ * Updates the `isRenderable` property based on various conditions.
1437
+ */
1438
+ updateIsRenderable() {
1439
+ let newIsRenderable = false;
1440
+ let needsTextureOwnership = false;
1441
+
1442
+ // If the node is out of bounds or has an alpha of 0, it is not renderable
1443
+ if (this.checkBasicRenderability() === false) {
1444
+ this.updateTextureOwnership(false);
1445
+ this.setRenderable(false);
1446
+ return;
1447
+ }
1448
+
1449
+ if (this.texture !== null) {
1450
+ needsTextureOwnership = true;
1451
+
1452
+ // we're only renderable if the texture state is loaded
1453
+ newIsRenderable = this.texture.state === 'loaded';
1454
+ } else if (
1455
+ (this.hasShader() || this.hasColorProperties() === true) &&
1456
+ this.hasDimensions() === true
1457
+ ) {
1458
+ // This mean we have dimensions and a color set, so we can render a ColorTexture
1459
+ if (
1460
+ this.stage.defaultTexture &&
1461
+ this.stage.defaultTexture.state === 'loaded'
1462
+ ) {
1463
+ newIsRenderable = true;
1464
+ }
1465
+ }
1466
+
1467
+ this.updateTextureOwnership(needsTextureOwnership);
1468
+ this.setRenderable(newIsRenderable);
1469
+ }
1470
+
1471
+ /**
1472
+ * Checks if the node is renderable based on world alpha, dimensions and out of bounds status.
1473
+ */
1474
+ checkBasicRenderability(): boolean {
1475
+ if (this.worldAlpha === 0 || this.isOutOfBounds() === true) {
1476
+ return false;
1477
+ } else {
1478
+ return true;
1479
+ }
1480
+ }
1481
+
1482
+ /**
1483
+ * Sets the renderable state and triggers changes if necessary.
1484
+ * @param isRenderable - The new renderable state
1485
+ */
1486
+ setRenderable(isRenderable: boolean) {
1487
+ this.isRenderable = isRenderable;
1488
+ }
1489
+
1490
+ /**
1491
+ * Changes the renderable state of the node.
1492
+ */
1493
+ updateTextureOwnership(isRenderable: boolean) {
1494
+ this.texture?.setRenderableOwner(this, isRenderable);
1495
+ }
1496
+
1497
+ /**
1498
+ * Checks if the node is out of the viewport bounds.
1499
+ */
1500
+ isOutOfBounds(): boolean {
1501
+ return this.renderState <= CoreNodeRenderState.OutOfBounds;
1502
+ }
1503
+
1504
+ /**
1505
+ * Checks if the node has dimensions (width/height)
1506
+ */
1507
+ hasDimensions(): boolean {
1508
+ return this.props.width !== 0 && this.props.height !== 0;
1509
+ }
1510
+
1511
+ /**
1512
+ * Checks if the node has any color properties set.
1513
+ */
1514
+ hasColorProperties(): boolean {
1515
+ return (
1516
+ this.props.color !== 0 ||
1517
+ this.props.colorTop !== 0 ||
1518
+ this.props.colorBottom !== 0 ||
1519
+ this.props.colorLeft !== 0 ||
1520
+ this.props.colorRight !== 0 ||
1521
+ this.props.colorTl !== 0 ||
1522
+ this.props.colorTr !== 0 ||
1523
+ this.props.colorBl !== 0 ||
1524
+ this.props.colorBr !== 0
1525
+ );
1526
+ }
1527
+
1528
+ hasShader(): boolean {
1529
+ return this.props.shader !== null;
1530
+ }
1531
+
1532
+ calculateRenderCoords() {
1533
+ const { width, height } = this;
1534
+ const { tx, ty, ta, tb, tc, td } = this.globalTransform!;
1535
+ if (tb === 0 && tc === 0) {
1536
+ const minX = tx;
1537
+ const maxX = tx + width * ta;
1538
+ const minY = ty;
1539
+ const maxY = ty + height * td;
1540
+ this.renderCoords = RenderCoords.translate(
1541
+ //top-left
1542
+ minX,
1543
+ minY,
1544
+ //top-right
1545
+ maxX,
1546
+ minY,
1547
+ //bottom-right
1548
+ maxX,
1549
+ maxY,
1550
+ //bottom-left
1551
+ minX,
1552
+ maxY,
1553
+ this.renderCoords,
1554
+ );
1555
+ } else {
1556
+ this.renderCoords = RenderCoords.translate(
1557
+ //top-left
1558
+ tx,
1559
+ ty,
1560
+ //top-right
1561
+ tx + width * ta,
1562
+ ty + width * tc,
1563
+ //bottom-right
1564
+ tx + width * ta + height * tb,
1565
+ ty + width * tc + height * td,
1566
+ //bottom-left
1567
+ tx + height * tb,
1568
+ ty + height * td,
1569
+ this.renderCoords,
1570
+ );
1571
+ }
1572
+ if (this.sceneGlobalTransform === undefined) {
1573
+ return;
1574
+ }
1575
+
1576
+ const {
1577
+ tx: stx,
1578
+ ty: sty,
1579
+ ta: sta,
1580
+ tb: stb,
1581
+ tc: stc,
1582
+ td: std,
1583
+ } = this.sceneGlobalTransform;
1584
+ if (stb === 0 && stc === 0) {
1585
+ const minX = stx;
1586
+ const maxX = stx + width * sta;
1587
+ const minY = sty;
1588
+ const maxY = sty + height * std;
1589
+ this.sceneRenderCoords = RenderCoords.translate(
1590
+ //top-left
1591
+ minX,
1592
+ minY,
1593
+ //top-right
1594
+ maxX,
1595
+ minY,
1596
+ //bottom-right
1597
+ maxX,
1598
+ maxY,
1599
+ //bottom-left
1600
+ minX,
1601
+ maxY,
1602
+ this.sceneRenderCoords,
1603
+ );
1604
+ } else {
1605
+ this.sceneRenderCoords = RenderCoords.translate(
1606
+ //top-left
1607
+ stx,
1608
+ sty,
1609
+ //top-right
1610
+ stx + width * sta,
1611
+ sty + width * stc,
1612
+ //bottom-right
1613
+ stx + width * sta + height * stb,
1614
+ sty + width * stc + height * std,
1615
+ //bottom-left
1616
+ stx + height * stb,
1617
+ sty + height * std,
1618
+ this.sceneRenderCoords,
1619
+ );
1620
+ }
1621
+ }
1622
+
1623
+ /**
1624
+ * This function calculates the clipping rectangle for a node.
1625
+ *
1626
+ * The function then checks if the node is rotated. If the node requires clipping and is not rotated, a new clipping rectangle is created based on the node's global transform and dimensions.
1627
+ * If a parent clipping rectangle exists, it is intersected with the node's clipping rectangle (if it exists), or replaces the node's clipping rectangle.
1628
+ *
1629
+ * Finally, the node's parentClippingRect and clippingRect properties are updated.
1630
+ */
1631
+ calculateClippingRect(parentClippingRect: RectWithValid) {
1632
+ assertTruthy(this.globalTransform);
1633
+ const { clippingRect, props, globalTransform: gt } = this;
1634
+ const { clipping } = props;
1635
+ const isRotated = gt.tb !== 0 || gt.tc !== 0;
1636
+
1637
+ if (clipping === true && isRotated === false) {
1638
+ clippingRect.x = gt.tx;
1639
+ clippingRect.y = gt.ty;
1640
+ clippingRect.width = this.width * gt.ta;
1641
+ clippingRect.height = this.height * gt.td;
1642
+ clippingRect.valid = true;
1643
+ } else {
1644
+ clippingRect.valid = false;
1645
+ }
1646
+
1647
+ if (parentClippingRect.valid === true && clippingRect.valid === true) {
1648
+ // Intersect parent clipping rect with node clipping rect
1649
+ intersectRect(parentClippingRect, clippingRect, clippingRect);
1650
+ } else if (parentClippingRect.valid === true) {
1651
+ // Copy parent clipping rect
1652
+ copyRect(parentClippingRect, clippingRect);
1653
+ clippingRect.valid = true;
1654
+ }
1655
+ }
1656
+
1657
+ calculateZIndex(): void {
1658
+ const props = this.props;
1659
+ const z = props.zIndex || 0;
1660
+ const p = props.parent?.zIndex || 0;
1661
+
1662
+ let zIndex = z;
1663
+ if (props.parent?.zIndexLocked) {
1664
+ zIndex = z < p ? z : p;
1665
+ }
1666
+ this.calcZIndex = zIndex;
1667
+ }
1668
+
1669
+ /**
1670
+ * Destroy the node and cleanup all resources
1671
+ */
1672
+ destroy(): void {
1673
+ this.unloadTexture();
1674
+
1675
+ this.clippingRect.valid = false;
1676
+ this.isRenderable = false;
1677
+
1678
+ this.renderCoords = undefined;
1679
+ this.renderBound = undefined;
1680
+ this.strictBound = undefined;
1681
+ this.preloadBound = undefined;
1682
+ this.globalTransform = undefined;
1683
+ this.scaleRotateTransform = undefined;
1684
+ this.localTransform = undefined;
1685
+
1686
+ this.props.texture = null;
1687
+ this.props.shader = this.stage.defShaderCtr;
1688
+
1689
+ while (this.children.length > 0) {
1690
+ this.children[0]?.destroy();
1691
+ }
1692
+
1693
+ // This very action will also remove the node from the parent's children array
1694
+ this.parent = null;
1695
+
1696
+ if (this.rtt) {
1697
+ this.stage.renderer.removeRTTNode(this);
1698
+ }
1699
+
1700
+ this.removeAllListeners();
1701
+ }
1702
+
1703
+ renderQuads(renderer: CoreRenderer): void {
1704
+ // Prevent quad rendering if parent has a render texture
1705
+ // and renderer is not currently rendering to a texture
1706
+ if (this.parentHasRenderTexture) {
1707
+ if (!renderer.renderToTextureActive) {
1708
+ return;
1709
+ }
1710
+ // Prevent quad rendering if parent render texture is not the active render texture
1711
+ if (this.parentRenderTexture !== renderer.activeRttNode) {
1712
+ return;
1713
+ }
1714
+ }
1715
+
1716
+ assertTruthy(this.globalTransform);
1717
+ assertTruthy(this.renderCoords);
1718
+
1719
+ // add to list of renderables to be sorted before rendering
1720
+ renderer.addQuad({
1721
+ width: this.props.width,
1722
+ height: this.props.height,
1723
+ colorTl: this.premultipliedColorTl,
1724
+ colorTr: this.premultipliedColorTr,
1725
+ colorBl: this.premultipliedColorBl,
1726
+ colorBr: this.premultipliedColorBr,
1727
+ // if we do not have a texture, use the default texture
1728
+ // this assumes any renderable node is either a distinct texture or a ColorTexture
1729
+ texture: this.texture || this.stage.defaultTexture,
1730
+ textureOptions: this.textureOptions,
1731
+ zIndex: this.zIndex,
1732
+ shader: this.shader.shader,
1733
+ shaderProps: this.shader.getResolvedProps(),
1734
+ alpha: this.worldAlpha,
1735
+ clippingRect: this.clippingRect,
1736
+ tx: this.globalTransform.tx,
1737
+ ty: this.globalTransform.ty,
1738
+ ta: this.globalTransform.ta,
1739
+ tb: this.globalTransform.tb,
1740
+ tc: this.globalTransform.tc,
1741
+ td: this.globalTransform.td,
1742
+ renderCoords: this.renderCoords,
1743
+ rtt: this.rtt,
1744
+ parentHasRenderTexture: this.parentHasRenderTexture,
1745
+ framebufferDimensions: this.framebufferDimensions,
1746
+ });
1747
+ }
1748
+
1749
+ //#region Properties
1750
+ get id(): number {
1751
+ return this._id;
1752
+ }
1753
+
1754
+ get data(): CustomDataMap | undefined {
1755
+ return this.props.data;
1756
+ }
1757
+
1758
+ set data(d: CustomDataMap | undefined) {
1759
+ this.props.data = d;
1760
+ }
1761
+
1762
+ get x(): number {
1763
+ return this.props.x;
1764
+ }
1765
+
1766
+ set x(value: number) {
1767
+ if (this.props.x !== value) {
1768
+ this.props.x = value;
1769
+ this.setUpdateType(UpdateType.Local);
1770
+ }
1771
+ }
1772
+
1773
+ get absX(): number {
1774
+ return (
1775
+ this.props.x +
1776
+ -this.props.width * this.props.mountX +
1777
+ (this.props.parent?.absX || this.props.parent?.globalTransform?.tx || 0)
1778
+ );
1779
+ }
1780
+
1781
+ get absY(): number {
1782
+ return (
1783
+ this.props.y +
1784
+ -this.props.height * this.props.mountY +
1785
+ (this.props.parent?.absY ?? 0)
1786
+ );
1787
+ }
1788
+
1789
+ get y(): number {
1790
+ return this.props.y;
1791
+ }
1792
+
1793
+ set y(value: number) {
1794
+ if (this.props.y !== value) {
1795
+ this.props.y = value;
1796
+ this.setUpdateType(UpdateType.Local);
1797
+ }
1798
+ }
1799
+
1800
+ get width(): number {
1801
+ return this.props.width;
1802
+ }
1803
+
1804
+ set width(value: number) {
1805
+ if (this.props.width !== value) {
1806
+ this.props.width = value;
1807
+ this.setUpdateType(UpdateType.Local);
1808
+
1809
+ if (this.props.rtt) {
1810
+ this.texture = this.stage.txManager.createTexture('RenderTexture', {
1811
+ width: this.width,
1812
+ height: this.height,
1813
+ });
1814
+
1815
+ this.setUpdateType(UpdateType.RenderTexture);
1816
+ }
1817
+ }
1818
+ }
1819
+
1820
+ get height(): number {
1821
+ return this.props.height;
1822
+ }
1823
+
1824
+ set height(value: number) {
1825
+ if (this.props.height !== value) {
1826
+ this.props.height = value;
1827
+ this.setUpdateType(UpdateType.Local);
1828
+
1829
+ if (this.props.rtt) {
1830
+ this.texture = this.stage.txManager.createTexture('RenderTexture', {
1831
+ width: this.width,
1832
+ height: this.height,
1833
+ });
1834
+
1835
+ this.setUpdateType(UpdateType.RenderTexture);
1836
+ }
1837
+ }
1838
+ }
1839
+
1840
+ get scale(): number {
1841
+ // The CoreNode `scale` property is only used by Animations.
1842
+ // Unlike INode, `null` should never be possibility for Animations.
1843
+ return this.scaleX;
1844
+ }
1845
+
1846
+ set scale(value: number) {
1847
+ // The CoreNode `scale` property is only used by Animations.
1848
+ // Unlike INode, `null` should never be possibility for Animations.
1849
+ this.scaleX = value;
1850
+ this.scaleY = value;
1851
+ }
1852
+
1853
+ get scaleX(): number {
1854
+ return this.props.scaleX;
1855
+ }
1856
+
1857
+ set scaleX(value: number) {
1858
+ if (this.props.scaleX !== value) {
1859
+ this.props.scaleX = value;
1860
+ this.setUpdateType(UpdateType.ScaleRotate);
1861
+ }
1862
+ }
1863
+
1864
+ get scaleY(): number {
1865
+ return this.props.scaleY;
1866
+ }
1867
+
1868
+ set scaleY(value: number) {
1869
+ if (this.props.scaleY !== value) {
1870
+ this.props.scaleY = value;
1871
+ this.setUpdateType(UpdateType.ScaleRotate);
1872
+ }
1873
+ }
1874
+
1875
+ get mount(): number {
1876
+ return this.props.mount;
1877
+ }
1878
+
1879
+ set mount(value: number) {
1880
+ if (this.props.mountX !== value || this.props.mountY !== value) {
1881
+ this.props.mountX = value;
1882
+ this.props.mountY = value;
1883
+ this.props.mount = value;
1884
+ this.setUpdateType(UpdateType.Local);
1885
+ }
1886
+ }
1887
+
1888
+ get mountX(): number {
1889
+ return this.props.mountX;
1890
+ }
1891
+
1892
+ set mountX(value: number) {
1893
+ if (this.props.mountX !== value) {
1894
+ this.props.mountX = value;
1895
+ this.setUpdateType(UpdateType.Local);
1896
+ }
1897
+ }
1898
+
1899
+ get mountY(): number {
1900
+ return this.props.mountY;
1901
+ }
1902
+
1903
+ set mountY(value: number) {
1904
+ if (this.props.mountY !== value) {
1905
+ this.props.mountY = value;
1906
+ this.setUpdateType(UpdateType.Local);
1907
+ }
1908
+ }
1909
+
1910
+ get pivot(): number {
1911
+ return this.props.pivot;
1912
+ }
1913
+
1914
+ set pivot(value: number) {
1915
+ if (this.props.pivotX !== value || this.props.pivotY !== value) {
1916
+ this.props.pivotX = value;
1917
+ this.props.pivotY = value;
1918
+ this.props.pivot = value;
1919
+ this.setUpdateType(UpdateType.Local);
1920
+ }
1921
+ }
1922
+
1923
+ get pivotX(): number {
1924
+ return this.props.pivotX;
1925
+ }
1926
+
1927
+ set pivotX(value: number) {
1928
+ if (this.props.pivotX !== value) {
1929
+ this.props.pivotX = value;
1930
+ this.setUpdateType(UpdateType.Local);
1931
+ }
1932
+ }
1933
+
1934
+ get pivotY(): number {
1935
+ return this.props.pivotY;
1936
+ }
1937
+
1938
+ set pivotY(value: number) {
1939
+ if (this.props.pivotY !== value) {
1940
+ this.props.pivotY = value;
1941
+ this.setUpdateType(UpdateType.Local);
1942
+ }
1943
+ }
1944
+
1945
+ get rotation(): number {
1946
+ return this.props.rotation;
1947
+ }
1948
+
1949
+ set rotation(value: number) {
1950
+ if (this.props.rotation !== value) {
1951
+ this.props.rotation = value;
1952
+ this.setUpdateType(UpdateType.ScaleRotate);
1953
+ }
1954
+ }
1955
+
1956
+ get alpha(): number {
1957
+ return this.props.alpha;
1958
+ }
1959
+
1960
+ set alpha(value: number) {
1961
+ this.props.alpha = value;
1962
+ this.setUpdateType(
1963
+ UpdateType.PremultipliedColors |
1964
+ UpdateType.WorldAlpha |
1965
+ UpdateType.Children |
1966
+ UpdateType.IsRenderable,
1967
+ );
1968
+ this.childUpdateType |= UpdateType.WorldAlpha;
1969
+ }
1970
+
1971
+ get autosize(): boolean {
1972
+ return this.props.autosize;
1973
+ }
1974
+
1975
+ set autosize(value: boolean) {
1976
+ this.props.autosize = value;
1977
+ }
1978
+
1979
+ get boundsMargin(): number | [number, number, number, number] | null {
1980
+ return (
1981
+ this.props.boundsMargin ??
1982
+ this.parent?.boundsMargin ??
1983
+ this.stage.boundsMargin
1984
+ );
1985
+ }
1986
+
1987
+ set boundsMargin(value: number | [number, number, number, number] | null) {
1988
+ if (value === this.props.boundsMargin) {
1989
+ return;
1990
+ }
1991
+
1992
+ if (value === null) {
1993
+ this.props.boundsMargin = value;
1994
+ } else {
1995
+ const bm: [number, number, number, number] = Array.isArray(value)
1996
+ ? value
1997
+ : [value, value, value, value];
1998
+
1999
+ this.props.boundsMargin = bm;
2000
+ }
2001
+ this.setUpdateType(UpdateType.RenderBounds);
2002
+ }
2003
+
2004
+ get clipping(): boolean {
2005
+ return this.props.clipping;
2006
+ }
2007
+
2008
+ set clipping(value: boolean) {
2009
+ this.props.clipping = value;
2010
+ this.setUpdateType(
2011
+ UpdateType.Clipping | UpdateType.RenderBounds | UpdateType.Children,
2012
+ );
2013
+ this.childUpdateType |= UpdateType.Global | UpdateType.Clipping;
2014
+ }
2015
+
2016
+ get color(): number {
2017
+ return this.props.color;
2018
+ }
2019
+
2020
+ set color(value: number) {
2021
+ this.colorTop = value;
2022
+ this.colorBottom = value;
2023
+ this.colorLeft = value;
2024
+ this.colorRight = value;
2025
+ this.props.color = value;
2026
+
2027
+ this.setUpdateType(UpdateType.PremultipliedColors);
2028
+ }
2029
+
2030
+ get colorTop(): number {
2031
+ return this.props.colorTop;
2032
+ }
2033
+
2034
+ set colorTop(value: number) {
2035
+ if (this.props.colorTl !== value || this.props.colorTr !== value) {
2036
+ this.colorTl = value;
2037
+ this.colorTr = value;
2038
+ }
2039
+ this.props.colorTop = value;
2040
+ this.setUpdateType(UpdateType.PremultipliedColors);
2041
+ }
2042
+
2043
+ get colorBottom(): number {
2044
+ return this.props.colorBottom;
2045
+ }
2046
+
2047
+ set colorBottom(value: number) {
2048
+ if (this.props.colorBl !== value || this.props.colorBr !== value) {
2049
+ this.colorBl = value;
2050
+ this.colorBr = value;
2051
+ }
2052
+ this.props.colorBottom = value;
2053
+ this.setUpdateType(UpdateType.PremultipliedColors);
2054
+ }
2055
+
2056
+ get colorLeft(): number {
2057
+ return this.props.colorLeft;
2058
+ }
2059
+
2060
+ set colorLeft(value: number) {
2061
+ if (this.props.colorTl !== value || this.props.colorBl !== value) {
2062
+ this.colorTl = value;
2063
+ this.colorBl = value;
2064
+ }
2065
+ this.props.colorLeft = value;
2066
+ this.setUpdateType(UpdateType.PremultipliedColors);
2067
+ }
2068
+
2069
+ get colorRight(): number {
2070
+ return this.props.colorRight;
2071
+ }
2072
+
2073
+ set colorRight(value: number) {
2074
+ if (this.props.colorTr !== value || this.props.colorBr !== value) {
2075
+ this.colorTr = value;
2076
+ this.colorBr = value;
2077
+ }
2078
+ this.props.colorRight = value;
2079
+ this.setUpdateType(UpdateType.PremultipliedColors);
2080
+ }
2081
+
2082
+ get colorTl(): number {
2083
+ return this.props.colorTl;
2084
+ }
2085
+
2086
+ set colorTl(value: number) {
2087
+ this.props.colorTl = value;
2088
+ this.setUpdateType(UpdateType.PremultipliedColors);
2089
+ }
2090
+
2091
+ get colorTr(): number {
2092
+ return this.props.colorTr;
2093
+ }
2094
+
2095
+ set colorTr(value: number) {
2096
+ this.props.colorTr = value;
2097
+ this.setUpdateType(UpdateType.PremultipliedColors);
2098
+ }
2099
+
2100
+ get colorBl(): number {
2101
+ return this.props.colorBl;
2102
+ }
2103
+
2104
+ set colorBl(value: number) {
2105
+ this.props.colorBl = value;
2106
+ this.setUpdateType(UpdateType.PremultipliedColors);
2107
+ }
2108
+
2109
+ get colorBr(): number {
2110
+ return this.props.colorBr;
2111
+ }
2112
+
2113
+ set colorBr(value: number) {
2114
+ this.props.colorBr = value;
2115
+ this.setUpdateType(UpdateType.PremultipliedColors);
2116
+ }
2117
+
2118
+ // we're only interested in parent zIndex to test
2119
+ // if we should use node zIndex is higher then parent zIndex
2120
+ get zIndexLocked(): number {
2121
+ return this.props.zIndexLocked || 0;
2122
+ }
2123
+
2124
+ set zIndexLocked(value: number) {
2125
+ this.props.zIndexLocked = value;
2126
+ this.setUpdateType(UpdateType.CalculatedZIndex | UpdateType.Children);
2127
+ for (let i = 0, length = this.children.length; i < length; i++) {
2128
+ this.children[i]!.setUpdateType(UpdateType.CalculatedZIndex);
2129
+ }
2130
+ }
2131
+
2132
+ get zIndex(): number {
2133
+ return this.props.zIndex;
2134
+ }
2135
+
2136
+ set zIndex(value: number) {
2137
+ this.props.zIndex = value;
2138
+ this.setUpdateType(UpdateType.CalculatedZIndex | UpdateType.Children);
2139
+ for (let i = 0, length = this.children.length; i < length; i++) {
2140
+ this.children[i]!.setUpdateType(UpdateType.CalculatedZIndex);
2141
+ }
2142
+ }
2143
+
2144
+ get parent(): CoreNode | null {
2145
+ return this.props.parent;
2146
+ }
2147
+
2148
+ set parent(newParent: CoreNode | null) {
2149
+ const oldParent = this.props.parent;
2150
+ if (oldParent === newParent) {
2151
+ return;
2152
+ }
2153
+ this.props.parent = newParent;
2154
+ if (oldParent) {
2155
+ const index = oldParent.children.indexOf(this);
2156
+ assertTruthy(
2157
+ index !== -1,
2158
+ "CoreNode.parent: Node not found in old parent's children!",
2159
+ );
2160
+ oldParent.children.splice(index, 1);
2161
+ oldParent.setUpdateType(
2162
+ UpdateType.Children | UpdateType.ZIndexSortedChildren,
2163
+ );
2164
+ }
2165
+ if (newParent) {
2166
+ newParent.children.push(this);
2167
+ // Since this node has a new parent, to be safe, have it do a full update.
2168
+ this.setUpdateType(UpdateType.All);
2169
+ // Tell parent that it's children need to be updated and sorted.
2170
+ newParent.setUpdateType(
2171
+ UpdateType.Children | UpdateType.ZIndexSortedChildren,
2172
+ );
2173
+
2174
+ // If the new parent has an RTT enabled, apply RTT inheritance
2175
+ if (newParent.rtt || newParent.parentHasRenderTexture) {
2176
+ this.applyRTTInheritance(newParent);
2177
+ }
2178
+ }
2179
+ this.updateScaleRotateTransform();
2180
+
2181
+ // fetch render bounds from parent
2182
+ this.setUpdateType(UpdateType.RenderBounds | UpdateType.Children);
2183
+ }
2184
+
2185
+ get preventCleanup(): boolean {
2186
+ return this.props.textureOptions.preventCleanup || false;
2187
+ }
2188
+
2189
+ set preventCleanup(value: boolean) {
2190
+ if (isProductionEnvironment() === false) {
2191
+ console.warn(
2192
+ 'CoreNode.preventCleanup: Is deprecated and will be removed in upcoming release, please use textureOptions.preventCleanup instead',
2193
+ );
2194
+ }
2195
+
2196
+ this.props.textureOptions.preventCleanup = value;
2197
+ }
2198
+
2199
+ get rtt(): boolean {
2200
+ return this.props.rtt;
2201
+ }
2202
+
2203
+ set rtt(value: boolean) {
2204
+ if (this.props.rtt === value) {
2205
+ return;
2206
+ }
2207
+ this.props.rtt = value;
2208
+
2209
+ if (value === true) {
2210
+ this.initRenderTexture();
2211
+ this.markChildrenWithRTT();
2212
+ } else {
2213
+ this.cleanupRenderTexture();
2214
+ }
2215
+
2216
+ this.setUpdateType(UpdateType.RenderTexture);
2217
+
2218
+ if (this.parentHasRenderTexture === true) {
2219
+ this.notifyParentRTTOfUpdate();
2220
+ }
2221
+ }
2222
+ private initRenderTexture() {
2223
+ this.texture = this.stage.txManager.createTexture('RenderTexture', {
2224
+ width: this.width,
2225
+ height: this.height,
2226
+ });
2227
+
2228
+ this.stage.renderer.renderToTexture(this);
2229
+ }
2230
+
2231
+ private cleanupRenderTexture() {
2232
+ this.unloadTexture();
2233
+ this.clearRTTInheritance();
2234
+
2235
+ this.hasRTTupdates = false;
2236
+ this.texture = null;
2237
+ }
2238
+
2239
+ private markChildrenWithRTT(node: CoreNode | null = null) {
2240
+ const parent = node || this;
2241
+
2242
+ for (const child of parent.children) {
2243
+ child.setUpdateType(UpdateType.All);
2244
+ child.parentHasRenderTexture = true;
2245
+ child.markChildrenWithRTT();
2246
+ }
2247
+ }
2248
+
2249
+ // Apply RTT inheritance when a node has an RTT-enabled parent
2250
+ private applyRTTInheritance(parent: CoreNode) {
2251
+ if (parent.rtt) {
2252
+ // Only the RTT node should be added to `renderToTexture`
2253
+ parent.setUpdateType(UpdateType.RenderTexture);
2254
+ }
2255
+
2256
+ // Propagate `parentHasRenderTexture` downwards
2257
+ this.markChildrenWithRTT(parent);
2258
+ }
2259
+
2260
+ // Clear RTT inheritance when detaching from an RTT chain
2261
+ private clearRTTInheritance() {
2262
+ // if this node is RTT itself stop the propagation important for nested RTT nodes
2263
+ // for the initial RTT node this is already handled in `set rtt`
2264
+ if (this.rtt) {
2265
+ return;
2266
+ }
2267
+
2268
+ for (const child of this.children) {
2269
+ // force child to update everything as the RTT inheritance has changed
2270
+ child.parentHasRenderTexture = false;
2271
+ child.rttParent = null;
2272
+ child.setUpdateType(UpdateType.All);
2273
+ child.clearRTTInheritance();
2274
+ }
2275
+ }
2276
+
2277
+ get shader(): BaseShaderController {
2278
+ return this.props.shader;
2279
+ }
2280
+
2281
+ set shader(value: BaseShaderController) {
2282
+ if (this.props.shader === value) {
2283
+ return;
2284
+ }
2285
+
2286
+ this.props.shader = value;
2287
+
2288
+ this.setUpdateType(UpdateType.IsRenderable);
2289
+ }
2290
+
2291
+ get src(): string | null {
2292
+ return this.props.src;
2293
+ }
2294
+
2295
+ set src(imageUrl: string | null) {
2296
+ if (this.props.src === imageUrl) {
2297
+ return;
2298
+ }
2299
+
2300
+ this.props.src = imageUrl;
2301
+
2302
+ if (!imageUrl) {
2303
+ this.texture = null;
2304
+ return;
2305
+ }
2306
+
2307
+ this.texture = this.stage.txManager.createTexture('ImageTexture', {
2308
+ src: imageUrl,
2309
+ width: this.props.width,
2310
+ height: this.props.height,
2311
+ type: this.props.imageType,
2312
+ sx: this.props.srcX,
2313
+ sy: this.props.srcY,
2314
+ sw: this.props.srcWidth,
2315
+ sh: this.props.srcHeight,
2316
+ });
2317
+ }
2318
+
2319
+ set imageType(type: 'regular' | 'compressed' | 'svg' | null) {
2320
+ if (this.props.imageType === type) {
2321
+ return;
2322
+ }
2323
+
2324
+ this.props.imageType = type;
2325
+ }
2326
+
2327
+ get imageType() {
2328
+ return this.props.imageType || null;
2329
+ }
2330
+
2331
+ get srcHeight(): number | undefined {
2332
+ return this.props.srcHeight;
2333
+ }
2334
+
2335
+ set srcHeight(value: number) {
2336
+ this.props.srcHeight = value;
2337
+ }
2338
+
2339
+ get srcWidth(): number | undefined {
2340
+ return this.props.srcWidth;
2341
+ }
2342
+
2343
+ set srcWidth(value: number) {
2344
+ this.props.srcWidth = value;
2345
+ }
2346
+
2347
+ get srcX(): number | undefined {
2348
+ return this.props.srcX;
2349
+ }
2350
+
2351
+ set srcX(value: number) {
2352
+ this.props.srcX = value;
2353
+ }
2354
+
2355
+ get srcY(): number | undefined {
2356
+ return this.props.srcY;
2357
+ }
2358
+
2359
+ set srcY(value: number) {
2360
+ this.props.srcY = value;
2361
+ }
2362
+
2363
+ /**
2364
+ * Returns the framebuffer dimensions of the node.
2365
+ * If the node has a render texture, the dimensions are the same as the node's dimensions.
2366
+ * If the node does not have a render texture, the dimensions are inherited from the parent.
2367
+ * If the node parent has a render texture and the node is a render texture, the nodes dimensions are used.
2368
+ */
2369
+ get framebufferDimensions(): Dimensions {
2370
+ if (this.parentHasRenderTexture && !this.rtt && this.parent) {
2371
+ return this.parent.framebufferDimensions;
2372
+ }
2373
+ return { width: this.width, height: this.height };
2374
+ }
2375
+
2376
+ /**
2377
+ * Returns the parent render texture node if it exists.
2378
+ */
2379
+ get parentRenderTexture(): CoreNode | null {
2380
+ let parent = this.parent;
2381
+ while (parent) {
2382
+ if (parent.rtt) {
2383
+ return parent;
2384
+ }
2385
+ parent = parent.parent;
2386
+ }
2387
+ return null;
2388
+ }
2389
+
2390
+ get texture(): Texture | null {
2391
+ return this.props.texture;
2392
+ }
2393
+
2394
+ set texture(value: Texture | null) {
2395
+ if (this.props.texture === value) {
2396
+ return;
2397
+ }
2398
+
2399
+ const oldTexture = this.props.texture;
2400
+ if (oldTexture) {
2401
+ oldTexture.setRenderableOwner(this, false);
2402
+ this.unloadTexture();
2403
+ }
2404
+
2405
+ this.props.texture = value;
2406
+ if (value !== null) {
2407
+ value.setRenderableOwner(this, this.isRenderable);
2408
+ this.loadTexture();
2409
+ }
2410
+
2411
+ this.setUpdateType(UpdateType.IsRenderable);
2412
+ }
2413
+
2414
+ set textureOptions(value: TextureOptions) {
2415
+ this.props.textureOptions = value;
2416
+ }
2417
+
2418
+ get textureOptions(): TextureOptions {
2419
+ return this.props.textureOptions;
2420
+ }
2421
+
2422
+ get strictBounds(): boolean {
2423
+ return this.props.strictBounds;
2424
+ }
2425
+
2426
+ set strictBounds(v) {
2427
+ if (v === this.props.strictBounds) {
2428
+ return;
2429
+ }
2430
+
2431
+ this.props.strictBounds = v;
2432
+ this.setUpdateType(UpdateType.RenderBounds | UpdateType.Children);
2433
+ this.childUpdateType |= UpdateType.RenderBounds | UpdateType.Children;
2434
+ }
2435
+
2436
+ animate(
2437
+ props: Partial<CoreNodeAnimateProps>,
2438
+ settings: Partial<AnimationSettings>,
2439
+ ): IAnimationController {
2440
+ const animation = new CoreAnimation(this, props, settings);
2441
+
2442
+ const controller = new CoreAnimationController(
2443
+ this.stage.animationManager,
2444
+ animation,
2445
+ );
2446
+
2447
+ return controller;
2448
+ }
2449
+
2450
+ flush() {
2451
+ // no-op
2452
+ }
2453
+
2454
+ //#endregion Properties
2455
+ }