@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,683 @@
|
|
|
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
|
+
import Geo from '../utils/geo';
|
|
7
|
+
import {TileID} from '../tile/tile_id';
|
|
8
|
+
import {createGlobePreloadCoordinates} from '../tile/globe_tile_preload';
|
|
9
|
+
import TangramTileTraversalAdapter from '../tile/tile_traversal_adapter';
|
|
10
|
+
import type {TangramTraversalState} from '../tile/tile_traversal_adapter';
|
|
11
|
+
import ExternalCamera from './external_camera';
|
|
12
|
+
import type Camera from './camera_base';
|
|
13
|
+
import type {CameraView, CameraConfiguration, MatrixSet, Program, UniformBuffer} from './camera_base';
|
|
14
|
+
import type HostFrame from './host_frame';
|
|
15
|
+
import type {NormalizedRenderView} from './host_frame';
|
|
16
|
+
import ProjectedTileLOD from './projected_tile_lod';
|
|
17
|
+
import type {HostCamera, HostProjection, HostTileResourceOptions} from '../types';
|
|
18
|
+
import type {Bounds, Meters, Tile} from '../utils/geo';
|
|
19
|
+
import type {TileCoordinate} from '../tile/tile_id';
|
|
20
|
+
import type {VisibilityLODAdapter, GlobeVisibilityLODAdapter, VisibilityViewState} from './visibility_adapter';
|
|
21
|
+
import type {SubscriptionMethods} from '../utils/subscribe';
|
|
22
|
+
import {
|
|
23
|
+
WebMercatorGlobeVisibilityAdapter,
|
|
24
|
+
WebMercatorVisibilityAdapter
|
|
25
|
+
} from './visibility_adapter';
|
|
26
|
+
import Utils from '../utils/utils';
|
|
27
|
+
import subscribeMixin from '../utils/subscribe';
|
|
28
|
+
import log from '../utils/log';
|
|
29
|
+
|
|
30
|
+
export const VIEW_PAN_SNAP_TIME = 0.5;
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
/** Optional camera construction policy, supplied only by the classic entry. */
|
|
34
|
+
export type CameraFactory = (name: string, view: CameraView, config: CameraConfiguration) => Camera;
|
|
35
|
+
|
|
36
|
+
/** Options owned by the renderer view rather than a specific host library. */
|
|
37
|
+
export interface ViewOptions {
|
|
38
|
+
cameraFactory?: CameraFactory;
|
|
39
|
+
cameraMode?: 'external' | 'scene';
|
|
40
|
+
externalCamera?: boolean;
|
|
41
|
+
visibilityAdapter?: VisibilityLODAdapter;
|
|
42
|
+
globeVisibilityAdapter?: GlobeVisibilityLODAdapter;
|
|
43
|
+
continuousZoom?: boolean;
|
|
44
|
+
wrapView?: boolean;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
interface ViewTile {
|
|
48
|
+
visible: boolean;
|
|
49
|
+
loading: boolean;
|
|
50
|
+
style_z: number;
|
|
51
|
+
preserve_tiles_within_zoom?: number;
|
|
52
|
+
coords: TileCoordinate;
|
|
53
|
+
key: string;
|
|
54
|
+
isProxy(): boolean;
|
|
55
|
+
setupProgram(matrices: MatrixSet & {model: Float64Array; model32: Float32Array}, program: ViewProgram, buffer?: UniformBuffer): void;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
interface ViewProgram extends Program {
|
|
59
|
+
uniform(type: string, name: string, value: number | boolean | number[] | Float32Array | Float64Array): void;
|
|
60
|
+
bindUniformBlocks(): void;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/** Scene services used by View; no dependency on the scene implementation. */
|
|
64
|
+
export interface ViewScene {
|
|
65
|
+
config: {cameras?: Record<string, CameraConfiguration & {active?: boolean}>} | null;
|
|
66
|
+
uniform_buffers?: Record<string, UniformBuffer>;
|
|
67
|
+
tile_manager: {
|
|
68
|
+
updateTilesForView(): void;
|
|
69
|
+
removeTiles(predicate: (tile: ViewTile) => boolean): void;
|
|
70
|
+
/** Report tiles pinned by the current bounded globe preload policy. */
|
|
71
|
+
isTilePreloaded?(key: string): boolean;
|
|
72
|
+
/** Install shared worker/cache limits before refreshing visibility. */
|
|
73
|
+
setResourceLimits?(options: Readonly<HostTileResourceOptions> | undefined): void;
|
|
74
|
+
} | null;
|
|
75
|
+
updateConfig(options: {rebuild: boolean; normalize: boolean}): unknown;
|
|
76
|
+
requestRedraw(): void;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
type ViewCenter = {lng: number; lat: number; meters?: Meters; tile?: Tile};
|
|
80
|
+
|
|
81
|
+
export default class View {
|
|
82
|
+
declare subscribe: SubscriptionMethods['subscribe'];
|
|
83
|
+
declare unsubscribe: SubscriptionMethods['unsubscribe'];
|
|
84
|
+
declare unsubscribeAll: SubscriptionMethods['unsubscribeAll'];
|
|
85
|
+
declare trigger: SubscriptionMethods['trigger'];
|
|
86
|
+
declare hasSubscribersFor: SubscriptionMethods['hasSubscribersFor'];
|
|
87
|
+
readonly scene: ViewScene;
|
|
88
|
+
/** Scene teardown retires its tile owner; view operations require the live manager. */
|
|
89
|
+
private get activeTileManager(): NonNullable<ViewScene['tile_manager']> {
|
|
90
|
+
if (!this.scene.tile_manager) throw new Error('View scene tile manager has been destroyed');
|
|
91
|
+
return this.scene.tile_manager;
|
|
92
|
+
}
|
|
93
|
+
readonly visibility_adapter: VisibilityLODAdapter;
|
|
94
|
+
readonly globe_visibility_adapter: GlobeVisibilityLODAdapter;
|
|
95
|
+
/** Shared traversal/bounds contract, independent of host viewport classes. */
|
|
96
|
+
readonly traversal_adapter: TangramTileTraversalAdapter;
|
|
97
|
+
private readonly cameraFactory?: CameraFactory;
|
|
98
|
+
private hostFrame: HostFrame | null = null;
|
|
99
|
+
private readonly projectedTileLOD = new ProjectedTileLOD();
|
|
100
|
+
private dataTileZoom?: number;
|
|
101
|
+
private visibilityKey = '';
|
|
102
|
+
private applyingFrame = false;
|
|
103
|
+
camera?: Camera;
|
|
104
|
+
zoom: number | null;
|
|
105
|
+
/** Integer style zoom retained for source display filters and worker geometry. */
|
|
106
|
+
tile_zoom: number | undefined;
|
|
107
|
+
center: ViewCenter | null;
|
|
108
|
+
bounds: Bounds | null;
|
|
109
|
+
meters_per_pixel: number | null;
|
|
110
|
+
size: {css: {width?: number; height?: number}; device: {width?: number; height?: number}; meters: Partial<Meters>};
|
|
111
|
+
aspect: number | null;
|
|
112
|
+
buffer: number;
|
|
113
|
+
projection: HostProjection;
|
|
114
|
+
camera_mode: 'external' | 'scene';
|
|
115
|
+
external_camera: boolean;
|
|
116
|
+
continuous_zoom: boolean;
|
|
117
|
+
wrap: boolean;
|
|
118
|
+
preserve_tiles_within_zoom: number;
|
|
119
|
+
panning: boolean;
|
|
120
|
+
panning_stop_at: number;
|
|
121
|
+
pan_snap_timer: number;
|
|
122
|
+
zoom_direction: number;
|
|
123
|
+
user_input_at: number;
|
|
124
|
+
user_input_timeout: number;
|
|
125
|
+
user_input_active: boolean;
|
|
126
|
+
matrices: MatrixSet & {model: Float64Array; model32: Float32Array; model_view: Float64Array; normal: Float64Array} = createViewMatrices();
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
constructor (scene: ViewScene, options: ViewOptions = {}) {
|
|
130
|
+
subscribeMixin(this);
|
|
131
|
+
|
|
132
|
+
this.scene = scene;
|
|
133
|
+
this.cameraFactory = options.cameraFactory;
|
|
134
|
+
this.visibility_adapter = options.visibilityAdapter || new WebMercatorVisibilityAdapter();
|
|
135
|
+
this.globe_visibility_adapter = options.globeVisibilityAdapter || new WebMercatorGlobeVisibilityAdapter();
|
|
136
|
+
this.traversal_adapter = new TangramTileTraversalAdapter(this.visibility_adapter, this.globe_visibility_adapter);
|
|
137
|
+
|
|
138
|
+
this.zoom = null;
|
|
139
|
+
this.center = null;
|
|
140
|
+
this.bounds = null;
|
|
141
|
+
this.meters_per_pixel = null;
|
|
142
|
+
|
|
143
|
+
this.panning = false;
|
|
144
|
+
this.panning_stop_at = 0;
|
|
145
|
+
this.pan_snap_timer = 0;
|
|
146
|
+
this.zoom_direction = 0;
|
|
147
|
+
|
|
148
|
+
this.user_input_at = 0;
|
|
149
|
+
this.user_input_timeout = 50;
|
|
150
|
+
this.user_input_active = false;
|
|
151
|
+
|
|
152
|
+
// Size of viewport in CSS pixels, device pixels, and mercator meters
|
|
153
|
+
this.size = {
|
|
154
|
+
css: {},
|
|
155
|
+
device: {},
|
|
156
|
+
meters: {}
|
|
157
|
+
};
|
|
158
|
+
this.aspect = null;
|
|
159
|
+
|
|
160
|
+
this.buffer = 0;
|
|
161
|
+
this.projection = { type: 'web-mercator' };
|
|
162
|
+
// Host-driven renderers own camera projection. `externalCamera` remains
|
|
163
|
+
// as a compatibility alias while callers migrate to the explicit mode.
|
|
164
|
+
this.camera_mode = options.cameraMode || (options.externalCamera === true || !options.cameraFactory ? 'external' : 'scene');
|
|
165
|
+
this.external_camera = this.camera_mode === 'external';
|
|
166
|
+
this.continuous_zoom = (typeof options.continuousZoom === 'boolean') ? options.continuousZoom : true;
|
|
167
|
+
this.wrap = (options.wrapView === false) ? false : true;
|
|
168
|
+
this.preserve_tiles_within_zoom = 1;
|
|
169
|
+
|
|
170
|
+
this.reset();
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
/** Installs every host field before making one visibility update. */
|
|
174
|
+
applyHostFrame(frame: HostFrame, resize: () => void, setCamera: () => void): void {
|
|
175
|
+
const eyeStates = frame.tileLOD ? frame.renderViews.map(eye => {
|
|
176
|
+
const state = this.getHostEyeState(frame, eye, Math.floor(frame.geographicAnchor.zoom));
|
|
177
|
+
const projection = eye.projection ?? frame.projection;
|
|
178
|
+
const bounds = getProjectionBounds(projection, this.visibility_adapter.calculateBounds(state).bounds);
|
|
179
|
+
return {eye, state: {...state, bounds}, camera: eye.camera, viewport: eye.viewport, projection, bounds};
|
|
180
|
+
}) : [];
|
|
181
|
+
const dataTileZoom = this.projectedTileLOD.select(frame, eyeStates, zoom => {
|
|
182
|
+
let count = 0;
|
|
183
|
+
for (const {eye, projection} of eyeStates) {
|
|
184
|
+
if (projection.type === 'globe') {
|
|
185
|
+
if (!this.globe_visibility_adapter.countTileCoordinates) {
|
|
186
|
+
throw new Error('Automatic tile LOD requires a globe visibility candidate counter');
|
|
187
|
+
}
|
|
188
|
+
count += this.globe_visibility_adapter.countTileCoordinates({
|
|
189
|
+
tile_zoom: zoom, buffer: frame.tileBuffer, visibleBounds: projection.visibleBounds,
|
|
190
|
+
cameraPosition: eye.camera.position, maxElevation: projection.maxElevation
|
|
191
|
+
});
|
|
192
|
+
}
|
|
193
|
+
else {
|
|
194
|
+
if (!this.visibility_adapter.countTileCoordinates) {
|
|
195
|
+
throw new Error('Automatic tile LOD requires a planar visibility candidate counter');
|
|
196
|
+
}
|
|
197
|
+
const state = this.getHostEyeState(frame, eye, zoom);
|
|
198
|
+
const bounds = getProjectionBounds(projection, this.visibility_adapter.calculateBounds(state).bounds);
|
|
199
|
+
count += this.visibility_adapter.countTileCoordinates({...state, bounds});
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
return count;
|
|
203
|
+
});
|
|
204
|
+
const key = JSON.stringify({
|
|
205
|
+
anchor: frame.geographicAnchor, projection: frame.projection, buffer: frame.tileBuffer, tileZoom: dataTileZoom,
|
|
206
|
+
tileLOD: frame.tileLOD, tileResources: frame.tileResources, globePreloadZoom: frame.globePreloadZoom,
|
|
207
|
+
views: frame.renderViews.map(view => ({
|
|
208
|
+
viewport: view.viewport, anchor: view.geographicAnchor, projection: view.projection,
|
|
209
|
+
camera: {view: Array.from(view.camera.view), projection: Array.from(view.camera.projection), position: view.camera.position}
|
|
210
|
+
}))
|
|
211
|
+
});
|
|
212
|
+
const changed = key !== this.visibilityKey;
|
|
213
|
+
this.hostFrame = frame;
|
|
214
|
+
this.dataTileZoom = dataTileZoom;
|
|
215
|
+
this.applyingFrame = true;
|
|
216
|
+
try {
|
|
217
|
+
this.activeTileManager.setResourceLimits?.(frame.tileResources);
|
|
218
|
+
this.buffer = frame.tileBuffer;
|
|
219
|
+
this.setProjection(frame.projection);
|
|
220
|
+
resize();
|
|
221
|
+
this.setView({lng: frame.geographicAnchor.longitude, lat: frame.geographicAnchor.latitude, zoom: frame.geographicAnchor.zoom});
|
|
222
|
+
setCamera();
|
|
223
|
+
}
|
|
224
|
+
finally {
|
|
225
|
+
this.applyingFrame = false;
|
|
226
|
+
}
|
|
227
|
+
this.visibilityKey = key;
|
|
228
|
+
if (changed) {
|
|
229
|
+
this.updateBounds();
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
private invalidateBounds(previousTileZoom = this.tile_zoom): void {
|
|
234
|
+
if (!this.applyingFrame) {
|
|
235
|
+
this.updateBounds(previousTileZoom);
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
private getVisibilityState(): VisibilityViewState | null {
|
|
240
|
+
if (!this.center || this.zoom === null || this.size.css.width === undefined || this.size.css.height === undefined) {
|
|
241
|
+
return null;
|
|
242
|
+
}
|
|
243
|
+
return {
|
|
244
|
+
center: this.center, zoom: this.zoom,
|
|
245
|
+
tile_zoom: this.dataTileZoom ?? this.tile_zoom ?? this.baseZoom(this.zoom),
|
|
246
|
+
size: {css: {width: this.size.css.width, height: this.size.css.height}},
|
|
247
|
+
bounds: this.bounds, buffer: this.buffer, wrap: this.wrap
|
|
248
|
+
};
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
/** Derives an eye's geographic state without mutating the installed logical frame. */
|
|
252
|
+
private getHostEyeState(frame: HostFrame, eye: NormalizedRenderView, tileZoom: number): VisibilityViewState {
|
|
253
|
+
const anchor = eye.geographicAnchor ?? frame.geographicAnchor;
|
|
254
|
+
const projection = eye.projection ?? frame.projection;
|
|
255
|
+
return {
|
|
256
|
+
center: {lng: anchor.longitude, lat: anchor.latitude}, zoom: anchor.zoom, tile_zoom: tileZoom,
|
|
257
|
+
size: {css: eye.viewport}, bounds: null, buffer: frame.tileBuffer, wrap: this.wrap,
|
|
258
|
+
camera: projection.type === 'web-mercator' ? eye.camera : undefined
|
|
259
|
+
};
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
private getReadyState() {
|
|
263
|
+
if (!this.center?.meters || this.zoom === null || this.meters_per_pixel === null || this.aspect === null ||
|
|
264
|
+
this.size.css.width === undefined || this.size.css.height === undefined ||
|
|
265
|
+
this.size.meters.x === undefined || this.size.meters.y === undefined) {
|
|
266
|
+
throw new Error('View camera state is not ready');
|
|
267
|
+
}
|
|
268
|
+
return {
|
|
269
|
+
centerMeters: this.center.meters, zoom: this.zoom, metersPerPixel: this.meters_per_pixel,
|
|
270
|
+
aspect: this.aspect, width: this.size.css.width, height: this.size.css.height,
|
|
271
|
+
sizeMeters: {x: this.size.meters.x, y: this.size.meters.y}
|
|
272
|
+
};
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
private getCameraView(): CameraView {
|
|
276
|
+
const view = this;
|
|
277
|
+
return {
|
|
278
|
+
scene: this.scene,
|
|
279
|
+
setView: state => this.setView(state),
|
|
280
|
+
get projection() { return view.projection; },
|
|
281
|
+
get size() {
|
|
282
|
+
const state = view.getReadyState();
|
|
283
|
+
return {css: {width: state.width, height: state.height}, meters: state.sizeMeters};
|
|
284
|
+
},
|
|
285
|
+
get aspect() { return view.getReadyState().aspect; },
|
|
286
|
+
get zoom() { return view.getReadyState().zoom; },
|
|
287
|
+
get meters_per_pixel() { return view.getReadyState().metersPerPixel; },
|
|
288
|
+
get center() { return {meters: view.getReadyState().centerMeters}; }
|
|
289
|
+
};
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
// Reset state before scene config is updated
|
|
293
|
+
reset () {
|
|
294
|
+
this.createCamera();
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
// Create camera
|
|
298
|
+
createCamera () {
|
|
299
|
+
if (this.external_camera) {
|
|
300
|
+
this.camera = new ExternalCamera('external', this.getCameraView(), { type: 'external' });
|
|
301
|
+
return;
|
|
302
|
+
}
|
|
303
|
+
let active_camera = this.getActiveCamera();
|
|
304
|
+
if (active_camera && this.cameraFactory && this.scene.config?.cameras) {
|
|
305
|
+
this.camera = this.cameraFactory(active_camera, this.getCameraView(), this.scene.config.cameras[active_camera]);
|
|
306
|
+
this.camera.updateView();
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
// Supply camera matrices from an embedding renderer
|
|
311
|
+
setCameraMatrices (matrices: HostCamera) {
|
|
312
|
+
if (!this.camera || !(this.camera instanceof ExternalCamera)) {
|
|
313
|
+
throw new Error('View must use cameraMode \'external\' to accept camera matrices');
|
|
314
|
+
}
|
|
315
|
+
if (this.camera.setMatrices(matrices)) {
|
|
316
|
+
this.invalidateBounds();
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
// Get active camera - for public API
|
|
321
|
+
getActiveCamera () {
|
|
322
|
+
if (this.scene.config && this.scene.config.cameras) {
|
|
323
|
+
for (let name in this.scene.config.cameras) {
|
|
324
|
+
if (this.scene.config.cameras[name].active) {
|
|
325
|
+
return name;
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
// If no camera set as active, use first one
|
|
330
|
+
let keys = Object.keys(this.scene.config.cameras);
|
|
331
|
+
return keys[0];
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
// Set active camera and recompile - for public API
|
|
336
|
+
setActiveCamera (name: string) {
|
|
337
|
+
let prev = this.getActiveCamera();
|
|
338
|
+
if (prev === name) {
|
|
339
|
+
return name;
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
if (this.scene.config?.cameras?.[name]) {
|
|
343
|
+
this.scene.config.cameras[name].active = true;
|
|
344
|
+
|
|
345
|
+
// Clear previously active camera
|
|
346
|
+
if (prev && this.scene.config.cameras[prev]) {
|
|
347
|
+
delete this.scene.config.cameras[prev].active;
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
this.scene.updateConfig({ rebuild: false, normalize: false });
|
|
352
|
+
return this.getActiveCamera();
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
// Update method called once per frame
|
|
356
|
+
update (time = Date.now()) {
|
|
357
|
+
if (this.camera != null && this.ready()) {
|
|
358
|
+
this.camera.update();
|
|
359
|
+
}
|
|
360
|
+
this.pan_snap_timer = (time - this.panning_stop_at) / 1000;
|
|
361
|
+
this.user_input_active = ((time - this.user_input_at) < this.user_input_timeout);
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
// Set logical pixel size of viewport
|
|
365
|
+
setViewportSize (width: number, height: number) {
|
|
366
|
+
this.size.css = { width, height };
|
|
367
|
+
this.size.device = {
|
|
368
|
+
width: Math.round(width * (Utils.device_pixel_ratio ?? 1)),
|
|
369
|
+
height: Math.round(height * (Utils.device_pixel_ratio ?? 1))
|
|
370
|
+
};
|
|
371
|
+
this.aspect = width / height;
|
|
372
|
+
this.invalidateBounds();
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
// Set the host projection and its geographic visibility metadata.
|
|
376
|
+
setProjection (projection: HostProjection = { type: 'web-mercator' }) {
|
|
377
|
+
if (projectionsEqual(this.projection, projection)) {
|
|
378
|
+
return false;
|
|
379
|
+
}
|
|
380
|
+
this.projection = projection;
|
|
381
|
+
this.invalidateBounds();
|
|
382
|
+
return true;
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
// Set the map view, can be passed an object with lat/lng and/or zoom
|
|
386
|
+
setView ({ lng, lat, zoom }: {lng?: number; lat?: number; zoom?: number} = {}) {
|
|
387
|
+
var changed = false;
|
|
388
|
+
|
|
389
|
+
// Set center
|
|
390
|
+
if (typeof lng === 'number' && typeof lat === 'number') {
|
|
391
|
+
if (!this.center || lng !== this.center.lng || lat !== this.center.lat) {
|
|
392
|
+
changed = true;
|
|
393
|
+
this.center = { lng, lat };
|
|
394
|
+
}
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
// Set zoom
|
|
398
|
+
if (typeof zoom === 'number' && zoom !== this.zoom) {
|
|
399
|
+
changed = true;
|
|
400
|
+
this.setZoom(zoom);
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
if (changed) {
|
|
404
|
+
this.invalidateBounds();
|
|
405
|
+
}
|
|
406
|
+
return changed;
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
setZoom (zoom: number) {
|
|
410
|
+
let last_tile_zoom = this.tile_zoom;
|
|
411
|
+
let tile_zoom = this.baseZoom(zoom);
|
|
412
|
+
if (!this.continuous_zoom) {
|
|
413
|
+
zoom = tile_zoom;
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
if (tile_zoom !== last_tile_zoom) {
|
|
417
|
+
this.zoom_direction = typeof last_tile_zoom === 'number' && tile_zoom > last_tile_zoom ? 1 : -1;
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
this.zoom = zoom;
|
|
421
|
+
this.tile_zoom = tile_zoom;
|
|
422
|
+
|
|
423
|
+
this.invalidateBounds(last_tile_zoom);
|
|
424
|
+
this.scene.requestRedraw();
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
// Choose the base zoom level to use for a given fractional zoom
|
|
428
|
+
baseZoom (zoom: number) {
|
|
429
|
+
return Math.floor(zoom);
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
setPanning (panning: boolean) {
|
|
433
|
+
this.panning = panning;
|
|
434
|
+
if (!this.panning) {
|
|
435
|
+
this.panning_stop_at = (+new Date());
|
|
436
|
+
}
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
markUserInput () {
|
|
440
|
+
this.user_input_at = (+new Date());
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
ready () {
|
|
444
|
+
// TODO: better concept of 'readiness' state?
|
|
445
|
+
if (typeof this.size.css.width !== 'number' ||
|
|
446
|
+
typeof this.size.css.height !== 'number' ||
|
|
447
|
+
this.center == null ||
|
|
448
|
+
typeof this.zoom !== 'number') {
|
|
449
|
+
return false;
|
|
450
|
+
}
|
|
451
|
+
return true;
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
// Calculate viewport bounds based on current center and zoom
|
|
455
|
+
updateBounds (previousTileZoom = this.tile_zoom) {
|
|
456
|
+
const state = this.getVisibilityState();
|
|
457
|
+
if (!state || !this.center) {
|
|
458
|
+
return;
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
const viewBounds = this.visibility_adapter.calculateBounds(state);
|
|
462
|
+
this.tile_zoom = this.dataTileZoom === undefined ? viewBounds.tileZoom : this.baseZoom(state.zoom);
|
|
463
|
+
if (typeof previousTileZoom === 'number' && this.tile_zoom !== previousTileZoom) {
|
|
464
|
+
this.zoom_direction = this.tile_zoom > previousTileZoom ? 1 : -1;
|
|
465
|
+
}
|
|
466
|
+
this.meters_per_pixel = viewBounds.metersPerPixel;
|
|
467
|
+
this.size.meters = viewBounds.sizeMeters;
|
|
468
|
+
this.center.meters = viewBounds.centerMeters;
|
|
469
|
+
this.center.tile = this.dataTileZoom === undefined ? viewBounds.centerTile :
|
|
470
|
+
Geo.tileForMeters([viewBounds.centerMeters.x, viewBounds.centerMeters.y], this.dataTileZoom);
|
|
471
|
+
this.bounds = viewBounds.bounds;
|
|
472
|
+
|
|
473
|
+
this.activeTileManager.updateTilesForView();
|
|
474
|
+
|
|
475
|
+
this.trigger('move');
|
|
476
|
+
this.scene.requestRedraw(); // TODO automate via move event?
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
findVisibleTileCoordinates (): TileCoordinate[] {
|
|
480
|
+
return this.traversal_adapter.getTileIndices({viewState: this.getTileTraversalState()});
|
|
481
|
+
}
|
|
482
|
+
|
|
483
|
+
/** Snapshot the logical/eye traversal inputs without exposing a View or Scene to a tileset. */
|
|
484
|
+
getTileTraversalState(): TangramTraversalState {
|
|
485
|
+
if (!this.bounds) {
|
|
486
|
+
return {eyes: []};
|
|
487
|
+
}
|
|
488
|
+
|
|
489
|
+
|
|
490
|
+
const state = this.getVisibilityState();
|
|
491
|
+
if (!state) {
|
|
492
|
+
return {eyes: []};
|
|
493
|
+
}
|
|
494
|
+
const hostFrame = this.hostFrame;
|
|
495
|
+
const views = hostFrame?.renderViews;
|
|
496
|
+
if (!views) {
|
|
497
|
+
return {eyes: [{view: state, projection: this.projection, position: this.camera?.position_meters}]};
|
|
498
|
+
}
|
|
499
|
+
return {union: true, eyes: views.map(eye => ({
|
|
500
|
+
view: this.getHostEyeState(hostFrame, eye,
|
|
501
|
+
this.dataTileZoom ?? this.baseZoom((eye.geographicAnchor ?? hostFrame.geographicAnchor).zoom)),
|
|
502
|
+
projection: eye.projection ?? this.projection, position: eye.camera.position,
|
|
503
|
+
calculateBounds: true, dataZoom: this.dataTileZoom
|
|
504
|
+
}))};
|
|
505
|
+
}
|
|
506
|
+
|
|
507
|
+
/** Global coarse coordinates at no finer a data level than the current view. */
|
|
508
|
+
findPreloadedTileCoordinates(): TileCoordinate[] {
|
|
509
|
+
const zoom = this.hostFrame?.globePreloadZoom;
|
|
510
|
+
if (this.projection.type !== 'globe' || zoom === undefined || !this.center?.tile) return [];
|
|
511
|
+
return createGlobePreloadCoordinates(Math.min(zoom, this.center.tile.z));
|
|
512
|
+
}
|
|
513
|
+
|
|
514
|
+
// Remove tiles too far outside of view
|
|
515
|
+
pruneTilesForView () {
|
|
516
|
+
// A projected footprint is a finite source region, not the geographic camera's rectangle.
|
|
517
|
+
// Keep its loaded tiles warm across projection switches; configured cache budgets still apply.
|
|
518
|
+
if (this.projection.type === 'projected') return;
|
|
519
|
+
// TODO: will this function ever be called when view isn't ready?
|
|
520
|
+
if (!this.ready() || !this.center?.meters || this.meters_per_pixel === null ||
|
|
521
|
+
this.size.meters.x === undefined || this.size.meters.y === undefined || this.tile_zoom === undefined) {
|
|
522
|
+
return;
|
|
523
|
+
}
|
|
524
|
+
const centerMeters = this.center.meters;
|
|
525
|
+
const sizeMeters = {x: this.size.meters.x, y: this.size.meters.y};
|
|
526
|
+
const metersPerPixel = this.meters_per_pixel;
|
|
527
|
+
const tileZoom = this.tile_zoom;
|
|
528
|
+
|
|
529
|
+
this.activeTileManager.removeTiles(tile => {
|
|
530
|
+
// Ignore visible tiles
|
|
531
|
+
if (tile.visible || tile.isProxy() || this.activeTileManager.isTilePreloaded?.(tile.key)) {
|
|
532
|
+
return false;
|
|
533
|
+
}
|
|
534
|
+
|
|
535
|
+
// Remove tiles outside given zoom that are still loading
|
|
536
|
+
if (tile.loading && tile.style_z !== this.tile_zoom) {
|
|
537
|
+
return true;
|
|
538
|
+
}
|
|
539
|
+
|
|
540
|
+
// Discard if too far from current zoom
|
|
541
|
+
const zdiff = Math.abs(tile.style_z - tileZoom);
|
|
542
|
+
const preserve_tiles_within_zoom = (tile.preserve_tiles_within_zoom != null ?
|
|
543
|
+
tile.preserve_tiles_within_zoom : this.preserve_tiles_within_zoom); // optionally tile source specific
|
|
544
|
+
if (zdiff > preserve_tiles_within_zoom) {
|
|
545
|
+
return true;
|
|
546
|
+
}
|
|
547
|
+
|
|
548
|
+
if (this.projection.type === 'globe') {
|
|
549
|
+
return true;
|
|
550
|
+
}
|
|
551
|
+
|
|
552
|
+
// Discard tiles outside an area surrounding the viewport, handling tiles at different zooms
|
|
553
|
+
// Get min and max tiles for the viewport, at the scale of the tile currently being evaluated
|
|
554
|
+
const view_buffer = metersPerPixel * Geo.tile_size; // buffer area to keep tiles surrounding viewport
|
|
555
|
+
const view_tile_min = TileID.coordAtZoom(
|
|
556
|
+
Geo.tileForMeters(
|
|
557
|
+
[
|
|
558
|
+
centerMeters.x - sizeMeters.x/2 - view_buffer,
|
|
559
|
+
centerMeters.y + sizeMeters.y/2 + view_buffer
|
|
560
|
+
],
|
|
561
|
+
tileZoom),
|
|
562
|
+
tile.coords.z);
|
|
563
|
+
const view_tile_max = TileID.coordAtZoom(
|
|
564
|
+
Geo.tileForMeters(
|
|
565
|
+
[
|
|
566
|
+
centerMeters.x + sizeMeters.x/2 + view_buffer,
|
|
567
|
+
centerMeters.y - sizeMeters.y/2 - view_buffer
|
|
568
|
+
],
|
|
569
|
+
tileZoom),
|
|
570
|
+
tile.coords.z);
|
|
571
|
+
|
|
572
|
+
if (tile.coords.x < view_tile_min.x || tile.coords.x > view_tile_max.x ||
|
|
573
|
+
tile.coords.y < view_tile_min.y || tile.coords.y > view_tile_max.y) {
|
|
574
|
+
log('trace', `View: remove tile ${tile.key} (as ${tile.coords.key}) ` +
|
|
575
|
+
`for being too far out of visible area (${view_tile_min.key}, ${view_tile_max.key})`);
|
|
576
|
+
return true;
|
|
577
|
+
}
|
|
578
|
+
return false;
|
|
579
|
+
});
|
|
580
|
+
}
|
|
581
|
+
|
|
582
|
+
// Allocate model-view matrices
|
|
583
|
+
// 64-bit versions are for CPU calcuations
|
|
584
|
+
// 32-bit versions are downsampled and sent to GPU
|
|
585
|
+
createMatrices () {
|
|
586
|
+
|
|
587
|
+
this.matrices = createViewMatrices();
|
|
588
|
+
}
|
|
589
|
+
|
|
590
|
+
// Calculate and set model/view and normal matrices for a tile
|
|
591
|
+
setupTile (tile: ViewTile, program: ViewProgram, clipBounds?: readonly number[]) {
|
|
592
|
+
const uniform_buffer = this.scene.uniform_buffers && this.scene.uniform_buffers.TangramTile;
|
|
593
|
+
|
|
594
|
+
// Tile-specific state
|
|
595
|
+
// TODO: calc these once per tile (currently being needlessly re-calculated per-tile-per-style)
|
|
596
|
+
tile.setupProgram(this.matrices, program, uniform_buffer);
|
|
597
|
+
const bounds = clipBounds ? Array.from(clipBounds) : [0, 0, 0, 0];
|
|
598
|
+
if (uniform_buffer) uniform_buffer.setUniforms({u_tile_clip_bounds: bounds});
|
|
599
|
+
else program.uniform('4fv', 'u_tile_clip_bounds', bounds);
|
|
600
|
+
|
|
601
|
+
// Model-view and normal matrices
|
|
602
|
+
this.camera?.setupMatrices(this.matrices, program, uniform_buffer);
|
|
603
|
+
if (uniform_buffer) {
|
|
604
|
+
program.bindUniformBlocks();
|
|
605
|
+
}
|
|
606
|
+
}
|
|
607
|
+
|
|
608
|
+
// Set general uniforms that must be updated once per program
|
|
609
|
+
setupProgram (program: ViewProgram, uniform_buffers: Record<string, UniformBuffer> = {}) {
|
|
610
|
+
const {centerMeters, zoom, metersPerPixel} = this.getReadyState();
|
|
611
|
+
const width = this.size.device.width ?? 0;
|
|
612
|
+
const height = this.size.device.height ?? 0;
|
|
613
|
+
if (uniform_buffers.TangramView) {
|
|
614
|
+
uniform_buffers.TangramView.setUniforms({
|
|
615
|
+
u_resolution: [width, height],
|
|
616
|
+
u_map_position: [centerMeters.x, centerMeters.y, zoom],
|
|
617
|
+
u_meters_per_pixel: metersPerPixel,
|
|
618
|
+
u_device_pixel_ratio: Utils.device_pixel_ratio ?? 1,
|
|
619
|
+
u_view_pan_snap_timer: this.pan_snap_timer,
|
|
620
|
+
u_view_panning: this.panning,
|
|
621
|
+
u_projection_mode: this.projection.type === 'globe' ? 1 : this.projection.type === 'projected' ? 2 : 0
|
|
622
|
+
});
|
|
623
|
+
}
|
|
624
|
+
else {
|
|
625
|
+
program.uniform('2fv', 'u_resolution', [width, height]);
|
|
626
|
+
program.uniform('3fv', 'u_map_position', [centerMeters.x, centerMeters.y, zoom]);
|
|
627
|
+
program.uniform('1f', 'u_meters_per_pixel', metersPerPixel);
|
|
628
|
+
program.uniform('1f', 'u_device_pixel_ratio', Utils.device_pixel_ratio ?? 1);
|
|
629
|
+
program.uniform('1f', 'u_view_pan_snap_timer', this.pan_snap_timer);
|
|
630
|
+
program.uniform('1i', 'u_view_panning', this.panning);
|
|
631
|
+
program.uniform('1i', 'u_projection_mode', this.projection.type === 'globe' ? 1 : this.projection.type === 'projected' ? 2 : 0);
|
|
632
|
+
}
|
|
633
|
+
|
|
634
|
+
this.camera?.setupProgram(program, uniform_buffers.TangramCamera);
|
|
635
|
+
}
|
|
636
|
+
|
|
637
|
+
// View requires some animation, such as after panning stops
|
|
638
|
+
isAnimating () {
|
|
639
|
+
return (this.pan_snap_timer <= VIEW_PAN_SNAP_TIME);
|
|
640
|
+
}
|
|
641
|
+
|
|
642
|
+
}
|
|
643
|
+
|
|
644
|
+
/** Projects explicit geographic footprints, unwrapping globe samples across the antimeridian. */
|
|
645
|
+
function getProjectionBounds(projection: HostProjection, fallback: Bounds): Bounds | null {
|
|
646
|
+
if (projection.visibleBounds === undefined) return fallback;
|
|
647
|
+
if (projection.visibleBounds === null) return null;
|
|
648
|
+
const [west, south, east, north] = projection.visibleBounds;
|
|
649
|
+
const clampLatitude = (latitude: number) => projection.type === 'globe'
|
|
650
|
+
? Math.max(-85.05112878, Math.min(85.05112878, latitude)) : latitude;
|
|
651
|
+
const southwest = Geo.latLngToMeters([west, clampLatitude(south)]);
|
|
652
|
+
const northeast = Geo.latLngToMeters([projection.type === 'globe' && east < west ? east + 360 : east, clampLatitude(north)]);
|
|
653
|
+
if (![...southwest, ...northeast].every(Number.isFinite)) {
|
|
654
|
+
throw new Error('HostFrame visibleBounds must project to finite meters');
|
|
655
|
+
}
|
|
656
|
+
return {sw: {x: southwest[0], y: southwest[1]}, ne: {x: northeast[0], y: northeast[1]}};
|
|
657
|
+
}
|
|
658
|
+
|
|
659
|
+
function projectionsEqual(previous: HostProjection, next: HostProjection): boolean {
|
|
660
|
+
if (previous === next) {
|
|
661
|
+
return true;
|
|
662
|
+
}
|
|
663
|
+
if (!previous || !next || previous.type !== next.type) {
|
|
664
|
+
return false;
|
|
665
|
+
}
|
|
666
|
+
const previousBounds = previous.visibleBounds;
|
|
667
|
+
const nextBounds = next.visibleBounds;
|
|
668
|
+
const heightMatches = previous.type !== 'globe' || next.type !== 'globe' ||
|
|
669
|
+
previous.maxElevation === next.maxElevation;
|
|
670
|
+
return heightMatches && (previousBounds === nextBounds || (
|
|
671
|
+
Array.isArray(previousBounds) && Array.isArray(nextBounds) &&
|
|
672
|
+
previousBounds.length === nextBounds.length &&
|
|
673
|
+
previousBounds.every((value, index) => value === nextBounds[index])));
|
|
674
|
+
}
|
|
675
|
+
|
|
676
|
+
/** Allocates the CPU and GPU matrix views used for one tile's transforms. */
|
|
677
|
+
function createViewMatrices() {
|
|
678
|
+
return {
|
|
679
|
+
model: new Float64Array(16), model32: new Float32Array(16),
|
|
680
|
+
model_view: new Float64Array(16), model_view32: new Float32Array(16),
|
|
681
|
+
normal: new Float64Array(9), normal32: new Float32Array(9), inverse_normal32: new Float32Array(9)
|
|
682
|
+
};
|
|
683
|
+
}
|