@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.
Files changed (321) hide show
  1. package/README.md +59 -2
  2. package/dist/core.js +36923 -0
  3. package/dist/core.js.map +6 -0
  4. package/dist/index.js +32 -0
  5. package/dist/loaders-gl-worker.js +82 -0
  6. package/dist/loaders-gl-worker.js.map +6 -0
  7. package/dist/map-logic.js +2 -0
  8. package/dist/map-logic.js.map +6 -0
  9. package/dist/projected-basemaps-worker.js +3 -0
  10. package/dist/projected-basemaps-worker.js.map +6 -0
  11. package/dist/shader-modules.js +141 -0
  12. package/dist/shader-modules.js.map +6 -0
  13. package/dist/style-schema.js +126 -0
  14. package/dist/style-schema.js.map +6 -0
  15. package/dist/tangram-style.schema.json +882 -0
  16. package/dist/tangram.debug.mjs +37139 -0
  17. package/dist/tangram.debug.mjs.map +6 -0
  18. package/dist/tangram.min.mjs +4950 -0
  19. package/dist/types/builders/earcut.d.ts +15 -0
  20. package/dist/types/core.d.ts +29 -0
  21. package/dist/types/gl/constants.d.ts +2 -0
  22. package/dist/types/gl/globe_mesh.d.ts +48 -0
  23. package/dist/types/gl/glsl.d.ts +17 -0
  24. package/dist/types/gl/vertex-constants.d.ts +4 -0
  25. package/dist/types/gl/vertex-types.d.ts +34 -0
  26. package/dist/types/gl/vertex_data.d.ts +32 -0
  27. package/dist/types/gl/vertex_elements.d.ts +8 -0
  28. package/dist/types/gl/vertex_layout.d.ts +32 -0
  29. package/dist/types/glsl.d.ts +14 -0
  30. package/dist/types/gpu/luma_device_renderer.d.ts +66 -0
  31. package/dist/types/gpu/resource_lifecycle.d.ts +4 -0
  32. package/dist/types/gpu/tangram_gpu_backend.d.ts +151 -0
  33. package/dist/types/index.d.ts +155 -0
  34. package/dist/types/lights/light-definitions.d.ts +121 -0
  35. package/dist/types/map-logic/draped-terrain-bounds.d.ts +41 -0
  36. package/dist/types/map-logic/globe-terrain-preparation.d.ts +85 -0
  37. package/dist/types/map-logic/globe-terrain.d.ts +54 -0
  38. package/dist/types/map-logic/index.d.ts +33 -0
  39. package/dist/types/map-logic/label-identity.d.ts +22 -0
  40. package/dist/types/map-logic/label-placement.d.ts +32 -0
  41. package/dist/types/map-logic/screen-bounds.d.ts +15 -0
  42. package/dist/types/map-logic/terrain-data.d.ts +55 -0
  43. package/dist/types/map-logic/terrain-error.d.ts +51 -0
  44. package/dist/types/map-logic/terrain-ground-topology.d.ts +26 -0
  45. package/dist/types/map-logic/terrain-height-field.d.ts +59 -0
  46. package/dist/types/map-logic/terrain-navigation.d.ts +27 -0
  47. package/dist/types/map-logic/terrain-neighborhood.d.ts +5 -0
  48. package/dist/types/map-logic/terrain-query.d.ts +33 -0
  49. package/dist/types/map-logic/terrain-resource-budget.d.ts +51 -0
  50. package/dist/types/map-logic/terrain-transition-topology.d.ts +23 -0
  51. package/dist/types/map-logic/tile-build-queue.d.ts +51 -0
  52. package/dist/types/map-logic/tile-cache-policy.d.ts +46 -0
  53. package/dist/types/map-logic/tile-residency.d.ts +17 -0
  54. package/dist/types/map-logic/triangle-query-index.d.ts +87 -0
  55. package/dist/types/procedures/js-yaml.d.ts +15 -0
  56. package/dist/types/procedures/mesh-projector.d.ts +107 -0
  57. package/dist/types/procedures/mvt-dependencies.d.ts +20 -0
  58. package/dist/types/procedures/projected-coordinate-transform.d.ts +27 -0
  59. package/dist/types/procedures/projection-batch-executor.d.ts +53 -0
  60. package/dist/types/procedures/scene-yaml-legacy.d.ts +6 -0
  61. package/dist/types/procedures/terrain-draping.d.ts +165 -0
  62. package/dist/types/procedures/terrain-globe.d.ts +6 -0
  63. package/dist/types/procedures/terrain-styled-topology.d.ts +34 -0
  64. package/dist/types/procedures/web-mercator-legacy.d.ts +4 -0
  65. package/dist/types/procedures/web-mercator-math.d.ts +4 -0
  66. package/dist/types/scene/globals.d.ts +11 -0
  67. package/dist/types/scene/ground_footprint.d.ts +16 -0
  68. package/dist/types/scene/host_frame.d.ts +54 -0
  69. package/dist/types/scene/projection_math.d.ts +52 -0
  70. package/dist/types/scene/scene-resource-types.d.ts +177 -0
  71. package/dist/types/scene/scene_bundle.d.ts +69 -0
  72. package/dist/types/scene/visibility_adapter.d.ts +106 -0
  73. package/dist/types/selection/draped-terrain-surface.d.ts +78 -0
  74. package/dist/types/selection/terrain_surface.d.ts +149 -0
  75. package/dist/types/shader-modules/globe-horizon.d.ts +15 -0
  76. package/dist/types/shader-modules/height-decode.d.ts +13 -0
  77. package/dist/types/shader-modules/hillshade.d.ts +13 -0
  78. package/dist/types/shader-modules/index.d.ts +6 -0
  79. package/dist/types/shader-modules/planar.d.ts +12 -0
  80. package/dist/types/shader-modules/sphere-map.d.ts +13 -0
  81. package/dist/types/shader-modules/triplanar.d.ts +15 -0
  82. package/dist/types/sources/decoded_tile_store.d.ts +95 -0
  83. package/dist/types/sources/tile_source_metadata.d.ts +32 -0
  84. package/dist/types/styles/text/fontfaceobserver.d.ts +10 -0
  85. package/dist/types/tile/tile_id.d.ts +37 -0
  86. package/dist/types/tile/tile_traversal_adapter.d.ts +63 -0
  87. package/dist/types/types.d.ts +293 -0
  88. package/dist/types/utils/geo.d.ts +57 -0
  89. package/dist/types/utils/hash.d.ts +1 -0
  90. package/dist/types/utils/log.d.ts +27 -0
  91. package/dist/types/utils/props.d.ts +6 -0
  92. package/dist/types/utils/subscribe.d.ts +10 -0
  93. package/dist/types/utils/thread.d.ts +6 -0
  94. package/dist/types/utils/urls.d.ts +26 -0
  95. package/dist/types/utils/utils.d.ts +26 -0
  96. package/dist/types/utils/version.d.ts +2 -0
  97. package/dist/types/utils/worker-types.d.ts +68 -0
  98. package/dist/types/utils/worker_broker.d.ts +3 -0
  99. package/package.json +103 -3
  100. package/src/builders/common.ts +41 -0
  101. package/src/builders/earcut.d.ts +15 -0
  102. package/src/builders/geometry-types.ts +60 -0
  103. package/src/builders/points.ts +115 -0
  104. package/src/builders/polygons.ts +225 -0
  105. package/src/builders/polylines.ts +682 -0
  106. package/src/builders/wireframe.ts +31 -0
  107. package/src/core.d.ts +29 -0
  108. package/src/core.ts +33 -0
  109. package/src/experimental/loaders-gl-worker.ts +19 -0
  110. package/src/experimental/projected-basemaps-worker.ts +13 -0
  111. package/src/experimental/projected-mesh-cache.ts +137 -0
  112. package/src/experimental/projected-mesh-domain.ts +110 -0
  113. package/src/experimental/projected-mesh-refinement.ts +193 -0
  114. package/src/experimental/projected-mesh.ts +399 -0
  115. package/src/experimental/projected-normal.ts +26 -0
  116. package/src/experimental/projected-symbol-mesh.ts +74 -0
  117. package/src/gl/constants.ts +18 -0
  118. package/src/gl/context.ts +103 -0
  119. package/src/gl/extensions.ts +30 -0
  120. package/src/gl/globe_mesh.ts +285 -0
  121. package/src/gl/glsl.ts +257 -0
  122. package/src/gl/mesh-types.ts +56 -0
  123. package/src/gl/render_state.ts +111 -0
  124. package/src/gl/shader_program.ts +1310 -0
  125. package/src/gl/texture.ts +814 -0
  126. package/src/gl/uniform_buffer.ts +340 -0
  127. package/src/gl/vao.ts +154 -0
  128. package/src/gl/vbo_mesh.ts +349 -0
  129. package/src/gl/vertex-constants.ts +19 -0
  130. package/src/gl/vertex-types.ts +35 -0
  131. package/src/gl/vertex_data.ts +126 -0
  132. package/src/gl/vertex_elements.ts +54 -0
  133. package/src/gl/vertex_layout.ts +241 -0
  134. package/src/glsl.d.ts +14 -0
  135. package/src/gpu/luma_device_renderer.ts +579 -0
  136. package/src/gpu/pixel_readback.ts +51 -0
  137. package/src/gpu/resource_lifecycle.ts +32 -0
  138. package/src/gpu/tangram_gpu_backend.ts +195 -0
  139. package/src/index.d.ts +155 -0
  140. package/src/index.ts +105 -0
  141. package/src/labels/collision-types.ts +94 -0
  142. package/src/labels/collision.ts +273 -0
  143. package/src/labels/collision_grid.ts +61 -0
  144. package/src/labels/intersect.ts +30 -0
  145. package/src/labels/label-types.ts +91 -0
  146. package/src/labels/label.ts +207 -0
  147. package/src/labels/label_line.ts +801 -0
  148. package/src/labels/label_point.ts +131 -0
  149. package/src/labels/main-pass-types.ts +73 -0
  150. package/src/labels/main_pass.ts +221 -0
  151. package/src/labels/point_anchor.ts +100 -0
  152. package/src/labels/point_placement.ts +150 -0
  153. package/src/labels/projected-pass.ts +259 -0
  154. package/src/labels/repeat_group.ts +72 -0
  155. package/src/lights/ambient_light.glsl +18 -0
  156. package/src/lights/directional_light.glsl +39 -0
  157. package/src/lights/geographic-lights.ts +21 -0
  158. package/src/lights/light-definitions.ts +285 -0
  159. package/src/lights/light.ts +590 -0
  160. package/src/lights/lighting-uniforms.ts +80 -0
  161. package/src/lights/lighting-wgsl.ts +116 -0
  162. package/src/lights/material.glsl +237 -0
  163. package/src/lights/material.ts +145 -0
  164. package/src/lights/native-falloff.ts +37 -0
  165. package/src/lights/point_light.glsl +96 -0
  166. package/src/lights/spot_light.glsl +111 -0
  167. package/src/map-logic/draped-terrain-bounds.ts +71 -0
  168. package/src/map-logic/globe-terrain-preparation.ts +217 -0
  169. package/src/map-logic/globe-terrain.ts +296 -0
  170. package/src/map-logic/index.ts +37 -0
  171. package/src/map-logic/label-identity.ts +36 -0
  172. package/src/map-logic/label-placement.ts +117 -0
  173. package/src/map-logic/screen-bounds.ts +34 -0
  174. package/src/map-logic/terrain-data.ts +107 -0
  175. package/src/map-logic/terrain-error.ts +134 -0
  176. package/src/map-logic/terrain-ground-topology.ts +164 -0
  177. package/src/map-logic/terrain-height-field.ts +254 -0
  178. package/src/map-logic/terrain-navigation.ts +50 -0
  179. package/src/map-logic/terrain-neighborhood.ts +43 -0
  180. package/src/map-logic/terrain-query.ts +51 -0
  181. package/src/map-logic/terrain-resource-budget.ts +124 -0
  182. package/src/map-logic/terrain-transition-topology.ts +164 -0
  183. package/src/map-logic/tile-build-queue.ts +110 -0
  184. package/src/map-logic/tile-cache-policy.ts +89 -0
  185. package/src/map-logic/tile-residency.ts +50 -0
  186. package/src/map-logic/triangle-query-index.ts +265 -0
  187. package/src/procedures/js-yaml.d.ts +15 -0
  188. package/src/procedures/mesh-projector.ts +191 -0
  189. package/src/procedures/mlt-loaders.ts +55 -0
  190. package/src/procedures/mvt-dependencies.d.ts +20 -0
  191. package/src/procedures/mvt-legacy.ts +150 -0
  192. package/src/procedures/mvt-loaders.ts +53 -0
  193. package/src/procedures/mvt-parser.ts +48 -0
  194. package/src/procedures/mvt-properties.ts +36 -0
  195. package/src/procedures/mvt-tile-provider.ts +72 -0
  196. package/src/procedures/pmtiles-loader.ts +75 -0
  197. package/src/procedures/projected-coordinate-transform.ts +79 -0
  198. package/src/procedures/projection-batch-executor.ts +140 -0
  199. package/src/procedures/scene-yaml-legacy.ts +16 -0
  200. package/src/procedures/scene-yaml-loaders.ts +10 -0
  201. package/src/procedures/terrain-draping.ts +713 -0
  202. package/src/procedures/terrain-globe.ts +32 -0
  203. package/src/procedures/terrain-styled-topology.ts +184 -0
  204. package/src/procedures/web-mercator-legacy.ts +24 -0
  205. package/src/procedures/web-mercator-math.ts +50 -0
  206. package/src/scene/camera.ts +298 -0
  207. package/src/scene/camera_base.ts +116 -0
  208. package/src/scene/classic_scene.ts +20 -0
  209. package/src/scene/external_camera.ts +101 -0
  210. package/src/scene/globals.ts +120 -0
  211. package/src/scene/ground_footprint.ts +139 -0
  212. package/src/scene/host_frame.ts +326 -0
  213. package/src/scene/projected_tile_lod.ts +125 -0
  214. package/src/scene/projection_math.ts +142 -0
  215. package/src/scene/projection_shaders.ts +183 -0
  216. package/src/scene/renderer.ts +516 -0
  217. package/src/scene/scene-resource-types.ts +174 -0
  218. package/src/scene/scene.ts +2378 -0
  219. package/src/scene/scene_bundle.ts +304 -0
  220. package/src/scene/scene_debug.ts +129 -0
  221. package/src/scene/scene_loader.ts +402 -0
  222. package/src/scene/scene_worker.ts +528 -0
  223. package/src/scene/view.ts +683 -0
  224. package/src/scene/visibility_adapter.ts +319 -0
  225. package/src/scene/worker_url.ts +15 -0
  226. package/src/selection/draped-terrain-surface.ts +377 -0
  227. package/src/selection/selection-types.ts +79 -0
  228. package/src/selection/selection.ts +542 -0
  229. package/src/selection/selection_fragment.glsl +18 -0
  230. package/src/selection/selection_globals.glsl +9 -0
  231. package/src/selection/selection_pixels.ts +59 -0
  232. package/src/selection/selection_vertex.glsl +15 -0
  233. package/src/selection/terrain_surface.ts +298 -0
  234. package/src/shader-modules/globe-horizon.ts +52 -0
  235. package/src/shader-modules/height-decode.ts +41 -0
  236. package/src/shader-modules/hillshade.ts +70 -0
  237. package/src/shader-modules/index.ts +10 -0
  238. package/src/shader-modules/planar.ts +24 -0
  239. package/src/shader-modules/sphere-map.ts +38 -0
  240. package/src/shader-modules/triplanar.ts +45 -0
  241. package/src/sources/aligned_tile_source.ts +122 -0
  242. package/src/sources/data_source.ts +655 -0
  243. package/src/sources/decoded_tile_store.ts +245 -0
  244. package/src/sources/geojson.ts +300 -0
  245. package/src/sources/mvt.ts +247 -0
  246. package/src/sources/raster.ts +341 -0
  247. package/src/sources/shared_tile_source_adapter.ts +117 -0
  248. package/src/sources/sources.ts +10 -0
  249. package/src/sources/tile_source_adapter.ts +84 -0
  250. package/src/sources/tile_source_metadata.ts +65 -0
  251. package/src/sources/tile_source_state.ts +112 -0
  252. package/src/sources/topojson.ts +90 -0
  253. package/src/styles/feature_annotations.ts +16 -0
  254. package/src/styles/filter.ts +190 -0
  255. package/src/styles/geometry-style-types.ts +209 -0
  256. package/src/styles/globe_visibility_wgsl.ts +22 -0
  257. package/src/styles/layer-types.ts +45 -0
  258. package/src/styles/layer.ts +586 -0
  259. package/src/styles/lines/dasharray.ts +49 -0
  260. package/src/styles/lines/lines.ts +707 -0
  261. package/src/styles/lines/lines_wgsl.ts +224 -0
  262. package/src/styles/points/point-runtime-types.ts +125 -0
  263. package/src/styles/points/points.ts +1079 -0
  264. package/src/styles/points/points_fragment.glsl +126 -0
  265. package/src/styles/points/points_vertex.glsl +230 -0
  266. package/src/styles/points/points_wgsl.ts +189 -0
  267. package/src/styles/polygons/polygons.ts +268 -0
  268. package/src/styles/polygons/polygons_fragment.glsl +173 -0
  269. package/src/styles/polygons/polygons_vertex.glsl +213 -0
  270. package/src/styles/polygons/polygons_wgsl.ts +127 -0
  271. package/src/styles/property-types.ts +125 -0
  272. package/src/styles/raster/raster.ts +33 -0
  273. package/src/styles/raster/raster_globals.glsl +46 -0
  274. package/src/styles/style-mixing-types.ts +101 -0
  275. package/src/styles/style-runtime-types.ts +220 -0
  276. package/src/styles/style-schema.ts +133 -0
  277. package/src/styles/style.ts +1033 -0
  278. package/src/styles/style_globals.glsl +69 -0
  279. package/src/styles/style_manager.ts +464 -0
  280. package/src/styles/style_parser.ts +620 -0
  281. package/src/styles/text/font_manager.ts +180 -0
  282. package/src/styles/text/fontfaceobserver.d.ts +10 -0
  283. package/src/styles/text/text-types.ts +220 -0
  284. package/src/styles/text/text.ts +378 -0
  285. package/src/styles/text/text_canvas.ts +773 -0
  286. package/src/styles/text/text_labels.ts +436 -0
  287. package/src/styles/text/text_segments.ts +142 -0
  288. package/src/styles/text/text_settings.ts +228 -0
  289. package/src/styles/text/text_wgsl.ts +159 -0
  290. package/src/styles/text/text_wrap.ts +194 -0
  291. package/src/tile/globe_tile_preload.ts +40 -0
  292. package/src/tile/tangram_tileset_2d.ts +108 -0
  293. package/src/tile/tile-build-types.ts +155 -0
  294. package/src/tile/tile.ts +807 -0
  295. package/src/tile/tile_id.ts +172 -0
  296. package/src/tile/tile_manager.ts +889 -0
  297. package/src/tile/tile_pyramid.ts +119 -0
  298. package/src/tile/tile_resource_cache.ts +91 -0
  299. package/src/tile/tile_traversal_adapter.ts +132 -0
  300. package/src/types.ts +332 -0
  301. package/src/utils/debounce.ts +17 -0
  302. package/src/utils/debug_settings.ts +43 -0
  303. package/src/utils/errors.ts +12 -0
  304. package/src/utils/functions.ts +95 -0
  305. package/src/utils/geo.ts +364 -0
  306. package/src/utils/hash.ts +14 -0
  307. package/src/utils/log.ts +108 -0
  308. package/src/utils/media_capture.ts +193 -0
  309. package/src/utils/merge.ts +44 -0
  310. package/src/utils/obb.ts +134 -0
  311. package/src/utils/props.ts +43 -0
  312. package/src/utils/slice.ts +14 -0
  313. package/src/utils/subscribe.ts +58 -0
  314. package/src/utils/task.ts +154 -0
  315. package/src/utils/thread.ts +26 -0
  316. package/src/utils/urls.ts +223 -0
  317. package/src/utils/utils.ts +310 -0
  318. package/src/utils/vector.ts +197 -0
  319. package/src/utils/version.ts +8 -0
  320. package/src/utils/worker-types.ts +79 -0
  321. 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
+ }