leaflet-city-mapper 0.0.0-stage → 0.0.2

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 (412) hide show
  1. package/LICENSE +674 -0
  2. package/README.md +78 -2
  3. package/cucumber.mjs +9 -0
  4. package/dist/leaflet-city-mapper.js +2 -0
  5. package/dist/leaflet-city-mapper.js.map +7 -0
  6. package/dist/utils/CELL.js +2 -0
  7. package/dist/utils/CELL.js.map +7 -0
  8. package/dist/utils/addBridges.js +2 -0
  9. package/dist/utils/addBridges.js.map +7 -0
  10. package/dist/utils/addRasterTile.js +2 -0
  11. package/dist/utils/addRasterTile.js.map +7 -0
  12. package/dist/utils/along.js +2 -0
  13. package/dist/utils/along.js.map +7 -0
  14. package/dist/utils/atmosphere.js +2 -0
  15. package/dist/utils/atmosphere.js.map +7 -0
  16. package/dist/utils/attach.js +2 -0
  17. package/dist/utils/attach.js.map +7 -0
  18. package/dist/utils/bindSunlight.js +2 -0
  19. package/dist/utils/bindSunlight.js.map +7 -0
  20. package/dist/utils/box.js +2 -0
  21. package/dist/utils/box.js.map +7 -0
  22. package/dist/utils/bridgeElevation.js +2 -0
  23. package/dist/utils/bridgeElevation.js.map +7 -0
  24. package/dist/utils/build.js +2 -0
  25. package/dist/utils/build.js.map +7 -0
  26. package/dist/utils/cameraControls.js +2 -0
  27. package/dist/utils/cameraControls.js.map +7 -0
  28. package/dist/utils/channel.js +2 -0
  29. package/dist/utils/channel.js.map +7 -0
  30. package/dist/utils/collider.js +2 -0
  31. package/dist/utils/collider.js.map +7 -0
  32. package/dist/utils/colliders.js +2 -0
  33. package/dist/utils/colliders.js.map +7 -0
  34. package/dist/utils/computeBounds.js +2 -0
  35. package/dist/utils/computeBounds.js.map +7 -0
  36. package/dist/utils/conformModelToTerrain.js +2 -0
  37. package/dist/utils/conformModelToTerrain.js.map +7 -0
  38. package/dist/utils/createMesh.js +2 -0
  39. package/dist/utils/createMesh.js.map +7 -0
  40. package/dist/utils/createProgram.js +3 -0
  41. package/dist/utils/createProgram.js.map +7 -0
  42. package/dist/utils/decodeDirectory.js +2 -0
  43. package/dist/utils/decodeDirectory.js.map +7 -0
  44. package/dist/utils/decodeHeader.js +2 -0
  45. package/dist/utils/decodeHeader.js.map +7 -0
  46. package/dist/utils/decodeModel.js +2 -0
  47. package/dist/utils/decodeModel.js.map +7 -0
  48. package/dist/utils/decompress.js +2 -0
  49. package/dist/utils/decompress.js.map +7 -0
  50. package/dist/utils/destroyTexture.js +2 -0
  51. package/dist/utils/destroyTexture.js.map +7 -0
  52. package/dist/utils/disposeMesh.js +2 -0
  53. package/dist/utils/disposeMesh.js.map +7 -0
  54. package/dist/utils/drawGeometry.js +2 -0
  55. package/dist/utils/drawGeometry.js.map +7 -0
  56. package/dist/utils/elevationWGSL.js +24 -0
  57. package/dist/utils/elevationWGSL.js.map +7 -0
  58. package/dist/utils/emptyNode.js +2 -0
  59. package/dist/utils/emptyNode.js.map +7 -0
  60. package/dist/utils/endZoom.js +2 -0
  61. package/dist/utils/endZoom.js.map +7 -0
  62. package/dist/utils/findFeature.js +2 -0
  63. package/dist/utils/findFeature.js.map +7 -0
  64. package/dist/utils/geometry.js +1 -0
  65. package/dist/utils/geometry.js.map +7 -0
  66. package/dist/utils/getElevation.js +2 -0
  67. package/dist/utils/getElevation.js.map +7 -0
  68. package/dist/utils/getHeader.js +2 -0
  69. package/dist/utils/getHeader.js.map +7 -0
  70. package/dist/utils/getZxy.js +2 -0
  71. package/dist/utils/getZxy.js.map +7 -0
  72. package/dist/utils/glass.js +2 -0
  73. package/dist/utils/glass.js.map +7 -0
  74. package/dist/utils/headlight.js +2 -0
  75. package/dist/utils/headlight.js.map +7 -0
  76. package/dist/utils/hit.js +2 -0
  77. package/dist/utils/hit.js.map +7 -0
  78. package/dist/utils/horizontalPosition.js +2 -0
  79. package/dist/utils/horizontalPosition.js.map +7 -0
  80. package/dist/utils/horizontalRay.js +2 -0
  81. package/dist/utils/horizontalRay.js.map +7 -0
  82. package/dist/utils/hull.js +2 -0
  83. package/dist/utils/hull.js.map +7 -0
  84. package/dist/utils/initSunlight.js +2 -0
  85. package/dist/utils/initSunlight.js.map +7 -0
  86. package/dist/utils/initTerrain.js +2 -0
  87. package/dist/utils/initTerrain.js.map +7 -0
  88. package/dist/utils/initTraffic.js +40 -0
  89. package/dist/utils/initTraffic.js.map +7 -0
  90. package/dist/utils/initWalk.js +2 -0
  91. package/dist/utils/initWalk.js.map +7 -0
  92. package/dist/utils/intersectMeshes.js +2 -0
  93. package/dist/utils/intersectMeshes.js.map +7 -0
  94. package/dist/utils/isClockWise.js +2 -0
  95. package/dist/utils/isClockWise.js.map +7 -0
  96. package/dist/utils/lampPostGeometry.js +2 -0
  97. package/dist/utils/lampPostGeometry.js.map +7 -0
  98. package/dist/utils/lightGeometry.js +2 -0
  99. package/dist/utils/lightGeometry.js.map +7 -0
  100. package/dist/utils/lightGrid.js +2 -0
  101. package/dist/utils/lightGrid.js.map +7 -0
  102. package/dist/utils/limbs.js +2 -0
  103. package/dist/utils/limbs.js.map +7 -0
  104. package/dist/utils/loadModel.js +2 -0
  105. package/dist/utils/loadModel.js.map +7 -0
  106. package/dist/utils/loadSource.js +2 -0
  107. package/dist/utils/loadSource.js.map +7 -0
  108. package/dist/utils/loadTerrain.js +2 -0
  109. package/dist/utils/loadTerrain.js.map +7 -0
  110. package/dist/utils/mapCamera.js +2 -0
  111. package/dist/utils/mapCamera.js.map +7 -0
  112. package/dist/utils/materialShaders.js +211 -0
  113. package/dist/utils/materialShaders.js.map +7 -0
  114. package/dist/utils/mirror.js +2 -0
  115. package/dist/utils/mirror.js.map +7 -0
  116. package/dist/utils/moonPosition.js +2 -0
  117. package/dist/utils/moonPosition.js.map +7 -0
  118. package/dist/utils/moves.js +2 -0
  119. package/dist/utils/moves.js.map +7 -0
  120. package/dist/utils/nativeEvents.js +2 -0
  121. package/dist/utils/nativeEvents.js.map +7 -0
  122. package/dist/utils/nativeGeometry.js +2 -0
  123. package/dist/utils/nativeGeometry.js.map +7 -0
  124. package/dist/utils/nativeLayer.js +2 -0
  125. package/dist/utils/nativeLayer.js.map +7 -0
  126. package/dist/utils/nodeMatrix.js +2 -0
  127. package/dist/utils/nodeMatrix.js.map +7 -0
  128. package/dist/utils/onAdd3DLayer.js +2 -0
  129. package/dist/utils/onAdd3DLayer.js.map +7 -0
  130. package/dist/utils/onAddSun.js +2 -0
  131. package/dist/utils/onAddSun.js.map +7 -0
  132. package/dist/utils/onClick.js +2 -0
  133. package/dist/utils/onClick.js.map +7 -0
  134. package/dist/utils/onMapChange.js +2 -0
  135. package/dist/utils/onMapChange.js.map +7 -0
  136. package/dist/utils/onPMTileAdd.js +2 -0
  137. package/dist/utils/onPMTileAdd.js.map +7 -0
  138. package/dist/utils/onPMTileRemove.js +2 -0
  139. package/dist/utils/onPMTileRemove.js.map +7 -0
  140. package/dist/utils/onPointerLeave.js +2 -0
  141. package/dist/utils/onPointerLeave.js.map +7 -0
  142. package/dist/utils/onPointerMove.js +2 -0
  143. package/dist/utils/onPointerMove.js.map +7 -0
  144. package/dist/utils/onRemove3DLayer.js +2 -0
  145. package/dist/utils/onRemove3DLayer.js.map +7 -0
  146. package/dist/utils/onRemoveSun.js +2 -0
  147. package/dist/utils/onRemoveSun.js.map +7 -0
  148. package/dist/utils/onTerrainAdd.js +2 -0
  149. package/dist/utils/onTerrainAdd.js.map +7 -0
  150. package/dist/utils/onTerrainChange.js +2 -0
  151. package/dist/utils/onTerrainChange.js.map +7 -0
  152. package/dist/utils/onTerrainRemove.js +2 -0
  153. package/dist/utils/onTerrainRemove.js.map +7 -0
  154. package/dist/utils/onZoomAnim.js +2 -0
  155. package/dist/utils/onZoomAnim.js.map +7 -0
  156. package/dist/utils/paints.js +2 -0
  157. package/dist/utils/paints.js.map +7 -0
  158. package/dist/utils/parseColor.js +2 -0
  159. package/dist/utils/parseColor.js.map +7 -0
  160. package/dist/utils/parts.js +2 -0
  161. package/dist/utils/parts.js.map +7 -0
  162. package/dist/utils/projectCelestialBody.js +2 -0
  163. package/dist/utils/projectCelestialBody.js.map +7 -0
  164. package/dist/utils/pump.js +2 -0
  165. package/dist/utils/pump.js.map +7 -0
  166. package/dist/utils/rasterTiles.js +2 -0
  167. package/dist/utils/rasterTiles.js.map +7 -0
  168. package/dist/utils/raycast.js +2 -0
  169. package/dist/utils/raycast.js.map +7 -0
  170. package/dist/utils/read.js +2 -0
  171. package/dist/utils/read.js.map +7 -0
  172. package/dist/utils/readAccessor.js +2 -0
  173. package/dist/utils/readAccessor.js.map +7 -0
  174. package/dist/utils/releaseArea.js +2 -0
  175. package/dist/utils/releaseArea.js.map +7 -0
  176. package/dist/utils/removeRasterTile.js +2 -0
  177. package/dist/utils/removeRasterTile.js.map +7 -0
  178. package/dist/utils/render.js +15 -0
  179. package/dist/utils/render.js.map +7 -0
  180. package/dist/utils/renderSunlight.js +13 -0
  181. package/dist/utils/renderSunlight.js.map +7 -0
  182. package/dist/utils/renderers.js +2 -0
  183. package/dist/utils/renderers.js.map +7 -0
  184. package/dist/utils/resizeSunlight.js +2 -0
  185. package/dist/utils/resizeSunlight.js.map +7 -0
  186. package/dist/utils/roofCoverage.js +2 -0
  187. package/dist/utils/roofCoverage.js.map +7 -0
  188. package/dist/utils/routeGeometry.js +2 -0
  189. package/dist/utils/routeGeometry.js.map +7 -0
  190. package/dist/utils/scheduledTrips.js +2 -0
  191. package/dist/utils/scheduledTrips.js.map +7 -0
  192. package/dist/utils/setGeoJSONData.js +2 -0
  193. package/dist/utils/setGeoJSONData.js.map +7 -0
  194. package/dist/utils/setHighlight.js +2 -0
  195. package/dist/utils/setHighlight.js.map +7 -0
  196. package/dist/utils/setPMTileOpacity.js +2 -0
  197. package/dist/utils/setPMTileOpacity.js.map +7 -0
  198. package/dist/utils/setPMTileZIndex.js +2 -0
  199. package/dist/utils/setPMTileZIndex.js.map +7 -0
  200. package/dist/utils/setParent.js +2 -0
  201. package/dist/utils/setParent.js.map +7 -0
  202. package/dist/utils/setTerrainHeight.js +2 -0
  203. package/dist/utils/setTerrainHeight.js.map +7 -0
  204. package/dist/utils/setVertexColor.js +2 -0
  205. package/dist/utils/setVertexColor.js.map +7 -0
  206. package/dist/utils/showFeature.js +2 -0
  207. package/dist/utils/showFeature.js.map +7 -0
  208. package/dist/utils/sizes.js +2 -0
  209. package/dist/utils/sizes.js.map +7 -0
  210. package/dist/utils/skin.js +2 -0
  211. package/dist/utils/skin.js.map +7 -0
  212. package/dist/utils/solarPosition.js +2 -0
  213. package/dist/utils/solarPosition.js.map +7 -0
  214. package/dist/utils/sortMeshes.js +2 -0
  215. package/dist/utils/sortMeshes.js.map +7 -0
  216. package/dist/utils/speed.js +2 -0
  217. package/dist/utils/speed.js.map +7 -0
  218. package/dist/utils/startZoom.js +2 -0
  219. package/dist/utils/startZoom.js.map +7 -0
  220. package/dist/utils/taillight.js +2 -0
  221. package/dist/utils/taillight.js.map +7 -0
  222. package/dist/utils/terrainAtlas.js +2 -0
  223. package/dist/utils/terrainAtlas.js.map +7 -0
  224. package/dist/utils/terrainElevation.js +2 -0
  225. package/dist/utils/terrainElevation.js.map +7 -0
  226. package/dist/utils/terrainGrid.js +2 -0
  227. package/dist/utils/terrainGrid.js.map +7 -0
  228. package/dist/utils/textureSampler.js +2 -0
  229. package/dist/utils/textureSampler.js.map +7 -0
  230. package/dist/utils/tileId.js +2 -0
  231. package/dist/utils/tileId.js.map +7 -0
  232. package/dist/utils/toLanes.js +2 -0
  233. package/dist/utils/toLanes.js.map +7 -0
  234. package/dist/utils/toSRGB.js +2 -0
  235. package/dist/utils/toSRGB.js.map +7 -0
  236. package/dist/utils/tooltip.js +3 -0
  237. package/dist/utils/tooltip.js.map +7 -0
  238. package/dist/utils/traffic.js +2 -0
  239. package/dist/utils/traffic.js.map +7 -0
  240. package/dist/utils/traverse.js +2 -0
  241. package/dist/utils/traverse.js.map +7 -0
  242. package/dist/utils/treeGeometry.js +2 -0
  243. package/dist/utils/treeGeometry.js.map +7 -0
  244. package/dist/utils/triangulate.js +2 -0
  245. package/dist/utils/triangulate.js.map +7 -0
  246. package/dist/utils/tripsAt.js +2 -0
  247. package/dist/utils/tripsAt.js.map +7 -0
  248. package/dist/utils/types.js +2 -0
  249. package/dist/utils/types.js.map +7 -0
  250. package/dist/utils/underEntry.js +2 -0
  251. package/dist/utils/underEntry.js.map +7 -0
  252. package/dist/utils/update.js +2 -0
  253. package/dist/utils/update.js.map +7 -0
  254. package/dist/utils/updateArea.js +2 -0
  255. package/dist/utils/updateArea.js.map +7 -0
  256. package/dist/utils/updateCamera.js +2 -0
  257. package/dist/utils/updateCamera.js.map +7 -0
  258. package/dist/utils/updatePointLights.js +2 -0
  259. package/dist/utils/updatePointLights.js.map +7 -0
  260. package/dist/utils/updateWorldMatrix.js +2 -0
  261. package/dist/utils/updateWorldMatrix.js.map +7 -0
  262. package/dist/utils/vehicleOffsets.js +2 -0
  263. package/dist/utils/vehicleOffsets.js.map +7 -0
  264. package/dist/utils/vehicles.js +2 -0
  265. package/dist/utils/vehicles.js.map +7 -0
  266. package/dist/utils/vertexNormals.js +2 -0
  267. package/dist/utils/vertexNormals.js.map +7 -0
  268. package/dist/utils/walk.js +2 -0
  269. package/dist/utils/walk.js.map +7 -0
  270. package/dist/utils/white.js +2 -0
  271. package/dist/utils/white.js.map +7 -0
  272. package/dist/utils/worldLights.js +2 -0
  273. package/dist/utils/worldLights.js.map +7 -0
  274. package/dist/utils/wrap.js +2 -0
  275. package/dist/utils/wrap.js.map +7 -0
  276. package/package.json +43 -4
  277. package/src/index.js +397 -0
  278. package/src/utils/CELL.js +4 -0
  279. package/src/utils/addBridges.js +66 -0
  280. package/src/utils/addRasterTile.js +112 -0
  281. package/src/utils/along.js +9 -0
  282. package/src/utils/atmosphere.js +20 -0
  283. package/src/utils/attach.js +255 -0
  284. package/src/utils/bindSunlight.js +12 -0
  285. package/src/utils/box.js +1 -0
  286. package/src/utils/bridgeElevation.js +18 -0
  287. package/src/utils/build.js +20 -0
  288. package/src/utils/cameraControls.js +287 -0
  289. package/src/utils/channel.js +4 -0
  290. package/src/utils/collider.js +62 -0
  291. package/src/utils/colliders.js +2 -0
  292. package/src/utils/computeBounds.js +17 -0
  293. package/src/utils/conformModelToTerrain.js +69 -0
  294. package/src/utils/createMesh.js +31 -0
  295. package/src/utils/createProgram.js +92 -0
  296. package/src/utils/decodeDirectory.js +40 -0
  297. package/src/utils/decodeHeader.js +15 -0
  298. package/src/utils/decodeModel.js +43 -0
  299. package/src/utils/decompress.js +5 -0
  300. package/src/utils/destroyTexture.js +7 -0
  301. package/src/utils/disposeMesh.js +13 -0
  302. package/src/utils/drawGeometry.js +122 -0
  303. package/src/utils/elevationWGSL.js +27 -0
  304. package/src/utils/emptyNode.js +9 -0
  305. package/src/utils/endZoom.js +15 -0
  306. package/src/utils/findFeature.js +49 -0
  307. package/src/utils/geometry.js +2 -0
  308. package/src/utils/getElevation.js +10 -0
  309. package/src/utils/getHeader.js +9 -0
  310. package/src/utils/getZxy.js +76 -0
  311. package/src/utils/glass.js +1 -0
  312. package/src/utils/headlight.js +1 -0
  313. package/src/utils/hit.js +41 -0
  314. package/src/utils/horizontalPosition.js +16 -0
  315. package/src/utils/horizontalRay.js +12 -0
  316. package/src/utils/hull.js +1 -0
  317. package/src/utils/initSunlight.js +9 -0
  318. package/src/utils/initTerrain.js +19 -0
  319. package/src/utils/initTraffic.js +93 -0
  320. package/src/utils/initWalk.js +72 -0
  321. package/src/utils/intersectMeshes.js +121 -0
  322. package/src/utils/isClockWise.js +12 -0
  323. package/src/utils/lampPostGeometry.js +26 -0
  324. package/src/utils/lightGeometry.js +22 -0
  325. package/src/utils/lightGrid.js +50 -0
  326. package/src/utils/limbs.js +9 -0
  327. package/src/utils/loadModel.js +217 -0
  328. package/src/utils/loadSource.js +17 -0
  329. package/src/utils/loadTerrain.js +65 -0
  330. package/src/utils/mapCamera.js +432 -0
  331. package/src/utils/materialShaders.js +243 -0
  332. package/src/utils/mirror.js +6 -0
  333. package/src/utils/moonPosition.js +17 -0
  334. package/src/utils/moves.js +11 -0
  335. package/src/utils/nativeEvents.js +77 -0
  336. package/src/utils/nativeGeometry.js +131 -0
  337. package/src/utils/nativeLayer.js +317 -0
  338. package/src/utils/nodeMatrix.js +22 -0
  339. package/src/utils/onAdd3DLayer.js +31 -0
  340. package/src/utils/onAddSun.js +147 -0
  341. package/src/utils/onClick.js +13 -0
  342. package/src/utils/onMapChange.js +16 -0
  343. package/src/utils/onPMTileAdd.js +15 -0
  344. package/src/utils/onPMTileRemove.js +8 -0
  345. package/src/utils/onPointerLeave.js +11 -0
  346. package/src/utils/onPointerMove.js +15 -0
  347. package/src/utils/onRemove3DLayer.js +78 -0
  348. package/src/utils/onRemoveSun.js +3 -0
  349. package/src/utils/onTerrainAdd.js +18 -0
  350. package/src/utils/onTerrainChange.js +13 -0
  351. package/src/utils/onTerrainRemove.js +40 -0
  352. package/src/utils/onZoomAnim.js +14 -0
  353. package/src/utils/paints.js +10 -0
  354. package/src/utils/parseColor.js +42 -0
  355. package/src/utils/parts.js +16 -0
  356. package/src/utils/projectCelestialBody.js +13 -0
  357. package/src/utils/pump.js +226 -0
  358. package/src/utils/rasterTiles.js +285 -0
  359. package/src/utils/raycast.js +60 -0
  360. package/src/utils/read.js +29 -0
  361. package/src/utils/readAccessor.js +68 -0
  362. package/src/utils/releaseArea.js +34 -0
  363. package/src/utils/removeRasterTile.js +13 -0
  364. package/src/utils/render.js +406 -0
  365. package/src/utils/renderSunlight.js +170 -0
  366. package/src/utils/renderers.js +2 -0
  367. package/src/utils/resizeSunlight.js +15 -0
  368. package/src/utils/roofCoverage.js +47 -0
  369. package/src/utils/routeGeometry.js +22 -0
  370. package/src/utils/scheduledTrips.js +29 -0
  371. package/src/utils/setGeoJSONData.js +11 -0
  372. package/src/utils/setHighlight.js +13 -0
  373. package/src/utils/setPMTileOpacity.js +7 -0
  374. package/src/utils/setPMTileZIndex.js +7 -0
  375. package/src/utils/setParent.js +7 -0
  376. package/src/utils/setTerrainHeight.js +10 -0
  377. package/src/utils/setVertexColor.js +14 -0
  378. package/src/utils/showFeature.js +27 -0
  379. package/src/utils/sizes.js +1 -0
  380. package/src/utils/skin.js +2 -0
  381. package/src/utils/solarPosition.js +17 -0
  382. package/src/utils/sortMeshes.js +12 -0
  383. package/src/utils/speed.js +3 -0
  384. package/src/utils/startZoom.js +5 -0
  385. package/src/utils/taillight.js +1 -0
  386. package/src/utils/terrainAtlas.js +21 -0
  387. package/src/utils/terrainElevation.js +20 -0
  388. package/src/utils/terrainGrid.js +23 -0
  389. package/src/utils/textureSampler.js +10 -0
  390. package/src/utils/tileId.js +18 -0
  391. package/src/utils/toLanes.js +7 -0
  392. package/src/utils/toSRGB.js +1 -0
  393. package/src/utils/tooltip.js +6 -0
  394. package/src/utils/traffic.js +315 -0
  395. package/src/utils/traverse.js +6 -0
  396. package/src/utils/treeGeometry.js +45 -0
  397. package/src/utils/triangulate.js +100 -0
  398. package/src/utils/tripsAt.js +16 -0
  399. package/src/utils/types.js +8 -0
  400. package/src/utils/underEntry.js +17 -0
  401. package/src/utils/update.js +18 -0
  402. package/src/utils/updateArea.js +40 -0
  403. package/src/utils/updateCamera.js +32 -0
  404. package/src/utils/updatePointLights.js +38 -0
  405. package/src/utils/updateWorldMatrix.js +47 -0
  406. package/src/utils/vehicleOffsets.js +8 -0
  407. package/src/utils/vehicles.js +65 -0
  408. package/src/utils/vertexNormals.js +30 -0
  409. package/src/utils/walk.js +187 -0
  410. package/src/utils/white.js +1 -0
  411. package/src/utils/worldLights.js +25 -0
  412. package/src/utils/wrap.js +1 -0
@@ -0,0 +1,255 @@
1
+ import { emptyNode } from './emptyNode.js';
2
+ import { setParent } from './setParent.js';
3
+ import { traverse } from './traverse.js';
4
+ import { createProgram } from './createProgram.js';
5
+ import { endZoom } from './endZoom.js';
6
+ import { onMapChange } from './onMapChange.js';
7
+ import { onZoomAnim } from './onZoomAnim.js';
8
+ import { render } from './render.js';
9
+ import { startZoom } from './startZoom.js';
10
+ import { update } from './update.js';
11
+ import { updateArea } from './updateArea.js';
12
+ import { setTerrainHeight } from './setTerrainHeight.js';
13
+ import { resizeSunlight } from './resizeSunlight.js';
14
+
15
+ function showWebGPUWarning(map) {
16
+ const container = map.getContainer();
17
+ if (container.querySelector('.leaflet-webgpu-warning')) return;
18
+ const warning = document.createElement('div');
19
+ warning.className = 'leaflet-webgpu-warning';
20
+ warning.setAttribute('role', 'alert');
21
+ warning.style.cssText = 'position:absolute;z-index:1000;top:50%;left:50%;transform:translate(-50%,-50%);max-width:calc(100% - 32px);box-sizing:border-box;padding:12px 16px;border:1px solid #c98b24;border-radius:6px;background:#fff8e6;box-shadow:0 3px 12px #0005;color:#30251d;font:14px/1.5 sans-serif;text-align:center;pointer-events:auto';
22
+ warning.append('WebGPU is not available in this browser or device. ');
23
+ const link = document.createElement('a');
24
+ link.href = 'https://developer.mozilla.org/en-US/docs/Web/API/WebGPU_API#browser_compatibility';
25
+ link.target = '_blank';
26
+ link.rel = 'noopener noreferrer';
27
+ link.textContent = 'Learn more';
28
+ warning.append(link);
29
+ container.append(warning);
30
+ }
31
+
32
+ /** WebGPU device attachment, materials/pipelines and draw calls. */
33
+ /**
34
+ * Join/create this map's shared scene and rebuild GPU objects if its context changed.
35
+ * Buffer/program handles belong to one context; typed arrays and decoded images do not.
36
+ * @private
37
+ */
38
+
39
+ export async function attach(backend, map) {
40
+ let shared = L.Layer._renderers.get(map);
41
+ if (shared?._rendererReady) {
42
+ await shared._rendererReady.promise;
43
+ }
44
+ if (!shared) {
45
+ shared = backend;
46
+ backend._rendererReady = Promise.withResolvers();
47
+ backend._rendererReady.promise.catch(() => { });
48
+ L.Layer._renderers.set(map, shared);
49
+ const fail = error => {
50
+ showWebGPUWarning(map);
51
+ backend._rendererReady.reject(error);
52
+ L.Layer._renderers.delete(map);
53
+ throw error;
54
+ };
55
+ const canvas = document.createElement('canvas');
56
+ if (!navigator.gpu) {
57
+ fail(new Error('Layer3D requires WebGPU'));
58
+ }
59
+ const adapter = await navigator.gpu.requestAdapter().catch(fail);
60
+ if (!adapter) {
61
+ fail(new Error('Unable to acquire a WebGPU adapter'));
62
+ }
63
+ const device = await adapter.requestDevice().catch(fail);
64
+ device.addEventListener('uncapturederror', event => {
65
+ const owner = L.Layer._renderers.get(map);
66
+ if (owner?.renderer?.device !== device) {
67
+ return;
68
+ }
69
+ for (const layer of owner?._layers || []) {
70
+ layer.fire('error', { error: event.error });
71
+ }
72
+ });
73
+ device.lost.then(info => {
74
+ const owner = L.Layer._renderers.get(map);
75
+ if (!owner || owner.renderer?.device !== device) {
76
+ return;
77
+ }
78
+ const error = new Error(`WebGPU device lost: ${info.message || info.reason}`);
79
+ for (const layer of owner._layers) {
80
+ layer.fire('error', { error });
81
+ }
82
+ });
83
+ const context = canvas.getContext('webgpu');
84
+ if (!context) {
85
+ device.destroy();
86
+ fail(new Error('Unable to create a WebGPU canvas context'));
87
+ }
88
+ const format = navigator.gpu.getPreferredCanvasFormat();
89
+ context.configure({ device, format, alphaMode: 'premultiplied' });
90
+ map.getContainer().querySelector('.leaflet-webgpu-warning')?.remove();
91
+ backend._shared = backend;
92
+ backend._layers = new Set();
93
+ backend._sceneOrigin = backend.location;
94
+ backend.scene = emptyNode();
95
+ // Perspective camera state: fov in degrees, near/far in scene metres.
96
+ backend.camera = {
97
+ position: [0, 0, 0],
98
+ up: [0, 1, 0],
99
+ target: [0, 0, 0],
100
+ viewMatrix: [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1],
101
+ projectionMatrix: [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1],
102
+ projectionViewMatrix: [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1],
103
+ fov: 45,
104
+ aspect: 1,
105
+ near: 0.1,
106
+ far: 10000,
107
+ };
108
+ backend.renderer = {
109
+ device,
110
+ context,
111
+ format,
112
+ canvas,
113
+ dpr: Math.min(window.devicePixelRatio, 2)
114
+ };
115
+ const layout = device.createBindGroupLayout({
116
+ entries: [
117
+ { binding: 0, visibility: GPUShaderStage.VERTEX | GPUShaderStage.FRAGMENT, buffer: { type: 'uniform' } },
118
+ { binding: 1, visibility: GPUShaderStage.FRAGMENT, texture: { sampleType: 'depth' } },
119
+ { binding: 2, visibility: GPUShaderStage.FRAGMENT, sampler: { type: 'comparison' } },
120
+ { binding: 3, visibility: GPUShaderStage.FRAGMENT, buffer: { type: 'read-only-storage' } },
121
+ ]
122
+ });
123
+ const buffer = backend.renderer.device.createBuffer({ size: 80, usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST });
124
+ const sampler = backend.renderer.device.createSampler({ compare: 'less-equal', minFilter: 'linear', magFilter: 'linear' });
125
+ const lights = backend.renderer.device.createBuffer({ size: 32, usage: GPUBufferUsage.STORAGE | GPUBufferUsage.COPY_DST });
126
+ backend.renderer.sunlight = { layout, buffer, sampler, lights, size: 0 };
127
+ const values = new Float32Array(20);
128
+ values.set([1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1]);
129
+ backend.renderer.device.queue.writeBuffer(buffer, 0, values);
130
+ resizeSunlight(backend, 1);
131
+ const emptyImage = document.createElement('canvas');
132
+ emptyImage.width = 1;
133
+ emptyImage.height = 1;
134
+ backend.renderer.roofFallbackTexture = {
135
+ device,
136
+ texture: device.createTexture({
137
+ size: [1, 1],
138
+ format: 'rgba8unorm',
139
+ usage: GPUTextureUsage.TEXTURE_BINDING | GPUTextureUsage.COPY_DST | GPUTextureUsage.RENDER_ATTACHMENT,
140
+ }),
141
+ sampler: device.createSampler({
142
+ addressModeU: 'clamp-to-edge', addressModeV: 'clamp-to-edge',
143
+ }),
144
+ image: emptyImage, format: 'rgba8unorm', srgb: false, uploaded: null,
145
+ };
146
+ shared._size = null;
147
+ canvas.className = 'leaflet-3d';
148
+ canvas.style.cssText = 'position:absolute;top:0;left:0;pointer-events:none';
149
+ canvas.setAttribute('role', 'img');
150
+ canvas.setAttribute('aria-label', backend.options.ariaLabel ?? '3D scene');
151
+ let pane = map.getPane('webgpuPane');
152
+ if (!pane) {
153
+ pane = map.createPane('webgpuPane', map.getContainer());
154
+ // The pitch sky uses 0 and may be inserted after this pane.
155
+ pane.style.zIndex = 1;
156
+ pane.style.pointerEvents = 'none';
157
+ }
158
+ pane.append(canvas);
159
+ map.on('move zoom rotate pitch resize viewreset layeradd layerremove', onMapChange, shared);
160
+ map.on('zoomstart', startZoom, shared);
161
+ map.on('zoomanim', onZoomAnim, shared);
162
+ map.on('zoomend', endZoom, shared);
163
+ backend._rendererReady.resolve();
164
+ delete shared._rendererReady;
165
+ render(shared);
166
+ L.Layer._renderers.set(map, shared);
167
+ }
168
+ const origin = map.options.crs.project(shared._sceneOrigin);
169
+ const point = map.options.crs.project(backend.location);
170
+ const metres = Math.cos(shared._sceneOrigin.lat * Math.PI / 180);
171
+ backend._terrainBaseY ??= backend.root.position[1];
172
+ backend.root.position[0] = (point.x - origin.x) * metres;
173
+ backend.root.position[1] = backend._terrainBaseY;
174
+ backend.root.position[2] = -(point.y - origin.y) * metres;
175
+ setTerrainHeight(backend);
176
+ // Each group's metres are measured at its own latitude, not the shared camera origin.
177
+ backend.root.scale.fill(metres / Math.cos(backend.location.lat * Math.PI / 180));
178
+ backend._shared = shared;
179
+ backend.renderer = shared.renderer;
180
+ backend.scene = shared.scene;
181
+ backend.camera = shared.camera;
182
+ shared._layers.add(backend);
183
+ setParent(backend.root, shared.scene);
184
+ traverse(backend.root, object => {
185
+ if (object.geometry && object.geometry.device !== shared.renderer.device) {
186
+ const geometry = object.geometry;
187
+ const material = object.program._layerMaterial;
188
+ // Retain packed CPU attributes, never old-context GPU buffer handles.
189
+ const attributes = {};
190
+ for (const name of ['position', 'normal', 'color', 'uv', 'index']) {
191
+ const attribute = geometry.attributes[name];
192
+ if (!attribute) continue;
193
+ const { data, size, count, normalized } = attribute;
194
+ attributes[name] = { data: data.slice(0, count * size), size, normalized };
195
+ }
196
+ const device = shared.renderer.device;
197
+ if (attributes.index?.data instanceof Uint8Array) {
198
+ attributes.index.data = Uint16Array.from(attributes.index.data);
199
+ }
200
+ for (const [name, attribute] of Object.entries(attributes)) {
201
+ attribute.size ||= 1;
202
+ attribute.count = attribute.data.length / attribute.size;
203
+ attribute.indexFormat = attribute.data instanceof Uint32Array ? 'uint32' : 'uint16';
204
+ attribute.buffer = device.createBuffer({
205
+ size: Math.max(4, attribute.data.byteLength),
206
+ usage: name === 'index'
207
+ ? GPUBufferUsage.INDEX | GPUBufferUsage.COPY_DST
208
+ : GPUBufferUsage.VERTEX | GPUBufferUsage.COPY_DST,
209
+ });
210
+ device.queue.writeBuffer(attribute.buffer, 0, attribute.data);
211
+ }
212
+ object.geometry = {
213
+ device,
214
+ attributes,
215
+ drawRange: { start: 0, count: attributes.index?.count || attributes.position.count },
216
+ };
217
+ for (const attribute of Object.values(geometry.attributes)) attribute.buffer.destroy();
218
+ object.bindGroup = null;
219
+ object.boundTextures = [];
220
+ for (const key of ['baseTexture', 'metalTexture', 'emissiveTexture', 'occlusionTexture']) {
221
+ const texture = material[key];
222
+ if (!texture || texture.device === device) continue;
223
+ material[key] = {
224
+ ...texture,
225
+ device,
226
+ texture: device.createTexture({
227
+ size: [texture.image.width, texture.image.height],
228
+ format: texture.format,
229
+ usage: GPUTextureUsage.TEXTURE_BINDING | GPUTextureUsage.COPY_DST | GPUTextureUsage.RENDER_ATTACHMENT,
230
+ }),
231
+ sampler: device.createSampler(texture.samplerOptions),
232
+ uploaded: null,
233
+ };
234
+ }
235
+ object.program.pipelineCache.clear();
236
+ object.program = createProgram(backend, material);
237
+ object.device = device;
238
+ }
239
+ if (object.texture && object.texture.device !== shared.renderer.device) {
240
+ object.texture = {
241
+ ...object.texture,
242
+ device: shared.renderer.device,
243
+ texture: shared.renderer.device.createTexture({
244
+ size: [object.texture.image.width, object.texture.image.height],
245
+ format: object.texture.format,
246
+ usage: GPUTextureUsage.TEXTURE_BINDING | GPUTextureUsage.COPY_DST | GPUTextureUsage.RENDER_ATTACHMENT,
247
+ }),
248
+ sampler: shared.renderer.device.createSampler(object.texture.samplerOptions),
249
+ uploaded: null,
250
+ };
251
+ }
252
+ });
253
+ update(shared);
254
+ if ((backend.header && backend._type !== 'pmtiles') || backend._geojson) updateArea(backend);
255
+ }
@@ -0,0 +1,12 @@
1
+
2
+ export function bindSunlight(backend) {
3
+ const sun = backend.renderer.sunlight;
4
+ sun.bindGroup = backend.renderer.device.createBindGroup({
5
+ layout: sun.layout, entries: [
6
+ { binding: 0, resource: { buffer: sun.buffer } },
7
+ { binding: 1, resource: sun.texture.createView() },
8
+ { binding: 2, resource: sun.sampler },
9
+ { binding: 3, resource: { buffer: sun.lights } },
10
+ ]
11
+ });
12
+ }
@@ -0,0 +1 @@
1
+ export const box = (x0, x1, z0, z1) => [[x0, z0], [x1, z0], [x1, z1], [x0, z1]];
@@ -0,0 +1,18 @@
1
+ /** Height inside a bridge deck, or null outside its triangles. */
2
+ export function bridgeElevation(point, bridges) {
3
+ // Bridges bucketed by 64 DEM pixels.
4
+ for (const bridge of bridges?.get(`${Math.floor(point.x / 64)}/${Math.floor(point.y / 64)}`) || []) {
5
+ if (point.x < bridge.min[0] || point.x > bridge.max[0] || point.y < bridge.min[1] || point.y > bridge.max[1]) {
6
+ continue;
7
+ }
8
+ for (const [a, b, c] of bridge.triangles) {
9
+ const d = (b[1] - c[1]) * (a[0] - c[0]) + (c[0] - b[0]) * (a[1] - c[1]);
10
+ const u = ((b[1] - c[1]) * (point.x - c[0]) + (c[0] - b[0]) * (point.y - c[1])) / d;
11
+ const v = ((c[1] - a[1]) * (point.x - c[0]) + (a[0] - c[0]) * (point.y - c[1])) / d;
12
+ if (u >= 0 && v >= 0 && u + v <= 1) {
13
+ return u * a[2] + v * b[2] + (1 - u - v) * c[2];
14
+ }
15
+ }
16
+ }
17
+ return null;
18
+ }
@@ -0,0 +1,20 @@
1
+
2
+ export const build = parts => {
3
+ const position = [], normal = [], color = [];
4
+ const triangle = (a, b, c, tint) => {
5
+ const u = b.map((v, i) => v - a[i]), v = c.map((w, i) => w - a[i]);
6
+ const n = [u[1] * v[2] - u[2] * v[1], u[2] * v[0] - u[0] * v[2], u[0] * v[1] - u[1] * v[0]], length = Math.hypot(...n);
7
+ for (const point of [a, b, c]) { position.push(...point); normal.push(...n.map(w => w / length)); color.push(...tint); }
8
+ };
9
+ for (const [ring, bottom, top, tint, topRing = ring] of parts) {
10
+ const low = ring.map(([x, z]) => [x, bottom, z]), high = topRing.map(([x, z]) => [x, top, z]);
11
+ for (let i = 0; i < ring.length; i++) {
12
+ const j = (i + 1) % ring.length;
13
+ triangle(low[i], low[j], high[j], tint); triangle(low[i], high[j], high[i], tint);
14
+ }
15
+ for (let i = 1; i < ring.length - 1; i++) {
16
+ triangle(high[0], high[i], high[i + 1], tint); triangle(low[0], low[i + 1], low[i], tint);
17
+ }
18
+ }
19
+ return { position, normal, color };
20
+ };
@@ -0,0 +1,287 @@
1
+ function editable(target) {
2
+ return target.closest?.('input,textarea,select,[contenteditable="true"]');
3
+ }
4
+
5
+ function dragCamera(map, element, accept, sensitivity = [0.8, 0.5], orbit = false) {
6
+ let drag, moved = false;
7
+ const end = event => {
8
+ if (!drag) return;
9
+ if (event?.pointerId !== undefined && event.pointerId !== drag.id) return;
10
+ const finished = drag;
11
+ drag = null;
12
+ if (element.hasPointerCapture?.(finished.id)) element.releasePointerCapture(finished.id);
13
+ if (finished.dragging && map.getContainer()._leaflet_id) map.dragging.enable();
14
+ element.style.cursor = finished.cursor;
15
+ };
16
+ const down = event => {
17
+ if (drag || !accept(event) || editable(event.target)) return;
18
+ event.preventDefault();
19
+ event.stopPropagation();
20
+ moved = false;
21
+ const rect = map.getContainer().getBoundingClientRect();
22
+ drag = { id: event.pointerId, x: event.clientX, y: event.clientY, cursor: element.style.cursor,
23
+ cx: rect.left + rect.width / 2, cy: rect.top + rect.height / 2, dragging: map.dragging?.enabled() };
24
+ map.dragging?.disable();
25
+ element.focus?.({ preventScroll: true });
26
+ element.setPointerCapture(event.pointerId);
27
+ element.style.cursor = 'grabbing';
28
+ };
29
+ const move = event => {
30
+ if (!drag || drag.id !== event.pointerId) return;
31
+ const x = event.clientX - drag.x, y = event.clientY - drag.y;
32
+ if (Math.hypot(x, y) < 3 && !moved) return;
33
+ map._stopPitchBearingAnimation();
34
+ moved = true;
35
+ event.preventDefault();
36
+ let bearing = x * sensitivity[0];
37
+ if (orbit) {
38
+ if (Math.abs(drag.cy - drag.y) > 100) {
39
+ const angle = Math.atan2(event.clientY - drag.cy, event.clientX - drag.cx) -
40
+ Math.atan2(event.clientY - drag.cy, drag.x - drag.cx);
41
+ bearing = Math.atan2(Math.sin(angle), Math.cos(angle)) * 180 / Math.PI;
42
+ } else bearing *= event.clientY < drag.cy ? 1 : -1;
43
+ }
44
+ map._setPitchAndBearing(map.getPitch() - y * sensitivity[1], map.getBearing() + bearing);
45
+ drag.x = event.clientX; drag.y = event.clientY;
46
+ };
47
+ const click = event => { if (moved) { event.preventDefault(); event.stopPropagation(); moved = false; } };
48
+ const context = event => {
49
+ if (drag || moved) { event.preventDefault(); event.stopPropagation(); }
50
+ };
51
+ element.addEventListener('pointerdown', down, true);
52
+ element.addEventListener('pointermove', move);
53
+ element.addEventListener('pointerup', end);
54
+ element.addEventListener('pointercancel', end);
55
+ element.addEventListener('lostpointercapture', end);
56
+ element.addEventListener('click', click, true);
57
+ element.addEventListener('contextmenu', context);
58
+ window.addEventListener('blur', end);
59
+ return () => {
60
+ end();
61
+ element.removeEventListener('pointerdown', down, true);
62
+ element.removeEventListener('pointermove', move);
63
+ element.removeEventListener('pointerup', end);
64
+ element.removeEventListener('pointercancel', end);
65
+ element.removeEventListener('lostpointercapture', end);
66
+ element.removeEventListener('click', click, true);
67
+ element.removeEventListener('contextmenu', context);
68
+ window.removeEventListener('blur', end);
69
+ };
70
+ }
71
+
72
+ L.Control.Rotate = L.Control.extend({
73
+ options: { position: 'topleft', closeOnZeroBearing: true, visualizePitch: true },
74
+ onAdd(map) {
75
+ const container = L.DomUtil.create('div', 'leaflet-control-rotate leaflet-bar');
76
+ const button = L.DomUtil.create('a', 'leaflet-control-rotate-toggle', container);
77
+ button.href = '#';
78
+ button.setAttribute('role', 'button');
79
+ button.title = this.options.visualizePitch ? 'Reset bearing and pitch' : 'Reset bearing to north';
80
+ button.setAttribute('aria-label', button.title);
81
+ button.style.cssText = 'cursor:grab;touch-action:none';
82
+ const arrow = L.DomUtil.create('span', 'leaflet-control-rotate-arrow', button);
83
+ arrow.setAttribute('aria-hidden', 'true');
84
+ arrow.style.cssText = 'display:block;width:100%;height:100%;pointer-events:none;background: no-repeat center / 29px 29px';
85
+ arrow.style.backgroundImage = 'url("data:image/svg+xml,%3Csvg xmlns=%27http://www.w3.org/2000/svg%27 viewBox=%270 0 29 29%27%3E%3Cpath fill=%27%23333%27 d=%27M10.5 14 14.5 6 18.5 14Z%27/%3E%3Cpath fill=%27%23ccc%27 d=%27M10.5 16 14.5 24 18.5 16Z%27/%3E%3C/svg%3E")';
86
+ button.onclick = event => {
87
+ event.preventDefault();
88
+ if (this.options.visualizePitch) map.resetNorthPitch();
89
+ else map.resetNorth();
90
+ if (event.detail) map.getContainer().focus({ preventScroll: true });
91
+ };
92
+ button.onkeydown = event => {
93
+ const step = event.shiftKey ? 15 : 5;
94
+ if (event.key === 'ArrowLeft') map.setBearing(map.getBearing() - step);
95
+ else if (event.key === 'ArrowRight') map.setBearing(map.getBearing() + step);
96
+ else if (event.key === 'ArrowUp' && this.options.visualizePitch) map.setPitch(map.getPitch() + step);
97
+ else if (event.key === 'ArrowDown' && this.options.visualizePitch) map.setPitch(map.getPitch() - step);
98
+ else if (event.key === 'Home' || event.key === ' ') button.click();
99
+ else return;
100
+ event.preventDefault();
101
+ event.stopPropagation();
102
+ };
103
+ this._refresh = () => {
104
+ const pitch = this.options.visualizePitch ? map.getPitch() : 0;
105
+ arrow.style.transform = `scale(${1 / Math.sqrt(Math.cos(pitch * Math.PI / 180))}) rotateX(${pitch}deg) rotate(${map.getBearing()}deg)`;
106
+ button.setAttribute('aria-description', `Bearing ${Math.round(map.getBearing())} degrees, pitch ${Math.round(map.getPitch())} degrees. Arrow keys adjust the camera; Home resets it.`);
107
+ // Do not hide a focused control, even when the caller opts into auto-hiding.
108
+ container.style.display = this.options.closeOnZeroBearing && !map.getBearing() && !pitch &&
109
+ !container.contains(document.activeElement) ? 'none' : '';
110
+ };
111
+ map.on('rotate pitch', this._refresh);
112
+ this._refresh();
113
+ this._endDrag = dragCamera(map, button, event => event.button === 0,
114
+ [0.8, this.options.visualizePitch ? 0.5 : 0]);
115
+ button.addEventListener('focusout', this._refresh);
116
+ L.DomEvent.on(button, 'dblclick contextmenu', L.DomEvent.stop);
117
+ L.DomEvent.disableClickPropagation(container);
118
+ L.DomEvent.disableScrollPropagation(container);
119
+ return container;
120
+ },
121
+ onRemove(map) {
122
+ map.off('rotate pitch', this._refresh);
123
+ this._endDrag();
124
+ },
125
+ });
126
+ if (L.control) L.control.rotate = options => new L.Control.Rotate(options);
127
+
128
+ const ShiftRotate = L.Handler.extend({
129
+ addHooks() {
130
+ this._wheel = event => {
131
+ if (!event.shiftKey || editable(event.target) || event.target.closest('.leaflet-control')) return;
132
+ event.preventDefault(); event.stopImmediatePropagation();
133
+ this._map.setBearing(this._map.getBearing() + Math.sign(event.deltaY) * 5);
134
+ };
135
+ this._map.getContainer().addEventListener('wheel', this._wheel, { passive: false, capture: true });
136
+ },
137
+ removeHooks() { this._map.getContainer().removeEventListener('wheel', this._wheel, true); },
138
+ });
139
+ const PitchDrag = L.Handler.extend({
140
+ addHooks() {
141
+ this._end = dragCamera(this._map, this._map.getContainer(), event =>
142
+ !event.target.closest('.leaflet-control,.leaflet-marker-icon,.leaflet-popup') &&
143
+ (event.button === 2 || event.button === 0 && event.ctrlKey), [0.8, 0.5], true);
144
+ },
145
+ removeHooks() {
146
+ this._end();
147
+ },
148
+ });
149
+ const Compass = L.Handler.extend({
150
+ addHooks() {
151
+ const generation = this._generation = (this._generation || 0) + 1;
152
+ this._orientation = event => {
153
+ const bearing = Number.isFinite(event.webkitCompassHeading) ? -event.webkitCompassHeading : event.alpha;
154
+ const orientation = event.absolute ? 0 : (screen.orientation?.angle ?? window.orientation ?? 0);
155
+ if (Number.isFinite(bearing)) this._map.setBearing(bearing - orientation);
156
+ };
157
+ const permission = window.DeviceOrientationEvent?.requestPermission;
158
+ new Promise(resolve => resolve(permission ? permission.call(window.DeviceOrientationEvent) : 'granted')).then(result => {
159
+ if (!this.enabled() || this._generation !== generation) return;
160
+ if (!window.DeviceOrientationEvent || result !== 'granted') throw new Error('Device compass permission unavailable or denied');
161
+ window.addEventListener('deviceorientation', this._orientation);
162
+ window.addEventListener('deviceorientationabsolute', this._orientation);
163
+ }).catch(error => {
164
+ this.disable();
165
+ this._map.fire('error', { error });
166
+ });
167
+ },
168
+ removeHooks() {
169
+ this._generation++;
170
+ window.removeEventListener('deviceorientation', this._orientation);
171
+ window.removeEventListener('deviceorientationabsolute', this._orientation);
172
+ },
173
+ });
174
+
175
+ const ContainerResize = L.Handler.extend({
176
+ addHooks() {
177
+ this._observer = new ResizeObserver(() => this._map.invalidateSize());
178
+ this._observer.observe(this._map.getContainer());
179
+ },
180
+ removeHooks() { this._observer.disconnect(); },
181
+ });
182
+
183
+ export function cameraControls(map) {
184
+ map.addHandler('trackContainerMutation', ContainerResize);
185
+ if (!map.options.rotate) return;
186
+ map.addHandler('shiftKeyRotate', ShiftRotate);
187
+ map.addHandler('pitchDrag', PitchDrag);
188
+ map.addHandler('compassBearing', Compass);
189
+ // Two optional touch modes share one pinch/pan recognizer with Leaflet's zoom setting.
190
+ const TouchMode = L.Handler.extend({
191
+ addHooks() { map.fire('cameratouchchange'); },
192
+ removeHooks() { map.fire('cameratouchchange'); },
193
+ });
194
+ map.addHandler('touchRotate', TouchMode);
195
+ map.addHandler('touchPitch', TouchMode);
196
+ const container = map.getContainer();
197
+ let touch, nativeZoom;
198
+ const start = event => {
199
+ if (event.touches.length !== 2 || event.target.closest('.leaflet-control')) return;
200
+ if (!map.touchRotate.enabled() && !map.touchPitch.enabled()) return;
201
+ const [a, b] = event.touches;
202
+ if (Math.hypot(b.clientX - a.clientX, b.clientY - a.clientY) < 1) return;
203
+ map._stopPitchBearingAnimation();
204
+ const middle = new L.Point((a.clientX + b.clientX) / 2, (a.clientY + b.clientY) / 2)
205
+ .subtract(new L.Point(container.getBoundingClientRect().left, container.getBoundingClientRect().top));
206
+ touch = { angle: Math.atan2(b.clientY - a.clientY, b.clientX - a.clientX),
207
+ distance: Math.hypot(b.clientX - a.clientX, b.clientY - a.clientY), middle,
208
+ anchor: map.containerPointToLatLng(middle), zoom: map.getZoom(),
209
+ bearing: map.getBearing(), pitch: map.getPitch(), zooming: map.touchZoom?.enabled(),
210
+ dragging: map.dragging?.enabled() };
211
+ map.dragging?.disable();
212
+ event.preventDefault(); event.stopImmediatePropagation();
213
+ };
214
+ const move = event => {
215
+ if (!touch || event.touches.length !== 2) return;
216
+ const [a, b] = event.touches;
217
+ const angle = Math.atan2(b.clientY - a.clientY, b.clientX - a.clientX);
218
+ const middle = new L.Point((a.clientX + b.clientX) / 2, (a.clientY + b.clientY) / 2)
219
+ .subtract(new L.Point(container.getBoundingClientRect().left, container.getBoundingClientRect().top));
220
+ const delta = Math.atan2(Math.sin(angle - touch.angle), Math.cos(angle - touch.angle)) * 180 / Math.PI;
221
+ map._setPitchAndBearing(map.touchPitch.enabled() ? touch.pitch - (middle.y - touch.middle.y) * 0.25 : touch.pitch,
222
+ map.touchRotate.enabled() ? touch.bearing + delta : touch.bearing);
223
+ const distance = Math.hypot(b.clientX - a.clientX, b.clientY - a.clientY);
224
+ if (distance < 1) return;
225
+ const zoom = touch.zooming ? Math.max(map.getMinZoom(), Math.min(map.getMaxZoom(),
226
+ touch.zoom + Math.log2(distance / touch.distance))) : touch.zoom;
227
+ map.setView(map.getCenter(), zoom, { animate: false });
228
+ if (map.options.touchZoom !== 'center') {
229
+ const projected = map.project(touch.anchor).subtract(map.project(map.containerPointToLatLng(middle)));
230
+ map.setView(map.unproject(map.project(map.getCenter()).add(projected)), map.getZoom(), { animate: false });
231
+ }
232
+ event.preventDefault(); event.stopImmediatePropagation();
233
+ };
234
+ const end = event => {
235
+ if (!touch) return;
236
+ if (event?.touches?.length >= 2) return;
237
+ if (touch.dragging && container._leaflet_id) map.dragging.enable();
238
+ const snap = touch.snap;
239
+ map.options.zoomSnap = snap;
240
+ if (touch.zooming && snap && container._leaflet_id) {
241
+ map.setZoom(Math.round(map.getZoom() / snap) * snap, { animate: false });
242
+ }
243
+ touch = null;
244
+ };
245
+ const change = () => {
246
+ const active = map.touchRotate.enabled() || map.touchPitch.enabled();
247
+ if (active && nativeZoom === undefined) {
248
+ nativeZoom = map.touchZoom?.enabled();
249
+ map.touchZoom?.disable();
250
+ } else if (!active && nativeZoom !== undefined) {
251
+ end();
252
+ if (nativeZoom && container._leaflet_id) map.touchZoom.enable();
253
+ nativeZoom = undefined;
254
+ }
255
+ };
256
+ // Preserve the zoom-enabled setting before handing pinch recognition over.
257
+ change();
258
+ const startTouch = event => {
259
+ if (touch) return;
260
+ start(event);
261
+ if (touch) {
262
+ if (nativeZoom !== undefined) touch.zooming = nativeZoom;
263
+ touch.snap = map.options.zoomSnap;
264
+ map.options.zoomSnap = 0;
265
+ }
266
+ };
267
+ container.addEventListener('touchstart', startTouch, { capture: true, passive: false });
268
+ container.addEventListener('touchmove', move, { capture: true, passive: false });
269
+ container.addEventListener('touchend', end, true);
270
+ container.addEventListener('touchcancel', end, true);
271
+ window.addEventListener('blur', end);
272
+ map.on('cameratouchchange', change);
273
+ if (map.options.rotateControl) {
274
+ map.rotateControl = new L.Control.Rotate(typeof map.options.rotateControl === 'object' ? map.options.rotateControl : {});
275
+ map.rotateControl.addTo(map);
276
+ }
277
+ map.once('unload', () => {
278
+ end();
279
+ container.removeEventListener('touchstart', startTouch, true);
280
+ container.removeEventListener('touchmove', move, true);
281
+ container.removeEventListener('touchend', end, true);
282
+ container.removeEventListener('touchcancel', end, true);
283
+ window.removeEventListener('blur', end);
284
+ map.off('cameratouchchange', change);
285
+ map.rotateControl?.remove();
286
+ });
287
+ }
@@ -0,0 +1,4 @@
1
+ export const channel = (n, h, l, a) => {
2
+ const k = (n + h * 12) % 12;
3
+ return l - a * Math.max(-1, Math.min(k - 3, 9 - k, 1));
4
+ };
@@ -0,0 +1,62 @@
1
+ import { CELL } from './CELL.js';
2
+ import { colliders } from './colliders.js';
3
+
4
+
5
+ export function collider(mesh) {
6
+ let c = colliders.get(mesh.geometry);
7
+ if (c) return c;
8
+ const m = mesh.worldMatrix, { position: { data: p }, index, normal, uv } = mesh.geometry.attributes;
9
+
10
+ const count = index ? index.count : p.length / 3, tri = [], nrm = [], cells = new Map(), v = [];
11
+
12
+ for (let i = 0; i < count; i += 3) {
13
+ const first = index ? index.data[i] : i;
14
+ // Streetlight halos (uv.x <= 2, see scene/picking.js) are light, not matter.
15
+ if (mesh.program.nightLight && uv && uv.data[first * 2] <= 2) {
16
+ continue;
17
+ }
18
+ for (let k = 0; k < 3; k++) {
19
+ const j = (index ? index.data[i + k] : i + k) * 3, x = p[j], y = p[j + 1], z = p[j + 2];
20
+ v[k * 3] = m[0] * x + m[4] * y + m[8] * z + m[12];
21
+ v[k * 3 + 1] = m[1] * x + m[5] * y + m[9] * z + m[13];
22
+ v[k * 3 + 2] = m[2] * x + m[6] * y + m[10] * z + m[14];
23
+ }
24
+ const ex = v[3] - v[0], ey = v[4] - v[1], ez = v[5] - v[2], fx = v[6] - v[0], fy = v[7] - v[1], fz = v[8] - v[2];
25
+
26
+ let nx = ey * fz - ez * fy, nz = ex * fy - ey * fx;
27
+
28
+ const ny = ez * fx - ex * fz, length = Math.hypot(nx, ny, nz);
29
+
30
+ if (!length || Math.abs(ny) > 0.9 * length) {
31
+ continue;
32
+ }
33
+
34
+ if (normal) {
35
+ // The stored normal tells the outside, whatever the winding.
36
+ const j = first * 3, a = normal.data;
37
+ if ((m[0] * a[j] + m[4] * a[j + 1] + m[8] * a[j + 2]) * nx + (m[2] * a[j] + m[6] * a[j + 1] + m[10] * a[j + 2]) * nz < 0) { nx = -nx; nz = -nz; }
38
+ }
39
+
40
+ const h = Math.hypot(nx, nz), t = nrm.length / 2;
41
+
42
+ tri.push(...v);
43
+ nrm.push(nx / h, nz / h);
44
+
45
+ for (let ix = Math.floor(Math.min(v[0], v[3], v[6]) / CELL); ix <= Math.floor(Math.max(v[0], v[3], v[6]) / CELL); ix++)
46
+ for (let iz = Math.floor(Math.min(v[2], v[5], v[8]) / CELL); iz <= Math.floor(Math.max(v[2], v[5], v[8]) / CELL); iz++) {
47
+ const key = ix + ',' + iz;
48
+ if (!cells.has(key)) {
49
+ cells.set(key, []);
50
+ }
51
+ cells.get(key).push(t);
52
+ }
53
+ }
54
+ c = {
55
+ tri: Float32Array.from(tri),
56
+ nrm: Float32Array.from(nrm),
57
+ cells,
58
+ origin: [m[12], m[13], m[14]]
59
+ };
60
+ colliders.set(mesh.geometry, c);
61
+ return c;
62
+ }
@@ -0,0 +1,2 @@
1
+
2
+ export const colliders = new WeakMap();