@pretty-chitty/core 1.1.2 → 1.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (351) hide show
  1. package/dist/components/BottomBarButton.d.ts +2 -1
  2. package/dist/components/BottomBarButton.d.ts.map +1 -1
  3. package/dist/components/BottomBarButton.js +2 -1
  4. package/dist/components/BottomBarButton.js.map +1 -1
  5. package/dist/components/ClientTrustMatchViewer.d.ts +4 -1
  6. package/dist/components/ClientTrustMatchViewer.d.ts.map +1 -1
  7. package/dist/components/ClientTrustMatchViewer.js +11 -4
  8. package/dist/components/ClientTrustMatchViewer.js.map +1 -1
  9. package/dist/components/ContextGalleryDisplay.js +1 -1
  10. package/dist/components/ContextGalleryDisplay.js.map +1 -1
  11. package/dist/components/DemoWrapper.d.ts +9 -0
  12. package/dist/components/DemoWrapper.d.ts.map +1 -0
  13. package/dist/components/DemoWrapper.js +76 -0
  14. package/dist/components/DemoWrapper.js.map +1 -0
  15. package/dist/components/FullScreenGalleryDisplay.d.ts +2 -0
  16. package/dist/components/FullScreenGalleryDisplay.d.ts.map +1 -0
  17. package/dist/components/FullScreenGalleryDisplay.js +56 -0
  18. package/dist/components/FullScreenGalleryDisplay.js.map +1 -0
  19. package/dist/components/Gallery/AnimationController.d.ts +19 -0
  20. package/dist/components/Gallery/AnimationController.d.ts.map +1 -0
  21. package/dist/components/Gallery/AnimationController.js +82 -0
  22. package/dist/components/Gallery/AnimationController.js.map +1 -0
  23. package/dist/components/Gallery/BuiltItem.d.ts +58 -0
  24. package/dist/components/Gallery/BuiltItem.d.ts.map +1 -0
  25. package/dist/components/Gallery/BuiltItem.js +313 -0
  26. package/dist/components/Gallery/BuiltItem.js.map +1 -0
  27. package/dist/components/Gallery/CameraManager.d.ts +14 -0
  28. package/dist/components/Gallery/CameraManager.d.ts.map +1 -0
  29. package/dist/components/Gallery/CameraManager.js +43 -0
  30. package/dist/components/Gallery/CameraManager.js.map +1 -0
  31. package/dist/components/Gallery/GalleryController.d.ts +30 -0
  32. package/dist/components/Gallery/GalleryController.d.ts.map +1 -0
  33. package/dist/components/Gallery/GalleryController.js +137 -0
  34. package/dist/components/Gallery/GalleryController.js.map +1 -0
  35. package/dist/components/Gallery/GalleryViewer.d.ts +18 -0
  36. package/dist/components/Gallery/GalleryViewer.d.ts.map +1 -0
  37. package/dist/components/Gallery/GalleryViewer.js +141 -0
  38. package/dist/components/Gallery/GalleryViewer.js.map +1 -0
  39. package/dist/components/Gallery/LayoutManager.d.ts +49 -0
  40. package/dist/components/Gallery/LayoutManager.d.ts.map +1 -0
  41. package/dist/components/Gallery/LayoutManager.js +132 -0
  42. package/dist/components/Gallery/LayoutManager.js.map +1 -0
  43. package/dist/components/Gallery/constants.d.ts +13 -0
  44. package/dist/components/Gallery/constants.d.ts.map +1 -0
  45. package/dist/components/Gallery/constants.js +13 -0
  46. package/dist/components/Gallery/constants.js.map +1 -0
  47. package/dist/components/Gallery/index.d.ts +3 -0
  48. package/dist/components/Gallery/index.d.ts.map +1 -0
  49. package/dist/components/Gallery/index.js +2 -0
  50. package/dist/components/Gallery/index.js.map +1 -0
  51. package/dist/components/Gallery/types.d.ts +45 -0
  52. package/dist/components/Gallery/types.d.ts.map +1 -0
  53. package/dist/components/Gallery/types.js +2 -0
  54. package/dist/components/Gallery/types.js.map +1 -0
  55. package/dist/components/GalleryPlayground.js +1 -1
  56. package/dist/components/GalleryPlayground.js.map +1 -1
  57. package/dist/components/GalleryViewer.d.ts +2 -47
  58. package/dist/components/GalleryViewer.d.ts.map +1 -1
  59. package/dist/components/GalleryViewer.js +3 -569
  60. package/dist/components/GalleryViewer.js.map +1 -1
  61. package/dist/components/GameDesigner.d.ts.map +1 -1
  62. package/dist/components/GameDesigner.js +13 -2
  63. package/dist/components/GameDesigner.js.map +1 -1
  64. package/dist/components/InlineGalleryDisplay.d.ts +2 -0
  65. package/dist/components/InlineGalleryDisplay.d.ts.map +1 -0
  66. package/dist/components/InlineGalleryDisplay.js +69 -0
  67. package/dist/components/InlineGalleryDisplay.js.map +1 -0
  68. package/dist/components/LiveButton.d.ts.map +1 -1
  69. package/dist/components/LiveButton.js +1 -1
  70. package/dist/components/LiveButton.js.map +1 -1
  71. package/dist/components/MatchViewer.d.ts +0 -1
  72. package/dist/components/MatchViewer.d.ts.map +1 -1
  73. package/dist/components/MatchViewer.js +20 -10
  74. package/dist/components/MatchViewer.js.map +1 -1
  75. package/dist/components/Panel/MultiPanel.d.ts.map +1 -1
  76. package/dist/components/Panel/MultiPanel.js +14 -7
  77. package/dist/components/Panel/MultiPanel.js.map +1 -1
  78. package/dist/components/Panel/PanelContents.d.ts +2 -1
  79. package/dist/components/Panel/PanelContents.d.ts.map +1 -1
  80. package/dist/components/Panel/PanelContents.js +16 -11
  81. package/dist/components/Panel/PanelContents.js.map +1 -1
  82. package/dist/components/Panel/SinglePanel.d.ts.map +1 -1
  83. package/dist/components/Panel/SinglePanel.js +4 -3
  84. package/dist/components/Panel/SinglePanel.js.map +1 -1
  85. package/dist/components/Panel/ViewerWrapper.d.ts +1 -3
  86. package/dist/components/Panel/ViewerWrapper.d.ts.map +1 -1
  87. package/dist/components/Panel/ViewerWrapper.js +2 -3
  88. package/dist/components/Panel/ViewerWrapper.js.map +1 -1
  89. package/dist/components/Playground.d.ts.map +1 -1
  90. package/dist/components/Playground.js +353 -50
  91. package/dist/components/Playground.js.map +1 -1
  92. package/dist/components/PromptControls.d.ts.map +1 -1
  93. package/dist/components/PromptControls.js +39 -6
  94. package/dist/components/PromptControls.js.map +1 -1
  95. package/dist/components/ServerTrustMatchViewer.d.ts +11 -0
  96. package/dist/components/ServerTrustMatchViewer.d.ts.map +1 -0
  97. package/dist/components/ServerTrustMatchViewer.js +26 -0
  98. package/dist/components/ServerTrustMatchViewer.js.map +1 -0
  99. package/dist/components/Viewer.d.ts.map +1 -1
  100. package/dist/components/Viewer.js +54 -14
  101. package/dist/components/Viewer.js.map +1 -1
  102. package/dist/game/Chit.d.ts +6 -2
  103. package/dist/game/Chit.d.ts.map +1 -1
  104. package/dist/game/Chit.js +50 -6
  105. package/dist/game/Chit.js.map +1 -1
  106. package/dist/game/ClientTimeState.d.ts +1 -0
  107. package/dist/game/ClientTimeState.d.ts.map +1 -1
  108. package/dist/game/ClientTimeState.js +4 -1
  109. package/dist/game/ClientTimeState.js.map +1 -1
  110. package/dist/game/GalleryItemChitChildrenSource.d.ts +1 -0
  111. package/dist/game/GalleryItemChitChildrenSource.d.ts.map +1 -1
  112. package/dist/game/GalleryItemChitChildrenSource.js +1 -0
  113. package/dist/game/GalleryItemChitChildrenSource.js.map +1 -1
  114. package/dist/game/GalleryItemRawSource.d.ts +1 -0
  115. package/dist/game/GalleryItemRawSource.d.ts.map +1 -1
  116. package/dist/game/GalleryItemRawSource.js +1 -0
  117. package/dist/game/GalleryItemRawSource.js.map +1 -1
  118. package/dist/game/Game.d.ts +2 -1
  119. package/dist/game/Game.d.ts.map +1 -1
  120. package/dist/game/GameButton.d.ts +1 -0
  121. package/dist/game/GameButton.d.ts.map +1 -1
  122. package/dist/game/GameButton.js +2 -0
  123. package/dist/game/GameButton.js.map +1 -1
  124. package/dist/game/GameDeckChit.d.ts +6 -0
  125. package/dist/game/GameDeckChit.d.ts.map +1 -1
  126. package/dist/game/GameDeckChit.js +32 -5
  127. package/dist/game/GameDeckChit.js.map +1 -1
  128. package/dist/game/GameMetaData.d.ts +18 -0
  129. package/dist/game/GameMetaData.d.ts.map +1 -0
  130. package/dist/game/GameMetaData.js +2 -0
  131. package/dist/game/GameMetaData.js.map +1 -0
  132. package/dist/game/GameTheme.d.ts +11 -0
  133. package/dist/game/GameTheme.d.ts.map +1 -1
  134. package/dist/game/GameTheme.js +19 -4
  135. package/dist/game/GameTheme.js.map +1 -1
  136. package/dist/game/Match.d.ts +2 -1
  137. package/dist/game/Match.d.ts.map +1 -1
  138. package/dist/game/Match.js +4 -3
  139. package/dist/game/Match.js.map +1 -1
  140. package/dist/game/MatchStorage.d.ts +10 -0
  141. package/dist/game/MatchStorage.d.ts.map +1 -1
  142. package/dist/game/MatchStorage.js +29 -0
  143. package/dist/game/MatchStorage.js.map +1 -1
  144. package/dist/game/ModalState.d.ts +1 -0
  145. package/dist/game/ModalState.d.ts.map +1 -1
  146. package/dist/game/ModalState.js +1 -0
  147. package/dist/game/ModalState.js.map +1 -1
  148. package/dist/game/OrderedOutlet.d.ts.map +1 -1
  149. package/dist/game/OrderedOutlet.js +6 -6
  150. package/dist/game/OrderedOutlet.js.map +1 -1
  151. package/dist/game/Pick.d.ts +11 -1
  152. package/dist/game/Pick.d.ts.map +1 -1
  153. package/dist/game/Pick.js +83 -1
  154. package/dist/game/Pick.js.map +1 -1
  155. package/dist/game/PlayerChit.d.ts +2 -1
  156. package/dist/game/PlayerChit.d.ts.map +1 -1
  157. package/dist/game/PlayerChit.js +13 -1
  158. package/dist/game/PlayerChit.js.map +1 -1
  159. package/dist/game/PlayerInfo.d.ts +2 -1
  160. package/dist/game/PlayerInfo.d.ts.map +1 -1
  161. package/dist/game/PlayerInfo.js +20 -3
  162. package/dist/game/PlayerInfo.js.map +1 -1
  163. package/dist/game/Prompt.d.ts +1 -11
  164. package/dist/game/Prompt.d.ts.map +1 -1
  165. package/dist/game/Prompt.js +0 -32
  166. package/dist/game/Prompt.js.map +1 -1
  167. package/dist/game/RootChit.d.ts +4 -0
  168. package/dist/game/RootChit.d.ts.map +1 -1
  169. package/dist/game/RootChit.js +36 -1
  170. package/dist/game/RootChit.js.map +1 -1
  171. package/dist/game/Turn.d.ts +9 -8
  172. package/dist/game/Turn.d.ts.map +1 -1
  173. package/dist/game/Turn.js +34 -34
  174. package/dist/game/Turn.js.map +1 -1
  175. package/dist/game/TurnState.d.ts +3 -2
  176. package/dist/game/TurnState.d.ts.map +1 -1
  177. package/dist/game/TurnState.js +22 -2
  178. package/dist/game/TurnState.js.map +1 -1
  179. package/dist/game/badAiTransport/BadAIClientPrompts.d.ts +14 -0
  180. package/dist/game/badAiTransport/BadAIClientPrompts.d.ts.map +1 -0
  181. package/dist/game/badAiTransport/BadAIClientPrompts.js +50 -0
  182. package/dist/game/badAiTransport/BadAIClientPrompts.js.map +1 -0
  183. package/dist/game/clientTransport/ClientTime.js +1 -1
  184. package/dist/game/clientTransport/ClientTime.js.map +1 -1
  185. package/dist/game/serverTransport/ServerTime.d.ts.map +1 -1
  186. package/dist/game/serverTransport/ServerTime.js +1 -1
  187. package/dist/game/serverTransport/ServerTime.js.map +1 -1
  188. package/dist/hooks/useButtonGalleriesOptions.d.ts +4 -0
  189. package/dist/hooks/useButtonGalleriesOptions.d.ts.map +1 -0
  190. package/dist/hooks/useButtonGalleriesOptions.js +7 -0
  191. package/dist/hooks/useButtonGalleriesOptions.js.map +1 -0
  192. package/dist/hooks/useEventChannelState.js +1 -1
  193. package/dist/hooks/useEventChannelState.js.map +1 -1
  194. package/dist/hooks/useLoadingStates.d.ts +17 -0
  195. package/dist/hooks/useLoadingStates.d.ts.map +1 -0
  196. package/dist/hooks/useLoadingStates.js +44 -0
  197. package/dist/hooks/useLoadingStates.js.map +1 -0
  198. package/dist/hooks/useModalState.d.ts +2 -1
  199. package/dist/hooks/useModalState.d.ts.map +1 -1
  200. package/dist/hooks/useModalState.js +2 -2
  201. package/dist/hooks/useModalState.js.map +1 -1
  202. package/dist/hooks/usePanelPositioning.d.ts +0 -1
  203. package/dist/hooks/usePanelPositioning.d.ts.map +1 -1
  204. package/dist/hooks/usePanelPositioning.js.map +1 -1
  205. package/dist/index.d.ts +7 -5
  206. package/dist/index.d.ts.map +1 -1
  207. package/dist/index.js +17 -1
  208. package/dist/index.js.map +1 -1
  209. package/dist/rendering/CameraWrapperPerspective.d.ts.map +1 -1
  210. package/dist/rendering/CameraWrapperPerspective.js +1 -1
  211. package/dist/rendering/CameraWrapperPerspective.js.map +1 -1
  212. package/dist/rendering/ChitGalleryItemInstance.d.ts +2 -0
  213. package/dist/rendering/ChitGalleryItemInstance.d.ts.map +1 -1
  214. package/dist/rendering/ChitGalleryItemInstance.js +2 -0
  215. package/dist/rendering/ChitGalleryItemInstance.js.map +1 -1
  216. package/dist/rendering/ChitRenderInstance.d.ts +13 -2
  217. package/dist/rendering/ChitRenderInstance.d.ts.map +1 -1
  218. package/dist/rendering/ChitRenderInstance.js +95 -47
  219. package/dist/rendering/ChitRenderInstance.js.map +1 -1
  220. package/dist/rendering/ChitRenderSpec.d.ts +3 -0
  221. package/dist/rendering/ChitRenderSpec.d.ts.map +1 -1
  222. package/dist/rendering/ChitRenderSpec.js +3 -0
  223. package/dist/rendering/ChitRenderSpec.js.map +1 -1
  224. package/dist/rendering/RootChitRenderInstance.d.ts +5 -2
  225. package/dist/rendering/RootChitRenderInstance.d.ts.map +1 -1
  226. package/dist/rendering/RootChitRenderInstance.js +76 -13
  227. package/dist/rendering/RootChitRenderInstance.js.map +1 -1
  228. package/dist/rendering/SplayCounter.d.ts.map +1 -1
  229. package/dist/rendering/SplayCounter.js +1 -1
  230. package/dist/rendering/SplayCounter.js.map +1 -1
  231. package/dist/rendering/TextureReferenceCounter.d.ts +1 -1
  232. package/dist/rendering/TextureReferenceCounter.d.ts.map +1 -1
  233. package/dist/rendering/TextureReferenceCounter.js +10 -10
  234. package/dist/rendering/TextureReferenceCounter.js.map +1 -1
  235. package/dist/rendering/outline/passes/DepthOcclusionPass.js +1 -1
  236. package/dist/rendering/outline/passes/DepthOcclusionPass.js.map +1 -1
  237. package/dist/utilities/Annotations.d.ts +59 -0
  238. package/dist/utilities/Annotations.d.ts.map +1 -1
  239. package/dist/utilities/Annotations.js +63 -0
  240. package/dist/utilities/Annotations.js.map +1 -1
  241. package/dist/utilities/CanvasStack/CanvasOperations.d.ts +10 -1
  242. package/dist/utilities/CanvasStack/CanvasOperations.d.ts.map +1 -1
  243. package/dist/utilities/CanvasStack/CanvasOperations.js +8 -0
  244. package/dist/utilities/CanvasStack/CanvasOperations.js.map +1 -1
  245. package/dist/utilities/CanvasStack/ReactCanvas.d.ts +4 -2
  246. package/dist/utilities/CanvasStack/ReactCanvas.d.ts.map +1 -1
  247. package/dist/utilities/CanvasStack/ReactCanvas.js +5 -2
  248. package/dist/utilities/CanvasStack/ReactCanvas.js.map +1 -1
  249. package/dist/utilities/CanvasStack/RichTextRenderer.d.ts.map +1 -1
  250. package/dist/utilities/CanvasStack/RichTextRenderer.js +12 -4
  251. package/dist/utilities/CanvasStack/RichTextRenderer.js.map +1 -1
  252. package/dist/utilities/EventChannel.d.ts.map +1 -1
  253. package/dist/utilities/EventChannel.js +2 -3
  254. package/dist/utilities/EventChannel.js.map +1 -1
  255. package/dist/utilities/GlbLoader.d.ts +15 -0
  256. package/dist/utilities/GlbLoader.d.ts.map +1 -0
  257. package/dist/utilities/GlbLoader.js +212 -0
  258. package/dist/utilities/GlbLoader.js.map +1 -0
  259. package/dist/utilities/LayoutHelper.js +23 -2
  260. package/dist/utilities/LayoutHelper.js.map +1 -1
  261. package/dist/utilities/ObjectWithProps.d.ts.map +1 -1
  262. package/dist/utilities/ObjectWithProps.js +32 -3
  263. package/dist/utilities/ObjectWithProps.js.map +1 -1
  264. package/package.json +6 -5
  265. package/src/library/components/BottomBarButton.tsx +3 -0
  266. package/src/library/components/ClientTrustMatchViewer.tsx +23 -8
  267. package/src/library/components/ContextGalleryDisplay.tsx +2 -2
  268. package/src/library/components/DemoWrapper.tsx +113 -0
  269. package/src/library/components/{GalleryDisplay.tsx → FullScreenGalleryDisplay.tsx} +28 -2
  270. package/src/library/components/Gallery/AnimationController.ts +110 -0
  271. package/src/library/components/Gallery/BuiltItem.ts +385 -0
  272. package/src/library/components/Gallery/CameraManager.ts +54 -0
  273. package/src/library/components/Gallery/GalleryController.ts +193 -0
  274. package/src/library/components/Gallery/GalleryViewer.tsx +211 -0
  275. package/src/library/components/Gallery/LayoutManager.ts +166 -0
  276. package/src/library/components/Gallery/constants.ts +12 -0
  277. package/src/library/components/Gallery/index.ts +2 -0
  278. package/src/library/components/Gallery/types.ts +55 -0
  279. package/src/library/components/GalleryPlayground.tsx +1 -1
  280. package/src/library/components/GalleryViewer.tsx +4 -773
  281. package/src/library/components/GameDesigner.tsx +21 -4
  282. package/src/library/components/InlineGalleryDisplay.tsx +101 -0
  283. package/src/library/components/LiveButton.tsx +2 -1
  284. package/src/library/components/MatchViewer.tsx +32 -14
  285. package/src/library/components/Panel/MultiPanel.tsx +20 -8
  286. package/src/library/components/Panel/PanelContents.tsx +17 -12
  287. package/src/library/components/Panel/SinglePanel.tsx +5 -4
  288. package/src/library/components/Panel/ViewerWrapper.tsx +0 -5
  289. package/src/library/components/Playground.tsx +692 -119
  290. package/src/library/components/PromptControls.tsx +61 -8
  291. package/src/library/components/ServerTrustMatchViewer.tsx +53 -0
  292. package/src/library/components/Viewer.tsx +60 -20
  293. package/src/library/game/Chit.ts +53 -6
  294. package/src/library/game/ClientTimeState.ts +5 -1
  295. package/src/library/game/GalleryItemChitChildrenSource.ts +2 -0
  296. package/src/library/game/GalleryItemRawSource.ts +2 -0
  297. package/src/library/game/Game.ts +3 -1
  298. package/src/library/game/GameButton.ts +3 -0
  299. package/src/library/game/GameDeckChit.ts +36 -5
  300. package/src/library/game/GameMetaData.ts +19 -0
  301. package/src/library/game/GameTheme.ts +23 -5
  302. package/src/library/game/Match.ts +4 -3
  303. package/src/library/game/MatchStorage.ts +37 -0
  304. package/src/library/game/ModalState.ts +1 -0
  305. package/src/library/game/OrderedOutlet.ts +6 -6
  306. package/src/library/game/Pick.ts +98 -2
  307. package/src/library/game/PlayerChit.ts +13 -1
  308. package/src/library/game/PlayerInfo.ts +22 -3
  309. package/src/library/game/Prompt.ts +1 -36
  310. package/src/library/game/RootChit.ts +41 -1
  311. package/src/library/game/Turn.ts +37 -40
  312. package/src/library/game/TurnState.ts +22 -3
  313. package/src/library/game/badAiTransport/BadAIClientPrompts.ts +60 -0
  314. package/src/library/game/clientTransport/ClientTime.ts +1 -1
  315. package/src/library/game/serverTransport/ServerTime.ts +2 -2
  316. package/src/library/hooks/useButtonGalleriesOptions.tsx +9 -0
  317. package/src/library/hooks/useEventChannelState.ts +1 -1
  318. package/src/library/hooks/useLoadingStates.tsx +55 -0
  319. package/src/library/hooks/useModalState.tsx +2 -2
  320. package/src/library/hooks/usePanelPositioning.tsx +0 -1
  321. package/src/library/index.ts +21 -1
  322. package/src/library/rendering/CameraWrapperPerspective.ts +10 -12
  323. package/src/library/rendering/ChitGalleryItemInstance.ts +2 -0
  324. package/src/library/rendering/ChitRenderInstance.ts +119 -61
  325. package/src/library/rendering/ChitRenderSpec.ts +4 -0
  326. package/src/library/rendering/RootChitRenderInstance.ts +87 -13
  327. package/src/library/rendering/SplayCounter.tsx +2 -1
  328. package/src/library/rendering/TextureReferenceCounter.ts +9 -9
  329. package/src/library/rendering/outline/passes/DepthOcclusionPass.ts +1 -1
  330. package/src/library/utilities/Annotations.ts +99 -0
  331. package/src/library/utilities/CanvasStack/CanvasOperations.tsx +19 -1
  332. package/src/library/utilities/CanvasStack/ReactCanvas.tsx +10 -3
  333. package/src/library/utilities/CanvasStack/RichTextRenderer.ts +14 -4
  334. package/src/library/utilities/EventChannel.ts +2 -4
  335. package/src/library/utilities/GlbLoader.ts +292 -0
  336. package/src/library/utilities/LayoutHelper.ts +28 -2
  337. package/src/library/utilities/ObjectWithProps.ts +27 -3
  338. package/dist/components/GalleryDisplay.d.ts +0 -2
  339. package/dist/components/GalleryDisplay.d.ts.map +0 -1
  340. package/dist/components/GalleryDisplay.js +0 -42
  341. package/dist/components/GalleryDisplay.js.map +0 -1
  342. package/dist/utilities/OutlineCanvas.d.ts +0 -12
  343. package/dist/utilities/OutlineCanvas.d.ts.map +0 -1
  344. package/dist/utilities/OutlineCanvas.js +0 -31
  345. package/dist/utilities/OutlineCanvas.js.map +0 -1
  346. package/dist/utilities/OutlineGeometry.d.ts +0 -3
  347. package/dist/utilities/OutlineGeometry.d.ts.map +0 -1
  348. package/dist/utilities/OutlineGeometry.js +0 -57
  349. package/dist/utilities/OutlineGeometry.js.map +0 -1
  350. package/src/library/utilities/OutlineCanvas.tsx +0 -45
  351. package/src/library/utilities/OutlineGeometry.ts +0 -69
@@ -0,0 +1,385 @@
1
+ import { Box3, Camera, Group, Mesh, Object3D, PlaneGeometry, Raycaster, Vector3 } from "three";
2
+ import { SceneWrapper } from "../../rendering/outline";
3
+ import { GalleryItem } from "../GalleryViewer";
4
+ import { Easing, Tween } from "@tweenjs/tween.js";
5
+ import { UpdateCallback } from "./types";
6
+ import { LayoutManager } from "./LayoutManager";
7
+ import { AnimationController } from "./AnimationController";
8
+ import { ROTATION_DIVISOR } from "./constants";
9
+ import { TextureReferenceCounter } from "../../rendering/TextureReferenceCounter";
10
+ import { RichTextRenderOptionsParameters } from "../../utilities/CanvasStack/RichTextRenderer";
11
+ import { GameTheme } from "../../game/GameTheme";
12
+ import {
13
+ ColorCanvasOperation,
14
+ LayeredCanvasOperation,
15
+ MarkdownCanvasOperation,
16
+ PadCanvasOperation,
17
+ } from "../../utilities/CanvasStack/CanvasOperations";
18
+ import { CanvasStack } from "../../utilities/CanvasStack/CanvasStack";
19
+
20
+ export class BuiltItem {
21
+ private removing = false;
22
+
23
+ public id: string;
24
+ public dirty = true;
25
+ private enteredAmount: number;
26
+ public group: Group;
27
+ private mesh: Object3D;
28
+
29
+ // to be implemented soon
30
+ private summaryMesh?: Object3D;
31
+ private summaryHeight: number;
32
+
33
+ private center: Vector3;
34
+ private _baseOffsetX: number;
35
+ private offsetAngle = 0;
36
+
37
+ private tween?: Tween<{ x: number }>;
38
+ private enterLeaveTween?: Tween<{ y: number }>;
39
+
40
+ private unsubscribe: UpdateCallback;
41
+
42
+ constructor(
43
+ private layoutManager: LayoutManager,
44
+ private animationController: AnimationController,
45
+ private item: GalleryItem,
46
+ private sceneWrapper: SceneWrapper,
47
+ private index: number,
48
+ private theme: GameTheme,
49
+ ) {
50
+ this.group = new Group();
51
+
52
+ this.item = item;
53
+ this.id = item.id;
54
+ this.mesh = new Group();
55
+ this.enteredAmount = 0;
56
+ this.index = index;
57
+ this.center = new Vector3();
58
+ this.summaryHeight = 0;
59
+ this._baseOffsetX = this.animationController.getOffsetX();
60
+ this.unsubscribe = item.registerUpdateHandler(() => this.rebuildMesh());
61
+
62
+ this.rebuildMesh();
63
+ this.sceneWrapper.scene.add(this.group);
64
+ this.enter();
65
+ }
66
+
67
+ getGalleryItem() {
68
+ return this.item;
69
+ }
70
+
71
+ getIndex() {
72
+ return this.index;
73
+ }
74
+
75
+ getSummaryHeight() {
76
+ return this.summaryHeight;
77
+ }
78
+
79
+ public raycastHitsItem(camera: Camera, x: number, y: number): boolean {
80
+ const { w, h } = this.layoutManager.getDimensions();
81
+ const ndc = new Vector3((x / w) * 2 - 1, -(y / h) * 2 + 1, 0.5);
82
+ ndc.unproject(camera);
83
+ const raycaster = new Raycaster(camera.position, ndc.sub(camera.position).normalize());
84
+
85
+ const combinedBox = new Box3().setFromObject(this.mesh);
86
+ if (this.summaryMesh) {
87
+ const summaryBox = new Box3().setFromObject(this.summaryMesh);
88
+ combinedBox.union(summaryBox);
89
+ }
90
+
91
+ const hit = raycaster.ray.intersectBox(combinedBox, new Vector3());
92
+ return !!hit;
93
+ }
94
+
95
+ setIndex(newIndex: number) {
96
+ if (this.tween) {
97
+ this.tween.stop();
98
+ }
99
+ this.tween = new Tween({ x: this.index })
100
+ .to({ x: newIndex }, this.animationController.changeIndexTweenDuration)
101
+ .easing(Easing.Quadratic.InOut)
102
+ .onUpdate((obj) => {
103
+ this.index = obj.x;
104
+ this.dirty = true;
105
+ })
106
+ .onComplete(() => {
107
+ this.tween = undefined;
108
+ })
109
+ .start();
110
+ }
111
+
112
+ _meshSize = new Vector3(1, 1, 1);
113
+ _meshCenter = new Vector3(0, 0, 0);
114
+
115
+ rebuildMesh() {
116
+ this.group.remove(this.mesh);
117
+
118
+ this.mesh = this.item.createMesh(this.sceneWrapper);
119
+ const box3 = new Box3();
120
+ box3.expandByObject(this.mesh);
121
+ if (!box3.isEmpty()) {
122
+ this._meshSize = box3.getSize(new Vector3());
123
+ this._meshCenter = box3.getCenter(new Vector3());
124
+ }
125
+
126
+ this.group.add(this.mesh);
127
+ this.scaleMesh();
128
+
129
+ this.checkRecreateSummaryMesh();
130
+
131
+ TextureReferenceCounter.update();
132
+ this.sceneWrapper.markDirty();
133
+ }
134
+
135
+ scaleMesh() {
136
+ const size = this._meshSize;
137
+ const center = this._meshCenter;
138
+
139
+ // Scale to fit within the provided dimensions (already in scaled/scene units)
140
+ const { w, h } = this.layoutManager.getItemDimensions();
141
+ const xScale = w / size.x;
142
+ const yScale = h / size.y;
143
+ const scale = Math.min(xScale, yScale);
144
+
145
+ this.mesh.scale.set(scale, scale, scale);
146
+ this.mesh.updateMatrix();
147
+
148
+ this.center = center.multiplyScalar(scale).negate();
149
+ this.center.z = 0;
150
+ }
151
+
152
+ remove() {
153
+ if (this.removing) {
154
+ return;
155
+ }
156
+
157
+ this.unsubscribe();
158
+ this.removing = true;
159
+
160
+ if (this.enterLeaveTween) {
161
+ this.enterLeaveTween.stop();
162
+ }
163
+ this.enterLeaveTween = new Tween({ y: this.enteredAmount })
164
+ .to({ y: 0 }, this.animationController.exitTweenDuration)
165
+ .easing(Easing.Quadratic.In)
166
+ .onUpdate((obj) => {
167
+ this.enteredAmount = obj.y;
168
+ this.dirty = true;
169
+ })
170
+ .onComplete(() => {
171
+ this.destroy();
172
+ this.enterLeaveTween = undefined;
173
+ })
174
+ .start();
175
+ }
176
+
177
+ private enter() {
178
+ this.enterLeaveTween = new Tween({ y: this.enteredAmount })
179
+ .to({ y: 1 }, this.animationController.enterTweenDuration)
180
+ .easing(Easing.Quadratic.In)
181
+ .onUpdate((obj) => {
182
+ this.enteredAmount = obj.y;
183
+ this.dirty = true;
184
+ })
185
+ .onComplete(() => {
186
+ this.enterLeaveTween = undefined;
187
+ this.enteredAmount = 1;
188
+ this.dirty = true;
189
+ })
190
+ .start();
191
+ }
192
+
193
+ public get baseOffsetX() {
194
+ return this._baseOffsetX;
195
+ }
196
+
197
+ public set baseOffsetX(value: number) {
198
+ if (this._baseOffsetX !== value) {
199
+ this._baseOffsetX = value;
200
+ this.dirty = true;
201
+ }
202
+ }
203
+
204
+ public update() {
205
+ if (this.tween) {
206
+ this.tween.update();
207
+ }
208
+ if (this.enterLeaveTween) {
209
+ this.enterLeaveTween.update();
210
+ }
211
+ if (this.dirty) {
212
+ this.positionGroup();
213
+ this.dirty = false;
214
+ return true;
215
+ }
216
+ return false;
217
+ }
218
+
219
+ public markDirty() {
220
+ this.dirty = true;
221
+ }
222
+
223
+ public layoutDirty() {
224
+ this.dirty = true;
225
+ this.scaleMesh();
226
+ this.checkRecreateSummaryMesh();
227
+ }
228
+
229
+ private positionGroup() {
230
+ const { frontStageWidth, itemsPerPage, itemSpacing } = this.layoutManager.getStageDimensions();
231
+ const { w: itemWidth, summaryMaxHeight } = this.layoutManager.getItemDimensions();
232
+ const { h } = this.layoutManager.getDimensions();
233
+ const initialOffset = -(frontStageWidth / 2 - itemWidth / 2);
234
+ const targetX = initialOffset + this.index * (itemWidth + itemSpacing) + this._baseOffsetX;
235
+
236
+ this.group.position.y =
237
+ (1 - this.enteredAmount) * -h + summaryMaxHeight / 2 + (this.summaryHeight > 0 ? this.theme.spacing / 2 : 0);
238
+
239
+ this.group.position.x = targetX;
240
+
241
+ const largestX = initialOffset + (itemsPerPage - 1) * (itemWidth + itemSpacing);
242
+ if (targetX > largestX) {
243
+ this.applyOvershootEffect(targetX - largestX, largestX, initialOffset);
244
+ this.repositionSummary(targetX - largestX);
245
+ } else if (targetX < initialOffset) {
246
+ this.applyOvershootEffect(-(initialOffset - targetX), largestX, initialOffset);
247
+ this.repositionSummary(-(initialOffset - targetX));
248
+ } else {
249
+ this.group.position.z = 0;
250
+ this.repositionSummary(0);
251
+ }
252
+ }
253
+
254
+ zFactor = 0;
255
+ setZFactor(zFactor: number) {
256
+ if (this.zFactor !== zFactor) {
257
+ this.zFactor = zFactor;
258
+ this.dirty = true;
259
+ }
260
+ }
261
+
262
+ private applyOvershootEffect(overshoot: number, largestX: number, initialOffset: number) {
263
+ const absOvershoot = Math.abs(overshoot);
264
+ const sign = Math.sign(overshoot);
265
+
266
+ this.group.position.x =
267
+ (overshoot > 0 ? largestX : initialOffset) + sign * Math.pow(absOvershoot, 1 - this.zFactor / 50);
268
+ this.group.position.z = (-absOvershoot / 1.5) * this.zFactor;
269
+ this.group.rotation.y = overshoot / ROTATION_DIVISOR;
270
+ // TODO:
271
+ /// - this.offsetAngle;
272
+ }
273
+
274
+ destroy() {
275
+ if (this.enterLeaveTween) {
276
+ this.enterLeaveTween.stop();
277
+ this.enterLeaveTween = undefined;
278
+ }
279
+
280
+ this.group.parent?.remove(this.group);
281
+ this.unsubscribe();
282
+ this.removing = true;
283
+ }
284
+
285
+ //
286
+ // summary crap
287
+ //
288
+
289
+ repositionSummary(overshootX: number) {
290
+ if (this.summaryMesh) {
291
+ const summaryHeight = this.summaryHeight;
292
+ const { h } = this.layoutManager.getItemDimensions();
293
+ this.summaryMesh.position.y = -summaryHeight / 2 - h / 2 - this.theme.spacing;
294
+ this.summaryMesh.position.x = (overshootX * this.zFactor) / 3;
295
+ }
296
+ }
297
+
298
+ private getSummaryText() {
299
+ if (this.layoutManager.getSummaryMode() === "full") {
300
+ return this.item.summary ?? this.item.shortSummary;
301
+ } else if (this.layoutManager.getSummaryMode() === "partial") {
302
+ return this.item.shortSummary;
303
+ }
304
+ return undefined;
305
+ }
306
+
307
+ private getSummaryRenderingOptions(): RichTextRenderOptionsParameters {
308
+ const baseOptions =
309
+ this.layoutManager.getSummaryMode() === "full"
310
+ ? (this.item.summaryRenderingOptions ?? this.item.shortSummaryRenderingOptions)
311
+ : (this.item.shortSummaryRenderingOptions ?? this.item.summaryRenderingOptions);
312
+ return {
313
+ align: "center",
314
+ color: this.theme.dialogForegroundColor,
315
+ fontSize: this.theme.dialogFontSize * window.devicePixelRatio,
316
+ fontFamily: this.theme.fontFamily,
317
+ ...baseOptions,
318
+ };
319
+ }
320
+
321
+ private _lastSummaryMeshWidth = 0;
322
+ private checkRecreateSummaryMesh() {
323
+ const { w: itemWidth } = this.layoutManager.getItemDimensions();
324
+ if (this._lastSummaryMeshWidth !== itemWidth) {
325
+ this.createHelpText();
326
+ this._lastSummaryMeshWidth = itemWidth;
327
+ }
328
+ }
329
+
330
+ private createHelpText() {
331
+ // Remove old summary mesh if it exists
332
+ if (this.summaryMesh) {
333
+ this.summaryMesh.removeFromParent();
334
+ this.summaryMesh = undefined;
335
+ }
336
+
337
+ const summary = this.getSummaryText();
338
+ if (!summary) {
339
+ this.summaryHeight = 0;
340
+ return;
341
+ }
342
+
343
+ const renderingOptions = this.getSummaryRenderingOptions();
344
+ const summaryMesh = this.createSummaryMesh(summary, renderingOptions);
345
+
346
+ this.summaryMesh = summaryMesh;
347
+ this.group.add(summaryMesh);
348
+ }
349
+
350
+ private createSummaryMesh(summary: string, specs: RichTextRenderOptionsParameters): Mesh {
351
+ const { w: itemWidth, h: itemHeight } = this.layoutManager.getItemDimensions();
352
+ const pad =
353
+ this.layoutManager.getSummaryMode() === "full"
354
+ ? this.theme.spacing * window.devicePixelRatio
355
+ : this.theme.spacing * 0.5 * window.devicePixelRatio;
356
+
357
+ const markdown = new MarkdownCanvasOperation(summary, this.item.summaryIconMap ?? {}, specs);
358
+ const ops = new LayeredCanvasOperation([
359
+ new ColorCanvasOperation(this.theme.gallerySummaryBackgroundColor, this.theme.gallerySummaryBackgroundOpacity),
360
+ new PadCanvasOperation({ top: pad, bottom: pad, left: pad, right: pad }, markdown),
361
+ ]);
362
+
363
+ // Calculate the maximum available height for the summary
364
+ const height = itemHeight * 2; // arbitrary large height to allow text to flow
365
+
366
+ // First pass: render with full height to let text flow and calculate actual size
367
+ const stack1 = new CanvasStack(itemWidth * window.devicePixelRatio, height * window.devicePixelRatio, ops);
368
+ stack1.render();
369
+
370
+ // Second pass: render with actual measured height for final material
371
+ const stack2 = new CanvasStack(itemWidth * window.devicePixelRatio, markdown.height + pad * 2, ops);
372
+ stack2.render();
373
+
374
+ const material = stack2.material;
375
+ material.transparent = true;
376
+ material.depthWrite = true;
377
+
378
+ const finalHeight = stack2.height / window.devicePixelRatio;
379
+ this.summaryHeight = finalHeight;
380
+
381
+ const geometry = new PlaneGeometry(itemWidth, finalHeight);
382
+ const mesh = new Mesh(geometry, material);
383
+ return mesh;
384
+ }
385
+ }
@@ -0,0 +1,54 @@
1
+ import { DirectionalLight, Fog, PerspectiveCamera, Vector3, AmbientLight, Scene } from "three";
2
+
3
+ export class CameraManager {
4
+ public camera: PerspectiveCamera;
5
+ private light: DirectionalLight;
6
+ private baseCameraZ = 500;
7
+
8
+ constructor(
9
+ private scene: Scene,
10
+ public offsetAngle: number,
11
+ fov: number,
12
+ ) {
13
+ this.camera = new PerspectiveCamera(fov, 1, 0.1, 10000);
14
+ this.camera.position.z = 500;
15
+
16
+ this.light = new DirectionalLight(0xffffff, 1);
17
+ this.light.position.copy(this.camera.position);
18
+ scene.add(this.light);
19
+
20
+ const ambient = new AmbientLight(0xffffff, 1);
21
+ scene.add(ambient);
22
+ }
23
+
24
+ updateCameraPosition(maxSummaryHeight: number) {
25
+ const verticalOffset = -maxSummaryHeight / 2;
26
+
27
+ this.camera.position.z = Math.cos(this.offsetAngle) * this.baseCameraZ;
28
+ this.camera.position.y = Math.sin(this.offsetAngle) * this.baseCameraZ + verticalOffset;
29
+ this.camera.lookAt(new Vector3(this.camera.position.x, verticalOffset, 0));
30
+
31
+ this.light.position.copy(this.camera.position);
32
+ this.light.lookAt(0, verticalOffset, 0);
33
+ }
34
+
35
+ updateCameraZDistance(w: number) {
36
+ const aspect = this.camera.aspect;
37
+ const vFov = (this.camera.fov * Math.PI) / 180;
38
+ const hFov = 2 * Math.atan(aspect * Math.tan(vFov / 2));
39
+ this.baseCameraZ = w / (2 * Math.tan(hFov / 2));
40
+ this.camera.position.x = 0;
41
+ }
42
+
43
+ setupFogAndClipping(w: number) {
44
+ this.scene.fog = new Fog(0x000000, this.baseCameraZ, this.baseCameraZ + w * 2);
45
+ this.camera.near = this.baseCameraZ * 0.5;
46
+ this.camera.far = this.baseCameraZ + w * 9;
47
+ this.camera.updateProjectionMatrix();
48
+ }
49
+
50
+ setAspect(w: number, h: number) {
51
+ this.camera.aspect = w / h;
52
+ this.camera.updateProjectionMatrix();
53
+ }
54
+ }
@@ -0,0 +1,193 @@
1
+ import { Object3D } from "three";
2
+ import { GameTheme } from "../../game/GameTheme";
3
+ import { SceneWrapper } from "../../rendering/outline";
4
+ import { TextureReferenceCounterRootGroup } from "../../rendering/TextureReferenceCounter";
5
+ import { GalleryItem, GallerySizeConfig, SummaryMode } from "./types";
6
+ import { CameraManager } from "./CameraManager";
7
+ import { LayoutManager } from "./LayoutManager";
8
+ import { AnimationController } from "./AnimationController";
9
+ import { BuiltItem } from "./BuiltItem";
10
+
11
+ export class GalleryController implements TextureReferenceCounterRootGroup {
12
+ private cameraManager: CameraManager;
13
+
14
+ private items: BuiltItem[] = [];
15
+ private leavingItems: BuiltItem[] = [];
16
+
17
+ private layoutManager: LayoutManager;
18
+ private animationController: AnimationController;
19
+
20
+ private zFactor = 3;
21
+
22
+ constructor(
23
+ public sceneWrapper: SceneWrapper,
24
+ private theme: GameTheme,
25
+ offsetAngle: number,
26
+ fov: number,
27
+ ) {
28
+ this.cameraManager = new CameraManager(sceneWrapper.scene, offsetAngle, fov);
29
+ this.layoutManager = new LayoutManager(theme);
30
+ this.animationController = new AnimationController();
31
+ }
32
+
33
+ dirty = false;
34
+ markHasChange(): void {
35
+ this.dirty = true;
36
+ }
37
+
38
+ get camera() {
39
+ return this.cameraManager.camera;
40
+ }
41
+
42
+ get showSummary(): SummaryMode {
43
+ return this.layoutManager.getSummaryMode();
44
+ }
45
+
46
+ set showSummary(value: SummaryMode) {
47
+ this.layoutManager.setSummaryMode(value);
48
+ }
49
+
50
+ setTweenDuration(duration: number) {
51
+ this.animationController.setTweenDuration(duration);
52
+ }
53
+
54
+ getRootGroup(): Object3D {
55
+ return this.sceneWrapper.scene;
56
+ }
57
+
58
+ setSize(config: GallerySizeConfig) {
59
+ this.zFactor = config.zFactor;
60
+ this.items.forEach((item) => item.setZFactor(this.zFactor));
61
+
62
+ this.layoutManager.setDimensions(config.w, config.h);
63
+ this.layoutManager.setBaseItemDimensions(config.itemWidth, config.itemHeight, config.itemSpacing);
64
+
65
+ this.cameraManager.setAspect(config.w, config.h);
66
+ this.cameraManager.updateCameraZDistance(config.w);
67
+ this.cameraManager.setupFogAndClipping(config.w);
68
+ this.cameraManager.updateCameraPosition(0);
69
+
70
+ this.pan(0, true);
71
+ }
72
+
73
+ isAnimating() {
74
+ return this.animationController.isAnimating();
75
+ }
76
+
77
+ getItemAtPosition(x: number, y: number): GalleryItem | null {
78
+ const { w } = this.layoutManager.getDimensions();
79
+ const { w: itemWidth } = this.layoutManager.getItemDimensions();
80
+ const { frontStageWidth, itemSpacing } = this.layoutManager.getStageDimensions();
81
+ const paddingX = (w - frontStageWidth) / 2;
82
+ const index = (-this.animationController.getOffsetX() + x - paddingX) / (itemWidth + itemSpacing);
83
+
84
+ const item = this.items.find((item) => index > item.getIndex() && Math.abs(index - item.getIndex()) < 1);
85
+ if (!item) {
86
+ return null;
87
+ }
88
+
89
+ if (index - item.getIndex() > 1 - itemSpacing / (itemWidth + itemSpacing)) {
90
+ return null;
91
+ }
92
+
93
+ if (!item.raycastHitsItem(this.cameraManager.camera, x, y)) {
94
+ return null;
95
+ }
96
+
97
+ return item.getGalleryItem();
98
+ }
99
+
100
+ render(): boolean {
101
+ let changed = this.dirty;
102
+
103
+ while (this.layoutManager.dirty) {
104
+ changed = true;
105
+ this.items.forEach((item) => item.layoutDirty());
106
+ this.layoutManager.dirty = false;
107
+
108
+ const newMaxHeight =
109
+ this.items.length === 0
110
+ ? this.layoutManager.getSummaryMaxHeight()
111
+ : Math.max(...this.items.map((item) => item.getSummaryHeight()));
112
+
113
+ this.layoutManager.setSummaryMaxHeight(newMaxHeight);
114
+ }
115
+
116
+ changed = this.animationController.update() || changed;
117
+
118
+ this.items.forEach((item) => {
119
+ changed = item.update() || changed;
120
+ });
121
+
122
+ this.leavingItems.forEach((item) => {
123
+ changed = true;
124
+ item.update();
125
+ });
126
+
127
+ this.dirty = false;
128
+ return changed;
129
+ }
130
+
131
+ pan(deltaX: number, animate = false) {
132
+ const { w } = this.layoutManager.getDimensions();
133
+ const { itemsPerPage, itemSpacing } = this.layoutManager.getStageDimensions();
134
+ const { w: itemWidth } = this.layoutManager.getItemDimensions();
135
+ this.animationController.pan(deltaX, animate, this.items, itemsPerPage, itemWidth, itemSpacing, w);
136
+ }
137
+
138
+ setItems(items: GalleryItem[]) {
139
+ const prevItemCount = this.items.length;
140
+ if (items.length > 0) {
141
+ this.layoutManager.setItemDimensions(items);
142
+ this.layoutManager.setItemCount(items.length);
143
+ }
144
+
145
+ const itemLookup = this.items.reduce(
146
+ (acc, item) => {
147
+ acc[item.id] = item;
148
+ return acc;
149
+ },
150
+ {} as { [id: string]: BuiltItem },
151
+ );
152
+ const seenIds = new Set<string>(this.items.map((item) => item.id));
153
+
154
+ const newItems: BuiltItem[] = [];
155
+ items.forEach((item, i) => {
156
+ seenIds.delete(item.id);
157
+
158
+ const existingItem = itemLookup[item.id];
159
+ if (!existingItem) {
160
+ const newItem = new BuiltItem(
161
+ this.layoutManager,
162
+ this.animationController,
163
+ item,
164
+ this.sceneWrapper,
165
+ i,
166
+ this.theme,
167
+ );
168
+ newItem.setZFactor(this.zFactor);
169
+ this.items.push(newItem);
170
+ newItems.push(newItem);
171
+ }
172
+ });
173
+
174
+ const itemsToDelete = this.items.filter((item) => seenIds.has(item.id));
175
+ itemsToDelete.forEach((item) => {
176
+ item.remove();
177
+ this.leavingItems.push(item);
178
+ });
179
+
180
+ this.items = this.items.filter((item) => !seenIds.has(item.id));
181
+ this.items.forEach((item, i) => {
182
+ item.setIndex(i);
183
+ });
184
+
185
+ // TODO: this is wrong-ish
186
+ this.cameraManager.updateCameraPosition(0);
187
+
188
+ const currentItemCount = this.items.length;
189
+ if (currentItemCount !== prevItemCount && !this.animationController.isAnimating()) {
190
+ this.pan(0, true);
191
+ }
192
+ }
193
+ }