@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,126 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Draggable Z-height handle for the napari-js WebGPU volume (jit-ui#102). A small DOM grip is drawn
|
|
3
|
+
* over the 3D canvas at the projected top-face centre of the volume box (reusing the axes-label
|
|
4
|
+
* {@link projectPoint} projection, so it tracks the box as the volume orbits). Dragging it up makes
|
|
5
|
+
* the volume taller, down makes it flatter — the host maps the drag to a Z-scale factor and
|
|
6
|
+
* restretches the volume + axes live (only the model matrix changes; the voxel texture is untouched).
|
|
7
|
+
*
|
|
8
|
+
* Direct-manipulation counterpart of {@link NapariAxesLabels}: both are absolute-positioned overlays
|
|
9
|
+
* updated on every `camera3d.changed`, but this one accepts pointer input.
|
|
10
|
+
*/
|
|
11
|
+
import { projectPoint } from './napari-axes-labels';
|
|
12
|
+
const DEFAULTS = { pixelsPerDouble: 180, minScale: 0.1, maxScale: 10 };
|
|
13
|
+
export class NapariVolumeZHandle {
|
|
14
|
+
constructor(host, camera3d, opts) {
|
|
15
|
+
this.host = host;
|
|
16
|
+
this.camera3d = camera3d;
|
|
17
|
+
this.visible = true;
|
|
18
|
+
this.dragging = false;
|
|
19
|
+
this.startY = 0;
|
|
20
|
+
this.startScale = 1;
|
|
21
|
+
this.onPointerDown = (e) => {
|
|
22
|
+
e.preventDefault();
|
|
23
|
+
e.stopPropagation();
|
|
24
|
+
this.dragging = true;
|
|
25
|
+
this.startY = e.clientY;
|
|
26
|
+
this.startScale = this.opts.getScale();
|
|
27
|
+
this.el.setPointerCapture?.(e.pointerId);
|
|
28
|
+
};
|
|
29
|
+
this.onPointerMove = (e) => {
|
|
30
|
+
if (!this.dragging)
|
|
31
|
+
return;
|
|
32
|
+
e.preventDefault();
|
|
33
|
+
e.stopPropagation();
|
|
34
|
+
// Up (negative dy) → taller. Exponential so each `pixelsPerDouble` px doubles/halves the height.
|
|
35
|
+
const dy = e.clientY - this.startY;
|
|
36
|
+
const factor = Math.pow(2, -dy / this.opts.pixelsPerDouble);
|
|
37
|
+
const next = Math.min(this.opts.maxScale, Math.max(this.opts.minScale, this.startScale * factor));
|
|
38
|
+
this.opts.setScale(next);
|
|
39
|
+
this.reposition();
|
|
40
|
+
};
|
|
41
|
+
this.onPointerUp = (e) => {
|
|
42
|
+
if (!this.dragging)
|
|
43
|
+
return;
|
|
44
|
+
this.dragging = false;
|
|
45
|
+
if (this.el.hasPointerCapture?.(e.pointerId))
|
|
46
|
+
this.el.releasePointerCapture?.(e.pointerId);
|
|
47
|
+
};
|
|
48
|
+
this.onDoubleClick = (e) => {
|
|
49
|
+
e.preventDefault();
|
|
50
|
+
e.stopPropagation();
|
|
51
|
+
this.opts.setScale(1);
|
|
52
|
+
this.reposition();
|
|
53
|
+
};
|
|
54
|
+
this.opts = { ...DEFAULTS, ...opts };
|
|
55
|
+
if (getComputedStyle(host).position === 'static')
|
|
56
|
+
host.style.position = 'relative';
|
|
57
|
+
this.el = document.createElement('div');
|
|
58
|
+
Object.assign(this.el.style, {
|
|
59
|
+
position: 'absolute',
|
|
60
|
+
zIndex: '31',
|
|
61
|
+
width: '22px',
|
|
62
|
+
height: '22px',
|
|
63
|
+
marginLeft: '-11px',
|
|
64
|
+
marginTop: '-11px',
|
|
65
|
+
borderRadius: '50%',
|
|
66
|
+
background: 'rgba(102,140,255,0.85)', // matches the Z axis colour (#668cff)
|
|
67
|
+
border: '1.5px solid #fff',
|
|
68
|
+
boxShadow: '0 0 4px rgba(0,0,0,0.6)',
|
|
69
|
+
color: '#fff',
|
|
70
|
+
font: '13px sans-serif',
|
|
71
|
+
lineHeight: '20px',
|
|
72
|
+
textAlign: 'center',
|
|
73
|
+
cursor: 'ns-resize',
|
|
74
|
+
touchAction: 'none',
|
|
75
|
+
userSelect: 'none',
|
|
76
|
+
});
|
|
77
|
+
this.el.textContent = '↕';
|
|
78
|
+
this.el.title = 'Drag to change the volume height (Z). Double-click to reset.';
|
|
79
|
+
this.host.appendChild(this.el);
|
|
80
|
+
this.el.addEventListener('pointerdown', this.onPointerDown);
|
|
81
|
+
this.el.addEventListener('pointermove', this.onPointerMove);
|
|
82
|
+
this.el.addEventListener('pointerup', this.onPointerUp);
|
|
83
|
+
this.el.addEventListener('pointercancel', this.onPointerUp);
|
|
84
|
+
this.el.addEventListener('dblclick', this.onDoubleClick);
|
|
85
|
+
this.disconnectCamera = this.camera3d.changed.connect(() => this.reposition());
|
|
86
|
+
if (typeof ResizeObserver !== 'undefined') {
|
|
87
|
+
this.resizeObserver = new ResizeObserver(() => this.reposition());
|
|
88
|
+
this.resizeObserver.observe(this.host);
|
|
89
|
+
}
|
|
90
|
+
this.reposition();
|
|
91
|
+
}
|
|
92
|
+
setVisible(visible) {
|
|
93
|
+
this.visible = visible;
|
|
94
|
+
this.reposition();
|
|
95
|
+
}
|
|
96
|
+
/** Reproject the handle to the current top-face anchor. Called on camera move, resize, and after
|
|
97
|
+
* the host applies a new scale (so the grip follows the growing/shrinking box). */
|
|
98
|
+
reposition() {
|
|
99
|
+
const vw = this.host.clientWidth;
|
|
100
|
+
const vh = this.host.clientHeight;
|
|
101
|
+
if (!this.visible || vw <= 0 || vh <= 0) {
|
|
102
|
+
this.el.style.display = 'none';
|
|
103
|
+
return;
|
|
104
|
+
}
|
|
105
|
+
const mvp = this.camera3d.viewProjection(vw, vh);
|
|
106
|
+
const s = projectPoint(mvp, this.opts.topAnchor(), vw, vh);
|
|
107
|
+
if (!s.visible) {
|
|
108
|
+
this.el.style.display = 'none';
|
|
109
|
+
return;
|
|
110
|
+
}
|
|
111
|
+
this.el.style.display = '';
|
|
112
|
+
this.el.style.left = `${s.x}px`;
|
|
113
|
+
this.el.style.top = `${s.y}px`;
|
|
114
|
+
}
|
|
115
|
+
destroy() {
|
|
116
|
+
this.disconnectCamera();
|
|
117
|
+
this.resizeObserver?.disconnect();
|
|
118
|
+
this.el.removeEventListener('pointerdown', this.onPointerDown);
|
|
119
|
+
this.el.removeEventListener('pointermove', this.onPointerMove);
|
|
120
|
+
this.el.removeEventListener('pointerup', this.onPointerUp);
|
|
121
|
+
this.el.removeEventListener('pointercancel', this.onPointerUp);
|
|
122
|
+
this.el.removeEventListener('dblclick', this.onDoubleClick);
|
|
123
|
+
this.el.parentNode?.removeChild(this.el);
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoibmFwYXJpLXZvbHVtZS16LWhhbmRsZS5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uLy4uLy4uL3NyYy9saWIvaW1wbGVtZW50YXRpb25zL25hcGFyaS1qcy9uYXBhcmktdm9sdW1lLXotaGFuZGxlLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBOzs7Ozs7Ozs7R0FTRztBQUNILE9BQU8sRUFBcUIsWUFBWSxFQUFFLE1BQU0sc0JBQXNCLENBQUM7QUFnQnZFLE1BQU0sUUFBUSxHQUFHLEVBQUUsZUFBZSxFQUFFLEdBQUcsRUFBRSxRQUFRLEVBQUUsR0FBRyxFQUFFLFFBQVEsRUFBRSxFQUFFLEVBQUUsQ0FBQztBQUV2RSxNQUFNLE9BQU8sbUJBQW1CO0lBVTlCLFlBQ21CLElBQWlCLEVBQ2pCLFFBQTJCLEVBQzVDLElBQTBCO1FBRlQsU0FBSSxHQUFKLElBQUksQ0FBYTtRQUNqQixhQUFRLEdBQVIsUUFBUSxDQUFtQjtRQVB0QyxZQUFPLEdBQUcsSUFBSSxDQUFDO1FBQ2YsYUFBUSxHQUFHLEtBQUssQ0FBQztRQUNqQixXQUFNLEdBQUcsQ0FBQyxDQUFDO1FBQ1gsZUFBVSxHQUFHLENBQUMsQ0FBQztRQXlFTixrQkFBYSxHQUFHLENBQUMsQ0FBZSxFQUFRLEVBQUU7WUFDekQsQ0FBQyxDQUFDLGNBQWMsRUFBRSxDQUFDO1lBQ25CLENBQUMsQ0FBQyxlQUFlLEVBQUUsQ0FBQztZQUNwQixJQUFJLENBQUMsUUFBUSxHQUFHLElBQUksQ0FBQztZQUNyQixJQUFJLENBQUMsTUFBTSxHQUFHLENBQUMsQ0FBQyxPQUFPLENBQUM7WUFDeEIsSUFBSSxDQUFDLFVBQVUsR0FBRyxJQUFJLENBQUMsSUFBSSxDQUFDLFFBQVEsRUFBRSxDQUFDO1lBQ3ZDLElBQUksQ0FBQyxFQUFFLENBQUMsaUJBQWlCLEVBQUUsQ0FBQyxDQUFDLENBQUMsU0FBUyxDQUFDLENBQUM7UUFDM0MsQ0FBQyxDQUFDO1FBRWUsa0JBQWEsR0FBRyxDQUFDLENBQWUs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QUM7UUFDL0QsSUFBSSxDQUFDLEVBQUUsQ0FBQyxtQkFBbUIsQ0FBQyxhQUFhLEVBQUUsSUFBSSxDQUFDLGFBQWEsQ0FBQyxDQUFDO1FBQy9ELElBQUksQ0FBQyxFQUFFLENBQUMsbUJBQW1CLENBQUMsV0FBVyxFQUFFLElBQUksQ0FBQyxXQUFXLENBQUMsQ0FBQztRQUMzRCxJQUFJLENBQUMsRUFBRSxDQUFDLG1CQUFtQixDQUFDLGVBQWUsRUFBRSxJQUFJLENBQUMsV0FBVyxDQUFDLENBQUM7UUFDL0QsSUFBSSxDQUFDLEVBQUUsQ0FBQyxtQkFBbUIsQ0FBQyxVQUFVLEVBQUUsSUFBSSxDQUFDLGFBQWEsQ0FBQyxDQUFDO1FBQzVELElBQUksQ0FBQyxFQUFFLENBQUMsVUFBVSxFQUFFLFdBQVcsQ0FBQyxJQUFJLENBQUMsRUFBRSxDQUFDLENBQUM7SUFDM0MsQ0FBQztDQUNGIiwic291cmNlc0NvbnRlbnQiOlsiLyoqXG4gKiBEcmFnZ2FibGUgWi1oZWlnaHQgaGFuZGxlIGZvciB0aGUgbmFwYXJpLWpzIFdlYkdQVSB2b2x1bWUgKGppdC11aSMxMDIpLiBBIHNtYWxsIERPTSBncmlwIGlzIGRyYXduXG4gKiBvdmVyIHRoZSAzRCBjYW52YXMgYXQgdGhlIHByb2plY3RlZCB0b3AtZmFjZSBjZW50cmUgb2YgdGhlIHZvbHVtZSBib3ggKHJldXNpbmcgdGhlIGF4ZXMtbGFiZWxcbiAqIHtAbGluayBwcm9qZWN0UG9pbnR9IHByb2plY3Rpb24sIHNvIGl0IHRyYWNrcyB0aGUgYm94IGFzIHRoZSB2b2x1bWUgb3JiaXRzKS4gRHJhZ2dpbmcgaXQgdXAgbWFrZXNcbiAqIHRoZSB2b2x1bWUgdGFsbGVyLCBkb3duIG1ha2VzIGl0IGZsYXR0ZXIg4oCUIHRoZSBob3N0IG1hcHMgdGhlIGRyYWcgdG8gYSBaLXNjYWxlIGZhY3RvciBhbmRcbiAqIHJlc3RyZXRjaGVzIHRoZSB2b2x1bWUgKyBheGVzIGxpdmUgKG9ubHkgdGhlIG1vZGVsIG1hdHJpeCBjaGFuZ2VzOyB0aGUgdm94ZWwgdGV4dHVyZSBpcyB1bnRvdWNoZWQpLlxuICpcbiAqIERpcmVjdC1tYW5pcHVsYXRpb24gY291bnRlcnBhcnQgb2Yge0BsaW5rIE5hcGFyaUF4ZXNMYWJlbHN9OiBib3RoIGFyZSBhYnNvbHV0ZS1wb3NpdGlvbmVkIG92ZXJsYXlzXG4gKiB1cGRhdGVkIG9uIGV2ZXJ5IGBjYW1lcmEzZC5jaGFuZ2VkYCwgYnV0IHRoaXMgb25lIGFjY2VwdHMgcG9pbnRlciBpbnB1dC5cbiAqL1xuaW1wb3J0IHsgQXhlc0xhYmVsQ2FtZXJhM0QsIHByb2plY3RQb2ludCB9IGZyb20gJy4vbmFwYXJpLWF4ZXMtbGFiZWxzJztcblxuZXhwb3J0IGludGVyZmFjZSBWb2x1bWVaSGFuZGxlT3B0aW9ucyB7XG4gIC8qKiBDdXJyZW50IHRvcC1mYWNlLWNlbnRyZSB3b3JsZCBwb2ludCAodGhlIGJveCBpcyBjZW50cmVkIGF0IHRoZSBvcmlnaW4sIHNvIGBbMCwgMCwgK2hhbGZaXWApLiAqL1xuICB0b3BBbmNob3I6ICgpID0+IFtudW1iZXIsIG51bWJlciwgbnVtYmVyXTtcbiAgLyoqIFRoZSBjdXJyZW50IFotc2NhbGUgZmFjdG9yICgxID0gdGhlIHZvbHVtZSdzIGRlZmF1bHQgcHJvcG9ydGlvbnMpLiAqL1xuICBnZXRTY2FsZTogKCkgPT4gbnVtYmVyO1xuICAvKiogQXBwbHkgYSBuZXcgWi1zY2FsZSBmYWN0b3Ig4oCUIHRoZSBob3N0IHJlc3RyZXRjaGVzIHRoZSB2b2x1bWUgKyBheGVzIGFuZCByZXBvc2l0aW9ucyBvdmVybGF5cy4gKi9cbiAgc2V0U2NhbGU6IChzY2FsZTogbnVtYmVyKSA9PiB2b2lkO1xuICAvKiogVmVydGljYWwgZHJhZyAocHgpIGZvciBvbmUgZG91YmxpbmcvaGFsdmluZyBvZiB0aGUgWiBoZWlnaHQuIExhcmdlciA9IGxlc3Mgc2Vuc2l0aXZlLiAqL1xuICBwaXhlbHNQZXJEb3VibGU/OiBudW1iZXI7XG4gIC8qKiBDbGFtcCBmb3IgdGhlIFotc2NhbGUgZmFjdG9yLiAqL1xuICBtaW5TY2FsZT86IG51bWJlcjtcbiAgbWF4U2NhbGU/OiBudW1iZXI7XG59XG5cbmNvbnN0IERFRkFVTFRTID0geyBwaXhlbHNQZXJEb3VibGU6IDE4MCwgbWluU2NhbGU6IDAuMSwgbWF4U2NhbGU6IDEwIH07XG5cbmV4cG9ydCBjbGFzcyBOYXBhcmlWb2x1bWVaSGFuZGxlIHtcbiAgcHJpdmF0ZSByZWFkb25seSBlbDogSFRNTERpdkVsZW1lbnQ7XG4gIHByaXZhdGUgcmVhZG9ubHkgZGlzY29ubmVjdENhbWVyYTogKCkgPT4gdm9pZDtcbiAgcHJpdmF0ZSByZWFkb25seSByZXNpemVPYnNlcnZlcj86IFJlc2l6ZU9ic2VydmVyO1xuICBwcml2YXRlIHJlYWRvbmx5IG9wdHM6IFJlcXVpcmVkPFZvbHVtZVpIYW5kbGVPcHRpb25zPjtcbiAgcHJpdmF0ZSB2aXNpYmxlID0gdHJ1ZTtcbiAgcHJpdmF0ZSBkcmFnZ2luZyA9IGZhbHNlO1xuICBwcml2YXRlIHN0YXJ0WSA9IDA7XG4gIHByaXZhdGUgc3RhcnRTY2FsZSA9IDE7XG5cbiAgY29uc3RydWN0b3IoXG4gICAgcHJpdmF0ZSByZWFkb25seSBob3N0OiBIVE1MRWxlbWVudCxcbiAgICBwcml2YXRlIHJlYWRvbmx5IGNhbWVyYTNkOiBBeGVzTGFiZWxDYW1lcmEzRCxcbiAgICBvcHRzOiBWb2x1bWVaSGFuZGxlT3B0aW9ucyxcbiAgKSB7XG4gICAgdGhpcy5vcHRzID0geyAuLi5ERUZBVUxUUywgLi4ub3B0cyB9O1xuICAgIGlmIChnZXRDb21wdXRlZFN0eWxlKGhvc3QpLnBvc2l0aW9uID09PSAnc3RhdGljJykgaG9zdC5zdHlsZS5wb3NpdGlvbiA9ICdyZWxhdGl2ZSc7XG5cbiAgICB0aGlzLmVsID0gZG9jdW1lbnQuY3JlYXRlRWxlbWVudCgnZGl2Jyk7XG4gICAgT2JqZWN0LmFzc2lnbih0aGlzLmVsLnN0eWxlLCB7XG4gICAgICBwb3NpdGlvbjogJ2Fic29sdXRlJyxcbiAgICAgIHpJbmRleDogJzMxJyxcbiAgICAgIHdpZHRoOiAnMjJweCcsXG4gICAgICBoZWlnaHQ6ICcyMnB4JyxcbiAgICAgIG1hcmdpbkxlZnQ6ICctMTFweCcsXG4gICAgICBtYXJnaW5Ub3A6ICctMTFweCcsXG4gICAgICBib3JkZXJSYWRpdXM6ICc1MCUnLFxuICAgICAgYmFja2dyb3VuZDogJ3JnYmEoMTAyLDE0MCwyNTUsMC44NSknLCAvLyBtYXRjaGVzIHRoZSBaIGF4aXMgY29sb3VyICgjNjY4Y2ZmKVxuICAgICAgYm9yZGVyOiAnMS41cHggc29saWQgI2ZmZicsXG4gICAgICBib3hTaGFkb3c6ICcwIDAgNHB4IHJnYmEoMCwwLDAsMC42KScsXG4gICAgICBjb2xvcjogJyNmZmYnLFxuICAgICAgZm9udDogJzEzcHggc2Fucy1zZXJpZicsXG4gICAgICBsaW5lSGVpZ2h0OiAnMjBweCcsXG4gICAgICB0ZXh0QWxpZ246ICdjZW50ZXInLFxuICAgICAgY3Vyc29yOiAnbnMtcmVzaXplJyxcbiAgICAgIHRvdWNoQWN0aW9uOiAnbm9uZScsXG4gICAgICB1c2VyU2VsZWN0OiAnbm9uZScsXG4gICAgfSBhcyBQYXJ0aWFsPENTU1N0eWxlRGVjbGFyYXRpb24+KTtcbiAgICB0aGlzLmVsLnRleHRDb250ZW50ID0gJ+KGlSc7XG4gICAgdGhpcy5lbC50aXRsZSA9ICdEcmFnIHRvIGNoYW5nZSB0aGUgdm9sdW1lIGhlaWdodCAoWikuIERvdWJsZS1jbGljayB0byByZXNldC4nO1xuICAgIHRoaXMuaG9zdC5hcHBlbmRDaGlsZCh0aGlzLmVsKTtcblxuICAgIHRoaXMuZWwuYWRkRXZlbnRMaXN0ZW5lcigncG9pbnRlcmRvd24nLCB0aGlzLm9uUG9pbnRlckRvd24pO1xuICAgIHRoaXMuZWwuYWRkRXZlbnRMaXN0ZW5lcigncG9pbnRlcm1vdmUnLCB0aGlzLm9uUG9pbnRlck1vdmUpO1xuICAgIHRoaXMuZWwuYWRkRXZlbnRMaXN0ZW5lcigncG9pbnRlcnVwJywgdGhpcy5vblBvaW50ZXJVcCk7XG4gICAgdGhpcy5lbC5hZGRFdmVudExpc3RlbmVyKCdwb2ludGVyY2FuY2VsJywgdGhpcy5vblBvaW50ZXJVcCk7XG4gICAgdGhpcy5lbC5hZGRFdmVudExpc3RlbmVyKCdkYmxjbGljaycsIHRoaXMub25Eb3VibGVDbGljayk7XG5cbiAgICB0aGlzLmRpc2Nvbm5lY3RDYW1lcmEgPSB0aGlzLmNhbWVyYTNkLmNoYW5nZWQuY29ubmVjdCgoKSA9PiB0aGlzLnJlcG9zaXRpb24oKSk7XG4gICAgaWYgKHR5cGVvZiBSZXNpemVPYnNlcnZlciAhPT0gJ3VuZGVmaW5lZCcpIHtcbiAgICAgIHRoaXMucmVzaXplT2JzZXJ2ZXIgPSBuZXcgUmVzaXplT2JzZXJ2ZXIoKCkgPT4gdGhpcy5yZXBvc2l0aW9uKCkpO1xuICAgICAgdGhpcy5yZXNpemVPYnNlcnZlci5vYnNlcnZlKHRoaXMuaG9zdCk7XG4gICAgfVxuICAgIHRoaXMucmVwb3NpdGlvbigpO1xuICB9XG5cbiAgc2V0VmlzaWJsZSh2aXNpYmxlOiBib29sZWFuKTogdm9pZCB7XG4gICAgdGhpcy52aXNpYmxlID0gdmlzaWJsZTtcbiAgICB0aGlzLnJlcG9zaXRpb24oKTtcbiAgfVxuXG4gIC8qKiBSZXByb2plY3QgdGhlIGhhbmRsZSB0byB0aGUgY3VycmVudCB0b3AtZmFjZSBhbmNob3IuIENhbGxlZCBvbiBjYW1lcmEgbW92ZSwgcmVzaXplLCBhbmQgYWZ0ZXJcbiAgICogIHRoZSBob3N0IGFwcGxpZXMgYSBuZXcgc2NhbGUgKHNvIHRoZSBncmlwIGZvbGxvd3MgdGhlIGdyb3dpbmcvc2hyaW5raW5nIGJveCkuICovXG4gIHJlcG9zaXRpb24oKTogdm9pZCB7XG4gICAgY29uc3QgdncgPSB0aGlzLmhvc3QuY2xpZW50V2lkdGg7XG4gICAgY29uc3QgdmggPSB0aGlzLmhvc3QuY2xpZW50SGVpZ2h0O1xuICAgIGlmICghdGhpcy52aXNpYmxlIHx8IHZ3IDw9IDAgfHwgdmggPD0gMCkge1xuICAgICAgdGhpcy5lbC5zdHlsZS5kaXNwbGF5ID0gJ25vbmUnO1xuICAgICAgcmV0dXJuO1xuICAgIH1cbiAgICBjb25zdCBtdnAgPSB0aGlzLmNhbWVyYTNkLnZpZXdQcm9qZWN0aW9uKHZ3LCB2aCk7XG4gICAgY29uc3QgcyA9IHByb2plY3RQb2ludChtdnAsIHRoaXMub3B0cy50b3BBbmNob3IoKSwgdncsIHZoKTtcbiAgICBpZiAoIXMudmlzaWJsZSkge1xuICAgICAgdGhpcy5lbC5zdHlsZS5kaXNwbGF5ID0gJ25vbmUnO1xuICAgICAgcmV0dXJuO1xuICAgIH1cbiAgICB0aGlzLmVsLnN0eWxlLmRpc3BsYXkgPSAnJztcbiAgICB0aGlzLmVsLnN0eWxlLmxlZnQgPSBgJHtzLnh9cHhgO1xuICAgIHRoaXMuZWwuc3R5bGUudG9wID0gYCR7cy55fXB4YDtcbiAgfVxuXG4gIHByaXZhdGUgcmVhZG9ubHkgb25Qb2ludGVyRG93biA9IChlOiBQb2ludGVyRXZlbnQpOiB2b2lkID0+IHtcbiAgICBlLnByZXZlbnREZWZhdWx0KCk7XG4gICAgZS5zdG9wUHJvcGFnYXRpb24oKTtcbiAgICB0aGlzLmRyYWdnaW5nID0gdHJ1ZTtcbiAgICB0aGlzLnN0YXJ0WSA9IGUuY2xpZW50WTtcbiAgICB0aGlzLnN0YXJ0U2NhbGUgPSB0aGlzLm9wdHMuZ2V0U2NhbGUoKTtcbiAgICB0aGlzLmVsLnNldFBvaW50ZXJDYXB0dXJlPy4oZS5wb2ludGVySWQpO1xuICB9O1xuXG4gIHByaXZhdGUgcmVhZG9ubHkgb25Qb2ludGVyTW92ZSA9IChlOiBQb2ludGVyRXZlbnQpOiB2b2lkID0+IHtcbiAgICBpZiAoIXRoaXMuZHJhZ2dpbmcpIHJldHVybjtcbiAgICBlLnByZXZlbnREZWZhdWx0KCk7XG4gICAgZS5zdG9wUHJvcGFnYXRpb24oKTtcbiAgICAvLyBVcCAobmVnYXRpdmUgZHkpIOKGkiB0YWxsZXIuIEV4cG9uZW50aWFsIHNvIGVhY2ggYHBpeGVsc1BlckRvdWJsZWAgcHggZG91Ymxlcy9oYWx2ZXMgdGhlIGhlaWdodC5cbiAgICBjb25zdCBkeSA9IGUuY2xpZW50WSAtIHRoaXMuc3RhcnRZO1xuICAgIGNvbnN0IGZhY3RvciA9IE1hdGgucG93KDIsIC1keSAvIHRoaXMub3B0cy5waXhlbHNQZXJEb3VibGUpO1xuICAgIGNvbnN0IG5leHQgPSBNYXRoLm1pbihcbiAgICAgIHRoaXMub3B0cy5tYXhTY2FsZSxcbiAgICAgIE1hdGgubWF4KHRoaXMub3B0cy5taW5TY2FsZSwgdGhpcy5zdGFydFNjYWxlICogZmFjdG9yKSxcbiAgICApO1xuICAgIHRoaXMub3B0cy5zZXRTY2FsZShuZXh0KTtcbiAgICB0aGlzLnJlcG9zaXRpb24oKTtcbiAgfTtcblxuICBwcml2YXRlIHJlYWRvbmx5IG9uUG9pbnRlclVwID0gKGU6IFBvaW50ZXJFdmVudCk6IHZvaWQgPT4ge1xuICAgIGlmICghdGhpcy5kcmFnZ2luZykgcmV0dXJuO1xuICAgIHRoaXMuZHJhZ2dpbmcgPSBmYWxzZTtcbiAgICBpZiAodGhpcy5lbC5oYXNQb2ludGVyQ2FwdHVyZT8uKGUucG9pbnRlcklkKSkgdGhpcy5lbC5yZWxlYXNlUG9pbnRlckNhcHR1cmU/LihlLnBvaW50ZXJJZCk7XG4gIH07XG5cbiAgcHJpdmF0ZSByZWFkb25seSBvbkRvdWJsZUNsaWNrID0gKGU6IE1vdXNlRXZlbnQpOiB2b2lkID0+IHtcbiAgICBlLnByZXZlbnREZWZhdWx0KCk7XG4gICAgZS5zdG9wUHJvcGFnYXRpb24oKTtcbiAgICB0aGlzLm9wdHMuc2V0U2NhbGUoMSk7XG4gICAgdGhpcy5yZXBvc2l0aW9uKCk7XG4gIH07XG5cbiAgZGVzdHJveSgpOiB2b2lkIHtcbiAgICB0aGlzLmRpc2Nvbm5lY3RDYW1lcmEoKTtcbiAgICB0aGlzLnJlc2l6ZU9ic2VydmVyPy5kaXNjb25uZWN0KCk7XG4gICAgdGhpcy5lbC5yZW1vdmVFdmVudExpc3RlbmVyKCdwb2ludGVyZG93bicsIHRoaXMub25Qb2ludGVyRG93bik7XG4gICAgdGhpcy5lbC5yZW1vdmVFdmVudExpc3RlbmVyKCdwb2ludGVybW92ZScsIHRoaXMub25Qb2ludGVyTW92ZSk7XG4gICAgdGhpcy5lbC5yZW1vdmVFdmVudExpc3RlbmVyKCdwb2ludGVydXAnLCB0aGlzLm9uUG9pbnRlclVwKTtcbiAgICB0aGlzLmVsLnJlbW92ZUV2ZW50TGlzdGVuZXIoJ3BvaW50ZXJjYW5jZWwnLCB0aGlzLm9uUG9pbnRlclVwKTtcbiAgICB0aGlzLmVsLnJlbW92ZUV2ZW50TGlzdGVuZXIoJ2RibGNsaWNrJywgdGhpcy5vbkRvdWJsZUNsaWNrKTtcbiAgICB0aGlzLmVsLnBhcmVudE5vZGU/LnJlbW92ZUNoaWxkKHRoaXMuZWwpO1xuICB9XG59XG4iXX0=
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
export class DisplayPipeline {
|
|
2
|
+
constructor(host) {
|
|
3
|
+
this.host = host;
|
|
4
|
+
}
|
|
5
|
+
/**
|
|
6
|
+
* Apply the current display pipeline to an RGBA buffer in place; returns
|
|
7
|
+
* whether any opaque pixel was written.
|
|
8
|
+
* - Grayscale: intensity → window + gamma + invert → colormap LUT.
|
|
9
|
+
* - RGB/multichannel: additive pseudo-colour merge — each visible channel's
|
|
10
|
+
* windowed intensity is tinted by its assigned colour and summed (Fiji
|
|
11
|
+
* "Merge Channels"). Defaults (R=red, G=green, B=blue) are the identity.
|
|
12
|
+
*/
|
|
13
|
+
applyToRgba(d) {
|
|
14
|
+
let changed = false;
|
|
15
|
+
const channelStates = this.host.channelStates();
|
|
16
|
+
const invertBg = this.host.invertBg();
|
|
17
|
+
if (this.host.isGrayscale()) {
|
|
18
|
+
const lut = this.host.colorLut();
|
|
19
|
+
if (!lut)
|
|
20
|
+
return false;
|
|
21
|
+
const ch = channelStates[0];
|
|
22
|
+
const wMin = ch ? ch.min : 0;
|
|
23
|
+
const wSpan = ch && ch.max > ch.min ? ch.max - ch.min : 255;
|
|
24
|
+
const invGamma = ch && ch.gamma > 0 ? 1 / ch.gamma : 1;
|
|
25
|
+
// Precompute raw(0..255) -> final RGB once (256 window+gamma+invert+colormap
|
|
26
|
+
// evaluations) and map each pixel by table lookup — a Math.pow per pixel
|
|
27
|
+
// (~262k/tile) made the window/gamma sliders crawl on large stacks.
|
|
28
|
+
const rL = new Uint8ClampedArray(256);
|
|
29
|
+
const gL = new Uint8ClampedArray(256);
|
|
30
|
+
const bL = new Uint8ClampedArray(256);
|
|
31
|
+
for (let raw = 0; raw < 256; raw++) {
|
|
32
|
+
let t = (raw - wMin) / wSpan;
|
|
33
|
+
t = t < 0 ? 0 : t > 1 ? 1 : t;
|
|
34
|
+
if (invGamma !== 1)
|
|
35
|
+
t = Math.pow(t, invGamma);
|
|
36
|
+
let v = Math.round(t * 255);
|
|
37
|
+
if (invertBg)
|
|
38
|
+
v = 255 - v;
|
|
39
|
+
const c = lut[v];
|
|
40
|
+
rL[raw] = c[0];
|
|
41
|
+
gL[raw] = c[1];
|
|
42
|
+
bL[raw] = c[2];
|
|
43
|
+
}
|
|
44
|
+
for (let i = 0; i < d.length; i += 4) {
|
|
45
|
+
if (d[i + 3] === 0)
|
|
46
|
+
continue;
|
|
47
|
+
// maxRgb (contracts/intensity) inlined on purpose: this loop runs
|
|
48
|
+
// ~262k times per tile on every recolor.
|
|
49
|
+
const raw = d[i] >= d[i + 1] ? (d[i] >= d[i + 2] ? d[i] : d[i + 2]) : d[i + 1] >= d[i + 2] ? d[i + 1] : d[i + 2];
|
|
50
|
+
d[i] = rL[raw];
|
|
51
|
+
d[i + 1] = gL[raw];
|
|
52
|
+
d[i + 2] = bL[raw];
|
|
53
|
+
changed = true;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
else {
|
|
57
|
+
const chans = [channelStates[0], channelStates[1], channelStates[2]];
|
|
58
|
+
const tints = chans.map((c) => this.tint01(c));
|
|
59
|
+
for (let i = 0; i < d.length; i += 4) {
|
|
60
|
+
if (d[i + 3] === 0)
|
|
61
|
+
continue;
|
|
62
|
+
let oR = 0, oG = 0, oB = 0;
|
|
63
|
+
for (let k = 0; k < 3; k++) {
|
|
64
|
+
const c = chans[k];
|
|
65
|
+
if (c && !c.visible)
|
|
66
|
+
continue;
|
|
67
|
+
const v = this.channelIntensity(d[i + k], c);
|
|
68
|
+
const tint = tints[k];
|
|
69
|
+
oR += v * tint[0];
|
|
70
|
+
oG += v * tint[1];
|
|
71
|
+
oB += v * tint[2];
|
|
72
|
+
}
|
|
73
|
+
if (oR > 255)
|
|
74
|
+
oR = 255;
|
|
75
|
+
if (oG > 255)
|
|
76
|
+
oG = 255;
|
|
77
|
+
if (oB > 255)
|
|
78
|
+
oB = 255;
|
|
79
|
+
if (invertBg) {
|
|
80
|
+
oR = 255 - oR;
|
|
81
|
+
oG = 255 - oG;
|
|
82
|
+
oB = 255 - oB;
|
|
83
|
+
}
|
|
84
|
+
d[i] = oR;
|
|
85
|
+
d[i + 1] = oG;
|
|
86
|
+
d[i + 2] = oB;
|
|
87
|
+
changed = true;
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
return changed;
|
|
91
|
+
}
|
|
92
|
+
/** Precomputed lum(0..255) → tinted-RGB lookup for a channel's window/gamma/
|
|
93
|
+
* colour. Building it costs 256 channelIntensity() calls; using it makes a
|
|
94
|
+
* full-tile recolor ~262k array lookups instead of ~262k Math.pow() calls. */
|
|
95
|
+
channelRgbLut(st) {
|
|
96
|
+
const r = new Uint8ClampedArray(256);
|
|
97
|
+
const g = new Uint8ClampedArray(256);
|
|
98
|
+
const b = new Uint8ClampedArray(256);
|
|
99
|
+
const [tr, tg, tb] = this.tint01(st);
|
|
100
|
+
for (let lum = 0; lum < 256; lum++) {
|
|
101
|
+
const v = this.channelIntensity(lum, st);
|
|
102
|
+
r[lum] = v * tr;
|
|
103
|
+
g[lum] = v * tg;
|
|
104
|
+
b[lum] = v * tb;
|
|
105
|
+
}
|
|
106
|
+
return { r, g, b };
|
|
107
|
+
}
|
|
108
|
+
/** Windowed + gamma intensity (0..255) for a channel, ignoring tint/invert. */
|
|
109
|
+
channelIntensity(val, c) {
|
|
110
|
+
if (!c)
|
|
111
|
+
return val;
|
|
112
|
+
const span = c.max > c.min ? c.max - c.min : 0;
|
|
113
|
+
if (!span)
|
|
114
|
+
return 0;
|
|
115
|
+
let t = (val - c.min) / span;
|
|
116
|
+
t = t < 0 ? 0 : t > 1 ? 1 : t;
|
|
117
|
+
if (c.gamma > 0 && c.gamma !== 1)
|
|
118
|
+
t = Math.pow(t, 1 / c.gamma);
|
|
119
|
+
return t * 255;
|
|
120
|
+
}
|
|
121
|
+
/** A channel's pseudo-colour tint as [r,g,b] in 0..1 (default white). */
|
|
122
|
+
tint01(c) {
|
|
123
|
+
const m = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(c?.color ?? '');
|
|
124
|
+
return m ? [parseInt(m[1], 16) / 255, parseInt(m[2], 16) / 255, parseInt(m[3], 16) / 255] : [1, 1, 1];
|
|
125
|
+
}
|
|
126
|
+
/** True when any RGB channel is windowed/hidden/gamma'd/re-tinted or the
|
|
127
|
+
* background is inverted — otherwise the tile passes through unchanged. */
|
|
128
|
+
rgbNeedsRecolor() {
|
|
129
|
+
if (this.host.invertBg())
|
|
130
|
+
return true;
|
|
131
|
+
const defaults = ['#ff0000', '#00ff00', '#0000ff'];
|
|
132
|
+
const channelStates = this.host.channelStates();
|
|
133
|
+
for (let k = 0; k < 3; k++) {
|
|
134
|
+
const c = channelStates[k];
|
|
135
|
+
if (c && (!c.visible || c.min !== 0 || c.max !== 255 || c.gamma !== 1 ||
|
|
136
|
+
(c.color || '').toLowerCase() !== defaults[k])) {
|
|
137
|
+
return true;
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
return false;
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZGlzcGxheS1waXBlbGluZS5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uLy4uLy4uL3NyYy9saWIvaW1wbGVtZW50YXRpb25zL29zZC9kaXNwbGF5LXBpcGVsaW5lLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQXFCQSxNQUFNLE9BQU8sZUFBZTtJQUMxQixZQUFvQixJQUF5QjtRQUF6QixTQUFJLEdBQUosSUFBSSxDQUFxQjtJQUFHLENBQUM7SUFFakQ7Ozs7Ozs7T0FPRztJQUNILFdBQVcsQ0FBQyxDQUFvQjtRQUM5QixJQUFJLE9BQU8sR0FBRyxLQUFLLENBQUM7UUFDcEIsTUFBTSxhQUFhLEdBQUcsSUFBSSxDQUFDLElBQUksQ0FBQyxhQUFhLEVBQUUsQ0FBQztRQUNoRCxNQUFNLFFBQVEsR0FBRyxJQUFJLENBQUMsSUFBSSxDQUFDLFFBQVEsRUFBRSxDQUFDO1FBQ3RDLElBQUksSUFBSSxDQUFDLElBQUksQ0FBQyxXQUFXLEVBQUUsRUFBRSxDQUFDO1lBQzVCLE1BQU0sR0FBRyxHQUFHLElBQUksQ0FBQyxJQUFJLENBQUMsUUFBUSxFQUFFLENBQUM7WUFDakMsSUFBSSxDQUFDLEdBQUc7Z0JBQUUsT0FBTyxLQUFLLENBQUM7WUFDdkIsTUFBTSxFQUFFLEdBQUcsYUFBYSxDQUFDLENBQUMsQ0FBQyxDQUFDO1lBQzVCLE1BQU0sSUFBSSxHQUFHLEVBQUUsQ0FBQyxDQUFDLENBQUMsRUFBRSxDQUFDLEdBQUcsQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDO1lBQzdCLE1BQU0sS0FBSyxHQUFHLEVBQUUsSUFBSSxFQUFFLENBQUMsR0FBRyxHQUFHLEVBQUUsQ0FBQyxHQUFHLENBQUMsQ0FBQyxDQUFDLEVBQUUsQ0FBQyxHQUFHLEdBQUcsRUFBRSxDQUFDLEdBQUcsQ0FBQyxDQUFDLENBQUMsR0FBRyxDQUFDO1lBQzVELE1BQU0sUUFBUSxHQUFHLEVBQUUsSUFBSSxFQUFFLENBQUMsS0FBSyxHQUFHLENBQUMsQ0FBQyxDQUFDLENB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QUMsQ0FBQyxLQUFLLElBQUksRUFBRSxDQUFDLENBQUMsV0FBVyxFQUFFLEtBQUssUUFBUSxDQUFDLENBQUMsQ0FBQyxDQUFDLEVBQ2hELENBQUM7Z0JBQ0QsT0FBTyxJQUFJLENBQUM7WUFDZCxDQUFDO1FBQ0gsQ0FBQztRQUNELE9BQU8sS0FBSyxDQUFDO0lBQ2YsQ0FBQztDQUNGIiwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IHsgSUNoYW5uZWxTdGF0ZSB9IGZyb20gJy4uLy4uL2NvbnRyYWN0cy9jaGFubmVsLWhpc3RvZ3JhbS1hcGkuY29udHJhY3QnO1xuaW1wb3J0IHsgUmdiIH0gZnJvbSAnLi4vLi4vY29udHJhY3RzL2NvbG9ybWFwLWx1dCc7XG5cbi8qKlxuICogVGhlIE9TRCBwaXhlbCBkaXNwbGF5IHBpcGVsaW5lIChyZWZhY3RvcmluZyBwbGFuLCBTdGVwIDQg4oCUIGEgcHVyZSBtb3ZlIG9mXG4gKiB0aGUgcmVjb2xvciBtYXRoIG91dCBvZiB0aGUgdmlzdWFsaXplciBzZXJ2aWNlKS4gU3RhdGVsZXNzOiBldmVyeSBjYWxsIHJlYWRzXG4gKiB0aGUgY3VycmVudCBkaXNwbGF5IHN0YXRlIHRocm91Z2ggdGhlIGhvc3QgY2xvc3VyZXMsIGV4YWN0bHkgbGlrZSB0aGUgbW92ZWRcbiAqIGNvZGUgcmVhZCB0aGUgc2VydmljZSdzIGZpZWxkcy4gU2hhcmVkIGJ5IHRpbGUgcmVjb2xvcmluZyBhbmQgdGhlIGNvbXBvc2l0ZVxuICogZXhwb3J0IHNvIHRoZXkgc3RheSBpZGVudGljYWwuXG4gKi9cbmV4cG9ydCBpbnRlcmZhY2UgRGlzcGxheVBpcGVsaW5lSG9zdCB7XG4gIC8qKiBHcmF5c2NhbGUgaW1hZ2UgKGNvbG9ybWFwIExVVCBwYXRoKSB2cyBSR0IvbXVsdGljaGFubmVsIChhZGRpdGl2ZSB0aW50KS4gKi9cbiAgaXNHcmF5c2NhbGUoKTogYm9vbGVhbjtcbiAgLyoqIDI1Ni1lbnRyeSBjb2xvcm1hcCBMVVQgKGdyYXlzY2FsZSBwYXRoKTsgbnVsbCB3aGlsZSBvcHRpb25zIHJlc29sdmUuICovXG4gIGNvbG9yTHV0KCk6IFJnYltdIHwgbnVsbDtcbiAgLyoqIExhdGVzdCBwZXItY2hhbm5lbCBkaXNwbGF5IHN0YXRlIGZyb20gdGhlIHN0b3JlLiAqL1xuICBjaGFubmVsU3RhdGVzKCk6IElDaGFubmVsU3RhdGVbXTtcbiAgLyoqIEludmVydGVkIGJhY2tncm91bmQgKHdoaXRlID0gemVybykuICovXG4gIGludmVydEJnKCk6IGJvb2xlYW47XG59XG5cbmV4cG9ydCBjbGFzcyBEaXNwbGF5UGlwZWxpbmUge1xuICBjb25zdHJ1Y3Rvcihwcml2YXRlIGhvc3Q6IERpc3BsYXlQaXBlbGluZUhvc3QpIHt9XG5cbiAgLyoqXG4gICAqIEFwcGx5IHRoZSBjdXJyZW50IGRpc3BsYXkgcGlwZWxpbmUgdG8gYW4gUkdCQSBidWZmZXIgaW4gcGxhY2U7IHJldHVybnNcbiAgICogd2hldGhlciBhbnkgb3BhcXVlIHBpeGVsIHdhcyB3cml0dGVuLlxuICAgKiAgLSBHcmF5c2NhbGU6IGludGVuc2l0eSDihpIgd2luZG93ICsgZ2FtbWEgKyBpbnZlcnQg4oaSIGNvbG9ybWFwIExVVC5cbiAgICogIC0gUkdCL211bHRpY2hhbm5lbDogYWRkaXRpdmUgcHNldWRvLWNvbG91ciBtZXJnZSDigJQgZWFjaCB2aXNpYmxlIGNoYW5uZWwnc1xuICAgKiAgICB3aW5kb3dlZCBpbnRlbnNpdHkgaXMgdGludGVkIGJ5IGl0cyBhc3NpZ25lZCBjb2xvdXIgYW5kIHN1bW1lZCAoRmlqaVxuICAgKiAgICBcIk1lcmdlIENoYW5uZWxzXCIpLiBEZWZhdWx0cyAoUj1yZWQsIEc9Z3JlZW4sIEI9Ymx1ZSkgYXJlIHRoZSBpZGVudGl0eS5cbiAgICovXG4gIGFwcGx5VG9SZ2JhKGQ6IFVpbnQ4Q2xhbXBlZEFycmF5KTogYm9vbGVhbiB7XG4gICAgbGV0IGNoYW5nZWQgPSBmYWxzZTtcbiAgICBjb25zdCBjaGFubmVsU3RhdGVzID0gdGhpcy5ob3N0LmNoYW5uZWxTdGF0ZXMoKTtcbiAgICBjb25zdCBpbnZlcnRCZyA9IHRoaXMuaG9zdC5pbnZlcnRCZygpO1xuICAgIGlmICh0aGlzLmhvc3QuaXNHcmF5c2NhbGUoKSkge1xuICAgICAgY29uc3QgbHV0ID0gdGhpcy5ob3N0LmNvbG9yTHV0KCk7XG4gICAgICBpZiAoIWx1dCkgcmV0dXJuIGZhbHNlO1xuICAgICAgY29uc3QgY2ggPSBjaGFubmVsU3RhdGVzWzBdO1xuICAgICAgY29uc3Qgd01pbiA9IGNoID8gY2gubWluIDogMDtcbiAgICAgIGNvbnN0IHdTcGFuID0gY2ggJiYgY2gubWF4ID4gY2gubWluID8gY2gubWF4IC0gY2gubWluIDogMjU1O1xuICAgICAgY29uc3QgaW52R2FtbWEgPSBjaCAmJiBjaC5nYW1tYSA+IDAgPyAxIC8gY2guZ2FtbWEgOiAxO1xuICAgICAgLy8gUHJlY29tcHV0ZSByYXcoMC4uMjU1KSAtPiBmaW5hbCBSR0Igb25jZSAoMjU2IHdpbmRvdytnYW1tYStpbnZlcnQrY29sb3JtYXBcbiAgICAgIC8vIGV2YWx1YXRpb25zKSBhbmQgbWFwIGVhY2ggcGl4ZWwgYnkgdGFibGUgbG9va3VwIOKAlCBhIE1hdGgucG93IHBlciBwaXhlbFxuICAgICAgLy8gKH4yNjJrL3RpbGUpIG1hZGUgdGhlIHdpbmRvdy9nYW1tYSBzbGlkZXJzIGNyYXdsIG9uIGxhcmdlIHN0YWNrcy5cbiAgICAgIGNvbnN0IHJMID0gbmV3IFVpbnQ4Q2xhbXBlZEFycmF5KDI1Nik7XG4gICAgICBjb25zdCBnTCA9IG5ldyBVaW50OENsYW1wZWRBcnJheSgyNTYpO1xuICAgICAgY29uc3QgYkwgPSBuZXcgVWludDhDbGFtcGVkQXJyYXkoMjU2KTtcbiAgICAgIGZvciAobGV0IHJhdyA9IDA7IHJhdyA8IDI1NjsgcmF3KyspIHtcbiAgICAgICAgbGV0IHQgPSAocmF3IC0gd01pbikgLyB3U3BhbjtcbiAgICAgICAgdCA9IHQgPCAwID8gMCA6IHQgPiAxID8gMSA6IHQ7XG4gICAgICAgIGlmIChpbnZHYW1tYSAhPT0gMSkgdCA9IE1hdGgucG93KHQsIGludkdhbW1hKTtcbiAgICAgICAgbGV0IHYgPSBNYXRoLnJvdW5kKHQgKiAyNTUpO1xuICAgICAgICBpZiAoaW52ZXJ0QmcpIHYgPSAyNTUgLSB2O1xuICAgICAgICBjb25zdCBjID0gbHV0W3ZdO1xuICAgICAgICByTFtyYXddID0gY1swXTtcbiAgICAgICAgZ0xbcmF3XSA9IGNbMV07XG4gICAgICAgIGJMW3Jhd10gPSBjWzJdO1xuICAgICAgfVxuICAgICAgZm9yIChsZXQgaSA9IDA7IGkgPCBkLmxlbmd0aDsgaSArPSA0KSB7XG4gICAgICAgIGlmIChkW2kgKyAzXSA9PT0gMCkgY29udGludWU7XG4gICAgICAgIC8vIG1heFJnYiAoY29udHJhY3RzL2ludGVuc2l0eSkgaW5saW5lZCBvbiBwdXJwb3NlOiB0aGlzIGxvb3AgcnVuc1xuICAgICAgICAvLyB+MjYyayB0aW1lcyBwZXIgdGlsZSBvbiBldmVyeSByZWNvbG9yLlxuICAgICAgICBjb25zdCByYXcgPVxuICAgICAgICAgIGRbaV0gPj0gZFtpICsgMV0gPyAoZFtpXSA+PSBkW2kgKyAyXSA/IGRbaV0gOiBkW2kgKyAyXSkgOiBkW2kgKyAxXSA+PSBkW2kgKyAyXSA/IGRbaSArIDFdIDogZFtpICsgMl07XG4gICAgICAgIGRbaV0gPSByTFtyYXddO1xuICAgICAgICBkW2kgKyAxXSA9IGdMW3Jhd107XG4gICAgICAgIGRbaSArIDJdID0gYkxbcmF3XTtcbiAgICAgICAgY2hhbmdlZCA9IHRydWU7XG4gICAgICB9XG4gICAgfSBlbHNlIHtcbiAgICAgIGNvbnN0IGNoYW5zID0gW2NoYW5uZWxTdGF0ZXNbMF0sIGNoYW5uZWxTdGF0ZXNbMV0sIGNoYW5uZWxTdGF0ZXNbMl1dO1xuICAgICAgY29uc3QgdGludHMgPSBjaGFucy5tYXAoKGMpID0+IHRoaXMudGludDAxKGMpKTtcbiAgICAgIGZvciAobGV0IGkgPSAwOyBpIDwgZC5sZW5ndGg7IGkgKz0gNCkge1xuICAgICAgICBpZiAoZFtpICsgM10gPT09IDApIGNvbnRpbnVlO1xuICAgICAgICBsZXQgb1IgPSAwLCBvRyA9IDAsIG9CID0gMDtcbiAgICAgICAgZm9yIChsZXQgayA9IDA7IGsgPCAzOyBrKyspIHtcbiAgICAgICAgICBjb25zdCBjID0gY2hhbnNba107XG4gICAgICAgICAgaWYgKGMgJiYgIWMudmlzaWJsZSkgY29udGludWU7XG4gICAgICAgICAgY29uc3QgdiA9IHRoaXMuY2hhbm5lbEludGVuc2l0eShkW2kgKyBrXSwgYyk7XG4gICAgICAgICAgY29uc3QgdGludCA9IHRpbnRzW2tdO1xuICAgICAgICAgIG9SICs9IHYgKiB0aW50WzBdO1xuICAgICAgICAgIG9HICs9IHYgKiB0aW50WzFdO1xuICAgICAgICAgIG9CICs9IHYgKiB0aW50WzJdO1xuICAgICAgICB9XG4gICAgICAgIGlmIChvUiA+IDI1NSkgb1IgPSAyNTU7XG4gICAgICAgIGlmIChvRyA+IDI1NSkgb0cgPSAyNTU7XG4gICAgICAgIGlmIChvQiA+IDI1NSkgb0IgPSAyNTU7XG4gICAgICAgIGlmIChpbnZlcnRCZykgeyBvUiA9IDI1NSAtIG9SOyBvRyA9IDI1NSAtIG9HOyBvQiA9IDI1NSAtIG9COyB9XG4gICAgICAgIGRbaV0gPSBvUjtcbiAgICAgICAgZFtpICsgMV0gPSBvRztcbiAgICAgICAgZFtpICsgMl0gPSBvQjtcbiAgICAgICAgY2hhbmdlZCA9IHRydWU7XG4gICAgICB9XG4gICAgfVxuICAgIHJldHVybiBjaGFuZ2VkO1xuICB9XG5cbiAgLyoqIFByZWNvbXB1dGVkIGx1bSgwLi4yNTUpIOKGkiB0aW50ZWQtUkdCIGxvb2t1cCBmb3IgYSBjaGFubmVsJ3Mgd2luZG93L2dhbW1hL1xuICAgKiAgY29sb3VyLiBCdWlsZGluZyBpdCBjb3N0cyAyNTYgY2hhbm5lbEludGVuc2l0eSgpIGNhbGxzOyB1c2luZyBpdCBtYWtlcyBhXG4gICAqICBmdWxsLXRpbGUgcmVjb2xvciB+MjYyayBhcnJheSBsb29rdXBzIGluc3RlYWQgb2YgfjI2MmsgTWF0aC5wb3coKSBjYWxscy4gKi9cbiAgY2hhbm5lbFJnYkx1dChzdD86IElDaGFubmVsU3RhdGUpOiB7IHI6IFVpbnQ4Q2xhbXBlZEFycmF5OyBnOiBVaW50OENsYW1wZWRBcnJheTsgYjogVWludDhDbGFtcGVkQXJyYXkgfSB7XG4gICAgY29uc3QgciA9IG5ldyBVaW50OENsYW1wZWRBcnJheSgyNTYpO1xuICAgIGNvbnN0IGcgPSBuZXcgVWludDhDbGFtcGVkQXJyYXkoMjU2KTtcbiAgICBjb25zdCBiID0gbmV3IFVpbnQ4Q2xhbXBlZEFycmF5KDI1Nik7XG4gICAgY29uc3QgW3RyLCB0ZywgdGJdID0gdGhpcy50aW50MDEoc3QpO1xuICAgIGZvciAobGV0IGx1bSA9IDA7IGx1bSA8IDI1NjsgbHVtKyspIHtcbiAgICAgIGNvbnN0IHYgPSB0aGlzLmNoYW5uZWxJbnRlbnNpdHkobHVtLCBzdCk7XG4gICAgICByW2x1bV0gPSB2ICogdHI7XG4gICAgICBnW2x1bV0gPSB2ICogdGc7XG4gICAgICBiW2x1bV0gPSB2ICogdGI7XG4gICAgfVxuICAgIHJldHVybiB7IHIsIGcsIGIgfTtcbiAgfVxuXG4gIC8qKiBXaW5kb3dlZCArIGdhbW1hIGludGVuc2l0eSAoMC4uMjU1KSBmb3IgYSBjaGFubmVsLCBpZ25vcmluZyB0aW50L2ludmVydC4gKi9cbiAgY2hhbm5lbEludGVuc2l0eSh2YWw6IG51bWJlciwgYz86IElDaGFubmVsU3RhdGUpOiBudW1iZXIge1xuICAgIGlmICghYykgcmV0dXJuIHZhbDtcbiAgICBjb25zdCBzcGFuID0gYy5tYXggPiBjLm1pbiA/IGMubWF4IC0gYy5taW4gOiAwO1xuICAgIGlmICghc3BhbikgcmV0dXJuIDA7XG4gICAgbGV0IHQgPSAodmFsIC0gYy5taW4pIC8gc3BhbjtcbiAgICB0ID0gdCA8IDAgPyAwIDogdCA+IDEgPyAxIDogdDtcbiAgICBpZiAoYy5nYW1tYSA+IDAgJiYgYy5nYW1tYSAhPT0gMSkgdCA9IE1hdGgucG93KHQsIDEgLyBjLmdhbW1hKTtcbiAgICByZXR1cm4gdCAqIDI1NTtcbiAgfVxuXG4gIC8qKiBBIGNoYW5uZWwncyBwc2V1ZG8tY29sb3VyIHRpbnQgYXMgW3IsZyxiXSBpbiAwLi4xIChkZWZhdWx0IHdoaXRlKS4gKi9cbiAgdGludDAxKGM/OiBJQ2hhbm5lbFN0YXRlKTogW251bWJlciwgbnVtYmVyLCBudW1iZXJdIHtcbiAgICBjb25zdCBtID0gL14jPyhbYS1mXFxkXXsyfSkoW2EtZlxcZF17Mn0pKFthLWZcXGRdezJ9KSQvaS5leGVjKGM/LmNvbG9yID8/ICcnKTtcbiAgICByZXR1cm4gbSA/IFtwYXJzZUludChtWzFdLCAxNikgLyAyNTUsIHBhcnNlSW50KG1bMl0sIDE2KSAvIDI1NSwgcGFyc2VJbnQobVszXSwgMTYpIC8gMjU1XSA6IFsxLCAxLCAxXTtcbiAgfVxuXG4gIC8qKiBUcnVlIHdoZW4gYW55IFJHQiBjaGFubmVsIGlzIHdpbmRvd2VkL2hpZGRlbi9nYW1tYSdkL3JlLXRpbnRlZCBvciB0aGVcbiAgICogIGJhY2tncm91bmQgaXMgaW52ZXJ0ZWQg4oCUIG90aGVyd2lzZSB0aGUgdGlsZSBwYXNzZXMgdGhyb3VnaCB1bmNoYW5nZWQuICovXG4gIHJnYk5lZWRzUmVjb2xvcigpOiBib29sZWFuIHtcbiAgICBpZiAodGhpcy5ob3N0LmludmVydEJnKCkpIHJldHVybiB0cnVlO1xuICAgIGNvbnN0IGRlZmF1bHRzID0gWycjZmYwMDAwJywgJyMwMGZmMDAnLCAnIzAwMDBmZiddO1xuICAgIGNvbnN0IGNoYW5uZWxTdGF0ZXMgPSB0aGlzLmhvc3QuY2hhbm5lbFN0YXRlcygpO1xuICAgIGZvciAobGV0IGsgPSAwOyBrIDwgMzsgaysrKSB7XG4gICAgICBjb25zdCBjID0gY2hhbm5lbFN0YXRlc1trXTtcbiAgICAgIGlmIChcbiAgICAgICAgYyAmJiAoIWMudmlzaWJsZSB8fCBjLm1pbiAhPT0gMCB8fCBjLm1heCAhPT0gMjU1IHx8IGMuZ2FtbWEgIT09IDEgfHxcbiAgICAgICAgICAoYy5jb2xvciB8fCAnJykudG9Mb3dlckNhc2UoKSAhPT0gZGVmYXVsdHNba10pXG4gICAgICApIHtcbiAgICAgICAgcmV0dXJuIHRydWU7XG4gICAgICB9XG4gICAgfVxuICAgIHJldHVybiBmYWxzZTtcbiAgfVxufVxuIl19
|
|
@@ -0,0 +1,217 @@
|
|
|
1
|
+
import { firstValueFrom, from, of } from 'rxjs';
|
|
2
|
+
import { timeout } from 'rxjs/operators';
|
|
3
|
+
import { buildTileUrl, fetchTileRgba } from './tile-client';
|
|
4
|
+
import { histogram256, maxRgb } from '../../contracts/intensity';
|
|
5
|
+
export class HistogramSampler {
|
|
6
|
+
constructor(http, api, host) {
|
|
7
|
+
this.http = http;
|
|
8
|
+
this.api = api;
|
|
9
|
+
this.host = host;
|
|
10
|
+
/** 256-bin intensity histogram per z-slice, from sampled tiles. */
|
|
11
|
+
this.sliceHistograms = new Map();
|
|
12
|
+
/** Native-bit-depth histograms from `/histogram`, keyed `${z}|${channel}`. */
|
|
13
|
+
this.nativeHistograms = new Map();
|
|
14
|
+
/** Per-app-load cache-buster for `/histogram` — the server marks the
|
|
15
|
+
* response cacheable for 24h, and a hard refresh can't bust a post-load
|
|
16
|
+
* XHR. A token stable within a session but new on each full load keeps the
|
|
17
|
+
* in-session dedup (via `nativeHistograms`) while always reflecting the
|
|
18
|
+
* live backend after a reload. */
|
|
19
|
+
this.histCacheBuster = Date.now();
|
|
20
|
+
}
|
|
21
|
+
/** The sampled histogram for one channel of slice z, or null until the
|
|
22
|
+
* async sampling resolves (or if it was skipped). */
|
|
23
|
+
get(z, channelIndex) {
|
|
24
|
+
return this.sliceHistograms.get(z)?.[channelIndex] ?? null;
|
|
25
|
+
}
|
|
26
|
+
/** Drop everything (teardown / image switch). */
|
|
27
|
+
clear() {
|
|
28
|
+
this.sliceHistograms.clear();
|
|
29
|
+
this.nativeHistograms.clear();
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Per-channel intensity histograms for multichannel images: sample each
|
|
33
|
+
* channel's single-band tiles at the coarsest real level and bin the
|
|
34
|
+
* luminance. Fire-and-forget.
|
|
35
|
+
*/
|
|
36
|
+
async computeMultiChannelHistograms(d, infoB64, z) {
|
|
37
|
+
try {
|
|
38
|
+
const t = d.tileSize;
|
|
39
|
+
// Sample the COARSEST real level (per-channel tiles exist only at real
|
|
40
|
+
// resolutions; the coarsest is small enough to bin even for whole-slides).
|
|
41
|
+
const res = Math.max(0, this.host.realLevels() - 1);
|
|
42
|
+
const lvl = d.levels?.[res];
|
|
43
|
+
const lw = lvl?.width ?? d.width;
|
|
44
|
+
const lh = lvl?.height ?? d.height;
|
|
45
|
+
const cols = Math.max(1, Math.ceil(lw / t));
|
|
46
|
+
const rows = Math.max(1, Math.ceil(lh / t));
|
|
47
|
+
if (cols * rows > 64)
|
|
48
|
+
return; // even the coarsest real level is too big
|
|
49
|
+
const nCh = this.host.channelCount(d);
|
|
50
|
+
if (nCh < 1)
|
|
51
|
+
return;
|
|
52
|
+
const counts = Array.from({ length: nCh }, () => new Array(256).fill(0));
|
|
53
|
+
const jobs = [];
|
|
54
|
+
for (let row = 0; row < rows; row++) {
|
|
55
|
+
for (let col = 0; col < cols; col++) {
|
|
56
|
+
for (let c = 0; c < nCh; c++) {
|
|
57
|
+
const url = buildTileUrl(this.api, infoB64, { res, col, row, z, tileSize: t, channel: c });
|
|
58
|
+
jobs.push((async () => {
|
|
59
|
+
try {
|
|
60
|
+
const img = await fetchTileRgba(this.http, url, 20000);
|
|
61
|
+
if (!img)
|
|
62
|
+
return;
|
|
63
|
+
const data = img.data;
|
|
64
|
+
const cc = counts[c];
|
|
65
|
+
for (let i = 0; i < data.length; i += 4) {
|
|
66
|
+
if (data[i + 3] === 0)
|
|
67
|
+
continue;
|
|
68
|
+
cc[maxRgb(data[i], data[i + 1], data[i + 2])]++;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
catch (err) {
|
|
72
|
+
// Skip this tile — the histogram is just missing its counts.
|
|
73
|
+
console.warn('[viz:histogram] channel tile sample failed, skipping', url, err);
|
|
74
|
+
}
|
|
75
|
+
})());
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
await Promise.all(jobs);
|
|
80
|
+
this.sliceHistograms.set(z, counts.map((c) => histogram256(c)));
|
|
81
|
+
this.host.onChannelHistogramsSampled();
|
|
82
|
+
}
|
|
83
|
+
catch (err) {
|
|
84
|
+
// Leave histograms unset (the pane shows empty) — but say why.
|
|
85
|
+
console.warn('[viz:histogram] multichannel histogram sampling failed', err);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* Compute a per-image intensity window [min,max] for grayscale auto-ranging,
|
|
90
|
+
* mirroring the heatmap's auto-stretch. Two constraints force the sampling
|
|
91
|
+
* strategy:
|
|
92
|
+
* - The window MUST be in the same value space as the displayed tiles: the
|
|
93
|
+
* `/preview` endpoint is server-normalized while `/tile` serves raw
|
|
94
|
+
* un-normalized 8-bit — a window from the preview wouldn't match.
|
|
95
|
+
* - It must read FULL-RESOLUTION tiles, not a downsampled overview:
|
|
96
|
+
* averaging sparse low values (label masks) toward 0 wipes the signal.
|
|
97
|
+
* So we sample the full-res level across the whole image — bounded to 64
|
|
98
|
+
* tiles (a huge grayscale image samples the coarsest overview for histograms
|
|
99
|
+
* only). Fire-and-forget; failures leave the window unset (identity 0..255).
|
|
100
|
+
*/
|
|
101
|
+
async computeImageWindow(d, infoB64, z) {
|
|
102
|
+
const gray = this.host.isGrayscale();
|
|
103
|
+
try {
|
|
104
|
+
const t = d.tileSize;
|
|
105
|
+
// Sample full-resolution when the grid is small (accurate for the grayscale
|
|
106
|
+
// auto-window); otherwise sample the coarsest overview just for histograms
|
|
107
|
+
// (whole-slide images). RGB only needs the histograms, not an auto-window.
|
|
108
|
+
let res = 0;
|
|
109
|
+
let cols = Math.max(1, Math.ceil(d.width / t));
|
|
110
|
+
let rows = Math.max(1, Math.ceil(d.height / t));
|
|
111
|
+
let fullRes = true;
|
|
112
|
+
if (cols * rows > 64) {
|
|
113
|
+
res = Math.max(0, (d.levels?.length ?? 1) - 1);
|
|
114
|
+
const lvl = d.levels?.[res];
|
|
115
|
+
cols = Math.max(1, Math.ceil((lvl?.width ?? d.width) / t));
|
|
116
|
+
rows = Math.max(1, Math.ceil((lvl?.height ?? d.height) / t));
|
|
117
|
+
fullRes = false;
|
|
118
|
+
if (cols * rows > 36)
|
|
119
|
+
return; // even the coarsest is too big — give up
|
|
120
|
+
}
|
|
121
|
+
const coords = [];
|
|
122
|
+
for (let row = 0; row < rows; row++) {
|
|
123
|
+
for (let col = 0; col < cols; col++)
|
|
124
|
+
coords.push([col, row]);
|
|
125
|
+
}
|
|
126
|
+
let min = 255;
|
|
127
|
+
let max = 0;
|
|
128
|
+
const cR = new Array(256).fill(0);
|
|
129
|
+
const cG = gray ? null : new Array(256).fill(0);
|
|
130
|
+
const cB = gray ? null : new Array(256).fill(0);
|
|
131
|
+
await Promise.all(coords.map(async ([col, row]) => {
|
|
132
|
+
const url = buildTileUrl(this.api, infoB64, { res, col, row, z, tileSize: t });
|
|
133
|
+
try {
|
|
134
|
+
const img = await fetchTileRgba(this.http, url, 20000);
|
|
135
|
+
if (!img)
|
|
136
|
+
return;
|
|
137
|
+
const data = img.data;
|
|
138
|
+
for (let i = 0; i < data.length; i += 4) {
|
|
139
|
+
if (data[i + 3] === 0)
|
|
140
|
+
continue; // skip transparent padding
|
|
141
|
+
if (gray) {
|
|
142
|
+
const v = maxRgb(data[i], data[i + 1], data[i + 2]);
|
|
143
|
+
if (v < min)
|
|
144
|
+
min = v;
|
|
145
|
+
if (v > max)
|
|
146
|
+
max = v;
|
|
147
|
+
cR[v]++;
|
|
148
|
+
}
|
|
149
|
+
else {
|
|
150
|
+
cR[data[i]]++;
|
|
151
|
+
cG[data[i + 1]]++;
|
|
152
|
+
cB[data[i + 2]]++;
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
catch (err) {
|
|
157
|
+
// Skip this tile — window/histogram just lose its samples.
|
|
158
|
+
console.warn('[viz:window] tile sample failed, skipping', url, err);
|
|
159
|
+
}
|
|
160
|
+
}));
|
|
161
|
+
// Cache per-channel histograms: grayscale → [intensity]; RGB → [R, G, B].
|
|
162
|
+
this.sliceHistograms.set(z, gray ? [histogram256(cR)] : [histogram256(cR), histogram256(cG), histogram256(cB)]);
|
|
163
|
+
// Grayscale auto-window — only from full-res samples (coarsest averaging
|
|
164
|
+
// is inaccurate); the host seeds the channel or re-invalidates.
|
|
165
|
+
if (gray && fullRes && max > min && (min > 0 || max < 255)) {
|
|
166
|
+
this.host.onGrayWindowSampled(min, max);
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
catch (err) {
|
|
170
|
+
console.warn('[viz:window] per-image window compute failed — using identity 0..255', err);
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
/**
|
|
174
|
+
* Native-bit-depth histogram for >8-bit images, from the server `/histogram`
|
|
175
|
+
* endpoint (the 8-bit canvas tiles can't carry 16-bit values). Cached per
|
|
176
|
+
* slice+channel for the session.
|
|
177
|
+
*/
|
|
178
|
+
native$(infoB64, z, channel, bins) {
|
|
179
|
+
const key = `${z}|${channel}`;
|
|
180
|
+
const cached = this.nativeHistograms.get(key);
|
|
181
|
+
if (cached)
|
|
182
|
+
return of(cached);
|
|
183
|
+
return from(this.fetchNative(infoB64, z, channel, bins, key));
|
|
184
|
+
}
|
|
185
|
+
/** Fetch + cache one channel's native histogram from `GET /histogram`. */
|
|
186
|
+
async fetchNative(infoB64, z, channel, bins, key) {
|
|
187
|
+
const url = `${this.api}histogram?info=${infoB64}&channel=${channel}&z=${z}&bins=${bins}&_=${this.histCacheBuster}`;
|
|
188
|
+
try {
|
|
189
|
+
// HttpClient calls get auth via the Angular interceptor (unlike OSD's own
|
|
190
|
+
// ajax tile loader, which needs authHeaders) — mirror the other fetches.
|
|
191
|
+
const hi = await firstValueFrom(this.http
|
|
192
|
+
.get(url)
|
|
193
|
+
.pipe(timeout(45000)));
|
|
194
|
+
if (!hi || !hi.counts)
|
|
195
|
+
return null;
|
|
196
|
+
const out = {
|
|
197
|
+
// Native bin left-edges: rangeMin + i*binWidth.
|
|
198
|
+
bins: hi.counts.map((_, i) => hi.rangeMin + i * hi.binWidth),
|
|
199
|
+
counts: hi.counts,
|
|
200
|
+
max: hi.counts.reduce((m, c) => (c > m ? c : m), 0),
|
|
201
|
+
bitDepth: hi.bitDepth,
|
|
202
|
+
rangeMin: hi.rangeMin,
|
|
203
|
+
rangeMax: hi.rangeMax,
|
|
204
|
+
observedMin: hi.observedMin,
|
|
205
|
+
observedMax: hi.observedMax,
|
|
206
|
+
};
|
|
207
|
+
this.nativeHistograms.set(key, out);
|
|
208
|
+
return out;
|
|
209
|
+
}
|
|
210
|
+
catch (err) {
|
|
211
|
+
// 202 (still caching) or a transient error → null; the pane retries.
|
|
212
|
+
console.warn('[viz:histogram] native histogram fetch failed', err);
|
|
213
|
+
return null;
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaGlzdG9ncmFtLXNhbXBsZXIuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi8uLi8uLi9zcmMvbGliL2ltcGxlbWVudGF0aW9ucy9vc2QvaGlzdG9ncmFtLXNhbXBsZXIudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQ0EsT0FBTyxFQUFjLGNBQWMsRUFBRSxJQUFJLEVBQUUsRUFBRSxFQUFFLE1BQU0sTUFBTSxDQUFDO0FBQzVELE9BQU8sRUFBRSxPQUFPLEVBQUUsTUFBTSxnQkFBZ0IsQ0FBQztBQUd6QyxPQUFPLEVBQUUsWUFBWSxFQUFFLGFBQWEsRUFBRSxNQUFNLGVBQWUsQ0FBQztBQUM1RCxPQUFPLEVBQUUsWUFBWSxFQUFFLE1BQU0sRUFBRSxNQUFNLDJCQUEyQixDQUFDO0FBbUNqRSxNQUFNLE9BQU8sZ0JBQWdCO0lBWTNCLFlBQ1UsSUFBZ0IsRUFDaEIsR0FBVyxFQUNYLElBQTBCO1FBRjFCLFNBQUksR0FBSixJQUFJLENBQVk7UUFDaEIsUUFBRyxHQUFILEdBQUcsQ0FBUTtRQUNYLFNBQUksR0FBSixJQUFJLENBQXNCO1FBZHBDLG1FQUFtRTtRQUMzRCxvQkFBZSxHQUFHLElBQUksR0FBRyxFQUF3QixDQUFDO1FBQzFELDhFQUE4RTtRQUN0RSxxQkFBZ0IsR0FBRyxJQUFJLEdBQUcsRUFBc0IsQ0FBQztRQUN6RDs7OzsyQ0FJbUM7UUFDbEIsb0JBQWUsR0FBRyxJQUFJLENBQUMsR0FBRyxFQUFFLENBQUM7SUFNM0MsQ0FBQztJQUVKOzBEQUNzRDtJQUN0RCxHQUFHLENBQUMsQ0FBUyxFQUFFLFlBQW9CO1FBQ2pDLE9BQU8sSUFBSSxDQUFDLGVBQWUsQ0FBQyxHQUFHLENBQUMsQ0FBQyxDQUFDLEVBQUUsQ0FBQyxZQUFZLENBQUMsSUFBSSxJQUFJLENBQUM7SUFDN0QsQ0FBQztJQUVELGlEQUFpRDtJQUNqRCxLQUFLO1FBQ0gsSUFBSSxDQUFDLGVBQWUsQ0FBQyxLQUFLLEVBQUUsQ0FBQztRQUM3QixJQUFJLENBQUMs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LEVBQUUsQ0FBQyxRQUFRO2dCQUNyQixXQUFXLEVBQUUsRUFBRSxDQUFDLFdBQVc7Z0JBQzNCLFdBQVcsRUFBRSxFQUFFLENBQUMsV0FBVzthQUM1QixDQUFDO1lBQ0YsSUFBSSxDQUFDLGdCQUFnQixDQUFDLEdBQUcsQ0FBQyxHQUFHLEVBQUUsR0FBRyxDQUFDLENBQUM7WUFDcEMsT0FBTyxHQUFHLENBQUM7UUFDYixDQUFDO1FBQUMsT0FBTyxHQUFHLEVBQUUsQ0FBQztZQUNiLHFFQUFxRTtZQUNyRSxPQUFPLENBQUMsSUFBSSxDQUFDLCtDQUErQyxFQUFFLEdBQUcsQ0FBQyxDQUFDO1lBQ25FLE9BQU8sSUFBSSxDQUFDO1FBQ2QsQ0FBQztJQUNILENBQUM7Q0FDRiIsInNvdXJjZXNDb250ZW50IjpbImltcG9ydCB7IEh0dHBDbGllbnQgfSBmcm9tICdAYW5ndWxhci9jb21tb24vaHR0cCc7XG5pbXBvcnQgeyBPYnNlcnZhYmxlLCBmaXJzdFZhbHVlRnJvbSwgZnJvbSwgb2YgfSBmcm9tICdyeGpzJztcbmltcG9ydCB7IHRpbWVvdXQgfSBmcm9tICdyeGpzL29wZXJhdG9ycyc7XG5cbmltcG9ydCB7IElIaXN0b2dyYW0gfSBmcm9tICcuLi8uLi9jb250cmFjdHMvY2hhbm5lbC1oaXN0b2dyYW0tYXBpLmNvbnRyYWN0JztcbmltcG9ydCB7IGJ1aWxkVGlsZVVybCwgZmV0Y2hUaWxlUmdiYSB9IGZyb20gJy4vdGlsZS1jbGllbnQnO1xuaW1wb3J0IHsgaGlzdG9ncmFtMjU2LCBtYXhSZ2IgfSBmcm9tICcuLi8uLi9jb250cmFjdHMvaW50ZW5zaXR5JztcblxuLyoqXG4gKiBIaXN0b2dyYW0gKyBhdXRvLXdpbmRvdyBzYW1wbGluZyBmb3IgdGhlIE9TRCBiYWNrZW5kIChyZWZhY3RvcmluZyBwbGFuLFxuICogU3RlcCA0IOKAlCBhIHB1cmUgbW92ZSBvdXQgb2YgdGhlIHZpc3VhbGl6ZXIgc2VydmljZSkuIE93bnMgdGhlIHBlci1zbGljZVxuICogOC1iaXQgaGlzdG9ncmFtcyAoc2FtcGxlZCBmcm9tIGRpc3BsYXllZCB0aWxlcykgYW5kIHRoZSBuYXRpdmUtYml0LWRlcHRoXG4gKiBoaXN0b2dyYW1zIChmZXRjaGVkIGZyb20gdGhlIHNlcnZlciBgL2hpc3RvZ3JhbWAgZW5kcG9pbnQpLiBSZWFkLW9ubHkgd2l0aFxuICogcmVzcGVjdCB0byB0aGUgdGlsZS9kaXNwbGF5IHBpcGVsaW5lLlxuICovXG5cbi8qKiBUaGUgZGVzY3JpcHRvciBmaWVsZHMgdGhlIHNhbXBsZXJzIHJlYWQgKHN0cnVjdHVyYWxseSBzYXRpc2ZpZWQgYnkgdGhlXG4gKiAgc2VydmljZSdzIFRpbGVEZXNjcmlwdG9yKS4gKi9cbmV4cG9ydCBpbnRlcmZhY2UgU2FtcGxlZERlc2NyaXB0b3Ige1xuICB3aWR0aDogbnVtYmVyO1xuICBoZWlnaHQ6IG51bWJlcjtcbiAgdGlsZVNpemU6IG51bWJlcjtcbiAgY2hhbm5lbHM/OiBudW1iZXI7XG4gIGxldmVscz86IEFycmF5PHsgd2lkdGg6IG51bWJlcjsgaGVpZ2h0OiBudW1iZXIgfT47XG59XG5cbmV4cG9ydCBpbnRlcmZhY2UgSGlzdG9ncmFtU2FtcGxlckhvc3Qge1xuICAvKiogUmVhbCBCaW8tRm9ybWF0cyBsZXZlbHMgKHBlci1jaGFubmVsIHRpbGVzIGV4aXN0IG9ubHkgdGhlcmUpLiAqL1xuICByZWFsTGV2ZWxzKCk6IG51bWJlcjtcbiAgLyoqIENoYW5uZWwgY291bnQgZm9yIHRoZSBtdWx0aWNoYW5uZWwgc2FtcGxlci4gKi9cbiAgY2hhbm5lbENvdW50KGQ6IFNhbXBsZWREZXNjcmlwdG9yKTogbnVtYmVyO1xuICAvKiogR3JheXNjYWxlIChpbnRlbnNpdHkgaGlzdG9ncmFtICsgYXV0by13aW5kb3cpIHZzIFJHQiAoUi9HL0IgaGlzdG9ncmFtcykuICovXG4gIGlzR3JheXNjYWxlKCk6IGJvb2xlYW47XG4gIC8qKiBIaXN0b2dyYW1zIGxhbmRlZCDigJQgbnVkZ2UgdGhlIHBhbmUgdG8gcmUtcmVhZCAoaXRzIHJldHJ5IHdpbmRvdyBtYXkgaGF2ZVxuICAgKiAgbGFwc2VkKS4gKi9cbiAgb25DaGFubmVsSGlzdG9ncmFtc1NhbXBsZWQoKTogdm9pZDtcbiAgLyoqIEEgZ3JheXNjYWxlIGF1dG8td2luZG93IHdhcyBtZWFzdXJlZCBmcm9tIGZ1bGwtcmVzIHRpbGVzIOKAlCBzZWVkIHRoZVxuICAgKiAgSW50ZW5zaXR5IGNoYW5uZWwgKG9yIHJlLWludmFsaWRhdGUgaWYgdGhlIHVzZXIgYWxyZWFkeSB3aW5kb3dlZCkuICovXG4gIG9uR3JheVdpbmRvd1NhbXBsZWQobWluOiBudW1iZXIsIG1heDogbnVtYmVyKTogdm9pZDtcbn1cblxuZXhwb3J0IGNsYXNzIEhpc3RvZ3JhbVNhbXBsZXIge1xuICAvKiogMjU2LWJpbiBpbnRlbnNpdHkgaGlzdG9ncmFtIHBlciB6LXNsaWNlLCBmcm9tIHNhbXBsZWQgdGlsZXMuICovXG4gIHByaXZhdGUgc2xpY2VIaXN0b2dyYW1zID0gbmV3IE1hcDxudW1iZXIsIElIaXN0b2dyYW1bXT4oKTtcbiAgLyoqIE5hdGl2ZS1iaXQtZGVwdGggaGlzdG9ncmFtcyBmcm9tIGAvaGlzdG9ncmFtYCwga2V5ZWQgYCR7en18JHtjaGFubmVsfWAuICovXG4gIHByaXZhdGUgbmF0aXZlSGlzdG9ncmFtcyA9IG5ldyBNYXA8c3RyaW5nLCBJSGlzdG9ncmFtPigpO1xuICAvKiogUGVyLWFwcC1sb2FkIGNhY2hlLWJ1c3RlciBmb3IgYC9oaXN0b2dyYW1gIOKAlCB0aGUgc2VydmVyIG1hcmtzIHRoZVxuICAgKiAgcmVzcG9uc2UgY2FjaGVhYmxlIGZvciAyNGgsIGFuZCBhIGhhcmQgcmVmcmVzaCBjYW4ndCBidXN0IGEgcG9zdC1sb2FkXG4gICAqICBYSFIuIEEgdG9rZW4gc3RhYmxlIHdpdGhpbiBhIHNlc3Npb24gYnV0IG5ldyBvbiBlYWNoIGZ1bGwgbG9hZCBrZWVwcyB0aGVcbiAgICogIGluLXNlc3Npb24gZGVkdXAgKHZpYSBgbmF0aXZlSGlzdG9ncmFtc2ApIHdoaWxlIGFsd2F5cyByZWZsZWN0aW5nIHRoZVxuICAgKiAgbGl2ZSBiYWNrZW5kIGFmdGVyIGEgcmVsb2FkLiAqL1xuICBwcml2YXRlIHJlYWRvbmx5IGhpc3RDYWNoZUJ1c3RlciA9IERhdGUubm93KCk7XG5cbiAgY29uc3RydWN0b3IoXG4gICAgcHJpdmF0ZSBodHRwOiBIdHRwQ2xpZW50LFxuICAgIHByaXZhdGUgYXBpOiBzdHJpbmcsXG4gICAgcHJpdmF0ZSBob3N0OiBIaXN0b2dyYW1TYW1wbGVySG9zdCxcbiAgKSB7fVxuXG4gIC8qKiBUaGUgc2FtcGxlZCBoaXN0b2dyYW0gZm9yIG9uZSBjaGFubmVsIG9mIHNsaWNlIHosIG9yIG51bGwgdW50aWwgdGhlXG4gICAqICBhc3luYyBzYW1wbGluZyByZXNvbHZlcyAob3IgaWYgaXQgd2FzIHNraXBwZWQpLiAqL1xuICBnZXQoejogbnVtYmVyLCBjaGFubmVsSW5kZXg6IG51bWJlcik6IElIaXN0b2dyYW0gfCBudWxsIHtcbiAgICByZXR1cm4gdGhpcy5zbGljZUhpc3RvZ3JhbXMuZ2V0KHopPy5bY2hhbm5lbEluZGV4XSA/PyBudWxsO1xuICB9XG5cbiAgLyoqIERyb3AgZXZlcnl0aGluZyAodGVhcmRvd24gLyBpbWFnZSBzd2l0Y2gpLiAqL1xuICBjbGVhcigpOiB2b2lkIHtcbiAgICB0aGlzLnNsaWNlSGlzdG9ncmFtcy5jbGVhcigpO1xuICAgIHRoaXMubmF0aXZlSGlzdG9ncmFtcy5jbGVhcigpO1xuICB9XG5cbiAgLyoqXG4gICAqIFBlci1jaGFubmVsIGludGVuc2l0eSBoaXN0b2dyYW1zIGZvciBtdWx0aWNoYW5uZWwgaW1hZ2VzOiBzYW1wbGUgZWFjaFxuICAgKiBjaGFubmVsJ3Mgc2luZ2xlLWJhbmQgdGlsZXMgYXQgdGhlIGNvYXJzZXN0IHJlYWwgbGV2ZWwgYW5kIGJpbiB0aGVcbiAgICogbHVtaW5hbmNlLiBGaXJlLWFuZC1mb3JnZXQuXG4gICAqL1xuICBhc3luYyBjb21wdXRlTXVsdGlDaGFubmVsSGlzdG9ncmFtcyhkOiBTYW1wbGVkRGVzY3JpcHRvciwgaW5mb0I2NDogc3RyaW5nLCB6OiBudW1iZXIpOiBQcm9taXNlPHZvaWQ+IHtcbiAgICB0cnkge1xuICAgICAgY29uc3QgdCA9IGQudGlsZVNpemU7XG4gICAgICAvLyBTYW1wbGUgdGhlIENPQVJTRVNUIHJlYWwgbGV2ZWwgKHBlci1jaGFubmVsIHRpbGVzIGV4aXN0IG9ubHkgYXQgcmVhbFxuICAgICAgLy8gcmVzb2x1dGlvbnM7IHRoZSBjb2Fyc2VzdCBpcyBzbWFsbCBlbm91Z2ggdG8gYmluIGV2ZW4gZm9yIHdob2xlLXNsaWRlcykuXG4gICAgICBjb25zdCByZXMgPSBNYXRoLm1heCgwLCB0aGlzLmhvc3QucmVhbExldmVscygpIC0gMSk7XG4gICAgICBjb25zdCBsdmwgPSBkLmxldmVscz8uW3Jlc107XG4gICAgICBjb25zdCBsdyA9IGx2bD8ud2lkdGggPz8gZC53aWR0aDtcbiAgICAgIGNvbnN0IGxoID0gbHZsPy5oZWlnaHQgPz8gZC5oZWlnaHQ7XG4gICAgICBjb25zdCBjb2xzID0gTWF0aC5tYXgoMSwgTWF0aC5jZWlsKGx3IC8gdCkpO1xuICAgICAgY29uc3Qgcm93cyA9IE1hdGgubWF4KDEsIE1hdGguY2VpbChsaCAvIHQpKTtcbiAgICAgIGlmIChjb2xzICogcm93cyA+IDY0KSByZXR1cm47IC8vIGV2ZW4gdGhlIGNvYXJzZXN0IHJlYWwgbGV2ZWwgaXMgdG9vIGJpZ1xuICAgICAgY29uc3QgbkNoID0gdGhpcy5ob3N0LmNoYW5uZWxDb3VudChkKTtcbiAgICAgIGlmIChuQ2ggPCAxKSByZXR1cm47XG4gICAgICBjb25zdCBjb3VudHM6IG51bWJlcltdW10gPSBBcnJheS5mcm9tKHsgbGVuZ3RoOiBuQ2ggfSwgKCkgPT4gbmV3IEFycmF5KDI1NikuZmlsbCgwKSk7XG4gICAgICBjb25zdCBqb2JzOiBBcnJheTxQcm9taXNlPHZvaWQ+PiA9IFtdO1xuICAgICAgZm9yIChsZXQgcm93ID0gMDsgcm93IDwgcm93czsgcm93KyspIHtcbiAgICAgICAgZm9yIChsZXQgY29sID0gMDsgY29sIDwgY29sczsgY29sKyspIHtcbiAgICAgICAgICBmb3IgKGxldCBjID0gMDsgYyA8IG5DaDsgYysrKSB7XG4gICAgICAgICAgICBjb25zdCB1cmwgPSBidWlsZFRpbGVVcmwodGhpcy5hcGksIGluZm9CNjQsIHsgcmVzLCBjb2wsIHJvdywgeiwgdGlsZVNpemU6IHQsIGNoYW5uZWw6IGMgfSk7XG4gICAgICAgICAgICBqb2JzLnB1c2goXG4gICAgICAgICAgICAgIChhc3luYyAoKSA9PiB7XG4gICAgICAgICAgICAgICAgdHJ5IHtcbiAgICAgICAgICAgICAgICAgIGNvbnN0IGltZyA9IGF3YWl0IGZldGNoVGlsZVJnYmEodGhpcy5odHRwLCB1cmwsIDIwMDAwKTtcbiAgICAgICAgICAgICAgICAgIGlmICghaW1nKSByZXR1cm47XG4gICAgICAgICAgICAgICAgICBjb25zdCBkYXRhID0gaW1nLmRhdGE7XG4gICAgICAgICAgICAgICAgICBjb25zdCBjYyA9IGNvdW50c1tjXTtcbiAgICAgICAgICAgICAgICAgIGZvciAobGV0IGkgPSAwOyBpIDwgZGF0YS5sZW5ndGg7IGkgKz0gNCkge1xuICAgICAgICAgICAgICAgICAgICBpZiAoZGF0YVtpICsgM10gPT09IDApIGNvbnRpbnVlO1xuICAgICAgICAgICAgICAgICAgICBjY1ttYXhSZ2IoZGF0YVtpXSwgZGF0YVtpICsgMV0sIGRhdGFbaSArIDJdKV0rKztcbiAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICB9IGNhdGNoIChlcnIpIHtcbiAgICAgICAgICAgICAgICAgIC8vIFNraXAgdGhpcyB0aWxlIOKAlCB0aGUgaGlzdG9ncmFtIGlzIGp1c3QgbWlzc2luZyBpdHMgY291bnRzLlxuICAgICAgICAgICAgICAgICAgY29uc29sZS53YXJuKCdbdml6Omhpc3RvZ3JhbV0gY2hhbm5lbCB0aWxlIHNhbXBsZSBmYWlsZWQsIHNraXBwaW5nJywgdXJsLCBlcnIpO1xuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgfSkoKSxcbiAgICAgICAgICAgICk7XG4gICAgICAgICAgfVxuICAgICAgICB9XG4gICAgICB9XG4gICAgICBhd2FpdCBQcm9taXNlLmFsbChqb2JzKTtcbiAgICAgIHRoaXMuc2xpY2VIaXN0b2dyYW1zLnNldCh6LCBjb3VudHMubWFwKChjKSA9PiBoaXN0b2dyYW0yNTYoYykpKTtcbiAgICAgIHRoaXMuaG9zdC5vbkNoYW5uZWxIaXN0b2dyYW1zU2FtcGxlZCgpO1xuICAgIH0gY2F0Y2ggKGVycikge1xuICAgICAgLy8gTGVhdmUgaGlzdG9ncmFtcyB1bnNldCAodGhlIHBhbmUgc2hvd3MgZW1wdHkpIOKAlCBidXQgc2F5IHdoeS5cbiAgICAgIGNvbnNvbGUud2FybignW3ZpejpoaXN0b2dyYW1dIG11bHRpY2hhbm5lbCBoaXN0b2dyYW0gc2FtcGxpbmcgZmFpbGVkJywgZXJyKTtcbiAgICB9XG4gIH1cblxuICAvKipcbiAgICogQ29tcHV0ZSBhIHBlci1pbWFnZSBpbnRlbnNpdHkgd2luZG93IFttaW4sbWF4XSBmb3IgZ3JheXNjYWxlIGF1dG8tcmFuZ2luZyxcbiAgICogbWlycm9yaW5nIHRoZSBoZWF0bWFwJ3MgYXV0by1zdHJldGNoLiBUd28gY29uc3RyYWludHMgZm9yY2UgdGhlIHNhbXBsaW5nXG4gICAqIHN0cmF0ZWd5OlxuICAgKiAgLSBUaGUgd2luZG93IE1VU1QgYmUgaW4gdGhlIHNhbWUgdmFsdWUgc3BhY2UgYXMgdGhlIGRpc3BsYXllZCB0aWxlczogdGhlXG4gICAqICAgIGAvcHJldmlld2AgZW5kcG9pbnQgaXMgc2VydmVyLW5vcm1hbGl6ZWQgd2hpbGUgYC90aWxlYCBzZXJ2ZXMgcmF3XG4gICAqICAgIHVuLW5vcm1hbGl6ZWQgOC1iaXQg4oCUIGEgd2luZG93IGZyb20gdGhlIHByZXZpZXcgd291bGRuJ3QgbWF0Y2guXG4gICAqICAtIEl0IG11c3QgcmVhZCBGVUxMLVJFU09MVVRJT04gdGlsZXMsIG5vdCBhIGRvd25zYW1wbGVkIG92ZXJ2aWV3OlxuICAgKiAgICBhdmVyYWdpbmcgc3BhcnNlIGxvdyB2YWx1ZXMgKGxhYmVsIG1hc2tzKSB0b3dhcmQgMCB3aXBlcyB0aGUgc2lnbmFsLlxuICAgKiBTbyB3ZSBzYW1wbGUgdGhlIGZ1bGwtcmVzIGxldmVsIGFjcm9zcyB0aGUgd2hvbGUgaW1hZ2Ug4oCUIGJvdW5kZWQgdG8gNjRcbiAgICogdGlsZXMgKGEgaHVnZSBncmF5c2NhbGUgaW1hZ2Ugc2FtcGxlcyB0aGUgY29hcnNlc3Qgb3ZlcnZpZXcgZm9yIGhpc3RvZ3JhbXNcbiAgICogb25seSkuIEZpcmUtYW5kLWZvcmdldDsgZmFpbHVyZXMgbGVhdmUgdGhlIHdpbmRvdyB1bnNldCAoaWRlbnRpdHkgMC4uMjU1KS5cbiAgICovXG4gIGFzeW5jIGNvbXB1dGVJbWFnZVdpbmRvdyhkOiBTYW1wbGVkRGVzY3JpcHRvciwgaW5mb0I2NDogc3RyaW5nLCB6OiBudW1iZXIpOiBQcm9taXNlPHZvaWQ+IHtcbiAgICBjb25zdCBncmF5ID0gdGhpcy5ob3N0LmlzR3JheXNjYWxlKCk7XG4gICAgdHJ5IHtcbiAgICAgIGNvbnN0IHQgPSBkLnRpbGVTaXplO1xuICAgICAgLy8gU2FtcGxlIGZ1bGwtcmVzb2x1dGlvbiB3aGVuIHRoZSBncmlkIGlzIHNtYWxsIChhY2N1cmF0ZSBmb3IgdGhlIGdyYXlzY2FsZVxuICAgICAgLy8gYXV0by13aW5kb3cpOyBvdGhlcndpc2Ugc2FtcGxlIHRoZSBjb2Fyc2VzdCBvdmVydmlldyBqdXN0IGZvciBoaXN0b2dyYW1zXG4gICAgICAvLyAod2hvbGUtc2xpZGUgaW1hZ2VzKS4gUkdCIG9ubHkgbmVlZHMgdGhlIGhpc3RvZ3JhbXMsIG5vdCBhbiBhdXRvLXdpbmRvdy5cbiAgICAgIGxldCByZXMgPSAwO1xuICAgICAgbGV0IGNvbHMgPSBNYXRoLm1heCgxLCBNYXRoLmNlaWwoZC53aWR0aCAvIHQpKTtcbiAgICAgIGxldCByb3dzID0gTWF0aC5tYXgoMSwgTWF0aC5jZWlsKGQuaGVpZ2h0IC8gdCkpO1xuICAgICAgbGV0IGZ1bGxSZXMgPSB0cnVlO1xuICAgICAgaWYgKGNvbHMgKiByb3dzID4gNjQpIHtcbiAgICAgICAgcmVzID0gTWF0aC5tYXgoMCwgKGQubGV2ZWxzPy5sZW5ndGggPz8gMSkgLSAxKTtcbiAgICAgICAgY29uc3QgbHZsID0gZC5sZXZlbHM/LltyZXNdO1xuICAgICAgICBjb2xzID0gTWF0aC5tYXgoMSwgTWF0aC5jZWlsKChsdmw/LndpZHRoID8/IGQud2lkdGgpIC8gdCkpO1xuICAgICAgICByb3dzID0gTWF0aC5tYXgoMSwgTWF0aC5jZWlsKChsdmw/LmhlaWdodCA/PyBkLmhlaWdodCkgLyB0KSk7XG4gICAgICAgIGZ1bGxSZXMgPSBmYWxzZTtcbiAgICAgICAgaWYgKGNvbHMgKiByb3dzID4gMzYpIHJldHVybjsgLy8gZXZlbiB0aGUgY29hcnNlc3QgaXMgdG9vIGJpZyDigJQgZ2l2ZSB1cFxuICAgICAgfVxuICAgICAgY29uc3QgY29vcmRzOiBBcnJheTxbbnVtYmVyLCBudW1iZXJdPiA9IFtdO1xuICAgICAgZm9yIChsZXQgcm93ID0gMDsgcm93IDwgcm93czsgcm93KyspIHtcbiAgICAgICAgZm9yIChsZXQgY29sID0gMDsgY29sIDwgY29sczsgY29sKyspIGNvb3Jkcy5wdXNoKFtjb2wsIHJvd10pO1xuICAgICAgfVxuICAgICAgbGV0IG1pbiA9IDI1NTtcbiAgICAgIGxldCBtYXggPSAwO1xuICAgICAgY29uc3QgY1IgPSBuZXcgQXJyYXkoMjU2KS5maWxsKDApO1xuICAgICAgY29uc3QgY0cgPSBncmF5ID8gbnVsbCA6IG5ldyBBcnJheSgyNTYpLmZpbGwoMCk7XG4gICAgICBjb25zdCBjQiA9IGdyYXkgPyBudWxsIDogbmV3IEFycmF5KDI1NikuZmlsbCgwKTtcbiAgICAgIGF3YWl0IFByb21pc2UuYWxsKFxuICAgICAgICBjb29yZHMubWFwKGFzeW5jIChbY29sLCByb3ddKSA9PiB7XG4gICAgICAgICAgY29uc3QgdXJsID0gYnVpbGRUaWxlVXJsKHRoaXMuYXBpLCBpbmZvQjY0LCB7IHJlcywgY29sLCByb3csIHosIHRpbGVTaXplOiB0IH0pO1xuICAgICAgICAgIHRyeSB7XG4gICAgICAgICAgICBjb25zdCBpbWcgPSBhd2FpdCBmZXRjaFRpbGVSZ2JhKHRoaXMuaHR0cCwgdXJsLCAyMDAwMCk7XG4gICAgICAgICAgICBpZiAoIWltZykgcmV0dXJuO1xuICAgICAgICAgICAgY29uc3QgZGF0YSA9IGltZy5kYXRhO1xuICAgICAgICAgICAgZm9yIChsZXQgaSA9IDA7IGkgPCBkYXRhLmxlbmd0aDsgaSArPSA0KSB7XG4gICAgICAgICAgICAgIGlmIChkYXRhW2kgKyAzXSA9PT0gMCkgY29udGludWU7IC8vIHNraXAgdHJhbnNwYXJlbnQgcGFkZGluZ1xuICAgICAgICAgICAgICBpZiAoZ3JheSkge1xuICAgICAgICAgICAgICAgIGNvbnN0IHYgPSBtYXhSZ2IoZGF0YVtpXSwgZGF0YVtpICsgMV0sIGRhdGFbaSArIDJdKTtcbiAgICAgICAgICAgICAgICBpZiAodiA8IG1pbikgbWluID0gdjtcbiAgICAgICAgICAgICAgICBpZiAodiA+IG1heCkgbWF4ID0gdjtcbiAgICAgICAgICAgICAgICBjUlt2XSsrO1xuICAgICAgICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgICAgIGNSW2RhdGFbaV1dKys7XG4gICAgICAgICAgICAgICAgY0chW2RhdGFbaSArIDFdXSsrO1xuICAgICAgICAgICAgICAgIGNCIVtkYXRhW2kgKyAyXV0rKztcbiAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgfVxuICAgICAgICAgIH0gY2F0Y2ggKGVycikge1xuICAgICAgICAgICAgLy8gU2tpcCB0aGlzIHRpbGUg4oCUIHdpbmRvdy9oaXN0b2dyYW0ganVzdCBsb3NlIGl0cyBzYW1wbGVzLlxuICAgICAgICAgICAgY29uc29sZS53YXJuKCdbdml6OndpbmRvd10gdGlsZSBzYW1wbGUgZmFpbGVkLCBza2lwcGluZycsIHVybCwgZXJyKTtcbiAgICAgICAgICB9XG4gICAgICAgIH0pLFxuICAgICAgKTtcbiAgICAgIC8vIENhY2hlIHBlci1jaGFubmVsIGhpc3RvZ3JhbXM6IGdyYXlzY2FsZSDihpIgW2ludGVuc2l0eV07IFJHQiDihpIgW1IsIEcsIEJdLlxuICAgICAgdGhpcy5zbGljZUhpc3RvZ3JhbXMuc2V0KFxuICAgICAgICB6LCBncmF5ID8gW2hpc3RvZ3JhbTI1NihjUildIDogW2hpc3RvZ3JhbTI1NihjUiksIGhpc3RvZ3JhbTI1NihjRyEpLCBoaXN0b2dyYW0yNTYoY0IhKV0sXG4gICAgICApO1xuICAgICAgLy8gR3JheXNjYWxlIGF1dG8td2luZG93IOKAlCBvbmx5IGZyb20gZnVsbC1yZXMgc2FtcGxlcyAoY29hcnNlc3QgYXZlcmFnaW5nXG4gICAgICAvLyBpcyBpbmFjY3VyYXRlKTsgdGhlIGhvc3Qgc2VlZHMgdGhlIGNoYW5uZWwgb3IgcmUtaW52YWxpZGF0ZXMuXG4gICAgICBpZiAoZ3JheSAmJiBmdWxsUmVzICYmIG1heCA+IG1pbiAmJiAobWluID4gMCB8fCBtYXggPCAyNTUpKSB7XG4gICAgICAgIHRoaXMuaG9zdC5vbkdyYXlXaW5kb3dTYW1wbGVkKG1pbiwgbWF4KTtcbiAgICAgIH1cbiAgICB9IGNhdGNoIChlcnIpIHtcbiAgICAgIGNvbnNvbGUud2FybignW3Zpejp3aW5kb3ddIHBlci1pbWFnZSB3aW5kb3cgY29tcHV0ZSBmYWlsZWQg4oCUIHVzaW5nIGlkZW50aXR5IDAuLjI1NScsIGVycik7XG4gICAgfVxuICB9XG5cbiAgLyoqXG4gICAqIE5hdGl2ZS1iaXQtZGVwdGggaGlzdG9ncmFtIGZvciA+OC1iaXQgaW1hZ2VzLCBmcm9tIHRoZSBzZXJ2ZXIgYC9oaXN0b2dyYW1gXG4gICAqIGVuZHBvaW50ICh0aGUgOC1iaXQgY2FudmFzIHRpbGVzIGNhbid0IGNhcnJ5IDE2LWJpdCB2YWx1ZXMpLiBDYWNoZWQgcGVyXG4gICAqIHNsaWNlK2NoYW5uZWwgZm9yIHRoZSBzZXNzaW9uLlxuICAgKi9cbiAgbmF0aXZlJChpbmZvQjY0OiBzdHJpbmcsIHo6IG51bWJlciwgY2hhbm5lbDogbnVtYmVyLCBiaW5zOiBudW1iZXIpOiBPYnNlcnZhYmxlPElIaXN0b2dyYW0gfCBudWxsPiB7XG4gICAgY29uc3Qga2V5ID0gYCR7en18JHtjaGFubmVsfWA7XG4gICAgY29uc3QgY2FjaGVkID0gdGhpcy5uYXRpdmVIaXN0b2dyYW1zLmdldChrZXkpO1xuICAgIGlmIChjYWNoZWQpIHJldHVybiBvZihjYWNoZWQpO1xuICAgIHJldHVybiBmcm9tKHRoaXMuZmV0Y2hOYXRpdmUoaW5mb0I2NCwgeiwgY2hhbm5lbCwgYmlucywga2V5KSk7XG4gIH1cblxuICAvKiogRmV0Y2ggKyBjYWNoZSBvbmUgY2hhbm5lbCdzIG5hdGl2ZSBoaXN0b2dyYW0gZnJvbSBgR0VUIC9oaXN0b2dyYW1gLiAqL1xuICBwcml2YXRlIGFzeW5jIGZldGNoTmF0aXZlKFxuICAgIGluZm9CNjQ6IHN0cmluZywgejogbnVtYmVyLCBjaGFubmVsOiBudW1iZXIsIGJpbnM6IG51bWJlciwga2V5OiBzdHJpbmcsXG4gICk6IFByb21pc2U8SUhpc3RvZ3JhbSB8IG51bGw+IHtcbiAgICBjb25zdCB1cmwgPSBgJHt0aGlzLmFwaX1oaXN0b2dyYW0/aW5mbz0ke2luZm9CNjR9JmNoYW5uZWw9JHtjaGFubmVsfSZ6PSR7en0mYmlucz0ke2JpbnN9Jl89JHt0aGlzLmhpc3RDYWNoZUJ1c3Rlcn1gO1xuICAgIHRyeSB7XG4gICAgICAvLyBIdHRwQ2xpZW50IGNhbGxzIGdldCBhdXRoIHZpYSB0aGUgQW5ndWxhciBpbnRlcmNlcHRvciAodW5saWtlIE9TRCdzIG93blxuICAgICAgLy8gYWpheCB0aWxlIGxvYWRlciwgd2hpY2ggbmVlZHMgYXV0aEhlYWRlcnMpIOKAlCBtaXJyb3IgdGhlIG90aGVyIGZldGNoZXMuXG4gICAgICBjb25zdCBoaSA9IGF3YWl0IGZpcnN0VmFsdWVGcm9tKFxuICAgICAgICB0aGlzLmh0dHBcbiAgICAgICAgICAuZ2V0PHtcbiAgICAgICAgICAgIGJpdERlcHRoOiBudW1iZXI7IHJhbmdlTWluOiBudW1iZXI7IHJhbmdlTWF4OiBudW1iZXI7XG4gICAgICAgICAgICBvYnNlcnZlZE1pbjogbnVtYmVyOyBvYnNlcnZlZE1heDogbnVtYmVyOyBiaW5XaWR0aDogbnVtYmVyOyBjb3VudHM6IG51bWJlcltdO1xuICAgICAgICAgIH0+KHVybClcbiAgICAgICAgICAucGlwZSh0aW1lb3V0KDQ1MDAwKSksXG4gICAgICApO1xuICAgICAgaWYgKCFoaSB8fCAhaGkuY291bnRzKSByZXR1cm4gbnVsbDtcbiAgICAgIGNvbnN0IG91dDogSUhpc3RvZ3JhbSA9IHtcbiAgICAgICAgLy8gTmF0aXZlIGJpbiBsZWZ0LWVkZ2VzOiByYW5nZU1pbiArIGkqYmluV2lkdGguXG4gICAgICAgIGJpbnM6IGhpLmNvdW50cy5tYXAoKF8sIGkpID0+IGhpLnJhbmdlTWluICsgaSAqIGhpLmJpbldpZHRoKSxcbiAgICAgICAgY291bnRzOiBoaS5jb3VudHMsXG4gICAgICAgIG1heDogaGkuY291bnRzLnJlZHVjZSgobSwgYykgPT4gKGMgPiBtID8gYyA6IG0pLCAwKSxcbiAgICAgICAgYml0RGVwdGg6IGhpLmJpdERlcHRoLFxuICAgICAgICByYW5nZU1pbjogaGkucmFuZ2VNaW4sXG4gICAgICAgIHJhbmdlTWF4OiBoaS5yYW5nZU1heCxcbiAgICAgICAgb2JzZXJ2ZWRNaW46IGhpLm9ic2VydmVkTWluLFxuICAgICAgICBvYnNlcnZlZE1heDogaGkub2JzZXJ2ZWRNYXgsXG4gICAgICB9O1xuICAgICAgdGhpcy5uYXRpdmVIaXN0b2dyYW1zLnNldChrZXksIG91dCk7XG4gICAgICByZXR1cm4gb3V0O1xuICAgIH0gY2F0Y2ggKGVycikge1xuICAgICAgLy8gMjAyIChzdGlsbCBjYWNoaW5nKSBvciBhIHRyYW5zaWVudCBlcnJvciDihpIgbnVsbDsgdGhlIHBhbmUgcmV0cmllcy5cbiAgICAgIGNvbnNvbGUud2FybignW3ZpejpoaXN0b2dyYW1dIG5hdGl2ZSBoaXN0b2dyYW0gZmV0Y2ggZmFpbGVkJywgZXJyKTtcbiAgICAgIHJldHVybiBudWxsO1xuICAgIH1cbiAgfVxufVxuIl19
|