angular-three 0.0.7 → 1.0.0-beta.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (441) hide show
  1. package/esm2020/angular-three.mjs +1 -1
  2. package/esm2020/index.mjs +2 -35
  3. package/esm2020/lib/canvas.mjs +141 -128
  4. package/esm2020/lib/di/catalogue.mjs +7 -0
  5. package/esm2020/lib/directives/args.mjs +37 -17
  6. package/esm2020/lib/directives/repeat.mjs +5 -8
  7. package/esm2020/lib/events.mjs +341 -52
  8. package/esm2020/lib/loader.mjs +55 -0
  9. package/esm2020/lib/loop.mjs +137 -0
  10. package/esm2020/lib/renderer/di.mjs +3 -0
  11. package/esm2020/lib/renderer/enums.mjs +2 -0
  12. package/esm2020/lib/renderer/provider.mjs +19 -0
  13. package/esm2020/lib/renderer/renderer.mjs +293 -0
  14. package/esm2020/lib/renderer/state.mjs +350 -0
  15. package/esm2020/lib/renderer/utils.mjs +177 -0
  16. package/esm2020/lib/stores/rx-store.mjs +99 -0
  17. package/esm2020/lib/stores/store.mjs +190 -239
  18. package/esm2020/lib/types.mjs +1 -1
  19. package/esm2020/lib/utils/apply-props.mjs +37 -80
  20. package/esm2020/lib/utils/attach.mjs +29 -0
  21. package/esm2020/lib/utils/instance.mjs +43 -28
  22. package/esm2020/lib/utils/is.mjs +7 -11
  23. package/esm2020/lib/utils/make.mjs +30 -10
  24. package/esm2020/lib/utils/update.mjs +37 -0
  25. package/esm2020/lib/web/events.mjs +68 -0
  26. package/fesm2015/angular-three.mjs +2053 -2046
  27. package/fesm2015/angular-three.mjs.map +1 -1
  28. package/fesm2020/angular-three.mjs +2003 -1990
  29. package/fesm2020/angular-three.mjs.map +1 -1
  30. package/index.d.ts +1 -34
  31. package/lib/canvas.d.ts +29 -28
  32. package/lib/di/catalogue.d.ts +3 -0
  33. package/lib/directives/args.d.ts +8 -12
  34. package/lib/events.d.ts +7 -2
  35. package/lib/loader.d.ts +15 -0
  36. package/lib/{utils/loop.d.ts → loop.d.ts} +7 -6
  37. package/lib/renderer/di.d.ts +2 -0
  38. package/lib/renderer/enums.d.ts +25 -0
  39. package/lib/renderer/provider.d.ts +25 -0
  40. package/lib/renderer/renderer.d.ts +49 -0
  41. package/lib/renderer/state.d.ts +59 -0
  42. package/lib/renderer/utils.d.ts +20 -0
  43. package/lib/stores/rx-store.d.ts +17 -0
  44. package/lib/stores/store.d.ts +6 -23
  45. package/lib/types.d.ts +182 -191
  46. package/lib/utils/apply-props.d.ts +2 -6
  47. package/lib/utils/attach.d.ts +3 -0
  48. package/lib/utils/instance.d.ts +3 -3
  49. package/lib/utils/is.d.ts +6 -10
  50. package/lib/utils/make.d.ts +5 -2
  51. package/lib/utils/update.d.ts +4 -0
  52. package/lib/web/events.d.ts +4 -0
  53. package/package.json +30 -99
  54. package/attributes/README.md +0 -3
  55. package/attributes/index.d.ts +0 -22
  56. package/attributes/lib/buffer-attribute/buffer-attribute.d.ts +0 -8
  57. package/attributes/lib/color-attribute/color-attribute.d.ts +0 -8
  58. package/attributes/lib/common.d.ts +0 -1
  59. package/attributes/lib/float16-buffer-attribute/float16-buffer-attribute.d.ts +0 -8
  60. package/attributes/lib/float32-buffer-attribute/float32-buffer-attribute.d.ts +0 -8
  61. package/attributes/lib/float64-buffer-attribute/float64-buffer-attribute.d.ts +0 -8
  62. package/attributes/lib/fog-attribute/fog-attribute.d.ts +0 -8
  63. package/attributes/lib/fog-exp2-attribute/fog-exp2-attribute.d.ts +0 -8
  64. package/attributes/lib/instanced-buffer-attribute/instanced-buffer-attribute.d.ts +0 -8
  65. package/attributes/lib/int16-buffer-attribute/int16-buffer-attribute.d.ts +0 -8
  66. package/attributes/lib/int32-buffer-attribute/int32-buffer-attribute.d.ts +0 -8
  67. package/attributes/lib/int8-buffer-attribute/int8-buffer-attribute.d.ts +0 -8
  68. package/attributes/lib/interleaved-buffer-attribute/interleaved-buffer-attribute.d.ts +0 -8
  69. package/attributes/lib/matrix3-attribute/matrix3-attribute.d.ts +0 -8
  70. package/attributes/lib/matrix4-attribute/matrix4-attribute.d.ts +0 -8
  71. package/attributes/lib/uint16-buffer-attribute/uint16-buffer-attribute.d.ts +0 -8
  72. package/attributes/lib/uint32-buffer-attribute/uint32-buffer-attribute.d.ts +0 -8
  73. package/attributes/lib/uint8-buffer-attribute/uint8-buffer-attribute.d.ts +0 -8
  74. package/attributes/lib/uint8-clamped-buffer-attribute/uint8-clamped-buffer-attribute.d.ts +0 -8
  75. package/attributes/lib/value-attribute/value-attribute.d.ts +0 -8
  76. package/attributes/lib/vector2-attribute/vector2-attribute.d.ts +0 -8
  77. package/attributes/lib/vector3-attribute/vector3-attribute.d.ts +0 -8
  78. package/attributes/lib/vector4-attribute/vector4-attribute.d.ts +0 -8
  79. package/audios/README.md +0 -3
  80. package/audios/index.d.ts +0 -3
  81. package/audios/lib/audio/audio.d.ts +0 -49
  82. package/audios/lib/audio-listener/audio-listener.d.ts +0 -35
  83. package/audios/lib/common.d.ts +0 -3
  84. package/audios/lib/positional-audio/positional-audio.d.ts +0 -49
  85. package/cameras/README.md +0 -3
  86. package/cameras/index.d.ts +0 -5
  87. package/cameras/lib/array-camera/array-camera.d.ts +0 -56
  88. package/cameras/lib/common.d.ts +0 -3
  89. package/cameras/lib/cube-camera/cube-camera.d.ts +0 -36
  90. package/cameras/lib/orthographic-camera/orthographic-camera.d.ts +0 -54
  91. package/cameras/lib/perspective-camera/perspective-camera.d.ts +0 -55
  92. package/cameras/lib/stereo-camera/stereo-camera.d.ts +0 -42
  93. package/esm2020/attributes/angular-three-attributes.mjs +0 -5
  94. package/esm2020/attributes/index.mjs +0 -24
  95. package/esm2020/attributes/lib/buffer-attribute/buffer-attribute.mjs +0 -26
  96. package/esm2020/attributes/lib/color-attribute/color-attribute.mjs +0 -26
  97. package/esm2020/attributes/lib/common.mjs +0 -13
  98. package/esm2020/attributes/lib/float16-buffer-attribute/float16-buffer-attribute.mjs +0 -26
  99. package/esm2020/attributes/lib/float32-buffer-attribute/float32-buffer-attribute.mjs +0 -26
  100. package/esm2020/attributes/lib/float64-buffer-attribute/float64-buffer-attribute.mjs +0 -26
  101. package/esm2020/attributes/lib/fog-attribute/fog-attribute.mjs +0 -26
  102. package/esm2020/attributes/lib/fog-exp2-attribute/fog-exp2-attribute.mjs +0 -26
  103. package/esm2020/attributes/lib/instanced-buffer-attribute/instanced-buffer-attribute.mjs +0 -26
  104. package/esm2020/attributes/lib/int16-buffer-attribute/int16-buffer-attribute.mjs +0 -26
  105. package/esm2020/attributes/lib/int32-buffer-attribute/int32-buffer-attribute.mjs +0 -26
  106. package/esm2020/attributes/lib/int8-buffer-attribute/int8-buffer-attribute.mjs +0 -26
  107. package/esm2020/attributes/lib/interleaved-buffer-attribute/interleaved-buffer-attribute.mjs +0 -30
  108. package/esm2020/attributes/lib/matrix3-attribute/matrix3-attribute.mjs +0 -26
  109. package/esm2020/attributes/lib/matrix4-attribute/matrix4-attribute.mjs +0 -26
  110. package/esm2020/attributes/lib/uint16-buffer-attribute/uint16-buffer-attribute.mjs +0 -26
  111. package/esm2020/attributes/lib/uint32-buffer-attribute/uint32-buffer-attribute.mjs +0 -26
  112. package/esm2020/attributes/lib/uint8-buffer-attribute/uint8-buffer-attribute.mjs +0 -26
  113. package/esm2020/attributes/lib/uint8-clamped-buffer-attribute/uint8-clamped-buffer-attribute.mjs +0 -26
  114. package/esm2020/attributes/lib/value-attribute/value-attribute.mjs +0 -28
  115. package/esm2020/attributes/lib/vector2-attribute/vector2-attribute.mjs +0 -26
  116. package/esm2020/attributes/lib/vector3-attribute/vector3-attribute.mjs +0 -26
  117. package/esm2020/attributes/lib/vector4-attribute/vector4-attribute.mjs +0 -26
  118. package/esm2020/audios/angular-three-audios.mjs +0 -5
  119. package/esm2020/audios/index.mjs +0 -5
  120. package/esm2020/audios/lib/audio/audio.mjs +0 -45
  121. package/esm2020/audios/lib/audio-listener/audio-listener.mjs +0 -38
  122. package/esm2020/audios/lib/common.mjs +0 -54
  123. package/esm2020/audios/lib/positional-audio/positional-audio.mjs +0 -45
  124. package/esm2020/cameras/angular-three-cameras.mjs +0 -5
  125. package/esm2020/cameras/index.mjs +0 -7
  126. package/esm2020/cameras/lib/array-camera/array-camera.mjs +0 -44
  127. package/esm2020/cameras/lib/common.mjs +0 -54
  128. package/esm2020/cameras/lib/cube-camera/cube-camera.mjs +0 -30
  129. package/esm2020/cameras/lib/orthographic-camera/orthographic-camera.mjs +0 -42
  130. package/esm2020/cameras/lib/perspective-camera/perspective-camera.mjs +0 -43
  131. package/esm2020/cameras/lib/stereo-camera/stereo-camera.mjs +0 -38
  132. package/esm2020/geometries/angular-three-geometries.mjs +0 -5
  133. package/esm2020/geometries/index.mjs +0 -25
  134. package/esm2020/geometries/lib/box-geometry/box-geometry.mjs +0 -26
  135. package/esm2020/geometries/lib/buffer-geometry/buffer-geometry.mjs +0 -26
  136. package/esm2020/geometries/lib/capsule-geometry/capsule-geometry.mjs +0 -26
  137. package/esm2020/geometries/lib/circle-geometry/circle-geometry.mjs +0 -26
  138. package/esm2020/geometries/lib/common.mjs +0 -13
  139. package/esm2020/geometries/lib/cone-geometry/cone-geometry.mjs +0 -26
  140. package/esm2020/geometries/lib/cylinder-geometry/cylinder-geometry.mjs +0 -26
  141. package/esm2020/geometries/lib/dodecahedron-geometry/dodecahedron-geometry.mjs +0 -26
  142. package/esm2020/geometries/lib/edges-geometry/edges-geometry.mjs +0 -26
  143. package/esm2020/geometries/lib/extrude-geometry/extrude-geometry.mjs +0 -26
  144. package/esm2020/geometries/lib/icosahedron-geometry/icosahedron-geometry.mjs +0 -26
  145. package/esm2020/geometries/lib/instanced-buffer-geometry/instanced-buffer-geometry.mjs +0 -26
  146. package/esm2020/geometries/lib/lathe-geometry/lathe-geometry.mjs +0 -26
  147. package/esm2020/geometries/lib/octahedron-geometry/octahedron-geometry.mjs +0 -26
  148. package/esm2020/geometries/lib/plane-geometry/plane-geometry.mjs +0 -26
  149. package/esm2020/geometries/lib/polyhedron-geometry/polyhedron-geometry.mjs +0 -26
  150. package/esm2020/geometries/lib/ring-geometry/ring-geometry.mjs +0 -26
  151. package/esm2020/geometries/lib/shape-geometry/shape-geometry.mjs +0 -26
  152. package/esm2020/geometries/lib/sphere-geometry/sphere-geometry.mjs +0 -26
  153. package/esm2020/geometries/lib/tetrahedron-geometry/tetrahedron-geometry.mjs +0 -26
  154. package/esm2020/geometries/lib/torus-geometry/torus-geometry.mjs +0 -26
  155. package/esm2020/geometries/lib/torus-knot-geometry/torus-knot-geometry.mjs +0 -26
  156. package/esm2020/geometries/lib/tube-geometry/tube-geometry.mjs +0 -26
  157. package/esm2020/geometries/lib/wireframe-geometry/wireframe-geometry.mjs +0 -26
  158. package/esm2020/helpers/angular-three-helpers.mjs +0 -5
  159. package/esm2020/helpers/index.mjs +0 -15
  160. package/esm2020/helpers/lib/arrow-helper/arrow-helper.mjs +0 -26
  161. package/esm2020/helpers/lib/axes-helper/axes-helper.mjs +0 -26
  162. package/esm2020/helpers/lib/box-helper/box-helper.mjs +0 -36
  163. package/esm2020/helpers/lib/box3-helper/box3-helper.mjs +0 -36
  164. package/esm2020/helpers/lib/camera-helper/camera-helper.mjs +0 -36
  165. package/esm2020/helpers/lib/common.mjs +0 -13
  166. package/esm2020/helpers/lib/directional-light-helper/directional-light-helper.mjs +0 -36
  167. package/esm2020/helpers/lib/grid-helper/grid-helper.mjs +0 -26
  168. package/esm2020/helpers/lib/hemisphere-light-helper/hemisphere-light-helper.mjs +0 -36
  169. package/esm2020/helpers/lib/plane-helper/plane-helper.mjs +0 -36
  170. package/esm2020/helpers/lib/point-light-helper/point-light-helper.mjs +0 -36
  171. package/esm2020/helpers/lib/polar-grid-helper/polar-grid-helper.mjs +0 -26
  172. package/esm2020/helpers/lib/skeleton-helper/skeleton-helper.mjs +0 -36
  173. package/esm2020/helpers/lib/spot-light-helper/spot-light-helper.mjs +0 -36
  174. package/esm2020/lib/di/resize.mjs +0 -19
  175. package/esm2020/lib/di/window.mjs +0 -13
  176. package/esm2020/lib/directives/cursor.mjs +0 -35
  177. package/esm2020/lib/instance.mjs +0 -368
  178. package/esm2020/lib/pipes/math.mjs +0 -15
  179. package/esm2020/lib/pipes/pi.mjs +0 -14
  180. package/esm2020/lib/pipes/radian.mjs +0 -14
  181. package/esm2020/lib/pipes/side.mjs +0 -22
  182. package/esm2020/lib/ref.mjs +0 -15
  183. package/esm2020/lib/services/loader.mjs +0 -45
  184. package/esm2020/lib/services/resize.mjs +0 -127
  185. package/esm2020/lib/stores/component-store.mjs +0 -137
  186. package/esm2020/lib/utils/build-graph.mjs +0 -15
  187. package/esm2020/lib/utils/camera.mjs +0 -28
  188. package/esm2020/lib/utils/capitalize.mjs +0 -4
  189. package/esm2020/lib/utils/check-update.mjs +0 -22
  190. package/esm2020/lib/utils/events.mjs +0 -353
  191. package/esm2020/lib/utils/get-instance-local-state.mjs +0 -6
  192. package/esm2020/lib/utils/inject.mjs +0 -18
  193. package/esm2020/lib/utils/loop.mjs +0 -139
  194. package/esm2020/lib/utils/mutate.mjs +0 -24
  195. package/esm2020/lib/utils/proxy.mjs +0 -99
  196. package/esm2020/lib/utils/renderer.mjs +0 -15
  197. package/esm2020/lights/angular-three-lights.mjs +0 -5
  198. package/esm2020/lights/index.mjs +0 -11
  199. package/esm2020/lights/lib/ambient-light/ambient-light.mjs +0 -30
  200. package/esm2020/lights/lib/ambient-light-probe/ambient-light-probe.mjs +0 -30
  201. package/esm2020/lights/lib/common.mjs +0 -54
  202. package/esm2020/lights/lib/directional-light/directional-light.mjs +0 -30
  203. package/esm2020/lights/lib/hemisphere-light/hemisphere-light.mjs +0 -30
  204. package/esm2020/lights/lib/hemisphere-light-probe/hemisphere-light-probe.mjs +0 -30
  205. package/esm2020/lights/lib/light-probe/light-probe.mjs +0 -30
  206. package/esm2020/lights/lib/point-light/point-light.mjs +0 -30
  207. package/esm2020/lights/lib/rect-area-light/rect-area-light.mjs +0 -30
  208. package/esm2020/lights/lib/spot-light/spot-light.mjs +0 -42
  209. package/esm2020/materials/angular-three-materials.mjs +0 -5
  210. package/esm2020/materials/index.mjs +0 -19
  211. package/esm2020/materials/lib/common.mjs +0 -13
  212. package/esm2020/materials/lib/line-basic-material/line-basic-material.mjs +0 -77
  213. package/esm2020/materials/lib/line-dashed-material/line-dashed-material.mjs +0 -80
  214. package/esm2020/materials/lib/mesh-basic-material/mesh-basic-material.mjs +0 -89
  215. package/esm2020/materials/lib/mesh-depth-material/mesh-depth-material.mjs +0 -80
  216. package/esm2020/materials/lib/mesh-distance-material/mesh-distance-material.mjs +0 -80
  217. package/esm2020/materials/lib/mesh-lambert-material/mesh-lambert-material.mjs +0 -100
  218. package/esm2020/materials/lib/mesh-matcap-material/mesh-matcap-material.mjs +0 -86
  219. package/esm2020/materials/lib/mesh-normal-material/mesh-normal-material.mjs +0 -83
  220. package/esm2020/materials/lib/mesh-phong-material/mesh-phong-material.mjs +0 -103
  221. package/esm2020/materials/lib/mesh-physical-material/mesh-physical-material.mjs +0 -119
  222. package/esm2020/materials/lib/mesh-standard-material/mesh-standard-material.mjs +0 -100
  223. package/esm2020/materials/lib/mesh-toon-material/mesh-toon-material.mjs +0 -96
  224. package/esm2020/materials/lib/points-material/points-material.mjs +0 -78
  225. package/esm2020/materials/lib/raw-shader-material/raw-shader-material.mjs +0 -83
  226. package/esm2020/materials/lib/shader-material/shader-material.mjs +0 -83
  227. package/esm2020/materials/lib/shadow-material/shadow-material.mjs +0 -74
  228. package/esm2020/materials/lib/sprite-material/sprite-material.mjs +0 -78
  229. package/esm2020/objects/angular-three-objects.mjs +0 -5
  230. package/esm2020/objects/index.mjs +0 -14
  231. package/esm2020/objects/lib/bone/bone.mjs +0 -27
  232. package/esm2020/objects/lib/common.mjs +0 -54
  233. package/esm2020/objects/lib/group/group.mjs +0 -27
  234. package/esm2020/objects/lib/instanced-mesh/instanced-mesh.mjs +0 -40
  235. package/esm2020/objects/lib/line/line.mjs +0 -30
  236. package/esm2020/objects/lib/line-loop/line-loop.mjs +0 -30
  237. package/esm2020/objects/lib/line-segments/line-segments.mjs +0 -30
  238. package/esm2020/objects/lib/lod/lod.mjs +0 -30
  239. package/esm2020/objects/lib/mesh/mesh.mjs +0 -30
  240. package/esm2020/objects/lib/points/points.mjs +0 -30
  241. package/esm2020/objects/lib/skeleton/skeleton.mjs +0 -41
  242. package/esm2020/objects/lib/skinned-mesh/skinned-mesh.mjs +0 -39
  243. package/esm2020/objects/lib/sprite/sprite.mjs +0 -30
  244. package/esm2020/primitives/angular-three-primitives.mjs +0 -5
  245. package/esm2020/primitives/index.mjs +0 -3
  246. package/esm2020/primitives/lib/common.mjs +0 -54
  247. package/esm2020/primitives/lib/object-primitive/object-primitive.mjs +0 -27
  248. package/esm2020/primitives/lib/primitive/primitive.mjs +0 -38
  249. package/esm2020/stats/angular-three-stats.mjs +0 -5
  250. package/esm2020/stats/index.mjs +0 -2
  251. package/esm2020/stats/lib/stats/stats.mjs +0 -54
  252. package/esm2020/textures/angular-three-textures.mjs +0 -5
  253. package/esm2020/textures/index.mjs +0 -12
  254. package/esm2020/textures/lib/canvas-texture/canvas-texture.mjs +0 -26
  255. package/esm2020/textures/lib/common.mjs +0 -13
  256. package/esm2020/textures/lib/compressed-array-texture/compressed-array-texture.mjs +0 -26
  257. package/esm2020/textures/lib/compressed-texture/compressed-texture.mjs +0 -26
  258. package/esm2020/textures/lib/cube-texture/cube-texture.mjs +0 -26
  259. package/esm2020/textures/lib/data-array-texture/data-array-texture.mjs +0 -26
  260. package/esm2020/textures/lib/data-texture/data-texture.mjs +0 -26
  261. package/esm2020/textures/lib/data3-dtexture/data3-dtexture.mjs +0 -26
  262. package/esm2020/textures/lib/depth-texture/depth-texture.mjs +0 -26
  263. package/esm2020/textures/lib/framebuffer-texture/framebuffer-texture.mjs +0 -26
  264. package/esm2020/textures/lib/video-texture/video-texture.mjs +0 -26
  265. package/fesm2015/angular-three-attributes.mjs +0 -475
  266. package/fesm2015/angular-three-attributes.mjs.map +0 -1
  267. package/fesm2015/angular-three-audios.mjs +0 -179
  268. package/fesm2015/angular-three-audios.mjs.map +0 -1
  269. package/fesm2015/angular-three-cameras.mjs +0 -236
  270. package/fesm2015/angular-three-cameras.mjs.map +0 -1
  271. package/fesm2015/angular-three-geometries.mjs +0 -488
  272. package/fesm2015/angular-three-geometries.mjs.map +0 -1
  273. package/fesm2015/angular-three-helpers.mjs +0 -378
  274. package/fesm2015/angular-three-helpers.mjs.map +0 -1
  275. package/fesm2015/angular-three-lights.mjs +0 -297
  276. package/fesm2015/angular-three-lights.mjs.map +0 -1
  277. package/fesm2015/angular-three-materials.mjs +0 -1415
  278. package/fesm2015/angular-three-materials.mjs.map +0 -1
  279. package/fesm2015/angular-three-objects.mjs +0 -381
  280. package/fesm2015/angular-three-objects.mjs.map +0 -1
  281. package/fesm2015/angular-three-primitives.mjs +0 -120
  282. package/fesm2015/angular-three-primitives.mjs.map +0 -1
  283. package/fesm2015/angular-three-stats.mjs +0 -62
  284. package/fesm2015/angular-three-stats.mjs.map +0 -1
  285. package/fesm2015/angular-three-textures.mjs +0 -228
  286. package/fesm2015/angular-three-textures.mjs.map +0 -1
  287. package/fesm2020/angular-three-attributes.mjs +0 -475
  288. package/fesm2020/angular-three-attributes.mjs.map +0 -1
  289. package/fesm2020/angular-three-audios.mjs +0 -179
  290. package/fesm2020/angular-three-audios.mjs.map +0 -1
  291. package/fesm2020/angular-three-cameras.mjs +0 -236
  292. package/fesm2020/angular-three-cameras.mjs.map +0 -1
  293. package/fesm2020/angular-three-geometries.mjs +0 -488
  294. package/fesm2020/angular-three-geometries.mjs.map +0 -1
  295. package/fesm2020/angular-three-helpers.mjs +0 -378
  296. package/fesm2020/angular-three-helpers.mjs.map +0 -1
  297. package/fesm2020/angular-three-lights.mjs +0 -297
  298. package/fesm2020/angular-three-lights.mjs.map +0 -1
  299. package/fesm2020/angular-three-materials.mjs +0 -1415
  300. package/fesm2020/angular-three-materials.mjs.map +0 -1
  301. package/fesm2020/angular-three-objects.mjs +0 -381
  302. package/fesm2020/angular-three-objects.mjs.map +0 -1
  303. package/fesm2020/angular-three-primitives.mjs +0 -120
  304. package/fesm2020/angular-three-primitives.mjs.map +0 -1
  305. package/fesm2020/angular-three-stats.mjs +0 -62
  306. package/fesm2020/angular-three-stats.mjs.map +0 -1
  307. package/fesm2020/angular-three-textures.mjs +0 -228
  308. package/fesm2020/angular-three-textures.mjs.map +0 -1
  309. package/geometries/README.md +0 -3
  310. package/geometries/index.d.ts +0 -23
  311. package/geometries/lib/box-geometry/box-geometry.d.ts +0 -8
  312. package/geometries/lib/buffer-geometry/buffer-geometry.d.ts +0 -8
  313. package/geometries/lib/capsule-geometry/capsule-geometry.d.ts +0 -8
  314. package/geometries/lib/circle-geometry/circle-geometry.d.ts +0 -8
  315. package/geometries/lib/common.d.ts +0 -1
  316. package/geometries/lib/cone-geometry/cone-geometry.d.ts +0 -8
  317. package/geometries/lib/cylinder-geometry/cylinder-geometry.d.ts +0 -8
  318. package/geometries/lib/dodecahedron-geometry/dodecahedron-geometry.d.ts +0 -8
  319. package/geometries/lib/edges-geometry/edges-geometry.d.ts +0 -8
  320. package/geometries/lib/extrude-geometry/extrude-geometry.d.ts +0 -8
  321. package/geometries/lib/icosahedron-geometry/icosahedron-geometry.d.ts +0 -8
  322. package/geometries/lib/instanced-buffer-geometry/instanced-buffer-geometry.d.ts +0 -8
  323. package/geometries/lib/lathe-geometry/lathe-geometry.d.ts +0 -8
  324. package/geometries/lib/octahedron-geometry/octahedron-geometry.d.ts +0 -8
  325. package/geometries/lib/plane-geometry/plane-geometry.d.ts +0 -8
  326. package/geometries/lib/polyhedron-geometry/polyhedron-geometry.d.ts +0 -8
  327. package/geometries/lib/ring-geometry/ring-geometry.d.ts +0 -8
  328. package/geometries/lib/shape-geometry/shape-geometry.d.ts +0 -8
  329. package/geometries/lib/sphere-geometry/sphere-geometry.d.ts +0 -8
  330. package/geometries/lib/tetrahedron-geometry/tetrahedron-geometry.d.ts +0 -8
  331. package/geometries/lib/torus-geometry/torus-geometry.d.ts +0 -8
  332. package/geometries/lib/torus-knot-geometry/torus-knot-geometry.d.ts +0 -8
  333. package/geometries/lib/tube-geometry/tube-geometry.d.ts +0 -8
  334. package/geometries/lib/wireframe-geometry/wireframe-geometry.d.ts +0 -8
  335. package/helpers/README.md +0 -3
  336. package/helpers/index.d.ts +0 -13
  337. package/helpers/lib/arrow-helper/arrow-helper.d.ts +0 -8
  338. package/helpers/lib/axes-helper/axes-helper.d.ts +0 -8
  339. package/helpers/lib/box-helper/box-helper.d.ts +0 -8
  340. package/helpers/lib/box3-helper/box3-helper.d.ts +0 -8
  341. package/helpers/lib/camera-helper/camera-helper.d.ts +0 -8
  342. package/helpers/lib/common.d.ts +0 -1
  343. package/helpers/lib/directional-light-helper/directional-light-helper.d.ts +0 -8
  344. package/helpers/lib/grid-helper/grid-helper.d.ts +0 -8
  345. package/helpers/lib/hemisphere-light-helper/hemisphere-light-helper.d.ts +0 -8
  346. package/helpers/lib/plane-helper/plane-helper.d.ts +0 -8
  347. package/helpers/lib/point-light-helper/point-light-helper.d.ts +0 -8
  348. package/helpers/lib/polar-grid-helper/polar-grid-helper.d.ts +0 -8
  349. package/helpers/lib/skeleton-helper/skeleton-helper.d.ts +0 -8
  350. package/helpers/lib/spot-light-helper/spot-light-helper.d.ts +0 -8
  351. package/lib/di/resize.d.ts +0 -12
  352. package/lib/di/window.d.ts +0 -1
  353. package/lib/directives/cursor.d.ts +0 -7
  354. package/lib/instance.d.ts +0 -81
  355. package/lib/pipes/math.d.ts +0 -7
  356. package/lib/pipes/pi.d.ts +0 -7
  357. package/lib/pipes/radian.d.ts +0 -7
  358. package/lib/pipes/side.d.ts +0 -8
  359. package/lib/ref.d.ts +0 -5
  360. package/lib/services/loader.d.ts +0 -11
  361. package/lib/services/resize.d.ts +0 -19
  362. package/lib/stores/component-store.d.ts +0 -69
  363. package/lib/utils/build-graph.d.ts +0 -3
  364. package/lib/utils/camera.d.ts +0 -4
  365. package/lib/utils/capitalize.d.ts +0 -1
  366. package/lib/utils/check-update.d.ts +0 -2
  367. package/lib/utils/events.d.ts +0 -6
  368. package/lib/utils/get-instance-local-state.d.ts +0 -2
  369. package/lib/utils/inject.d.ts +0 -9
  370. package/lib/utils/mutate.d.ts +0 -2
  371. package/lib/utils/proxy.d.ts +0 -7
  372. package/lib/utils/renderer.d.ts +0 -3
  373. package/lights/README.md +0 -3
  374. package/lights/index.d.ts +0 -9
  375. package/lights/lib/ambient-light/ambient-light.d.ts +0 -39
  376. package/lights/lib/ambient-light-probe/ambient-light-probe.d.ts +0 -40
  377. package/lights/lib/common.d.ts +0 -3
  378. package/lights/lib/directional-light/directional-light.d.ts +0 -40
  379. package/lights/lib/hemisphere-light/hemisphere-light.d.ts +0 -41
  380. package/lights/lib/hemisphere-light-probe/hemisphere-light-probe.d.ts +0 -42
  381. package/lights/lib/light-probe/light-probe.d.ts +0 -40
  382. package/lights/lib/point-light/point-light.d.ts +0 -42
  383. package/lights/lib/rect-area-light/rect-area-light.d.ts +0 -42
  384. package/lights/lib/spot-light/spot-light.d.ts +0 -45
  385. package/materials/README.md +0 -3
  386. package/materials/index.d.ts +0 -17
  387. package/materials/lib/common.d.ts +0 -1
  388. package/materials/lib/line-basic-material/line-basic-material.d.ts +0 -55
  389. package/materials/lib/line-dashed-material/line-dashed-material.d.ts +0 -58
  390. package/materials/lib/mesh-basic-material/mesh-basic-material.d.ts +0 -67
  391. package/materials/lib/mesh-depth-material/mesh-depth-material.d.ts +0 -58
  392. package/materials/lib/mesh-distance-material/mesh-distance-material.d.ts +0 -58
  393. package/materials/lib/mesh-lambert-material/mesh-lambert-material.d.ts +0 -78
  394. package/materials/lib/mesh-matcap-material/mesh-matcap-material.d.ts +0 -64
  395. package/materials/lib/mesh-normal-material/mesh-normal-material.d.ts +0 -61
  396. package/materials/lib/mesh-phong-material/mesh-phong-material.d.ts +0 -81
  397. package/materials/lib/mesh-physical-material/mesh-physical-material.d.ts +0 -97
  398. package/materials/lib/mesh-standard-material/mesh-standard-material.d.ts +0 -78
  399. package/materials/lib/mesh-toon-material/mesh-toon-material.d.ts +0 -74
  400. package/materials/lib/points-material/points-material.d.ts +0 -56
  401. package/materials/lib/raw-shader-material/raw-shader-material.d.ts +0 -68
  402. package/materials/lib/shader-material/shader-material.d.ts +0 -68
  403. package/materials/lib/shadow-material/shadow-material.d.ts +0 -52
  404. package/materials/lib/sprite-material/sprite-material.d.ts +0 -56
  405. package/objects/README.md +0 -3
  406. package/objects/index.d.ts +0 -12
  407. package/objects/lib/bone/bone.d.ts +0 -35
  408. package/objects/lib/common.d.ts +0 -3
  409. package/objects/lib/group/group.d.ts +0 -35
  410. package/objects/lib/instanced-mesh/instanced-mesh.d.ts +0 -44
  411. package/objects/lib/line/line.d.ts +0 -41
  412. package/objects/lib/line-loop/line-loop.d.ts +0 -41
  413. package/objects/lib/line-segments/line-segments.d.ts +0 -41
  414. package/objects/lib/lod/lod.d.ts +0 -40
  415. package/objects/lib/mesh/mesh.d.ts +0 -41
  416. package/objects/lib/points/points.d.ts +0 -41
  417. package/objects/lib/skeleton/skeleton.d.ts +0 -16
  418. package/objects/lib/skinned-mesh/skinned-mesh.d.ts +0 -45
  419. package/objects/lib/sprite/sprite.d.ts +0 -38
  420. package/primitives/README.md +0 -3
  421. package/primitives/index.d.ts +0 -2
  422. package/primitives/lib/common.d.ts +0 -3
  423. package/primitives/lib/object-primitive/object-primitive.d.ts +0 -35
  424. package/primitives/lib/primitive/primitive.d.ts +0 -10
  425. package/schematics/README.md +0 -11
  426. package/stats/README.md +0 -3
  427. package/stats/index.d.ts +0 -1
  428. package/stats/lib/stats/stats.d.ts +0 -16
  429. package/textures/README.md +0 -3
  430. package/textures/index.d.ts +0 -10
  431. package/textures/lib/canvas-texture/canvas-texture.d.ts +0 -8
  432. package/textures/lib/common.d.ts +0 -1
  433. package/textures/lib/compressed-array-texture/compressed-array-texture.d.ts +0 -8
  434. package/textures/lib/compressed-texture/compressed-texture.d.ts +0 -8
  435. package/textures/lib/cube-texture/cube-texture.d.ts +0 -8
  436. package/textures/lib/data-array-texture/data-array-texture.d.ts +0 -8
  437. package/textures/lib/data-texture/data-texture.d.ts +0 -8
  438. package/textures/lib/data3-dtexture/data3-dtexture.d.ts +0 -8
  439. package/textures/lib/depth-texture/depth-texture.d.ts +0 -8
  440. package/textures/lib/framebuffer-texture/framebuffer-texture.d.ts +0 -8
  441. package/textures/lib/video-texture/video-texture.d.ts +0 -8
@@ -1,477 +1,161 @@
1
- import { DOCUMENT, NgIf, NgTemplateOutlet, AsyncPipe, NgForOf } from '@angular/common';
2
1
  import * as i0 from '@angular/core';
3
- import { Injectable, InjectionToken, inject, NgZone, EventEmitter, Directive, Input, Output, ElementRef, Component, TemplateRef, ChangeDetectionStrategy, HostBinding, ViewChild, ContentChild, ViewContainerRef, Injector, Optional, Self, Inject, Pipe } from '@angular/core';
2
+ import { ElementRef, Injectable, inject, InjectionToken, ViewContainerRef, TemplateRef, Directive, Input, EventEmitter, getDebugNode, ChangeDetectorRef, RendererFactory2, Component, Output, EnvironmentInjector, createEnvironmentInjector, HostBinding, ViewChild } from '@angular/core';
3
+ import { injectNgxResize, provideNgxResizeOptions } from 'ngx-resize';
4
+ import { BehaviorSubject, startWith, tap, isObservable, of, map, from, retry, catchError, share, ReplaySubject, switchMap, forkJoin, take, filter } from 'rxjs';
5
+ import { DOCUMENT, NgForOf } from '@angular/common';
6
+ import { RxState, selectSlice } from '@rx-angular/state';
4
7
  import * as THREE from 'three';
5
- import { BehaviorSubject, filter, noop, tap, isObservable, combineLatest, Observable, Subject, pipe, debounceTime, takeUntil, fromEvent, share, ReplaySubject, from, retry, catchError, of, forkJoin, map, merge } from 'rxjs';
6
- import { ComponentStore } from '@ngrx/component-store';
8
+ import { ɵDomRendererFactory2 } from '@angular/platform-browser';
7
9
 
8
- function getInstanceLocalState(obj) {
9
- if (!obj)
10
- return undefined;
11
- return obj['__ngt__'];
12
- }
13
-
14
- const idCache = {};
15
- function makeId(event) {
16
- if (event) {
17
- return (event.eventObject || event.object).uuid + '/' + event.index + event.instanceId;
18
- }
19
- const newId = THREE.MathUtils.generateUUID();
20
- // ensure not already used
21
- if (!idCache[newId]) {
22
- idCache[newId] = true;
23
- return newId;
24
- }
25
- return makeId();
26
- }
27
- function makeDpr(dpr, window) {
28
- const target = window?.devicePixelRatio || 1;
29
- return Array.isArray(dpr) ? Math.min(Math.max(dpr[0], target), dpr[1]) : dpr;
30
- }
31
- function make(type, input) {
32
- if (!input)
33
- return new type();
34
- if (input instanceof type) {
35
- return input;
36
- }
37
- if (!Array.isArray(input)) {
38
- input = typeof input === 'number' ? [input, input, input, input] : [input];
39
- }
40
- return new type(...input);
10
+ function createSubs(callback, subs) {
11
+ const sub = { callback };
12
+ subs.add(sub);
13
+ return () => void subs.delete(sub);
41
14
  }
42
-
15
+ const globalEffects = new Set();
16
+ const globalAfterEffects = new Set();
17
+ const globalTailEffects = new Set();
43
18
  /**
44
- * Release pointer captures.
45
- * This is called by releasePointerCapture in the API, and when an object is removed.
19
+ * Adds a global render callback which is called each frame.
20
+ * @see https://docs.pmnd.rs/react-three-fiber/api/additional-exports#addEffect
46
21
  */
47
- function releaseInternalPointerCapture(capturedMap, obj, captures, pointerId) {
48
- const captureData = captures.get(obj);
49
- if (captureData) {
50
- captures.delete(obj);
51
- // If this was the last capturing object for this pointer
52
- if (captures.size === 0) {
53
- capturedMap.delete(pointerId);
54
- captureData.target.releasePointerCapture(pointerId);
55
- }
22
+ const addEffect = (callback) => createSubs(callback, globalEffects);
23
+ /**
24
+ * Adds a global after-render callback which is called each frame.
25
+ * @see https://docs.pmnd.rs/react-three-fiber/api/additional-exports#addAfterEffect
26
+ */
27
+ const addAfterEffect = (callback) => createSubs(callback, globalAfterEffects);
28
+ /**
29
+ * Adds a global callback which is called when rendering stops.
30
+ * @see https://docs.pmnd.rs/react-three-fiber/api/additional-exports#addTail
31
+ */
32
+ const addTail = (callback) => createSubs(callback, globalTailEffects);
33
+ function run(effects, timestamp) {
34
+ if (!effects.size)
35
+ return;
36
+ for (const { callback } of effects.values()) {
37
+ callback(timestamp);
56
38
  }
57
39
  }
58
- function removeInteractivity(stateFactory, object) {
59
- const { internal } = stateFactory();
60
- // Removes every trace of an object from the data store
61
- internal.interaction = internal.interaction.filter((o) => o !== object);
62
- internal.initialHits = internal.initialHits.filter((o) => o !== object);
63
- internal.hovered.forEach((value, key) => {
64
- if (value.eventObject === object || value.object === object) {
65
- // Clear out intersects, they are outdated by now
66
- internal.hovered.delete(key);
67
- }
68
- });
69
- internal.capturedMap.forEach((captures, pointerId) => {
70
- releaseInternalPointerCapture(internal.capturedMap, object, captures, pointerId);
71
- });
72
- }
73
- function createEvents(stateFactory) {
74
- /** Calculates delta */
75
- function calculateDistance(event) {
76
- const { internal } = stateFactory();
77
- const dx = event.offsetX - internal.initialClick[0];
78
- const dy = event.offsetY - internal.initialClick[1];
79
- return Math.round(Math.sqrt(dx * dx + dy * dy));
40
+ function flushGlobalEffects(type, timestamp) {
41
+ switch (type) {
42
+ case 'before':
43
+ return run(globalEffects, timestamp);
44
+ case 'after':
45
+ return run(globalAfterEffects, timestamp);
46
+ case 'tail':
47
+ return run(globalTailEffects, timestamp);
80
48
  }
81
- /** Returns true if an instance has a valid pointer-event registered, this excludes scroll, clicks etc */
82
- function filterPointerEvents(objects) {
83
- return objects.filter((obj) => ['move', 'over', 'enter', 'out', 'leave'].some((name) => {
84
- const eventName = ('pointer' + name);
85
- return getInstanceLocalState(obj)?.handlers[eventName];
86
- }));
49
+ }
50
+ function render(timestamp, store, frame) {
51
+ const state = store.get();
52
+ // Run local effects
53
+ let delta = state.clock.getDelta();
54
+ // In frameloop='never' mode, clock times are updated using the provided timestamp
55
+ if (state.frameloop === 'never' && typeof timestamp === 'number') {
56
+ delta = timestamp - state.clock.elapsedTime;
57
+ state.clock.oldTime = state.clock.elapsedTime;
58
+ state.clock.elapsedTime = timestamp;
87
59
  }
88
- function intersect(event, filter) {
89
- const state = stateFactory();
90
- const duplicates = new Set();
91
- const intersections = [];
92
- // Allow callers to eliminate event objects
93
- const eventsObjects = filter ? filter(state.internal.interaction) : state.internal.interaction;
94
- // Reset all raycaster cameras to undefined
95
- for (let i = 0; i < eventsObjects.length; i++) {
96
- const instanceState = getInstanceLocalState(eventsObjects[i])?.stateFactory();
97
- if (instanceState) {
98
- instanceState.raycaster.camera = undefined;
99
- }
100
- }
101
- if (!state.previousStateFactory) {
102
- // Make sure root-level pointer and ray are set up
103
- state.events.compute?.(event, () => state);
104
- }
105
- function handleRaycast(obj) {
106
- const state = getInstanceLocalState(obj)?.stateFactory();
107
- // Skip event handling when noEvents is set, or when the raycasters camera is null
108
- if (!state || !state.events.enabled || state.raycaster.camera === null)
109
- return [];
110
- // When the camera is undefined we have to call the event layers update function
111
- if (state.raycaster.camera === undefined) {
112
- state.events.compute?.(event, () => state, state.previousStateFactory);
113
- // If the camera is still undefined we have to skip this layer entirely
114
- if (state.raycaster.camera === undefined)
115
- state.raycaster.camera = null;
116
- }
117
- // Intersect object by object
118
- return state.raycaster.camera ? state.raycaster.intersectObject(obj, true) : [];
119
- }
120
- // Collect events
121
- let hits = eventsObjects
122
- // Intersect objects
123
- .flatMap(handleRaycast)
124
- // Sort by event priority and distance
125
- .sort((a, b) => {
126
- const aState = getInstanceLocalState(a.object)?.stateFactory();
127
- const bState = getInstanceLocalState(b.object)?.stateFactory();
128
- if (!aState || !bState)
129
- return 0;
130
- return bState.events.priority - aState.events.priority || a.distance - b.distance;
131
- })
132
- // Filter out duplicates
133
- .filter((item) => {
134
- const id = makeId(item);
135
- if (duplicates.has(id))
136
- return false;
137
- duplicates.add(id);
138
- return true;
139
- });
140
- // https://github.com/mrdoob/three.js/issues/16031
141
- // Allow custom userland intersect sort order, this likely only makes sense on the root filter
142
- if (state.events.filter)
143
- hits = state.events.filter(hits, () => state);
144
- // Bubble up the events, find the event source (eventObject)
145
- for (const hit of hits) {
146
- let eventObject = hit.object;
147
- // Bubble event up
148
- while (eventObject) {
149
- if (getInstanceLocalState(eventObject)?.eventCount)
150
- intersections.push({ ...hit, eventObject });
151
- eventObject = eventObject.parent;
152
- }
153
- }
154
- // If the interaction is captured, make all capturing targets part of the intersect.
155
- if ('pointerId' in event && state.internal.capturedMap.has(event.pointerId)) {
156
- for (const captureData of state.internal.capturedMap.get(event.pointerId).values()) {
157
- intersections.push(captureData.intersection);
158
- }
159
- }
160
- return intersections;
60
+ // Call subscribers (useFrame)
61
+ // subscribers = state.internal.subscribers;
62
+ for (let i = 0; i < state.internal.subscribers.length; i++) {
63
+ const subscriber = state.internal.subscribers[i];
64
+ subscriber.callback({ ...state, delta, frame });
161
65
  }
162
- /** Handles intersections by forwarding them to handlers */
163
- function handleIntersects(intersections, event, delta, callback) {
164
- const rootState = stateFactory();
165
- // If anything has been found, forward it to the event listeners
166
- if (intersections.length) {
167
- const localState = { stopped: false };
168
- for (const hit of intersections) {
169
- const state = getInstanceLocalState(hit.object)?.stateFactory() || rootState;
170
- const { raycaster, pointer, camera, internal } = state;
171
- const unprojectedPoint = new THREE.Vector3(pointer.x, pointer.y, 0).unproject(camera);
172
- const hasPointerCapture = (id) => internal.capturedMap.get(id)?.has(hit.eventObject) ?? false;
173
- const setPointerCapture = (id) => {
174
- const captureData = {
175
- intersection: hit,
176
- target: event.target,
177
- };
178
- if (internal.capturedMap.has(id)) {
179
- // if the pointerId was previously captured, we add the hit to the
180
- // event capturedMap.
181
- internal.capturedMap.get(id).set(hit.eventObject, captureData);
182
- }
183
- else {
184
- // if the pointerId was not previously captured, we create a map
185
- // containing the hitObject, and the hit. hitObject is used for
186
- // faster access.
187
- internal.capturedMap.set(id, new Map([[hit.eventObject, captureData]]));
188
- }
189
- // Call the original event now
190
- event.target.setPointerCapture(id);
191
- };
192
- const releasePointerCapture = (id) => {
193
- const captures = internal.capturedMap.get(id);
194
- if (captures) {
195
- releaseInternalPointerCapture(internal.capturedMap, hit.eventObject, captures, id);
196
- }
197
- };
198
- // Add native event props
199
- const extractEventProps = {};
200
- // This iterates over the event's properties including the inherited ones. Native PointerEvents have most of their props as getters which are inherited, but polyfilled PointerEvents have them all as their own properties (i.e. not inherited). We can't use Object.keys() or Object.entries() as they only return "own" properties; nor Object.getPrototypeOf(event) as that *doesn't* return "own" properties, only inherited ones.
201
- for (const prop in event) {
202
- const property = event[prop];
203
- // Only copy over atomics, leave functions alone as these should be
204
- // called as event.nativeEvent.fn()
205
- if (typeof property !== 'function')
206
- extractEventProps[prop] = property;
207
- }
208
- const raycastEvent = {
209
- ...hit,
210
- ...extractEventProps,
211
- pointer,
212
- intersections,
213
- stopped: localState.stopped,
214
- delta,
215
- unprojectedPoint,
216
- ray: raycaster.ray,
217
- camera: camera,
218
- // Hijack stopPropagation, which just sets a flag
219
- stopPropagation() {
220
- // https://github.com/pmndrs/react-three-fiber/issues/596
221
- // Events are not allowed to stop propagation if the pointer has been captured
222
- const capturesForPointer = 'pointerId' in event && internal.capturedMap.get(event.pointerId);
223
- // We only authorize stopPropagation...
224
- if (
225
- // ...if this pointer hasn't been captured
226
- !capturesForPointer ||
227
- // ... or if the hit object is capturing the pointer
228
- capturesForPointer.has(hit.eventObject)) {
229
- raycastEvent.stopped = localState.stopped = true;
230
- // Propagation is stopped, remove all other hover records
231
- // An event handler is only allowed to flush other handlers if it is hovered itself
232
- if (internal.hovered.size &&
233
- Array.from(internal.hovered.values()).find((i) => i.eventObject === hit.eventObject)) {
234
- // Objects cannot flush out higher up objects that have already caught the event
235
- const higher = intersections.slice(0, intersections.indexOf(hit));
236
- cancelPointer([...higher, hit]);
237
- }
238
- }
239
- },
240
- // there should be a distinction between target and currentTarget
241
- target: {
242
- hasPointerCapture,
243
- setPointerCapture,
244
- releasePointerCapture,
245
- },
246
- currentTarget: {
247
- hasPointerCapture,
248
- setPointerCapture,
249
- releasePointerCapture,
250
- },
251
- nativeEvent: event,
252
- };
253
- // Call subscribers
254
- callback(raycastEvent);
255
- // Event bubbling may be interrupted by stopPropagation
256
- if (localState.stopped === true)
257
- break;
258
- }
259
- }
260
- return intersections;
66
+ // Render content
67
+ if (!state.internal.priority && state.gl.render) {
68
+ state.gl.render(state.scene, state.camera);
261
69
  }
262
- function cancelPointer(intersections) {
263
- const { internal } = stateFactory();
264
- for (const hoveredObj of internal.hovered.values()) {
265
- // When no objects were hit or the hovered object wasn't found underneath the cursor
266
- // we call onPointerOut and delete the object from the hovered-elements map
267
- if (!intersections.length ||
268
- !intersections.find((hit) => hit.object === hoveredObj.object &&
269
- hit.index === hoveredObj.index &&
270
- hit.instanceId === hoveredObj.instanceId)) {
271
- const eventObject = hoveredObj.eventObject;
272
- const instance = getInstanceLocalState(eventObject);
273
- const handlers = instance?.handlers;
274
- internal.hovered.delete(makeId(hoveredObj));
275
- if (instance?.eventCount) {
276
- // Clear out intersects, they are outdated by now
277
- const data = { ...hoveredObj, intersections };
278
- handlers?.pointerout?.(data);
279
- handlers?.pointerleave?.(data);
280
- }
70
+ // Decrease frame count
71
+ state.internal.frames = Math.max(0, state.internal.frames - 1);
72
+ return state.frameloop === 'always' ? 1 : state.internal.frames;
73
+ }
74
+ function createLoop(roots) {
75
+ let running = false;
76
+ let repeat;
77
+ let frame;
78
+ function loop(timestamp) {
79
+ frame = requestAnimationFrame(loop);
80
+ running = true;
81
+ repeat = 0;
82
+ // Run effects
83
+ flushGlobalEffects('before', timestamp);
84
+ // Render all roots
85
+ for (const root of roots.values()) {
86
+ const state = root.get();
87
+ // If the frameloop is invalidated, do not run another frame
88
+ if (state.internal.active &&
89
+ (state.frameloop === 'always' || state.internal.frames > 0) &&
90
+ !state.gl.xr?.isPresenting) {
91
+ repeat += render(timestamp, root);
281
92
  }
282
93
  }
283
- }
284
- function pointerMissed(event, objects) {
285
- for (let i = 0; i < objects.length; i++) {
286
- const instance = getInstanceLocalState(objects[i]);
287
- instance?.handlers.pointermissed?.(event);
288
- }
289
- }
290
- function handlePointer(name) {
291
- // Deal with cancelation
292
- switch (name) {
293
- case 'pointerleave':
294
- case 'pointercancel':
295
- return () => cancelPointer([]);
296
- case 'lostpointercapture':
297
- return (event) => {
298
- const { internal } = stateFactory();
299
- if ('pointerId' in event && !internal.capturedMap.has(event.pointerId)) {
300
- // If the object event interface had onLostPointerCapture, we'd call it here on every
301
- // object that's getting removed.
302
- internal.capturedMap.delete(event.pointerId);
303
- cancelPointer([]);
304
- }
305
- };
94
+ // Run after-effects
95
+ flushGlobalEffects('after', timestamp);
96
+ // Stop the loop if nothing invalidates it
97
+ if (repeat === 0) {
98
+ // Tail call effects, they are called when rendering stops
99
+ flushGlobalEffects('tail', timestamp);
100
+ // Flag end of operation
101
+ running = false;
102
+ return cancelAnimationFrame(frame);
306
103
  }
307
- // Any other pointer goes here ...
308
- return function handleEvent(event) {
309
- const { onPointerMissed, internal } = stateFactory();
310
- // prepareRay(event)
311
- internal.lastEvent = event;
312
- // Get fresh intersects
313
- const isPointerMove = name === 'pointermove';
314
- const isClickEvent = name === 'click' || name === 'contextmenu' || name === 'dblclick';
315
- const filter = isPointerMove ? filterPointerEvents : undefined;
316
- // const hits = patchIntersects(intersect(filter), event)
317
- const hits = intersect(event, filter);
318
- const delta = isClickEvent ? calculateDistance(event) : 0;
319
- // Save initial coordinates on pointer-down
320
- if (name === 'pointerdown') {
321
- internal.initialClick = [event.offsetX, event.offsetY];
322
- internal.initialHits = hits.map((hit) => hit.eventObject);
323
- }
324
- // If a click yields no results, pass it back to the user as a miss
325
- // Missed events have to come first in order to establish user-land side-effect clean up
326
- if (isClickEvent && !hits.length) {
327
- if (delta <= 2) {
328
- pointerMissed(event, internal.interaction);
329
- if (onPointerMissed)
330
- onPointerMissed(event);
331
- }
332
- }
333
- // Take care of unhover
334
- if (isPointerMove)
335
- cancelPointer(hits);
336
- function onIntersect(data) {
337
- const eventObject = data.eventObject;
338
- const instance = getInstanceLocalState(eventObject);
339
- const handlers = instance?.handlers;
340
- // Check presence of handlers
341
- if (!instance?.eventCount)
342
- return;
343
- if (isPointerMove) {
344
- // Move event ...
345
- if (handlers?.pointerover ||
346
- handlers?.pointerenter ||
347
- handlers?.pointerout ||
348
- handlers?.pointerleave) {
349
- // When enter or out is present take care of hover-state
350
- const id = makeId(data);
351
- const hoveredItem = internal.hovered.get(id);
352
- if (!hoveredItem) {
353
- // If the object wasn't previously hovered, book it and call its handler
354
- internal.hovered.set(id, data);
355
- handlers.pointerover?.(data);
356
- handlers.pointerenter?.(data);
357
- }
358
- else if (hoveredItem.stopped) {
359
- // If the object was previously hovered and stopped, we shouldn't allow other items to proceed
360
- data.stopPropagation();
361
- }
362
- }
363
- // Call mouse move
364
- handlers?.pointermove?.(data);
365
- }
366
- else {
367
- // All other events ...
368
- const handler = handlers[name];
369
- if (handler) {
370
- // Forward all events back to their respective handlers with the exception of click events,
371
- // which must use the initial target
372
- if (!isClickEvent || internal.initialHits.includes(eventObject)) {
373
- // Missed events have to come first
374
- pointerMissed(event, internal.interaction.filter((object) => !internal.initialHits.includes(object)));
375
- // Now call the handler
376
- handler(data);
377
- }
378
- }
379
- else {
380
- // Trigger onPointerMissed on all elements that have pointer over/out handlers, but not click and weren't hit
381
- if (isClickEvent && internal.initialHits.includes(eventObject)) {
382
- pointerMissed(event, internal.interaction.filter((object) => !internal.initialHits.includes(object)));
383
- }
384
- }
385
- }
386
- }
387
- handleIntersects(hits, event, delta, onIntersect);
388
- };
389
104
  }
390
- return { handlePointer };
391
- }
392
-
393
- const DOM_EVENTS = {
394
- click: false,
395
- contextmenu: false,
396
- dblclick: false,
397
- wheel: false,
398
- pointerdown: true,
399
- pointerup: true,
400
- pointerleave: true,
401
- pointermove: true,
402
- pointercancel: true,
403
- lostpointercapture: true,
404
- };
405
- function createPointerEvents(stateFactory) {
406
- const { handlePointer } = createEvents(stateFactory);
407
- return {
408
- priority: 1,
409
- enabled: true,
410
- compute: (event, rootFactory) => {
411
- const state = rootFactory();
412
- // https://github.com/pmndrs/react-three-fiber/pull/782
413
- // Events trigger outside of canvas when moved, use offsetX/Y by default and allow overrides
414
- state.pointer.set((event.offsetX / state.size.width) * 2 - 1, -(event.offsetY / state.size.height) * 2 + 1);
415
- state.raycaster.setFromCamera(state.pointer, state.camera);
416
- },
417
- connected: undefined,
418
- handlers: Object.keys(DOM_EVENTS).reduce((handlers, supportedEventName) => {
419
- handlers[supportedEventName] = handlePointer(supportedEventName);
420
- return handlers;
421
- }, {}),
422
- connect: (target) => {
423
- const state = stateFactory();
424
- state.events.disconnect?.();
425
- state.setEvents({ connected: target });
426
- Object.entries(state.events.handlers ?? {}).forEach(([eventName, eventHandler]) => {
427
- const passive = DOM_EVENTS[eventName];
428
- target.addEventListener(eventName, eventHandler, { passive });
429
- });
430
- },
431
- disconnect: () => {
432
- const { events, setEvents } = stateFactory();
433
- if (events.connected) {
434
- Object.entries(events.handlers ?? {}).forEach(([eventName, eventHandler]) => {
435
- if (events.connected instanceof HTMLElement) {
436
- events.connected.removeEventListener(eventName, eventHandler);
437
- }
438
- });
439
- setEvents({ connected: undefined });
440
- }
441
- },
442
- };
443
- }
444
-
445
- class NgtRef extends BehaviorSubject {
446
- constructor(value) {
447
- super(value ? value : null);
448
- }
449
- set(valueOrFactory) {
450
- if (typeof valueOrFactory === 'function') {
451
- this.next(valueOrFactory(this.value));
452
- }
453
- else {
454
- this.next(valueOrFactory);
105
+ function invalidate(store, frames = 1) {
106
+ const state = store?.get();
107
+ if (!state)
108
+ return roots.forEach((root) => invalidate(root, frames));
109
+ if (state.gl.xr?.isPresenting || !state.internal.active || state.frameloop === 'never')
110
+ return;
111
+ // Increase frames, do not go higher than 60
112
+ state.internal.frames = Math.min(60, state.internal.frames + frames);
113
+ // If the render-loop isn't active, start it
114
+ if (!running) {
115
+ running = true;
116
+ requestAnimationFrame(loop);
455
117
  }
456
118
  }
119
+ function advance(timestamp, runGlobalEffects = true, store, frame) {
120
+ const state = store?.get();
121
+ if (runGlobalEffects)
122
+ flushGlobalEffects('before', timestamp);
123
+ if (!state)
124
+ for (const root of roots.values())
125
+ render(timestamp, root);
126
+ // safe to assume store is available here
127
+ else
128
+ render(timestamp, store, frame);
129
+ if (runGlobalEffects)
130
+ flushGlobalEffects('after', timestamp);
131
+ }
132
+ return {
133
+ loop,
134
+ /**
135
+ * Invalidates the view, requesting a frame to be rendered. Will globally invalidate unless passed a root's state.
136
+ * @see https://docs.pmnd.rs/react-three-fiber/api/additional-exports#invalidate
137
+ */
138
+ invalidate,
139
+ /**
140
+ * Advances the frameloop and runs render effects, useful for when manually rendering via `frameloop="never"`.
141
+ * @see https://docs.pmnd.rs/react-three-fiber/api/additional-exports#advance
142
+ */
143
+ advance,
144
+ };
457
145
  }
458
146
 
459
147
  const is = {
460
148
  obj: (a) => a === Object(a) && !Array.isArray(a) && typeof a !== 'function',
461
149
  material: (a) => !!a && a.isMaterial,
462
150
  geometry: (a) => !!a && a.isBufferGeometry,
463
- mesh: (a) => !!a && a.isMesh,
464
- color: (a) => !!a && a.isColor,
465
- orthographic: (a) => !!a && a.isOrthographicCamera,
466
- perspective: (a) => !!a && a.isPerspectiveCamera,
151
+ orthographicCamera: (a) => !!a && a.isOrthographicCamera,
152
+ perspectiveCamera: (a) => !!a && a.isPerspectiveCamera,
467
153
  camera: (a) => !!a && a.isCamera,
468
- glRenderer: (a) => !!a && a instanceof THREE.WebGLRenderer,
154
+ renderer: (a) => !!a && a instanceof THREE.WebGLRenderer,
469
155
  scene: (a) => !!a && a.isScene,
470
- object3d: (a) => !!a && a.isObject3D,
156
+ object3D: (a) => !!a && a.isObject3D,
471
157
  instance: (a) => !!a && !!a['__ngt__'],
472
- ref: (a) => !!a && a instanceof NgtRef,
473
- supportColorManagement: () => 'ColorManagement' in THREE,
474
- canvas: (a) => a instanceof HTMLCanvasElement,
158
+ ref: (a) => a instanceof ElementRef,
475
159
  equ(a, b, { arrays = 'shallow', objects = 'reference', strict = true } = {}) {
476
160
  // Wrong type or one of the two undefined, doesn't match
477
161
  if (typeof a !== typeof b || !!a !== !!b)
@@ -508,165 +192,6 @@ const is = {
508
192
  },
509
193
  };
510
194
 
511
- /**
512
- * a default Selector that consumers can quickly use for their selectors projector
513
- */
514
- const defaultProjector = () => ({});
515
- /**
516
- * A custom operator that skips the first undefined value but allows subsequent undefined values.
517
- * NgRxComponentStore#select always emits the first value regardless of undefined or not after initialize
518
- */
519
- const skipFirstUndefined = () => filter((value, index) => index > 0 || value !== undefined);
520
- /**
521
- * An extended `tap` operator that accepts an `effectFn` which:
522
- * - runs on every `next` notification from `source$`
523
- * - can optionally return a `cleanUp` function that
524
- * invokes from the 2nd `next` notification onward and on `unsubscribe` (destroyed)
525
- *
526
- *
527
- * @example
528
- * ```typescript
529
- * source$.pipe(
530
- * tapEffect((sourceValue) = {
531
- * const cb = () => {
532
- * doStuff(sourceValue);
533
- * };
534
- * addListener('event', cb);
535
- *
536
- * return () => {
537
- * removeListener('event', cb);
538
- * }
539
- * })
540
- * )
541
- * ```
542
- */
543
- function tapEffect(effectFn) {
544
- let cleanupFn = noop;
545
- let firstRun = false;
546
- let prev = undefined;
547
- const teardown = (error) => {
548
- return () => {
549
- if (cleanupFn) {
550
- cleanupFn({ prev, complete: true, error });
551
- }
552
- };
553
- };
554
- return tap({
555
- next: (value) => {
556
- if (cleanupFn && firstRun) {
557
- cleanupFn({ prev, complete: false, error: false });
558
- }
559
- const cleanUpOrVoid = effectFn(value);
560
- if (cleanUpOrVoid) {
561
- cleanupFn = cleanUpOrVoid;
562
- }
563
- prev = value;
564
- if (!firstRun) {
565
- firstRun = true;
566
- }
567
- },
568
- complete: teardown(false),
569
- unsubscribe: teardown(false),
570
- error: teardown(true),
571
- });
572
- }
573
- class NgtComponentStore extends ComponentStore {
574
- constructor() {
575
- super({});
576
- // exposing get since THREE is imperative at its core
577
- // we need to imperatively get state sometimes for usages in Animation Loop
578
- // we also bind "this" instance, so we don't have to bind it later
579
- this.read = this.get.bind(this);
580
- this.initialize();
581
- }
582
- /**
583
- * A custom patchState that allows for:
584
- * - Partial state updates and Observable of partial state updates like patchState
585
- * - Pass a Record<string, ObservableInput> to update a specific key with an Observable.
586
- * This is similar to `RxState.connect()` API
587
- */
588
- write(partialStateOrFactory) {
589
- if (typeof partialStateOrFactory === 'function') {
590
- return this.write(partialStateOrFactory(this.read()));
591
- }
592
- const partialState = partialStateOrFactory;
593
- if (Object.keys(partialState).length === 0) {
594
- return;
595
- }
596
- if (isObservable(partialState)) {
597
- return this.patchState(partialState);
598
- }
599
- const entries = Object.entries(partialState);
600
- const hasObservable = entries.some(([_, value]) => isObservable(value) && !is.ref(value));
601
- if (!hasObservable) {
602
- return this.patchState(partialState);
603
- }
604
- const [rawValues, observableValues] = entries.reduce((result, [key, value]) => {
605
- if (isObservable(value)) {
606
- result[1][key] = value;
607
- }
608
- else {
609
- result[0][key] = value;
610
- }
611
- return result;
612
- }, [{}, {}]);
613
- if (Object.keys(rawValues).length > 0) {
614
- this.patchState(rawValues);
615
- }
616
- if (Object.keys(observableValues).length > 0) {
617
- this.patchState(combineLatest(observableValues));
618
- }
619
- }
620
- initialize() {
621
- return;
622
- }
623
- /**
624
- * A utility class method to select a state on the template as Observable
625
- * - This method always debounce the Observable
626
- */
627
- selectKey(key, skipFirst = false) {
628
- return this.select((s) => s[key], { debounce: true }).pipe(skipFirst ? skipFirstUndefined() : (s) => s);
629
- }
630
- /**
631
- * A utility class method to get a state on the template as imperative Value
632
- */
633
- readKey(key) {
634
- return this.read((s) => s[key]);
635
- }
636
- }
637
- NgtComponentStore.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.0.0", ngImport: i0, type: NgtComponentStore, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
638
- NgtComponentStore.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "15.0.0", ngImport: i0, type: NgtComponentStore });
639
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.0.0", ngImport: i0, type: NgtComponentStore, decorators: [{
640
- type: Injectable
641
- }], ctorParameters: function () { return []; } });
642
-
643
- function createInjection(description, { defaultValueOrFactory, provideValueFactory, } = {}) {
644
- const factory = (defaultValueOrFactory && typeof defaultValueOrFactory === 'function'
645
- ? defaultValueOrFactory
646
- : () => defaultValueOrFactory);
647
- const injectionToken = new InjectionToken(description, { factory });
648
- function injectFn(options = {}) {
649
- return inject(injectionToken, options);
650
- }
651
- function provideFn(value) {
652
- return {
653
- provide: injectionToken,
654
- useValue: provideValueFactory ? provideValueFactory(value) : value,
655
- };
656
- }
657
- return [injectFn, provideFn, injectionToken];
658
- }
659
-
660
- const [injectWindow] = createInjection('window', {
661
- defaultValueOrFactory: () => {
662
- const { defaultView } = inject(DOCUMENT);
663
- if (!defaultView) {
664
- throw `window is not available!`;
665
- }
666
- return defaultView;
667
- },
668
- });
669
-
670
195
  function checkNeedsUpdate(value) {
671
196
  if (value !== null && is.obj(value) && 'needsUpdate' in value) {
672
197
  value['needsUpdate'] = true;
@@ -676,183 +201,20 @@ function checkNeedsUpdate(value) {
676
201
  }
677
202
  }
678
203
  function checkUpdate(value) {
679
- if (is.object3d(value)) {
204
+ if (is.object3D(value)) {
680
205
  value.updateMatrix();
681
206
  }
682
- else if (is.camera(value)) {
683
- if (is.perspective(value) || is.orthographic(value)) {
207
+ if (is.camera(value)) {
208
+ if (is.perspectiveCamera(value) || is.orthographicCamera(value)) {
684
209
  value.updateProjectionMatrix();
685
210
  }
686
211
  value.updateMatrixWorld();
687
212
  }
688
213
  checkNeedsUpdate(value);
689
214
  }
690
-
691
- function invalidateInstance(instance) {
692
- const state = getInstanceLocalState(instance)?.stateFactory();
693
- if (state && state.internal.frames === 0)
694
- state.invalidate();
695
- checkUpdate(instance);
696
- }
697
- function prepare(instance, parentStateFactory, rootStateFactory, parentInstance, previousInstance, isPrimitive = false) {
698
- const previousInstanceInternal = getInstanceLocalState(previousInstance?.value);
699
- const parent = parentInstance
700
- ? parentInstance
701
- : previousInstanceInternal
702
- ? previousInstanceInternal.parentRef
703
- : undefined;
704
- if (is.scene(instance)) {
705
- applyProps(instance, { dispose: null });
706
- }
707
- return Object.assign(instance, {
708
- __ngt__: {
709
- stateFactory: parentStateFactory,
710
- rootFactory: rootStateFactory,
711
- isPrimitive: !isPrimitive ? previousInstanceInternal?.isPrimitive : isPrimitive,
712
- eventCount: previousInstanceInternal?.eventCount ?? 0,
713
- handlers: previousInstanceInternal?.handlers ?? {},
714
- instancesRefs: previousInstanceInternal?.instancesRefs ?? new NgtRef([]),
715
- objectsRefs: previousInstanceInternal?.objectsRefs ?? new NgtRef([]),
716
- parentRef: parent ? (parent === instance || parent.value === instance ? null : parent) : null,
717
- },
718
- });
719
- }
720
-
721
- const DEFAULT = '__default';
722
- function diffProps(instance, props, previousProps = {}, remove = false) {
723
- const localState = getInstanceLocalState(instance) || {};
724
- const propsEntries = Object.entries(props);
725
- const changes = [];
726
- // Catch removed props, prepend them, so they can be reset or removed
727
- if (remove) {
728
- const previousKeys = Object.keys(previousProps);
729
- for (const previousKey of previousKeys) {
730
- // @ts-ignore
731
- if (!Object.hasOwn(props, previousKey)) {
732
- propsEntries.unshift([previousKey, DEFAULT + 'remove']);
733
- }
734
- }
735
- }
736
- for (const [propKey, propValue] of propsEntries) {
737
- if (is.equ(propValue, previousProps[propKey]))
738
- continue;
739
- changes.push([propKey, propValue]);
740
- }
741
- const memoized = { ...props };
742
- if (localState.memoized && localState.memoized['args']) {
743
- memoized['args'] = localState.memoized['args'];
744
- }
745
- if (localState.memoized && localState.memoized['attach']) {
746
- memoized['attach'] = localState.memoized['attach'] || localState.attach;
747
- }
748
- return { changes, memoized };
749
- }
750
- function applyProps(instance, props) {
751
- // props is empty
752
- if (!Object.keys(props).length)
753
- return instance;
754
- // Filter equals, events and reserved props
755
- const localState = getInstanceLocalState(instance) || {};
756
- const rootState = localState.stateFactory?.();
757
- const { changes, memoized } = diffProps(instance, props);
758
- const instanceHandlers = localState.eventCount;
759
- if (getInstanceLocalState(instance)) {
760
- getInstanceLocalState(instance).memoized = memoized;
761
- }
762
- for (let i = 0; i < changes.length; i++) {
763
- const key = changes[i][0];
764
- const currentInstance = instance;
765
- const targetProp = currentInstance[key];
766
- let value = changes[i][1];
767
- if (is.ref(value)) {
768
- value = value.value;
769
- }
770
- if (value === DEFAULT + 'remove') {
771
- if (targetProp && targetProp.constructor) {
772
- // use the prop constructor to find the default it should be
773
- value = new targetProp.constructor(...(memoized['args'] ?? []));
774
- }
775
- else if (currentInstance.constructor) {
776
- const dummyInstance = new currentInstance.constructor(...(getInstanceLocalState(currentInstance)?.memoized?.['args'] || []));
777
- value = dummyInstance[targetProp];
778
- // destroy the instance
779
- if (dummyInstance.dispose)
780
- dummyInstance.dispose();
781
- }
782
- else {
783
- value = 0;
784
- }
785
- }
786
- // Special treatment for objects with support for set/copy, and layers
787
- if (targetProp && targetProp['set'] && (targetProp['copy'] || targetProp instanceof THREE.Layers)) {
788
- const isColor = targetProp instanceof THREE.Color;
789
- // If value is an array
790
- if (Array.isArray(value)) {
791
- if (targetProp['fromArray'])
792
- targetProp['fromArray'](value);
793
- else
794
- targetProp['set'](...value);
795
- }
796
- // Test again target.copy(class) next ...
797
- else if (targetProp['copy'] &&
798
- value &&
799
- value.constructor &&
800
- targetProp.constructor.name === value.constructor.name) {
801
- targetProp['copy'](value);
802
- if (!is.supportColorManagement() && !rootState.linear && isColor) {
803
- targetProp['convertSRGBToLinear']();
804
- }
805
- }
806
- // If nothing else fits, just set the single value, ignore undefined
807
- // https://github.com/pmndrs/react-three-fiber/issues/274
808
- else if (value !== undefined) {
809
- const isColor = targetProp instanceof THREE.Color;
810
- // Allow setting array scalars
811
- if (!isColor && targetProp['setScalar'])
812
- targetProp['setScalar'](value);
813
- // Layers have no copy function, we must therefore copy the mask property
814
- else if (targetProp instanceof THREE.Layers && value instanceof THREE.Layers)
815
- targetProp.mask = value.mask;
816
- // Otherwise just set ...
817
- else
818
- targetProp['set'](value);
819
- // For versions of three which don't support THREE.ColorManagement,
820
- // Auto-convert sRGB colors
821
- // https://github.com/pmndrs/react-three-fiber/issues/344
822
- if (!is.supportColorManagement() && !rootState.linear && isColor)
823
- targetProp.convertSRGBToLinear();
824
- }
825
- // Else, just overwrite the value
826
- }
827
- else {
828
- currentInstance[key] = value;
829
- // Auto-convert sRGB textures, for now ...
830
- // https://github.com/pmndrs/react-three-fiber/issues/344
831
- if (!rootState?.linear && currentInstance[key] instanceof THREE.Texture) {
832
- currentInstance[key]['encoding'] = THREE.sRGBEncoding;
833
- }
834
- }
835
- checkNeedsUpdate(targetProp);
836
- invalidateInstance(instance);
837
- }
838
- if (localState.parentRef &&
839
- rootState.internal &&
840
- instance['raycast'] &&
841
- instanceHandlers !== localState.eventCount) {
842
- // Pre-emptively remove the instance from the interaction manager
843
- rootState.removeInteraction(instance['uuid']);
844
- // Add the instance to the interaction manager only when it has handlers
845
- if (localState.eventCount)
846
- rootState.addInteraction(instance);
847
- }
848
- return instance;
849
- }
850
-
851
215
  function updateCamera(camera, size) {
852
- // https://github.com/pmndrs/react-three-fiber/issues/92
853
- // Do not mess with the camera if it belongs to the user
854
216
  if (!camera.manual) {
855
- if (is.orthographic(camera)) {
217
+ if (is.orthographicCamera(camera)) {
856
218
  camera.left = size.width / -2;
857
219
  camera.right = size.width / 2;
858
220
  camera.top = size.height / 2;
@@ -862,161 +224,172 @@ function updateCamera(camera, size) {
862
224
  camera.aspect = size.width / size.height;
863
225
  }
864
226
  camera.updateProjectionMatrix();
865
- // https://github.com/pmndrs/react-three-fiber/issues/178
866
- // Update matrix world since the renderer is a frame late
867
227
  camera.updateMatrixWorld();
868
228
  }
869
229
  }
870
- function createDefaultCamera(isOrthographic, size) {
871
- if (isOrthographic) {
872
- return new THREE.OrthographicCamera(0, 0, 0, 0, 0.1, 1000);
873
- }
874
- return new THREE.PerspectiveCamera(75, size.width / size.height, 0.1, 1000);
875
- }
876
230
 
877
- function createSubs(callback, subs) {
878
- const sub = { callback };
879
- subs.add(sub);
880
- return () => void subs.delete(sub);
231
+ function getLocalState(obj) {
232
+ if (!obj)
233
+ return {};
234
+ return obj['__ngt__'] || {};
881
235
  }
882
- const globalEffects = new Set();
883
- const globalAfterEffects = new Set();
884
- const globalTailEffects = new Set();
885
- /**
886
- * Adds a global render callback which is called each frame.
887
- * @see https://docs.pmnd.rs/react-three-fiber/api/additional-exports#addEffect
888
- */
889
- const addEffect = (callback) => createSubs(callback, globalEffects);
890
- /**
891
- * Adds a global after-render callback which is called each frame.
892
- * @see https://docs.pmnd.rs/react-three-fiber/api/additional-exports#addAfterEffect
893
- */
894
- const addAfterEffect = (callback) => createSubs(callback, globalAfterEffects);
895
- /**
896
- * Adds a global callback which is called when rendering stops.
897
- * @see https://docs.pmnd.rs/react-three-fiber/api/additional-exports#addTail
898
- */
899
- const addTail = (callback) => createSubs(callback, globalTailEffects);
900
- function run(effects, timestamp) {
901
- if (!effects.size)
902
- return;
903
- for (const { callback } of effects.values()) {
904
- callback(timestamp);
905
- }
236
+ function invalidateInstance(instance) {
237
+ const state = getLocalState(instance).store?.get();
238
+ if (state && state.internal.frames === 0)
239
+ state.invalidate();
240
+ checkUpdate(instance);
906
241
  }
907
- function flushGlobalEffects(type, timestamp) {
908
- switch (type) {
909
- case 'before':
910
- return run(globalEffects, timestamp);
911
- case 'after':
912
- return run(globalAfterEffects, timestamp);
913
- case 'tail':
914
- return run(globalTailEffects, timestamp);
242
+ function prepare(object, localState) {
243
+ const instance = object;
244
+ if (localState?.primitive || !instance.__ngt__) {
245
+ const { objects = new BehaviorSubject([]), nonObjects = new BehaviorSubject([]), ...rest } = localState || {};
246
+ instance.__ngt__ = {
247
+ previousAttach: null,
248
+ store: null,
249
+ parent: null,
250
+ memoized: {},
251
+ eventCount: 0,
252
+ handlers: {},
253
+ objects,
254
+ nonObjects,
255
+ add: (object, type) => {
256
+ instance.__ngt__[type].next([...instance.__ngt__[type].value, object]);
257
+ notifyAncestors(instance.__ngt__.parent);
258
+ },
259
+ remove: (object, type) => {
260
+ instance.__ngt__[type].next(instance.__ngt__[type].value.filter((o) => o !== object));
261
+ notifyAncestors(instance.__ngt__.parent);
262
+ },
263
+ ...rest,
264
+ };
915
265
  }
266
+ return instance;
916
267
  }
917
- function render(timestamp, stateFactory, frame) {
918
- const state = stateFactory();
919
- // Run local effects
920
- let delta = state.clock.getDelta();
921
- // In frameloop='never' mode, clock times are updated using the provided timestamp
922
- if (state.frameloop === 'never' && typeof timestamp === 'number') {
923
- delta = timestamp - state.clock.elapsedTime;
924
- state.clock.oldTime = state.clock.elapsedTime;
925
- state.clock.elapsedTime = timestamp;
926
- }
927
- // Call subscribers (useFrame)
928
- // subscribers = state.internal.subscribers;
929
- for (let i = 0; i < state.internal.subscribers.length; i++) {
930
- const subscriber = state.internal.subscribers[i];
931
- const object = is.ref(subscriber.obj) ? subscriber.obj.value : subscriber.obj;
932
- subscriber.callback({ ...state, delta, frame }, object);
268
+ function notifyAncestors(instance) {
269
+ if (!instance)
270
+ return;
271
+ const localState = getLocalState(instance);
272
+ if (localState.objects)
273
+ localState.objects.next(localState.objects.value);
274
+ if (localState.nonObjects)
275
+ localState.nonObjects.next(localState.nonObjects.value);
276
+ notifyAncestors(localState.parent);
277
+ }
278
+
279
+ function diffProps(instance, props) {
280
+ const propsEntries = Object.entries(props);
281
+ const changes = [];
282
+ for (const [propKey, propValue] of propsEntries) {
283
+ if (is.equ(propValue, instance[propKey]))
284
+ continue;
285
+ changes.push([propKey, propValue]);
933
286
  }
934
- // Render content
935
- if (!state.internal.priority && state.gl.render)
936
- state.gl.render(state.scene, state.camera);
937
- // Decrease frame count
938
- state.internal.frames = Math.max(0, state.internal.frames - 1);
939
- return state.frameloop === 'always' ? 1 : state.internal.frames;
287
+ return changes;
940
288
  }
941
- function createLoop(roots) {
942
- let running = false;
943
- let repeat;
944
- let frame;
945
- let state;
946
- function loop(timestamp) {
947
- frame = requestAnimationFrame(loop);
948
- running = true;
949
- repeat = 0;
950
- // Run effects
951
- flushGlobalEffects('before', timestamp);
952
- // Render all roots
953
- for (const root of roots.values()) {
954
- state = root();
955
- // If the frameloop is invalidated, do not run another frame
956
- if (state.internal.active &&
957
- (state.frameloop === 'always' || state.internal.frames > 0) &&
958
- !state.gl.xr?.isPresenting) {
959
- repeat += render(timestamp, root);
289
+ function applyProps(instance, props) {
290
+ // if props is empty
291
+ if (!Object.keys(props).length)
292
+ return instance;
293
+ // filter equals, events , and reserved props
294
+ const localState = getLocalState(instance);
295
+ const rootState = localState.store?.get();
296
+ const changes = diffProps(instance, props);
297
+ for (let i = 0; i < changes.length; i++) {
298
+ const key = changes[i][0];
299
+ const currentInstance = instance;
300
+ const targetProp = currentInstance[key];
301
+ const value = changes[i][1];
302
+ // special treatmen for objects with support for set/copy, and layers
303
+ if (targetProp && targetProp['set'] && (targetProp['copy'] || targetProp instanceof THREE.Layers)) {
304
+ const isColor = targetProp instanceof THREE.Color;
305
+ // if value is an array
306
+ if (Array.isArray(value)) {
307
+ if (targetProp['fromArray'])
308
+ targetProp['fromArray'](value);
309
+ else
310
+ targetProp['set'](...value);
311
+ }
312
+ // test again target.copy
313
+ else if (targetProp['copy'] &&
314
+ value &&
315
+ value.constructor &&
316
+ targetProp.constructor.name === value.constructor.name) {
317
+ targetProp['copy'](value);
318
+ if (!THREE.ColorManagement && !rootState.linear && isColor) {
319
+ targetProp['convertSRGBToLinear']();
320
+ }
321
+ }
322
+ // if nothing else fits, just set the single value, ignore undefined
323
+ else if (value !== undefined) {
324
+ const isColor = targetProp instanceof THREE.Color;
325
+ // allow setting array scalars
326
+ if (!isColor && targetProp['setScalar'])
327
+ targetProp['setScalar'](value);
328
+ // layers have no copy function, copy the mask
329
+ else if (targetProp instanceof THREE.Layers && value instanceof THREE.Layers) {
330
+ targetProp.mask = value.mask;
331
+ }
332
+ // otherwise just set ...
333
+ else {
334
+ targetProp['set'](value);
335
+ }
336
+ // auto-convert srgb
337
+ if (!THREE.ColorManagement && !rootState?.linear && isColor) {
338
+ targetProp.convertSRGBToLinear();
339
+ }
960
340
  }
961
341
  }
962
- // Run after-effects
963
- flushGlobalEffects('after', timestamp);
964
- // Stop the loop if nothing invalidates it
965
- if (repeat === 0) {
966
- // Tail call effects, they are called when rendering stops
967
- flushGlobalEffects('tail', timestamp);
968
- // Flag end of operation
969
- running = false;
970
- return cancelAnimationFrame(frame);
342
+ // else just overwrite the value
343
+ else {
344
+ currentInstance[key] = value;
345
+ // auto-convert srgb textures
346
+ if (!rootState?.linear && currentInstance[key] instanceof THREE.Texture) {
347
+ currentInstance[key]['encoding'] = THREE.sRGBEncoding;
348
+ }
971
349
  }
350
+ checkUpdate(targetProp);
351
+ invalidateInstance(instance);
972
352
  }
973
- function invalidate(stateFactory, frames = 1) {
974
- const stateToInvalidate = stateFactory?.();
975
- if (!stateToInvalidate)
976
- return roots.forEach((root) => invalidate(root, frames));
977
- if (stateToInvalidate.gl.xr?.isPresenting ||
978
- !stateToInvalidate.internal.active ||
979
- stateToInvalidate.frameloop === 'never')
980
- return;
981
- // Increase frames, do not go higher than 60
982
- stateToInvalidate.internal.frames = Math.min(60, stateToInvalidate.internal.frames + frames);
983
- // If the render-loop isn't active, start it
984
- if (!running) {
985
- running = true;
986
- requestAnimationFrame(loop);
987
- }
353
+ const instanceHandlers = localState.eventCount;
354
+ if (localState.parent && rootState.internal && instance['raycast'] && instanceHandlers !== localState.eventCount) {
355
+ // pre-emptively remove the interaction from manager
356
+ rootState.removeInteraction(instance['uuid']);
357
+ // add the instance to the interaction manager only when it has handlers
358
+ if (localState.eventCount)
359
+ rootState.addInteraction(instance);
988
360
  }
989
- function advance(timestamp, runGlobalEffects = true, stateFactory, frame) {
990
- if (runGlobalEffects)
991
- flushGlobalEffects('before', timestamp);
992
- if (!stateFactory)
993
- for (const root of roots.values())
994
- render(timestamp, root);
995
- else
996
- render(timestamp, stateFactory, frame);
997
- if (runGlobalEffects)
998
- flushGlobalEffects('after', timestamp);
361
+ if (localState.parent && localState.afterUpdate && localState.afterUpdate.observed && changes.length) {
362
+ localState.afterUpdate.emit(instance);
999
363
  }
1000
- return {
1001
- loop,
1002
- /**
1003
- * Invalidates the view, requesting a frame to be rendered. Will globally invalidate unless passed a root's state.
1004
- * @see https://docs.pmnd.rs/react-three-fiber/api/additional-exports#invalidate
1005
- */
1006
- invalidate,
1007
- /**
1008
- * Advances the frameloop and runs render effects, useful for when manually rendering via `frameloop="never"`.
1009
- * @see https://docs.pmnd.rs/react-three-fiber/api/additional-exports#advance
1010
- */
1011
- advance,
1012
- };
364
+ return instance;
1013
365
  }
1014
366
 
1015
- function createRenderer(glOptions, canvasElement) {
367
+ const idCache = {};
368
+ function makeId(event) {
369
+ if (event) {
370
+ return (event.eventObject || event.object).uuid + '/' + event.index + event.instanceId;
371
+ }
372
+ const newId = THREE.MathUtils.generateUUID();
373
+ // ensure not already used
374
+ if (!idCache[newId]) {
375
+ idCache[newId] = true;
376
+ return newId;
377
+ }
378
+ return makeId();
379
+ }
380
+ function makeDpr(dpr, window) {
381
+ const target = window?.devicePixelRatio || 1;
382
+ return Array.isArray(dpr) ? Math.min(Math.max(dpr[0], target), dpr[1]) : dpr;
383
+ }
384
+ function makeDefaultCamera(isOrthographic, size) {
385
+ if (isOrthographic)
386
+ return new THREE.OrthographicCamera(0, 0, 0, 0, 0.1, 1000);
387
+ return new THREE.PerspectiveCamera(75, size.width / size.height, 0.1, 1000);
388
+ }
389
+ function makeDefaultRenderer(glOptions, canvasElement) {
1016
390
  const customRenderer = (typeof glOptions === 'function' ? glOptions(canvasElement) : glOptions);
1017
- if (customRenderer?.render != null) {
391
+ if (customRenderer?.render != null)
1018
392
  return customRenderer;
1019
- }
1020
393
  return new THREE.WebGLRenderer({
1021
394
  powerPreference: 'high-performance',
1022
395
  canvas: canvasElement,
@@ -1025,157 +398,176 @@ function createRenderer(glOptions, canvasElement) {
1025
398
  ...(glOptions || {}),
1026
399
  });
1027
400
  }
401
+ function makeObjectGraph(object) {
402
+ const data = { nodes: {}, materials: {} };
403
+ if (object) {
404
+ object.traverse((child) => {
405
+ if (child.name)
406
+ data.nodes[child.name] = child;
407
+ if ('material' in child && !data.materials[child.material.name]) {
408
+ data.materials[child.material.name] = child
409
+ .material;
410
+ }
411
+ });
412
+ }
413
+ return data;
414
+ }
415
+
416
+ const startWithUndefined = () => startWith(undefined);
417
+ /**
418
+ * An extended `tap` operator that accepts an `effectFn` which:
419
+ * - runs on every `next` notification from `source$`
420
+ * - can optionally return a `cleanUp` function that
421
+ * invokes from the 2nd `next` notification onward and on `unsubscribe` (destroyed)
422
+ *
423
+ *
424
+ * @example
425
+ * ```typescript
426
+ * source$.pipe(
427
+ * tapEffect((sourceValue) = {
428
+ * const cb = () => {
429
+ * doStuff(sourceValue);
430
+ * };
431
+ * addListener('event', cb);
432
+ *
433
+ * return () => {
434
+ * removeListener('event', cb);
435
+ * }
436
+ * })
437
+ * )
438
+ * ```
439
+ */
440
+ function tapEffect(effectFn) {
441
+ let cleanupFn = () => { };
442
+ let firstRun = false;
443
+ let prev = undefined;
444
+ const teardown = (error) => {
445
+ return () => {
446
+ if (cleanupFn) {
447
+ cleanupFn({ prev, complete: true, error });
448
+ }
449
+ };
450
+ };
451
+ return tap({
452
+ next: (value) => {
453
+ if (cleanupFn && firstRun) {
454
+ cleanupFn({ prev, complete: false, error: false });
455
+ }
456
+ const cleanUpOrVoid = effectFn(value);
457
+ if (cleanUpOrVoid) {
458
+ cleanupFn = cleanUpOrVoid;
459
+ }
460
+ prev = value;
461
+ if (!firstRun) {
462
+ firstRun = true;
463
+ }
464
+ },
465
+ complete: teardown(false),
466
+ unsubscribe: teardown(false),
467
+ error: teardown(true),
468
+ });
469
+ }
470
+ class NgtRxStore extends RxState {
471
+ constructor() {
472
+ super();
473
+ // set a dummy property so that initial this.get() won't return undefined
474
+ this.set({ __ngt_dummy__: '__ngt_dummy__' });
475
+ // call initialize that might be setup by derived Stores
476
+ this.initialize();
477
+ // override set so our consumers don't have to handle undefined for state that already have default values
478
+ const originalSet = this.set.bind(this);
479
+ Object.defineProperty(this, 'set', {
480
+ get: () => {
481
+ // Parameters type does not do well with overloads. So we use any[] here
482
+ return (...args) => {
483
+ const firstArg = args[0];
484
+ if (is.obj(firstArg)) {
485
+ const modArgs = Object.entries(firstArg).reduce((modded, [key, value]) => {
486
+ modded[key] = value === undefined ? this.get(key) : value;
487
+ return modded;
488
+ }, {});
489
+ return originalSet(modArgs);
490
+ }
491
+ // @ts-ignore
492
+ return originalSet(...args);
493
+ };
494
+ },
495
+ });
496
+ }
497
+ initialize() {
498
+ return;
499
+ }
500
+ effect(obs, sideEffectFn) {
501
+ return this.hold(obs.pipe(tapEffect(sideEffectFn)));
502
+ }
503
+ }
504
+ NgtRxStore.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.1.1", ngImport: i0, type: NgtRxStore, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
505
+ NgtRxStore.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "15.1.1", ngImport: i0, type: NgtRxStore });
506
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.1.1", ngImport: i0, type: NgtRxStore, decorators: [{
507
+ type: Injectable
508
+ }], ctorParameters: function () { return []; } });
1028
509
 
1029
510
  const rootStateMap = new Map();
1030
511
  const { invalidate, advance } = createLoop(rootStateMap);
1031
512
  const shallowLoose = { objects: 'shallow', strict: false };
1032
- class NgtStore extends NgtComponentStore {
513
+ class NgtStore extends NgtRxStore {
1033
514
  constructor() {
1034
515
  super(...arguments);
1035
516
  this.parentStore = inject(NgtStore, { optional: true, skipSelf: true });
1036
- this.zone = inject(NgZone);
1037
- this.window = injectWindow();
1038
- this.position = new THREE.Vector3();
1039
- this.defaultTarget = new THREE.Vector3();
1040
- this.tempTarget = new THREE.Vector3();
1041
- this.resize = this.effect(($) => {
1042
- let oldSize = this.read((s) => s.size);
1043
- let oldDpr = this.read((s) => s.viewport?.dpr);
1044
- return $.pipe(tap(() => {
1045
- const { camera, size, viewport, gl } = this.read();
1046
- // Resize camera and renderer on changes to size and pixelratio
1047
- if (size !== oldSize || viewport.dpr !== oldDpr) {
1048
- oldSize = size;
1049
- oldDpr = viewport.dpr;
1050
- // Update camera & renderer
1051
- updateCamera(camera, size);
1052
- gl.setPixelRatio(viewport.dpr);
1053
- gl.setSize(size.width, size.height, size.updateStyle);
1054
- }
1055
- }));
1056
- });
1057
- this.invalidate = this.effect(tap(() => void this.read((s) => s.invalidate)()));
517
+ this.window = inject(DOCUMENT).defaultView;
1058
518
  this.isInit = false;
1059
- this.isConfigured = false;
1060
- this.addInteraction = (interaction) => {
1061
- this.write((state) => ({
1062
- ...state,
1063
- internal: {
1064
- ...state.internal,
1065
- interaction: [...state.internal.interaction, interaction],
1066
- },
1067
- }));
1068
- };
1069
- this.removeInteraction = (uuid) => {
1070
- this.write((state) => ({
1071
- ...state,
1072
- internal: {
1073
- ...state.internal,
1074
- interaction: state.internal.interaction.filter((interaction) => interaction.uuid !== uuid),
1075
- },
1076
- }));
1077
- };
1078
- this.setPerformanceCurrent = (current) => {
1079
- this.write((s) => ({ performance: { ...s.performance, current } }));
1080
- };
1081
- this.setEvents = (events) => {
1082
- this.write((s) => ({ events: { ...s.events, ...events } }));
1083
- };
1084
- this.setSize = (width, height, top, left, updateStyle) => {
1085
- const camera = this.read((s) => s.camera);
1086
- const size = { width, height, top: top || 0, left: left || 0, updateStyle };
1087
- this.write((state) => ({
1088
- size,
1089
- viewport: {
1090
- ...state.viewport,
1091
- ...this.getCurrentViewport(camera, this.defaultTarget, size),
1092
- },
1093
- }));
1094
- };
1095
- this.setCamera = (camera) => {
1096
- this.write((s) => ({
1097
- camera,
1098
- viewport: {
1099
- ...s.viewport,
1100
- ...this.getCurrentViewport(camera),
1101
- },
1102
- }));
1103
- this.read((s) => s.cameraRef).set(camera);
1104
- };
1105
- this.setDpr = (dpr) => {
1106
- const resolved = makeDpr(dpr, this.window);
1107
- this.write((s) => ({
1108
- viewport: {
1109
- ...s.viewport,
1110
- dpr: resolved,
1111
- initialDpr: s.viewport.initialDpr || resolved,
1112
- },
1113
- }));
1114
- };
1115
- this.setFrameloop = (frameloop = 'always') => {
1116
- const clock = this.read((s) => s.clock);
1117
- // if frameloop === "never" clock.elapsedTime is updated using advance(timestamp)
1118
- clock.stop();
1119
- clock.elapsedTime = 0;
1120
- if (frameloop !== 'never') {
1121
- clock.start();
1122
- clock.elapsedTime = 0;
1123
- }
1124
- this.write({ frameloop });
1125
- };
1126
- this.getCurrentViewport = (camera = this.read((s) => s.camera), target = this.defaultTarget, size = this.read((s) => s.size)) => {
1127
- const { width, height, top, left } = size;
1128
- const aspect = width / height;
1129
- if (target instanceof THREE.Vector3)
1130
- this.tempTarget.copy(target);
1131
- else
1132
- this.tempTarget.set(...target);
1133
- const distance = camera.getWorldPosition(this.position).distanceTo(this.tempTarget);
1134
- if (is.orthographic(camera)) {
519
+ }
520
+ init() {
521
+ if (!this.isInit) {
522
+ const position = new THREE.Vector3();
523
+ const defaultTarget = new THREE.Vector3();
524
+ const tempTarget = new THREE.Vector3();
525
+ const getCurrentViewport = (camera = this.get('camera'), target = defaultTarget, size = this.get('size')) => {
526
+ const { width, height, top, left } = size;
527
+ const aspect = width / height;
528
+ if (target instanceof THREE.Vector3)
529
+ tempTarget.copy(target);
530
+ else
531
+ tempTarget.set(...target);
532
+ const distance = camera.getWorldPosition(position).distanceTo(tempTarget);
533
+ if (is.orthographicCamera(camera)) {
534
+ return {
535
+ width: width / camera.zoom,
536
+ height: height / camera.zoom,
537
+ top,
538
+ left,
539
+ factor: 1,
540
+ distance,
541
+ aspect,
542
+ };
543
+ }
544
+ const fov = (camera.fov * Math.PI) / 180; // convert vertical fov to radians
545
+ const h = 2 * Math.tan(fov / 2) * distance; // visible height
546
+ const w = h * aspect;
1135
547
  return {
1136
- width: width / camera.zoom,
1137
- height: height / camera.zoom,
548
+ width: w,
549
+ height: h,
1138
550
  top,
1139
551
  left,
1140
- factor: 1,
552
+ factor: width / w,
1141
553
  distance,
1142
554
  aspect,
1143
555
  };
1144
- }
1145
- const fov = (camera.fov * Math.PI) / 180; // convert vertical fov to radians
1146
- const h = 2 * Math.tan(fov / 2) * distance; // visible height
1147
- const w = h * (width / height);
1148
- return {
1149
- width: w,
1150
- height: h,
1151
- top,
1152
- left,
1153
- factor: width / w,
1154
- distance,
1155
- aspect,
1156
556
  };
1157
- };
1158
- }
1159
- get rootStateFactory() {
1160
- let root = this.read((s) => s.previousStateFactory);
1161
- while (root && root().previousStateFactory) {
1162
- root = root().previousStateFactory;
1163
- }
1164
- return root || this.read;
1165
- }
1166
- init() {
1167
- if (!this.isInit) {
1168
557
  const pointer = new THREE.Vector2();
1169
- const scene = prepare(new THREE.Scene(), this.read, this.rootStateFactory, this.parentStore?.get((s) => s.sceneRef));
1170
558
  let performanceTimeout;
1171
- this.write({
559
+ const setPerformanceCurrent = (current) => {
560
+ this.set((state) => ({ performance: { ...state.performance, current } }));
561
+ };
562
+ this.set({
563
+ get: this.get.bind(this),
564
+ set: this.set.bind(this),
565
+ select: this.select.bind(this),
1172
566
  ready: false,
1173
- cameraRef: new NgtRef(),
1174
- scene,
1175
- sceneRef: new NgtRef(scene),
567
+ scene: prepare(new THREE.Scene(), { store: this }),
1176
568
  events: { priority: 1, enabled: true, connected: false },
1177
- invalidate: (frames = 1) => invalidate(this.read, frames),
1178
- advance: (timestamp, runGlobalEffects) => advance(timestamp, runGlobalEffects, this.read),
569
+ invalidate: (frames = 1) => invalidate(this, frames),
570
+ advance: (timestamp, runGlobalEffects) => advance(timestamp, runGlobalEffects, this),
1179
571
  legacy: false,
1180
572
  linear: false,
1181
573
  flat: false,
@@ -1189,20 +581,19 @@ class NgtStore extends NgtComponentStore {
1189
581
  max: 1,
1190
582
  debounce: 200,
1191
583
  regress: () => {
1192
- this.zone.runOutsideAngular(() => {
1193
- const state = this.read();
1194
- // Clear timeout
1195
- if (performanceTimeout)
1196
- clearTimeout(performanceTimeout);
1197
- // Set lower bound performance
1198
- if (state.performance.current !== state.performance.min)
1199
- this.setPerformanceCurrent(state.performance.min);
1200
- // Go back to upper bound performance after a while unless something regresses meanwhile
1201
- performanceTimeout = setTimeout(() => this.setPerformanceCurrent(this.read((s) => s.performance)?.max || 1), state.performance.debounce);
1202
- });
584
+ const state = this.get();
585
+ // clear timeout
586
+ if (performanceTimeout)
587
+ clearTimeout(performanceTimeout);
588
+ // set lower bound
589
+ if (state.performance.current !== state.performance.min) {
590
+ setPerformanceCurrent(state.performance.min);
591
+ }
592
+ // go back to upper bound
593
+ performanceTimeout = setTimeout(() => setPerformanceCurrent(this.get('performance', 'max') || 1), state.performance.debounce);
1203
594
  },
1204
595
  },
1205
- size: { width: 0, height: 0, top: 0, left: 0, updateStyle: false },
596
+ size: { width: 0, height: 0, top: 0, left: 0 },
1206
597
  viewport: {
1207
598
  initialDpr: 0,
1208
599
  dpr: 0,
@@ -1213,9 +604,9 @@ class NgtStore extends NgtComponentStore {
1213
604
  aspect: 0,
1214
605
  distance: 0,
1215
606
  factor: 0,
1216
- getCurrentViewport: this.getCurrentViewport,
607
+ getCurrentViewport,
1217
608
  },
1218
- previousStateFactory: this.parentStore?.read.bind(this.parentStore),
609
+ previousStore: this.parentStore,
1219
610
  internal: {
1220
611
  active: false,
1221
612
  priority: 0,
@@ -1223,127 +614,153 @@ class NgtStore extends NgtComponentStore {
1223
614
  lastEvent: null,
1224
615
  interaction: [],
1225
616
  hovered: new Map(),
1226
- capturedMap: new Map(),
1227
- animations: new Map(),
1228
617
  subscribers: [],
1229
618
  initialClick: [0, 0],
1230
619
  initialHits: [],
1231
- subscribe: (callback, priority = 0, stateFactory = this.read, obj) => {
1232
- const internal = this.read((s) => s.internal);
620
+ capturedMap: new Map(),
621
+ subscribe: (callback, priority = 0, store = this) => {
622
+ const internal = this.get('internal');
1233
623
  // If this subscription was given a priority, it takes rendering into its own hands
1234
624
  // For that reason we switch off automatic rendering and increase the manual flag
1235
625
  // As long as this flag is positive there can be no internal rendering at all
1236
626
  // because there could be multiple render subscriptions
1237
627
  internal.priority = internal.priority + (priority > 0 ? 1 : 0);
1238
- internal.subscribers.push({ priority, stateFactory, callback, obj });
628
+ internal.subscribers.push({ priority, store, callback });
1239
629
  // Register subscriber and sort layers from lowest to highest, meaning,
1240
630
  // highest priority renders last (on top of the other frames)
1241
631
  internal.subscribers.sort((a, b) => (a.priority || 0) - (b.priority || 0));
1242
632
  return () => {
1243
- const internalOnCleanUp = this.read((s) => s.internal);
1244
- if (internalOnCleanUp.subscribers) {
633
+ const internal = this.get('internal');
634
+ if (internal?.subscribers) {
1245
635
  // Decrease manual flag if this subscription had a priority
1246
- internalOnCleanUp.priority = internalOnCleanUp.priority - (priority > 0 ? 1 : 0);
636
+ internal.priority = internal.priority - (priority > 0 ? 1 : 0);
1247
637
  // Remove subscriber from list
1248
- internalOnCleanUp.subscribers = internalOnCleanUp.subscribers.filter((s) => s.callback !== callback);
638
+ internal.subscribers = internal.subscribers.filter((s) => s.callback !== callback);
1249
639
  }
1250
640
  };
1251
641
  },
1252
642
  },
1253
- setPerformanceCurrent: this.setPerformanceCurrent,
1254
- setEvents: this.setEvents,
1255
- setFrameloop: this.setFrameloop,
1256
- setSize: this.setSize,
1257
- setDpr: this.setDpr,
1258
- setCamera: this.setCamera,
1259
- addInteraction: this.addInteraction,
1260
- removeInteraction: this.removeInteraction,
643
+ setEvents: (events) => {
644
+ this.set((state) => ({ events: { ...state.events, ...events } }));
645
+ },
646
+ setSize: (width, height, top, left) => {
647
+ const camera = this.get('camera');
648
+ const size = { width, height, top: top || 0, left: left || 0 };
649
+ this.set((state) => ({
650
+ size,
651
+ viewport: { ...state.viewport, ...getCurrentViewport(camera, defaultTarget, size) },
652
+ }));
653
+ },
654
+ setDpr: (dpr) => {
655
+ const resolved = makeDpr(dpr, this.window);
656
+ this.set((state) => ({
657
+ viewport: {
658
+ ...state.viewport,
659
+ dpr: resolved,
660
+ initialDpr: state.viewport.initialDpr || resolved,
661
+ },
662
+ }));
663
+ },
664
+ setFrameloop: (frameloop = 'always') => {
665
+ const clock = this.get('clock');
666
+ clock.stop();
667
+ clock.elapsedTime = 0;
668
+ if (frameloop !== 'never') {
669
+ clock.start();
670
+ clock.elapsedTime = 0;
671
+ }
672
+ this.set({ frameloop });
673
+ },
674
+ addInteraction: (interaction) => {
675
+ this.set((state) => ({
676
+ internal: { ...state.internal, interaction: [...state.internal.interaction, interaction] },
677
+ }));
678
+ },
679
+ removeInteraction: (uuid) => {
680
+ this.set((state) => ({
681
+ internal: {
682
+ ...state.internal,
683
+ interaction: state.internal.interaction.filter((interaction) => interaction.uuid !== uuid),
684
+ },
685
+ }));
686
+ },
1261
687
  });
1262
688
  this.isInit = true;
689
+ this.resize();
1263
690
  }
1264
691
  }
1265
692
  configure(inputs, canvasElement) {
1266
- const { gl: glOptions, size: sizeOptions, camera: cameraOptions, raycaster: raycasterOptions, events, orthographic, lookAt, shadows, linear, flat, legacy, dpr, frameloop, performance, } = inputs;
1267
- const state = this.read();
1268
- // Set up renderer (one time only!)
693
+ const { gl: glOptions, size: sizeOptions, camera: cameraOptions, raycaster: raycasterOptions, events, orthographic, lookAt, shadows, linear, legacy, flat, dpr, frameloop, performance, } = inputs;
694
+ const state = this.get();
695
+ const stateToUpdate = {};
696
+ // setup renderer
1269
697
  let gl = state.gl;
1270
698
  if (!state.gl) {
1271
- this.write({ gl: (gl = createRenderer(glOptions, canvasElement)) });
699
+ stateToUpdate.gl = gl = makeDefaultRenderer(glOptions, canvasElement);
1272
700
  }
1273
- // Set up raycaster (one time only!)
701
+ // setup raycaster
1274
702
  let raycaster = state.raycaster;
1275
703
  if (!raycaster) {
1276
- this.write({ raycaster: (raycaster = new THREE.Raycaster()) });
704
+ stateToUpdate.raycaster = raycaster = new THREE.Raycaster();
1277
705
  }
1278
- // Set raycaster options
706
+ // set raycaster options
1279
707
  const { params, ...options } = raycasterOptions || {};
1280
- if (!is.equ(options, raycaster, shallowLoose)) {
708
+ if (!is.equ(options, raycaster, shallowLoose))
1281
709
  applyProps(raycaster, { ...options });
1282
- }
1283
710
  if (!is.equ(params, raycaster.params, shallowLoose)) {
1284
- applyProps(raycaster, {
1285
- params: { ...raycaster.params, ...(params || {}) },
1286
- });
711
+ applyProps(raycaster, { params: { ...raycaster.params, ...(params || {}) } });
1287
712
  }
1288
- // Create default camera (one time only!)
713
+ // create default camera
1289
714
  if (!state.camera) {
1290
715
  const isCamera = is.camera(cameraOptions);
1291
- let camera = isCamera ? cameraOptions : createDefaultCamera(orthographic, state.size);
716
+ let camera = isCamera ? cameraOptions : makeDefaultCamera(orthographic || false, state.size);
1292
717
  if (!isCamera) {
1293
- if (cameraOptions) {
718
+ if (cameraOptions)
1294
719
  applyProps(camera, cameraOptions);
1295
- }
1296
- // Set position.z if position not passed in
1297
- if (!cameraOptions?.position) {
720
+ // set position.z
721
+ if (!cameraOptions?.position)
1298
722
  camera.position.z = 5;
1299
- }
1300
- // Always look at center or passed-in lookAt by default
723
+ // always look at center or passed-in lookAt by default
1301
724
  if (!cameraOptions?.rotation) {
1302
- if (Array.isArray(lookAt)) {
725
+ if (Array.isArray(lookAt))
1303
726
  camera.lookAt(lookAt[0], lookAt[1], lookAt[2]);
1304
- }
1305
- else if (lookAt instanceof THREE.Vector3) {
727
+ else if (lookAt instanceof THREE.Vector3)
1306
728
  camera.lookAt(lookAt);
1307
- }
1308
- else {
729
+ else
1309
730
  camera.lookAt(0, 0, 0);
1310
- }
1311
731
  }
1312
- // Update projection matrix after applying props
732
+ // update projection matrix after applyprops
1313
733
  camera.updateProjectionMatrix();
1314
734
  }
1315
735
  if (!is.instance(camera)) {
1316
- camera = prepare(camera, this.read, this.rootStateFactory, this.parentStore?.get((s) => s.cameraRef));
736
+ camera = prepare(camera, { store: this });
1317
737
  }
1318
- this.write({ camera });
1319
- this.read((s) => s.cameraRef).set(camera);
738
+ stateToUpdate.camera = camera;
1320
739
  }
1321
740
  // Set up XR (one time only!)
1322
741
  if (!state.xr) {
1323
742
  // Handle frame behavior in WebXR
1324
743
  const handleXRFrame = (timestamp, frame) => {
1325
- const state = this.read();
744
+ const state = this.get();
1326
745
  if (state.frameloop === 'never')
1327
746
  return;
1328
- advance(timestamp, true, this.read, frame);
747
+ advance(timestamp, true, this, frame);
1329
748
  };
1330
749
  // Toggle render switching on session
1331
750
  const handleSessionChange = () => {
1332
- const state = this.read();
751
+ const state = this.get();
1333
752
  state.gl.xr.enabled = state.gl.xr.isPresenting;
1334
753
  state.gl.xr.setAnimationLoop(state.gl.xr.isPresenting ? handleXRFrame : null);
1335
754
  if (!state.gl.xr.isPresenting)
1336
- invalidate(this.read);
755
+ invalidate(this);
1337
756
  };
1338
757
  // WebXR session manager
1339
758
  const xr = {
1340
759
  connect: () => {
1341
- const gl = this.read((s) => s.gl);
1342
760
  gl.xr.addEventListener('sessionstart', handleSessionChange);
1343
761
  gl.xr.addEventListener('sessionend', handleSessionChange);
1344
762
  },
1345
763
  disconnect: () => {
1346
- const gl = this.read((s) => s.gl);
1347
764
  gl.xr.removeEventListener('sessionstart', handleSessionChange);
1348
765
  gl.xr.removeEventListener('sessionend', handleSessionChange);
1349
766
  },
@@ -1351,7 +768,7 @@ class NgtStore extends NgtComponentStore {
1351
768
  // Subscribe to WebXR session events
1352
769
  if (gl.xr)
1353
770
  xr.connect();
1354
- this.write({ xr });
771
+ stateToUpdate.xr = xr;
1355
772
  }
1356
773
  // Set shadowmap
1357
774
  if (gl.shadowMap) {
@@ -1369,9 +786,8 @@ class NgtStore extends NgtComponentStore {
1369
786
  }
1370
787
  // Safely set color management if available.
1371
788
  // Avoid accessing THREE.ColorManagement to play nice with older versions
1372
- if (is.supportColorManagement()) {
789
+ if (THREE.ColorManagement)
1373
790
  THREE.ColorManagement.legacyMode = state.legacy;
1374
- }
1375
791
  const outputEncoding = linear ? THREE.LinearEncoding : THREE.sRGBEncoding;
1376
792
  const toneMapping = flat ? THREE.NoToneMapping : THREE.ACESFilmicToneMapping;
1377
793
  if (gl.outputEncoding !== outputEncoding)
@@ -1380,71 +796,485 @@ class NgtStore extends NgtComponentStore {
1380
796
  gl.toneMapping = toneMapping;
1381
797
  // Update color management state
1382
798
  if (state.legacy !== legacy)
1383
- this.write({ legacy });
799
+ stateToUpdate.legacy = legacy;
1384
800
  if (state.linear !== linear)
1385
- this.write({ linear });
801
+ stateToUpdate.linear = linear;
1386
802
  if (state.flat !== flat)
1387
- this.write({ flat });
803
+ stateToUpdate.flat = flat;
1388
804
  // Set gl props
1389
805
  gl.setClearAlpha(0);
1390
806
  gl.setPixelRatio(makeDpr(state.viewport.dpr));
1391
- gl.setSize(state.size.width, state.size.height, state.size.updateStyle);
807
+ gl.setSize(state.size.width, state.size.height);
1392
808
  if (is.obj(glOptions) &&
1393
809
  !(typeof glOptions === 'function') &&
1394
- !is.glRenderer(glOptions) &&
810
+ !is.renderer(glOptions) &&
1395
811
  !is.equ(glOptions, gl, shallowLoose)) {
1396
812
  applyProps(gl, glOptions);
1397
813
  }
1398
814
  // Store events internally
1399
815
  if (events && !state.events.handlers) {
1400
- this.write({ events: events(this.read) });
816
+ stateToUpdate.events = events(this);
1401
817
  }
818
+ // Check performance
819
+ if (performance && !is.equ(performance, state.performance, shallowLoose)) {
820
+ stateToUpdate.performance = { ...state.performance, ...performance };
821
+ }
822
+ this.set(stateToUpdate);
1402
823
  // Check pixelratio
1403
- if (dpr && state.viewport.dpr !== makeDpr(dpr)) {
824
+ if (dpr && state.viewport.dpr !== makeDpr(dpr))
1404
825
  state.setDpr(dpr);
1405
- }
1406
826
  // Check size, allow it to take on container bounds initially
1407
827
  const size = computeInitialSize(canvasElement, sizeOptions);
1408
- if (!is.equ(size, state.size, shallowLoose)) {
828
+ if (!is.equ(size, state.size, shallowLoose))
1409
829
  state.setSize(size.width, size.height, size.top, size.left);
1410
- }
1411
830
  // Check frameloop
1412
831
  if (state.frameloop !== frameloop)
1413
832
  state.setFrameloop(frameloop);
1414
- // Check performance
1415
- if (performance && !is.equ(performance, state.performance, shallowLoose)) {
1416
- this.write((state) => ({
1417
- performance: { ...state.performance, ...performance },
1418
- }));
1419
- }
1420
- this.write({ ready: true });
1421
- this.resize(this.select(this.select((s) => s.size), this.select((s) => s.viewport.dpr), defaultProjector, { debounce: true }));
1422
- this.invalidate(this.select((s) => s, { debounce: true }));
1423
- this.zone.run(() => {
1424
- this.isConfigured = true;
833
+ if (!this.get('ready')) {
834
+ this.set({ ready: true });
835
+ }
836
+ this.invalidate();
837
+ }
838
+ resize() {
839
+ const state = this.get();
840
+ let oldSize = state.size;
841
+ let oldDpr = state.viewport.dpr;
842
+ let oldCamera = state.camera;
843
+ this.hold(this.select(selectSlice(['camera', 'size', 'viewport', 'gl'])), () => {
844
+ const { camera, size, viewport, gl, set } = this.get();
845
+ // resize camera and renderer on changes to size and dpr
846
+ if (size !== oldSize || viewport.dpr !== oldDpr) {
847
+ oldSize = size;
848
+ oldDpr = viewport.dpr;
849
+ // update camera
850
+ updateCamera(camera, size);
851
+ gl.setPixelRatio(viewport.dpr);
852
+ gl.setSize(size.width, size.height);
853
+ }
854
+ // update viewport when camera changes
855
+ if (camera !== oldCamera) {
856
+ updateCamera(camera, size);
857
+ oldCamera = camera;
858
+ set((state) => ({ viewport: { ...state.viewport, ...state.viewport.getCurrentViewport(camera) } }));
859
+ }
1425
860
  });
1426
861
  }
1427
- onReady(cb) {
1428
- return this.effect(tapEffect(cb))(this.select((s) => s.ready).pipe(filter((ready) => ready)));
862
+ invalidate() {
863
+ this.hold(this.select(), () => invalidate(this));
1429
864
  }
1430
865
  }
1431
- NgtStore.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.0.0", ngImport: i0, type: NgtStore, deps: null, target: i0.ɵɵFactoryTarget.Injectable });
1432
- NgtStore.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "15.0.0", ngImport: i0, type: NgtStore });
1433
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.0.0", ngImport: i0, type: NgtStore, decorators: [{
866
+ NgtStore.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.1.1", ngImport: i0, type: NgtStore, deps: null, target: i0.ɵɵFactoryTarget.Injectable });
867
+ NgtStore.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "15.1.1", ngImport: i0, type: NgtStore });
868
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.1.1", ngImport: i0, type: NgtStore, decorators: [{
1434
869
  type: Injectable
1435
870
  }] });
1436
871
  function computeInitialSize(canvas, defaultSize) {
1437
872
  if (defaultSize) {
1438
873
  return defaultSize;
1439
874
  }
1440
- if (is.canvas(canvas) && canvas.parentElement) {
875
+ if (canvas instanceof HTMLCanvasElement && canvas.parentElement) {
1441
876
  const { width, height, top, left } = canvas.parentElement.getBoundingClientRect();
1442
877
  return { width, height, top, left };
1443
878
  }
1444
- return { width: 0, height: 0, top: 0, left: 0 };
879
+ return { width: 0, height: 0, top: 0, left: 0 };
880
+ }
881
+
882
+ const NGT_COMPOUND_PREFIXES = new InjectionToken('NgtCompoundPrefixes');
883
+
884
+ const catalogue = {};
885
+ function extend(objects) {
886
+ Object.assign(catalogue, objects);
887
+ }
888
+ const NGT_CATALOGUE = new InjectionToken('THREE Constructors Catalogue', { factory: () => catalogue });
889
+
890
+ class NgtArgs {
891
+ constructor() {
892
+ this.vcr = inject(ViewContainerRef);
893
+ this.template = inject(TemplateRef);
894
+ this.injectedArgs = [];
895
+ this.injected = false;
896
+ const commentNode = this.vcr.element.nativeElement;
897
+ if (commentNode['__ngt_renderer_add_comment__']) {
898
+ commentNode['__ngt_renderer_add_comment__']();
899
+ delete commentNode['__ngt_renderer_add_comment__'];
900
+ }
901
+ }
902
+ set args(args) {
903
+ if (args == null || !Array.isArray(args))
904
+ return;
905
+ if (args.length === 1 && args[0] === null)
906
+ return;
907
+ this.injected = false;
908
+ this.injectedArgs = args;
909
+ this.createView();
910
+ }
911
+ get args() {
912
+ if (this.validate()) {
913
+ this.injected = true;
914
+ return this.injectedArgs;
915
+ }
916
+ return null;
917
+ }
918
+ validate() {
919
+ return !this.injected && !!this.injectedArgs.length;
920
+ }
921
+ createView() {
922
+ if (this.view && !this.view.destroyed) {
923
+ this.view.destroy();
924
+ }
925
+ this.view = this.vcr.createEmbeddedView(this.template);
926
+ this.view.detectChanges();
927
+ }
928
+ }
929
+ NgtArgs.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.1.1", ngImport: i0, type: NgtArgs, deps: [], target: i0.ɵɵFactoryTarget.Directive });
930
+ NgtArgs.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "15.1.1", type: NgtArgs, isStandalone: true, selector: "[args]", inputs: { args: "args" }, ngImport: i0 });
931
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.1.1", ngImport: i0, type: NgtArgs, decorators: [{
932
+ type: Directive,
933
+ args: [{ selector: '[args]', standalone: true }]
934
+ }], ctorParameters: function () { return []; }, propDecorators: { args: [{
935
+ type: Input
936
+ }] } });
937
+
938
+ function releaseInternalPointerCapture(capturedMap, obj, captures, pointerId) {
939
+ const captureData = captures.get(obj);
940
+ if (captureData) {
941
+ captures.delete(obj);
942
+ // if this was the last captured object for this pointer
943
+ if (captures.size === 0) {
944
+ capturedMap.delete(pointerId);
945
+ captureData.target.releasePointerCapture(pointerId);
946
+ }
947
+ }
948
+ }
949
+ function removeInteractivity(store, object) {
950
+ const internal = store.get('internal');
951
+ // removes every trace of an object from data store
952
+ internal.interaction = internal.interaction.filter((o) => o !== object);
953
+ internal.initialHits = internal.initialHits.filter((o) => o !== object);
954
+ internal.hovered.forEach((value, key) => {
955
+ if (value.eventObject === object || value.object === object) {
956
+ // clear out intersects, they are outdated by now
957
+ internal.hovered.delete(key);
958
+ }
959
+ });
960
+ internal.capturedMap.forEach((captures, pointerId) => {
961
+ releaseInternalPointerCapture(internal.capturedMap, object, captures, pointerId);
962
+ });
963
+ if (store.get('previousStore')) {
964
+ removeInteractivity(store.get('previousStore'), object);
965
+ }
966
+ }
967
+ function createEvents(store) {
968
+ /** calculates delta **/
969
+ function calculateDistance(event) {
970
+ const internal = store.get('internal');
971
+ const dx = event.offsetX - internal.initialClick[0];
972
+ const dy = event.offsetY - internal.initialClick[1];
973
+ return Math.round(Math.sqrt(dx * dx + dy * dy));
974
+ }
975
+ /** returns true if an instance has a valid pointer-event registered, this excludes scroll, clicks etc... **/
976
+ function filterPointerEvents(objects) {
977
+ return objects.filter((obj) => ['move', 'over', 'enter', 'out', 'leave'].some((name) => {
978
+ const eventName = `pointer${name}`;
979
+ return getLocalState(obj).handlers?.[eventName];
980
+ }));
981
+ }
982
+ function intersect(event, filter) {
983
+ const state = store.get();
984
+ const duplicates = new Set();
985
+ const intersections = [];
986
+ // allow callers to eliminate event objects
987
+ const eventObjects = filter ? filter(state.internal.interaction) : state.internal.interaction;
988
+ // reset all raycaster cameras to undefined
989
+ for (let i = 0; i < eventObjects.length; i++) {
990
+ const instanceState = getLocalState(eventObjects[i]).store?.get();
991
+ if (instanceState) {
992
+ instanceState.raycaster.camera = undefined;
993
+ }
994
+ }
995
+ if (!state.previousStore) {
996
+ // make sure root-level pointer and ray are setup
997
+ state.events.compute?.(event, store);
998
+ }
999
+ function handleRaycast(obj) {
1000
+ const objLocalState = getLocalState(obj);
1001
+ const objStore = objLocalState.store;
1002
+ const objState = objStore?.get();
1003
+ // skip event handling when noEvents is set, or when raycaster camera is null
1004
+ if (!objState || !objState.events.enabled || objState.raycaster.camera === null)
1005
+ return [];
1006
+ // when the camera is undefined, we have to call the events layers to update function
1007
+ if (objState.raycaster.camera === undefined) {
1008
+ objState.events.compute?.(event, objStore, objState.previousStore);
1009
+ // if the camera is still undefined, we have to skip this layer entirely
1010
+ if (objState.raycaster.camera === undefined)
1011
+ objState.raycaster.camera = null;
1012
+ }
1013
+ // intersect object by object
1014
+ return objState.raycaster.camera ? objState.raycaster.intersectObject(obj, true) : [];
1015
+ }
1016
+ // collect events
1017
+ let hits = eventObjects
1018
+ // intersect objects
1019
+ .flatMap(handleRaycast)
1020
+ // sort by event priority
1021
+ .sort((a, b) => {
1022
+ const aState = getLocalState(a.object).store.get();
1023
+ const bState = getLocalState(b.object).store.get();
1024
+ if (!aState || !bState)
1025
+ return a.distance - b.distance;
1026
+ return bState.events.priority - aState.events.priority || a.distance - b.distance;
1027
+ })
1028
+ // filter out duplicates
1029
+ .filter((item) => {
1030
+ const id = makeId(item);
1031
+ if (duplicates.has(id))
1032
+ return false;
1033
+ duplicates.add(id);
1034
+ return true;
1035
+ });
1036
+ // allow custom userland intersect sort order, this likely only makes sense on the root
1037
+ if (state.events.filter)
1038
+ hits = state.events.filter(hits, store);
1039
+ // bubble up the events, find the event source
1040
+ for (const hit of hits) {
1041
+ let eventObject = hit.object;
1042
+ // bubble event up
1043
+ while (eventObject) {
1044
+ if (getLocalState(eventObject).eventCount) {
1045
+ intersections.push({ ...hit, eventObject });
1046
+ }
1047
+ eventObject = eventObject.parent;
1048
+ }
1049
+ }
1050
+ // if the interaction is captured, make all capturing targets part of the intersects
1051
+ if ('pointerId' in event && state.internal.capturedMap.has(event.pointerId)) {
1052
+ for (const capturedData of state.internal.capturedMap.get(event.pointerId).values()) {
1053
+ if (!duplicates.has(makeId(capturedData.intersection))) {
1054
+ intersections.push(capturedData.intersection);
1055
+ }
1056
+ }
1057
+ }
1058
+ return intersections;
1059
+ }
1060
+ /** handle intersections by forwarding them to handlers */
1061
+ function handleIntersects(intersections, event, delta, callback) {
1062
+ const rootState = store.get();
1063
+ // if anything has been found, forward it to the event listeners
1064
+ if (intersections.length) {
1065
+ const localState = { stopped: false };
1066
+ for (const hit of intersections) {
1067
+ const state = getLocalState(hit.object).store?.get() || rootState;
1068
+ const { raycaster, pointer, camera, internal } = state;
1069
+ const unprojectedPoint = new THREE.Vector3(pointer.x, pointer.y, 0).unproject(camera);
1070
+ const hasPointerCapture = (id) => internal.capturedMap.get(id)?.has(hit.eventObject) ?? false;
1071
+ const setPointerCapture = (id) => {
1072
+ const captureData = { intersection: hit, target: event.target };
1073
+ if (internal.capturedMap.has(id)) {
1074
+ // if the pointerId was previously captured, we add the hit to the event capturedMap
1075
+ internal.capturedMap.get(id).set(hit.eventObject, captureData);
1076
+ }
1077
+ else {
1078
+ // if the pointerId was not previously captured, we create a Map containing the hitObject, and the hit. hitObject is used for faster access
1079
+ internal.capturedMap.set(id, new Map([[hit.eventObject, captureData]]));
1080
+ }
1081
+ // call the original event now
1082
+ event.target.setPointerCapture(id);
1083
+ };
1084
+ const releasePointerCapture = (id) => {
1085
+ const captures = internal.capturedMap.get(id);
1086
+ if (captures) {
1087
+ releaseInternalPointerCapture(internal.capturedMap, hit.eventObject, captures, id);
1088
+ }
1089
+ };
1090
+ // add native event props
1091
+ const extractEventProps = {};
1092
+ // This iterates over the event's properties including the inherited ones. Native PointerEvents have most of their props as getters which are inherited, but polyfilled PointerEvents have them all as their own properties (i.e. not inherited). We can't use Object.keys() or Object.entries() as they only return "own" properties; nor Object.getPrototypeOf(event) as that *doesn't* return "own" properties, only inherited ones.
1093
+ for (const prop in event) {
1094
+ const property = event[prop];
1095
+ // only copy over atomics, leave functions alone as these should be called as event.nativeEvent.fn()
1096
+ if (typeof property !== 'function') {
1097
+ extractEventProps[prop] = property;
1098
+ }
1099
+ }
1100
+ const raycastEvent = {
1101
+ ...hit,
1102
+ ...extractEventProps,
1103
+ pointer,
1104
+ intersections,
1105
+ stopped: localState.stopped,
1106
+ delta,
1107
+ unprojectedPoint,
1108
+ ray: raycaster.ray,
1109
+ camera: camera,
1110
+ // Hijack stopPropagation, which just sets a flag
1111
+ stopPropagation() {
1112
+ // https://github.com/pmndrs/react-three-fiber/issues/596
1113
+ // Events are not allowed to stop propagation if the pointer has been captured
1114
+ const capturesForPointer = 'pointerId' in event && internal.capturedMap.get(event.pointerId);
1115
+ // We only authorize stopPropagation...
1116
+ if (
1117
+ // ...if this pointer hasn't been captured
1118
+ !capturesForPointer ||
1119
+ // ... or if the hit object is capturing the pointer
1120
+ capturesForPointer.has(hit.eventObject)) {
1121
+ raycastEvent.stopped = localState.stopped = true;
1122
+ // Propagation is stopped, remove all other hover records
1123
+ // An event handler is only allowed to flush other handlers if it is hovered itself
1124
+ if (internal.hovered.size &&
1125
+ Array.from(internal.hovered.values()).find((i) => i.eventObject === hit.eventObject)) {
1126
+ // Objects cannot flush out higher up objects that have already caught the event
1127
+ const higher = intersections.slice(0, intersections.indexOf(hit));
1128
+ cancelPointer([...higher, hit]);
1129
+ }
1130
+ }
1131
+ },
1132
+ // there should be a distinction between target and currentTarget
1133
+ target: { hasPointerCapture, setPointerCapture, releasePointerCapture },
1134
+ currentTarget: { hasPointerCapture, setPointerCapture, releasePointerCapture },
1135
+ nativeEvent: event,
1136
+ };
1137
+ // call subscribers
1138
+ callback(raycastEvent);
1139
+ // event bubbling may be interupted by stopPropagation
1140
+ if (localState.stopped === true)
1141
+ break;
1142
+ }
1143
+ }
1144
+ return intersections;
1145
+ }
1146
+ function cancelPointer(intersections) {
1147
+ const { internal } = store.get();
1148
+ for (const hoveredObj of internal.hovered.values()) {
1149
+ // When no objects were hit or the hovered object wasn't found underneath the cursor
1150
+ // we call onPointerOut and delete the object from the hovered-elements map
1151
+ if (!intersections.length ||
1152
+ !intersections.find((hit) => hit.object === hoveredObj.object &&
1153
+ hit.index === hoveredObj.index &&
1154
+ hit.instanceId === hoveredObj.instanceId)) {
1155
+ const eventObject = hoveredObj.eventObject;
1156
+ const instance = getLocalState(eventObject);
1157
+ const handlers = instance?.handlers;
1158
+ internal.hovered.delete(makeId(hoveredObj));
1159
+ if (instance?.eventCount) {
1160
+ // Clear out intersects, they are outdated by now
1161
+ const data = { ...hoveredObj, intersections };
1162
+ handlers?.pointerout?.(data);
1163
+ handlers?.pointerleave?.(data);
1164
+ }
1165
+ }
1166
+ }
1167
+ }
1168
+ function pointerMissed(event, objects) {
1169
+ for (let i = 0; i < objects.length; i++) {
1170
+ const instance = getLocalState(objects[i]);
1171
+ instance?.handlers.pointermissed?.(event);
1172
+ }
1173
+ }
1174
+ function handlePointer(name) {
1175
+ // Deal with cancelation
1176
+ switch (name) {
1177
+ case 'pointerleave':
1178
+ case 'pointercancel':
1179
+ return () => cancelPointer([]);
1180
+ case 'lostpointercapture':
1181
+ return (event) => {
1182
+ const { internal } = store.get();
1183
+ if ('pointerId' in event && !internal.capturedMap.has(event.pointerId)) {
1184
+ // If the object event interface had onLostPointerCapture, we'd call it here on every
1185
+ // object that's getting removed.
1186
+ internal.capturedMap.delete(event.pointerId);
1187
+ cancelPointer([]);
1188
+ }
1189
+ };
1190
+ }
1191
+ // Any other pointer goes here ...
1192
+ return function handleEvent(event) {
1193
+ const { onPointerMissed, internal } = store.get();
1194
+ // prepareRay(event)
1195
+ internal.lastEvent = event;
1196
+ // Get fresh intersects
1197
+ const isPointerMove = name === 'pointermove';
1198
+ const isClickEvent = name === 'click' || name === 'contextmenu' || name === 'dblclick';
1199
+ const filter = isPointerMove ? filterPointerEvents : undefined;
1200
+ // const hits = patchIntersects(intersect(filter), event)
1201
+ const hits = intersect(event, filter);
1202
+ const delta = isClickEvent ? calculateDistance(event) : 0;
1203
+ // Save initial coordinates on pointer-down
1204
+ if (name === 'pointerdown') {
1205
+ internal.initialClick = [event.offsetX, event.offsetY];
1206
+ internal.initialHits = hits.map((hit) => hit.eventObject);
1207
+ }
1208
+ // If a click yields no results, pass it back to the user as a miss
1209
+ // Missed events have to come first in order to establish user-land side-effect clean up
1210
+ if (isClickEvent && !hits.length) {
1211
+ if (delta <= 2) {
1212
+ pointerMissed(event, internal.interaction);
1213
+ if (onPointerMissed)
1214
+ onPointerMissed(event);
1215
+ }
1216
+ }
1217
+ // Take care of unhover
1218
+ if (isPointerMove)
1219
+ cancelPointer(hits);
1220
+ function onIntersect(data) {
1221
+ const eventObject = data.eventObject;
1222
+ const instance = getLocalState(eventObject);
1223
+ const handlers = instance?.handlers;
1224
+ // Check presence of handlers
1225
+ if (!instance?.eventCount)
1226
+ return;
1227
+ if (isPointerMove) {
1228
+ // Move event ...
1229
+ if (handlers?.pointerover ||
1230
+ handlers?.pointerenter ||
1231
+ handlers?.pointerout ||
1232
+ handlers?.pointerleave) {
1233
+ // When enter or out is present take care of hover-state
1234
+ const id = makeId(data);
1235
+ const hoveredItem = internal.hovered.get(id);
1236
+ if (!hoveredItem) {
1237
+ // If the object wasn't previously hovered, book it and call its handler
1238
+ internal.hovered.set(id, data);
1239
+ handlers.pointerover?.(data);
1240
+ handlers.pointerenter?.(data);
1241
+ }
1242
+ else if (hoveredItem.stopped) {
1243
+ // If the object was previously hovered and stopped, we shouldn't allow other items to proceed
1244
+ data.stopPropagation();
1245
+ }
1246
+ }
1247
+ // Call mouse move
1248
+ handlers?.pointermove?.(data);
1249
+ }
1250
+ else {
1251
+ // All other events ...
1252
+ const handler = handlers?.[name];
1253
+ if (handler) {
1254
+ // Forward all events back to their respective handlers with the exception of click events,
1255
+ // which must use the initial target
1256
+ if (!isClickEvent || internal.initialHits.includes(eventObject)) {
1257
+ // Missed events have to come first
1258
+ pointerMissed(event, internal.interaction.filter((object) => !internal.initialHits.includes(object)));
1259
+ // Now call the handler
1260
+ handler(data);
1261
+ }
1262
+ }
1263
+ else {
1264
+ // Trigger onPointerMissed on all elements that have pointer over/out handlers, but not click and weren't hit
1265
+ if (isClickEvent && internal.initialHits.includes(eventObject)) {
1266
+ pointerMissed(event, internal.interaction.filter((object) => !internal.initialHits.includes(object)));
1267
+ }
1268
+ }
1269
+ }
1270
+ }
1271
+ handleIntersects(hits, event, delta, onIntersect);
1272
+ };
1273
+ }
1274
+ return { handlePointer };
1445
1275
  }
1446
1276
 
1447
- function mutate(object, value, paths = []) {
1277
+ function attach(object, value, paths = []) {
1448
1278
  const [base, ...remaining] = paths;
1449
1279
  if (!base)
1450
1280
  return;
@@ -1452,751 +1282,1150 @@ function mutate(object, value, paths = []) {
1452
1282
  applyProps(object, { [base]: value });
1453
1283
  }
1454
1284
  else {
1455
- // assign an empty object in order to spread object if undefined
1456
1285
  assignEmpty(object, base);
1457
- // recursion
1458
- mutate(object[base], value, remaining);
1286
+ attach(object[base], value, remaining);
1287
+ }
1288
+ }
1289
+ function detach(parent, child, attachProp) {
1290
+ const childLocalState = getLocalState(child);
1291
+ if (Array.isArray(attachProp)) {
1292
+ attach(parent, childLocalState.previousAttach, attachProp);
1293
+ }
1294
+ else {
1295
+ childLocalState.previousAttach();
1459
1296
  }
1460
1297
  }
1461
1298
  function assignEmpty(obj, base) {
1462
- if (
1463
- // @ts-ignore
1464
- (!Object.hasOwn(obj, base) && Reflect && !!Reflect.has && !Reflect.has(obj, base)) ||
1465
- obj[base] === undefined) {
1299
+ if ((!Object.hasOwn(obj, base) && Reflect && !!Reflect.has && !Reflect.has(obj, base)) || obj[base] === undefined) {
1466
1300
  obj[base] = {};
1467
1301
  }
1468
1302
  }
1469
1303
 
1470
- const NGT_PROXY_INSTANCE = Symbol.for('__ngt__proxy__instance__');
1471
- const NGT_INSTANCE_REF_FACTORY = new InjectionToken('NgtInstance[instanceRef] factory');
1472
- function provideInstanceRef(ctor, factory) {
1473
- return {
1474
- provide: NGT_INSTANCE_REF_FACTORY,
1475
- useFactory: (instance) => {
1476
- if (factory) {
1477
- return () => factory(instance);
1304
+ const SPECIAL_DOM_TAG = {
1305
+ NGT_PORTAL: 'ngt-portal',
1306
+ NGT_PRIMITIVE: 'ngt-primitive',
1307
+ NGT_VALUE: 'ngt-value',
1308
+ };
1309
+ const SPECIAL_PROPERTIES = {
1310
+ COMPOUND: 'ngtCompound',
1311
+ RENDER_PRIORITY: 'priority',
1312
+ ATTACH: 'attach',
1313
+ VALUE: 'rawValue',
1314
+ REF: 'ref',
1315
+ };
1316
+ const SPECIAL_EVENTS = {
1317
+ BEFORE_RENDER: 'beforeRender',
1318
+ AFTER_UPDATE: 'afterUpdate',
1319
+ AFTER_ATTACH: 'afterAttach',
1320
+ };
1321
+ function attachThreeChild(parent, child) {
1322
+ const pLS = getLocalState(parent);
1323
+ const cLS = getLocalState(child);
1324
+ if (!pLS || !cLS) {
1325
+ throw new Error(`[NGT] THREE instances need to be prepared with local state.`);
1326
+ }
1327
+ // whether the child is added to the parent with parent.add()
1328
+ let added = false;
1329
+ // assign store on child if not already exist
1330
+ if (!cLS.store) {
1331
+ cLS.store = pLS.store;
1332
+ }
1333
+ if (cLS.attach) {
1334
+ const attachProp = cLS.attach;
1335
+ if (typeof attachProp === 'function') {
1336
+ const attachCleanUp = attachProp(parent, child, cLS.store);
1337
+ if (attachCleanUp)
1338
+ cLS.previousAttach = attachCleanUp;
1339
+ }
1340
+ else {
1341
+ // we skip attach none if set explicitly
1342
+ if (attachProp[0] === 'none') {
1343
+ invalidateInstance(child);
1344
+ return;
1478
1345
  }
1479
- return () => instance['instanceRef'] || instance['instance']['instanceRef'];
1480
- },
1481
- deps: [ctor],
1346
+ // handle material array
1347
+ if (attachProp[0] === 'material' &&
1348
+ attachProp[1] &&
1349
+ typeof Number(attachProp[1]) === 'number' &&
1350
+ is.material(child) &&
1351
+ !Array.isArray(parent['material'])) {
1352
+ parent['material'] = [];
1353
+ }
1354
+ // attach
1355
+ if (cLS.isRaw) {
1356
+ cLS.parent = parent;
1357
+ // at this point we don't have rawValue yet, so we bail and wait until the Renderer recalls attach
1358
+ if (child.__ngt_renderer__[10 /* NgtRendererClassId.rawValue */] === undefined)
1359
+ return;
1360
+ attach(parent, child.__ngt_renderer__[10 /* NgtRendererClassId.rawValue */], attachProp);
1361
+ }
1362
+ else {
1363
+ attach(parent, child, attachProp);
1364
+ }
1365
+ // save value
1366
+ cLS.previousAttach = attachProp.reduce((value, property) => value[property], parent);
1367
+ }
1368
+ }
1369
+ else if (is.object3D(parent) && is.object3D(child)) {
1370
+ parent.add(child);
1371
+ added = true;
1372
+ }
1373
+ pLS.add(child, added ? 'objects' : 'nonObjects');
1374
+ cLS.parent = parent;
1375
+ if (cLS.afterAttach) {
1376
+ cLS.afterAttach.emit({ parent, node: child });
1377
+ }
1378
+ invalidateInstance(child);
1379
+ invalidateInstance(parent);
1380
+ }
1381
+ function removeThreeChild(parent, child, dispose) {
1382
+ const pLS = getLocalState(parent);
1383
+ const cLS = getLocalState(child);
1384
+ // clear parent ref
1385
+ cLS.parent = null;
1386
+ // remove child from parent
1387
+ if (pLS.objects) {
1388
+ pLS.remove(child, 'objects');
1389
+ }
1390
+ if (pLS.nonObjects) {
1391
+ pLS.remove(child, 'nonObjects');
1392
+ }
1393
+ if (cLS.attach) {
1394
+ detach(parent, child, cLS.attach);
1395
+ }
1396
+ else if (is.object3D(parent) && is.object3D(child)) {
1397
+ parent.remove(child);
1398
+ removeInteractivity(cLS.store || pLS.store, child);
1399
+ }
1400
+ const isPrimitive = cLS.primitive;
1401
+ if (!isPrimitive) {
1402
+ removeThreeRecursive(cLS.objects?.value || [], child, !!dispose);
1403
+ removeThreeRecursive(child.childre, child, !!dispose);
1404
+ }
1405
+ // dispose
1406
+ if (child['dispose'] && !is.scene(child)) {
1407
+ queueMicrotask(() => child['dispose']());
1408
+ }
1409
+ invalidateInstance(parent);
1410
+ }
1411
+ function removeThreeRecursive(array, parent, dispose) {
1412
+ if (array)
1413
+ [...array].forEach((child) => removeThreeChild(parent, child, dispose));
1414
+ }
1415
+ function processThreeEvent(instance, priority, eventName, callback, cdr) {
1416
+ const lS = getLocalState(instance);
1417
+ if (eventName === SPECIAL_EVENTS.BEFORE_RENDER) {
1418
+ return lS.store
1419
+ .get('internal')
1420
+ .subscribe((state) => callback({ state, object: instance }), priority || lS.priority || 0);
1421
+ }
1422
+ if (eventName === SPECIAL_EVENTS.AFTER_UPDATE || eventName === SPECIAL_EVENTS.AFTER_ATTACH) {
1423
+ if (!lS[eventName]) {
1424
+ lS[eventName] = new EventEmitter();
1425
+ }
1426
+ const sub = lS[eventName]?.subscribe(callback);
1427
+ return sub.unsubscribe.bind(sub);
1428
+ }
1429
+ if (!lS.handlers) {
1430
+ lS.handlers = {};
1431
+ }
1432
+ // try to get the previous handler. compound might have one, the THREE object might also have one with the same name
1433
+ const previousHandler = lS.handlers[eventName];
1434
+ // readjust the callback
1435
+ const updatedCallback = (event) => {
1436
+ if (previousHandler)
1437
+ previousHandler(event);
1438
+ callback(event);
1439
+ };
1440
+ lS.handlers = {
1441
+ ...lS.handlers,
1442
+ [eventName]: eventToHandler(updatedCallback, cdr),
1443
+ };
1444
+ // increment the count everytime
1445
+ lS.eventCount += 1;
1446
+ // but only add the instance (target) to the interaction array (so that it is handled by the EventManager with Raycast)
1447
+ // the first time eventCount is incremented
1448
+ if (lS.eventCount === 1 && instance['raycast']) {
1449
+ lS.store.get('addInteraction')(instance);
1450
+ }
1451
+ // clean up the event listener by removing the target from the interaction array
1452
+ return () => {
1453
+ const localState = getLocalState(instance);
1454
+ if (localState && localState.eventCount) {
1455
+ localState.store.get('removeInteraction')(instance['uuid']);
1456
+ }
1482
1457
  };
1483
1458
  }
1484
- function injectInstanceRef(options = {}) {
1485
- return inject(NGT_INSTANCE_REF_FACTORY, options);
1459
+ function eventToHandler(callback, cdr) {
1460
+ return (event) => {
1461
+ callback(event);
1462
+ cdr.detectChanges();
1463
+ };
1486
1464
  }
1487
- function injectInstance(options = {}) {
1488
- return inject(NgtInstance, options);
1465
+ function kebabToPascal(str) {
1466
+ // split the string at each hyphen
1467
+ const parts = str.split('-');
1468
+ // map over the parts, capitalizing the first letter of each part
1469
+ const pascalParts = parts.map((part) => {
1470
+ return part.charAt(0).toUpperCase() + part.slice(1);
1471
+ });
1472
+ // join the parts together to create the final PascalCase string
1473
+ return pascalParts.join('');
1489
1474
  }
1490
- const supportedEvents = [
1491
- 'click',
1492
- 'contextmenu',
1493
- 'dblclick',
1494
- 'pointerup',
1495
- 'pointerdown',
1496
- 'pointerover',
1497
- 'pointerout',
1498
- 'pointerenter',
1499
- 'pointerleave',
1500
- 'pointermove',
1501
- 'pointermissed',
1502
- 'pointercancel',
1503
- 'wheel',
1504
- ];
1505
- class NgtInstance extends NgtComponentStore {
1506
- constructor() {
1507
- super(...arguments);
1508
- this.priority = 0;
1509
- this.click = new EventEmitter();
1510
- this.contextmenu = new EventEmitter();
1511
- this.dblclick = new EventEmitter();
1512
- this.pointerup = new EventEmitter();
1513
- this.pointerdown = new EventEmitter();
1514
- this.pointerover = new EventEmitter();
1515
- this.pointerout = new EventEmitter();
1516
- this.pointerenter = new EventEmitter();
1517
- this.pointerleave = new EventEmitter();
1518
- this.pointermove = new EventEmitter();
1519
- this.pointermissed = new EventEmitter();
1520
- this.pointercancel = new EventEmitter();
1521
- this.wheel = new EventEmitter();
1522
- this.zone = inject(NgZone);
1523
- this.store = inject(NgtStore);
1524
- this.parentRef = injectInstanceRef({ skipSelf: true, optional: true });
1525
- this._isRaw = false;
1526
- this.hasEmittedAlready = false;
1527
- this.instanceReady = this.effect(tapEffect(() => {
1528
- this.handleEvents();
1529
- let attachToParentSubscription;
1530
- if (this.parentRef) {
1531
- attachToParentSubscription = this.attachToParent(this.parent.pipe(filter((parent) => !!parent)));
1532
- const { noAttach, skipWrapper } = this.read();
1533
- if (!noAttach && !skipWrapper) {
1534
- const parentInstanceNode = getInstanceLocalState(getInstanceLocalState(this.instanceValue)?.parentRef?.value);
1535
- if (parentInstanceNode) {
1536
- const collections = is.object3d(this.instanceValue)
1537
- ? parentInstanceNode.objectsRefs
1538
- : parentInstanceNode.instancesRefs;
1539
- collections.set((s) => [...s, this.instanceRef]);
1540
- }
1541
- }
1542
- }
1543
- let beforeRenderCleanUp;
1544
- if (this.beforeRender && is.object3d(this.instanceValue) && is.object3d(this.proxyInstance)) {
1545
- beforeRenderCleanUp = this.store
1546
- .read((s) => s.internal)
1547
- .subscribe((state, object) => this.beforeRender(state, object), this.priority, this.store.read, this.proxyInstance);
1548
- }
1549
- if (this.readyCallback)
1550
- this.readyCallback(this.instanceValue);
1551
- return () => {
1552
- beforeRenderCleanUp?.();
1553
- attachToParentSubscription?.unsubscribe();
1475
+
1476
+ class NgtRendererStore {
1477
+ constructor(root) {
1478
+ this.root = root;
1479
+ this.comments = [];
1480
+ this.portals = [];
1481
+ }
1482
+ createNode(type, node) {
1483
+ const state = [
1484
+ type,
1485
+ null,
1486
+ [],
1487
+ false,
1488
+ undefined,
1489
+ undefined,
1490
+ undefined,
1491
+ undefined,
1492
+ undefined,
1493
+ undefined,
1494
+ undefined,
1495
+ undefined,
1496
+ undefined,
1497
+ undefined,
1498
+ ];
1499
+ const rendererNode = Object.assign(node, { __ngt_renderer__: state });
1500
+ if (state[0 /* NgtRendererClassId.type */] === 'comment') {
1501
+ state[13 /* NgtRendererClassId.injectorFactory */] = () => getDebugNode(rendererNode).injector;
1502
+ // we attach an arrow function to the Comment node
1503
+ // In our directives, we can call this function to then start tracking the RendererNode
1504
+ rendererNode['__ngt_renderer_add_comment__'] = () => {
1505
+ this.comments.push(rendererNode);
1554
1506
  };
1555
- }));
1556
- this.handleEvents = this.effect(tapEffect(() => {
1557
- if (this.instanceValue && is.object3d(this.instanceValue)) {
1558
- const observedEvents = supportedEvents.reduce((result, event) => {
1559
- const controllerEvent = this[event].observed ? this[event] : null;
1560
- if (controllerEvent) {
1561
- result.handlers[event] = this.eventNameToHandler(controllerEvent);
1562
- result.eventCount += 1;
1563
- }
1564
- return result;
1565
- }, { handlers: {}, eventCount: 0 });
1566
- // patch __ngt__ with events
1567
- applyProps(this.__ngt__, observedEvents);
1568
- // add as an interaction if there are events observed
1569
- if (observedEvents.eventCount > 0) {
1570
- getInstanceLocalState(this.instanceValue)?.rootFactory().addInteraction(this.instanceValue);
1571
- }
1507
+ return rendererNode;
1508
+ }
1509
+ if (state[0 /* NgtRendererClassId.type */] === 'portal') {
1510
+ state[13 /* NgtRendererClassId.injectorFactory */] = () => getDebugNode(rendererNode).injector;
1511
+ this.portals.push(rendererNode);
1512
+ return rendererNode;
1513
+ }
1514
+ if (state[0 /* NgtRendererClassId.type */] === 'compound') {
1515
+ state[7 /* NgtRendererClassId.queueOps */] = new Set();
1516
+ state[8 /* NgtRendererClassId.attributes */] = {};
1517
+ state[9 /* NgtRendererClassId.properties */] = {};
1518
+ return rendererNode;
1519
+ }
1520
+ return rendererNode;
1521
+ }
1522
+ setParent(node, parent) {
1523
+ if (!node.__ngt_renderer__[1 /* NgtRendererClassId.parent */]) {
1524
+ node.__ngt_renderer__[1 /* NgtRendererClassId.parent */] = parent;
1525
+ }
1526
+ }
1527
+ addChild(node, child) {
1528
+ if (!node.__ngt_renderer__[2 /* NgtRendererClassId.children */].includes(child)) {
1529
+ node.__ngt_renderer__[2 /* NgtRendererClassId.children */].push(child);
1530
+ }
1531
+ }
1532
+ removeChild(node, child) {
1533
+ const index = node.__ngt_renderer__[2 /* NgtRendererClassId.children */].findIndex((c) => child === c);
1534
+ if (index >= 0) {
1535
+ node.__ngt_renderer__[2 /* NgtRendererClassId.children */].splice(index, 1);
1536
+ }
1537
+ }
1538
+ setCompound(compound, instance) {
1539
+ compound.__ngt_renderer__[6 /* NgtRendererClassId.compounded */] = instance;
1540
+ const attributes = Object.keys(compound.__ngt_renderer__[8 /* NgtRendererClassId.attributes */]);
1541
+ const properties = Object.keys(compound.__ngt_renderer__[9 /* NgtRendererClassId.properties */]);
1542
+ if (attributes.length) {
1543
+ for (const key of attributes) {
1544
+ this.applyAttribute(instance, key, compound.__ngt_renderer__[8 /* NgtRendererClassId.attributes */][key]);
1572
1545
  }
1573
- return () => {
1574
- if (is.object3d(this.instanceValue) && this.__ngt__.eventCount > 0) {
1575
- getInstanceLocalState(this.instanceValue)?.rootFactory().removeInteraction(this.instanceValue.uuid);
1576
- }
1577
- };
1578
- }));
1579
- this.attachToParent = this.effect(tap(() => {
1580
- const attach = this.read((s) => s.attach);
1581
- let parentInstanceRef = this.__ngt__?.parentRef;
1582
- // if no parentInstance, try re-run the factory due to late init
1583
- if (!parentInstanceRef || !parentInstanceRef.value) {
1584
- // return early if failed to retrieve
1585
- if (!this.parent?.value)
1586
- return;
1587
- // reassign on instance internal state
1588
- if (this.__ngt__) {
1589
- this.__ngt__.parentRef = this.parent;
1590
- }
1591
- parentInstanceRef = this.parent;
1546
+ }
1547
+ if (properties.length) {
1548
+ for (const key of properties) {
1549
+ this.applyProperty(instance, key, compound.__ngt_renderer__[9 /* NgtRendererClassId.properties */][key]);
1592
1550
  }
1593
- if (typeof attach === 'function') {
1594
- const attachCleanUp = attach(parentInstanceRef, this.instanceRef, this.store.read);
1595
- if (attachCleanUp) {
1596
- this.__ngt__.attach = attachCleanUp;
1551
+ }
1552
+ this.executeOperation(compound);
1553
+ }
1554
+ queueOperation(node, op) {
1555
+ node.__ngt_renderer__[7 /* NgtRendererClassId.queueOps */].add(op);
1556
+ }
1557
+ executeOperation(node, type = 'op') {
1558
+ if (node.__ngt_renderer__[7 /* NgtRendererClassId.queueOps */]?.size) {
1559
+ node.__ngt_renderer__[7 /* NgtRendererClassId.queueOps */].forEach((op) => {
1560
+ if (op[0 /* NgtQueueOpClassId.type */] === type) {
1561
+ op[1 /* NgtQueueOpClassId.op */]();
1562
+ node.__ngt_renderer__[7 /* NgtRendererClassId.queueOps */].delete(op);
1597
1563
  }
1564
+ });
1565
+ }
1566
+ }
1567
+ processPortalContainer(portal) {
1568
+ const injectorFactory = portal.__ngt_renderer__[13 /* NgtRendererClassId.injectorFactory */];
1569
+ const injector = injectorFactory?.();
1570
+ if (!injector)
1571
+ return;
1572
+ const portalStore = injector.get(NgtStore, null);
1573
+ if (!portalStore)
1574
+ return;
1575
+ const portalContainer = portalStore.get('scene');
1576
+ if (!portalContainer)
1577
+ return;
1578
+ portal.__ngt_renderer__[12 /* NgtRendererClassId.portalContainer */] = this.createNode('three', portalContainer);
1579
+ }
1580
+ applyAttribute(node, name, value) {
1581
+ if (node.__ngt_renderer__[3 /* NgtRendererClassId.destroyed */])
1582
+ return;
1583
+ if (name === SPECIAL_PROPERTIES.RENDER_PRIORITY) {
1584
+ // priority needs to be set as an attribute string so that they can be set as early as possible
1585
+ // we convert that string to a number. if it's invalid, 0
1586
+ let priority = Number(value);
1587
+ if (isNaN(priority)) {
1588
+ priority = 0;
1589
+ console.warn(`[NGT] "priority" is an invalid number, default to 0`);
1598
1590
  }
1599
- else {
1600
- const propertyToAttach = [...attach];
1601
- // if propertyToAttach is empty
1602
- if (propertyToAttach.length === 0) {
1603
- // this might be the case where we are attaching an object3D to the parent object3D
1604
- if (is.object3d(this.instanceValue) &&
1605
- is.object3d(this.proxyInstance) &&
1606
- is.object3d(this.parent?.value)) {
1607
- this.parent?.value.add(this.proxyInstance);
1608
- }
1609
- }
1610
- else {
1611
- // array material handling
1612
- if (propertyToAttach[0] === 'material' &&
1613
- propertyToAttach[1] &&
1614
- typeof Number(propertyToAttach[1]) === 'number' &&
1615
- is.material(this.instanceValue)) {
1616
- if (!Array.isArray(parentInstanceRef.value.material)) {
1617
- parentInstanceRef.value.material = [];
1618
- }
1619
- }
1620
- // retrieve the current value on the parentInstance, so we can reset it later
1621
- if (this.__ngt__) {
1622
- this.__ngt__.attachValue = propertyToAttach.reduce((value, property) => value[property], parentInstanceRef.value);
1623
- }
1624
- // attach the instance value on the parent
1625
- mutate(parentInstanceRef.value, this.proxyInstance, propertyToAttach);
1626
- }
1627
- // validate on the instance
1628
- invalidateInstance(this.instanceValue);
1629
- // validate on the parent
1630
- if (getInstanceLocalState(parentInstanceRef.value)) {
1631
- invalidateInstance(parentInstanceRef.value);
1632
- }
1633
- // save the attach
1634
- if (this.__ngt__) {
1635
- this.__ngt__.attach = propertyToAttach;
1636
- }
1637
- this.write({ attach: propertyToAttach });
1591
+ getLocalState(node).priority = priority;
1592
+ }
1593
+ if (name === SPECIAL_PROPERTIES.COMPOUND) {
1594
+ // we set the compound property on instance node now so we know that this instance is being compounded
1595
+ node.__ngt_renderer__[4 /* NgtRendererClassId.compound */] = [value === '' || value === 'first', {}];
1596
+ return;
1597
+ }
1598
+ if (name === SPECIAL_PROPERTIES.ATTACH) {
1599
+ // handle attach as tring
1600
+ const paths = value.split('.');
1601
+ if (paths.length)
1602
+ getLocalState(node).attach = paths;
1603
+ return;
1604
+ }
1605
+ if (name === SPECIAL_PROPERTIES.VALUE) {
1606
+ // TODO the last time we tried this here, this didn't work
1607
+ // coercion
1608
+ let maybeCoerced = value;
1609
+ if (maybeCoerced === '' || maybeCoerced === 'true' || maybeCoerced === 'false') {
1610
+ maybeCoerced = maybeCoerced === 'true' || maybeCoerced === '';
1638
1611
  }
1639
- }));
1612
+ else if (!isNaN(Number(maybeCoerced))) {
1613
+ maybeCoerced = Number(maybeCoerced);
1614
+ }
1615
+ node.__ngt_renderer__[10 /* NgtRendererClassId.rawValue */] = maybeCoerced;
1616
+ return;
1617
+ }
1618
+ applyProps(node, { [name]: value });
1640
1619
  }
1641
- set ref(instance) {
1642
- this.write({ instanceRef: is.ref(instance) ? instance : new NgtRef(instance) });
1620
+ applyProperty(node, name, value) {
1621
+ if (node.__ngt_renderer__[3 /* NgtRendererClassId.destroyed */])
1622
+ return;
1623
+ // setup [ref] here
1624
+ if (name === SPECIAL_PROPERTIES.REF && is.ref(value)) {
1625
+ node.__ngt_renderer__[11 /* NgtRendererClassId.ref */] = value;
1626
+ value.nativeElement = node;
1627
+ return;
1628
+ }
1629
+ const parent = getLocalState(node).parent || node.__ngt_renderer__[1 /* NgtRendererClassId.parent */];
1630
+ // rawValue
1631
+ if (getLocalState(node).isRaw && name === SPECIAL_PROPERTIES.VALUE) {
1632
+ node.__ngt_renderer__[10 /* NgtRendererClassId.rawValue */] = value;
1633
+ attachThreeChild(parent, node);
1634
+ return;
1635
+ }
1636
+ // attach
1637
+ if (name === SPECIAL_PROPERTIES.ATTACH) {
1638
+ getLocalState(node).attach = Array.isArray(value) ? value.map((v) => v.toString()) : value;
1639
+ attachThreeChild(parent, node);
1640
+ return;
1641
+ }
1642
+ const compound = node.__ngt_renderer__[4 /* NgtRendererClassId.compound */];
1643
+ if (compound?.[1 /* NgtCompoundClassId.props */] &&
1644
+ name in compound[1 /* NgtCompoundClassId.props */] &&
1645
+ !compound[0 /* NgtCompoundClassId.applyFirst */]) {
1646
+ value = compound[1 /* NgtCompoundClassId.props */][name];
1647
+ }
1648
+ applyProps(node, { [name]: value });
1643
1649
  }
1644
- set skipWrapper(skipWrapper) {
1645
- this.write({ skipWrapper });
1646
- this.write({ skipWrapperExplicit: true });
1650
+ isCompound(name) {
1651
+ return this.root.compoundPrefixes.some((prefix) => name.startsWith(prefix));
1647
1652
  }
1648
- set skipInit(skipInit) {
1649
- this.write({ skipInit });
1650
- this.write({ skipInitExplicit: true });
1653
+ get rootScene() {
1654
+ return this.root.store.get('scene');
1651
1655
  }
1652
- set noAttach(noAttach) {
1653
- this.write({ noAttach });
1654
- this.write({ noAttachExplicit: true });
1656
+ get rootCdr() {
1657
+ return this.root.cdr;
1655
1658
  }
1656
- set attach(value) {
1657
- if (value) {
1658
- const attach = typeof value === 'function'
1659
- ? value
1660
- : Array.isArray(value)
1661
- ? value.map((item) => (typeof item === 'number' ? item.toString() : item))
1662
- : [value];
1663
- this.write({ attach, attachExplicit: true });
1659
+ getClosestParentWithInstance(node) {
1660
+ let parent = node.__ngt_renderer__[1 /* NgtRendererClassId.parent */];
1661
+ while (parent && parent.__ngt_renderer__[0 /* NgtRendererClassId.type */] !== 'three') {
1662
+ parent = parent.__ngt_renderer__[12 /* NgtRendererClassId.portalContainer */]
1663
+ ? parent.__ngt_renderer__[12 /* NgtRendererClassId.portalContainer */]
1664
+ : parent.__ngt_renderer__[1 /* NgtRendererClassId.parent */];
1664
1665
  }
1666
+ return parent;
1665
1667
  }
1666
- set isRaw(val) {
1667
- this._isRaw = val;
1668
- }
1669
- initialize() {
1670
- super.initialize();
1671
- this.write({
1672
- instanceRef: new NgtRef(null),
1673
- attach: [],
1674
- noAttach: false,
1675
- skipWrapper: false,
1676
- skipInit: false,
1677
- });
1668
+ getClosestParentWithCompound(node) {
1669
+ if (node.__ngt_renderer__[5 /* NgtRendererClassId.compoundParent */]) {
1670
+ return node.__ngt_renderer__[5 /* NgtRendererClassId.compoundParent */];
1671
+ }
1672
+ let parent = node.__ngt_renderer__[1 /* NgtRendererClassId.parent */];
1673
+ if (parent &&
1674
+ parent.__ngt_renderer__[0 /* NgtRendererClassId.type */] === 'compound' &&
1675
+ !parent.__ngt_renderer__[6 /* NgtRendererClassId.compounded */]) {
1676
+ return parent;
1677
+ }
1678
+ while (parent &&
1679
+ (parent.__ngt_renderer__[0 /* NgtRendererClassId.type */] === 'three' ||
1680
+ !parent.__ngt_renderer__[5 /* NgtRendererClassId.compoundParent */] ||
1681
+ parent.__ngt_renderer__[0 /* NgtRendererClassId.type */] !== 'compound')) {
1682
+ parent = parent.__ngt_renderer__[1 /* NgtRendererClassId.parent */];
1683
+ }
1684
+ if (!parent)
1685
+ return;
1686
+ if (parent.__ngt_renderer__[0 /* NgtRendererClassId.type */] === 'three' &&
1687
+ parent.__ngt_renderer__[5 /* NgtRendererClassId.compoundParent */]) {
1688
+ return parent.__ngt_renderer__[5 /* NgtRendererClassId.compoundParent */];
1689
+ }
1690
+ if (!parent.__ngt_renderer__[6 /* NgtRendererClassId.compounded */]) {
1691
+ return parent;
1692
+ }
1693
+ return null;
1678
1694
  }
1679
- ngOnInit() {
1680
- this.zone.runOutsideAngular(() => {
1681
- this.store.onReady(() => {
1682
- this.instanceReady(this.instanceRef.pipe(filter((instance) => !!instance)));
1683
- });
1684
- });
1695
+ getCreationState() {
1696
+ const injectedArgs = this.firstNonInjectedDirective(NgtArgs)?.args || [];
1697
+ const store = this.tryGetPortalStore();
1698
+ return { injectedArgs, store };
1685
1699
  }
1686
- get instanceRef() {
1687
- return this.read((s) => s.instanceRef);
1700
+ destroy(node, parent) {
1701
+ const state = node.__ngt_renderer__;
1702
+ if (state[3 /* NgtRendererClassId.destroyed */])
1703
+ return;
1704
+ if (state[0 /* NgtRendererClassId.type */] === 'three') {
1705
+ state[4 /* NgtRendererClassId.compound */] = undefined;
1706
+ state[5 /* NgtRendererClassId.compoundParent */] = undefined;
1707
+ const localState = getLocalState(node);
1708
+ if (localState.objects) {
1709
+ localState.objects.value.forEach((obj) => this.destroy(obj, parent));
1710
+ localState.objects.complete();
1711
+ }
1712
+ if (localState.nonObjects) {
1713
+ localState.nonObjects.value.forEach((obj) => this.destroy(obj, parent));
1714
+ localState.nonObjects.complete();
1715
+ }
1716
+ if (localState.afterUpdate)
1717
+ localState.afterUpdate.complete();
1718
+ if (localState.afterAttach)
1719
+ localState.afterAttach.complete();
1720
+ delete localState['objects'];
1721
+ delete localState['nonObjects'];
1722
+ delete localState['add'];
1723
+ delete localState['remove'];
1724
+ delete localState['afterUpdate'];
1725
+ delete localState['afterAttach'];
1726
+ delete localState['store'];
1727
+ delete localState['handlers'];
1728
+ if (!localState.primitive) {
1729
+ delete node['__ngt__'];
1730
+ }
1731
+ }
1732
+ if (state[0 /* NgtRendererClassId.type */] === 'comment') {
1733
+ state[13 /* NgtRendererClassId.injectorFactory */] = null;
1734
+ delete node['__ngt_renderer_add_comment__'];
1735
+ const index = this.comments.findIndex((comment) => comment === node);
1736
+ if (index > -1) {
1737
+ this.comments.splice(index, 1);
1738
+ }
1739
+ }
1740
+ if (state[0 /* NgtRendererClassId.type */] === 'portal') {
1741
+ state[13 /* NgtRendererClassId.injectorFactory */] = null;
1742
+ const index = this.portals.findIndex((portal) => portal === node);
1743
+ if (index > -1) {
1744
+ this.portals.splice(index, 1);
1745
+ }
1746
+ }
1747
+ if (state[0 /* NgtRendererClassId.type */] === 'compound') {
1748
+ state[6 /* NgtRendererClassId.compounded */] = undefined;
1749
+ state[8 /* NgtRendererClassId.attributes */] = null;
1750
+ state[9 /* NgtRendererClassId.properties */] = null;
1751
+ this.executeOperation(node, 'cleanUp');
1752
+ state[7 /* NgtRendererClassId.queueOps */].clear();
1753
+ state[7 /* NgtRendererClassId.queueOps */] = null;
1754
+ }
1755
+ if (state[11 /* NgtRendererClassId.ref */]) {
1756
+ // nullify ref
1757
+ state[11 /* NgtRendererClassId.ref */].nativeElement = null;
1758
+ state[11 /* NgtRendererClassId.ref */] = undefined;
1759
+ }
1760
+ // nullify parent
1761
+ state[1 /* NgtRendererClassId.parent */] = null;
1762
+ for (const renderChild of state[2 /* NgtRendererClassId.children */] || []) {
1763
+ if (renderChild.__ngt_renderer__[0 /* NgtRendererClassId.type */] === 'three' && parent) {
1764
+ removeThreeChild(parent, renderChild, true);
1765
+ }
1766
+ this.destroy(renderChild, parent);
1767
+ }
1768
+ state[2 /* NgtRendererClassId.children */] = [];
1769
+ state[3 /* NgtRendererClassId.destroyed */] = true;
1770
+ if (parent) {
1771
+ this.removeChild(parent, node);
1772
+ }
1688
1773
  }
1689
- get instanceValue() {
1690
- if (!this.instanceRef.value)
1691
- return this.instanceRef.value;
1692
- return this._isRaw ? this.instanceRef.value?.valueOf() : this.instanceRef.value;
1774
+ firstNonInjectedDirective(dir) {
1775
+ let directive;
1776
+ let i = this.comments.length - 1;
1777
+ while (i >= 0) {
1778
+ const comment = this.comments[i];
1779
+ if (comment.__ngt_renderer__[3 /* NgtRendererClassId.destroyed */]) {
1780
+ i--;
1781
+ continue;
1782
+ }
1783
+ const injector = comment.__ngt_renderer__[13 /* NgtRendererClassId.injectorFactory */]();
1784
+ if (!injector) {
1785
+ i--;
1786
+ continue;
1787
+ }
1788
+ const instance = injector.get(dir, null);
1789
+ if (instance && instance.validate()) {
1790
+ directive = instance;
1791
+ break;
1792
+ }
1793
+ i--;
1794
+ }
1795
+ return directive;
1796
+ }
1797
+ tryGetPortalStore() {
1798
+ let store;
1799
+ // we only care about the portal states because NgtStore only differs per Portal
1800
+ let i = this.portals.length - 1;
1801
+ while (i >= 0) {
1802
+ // loop through the portal state backwards to find the closest NgtStore
1803
+ const injector = this.portals[i].__ngt_renderer__[13 /* NgtRendererClassId.injectorFactory */]();
1804
+ if (!injector) {
1805
+ i--;
1806
+ continue;
1807
+ }
1808
+ const instance = injector.get(NgtStore, null);
1809
+ if (instance) {
1810
+ store = instance;
1811
+ break;
1812
+ }
1813
+ i--;
1814
+ }
1815
+ return store || this.root.store;
1693
1816
  }
1694
- get proxyInstance() {
1695
- return this.instanceValue?.[NGT_PROXY_INSTANCE];
1817
+ }
1818
+
1819
+ class NgtRendererFactory {
1820
+ constructor() {
1821
+ this.domRendererFactory = inject(ɵDomRendererFactory2);
1822
+ this.cdr = inject(ChangeDetectorRef);
1823
+ this.store = inject(NgtStore);
1824
+ this.catalogue = inject(NGT_CATALOGUE);
1825
+ this.compoundPrefixes = inject(NGT_COMPOUND_PREFIXES);
1826
+ this.rendererStore = new NgtRendererStore({
1827
+ store: this.store,
1828
+ cdr: this.cdr,
1829
+ compoundPrefixes: this.compoundPrefixes,
1830
+ });
1696
1831
  }
1697
- get __ngt__() {
1698
- return getInstanceLocalState(this._isRaw ? this.instanceRef.value : this.instanceValue);
1832
+ createRenderer(hostElement, type) {
1833
+ // TODO we might need to check on "type" to return DomRenderer for that particular type to support HTML
1834
+ if (!this.renderer) {
1835
+ const domRenderer = this.domRendererFactory.createRenderer(hostElement, type);
1836
+ this.renderer = new NgtRenderer(domRenderer, this.rendererStore, this.catalogue);
1837
+ }
1838
+ return this.renderer;
1699
1839
  }
1700
- get parent() {
1701
- return this.parentRef?.();
1840
+ }
1841
+ NgtRendererFactory.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.1.1", ngImport: i0, type: NgtRendererFactory, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
1842
+ NgtRendererFactory.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "15.1.1", ngImport: i0, type: NgtRendererFactory });
1843
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.1.1", ngImport: i0, type: NgtRendererFactory, decorators: [{
1844
+ type: Injectable
1845
+ }] });
1846
+ class NgtRenderer {
1847
+ constructor(domRenderer, store, catalogue) {
1848
+ this.domRenderer = domRenderer;
1849
+ this.store = store;
1850
+ this.catalogue = catalogue;
1851
+ this.first = false;
1852
+ this.createText = this.domRenderer.createText.bind(this.domRenderer);
1853
+ this.destroy = this.domRenderer.destroy.bind(this.domRenderer);
1854
+ this.destroyNode = null;
1855
+ this.selectRootElement = this.domRenderer.selectRootElement.bind(this.domRenderer);
1856
+ this.nextSibling = this.domRenderer.nextSibling.bind(this.domRenderer);
1857
+ this.removeAttribute = this.domRenderer.removeAttribute.bind(this.domRenderer);
1858
+ this.addClass = this.domRenderer.addClass.bind(this.domRenderer);
1859
+ this.removeClass = this.domRenderer.removeClass.bind(this.domRenderer);
1860
+ this.setStyle = this.domRenderer.setStyle.bind(this.domRenderer);
1861
+ this.removeStyle = this.domRenderer.removeStyle.bind(this.domRenderer);
1862
+ this.setValue = this.domRenderer.setValue.bind(this.domRenderer);
1863
+ }
1864
+ createElement(name, namespace) {
1865
+ const element = this.domRenderer.createElement(name, namespace);
1866
+ // on first pass, we return the Root Scene as the root node
1867
+ if (!this.first) {
1868
+ this.first = true;
1869
+ return this.store.createNode('three', this.store.rootScene);
1870
+ }
1871
+ // handle compound
1872
+ if (this.store.isCompound(name))
1873
+ return this.store.createNode('compound', element);
1874
+ // handle portal
1875
+ if (name === SPECIAL_DOM_TAG.NGT_PORTAL) {
1876
+ return this.store.createNode('portal', element);
1877
+ }
1878
+ // handle raw value
1879
+ if (name === SPECIAL_DOM_TAG.NGT_VALUE) {
1880
+ return this.store.createNode('three', Object.assign({ __ngt_renderer__: { rawValue: undefined } }, { __ngt__: { isRaw: true } }));
1881
+ }
1882
+ const { injectedArgs, store } = this.store.getCreationState();
1883
+ // handle primitive
1884
+ if (name === SPECIAL_DOM_TAG.NGT_PRIMITIVE) {
1885
+ if (!injectedArgs[0])
1886
+ throw new Error(`[NGT] ngt-primitive without args is invalid`);
1887
+ const object = injectedArgs[0];
1888
+ let localState = getLocalState(object);
1889
+ if (!Object.keys(localState).length) {
1890
+ prepare(object, { store, args: injectedArgs, primitive: true });
1891
+ localState = getLocalState(object);
1892
+ }
1893
+ if (!localState.store)
1894
+ localState.store = store;
1895
+ return this.store.createNode('three', object);
1896
+ }
1897
+ const threeTag = name.startsWith('ngt') ? name.slice(4) : name;
1898
+ const threeName = kebabToPascal(threeTag);
1899
+ const threeTarget = this.catalogue[threeName];
1900
+ // we have the THREE constructor here, handle it
1901
+ if (threeTarget) {
1902
+ const instance = prepare(new threeTarget(...injectedArgs), { store, args: injectedArgs });
1903
+ const node = this.store.createNode('three', instance);
1904
+ const localState = getLocalState(instance);
1905
+ if (is.geometry(instance)) {
1906
+ localState.attach = ['geometry'];
1907
+ }
1908
+ else if (is.material(instance)) {
1909
+ localState.attach = ['material'];
1910
+ }
1911
+ return node;
1912
+ }
1913
+ return this.store.createNode('dom', element);
1702
1914
  }
1703
- ngOnDestroy() {
1704
- this.zone.runOutsideAngular(() => {
1705
- this.destroy();
1706
- });
1707
- super.ngOnDestroy();
1915
+ createComment(value) {
1916
+ const comment = this.domRenderer.createComment(value);
1917
+ return this.store.createNode('comment', comment);
1708
1918
  }
1709
- destroy() {
1710
- if (this.instanceValue) {
1711
- if (is.object3d(this.instanceValue)) {
1712
- const parentInstance = this.parent;
1713
- if (parentInstance && is.object3d(parentInstance.value)) {
1714
- removeInteractivity(this.__ngt__.stateFactory.bind(this.__ngt__), this.instanceValue);
1715
- parentInstance.value.remove(this.instanceValue);
1716
- invalidateInstance(parentInstance.value);
1717
- }
1718
- if (this.instanceValue.clear != null) {
1719
- this.instanceValue.clear();
1919
+ appendChild(parent, newChild) {
1920
+ // TODO: just ignore text node for now
1921
+ if (newChild instanceof Text)
1922
+ return;
1923
+ if (newChild.__ngt_renderer__[0 /* NgtRendererClassId.type */] === 'comment') {
1924
+ this.store.setParent(newChild, parent);
1925
+ return;
1926
+ }
1927
+ this.store.setParent(newChild, parent);
1928
+ this.store.addChild(parent, newChild);
1929
+ // if new chlid is a portal
1930
+ if (newChild.__ngt_renderer__[0 /* NgtRendererClassId.type */] === 'portal') {
1931
+ this.store.processPortalContainer(newChild);
1932
+ if (newChild.__ngt_renderer__[12 /* NgtRendererClassId.portalContainer */]) {
1933
+ this.appendChild(parent, newChild.__ngt_renderer__[12 /* NgtRendererClassId.portalContainer */]);
1934
+ }
1935
+ return;
1936
+ }
1937
+ // if parent is a portal
1938
+ if (parent.__ngt_renderer__[0 /* NgtRendererClassId.type */] === 'portal') {
1939
+ this.store.processPortalContainer(parent);
1940
+ if (parent.__ngt_renderer__[12 /* NgtRendererClassId.portalContainer */]) {
1941
+ this.appendChild(parent.__ngt_renderer__[12 /* NgtRendererClassId.portalContainer */], newChild);
1942
+ }
1943
+ return;
1944
+ }
1945
+ // if both are three instances, straightforward case
1946
+ if (parent.__ngt_renderer__[0 /* NgtRendererClassId.type */] === 'three' &&
1947
+ newChild.__ngt_renderer__[0 /* NgtRendererClassId.type */] === 'three') {
1948
+ attachThreeChild(parent, newChild);
1949
+ // here, we handle the special case of if the parent has a compoundParent, which means this child is part of a compound parent template
1950
+ if (!newChild.__ngt_renderer__[4 /* NgtRendererClassId.compound */])
1951
+ return;
1952
+ const closestGrandparentWithCompound = this.store.getClosestParentWithCompound(parent);
1953
+ if (!closestGrandparentWithCompound)
1954
+ return;
1955
+ this.appendChild(closestGrandparentWithCompound, newChild);
1956
+ return;
1957
+ }
1958
+ // if only the parent is the THREE instance
1959
+ if (parent.__ngt_renderer__[0 /* NgtRendererClassId.type */] === 'three') {
1960
+ if (newChild.__ngt_renderer__[2 /* NgtRendererClassId.children */].length) {
1961
+ for (const renderChild of newChild.__ngt_renderer__[2 /* NgtRendererClassId.children */]) {
1962
+ this.appendChild(parent, renderChild);
1720
1963
  }
1721
1964
  }
1722
- else {
1723
- if (this.__ngt__) {
1724
- // non-scene objects
1725
- const previousAttach = this.__ngt__.attach;
1726
- if (previousAttach != null) {
1727
- if (typeof previousAttach === 'function') {
1728
- previousAttach();
1729
- }
1730
- else {
1731
- const previousAttachValue = this.__ngt__.attachValue;
1732
- if (this.__ngt__.parentRef && this.__ngt__.parentRef.value) {
1733
- mutate(this.__ngt__.parentRef.value, previousAttachValue, previousAttach);
1734
- }
1735
- }
1736
- if (this.__ngt__.parentRef && this.__ngt__.parentRef.value) {
1737
- invalidateInstance(this.__ngt__.parentRef.value);
1738
- }
1965
+ }
1966
+ // if parent is a compound
1967
+ if (parent.__ngt_renderer__[0 /* NgtRendererClassId.type */] === 'compound') {
1968
+ // if compound doesn't have a THREE instance set yet
1969
+ if (!parent.__ngt_renderer__[6 /* NgtRendererClassId.compounded */] &&
1970
+ newChild.__ngt_renderer__[0 /* NgtRendererClassId.type */] === 'three') {
1971
+ // if child is indeed an ngtCompound
1972
+ if (newChild.__ngt_renderer__[4 /* NgtRendererClassId.compound */]) {
1973
+ this.store.setCompound(parent, newChild);
1974
+ }
1975
+ else {
1976
+ // if not, we track the parent (that is supposedly the compound component) on this three instance
1977
+ if (!newChild.__ngt_renderer__[5 /* NgtRendererClassId.compoundParent */]) {
1978
+ newChild.__ngt_renderer__[5 /* NgtRendererClassId.compoundParent */] = parent;
1739
1979
  }
1740
1980
  }
1741
1981
  }
1742
- const dispose = this.instanceValue['dispose'];
1743
- if (dispose && typeof dispose === 'function') {
1744
- dispose.apply(this.instanceValue);
1982
+ // reset the compound if it's changed
1983
+ if (parent.__ngt_renderer__[6 /* NgtRendererClassId.compounded */] &&
1984
+ newChild.__ngt_renderer__[0 /* NgtRendererClassId.type */] === 'three' &&
1985
+ newChild.__ngt_renderer__[4 /* NgtRendererClassId.compound */] &&
1986
+ parent.__ngt_renderer__[6 /* NgtRendererClassId.compounded */] !== newChild) {
1987
+ this.store.setCompound(parent, newChild);
1988
+ }
1989
+ }
1990
+ if (newChild.__ngt_renderer__[0 /* NgtRendererClassId.type */] === 'three' && !getLocalState(newChild).parent) {
1991
+ // we'll try to get the grandparent instance here so that we can run appendChild with both instances
1992
+ const closestGrandparentInstance = this.store.getClosestParentWithInstance(parent);
1993
+ if (closestGrandparentInstance) {
1994
+ this.appendChild(closestGrandparentInstance, newChild);
1995
+ }
1996
+ return;
1997
+ }
1998
+ if (parent.__ngt_renderer__[0 /* NgtRendererClassId.type */] === 'dom' &&
1999
+ newChild.__ngt_renderer__[0 /* NgtRendererClassId.type */] === 'dom') {
2000
+ const closestGrandparentInstance = this.store.getClosestParentWithInstance(parent);
2001
+ if (closestGrandparentInstance) {
2002
+ this.appendChild(closestGrandparentInstance, newChild);
2003
+ }
2004
+ }
2005
+ }
2006
+ insertBefore(parent, newChild
2007
+ // TODO we might need these?
2008
+ // refChild: NgtRendererNode,
2009
+ // isMove?: boolean | undefined
2010
+ ) {
2011
+ if (!parent.__ngt_renderer__)
2012
+ return;
2013
+ this.appendChild(parent, newChild);
2014
+ }
2015
+ removeChild(parent, oldChild, isHostElement) {
2016
+ if (parent.__ngt_renderer__[0 /* NgtRendererClassId.type */] === 'three' &&
2017
+ oldChild.__ngt_renderer__[0 /* NgtRendererClassId.type */] === 'three') {
2018
+ removeThreeChild(parent, oldChild, true);
2019
+ this.store.destroy(oldChild, parent);
2020
+ return;
2021
+ }
2022
+ if (parent.__ngt_renderer__[0 /* NgtRendererClassId.type */] === 'compound' &&
2023
+ parent.__ngt_renderer__[1 /* NgtRendererClassId.parent */]) {
2024
+ this.removeChild(parent.__ngt_renderer__[1 /* NgtRendererClassId.parent */], oldChild, isHostElement);
2025
+ return;
2026
+ }
2027
+ if (parent.__ngt_renderer__[0 /* NgtRendererClassId.type */] === 'three') {
2028
+ this.store.destroy(oldChild, parent);
2029
+ return;
2030
+ }
2031
+ const closestGrandparentInstance = this.store.getClosestParentWithInstance(parent);
2032
+ if (closestGrandparentInstance) {
2033
+ this.removeChild(closestGrandparentInstance, oldChild, isHostElement);
2034
+ }
2035
+ this.store.destroy(oldChild, closestGrandparentInstance);
2036
+ }
2037
+ parentNode(node) {
2038
+ if (node.__ngt_renderer__?.[1 /* NgtRendererClassId.parent */])
2039
+ return node.__ngt_renderer__[1 /* NgtRendererClassId.parent */];
2040
+ return this.domRenderer.parentNode(node);
2041
+ }
2042
+ setAttribute(el, name, value, namespace) {
2043
+ if (el.__ngt_renderer__[0 /* NgtRendererClassId.type */] === 'compound') {
2044
+ // we don't have the compound instance yet
2045
+ el.__ngt_renderer__[8 /* NgtRendererClassId.attributes */][name] = value;
2046
+ if (!el.__ngt_renderer__[6 /* NgtRendererClassId.compounded */]) {
2047
+ this.store.queueOperation(el, ['op', () => this.setAttribute(el, name, value, namespace)]);
2048
+ return;
2049
+ }
2050
+ this.setAttribute(el.__ngt_renderer__[6 /* NgtRendererClassId.compounded */], name, value, namespace);
2051
+ return;
2052
+ }
2053
+ if (el.__ngt_renderer__[0 /* NgtRendererClassId.type */] === 'three') {
2054
+ this.store.applyAttribute(el, name, value);
2055
+ }
2056
+ }
2057
+ setProperty(el, name, value) {
2058
+ if (el.__ngt_renderer__[0 /* NgtRendererClassId.type */] === 'compound') {
2059
+ // we don't have the compound instance yet
2060
+ el.__ngt_renderer__[9 /* NgtRendererClassId.properties */][name] = value;
2061
+ if (!el.__ngt_renderer__[6 /* NgtRendererClassId.compounded */]) {
2062
+ this.store.queueOperation(el, ['op', () => this.setProperty(el, name, value)]);
2063
+ return;
2064
+ }
2065
+ if (el.__ngt_renderer__[6 /* NgtRendererClassId.compounded */].__ngt_renderer__[4 /* NgtRendererClassId.compound */]) {
2066
+ Object.assign(el.__ngt_renderer__[6 /* NgtRendererClassId.compounded */].__ngt_renderer__[4 /* NgtRendererClassId.compound */], {
2067
+ props: {
2068
+ ...el.__ngt_renderer__[6 /* NgtRendererClassId.compounded */].__ngt_renderer__[4 /* NgtRendererClassId.compound */],
2069
+ [name]: value,
2070
+ },
2071
+ });
1745
2072
  }
2073
+ this.setProperty(el.__ngt_renderer__[6 /* NgtRendererClassId.compounded */], name, value);
2074
+ return;
2075
+ }
2076
+ if (el.__ngt_renderer__[0 /* NgtRendererClassId.type */] === 'three') {
2077
+ this.store.applyProperty(el, name, value);
1746
2078
  }
1747
- this.write({ attach: [] });
1748
- this.instanceRef.complete();
1749
2079
  }
1750
- eventNameToHandler(controllerEvent) {
1751
- return (event) => {
1752
- // go back into Angular Zone so that state updates on these events trigger CD
1753
- this.zone.run(() => {
1754
- controllerEvent.emit(event);
1755
- });
1756
- };
2080
+ listen(target, eventName, callback) {
2081
+ if (target.__ngt_renderer__[0 /* NgtRendererClassId.type */] === 'three' ||
2082
+ (target.__ngt_renderer__[0 /* NgtRendererClassId.type */] === 'compound' &&
2083
+ target.__ngt_renderer__[6 /* NgtRendererClassId.compounded */])) {
2084
+ const instance = target.__ngt_renderer__[6 /* NgtRendererClassId.compounded */] || target;
2085
+ const priority = getLocalState(target).priority;
2086
+ return processThreeEvent(instance, priority || 0, eventName, callback, this.store.rootCdr);
2087
+ }
2088
+ if (target.__ngt_renderer__[0 /* NgtRendererClassId.type */] === 'compound' &&
2089
+ !target.__ngt_renderer__[6 /* NgtRendererClassId.compounded */]) {
2090
+ this.store.queueOperation(target, [
2091
+ 'op',
2092
+ () => this.store.queueOperation(target, ['cleanUp', this.listen(target, eventName, callback)]),
2093
+ ]);
2094
+ }
2095
+ return () => { };
1757
2096
  }
1758
- }
1759
- NgtInstance.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.0.0", ngImport: i0, type: NgtInstance, deps: null, target: i0.ɵɵFactoryTarget.Directive });
1760
- NgtInstance.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "15.0.0", type: NgtInstance, isStandalone: true, selector: "[ngtInstance]", inputs: { ref: "ref", skipWrapper: "skipWrapper", skipInit: "skipInit", noAttach: "noAttach", attach: "attach", priority: "priority", beforeRender: "beforeRender", readyCallback: "readyCallback", updateCallback: "updateCallback" }, outputs: { click: "click", contextmenu: "contextmenu", dblclick: "dblclick", pointerup: "pointerup", pointerdown: "pointerdown", pointerover: "pointerover", pointerout: "pointerout", pointerenter: "pointerenter", pointerleave: "pointerleave", pointermove: "pointermove", pointermissed: "pointermissed", pointercancel: "pointercancel", wheel: "wheel" }, exportAs: ["ngtInstance"], usesInheritance: true, ngImport: i0 });
1761
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.0.0", ngImport: i0, type: NgtInstance, decorators: [{
1762
- type: Directive,
1763
- args: [{
1764
- selector: '[ngtInstance]',
1765
- exportAs: 'ngtInstance',
1766
- standalone: true,
1767
- }]
1768
- }], propDecorators: { ref: [{
1769
- type: Input
1770
- }], skipWrapper: [{
1771
- type: Input
1772
- }], skipInit: [{
1773
- type: Input
1774
- }], noAttach: [{
1775
- type: Input
1776
- }], attach: [{
1777
- type: Input
1778
- }], priority: [{
1779
- type: Input
1780
- }], beforeRender: [{
1781
- type: Input
1782
- }], readyCallback: [{
1783
- type: Input
1784
- }], updateCallback: [{
1785
- type: Input
1786
- }], click: [{
1787
- type: Output
1788
- }], contextmenu: [{
1789
- type: Output
1790
- }], dblclick: [{
1791
- type: Output
1792
- }], pointerup: [{
1793
- type: Output
1794
- }], pointerdown: [{
1795
- type: Output
1796
- }], pointerover: [{
1797
- type: Output
1798
- }], pointerout: [{
1799
- type: Output
1800
- }], pointerenter: [{
1801
- type: Output
1802
- }], pointerleave: [{
1803
- type: Output
1804
- }], pointermove: [{
1805
- type: Output
1806
- }], pointermissed: [{
1807
- type: Output
1808
- }], pointercancel: [{
1809
- type: Output
1810
- }], wheel: [{
1811
- type: Output
1812
- }] } });
1813
- const NGT_INSTANCE_INPUTS = [
1814
- 'ref',
1815
- 'attach',
1816
- 'skipWrapper',
1817
- 'skipInit',
1818
- 'noAttach',
1819
- 'beforeRender',
1820
- 'priority',
1821
- 'updateCallback',
1822
- 'readyCallback',
1823
- ];
1824
- const NGT_INSTANCE_OUTPUTS = [...supportedEvents];
1825
-
1826
- const defaultResizeOptions = {
1827
- box: 'content-box',
1828
- scroll: false,
1829
- offsetSize: false,
1830
- debounce: { scroll: 50, resize: 0 },
1831
- };
1832
- const [injectResizeOptions, provideResizeOptions] = createInjection('ngtResize Options', {
1833
- defaultValueOrFactory: defaultResizeOptions,
1834
- provideValueFactory: (value) => ({ ...defaultResizeOptions, ...value }),
1835
- });
1836
- const [injectResizeObserverSupport] = createInjection('Resize Observer API support', {
1837
- defaultValueOrFactory: () => {
1838
- const window = injectWindow();
1839
- return 'ResizeObserver' in window && window['ResizeObserver'] != null;
1840
- },
1841
- });
1842
-
1843
- class NgtResize extends Observable {
1844
- constructor() {
1845
- const { nativeElement } = inject(ElementRef);
1846
- const zone = inject(NgZone);
1847
- const document = inject(DOCUMENT);
1848
- const window = injectWindow();
1849
- const isSupport = injectResizeObserverSupport();
1850
- const { box, offsetSize, scroll, debounce } = injectResizeOptions();
1851
- let observer;
1852
- let lastBounds;
1853
- let lastEntries = [];
1854
- const torndown$ = new Subject();
1855
- const scrollContainers = findScrollContainers(nativeElement, document.body);
1856
- // set actual debounce values early, so effects know if they should react accordingly
1857
- const scrollDebounce = debounce ? (typeof debounce === 'number' ? debounce : debounce.scroll) : null;
1858
- const resizeDebounce = debounce ? (typeof debounce === 'number' ? debounce : debounce.resize) : null;
1859
- const debounceAndDestroy = (debounce) => {
1860
- return pipe(debounceTime(debounce ?? 0), takeUntil(torndown$));
1861
- };
1862
- super((subscriber) => {
1863
- if (!isSupport) {
1864
- subscriber.error('ResizeObserver is not supported in your browser. Please use a polyfill');
1865
- return;
1866
- }
1867
- zone.runOutsideAngular(() => {
1868
- const callback = (entries) => {
1869
- lastEntries = entries;
1870
- const { left, top, width, height, bottom, right, x, y } = nativeElement.getBoundingClientRect();
1871
- const size = {
1872
- left,
1873
- top,
1874
- width,
1875
- height,
1876
- bottom,
1877
- right,
1878
- x,
1879
- y,
1880
- };
1881
- if (nativeElement instanceof HTMLElement && offsetSize) {
1882
- size.height = nativeElement.offsetHeight;
1883
- size.width = nativeElement.offsetWidth;
1884
- }
1885
- Object.freeze(size);
1886
- subscriber.next({
1887
- entries,
1888
- dpr: window.devicePixelRatio,
1889
- ...size,
1890
- });
1891
- if (!areBoundsEqual(lastBounds || {}, size)) {
1892
- lastBounds = size;
1893
- }
1894
- };
1895
- const boundEntriesCallback = () => {
1896
- callback(lastEntries);
1897
- };
1898
- observer = new ResizeObserver(callback);
1899
- observer.observe(nativeElement, { box });
1900
- if (scroll) {
1901
- if (scrollContainers) {
1902
- scrollContainers.forEach((scrollContainer) => {
1903
- fromEvent(scrollContainer, 'scroll', {
1904
- capture: true,
1905
- passive: true,
1906
- })
1907
- .pipe(debounceAndDestroy(scrollDebounce))
1908
- .subscribe(boundEntriesCallback);
1909
- });
1910
- }
1911
- fromEvent(window, 'scroll', {
1912
- capture: true,
1913
- passive: true,
1914
- })
1915
- .pipe(debounceAndDestroy(scrollDebounce))
1916
- .subscribe(boundEntriesCallback);
1917
- }
1918
- fromEvent(window, 'resize').pipe(debounceAndDestroy(resizeDebounce)).subscribe(boundEntriesCallback);
1919
- });
1920
- return () => {
1921
- if (observer) {
1922
- observer.unobserve(nativeElement);
1923
- observer.disconnect();
1924
- }
1925
- torndown$.next();
1926
- torndown$.complete();
1927
- };
1928
- });
1929
- return this.pipe(debounceTime(scrollDebounce || 0), share({
1930
- connector: () => new ReplaySubject(1),
1931
- resetOnRefCountZero: true,
1932
- resetOnComplete: true,
1933
- }));
2097
+ get data() {
2098
+ return this.domRenderer.data;
1934
2099
  }
1935
2100
  }
1936
- NgtResize.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.0.0", ngImport: i0, type: NgtResize, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
1937
- NgtResize.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "15.0.0", ngImport: i0, type: NgtResize });
1938
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.0.0", ngImport: i0, type: NgtResize, decorators: [{
1939
- type: Injectable
1940
- }], ctorParameters: function () { return []; } });
1941
- // Returns a list of scroll offsets
1942
- function findScrollContainers(element, documentBody) {
1943
- const result = [];
1944
- if (!element || element === documentBody)
1945
- return result;
1946
- const { overflow, overflowX, overflowY } = window.getComputedStyle(element);
1947
- if ([overflow, overflowX, overflowY].some((prop) => prop === 'auto' || prop === 'scroll'))
1948
- result.push(element);
1949
- return [...result, ...findScrollContainers(element.parentElement, documentBody)];
2101
+
2102
+ function provideNgtRenderer({ store, changeDetectorRef, compoundPrefixes = [] }) {
2103
+ if (!compoundPrefixes.includes('ngts')) {
2104
+ compoundPrefixes.push('ngts');
2105
+ }
2106
+ if (!compoundPrefixes.includes('ngtp')) {
2107
+ compoundPrefixes.push('ngtp');
2108
+ }
2109
+ return [
2110
+ { provide: RendererFactory2, useClass: NgtRendererFactory },
2111
+ { provide: NgtStore, useValue: store },
2112
+ { provide: ChangeDetectorRef, useValue: changeDetectorRef },
2113
+ { provide: NGT_COMPOUND_PREFIXES, useValue: compoundPrefixes },
2114
+ ];
1950
2115
  }
1951
- // Checks if element boundaries are equal
1952
- const keys = [
1953
- 'x',
1954
- 'y',
1955
- 'top',
1956
- 'bottom',
1957
- 'left',
1958
- 'right',
1959
- 'width',
1960
- 'height',
1961
- ];
1962
- const areBoundsEqual = (a, b) => keys.every((key) => a[key] === b[key]);
1963
2116
 
1964
- function buildGraph(object) {
1965
- const data = { nodes: {}, materials: {} };
1966
- if (object) {
1967
- object.traverse((obj) => {
1968
- if (obj.name)
1969
- data.nodes[obj.name] = obj;
1970
- if ('material' in obj && !data.materials[obj.material.name]) {
1971
- data.materials[obj.material.name] = obj
1972
- .material;
2117
+ const DOM_EVENTS = {
2118
+ click: false,
2119
+ contextmenu: false,
2120
+ dblclick: false,
2121
+ wheel: false,
2122
+ pointerdown: true,
2123
+ pointerup: true,
2124
+ pointerleave: true,
2125
+ pointermove: true,
2126
+ pointercancel: true,
2127
+ lostpointercapture: true,
2128
+ };
2129
+ const supportedEvents = [
2130
+ 'click',
2131
+ 'contextmenu',
2132
+ 'dblclick',
2133
+ 'pointerup',
2134
+ 'pointerdown',
2135
+ 'pointerover',
2136
+ 'pointerout',
2137
+ 'pointerenter',
2138
+ 'pointerleave',
2139
+ 'pointermove',
2140
+ 'pointermissed',
2141
+ 'pointercancel',
2142
+ 'wheel',
2143
+ ];
2144
+ function createPointerEvents(store) {
2145
+ const { handlePointer } = createEvents(store);
2146
+ return {
2147
+ priority: 1,
2148
+ enabled: true,
2149
+ compute: (event, root) => {
2150
+ const state = root.get();
2151
+ // https://github.com/pmndrs/react-three-fiber/pull/782
2152
+ // Events trigger outside of canvas when moved, use offsetX/Y by default and allow overrides
2153
+ state.pointer.set((event.offsetX / state.size.width) * 2 - 1, -(event.offsetY / state.size.height) * 2 + 1);
2154
+ state.raycaster.setFromCamera(state.pointer, state.camera);
2155
+ },
2156
+ connected: undefined,
2157
+ handlers: Object.keys(DOM_EVENTS).reduce((handlers, supportedEventName) => {
2158
+ handlers[supportedEventName] = handlePointer(supportedEventName);
2159
+ return handlers;
2160
+ }, {}),
2161
+ connect: (target) => {
2162
+ const state = store.get();
2163
+ state.events.disconnect?.();
2164
+ state.setEvents({ connected: target });
2165
+ Object.entries(state.events.handlers ?? {}).forEach(([eventName, eventHandler]) => {
2166
+ const passive = DOM_EVENTS[eventName];
2167
+ target.addEventListener(eventName, eventHandler, { passive });
2168
+ });
2169
+ },
2170
+ disconnect: () => {
2171
+ const { events, setEvents } = store.get();
2172
+ if (events.connected) {
2173
+ Object.entries(events.handlers ?? {}).forEach(([eventName, eventHandler]) => {
2174
+ if (events.connected instanceof HTMLElement) {
2175
+ events.connected.removeEventListener(eventName, eventHandler);
2176
+ }
2177
+ });
2178
+ setEvents({ connected: undefined });
1973
2179
  }
1974
- });
1975
- }
1976
- return data;
2180
+ },
2181
+ };
1977
2182
  }
1978
2183
 
1979
- class NgtLoader {
1980
- constructor() {
1981
- this.cached = new Map();
1982
- }
1983
- use(loadConstructor, input, extensions, onProgress) {
1984
- const urls = Array.isArray(input) ? input : [input];
1985
- const loader = new loadConstructor();
2184
+ const cached = new Map();
2185
+ function injectLoader(loaderConstructorFactory, input, extensions, onProgress) {
2186
+ const urls$ = isObservable(input) ? input : of(input);
2187
+ return urls$.pipe(map((inputs) => {
2188
+ const loaderConstructor = loaderConstructorFactory(inputs);
2189
+ const loader = new loaderConstructor();
1986
2190
  if (extensions) {
1987
2191
  extensions(loader);
1988
2192
  }
1989
- const observables$ = urls.map((url) => {
1990
- if (!this.cached.has(url)) {
1991
- this.cached.set(url, from(loader.loadAsync(url, onProgress)).pipe(tap((data) => {
1992
- if (data.scene) {
1993
- Object.assign(data, buildGraph(data.scene));
1994
- }
1995
- }), retry(2), catchError((err) => {
1996
- console.error(`[NgtLoader]: Error loading ${url}: ${err.message}`);
1997
- return of(null);
1998
- }), share({
1999
- connector: () => new ReplaySubject(1),
2000
- resetOnComplete: true,
2001
- resetOnRefCountZero: true,
2002
- resetOnError: true,
2003
- })));
2004
- }
2005
- return this.cached.get(url);
2006
- });
2007
- return forkJoin(observables$).pipe(map((results) => (Array.isArray(input) ? results : results[0])));
2008
- }
2009
- destroy() {
2010
- this.cached.clear();
2011
- }
2193
+ const urls = Array.isArray(inputs) ? inputs : typeof inputs === 'string' ? [inputs] : Object.values(inputs);
2194
+ return [
2195
+ urls.map((url) => {
2196
+ if (!cached.has(url)) {
2197
+ cached.set(url, from(loader.loadAsync(url, onProgress)).pipe(tap((data) => {
2198
+ if (data.scene) {
2199
+ Object.assign(data, makeObjectGraph(data.scene));
2200
+ }
2201
+ }), retry(2), catchError((err) => {
2202
+ console.error(`[NGT] Error loading ${url}: ${err.message}`);
2203
+ return of([]);
2204
+ }), share({
2205
+ connector: () => new ReplaySubject(1),
2206
+ resetOnComplete: true,
2207
+ resetOnError: true,
2208
+ resetOnRefCountZero: true,
2209
+ })));
2210
+ }
2211
+ return cached.get(url);
2212
+ }),
2213
+ inputs,
2214
+ ];
2215
+ }), switchMap(([observables$, inputs]) => {
2216
+ return forkJoin(observables$).pipe(map((results) => {
2217
+ if (Array.isArray(inputs))
2218
+ return results;
2219
+ if (typeof inputs === 'string')
2220
+ return results[0];
2221
+ const keys = Object.keys(inputs);
2222
+ return keys.reduce((result, key) => {
2223
+ result[key] = results[keys.indexOf(key)];
2224
+ return result;
2225
+ }, {});
2226
+ }));
2227
+ }));
2012
2228
  }
2013
- NgtLoader.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.0.0", ngImport: i0, type: NgtLoader, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
2014
- NgtLoader.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "15.0.0", ngImport: i0, type: NgtLoader, providedIn: 'root' });
2015
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.0.0", ngImport: i0, type: NgtLoader, decorators: [{
2016
- type: Injectable,
2017
- args: [{ providedIn: 'root' }]
2018
- }] });
2229
+ injectLoader.destroy = () => {
2230
+ cached.clear();
2231
+ };
2232
+ injectLoader.preLoad = (loaderConstructorFactory, inputs, extensions) => {
2233
+ injectLoader(loaderConstructorFactory, inputs, extensions).pipe(take(1)).subscribe();
2234
+ };
2235
+ const injectNgtLoader = injectLoader;
2019
2236
 
2020
2237
  class NgtCanvasContainer {
2021
2238
  constructor() {
2022
- this.resize = inject(NgtResize);
2239
+ this.canvasResize = injectNgxResize();
2023
2240
  }
2024
2241
  }
2025
- NgtCanvasContainer.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.0.0", ngImport: i0, type: NgtCanvasContainer, deps: [], target: i0.ɵɵFactoryTarget.Component });
2026
- NgtCanvasContainer.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "15.0.0", type: NgtCanvasContainer, isStandalone: true, selector: "ngt-canvas-container", outputs: { resize: "resize" }, providers: [NgtResize], ngImport: i0, template: '<ng-content></ng-content>', isInline: true, styles: [":host{display:block;height:100%;width:100%}\n"] });
2027
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.0.0", ngImport: i0, type: NgtCanvasContainer, decorators: [{
2242
+ NgtCanvasContainer.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.1.1", ngImport: i0, type: NgtCanvasContainer, deps: [], target: i0.ɵɵFactoryTarget.Component });
2243
+ NgtCanvasContainer.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "15.1.1", type: NgtCanvasContainer, isStandalone: true, selector: "ngt-canvas-container", outputs: { canvasResize: "canvasResize" }, providers: [provideNgxResizeOptions({ emitInZone: false })], ngImport: i0, template: '<ng-content />', isInline: true, styles: [":host{display:block;width:100%;height:100%}\n"] });
2244
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.1.1", ngImport: i0, type: NgtCanvasContainer, decorators: [{
2028
2245
  type: Component,
2029
- args: [{ selector: 'ngt-canvas-container', standalone: true, template: '<ng-content></ng-content>', providers: [NgtResize], styles: [":host{display:block;height:100%;width:100%}\n"] }]
2030
- }], propDecorators: { resize: [{
2246
+ args: [{ selector: 'ngt-canvas-container', standalone: true, template: '<ng-content />', providers: [provideNgxResizeOptions({ emitInZone: false })], styles: [":host{display:block;width:100%;height:100%}\n"] }]
2247
+ }], propDecorators: { canvasResize: [{
2031
2248
  type: Output
2032
2249
  }] } });
2033
- class NgtCanvas extends NgtComponentStore {
2250
+ class NgtCanvas extends NgtRxStore {
2034
2251
  constructor() {
2035
2252
  super(...arguments);
2253
+ this.cdr = inject(ChangeDetectorRef);
2254
+ this.envInjector = inject(EnvironmentInjector);
2255
+ this.host = inject(ElementRef);
2256
+ this.store = inject(NgtStore);
2036
2257
  this.hostClass = true;
2258
+ this.compoundPrefixes = [];
2037
2259
  this.created = new EventEmitter();
2038
- this.canvasPointerMissed = new EventEmitter();
2039
- this.zone = inject(NgZone);
2040
- this.loader = inject(NgtLoader);
2041
- this.store = inject(NgtStore, { self: true });
2042
- this.host = inject(ElementRef);
2260
+ this.pointerMissed = new EventEmitter();
2261
+ }
2262
+ initialize() {
2263
+ super.initialize();
2264
+ this.set({
2265
+ shadows: false,
2266
+ linear: false,
2267
+ flat: false,
2268
+ legacy: false,
2269
+ orthographic: false,
2270
+ frameloop: 'always',
2271
+ dpr: [1, 2],
2272
+ events: createPointerEvents,
2273
+ });
2043
2274
  }
2044
2275
  get pointerEvents() {
2045
- return this.read((s) => s.eventSource) !== this.host.nativeElement ? 'none' : 'auto';
2276
+ return this.get('eventSource') !== this.host.nativeElement ? 'none' : 'auto';
2046
2277
  }
2047
2278
  set linear(linear) {
2048
- this.write({ linear });
2279
+ this.set({ linear });
2049
2280
  }
2050
2281
  set legacy(legacy) {
2051
- this.write({ legacy });
2282
+ this.set({ legacy });
2052
2283
  }
2053
2284
  set flat(flat) {
2054
- this.write({ flat });
2285
+ this.set({ flat });
2055
2286
  }
2056
2287
  set orthographic(orthographic) {
2057
- this.write({ orthographic });
2288
+ this.set({ orthographic });
2058
2289
  }
2059
2290
  set frameloop(frameloop) {
2060
- this.write({ frameloop });
2291
+ this.set({ frameloop });
2061
2292
  }
2062
2293
  set dpr(dpr) {
2063
- this.write({ dpr });
2294
+ this.set({ dpr });
2064
2295
  }
2065
2296
  set raycaster(raycaster) {
2066
- this.write({ raycaster });
2297
+ this.set({ raycaster });
2067
2298
  }
2068
2299
  set shadows(shadows) {
2069
- this.write({
2300
+ this.set({
2070
2301
  shadows: typeof shadows === 'object' ? shadows : shadows,
2071
2302
  });
2072
2303
  }
2073
2304
  set camera(camera) {
2074
- this.write({ camera });
2305
+ this.set({ camera });
2075
2306
  }
2076
2307
  set gl(gl) {
2077
- this.write({ gl });
2308
+ this.set({ gl });
2078
2309
  }
2079
2310
  set eventSource(eventSource) {
2080
- this.write({ eventSource });
2311
+ this.set({ eventSource });
2081
2312
  }
2082
2313
  set eventPrefix(eventPrefix) {
2083
- this.write({ eventPrefix });
2314
+ this.set({ eventPrefix });
2084
2315
  }
2085
2316
  set lookAt(lookAt) {
2086
- this.write({ lookAt });
2317
+ this.set({ lookAt });
2087
2318
  }
2088
2319
  set performance(performance) {
2089
- this.write({ performance });
2090
- }
2091
- get sceneRef() {
2092
- return this.store.read((s) => s.sceneRef);
2093
- }
2094
- get isConfigured() {
2095
- return this.store.isConfigured;
2096
- }
2097
- initialize() {
2098
- super.initialize();
2099
- this.write({
2100
- shadows: false,
2101
- linear: false,
2102
- flat: false,
2103
- legacy: false,
2104
- orthographic: false,
2105
- frameloop: 'always',
2106
- dpr: [1, 2],
2107
- events: createPointerEvents,
2108
- });
2320
+ this.set({ performance });
2109
2321
  }
2110
2322
  ngOnInit() {
2111
- this.zone.runOutsideAngular(() => {
2112
- if (!this.eventSource) {
2113
- this.eventSource = this.host.nativeElement;
2114
- }
2115
- if (this.canvasPointerMissed.observed) {
2116
- // update pointerMissed event
2117
- this.store.write({
2118
- onPointerMissed: (event) => {
2119
- // go back into angular zone
2120
- this.zone.run(() => {
2121
- this.canvasPointerMissed.emit(event);
2122
- });
2123
- },
2124
- });
2125
- }
2126
- // set rootStateMap
2127
- rootStateMap.set(this.glCanvas.nativeElement, this.store.read);
2128
- // setup canvas state
2129
- this.store.init();
2130
- this.store.onReady(() => {
2131
- const state = this.store.read();
2132
- const inputs = this.read();
2133
- // canvas is ready
2134
- this.store.write((s) => ({ internal: { ...s.internal, active: true } }));
2135
- // Connect to eventsource
2136
- state.events.connect?.(inputs.eventSource instanceof ElementRef ? inputs.eventSource.nativeElement : inputs.eventSource);
2137
- // Set up compute function
2138
- if (inputs.eventPrefix) {
2139
- state.setEvents({
2140
- compute: (event, stateGetter) => {
2141
- const innerState = stateGetter();
2142
- const x = event[(inputs.eventPrefix + 'X')];
2143
- const y = event[(inputs.eventPrefix + 'Y')];
2144
- innerState.pointer.set((x / innerState.size.width) * 2 - 1, -(y / innerState.size.height) * 2 + 1);
2145
- innerState.raycaster.setFromCamera(innerState.pointer, innerState.camera);
2146
- },
2147
- });
2148
- }
2149
- if (this.created.observed) {
2150
- this.zone.run(() => {
2151
- requestAnimationFrame(() => {
2152
- this.created.emit(this.store.read());
2153
- });
2154
- });
2155
- }
2323
+ if (!this.get('eventSource')) {
2324
+ // set default event source to the host element
2325
+ this.eventSource = this.host.nativeElement;
2326
+ }
2327
+ if (this.pointerMissed.observed) {
2328
+ this.store.set({
2329
+ onPointerMissed: (event) => {
2330
+ this.pointerMissed.emit(event);
2331
+ this.cdr.detectChanges();
2332
+ },
2156
2333
  });
2334
+ }
2335
+ // setup NgtStore
2336
+ this.store.init();
2337
+ // set rootStateMap
2338
+ rootStateMap.set(this.glCanvas.nativeElement, this.store);
2339
+ // subscribe to store to listen for ready state
2340
+ this.hold(this.store.select('ready').pipe(filter((ready) => ready)), () => {
2341
+ this.storeReady();
2157
2342
  });
2158
2343
  }
2159
- onResize(resizeResult) {
2160
- this.zone.runOutsideAngular(() => {
2161
- const { width, height } = resizeResult;
2162
- if (width > 0 && height > 0) {
2163
- if (!this.store.isInit) {
2164
- this.store.init();
2165
- }
2166
- this.store.configure(this.read(), this.glCanvas.nativeElement);
2167
- }
2344
+ onResize({ width, height }) {
2345
+ if (width > 0 && height > 0) {
2346
+ if (!this.store.isInit)
2347
+ this.store.init();
2348
+ this.store.configure(this.get(), this.glCanvas.nativeElement);
2349
+ }
2350
+ }
2351
+ storeReady() {
2352
+ // canvas is ready, let's activate the loop
2353
+ this.store.set((state) => ({ internal: { ...state.internal, active: true } }));
2354
+ const inputs = this.get();
2355
+ const state = this.store.get();
2356
+ // connect to event source
2357
+ state.events.connect?.(is.ref(inputs.eventSource) ? inputs.eventSource.nativeElement : inputs.eventSource);
2358
+ // setup compute function for events
2359
+ if (inputs.eventPrefix) {
2360
+ state.setEvents({
2361
+ compute: (event, store) => {
2362
+ const innerState = store.get();
2363
+ const x = event[(inputs.eventPrefix + 'X')];
2364
+ const y = event[(inputs.eventPrefix + 'Y')];
2365
+ innerState.pointer.set((x / innerState.size.width) * 2 - 1, -(y / innerState.size.height) * 2 + 1);
2366
+ innerState.raycaster.setFromCamera(innerState.pointer, innerState.camera);
2367
+ },
2368
+ });
2369
+ }
2370
+ // emit created event if observed
2371
+ if (this.created.observed) {
2372
+ this.created.emit(this.store.get());
2373
+ this.cdr.detectChanges();
2374
+ }
2375
+ // render
2376
+ if (this.glRef) {
2377
+ this.glRef.destroy();
2378
+ }
2379
+ requestAnimationFrame(() => {
2380
+ this.glEnvInjector = createEnvironmentInjector([
2381
+ provideNgtRenderer({
2382
+ store: this.store,
2383
+ changeDetectorRef: this.cdr,
2384
+ compoundPrefixes: this.compoundPrefixes,
2385
+ }),
2386
+ ], this.envInjector);
2387
+ this.glRef = this.glAnchor.createComponent(this.scene, { environmentInjector: this.glEnvInjector });
2388
+ this.glRef.changeDetectorRef.detectChanges();
2389
+ this.glRef.changeDetectorRef.detach();
2390
+ this.cdr.detectChanges();
2168
2391
  });
2169
2392
  }
2170
2393
  ngOnDestroy() {
2171
- this.loader.destroy();
2394
+ if (this.glRef) {
2395
+ this.glRef.destroy();
2396
+ }
2397
+ if (this.glEnvInjector) {
2398
+ this.glEnvInjector.destroy();
2399
+ }
2400
+ injectNgtLoader.destroy();
2172
2401
  super.ngOnDestroy();
2173
2402
  }
2174
2403
  }
2175
- NgtCanvas.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.0.0", ngImport: i0, type: NgtCanvas, deps: null, target: i0.ɵɵFactoryTarget.Component });
2176
- NgtCanvas.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "15.0.0", type: NgtCanvas, isStandalone: true, selector: "ngt-canvas", inputs: { linear: "linear", legacy: "legacy", flat: "flat", orthographic: "orthographic", frameloop: "frameloop", dpr: "dpr", raycaster: "raycaster", shadows: "shadows", camera: "camera", gl: "gl", eventSource: "eventSource", eventPrefix: "eventPrefix", lookAt: "lookAt", performance: "performance" }, outputs: { created: "created", canvasPointerMissed: "canvasPointerMissed" }, host: { properties: { "class.ngt-canvas": "this.hostClass", "style.pointerEvents": "this.pointerEvents" } }, providers: [NgtStore, provideInstanceRef(NgtCanvas, (canvas) => canvas.sceneRef)], queries: [{ propertyName: "canvasContent", first: true, predicate: TemplateRef, descendants: true, static: true }], viewQueries: [{ propertyName: "glCanvas", first: true, predicate: ["glCanvas"], descendants: true, static: true }], usesInheritance: true, ngImport: i0, template: `
2177
- <ngt-canvas-container (resize)="onResize($event)">
2178
- <canvas #glCanvas style="display: block">
2179
- <ng-container *ngIf="isConfigured" [ngTemplateOutlet]="canvasContent"></ng-container>
2180
- <ng-content></ng-content>
2181
- </canvas>
2404
+ NgtCanvas.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.1.1", ngImport: i0, type: NgtCanvas, deps: null, target: i0.ɵɵFactoryTarget.Component });
2405
+ NgtCanvas.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "15.1.1", type: NgtCanvas, isStandalone: true, selector: "ngt-canvas", inputs: { scene: "scene", compoundPrefixes: "compoundPrefixes", linear: "linear", legacy: "legacy", flat: "flat", orthographic: "orthographic", frameloop: "frameloop", dpr: "dpr", raycaster: "raycaster", shadows: "shadows", camera: "camera", gl: "gl", eventSource: "eventSource", eventPrefix: "eventPrefix", lookAt: "lookAt", performance: "performance" }, outputs: { created: "created", pointerMissed: "pointerMissed" }, host: { properties: { "class.ngt-canvas": "this.hostClass", "style.pointerEvents": "this.pointerEvents" } }, providers: [NgtStore], viewQueries: [{ propertyName: "glCanvas", first: true, predicate: ["glCanvas"], descendants: true, static: true }, { propertyName: "glAnchor", first: true, predicate: ["glAnchor"], descendants: true, read: ViewContainerRef, static: true }], usesInheritance: true, ngImport: i0, template: `
2406
+ <ngt-canvas-container (canvasResize)="onResize($event)">
2407
+ <canvas #glCanvas style="display: block;"></canvas>
2408
+ <ng-container #glAnchor />
2182
2409
  </ngt-canvas-container>
2183
- `, isInline: true, styles: [":host{display:block;position:relative;width:100%;height:100%;overflow:hidden}\n"], dependencies: [{ kind: "component", type: NgtCanvasContainer, selector: "ngt-canvas-container", outputs: ["resize"] }, { kind: "directive", type: NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
2184
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.0.0", ngImport: i0, type: NgtCanvas, decorators: [{
2410
+ `, isInline: true, styles: [":host{display:block;position:relative;width:100%;height:100%;overflow:hidden}\n"], dependencies: [{ kind: "component", type: NgtCanvasContainer, selector: "ngt-canvas-container", outputs: ["canvasResize"] }] });
2411
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.1.1", ngImport: i0, type: NgtCanvas, decorators: [{
2185
2412
  type: Component,
2186
2413
  args: [{ selector: 'ngt-canvas', standalone: true, template: `
2187
- <ngt-canvas-container (resize)="onResize($event)">
2188
- <canvas #glCanvas style="display: block">
2189
- <ng-container *ngIf="isConfigured" [ngTemplateOutlet]="canvasContent"></ng-container>
2190
- <ng-content></ng-content>
2191
- </canvas>
2414
+ <ngt-canvas-container (canvasResize)="onResize($event)">
2415
+ <canvas #glCanvas style="display: block;"></canvas>
2416
+ <ng-container #glAnchor />
2192
2417
  </ngt-canvas-container>
2193
- `, changeDetection: ChangeDetectionStrategy.OnPush, imports: [NgtCanvasContainer, NgIf, NgTemplateOutlet, AsyncPipe], providers: [NgtStore, provideInstanceRef(NgtCanvas, (canvas) => canvas.sceneRef)], styles: [":host{display:block;position:relative;width:100%;height:100%;overflow:hidden}\n"] }]
2418
+ `, imports: [NgtCanvasContainer], providers: [NgtStore], styles: [":host{display:block;position:relative;width:100%;height:100%;overflow:hidden}\n"] }]
2194
2419
  }], propDecorators: { hostClass: [{
2195
2420
  type: HostBinding,
2196
2421
  args: ['class.ngt-canvas']
2197
2422
  }], pointerEvents: [{
2198
2423
  type: HostBinding,
2199
2424
  args: ['style.pointerEvents']
2425
+ }], scene: [{
2426
+ type: Input
2427
+ }], compoundPrefixes: [{
2428
+ type: Input
2200
2429
  }], linear: [{
2201
2430
  type: Input
2202
2431
  }], legacy: [{
@@ -2227,249 +2456,33 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.0.0", ngImpor
2227
2456
  type: Input
2228
2457
  }], created: [{
2229
2458
  type: Output
2230
- }], canvasPointerMissed: [{
2459
+ }], pointerMissed: [{
2231
2460
  type: Output
2232
2461
  }], glCanvas: [{
2233
2462
  type: ViewChild,
2234
2463
  args: ['glCanvas', { static: true }]
2235
- }], canvasContent: [{
2236
- type: ContentChild,
2237
- args: [TemplateRef, { static: true }]
2238
- }] } });
2239
-
2240
- const NGT_ARGS = new InjectionToken('NgtArgs');
2241
- function injectArgs(options = {}) {
2242
- return inject(NGT_ARGS, options);
2243
- }
2244
- class NgtArgs {
2245
- constructor() {
2246
- this.templateRef = inject(TemplateRef);
2247
- this.vcr = inject(ViewContainerRef);
2248
- }
2249
- set args(args) {
2250
- if (this.view) {
2251
- this.view.destroy();
2252
- }
2253
- this.view = this.vcr.createEmbeddedView(this.templateRef, {}, { injector: Injector.create({ providers: [{ provide: NGT_ARGS, useValue: args }] }) });
2254
- }
2255
- }
2256
- NgtArgs.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.0.0", ngImport: i0, type: NgtArgs, deps: [], target: i0.ɵɵFactoryTarget.Directive });
2257
- NgtArgs.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "15.0.0", type: NgtArgs, isStandalone: true, selector: "[args]", inputs: { args: "args" }, ngImport: i0 });
2258
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.0.0", ngImport: i0, type: NgtArgs, decorators: [{
2259
- type: Directive,
2260
- args: [{
2261
- selector: '[args]',
2262
- standalone: true,
2263
- }]
2264
- }], propDecorators: { args: [{
2265
- type: Input
2464
+ }], glAnchor: [{
2465
+ type: ViewChild,
2466
+ args: ['glAnchor', { static: true, read: ViewContainerRef }]
2266
2467
  }] } });
2267
2468
 
2268
- class NgtCursor {
2269
- constructor(instance, document) {
2270
- if (!instance)
2271
- return;
2272
- merge(instance.pointerover.pipe(map(() => true)), instance.pointerout.pipe(map(() => false)))
2273
- .pipe(takeUntil(instance.destroy$))
2274
- .subscribe((hovered) => {
2275
- document.body.style.cursor = hovered ? 'pointer' : 'auto';
2276
- });
2277
- }
2278
- }
2279
- NgtCursor.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.0.0", ngImport: i0, type: NgtCursor, deps: [{ token: NgtInstance, optional: true, self: true }, { token: DOCUMENT }], target: i0.ɵɵFactoryTarget.Directive });
2280
- NgtCursor.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "15.0.0", type: NgtCursor, isStandalone: true, selector: "[ngtCursor]", exportAs: ["ngtCursor"], ngImport: i0 });
2281
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.0.0", ngImport: i0, type: NgtCursor, decorators: [{
2282
- type: Directive,
2283
- args: [{
2284
- selector: '[ngtCursor]',
2285
- standalone: true,
2286
- exportAs: 'ngtCursor',
2287
- }]
2288
- }], ctorParameters: function () { return [{ type: NgtInstance, decorators: [{
2289
- type: Optional
2290
- }, {
2291
- type: Self
2292
- }] }, { type: Document, decorators: [{
2293
- type: Inject,
2294
- args: [DOCUMENT]
2295
- }] }]; } });
2296
-
2297
2469
  class NgtRepeat extends NgForOf {
2298
2470
  set ngForRepeat(count) {
2299
2471
  this.ngForOf = Number.isInteger(count) ? Array.from({ length: count }, (_, i) => i) : [];
2300
2472
  }
2301
2473
  }
2302
- NgtRepeat.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.0.0", ngImport: i0, type: NgtRepeat, deps: null, target: i0.ɵɵFactoryTarget.Directive });
2303
- NgtRepeat.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "15.0.0", type: NgtRepeat, isStandalone: true, selector: "[ngFor][ngForRepeat]", inputs: { ngForRepeat: "ngForRepeat" }, usesInheritance: true, ngImport: i0 });
2304
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.0.0", ngImport: i0, type: NgtRepeat, decorators: [{
2474
+ NgtRepeat.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.1.1", ngImport: i0, type: NgtRepeat, deps: null, target: i0.ɵɵFactoryTarget.Directive });
2475
+ NgtRepeat.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "15.1.1", type: NgtRepeat, isStandalone: true, selector: "[ngFor][ngForRepeat]", inputs: { ngForRepeat: "ngForRepeat" }, usesInheritance: true, ngImport: i0 });
2476
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.1.1", ngImport: i0, type: NgtRepeat, decorators: [{
2305
2477
  type: Directive,
2306
- args: [{
2307
- selector: '[ngFor][ngForRepeat]',
2308
- standalone: true,
2309
- }]
2478
+ args: [{ selector: '[ngFor][ngForRepeat]', standalone: true }]
2310
2479
  }], propDecorators: { ngForRepeat: [{
2311
2480
  type: Input
2312
2481
  }] } });
2313
2482
 
2314
- class NgtPiPipe {
2315
- transform(value) {
2316
- return value * Math.PI;
2317
- }
2318
- }
2319
- NgtPiPipe.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.0.0", ngImport: i0, type: NgtPiPipe, deps: [], target: i0.ɵɵFactoryTarget.Pipe });
2320
- NgtPiPipe.ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "15.0.0", ngImport: i0, type: NgtPiPipe, isStandalone: true, name: "pi" });
2321
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.0.0", ngImport: i0, type: NgtPiPipe, decorators: [{
2322
- type: Pipe,
2323
- args: [{ name: 'pi', standalone: true }]
2324
- }] });
2325
-
2326
- class NgtMathPipe {
2327
- transform(value, keyOfMath) {
2328
- const params = Array.isArray(value) ? value : [value];
2329
- return Math[keyOfMath](...params);
2330
- }
2331
- }
2332
- NgtMathPipe.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.0.0", ngImport: i0, type: NgtMathPipe, deps: [], target: i0.ɵɵFactoryTarget.Pipe });
2333
- NgtMathPipe.ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "15.0.0", ngImport: i0, type: NgtMathPipe, isStandalone: true, name: "math" });
2334
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.0.0", ngImport: i0, type: NgtMathPipe, decorators: [{
2335
- type: Pipe,
2336
- args: [{ name: 'math', standalone: true }]
2337
- }] });
2338
-
2339
- class NgtRadianPipe {
2340
- transform(degree) {
2341
- return (degree * Math.PI) / 180;
2342
- }
2343
- }
2344
- NgtRadianPipe.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.0.0", ngImport: i0, type: NgtRadianPipe, deps: [], target: i0.ɵɵFactoryTarget.Pipe });
2345
- NgtRadianPipe.ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "15.0.0", ngImport: i0, type: NgtRadianPipe, isStandalone: true, name: "radian" });
2346
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.0.0", ngImport: i0, type: NgtRadianPipe, decorators: [{
2347
- type: Pipe,
2348
- args: [{ name: 'radian', standalone: true }]
2349
- }] });
2350
-
2351
- class NgtSidePipe {
2352
- transform(side) {
2353
- switch (side) {
2354
- case 'front':
2355
- return THREE.FrontSide;
2356
- case 'back':
2357
- return THREE.BackSide;
2358
- case 'double':
2359
- return THREE.DoubleSide;
2360
- }
2361
- }
2362
- }
2363
- NgtSidePipe.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.0.0", ngImport: i0, type: NgtSidePipe, deps: [], target: i0.ɵɵFactoryTarget.Pipe });
2364
- NgtSidePipe.ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "15.0.0", ngImport: i0, type: NgtSidePipe, isStandalone: true, name: "side" });
2365
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.0.0", ngImport: i0, type: NgtSidePipe, decorators: [{
2366
- type: Pipe,
2367
- args: [{ name: 'side', standalone: true }]
2368
- }] });
2369
-
2370
- function capitalize(str) {
2371
- return str.charAt(0).toUpperCase() + str.slice(1);
2372
- }
2373
-
2374
- function proxify(instance, proxifyOptions = {}) {
2375
- const ngtInstance = injectInstance({ host: true });
2376
- const store = inject(NgtStore);
2377
- const zone = inject(NgZone);
2378
- const parentInstance = injectInstanceRef({ skipSelf: true, optional: true });
2379
- return zone.runOutsideAngular(() => {
2380
- // prep the instance w/ local state
2381
- instance = prepare(instance, store.read, store.rootStateFactory, parentInstance?.(), ngtInstance.instanceValue ? ngtInstance.instanceRef : undefined, !!proxifyOptions.primitive);
2382
- let storeReadySubscription;
2383
- const newValueSubscriptionMap = new Map();
2384
- function setProp(obj, prop, newValue) {
2385
- const capitalizedProp = `set${capitalize(prop)}`;
2386
- if (isObservable(newValue)) {
2387
- const sub = newValue.subscribe((val) => {
2388
- if (obj[capitalizedProp] && typeof obj[capitalizedProp] === 'function') {
2389
- obj[capitalizedProp](val);
2390
- }
2391
- else {
2392
- applyProps(obj, { [prop]: val });
2393
- }
2394
- ngtInstance.write({ [prop]: val });
2395
- });
2396
- return () => sub.unsubscribe();
2397
- }
2398
- if (obj[capitalizedProp] && typeof obj[capitalizedProp] === 'function') {
2399
- obj[capitalizedProp](newValue);
2400
- }
2401
- else {
2402
- applyProps(obj, { [prop]: newValue });
2403
- }
2404
- ngtInstance.write({ [prop]: newValue });
2405
- return;
2406
- }
2407
- const handler = {
2408
- get(target, p, receiver) {
2409
- if (p === 'instanceRef')
2410
- return ngtInstance.instanceRef;
2411
- if (p === 'instance')
2412
- return ngtInstance;
2413
- if (p === NGT_PROXY_INSTANCE)
2414
- return target;
2415
- const capitalizedProp = `get${capitalize(p)}`;
2416
- if (target[capitalizedProp] && typeof target[capitalizedProp] === 'function') {
2417
- return target[capitalizedProp]();
2418
- }
2419
- return Reflect.get(target, p, receiver);
2420
- },
2421
- set(target, p, newValue, receiver) {
2422
- const prop = p;
2423
- if (storeReadySubscription)
2424
- storeReadySubscription.unsubscribe();
2425
- if (newValueSubscriptionMap.has(prop))
2426
- newValueSubscriptionMap.get(prop)();
2427
- // Angular sets this property
2428
- if (p === '__ngContext__')
2429
- return Reflect.set(target, p, newValue, receiver);
2430
- return zone.runOutsideAngular(() => {
2431
- // TODO: figure out what else we need to handle
2432
- // we should handle if newValue is an Observable as well
2433
- if (store.read((s) => s.ready)) {
2434
- const cleanUp = setProp(instance, prop, newValue);
2435
- if (cleanUp)
2436
- newValueSubscriptionMap.set(prop, cleanUp);
2437
- }
2438
- else {
2439
- storeReadySubscription = store.onReady(() => setProp(instance, prop, newValue));
2440
- }
2441
- // schedule updateCallback on next event loop
2442
- queueMicrotask(() => {
2443
- if (ngtInstance.updateCallback)
2444
- ngtInstance.updateCallback(instance);
2445
- });
2446
- return true;
2447
- });
2448
- },
2449
- };
2450
- ngtInstance.effect(tapEffect(() => () => {
2451
- if (storeReadySubscription)
2452
- storeReadySubscription.unsubscribe();
2453
- newValueSubscriptionMap.forEach((cleanUp) => cleanUp());
2454
- }))();
2455
- const proxied = new Proxy(instance, handler);
2456
- if (proxifyOptions.attach)
2457
- ngtInstance.attach = proxifyOptions.attach;
2458
- if (proxifyOptions.created)
2459
- proxifyOptions.created(proxied, store.read, ngtInstance);
2460
- ngtInstance.instanceRef.set(proxied);
2461
- return proxied;
2462
- });
2463
- }
2464
-
2465
- /**
2466
- * main exports
2467
- */
2468
-
2469
2483
  /**
2470
2484
  * Generated bundle index. Do not edit.
2471
2485
  */
2472
2486
 
2473
- export { NGT_ARGS, NGT_INSTANCE_INPUTS, NGT_INSTANCE_OUTPUTS, NGT_INSTANCE_REF_FACTORY, NGT_PROXY_INSTANCE, NgtArgs, NgtCanvas, NgtCanvasContainer, NgtComponentStore, NgtCursor, NgtInstance, NgtLoader, NgtMathPipe, NgtPiPipe, NgtRadianPipe, NgtRepeat, NgtResize, NgtSidePipe, NgtStore, addAfterEffect, addEffect, addTail, createLoop, defaultProjector, defaultResizeOptions, flushGlobalEffects, injectArgs, injectInstance, injectInstanceRef, injectResizeObserverSupport, injectResizeOptions, provideInstanceRef, provideResizeOptions, proxify, rootStateMap, skipFirstUndefined, tapEffect };
2474
- //# sourceMappingURL=angular-three.mjs.map
2487
+ export { NGT_CATALOGUE, NgtArgs, NgtCanvas, NgtCanvasContainer, NgtRepeat, extend };
2475
2488
  //# sourceMappingURL=angular-three.mjs.map