@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,910 @@
|
|
|
1
|
+
// SPDX-FileCopyrightText: 2026 SAKAIDA Atsushi
|
|
2
|
+
// SPDX-License-Identifier: AGPL-3.0-only
|
|
3
|
+
/**
|
|
4
|
+
* The zoom at which the thinning stops (default). At or above it every feature is drawn
|
|
5
|
+
*
|
|
6
|
+
* @internal
|
|
7
|
+
*/
|
|
8
|
+
export const DEFAULT_FULL_DISPLAY_ZOOM = 17;
|
|
9
|
+
/**
|
|
10
|
+
* Margin added to a footprint (px, default)
|
|
11
|
+
*
|
|
12
|
+
* @internal
|
|
13
|
+
*/
|
|
14
|
+
export const DEFAULT_THINNING_MARGIN_PX = 2;
|
|
15
|
+
/**
|
|
16
|
+
* How many bands of winners a dataset keeps (one byte per row each). A zoom gesture crosses a few
|
|
17
|
+
* bands; going back and forth over them costs no selection
|
|
18
|
+
*/
|
|
19
|
+
const MAX_CACHED_BANDS = 6;
|
|
20
|
+
/** Upper limit of the zoom bands (22 steps, the same as the tile coordinates) */
|
|
21
|
+
const MAX_ZOOM_BAND = 22;
|
|
22
|
+
/** Screen pixels of one tile of the mercator world coordinates (512px tiles) */
|
|
23
|
+
const TILE_SIZE_PX = 512;
|
|
24
|
+
/** Upper limit of the latitude that Web Mercator can express */
|
|
25
|
+
const MAX_MERCATOR_LAT = 85.0511287798066;
|
|
26
|
+
/**
|
|
27
|
+
* Resolves the settings (an unspecified item takes its default)
|
|
28
|
+
*
|
|
29
|
+
* fullDisplayZoom and marginPx fall back to their defaults for an invalid value (NaN, negative).
|
|
30
|
+
*
|
|
31
|
+
* @internal
|
|
32
|
+
*/
|
|
33
|
+
export function resolveCollisionThinning(input) {
|
|
34
|
+
return {
|
|
35
|
+
enabled: input?.enabled ?? false,
|
|
36
|
+
fullDisplayZoom: input?.fullDisplayZoom !== undefined && Number.isFinite(input.fullDisplayZoom)
|
|
37
|
+
? input.fullDisplayZoom
|
|
38
|
+
: DEFAULT_FULL_DISPLAY_ZOOM,
|
|
39
|
+
marginPx: input?.marginPx !== undefined && Number.isFinite(input.marginPx) && input.marginPx >= 0
|
|
40
|
+
? input.marginPx
|
|
41
|
+
: DEFAULT_THINNING_MARGIN_PX,
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Whether two settings are the same
|
|
46
|
+
*
|
|
47
|
+
* @internal
|
|
48
|
+
*/
|
|
49
|
+
export function sameCollisionThinning(a, b) {
|
|
50
|
+
if (a === null || b === null)
|
|
51
|
+
return a === b;
|
|
52
|
+
return (a.enabled === b.enabled && a.fullDisplayZoom === b.fullDisplayZoom && a.marginPx === b.marginPx);
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Computes the integer band from a zoom (clamped into 0..22)
|
|
56
|
+
*
|
|
57
|
+
* @internal
|
|
58
|
+
*/
|
|
59
|
+
export function zoomBandFor(zoom) {
|
|
60
|
+
if (!Number.isFinite(zoom))
|
|
61
|
+
return 0;
|
|
62
|
+
return Math.min(MAX_ZOOM_BAND, Math.max(0, Math.floor(zoom)));
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* The length in mercator world coordinates that corresponds to one screen pixel in band b
|
|
66
|
+
*
|
|
67
|
+
* The whole world is `512 * 2^b` px on a side, so it is the reciprocal of that. It works not
|
|
68
|
+
* only for an integer band but also for a fractional zoom such as an effective zoom.
|
|
69
|
+
*
|
|
70
|
+
* @internal
|
|
71
|
+
*/
|
|
72
|
+
export function worldUnitsPerPixel(band) {
|
|
73
|
+
return 1 / (TILE_SIZE_PX * 2 ** band);
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Upper limit of the drop of the effective zoom allowed by the pitch correction
|
|
77
|
+
*
|
|
78
|
+
* When the pitch is large and the horizon enters the screen, the top edge goes to infinity and
|
|
79
|
+
* the ratio diverges. The cap keeps the band from falling to 0 and tipping into "almost nothing
|
|
80
|
+
* is drawn". At the default maximum pitch of 60 degrees the drop is about 3.2, so it normally
|
|
81
|
+
* has no effect.
|
|
82
|
+
*
|
|
83
|
+
* @internal
|
|
84
|
+
*/
|
|
85
|
+
export const MAX_PITCH_ZOOM_DROP = 8;
|
|
86
|
+
/**
|
|
87
|
+
* Computes the shallowest effective zoom on screen (a pure function)
|
|
88
|
+
*
|
|
89
|
+
* With a pitch, the higher part of the screen is farther away and only there the effective scale
|
|
90
|
+
* gets shallower. Deciding the band with the zoom of the camera would let everything flow into
|
|
91
|
+
* the distance and get squashed. So the ratio "how many pixels the distance from the center of
|
|
92
|
+
* the screen to the top center would correspond to on a plane (pitch 0)" is measured, and the
|
|
93
|
+
* zoom is lowered by that much.
|
|
94
|
+
*
|
|
95
|
+
* - On a plane, the distance in mercator world coordinates from the center to the top edge
|
|
96
|
+
* matches `pixelDistance / (512 * 2^zoom)` exactly (ratio = 1), so at pitch 0 the result is
|
|
97
|
+
* exactly the zoom
|
|
98
|
+
* - The same camera state always returns the same value (it is deterministic)
|
|
99
|
+
* - The drop is capped by `MAX_PITCH_ZOOM_DROP`
|
|
100
|
+
*
|
|
101
|
+
* @param zoom The zoom of the camera
|
|
102
|
+
* @param center The map coordinate of the center of the screen
|
|
103
|
+
* @param topCenter The map coordinate of the top center of the screen
|
|
104
|
+
* @param pixelDistance The screen pixels from the center to the top edge (half the height)
|
|
105
|
+
*
|
|
106
|
+
* @internal
|
|
107
|
+
*/
|
|
108
|
+
export function effectiveZoomForPitch(input) {
|
|
109
|
+
const { zoom, center, topCenter, pixelDistance } = input;
|
|
110
|
+
if (!Number.isFinite(zoom))
|
|
111
|
+
return zoom;
|
|
112
|
+
if (!Number.isFinite(pixelDistance) || pixelDistance <= 0)
|
|
113
|
+
return zoom;
|
|
114
|
+
const dx = lngToWorldX(topCenter[0]) - lngToWorldX(center[0]);
|
|
115
|
+
const dy = latToWorldY(topCenter[1]) - latToWorldY(center[1]);
|
|
116
|
+
const measured = Math.sqrt(dx * dx + dy * dy);
|
|
117
|
+
const flat = pixelDistance * worldUnitsPerPixel(zoom);
|
|
118
|
+
if (!Number.isFinite(measured) || measured <= 0 || flat <= 0)
|
|
119
|
+
return zoom;
|
|
120
|
+
const ratio = measured / flat;
|
|
121
|
+
// When the ratio is at most 1 (pitch 0, rounding error) the zoom of the camera is used as before
|
|
122
|
+
if (!Number.isFinite(ratio) || ratio <= 1)
|
|
123
|
+
return zoom;
|
|
124
|
+
return Math.max(zoom - MAX_PITCH_ZOOM_DROP, zoom - Math.log2(ratio));
|
|
125
|
+
}
|
|
126
|
+
/**
|
|
127
|
+
* Computes the effective zoom from the camera
|
|
128
|
+
*
|
|
129
|
+
* When the pitch is 0 it returns the zoom of the camera without measuring anything (so even with
|
|
130
|
+
* a projection that cannot be measured on a plane, such as globe, the result is exactly the same
|
|
131
|
+
* as before as long as there is no pitch).
|
|
132
|
+
*
|
|
133
|
+
* @internal
|
|
134
|
+
*/
|
|
135
|
+
export function effectiveZoomForCamera(camera) {
|
|
136
|
+
const zoom = camera.getZoom();
|
|
137
|
+
const pitch = camera.getPitch();
|
|
138
|
+
if (!Number.isFinite(pitch) || pitch <= 0)
|
|
139
|
+
return zoom;
|
|
140
|
+
const canvas = camera.getCanvas();
|
|
141
|
+
const width = canvas.clientWidth;
|
|
142
|
+
const height = canvas.clientHeight;
|
|
143
|
+
if (!(width > 0) || !(height > 0))
|
|
144
|
+
return zoom;
|
|
145
|
+
const centerX = width / 2;
|
|
146
|
+
const centerY = height / 2;
|
|
147
|
+
const center = camera.unproject([centerX, centerY]);
|
|
148
|
+
const topCenter = camera.unproject([centerX, 0]);
|
|
149
|
+
return effectiveZoomForPitch({
|
|
150
|
+
zoom,
|
|
151
|
+
center: [center.lng, center.lat],
|
|
152
|
+
topCenter: [topCenter.lng, topCenter.lat],
|
|
153
|
+
pixelDistance: centerY,
|
|
154
|
+
});
|
|
155
|
+
}
|
|
156
|
+
/**
|
|
157
|
+
* Longitude into the mercator world coordinate X (0..1)
|
|
158
|
+
*
|
|
159
|
+
* @internal
|
|
160
|
+
*/
|
|
161
|
+
export function lngToWorldX(lng) {
|
|
162
|
+
return (Math.min(180, Math.max(-180, lng)) + 180) / 360;
|
|
163
|
+
}
|
|
164
|
+
/**
|
|
165
|
+
* Latitude into the mercator world coordinate Y (0..1)
|
|
166
|
+
*
|
|
167
|
+
* @internal
|
|
168
|
+
*/
|
|
169
|
+
export function latToWorldY(lat) {
|
|
170
|
+
const clamped = Math.min(MAX_MERCATOR_LAT, Math.max(-MAX_MERCATOR_LAT, lat));
|
|
171
|
+
const rad = (clamped * Math.PI) / 180;
|
|
172
|
+
return (1 - Math.log(Math.tan(rad) + 1 / Math.cos(rad)) / Math.PI) / 2;
|
|
173
|
+
}
|
|
174
|
+
/**
|
|
175
|
+
* Computes the radius (px) at which a point marker is drawn
|
|
176
|
+
*
|
|
177
|
+
* It is "the size that appears on screen": the radius of the fill plus the width of the white
|
|
178
|
+
* outline, multiplied by the zoom-dependent factor. The radius is resolved in the same order as
|
|
179
|
+
* on the rendering side (FeatureDrawer.getPointStyle): "the individual style > the base style >
|
|
180
|
+
* the core default". The outline width of a point cannot be changed by an individual style, so
|
|
181
|
+
* the default value is used as it is.
|
|
182
|
+
*
|
|
183
|
+
* Both the footprint of the thinning (with the margin added) and the hit radius of the hit
|
|
184
|
+
* testing (grabbable exactly as it looks) go through this single place.
|
|
185
|
+
*
|
|
186
|
+
* @param styleRadius The radius of the individual style (undefined when there is none)
|
|
187
|
+
* @param baseStyle The base style of the dataset
|
|
188
|
+
* @param defaults The default point style of core
|
|
189
|
+
* @param scale The zoom-dependent size factor (the scale of zoomScale)
|
|
190
|
+
*
|
|
191
|
+
* @internal
|
|
192
|
+
*/
|
|
193
|
+
export function pointMarkerRadiusPx(styleRadius, baseStyle, defaults, scale) {
|
|
194
|
+
const radius = styleRadius ?? baseStyle?.point?.pointRadius ?? defaults.size / 2;
|
|
195
|
+
const stroke = defaults.strokeOpacity > 0 ? defaults.strokeWidth : 0;
|
|
196
|
+
return Math.max(0, (radius + stroke) * scale);
|
|
197
|
+
}
|
|
198
|
+
/**
|
|
199
|
+
* Computes the footprint radius (px) of a point
|
|
200
|
+
*
|
|
201
|
+
* The decision uses the size actually drawn, so the margin is added to the radius that is drawn
|
|
202
|
+
* (`pointMarkerRadiusPx`).
|
|
203
|
+
*
|
|
204
|
+
* @param feature The target (a Point)
|
|
205
|
+
* @param baseStyle The base style of the dataset
|
|
206
|
+
* @param defaults The default point style of core
|
|
207
|
+
* @param scale The zoom-dependent size factor (the scale of zoomScale)
|
|
208
|
+
* @param marginPx The margin (px)
|
|
209
|
+
*
|
|
210
|
+
* @internal
|
|
211
|
+
*/
|
|
212
|
+
export function pointFootprintPx(feature, baseStyle, defaults, scale, marginPx) {
|
|
213
|
+
return pointMarkerRadiusPx(feature.style?.pointRadius, baseStyle, defaults, scale) + marginPx;
|
|
214
|
+
}
|
|
215
|
+
/**
|
|
216
|
+
* The number of rows handled between two looks at the clock by a selection advanced in slices
|
|
217
|
+
*
|
|
218
|
+
* @internal
|
|
219
|
+
*/
|
|
220
|
+
export const SELECTION_SLICE_ROWS = 4096;
|
|
221
|
+
/**
|
|
222
|
+
* The selection of the winners of one band, advanced in slices (`step`)
|
|
223
|
+
*
|
|
224
|
+
* The work is the same as `selectCollisionWinnerRows`, in two phases that both stop at a deadline:
|
|
225
|
+
* the rows are read into arrays of points in world coordinates, then the points are taken
|
|
226
|
+
* greedily from the front. Whatever the slicing, the result is the same as a selection run in one
|
|
227
|
+
* go (the greedy order and the grid do not depend on where it stopped).
|
|
228
|
+
*
|
|
229
|
+
* @internal
|
|
230
|
+
*/
|
|
231
|
+
export class CollisionSelection {
|
|
232
|
+
/**
|
|
233
|
+
* @param radiusPx The footprint radius of a row (px, including the outline and the margin)
|
|
234
|
+
*/
|
|
235
|
+
constructor(rows, band, radiusPx) {
|
|
236
|
+
Object.defineProperty(this, "rows", {
|
|
237
|
+
enumerable: true,
|
|
238
|
+
configurable: true,
|
|
239
|
+
writable: true,
|
|
240
|
+
value: rows
|
|
241
|
+
});
|
|
242
|
+
Object.defineProperty(this, "radiusPx", {
|
|
243
|
+
enumerable: true,
|
|
244
|
+
configurable: true,
|
|
245
|
+
writable: true,
|
|
246
|
+
value: radiusPx
|
|
247
|
+
});
|
|
248
|
+
Object.defineProperty(this, "winners", {
|
|
249
|
+
enumerable: true,
|
|
250
|
+
configurable: true,
|
|
251
|
+
writable: true,
|
|
252
|
+
value: void 0
|
|
253
|
+
});
|
|
254
|
+
Object.defineProperty(this, "xs", {
|
|
255
|
+
enumerable: true,
|
|
256
|
+
configurable: true,
|
|
257
|
+
writable: true,
|
|
258
|
+
value: void 0
|
|
259
|
+
});
|
|
260
|
+
Object.defineProperty(this, "ys", {
|
|
261
|
+
enumerable: true,
|
|
262
|
+
configurable: true,
|
|
263
|
+
writable: true,
|
|
264
|
+
value: void 0
|
|
265
|
+
});
|
|
266
|
+
Object.defineProperty(this, "rs", {
|
|
267
|
+
enumerable: true,
|
|
268
|
+
configurable: true,
|
|
269
|
+
writable: true,
|
|
270
|
+
value: void 0
|
|
271
|
+
});
|
|
272
|
+
Object.defineProperty(this, "pointRows", {
|
|
273
|
+
enumerable: true,
|
|
274
|
+
configurable: true,
|
|
275
|
+
writable: true,
|
|
276
|
+
value: void 0
|
|
277
|
+
});
|
|
278
|
+
Object.defineProperty(this, "perPixel", {
|
|
279
|
+
enumerable: true,
|
|
280
|
+
configurable: true,
|
|
281
|
+
writable: true,
|
|
282
|
+
value: void 0
|
|
283
|
+
});
|
|
284
|
+
/** The next row to read (the first phase) */
|
|
285
|
+
Object.defineProperty(this, "nextRow", {
|
|
286
|
+
enumerable: true,
|
|
287
|
+
configurable: true,
|
|
288
|
+
writable: true,
|
|
289
|
+
value: 0
|
|
290
|
+
});
|
|
291
|
+
Object.defineProperty(this, "pointCount", {
|
|
292
|
+
enumerable: true,
|
|
293
|
+
configurable: true,
|
|
294
|
+
writable: true,
|
|
295
|
+
value: 0
|
|
296
|
+
});
|
|
297
|
+
Object.defineProperty(this, "maxRadius", {
|
|
298
|
+
enumerable: true,
|
|
299
|
+
configurable: true,
|
|
300
|
+
writable: true,
|
|
301
|
+
value: 0
|
|
302
|
+
});
|
|
303
|
+
/** The grid of the winners (null until the rows are read) */
|
|
304
|
+
Object.defineProperty(this, "grid", {
|
|
305
|
+
enumerable: true,
|
|
306
|
+
configurable: true,
|
|
307
|
+
writable: true,
|
|
308
|
+
value: null
|
|
309
|
+
});
|
|
310
|
+
/** The next point to decide, from the front (the second phase) */
|
|
311
|
+
Object.defineProperty(this, "nextPoint", {
|
|
312
|
+
enumerable: true,
|
|
313
|
+
configurable: true,
|
|
314
|
+
writable: true,
|
|
315
|
+
value: -1
|
|
316
|
+
});
|
|
317
|
+
Object.defineProperty(this, "finished", {
|
|
318
|
+
enumerable: true,
|
|
319
|
+
configurable: true,
|
|
320
|
+
writable: true,
|
|
321
|
+
value: false
|
|
322
|
+
});
|
|
323
|
+
const count = rows.length;
|
|
324
|
+
this.winners = new Uint8Array(count);
|
|
325
|
+
this.xs = new Float64Array(count);
|
|
326
|
+
this.ys = new Float64Array(count);
|
|
327
|
+
this.rs = new Float64Array(count);
|
|
328
|
+
this.pointRows = new Int32Array(count);
|
|
329
|
+
this.perPixel = worldUnitsPerPixel(band);
|
|
330
|
+
}
|
|
331
|
+
/** Whether the selection is finished */
|
|
332
|
+
get done() {
|
|
333
|
+
return this.finished;
|
|
334
|
+
}
|
|
335
|
+
/**
|
|
336
|
+
* Advances until the deadline or the end
|
|
337
|
+
*
|
|
338
|
+
* At least one slice of rows is handled per call (otherwise it would never end).
|
|
339
|
+
*
|
|
340
|
+
* @returns Whether it is finished
|
|
341
|
+
*/
|
|
342
|
+
step(deadline, now) {
|
|
343
|
+
while (!this.finished) {
|
|
344
|
+
if (this.grid === null)
|
|
345
|
+
this.readRows();
|
|
346
|
+
else
|
|
347
|
+
this.decidePoints();
|
|
348
|
+
if (this.finished || now() >= deadline)
|
|
349
|
+
break;
|
|
350
|
+
}
|
|
351
|
+
return this.finished;
|
|
352
|
+
}
|
|
353
|
+
/** 1 for each row that may be drawn, 0 otherwise (complete once `done`) */
|
|
354
|
+
result() {
|
|
355
|
+
return this.winners;
|
|
356
|
+
}
|
|
357
|
+
/** Reads one slice of rows into the arrays of points (the first phase) */
|
|
358
|
+
readRows() {
|
|
359
|
+
const rows = this.rows;
|
|
360
|
+
const winners = this.winners;
|
|
361
|
+
const xs = this.xs;
|
|
362
|
+
const ys = this.ys;
|
|
363
|
+
const rs = this.rs;
|
|
364
|
+
const pointRows = this.pointRows;
|
|
365
|
+
const count = rows.length;
|
|
366
|
+
const end = Math.min(count, this.nextRow + SELECTION_SLICE_ROWS);
|
|
367
|
+
for (let row = this.nextRow; row < end; row++) {
|
|
368
|
+
const role = rows.thinningRole(row);
|
|
369
|
+
if (role === 'skip')
|
|
370
|
+
continue;
|
|
371
|
+
if (role === 'winner') {
|
|
372
|
+
// A line, a polygon, a MultiPoint and so on are not thinned (they win unconditionally)
|
|
373
|
+
winners[row] = 1;
|
|
374
|
+
continue;
|
|
375
|
+
}
|
|
376
|
+
const coord = rows.pointOf(row);
|
|
377
|
+
const radius = this.radiusPx(row) * this.perPixel;
|
|
378
|
+
const k = this.pointCount++;
|
|
379
|
+
xs[k] = lngToWorldX(coord[0]);
|
|
380
|
+
ys[k] = latToWorldY(coord[1]);
|
|
381
|
+
rs[k] = radius;
|
|
382
|
+
pointRows[k] = row;
|
|
383
|
+
if (radius > this.maxRadius)
|
|
384
|
+
this.maxRadius = radius;
|
|
385
|
+
}
|
|
386
|
+
this.nextRow = end;
|
|
387
|
+
if (end < count)
|
|
388
|
+
return;
|
|
389
|
+
if (this.pointCount === 0) {
|
|
390
|
+
this.finished = true;
|
|
391
|
+
return;
|
|
392
|
+
}
|
|
393
|
+
// With no footprint (radius 0) nobody collides
|
|
394
|
+
if (this.maxRadius <= 0) {
|
|
395
|
+
for (let k = 0; k < this.pointCount; k++)
|
|
396
|
+
winners[pointRows[k]] = 1;
|
|
397
|
+
this.finished = true;
|
|
398
|
+
return;
|
|
399
|
+
}
|
|
400
|
+
this.grid = createHashGrid(xs, ys, this.pointCount, this.maxRadius);
|
|
401
|
+
this.nextPoint = this.pointCount - 1;
|
|
402
|
+
}
|
|
403
|
+
/** Decides one slice of points, from the front (the second phase) */
|
|
404
|
+
decidePoints() {
|
|
405
|
+
const xs = this.xs;
|
|
406
|
+
const ys = this.ys;
|
|
407
|
+
const rs = this.rs;
|
|
408
|
+
const pointRows = this.pointRows;
|
|
409
|
+
const winners = this.winners;
|
|
410
|
+
const { cell, minX, minY, stride, buckets, keyDeltas } = this.grid;
|
|
411
|
+
const end = Math.max(-1, this.nextPoint - SELECTION_SLICE_ROWS);
|
|
412
|
+
// Taken greedily in the reverse of the draw order (from the frontmost)
|
|
413
|
+
for (let k = this.nextPoint; k > end; k--) {
|
|
414
|
+
const x = xs[k];
|
|
415
|
+
const y = ys[k];
|
|
416
|
+
const r = rs[k];
|
|
417
|
+
const key = (Math.floor((y - minY) / cell) + 1) * stride + (Math.floor((x - minX) / cell) + 1);
|
|
418
|
+
let blocked = false;
|
|
419
|
+
// From its own cell outwards, decided by the real distance to the winners (not by occupancy)
|
|
420
|
+
for (let d = 0; d < keyDeltas.length; d++) {
|
|
421
|
+
const bucket = buckets.get(key + keyDeltas[d]);
|
|
422
|
+
if (bucket === undefined)
|
|
423
|
+
continue;
|
|
424
|
+
for (let i = 0; i < bucket.length; i++) {
|
|
425
|
+
const j = bucket[i];
|
|
426
|
+
const ddx = xs[j] - x;
|
|
427
|
+
const ddy = ys[j] - y;
|
|
428
|
+
const sum = rs[j] + r;
|
|
429
|
+
if (ddx * ddx + ddy * ddy < sum * sum) {
|
|
430
|
+
blocked = true;
|
|
431
|
+
break;
|
|
432
|
+
}
|
|
433
|
+
}
|
|
434
|
+
if (blocked)
|
|
435
|
+
break;
|
|
436
|
+
}
|
|
437
|
+
if (blocked)
|
|
438
|
+
continue;
|
|
439
|
+
const bucket = buckets.get(key);
|
|
440
|
+
if (bucket)
|
|
441
|
+
bucket.push(k);
|
|
442
|
+
else
|
|
443
|
+
buckets.set(key, [k]);
|
|
444
|
+
winners[pointRows[k]] = 1;
|
|
445
|
+
}
|
|
446
|
+
this.nextPoint = end;
|
|
447
|
+
if (end < 0)
|
|
448
|
+
this.finished = true;
|
|
449
|
+
}
|
|
450
|
+
}
|
|
451
|
+
/**
|
|
452
|
+
* Picks the winners among the rows (a `CollisionSelection` run in one go)
|
|
453
|
+
*
|
|
454
|
+
* The result also marks what is out of scope for the thinning (anything but a Point, and so on).
|
|
455
|
+
* A row that is not drawn (`skip`) becomes neither a winner nor an obstacle.
|
|
456
|
+
*
|
|
457
|
+
* @param radiusPx The footprint radius of a row (px, including the outline and the margin)
|
|
458
|
+
* @returns 1 for each row that may be drawn, 0 otherwise
|
|
459
|
+
*
|
|
460
|
+
* @internal
|
|
461
|
+
*/
|
|
462
|
+
export function selectCollisionWinnerRows(rows, band, radiusPx) {
|
|
463
|
+
const selection = new CollisionSelection(rows, band, radiusPx);
|
|
464
|
+
selection.step(Number.POSITIVE_INFINITY, () => 0);
|
|
465
|
+
return selection.result();
|
|
466
|
+
}
|
|
467
|
+
/**
|
|
468
|
+
* Picks the set of winners among features (`selectCollisionWinnerRows` over an array)
|
|
469
|
+
*
|
|
470
|
+
* @returns The set of ids of the features that may be drawn
|
|
471
|
+
*
|
|
472
|
+
* @internal
|
|
473
|
+
*/
|
|
474
|
+
export function selectCollisionWinners(input) {
|
|
475
|
+
const { features, band, radiusPx } = input;
|
|
476
|
+
const rows = {
|
|
477
|
+
length: features.length,
|
|
478
|
+
thinningRole: (row) => featureThinningRole(features[row]),
|
|
479
|
+
pointOf: (row) => features[row].coordinates,
|
|
480
|
+
styleRadiusOf: (row) => features[row].style?.pointRadius,
|
|
481
|
+
idOf: (row) => features[row].id,
|
|
482
|
+
};
|
|
483
|
+
const mask = selectCollisionWinnerRows(rows, band, (row) => radiusPx(features[row]));
|
|
484
|
+
const winners = new Set();
|
|
485
|
+
for (let row = 0; row < mask.length; row++) {
|
|
486
|
+
if (mask[row] === 1)
|
|
487
|
+
winners.add(features[row].id);
|
|
488
|
+
}
|
|
489
|
+
return winners;
|
|
490
|
+
}
|
|
491
|
+
/**
|
|
492
|
+
* What a feature is to the collision thinning
|
|
493
|
+
*
|
|
494
|
+
* @internal
|
|
495
|
+
*/
|
|
496
|
+
export function featureThinningRole(feature) {
|
|
497
|
+
if (!feature.visible)
|
|
498
|
+
return 'skip';
|
|
499
|
+
if (feature.type !== 'Point')
|
|
500
|
+
return 'winner';
|
|
501
|
+
const coord = feature.coordinates;
|
|
502
|
+
if (!Array.isArray(coord) || typeof coord[0] !== 'number' || typeof coord[1] !== 'number') {
|
|
503
|
+
return 'winner';
|
|
504
|
+
}
|
|
505
|
+
return 'point';
|
|
506
|
+
}
|
|
507
|
+
/**
|
|
508
|
+
* Prepares the grid
|
|
509
|
+
*
|
|
510
|
+
* The cell side is "the diameter of the largest footprint of that band". Two colliding points
|
|
511
|
+
* always fall within a displacement of one cell, so looking only at its own cell and the 8 cells
|
|
512
|
+
* around it misses nothing.
|
|
513
|
+
*
|
|
514
|
+
* The key is an integer built from the row and the column, but when the range is so wide that it
|
|
515
|
+
* would leave the safe integers, the cells are made coarser (this only adds candidates; the
|
|
516
|
+
* decision uses the real distance, so the result does not change).
|
|
517
|
+
*/
|
|
518
|
+
function createHashGrid(xs, ys, count, maxRadius) {
|
|
519
|
+
let minX = Number.POSITIVE_INFINITY;
|
|
520
|
+
let minY = Number.POSITIVE_INFINITY;
|
|
521
|
+
let maxX = Number.NEGATIVE_INFINITY;
|
|
522
|
+
let maxY = Number.NEGATIVE_INFINITY;
|
|
523
|
+
for (let i = 0; i < count; i++) {
|
|
524
|
+
if (xs[i] < minX)
|
|
525
|
+
minX = xs[i];
|
|
526
|
+
if (xs[i] > maxX)
|
|
527
|
+
maxX = xs[i];
|
|
528
|
+
if (ys[i] < minY)
|
|
529
|
+
minY = ys[i];
|
|
530
|
+
if (ys[i] > maxY)
|
|
531
|
+
maxY = ys[i];
|
|
532
|
+
}
|
|
533
|
+
let cell = maxRadius * 2;
|
|
534
|
+
let cols = Math.floor((maxX - minX) / cell) + 1;
|
|
535
|
+
let rows = Math.floor((maxY - minY) / cell) + 1;
|
|
536
|
+
// Make them coarser until they fit in the safe integers even with the room to subtract one
|
|
537
|
+
// surrounding cell (+2)
|
|
538
|
+
while ((cols + 2) * (rows + 2) > Number.MAX_SAFE_INTEGER) {
|
|
539
|
+
cell *= 2;
|
|
540
|
+
cols = Math.floor((maxX - minX) / cell) + 1;
|
|
541
|
+
rows = Math.floor((maxY - minY) / cell) + 1;
|
|
542
|
+
}
|
|
543
|
+
const stride = cols + 2;
|
|
544
|
+
return {
|
|
545
|
+
cell,
|
|
546
|
+
minX,
|
|
547
|
+
minY,
|
|
548
|
+
stride,
|
|
549
|
+
buckets: new Map(),
|
|
550
|
+
// Its own cell is looked at first, so that the denser it is, the sooner it is rejected
|
|
551
|
+
keyDeltas: [0, -1, 1, -stride, stride, -stride - 1, -stride + 1, stride - 1, stride + 1],
|
|
552
|
+
};
|
|
553
|
+
}
|
|
554
|
+
/**
|
|
555
|
+
* The collision thinning state of one dataset
|
|
556
|
+
*
|
|
557
|
+
* It holds the settings and the rows that are drawn (`mask`). There is one way to bring the
|
|
558
|
+
* drawn rows up to date, `sync(zoom)`: it picks the winners of the band of `zoom` for the current
|
|
559
|
+
* contents, style and settings, and does nothing when they are already those. The winners of
|
|
560
|
+
* each band are kept for the current contents and style, so going back to a band costs nothing,
|
|
561
|
+
* and the bands next to the current one can be picked ahead in slices while the page is idle
|
|
562
|
+
* (`prefetch`). Every change of the drawn rows advances `revision`.
|
|
563
|
+
*
|
|
564
|
+
* @internal
|
|
565
|
+
*/
|
|
566
|
+
export class CollisionThinningState {
|
|
567
|
+
constructor(host, options) {
|
|
568
|
+
Object.defineProperty(this, "host", {
|
|
569
|
+
enumerable: true,
|
|
570
|
+
configurable: true,
|
|
571
|
+
writable: true,
|
|
572
|
+
value: host
|
|
573
|
+
});
|
|
574
|
+
/** Settings of the collision thinning (null = not set = disabled) */
|
|
575
|
+
Object.defineProperty(this, "settings", {
|
|
576
|
+
enumerable: true,
|
|
577
|
+
configurable: true,
|
|
578
|
+
writable: true,
|
|
579
|
+
value: void 0
|
|
580
|
+
});
|
|
581
|
+
/** What the drawn rows were picked for (null = nothing picked: every row is drawn) */
|
|
582
|
+
Object.defineProperty(this, "picked", {
|
|
583
|
+
enumerable: true,
|
|
584
|
+
configurable: true,
|
|
585
|
+
writable: true,
|
|
586
|
+
value: null
|
|
587
|
+
});
|
|
588
|
+
/** The drawn rows */
|
|
589
|
+
Object.defineProperty(this, "drawnRows", {
|
|
590
|
+
enumerable: true,
|
|
591
|
+
configurable: true,
|
|
592
|
+
writable: true,
|
|
593
|
+
value: null
|
|
594
|
+
});
|
|
595
|
+
/** The number of rows that are drawn */
|
|
596
|
+
Object.defineProperty(this, "drawnCount", {
|
|
597
|
+
enumerable: true,
|
|
598
|
+
configurable: true,
|
|
599
|
+
writable: true,
|
|
600
|
+
value: 0
|
|
601
|
+
});
|
|
602
|
+
/** The ids of the drawn rows, built on the first request */
|
|
603
|
+
Object.defineProperty(this, "drawnIds", {
|
|
604
|
+
enumerable: true,
|
|
605
|
+
configurable: true,
|
|
606
|
+
writable: true,
|
|
607
|
+
value: null
|
|
608
|
+
});
|
|
609
|
+
/** Advances whenever the drawn rows change */
|
|
610
|
+
Object.defineProperty(this, "drawnRevision", {
|
|
611
|
+
enumerable: true,
|
|
612
|
+
configurable: true,
|
|
613
|
+
writable: true,
|
|
614
|
+
value: 0
|
|
615
|
+
});
|
|
616
|
+
/**
|
|
617
|
+
* The winners already picked for each band, for the key in `cacheKey` (null = every row).
|
|
618
|
+
* Going back to a band seen before costs nothing
|
|
619
|
+
*/
|
|
620
|
+
Object.defineProperty(this, "bandCache", {
|
|
621
|
+
enumerable: true,
|
|
622
|
+
configurable: true,
|
|
623
|
+
writable: true,
|
|
624
|
+
value: new Map()
|
|
625
|
+
});
|
|
626
|
+
/** The contents and the style the bands in `bandCache` were picked for */
|
|
627
|
+
Object.defineProperty(this, "cacheKey", {
|
|
628
|
+
enumerable: true,
|
|
629
|
+
configurable: true,
|
|
630
|
+
writable: true,
|
|
631
|
+
value: null
|
|
632
|
+
});
|
|
633
|
+
/** The band being picked ahead, a slice at a time (null = none) */
|
|
634
|
+
Object.defineProperty(this, "ahead", {
|
|
635
|
+
enumerable: true,
|
|
636
|
+
configurable: true,
|
|
637
|
+
writable: true,
|
|
638
|
+
value: null
|
|
639
|
+
});
|
|
640
|
+
this.settings = options ? resolveCollisionThinning(options) : null;
|
|
641
|
+
}
|
|
642
|
+
/** The settings (null when not set) */
|
|
643
|
+
get options() {
|
|
644
|
+
return this.settings;
|
|
645
|
+
}
|
|
646
|
+
/** Whether the thinning is enabled */
|
|
647
|
+
get enabled() {
|
|
648
|
+
return this.settings?.enabled ?? false;
|
|
649
|
+
}
|
|
650
|
+
/** Whether some rows are thinned away (false: every row is drawn) */
|
|
651
|
+
get active() {
|
|
652
|
+
return this.drawnRows !== null;
|
|
653
|
+
}
|
|
654
|
+
/** The drawn rows (null = every row) */
|
|
655
|
+
get mask() {
|
|
656
|
+
return this.drawnRows;
|
|
657
|
+
}
|
|
658
|
+
/** A number that advances whenever the drawn rows change */
|
|
659
|
+
get revision() {
|
|
660
|
+
return this.drawnRevision;
|
|
661
|
+
}
|
|
662
|
+
/** The ids of the rows that are drawn (null = every row) */
|
|
663
|
+
get drawable() {
|
|
664
|
+
const mask = this.drawnRows;
|
|
665
|
+
if (!mask)
|
|
666
|
+
return null;
|
|
667
|
+
if (!this.drawnIds) {
|
|
668
|
+
const rows = this.host.rows();
|
|
669
|
+
const ids = new Set();
|
|
670
|
+
for (let row = 0; row < mask.length; row++) {
|
|
671
|
+
if (mask[row] === 1)
|
|
672
|
+
ids.add(rows.idOf(row));
|
|
673
|
+
}
|
|
674
|
+
this.drawnIds = ids;
|
|
675
|
+
}
|
|
676
|
+
return this.drawnIds;
|
|
677
|
+
}
|
|
678
|
+
/**
|
|
679
|
+
* Replaces the settings and brings the drawn rows up to date for `zoom`
|
|
680
|
+
*
|
|
681
|
+
* @param zoom The zoom the band is decided from (with the pitch correction applied)
|
|
682
|
+
* @returns null when the settings are the same; otherwise whether the drawn rows changed
|
|
683
|
+
*/
|
|
684
|
+
setOptions(options, zoom) {
|
|
685
|
+
const next = options ? resolveCollisionThinning(options) : null;
|
|
686
|
+
if (sameCollisionThinning(this.settings, next))
|
|
687
|
+
return null;
|
|
688
|
+
this.settings = next;
|
|
689
|
+
this.picked = null;
|
|
690
|
+
this.forgetBands();
|
|
691
|
+
return { drawnChanged: this.sync(zoom) };
|
|
692
|
+
}
|
|
693
|
+
/** Whether a row is drawn (it survived the thinning, or nothing is thinned) */
|
|
694
|
+
isDrawnRow(row) {
|
|
695
|
+
return this.drawnRows === null || this.drawnRows[row] === 1;
|
|
696
|
+
}
|
|
697
|
+
/** The statistics of the thinning for `total` features */
|
|
698
|
+
stats(total) {
|
|
699
|
+
const thinning = this.drawnRows !== null;
|
|
700
|
+
return {
|
|
701
|
+
enabled: this.enabled,
|
|
702
|
+
active: thinning,
|
|
703
|
+
total,
|
|
704
|
+
visible: thinning ? this.drawnCount : total,
|
|
705
|
+
band: thinning ? (this.picked?.band ?? null) : null,
|
|
706
|
+
};
|
|
707
|
+
}
|
|
708
|
+
/**
|
|
709
|
+
* Brings the drawn rows up to date: the winners of the band of `zoom` for the current contents,
|
|
710
|
+
* style and settings (every row while the thinning is disabled)
|
|
711
|
+
*
|
|
712
|
+
* It is the only way the drawn rows change. Nothing is picked when they are already those; a
|
|
713
|
+
* band kept from before (or picked ahead) is reused, and a band being picked ahead is
|
|
714
|
+
* finished rather than started again.
|
|
715
|
+
*
|
|
716
|
+
* @param zoom The zoom the band is decided from (with the pitch correction applied)
|
|
717
|
+
* @returns Whether the drawn rows changed
|
|
718
|
+
*/
|
|
719
|
+
sync(zoom) {
|
|
720
|
+
const settings = this.settings;
|
|
721
|
+
if (!settings?.enabled) {
|
|
722
|
+
this.picked = null;
|
|
723
|
+
return this.setDrawn(null);
|
|
724
|
+
}
|
|
725
|
+
const key = this.currentKey();
|
|
726
|
+
const band = zoomBandFor(zoom);
|
|
727
|
+
const picked = this.picked;
|
|
728
|
+
if (picked && sameKey(picked, key) && picked.band === band)
|
|
729
|
+
return false;
|
|
730
|
+
if (!this.cacheKey || !sameKey(this.cacheKey, key)) {
|
|
731
|
+
this.forgetBands();
|
|
732
|
+
this.cacheKey = key;
|
|
733
|
+
}
|
|
734
|
+
let mask = this.bandCache.get(band);
|
|
735
|
+
if (mask === undefined)
|
|
736
|
+
mask = this.finishAhead(band, key) ?? this.pickWinners(band, settings);
|
|
737
|
+
this.keepBand(band, mask);
|
|
738
|
+
this.picked = { ...key, band };
|
|
739
|
+
return this.setDrawn(mask);
|
|
740
|
+
}
|
|
741
|
+
/**
|
|
742
|
+
* Picks the winners of the bands next to the current one ahead of time, until the deadline
|
|
743
|
+
*
|
|
744
|
+
* Called while the page is idle, so that crossing into one of those bands during a gesture
|
|
745
|
+
* costs no selection in the middle of a frame. The selection of a band is advanced in slices
|
|
746
|
+
* (`CollisionSelection`), so an idle period is never overrun by more than one slice.
|
|
747
|
+
*
|
|
748
|
+
* @returns Whether a band next to the current one is still missing
|
|
749
|
+
*/
|
|
750
|
+
prefetch(deadline, now) {
|
|
751
|
+
const settings = this.settings;
|
|
752
|
+
const picked = this.picked;
|
|
753
|
+
const key = this.currentKey();
|
|
754
|
+
if (!settings?.enabled || !picked || !sameKey(picked, key)) {
|
|
755
|
+
this.ahead = null;
|
|
756
|
+
return false;
|
|
757
|
+
}
|
|
758
|
+
for (;;) {
|
|
759
|
+
let ahead = this.ahead;
|
|
760
|
+
if (ahead && !sameKey(ahead, key))
|
|
761
|
+
ahead = null;
|
|
762
|
+
if (!ahead) {
|
|
763
|
+
const band = this.missingNeighbors(picked.band)[0];
|
|
764
|
+
if (band === undefined)
|
|
765
|
+
return false;
|
|
766
|
+
if (band >= settings.fullDisplayZoom) {
|
|
767
|
+
// Nothing is thinned there; there is nothing to pick
|
|
768
|
+
this.keepBand(band, null);
|
|
769
|
+
continue;
|
|
770
|
+
}
|
|
771
|
+
ahead = { ...key, band, selection: this.selectionFor(band, settings) };
|
|
772
|
+
this.ahead = ahead;
|
|
773
|
+
}
|
|
774
|
+
if (!ahead.selection.step(deadline, now))
|
|
775
|
+
return true;
|
|
776
|
+
this.keepBand(ahead.band, ahead.selection.result());
|
|
777
|
+
this.ahead = null;
|
|
778
|
+
if (now() >= deadline)
|
|
779
|
+
return this.missingNeighbors(picked.band).length > 0;
|
|
780
|
+
}
|
|
781
|
+
}
|
|
782
|
+
/** Forgets the winners (the settings are kept) */
|
|
783
|
+
clear() {
|
|
784
|
+
this.picked = null;
|
|
785
|
+
this.setDrawn(null);
|
|
786
|
+
this.forgetBands();
|
|
787
|
+
}
|
|
788
|
+
/**
|
|
789
|
+
* The bands near `band` (two on each side) whose winners are not kept yet, the nearest and the
|
|
790
|
+
* lower first. A fast zoom gesture crosses two bands before the page is idle again
|
|
791
|
+
*/
|
|
792
|
+
missingNeighbors(band) {
|
|
793
|
+
const missing = [];
|
|
794
|
+
for (const next of [band - 1, band + 1, band - 2, band + 2]) {
|
|
795
|
+
if (next < 0 || next > MAX_ZOOM_BAND)
|
|
796
|
+
continue;
|
|
797
|
+
const kept = this.bandCache.get(next);
|
|
798
|
+
// A band still kept is marked as used, so that picking the missing ones does not drop it
|
|
799
|
+
if (kept === undefined)
|
|
800
|
+
missing.push(next);
|
|
801
|
+
else
|
|
802
|
+
this.keepBand(next, kept);
|
|
803
|
+
}
|
|
804
|
+
return missing;
|
|
805
|
+
}
|
|
806
|
+
/** The contents and the style of now */
|
|
807
|
+
currentKey() {
|
|
808
|
+
return { generation: this.host.featuresGeneration(), revision: this.host.styleRevision() };
|
|
809
|
+
}
|
|
810
|
+
/** Discards the winners kept per band and the band being picked ahead */
|
|
811
|
+
forgetBands() {
|
|
812
|
+
this.bandCache.clear();
|
|
813
|
+
this.cacheKey = null;
|
|
814
|
+
this.ahead = null;
|
|
815
|
+
}
|
|
816
|
+
/**
|
|
817
|
+
* Replaces the drawn rows (null = every row)
|
|
818
|
+
*
|
|
819
|
+
* A mask with the same rows as the current one keeps the current one, so nothing that depends
|
|
820
|
+
* on the drawn rows is redone.
|
|
821
|
+
*
|
|
822
|
+
* @returns Whether the drawn rows changed
|
|
823
|
+
*/
|
|
824
|
+
setDrawn(mask) {
|
|
825
|
+
if (sameMask(this.drawnRows, mask))
|
|
826
|
+
return false;
|
|
827
|
+
this.drawnRows = mask;
|
|
828
|
+
this.drawnIds = null;
|
|
829
|
+
let count = 0;
|
|
830
|
+
if (mask)
|
|
831
|
+
for (let row = 0; row < mask.length; row++)
|
|
832
|
+
count += mask[row];
|
|
833
|
+
this.drawnCount = count;
|
|
834
|
+
this.drawnRevision++;
|
|
835
|
+
return true;
|
|
836
|
+
}
|
|
837
|
+
/**
|
|
838
|
+
* Keeps the winners of a band as the most recently used one (the least recently used band is
|
|
839
|
+
* dropped first; a Map keeps the order of insertion)
|
|
840
|
+
*/
|
|
841
|
+
keepBand(band, mask) {
|
|
842
|
+
this.bandCache.delete(band);
|
|
843
|
+
if (this.bandCache.size >= MAX_CACHED_BANDS) {
|
|
844
|
+
const oldest = this.bandCache.keys().next().value;
|
|
845
|
+
if (oldest !== undefined)
|
|
846
|
+
this.bandCache.delete(oldest);
|
|
847
|
+
}
|
|
848
|
+
this.bandCache.set(band, mask);
|
|
849
|
+
}
|
|
850
|
+
/** Finishes the band being picked ahead when it is `band` (undefined otherwise) */
|
|
851
|
+
finishAhead(band, key) {
|
|
852
|
+
const ahead = this.ahead;
|
|
853
|
+
if (!ahead || ahead.band !== band || !sameKey(ahead, key))
|
|
854
|
+
return undefined;
|
|
855
|
+
this.ahead = null;
|
|
856
|
+
ahead.selection.step(Number.POSITIVE_INFINITY, () => 0);
|
|
857
|
+
return ahead.selection.result();
|
|
858
|
+
}
|
|
859
|
+
/** The winners of the band `band` (null = every row) */
|
|
860
|
+
pickWinners(band, settings) {
|
|
861
|
+
if (band >= settings.fullDisplayZoom)
|
|
862
|
+
return null;
|
|
863
|
+
const selection = this.selectionFor(band, settings);
|
|
864
|
+
selection.step(Number.POSITIVE_INFINITY, () => 0);
|
|
865
|
+
return selection.result();
|
|
866
|
+
}
|
|
867
|
+
/** A selection of the winners of the band `band`, not started yet */
|
|
868
|
+
selectionFor(band, settings) {
|
|
869
|
+
const rows = this.host.rows();
|
|
870
|
+
const footprint = this.host.footprintPx(band, settings.marginPx);
|
|
871
|
+
return new CollisionSelection(rows, band, (row) => footprint(rows.styleRadiusOf(row)));
|
|
872
|
+
}
|
|
873
|
+
}
|
|
874
|
+
/** Whether two keys name the same contents and style */
|
|
875
|
+
function sameKey(a, b) {
|
|
876
|
+
return a.generation === b.generation && a.revision === b.revision;
|
|
877
|
+
}
|
|
878
|
+
/**
|
|
879
|
+
* Whether two masks of drawn rows are the same (null = every row)
|
|
880
|
+
*
|
|
881
|
+
* @internal
|
|
882
|
+
*/
|
|
883
|
+
export function sameMask(a, b) {
|
|
884
|
+
if (a === b)
|
|
885
|
+
return true;
|
|
886
|
+
if (a === null || b === null)
|
|
887
|
+
return false;
|
|
888
|
+
if (a.length !== b.length)
|
|
889
|
+
return false;
|
|
890
|
+
for (let i = 0; i < a.length; i++) {
|
|
891
|
+
if (a[i] !== b[i])
|
|
892
|
+
return false;
|
|
893
|
+
}
|
|
894
|
+
return true;
|
|
895
|
+
}
|
|
896
|
+
/**
|
|
897
|
+
* Whether the rows `rows` are drawn alike in two masks (null = every row)
|
|
898
|
+
*
|
|
899
|
+
* @internal
|
|
900
|
+
*/
|
|
901
|
+
export function sameRowsInMasks(a, b, rows) {
|
|
902
|
+
if (a === b)
|
|
903
|
+
return true;
|
|
904
|
+
for (let i = 0; i < rows.length; i++) {
|
|
905
|
+
const row = rows[i];
|
|
906
|
+
if ((a === null ? 1 : a[row]) !== (b === null ? 1 : b[row]))
|
|
907
|
+
return false;
|
|
908
|
+
}
|
|
909
|
+
return true;
|
|
910
|
+
}
|