@navaramap/three 0.0.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 (350) hide show
  1. package/LICENSE +8 -0
  2. package/dist/assets/atmosphere/README.md +1 -0
  3. package/dist/assets/atmosphere/higher_order_scattering.bin +0 -0
  4. package/dist/assets/atmosphere/higher_order_scattering.exr +0 -0
  5. package/dist/assets/atmosphere/irradiance.bin +0 -0
  6. package/dist/assets/atmosphere/irradiance.exr +0 -0
  7. package/dist/assets/atmosphere/scattering.bin +0 -0
  8. package/dist/assets/atmosphere/scattering.exr +0 -0
  9. package/dist/assets/atmosphere/single_mie_scattering.bin +0 -0
  10. package/dist/assets/atmosphere/single_mie_scattering.exr +0 -0
  11. package/dist/assets/atmosphere/stars.bin +0 -0
  12. package/dist/assets/atmosphere/transmittance.bin +0 -0
  13. package/dist/assets/atmosphere/transmittance.exr +0 -0
  14. package/dist/assets/cloud/README.md +1 -0
  15. package/dist/assets/cloud/local_weather.png +0 -0
  16. package/dist/assets/cloud/shape.bin +1 -0
  17. package/dist/assets/cloud/shape_detail.bin +1 -0
  18. package/dist/assets/cloud/turbulence.png +0 -0
  19. package/dist/assets/fontWorker-CEJEfRpb.js +2 -0
  20. package/dist/assets/fontWorker-CEJEfRpb.js.map +1 -0
  21. package/dist/assets/index-BUB-ijMO.js +12 -0
  22. package/dist/assets/index-BUB-ijMO.js.map +1 -0
  23. package/dist/assets/navara_wasm_font_worker_bg-665OLxjy.wasm +0 -0
  24. package/dist/assets/navara_wasm_worker_bg-BXhKC5bE.wasm +0 -0
  25. package/dist/assets/noise/README.md +1 -0
  26. package/dist/assets/noise/stbn.bin +0 -0
  27. package/dist/assets/water/README.md +3 -0
  28. package/dist/assets/water/waternormals.jpg +0 -0
  29. package/dist/index.d.ts +10114 -0
  30. package/dist/index.js +34481 -0
  31. package/dist/index.js.map +1 -0
  32. package/dist/index.umd.cjs +6345 -0
  33. package/dist/index.umd.cjs.map +1 -0
  34. package/package.json +95 -0
  35. package/src/Color.ts +111 -0
  36. package/src/ShadowMapViewers.ts +49 -0
  37. package/src/atmosphere.test.ts +76 -0
  38. package/src/atmosphere.ts +364 -0
  39. package/src/bufferView.ts +90 -0
  40. package/src/camera.ts +182 -0
  41. package/src/concurrency.ts +15 -0
  42. package/src/constants/assets.ts +30 -0
  43. package/src/constants/decoder.ts +7 -0
  44. package/src/constants/index.ts +2 -0
  45. package/src/core/BaseDesc.ts +142 -0
  46. package/src/core/BaseHandle.ts +126 -0
  47. package/src/core/DescRegistry.ts +88 -0
  48. package/src/core/EffectDesc.ts +343 -0
  49. package/src/core/EffectDescRegistry.ts +31 -0
  50. package/src/core/InstancedMeshDesc.ts +413 -0
  51. package/src/core/LightDesc.ts +216 -0
  52. package/src/core/LightDescRegistry.ts +32 -0
  53. package/src/core/MeshDesc.ts +457 -0
  54. package/src/core/MeshDescRegistry.ts +32 -0
  55. package/src/core/MeshDescWithSelectiveEffect.ts +129 -0
  56. package/src/core/Registries.ts +59 -0
  57. package/src/core/SelectiveEffectRegistry.ts +90 -0
  58. package/src/core/ViewContext.ts +235 -0
  59. package/src/core/errors.ts +16 -0
  60. package/src/core/index.ts +50 -0
  61. package/src/device.ts +414 -0
  62. package/src/effects/CustomEffectPass.ts +28 -0
  63. package/src/effects/copy.ts +10 -0
  64. package/src/effects/effect.ts +87 -0
  65. package/src/effects/index.ts +4 -0
  66. package/src/effects/render.ts +10 -0
  67. package/src/evaluations/FeatureEvaluator.ts +529 -0
  68. package/src/evaluations/index.ts +1 -0
  69. package/src/event/HillshadeContext.test.ts +232 -0
  70. package/src/event/HillshadeContext.ts +299 -0
  71. package/src/event/context.ts +279 -0
  72. package/src/event/feature.ts +357 -0
  73. package/src/event/featureEvent.ts +81 -0
  74. package/src/event/features/billboard.ts +30 -0
  75. package/src/event/features/model.ts +192 -0
  76. package/src/event/features/modelGpuBytes.test.ts +69 -0
  77. package/src/event/features/modelGpuBytes.ts +87 -0
  78. package/src/event/features/point.ts +25 -0
  79. package/src/event/features/polygon.ts +23 -0
  80. package/src/event/features/polygonOutline.ts +19 -0
  81. package/src/event/features/polyline.ts +19 -0
  82. package/src/event/features/text.ts +71 -0
  83. package/src/event/hillshade.test.ts +842 -0
  84. package/src/event/hillshade.ts +588 -0
  85. package/src/event/index.ts +700 -0
  86. package/src/event/loaders.ts +53 -0
  87. package/src/event/tile.ts +19 -0
  88. package/src/event/worker.ts +999 -0
  89. package/src/event/workerTaskSettlement.test.ts +243 -0
  90. package/src/event/workerTaskSettlement.ts +128 -0
  91. package/src/inMemoryBufferStore.test.ts +60 -0
  92. package/src/inMemoryBufferStore.ts +67 -0
  93. package/src/index.iife.ts +11 -0
  94. package/src/index.ts +2556 -0
  95. package/src/input.ts +144 -0
  96. package/src/layer.ts +183 -0
  97. package/src/layers/effect/FinalCopyEffectDesc.ts +29 -0
  98. package/src/layers/effect/MRTPassEffectDesc.ts +105 -0
  99. package/src/layers/effect/README.md +9 -0
  100. package/src/layers/effect/SelectiveEffectDesc.ts +113 -0
  101. package/src/layers/effect/SkyEnvMapEffectDesc.ts +58 -0
  102. package/src/layers/effect/TransparentPassEffectDesc.ts +62 -0
  103. package/src/layers/effect/index.ts +5 -0
  104. package/src/layers/index.ts +1 -0
  105. package/src/layersManager.ts +63 -0
  106. package/src/light.ts +15 -0
  107. package/src/loaders/AbortableImageLoader.ts +203 -0
  108. package/src/loaders/AbortableTextureLoader.ts +74 -0
  109. package/src/loaders/index.ts +5 -0
  110. package/src/material/enhancer/DESIGN.md +300 -0
  111. package/src/material/enhancer/MaterialEnhancer.ts +112 -0
  112. package/src/material/enhancer/ShaderReplacer.test.ts +114 -0
  113. package/src/material/enhancer/ShaderReplacer.ts +97 -0
  114. package/src/material/enhancer/batchTexture.ts +8 -0
  115. package/src/material/enhancer/index.ts +46 -0
  116. package/src/material/enhancer/instancedSprite/index.ts +7 -0
  117. package/src/material/enhancer/instancedSprite/instancedSpriteBaseEnhancer/index.test.ts +214 -0
  118. package/src/material/enhancer/instancedSprite/instancedSpriteBaseEnhancer/index.ts +134 -0
  119. package/src/material/enhancer/instancedSprite/instancedSpriteBaseEnhancer/markers.ts +21 -0
  120. package/src/material/enhancer/instancedSprite/instancedSpriteBaseEnhancer/material.ts +32 -0
  121. package/src/material/enhancer/instancedSprite/instancedSpriteBaseEnhancer/mutates.test.ts +277 -0
  122. package/src/material/enhancer/instancedSprite/instancedSpriteBaseEnhancer/mutates.ts +151 -0
  123. package/src/material/enhancer/instancedSprite/instancedSpriteBaseEnhancer/shader.test.ts +114 -0
  124. package/src/material/enhancer/instancedSprite/instancedSpriteBaseEnhancer/shader.ts +49 -0
  125. package/src/material/enhancer/instancedSprite/instancedSpriteBaseEnhancer/state.test.ts +82 -0
  126. package/src/material/enhancer/instancedSprite/instancedSpriteBaseEnhancer/state.ts +80 -0
  127. package/src/material/enhancer/instancedSprite/instancedSpriteBaseEnhancer/types.ts +146 -0
  128. package/src/material/enhancer/instancedSprite/instancedSpriteMaterialEnhancer.ts +105 -0
  129. package/src/material/enhancer/model/index.ts +25 -0
  130. package/src/material/enhancer/model/modelBaseEnhancer/index.test.ts +74 -0
  131. package/src/material/enhancer/model/modelBaseEnhancer/index.ts +110 -0
  132. package/src/material/enhancer/model/modelBaseEnhancer/markers.ts +27 -0
  133. package/src/material/enhancer/model/modelBaseEnhancer/material.test.ts +66 -0
  134. package/src/material/enhancer/model/modelBaseEnhancer/material.ts +44 -0
  135. package/src/material/enhancer/model/modelBaseEnhancer/mutates.test.ts +84 -0
  136. package/src/material/enhancer/model/modelBaseEnhancer/mutates.ts +47 -0
  137. package/src/material/enhancer/model/modelBaseEnhancer/shader.test.ts +24 -0
  138. package/src/material/enhancer/model/modelBaseEnhancer/shader.ts +162 -0
  139. package/src/material/enhancer/model/modelBaseEnhancer/state.test.ts +103 -0
  140. package/src/material/enhancer/model/modelBaseEnhancer/state.ts +52 -0
  141. package/src/material/enhancer/model/modelBaseEnhancer/types.ts +89 -0
  142. package/src/material/enhancer/model/modelMaterialEnhancer.ts +91 -0
  143. package/src/material/enhancer/model/modelWaterEnhancer/index.test.ts +71 -0
  144. package/src/material/enhancer/model/modelWaterEnhancer/index.ts +175 -0
  145. package/src/material/enhancer/model/modelWaterEnhancer/material.test.ts +48 -0
  146. package/src/material/enhancer/model/modelWaterEnhancer/material.ts +25 -0
  147. package/src/material/enhancer/model/modelWaterEnhancer/mutates.test.ts +89 -0
  148. package/src/material/enhancer/model/modelWaterEnhancer/mutates.ts +89 -0
  149. package/src/material/enhancer/model/modelWaterEnhancer/shader.test.ts +34 -0
  150. package/src/material/enhancer/model/modelWaterEnhancer/shader.ts +111 -0
  151. package/src/material/enhancer/model/modelWaterEnhancer/state.test.ts +105 -0
  152. package/src/material/enhancer/model/modelWaterEnhancer/state.ts +59 -0
  153. package/src/material/enhancer/model/modelWaterEnhancer/types.ts +141 -0
  154. package/src/material/enhancer/model/pntsEnhancer/index.test.ts +96 -0
  155. package/src/material/enhancer/model/pntsEnhancer/index.ts +82 -0
  156. package/src/material/enhancer/model/pntsEnhancer/markers.ts +6 -0
  157. package/src/material/enhancer/model/pntsEnhancer/material.test.ts +37 -0
  158. package/src/material/enhancer/model/pntsEnhancer/material.ts +21 -0
  159. package/src/material/enhancer/model/pntsEnhancer/mutates.test.ts +80 -0
  160. package/src/material/enhancer/model/pntsEnhancer/mutates.ts +29 -0
  161. package/src/material/enhancer/model/pntsEnhancer/shader.test.ts +32 -0
  162. package/src/material/enhancer/model/pntsEnhancer/shader.ts +56 -0
  163. package/src/material/enhancer/model/pntsEnhancer/state.test.ts +87 -0
  164. package/src/material/enhancer/model/pntsEnhancer/state.ts +29 -0
  165. package/src/material/enhancer/model/pntsEnhancer/types.ts +38 -0
  166. package/src/material/enhancer/polygon/index.ts +22 -0
  167. package/src/material/enhancer/polygon/polygonBaseEnhancer/index.test.ts +101 -0
  168. package/src/material/enhancer/polygon/polygonBaseEnhancer/index.ts +130 -0
  169. package/src/material/enhancer/polygon/polygonBaseEnhancer/markers.ts +23 -0
  170. package/src/material/enhancer/polygon/polygonBaseEnhancer/material.test.ts +19 -0
  171. package/src/material/enhancer/polygon/polygonBaseEnhancer/material.ts +38 -0
  172. package/src/material/enhancer/polygon/polygonBaseEnhancer/mutates.test.ts +75 -0
  173. package/src/material/enhancer/polygon/polygonBaseEnhancer/mutates.ts +122 -0
  174. package/src/material/enhancer/polygon/polygonBaseEnhancer/shader.test.ts +25 -0
  175. package/src/material/enhancer/polygon/polygonBaseEnhancer/shader.ts +249 -0
  176. package/src/material/enhancer/polygon/polygonBaseEnhancer/state.test.ts +102 -0
  177. package/src/material/enhancer/polygon/polygonBaseEnhancer/state.ts +92 -0
  178. package/src/material/enhancer/polygon/polygonBaseEnhancer/types.ts +150 -0
  179. package/src/material/enhancer/polygon/polygonMaterialEnhancer.ts +97 -0
  180. package/src/material/enhancer/polygon/polygonWaterEnhancer/index.test.ts +72 -0
  181. package/src/material/enhancer/polygon/polygonWaterEnhancer/index.ts +164 -0
  182. package/src/material/enhancer/polygon/polygonWaterEnhancer/material.test.ts +48 -0
  183. package/src/material/enhancer/polygon/polygonWaterEnhancer/material.ts +35 -0
  184. package/src/material/enhancer/polygon/polygonWaterEnhancer/mutates.ts +61 -0
  185. package/src/material/enhancer/polygon/polygonWaterEnhancer/shader.test.ts +34 -0
  186. package/src/material/enhancer/polygon/polygonWaterEnhancer/shader.ts +96 -0
  187. package/src/material/enhancer/polygon/polygonWaterEnhancer/state.test.ts +98 -0
  188. package/src/material/enhancer/polygon/polygonWaterEnhancer/state.ts +57 -0
  189. package/src/material/enhancer/polygon/polygonWaterEnhancer/types.ts +115 -0
  190. package/src/material/enhancer/polyline/index.ts +7 -0
  191. package/src/material/enhancer/polyline/polylineBaseEnhancer/index.test.ts +157 -0
  192. package/src/material/enhancer/polyline/polylineBaseEnhancer/index.ts +154 -0
  193. package/src/material/enhancer/polyline/polylineBaseEnhancer/markers.ts +19 -0
  194. package/src/material/enhancer/polyline/polylineBaseEnhancer/material.test.ts +42 -0
  195. package/src/material/enhancer/polyline/polylineBaseEnhancer/material.ts +38 -0
  196. package/src/material/enhancer/polyline/polylineBaseEnhancer/mutates.test.ts +120 -0
  197. package/src/material/enhancer/polyline/polylineBaseEnhancer/mutates.ts +169 -0
  198. package/src/material/enhancer/polyline/polylineBaseEnhancer/shader.test.ts +67 -0
  199. package/src/material/enhancer/polyline/polylineBaseEnhancer/shader.ts +75 -0
  200. package/src/material/enhancer/polyline/polylineBaseEnhancer/state.test.ts +55 -0
  201. package/src/material/enhancer/polyline/polylineBaseEnhancer/state.ts +91 -0
  202. package/src/material/enhancer/polyline/polylineBaseEnhancer/types.ts +170 -0
  203. package/src/material/enhancer/polyline/polylineMaterialEnhancer.ts +102 -0
  204. package/src/material/enhancer/sdfText/index.ts +7 -0
  205. package/src/material/enhancer/sdfText/sdfTextBaseEnhancer/index.ts +125 -0
  206. package/src/material/enhancer/sdfText/sdfTextBaseEnhancer/markers.ts +19 -0
  207. package/src/material/enhancer/sdfText/sdfTextBaseEnhancer/material.test.ts +35 -0
  208. package/src/material/enhancer/sdfText/sdfTextBaseEnhancer/material.ts +32 -0
  209. package/src/material/enhancer/sdfText/sdfTextBaseEnhancer/mutates.test.ts +381 -0
  210. package/src/material/enhancer/sdfText/sdfTextBaseEnhancer/mutates.ts +232 -0
  211. package/src/material/enhancer/sdfText/sdfTextBaseEnhancer/shader.ts +36 -0
  212. package/src/material/enhancer/sdfText/sdfTextBaseEnhancer/state.test.ts +118 -0
  213. package/src/material/enhancer/sdfText/sdfTextBaseEnhancer/state.ts +107 -0
  214. package/src/material/enhancer/sdfText/sdfTextBaseEnhancer/types.ts +184 -0
  215. package/src/material/enhancer/sdfText/sdfTextMaterialEnhancer.ts +100 -0
  216. package/src/material/enhancer/shadowMap/index.ts +7 -0
  217. package/src/material/enhancer/shadowMap/shadowMapDepthEnhancer/index.ts +97 -0
  218. package/src/material/enhancer/shadowMap/shadowMapDepthEnhancer/markers.ts +10 -0
  219. package/src/material/enhancer/shadowMap/shadowMapDepthEnhancer/material.ts +31 -0
  220. package/src/material/enhancer/shadowMap/shadowMapDepthEnhancer/mutates.ts +8 -0
  221. package/src/material/enhancer/shadowMap/shadowMapDepthEnhancer/shader.test.ts +17 -0
  222. package/src/material/enhancer/shadowMap/shadowMapDepthEnhancer/shader.ts +53 -0
  223. package/src/material/enhancer/shadowMap/shadowMapDepthEnhancer/state.ts +17 -0
  224. package/src/material/enhancer/shadowMap/shadowMapDepthEnhancer/types.ts +26 -0
  225. package/src/material/enhancer/test-utils/shaderCompatibility.ts +124 -0
  226. package/src/material/enhancer/tileComposite/compose.test.ts +95 -0
  227. package/src/material/enhancer/tileComposite/compose.ts +112 -0
  228. package/src/material/enhancer/tileComposite/index.ts +23 -0
  229. package/src/material/enhancer/tileComposite/tileCompositeBaseEnhancer/index.ts +11 -0
  230. package/src/material/enhancer/tileComposite/tileCompositeBaseEnhancer/mutates.ts +132 -0
  231. package/src/material/enhancer/tileComposite/tileCompositeBaseEnhancer/shader.ts +245 -0
  232. package/src/material/enhancer/tileComposite/tileCompositeBaseEnhancer/types.ts +43 -0
  233. package/src/material/enhancer/tileComposite/tileElevationHeatmapEnhancer/index.ts +29 -0
  234. package/src/material/enhancer/tileComposite/tileElevationHeatmapEnhancer/mutates.ts +68 -0
  235. package/src/material/enhancer/tileComposite/tileElevationHeatmapEnhancer/shader.ts +24 -0
  236. package/src/material/enhancer/tileComposite/tileHillshadeEnhancer/index.ts +29 -0
  237. package/src/material/enhancer/tileComposite/tileHillshadeEnhancer/mutates.ts +34 -0
  238. package/src/material/enhancer/tileComposite/tileHillshadeEnhancer/shader.ts +36 -0
  239. package/src/material/enhancer/tileComposite/tileWaterEnhancer/index.ts +23 -0
  240. package/src/material/enhancer/tileComposite/tileWaterEnhancer/mutates.ts +23 -0
  241. package/src/material/enhancer/tileComposite/tileWaterEnhancer/shader.ts +8 -0
  242. package/src/material/enhancer/tileComposite/tileWatermaskEnhancer/index.ts +23 -0
  243. package/src/material/enhancer/tileComposite/tileWatermaskEnhancer/mutates.ts +34 -0
  244. package/src/material/enhancer/tileComposite/tileWatermaskEnhancer/shader.ts +13 -0
  245. package/src/material/enhancer/tileComposite/types.ts +58 -0
  246. package/src/material/index.ts +3 -0
  247. package/src/material/macro/index.ts +1 -0
  248. package/src/material/macro/tileShader.test.ts +136 -0
  249. package/src/material/macro/tileShader.ts +287 -0
  250. package/src/material/overrideMaterialsForMRT.ts +439 -0
  251. package/src/material/selectiveEffectMaterialSetup.test.ts +52 -0
  252. package/src/material/selectiveEffectMaterialSetup.ts +53 -0
  253. package/src/material/types/index.ts +1 -0
  254. package/src/material/types/uniform.ts +1 -0
  255. package/src/mesh/DrapedMesh.ts +101 -0
  256. package/src/mesh/batchTexture.test.ts +646 -0
  257. package/src/mesh/batchTexture.ts +437 -0
  258. package/src/mesh/batchedFeature.ts +186 -0
  259. package/src/mesh/batchedSdfText.ts +484 -0
  260. package/src/mesh/featureMesh.ts +43 -0
  261. package/src/mesh/index.ts +16 -0
  262. package/src/mesh/instanced.ts +117 -0
  263. package/src/mesh/model.ts +483 -0
  264. package/src/mesh/pickableMesh.ts +55 -0
  265. package/src/mesh/pickableMeshWrapper.ts +529 -0
  266. package/src/mesh/polygon.ts +699 -0
  267. package/src/mesh/polygonOutline.ts +393 -0
  268. package/src/mesh/polyline.ts +523 -0
  269. package/src/mesh/releaseGeometryArrays.ts +57 -0
  270. package/src/mesh/rtcRteHelper.ts +211 -0
  271. package/src/mesh/sdfText.test.ts +200 -0
  272. package/src/mesh/sdfText.ts +1019 -0
  273. package/src/mesh/sprite/billboardAtlas.test.ts +177 -0
  274. package/src/mesh/sprite/billboardAtlas.ts +210 -0
  275. package/src/mesh/sprite/billboardAtlasImageLoader.ts +29 -0
  276. package/src/mesh/sprite/index.ts +1 -0
  277. package/src/mesh/sprite/instancedSprite.ts +694 -0
  278. package/src/mesh/tile.test.ts +393 -0
  279. package/src/mesh/tile.ts +1790 -0
  280. package/src/object3DEvent.ts +6 -0
  281. package/src/orchestrators/RenderPassOrchestrator.ts +147 -0
  282. package/src/orchestrators/index.ts +1 -0
  283. package/src/passes/AllDepthCopyPass.ts +165 -0
  284. package/src/passes/CustomRenderPass.ts +279 -0
  285. package/src/passes/NormalCopyPass.ts +125 -0
  286. package/src/passes/RenderTargetCopyPass.ts +91 -0
  287. package/src/passes/SkyEnvMapPass.ts +58 -0
  288. package/src/passes/index.ts +5 -0
  289. package/src/pick/pickHelper.ts +273 -0
  290. package/src/pick/pickTerrain.ts +254 -0
  291. package/src/plugins/AttributionPlugin/AttributionPlugin.test.ts +210 -0
  292. package/src/plugins/AttributionPlugin/attribution.test.ts +225 -0
  293. package/src/plugins/AttributionPlugin/attribution.ts +355 -0
  294. package/src/plugins/AttributionPlugin/attributionStyles.ts +163 -0
  295. package/src/plugins/AttributionPlugin/index.ts +702 -0
  296. package/src/plugins/index.ts +1 -0
  297. package/src/quality.ts +1 -0
  298. package/src/renderOrder.ts +3 -0
  299. package/src/scene.test.ts +164 -0
  300. package/src/scene.ts +127 -0
  301. package/src/shaders/index.ts +1 -0
  302. package/src/solar.test.ts +81 -0
  303. package/src/solar.ts +101 -0
  304. package/src/source.ts +80 -0
  305. package/src/stats.ts +102 -0
  306. package/src/tasks/computeVertexNormalsAsync.ts +15 -0
  307. package/src/tasks/constructPolygonBatchedFeature.ts +34 -0
  308. package/src/tasks/constructPolylineBatchedFeature.ts +27 -0
  309. package/src/tasks/constructQuantizedMeshTerrainMesh.ts +24 -0
  310. package/src/tasks/constructTerrainMesh.ts +25 -0
  311. package/src/tasks/getImageDataFromBlob.ts +13 -0
  312. package/src/tasks/getImageDataFromImageBitmap.ts +12 -0
  313. package/src/tasks/parseMvtTile.ts +22 -0
  314. package/src/tasks/queueTask.ts +19 -0
  315. package/src/tasks/toCreasedNormalsAsync.ts +18 -0
  316. package/src/tasks/upsampleQuantizedMeshTerrainMesh.ts +44 -0
  317. package/src/tasks/upsampleTerrainMesh.ts +39 -0
  318. package/src/texture.ts +19 -0
  319. package/src/textures.ts +13 -0
  320. package/src/tileTexture/CompositeShader.snapshot.test.ts +59 -0
  321. package/src/tileTexture/CompositeShader.test.ts +168 -0
  322. package/src/tileTexture/CompositeShader.ts +53 -0
  323. package/src/tileTexture/SlotPlanner.test.ts +105 -0
  324. package/src/tileTexture/SlotPlanner.ts +102 -0
  325. package/src/tileTexture/TileTextureCache.test.ts +227 -0
  326. package/src/tileTexture/TileTextureCache.ts +192 -0
  327. package/src/tileTexture/TileTextureCompositor.test.ts +260 -0
  328. package/src/tileTexture/TileTextureCompositor.ts +421 -0
  329. package/src/tileTexture/__snapshots__/CompositeShader.snapshot.test.ts.snap +2649 -0
  330. package/src/tileTexture/index.ts +8 -0
  331. package/src/tileTexture/types.ts +150 -0
  332. package/src/type/index.ts +261 -0
  333. package/src/uniforms.ts +20 -0
  334. package/src/utils/arraysEqual.ts +9 -0
  335. package/src/utils/blendModes.ts +80 -0
  336. package/src/utils/geometry.ts +53 -0
  337. package/src/utils/hillshadeNormalMapGenerator.ts +193 -0
  338. package/src/utils/index.ts +8 -0
  339. package/src/utils/object.test.ts +14 -0
  340. package/src/utils/object.ts +7 -0
  341. package/src/utils/replacer.test.ts +42 -0
  342. package/src/utils/replacer.ts +89 -0
  343. package/src/utils/screen.ts +29 -0
  344. package/src/utils/textureFragmentIndex.test.ts +224 -0
  345. package/src/utils/textureFragmentIndex.ts +97 -0
  346. package/src/utils/worker.ts +3 -0
  347. package/src/vite-env.d.ts +2 -0
  348. package/src/worker/index.ts +13 -0
  349. package/src/worker/tasks/computeVertexNormals.ts +15 -0
  350. package/src/worker/tasks/toCreasedNormals.ts +22 -0
@@ -0,0 +1,842 @@
1
+ import type { EntityEvent, HillshadeBackfilledEvent } from "@navaramap/engine";
2
+ import { Texture } from "three";
3
+ import { describe, it, expect, vi, beforeEach } from "vitest";
4
+
5
+ import type { EventContext } from "./context";
6
+ import {
7
+ updatePaddingEdge,
8
+ replicateEdgesToPadding,
9
+ processHillshadeBackfilled,
10
+ processHillshadeCanceled,
11
+ } from "./hillshade";
12
+ import { HillshadeContext } from "./HillshadeContext";
13
+
14
+ // Mock generate_id_from_entity
15
+ vi.mock("@navaramap/core", () => ({
16
+ generate_id_from_entity: (event: any) => `${event.ind}_${event.gen}`,
17
+ }));
18
+
19
+ /**
20
+ * Helper to create a test texture with unique RGBA values per pixel
21
+ * R channel = x coordinate
22
+ * G channel = y coordinate
23
+ * B channel = x + y
24
+ * A channel = 255
25
+ */
26
+ function createTestTexture(size: number): Uint8Array {
27
+ const buffer = new Uint8Array(size * size * 4);
28
+ for (let y = 0; y < size; y++) {
29
+ for (let x = 0; x < size; x++) {
30
+ const idx = (y * size + x) * 4;
31
+ buffer[idx] = x; // R
32
+ buffer[idx + 1] = y; // G
33
+ buffer[idx + 2] = (x + y) % 256; // B
34
+ buffer[idx + 3] = 255; // A
35
+ }
36
+ }
37
+ return buffer;
38
+ }
39
+
40
+ /**
41
+ * Helper to get pixel RGBA values from texture
42
+ */
43
+ function getPixel(
44
+ buffer: Uint8Array,
45
+ x: number,
46
+ y: number,
47
+ width: number,
48
+ ): [number, number, number, number] {
49
+ const idx = (y * width + x) * 4;
50
+ return [buffer[idx], buffer[idx + 1], buffer[idx + 2], buffer[idx + 3]];
51
+ }
52
+
53
+ describe("replicateEdgesToPadding", () => {
54
+ it("replicates edges for a 4×4 content texture (6×6 padded)", () => {
55
+ const contentSize = 4;
56
+ const paddedSize = contentSize + 2;
57
+ const paddedBytes = new Uint8Array(paddedSize * paddedSize * 4);
58
+
59
+ // Fill center with test pattern
60
+ const testContent = createTestTexture(contentSize);
61
+ for (let y = 0; y < contentSize; y++) {
62
+ for (let x = 0; x < contentSize; x++) {
63
+ const srcIdx = (y * contentSize + x) * 4;
64
+ const dstIdx = ((y + 1) * paddedSize + (x + 1)) * 4;
65
+ paddedBytes[dstIdx] = testContent[srcIdx];
66
+ paddedBytes[dstIdx + 1] = testContent[srcIdx + 1];
67
+ paddedBytes[dstIdx + 2] = testContent[srcIdx + 2];
68
+ paddedBytes[dstIdx + 3] = testContent[srcIdx + 3];
69
+ }
70
+ }
71
+
72
+ replicateEdgesToPadding(paddedBytes, paddedSize);
73
+
74
+ // Verify top padding (y=0): should match first content row (y=1)
75
+ for (let x = 1; x <= contentSize; x++) {
76
+ const paddingPixel = getPixel(paddedBytes, x, 0, paddedSize);
77
+ const contentPixel = getPixel(paddedBytes, x, 1, paddedSize);
78
+ expect(paddingPixel).toEqual(contentPixel);
79
+ }
80
+
81
+ // Verify bottom padding (y=5): should match last content row (y=4)
82
+ for (let x = 1; x <= contentSize; x++) {
83
+ const paddingPixel = getPixel(paddedBytes, x, paddedSize - 1, paddedSize);
84
+ const contentPixel = getPixel(paddedBytes, x, contentSize, paddedSize);
85
+ expect(paddingPixel).toEqual(contentPixel);
86
+ }
87
+
88
+ // Verify left padding (x=0): should match first content column (x=1)
89
+ for (let y = 1; y <= contentSize; y++) {
90
+ const paddingPixel = getPixel(paddedBytes, 0, y, paddedSize);
91
+ const contentPixel = getPixel(paddedBytes, 1, y, paddedSize);
92
+ expect(paddingPixel).toEqual(contentPixel);
93
+ }
94
+
95
+ // Verify right padding (x=5): should match last content column (x=4)
96
+ for (let y = 1; y <= contentSize; y++) {
97
+ const paddingPixel = getPixel(paddedBytes, paddedSize - 1, y, paddedSize);
98
+ const contentPixel = getPixel(paddedBytes, contentSize, y, paddedSize);
99
+ expect(paddingPixel).toEqual(contentPixel);
100
+ }
101
+
102
+ // Verify corners
103
+ const topLeft = getPixel(paddedBytes, 0, 0, paddedSize);
104
+ const topLeftContent = getPixel(paddedBytes, 1, 1, paddedSize);
105
+ expect(topLeft).toEqual(topLeftContent);
106
+
107
+ const topRight = getPixel(paddedBytes, paddedSize - 1, 0, paddedSize);
108
+ const topRightContent = getPixel(paddedBytes, contentSize, 1, paddedSize);
109
+ expect(topRight).toEqual(topRightContent);
110
+
111
+ const bottomLeft = getPixel(paddedBytes, 0, paddedSize - 1, paddedSize);
112
+ const bottomLeftContent = getPixel(paddedBytes, 1, contentSize, paddedSize);
113
+ expect(bottomLeft).toEqual(bottomLeftContent);
114
+
115
+ const bottomRight = getPixel(
116
+ paddedBytes,
117
+ paddedSize - 1,
118
+ paddedSize - 1,
119
+ paddedSize,
120
+ );
121
+ const bottomRightContent = getPixel(
122
+ paddedBytes,
123
+ contentSize,
124
+ contentSize,
125
+ paddedSize,
126
+ );
127
+ expect(bottomRight).toEqual(bottomRightContent);
128
+ });
129
+
130
+ it("replicates edges for a 256×256 content texture (258×258 padded)", () => {
131
+ const contentSize = 256;
132
+ const paddedSize = contentSize + 2;
133
+ const paddedBytes = new Uint8Array(paddedSize * paddedSize * 4);
134
+
135
+ // Fill center with test pattern
136
+ const testContent = createTestTexture(contentSize);
137
+ for (let y = 0; y < contentSize; y++) {
138
+ for (let x = 0; x < contentSize; x++) {
139
+ const srcIdx = (y * contentSize + x) * 4;
140
+ const dstIdx = ((y + 1) * paddedSize + (x + 1)) * 4;
141
+ paddedBytes[dstIdx] = testContent[srcIdx];
142
+ paddedBytes[dstIdx + 1] = testContent[srcIdx + 1];
143
+ paddedBytes[dstIdx + 2] = testContent[srcIdx + 2];
144
+ paddedBytes[dstIdx + 3] = testContent[srcIdx + 3];
145
+ }
146
+ }
147
+
148
+ replicateEdgesToPadding(paddedBytes, paddedSize);
149
+
150
+ // Sample check: verify a few pixels on each edge
151
+ // Top edge (y=0, x=128) should match (y=1, x=128)
152
+ expect(getPixel(paddedBytes, 128, 0, paddedSize)).toEqual(
153
+ getPixel(paddedBytes, 128, 1, paddedSize),
154
+ );
155
+
156
+ // Bottom edge (y=257, x=128) should match (y=256, x=128)
157
+ expect(getPixel(paddedBytes, 128, 257, paddedSize)).toEqual(
158
+ getPixel(paddedBytes, 128, 256, paddedSize),
159
+ );
160
+
161
+ // Left edge (x=0, y=128) should match (x=1, y=128)
162
+ expect(getPixel(paddedBytes, 0, 128, paddedSize)).toEqual(
163
+ getPixel(paddedBytes, 1, 128, paddedSize),
164
+ );
165
+
166
+ // Right edge (x=257, y=128) should match (x=256, y=128)
167
+ expect(getPixel(paddedBytes, 257, 128, paddedSize)).toEqual(
168
+ getPixel(paddedBytes, 256, 128, paddedSize),
169
+ );
170
+ });
171
+ });
172
+
173
+ describe("updatePaddingEdge", () => {
174
+ const contentSize = 4;
175
+ const paddedSize = contentSize + 2; // 6×6
176
+
177
+ function createPaddedTexture(): Uint8Array {
178
+ const paddedBytes = new Uint8Array(paddedSize * paddedSize * 4);
179
+ const testContent = createTestTexture(contentSize);
180
+
181
+ // Fill center
182
+ for (let y = 0; y < contentSize; y++) {
183
+ for (let x = 0; x < contentSize; x++) {
184
+ const srcIdx = (y * contentSize + x) * 4;
185
+ const dstIdx = ((y + 1) * paddedSize + (x + 1)) * 4;
186
+ paddedBytes[dstIdx] = testContent[srcIdx];
187
+ paddedBytes[dstIdx + 1] = testContent[srcIdx + 1];
188
+ paddedBytes[dstIdx + 2] = testContent[srcIdx + 2];
189
+ paddedBytes[dstIdx + 3] = testContent[srcIdx + 3];
190
+ }
191
+ }
192
+
193
+ replicateEdgesToPadding(paddedBytes, paddedSize);
194
+ return paddedBytes;
195
+ }
196
+
197
+ function createEdgeData(
198
+ direction: "left" | "right" | "top" | "bottom",
199
+ ): Uint8Array {
200
+ const edgeBytes = new Uint8Array(contentSize * 4);
201
+ for (let i = 0; i < contentSize; i++) {
202
+ const idx = i * 4;
203
+ // Use unique values to verify correct placement
204
+ switch (direction) {
205
+ case "left":
206
+ edgeBytes[idx] = 100 + i; // R
207
+ edgeBytes[idx + 1] = i; // G
208
+ break;
209
+ case "right":
210
+ edgeBytes[idx] = 200 + i; // R
211
+ edgeBytes[idx + 1] = i; // G
212
+ break;
213
+ case "top":
214
+ edgeBytes[idx] = i; // R
215
+ edgeBytes[idx + 1] = 100 + i; // G
216
+ break;
217
+ case "bottom":
218
+ edgeBytes[idx] = i; // R
219
+ edgeBytes[idx + 1] = 200 + i; // G
220
+ break;
221
+ }
222
+ edgeBytes[idx + 2] = 50; // B (constant)
223
+ edgeBytes[idx + 3] = 255; // A
224
+ }
225
+ return edgeBytes;
226
+ }
227
+
228
+ it("updates left padding (direction=0, x=0)", () => {
229
+ const textureData = createPaddedTexture();
230
+ const edgeBytes = createEdgeData("left");
231
+
232
+ updatePaddingEdge(textureData, edgeBytes, paddedSize, 0);
233
+
234
+ // Verify left padding column (x=0, y=1..4)
235
+ for (let y = 0; y < contentSize; y++) {
236
+ const pixel = getPixel(textureData, 0, y + 1, paddedSize);
237
+ expect(pixel[0]).toBe(100 + y); // R
238
+ expect(pixel[1]).toBe(y); // G
239
+ expect(pixel[2]).toBe(50); // B
240
+ expect(pixel[3]).toBe(255); // A
241
+ }
242
+ });
243
+
244
+ it("updates right padding (direction=1, x=paddedSize-1)", () => {
245
+ const textureData = createPaddedTexture();
246
+ const edgeBytes = createEdgeData("right");
247
+
248
+ updatePaddingEdge(textureData, edgeBytes, paddedSize, 1);
249
+
250
+ // Verify right padding column (x=5, y=1..4)
251
+ for (let y = 0; y < contentSize; y++) {
252
+ const pixel = getPixel(textureData, paddedSize - 1, y + 1, paddedSize);
253
+ expect(pixel[0]).toBe(200 + y); // R
254
+ expect(pixel[1]).toBe(y); // G
255
+ expect(pixel[2]).toBe(50); // B
256
+ expect(pixel[3]).toBe(255); // A
257
+ }
258
+ });
259
+
260
+ it("updates top padding (direction=2, y=0)", () => {
261
+ const textureData = createPaddedTexture();
262
+ const edgeBytes = createEdgeData("top");
263
+
264
+ updatePaddingEdge(textureData, edgeBytes, paddedSize, 2);
265
+
266
+ // Verify top padding row (y=0, x=1..4)
267
+ for (let x = 0; x < contentSize; x++) {
268
+ const pixel = getPixel(textureData, x + 1, 0, paddedSize);
269
+ expect(pixel[0]).toBe(x); // R
270
+ expect(pixel[1]).toBe(100 + x); // G
271
+ expect(pixel[2]).toBe(50); // B
272
+ expect(pixel[3]).toBe(255); // A
273
+ }
274
+ });
275
+
276
+ it("updates bottom padding (direction=3, y=paddedSize-1)", () => {
277
+ const textureData = createPaddedTexture();
278
+ const edgeBytes = createEdgeData("bottom");
279
+
280
+ updatePaddingEdge(textureData, edgeBytes, paddedSize, 3);
281
+
282
+ // Verify bottom padding row (y=5, x=1..4)
283
+ for (let x = 0; x < contentSize; x++) {
284
+ const pixel = getPixel(textureData, x + 1, paddedSize - 1, paddedSize);
285
+ expect(pixel[0]).toBe(x); // R
286
+ expect(pixel[1]).toBe(200 + x); // G
287
+ expect(pixel[2]).toBe(50); // B
288
+ expect(pixel[3]).toBe(255); // A
289
+ }
290
+ });
291
+
292
+ it("does not modify content area when updating padding", () => {
293
+ const textureData = createPaddedTexture();
294
+ const edgeBytes = createEdgeData("left");
295
+
296
+ // Capture content before update
297
+ const contentBefore = new Uint8Array(contentSize * contentSize * 4);
298
+ for (let y = 0; y < contentSize; y++) {
299
+ for (let x = 0; x < contentSize; x++) {
300
+ const srcIdx = ((y + 1) * paddedSize + (x + 1)) * 4;
301
+ const dstIdx = (y * contentSize + x) * 4;
302
+ contentBefore[dstIdx] = textureData[srcIdx];
303
+ contentBefore[dstIdx + 1] = textureData[srcIdx + 1];
304
+ contentBefore[dstIdx + 2] = textureData[srcIdx + 2];
305
+ contentBefore[dstIdx + 3] = textureData[srcIdx + 3];
306
+ }
307
+ }
308
+
309
+ updatePaddingEdge(textureData, edgeBytes, paddedSize, 0);
310
+
311
+ // Verify content is unchanged
312
+ for (let y = 0; y < contentSize; y++) {
313
+ for (let x = 0; x < contentSize; x++) {
314
+ const srcIdx = ((y + 1) * paddedSize + (x + 1)) * 4;
315
+ const dstIdx = (y * contentSize + x) * 4;
316
+ expect(textureData[srcIdx]).toBe(contentBefore[dstIdx]);
317
+ expect(textureData[srcIdx + 1]).toBe(contentBefore[dstIdx + 1]);
318
+ expect(textureData[srcIdx + 2]).toBe(contentBefore[dstIdx + 2]);
319
+ expect(textureData[srcIdx + 3]).toBe(contentBefore[dstIdx + 3]);
320
+ }
321
+ }
322
+ });
323
+
324
+ it("handles 256×256 content (258×258 padded)", () => {
325
+ const largeContentSize = 256;
326
+ const largePaddedSize = largeContentSize + 2;
327
+ const textureData = new Uint8Array(largePaddedSize * largePaddedSize * 4);
328
+ const edgeBytes = new Uint8Array(largeContentSize * 4);
329
+
330
+ // Fill edge with marker values
331
+ for (let i = 0; i < largeContentSize; i++) {
332
+ edgeBytes[i * 4] = 123; // R
333
+ edgeBytes[i * 4 + 1] = i % 256; // G
334
+ edgeBytes[i * 4 + 2] = 45; // B
335
+ edgeBytes[i * 4 + 3] = 255; // A
336
+ }
337
+
338
+ updatePaddingEdge(textureData, edgeBytes, largePaddedSize, 0);
339
+
340
+ // Verify left padding (sample a few pixels)
341
+ for (let y = 0; y < largeContentSize; y += 64) {
342
+ const pixel = getPixel(textureData, 0, y + 1, largePaddedSize);
343
+ expect(pixel[0]).toBe(123);
344
+ expect(pixel[1]).toBe(y % 256);
345
+ expect(pixel[2]).toBe(45);
346
+ expect(pixel[3]).toBe(255);
347
+ }
348
+ });
349
+ });
350
+
351
+ /**
352
+ * Tests for hillshade normal map generation lifecycle
353
+ * Covers: original-before-edges, edges-before-original, size mismatch, temp DEM cleanup
354
+ */
355
+ describe("hillshade normal map generation", () => {
356
+ let mockContext: EventContext;
357
+ let hillshadeContext: HillshadeContext;
358
+ let loadedTexs: Map<string, Texture>;
359
+ let mockBufferStore: Map<number, Uint8Array>;
360
+ let nextHandle: number;
361
+
362
+ beforeEach(() => {
363
+ // Reset state for each test
364
+ hillshadeContext = new HillshadeContext();
365
+ loadedTexs = new Map();
366
+ mockBufferStore = new Map();
367
+ nextHandle = 1;
368
+
369
+ // Create mock ViewContext with renderer
370
+ const mockRenderer = {
371
+ getRenderTarget: vi.fn(() => null),
372
+ setRenderTarget: vi.fn(),
373
+ render: vi.fn(),
374
+ readRenderTargetPixels: vi.fn((_target, _x, _y, _w, _h, buffer) => {
375
+ // Fill with test pattern
376
+ for (let i = 0; i < buffer.length; i += 4) {
377
+ buffer[i] = 128; // R
378
+ buffer[i + 1] = 128; // G
379
+ buffer[i + 2] = 255; // B
380
+ buffer[i + 3] = 255; // A
381
+ }
382
+ }),
383
+ };
384
+
385
+ const mockViewContext = {
386
+ getRenderer: () => mockRenderer,
387
+ };
388
+
389
+ // Create mock TileHandler
390
+ const mockTileHandler = {
391
+ getTile: vi.fn((_handle: bigint) => ({
392
+ coords: { z: 14, x: 14507, y: 6473 },
393
+ })),
394
+ calcMetersPerTexel: vi.fn(() => 7.798810005187988),
395
+ getTileElevationDecoder: vi.fn(() => ({
396
+ r_scaler: 256,
397
+ g_scaler: 1,
398
+ b_scaler: 1 / 256,
399
+ boundary: 0,
400
+ min_offset: 0,
401
+ max_offset: 0,
402
+ epsilon: 1.0,
403
+ offset: -32768,
404
+ })),
405
+ };
406
+
407
+ // Create mock BufferStore
408
+ const mockBuf = {
409
+ u8: vi.fn((handle: number) => mockBufferStore.get(handle)),
410
+ removeU8: vi.fn((handle: number) => mockBufferStore.get(handle)),
411
+ };
412
+
413
+ // Create mock texture fragment index
414
+ const mockTextureFragmentIndex = new Map();
415
+
416
+ // Create mock texture options
417
+ const mockTextureOptions = new Map();
418
+
419
+ mockContext = {
420
+ viewContext: mockViewContext as any,
421
+ loadedTexs,
422
+ buf: mockBuf as any,
423
+ textureFragmentIndex: mockTextureFragmentIndex as any,
424
+ textureOptions: mockTextureOptions as any,
425
+ tileHandler: mockTileHandler as any,
426
+ hillshadeContext,
427
+ eventManager: {
428
+ stacks: { hillshade_backfilled: [] },
429
+ },
430
+ } as unknown as EventContext;
431
+ });
432
+
433
+ /**
434
+ * Helper to create test DEM data (4×4 content = 16 pixels)
435
+ */
436
+ function createTestDemData(): Uint8Array {
437
+ const size = 4;
438
+ const buffer = new Uint8Array(size * size * 4);
439
+ for (let y = 0; y < size; y++) {
440
+ for (let x = 0; x < size; x++) {
441
+ const idx = (y * size + x) * 4;
442
+ buffer[idx] = x * 10; // R
443
+ buffer[idx + 1] = y * 10; // G
444
+ buffer[idx + 2] = (x + y) * 5; // B
445
+ buffer[idx + 3] = 255; // A
446
+ }
447
+ }
448
+ return buffer;
449
+ }
450
+
451
+ /**
452
+ * Helper to create edge data (4 pixels for a 4×4 content texture)
453
+ */
454
+ function createEdgeData(value: number): Uint8Array {
455
+ const edgeSize = 4;
456
+ const buffer = new Uint8Array(edgeSize * 4);
457
+ for (let i = 0; i < edgeSize; i++) {
458
+ buffer[i * 4] = value; // R
459
+ buffer[i * 4 + 1] = value + 10; // G
460
+ buffer[i * 4 + 2] = value + 20; // B
461
+ buffer[i * 4 + 3] = 255; // A
462
+ }
463
+ return buffer;
464
+ }
465
+
466
+ /**
467
+ * Helper to store buffer and get handle
468
+ */
469
+ function storeBuffer(data: Uint8Array): number {
470
+ const handle = nextHandle++;
471
+ mockBufferStore.set(handle, data);
472
+ return handle;
473
+ }
474
+
475
+ it("generates normal map when original DEM arrives (original-before-edges)", () => {
476
+ const entityId = "1_123"; // Format: {entity_ind}_{entity_gen}
477
+ const demData = createTestDemData();
478
+ const demHandle = storeBuffer(demData);
479
+
480
+ const event = {
481
+ ind: 1,
482
+ gen: 123,
483
+ tile_handle: 999n,
484
+ original_handle: demHandle,
485
+ edge_data_handle: -1,
486
+ edge_direction: 0,
487
+ target_entity_ind: undefined,
488
+ target_entity_gen: undefined,
489
+ } as HillshadeBackfilledEvent;
490
+
491
+ processHillshadeBackfilled(mockContext, event);
492
+
493
+ // Verify normal map was generated and stored
494
+ expect(loadedTexs.has(entityId)).toBe(true);
495
+ const normalMap = loadedTexs.get(entityId);
496
+ expect(normalMap).toBeInstanceOf(Texture);
497
+ if (normalMap) {
498
+ expect((normalMap.image as { width: number }).width).toBe(4); // Content size (no padding in normal map)
499
+ }
500
+
501
+ // Verify temp DEM entry was stored for edge updates
502
+ const tempDem = hillshadeContext.getTempDem(entityId);
503
+ expect(tempDem).toBeDefined();
504
+ expect(tempDem?.originalHandle).toBe(demHandle);
505
+ expect(tempDem?.paddedSize).toBe(6); // Padded size (4 + 2)
506
+ expect(tempDem?.receivedEdges.size).toBe(0);
507
+ });
508
+
509
+ it("regenerates normal map when edge arrives after original", () => {
510
+ const entityId = "1_123"; // Format: {entity_ind}_{entity_gen}
511
+
512
+ // Step 1: Original DEM arrives
513
+ const demData = createTestDemData();
514
+ const demHandle = storeBuffer(demData);
515
+ processHillshadeBackfilled(mockContext, {
516
+ ind: 1,
517
+ gen: 123,
518
+ tile_handle: 999n,
519
+ original_handle: demHandle,
520
+ edge_data_handle: -1,
521
+ edge_direction: 0,
522
+ target_entity_ind: undefined,
523
+ target_entity_gen: undefined,
524
+ } as HillshadeBackfilledEvent);
525
+
526
+ const firstNormalMap = loadedTexs.get(entityId);
527
+ expect(firstNormalMap).toBeInstanceOf(Texture);
528
+
529
+ // Step 2: Left edge arrives (direction 0)
530
+ const edgeData = createEdgeData(100);
531
+ const edgeHandle = storeBuffer(edgeData);
532
+ processHillshadeBackfilled(mockContext, {
533
+ ind: 999,
534
+ gen: 999,
535
+ tile_handle: 999n,
536
+ original_handle: -1,
537
+ edge_data_handle: edgeHandle,
538
+ edge_direction: 0,
539
+ target_entity_ind: 1,
540
+ target_entity_gen: 123,
541
+ } as HillshadeBackfilledEvent);
542
+
543
+ // Verify normal map is still the same texture instance (RenderTarget reuse)
544
+ // Content is updated in-place on GPU, texture reference stays the same
545
+ const secondNormalMap = loadedTexs.get(entityId);
546
+ expect(secondNormalMap).toBeInstanceOf(Texture);
547
+ expect(secondNormalMap).toBe(firstNormalMap); // Same texture instance (updated in-place)
548
+
549
+ // Verify edge was marked as received
550
+ const tempDem = hillshadeContext.getTempDem(entityId);
551
+ expect(tempDem?.receivedEdges.has(0)).toBe(true);
552
+ expect(tempDem?.receivedEdges.size).toBe(1);
553
+ });
554
+
555
+ it("cleans up temp DEM after all 4 edges received", () => {
556
+ const entityId = "1_123"; // Format: {entity_ind}_{entity_gen}
557
+
558
+ // Step 1: Original arrives
559
+ const demData = createTestDemData();
560
+ processHillshadeBackfilled(mockContext, {
561
+ ind: 1,
562
+ gen: 123,
563
+ tile_handle: 999n,
564
+ original_handle: storeBuffer(demData),
565
+ edge_data_handle: -1,
566
+ edge_direction: 0,
567
+ target_entity_ind: undefined,
568
+ target_entity_gen: undefined,
569
+ } as HillshadeBackfilledEvent);
570
+
571
+ expect(hillshadeContext.getTempDem(entityId)).toBeDefined();
572
+ expect(loadedTexs.has(entityId)).toBe(true);
573
+
574
+ // Step 2-5: All 4 edges arrive (directions 0, 1, 2, 3)
575
+ for (let direction = 0; direction < 4; direction++) {
576
+ processHillshadeBackfilled(mockContext, {
577
+ ind: 999,
578
+ gen: 999,
579
+ tile_handle: 999n,
580
+ original_handle: -1,
581
+ edge_data_handle: storeBuffer(createEdgeData(100 + direction)),
582
+ edge_direction: direction,
583
+ target_entity_ind: 1,
584
+ target_entity_gen: 123,
585
+ } as HillshadeBackfilledEvent);
586
+
587
+ if (direction < 3) {
588
+ // Not all edges received yet
589
+ expect(hillshadeContext.getTempDem(entityId)).toBeDefined();
590
+ expect(hillshadeContext.getTempDem(entityId)?.receivedEdges.size).toBe(
591
+ direction + 1,
592
+ );
593
+ } else {
594
+ // All 4 edges received, temp DEM should be cleaned up
595
+ expect(hillshadeContext.getTempDem(entityId)).toBeUndefined();
596
+ }
597
+ }
598
+
599
+ // Verify texture still exists after cleanup
600
+ expect(loadedTexs.has(entityId)).toBe(true);
601
+ });
602
+
603
+ it("queues edges that arrive before original (edges-before-original)", () => {
604
+ const entityId = "1_123"; // Format: {entity_ind}_{entity_gen}
605
+
606
+ // Step 1: Edges arrive first (directions 0, 1)
607
+ processHillshadeBackfilled(mockContext, {
608
+ ind: 999,
609
+ gen: 999,
610
+ tile_handle: 999n,
611
+ original_handle: -1,
612
+ edge_data_handle: storeBuffer(createEdgeData(100)),
613
+ edge_direction: 0,
614
+ target_entity_ind: 1,
615
+ target_entity_gen: 123,
616
+ } as HillshadeBackfilledEvent);
617
+
618
+ processHillshadeBackfilled(mockContext, {
619
+ ind: 999,
620
+ gen: 999,
621
+ tile_handle: 999n,
622
+ original_handle: -1,
623
+ edge_data_handle: storeBuffer(createEdgeData(110)),
624
+ edge_direction: 1,
625
+ target_entity_ind: 1,
626
+ target_entity_gen: 123,
627
+ } as HillshadeBackfilledEvent);
628
+
629
+ // Verify edges are queued, no texture created yet
630
+ expect(loadedTexs.has(entityId)).toBe(false);
631
+ const pendingMap = hillshadeContext.pendingEdges.get(entityId);
632
+ expect(pendingMap).toBeDefined();
633
+ expect(pendingMap?.size).toBe(2);
634
+ expect(pendingMap?.has(0)).toBe(true);
635
+ expect(pendingMap?.has(1)).toBe(true);
636
+
637
+ // Step 2: Original arrives
638
+ const demData = createTestDemData();
639
+ processHillshadeBackfilled(mockContext, {
640
+ ind: 1,
641
+ gen: 123,
642
+ tile_handle: 999n,
643
+ original_handle: storeBuffer(demData),
644
+ edge_data_handle: -1,
645
+ edge_direction: 0,
646
+ target_entity_ind: undefined,
647
+ target_entity_gen: undefined,
648
+ } as HillshadeBackfilledEvent);
649
+
650
+ // Verify normal map was generated with pending edges applied
651
+ expect(loadedTexs.has(entityId)).toBe(true);
652
+ expect(loadedTexs.get(entityId)).toBeInstanceOf(Texture);
653
+
654
+ // Verify pending edges were cleared
655
+ expect(hillshadeContext.pendingEdges.has(entityId)).toBe(false);
656
+
657
+ // Verify edges were marked as received in temp DEM
658
+ const tempDem = hillshadeContext.getTempDem(entityId);
659
+ expect(tempDem?.receivedEdges.has(0)).toBe(true);
660
+ expect(tempDem?.receivedEdges.has(1)).toBe(true);
661
+ expect(tempDem?.receivedEdges.size).toBe(2);
662
+ });
663
+
664
+ it("handles multiple entities independently", () => {
665
+ const entity1 = "1_123";
666
+ const entity2 = "2_456";
667
+
668
+ // Create textures for both entities
669
+ processHillshadeBackfilled(mockContext, {
670
+ ind: 1,
671
+ gen: 123,
672
+ tile_handle: 999n,
673
+ original_handle: storeBuffer(createTestDemData()),
674
+ edge_data_handle: -1,
675
+ edge_direction: 0,
676
+ target_entity_ind: undefined,
677
+ target_entity_gen: undefined,
678
+ } as HillshadeBackfilledEvent);
679
+
680
+ processHillshadeBackfilled(mockContext, {
681
+ ind: 2,
682
+ gen: 456,
683
+ tile_handle: 888n,
684
+ original_handle: storeBuffer(createTestDemData()),
685
+ edge_data_handle: -1,
686
+ edge_direction: 0,
687
+ target_entity_ind: undefined,
688
+ target_entity_gen: undefined,
689
+ } as HillshadeBackfilledEvent);
690
+
691
+ // Verify both have textures and temp DEMs
692
+ expect(loadedTexs.has(entity1)).toBe(true);
693
+ expect(loadedTexs.has(entity2)).toBe(true);
694
+ expect(hillshadeContext.getTempDem(entity1)).toBeDefined();
695
+ expect(hillshadeContext.getTempDem(entity2)).toBeDefined();
696
+
697
+ // Send edge to entity1
698
+ processHillshadeBackfilled(mockContext, {
699
+ ind: 999,
700
+ gen: 999,
701
+ tile_handle: 999n,
702
+ original_handle: -1,
703
+ edge_data_handle: storeBuffer(createEdgeData(100)),
704
+ edge_direction: 0,
705
+ target_entity_ind: 1,
706
+ target_entity_gen: 123,
707
+ } as HillshadeBackfilledEvent);
708
+
709
+ // Verify only entity1 was updated
710
+ const tempDem1 = hillshadeContext.getTempDem(entity1);
711
+ const tempDem2 = hillshadeContext.getTempDem(entity2);
712
+ expect(tempDem1?.receivedEdges.size).toBe(1);
713
+ expect(tempDem2?.receivedEdges.size).toBe(0);
714
+ });
715
+
716
+ it("clears pendingEdges and tempDem on cancel but leaves loadedTexs alone", () => {
717
+ const entityId = "1_123";
718
+
719
+ // Queue a pending edge before any texture exists
720
+ processHillshadeBackfilled(mockContext, {
721
+ ind: 999,
722
+ gen: 999,
723
+ tile_handle: 999n,
724
+ original_handle: -1,
725
+ edge_data_handle: storeBuffer(createEdgeData(100)),
726
+ edge_direction: 0,
727
+ target_entity_ind: 1,
728
+ target_entity_gen: 123,
729
+ } as HillshadeBackfilledEvent);
730
+ expect(hillshadeContext.pendingEdges.get(entityId)?.size).toBe(1);
731
+
732
+ // Also seed loadedTexs / tempDem via initial backfill
733
+ processHillshadeBackfilled(mockContext, {
734
+ ind: 1,
735
+ gen: 123,
736
+ tile_handle: 999n,
737
+ original_handle: storeBuffer(createTestDemData()),
738
+ edge_data_handle: -1,
739
+ edge_direction: 255,
740
+ target_entity_ind: undefined,
741
+ target_entity_gen: undefined,
742
+ } as HillshadeBackfilledEvent);
743
+ expect(loadedTexs.has(entityId)).toBe(true);
744
+
745
+ // Cancel the entity
746
+ processHillshadeCanceled(mockContext, {
747
+ ind: 1,
748
+ gen: 123,
749
+ } as EntityEvent);
750
+
751
+ // In-flight state should be dropped
752
+ expect(hillshadeContext.pendingEdges.has(entityId)).toBe(false);
753
+ expect(hillshadeContext.getTempDem(entityId)).toBeUndefined();
754
+ // Texture lifecycle belongs to texture_fragment_removed, not cancel
755
+ expect(loadedTexs.has(entityId)).toBe(true);
756
+ });
757
+
758
+ it("is idempotent for entities with no hillshade state", () => {
759
+ // Cancel an entity that was never seen — must not throw or create state
760
+ expect(() =>
761
+ processHillshadeCanceled(mockContext, {
762
+ ind: 42,
763
+ gen: 7,
764
+ } as EntityEvent),
765
+ ).not.toThrow();
766
+ expect(hillshadeContext.pendingEdges.size).toBe(0);
767
+ expect(loadedTexs.size).toBe(0);
768
+ });
769
+
770
+ it("drops pending backfills targeting the canceled entity but keeps others", () => {
771
+ // Source X has init + 2 edge updates queued. A neighbor Z also has an init
772
+ // queued. Canceling X should drop X's init only; X→Y edge update (keyed by
773
+ // target Y) and Z's init must survive.
774
+ const initX = {
775
+ ind: 10,
776
+ gen: 1,
777
+ tile_handle: 1n,
778
+ original_handle: 42,
779
+ edge_data_handle: -1,
780
+ edge_direction: 255,
781
+ target_entity_ind: undefined,
782
+ target_entity_gen: undefined,
783
+ free: vi.fn(),
784
+ } as unknown as HillshadeBackfilledEvent;
785
+ const edgeXtoY = {
786
+ ind: 10,
787
+ gen: 1,
788
+ tile_handle: 1n,
789
+ original_handle: -1,
790
+ edge_data_handle: 100,
791
+ edge_direction: 0,
792
+ target_entity_ind: 20,
793
+ target_entity_gen: 1,
794
+ free: vi.fn(),
795
+ } as unknown as HillshadeBackfilledEvent;
796
+ const edgeXtoX = {
797
+ ind: 10,
798
+ gen: 1,
799
+ tile_handle: 1n,
800
+ original_handle: -1,
801
+ edge_data_handle: 101,
802
+ edge_direction: 1,
803
+ target_entity_ind: 10,
804
+ target_entity_gen: 1,
805
+ free: vi.fn(),
806
+ } as unknown as HillshadeBackfilledEvent;
807
+ const initZ = {
808
+ ind: 30,
809
+ gen: 2,
810
+ tile_handle: 2n,
811
+ original_handle: 43,
812
+ edge_data_handle: -1,
813
+ edge_direction: 255,
814
+ target_entity_ind: undefined,
815
+ target_entity_gen: undefined,
816
+ free: vi.fn(),
817
+ } as unknown as HillshadeBackfilledEvent;
818
+
819
+ const stack: HillshadeBackfilledEvent[] = [
820
+ initX,
821
+ edgeXtoY,
822
+ edgeXtoX,
823
+ initZ,
824
+ ];
825
+ (mockContext.eventManager as any).stacks.hillshade_backfilled = stack;
826
+
827
+ processHillshadeCanceled(mockContext, {
828
+ ind: 10,
829
+ gen: 1,
830
+ } as EntityEvent);
831
+
832
+ // initX (id "10_1") and edgeXtoX (target "10_1") are dropped.
833
+ // edgeXtoY (target "20_1") and initZ (id "30_2") survive.
834
+ expect(
835
+ (mockContext.eventManager as any).stacks.hillshade_backfilled,
836
+ ).toEqual([edgeXtoY, initZ]);
837
+ expect(initX.free).toHaveBeenCalledTimes(1);
838
+ expect(edgeXtoX.free).toHaveBeenCalledTimes(1);
839
+ expect(edgeXtoY.free).not.toHaveBeenCalled();
840
+ expect(initZ.free).not.toHaveBeenCalled();
841
+ });
842
+ });