leaflet-city-mapper 0.0.1 → 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.
- package/README.md +15 -13
- package/cucumber.mjs +4 -0
- package/dist/leaflet-city-mapper.js +1 -1
- package/dist/leaflet-city-mapper.js.map +4 -4
- package/dist/utils/CELL.js +2 -0
- package/dist/utils/CELL.js.map +7 -0
- package/dist/utils/addBridges.js +2 -0
- package/dist/utils/addBridges.js.map +7 -0
- package/dist/utils/addRasterTile.js +2 -0
- package/dist/utils/addRasterTile.js.map +7 -0
- package/dist/utils/along.js +2 -0
- package/dist/utils/along.js.map +7 -0
- package/dist/utils/atmosphere.js +2 -0
- package/dist/utils/atmosphere.js.map +7 -0
- package/dist/utils/attach.js +2 -0
- package/dist/utils/attach.js.map +7 -0
- package/dist/utils/bindSunlight.js +2 -0
- package/dist/utils/bindSunlight.js.map +7 -0
- package/dist/utils/box.js +2 -0
- package/dist/utils/box.js.map +7 -0
- package/dist/utils/bridgeElevation.js +2 -0
- package/dist/utils/bridgeElevation.js.map +7 -0
- package/dist/utils/build.js +2 -0
- package/dist/utils/build.js.map +7 -0
- package/dist/utils/cameraControls.js +2 -0
- package/dist/utils/cameraControls.js.map +7 -0
- package/dist/utils/channel.js +2 -0
- package/dist/utils/channel.js.map +7 -0
- package/dist/utils/collider.js +2 -0
- package/dist/utils/collider.js.map +7 -0
- package/dist/utils/colliders.js +2 -0
- package/dist/utils/colliders.js.map +7 -0
- package/dist/utils/computeBounds.js +2 -0
- package/dist/utils/computeBounds.js.map +7 -0
- package/dist/utils/conformModelToTerrain.js +2 -0
- package/dist/utils/conformModelToTerrain.js.map +7 -0
- package/dist/utils/createMesh.js +2 -0
- package/dist/utils/createMesh.js.map +7 -0
- package/dist/utils/createProgram.js +3 -0
- package/dist/utils/createProgram.js.map +7 -0
- package/dist/utils/decodeDirectory.js +2 -0
- package/dist/utils/decodeDirectory.js.map +7 -0
- package/dist/utils/decodeHeader.js +2 -0
- package/dist/utils/decodeHeader.js.map +7 -0
- package/dist/utils/decodeModel.js +2 -0
- package/dist/utils/decodeModel.js.map +7 -0
- package/dist/utils/decompress.js +2 -0
- package/dist/utils/decompress.js.map +7 -0
- package/dist/utils/destroyTexture.js +2 -0
- package/dist/utils/destroyTexture.js.map +7 -0
- package/dist/utils/disposeMesh.js +2 -0
- package/dist/utils/disposeMesh.js.map +7 -0
- package/dist/utils/drawGeometry.js +2 -0
- package/dist/utils/drawGeometry.js.map +7 -0
- package/dist/utils/elevationWGSL.js +24 -0
- package/dist/utils/elevationWGSL.js.map +7 -0
- package/dist/utils/emptyNode.js +2 -0
- package/dist/utils/emptyNode.js.map +7 -0
- package/dist/utils/endZoom.js +2 -0
- package/dist/utils/endZoom.js.map +7 -0
- package/dist/utils/findFeature.js +2 -0
- package/dist/utils/findFeature.js.map +7 -0
- package/dist/utils/geometry.js +0 -1
- package/dist/utils/geometry.js.map +4 -4
- package/dist/utils/getElevation.js +2 -0
- package/dist/utils/getElevation.js.map +7 -0
- package/dist/utils/getHeader.js +2 -0
- package/dist/utils/getHeader.js.map +7 -0
- package/dist/utils/getZxy.js +2 -0
- package/dist/utils/getZxy.js.map +7 -0
- package/dist/utils/glass.js +2 -0
- package/dist/utils/glass.js.map +7 -0
- package/dist/utils/headlight.js +2 -0
- package/dist/utils/headlight.js.map +7 -0
- package/dist/utils/hit.js +2 -0
- package/dist/utils/hit.js.map +7 -0
- package/dist/utils/horizontalPosition.js +2 -0
- package/dist/utils/horizontalPosition.js.map +7 -0
- package/dist/utils/horizontalRay.js +2 -0
- package/dist/utils/horizontalRay.js.map +7 -0
- package/dist/utils/hull.js +2 -0
- package/dist/utils/hull.js.map +7 -0
- package/dist/utils/initSunlight.js +2 -0
- package/dist/utils/initSunlight.js.map +7 -0
- package/dist/utils/initTerrain.js +2 -0
- package/dist/utils/initTerrain.js.map +7 -0
- package/dist/utils/initTraffic.js +40 -0
- package/dist/utils/initTraffic.js.map +7 -0
- package/dist/utils/initWalk.js +2 -0
- package/dist/utils/initWalk.js.map +7 -0
- package/dist/utils/intersectMeshes.js +2 -0
- package/dist/utils/intersectMeshes.js.map +7 -0
- package/dist/utils/isClockWise.js +2 -0
- package/dist/utils/isClockWise.js.map +7 -0
- package/dist/utils/lampPostGeometry.js +2 -0
- package/dist/utils/lampPostGeometry.js.map +7 -0
- package/dist/utils/lightGeometry.js +2 -0
- package/dist/utils/lightGeometry.js.map +7 -0
- package/dist/utils/lightGrid.js +2 -0
- package/dist/utils/lightGrid.js.map +7 -0
- package/dist/utils/limbs.js +2 -0
- package/dist/utils/limbs.js.map +7 -0
- package/dist/utils/loadModel.js +2 -0
- package/dist/utils/loadModel.js.map +7 -0
- package/dist/utils/loadSource.js +2 -0
- package/dist/utils/loadSource.js.map +7 -0
- package/dist/utils/loadTerrain.js +2 -0
- package/dist/utils/loadTerrain.js.map +7 -0
- package/dist/utils/mapCamera.js +2 -0
- package/dist/utils/mapCamera.js.map +7 -0
- package/dist/utils/{shaders.js → materialShaders.js} +16 -8
- package/dist/utils/materialShaders.js.map +7 -0
- package/dist/utils/mirror.js +2 -0
- package/dist/utils/mirror.js.map +7 -0
- package/dist/utils/moonPosition.js +2 -0
- package/dist/utils/moonPosition.js.map +7 -0
- package/dist/utils/moves.js +2 -0
- package/dist/utils/moves.js.map +7 -0
- package/dist/utils/nativeEvents.js +2 -0
- package/dist/utils/nativeEvents.js.map +7 -0
- package/dist/utils/nativeGeometry.js +2 -0
- package/dist/utils/nativeGeometry.js.map +7 -0
- package/dist/utils/nativeLayer.js +2 -0
- package/dist/utils/nativeLayer.js.map +7 -0
- package/dist/utils/nodeMatrix.js +2 -0
- package/dist/utils/nodeMatrix.js.map +7 -0
- package/dist/utils/onAdd3DLayer.js +2 -0
- package/dist/utils/onAdd3DLayer.js.map +7 -0
- package/dist/utils/onAddSun.js +2 -0
- package/dist/utils/onAddSun.js.map +7 -0
- package/dist/utils/onClick.js +2 -0
- package/dist/utils/onClick.js.map +7 -0
- package/dist/utils/onMapChange.js +2 -0
- package/dist/utils/onMapChange.js.map +7 -0
- package/dist/utils/onPMTileAdd.js +2 -0
- package/dist/utils/onPMTileAdd.js.map +7 -0
- package/dist/utils/onPMTileRemove.js +2 -0
- package/dist/utils/onPMTileRemove.js.map +7 -0
- package/dist/utils/onPointerLeave.js +2 -0
- package/dist/utils/onPointerLeave.js.map +7 -0
- package/dist/utils/onPointerMove.js +2 -0
- package/dist/utils/onPointerMove.js.map +7 -0
- package/dist/utils/onRemove3DLayer.js +2 -0
- package/dist/utils/onRemove3DLayer.js.map +7 -0
- package/dist/utils/onRemoveSun.js +2 -0
- package/dist/utils/onRemoveSun.js.map +7 -0
- package/dist/utils/onTerrainAdd.js +2 -0
- package/dist/utils/onTerrainAdd.js.map +7 -0
- package/dist/utils/onTerrainChange.js +2 -0
- package/dist/utils/onTerrainChange.js.map +7 -0
- package/dist/utils/onTerrainRemove.js +2 -0
- package/dist/utils/onTerrainRemove.js.map +7 -0
- package/dist/utils/onZoomAnim.js +2 -0
- package/dist/utils/onZoomAnim.js.map +7 -0
- package/dist/utils/paints.js +2 -0
- package/dist/utils/paints.js.map +7 -0
- package/dist/utils/parseColor.js +2 -0
- package/dist/utils/parseColor.js.map +7 -0
- package/dist/utils/parts.js +2 -0
- package/dist/utils/parts.js.map +7 -0
- package/dist/utils/projectCelestialBody.js +2 -0
- package/dist/utils/projectCelestialBody.js.map +7 -0
- package/dist/utils/pump.js +2 -0
- package/dist/utils/pump.js.map +7 -0
- package/dist/utils/rasterTiles.js +2 -0
- package/dist/utils/rasterTiles.js.map +7 -0
- package/dist/utils/raycast.js +2 -0
- package/dist/utils/raycast.js.map +7 -0
- package/dist/utils/read.js +2 -0
- package/dist/utils/read.js.map +7 -0
- package/dist/utils/readAccessor.js +2 -0
- package/dist/utils/readAccessor.js.map +7 -0
- package/dist/utils/releaseArea.js +2 -0
- package/dist/utils/releaseArea.js.map +7 -0
- package/dist/utils/removeRasterTile.js +2 -0
- package/dist/utils/removeRasterTile.js.map +7 -0
- package/dist/utils/render.js +15 -0
- package/dist/utils/render.js.map +7 -0
- package/dist/utils/renderSunlight.js +13 -0
- package/dist/utils/renderSunlight.js.map +7 -0
- package/dist/utils/renderers.js +2 -0
- package/dist/utils/renderers.js.map +7 -0
- package/dist/utils/resizeSunlight.js +2 -0
- package/dist/utils/resizeSunlight.js.map +7 -0
- package/dist/utils/roofCoverage.js +2 -0
- package/dist/utils/roofCoverage.js.map +7 -0
- package/dist/utils/routeGeometry.js +2 -0
- package/dist/utils/routeGeometry.js.map +7 -0
- package/dist/utils/scheduledTrips.js +2 -0
- package/dist/utils/scheduledTrips.js.map +7 -0
- package/dist/utils/setGeoJSONData.js +2 -0
- package/dist/utils/setGeoJSONData.js.map +7 -0
- package/dist/utils/setHighlight.js +2 -0
- package/dist/utils/setHighlight.js.map +7 -0
- package/dist/utils/setPMTileOpacity.js +2 -0
- package/dist/utils/setPMTileOpacity.js.map +7 -0
- package/dist/utils/setPMTileZIndex.js +2 -0
- package/dist/utils/setPMTileZIndex.js.map +7 -0
- package/dist/utils/setParent.js +2 -0
- package/dist/utils/setParent.js.map +7 -0
- package/dist/utils/setTerrainHeight.js +2 -0
- package/dist/utils/setTerrainHeight.js.map +7 -0
- package/dist/utils/setVertexColor.js +2 -0
- package/dist/utils/setVertexColor.js.map +7 -0
- package/dist/utils/showFeature.js +2 -0
- package/dist/utils/showFeature.js.map +7 -0
- package/dist/utils/sizes.js +2 -0
- package/dist/utils/sizes.js.map +7 -0
- package/dist/utils/skin.js +2 -0
- package/dist/utils/skin.js.map +7 -0
- package/dist/utils/solarPosition.js +2 -0
- package/dist/utils/solarPosition.js.map +7 -0
- package/dist/utils/sortMeshes.js +2 -0
- package/dist/utils/sortMeshes.js.map +7 -0
- package/dist/utils/speed.js +2 -0
- package/dist/utils/speed.js.map +7 -0
- package/dist/utils/startZoom.js +2 -0
- package/dist/utils/startZoom.js.map +7 -0
- package/dist/utils/taillight.js +2 -0
- package/dist/utils/taillight.js.map +7 -0
- package/dist/utils/terrainAtlas.js +2 -0
- package/dist/utils/terrainAtlas.js.map +7 -0
- package/dist/utils/terrainElevation.js +2 -0
- package/dist/utils/terrainElevation.js.map +7 -0
- package/dist/utils/terrainGrid.js +2 -0
- package/dist/utils/terrainGrid.js.map +7 -0
- package/dist/utils/textureSampler.js +2 -0
- package/dist/utils/textureSampler.js.map +7 -0
- package/dist/utils/tileId.js +2 -0
- package/dist/utils/tileId.js.map +7 -0
- package/dist/utils/toLanes.js +2 -0
- package/dist/utils/toLanes.js.map +7 -0
- package/dist/utils/toSRGB.js +2 -0
- package/dist/utils/toSRGB.js.map +7 -0
- package/dist/utils/tooltip.js +3 -0
- package/dist/utils/tooltip.js.map +7 -0
- package/dist/utils/traffic.js +1 -1
- package/dist/utils/traffic.js.map +3 -3
- package/dist/utils/traverse.js +2 -0
- package/dist/utils/traverse.js.map +7 -0
- package/dist/utils/treeGeometry.js +2 -0
- package/dist/utils/treeGeometry.js.map +7 -0
- package/dist/utils/triangulate.js +2 -0
- package/dist/utils/triangulate.js.map +7 -0
- package/dist/utils/tripsAt.js +2 -0
- package/dist/utils/tripsAt.js.map +7 -0
- package/dist/utils/types.js +2 -0
- package/dist/utils/types.js.map +7 -0
- package/dist/utils/underEntry.js +2 -0
- package/dist/utils/underEntry.js.map +7 -0
- package/dist/utils/update.js +2 -0
- package/dist/utils/update.js.map +7 -0
- package/dist/utils/updateArea.js +2 -0
- package/dist/utils/updateArea.js.map +7 -0
- package/dist/utils/updateCamera.js +2 -0
- package/dist/utils/updateCamera.js.map +7 -0
- package/dist/utils/updatePointLights.js +2 -0
- package/dist/utils/updatePointLights.js.map +7 -0
- package/dist/utils/updateWorldMatrix.js +2 -0
- package/dist/utils/updateWorldMatrix.js.map +7 -0
- package/dist/utils/vehicleOffsets.js +2 -0
- package/dist/utils/vehicleOffsets.js.map +7 -0
- package/dist/utils/vehicles.js +1 -1
- package/dist/utils/vehicles.js.map +3 -3
- package/dist/utils/vertexNormals.js +2 -0
- package/dist/utils/vertexNormals.js.map +7 -0
- package/dist/utils/walk.js +1 -1
- package/dist/utils/walk.js.map +3 -3
- package/dist/utils/white.js +2 -0
- package/dist/utils/white.js.map +7 -0
- package/dist/utils/worldLights.js +2 -0
- package/dist/utils/worldLights.js.map +7 -0
- package/dist/utils/wrap.js +2 -0
- package/dist/utils/wrap.js.map +7 -0
- package/package.json +2 -4
- package/src/index.js +365 -50
- package/src/utils/CELL.js +4 -0
- package/src/utils/addBridges.js +66 -0
- package/src/utils/addRasterTile.js +112 -0
- package/src/utils/along.js +9 -0
- package/src/utils/atmosphere.js +20 -0
- package/src/utils/attach.js +255 -0
- package/src/utils/bindSunlight.js +12 -0
- package/src/utils/box.js +1 -0
- package/src/utils/bridgeElevation.js +18 -0
- package/src/utils/build.js +20 -0
- package/src/utils/cameraControls.js +287 -0
- package/src/utils/channel.js +4 -0
- package/src/utils/collider.js +62 -0
- package/src/utils/colliders.js +2 -0
- package/src/utils/computeBounds.js +17 -0
- package/src/utils/conformModelToTerrain.js +69 -0
- package/src/utils/createMesh.js +31 -0
- package/src/utils/createProgram.js +92 -0
- package/src/utils/decodeDirectory.js +40 -0
- package/src/utils/decodeHeader.js +15 -0
- package/src/utils/decodeModel.js +43 -0
- package/src/utils/decompress.js +5 -0
- package/src/utils/destroyTexture.js +7 -0
- package/src/utils/disposeMesh.js +13 -0
- package/src/utils/drawGeometry.js +122 -0
- package/src/utils/elevationWGSL.js +27 -0
- package/src/utils/emptyNode.js +9 -0
- package/src/utils/endZoom.js +15 -0
- package/src/utils/findFeature.js +49 -0
- package/src/utils/geometry.js +0 -254
- package/src/utils/getElevation.js +10 -0
- package/src/utils/getHeader.js +9 -0
- package/src/utils/getZxy.js +76 -0
- package/src/utils/glass.js +1 -0
- package/src/utils/headlight.js +1 -0
- package/src/utils/hit.js +41 -0
- package/src/utils/horizontalPosition.js +16 -0
- package/src/utils/horizontalRay.js +12 -0
- package/src/utils/hull.js +1 -0
- package/src/utils/initSunlight.js +9 -0
- package/src/utils/initTerrain.js +19 -0
- package/src/utils/initTraffic.js +93 -0
- package/src/utils/initWalk.js +72 -0
- package/src/utils/{picking.js → intersectMeshes.js} +121 -119
- package/src/utils/isClockWise.js +12 -0
- package/src/utils/lampPostGeometry.js +26 -0
- package/src/utils/lightGeometry.js +22 -0
- package/src/utils/{lighting.js → lightGrid.js} +50 -51
- package/src/utils/limbs.js +9 -0
- package/src/{scene/model.js → utils/loadModel.js} +37 -24
- package/src/utils/loadSource.js +17 -0
- package/src/utils/loadTerrain.js +65 -0
- package/src/utils/mapCamera.js +432 -0
- package/src/utils/{shaders.js → materialShaders.js} +243 -237
- package/src/utils/mirror.js +6 -0
- package/src/utils/moonPosition.js +17 -0
- package/src/utils/moves.js +11 -0
- package/src/utils/nativeEvents.js +77 -0
- package/src/utils/nativeGeometry.js +131 -0
- package/src/utils/nativeLayer.js +317 -0
- package/src/utils/nodeMatrix.js +22 -0
- package/src/utils/onAdd3DLayer.js +31 -0
- package/src/utils/onAddSun.js +147 -0
- package/src/utils/onClick.js +13 -0
- package/src/utils/onMapChange.js +16 -0
- package/src/utils/onPMTileAdd.js +15 -0
- package/src/utils/onPMTileRemove.js +8 -0
- package/src/utils/onPointerLeave.js +11 -0
- package/src/utils/onPointerMove.js +15 -0
- package/src/utils/onRemove3DLayer.js +78 -0
- package/src/utils/onRemoveSun.js +3 -0
- package/src/utils/onTerrainAdd.js +18 -0
- package/src/utils/onTerrainChange.js +13 -0
- package/src/utils/onTerrainRemove.js +40 -0
- package/src/utils/onZoomAnim.js +14 -0
- package/src/utils/paints.js +10 -0
- package/src/utils/parseColor.js +42 -0
- package/src/utils/parts.js +16 -0
- package/src/utils/projectCelestialBody.js +13 -0
- package/src/{scene/sources.js → utils/pump.js} +59 -98
- package/src/utils/rasterTiles.js +285 -0
- package/src/utils/raycast.js +60 -0
- package/src/utils/read.js +29 -0
- package/src/utils/readAccessor.js +68 -0
- package/src/utils/releaseArea.js +34 -0
- package/src/utils/removeRasterTile.js +13 -0
- package/src/{scene → utils}/render.js +112 -173
- package/src/utils/renderSunlight.js +170 -0
- package/src/utils/renderers.js +2 -0
- package/src/utils/resizeSunlight.js +15 -0
- package/src/utils/roofCoverage.js +47 -0
- package/src/utils/routeGeometry.js +22 -0
- package/src/utils/scheduledTrips.js +29 -0
- package/src/utils/setGeoJSONData.js +11 -0
- package/src/utils/setHighlight.js +13 -0
- package/src/utils/setPMTileOpacity.js +7 -0
- package/src/utils/setPMTileZIndex.js +7 -0
- package/src/utils/setParent.js +7 -0
- package/src/utils/setTerrainHeight.js +10 -0
- package/src/utils/setVertexColor.js +14 -0
- package/src/utils/showFeature.js +27 -0
- package/src/utils/sizes.js +1 -0
- package/src/utils/skin.js +2 -0
- package/src/utils/solarPosition.js +17 -0
- package/src/utils/sortMeshes.js +12 -0
- package/src/utils/speed.js +3 -0
- package/src/utils/startZoom.js +5 -0
- package/src/utils/taillight.js +1 -0
- package/src/utils/terrainAtlas.js +21 -0
- package/src/utils/terrainElevation.js +20 -0
- package/src/utils/terrainGrid.js +23 -0
- package/src/utils/textureSampler.js +10 -0
- package/src/utils/tileId.js +18 -0
- package/src/utils/toLanes.js +7 -0
- package/src/utils/toSRGB.js +1 -0
- package/src/utils/tooltip.js +6 -0
- package/src/utils/traffic.js +310 -85
- package/src/utils/traverse.js +6 -0
- package/src/utils/treeGeometry.js +45 -0
- package/src/utils/triangulate.js +100 -0
- package/src/utils/tripsAt.js +16 -0
- package/src/utils/types.js +8 -0
- package/src/utils/{control.js → underEntry.js} +1 -2
- package/src/utils/update.js +18 -0
- package/src/utils/updateArea.js +40 -0
- package/src/utils/updateCamera.js +32 -0
- package/src/utils/updatePointLights.js +38 -0
- package/src/utils/updateWorldMatrix.js +47 -0
- package/src/utils/vehicleOffsets.js +8 -0
- package/src/utils/vehicles.js +65 -69
- package/src/utils/vertexNormals.js +30 -0
- package/src/utils/walk.js +181 -66
- package/src/utils/white.js +1 -0
- package/src/utils/worldLights.js +25 -0
- package/src/utils/wrap.js +1 -0
- package/dist/scene/gpu.js +0 -3
- package/dist/scene/gpu.js.map +0 -7
- package/dist/scene/lighting.js +0 -9
- package/dist/scene/lighting.js.map +0 -7
- package/dist/scene/model.js +0 -2
- package/dist/scene/model.js.map +0 -7
- package/dist/scene/picking.js +0 -2
- package/dist/scene/picking.js.map +0 -7
- package/dist/scene/pmtiles.js +0 -2
- package/dist/scene/pmtiles.js.map +0 -7
- package/dist/scene/render.js +0 -15
- package/dist/scene/render.js.map +0 -7
- package/dist/scene/sources.js +0 -2
- package/dist/scene/sources.js.map +0 -7
- package/dist/types/3d.js +0 -2
- package/dist/types/3d.js.map +0 -7
- package/dist/types/geojson.js +0 -2
- package/dist/types/geojson.js.map +0 -7
- package/dist/types/pmtiles.js +0 -2
- package/dist/types/pmtiles.js.map +0 -7
- package/dist/types/sunlight.js +0 -2
- package/dist/types/sunlight.js.map +0 -7
- package/dist/types/terrain.js +0 -24
- package/dist/types/terrain.js.map +0 -7
- package/dist/types/traffic.js +0 -41
- package/dist/types/traffic.js.map +0 -7
- package/dist/types/walk.js +0 -2
- package/dist/types/walk.js.map +0 -7
- package/dist/utils/control.js +0 -2
- package/dist/utils/control.js.map +0 -7
- package/dist/utils/lighting.js +0 -2
- package/dist/utils/lighting.js.map +0 -7
- package/dist/utils/model.js +0 -2
- package/dist/utils/model.js.map +0 -7
- package/dist/utils/picking.js +0 -2
- package/dist/utils/picking.js.map +0 -7
- package/dist/utils/pmtiles.js +0 -2
- package/dist/utils/pmtiles.js.map +0 -7
- package/dist/utils/scene.js +0 -2
- package/dist/utils/scene.js.map +0 -7
- package/dist/utils/shaders.js.map +0 -7
- package/dist/utils/sun.js +0 -2
- package/dist/utils/sun.js.map +0 -7
- package/dist/utils/terrain.js +0 -2
- package/dist/utils/terrain.js.map +0 -7
- package/src/scene/gpu.js +0 -438
- package/src/scene/lighting.js +0 -193
- package/src/scene/picking.js +0 -150
- package/src/scene/pmtiles.js +0 -94
- package/src/types/3d.js +0 -337
- package/src/types/geojson.js +0 -42
- package/src/types/pmtiles.js +0 -143
- package/src/types/sunlight.js +0 -142
- package/src/types/terrain.js +0 -331
- package/src/types/traffic.js +0 -356
- package/src/types/walk.js +0 -219
- package/src/utils/model.js +0 -178
- package/src/utils/pmtiles.js +0 -79
- package/src/utils/scene.js +0 -102
- package/src/utils/sun.js +0 -71
- package/src/utils/terrain.js +0 -78
|
@@ -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
|
+
}
|
package/src/utils/box.js
ADDED
|
@@ -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,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
|
+
}
|