@tangram.gl/tangram-renderer 0.0.0-stage → 0.0.0
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 +59 -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,273 @@
|
|
|
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 Label from './label';
|
|
7
|
+
import RepeatGroup from './repeat_group';
|
|
8
|
+
import CollisionGrid from './collision_grid';
|
|
9
|
+
import log from '../utils/log';
|
|
10
|
+
import type {CollisionObject, CollisionTileState, PendingCollisionObject} from './collision-types';
|
|
11
|
+
|
|
12
|
+
const Collision = {
|
|
13
|
+
|
|
14
|
+
/** Started tile batches, removed after completion or abort. */
|
|
15
|
+
tiles: {} as Partial<Record<string, CollisionTileState>>,
|
|
16
|
+
/** Optional spatial index; null uses each tile's placed bounds. */
|
|
17
|
+
grid: null as CollisionGrid | null, // no collision grid by default
|
|
18
|
+
|
|
19
|
+
/** Select grid-based or tile-wide collision testing. */
|
|
20
|
+
initGrid (options?: {anchor: {x: number; y: number}; span: number} | null): void {
|
|
21
|
+
if (options == null) {
|
|
22
|
+
this.grid = null;
|
|
23
|
+
}
|
|
24
|
+
else {
|
|
25
|
+
this.grid = new CollisionGrid(options.anchor, options.span);
|
|
26
|
+
}
|
|
27
|
+
},
|
|
28
|
+
|
|
29
|
+
/** Initialize a batch before registering/submitting its contributing styles. */
|
|
30
|
+
startTile (tile: string | number, { apply_repeat_groups = true, return_hidden = false }: {
|
|
31
|
+
apply_repeat_groups?: boolean; return_hidden?: boolean
|
|
32
|
+
} = {}): void {
|
|
33
|
+
let state: CollisionTileState = this.tiles[tile] = {
|
|
34
|
+
bboxes: { // current set of placed bounding boxes
|
|
35
|
+
aabb: [],
|
|
36
|
+
obb: []
|
|
37
|
+
},
|
|
38
|
+
objects: {}, // objects to collide, grouped by priority, then by style
|
|
39
|
+
labels: {}, // objects post-collision, grouped by style, marked as show/hide
|
|
40
|
+
styles: {}, // styles contributing collision objects
|
|
41
|
+
repeat: apply_repeat_groups,
|
|
42
|
+
return_hidden
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
// Promise resolved when all registered styles have added objects
|
|
46
|
+
if (state.complete == null) {
|
|
47
|
+
state.complete = new Promise<void | CollisionObject[]>((resolve, reject) => {
|
|
48
|
+
state.resolve = resolve;
|
|
49
|
+
state.reject = reject;
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
},
|
|
53
|
+
|
|
54
|
+
/** Drop all collision state for a completed or abandoned tile. */
|
|
55
|
+
resetTile (tile: string | number | undefined): void {
|
|
56
|
+
delete this.tiles[String(tile)];
|
|
57
|
+
},
|
|
58
|
+
|
|
59
|
+
/** Resolve pending consumers before removing an abandoned batch. */
|
|
60
|
+
abortTile (tile: string | number | undefined): void {
|
|
61
|
+
const key = String(tile);
|
|
62
|
+
if (this.tiles[key] && this.tiles[key].resolve) {
|
|
63
|
+
this.tiles[key].resolve([]);
|
|
64
|
+
}
|
|
65
|
+
this.resetTile(tile);
|
|
66
|
+
},
|
|
67
|
+
|
|
68
|
+
// Add a style to the pending set, collision will block on all styles submitting to collision set
|
|
69
|
+
/** Register a contributor on an already-started tile. */
|
|
70
|
+
addStyle (style: string, tile: string): void {
|
|
71
|
+
this.tiles[tile]!.styles[style] = true;
|
|
72
|
+
},
|
|
73
|
+
|
|
74
|
+
// Add collision objects for a style
|
|
75
|
+
/** Submit one style and return the same containers, retaining their payload types. */
|
|
76
|
+
collide<T extends CollisionObject> (objects: readonly T[], style: string, tile: string): Promise<T[]> {
|
|
77
|
+
let state = this.tiles[tile];
|
|
78
|
+
if (!state) {
|
|
79
|
+
log('trace', 'Collision.collide() called with null tile', tile, this.tiles, style, objects);
|
|
80
|
+
return Promise.resolve([]);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
// Group by priority and style
|
|
84
|
+
let tile_objects = state.objects;
|
|
85
|
+
for (let i=0; i < objects.length; i++) {
|
|
86
|
+
let obj = objects[i];
|
|
87
|
+
let priority = obj.label.layout.priority;
|
|
88
|
+
tile_objects[priority] = tile_objects[priority] || {};
|
|
89
|
+
tile_objects[priority][style] = tile_objects[priority][style] || [];
|
|
90
|
+
tile_objects[priority][style].push(obj);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
// Remove from pending style set, if no more styles, do collision & finish tile
|
|
94
|
+
delete state.styles[style];
|
|
95
|
+
if (Object.keys(state.styles).length === 0) {
|
|
96
|
+
this.endTile(tile);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
// Wait for objects to be added from all styles
|
|
100
|
+
return state.complete!.then(() => {
|
|
101
|
+
state.resolve = null;
|
|
102
|
+
// Each style's result contains only containers submitted by that style.
|
|
103
|
+
return state.labels[style] as T[] || [];
|
|
104
|
+
});
|
|
105
|
+
},
|
|
106
|
+
|
|
107
|
+
// Test labels for collisions, higher to lower priority
|
|
108
|
+
// When two collide, hide the lower-priority label
|
|
109
|
+
/** Resolve an already-started batch in priority/style submission order. */
|
|
110
|
+
endTile (tile: string): void {
|
|
111
|
+
let state = this.tiles[tile]!;
|
|
112
|
+
let labels = state.labels;
|
|
113
|
+
|
|
114
|
+
if (this.grid) {
|
|
115
|
+
this.addLabelsToGrid(tile);
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
if (state.repeat) {
|
|
119
|
+
RepeatGroup.clear(tile);
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
// Process labels by priority, then by style
|
|
123
|
+
let priorities = Object.keys(state.objects).sort((a, b) => Number(a) - Number(b));
|
|
124
|
+
for (let p=0; p < priorities.length; p++) {
|
|
125
|
+
let style_objects = state.objects[priorities[p]];
|
|
126
|
+
if (!style_objects) { // no labels at this priority, skip to next
|
|
127
|
+
continue;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
// For each style
|
|
131
|
+
for (let style in style_objects) {
|
|
132
|
+
let objects = style_objects[style];
|
|
133
|
+
labels[style] = labels[style] || [];
|
|
134
|
+
|
|
135
|
+
for (let i = 0; i < objects.length; i++) {
|
|
136
|
+
let object = objects[i];
|
|
137
|
+
if (object.linked && !isPreparedCollisionObject(object.linked)) throw new TypeError('Collision partner has not completed label preparation');
|
|
138
|
+
if (this.canBePlaced(object, tile, object.linked, state)) {
|
|
139
|
+
// show object if it isn't dependent on a parent object
|
|
140
|
+
if (!object.linked) {
|
|
141
|
+
object.show = true;
|
|
142
|
+
labels[style].push(object);
|
|
143
|
+
this.place(object, tile, state);
|
|
144
|
+
}
|
|
145
|
+
// If object is dependent on a parent, only show if both can be placed
|
|
146
|
+
else if (this.canBePlaced(object.linked, tile, object, state)) {
|
|
147
|
+
object.show = true;
|
|
148
|
+
|
|
149
|
+
// If a label is breach, its linked label should be considered breach as well
|
|
150
|
+
// (this keeps linked labels (in)visible in tandem)
|
|
151
|
+
if (object.label.breach || object.linked.label.breach) {
|
|
152
|
+
object.label.breach = true;
|
|
153
|
+
object.linked.label.breach = true;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
// Similarly for labels that need main thread repeat culling, keep linked labels in sync
|
|
157
|
+
if (object.label.may_repeat_across_tiles || object.linked.label.may_repeat_across_tiles) {
|
|
158
|
+
object.label.may_repeat_across_tiles = true;
|
|
159
|
+
object.linked.label.may_repeat_across_tiles = true;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
labels[style].push(object);
|
|
163
|
+
this.place(object, tile, state);
|
|
164
|
+
this.place(object.linked, tile, state);
|
|
165
|
+
}
|
|
166
|
+
else if (state.return_hidden) {
|
|
167
|
+
object.show = false;
|
|
168
|
+
labels[style].push(object);
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
else if (state.return_hidden) {
|
|
172
|
+
object.show = false;
|
|
173
|
+
labels[style].push(object);
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
delete this.tiles[tile];
|
|
180
|
+
state.resolve!();
|
|
181
|
+
},
|
|
182
|
+
|
|
183
|
+
/** Index candidates after grid initialization and before placement starts. */
|
|
184
|
+
addLabelsToGrid (tile_id: string): void {
|
|
185
|
+
// Process labels by priority, then by style
|
|
186
|
+
const tile = this.tiles[tile_id]!;
|
|
187
|
+
for (const priority in tile.objects) {
|
|
188
|
+
const style_objects = tile.objects[priority];
|
|
189
|
+
if (!style_objects) { // no labels at this priority, skip to next
|
|
190
|
+
continue;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
// For each style
|
|
194
|
+
for (const style in style_objects) {
|
|
195
|
+
const objects = style_objects[style];
|
|
196
|
+
objects.forEach(object => this.grid!.addLabel(object.label));
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
},
|
|
200
|
+
|
|
201
|
+
// Run collision and repeat check to see if label can currently be placed
|
|
202
|
+
/** Check a candidate, preserving previously resolved placement outcomes. */
|
|
203
|
+
canBePlaced (object: CollisionObject, tile: string, exclude: PendingCollisionObject | null | 0 | '' = null, { repeat = true }: {repeat?: boolean} = {}): boolean | null | undefined {
|
|
204
|
+
let label = object.label;
|
|
205
|
+
let layout = object.label.layout;
|
|
206
|
+
|
|
207
|
+
// Skip if already processed (e.g. by parent object)
|
|
208
|
+
if (label.placed != null) {
|
|
209
|
+
return label.placed;
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
let placeable = !layout.collide;
|
|
213
|
+
if (!placeable) {
|
|
214
|
+
// Test the label for intersections with other labels
|
|
215
|
+
if (this.grid && label.cells) {
|
|
216
|
+
// test label candidate against labels placed in each grid cell
|
|
217
|
+
placeable = label.cells.reduce((keep, cell) => {
|
|
218
|
+
if (keep && label.discard(cell, (exclude && exclude.label) ?? null)) {
|
|
219
|
+
keep = false;
|
|
220
|
+
}
|
|
221
|
+
return keep;
|
|
222
|
+
}, true);
|
|
223
|
+
}
|
|
224
|
+
else {
|
|
225
|
+
placeable = !label.discard(this.tiles[tile]!.bboxes, (exclude && exclude.label) ?? null);
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
if (placeable) {
|
|
230
|
+
// repeat culling with nearby labels
|
|
231
|
+
if (repeat && RepeatGroup.check(label, layout, tile)) {
|
|
232
|
+
label.placed = false;
|
|
233
|
+
}
|
|
234
|
+
else {
|
|
235
|
+
return true;
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
else if (layout.collide) {
|
|
239
|
+
// log('trace', `hide label '${label.text}' due to collision`);
|
|
240
|
+
label.placed = false;
|
|
241
|
+
}
|
|
242
|
+
return label.placed;
|
|
243
|
+
},
|
|
244
|
+
|
|
245
|
+
// Place label
|
|
246
|
+
/** Register a candidate once, after its dependency and repeat checks succeed. */
|
|
247
|
+
place ({ label }: CollisionObject, tile: string, { repeat = true }: {repeat?: boolean}): void {
|
|
248
|
+
// Skip if already processed (e.g. by parent object)
|
|
249
|
+
if (label.placed != null) {
|
|
250
|
+
return;
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
// Register as placed for future collision and repeat culling
|
|
254
|
+
if (repeat) {
|
|
255
|
+
RepeatGroup.add(label, label.layout, tile);
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
if (this.grid && label.cells) {
|
|
259
|
+
label.cells.forEach(cell => Label.add(label, cell));
|
|
260
|
+
}
|
|
261
|
+
else {
|
|
262
|
+
Label.add(label, this.tiles[tile]!.bboxes);
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
};
|
|
267
|
+
|
|
268
|
+
export default Collision;
|
|
269
|
+
|
|
270
|
+
/** Linked text may queue before measurement, but placement requires its concrete label. */
|
|
271
|
+
function isPreparedCollisionObject(object: PendingCollisionObject): object is CollisionObject {
|
|
272
|
+
return object.label !== undefined;
|
|
273
|
+
}
|
|
@@ -0,0 +1,61 @@
|
|
|
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 type {CollisionBounds} from './collision-types';
|
|
7
|
+
/** Grid origin in Tangram's negative-Y tile coordinate system. */
|
|
8
|
+
type Anchor = {x: number; y: number};
|
|
9
|
+
/** Geometry required to assign a candidate's single or multiple collision boxes. */
|
|
10
|
+
type LabelLike = {aabb?: number[] | null; aabbs?: number[][]; cells?: CollisionBounds[]};
|
|
11
|
+
/** Spatial index sharing placed-label bounds between overlapping candidates. */
|
|
12
|
+
export default class CollisionGrid {
|
|
13
|
+
/** Coordinate origin used for cell indexing. */
|
|
14
|
+
anchor: Anchor;
|
|
15
|
+
/** Cell size in the same units as candidate bounds. */
|
|
16
|
+
span: number;
|
|
17
|
+
/** Rows and columns containing placed-label bounds. */
|
|
18
|
+
cells: Record<number, Record<number, CollisionBounds>>;
|
|
19
|
+
|
|
20
|
+
/** Create an empty grid anchored in the current collision coordinate system. */
|
|
21
|
+
constructor (anchor: Anchor, span: number) {
|
|
22
|
+
this.anchor = anchor;
|
|
23
|
+
this.span = span;
|
|
24
|
+
this.cells = {};
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/** Assign candidate bounds to grid cells without placing the candidate. */
|
|
28
|
+
addLabel (label: LabelLike): void {
|
|
29
|
+
if (label.aabb) {
|
|
30
|
+
this.addLabelBboxes(label, label.aabb);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
if (label.aabbs) {
|
|
34
|
+
label.aabbs.forEach(aabb => this.addLabelBboxes(label, aabb));
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/** Index one box, retaining the existing per-box candidate-cell assignment. */
|
|
39
|
+
addLabelBboxes (label: LabelLike, aabb: number[]): void {
|
|
40
|
+
// min/max cells that the label falls into
|
|
41
|
+
// keep grid coordinates at zero or above so any labels that go "below" the anchor are in the lowest grid cell
|
|
42
|
+
const cell_bounds = [
|
|
43
|
+
Math.max(Math.floor((aabb[0] - this.anchor.x) / this.span), 0),
|
|
44
|
+
Math.max(Math.floor(-(aabb[1] - this.anchor.y) / this.span), 0),
|
|
45
|
+
Math.max(Math.floor((aabb[2] - this.anchor.x) / this.span), 0),
|
|
46
|
+
Math.max(Math.floor(-(aabb[3] - this.anchor.y) / this.span), 0)
|
|
47
|
+
];
|
|
48
|
+
|
|
49
|
+
label.cells = []; // label knows which cells it falls in
|
|
50
|
+
|
|
51
|
+
// initialize each grid cell as necessary, and add to label's list of cells
|
|
52
|
+
for (let gy = cell_bounds[1]; gy <= cell_bounds[3]; gy++) {
|
|
53
|
+
this.cells[gy] = this.cells[gy] || {};
|
|
54
|
+
for (let gx = cell_bounds[0]; gx <= cell_bounds[2]; gx++) {
|
|
55
|
+
this.cells[gy][gx] = this.cells[gy][gx] || { aabb: [], obb: [] };
|
|
56
|
+
label.cells.push(this.cells[gy][gx]);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
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
|
+
type BoundingBox = [number, number, number, number];
|
|
7
|
+
|
|
8
|
+
// Do AABB `a` and `b` intersect?
|
|
9
|
+
export function boxIntersectsBox (a: BoundingBox, b: BoundingBox): boolean {
|
|
10
|
+
if (a[2] < b[0] || // a is left of b
|
|
11
|
+
a[0] > b[2] || // a is right of b
|
|
12
|
+
a[3] < b[1] || // a is above b
|
|
13
|
+
a[1] > b[3]) { // a is below b
|
|
14
|
+
return false;
|
|
15
|
+
}
|
|
16
|
+
return true; // boxes overlap
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
// Does AABB `a` intersect any of the AABBs in array `boxes`?
|
|
20
|
+
// Invokes `callback` with index of intersecting box
|
|
21
|
+
// Stops intersecting if `callback` returns non-null value (continues otherwise)
|
|
22
|
+
export function boxIntersectsList (a: BoundingBox, boxes: BoundingBox[], callback: (index: number) => unknown): void {
|
|
23
|
+
for (let i=0; i < boxes.length; i++) {
|
|
24
|
+
if (boxIntersectsBox(a, boxes[i])) {
|
|
25
|
+
if (callback(i) != null) {
|
|
26
|
+
break;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
}
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
// tangram-layers
|
|
2
|
+
// SPDX-License-Identifier: MIT
|
|
3
|
+
// Copyright (c) vis.gl contributors
|
|
4
|
+
|
|
5
|
+
import type {CollisionLayout} from './collision-types';
|
|
6
|
+
import type OBB from '../utils/obb';
|
|
7
|
+
|
|
8
|
+
/** Mutable pixel or tile-coordinate pair. */
|
|
9
|
+
export type LabelPointCoordinate = [number, number];
|
|
10
|
+
|
|
11
|
+
/** Normalized layout supplied by point/text styles before label construction. */
|
|
12
|
+
export interface LabelLayout extends CollisionLayout {
|
|
13
|
+
/** Screen-space collision policy deferred until projected cameras are known. */
|
|
14
|
+
projected_collide?: boolean;
|
|
15
|
+
/** Source/style/feature identity for buffered copies, absent for anonymous points. */
|
|
16
|
+
projected_identity?: string;
|
|
17
|
+
/** Quantized physical anchor height in meters; omitted for nonprojected labels. */
|
|
18
|
+
projected_height?: number;
|
|
19
|
+
/** Pixel offset before geographic scaling. */
|
|
20
|
+
offset: LabelPointCoordinate;
|
|
21
|
+
/** Collision padding in pixels. */
|
|
22
|
+
buffer: LabelPointCoordinate;
|
|
23
|
+
/** Tile units per screen pixel. */
|
|
24
|
+
units_per_pixel: number;
|
|
25
|
+
/** Candidate point anchors, tried in order. */
|
|
26
|
+
anchor?: string | string[];
|
|
27
|
+
/** Canvas text alignment override. */
|
|
28
|
+
align?: string;
|
|
29
|
+
/** Extra collision width for non-normal fonts. */
|
|
30
|
+
italic?: boolean;
|
|
31
|
+
/** Point label carrying an attached text label. */
|
|
32
|
+
parent?: LabelParent;
|
|
33
|
+
/** Maximum line-placement subdivisions. */
|
|
34
|
+
subdiv?: number;
|
|
35
|
+
/** Raster padding excluded from oriented offsets. */
|
|
36
|
+
vertical_buffer?: number;
|
|
37
|
+
/** Whether contextual shaping requires straight placement. */
|
|
38
|
+
no_curving?: boolean;
|
|
39
|
+
/** Side of a boundary line: left (-1) or right (1). */
|
|
40
|
+
orientation?: number;
|
|
41
|
+
/** Point-placement strategy identifier. */
|
|
42
|
+
placement?: number;
|
|
43
|
+
/** Minimum segment length relative to label size. */
|
|
44
|
+
placement_min_length_ratio?: number;
|
|
45
|
+
/** Requested spacing along a line in pixels. */
|
|
46
|
+
placement_spacing?: number;
|
|
47
|
+
/** Whether points may be placed outside tile bounds. */
|
|
48
|
+
tile_edges?: boolean;
|
|
49
|
+
/** Fixed point rotation or automatic line rotation. */
|
|
50
|
+
angle?: number | 'auto';
|
|
51
|
+
/** Whether this layout uses vertex placement. */
|
|
52
|
+
vertex?: boolean;
|
|
53
|
+
/** Optional feature identity retained for debugging. */
|
|
54
|
+
id?: unknown;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/** Point geometry used to anchor attached text. */
|
|
58
|
+
export interface LabelParent {
|
|
59
|
+
/** Parent's unscaled dimensions. */
|
|
60
|
+
size: LabelPointCoordinate;
|
|
61
|
+
/** Parent's point anchor. */
|
|
62
|
+
anchor?: string | string[];
|
|
63
|
+
/** Parent's pixel offset. */
|
|
64
|
+
offset: LabelPointCoordinate;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/** Geometry serialized across the worker/main-thread collision boundary. */
|
|
68
|
+
export interface SerializedLabel {
|
|
69
|
+
/** Worker-scoped label identity. */
|
|
70
|
+
id: number;
|
|
71
|
+
/** Point, straight, or curved placement kind. */
|
|
72
|
+
type: string;
|
|
73
|
+
/** Original tile-local position. */
|
|
74
|
+
position: LabelPointCoordinate;
|
|
75
|
+
/** Minimal repeat/collision settings. */
|
|
76
|
+
layout: Pick<LabelLayout, 'priority' | 'collide' | 'repeat_distance' | 'repeat_group' | 'buffer' | 'italic' | 'projected_collide' | 'projected_identity' | 'projected_height'>;
|
|
77
|
+
/** Point/straight dimensions, absent for articulated text. */
|
|
78
|
+
size?: LabelPointCoordinate;
|
|
79
|
+
/** Rotation for point/straight placement. */
|
|
80
|
+
angle?: number;
|
|
81
|
+
/** Pixel offset for point/straight placement. */
|
|
82
|
+
offset?: LabelPointCoordinate;
|
|
83
|
+
/** Point-oriented box snapshot. */
|
|
84
|
+
obb?: ReturnType<OBB['toJSON']>;
|
|
85
|
+
/** Curved per-segment box snapshots. */
|
|
86
|
+
obbs?: ReturnType<OBB['toJSON']>[];
|
|
87
|
+
/** Whether this label crosses the tile boundary. */
|
|
88
|
+
breach?: boolean;
|
|
89
|
+
/** Whether repeat culling must run again on the main thread. */
|
|
90
|
+
may_repeat_across_tiles?: boolean;
|
|
91
|
+
}
|
|
@@ -0,0 +1,207 @@
|
|
|
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 type {CollisionBounds, CollisionLabel} from './collision-types';
|
|
7
|
+
import type {LabelLayout, LabelPointCoordinate, SerializedLabel} from './label-types';
|
|
8
|
+
import PointAnchor from './point_anchor';
|
|
9
|
+
import {boxIntersectsList} from './intersect';
|
|
10
|
+
import OBB from '../utils/obb';
|
|
11
|
+
import Geo from '../utils/geo';
|
|
12
|
+
// import log from '../utils/log';
|
|
13
|
+
|
|
14
|
+
export default class Label {
|
|
15
|
+
|
|
16
|
+
static epsilon: number;
|
|
17
|
+
static nextLabelId: () => number;
|
|
18
|
+
static add: (label: CollisionLabel, bboxes: CollisionBounds) => void;
|
|
19
|
+
static id: number;
|
|
20
|
+
static id_prefix: number;
|
|
21
|
+
static id_multiplier: number;
|
|
22
|
+
id: number;
|
|
23
|
+
type: string;
|
|
24
|
+
/** Unscaled width and height in pixels. */
|
|
25
|
+
size: LabelPointCoordinate;
|
|
26
|
+
/** Normalized collision and positioning rules supplied by a style. */
|
|
27
|
+
layout: LabelLayout;
|
|
28
|
+
/** Tile-local position, installed by a concrete label after base construction. */
|
|
29
|
+
position: LabelPointCoordinate | null;
|
|
30
|
+
/** Rotation in radians. */
|
|
31
|
+
angle: number;
|
|
32
|
+
/** Current candidate anchor, possibly changed by point collision retries. */
|
|
33
|
+
anchor: string | undefined;
|
|
34
|
+
/** Collision outcome; null means not processed. */
|
|
35
|
+
placed: boolean | null;
|
|
36
|
+
/** Pixel offset before scaling into tile coordinates. */
|
|
37
|
+
offset: LabelPointCoordinate;
|
|
38
|
+
/** Tile units per pixel. */
|
|
39
|
+
unit_scale: number;
|
|
40
|
+
/** Broad-phase extent, absent until bounds are constructed. */
|
|
41
|
+
aabb: number[] | null;
|
|
42
|
+
/** Corresponding oriented collision box. */
|
|
43
|
+
obb: OBB | null;
|
|
44
|
+
/** Optional broad-phase extents for segmented geometry. */
|
|
45
|
+
aabbs: number[][] | undefined;
|
|
46
|
+
/** Oriented boxes corresponding to segmented extents. */
|
|
47
|
+
obbs: OBB[] | undefined;
|
|
48
|
+
/** Canvas text alignment derived from the anchor or explicit layout. */
|
|
49
|
+
align: string;
|
|
50
|
+
/** Whether geometry fitting rejected the label. */
|
|
51
|
+
throw_away: boolean;
|
|
52
|
+
/** Whether bounds cross a tile edge. */
|
|
53
|
+
breach?: boolean;
|
|
54
|
+
/** Whether a second repeat-distance pass is needed across tiles. */
|
|
55
|
+
may_repeat_across_tiles?: boolean;
|
|
56
|
+
|
|
57
|
+
/** Concrete labels supply normalized layouts; the legacy empty base default remains supported. */
|
|
58
|
+
constructor (size: LabelPointCoordinate, layout: LabelLayout = {} as LabelLayout) {
|
|
59
|
+
this.id = Label.nextLabelId();
|
|
60
|
+
this.type = ''; // set by subclass
|
|
61
|
+
this.size = size;
|
|
62
|
+
this.layout = layout;
|
|
63
|
+
this.position = null;
|
|
64
|
+
this.angle = 0;
|
|
65
|
+
this.anchor = Array.isArray(this.layout.anchor) ? this.layout.anchor[0] : this.layout.anchor; // initial anchor
|
|
66
|
+
this.placed = null;
|
|
67
|
+
this.offset = layout.offset;
|
|
68
|
+
this.unit_scale = this.layout.units_per_pixel;
|
|
69
|
+
this.aabb = null;
|
|
70
|
+
this.obb = null;
|
|
71
|
+
this.align = 'center';
|
|
72
|
+
this.throw_away = false; // if label does not fit (exceeds tile boundary, etc) this boolean will be true
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// Minimal representation of label
|
|
76
|
+
toJSON (): SerializedLabel {
|
|
77
|
+
return {
|
|
78
|
+
id: this.id,
|
|
79
|
+
type: this.type,
|
|
80
|
+
obb: this.obb!.toJSON(),
|
|
81
|
+
position: this.position!,
|
|
82
|
+
angle: this.angle,
|
|
83
|
+
size: this.size,
|
|
84
|
+
offset: this.offset,
|
|
85
|
+
breach: this.breach,
|
|
86
|
+
may_repeat_across_tiles: this.may_repeat_across_tiles,
|
|
87
|
+
layout: textLayoutToJSON(this.layout)
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
update () {
|
|
92
|
+
this.align = this.layout.align || PointAnchor.alignForAnchor(this.anchor);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
// check for overlaps with other labels in the tile
|
|
96
|
+
occluded (this: {aabb: number[] | null; obb: OBB | null}, bboxes: CollisionBounds, exclude: CollisionLabel | null | 0 | '' = null): boolean {
|
|
97
|
+
let intersect = false;
|
|
98
|
+
let aabbs = bboxes.aabb;
|
|
99
|
+
let obbs = bboxes.obb;
|
|
100
|
+
|
|
101
|
+
// Broad phase
|
|
102
|
+
if (aabbs.length > 0) {
|
|
103
|
+
boxIntersectsList(this.aabb! as [number, number, number, number], aabbs as [number, number, number, number][], (j) => {
|
|
104
|
+
// log('trace', 'collision: broad phase collide', this.layout.id, this, this.aabb, aabbs[j]);
|
|
105
|
+
|
|
106
|
+
// Skip if colliding with excluded label
|
|
107
|
+
if (exclude && aabbs[j] === exclude.aabb) {
|
|
108
|
+
// log('trace', 'collision: skipping due to explicit exclusion', this, exclude);
|
|
109
|
+
return;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
// Skip narrow phase collision if no rotation
|
|
113
|
+
if (this.obb!.angle === 0 && obbs[j].angle === 0) {
|
|
114
|
+
// log('trace', 'collision: skip narrow phase collide because neither is rotated', this.layout.id, this, this.obb, obbs[j]);
|
|
115
|
+
intersect = true;
|
|
116
|
+
return true;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
// Narrow phase
|
|
120
|
+
if (OBB.intersect(this.obb!, obbs[j])) {
|
|
121
|
+
// log('trace', 'collision: narrow phase collide', this.layout.id, this, this.obb, obbs[j]);
|
|
122
|
+
intersect = true;
|
|
123
|
+
return true;
|
|
124
|
+
}
|
|
125
|
+
});
|
|
126
|
+
}
|
|
127
|
+
return intersect;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
// checks whether the label is within the tile boundaries
|
|
131
|
+
inTileBounds (this: {aabb: number[] | null}): boolean {
|
|
132
|
+
if ((this.aabb![0] >= 0 && this.aabb![1] > -Geo.tile_scale && this.aabb![0] < Geo.tile_scale && this.aabb![1] <= 0) ||
|
|
133
|
+
(this.aabb![2] >= 0 && this.aabb![3] > -Geo.tile_scale && this.aabb![2] < Geo.tile_scale && this.aabb![3] <= 0)) {
|
|
134
|
+
return true;
|
|
135
|
+
}
|
|
136
|
+
return false;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
// some labels need further repeat culling checks on the main thread
|
|
140
|
+
// checks whether the label is within its repeat distance of the tile boundaries
|
|
141
|
+
mayRepeatAcrossTiles () {
|
|
142
|
+
if (this.layout.collide) {
|
|
143
|
+
return true; // additional collision pass will already apply, so skip further distance checks
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
const dist = this.layout.repeat_distance!;
|
|
147
|
+
if (dist === 0) {
|
|
148
|
+
return false;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
return (Math.abs(this.position![0]) < dist || Math.abs(this.position![0] - Geo.tile_scale) < dist) ||
|
|
152
|
+
(Math.abs(this.position![1]) < dist || Math.abs(-(this.position![1] - Geo.tile_scale)) < dist);
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
// Whether the label should be discarded
|
|
156
|
+
// Depends on whether label must fit in the tile bounds, and if so, can it be moved to fit there
|
|
157
|
+
discard(bboxes: CollisionBounds, exclude: CollisionLabel | null | 0 | '' = null): boolean {
|
|
158
|
+
if (this.throw_away) {
|
|
159
|
+
return true;
|
|
160
|
+
}
|
|
161
|
+
return this.occluded(bboxes, exclude);
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
// Generic label placement function, adds a label's bounding boxes to the currently placed set
|
|
166
|
+
// Supports single or multiple collision boxes
|
|
167
|
+
Label.add = function (label, bboxes) {
|
|
168
|
+
label.placed = true;
|
|
169
|
+
|
|
170
|
+
if (label.aabb) {
|
|
171
|
+
bboxes.aabb.push(label.aabb);
|
|
172
|
+
bboxes.obb.push(label.obb!);
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
if (label.aabbs) {
|
|
176
|
+
for (let i = 0; i < label.aabbs.length; i++) {
|
|
177
|
+
bboxes.aabb.push(label.aabbs[i]);
|
|
178
|
+
bboxes.obb.push(label.obbs![i]);
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
};
|
|
182
|
+
|
|
183
|
+
Label.id = 0;
|
|
184
|
+
Label.id_prefix = 0; // id prefix scoped to worker thread
|
|
185
|
+
Label.id_multiplier = 0; // multiplier to keep label ids distinct across threads
|
|
186
|
+
|
|
187
|
+
Label.nextLabelId = function () {
|
|
188
|
+
return Label.id_prefix + ((Label.id++) * Label.id_multiplier);
|
|
189
|
+
};
|
|
190
|
+
|
|
191
|
+
Label.epsilon = 0.9999; // tolerance around collision boxes, prevent perfectly adjacent objects from colliding
|
|
192
|
+
|
|
193
|
+
// Minimal representation of text layout, sent to main thread for label collisions
|
|
194
|
+
export function textLayoutToJSON (layout: LabelLayout): SerializedLabel['layout'] {
|
|
195
|
+
return {
|
|
196
|
+
priority: layout.priority,
|
|
197
|
+
...(layout.projected_collide === undefined ? {} : {projected_collide: layout.projected_collide}),
|
|
198
|
+
...(layout.projected_identity === undefined ? {} : {projected_identity: layout.projected_identity}),
|
|
199
|
+
...(layout.projected_height === undefined ? {} : {projected_height: layout.projected_height}),
|
|
200
|
+
collide: layout.collide,
|
|
201
|
+
repeat_distance: layout.projected_collide === undefined ? layout.repeat_distance :
|
|
202
|
+
(layout.repeat_distance ?? 0) / layout.units_per_pixel,
|
|
203
|
+
repeat_group: layout.repeat_group,
|
|
204
|
+
buffer: layout.buffer,
|
|
205
|
+
italic: layout.italic // affects bounding box size
|
|
206
|
+
};
|
|
207
|
+
}
|