@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.
- package/dist/deflate-24a61b7a.js +13 -0
- package/dist/index-d78d4568.js +142036 -0
- package/dist/index.js +15 -5
- package/dist/jpeg-19791032.js +840 -0
- package/dist/lerc-867fd2ef.js +2014 -0
- package/dist/lzw-40577136.js +128 -0
- package/dist/packbits-eab43a40.js +30 -0
- package/dist/pako.esm-68f84e2a.js +4022 -0
- package/dist/raw-66364181.js +12 -0
- package/dist/webimage-0fe27785.js +32 -0
- package/dist-tsc/EmptyMessage.d.ts +2 -0
- package/dist-tsc/EmptyMessage.d.ts.map +1 -0
- package/dist-tsc/Scatterplot.d.ts +10 -0
- package/dist-tsc/Scatterplot.d.ts.map +1 -0
- package/{dist → dist-tsc}/Scatterplot.js +12 -2
- package/dist-tsc/ScatterplotOptions.d.ts +2 -0
- package/dist-tsc/ScatterplotOptions.d.ts.map +1 -0
- package/{dist → dist-tsc}/ScatterplotOptions.js +13 -7
- package/dist-tsc/ScatterplotTooltipSubscriber.d.ts +2 -0
- package/dist-tsc/ScatterplotTooltipSubscriber.d.ts.map +1 -0
- package/dist-tsc/index.d.ts +6 -0
- package/dist-tsc/index.d.ts.map +1 -0
- package/dist-tsc/index.js +5 -0
- package/dist-tsc/shared-spatial-scatterplot/AbstractSpatialOrScatterplot.d.ts +82 -0
- package/dist-tsc/shared-spatial-scatterplot/AbstractSpatialOrScatterplot.d.ts.map +1 -0
- package/{dist → dist-tsc}/shared-spatial-scatterplot/AbstractSpatialOrScatterplot.js +9 -4
- package/dist-tsc/shared-spatial-scatterplot/ToolMenu.d.ts +4 -0
- package/dist-tsc/shared-spatial-scatterplot/ToolMenu.d.ts.map +1 -0
- package/{dist → dist-tsc}/shared-spatial-scatterplot/ToolMenu.js +32 -7
- package/dist-tsc/shared-spatial-scatterplot/ToolMenu.test.d.ts +2 -0
- package/dist-tsc/shared-spatial-scatterplot/ToolMenu.test.d.ts.map +1 -0
- package/dist-tsc/shared-spatial-scatterplot/ToolMenu.test.js +23 -0
- package/dist-tsc/shared-spatial-scatterplot/cursor.d.ts +4 -0
- package/dist-tsc/shared-spatial-scatterplot/cursor.d.ts.map +1 -0
- package/dist-tsc/shared-spatial-scatterplot/dynamic-opacity.d.ts +3 -0
- package/dist-tsc/shared-spatial-scatterplot/dynamic-opacity.d.ts.map +1 -0
- package/{dist → dist-tsc}/shared-spatial-scatterplot/dynamic-opacity.js +1 -1
- package/dist-tsc/shared-spatial-scatterplot/dynamic-opacity.test.d.ts +2 -0
- package/dist-tsc/shared-spatial-scatterplot/dynamic-opacity.test.d.ts.map +1 -0
- package/{dist → dist-tsc}/shared-spatial-scatterplot/dynamic-opacity.test.js +1 -1
- package/dist-tsc/shared-spatial-scatterplot/force-collide-rects.d.ts +13 -0
- package/dist-tsc/shared-spatial-scatterplot/force-collide-rects.d.ts.map +1 -0
- package/dist-tsc/shared-spatial-scatterplot/force-collide-rects.test.d.ts +2 -0
- package/dist-tsc/shared-spatial-scatterplot/force-collide-rects.test.d.ts.map +1 -0
- package/{dist → dist-tsc}/shared-spatial-scatterplot/force-collide-rects.test.js +1 -1
- package/dist-tsc/shared-spatial-scatterplot/index.d.ts +6 -0
- package/dist-tsc/shared-spatial-scatterplot/index.d.ts.map +1 -0
- package/dist-tsc/shared-spatial-scatterplot/index.js +5 -0
- package/dist-tsc/shared-spatial-scatterplot/quadtree.d.ts +10 -0
- package/dist-tsc/shared-spatial-scatterplot/quadtree.d.ts.map +1 -0
- package/{dist → dist-tsc}/shared-spatial-scatterplot/quadtree.js +1 -1
- package/package.json +25 -13
- package/src/EmptyMessage.js +11 -0
- package/src/Scatterplot.js +396 -0
- package/src/ScatterplotOptions.js +269 -0
- package/src/ScatterplotTooltipSubscriber.js +38 -0
- package/src/index.js +11 -0
- package/src/shared-spatial-scatterplot/AbstractSpatialOrScatterplot.js +280 -0
- package/src/shared-spatial-scatterplot/ToolMenu.js +143 -0
- package/src/shared-spatial-scatterplot/ToolMenu.test.jsx +26 -0
- package/src/shared-spatial-scatterplot/cursor.js +23 -0
- package/src/shared-spatial-scatterplot/dynamic-opacity.js +58 -0
- package/src/shared-spatial-scatterplot/dynamic-opacity.test.js +33 -0
- package/src/shared-spatial-scatterplot/force-collide-rects.js +189 -0
- package/src/shared-spatial-scatterplot/force-collide-rects.test.js +72 -0
- package/src/shared-spatial-scatterplot/index.js +8 -0
- package/src/shared-spatial-scatterplot/quadtree.js +27 -0
- package/dist/shared-spatial-scatterplot/ToolMenu.test.js +0 -16
- package/dist/shared-spatial-scatterplot/index.js +0 -5
- /package/{dist → dist-tsc}/EmptyMessage.js +0 -0
- /package/{dist → dist-tsc}/ScatterplotTooltipSubscriber.js +0 -0
- /package/{dist → dist-tsc}/shared-spatial-scatterplot/cursor.js +0 -0
- /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
|
+
}
|