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.
Files changed (248) hide show
  1. plexora/__init__.py +129 -0
  2. plexora/__main__.py +15 -0
  3. plexora/_url.py +105 -0
  4. plexora/api/__init__.py +55 -0
  5. plexora/api/dataset.py +398 -0
  6. plexora/api/http.py +19 -0
  7. plexora/api/plugin.py +541 -0
  8. plexora/api/store.py +221 -0
  9. plexora/cli.py +777 -0
  10. plexora/client/dist/354_bundle.js +2 -0
  11. plexora/client/dist/354_bundle.js.LICENSE.txt +20 -0
  12. plexora/client/dist/418_bundle.js +2 -0
  13. plexora/client/dist/418_bundle.js.LICENSE.txt +1 -0
  14. plexora/client/dist/693b935cb1f907814be9c6199a68c217.svg +19 -0
  15. plexora/client/dist/713c134ff47cd328a5b711526f151082.svg +18 -0
  16. plexora/client/dist/770_bundle.js +1 -0
  17. plexora/client/dist/ff743f408972e0e96ca9153ed5ae4c83.svg +23 -0
  18. plexora/client/dist/vendor_bundle.js +2 -0
  19. plexora/client/dist/vendor_bundle.js.LICENSE.txt +308 -0
  20. plexora/client/external/openseadragon-bin-2.4.0/LICENSE.txt +28 -0
  21. plexora/client/external/openseadragon-bin-2.4.0/canvas-overlay-hd.js +140 -0
  22. plexora/client/external/openseadragon-bin-2.4.0/changelog.txt +501 -0
  23. plexora/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/.gitattributes +17 -0
  24. plexora/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/LICENSE.txt +116 -0
  25. plexora/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/Photoshop/Toolbar.png +0 -0
  26. plexora/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/Photoshop/fullpage.psd +0 -0
  27. plexora/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/Photoshop/home.psd +0 -0
  28. plexora/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/Photoshop/next.psd +0 -0
  29. plexora/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/Photoshop/previous.psd +0 -0
  30. plexora/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/Photoshop/rotateleft.psd +0 -0
  31. plexora/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/Photoshop/rotateright.psd +0 -0
  32. plexora/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/Photoshop/zoom.psd +0 -0
  33. plexora/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/README.md +9 -0
  34. plexora/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/images/fullpage_grouphover.png +0 -0
  35. plexora/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/images/fullpage_hover.png +0 -0
  36. plexora/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/images/fullpage_pressed.png +0 -0
  37. plexora/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/images/fullpage_rest.png +0 -0
  38. plexora/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/images/home_grouphover.png +0 -0
  39. plexora/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/images/home_hover.png +0 -0
  40. plexora/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/images/home_pressed.png +0 -0
  41. plexora/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/images/home_rest.png +0 -0
  42. plexora/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/images/next_grouphover.png +0 -0
  43. plexora/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/images/next_hover.png +0 -0
  44. plexora/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/images/next_pressed.png +0 -0
  45. plexora/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/images/next_rest.png +0 -0
  46. plexora/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/images/previous_grouphover.png +0 -0
  47. plexora/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/images/previous_hover.png +0 -0
  48. plexora/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/images/previous_pressed.png +0 -0
  49. plexora/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/images/previous_rest.png +0 -0
  50. plexora/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/images/rotateleft_grouphover.png +0 -0
  51. plexora/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/images/rotateleft_hover.png +0 -0
  52. plexora/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/images/rotateleft_pressed.png +0 -0
  53. plexora/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/images/rotateleft_rest.png +0 -0
  54. plexora/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/images/rotateright_grouphover.png +0 -0
  55. plexora/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/images/rotateright_hover.png +0 -0
  56. plexora/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/images/rotateright_pressed.png +0 -0
  57. plexora/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/images/rotateright_rest.png +0 -0
  58. plexora/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/images/zoomin_grouphover.png +0 -0
  59. plexora/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/images/zoomin_hover.png +0 -0
  60. plexora/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/images/zoomin_pressed.png +0 -0
  61. plexora/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/images/zoomin_rest.png +0 -0
  62. plexora/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/images/zoomout_grouphover.png +0 -0
  63. plexora/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/images/zoomout_hover.png +0 -0
  64. plexora/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/images/zoomout_pressed.png +0 -0
  65. plexora/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/images/zoomout_rest.png +0 -0
  66. plexora/client/external/openseadragon-bin-2.4.0/openseadragon-scalebar.js +562 -0
  67. plexora/client/src/css/import.css +474 -0
  68. plexora/client/src/css/main.css +903 -0
  69. plexora/client/src/css/openProject.css +363 -0
  70. plexora/client/src/css/quickView.css +166 -0
  71. plexora/client/src/css/tokens.css +51 -0
  72. plexora/client/src/css/viewer.css +1655 -0
  73. plexora/client/src/img/apple-touch-icon.png +0 -0
  74. plexora/client/src/img/favicon.ico +0 -0
  75. plexora/client/src/img/logo.ai +8892 -33
  76. plexora/client/src/img/logo.svg +1 -0
  77. plexora/client/src/img/logo_with_text.ai +9011 -34
  78. plexora/client/src/img/logo_with_text.svg +1 -0
  79. plexora/client/src/js/main.js +704 -0
  80. plexora/client/src/js/pluginRegistry.js +108 -0
  81. plexora/client/src/js/services/appStatus.js +286 -0
  82. plexora/client/src/js/services/browsePicker.js +73 -0
  83. plexora/client/src/js/services/dataLayer.js +400 -0
  84. plexora/client/src/js/services/datasetContext.js +143 -0
  85. plexora/client/src/js/services/glRenderer.js +143 -0
  86. plexora/client/src/js/services/importFormValidation.js +257 -0
  87. plexora/client/src/js/services/numericData.js +100 -0
  88. plexora/client/src/js/services/passVariablesToFrontend.js +20 -0
  89. plexora/client/src/js/services/simpleEventHandler.js +25 -0
  90. plexora/client/src/js/vendor.js +29 -0
  91. plexora/client/src/js/views/channelList.js +663 -0
  92. plexora/client/src/js/views/colorSwatchPicker.js +202 -0
  93. plexora/client/src/js/views/columnClassifier.js +146 -0
  94. plexora/client/src/js/views/coordinateField.js +133 -0
  95. plexora/client/src/js/views/dataSourceField.js +276 -0
  96. plexora/client/src/js/views/imageViewer.js +3306 -0
  97. plexora/client/src/js/views/miniMap.js +787 -0
  98. plexora/client/src/js/views/navbarControls.js +178 -0
  99. plexora/client/src/js/views/openProjectPage.js +239 -0
  100. plexora/client/src/js/views/projectEdit.js +335 -0
  101. plexora/client/src/js/views/quickViewLanding.js +136 -0
  102. plexora/client/src/js/views/rainbow.js +326 -0
  103. plexora/client/src/js/views/requirementsModal.js +572 -0
  104. plexora/client/src/js/views/rgbImageViewer.js +115 -0
  105. plexora/client/src/js/views/searchableSelect.js +411 -0
  106. plexora/client/src/js/views/segmentationProgress.js +122 -0
  107. plexora/client/src/js/views/toolLoader.js +747 -0
  108. plexora/client/src/js/views/viewerControls.js +689 -0
  109. plexora/client/src/js/views/viewerManager.js +434 -0
  110. plexora/client/src/js/views/viewerSidebar.js +1117 -0
  111. plexora/client/src/js/workers/tileDecoder.js +130 -0
  112. plexora/client/src/shaders/frag.glsl +528 -0
  113. plexora/client/src/shaders/vert.glsl +12 -0
  114. plexora/client/templates/base.html +212 -0
  115. plexora/client/templates/index.html +238 -0
  116. plexora/client/templates/open_project.html +96 -0
  117. plexora/client/templates/project_columns.html +86 -0
  118. plexora/client/templates/project_edit.html +196 -0
  119. plexora/client/templates/upload.html +118 -0
  120. plexora/connect.py +528 -0
  121. plexora/datasource.py +782 -0
  122. plexora/jupyter.py +406 -0
  123. plexora/notebook_env.py +232 -0
  124. plexora/paths.py +486 -0
  125. plexora/plugins/__init__.py +12 -0
  126. plexora/plugins/cell_explorer/__init__.py +100 -0
  127. plexora/plugins/cell_explorer/server/__init__.py +11 -0
  128. plexora/plugins/cell_explorer/server/routes.py +167 -0
  129. plexora/plugins/cell_explorer/server/state.py +281 -0
  130. plexora/plugins/cell_explorer/server/values.py +145 -0
  131. plexora/plugins/cell_explorer/server/variables.py +367 -0
  132. plexora/plugins/cell_explorer/static/cellExplorerApi.js +158 -0
  133. plexora/plugins/cell_explorer/static/cellExplorerColors.js +316 -0
  134. plexora/plugins/cell_explorer/static/cellExplorerContinuous.js +403 -0
  135. plexora/plugins/cell_explorer/static/cellExplorerFigureBridge.js +133 -0
  136. plexora/plugins/cell_explorer/static/cellExplorerLegend.js +188 -0
  137. plexora/plugins/cell_explorer/static/cellExplorerRoiBridge.js +822 -0
  138. plexora/plugins/cell_explorer/static/cellExplorerSidebarController.js +826 -0
  139. plexora/plugins/cell_explorer/static/cellExplorerState.js +372 -0
  140. plexora/plugins/cell_explorer/static/cell_explorer.css +752 -0
  141. plexora/plugins/cell_explorer/templates/cell_explorer/panel.html +137 -0
  142. plexora/plugins/figure_builder/__init__.py +125 -0
  143. plexora/plugins/figure_builder/server/__init__.py +7 -0
  144. plexora/plugins/figure_builder/server/compose.py +365 -0
  145. plexora/plugins/figure_builder/server/export.py +784 -0
  146. plexora/plugins/figure_builder/server/export_jobs.py +148 -0
  147. plexora/plugins/figure_builder/server/operations.py +556 -0
  148. plexora/plugins/figure_builder/server/pixels.py +142 -0
  149. plexora/plugins/figure_builder/server/provenance.py +174 -0
  150. plexora/plugins/figure_builder/server/render.py +278 -0
  151. plexora/plugins/figure_builder/server/repository.py +701 -0
  152. plexora/plugins/figure_builder/server/routes.py +594 -0
  153. plexora/plugins/figure_builder/server/schema.py +1023 -0
  154. plexora/plugins/figure_builder/server/sources.py +120 -0
  155. plexora/plugins/figure_builder/server/textmetrics.py +155 -0
  156. plexora/plugins/figure_builder/static/figureActions.js +363 -0
  157. plexora/plugins/figure_builder/static/figureBuilderApi.js +255 -0
  158. plexora/plugins/figure_builder/static/figureCanvas.js +2362 -0
  159. plexora/plugins/figure_builder/static/figureCaptureBoxes.js +369 -0
  160. plexora/plugins/figure_builder/static/figureCaptureDock.js +466 -0
  161. plexora/plugins/figure_builder/static/figureCaptureTool.js +1034 -0
  162. plexora/plugins/figure_builder/static/figureConfirm.js +136 -0
  163. plexora/plugins/figure_builder/static/figureContextBar.js +1193 -0
  164. plexora/plugins/figure_builder/static/figureContextMenu.js +253 -0
  165. plexora/plugins/figure_builder/static/figureDocumentState.js +275 -0
  166. plexora/plugins/figure_builder/static/figureExportUi.js +174 -0
  167. plexora/plugins/figure_builder/static/figureLibrary.js +247 -0
  168. plexora/plugins/figure_builder/static/figureQuickEdit.js +628 -0
  169. plexora/plugins/figure_builder/static/figureRichText.js +634 -0
  170. plexora/plugins/figure_builder/static/figureSceneSnapshot.js +361 -0
  171. plexora/plugins/figure_builder/static/figureSchema.js +218 -0
  172. plexora/plugins/figure_builder/static/figureSidebarController.js +1159 -0
  173. plexora/plugins/figure_builder/static/figureTextEditor.js +503 -0
  174. plexora/plugins/figure_builder/static/figureTextPanel.js +491 -0
  175. plexora/plugins/figure_builder/static/figureViewOptions.js +273 -0
  176. plexora/plugins/figure_builder/static/figureWorkspace.js +2030 -0
  177. plexora/plugins/figure_builder/static/figure_builder.css +3292 -0
  178. plexora/plugins/figure_builder/templates/figure_builder/library.html +74 -0
  179. plexora/plugins/figure_builder/templates/figure_builder/workspace.html +27 -0
  180. plexora/plugins/figure_builder/templates/figure_builder/workspace_body.html +458 -0
  181. plexora/plugins/gating/__init__.py +76 -0
  182. plexora/plugins/gating/server/__init__.py +0 -0
  183. plexora/plugins/gating/server/anndata_gates.py +367 -0
  184. plexora/plugins/gating/server/database.py +16 -0
  185. plexora/plugins/gating/server/model.py +320 -0
  186. plexora/plugins/gating/server/routes.py +235 -0
  187. plexora/plugins/gating/static/csvGatingList.js +947 -0
  188. plexora/plugins/gating/static/gating.css +289 -0
  189. plexora/plugins/gating/static/gatingApi.js +221 -0
  190. plexora/plugins/gating/static/gatingSidebarController.js +513 -0
  191. plexora/plugins/gating/templates/gating/legacy.html +26 -0
  192. plexora/plugins/gating/templates/gating/panel.html +69 -0
  193. plexora/plugins/roi/__init__.py +95 -0
  194. plexora/plugins/roi/server/__init__.py +8 -0
  195. plexora/plugins/roi/server/adapters.py +655 -0
  196. plexora/plugins/roi/server/geojson.py +267 -0
  197. plexora/plugins/roi/server/geometry.py +181 -0
  198. plexora/plugins/roi/server/mapping.py +169 -0
  199. plexora/plugins/roi/server/operations.py +293 -0
  200. plexora/plugins/roi/server/repository.py +215 -0
  201. plexora/plugins/roi/server/routes.py +409 -0
  202. plexora/plugins/roi/server/schema.py +323 -0
  203. plexora/plugins/roi/static/roi.css +583 -0
  204. plexora/plugins/roi/static/roiApi.js +177 -0
  205. plexora/plugins/roi/static/roiFigureBridge.js +107 -0
  206. plexora/plugins/roi/static/roiGeometry.js +350 -0
  207. plexora/plugins/roi/static/roiRenderer.js +278 -0
  208. plexora/plugins/roi/static/roiSidebarController.js +1038 -0
  209. plexora/plugins/roi/static/roiState.js +518 -0
  210. plexora/plugins/roi/static/roiTools.js +993 -0
  211. plexora/plugins/roi/templates/roi/panel.html +240 -0
  212. plexora/proxy.py +57 -0
  213. plexora/server/models/adapters/__init__.py +94 -0
  214. plexora/server/models/adapters/anndata_adapter.py +388 -0
  215. plexora/server/models/adapters/base.py +93 -0
  216. plexora/server/models/adapters/classify.py +192 -0
  217. plexora/server/models/adapters/csv_adapter.py +105 -0
  218. plexora/server/models/adapters/inspection.py +265 -0
  219. plexora/server/models/adapters/spatialdata_adapter.py +248 -0
  220. plexora/server/models/centroid_tiles.py +305 -0
  221. plexora/server/models/data_model.py +1659 -0
  222. plexora/server/models/database_model.py +169 -0
  223. plexora/server/models/project.py +1270 -0
  224. plexora/server/plugins.py +199 -0
  225. plexora/server/routes/browse_routes.py +41 -0
  226. plexora/server/routes/data_routes.py +294 -0
  227. plexora/server/routes/import_routes.py +603 -0
  228. plexora/server/routes/page_routes.py +139 -0
  229. plexora/server/routes/project_routes.py +438 -0
  230. plexora/server/routes/quick_view_routes.py +62 -0
  231. plexora/server/routes/system_routes.py +39 -0
  232. plexora/server/routes/tool_routes.py +418 -0
  233. plexora/server/utils/addHEColumns.py +11 -0
  234. plexora/server/utils/fast_png.py +97 -0
  235. plexora/server/utils/fullConversion.py +50 -0
  236. plexora/server/utils/native_dialog.py +168 -0
  237. plexora/server/utils/pre_normalization.py +47 -0
  238. plexora/server/utils/segmentation_pyramid.py +598 -0
  239. plexora/server/utils/smallestenclosingcircle.py +126 -0
  240. plexora/server/utils/tiffsurgeon.py +372 -0
  241. plexora/server_cli.py +48 -0
  242. plexora/test.py +0 -0
  243. plexora-0.0.1.dist-info/METADATA +341 -0
  244. plexora-0.0.1.dist-info/RECORD +248 -0
  245. plexora-0.0.1.dist-info/WHEEL +5 -0
  246. plexora-0.0.1.dist-info/entry_points.txt +12 -0
  247. plexora-0.0.1.dist-info/licenses/LICENSE +207 -0
  248. plexora-0.0.1.dist-info/top_level.txt +1 -0
@@ -0,0 +1,434 @@
1
+ import "regenerator-runtime/runtime.js";
2
+
3
+ // Global HD-toggle state, read by getTileUrl() below. A plain module-scope
4
+ // flag (not a ViewerManager instance field) because getTileUrl runs with
5
+ // `this` bound to the tileSource object it's attached to, not the
6
+ // ViewerManager -- this is the one place in the tile-loading path that
7
+ // needs the toggle state and can't reach it via `this`.
8
+ export const tileQuality = { hd: false };
9
+
10
+ /**
11
+ * @function toIdealTile -- full tile dimension in full image pixels
12
+ * @param fullScale - scale factor to full image
13
+ * @param useY - 0 for x and 1 for y
14
+ * @returns Number
15
+ */
16
+ function toIdealTile(fullScale, useY) {
17
+ const { _tileWidth, _tileHeight } = this;
18
+ return [_tileWidth, _tileHeight][useY] * fullScale;
19
+ }
20
+
21
+ /**
22
+ * @function toIdealTile -- clipped tile dimension in full image pixels
23
+ * @param fullScale - scale factor to full image
24
+ * @param v - x or y index of tile
25
+ * @param useY - x=0 and y=1
26
+ * @returns Number
27
+ */
28
+ function toRealTile(fullScale, v, useY) {
29
+ const shape = [this.width, this.height][useY];
30
+ const tileShape = this.toIdealTile(fullScale, useY);
31
+ return Math.min(shape - v * tileShape, tileShape);
32
+ }
33
+
34
+ /**
35
+ * @function toTileBoundary -- tile start and size in full image pixels
36
+ * @param fullScale - scale factor to full image
37
+ * @param v - x or y index of tile
38
+ * @param useY - x=0 and y=1
39
+ * @typedef {object} Bound
40
+ * @property {number} start - full image pixel start of tile
41
+ * @property {number} size - full image pixel size of tile
42
+ * @returns Bound
43
+ */
44
+ function toTileBoundary(fullScale, v, useY) {
45
+ const start = v * this.toIdealTile(fullScale, useY);
46
+ const size = this.toRealTile(fullScale, v, useY);
47
+ return { start, size };
48
+ }
49
+
50
+ /**
51
+ * @function toMagnifiedBounds -- return bounds of magnified tile
52
+ * @param _level - openseadragon tile level
53
+ * @param _x - openseadragon tile x index
54
+ * @param _y - openseadragon tile y index
55
+ * @typedef {object} Bounds
56
+ * @property {Array} x - start and end image x-coordinates
57
+ * @property {Array} y - start and end image y-coordinates
58
+ * @returns Bounds
59
+ */
60
+ function toMagnifiedBounds(_level, _x, _y) {
61
+ const tl = this.toTileLevels(_level, _x, _y);
62
+ if (tl.relativeImageScale >= 1) {
63
+ return { x: [0, 1], y: [0, 1] };
64
+ }
65
+ const ownScale = tl.outputFullScale;
66
+ const parentScale = tl.inputFullScale;
67
+ const [x, y] = [tl.outputTile.x, tl.outputTile.y].map((parentOffset, i) => {
68
+ const hd = this.toTileBoundary(ownScale, [_x, _y][i], i);
69
+ const sd = this.toTileBoundary(parentScale, parentOffset, i);
70
+ const start = (hd.start - sd.start) / sd.size;
71
+ const end = start + hd.size / sd.size;
72
+ return [
73
+ [start, end],
74
+ [1 - end, 1 - start],
75
+ ][i];
76
+ });
77
+ return { x, y };
78
+ }
79
+
80
+ /**
81
+ * @function toTileLevels -- measure scaled/non-scaled tile details
82
+ * @param level - openseadragon tile level
83
+ * @param x - openseadragon tile x index
84
+ * @param y - openseadragon tile y index
85
+ * @typedef {object} TileLevels
86
+ * @property {number} inputFullScale - full scale of source tile
87
+ * @property {number} outputFullScale - full scale of renedered tile
88
+ * @property {number} relativeImageScale - scale relative to image pixels
89
+ * @property {object} inputTile - level, x, and y of source tile
90
+ * @property {object} outputTile - level, x, and y of rendered tile
91
+ * @returns TileLevels
92
+ */
93
+ function toTileLevels(level, x, y) {
94
+ const { extraZoomLevels } = this;
95
+ const flipped = this.maxLevel - level;
96
+ const relativeLevel = flipped - extraZoomLevels;
97
+ const sourceLevel = Math.max(relativeLevel, 0);
98
+ const extraZoom = sourceLevel - relativeLevel;
99
+ const inputTile = {
100
+ x: Math.floor(x / 2 ** extraZoom),
101
+ y: Math.floor(y / 2 ** extraZoom),
102
+ level: sourceLevel,
103
+ };
104
+ const outputTile = {
105
+ ...inputTile,
106
+ level: level - extraZoom,
107
+ };
108
+ return {
109
+ inputFullScale: 2 ** (flipped + extraZoom),
110
+ relativeImageScale: 2 ** relativeLevel,
111
+ outputFullScale: 2 ** flipped,
112
+ inputTile,
113
+ outputTile,
114
+ };
115
+ }
116
+
117
+ /**
118
+ * @function getTileUrl -- return url for tile
119
+ * @param level - openseadragon tile level
120
+ * @param x - openseadragon tile x index
121
+ * @param y - openseadragon tile y index
122
+ * @returns string
123
+ */
124
+ function getTileUrl(level, x, y) {
125
+ const s = this.toTileLevels(level, x, y).inputTile;
126
+ // Segmentation (tileFormat 32) always ignores the HD toggle -- it has
127
+ // its own fixed encoding regardless -- so its URL (and OSD's URL-keyed
128
+ // tile cache) never churns when HD is flipped.
129
+ const hdParam = this.tileFormat !== 32 && tileQuality.hd ? "?q=hd" : "";
130
+ return `${this.src}${s.level}/${s.x}_${s.y}.png${hdParam}`;
131
+ }
132
+
133
+ /**
134
+ * @function getTileKey -- return string key for tile
135
+ * @param level - openseadragon tile level
136
+ * @param x - openseadragon tile x index
137
+ * @param y - openseadragon tile y index
138
+ * @returns string
139
+ */
140
+ function getTileKey(level, x, y) {
141
+ const { srcIdx, tileFormat } = this;
142
+ const s = this.toTileLevels(level, x, y).inputTile;
143
+ return `${tileFormat}-${srcIdx}-${s.level}-${s.x}-${s.y}`;
144
+ }
145
+
146
+ /**
147
+ * @function getImagePixel -- return image pixel for screen position
148
+ * @param tiledImage - openseadragon tiled image
149
+ * @param position - screen position
150
+ * @returns array
151
+ */
152
+ function getImagePixel(tiledImage, position) {
153
+ const tileScale = 2 ** this.extraZoomLevels;
154
+ const frac = tiledImage.viewport.pointFromPixel(position);
155
+ const zoomed = tiledImage.viewportToImageCoordinates(frac);
156
+ return [zoomed.x, zoomed.y].map((v) => v / tileScale);
157
+ }
158
+
159
+ /**
160
+ * @class ViewerManager
161
+ */
162
+ export class ViewerManager {
163
+ colorConnector = {};
164
+ rangeConnector = {};
165
+
166
+ show_sel = true;
167
+ sel_outlines = false;
168
+ labelLayerRequested = false;
169
+
170
+ /**
171
+ * Constructs a ColorManager instance before delegating initialization.
172
+ *
173
+ * @param imageViewer - ImageViewer instance
174
+ * @param channelList - ChannelList instance
175
+ */
176
+ constructor(imageViewer, channelList) {
177
+ this.viewer = imageViewer.viewer;
178
+ this.imageViewer = imageViewer;
179
+ this.channelList = channelList;
180
+ }
181
+
182
+ /**
183
+ * @function init
184
+ * Setups up the color manager.
185
+ */
186
+ init() {
187
+ // Load label image
188
+ this.load_label_image();
189
+ }
190
+
191
+ /**
192
+ * @function setHdMode
193
+ * Toggle the global HD (full-precision 16-bit) tile quality on/off and
194
+ * force OpenSeadragon to re-fetch currently-loaded tiles at the new
195
+ * quality. Segmentation tiles are unaffected (see getTileUrl). Dispatches
196
+ * a DOM event so the (unbundled) sidebar can remap its per-channel range
197
+ * sliders between byte units ([0,255], default mode) and raw 16-bit
198
+ * units (HD mode) -- a plain window event because viewerSidebar.js is a
199
+ * raw <script>, not an ES module, so it can't import tileQuality here
200
+ * directly.
201
+ *
202
+ * Just flipping the flag and clearing OpenSeadragon's shared tile cache
203
+ * isn't enough: each TiledImage also keeps its own per-address Tile
204
+ * cache (tilesMatrix), and invalidating that in place left stale/
205
+ * recycled tile canvases on screen without OpenSeadragon ever
206
+ * re-invoking tile-drawing to repaint them (visible as leftover static
207
+ * from whatever was on screen at the moment of the toggle). Removing
208
+ * and re-adding every active channel sidesteps that entirely by making
209
+ * OpenSeadragon build a brand new TiledImage for each one -- the same
210
+ * thing that already happens (and reliably works) when a channel gets
211
+ * toggled off and back on.
212
+ * @param enabled - true for HD (16-bit), false for the fast/default WebP path
213
+ */
214
+ setHdMode(enabled) {
215
+ tileQuality.hd = enabled;
216
+ Object.keys(this.channelList.currentChannels).map(Number).forEach((srcIdx) => {
217
+ this.channel_remove(srcIdx);
218
+ this.channel_add(srcIdx);
219
+ });
220
+ window.dispatchEvent(new CustomEvent("plexora:hd-mode-changed", { detail: { enabled } }));
221
+ }
222
+
223
+ /**
224
+ * @function isHdMode
225
+ * @returns {boolean} whether the global HD (full-precision 16-bit) tile
226
+ * quality is currently enabled.
227
+ */
228
+ isHdMode() {
229
+ return tileQuality.hd;
230
+ }
231
+
232
+ /**
233
+ * @function channel_add
234
+ * Add channel to multi-channel rendering
235
+ * @param srcIdx - integer id of channel to add
236
+ */
237
+ channel_add(srcIdx) {
238
+ // If already exists
239
+ if (srcIdx in this.channelList.currentChannels) {
240
+ return;
241
+ }
242
+
243
+ const url = this.imageViewer.config["imageData"][srcIdx]["src"];
244
+ const { maxLevel, extraZoomLevels } = this.imageViewer.config;
245
+ const magnification = 2 ** extraZoomLevels;
246
+
247
+ // Define url and suburl
248
+ const group = url.split("/");
249
+ const sub_url = group[group.length - 2];
250
+ const range = this.channelList.rangeConnector[srcIdx];
251
+ const { color } = this.channelList.colorConnector[srcIdx] || {};
252
+ const viewerChannel = {
253
+ url: url,
254
+ sub_url: sub_url,
255
+ color: color || d3.color("white"),
256
+ range: range || this.imageViewer.numericData.bitRange,
257
+ };
258
+ this.channelList.currentChannels[srcIdx] = viewerChannel;
259
+
260
+ this.viewer.addTiledImage({
261
+ tileSource: {
262
+ height: this.imageViewer.config.height * magnification,
263
+ width: this.imageViewer.config.width * magnification,
264
+ maxLevel: extraZoomLevels + maxLevel - 1,
265
+ compositeOperation: "lighter",
266
+ tileWidth: this.imageViewer.config.tileWidth,
267
+ tileHeight: this.imageViewer.config.tileHeight,
268
+ toMagnifiedBounds: toMagnifiedBounds,
269
+ extraZoomLevels: extraZoomLevels,
270
+ toTileBoundary: toTileBoundary,
271
+ getImagePixel: getImagePixel,
272
+ toTileLevels: toTileLevels,
273
+ toIdealTile: toIdealTile,
274
+ toRealTile: toRealTile,
275
+ getTileUrl: getTileUrl,
276
+ getTileKey: getTileKey,
277
+ tileFormat: 16,
278
+ srcIdx: srcIdx,
279
+ src: url,
280
+ },
281
+ // index: 0,
282
+ opacity: 1,
283
+ preload: true,
284
+ // "open" is what wires up the GL colorize pipeline (see imageViewer.js's
285
+ // initGL, bound to the "open" handler) -- previously only raised from
286
+ // load_label_image()'s success callback, which never runs for a
287
+ // datasource with no segmentation (noLabel short-circuits it). That left
288
+ // image channels fetching real tile bytes successfully but never
289
+ // getting GL-rendered: tiles loaded, nothing drew. initGL is safe to
290
+ // run more than once, so raising it here too (redundant when a label
291
+ // image is also present) is harmless.
292
+ success: (e) => {
293
+ this.viewer.raiseEvent("open", e.item);
294
+ this.raiseLabelLayer();
295
+ },
296
+ });
297
+
298
+ }
299
+
300
+ /**
301
+ * @function channel_remove - remove channel from multichannel rendering
302
+ * @param srcIdx - integer id of channel to remove
303
+ */
304
+ channel_remove(srcIdx) {
305
+ const img_count = this.viewer.world.getItemCount();
306
+
307
+ // remove channel
308
+ if (srcIdx in this.channelList.currentChannels) {
309
+ // remove channel - first find it
310
+ for (let i = 0; i < img_count; i = i + 1) {
311
+ const url = this.viewer.world.getItemAt(i).source.src;
312
+ if (url === this.channelList.currentChannels[srcIdx]?.url) {
313
+ this.viewer.world.removeItem(this.viewer.world.getItemAt(i));
314
+ delete this.channelList.currentChannels[srcIdx];
315
+ break;
316
+ }
317
+ }
318
+ }
319
+ }
320
+
321
+
322
+ /**
323
+ * @function evaluateTF - finds color for value in transfer function
324
+ * @param val - input to transfer function
325
+ * @param tf - colors of transfer function
326
+ * @returns object
327
+ */
328
+ evaluateTF(val, tf) {
329
+ let lerpFactor = Math.round(((val - tf.min) / (tf.max - tf.min)) * (tf.num_bins - 1));
330
+
331
+ if (lerpFactor >= tf.num_bins) {
332
+ lerpFactor = tf.num_bins - 1;
333
+ }
334
+
335
+ if (lerpFactor < 0) {
336
+ lerpFactor = 0;
337
+ }
338
+
339
+ return tf.tf[lerpFactor];
340
+ }
341
+
342
+ /**
343
+ * @function force_repaint
344
+ */
345
+ force_repaint() {
346
+ // Refilter, redraw
347
+ // this.viewer.forceRefilter();
348
+ this.viewer.forceRedraw();
349
+ }
350
+
351
+ /**
352
+ * Keep the transparent segmentation layer above image channels.
353
+ */
354
+ raiseLabelLayer() {
355
+ const world = this.viewer?.world;
356
+ if (!world) return;
357
+ for (let i = 0; i < world.getItemCount(); i += 1) {
358
+ const item = world.getItemAt(i);
359
+ if (item?.source?.tileFormat == 32) {
360
+ world.setItemIndex(item, world.getItemCount() - 1);
361
+ return;
362
+ }
363
+ }
364
+ }
365
+
366
+ /**
367
+ * @function load_label_image
368
+ */
369
+ load_label_image() {
370
+ if (this.labelLayerRequested) {
371
+ return;
372
+ }
373
+ this.labelLayerRequested = true;
374
+ const self = this;
375
+
376
+ // Load label image in background if it exists. Gate on config.segmentation
377
+ // (set only when a segmentation file was actually registered), not just
378
+ // imageData[0].src -- imageData[0] is the label/"Area" channel only when
379
+ // segmentation exists; otherwise it's just the first real image channel
380
+ // (e.g. "DNA"), which always has a real src and would be wrongly loaded
381
+ // as a label layer, silently, with no error to trigger the centroid
382
+ // fallback below.
383
+ if (this.imageViewer.config["segmentation"] && this.imageViewer.config["imageData"]?.[0]?.["src"]) {
384
+ let url = this.imageViewer.config["imageData"][0]["src"];
385
+ const { maxLevel, extraZoomLevels } = this.imageViewer.config;
386
+ const magnification = 2 ** extraZoomLevels;
387
+ this.viewer.addTiledImage({
388
+ tileSource: {
389
+ height: this.imageViewer.config.height * magnification,
390
+ width: this.imageViewer.config.width * magnification,
391
+ maxLevel: extraZoomLevels + maxLevel - 1,
392
+ maxImageCacheCount: 50,
393
+ compositeOperation: "source-over",
394
+ tileWidth: this.imageViewer.config.tileWidth,
395
+ tileHeight: this.imageViewer.config.tileHeight,
396
+ toMagnifiedBounds: toMagnifiedBounds,
397
+ extraZoomLevels: extraZoomLevels,
398
+ toTileBoundary: toTileBoundary,
399
+ getImagePixel: getImagePixel,
400
+ toTileLevels: toTileLevels,
401
+ toIdealTile: toIdealTile,
402
+ toRealTile: toRealTile,
403
+ getTileUrl: getTileUrl,
404
+ getTileKey: getTileKey,
405
+ tileFormat: 32,
406
+ srcIdx: 0,
407
+ src: url,
408
+ },
409
+ opacity: 1,
410
+ success: (e) => {
411
+ // The GL layer initializes on 'open', so raise it here.
412
+ self.viewer.raiseEvent("open", e.item);
413
+ self.raiseLabelLayer();
414
+ },
415
+ error: () => {
416
+ this.imageViewer.noLabel = true;
417
+ if (PlexoraDataset.hasCentroids(this.imageViewer.config)) {
418
+ this.imageViewer.updateCentroidFallback(true);
419
+ }
420
+ },
421
+ });
422
+ } else {
423
+ this.imageViewer.noLabel = true;
424
+ // A project with no table -- or one whose coordinate columns are
425
+ // still unidentified -- has no per-cell positions. Falling back to
426
+ // centroids there would round-trip to the server for a manifest
427
+ // that was never going to have any points, delaying load for
428
+ // nothing.
429
+ if (PlexoraDataset.hasCentroids(this.imageViewer.config)) {
430
+ this.imageViewer.updateCentroidFallback(true);
431
+ }
432
+ }
433
+ }
434
+ }