@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,924 @@
|
|
|
1
|
+
// SPDX-FileCopyrightText: 2026 SAKAIDA Atsushi
|
|
2
|
+
// SPDX-License-Identifier: AGPL-3.0-only
|
|
3
|
+
/**
|
|
4
|
+
* Whether the rectangles intersect
|
|
5
|
+
*/
|
|
6
|
+
export function rectsIntersect(minX, minY, maxX, maxY, region) {
|
|
7
|
+
return minX <= region.x1 && maxX >= region.x0 && minY <= region.y1 && maxY >= region.y0;
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* Whether the rectangle `outer` wholly contains the rectangle `inner`
|
|
11
|
+
*/
|
|
12
|
+
export function containsRect(outer, inner) {
|
|
13
|
+
return (outer.x0 <= inner.x0 && outer.y0 <= inner.y0 && outer.x1 >= inner.x1 && outer.y1 >= inner.y1);
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Expand a rectangle outwards by `margin` as a ratio of its side lengths
|
|
17
|
+
*/
|
|
18
|
+
export function expandRect(rect, margin) {
|
|
19
|
+
const dx = (rect.x1 - rect.x0) * margin;
|
|
20
|
+
const dy = (rect.y1 - rect.y0) * margin;
|
|
21
|
+
return { x0: rect.x0 - dx, y0: rect.y0 - dy, x1: rect.x1 + dx, y1: rect.y1 + dy };
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* The intersection of two rectangles (null if they do not intersect)
|
|
25
|
+
*/
|
|
26
|
+
export function intersectRect(a, b) {
|
|
27
|
+
const x0 = Math.max(a.x0, b.x0);
|
|
28
|
+
const y0 = Math.max(a.y0, b.y0);
|
|
29
|
+
const x1 = Math.min(a.x1, b.x1);
|
|
30
|
+
const y1 = Math.min(a.y1, b.y1);
|
|
31
|
+
if (x1 <= x0 || y1 <= y0)
|
|
32
|
+
return null;
|
|
33
|
+
return { x0, y0, x1, y1 };
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Convert the DEM atlas uniform representation [x0, y0, 1/width, 1/height] into a rectangle
|
|
37
|
+
*/
|
|
38
|
+
export function atlasCoverage(rect) {
|
|
39
|
+
const width = rect[2] > 0 ? 1 / rect[2] : 0;
|
|
40
|
+
const height = rect[3] > 0 ? 1 / rect[3] : 0;
|
|
41
|
+
return { x0: rect[0], y0: rect[1], x1: rect[0] + width, y1: rect[1] + height };
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* The number of lattice cells a bounding box touches within the area (an estimate)
|
|
45
|
+
*
|
|
46
|
+
* We do not split outside the area, so it is left out of the cost estimate too.
|
|
47
|
+
*/
|
|
48
|
+
export function cellsInRegion(minX, minY, maxX, maxY, grid, region) {
|
|
49
|
+
if (!(grid > 0))
|
|
50
|
+
return 0;
|
|
51
|
+
let x0 = minX;
|
|
52
|
+
let y0 = minY;
|
|
53
|
+
let x1 = maxX;
|
|
54
|
+
let y1 = maxY;
|
|
55
|
+
if (region) {
|
|
56
|
+
x0 = Math.max(x0, region.x0);
|
|
57
|
+
y0 = Math.max(y0, region.y0);
|
|
58
|
+
x1 = Math.min(x1, region.x1);
|
|
59
|
+
y1 = Math.min(y1, region.y1);
|
|
60
|
+
if (x1 < x0 || y1 < y0)
|
|
61
|
+
return 0;
|
|
62
|
+
}
|
|
63
|
+
return ((x1 - x0) / grid) * ((y1 - y0) / grid);
|
|
64
|
+
}
|
|
65
|
+
/** Upper limit on the number of lattice cells one triangle may cover (a runaway guard) */
|
|
66
|
+
const MAX_CELLS_PER_TRIANGLE = 65536;
|
|
67
|
+
/** Longitude -> Mercator x */
|
|
68
|
+
export function mercatorX(lng) {
|
|
69
|
+
return (lng + 180) / 360;
|
|
70
|
+
}
|
|
71
|
+
/** Latitude -> Mercator y */
|
|
72
|
+
export function mercatorY(lat) {
|
|
73
|
+
const latRad = (lat * Math.PI) / 180;
|
|
74
|
+
return (1 - Math.log(Math.tan(latRad) + 1 / Math.cos(latRad)) / Math.PI) / 2;
|
|
75
|
+
}
|
|
76
|
+
/** Mercator x -> longitude */
|
|
77
|
+
export function lngFromMercatorX(x) {
|
|
78
|
+
return x * 360 - 180;
|
|
79
|
+
}
|
|
80
|
+
/** Mercator y -> latitude */
|
|
81
|
+
export function latFromMercatorY(y) {
|
|
82
|
+
return (Math.atan(Math.sinh(Math.PI * (1 - 2 * y))) * 180) / Math.PI;
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* Inserts vertices where a line crosses the lattice of the terrain mesh, so that it follows
|
|
86
|
+
* the terrain when each vertex is lifted.
|
|
87
|
+
*
|
|
88
|
+
* The original vertices are always kept, and only the intersections with the lattice lines
|
|
89
|
+
* are inserted. The intersections are found by linear interpolation in Mercator space
|
|
90
|
+
* (rendering also draws a straight line after projection, so an inserted point always lies
|
|
91
|
+
* on the original line).
|
|
92
|
+
*
|
|
93
|
+
* @param path The vertices `[lng, lat]` in degrees
|
|
94
|
+
* @param step The step from {@link getTerrainTessellationStep}
|
|
95
|
+
* @returns The densified vertices. The input itself for fewer than 2 positions or a step of
|
|
96
|
+
* 0; insertion stops when `step.maxPoints` is reached
|
|
97
|
+
*/
|
|
98
|
+
export function densifyPath(path, step) {
|
|
99
|
+
if (path.length < 2 || !(step.grid > 0))
|
|
100
|
+
return path;
|
|
101
|
+
const out = [path[0]];
|
|
102
|
+
let budget = step.maxPoints;
|
|
103
|
+
for (let i = 1; i < path.length; i++) {
|
|
104
|
+
const a = path[i - 1];
|
|
105
|
+
const b = path[i];
|
|
106
|
+
if (budget > 0) {
|
|
107
|
+
const inserted = crossingPoints(a, b, step.grid, budget, step.region);
|
|
108
|
+
for (const point of inserted)
|
|
109
|
+
out.push(point);
|
|
110
|
+
budget -= inserted.length;
|
|
111
|
+
}
|
|
112
|
+
out.push(b);
|
|
113
|
+
}
|
|
114
|
+
return out;
|
|
115
|
+
}
|
|
116
|
+
/**
|
|
117
|
+
* Densify an array of rings along the lattice
|
|
118
|
+
*
|
|
119
|
+
* A ring is closed, so the edge that goes back from the end to the start is split too.
|
|
120
|
+
*
|
|
121
|
+
* @internal
|
|
122
|
+
*/
|
|
123
|
+
export function densifyRings(rings, step) {
|
|
124
|
+
if (!(step.grid > 0))
|
|
125
|
+
return rings;
|
|
126
|
+
return rings.map((ring) => {
|
|
127
|
+
if (ring.length < 3)
|
|
128
|
+
return ring;
|
|
129
|
+
// Regardless of whether a closing point is present, add one "edge back to the start",
|
|
130
|
+
// densify, then drop the closing point
|
|
131
|
+
const closed = [...ring, ring[0]];
|
|
132
|
+
const dense = densifyPath(closed, step);
|
|
133
|
+
dense.pop();
|
|
134
|
+
return dense;
|
|
135
|
+
});
|
|
136
|
+
}
|
|
137
|
+
/**
|
|
138
|
+
* Return the points where the edge (a, b) crosses the lattice lines, in order from a to b
|
|
139
|
+
*/
|
|
140
|
+
function crossingPoints(a, b, grid, budget, region) {
|
|
141
|
+
const ax = mercatorX(a[0]);
|
|
142
|
+
const ay = mercatorY(a[1]);
|
|
143
|
+
const bx = mercatorX(b[0]);
|
|
144
|
+
const by = mercatorY(b[1]);
|
|
145
|
+
// An edge outside the area is not split (the elevation saturates at the edge value, so
|
|
146
|
+
// there is no point in splitting it)
|
|
147
|
+
if (region &&
|
|
148
|
+
!rectsIntersect(Math.min(ax, bx), Math.min(ay, by), Math.max(ax, bx), Math.max(ay, by), region)) {
|
|
149
|
+
return [];
|
|
150
|
+
}
|
|
151
|
+
const ts = [];
|
|
152
|
+
collectAxisCrossings(ax, bx, grid, ts);
|
|
153
|
+
collectAxisCrossings(ay, by, grid, ts);
|
|
154
|
+
if (ts.length === 0)
|
|
155
|
+
return [];
|
|
156
|
+
ts.sort((x, y) => x - y);
|
|
157
|
+
const points = [];
|
|
158
|
+
let previous = -1;
|
|
159
|
+
for (const t of ts) {
|
|
160
|
+
if (t <= 0 || t >= 1 || t === previous)
|
|
161
|
+
continue;
|
|
162
|
+
if (points.length >= budget)
|
|
163
|
+
break;
|
|
164
|
+
previous = t;
|
|
165
|
+
const mx = ax + (bx - ax) * t;
|
|
166
|
+
const my = ay + (by - ay) * t;
|
|
167
|
+
points.push([lngFromMercatorX(mx), latFromMercatorY(my)]);
|
|
168
|
+
}
|
|
169
|
+
return points;
|
|
170
|
+
}
|
|
171
|
+
/**
|
|
172
|
+
* Collect the parameters of the intersections with the lattice lines for one axis
|
|
173
|
+
*/
|
|
174
|
+
function collectAxisCrossings(v0, v1, grid, out) {
|
|
175
|
+
if (!(grid > 0) || v0 === v1)
|
|
176
|
+
return;
|
|
177
|
+
const lo = Math.min(v0, v1);
|
|
178
|
+
const hi = Math.max(v0, v1);
|
|
179
|
+
const first = Math.floor(lo / grid) + 1;
|
|
180
|
+
const last = Math.ceil(hi / grid) - 1;
|
|
181
|
+
if (last < first)
|
|
182
|
+
return;
|
|
183
|
+
// If the step is so fine that the intersections explode, give up (the caller's upper
|
|
184
|
+
// limit protects us)
|
|
185
|
+
if (last - first > 100000)
|
|
186
|
+
return;
|
|
187
|
+
for (let k = first; k <= last; k++) {
|
|
188
|
+
const value = k * grid;
|
|
189
|
+
const t = (value - v0) / (v1 - v0);
|
|
190
|
+
if (t > 0 && t < 1)
|
|
191
|
+
out.push(t);
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
/**
|
|
195
|
+
* Subdivide the result of a triangulation along the lattice of the terrain mesh
|
|
196
|
+
*
|
|
197
|
+
* @param flatCoords The flattened coordinate array ([lng, lat, ...])
|
|
198
|
+
* @param indices The index array of the triangles
|
|
199
|
+
*
|
|
200
|
+
* @internal
|
|
201
|
+
*/
|
|
202
|
+
export function subdivideTriangles(flatCoords, indices, step) {
|
|
203
|
+
if (indices.length === 0 || !(step.grid > 0)) {
|
|
204
|
+
return {
|
|
205
|
+
flatCoords: [...flatCoords],
|
|
206
|
+
indices: [...indices],
|
|
207
|
+
overflowed: false,
|
|
208
|
+
overflowFactor: 1,
|
|
209
|
+
};
|
|
210
|
+
}
|
|
211
|
+
const pool = new VertexPool(flatCoords, step.maxPoints);
|
|
212
|
+
const out = [];
|
|
213
|
+
const tiling = step.tiling ?? null;
|
|
214
|
+
const coarsen = step.coarsen && step.coarsen > 1 ? step.coarsen : 1;
|
|
215
|
+
// Raised if there is even one triangle that was "emitted as it was, without splitting"
|
|
216
|
+
let overflowed = false;
|
|
217
|
+
// The step factor needed to split through (derived from the amount that overflowed)
|
|
218
|
+
let overflowFactor = 1;
|
|
219
|
+
for (let i = 0; i + 2 < indices.length; i += 3) {
|
|
220
|
+
const triangle = [indices[i], indices[i + 1], indices[i + 2]];
|
|
221
|
+
if (pool.exhausted()) {
|
|
222
|
+
overflowed = true;
|
|
223
|
+
pushOriented(out, pool, triangle[0], triangle[1], triangle[2]);
|
|
224
|
+
continue;
|
|
225
|
+
}
|
|
226
|
+
let minX = Number.POSITIVE_INFINITY;
|
|
227
|
+
let maxX = Number.NEGATIVE_INFINITY;
|
|
228
|
+
let minY = Number.POSITIVE_INFINITY;
|
|
229
|
+
let maxY = Number.NEGATIVE_INFINITY;
|
|
230
|
+
for (const index of triangle) {
|
|
231
|
+
const x = pool.x(index);
|
|
232
|
+
const y = pool.y(index);
|
|
233
|
+
if (x < minX)
|
|
234
|
+
minX = x;
|
|
235
|
+
if (x > maxX)
|
|
236
|
+
maxX = x;
|
|
237
|
+
if (y < minY)
|
|
238
|
+
minY = y;
|
|
239
|
+
if (y > maxY)
|
|
240
|
+
maxY = y;
|
|
241
|
+
}
|
|
242
|
+
// Outside the area, emit as it is (splitting would not change the elevation, so this is
|
|
243
|
+
// not a breakdown).
|
|
244
|
+
if (step.region && !rectsIntersect(minX, minY, maxX, maxY, step.region)) {
|
|
245
|
+
emitFan(triangle, 3, pool, out);
|
|
246
|
+
continue;
|
|
247
|
+
}
|
|
248
|
+
// A triangle that straddles the area is cut into the part inside and the part outside
|
|
249
|
+
// before being split.
|
|
250
|
+
//
|
|
251
|
+
// Without that cut, covering every cell of the whole bounding box means that a single
|
|
252
|
+
// triangle of a large feature (a municipal boundary, the outline of a country) holds
|
|
253
|
+
// hundreds of millions of cells, and one build of a retained batch turns into seconds
|
|
254
|
+
// or tens of seconds (measured at 6.8 seconds for one chunk). Holding the cells we
|
|
255
|
+
// cover to the inside of the area alone caps the cost at how wide the view is.
|
|
256
|
+
//
|
|
257
|
+
// The outside is emitted as convex polygon fragments, as they are, without splitting.
|
|
258
|
+
// Outside the area the elevation saturates at the edge value so there is no point in
|
|
259
|
+
// splitting, and a fragment shares its edges with the original triangle, so neither
|
|
260
|
+
// gaps nor overlaps arise.
|
|
261
|
+
let piece = triangle;
|
|
262
|
+
let pieceLength = 3;
|
|
263
|
+
if (step.region) {
|
|
264
|
+
pieceLength = clipToRegion(triangle, 3, pool, step.region, minX, minY, maxX, maxY, out);
|
|
265
|
+
if (pieceLength < 3)
|
|
266
|
+
continue;
|
|
267
|
+
piece = regionBuffer;
|
|
268
|
+
minX = Number.POSITIVE_INFINITY;
|
|
269
|
+
maxX = Number.NEGATIVE_INFINITY;
|
|
270
|
+
minY = Number.POSITIVE_INFINITY;
|
|
271
|
+
maxY = Number.NEGATIVE_INFINITY;
|
|
272
|
+
for (let k = 0; k < pieceLength; k++) {
|
|
273
|
+
const x = pool.x(piece[k]);
|
|
274
|
+
const y = pool.y(piece[k]);
|
|
275
|
+
if (x < minX)
|
|
276
|
+
minX = x;
|
|
277
|
+
if (x > maxX)
|
|
278
|
+
maxX = x;
|
|
279
|
+
if (y < minY)
|
|
280
|
+
minY = y;
|
|
281
|
+
if (y > maxY)
|
|
282
|
+
maxY = y;
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
if (!tiling) {
|
|
286
|
+
subdividePiece(piece, pieceLength, pool, step.grid, minX, minY, maxX, maxY, out);
|
|
287
|
+
if (pieceOverflowed) {
|
|
288
|
+
overflowed = true;
|
|
289
|
+
if (pieceOverflowFactor > overflowFactor)
|
|
290
|
+
overflowFactor = pieceOverflowFactor;
|
|
291
|
+
}
|
|
292
|
+
continue;
|
|
293
|
+
}
|
|
294
|
+
// Cut along each cell of the tile index and split with that cell's step.
|
|
295
|
+
//
|
|
296
|
+
// An index cell has the size of the finest tile, and a coarse tile occupies an integer
|
|
297
|
+
// multiple of it. A cell boundary always lies on that tile's mesh lines, so cutting at
|
|
298
|
+
// the boundary only adds nodes and does not create a seam.
|
|
299
|
+
const ix0 = Math.max(0, Math.floor((minX - tiling.rect.x0) / tiling.cell));
|
|
300
|
+
const ix1 = Math.min(tiling.cols - 1, Math.floor((maxX - tiling.rect.x0) / tiling.cell));
|
|
301
|
+
const iy0 = Math.max(0, Math.floor((minY - tiling.rect.y0) / tiling.cell));
|
|
302
|
+
const iy1 = Math.min(tiling.rows - 1, Math.floor((maxY - tiling.rect.y0) / tiling.cell));
|
|
303
|
+
if (ix1 < ix0 || iy1 < iy0) {
|
|
304
|
+
// Outside the index (where there is no tile), emit as it is
|
|
305
|
+
emitFan(piece, pieceLength, pool, out);
|
|
306
|
+
continue;
|
|
307
|
+
}
|
|
308
|
+
for (let iy = iy0; iy <= iy1; iy++) {
|
|
309
|
+
for (let ix = ix0; ix <= ix1; ix++) {
|
|
310
|
+
const tileGrid = tiling.grids[iy * tiling.cols + ix] * coarsen;
|
|
311
|
+
const tx0 = tiling.rect.x0 + ix * tiling.cell;
|
|
312
|
+
const ty0 = tiling.rect.y0 + iy * tiling.cell;
|
|
313
|
+
const tx1 = tx0 + tiling.cell;
|
|
314
|
+
const ty1 = ty0 + tiling.cell;
|
|
315
|
+
// If it fits within a single index cell we can do without cutting (most features)
|
|
316
|
+
let sub = piece;
|
|
317
|
+
let subLength = pieceLength;
|
|
318
|
+
let sminX = minX;
|
|
319
|
+
let sminY = minY;
|
|
320
|
+
let smaxX = maxX;
|
|
321
|
+
let smaxY = maxY;
|
|
322
|
+
const contained = minX >= tx0 && maxX <= tx1 && minY >= ty0 && maxY <= ty1;
|
|
323
|
+
if (!contained) {
|
|
324
|
+
subLength = clipHalfPlane(piece, pieceLength, tileBufferA, pool, 0, tx0, false);
|
|
325
|
+
if (subLength < 3)
|
|
326
|
+
continue;
|
|
327
|
+
subLength = clipHalfPlane(tileBufferA, subLength, tileBufferB, pool, 0, tx1, true);
|
|
328
|
+
if (subLength < 3)
|
|
329
|
+
continue;
|
|
330
|
+
subLength = clipHalfPlane(tileBufferB, subLength, tileBufferA, pool, 1, ty0, false);
|
|
331
|
+
if (subLength < 3)
|
|
332
|
+
continue;
|
|
333
|
+
subLength = clipHalfPlane(tileBufferA, subLength, tileBufferB, pool, 1, ty1, true);
|
|
334
|
+
if (subLength < 3)
|
|
335
|
+
continue;
|
|
336
|
+
sub = tileBufferB;
|
|
337
|
+
sminX = Number.POSITIVE_INFINITY;
|
|
338
|
+
smaxX = Number.NEGATIVE_INFINITY;
|
|
339
|
+
sminY = Number.POSITIVE_INFINITY;
|
|
340
|
+
smaxY = Number.NEGATIVE_INFINITY;
|
|
341
|
+
for (let k = 0; k < subLength; k++) {
|
|
342
|
+
const x = pool.x(sub[k]);
|
|
343
|
+
const y = pool.y(sub[k]);
|
|
344
|
+
if (x < sminX)
|
|
345
|
+
sminX = x;
|
|
346
|
+
if (x > smaxX)
|
|
347
|
+
smaxX = x;
|
|
348
|
+
if (y < sminY)
|
|
349
|
+
sminY = y;
|
|
350
|
+
if (y > smaxY)
|
|
351
|
+
smaxY = y;
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
if (!(tileGrid > 0)) {
|
|
355
|
+
// A cell with no tile is not split (the elevation saturates at the atlas edge
|
|
356
|
+
// value)
|
|
357
|
+
emitFan(sub, subLength, pool, out);
|
|
358
|
+
continue;
|
|
359
|
+
}
|
|
360
|
+
subdividePiece(sub, subLength, pool, tileGrid, sminX, sminY, smaxX, smaxY, out);
|
|
361
|
+
if (pieceOverflowed) {
|
|
362
|
+
overflowed = true;
|
|
363
|
+
if (pieceOverflowFactor > overflowFactor)
|
|
364
|
+
overflowFactor = pieceOverflowFactor;
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
if (pool.exhausted()) {
|
|
370
|
+
const needed = pool.overflowRatio();
|
|
371
|
+
if (needed > overflowFactor)
|
|
372
|
+
overflowFactor = needed;
|
|
373
|
+
}
|
|
374
|
+
return {
|
|
375
|
+
flatCoords: pool.output(),
|
|
376
|
+
indices: out,
|
|
377
|
+
overflowed: overflowed || pool.exhausted(),
|
|
378
|
+
overflowFactor,
|
|
379
|
+
};
|
|
380
|
+
}
|
|
381
|
+
/** A note for subdividePiece to report that it "could not split through" */
|
|
382
|
+
let pieceOverflowed = false;
|
|
383
|
+
/** The "necessary coarsening factor" that subdividePiece worked out */
|
|
384
|
+
let pieceOverflowFactor = 1;
|
|
385
|
+
/**
|
|
386
|
+
* Split one convex polygon along the lattice of the given step and emit it
|
|
387
|
+
*
|
|
388
|
+
* For each cell we cut "the original polygon". Since what we cut is always the same, the
|
|
389
|
+
* intersections produced when the neighbouring triangle that shares an edge cuts the same
|
|
390
|
+
* cell agree down to the last bit (they do not depend on the order). With a scheme that
|
|
391
|
+
* recursively cuts in half, the order of the cuts differs between neighbours, so the
|
|
392
|
+
* intersections drift slightly and a T-junction appears.
|
|
393
|
+
*/
|
|
394
|
+
function subdividePiece(poly, polyLength, pool, grid, minX, minY, maxX, maxY, out) {
|
|
395
|
+
pieceOverflowed = false;
|
|
396
|
+
pieceOverflowFactor = 1;
|
|
397
|
+
const cx0 = Math.floor(minX / grid);
|
|
398
|
+
const cx1 = Math.floor(maxX / grid);
|
|
399
|
+
const cy0 = Math.floor(minY / grid);
|
|
400
|
+
const cy1 = Math.floor(maxY / grid);
|
|
401
|
+
// If it fits within a single cell there is no need to split (this is not a breakdown).
|
|
402
|
+
// Anything that covers too many cells has to be emitted as it is, but that is a flat
|
|
403
|
+
// surface that does not follow the terrain, so we report it as a breakdown and have the
|
|
404
|
+
// caller coarsen the step and split again.
|
|
405
|
+
const cellCount = (cx1 - cx0 + 1) * (cy1 - cy0 + 1);
|
|
406
|
+
if (cellCount <= 1) {
|
|
407
|
+
emitFan(poly, polyLength, pool, out);
|
|
408
|
+
return;
|
|
409
|
+
}
|
|
410
|
+
if (cellCount > MAX_CELLS_PER_TRIANGLE) {
|
|
411
|
+
pieceOverflowed = true;
|
|
412
|
+
// The number of cells falls with the square of the step
|
|
413
|
+
pieceOverflowFactor = Math.sqrt(cellCount / MAX_CELLS_PER_TRIANGLE);
|
|
414
|
+
emitFan(poly, polyLength, pool, out);
|
|
415
|
+
return;
|
|
416
|
+
}
|
|
417
|
+
// A sign that absorbs the orientation (clockwise / counter-clockwise)
|
|
418
|
+
const orientation = signedAreaSign(poly, polyLength, pool);
|
|
419
|
+
// Remember the numbers of the lattice nodes row by row. Neighbouring cells share two
|
|
420
|
+
// nodes, and the rows above and below share a whole row, so each node only has to be
|
|
421
|
+
// registered once.
|
|
422
|
+
const nodeWidth = cx1 - cx0 + 2;
|
|
423
|
+
let nodesLow = ensureNodeRow(0, nodeWidth);
|
|
424
|
+
let nodesHigh = ensureNodeRow(1, nodeWidth);
|
|
425
|
+
nodesLow.fill(-1, 0, nodeWidth);
|
|
426
|
+
nodesHigh.fill(-1, 0, nodeWidth);
|
|
427
|
+
for (let cy = cy0; cy <= cy1; cy++) {
|
|
428
|
+
const gy0 = cy * grid;
|
|
429
|
+
const gy1 = (cy + 1) * grid;
|
|
430
|
+
// The top edge of the previous row is the bottom edge of this row
|
|
431
|
+
const swap = nodesLow;
|
|
432
|
+
nodesLow = nodesHigh;
|
|
433
|
+
nodesHigh = swap;
|
|
434
|
+
nodesHigh.fill(-1, 0, nodeWidth);
|
|
435
|
+
if (cy === cy0)
|
|
436
|
+
nodesLow.fill(-1, 0, nodeWidth);
|
|
437
|
+
// Work out "the columns this row actually touches" from the row's band. For a long thin
|
|
438
|
+
// triangle most of the bounding box is a miss, so simply narrowing the column range row
|
|
439
|
+
// by row cuts the number of cells we cover by an order of magnitude.
|
|
440
|
+
const range = bandXRange(poly, polyLength, pool, gy0, gy1);
|
|
441
|
+
if (!range)
|
|
442
|
+
continue;
|
|
443
|
+
const bx0 = Math.max(cx0, Math.floor(range[0] / grid));
|
|
444
|
+
const bx1 = Math.min(cx1, Math.floor(range[1] / grid));
|
|
445
|
+
for (let cx = bx0; cx <= bx1; cx++) {
|
|
446
|
+
const gx0 = cx * grid;
|
|
447
|
+
const gx1 = (cx + 1) * grid;
|
|
448
|
+
// If the cell is wholly inside there is no need to cut and the four corners of the
|
|
449
|
+
// cell can be emitted as they are (the coordinates of an intersection come out exactly
|
|
450
|
+
// equal to the corner even when found by cutting: one axis is the value of the lattice
|
|
451
|
+
// line itself, and the other is an interpolation between two identical values). A
|
|
452
|
+
// polygon is usually bigger than a cell, so this path accounts for the vast majority.
|
|
453
|
+
const inside = cellInsidePolygon(poly, polyLength, pool, gx0, gy0, gx1, gy1, orientation);
|
|
454
|
+
if (inside === CELL_OUTSIDE)
|
|
455
|
+
continue;
|
|
456
|
+
if (inside === CELL_INSIDE) {
|
|
457
|
+
const i0 = cx - cx0;
|
|
458
|
+
const v00 = nodeAt(pool, nodesLow, i0, gx0, gy0);
|
|
459
|
+
const v10 = nodeAt(pool, nodesLow, i0 + 1, gx1, gy0);
|
|
460
|
+
const v11 = nodeAt(pool, nodesHigh, i0 + 1, gx1, gy1);
|
|
461
|
+
const v01 = nodeAt(pool, nodesHigh, i0, gx0, gy1);
|
|
462
|
+
// The direction of the diagonal is the same as in emitFan (fan out from the corner
|
|
463
|
+
// with the smallest coordinate sum). It is the same way of splitting as MapLibre's
|
|
464
|
+
// terrain mesh, so the surfaces agree inside the cell as well
|
|
465
|
+
pushOriented(out, pool, v00, v10, v11);
|
|
466
|
+
pushOriented(out, pool, v00, v11, v01);
|
|
467
|
+
continue;
|
|
468
|
+
}
|
|
469
|
+
// The working arrays are reused (creating 8 arrays per cell would make array creation
|
|
470
|
+
// dominant for data with thousands of small polygons)
|
|
471
|
+
let length = clipHalfPlane(poly, polyLength, clipBufferA, pool, 0, gx0, false);
|
|
472
|
+
if (length < 3)
|
|
473
|
+
continue;
|
|
474
|
+
length = clipHalfPlane(clipBufferA, length, clipBufferB, pool, 0, gx1, true);
|
|
475
|
+
if (length < 3)
|
|
476
|
+
continue;
|
|
477
|
+
length = clipHalfPlane(clipBufferB, length, clipBufferA, pool, 1, gy0, false);
|
|
478
|
+
if (length < 3)
|
|
479
|
+
continue;
|
|
480
|
+
length = clipHalfPlane(clipBufferA, length, clipBufferB, pool, 1, gy1, true);
|
|
481
|
+
if (length < 3)
|
|
482
|
+
continue;
|
|
483
|
+
emitFan(clipBufferB, length, pool, out);
|
|
484
|
+
}
|
|
485
|
+
}
|
|
486
|
+
}
|
|
487
|
+
/**
|
|
488
|
+
* Working storage for cutting with the area (a convex polygon has at most 7 vertices; 16
|
|
489
|
+
* for headroom)
|
|
490
|
+
*/
|
|
491
|
+
const regionBuffer = new Int32Array(16);
|
|
492
|
+
const tileBufferA = new Int32Array(16);
|
|
493
|
+
const tileBufferB = new Int32Array(16);
|
|
494
|
+
const regionScratch = new Int32Array(16);
|
|
495
|
+
const outsideBuffer = new Int32Array(16);
|
|
496
|
+
/**
|
|
497
|
+
* Cut a triangle with the rectangle of the area and leave what is inside in regionBuffer
|
|
498
|
+
*
|
|
499
|
+
* A fragment that falls outside the area is emitted to `out` on the spot (without
|
|
500
|
+
* splitting). A fragment shares its edges with the original triangle, so neither gaps nor
|
|
501
|
+
* overlaps arise.
|
|
502
|
+
*
|
|
503
|
+
* A triangle that lies wholly inside the area is left untouched. Cutting it would mean the
|
|
504
|
+
* intersections of its edges with the lattice lines are no longer "values derived from the
|
|
505
|
+
* original edge", and the agreement with the neighbouring triangle (down to the last bit)
|
|
506
|
+
* would break. The only triangles that straddle the area are those along its border, and
|
|
507
|
+
* that is off screen.
|
|
508
|
+
*
|
|
509
|
+
* @returns The number of vertices left inside the area (fewer than 3 means nothing is left)
|
|
510
|
+
*/
|
|
511
|
+
function clipToRegion(poly, polyLength, pool, region, minX, minY, maxX, maxY, out) {
|
|
512
|
+
// If it is wholly inside, leave it untouched
|
|
513
|
+
if (minX >= region.x0 && maxX <= region.x1 && minY >= region.y0 && maxY <= region.y1) {
|
|
514
|
+
for (let i = 0; i < polyLength; i++)
|
|
515
|
+
regionBuffer[i] = poly[i];
|
|
516
|
+
return polyLength;
|
|
517
|
+
}
|
|
518
|
+
let source = poly;
|
|
519
|
+
let length = polyLength;
|
|
520
|
+
for (let plane = 0; plane < 4; plane++) {
|
|
521
|
+
const axis = plane < 2 ? 0 : 1;
|
|
522
|
+
const line = plane === 0 ? region.x0 : plane === 1 ? region.x1 : plane === 2 ? region.y0 : region.y1;
|
|
523
|
+
const keepLower = plane === 1 || plane === 3;
|
|
524
|
+
// The outside fragment is emitted without splitting (outside the area the elevation
|
|
525
|
+
// saturates at the edge, so there is no point in splitting it)
|
|
526
|
+
const outsideLength = clipHalfPlane(source, length, outsideBuffer, pool, axis, line, !keepLower);
|
|
527
|
+
if (outsideLength >= 3)
|
|
528
|
+
emitFan(outsideBuffer, outsideLength, pool, out);
|
|
529
|
+
length = clipHalfPlane(source, length, regionScratch, pool, axis, line, keepLower);
|
|
530
|
+
if (length < 3)
|
|
531
|
+
return 0;
|
|
532
|
+
for (let i = 0; i < length; i++)
|
|
533
|
+
regionBuffer[i] = regionScratch[i];
|
|
534
|
+
source = regionBuffer;
|
|
535
|
+
}
|
|
536
|
+
return length;
|
|
537
|
+
}
|
|
538
|
+
/**
|
|
539
|
+
* Emit a triangle with its winding order aligned
|
|
540
|
+
*
|
|
541
|
+
* The terrain-following fill culls the far side of a ridge with back-face culling (the cure
|
|
542
|
+
* for the silhouette bleed-through in which a fill laid on the far slope shows through the
|
|
543
|
+
* ridge in front). For the culling to hold, the winding order of every triangle must be
|
|
544
|
+
* uniform, but the orientation of the input ring (clockwise / counter-clockwise) differs
|
|
545
|
+
* from feature to feature. So here we normalise to the orientation in which the cross
|
|
546
|
+
* product in Mercator (x right, y down) is positive. In a y-down coordinate system a
|
|
547
|
+
* positive cross product = clockwise on screen = counter-clockwise in GL window coordinates
|
|
548
|
+
* (y up) = the front face of the default frontFace(CCW).
|
|
549
|
+
*/
|
|
550
|
+
export function pushOriented(out, pool, a, b, c) {
|
|
551
|
+
const ax = pool.x(a);
|
|
552
|
+
const ay = pool.y(a);
|
|
553
|
+
const cross = (pool.x(b) - ax) * (pool.y(c) - ay) - (pool.x(c) - ax) * (pool.y(b) - ay);
|
|
554
|
+
// The sign determined on real hardware: in Mercator (y down), the orientation with a
|
|
555
|
+
// negative cross product is GL's front face (CCW). Choosing the opposite makes the whole
|
|
556
|
+
// fill get culled and disappear in a straight-down view (confirmed in the field).
|
|
557
|
+
if (cross <= 0)
|
|
558
|
+
out.push(a, b, c);
|
|
559
|
+
else
|
|
560
|
+
out.push(a, c, b);
|
|
561
|
+
}
|
|
562
|
+
/**
|
|
563
|
+
* The orientation of a convex polygon (the sign of the signed area)
|
|
564
|
+
*/
|
|
565
|
+
function signedAreaSign(poly, n, pool) {
|
|
566
|
+
let sum = 0;
|
|
567
|
+
for (let i = 0; i < n; i++) {
|
|
568
|
+
const a = poly[i];
|
|
569
|
+
const b = poly[(i + 1) % n];
|
|
570
|
+
sum += pool.x(a) * pool.y(b) - pool.x(b) * pool.y(a);
|
|
571
|
+
}
|
|
572
|
+
return sum >= 0 ? 1 : -1;
|
|
573
|
+
}
|
|
574
|
+
/** Working storage for returning the x range of a band (not created on every call) */
|
|
575
|
+
const bandRange = [0, 0];
|
|
576
|
+
/**
|
|
577
|
+
* The x range of a convex polygon cut by the horizontal band [y0, y1]
|
|
578
|
+
*
|
|
579
|
+
* Nothing is added to the vertex pool. What we work out here is only "the columns to look
|
|
580
|
+
* at in this row", while the cut shape itself is derived from the original polygon for each
|
|
581
|
+
* cell (putting it into the pool would leave vertices that no triangle uses).
|
|
582
|
+
*
|
|
583
|
+
* @returns [minX, maxX]. null if it does not touch the band
|
|
584
|
+
*/
|
|
585
|
+
function bandXRange(poly, n, pool, y0, y1) {
|
|
586
|
+
let minX = Number.POSITIVE_INFINITY;
|
|
587
|
+
let maxX = Number.NEGATIVE_INFINITY;
|
|
588
|
+
const consider = (x) => {
|
|
589
|
+
if (x < minX)
|
|
590
|
+
minX = x;
|
|
591
|
+
if (x > maxX)
|
|
592
|
+
maxX = x;
|
|
593
|
+
};
|
|
594
|
+
for (let edge = 0; edge < n; edge++) {
|
|
595
|
+
const a = poly[edge];
|
|
596
|
+
const b = poly[(edge + 1) % n];
|
|
597
|
+
const ax = pool.x(a);
|
|
598
|
+
const ay = pool.y(a);
|
|
599
|
+
const bx = pool.x(b);
|
|
600
|
+
const by = pool.y(b);
|
|
601
|
+
// Vertices that lie inside the band
|
|
602
|
+
if (ay >= y0 && ay <= y1)
|
|
603
|
+
consider(ax);
|
|
604
|
+
// Intersections of the edge with the lines at the top and bottom of the band
|
|
605
|
+
if (ay !== by) {
|
|
606
|
+
for (let k = 0; k < 2; k++) {
|
|
607
|
+
const line = k === 0 ? y0 : y1;
|
|
608
|
+
const t = (line - ay) / (by - ay);
|
|
609
|
+
if (t < 0 || t > 1)
|
|
610
|
+
continue;
|
|
611
|
+
consider(ax + (bx - ax) * t);
|
|
612
|
+
}
|
|
613
|
+
}
|
|
614
|
+
}
|
|
615
|
+
if (minX > maxX)
|
|
616
|
+
return null;
|
|
617
|
+
bandRange[0] = minX;
|
|
618
|
+
bandRange[1] = maxX;
|
|
619
|
+
return bandRange;
|
|
620
|
+
}
|
|
621
|
+
/** The cell is outside the triangle (all four corners lie outside one of the edges) */
|
|
622
|
+
const CELL_OUTSIDE = 0;
|
|
623
|
+
/** The cell is inside the triangle (the four corners are inside for all three edges) */
|
|
624
|
+
const CELL_INSIDE = 1;
|
|
625
|
+
/** The cell touches the border of the triangle (it has to be cut) */
|
|
626
|
+
const CELL_STRADDLE = 2;
|
|
627
|
+
/**
|
|
628
|
+
* Decide the relation between a cell and a triangle from the signs of the three edges alone
|
|
629
|
+
*
|
|
630
|
+
* The decision is conservative. When a corner lies exactly on the border we fall to
|
|
631
|
+
* "touches" and send it down the cutting path. The cutting path and the emitting path give
|
|
632
|
+
* the same result, so which way we fall never changes the result (it is only a matter of
|
|
633
|
+
* speed).
|
|
634
|
+
*/
|
|
635
|
+
function cellInsidePolygon(poly, n, pool, gx0, gy0, gx1, gy1, orientation) {
|
|
636
|
+
let allInside = true;
|
|
637
|
+
for (let edge = 0; edge < n; edge++) {
|
|
638
|
+
const a = poly[edge];
|
|
639
|
+
const b = poly[(edge + 1) % n];
|
|
640
|
+
const x0 = pool.x(a);
|
|
641
|
+
const y0 = pool.y(a);
|
|
642
|
+
const ex = pool.x(b) - x0;
|
|
643
|
+
const ey = pool.y(b) - y0;
|
|
644
|
+
// Count the signs of the cross products at the four corners
|
|
645
|
+
let insideCount = 0;
|
|
646
|
+
let outsideCount = 0;
|
|
647
|
+
for (let corner = 0; corner < 4; corner++) {
|
|
648
|
+
const px = corner === 0 || corner === 3 ? gx0 : gx1;
|
|
649
|
+
const py = corner < 2 ? gy0 : gy1;
|
|
650
|
+
const cross = (ex * (py - y0) - ey * (px - x0)) * orientation;
|
|
651
|
+
if (cross > 0)
|
|
652
|
+
insideCount++;
|
|
653
|
+
else
|
|
654
|
+
outsideCount++;
|
|
655
|
+
}
|
|
656
|
+
if (insideCount === 0)
|
|
657
|
+
return CELL_OUTSIDE;
|
|
658
|
+
if (outsideCount > 0)
|
|
659
|
+
allInside = false;
|
|
660
|
+
}
|
|
661
|
+
return allInside ? CELL_INSIDE : CELL_STRADDLE;
|
|
662
|
+
}
|
|
663
|
+
/**
|
|
664
|
+
* Rows of lattice node numbers (two rows are reused; they are not rebuilt per triangle)
|
|
665
|
+
*/
|
|
666
|
+
const nodeRows = [new Int32Array(0), new Int32Array(0)];
|
|
667
|
+
/**
|
|
668
|
+
* Grow the row array to the required length and return it
|
|
669
|
+
*/
|
|
670
|
+
function ensureNodeRow(slot, width) {
|
|
671
|
+
if (nodeRows[slot].length < width)
|
|
672
|
+
nodeRows[slot] = new Int32Array(width);
|
|
673
|
+
return nodeRows[slot];
|
|
674
|
+
}
|
|
675
|
+
/**
|
|
676
|
+
* Look up the number of a lattice node (registering it if the row does not remember it)
|
|
677
|
+
*/
|
|
678
|
+
function nodeAt(pool, row, index, x, y) {
|
|
679
|
+
const cached = row[index];
|
|
680
|
+
if (cached >= 0)
|
|
681
|
+
return cached;
|
|
682
|
+
const created = pool.intern(x, y);
|
|
683
|
+
row[index] = created;
|
|
684
|
+
return created;
|
|
685
|
+
}
|
|
686
|
+
/**
|
|
687
|
+
* The vertex pool (held in Mercator coordinates, with duplicates merged)
|
|
688
|
+
*
|
|
689
|
+
* By assigning the same number to the same coordinate, neighbouring triangles share their
|
|
690
|
+
* split points. If they are shared, their elevations necessarily agree too, so no crack
|
|
691
|
+
* appears in the fill.
|
|
692
|
+
*
|
|
693
|
+
* The longitude and latitude of the vertices that were there from the start are emitted as
|
|
694
|
+
* they are, without a round trip through the conversion.
|
|
695
|
+
*/
|
|
696
|
+
class VertexPool {
|
|
697
|
+
constructor(source, maxPoints) {
|
|
698
|
+
Object.defineProperty(this, "mx", {
|
|
699
|
+
enumerable: true,
|
|
700
|
+
configurable: true,
|
|
701
|
+
writable: true,
|
|
702
|
+
value: []
|
|
703
|
+
});
|
|
704
|
+
Object.defineProperty(this, "my", {
|
|
705
|
+
enumerable: true,
|
|
706
|
+
configurable: true,
|
|
707
|
+
writable: true,
|
|
708
|
+
value: []
|
|
709
|
+
});
|
|
710
|
+
Object.defineProperty(this, "lng", {
|
|
711
|
+
enumerable: true,
|
|
712
|
+
configurable: true,
|
|
713
|
+
writable: true,
|
|
714
|
+
value: []
|
|
715
|
+
});
|
|
716
|
+
Object.defineProperty(this, "lat", {
|
|
717
|
+
enumerable: true,
|
|
718
|
+
configurable: true,
|
|
719
|
+
writable: true,
|
|
720
|
+
value: []
|
|
721
|
+
});
|
|
722
|
+
/** A two-stage lookup, x -> (y -> number) (the keys are numbers; no strings are made) */
|
|
723
|
+
Object.defineProperty(this, "lookup", {
|
|
724
|
+
enumerable: true,
|
|
725
|
+
configurable: true,
|
|
726
|
+
writable: true,
|
|
727
|
+
value: new Map()
|
|
728
|
+
});
|
|
729
|
+
Object.defineProperty(this, "limit", {
|
|
730
|
+
enumerable: true,
|
|
731
|
+
configurable: true,
|
|
732
|
+
writable: true,
|
|
733
|
+
value: void 0
|
|
734
|
+
});
|
|
735
|
+
Object.defineProperty(this, "overflow", {
|
|
736
|
+
enumerable: true,
|
|
737
|
+
configurable: true,
|
|
738
|
+
writable: true,
|
|
739
|
+
value: false
|
|
740
|
+
});
|
|
741
|
+
// The vertices that were there from the start are not put into the matching table.
|
|
742
|
+
//
|
|
743
|
+
// The table exists in order to "let a point newly created by splitting be shared with
|
|
744
|
+
// the neighbouring cell". Even if an original vertex and a split point happen to land on
|
|
745
|
+
// the same coordinate, nothing goes wrong by not sharing them: if the coordinates are
|
|
746
|
+
// identical down to the last bit, the elevation comes out the same value too and the
|
|
747
|
+
// surface does not crack (sharing saves vertices; it does not prevent cracks). Meanwhile
|
|
748
|
+
// the cost of putting them into the table is proportional to the feature's vertex count,
|
|
749
|
+
// and for data with "thousands of small polygons" such as administrative boundaries it
|
|
750
|
+
// costs far more than the number of cells does (measured: most of the 362μs per
|
|
751
|
+
// feature).
|
|
752
|
+
for (let i = 0; i < source.length; i += 2) {
|
|
753
|
+
const lng = source[i];
|
|
754
|
+
const lat = source[i + 1];
|
|
755
|
+
this.mx.push(mercatorX(lng));
|
|
756
|
+
this.my.push(mercatorY(lat));
|
|
757
|
+
this.lng.push(lng);
|
|
758
|
+
this.lat.push(lat);
|
|
759
|
+
}
|
|
760
|
+
this.limit = this.mx.length + Math.max(0, maxPoints);
|
|
761
|
+
}
|
|
762
|
+
exhausted() {
|
|
763
|
+
return this.overflow;
|
|
764
|
+
}
|
|
765
|
+
/**
|
|
766
|
+
* How many times the budget's worth of vertices we tried to create (usable as a step
|
|
767
|
+
* factor)
|
|
768
|
+
*
|
|
769
|
+
* The vertex count is inversely proportional to the square of the step, so the square root
|
|
770
|
+
* gives the step factor.
|
|
771
|
+
*/
|
|
772
|
+
overflowRatio() {
|
|
773
|
+
if (!(this.limit > 0))
|
|
774
|
+
return 1;
|
|
775
|
+
return Math.max(1, Math.sqrt(this.mx.length / this.limit));
|
|
776
|
+
}
|
|
777
|
+
/**
|
|
778
|
+
* Register a Mercator coordinate and return its number
|
|
779
|
+
*
|
|
780
|
+
* The matching is exact. It is tempting to round before matching, but it must not be
|
|
781
|
+
* done. The intersection of a lattice line and an edge can come arbitrarily close to a
|
|
782
|
+
* lattice node, so with even the slightest rounding "a nearby but different point" is
|
|
783
|
+
* treated as the same one, that cell's fragment degenerates and drops out, and a hole one
|
|
784
|
+
* cell wide opens in the surface (measured). With exact matching, what remains instead is
|
|
785
|
+
* that vertices that are "the same point but differ in the last bit" occasionally survive.
|
|
786
|
+
* That becomes a crack 1e-16 degrees wide (1e-11 m on the ground), but it does not show on
|
|
787
|
+
* screen. Unlike a hole, the coverage of the surface is not lost.
|
|
788
|
+
*
|
|
789
|
+
* The key is a two-stage numeric lookup. A Map's numeric keys use SameValueZero
|
|
790
|
+
* comparison, so the matching is just as exact as it was back when we turned them into the
|
|
791
|
+
* string `${x},${y}`, while the per-point string creation disappears.
|
|
792
|
+
*/
|
|
793
|
+
intern(x, y) {
|
|
794
|
+
const row = this.lookup.get(x);
|
|
795
|
+
if (row) {
|
|
796
|
+
const found = row.get(y);
|
|
797
|
+
if (found !== undefined)
|
|
798
|
+
return found;
|
|
799
|
+
}
|
|
800
|
+
if (this.mx.length >= this.limit)
|
|
801
|
+
this.overflow = true;
|
|
802
|
+
const index = this.mx.length;
|
|
803
|
+
this.mx.push(x);
|
|
804
|
+
this.my.push(y);
|
|
805
|
+
this.lng.push(lngFromMercatorX(x));
|
|
806
|
+
this.lat.push(latFromMercatorY(y));
|
|
807
|
+
if (row)
|
|
808
|
+
row.set(y, index);
|
|
809
|
+
else
|
|
810
|
+
this.lookup.set(x, new Map([[y, index]]));
|
|
811
|
+
return index;
|
|
812
|
+
}
|
|
813
|
+
x(index) {
|
|
814
|
+
return this.mx[index];
|
|
815
|
+
}
|
|
816
|
+
y(index) {
|
|
817
|
+
return this.my[index];
|
|
818
|
+
}
|
|
819
|
+
/** Build the flattened coordinate array of longitudes and latitudes */
|
|
820
|
+
output() {
|
|
821
|
+
const out = new Array(this.lng.length * 2);
|
|
822
|
+
for (let i = 0; i < this.lng.length; i++) {
|
|
823
|
+
out[i * 2] = this.lng[i];
|
|
824
|
+
out[i * 2 + 1] = this.lat[i];
|
|
825
|
+
}
|
|
826
|
+
return out;
|
|
827
|
+
}
|
|
828
|
+
}
|
|
829
|
+
/**
|
|
830
|
+
* Cut a convex polygon with an axis-aligned straight line (Sutherland-Hodgman)
|
|
831
|
+
*
|
|
832
|
+
* @param axis 0 = Mercator x (a vertical line), 1 = Mercator y (a horizontal line)
|
|
833
|
+
* @param keepLower If true, keep the side with the smaller value
|
|
834
|
+
*/
|
|
835
|
+
function clipHalfPlane(poly, polyLength, out, pool, axis, line, keepLower) {
|
|
836
|
+
let count = 0;
|
|
837
|
+
const push = (index) => {
|
|
838
|
+
// Fold it away if it has the same number as the one just before (a vertex on a lattice
|
|
839
|
+
// line can be added twice)
|
|
840
|
+
if (count > 0 && out[count - 1] === index)
|
|
841
|
+
return;
|
|
842
|
+
out[count++] = index;
|
|
843
|
+
};
|
|
844
|
+
for (let i = 0; i < polyLength; i++) {
|
|
845
|
+
const current = poly[i];
|
|
846
|
+
const next = poly[(i + 1) % polyLength];
|
|
847
|
+
const currentValue = axis === 0 ? pool.x(current) : pool.y(current);
|
|
848
|
+
const nextValue = axis === 0 ? pool.x(next) : pool.y(next);
|
|
849
|
+
const currentIn = keepLower ? currentValue <= line : currentValue >= line;
|
|
850
|
+
const nextIn = keepLower ? nextValue <= line : nextValue >= line;
|
|
851
|
+
if (currentIn)
|
|
852
|
+
push(current);
|
|
853
|
+
if (currentIn !== nextIn) {
|
|
854
|
+
const crossed = intersectAxis(current, next, pool, axis, line);
|
|
855
|
+
if (crossed !== current)
|
|
856
|
+
push(crossed);
|
|
857
|
+
}
|
|
858
|
+
}
|
|
859
|
+
if (count > 1 && out[0] === out[count - 1])
|
|
860
|
+
count--;
|
|
861
|
+
return count;
|
|
862
|
+
}
|
|
863
|
+
/**
|
|
864
|
+
* The intersection of an edge and an axis-aligned line (independent of the order of the
|
|
865
|
+
* endpoints)
|
|
866
|
+
*/
|
|
867
|
+
function intersectAxis(a, b, pool, axis, line) {
|
|
868
|
+
// Normalise the order of the endpoints, so that the neighbouring triangle that shares the
|
|
869
|
+
// same edge gets the same floating point result even when it traverses in the opposite
|
|
870
|
+
// direction.
|
|
871
|
+
let p = a;
|
|
872
|
+
let q = b;
|
|
873
|
+
const ax = pool.x(a);
|
|
874
|
+
const ay = pool.y(a);
|
|
875
|
+
const bx = pool.x(b);
|
|
876
|
+
const by = pool.y(b);
|
|
877
|
+
if (bx < ax || (bx === ax && by < ay)) {
|
|
878
|
+
p = b;
|
|
879
|
+
q = a;
|
|
880
|
+
}
|
|
881
|
+
const px = pool.x(p);
|
|
882
|
+
const py = pool.y(p);
|
|
883
|
+
const qx = pool.x(q);
|
|
884
|
+
const qy = pool.y(q);
|
|
885
|
+
if (axis === 0) {
|
|
886
|
+
const denominator = qx - px;
|
|
887
|
+
const t = denominator === 0 ? 0 : (line - px) / denominator;
|
|
888
|
+
return pool.intern(line, py + (qy - py) * t);
|
|
889
|
+
}
|
|
890
|
+
const denominator = qy - py;
|
|
891
|
+
const t = denominator === 0 ? 0 : (line - py) / denominator;
|
|
892
|
+
return pool.intern(px + (qx - px) * t, line);
|
|
893
|
+
}
|
|
894
|
+
/**
|
|
895
|
+
* Triangulate a convex polygon as a fan and emit it
|
|
896
|
+
*
|
|
897
|
+
* A lattice cell (4 vertices) is split along the same diagonal as MapLibre's terrain mesh.
|
|
898
|
+
* MapLibre's `getTerrainMesh` splits along the main diagonal joining (x, y) and (x+1, y+1),
|
|
899
|
+
* so we take the corner with the smallest Mercator coordinates as the origin of the fan. If
|
|
900
|
+
* this disagrees, the surfaces twist and disagree inside a cell even though they agree on
|
|
901
|
+
* the nodes.
|
|
902
|
+
*/
|
|
903
|
+
function emitFan(poly, n, pool, out) {
|
|
904
|
+
let start = 0;
|
|
905
|
+
if (n === 4) {
|
|
906
|
+
let best = Number.POSITIVE_INFINITY;
|
|
907
|
+
for (let i = 0; i < n; i++) {
|
|
908
|
+
const score = pool.x(poly[i]) + pool.y(poly[i]);
|
|
909
|
+
if (score < best) {
|
|
910
|
+
best = score;
|
|
911
|
+
start = i;
|
|
912
|
+
}
|
|
913
|
+
}
|
|
914
|
+
}
|
|
915
|
+
for (let i = 1; i + 1 < n; i++) {
|
|
916
|
+
pushOriented(out, pool, poly[start], poly[(start + i) % n], poly[(start + i + 1) % n]);
|
|
917
|
+
}
|
|
918
|
+
}
|
|
919
|
+
/**
|
|
920
|
+
* Working storage used for carving out each cell (a convex polygon obtained by cutting a
|
|
921
|
+
* triangle with 4 axis-aligned lines has at most 7 vertices; we reserve 16 for headroom)
|
|
922
|
+
*/
|
|
923
|
+
const clipBufferA = new Int32Array(16);
|
|
924
|
+
const clipBufferB = new Int32Array(16);
|