@sakuzu/maplibre-gl-draw 1.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/CHANGELOG.md +43 -0
- package/LICENSE +661 -0
- package/README.ja.md +248 -0
- package/README.md +255 -0
- package/THIRD_PARTY_NOTICES.md +275 -0
- package/dist/api/api.d.ts +1259 -0
- package/dist/api/api.js +86 -0
- package/dist/api/context.d.ts +173 -0
- package/dist/api/context.js +193 -0
- package/dist/api/display-api.d.ts +1 -0
- package/dist/api/display-api.js +23 -0
- package/dist/api/event-api.d.ts +13 -0
- package/dist/api/event-api.js +17 -0
- package/dist/api/extension-api.d.ts +46 -0
- package/dist/api/extension-api.js +85 -0
- package/dist/api/feature-api.d.ts +11 -0
- package/dist/api/feature-api.js +101 -0
- package/dist/api/geometry/apply.d.ts +51 -0
- package/dist/api/geometry/apply.js +114 -0
- package/dist/api/geometry/boolean.d.ts +28 -0
- package/dist/api/geometry/boolean.js +65 -0
- package/dist/api/geometry/buffer.d.ts +11 -0
- package/dist/api/geometry/buffer.js +105 -0
- package/dist/api/geometry/split.d.ts +16 -0
- package/dist/api/geometry/split.js +75 -0
- package/dist/api/geometry/targets.d.ts +76 -0
- package/dist/api/geometry/targets.js +158 -0
- package/dist/api/geometry/types.d.ts +31 -0
- package/dist/api/geometry/types.js +3 -0
- package/dist/api/geometry-operations.d.ts +162 -0
- package/dist/api/geometry-operations.js +94 -0
- package/dist/api/group-api.d.ts +10 -0
- package/dist/api/group-api.js +78 -0
- package/dist/api/import-export/constants.d.ts +29 -0
- package/dist/api/import-export/constants.js +40 -0
- package/dist/api/import-export/embedded-file.d.ts +13 -0
- package/dist/api/import-export/embedded-file.js +31 -0
- package/dist/api/import-export/file-name.d.ts +10 -0
- package/dist/api/import-export/file-name.js +20 -0
- package/dist/api/import-export/format-detection.d.ts +12 -0
- package/dist/api/import-export/format-detection.js +29 -0
- package/dist/api/import-export/geojson-export.d.ts +19 -0
- package/dist/api/import-export/geojson-export.js +262 -0
- package/dist/api/import-export/geojson-import.d.ts +34 -0
- package/dist/api/import-export/geojson-import.js +538 -0
- package/dist/api/import-export/geometry-validation.d.ts +20 -0
- package/dist/api/import-export/geometry-validation.js +107 -0
- package/dist/api/import-export/image-import.d.ts +12 -0
- package/dist/api/import-export/image-import.js +53 -0
- package/dist/api/import-export/index.d.ts +9 -0
- package/dist/api/import-export/index.js +107 -0
- package/dist/api/import-export/native-format.d.ts +15 -0
- package/dist/api/import-export/native-format.js +126 -0
- package/dist/api/import-export/native-validation.d.ts +28 -0
- package/dist/api/import-export/native-validation.js +212 -0
- package/dist/api/import-export/own-property.d.ts +10 -0
- package/dist/api/import-export/own-property.js +19 -0
- package/dist/api/import-export/style-validation.d.ts +27 -0
- package/dist/api/import-export/style-validation.js +49 -0
- package/dist/api/import-export/types.d.ts +26 -0
- package/dist/api/import-export/types.js +3 -0
- package/dist/api/index.d.ts +15 -0
- package/dist/api/index.js +3 -0
- package/dist/api/input-api.d.ts +158 -0
- package/dist/api/input-api.js +144 -0
- package/dist/api/instance-api.d.ts +38 -0
- package/dist/api/instance-api.js +136 -0
- package/dist/api/layer-api.d.ts +12 -0
- package/dist/api/layer-api.js +62 -0
- package/dist/api/selection-api.d.ts +7 -0
- package/dist/api/selection-api.js +78 -0
- package/dist/api/snapping-api.d.ts +125 -0
- package/dist/api/snapping-api.js +50 -0
- package/dist/api/topology-api.d.ts +30 -0
- package/dist/api/topology-api.js +16 -0
- package/dist/api/tracing-api.d.ts +31 -0
- package/dist/api/tracing-api.js +16 -0
- package/dist/dispatcher/hit-test/box-strategies.d.ts +19 -0
- package/dist/dispatcher/hit-test/box-strategies.js +275 -0
- package/dist/dispatcher/hit-test/box-strategy.d.ts +59 -0
- package/dist/dispatcher/hit-test/box-strategy.js +37 -0
- package/dist/dispatcher/hit-test/globe-shape.d.ts +46 -0
- package/dist/dispatcher/hit-test/globe-shape.js +70 -0
- package/dist/dispatcher/hit-test/index.d.ts +14 -0
- package/dist/dispatcher/hit-test/index.js +9 -0
- package/dist/dispatcher/hit-test/local-frame.d.ts +77 -0
- package/dist/dispatcher/hit-test/local-frame.js +112 -0
- package/dist/dispatcher/hit-test/segment-grid.d.ts +32 -0
- package/dist/dispatcher/hit-test/segment-grid.js +79 -0
- package/dist/dispatcher/hit-test/service.d.ts +120 -0
- package/dist/dispatcher/hit-test/service.js +333 -0
- package/dist/dispatcher/hit-test/strategies/base.d.ts +136 -0
- package/dist/dispatcher/hit-test/strategies/base.js +66 -0
- package/dist/dispatcher/hit-test/strategies/circle.d.ts +10 -0
- package/dist/dispatcher/hit-test/strategies/circle.js +56 -0
- package/dist/dispatcher/hit-test/strategies/image.d.ts +22 -0
- package/dist/dispatcher/hit-test/strategies/image.js +115 -0
- package/dist/dispatcher/hit-test/strategies/index.d.ts +11 -0
- package/dist/dispatcher/hit-test/strategies/index.js +10 -0
- package/dist/dispatcher/hit-test/strategies/line.d.ts +17 -0
- package/dist/dispatcher/hit-test/strategies/line.js +30 -0
- package/dist/dispatcher/hit-test/strategies/multi.d.ts +44 -0
- package/dist/dispatcher/hit-test/strategies/multi.js +141 -0
- package/dist/dispatcher/hit-test/strategies/point.d.ts +20 -0
- package/dist/dispatcher/hit-test/strategies/point.js +29 -0
- package/dist/dispatcher/hit-test/strategies/polygon.d.ts +52 -0
- package/dist/dispatcher/hit-test/strategies/polygon.js +138 -0
- package/dist/dispatcher/hit-test/topmost.d.ts +48 -0
- package/dist/dispatcher/hit-test/topmost.js +157 -0
- package/dist/dispatcher/hit-test/visibility-lookup.d.ts +23 -0
- package/dist/dispatcher/hit-test/visibility-lookup.js +40 -0
- package/dist/dispatcher/index.d.ts +15 -0
- package/dist/dispatcher/index.js +3 -0
- package/dist/dispatcher/input-router.d.ts +1 -0
- package/dist/dispatcher/input-router.js +288 -0
- package/dist/dispatcher/normalizer.d.ts +1 -0
- package/dist/dispatcher/normalizer.js +513 -0
- package/dist/dispatcher/types.d.ts +144 -0
- package/dist/dispatcher/types.js +15 -0
- package/dist/display/chunk-set.d.ts +24 -0
- package/dist/display/chunk-set.js +533 -0
- package/dist/display/chunk.d.ts +1 -0
- package/dist/display/chunk.js +104 -0
- package/dist/display/columnar/index.d.ts +12 -0
- package/dist/display/columnar/index.js +13 -0
- package/dist/display/columnar/prepare.d.ts +42 -0
- package/dist/display/columnar/prepare.js +116 -0
- package/dist/display/columnar/source.d.ts +1 -0
- package/dist/display/columnar/source.js +531 -0
- package/dist/display/columnar/table.d.ts +1 -0
- package/dist/display/columnar/table.js +471 -0
- package/dist/display/columnar/types.d.ts +223 -0
- package/dist/display/columnar/types.js +3 -0
- package/dist/display/dataset.d.ts +2 -0
- package/dist/display/dataset.js +891 -0
- package/dist/display/index.d.ts +13 -0
- package/dist/display/index.js +3 -0
- package/dist/display/interaction.d.ts +1 -0
- package/dist/display/interaction.js +90 -0
- package/dist/display/manager.d.ts +1 -0
- package/dist/display/manager.js +440 -0
- package/dist/display/packed-rtree.d.ts +1 -0
- package/dist/display/packed-rtree.js +244 -0
- package/dist/display/partition.d.ts +1 -0
- package/dist/display/partition.js +226 -0
- package/dist/display/provider.d.ts +1 -0
- package/dist/display/provider.js +342 -0
- package/dist/display/retained.d.ts +40 -0
- package/dist/display/retained.js +898 -0
- package/dist/display/selection.d.ts +13 -0
- package/dist/display/selection.js +226 -0
- package/dist/display/source.d.ts +1 -0
- package/dist/display/source.js +119 -0
- package/dist/display/spatial.d.ts +1 -0
- package/dist/display/spatial.js +71 -0
- package/dist/display/style.d.ts +1 -0
- package/dist/display/style.js +126 -0
- package/dist/display/thinning.d.ts +65 -0
- package/dist/display/thinning.js +910 -0
- package/dist/display/triangulation.d.ts +1 -0
- package/dist/display/triangulation.js +0 -0
- package/dist/display/types.d.ts +561 -0
- package/dist/display/types.js +43 -0
- package/dist/extension/feature-handler.d.ts +182 -0
- package/dist/extension/feature-handler.js +3 -0
- package/dist/extension/index.d.ts +9 -0
- package/dist/extension/index.js +3 -0
- package/dist/extension/renderers.d.ts +208 -0
- package/dist/extension/renderers.js +3 -0
- package/dist/geometry/angle.d.ts +47 -0
- package/dist/geometry/angle.js +44 -0
- package/dist/geometry/bbox.d.ts +48 -0
- package/dist/geometry/bbox.js +83 -0
- package/dist/geometry/boolean.d.ts +162 -0
- package/dist/geometry/boolean.js +331 -0
- package/dist/geometry/buffer.d.ts +72 -0
- package/dist/geometry/buffer.js +218 -0
- package/dist/geometry/circle.d.ts +55 -0
- package/dist/geometry/circle.js +107 -0
- package/dist/geometry/coords.d.ts +97 -0
- package/dist/geometry/coords.js +149 -0
- package/dist/geometry/distance.d.ts +79 -0
- package/dist/geometry/distance.js +111 -0
- package/dist/geometry/errors.d.ts +74 -0
- package/dist/geometry/errors.js +104 -0
- package/dist/geometry/index.d.ts +28 -0
- package/dist/geometry/index.js +38 -0
- package/dist/geometry/measure.d.ts +81 -0
- package/dist/geometry/measure.js +236 -0
- package/dist/geometry/predicates.d.ts +62 -0
- package/dist/geometry/predicates.js +132 -0
- package/dist/geometry/simplify.d.ts +74 -0
- package/dist/geometry/simplify.js +159 -0
- package/dist/geometry/split.d.ts +68 -0
- package/dist/geometry/split.js +430 -0
- package/dist/geometry/types.d.ts +95 -0
- package/dist/geometry/types.js +3 -0
- package/dist/geometry/units.d.ts +35 -0
- package/dist/geometry/units.js +41 -0
- package/dist/index.d.ts +72 -0
- package/dist/index.js +20 -0
- package/dist/maplibre-gl-draw.d.ts +47 -0
- package/dist/maplibre-gl-draw.js +407 -0
- package/dist/messages.d.ts +64 -0
- package/dist/messages.js +36 -0
- package/dist/modes/cursor.d.ts +1 -0
- package/dist/modes/cursor.js +72 -0
- package/dist/modes/draw/circle.d.ts +1 -0
- package/dist/modes/draw/circle.js +222 -0
- package/dist/modes/draw/commit-layer.d.ts +26 -0
- package/dist/modes/draw/commit-layer.js +31 -0
- package/dist/modes/draw/freehand.d.ts +1 -0
- package/dist/modes/draw/freehand.js +264 -0
- package/dist/modes/draw/image.d.ts +1 -0
- package/dist/modes/draw/image.js +60 -0
- package/dist/modes/draw/index.d.ts +9 -0
- package/dist/modes/draw/index.js +11 -0
- package/dist/modes/draw/line.d.ts +1 -0
- package/dist/modes/draw/line.js +363 -0
- package/dist/modes/draw/point.d.ts +1 -0
- package/dist/modes/draw/point.js +87 -0
- package/dist/modes/draw/polygon.d.ts +1 -0
- package/dist/modes/draw/polygon.js +378 -0
- package/dist/modes/draw/trace-support.d.ts +79 -0
- package/dist/modes/draw/trace-support.js +161 -0
- package/dist/modes/handler.d.ts +285 -0
- package/dist/modes/handler.js +3 -0
- package/dist/modes/index.d.ts +7 -0
- package/dist/modes/index.js +3 -0
- package/dist/modes/manager.d.ts +15 -0
- package/dist/modes/manager.js +196 -0
- package/dist/modes/select/box-selection.d.ts +52 -0
- package/dist/modes/select/box-selection.js +140 -0
- package/dist/modes/select/click-handler.d.ts +32 -0
- package/dist/modes/select/click-handler.js +166 -0
- package/dist/modes/select/cursor-handler.d.ts +15 -0
- package/dist/modes/select/cursor-handler.js +59 -0
- package/dist/modes/select/drag/auxiliary.d.ts +26 -0
- package/dist/modes/select/drag/auxiliary.js +56 -0
- package/dist/modes/select/drag/intermediate-writes.d.ts +1 -0
- package/dist/modes/select/drag/intermediate-writes.js +171 -0
- package/dist/modes/select/drag/move.d.ts +24 -0
- package/dist/modes/select/drag/move.js +59 -0
- package/dist/modes/select/drag/operation.d.ts +30 -0
- package/dist/modes/select/drag/operation.js +18 -0
- package/dist/modes/select/drag/radius.d.ts +15 -0
- package/dist/modes/select/drag/radius.js +49 -0
- package/dist/modes/select/drag/transform.d.ts +17 -0
- package/dist/modes/select/drag/transform.js +155 -0
- package/dist/modes/select/drag/vertex.d.ts +17 -0
- package/dist/modes/select/drag/vertex.js +163 -0
- package/dist/modes/select/drag-handler.d.ts +1 -0
- package/dist/modes/select/drag-handler.js +254 -0
- package/dist/modes/select/hit-helpers.d.ts +40 -0
- package/dist/modes/select/hit-helpers.js +51 -0
- package/dist/modes/select/mode.d.ts +1 -0
- package/dist/modes/select/mode.js +397 -0
- package/dist/modes/select/shortcut-handler.d.ts +40 -0
- package/dist/modes/select/shortcut-handler.js +128 -0
- package/dist/operations/index.d.ts +9 -0
- package/dist/operations/index.js +3 -0
- package/dist/operations/layer-operations.d.ts +1 -0
- package/dist/operations/layer-operations.js +350 -0
- package/dist/operations/resize.d.ts +71 -0
- package/dist/operations/resize.js +252 -0
- package/dist/operations/rotate.d.ts +17 -0
- package/dist/operations/rotate.js +166 -0
- package/dist/operations/selection-operations.d.ts +20 -0
- package/dist/operations/selection-operations.js +159 -0
- package/dist/operations/shared-vertex.d.ts +1 -0
- package/dist/operations/shared-vertex.js +210 -0
- package/dist/operations/trace-graph.d.ts +96 -0
- package/dist/operations/trace-graph.js +283 -0
- package/dist/operations/vertex.d.ts +1 -0
- package/dist/operations/vertex.js +538 -0
- package/dist/plugins/index.d.ts +10 -0
- package/dist/plugins/index.js +3 -0
- package/dist/plugins/mutation-hooks.d.ts +28 -0
- package/dist/plugins/mutation-hooks.js +84 -0
- package/dist/plugins/plugin-context.d.ts +1 -0
- package/dist/plugins/plugin-context.js +367 -0
- package/dist/plugins/plugin-manager.d.ts +103 -0
- package/dist/plugins/plugin-manager.js +316 -0
- package/dist/plugins/plugin.d.ts +529 -0
- package/dist/plugins/plugin.js +3 -0
- package/dist/shared/config/constants.d.ts +66 -0
- package/dist/shared/config/constants.js +60 -0
- package/dist/shared/config/feature-style.d.ts +155 -0
- package/dist/shared/config/feature-style.js +154 -0
- package/dist/shared/config/index.d.ts +17 -0
- package/dist/shared/config/index.js +8 -0
- package/dist/shared/config/rendering.d.ts +107 -0
- package/dist/shared/config/rendering.js +33 -0
- package/dist/shared/config/selection-highlight.d.ts +29 -0
- package/dist/shared/config/selection-highlight.js +35 -0
- package/dist/shared/config/selection.d.ts +273 -0
- package/dist/shared/config/selection.js +266 -0
- package/dist/shared/config/topology.d.ts +33 -0
- package/dist/shared/config/topology.js +17 -0
- package/dist/shared/config/trace.d.ts +40 -0
- package/dist/shared/config/trace.js +17 -0
- package/dist/shared/math/angle.d.ts +21 -0
- package/dist/shared/math/angle.js +34 -0
- package/dist/shared/math/circle.d.ts +6 -0
- package/dist/shared/math/circle.js +10 -0
- package/dist/shared/math/constants.d.ts +48 -0
- package/dist/shared/math/constants.js +59 -0
- package/dist/shared/math/distance.d.ts +78 -0
- package/dist/shared/math/distance.js +147 -0
- package/dist/shared/math/globe-subdivision.d.ts +62 -0
- package/dist/shared/math/globe-subdivision.js +99 -0
- package/dist/shared/math/index.d.ts +17 -0
- package/dist/shared/math/index.js +21 -0
- package/dist/shared/math/intersection.d.ts +63 -0
- package/dist/shared/math/intersection.js +236 -0
- package/dist/shared/math/longitude.d.ts +20 -0
- package/dist/shared/math/longitude.js +31 -0
- package/dist/shared/math/mercator-plane.d.ts +27 -0
- package/dist/shared/math/mercator-plane.js +26 -0
- package/dist/shared/math/obb.d.ts +65 -0
- package/dist/shared/math/obb.js +107 -0
- package/dist/shared/math/rotation.d.ts +93 -0
- package/dist/shared/math/rotation.js +186 -0
- package/dist/shared/math/segment-grid.d.ts +69 -0
- package/dist/shared/math/segment-grid.js +159 -0
- package/dist/shared/math/transform.d.ts +140 -0
- package/dist/shared/math/transform.js +189 -0
- package/dist/shared/types/events.d.ts +128 -0
- package/dist/shared/types/events.js +3 -0
- package/dist/shared/types/model.d.ts +950 -0
- package/dist/shared/types/model.js +3 -0
- package/dist/shared/types/selection-box.d.ts +23 -0
- package/dist/shared/types/selection-box.js +3 -0
- package/dist/shared/types/style.d.ts +68 -0
- package/dist/shared/types/style.js +3 -0
- package/dist/shared/utils/color.d.ts +49 -0
- package/dist/shared/utils/color.js +180 -0
- package/dist/shared/utils/coordinates.d.ts +52 -0
- package/dist/shared/utils/coordinates.js +75 -0
- package/dist/shared/utils/embedded-image.d.ts +34 -0
- package/dist/shared/utils/embedded-image.js +161 -0
- package/dist/shared/utils/event-emitter.d.ts +262 -0
- package/dist/shared/utils/event-emitter.js +44 -0
- package/dist/shared/utils/feature-bbox.d.ts +8 -0
- package/dist/shared/utils/feature-bbox.js +207 -0
- package/dist/shared/utils/feature-segments.d.ts +23 -0
- package/dist/shared/utils/feature-segments.js +179 -0
- package/dist/shared/utils/id.d.ts +1 -0
- package/dist/shared/utils/id.js +35 -0
- package/dist/shared/utils/image.d.ts +34 -0
- package/dist/shared/utils/image.js +106 -0
- package/dist/shared/utils/index.d.ts +15 -0
- package/dist/shared/utils/index.js +24 -0
- package/dist/shared/utils/map.d.ts +26 -0
- package/dist/shared/utils/map.js +35 -0
- package/dist/shared/utils/name-generator.d.ts +197 -0
- package/dist/shared/utils/name-generator.js +337 -0
- package/dist/shared/utils/pixel-ratio.d.ts +111 -0
- package/dist/shared/utils/pixel-ratio.js +85 -0
- package/dist/shared/utils/property.d.ts +88 -0
- package/dist/shared/utils/property.js +140 -0
- package/dist/shared/utils/vertex-ref.d.ts +23 -0
- package/dist/shared/utils/vertex-ref.js +20 -0
- package/dist/snapping/custom-targets.d.ts +1 -0
- package/dist/snapping/custom-targets.js +21 -0
- package/dist/snapping/geometry.d.ts +1 -0
- package/dist/snapping/geometry.js +61 -0
- package/dist/snapping/index.d.ts +14 -0
- package/dist/snapping/index.js +4 -0
- package/dist/snapping/indicator.d.ts +19 -0
- package/dist/snapping/indicator.js +145 -0
- package/dist/snapping/providers/display.d.ts +72 -0
- package/dist/snapping/providers/display.js +268 -0
- package/dist/snapping/providers/guide.d.ts +69 -0
- package/dist/snapping/providers/guide.js +187 -0
- package/dist/snapping/providers/intersection.d.ts +1 -0
- package/dist/snapping/providers/intersection.js +92 -0
- package/dist/snapping/providers/shared.d.ts +39 -0
- package/dist/snapping/providers/shared.js +146 -0
- package/dist/snapping/providers/store.d.ts +1 -0
- package/dist/snapping/providers/store.js +132 -0
- package/dist/snapping/service.d.ts +1 -0
- package/dist/snapping/service.js +272 -0
- package/dist/snapping/types.d.ts +267 -0
- package/dist/snapping/types.js +66 -0
- package/dist/store/change-merger.d.ts +1 -0
- package/dist/store/change-merger.js +102 -0
- package/dist/store/draw-store.d.ts +1 -0
- package/dist/store/draw-store.js +396 -0
- package/dist/store/event-bridge.d.ts +1 -0
- package/dist/store/event-bridge.js +161 -0
- package/dist/store/index.d.ts +12 -0
- package/dist/store/index.js +4 -0
- package/dist/store/local-visibility.d.ts +21 -0
- package/dist/store/local-visibility.js +27 -0
- package/dist/store/lock.d.ts +67 -0
- package/dist/store/lock.js +54 -0
- package/dist/store/memory/change-bus.d.ts +60 -0
- package/dist/store/memory/change-bus.js +232 -0
- package/dist/store/memory/file-store.d.ts +15 -0
- package/dist/store/memory/file-store.js +33 -0
- package/dist/store/memory/frozen.d.ts +21 -0
- package/dist/store/memory/frozen.js +51 -0
- package/dist/store/memory/ui-state.d.ts +51 -0
- package/dist/store/memory/ui-state.js +223 -0
- package/dist/store/memory.d.ts +26 -0
- package/dist/store/memory.js +708 -0
- package/dist/store/spatial/index.d.ts +6 -0
- package/dist/store/spatial/index.js +4 -0
- package/dist/store/spatial/spatial-index.d.ts +90 -0
- package/dist/store/spatial/spatial-index.js +148 -0
- package/dist/store/spatial/store-spatial-index.d.ts +60 -0
- package/dist/store/spatial/store-spatial-index.js +125 -0
- package/dist/store/store.d.ts +417 -0
- package/dist/store/store.js +3 -0
- package/dist/store/types.d.ts +8 -0
- package/dist/store/types.js +3 -0
- package/dist/store/writable-layer.d.ts +35 -0
- package/dist/store/writable-layer.js +20 -0
- package/dist/view/cache/buffer.d.ts +28 -0
- package/dist/view/cache/buffer.js +176 -0
- package/dist/view/cache/earcut.d.ts +1 -0
- package/dist/view/cache/earcut.js +122 -0
- package/dist/view/cache/style-rule.d.ts +1 -0
- package/dist/view/cache/style-rule.js +97 -0
- package/dist/view/cache/terrain-fill.d.ts +30 -0
- package/dist/view/cache/terrain-fill.js +60 -0
- package/dist/view/cache/texture.d.ts +14 -0
- package/dist/view/cache/texture.js +292 -0
- package/dist/view/coordinator.d.ts +27 -0
- package/dist/view/coordinator.js +72 -0
- package/dist/view/feature-companion.d.ts +173 -0
- package/dist/view/feature-companion.js +108 -0
- package/dist/view/globe-subdivision.d.ts +78 -0
- package/dist/view/globe-subdivision.js +128 -0
- package/dist/view/index.d.ts +47 -0
- package/dist/view/index.js +20 -0
- package/dist/view/layer/attach.d.ts +20 -0
- package/dist/view/layer/attach.js +47 -0
- package/dist/view/layer/blend.d.ts +57 -0
- package/dist/view/layer/blend.js +14 -0
- package/dist/view/layer/custom-layer.d.ts +1 -0
- package/dist/view/layer/custom-layer.js +509 -0
- package/dist/view/layer/depth-state.d.ts +14 -0
- package/dist/view/layer/depth-state.js +44 -0
- package/dist/view/layer/display-list.d.ts +1 -0
- package/dist/view/layer/display-list.js +111 -0
- package/dist/view/layer/drape-planner.d.ts +139 -0
- package/dist/view/layer/drape-planner.js +726 -0
- package/dist/view/layer/frame-render.d.ts +57 -0
- package/dist/view/layer/frame-render.js +209 -0
- package/dist/view/layer/frame-state.d.ts +147 -0
- package/dist/view/layer/frame-state.js +484 -0
- package/dist/view/layer/gl-state.d.ts +77 -0
- package/dist/view/layer/gl-state.js +132 -0
- package/dist/view/layer/index.d.ts +6 -0
- package/dist/view/layer/index.js +5 -0
- package/dist/view/layer/render-scope.d.ts +30 -0
- package/dist/view/layer/render-scope.js +28 -0
- package/dist/view/layer/render.d.ts +87 -0
- package/dist/view/layer/render.js +187 -0
- package/dist/view/layer/renderers.d.ts +81 -0
- package/dist/view/layer/renderers.js +142 -0
- package/dist/view/layer/slot-manager.d.ts +26 -0
- package/dist/view/layer/slot-manager.js +133 -0
- package/dist/view/layer/slots.d.ts +33 -0
- package/dist/view/layer/slots.js +50 -0
- package/dist/view/layer/store-retained-bbox.d.ts +36 -0
- package/dist/view/layer/store-retained-bbox.js +188 -0
- package/dist/view/layer/store-retained-chunk.d.ts +97 -0
- package/dist/view/layer/store-retained-chunk.js +94 -0
- package/dist/view/layer/store-retained-classify.d.ts +34 -0
- package/dist/view/layer/store-retained-classify.js +88 -0
- package/dist/view/layer/store-retained-collect.d.ts +24 -0
- package/dist/view/layer/store-retained-collect.js +121 -0
- package/dist/view/layer/store-retained-coord-patch.d.ts +119 -0
- package/dist/view/layer/store-retained-coord-patch.js +168 -0
- package/dist/view/layer/store-retained-immediate.d.ts +63 -0
- package/dist/view/layer/store-retained-immediate.js +49 -0
- package/dist/view/layer/store-retained-invalidation.d.ts +96 -0
- package/dist/view/layer/store-retained-invalidation.js +192 -0
- package/dist/view/layer/store-retained.d.ts +198 -0
- package/dist/view/layer/store-retained.js +430 -0
- package/dist/view/layer/terrain-resolver.d.ts +1 -0
- package/dist/view/layer/terrain-resolver.js +174 -0
- package/dist/view/renderers/batch-manager.d.ts +61 -0
- package/dist/view/renderers/batch-manager.js +718 -0
- package/dist/view/renderers/draw-factors.d.ts +60 -0
- package/dist/view/renderers/draw-factors.js +35 -0
- package/dist/view/renderers/drawer.d.ts +1 -0
- package/dist/view/renderers/drawer.js +346 -0
- package/dist/view/renderers/image.d.ts +1 -0
- package/dist/view/renderers/image.js +255 -0
- package/dist/view/renderers/line/dash.d.ts +31 -0
- package/dist/view/renderers/line/dash.js +152 -0
- package/dist/view/renderers/line/line-geometry.d.ts +39 -0
- package/dist/view/renderers/line/line-geometry.js +460 -0
- package/dist/view/renderers/line/line-gl.d.ts +1 -0
- package/dist/view/renderers/line/line-gl.js +236 -0
- package/dist/view/renderers/line/line-shader.d.ts +1 -0
- package/dist/view/renderers/line/line-shader.js +364 -0
- package/dist/view/renderers/line/line-types.d.ts +43 -0
- package/dist/view/renderers/line/line-types.js +3 -0
- package/dist/view/renderers/line/line-uniforms.d.ts +1 -0
- package/dist/view/renderers/line/line-uniforms.js +390 -0
- package/dist/view/renderers/line/sdf-line.d.ts +92 -0
- package/dist/view/renderers/line/sdf-line.js +567 -0
- package/dist/view/renderers/point/billboard-depth.d.ts +10 -0
- package/dist/view/renderers/point/billboard-depth.js +56 -0
- package/dist/view/renderers/point/point-instance.d.ts +124 -0
- package/dist/view/renderers/point/point-instance.js +738 -0
- package/dist/view/renderers/point/point-sdf.d.ts +58 -0
- package/dist/view/renderers/point/point-sdf.js +170 -0
- package/dist/view/renderers/point/point-shape.d.ts +99 -0
- package/dist/view/renderers/point/point-shape.js +504 -0
- package/dist/view/renderers/point/point-style.d.ts +1 -0
- package/dist/view/renderers/point/point-style.js +46 -0
- package/dist/view/renderers/polygon/batch.d.ts +26 -0
- package/dist/view/renderers/polygon/batch.js +336 -0
- package/dist/view/renderers/polygon/earcut-input.d.ts +37 -0
- package/dist/view/renderers/polygon/earcut-input.js +55 -0
- package/dist/view/renderers/polygon/earcut-sliced.d.ts +69 -0
- package/dist/view/renderers/polygon/earcut-sliced.js +960 -0
- package/dist/view/renderers/polygon/fill.d.ts +74 -0
- package/dist/view/renderers/polygon/fill.js +251 -0
- package/dist/view/renderers/polygon/sdf-polygon.d.ts +62 -0
- package/dist/view/renderers/polygon/sdf-polygon.js +919 -0
- package/dist/view/renderers/polygon/terrain-cull.d.ts +3 -0
- package/dist/view/renderers/polygon/terrain-cull.js +14 -0
- package/dist/view/renderers/polygon/triangulator.d.ts +41 -0
- package/dist/view/renderers/polygon/triangulator.js +3 -0
- package/dist/view/renderers/retained.d.ts +43 -0
- package/dist/view/renderers/retained.js +3 -0
- package/dist/view/renderers/stroke.d.ts +121 -0
- package/dist/view/renderers/stroke.js +621 -0
- package/dist/view/shaders/frame.d.ts +8 -0
- package/dist/view/shaders/frame.js +37 -0
- package/dist/view/shaders/helpers.d.ts +238 -0
- package/dist/view/shaders/helpers.js +611 -0
- package/dist/view/shaders/initializer.d.ts +27 -0
- package/dist/view/shaders/initializer.js +125 -0
- package/dist/view/shaders/projection.d.ts +123 -0
- package/dist/view/shaders/projection.js +299 -0
- package/dist/view/shaders/quad-grid.d.ts +77 -0
- package/dist/view/shaders/quad-grid.js +128 -0
- package/dist/view/shaders/quad.d.ts +189 -0
- package/dist/view/shaders/quad.js +582 -0
- package/dist/view/shaders/retained-origin.d.ts +50 -0
- package/dist/view/shaders/retained-origin.js +67 -0
- package/dist/view/shaders/terrain-shade.d.ts +23 -0
- package/dist/view/shaders/terrain-shade.js +93 -0
- package/dist/view/style-rule.d.ts +166 -0
- package/dist/view/style-rule.js +300 -0
- package/dist/view/terrain/anchor.d.ts +135 -0
- package/dist/view/terrain/anchor.js +226 -0
- package/dist/view/terrain/context.d.ts +132 -0
- package/dist/view/terrain/context.js +437 -0
- package/dist/view/terrain/dem-atlas.d.ts +97 -0
- package/dist/view/terrain/dem-atlas.js +534 -0
- package/dist/view/terrain/detect.d.ts +126 -0
- package/dist/view/terrain/detect.js +198 -0
- package/dist/view/terrain/drape/bin-store.d.ts +179 -0
- package/dist/view/terrain/drape/bin-store.js +529 -0
- package/dist/view/terrain/drape/binning.d.ts +259 -0
- package/dist/view/terrain/drape/binning.js +604 -0
- package/dist/view/terrain/drape/edge-constrain.d.ts +37 -0
- package/dist/view/terrain/drape/edge-constrain.js +175 -0
- package/dist/view/terrain/drape/geometry.d.ts +95 -0
- package/dist/view/terrain/drape/geometry.js +260 -0
- package/dist/view/terrain/drape/mesh.d.ts +50 -0
- package/dist/view/terrain/drape/mesh.js +86 -0
- package/dist/view/terrain/drape/pass.d.ts +122 -0
- package/dist/view/terrain/drape/pass.js +379 -0
- package/dist/view/terrain/drape/quad-glyphs.d.ts +64 -0
- package/dist/view/terrain/drape/quad-glyphs.js +117 -0
- package/dist/view/terrain/drape/quad.d.ts +249 -0
- package/dist/view/terrain/drape/quad.js +835 -0
- package/dist/view/terrain/drape/renderer.d.ts +184 -0
- package/dist/view/terrain/drape/renderer.js +706 -0
- package/dist/view/terrain/drape/shared.d.ts +80 -0
- package/dist/view/terrain/drape/shared.js +173 -0
- package/dist/view/terrain/drape/stitch.d.ts +155 -0
- package/dist/view/terrain/drape/stitch.js +228 -0
- package/dist/view/terrain/ground.d.ts +72 -0
- package/dist/view/terrain/ground.js +151 -0
- package/dist/view/terrain/metrics.d.ts +180 -0
- package/dist/view/terrain/metrics.js +231 -0
- package/dist/view/terrain/occlusion.d.ts +11 -0
- package/dist/view/terrain/occlusion.js +129 -0
- package/dist/view/terrain/polygon.d.ts +86 -0
- package/dist/view/terrain/polygon.js +301 -0
- package/dist/view/terrain/shade.d.ts +48 -0
- package/dist/view/terrain/shade.js +45 -0
- package/dist/view/terrain/state.d.ts +99 -0
- package/dist/view/terrain/state.js +186 -0
- package/dist/view/terrain/tessellation.d.ts +265 -0
- package/dist/view/terrain/tessellation.js +924 -0
- package/dist/view/terrain/tiling.d.ts +56 -0
- package/dist/view/terrain/tiling.js +155 -0
- package/dist/view/terrain/upstream-terrain.d.ts +82 -0
- package/dist/view/terrain/upstream-terrain.js +116 -0
- package/dist/view/ui/auxiliary-handles.d.ts +151 -0
- package/dist/view/ui/auxiliary-handles.js +22 -0
- package/dist/view/ui/bounds.d.ts +53 -0
- package/dist/view/ui/bounds.js +149 -0
- package/dist/view/ui/box-selection.d.ts +1 -0
- package/dist/view/ui/box-selection.js +83 -0
- package/dist/view/ui/handle-test.d.ts +1 -0
- package/dist/view/ui/handle-test.js +437 -0
- package/dist/view/ui/handle-thinning.d.ts +122 -0
- package/dist/view/ui/handle-thinning.js +430 -0
- package/dist/view/ui/handles.d.ts +25 -0
- package/dist/view/ui/handles.js +673 -0
- package/dist/view/ui/helper.d.ts +19 -0
- package/dist/view/ui/helper.js +76 -0
- package/dist/view/ui/index.d.ts +15 -0
- package/dist/view/ui/index.js +15 -0
- package/dist/view/ui/selection-scope.d.ts +25 -0
- package/dist/view/ui/selection-scope.js +34 -0
- package/dist/view/ui/selection-ui/bounding-box.d.ts +44 -0
- package/dist/view/ui/selection-ui/bounding-box.js +240 -0
- package/dist/view/ui/selection-ui/extension-registry.d.ts +76 -0
- package/dist/view/ui/selection-ui/extension-registry.js +78 -0
- package/dist/view/ui/selection-ui/index.d.ts +12 -0
- package/dist/view/ui/selection-ui/index.js +12 -0
- package/dist/view/ui/selection-ui/renderer.d.ts +29 -0
- package/dist/view/ui/selection-ui/renderer.js +227 -0
- package/dist/view/ui/selection-ui/types.d.ts +55 -0
- package/dist/view/ui/selection-ui/types.js +3 -0
- package/dist/view/ui/selection-ui-drawer.d.ts +52 -0
- package/dist/view/ui/selection-ui-drawer.js +329 -0
- package/dist/view/ui/tentative.d.ts +1 -0
- package/dist/view/ui/tentative.js +320 -0
- package/dist/view/viewport.d.ts +119 -0
- package/dist/view/viewport.js +254 -0
- package/package.json +113 -0
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
// SPDX-FileCopyrightText: 2026 SAKAIDA Atsushi
|
|
2
|
+
// SPDX-License-Identifier: AGPL-3.0-only
|
|
3
|
+
/**
|
|
4
|
+
* Deciding whether a symbol is occluded by the terrain (whether to ghost it)
|
|
5
|
+
*
|
|
6
|
+
* Points, icons and billboard labels are drawn without a depth test. A billboard's front/back
|
|
7
|
+
* order is decided by the depth of its single center point, so enabling depth makes the whole
|
|
8
|
+
* thing disappear as soon as it sinks a few meters into the ground (confirmed by measurement).
|
|
9
|
+
* On the other hand, a symbol behind a mountain stays clearly visible, which is misread as
|
|
10
|
+
* "being in front". So what is occluded is drawn faintly (a CesiumJS-style ghost).
|
|
11
|
+
*
|
|
12
|
+
* The test runs on the CPU. The segment drawn from the camera to the anchor is sampled, and if
|
|
13
|
+
* the ground elevation along the way rises above the segment it is occluded. Fetching a single
|
|
14
|
+
* elevation takes about 0.4us, so 24 samples along the segment cost about 10us per anchor,
|
|
15
|
+
* which is enough. Solving it on the GPU would mean ray marching in the fragment shader, whose
|
|
16
|
+
* cost per symbol is far larger.
|
|
17
|
+
*
|
|
18
|
+
* Reading the depth buffer (MapLibre's `terrain.depthAtPoint`) is not an option: `readPixels`
|
|
19
|
+
* runs for every single point, which inserts a synchronous wait and stalls rendering once per
|
|
20
|
+
* symbol.
|
|
21
|
+
*/
|
|
22
|
+
import { drawnLongitude } from './anchor.js';
|
|
23
|
+
import { groundElevationMeters } from './ground.js';
|
|
24
|
+
import { mercatorX, mercatorY } from './tessellation.js';
|
|
25
|
+
/**
|
|
26
|
+
* Opacity of an occluded symbol
|
|
27
|
+
*
|
|
28
|
+
* @internal
|
|
29
|
+
*/
|
|
30
|
+
export const TERRAIN_GHOST_OPACITY = 0.35;
|
|
31
|
+
/** Upper bound on the number of samples along the segment */
|
|
32
|
+
const MAX_SAMPLES = 24;
|
|
33
|
+
/** Lower bound on the number of samples along the segment */
|
|
34
|
+
const MIN_SAMPLES = 6;
|
|
35
|
+
/**
|
|
36
|
+
* Margin discarded on the camera side
|
|
37
|
+
*
|
|
38
|
+
* The ground directly below the camera almost always rises above the segment (the camera is
|
|
39
|
+
* above the ground, but the line of sight points downward, so it grazes the ground just past
|
|
40
|
+
* the starting point). Looking there would make everything occluded, so the first few percent
|
|
41
|
+
* on the start side are skipped.
|
|
42
|
+
*/
|
|
43
|
+
const NEAR_SKIP = 0.08;
|
|
44
|
+
/**
|
|
45
|
+
* Margin discarded on the anchor side (counted in terrain mesh nodes)
|
|
46
|
+
*
|
|
47
|
+
* The anchor sits on top of the ground, so the ground just in front of it is necessarily at
|
|
48
|
+
* the same height. To keep it from hiding itself, two nodes' worth is skipped.
|
|
49
|
+
*/
|
|
50
|
+
const SELF_SKIP_NODES = 2;
|
|
51
|
+
/**
|
|
52
|
+
* Converts a Mercator y back into a latitude
|
|
53
|
+
*/
|
|
54
|
+
function inverseMercatorY(y) {
|
|
55
|
+
return (Math.atan(Math.sinh(Math.PI * (1 - 2 * y))) * 180) / Math.PI;
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Whether an anchor is occluded by the terrain
|
|
59
|
+
*
|
|
60
|
+
* Always false (not occluded) when the terrain is disabled or the camera position cannot be
|
|
61
|
+
* obtained. This matches the appearance before the terrain was introduced exactly.
|
|
62
|
+
*
|
|
63
|
+
* @internal
|
|
64
|
+
*/
|
|
65
|
+
export function isAnchorOccluded(context, lng, lat) {
|
|
66
|
+
const frame = context.anchorFrame;
|
|
67
|
+
const terrain = frame.terrain;
|
|
68
|
+
const camera = frame.cameraMercator;
|
|
69
|
+
if (!terrain || !camera)
|
|
70
|
+
return false;
|
|
71
|
+
const scale = frame.elevationScale;
|
|
72
|
+
if (!(scale > 0))
|
|
73
|
+
return false;
|
|
74
|
+
const cached = context.occlusionCache.get(`${lng},${lat}`);
|
|
75
|
+
if (cached !== undefined)
|
|
76
|
+
return cached;
|
|
77
|
+
const result = computeOccluded(lng, lat, scale, camera, terrain, context);
|
|
78
|
+
context.occlusionCache.set(`${lng},${lat}`, result);
|
|
79
|
+
return result;
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* Returns the opacity for a symbol at a point: 0.35 when the terrain hides it from the camera,
|
|
83
|
+
* 1 otherwise.
|
|
84
|
+
*
|
|
85
|
+
* @param context The terrain state of the draw instance
|
|
86
|
+
* @param lng The longitude in degrees
|
|
87
|
+
* @param lat The latitude in degrees
|
|
88
|
+
* @returns 0.35 or 1. Always 1 when the terrain is disabled
|
|
89
|
+
*/
|
|
90
|
+
export function anchorGhostOpacity(context, lng, lat) {
|
|
91
|
+
return isAnchorOccluded(context, lng, lat) ? TERRAIN_GHOST_OPACITY : 1;
|
|
92
|
+
}
|
|
93
|
+
function computeOccluded(lng, lat, scale, camera, terrain, context) {
|
|
94
|
+
const frame = context.anchorFrame;
|
|
95
|
+
// The elevation of the ground maplibre draws (ground.ts), on the copy of the world the
|
|
96
|
+
// anchor is drawn on
|
|
97
|
+
const elevationAt = (sampleLng, sampleLat) => groundElevationMeters(frame.elevationQuery, terrain, context.coverage, drawnLongitude(context, sampleLng), sampleLat, frame.tileZoom);
|
|
98
|
+
const ax = mercatorX(lng);
|
|
99
|
+
const ay = mercatorY(lat);
|
|
100
|
+
const az = elevationAt(lng, lat) * scale;
|
|
101
|
+
const [cx, cy, cz] = camera;
|
|
102
|
+
const dx = ax - cx;
|
|
103
|
+
const dy = ay - cy;
|
|
104
|
+
const dz = az - cz;
|
|
105
|
+
const horizontal = Math.hypot(dx, dy);
|
|
106
|
+
if (!(horizontal > 0))
|
|
107
|
+
return false;
|
|
108
|
+
// The sample spacing matches the node spacing of the terrain mesh (sampling any finer only
|
|
109
|
+
// goes beyond the resolution of the bilinear interpolation and adds no information)
|
|
110
|
+
const node = context.renderState.stepGrid > 0 ? context.renderState.stepGrid : horizontal / 16;
|
|
111
|
+
const samples = Math.min(MAX_SAMPLES, Math.max(MIN_SAMPLES, Math.ceil(horizontal / node)));
|
|
112
|
+
// The ground just in front of the anchor is the anchor's own ground, so it is skipped
|
|
113
|
+
const selfSkip = Math.min(0.5, (SELF_SKIP_NODES * node) / horizontal);
|
|
114
|
+
const tEnd = 1 - selfSkip;
|
|
115
|
+
if (!(tEnd > NEAR_SKIP))
|
|
116
|
+
return false;
|
|
117
|
+
for (let i = 0; i < samples; i++) {
|
|
118
|
+
const t = NEAR_SKIP + ((tEnd - NEAR_SKIP) * (i + 0.5)) / samples;
|
|
119
|
+
const px = cx + dx * t;
|
|
120
|
+
const py = cy + dy * t;
|
|
121
|
+
const pz = cz + dz * t;
|
|
122
|
+
const sampleLng = px * 360 - 180;
|
|
123
|
+
const sampleLat = inverseMercatorY(py);
|
|
124
|
+
const surface = elevationAt(sampleLng, sampleLat) * scale;
|
|
125
|
+
if (surface > pz)
|
|
126
|
+
return true;
|
|
127
|
+
}
|
|
128
|
+
return false;
|
|
129
|
+
}
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The injection point for terrain subdivision of polygons
|
|
3
|
+
*
|
|
4
|
+
* Called from the polygon renderers (polygon/sdf-polygon.ts and polygon/batch.ts). When the
|
|
5
|
+
* terrain is disabled, `getTerrainTessellationStep(context)` returns null and the caller does
|
|
6
|
+
* nothing. Rendering without terrain therefore does not change by a single byte.
|
|
7
|
+
*/
|
|
8
|
+
import type { Coordinate } from '../../store/types.js';
|
|
9
|
+
import type { TerrainContext } from './context.js';
|
|
10
|
+
import { type MercatorRect, type TessellationStep } from './tessellation.js';
|
|
11
|
+
/**
|
|
12
|
+
* Returns the subdivision step of the terrain mesh for the current frame.
|
|
13
|
+
*
|
|
14
|
+
* @param context The terrain state of the draw instance
|
|
15
|
+
* @returns The step, or `null` when the terrain is disabled
|
|
16
|
+
*/
|
|
17
|
+
export declare function getTerrainTessellationStep(context: TerrainContext): TessellationStep | null;
|
|
18
|
+
/**
|
|
19
|
+
* Decides the effective step for a single feature
|
|
20
|
+
*
|
|
21
|
+
* The step is decided by the screen (the zoom), but for a large feature the grid can reach
|
|
22
|
+
* hundreds of thousands of cells. Cutting off once the upper bound is reached produces the
|
|
23
|
+
* distorted result "only the first triangles are fine and the rest stay coarse", and only part
|
|
24
|
+
* of the polygon sits on the terrain. So instead of cutting off, the step is coarsened by a
|
|
25
|
+
* power of 2 and the whole feature is subdivided uniformly. It is a power of 2 so that the
|
|
26
|
+
* coarse grid is a subset of the fine grid (the fill and the outline are subdivided at the
|
|
27
|
+
* same points).
|
|
28
|
+
*/
|
|
29
|
+
export declare function polygonTessellationStep(base: TessellationStep, flatCoords: readonly number[]): TessellationStep;
|
|
30
|
+
/**
|
|
31
|
+
* The number of grid cells the bounding box covers (an estimate, from a flat coordinate array)
|
|
32
|
+
*
|
|
33
|
+
* Outside the range (region) nothing is subdivided, so that part is not counted.
|
|
34
|
+
*/
|
|
35
|
+
export declare function boundingCells(flatCoords: readonly number[], grid: number, region?: MercatorRect | null): number;
|
|
36
|
+
/**
|
|
37
|
+
* The number of grid cells the bounding box covers (an estimate, from rings)
|
|
38
|
+
*
|
|
39
|
+
* Every feature is scanned in order to estimate the total amount of the batch, so the count is
|
|
40
|
+
* made without building an array for flattening.
|
|
41
|
+
*/
|
|
42
|
+
export declare function ringBoundingCells(ring: readonly Coordinate[], grid: number, region?: MercatorRect | null): number;
|
|
43
|
+
/**
|
|
44
|
+
* Whether the bounding box of a flat coordinate array intersects the range
|
|
45
|
+
*/
|
|
46
|
+
export declare function flatCoordsIntersect(flatCoords: readonly number[], region: MercatorRect): boolean;
|
|
47
|
+
/**
|
|
48
|
+
* Whether the bounding box of an array of rings intersects the range
|
|
49
|
+
*/
|
|
50
|
+
export declare function ringsIntersect(rings: readonly Coordinate[][], region: MercatorRect): boolean;
|
|
51
|
+
/**
|
|
52
|
+
* Decides the coarsening factor applied to the whole batch (a power of 2)
|
|
53
|
+
*
|
|
54
|
+
* A per-feature upper bound cannot hold back a batch with thousands of features (a
|
|
55
|
+
* dataset). The total number of cells is estimated before construction, and if
|
|
56
|
+
* it exceeds the budget the whole batch is coarsened uniformly. Applying a different factor
|
|
57
|
+
* per feature would put neighboring features on different grids and make them disagree at the
|
|
58
|
+
* boundary, so there is one factor per batch.
|
|
59
|
+
*
|
|
60
|
+
* @param totalCells The sum of the bounding box cell counts of all features
|
|
61
|
+
*/
|
|
62
|
+
export declare function batchTessellationFactor(totalCells: number): number;
|
|
63
|
+
/**
|
|
64
|
+
* Subdivides the fill of a single feature and returns the step actually used
|
|
65
|
+
*
|
|
66
|
+
* When the step was too fine to finish subdividing (the vertex budget ran out, or a single
|
|
67
|
+
* triangle sweeps too many cells), emitting it as is produces a mixture where "part of it
|
|
68
|
+
* follows the terrain while the rest are flat triangles breaking through the terrain". This is
|
|
69
|
+
* what is behind the wedge-shaped breakage sticking out of the terrain, so it is coarsened and
|
|
70
|
+
* subdivided again. Subdividing the outline with the returned step as well guarantees that the
|
|
71
|
+
* fill and the outline lie on the same grid.
|
|
72
|
+
*
|
|
73
|
+
* @returns The subdivision result and the step that produced it
|
|
74
|
+
*/
|
|
75
|
+
export declare function tessellatePolygonFill(context: TerrainContext, featureId: string | undefined, partIndex: number, flatCoords: number[], indices: number[], base: TessellationStep): {
|
|
76
|
+
flatCoords: number[];
|
|
77
|
+
indices: number[];
|
|
78
|
+
step: TessellationStep;
|
|
79
|
+
};
|
|
80
|
+
/**
|
|
81
|
+
* Subdivides the rings of the outline
|
|
82
|
+
*
|
|
83
|
+
* It uses the same grid as the fill, so the edge of the fill and the vertices of the outline
|
|
84
|
+
* still agree after subdivision.
|
|
85
|
+
*/
|
|
86
|
+
export declare function tessellateRings(rings: Coordinate[][], step: TessellationStep): Coordinate[][];
|
|
@@ -0,0 +1,301 @@
|
|
|
1
|
+
// SPDX-FileCopyrightText: 2026 SAKAIDA Atsushi
|
|
2
|
+
// SPDX-License-Identifier: AGPL-3.0-only
|
|
3
|
+
import { TERRAIN_MAX_BATCH_SUBDIVISION_CELLS, TERRAIN_MAX_COARSEN_STEPS, TERRAIN_MAX_SUBDIVISION_POINTS, TERRAIN_MAX_TILED_SUBDIVISION_POINTS, } from './metrics.js';
|
|
4
|
+
import { getTerrainRenderState } from './state.js';
|
|
5
|
+
import { cellsInRegion, densifyRings, mercatorX, mercatorY, rectsIntersect, subdivideTriangles, } from './tessellation.js';
|
|
6
|
+
import { tilingSignature } from './tiling.js';
|
|
7
|
+
/**
|
|
8
|
+
* Returns the subdivision step of the terrain mesh for the current frame.
|
|
9
|
+
*
|
|
10
|
+
* @param context The terrain state of the draw instance
|
|
11
|
+
* @returns The step, or `null` when the terrain is disabled
|
|
12
|
+
*/
|
|
13
|
+
export function getTerrainTessellationStep(context) {
|
|
14
|
+
const terrain = getTerrainRenderState(context);
|
|
15
|
+
if (!terrain.active || !(terrain.stepGrid > 0))
|
|
16
|
+
return null;
|
|
17
|
+
const tiling = terrain.tessellationTiling;
|
|
18
|
+
return {
|
|
19
|
+
grid: terrain.stepGrid,
|
|
20
|
+
// When subdividing to match the tile's actual mesh, the amount is capped by the view, so
|
|
21
|
+
// the upper bound is set high (falling back to coarsening midway makes the fill drop out
|
|
22
|
+
// on ridges)
|
|
23
|
+
maxPoints: tiling ? TERRAIN_MAX_TILED_SUBDIVISION_POINTS : TERRAIN_MAX_SUBDIVISION_POINTS,
|
|
24
|
+
region: terrain.tessellationRegion,
|
|
25
|
+
tiling,
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Decides the effective step for a single feature
|
|
30
|
+
*
|
|
31
|
+
* The step is decided by the screen (the zoom), but for a large feature the grid can reach
|
|
32
|
+
* hundreds of thousands of cells. Cutting off once the upper bound is reached produces the
|
|
33
|
+
* distorted result "only the first triangles are fine and the rest stay coarse", and only part
|
|
34
|
+
* of the polygon sits on the terrain. So instead of cutting off, the step is coarsened by a
|
|
35
|
+
* power of 2 and the whole feature is subdivided uniformly. It is a power of 2 so that the
|
|
36
|
+
* coarse grid is a subset of the fine grid (the fill and the outline are subdivided at the
|
|
37
|
+
* same points).
|
|
38
|
+
*/
|
|
39
|
+
export function polygonTessellationStep(base, flatCoords) {
|
|
40
|
+
// When subdividing to match the tile's actual mesh, no coarsening is applied. Coarsening
|
|
41
|
+
// would make the step coarser than the tile's mesh, and our own chord would dive below the
|
|
42
|
+
// ridge and be carved away. The amount is capped by the number of tiles, so no brake is
|
|
43
|
+
// needed in the first place.
|
|
44
|
+
if (base.tiling)
|
|
45
|
+
return base;
|
|
46
|
+
const cells = boundingCells(flatCoords, base.grid, base.region);
|
|
47
|
+
if (!(cells > base.maxPoints))
|
|
48
|
+
return base;
|
|
49
|
+
// Coarsened by a power of 2 (the coarse grid still lies on a subset of the terrain mesh
|
|
50
|
+
// nodes)
|
|
51
|
+
const factor = 2 ** Math.ceil(Math.log2(Math.sqrt(cells / base.maxPoints)));
|
|
52
|
+
return coarsen(base, factor);
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* The number of grid cells the bounding box covers (an estimate, from a flat coordinate array)
|
|
56
|
+
*
|
|
57
|
+
* Outside the range (region) nothing is subdivided, so that part is not counted.
|
|
58
|
+
*/
|
|
59
|
+
export function boundingCells(flatCoords, grid, region) {
|
|
60
|
+
if (flatCoords.length < 2 || !(grid > 0))
|
|
61
|
+
return 0;
|
|
62
|
+
let minX = Number.POSITIVE_INFINITY;
|
|
63
|
+
let maxX = Number.NEGATIVE_INFINITY;
|
|
64
|
+
let minY = Number.POSITIVE_INFINITY;
|
|
65
|
+
let maxY = Number.NEGATIVE_INFINITY;
|
|
66
|
+
for (let i = 0; i < flatCoords.length; i += 2) {
|
|
67
|
+
const x = flatCoords[i];
|
|
68
|
+
const y = flatCoords[i + 1];
|
|
69
|
+
if (x < minX)
|
|
70
|
+
minX = x;
|
|
71
|
+
if (x > maxX)
|
|
72
|
+
maxX = x;
|
|
73
|
+
if (y < minY)
|
|
74
|
+
minY = y;
|
|
75
|
+
if (y > maxY)
|
|
76
|
+
maxY = y;
|
|
77
|
+
}
|
|
78
|
+
return cellsFromBounds(minX, maxX, minY, maxY, grid, region);
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* The number of grid cells the bounding box covers (an estimate, from rings)
|
|
82
|
+
*
|
|
83
|
+
* Every feature is scanned in order to estimate the total amount of the batch, so the count is
|
|
84
|
+
* made without building an array for flattening.
|
|
85
|
+
*/
|
|
86
|
+
export function ringBoundingCells(ring, grid, region) {
|
|
87
|
+
if (ring.length === 0 || !(grid > 0))
|
|
88
|
+
return 0;
|
|
89
|
+
let minX = Number.POSITIVE_INFINITY;
|
|
90
|
+
let maxX = Number.NEGATIVE_INFINITY;
|
|
91
|
+
let minY = Number.POSITIVE_INFINITY;
|
|
92
|
+
let maxY = Number.NEGATIVE_INFINITY;
|
|
93
|
+
for (const [x, y] of ring) {
|
|
94
|
+
if (x < minX)
|
|
95
|
+
minX = x;
|
|
96
|
+
if (x > maxX)
|
|
97
|
+
maxX = x;
|
|
98
|
+
if (y < minY)
|
|
99
|
+
minY = y;
|
|
100
|
+
if (y > maxY)
|
|
101
|
+
maxY = y;
|
|
102
|
+
}
|
|
103
|
+
return cellsFromBounds(minX, maxX, minY, maxY, grid, region);
|
|
104
|
+
}
|
|
105
|
+
/**
|
|
106
|
+
* The fingerprint of the conditions that decide this feature's subdivision
|
|
107
|
+
*
|
|
108
|
+
* It is decided by the tile configuration covering it (which cell has which step), the
|
|
109
|
+
* coarsening factor and the relationship with the range. The elevation is not included,
|
|
110
|
+
* because the shader looks the elevation up every frame, so there is no need to subdivide
|
|
111
|
+
* again when the DEM arrives.
|
|
112
|
+
*/
|
|
113
|
+
function fillSignature(step, flatCoords) {
|
|
114
|
+
if (flatCoords.length < 2)
|
|
115
|
+
return 'empty';
|
|
116
|
+
let minLng = Number.POSITIVE_INFINITY;
|
|
117
|
+
let maxLng = Number.NEGATIVE_INFINITY;
|
|
118
|
+
let minLat = Number.POSITIVE_INFINITY;
|
|
119
|
+
let maxLat = Number.NEGATIVE_INFINITY;
|
|
120
|
+
for (let i = 0; i < flatCoords.length; i += 2) {
|
|
121
|
+
const lng = flatCoords[i];
|
|
122
|
+
const lat = flatCoords[i + 1];
|
|
123
|
+
if (lng < minLng)
|
|
124
|
+
minLng = lng;
|
|
125
|
+
if (lng > maxLng)
|
|
126
|
+
maxLng = lng;
|
|
127
|
+
if (lat < minLat)
|
|
128
|
+
minLat = lat;
|
|
129
|
+
if (lat > maxLat)
|
|
130
|
+
maxLat = lat;
|
|
131
|
+
}
|
|
132
|
+
return tilingSignature(step.tiling ?? null, step.region ?? null, step.coarsen && step.coarsen > 1 ? step.coarsen : 1, step.grid, mercatorX(minLng),
|
|
133
|
+
// Mercator y decreases as the latitude increases
|
|
134
|
+
mercatorY(maxLat), mercatorX(maxLng), mercatorY(minLat));
|
|
135
|
+
}
|
|
136
|
+
/**
|
|
137
|
+
* Whether the bounding box of a flat coordinate array intersects the range
|
|
138
|
+
*/
|
|
139
|
+
export function flatCoordsIntersect(flatCoords, region) {
|
|
140
|
+
if (flatCoords.length < 2)
|
|
141
|
+
return false;
|
|
142
|
+
let minLng = Number.POSITIVE_INFINITY;
|
|
143
|
+
let maxLng = Number.NEGATIVE_INFINITY;
|
|
144
|
+
let minLat = Number.POSITIVE_INFINITY;
|
|
145
|
+
let maxLat = Number.NEGATIVE_INFINITY;
|
|
146
|
+
for (let i = 0; i < flatCoords.length; i += 2) {
|
|
147
|
+
const lng = flatCoords[i];
|
|
148
|
+
const lat = flatCoords[i + 1];
|
|
149
|
+
if (lng < minLng)
|
|
150
|
+
minLng = lng;
|
|
151
|
+
if (lng > maxLng)
|
|
152
|
+
maxLng = lng;
|
|
153
|
+
if (lat < minLat)
|
|
154
|
+
minLat = lat;
|
|
155
|
+
if (lat > maxLat)
|
|
156
|
+
maxLat = lat;
|
|
157
|
+
}
|
|
158
|
+
// Mercator y decreases as the latitude increases
|
|
159
|
+
return rectsIntersect(mercatorX(minLng), mercatorY(maxLat), mercatorX(maxLng), mercatorY(minLat), region);
|
|
160
|
+
}
|
|
161
|
+
/**
|
|
162
|
+
* Whether the bounding box of an array of rings intersects the range
|
|
163
|
+
*/
|
|
164
|
+
export function ringsIntersect(rings, region) {
|
|
165
|
+
let minLng = Number.POSITIVE_INFINITY;
|
|
166
|
+
let maxLng = Number.NEGATIVE_INFINITY;
|
|
167
|
+
let minLat = Number.POSITIVE_INFINITY;
|
|
168
|
+
let maxLat = Number.NEGATIVE_INFINITY;
|
|
169
|
+
for (const ring of rings) {
|
|
170
|
+
for (const [lng, lat] of ring) {
|
|
171
|
+
if (lng < minLng)
|
|
172
|
+
minLng = lng;
|
|
173
|
+
if (lng > maxLng)
|
|
174
|
+
maxLng = lng;
|
|
175
|
+
if (lat < minLat)
|
|
176
|
+
minLat = lat;
|
|
177
|
+
if (lat > maxLat)
|
|
178
|
+
maxLat = lat;
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
if (minLng > maxLng)
|
|
182
|
+
return false;
|
|
183
|
+
return rectsIntersect(mercatorX(minLng), mercatorY(maxLat), mercatorX(maxLng), mercatorY(minLat), region);
|
|
184
|
+
}
|
|
185
|
+
/**
|
|
186
|
+
* Converts a longitude/latitude bounding box into a number of Mercator cells (an estimate is
|
|
187
|
+
* enough)
|
|
188
|
+
*/
|
|
189
|
+
function cellsFromBounds(minX, maxX, minY, maxY, grid, region) {
|
|
190
|
+
// Mercator y decreases as the latitude increases, so top and bottom are swapped
|
|
191
|
+
return cellsInRegion(mercatorX(minX), mercatorY(maxY), mercatorX(maxX), mercatorY(minY), grid, region);
|
|
192
|
+
}
|
|
193
|
+
/**
|
|
194
|
+
* Coarsens the step by a power of 2
|
|
195
|
+
*/
|
|
196
|
+
function coarsen(step, factor) {
|
|
197
|
+
if (!(factor > 1))
|
|
198
|
+
return step;
|
|
199
|
+
if (step.tiling) {
|
|
200
|
+
// When matching the tile's actual mesh, the factor is raised rather than the step
|
|
201
|
+
// (being a power of 2, the subdivision points keep lying on a subset of the nodes)
|
|
202
|
+
return { ...step, coarsen: (step.coarsen ?? 1) * factor };
|
|
203
|
+
}
|
|
204
|
+
return { grid: step.grid * factor, maxPoints: step.maxPoints, region: step.region };
|
|
205
|
+
}
|
|
206
|
+
/**
|
|
207
|
+
* Decides the coarsening factor applied to the whole batch (a power of 2)
|
|
208
|
+
*
|
|
209
|
+
* A per-feature upper bound cannot hold back a batch with thousands of features (a
|
|
210
|
+
* dataset). The total number of cells is estimated before construction, and if
|
|
211
|
+
* it exceeds the budget the whole batch is coarsened uniformly. Applying a different factor
|
|
212
|
+
* per feature would put neighboring features on different grids and make them disagree at the
|
|
213
|
+
* boundary, so there is one factor per batch.
|
|
214
|
+
*
|
|
215
|
+
* @param totalCells The sum of the bounding box cell counts of all features
|
|
216
|
+
*/
|
|
217
|
+
export function batchTessellationFactor(totalCells) {
|
|
218
|
+
if (!(totalCells > TERRAIN_MAX_BATCH_SUBDIVISION_CELLS))
|
|
219
|
+
return 1;
|
|
220
|
+
const steps = Math.ceil(Math.log2(Math.sqrt(totalCells / TERRAIN_MAX_BATCH_SUBDIVISION_CELLS)));
|
|
221
|
+
return 2 ** Math.min(steps, TERRAIN_MAX_COARSEN_STEPS);
|
|
222
|
+
}
|
|
223
|
+
/**
|
|
224
|
+
* Subdivides the fill of a single feature and returns the step actually used
|
|
225
|
+
*
|
|
226
|
+
* When the step was too fine to finish subdividing (the vertex budget ran out, or a single
|
|
227
|
+
* triangle sweeps too many cells), emitting it as is produces a mixture where "part of it
|
|
228
|
+
* follows the terrain while the rest are flat triangles breaking through the terrain". This is
|
|
229
|
+
* what is behind the wedge-shaped breakage sticking out of the terrain, so it is coarsened and
|
|
230
|
+
* subdivided again. Subdividing the outline with the returned step as well guarantees that the
|
|
231
|
+
* fill and the outline lie on the same grid.
|
|
232
|
+
*
|
|
233
|
+
* @returns The subdivision result and the step that produced it
|
|
234
|
+
*/
|
|
235
|
+
export function tessellatePolygonFill(context, featureId, partIndex, flatCoords, indices, base) {
|
|
236
|
+
// Features outside the range are left alone. Since it is already decided that they will not
|
|
237
|
+
// be subdivided, even building the vertex pool (registering every coordinate into a Map) is
|
|
238
|
+
// expensive. With 8,172 administrative boundaries and 1.1 million vertices in total, this
|
|
239
|
+
// early exit decides most of the construction time (measured: 3.9 s -> 0.5 s).
|
|
240
|
+
if (base.region && !flatCoordsIntersect(flatCoords, base.region)) {
|
|
241
|
+
return { flatCoords, indices, step: base };
|
|
242
|
+
}
|
|
243
|
+
// The subdivision cache belongs to the draw instance whose context is passed. Back when it
|
|
244
|
+
// was shared globally, triangles subdivided with a different step were returned whenever
|
|
245
|
+
// the generation happened to coincide with another instance's.
|
|
246
|
+
const fillCache = context.fillCache;
|
|
247
|
+
// The fingerprint of the conditions that decide this feature's subdivision (the covering
|
|
248
|
+
// tile configuration, the coarsening and the relationship with the range). As long as the
|
|
249
|
+
// conditions are the same it can be reused even when the range moves or a chunk is re-baked
|
|
250
|
+
const signature = fillSignature(base, flatCoords);
|
|
251
|
+
if (featureId) {
|
|
252
|
+
const cached = fillCache.get(featureId, partIndex, signature, indices);
|
|
253
|
+
if (cached) {
|
|
254
|
+
return {
|
|
255
|
+
flatCoords: cached.flatCoords,
|
|
256
|
+
indices: cached.indices,
|
|
257
|
+
step: {
|
|
258
|
+
grid: cached.grid,
|
|
259
|
+
maxPoints: base.maxPoints,
|
|
260
|
+
region: base.region,
|
|
261
|
+
tiling: cached.grid === base.grid ? base.tiling : null,
|
|
262
|
+
},
|
|
263
|
+
};
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
let step = polygonTessellationStep(base, flatCoords);
|
|
267
|
+
let result = subdivideTriangles(flatCoords, indices, step);
|
|
268
|
+
let coarsened = 0;
|
|
269
|
+
while (result.overflowed && coarsened < TERRAIN_MAX_COARSEN_STEPS) {
|
|
270
|
+
// The required factor is derived from the amount of the overflow, rounded up to a power
|
|
271
|
+
// of 2 and applied in one go (trying factor 2 at a time would mean redoing it up to the
|
|
272
|
+
// upper bound every time for a huge feature)
|
|
273
|
+
const wanted = Math.max(2, 2 ** Math.ceil(Math.log2(result.overflowFactor)));
|
|
274
|
+
const remaining = 2 ** (TERRAIN_MAX_COARSEN_STEPS - coarsened);
|
|
275
|
+
const factor = Math.min(wanted, remaining);
|
|
276
|
+
coarsened += Math.round(Math.log2(factor));
|
|
277
|
+
step = coarsen(step, factor);
|
|
278
|
+
result = subdivideTriangles(flatCoords, indices, step);
|
|
279
|
+
}
|
|
280
|
+
if (featureId) {
|
|
281
|
+
fillCache.set(featureId, partIndex, signature, indices, result.flatCoords, result.indices, step.grid);
|
|
282
|
+
}
|
|
283
|
+
return { flatCoords: result.flatCoords, indices: result.indices, step };
|
|
284
|
+
}
|
|
285
|
+
/**
|
|
286
|
+
* Subdivides the rings of the outline
|
|
287
|
+
*
|
|
288
|
+
* It uses the same grid as the fill, so the edge of the fill and the vertices of the outline
|
|
289
|
+
* still agree after subdivision.
|
|
290
|
+
*/
|
|
291
|
+
export function tessellateRings(rings, step) {
|
|
292
|
+
// Outside the range nothing is subdivided (the same test as the fill; if the two disagree
|
|
293
|
+
// they drift apart)
|
|
294
|
+
if (step.region && !ringsIntersect(rings, step.region))
|
|
295
|
+
return rings;
|
|
296
|
+
// The coarsening applied to the fill is applied to the outline too. Forgetting it would
|
|
297
|
+
// subdivide only the outline on the finest grid and make the number of points jump (both
|
|
298
|
+
// construction and rendering get heavier)
|
|
299
|
+
const coarsen = step.coarsen && step.coarsen > 1 ? step.coarsen : 1;
|
|
300
|
+
return densifyRings(rings, coarsen > 1 ? { ...step, grid: step.grid * coarsen } : step);
|
|
301
|
+
}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Formula for terrain shading (an aid to reading the map)
|
|
3
|
+
*
|
|
4
|
+
* There are two paths that put a polygon fill onto the ground surface: analytic drape
|
|
5
|
+
* (filling as pixels of the ground surface, `drape/renderer.ts` and `drape/quad.ts`) and
|
|
6
|
+
* vertex displacement (the path in `shaders/helpers.ts` that lifts the vertices of a
|
|
7
|
+
* feature and fills them). So that both produce the same pixels, the formula that derives
|
|
8
|
+
* the brightness factor from the gradient is kept here in a single place, and only the way
|
|
9
|
+
* the samples are taken is adapted to each path. If each path had its own formula, the
|
|
10
|
+
* brightness of the fill would jump where the bands switch over.
|
|
11
|
+
*
|
|
12
|
+
* Shading does not change the hue; it is applied to the brightness only. It is not 3D
|
|
13
|
+
* lighting that illuminates symbols with an arbitrary light, it is also distinct from
|
|
14
|
+
* vertical exaggeration of the terrain (which follows the map's own factor), and it does not
|
|
15
|
+
* change the dimensions of the relief at all.
|
|
16
|
+
*/
|
|
17
|
+
/**
|
|
18
|
+
* Light source for the shading
|
|
19
|
+
*
|
|
20
|
+
* It is a pair of a unit vector in the map coordinate system (east, north, up) and a
|
|
21
|
+
* strength.
|
|
22
|
+
*/
|
|
23
|
+
export interface TerrainShadeLight {
|
|
24
|
+
/** Unit vector in the map coordinate system (east, north, up) */
|
|
25
|
+
readonly direction: readonly [number, number, number];
|
|
26
|
+
/** Strength of the shading (0 means no shading) */
|
|
27
|
+
readonly strength: number;
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* GLSL that derives the brightness factor from the gradient
|
|
31
|
+
*
|
|
32
|
+
* The caller must declare `u_light_dir` (map coordinate system, east / north / up) and
|
|
33
|
+
* `u_shade_strength`. The unit of the gradient is meters per meter (eastward and
|
|
34
|
+
* northward), and every path converts its samples to this unit before passing them in.
|
|
35
|
+
*/
|
|
36
|
+
export declare const TERRAIN_SHADE_CORE_GLSL = "\nfloat terrainShadeFromGradient(float dzdEast, float dzdNorth) {\n if (u_shade_strength <= 0.0) return 1.0;\n vec3 normal = normalize(vec3(-dzdEast, -dzdNorth, 1.0));\n float lambert = dot(normal, u_light_dir);\n // The value on level ground (the normal pointing straight up). It is normalized to\n // 1.0 here, so no shading is applied on flat places (the name flat cannot be used\n // because it is a reserved word in GLSL)\n float level = max(u_light_dir.z, 1e-3);\n float factor = 1.0 + u_shade_strength * (lambert - level) / level;\n return clamp(factor, 1.0 - u_shade_strength, 1.0 + u_shade_strength * 0.5);\n}\n";
|
|
37
|
+
/**
|
|
38
|
+
* CPU mirror of `terrainShadeFromGradient`
|
|
39
|
+
*
|
|
40
|
+
* The GLSL is the authority for the implementation, but the formula itself is placed here
|
|
41
|
+
* as a pure function so that it can be verified (the same practice as `drapeShadeWindow`).
|
|
42
|
+
* Given the same samples, every path arrives at this value.
|
|
43
|
+
*
|
|
44
|
+
* @param dzdEast Eastward gradient (meters per meter)
|
|
45
|
+
* @param dzdNorth Northward gradient (meters per meter)
|
|
46
|
+
* @param light The light source
|
|
47
|
+
*/
|
|
48
|
+
export declare function terrainShadeFactor(dzdEast: number, dzdNorth: number, light: TerrainShadeLight): number;
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
// SPDX-FileCopyrightText: 2026 SAKAIDA Atsushi
|
|
2
|
+
// SPDX-License-Identifier: AGPL-3.0-only
|
|
3
|
+
/**
|
|
4
|
+
* GLSL that derives the brightness factor from the gradient
|
|
5
|
+
*
|
|
6
|
+
* The caller must declare `u_light_dir` (map coordinate system, east / north / up) and
|
|
7
|
+
* `u_shade_strength`. The unit of the gradient is meters per meter (eastward and
|
|
8
|
+
* northward), and every path converts its samples to this unit before passing them in.
|
|
9
|
+
*/
|
|
10
|
+
export const TERRAIN_SHADE_CORE_GLSL = `
|
|
11
|
+
float terrainShadeFromGradient(float dzdEast, float dzdNorth) {
|
|
12
|
+
if (u_shade_strength <= 0.0) return 1.0;
|
|
13
|
+
vec3 normal = normalize(vec3(-dzdEast, -dzdNorth, 1.0));
|
|
14
|
+
float lambert = dot(normal, u_light_dir);
|
|
15
|
+
// The value on level ground (the normal pointing straight up). It is normalized to
|
|
16
|
+
// 1.0 here, so no shading is applied on flat places (the name flat cannot be used
|
|
17
|
+
// because it is a reserved word in GLSL)
|
|
18
|
+
float level = max(u_light_dir.z, 1e-3);
|
|
19
|
+
float factor = 1.0 + u_shade_strength * (lambert - level) / level;
|
|
20
|
+
return clamp(factor, 1.0 - u_shade_strength, 1.0 + u_shade_strength * 0.5);
|
|
21
|
+
}
|
|
22
|
+
`;
|
|
23
|
+
/**
|
|
24
|
+
* CPU mirror of `terrainShadeFromGradient`
|
|
25
|
+
*
|
|
26
|
+
* The GLSL is the authority for the implementation, but the formula itself is placed here
|
|
27
|
+
* as a pure function so that it can be verified (the same practice as `drapeShadeWindow`).
|
|
28
|
+
* Given the same samples, every path arrives at this value.
|
|
29
|
+
*
|
|
30
|
+
* @param dzdEast Eastward gradient (meters per meter)
|
|
31
|
+
* @param dzdNorth Northward gradient (meters per meter)
|
|
32
|
+
* @param light The light source
|
|
33
|
+
*/
|
|
34
|
+
export function terrainShadeFactor(dzdEast, dzdNorth, light) {
|
|
35
|
+
const strength = light.strength;
|
|
36
|
+
if (strength <= 0)
|
|
37
|
+
return 1;
|
|
38
|
+
const length = Math.hypot(dzdEast, dzdNorth, 1);
|
|
39
|
+
const normal = [-dzdEast / length, -dzdNorth / length, 1 / length];
|
|
40
|
+
const dir = light.direction;
|
|
41
|
+
const lambert = normal[0] * dir[0] + normal[1] * dir[1] + normal[2] * dir[2];
|
|
42
|
+
const level = Math.max(dir[2], 1e-3);
|
|
43
|
+
const factor = 1 + (strength * (lambert - level)) / level;
|
|
44
|
+
return Math.min(Math.max(factor, 1 - strength), 1 + strength * 0.5);
|
|
45
|
+
}
|