@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,301 @@
|
|
|
1
|
+
import { Injectable } from '@angular/core';
|
|
2
|
+
import { Region, Rectangle, Polygon, MultiPolygon } from './models/region';
|
|
3
|
+
import { unionMasks, simplifyRing } from './models/geometry';
|
|
4
|
+
import { WandService } from './toolbar/wand/wand.service';
|
|
5
|
+
import * as i0 from "@angular/core";
|
|
6
|
+
import * as i1 from "./toolbar/wand/wand.service";
|
|
7
|
+
/**
|
|
8
|
+
* Pure (DOM-free) region set-operations — merge / inverse / ungroup (jit-ui#85).
|
|
9
|
+
*
|
|
10
|
+
* Raster-based, reusing the wand/brush mask pipeline
|
|
11
|
+
* ({@link WandService.rasterizePolygon} → {@link WandService.maskToPolygons}),
|
|
12
|
+
* so "select + merge" yields the same geometry as brushing regions together:
|
|
13
|
+
* one engine, one set of behaviours, holes + multi-part results for free.
|
|
14
|
+
*
|
|
15
|
+
* Every method takes the current regions plus the image dimensions and returns
|
|
16
|
+
* new {@link Region}(s); it never mutates the store. Callers commit the result
|
|
17
|
+
* through the normal region-write path (so undo/redo applies). Coordinates are
|
|
18
|
+
* image pixels throughout.
|
|
19
|
+
*/
|
|
20
|
+
export class RegionOpsService {
|
|
21
|
+
/**
|
|
22
|
+
* Largest raster (in pixels) a region set-op will allocate on the main thread.
|
|
23
|
+
* Selections whose clipped bbox exceeds this are rasterized at a proportional
|
|
24
|
+
* downscale (then the traced geometry is scaled back), so merge/inverse stay
|
|
25
|
+
* responsive on gigapixel slides. ~16 MP keeps the synchronous contour trace
|
|
26
|
+
* well under a second; the downscale only affects boundary fidelity of very
|
|
27
|
+
* large-extent selections (small selections keep full resolution).
|
|
28
|
+
*/
|
|
29
|
+
static { this.MAX_OP_PIXELS = 16_000_000; }
|
|
30
|
+
constructor(wand) {
|
|
31
|
+
this.wand = wand;
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Geometric union of `regions` into a single region: a {@link Polygon} when
|
|
35
|
+
* the union is one connected piece, a {@link MultiPolygon} when it stays
|
|
36
|
+
* disjoint, with holes preserved. Returns null when nothing rasterises (e.g.
|
|
37
|
+
* only open polylines were selected).
|
|
38
|
+
*/
|
|
39
|
+
merge(regions, imageWidth, imageHeight) {
|
|
40
|
+
// The raster is bounded by the selection's bbox, not the image — but on a
|
|
41
|
+
// whole-slide image a large-extent selection still spans billions of pixels,
|
|
42
|
+
// which freezes (or overflows the typed-array limit) on the main thread.
|
|
43
|
+
// Rasterize at a capped resolution and scale the traced polygons back up.
|
|
44
|
+
const scale = this.opRasterScale(regions, imageWidth, imageHeight);
|
|
45
|
+
const mask = this.unionMask(regions, imageWidth, imageHeight, scale);
|
|
46
|
+
if (!mask)
|
|
47
|
+
return null;
|
|
48
|
+
const sw = Math.max(1, Math.round(imageWidth * scale));
|
|
49
|
+
const sh = Math.max(1, Math.round(imageHeight * scale));
|
|
50
|
+
let polys = this.wand.maskToPolygons(mask.mask, mask.bw, mask.bh, sw, sh, mask.bx, mask.by, 1, 1);
|
|
51
|
+
if (scale !== 1)
|
|
52
|
+
polys = polys.map((p) => this.scalePolygon(p, 1 / scale));
|
|
53
|
+
return this.regionFromParts(polys, regions[0]);
|
|
54
|
+
}
|
|
55
|
+
/** Downscale factor (≤ 1) keeping the selection's clipped bbox within
|
|
56
|
+
* {@link MAX_OP_PIXELS}; 1 when it already fits. */
|
|
57
|
+
opRasterScale(regions, W, H) {
|
|
58
|
+
let minX = Infinity, minY = Infinity, maxX = -Infinity, maxY = -Infinity;
|
|
59
|
+
const scan = (xs, ys) => {
|
|
60
|
+
for (const x of xs) {
|
|
61
|
+
if (x < minX)
|
|
62
|
+
minX = x;
|
|
63
|
+
if (x > maxX)
|
|
64
|
+
maxX = x;
|
|
65
|
+
}
|
|
66
|
+
for (const y of ys) {
|
|
67
|
+
if (y < minY)
|
|
68
|
+
minY = y;
|
|
69
|
+
if (y > maxY)
|
|
70
|
+
maxY = y;
|
|
71
|
+
}
|
|
72
|
+
};
|
|
73
|
+
for (const r of regions || []) {
|
|
74
|
+
const b = r?.bounds;
|
|
75
|
+
if (b instanceof Rectangle)
|
|
76
|
+
scan([b.x, b.x + b.width], [b.y, b.y + b.height]);
|
|
77
|
+
else if (b instanceof Polygon)
|
|
78
|
+
scan(b.xpoints, b.ypoints);
|
|
79
|
+
else if (b instanceof MultiPolygon)
|
|
80
|
+
for (const p of b.polygons)
|
|
81
|
+
scan(p.xpoints, p.ypoints);
|
|
82
|
+
}
|
|
83
|
+
if (!Number.isFinite(minX))
|
|
84
|
+
return 1;
|
|
85
|
+
const bw = Math.min(W, maxX) - Math.max(0, minX);
|
|
86
|
+
const bh = Math.min(H, maxY) - Math.max(0, minY);
|
|
87
|
+
const area = bw * bh;
|
|
88
|
+
if (!(area > RegionOpsService.MAX_OP_PIXELS))
|
|
89
|
+
return 1;
|
|
90
|
+
return Math.sqrt(RegionOpsService.MAX_OP_PIXELS / area);
|
|
91
|
+
}
|
|
92
|
+
/** A copy of `p` with every coordinate (and hole coordinate) multiplied by `s`. */
|
|
93
|
+
scalePolygon(p, s) {
|
|
94
|
+
const poly = new Polygon();
|
|
95
|
+
poly.xpoints = p.xpoints.map((x) => x * s);
|
|
96
|
+
poly.ypoints = p.ypoints.map((y) => y * s);
|
|
97
|
+
poly.npoints = poly.xpoints.length;
|
|
98
|
+
poly.closed = p.closed;
|
|
99
|
+
poly.coordinates = poly.xpoints.map((x, i) => [x, poly.ypoints[i]]);
|
|
100
|
+
if (p.holes)
|
|
101
|
+
poly.holes = p.holes.map((ring) => ring.map(([x, y]) => [x * s, y * s]));
|
|
102
|
+
return poly;
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* Replace `regions` with their inverse inside the image rectangle: the image
|
|
106
|
+
* minus the selection's union (the former regions become holes). Returns null
|
|
107
|
+
* for a degenerate image or an empty selection.
|
|
108
|
+
*
|
|
109
|
+
* Done *vectorially* so it scales to gigapixel images: the selection is
|
|
110
|
+
* unioned first (raster, bounded by the ROIs' extent — never the whole image),
|
|
111
|
+
* then the result is the image rectangle as the exterior with each union
|
|
112
|
+
* component's outline punched out as a hole. A component's own hole (a donut)
|
|
113
|
+
* becomes a solid island part. Only the image's corner coordinates are used —
|
|
114
|
+
* no full-image buffer is ever allocated.
|
|
115
|
+
*/
|
|
116
|
+
inverse(regions, imageWidth, imageHeight) {
|
|
117
|
+
if (imageWidth <= 0 || imageHeight <= 0)
|
|
118
|
+
return null;
|
|
119
|
+
const merged = this.merge(regions, imageWidth, imageHeight);
|
|
120
|
+
if (!merged)
|
|
121
|
+
return null;
|
|
122
|
+
const comps = merged.bounds instanceof MultiPolygon
|
|
123
|
+
? merged.bounds.polygons
|
|
124
|
+
: merged.bounds instanceof Polygon ? [merged.bounds] : [];
|
|
125
|
+
if (comps.length === 0)
|
|
126
|
+
return null;
|
|
127
|
+
// Image rectangle exterior with every component outline as a hole.
|
|
128
|
+
const rect = this.ringPolygon([0, imageWidth, imageWidth, 0], [0, 0, imageHeight, imageHeight]);
|
|
129
|
+
rect.holes = comps.map((c) => c.xpoints.map((x, i) => [x, c.ypoints[i]]));
|
|
130
|
+
// Each component's own hole (donut interior) is *outside* the region, so it
|
|
131
|
+
// stays solid in the inverse — emit it as its own island part.
|
|
132
|
+
const parts = [rect];
|
|
133
|
+
for (const c of comps) {
|
|
134
|
+
if (c.holes) {
|
|
135
|
+
for (const ring of c.holes) {
|
|
136
|
+
parts.push(this.ringPolygon(ring.map((p) => p[0]), ring.map((p) => p[1])));
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
const bounds = parts.length === 1 ? parts[0] : Object.assign(new MultiPolygon(), { polygons: parts });
|
|
141
|
+
return this.makeRegion(bounds, regions[0]);
|
|
142
|
+
}
|
|
143
|
+
/** A closed straight-edged Polygon from parallel coord arrays. */
|
|
144
|
+
ringPolygon(xs, ys) {
|
|
145
|
+
const p = new Polygon();
|
|
146
|
+
p.xpoints = xs.slice();
|
|
147
|
+
p.ypoints = ys.slice();
|
|
148
|
+
p.npoints = xs.length;
|
|
149
|
+
p.coordinates = xs.map((x, i) => [x, ys[i]]);
|
|
150
|
+
p.closed = true;
|
|
151
|
+
return p;
|
|
152
|
+
}
|
|
153
|
+
/**
|
|
154
|
+
* Split a region into its disjoint parts — one region per {@link MultiPolygon}
|
|
155
|
+
* part. A single connected {@link Polygon}/{@link Rectangle} has nothing to
|
|
156
|
+
* split and is returned unchanged (as a one-element list).
|
|
157
|
+
*/
|
|
158
|
+
ungroup(region) {
|
|
159
|
+
const b = region.bounds;
|
|
160
|
+
if (b instanceof MultiPolygon) {
|
|
161
|
+
return b.polygons
|
|
162
|
+
.filter((p) => p.xpoints.length >= 3)
|
|
163
|
+
.map((p) => this.makeRegion(this.clonePolygon(p), region));
|
|
164
|
+
}
|
|
165
|
+
return [region];
|
|
166
|
+
}
|
|
167
|
+
/** True when ungrouping `region` would actually split it (≥2 parts). */
|
|
168
|
+
canUngroup(region) {
|
|
169
|
+
return region.bounds instanceof MultiPolygon && region.bounds.polygons.length > 1;
|
|
170
|
+
}
|
|
171
|
+
/**
|
|
172
|
+
* Simplify a region's outline(s) with Douglas–Peucker — drop vertices within
|
|
173
|
+
* `altitudePx` pixels of the line between their kept neighbours. Applies to
|
|
174
|
+
* the exterior and every hole, of a polygon or each part of a MultiPolygon; a
|
|
175
|
+
* ring that would degenerate below a triangle is left at its previous detail
|
|
176
|
+
* (the part/hole is kept, not dropped). Rectangles and open polylines are
|
|
177
|
+
* returned unchanged. Result is always a straight-edged polygon (any Bézier
|
|
178
|
+
* smoothing is dropped, since the anchor set changes).
|
|
179
|
+
*/
|
|
180
|
+
simplify(region, altitudePx) {
|
|
181
|
+
const b = region.bounds;
|
|
182
|
+
if (!(altitudePx > 0))
|
|
183
|
+
return region;
|
|
184
|
+
if (b instanceof Polygon && b.closed !== false) {
|
|
185
|
+
return this.makeRegion(this.simplifyPolygon(b, altitudePx), region);
|
|
186
|
+
}
|
|
187
|
+
if (b instanceof MultiPolygon) {
|
|
188
|
+
const mp = new MultiPolygon();
|
|
189
|
+
mp.polygons = b.polygons.map((p) => this.simplifyPolygon(p, altitudePx));
|
|
190
|
+
return this.makeRegion(mp, region);
|
|
191
|
+
}
|
|
192
|
+
return region;
|
|
193
|
+
}
|
|
194
|
+
/** Douglas–Peucker the exterior + each hole of one polygon. A ring that
|
|
195
|
+
* collapses below 3 vertices keeps its original points (never degenerates). */
|
|
196
|
+
simplifyPolygon(p, eps) {
|
|
197
|
+
const keepOrSrc = (xs, ys) => {
|
|
198
|
+
const s = simplifyRing(xs, ys, eps);
|
|
199
|
+
return s.xs.length >= 3 ? s : { xs: xs.slice(), ys: ys.slice() };
|
|
200
|
+
};
|
|
201
|
+
const ext = keepOrSrc(p.xpoints, p.ypoints);
|
|
202
|
+
const poly = new Polygon();
|
|
203
|
+
poly.npoints = ext.xs.length;
|
|
204
|
+
poly.xpoints = ext.xs;
|
|
205
|
+
poly.ypoints = ext.ys;
|
|
206
|
+
poly.coordinates = ext.xs.map((x, i) => [x, ext.ys[i]]);
|
|
207
|
+
poly.closed = true;
|
|
208
|
+
if (p.holes) {
|
|
209
|
+
const holes = p.holes.map((ring) => {
|
|
210
|
+
const s = keepOrSrc(ring.map((q) => q[0]), ring.map((q) => q[1]));
|
|
211
|
+
return s.xs.map((x, i) => [x, s.ys[i]]);
|
|
212
|
+
});
|
|
213
|
+
if (holes.length)
|
|
214
|
+
poly.holes = holes;
|
|
215
|
+
}
|
|
216
|
+
return poly;
|
|
217
|
+
}
|
|
218
|
+
// ── internals ──────────────────────────────────────────────────────────
|
|
219
|
+
/** Union of every region's filled mask (exterior minus holes), or null.
|
|
220
|
+
* `scale` (≤ 1) downscales the raster for large selections; coordinates and
|
|
221
|
+
* the clip bounds are scaled to match. */
|
|
222
|
+
unionMask(regions, W, H, scale = 1) {
|
|
223
|
+
let acc = null;
|
|
224
|
+
for (const region of regions || []) {
|
|
225
|
+
const m = this.rasterizeRegion(region, W, H, scale);
|
|
226
|
+
if (!m)
|
|
227
|
+
continue;
|
|
228
|
+
acc = acc ? unionMasks(acc, m) : m;
|
|
229
|
+
}
|
|
230
|
+
return acc;
|
|
231
|
+
}
|
|
232
|
+
/** Rasterise any region (rect / closed polygon / multi-polygon, holes punched
|
|
233
|
+
* out) into a bbox mask. Null for open polylines or degenerate geometry.
|
|
234
|
+
* `scale` (≤ 1) shrinks the coordinates and clip bounds for a downscaled raster. */
|
|
235
|
+
rasterizeRegion(region, W, H, scale = 1) {
|
|
236
|
+
const sw = scale === 1 ? W : Math.max(1, Math.round(W * scale));
|
|
237
|
+
const sh = scale === 1 ? H : Math.max(1, Math.round(H * scale));
|
|
238
|
+
const sc = (arr) => (scale === 1 ? arr : arr.map((v) => v * scale));
|
|
239
|
+
const scHoles = (holes) => !holes || scale === 1 ? holes : holes.map((ring) => ring.map(([x, y]) => [x * scale, y * scale]));
|
|
240
|
+
const b = region?.bounds;
|
|
241
|
+
if (b instanceof Rectangle) {
|
|
242
|
+
const xs = [b.x, b.x + b.width, b.x + b.width, b.x];
|
|
243
|
+
const ys = [b.y, b.y, b.y + b.height, b.y + b.height];
|
|
244
|
+
return this.wand.rasterizePolygon(sc(xs), sc(ys), sw, sh);
|
|
245
|
+
}
|
|
246
|
+
if (b instanceof Polygon) {
|
|
247
|
+
if (b.closed === false || b.xpoints.length < 3)
|
|
248
|
+
return null;
|
|
249
|
+
return this.wand.rasterizePolygon(sc(b.xpoints), sc(b.ypoints), sw, sh, scHoles(b.holes));
|
|
250
|
+
}
|
|
251
|
+
if (b instanceof MultiPolygon) {
|
|
252
|
+
let acc = null;
|
|
253
|
+
for (const part of b.polygons) {
|
|
254
|
+
if (part.xpoints.length < 3)
|
|
255
|
+
continue;
|
|
256
|
+
const m = this.wand.rasterizePolygon(sc(part.xpoints), sc(part.ypoints), sw, sh, scHoles(part.holes));
|
|
257
|
+
if (m)
|
|
258
|
+
acc = acc ? unionMasks(acc, m) : m;
|
|
259
|
+
}
|
|
260
|
+
return acc;
|
|
261
|
+
}
|
|
262
|
+
return null;
|
|
263
|
+
}
|
|
264
|
+
/** Wrap traced parts as one region (Polygon if single, else MultiPolygon),
|
|
265
|
+
* inheriting colour/label from `proto`. Null when there are no parts. */
|
|
266
|
+
regionFromParts(polys, proto) {
|
|
267
|
+
if (!polys || polys.length === 0)
|
|
268
|
+
return null;
|
|
269
|
+
if (polys.length === 1)
|
|
270
|
+
return this.makeRegion(polys[0], proto);
|
|
271
|
+
const mp = new MultiPolygon();
|
|
272
|
+
mp.polygons = polys;
|
|
273
|
+
return this.makeRegion(mp, proto);
|
|
274
|
+
}
|
|
275
|
+
makeRegion(bounds, proto) {
|
|
276
|
+
const r = new Region();
|
|
277
|
+
r.bounds = bounds;
|
|
278
|
+
r.color = proto?.color;
|
|
279
|
+
r.label = proto?.label ?? 'Region';
|
|
280
|
+
return r;
|
|
281
|
+
}
|
|
282
|
+
clonePolygon(p) {
|
|
283
|
+
const poly = new Polygon();
|
|
284
|
+
poly.npoints = p.npoints;
|
|
285
|
+
poly.xpoints = p.xpoints.slice();
|
|
286
|
+
poly.ypoints = p.ypoints.slice();
|
|
287
|
+
poly.coordinates = p.coordinates.map((c) => c.slice());
|
|
288
|
+
poly.closed = p.closed;
|
|
289
|
+
poly.bezier = p.bezier;
|
|
290
|
+
if (p.holes)
|
|
291
|
+
poly.holes = p.holes.map((ring) => ring.map((pt) => pt.slice()));
|
|
292
|
+
return poly;
|
|
293
|
+
}
|
|
294
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.3.9", ngImport: i0, type: RegionOpsService, deps: [{ token: i1.WandService }], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
295
|
+
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "17.3.9", ngImport: i0, type: RegionOpsService, providedIn: 'root' }); }
|
|
296
|
+
}
|
|
297
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.9", ngImport: i0, type: RegionOpsService, decorators: [{
|
|
298
|
+
type: Injectable,
|
|
299
|
+
args: [{ providedIn: 'root' }]
|
|
300
|
+
}], ctorParameters: () => [{ type: i1.WandService }] });
|
|
301
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicmVnaW9uLW9wcy5zZXJ2aWNlLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vc3JjL2xpYi9yZWdpb24tb3BzLnNlcnZpY2UudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsT0FBTyxFQUFFLFVBQVUsRUFBRSxNQUFNLGVBQWUsQ0FBQztBQUUzQyxPQUFPLEVBQUUsTUFBTSxFQUFFLFNBQVMsRUFBRSxPQUFPLEVBQUUsWUFBWSxFQUFFLE1BQU0saUJBQWlCLENBQUM7QUFDM0UsT0FBTyxFQUFZLFVBQVUsRUFBRSxZQUFZLEVBQUUsTUFBTSxtQkFBbUIsQ0FBQztBQUN2RSxPQUFPLEVBQUUsV0FBVyxFQUFFLE1BQU0sNkJBQTZCLENBQUM7OztBQUUxRDs7Ozs7Ozs7Ozs7O0dBWUc7QUFFSCxNQUFNLE9BQU8sZ0JBQWdCO0lBQzNCOzs7Ozs7O09BT0c7YUFDcUIsa0JBQWEsR0FBRyxVQUFVLEFBQWIsQ0FBYztJQUVuRCxZQUFvQixJQUFpQjtRQUFqQixTQUFJLEdBQUosSUFBSSxDQUFhO0lBQUcsQ0FBQztJQUV6Qzs7Ozs7T0FLRztJQUNILEtBQUssQ0FBQyxPQUFpQixFQUFFLFVBQWtCLEVBQUUsV0FBbUI7UUFDOUQsMEVBQTBFO1FBQzFFLDZFQUE2RTtRQUM3RSx5RUFBeUU7UUFDekUsMEVBQTBFO1FBQzFFLE1BQU0sS0FBSyxHQUFHLElBQUksQ0FBQyxhQUFhLENBQUMsT0FBTyxFQUFFLFVBQVUsRUFBRSxXQUFXLENBQUMsQ0FBQztRQUNuRSxNQUFNLElBQUksR0FBRyxJQUFJLENBQUMsU0FBUyxDQUFDLE9BQU8sRUFBRSxVQUFVLEVBQUUsV0FBVyxFQUFFLEtBQUssQ0FBQyxDQUFDO1FBQ3JFLElBQUksQ0FBQyxJQUFJO1lBQUUsT0FBTyxJQUFJLENBQUM7UUFDdkIsTUFBTSxFQUFFLEdBQUcsSUFBSSxDQUFDLEdBQUcsQ0FBQyxDQUFDLEVBQUUsSUFBSSxDQUFDLEtBQUssQ0FBQyxVQUFVLEdBQUcsS0FBSyxDQUFDLENB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RyxDQUFDLENBQUMsV0FBVyxDQUFDLEdBQUcsQ0FBQyxDQUFDLENBQUMsRUFBRSxFQUFFLENBQUMsQ0FBQyxDQUFDLEtBQUssRUFBRSxDQUFDLENBQUM7UUFDdkQsSUFBSSxDQUFDLE1BQU0sR0FBRyxDQUFDLENBQUMsTUFBTSxDQUFDO1FBQ3ZCLElBQUksQ0FBQyxNQUFNLEdBQUcsQ0FBQyxDQUFDLE1BQU0sQ0FBQztRQUN2QixJQUFJLENBQUMsQ0FBQyxLQUFLO1lBQUUsSUFBSSxDQUFDLEtBQUssR0FBRyxDQUFDLENBQUMsS0FBSyxDQUFDLEdBQUcsQ0FBQyxDQUFDLElBQUksRUFBRSxFQUFFLENBQUMsSUFBSSxDQUFDLEdBQUcsQ0FBQyxDQUFDLEVBQUUsRUFBRSxFQUFFLENBQUMsRUFBRSxDQUFDLEtBQUssRUFBRSxDQUFDLENBQUMsQ0FBQztRQUM5RSxPQUFPLElBQUksQ0FBQztJQUNkLENBQUM7OEdBcFFVLGdCQUFnQjtrSEFBaEIsZ0JBQWdCLGNBREgsTUFBTTs7MkZBQ25CLGdCQUFnQjtrQkFENUIsVUFBVTttQkFBQyxFQUFFLFVBQVUsRUFBRSxNQUFNLEVBQUUiLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgeyBJbmplY3RhYmxlIH0gZnJvbSAnQGFuZ3VsYXIvY29yZSc7XG5cbmltcG9ydCB7IFJlZ2lvbiwgUmVjdGFuZ2xlLCBQb2x5Z29uLCBNdWx0aVBvbHlnb24gfSBmcm9tICcuL21vZGVscy9yZWdpb24nO1xuaW1wb3J0IHsgQkJveE1hc2ssIHVuaW9uTWFza3MsIHNpbXBsaWZ5UmluZyB9IGZyb20gJy4vbW9kZWxzL2dlb21ldHJ5JztcbmltcG9ydCB7IFdhbmRTZXJ2aWNlIH0gZnJvbSAnLi90b29sYmFyL3dhbmQvd2FuZC5zZXJ2aWNlJztcblxuLyoqXG4gKiBQdXJlIChET00tZnJlZSkgcmVnaW9uIHNldC1vcGVyYXRpb25zIOKAlCBtZXJnZSAvIGludmVyc2UgLyB1bmdyb3VwIChqaXQtdWkjODUpLlxuICpcbiAqIFJhc3Rlci1iYXNlZCwgcmV1c2luZyB0aGUgd2FuZC9icnVzaCBtYXNrIHBpcGVsaW5lXG4gKiAoe0BsaW5rIFdhbmRTZXJ2aWNlLnJhc3Rlcml6ZVBvbHlnb259IOKGkiB7QGxpbmsgV2FuZFNlcnZpY2UubWFza1RvUG9seWdvbnN9KSxcbiAqIHNvIFwic2VsZWN0ICsgbWVyZ2VcIiB5aWVsZHMgdGhlIHNhbWUgZ2VvbWV0cnkgYXMgYnJ1c2hpbmcgcmVnaW9ucyB0b2dldGhlcjpcbiAqIG9uZSBlbmdpbmUsIG9uZSBzZXQgb2YgYmVoYXZpb3VycywgaG9sZXMgKyBtdWx0aS1wYXJ0IHJlc3VsdHMgZm9yIGZyZWUuXG4gKlxuICogRXZlcnkgbWV0aG9kIHRha2VzIHRoZSBjdXJyZW50IHJlZ2lvbnMgcGx1cyB0aGUgaW1hZ2UgZGltZW5zaW9ucyBhbmQgcmV0dXJuc1xuICogbmV3IHtAbGluayBSZWdpb259KHMpOyBpdCBuZXZlciBtdXRhdGVzIHRoZSBzdG9yZS4gQ2FsbGVycyBjb21taXQgdGhlIHJlc3VsdFxuICogdGhyb3VnaCB0aGUgbm9ybWFsIHJlZ2lvbi13cml0ZSBwYXRoIChzbyB1bmRvL3JlZG8gYXBwbGllcykuIENvb3JkaW5hdGVzIGFyZVxuICogaW1hZ2UgcGl4ZWxzIHRocm91Z2hvdXQuXG4gKi9cbkBJbmplY3RhYmxlKHsgcHJvdmlkZWRJbjogJ3Jvb3QnIH0pXG5leHBvcnQgY2xhc3MgUmVnaW9uT3BzU2VydmljZSB7XG4gIC8qKlxuICAgKiBMYXJnZXN0IHJhc3RlciAoaW4gcGl4ZWxzKSBhIHJlZ2lvbiBzZXQtb3Agd2lsbCBhbGxvY2F0ZSBvbiB0aGUgbWFpbiB0aHJlYWQuXG4gICAqIFNlbGVjdGlvbnMgd2hvc2UgY2xpcHBlZCBiYm94IGV4Y2VlZHMgdGhpcyBhcmUgcmFzdGVyaXplZCBhdCBhIHByb3BvcnRpb25hbFxuICAgKiBkb3duc2NhbGUgKHRoZW4gdGhlIHRyYWNlZCBnZW9tZXRyeSBpcyBzY2FsZWQgYmFjayksIHNvIG1lcmdlL2ludmVyc2Ugc3RheVxuICAgKiByZXNwb25zaXZlIG9uIGdpZ2FwaXhlbCBzbGlkZXMuIH4xNiBNUCBrZWVwcyB0aGUgc3luY2hyb25vdXMgY29udG91ciB0cmFjZVxuICAgKiB3ZWxsIHVuZGVyIGEgc2Vjb25kOyB0aGUgZG93bnNjYWxlIG9ubHkgYWZmZWN0cyBib3VuZGFyeSBmaWRlbGl0eSBvZiB2ZXJ5XG4gICAqIGxhcmdlLWV4dGVudCBzZWxlY3Rpb25zIChzbWFsbCBzZWxlY3Rpb25zIGtlZXAgZnVsbCByZXNvbHV0aW9uKS5cbiAgICovXG4gIHByaXZhdGUgc3RhdGljIHJlYWRvbmx5IE1BWF9PUF9QSVhFTFMgPSAxNl8wMDBfMDAwO1xuXG4gIGNvbnN0cnVjdG9yKHByaXZhdGUgd2FuZDogV2FuZFNlcnZpY2UpIHt9XG5cbiAgLyoqXG4gICAqIEdlb21ldHJpYyB1bmlvbiBvZiBgcmVnaW9uc2AgaW50byBhIHNpbmdsZSByZWdpb246IGEge0BsaW5rIFBvbHlnb259IHdoZW5cbiAgICogdGhlIHVuaW9uIGlzIG9uZSBjb25uZWN0ZWQgcGllY2UsIGEge0BsaW5rIE11bHRpUG9seWdvbn0gd2hlbiBpdCBzdGF5c1xuICAgKiBkaXNqb2ludCwgd2l0aCBob2xlcyBwcmVzZXJ2ZWQuIFJldHVybnMgbnVsbCB3aGVuIG5vdGhpbmcgcmFzdGVyaXNlcyAoZS5nLlxuICAgKiBvbmx5IG9wZW4gcG9seWxpbmVzIHdlcmUgc2VsZWN0ZWQpLlxuICAgKi9cbiAgbWVyZ2UocmVnaW9uczogUmVnaW9uW10sIGltYWdlV2lkdGg6IG51bWJlciwgaW1hZ2VIZWlnaHQ6IG51bWJlcik6IFJlZ2lvbiB8IG51bGwge1xuICAgIC8vIFRoZSByYXN0ZXIgaXMgYm91bmRlZCBieSB0aGUgc2VsZWN0aW9uJ3MgYmJveCwgbm90IHRoZSBpbWFnZSDigJQgYnV0IG9uIGFcbiAgICAvLyB3aG9sZS1zbGlkZSBpbWFnZSBhIGxhcmdlLWV4dGVudCBzZWxlY3Rpb24gc3RpbGwgc3BhbnMgYmlsbGlvbnMgb2YgcGl4ZWxzLFxuICAgIC8vIHdoaWNoIGZyZWV6ZXMgKG9yIG92ZXJmbG93cyB0aGUgdHlwZWQtYXJyYXkgbGltaXQpIG9uIHRoZSBtYWluIHRocmVhZC5cbiAgICAvLyBSYXN0ZXJpemUgYXQgYSBjYXBwZWQgcmVzb2x1dGlvbiBhbmQgc2NhbGUgdGhlIHRyYWNlZCBwb2x5Z29ucyBiYWNrIHVwLlxuICAgIGNvbnN0IHNjYWxlID0gdGhpcy5vcFJhc3RlclNjYWxlKHJlZ2lvbnMsIGltYWdlV2lkdGgsIGltYWdlSGVpZ2h0KTtcbiAgICBjb25zdCBtYXNrID0gdGhpcy51bmlvbk1hc2socmVnaW9ucywgaW1hZ2VXaWR0aCwgaW1hZ2VIZWlnaHQsIHNjYWxlKTtcbiAgICBpZiAoIW1hc2spIHJldHVybiBudWxsO1xuICAgIGNvbnN0IHN3ID0gTWF0aC5tYXgoMSwgTWF0aC5yb3VuZChpbWFnZVdpZHRoICogc2NhbGUpKTtcbiAgICBjb25zdCBzaCA9IE1hdGgubWF4KDEsIE1hdGgucm91bmQoaW1hZ2VIZWlnaHQgKiBzY2FsZSkpO1xuICAgIGxldCBwb2x5cyA9IHRoaXMud2FuZC5tYXNrVG9Qb2x5Z29ucyhtYXNrLm1hc2ssIG1hc2suYncsIG1hc2suYmgsIHN3LCBzaCwgbWFzay5ieCwgbWFzay5ieSwgMSwgMSk7XG4gICAgaWYgKHNjYWxlICE9PSAxKSBwb2x5cyA9IHBvbHlzLm1hcCgocCkgPT4gdGhpcy5zY2FsZVBvbHlnb24ocCwgMSAvIHNjYWxlKSk7XG4gICAgcmV0dXJuIHRoaXMucmVnaW9uRnJvbVBhcnRzKHBvbHlzLCByZWdpb25zWzBdKTtcbiAgfVxuXG4gIC8qKiBEb3duc2NhbGUgZmFjdG9yICjiiaQgMSkga2VlcGluZyB0aGUgc2VsZWN0aW9uJ3MgY2xpcHBlZCBiYm94IHdpdGhpblxuICAgKiAge0BsaW5rIE1BWF9PUF9QSVhFTFN9OyAxIHdoZW4gaXQgYWxyZWFkeSBmaXRzLiAqL1xuICBwcml2YXRlIG9wUmFzdGVyU2NhbGUocmVnaW9uczogUmVnaW9uW10sIFc6IG51bWJlciwgSDogbnVtYmVyKTogbnVtYmVyIHtcbiAgICBsZXQgbWluWCA9IEluZmluaXR5LCBtaW5ZID0gSW5maW5pdHksIG1heFggPSAtSW5maW5pdHksIG1heFkgPSAtSW5maW5pdHk7XG4gICAgY29uc3Qgc2NhbiA9ICh4czogbnVtYmVyW10sIHlzOiBudW1iZXJbXSkgPT4ge1xuICAgICAgZm9yIChjb25zdCB4IG9mIHhzKSB7IGlmICh4IDwgbWluWCkgbWluWCA9IHg7IGlmICh4ID4gbWF4WCkgbWF4WCA9IHg7IH1cbiAgICAgIGZvciAoY29uc3QgeSBvZiB5cykgeyBpZiAoeSA8IG1pblkpIG1pblkgPSB5OyBpZiAoeSA+IG1heFkpIG1heFkgPSB5OyB9XG4gICAgfTtcbiAgICBmb3IgKGNvbnN0IHIgb2YgcmVnaW9ucyB8fCBbXSkge1xuICAgICAgY29uc3QgYiA9IHI/LmJvdW5kcztcbiAgICAgIGlmIChiIGluc3RhbmNlb2YgUmVjdGFuZ2xlKSBzY2FuKFtiLngsIGIueCArIGIud2lkdGhdLCBbYi55LCBiLnkgKyBiLmhlaWdodF0pO1xuICAgICAgZWxzZSBpZiAoYiBpbnN0YW5jZW9mIFBvbHlnb24pIHNjYW4oYi54cG9pbnRzLCBiLnlwb2ludHMpO1xuICAgICAgZWxzZSBpZiAoYiBpbnN0YW5jZW9mIE11bHRpUG9seWdvbikgZm9yIChjb25zdCBwIG9mIGIucG9seWdvbnMpIHNjYW4ocC54cG9pbnRzLCBwLnlwb2ludHMpO1xuICAgIH1cbiAgICBpZiAoIU51bWJlci5pc0Zpbml0ZShtaW5YKSkgcmV0dXJuIDE7XG4gICAgY29uc3QgYncgPSBNYXRoLm1pbihXLCBtYXhYKSAtIE1hdGgubWF4KDAsIG1pblgpO1xuICAgIGNvbnN0IGJoID0gTWF0aC5taW4oSCwgbWF4WSkgLSBNYXRoLm1heCgwLCBtaW5ZKTtcbiAgICBjb25zdCBhcmVhID0gYncgKiBiaDtcbiAgICBpZiAoIShhcmVhID4gUmVnaW9uT3BzU2VydmljZS5NQVhfT1BfUElYRUxTKSkgcmV0dXJuIDE7XG4gICAgcmV0dXJuIE1hdGguc3FydChSZWdpb25PcHNTZXJ2aWNlLk1BWF9PUF9QSVhFTFMgLyBhcmVhKTtcbiAgfVxuXG4gIC8qKiBBIGNvcHkgb2YgYHBgIHdpdGggZXZlcnkgY29vcmRpbmF0ZSAoYW5kIGhvbGUgY29vcmRpbmF0ZSkgbXVsdGlwbGllZCBieSBgc2AuICovXG4gIHByaXZhdGUgc2NhbGVQb2x5Z29uKHA6IFBvbHlnb24sIHM6IG51bWJlcik6IFBvbHlnb24ge1xuICAgIGNvbnN0IHBvbHkgPSBuZXcgUG9seWdvbigpO1xuICAgIHBvbHkueHBvaW50cyA9IHAueHBvaW50cy5tYXAoKHgpID0+IHggKiBzKTtcbiAgICBwb2x5Lnlwb2ludHMgPSBwLnlwb2ludHMubWFwKCh5KSA9PiB5ICogcyk7XG4gICAgcG9seS5ucG9pbnRzID0gcG9seS54cG9pbnRzLmxlbmd0aDtcbiAgICBwb2x5LmNsb3NlZCA9IHAuY2xvc2VkO1xuICAgIHBvbHkuY29vcmRpbmF0ZXMgPSBwb2x5Lnhwb2ludHMubWFwKCh4LCBpKSA9PiBbeCwgcG9seS55cG9pbnRzW2ldXSk7XG4gICAgaWYgKHAuaG9sZXMpIHBvbHkuaG9sZXMgPSBwLmhvbGVzLm1hcCgocmluZykgPT4gcmluZy5tYXAoKFt4LCB5XSkgPT4gW3ggKiBzLCB5ICogc10pKTtcbiAgICByZXR1cm4gcG9seTtcbiAgfVxuXG4gIC8qKlxuICAgKiBSZXBsYWNlIGByZWdpb25zYCB3aXRoIHRoZWlyIGludmVyc2UgaW5zaWRlIHRoZSBpbWFnZSByZWN0YW5nbGU6IHRoZSBpbWFnZVxuICAgKiBtaW51cyB0aGUgc2VsZWN0aW9uJ3MgdW5pb24gKHRoZSBmb3JtZXIgcmVnaW9ucyBiZWNvbWUgaG9sZXMpLiBSZXR1cm5zIG51bGxcbiAgICogZm9yIGEgZGVnZW5lcmF0ZSBpbWFnZSBvciBhbiBlbXB0eSBzZWxlY3Rpb24uXG4gICAqXG4gICAqIERvbmUgKnZlY3RvcmlhbGx5KiBzbyBpdCBzY2FsZXMgdG8gZ2lnYXBpeGVsIGltYWdlczogdGhlIHNlbGVjdGlvbiBpc1xuICAgKiB1bmlvbmVkIGZpcnN0IChyYXN0ZXIsIGJvdW5kZWQgYnkgdGhlIFJPSXMnIGV4dGVudCDigJQgbmV2ZXIgdGhlIHdob2xlIGltYWdlKSxcbiAgICogdGhlbiB0aGUgcmVzdWx0IGlzIHRoZSBpbWFnZSByZWN0YW5nbGUgYXMgdGhlIGV4dGVyaW9yIHdpdGggZWFjaCB1bmlvblxuICAgKiBjb21wb25lbnQncyBvdXRsaW5lIHB1bmNoZWQgb3V0IGFzIGEgaG9sZS4gQSBjb21wb25lbnQncyBvd24gaG9sZSAoYSBkb251dClcbiAgICogYmVjb21lcyBhIHNvbGlkIGlzbGFuZCBwYXJ0LiBPbmx5IHRoZSBpbWFnZSdzIGNvcm5lciBjb29yZGluYXRlcyBhcmUgdXNlZCDigJRcbiAgICogbm8gZnVsbC1pbWFnZSBidWZmZXIgaXMgZXZlciBhbGxvY2F0ZWQuXG4gICAqL1xuICBpbnZlcnNlKHJlZ2lvbnM6IFJlZ2lvbltdLCBpbWFnZVdpZHRoOiBudW1iZXIsIGltYWdlSGVpZ2h0OiBudW1iZXIpOiBSZWdpb24gfCBudWxsIHtcbiAgICBpZiAoaW1hZ2VXaWR0aCA8PSAwIHx8IGltYWdlSGVpZ2h0IDw9IDApIHJldHVybiBudWxsO1xuICAgIGNvbnN0IG1lcmdlZCA9IHRoaXMubWVyZ2UocmVnaW9ucywgaW1hZ2VXaWR0aCwgaW1hZ2VIZWlnaHQpO1xuICAgIGlmICghbWVyZ2VkKSByZXR1cm4gbnVsbDtcbiAgICBjb25zdCBjb21wczogUG9seWdvbltdID0gbWVyZ2VkLmJvdW5kcyBpbnN0YW5jZW9mIE11bHRpUG9seWdvblxuICAgICAgPyBtZXJnZWQuYm91bmRzLnBvbHlnb25zXG4gICAgICA6IG1lcmdlZC5ib3VuZHMgaW5zdGFuY2VvZiBQb2x5Z29uID8gW21lcmdlZC5ib3VuZHNdIDogW107XG4gICAgaWYgKGNvbXBzLmxlbmd0aCA9PT0gMCkgcmV0dXJuIG51bGw7XG5cbiAgICAvLyBJbWFnZSByZWN0YW5nbGUgZXh0ZXJpb3Igd2l0aCBldmVyeSBjb21wb25lbnQgb3V0bGluZSBhcyBhIGhvbGUuXG4gICAgY29uc3QgcmVjdCA9IHRoaXMucmluZ1BvbHlnb24oXG4gICAgICBbMCwgaW1hZ2VXaWR0aCwgaW1hZ2VXaWR0aCwgMF0sIFswLCAwLCBpbWFnZUhlaWdodCwgaW1hZ2VIZWlnaHRdKTtcbiAgICByZWN0LmhvbGVzID0gY29tcHMubWFwKChjKSA9PiBjLnhwb2ludHMubWFwKCh4LCBpKSA9PiBbeCwgYy55cG9pbnRzW2ldXSkpO1xuXG4gICAgLy8gRWFjaCBjb21wb25lbnQncyBvd24gaG9sZSAoZG9udXQgaW50ZXJpb3IpIGlzICpvdXRzaWRlKiB0aGUgcmVnaW9uLCBzbyBpdFxuICAgIC8vIHN0YXlzIHNvbGlkIGluIHRoZSBpbnZlcnNlIOKAlCBlbWl0IGl0IGFzIGl0cyBvd24gaXNsYW5kIHBhcnQuXG4gICAgY29uc3QgcGFydHM6IFBvbHlnb25bXSA9IFtyZWN0XTtcbiAgICBmb3IgKGNvbnN0IGMgb2YgY29tcHMpIHtcbiAgICAgIGlmIChjLmhvbGVzKSB7XG4gICAgICAgIGZvciAoY29uc3QgcmluZyBvZiBjLmhvbGVzKSB7XG4gICAgICAgICAgcGFydHMucHVzaCh0aGlzLnJpbmdQb2x5Z29uKHJpbmcubWFwKChwKSA9PiBwWzBdKSwgcmluZy5tYXAoKHApID0+IHBbMV0pKSk7XG4gICAgICAgIH1cbiAgICAgIH1cbiAgICB9XG4gICAgY29uc3QgYm91bmRzID0gcGFydHMubGVuZ3RoID09PSAxID8gcGFydHNbMF0gOiBPYmplY3QuYXNzaWduKG5ldyBNdWx0aVBvbHlnb24oKSwgeyBwb2x5Z29uczogcGFydHMgfSk7XG4gICAgcmV0dXJuIHRoaXMubWFrZVJlZ2lvbihib3VuZHMsIHJlZ2lvbnNbMF0pO1xuICB9XG5cbiAgLyoqIEEgY2xvc2VkIHN0cmFpZ2h0LWVkZ2VkIFBvbHlnb24gZnJvbSBwYXJhbGxlbCBjb29yZCBhcnJheXMuICovXG4gIHByaXZhdGUgcmluZ1BvbHlnb24oeHM6IG51bWJlcltdLCB5czogbnVtYmVyW10pOiBQb2x5Z29uIHtcbiAgICBjb25zdCBwID0gbmV3IFBvbHlnb24oKTtcbiAgICBwLnhwb2ludHMgPSB4cy5zbGljZSgpO1xuICAgIHAueXBvaW50cyA9IHlzLnNsaWNlKCk7XG4gICAgcC5ucG9pbnRzID0geHMubGVuZ3RoO1xuICAgIHAuY29vcmRpbmF0ZXMgPSB4cy5tYXAoKHgsIGkpID0+IFt4LCB5c1tpXV0pO1xuICAgIHAuY2xvc2VkID0gdHJ1ZTtcbiAgICByZXR1cm4gcDtcbiAgfVxuXG4gIC8qKlxuICAgKiBTcGxpdCBhIHJlZ2lvbiBpbnRvIGl0cyBkaXNqb2ludCBwYXJ0cyDigJQgb25lIHJlZ2lvbiBwZXIge0BsaW5rIE11bHRpUG9seWdvbn1cbiAgICogcGFydC4gQSBzaW5nbGUgY29ubmVjdGVkIHtAbGluayBQb2x5Z29ufS97QGxpbmsgUmVjdGFuZ2xlfSBoYXMgbm90aGluZyB0b1xuICAgKiBzcGxpdCBhbmQgaXMgcmV0dXJuZWQgdW5jaGFuZ2VkIChhcyBhIG9uZS1lbGVtZW50IGxpc3QpLlxuICAgKi9cbiAgdW5ncm91cChyZWdpb246IFJlZ2lvbik6IFJlZ2lvbltdIHtcbiAgICBjb25zdCBiID0gcmVnaW9uLmJvdW5kcztcbiAgICBpZiAoYiBpbnN0YW5jZW9mIE11bHRpUG9seWdvbikge1xuICAgICAgcmV0dXJuIGIucG9seWdvbnNcbiAgICAgICAgLmZpbHRlcigocCkgPT4gcC54cG9pbnRzLmxlbmd0aCA+PSAzKVxuICAgICAgICAubWFwKChwKSA9PiB0aGlzLm1ha2VSZWdpb24odGhpcy5jbG9uZVBvbHlnb24ocCksIHJlZ2lvbikpO1xuICAgIH1cbiAgICByZXR1cm4gW3JlZ2lvbl07XG4gIH1cblxuICAvKiogVHJ1ZSB3aGVuIHVuZ3JvdXBpbmcgYHJlZ2lvbmAgd291bGQgYWN0dWFsbHkgc3BsaXQgaXQgKOKJpTIgcGFydHMpLiAqL1xuICBjYW5Vbmdyb3VwKHJlZ2lvbjogUmVnaW9uKTogYm9vbGVhbiB7XG4gICAgcmV0dXJuIHJlZ2lvbi5ib3VuZHMgaW5zdGFuY2VvZiBNdWx0aVBvbHlnb24gJiYgcmVnaW9uLmJvdW5kcy5wb2x5Z29ucy5sZW5ndGggPiAxO1xuICB9XG5cbiAgLyoqXG4gICAqIFNpbXBsaWZ5IGEgcmVnaW9uJ3Mgb3V0bGluZShzKSB3aXRoIERvdWdsYXPigJNQZXVja2VyIOKAlCBkcm9wIHZlcnRpY2VzIHdpdGhpblxuICAgKiBgYWx0aXR1ZGVQeGAgcGl4ZWxzIG9mIHRoZSBsaW5lIGJldHdlZW4gdGhlaXIga2VwdCBuZWlnaGJvdXJzLiBBcHBsaWVzIHRvXG4gICAqIHRoZSBleHRlcmlvciBhbmQgZXZlcnkgaG9sZSwgb2YgYSBwb2x5Z29uIG9yIGVhY2ggcGFydCBvZiBhIE11bHRpUG9seWdvbjsgYVxuICAgKiByaW5nIHRoYXQgd291bGQgZGVnZW5lcmF0ZSBiZWxvdyBhIHRyaWFuZ2xlIGlzIGxlZnQgYXQgaXRzIHByZXZpb3VzIGRldGFpbFxuICAgKiAodGhlIHBhcnQvaG9sZSBpcyBrZXB0LCBub3QgZHJvcHBlZCkuIFJlY3RhbmdsZXMgYW5kIG9wZW4gcG9seWxpbmVzIGFyZVxuICAgKiByZXR1cm5lZCB1bmNoYW5nZWQuIFJlc3VsdCBpcyBhbHdheXMgYSBzdHJhaWdodC1lZGdlZCBwb2x5Z29uIChhbnkgQsOpemllclxuICAgKiBzbW9vdGhpbmcgaXMgZHJvcHBlZCwgc2luY2UgdGhlIGFuY2hvciBzZXQgY2hhbmdlcykuXG4gICAqL1xuICBzaW1wbGlmeShyZWdpb246IFJlZ2lvbiwgYWx0aXR1ZGVQeDogbnVtYmVyKTogUmVnaW9uIHtcbiAgICBjb25zdCBiID0gcmVnaW9uLmJvdW5kcztcbiAgICBpZiAoIShhbHRpdHVkZVB4ID4gMCkpIHJldHVybiByZWdpb247XG4gICAgaWYgKGIgaW5zdGFuY2VvZiBQb2x5Z29uICYmIGIuY2xvc2VkICE9PSBmYWxzZSkge1xuICAgICAgcmV0dXJuIHRoaXMubWFrZVJlZ2lvbih0aGlzLnNpbXBsaWZ5UG9seWdvbihiLCBhbHRpdHVkZVB4KSwgcmVnaW9uKTtcbiAgICB9XG4gICAgaWYgKGIgaW5zdGFuY2VvZiBNdWx0aVBvbHlnb24pIHtcbiAgICAgIGNvbnN0IG1wID0gbmV3IE11bHRpUG9seWdvbigpO1xuICAgICAgbXAucG9seWdvbnMgPSBiLnBvbHlnb25zLm1hcCgocCkgPT4gdGhpcy5zaW1wbGlmeVBvbHlnb24ocCwgYWx0aXR1ZGVQeCkpO1xuICAgICAgcmV0dXJuIHRoaXMubWFrZVJlZ2lvbihtcCwgcmVnaW9uKTtcbiAgICB9XG4gICAgcmV0dXJuIHJlZ2lvbjtcbiAgfVxuXG4gIC8qKiBEb3VnbGFz4oCTUGV1Y2tlciB0aGUgZXh0ZXJpb3IgKyBlYWNoIGhvbGUgb2Ygb25lIHBvbHlnb24uIEEgcmluZyB0aGF0XG4gICAqICBjb2xsYXBzZXMgYmVsb3cgMyB2ZXJ0aWNlcyBrZWVwcyBpdHMgb3JpZ2luYWwgcG9pbnRzIChuZXZlciBkZWdlbmVyYXRlcykuICovXG4gIHByaXZhdGUgc2ltcGxpZnlQb2x5Z29uKHA6IFBvbHlnb24sIGVwczogbnVtYmVyKTogUG9seWdvbiB7XG4gICAgY29uc3Qga2VlcE9yU3JjID0gKHhzOiBudW1iZXJbXSwgeXM6IG51bWJlcltdKTogeyB4czogbnVtYmVyW107IHlzOiBudW1iZXJbXSB9ID0+IHtcbiAgICAgIGNvbnN0IHMgPSBzaW1wbGlmeVJpbmcoeHMsIHlzLCBlcHMpO1xuICAgICAgcmV0dXJuIHMueHMubGVuZ3RoID49IDMgPyBzIDogeyB4czogeHMuc2xpY2UoKSwgeXM6IHlzLnNsaWNlKCkgfTtcbiAgICB9O1xuICAgIGNvbnN0IGV4dCA9IGtlZXBPclNyYyhwLnhwb2ludHMsIHAueXBvaW50cyk7XG4gICAgY29uc3QgcG9seSA9IG5ldyBQb2x5Z29uKCk7XG4gICAgcG9seS5ucG9pbnRzID0gZXh0LnhzLmxlbmd0aDtcbiAgICBwb2x5Lnhwb2ludHMgPSBleHQueHM7XG4gICAgcG9seS55cG9pbnRzID0gZXh0LnlzO1xuICAgIHBvbHkuY29vcmRpbmF0ZXMgPSBleHQueHMubWFwKCh4LCBpKSA9PiBbeCwgZXh0LnlzW2ldXSk7XG4gICAgcG9seS5jbG9zZWQgPSB0cnVlO1xuICAgIGlmIChwLmhvbGVzKSB7XG4gICAgICBjb25zdCBob2xlcyA9IHAuaG9sZXMubWFwKChyaW5nKSA9PiB7XG4gICAgICAgIGNvbnN0IHMgPSBrZWVwT3JTcmMocmluZy5tYXAoKHEpID0+IHFbMF0pLCByaW5nLm1hcCgocSkgPT4gcVsxXSkpO1xuICAgICAgICByZXR1cm4gcy54cy5tYXAoKHgsIGkpID0+IFt4LCBzLnlzW2ldXSk7XG4gICAgICB9KTtcbiAgICAgIGlmIChob2xlcy5sZW5ndGgpIHBvbHkuaG9sZXMgPSBob2xlcztcbiAgICB9XG4gICAgcmV0dXJuIHBvbHk7XG4gIH1cblxuICAvLyDilIDilIAgaW50ZXJuYWxzIOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgFxuXG4gIC8qKiBVbmlvbiBvZiBldmVyeSByZWdpb24ncyBmaWxsZWQgbWFzayAoZXh0ZXJpb3IgbWludXMgaG9sZXMpLCBvciBudWxsLlxuICAgKiAgYHNjYWxlYCAo4omkIDEpIGRvd25zY2FsZXMgdGhlIHJhc3RlciBmb3IgbGFyZ2Ugc2VsZWN0aW9uczsgY29vcmRpbmF0ZXMgYW5kXG4gICAqICB0aGUgY2xpcCBib3VuZHMgYXJlIHNjYWxlZCB0byBtYXRjaC4gKi9cbiAgcHJpdmF0ZSB1bmlvbk1hc2socmVnaW9uczogUmVnaW9uW10sIFc6IG51bWJlciwgSDogbnVtYmVyLCBzY2FsZSA9IDEpOiBCQm94TWFzayB8IG51bGwge1xuICAgIGxldCBhY2M6IEJCb3hNYXNrIHwgbnVsbCA9IG51bGw7XG4gICAgZm9yIChjb25zdCByZWdpb24gb2YgcmVnaW9ucyB8fCBbXSkge1xuICAgICAgY29uc3QgbSA9IHRoaXMucmFzdGVyaXplUmVnaW9uKHJlZ2lvbiwgVywgSCwgc2NhbGUpO1xuICAgICAgaWYgKCFtKSBjb250aW51ZTtcbiAgICAgIGFjYyA9IGFjYyA/IHVuaW9uTWFza3MoYWNjLCBtKSA6IG07XG4gICAgfVxuICAgIHJldHVybiBhY2M7XG4gIH1cblxuICAvKiogUmFzdGVyaXNlIGFueSByZWdpb24gKHJlY3QgLyBjbG9zZWQgcG9seWdvbiAvIG11bHRpLXBvbHlnb24sIGhvbGVzIHB1bmNoZWRcbiAgICogIG91dCkgaW50byBhIGJib3ggbWFzay4gTnVsbCBmb3Igb3BlbiBwb2x5bGluZXMgb3IgZGVnZW5lcmF0ZSBnZW9tZXRyeS5cbiAgICogIGBzY2FsZWAgKOKJpCAxKSBzaHJpbmtzIHRoZSBjb29yZGluYXRlcyBhbmQgY2xpcCBib3VuZHMgZm9yIGEgZG93bnNjYWxlZCByYXN0ZXIuICovXG4gIHByaXZhdGUgcmFzdGVyaXplUmVnaW9uKHJlZ2lvbjogUmVnaW9uLCBXOiBudW1iZXIsIEg6IG51bWJlciwgc2NhbGUgPSAxKTogQkJveE1hc2sgfCBudWxsIHtcbiAgICBjb25zdCBzdyA9IHNjYWxlID09PSAxID8gVyA6IE1hdGgubWF4KDEsIE1hdGgucm91bmQoVyAqIHNjYWxlKSk7XG4gICAgY29uc3Qgc2ggPSBzY2FsZSA9PT0gMSA/IEggOiBNYXRoLm1heCgxLCBNYXRoLnJvdW5kKEggKiBzY2FsZSkpO1xuICAgIGNvbnN0IHNjID0gKGFycjogbnVtYmVyW10pID0+IChzY2FsZSA9PT0gMSA/IGFyciA6IGFyci5tYXAoKHYpID0+IHYgKiBzY2FsZSkpO1xuICAgIGNvbnN0IHNjSG9sZXMgPSAoaG9sZXM/OiBudW1iZXJbXVtdW10pID0+XG4gICAgICAhaG9sZXMgfHwgc2NhbGUgPT09IDEgPyBob2xlcyA6IGhvbGVzLm1hcCgocmluZykgPT4gcmluZy5tYXAoKFt4LCB5XSkgPT4gW3ggKiBzY2FsZSwgeSAqIHNjYWxlXSkpO1xuICAgIGNvbnN0IGIgPSByZWdpb24/LmJvdW5kcztcbiAgICBpZiAoYiBpbnN0YW5jZW9mIFJlY3RhbmdsZSkge1xuICAgICAgY29uc3QgeHMgPSBbYi54LCBiLnggKyBiLndpZHRoLCBiLnggKyBiLndpZHRoLCBiLnhdO1xuICAgICAgY29uc3QgeXMgPSBbYi55LCBiLnksIGIueSArIGIuaGVpZ2h0LCBiLnkgKyBiLmhlaWdodF07XG4gICAgICByZXR1cm4gdGhpcy53YW5kLnJhc3Rlcml6ZVBvbHlnb24oc2MoeHMpLCBzYyh5cyksIHN3LCBzaCk7XG4gICAgfVxuICAgIGlmIChiIGluc3RhbmNlb2YgUG9seWdvbikge1xuICAgICAgaWYgKGIuY2xvc2VkID09PSBmYWxzZSB8fCBiLnhwb2ludHMubGVuZ3RoIDwgMykgcmV0dXJuIG51bGw7XG4gICAgICByZXR1cm4gdGhpcy53YW5kLnJhc3Rlcml6ZVBvbHlnb24oc2MoYi54cG9pbnRzKSwgc2MoYi55cG9pbnRzKSwgc3csIHNoLCBzY0hvbGVzKGIuaG9sZXMpKTtcbiAgICB9XG4gICAgaWYgKGIgaW5zdGFuY2VvZiBNdWx0aVBvbHlnb24pIHtcbiAgICAgIGxldCBhY2M6IEJCb3hNYXNrIHwgbnVsbCA9IG51bGw7XG4gICAgICBmb3IgKGNvbnN0IHBhcnQgb2YgYi5wb2x5Z29ucykge1xuICAgICAgICBpZiAocGFydC54cG9pbnRzLmxlbmd0aCA8IDMpIGNvbnRpbnVlO1xuICAgICAgICBjb25zdCBtID0gdGhpcy53YW5kLnJhc3Rlcml6ZVBvbHlnb24oc2MocGFydC54cG9pbnRzKSwgc2MocGFydC55cG9pbnRzKSwgc3csIHNoLCBzY0hvbGVzKHBhcnQuaG9sZXMpKTtcbiAgICAgICAgaWYgKG0pIGFjYyA9IGFjYyA/IHVuaW9uTWFza3MoYWNjLCBtKSA6IG07XG4gICAgICB9XG4gICAgICByZXR1cm4gYWNjO1xuICAgIH1cbiAgICByZXR1cm4gbnVsbDtcbiAgfVxuXG4gIC8qKiBXcmFwIHRyYWNlZCBwYXJ0cyBhcyBvbmUgcmVnaW9uIChQb2x5Z29uIGlmIHNpbmdsZSwgZWxzZSBNdWx0aVBvbHlnb24pLFxuICAgKiAgaW5oZXJpdGluZyBjb2xvdXIvbGFiZWwgZnJvbSBgcHJvdG9gLiBOdWxsIHdoZW4gdGhlcmUgYXJlIG5vIHBhcnRzLiAqL1xuICBwcml2YXRlIHJlZ2lvbkZyb21QYXJ0cyhwb2x5czogUG9seWdvbltdLCBwcm90bzogUmVnaW9uIHwgdW5kZWZpbmVkKTogUmVnaW9uIHwgbnVsbCB7XG4gICAgaWYgKCFwb2x5cyB8fCBwb2x5cy5sZW5ndGggPT09IDApIHJldHVybiBudWxsO1xuICAgIGlmIChwb2x5cy5sZW5ndGggPT09IDEpIHJldHVybiB0aGlzLm1ha2VSZWdpb24ocG9seXNbMF0sIHByb3RvKTtcbiAgICBjb25zdCBtcCA9IG5ldyBNdWx0aVBvbHlnb24oKTtcbiAgICBtcC5wb2x5Z29ucyA9IHBvbHlzO1xuICAgIHJldHVybiB0aGlzLm1ha2VSZWdpb24obXAsIHByb3RvKTtcbiAgfVxuXG4gIHByaXZhdGUgbWFrZVJlZ2lvbihib3VuZHM6IFBvbHlnb24gfCBNdWx0aVBvbHlnb24sIHByb3RvOiBSZWdpb24gfCB1bmRlZmluZWQpOiBSZWdpb24ge1xuICAgIGNvbnN0IHIgPSBuZXcgUmVnaW9uKCk7XG4gICAgci5ib3VuZHMgPSBib3VuZHM7XG4gICAgci5jb2xvciA9IHByb3RvPy5jb2xvcjtcbiAgICByLmxhYmVsID0gcHJvdG8/LmxhYmVsID8/ICdSZWdpb24nO1xuICAgIHJldHVybiByO1xuICB9XG5cbiAgcHJpdmF0ZSBjbG9uZVBvbHlnb24ocDogUG9seWdvbik6IFBvbHlnb24ge1xuICAgIGNvbnN0IHBvbHkgPSBuZXcgUG9seWdvbigpO1xuICAgIHBvbHkubnBvaW50cyA9IHAubnBvaW50cztcbiAgICBwb2x5Lnhwb2ludHMgPSBwLnhwb2ludHMuc2xpY2UoKTtcbiAgICBwb2x5Lnlwb2ludHMgPSBwLnlwb2ludHMuc2xpY2UoKTtcbiAgICBwb2x5LmNvb3JkaW5hdGVzID0gcC5jb29yZGluYXRlcy5tYXAoKGMpID0+IGMuc2xpY2UoKSk7XG4gICAgcG9seS5jbG9zZWQgPSBwLmNsb3NlZDtcbiAgICBwb2x5LmJlemllciA9IHAuYmV6aWVyO1xuICAgIGlmIChwLmhvbGVzKSBwb2x5LmhvbGVzID0gcC5ob2xlcy5tYXAoKHJpbmcpID0+IHJpbmcubWFwKChwdCkgPT4gcHQuc2xpY2UoKSkpO1xuICAgIHJldHVybiBwb2x5O1xuICB9XG59XG4iXX0=
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
export class RenderOrchestrator {
|
|
2
|
+
constructor(host, retryDelayMs = 1000) {
|
|
3
|
+
this.host = host;
|
|
4
|
+
this.retryDelayMs = retryDelayMs;
|
|
5
|
+
}
|
|
6
|
+
/** Run the render: single-pass when `smallInfo` is null, two-pass otherwise. */
|
|
7
|
+
async render(info, smallInfo) {
|
|
8
|
+
if (!smallInfo) {
|
|
9
|
+
try {
|
|
10
|
+
await this.host.renderPhase(info, false);
|
|
11
|
+
this.host.finished(false, 'finished plotting');
|
|
12
|
+
}
|
|
13
|
+
catch (err) {
|
|
14
|
+
console.error('Preview failed', err);
|
|
15
|
+
this.host.finished(false, 'plotting aborted');
|
|
16
|
+
}
|
|
17
|
+
return;
|
|
18
|
+
}
|
|
19
|
+
let smallReleasedOverlay = false;
|
|
20
|
+
try {
|
|
21
|
+
await this.host.renderPhase(smallInfo, false);
|
|
22
|
+
this.host.smallShown();
|
|
23
|
+
smallReleasedOverlay = true;
|
|
24
|
+
console.log('multi-tier: small tier rendered, starting large');
|
|
25
|
+
}
|
|
26
|
+
catch (err) {
|
|
27
|
+
// Small-tier render failed (older backend without tier support, transient
|
|
28
|
+
// error, …). Continue with large only; the overlay stays up until it lands.
|
|
29
|
+
console.warn('Small-tier preview failed, falling back to large', err);
|
|
30
|
+
}
|
|
31
|
+
try {
|
|
32
|
+
await this.renderLargeWithRetry(info);
|
|
33
|
+
this.host.sharpenSettled();
|
|
34
|
+
if (smallReleasedOverlay) {
|
|
35
|
+
this.host.finished(true, 'multi-tier: large tier rendered, sharpening complete');
|
|
36
|
+
}
|
|
37
|
+
else {
|
|
38
|
+
this.host.finished(false, 'finished plotting (large only after small fallback)');
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
catch (err) {
|
|
42
|
+
this.host.sharpenSettled();
|
|
43
|
+
console.error('Large-tier preview failed after retry', err);
|
|
44
|
+
this.host.sharpenFailed(err);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
/** One retry after a brief delay before giving up on the large tier. */
|
|
48
|
+
renderLargeWithRetry(info) {
|
|
49
|
+
return this.host.renderPhase(info, true).catch((err) => {
|
|
50
|
+
console.warn('Large-tier preview failed on first try, retrying in 1s', err);
|
|
51
|
+
return new Promise((resolve) => setTimeout(resolve, this.retryDelayMs)).then(() => this.host.renderPhase(info, true));
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Debounced z-slice scrubbing (extracted with the orchestrator): coalesce the
|
|
57
|
+
* rapid changes a slider drag fires so a slice swap doesn't run on every
|
|
58
|
+
* pixel; the final value lands immediately via {@link commit} (onSlideEnd).
|
|
59
|
+
*/
|
|
60
|
+
export class SliceScrubber {
|
|
61
|
+
constructor(apply, delayMs = 120) {
|
|
62
|
+
this.apply = apply;
|
|
63
|
+
this.delayMs = delayMs;
|
|
64
|
+
this.timer = null;
|
|
65
|
+
}
|
|
66
|
+
/** Debounced application of z while dragging. */
|
|
67
|
+
scrub(z) {
|
|
68
|
+
this.cancel();
|
|
69
|
+
this.timer = setTimeout(() => {
|
|
70
|
+
this.timer = null;
|
|
71
|
+
this.apply(z);
|
|
72
|
+
}, this.delayMs);
|
|
73
|
+
}
|
|
74
|
+
/** Apply z immediately (slide end / keyboard step), dropping any pending scrub. */
|
|
75
|
+
commit(z) {
|
|
76
|
+
this.cancel();
|
|
77
|
+
this.apply(z);
|
|
78
|
+
}
|
|
79
|
+
/** Drop a pending scrub without applying (teardown). */
|
|
80
|
+
cancel() {
|
|
81
|
+
if (this.timer) {
|
|
82
|
+
clearTimeout(this.timer);
|
|
83
|
+
this.timer = null;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicmVuZGVyLW9yY2hlc3RyYXRvci5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uL3NyYy9saWIvcmVuZGVyLW9yY2hlc3RyYXRvci50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFtQ0EsTUFBTSxPQUFPLGtCQUFrQjtJQUM3QixZQUNVLElBQXVCLEVBQ3ZCLGVBQWUsSUFBSTtRQURuQixTQUFJLEdBQUosSUFBSSxDQUFtQjtRQUN2QixpQkFBWSxHQUFaLFlBQVksQ0FBTztJQUMxQixDQUFDO0lBRUosZ0ZBQWdGO0lBQ2hGLEtBQUssQ0FBQyxNQUFNLENBQUMsSUFBZ0IsRUFBRSxTQUE0QjtRQUN6RCxJQUFJLENBQUMsU0FBUyxFQUFFLENBQUM7WUFDZixJQUFJLENBQUM7Z0JBQ0gsTUFBTSxJQUFJLENBQUMsSUFBSSxDQUFDLFdBQVcsQ0FBQyxJQUFJLEVBQUUsS0FBSyxDQUFDLENBQUM7Z0JBQ3pDLElBQUksQ0FBQyxJQUFJLENBQUMsUUFBUSxDQUFDLEtBQUssRUFBRSxtQkFBbUIsQ0FBQyxDQUFDO1lBQ2pELENBQUM7WUFBQyxPQUFPLEdBQUcsRUFBRSxDQUFDO2dCQUNiLE9BQU8sQ0FBQyxLQUFLLENBQUMsZ0JBQWdCLEVBQUUsR0FBRyxDQUFDLENBQUM7Z0JBQ3JDLElBQUksQ0FBQyxJQUFJLENBQUMsUUFBUSxDQUFDLEtBQUssRUFBRSxrQkFBa0IsQ0FBQyxDQUFDO1lBQ2hELENBQUM7WUFDRCxPQUFPO1FBQ1QsQ0FBQztRQUVELElBQUksb0JBQW9CLEdBQUcsS0FBSyxDQUFDO1FBQ2pDLElBQUksQ0FBQztZQUNILE1BQU0sSUFBSSxDQUFDLElBQUksQ0FBQyxXQUFXLENBQUMsU0FBUyxFQUFFLEtBQUssQ0FBQyxDQUFDO1lBQzlDLElBQUksQ0FBQyxJQUFJLENBQUMsVUFBVSxFQUFFLENBQUM7WUFDdkIsb0JBQW9CLEdBQUcsSUFBSSxDQUFDO1lBQzVCLE9BQU8sQ0FBQyxHQUFHLENBQUMsaURBQWlELENBQUMsQ0FBQztRQUNqRSxDQUFDO1FBQUMsT0FBTyxHQUFHLEVBQUUsQ0FBQztZQUNiLDBFQUEwRTtZQUMxRSw0RUFBNEU7WUFDNUUsT0FBTyxDQUFDLElBQUksQ0FBQyxrREFBa0QsRUFBRSxHQUFHLENBQUMsQ0FBQztRQUN4RSxDQUFDO1FBRUQsSUFBSSxDQUFDO1lBQ0gsTUFBTSxJQUFJLENBQUMsb0JBQW9CLENBQUMsSUFBSSxDQUFDLENBQUM7WUFDdEMsSUFBSSxDQUFDLElBQUksQ0FBQyxjQUFjLEVBQUUsQ0FBQztZQUMzQixJQUFJLG9CQUFvQixFQUFFLENBQUM7Z0JBQ3pCLElBQUksQ0FBQyxJQUFJLENBQUMsUUFBUSxDQUFDLElBQUksRUFBRSxzREFBc0QsQ0FBQyxDQUFDO1lBQ25GLENBQUM7aUJBQU0sQ0FBQztnQkFDTixJQUFJLENBQUMsSUFBSSxDQUFDLFFBQVEsQ0FBQyxLQUFLLEVBQUUscURBQXFELENBQUMsQ0FBQztZQUNuRixDQUFDO1FBQ0gsQ0FBQztRQUFDLE9BQU8sR0FBRyxFQUFFLENBQUM7WUFDYixJQUFJLENBQUMsSUFBSSxDQUFDLGNBQWMsRUFBRSxDQUFDO1lBQzNCLE9BQU8sQ0FBQyxLQUFLLENBQUMsdUNBQXVDLEVBQUUsR0FBRyxDQUFDLENBQUM7WUFDNUQsSUFBSSxDQUFDLElBQUksQ0FBQyxhQUFhLENBQUMsR0FBRyxDQUFDLENBQUM7UUFDL0IsQ0FBQztJQUNILENBQUM7SUFFRCx3RUFBd0U7SUFDaEUsb0JBQW9CLENBQUMsSUFBZ0I7UUFDM0MsT0FBTyxJQUFJLENBQUMsSUFBSSxDQUFDLFdBQVcsQ0FBQyxJQUFJLEVBQUUsSUFBSSxDQUFDLENBQUMsS0FBSyxDQUFDLENBQUMsR0FBRyxFQUFFLEVBQUU7WUFDckQsT0FBTyxDQUFDLElBQUksQ0FBQyx3REFBd0QsRUFBRSxHQUFHLENBQUMsQ0FBQztZQUM1RSxPQUFPLElBQUksT0FBTyxDQUFPLENBQUMsT0FBTyxFQUFFLEVBQUUsQ0FBQyxVQUFVLENBQUMsT0FBTyxFQUFFLElBQUksQ0FBQyxZQUFZLENBQUMsQ0FBQyxDQUFDLElBQUksQ0FBQyxHQUFHLEVBQUUsQ0FDdEYsSUFBSSxDQUFDLElBQUksQ0FBQyxXQUFXLENBQUMsSUFBSSxFQUFFLElBQUksQ0FBQyxDQUNsQyxDQUFDO1FBQ0osQ0FBQyxDQUFDLENBQUM7SUFDTCxDQUFDO0NBQ0Y7QUFFRDs7OztHQUlHO0FBQ0gsTUFBTSxPQUFPLGFBQWE7SUFHeEIsWUFDVSxLQUEwQixFQUMxQixVQUFVLEdBQUc7UUFEYixVQUFLLEdBQUwsS0FBSyxDQUFxQjtRQUMxQixZQUFPLEdBQVAsT0FBTyxDQUFNO1FBSmYsVUFBSyxHQUF5QyxJQUFJLENBQUM7SUFLeEQsQ0FBQztJQUVKLGlEQUFpRDtJQUNqRCxLQUFLLENBQUMsQ0FBUztRQUNiLElBQUksQ0FBQyxNQUFNLEVBQUUsQ0FBQztRQUNkLElBQUksQ0FBQyxLQUFLLEdBQUcsVUFBVSxDQUFDLEdBQUcsRUFBRTtZQUMzQixJQUFJLENBQUMsS0FBSyxHQUFHLElBQUksQ0FBQztZQUNsQixJQUFJLENBQUMsS0FBSyxDQUFDLENBQUMsQ0FBQyxDQUFDO1FBQ2hCLENBQUMsRUFBRSxJQUFJLENBQUMsT0FBTyxDQUFDLENBQUM7SUFDbkIsQ0FBQztJQUVELG1GQUFtRjtJQUNuRixNQUFNLENBQUMsQ0FBUztRQUNkLElBQUksQ0FBQyxNQUFNLEVBQUUsQ0FBQztRQUNkLElBQUksQ0FBQyxLQUFLLENBQUMsQ0FBQyxDQUFDLENBQUM7SUFDaEIsQ0FBQztJQUVELHdEQUF3RDtJQUN4RCxNQUFNO1FBQ0osSUFBSSxJQUFJLENBQUMsS0FBSyxFQUFFLENBQUM7WUFDZixZQUFZLENBQUMsSUFBSSxDQUFDLEtBQUssQ0FBQyxDQUFDO1lBQ3pCLElBQUksQ0FBQyxLQUFLLEdBQUcsSUFBSSxDQUFDO1FBQ3BCLENBQUM7SUFDSCxDQUFDO0NBQ0YiLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgeyBJSW1hZ2VJbmZvIH0gZnJvbSAnLi9jb250cmFjdHMvaW1hZ2UuY29udHJhY3QnO1xuXG4vKipcbiAqIFR3by1wYXNzIChzbWFsbOKGkmxhcmdlKSByZW5kZXIgc2VxdWVuY2luZywgZXh0cmFjdGVkIGZyb21cbiAqIFZpc3VhbGl6ZXJDb21wb25lbnQgKHJlZmFjdG9yaW5nIHBsYW4sIFN0ZXAgNyDigJQgYSBwdXJlIG1vdmUgb2YgdGhlXG4gKiBvcmNoZXN0cmF0aW9uOyB0aGUgY29tcG9uZW50IGtlZXBzIHRoZSBVSSBmbGFncyBhbmQgc3VwcGxpZXMgdGhlbSB0aHJvdWdoXG4gKiB0aGUgaG9zdCBjYWxsYmFja3MpLlxuICpcbiAqIFdoZW4gdGhlIGZpbGUgaGFzIHNtYWxsLXRpZXIgVVJMcyB3ZSByZW5kZXIgdGhlIHNtYWxsIHRpZXIgZmlyc3QgZm9yIGEgZmFzdFxuICogYmx1cnJ5IHByZXZpZXcsIHJlbGVhc2UgdGhlIGxvYWRpbmcgb3ZlcmxheSAoa2VlcGluZyBhIHRyYW5zbHVjZW50XG4gKiBcInNoYXJwZW5pbmdcIiBzcGlubmVyKSwgdGhlbiBzaGFycGVuIGJ5IHJlLXJlbmRlcmluZyB0aGUgY2Fub25pY2FsIGxhcmdlIHRpZXJcbiAqIGluIHBsYWNlLiBXaXRob3V0IGEgc21hbGwgdGllciBpdCdzIHRoZSBvcmlnaW5hbCBzaW5nbGUgcGFzcy4gVGhlIGxhcmdlIHBhc3NcbiAqIHJldHJpZXMgb25jZSBhZnRlciBhIHNob3J0IGRlbGF5IOKAlCB0aGUgcHJldmlldyBlbmRwb2ludCBjYW4gaGl0IGEgdHJhbnNpZW50XG4gKiA1MDMgb3IgYSBsb2NrLWNvbnRlbnRpb24gd2luZG93LCBhbmQgdGhlIHNlY29uZCBhdHRlbXB0IHVzdWFsbHkgbGFuZHMgb25cbiAqIGZyZXNoIHByZS1nZW4tY2FjaGVkIFBOR3MuXG4gKi9cbmV4cG9ydCBpbnRlcmZhY2UgVHdvUGFzc1JlbmRlckhvc3Qge1xuICAvKiogTG9hZCArIHBsb3Qgb25lIHBoYXNlICh0aGUgY29tcG9uZW50IG93bnMgZGl2L3NjcmVlbi9wbG90LXR5cGUveiBhbmQgdGhlXG4gICAqICBuZXdlci1jbGljayBmaWxlbmFtZSBndWFyZCkuIGBpblBsYWNlYCB1cGRhdGVzIHRoZSBleGlzdGluZyByZW5kZXIgc28gdGhlXG4gICAqICBjYW52YXMgZG9lc24ndCBibGFuayBkdXJpbmcgdGhlIHNtYWxs4oaSbGFyZ2Ugc3dhcC4gKi9cbiAgcmVuZGVyUGhhc2UoaW5mbzogSUltYWdlSW5mbywgaW5QbGFjZTogYm9vbGVhbik6IFByb21pc2U8dW5rbm93bj47XG4gIC8qKiBUaGUgc21hbGwgdGllciBpcyBvbiBzY3JlZW4g4oCUIGRyb3AgdGhlIGZ1bGwgbG9hZGluZyBvdmVybGF5IHNvIHRoZSB1c2VyXG4gICAqICBzZWVzIHRoZSBibHVycnkgcHJldmlldywgYW5kIHNob3cgdGhlIHNoYXJwZW5pbmcgc3Bpbm5lci4gKi9cbiAgc21hbGxTaG93bigpOiB2b2lkO1xuICAvKiogVGhlIGxhcmdlIHJlbmRlciBzZXR0bGVkIChlaXRoZXIgd2F5KSDigJQgc2hhcnBlbmluZyBzcGlubmVyIG9mZi4gKi9cbiAgc2hhcnBlblNldHRsZWQoKTogdm9pZDtcbiAgLyoqIFJlbmRlcmluZyBmaW5pc2hlZC4gYHZpYVNtYWxsYCA9IHRoZSBvdmVybGF5IHdhcyBhbHJlYWR5IHJlbGVhc2VkIGJ5IHRoZVxuICAgKiAgc21hbGwgdGllciAoc28gb25seSByZWxlYXNlIHRoZSBydW5uaW5nIGd1YXJkICsgYXBwbHkgdGhlIFJPSSk7IG90aGVyd2lzZVxuICAgKiAgZG8gdGhlIGZ1bGwgZmluYWxpemUgKG92ZXJsYXkgKyBydW5uaW5nICsgUk9JKS4gKi9cbiAgZmluaXNoZWQodmlhU21hbGw6IGJvb2xlYW4sIGxvZ1RhZzogc3RyaW5nKTogdm9pZDtcbiAgLyoqIEJvdGggbGFyZ2UtdGllciBhdHRlbXB0cyBmYWlsZWQ7IHRoZSBzbWFsbCB0aWVyIHN0YXlzIG9uIHNjcmVlbiBhcyB0aGVcbiAgICogIGZhbGxiYWNrLiBTdXJmYWNlIGl0IHRvIHRoZSB1c2VyIGFuZCByZWxlYXNlIHRoZSBydW5uaW5nIGd1YXJkICsgUk9JLiAqL1xuICBzaGFycGVuRmFpbGVkKGVycjogdW5rbm93bik6IHZvaWQ7XG59XG5cbmV4cG9ydCBjbGFzcyBSZW5kZXJPcmNoZXN0cmF0b3Ige1xuICBjb25zdHJ1Y3RvcihcbiAgICBwcml2YXRlIGhvc3Q6IFR3b1Bhc3NSZW5kZXJIb3N0LFxuICAgIHByaXZhdGUgcmV0cnlEZWxheU1zID0gMTAwMCxcbiAgKSB7fVxuXG4gIC8qKiBSdW4gdGhlIHJlbmRlcjogc2luZ2xlLXBhc3Mgd2hlbiBgc21hbGxJbmZvYCBpcyBudWxsLCB0d28tcGFzcyBvdGhlcndpc2UuICovXG4gIGFzeW5jIHJlbmRlcihpbmZvOiBJSW1hZ2VJbmZvLCBzbWFsbEluZm86IElJbWFnZUluZm8gfCBudWxsKTogUHJvbWlzZTx2b2lkPiB7XG4gICAgaWYgKCFzbWFsbEluZm8pIHtcbiAgICAgIHRyeSB7XG4gICAgICAgIGF3YWl0IHRoaXMuaG9zdC5yZW5kZXJQaGFzZShpbmZvLCBmYWxzZSk7XG4gICAgICAgIHRoaXMuaG9zdC5maW5pc2hlZChmYWxzZSwgJ2ZpbmlzaGVkIHBsb3R0aW5nJyk7XG4gICAgICB9IGNhdGNoIChlcnIpIHtcbiAgICAgICAgY29uc29sZS5lcnJvcignUHJldmlldyBmYWlsZWQnLCBlcnIpO1xuICAgICAgICB0aGlzLmhvc3QuZmluaXNoZWQoZmFsc2UsICdwbG90dGluZyBhYm9ydGVkJyk7XG4gICAgICB9XG4gICAgICByZXR1cm47XG4gICAgfVxuXG4gICAgbGV0IHNtYWxsUmVsZWFzZWRPdmVybGF5ID0gZmFsc2U7XG4gICAgdHJ5IHtcbiAgICAgIGF3YWl0IHRoaXMuaG9zdC5yZW5kZXJQaGFzZShzbWFsbEluZm8sIGZhbHNlKTtcbiAgICAgIHRoaXMuaG9zdC5zbWFsbFNob3duKCk7XG4gICAgICBzbWFsbFJlbGVhc2VkT3ZlcmxheSA9IHRydWU7XG4gICAgICBjb25zb2xlLmxvZygnbXVsdGktdGllcjogc21hbGwgdGllciByZW5kZXJlZCwgc3RhcnRpbmcgbGFyZ2UnKTtcbiAgICB9IGNhdGNoIChlcnIpIHtcbiAgICAgIC8vIFNtYWxsLXRpZXIgcmVuZGVyIGZhaWxlZCAob2xkZXIgYmFja2VuZCB3aXRob3V0IHRpZXIgc3VwcG9ydCwgdHJhbnNpZW50XG4gICAgICAvLyBlcnJvciwg4oCmKS4gQ29udGludWUgd2l0aCBsYXJnZSBvbmx5OyB0aGUgb3ZlcmxheSBzdGF5cyB1cCB1bnRpbCBpdCBsYW5kcy5cbiAgICAgIGNvbnNvbGUud2FybignU21hbGwtdGllciBwcmV2aWV3IGZhaWxlZCwgZmFsbGluZyBiYWNrIHRvIGxhcmdlJywgZXJyKTtcbiAgICB9XG5cbiAgICB0cnkge1xuICAgICAgYXdhaXQgdGhpcy5yZW5kZXJMYXJnZVdpdGhSZXRyeShpbmZvKTtcbiAgICAgIHRoaXMuaG9zdC5zaGFycGVuU2V0dGxlZCgpO1xuICAgICAgaWYgKHNtYWxsUmVsZWFzZWRPdmVybGF5KSB7XG4gICAgICAgIHRoaXMuaG9zdC5maW5pc2hlZCh0cnVlLCAnbXVsdGktdGllcjogbGFyZ2UgdGllciByZW5kZXJlZCwgc2hhcnBlbmluZyBjb21wbGV0ZScpO1xuICAgICAgfSBlbHNlIHtcbiAgICAgICAgdGhpcy5ob3N0LmZpbmlzaGVkKGZhbHNlLCAnZmluaXNoZWQgcGxvdHRpbmcgKGxhcmdlIG9ubHkgYWZ0ZXIgc21hbGwgZmFsbGJhY2spJyk7XG4gICAgICB9XG4gICAgfSBjYXRjaCAoZXJyKSB7XG4gICAgICB0aGlzLmhvc3Quc2hhcnBlblNldHRsZWQoKTtcbiAgICAgIGNvbnNvbGUuZXJyb3IoJ0xhcmdlLXRpZXIgcHJldmlldyBmYWlsZWQgYWZ0ZXIgcmV0cnknLCBlcnIpO1xuICAgICAgdGhpcy5ob3N0LnNoYXJwZW5GYWlsZWQoZXJyKTtcbiAgICB9XG4gIH1cblxuICAvKiogT25lIHJldHJ5IGFmdGVyIGEgYnJpZWYgZGVsYXkgYmVmb3JlIGdpdmluZyB1cCBvbiB0aGUgbGFyZ2UgdGllci4gKi9cbiAgcHJpdmF0ZSByZW5kZXJMYXJnZVdpdGhSZXRyeShpbmZvOiBJSW1hZ2VJbmZvKTogUHJvbWlzZTx1bmtub3duPiB7XG4gICAgcmV0dXJuIHRoaXMuaG9zdC5yZW5kZXJQaGFzZShpbmZvLCB0cnVlKS5jYXRjaCgoZXJyKSA9PiB7XG4gICAgICBjb25zb2xlLndhcm4oJ0xhcmdlLXRpZXIgcHJldmlldyBmYWlsZWQgb24gZmlyc3QgdHJ5LCByZXRyeWluZyBpbiAxcycsIGVycik7XG4gICAgICByZXR1cm4gbmV3IFByb21pc2U8dm9pZD4oKHJlc29sdmUpID0+IHNldFRpbWVvdXQocmVzb2x2ZSwgdGhpcy5yZXRyeURlbGF5TXMpKS50aGVuKCgpID0+XG4gICAgICAgIHRoaXMuaG9zdC5yZW5kZXJQaGFzZShpbmZvLCB0cnVlKSxcbiAgICAgICk7XG4gICAgfSk7XG4gIH1cbn1cblxuLyoqXG4gKiBEZWJvdW5jZWQgei1zbGljZSBzY3J1YmJpbmcgKGV4dHJhY3RlZCB3aXRoIHRoZSBvcmNoZXN0cmF0b3IpOiBjb2FsZXNjZSB0aGVcbiAqIHJhcGlkIGNoYW5nZXMgYSBzbGlkZXIgZHJhZyBmaXJlcyBzbyBhIHNsaWNlIHN3YXAgZG9lc24ndCBydW4gb24gZXZlcnlcbiAqIHBpeGVsOyB0aGUgZmluYWwgdmFsdWUgbGFuZHMgaW1tZWRpYXRlbHkgdmlhIHtAbGluayBjb21taXR9IChvblNsaWRlRW5kKS5cbiAqL1xuZXhwb3J0IGNsYXNzIFNsaWNlU2NydWJiZXIge1xuICBwcml2YXRlIHRpbWVyOiBSZXR1cm5UeXBlPHR5cGVvZiBzZXRUaW1lb3V0PiB8IG51bGwgPSBudWxsO1xuXG4gIGNvbnN0cnVjdG9yKFxuICAgIHByaXZhdGUgYXBwbHk6ICh6OiBudW1iZXIpID0+IHZvaWQsXG4gICAgcHJpdmF0ZSBkZWxheU1zID0gMTIwLFxuICApIHt9XG5cbiAgLyoqIERlYm91bmNlZCBhcHBsaWNhdGlvbiBvZiB6IHdoaWxlIGRyYWdnaW5nLiAqL1xuICBzY3J1Yih6OiBudW1iZXIpOiB2b2lkIHtcbiAgICB0aGlzLmNhbmNlbCgpO1xuICAgIHRoaXMudGltZXIgPSBzZXRUaW1lb3V0KCgpID0+IHtcbiAgICAgIHRoaXMudGltZXIgPSBudWxsO1xuICAgICAgdGhpcy5hcHBseSh6KTtcbiAgICB9LCB0aGlzLmRlbGF5TXMpO1xuICB9XG5cbiAgLyoqIEFwcGx5IHogaW1tZWRpYXRlbHkgKHNsaWRlIGVuZCAvIGtleWJvYXJkIHN0ZXApLCBkcm9wcGluZyBhbnkgcGVuZGluZyBzY3J1Yi4gKi9cbiAgY29tbWl0KHo6IG51bWJlcik6IHZvaWQge1xuICAgIHRoaXMuY2FuY2VsKCk7XG4gICAgdGhpcy5hcHBseSh6KTtcbiAgfVxuXG4gIC8qKiBEcm9wIGEgcGVuZGluZyBzY3J1YiB3aXRob3V0IGFwcGx5aW5nICh0ZWFyZG93bikuICovXG4gIGNhbmNlbCgpOiB2b2lkIHtcbiAgICBpZiAodGhpcy50aW1lcikge1xuICAgICAgY2xlYXJUaW1lb3V0KHRoaXMudGltZXIpO1xuICAgICAgdGhpcy50aW1lciA9IG51bGw7XG4gICAgfVxuICB9XG59XG4iXX0=
|