@lightningjs/renderer 3.0.0-beta5 → 3.0.0-beta7

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