plexora 0.0.1__py3-none-any.whl
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.
- plexora/__init__.py +129 -0
- plexora/__main__.py +15 -0
- plexora/_url.py +105 -0
- plexora/api/__init__.py +55 -0
- plexora/api/dataset.py +398 -0
- plexora/api/http.py +19 -0
- plexora/api/plugin.py +541 -0
- plexora/api/store.py +221 -0
- plexora/cli.py +777 -0
- plexora/client/dist/354_bundle.js +2 -0
- plexora/client/dist/354_bundle.js.LICENSE.txt +20 -0
- plexora/client/dist/418_bundle.js +2 -0
- plexora/client/dist/418_bundle.js.LICENSE.txt +1 -0
- plexora/client/dist/693b935cb1f907814be9c6199a68c217.svg +19 -0
- plexora/client/dist/713c134ff47cd328a5b711526f151082.svg +18 -0
- plexora/client/dist/770_bundle.js +1 -0
- plexora/client/dist/ff743f408972e0e96ca9153ed5ae4c83.svg +23 -0
- plexora/client/dist/vendor_bundle.js +2 -0
- plexora/client/dist/vendor_bundle.js.LICENSE.txt +308 -0
- plexora/client/external/openseadragon-bin-2.4.0/LICENSE.txt +28 -0
- plexora/client/external/openseadragon-bin-2.4.0/canvas-overlay-hd.js +140 -0
- plexora/client/external/openseadragon-bin-2.4.0/changelog.txt +501 -0
- plexora/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/.gitattributes +17 -0
- plexora/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/LICENSE.txt +116 -0
- plexora/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/Photoshop/Toolbar.png +0 -0
- plexora/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/Photoshop/fullpage.psd +0 -0
- plexora/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/Photoshop/home.psd +0 -0
- plexora/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/Photoshop/next.psd +0 -0
- plexora/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/Photoshop/previous.psd +0 -0
- plexora/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/Photoshop/rotateleft.psd +0 -0
- plexora/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/Photoshop/rotateright.psd +0 -0
- plexora/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/Photoshop/zoom.psd +0 -0
- plexora/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/README.md +9 -0
- plexora/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/images/fullpage_grouphover.png +0 -0
- plexora/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/images/fullpage_hover.png +0 -0
- plexora/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/images/fullpage_pressed.png +0 -0
- plexora/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/images/fullpage_rest.png +0 -0
- plexora/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/images/home_grouphover.png +0 -0
- plexora/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/images/home_hover.png +0 -0
- plexora/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/images/home_pressed.png +0 -0
- plexora/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/images/home_rest.png +0 -0
- plexora/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/images/next_grouphover.png +0 -0
- plexora/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/images/next_hover.png +0 -0
- plexora/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/images/next_pressed.png +0 -0
- plexora/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/images/next_rest.png +0 -0
- plexora/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/images/previous_grouphover.png +0 -0
- plexora/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/images/previous_hover.png +0 -0
- plexora/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/images/previous_pressed.png +0 -0
- plexora/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/images/previous_rest.png +0 -0
- plexora/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/images/rotateleft_grouphover.png +0 -0
- plexora/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/images/rotateleft_hover.png +0 -0
- plexora/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/images/rotateleft_pressed.png +0 -0
- plexora/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/images/rotateleft_rest.png +0 -0
- plexora/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/images/rotateright_grouphover.png +0 -0
- plexora/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/images/rotateright_hover.png +0 -0
- plexora/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/images/rotateright_pressed.png +0 -0
- plexora/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/images/rotateright_rest.png +0 -0
- plexora/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/images/zoomin_grouphover.png +0 -0
- plexora/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/images/zoomin_hover.png +0 -0
- plexora/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/images/zoomin_pressed.png +0 -0
- plexora/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/images/zoomin_rest.png +0 -0
- plexora/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/images/zoomout_grouphover.png +0 -0
- plexora/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/images/zoomout_hover.png +0 -0
- plexora/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/images/zoomout_pressed.png +0 -0
- plexora/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/images/zoomout_rest.png +0 -0
- plexora/client/external/openseadragon-bin-2.4.0/openseadragon-scalebar.js +562 -0
- plexora/client/src/css/import.css +474 -0
- plexora/client/src/css/main.css +903 -0
- plexora/client/src/css/openProject.css +363 -0
- plexora/client/src/css/quickView.css +166 -0
- plexora/client/src/css/tokens.css +51 -0
- plexora/client/src/css/viewer.css +1655 -0
- plexora/client/src/img/apple-touch-icon.png +0 -0
- plexora/client/src/img/favicon.ico +0 -0
- plexora/client/src/img/logo.ai +8892 -33
- plexora/client/src/img/logo.svg +1 -0
- plexora/client/src/img/logo_with_text.ai +9011 -34
- plexora/client/src/img/logo_with_text.svg +1 -0
- plexora/client/src/js/main.js +704 -0
- plexora/client/src/js/pluginRegistry.js +108 -0
- plexora/client/src/js/services/appStatus.js +286 -0
- plexora/client/src/js/services/browsePicker.js +73 -0
- plexora/client/src/js/services/dataLayer.js +400 -0
- plexora/client/src/js/services/datasetContext.js +143 -0
- plexora/client/src/js/services/glRenderer.js +143 -0
- plexora/client/src/js/services/importFormValidation.js +257 -0
- plexora/client/src/js/services/numericData.js +100 -0
- plexora/client/src/js/services/passVariablesToFrontend.js +20 -0
- plexora/client/src/js/services/simpleEventHandler.js +25 -0
- plexora/client/src/js/vendor.js +29 -0
- plexora/client/src/js/views/channelList.js +663 -0
- plexora/client/src/js/views/colorSwatchPicker.js +202 -0
- plexora/client/src/js/views/columnClassifier.js +146 -0
- plexora/client/src/js/views/coordinateField.js +133 -0
- plexora/client/src/js/views/dataSourceField.js +276 -0
- plexora/client/src/js/views/imageViewer.js +3306 -0
- plexora/client/src/js/views/miniMap.js +787 -0
- plexora/client/src/js/views/navbarControls.js +178 -0
- plexora/client/src/js/views/openProjectPage.js +239 -0
- plexora/client/src/js/views/projectEdit.js +335 -0
- plexora/client/src/js/views/quickViewLanding.js +136 -0
- plexora/client/src/js/views/rainbow.js +326 -0
- plexora/client/src/js/views/requirementsModal.js +572 -0
- plexora/client/src/js/views/rgbImageViewer.js +115 -0
- plexora/client/src/js/views/searchableSelect.js +411 -0
- plexora/client/src/js/views/segmentationProgress.js +122 -0
- plexora/client/src/js/views/toolLoader.js +747 -0
- plexora/client/src/js/views/viewerControls.js +689 -0
- plexora/client/src/js/views/viewerManager.js +434 -0
- plexora/client/src/js/views/viewerSidebar.js +1117 -0
- plexora/client/src/js/workers/tileDecoder.js +130 -0
- plexora/client/src/shaders/frag.glsl +528 -0
- plexora/client/src/shaders/vert.glsl +12 -0
- plexora/client/templates/base.html +212 -0
- plexora/client/templates/index.html +238 -0
- plexora/client/templates/open_project.html +96 -0
- plexora/client/templates/project_columns.html +86 -0
- plexora/client/templates/project_edit.html +196 -0
- plexora/client/templates/upload.html +118 -0
- plexora/connect.py +528 -0
- plexora/datasource.py +782 -0
- plexora/jupyter.py +406 -0
- plexora/notebook_env.py +232 -0
- plexora/paths.py +486 -0
- plexora/plugins/__init__.py +12 -0
- plexora/plugins/cell_explorer/__init__.py +100 -0
- plexora/plugins/cell_explorer/server/__init__.py +11 -0
- plexora/plugins/cell_explorer/server/routes.py +167 -0
- plexora/plugins/cell_explorer/server/state.py +281 -0
- plexora/plugins/cell_explorer/server/values.py +145 -0
- plexora/plugins/cell_explorer/server/variables.py +367 -0
- plexora/plugins/cell_explorer/static/cellExplorerApi.js +158 -0
- plexora/plugins/cell_explorer/static/cellExplorerColors.js +316 -0
- plexora/plugins/cell_explorer/static/cellExplorerContinuous.js +403 -0
- plexora/plugins/cell_explorer/static/cellExplorerFigureBridge.js +133 -0
- plexora/plugins/cell_explorer/static/cellExplorerLegend.js +188 -0
- plexora/plugins/cell_explorer/static/cellExplorerRoiBridge.js +822 -0
- plexora/plugins/cell_explorer/static/cellExplorerSidebarController.js +826 -0
- plexora/plugins/cell_explorer/static/cellExplorerState.js +372 -0
- plexora/plugins/cell_explorer/static/cell_explorer.css +752 -0
- plexora/plugins/cell_explorer/templates/cell_explorer/panel.html +137 -0
- plexora/plugins/figure_builder/__init__.py +125 -0
- plexora/plugins/figure_builder/server/__init__.py +7 -0
- plexora/plugins/figure_builder/server/compose.py +365 -0
- plexora/plugins/figure_builder/server/export.py +784 -0
- plexora/plugins/figure_builder/server/export_jobs.py +148 -0
- plexora/plugins/figure_builder/server/operations.py +556 -0
- plexora/plugins/figure_builder/server/pixels.py +142 -0
- plexora/plugins/figure_builder/server/provenance.py +174 -0
- plexora/plugins/figure_builder/server/render.py +278 -0
- plexora/plugins/figure_builder/server/repository.py +701 -0
- plexora/plugins/figure_builder/server/routes.py +594 -0
- plexora/plugins/figure_builder/server/schema.py +1023 -0
- plexora/plugins/figure_builder/server/sources.py +120 -0
- plexora/plugins/figure_builder/server/textmetrics.py +155 -0
- plexora/plugins/figure_builder/static/figureActions.js +363 -0
- plexora/plugins/figure_builder/static/figureBuilderApi.js +255 -0
- plexora/plugins/figure_builder/static/figureCanvas.js +2362 -0
- plexora/plugins/figure_builder/static/figureCaptureBoxes.js +369 -0
- plexora/plugins/figure_builder/static/figureCaptureDock.js +466 -0
- plexora/plugins/figure_builder/static/figureCaptureTool.js +1034 -0
- plexora/plugins/figure_builder/static/figureConfirm.js +136 -0
- plexora/plugins/figure_builder/static/figureContextBar.js +1193 -0
- plexora/plugins/figure_builder/static/figureContextMenu.js +253 -0
- plexora/plugins/figure_builder/static/figureDocumentState.js +275 -0
- plexora/plugins/figure_builder/static/figureExportUi.js +174 -0
- plexora/plugins/figure_builder/static/figureLibrary.js +247 -0
- plexora/plugins/figure_builder/static/figureQuickEdit.js +628 -0
- plexora/plugins/figure_builder/static/figureRichText.js +634 -0
- plexora/plugins/figure_builder/static/figureSceneSnapshot.js +361 -0
- plexora/plugins/figure_builder/static/figureSchema.js +218 -0
- plexora/plugins/figure_builder/static/figureSidebarController.js +1159 -0
- plexora/plugins/figure_builder/static/figureTextEditor.js +503 -0
- plexora/plugins/figure_builder/static/figureTextPanel.js +491 -0
- plexora/plugins/figure_builder/static/figureViewOptions.js +273 -0
- plexora/plugins/figure_builder/static/figureWorkspace.js +2030 -0
- plexora/plugins/figure_builder/static/figure_builder.css +3292 -0
- plexora/plugins/figure_builder/templates/figure_builder/library.html +74 -0
- plexora/plugins/figure_builder/templates/figure_builder/workspace.html +27 -0
- plexora/plugins/figure_builder/templates/figure_builder/workspace_body.html +458 -0
- plexora/plugins/gating/__init__.py +76 -0
- plexora/plugins/gating/server/__init__.py +0 -0
- plexora/plugins/gating/server/anndata_gates.py +367 -0
- plexora/plugins/gating/server/database.py +16 -0
- plexora/plugins/gating/server/model.py +320 -0
- plexora/plugins/gating/server/routes.py +235 -0
- plexora/plugins/gating/static/csvGatingList.js +947 -0
- plexora/plugins/gating/static/gating.css +289 -0
- plexora/plugins/gating/static/gatingApi.js +221 -0
- plexora/plugins/gating/static/gatingSidebarController.js +513 -0
- plexora/plugins/gating/templates/gating/legacy.html +26 -0
- plexora/plugins/gating/templates/gating/panel.html +69 -0
- plexora/plugins/roi/__init__.py +95 -0
- plexora/plugins/roi/server/__init__.py +8 -0
- plexora/plugins/roi/server/adapters.py +655 -0
- plexora/plugins/roi/server/geojson.py +267 -0
- plexora/plugins/roi/server/geometry.py +181 -0
- plexora/plugins/roi/server/mapping.py +169 -0
- plexora/plugins/roi/server/operations.py +293 -0
- plexora/plugins/roi/server/repository.py +215 -0
- plexora/plugins/roi/server/routes.py +409 -0
- plexora/plugins/roi/server/schema.py +323 -0
- plexora/plugins/roi/static/roi.css +583 -0
- plexora/plugins/roi/static/roiApi.js +177 -0
- plexora/plugins/roi/static/roiFigureBridge.js +107 -0
- plexora/plugins/roi/static/roiGeometry.js +350 -0
- plexora/plugins/roi/static/roiRenderer.js +278 -0
- plexora/plugins/roi/static/roiSidebarController.js +1038 -0
- plexora/plugins/roi/static/roiState.js +518 -0
- plexora/plugins/roi/static/roiTools.js +993 -0
- plexora/plugins/roi/templates/roi/panel.html +240 -0
- plexora/proxy.py +57 -0
- plexora/server/models/adapters/__init__.py +94 -0
- plexora/server/models/adapters/anndata_adapter.py +388 -0
- plexora/server/models/adapters/base.py +93 -0
- plexora/server/models/adapters/classify.py +192 -0
- plexora/server/models/adapters/csv_adapter.py +105 -0
- plexora/server/models/adapters/inspection.py +265 -0
- plexora/server/models/adapters/spatialdata_adapter.py +248 -0
- plexora/server/models/centroid_tiles.py +305 -0
- plexora/server/models/data_model.py +1659 -0
- plexora/server/models/database_model.py +169 -0
- plexora/server/models/project.py +1270 -0
- plexora/server/plugins.py +199 -0
- plexora/server/routes/browse_routes.py +41 -0
- plexora/server/routes/data_routes.py +294 -0
- plexora/server/routes/import_routes.py +603 -0
- plexora/server/routes/page_routes.py +139 -0
- plexora/server/routes/project_routes.py +438 -0
- plexora/server/routes/quick_view_routes.py +62 -0
- plexora/server/routes/system_routes.py +39 -0
- plexora/server/routes/tool_routes.py +418 -0
- plexora/server/utils/addHEColumns.py +11 -0
- plexora/server/utils/fast_png.py +97 -0
- plexora/server/utils/fullConversion.py +50 -0
- plexora/server/utils/native_dialog.py +168 -0
- plexora/server/utils/pre_normalization.py +47 -0
- plexora/server/utils/segmentation_pyramid.py +598 -0
- plexora/server/utils/smallestenclosingcircle.py +126 -0
- plexora/server/utils/tiffsurgeon.py +372 -0
- plexora/server_cli.py +48 -0
- plexora/test.py +0 -0
- plexora-0.0.1.dist-info/METADATA +341 -0
- plexora-0.0.1.dist-info/RECORD +248 -0
- plexora-0.0.1.dist-info/WHEEL +5 -0
- plexora-0.0.1.dist-info/entry_points.txt +12 -0
- plexora-0.0.1.dist-info/licenses/LICENSE +207 -0
- plexora-0.0.1.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,278 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* roiRenderer.js - drawing the regions over the image.
|
|
3
|
+
*
|
|
4
|
+
* Built on OpenSeadragon.CanvasOverlayHd, which core already loads (base.html)
|
|
5
|
+
* and which does the one hard part: it hands `onRedraw` a 2D context already
|
|
6
|
+
* transformed into FULL-RESOLUTION IMAGE PIXEL space, and re-fires it on every
|
|
7
|
+
* pan and zoom. So this file draws polygons at the coordinates they are stored
|
|
8
|
+
* at and never converts anything.
|
|
9
|
+
*
|
|
10
|
+
* The plugin gets its OWN overlay instance rather than sharing the viewer's.
|
|
11
|
+
* Core's is wired to `selectionPolygonToDraw`, a leftover from the removed
|
|
12
|
+
* lasso that nothing writes to any more, and its onRedraw also paints
|
|
13
|
+
* centroids; stacking a second canvas on the same parent costs nothing and
|
|
14
|
+
* keeps the two from having to know about each other.
|
|
15
|
+
*
|
|
16
|
+
* Two things about that overlay are easy to get wrong and expensive to debug:
|
|
17
|
+
*
|
|
18
|
+
* **onRedraw fires once per world item**, i.e. once per active channel. Drawing
|
|
19
|
+
* unconditionally paints every region N times -- invisible at full opacity,
|
|
20
|
+
* obvious the moment anything is translucent, which regions are. Hence the
|
|
21
|
+
* index guard.
|
|
22
|
+
*
|
|
23
|
+
* **Everything is in image pixels, including line widths.** A 2px stroke is 2
|
|
24
|
+
* IMAGE pixels, so at 10x zoom it is a 20px slab and at 0.1x it vanishes.
|
|
25
|
+
* Dividing by `opts.zoom` is what keeps an outline looking like an outline.
|
|
26
|
+
*/
|
|
27
|
+
class RoiRenderer {
|
|
28
|
+
|
|
29
|
+
constructor(ctx, store) {
|
|
30
|
+
this.ctx = ctx;
|
|
31
|
+
this.store = store;
|
|
32
|
+
this.viewer = ctx.viewer?.viewer || null;
|
|
33
|
+
|
|
34
|
+
this.enabled = false;
|
|
35
|
+
this.overlay = null;
|
|
36
|
+
this.draft = null; // {points, tool, closing} while drawing
|
|
37
|
+
this.hoverId = null;
|
|
38
|
+
this._frame = null;
|
|
39
|
+
this._paths = new Map(); // feature id -> {path, geometry}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
//: Screen-space appearance, divided by the zoom at draw time.
|
|
43
|
+
static get STROKE() { return 1.6; }
|
|
44
|
+
static get SELECTED_STROKE() { return 2.6; }
|
|
45
|
+
static get HANDLE_RADIUS() { return 4.5; }
|
|
46
|
+
static get FILL_ALPHA() { return 0.15; }
|
|
47
|
+
|
|
48
|
+
attach() {
|
|
49
|
+
if (this.overlay || !this.viewer) return;
|
|
50
|
+
this.overlay = new OpenSeadragon.CanvasOverlayHd(this.viewer, {
|
|
51
|
+
onRedraw: (opts) => this.draw(opts),
|
|
52
|
+
});
|
|
53
|
+
this.enabled = true;
|
|
54
|
+
this.schedule();
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/** Stop drawing without tearing the overlay down.
|
|
58
|
+
*
|
|
59
|
+
* The overlay registers its own `update-viewport` handler and offers no way
|
|
60
|
+
* to remove it, so "detached" means the callback returns immediately rather
|
|
61
|
+
* than that it stopped being called. Cheap, and the canvas is cleared so
|
|
62
|
+
* nothing of this plugin's is left on screen. */
|
|
63
|
+
setEnabled(enabled) {
|
|
64
|
+
this.enabled = enabled;
|
|
65
|
+
if (!enabled) this.overlay?.clear();
|
|
66
|
+
else this.schedule();
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
destroy() {
|
|
70
|
+
this.enabled = false;
|
|
71
|
+
if (this._frame) cancelAnimationFrame(this._frame);
|
|
72
|
+
this._frame = null;
|
|
73
|
+
this._paths.clear();
|
|
74
|
+
if (this.overlay) {
|
|
75
|
+
this.overlay.clear();
|
|
76
|
+
this.overlay._canvasdiv?.remove();
|
|
77
|
+
this.overlay = null;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/** Repaint on the next frame, however many times this is called first.
|
|
82
|
+
*
|
|
83
|
+
* A pointer-move handler can fire several times between two frames, and a
|
|
84
|
+
* geometry rebuild per event is work whose result is thrown away. */
|
|
85
|
+
schedule() {
|
|
86
|
+
if (!this.enabled || !this.overlay || this._frame) return;
|
|
87
|
+
this._frame = requestAnimationFrame(() => {
|
|
88
|
+
this._frame = null;
|
|
89
|
+
if (!this.enabled || !this.overlay) return;
|
|
90
|
+
this.overlay.resize();
|
|
91
|
+
this.overlay.clear();
|
|
92
|
+
this.overlay._updateCanvas();
|
|
93
|
+
});
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/** Drop a cached path because its geometry changed. */
|
|
97
|
+
invalidate(featureId) {
|
|
98
|
+
if (featureId) this._paths.delete(featureId);
|
|
99
|
+
else this._paths.clear();
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* A Path2D for one feature, cached.
|
|
104
|
+
*
|
|
105
|
+
* Cacheable because the coordinates are in image space and therefore do not
|
|
106
|
+
* change when the user pans or zooms -- the context transform does. So
|
|
107
|
+
* scrolling around a project with a thousand regions rebuilds nothing, and
|
|
108
|
+
* dragging one vertex rebuilds one shape.
|
|
109
|
+
*/
|
|
110
|
+
pathFor(feature) {
|
|
111
|
+
const cached = this._paths.get(feature.id);
|
|
112
|
+
if (cached && cached.geometry === feature.geometry) return cached.path;
|
|
113
|
+
|
|
114
|
+
const path = new Path2D();
|
|
115
|
+
for (const ring of RoiGeometry.rings(feature.geometry)) {
|
|
116
|
+
const points = RoiGeometry.closeRing(ring);
|
|
117
|
+
if (!points.length) continue;
|
|
118
|
+
path.moveTo(points[0][0], points[0][1]);
|
|
119
|
+
for (let i = 1; i < points.length; i++) path.lineTo(points[i][0], points[i][1]);
|
|
120
|
+
path.closePath();
|
|
121
|
+
}
|
|
122
|
+
this._paths.set(feature.id, { path, geometry: feature.geometry });
|
|
123
|
+
return path;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
draw(opts) {
|
|
127
|
+
// Once per world item, and one canvas: draw for the first and let the
|
|
128
|
+
// rest fall through, or every translucent fill is composited N times.
|
|
129
|
+
if (!this.enabled || opts.index !== 0) return;
|
|
130
|
+
|
|
131
|
+
const context = opts.context;
|
|
132
|
+
const zoom = opts.zoom || 1;
|
|
133
|
+
const view = this.viewportBounds();
|
|
134
|
+
|
|
135
|
+
for (const feature of this.store.visibleFeatures()) {
|
|
136
|
+
if (view && !this.intersects(feature, view)) continue;
|
|
137
|
+
this.drawFeature(context, feature, zoom);
|
|
138
|
+
}
|
|
139
|
+
if (this.draft) this.drawDraft(context, zoom);
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
/** The image-pixel rectangle currently on screen, for culling.
|
|
143
|
+
*
|
|
144
|
+
* Culling matters here rather than being premature: a project with several
|
|
145
|
+
* thousand regions zoomed into one corner would otherwise stroke every one
|
|
146
|
+
* of them, every frame, for shapes entirely off screen. */
|
|
147
|
+
viewportBounds() {
|
|
148
|
+
try {
|
|
149
|
+
const item = this.viewer?.world?.getItemAt(0);
|
|
150
|
+
if (!item) return null;
|
|
151
|
+
const rect = item.viewportToImageRectangle(this.viewer.viewport.getBounds(true));
|
|
152
|
+
const scale = 2 ** (this.ctx.config?.extraZoomLevels || 0);
|
|
153
|
+
const pad = 16;
|
|
154
|
+
return {
|
|
155
|
+
minX: rect.x / scale - pad,
|
|
156
|
+
minY: rect.y / scale - pad,
|
|
157
|
+
maxX: (rect.x + rect.width) / scale + pad,
|
|
158
|
+
maxY: (rect.y + rect.height) / scale + pad,
|
|
159
|
+
};
|
|
160
|
+
} catch (error) {
|
|
161
|
+
return null;
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
intersects(feature, view) {
|
|
166
|
+
const box = RoiGeometry.bounds(feature.geometry);
|
|
167
|
+
if (!box) return false;
|
|
168
|
+
return !(box.maxX < view.minX || box.minX > view.maxX
|
|
169
|
+
|| box.maxY < view.minY || box.minY > view.maxY);
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
drawFeature(context, feature, zoom) {
|
|
173
|
+
const category = this.store.category(feature.category_id) || {};
|
|
174
|
+
const color = category.color || "#8b93a6";
|
|
175
|
+
const selected = feature.id === this.store.selectionId;
|
|
176
|
+
const path = this.pathFor(feature);
|
|
177
|
+
|
|
178
|
+
context.save();
|
|
179
|
+
// Even-odd so an imported polygon's interior rings read as holes rather
|
|
180
|
+
// than being filled over. Nonzero would paint straight across them.
|
|
181
|
+
context.fillStyle = color;
|
|
182
|
+
context.globalAlpha = selected ? RoiRenderer.FILL_ALPHA * 1.6 : RoiRenderer.FILL_ALPHA;
|
|
183
|
+
context.fill(path, "evenodd");
|
|
184
|
+
|
|
185
|
+
context.globalAlpha = 1;
|
|
186
|
+
context.strokeStyle = color;
|
|
187
|
+
context.lineWidth = (selected ? RoiRenderer.SELECTED_STROKE : RoiRenderer.STROKE) / zoom;
|
|
188
|
+
context.lineJoin = "round";
|
|
189
|
+
|
|
190
|
+
if (feature.flags && feature.flags.self_intersecting) {
|
|
191
|
+
// Marked, never corrected. A dashed outline is the whole
|
|
192
|
+
// notification: the shape is stored exactly as drawn, and what to
|
|
193
|
+
// do about a bow-tie is the user's call, not this renderer's.
|
|
194
|
+
context.setLineDash([7 / zoom, 4 / zoom]);
|
|
195
|
+
}
|
|
196
|
+
if (this.store.isLocked(feature)) {
|
|
197
|
+
context.globalAlpha = 0.75;
|
|
198
|
+
}
|
|
199
|
+
context.stroke(path);
|
|
200
|
+
context.setLineDash([]);
|
|
201
|
+
|
|
202
|
+
if (feature.id === this.hoverId && !selected) {
|
|
203
|
+
context.globalAlpha = 0.35;
|
|
204
|
+
context.lineWidth = (RoiRenderer.SELECTED_STROKE + 2) / zoom;
|
|
205
|
+
context.stroke(path);
|
|
206
|
+
}
|
|
207
|
+
context.restore();
|
|
208
|
+
|
|
209
|
+
if (selected) this.drawHandles(context, feature, zoom, color);
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
/** Vertex handles, on the selected shape only.
|
|
213
|
+
*
|
|
214
|
+
* Not drawn for a locked shape or one whose vertices cannot be edited (a
|
|
215
|
+
* hole-bearing or multi-part import): a handle that does nothing when
|
|
216
|
+
* dragged is worse than no handle. */
|
|
217
|
+
drawHandles(context, feature, zoom, color) {
|
|
218
|
+
if (this.store.isLocked(feature)) return;
|
|
219
|
+
if (!RoiGeometry.isVertexEditable(feature.geometry)) return;
|
|
220
|
+
|
|
221
|
+
const radius = RoiRenderer.HANDLE_RADIUS / zoom;
|
|
222
|
+
const points = RoiGeometry.openRing(feature.geometry.coordinates[0]);
|
|
223
|
+
|
|
224
|
+
context.save();
|
|
225
|
+
context.lineWidth = 1.4 / zoom;
|
|
226
|
+
for (const [x, y] of points) {
|
|
227
|
+
context.beginPath();
|
|
228
|
+
context.arc(x, y, radius, 0, Math.PI * 2);
|
|
229
|
+
context.fillStyle = "#0b0f15";
|
|
230
|
+
context.fill();
|
|
231
|
+
context.strokeStyle = color;
|
|
232
|
+
context.stroke();
|
|
233
|
+
}
|
|
234
|
+
context.restore();
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
/** The shape being drawn right now: dashed, so it never looks committed. */
|
|
238
|
+
drawDraft(context, zoom) {
|
|
239
|
+
const { points, tool } = this.draft;
|
|
240
|
+
if (!points.length) return;
|
|
241
|
+
const color = (this.store.activeCategory || {}).color || "#38bdf8";
|
|
242
|
+
|
|
243
|
+
context.save();
|
|
244
|
+
context.strokeStyle = color;
|
|
245
|
+
context.fillStyle = color;
|
|
246
|
+
context.lineWidth = RoiRenderer.SELECTED_STROKE / zoom;
|
|
247
|
+
context.lineJoin = "round";
|
|
248
|
+
context.setLineDash([8 / zoom, 5 / zoom]);
|
|
249
|
+
|
|
250
|
+
context.beginPath();
|
|
251
|
+
context.moveTo(points[0][0], points[0][1]);
|
|
252
|
+
for (let i = 1; i < points.length; i++) context.lineTo(points[i][0], points[i][1]);
|
|
253
|
+
if (tool !== "polygon") context.closePath();
|
|
254
|
+
context.stroke();
|
|
255
|
+
|
|
256
|
+
if (points.length > 2) {
|
|
257
|
+
context.globalAlpha = 0.1;
|
|
258
|
+
context.setLineDash([]);
|
|
259
|
+
context.fill();
|
|
260
|
+
}
|
|
261
|
+
context.restore();
|
|
262
|
+
|
|
263
|
+
if (tool === "polygon") {
|
|
264
|
+
// Only the polygon tool has committed vertices worth showing: the
|
|
265
|
+
// others are a single continuous gesture with nothing to click back
|
|
266
|
+
// to.
|
|
267
|
+
const radius = (RoiRenderer.HANDLE_RADIUS - 1) / zoom;
|
|
268
|
+
context.save();
|
|
269
|
+
context.fillStyle = color;
|
|
270
|
+
for (const [x, y] of points) {
|
|
271
|
+
context.beginPath();
|
|
272
|
+
context.arc(x, y, radius, 0, Math.PI * 2);
|
|
273
|
+
context.fill();
|
|
274
|
+
}
|
|
275
|
+
context.restore();
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
}
|