@vitessce/scatterplot 2.0.3-beta.0 → 3.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (73) hide show
  1. package/dist/deflate-24a61b7a.js +13 -0
  2. package/dist/index-d78d4568.js +142036 -0
  3. package/dist/index.js +15 -5
  4. package/dist/jpeg-19791032.js +840 -0
  5. package/dist/lerc-867fd2ef.js +2014 -0
  6. package/dist/lzw-40577136.js +128 -0
  7. package/dist/packbits-eab43a40.js +30 -0
  8. package/dist/pako.esm-68f84e2a.js +4022 -0
  9. package/dist/raw-66364181.js +12 -0
  10. package/dist/webimage-0fe27785.js +32 -0
  11. package/dist-tsc/EmptyMessage.d.ts +2 -0
  12. package/dist-tsc/EmptyMessage.d.ts.map +1 -0
  13. package/dist-tsc/Scatterplot.d.ts +10 -0
  14. package/dist-tsc/Scatterplot.d.ts.map +1 -0
  15. package/{dist → dist-tsc}/Scatterplot.js +12 -2
  16. package/dist-tsc/ScatterplotOptions.d.ts +2 -0
  17. package/dist-tsc/ScatterplotOptions.d.ts.map +1 -0
  18. package/{dist → dist-tsc}/ScatterplotOptions.js +13 -7
  19. package/dist-tsc/ScatterplotTooltipSubscriber.d.ts +2 -0
  20. package/dist-tsc/ScatterplotTooltipSubscriber.d.ts.map +1 -0
  21. package/dist-tsc/index.d.ts +6 -0
  22. package/dist-tsc/index.d.ts.map +1 -0
  23. package/dist-tsc/index.js +5 -0
  24. package/dist-tsc/shared-spatial-scatterplot/AbstractSpatialOrScatterplot.d.ts +82 -0
  25. package/dist-tsc/shared-spatial-scatterplot/AbstractSpatialOrScatterplot.d.ts.map +1 -0
  26. package/{dist → dist-tsc}/shared-spatial-scatterplot/AbstractSpatialOrScatterplot.js +9 -4
  27. package/dist-tsc/shared-spatial-scatterplot/ToolMenu.d.ts +4 -0
  28. package/dist-tsc/shared-spatial-scatterplot/ToolMenu.d.ts.map +1 -0
  29. package/{dist → dist-tsc}/shared-spatial-scatterplot/ToolMenu.js +32 -7
  30. package/dist-tsc/shared-spatial-scatterplot/ToolMenu.test.d.ts +2 -0
  31. package/dist-tsc/shared-spatial-scatterplot/ToolMenu.test.d.ts.map +1 -0
  32. package/dist-tsc/shared-spatial-scatterplot/ToolMenu.test.js +23 -0
  33. package/dist-tsc/shared-spatial-scatterplot/cursor.d.ts +4 -0
  34. package/dist-tsc/shared-spatial-scatterplot/cursor.d.ts.map +1 -0
  35. package/dist-tsc/shared-spatial-scatterplot/dynamic-opacity.d.ts +3 -0
  36. package/dist-tsc/shared-spatial-scatterplot/dynamic-opacity.d.ts.map +1 -0
  37. package/{dist → dist-tsc}/shared-spatial-scatterplot/dynamic-opacity.js +1 -1
  38. package/dist-tsc/shared-spatial-scatterplot/dynamic-opacity.test.d.ts +2 -0
  39. package/dist-tsc/shared-spatial-scatterplot/dynamic-opacity.test.d.ts.map +1 -0
  40. package/{dist → dist-tsc}/shared-spatial-scatterplot/dynamic-opacity.test.js +1 -1
  41. package/dist-tsc/shared-spatial-scatterplot/force-collide-rects.d.ts +13 -0
  42. package/dist-tsc/shared-spatial-scatterplot/force-collide-rects.d.ts.map +1 -0
  43. package/dist-tsc/shared-spatial-scatterplot/force-collide-rects.test.d.ts +2 -0
  44. package/dist-tsc/shared-spatial-scatterplot/force-collide-rects.test.d.ts.map +1 -0
  45. package/{dist → dist-tsc}/shared-spatial-scatterplot/force-collide-rects.test.js +1 -1
  46. package/dist-tsc/shared-spatial-scatterplot/index.d.ts +6 -0
  47. package/dist-tsc/shared-spatial-scatterplot/index.d.ts.map +1 -0
  48. package/dist-tsc/shared-spatial-scatterplot/index.js +5 -0
  49. package/dist-tsc/shared-spatial-scatterplot/quadtree.d.ts +10 -0
  50. package/dist-tsc/shared-spatial-scatterplot/quadtree.d.ts.map +1 -0
  51. package/{dist → dist-tsc}/shared-spatial-scatterplot/quadtree.js +1 -1
  52. package/package.json +25 -13
  53. package/src/EmptyMessage.js +11 -0
  54. package/src/Scatterplot.js +396 -0
  55. package/src/ScatterplotOptions.js +269 -0
  56. package/src/ScatterplotTooltipSubscriber.js +38 -0
  57. package/src/index.js +11 -0
  58. package/src/shared-spatial-scatterplot/AbstractSpatialOrScatterplot.js +280 -0
  59. package/src/shared-spatial-scatterplot/ToolMenu.js +143 -0
  60. package/src/shared-spatial-scatterplot/ToolMenu.test.jsx +26 -0
  61. package/src/shared-spatial-scatterplot/cursor.js +23 -0
  62. package/src/shared-spatial-scatterplot/dynamic-opacity.js +58 -0
  63. package/src/shared-spatial-scatterplot/dynamic-opacity.test.js +33 -0
  64. package/src/shared-spatial-scatterplot/force-collide-rects.js +189 -0
  65. package/src/shared-spatial-scatterplot/force-collide-rects.test.js +72 -0
  66. package/src/shared-spatial-scatterplot/index.js +8 -0
  67. package/src/shared-spatial-scatterplot/quadtree.js +27 -0
  68. package/dist/shared-spatial-scatterplot/ToolMenu.test.js +0 -16
  69. package/dist/shared-spatial-scatterplot/index.js +0 -5
  70. /package/{dist → dist-tsc}/EmptyMessage.js +0 -0
  71. /package/{dist → dist-tsc}/ScatterplotTooltipSubscriber.js +0 -0
  72. /package/{dist → dist-tsc}/shared-spatial-scatterplot/cursor.js +0 -0
  73. /package/{dist → dist-tsc}/shared-spatial-scatterplot/force-collide-rects.js +0 -0
@@ -0,0 +1,269 @@
1
+ import React, { useCallback } from 'react';
2
+ import { debounce } from 'lodash-es';
3
+ import { Checkbox, Slider, TableCell, TableRow } from '@material-ui/core';
4
+ import { capitalize } from '@vitessce/utils';
5
+ import {
6
+ usePlotOptionsStyles, CellColorEncodingOption, OptionsContainer, OptionSelect,
7
+ } from '@vitessce/vit-s';
8
+ import { GLSL_COLORMAPS } from '@vitessce/gl';
9
+
10
+ export default function ScatterplotOptions(props) {
11
+ const {
12
+ children,
13
+ observationsLabel,
14
+ cellRadius,
15
+ setCellRadius,
16
+ cellRadiusMode,
17
+ setCellRadiusMode,
18
+ cellOpacity,
19
+ setCellOpacity,
20
+ cellOpacityMode,
21
+ setCellOpacityMode,
22
+ cellSetLabelsVisible,
23
+ setCellSetLabelsVisible,
24
+ tooltipsVisible,
25
+ setTooltipsVisible,
26
+ cellSetLabelSize,
27
+ setCellSetLabelSize,
28
+ cellSetPolygonsVisible,
29
+ setCellSetPolygonsVisible,
30
+ cellColorEncoding,
31
+ setCellColorEncoding,
32
+ geneExpressionColormap,
33
+ setGeneExpressionColormap,
34
+ geneExpressionColormapRange,
35
+ setGeneExpressionColormapRange,
36
+ } = props;
37
+
38
+ const observationsLabelNice = capitalize(observationsLabel);
39
+
40
+ const classes = usePlotOptionsStyles();
41
+
42
+ function handleCellRadiusModeChange(event) {
43
+ setCellRadiusMode(event.target.value);
44
+ }
45
+
46
+ function handleCellOpacityModeChange(event) {
47
+ setCellOpacityMode(event.target.value);
48
+ }
49
+
50
+ function handleRadiusChange(event, value) {
51
+ setCellRadius(value);
52
+ }
53
+
54
+ function handleOpacityChange(event, value) {
55
+ setCellOpacity(value);
56
+ }
57
+
58
+ function handleLabelVisibilityChange(event) {
59
+ setCellSetLabelsVisible(event.target.checked);
60
+ }
61
+
62
+ function handleTooltipsVisibilityChange(event) {
63
+ setTooltipsVisible(event.target.checked);
64
+ }
65
+
66
+ function handleLabelSizeChange(event, value) {
67
+ setCellSetLabelSize(value);
68
+ }
69
+
70
+ function handlePolygonVisibilityChange(event) {
71
+ setCellSetPolygonsVisible(event.target.checked);
72
+ }
73
+
74
+ function handleGeneExpressionColormapChange(event) {
75
+ setGeneExpressionColormap(event.target.value);
76
+ }
77
+
78
+ function handleColormapRangeChange(event, value) {
79
+ setGeneExpressionColormapRange(value);
80
+ }
81
+ const handleColormapRangeChangeDebounced = useCallback(
82
+ debounce(handleColormapRangeChange, 5, { trailing: true }),
83
+ [handleColormapRangeChange],
84
+ );
85
+
86
+ return (
87
+ <OptionsContainer>
88
+ {children}
89
+ <CellColorEncodingOption
90
+ observationsLabel={observationsLabel}
91
+ cellColorEncoding={cellColorEncoding}
92
+ setCellColorEncoding={setCellColorEncoding}
93
+ />
94
+ <TableRow>
95
+ <TableCell className={classes.labelCell}>
96
+ {observationsLabelNice} Set Labels Visible
97
+ </TableCell>
98
+ <TableCell className={classes.inputCell}>
99
+ <Checkbox
100
+ className={classes.checkbox}
101
+ checked={cellSetLabelsVisible}
102
+ onChange={handleLabelVisibilityChange}
103
+ name="scatterplot-option-cell-set-labels"
104
+ color="default"
105
+ />
106
+ </TableCell>
107
+ </TableRow>
108
+ <TableRow>
109
+ <TableCell className={classes.labelCell}>
110
+ Tooltips Visible
111
+ </TableCell>
112
+ <TableCell className={classes.inputCell}>
113
+ <Checkbox
114
+ className={classes.checkbox}
115
+ /**
116
+ * We have to use "checked" here, not "value".
117
+ * The checkbox state is not persisting with value.
118
+ * For reference, https://v4.mui.com/api/checkbox/
119
+ */
120
+ checked={tooltipsVisible}
121
+ onChange={handleTooltipsVisibilityChange}
122
+ name="scatterplot-option-toltip-visibility"
123
+ color="default"
124
+ />
125
+ </TableCell>
126
+ </TableRow>
127
+ <TableRow>
128
+ <TableCell className={classes.labelCell}>
129
+ {observationsLabelNice} Set Label Size
130
+ </TableCell>
131
+ <TableCell className={classes.inputCell}>
132
+ <Slider
133
+ disabled={!cellSetLabelsVisible}
134
+ classes={{ root: classes.slider, valueLabel: classes.sliderValueLabel }}
135
+ value={cellSetLabelSize}
136
+ onChange={handleLabelSizeChange}
137
+ aria-labelledby="cell-set-label-size-slider"
138
+ valueLabelDisplay="auto"
139
+ step={1}
140
+ min={8}
141
+ max={36}
142
+ />
143
+ </TableCell>
144
+ </TableRow>
145
+ <TableRow>
146
+ <TableCell className={classes.labelCell}>
147
+ {observationsLabelNice} Set Polygons Visible
148
+ </TableCell>
149
+ <TableCell className={classes.inputCell}>
150
+ <Checkbox
151
+ className={classes.checkbox}
152
+ checked={cellSetPolygonsVisible}
153
+ onChange={handlePolygonVisibilityChange}
154
+ name="scatterplot-option-cell-set-polygons"
155
+ color="default"
156
+ />
157
+ </TableCell>
158
+ </TableRow>
159
+ <TableRow>
160
+ <TableCell className={classes.labelCell} htmlFor="cell-radius-mode-select">
161
+ {observationsLabelNice} Radius Mode
162
+ </TableCell>
163
+ <TableCell className={classes.inputCell}>
164
+ <OptionSelect
165
+ className={classes.select}
166
+ value={cellRadiusMode}
167
+ onChange={handleCellRadiusModeChange}
168
+ inputProps={{
169
+ id: 'cell-radius-mode-select',
170
+ }}
171
+ >
172
+ <option value="auto">Auto</option>
173
+ <option value="manual">Manual</option>
174
+ </OptionSelect>
175
+ </TableCell>
176
+ </TableRow>
177
+ <TableRow>
178
+ <TableCell className={classes.labelCell}>
179
+ {observationsLabelNice} Radius
180
+ </TableCell>
181
+ <TableCell className={classes.inputCell}>
182
+ <Slider
183
+ disabled={cellRadiusMode !== 'manual'}
184
+ classes={{ root: classes.slider, valueLabel: classes.sliderValueLabel }}
185
+ value={cellRadius}
186
+ onChange={handleRadiusChange}
187
+ aria-labelledby="cell-radius-slider"
188
+ valueLabelDisplay="auto"
189
+ step={0.01}
190
+ min={0.01}
191
+ max={10}
192
+ />
193
+ </TableCell>
194
+ </TableRow>
195
+ <TableRow>
196
+ <TableCell className={classes.labelCell} htmlFor="cell-opacity-mode-select">
197
+ {observationsLabelNice} Opacity Mode
198
+ </TableCell>
199
+ <TableCell className={classes.inputCell}>
200
+ <OptionSelect
201
+ className={classes.select}
202
+ value={cellOpacityMode}
203
+ onChange={handleCellOpacityModeChange}
204
+ inputProps={{
205
+ id: 'cell-opacity-mode-select',
206
+ }}
207
+ >
208
+ <option value="auto">Auto</option>
209
+ <option value="manual">Manual</option>
210
+ </OptionSelect>
211
+ </TableCell>
212
+ </TableRow>
213
+ <TableRow>
214
+ <TableCell className={classes.labelCell}>
215
+ {observationsLabelNice} Opacity
216
+ </TableCell>
217
+ <TableCell className={classes.inputCell}>
218
+ <Slider
219
+ disabled={cellOpacityMode !== 'manual'}
220
+ classes={{ root: classes.slider, valueLabel: classes.sliderValueLabel }}
221
+ value={cellOpacity}
222
+ onChange={handleOpacityChange}
223
+ aria-labelledby="cell-opacity-slider"
224
+ valueLabelDisplay="auto"
225
+ step={0.05}
226
+ min={0.0}
227
+ max={1.0}
228
+ />
229
+ </TableCell>
230
+ </TableRow>
231
+ <TableRow>
232
+ <TableCell className={classes.labelCell} htmlFor="gene-expression-colormap-select">
233
+ Gene Expression Colormap
234
+ </TableCell>
235
+ <TableCell className={classes.inputCell}>
236
+ <OptionSelect
237
+ className={classes.select}
238
+ value={geneExpressionColormap}
239
+ onChange={handleGeneExpressionColormapChange}
240
+ inputProps={{
241
+ id: 'gene-expression-colormap-select',
242
+ }}
243
+ >
244
+ {GLSL_COLORMAPS.map(cmap => (
245
+ <option key={cmap} value={cmap}>{cmap}</option>
246
+ ))}
247
+ </OptionSelect>
248
+ </TableCell>
249
+ </TableRow>
250
+ <TableRow>
251
+ <TableCell className={classes.labelCell}>
252
+ Gene Expression Colormap Range
253
+ </TableCell>
254
+ <TableCell className={classes.inputCell}>
255
+ <Slider
256
+ classes={{ root: classes.slider, valueLabel: classes.sliderValueLabel }}
257
+ value={geneExpressionColormapRange}
258
+ onChange={handleColormapRangeChangeDebounced}
259
+ aria-labelledby="gene-expression-colormap-range-slider"
260
+ valueLabelDisplay="auto"
261
+ step={0.005}
262
+ min={0.0}
263
+ max={1.0}
264
+ />
265
+ </TableCell>
266
+ </TableRow>
267
+ </OptionsContainer>
268
+ );
269
+ }
@@ -0,0 +1,38 @@
1
+ import React from 'react';
2
+ import { Tooltip2D, TooltipContent } from '@vitessce/tooltip';
3
+ import { useComponentHover, useComponentViewInfo } from '@vitessce/vit-s';
4
+
5
+ export default function ScatterplotTooltipSubscriber(props) {
6
+ const {
7
+ parentUuid,
8
+ obsHighlight,
9
+ width,
10
+ height,
11
+ getObsInfo,
12
+ } = props;
13
+
14
+ const sourceUuid = useComponentHover();
15
+ const viewInfo = useComponentViewInfo(parentUuid);
16
+
17
+ const [cellInfo, x, y] = (obsHighlight && getObsInfo ? (
18
+ [
19
+ getObsInfo(obsHighlight),
20
+ ...(viewInfo && viewInfo.project ? viewInfo.project(obsHighlight) : [null, null]),
21
+ ]
22
+ ) : ([null, null, null]));
23
+
24
+ return (
25
+ (cellInfo ? (
26
+ <Tooltip2D
27
+ x={x}
28
+ y={y}
29
+ parentUuid={parentUuid}
30
+ sourceUuid={sourceUuid}
31
+ parentWidth={width}
32
+ parentHeight={height}
33
+ >
34
+ <TooltipContent info={cellInfo} />
35
+ </Tooltip2D>
36
+ ) : null)
37
+ );
38
+ }
package/src/index.js ADDED
@@ -0,0 +1,11 @@
1
+ export { default as Scatterplot } from './Scatterplot.js';
2
+ export { default as ScatterplotOptions } from './ScatterplotOptions.js';
3
+ export { default as ScatterplotTooltipSubscriber } from './ScatterplotTooltipSubscriber.js';
4
+ export { default as EmptyMessage } from './EmptyMessage.js';
5
+ export {
6
+ getPointSizeDevicePixels,
7
+ getPointOpacity,
8
+ getOnHoverCallback,
9
+ createQuadTree,
10
+ AbstractSpatialOrScatterplot,
11
+ } from './shared-spatial-scatterplot/index.js';
@@ -0,0 +1,280 @@
1
+ import React, { PureComponent } from 'react';
2
+ import { deck, DEFAULT_GL_OPTIONS } from '@vitessce/gl';
3
+ import ToolMenu from './ToolMenu.js';
4
+ import { getCursor, getCursorWithTool } from './cursor.js';
5
+
6
+ /**
7
+ * Abstract class component intended to be inherited by
8
+ * the Spatial and Scatterplot class components.
9
+ * Contains a common constructor, common DeckGL callbacks,
10
+ * and common render function.
11
+ */
12
+ export default class AbstractSpatialOrScatterplot extends PureComponent {
13
+ constructor(props) {
14
+ super(props);
15
+
16
+ this.state = {
17
+ gl: null,
18
+ tool: null,
19
+ };
20
+
21
+ this.viewport = null;
22
+ this.onViewStateChange = this.onViewStateChange.bind(this);
23
+ this.onInitializeViewInfo = this.onInitializeViewInfo.bind(this);
24
+ this.onWebGLInitialized = this.onWebGLInitialized.bind(this);
25
+ this.onToolChange = this.onToolChange.bind(this);
26
+ this.onHover = this.onHover.bind(this);
27
+ this.recenter = this.recenter.bind(this);
28
+ }
29
+
30
+ /**
31
+ * Called by DeckGL upon a viewState change,
32
+ * for example zoom or pan interaction.
33
+ * Emit the new viewState to the `setViewState`
34
+ * handler prop.
35
+ * @param {object} params
36
+ * @param {object} params.viewState The next deck.gl viewState.
37
+ */
38
+ onViewStateChange({ viewState: nextViewState }) {
39
+ const {
40
+ setViewState, viewState, layers, spatialAxisFixed,
41
+ } = this.props;
42
+ const use3d = layers?.some(l => l.use3d);
43
+ setViewState({
44
+ ...nextViewState,
45
+ // If the axis is fixed, just use the current target in state i.e don't change target.
46
+ target: spatialAxisFixed && use3d ? viewState.target : nextViewState.target,
47
+ });
48
+ }
49
+
50
+ /**
51
+ * Called by DeckGL upon viewport
52
+ * initialization.
53
+ * @param {object} viewState
54
+ * @param {object} viewState.viewport
55
+ */
56
+ onInitializeViewInfo({ viewport }) {
57
+ this.viewport = viewport;
58
+ }
59
+
60
+ /**
61
+ * Called by DeckGL upon initialization,
62
+ * helps to understand when to pass layers
63
+ * to the DeckGL component.
64
+ * @param {object} gl The WebGL context object.
65
+ */
66
+ onWebGLInitialized(gl) {
67
+ this.setState({ gl });
68
+ }
69
+
70
+ /**
71
+ * Called by the ToolMenu buttons.
72
+ * Emits the new tool value to the
73
+ * `onToolChange` prop.
74
+ * @param {string} tool Name of tool.
75
+ */
76
+ onToolChange(tool) {
77
+ const { onToolChange: onToolChangeProp } = this.props;
78
+ this.setState({ tool });
79
+ if (onToolChangeProp) {
80
+ onToolChangeProp(tool);
81
+ }
82
+ }
83
+
84
+ /**
85
+ * Create the DeckGL layers.
86
+ * @returns {object[]} Array of
87
+ * DeckGL layer objects.
88
+ * Intended to be overriden by descendants.
89
+ */
90
+ // eslint-disable-next-line class-methods-use-this
91
+ getLayers() {
92
+ return [];
93
+ }
94
+
95
+ // eslint-disable-next-line consistent-return
96
+ onHover(info) {
97
+ const {
98
+ coordinate, sourceLayer: layer, tile,
99
+ } = info;
100
+ const {
101
+ setCellHighlight, cellHighlight, setComponentHover, layers,
102
+ } = this.props;
103
+ const hasBitmask = (layers || []).some(l => l.type === 'bitmask');
104
+ if (!setCellHighlight || !tile) {
105
+ return null;
106
+ }
107
+ if (!layer || !coordinate) {
108
+ if (cellHighlight && hasBitmask) {
109
+ setCellHighlight(null);
110
+ }
111
+ return null;
112
+ }
113
+ const {
114
+ content,
115
+ bbox,
116
+ index: { z },
117
+ } = tile;
118
+ if (!content) {
119
+ if (cellHighlight && hasBitmask) {
120
+ setCellHighlight(null);
121
+ }
122
+ return null;
123
+ }
124
+ const { data, width, height } = content;
125
+ const {
126
+ left, right, top, bottom,
127
+ } = bbox;
128
+ const bounds = [
129
+ left,
130
+ data.height < layer.tileSize ? height : bottom,
131
+ data.width < layer.tileSize ? width : right,
132
+ top,
133
+ ];
134
+ if (!data) {
135
+ if (cellHighlight && hasBitmask) {
136
+ setCellHighlight(null);
137
+ }
138
+ return null;
139
+ }
140
+ // Tiled layer needs a custom layerZoomScale.
141
+ if (layer.id.includes('bitmask')) {
142
+ // The zoomed out layer needs to use the fixed zoom at which it is rendered.
143
+ const layerZoomScale = Math.max(
144
+ 1,
145
+ 2 ** Math.round(-z),
146
+ );
147
+ const dataCoords = [
148
+ Math.floor((coordinate[0] - bounds[0]) / layerZoomScale),
149
+ Math.floor((coordinate[1] - bounds[3]) / layerZoomScale),
150
+ ];
151
+ const coords = dataCoords[1] * width + dataCoords[0];
152
+ const hoverData = data.map(d => d[coords]);
153
+ const cellId = hoverData.find(i => i > 0);
154
+ if (cellId !== Number(cellHighlight)) {
155
+ if (setComponentHover) {
156
+ setComponentHover();
157
+ }
158
+ // eslint-disable-next-line no-unused-expressions
159
+ setCellHighlight(cellId ? String(cellId) : null);
160
+ }
161
+ }
162
+ }
163
+
164
+ /**
165
+ * Emits a function to project from the
166
+ * cell ID space to the scatterplot or
167
+ * spatial coordinate space, via the
168
+ * `updateViewInfo` prop.
169
+ */
170
+ viewInfoDidUpdate(obsIndex, obsLocations, makeGetObsCoords) {
171
+ const { updateViewInfo, uuid } = this.props;
172
+ const { viewport } = this;
173
+ if (updateViewInfo && viewport) {
174
+ updateViewInfo({
175
+ uuid,
176
+ project: (obsId) => {
177
+ try {
178
+ if (obsIndex && obsLocations) {
179
+ const getObsCoords = makeGetObsCoords(obsLocations);
180
+ const obsIdx = obsIndex.indexOf(obsId);
181
+ const obsCoord = getObsCoords(obsIdx);
182
+ return viewport.project(obsCoord);
183
+ }
184
+ return [null, null];
185
+ } catch (e) {
186
+ return [null, null];
187
+ }
188
+ },
189
+ });
190
+ }
191
+ }
192
+
193
+ /**
194
+ * Intended to be overridden by descendants.
195
+ */
196
+ // eslint-disable-next-line @typescript-eslint/no-empty-function
197
+ componentDidUpdate() {
198
+
199
+ }
200
+
201
+ /** Intended to be overridden by descendants.
202
+ * Resets the view type to its original position.
203
+ */
204
+ // eslint-disable-next-line class-methods-use-this
205
+ recenter() {}
206
+
207
+ /**
208
+ * Intended to be overridden by descendants.
209
+ * @returns {boolean} Whether or not any layers are 3D.
210
+ */
211
+ // eslint-disable-next-line class-methods-use-this
212
+ use3d() {
213
+ return false;
214
+ }
215
+
216
+ /**
217
+ * A common render function for both Spatial
218
+ * and Scatterplot components.
219
+ */
220
+ render() {
221
+ const {
222
+ deckRef, viewState, uuid, hideTools,
223
+ } = this.props;
224
+ const { gl, tool } = this.state;
225
+ const layers = this.getLayers();
226
+ const use3d = this.use3d();
227
+
228
+ const showCellSelectionTools = this.obsSegmentationsData !== null;
229
+ const showPanTool = layers.length > 0;
230
+ // For large datasets or ray casting, the visual quality takes only a small
231
+ // hit in exchange for much better performance by setting this to false:
232
+ // https://deck.gl/docs/api-reference/core/deck#usedevicepixels
233
+ const useDevicePixels = (!use3d
234
+ && (
235
+ this.obsSegmentationsData?.shape?.[0] < 100000
236
+ || this.obsLocationsData?.shape?.[1] < 100000
237
+ )
238
+ );
239
+
240
+ return (
241
+ <>
242
+ <ToolMenu
243
+ activeTool={tool}
244
+ setActiveTool={this.onToolChange}
245
+ visibleTools={{
246
+ pan: showPanTool && !hideTools,
247
+ selectLasso: showCellSelectionTools && !hideTools,
248
+ }}
249
+ recenterOnClick={this.recenter}
250
+ />
251
+ <deck.DeckGL
252
+ id={`deckgl-overlay-${uuid}`}
253
+ ref={deckRef}
254
+ views={[
255
+ use3d
256
+ ? new deck.OrbitView({ id: 'orbit', controller: true, orbitAxis: 'Y' })
257
+ : new deck.OrthographicView({
258
+ id: 'ortho',
259
+ }),
260
+ ]} // id is a fix for https://github.com/uber/deck.gl/issues/3259
261
+ layers={
262
+ gl && viewState.target.slice(0, 2).every(i => typeof i === 'number')
263
+ ? layers
264
+ : []
265
+ }
266
+ glOptions={DEFAULT_GL_OPTIONS}
267
+ onWebGLInitialized={this.onWebGLInitialized}
268
+ onViewStateChange={this.onViewStateChange}
269
+ viewState={viewState}
270
+ useDevicePixels={useDevicePixels}
271
+ controller={tool ? { dragPan: false } : true}
272
+ getCursor={tool ? getCursorWithTool : getCursor}
273
+ onHover={this.onHover}
274
+ >
275
+ {this.onInitializeViewInfo}
276
+ </deck.DeckGL>
277
+ </>
278
+ );
279
+ }
280
+ }