@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,960 @@
|
|
|
1
|
+
// SPDX-FileCopyrightText: 2026 SAKAIDA Atsushi
|
|
2
|
+
// SPDX-License-Identifier: AGPL-3.0-only AND ISC
|
|
3
|
+
/**
|
|
4
|
+
* The number of ear clipping loop iterations run in a single step
|
|
5
|
+
*
|
|
6
|
+
* The caller checks the clock at this granularity, so this determines "how far the
|
|
7
|
+
* budget can be overrun". The measurements on a million-vertex water body are as
|
|
8
|
+
* follows.
|
|
9
|
+
*
|
|
10
|
+
* | slice | average per step | p99 | max | total CPU of the triangulation |
|
|
11
|
+
* | --- | --- | --- | --- | --- |
|
|
12
|
+
* | 16 | 0.14ms | 5.1ms | 23ms | 15.0s |
|
|
13
|
+
* | 32 | 0.28ms | 10.3ms | 35ms | 15.3s |
|
|
14
|
+
* | 64 | 0.52ms | 19.5ms | 55ms | 14.2s |
|
|
15
|
+
*
|
|
16
|
+
* Since the total hardly changes even when the slice is made finer (the cost of slicing
|
|
17
|
+
* is negligible), 16 is adopted because it keeps within the budget. A long step appears
|
|
18
|
+
* rarely because the recovery performed when no more ears can be found (the full sweep
|
|
19
|
+
* of filterPoints) runs within a single iteration.
|
|
20
|
+
*/
|
|
21
|
+
export const EARCUT_SLICE_ITERATIONS = 16;
|
|
22
|
+
// === The following is upstream's mutable scratch state (alive only within one step) ===
|
|
23
|
+
/**
|
|
24
|
+
* The "single-vertex holes" (steiner points) that filterPoints stores
|
|
25
|
+
*
|
|
26
|
+
* Because the triangulation stops partway through, this set has to be swapped per job
|
|
27
|
+
* (it is swapped to the one of the job in question at the entrance of step).
|
|
28
|
+
*/
|
|
29
|
+
let steiners = new Set();
|
|
30
|
+
/** The empty set steiners points at while no job is running (avoids allocating each time) */
|
|
31
|
+
const EMPTY_STEINERS = new Set();
|
|
32
|
+
// set by filterPoints whenever it removes at least one node; read by the ear clipping
|
|
33
|
+
// loop's stall handler to decide whether another clip pass is worth attempting
|
|
34
|
+
let filteredOut = false;
|
|
35
|
+
// true only while eliminateHoles merges holes, so removeNode keeps the block index live
|
|
36
|
+
let indexActive = false;
|
|
37
|
+
/**
|
|
38
|
+
* Time-sliceable earcut
|
|
39
|
+
*
|
|
40
|
+
* If `step()` is called repeatedly until completion (true), `triangles` ends up holding
|
|
41
|
+
* the same index sequence as the `earcut(data, holeIndices, dim)` dependency.
|
|
42
|
+
*/
|
|
43
|
+
export class SlicedEarcut {
|
|
44
|
+
constructor(data, holeIndices, dim = 2) {
|
|
45
|
+
Object.defineProperty(this, "data", {
|
|
46
|
+
enumerable: true,
|
|
47
|
+
configurable: true,
|
|
48
|
+
writable: true,
|
|
49
|
+
value: void 0
|
|
50
|
+
});
|
|
51
|
+
Object.defineProperty(this, "holeIndices", {
|
|
52
|
+
enumerable: true,
|
|
53
|
+
configurable: true,
|
|
54
|
+
writable: true,
|
|
55
|
+
value: void 0
|
|
56
|
+
});
|
|
57
|
+
Object.defineProperty(this, "dim", {
|
|
58
|
+
enumerable: true,
|
|
59
|
+
configurable: true,
|
|
60
|
+
writable: true,
|
|
61
|
+
value: void 0
|
|
62
|
+
});
|
|
63
|
+
/** The triangulation result (partial progress until it finishes) */
|
|
64
|
+
Object.defineProperty(this, "triangles", {
|
|
65
|
+
enumerable: true,
|
|
66
|
+
configurable: true,
|
|
67
|
+
writable: true,
|
|
68
|
+
value: []
|
|
69
|
+
});
|
|
70
|
+
Object.defineProperty(this, "phase", {
|
|
71
|
+
enumerable: true,
|
|
72
|
+
configurable: true,
|
|
73
|
+
writable: true,
|
|
74
|
+
value: 'setup'
|
|
75
|
+
});
|
|
76
|
+
// State of the ear clipping loop (carried over across steps)
|
|
77
|
+
Object.defineProperty(this, "ear", {
|
|
78
|
+
enumerable: true,
|
|
79
|
+
configurable: true,
|
|
80
|
+
writable: true,
|
|
81
|
+
value: null
|
|
82
|
+
});
|
|
83
|
+
Object.defineProperty(this, "stop", {
|
|
84
|
+
enumerable: true,
|
|
85
|
+
configurable: true,
|
|
86
|
+
writable: true,
|
|
87
|
+
value: null
|
|
88
|
+
});
|
|
89
|
+
Object.defineProperty(this, "cured", {
|
|
90
|
+
enumerable: true,
|
|
91
|
+
configurable: true,
|
|
92
|
+
writable: true,
|
|
93
|
+
value: false
|
|
94
|
+
});
|
|
95
|
+
Object.defineProperty(this, "minX", {
|
|
96
|
+
enumerable: true,
|
|
97
|
+
configurable: true,
|
|
98
|
+
writable: true,
|
|
99
|
+
value: 0
|
|
100
|
+
});
|
|
101
|
+
Object.defineProperty(this, "minY", {
|
|
102
|
+
enumerable: true,
|
|
103
|
+
configurable: true,
|
|
104
|
+
writable: true,
|
|
105
|
+
value: 0
|
|
106
|
+
});
|
|
107
|
+
Object.defineProperty(this, "invSize", {
|
|
108
|
+
enumerable: true,
|
|
109
|
+
configurable: true,
|
|
110
|
+
writable: true,
|
|
111
|
+
value: 0
|
|
112
|
+
});
|
|
113
|
+
/** The steiner points of this job (swapped into the module variable at step's entrance) */
|
|
114
|
+
Object.defineProperty(this, "ownSteiners", {
|
|
115
|
+
enumerable: true,
|
|
116
|
+
configurable: true,
|
|
117
|
+
writable: true,
|
|
118
|
+
value: new Set()
|
|
119
|
+
});
|
|
120
|
+
/** The filteredOut of this job (same as above) */
|
|
121
|
+
Object.defineProperty(this, "ownFilteredOut", {
|
|
122
|
+
enumerable: true,
|
|
123
|
+
configurable: true,
|
|
124
|
+
writable: true,
|
|
125
|
+
value: false
|
|
126
|
+
});
|
|
127
|
+
this.data = data;
|
|
128
|
+
this.holeIndices = holeIndices ?? null;
|
|
129
|
+
this.dim = dim;
|
|
130
|
+
}
|
|
131
|
+
/** Whether it has finished */
|
|
132
|
+
get done() {
|
|
133
|
+
return this.phase === 'done';
|
|
134
|
+
}
|
|
135
|
+
/**
|
|
136
|
+
* Advances the triangulation a little
|
|
137
|
+
*
|
|
138
|
+
* @param iterations The upper bound on ear clipping loop iterations (the default slice
|
|
139
|
+
* when omitted)
|
|
140
|
+
* @returns true once it has finished
|
|
141
|
+
*/
|
|
142
|
+
step(iterations = EARCUT_SLICE_ITERATIONS) {
|
|
143
|
+
if (this.done)
|
|
144
|
+
return true;
|
|
145
|
+
// Swap the module scratch state to this job's (so that even if another job runs
|
|
146
|
+
// while this triangulation is partway through, they do not break each other's state)
|
|
147
|
+
steiners = this.ownSteiners;
|
|
148
|
+
filteredOut = this.ownFilteredOut;
|
|
149
|
+
try {
|
|
150
|
+
if (this.phase === 'setup') {
|
|
151
|
+
this.runSetup();
|
|
152
|
+
}
|
|
153
|
+
if (this.phase === 'clip') {
|
|
154
|
+
this.runClip(iterations);
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
finally {
|
|
158
|
+
this.ownFilteredOut = filteredOut;
|
|
159
|
+
steiners = EMPTY_STEINERS;
|
|
160
|
+
}
|
|
161
|
+
return this.done;
|
|
162
|
+
}
|
|
163
|
+
/**
|
|
164
|
+
* Preprocessing (building the linked list, bridging the holes, bbox, z-order indexing)
|
|
165
|
+
*
|
|
166
|
+
* This part is not sliced. It takes only tens of milliseconds even at a million
|
|
167
|
+
* vertices, so fidelity to the port is preferred over the value of slicing it.
|
|
168
|
+
*/
|
|
169
|
+
runSetup() {
|
|
170
|
+
const data = this.data;
|
|
171
|
+
const holeIndices = this.holeIndices;
|
|
172
|
+
const dim = this.dim;
|
|
173
|
+
const hasHoles = holeIndices !== null && holeIndices.length > 0;
|
|
174
|
+
const outerLen = hasHoles ? holeIndices[0] * dim : data.length;
|
|
175
|
+
if (steiners.size)
|
|
176
|
+
steiners.clear();
|
|
177
|
+
let outerNode = linkedList(data, 0, outerLen, dim, true);
|
|
178
|
+
if (!outerNode || outerNode.next === outerNode.prev) {
|
|
179
|
+
this.phase = 'done';
|
|
180
|
+
return;
|
|
181
|
+
}
|
|
182
|
+
if (hasHoles)
|
|
183
|
+
outerNode = eliminateHoles(data, holeIndices, outerNode, dim);
|
|
184
|
+
// if the shape is not too simple, we'll use z-order curve hash later; calculate polygon bbox
|
|
185
|
+
if (data.length > 80 * dim) {
|
|
186
|
+
let minX = data[0];
|
|
187
|
+
let minY = data[1];
|
|
188
|
+
let maxX = minX;
|
|
189
|
+
let maxY = minY;
|
|
190
|
+
for (let i = dim; i < outerLen; i += dim) {
|
|
191
|
+
const x = data[i];
|
|
192
|
+
const y = data[i + 1];
|
|
193
|
+
if (x < minX)
|
|
194
|
+
minX = x;
|
|
195
|
+
if (y < minY)
|
|
196
|
+
minY = y;
|
|
197
|
+
if (x > maxX)
|
|
198
|
+
maxX = x;
|
|
199
|
+
if (y > maxY)
|
|
200
|
+
maxY = y;
|
|
201
|
+
}
|
|
202
|
+
// minX, minY and invSize are later used to transform coords into integers for z-order
|
|
203
|
+
let invSize = Math.max(maxX - minX, maxY - minY);
|
|
204
|
+
invSize = invSize !== 0 ? 32767 / invSize : 0;
|
|
205
|
+
this.minX = minX;
|
|
206
|
+
this.minY = minY;
|
|
207
|
+
this.invSize = invSize;
|
|
208
|
+
}
|
|
209
|
+
// interlink polygon nodes in z-order
|
|
210
|
+
if (this.invSize)
|
|
211
|
+
indexCurve(outerNode, this.minX, this.minY, this.invSize);
|
|
212
|
+
this.ear = outerNode;
|
|
213
|
+
this.stop = outerNode;
|
|
214
|
+
this.cured = false;
|
|
215
|
+
this.phase = 'clip';
|
|
216
|
+
}
|
|
217
|
+
/**
|
|
218
|
+
* The main ear clipping loop (identical to upstream's earcutLinked; breaks out on the
|
|
219
|
+
* iteration count)
|
|
220
|
+
*
|
|
221
|
+
* splitEarcut, the last resort, is recursive, so it is not sliced (it is a path that
|
|
222
|
+
* runs only once against the remaining polygon and is not taken for ordinary inputs).
|
|
223
|
+
*/
|
|
224
|
+
runClip(iterations) {
|
|
225
|
+
const { triangles, minX, minY, invSize } = this;
|
|
226
|
+
let ear = this.ear;
|
|
227
|
+
let stop = this.stop;
|
|
228
|
+
let cured = this.cured;
|
|
229
|
+
let remaining = iterations;
|
|
230
|
+
// iterate through ears, slicing them one by one
|
|
231
|
+
while (ear.prev !== ear.next) {
|
|
232
|
+
if (remaining-- <= 0) {
|
|
233
|
+
this.ear = ear;
|
|
234
|
+
this.stop = stop;
|
|
235
|
+
this.cured = cured;
|
|
236
|
+
return;
|
|
237
|
+
}
|
|
238
|
+
const prev = ear.prev;
|
|
239
|
+
const next = ear.next;
|
|
240
|
+
if (area(prev, ear, next) < 0 &&
|
|
241
|
+
(invSize ? isEarHashed(ear, minX, minY, invSize) : isEar(ear))) {
|
|
242
|
+
triangles.push(prev.i, ear.i, next.i); // cut off the triangle
|
|
243
|
+
removeNode(ear);
|
|
244
|
+
ear = next;
|
|
245
|
+
stop = next;
|
|
246
|
+
continue;
|
|
247
|
+
}
|
|
248
|
+
ear = next;
|
|
249
|
+
// if we looped through the whole remaining polygon and can't find any more ears
|
|
250
|
+
if (ear === stop) {
|
|
251
|
+
// try filtering collinear/coincident points and slicing again — repeat as long as
|
|
252
|
+
// filtering actually removes nodes, since each removal can expose new ears
|
|
253
|
+
filteredOut = false;
|
|
254
|
+
ear = filterPoints(ear);
|
|
255
|
+
if (filteredOut) {
|
|
256
|
+
stop = ear;
|
|
257
|
+
continue;
|
|
258
|
+
}
|
|
259
|
+
// filtering is exhausted: cure small local self-intersections once, then retry
|
|
260
|
+
if (!cured) {
|
|
261
|
+
ear = cureLocalIntersections(ear, triangles);
|
|
262
|
+
stop = ear;
|
|
263
|
+
cured = true;
|
|
264
|
+
continue;
|
|
265
|
+
}
|
|
266
|
+
// as a last resort, try splitting the remaining polygon into two
|
|
267
|
+
splitEarcut(ear, triangles, minX, minY, invSize);
|
|
268
|
+
break;
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
this.ear = ear;
|
|
272
|
+
this.stop = stop;
|
|
273
|
+
this.cured = cured;
|
|
274
|
+
this.phase = 'done';
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
// === The following is the port of upstream earcut (the algorithm is unmodified) ===
|
|
278
|
+
// create a circular doubly linked list from polygon points in the specified winding order
|
|
279
|
+
function linkedList(data, start, end, dim, clockwise) {
|
|
280
|
+
let last = null;
|
|
281
|
+
if (clockwise === signedArea(data, start, end, dim) > 0) {
|
|
282
|
+
for (let i = start; i < end; i += dim)
|
|
283
|
+
last = insertNode((i / dim) | 0, data[i], data[i + 1], last);
|
|
284
|
+
}
|
|
285
|
+
else {
|
|
286
|
+
for (let i = end - dim; i >= start; i -= dim)
|
|
287
|
+
last = insertNode((i / dim) | 0, data[i], data[i + 1], last);
|
|
288
|
+
}
|
|
289
|
+
if (last && equals(last, last.next)) {
|
|
290
|
+
removeNode(last);
|
|
291
|
+
last = last.next;
|
|
292
|
+
}
|
|
293
|
+
return last;
|
|
294
|
+
}
|
|
295
|
+
// Remove collinear or coincident points; removability depends only on a node's immediate
|
|
296
|
+
// neighbors, so we sweep forward and re-check the predecessor after each removal. With no `end`
|
|
297
|
+
// we sweep the whole ring, lapping until nothing is removable (the fixpoint the clipper needs).
|
|
298
|
+
// With an explicit `end` we heal only the dirty window around a bridge/diagonal cut, stopping at
|
|
299
|
+
// `end` rather than lapping — O(window) instead of O(ring).
|
|
300
|
+
function filterPoints(start, end = start) {
|
|
301
|
+
const full = end === start;
|
|
302
|
+
let p = start;
|
|
303
|
+
let again;
|
|
304
|
+
do {
|
|
305
|
+
again = false;
|
|
306
|
+
if (p !== p.next &&
|
|
307
|
+
(steiners.size === 0 || !steiners.has(p)) &&
|
|
308
|
+
(equals(p, p.next) || area(p.prev, p, p.next) === 0)) {
|
|
309
|
+
if (full || p === end)
|
|
310
|
+
end = p.prev; // pull the stop bound back past the removal
|
|
311
|
+
filteredOut = true;
|
|
312
|
+
removeNode(p);
|
|
313
|
+
p = p.prev; // re-check the predecessor
|
|
314
|
+
again = true;
|
|
315
|
+
}
|
|
316
|
+
else if (full || p !== end) {
|
|
317
|
+
p = p.next;
|
|
318
|
+
again = !full; // local heal: keep looping until the sweep reaches end
|
|
319
|
+
}
|
|
320
|
+
} while (again || p !== end);
|
|
321
|
+
return end;
|
|
322
|
+
}
|
|
323
|
+
// main ear slicing loop which triangulates a polygon (given as a linked list)
|
|
324
|
+
//
|
|
325
|
+
// This has the same content as SlicedEarcut.runClip. It is left as upstream for the path
|
|
326
|
+
// that runs through in one go without slicing (the recursion of splitEarcut).
|
|
327
|
+
function earcutLinked(ear, triangles, minX, minY, invSize) {
|
|
328
|
+
// interlink polygon nodes in z-order
|
|
329
|
+
if (invSize)
|
|
330
|
+
indexCurve(ear, minX, minY, invSize);
|
|
331
|
+
let stop = ear;
|
|
332
|
+
let cured = false;
|
|
333
|
+
// iterate through ears, slicing them one by one
|
|
334
|
+
while (ear.prev !== ear.next) {
|
|
335
|
+
const prev = ear.prev;
|
|
336
|
+
const next = ear.next;
|
|
337
|
+
if (area(prev, ear, next) < 0 &&
|
|
338
|
+
(invSize ? isEarHashed(ear, minX, minY, invSize) : isEar(ear))) {
|
|
339
|
+
triangles.push(prev.i, ear.i, next.i); // cut off the triangle
|
|
340
|
+
removeNode(ear);
|
|
341
|
+
ear = next;
|
|
342
|
+
stop = next;
|
|
343
|
+
continue;
|
|
344
|
+
}
|
|
345
|
+
ear = next;
|
|
346
|
+
// if we looped through the whole remaining polygon and can't find any more ears
|
|
347
|
+
if (ear === stop) {
|
|
348
|
+
filteredOut = false;
|
|
349
|
+
ear = filterPoints(ear);
|
|
350
|
+
if (filteredOut) {
|
|
351
|
+
stop = ear;
|
|
352
|
+
continue;
|
|
353
|
+
}
|
|
354
|
+
if (!cured) {
|
|
355
|
+
ear = cureLocalIntersections(ear, triangles);
|
|
356
|
+
stop = ear;
|
|
357
|
+
cured = true;
|
|
358
|
+
continue;
|
|
359
|
+
}
|
|
360
|
+
splitEarcut(ear, triangles, minX, minY, invSize);
|
|
361
|
+
break;
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
}
|
|
365
|
+
// check whether a polygon node forms a valid ear with adjacent nodes
|
|
366
|
+
function isEar(ear) {
|
|
367
|
+
// reflex check (area(a, b, c) >= 0) is hoisted into the caller
|
|
368
|
+
const a = ear.prev;
|
|
369
|
+
const b = ear;
|
|
370
|
+
const c = ear.next;
|
|
371
|
+
const ax = a.x;
|
|
372
|
+
const bx = b.x;
|
|
373
|
+
const cx = c.x;
|
|
374
|
+
const ay = a.y;
|
|
375
|
+
const by = b.y;
|
|
376
|
+
const cy = c.y;
|
|
377
|
+
const x0 = Math.min(ax, bx, cx); // triangle bbox
|
|
378
|
+
const y0 = Math.min(ay, by, cy);
|
|
379
|
+
const x1 = Math.max(ax, bx, cx);
|
|
380
|
+
const y1 = Math.max(ay, by, cy);
|
|
381
|
+
// make sure we don't have other points inside the potential ear
|
|
382
|
+
let p = c.next;
|
|
383
|
+
while (p !== a) {
|
|
384
|
+
if (p.x >= x0 &&
|
|
385
|
+
p.x <= x1 &&
|
|
386
|
+
p.y >= y0 &&
|
|
387
|
+
p.y <= y1 &&
|
|
388
|
+
!(ax === p.x && ay === p.y) &&
|
|
389
|
+
pointInTriangle(ax, ay, bx, by, cx, cy, p.x, p.y) &&
|
|
390
|
+
area(p.prev, p, p.next) >= 0)
|
|
391
|
+
return false;
|
|
392
|
+
p = p.next;
|
|
393
|
+
}
|
|
394
|
+
return true;
|
|
395
|
+
}
|
|
396
|
+
function isEarHashed(ear, minX, minY, invSize) {
|
|
397
|
+
// reflex check is hoisted into the caller (see isEar)
|
|
398
|
+
const a = ear.prev;
|
|
399
|
+
const b = ear;
|
|
400
|
+
const c = ear.next;
|
|
401
|
+
const ax = a.x;
|
|
402
|
+
const bx = b.x;
|
|
403
|
+
const cx = c.x;
|
|
404
|
+
const ay = a.y;
|
|
405
|
+
const by = b.y;
|
|
406
|
+
const cy = c.y;
|
|
407
|
+
const x0 = Math.min(ax, bx, cx); // triangle bbox
|
|
408
|
+
const y0 = Math.min(ay, by, cy);
|
|
409
|
+
const x1 = Math.max(ax, bx, cx);
|
|
410
|
+
const y1 = Math.max(ay, by, cy);
|
|
411
|
+
// z-order range for the current triangle bbox
|
|
412
|
+
const minZ = zOrder(x0, y0, minX, minY, invSize);
|
|
413
|
+
const maxZ = zOrder(x1, y1, minX, minY, invSize);
|
|
414
|
+
let p = ear.prevZ;
|
|
415
|
+
while (p && p.z >= minZ) {
|
|
416
|
+
// look for points inside the triangle in decreasing z-order
|
|
417
|
+
if (p.x >= x0 &&
|
|
418
|
+
p.x <= x1 &&
|
|
419
|
+
p.y >= y0 &&
|
|
420
|
+
p.y <= y1 &&
|
|
421
|
+
p !== c &&
|
|
422
|
+
!(ax === p.x && ay === p.y) &&
|
|
423
|
+
pointInTriangle(ax, ay, bx, by, cx, cy, p.x, p.y) &&
|
|
424
|
+
area(p.prev, p, p.next) >= 0)
|
|
425
|
+
return false;
|
|
426
|
+
p = p.prevZ;
|
|
427
|
+
}
|
|
428
|
+
let n = ear.nextZ;
|
|
429
|
+
while (n && n.z <= maxZ) {
|
|
430
|
+
// look for points in increasing z-order
|
|
431
|
+
if (n.x >= x0 &&
|
|
432
|
+
n.x <= x1 &&
|
|
433
|
+
n.y >= y0 &&
|
|
434
|
+
n.y <= y1 &&
|
|
435
|
+
n !== c &&
|
|
436
|
+
!(ax === n.x && ay === n.y) &&
|
|
437
|
+
pointInTriangle(ax, ay, bx, by, cx, cy, n.x, n.y) &&
|
|
438
|
+
area(n.prev, n, n.next) >= 0)
|
|
439
|
+
return false;
|
|
440
|
+
n = n.nextZ;
|
|
441
|
+
}
|
|
442
|
+
return true;
|
|
443
|
+
}
|
|
444
|
+
// go through all polygon nodes and cure small local self-intersections
|
|
445
|
+
function cureLocalIntersections(start, triangles) {
|
|
446
|
+
let p = start;
|
|
447
|
+
let cured = false;
|
|
448
|
+
do {
|
|
449
|
+
const a = p.prev;
|
|
450
|
+
const b = p.next.next;
|
|
451
|
+
if (intersects(a, p, p.next, b, false) && locallyInside(a, b) && locallyInside(b, a)) {
|
|
452
|
+
triangles.push(a.i, p.i, b.i);
|
|
453
|
+
// remove two nodes involved
|
|
454
|
+
removeNode(p);
|
|
455
|
+
removeNode(p.next);
|
|
456
|
+
p = b;
|
|
457
|
+
start = b;
|
|
458
|
+
cured = true;
|
|
459
|
+
}
|
|
460
|
+
p = p.next;
|
|
461
|
+
} while (p !== start);
|
|
462
|
+
return cured ? filterPoints(p) : p;
|
|
463
|
+
}
|
|
464
|
+
// try splitting polygon into two and triangulate them independently
|
|
465
|
+
function splitEarcut(start, triangles, minX, minY, invSize) {
|
|
466
|
+
// look for a valid diagonal that divides the polygon into two
|
|
467
|
+
let a = start;
|
|
468
|
+
do {
|
|
469
|
+
let b = a.next.next;
|
|
470
|
+
while (b !== a.prev) {
|
|
471
|
+
if (a.i !== b.i && isValidDiagonal(a, b)) {
|
|
472
|
+
// split the polygon in two by the diagonal
|
|
473
|
+
let c = splitPolygon(a, b);
|
|
474
|
+
// filter colinear points around the cuts
|
|
475
|
+
a = filterPoints(a, a.next);
|
|
476
|
+
c = filterPoints(c, c.next);
|
|
477
|
+
// run earcut on each half
|
|
478
|
+
earcutLinked(a, triangles, minX, minY, invSize);
|
|
479
|
+
earcutLinked(c, triangles, minX, minY, invSize);
|
|
480
|
+
return;
|
|
481
|
+
}
|
|
482
|
+
b = b.next;
|
|
483
|
+
}
|
|
484
|
+
a = a.next;
|
|
485
|
+
} while (a !== start);
|
|
486
|
+
}
|
|
487
|
+
// link every hole into the outer loop, producing a single-ring polygon without holes
|
|
488
|
+
function eliminateHoles(data, holeIndices, outerNode, dim) {
|
|
489
|
+
const queue = [];
|
|
490
|
+
for (let i = 0, len = holeIndices.length; i < len; i++) {
|
|
491
|
+
const start = holeIndices[i] * dim;
|
|
492
|
+
const end = i < len - 1 ? holeIndices[i + 1] * dim : data.length;
|
|
493
|
+
const list = linkedList(data, start, end, dim, false);
|
|
494
|
+
if (list === list.next)
|
|
495
|
+
steiners.add(list);
|
|
496
|
+
queue.push(getLeftmost(list));
|
|
497
|
+
}
|
|
498
|
+
queue.sort(compareXYSlope);
|
|
499
|
+
// block-bbox index for findHoleBridge, grown append-only as holes merge
|
|
500
|
+
buildBlockIndex(data.length / dim, holeIndices.length);
|
|
501
|
+
indexSegment(outerNode, outerNode);
|
|
502
|
+
// process holes from left to right
|
|
503
|
+
indexActive = true;
|
|
504
|
+
for (let i = 0; i < queue.length; i++) {
|
|
505
|
+
outerNode = eliminateHole(queue[i], outerNode);
|
|
506
|
+
}
|
|
507
|
+
indexActive = false;
|
|
508
|
+
// collapse collinear/coincident points across the whole merged ring once before clipping
|
|
509
|
+
return filterPoints(outerNode);
|
|
510
|
+
}
|
|
511
|
+
function compareXYSlope(a, b) {
|
|
512
|
+
// when the left-most point of 2 holes meet at a vertex, sort the holes counterclockwise so that
|
|
513
|
+
// when we find the bridge to the outer shell it is always the point that they meet at.
|
|
514
|
+
return (a.x - b.x ||
|
|
515
|
+
a.y - b.y ||
|
|
516
|
+
(a.next.y - a.y) / (a.next.x - a.x) - (b.next.y - b.y) / (b.next.x - b.x));
|
|
517
|
+
}
|
|
518
|
+
// find a bridge between vertices that connects hole with an outer ring and link it
|
|
519
|
+
function eliminateHole(hole, outerNode) {
|
|
520
|
+
const bridge = findHoleBridge(hole, outerNode);
|
|
521
|
+
if (!bridge) {
|
|
522
|
+
return outerNode;
|
|
523
|
+
}
|
|
524
|
+
const bridgeReverse = splitPolygon(bridge, hole);
|
|
525
|
+
// index the merged-in segment before filtering
|
|
526
|
+
const bridge2 = bridgeReverse.next;
|
|
527
|
+
indexSegment(bridge, bridge2.next);
|
|
528
|
+
// heal collinear/coincident points around the two new slit edges
|
|
529
|
+
filterPoints(bridgeReverse, bridgeReverse.next);
|
|
530
|
+
return filterPoints(bridge, bridge.next);
|
|
531
|
+
}
|
|
532
|
+
// Block-bbox index for findHoleBridge: one [minX,minY,maxX,maxY] bbox per K consecutive ring
|
|
533
|
+
// edges, in a flat Float64Array, so the leftward-ray scan can skip whole blocks in O(1).
|
|
534
|
+
const K = 16; // edges per block
|
|
535
|
+
let blockBBox = new Float64Array(0); // [minX,minY,maxX,maxY] per block
|
|
536
|
+
let numBlocks = 0;
|
|
537
|
+
const blockHead = []; // first node of each block's segment
|
|
538
|
+
const blockStop = []; // node just past each block's segment
|
|
539
|
+
function buildBlockIndex(maxNodes, numHoles) {
|
|
540
|
+
// upper bound: every input node indexed once, +2 bridge nodes per hole, plus a partial
|
|
541
|
+
// trailing block per appended segment (outer ring + one per hole)
|
|
542
|
+
const maxBlocks = Math.ceil((maxNodes + 2 * numHoles) / K) + numHoles + 2;
|
|
543
|
+
if (blockBBox.length < maxBlocks * 4)
|
|
544
|
+
blockBBox = new Float64Array(maxBlocks * 4);
|
|
545
|
+
numBlocks = 0;
|
|
546
|
+
}
|
|
547
|
+
// index the ring run head..stop (exclusive) as ceil(len / K) blocks; head === stop means
|
|
548
|
+
// the whole ring. each block's bbox covers both endpoints of every edge it owns.
|
|
549
|
+
function indexSegment(head, stop) {
|
|
550
|
+
let p = head;
|
|
551
|
+
do {
|
|
552
|
+
const b = numBlocks++;
|
|
553
|
+
blockHead[b] = p;
|
|
554
|
+
let minX = Number.POSITIVE_INFINITY;
|
|
555
|
+
let minY = Number.POSITIVE_INFINITY;
|
|
556
|
+
let maxX = Number.NEGATIVE_INFINITY;
|
|
557
|
+
let maxY = Number.NEGATIVE_INFINITY;
|
|
558
|
+
let k = 0;
|
|
559
|
+
do {
|
|
560
|
+
const c = p.next; // edge p->c; bbox must bound both endpoints
|
|
561
|
+
p.z = b; // reuse z as the owning block during eliminateHoles (see growBlock)
|
|
562
|
+
if (p.x < minX)
|
|
563
|
+
minX = p.x;
|
|
564
|
+
if (p.x > maxX)
|
|
565
|
+
maxX = p.x;
|
|
566
|
+
if (p.y < minY)
|
|
567
|
+
minY = p.y;
|
|
568
|
+
if (p.y > maxY)
|
|
569
|
+
maxY = p.y;
|
|
570
|
+
if (c.x < minX)
|
|
571
|
+
minX = c.x;
|
|
572
|
+
if (c.x > maxX)
|
|
573
|
+
maxX = c.x;
|
|
574
|
+
if (c.y < minY)
|
|
575
|
+
minY = c.y;
|
|
576
|
+
if (c.y > maxY)
|
|
577
|
+
maxY = c.y;
|
|
578
|
+
p = c;
|
|
579
|
+
} while (++k < K && p !== stop);
|
|
580
|
+
blockStop[b] = p;
|
|
581
|
+
const g = b * 4;
|
|
582
|
+
blockBBox[g] = minX;
|
|
583
|
+
blockBBox[g + 1] = minY;
|
|
584
|
+
blockBBox[g + 2] = maxX;
|
|
585
|
+
blockBBox[g + 3] = maxY;
|
|
586
|
+
} while (p !== stop);
|
|
587
|
+
}
|
|
588
|
+
// when filterPoints heals an edge head->tail, grow head's block bbox to cover tail so the
|
|
589
|
+
// leftward-ray prune can't false-skip it.
|
|
590
|
+
function growBlock(head, tail) {
|
|
591
|
+
const g = head.z * 4;
|
|
592
|
+
if (tail.x < blockBBox[g])
|
|
593
|
+
blockBBox[g] = tail.x;
|
|
594
|
+
if (tail.y < blockBBox[g + 1])
|
|
595
|
+
blockBBox[g + 1] = tail.y;
|
|
596
|
+
if (tail.x > blockBBox[g + 2])
|
|
597
|
+
blockBBox[g + 2] = tail.x;
|
|
598
|
+
if (tail.y > blockBBox[g + 3])
|
|
599
|
+
blockBBox[g + 3] = tail.y;
|
|
600
|
+
}
|
|
601
|
+
function liveBlockStop(b) {
|
|
602
|
+
let stop = blockStop[b];
|
|
603
|
+
while (stop.prev.next !== stop)
|
|
604
|
+
stop = stop.next;
|
|
605
|
+
blockStop[b] = stop;
|
|
606
|
+
return stop;
|
|
607
|
+
}
|
|
608
|
+
// the block's head node can be removed by filterPoints during merges; advance it to the next
|
|
609
|
+
// live node so the walk doesn't start on (and immediately terminate at) a dead node.
|
|
610
|
+
function liveBlockHead(b) {
|
|
611
|
+
let head = blockHead[b];
|
|
612
|
+
while (head.prev.next !== head)
|
|
613
|
+
head = head.next;
|
|
614
|
+
blockHead[b] = head;
|
|
615
|
+
return head;
|
|
616
|
+
}
|
|
617
|
+
// David Eberly's algorithm for finding a bridge between hole and outer polygon
|
|
618
|
+
function findHoleBridge(hole, outerNode) {
|
|
619
|
+
let p = outerNode;
|
|
620
|
+
const hx = hole.x;
|
|
621
|
+
const hy = hole.y;
|
|
622
|
+
let qx = Number.NEGATIVE_INFINITY;
|
|
623
|
+
let m;
|
|
624
|
+
// find a segment intersected by a ray from the hole's leftmost point to the left;
|
|
625
|
+
// segment's endpoint with lesser x will be potential connection point
|
|
626
|
+
// unless they intersect at a vertex, then choose the vertex
|
|
627
|
+
if (equals(hole, p))
|
|
628
|
+
return p;
|
|
629
|
+
// scan blocks; skip any whose bbox can't hold a crossing that beats qx and lies left of hx
|
|
630
|
+
for (let b = 0, g = 0; b < numBlocks; b++, g += 4) {
|
|
631
|
+
if (hy < blockBBox[g + 1] ||
|
|
632
|
+
hy > blockBBox[g + 3] ||
|
|
633
|
+
blockBBox[g] > hx ||
|
|
634
|
+
blockBBox[g + 2] <= qx)
|
|
635
|
+
continue;
|
|
636
|
+
// ensure the walk's exclusive bound is live so we don't overrun into other blocks
|
|
637
|
+
const stop = liveBlockStop(b);
|
|
638
|
+
p = liveBlockHead(b);
|
|
639
|
+
do {
|
|
640
|
+
if (p.prev.next === p) {
|
|
641
|
+
// skip nodes removed by filterPoints (stale in the index)
|
|
642
|
+
if (equals(hole, p.next))
|
|
643
|
+
return p.next;
|
|
644
|
+
if (hy <= p.y && hy >= p.next.y && p.next.y !== p.y) {
|
|
645
|
+
const x = p.x + ((hy - p.y) * (p.next.x - p.x)) / (p.next.y - p.y);
|
|
646
|
+
if (x <= hx && x > qx) {
|
|
647
|
+
qx = x;
|
|
648
|
+
m = p.x < p.next.x ? p : p.next;
|
|
649
|
+
if (x === hx)
|
|
650
|
+
return m; // hole touches outer segment; pick leftmost endpoint
|
|
651
|
+
}
|
|
652
|
+
}
|
|
653
|
+
}
|
|
654
|
+
p = p.next;
|
|
655
|
+
} while (p !== stop);
|
|
656
|
+
}
|
|
657
|
+
if (!m)
|
|
658
|
+
return null;
|
|
659
|
+
// look for points inside the triangle of hole point, segment intersection and endpoint;
|
|
660
|
+
// if there are no points found, we have a valid connection;
|
|
661
|
+
// otherwise choose the point of the minimum angle with the ray as connection point
|
|
662
|
+
const mx = m.x;
|
|
663
|
+
const my = m.y;
|
|
664
|
+
const tminY = Math.min(hy, my); // the triangle's y span; x span is [mx, hx]
|
|
665
|
+
const tmaxY = Math.max(hy, my);
|
|
666
|
+
let tanMin = Number.POSITIVE_INFINITY;
|
|
667
|
+
// scan the same blocks; skip any whose bbox can't overlap the triangle's box
|
|
668
|
+
for (let b = 0, g = 0; b < numBlocks; b++, g += 4) {
|
|
669
|
+
if (blockBBox[g + 2] < mx ||
|
|
670
|
+
blockBBox[g] > hx ||
|
|
671
|
+
blockBBox[g + 3] < tminY ||
|
|
672
|
+
blockBBox[g + 1] > tmaxY)
|
|
673
|
+
continue;
|
|
674
|
+
const stop = liveBlockStop(b);
|
|
675
|
+
p = liveBlockHead(b);
|
|
676
|
+
do {
|
|
677
|
+
if (p.prev.next === p &&
|
|
678
|
+
hx >= p.x &&
|
|
679
|
+
p.x >= mx &&
|
|
680
|
+
hx !== p.x && // skip dead nodes
|
|
681
|
+
pointInTriangle(hy < my ? hx : qx, hy, mx, my, hy < my ? qx : hx, hy, p.x, p.y)) {
|
|
682
|
+
const tan = Math.abs(hy - p.y) / (hx - p.x); // tangential
|
|
683
|
+
// if hole point sits on p's horizontal edge (T-junction touch): the bridge runs
|
|
684
|
+
// along that edge — locallyInside rejects it as collinear, but it's valid
|
|
685
|
+
if ((locallyInside(p, hole) || (p.y === hy && p.next.y === hy && p.next.x > hx)) &&
|
|
686
|
+
(tan < tanMin ||
|
|
687
|
+
(tan === tanMin && (p.x > m.x || (p.x === m.x && sectorContainsSector(m, p)))))) {
|
|
688
|
+
m = p;
|
|
689
|
+
tanMin = tan;
|
|
690
|
+
}
|
|
691
|
+
}
|
|
692
|
+
p = p.next;
|
|
693
|
+
} while (p !== stop);
|
|
694
|
+
}
|
|
695
|
+
return m;
|
|
696
|
+
}
|
|
697
|
+
// whether sector in vertex m contains sector in vertex p in the same coordinates
|
|
698
|
+
function sectorContainsSector(m, p) {
|
|
699
|
+
return area(m.prev, m, p.prev) < 0 && area(p.next, m, m.next) < 0;
|
|
700
|
+
}
|
|
701
|
+
// scratch buffers reused across calls and grown on demand
|
|
702
|
+
const sortArr = [];
|
|
703
|
+
let sortBuf = [];
|
|
704
|
+
let zArr = new Uint32Array(0);
|
|
705
|
+
let zBuf = new Uint32Array(0);
|
|
706
|
+
const counts = new Uint32Array(256);
|
|
707
|
+
// interlink polygon nodes in z-order: collect into an array, sort by z, relink
|
|
708
|
+
function indexCurve(start, minX, minY, invSize) {
|
|
709
|
+
let p = start;
|
|
710
|
+
let n = 0;
|
|
711
|
+
do {
|
|
712
|
+
// always (re)compute: z may still hold a block index left over from eliminateHoles
|
|
713
|
+
p.z = zOrder(p.x, p.y, minX, minY, invSize);
|
|
714
|
+
sortArr[n++] = p;
|
|
715
|
+
p = p.next;
|
|
716
|
+
} while (p !== start);
|
|
717
|
+
sortNodes(n);
|
|
718
|
+
let prev = null;
|
|
719
|
+
for (let i = 0; i < n; i++) {
|
|
720
|
+
const node = sortArr[i];
|
|
721
|
+
node.prevZ = prev;
|
|
722
|
+
if (prev)
|
|
723
|
+
prev.nextZ = node;
|
|
724
|
+
prev = node;
|
|
725
|
+
}
|
|
726
|
+
prev.nextZ = null;
|
|
727
|
+
}
|
|
728
|
+
// sort the first n nodes of sortArr by z, in place: insertion sort for small n, else LSD radix
|
|
729
|
+
function sortNodes(n) {
|
|
730
|
+
if (n <= 32) {
|
|
731
|
+
for (let i = 1; i < n; i++) {
|
|
732
|
+
const node = sortArr[i];
|
|
733
|
+
const z = node.z;
|
|
734
|
+
let j = i - 1;
|
|
735
|
+
while (j >= 0 && sortArr[j].z > z) {
|
|
736
|
+
sortArr[j + 1] = sortArr[j];
|
|
737
|
+
j--;
|
|
738
|
+
}
|
|
739
|
+
sortArr[j + 1] = node;
|
|
740
|
+
}
|
|
741
|
+
return;
|
|
742
|
+
}
|
|
743
|
+
if (zArr.length < n) {
|
|
744
|
+
zArr = new Uint32Array(n);
|
|
745
|
+
zBuf = new Uint32Array(n);
|
|
746
|
+
sortBuf = new Array(n);
|
|
747
|
+
}
|
|
748
|
+
for (let i = 0; i < n; i++)
|
|
749
|
+
zArr[i] = sortArr[i].z;
|
|
750
|
+
// even pass count lands the sorted result back in sortArr
|
|
751
|
+
radixPass(n, sortArr, zArr, sortBuf, zBuf, 0);
|
|
752
|
+
radixPass(n, sortBuf, zBuf, sortArr, zArr, 8);
|
|
753
|
+
radixPass(n, sortArr, zArr, sortBuf, zBuf, 16);
|
|
754
|
+
radixPass(n, sortBuf, zBuf, sortArr, zArr, 24);
|
|
755
|
+
}
|
|
756
|
+
// one LSD radix pass: stably scatter the first n nodes (and their z) from src to dst
|
|
757
|
+
function radixPass(n, src, srcZ, dst, dstZ, shift) {
|
|
758
|
+
counts.fill(0);
|
|
759
|
+
for (let i = 0; i < n; i++)
|
|
760
|
+
counts[(srcZ[i] >>> shift) & 0xff]++;
|
|
761
|
+
// turn per-bucket counts into start offsets (prefix sum)
|
|
762
|
+
let sum = 0;
|
|
763
|
+
for (let b = 0; b < 256; b++) {
|
|
764
|
+
const c = counts[b];
|
|
765
|
+
counts[b] = sum;
|
|
766
|
+
sum += c;
|
|
767
|
+
}
|
|
768
|
+
for (let i = 0; i < n; i++) {
|
|
769
|
+
const z = srcZ[i];
|
|
770
|
+
const pos = counts[(z >>> shift) & 0xff]++;
|
|
771
|
+
dst[pos] = src[i];
|
|
772
|
+
dstZ[pos] = z;
|
|
773
|
+
}
|
|
774
|
+
}
|
|
775
|
+
// z-order of a point given coords and inverse of the longer side of data bbox
|
|
776
|
+
function zOrder(x, y, minX, minY, invSize) {
|
|
777
|
+
// coords are transformed into non-negative 15-bit integer range
|
|
778
|
+
x = ((x - minX) * invSize) | 0;
|
|
779
|
+
y = ((y - minY) * invSize) | 0;
|
|
780
|
+
x = (x | (x << 8)) & 0x00ff00ff;
|
|
781
|
+
x = (x | (x << 4)) & 0x0f0f0f0f;
|
|
782
|
+
x = (x | (x << 2)) & 0x33333333;
|
|
783
|
+
x = (x | (x << 1)) & 0x55555555;
|
|
784
|
+
y = (y | (y << 8)) & 0x00ff00ff;
|
|
785
|
+
y = (y | (y << 4)) & 0x0f0f0f0f;
|
|
786
|
+
y = (y | (y << 2)) & 0x33333333;
|
|
787
|
+
y = (y | (y << 1)) & 0x55555555;
|
|
788
|
+
return x | (y << 1);
|
|
789
|
+
}
|
|
790
|
+
// find the leftmost node of a polygon ring
|
|
791
|
+
function getLeftmost(start) {
|
|
792
|
+
let p = start;
|
|
793
|
+
let leftmost = start;
|
|
794
|
+
do {
|
|
795
|
+
if (p.x < leftmost.x || (p.x === leftmost.x && p.y < leftmost.y))
|
|
796
|
+
leftmost = p;
|
|
797
|
+
p = p.next;
|
|
798
|
+
} while (p !== start);
|
|
799
|
+
return leftmost;
|
|
800
|
+
}
|
|
801
|
+
// check if a point lies within a convex triangle
|
|
802
|
+
function pointInTriangle(ax, ay, bx, by, cx, cy, px, py) {
|
|
803
|
+
return ((cx - px) * (ay - py) >= (ax - px) * (cy - py) &&
|
|
804
|
+
(ax - px) * (by - py) >= (bx - px) * (ay - py) &&
|
|
805
|
+
(bx - px) * (cy - py) >= (cx - px) * (by - py));
|
|
806
|
+
}
|
|
807
|
+
// check if a diagonal between two polygon nodes is valid (lies in polygon interior)
|
|
808
|
+
function isValidDiagonal(a, b) {
|
|
809
|
+
// degenerate case
|
|
810
|
+
const zeroLength = equals(a, b) && area(a.prev, a, a.next) > 0 && area(b.prev, b, b.next) > 0;
|
|
811
|
+
return (a.next.i !== b.i &&
|
|
812
|
+
(zeroLength ||
|
|
813
|
+
(locallyInside(a, b) && // locally visible
|
|
814
|
+
locallyInside(b, a) &&
|
|
815
|
+
(area(a.prev, a, b.prev) !== 0 || area(a, b.prev, b) !== 0))) && // no opposite-facing sectors
|
|
816
|
+
!intersectsPolygon(a, b) &&
|
|
817
|
+
(zeroLength || middleInside(a, b)));
|
|
818
|
+
}
|
|
819
|
+
// signed area of a triangle
|
|
820
|
+
function area(p, q, r) {
|
|
821
|
+
return (q.y - p.y) * (r.x - q.x) - (q.x - p.x) * (r.y - q.y);
|
|
822
|
+
}
|
|
823
|
+
// check if two points are equal
|
|
824
|
+
function equals(p1, p2) {
|
|
825
|
+
return p1.x === p2.x && p1.y === p2.y;
|
|
826
|
+
}
|
|
827
|
+
// check if two segments intersect; by default includes collinear boundary touches
|
|
828
|
+
function intersects(p1, q1, p2, q2, includeBoundary = true) {
|
|
829
|
+
const o1 = area(p1, q1, p2);
|
|
830
|
+
const o2 = area(p1, q1, q2);
|
|
831
|
+
const o3 = area(p2, q2, p1);
|
|
832
|
+
const o4 = area(p2, q2, q1);
|
|
833
|
+
if (((o1 > 0 && o2 < 0) || (o1 < 0 && o2 > 0)) && ((o3 > 0 && o4 < 0) || (o3 < 0 && o4 > 0)))
|
|
834
|
+
return true;
|
|
835
|
+
if (!includeBoundary)
|
|
836
|
+
return false;
|
|
837
|
+
if (o1 === 0 && onSegment(p1, p2, q1))
|
|
838
|
+
return true; // p1, q1 and p2 are collinear and p2 lies on p1q1
|
|
839
|
+
if (o2 === 0 && onSegment(p1, q2, q1))
|
|
840
|
+
return true; // p1, q1 and q2 are collinear and q2 lies on p1q1
|
|
841
|
+
if (o3 === 0 && onSegment(p2, p1, q2))
|
|
842
|
+
return true; // p2, q2 and p1 are collinear and p1 lies on p2q2
|
|
843
|
+
if (o4 === 0 && onSegment(p2, q1, q2))
|
|
844
|
+
return true; // p2, q2 and q1 are collinear and q1 lies on p2q2
|
|
845
|
+
return false;
|
|
846
|
+
}
|
|
847
|
+
// for collinear points p, q, r, check if point q lies on segment pr
|
|
848
|
+
function onSegment(p, q, r) {
|
|
849
|
+
return (q.x <= Math.max(p.x, r.x) &&
|
|
850
|
+
q.x >= Math.min(p.x, r.x) &&
|
|
851
|
+
q.y <= Math.max(p.y, r.y) &&
|
|
852
|
+
q.y >= Math.min(p.y, r.y));
|
|
853
|
+
}
|
|
854
|
+
// check if a polygon diagonal intersects any polygon segments
|
|
855
|
+
function intersectsPolygon(a, b) {
|
|
856
|
+
// diagonal bbox; an edge whose bbox can't overlap it can't intersect it
|
|
857
|
+
const minX = Math.min(a.x, b.x);
|
|
858
|
+
const maxX = Math.max(a.x, b.x);
|
|
859
|
+
const minY = Math.min(a.y, b.y);
|
|
860
|
+
const maxY = Math.max(a.y, b.y);
|
|
861
|
+
let p = a;
|
|
862
|
+
do {
|
|
863
|
+
const n = p.next;
|
|
864
|
+
if ((p.x > maxX && n.x > maxX) ||
|
|
865
|
+
(p.x < minX && n.x < minX) ||
|
|
866
|
+
(p.y > maxY && n.y > maxY) ||
|
|
867
|
+
(p.y < minY && n.y < minY)) {
|
|
868
|
+
p = n;
|
|
869
|
+
continue;
|
|
870
|
+
}
|
|
871
|
+
if (p.i !== a.i && n.i !== a.i && p.i !== b.i && n.i !== b.i && intersects(p, n, a, b))
|
|
872
|
+
return true;
|
|
873
|
+
p = n;
|
|
874
|
+
} while (p !== a);
|
|
875
|
+
return false;
|
|
876
|
+
}
|
|
877
|
+
// check if a polygon diagonal is locally inside the polygon
|
|
878
|
+
function locallyInside(a, b) {
|
|
879
|
+
return area(a.prev, a, a.next) < 0
|
|
880
|
+
? area(a, b, a.next) >= 0 && area(a, a.prev, b) >= 0
|
|
881
|
+
: area(a, b, a.prev) < 0 || area(a, a.next, b) < 0;
|
|
882
|
+
}
|
|
883
|
+
// check if the middle point of a polygon diagonal is inside the polygon
|
|
884
|
+
function middleInside(a, b) {
|
|
885
|
+
let p = a;
|
|
886
|
+
let inside = false;
|
|
887
|
+
const px = (a.x + b.x) / 2;
|
|
888
|
+
const py = (a.y + b.y) / 2;
|
|
889
|
+
do {
|
|
890
|
+
const n = p.next;
|
|
891
|
+
if (p.y > py !== n.y > py && px < ((n.x - p.x) * (py - p.y)) / (n.y - p.y) + p.x)
|
|
892
|
+
inside = !inside;
|
|
893
|
+
p = n;
|
|
894
|
+
} while (p !== a);
|
|
895
|
+
return inside;
|
|
896
|
+
}
|
|
897
|
+
// link two polygon vertices with a bridge; if the vertices belong to the same ring, it splits
|
|
898
|
+
// polygon into two; if one belongs to the outer ring and another to a hole, it merges it
|
|
899
|
+
function splitPolygon(a, b) {
|
|
900
|
+
const a2 = createNode(a.i, a.x, a.y);
|
|
901
|
+
const b2 = createNode(b.i, b.x, b.y);
|
|
902
|
+
const an = a.next;
|
|
903
|
+
const bp = b.prev;
|
|
904
|
+
a.next = b;
|
|
905
|
+
b.prev = a;
|
|
906
|
+
a2.next = an;
|
|
907
|
+
an.prev = a2;
|
|
908
|
+
b2.next = a2;
|
|
909
|
+
a2.prev = b2;
|
|
910
|
+
bp.next = b2;
|
|
911
|
+
b2.prev = bp;
|
|
912
|
+
return b2;
|
|
913
|
+
}
|
|
914
|
+
// create a node and optionally link it with previous one (in a circular doubly linked list)
|
|
915
|
+
function insertNode(i, x, y, last) {
|
|
916
|
+
const p = createNode(i, x, y);
|
|
917
|
+
if (!last) {
|
|
918
|
+
p.prev = p;
|
|
919
|
+
p.next = p;
|
|
920
|
+
}
|
|
921
|
+
else {
|
|
922
|
+
p.next = last.next;
|
|
923
|
+
p.prev = last;
|
|
924
|
+
last.next.prev = p;
|
|
925
|
+
last.next = p;
|
|
926
|
+
}
|
|
927
|
+
return p;
|
|
928
|
+
}
|
|
929
|
+
function removeNode(p) {
|
|
930
|
+
p.next.prev = p.prev;
|
|
931
|
+
p.prev.next = p.next;
|
|
932
|
+
if (p.prevZ)
|
|
933
|
+
p.prevZ.nextZ = p.nextZ;
|
|
934
|
+
if (p.nextZ)
|
|
935
|
+
p.nextZ.prevZ = p.prevZ;
|
|
936
|
+
// keep the hole-bridge index's block bboxes covering the healed prev->next edge
|
|
937
|
+
if (indexActive)
|
|
938
|
+
growBlock(p.prev, p.next);
|
|
939
|
+
}
|
|
940
|
+
function createNode(i, x, y) {
|
|
941
|
+
// prev/next are assigned by the caller before any read
|
|
942
|
+
return {
|
|
943
|
+
i, // vertex index in coordinates array
|
|
944
|
+
x,
|
|
945
|
+
y, // vertex coordinates
|
|
946
|
+
prev: null, // previous and next vertex nodes in a polygon ring
|
|
947
|
+
next: null,
|
|
948
|
+
z: 0, // z-order curve value; doubles as owning block in the hole-bridge index
|
|
949
|
+
prevZ: null, // previous and next nodes in z-order
|
|
950
|
+
nextZ: null,
|
|
951
|
+
};
|
|
952
|
+
}
|
|
953
|
+
function signedArea(data, start, end, dim) {
|
|
954
|
+
let sum = 0;
|
|
955
|
+
for (let i = start, j = end - dim; i < end; i += dim) {
|
|
956
|
+
sum += (data[j] - data[i]) * (data[i + 1] + data[j + 1]);
|
|
957
|
+
j = i;
|
|
958
|
+
}
|
|
959
|
+
return sum;
|
|
960
|
+
}
|