@vitessce/scatterplot-embedding 2.0.3 → 3.0.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/{deflate.0903602c.mjs → deflate-3356a57c.js} +2 -2
- package/dist/{index.30ffff67.mjs → index-b3472cce.js} +34818 -23424
- package/dist/index.js +7 -0
- package/dist/{jpeg.b1faad97.mjs → jpeg-b8a2012a.js} +1 -1
- package/dist/{lerc.6382beb0.mjs → lerc-3fc6f6c4.js} +76 -5
- package/dist/{lzw.db43ecd8.mjs → lzw-abc3998f.js} +1 -1
- package/dist/{packbits.0886c4a4.mjs → packbits-f739d6c6.js} +1 -1
- package/dist/{pako.esm.4b234125.mjs → pako.esm-68f84e2a.js} +97 -15
- package/dist/{raw.779e896c.mjs → raw-f14877a3.js} +1 -1
- package/dist/{webimage.53b96d4c.mjs → webimage-be9ed9e6.js} +1 -1
- package/dist-tsc/EmbeddingScatterplotOptions.d.ts +2 -0
- package/dist-tsc/EmbeddingScatterplotOptions.d.ts.map +1 -0
- package/dist-tsc/EmbeddingScatterplotOptions.js +17 -0
- package/dist-tsc/EmbeddingScatterplotSubscriber.d.ts +22 -0
- package/dist-tsc/EmbeddingScatterplotSubscriber.d.ts.map +1 -0
- package/dist-tsc/EmbeddingScatterplotSubscriber.js +170 -0
- package/dist-tsc/index.d.ts +2 -0
- package/dist-tsc/index.d.ts.map +1 -0
- package/dist-tsc/index.js +1 -1
- package/package.json +22 -12
- package/src/EmbeddingScatterplotOptions.js +1 -2
- package/src/EmbeddingScatterplotSubscriber.js +87 -40
- package/src/index.js +1 -1
- package/dist/index.mjs +0 -8
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import React, { useState, useEffect, useCallback, useMemo, } from 'react';
|
|
3
|
+
import { extent } from 'd3-array';
|
|
4
|
+
import { isEqual } from 'lodash-es';
|
|
5
|
+
import plur from 'plur';
|
|
6
|
+
import { TitleInfo, useReady, useUrls, useDeckCanvasSize, useUint8FeatureSelection, useExpressionValueGetter, useGetObsInfo, useObsEmbeddingData, useObsSetsData, useFeatureSelection, useObsFeatureMatrixIndices, useFeatureLabelsData, useMultiObsLabels, useCoordination, useLoaders, useSetComponentHover, useSetComponentViewInfo, useInitialCoordination, } from '@vitessce/vit-s';
|
|
7
|
+
import { setObsSelection, mergeObsSets, getCellSetPolygons } from '@vitessce/sets-utils';
|
|
8
|
+
import { getCellColors, commaNumber } from '@vitessce/utils';
|
|
9
|
+
import { Scatterplot, ScatterplotTooltipSubscriber, ScatterplotOptions, getPointSizeDevicePixels, getPointOpacity, } from '@vitessce/scatterplot';
|
|
10
|
+
import { Legend } from '@vitessce/legend';
|
|
11
|
+
import { ViewType, COMPONENT_COORDINATION_TYPES } from '@vitessce/constants-internal';
|
|
12
|
+
/**
|
|
13
|
+
* A subscriber component for the scatterplot.
|
|
14
|
+
* @param {object} props
|
|
15
|
+
* @param {number} props.uuid The unique identifier for this component.
|
|
16
|
+
* @param {string} props.theme The current theme name.
|
|
17
|
+
* @param {object} props.coordinationScopes The mapping from coordination types to coordination
|
|
18
|
+
* scopes.
|
|
19
|
+
* @param {function} props.removeGridComponent The callback function to pass to TitleInfo,
|
|
20
|
+
* to call when the component has been removed from the grid.
|
|
21
|
+
* @param {string} props.title An override value for the component title.
|
|
22
|
+
* @param {number} props.averageFillDensity Override the average fill density calculation
|
|
23
|
+
* when using dynamic opacity mode.
|
|
24
|
+
*/
|
|
25
|
+
export function EmbeddingScatterplotSubscriber(props) {
|
|
26
|
+
const { uuid, coordinationScopes, removeGridComponent, theme, observationsLabelOverride, title: titleOverride,
|
|
27
|
+
// Average fill density for dynamic opacity calculation.
|
|
28
|
+
averageFillDensity, } = props;
|
|
29
|
+
const loaders = useLoaders();
|
|
30
|
+
const setComponentHover = useSetComponentHover();
|
|
31
|
+
const setComponentViewInfo = useSetComponentViewInfo(uuid);
|
|
32
|
+
// Get "props" from the coordination space.
|
|
33
|
+
const [{ dataset, obsType, featureType, featureValueType, embeddingZoom: zoom, embeddingTargetX: targetX, embeddingTargetY: targetY, embeddingTargetZ: targetZ, embeddingType: mapping, obsFilter: cellFilter, obsHighlight: cellHighlight, featureSelection: geneSelection, obsSetSelection: cellSetSelection, obsSetColor: cellSetColor, obsColorEncoding: cellColorEncoding, additionalObsSets: additionalCellSets, embeddingObsSetPolygonsVisible: cellSetPolygonsVisible, embeddingObsSetLabelsVisible: cellSetLabelsVisible, embeddingObsSetLabelSize: cellSetLabelSize, embeddingObsRadius: cellRadiusFixed, embeddingObsRadiusMode: cellRadiusMode, embeddingObsOpacity: cellOpacityFixed, embeddingObsOpacityMode: cellOpacityMode, featureValueColormap: geneExpressionColormap, featureValueColormapRange: geneExpressionColormapRange, tooltipsVisible, }, { setEmbeddingZoom: setZoom, setEmbeddingTargetX: setTargetX, setEmbeddingTargetY: setTargetY, setEmbeddingTargetZ: setTargetZ, setObsFilter: setCellFilter, setObsSetSelection: setCellSetSelection, setObsHighlight: setCellHighlight, setObsSetColor: setCellSetColor, setObsColorEncoding: setCellColorEncoding, setAdditionalObsSets: setAdditionalCellSets, setEmbeddingObsSetPolygonsVisible: setCellSetPolygonsVisible, setEmbeddingObsSetLabelsVisible: setCellSetLabelsVisible, setEmbeddingObsSetLabelSize: setCellSetLabelSize, setEmbeddingObsRadius: setCellRadiusFixed, setEmbeddingObsRadiusMode: setCellRadiusMode, setEmbeddingObsOpacity: setCellOpacityFixed, setEmbeddingObsOpacityMode: setCellOpacityMode, setFeatureValueColormap: setGeneExpressionColormap, setFeatureValueColormapRange: setGeneExpressionColormapRange, setTooltipsVisible, }] = useCoordination(COMPONENT_COORDINATION_TYPES[ViewType.SCATTERPLOT], coordinationScopes);
|
|
34
|
+
const { embeddingZoom: initialZoom, embeddingTargetX: initialTargetX, embeddingTargetY: initialTargetY, } = useInitialCoordination(COMPONENT_COORDINATION_TYPES[ViewType.SCATTERPLOT], coordinationScopes);
|
|
35
|
+
const observationsLabel = observationsLabelOverride || obsType;
|
|
36
|
+
const [width, height, deckRef] = useDeckCanvasSize();
|
|
37
|
+
const title = titleOverride || `Scatterplot (${mapping})`;
|
|
38
|
+
const [obsLabelsTypes, obsLabelsData] = useMultiObsLabels(coordinationScopes, obsType, loaders, dataset);
|
|
39
|
+
// Get data from loaders using the data hooks.
|
|
40
|
+
const [{ obsIndex: obsEmbeddingIndex, obsEmbedding }, obsEmbeddingStatus, obsEmbeddingUrls,] = useObsEmbeddingData(loaders, dataset, true, {}, {}, { obsType, embeddingType: mapping });
|
|
41
|
+
const cellsCount = obsEmbeddingIndex?.length || 0;
|
|
42
|
+
const [{ obsSets: cellSets, obsSetsMembership }, obsSetsStatus, obsSetsUrls] = useObsSetsData(loaders, dataset, false, { setObsSetSelection: setCellSetSelection, setObsSetColor: setCellSetColor }, { obsSetSelection: cellSetSelection, obsSetColor: cellSetColor }, { obsType });
|
|
43
|
+
// eslint-disable-next-line no-unused-vars
|
|
44
|
+
const [expressionData, loadedFeatureSelection, featureSelectionStatus] = useFeatureSelection(loaders, dataset, false, geneSelection, { obsType, featureType, featureValueType });
|
|
45
|
+
const [{ obsIndex: matrixObsIndex }, matrixIndicesStatus, matrixIndicesUrls,] = useObsFeatureMatrixIndices(loaders, dataset, false, { obsType, featureType, featureValueType });
|
|
46
|
+
const [{ featureLabelsMap }, featureLabelsStatus, featureLabelsUrls] = useFeatureLabelsData(loaders, dataset, false, {}, {}, { featureType });
|
|
47
|
+
const isReady = useReady([
|
|
48
|
+
obsEmbeddingStatus,
|
|
49
|
+
obsSetsStatus,
|
|
50
|
+
featureSelectionStatus,
|
|
51
|
+
featureLabelsStatus,
|
|
52
|
+
matrixIndicesStatus,
|
|
53
|
+
]);
|
|
54
|
+
const urls = useUrls([
|
|
55
|
+
obsEmbeddingUrls,
|
|
56
|
+
obsSetsUrls,
|
|
57
|
+
matrixIndicesUrls,
|
|
58
|
+
featureLabelsUrls,
|
|
59
|
+
]);
|
|
60
|
+
const [dynamicCellRadius, setDynamicCellRadius] = useState(cellRadiusFixed);
|
|
61
|
+
const [dynamicCellOpacity, setDynamicCellOpacity] = useState(cellOpacityFixed);
|
|
62
|
+
const [originalViewState, setOriginalViewState] = useState(null);
|
|
63
|
+
const mergedCellSets = useMemo(() => mergeObsSets(cellSets, additionalCellSets), [cellSets, additionalCellSets]);
|
|
64
|
+
const setCellSelectionProp = useCallback((v) => {
|
|
65
|
+
setObsSelection(v, additionalCellSets, cellSetColor, setCellSetSelection, setAdditionalCellSets, setCellSetColor, setCellColorEncoding);
|
|
66
|
+
}, [additionalCellSets, cellSetColor, setCellColorEncoding,
|
|
67
|
+
setAdditionalCellSets, setCellSetColor, setCellSetSelection]);
|
|
68
|
+
const cellColors = useMemo(() => getCellColors({
|
|
69
|
+
cellSets: mergedCellSets,
|
|
70
|
+
cellSetSelection,
|
|
71
|
+
cellSetColor,
|
|
72
|
+
obsIndex: matrixObsIndex,
|
|
73
|
+
theme,
|
|
74
|
+
}), [mergedCellSets, theme,
|
|
75
|
+
cellSetSelection, cellSetColor, matrixObsIndex]);
|
|
76
|
+
// cellSetPolygonCache is an array of tuples like [(key0, val0), (key1, val1), ...],
|
|
77
|
+
// where the keys are cellSetSelection arrays.
|
|
78
|
+
const [cellSetPolygonCache, setCellSetPolygonCache] = useState([]);
|
|
79
|
+
const cacheHas = (cache, key) => cache.findIndex(el => isEqual(el[0], key)) !== -1;
|
|
80
|
+
const cacheGet = (cache, key) => cache.find(el => isEqual(el[0], key))?.[1];
|
|
81
|
+
const cellSetPolygons = useMemo(() => {
|
|
82
|
+
if ((cellSetLabelsVisible || cellSetPolygonsVisible)
|
|
83
|
+
&& !cacheHas(cellSetPolygonCache, cellSetSelection)
|
|
84
|
+
&& mergedCellSets?.tree?.length
|
|
85
|
+
&& obsEmbedding
|
|
86
|
+
&& obsEmbeddingIndex
|
|
87
|
+
&& cellSetColor?.length) {
|
|
88
|
+
const newCellSetPolygons = getCellSetPolygons({
|
|
89
|
+
obsIndex: obsEmbeddingIndex,
|
|
90
|
+
obsEmbedding,
|
|
91
|
+
cellSets: mergedCellSets,
|
|
92
|
+
cellSetSelection,
|
|
93
|
+
cellSetColor,
|
|
94
|
+
theme,
|
|
95
|
+
});
|
|
96
|
+
setCellSetPolygonCache(cache => [...cache, [cellSetSelection, newCellSetPolygons]]);
|
|
97
|
+
return newCellSetPolygons;
|
|
98
|
+
}
|
|
99
|
+
return cacheGet(cellSetPolygonCache, cellSetSelection) || [];
|
|
100
|
+
}, [cellSetPolygonsVisible, cellSetPolygonCache, cellSetLabelsVisible, theme,
|
|
101
|
+
obsEmbeddingIndex, obsEmbedding, mergedCellSets, cellSetSelection, cellSetColor]);
|
|
102
|
+
const cellSelection = useMemo(() => Array.from(cellColors.keys()), [cellColors]);
|
|
103
|
+
const [xRange, yRange, xExtent, yExtent, numCells] = useMemo(() => {
|
|
104
|
+
if (obsEmbedding && obsEmbedding.data && obsEmbedding.shape) {
|
|
105
|
+
const cellCount = obsEmbedding.shape[1];
|
|
106
|
+
const xE = extent(obsEmbedding.data[0]);
|
|
107
|
+
const yE = extent(obsEmbedding.data[1]);
|
|
108
|
+
const xR = xE[1] - xE[0];
|
|
109
|
+
const yR = yE[1] - yE[0];
|
|
110
|
+
return [xR, yR, xE, yE, cellCount];
|
|
111
|
+
}
|
|
112
|
+
return [null, null, null, null, null];
|
|
113
|
+
}, [obsEmbedding]);
|
|
114
|
+
// After cells have loaded or changed,
|
|
115
|
+
// compute the cell radius scale based on the
|
|
116
|
+
// extents of the cell coordinates on the x/y axes.
|
|
117
|
+
useEffect(() => {
|
|
118
|
+
if (xRange && yRange) {
|
|
119
|
+
const pointSizeDevicePixels = getPointSizeDevicePixels(window.devicePixelRatio, zoom, xRange, yRange, width, height);
|
|
120
|
+
setDynamicCellRadius(pointSizeDevicePixels);
|
|
121
|
+
const nextCellOpacityScale = getPointOpacity(zoom, xRange, yRange, width, height, numCells, averageFillDensity);
|
|
122
|
+
setDynamicCellOpacity(nextCellOpacityScale);
|
|
123
|
+
if (typeof initialTargetX !== 'number' || typeof initialTargetY !== 'number') {
|
|
124
|
+
// The view config did not define an initial viewState so
|
|
125
|
+
// we calculate one based on the data and set it.
|
|
126
|
+
const newTargetX = xExtent[0] + xRange / 2;
|
|
127
|
+
const newTargetY = yExtent[0] + yRange / 2;
|
|
128
|
+
const newZoom = Math.log2(Math.min(width / xRange, height / yRange));
|
|
129
|
+
const notYetInitialized = (typeof targetX !== 'number' || typeof targetY !== 'number');
|
|
130
|
+
const stillDefaultInitialized = (targetX === newTargetX && targetY === -newTargetY);
|
|
131
|
+
if (notYetInitialized || stillDefaultInitialized) {
|
|
132
|
+
setTargetX(newTargetX);
|
|
133
|
+
// Graphics rendering has the y-axis going south so we need to multiply by negative one.
|
|
134
|
+
setTargetY(-newTargetY);
|
|
135
|
+
setZoom(newZoom);
|
|
136
|
+
}
|
|
137
|
+
setOriginalViewState({ target: [newTargetX, -newTargetY, 0], zoom: newZoom });
|
|
138
|
+
}
|
|
139
|
+
else if (!originalViewState) {
|
|
140
|
+
// originalViewState has not yet been set and
|
|
141
|
+
// the view config defined an initial viewState.
|
|
142
|
+
setOriginalViewState({ target: [initialTargetX, initialTargetY, 0], zoom: initialZoom });
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
146
|
+
}, [xRange, yRange, xExtent, yExtent, numCells,
|
|
147
|
+
width, height, initialZoom, zoom, initialTargetX, initialTargetY, averageFillDensity]);
|
|
148
|
+
const getObsInfo = useGetObsInfo(observationsLabel, obsLabelsTypes, obsLabelsData, obsSetsMembership);
|
|
149
|
+
const cellSelectionSet = useMemo(() => new Set(cellSelection), [cellSelection]);
|
|
150
|
+
const getCellIsSelected = useCallback((object, { index }) => ((cellSelectionSet || new Set([])).has(obsEmbeddingIndex[index]) ? 1.0 : 0.0), [cellSelectionSet, obsEmbeddingIndex]);
|
|
151
|
+
const cellRadius = (cellRadiusMode === 'manual' ? cellRadiusFixed : dynamicCellRadius);
|
|
152
|
+
const cellOpacity = (cellOpacityMode === 'manual' ? cellOpacityFixed : dynamicCellOpacity);
|
|
153
|
+
const [uint8ExpressionData, expressionExtents] = useUint8FeatureSelection(expressionData);
|
|
154
|
+
// Set up a getter function for gene expression values, to be used
|
|
155
|
+
// by the DeckGL layer to obtain values for instanced attributes.
|
|
156
|
+
const getExpressionValue = useExpressionValueGetter({
|
|
157
|
+
instanceObsIndex: obsEmbeddingIndex,
|
|
158
|
+
matrixObsIndex,
|
|
159
|
+
expressionData: uint8ExpressionData,
|
|
160
|
+
});
|
|
161
|
+
const setViewState = ({ zoom: newZoom, target }) => {
|
|
162
|
+
setZoom(newZoom);
|
|
163
|
+
setTargetX(target[0]);
|
|
164
|
+
setTargetY(target[1]);
|
|
165
|
+
setTargetZ(target[2] || 0);
|
|
166
|
+
};
|
|
167
|
+
return (_jsxs(TitleInfo, { title: title, info: `${commaNumber(cellsCount)} ${plur(observationsLabel, cellsCount)}`, removeGridComponent: removeGridComponent, urls: urls, theme: theme, isReady: isReady, options: (_jsx(ScatterplotOptions, { observationsLabel: observationsLabel, cellRadius: cellRadiusFixed, setCellRadius: setCellRadiusFixed, cellRadiusMode: cellRadiusMode, setCellRadiusMode: setCellRadiusMode, cellOpacity: cellOpacityFixed, setCellOpacity: setCellOpacityFixed, cellOpacityMode: cellOpacityMode, setCellOpacityMode: setCellOpacityMode, cellSetLabelsVisible: cellSetLabelsVisible, setCellSetLabelsVisible: setCellSetLabelsVisible, tooltipsVisible: tooltipsVisible, setTooltipsVisible: setTooltipsVisible, cellSetLabelSize: cellSetLabelSize, setCellSetLabelSize: setCellSetLabelSize, cellSetPolygonsVisible: cellSetPolygonsVisible, setCellSetPolygonsVisible: setCellSetPolygonsVisible, cellColorEncoding: cellColorEncoding, setCellColorEncoding: setCellColorEncoding, geneExpressionColormap: geneExpressionColormap, setGeneExpressionColormap: setGeneExpressionColormap, geneExpressionColormapRange: geneExpressionColormapRange, setGeneExpressionColormapRange: setGeneExpressionColormapRange })), children: [_jsx(Scatterplot, { ref: deckRef, uuid: uuid, theme: theme, viewState: { zoom, target: [targetX, targetY, targetZ] }, setViewState: setViewState, originalViewState: originalViewState, obsEmbeddingIndex: obsEmbeddingIndex, obsEmbedding: obsEmbedding, cellFilter: cellFilter, cellSelection: cellSelection, cellHighlight: cellHighlight, cellColors: cellColors, cellSetPolygons: cellSetPolygons, cellSetLabelSize: cellSetLabelSize, cellSetLabelsVisible: cellSetLabelsVisible, cellSetPolygonsVisible: cellSetPolygonsVisible, setCellFilter: setCellFilter, setCellSelection: setCellSelectionProp, setCellHighlight: setCellHighlight, cellRadius: cellRadius, cellOpacity: cellOpacity, cellColorEncoding: cellColorEncoding, geneExpressionColormap: geneExpressionColormap, geneExpressionColormapRange: geneExpressionColormapRange, setComponentHover: () => {
|
|
168
|
+
setComponentHover(uuid);
|
|
169
|
+
}, updateViewInfo: setComponentViewInfo, getExpressionValue: getExpressionValue, getCellIsSelected: getCellIsSelected }), tooltipsVisible && (_jsx(ScatterplotTooltipSubscriber, { parentUuid: uuid, obsHighlight: cellHighlight, width: width, height: height, getObsInfo: getObsInfo })), _jsx(Legend, { visible: true, theme: theme, featureType: featureType, featureValueType: featureValueType, obsColorEncoding: cellColorEncoding, featureSelection: geneSelection, featureLabelsMap: featureLabelsMap, featureValueColormap: geneExpressionColormap, featureValueColormapRange: geneExpressionColormapRange, extent: expressionExtents?.[0] })] }));
|
|
170
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.js"],"names":[],"mappings":""}
|
package/dist-tsc/index.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export { EmbeddingScatterplotSubscriber
|
|
1
|
+
export { EmbeddingScatterplotSubscriber } from './EmbeddingScatterplotSubscriber.js';
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vitessce/scatterplot-embedding",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "3.0.1",
|
|
4
4
|
"author": "Gehlenborg Lab",
|
|
5
5
|
"homepage": "http://vitessce.io",
|
|
6
6
|
"repository": {
|
|
@@ -8,32 +8,42 @@
|
|
|
8
8
|
"url": "git+https://github.com/vitessce/vitessce.git"
|
|
9
9
|
},
|
|
10
10
|
"license": "MIT",
|
|
11
|
-
"
|
|
11
|
+
"type": "module",
|
|
12
|
+
"main": "dist/index.js",
|
|
12
13
|
"files": [
|
|
14
|
+
"src",
|
|
13
15
|
"dist",
|
|
14
|
-
"
|
|
16
|
+
"dist-tsc"
|
|
15
17
|
],
|
|
16
18
|
"dependencies": {
|
|
17
19
|
"@material-ui/core": "~4.12.3",
|
|
18
20
|
"d3-array": "^2.4.0",
|
|
19
|
-
"lodash": "^4.17.21",
|
|
21
|
+
"lodash-es": "^4.17.21",
|
|
20
22
|
"plur": "^5.1.0",
|
|
21
|
-
"@vitessce/constants-internal": "
|
|
22
|
-
"@vitessce/
|
|
23
|
-
"@vitessce/
|
|
24
|
-
"@vitessce/utils": "
|
|
25
|
-
"@vitessce/
|
|
23
|
+
"@vitessce/constants-internal": "3.0.1",
|
|
24
|
+
"@vitessce/legend": "3.0.1",
|
|
25
|
+
"@vitessce/scatterplot": "3.0.1",
|
|
26
|
+
"@vitessce/sets-utils": "3.0.1",
|
|
27
|
+
"@vitessce/utils": "3.0.1",
|
|
28
|
+
"@vitessce/vit-s": "3.0.1"
|
|
26
29
|
},
|
|
27
30
|
"devDependencies": {
|
|
28
31
|
"react": "^18.0.0",
|
|
29
|
-
"vite": "^3.0
|
|
30
|
-
"vitest": "^0.
|
|
32
|
+
"vite": "^4.3.0",
|
|
33
|
+
"vitest": "^0.32.2"
|
|
31
34
|
},
|
|
32
35
|
"peerDependencies": {
|
|
33
36
|
"react": "^16.8.0 || ^17.0.0 || ^18.0.0"
|
|
34
37
|
},
|
|
35
38
|
"scripts": {
|
|
36
39
|
"bundle": "pnpm exec vite build -c ../../../scripts/vite.config.js",
|
|
37
|
-
"test": "pnpm exec vitest --run
|
|
40
|
+
"test": "pnpm exec vitest --run"
|
|
41
|
+
},
|
|
42
|
+
"module": "dist/index.js",
|
|
43
|
+
"exports": {
|
|
44
|
+
".": {
|
|
45
|
+
"types": "./dist-tsc/index.d.ts",
|
|
46
|
+
"import": "./dist/index.js"
|
|
47
|
+
}
|
|
38
48
|
}
|
|
39
49
|
}
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
|
-
import TableCell from '@material-ui/core
|
|
3
|
-
import TableRow from '@material-ui/core/TableRow';
|
|
2
|
+
import { TableCell, TableRow } from '@material-ui/core';
|
|
4
3
|
import { usePlotOptionsStyles, OptionSelect } from '@vitessce/vit-s';
|
|
5
4
|
|
|
6
5
|
export default function EmbeddingScatterplotOptions(props) {
|
|
@@ -2,23 +2,26 @@ import React, {
|
|
|
2
2
|
useState, useEffect, useCallback, useMemo,
|
|
3
3
|
} from 'react';
|
|
4
4
|
import { extent } from 'd3-array';
|
|
5
|
-
import isEqual from 'lodash
|
|
5
|
+
import { isEqual } from 'lodash-es';
|
|
6
6
|
import plur from 'plur';
|
|
7
7
|
import {
|
|
8
8
|
TitleInfo,
|
|
9
|
-
registerPluginViewType,
|
|
10
9
|
useReady, useUrls,
|
|
11
10
|
useDeckCanvasSize,
|
|
12
|
-
|
|
11
|
+
useUint8FeatureSelection,
|
|
12
|
+
useExpressionValueGetter,
|
|
13
|
+
useGetObsInfo,
|
|
13
14
|
useObsEmbeddingData,
|
|
14
15
|
useObsSetsData,
|
|
15
16
|
useFeatureSelection,
|
|
16
17
|
useObsFeatureMatrixIndices,
|
|
18
|
+
useFeatureLabelsData,
|
|
17
19
|
useMultiObsLabels,
|
|
18
20
|
useCoordination,
|
|
19
21
|
useLoaders,
|
|
20
22
|
useSetComponentHover,
|
|
21
23
|
useSetComponentViewInfo,
|
|
24
|
+
useInitialCoordination,
|
|
22
25
|
} from '@vitessce/vit-s';
|
|
23
26
|
import { setObsSelection, mergeObsSets, getCellSetPolygons } from '@vitessce/sets-utils';
|
|
24
27
|
import { getCellColors, commaNumber } from '@vitessce/utils';
|
|
@@ -27,6 +30,7 @@ import {
|
|
|
27
30
|
getPointSizeDevicePixels,
|
|
28
31
|
getPointOpacity,
|
|
29
32
|
} from '@vitessce/scatterplot';
|
|
33
|
+
import { Legend } from '@vitessce/legend';
|
|
30
34
|
import { ViewType, COMPONENT_COORDINATION_TYPES } from '@vitessce/constants-internal';
|
|
31
35
|
|
|
32
36
|
/**
|
|
@@ -36,7 +40,6 @@ import { ViewType, COMPONENT_COORDINATION_TYPES } from '@vitessce/constants-inte
|
|
|
36
40
|
* @param {string} props.theme The current theme name.
|
|
37
41
|
* @param {object} props.coordinationScopes The mapping from coordination types to coordination
|
|
38
42
|
* scopes.
|
|
39
|
-
* @param {boolean} props.disableTooltip Should the tooltip be disabled?
|
|
40
43
|
* @param {function} props.removeGridComponent The callback function to pass to TitleInfo,
|
|
41
44
|
* to call when the component has been removed from the grid.
|
|
42
45
|
* @param {string} props.title An override value for the component title.
|
|
@@ -49,7 +52,6 @@ export function EmbeddingScatterplotSubscriber(props) {
|
|
|
49
52
|
coordinationScopes,
|
|
50
53
|
removeGridComponent,
|
|
51
54
|
theme,
|
|
52
|
-
disableTooltip = false,
|
|
53
55
|
observationsLabelOverride,
|
|
54
56
|
title: titleOverride,
|
|
55
57
|
// Average fill density for dynamic opacity calculation.
|
|
@@ -87,6 +89,7 @@ export function EmbeddingScatterplotSubscriber(props) {
|
|
|
87
89
|
embeddingObsOpacityMode: cellOpacityMode,
|
|
88
90
|
featureValueColormap: geneExpressionColormap,
|
|
89
91
|
featureValueColormapRange: geneExpressionColormapRange,
|
|
92
|
+
tooltipsVisible,
|
|
90
93
|
}, {
|
|
91
94
|
setEmbeddingZoom: setZoom,
|
|
92
95
|
setEmbeddingTargetX: setTargetX,
|
|
@@ -107,27 +110,37 @@ export function EmbeddingScatterplotSubscriber(props) {
|
|
|
107
110
|
setEmbeddingObsOpacityMode: setCellOpacityMode,
|
|
108
111
|
setFeatureValueColormap: setGeneExpressionColormap,
|
|
109
112
|
setFeatureValueColormapRange: setGeneExpressionColormapRange,
|
|
113
|
+
setTooltipsVisible,
|
|
110
114
|
}] = useCoordination(COMPONENT_COORDINATION_TYPES[ViewType.SCATTERPLOT], coordinationScopes);
|
|
111
115
|
|
|
116
|
+
const {
|
|
117
|
+
embeddingZoom: initialZoom,
|
|
118
|
+
embeddingTargetX: initialTargetX,
|
|
119
|
+
embeddingTargetY: initialTargetY,
|
|
120
|
+
} = useInitialCoordination(
|
|
121
|
+
COMPONENT_COORDINATION_TYPES[ViewType.SCATTERPLOT], coordinationScopes,
|
|
122
|
+
);
|
|
123
|
+
|
|
112
124
|
const observationsLabel = observationsLabelOverride || obsType;
|
|
113
125
|
|
|
114
|
-
const [urls, addUrl] = useUrls(loaders, dataset);
|
|
115
126
|
const [width, height, deckRef] = useDeckCanvasSize();
|
|
116
127
|
|
|
117
128
|
const title = titleOverride || `Scatterplot (${mapping})`;
|
|
118
129
|
|
|
119
130
|
const [obsLabelsTypes, obsLabelsData] = useMultiObsLabels(
|
|
120
|
-
coordinationScopes, obsType, loaders, dataset,
|
|
131
|
+
coordinationScopes, obsType, loaders, dataset,
|
|
121
132
|
);
|
|
122
133
|
|
|
123
134
|
// Get data from loaders using the data hooks.
|
|
124
|
-
const [
|
|
125
|
-
|
|
135
|
+
const [
|
|
136
|
+
{ obsIndex: obsEmbeddingIndex, obsEmbedding }, obsEmbeddingStatus, obsEmbeddingUrls,
|
|
137
|
+
] = useObsEmbeddingData(
|
|
138
|
+
loaders, dataset, true, {}, {},
|
|
126
139
|
{ obsType, embeddingType: mapping },
|
|
127
140
|
);
|
|
128
141
|
const cellsCount = obsEmbeddingIndex?.length || 0;
|
|
129
|
-
const [{ obsSets: cellSets, obsSetsMembership }, obsSetsStatus] = useObsSetsData(
|
|
130
|
-
loaders, dataset,
|
|
142
|
+
const [{ obsSets: cellSets, obsSetsMembership }, obsSetsStatus, obsSetsUrls] = useObsSetsData(
|
|
143
|
+
loaders, dataset, false,
|
|
131
144
|
{ setObsSetSelection: setCellSetSelection, setObsSetColor: setCellSetColor },
|
|
132
145
|
{ obsSetSelection: cellSetSelection, obsSetColor: cellSetColor },
|
|
133
146
|
{ obsType },
|
|
@@ -137,21 +150,36 @@ export function EmbeddingScatterplotSubscriber(props) {
|
|
|
137
150
|
loaders, dataset, false, geneSelection,
|
|
138
151
|
{ obsType, featureType, featureValueType },
|
|
139
152
|
);
|
|
140
|
-
const [
|
|
141
|
-
|
|
153
|
+
const [
|
|
154
|
+
{ obsIndex: matrixObsIndex }, matrixIndicesStatus, matrixIndicesUrls,
|
|
155
|
+
] = useObsFeatureMatrixIndices(
|
|
156
|
+
loaders, dataset, false,
|
|
142
157
|
{ obsType, featureType, featureValueType },
|
|
143
158
|
);
|
|
159
|
+
const [{ featureLabelsMap }, featureLabelsStatus, featureLabelsUrls] = useFeatureLabelsData(
|
|
160
|
+
loaders, dataset, false, {}, {},
|
|
161
|
+
{ featureType },
|
|
162
|
+
);
|
|
144
163
|
|
|
145
164
|
const isReady = useReady([
|
|
146
165
|
obsEmbeddingStatus,
|
|
147
166
|
obsSetsStatus,
|
|
148
167
|
featureSelectionStatus,
|
|
168
|
+
featureLabelsStatus,
|
|
149
169
|
matrixIndicesStatus,
|
|
150
170
|
]);
|
|
171
|
+
const urls = useUrls([
|
|
172
|
+
obsEmbeddingUrls,
|
|
173
|
+
obsSetsUrls,
|
|
174
|
+
matrixIndicesUrls,
|
|
175
|
+
featureLabelsUrls,
|
|
176
|
+
]);
|
|
151
177
|
|
|
152
178
|
const [dynamicCellRadius, setDynamicCellRadius] = useState(cellRadiusFixed);
|
|
153
179
|
const [dynamicCellOpacity, setDynamicCellOpacity] = useState(cellOpacityFixed);
|
|
154
180
|
|
|
181
|
+
const [originalViewState, setOriginalViewState] = useState(null);
|
|
182
|
+
|
|
155
183
|
const mergedCellSets = useMemo(() => mergeObsSets(
|
|
156
184
|
cellSets, additionalCellSets,
|
|
157
185
|
), [cellSets, additionalCellSets]);
|
|
@@ -166,16 +194,13 @@ export function EmbeddingScatterplotSubscriber(props) {
|
|
|
166
194
|
setAdditionalCellSets, setCellSetColor, setCellSetSelection]);
|
|
167
195
|
|
|
168
196
|
const cellColors = useMemo(() => getCellColors({
|
|
169
|
-
cellColorEncoding,
|
|
170
|
-
expressionData: expressionData && expressionData[0],
|
|
171
|
-
geneSelection,
|
|
172
197
|
cellSets: mergedCellSets,
|
|
173
198
|
cellSetSelection,
|
|
174
199
|
cellSetColor,
|
|
175
200
|
obsIndex: matrixObsIndex,
|
|
176
201
|
theme,
|
|
177
|
-
}), [
|
|
178
|
-
cellSetSelection, cellSetColor,
|
|
202
|
+
}), [mergedCellSets, theme,
|
|
203
|
+
cellSetSelection, cellSetColor, matrixObsIndex]);
|
|
179
204
|
|
|
180
205
|
// cellSetPolygonCache is an array of tuples like [(key0, val0), (key1, val1), ...],
|
|
181
206
|
// where the keys are cellSetSelection arrays.
|
|
@@ -234,19 +259,30 @@ export function EmbeddingScatterplotSubscriber(props) {
|
|
|
234
259
|
);
|
|
235
260
|
setDynamicCellOpacity(nextCellOpacityScale);
|
|
236
261
|
|
|
237
|
-
if (typeof
|
|
262
|
+
if (typeof initialTargetX !== 'number' || typeof initialTargetY !== 'number') {
|
|
263
|
+
// The view config did not define an initial viewState so
|
|
264
|
+
// we calculate one based on the data and set it.
|
|
238
265
|
const newTargetX = xExtent[0] + xRange / 2;
|
|
239
266
|
const newTargetY = yExtent[0] + yRange / 2;
|
|
240
267
|
const newZoom = Math.log2(Math.min(width / xRange, height / yRange));
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
268
|
+
const notYetInitialized = (typeof targetX !== 'number' || typeof targetY !== 'number');
|
|
269
|
+
const stillDefaultInitialized = (targetX === newTargetX && targetY === -newTargetY);
|
|
270
|
+
if (notYetInitialized || stillDefaultInitialized) {
|
|
271
|
+
setTargetX(newTargetX);
|
|
272
|
+
// Graphics rendering has the y-axis going south so we need to multiply by negative one.
|
|
273
|
+
setTargetY(-newTargetY);
|
|
274
|
+
setZoom(newZoom);
|
|
275
|
+
}
|
|
276
|
+
setOriginalViewState({ target: [newTargetX, -newTargetY, 0], zoom: newZoom });
|
|
277
|
+
} else if (!originalViewState) {
|
|
278
|
+
// originalViewState has not yet been set and
|
|
279
|
+
// the view config defined an initial viewState.
|
|
280
|
+
setOriginalViewState({ target: [initialTargetX, initialTargetY, 0], zoom: initialZoom });
|
|
245
281
|
}
|
|
246
282
|
}
|
|
247
283
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
248
284
|
}, [xRange, yRange, xExtent, yExtent, numCells,
|
|
249
|
-
width, height, zoom, averageFillDensity]);
|
|
285
|
+
width, height, initialZoom, zoom, initialTargetX, initialTargetY, averageFillDensity]);
|
|
250
286
|
|
|
251
287
|
const getObsInfo = useGetObsInfo(
|
|
252
288
|
observationsLabel, obsLabelsTypes, obsLabelsData, obsSetsMembership,
|
|
@@ -260,14 +296,23 @@ export function EmbeddingScatterplotSubscriber(props) {
|
|
|
260
296
|
const cellRadius = (cellRadiusMode === 'manual' ? cellRadiusFixed : dynamicCellRadius);
|
|
261
297
|
const cellOpacity = (cellOpacityMode === 'manual' ? cellOpacityFixed : dynamicCellOpacity);
|
|
262
298
|
|
|
299
|
+
const [uint8ExpressionData, expressionExtents] = useUint8FeatureSelection(expressionData);
|
|
300
|
+
|
|
263
301
|
// Set up a getter function for gene expression values, to be used
|
|
264
302
|
// by the DeckGL layer to obtain values for instanced attributes.
|
|
265
303
|
const getExpressionValue = useExpressionValueGetter({
|
|
266
304
|
instanceObsIndex: obsEmbeddingIndex,
|
|
267
305
|
matrixObsIndex,
|
|
268
|
-
expressionData,
|
|
306
|
+
expressionData: uint8ExpressionData,
|
|
269
307
|
});
|
|
270
308
|
|
|
309
|
+
const setViewState = ({ zoom: newZoom, target }) => {
|
|
310
|
+
setZoom(newZoom);
|
|
311
|
+
setTargetX(target[0]);
|
|
312
|
+
setTargetY(target[1]);
|
|
313
|
+
setTargetZ(target[2] || 0);
|
|
314
|
+
};
|
|
315
|
+
|
|
271
316
|
return (
|
|
272
317
|
<TitleInfo
|
|
273
318
|
title={title}
|
|
@@ -289,6 +334,8 @@ export function EmbeddingScatterplotSubscriber(props) {
|
|
|
289
334
|
setCellOpacityMode={setCellOpacityMode}
|
|
290
335
|
cellSetLabelsVisible={cellSetLabelsVisible}
|
|
291
336
|
setCellSetLabelsVisible={setCellSetLabelsVisible}
|
|
337
|
+
tooltipsVisible={tooltipsVisible}
|
|
338
|
+
setTooltipsVisible={setTooltipsVisible}
|
|
292
339
|
cellSetLabelSize={cellSetLabelSize}
|
|
293
340
|
setCellSetLabelSize={setCellSetLabelSize}
|
|
294
341
|
cellSetPolygonsVisible={cellSetPolygonsVisible}
|
|
@@ -307,12 +354,8 @@ export function EmbeddingScatterplotSubscriber(props) {
|
|
|
307
354
|
uuid={uuid}
|
|
308
355
|
theme={theme}
|
|
309
356
|
viewState={{ zoom, target: [targetX, targetY, targetZ] }}
|
|
310
|
-
setViewState={
|
|
311
|
-
|
|
312
|
-
setTargetX(target[0]);
|
|
313
|
-
setTargetY(target[1]);
|
|
314
|
-
setTargetZ(target[2] || 0);
|
|
315
|
-
}}
|
|
357
|
+
setViewState={setViewState}
|
|
358
|
+
originalViewState={originalViewState}
|
|
316
359
|
obsEmbeddingIndex={obsEmbeddingIndex}
|
|
317
360
|
obsEmbedding={obsEmbedding}
|
|
318
361
|
cellFilter={cellFilter}
|
|
@@ -339,7 +382,7 @@ export function EmbeddingScatterplotSubscriber(props) {
|
|
|
339
382
|
getCellIsSelected={getCellIsSelected}
|
|
340
383
|
|
|
341
384
|
/>
|
|
342
|
-
{
|
|
385
|
+
{tooltipsVisible && (
|
|
343
386
|
<ScatterplotTooltipSubscriber
|
|
344
387
|
parentUuid={uuid}
|
|
345
388
|
obsHighlight={cellHighlight}
|
|
@@ -348,14 +391,18 @@ export function EmbeddingScatterplotSubscriber(props) {
|
|
|
348
391
|
getObsInfo={getObsInfo}
|
|
349
392
|
/>
|
|
350
393
|
)}
|
|
394
|
+
<Legend
|
|
395
|
+
visible
|
|
396
|
+
theme={theme}
|
|
397
|
+
featureType={featureType}
|
|
398
|
+
featureValueType={featureValueType}
|
|
399
|
+
obsColorEncoding={cellColorEncoding}
|
|
400
|
+
featureSelection={geneSelection}
|
|
401
|
+
featureLabelsMap={featureLabelsMap}
|
|
402
|
+
featureValueColormap={geneExpressionColormap}
|
|
403
|
+
featureValueColormapRange={geneExpressionColormapRange}
|
|
404
|
+
extent={expressionExtents?.[0]}
|
|
405
|
+
/>
|
|
351
406
|
</TitleInfo>
|
|
352
407
|
);
|
|
353
408
|
}
|
|
354
|
-
|
|
355
|
-
export function register() {
|
|
356
|
-
registerPluginViewType(
|
|
357
|
-
ViewType.SCATTERPLOT,
|
|
358
|
-
EmbeddingScatterplotSubscriber,
|
|
359
|
-
COMPONENT_COORDINATION_TYPES[ViewType.SCATTERPLOT],
|
|
360
|
-
);
|
|
361
|
-
}
|
package/src/index.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export { EmbeddingScatterplotSubscriber
|
|
1
|
+
export { EmbeddingScatterplotSubscriber } from './EmbeddingScatterplotSubscriber.js';
|