@genome-spy/core 0.64.0 → 0.66.1
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/dist/bundle/{index-CCJIjehY.js → AbortablePromiseCache-CcuMrnn7.js} +22 -91
- package/dist/bundle/browser-BRemItdO.js +138 -0
- package/dist/bundle/index-BatuyGAI.js +271 -0
- package/dist/bundle/index-ByuE8dvu.js +332 -0
- package/dist/bundle/index-Cq3QFUxX.js +1781 -0
- package/dist/bundle/{index-C08YCM2T.js → index-D-w7Mmt9.js} +246 -126
- package/dist/bundle/index-D28m8tSW.js +1607 -0
- package/dist/bundle/index-D74H8TTz.js +508 -0
- package/dist/bundle/index-DbJ0oeYM.js +631 -0
- package/dist/bundle/index.es.js +15034 -13842
- package/dist/bundle/index.js +223 -237
- package/dist/bundle/inflate-GtwLkvSP.js +1048 -0
- package/dist/bundle/unzip-NywezaRR.js +1492 -0
- package/dist/schema.json +22 -4
- package/dist/src/config/scaleDefaults.d.ts +8 -0
- package/dist/src/config/scaleDefaults.d.ts.map +1 -0
- package/dist/src/config/scaleDefaults.js +45 -0
- package/dist/src/data/collector.d.ts +7 -2
- package/dist/src/data/collector.d.ts.map +1 -1
- package/dist/src/data/collector.js +13 -2
- package/dist/src/data/dataFlow.d.ts +20 -42
- package/dist/src/data/dataFlow.d.ts.map +1 -1
- package/dist/src/data/dataFlow.js +57 -80
- package/dist/src/data/flowHandle.d.ts +15 -0
- package/dist/src/data/flowHandle.d.ts.map +1 -0
- package/dist/src/data/flowHandle.js +13 -0
- package/dist/src/data/flowInit.d.ts +85 -0
- package/dist/src/data/flowInit.d.ts.map +1 -0
- package/dist/src/data/flowInit.js +238 -0
- package/dist/src/data/flowInit.test.d.ts +2 -0
- package/dist/src/data/flowInit.test.d.ts.map +1 -0
- package/dist/src/data/flowOptimizer.d.ts +6 -4
- package/dist/src/data/flowOptimizer.d.ts.map +1 -1
- package/dist/src/data/flowOptimizer.js +29 -14
- package/dist/src/data/sources/lazy/axisTickSource.js +1 -1
- package/dist/src/data/sources/lazy/bamSource.js +1 -1
- package/dist/src/data/sources/lazy/bigBedSource.js +1 -1
- package/dist/src/data/sources/lazy/bigWigSource.js +1 -1
- package/dist/src/data/sources/lazy/gff3Source.d.ts +2 -6
- package/dist/src/data/sources/lazy/gff3Source.d.ts.map +1 -1
- package/dist/src/data/sources/lazy/gff3Source.js +4 -8
- package/dist/src/data/sources/lazy/indexedFastaSource.d.ts.map +1 -1
- package/dist/src/data/sources/lazy/indexedFastaSource.js +17 -17
- package/dist/src/data/sources/lazy/singleAxisLazySource.d.ts +1 -1
- package/dist/src/data/sources/lazy/singleAxisLazySource.d.ts.map +1 -1
- package/dist/src/data/sources/lazy/singleAxisLazySource.js +10 -3
- package/dist/src/data/sources/lazy/singleAxisWindowedSource.d.ts.map +1 -1
- package/dist/src/data/sources/lazy/singleAxisWindowedSource.js +5 -1
- package/dist/src/data/sources/lazy/tabixSource.js +1 -1
- package/dist/src/data/transforms/filterScoredLabels.d.ts +1 -1
- package/dist/src/data/transforms/filterScoredLabels.d.ts.map +1 -1
- package/dist/src/data/transforms/filterScoredLabels.js +1 -1
- package/dist/src/data/transforms/linearizeGenomicCoordinate.d.ts.map +1 -1
- package/dist/src/data/transforms/linearizeGenomicCoordinate.js +2 -1
- package/dist/src/encoder/encoder.d.ts +1 -1
- package/dist/src/encoder/encoder.d.ts.map +1 -1
- package/dist/src/encoder/encoder.js +1 -1
- package/dist/src/genome/scaleLocus.d.ts +39 -0
- package/dist/src/genome/scaleLocus.d.ts.map +1 -1
- package/dist/src/genome/scaleLocus.js +76 -0
- package/dist/src/genomeSpy/canvasExport.d.ts +19 -0
- package/dist/src/genomeSpy/canvasExport.d.ts.map +1 -0
- package/dist/src/genomeSpy/canvasExport.js +66 -0
- package/dist/src/genomeSpy/containerUi.d.ts +17 -0
- package/dist/src/genomeSpy/containerUi.d.ts.map +1 -0
- package/dist/src/genomeSpy/containerUi.js +78 -0
- package/dist/src/genomeSpy/eventListenerRegistry.d.ts +19 -0
- package/dist/src/genomeSpy/eventListenerRegistry.d.ts.map +1 -0
- package/dist/src/genomeSpy/eventListenerRegistry.js +38 -0
- package/dist/src/genomeSpy/inputBindingManager.d.ts +14 -0
- package/dist/src/genomeSpy/inputBindingManager.d.ts.map +1 -0
- package/dist/src/genomeSpy/inputBindingManager.js +63 -0
- package/dist/src/genomeSpy/interactionController.d.ts +40 -0
- package/dist/src/genomeSpy/interactionController.d.ts.map +1 -0
- package/dist/src/genomeSpy/interactionController.js +371 -0
- package/dist/src/genomeSpy/keyboardListenerManager.d.ts +10 -0
- package/dist/src/genomeSpy/keyboardListenerManager.d.ts.map +1 -0
- package/dist/src/genomeSpy/keyboardListenerManager.js +31 -0
- package/dist/src/genomeSpy/loadingIndicatorManager.d.ts +15 -0
- package/dist/src/genomeSpy/loadingIndicatorManager.d.ts.map +1 -0
- package/dist/src/genomeSpy/loadingIndicatorManager.js +92 -0
- package/dist/src/genomeSpy/renderCoordinator.d.ts +22 -0
- package/dist/src/genomeSpy/renderCoordinator.d.ts.map +1 -0
- package/dist/src/genomeSpy/renderCoordinator.js +118 -0
- package/dist/src/genomeSpy/viewContextFactory.d.ts +18 -0
- package/dist/src/genomeSpy/viewContextFactory.d.ts.map +1 -0
- package/dist/src/genomeSpy/viewContextFactory.js +79 -0
- package/dist/src/genomeSpy/viewDataInit.d.ts +12 -0
- package/dist/src/genomeSpy/viewDataInit.d.ts.map +1 -0
- package/dist/src/genomeSpy/viewDataInit.js +41 -0
- package/dist/src/genomeSpy/viewHierarchyConfig.d.ts +14 -0
- package/dist/src/genomeSpy/viewHierarchyConfig.d.ts.map +1 -0
- package/dist/src/genomeSpy/viewHierarchyConfig.js +24 -0
- package/dist/src/genomeSpy/viewHighlight.d.ts +5 -0
- package/dist/src/genomeSpy/viewHighlight.d.ts.map +1 -0
- package/dist/src/genomeSpy/viewHighlight.js +30 -0
- package/dist/src/genomeSpy.d.ts +17 -72
- package/dist/src/genomeSpy.d.ts.map +1 -1
- package/dist/src/genomeSpy.js +180 -789
- package/dist/src/gl/glslScaleGenerator.d.ts +1 -1
- package/dist/src/gl/webGLHelper.d.ts +2 -2
- package/dist/src/gl/webGLHelper.d.ts.map +1 -1
- package/dist/src/gl/webGLHelper.js +4 -4
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +2 -12
- package/dist/src/marks/mark.d.ts +1 -0
- package/dist/src/marks/mark.d.ts.map +1 -1
- package/dist/src/marks/mark.js +26 -3
- package/dist/src/{view → scales}/axisResolution.d.ts +12 -14
- package/dist/src/scales/axisResolution.d.ts.map +1 -0
- package/dist/src/{view → scales}/axisResolution.js +38 -12
- package/dist/src/scales/axisResolution.test.d.ts.map +1 -0
- package/dist/src/scales/scaleDomainAggregator.d.ts +57 -0
- package/dist/src/scales/scaleDomainAggregator.d.ts.map +1 -0
- package/dist/src/scales/scaleDomainAggregator.js +162 -0
- package/dist/src/scales/scaleDomainAggregator.test.d.ts +2 -0
- package/dist/src/scales/scaleDomainAggregator.test.d.ts.map +1 -0
- package/dist/src/scales/scaleInstanceManager.d.ts +40 -0
- package/dist/src/scales/scaleInstanceManager.d.ts.map +1 -0
- package/dist/src/scales/scaleInstanceManager.js +313 -0
- package/dist/src/scales/scaleInstanceManager.test.d.ts +2 -0
- package/dist/src/scales/scaleInstanceManager.test.d.ts.map +1 -0
- package/dist/src/scales/scaleInteractionController.d.ts +73 -0
- package/dist/src/scales/scaleInteractionController.d.ts.map +1 -0
- package/dist/src/scales/scaleInteractionController.js +336 -0
- package/dist/src/scales/scaleInteractionController.test.d.ts +2 -0
- package/dist/src/scales/scaleInteractionController.test.d.ts.map +1 -0
- package/dist/src/scales/scalePropsResolver.d.ts +23 -0
- package/dist/src/scales/scalePropsResolver.d.ts.map +1 -0
- package/dist/src/scales/scalePropsResolver.js +74 -0
- package/dist/src/{view → scales}/scaleResolution.d.ts +53 -31
- package/dist/src/scales/scaleResolution.d.ts.map +1 -0
- package/dist/src/scales/scaleResolution.js +658 -0
- package/dist/src/scales/scaleResolution.test.d.ts.map +1 -0
- package/dist/src/scales/scaleResolutionConstants.d.ts +6 -0
- package/dist/src/scales/scaleResolutionConstants.d.ts.map +1 -0
- package/dist/src/scales/scaleResolutionConstants.js +5 -0
- package/dist/src/scales/scaleRules.d.ts +16 -0
- package/dist/src/scales/scaleRules.d.ts.map +1 -0
- package/dist/src/scales/scaleRules.js +103 -0
- package/dist/src/scales/scaleRules.test.d.ts +2 -0
- package/dist/src/scales/scaleRules.test.d.ts.map +1 -0
- package/dist/src/spec/channel.d.ts +13 -18
- package/dist/src/spec/sampleView.d.ts +3 -2
- package/dist/src/spec/scale.d.ts +6 -0
- package/dist/src/types/embedApi.d.ts +5 -0
- package/dist/src/types/scaleResolutionApi.d.ts +1 -1
- package/dist/src/types/viewContext.d.ts +1 -1
- package/dist/src/view/concatView.d.ts +18 -0
- package/dist/src/view/concatView.d.ts.map +1 -1
- package/dist/src/view/concatView.js +73 -0
- package/dist/src/view/concatView.test.d.ts +2 -0
- package/dist/src/view/concatView.test.d.ts.map +1 -0
- package/dist/src/view/containerMutationHelper.d.ts +74 -0
- package/dist/src/view/containerMutationHelper.d.ts.map +1 -0
- package/dist/src/view/containerMutationHelper.js +114 -0
- package/dist/src/view/containerView.d.ts +0 -7
- package/dist/src/view/containerView.d.ts.map +1 -1
- package/dist/src/view/containerView.js +0 -10
- package/dist/src/view/facetView.d.ts.map +1 -1
- package/dist/src/view/facetView.js +0 -14
- package/dist/src/view/flowBuilder.d.ts +2 -2
- package/dist/src/view/flowBuilder.d.ts.map +1 -1
- package/dist/src/view/flowBuilder.js +21 -4
- package/dist/src/view/gridView/gridChild.d.ts +11 -0
- package/dist/src/view/gridView/gridChild.d.ts.map +1 -1
- package/dist/src/view/gridView/gridChild.js +32 -6
- package/dist/src/view/gridView/gridView.d.ts +39 -1
- package/dist/src/view/gridView/gridView.d.ts.map +1 -1
- package/dist/src/view/gridView/gridView.js +113 -42
- package/dist/src/view/gridView/gridView.test.d.ts +2 -0
- package/dist/src/view/gridView/gridView.test.d.ts.map +1 -0
- package/dist/src/view/gridView/scrollbar.d.ts +39 -8
- package/dist/src/view/gridView/scrollbar.d.ts.map +1 -1
- package/dist/src/view/gridView/scrollbar.js +184 -69
- package/dist/src/view/gridView/selectionRect.d.ts +8 -4
- package/dist/src/view/gridView/selectionRect.d.ts.map +1 -1
- package/dist/src/view/gridView/selectionRect.js +28 -3
- package/dist/src/view/gridView/selectionRect.test.d.ts +2 -0
- package/dist/src/view/gridView/selectionRect.test.d.ts.map +1 -0
- package/dist/src/view/layerView.d.ts +14 -0
- package/dist/src/view/layerView.d.ts.map +1 -1
- package/dist/src/view/layerView.js +66 -0
- package/dist/src/view/layerView.test.d.ts +2 -0
- package/dist/src/view/layerView.test.d.ts.map +1 -0
- package/dist/src/view/paramMediator.d.ts +2 -1
- package/dist/src/view/paramMediator.d.ts.map +1 -1
- package/dist/src/view/paramMediator.js +13 -1
- package/dist/src/view/testUtils.d.ts.map +1 -1
- package/dist/src/view/testUtils.js +18 -5
- package/dist/src/view/unitView.d.ts.map +1 -1
- package/dist/src/view/unitView.js +52 -12
- package/dist/src/view/view.d.ts +23 -7
- package/dist/src/view/view.d.ts.map +1 -1
- package/dist/src/view/view.js +61 -5
- package/dist/src/view/viewDispose.test.d.ts +2 -0
- package/dist/src/view/viewDispose.test.d.ts.map +1 -0
- package/dist/src/view/viewUtils.d.ts +4 -4
- package/dist/src/view/viewUtils.d.ts.map +1 -1
- package/dist/src/view/viewUtils.js +19 -15
- package/dist/src/view/viewUtils.test.d.ts +2 -0
- package/dist/src/view/viewUtils.test.d.ts.map +1 -0
- package/package.json +10 -10
- package/dist/bundle/__vite-browser-external-C--ziKoh.js +0 -8
- package/dist/bundle/_commonjsHelpers-DjF3Plf2.js +0 -26
- package/dist/bundle/index-5ajWdKly.js +0 -1319
- package/dist/bundle/index-B03-Om4z.js +0 -274
- package/dist/bundle/index-BftNdA0O.js +0 -27
- package/dist/bundle/index-Bg7C4Xat.js +0 -2750
- package/dist/bundle/index-C3QR8Lv6.js +0 -2131
- package/dist/bundle/index-DTcHjAHp.js +0 -505
- package/dist/bundle/index-DnIkxb0L.js +0 -1025
- package/dist/bundle/index-Ww3TAo6_.js +0 -71
- package/dist/bundle/index-g8iXgW0W.js +0 -651
- package/dist/bundle/long-B-FASCSo.js +0 -2387
- package/dist/bundle/remoteFile-BuaqFGWk.js +0 -94
- package/dist/src/data/collector.test.js +0 -138
- package/dist/src/data/dataFlow.test.js +0 -5
- package/dist/src/data/flow.test.js +0 -81
- package/dist/src/data/flowNode.test.js +0 -50
- package/dist/src/data/flowOptimizer.test.js +0 -204
- package/dist/src/data/formats/fasta.test.js +0 -27
- package/dist/src/data/sources/inlineSource.test.js +0 -63
- package/dist/src/data/sources/sequenceSource.test.js +0 -81
- package/dist/src/data/transforms/aggregate.test.js +0 -134
- package/dist/src/data/transforms/clone.test.js +0 -11
- package/dist/src/data/transforms/coverage.test.js +0 -238
- package/dist/src/data/transforms/filter.test.js +0 -20
- package/dist/src/data/transforms/flatten.test.js +0 -96
- package/dist/src/data/transforms/flattenDelimited.test.js +0 -90
- package/dist/src/data/transforms/flattenSequence.test.js +0 -34
- package/dist/src/data/transforms/formula.test.js +0 -25
- package/dist/src/data/transforms/identifier.test.js +0 -92
- package/dist/src/data/transforms/pileup.test.js +0 -70
- package/dist/src/data/transforms/project.test.js +0 -32
- package/dist/src/data/transforms/regexExtract.test.js +0 -70
- package/dist/src/data/transforms/regexFold.test.js +0 -201
- package/dist/src/data/transforms/sample.test.js +0 -38
- package/dist/src/data/transforms/stack.test.js +0 -91
- package/dist/src/encoder/accessor.test.js +0 -162
- package/dist/src/encoder/encoder.test.js +0 -105
- package/dist/src/genome/genome.test.js +0 -268
- package/dist/src/genome/genomes.test.js +0 -8
- package/dist/src/genome/scaleIndex.test.js +0 -78
- package/dist/src/genome/scaleLocus.test.js +0 -4
- package/dist/src/scale/scale.test.js +0 -326
- package/dist/src/scale/ticks.test.js +0 -46
- package/dist/src/selection/selection.test.js +0 -14
- package/dist/src/utils/addBaseUrl.test.js +0 -30
- package/dist/src/utils/binnedIndex.test.js +0 -201
- package/dist/src/utils/cloner.test.js +0 -35
- package/dist/src/utils/coalesce.test.js +0 -16
- package/dist/src/utils/concatIterables.test.js +0 -8
- package/dist/src/utils/domainArray.test.js +0 -130
- package/dist/src/utils/indexer.test.js +0 -49
- package/dist/src/utils/interactionEvent.test.js +0 -35
- package/dist/src/utils/iterateNestedMaps.test.js +0 -33
- package/dist/src/utils/kWayMerge.test.js +0 -30
- package/dist/src/utils/mergeObjects.test.js +0 -42
- package/dist/src/utils/numberExtractor.test.js +0 -6
- package/dist/src/utils/propertyCacher.test.js +0 -89
- package/dist/src/utils/propertyCoalescer.test.js +0 -25
- package/dist/src/utils/radixSort.test.js +0 -51
- package/dist/src/utils/reservationMap.test.js +0 -20
- package/dist/src/utils/ringBuffer.test.js +0 -39
- package/dist/src/utils/topK.test.js +0 -54
- package/dist/src/utils/trees.test.js +0 -135
- package/dist/src/utils/url.test.js +0 -28
- package/dist/src/utils/variableTools.test.js +0 -13
- package/dist/src/view/axisResolution.d.ts.map +0 -1
- package/dist/src/view/axisResolution.test.d.ts.map +0 -1
- package/dist/src/view/axisResolution.test.js +0 -206
- package/dist/src/view/flowBuilder.test.js +0 -125
- package/dist/src/view/layout/flexLayout.test.js +0 -323
- package/dist/src/view/layout/grid.test.js +0 -71
- package/dist/src/view/layout/rectangle.test.js +0 -192
- package/dist/src/view/paramMediator.test.js +0 -260
- package/dist/src/view/scaleResolution.d.ts.map +0 -1
- package/dist/src/view/scaleResolution.js +0 -1049
- package/dist/src/view/scaleResolution.test.d.ts.map +0 -1
- package/dist/src/view/scaleResolution.test.js +0 -645
- package/dist/src/view/view.test.js +0 -245
- package/dist/src/view/viewFactory.test.js +0 -25
- /package/dist/src/{view → scales}/axisResolution.test.d.ts +0 -0
- /package/dist/src/{view → scales}/scaleResolution.test.d.ts +0 -0
package/dist/src/genomeSpy.js
CHANGED
|
@@ -1,53 +1,63 @@
|
|
|
1
1
|
import { formats as vegaFormats } from "vega-loader";
|
|
2
|
-
import { html, nothing, render } from "lit";
|
|
3
|
-
import { styleMap } from "lit/directives/style-map.js";
|
|
4
|
-
import SPINNER from "./img/90-ring-with-bg.svg";
|
|
5
|
-
|
|
6
|
-
import css from "./styles/genome-spy.css.js";
|
|
7
|
-
import Tooltip from "./utils/ui/tooltip.js";
|
|
8
2
|
|
|
9
3
|
import {
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
4
|
+
createContainerUi,
|
|
5
|
+
createMessageBox,
|
|
6
|
+
} from "./genomeSpy/containerUi.js";
|
|
7
|
+
import LoadingIndicatorManager from "./genomeSpy/loadingIndicatorManager.js";
|
|
8
|
+
import { createViewHighlighter } from "./genomeSpy/viewHighlight.js";
|
|
9
|
+
import KeyboardListenerManager from "./genomeSpy/keyboardListenerManager.js";
|
|
10
|
+
import EventListenerRegistry from "./genomeSpy/eventListenerRegistry.js";
|
|
11
|
+
import InputBindingManager from "./genomeSpy/inputBindingManager.js";
|
|
12
|
+
|
|
13
|
+
import { calculateCanvasSize } from "./view/viewUtils.js";
|
|
14
|
+
import { initializeViewData } from "./genomeSpy/viewDataInit.js";
|
|
14
15
|
import UnitView from "./view/unitView.js";
|
|
15
16
|
|
|
16
|
-
import WebGLHelper
|
|
17
|
-
framebufferToDataUrl,
|
|
18
|
-
readPickingPixel,
|
|
19
|
-
} from "./gl/webGLHelper.js";
|
|
20
|
-
import Rectangle from "./view/layout/rectangle.js";
|
|
21
|
-
import BufferedViewRenderingContext from "./view/renderingContext/bufferedViewRenderingContext.js";
|
|
22
|
-
import CompositeViewRenderingContext from "./view/renderingContext/compositeViewRenderingContext.js";
|
|
23
|
-
import InteractionEvent from "./utils/interactionEvent.js";
|
|
24
|
-
import Point from "./view/layout/point.js";
|
|
17
|
+
import WebGLHelper from "./gl/webGLHelper.js";
|
|
25
18
|
import Animator from "./utils/animator.js";
|
|
26
19
|
import DataFlow from "./data/dataFlow.js";
|
|
27
|
-
import { buildDataFlow } from "./view/flowBuilder.js";
|
|
28
|
-
import { optimizeDataFlow } from "./data/flowOptimizer.js";
|
|
29
20
|
import GenomeStore from "./genome/genomeStore.js";
|
|
30
21
|
import BmFontManager from "./fonts/bmFontManager.js";
|
|
31
22
|
import fasta from "./data/formats/fasta.js";
|
|
32
|
-
import { VISIT_STOP } from "./view/view.js";
|
|
33
|
-
import Inertia, { makeEventTemplate } from "./utils/inertia.js";
|
|
34
23
|
import refseqGeneTooltipHandler from "./tooltip/refseqGeneTooltipHandler.js";
|
|
35
24
|
import dataTooltipHandler from "./tooltip/dataTooltipHandler.js";
|
|
36
25
|
import { invalidatePrefix } from "./utils/propertyCacher.js";
|
|
37
26
|
import { VIEW_ROOT_NAME, ViewFactory } from "./view/viewFactory.js";
|
|
38
|
-
import
|
|
39
|
-
import
|
|
40
|
-
import {
|
|
41
|
-
import {
|
|
27
|
+
import InteractionController from "./genomeSpy/interactionController.js";
|
|
28
|
+
import RenderCoordinator from "./genomeSpy/renderCoordinator.js";
|
|
29
|
+
import { createViewContext } from "./genomeSpy/viewContextFactory.js";
|
|
30
|
+
import {
|
|
31
|
+
configureViewHierarchy,
|
|
32
|
+
configureViewOpacity,
|
|
33
|
+
} from "./genomeSpy/viewHierarchyConfig.js";
|
|
34
|
+
import { exportCanvas } from "./genomeSpy/canvasExport.js";
|
|
42
35
|
|
|
43
36
|
/**
|
|
44
37
|
* Events that are broadcasted to all views.
|
|
45
|
-
* @typedef {"dataFlowBuilt" | "
|
|
38
|
+
* @typedef {"dataFlowBuilt" | "layout" | "layoutComputed" | "subtreeDataReady"} BroadcastEventType
|
|
46
39
|
*/
|
|
47
40
|
|
|
48
41
|
vegaFormats("fasta", fasta);
|
|
49
42
|
|
|
50
43
|
export default class GenomeSpy {
|
|
44
|
+
/** @type {(() => void)[]} */
|
|
45
|
+
#destructionCallbacks = [];
|
|
46
|
+
/** @type {RenderCoordinator} */
|
|
47
|
+
#renderCoordinator;
|
|
48
|
+
/** @type {LoadingIndicatorManager} */
|
|
49
|
+
#loadingIndicatorManager;
|
|
50
|
+
/** @type {InputBindingManager} */
|
|
51
|
+
#inputBindingManager;
|
|
52
|
+
/** @type {InteractionController} */
|
|
53
|
+
#interactionController;
|
|
54
|
+
/** @type {WebGLHelper} */
|
|
55
|
+
#glHelper;
|
|
56
|
+
|
|
57
|
+
#keyboardListenerManager = new KeyboardListenerManager();
|
|
58
|
+
#eventListeners = new EventListenerRegistry();
|
|
59
|
+
#extraBroadcastListeners = new EventListenerRegistry();
|
|
60
|
+
|
|
51
61
|
/**
|
|
52
62
|
* @typedef {import("./view/view.js").default} View
|
|
53
63
|
* @typedef {import("./spec/view.js").ViewSpec} ViewSpec
|
|
@@ -67,9 +77,6 @@ export default class GenomeSpy {
|
|
|
67
77
|
|
|
68
78
|
options.inputBindingContainer ??= "default";
|
|
69
79
|
|
|
70
|
-
/** @type {(() => void)[]} */
|
|
71
|
-
this._destructionCallbacks = [];
|
|
72
|
-
|
|
73
80
|
/** Root level configuration object */
|
|
74
81
|
this.spec = spec;
|
|
75
82
|
|
|
@@ -91,43 +98,6 @@ export default class GenomeSpy {
|
|
|
91
98
|
*/
|
|
92
99
|
this.viewVisibilityPredicate = (view) => view.isVisibleInSpec();
|
|
93
100
|
|
|
94
|
-
/** @type {BufferedViewRenderingContext} */
|
|
95
|
-
this._renderingContext = undefined;
|
|
96
|
-
/** @type {BufferedViewRenderingContext} */
|
|
97
|
-
this._pickingContext = undefined;
|
|
98
|
-
|
|
99
|
-
/** Does picking buffer need to be rendered again */
|
|
100
|
-
this._dirtyPickingBuffer = false;
|
|
101
|
-
|
|
102
|
-
/**
|
|
103
|
-
* Currently hovered mark and datum
|
|
104
|
-
* @type {{ mark: import("./marks/mark.js").default, datum: import("./data/flowNode.js").Datum, uniqueId: number }}
|
|
105
|
-
*/
|
|
106
|
-
this._currentHover = undefined;
|
|
107
|
-
|
|
108
|
-
this._wheelInertia = new Inertia(this.animator);
|
|
109
|
-
|
|
110
|
-
/**
|
|
111
|
-
* Keeping track so that these can be cleaned up upon finalization.
|
|
112
|
-
* @type {Map<string, (function(KeyboardEvent):void)[]>}
|
|
113
|
-
*/
|
|
114
|
-
this._keyboardListeners = new Map();
|
|
115
|
-
|
|
116
|
-
/**
|
|
117
|
-
* Listers for exposed high-level events such as click on a mark instance.
|
|
118
|
-
* These should probably be in the View class and support bubbling through
|
|
119
|
-
* the hierarchy.
|
|
120
|
-
*
|
|
121
|
-
* @type {Map<string, Set<(event: any) => void>>}
|
|
122
|
-
*/
|
|
123
|
-
this._eventListeners = new Map();
|
|
124
|
-
|
|
125
|
-
/**
|
|
126
|
-
*
|
|
127
|
-
* @type {Map<string, Set<(event: any) => void>>}
|
|
128
|
-
*/
|
|
129
|
-
this._extraBroadcastListeners = new Map();
|
|
130
|
-
|
|
131
101
|
/** @type {Record<string, import("./tooltip/tooltipHandler.js").TooltipHandler>}> */
|
|
132
102
|
this.tooltipHandlers = {
|
|
133
103
|
default: dataTooltipHandler,
|
|
@@ -138,20 +108,7 @@ export default class GenomeSpy {
|
|
|
138
108
|
/** @type {View} */
|
|
139
109
|
this.viewRoot = undefined;
|
|
140
110
|
|
|
141
|
-
|
|
142
|
-
* Views that are currently loading data using lazy sources.
|
|
143
|
-
*
|
|
144
|
-
* @type {Map<View, { status: import("./types/viewContext.js").DataLoadingStatus, detail?: string }>}
|
|
145
|
-
*/
|
|
146
|
-
this._loadingViews = new Map();
|
|
147
|
-
|
|
148
|
-
/**
|
|
149
|
-
* @type {HTMLElement}
|
|
150
|
-
*/
|
|
151
|
-
this._inputBindingContainer = undefined;
|
|
152
|
-
|
|
153
|
-
/** @type {Point} */
|
|
154
|
-
this._mouseDownCoords = undefined;
|
|
111
|
+
this.#inputBindingManager = new InputBindingManager(container, options);
|
|
155
112
|
|
|
156
113
|
this.dpr = window.devicePixelRatio;
|
|
157
114
|
}
|
|
@@ -163,37 +120,7 @@ export default class GenomeSpy {
|
|
|
163
120
|
}
|
|
164
121
|
|
|
165
122
|
#initializeParameterBindings() {
|
|
166
|
-
|
|
167
|
-
const inputs = [];
|
|
168
|
-
|
|
169
|
-
this.viewRoot.visit((view) => {
|
|
170
|
-
const mediator = view.paramMediator;
|
|
171
|
-
inputs.push(...createBindingInputs(mediator));
|
|
172
|
-
});
|
|
173
|
-
const ibc = this.options.inputBindingContainer;
|
|
174
|
-
|
|
175
|
-
if (!ibc || ibc == "none" || !inputs.length) {
|
|
176
|
-
return;
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
this._inputBindingContainer = element("div", {
|
|
180
|
-
className: "gs-input-bindings",
|
|
181
|
-
});
|
|
182
|
-
|
|
183
|
-
if (ibc == "default") {
|
|
184
|
-
this.container.appendChild(this._inputBindingContainer);
|
|
185
|
-
} else if (ibc instanceof HTMLElement) {
|
|
186
|
-
ibc.appendChild(this._inputBindingContainer);
|
|
187
|
-
} else {
|
|
188
|
-
throw new Error("Invalid inputBindingContainer");
|
|
189
|
-
}
|
|
190
|
-
|
|
191
|
-
if (inputs.length) {
|
|
192
|
-
render(
|
|
193
|
-
html`<div class="gs-input-binding">${inputs}</div>`,
|
|
194
|
-
this._inputBindingContainer
|
|
195
|
-
);
|
|
196
|
-
}
|
|
123
|
+
this.#inputBindingManager.initialize(this.viewRoot);
|
|
197
124
|
}
|
|
198
125
|
|
|
199
126
|
/**
|
|
@@ -229,11 +156,26 @@ export default class GenomeSpy {
|
|
|
229
156
|
}
|
|
230
157
|
|
|
231
158
|
namedSource.dataSource.updateDynamicData(data);
|
|
232
|
-
reconfigureScales(namedSource.hosts);
|
|
233
159
|
|
|
234
160
|
this.animator.requestRender();
|
|
235
161
|
}
|
|
236
162
|
|
|
163
|
+
/**
|
|
164
|
+
* @param {string} type
|
|
165
|
+
* @param {(event: any) => void} listener
|
|
166
|
+
*/
|
|
167
|
+
addEventListener(type, listener) {
|
|
168
|
+
this.#eventListeners.add(type, listener);
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
/**
|
|
172
|
+
* @param {string} type
|
|
173
|
+
* @param {(event: any) => void} listener
|
|
174
|
+
*/
|
|
175
|
+
removeEventListener(type, listener) {
|
|
176
|
+
this.#eventListeners.remove(type, listener);
|
|
177
|
+
}
|
|
178
|
+
|
|
237
179
|
/**
|
|
238
180
|
* Broadcast a message to all views
|
|
239
181
|
|
|
@@ -243,71 +185,7 @@ export default class GenomeSpy {
|
|
|
243
185
|
broadcast(type, payload) {
|
|
244
186
|
const message = { type, payload };
|
|
245
187
|
this.viewRoot.visit((view) => view.handleBroadcast(message));
|
|
246
|
-
this.
|
|
247
|
-
.get(type)
|
|
248
|
-
?.forEach((listener) => listener(message));
|
|
249
|
-
}
|
|
250
|
-
|
|
251
|
-
/**
|
|
252
|
-
* Draw some layers on top of the canvas. It's easier to do fancy spinning
|
|
253
|
-
* animations with html elements than with WebGL.
|
|
254
|
-
*/
|
|
255
|
-
_updateLoadingIndicators() {
|
|
256
|
-
/** @type {import("lit").TemplateResult[]} */
|
|
257
|
-
const indicators = [];
|
|
258
|
-
|
|
259
|
-
const isSomethingVisible = () =>
|
|
260
|
-
[...this._loadingViews.values()].some(
|
|
261
|
-
(v) => v.status == "loading" || v.status == "error"
|
|
262
|
-
);
|
|
263
|
-
|
|
264
|
-
for (const [view, status] of this._loadingViews) {
|
|
265
|
-
const c = view.coords;
|
|
266
|
-
if (c) {
|
|
267
|
-
const style = {
|
|
268
|
-
left: `${c.x}px`,
|
|
269
|
-
top: `${c.y}px`,
|
|
270
|
-
width: `${c.width}px`,
|
|
271
|
-
height: `${c.height}px`,
|
|
272
|
-
};
|
|
273
|
-
indicators.push(
|
|
274
|
-
html`<div style=${styleMap(style)}>
|
|
275
|
-
<div class=${status.status}>
|
|
276
|
-
${status.status == "error"
|
|
277
|
-
? html`<span
|
|
278
|
-
>Loading
|
|
279
|
-
failed${status.detail
|
|
280
|
-
? html`: ${status.detail}`
|
|
281
|
-
: nothing}</span
|
|
282
|
-
>`
|
|
283
|
-
: html`
|
|
284
|
-
<img src="${SPINNER}" alt="" />
|
|
285
|
-
<span>Loading...</span>
|
|
286
|
-
`}
|
|
287
|
-
</div>
|
|
288
|
-
</div>`
|
|
289
|
-
);
|
|
290
|
-
}
|
|
291
|
-
}
|
|
292
|
-
|
|
293
|
-
// Do some hacks to stop css animations of the loading indicators.
|
|
294
|
-
// Otherwise they fire animation frames even when their opacity is zero.
|
|
295
|
-
// TODO: Instead of this, replace the animated spinners with static images.
|
|
296
|
-
// Or even better, once more widely supported, use `allow-discrete`
|
|
297
|
-
// https://developer.mozilla.org/en-US/docs/Web/CSS/transition-behavior
|
|
298
|
-
// to enable transition of the display property.
|
|
299
|
-
if (isSomethingVisible()) {
|
|
300
|
-
this.loadingIndicatorsElement.style.display = "block";
|
|
301
|
-
} else {
|
|
302
|
-
// TODO: Clear previous timeout
|
|
303
|
-
setTimeout(() => {
|
|
304
|
-
if (!isSomethingVisible()) {
|
|
305
|
-
this.loadingIndicatorsElement.style.display = "none";
|
|
306
|
-
}
|
|
307
|
-
}, 3000);
|
|
308
|
-
}
|
|
309
|
-
|
|
310
|
-
render(indicators, this.loadingIndicatorsElement);
|
|
188
|
+
this.#extraBroadcastListeners.emit(type, message);
|
|
311
189
|
}
|
|
312
190
|
|
|
313
191
|
#setupDpr() {
|
|
@@ -317,7 +195,7 @@ export default class GenomeSpy {
|
|
|
317
195
|
);
|
|
318
196
|
|
|
319
197
|
const resizeCallback = () => {
|
|
320
|
-
this.
|
|
198
|
+
this.#glHelper.invalidateSize();
|
|
321
199
|
this.dpr = window.devicePixelRatio;
|
|
322
200
|
dprSetter(this.dpr);
|
|
323
201
|
this.computeLayout();
|
|
@@ -329,7 +207,7 @@ export default class GenomeSpy {
|
|
|
329
207
|
// TODO: Size should be observed only if the content is not absolutely sized
|
|
330
208
|
const resizeObserver = new ResizeObserver(resizeCallback);
|
|
331
209
|
resizeObserver.observe(this.container);
|
|
332
|
-
this.
|
|
210
|
+
this.#destructionCallbacks.push(() => resizeObserver.disconnect());
|
|
333
211
|
}
|
|
334
212
|
|
|
335
213
|
/** @type {() => void} */
|
|
@@ -351,25 +229,19 @@ export default class GenomeSpy {
|
|
|
351
229
|
updatePixelRatio();
|
|
352
230
|
|
|
353
231
|
if (remove) {
|
|
354
|
-
this.
|
|
232
|
+
this.#destructionCallbacks.push(remove);
|
|
355
233
|
}
|
|
356
234
|
}
|
|
357
235
|
|
|
358
236
|
#prepareContainer() {
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
const canvasWrapper = element("div", {
|
|
366
|
-
class: "canvas-wrapper",
|
|
367
|
-
});
|
|
368
|
-
this.container.appendChild(canvasWrapper);
|
|
369
|
-
|
|
370
|
-
canvasWrapper.classList.add("loading");
|
|
237
|
+
const {
|
|
238
|
+
canvasWrapper,
|
|
239
|
+
loadingMessageElement,
|
|
240
|
+
loadingIndicatorsElement,
|
|
241
|
+
tooltip,
|
|
242
|
+
} = createContainerUi(this.container);
|
|
371
243
|
|
|
372
|
-
this
|
|
244
|
+
this.#glHelper = new WebGLHelper(
|
|
373
245
|
canvasWrapper,
|
|
374
246
|
() =>
|
|
375
247
|
this.viewRoot
|
|
@@ -379,30 +251,16 @@ export default class GenomeSpy {
|
|
|
379
251
|
);
|
|
380
252
|
|
|
381
253
|
// The initial loading message that is shown until the first frame is rendered
|
|
382
|
-
this.loadingMessageElement =
|
|
383
|
-
class: "loading-message",
|
|
384
|
-
innerHTML: `<div class="message">Loading<span class="ellipsis">...</span></div>`,
|
|
385
|
-
});
|
|
386
|
-
canvasWrapper.appendChild(this.loadingMessageElement);
|
|
387
|
-
|
|
254
|
+
this.loadingMessageElement = loadingMessageElement;
|
|
388
255
|
// A container for loading indicators (for lazy data sources.)
|
|
389
256
|
// These could alternatively be included in the view hierarchy,
|
|
390
257
|
// but it's easier this way – particularly if we want to show
|
|
391
258
|
// some fancy animated spinners.
|
|
392
|
-
this.loadingIndicatorsElement =
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
this.tooltip = new Tooltip(this.container);
|
|
398
|
-
|
|
399
|
-
this.loadingMessageElement
|
|
400
|
-
.querySelector(".message")
|
|
401
|
-
.addEventListener("transitionend", () => {
|
|
402
|
-
/** @type {HTMLElement} */ (
|
|
403
|
-
this.loadingMessageElement
|
|
404
|
-
).style.display = "none";
|
|
405
|
-
});
|
|
259
|
+
this.loadingIndicatorsElement = loadingIndicatorsElement;
|
|
260
|
+
this.tooltip = tooltip;
|
|
261
|
+
this.#loadingIndicatorManager = new LoadingIndicatorManager(
|
|
262
|
+
loadingIndicatorsElement
|
|
263
|
+
);
|
|
406
264
|
}
|
|
407
265
|
|
|
408
266
|
/**
|
|
@@ -416,125 +274,91 @@ export default class GenomeSpy {
|
|
|
416
274
|
this.container.classList.remove("genome-spy");
|
|
417
275
|
canvasWrapper.classList.remove("loading");
|
|
418
276
|
|
|
419
|
-
|
|
420
|
-
for (const listener of listeners) {
|
|
421
|
-
document.removeEventListener(type, listener);
|
|
422
|
-
}
|
|
423
|
-
}
|
|
277
|
+
this.#keyboardListenerManager.removeAll();
|
|
424
278
|
|
|
425
|
-
this.
|
|
279
|
+
this.#destructionCallbacks.forEach((callback) => callback());
|
|
426
280
|
|
|
427
|
-
this.
|
|
281
|
+
this.#glHelper.finalize();
|
|
428
282
|
|
|
429
|
-
this.
|
|
283
|
+
this.#inputBindingManager.remove();
|
|
430
284
|
|
|
431
285
|
while (this.container.firstChild) {
|
|
432
286
|
this.container.firstChild.remove();
|
|
433
287
|
}
|
|
434
288
|
}
|
|
435
289
|
|
|
436
|
-
async
|
|
290
|
+
async #prepareViewsAndData() {
|
|
291
|
+
await this.#initializeGenomeStore();
|
|
292
|
+
const context = this.#createViewContext();
|
|
293
|
+
await this.#initializeViewHierarchy(context);
|
|
294
|
+
await initializeViewData(
|
|
295
|
+
this.viewRoot,
|
|
296
|
+
context.dataFlow,
|
|
297
|
+
context.fontManager,
|
|
298
|
+
(flow) => this.broadcast("dataFlowBuilt", flow)
|
|
299
|
+
);
|
|
300
|
+
this.#finalizeViewInitialization(context);
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
async #initializeGenomeStore() {
|
|
437
304
|
if (this.spec.genome) {
|
|
438
305
|
this.genomeStore = new GenomeStore(this.spec.baseUrl);
|
|
439
306
|
await this.genomeStore.initialize(this.spec.genome);
|
|
440
307
|
}
|
|
308
|
+
}
|
|
441
309
|
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
/** @type {import("./types/viewContext.js").default} */
|
|
446
|
-
const context = {
|
|
310
|
+
#createViewContext() {
|
|
311
|
+
return createViewContext({
|
|
447
312
|
dataFlow: new DataFlow(),
|
|
448
|
-
glHelper: this
|
|
313
|
+
glHelper: this.#glHelper,
|
|
449
314
|
animator: this.animator,
|
|
450
315
|
genomeStore: this.genomeStore,
|
|
451
|
-
fontManager: new BmFontManager(this
|
|
452
|
-
|
|
453
|
-
requestLayoutReflow: () => {
|
|
454
|
-
// placeholder
|
|
455
|
-
},
|
|
316
|
+
fontManager: new BmFontManager(this.#glHelper),
|
|
456
317
|
updateTooltip: this.updateTooltip.bind(this),
|
|
457
318
|
getNamedDataFromProvider: this.getNamedDataFromProvider.bind(this),
|
|
458
|
-
getCurrentHover: () =>
|
|
459
|
-
|
|
460
|
-
setDataLoadingStatus: (view, status, detail) =>
|
|
461
|
-
this.
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
319
|
+
getCurrentHover: () =>
|
|
320
|
+
this.#interactionController.getCurrentHover(),
|
|
321
|
+
setDataLoadingStatus: (view, status, detail) =>
|
|
322
|
+
this.#loadingIndicatorManager.setDataLoadingStatus(
|
|
323
|
+
view,
|
|
324
|
+
status,
|
|
325
|
+
detail
|
|
326
|
+
),
|
|
465
327
|
addKeyboardListener: (type, listener) => {
|
|
466
328
|
// TODO: Listeners should be called only when the mouse pointer is inside the
|
|
467
329
|
// container or the app covers the full document.
|
|
468
|
-
|
|
469
|
-
let listeners = this._keyboardListeners.get(type);
|
|
470
|
-
if (!listeners) {
|
|
471
|
-
listeners = [];
|
|
472
|
-
this._keyboardListeners.set(type, listeners);
|
|
473
|
-
}
|
|
474
|
-
listeners.push(listener);
|
|
475
|
-
},
|
|
476
|
-
|
|
477
|
-
addBroadcastListener(type, listener) {
|
|
478
|
-
const listenersByType = self._extraBroadcastListeners;
|
|
479
|
-
|
|
480
|
-
// Copy-paste code. TODO: Refactor into a helper function.
|
|
481
|
-
let listeners = listenersByType.get(type);
|
|
482
|
-
if (!listeners) {
|
|
483
|
-
listeners = new Set();
|
|
484
|
-
listenersByType.set(type, listeners);
|
|
485
|
-
}
|
|
486
|
-
|
|
487
|
-
listeners.add(listener);
|
|
488
|
-
},
|
|
489
|
-
|
|
490
|
-
removeBroadcastListener(type, listener) {
|
|
491
|
-
const listenersByType = self._extraBroadcastListeners;
|
|
492
|
-
|
|
493
|
-
listenersByType.get(type)?.delete(listener);
|
|
330
|
+
this.#keyboardListenerManager.add(type, listener);
|
|
494
331
|
},
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
332
|
+
addBroadcastListener: (type, listener) =>
|
|
333
|
+
this.#extraBroadcastListeners.add(type, listener),
|
|
334
|
+
removeBroadcastListener: (type, listener) =>
|
|
335
|
+
this.#extraBroadcastListeners.remove(type, listener),
|
|
336
|
+
isViewConfiguredVisible: this.viewVisibilityPredicate,
|
|
337
|
+
isViewSpec: (spec) => this.viewFactory.isViewSpec(spec),
|
|
338
|
+
createOrImportViewWithContext: (
|
|
339
|
+
ctx,
|
|
501
340
|
spec,
|
|
502
341
|
layoutParent,
|
|
503
342
|
dataParent,
|
|
504
343
|
defaultName,
|
|
505
344
|
validator
|
|
506
|
-
)
|
|
507
|
-
|
|
345
|
+
) =>
|
|
346
|
+
this.viewFactory.createOrImportView(
|
|
508
347
|
spec,
|
|
509
|
-
|
|
348
|
+
ctx,
|
|
510
349
|
layoutParent,
|
|
511
350
|
dataParent,
|
|
512
351
|
defaultName,
|
|
513
352
|
validator
|
|
514
|
-
)
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
this.container.querySelector(".view-highlight")?.remove();
|
|
519
|
-
if (view) {
|
|
520
|
-
const coords = view.coords;
|
|
521
|
-
if (coords) {
|
|
522
|
-
const div = document.createElement("div");
|
|
523
|
-
div.className = "view-highlight";
|
|
524
|
-
div.style.position = "absolute";
|
|
525
|
-
div.style.left = coords.x + "px";
|
|
526
|
-
div.style.top = coords.y + "px";
|
|
527
|
-
div.style.width = coords.width + "px";
|
|
528
|
-
div.style.height = coords.height + "px";
|
|
529
|
-
div.style.border = "1px solid green";
|
|
530
|
-
div.style.backgroundColor = "rgba(0, 255, 0, 0.1)";
|
|
531
|
-
div.style.pointerEvents = "none";
|
|
532
|
-
this.container.appendChild(div);
|
|
533
|
-
}
|
|
534
|
-
}
|
|
535
|
-
},
|
|
536
|
-
};
|
|
353
|
+
),
|
|
354
|
+
highlightView: createViewHighlighter(this.container),
|
|
355
|
+
});
|
|
356
|
+
}
|
|
537
357
|
|
|
358
|
+
/**
|
|
359
|
+
* @param {import("./types/viewContext.js").default} context
|
|
360
|
+
*/
|
|
361
|
+
async #initializeViewHierarchy(context) {
|
|
538
362
|
/** @type {ViewSpec & RootConfig} */
|
|
539
363
|
const rootSpec = this.spec;
|
|
540
364
|
|
|
@@ -556,96 +380,51 @@ export default class GenomeSpy {
|
|
|
556
380
|
|
|
557
381
|
this.#initializeParameterBindings();
|
|
558
382
|
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
setImplicitScaleNames(this.viewRoot);
|
|
562
|
-
|
|
563
|
-
const views = this.viewRoot.getDescendants();
|
|
564
|
-
|
|
565
|
-
// View opacity should be configured after all scales have been resolved.
|
|
566
|
-
// Currently this doesn't work if new views are added dynamically.
|
|
567
|
-
// TODO: Figure out how to handle dynamic view addition/removal nicely.
|
|
568
|
-
views.forEach((view) => view.configureViewOpacity());
|
|
383
|
+
configureViewHierarchy(this.viewRoot);
|
|
384
|
+
configureViewOpacity(this.viewRoot);
|
|
569
385
|
|
|
570
386
|
// We should now have a complete view hierarchy. Let's update the canvas size
|
|
571
387
|
// and ensure that the loading message is visible.
|
|
572
|
-
this.
|
|
573
|
-
this.#
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
const flow = buildDataFlow(this.viewRoot, context.dataFlow);
|
|
582
|
-
optimizeDataFlow(flow);
|
|
583
|
-
this.broadcast("dataFlowBuilt", flow);
|
|
584
|
-
|
|
585
|
-
// @ts-expect-error
|
|
586
|
-
if (import.meta.env?.DEV) {
|
|
587
|
-
flow.dataSources.forEach((ds) => console.log(ds.subtreeToString()));
|
|
588
|
-
}
|
|
589
|
-
|
|
590
|
-
// Create encoders (accessors, scales and related metadata)
|
|
591
|
-
unitViews.forEach((view) => view.mark.initializeEncoders());
|
|
592
|
-
|
|
593
|
-
// Compile shaders, create or load textures, etc.
|
|
594
|
-
const graphicsInitialized = Promise.all(
|
|
595
|
-
unitViews.map((view) => view.mark.initializeGraphics())
|
|
596
|
-
);
|
|
597
|
-
|
|
598
|
-
for (const view of unitViews) {
|
|
599
|
-
flow.addObserver((collector) => {
|
|
600
|
-
view.mark.initializeData();
|
|
601
|
-
try {
|
|
602
|
-
// Update WebGL buffers
|
|
603
|
-
view.mark.updateGraphicsData();
|
|
604
|
-
} catch (e) {
|
|
605
|
-
e.view = view;
|
|
606
|
-
throw e;
|
|
607
|
-
}
|
|
608
|
-
context.animator.requestRender();
|
|
609
|
-
}, view);
|
|
610
|
-
}
|
|
611
|
-
|
|
612
|
-
// Have to wait until asynchronous font loading is complete.
|
|
613
|
-
// Text mark's geometry builder needs font metrics before data can be
|
|
614
|
-
// converted into geometries.
|
|
615
|
-
// TODO: Make updateGraphicsData async and await font loading there.
|
|
616
|
-
await context.fontManager.waitUntilReady();
|
|
617
|
-
|
|
618
|
-
// Find all data sources and initiate loading
|
|
619
|
-
flow.initialize();
|
|
620
|
-
await Promise.all(
|
|
621
|
-
flow.dataSources.map((dataSource) => dataSource.load())
|
|
622
|
-
);
|
|
623
|
-
|
|
624
|
-
// Now that all data have been loaded, the domains may need adjusting
|
|
625
|
-
// IMPORTANT TODO: Check that discrete domains and indexers match!!!!!!!!!
|
|
626
|
-
reconfigureScales(this.viewRoot);
|
|
627
|
-
|
|
628
|
-
// This event is needed by SampleView so that it can extract the sample ids
|
|
629
|
-
// from the data once they are loaded.
|
|
630
|
-
// TODO: It would be great if this could be attached to the data flow,
|
|
631
|
-
// because now this is somewhat a hack and is incompatible with dynamic data
|
|
632
|
-
// loading in the future.
|
|
633
|
-
this.broadcast("dataLoaded");
|
|
388
|
+
this.#glHelper.invalidateSize();
|
|
389
|
+
this.#renderCoordinator = new RenderCoordinator({
|
|
390
|
+
viewRoot: this.viewRoot,
|
|
391
|
+
glHelper: this.#glHelper,
|
|
392
|
+
getBackground: () => this.spec.background,
|
|
393
|
+
broadcast: this.broadcast.bind(this),
|
|
394
|
+
onLayoutComputed: () =>
|
|
395
|
+
this.#loadingIndicatorManager.updateLayout(),
|
|
396
|
+
});
|
|
634
397
|
|
|
635
|
-
|
|
398
|
+
// Allow early layout requests from view subscriptions created during initialization.
|
|
399
|
+
// Layout will be recomputed anyway once launch completes.
|
|
400
|
+
context.requestLayoutReflow = this.computeLayout.bind(this);
|
|
636
401
|
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
}
|
|
402
|
+
this.#setupDpr();
|
|
403
|
+
}
|
|
640
404
|
|
|
641
|
-
|
|
405
|
+
/**
|
|
406
|
+
* @param {import("./types/viewContext.js").default} context
|
|
407
|
+
*/
|
|
408
|
+
#finalizeViewInitialization(context) {
|
|
409
|
+
// Allow layout computation (in case a custom context overrode the early assignment).
|
|
642
410
|
// eslint-disable-next-line require-atomic-updates
|
|
643
411
|
context.requestLayoutReflow = this.computeLayout.bind(this);
|
|
644
412
|
|
|
645
413
|
// Invalidate cached sizes to ensure that step-based sizes are current.
|
|
646
414
|
// TODO: This should be done automatically when the domains of band/point scales are updated.
|
|
647
415
|
this.viewRoot.visit((view) => invalidatePrefix(view, "size"));
|
|
648
|
-
this.
|
|
416
|
+
this.#glHelper.invalidateSize();
|
|
417
|
+
|
|
418
|
+
this.#interactionController = new InteractionController({
|
|
419
|
+
viewRoot: this.viewRoot,
|
|
420
|
+
glHelper: this.#glHelper,
|
|
421
|
+
tooltip: this.tooltip,
|
|
422
|
+
animator: this.animator,
|
|
423
|
+
emitEvent: this.#eventListeners.emit.bind(this.#eventListeners),
|
|
424
|
+
tooltipHandlers: this.tooltipHandlers,
|
|
425
|
+
renderPickingFramebuffer: this.renderPickingFramebuffer.bind(this),
|
|
426
|
+
getDevicePixelRatio: () => this.dpr,
|
|
427
|
+
});
|
|
649
428
|
}
|
|
650
429
|
|
|
651
430
|
/**
|
|
@@ -656,7 +435,7 @@ export default class GenomeSpy {
|
|
|
656
435
|
try {
|
|
657
436
|
this.#prepareContainer();
|
|
658
437
|
|
|
659
|
-
await this
|
|
438
|
+
await this.#prepareViewsAndData();
|
|
660
439
|
|
|
661
440
|
this.registerMouseEvents();
|
|
662
441
|
|
|
@@ -669,7 +448,10 @@ export default class GenomeSpy {
|
|
|
669
448
|
reason.view ? `At "${reason.view.getPathString()}": ` : ""
|
|
670
449
|
}${reason.toString()}`;
|
|
671
450
|
console.error(reason.stack);
|
|
672
|
-
|
|
451
|
+
const handled = this.options.onError?.(reason, this.container);
|
|
452
|
+
if (!handled) {
|
|
453
|
+
createMessageBox(this.container, message);
|
|
454
|
+
}
|
|
673
455
|
|
|
674
456
|
return false;
|
|
675
457
|
} finally {
|
|
@@ -682,271 +464,7 @@ export default class GenomeSpy {
|
|
|
682
464
|
}
|
|
683
465
|
|
|
684
466
|
registerMouseEvents() {
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
// TODO: This function is huge. Refactor this into a separate class
|
|
688
|
-
// that would also contain state-related stuff that currently pollute the
|
|
689
|
-
// GenomeSpy class.
|
|
690
|
-
|
|
691
|
-
let lastWheelEvent = performance.now();
|
|
692
|
-
|
|
693
|
-
let longPressTriggered = false;
|
|
694
|
-
|
|
695
|
-
/** @param {Event} event */
|
|
696
|
-
const listener = (event) => {
|
|
697
|
-
const now = performance.now();
|
|
698
|
-
const wheeling = now - lastWheelEvent < 200;
|
|
699
|
-
|
|
700
|
-
if (event instanceof MouseEvent) {
|
|
701
|
-
const rect = canvas.getBoundingClientRect();
|
|
702
|
-
const point = new Point(
|
|
703
|
-
event.clientX - rect.left - canvas.clientLeft,
|
|
704
|
-
event.clientY - rect.top - canvas.clientTop
|
|
705
|
-
);
|
|
706
|
-
|
|
707
|
-
if (event.type == "mousemove" && !wheeling) {
|
|
708
|
-
this.tooltip.handleMouseMove(event);
|
|
709
|
-
this._tooltipUpdateRequested = false;
|
|
710
|
-
|
|
711
|
-
// Disable picking during dragging. Also postpone picking until
|
|
712
|
-
// the user has stopped zooming as reading pixels from the
|
|
713
|
-
// picking buffer is slow and ruins smooth animations.
|
|
714
|
-
if (event.buttons == 0 && !isStillZooming()) {
|
|
715
|
-
this.renderPickingFramebuffer();
|
|
716
|
-
this._handlePicking(point.x, point.y);
|
|
717
|
-
}
|
|
718
|
-
}
|
|
719
|
-
|
|
720
|
-
/**
|
|
721
|
-
* @param {MouseEvent} event
|
|
722
|
-
*/
|
|
723
|
-
const dispatchEvent = (event) => {
|
|
724
|
-
this.viewRoot.propagateInteractionEvent(
|
|
725
|
-
new InteractionEvent(point, event)
|
|
726
|
-
);
|
|
727
|
-
|
|
728
|
-
if (!this._tooltipUpdateRequested) {
|
|
729
|
-
this.tooltip.clear();
|
|
730
|
-
}
|
|
731
|
-
};
|
|
732
|
-
|
|
733
|
-
if (event.type != "wheel") {
|
|
734
|
-
this._wheelInertia.cancel();
|
|
735
|
-
}
|
|
736
|
-
|
|
737
|
-
if (
|
|
738
|
-
(event.type == "mousedown" || event.type == "mouseup") &&
|
|
739
|
-
!isStillZooming()
|
|
740
|
-
) {
|
|
741
|
-
// Actually, only needed when clicking on a mark
|
|
742
|
-
this.renderPickingFramebuffer();
|
|
743
|
-
} else if (event.type == "wheel") {
|
|
744
|
-
lastWheelEvent = now;
|
|
745
|
-
this._tooltipUpdateRequested = false;
|
|
746
|
-
|
|
747
|
-
const wheelEvent = /** @type {WheelEvent} */ (event);
|
|
748
|
-
|
|
749
|
-
if (
|
|
750
|
-
Math.abs(wheelEvent.deltaX) >
|
|
751
|
-
Math.abs(wheelEvent.deltaY)
|
|
752
|
-
) {
|
|
753
|
-
// If the viewport is panned (horizontally) using the wheel (touchpad),
|
|
754
|
-
// the picking buffer becomes stale and needs redrawing. However, we
|
|
755
|
-
// optimize by just clearing the currently hovered item so that snapping
|
|
756
|
-
// doesn't work incorrectly when zooming in/out.
|
|
757
|
-
|
|
758
|
-
// TODO: More robust solution (handle at higher level such as ScaleResolution's zoom method)
|
|
759
|
-
this._currentHover = null;
|
|
760
|
-
|
|
761
|
-
this._wheelInertia.cancel();
|
|
762
|
-
} else {
|
|
763
|
-
// Vertical wheeling zooms.
|
|
764
|
-
// We use inertia to generate fake wheel events for smoother zooming
|
|
765
|
-
|
|
766
|
-
const template = makeEventTemplate(wheelEvent);
|
|
767
|
-
|
|
768
|
-
this._wheelInertia.setMomentum(
|
|
769
|
-
wheelEvent.deltaY * (wheelEvent.deltaMode ? 80 : 1),
|
|
770
|
-
(delta) => {
|
|
771
|
-
const e = new WheelEvent("wheel", {
|
|
772
|
-
...template,
|
|
773
|
-
deltaMode: 0,
|
|
774
|
-
deltaX: 0,
|
|
775
|
-
deltaY: delta,
|
|
776
|
-
});
|
|
777
|
-
dispatchEvent(e);
|
|
778
|
-
}
|
|
779
|
-
);
|
|
780
|
-
|
|
781
|
-
wheelEvent.preventDefault();
|
|
782
|
-
return;
|
|
783
|
-
}
|
|
784
|
-
}
|
|
785
|
-
|
|
786
|
-
// TODO: Should be handled at the view level, not globally
|
|
787
|
-
if (event.type == "click") {
|
|
788
|
-
if (longPressTriggered) {
|
|
789
|
-
return;
|
|
790
|
-
}
|
|
791
|
-
|
|
792
|
-
const e = this._currentHover
|
|
793
|
-
? {
|
|
794
|
-
type: event.type,
|
|
795
|
-
viewPath: this._currentHover.mark.unitView
|
|
796
|
-
.getLayoutAncestors()
|
|
797
|
-
.map((view) => view.name)
|
|
798
|
-
.reverse(),
|
|
799
|
-
datum: this._currentHover.datum,
|
|
800
|
-
}
|
|
801
|
-
: {
|
|
802
|
-
type: event.type,
|
|
803
|
-
viewPath: null,
|
|
804
|
-
datum: null,
|
|
805
|
-
};
|
|
806
|
-
|
|
807
|
-
this._eventListeners
|
|
808
|
-
.get("click")
|
|
809
|
-
?.forEach((listener) => listener(e));
|
|
810
|
-
}
|
|
811
|
-
|
|
812
|
-
if (
|
|
813
|
-
event.type != "click" ||
|
|
814
|
-
// Suppress click events if the mouse has been dragged
|
|
815
|
-
this._mouseDownCoords?.subtract(Point.fromMouseEvent(event))
|
|
816
|
-
.length < 3
|
|
817
|
-
) {
|
|
818
|
-
dispatchEvent(event);
|
|
819
|
-
}
|
|
820
|
-
}
|
|
821
|
-
};
|
|
822
|
-
|
|
823
|
-
[
|
|
824
|
-
"mousedown",
|
|
825
|
-
"mouseup",
|
|
826
|
-
"wheel",
|
|
827
|
-
"click",
|
|
828
|
-
"mousemove",
|
|
829
|
-
"gesturechange",
|
|
830
|
-
"contextmenu",
|
|
831
|
-
"dblclick",
|
|
832
|
-
].forEach((type) => canvas.addEventListener(type, listener));
|
|
833
|
-
|
|
834
|
-
canvas.addEventListener("mousedown", (/** @type {MouseEvent} */ e) => {
|
|
835
|
-
this._mouseDownCoords = Point.fromMouseEvent(e);
|
|
836
|
-
if (this.tooltip.sticky) {
|
|
837
|
-
this.tooltip.sticky = false;
|
|
838
|
-
this.tooltip.clear();
|
|
839
|
-
// A hack to prevent selection if the tooltip is sticky.
|
|
840
|
-
// Let the tooltip be destickified first.
|
|
841
|
-
longPressTriggered = true;
|
|
842
|
-
} else {
|
|
843
|
-
longPressTriggered = false;
|
|
844
|
-
}
|
|
845
|
-
|
|
846
|
-
const disableTooltip = () => {
|
|
847
|
-
document.addEventListener(
|
|
848
|
-
"mouseup",
|
|
849
|
-
() => this.tooltip.popEnabledState(),
|
|
850
|
-
{ once: true }
|
|
851
|
-
);
|
|
852
|
-
this.tooltip.pushEnabledState(false);
|
|
853
|
-
};
|
|
854
|
-
|
|
855
|
-
// Opening context menu or using modifier keys disables the tooltip
|
|
856
|
-
if (e.button == 2 || e.shiftKey || e.ctrlKey || e.metaKey) {
|
|
857
|
-
disableTooltip();
|
|
858
|
-
} else if (this.tooltip.visible) {
|
|
859
|
-
// Make tooltip sticky if the user long-presses
|
|
860
|
-
const timeout = setTimeout(() => {
|
|
861
|
-
longPressTriggered = true;
|
|
862
|
-
this.tooltip.sticky = true;
|
|
863
|
-
}, 400);
|
|
864
|
-
|
|
865
|
-
const clear = () => clearTimeout(timeout);
|
|
866
|
-
document.addEventListener("mouseup", clear, { once: true });
|
|
867
|
-
document.addEventListener("mousemove", clear, { once: true });
|
|
868
|
-
}
|
|
869
|
-
});
|
|
870
|
-
|
|
871
|
-
// Prevent text selections etc while dragging
|
|
872
|
-
canvas.addEventListener("dragstart", (event) =>
|
|
873
|
-
event.stopPropagation()
|
|
874
|
-
);
|
|
875
|
-
}
|
|
876
|
-
|
|
877
|
-
/**
|
|
878
|
-
* @param {number} x
|
|
879
|
-
* @param {number} y
|
|
880
|
-
*/
|
|
881
|
-
_handlePicking(x, y) {
|
|
882
|
-
const dpr = this.dpr;
|
|
883
|
-
const pp = readPickingPixel(
|
|
884
|
-
this._glHelper.gl,
|
|
885
|
-
this._glHelper._pickingBufferInfo,
|
|
886
|
-
x * dpr,
|
|
887
|
-
y * dpr
|
|
888
|
-
);
|
|
889
|
-
|
|
890
|
-
const uniqueId = pp[0] | (pp[1] << 8) | (pp[2] << 16) | (pp[3] << 24);
|
|
891
|
-
|
|
892
|
-
if (uniqueId == 0) {
|
|
893
|
-
this._currentHover = null;
|
|
894
|
-
return;
|
|
895
|
-
}
|
|
896
|
-
|
|
897
|
-
if (uniqueId !== this._currentHover?.uniqueId) {
|
|
898
|
-
this._currentHover = null;
|
|
899
|
-
}
|
|
900
|
-
|
|
901
|
-
if (!this._currentHover) {
|
|
902
|
-
this.viewRoot.visit((view) => {
|
|
903
|
-
if (view instanceof UnitView) {
|
|
904
|
-
if (
|
|
905
|
-
view.mark.isPickingParticipant() &&
|
|
906
|
-
[...view.facetCoords.values()].some((coords) =>
|
|
907
|
-
coords.containsPoint(x, y)
|
|
908
|
-
)
|
|
909
|
-
) {
|
|
910
|
-
const datum = view
|
|
911
|
-
.getCollector()
|
|
912
|
-
.findDatumByUniqueId(uniqueId);
|
|
913
|
-
if (datum) {
|
|
914
|
-
this._currentHover = {
|
|
915
|
-
mark: view.mark,
|
|
916
|
-
datum,
|
|
917
|
-
uniqueId,
|
|
918
|
-
};
|
|
919
|
-
}
|
|
920
|
-
}
|
|
921
|
-
if (this._currentHover) {
|
|
922
|
-
return VISIT_STOP;
|
|
923
|
-
}
|
|
924
|
-
}
|
|
925
|
-
});
|
|
926
|
-
}
|
|
927
|
-
|
|
928
|
-
if (this._currentHover) {
|
|
929
|
-
const mark = this._currentHover.mark;
|
|
930
|
-
this.updateTooltip(this._currentHover.datum, async (datum) => {
|
|
931
|
-
if (!mark.isPickingParticipant()) {
|
|
932
|
-
return;
|
|
933
|
-
}
|
|
934
|
-
|
|
935
|
-
const tooltipProps = mark.properties.tooltip;
|
|
936
|
-
|
|
937
|
-
if (tooltipProps !== null) {
|
|
938
|
-
const handlerName = tooltipProps?.handler ?? "default";
|
|
939
|
-
const handler = this.tooltipHandlers[handlerName];
|
|
940
|
-
if (!handler) {
|
|
941
|
-
throw new Error(
|
|
942
|
-
"No such tooltip handler: " + handlerName
|
|
943
|
-
);
|
|
944
|
-
}
|
|
945
|
-
|
|
946
|
-
return handler(datum, mark, tooltipProps?.params);
|
|
947
|
-
}
|
|
948
|
-
});
|
|
949
|
-
}
|
|
467
|
+
this.#interactionController.registerMouseEvents();
|
|
950
468
|
}
|
|
951
469
|
|
|
952
470
|
/**
|
|
@@ -958,14 +476,7 @@ export default class GenomeSpy {
|
|
|
958
476
|
* @template T
|
|
959
477
|
*/
|
|
960
478
|
updateTooltip(datum, converter) {
|
|
961
|
-
|
|
962
|
-
this.tooltip.updateWithDatum(datum, converter);
|
|
963
|
-
this._tooltipUpdateRequested = true;
|
|
964
|
-
} else {
|
|
965
|
-
throw new Error(
|
|
966
|
-
"Tooltip has already been updated! Duplicate event handler?"
|
|
967
|
-
);
|
|
968
|
-
}
|
|
479
|
+
this.#interactionController.updateTooltip(datum, converter);
|
|
969
480
|
}
|
|
970
481
|
|
|
971
482
|
/**
|
|
@@ -983,49 +494,14 @@ export default class GenomeSpy {
|
|
|
983
494
|
devicePixelRatio,
|
|
984
495
|
clearColor = "white"
|
|
985
496
|
) {
|
|
986
|
-
const
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
const width = Math.floor(logicalWidth * devicePixelRatio);
|
|
995
|
-
const height = Math.floor(logicalHeight * devicePixelRatio);
|
|
996
|
-
|
|
997
|
-
const framebufferInfo = createFramebufferInfo(
|
|
998
|
-
gl,
|
|
999
|
-
[
|
|
1000
|
-
{
|
|
1001
|
-
format: gl.RGBA,
|
|
1002
|
-
type: gl.UNSIGNED_BYTE,
|
|
1003
|
-
minMag: gl.LINEAR,
|
|
1004
|
-
wrap: gl.CLAMP_TO_EDGE,
|
|
1005
|
-
},
|
|
1006
|
-
],
|
|
1007
|
-
width,
|
|
1008
|
-
height
|
|
1009
|
-
);
|
|
1010
|
-
|
|
1011
|
-
const renderingContext = new BufferedViewRenderingContext(
|
|
1012
|
-
{ picking: false },
|
|
1013
|
-
{
|
|
1014
|
-
webGLHelper: this._glHelper,
|
|
1015
|
-
canvasSize: { width: logicalWidth, height: logicalHeight },
|
|
1016
|
-
devicePixelRatio,
|
|
1017
|
-
clearColor,
|
|
1018
|
-
framebufferInfo,
|
|
1019
|
-
}
|
|
1020
|
-
);
|
|
1021
|
-
|
|
1022
|
-
this.viewRoot.render(
|
|
1023
|
-
renderingContext,
|
|
1024
|
-
Rectangle.create(0, 0, logicalWidth, logicalHeight)
|
|
1025
|
-
);
|
|
1026
|
-
renderingContext.render();
|
|
1027
|
-
|
|
1028
|
-
const pngUrl = framebufferToDataUrl(gl, framebufferInfo, "image/png");
|
|
497
|
+
const pngUrl = exportCanvas({
|
|
498
|
+
glHelper: this.#glHelper,
|
|
499
|
+
viewRoot: this.viewRoot,
|
|
500
|
+
logicalWidth,
|
|
501
|
+
logicalHeight,
|
|
502
|
+
devicePixelRatio,
|
|
503
|
+
clearColor,
|
|
504
|
+
});
|
|
1029
505
|
|
|
1030
506
|
// Clean up
|
|
1031
507
|
this.computeLayout();
|
|
@@ -1034,74 +510,20 @@ export default class GenomeSpy {
|
|
|
1034
510
|
return pngUrl;
|
|
1035
511
|
}
|
|
1036
512
|
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
return;
|
|
1041
|
-
}
|
|
1042
|
-
|
|
1043
|
-
this.broadcast("layout");
|
|
1044
|
-
|
|
1045
|
-
const canvasSize = this._glHelper.getLogicalCanvasSize();
|
|
1046
|
-
|
|
1047
|
-
if (isNaN(canvasSize.width) || isNaN(canvasSize.height)) {
|
|
1048
|
-
// TODO: Figure out what causes this
|
|
1049
|
-
console.log(
|
|
1050
|
-
`NaN in canvas size: ${canvasSize.width}x${canvasSize.height}. Skipping computeLayout().`
|
|
1051
|
-
);
|
|
1052
|
-
return;
|
|
1053
|
-
}
|
|
1054
|
-
|
|
1055
|
-
const commonOptions = {
|
|
1056
|
-
webGLHelper: this._glHelper,
|
|
1057
|
-
canvasSize,
|
|
1058
|
-
devicePixelRatio: window.devicePixelRatio ?? 1,
|
|
1059
|
-
};
|
|
1060
|
-
|
|
1061
|
-
this._renderingContext = new BufferedViewRenderingContext(
|
|
1062
|
-
{ picking: false },
|
|
1063
|
-
{
|
|
1064
|
-
...commonOptions,
|
|
1065
|
-
clearColor: this.spec.background,
|
|
1066
|
-
}
|
|
1067
|
-
);
|
|
1068
|
-
this._pickingContext = new BufferedViewRenderingContext(
|
|
1069
|
-
{ picking: true },
|
|
1070
|
-
{
|
|
1071
|
-
...commonOptions,
|
|
1072
|
-
framebufferInfo: this._glHelper._pickingBufferInfo,
|
|
1073
|
-
}
|
|
1074
|
-
);
|
|
1075
|
-
|
|
1076
|
-
root.render(
|
|
1077
|
-
new CompositeViewRenderingContext(
|
|
1078
|
-
this._renderingContext,
|
|
1079
|
-
this._pickingContext
|
|
1080
|
-
),
|
|
1081
|
-
// Canvas should now be sized based on the root view or the container
|
|
1082
|
-
Rectangle.create(0, 0, canvasSize.width, canvasSize.height)
|
|
1083
|
-
);
|
|
1084
|
-
|
|
1085
|
-
// The view coordinates may have not been known during the initial data loading.
|
|
1086
|
-
// Thus, update them so that possible error messages are shown in the correct place.
|
|
1087
|
-
this._updateLoadingIndicators();
|
|
513
|
+
getLogicalCanvasSize() {
|
|
514
|
+
return this.#glHelper.getLogicalCanvasSize();
|
|
515
|
+
}
|
|
1088
516
|
|
|
1089
|
-
|
|
517
|
+
computeLayout() {
|
|
518
|
+
this.#renderCoordinator.computeLayout();
|
|
1090
519
|
}
|
|
1091
520
|
|
|
1092
521
|
renderAll() {
|
|
1093
|
-
this.
|
|
1094
|
-
|
|
1095
|
-
this._dirtyPickingBuffer = true;
|
|
522
|
+
this.#renderCoordinator.renderAll();
|
|
1096
523
|
}
|
|
1097
524
|
|
|
1098
525
|
renderPickingFramebuffer() {
|
|
1099
|
-
|
|
1100
|
-
return;
|
|
1101
|
-
}
|
|
1102
|
-
|
|
1103
|
-
this._pickingContext.render();
|
|
1104
|
-
this._dirtyPickingBuffer = false;
|
|
526
|
+
this.#renderCoordinator.renderPickingFramebuffer();
|
|
1105
527
|
}
|
|
1106
528
|
|
|
1107
529
|
getSearchableViews() {
|
|
@@ -1116,7 +538,7 @@ export default class GenomeSpy {
|
|
|
1116
538
|
}
|
|
1117
539
|
|
|
1118
540
|
getNamedScaleResolutions() {
|
|
1119
|
-
/** @type {Map<string, import("./
|
|
541
|
+
/** @type {Map<string, import("./scales/scaleResolution.js").default>} */
|
|
1120
542
|
const resolutions = new Map();
|
|
1121
543
|
this.viewRoot.visit((view) => {
|
|
1122
544
|
for (const resolution of Object.values(view.resolutions.scale)) {
|
|
@@ -1128,34 +550,3 @@ export default class GenomeSpy {
|
|
|
1128
550
|
return resolutions;
|
|
1129
551
|
}
|
|
1130
552
|
}
|
|
1131
|
-
|
|
1132
|
-
/**
|
|
1133
|
-
*
|
|
1134
|
-
* @param {HTMLElement} container
|
|
1135
|
-
* @param {string} message
|
|
1136
|
-
*/
|
|
1137
|
-
function createMessageBox(container, message) {
|
|
1138
|
-
// Uh, need a templating thingy
|
|
1139
|
-
const messageBox = document.createElement("div");
|
|
1140
|
-
messageBox.className = "message-box";
|
|
1141
|
-
const messageText = document.createElement("div");
|
|
1142
|
-
messageText.textContent = message;
|
|
1143
|
-
messageBox.appendChild(messageText);
|
|
1144
|
-
container.appendChild(messageBox);
|
|
1145
|
-
}
|
|
1146
|
-
|
|
1147
|
-
/**
|
|
1148
|
-
* @param {string} tag
|
|
1149
|
-
* @param {Record<string, any>} attrs
|
|
1150
|
-
*/
|
|
1151
|
-
function element(tag, attrs) {
|
|
1152
|
-
const el = document.createElement(tag);
|
|
1153
|
-
for (const [key, value] of Object.entries(attrs)) {
|
|
1154
|
-
if (["innerHTML", "innerText", "className"].includes(key)) {
|
|
1155
|
-
// @ts-ignore
|
|
1156
|
-
el[key] = value;
|
|
1157
|
-
}
|
|
1158
|
-
el.setAttribute(key, value);
|
|
1159
|
-
}
|
|
1160
|
-
return el;
|
|
1161
|
-
}
|