@jax-data-science/sci-image-visualizer 0.1.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/LICENSE +21 -0
- package/README.md +404 -0
- package/esm2022/index.mjs +52 -0
- package/esm2022/jax-data-science-sci-image-visualizer.mjs +5 -0
- package/esm2022/lib/channel-histogram/channel-histogram.component.mjs +398 -0
- package/esm2022/lib/contracts/capabilities.contract.mjs +32 -0
- package/esm2022/lib/contracts/cell-segmenter.contract.mjs +12 -0
- package/esm2022/lib/contracts/channel-histogram-api.contract.mjs +14 -0
- package/esm2022/lib/contracts/colormap-lut.mjs +95 -0
- package/esm2022/lib/contracts/coordinate-transform.contract.mjs +2 -0
- package/esm2022/lib/contracts/display-types.mjs +8 -0
- package/esm2022/lib/contracts/image.contract.mjs +11 -0
- package/esm2022/lib/contracts/intensity.mjs +28 -0
- package/esm2022/lib/contracts/plot-type.mjs +112 -0
- package/esm2022/lib/contracts/ports/image-state.port.mjs +3 -0
- package/esm2022/lib/contracts/ports/preferences.port.mjs +3 -0
- package/esm2022/lib/contracts/ports/region-io.port.mjs +3 -0
- package/esm2022/lib/contracts/ports/tile-access.port.mjs +3 -0
- package/esm2022/lib/contracts/region-editor-api.contract.mjs +4 -0
- package/esm2022/lib/contracts/region-overlay.contract.mjs +2 -0
- package/esm2022/lib/contracts/region-store.contract.mjs +2 -0
- package/esm2022/lib/contracts/sam.contract.mjs +12 -0
- package/esm2022/lib/contracts/toolbar-config.mjs +8 -0
- package/esm2022/lib/contracts/visualizer.contract.mjs +10 -0
- package/esm2022/lib/contracts/viz-config.mjs +3 -0
- package/esm2022/lib/hex-color-picker/hex-color-picker.component.mjs +260 -0
- package/esm2022/lib/implementations/base-store-visualizer.mjs +76 -0
- package/esm2022/lib/implementations/napari-js/napari-axes-labels.mjs +106 -0
- package/esm2022/lib/implementations/napari-js/napari-region-overlay.mjs +847 -0
- package/esm2022/lib/implementations/napari-js/napari-scale-bar.mjs +92 -0
- package/esm2022/lib/implementations/napari-js/napari-visualizer.service.mjs +2283 -0
- package/esm2022/lib/implementations/napari-js/napari-volume-z-handle.mjs +126 -0
- package/esm2022/lib/implementations/osd/display-pipeline.mjs +143 -0
- package/esm2022/lib/implementations/osd/histogram-sampler.mjs +217 -0
- package/esm2022/lib/implementations/osd/openseadragon-visualizer.service.mjs +1780 -0
- package/esm2022/lib/implementations/osd/osd-coordinate-transform.mjs +25 -0
- package/esm2022/lib/implementations/osd/osd-coords.mjs +53 -0
- package/esm2022/lib/implementations/osd/osd-lib.mjs +20 -0
- package/esm2022/lib/implementations/osd/osd-region-overlay.mjs +1237 -0
- package/esm2022/lib/implementations/osd/osd-scale-bar.mjs +88 -0
- package/esm2022/lib/implementations/osd/slice-cache.mjs +488 -0
- package/esm2022/lib/implementations/osd/tile-client.mjs +29 -0
- package/esm2022/lib/implementations/plotly/plotly-coordinate-transform.mjs +37 -0
- package/esm2022/lib/implementations/plotly/plotly-region-overlay.mjs +31 -0
- package/esm2022/lib/implementations/plotly/plotly-trace-builders.mjs +183 -0
- package/esm2022/lib/implementations/plotly/plotly.service.mjs +2137 -0
- package/esm2022/lib/implementations/simple-slice-access.service.mjs +99 -0
- package/esm2022/lib/models/bezier.mjs +120 -0
- package/esm2022/lib/models/class-preset.mjs +46 -0
- package/esm2022/lib/models/geometry.mjs +198 -0
- package/esm2022/lib/models/region.mjs +166 -0
- package/esm2022/lib/models/shape.mjs +53 -0
- package/esm2022/lib/plot.utilities.mjs +693 -0
- package/esm2022/lib/processing/image-converter.service.mjs +90 -0
- package/esm2022/lib/processing/image-crop.mjs +62 -0
- package/esm2022/lib/processing/processing-image.mjs +50 -0
- package/esm2022/lib/provide-visualization.mjs +63 -0
- package/esm2022/lib/region-editor/mask-raster.mjs +95 -0
- package/esm2022/lib/region-editor/mask-worker.mjs +10 -0
- package/esm2022/lib/region-editor/region-editor.component.mjs +1190 -0
- package/esm2022/lib/region-ops.service.mjs +301 -0
- package/esm2022/lib/render-orchestrator.mjs +87 -0
- package/esm2022/lib/routing-visualizer.service.mjs +533 -0
- package/esm2022/lib/store/class-color.util.mjs +52 -0
- package/esm2022/lib/store/region-store.service.mjs +1029 -0
- package/esm2022/lib/store/visualizer-store.service.mjs +352 -0
- package/esm2022/lib/toolbar/brush/brush-tool.service.mjs +546 -0
- package/esm2022/lib/toolbar/crop/slide-crop.mjs +57 -0
- package/esm2022/lib/toolbar/segmentation/cell-segment-tool.service.mjs +115 -0
- package/esm2022/lib/toolbar/segmentation/cellpose-segmenter.service.mjs +84 -0
- package/esm2022/lib/toolbar/segmentation/onnx-sam-session.mjs +133 -0
- package/esm2022/lib/toolbar/segmentation/ort-runtime-config.mjs +31 -0
- package/esm2022/lib/toolbar/segmentation/sam-model-registry.mjs +108 -0
- package/esm2022/lib/toolbar/segmentation/sam-onnx-core.mjs +159 -0
- package/esm2022/lib/toolbar/segmentation/sam-point-tool.service.mjs +255 -0
- package/esm2022/lib/toolbar/segmentation/sam-prompt.mjs +94 -0
- package/esm2022/lib/toolbar/segmentation/sam-tool.service.mjs +188 -0
- package/esm2022/lib/toolbar/toolbar.component.mjs +311 -0
- package/esm2022/lib/toolbar/vertex-eraser/vertex-eraser-tool.service.mjs +234 -0
- package/esm2022/lib/toolbar/wand/wand-tool.service.mjs +499 -0
- package/esm2022/lib/toolbar/wand/wand.service.mjs +921 -0
- package/esm2022/lib/toolbar/zoom-to-box/zoom-to-box-tool.service.mjs +157 -0
- package/esm2022/lib/visualization.module.mjs +162 -0
- package/esm2022/lib/visualizer.component.mjs +1597 -0
- package/fesm2022/jax-data-science-sci-image-visualizer-mask-worker-uhepXUhM.mjs +12 -0
- package/fesm2022/jax-data-science-sci-image-visualizer-mask-worker-uhepXUhM.mjs.map +1 -0
- package/fesm2022/jax-data-science-sci-image-visualizer-onnx-sam-session-ci1q0xnJ.mjs +327 -0
- package/fesm2022/jax-data-science-sci-image-visualizer-onnx-sam-session-ci1q0xnJ.mjs.map +1 -0
- package/fesm2022/jax-data-science-sci-image-visualizer.mjs +21314 -0
- package/fesm2022/jax-data-science-sci-image-visualizer.mjs.map +1 -0
- package/fesm2022/mask.worker.js +989 -0
- package/fesm2022/onnx-sam.worker.js +202 -0
- package/index.d.ts +39 -0
- package/lib/channel-histogram/channel-histogram.component.d.ts +112 -0
- package/lib/contracts/capabilities.contract.d.ts +30 -0
- package/lib/contracts/cell-segmenter.contract.d.ts +40 -0
- package/lib/contracts/channel-histogram-api.contract.d.ts +98 -0
- package/lib/contracts/colormap-lut.d.ts +21 -0
- package/lib/contracts/coordinate-transform.contract.d.ts +43 -0
- package/lib/contracts/display-types.d.ts +41 -0
- package/lib/contracts/image.contract.d.ts +72 -0
- package/lib/contracts/intensity.d.ts +18 -0
- package/lib/contracts/plot-type.d.ts +108 -0
- package/lib/contracts/ports/image-state.port.d.ts +29 -0
- package/lib/contracts/ports/preferences.port.d.ts +19 -0
- package/lib/contracts/ports/region-io.port.d.ts +29 -0
- package/lib/contracts/ports/tile-access.port.d.ts +27 -0
- package/lib/contracts/region-editor-api.contract.d.ts +83 -0
- package/lib/contracts/region-overlay.contract.d.ts +47 -0
- package/lib/contracts/region-store.contract.d.ts +74 -0
- package/lib/contracts/sam.contract.d.ts +98 -0
- package/lib/contracts/toolbar-config.d.ts +24 -0
- package/lib/contracts/visualizer.contract.d.ts +346 -0
- package/lib/contracts/viz-config.d.ts +25 -0
- package/lib/hex-color-picker/hex-color-picker.component.d.ts +47 -0
- package/lib/implementations/base-store-visualizer.d.ts +71 -0
- package/lib/implementations/napari-js/napari-axes-labels.d.ts +47 -0
- package/lib/implementations/napari-js/napari-region-overlay.d.ts +115 -0
- package/lib/implementations/napari-js/napari-scale-bar.d.ts +31 -0
- package/lib/implementations/napari-js/napari-visualizer.service.d.ts +455 -0
- package/lib/implementations/napari-js/napari-volume-z-handle.d.ts +46 -0
- package/lib/implementations/osd/display-pipeline.d.ts +47 -0
- package/lib/implementations/osd/histogram-sampler.d.ts +85 -0
- package/lib/implementations/osd/openseadragon-visualizer.service.d.ts +476 -0
- package/lib/implementations/osd/osd-coordinate-transform.d.ts +17 -0
- package/lib/implementations/osd/osd-coords.d.ts +16 -0
- package/lib/implementations/osd/osd-lib.d.ts +19 -0
- package/lib/implementations/osd/osd-region-overlay.d.ts +191 -0
- package/lib/implementations/osd/osd-scale-bar.d.ts +17 -0
- package/lib/implementations/osd/slice-cache.d.ts +161 -0
- package/lib/implementations/osd/tile-client.d.ts +32 -0
- package/lib/implementations/plotly/plotly-coordinate-transform.d.ts +19 -0
- package/lib/implementations/plotly/plotly-region-overlay.d.ts +23 -0
- package/lib/implementations/plotly/plotly-trace-builders.d.ts +60 -0
- package/lib/implementations/plotly/plotly.service.d.ts +594 -0
- package/lib/implementations/simple-slice-access.service.d.ts +64 -0
- package/lib/models/bezier.d.ts +59 -0
- package/lib/models/class-preset.d.ts +39 -0
- package/lib/models/geometry.d.ts +66 -0
- package/lib/models/region.d.ts +132 -0
- package/lib/models/shape.d.ts +44 -0
- package/lib/plot.utilities.d.ts +107 -0
- package/lib/processing/image-converter.service.d.ts +28 -0
- package/lib/processing/image-crop.d.ts +18 -0
- package/lib/processing/processing-image.d.ts +17 -0
- package/lib/provide-visualization.d.ts +34 -0
- package/lib/region-editor/mask-raster.d.ts +61 -0
- package/lib/region-editor/mask-worker.d.ts +7 -0
- package/lib/region-editor/region-editor.component.d.ts +305 -0
- package/lib/region-ops.service.d.ts +92 -0
- package/lib/render-orchestrator.d.ts +59 -0
- package/lib/routing-visualizer.service.d.ts +227 -0
- package/lib/store/class-color.util.d.ts +26 -0
- package/lib/store/region-store.service.d.ts +315 -0
- package/lib/store/visualizer-store.service.d.ts +123 -0
- package/lib/toolbar/brush/brush-tool.service.d.ts +143 -0
- package/lib/toolbar/crop/slide-crop.d.ts +30 -0
- package/lib/toolbar/segmentation/cell-segment-tool.service.d.ts +32 -0
- package/lib/toolbar/segmentation/cellpose-segmenter.service.d.ts +39 -0
- package/lib/toolbar/segmentation/onnx-sam-session.d.ts +25 -0
- package/lib/toolbar/segmentation/ort-runtime-config.d.ts +4 -0
- package/lib/toolbar/segmentation/sam-model-registry.d.ts +36 -0
- package/lib/toolbar/segmentation/sam-onnx-core.d.ts +48 -0
- package/lib/toolbar/segmentation/sam-point-tool.service.d.ts +59 -0
- package/lib/toolbar/segmentation/sam-prompt.d.ts +34 -0
- package/lib/toolbar/segmentation/sam-tool.service.d.ts +50 -0
- package/lib/toolbar/toolbar.component.d.ts +167 -0
- package/lib/toolbar/vertex-eraser/vertex-eraser-tool.service.d.ts +64 -0
- package/lib/toolbar/wand/wand-tool.service.d.ts +149 -0
- package/lib/toolbar/wand/wand.service.d.ts +133 -0
- package/lib/toolbar/zoom-to-box/zoom-to-box-tool.service.d.ts +49 -0
- package/lib/visualization.module.d.ts +45 -0
- package/lib/visualizer.component.d.ts +378 -0
- package/package.json +73 -0
- package/src/lib/assets/3d-coordinates.svg +49 -0
- package/src/lib/assets/cells.svg +1 -0
- package/src/lib/assets/click.svg +1 -0
- package/src/lib/assets/colormap-luts.json +1 -0
- package/src/lib/assets/contour.svg +48 -0
- package/src/lib/assets/cube-3d.svg +1 -0
- package/src/lib/assets/edit-selection-white.svg +47 -0
- package/src/lib/assets/heatmap.svg +1 -0
- package/src/lib/assets/icons/colormap-Pastel1.png +0 -0
- package/src/lib/assets/icons/colormap-accent.png +0 -0
- package/src/lib/assets/icons/colormap-afmhot.png +0 -0
- package/src/lib/assets/icons/colormap-autumn.png +0 -0
- package/src/lib/assets/icons/colormap-berlin.png +0 -0
- package/src/lib/assets/icons/colormap-binary.png +0 -0
- package/src/lib/assets/icons/colormap-blackbody.png +0 -0
- package/src/lib/assets/icons/colormap-bluered.png +0 -0
- package/src/lib/assets/icons/colormap-blues.png +0 -0
- package/src/lib/assets/icons/colormap-bone.png +0 -0
- package/src/lib/assets/icons/colormap-brbg.png +0 -0
- package/src/lib/assets/icons/colormap-brg.png +0 -0
- package/src/lib/assets/icons/colormap-bugn.png +0 -0
- package/src/lib/assets/icons/colormap-bupu.png +0 -0
- package/src/lib/assets/icons/colormap-bwr.png +0 -0
- package/src/lib/assets/icons/colormap-cividis.png +0 -0
- package/src/lib/assets/icons/colormap-cmrmap.png +0 -0
- package/src/lib/assets/icons/colormap-cool.png +0 -0
- package/src/lib/assets/icons/colormap-coolwarm.png +0 -0
- package/src/lib/assets/icons/colormap-copper.png +0 -0
- package/src/lib/assets/icons/colormap-cubehelix.png +0 -0
- package/src/lib/assets/icons/colormap-dark2.png +0 -0
- package/src/lib/assets/icons/colormap-earth.png +0 -0
- package/src/lib/assets/icons/colormap-electric.png +0 -0
- package/src/lib/assets/icons/colormap-flag.png +0 -0
- package/src/lib/assets/icons/colormap-gist_earth.png +0 -0
- package/src/lib/assets/icons/colormap-gist_gray.png +0 -0
- package/src/lib/assets/icons/colormap-gist_heat.png +0 -0
- package/src/lib/assets/icons/colormap-gist_ncar.png +0 -0
- package/src/lib/assets/icons/colormap-gist_rainbow.png +0 -0
- package/src/lib/assets/icons/colormap-gist_stern.png +0 -0
- package/src/lib/assets/icons/colormap-gist_yarg.png +0 -0
- package/src/lib/assets/icons/colormap-gnbu.png +0 -0
- package/src/lib/assets/icons/colormap-gnuplot.png +0 -0
- package/src/lib/assets/icons/colormap-gnuplot2.png +0 -0
- package/src/lib/assets/icons/colormap-gray.png +0 -0
- package/src/lib/assets/icons/colormap-greens-inv.png +0 -0
- package/src/lib/assets/icons/colormap-greens.png +0 -0
- package/src/lib/assets/icons/colormap-greys-inv.png +0 -0
- package/src/lib/assets/icons/colormap-greys.png +0 -0
- package/src/lib/assets/icons/colormap-hot.png +0 -0
- package/src/lib/assets/icons/colormap-hsv.png +0 -0
- package/src/lib/assets/icons/colormap-inferno.png +0 -0
- package/src/lib/assets/icons/colormap-jet.png +0 -0
- package/src/lib/assets/icons/colormap-magma.png +0 -0
- package/src/lib/assets/icons/colormap-managua.png +0 -0
- package/src/lib/assets/icons/colormap-nipy_spectral.png +0 -0
- package/src/lib/assets/icons/colormap-ocean.png +0 -0
- package/src/lib/assets/icons/colormap-oranges.png +0 -0
- package/src/lib/assets/icons/colormap-orrd.png +0 -0
- package/src/lib/assets/icons/colormap-pastel2.png +0 -0
- package/src/lib/assets/icons/colormap-picnic.png +0 -0
- package/src/lib/assets/icons/colormap-pink.png +0 -0
- package/src/lib/assets/icons/colormap-piyg.png +0 -0
- package/src/lib/assets/icons/colormap-placeholder.png +0 -0
- package/src/lib/assets/icons/colormap-plasma.png +0 -0
- package/src/lib/assets/icons/colormap-portland.png +0 -0
- package/src/lib/assets/icons/colormap-prgn.png +0 -0
- package/src/lib/assets/icons/colormap-prism.png +0 -0
- package/src/lib/assets/icons/colormap-pubu.png +0 -0
- package/src/lib/assets/icons/colormap-pubugn.png +0 -0
- package/src/lib/assets/icons/colormap-puor.png +0 -0
- package/src/lib/assets/icons/colormap-purd.png +0 -0
- package/src/lib/assets/icons/colormap-purples.png +0 -0
- package/src/lib/assets/icons/colormap-rainbow.png +0 -0
- package/src/lib/assets/icons/colormap-rdbu.png +0 -0
- package/src/lib/assets/icons/colormap-rdgy.png +0 -0
- package/src/lib/assets/icons/colormap-rdpu.png +0 -0
- package/src/lib/assets/icons/colormap-rdylbu.png +0 -0
- package/src/lib/assets/icons/colormap-rdylgn.png +0 -0
- package/src/lib/assets/icons/colormap-reds.png +0 -0
- package/src/lib/assets/icons/colormap-seismic.png +0 -0
- package/src/lib/assets/icons/colormap-set1.png +0 -0
- package/src/lib/assets/icons/colormap-set2.png +0 -0
- package/src/lib/assets/icons/colormap-set3.png +0 -0
- package/src/lib/assets/icons/colormap-spectral.png +0 -0
- package/src/lib/assets/icons/colormap-spring.png +0 -0
- package/src/lib/assets/icons/colormap-summer.png +0 -0
- package/src/lib/assets/icons/colormap-tab10.png +0 -0
- package/src/lib/assets/icons/colormap-tab20.png +0 -0
- package/src/lib/assets/icons/colormap-tab20b.png +0 -0
- package/src/lib/assets/icons/colormap-tab20c.png +0 -0
- package/src/lib/assets/icons/colormap-terrain.png +0 -0
- package/src/lib/assets/icons/colormap-turbo.png +0 -0
- package/src/lib/assets/icons/colormap-twilight.png +0 -0
- package/src/lib/assets/icons/colormap-twilight_shifted.png +0 -0
- package/src/lib/assets/icons/colormap-vanimo.png +0 -0
- package/src/lib/assets/icons/colormap-viridis.png +0 -0
- package/src/lib/assets/icons/colormap-winter.png +0 -0
- package/src/lib/assets/icons/colormap-wistia.png +0 -0
- package/src/lib/assets/icons/colormap-ylgn.png +0 -0
- package/src/lib/assets/icons/colormap-ylgnbu-inv.png +0 -0
- package/src/lib/assets/icons/colormap-ylgnbu.png +0 -0
- package/src/lib/assets/icons/colormap-ylorbr.png +0 -0
- package/src/lib/assets/icons/colormap-ylorrd-inv.png +0 -0
- package/src/lib/assets/icons/colormap-ylorrd.png +0 -0
- package/src/lib/assets/interactive-segmentation-cursor.svg +1 -0
- package/src/lib/assets/invert.svg +1 -0
- package/src/lib/assets/isosurface.svg +7 -0
- package/src/lib/assets/mask.svg +1 -0
- package/src/lib/assets/paintbrush.svg +1 -0
- package/src/lib/assets/picture-segmentation.svg +43 -0
- package/src/lib/assets/polygon-vertices.svg +13 -0
- package/src/lib/assets/polyline.svg +3 -0
- package/src/lib/assets/redo.svg +39 -0
- package/src/lib/assets/region-editor.svg +37 -0
- package/src/lib/assets/region-move.svg +7 -0
- package/src/lib/assets/smooth.svg +56 -0
- package/src/lib/assets/spline-chart.svg +7 -0
- package/src/lib/assets/surface.svg +37 -0
- package/src/lib/assets/test/download-379.svg +1 -0
- package/src/lib/assets/test/download-98.svg +1 -0
- package/src/lib/assets/test/json-1.svg +1 -0
- package/src/lib/assets/test/load-53.svg +1 -0
- package/src/lib/assets/test/mask-44.svg +1 -0
- package/src/lib/assets/test/mask-download-44.svg +15 -0
- package/src/lib/assets/test/mask-save-44.svg +11 -0
- package/src/lib/assets/test/overlay-1.svg +1 -0
- package/src/lib/assets/test/overlay-4.svg +1 -0
- package/src/lib/assets/test/region-26.svg +1 -0
- package/src/lib/assets/test/save-56.svg +1 -0
- package/src/lib/assets/test/upload-43.svg +1 -0
- package/src/lib/assets/to-bezier.svg +15 -0
- package/src/lib/assets/to-polygon.svg +11 -0
- package/src/lib/assets/undo.svg +38 -0
- package/src/lib/assets/vertex-add.svg +12 -0
- package/src/lib/assets/vertex-delete.svg +12 -0
- package/src/lib/assets/wand.svg +54 -0
- package/src/lib/assets/zoom-box.svg +10 -0
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
import { Injectable } from '@angular/core';
|
|
2
|
+
import { HttpClient } from '@angular/common/http';
|
|
3
|
+
import { firstValueFrom } from 'rxjs';
|
|
4
|
+
import * as i0 from "@angular/core";
|
|
5
|
+
import * as i1 from "@angular/common/http";
|
|
6
|
+
/**
|
|
7
|
+
* Shared access to a "simple" (`IImageInfo.tiled === false`) stack — a
|
|
8
|
+
* self-contained per-slice-URL image, as opposed to one server-tiled file
|
|
9
|
+
* with an internal z dimension (the tile-pyramid case each backend already
|
|
10
|
+
* handles on its own, since the tiling protocol itself IS backend-specific).
|
|
11
|
+
* A numbered image series assembled client-side from separate files (the
|
|
12
|
+
* jit-ui folder-stack feature) is the current producer of this shape.
|
|
13
|
+
*
|
|
14
|
+
* Every backend that can render a 2D image (OSD, napari-js) needs the exact
|
|
15
|
+
* same three things for this case — detect it, resolve `urls[z]`, and fetch
|
|
16
|
+
* that URL with the app's auth applied — so they live here once instead of
|
|
17
|
+
* being reimplemented per backend. Plotly needs no equivalent: its `load()`
|
|
18
|
+
* already fetches `urls[i]` generically for every plot type, with no
|
|
19
|
+
* tile-server concept to route around.
|
|
20
|
+
*
|
|
21
|
+
* Fetches go through Angular's `HttpClient` so the app's auth interceptor
|
|
22
|
+
* (Bearer token) applies — a raw `fetch()`/`<img src>` bypasses it and, behind
|
|
23
|
+
* an OAuth2-proxied deployment, gets redirected to the login page and then
|
|
24
|
+
* CORS-fails. Mirrors `PlotlyService.loadImage`, the reference
|
|
25
|
+
* implementation this generalizes for the other backends.
|
|
26
|
+
*/
|
|
27
|
+
export class SimpleSliceAccessService {
|
|
28
|
+
constructor(http) {
|
|
29
|
+
this.http = http;
|
|
30
|
+
/** raw slice URL → already-fetched blob: URL, so a backend that opens a
|
|
31
|
+
* URL (OSD) doesn't repeat the auth round-trip revisiting a slice (scrub
|
|
32
|
+
* back, the small→large tier re-fetch). Shared across backends: switching
|
|
33
|
+
* plot types on the same file (e.g. Image → Volume) reuses entries. */
|
|
34
|
+
this.blobUrls = new Map();
|
|
35
|
+
}
|
|
36
|
+
/** True when `imageInfo` is a self-contained per-slice-URL stack with no
|
|
37
|
+
* server tile pyramid to route to. */
|
|
38
|
+
isSimple(imageInfo) {
|
|
39
|
+
return imageInfo?.tiled === false;
|
|
40
|
+
}
|
|
41
|
+
/** The URL for slice `z` of a simple stack (falls back to slice 0 if `z`
|
|
42
|
+
* is out of range or unset). */
|
|
43
|
+
urlFor(imageInfo, z) {
|
|
44
|
+
return imageInfo.urls?.[z] ?? imageInfo.urls?.[0];
|
|
45
|
+
}
|
|
46
|
+
/** Call once per backend `load()` so the blob cache is evicted when a
|
|
47
|
+
* genuinely different file is selected — not on every phase/z-scrub of
|
|
48
|
+
* the SAME file, and not wrongly evicted by a second backend loading the
|
|
49
|
+
* same file for a different plot type. */
|
|
50
|
+
noteActiveFile(fileName) {
|
|
51
|
+
if (fileName && this.currentFileName && fileName !== this.currentFileName) {
|
|
52
|
+
this.revokeAll();
|
|
53
|
+
}
|
|
54
|
+
this.currentFileName = fileName;
|
|
55
|
+
}
|
|
56
|
+
/** Fetch a slice's own URL and hand back a blob: URL — for backends (OSD)
|
|
57
|
+
* that open a URL rather than decode bytes directly. A true in-memory
|
|
58
|
+
* blob:/data: URL (the processing-pipeline's original tiled:false use
|
|
59
|
+
* case) is already self-contained and returned as-is, no fetch/cache. */
|
|
60
|
+
async fetchAsBlobUrl(rawUrl) {
|
|
61
|
+
if (rawUrl.startsWith('blob:') || rawUrl.startsWith('data:'))
|
|
62
|
+
return rawUrl;
|
|
63
|
+
const cached = this.blobUrls.get(rawUrl);
|
|
64
|
+
if (cached)
|
|
65
|
+
return cached;
|
|
66
|
+
const blob = await firstValueFrom(this.http.get(rawUrl, { responseType: 'blob' }));
|
|
67
|
+
const blobUrl = URL.createObjectURL(blob);
|
|
68
|
+
this.blobUrls.set(rawUrl, blobUrl);
|
|
69
|
+
return blobUrl;
|
|
70
|
+
}
|
|
71
|
+
/** Fetch a slice's own URL and decode it as an `ImageBitmap` — for
|
|
72
|
+
* backends (napari-js) that upload pixels directly rather than opening a
|
|
73
|
+
* URL. Not cached (ImageBitmaps are consumed once and closed by the
|
|
74
|
+
* caller); the underlying HTTP response may still hit the browser cache.
|
|
75
|
+
*
|
|
76
|
+
* A self-contained blob:/data: URL (e.g. the processing-pipeline's
|
|
77
|
+
* in-memory preview, which also emits `tiled:false`) needs no auth and
|
|
78
|
+
* isn't a server URL to route through the interceptor — read it directly
|
|
79
|
+
* via the browser. Only real server URLs go through HttpClient so the auth
|
|
80
|
+
* interceptor applies. Mirrors {@link fetchAsBlobUrl}'s blob:/data: branch. */
|
|
81
|
+
async fetchAsBitmap(rawUrl) {
|
|
82
|
+
const blob = rawUrl.startsWith('blob:') || rawUrl.startsWith('data:')
|
|
83
|
+
? await (await fetch(rawUrl)).blob()
|
|
84
|
+
: await firstValueFrom(this.http.get(rawUrl, { responseType: 'blob' }));
|
|
85
|
+
return createImageBitmap(blob);
|
|
86
|
+
}
|
|
87
|
+
revokeAll() {
|
|
88
|
+
for (const url of this.blobUrls.values())
|
|
89
|
+
URL.revokeObjectURL(url);
|
|
90
|
+
this.blobUrls.clear();
|
|
91
|
+
}
|
|
92
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.3.9", ngImport: i0, type: SimpleSliceAccessService, deps: [{ token: i1.HttpClient }], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
93
|
+
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "17.3.9", ngImport: i0, type: SimpleSliceAccessService, providedIn: 'root' }); }
|
|
94
|
+
}
|
|
95
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.9", ngImport: i0, type: SimpleSliceAccessService, decorators: [{
|
|
96
|
+
type: Injectable,
|
|
97
|
+
args: [{ providedIn: 'root' }]
|
|
98
|
+
}], ctorParameters: () => [{ type: i1.HttpClient }] });
|
|
99
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic2ltcGxlLXNsaWNlLWFjY2Vzcy5zZXJ2aWNlLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vLi4vc3JjL2xpYi9pbXBsZW1lbnRhdGlvbnMvc2ltcGxlLXNsaWNlLWFjY2Vzcy5zZXJ2aWNlLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sRUFBRSxVQUFVLEVBQUUsTUFBTSxlQUFlLENBQUM7QUFDM0MsT0FBTyxFQUFFLFVBQVUsRUFBRSxNQUFNLHNCQUFzQixDQUFDO0FBQ2xELE9BQU8sRUFBRSxjQUFjLEVBQUUsTUFBTSxNQUFNLENBQUM7OztBQUl0Qzs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7R0FvQkc7QUFFSCxNQUFNLE9BQU8sd0JBQXdCO0lBUW5DLFlBQW9CLElBQWdCO1FBQWhCLFNBQUksR0FBSixJQUFJLENBQVk7UUFQcEM7OztnRkFHd0U7UUFDdkQsYUFBUSxHQUFHLElBQUksR0FBRyxFQUFrQixDQUFDO0lBR2YsQ0FBQztJQUV4QzsyQ0FDdUM7SUFDdkMsUUFBUSxDQUFDLFNBQXdDO1FBQy9DLE9BQU8sU0FBUyxFQUFFLEtBQUssS0FBSyxLQUFLLENBQUM7SUFDcEMsQ0FBQztJQUVEO3FDQUNpQztJQUNqQyxNQUFNLENBQUMsU0FBcUIsRUFBRSxDQUFTO1FBQ3JDLE9BQU8sU0FBUyxDQUFDLElBQUksRUFBRSxDQUFDLENBQUMsQ0FBQyxJQUFJLFNBQVMsQ0FBQyxJQUFJLEVBQUUsQ0FBQyxDQUFDLENBQUMsQ0FBQztJQUNwRCxDQUFDO0lBRUQ7OzsrQ0FHMkM7SUFDM0MsY0FBYyxDQUFDLFFBQTRCO1FBQ3pDLElBQUksUUFBUSxJQUFJLElBQUksQ0FBQyxlQUFlLElBQUksUUFBUSxLQUFLLElBQUksQ0FBQyxlQUFlLEVBQUUsQ0FBQztZQUMxRSxJQUFJLENBQUMsU0FBUyxFQUFFLENBQUM7UUFDbkIsQ0FBQztRQUNELElBQUksQ0FBQyxlQUFlLEdBQUcsUUFBUSxDQUFDO0lBQ2xDLENBQUM7SUFFRDs7OzhFQUcwRTtJQUMxRSxLQUFLLENBQUMsY0FBYyxDQUFDLE1BQWM7UUFDakMsSUFBSSxNQUFNLENBQUMsVUFBVSxDQUFDLE9BQU8sQ0FBQyxJQUFJLE1BQU0sQ0FBQyxVQUFVLENBQUMsT0FBTyxDQUFDO1lBQUUsT0FBTyxNQUFNLENBQUM7UUFDNUUsTUFBTSxNQUFNLEdBQUcsSUFBSSxDQUFDLFFBQVEsQ0FBQyxHQUFHLENBQUMsTUFBTSxDQUFDLENBQUM7UUFDekMsSUFBSSxNQUFNO1lBQUUsT0FBTyxNQUFNLENBQUM7UUFDMUIsTUFBTSxJQUFJLEdBQUcsTUFBTSxjQUFjLENBQUMsSUFBSSxDQUFDLElBQUksQ0FBQyxHQUFHLENBQUMsTUFBTSxFQUFFLEVBQUUsWUFBWSxFQUFFLE1BQU0sRUFBRSxDQUFDLENBQUMsQ0FBQztRQUNuRixNQUFNLE9BQU8sR0FBRyxHQUFHLENBQUMsZUFBZSxDQUFDLElBQUksQ0FBQyxDQUFDO1FBQzFDLElBQUksQ0FBQyxRQUFRLENBQUMsR0FBRyxDQUFDLE1BQU0sRUFBRSxPQUFPLENBQUMsQ0FBQztRQUNuQyxPQUFPLE9BQU8sQ0FBQztJQUNqQixDQUFDO0lBRUQ7Ozs7Ozs7OztvRkFTZ0Y7SUFDaEYsS0FBSyxDQUFDLGFBQWEsQ0FBQyxNQUFjO1FBQ2hDLE1BQU0sSUFBSSxHQUFHLE1BQU0sQ0FBQyxVQUFVLENBQUMsT0FBTyxDQUFDLElBQUksTUFBTSxDQUFDLFVBQVUsQ0FBQyxPQUFPLENBQUM7WUFDbkUsQ0FBQyxDQUFDLE1BQU0sQ0FBQyxNQUFNLEtBQUssQ0FBQyxNQUFNLENBQUMsQ0FBQyxDQUFDLElBQUksRUFBRTtZQUNwQyxDQUFDLENBQUMsTUFBTSxjQUFjLENBQUMsSUFBSSxDQUFDLElBQUksQ0FBQyxHQUFHLENBQUMsTUFBTSxFQUFFLEVBQUUsWUFBWSxFQUFFLE1BQU0sRUFBRSxDQUFDLENBQUMsQ0FBQztRQUMxRSxPQUFPLGlCQUFpQixDQUFDLElBQUksQ0FBQyxDQUFDO0lBQ2pDLENBQUM7SUFFTyxTQUFTO1FBQ2YsS0FBSyxNQUFNLEdBQUcsSUFBSSxJQUFJLENBQUMsUUFBUSxDQUFDLE1BQU0sRUFBRTtZQUFFLEdBQUcsQ0FBQyxlQUFlLENBQUMsR0FBRyxDQUFDLENBQUM7UUFDbkUsSUFBSSxDQUFDLFFBQVEsQ0FBQyxLQUFLLEVBQUUsQ0FBQztJQUN4QixDQUFDOzhHQW5FVSx3QkFBd0I7a0hBQXhCLHdCQUF3QixjQURYLE1BQU07OzJGQUNuQix3QkFBd0I7a0JBRHBDLFVBQVU7bUJBQUMsRUFBRSxVQUFVLEVBQUUsTUFBTSxFQUFFIiwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IHsgSW5qZWN0YWJsZSB9IGZyb20gJ0Bhbmd1bGFyL2NvcmUnO1xuaW1wb3J0IHsgSHR0cENsaWVudCB9IGZyb20gJ0Bhbmd1bGFyL2NvbW1vbi9odHRwJztcbmltcG9ydCB7IGZpcnN0VmFsdWVGcm9tIH0gZnJvbSAncnhqcyc7XG5cbmltcG9ydCB7IElJbWFnZUluZm8gfSBmcm9tICcuLi9jb250cmFjdHMvaW1hZ2UuY29udHJhY3QnO1xuXG4vKipcbiAqIFNoYXJlZCBhY2Nlc3MgdG8gYSBcInNpbXBsZVwiIChgSUltYWdlSW5mby50aWxlZCA9PT0gZmFsc2VgKSBzdGFjayDigJQgYVxuICogc2VsZi1jb250YWluZWQgcGVyLXNsaWNlLVVSTCBpbWFnZSwgYXMgb3Bwb3NlZCB0byBvbmUgc2VydmVyLXRpbGVkIGZpbGVcbiAqIHdpdGggYW4gaW50ZXJuYWwgeiBkaW1lbnNpb24gKHRoZSB0aWxlLXB5cmFtaWQgY2FzZSBlYWNoIGJhY2tlbmQgYWxyZWFkeVxuICogaGFuZGxlcyBvbiBpdHMgb3duLCBzaW5jZSB0aGUgdGlsaW5nIHByb3RvY29sIGl0c2VsZiBJUyBiYWNrZW5kLXNwZWNpZmljKS5cbiAqIEEgbnVtYmVyZWQgaW1hZ2Ugc2VyaWVzIGFzc2VtYmxlZCBjbGllbnQtc2lkZSBmcm9tIHNlcGFyYXRlIGZpbGVzICh0aGVcbiAqIGppdC11aSBmb2xkZXItc3RhY2sgZmVhdHVyZSkgaXMgdGhlIGN1cnJlbnQgcHJvZHVjZXIgb2YgdGhpcyBzaGFwZS5cbiAqXG4gKiBFdmVyeSBiYWNrZW5kIHRoYXQgY2FuIHJlbmRlciBhIDJEIGltYWdlIChPU0QsIG5hcGFyaS1qcykgbmVlZHMgdGhlIGV4YWN0XG4gKiBzYW1lIHRocmVlIHRoaW5ncyBmb3IgdGhpcyBjYXNlIOKAlCBkZXRlY3QgaXQsIHJlc29sdmUgYHVybHNbel1gLCBhbmQgZmV0Y2hcbiAqIHRoYXQgVVJMIHdpdGggdGhlIGFwcCdzIGF1dGggYXBwbGllZCDigJQgc28gdGhleSBsaXZlIGhlcmUgb25jZSBpbnN0ZWFkIG9mXG4gKiBiZWluZyByZWltcGxlbWVudGVkIHBlciBiYWNrZW5kLiBQbG90bHkgbmVlZHMgbm8gZXF1aXZhbGVudDogaXRzIGBsb2FkKClgXG4gKiBhbHJlYWR5IGZldGNoZXMgYHVybHNbaV1gIGdlbmVyaWNhbGx5IGZvciBldmVyeSBwbG90IHR5cGUsIHdpdGggbm9cbiAqIHRpbGUtc2VydmVyIGNvbmNlcHQgdG8gcm91dGUgYXJvdW5kLlxuICpcbiAqIEZldGNoZXMgZ28gdGhyb3VnaCBBbmd1bGFyJ3MgYEh0dHBDbGllbnRgIHNvIHRoZSBhcHAncyBhdXRoIGludGVyY2VwdG9yXG4gKiAoQmVhcmVyIHRva2VuKSBhcHBsaWVzIOKAlCBhIHJhdyBgZmV0Y2goKWAvYDxpbWcgc3JjPmAgYnlwYXNzZXMgaXQgYW5kLCBiZWhpbmRcbiAqIGFuIE9BdXRoMi1wcm94aWVkIGRlcGxveW1lbnQsIGdldHMgcmVkaXJlY3RlZCB0byB0aGUgbG9naW4gcGFnZSBhbmQgdGhlblxuICogQ09SUy1mYWlscy4gTWlycm9ycyBgUGxvdGx5U2VydmljZS5sb2FkSW1hZ2VgLCB0aGUgcmVmZXJlbmNlXG4gKiBpbXBsZW1lbnRhdGlvbiB0aGlzIGdlbmVyYWxpemVzIGZvciB0aGUgb3RoZXIgYmFja2VuZHMuXG4gKi9cbkBJbmplY3RhYmxlKHsgcHJvdmlkZWRJbjogJ3Jvb3QnIH0pXG5leHBvcnQgY2xhc3MgU2ltcGxlU2xpY2VBY2Nlc3NTZXJ2aWNlIHtcbiAgLyoqIHJhdyBzbGljZSBVUkwg4oaSIGFscmVhZHktZmV0Y2hlZCBibG9iOiBVUkwsIHNvIGEgYmFja2VuZCB0aGF0IG9wZW5zIGFcbiAgICogIFVSTCAoT1NEKSBkb2Vzbid0IHJlcGVhdCB0aGUgYXV0aCByb3VuZC10cmlwIHJldmlzaXRpbmcgYSBzbGljZSAoc2NydWJcbiAgICogIGJhY2ssIHRoZSBzbWFsbOKGkmxhcmdlIHRpZXIgcmUtZmV0Y2gpLiBTaGFyZWQgYWNyb3NzIGJhY2tlbmRzOiBzd2l0Y2hpbmdcbiAgICogIHBsb3QgdHlwZXMgb24gdGhlIHNhbWUgZmlsZSAoZS5nLiBJbWFnZSDihpIgVm9sdW1lKSByZXVzZXMgZW50cmllcy4gKi9cbiAgcHJpdmF0ZSByZWFkb25seSBibG9iVXJscyA9IG5ldyBNYXA8c3RyaW5nLCBzdHJpbmc+KCk7XG4gIHByaXZhdGUgY3VycmVudEZpbGVOYW1lOiBzdHJpbmcgfCB1bmRlZmluZWQ7XG5cbiAgY29uc3RydWN0b3IocHJpdmF0ZSBodHRwOiBIdHRwQ2xpZW50KSB7fVxuXG4gIC8qKiBUcnVlIHdoZW4gYGltYWdlSW5mb2AgaXMgYSBzZWxmLWNvbnRhaW5lZCBwZXItc2xpY2UtVVJMIHN0YWNrIHdpdGggbm9cbiAgICogIHNlcnZlciB0aWxlIHB5cmFtaWQgdG8gcm91dGUgdG8uICovXG4gIGlzU2ltcGxlKGltYWdlSW5mbzogSUltYWdlSW5mbyB8IG51bGwgfCB1bmRlZmluZWQpOiBib29sZWFuIHtcbiAgICByZXR1cm4gaW1hZ2VJbmZvPy50aWxlZCA9PT0gZmFsc2U7XG4gIH1cblxuICAvKiogVGhlIFVSTCBmb3Igc2xpY2UgYHpgIG9mIGEgc2ltcGxlIHN0YWNrIChmYWxscyBiYWNrIHRvIHNsaWNlIDAgaWYgYHpgXG4gICAqICBpcyBvdXQgb2YgcmFuZ2Ugb3IgdW5zZXQpLiAqL1xuICB1cmxGb3IoaW1hZ2VJbmZvOiBJSW1hZ2VJbmZvLCB6OiBudW1iZXIpOiBzdHJpbmcgfCB1bmRlZmluZWQge1xuICAgIHJldHVybiBpbWFnZUluZm8udXJscz8uW3pdID8/IGltYWdlSW5mby51cmxzPy5bMF07XG4gIH1cblxuICAvKiogQ2FsbCBvbmNlIHBlciBiYWNrZW5kIGBsb2FkKClgIHNvIHRoZSBibG9iIGNhY2hlIGlzIGV2aWN0ZWQgd2hlbiBhXG4gICAqICBnZW51aW5lbHkgZGlmZmVyZW50IGZpbGUgaXMgc2VsZWN0ZWQg4oCUIG5vdCBvbiBldmVyeSBwaGFzZS96LXNjcnViIG9mXG4gICAqICB0aGUgU0FNRSBmaWxlLCBhbmQgbm90IHdyb25nbHkgZXZpY3RlZCBieSBhIHNlY29uZCBiYWNrZW5kIGxvYWRpbmcgdGhlXG4gICAqICBzYW1lIGZpbGUgZm9yIGEgZGlmZmVyZW50IHBsb3QgdHlwZS4gKi9cbiAgbm90ZUFjdGl2ZUZpbGUoZmlsZU5hbWU6IHN0cmluZyB8IHVuZGVmaW5lZCk6IHZvaWQge1xuICAgIGlmIChmaWxlTmFtZSAmJiB0aGlzLmN1cnJlbnRGaWxlTmFtZSAmJiBmaWxlTmFtZSAhPT0gdGhpcy5jdXJyZW50RmlsZU5hbWUpIHtcbiAgICAgIHRoaXMucmV2b2tlQWxsKCk7XG4gICAgfVxuICAgIHRoaXMuY3VycmVudEZpbGVOYW1lID0gZmlsZU5hbWU7XG4gIH1cblxuICAvKiogRmV0Y2ggYSBzbGljZSdzIG93biBVUkwgYW5kIGhhbmQgYmFjayBhIGJsb2I6IFVSTCDigJQgZm9yIGJhY2tlbmRzIChPU0QpXG4gICAqICB0aGF0IG9wZW4gYSBVUkwgcmF0aGVyIHRoYW4gZGVjb2RlIGJ5dGVzIGRpcmVjdGx5LiBBIHRydWUgaW4tbWVtb3J5XG4gICAqICBibG9iOi9kYXRhOiBVUkwgKHRoZSBwcm9jZXNzaW5nLXBpcGVsaW5lJ3Mgb3JpZ2luYWwgdGlsZWQ6ZmFsc2UgdXNlXG4gICAqICBjYXNlKSBpcyBhbHJlYWR5IHNlbGYtY29udGFpbmVkIGFuZCByZXR1cm5lZCBhcy1pcywgbm8gZmV0Y2gvY2FjaGUuICovXG4gIGFzeW5jIGZldGNoQXNCbG9iVXJsKHJhd1VybDogc3RyaW5nKTogUHJvbWlzZTxzdHJpbmc+IHtcbiAgICBpZiAocmF3VXJsLnN0YXJ0c1dpdGgoJ2Jsb2I6JykgfHwgcmF3VXJsLnN0YXJ0c1dpdGgoJ2RhdGE6JykpIHJldHVybiByYXdVcmw7XG4gICAgY29uc3QgY2FjaGVkID0gdGhpcy5ibG9iVXJscy5nZXQocmF3VXJsKTtcbiAgICBpZiAoY2FjaGVkKSByZXR1cm4gY2FjaGVkO1xuICAgIGNvbnN0IGJsb2IgPSBhd2FpdCBmaXJzdFZhbHVlRnJvbSh0aGlzLmh0dHAuZ2V0KHJhd1VybCwgeyByZXNwb25zZVR5cGU6ICdibG9iJyB9KSk7XG4gICAgY29uc3QgYmxvYlVybCA9IFVSTC5jcmVhdGVPYmplY3RVUkwoYmxvYik7XG4gICAgdGhpcy5ibG9iVXJscy5zZXQocmF3VXJsLCBibG9iVXJsKTtcbiAgICByZXR1cm4gYmxvYlVybDtcbiAgfVxuXG4gIC8qKiBGZXRjaCBhIHNsaWNlJ3Mgb3duIFVSTCBhbmQgZGVjb2RlIGl0IGFzIGFuIGBJbWFnZUJpdG1hcGAg4oCUIGZvclxuICAgKiAgYmFja2VuZHMgKG5hcGFyaS1qcykgdGhhdCB1cGxvYWQgcGl4ZWxzIGRpcmVjdGx5IHJhdGhlciB0aGFuIG9wZW5pbmcgYVxuICAgKiAgVVJMLiBOb3QgY2FjaGVkIChJbWFnZUJpdG1hcHMgYXJlIGNvbnN1bWVkIG9uY2UgYW5kIGNsb3NlZCBieSB0aGVcbiAgICogIGNhbGxlcik7IHRoZSB1bmRlcmx5aW5nIEhUVFAgcmVzcG9uc2UgbWF5IHN0aWxsIGhpdCB0aGUgYnJvd3NlciBjYWNoZS5cbiAgICpcbiAgICogIEEgc2VsZi1jb250YWluZWQgYmxvYjovZGF0YTogVVJMIChlLmcuIHRoZSBwcm9jZXNzaW5nLXBpcGVsaW5lJ3NcbiAgICogIGluLW1lbW9yeSBwcmV2aWV3LCB3aGljaCBhbHNvIGVtaXRzIGB0aWxlZDpmYWxzZWApIG5lZWRzIG5vIGF1dGggYW5kXG4gICAqICBpc24ndCBhIHNlcnZlciBVUkwgdG8gcm91dGUgdGhyb3VnaCB0aGUgaW50ZXJjZXB0b3Ig4oCUIHJlYWQgaXQgZGlyZWN0bHlcbiAgICogIHZpYSB0aGUgYnJvd3Nlci4gT25seSByZWFsIHNlcnZlciBVUkxzIGdvIHRocm91Z2ggSHR0cENsaWVudCBzbyB0aGUgYXV0aFxuICAgKiAgaW50ZXJjZXB0b3IgYXBwbGllcy4gTWlycm9ycyB7QGxpbmsgZmV0Y2hBc0Jsb2JVcmx9J3MgYmxvYjovZGF0YTogYnJhbmNoLiAqL1xuICBhc3luYyBmZXRjaEFzQml0bWFwKHJhd1VybDogc3RyaW5nKTogUHJvbWlzZTxJbWFnZUJpdG1hcD4ge1xuICAgIGNvbnN0IGJsb2IgPSByYXdVcmwuc3RhcnRzV2l0aCgnYmxvYjonKSB8fCByYXdVcmwuc3RhcnRzV2l0aCgnZGF0YTonKVxuICAgICAgPyBhd2FpdCAoYXdhaXQgZmV0Y2gocmF3VXJsKSkuYmxvYigpXG4gICAgICA6IGF3YWl0IGZpcnN0VmFsdWVGcm9tKHRoaXMuaHR0cC5nZXQocmF3VXJsLCB7IHJlc3BvbnNlVHlwZTogJ2Jsb2InIH0pKTtcbiAgICByZXR1cm4gY3JlYXRlSW1hZ2VCaXRtYXAoYmxvYik7XG4gIH1cblxuICBwcml2YXRlIHJldm9rZUFsbCgpOiB2b2lkIHtcbiAgICBmb3IgKGNvbnN0IHVybCBvZiB0aGlzLmJsb2JVcmxzLnZhbHVlcygpKSBVUkwucmV2b2tlT2JqZWN0VVJMKHVybCk7XG4gICAgdGhpcy5ibG9iVXJscy5jbGVhcigpO1xuICB9XG59XG4iXX0=
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Bezier-curve helpers (no external dependency).
|
|
3
|
+
*
|
|
4
|
+
* JIT supports true bezier regions: a polygon's vertices are the editable
|
|
5
|
+
* *anchors*, and the smooth curve through them is derived here with the
|
|
6
|
+
* Catmull-Rom construction — the same algorithm paper.js uses in
|
|
7
|
+
* `path.smooth()`. The curve interpolates every anchor and
|
|
8
|
+
* each anchor's two handles are colinear (a smooth tangent).
|
|
9
|
+
*
|
|
10
|
+
* The OSD overlay renders the curve as a true cubic SVG path + draggable handles
|
|
11
|
+
* (see {@link bezierAnchorHandles}); GeoJSON export flattens the *same* cubics
|
|
12
|
+
* into a dense point list (see {@link bezierCurve}) so a viewer without bezier
|
|
13
|
+
* support (e.g. QuPath, which just draws the coordinate list) still shows the
|
|
14
|
+
* smooth shape. Using one algorithm for both keeps what JIT draws identical to
|
|
15
|
+
* what it exports.
|
|
16
|
+
*
|
|
17
|
+
* Pure functions: no DOM, no service state. Coordinates are image pixels.
|
|
18
|
+
*/
|
|
19
|
+
/** How many points to sample per cubic segment when flattening for export. */
|
|
20
|
+
const SAMPLES_PER_SEGMENT = 16;
|
|
21
|
+
/**
|
|
22
|
+
* Per-anchor cubic-bezier control points using the Catmull-Rom construction
|
|
23
|
+
* (tension 1: control = anchor ± (next − prev) / 6). Matches paper.js
|
|
24
|
+
* `path.smooth()`: the resulting cubic interpolates every anchor and the two
|
|
25
|
+
* handles of each anchor are colinear. Coordinates are in the input space.
|
|
26
|
+
*/
|
|
27
|
+
export function bezierAnchorHandles(xs, ys, closed) {
|
|
28
|
+
const n = Math.min(xs.length, ys.length);
|
|
29
|
+
const out = [];
|
|
30
|
+
for (let i = 0; i < n; i++) {
|
|
31
|
+
const prev = closed ? (i - 1 + n) % n : Math.max(0, i - 1);
|
|
32
|
+
const next = closed ? (i + 1) % n : Math.min(n - 1, i + 1);
|
|
33
|
+
const tx = (xs[next] - xs[prev]) / 6;
|
|
34
|
+
const ty = (ys[next] - ys[prev]) / 6;
|
|
35
|
+
out.push({
|
|
36
|
+
out: [xs[i] + tx, ys[i] + ty],
|
|
37
|
+
in: [xs[i] - tx, ys[i] - ty],
|
|
38
|
+
hasOut: closed || i < n - 1,
|
|
39
|
+
hasIn: closed || i > 0,
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
return out;
|
|
43
|
+
}
|
|
44
|
+
/** The Catmull-Rom handles as **relative** offsets `[dx, dy]` per anchor — used
|
|
45
|
+
* to initialise a region's editable handles when bezier is turned on. */
|
|
46
|
+
export function defaultHandleOffsets(xs, ys, closed) {
|
|
47
|
+
const h = bezierAnchorHandles(xs, ys, closed);
|
|
48
|
+
return {
|
|
49
|
+
in: h.map((a, i) => [a.in[0] - xs[i], a.in[1] - ys[i]]),
|
|
50
|
+
out: h.map((a, i) => [a.out[0] - xs[i], a.out[1] - ys[i]]),
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
/** Absolute handles from stored relative offsets (anchor + offset). */
|
|
54
|
+
export function handlesFromOffsets(xs, ys, inOff, outOff, closed) {
|
|
55
|
+
const n = Math.min(xs.length, ys.length);
|
|
56
|
+
const out = [];
|
|
57
|
+
for (let i = 0; i < n; i++) {
|
|
58
|
+
out.push({
|
|
59
|
+
in: [xs[i] + (inOff[i]?.[0] ?? 0), ys[i] + (inOff[i]?.[1] ?? 0)],
|
|
60
|
+
out: [xs[i] + (outOff[i]?.[0] ?? 0), ys[i] + (outOff[i]?.[1] ?? 0)],
|
|
61
|
+
hasOut: closed || i < n - 1,
|
|
62
|
+
hasIn: closed || i > 0,
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
return out;
|
|
66
|
+
}
|
|
67
|
+
/** A region's handles: from its stored (editable) offsets when present, else the
|
|
68
|
+
* Catmull-Rom default. The single source of truth for both rendering and export. */
|
|
69
|
+
export function resolveHandles(xs, ys, closed, inOff, outOff) {
|
|
70
|
+
if (inOff && outOff && inOff.length === xs.length && outOff.length === xs.length) {
|
|
71
|
+
return handlesFromOffsets(xs, ys, inOff, outOff, closed);
|
|
72
|
+
}
|
|
73
|
+
return bezierAnchorHandles(xs, ys, closed);
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Flatten a cubic bezier (defined by explicit per-anchor handles) into a dense
|
|
77
|
+
* point list by sampling each segment. The curve passes through every anchor; a
|
|
78
|
+
* closed ring returns to its first point.
|
|
79
|
+
*/
|
|
80
|
+
export function bezierCurveFromHandles(xs, ys, handles, closed) {
|
|
81
|
+
const n = Math.min(xs.length, ys.length);
|
|
82
|
+
if (n < 3)
|
|
83
|
+
return { xs: xs.slice(), ys: ys.slice() };
|
|
84
|
+
const outX = [];
|
|
85
|
+
const outY = [];
|
|
86
|
+
const segs = closed ? n : n - 1;
|
|
87
|
+
for (let i = 0; i < segs; i++) {
|
|
88
|
+
const j = (i + 1) % n;
|
|
89
|
+
const p0x = xs[i], p0y = ys[i];
|
|
90
|
+
const c1 = handles[i].out, c2 = handles[j].in;
|
|
91
|
+
const p3x = xs[j], p3y = ys[j];
|
|
92
|
+
for (let s = 0; s < SAMPLES_PER_SEGMENT; s++) {
|
|
93
|
+
const t = s / SAMPLES_PER_SEGMENT;
|
|
94
|
+
const u = 1 - t;
|
|
95
|
+
const w0 = u * u * u, w1 = 3 * u * u * t, w2 = 3 * u * t * t, w3 = t * t * t;
|
|
96
|
+
outX.push(w0 * p0x + w1 * c1[0] + w2 * c2[0] + w3 * p3x);
|
|
97
|
+
outY.push(w0 * p0y + w1 * c1[1] + w2 * c2[1] + w3 * p3y);
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
if (closed) {
|
|
101
|
+
outX.push(xs[0]);
|
|
102
|
+
outY.push(ys[0]);
|
|
103
|
+
}
|
|
104
|
+
else {
|
|
105
|
+
outX.push(xs[n - 1]);
|
|
106
|
+
outY.push(ys[n - 1]);
|
|
107
|
+
}
|
|
108
|
+
return { xs: outX, ys: outY };
|
|
109
|
+
}
|
|
110
|
+
/**
|
|
111
|
+
* Flatten the smooth Catmull-Rom bezier through the anchors (no custom handles).
|
|
112
|
+
* Falls back to the anchors when there are too few to form a curve.
|
|
113
|
+
*/
|
|
114
|
+
export function bezierCurve(xs, ys, closed) {
|
|
115
|
+
const n = Math.min(xs.length, ys.length);
|
|
116
|
+
if (n < 3)
|
|
117
|
+
return { xs: xs.slice(), ys: ys.slice() };
|
|
118
|
+
return bezierCurveFromHandles(xs, ys, bezierAnchorHandles(xs, ys, closed), closed);
|
|
119
|
+
}
|
|
120
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiYmV6aWVyLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vLi4vc3JjL2xpYi9tb2RlbHMvYmV6aWVyLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBOzs7Ozs7Ozs7Ozs7Ozs7OztHQWlCRztBQWdCSCw4RUFBOEU7QUFDOUUsTUFBTSxtQkFBbUIsR0FBRyxFQUFFLENBQUM7QUFFL0I7Ozs7O0dBS0c7QUFDSCxNQUFNLFVBQVUsbUJBQW1CLENBQUMsRUFBWSxFQUFFLEVBQVksRUFBRSxNQUFlO0lBQzdFLE1BQU0sQ0FBQyxHQUFHLElBQUksQ0FBQyxHQUFHLENBQUMsRUFBRSxDQUFDLE1BQU0sRUFBRSxFQUFFLENBQUMsTUFBTSxDQUFDLENBQUM7SUFDekMsTUFBTSxHQUFHLEdBQW1CLEVBQUUsQ0FBQztJQUMvQixLQUFLLElBQUksQ0FBQyxHQUFHLENBQUMsRUFBRSxDQUFDLEdBQUcsQ0FBQyxFQUFFLENBQUMsRUFBRSxFQUFFLENBQUM7UUFDM0IsTUFBTSxJQUFJLEdBQUcsTUFBTSxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUMsR0FBRyxDQUFDLEdBQUcsQ0FBQyxDQUFDLEdBQUcsQ0FBQyxDQUFDLENBQUMsQ0FBQyxJQUFJLENBQUMsR0FBRyxDQUFDLENBQUMsRUFBRSxDQUFDLEdBQUcsQ0FBQyxDQUFDLENBQUM7UUFDM0QsTUFBTSxJQUFJLEdBQUcsTUFBTSxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUMsR0FBRyxDQUFDLENBQUMsR0FBRyxDQUFDLENBQUMsQ0FBQyxDQUFDLElBQUksQ0FBQyxHQUFHLENBQUMsQ0FBQyxHQUFHLENBQUMsRUFBRSxDQUFDLEdBQUcsQ0FBQyxDQUFDLENBQUM7UUFDM0QsTUFBTSxFQUFFLEdBQUcsQ0FBQyxFQUFFLENBQUMsSUFBSSxDQUFDLEdBQUcsRUFBRSxDQUFDLElBQUksQ0FBQyxDQUFDLEdBQUcsQ0FBQyxDQUFDO1FBQ3JDLE1BQU0sRUFBRSxHQUFHLENBQUMsRUFBRSxDQUFDLElBQUks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LEdBQUcsQ0FBQyxDQUFDLEdBQUcsQ0FBQyxDQUFDLEdBQUcsQ0FBQyxDQUFDO1FBQ3RCLE1BQU0sR0FBRyxHQUFHLEVBQUUsQ0FBQyxDQUFDLENBQUMsRUFBRSxHQUFHLEdBQUcsRUFBRSxDQUFDLENBQUMsQ0FBQyxDQUFDO1FBQy9CLE1BQU0sRUFBRSxHQUFHLE9BQU8sQ0FBQyxDQUFDLENBQUMsQ0FBQyxHQUFHLEVBQUUsRUFBRSxHQUFHLE9BQU8sQ0FBQyxDQUFDLENBQUMsQ0FBQyxFQUFFLENBQUM7UUFDOUMsTUFBTSxHQUFHLEdBQUcsRUFBRSxDQUFDLENBQUMsQ0FBQyxFQUFFLEdBQUcsR0FBRyxFQUFFLENBQUMsQ0FBQyxDQUFDLENBQUM7UUFDL0IsS0FBSyxJQUFJLENBQUMsR0FBRyxDQUFDLEVBQUUsQ0FBQyxHQUFHLG1CQUFtQixFQUFFLENBQUMsRUFBRSxFQUFFLENBQUM7WUFDN0MsTUFBTSxDQUFDLEdBQUcsQ0FBQyxHQUFHLG1CQUFtQixDQUFDO1lBQ2xDLE1BQU0sQ0FBQyxHQUFHLENBQUMsR0FBRyxDQUFDLENBQUM7WUFDaEIsTUFBTSxFQUFFLEdBQUcsQ0FBQyxHQUFHLENBQUMsR0FBRyxDQUFDLEVBQUUsRUFBRSxHQUFHLENBQUMsR0FBRyxDQUFDLEdBQUcsQ0FBQyxHQUFHLENBQUMsRUFBRSxFQUFFLEdBQUcsQ0FBQyxHQUFHLENBQUMsR0FBRyxDQUFDLEdBQUcsQ0FBQyxFQUFFLEVBQUUsR0FBRyxDQUFDLEdBQUcsQ0FBQyxHQUFHLENBQUMsQ0FBQztZQUM3RSxJQUFJLENBQUMsSUFBSSxDQUFDLEVBQUUsR0FBRyxHQUFHLEdBQUcsRUFBRSxHQUFHLEVBQUUsQ0FBQyxDQUFDLENBQUMsR0FBRyxFQUFFLEdBQUcsRUFBRSxDQUFDLENBQUMsQ0FBQyxHQUFHLEVBQUUsR0FBRyxHQUFHLENBQUMsQ0FBQztZQUN6RCxJQUFJLENBQUMsSUFBSSxDQUFDLEVBQUUsR0FBRyxHQUFHLEdBQUcsRUFBRSxHQUFHLEVBQUUsQ0FBQyxDQUFDLENBQUMsR0FBRyxFQUFFLEdBQUcsRUFBRSxDQUFDLENBQUMsQ0FBQyxHQUFHLEVBQUUsR0FBRyxHQUFHLENBQUMsQ0FBQztRQUMzRCxDQUFDO0lBQ0gsQ0FBQztJQUNELElBQUksTUFBTSxFQUFFLENBQUM7UUFDWCxJQUFJLENBQUMsSUFBSSxDQUFDLEVBQUUsQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDO1FBQUMsSUFBSSxDQUFDLElBQUksQ0FBQyxFQUFFLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQztJQUNyQyxDQUFDO1NBQU0sQ0FBQztRQUNOLElBQUksQ0FBQyxJQUFJLENBQUMsRUFBRSxDQUFDLENBQUMsR0FBRyxDQUFDLENBQUMsQ0FBQyxDQUFDO1FBQUMsSUFBSSxDQUFDLElBQUksQ0FBQyxFQUFFLENBQUMsQ0FBQyxHQUFHLENBQUMsQ0FBQyxDQUFDLENBQUM7SUFDN0MsQ0FBQztJQUNELE9BQU8sRUFBRSxFQUFFLEVBQUUsSUFBSSxFQUFFLEVBQUUsRUFBRSxJQUFJLEVBQUUsQ0FBQztBQUNoQyxDQUFDO0FBRUQ7OztHQUdHO0FBQ0gsTUFBTSxVQUFVLFdBQVcsQ0FBQyxFQUFZLEVBQUUsRUFBWSxFQUFFLE1BQWU7SUFDckUsTUFBTSxDQUFDLEdBQUcsSUFBSSxDQUFDLEdBQUcsQ0FBQyxFQUFFLENBQUMsTUFBTSxFQUFFLEVBQUUsQ0FBQyxNQUFNLENBQUMsQ0FBQztJQUN6QyxJQUFJLENBQUMsR0FBRyxDQUFDO1FBQUUsT0FBTyxFQUFFLEVBQUUsRUFBRSxFQUFFLENBQUMsS0FBSyxFQUFFLEVBQUUsRUFBRSxFQUFFLEVBQUUsQ0FBQyxLQUFLLEVBQUUsRUFBRSxDQUFDO0lBQ3JELE9BQU8sc0JBQXNCLENBQUMsRUFBRSxFQUFFLEVBQUUsRUFBRSxtQkFBbUIsQ0FBQyxFQUFFLEVBQUUsRUFBRSxFQUFFLE1BQU0sQ0FBQyxFQUFFLE1BQU0sQ0FBQyxDQUFDO0FBQ3JGLENBQUMiLCJzb3VyY2VzQ29udGVudCI6WyIvKipcbiAqIEJlemllci1jdXJ2ZSBoZWxwZXJzIChubyBleHRlcm5hbCBkZXBlbmRlbmN5KS5cbiAqXG4gKiBKSVQgc3VwcG9ydHMgdHJ1ZSBiZXppZXIgcmVnaW9uczogYSBwb2x5Z29uJ3MgdmVydGljZXMgYXJlIHRoZSBlZGl0YWJsZVxuICogKmFuY2hvcnMqLCBhbmQgdGhlIHNtb290aCBjdXJ2ZSB0aHJvdWdoIHRoZW0gaXMgZGVyaXZlZCBoZXJlIHdpdGggdGhlXG4gKiBDYXRtdWxsLVJvbSBjb25zdHJ1Y3Rpb24g4oCUIHRoZSBzYW1lIGFsZ29yaXRobSBwYXBlci5qcyB1c2VzIGluXG4gKiBgcGF0aC5zbW9vdGgoKWAuIFRoZSBjdXJ2ZSBpbnRlcnBvbGF0ZXMgZXZlcnkgYW5jaG9yIGFuZFxuICogZWFjaCBhbmNob3IncyB0d28gaGFuZGxlcyBhcmUgY29saW5lYXIgKGEgc21vb3RoIHRhbmdlbnQpLlxuICpcbiAqIFRoZSBPU0Qgb3ZlcmxheSByZW5kZXJzIHRoZSBjdXJ2ZSBhcyBhIHRydWUgY3ViaWMgU1ZHIHBhdGggKyBkcmFnZ2FibGUgaGFuZGxlc1xuICogKHNlZSB7QGxpbmsgYmV6aWVyQW5jaG9ySGFuZGxlc30pOyBHZW9KU09OIGV4cG9ydCBmbGF0dGVucyB0aGUgKnNhbWUqIGN1Ymljc1xuICogaW50byBhIGRlbnNlIHBvaW50IGxpc3QgKHNlZSB7QGxpbmsgYmV6aWVyQ3VydmV9KSBzbyBhIHZpZXdlciB3aXRob3V0IGJlemllclxuICogc3VwcG9ydCAoZS5nLiBRdVBhdGgsIHdoaWNoIGp1c3QgZHJhd3MgdGhlIGNvb3JkaW5hdGUgbGlzdCkgc3RpbGwgc2hvd3MgdGhlXG4gKiBzbW9vdGggc2hhcGUuIFVzaW5nIG9uZSBhbGdvcml0aG0gZm9yIGJvdGgga2VlcHMgd2hhdCBKSVQgZHJhd3MgaWRlbnRpY2FsIHRvXG4gKiB3aGF0IGl0IGV4cG9ydHMuXG4gKlxuICogUHVyZSBmdW5jdGlvbnM6IG5vIERPTSwgbm8gc2VydmljZSBzdGF0ZS4gQ29vcmRpbmF0ZXMgYXJlIGltYWdlIHBpeGVscy5cbiAqL1xuXG5leHBvcnQgaW50ZXJmYWNlIEN1cnZlUG9pbnRzIHtcbiAgeHM6IG51bWJlcltdO1xuICB5czogbnVtYmVyW107XG59XG5cbi8qKiBBIHZlcnRleCdzIHR3byBjdWJpYy1iZXppZXIgY29udHJvbCBwb2ludHMgKGFic29sdXRlIGNvb3JkcykgcGx1cyB3aGV0aGVyXG4gKiAgZWFjaCBzaWRlIGhhcyBhIGhhbmRsZSAob3Blbi1wYXRoIGVuZHBvaW50cyBoYXZlIG9ubHkgb25lKS4gKi9cbmV4cG9ydCBpbnRlcmZhY2UgQW5jaG9ySGFuZGxlIHtcbiAgaW46IFtudW1iZXIsIG51bWJlcl07XG4gIG91dDogW251bWJlciwgbnVtYmVyXTtcbiAgaGFzSW46IGJvb2xlYW47XG4gIGhhc091dDogYm9vbGVhbjtcbn1cblxuLyoqIEhvdyBtYW55IHBvaW50cyB0byBzYW1wbGUgcGVyIGN1YmljIHNlZ21lbnQgd2hlbiBmbGF0dGVuaW5nIGZvciBleHBvcnQuICovXG5jb25zdCBTQU1QTEVTX1BFUl9TRUdNRU5UID0gMTY7XG5cbi8qKlxuICogUGVyLWFuY2hvciBjdWJpYy1iZXppZXIgY29udHJvbCBwb2ludHMgdXNpbmcgdGhlIENhdG11bGwtUm9tIGNvbnN0cnVjdGlvblxuICogKHRlbnNpb24gMTogY29udHJvbCA9IGFuY2hvciDCsSAobmV4dCDiiJIgcHJldikgLyA2KS4gTWF0Y2hlcyBwYXBlci5qc1xuICogYHBhdGguc21vb3RoKClgOiB0aGUgcmVzdWx0aW5nIGN1YmljIGludGVycG9sYXRlcyBldmVyeSBhbmNob3IgYW5kIHRoZSB0d29cbiAqIGhhbmRsZXMgb2YgZWFjaCBhbmNob3IgYXJlIGNvbGluZWFyLiBDb29yZGluYXRlcyBhcmUgaW4gdGhlIGlucHV0IHNwYWNlLlxuICovXG5leHBvcnQgZnVuY3Rpb24gYmV6aWVyQW5jaG9ySGFuZGxlcyh4czogbnVtYmVyW10sIHlzOiBudW1iZXJbXSwgY2xvc2VkOiBib29sZWFuKTogQW5jaG9ySGFuZGxlW10ge1xuICBjb25zdCBuID0gTWF0aC5taW4oeHMubGVuZ3RoLCB5cy5sZW5ndGgpO1xuICBjb25zdCBvdXQ6IEFuY2hvckhhbmRsZVtdID0gW107XG4gIGZvciAobGV0IGkgPSAwOyBpIDwgbjsgaSsrKSB7XG4gICAgY29uc3QgcHJldiA9IGNsb3NlZCA/IChpIC0gMSArIG4pICUgbiA6IE1hdGgubWF4KDAsIGkgLSAxKTtcbiAgICBjb25zdCBuZXh0ID0gY2xvc2VkID8gKGkgKyAxKSAlIG4gOiBNYXRoLm1pbihuIC0gMSwgaSArIDEpO1xuICAgIGNvbnN0IHR4ID0gKHhzW25leHRdIC0geHNbcHJldl0pIC8gNjtcbiAgICBjb25zdCB0eSA9ICh5c1tuZXh0XSAtIHlzW3ByZXZdKSAvIDY7XG4gICAgb3V0LnB1c2goe1xuICAgICAgb3V0OiBbeHNbaV0gKyB0eCwgeXNbaV0gKyB0eV0sXG4gICAgICBpbjogW3hzW2ldIC0gdHgsIHlzW2ldIC0gdHldLFxuICAgICAgaGFzT3V0OiBjbG9zZWQgfHwgaSA8IG4gLSAxLFxuICAgICAgaGFzSW46IGNsb3NlZCB8fCBpID4gMCxcbiAgICB9KTtcbiAgfVxuICByZXR1cm4gb3V0O1xufVxuXG4vKiogVGhlIENhdG11bGwtUm9tIGhhbmRsZXMgYXMgKipyZWxhdGl2ZSoqIG9mZnNldHMgYFtkeCwgZHldYCBwZXIgYW5jaG9yIOKAlCB1c2VkXG4gKiAgdG8gaW5pdGlhbGlzZSBhIHJlZ2lvbidzIGVkaXRhYmxlIGhhbmRsZXMgd2hlbiBiZXppZXIgaXMgdHVybmVkIG9uLiAqL1xuZXhwb3J0IGZ1bmN0aW9uIGRlZmF1bHRIYW5kbGVPZmZzZXRzKHhzOiBudW1iZXJbXSwgeXM6IG51bWJlcltdLCBjbG9zZWQ6IGJvb2xlYW4pOlxuICB7IGluOiBudW1iZXJbXVtdOyBvdXQ6IG51bWJlcltdW10gfSB7XG4gIGNvbnN0IGggPSBiZXppZXJBbmNob3JIYW5kbGVzKHhzLCB5cywgY2xvc2VkKTtcbiAgcmV0dXJuIHtcbiAgICBpbjogaC5tYXAoKGEsIGkpID0+IFthLmluWzBdIC0geHNbaV0sIGEuaW5bMV0gLSB5c1tpXV0pLFxuICAgIG91dDogaC5tYXAoKGEsIGkpID0+IFthLm91dFswXSAtIHhzW2ldLCBhLm91dFsxXSAtIHlzW2ldXSksXG4gIH07XG59XG5cbi8qKiBBYnNvbHV0ZSBoYW5kbGVzIGZyb20gc3RvcmVkIHJlbGF0aXZlIG9mZnNldHMgKGFuY2hvciArIG9mZnNldCkuICovXG5leHBvcnQgZnVuY3Rpb24gaGFuZGxlc0Zyb21PZmZzZXRzKHhzOiBudW1iZXJbXSwgeXM6IG51bWJlcltdLFxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBpbk9mZjogbnVtYmVyW11bXSwgb3V0T2ZmOiBudW1iZXJbXVtdLCBjbG9zZWQ6IGJvb2xlYW4pOiBBbmNob3JIYW5kbGVbXSB7XG4gIGNvbnN0IG4gPSBNYXRoLm1pbih4cy5sZW5ndGgsIHlzLmxlbmd0aCk7XG4gIGNvbnN0IG91dDogQW5jaG9ySGFuZGxlW10gPSBbXTtcbiAgZm9yIChsZXQgaSA9IDA7IGkgPCBuOyBpKyspIHtcbiAgICBvdXQucHVzaCh7XG4gICAgICBpbjogW3hzW2ldICsgKGluT2ZmW2ldPy5bMF0gPz8gMCksIHlzW2ldICsgKGluT2ZmW2ldPy5bMV0gPz8gMCldLFxuICAgICAgb3V0OiBbeHNbaV0gKyAob3V0T2ZmW2ldPy5bMF0gPz8gMCksIHlzW2ldICsgKG91dE9mZltpXT8uWzFdID8/IDApXSxcbiAgICAgIGhhc091dDogY2xvc2VkIHx8IGkgPCBuIC0gMSxcbiAgICAgIGhhc0luOiBjbG9zZWQgfHwgaSA+IDAsXG4gICAgfSk7XG4gIH1cbiAgcmV0dXJuIG91dDtcbn1cblxuLyoqIEEgcmVnaW9uJ3MgaGFuZGxlczogZnJvbSBpdHMgc3RvcmVkIChlZGl0YWJsZSkgb2Zmc2V0cyB3aGVuIHByZXNlbnQsIGVsc2UgdGhlXG4gKiAgQ2F0bXVsbC1Sb20gZGVmYXVsdC4gVGhlIHNpbmdsZSBzb3VyY2Ugb2YgdHJ1dGggZm9yIGJvdGggcmVuZGVyaW5nIGFuZCBleHBvcnQuICovXG5leHBvcnQgZnVuY3Rpb24gcmVzb2x2ZUhhbmRsZXMoeHM6IG51bWJlcltdLCB5czogbnVtYmVyW10sIGNsb3NlZDogYm9vbGVhbixcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBpbk9mZj86IG51bWJlcltdW10sIG91dE9mZj86IG51bWJlcltdW10pOiBBbmNob3JIYW5kbGVbXSB7XG4gIGlmIChpbk9mZiAmJiBvdXRPZmYgJiYgaW5PZmYubGVuZ3RoID09PSB4cy5sZW5ndGggJiYgb3V0T2ZmLmxlbmd0aCA9PT0geHMubGVuZ3RoKSB7XG4gICAgcmV0dXJuIGhhbmRsZXNGcm9tT2Zmc2V0cyh4cywgeXMsIGluT2ZmLCBvdXRPZmYsIGNsb3NlZCk7XG4gIH1cbiAgcmV0dXJuIGJlemllckFuY2hvckhhbmRsZXMoeHMsIHlzLCBjbG9zZWQpO1xufVxuXG4vKipcbiAqIEZsYXR0ZW4gYSBjdWJpYyBiZXppZXIgKGRlZmluZWQgYnkgZXhwbGljaXQgcGVyLWFuY2hvciBoYW5kbGVzKSBpbnRvIGEgZGVuc2VcbiAqIHBvaW50IGxpc3QgYnkgc2FtcGxpbmcgZWFjaCBzZWdtZW50LiBUaGUgY3VydmUgcGFzc2VzIHRocm91Z2ggZXZlcnkgYW5jaG9yOyBhXG4gKiBjbG9zZWQgcmluZyByZXR1cm5zIHRvIGl0cyBmaXJzdCBwb2ludC5cbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIGJlemllckN1cnZlRnJvbUhhbmRsZXMoeHM6IG51bWJlcltdLCB5czogbnVtYmVyW10sXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBoYW5kbGVzOiBBbmNob3JIYW5kbGVbXSwgY2xvc2VkOiBib29sZWFuKTogQ3VydmVQb2ludHMge1xuICBjb25zdCBuID0gTWF0aC5taW4oeHMubGVuZ3RoLCB5cy5sZW5ndGgpO1xuICBpZiAobiA8IDMpIHJldHVybiB7IHhzOiB4cy5zbGljZSgpLCB5czogeXMuc2xpY2UoKSB9O1xuXG4gIGNvbnN0IG91dFg6IG51bWJlcltdID0gW107XG4gIGNvbnN0IG91dFk6IG51bWJlcltdID0gW107XG4gIGNvbnN0IHNlZ3MgPSBjbG9zZWQgPyBuIDogbiAtIDE7XG4gIGZvciAobGV0IGkgPSAwOyBpIDwgc2VnczsgaSsrKSB7XG4gICAgY29uc3QgaiA9IChpICsgMSkgJSBuO1xuICAgIGNvbnN0IHAweCA9IHhzW2ldLCBwMHkgPSB5c1tpXTtcbiAgICBjb25zdCBjMSA9IGhhbmRsZXNbaV0ub3V0LCBjMiA9IGhhbmRsZXNbal0uaW47XG4gICAgY29uc3QgcDN4ID0geHNbal0sIHAzeSA9IHlzW2pdO1xuICAgIGZvciAobGV0IHMgPSAwOyBzIDwgU0FNUExFU19QRVJfU0VHTUVOVDsgcysrKSB7XG4gICAgICBjb25zdCB0ID0gcyAvIFNBTVBMRVNfUEVSX1NFR01FTlQ7XG4gICAgICBjb25zdCB1ID0gMSAtIHQ7XG4gICAgICBjb25zdCB3MCA9IHUgKiB1ICogdSwgdzEgPSAzICogdSAqIHUgKiB0LCB3MiA9IDMgKiB1ICogdCAqIHQsIHczID0gdCAqIHQgKiB0O1xuICAgICAgb3V0WC5wdXNoKHcwICogcDB4ICsgdzEgKiBjMVswXSArIHcyICogYzJbMF0gKyB3MyAqIHAzeCk7XG4gICAgICBvdXRZLnB1c2godzAgKiBwMHkgKyB3MSAqIGMxWzFdICsgdzIgKiBjMlsxXSArIHczICogcDN5KTtcbiAgICB9XG4gIH1cbiAgaWYgKGNsb3NlZCkge1xuICAgIG91dFgucHVzaCh4c1swXSk7IG91dFkucHVzaCh5c1swXSk7XG4gIH0gZWxzZSB7XG4gICAgb3V0WC5wdXNoKHhzW24gLSAxXSk7IG91dFkucHVzaCh5c1tuIC0gMV0pO1xuICB9XG4gIHJldHVybiB7IHhzOiBvdXRYLCB5czogb3V0WSB9O1xufVxuXG4vKipcbiAqIEZsYXR0ZW4gdGhlIHNtb290aCBDYXRtdWxsLVJvbSBiZXppZXIgdGhyb3VnaCB0aGUgYW5jaG9ycyAobm8gY3VzdG9tIGhhbmRsZXMpLlxuICogRmFsbHMgYmFjayB0byB0aGUgYW5jaG9ycyB3aGVuIHRoZXJlIGFyZSB0b28gZmV3IHRvIGZvcm0gYSBjdXJ2ZS5cbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIGJlemllckN1cnZlKHhzOiBudW1iZXJbXSwgeXM6IG51bWJlcltdLCBjbG9zZWQ6IGJvb2xlYW4pOiBDdXJ2ZVBvaW50cyB7XG4gIGNvbnN0IG4gPSBNYXRoLm1pbih4cy5sZW5ndGgsIHlzLmxlbmd0aCk7XG4gIGlmIChuIDwgMykgcmV0dXJuIHsgeHM6IHhzLnNsaWNlKCksIHlzOiB5cy5zbGljZSgpIH07XG4gIHJldHVybiBiZXppZXJDdXJ2ZUZyb21IYW5kbGVzKHhzLCB5cywgYmV6aWVyQW5jaG9ySGFuZGxlcyh4cywgeXMsIGNsb3NlZCksIGNsb3NlZCk7XG59XG4iXX0=
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Preset annotation classes (jit-ui#70).
|
|
3
|
+
*
|
|
4
|
+
* A class is a flat `{ name, color }` pair (v1: no hierarchy, no special-class semantics).
|
|
5
|
+
* The `PresetSet` is the per-user, server-persisted source of truth for region colours;
|
|
6
|
+
* see `class-color.util.ts` for the deterministic colour-resolution engine.
|
|
7
|
+
*/
|
|
8
|
+
/**
|
|
9
|
+
* Fallback palette for classes not present in the preset set — reasonably distinct hues.
|
|
10
|
+
* Colours are assigned by a stable hash of the class name, so the same unknown class always
|
|
11
|
+
* gets the same colour with no stored state.
|
|
12
|
+
*/
|
|
13
|
+
export const DEFAULT_FALLBACK_PALETTE = [
|
|
14
|
+
'#6C8EBF', '#82B366', '#B85450', '#9673A6', '#D79B00',
|
|
15
|
+
'#3C948B', '#A64CA6', '#CC6677', '#4477AA', '#228833',
|
|
16
|
+
];
|
|
17
|
+
/** Seed classes — mirrors the historical hard-coded `classificationColors` map. */
|
|
18
|
+
const DEFAULT_CLASSES = [
|
|
19
|
+
{ name: 'Fragmented-embryo', color: '#FF8C00', source: 'default' },
|
|
20
|
+
{ name: 'Dying-embryo', color: '#FF3333', source: 'default' },
|
|
21
|
+
{ name: 'Two-cell-embryo', color: '#33CC66', source: 'default' },
|
|
22
|
+
{ name: 'One-cell-embryo', color: '#3399FF', source: 'default' },
|
|
23
|
+
{ name: 'Unknown', color: '#888888', source: 'default' },
|
|
24
|
+
{ name: 'Tumor', color: '#FF4444', source: 'default' },
|
|
25
|
+
{ name: 'Stroma', color: '#44AAFF', source: 'default' },
|
|
26
|
+
{ name: 'Immune cells', color: '#FFDD00', source: 'default' },
|
|
27
|
+
{ name: 'Necrosis', color: '#AA6633', source: 'default' },
|
|
28
|
+
{ name: 'Region', color: '#00FFFF', source: 'default' },
|
|
29
|
+
{ name: 'Ignore', color: '#AAAAAA', source: 'default' },
|
|
30
|
+
{ name: 'Positive', color: '#00CC44', source: 'default' },
|
|
31
|
+
{ name: 'Negative', color: '#CC0000', source: 'default' },
|
|
32
|
+
];
|
|
33
|
+
/** A fresh default preset set (deep-copied so callers can mutate safely). */
|
|
34
|
+
export function defaultPresetSet() {
|
|
35
|
+
return {
|
|
36
|
+
classes: DEFAULT_CLASSES.map((c) => ({ ...c })),
|
|
37
|
+
fallbackPalette: [...DEFAULT_FALLBACK_PALETTE],
|
|
38
|
+
autoPromote: false,
|
|
39
|
+
matchMode: 'exact',
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
/** True when the set has no usable classes (e.g. the server returned an empty/blank record). */
|
|
43
|
+
export function isEmptyPresetSet(set) {
|
|
44
|
+
return !set || !Array.isArray(set.classes) || set.classes.length === 0;
|
|
45
|
+
}
|
|
46
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY2xhc3MtcHJlc2V0LmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vLi4vc3JjL2xpYi9tb2RlbHMvY2xhc3MtcHJlc2V0LnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBOzs7Ozs7R0FNRztBQTRCSDs7OztHQUlHO0FBQ0gsTUFBTSxDQUFDLE1BQU0sd0JBQXdCLEdBQWE7SUFDaEQsU0FBUyxFQUFFLFNBQVMsRUFBRSxTQUFTLEVBQUUsU0FBUyxFQUFFLFNBQVM7SUFDckQsU0FBUyxFQUFFLFNBQVMsRUFBRSxTQUFTLEVBQUUsU0FBUyxFQUFFLFNBQVM7Q0FDdEQsQ0FBQztBQUVGLG1GQUFtRjtBQUNuRixNQUFNLGVBQWUsR0FBa0I7SUFDckMsRUFBRSxJQUFJLEVBQUUsbUJBQW1CLEVBQUUsS0FBSyxFQUFFLFNBQVMsRUFBRSxNQUFNLEVBQUUsU0FBUyxFQUFFO0lBQ2xFLEVBQUUsSUFBSSxFQUFFLGNBQWMsRUFBRSxLQUFLLEVBQUUsU0FBUyxFQUFFLE1BQU0sRUFBRSxTQUFTLEVBQUU7SUFDN0QsRUFBRSxJQUFJLEVBQUUsaUJBQWlCLEVBQUUsS0FBSyxFQUFFLFNBQVMsRUFBRSxNQUFNLEVBQUUsU0FBUyxFQUFFO0lBQ2hFLEVBQUUsSUFBSSxFQUFFLGlCQUFpQixFQUFFLEtBQUssRUFBRSxTQUFTLEVBQUUsTUFBTSxFQUFFLFNBQVMsRUFBRTtJQUNoRSxFQUFFLElBQUksRUFBRSxTQUFTLEVBQUUsS0FBSyxFQUFFLFNBQVMsRUFBRSxNQUFNLEVBQUUsU0FBUyxFQUFFO0lBQ3hELEVBQUUsSUFBSSxFQUFFLE9BQU8sRUFBRSxLQUFLLEVBQUUsU0FBUyxFQUFFLE1BQU0sRUFBRSxTQUFTLEVBQUU7SUFDdEQsRUFBRSxJQUFJLEVBQUUsUUFBUSxFQUFFLEtBQUssRUFBRSxTQUFTLEVBQUUsTUFBTSxFQUFFLFNBQVMsRUFBRTtJQUN2RCxFQUFFLElBQUksRUFBRSxjQUFjLEVBQUUsS0FBSyxFQUFFLFNBQVMsRUFBRSxNQUFNLEVBQUUsU0FBUyxFQUFFO0lBQzdELEVBQUUsSUFBSSxFQUFFLFVBQVUsRUFBRSxLQUFLLEVBQUUsU0FBUyxFQUFFLE1BQU0sRUFBRSxTQUFTLEVBQUU7SUFDekQsRUFBRSxJQUFJLEVBQUUsUUFBUSxFQUFFLEtBQUssRUFBRSxTQUFTLEVBQUUsTUFBTSxFQUFFLFNBQVMsRUFBRTtJQUN2RCxFQUFFLElBQUksRUFBRSxRQUFRLEVBQUUsS0FBSyxFQUFFLFNBQVMsRUFBRSxNQUFNLEVBQUUsU0FBUyxFQUFFO0lBQ3ZELEVBQUUsSUFBSSxFQUFFLFVBQVUsRUFBRSxLQUFLLEVBQUUsU0FBUyxFQUFFLE1BQU0sRUFBRSxTQUFTLEVBQUU7SUFDekQsRUFBRSxJQUFJLEVBQUUsVUFBVSxFQUFFLEtBQUssRUFBRSxTQUFTLEVBQUUsTUFBTSxFQUFFLFNBQVMsRUFBRTtDQUMxRCxDQUFDO0FBRUYsNkVBQTZFO0FBQzdFLE1BQU0sVUFBVSxnQkFBZ0I7SUFDOUIsT0FBTztRQUNMLE9BQU8sRUFBRSxlQUFlLENBQUMsR0FBRyxDQUFDLENBQUMsQ0FBQyxFQUFFLEVBQUUsQ0FBQyxDQUFDLEVBQUUsR0FBRyxDQUFDLEVBQUUsQ0FBQyxDQUFDO1FBQy9DLGVBQWUsRUFBRSxDQUFDLEdBQUcsd0JBQXdCLENBQUM7UUFDOUMsV0FBVyxFQUFFLEtBQUs7UUFDbEIsU0FBUyxFQUFFLE9BQU87S0FDbkIsQ0FBQztBQUNKLENBQUM7QUFFRCxnR0FBZ0c7QUFDaEcsTUFBTSxVQUFVLGdCQUFnQixDQUFDLEdBQWlDO0lBQ2hFLE9BQU8sQ0FBQyxHQUFHLElBQUksQ0FBQyxLQUFLLENBQUMsT0FBTyxDQUFDLEdBQUcsQ0FBQyxPQUFPLENBQUMsSUFBSSxHQUFHLENBQUMsT0FBTyxDQUFDLE1BQU0sS0FBSyxDQUFDLENBQUM7QUFDekUsQ0FBQyIsInNvdXJjZXNDb250ZW50IjpbIi8qKlxuICogUHJlc2V0IGFubm90YXRpb24gY2xhc3NlcyAoaml0LXVpIzcwKS5cbiAqXG4gKiBBIGNsYXNzIGlzIGEgZmxhdCBgeyBuYW1lLCBjb2xvciB9YCBwYWlyICh2MTogbm8gaGllcmFyY2h5LCBubyBzcGVjaWFsLWNsYXNzIHNlbWFudGljcykuXG4gKiBUaGUgYFByZXNldFNldGAgaXMgdGhlIHBlci11c2VyLCBzZXJ2ZXItcGVyc2lzdGVkIHNvdXJjZSBvZiB0cnV0aCBmb3IgcmVnaW9uIGNvbG91cnM7XG4gKiBzZWUgYGNsYXNzLWNvbG9yLnV0aWwudHNgIGZvciB0aGUgZGV0ZXJtaW5pc3RpYyBjb2xvdXItcmVzb2x1dGlvbiBlbmdpbmUuXG4gKi9cblxuZXhwb3J0IHR5cGUgUHJlc2V0U291cmNlID0gJ3VzZXInIHwgJ2RlZmF1bHQnIHwgJ2F1dG8nO1xuXG5leHBvcnQgaW50ZXJmYWNlIENsYXNzUHJlc2V0IHtcbiAgLyoqIFRoZSBjbGFzcyBuYW1lIOKAlCBtYXRjaGVkIGFnYWluc3QgYFJlZ2lvbi5sYWJlbGAuICovXG4gIG5hbWU6IHN0cmluZztcbiAgLyoqIEhleCBjb2xvdXIsIGAjUlJHR0JCYC4gKi9cbiAgY29sb3I6IHN0cmluZztcbiAgLyoqIFJlc2VydmVkIGZvciBhIGZ1dHVyZSBwZXItY2xhc3Mgb3ZlcmxheS12aXNpYmlsaXR5IHRvZ2dsZS4gKi9cbiAgdmlzaWJsZT86IGJvb2xlYW47XG4gIC8qKiBQcm92ZW5hbmNlOyBgJ2F1dG8nYCA9IGFkZGVkIGJ5IG9wdC1pbiBmYWxsYmFjayBwcm9tb3Rpb24uICovXG4gIHNvdXJjZT86IFByZXNldFNvdXJjZTtcbn1cblxuZXhwb3J0IHR5cGUgTWF0Y2hNb2RlID0gJ2V4YWN0JyB8ICdub3JtYWxpemVkJztcblxuZXhwb3J0IGludGVyZmFjZSBQcmVzZXRTZXQge1xuICAvKiogT3JkZXJlZCBsaXN0IG9mIHByZXNldCBjbGFzc2VzLiAqL1xuICBjbGFzc2VzOiBDbGFzc1ByZXNldFtdO1xuICAvKiogQ29sb3VycyB1c2VkIGZvciBjbGFzc2VzIG5vdCBpbiBgY2xhc3Nlc2AgKGFzc2lnbmVkIGRldGVybWluaXN0aWNhbGx5KS4gKi9cbiAgZmFsbGJhY2tQYWxldHRlOiBzdHJpbmdbXTtcbiAgLyoqIE9wdC1pbiAoZGVmYXVsdCBmYWxzZSk6IGFkZCBhbiB1bmtub3duIGNsYXNzIHRvIGBjbGFzc2VzYCB0aGUgZmlyc3QgdGltZSBpdCBpcyBzZWVuLiAqL1xuICBhdXRvUHJvbW90ZTogYm9vbGVhbjtcbiAgLyoqIExhYmVsIG1hdGNoaW5nIGFnYWluc3QgYFJlZ2lvbi5sYWJlbGAgKGRlZmF1bHQgYCdleGFjdCdgKS4gKi9cbiAgbWF0Y2hNb2RlOiBNYXRjaE1vZGU7XG59XG5cbi8qKlxuICogRmFsbGJhY2sgcGFsZXR0ZSBmb3IgY2xhc3NlcyBub3QgcHJlc2VudCBpbiB0aGUgcHJlc2V0IHNldCDigJQgcmVhc29uYWJseSBkaXN0aW5jdCBodWVzLlxuICogQ29sb3VycyBhcmUgYXNzaWduZWQgYnkgYSBzdGFibGUgaGFzaCBvZiB0aGUgY2xhc3MgbmFtZSwgc28gdGhlIHNhbWUgdW5rbm93biBjbGFzcyBhbHdheXNcbiAqIGdldHMgdGhlIHNhbWUgY29sb3VyIHdpdGggbm8gc3RvcmVkIHN0YXRlLlxuICovXG5leHBvcnQgY29uc3QgREVGQVVMVF9GQUxMQkFDS19QQUxFVFRFOiBzdHJpbmdbXSA9IFtcbiAgJyM2QzhFQkYnLCAnIzgyQjM2NicsICcjQjg1NDUwJywgJyM5NjczQTYnLCAnI0Q3OUIwMCcsXG4gICcjM0M5NDhCJywgJyNBNjRDQTYnLCAnI0NDNjY3NycsICcjNDQ3N0FBJywgJyMyMjg4MzMnLFxuXTtcblxuLyoqIFNlZWQgY2xhc3NlcyDigJQgbWlycm9ycyB0aGUgaGlzdG9yaWNhbCBoYXJkLWNvZGVkIGBjbGFzc2lmaWNhdGlvbkNvbG9yc2AgbWFwLiAqL1xuY29uc3QgREVGQVVMVF9DTEFTU0VTOiBDbGFzc1ByZXNldFtdID0gW1xuICB7IG5hbWU6ICdGcmFnbWVudGVkLWVtYnJ5bycsIGNvbG9yOiAnI0ZGOEMwMCcsIHNvdXJjZTogJ2RlZmF1bHQnIH0sXG4gIHsgbmFtZTogJ0R5aW5nLWVtYnJ5bycsIGNvbG9yOiAnI0ZGMzMzMycsIHNvdXJjZTogJ2RlZmF1bHQnIH0sXG4gIHsgbmFtZTogJ1R3by1jZWxsLWVtYnJ5bycsIGNvbG9yOiAnIzMzQ0M2NicsIHNvdXJjZTogJ2RlZmF1bHQnIH0sXG4gIHsgbmFtZTogJ09uZS1jZWxsLWVtYnJ5bycsIGNvbG9yOiAnIzMzOTlGRicsIHNvdXJjZTogJ2RlZmF1bHQnIH0sXG4gIHsgbmFtZTogJ1Vua25vd24nLCBjb2xvcjogJyM4ODg4ODgnLCBzb3VyY2U6ICdkZWZhdWx0JyB9LFxuICB7IG5hbWU6ICdUdW1vcicsIGNvbG9yOiAnI0ZGNDQ0NCcsIHNvdXJjZTogJ2RlZmF1bHQnIH0sXG4gIHsgbmFtZTogJ1N0cm9tYScsIGNvbG9yOiAnIzQ0QUFGRicsIHNvdXJjZTogJ2RlZmF1bHQnIH0sXG4gIHsgbmFtZTogJ0ltbXVuZSBjZWxscycsIGNvbG9yOiAnI0ZGREQwMCcsIHNvdXJjZTogJ2RlZmF1bHQnIH0sXG4gIHsgbmFtZTogJ05lY3Jvc2lzJywgY29sb3I6ICcjQUE2NjMzJywgc291cmNlOiAnZGVmYXVsdCcgfSxcbiAgeyBuYW1lOiAnUmVnaW9uJywgY29sb3I6ICcjMDBGRkZGJywgc291cmNlOiAnZGVmYXVsdCcgfSxcbiAgeyBuYW1lOiAnSWdub3JlJywgY29sb3I6ICcjQUFBQUFBJywgc291cmNlOiAnZGVmYXVsdCcgfSxcbiAgeyBuYW1lOiAnUG9zaXRpdmUnLCBjb2xvcjogJyMwMENDNDQnLCBzb3VyY2U6ICdkZWZhdWx0JyB9LFxuICB7IG5hbWU6ICdOZWdhdGl2ZScsIGNvbG9yOiAnI0NDMDAwMCcsIHNvdXJjZTogJ2RlZmF1bHQnIH0sXG5dO1xuXG4vKiogQSBmcmVzaCBkZWZhdWx0IHByZXNldCBzZXQgKGRlZXAtY29waWVkIHNvIGNhbGxlcnMgY2FuIG11dGF0ZSBzYWZlbHkpLiAqL1xuZXhwb3J0IGZ1bmN0aW9uIGRlZmF1bHRQcmVzZXRTZXQoKTogUHJlc2V0U2V0IHtcbiAgcmV0dXJuIHtcbiAgICBjbGFzc2VzOiBERUZBVUxUX0NMQVNTRVMubWFwKChjKSA9PiAoeyAuLi5jIH0pKSxcbiAgICBmYWxsYmFja1BhbGV0dGU6IFsuLi5ERUZBVUxUX0ZBTExCQUNLX1BBTEVUVEVdLFxuICAgIGF1dG9Qcm9tb3RlOiBmYWxzZSxcbiAgICBtYXRjaE1vZGU6ICdleGFjdCcsXG4gIH07XG59XG5cbi8qKiBUcnVlIHdoZW4gdGhlIHNldCBoYXMgbm8gdXNhYmxlIGNsYXNzZXMgKGUuZy4gdGhlIHNlcnZlciByZXR1cm5lZCBhbiBlbXB0eS9ibGFuayByZWNvcmQpLiAqL1xuZXhwb3J0IGZ1bmN0aW9uIGlzRW1wdHlQcmVzZXRTZXQoc2V0OiBQcmVzZXRTZXQgfCBudWxsIHwgdW5kZWZpbmVkKTogYm9vbGVhbiB7XG4gIHJldHVybiAhc2V0IHx8ICFBcnJheS5pc0FycmF5KHNldC5jbGFzc2VzKSB8fCBzZXQuY2xhc3Nlcy5sZW5ndGggPT09IDA7XG59XG4iXX0=
|
|
@@ -0,0 +1,198 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pure geometry helpers extracted from PlotlyService.
|
|
3
|
+
*
|
|
4
|
+
* No DOM access, no service state, no `this` — every function is testable
|
|
5
|
+
* in isolation. Keep it that way: this module is the dumping ground for
|
|
6
|
+
* shape/mask/SVG-path utilities the wand, vertex eraser, and region editor
|
|
7
|
+
* share. If a helper grows a `this.shapes` reference, it doesn't belong here.
|
|
8
|
+
*/
|
|
9
|
+
// ── SVG path parsing / building ──────────────────────────────────────
|
|
10
|
+
/**
|
|
11
|
+
* Parse a single-subpath `M x,y L x,y ... Z` path into vertex arrays.
|
|
12
|
+
*
|
|
13
|
+
* Returns null for paths we can't safely round-trip (multi-subpath, curves,
|
|
14
|
+
* fewer than three vertices). The wand only adopts shapes it produced or
|
|
15
|
+
* that Plotly's drawclosedpath produces, both of which are single-subpath
|
|
16
|
+
* polygons.
|
|
17
|
+
*/
|
|
18
|
+
export function parseSvgPathPolygon(path) {
|
|
19
|
+
if (!path || path[0] !== 'M')
|
|
20
|
+
return null;
|
|
21
|
+
if (path.indexOf('M', 1) !== -1)
|
|
22
|
+
return null; // multi-subpath
|
|
23
|
+
const trimmed = path.endsWith('Z') ? path.slice(1, -1) : path.slice(1);
|
|
24
|
+
const segs = trimmed.split('L');
|
|
25
|
+
const xpoints = [];
|
|
26
|
+
const ypoints = [];
|
|
27
|
+
for (const seg of segs) {
|
|
28
|
+
const [sx, sy] = seg.split(',');
|
|
29
|
+
const x = parseFloat(sx);
|
|
30
|
+
const y = parseFloat(sy);
|
|
31
|
+
if (!Number.isFinite(x) || !Number.isFinite(y))
|
|
32
|
+
return null;
|
|
33
|
+
xpoints.push(x);
|
|
34
|
+
ypoints.push(y);
|
|
35
|
+
}
|
|
36
|
+
if (xpoints.length < 3)
|
|
37
|
+
return null;
|
|
38
|
+
return { xpoints, ypoints };
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Build a closed SVG path string `M x,y L x,y L ... Z` from vertex arrays.
|
|
42
|
+
* Returns '' for fewer than three vertices (degenerate).
|
|
43
|
+
*/
|
|
44
|
+
export function polygonToSvgPath(xpoints, ypoints) {
|
|
45
|
+
if (xpoints.length < 3)
|
|
46
|
+
return '';
|
|
47
|
+
let path = 'M';
|
|
48
|
+
for (let i = 0; i < xpoints.length; i++) {
|
|
49
|
+
path += `${xpoints[i]},${ypoints[i]}`;
|
|
50
|
+
path += i < xpoints.length - 1 ? 'L' : 'Z';
|
|
51
|
+
}
|
|
52
|
+
return path;
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Build an SVG path string from vertex arrays. Closed polygons end with `Z`,
|
|
56
|
+
* open polylines do not. Returns '' for fewer than two vertices (degenerate).
|
|
57
|
+
*/
|
|
58
|
+
export function verticesToSvgPath(xs, ys, closed) {
|
|
59
|
+
if (xs.length < 2)
|
|
60
|
+
return '';
|
|
61
|
+
let path = 'M';
|
|
62
|
+
for (let i = 0; i < xs.length; i++) {
|
|
63
|
+
path += `${xs[i]},${ys[i]}`;
|
|
64
|
+
if (i < xs.length - 1)
|
|
65
|
+
path += 'L';
|
|
66
|
+
}
|
|
67
|
+
if (closed)
|
|
68
|
+
path += 'Z';
|
|
69
|
+
return path;
|
|
70
|
+
}
|
|
71
|
+
// ── Region equality ──────────────────────────────────────────────────
|
|
72
|
+
/**
|
|
73
|
+
* Compare two Plotly shape objects for "same region" — used by the wand to
|
|
74
|
+
* avoid appending duplicates when the user pushes the same find result more
|
|
75
|
+
* than once. Polygons compare by SVG path string, rectangles compare by
|
|
76
|
+
* (x0, y0, x1, y1).
|
|
77
|
+
*/
|
|
78
|
+
export function shapesEqual(a, b) {
|
|
79
|
+
if (a.path && b.path) {
|
|
80
|
+
return a.path === b.path;
|
|
81
|
+
}
|
|
82
|
+
if (a.x0 !== undefined && a.y0 !== undefined &&
|
|
83
|
+
a.x1 !== undefined && a.y1 !== undefined &&
|
|
84
|
+
b.x0 !== undefined && b.y0 !== undefined &&
|
|
85
|
+
b.x1 !== undefined && b.y1 !== undefined) {
|
|
86
|
+
return a.x0 === b.x0 &&
|
|
87
|
+
a.y0 === b.y0 &&
|
|
88
|
+
a.x1 === b.x1 &&
|
|
89
|
+
a.y1 === b.y1;
|
|
90
|
+
}
|
|
91
|
+
return false;
|
|
92
|
+
}
|
|
93
|
+
// ── BBox-mask operations (used by the wand stroke accumulator) ───────
|
|
94
|
+
/** Returns true if the two bbox-relative masks share at least one set pixel. */
|
|
95
|
+
export function masksOverlap(a, b) {
|
|
96
|
+
const ix0 = Math.max(a.bx, b.bx);
|
|
97
|
+
const iy0 = Math.max(a.by, b.by);
|
|
98
|
+
const ix1 = Math.min(a.bx + a.bw, b.bx + b.bw);
|
|
99
|
+
const iy1 = Math.min(a.by + a.bh, b.by + b.bh);
|
|
100
|
+
if (ix0 >= ix1 || iy0 >= iy1)
|
|
101
|
+
return false;
|
|
102
|
+
for (let y = iy0; y < iy1; y++) {
|
|
103
|
+
const ar = (y - a.by) * a.bw;
|
|
104
|
+
const br = (y - b.by) * b.bw;
|
|
105
|
+
for (let x = ix0; x < ix1; x++) {
|
|
106
|
+
if (a.mask[ar + (x - a.bx)] && b.mask[br + (x - b.bx)])
|
|
107
|
+
return true;
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
return false;
|
|
111
|
+
}
|
|
112
|
+
/**
|
|
113
|
+
* Allocate a fresh accumulator covering both masks and OR them in. The
|
|
114
|
+
* resulting mask's bbox is the smallest rectangle containing both inputs.
|
|
115
|
+
*/
|
|
116
|
+
export function unionMasks(a, b) {
|
|
117
|
+
const ux0 = Math.min(a.bx, b.bx);
|
|
118
|
+
const uy0 = Math.min(a.by, b.by);
|
|
119
|
+
const ux1 = Math.max(a.bx + a.bw, b.bx + b.bw);
|
|
120
|
+
const uy1 = Math.max(a.by + a.bh, b.by + b.bh);
|
|
121
|
+
const ubw = ux1 - ux0;
|
|
122
|
+
const ubh = uy1 - uy0;
|
|
123
|
+
const umask = new Uint8Array(ubw * ubh);
|
|
124
|
+
const dax = a.bx - ux0;
|
|
125
|
+
const day = a.by - uy0;
|
|
126
|
+
for (let row = 0; row < a.bh; row++) {
|
|
127
|
+
const src = row * a.bw;
|
|
128
|
+
const dst = (row + day) * ubw + dax;
|
|
129
|
+
umask.set(a.mask.subarray(src, src + a.bw), dst);
|
|
130
|
+
}
|
|
131
|
+
const dbx = b.bx - ux0;
|
|
132
|
+
const dby = b.by - uy0;
|
|
133
|
+
for (let row = 0; row < b.bh; row++) {
|
|
134
|
+
const srcRow = row * b.bw;
|
|
135
|
+
const dstRow = (row + dby) * ubw;
|
|
136
|
+
for (let col = 0; col < b.bw; col++) {
|
|
137
|
+
if (b.mask[srcRow + col])
|
|
138
|
+
umask[dstRow + (dbx + col)] = 1;
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
return { bx: ux0, by: uy0, bw: ubw, bh: ubh, mask: umask };
|
|
142
|
+
}
|
|
143
|
+
// ── Ring simplification (Douglas–Peucker) ───────────────────────────────
|
|
144
|
+
/** Perpendicular distance from (px,py) to the segment (ax,ay)–(bx,by). */
|
|
145
|
+
function perpDistance(px, py, ax, ay, bx, by) {
|
|
146
|
+
const dx = bx - ax, dy = by - ay;
|
|
147
|
+
const len2 = dx * dx + dy * dy;
|
|
148
|
+
if (len2 === 0)
|
|
149
|
+
return Math.hypot(px - ax, py - ay);
|
|
150
|
+
let t = ((px - ax) * dx + (py - ay) * dy) / len2;
|
|
151
|
+
t = Math.max(0, Math.min(1, t));
|
|
152
|
+
return Math.hypot(px - (ax + t * dx), py - (ay + t * dy));
|
|
153
|
+
}
|
|
154
|
+
/**
|
|
155
|
+
* Simplify a closed ring with the Douglas–Peucker algorithm (jit-ui#85):
|
|
156
|
+
* drop every vertex that lies within `epsilon` pixels (the "altitude
|
|
157
|
+
* threshold") of the line between its kept neighbours. Anchored at vertex 0
|
|
158
|
+
* (the ring is closed by appending it). Rings of ≤ 3 vertices, or a
|
|
159
|
+
* non-positive epsilon, are returned unchanged. Output keeps the closed-ring
|
|
160
|
+
* convention (no repeated closing point).
|
|
161
|
+
*/
|
|
162
|
+
export function simplifyRing(xs, ys, epsilon) {
|
|
163
|
+
const n = xs.length;
|
|
164
|
+
if (n <= 3 || !(epsilon > 0))
|
|
165
|
+
return { xs: xs.slice(), ys: ys.slice() };
|
|
166
|
+
// Close the ring so both endpoints of the DP run are vertex 0.
|
|
167
|
+
const px = [...xs, xs[0]];
|
|
168
|
+
const py = [...ys, ys[0]];
|
|
169
|
+
const m = px.length;
|
|
170
|
+
const keep = new Array(m).fill(false);
|
|
171
|
+
keep[0] = true;
|
|
172
|
+
keep[m - 1] = true;
|
|
173
|
+
const stack = [[0, m - 1]];
|
|
174
|
+
while (stack.length) {
|
|
175
|
+
const [a, b] = stack.pop();
|
|
176
|
+
let maxD = -1, idx = -1;
|
|
177
|
+
for (let i = a + 1; i < b; i++) {
|
|
178
|
+
const d = perpDistance(px[i], py[i], px[a], py[a], px[b], py[b]);
|
|
179
|
+
if (d > maxD) {
|
|
180
|
+
maxD = d;
|
|
181
|
+
idx = i;
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
if (maxD > epsilon && idx > a) {
|
|
185
|
+
keep[idx] = true;
|
|
186
|
+
stack.push([a, idx], [idx, b]);
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
const outX = [], outY = [];
|
|
190
|
+
for (let i = 0; i < m - 1; i++) { // drop the duplicated closing vertex
|
|
191
|
+
if (keep[i]) {
|
|
192
|
+
outX.push(px[i]);
|
|
193
|
+
outY.push(py[i]);
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
return { xs: outX, ys: outY };
|
|
197
|
+
}
|
|
198
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZ2VvbWV0cnkuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi8uLi9zcmMvbGliL21vZGVscy9nZW9tZXRyeS50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQTs7Ozs7OztHQU9HO0FBaUJILHdFQUF3RTtBQUV4RTs7Ozs7OztHQU9HO0FBQ0gsTUFBTSxVQUFVLG1CQUFtQixDQUFDLElBQVk7SUFDOUMsSUFBSSxDQUFDLElBQUksSUFBSSxJQUFJLENBQUMsQ0FBQyxDQUFDLEtBQUssR0FBRztRQUFFLE9BQU8sSUFBSSxDQUFDO0lBQzFDLElBQUksSUFBSSxDQUFDLE9BQU8sQ0FBQyxHQUFHLEVBQUUsQ0FBQyxDQUFDLEtBQUssQ0FBQyxDQUFDO1FBQUUsT0FBTyxJQUFJLENBQUMsQ0FBQyxnQkFBZ0I7SUFDOUQsTUFBTSxPQUFPLEdBQUcsSUFBSSxDQUFDLFFBQVEsQ0FBQyxHQUFHLENBQUMsQ0FBQyxDQUFDLENBQUMsSUFBSSxDQUFDLEtBQUssQ0FBQyxDQUFDLEVBQUUsQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUMsSUFBSSxDQUFDLEtBQUssQ0FBQyxDQUFDLENBQUMsQ0FBQztJQUN2RSxNQUFNLElBQUksR0FBRyxPQUFPLENBQUMsS0FBSyxDQUFDLEdBQUcsQ0FBQyxDQUFDO0lBQ2hDLE1BQU0sT0FBTyxHQUFhLEVBQUUsQ0FBQztJQUM3QixNQUFNLE9BQU8sR0FBYSxFQUFFLENBQUM7SUFDN0IsS0FBSyxNQUFNLEdBQUcsSUFBSSxJQUFJLEVBQUUsQ0FBQztRQUN2QixNQUFNLENBQUMsRUFBRSxFQUFFLEVBQUUsQ0FBQyxHQUFHLEdBQUcsQ0FBQyxLQUFLLENBQUMsR0FBRyxDQUFDLENBQUM7UUFDaEMsTUFBTSxDQUFDLEdBQUcsVUFBVSxDQUFDLEVBQUUsQ0FBQyxDQUFDO1FBQ3pCLE1BQU0sQ0FBQyxHQUFHLFVBQVUsQ0FBQyxFQUFFLENBQUMsQ0FBQztRQUN6QixJQUFJLENBQUMsTUFBTSxDQUFDLFFBQVEsQ0FB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O0lBQ3ZCLE1BQU0sR0FBRyxHQUFHLENBQUMsQ0FBQyxFQUFFLEdBQUcsR0FBRyxDQUFDO0lBQ3ZCLEtBQUssSUFBSSxHQUFHLEdBQUcsQ0FBQyxFQUFFLEdBQUcsR0FBRyxDQUFDLENBQUMsRUFBRSxFQUFFLEdBQUcsRUFBRSxFQUFFLENBQUM7UUFDcEMsTUFBTSxHQUFHLEdBQUcsR0FBRyxHQUFHLENBQUMsQ0FBQyxFQUFFLENBQUM7UUFDdkIsTUFBTSxHQUFHLEdBQUcsQ0FBQyxHQUFHLEdBQUcsR0FBRyxDQUFDLEdBQUcsR0FBRyxHQUFHLEdBQUcsQ0FBQztRQUNwQyxLQUFLLENBQUMsR0FBRyxDQUFDLENBQUMsQ0FBQyxJQUFJLENBQUMsUUFBUSxDQUFDLEdBQUcsRUFBRSxHQUFHLEdBQUcsQ0FBQyxDQUFDLEVBQUUsQ0FBQyxFQUFFLEdBQUcsQ0FBQyxDQUFDO0lBQ25ELENBQUM7SUFDRCxNQUFNLEdBQUcsR0FBRyxDQUFDLENBQUMsRUFBRSxHQUFHLEdBQUcsQ0FBQztJQUN2QixNQUFNLEdBQUcsR0FBRyxDQUFDLENBQUMsRUFBRSxHQUFHLEdBQUcsQ0FBQztJQUN2QixLQUFLLElBQUksR0FBRyxHQUFHLENBQUMsRUFBRSxHQUFHLEdBQUcsQ0FBQyxDQUFDLEVBQUUsRUFBRSxHQUFHLEVBQUUsRUFBRSxDQUFDO1FBQ3BDLE1BQU0sTUFBTSxHQUFHLEdBQUcsR0FBRyxDQUFDLENBQUMsRUFBRSxDQUFDO1FBQzFCLE1BQU0sTUFBTSxHQUFHLENBQUMsR0FBRyxHQUFHLEdBQUcsQ0FBQyxHQUFHLEdBQUcsQ0FBQztRQUNqQyxLQUFLLElBQUksR0FBRyxHQUFHLENBQUMsRUFBRSxHQUFHLEdBQUcsQ0FBQyxDQUFDLEVBQUUsRUFBRSxHQUFHLEVBQUUsRUFBRSxDQUFDO1lBQ3BDLElBQUksQ0FBQyxDQUFDLElBQUksQ0FBQyxNQUFNLEdBQUcsR0FBRyxDQUFDO2dCQUFFLEtBQUssQ0FBQyxNQUFNLEdBQUcsQ0FBQyxHQUFHLEdBQUcsR0FBRyxDQUFDLENBQUMsR0FBRyxDQUFDLENBQUM7UUFDNUQs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WyIvKipcbiAqIFB1cmUgZ2VvbWV0cnkgaGVscGVycyBleHRyYWN0ZWQgZnJvbSBQbG90bHlTZXJ2aWNlLlxuICpcbiAqIE5vIERPTSBhY2Nlc3MsIG5vIHNlcnZpY2Ugc3RhdGUsIG5vIGB0aGlzYCDigJQgZXZlcnkgZnVuY3Rpb24gaXMgdGVzdGFibGVcbiAqIGluIGlzb2xhdGlvbi4gS2VlcCBpdCB0aGF0IHdheTogdGhpcyBtb2R1bGUgaXMgdGhlIGR1bXBpbmcgZ3JvdW5kIGZvclxuICogc2hhcGUvbWFzay9TVkctcGF0aCB1dGlsaXRpZXMgdGhlIHdhbmQsIHZlcnRleCBlcmFzZXIsIGFuZCByZWdpb24gZWRpdG9yXG4gKiBzaGFyZS4gSWYgYSBoZWxwZXIgZ3Jvd3MgYSBgdGhpcy5zaGFwZXNgIHJlZmVyZW5jZSwgaXQgZG9lc24ndCBiZWxvbmcgaGVyZS5cbiAqL1xuXG4vKiogQSBiYm94LXJlbGF0aXZlIGJpbmFyeSBtYXNrICgxID0gZmlsbGVkLCAwID0gZW1wdHkpLiAqL1xuZXhwb3J0IGludGVyZmFjZSBCQm94TWFzayB7XG4gIGJ4OiBudW1iZXI7XG4gIGJ5OiBudW1iZXI7XG4gIGJ3OiBudW1iZXI7XG4gIGJoOiBudW1iZXI7XG4gIG1hc2s6IFVpbnQ4QXJyYXk7XG59XG5cbi8qKiBQYXJzZWQgc2luZ2xlLXN1YnBhdGggcG9seWdvbiB2ZXJ0aWNlcy4gKi9cbmV4cG9ydCBpbnRlcmZhY2UgUG9seWdvblZlcnRpY2VzIHtcbiAgeHBvaW50czogbnVtYmVyW107XG4gIHlwb2ludHM6IG51bWJlcltdO1xufVxuXG4vLyDilIDilIAgU1ZHIHBhdGggcGFyc2luZyAvIGJ1aWxkaW5nIOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgFxuXG4vKipcbiAqIFBhcnNlIGEgc2luZ2xlLXN1YnBhdGggYE0geCx5IEwgeCx5IC4uLiBaYCBwYXRoIGludG8gdmVydGV4IGFycmF5cy5cbiAqXG4gKiBSZXR1cm5zIG51bGwgZm9yIHBhdGhzIHdlIGNhbid0IHNhZmVseSByb3VuZC10cmlwIChtdWx0aS1zdWJwYXRoLCBjdXJ2ZXMsXG4gKiBmZXdlciB0aGFuIHRocmVlIHZlcnRpY2VzKS4gVGhlIHdhbmQgb25seSBhZG9wdHMgc2hhcGVzIGl0IHByb2R1Y2VkIG9yXG4gKiB0aGF0IFBsb3RseSdzIGRyYXdjbG9zZWRwYXRoIHByb2R1Y2VzLCBib3RoIG9mIHdoaWNoIGFyZSBzaW5nbGUtc3VicGF0aFxuICogcG9seWdvbnMuXG4gKi9cbmV4cG9ydCBmdW5jdGlvbiBwYXJzZVN2Z1BhdGhQb2x5Z29uKHBhdGg6IHN0cmluZyk6IFBvbHlnb25WZXJ0aWNlcyB8IG51bGwge1xuICBpZiAoIXBhdGggfHwgcGF0aFswXSAhPT0gJ00nKSByZXR1cm4gbnVsbDtcbiAgaWYgKHBhdGguaW5kZXhPZignTScsIDEpICE9PSAtMSkgcmV0dXJuIG51bGw7IC8vIG11bHRpLXN1YnBhdGhcbiAgY29uc3QgdHJpbW1lZCA9IHBhdGguZW5kc1dpdGgoJ1onKSA/IHBhdGguc2xpY2UoMSwgLTEpIDogcGF0aC5zbGljZSgxKTtcbiAgY29uc3Qgc2VncyA9IHRyaW1tZWQuc3BsaXQoJ0wnKTtcbiAgY29uc3QgeHBvaW50czogbnVtYmVyW10gPSBbXTtcbiAgY29uc3QgeXBvaW50czogbnVtYmVyW10gPSBbXTtcbiAgZm9yIChjb25zdCBzZWcgb2Ygc2Vncykge1xuICAgIGNvbnN0IFtzeCwgc3ldID0gc2VnLnNwbGl0KCcsJyk7XG4gICAgY29uc3QgeCA9IHBhcnNlRmxvYXQoc3gpO1xuICAgIGNvbnN0IHkgPSBwYXJzZUZsb2F0KHN5KTtcbiAgICBpZiAoIU51bWJlci5pc0Zpbml0ZSh4KSB8fCAhTnVtYmVyLmlzRmluaXRlKHkpKSByZXR1cm4gbnVsbDtcbiAgICB4cG9pbnRzLnB1c2goeCk7XG4gICAgeXBvaW50cy5wdXNoKHkpO1xuICB9XG4gIGlmICh4cG9pbnRzLmxlbmd0aCA8IDMpIHJldHVybiBudWxsO1xuICByZXR1cm4geyB4cG9pbnRzLCB5cG9pbnRzIH07XG59XG5cbi8qKlxuICogQnVpbGQgYSBjbG9zZWQgU1ZHIHBhdGggc3RyaW5nIGBNIHgseSBMIHgseSBMIC4uLiBaYCBmcm9tIHZlcnRleCBhcnJheXMuXG4gKiBSZXR1cm5zICcnIGZvciBmZXdlciB0aGFuIHRocmVlIHZlcnRpY2VzIChkZWdlbmVyYXRlKS5cbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIHBvbHlnb25Ub1N2Z1BhdGgoeHBvaW50czogbnVtYmVyW10sIHlwb2ludHM6IG51bWJlcltdKTogc3RyaW5nIHtcbiAgaWYgKHhwb2ludHMubGVuZ3RoIDwgMykgcmV0dXJuICcnO1xuICBsZXQgcGF0aCA9ICdNJztcbiAgZm9yIChsZXQgaSA9IDA7IGkgPCB4cG9pbnRzLmxlbmd0aDsgaSsrKSB7XG4gICAgcGF0aCArPSBgJHt4cG9pbnRzW2ldfSwke3lwb2ludHNbaV19YDtcbiAgICBwYXRoICs9IGkgPCB4cG9pbnRzLmxlbmd0aCAtIDEgPyAnTCcgOiAnWic7XG4gIH1cbiAgcmV0dXJuIHBhdGg7XG59XG5cbi8qKlxuICogQnVpbGQgYW4gU1ZHIHBhdGggc3RyaW5nIGZyb20gdmVydGV4IGFycmF5cy4gQ2xvc2VkIHBvbHlnb25zIGVuZCB3aXRoIGBaYCxcbiAqIG9wZW4gcG9seWxpbmVzIGRvIG5vdC4gUmV0dXJucyAnJyBmb3IgZmV3ZXIgdGhhbiB0d28gdmVydGljZXMgKGRlZ2VuZXJhdGUpLlxuICovXG5leHBvcnQgZnVuY3Rpb24gdmVydGljZXNUb1N2Z1BhdGgoeHM6IG51bWJlcltdLCB5czogbnVtYmVyW10sIGNsb3NlZDogYm9vbGVhbik6IHN0cmluZyB7XG4gIGlmICh4cy5sZW5ndGggPCAyKSByZXR1cm4gJyc7XG4gIGxldCBwYXRoID0gJ00nO1xuICBmb3IgKGxldCBpID0gMDsgaSA8IHhzLmxlbmd0aDsgaSsrKSB7XG4gICAgcGF0aCArPSBgJHt4c1tpXX0sJHt5c1tpXX1gO1xuICAgIGlmIChpIDwgeHMubGVuZ3RoIC0gMSkgcGF0aCArPSAnTCc7XG4gIH1cbiAgaWYgKGNsb3NlZCkgcGF0aCArPSAnWic7XG4gIHJldHVybiBwYXRoO1xufVxuXG4vLyDilIDilIAgUmVnaW9uIGVxdWFsaXR5IOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgFxuXG4vKipcbiAqIENvbXBhcmUgdHdvIFBsb3RseSBzaGFwZSBvYmplY3RzIGZvciBcInNhbWUgcmVnaW9uXCIg4oCUIHVzZWQgYnkgdGhlIHdhbmQgdG9cbiAqIGF2b2lkIGFwcGVuZGluZyBkdXBsaWNhdGVzIHdoZW4gdGhlIHVzZXIgcHVzaGVzIHRoZSBzYW1lIGZpbmQgcmVzdWx0IG1vcmVcbiAqIHRoYW4gb25jZS4gUG9seWdvbnMgY29tcGFyZSBieSBTVkcgcGF0aCBzdHJpbmcsIHJlY3RhbmdsZXMgY29tcGFyZSBieVxuICogKHgwLCB5MCwgeDEsIHkxKS5cbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIHNoYXBlc0VxdWFsKGE6IGFueSwgYjogYW55KTogYm9vbGVhbiB7XG4gIGlmIChhLnBhdGggJiYgYi5wYXRoKSB7XG4gICAgcmV0dXJuIGEucGF0aCA9PT0gYi5wYXRoO1xuICB9XG4gIGlmIChcbiAgICBhLngwICE9PSB1bmRlZmluZWQgJiYgYS55MCAhPT0gdW5kZWZpbmVkICYmXG4gICAgYS54MSAhPT0gdW5kZWZpbmVkICYmIGEueTEgIT09IHVuZGVmaW5lZCAmJlxuICAgIGIueDAgIT09IHVuZGVmaW5lZCAmJiBiLnkwICE9PSB1bmRlZmluZWQgJiZcbiAgICBiLngxICE9PSB1bmRlZmluZWQgJiYgYi55MSAhPT0gdW5kZWZpbmVkXG4gICkge1xuICAgIHJldHVybiBhLngwID09PSBiLngwICYmXG4gICAgICAgICAgIGEueTAgPT09IGIueTAgJiZcbiAgICAgICAgICAgYS54MSA9PT0gYi54MSAmJlxuICAgICAgICAgICBhLnkxID09PSBiLnkxO1xuICB9XG4gIHJldHVybiBmYWxzZTtcbn1cblxuLy8g4pSA4pSAIEJCb3gtbWFzayBvcGVyYXRpb25zICh1c2VkIGJ5IHRoZSB3YW5kIHN0cm9rZSBhY2N1bXVsYXRvcikg4pSA4pSA4pSA4pSA4pSA4pSA4pSAXG5cbi8qKiBSZXR1cm5zIHRydWUgaWYgdGhlIHR3byBiYm94LXJlbGF0aXZlIG1hc2tzIHNoYXJlIGF0IGxlYXN0IG9uZSBzZXQgcGl4ZWwuICovXG5leHBvcnQgZnVuY3Rpb24gbWFza3NPdmVybGFwKGE6IEJCb3hNYXNrLCBiOiBCQm94TWFzayk6IGJvb2xlYW4ge1xuICBjb25zdCBpeDAgPSBNYXRoLm1heChhLmJ4LCBiLmJ4KTtcbiAgY29uc3QgaXkwID0gTWF0aC5tYXgoYS5ieSwgYi5ieSk7XG4gIGNvbnN0IGl4MSA9IE1hdGgubWluKGEuYnggKyBhLmJ3LCBiLmJ4ICsgYi5idyk7XG4gIGNvbnN0IGl5MSA9IE1hdGgubWluKGEuYnkgKyBhLmJoLCBiLmJ5ICsgYi5iaCk7XG4gIGlmIChpeDAgPj0gaXgxIHx8IGl5MCA+PSBpeTEpIHJldHVybiBmYWxzZTtcbiAgZm9yIChsZXQgeSA9IGl5MDsgeSA8IGl5MTsgeSsrKSB7XG4gICAgY29uc3QgYXIgPSAoeSAtIGEuYnkpICogYS5idztcbiAgICBjb25zdCBiciA9ICh5IC0gYi5ieSkgKiBiLmJ3O1xuICAgIGZvciAobGV0IHggPSBpeDA7IHggPCBpeDE7IHgrKykge1xuICAgICAgaWYgKGEubWFza1thciArICh4IC0gYS5ieCldICYmIGIubWFza1ticiArICh4IC0gYi5ieCldKSByZXR1cm4gdHJ1ZTtcbiAgICB9XG4gIH1cbiAgcmV0dXJuIGZhbHNlO1xufVxuXG4vKipcbiAqIEFsbG9jYXRlIGEgZnJlc2ggYWNjdW11bGF0b3IgY292ZXJpbmcgYm90aCBtYXNrcyBhbmQgT1IgdGhlbSBpbi4gVGhlXG4gKiByZXN1bHRpbmcgbWFzaydzIGJib3ggaXMgdGhlIHNtYWxsZXN0IHJlY3RhbmdsZSBjb250YWluaW5nIGJvdGggaW5wdXRzLlxuICovXG5leHBvcnQgZnVuY3Rpb24gdW5pb25NYXNrcyhhOiBCQm94TWFzaywgYjogQkJveE1hc2spOiBCQm94TWFzayB7XG4gIGNvbnN0IHV4MCA9IE1hdGgubWluKGEuYngsIGIuYngpO1xuICBjb25zdCB1eTAgPSBNYXRoLm1pbihhLmJ5LCBiLmJ5KTtcbiAgY29uc3QgdXgxID0gTWF0aC5tYXgoYS5ieCArIGEuYncsIGIuYnggKyBiLmJ3KTtcbiAgY29uc3QgdXkxID0gTWF0aC5tYXgoYS5ieSArIGEuYmgsIGIuYnkgKyBiLmJoKTtcbiAgY29uc3QgdWJ3ID0gdXgxIC0gdXgwO1xuICBjb25zdCB1YmggPSB1eTEgLSB1eTA7XG4gIGNvbnN0IHVtYXNrID0gbmV3IFVpbnQ4QXJyYXkodWJ3ICogdWJoKTtcblxuICBjb25zdCBkYXggPSBhLmJ4IC0gdXgwO1xuICBjb25zdCBkYXkgPSBhLmJ5IC0gdXkwO1xuICBmb3IgKGxldCByb3cgPSAwOyByb3cgPCBhLmJoOyByb3crKykge1xuICAgIGNvbnN0IHNyYyA9IHJvdyAqIGEuYnc7XG4gICAgY29uc3QgZHN0ID0gKHJvdyArIGRheSkgKiB1YncgKyBkYXg7XG4gICAgdW1hc2suc2V0KGEubWFzay5zdWJhcnJheShzcmMsIHNyYyArIGEuYncpLCBkc3QpO1xuICB9XG4gIGNvbnN0IGRieCA9IGIuYnggLSB1eDA7XG4gIGNvbnN0IGRieSA9IGIuYnkgLSB1eTA7XG4gIGZvciAobGV0IHJvdyA9IDA7IHJvdyA8IGIuYmg7IHJvdysrKSB7XG4gICAgY29uc3Qgc3JjUm93ID0gcm93ICogYi5idztcbiAgICBjb25zdCBkc3RSb3cgPSAocm93ICsgZGJ5KSAqIHVidztcbiAgICBmb3IgKGxldCBjb2wgPSAwOyBjb2wgPCBiLmJ3OyBjb2wrKykge1xuICAgICAgaWYgKGIubWFza1tzcmNSb3cgKyBjb2xdKSB1bWFza1tkc3RSb3cgKyAoZGJ4ICsgY29sKV0gPSAxO1xuICAgIH1cbiAgfVxuICByZXR1cm4geyBieDogdXgwLCBieTogdXkwLCBidzogdWJ3LCBiaDogdWJoLCBtYXNrOiB1bWFzayB9O1xufVxuXG4vLyDilIDilIAgUmluZyBzaW1wbGlmaWNhdGlvbiAoRG91Z2xhc+KAk1BldWNrZXIpIOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgFxuXG4vKiogUGVycGVuZGljdWxhciBkaXN0YW5jZSBmcm9tIChweCxweSkgdG8gdGhlIHNlZ21lbnQgKGF4LGF5KeKAkyhieCxieSkuICovXG5mdW5jdGlvbiBwZXJwRGlzdGFuY2UocHg6IG51bWJlciwgcHk6IG51bWJlcixcbiAgICAgICAgICAgICAgICAgICAgICBheDogbnVtYmVyLCBheTogbnVtYmVyLCBieDogbnVtYmVyLCBieTogbnVtYmVyKTogbnVtYmVyIHtcbiAgY29uc3QgZHggPSBieCAtIGF4LCBkeSA9IGJ5IC0gYXk7XG4gIGNvbnN0IGxlbjIgPSBkeCAqIGR4ICsgZHkgKiBkeTtcbiAgaWYgKGxlbjIgPT09IDApIHJldHVybiBNYXRoLmh5cG90KHB4IC0gYXgsIHB5IC0gYXkpO1xuICBsZXQgdCA9ICgocHggLSBheCkgKiBkeCArIChweSAtIGF5KSAqIGR5KSAvIGxlbjI7XG4gIHQgPSBNYXRoLm1heCgwLCBNYXRoLm1pbigxLCB0KSk7XG4gIHJldHVybiBNYXRoLmh5cG90KHB4IC0gKGF4ICsgdCAqIGR4KSwgcHkgLSAoYXkgKyB0ICogZHkpKTtcbn1cblxuLyoqXG4gKiBTaW1wbGlmeSBhIGNsb3NlZCByaW5nIHdpdGggdGhlIERvdWdsYXPigJNQZXVja2VyIGFsZ29yaXRobSAoaml0LXVpIzg1KTpcbiAqIGRyb3AgZXZlcnkgdmVydGV4IHRoYXQgbGllcyB3aXRoaW4gYGVwc2lsb25gIHBpeGVscyAodGhlIFwiYWx0aXR1ZGVcbiAqIHRocmVzaG9sZFwiKSBvZiB0aGUgbGluZSBiZXR3ZWVuIGl0cyBrZXB0IG5laWdoYm91cnMuIEFuY2hvcmVkIGF0IHZlcnRleCAwXG4gKiAodGhlIHJpbmcgaXMgY2xvc2VkIGJ5IGFwcGVuZGluZyBpdCkuIFJpbmdzIG9mIOKJpCAzIHZlcnRpY2VzLCBvciBhXG4gKiBub24tcG9zaXRpdmUgZXBzaWxvbiwgYXJlIHJldHVybmVkIHVuY2hhbmdlZC4gT3V0cHV0IGtlZXBzIHRoZSBjbG9zZWQtcmluZ1xuICogY29udmVudGlvbiAobm8gcmVwZWF0ZWQgY2xvc2luZyBwb2ludCkuXG4gKi9cbmV4cG9ydCBmdW5jdGlvbiBzaW1wbGlmeVJpbmcoeHM6IG51bWJlcltdLCB5czogbnVtYmVyW10sIGVwc2lsb246IG51bWJlcilcbiAgOiB7IHhzOiBudW1iZXJbXTsgeXM6IG51bWJlcltdIH0ge1xuICBjb25zdCBuID0geHMubGVuZ3RoO1xuICBpZiAobiA8PSAzIHx8ICEoZXBzaWxvbiA+IDApKSByZXR1cm4geyB4czogeHMuc2xpY2UoKSwgeXM6IHlzLnNsaWNlKCkgfTtcblxuICAvLyBDbG9zZSB0aGUgcmluZyBzbyBib3RoIGVuZHBvaW50cyBvZiB0aGUgRFAgcnVuIGFyZSB2ZXJ0ZXggMC5cbiAgY29uc3QgcHggPSBbLi4ueHMsIHhzWzBdXTtcbiAgY29uc3QgcHkgPSBbLi4ueXMsIHlzWzBdXTtcbiAgY29uc3QgbSA9IHB4Lmxlbmd0aDtcbiAgY29uc3Qga2VlcCA9IG5ldyBBcnJheTxib29sZWFuPihtKS5maWxsKGZhbHNlKTtcbiAga2VlcFswXSA9IHRydWU7XG4gIGtlZXBbbSAtIDFdID0gdHJ1ZTtcblxuICBjb25zdCBzdGFjazogQXJyYXk8W251bWJlciwgbnVtYmVyXT4gPSBbWzAsIG0gLSAxXV07XG4gIHdoaWxlIChzdGFjay5sZW5ndGgpIHtcbiAgICBjb25zdCBbYSwgYl0gPSBzdGFjay5wb3AoKSBhcyBbbnVtYmVyLCBudW1iZXJdO1xuICAgIGxldCBtYXhEID0gLTEsIGlkeCA9IC0xO1xuICAgIGZvciAobGV0IGkgPSBhICsgMTsgaSA8IGI7IGkrKykge1xuICAgICAgY29uc3QgZCA9IHBlcnBEaXN0YW5jZShweFtpXSwgcHlbaV0sIHB4W2FdLCBweVthXSwgcHhbYl0sIHB5W2JdKTtcbiAgICAgIGlmIChkID4gbWF4RCkgeyBtYXhEID0gZDsgaWR4ID0gaTsgfVxuICAgIH1cbiAgICBpZiAobWF4RCA+IGVwc2lsb24gJiYgaWR4ID4gYSkge1xuICAgICAga2VlcFtpZHhdID0gdHJ1ZTtcbiAgICAgIHN0YWNrLnB1c2goW2EsIGlkeF0sIFtpZHgsIGJdKTtcbiAgICB9XG4gIH1cblxuICBjb25zdCBvdXRYOiBudW1iZXJbXSA9IFtdLCBvdXRZOiBudW1iZXJbXSA9IFtdO1xuICBmb3IgKGxldCBpID0gMDsgaSA8IG0gLSAxOyBpKyspIHsgICAgICAvLyBkcm9wIHRoZSBkdXBsaWNhdGVkIGNsb3NpbmcgdmVydGV4XG4gICAgaWYgKGtlZXBbaV0pIHsgb3V0WC5wdXNoKHB4W2ldKTsgb3V0WS5wdXNoKHB5W2ldKTsgfVxuICB9XG4gIHJldHVybiB7IHhzOiBvdXRYLCB5czogb3V0WSB9O1xufVxuIl19
|