@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,436 @@
|
|
|
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
|
+
// Text label rendering methods, can be mixed into a rendering style
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
import StyleParser from '../style_parser';
|
|
10
|
+
import type {LabelLayout} from '../../labels/label-types';
|
|
11
|
+
import type {TextSettingsResult} from './text_settings';
|
|
12
|
+
import type {TextLabelState, TextLabelDraw, TextFeature, TextContext, TextTile, TextFeatureQueue, TextLabelCandidate, ParsedTextFeature, TextTable, TextSourceValue, TextValue, TextAtlas} from './text-types';
|
|
13
|
+
|
|
14
|
+
/** Typed boundary to the legacy property-cache parser, not a replacement implementation. */
|
|
15
|
+
const typedStyleParser = StyleParser as unknown as {
|
|
16
|
+
createPropertyCache(value: unknown, parse?: (value: unknown) => unknown, interpolate?: (value: unknown) => unknown): unknown;
|
|
17
|
+
parsePositiveNumber(value: unknown): number;
|
|
18
|
+
parseNumber(value: unknown): number;
|
|
19
|
+
};
|
|
20
|
+
/** Host style state plus the checked text-label operations mixed into points/text. */
|
|
21
|
+
export type TextLabelRuntime = TextLabelState & TextLabelMethods;
|
|
22
|
+
/** Operations shared by standalone text and attached point text. */
|
|
23
|
+
export interface TextLabelMethods {
|
|
24
|
+
resetText(this: TextLabelRuntime): void;
|
|
25
|
+
freeText(this: TextLabelRuntime, tile: TextTile): void;
|
|
26
|
+
parseTextFeature(this: TextLabelRuntime, feature: TextFeature, draw: TextLabelDraw, context: TextContext, tile: TextTile): ParsedTextFeature | ParsedTextFeature[] | false | undefined;
|
|
27
|
+
parseTextSource(this: TextLabelRuntime, feature: TextFeature, draw: TextLabelDraw, context: TextContext): TextValue | Record<string, TextValue>;
|
|
28
|
+
parseTextSourceValue(this: TextLabelRuntime, source: TextSourceValue | TextSourceValue[], feature: TextFeature, context: TextContext): TextValue;
|
|
29
|
+
prepareTextLabels(this: TextLabelRuntime, tile: TextTile, queue: TextFeatureQueue[]): Promise<TextLabelCandidate[]>;
|
|
30
|
+
collideAndRenderTextLabels(this: TextLabelRuntime, tile: TextTile, group: string, queue: TextFeatureQueue[]): Promise<TextLabelResult>;
|
|
31
|
+
cullTextStyles(this: TextLabelRuntime, texts: TextTable, labels: TextLabelCandidate[]): void;
|
|
32
|
+
calcTextSizes(this: TextLabelRuntime, tileId: string, texts: TextTable): Promise<TextTable | undefined>;
|
|
33
|
+
rasterizeTexts(this: TextLabelRuntime, tileId: string, tileKey: string, texts: TextTable): Promise<TextLabelResult>;
|
|
34
|
+
preprocessText(this: TextLabelRuntime, draw: TextLabelDraw): TextLabelDraw | undefined;
|
|
35
|
+
computeTextLayout(this: TextLabelRuntime, target: Partial<LabelLayout>, feature: TextFeature, draw: TextLabelDraw, context: TextContext, tile: TextTile, text: TextValue, settings: TextSettingsResult, repeatGroup?: string, orientation?: string): LabelLayout;
|
|
36
|
+
}
|
|
37
|
+
/** Legacy broker boundary with explicit asynchronous payload types. */
|
|
38
|
+
const typedWorkerBroker = WorkerBroker;
|
|
39
|
+
/** Collision/rasterization may return no data after cancellation or teardown. */
|
|
40
|
+
export interface TextLabelResult {
|
|
41
|
+
labels?: TextLabelCandidate[];
|
|
42
|
+
texts?: TextTable;
|
|
43
|
+
textures?: string[];
|
|
44
|
+
}
|
|
45
|
+
import Geo from '../../utils/geo';
|
|
46
|
+
import log from '../../utils/log';
|
|
47
|
+
import Thread from '../../utils/thread';
|
|
48
|
+
import WorkerBroker from '../../utils/worker_broker';
|
|
49
|
+
import Collision from '../../labels/collision';
|
|
50
|
+
import TextSettings from '../text/text_settings';
|
|
51
|
+
import TextCanvas from './text_canvas';
|
|
52
|
+
|
|
53
|
+
// namespaces label textures (ensures new texture name when a tile is built multiple times)
|
|
54
|
+
let text_texture_id = 0;
|
|
55
|
+
|
|
56
|
+
export const TextLabels: TextLabelMethods = {
|
|
57
|
+
|
|
58
|
+
resetText (this: TextLabelRuntime): void {
|
|
59
|
+
if (Thread.is_main) {
|
|
60
|
+
this.canvas = new TextCanvas();
|
|
61
|
+
}
|
|
62
|
+
else if (Thread.is_worker) {
|
|
63
|
+
this.texts = {}; // unique texts, grouped by tile, by style
|
|
64
|
+
}
|
|
65
|
+
},
|
|
66
|
+
|
|
67
|
+
freeText (this: TextLabelRuntime, tile: TextTile): void {
|
|
68
|
+
delete this.texts[tile.id];
|
|
69
|
+
},
|
|
70
|
+
|
|
71
|
+
parseTextFeature (this: TextLabelRuntime, feature: TextFeature, draw: TextLabelDraw, context: TextContext, tile: TextTile): ParsedTextFeature | ParsedTextFeature[] | false | undefined {
|
|
72
|
+
// Compute label text
|
|
73
|
+
let text = this.parseTextSource(feature, draw, context);
|
|
74
|
+
|
|
75
|
+
if (text == null || text === '') {
|
|
76
|
+
return; // no text for this feature
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
// Compute text style and layout settings for this feature label
|
|
80
|
+
let text_settings = TextSettings.compute(draw, context);
|
|
81
|
+
let text_settings_key = TextSettings.key(text_settings);
|
|
82
|
+
|
|
83
|
+
// first label in tile, or with this style?
|
|
84
|
+
this.texts[tile.id] = this.texts[tile.id] || {};
|
|
85
|
+
let sizes = this.texts[tile.id][text_settings_key] = this.texts[tile.id][text_settings_key] || {};
|
|
86
|
+
|
|
87
|
+
if (text instanceof Object){
|
|
88
|
+
let results: ParsedTextFeature[] = [];
|
|
89
|
+
|
|
90
|
+
// add both left/right text elements to repeat group to improve repeat culling
|
|
91
|
+
// avoids one component of a boundary label (e.g. Colorado) being culled too aggressively when it also
|
|
92
|
+
// appears in nearby boundary labels (e.g. Colorado/Utah & Colorado/New Mexico repeat as separate groups)
|
|
93
|
+
let repeat_group_prefix = text.left + '-' + text.right; // NB: should be all text keys, not just left/right
|
|
94
|
+
|
|
95
|
+
for (let key in text){
|
|
96
|
+
let current_text = text[key];
|
|
97
|
+
if (!current_text) {
|
|
98
|
+
continue;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
let layout = this.computeTextLayout({}, feature, draw, context, tile, current_text, text_settings, repeat_group_prefix, key);
|
|
102
|
+
if (!sizes[current_text as string]) {
|
|
103
|
+
// first label with this text/style/tile combination, make a new label entry
|
|
104
|
+
sizes[current_text as string] = {
|
|
105
|
+
text_settings,
|
|
106
|
+
ref: 0 // # of times this text/style combo appears in tile
|
|
107
|
+
};
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
results.push({
|
|
111
|
+
draw, text : current_text as string, text_settings_key, layout
|
|
112
|
+
});
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
return (results.length > 0 && results); // return null if no boundary labels found
|
|
116
|
+
}
|
|
117
|
+
else {
|
|
118
|
+
// unique text strings, grouped by text drawing style
|
|
119
|
+
let layout = this.computeTextLayout({}, feature, draw, context, tile, text, text_settings);
|
|
120
|
+
if (!sizes[text as string]) {
|
|
121
|
+
// first label with this text/style/tile combination, make a new label entry
|
|
122
|
+
sizes[text as string] = {
|
|
123
|
+
text_settings,
|
|
124
|
+
ref: 0 // # of times this text/style combo appears in tile
|
|
125
|
+
};
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
return {
|
|
129
|
+
draw, text: text as string, text_settings_key, layout
|
|
130
|
+
};
|
|
131
|
+
}
|
|
132
|
+
},
|
|
133
|
+
|
|
134
|
+
// Compute the label text, default is value of feature.properties.name
|
|
135
|
+
// - String value indicates a feature property look-up, e.g. `short_name` means use feature.properties.short_name
|
|
136
|
+
// - Function will use the return value as the label text (for custom labels)
|
|
137
|
+
// - Array (of strings and/or functions) defines a list of fallbacks, evaluated according to the above rules,
|
|
138
|
+
// with the first non-null value used as the label text
|
|
139
|
+
// e.g. `[name:es, name:en, name]` prefers Spanish names, followed by English, and last the default local name
|
|
140
|
+
parseTextSource (this: TextLabelRuntime, feature: TextFeature, draw: TextLabelDraw, context: TextContext): TextValue | Record<string, TextValue> {
|
|
141
|
+
let text: TextValue | Record<string, TextValue>;
|
|
142
|
+
let source = draw.text_source || 'name';
|
|
143
|
+
|
|
144
|
+
if (source != null && !Array.isArray(source) && typeof source === 'object') {
|
|
145
|
+
// left/right boundary labels
|
|
146
|
+
text = {};
|
|
147
|
+
for (let key in source) {
|
|
148
|
+
text[key] = this.parseTextSourceValue(source[key], feature, context);
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
else {
|
|
152
|
+
// single label
|
|
153
|
+
text = this.parseTextSourceValue(source, feature, context);
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
return text;
|
|
157
|
+
},
|
|
158
|
+
|
|
159
|
+
parseTextSourceValue (this: TextLabelRuntime, source: TextSourceValue | TextSourceValue[], feature: TextFeature, context: TextContext): TextValue {
|
|
160
|
+
let text: TextValue;
|
|
161
|
+
if (Array.isArray(source)) {
|
|
162
|
+
for (let s=0; s < source.length; s++) {
|
|
163
|
+
if (typeof source[s] === 'string') {
|
|
164
|
+
text = feature.properties[source[s] as string];
|
|
165
|
+
} else if (typeof source[s] === 'function') {
|
|
166
|
+
text = (source[s] as (context: TextContext) => TextValue)(context);
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
if (text) {
|
|
170
|
+
return text; // stop if we found a text property
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
else if (typeof source === 'string') {
|
|
175
|
+
text = feature.properties[source];
|
|
176
|
+
}
|
|
177
|
+
else if (source instanceof Function) {
|
|
178
|
+
text = source(context);
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
return text;
|
|
182
|
+
},
|
|
183
|
+
|
|
184
|
+
async prepareTextLabels (this: TextLabelRuntime, tile: TextTile, queue: TextFeatureQueue[]): Promise<TextLabelCandidate[]> {
|
|
185
|
+
if (Object.keys(this.texts[tile.id]||{}).length === 0) {
|
|
186
|
+
return [];
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
// first call to main thread, ask for text pixel sizes
|
|
190
|
+
try {
|
|
191
|
+
const texts = await typedWorkerBroker.postMessage<TextTable | undefined>(this.main_thread_target+'.calcTextSizes', tile.id, this.texts[tile.id]);
|
|
192
|
+
if (tile.canceled) {
|
|
193
|
+
log('trace', `Style ${this.name}: stop tile build because tile was canceled: ${tile.key}, post-calcTextSizes()`);
|
|
194
|
+
return [];
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
this.texts[tile.id] = texts || [] as unknown as TextTable;
|
|
198
|
+
if (!texts) {
|
|
199
|
+
Collision.abortTile(tile.id);
|
|
200
|
+
return [];
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
return this.buildTextLabels(tile, queue);
|
|
204
|
+
}
|
|
205
|
+
catch (e) { // error thrown if style has been removed from main thread
|
|
206
|
+
Collision.abortTile(tile.id);
|
|
207
|
+
return [];
|
|
208
|
+
}
|
|
209
|
+
},
|
|
210
|
+
|
|
211
|
+
async collideAndRenderTextLabels (this: TextLabelRuntime, tile: TextTile, collision_group: string, queue: TextFeatureQueue[]): Promise<TextLabelResult> {
|
|
212
|
+
let labels = await this.prepareTextLabels(tile, queue);
|
|
213
|
+
if (labels.length === 0) {
|
|
214
|
+
Collision.collide([], collision_group, tile.id);
|
|
215
|
+
return {};
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
labels = await Collision.collide(labels, collision_group, tile.id);
|
|
219
|
+
if (tile.canceled) {
|
|
220
|
+
log('trace', `stop tile build because tile was canceled: ${tile.key}, post-collide()`);
|
|
221
|
+
return {};
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
let texts = this.texts[tile.id];
|
|
225
|
+
if (texts == null || labels.length === 0) {
|
|
226
|
+
return {};
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
this.cullTextStyles(texts, labels);
|
|
230
|
+
|
|
231
|
+
// set alignments
|
|
232
|
+
labels.forEach(q => {
|
|
233
|
+
let text_settings_key = q.text_settings_key;
|
|
234
|
+
let text_info = texts[text_settings_key] && texts[text_settings_key][q.text];
|
|
235
|
+
if (!text_info.text_settings.can_articulate){
|
|
236
|
+
text_info.align = text_info.align || {};
|
|
237
|
+
text_info.align[q.label.align!] = {};
|
|
238
|
+
}
|
|
239
|
+
else {
|
|
240
|
+
// consider making it a set
|
|
241
|
+
if (!text_info.type) {
|
|
242
|
+
text_info.type = [];
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
if (text_info.type.indexOf(q.label.type) === -1){
|
|
246
|
+
text_info.type.push(q.label.type);
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
});
|
|
250
|
+
|
|
251
|
+
// second call to main thread, for rasterizing the set of texts
|
|
252
|
+
try {
|
|
253
|
+
const rasterized = await typedWorkerBroker.postMessage<TextLabelResult>(this.main_thread_target+'.rasterizeTexts', tile.id, tile.key, texts);
|
|
254
|
+
if (tile.canceled) {
|
|
255
|
+
log('trace', `stop tile build because tile was canceled: ${tile.key}, post-rasterizeTexts()`);
|
|
256
|
+
return {};
|
|
257
|
+
}
|
|
258
|
+
return { labels, ...rasterized };
|
|
259
|
+
}
|
|
260
|
+
catch (e) { // error thrown if style has been removed from main thread
|
|
261
|
+
return {};
|
|
262
|
+
}
|
|
263
|
+
},
|
|
264
|
+
|
|
265
|
+
// Remove unused text/style combinations to avoid unnecessary rasterization
|
|
266
|
+
cullTextStyles(this: TextLabelRuntime, texts: TextTable, labels: TextLabelCandidate[]): void {
|
|
267
|
+
// Count how many times each text/style combination is used
|
|
268
|
+
for (let i=0; i < labels.length; i++) {
|
|
269
|
+
let label = labels[i];
|
|
270
|
+
texts[label.text_settings_key][label.text].ref++;
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
// Remove text/style combinations that have no visible labels
|
|
274
|
+
for (let style in texts) {
|
|
275
|
+
for (let text in texts[style]) {
|
|
276
|
+
// no labels for this text
|
|
277
|
+
if (texts[style][text].ref < 1) {
|
|
278
|
+
delete texts[style][text];
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
for (let style in texts) {
|
|
284
|
+
// no labels for this style
|
|
285
|
+
if (Object.keys(texts[style]).length === 0) {
|
|
286
|
+
delete texts[style];
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
},
|
|
290
|
+
|
|
291
|
+
// Called on main thread from worker, to compute the size of each text string,
|
|
292
|
+
// were it to be rendered. This info is then used to perform initial label culling, *before*
|
|
293
|
+
// labels are actually rendered.
|
|
294
|
+
calcTextSizes (this: TextLabelRuntime, tile_id: string, texts: TextTable): Promise<TextTable | undefined> {
|
|
295
|
+
return this.canvas.textSizes(tile_id, texts);
|
|
296
|
+
},
|
|
297
|
+
|
|
298
|
+
// Called on main thread from worker, to create atlas of labels for a tile
|
|
299
|
+
async rasterizeTexts (this: TextLabelRuntime, tile_id: string, tile_key: string, texts: TextTable): Promise<TextLabelResult> {
|
|
300
|
+
let canvas = new TextCanvas(); // one per style per tile (style may be rendering multiple tiles at once)
|
|
301
|
+
let max_texture_size = Math.min(this.max_texture_size, 2048); // cap each label texture at 2048x2048
|
|
302
|
+
|
|
303
|
+
let textures: TextAtlas[] | string[] | undefined = canvas.setTextureTextPositions(texts, max_texture_size);
|
|
304
|
+
let texture_prefix = ['labels', this.name, tile_key, tile_id, text_texture_id, ''].join('-');
|
|
305
|
+
text_texture_id++;
|
|
306
|
+
|
|
307
|
+
textures = await canvas.rasterize(
|
|
308
|
+
texts,
|
|
309
|
+
textures,
|
|
310
|
+
tile_id,
|
|
311
|
+
texture_prefix,
|
|
312
|
+
this.resource_context
|
|
313
|
+
);
|
|
314
|
+
if (!textures) {
|
|
315
|
+
return {};
|
|
316
|
+
}
|
|
317
|
+
return { texts, textures };
|
|
318
|
+
},
|
|
319
|
+
|
|
320
|
+
preprocessText (this: TextLabelRuntime, draw: TextLabelDraw): TextLabelDraw | undefined {
|
|
321
|
+
// Font settings are required
|
|
322
|
+
if (!draw || !draw.font || typeof draw.font !== 'object') {
|
|
323
|
+
return;
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
// Font weight
|
|
327
|
+
draw.font.weight = typedStyleParser.createPropertyCache(draw.font.weight);
|
|
328
|
+
|
|
329
|
+
// Colors
|
|
330
|
+
draw.font.fill = typedStyleParser.createPropertyCache(draw.font.fill || TextSettings.defaults.fill);
|
|
331
|
+
draw.font.alpha = typedStyleParser.createPropertyCache(draw.font.alpha);
|
|
332
|
+
|
|
333
|
+
if (draw.font.stroke) {
|
|
334
|
+
draw.font.stroke.color = typedStyleParser.createPropertyCache(draw.font.stroke.color);
|
|
335
|
+
draw.font.stroke.alpha = typedStyleParser.createPropertyCache(draw.font.stroke.alpha);
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
if (draw.font.background) {
|
|
339
|
+
draw.font.background.color = typedStyleParser.createPropertyCache(draw.font.background.color);
|
|
340
|
+
draw.font.background.alpha = typedStyleParser.createPropertyCache(draw.font.background.alpha);
|
|
341
|
+
draw.font.background.width = typedStyleParser.createPropertyCache(draw.font.background.width, typedStyleParser.parsePositiveNumber);
|
|
342
|
+
if (draw.font.background.stroke) {
|
|
343
|
+
draw.font.background.stroke.color = typedStyleParser.createPropertyCache(draw.font.background.stroke.color);
|
|
344
|
+
draw.font.background.stroke.alpha = typedStyleParser.createPropertyCache(draw.font.background.stroke.alpha);
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
// Convert font and text stroke sizes
|
|
349
|
+
draw.font.px_size = typedStyleParser.createPropertyCache(draw.font.size || TextSettings.defaults.size, TextCanvas.fontPixelSize, TextCanvas.fontPixelSize);
|
|
350
|
+
|
|
351
|
+
if (draw.font.stroke && draw.font.stroke.width != null) {
|
|
352
|
+
draw.font.stroke.width = typedStyleParser.createPropertyCache(draw.font.stroke.width, typedStyleParser.parsePositiveNumber);
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
if (draw.font.background && draw.font.background.stroke && draw.font.background.stroke.width != null) {
|
|
356
|
+
draw.font.background.stroke.width = typedStyleParser.createPropertyCache(draw.font.background.stroke.width, typedStyleParser.parsePositiveNumber);
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
// Offset (2d array)
|
|
360
|
+
draw.offset = typedStyleParser.createPropertyCache(draw.offset,
|
|
361
|
+
v => Array.isArray(v) && v.map(typedStyleParser.parseNumber)
|
|
362
|
+
);
|
|
363
|
+
|
|
364
|
+
// Buffer (1d value or or 2d array) - must be >= 0
|
|
365
|
+
draw.buffer = typedStyleParser.createPropertyCache(draw.buffer,
|
|
366
|
+
v => (Array.isArray(v) ? v : [v, v]).map(typedStyleParser.parsePositiveNumber)
|
|
367
|
+
);
|
|
368
|
+
|
|
369
|
+
// Repeat rules - for text labels, defaults to tile size
|
|
370
|
+
draw.repeat_distance = typedStyleParser.createPropertyCache(
|
|
371
|
+
draw.repeat_distance,
|
|
372
|
+
typedStyleParser.parsePositiveNumber
|
|
373
|
+
);
|
|
374
|
+
|
|
375
|
+
return draw;
|
|
376
|
+
},
|
|
377
|
+
|
|
378
|
+
// Additional text-specific layout settings
|
|
379
|
+
computeTextLayout (this: TextLabelRuntime, target: Partial<LabelLayout>, feature: TextFeature, draw: TextLabelDraw, context: TextContext, tile: TextTile, text: TextValue, text_settings: TextSettingsResult, repeat_group_prefix?: string, orientation?: string): LabelLayout {
|
|
380
|
+
let layout: Partial<LabelLayout> = target || {};
|
|
381
|
+
|
|
382
|
+
// common settings w/points
|
|
383
|
+
layout = this.computeLayout(layout, feature, draw, context, tile);
|
|
384
|
+
|
|
385
|
+
// if draw group didn't specify repeat distance, override with text label-specific logic
|
|
386
|
+
if (draw.repeat_distance == null) {
|
|
387
|
+
// defaults: no limit on labels for point geometries, tile size (256px) limit for other geometries
|
|
388
|
+
layout.repeat_distance = (context.geometry === 'point' ? 0 : Geo.tile_size);
|
|
389
|
+
|
|
390
|
+
if (layout.repeat_distance) {
|
|
391
|
+
layout.repeat_distance *= layout.units_per_pixel!;
|
|
392
|
+
layout.repeat_scale = 1; // initial repeat pass in tile with full scale
|
|
393
|
+
|
|
394
|
+
if (typeof draw.repeat_group === 'function') {
|
|
395
|
+
layout.repeat_group = draw.repeat_group(context); // dynamic repeat group
|
|
396
|
+
}
|
|
397
|
+
else {
|
|
398
|
+
layout.repeat_group = draw.repeat_group; // pre-computed repeat group
|
|
399
|
+
}
|
|
400
|
+
}
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
// repeat rules include the text
|
|
404
|
+
if (layout.repeat_distance) {
|
|
405
|
+
if (repeat_group_prefix) {
|
|
406
|
+
layout.repeat_group += '/' + repeat_group_prefix;
|
|
407
|
+
}
|
|
408
|
+
layout.repeat_group += '/' + text;
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
// Max number of subdivisions to try
|
|
412
|
+
if (this.screen_space_labels || this.cpu_projection) {
|
|
413
|
+
// Preserve text identity even without IDs; position matching prevents unrelated names collapsing.
|
|
414
|
+
layout.projected_identity = JSON.stringify([layout.projected_identity ?? [context.source, context.layer,
|
|
415
|
+
this.name, draw.key], String(text)]);
|
|
416
|
+
}
|
|
417
|
+
layout.subdiv = tile.overzoom2;
|
|
418
|
+
|
|
419
|
+
layout.align = draw.align;
|
|
420
|
+
|
|
421
|
+
// used to fudge width value as text may overflow bounding box if it has italic, bold, etc style
|
|
422
|
+
// TODO rename to more generic, not italic-specific (bold)
|
|
423
|
+
layout.italic = (text_settings.style !== 'normal');
|
|
424
|
+
|
|
425
|
+
// used to determine orientation of text if the text_source has a `left` or `right` key
|
|
426
|
+
if (orientation === 'right') {
|
|
427
|
+
layout.orientation = 1;
|
|
428
|
+
}
|
|
429
|
+
else if (orientation === 'left'){
|
|
430
|
+
layout.orientation = -1;
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
return layout as LabelLayout;
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
};
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
// Tangram
|
|
2
|
+
// SPDX-License-Identifier: MIT
|
|
3
|
+
// Copyright (c) 2013-2016 Brett Camper and Mapzen
|
|
4
|
+
|
|
5
|
+
// Text directionality (right-to-left, bi-directional) and segmentation (curved labels, Arabic handling)
|
|
6
|
+
|
|
7
|
+
// Right-to-left / bi-directional text handling
|
|
8
|
+
// Taken from http://stackoverflow.com/questions/12006095/javascript-how-to-check-if-character-is-rtl
|
|
9
|
+
|
|
10
|
+
type SegmentCache = {
|
|
11
|
+
segment: Record<string, string[]>;
|
|
12
|
+
stats: {
|
|
13
|
+
segment_hits: number;
|
|
14
|
+
segment_misses: number;
|
|
15
|
+
};
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
const rtl_test = new RegExp('[\u0591-\u07FF\u200F\u202B\u202E\uFB1D-\uFDFD\uFE70-\uFEFC]');
|
|
19
|
+
export function isTextRTL(s: string): boolean {
|
|
20
|
+
return rtl_test.test(s);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
const neutral_chars = '\u0000-\u002F\u003A-\u0040\u005B-\u0060\u007B-\u00BF\u00D7\u00F7\u02B9-\u02FF\u2000-\u2BFF\u2010-\u2029\u202C\u202F-\u2BFF';
|
|
24
|
+
const neutral_test = new RegExp('['+neutral_chars+']+');
|
|
25
|
+
export function isTextNeutral(s: string): boolean {
|
|
26
|
+
return neutral_test.test(s);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export const RTL_MARKER = '\u200F'; // explicit right-to-left marker
|
|
30
|
+
|
|
31
|
+
// Arabic script ranges
|
|
32
|
+
// test http://localhost:8000/#16.72917/30.08541/31.28466
|
|
33
|
+
const arabic_range = new RegExp('^['+neutral_chars+'\u0600-\u06FF]+'); // all characters are Arabic or neutral
|
|
34
|
+
const arabic_splitters = new RegExp('['+neutral_chars+'\u0622-\u0625\u0627\u062F-\u0632\u0648\u0671-\u0677\u0688-\u0699\u06C4-\u06CB\u06CF\u06D2\u06D3\u06EE\u06EF]');
|
|
35
|
+
const arabic_vowels = new RegExp('^[\u0610-\u061A\u064B-\u065F\u0670\u06D6-\u06DC\u06DF-\u06E4\u06E7\u06E8\u06EA-\u06ED]+');
|
|
36
|
+
|
|
37
|
+
// Complex script ranges (non-Arabic)
|
|
38
|
+
const accents_and_vowels = '[\u0300-\u036F' + // Combining Diacritical Marks
|
|
39
|
+
'\u0591-\u05BD\u05BF\u05C1\u05C2\u05C4\u05C5\u05C7' + // Hebrew
|
|
40
|
+
'\u07A6-\u07B0' + // Thaana
|
|
41
|
+
'\u0900-\u0903\u093A-\u094C\u094E\u094F\u0951-\u0957\u0962\u0963' + // Devanagari
|
|
42
|
+
'\u0981-\u0983\u09BC\u09BE-\u09CC\u09D7\u09E2\u09E3' + // Bengali
|
|
43
|
+
'\u0A01-\u0A03\u0A3C-\u0A4C\u0A51' + // Gurmukhi
|
|
44
|
+
'\u0A81-\u0A83\u0ABC\u0ABE-\u0ACC\u0AE2\u0AE3' + // Gujarati
|
|
45
|
+
'\u0B01-\u0B03\u0B3C\u0B3E-\u0B4C\u0B56\u0B57\u0B62\u0B63' + // Oriya
|
|
46
|
+
'\u0B82\u0BBE-\u0BCD\u0BD7' + // Tamil
|
|
47
|
+
'\u0C00-\u0C03\u0C3E-\u0C4C\u0C55\u0C56\u0C62\u0C63' + // Telugu
|
|
48
|
+
'\u0C81-\u0C83\u0CBC\u0CBE-\u0CCC\u0CD5\u0CD6\u0CE2\u0CE3' + // Kannada
|
|
49
|
+
'\u0D01-\u0D03\u0D3E-\u0D4C\u0D4E\u0D57\u0D62\u0D63' + // Malayalam
|
|
50
|
+
'\u0D82\u0D83\u0DCA-\u0DDF\u0DF2\u0DF3' + // Sinhala
|
|
51
|
+
'\u0E31\u0E34-\u0E3A\u0E47-\u0E4E' + // Thai
|
|
52
|
+
'\u0EB1\u0EB4-\u0EBC\u0EC8-\u0ECD' + // Lao
|
|
53
|
+
'\u0F18\u0F19\u0F35\u0F37\u0F39\u0F3E\u0F3F\u0F71-\u0F83\u0F86\u0F87\u0F8D-\u0FBC\u0FC6' + // Tibetan
|
|
54
|
+
'\u102B-\u1038\u103A-\u103E\u1056-\u1059\u105E-\u1060\u1062-\u1064\u1067-\u106D\u1071-\u1074\u1082-\u108D\u108F\u109A-\u109D' + // Burmese
|
|
55
|
+
'\u17B4-\u17D1\u17D3' + // Khmer
|
|
56
|
+
'\u1A55-\u1A5E\u1A61-\u1A7C' + // Tai Tham
|
|
57
|
+
'\u1DC0-\u1DFF' + // Combining Diacritical Marks Supplement
|
|
58
|
+
'\u20D0-\u20FF' + // Combining Diacritical Marks for Symbols
|
|
59
|
+
']';
|
|
60
|
+
const combo_characters = '[\u094D\u09CD\u0A4D\u0ACD\u0B4D\u0C4D\u0CCD\u0D4D\u0F84\u1039\u17D2\u1A60\u1A7F]';
|
|
61
|
+
|
|
62
|
+
// Find the next grapheme cluster (non-Arabic)
|
|
63
|
+
const grapheme_match = new RegExp(`^.(?:${accents_and_vowels}+)?(${combo_characters}\\W(?:${accents_and_vowels}+)?)*`); // eslint-disable-line no-misleading-character-class
|
|
64
|
+
|
|
65
|
+
// Scripts that cannot be curved due (due to contextual shaping and/or layout complexity)
|
|
66
|
+
const curve_blacklist: Record<string, string> = {
|
|
67
|
+
Mongolian: '\u1800-\u18AF'
|
|
68
|
+
};
|
|
69
|
+
const curve_blacklist_range = Object.keys(curve_blacklist).map(r => curve_blacklist[r]).join('');
|
|
70
|
+
const curve_blacklist_test = new RegExp('['+curve_blacklist_range+']');
|
|
71
|
+
export function isTextCurveBlacklisted(s: string): boolean {
|
|
72
|
+
return curve_blacklist_test.test(s);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// Splitting strategy for chopping a label into segments
|
|
76
|
+
const default_segment_length = 2; // character length of each segment when dividing up label text
|
|
77
|
+
|
|
78
|
+
export function splitLabelText(text: string, rtl: boolean, cache: SegmentCache): string[] {
|
|
79
|
+
// Use single-character segments for RTL, to avoid additional handling for neutral characters
|
|
80
|
+
// (see https://github.com/tangrams/tangram/issues/541)
|
|
81
|
+
const segment_length = rtl ? 1 : default_segment_length;
|
|
82
|
+
|
|
83
|
+
// Only one segment
|
|
84
|
+
if (text.length < segment_length) {
|
|
85
|
+
return [text];
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
// Check segment cache first (skips processing for labels we've seen before)
|
|
89
|
+
let key = text;
|
|
90
|
+
if (cache.segment[key]) {
|
|
91
|
+
cache.stats.segment_hits++;
|
|
92
|
+
return cache.segment[key];
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
let segments: string[] = [];
|
|
96
|
+
|
|
97
|
+
// Arabic-specific text handling
|
|
98
|
+
// NB: works for strings that are *only* Arabic; mixed-script labels may need more work
|
|
99
|
+
if (arabic_range.exec(text)) {
|
|
100
|
+
segments = text.split(arabic_splitters);
|
|
101
|
+
let offset = -1;
|
|
102
|
+
for (var s = 0; s < segments.length - 1; s++) {
|
|
103
|
+
if (s > 0) {
|
|
104
|
+
let carryover_vowels = arabic_vowels.exec(segments[s]);
|
|
105
|
+
if (carryover_vowels) {
|
|
106
|
+
segments[s] = segments[s].substring(carryover_vowels[0].length);
|
|
107
|
+
segments[s - 1] += carryover_vowels[0];
|
|
108
|
+
offset += carryover_vowels[0].length;
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
offset += 1 + segments[s].length;
|
|
112
|
+
segments[s] += text.slice(offset, offset + 1);
|
|
113
|
+
}
|
|
114
|
+
text = ''; // will skip non-Arabic handling below
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
// Non-Arabic text handling
|
|
118
|
+
while (text.length) {
|
|
119
|
+
let segment = '';
|
|
120
|
+
let test_text = text;
|
|
121
|
+
let grapheme_count = 0;
|
|
122
|
+
|
|
123
|
+
for (grapheme_count; grapheme_count < segment_length && test_text.length; grapheme_count++) {
|
|
124
|
+
let grapheme_cluster = (grapheme_match.exec(test_text) || test_text)[0];
|
|
125
|
+
segment += grapheme_cluster;
|
|
126
|
+
test_text = test_text.substring(grapheme_cluster.length);
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
segments.push(segment);
|
|
130
|
+
text = text.substring(segment.length);
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
// Reverse segments if needed
|
|
134
|
+
if (rtl) {
|
|
135
|
+
segments.reverse();
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
// Cache and return
|
|
139
|
+
cache.stats.segment_misses++;
|
|
140
|
+
cache.segment[key] = segments;
|
|
141
|
+
return segments;
|
|
142
|
+
}
|