@tangram.gl/tangram-renderer 0.0.0-stage → 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +60 -2
- package/dist/core.js +36923 -0
- package/dist/core.js.map +6 -0
- package/dist/index.js +32 -0
- package/dist/loaders-gl-worker.js +82 -0
- package/dist/loaders-gl-worker.js.map +6 -0
- package/dist/map-logic.js +2 -0
- package/dist/map-logic.js.map +6 -0
- package/dist/projected-basemaps-worker.js +3 -0
- package/dist/projected-basemaps-worker.js.map +6 -0
- package/dist/shader-modules.js +141 -0
- package/dist/shader-modules.js.map +6 -0
- package/dist/style-schema.js +126 -0
- package/dist/style-schema.js.map +6 -0
- package/dist/tangram-style.schema.json +882 -0
- package/dist/tangram.debug.mjs +37139 -0
- package/dist/tangram.debug.mjs.map +6 -0
- package/dist/tangram.min.mjs +4950 -0
- package/dist/types/builders/earcut.d.ts +15 -0
- package/dist/types/core.d.ts +29 -0
- package/dist/types/gl/constants.d.ts +2 -0
- package/dist/types/gl/globe_mesh.d.ts +48 -0
- package/dist/types/gl/glsl.d.ts +17 -0
- package/dist/types/gl/vertex-constants.d.ts +4 -0
- package/dist/types/gl/vertex-types.d.ts +34 -0
- package/dist/types/gl/vertex_data.d.ts +32 -0
- package/dist/types/gl/vertex_elements.d.ts +8 -0
- package/dist/types/gl/vertex_layout.d.ts +32 -0
- package/dist/types/glsl.d.ts +14 -0
- package/dist/types/gpu/luma_device_renderer.d.ts +66 -0
- package/dist/types/gpu/resource_lifecycle.d.ts +4 -0
- package/dist/types/gpu/tangram_gpu_backend.d.ts +151 -0
- package/dist/types/index.d.ts +155 -0
- package/dist/types/lights/light-definitions.d.ts +121 -0
- package/dist/types/map-logic/draped-terrain-bounds.d.ts +41 -0
- package/dist/types/map-logic/globe-terrain-preparation.d.ts +85 -0
- package/dist/types/map-logic/globe-terrain.d.ts +54 -0
- package/dist/types/map-logic/index.d.ts +33 -0
- package/dist/types/map-logic/label-identity.d.ts +22 -0
- package/dist/types/map-logic/label-placement.d.ts +32 -0
- package/dist/types/map-logic/screen-bounds.d.ts +15 -0
- package/dist/types/map-logic/terrain-data.d.ts +55 -0
- package/dist/types/map-logic/terrain-error.d.ts +51 -0
- package/dist/types/map-logic/terrain-ground-topology.d.ts +26 -0
- package/dist/types/map-logic/terrain-height-field.d.ts +59 -0
- package/dist/types/map-logic/terrain-navigation.d.ts +27 -0
- package/dist/types/map-logic/terrain-neighborhood.d.ts +5 -0
- package/dist/types/map-logic/terrain-query.d.ts +33 -0
- package/dist/types/map-logic/terrain-resource-budget.d.ts +51 -0
- package/dist/types/map-logic/terrain-transition-topology.d.ts +23 -0
- package/dist/types/map-logic/tile-build-queue.d.ts +51 -0
- package/dist/types/map-logic/tile-cache-policy.d.ts +46 -0
- package/dist/types/map-logic/tile-residency.d.ts +17 -0
- package/dist/types/map-logic/triangle-query-index.d.ts +87 -0
- package/dist/types/procedures/js-yaml.d.ts +15 -0
- package/dist/types/procedures/mesh-projector.d.ts +107 -0
- package/dist/types/procedures/mvt-dependencies.d.ts +20 -0
- package/dist/types/procedures/projected-coordinate-transform.d.ts +27 -0
- package/dist/types/procedures/projection-batch-executor.d.ts +53 -0
- package/dist/types/procedures/scene-yaml-legacy.d.ts +6 -0
- package/dist/types/procedures/terrain-draping.d.ts +165 -0
- package/dist/types/procedures/terrain-globe.d.ts +6 -0
- package/dist/types/procedures/terrain-styled-topology.d.ts +34 -0
- package/dist/types/procedures/web-mercator-legacy.d.ts +4 -0
- package/dist/types/procedures/web-mercator-math.d.ts +4 -0
- package/dist/types/scene/globals.d.ts +11 -0
- package/dist/types/scene/ground_footprint.d.ts +16 -0
- package/dist/types/scene/host_frame.d.ts +54 -0
- package/dist/types/scene/projection_math.d.ts +52 -0
- package/dist/types/scene/scene-resource-types.d.ts +177 -0
- package/dist/types/scene/scene_bundle.d.ts +69 -0
- package/dist/types/scene/visibility_adapter.d.ts +106 -0
- package/dist/types/selection/draped-terrain-surface.d.ts +78 -0
- package/dist/types/selection/terrain_surface.d.ts +149 -0
- package/dist/types/shader-modules/globe-horizon.d.ts +15 -0
- package/dist/types/shader-modules/height-decode.d.ts +13 -0
- package/dist/types/shader-modules/hillshade.d.ts +13 -0
- package/dist/types/shader-modules/index.d.ts +6 -0
- package/dist/types/shader-modules/planar.d.ts +12 -0
- package/dist/types/shader-modules/sphere-map.d.ts +13 -0
- package/dist/types/shader-modules/triplanar.d.ts +15 -0
- package/dist/types/sources/decoded_tile_store.d.ts +95 -0
- package/dist/types/sources/tile_source_metadata.d.ts +32 -0
- package/dist/types/styles/text/fontfaceobserver.d.ts +10 -0
- package/dist/types/tile/tile_id.d.ts +37 -0
- package/dist/types/tile/tile_traversal_adapter.d.ts +63 -0
- package/dist/types/types.d.ts +293 -0
- package/dist/types/utils/geo.d.ts +57 -0
- package/dist/types/utils/hash.d.ts +1 -0
- package/dist/types/utils/log.d.ts +27 -0
- package/dist/types/utils/props.d.ts +6 -0
- package/dist/types/utils/subscribe.d.ts +10 -0
- package/dist/types/utils/thread.d.ts +6 -0
- package/dist/types/utils/urls.d.ts +26 -0
- package/dist/types/utils/utils.d.ts +26 -0
- package/dist/types/utils/version.d.ts +2 -0
- package/dist/types/utils/worker-types.d.ts +68 -0
- package/dist/types/utils/worker_broker.d.ts +3 -0
- package/package.json +103 -3
- package/src/builders/common.ts +41 -0
- package/src/builders/earcut.d.ts +15 -0
- package/src/builders/geometry-types.ts +60 -0
- package/src/builders/points.ts +115 -0
- package/src/builders/polygons.ts +225 -0
- package/src/builders/polylines.ts +682 -0
- package/src/builders/wireframe.ts +31 -0
- package/src/core.d.ts +29 -0
- package/src/core.ts +33 -0
- package/src/experimental/loaders-gl-worker.ts +19 -0
- package/src/experimental/projected-basemaps-worker.ts +13 -0
- package/src/experimental/projected-mesh-cache.ts +137 -0
- package/src/experimental/projected-mesh-domain.ts +110 -0
- package/src/experimental/projected-mesh-refinement.ts +193 -0
- package/src/experimental/projected-mesh.ts +399 -0
- package/src/experimental/projected-normal.ts +26 -0
- package/src/experimental/projected-symbol-mesh.ts +74 -0
- package/src/gl/constants.ts +18 -0
- package/src/gl/context.ts +103 -0
- package/src/gl/extensions.ts +30 -0
- package/src/gl/globe_mesh.ts +285 -0
- package/src/gl/glsl.ts +257 -0
- package/src/gl/mesh-types.ts +56 -0
- package/src/gl/render_state.ts +111 -0
- package/src/gl/shader_program.ts +1310 -0
- package/src/gl/texture.ts +814 -0
- package/src/gl/uniform_buffer.ts +340 -0
- package/src/gl/vao.ts +154 -0
- package/src/gl/vbo_mesh.ts +349 -0
- package/src/gl/vertex-constants.ts +19 -0
- package/src/gl/vertex-types.ts +35 -0
- package/src/gl/vertex_data.ts +126 -0
- package/src/gl/vertex_elements.ts +54 -0
- package/src/gl/vertex_layout.ts +241 -0
- package/src/glsl.d.ts +14 -0
- package/src/gpu/luma_device_renderer.ts +579 -0
- package/src/gpu/pixel_readback.ts +51 -0
- package/src/gpu/resource_lifecycle.ts +32 -0
- package/src/gpu/tangram_gpu_backend.ts +195 -0
- package/src/index.d.ts +155 -0
- package/src/index.ts +105 -0
- package/src/labels/collision-types.ts +94 -0
- package/src/labels/collision.ts +273 -0
- package/src/labels/collision_grid.ts +61 -0
- package/src/labels/intersect.ts +30 -0
- package/src/labels/label-types.ts +91 -0
- package/src/labels/label.ts +207 -0
- package/src/labels/label_line.ts +801 -0
- package/src/labels/label_point.ts +131 -0
- package/src/labels/main-pass-types.ts +73 -0
- package/src/labels/main_pass.ts +221 -0
- package/src/labels/point_anchor.ts +100 -0
- package/src/labels/point_placement.ts +150 -0
- package/src/labels/projected-pass.ts +259 -0
- package/src/labels/repeat_group.ts +72 -0
- package/src/lights/ambient_light.glsl +18 -0
- package/src/lights/directional_light.glsl +39 -0
- package/src/lights/geographic-lights.ts +21 -0
- package/src/lights/light-definitions.ts +285 -0
- package/src/lights/light.ts +590 -0
- package/src/lights/lighting-uniforms.ts +80 -0
- package/src/lights/lighting-wgsl.ts +116 -0
- package/src/lights/material.glsl +237 -0
- package/src/lights/material.ts +145 -0
- package/src/lights/native-falloff.ts +37 -0
- package/src/lights/point_light.glsl +96 -0
- package/src/lights/spot_light.glsl +111 -0
- package/src/map-logic/draped-terrain-bounds.ts +71 -0
- package/src/map-logic/globe-terrain-preparation.ts +217 -0
- package/src/map-logic/globe-terrain.ts +296 -0
- package/src/map-logic/index.ts +37 -0
- package/src/map-logic/label-identity.ts +36 -0
- package/src/map-logic/label-placement.ts +117 -0
- package/src/map-logic/screen-bounds.ts +34 -0
- package/src/map-logic/terrain-data.ts +107 -0
- package/src/map-logic/terrain-error.ts +134 -0
- package/src/map-logic/terrain-ground-topology.ts +164 -0
- package/src/map-logic/terrain-height-field.ts +254 -0
- package/src/map-logic/terrain-navigation.ts +50 -0
- package/src/map-logic/terrain-neighborhood.ts +43 -0
- package/src/map-logic/terrain-query.ts +51 -0
- package/src/map-logic/terrain-resource-budget.ts +124 -0
- package/src/map-logic/terrain-transition-topology.ts +164 -0
- package/src/map-logic/tile-build-queue.ts +110 -0
- package/src/map-logic/tile-cache-policy.ts +89 -0
- package/src/map-logic/tile-residency.ts +50 -0
- package/src/map-logic/triangle-query-index.ts +265 -0
- package/src/procedures/js-yaml.d.ts +15 -0
- package/src/procedures/mesh-projector.ts +191 -0
- package/src/procedures/mlt-loaders.ts +55 -0
- package/src/procedures/mvt-dependencies.d.ts +20 -0
- package/src/procedures/mvt-legacy.ts +150 -0
- package/src/procedures/mvt-loaders.ts +53 -0
- package/src/procedures/mvt-parser.ts +48 -0
- package/src/procedures/mvt-properties.ts +36 -0
- package/src/procedures/mvt-tile-provider.ts +72 -0
- package/src/procedures/pmtiles-loader.ts +75 -0
- package/src/procedures/projected-coordinate-transform.ts +79 -0
- package/src/procedures/projection-batch-executor.ts +140 -0
- package/src/procedures/scene-yaml-legacy.ts +16 -0
- package/src/procedures/scene-yaml-loaders.ts +10 -0
- package/src/procedures/terrain-draping.ts +713 -0
- package/src/procedures/terrain-globe.ts +32 -0
- package/src/procedures/terrain-styled-topology.ts +184 -0
- package/src/procedures/web-mercator-legacy.ts +24 -0
- package/src/procedures/web-mercator-math.ts +50 -0
- package/src/scene/camera.ts +298 -0
- package/src/scene/camera_base.ts +116 -0
- package/src/scene/classic_scene.ts +20 -0
- package/src/scene/external_camera.ts +101 -0
- package/src/scene/globals.ts +120 -0
- package/src/scene/ground_footprint.ts +139 -0
- package/src/scene/host_frame.ts +326 -0
- package/src/scene/projected_tile_lod.ts +125 -0
- package/src/scene/projection_math.ts +142 -0
- package/src/scene/projection_shaders.ts +183 -0
- package/src/scene/renderer.ts +516 -0
- package/src/scene/scene-resource-types.ts +174 -0
- package/src/scene/scene.ts +2378 -0
- package/src/scene/scene_bundle.ts +304 -0
- package/src/scene/scene_debug.ts +129 -0
- package/src/scene/scene_loader.ts +402 -0
- package/src/scene/scene_worker.ts +528 -0
- package/src/scene/view.ts +683 -0
- package/src/scene/visibility_adapter.ts +319 -0
- package/src/scene/worker_url.ts +15 -0
- package/src/selection/draped-terrain-surface.ts +377 -0
- package/src/selection/selection-types.ts +79 -0
- package/src/selection/selection.ts +542 -0
- package/src/selection/selection_fragment.glsl +18 -0
- package/src/selection/selection_globals.glsl +9 -0
- package/src/selection/selection_pixels.ts +59 -0
- package/src/selection/selection_vertex.glsl +15 -0
- package/src/selection/terrain_surface.ts +298 -0
- package/src/shader-modules/globe-horizon.ts +52 -0
- package/src/shader-modules/height-decode.ts +41 -0
- package/src/shader-modules/hillshade.ts +70 -0
- package/src/shader-modules/index.ts +10 -0
- package/src/shader-modules/planar.ts +24 -0
- package/src/shader-modules/sphere-map.ts +38 -0
- package/src/shader-modules/triplanar.ts +45 -0
- package/src/sources/aligned_tile_source.ts +122 -0
- package/src/sources/data_source.ts +655 -0
- package/src/sources/decoded_tile_store.ts +245 -0
- package/src/sources/geojson.ts +300 -0
- package/src/sources/mvt.ts +247 -0
- package/src/sources/raster.ts +341 -0
- package/src/sources/shared_tile_source_adapter.ts +117 -0
- package/src/sources/sources.ts +10 -0
- package/src/sources/tile_source_adapter.ts +84 -0
- package/src/sources/tile_source_metadata.ts +65 -0
- package/src/sources/tile_source_state.ts +112 -0
- package/src/sources/topojson.ts +90 -0
- package/src/styles/feature_annotations.ts +16 -0
- package/src/styles/filter.ts +190 -0
- package/src/styles/geometry-style-types.ts +209 -0
- package/src/styles/globe_visibility_wgsl.ts +22 -0
- package/src/styles/layer-types.ts +45 -0
- package/src/styles/layer.ts +586 -0
- package/src/styles/lines/dasharray.ts +49 -0
- package/src/styles/lines/lines.ts +707 -0
- package/src/styles/lines/lines_wgsl.ts +224 -0
- package/src/styles/points/point-runtime-types.ts +125 -0
- package/src/styles/points/points.ts +1079 -0
- package/src/styles/points/points_fragment.glsl +126 -0
- package/src/styles/points/points_vertex.glsl +230 -0
- package/src/styles/points/points_wgsl.ts +189 -0
- package/src/styles/polygons/polygons.ts +268 -0
- package/src/styles/polygons/polygons_fragment.glsl +173 -0
- package/src/styles/polygons/polygons_vertex.glsl +213 -0
- package/src/styles/polygons/polygons_wgsl.ts +127 -0
- package/src/styles/property-types.ts +125 -0
- package/src/styles/raster/raster.ts +33 -0
- package/src/styles/raster/raster_globals.glsl +46 -0
- package/src/styles/style-mixing-types.ts +101 -0
- package/src/styles/style-runtime-types.ts +220 -0
- package/src/styles/style-schema.ts +133 -0
- package/src/styles/style.ts +1033 -0
- package/src/styles/style_globals.glsl +69 -0
- package/src/styles/style_manager.ts +464 -0
- package/src/styles/style_parser.ts +620 -0
- package/src/styles/text/font_manager.ts +180 -0
- package/src/styles/text/fontfaceobserver.d.ts +10 -0
- package/src/styles/text/text-types.ts +220 -0
- package/src/styles/text/text.ts +378 -0
- package/src/styles/text/text_canvas.ts +773 -0
- package/src/styles/text/text_labels.ts +436 -0
- package/src/styles/text/text_segments.ts +142 -0
- package/src/styles/text/text_settings.ts +228 -0
- package/src/styles/text/text_wgsl.ts +159 -0
- package/src/styles/text/text_wrap.ts +194 -0
- package/src/tile/globe_tile_preload.ts +40 -0
- package/src/tile/tangram_tileset_2d.ts +108 -0
- package/src/tile/tile-build-types.ts +155 -0
- package/src/tile/tile.ts +807 -0
- package/src/tile/tile_id.ts +172 -0
- package/src/tile/tile_manager.ts +889 -0
- package/src/tile/tile_pyramid.ts +119 -0
- package/src/tile/tile_resource_cache.ts +91 -0
- package/src/tile/tile_traversal_adapter.ts +132 -0
- package/src/types.ts +332 -0
- package/src/utils/debounce.ts +17 -0
- package/src/utils/debug_settings.ts +43 -0
- package/src/utils/errors.ts +12 -0
- package/src/utils/functions.ts +95 -0
- package/src/utils/geo.ts +364 -0
- package/src/utils/hash.ts +14 -0
- package/src/utils/log.ts +108 -0
- package/src/utils/media_capture.ts +193 -0
- package/src/utils/merge.ts +44 -0
- package/src/utils/obb.ts +134 -0
- package/src/utils/props.ts +43 -0
- package/src/utils/slice.ts +14 -0
- package/src/utils/subscribe.ts +58 -0
- package/src/utils/task.ts +154 -0
- package/src/utils/thread.ts +26 -0
- package/src/utils/urls.ts +223 -0
- package/src/utils/utils.ts +310 -0
- package/src/utils/vector.ts +197 -0
- package/src/utils/version.ts +8 -0
- package/src/utils/worker-types.ts +79 -0
- package/src/utils/worker_broker.ts +523 -0
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
// Tangram
|
|
2
|
+
// SPDX-License-Identifier: MIT
|
|
3
|
+
// Copyright (c) 2013-2016 Brett Camper and Mapzen
|
|
4
|
+
// Copyright (c) 2026 vis.gl contributors
|
|
5
|
+
|
|
6
|
+
// Rearranges element array for triangles into a new element array that draws a wireframe
|
|
7
|
+
// Used for debugging
|
|
8
|
+
export default function makeWireframeForTriangleElementData (element_data: Uint16Array | Uint32Array): Uint16Array {
|
|
9
|
+
const wireframe_data = new Uint16Array(element_data.length * 2);
|
|
10
|
+
|
|
11
|
+
// Draw triangles as lines:
|
|
12
|
+
// Make a copy of element_data, and for every group of three vertices, duplicate
|
|
13
|
+
// each vertex according to the following pattern:
|
|
14
|
+
// [1, 2, 3] => [1, 2, 2, 3, 3, 1]
|
|
15
|
+
// This takes three vertices which would have been interpreted as a triangle,
|
|
16
|
+
// and converts them into three 2-vertex line segments.
|
|
17
|
+
for (let i = 0; i < element_data.length; i += 3) {
|
|
18
|
+
wireframe_data.set(
|
|
19
|
+
[
|
|
20
|
+
element_data[i],
|
|
21
|
+
element_data[i+1],
|
|
22
|
+
element_data[i+1],
|
|
23
|
+
element_data[i+2],
|
|
24
|
+
element_data[i+2],
|
|
25
|
+
element_data[i]
|
|
26
|
+
],
|
|
27
|
+
i * 2
|
|
28
|
+
);
|
|
29
|
+
}
|
|
30
|
+
return wireframe_data;
|
|
31
|
+
}
|
package/src/core.d.ts
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
// tangram-layers
|
|
2
|
+
// SPDX-License-Identifier: MIT
|
|
3
|
+
// Copyright (c) vis.gl contributors
|
|
4
|
+
|
|
5
|
+
export {ClassicWebGLRenderer as Renderer, HostFrame, LumaDeviceRenderer, WebMercatorGlobeVisibilityAdapter, WebMercatorVisibilityAdapter} from '@tangram.gl/tangram-renderer';
|
|
6
|
+
export {DrapedTerrainSurfaceSnapshot} from './selection/draped-terrain-surface';
|
|
7
|
+
export type {TerrainDrapingOptions, TerrainDrapingStatistics} from './procedures/terrain-draping';
|
|
8
|
+
export type {FeatureSelectionResult, FeatureSelectionOptions} from '@tangram.gl/tangram-renderer';
|
|
9
|
+
export {TerrainMeshSurface, TerrainSurfaceCollection, sampleTerrainElevation, resolveTerrainClearance, pickTerrainAt} from './selection/terrain_surface';
|
|
10
|
+
export type {TerrainSurface, TerrainSurfaceBounds, TerrainRay, TerrainSurfaceHit, TerrainMeshOptions, TerrainMeshStatistics, TerrainSurfaceEntry, TerrainCollectionHit, TerrainElevationProbe} from './selection/terrain_surface';
|
|
11
|
+
export {calculatePlanarGroundBounds, calculatePlanarVolumeBounds} from './scene/ground_footprint';
|
|
12
|
+
export {validateProjectedLights} from './procedures/mesh-projector';
|
|
13
|
+
export {normalizeProjectedBasemapOptions} from './procedures/mesh-projector';
|
|
14
|
+
export {getProjectedRoadUnit} from './procedures/mesh-projector';
|
|
15
|
+
export {countProjectedTileCoordinates, getTileGeographicBounds} from './tile/tile_traversal_adapter';
|
|
16
|
+
export {getProjectedCoordinateOptions, PROJECTED_COMMON_SCALE} from './procedures/projected-coordinate-transform';
|
|
17
|
+
export {ProjectionBatchExecutor} from './procedures/projection-batch-executor.js';
|
|
18
|
+
export type {ProjectionExecutionOptions, ProjectionExecutionStatistics, ProjectionBatchRequestOptions} from './procedures/projection-batch-executor.js';
|
|
19
|
+
export type {ProjectedBasemapOptions, MeshProjectionStatistics} from './procedures/mesh-projector';
|
|
20
|
+
export {PROJECTION_CONSTANTS, projectGeographicPosition, projectGeographicVector, unprojectGlobePosition, getGeographicProjectionProcedure} from './scene/projection_math';
|
|
21
|
+
export type {GeographicProjectionPosition, GeographicProjectionProcedure} from './scene/projection_math';
|
|
22
|
+
export {convertLumaLight, mapTangramLight} from './lights/light-definitions';
|
|
23
|
+
export type {ResolvedTangramLight, TangramLightMapping, TangramLight, TangramAmbientLight,
|
|
24
|
+
TangramDirectionalLight, TangramPointLight, TangramSpotLight, TangramLightExtensions,
|
|
25
|
+
TangramPositionalLightExtensions, TangramLightColor} from './lights/light-definitions';
|
|
26
|
+
export type {Light as LumaLight} from '@luma.gl/shadertools';
|
|
27
|
+
export type {TangramTileSourceMetadata} from './sources/tile_source_metadata';
|
|
28
|
+
export type {HostFrameOptions, HostRenderView, HostTileLODOptions, HostTileResourceOptions, TileResourceStatistics, TileSourceStatistics, HostCamera, HostProjection, GeographicAnchor, LegacyHostFrame, RendererOptions, RenderOptions, Viewport, SceneDefinition, SceneListeners, SceneLoadOptions, VisibilityLODAdapter, GlobeVisibilityLODAdapter, VisibilityViewState, GlobeVisibilityViewState, CalculatedViewBounds} from '@tangram.gl/tangram-renderer';
|
|
29
|
+
export type {ProjectionEngine, ProjectionEngineOptions, ProjectionEngineTransform} from './types.js';
|
package/src/core.ts
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
// tangram-layers
|
|
2
|
+
// SPDX-License-Identifier: MIT
|
|
3
|
+
// Copyright (c) vis.gl contributors
|
|
4
|
+
|
|
5
|
+
export {default as Renderer} from './scene/renderer';
|
|
6
|
+
export {DrapedTerrainSurfaceSnapshot} from './selection/draped-terrain-surface';
|
|
7
|
+
export type {TerrainDrapingOptions, TerrainDrapingStatistics} from './procedures/terrain-draping';
|
|
8
|
+
export {default as HostFrame} from './scene/host_frame';
|
|
9
|
+
export {TerrainMeshSurface, TerrainSurfaceCollection, sampleTerrainElevation, resolveTerrainClearance, pickTerrainAt} from './selection/terrain_surface';
|
|
10
|
+
export type {TerrainSurface, TerrainSurfaceBounds, TerrainRay, TerrainSurfaceHit, TerrainMeshOptions, TerrainMeshStatistics, TerrainSurfaceEntry, TerrainCollectionHit, TerrainElevationProbe} from './selection/terrain_surface';
|
|
11
|
+
export type {FeatureSelectionResult, FeatureSelectionOptions} from './types';
|
|
12
|
+
export {normalizeProjectedBasemapOptions} from './procedures/mesh-projector';
|
|
13
|
+
export {getProjectedRoadUnit} from './procedures/mesh-projector';
|
|
14
|
+
export {countProjectedTileCoordinates, getTileGeographicBounds} from './tile/tile_traversal_adapter';
|
|
15
|
+
export {getProjectedCoordinateOptions, PROJECTED_COMMON_SCALE} from './procedures/projected-coordinate-transform';
|
|
16
|
+
export {ProjectionBatchExecutor} from './procedures/projection-batch-executor';
|
|
17
|
+
export type {ProjectionExecutionOptions, ProjectionExecutionStatistics, ProjectionBatchRequestOptions} from './procedures/projection-batch-executor';
|
|
18
|
+
export type {ProjectionEngine, ProjectionEngineTransform} from './types';
|
|
19
|
+
export type {ProjectedBasemapOptions, MeshProjectionStatistics} from './procedures/mesh-projector';
|
|
20
|
+
export {calculatePlanarGroundBounds, calculatePlanarVolumeBounds} from './scene/ground_footprint';
|
|
21
|
+
export {validateProjectedLights} from './procedures/mesh-projector';
|
|
22
|
+
export {PROJECTION_CONSTANTS, projectGeographicPosition, projectGeographicVector, unprojectGlobePosition, getGeographicProjectionProcedure} from './scene/projection_math';
|
|
23
|
+
export type {GeographicProjectionPosition, GeographicProjectionProcedure} from './scene/projection_math';
|
|
24
|
+
export {convertLumaLight, mapTangramLight} from './lights/light-definitions';
|
|
25
|
+
export type {ResolvedTangramLight, TangramLightMapping, TangramLight, TangramAmbientLight,
|
|
26
|
+
TangramDirectionalLight, TangramPointLight, TangramSpotLight, TangramLightExtensions,
|
|
27
|
+
TangramPositionalLightExtensions, TangramLightColor} from './lights/light-definitions';
|
|
28
|
+
export type {Light as LumaLight} from '@luma.gl/shadertools';
|
|
29
|
+
export type {TangramTileSourceMetadata} from './sources/tile_source_metadata';
|
|
30
|
+
export {default as LumaDeviceRenderer} from './gpu/luma_device_renderer';
|
|
31
|
+
export {WebMercatorGlobeVisibilityAdapter, WebMercatorVisibilityAdapter} from './scene/visibility_adapter';
|
|
32
|
+
export type {HostFrameOptions, HostRenderView, HostTileLODOptions, HostTileResourceOptions, TileResourceStatistics, TileSourceStatistics, HostCamera, HostProjection, GeographicAnchor, LegacyHostFrame, RendererOptions, RenderOptions, Viewport} from './types';
|
|
33
|
+
export type {VisibilityLODAdapter, GlobeVisibilityLODAdapter, VisibilityViewState, GlobeVisibilityViewState, CalculatedViewBounds} from './scene/visibility_adapter';
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
// tangram-layers
|
|
2
|
+
// SPDX-License-Identifier: MIT
|
|
3
|
+
// Copyright (c) vis.gl contributors
|
|
4
|
+
|
|
5
|
+
import {pmtilesTileProvider} from '../procedures/pmtiles-loader';
|
|
6
|
+
import {parseMltWithLoaders} from '../procedures/mlt-loaders';
|
|
7
|
+
import {parseMvtWithLoaders} from '../procedures/mvt-loaders';
|
|
8
|
+
import type {MvtDecoder} from '../procedures/mvt-parser';
|
|
9
|
+
import type {MvtTileProviderRegistration} from '../procedures/mvt-tile-provider';
|
|
10
|
+
|
|
11
|
+
type TangramWorker = typeof self & {
|
|
12
|
+
registerMvtDecoder: (name: string, decoder: MvtDecoder) => void;
|
|
13
|
+
registerMvtTileProvider: (name: string, provider: MvtTileProviderRegistration) => void;
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
const tangramWorker = self as TangramWorker;
|
|
17
|
+
tangramWorker.registerMvtDecoder('loaders-mvt', parseMvtWithLoaders);
|
|
18
|
+
tangramWorker.registerMvtDecoder('loaders-mlt', parseMltWithLoaders);
|
|
19
|
+
tangramWorker.registerMvtTileProvider('loaders-pmtiles', pmtilesTileProvider);
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
// tangram-layers
|
|
2
|
+
// SPDX-License-Identifier: MIT
|
|
3
|
+
// Copyright (c) vis.gl contributors
|
|
4
|
+
|
|
5
|
+
import {projectBasemapMesh, projectBasemapMeshWithEngine, clearProjectedMeshPreparation,
|
|
6
|
+
getProjectedMeshPreparationStatistics, getProjectedMeshWorkStatistics} from './projected-mesh';
|
|
7
|
+
import type {MeshProjectionRequest} from '../procedures/mesh-projector';
|
|
8
|
+
if (!('registerMeshProjector' in self) || typeof self.registerMeshProjector !== 'function') {
|
|
9
|
+
throw new Error('Projected basemap script must run inside a Tangram scene worker');
|
|
10
|
+
}
|
|
11
|
+
self.registerMeshProjector((request: MeshProjectionRequest) => request.projectPositions ?
|
|
12
|
+
projectBasemapMeshWithEngine(request) : projectBasemapMesh(request), clearProjectedMeshPreparation,
|
|
13
|
+
getProjectedMeshPreparationStatistics, getProjectedMeshWorkStatistics);
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
// tangram-layers
|
|
2
|
+
// SPDX-License-Identifier: MIT
|
|
3
|
+
// Copyright (c) vis.gl contributors
|
|
4
|
+
|
|
5
|
+
/** Immutable prepared or projected mesh owned by one worker. */
|
|
6
|
+
export interface PreparedProjectedMesh {
|
|
7
|
+
/** Packed source attributes, never transferred or written into projected output. */
|
|
8
|
+
readonly vertices: Uint8Array;
|
|
9
|
+
/** Source-space refined triangle topology. */
|
|
10
|
+
readonly indices: Uint16Array | Uint32Array;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
/** Content-addressed entry; exact byte comparison guards against hash collisions. */
|
|
14
|
+
interface PreparationEntry {
|
|
15
|
+
/** Metadata and source-content lookup identity. */
|
|
16
|
+
readonly key: string;
|
|
17
|
+
/** Detached original source bytes for exact equality and mutation detection. */
|
|
18
|
+
readonly source: Uint8Array;
|
|
19
|
+
/** Detached source topology; false denotes an unindexed stream. */
|
|
20
|
+
readonly elements: Uint16Array | Uint32Array | false;
|
|
21
|
+
/** Immutable prepared geometry. */
|
|
22
|
+
readonly result: PreparedProjectedMesh;
|
|
23
|
+
/** Accounted retained typed-array bytes. */
|
|
24
|
+
readonly bytes: number;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/** Bounded content-addressed LRU; callers supply preparation or target/engine metadata. */
|
|
28
|
+
export class ProjectedMeshPreparationCache {
|
|
29
|
+
/** Entries ordered oldest to newest, including colliding content hashes. */
|
|
30
|
+
private readonly entries: PreparationEntry[] = [];
|
|
31
|
+
/** Bytes retained in original and prepared buffers. */
|
|
32
|
+
private bytes = 0;
|
|
33
|
+
/** Number of preparations successfully reused. */
|
|
34
|
+
private hits = 0;
|
|
35
|
+
/** Number of requests requiring source preparation. */
|
|
36
|
+
private misses = 0;
|
|
37
|
+
/** Reset epoch prevents a late asynchronous preparation from repopulating a cleared cache. */
|
|
38
|
+
private epoch = 0;
|
|
39
|
+
|
|
40
|
+
/** Set worker-local limits; oversized entries are prepared but never retained. */
|
|
41
|
+
constructor(private readonly maxBytes = 16 * 1024 * 1024, private readonly maxEntries = 64) {
|
|
42
|
+
if (!Number.isSafeInteger(maxBytes) || maxBytes < 0 ||
|
|
43
|
+
!Number.isSafeInteger(maxEntries) || maxEntries < 0) {
|
|
44
|
+
throw new RangeError('Projection preparation cache requires nonnegative integer limits');
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/** Reuse only byte-identical source geometry and identical refinement/layout metadata. */
|
|
49
|
+
getOrCreate(metadata: string, vertices: Uint8Array, indices: Uint16Array | Uint32Array | false,
|
|
50
|
+
prepare: () => PreparedProjectedMesh): PreparedProjectedMesh {
|
|
51
|
+
const cached = this.find(metadata, vertices, indices);
|
|
52
|
+
return cached ?? this.retain(metadata, vertices, indices, prepare());
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/** Cache completed asynchronous results only; errors and replies from older epochs are not retained. */
|
|
56
|
+
async getOrCreateAsync(metadata: string, vertices: Uint8Array, indices: Uint16Array | Uint32Array | false,
|
|
57
|
+
prepare: () => Promise<PreparedProjectedMesh>): Promise<PreparedProjectedMesh> {
|
|
58
|
+
const cached = this.find(metadata, vertices, indices);
|
|
59
|
+
if (cached) return cached;
|
|
60
|
+
const epoch = this.epoch;
|
|
61
|
+
const source = vertices.slice();
|
|
62
|
+
const elements = indices === false ? false : indices.slice();
|
|
63
|
+
const result = await prepare();
|
|
64
|
+
return epoch === this.epoch ? this.retain(metadata, source, elements, result) : result;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/** Locate byte-identical inputs and update LRU/work counters. */
|
|
68
|
+
private find(metadata: string, vertices: Uint8Array, indices: Uint16Array | Uint32Array | false): PreparedProjectedMesh | undefined {
|
|
69
|
+
const key = `${metadata}:${hashBytes(vertices)}:${indices === false ? 'stream' : hashBytes(asBytes(indices))}`;
|
|
70
|
+
const index = this.entries.findIndex(entry => entry.key === key && equalBytes(entry.source, vertices) &&
|
|
71
|
+
(entry.elements === false || indices === false ? entry.elements === indices :
|
|
72
|
+
entry.elements.constructor === indices.constructor && equalBytes(asBytes(entry.elements), asBytes(indices))));
|
|
73
|
+
if (index >= 0) {
|
|
74
|
+
const [entry] = this.entries.splice(index, 1);
|
|
75
|
+
this.entries.push(entry);
|
|
76
|
+
this.hits++;
|
|
77
|
+
return entry.result;
|
|
78
|
+
}
|
|
79
|
+
this.misses++;
|
|
80
|
+
return undefined;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/** Detach retained inputs and output within the configured byte/entry limits. */
|
|
84
|
+
private retain(metadata: string, vertices: Uint8Array, indices: Uint16Array | Uint32Array | false,
|
|
85
|
+
prepared: PreparedProjectedMesh): PreparedProjectedMesh {
|
|
86
|
+
const key = `${metadata}:${hashBytes(vertices)}:${indices === false ? 'stream' : hashBytes(asBytes(indices))}`;
|
|
87
|
+
// Concurrent misses may both compute; retain only one byte-identical completed entry.
|
|
88
|
+
const existing = this.entries.find(entry => entry.key === key && equalBytes(entry.source, vertices) &&
|
|
89
|
+
(entry.elements === false || indices === false ? entry.elements === indices :
|
|
90
|
+
entry.elements.constructor === indices.constructor && equalBytes(asBytes(entry.elements), asBytes(indices))));
|
|
91
|
+
if (existing) return existing.result;
|
|
92
|
+
const bytes = vertices.byteLength + (indices === false ? 0 : indices.byteLength) +
|
|
93
|
+
prepared.vertices.byteLength + prepared.indices.byteLength;
|
|
94
|
+
if (this.maxEntries > 0 && bytes <= this.maxBytes) {
|
|
95
|
+
while (this.entries.length >= this.maxEntries || this.bytes + bytes > this.maxBytes) {
|
|
96
|
+
this.bytes -= this.entries.shift()!.bytes;
|
|
97
|
+
}
|
|
98
|
+
// Preparation can return aliases of caller input. Detach everything kept by the cache.
|
|
99
|
+
const result = {vertices: prepared.vertices.slice(), indices: prepared.indices.slice()};
|
|
100
|
+
this.entries.push({key, source: vertices.slice(), elements: indices === false ? false : indices.slice(), result, bytes});
|
|
101
|
+
this.bytes += bytes;
|
|
102
|
+
return result;
|
|
103
|
+
}
|
|
104
|
+
return prepared;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/** Snapshot diagnostics without exposing mutable cached records. */
|
|
108
|
+
getStatistics(): {entries: number; bytes: number; hits: number; misses: number} {
|
|
109
|
+
return {entries: this.entries.length, bytes: this.bytes, hits: this.hits, misses: this.misses};
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/** Release worker-owned preparation at reset or teardown. */
|
|
113
|
+
clear(): void {
|
|
114
|
+
this.epoch++;
|
|
115
|
+
this.entries.length = 0;
|
|
116
|
+
this.bytes = this.hits = this.misses = 0;
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/** View only the typed array's occupied bytes, including nonzero buffer offsets. */
|
|
121
|
+
function asBytes(values: Uint16Array | Uint32Array): Uint8Array {
|
|
122
|
+
return new Uint8Array(values.buffer, values.byteOffset, values.byteLength);
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
/** Cheap lookup hash, never a substitute for exact source comparison. */
|
|
126
|
+
function hashBytes(values: Uint8Array): number {
|
|
127
|
+
let hash = 2166136261;
|
|
128
|
+
for (const value of values) hash = Math.imul(hash ^ value, 16777619);
|
|
129
|
+
return hash >>> 0;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
/** Compare detached content, not transferable buffer identity. */
|
|
133
|
+
function equalBytes(first: Uint8Array, second: Uint8Array): boolean {
|
|
134
|
+
if (first.length !== second.length) return false;
|
|
135
|
+
for (let index = 0; index < first.length; index++) if (first[index] !== second[index]) return false;
|
|
136
|
+
return true;
|
|
137
|
+
}
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
// tangram-layers
|
|
2
|
+
// SPDX-License-Identifier: MIT
|
|
3
|
+
// Copyright (c) vis.gl contributors
|
|
4
|
+
|
|
5
|
+
import {interpolatePackedVertex} from '../gl/globe_mesh';
|
|
6
|
+
import type {MeshProjectionRequest, ProjectedMesh} from '../procedures/mesh-projector';
|
|
7
|
+
|
|
8
|
+
/** Geographic latitude limit of the source Web Mercator tile world. */
|
|
9
|
+
export const PROJECTED_TILE_LATITUDE = 85.0511287798066;
|
|
10
|
+
/** Half the width of one EPSG:3857 world, in meters. */
|
|
11
|
+
const WORLD_HALF_WIDTH = Math.PI * 6378137;
|
|
12
|
+
|
|
13
|
+
/** World bounds for continuous projections, with the preview's explicit Albers region. */
|
|
14
|
+
export function getProjectedSourceDomain(type: MeshProjectionRequest['projection']['type']): readonly [number, number, number, number] {
|
|
15
|
+
if (type === 'albers') {
|
|
16
|
+
const latitudeToMeters = (latitude: number) => 6378137 * Math.asinh(Math.tan(latitude * Math.PI / 180));
|
|
17
|
+
return [-170 / 180 * WORLD_HALF_WIDTH, latitudeToMeters(5),
|
|
18
|
+
-40 / 180 * WORLD_HALF_WIDTH, latitudeToMeters(75)];
|
|
19
|
+
}
|
|
20
|
+
return [-WORLD_HALF_WIDTH, -WORLD_HALF_WIDTH, WORLD_HALF_WIDTH, WORLD_HALF_WIDTH];
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/** Clip triangles before nonlinear projection, retaining winding, UVs and flat feature attributes. */
|
|
24
|
+
export function clipProjectedMesh(mesh: ProjectedMesh, request: MeshProjectionRequest,
|
|
25
|
+
readPosition: (vertex: DataView) => readonly [number, number], remainingVertices: number): ProjectedMesh {
|
|
26
|
+
const {layout, tile} = request;
|
|
27
|
+
const unitsPerMeter = 4096 * 2 ** tile.coords.z / (2 * WORLD_HALF_WIDTH);
|
|
28
|
+
const domain = getProjectedSourceDomain(request.projection.type);
|
|
29
|
+
const bounds = [(domain[0] - tile.min.x) * unitsPerMeter, (domain[1] - tile.min.y) * unitsPerMeter,
|
|
30
|
+
(domain[2] - tile.min.x) * unitsPerMeter, (domain[3] - tile.min.y) * unitsPerMeter];
|
|
31
|
+
const records: Uint8Array[] = [];
|
|
32
|
+
const positions: (readonly [number, number])[] = [];
|
|
33
|
+
for (let offset = 0; offset < mesh.vertices.byteLength; offset += layout.stride) {
|
|
34
|
+
const record = mesh.vertices.subarray(offset, offset + layout.stride);
|
|
35
|
+
records.push(record);
|
|
36
|
+
positions.push(readPosition(new DataView(record.buffer, record.byteOffset, record.byteLength)));
|
|
37
|
+
}
|
|
38
|
+
const originalCount = records.length;
|
|
39
|
+
const inside = (index: number) => positions[index][0] >= bounds[0] && positions[index][0] <= bounds[2] &&
|
|
40
|
+
positions[index][1] >= bounds[1] && positions[index][1] <= bounds[3];
|
|
41
|
+
if (mesh.indices.every(inside)) return mesh;
|
|
42
|
+
const intersections = new Map<string, number>();
|
|
43
|
+
const triangles: number[] = [];
|
|
44
|
+
const positionOffset = layout.dynamic_attribs.find(attribute => attribute.name === 'a_position')?.offset;
|
|
45
|
+
const height = (vertex: number) => {
|
|
46
|
+
if (positionOffset === undefined) throw new Error('Projected surface requires packed positions');
|
|
47
|
+
return new DataView(records[vertex].buffer, records[vertex].byteOffset, layout.stride).getInt16(positionOffset + 4, true);
|
|
48
|
+
};
|
|
49
|
+
for (let offset = 0; offset < mesh.indices.length; offset += 3) {
|
|
50
|
+
let polygon = Array.from(mesh.indices.subarray(offset, offset + 3));
|
|
51
|
+
// Reject against every plane first: an early cut must not spend budget on
|
|
52
|
+
// a triangle that a later plane will discard entirely.
|
|
53
|
+
if (bounds.some((boundary, plane) => polygon.every(index =>
|
|
54
|
+
(positions[index][plane % 2] - boundary) * (plane < 2 ? 1 : -1) < 0))) continue;
|
|
55
|
+
for (let plane = 0; plane < 4 && polygon.length; plane++) {
|
|
56
|
+
const axis = plane % 2;
|
|
57
|
+
const boundary = bounds[plane];
|
|
58
|
+
const distance = (index: number) => (positions[index][axis] - boundary) * (plane < 2 ? 1 : -1);
|
|
59
|
+
const clipped: number[] = [];
|
|
60
|
+
let previous = polygon[polygon.length - 1];
|
|
61
|
+
for (const current of polygon) {
|
|
62
|
+
const previousDistance = distance(previous), currentDistance = distance(current);
|
|
63
|
+
if ((previousDistance >= 0) !== (currentDistance >= 0)) {
|
|
64
|
+
const first = Math.min(previous, current), second = Math.max(previous, current);
|
|
65
|
+
const key = `${plane}:${first}:${second}`;
|
|
66
|
+
let intersection = intersections.get(key);
|
|
67
|
+
if (intersection === undefined) {
|
|
68
|
+
const fraction = distance(first) / (distance(first) - distance(second));
|
|
69
|
+
if (fraction === 0 || fraction === 1) intersection = fraction === 0 ? first : second;
|
|
70
|
+
else {
|
|
71
|
+
if (records.length - originalCount >= remainingVertices) {
|
|
72
|
+
throw new RangeError('CPU projection clipping vertex budget exceeded');
|
|
73
|
+
}
|
|
74
|
+
intersection = records.length;
|
|
75
|
+
records.push(interpolatePackedVertex(records[first], records[second], layout, fraction));
|
|
76
|
+
positions.push(axis === 0 ? [boundary, positions[first][1] * (1 - fraction) + positions[second][1] * fraction] :
|
|
77
|
+
[positions[first][0] * (1 - fraction) + positions[second][0] * fraction, boundary]);
|
|
78
|
+
}
|
|
79
|
+
intersections.set(key, intersection);
|
|
80
|
+
}
|
|
81
|
+
if (clipped[clipped.length - 1] !== intersection) clipped.push(intersection);
|
|
82
|
+
}
|
|
83
|
+
if (currentDistance >= 0 && clipped[clipped.length - 1] !== current) clipped.push(current);
|
|
84
|
+
previous = current;
|
|
85
|
+
}
|
|
86
|
+
if (clipped.length > 1 && clipped[0] === clipped[clipped.length - 1]) clipped.pop();
|
|
87
|
+
polygon = clipped;
|
|
88
|
+
}
|
|
89
|
+
for (let index = 1; index < polygon.length - 1; index++) {
|
|
90
|
+
const first = positions[polygon[0]], second = positions[polygon[index]], third = positions[polygon[index + 1]];
|
|
91
|
+
const area = (second[0] - first[0]) * (third[1] - first[1]) -
|
|
92
|
+
(second[1] - first[1]) * (third[0] - first[0]);
|
|
93
|
+
// A vertical wall has no XY area. Retain its XZ/YZ area after cuts.
|
|
94
|
+
const elevated = request.projection.allowElevation && request.geometry !== 'lines' &&
|
|
95
|
+
((second[0] - first[0]) * (height(polygon[index + 1]) - height(polygon[0])) -
|
|
96
|
+
(height(polygon[index]) - height(polygon[0])) * (third[0] - first[0]) !== 0 ||
|
|
97
|
+
(second[1] - first[1]) * (height(polygon[index + 1]) - height(polygon[0])) -
|
|
98
|
+
(height(polygon[index]) - height(polygon[0])) * (third[1] - first[1]) !== 0);
|
|
99
|
+
if (area !== 0 || elevated) triangles.push(polygon[0], polygon[index], polygon[index + 1]);
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
// Drop unused/outside vertices so no invalid coordinate is submitted to the engine.
|
|
103
|
+
const remap = new Map<number, number>();
|
|
104
|
+
for (const index of triangles) if (!remap.has(index)) remap.set(index, remap.size);
|
|
105
|
+
const vertices = new Uint8Array(remap.size * layout.stride);
|
|
106
|
+
for (const [original, index] of remap) vertices.set(records[original], index * layout.stride);
|
|
107
|
+
const indices = remap.size <= 65536 ? new Uint16Array(triangles.length) : new Uint32Array(triangles.length);
|
|
108
|
+
triangles.forEach((index, offset) => {indices[offset] = remap.get(index)!;});
|
|
109
|
+
return {vertices, indices};
|
|
110
|
+
}
|
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
// tangram-layers
|
|
2
|
+
// SPDX-License-Identifier: MIT
|
|
3
|
+
// Copyright (c) vis.gl contributors
|
|
4
|
+
|
|
5
|
+
import {interpolatePackedVertex, refineGlobeMesh, selectTrianglePlane} from '../gl/globe_mesh';
|
|
6
|
+
import type {MeshProjectionRequest, ProjectedMesh} from '../procedures/mesh-projector';
|
|
7
|
+
|
|
8
|
+
/** Per-request refinement work, separate from cached source preparation. */
|
|
9
|
+
export interface ProjectedRefinementWork {
|
|
10
|
+
/** Rounds that subdivide curved edges. */
|
|
11
|
+
edgeRounds: number;
|
|
12
|
+
/** Rounds that add strictly interior triangle vertices. */
|
|
13
|
+
interiorRounds: number;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
const INTERIOR_WEIGHTS = [[1 / 3, 1 / 3, 1 / 3], [0.5, 0.25, 0.25],
|
|
17
|
+
[0.25, 0.5, 0.25], [0.25, 0.25, 0.5]] as const;
|
|
18
|
+
|
|
19
|
+
/** Batch projection samples per refinement round; local and caller-owned kernels use identical topology decisions.
|
|
20
|
+
* Edge and barycentric interior probes are sampled estimates, not certified bounds for arbitrary kernels.
|
|
21
|
+
* Indexed neighbors share decisions. Independent source tiles use identical packed midpoint rules and tolerance.
|
|
22
|
+
*/
|
|
23
|
+
export function* refineProjectedMesh(mesh: ProjectedMesh, request: MeshProjectionRequest,
|
|
24
|
+
geographicPosition: (vertex: DataView, displacement?: readonly [number, number]) => readonly [number, number],
|
|
25
|
+
readPosition: (vertex: DataView) => [number, number],
|
|
26
|
+
work: ProjectedRefinementWork = {edgeRounds: 0, interiorRounds: 0}): Generator<Float64Array, ProjectedMesh, Float64Array> {
|
|
27
|
+
const tolerance = request.projection.maxProjectedError;
|
|
28
|
+
if (tolerance === undefined || mesh.indices.length === 0) return mesh;
|
|
29
|
+
const {layout} = request;
|
|
30
|
+
const originalCount = request.vertices.byteLength / layout.stride;
|
|
31
|
+
const budget = request.projection.maxAdditionalVertices ?? 65536;
|
|
32
|
+
let result = mesh;
|
|
33
|
+
const key = (vertex: DataView): string => Array.from(new Uint8Array(vertex.buffer, vertex.byteOffset, vertex.byteLength)).join(',');
|
|
34
|
+
const edgeKey = (first: DataView, second: DataView): string => {
|
|
35
|
+
const firstKey = key(first), secondKey = key(second);
|
|
36
|
+
return firstKey < secondKey ? `${firstKey}:${secondKey}` : `${secondKey}:${firstKey}`;
|
|
37
|
+
};
|
|
38
|
+
for (let iteration = 0; iteration < 32; iteration++) {
|
|
39
|
+
const edges = new Map<string, [Uint8Array, Uint8Array]>();
|
|
40
|
+
for (let index = 0; index < result.indices.length; index += 3) for (let edge = 0; edge < 3; edge++) {
|
|
41
|
+
const first = result.vertices.subarray(result.indices[index + edge] * layout.stride,
|
|
42
|
+
(result.indices[index + edge] + 1) * layout.stride);
|
|
43
|
+
const secondIndex = result.indices[index + (edge + 1) % 3];
|
|
44
|
+
const second = result.vertices.subarray(secondIndex * layout.stride, (secondIndex + 1) * layout.stride);
|
|
45
|
+
edges.set(edgeKey(new DataView(first.buffer, first.byteOffset, first.byteLength),
|
|
46
|
+
new DataView(second.buffer, second.byteOffset, second.byteLength)), [first, second]);
|
|
47
|
+
}
|
|
48
|
+
const coordinates: number[] = [];
|
|
49
|
+
for (const [first, second] of edges.values()) {
|
|
50
|
+
const start = readPosition(new DataView(first.buffer, first.byteOffset, first.byteLength));
|
|
51
|
+
const end = readPosition(new DataView(second.buffer, second.byteOffset, second.byteLength));
|
|
52
|
+
for (const fraction of [0, 0.25, 0.5, 0.75, 1]) {
|
|
53
|
+
const sample = fraction === 0 ? first : fraction === 1 ? second : interpolatePackedVertex(first, second, layout, fraction);
|
|
54
|
+
const view = new DataView(sample.buffer, sample.byteOffset, sample.byteLength);
|
|
55
|
+
const packed = readPosition(view);
|
|
56
|
+
// Test the exact source chord, not off-chord integer rounding of
|
|
57
|
+
// its probes. Generated vertices still retain the packed rules.
|
|
58
|
+
coordinates.push(...geographicPosition(view, [start[0] * (1 - fraction) + end[0] * fraction - packed[0],
|
|
59
|
+
start[1] * (1 - fraction) + end[1] * fraction - packed[1]]));
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
const edgeCoordinateCount = coordinates.length;
|
|
63
|
+
// Ribbons are parameterized around centerlines; their pixel-width/fan
|
|
64
|
+
// reconstruction is not a geographic surface interpolant.
|
|
65
|
+
const inspectInteriors = request.geometry !== 'lines';
|
|
66
|
+
if (inspectInteriors) for (let index = 0; index < result.indices.length; index += 3) {
|
|
67
|
+
const corners = [0, 1, 2].map(component => result.vertices.subarray(
|
|
68
|
+
result.indices[index + component] * layout.stride, (result.indices[index + component] + 1) * layout.stride));
|
|
69
|
+
const points = corners.map(record => readPosition(new DataView(record.buffer, record.byteOffset, layout.stride)));
|
|
70
|
+
for (const corner of corners) coordinates.push(...geographicPosition(new DataView(corner.buffer, corner.byteOffset, layout.stride)));
|
|
71
|
+
for (const weights of INTERIOR_WEIGHTS) {
|
|
72
|
+
const sample = interpolateTriangle(corners, request, weights);
|
|
73
|
+
const view = new DataView(sample.buffer, sample.byteOffset, layout.stride);
|
|
74
|
+
const packed = readPosition(view);
|
|
75
|
+
coordinates.push(...geographicPosition(view, [
|
|
76
|
+
points.reduce((sum, point, component) => sum + point[0] * weights[component], 0) - packed[0],
|
|
77
|
+
points.reduce((sum, point, component) => sum + point[1] * weights[component], 0) - packed[1]]));
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
const projected = yield new Float64Array(coordinates);
|
|
81
|
+
if (!(projected instanceof Float64Array) || projected.length !== coordinates.length || !projected.every(Number.isFinite)) {
|
|
82
|
+
throw new Error('Projection refinement returned an invalid common-position batch');
|
|
83
|
+
}
|
|
84
|
+
const split = new Set<string>();
|
|
85
|
+
let offset = 0;
|
|
86
|
+
for (const edge of edges.keys()) {
|
|
87
|
+
const differenceX = projected[offset + 8] - projected[offset];
|
|
88
|
+
const differenceY = projected[offset + 9] - projected[offset + 1];
|
|
89
|
+
const lengthSquared = differenceX ** 2 + differenceY ** 2;
|
|
90
|
+
for (let sample = 1; sample < 4; sample++) {
|
|
91
|
+
// Packed samples round to the source grid. Movement along a straight
|
|
92
|
+
// chord is not curvature, even when it no longer equals sample / 4.
|
|
93
|
+
const fraction = request.geometry !== 'lines' ? sample / 4 : lengthSquared === 0 ? 0 : Math.max(0, Math.min(1,
|
|
94
|
+
((projected[offset + sample * 2] - projected[offset]) * differenceX +
|
|
95
|
+
(projected[offset + sample * 2 + 1] - projected[offset + 1]) * differenceY) / lengthSquared));
|
|
96
|
+
// Surfaces need parameterized interpolation accuracy for raster
|
|
97
|
+
// UVs, not just perpendicular silhouette error. Exact source
|
|
98
|
+
// probes above keep packed rounding out of this measurement.
|
|
99
|
+
const error = Math.hypot(projected[offset + sample * 2] -
|
|
100
|
+
(projected[offset] * (1 - fraction) + projected[offset + 8] * fraction),
|
|
101
|
+
projected[offset + sample * 2 + 1] -
|
|
102
|
+
(projected[offset + 1] * (1 - fraction) + projected[offset + 9] * fraction));
|
|
103
|
+
if (error > tolerance) split.add(edge);
|
|
104
|
+
}
|
|
105
|
+
offset += 10;
|
|
106
|
+
}
|
|
107
|
+
if (split.size === 0) {
|
|
108
|
+
const interiors = new Set<number>();
|
|
109
|
+
offset = edgeCoordinateCount;
|
|
110
|
+
if (inspectInteriors) for (let index = 0; index < result.indices.length; index += 3) {
|
|
111
|
+
for (const [sample, weights] of INTERIOR_WEIGHTS.entries()) {
|
|
112
|
+
const expected = [0, 1].map(component => weights.reduce((sum, weight, corner) =>
|
|
113
|
+
sum + weight * projected[offset + corner * 2 + component], 0));
|
|
114
|
+
const actual = offset + 6 + sample * 2;
|
|
115
|
+
if (Math.hypot(projected[actual] - expected[0], projected[actual + 1] - expected[1]) > tolerance) {
|
|
116
|
+
interiors.add(index);
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
offset += 14;
|
|
120
|
+
}
|
|
121
|
+
if (interiors.size === 0) return result;
|
|
122
|
+
result = splitTriangleInteriors(result, request, interiors, readPosition,
|
|
123
|
+
budget - (result.vertices.byteLength / layout.stride - originalCount));
|
|
124
|
+
work.interiorRounds++;
|
|
125
|
+
continue;
|
|
126
|
+
}
|
|
127
|
+
const refined = refineGlobeMesh(result.vertices, result.indices, layout, {tileZoom: request.tile.coords.z,
|
|
128
|
+
maxAngularSpan: 180, maxAdditionalVertices: budget - (result.vertices.byteLength / layout.stride - originalCount),
|
|
129
|
+
splitRemainderInterior: inspectInteriors,
|
|
130
|
+
getElevation: getPackedElevationReader(request),
|
|
131
|
+
getPosition: readPosition, shouldSplitEdge: (first, second) => split.has(edgeKey(first, second))});
|
|
132
|
+
if (refined.vertices.byteLength === result.vertices.byteLength) {
|
|
133
|
+
throw new RangeError('Projected error tolerance exceeds packed coordinate precision');
|
|
134
|
+
}
|
|
135
|
+
result = refined;
|
|
136
|
+
work.edgeRounds++;
|
|
137
|
+
}
|
|
138
|
+
throw new RangeError('Projected refinement iteration budget exceeded');
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
/** Interpolate all supported varying attributes while retaining flat feature provenance. */
|
|
142
|
+
function interpolateTriangle(corners: Uint8Array[], request: MeshProjectionRequest, weights: readonly number[]): Uint8Array {
|
|
143
|
+
return interpolatePackedVertex(interpolatePackedVertex(corners[0], corners[1], request.layout,
|
|
144
|
+
weights[1] / (weights[0] + weights[1])), corners[2], request.layout, weights[2]);
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
/** Insert strictly interior packed centers without changing any shared edge or its neighbors. */
|
|
148
|
+
function splitTriangleInteriors(mesh: ProjectedMesh, request: MeshProjectionRequest, selected: Set<number>,
|
|
149
|
+
readPosition: (vertex: DataView) => [number, number], remainingBudget: number): ProjectedMesh {
|
|
150
|
+
if (selected.size > remainingBudget) throw new RangeError('Projected interior refinement vertex budget exceeded');
|
|
151
|
+
const {stride} = request.layout;
|
|
152
|
+
const vertexCount = mesh.vertices.byteLength / stride;
|
|
153
|
+
const vertices = new Uint8Array(mesh.vertices.byteLength + selected.size * stride);
|
|
154
|
+
vertices.set(mesh.vertices);
|
|
155
|
+
const indices: number[] = [];
|
|
156
|
+
let inserted = 0;
|
|
157
|
+
for (let index = 0; index < mesh.indices.length; index += 3) {
|
|
158
|
+
const triangle = [mesh.indices[index], mesh.indices[index + 1], mesh.indices[index + 2]];
|
|
159
|
+
if (!selected.has(index)) {indices.push(...triangle); continue;}
|
|
160
|
+
const corners = triangle.map(vertex => mesh.vertices.subarray(vertex * stride, (vertex + 1) * stride));
|
|
161
|
+
const center = interpolateTriangle(corners, request, INTERIOR_WEIGHTS[0]);
|
|
162
|
+
const readElevation = getPackedElevationReader(request);
|
|
163
|
+
const topologyPosition = (vertex: Uint8Array): [number, number, number] => {
|
|
164
|
+
const view = new DataView(vertex.buffer, vertex.byteOffset, stride);
|
|
165
|
+
return [...readPosition(view), readElevation?.(view) ?? 0];
|
|
166
|
+
};
|
|
167
|
+
const corners3D = corners.map(topologyPosition);
|
|
168
|
+
const axes = selectTrianglePlane(corners3D);
|
|
169
|
+
const flatten = (position: readonly [number, number, number]): [number, number] => [position[axes[0]], position[axes[1]]];
|
|
170
|
+
const point = flatten(topologyPosition(center));
|
|
171
|
+
const points = corners3D.map(flatten);
|
|
172
|
+
const cross = (first: number[], second: number[], third: number[]) =>
|
|
173
|
+
(second[0] - first[0]) * (third[1] - first[1]) - (second[1] - first[1]) * (third[0] - first[0]);
|
|
174
|
+
const orientation = cross(points[0], points[1], points[2]);
|
|
175
|
+
if (points.some((start, edge) => cross(start, points[(edge + 1) % 3], point) * orientation <= 0)) {
|
|
176
|
+
throw new RangeError('Projected interior tolerance exceeds packed coordinate precision');
|
|
177
|
+
}
|
|
178
|
+
const centerIndex = vertexCount + inserted++;
|
|
179
|
+
vertices.set(center, centerIndex * stride);
|
|
180
|
+
indices.push(triangle[0], triangle[1], centerIndex, triangle[1], triangle[2], centerIndex,
|
|
181
|
+
triangle[2], triangle[0], centerIndex);
|
|
182
|
+
}
|
|
183
|
+
return {vertices, indices: vertexCount + selected.size <= 65536 ? new Uint16Array(indices) : new Uint32Array(indices)};
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
/** Read the original packed height only for opted-in surface topology checks. */
|
|
187
|
+
function getPackedElevationReader(request: MeshProjectionRequest): ((vertex: DataView) => number) | undefined {
|
|
188
|
+
if (!request.projection.allowElevation || request.geometry === 'lines') return undefined;
|
|
189
|
+
const position = request.layout.dynamic_attribs.find(attribute => attribute.name === 'a_position');
|
|
190
|
+
if (!position || position.offset === undefined || position.size < 3) throw new Error('Projected elevation requires a position layout');
|
|
191
|
+
const offset = position.offset;
|
|
192
|
+
return vertex => vertex.getInt16(offset + 4, true);
|
|
193
|
+
}
|