@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,1079 @@
|
|
|
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
|
+
// Point + text label rendering style
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
import log from '../../utils/log';
|
|
10
|
+
import {Style} from '../style';
|
|
11
|
+
import type {PointStyleRuntime, PointMethods, PointState, PointFeatureStyle, PointCandidate, AttachedTextQueue} from './point-runtime-types';
|
|
12
|
+
import type {ParsedTextFeature} from '../text/text-types';
|
|
13
|
+
import type {LabelPointCoordinate} from '../../labels/label-types';
|
|
14
|
+
import type {StyleState} from '../style-runtime-types';
|
|
15
|
+
import StyleParser from '../style_parser';
|
|
16
|
+
import gl from '../../gl/constants'; // web workers don't have access to GL context, so import all GL constants
|
|
17
|
+
import VertexLayout from '../../gl/vertex_layout';
|
|
18
|
+
import {PACKED_HEIGHT_SCALE, packProjectedAnnotationHeight} from '../../gl/vertex-constants';
|
|
19
|
+
import { buildQuadForPoint } from '../../builders/points';
|
|
20
|
+
import Texture from '../../gl/texture';
|
|
21
|
+
import Geo from '../../utils/geo';
|
|
22
|
+
import Collision from '../../labels/collision';
|
|
23
|
+
import LabelPoint from '../../labels/label_point';
|
|
24
|
+
import placePointsOnLine from '../../labels/point_placement';
|
|
25
|
+
import {TextLabels} from '../text/text_labels';
|
|
26
|
+
import {VIEW_PAN_SNAP_TIME} from '../../scene/view';
|
|
27
|
+
import debugSettings from '../../utils/debug_settings';
|
|
28
|
+
|
|
29
|
+
import points_vs from './points_vertex.glsl';
|
|
30
|
+
import points_fs from './points_fragment.glsl';
|
|
31
|
+
import {buildPointsWGSL} from './points_wgsl';
|
|
32
|
+
import {GLOBE_PROJECTION_GLSL, TERRAIN_DRAPING_GLSL} from '../../scene/projection_shaders';
|
|
33
|
+
import {GLOBE_VISIBILITY_GLSL} from '../globe_visibility_wgsl';
|
|
34
|
+
|
|
35
|
+
const PLACEMENT = LabelPoint.PLACEMENT;
|
|
36
|
+
|
|
37
|
+
export const Points: PointStyleRuntime = Object.create(Style);
|
|
38
|
+
|
|
39
|
+
const SHADER_POINT_VARIANT = '__shader_point';
|
|
40
|
+
|
|
41
|
+
// texture types
|
|
42
|
+
const TANGRAM_POINT_TYPE_TEXTURE = 1; // style texture/sprites (assigned by user)
|
|
43
|
+
const TANGRAM_POINT_TYPE_LABEL = 2; // labels (generated by rendering labels to canvas)
|
|
44
|
+
const TANGRAM_POINT_TYPE_SHADER = 3; // point (drawn in shader)
|
|
45
|
+
|
|
46
|
+
// default point size in pixels
|
|
47
|
+
const DEFAULT_POINT_SIZE = 16;
|
|
48
|
+
|
|
49
|
+
// Mixin text label methods
|
|
50
|
+
Object.assign(Points, TextLabels);
|
|
51
|
+
|
|
52
|
+
const pointMethods: PointMethods & Partial<PointState> & Partial<StyleState> & {built_in: boolean; collision: boolean} & ThisType<PointStyleRuntime> = {
|
|
53
|
+
name: 'points',
|
|
54
|
+
built_in: true,
|
|
55
|
+
vertex_shader_src: points_vs.replace('#pragma tangram: projection', GLOBE_PROJECTION_GLSL + TERRAIN_DRAPING_GLSL)
|
|
56
|
+
.replace('#pragma tangram: globe_visibility', GLOBE_VISIBILITY_GLSL),
|
|
57
|
+
fragment_shader_src: points_fs,
|
|
58
|
+
selection: true, // enable feature selection
|
|
59
|
+
collision: true, // style includes a collision pass
|
|
60
|
+
blend: 'overlay', // overlays drawn on top of all other styles, with blending
|
|
61
|
+
|
|
62
|
+
getWGSLShaderSource(selection = false) {
|
|
63
|
+
return buildPointsWGSL(Boolean(this.cpu_projection), selection, Boolean(this.terrain));
|
|
64
|
+
},
|
|
65
|
+
|
|
66
|
+
init(options = {}) {
|
|
67
|
+
Style.init.call(this, options);
|
|
68
|
+
|
|
69
|
+
// Shader defines
|
|
70
|
+
this.setupDefines();
|
|
71
|
+
|
|
72
|
+
// Include code for SDF-drawn shader points
|
|
73
|
+
this.defines.TANGRAM_HAS_SHADER_POINTS = true;
|
|
74
|
+
|
|
75
|
+
// texture types
|
|
76
|
+
this.defines.TANGRAM_POINT_TYPE_TEXTURE = TANGRAM_POINT_TYPE_TEXTURE;
|
|
77
|
+
this.defines.TANGRAM_POINT_TYPE_LABEL = TANGRAM_POINT_TYPE_LABEL;
|
|
78
|
+
this.defines.TANGRAM_POINT_TYPE_SHADER = TANGRAM_POINT_TYPE_SHADER;
|
|
79
|
+
|
|
80
|
+
this.collision_group_points = this.name+'-points';
|
|
81
|
+
this.collision_group_text = this.name+'-text';
|
|
82
|
+
|
|
83
|
+
// Stenciling proxy tiles (to avoid compounding alpha artifacts) doesn't work well with
|
|
84
|
+
// points/text labels, which have pure transparent pixels that interfere with the stencil buffer,
|
|
85
|
+
// causing a "cut-out"/"x-ray" effect (preventing pixels that would usually be covered by proxy tiles
|
|
86
|
+
// underneath from being rendered).
|
|
87
|
+
this.stencil_proxy_tiles = false;
|
|
88
|
+
|
|
89
|
+
this.reset();
|
|
90
|
+
},
|
|
91
|
+
|
|
92
|
+
// Setup defines common to points base and child (text) styles
|
|
93
|
+
setupDefines () {
|
|
94
|
+
this.defines.TANGRAM_CPU_PROJECTED = Boolean(this.cpu_projection);
|
|
95
|
+
// If we're not rendering as overlay, we need a layer attribute
|
|
96
|
+
if (this.blend !== 'overlay') {
|
|
97
|
+
this.defines.TANGRAM_LAYER_ORDER = true;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
// Fade in labels
|
|
101
|
+
if (debugSettings.suppress_label_fade_in === true) {
|
|
102
|
+
this.fade_in_time = 0;
|
|
103
|
+
this.defines.TANGRAM_FADE_IN_RATE = null;
|
|
104
|
+
}
|
|
105
|
+
else {
|
|
106
|
+
this.fade_in_time = 0.15; // time in seconds
|
|
107
|
+
this.defines.TANGRAM_FADE_IN_RATE = 1 / this.fade_in_time;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
// Snap points to pixel grid after panning stop
|
|
111
|
+
if (debugSettings.suppress_label_snap_animation !== true) {
|
|
112
|
+
this.defines.TANGRAM_VIEW_PAN_SNAP_RATE = 1 / VIEW_PAN_SNAP_TIME; // inverse time in seconds
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
// Show hidden labels for debugging
|
|
116
|
+
if (debugSettings.show_hidden_labels === true) {
|
|
117
|
+
this.defines.TANGRAM_SHOW_HIDDEN_LABELS = true;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
// Enable wireframe for debugging
|
|
121
|
+
if (debugSettings.wireframe === true) {
|
|
122
|
+
this.defines.TANGRAM_WIREFRAME = true;
|
|
123
|
+
}
|
|
124
|
+
},
|
|
125
|
+
|
|
126
|
+
reset () {
|
|
127
|
+
this.queues = {};
|
|
128
|
+
this.resetText();
|
|
129
|
+
this.texture_missing_sprites = {}; // track which missing sprites we've found (reduce dupe log messages)
|
|
130
|
+
},
|
|
131
|
+
|
|
132
|
+
// Override to queue features instead of processing immediately
|
|
133
|
+
addFeature (feature, draw, context) {
|
|
134
|
+
let tile = context.tile;
|
|
135
|
+
if (tile.generation !== this.generation) {
|
|
136
|
+
return;
|
|
137
|
+
}
|
|
138
|
+
if (this.cpu_projection && !['Point', 'MultiPoint'].includes(feature.geometry.type)) {
|
|
139
|
+
throw new Error('Projected annotations require Point or MultiPoint source geometry');
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
// Point styling
|
|
143
|
+
let style: PointFeatureStyle = {};
|
|
144
|
+
style.color = this.parseColor(draw.color, context);
|
|
145
|
+
style.texture = draw.texture; // optional point texture, specified in `draw` or at style level
|
|
146
|
+
style.label_texture = null; // assigned by labelling code if needed
|
|
147
|
+
style.blend_order = draw.blend_order; // copy pre-computed blend order
|
|
148
|
+
|
|
149
|
+
// require color or texture
|
|
150
|
+
if (!style.color && !style.texture) {
|
|
151
|
+
return;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
style.alpha = StyleParser.evalCachedProperty(draw.alpha, context) as number | undefined; // optional alpha override
|
|
155
|
+
|
|
156
|
+
// optional sprite and texture
|
|
157
|
+
let sprite_info;
|
|
158
|
+
if (this.hasSprites(style)) {
|
|
159
|
+
// populate sprite_info object with used sprites
|
|
160
|
+
sprite_info = this.parseSprite(style, draw, context);
|
|
161
|
+
|
|
162
|
+
if (sprite_info) {
|
|
163
|
+
style.texcoords = sprite_info.texcoords;
|
|
164
|
+
}
|
|
165
|
+
else {
|
|
166
|
+
// sprites are defined in the style's texture, but none are used in the current layer
|
|
167
|
+
log({ level: 'debug', once: true }, `Layer group '${draw.layers?.join(', ')}' ` +
|
|
168
|
+
`uses a texture '${style.texture}', but doesn't specify which sprite to draw. ` +
|
|
169
|
+
'Features that match this layer group won\'t be drawn without specifying the sprite with the ' +
|
|
170
|
+
'\'sprite\' or \'sprite_default\' properties. The merged draw parameters for this layer group are:', draw).then(logged => {
|
|
171
|
+
if (logged) {
|
|
172
|
+
log('debug', `Example feature for layer group '${draw.layers?.join(', ')}'`, feature);
|
|
173
|
+
}
|
|
174
|
+
});
|
|
175
|
+
return;
|
|
176
|
+
}
|
|
177
|
+
} else if (draw.sprite) {
|
|
178
|
+
// sprite specified in the draw layer but no sprites defined in the texture
|
|
179
|
+
log({ level: 'warn', once: true }, `Layer group '${draw.layers?.join(', ')}' ` +
|
|
180
|
+
`specifies sprite '${draw.sprite}', but the texture '${draw.texture}' doesn't define any sprites. ` +
|
|
181
|
+
'Features that match this layer group won\'t be drawn. The merged draw parameters for this layer group are:', draw);
|
|
182
|
+
return;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
this.calcSize(draw, style, sprite_info, context);
|
|
186
|
+
if (!isPointPair(style.size)) throw new TypeError('Point size must contain two numeric dimensions');
|
|
187
|
+
|
|
188
|
+
// incorporate outline into size
|
|
189
|
+
if (draw.outline) {
|
|
190
|
+
style.outline_width = (StyleParser.evalCachedProperty(draw.outline.width, context) || StyleParser.defaults.outline.width) as number;
|
|
191
|
+
style.outline_color = this.parseColor(draw.outline.color, context);
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
style.outline_edge_pct = 0;
|
|
195
|
+
if (style.outline_width && style.outline_color) {
|
|
196
|
+
// adjust size and UVs for outline
|
|
197
|
+
let outline_width = style.outline_width;
|
|
198
|
+
style.size[0] += outline_width;
|
|
199
|
+
style.size[1] += outline_width;
|
|
200
|
+
style.outline_edge_pct = outline_width / Math.min(style.size[0], style.size[1]) * 2; // UV distance at which outline starts
|
|
201
|
+
style.outline_alpha = StyleParser.evalCachedProperty(draw.outline?.alpha, context) as number | undefined; // optional alpha override
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
// size will be scaled to 16-bit signed int, so max allowed width + height of 256 pixels
|
|
205
|
+
style.size[0] = Math.min(style.size[0], 256);
|
|
206
|
+
style.size[1] = Math.min(style.size[1], 256);
|
|
207
|
+
|
|
208
|
+
// Placement strategy
|
|
209
|
+
style.placement = typeof draw.placement === 'number' ? draw.placement : undefined;
|
|
210
|
+
style.placement_min_length_ratio = StyleParser.evalCachedProperty(draw.placement_min_length_ratio, context) as number;
|
|
211
|
+
|
|
212
|
+
// Spacing parameter (in pixels) to equally space points along a line
|
|
213
|
+
if (style.placement === PLACEMENT.SPACED && draw.placement_spacing) {
|
|
214
|
+
style.placement_spacing = StyleParser.evalCachedProperty(draw.placement_spacing, context) as number;
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
// Angle parameter (can be a number or the string "auto")
|
|
218
|
+
style.angle = (StyleParser.evalProperty(draw.angle, context) || 0) as number | 'auto';
|
|
219
|
+
|
|
220
|
+
// points can be placed off the ground
|
|
221
|
+
const height = this.cpu_projection ? StyleParser.evalProjectedHeightProperty(draw.z, context) :
|
|
222
|
+
StyleParser.evalCachedDistanceProperty(draw.z, context);
|
|
223
|
+
style.z = this.cpu_projection ? packProjectedAnnotationHeight(height, this.cpu_projection.allowElevation) :
|
|
224
|
+
(height || StyleParser.defaults.z) as number;
|
|
225
|
+
if (this.cpu_projection) style.projected_height = style.z / PACKED_HEIGHT_SCALE;
|
|
226
|
+
|
|
227
|
+
style.tile_edges = draw.tile_edges; // usually activated for debugging, or rare visualization needs
|
|
228
|
+
|
|
229
|
+
this.computeLayout(style, feature, draw, context, tile);
|
|
230
|
+
|
|
231
|
+
// Text styling
|
|
232
|
+
let tf: ParsedTextFeature | ParsedTextFeature[] | false | null | undefined =
|
|
233
|
+
draw.text &&
|
|
234
|
+
draw.text.visible !== false && // explicitly handle `visible` property for nested text
|
|
235
|
+
this.parseTextFeature(feature, draw.text, context, tile);
|
|
236
|
+
|
|
237
|
+
if (Array.isArray(tf)) {
|
|
238
|
+
tf = null; // NB: boundary labels not supported for point label attachments, should log warning
|
|
239
|
+
log({ level: 'warn', once: true }, `Layer group '${draw.layers?.join(', ')}': ` +
|
|
240
|
+
'cannot use boundary labels (e.g. \'text_source: { left: ..., right: ... }\') for \'text\' labels attached to \'points\'; ' +
|
|
241
|
+
`provided 'text_source' value was ${JSON.stringify(draw.text?.text_source)}`);
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
if (tf) {
|
|
245
|
+
if (!isPointParent(style)) throw new TypeError('Attached text requires a sized point parent with an offset');
|
|
246
|
+
tf.layout.parent = style; // parent point will apply additional anchor/offset to text
|
|
247
|
+
if (this.cpu_projection) tf.layout.projected_height = style.projected_height;
|
|
248
|
+
|
|
249
|
+
// Text labels have a default priority of 0.5 below their parent point (+0.5, priority is lower-is-better)
|
|
250
|
+
// This can be overriden, as long as it is less than or equal to the default
|
|
251
|
+
tf.layout.priority = draw.text?.priority ? Math.max(tf.layout.priority, (style.priority as number) + 0.5) : ((style.priority as number) + 0.5);
|
|
252
|
+
|
|
253
|
+
Collision.addStyle(this.collision_group_text, tile.id);
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
this.queueFeature({ feature, draw, context, style, text_feature: tf }, tile); // queue the feature for later processing
|
|
257
|
+
|
|
258
|
+
// Register with collision manager
|
|
259
|
+
Collision.addStyle(this.collision_group_points, tile.id);
|
|
260
|
+
},
|
|
261
|
+
|
|
262
|
+
// Calcuate the size for the current point feature
|
|
263
|
+
calcSize (draw, style, sprite_info, context) {
|
|
264
|
+
// point size defined explicitly, or defaults to sprite size, or generic fallback
|
|
265
|
+
if (!draw.size) {
|
|
266
|
+
// a 'size' property has not been set in the draw layer -
|
|
267
|
+
// use the sprite size if it exists and a generic fallback if it doesn't
|
|
268
|
+
const size = (sprite_info && sprite_info.css_size) || [DEFAULT_POINT_SIZE, DEFAULT_POINT_SIZE];
|
|
269
|
+
if (!isPointPair(size)) throw new TypeError('Sprite size must contain two numeric dimensions');
|
|
270
|
+
style.size = size;
|
|
271
|
+
}
|
|
272
|
+
else {
|
|
273
|
+
// check for a cached size, passing the texture and any sprite references
|
|
274
|
+
const texture = style.texture ? Texture.textures[style.texture] : undefined;
|
|
275
|
+
if (texture && !isPointSizeImage(texture)) throw new TypeError('Texture dimensions are not initialized');
|
|
276
|
+
const size = StyleParser.evalCachedPointSizeProperty(draw.size, sprite_info, texture, context);
|
|
277
|
+
if (size == null) {
|
|
278
|
+
style.size = undefined;
|
|
279
|
+
// the StyleParser couldn't evaluate a sprite size
|
|
280
|
+
log({ level: 'warn', once: true }, `Layer group '${draw.layers?.join(', ')}': ` +
|
|
281
|
+
`'size' (${JSON.stringify(draw.size.value)}) couldn't be interpreted, features that match ` +
|
|
282
|
+
'this layer group won\'t be drawn');
|
|
283
|
+
return;
|
|
284
|
+
}
|
|
285
|
+
else if (typeof size === 'number') {
|
|
286
|
+
style.size = [size, size]; // convert 1d size to 2d
|
|
287
|
+
}
|
|
288
|
+
else if (isPointPair(size)) style.size = size;
|
|
289
|
+
else throw new TypeError('Point size must contain two numeric dimensions');
|
|
290
|
+
}
|
|
291
|
+
},
|
|
292
|
+
|
|
293
|
+
hasSprites (style) {
|
|
294
|
+
return style.texture && Texture.textures[style.texture] && Texture.textures[style.texture].sprites;
|
|
295
|
+
},
|
|
296
|
+
|
|
297
|
+
// Generate a sprite_info object
|
|
298
|
+
getSpriteInfo (style, sprite) {
|
|
299
|
+
const textureName = style.texture;
|
|
300
|
+
if (!textureName) return undefined;
|
|
301
|
+
const key = String(sprite);
|
|
302
|
+
let info = Texture.textures[textureName]?.sprites?.[key] && Texture.getSpriteInfo(textureName, key);
|
|
303
|
+
if (info && !isPointSizeImage(info)) throw new TypeError('Sprite dimensions are not initialized');
|
|
304
|
+
if (sprite && !info) {
|
|
305
|
+
// track missing sprites (per texture)
|
|
306
|
+
this.texture_missing_sprites[textureName] = this.texture_missing_sprites[textureName] || {};
|
|
307
|
+
if (!this.texture_missing_sprites[textureName][key]) { // only log each missing sprite once
|
|
308
|
+
log('debug', `Style: in style '${this.name}', could not find sprite '${sprite}' for texture '${style.texture}'`);
|
|
309
|
+
this.texture_missing_sprites[textureName][key] = true;
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
else if (info) {
|
|
313
|
+
Object.assign(info, {sprite});
|
|
314
|
+
}
|
|
315
|
+
return info;
|
|
316
|
+
},
|
|
317
|
+
|
|
318
|
+
// Check a sprite name against available sprites and return a sprite_info object
|
|
319
|
+
parseSprite (style, draw, context) {
|
|
320
|
+
// check for functions
|
|
321
|
+
let sprite = StyleParser.evalProperty(draw.sprite, context);
|
|
322
|
+
let sprite_info = this.getSpriteInfo(style, sprite as string | undefined) || this.getSpriteInfo(style, draw.sprite_default);
|
|
323
|
+
return sprite_info;
|
|
324
|
+
},
|
|
325
|
+
|
|
326
|
+
// Queue features for deferred processing (collect all features first so we can do collision on the whole group)
|
|
327
|
+
queueFeature (q, tile) {
|
|
328
|
+
if (!this.tile_data[tile.id] || !this.queues[tile.id]) {
|
|
329
|
+
this.startData(tile);
|
|
330
|
+
}
|
|
331
|
+
this.queues[tile.id] = this.queues[tile.id] || [];
|
|
332
|
+
this.queues[tile.id].push(q);
|
|
333
|
+
},
|
|
334
|
+
|
|
335
|
+
// Override
|
|
336
|
+
async endData (tile) {
|
|
337
|
+
if (tile.canceled) {
|
|
338
|
+
log('trace', `Style ${this.name}: stop tile build because tile was canceled: ${tile.key}`);
|
|
339
|
+
return null;
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
let queue = this.queues[tile.id];
|
|
343
|
+
delete this.queues[tile.id];
|
|
344
|
+
|
|
345
|
+
// For each point feature, create one or more labels
|
|
346
|
+
let text_objs: AttachedTextQueue[] = [];
|
|
347
|
+
let point_objs: PointCandidate[] = [];
|
|
348
|
+
|
|
349
|
+
queue.forEach(q => {
|
|
350
|
+
let style = q.style;
|
|
351
|
+
let feature = q.feature;
|
|
352
|
+
let geometry = feature.geometry;
|
|
353
|
+
const size = style.size;
|
|
354
|
+
if (!isPointPair(size) || !isCommonLayout(style)) throw new TypeError('Queued point layout is incomplete');
|
|
355
|
+
let feature_labels = this.buildLabels(size, geometry, style);
|
|
356
|
+
for (let i = 0; i < feature_labels.length; i++) {
|
|
357
|
+
let label = feature_labels[i];
|
|
358
|
+
let point_obj: PointCandidate = {
|
|
359
|
+
feature,
|
|
360
|
+
draw: q.draw,
|
|
361
|
+
context: q.context,
|
|
362
|
+
style,
|
|
363
|
+
label
|
|
364
|
+
};
|
|
365
|
+
point_objs.push(point_obj);
|
|
366
|
+
|
|
367
|
+
if (q.text_feature) {
|
|
368
|
+
let text_obj = {
|
|
369
|
+
feature,
|
|
370
|
+
draw: q.text_feature.draw,
|
|
371
|
+
context: q.context,
|
|
372
|
+
text: q.text_feature.text,
|
|
373
|
+
text_settings_key: q.text_feature.text_settings_key,
|
|
374
|
+
layout: q.text_feature.layout,
|
|
375
|
+
point_label: label,
|
|
376
|
+
linked: point_obj // link so text only renders when parent point is placed
|
|
377
|
+
};
|
|
378
|
+
text_objs.push(text_obj);
|
|
379
|
+
|
|
380
|
+
// Unless text feature is optional, create a two-way link so that parent
|
|
381
|
+
// point will only render when text is also placed
|
|
382
|
+
if (!q.draw.text?.optional) {
|
|
383
|
+
point_obj.linked = text_obj; // two-way link
|
|
384
|
+
}
|
|
385
|
+
}
|
|
386
|
+
}
|
|
387
|
+
});
|
|
388
|
+
|
|
389
|
+
// Collide both points and text, then build features
|
|
390
|
+
const [, { labels, texts, textures }] = await Promise.all([
|
|
391
|
+
// Points
|
|
392
|
+
Collision.collide(point_objs, this.collision_group_points, tile.id).then(point_objs => {
|
|
393
|
+
point_objs.forEach(q => {
|
|
394
|
+
this.feature_style = q.style;
|
|
395
|
+
this.feature_style.label = q.label;
|
|
396
|
+
this.feature_style.linked = q.linked; // TODO: move linked into label to avoid extra prop tracking?
|
|
397
|
+
Style.addFeature.call(this, q.feature, q.draw, q.context);
|
|
398
|
+
});
|
|
399
|
+
}),
|
|
400
|
+
// Labels
|
|
401
|
+
this.collideAndRenderTextLabels(tile, this.collision_group_text, text_objs)
|
|
402
|
+
]);
|
|
403
|
+
|
|
404
|
+
// Process labels
|
|
405
|
+
if (labels && texts) {
|
|
406
|
+
// Build queued features
|
|
407
|
+
labels.forEach(q => {
|
|
408
|
+
let text_settings_key = q.text_settings_key;
|
|
409
|
+
let text_info = texts[text_settings_key] && texts[text_settings_key][q.text];
|
|
410
|
+
const alignment = q.label.align === undefined ? undefined : text_info?.align?.[q.label.align];
|
|
411
|
+
if (!text_info?.size || !alignment || alignment.texture_id === undefined || !textures) throw new TypeError('Attached text atlas is incomplete');
|
|
412
|
+
|
|
413
|
+
// setup styling object expected by Style class
|
|
414
|
+
let style = this.feature_style;
|
|
415
|
+
style.label = q.label;
|
|
416
|
+
// The shared feature_style belongs to the last point, not necessarily this text.
|
|
417
|
+
if (this.cpu_projection) style.z = (q.layout.projected_height ?? 0) * PACKED_HEIGHT_SCALE;
|
|
418
|
+
style.linked = q.linked; // TODO: move linked into label to avoid extra prop tracking?
|
|
419
|
+
style.size = text_info.size.logical_size;
|
|
420
|
+
style.texcoords = alignment.texcoords;
|
|
421
|
+
style.label_texture = textures[alignment.texture_id];
|
|
422
|
+
style.blend_order = q.draw.blend_order; // copy blend order from parent point
|
|
423
|
+
|
|
424
|
+
Style.addFeature.call(this, q.feature, q.draw, q.context);
|
|
425
|
+
});
|
|
426
|
+
}
|
|
427
|
+
this.freeText(tile);
|
|
428
|
+
|
|
429
|
+
// Finish tile mesh
|
|
430
|
+
const tile_data = await Style.endData.call(this, tile);
|
|
431
|
+
// Attach tile-specific label atlas to mesh as a texture uniform
|
|
432
|
+
if (tile_data && textures && textures.length) {
|
|
433
|
+
tile_data.textures = tile_data.textures || [];
|
|
434
|
+
tile_data.textures.push(...textures); // assign texture ownership to tile
|
|
435
|
+
}
|
|
436
|
+
return tile_data;
|
|
437
|
+
},
|
|
438
|
+
|
|
439
|
+
_preprocess (draw) {
|
|
440
|
+
draw.color = StyleParser.createColorPropertyCache(draw.color);
|
|
441
|
+
draw.alpha = StyleParser.createPropertyCache(draw.alpha);
|
|
442
|
+
draw.texture = (draw.texture !== undefined ? draw.texture : this.texture); // optional or default texture
|
|
443
|
+
draw.blend_order = this.getBlendOrderForDraw(draw); // from draw block, or fall back on default style blend order
|
|
444
|
+
|
|
445
|
+
if (draw.outline) {
|
|
446
|
+
draw.outline.color = StyleParser.createColorPropertyCache(draw.outline.color);
|
|
447
|
+
draw.outline.alpha = StyleParser.createPropertyCache(draw.outline.alpha);
|
|
448
|
+
draw.outline.width = StyleParser.createPropertyCache(draw.outline.width, StyleParser.parsePositiveNumber);
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
draw.z = this.cpu_projection ? StyleParser.createProjectedHeightPropertyCache(draw.z) :
|
|
452
|
+
StyleParser.createPropertyCache(draw.z, StyleParser.parseUnits);
|
|
453
|
+
|
|
454
|
+
// Size (1d value or 2d array)
|
|
455
|
+
try {
|
|
456
|
+
draw.size = StyleParser.createPointSizePropertyCache(draw.size, draw.texture);
|
|
457
|
+
}
|
|
458
|
+
catch(e) {
|
|
459
|
+
log({ level: 'warn', once: true }, `Layer group '${draw.layers?.join(', ')}': ` +
|
|
460
|
+
`${e} (${JSON.stringify(draw.size)}), features that match this layer group won't be drawn.`);
|
|
461
|
+
return null;
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
// Offset (2d array)
|
|
465
|
+
draw.offset = StyleParser.createPropertyCache(draw.offset,
|
|
466
|
+
v => Array.isArray(v) && v.map(StyleParser.parseNumber)
|
|
467
|
+
);
|
|
468
|
+
|
|
469
|
+
// Buffer (1d value or or 2d array) - must be >= 0
|
|
470
|
+
draw.buffer = StyleParser.createPropertyCache(draw.buffer,
|
|
471
|
+
v => (Array.isArray(v) ? v : [v, v]).map(StyleParser.parsePositiveNumber)
|
|
472
|
+
);
|
|
473
|
+
|
|
474
|
+
// Repeat rules - no repeat limitation for points by default
|
|
475
|
+
draw.repeat_distance = StyleParser.createPropertyCache(draw.repeat_distance, StyleParser.parseNumber);
|
|
476
|
+
|
|
477
|
+
// Placement strategies
|
|
478
|
+
draw.placement = PLACEMENT[typeof draw.placement === 'string' ? draw.placement.toUpperCase() : ''];
|
|
479
|
+
if (draw.placement == null) {
|
|
480
|
+
draw.placement = PLACEMENT.VERTEX;
|
|
481
|
+
}
|
|
482
|
+
|
|
483
|
+
draw.placement_spacing = StyleParser.createPropertyCache(draw.placement_spacing != null ? draw.placement_spacing : 80, StyleParser.parsePositiveNumber);
|
|
484
|
+
|
|
485
|
+
draw.placement_min_length_ratio = StyleParser.createPropertyCache(draw.placement_min_length_ratio != null ? draw.placement_min_length_ratio : 1, StyleParser.parsePositiveNumber);
|
|
486
|
+
|
|
487
|
+
if (typeof draw.angle === 'number') {
|
|
488
|
+
draw.angle = draw.angle * Math.PI / 180; // convert static value to radians
|
|
489
|
+
}
|
|
490
|
+
else if (typeof draw.angle === 'function') {
|
|
491
|
+
// convert function return value to radians
|
|
492
|
+
const angle_func = draw.angle;
|
|
493
|
+
draw.angle = context => angle_func(context) * Math.PI / 180;
|
|
494
|
+
}
|
|
495
|
+
else {
|
|
496
|
+
draw.angle = draw.angle || 0; // angle can be a string like "auto" (use angle of geometry)
|
|
497
|
+
}
|
|
498
|
+
|
|
499
|
+
// Optional text styling
|
|
500
|
+
draw.text = draw.text ? this.preprocessText(draw.text) : undefined; // will return null if valid text styling wasn't provided
|
|
501
|
+
if (draw.text) {
|
|
502
|
+
draw.text.key = draw.key; // inherits parent properties
|
|
503
|
+
draw.text.group = draw.group;
|
|
504
|
+
draw.text.layers = draw.layers;
|
|
505
|
+
draw.text.order = draw.order;
|
|
506
|
+
draw.text.blend_order = draw.blend_order;
|
|
507
|
+
draw.text.repeat_group = (draw.text.repeat_group != null ? draw.text.repeat_group : draw.repeat_group);
|
|
508
|
+
draw.text.anchor = draw.text.anchor || this.default_anchor;
|
|
509
|
+
draw.text.optional = (typeof draw.text.optional === 'boolean') ? draw.text.optional : false; // default text to required
|
|
510
|
+
draw.text.interactive = draw.text.interactive || draw.interactive; // inherits from point
|
|
511
|
+
}
|
|
512
|
+
|
|
513
|
+
return draw;
|
|
514
|
+
},
|
|
515
|
+
|
|
516
|
+
// Default to trying all anchor placements
|
|
517
|
+
default_anchor: ['bottom', 'top', 'right', 'left'],
|
|
518
|
+
|
|
519
|
+
// Compute label layout-related properties
|
|
520
|
+
computeLayout (target, feature, draw, context, tile) {
|
|
521
|
+
let layout = target || {};
|
|
522
|
+
layout.id = feature;
|
|
523
|
+
layout.units_per_pixel = tile.units_per_pixel || 1;
|
|
524
|
+
|
|
525
|
+
// collision flag
|
|
526
|
+
layout.collide = (draw.collide === false) ? false : true;
|
|
527
|
+
|
|
528
|
+
// label anchors (point labels only)
|
|
529
|
+
// label position will be adjusted in the given direction, relative to its original point
|
|
530
|
+
// one of: left, right, top, bottom, top-left, top-right, bottom-left, bottom-right
|
|
531
|
+
layout.anchor = draw.anchor;
|
|
532
|
+
|
|
533
|
+
// label offset and buffer in pixel (applied in screen space)
|
|
534
|
+
layout.offset = (StyleParser.evalCachedProperty(draw.offset, context) || StyleParser.zeroPair) as LabelPointCoordinate;
|
|
535
|
+
layout.buffer = (StyleParser.evalCachedProperty(draw.buffer, context) || StyleParser.zeroPair) as LabelPointCoordinate;
|
|
536
|
+
|
|
537
|
+
// repeat rules
|
|
538
|
+
layout.repeat_distance = StyleParser.evalCachedProperty(draw.repeat_distance, context) as number | undefined;
|
|
539
|
+
if (layout.repeat_distance) {
|
|
540
|
+
layout.repeat_distance *= layout.units_per_pixel;
|
|
541
|
+
layout.repeat_scale = 1; // initial repeat pass in tile with full scale
|
|
542
|
+
|
|
543
|
+
if (typeof draw.repeat_group === 'function') {
|
|
544
|
+
layout.repeat_group = draw.repeat_group(context); // dynamic repeat group
|
|
545
|
+
}
|
|
546
|
+
else {
|
|
547
|
+
// default to top-level layer name
|
|
548
|
+
// (e.g. all labels under `roads` layer, including sub-layers, are in one repeat group)
|
|
549
|
+
layout.repeat_group = draw.repeat_group || context.layer;
|
|
550
|
+
}
|
|
551
|
+
}
|
|
552
|
+
|
|
553
|
+
// label priority (lower is higher)
|
|
554
|
+
let priority = draw.priority;
|
|
555
|
+
if (priority != null) {
|
|
556
|
+
if (typeof priority === 'function') {
|
|
557
|
+
priority = priority(context);
|
|
558
|
+
}
|
|
559
|
+
}
|
|
560
|
+
else {
|
|
561
|
+
priority = -1 >>> 0; // default to max priority value if none set
|
|
562
|
+
}
|
|
563
|
+
layout.priority = priority;
|
|
564
|
+
|
|
565
|
+
if (this.screen_space_labels || this.cpu_projection) {
|
|
566
|
+
// Keep all candidates until the main thread has the projected camera.
|
|
567
|
+
layout.projected_collide = layout.collide;
|
|
568
|
+
layout.collide = false;
|
|
569
|
+
const identity = feature.id ?? feature.properties?.id;
|
|
570
|
+
if (typeof identity === 'string' || typeof identity === 'number') {
|
|
571
|
+
layout.projected_identity = JSON.stringify([context.source, context.layer, this.name, draw.key,
|
|
572
|
+
typeof identity, identity]);
|
|
573
|
+
}
|
|
574
|
+
}
|
|
575
|
+
|
|
576
|
+
if (!isCommonLayout(layout)) throw new TypeError('Point layout is incomplete');
|
|
577
|
+
return layout;
|
|
578
|
+
},
|
|
579
|
+
|
|
580
|
+
// Implements label building for TextLabels mixin
|
|
581
|
+
buildTextLabels (tile, feature_queue) {
|
|
582
|
+
let labels = [];
|
|
583
|
+
for (let f=0; f < feature_queue.length; f++) {
|
|
584
|
+
let fq = feature_queue[f];
|
|
585
|
+
if (!('point_label' in fq) || !(fq.point_label instanceof LabelPoint)) throw new TypeError('Attached text has no parent label');
|
|
586
|
+
let text_info = this.texts[tile.id][fq.text_settings_key][fq.text];
|
|
587
|
+
if (!text_info?.size) throw new TypeError('Attached text measurement is incomplete');
|
|
588
|
+
let size = text_info.size.collision_size;
|
|
589
|
+
labels.push(Object.assign(fq, {label: new LabelPoint(fq.point_label.position, size, fq.layout)}));
|
|
590
|
+
}
|
|
591
|
+
return labels;
|
|
592
|
+
},
|
|
593
|
+
|
|
594
|
+
// Builds one or more point labels for a geometry
|
|
595
|
+
buildLabels (size, geometry, layout) {
|
|
596
|
+
let labels = [];
|
|
597
|
+
|
|
598
|
+
if (geometry.type === 'Point') {
|
|
599
|
+
labels.push(new LabelPoint(geometry.coordinates, size, layout, layout.angle as number | undefined));
|
|
600
|
+
}
|
|
601
|
+
else if (geometry.type === 'MultiPoint') {
|
|
602
|
+
let points = geometry.coordinates;
|
|
603
|
+
for (let i = 0; i < points.length; ++i) {
|
|
604
|
+
let point = points[i];
|
|
605
|
+
labels.push(new LabelPoint(point, size, layout, layout.angle as number | undefined));
|
|
606
|
+
}
|
|
607
|
+
}
|
|
608
|
+
else if (geometry.type === 'LineString') {
|
|
609
|
+
let line = geometry.coordinates;
|
|
610
|
+
let point_labels = placePointsOnLine(line, size, layout);
|
|
611
|
+
for (let i = 0; i < point_labels.length; ++i) {
|
|
612
|
+
labels.push(point_labels[i]);
|
|
613
|
+
}
|
|
614
|
+
}
|
|
615
|
+
else if (geometry.type === 'MultiLineString') {
|
|
616
|
+
let lines = geometry.coordinates;
|
|
617
|
+
for (let ln = 0; ln < lines.length; ln++) {
|
|
618
|
+
let line = lines[ln];
|
|
619
|
+
let point_labels = placePointsOnLine(line, size, layout);
|
|
620
|
+
for (let i = 0; i < point_labels.length; ++i) {
|
|
621
|
+
labels.push(point_labels[i]);
|
|
622
|
+
}
|
|
623
|
+
}
|
|
624
|
+
}
|
|
625
|
+
else if (geometry.type === 'Polygon') {
|
|
626
|
+
// Point at polygon centroid (of outer ring)
|
|
627
|
+
if (layout.placement === PLACEMENT.CENTROID) {
|
|
628
|
+
let centroid = Geo.centroid(geometry.coordinates);
|
|
629
|
+
if (centroid) { // skip degenerate polygons
|
|
630
|
+
labels.push(new LabelPoint([centroid[0], centroid[1]], size, layout, layout.angle as number | undefined));
|
|
631
|
+
}
|
|
632
|
+
}
|
|
633
|
+
// Point at each polygon vertex (all rings)
|
|
634
|
+
else {
|
|
635
|
+
let rings = geometry.coordinates;
|
|
636
|
+
for (let ln = 0; ln < rings.length; ln++) {
|
|
637
|
+
let point_labels = placePointsOnLine(rings[ln], size, layout);
|
|
638
|
+
for (let i = 0; i < point_labels.length; ++i) {
|
|
639
|
+
labels.push(point_labels[i]);
|
|
640
|
+
}
|
|
641
|
+
}
|
|
642
|
+
}
|
|
643
|
+
}
|
|
644
|
+
else if (geometry.type === 'MultiPolygon') {
|
|
645
|
+
if (layout.placement === PLACEMENT.CENTROID) {
|
|
646
|
+
let centroid = Geo.multiCentroid(geometry.coordinates);
|
|
647
|
+
if (centroid) { // skip degenerate polygons
|
|
648
|
+
labels.push(new LabelPoint([centroid[0], centroid[1]], size, layout, layout.angle as number | undefined));
|
|
649
|
+
}
|
|
650
|
+
}
|
|
651
|
+
else {
|
|
652
|
+
let polys = geometry.coordinates;
|
|
653
|
+
for (let p = 0; p < polys.length; p++) {
|
|
654
|
+
let rings = polys[p];
|
|
655
|
+
for (let ln = 0; ln < rings.length; ln++) {
|
|
656
|
+
let point_labels = placePointsOnLine(rings[ln], size, layout);
|
|
657
|
+
for (let i = 0; i < point_labels.length; ++i) {
|
|
658
|
+
labels.push(point_labels[i]);
|
|
659
|
+
}
|
|
660
|
+
}
|
|
661
|
+
}
|
|
662
|
+
}
|
|
663
|
+
}
|
|
664
|
+
|
|
665
|
+
return labels;
|
|
666
|
+
},
|
|
667
|
+
|
|
668
|
+
/**
|
|
669
|
+
* A "template" that sets constant attibutes for each vertex, which is then modified per vertex or per feature.
|
|
670
|
+
* A plain JS array matching the order of the vertex layout.
|
|
671
|
+
*/
|
|
672
|
+
makeVertexTemplate(style, mesh, add_custom_attribs = true) {
|
|
673
|
+
if (!style.label) throw new TypeError('Point vertex template requires a prepared label');
|
|
674
|
+
const portable = mesh.vertex_data.vertex_layout.index.a_point_type != null;
|
|
675
|
+
let i = 0;
|
|
676
|
+
|
|
677
|
+
// a_position.xyz - vertex position
|
|
678
|
+
// a_position.w - layer order
|
|
679
|
+
this.vertex_template[i++] = 0;
|
|
680
|
+
this.vertex_template[i++] = 0;
|
|
681
|
+
this.vertex_template[i++] = style.z || 0;
|
|
682
|
+
this.vertex_template[i++] = this.scaleOrder(Number(style.order));
|
|
683
|
+
|
|
684
|
+
// a_shape.xy - size of point in pixels (scaling vector)
|
|
685
|
+
// a_shape.z - angle of point
|
|
686
|
+
// a_shape.w - show/hide flag
|
|
687
|
+
this.vertex_template[i++] = 0;
|
|
688
|
+
this.vertex_template[i++] = 0;
|
|
689
|
+
this.vertex_template[i++] = 0;
|
|
690
|
+
this.vertex_template[i++] = style.label.layout.collide ? 0 : 1; // set initial label hide/show state
|
|
691
|
+
|
|
692
|
+
// a_texcoord.xy - texture coords
|
|
693
|
+
if (!mesh.variant.shader_point || portable) {
|
|
694
|
+
this.vertex_template[i++] = 0;
|
|
695
|
+
this.vertex_template[i++] = 0;
|
|
696
|
+
}
|
|
697
|
+
|
|
698
|
+
// a_offset.xy - offset of point from center, in pixels
|
|
699
|
+
this.vertex_template[i++] = 0;
|
|
700
|
+
this.vertex_template[i++] = 0;
|
|
701
|
+
|
|
702
|
+
// a_color.rgba - feature color
|
|
703
|
+
const color = style.color || StyleParser.defaults.color;
|
|
704
|
+
this.vertex_template[i++] = color[0] * 255;
|
|
705
|
+
this.vertex_template[i++] = color[1] * 255;
|
|
706
|
+
this.vertex_template[i++] = color[2] * 255;
|
|
707
|
+
this.vertex_template[i++] = (style.alpha != null ? style.alpha : color[3]) * 255;
|
|
708
|
+
|
|
709
|
+
// a_selection_color.rgba - selection color
|
|
710
|
+
if (mesh.variant.selection) {
|
|
711
|
+
if (!style.selection_color) throw new TypeError('Interactive point has no selection color');
|
|
712
|
+
this.vertex_template[i++] = style.selection_color[0] * 255;
|
|
713
|
+
this.vertex_template[i++] = style.selection_color[1] * 255;
|
|
714
|
+
this.vertex_template[i++] = style.selection_color[2] * 255;
|
|
715
|
+
this.vertex_template[i++] = style.selection_color[3] * 255;
|
|
716
|
+
}
|
|
717
|
+
|
|
718
|
+
// point outline
|
|
719
|
+
if (mesh.variant.shader_point || portable) {
|
|
720
|
+
// a_outline_color.rgba - outline color
|
|
721
|
+
const outline_color = style.outline_color || StyleParser.defaults.outline.color;
|
|
722
|
+
this.vertex_template[i++] = outline_color[0] * 255;
|
|
723
|
+
this.vertex_template[i++] = outline_color[1] * 255;
|
|
724
|
+
this.vertex_template[i++] = outline_color[2] * 255;
|
|
725
|
+
this.vertex_template[i++] = (style.outline_alpha != null ? style.outline_alpha : outline_color[3]) * 255;
|
|
726
|
+
|
|
727
|
+
// a_outline_edge - point outline edge (as % of point size where outline begins)
|
|
728
|
+
this.vertex_template[i++] = style.outline_edge_pct || StyleParser.defaults.outline.width;
|
|
729
|
+
}
|
|
730
|
+
|
|
731
|
+
if (portable) {
|
|
732
|
+
this.vertex_template[i++] = Number(mesh.variant.point_type);
|
|
733
|
+
}
|
|
734
|
+
|
|
735
|
+
if (this.cpu_projection) {
|
|
736
|
+
const projected = mesh.vertex_data.vertex_layout.index.a_projected_position;
|
|
737
|
+
for (let component = 0; component < 3; component++) this.vertex_template[projected + component] = 0;
|
|
738
|
+
}
|
|
739
|
+
if (add_custom_attribs) {
|
|
740
|
+
this.addCustomAttributesToVertexTemplate(style, i);
|
|
741
|
+
}
|
|
742
|
+
return this.vertex_template;
|
|
743
|
+
},
|
|
744
|
+
|
|
745
|
+
buildQuad(point, size, angle, angles, pre_angles, offset, offsets, texcoords, curve, vertex_data, vertex_template) {
|
|
746
|
+
if (size[0] <= 0 || size[1] <= 0) {
|
|
747
|
+
return 0; // size must be positive
|
|
748
|
+
}
|
|
749
|
+
|
|
750
|
+
return buildQuadForPoint(
|
|
751
|
+
point,
|
|
752
|
+
vertex_data,
|
|
753
|
+
vertex_template,
|
|
754
|
+
vertex_data.vertex_layout.index,
|
|
755
|
+
size,
|
|
756
|
+
offset,
|
|
757
|
+
offsets,
|
|
758
|
+
pre_angles,
|
|
759
|
+
angle * 4096, // angle values have a 12-bit fraction
|
|
760
|
+
angles,
|
|
761
|
+
texcoords,
|
|
762
|
+
curve
|
|
763
|
+
);
|
|
764
|
+
},
|
|
765
|
+
|
|
766
|
+
// Build quad for point sprite
|
|
767
|
+
build (style, context) {
|
|
768
|
+
let label = style.label;
|
|
769
|
+
if (!label) throw new TypeError('Point geometry requires a prepared label');
|
|
770
|
+
if (label.type === 'curved') {
|
|
771
|
+
return this.buildCurvedLabel(label, style, context);
|
|
772
|
+
}
|
|
773
|
+
else {
|
|
774
|
+
return this.buildStraightLabel(label, style, context);
|
|
775
|
+
}
|
|
776
|
+
},
|
|
777
|
+
|
|
778
|
+
buildStraightLabel (label, style, context) {
|
|
779
|
+
let mesh = this.getTileMesh(context.tile, this.meshVariantTypeForDraw(style));
|
|
780
|
+
let vertex_template = this.makeVertexTemplate(style, mesh);
|
|
781
|
+
|
|
782
|
+
let size, texcoords;
|
|
783
|
+
if (label.type !== 'point') {
|
|
784
|
+
size = !Array.isArray(style.size) ? style.size?.straight : undefined;
|
|
785
|
+
texcoords = !Array.isArray(style.texcoords) ? style.texcoords?.straight?.texcoord : undefined;
|
|
786
|
+
if (!size || !texcoords) throw new TypeError('Straight text dimensions and atlas coordinates are missing');
|
|
787
|
+
}
|
|
788
|
+
else {
|
|
789
|
+
if (!isPointPair(style.size)) throw new TypeError('Point dimensions are missing');
|
|
790
|
+
size = style.size;
|
|
791
|
+
texcoords = Array.isArray(style.texcoords) ? style.texcoords : undefined;
|
|
792
|
+
}
|
|
793
|
+
|
|
794
|
+
// setup style or label texture if applicable
|
|
795
|
+
mesh.uniforms = mesh.uniforms || {};
|
|
796
|
+
if (style.label_texture) {
|
|
797
|
+
mesh.uniforms.u_texture = style.label_texture;
|
|
798
|
+
mesh.uniforms.u_point_type = TANGRAM_POINT_TYPE_LABEL;
|
|
799
|
+
mesh.uniforms.u_apply_color_blocks = false;
|
|
800
|
+
}
|
|
801
|
+
else if (style.texture) {
|
|
802
|
+
mesh.uniforms.u_texture = style.texture;
|
|
803
|
+
mesh.uniforms.u_point_type = TANGRAM_POINT_TYPE_TEXTURE;
|
|
804
|
+
mesh.uniforms.u_apply_color_blocks = true;
|
|
805
|
+
}
|
|
806
|
+
else {
|
|
807
|
+
mesh.uniforms.u_texture = Texture.default; // ensure a tetxure is always bound to avoid GL warnings ('no texture bound to unit' in Chrome)
|
|
808
|
+
mesh.uniforms.u_point_type = TANGRAM_POINT_TYPE_SHADER;
|
|
809
|
+
mesh.uniforms.u_apply_color_blocks = true;
|
|
810
|
+
}
|
|
811
|
+
|
|
812
|
+
let offset = label.offset;
|
|
813
|
+
|
|
814
|
+
// TODO: instead of passing null, pass arrays with fingerprintable values
|
|
815
|
+
// This value is checked in the shader to determine whether to apply curving logic
|
|
816
|
+
let geom_count = this.buildQuad(
|
|
817
|
+
label.position, // position
|
|
818
|
+
size, // size in pixels
|
|
819
|
+
label.angle, // angle in radians
|
|
820
|
+
null, // placeholder for multiple angles
|
|
821
|
+
null, // placeholder for multiple pre_angles
|
|
822
|
+
offset, // offset from center in pixels
|
|
823
|
+
null, // placeholder for multiple offsets
|
|
824
|
+
texcoords, // texture UVs
|
|
825
|
+
false, // if curved boolean
|
|
826
|
+
mesh.vertex_data, vertex_template // VBO and data for current vertex
|
|
827
|
+
);
|
|
828
|
+
|
|
829
|
+
// track label mesh buffer data
|
|
830
|
+
const linked = getLinkedLabelId(style.linked);
|
|
831
|
+
this.trackLabel(label, linked, mesh, geom_count, context);
|
|
832
|
+
|
|
833
|
+
return geom_count;
|
|
834
|
+
},
|
|
835
|
+
|
|
836
|
+
buildCurvedLabel (label, style, context) {
|
|
837
|
+
const sizes = !Array.isArray(style.size) ? style.size?.curved : undefined;
|
|
838
|
+
const texcoords = !Array.isArray(style.texcoords) ? style.texcoords?.curved : undefined;
|
|
839
|
+
const strokes = style.texcoords_stroke, textures = style.label_textures;
|
|
840
|
+
if (!sizes || !texcoords || !strokes || !textures) throw new TypeError('Curved text dimensions and atlas coordinates are missing');
|
|
841
|
+
let mesh, vertex_template;
|
|
842
|
+
let geom_count = 0;
|
|
843
|
+
|
|
844
|
+
// two passes for stroke and fill, where stroke needs to be drawn first (painter's algorithm)
|
|
845
|
+
// this ensures strokes don't overlap on other fills
|
|
846
|
+
|
|
847
|
+
// pass for stroke
|
|
848
|
+
for (let i = 0; i < label.num_segments; i++){
|
|
849
|
+
let size = sizes[i];
|
|
850
|
+
let texcoord_stroke = strokes[i];
|
|
851
|
+
|
|
852
|
+
// re-point to correct label texture
|
|
853
|
+
style.label_texture = textures[i];
|
|
854
|
+
mesh = this.getTileMesh(context.tile, this.meshVariantTypeForDraw(style));
|
|
855
|
+
vertex_template = this.makeVertexTemplate(style, mesh);
|
|
856
|
+
|
|
857
|
+
// add label texture uniform if needed
|
|
858
|
+
mesh.uniforms = mesh.uniforms || {};
|
|
859
|
+
mesh.uniforms.u_texture = style.label_texture;
|
|
860
|
+
mesh.uniforms.u_point_type = TANGRAM_POINT_TYPE_LABEL;
|
|
861
|
+
mesh.uniforms.u_apply_color_blocks = false;
|
|
862
|
+
|
|
863
|
+
let offset = label.offset || [0,0];
|
|
864
|
+
let position = label.position;
|
|
865
|
+
|
|
866
|
+
let angles = label.angles[i];
|
|
867
|
+
let offsets = label.offsets[i];
|
|
868
|
+
let pre_angles = label.pre_angles[i];
|
|
869
|
+
|
|
870
|
+
let seg_count = this.buildQuad(
|
|
871
|
+
position, // position
|
|
872
|
+
size, // size in pixels
|
|
873
|
+
label.angle, // angle in degrees
|
|
874
|
+
angles, // angles per segment
|
|
875
|
+
pre_angles, // pre_angle array (rotation applied before offseting)
|
|
876
|
+
offset, // offset from center in pixels
|
|
877
|
+
offsets, // offsets per segment
|
|
878
|
+
texcoord_stroke, // texture UVs for stroked text
|
|
879
|
+
true, // if curved
|
|
880
|
+
mesh.vertex_data, vertex_template // VBO and data for current vertex
|
|
881
|
+
);
|
|
882
|
+
geom_count += seg_count;
|
|
883
|
+
|
|
884
|
+
// track label mesh buffer data
|
|
885
|
+
const linked = getLinkedLabelId(style.linked);
|
|
886
|
+
this.trackLabel(label, linked, mesh, seg_count, context);
|
|
887
|
+
}
|
|
888
|
+
|
|
889
|
+
// pass for fill
|
|
890
|
+
for (let i = 0; i < label.num_segments; i++){
|
|
891
|
+
let size = sizes[i];
|
|
892
|
+
let texcoord = texcoords[i].texcoord;
|
|
893
|
+
|
|
894
|
+
// re-point to correct label texture
|
|
895
|
+
style.label_texture = textures[i];
|
|
896
|
+
mesh = this.getTileMesh(context.tile, this.meshVariantTypeForDraw(style));
|
|
897
|
+
vertex_template = this.makeVertexTemplate(style, mesh);
|
|
898
|
+
|
|
899
|
+
// add label texture uniform if needed
|
|
900
|
+
mesh.uniforms = mesh.uniforms || {};
|
|
901
|
+
mesh.uniforms.u_texture = style.label_texture;
|
|
902
|
+
mesh.uniforms.u_point_type = TANGRAM_POINT_TYPE_LABEL;
|
|
903
|
+
mesh.uniforms.u_apply_color_blocks = false;
|
|
904
|
+
|
|
905
|
+
let offset = label.offset || [0,0];
|
|
906
|
+
let position = label.position;
|
|
907
|
+
|
|
908
|
+
let angles = label.angles[i];
|
|
909
|
+
let offsets = label.offsets[i];
|
|
910
|
+
let pre_angles = label.pre_angles[i];
|
|
911
|
+
|
|
912
|
+
let seg_count = this.buildQuad(
|
|
913
|
+
position, // position
|
|
914
|
+
size, // size in pixels
|
|
915
|
+
label.angle, // angle in degrees
|
|
916
|
+
angles, // angles per segment
|
|
917
|
+
pre_angles, // pre_angle array (rotation applied before offseting)
|
|
918
|
+
offset, // offset from center in pixels
|
|
919
|
+
offsets, // offsets per segment
|
|
920
|
+
texcoord, // texture UVs for fill text
|
|
921
|
+
true, // if curved
|
|
922
|
+
mesh.vertex_data, vertex_template // VBO and data for current vertex
|
|
923
|
+
);
|
|
924
|
+
geom_count += seg_count;
|
|
925
|
+
|
|
926
|
+
// track label mesh buffer data
|
|
927
|
+
const linked = getLinkedLabelId(style.linked);
|
|
928
|
+
this.trackLabel(label, linked, mesh, seg_count, context);
|
|
929
|
+
}
|
|
930
|
+
|
|
931
|
+
return geom_count;
|
|
932
|
+
},
|
|
933
|
+
|
|
934
|
+
// track mesh data for label on main thread, for additional cross-tile collision/repeat passes
|
|
935
|
+
trackLabel (label, linked, mesh, geom_count/*, context*/) {
|
|
936
|
+
// track if collision is enabled, or if the label is near enough to the tile edge to
|
|
937
|
+
// necessitate further repeat checking
|
|
938
|
+
if (this.screen_space_labels || this.cpu_projection || label.layout.collide || label.may_repeat_across_tiles) {
|
|
939
|
+
mesh.labels = mesh.labels || {};
|
|
940
|
+
mesh.labels[label.id] = mesh.labels[label.id] || {
|
|
941
|
+
container: {
|
|
942
|
+
label: label.toJSON(),
|
|
943
|
+
linked,
|
|
944
|
+
},
|
|
945
|
+
ranges: [],
|
|
946
|
+
// debug: { // uncomment and pass in context for debugging
|
|
947
|
+
// id: context.feature.properties.id,
|
|
948
|
+
// name: context.feature.properties.name,
|
|
949
|
+
// props: JSON.stringify(context.feature.properties),
|
|
950
|
+
// point_type: mesh.uniforms.u_point_type
|
|
951
|
+
// }
|
|
952
|
+
};
|
|
953
|
+
|
|
954
|
+
// store byte ranges occupied by label in VBO, so they can be updated on main thread
|
|
955
|
+
const vertex_count = geom_count * 2; // geom count is triangles: 2 triangles = 1 quad = 4 vertices
|
|
956
|
+
const start = mesh.vertex_data.offset - mesh.vertex_data.stride * vertex_count; // start offset of byte range
|
|
957
|
+
mesh.labels[label.id].ranges.push([
|
|
958
|
+
start,
|
|
959
|
+
vertex_count
|
|
960
|
+
]);
|
|
961
|
+
}
|
|
962
|
+
},
|
|
963
|
+
|
|
964
|
+
// Override to pass-through to generic point builder
|
|
965
|
+
buildLines (lines, style, context) {
|
|
966
|
+
return this.build(style, context);
|
|
967
|
+
},
|
|
968
|
+
|
|
969
|
+
buildPoints (points, style, context) {
|
|
970
|
+
return this.build(style, context);
|
|
971
|
+
},
|
|
972
|
+
|
|
973
|
+
buildPolygons (points, style, context) {
|
|
974
|
+
return this.build(style, context);
|
|
975
|
+
},
|
|
976
|
+
|
|
977
|
+
// Override
|
|
978
|
+
// Create or return desired vertex layout permutation based on flags
|
|
979
|
+
vertexLayoutForMeshVariant (variant) {
|
|
980
|
+
if (this.shader_language === 'wgsl') {
|
|
981
|
+
if (this.vertex_layouts.portable == null) {
|
|
982
|
+
this.vertex_layouts.portable = new VertexLayout([
|
|
983
|
+
{ name: 'a_position', size: 4, type: gl.SHORT, normalized: false },
|
|
984
|
+
{ name: 'a_shape', size: 4, type: gl.SHORT, normalized: false },
|
|
985
|
+
{ name: 'a_texcoord', size: 2, type: gl.UNSIGNED_SHORT, normalized: true },
|
|
986
|
+
{ name: 'a_offset', size: 2, type: gl.SHORT, normalized: false },
|
|
987
|
+
{ name: 'a_color', size: 4, type: gl.UNSIGNED_BYTE, normalized: true },
|
|
988
|
+
{ name: 'a_selection_color', size: 4, type: gl.UNSIGNED_BYTE, normalized: true },
|
|
989
|
+
{ name: 'a_outline_color', size: 4, type: gl.UNSIGNED_BYTE, normalized: true },
|
|
990
|
+
{ name: 'a_outline_edge', size: 1, type: gl.FLOAT, normalized: false },
|
|
991
|
+
{ name: 'a_point_type', size: 1, type: gl.FLOAT, normalized: false },
|
|
992
|
+
...(this.cpu_projection ? [{name: 'a_projected_position', size: 3, type: gl.FLOAT, normalized: false}] : [])
|
|
993
|
+
]);
|
|
994
|
+
}
|
|
995
|
+
return this.vertex_layouts.portable;
|
|
996
|
+
}
|
|
997
|
+
|
|
998
|
+
// Vertex layout only depends on shader point flag, so using it as layout key to avoid duplicate layouts
|
|
999
|
+
const layoutKey = String(variant.shader_point);
|
|
1000
|
+
if (this.vertex_layouts[layoutKey] == null) {
|
|
1001
|
+
// Attributes for this mesh variant
|
|
1002
|
+
// Optional attributes have placeholder values assigned with `static` parameter
|
|
1003
|
+
// TODO: could support optional attributes for selection and offset, but may not be worth it
|
|
1004
|
+
// since points generally don't consume much memory anyway
|
|
1005
|
+
const attribs = [
|
|
1006
|
+
{ name: 'a_position', size: 4, type: gl.SHORT, normalized: false },
|
|
1007
|
+
{ name: 'a_shape', size: 4, type: gl.SHORT, normalized: false },
|
|
1008
|
+
{ name: 'a_texcoord', size: 2, type: gl.UNSIGNED_SHORT, normalized: true, static: (variant.shader_point ? [0, 0] : null) },
|
|
1009
|
+
{ name: 'a_offset', size: 2, type: gl.SHORT, normalized: false },
|
|
1010
|
+
{ name: 'a_color', size: 4, type: gl.UNSIGNED_BYTE, normalized: true },
|
|
1011
|
+
{ name: 'a_selection_color', size: 4, type: gl.UNSIGNED_BYTE, normalized: true, static: (variant.selection ? null : [0, 0, 0, 0]) },
|
|
1012
|
+
{ name: 'a_outline_color', size: 4, type: gl.UNSIGNED_BYTE, normalized: true, static: (variant.shader_point ? null : [0, 0, 0, 0]) },
|
|
1013
|
+
{ name: 'a_outline_edge', size: 1, type: gl.FLOAT, normalized: false, static: (variant.shader_point ? null : 0) }
|
|
1014
|
+
];
|
|
1015
|
+
|
|
1016
|
+
this.addCustomAttributesToAttributeList(attribs);
|
|
1017
|
+
if (this.cpu_projection) attribs.push({name: 'a_projected_position', size: 3, type: gl.FLOAT, normalized: false});
|
|
1018
|
+
this.vertex_layouts[layoutKey] = new VertexLayout(attribs);
|
|
1019
|
+
}
|
|
1020
|
+
return this.vertex_layouts[layoutKey];
|
|
1021
|
+
|
|
1022
|
+
},
|
|
1023
|
+
|
|
1024
|
+
// Override
|
|
1025
|
+
meshVariantTypeForDraw (draw) {
|
|
1026
|
+
const texture = draw.label_texture || draw.texture || SHADER_POINT_VARIANT; // unique key by texture name
|
|
1027
|
+
const key = texture + '/' + draw.blend_order;
|
|
1028
|
+
if (this.variants[key] == null) {
|
|
1029
|
+
this.variants[key] = {
|
|
1030
|
+
key,
|
|
1031
|
+
selection: 1, // TODO: make this vary by draw params
|
|
1032
|
+
shader_point: (texture === SHADER_POINT_VARIANT), // is shader point
|
|
1033
|
+
point_type: draw.label_texture ? TANGRAM_POINT_TYPE_LABEL :
|
|
1034
|
+
draw.texture ? TANGRAM_POINT_TYPE_TEXTURE : TANGRAM_POINT_TYPE_SHADER,
|
|
1035
|
+
blend_order: draw.blend_order,
|
|
1036
|
+
mesh_order: (draw.label_texture ? 1 : 0) // put text on top of points (e.g. for highway shields, etc.)
|
|
1037
|
+
};
|
|
1038
|
+
}
|
|
1039
|
+
return this.variants[key]; // return pre-calculated mesh variant
|
|
1040
|
+
},
|
|
1041
|
+
|
|
1042
|
+
// Override
|
|
1043
|
+
makeMesh (vertex_data, vertex_elements, options = {}) {
|
|
1044
|
+
// Add label fade time
|
|
1045
|
+
options = Object.assign({}, options, { fade_in_time: this.fade_in_time });
|
|
1046
|
+
return Style.makeMesh.call(this, vertex_data, vertex_elements, options);
|
|
1047
|
+
}
|
|
1048
|
+
|
|
1049
|
+
};
|
|
1050
|
+
|
|
1051
|
+
Object.assign(Points, pointMethods);
|
|
1052
|
+
|
|
1053
|
+
/** Cached point/sprite dimensions are a mutable numeric pair, retained without copying. */
|
|
1054
|
+
function isPointPair(value: unknown): value is LabelPointCoordinate {
|
|
1055
|
+
return Array.isArray(value) && value.length === 2 && typeof value[0] === 'number' && typeof value[1] === 'number';
|
|
1056
|
+
}
|
|
1057
|
+
|
|
1058
|
+
/** Validate only the geometry consumed by an attached text anchor, preserving parent identity. */
|
|
1059
|
+
function isPointParent(style: PointFeatureStyle): style is PointFeatureStyle & import('../../labels/label-types').LabelParent {
|
|
1060
|
+
return isPointPair(style.size) && isPointPair(style.offset);
|
|
1061
|
+
}
|
|
1062
|
+
|
|
1063
|
+
/** Shared layout fields are filled before point construction; text-only fields are optional. */
|
|
1064
|
+
function isCommonLayout(layout: Partial<import('../../labels/label-types').LabelLayout>): layout is import('../../labels/label-types').LabelLayout {
|
|
1065
|
+
return typeof layout.priority === 'number' && typeof layout.units_per_pixel === 'number' && isPointPair(layout.offset) && isPointPair(layout.buffer);
|
|
1066
|
+
}
|
|
1067
|
+
|
|
1068
|
+
/** Worker texture metadata contains the same dimensional capabilities as live textures. */
|
|
1069
|
+
function isPointSizeImage(value: object): value is import('../property-types').PointSizeImage {
|
|
1070
|
+
return 'css_size' in value && Array.isArray(value.css_size) && 'aspect' in value && typeof value.aspect === 'number';
|
|
1071
|
+
}
|
|
1072
|
+
|
|
1073
|
+
/** Preserve legacy falsy link identities while admitting only serializable label IDs. */
|
|
1074
|
+
function getLinkedLabelId(link: import('../../labels/collision-types').PendingCollisionObject | null | 0 | '' | undefined): number | string | null | undefined {
|
|
1075
|
+
if (!link) return link;
|
|
1076
|
+
const label = link.label;
|
|
1077
|
+
const identity = label && 'id' in label ? label.id : undefined;
|
|
1078
|
+
return typeof identity === 'number' || typeof identity === 'string' ? identity : undefined;
|
|
1079
|
+
}
|