@vitessce/scatterplot 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.65a17097.mjs → deflate-162fa772.js} +2 -2
- package/dist/{index.8206952d.mjs → index-578beee3.js} +61450 -51757
- package/dist/{index.mjs → index.js} +5 -5
- package/dist/{jpeg.4221d32f.mjs → jpeg-00fef901.js} +1 -1
- package/dist/{lerc.8d649494.mjs → lerc-55d38607.js} +76 -5
- package/dist/{lzw.89350f4e.mjs → lzw-c3bdd9f6.js} +1 -1
- package/dist/{packbits.986f9d9f.mjs → packbits-c1c71d64.js} +1 -1
- package/dist/{pako.esm.4b234125.mjs → pako.esm-68f84e2a.js} +97 -15
- package/dist/{raw.1cc73933.mjs → raw-e1c8b3d7.js} +1 -1
- package/dist/{webimage.be69a2d5.mjs → webimage-c7fc297e.js} +1 -1
- package/dist-tsc/EmptyMessage.d.ts +2 -0
- package/dist-tsc/EmptyMessage.d.ts.map +1 -0
- package/dist-tsc/EmptyMessage.js +6 -0
- package/dist-tsc/Scatterplot.d.ts +10 -0
- package/dist-tsc/Scatterplot.d.ts.map +1 -0
- package/dist-tsc/Scatterplot.js +314 -0
- package/dist-tsc/ScatterplotOptions.d.ts +2 -0
- package/dist-tsc/ScatterplotOptions.d.ts.map +1 -0
- package/dist-tsc/ScatterplotOptions.js +56 -0
- package/dist-tsc/ScatterplotTooltipSubscriber.d.ts +2 -0
- package/dist-tsc/ScatterplotTooltipSubscriber.d.ts.map +1 -0
- package/dist-tsc/ScatterplotTooltipSubscriber.js +14 -0
- package/dist-tsc/index.d.ts +6 -0
- package/dist-tsc/index.d.ts.map +1 -0
- package/dist-tsc/index.js +5 -5
- 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-tsc/shared-spatial-scatterplot/AbstractSpatialOrScatterplot.js +218 -0
- 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-tsc/shared-spatial-scatterplot/ToolMenu.js +83 -0
- 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/cursor.js +22 -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-tsc/shared-spatial-scatterplot/dynamic-opacity.js +47 -0
- 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-tsc/shared-spatial-scatterplot/dynamic-opacity.test.js +29 -0
- 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.js +169 -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-tsc/shared-spatial-scatterplot/force-collide-rects.test.js +59 -0
- 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-tsc/shared-spatial-scatterplot/quadtree.js +26 -0
- package/package.json +25 -13
- package/src/Scatterplot.js +13 -2
- package/src/ScatterplotOptions.js +27 -5
- package/src/index.js +5 -5
- package/src/shared-spatial-scatterplot/AbstractSpatialOrScatterplot.js +10 -4
- package/src/shared-spatial-scatterplot/ToolMenu.js +56 -18
- package/src/shared-spatial-scatterplot/ToolMenu.test.jsx +14 -6
- package/src/shared-spatial-scatterplot/dynamic-opacity.js +1 -1
- package/src/shared-spatial-scatterplot/dynamic-opacity.test.js +2 -1
- package/src/shared-spatial-scatterplot/force-collide-rects.test.js +2 -1
- package/src/shared-spatial-scatterplot/index.js +5 -5
- package/src/shared-spatial-scatterplot/quadtree.js +1 -1
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { describe, it, expect } from 'vitest';
|
|
2
|
+
import { forceSimulation } from 'd3-force';
|
|
3
|
+
import { forceCollideRects } from './force-collide-rects.js';
|
|
4
|
+
describe('force-collide-rects.js', () => {
|
|
5
|
+
describe('forceCollideRects', () => {
|
|
6
|
+
it('can be initialized with a size function', () => {
|
|
7
|
+
const collisionForce = forceCollideRects()
|
|
8
|
+
.size(d => ([d.width, d.height]));
|
|
9
|
+
const size = collisionForce.size();
|
|
10
|
+
const [w, h] = size({ width: 2, height: 3 });
|
|
11
|
+
expect(w).toEqual(2);
|
|
12
|
+
expect(h).toEqual(3);
|
|
13
|
+
});
|
|
14
|
+
it('cannot prevent a collision of rects after few iterations', () => {
|
|
15
|
+
const collisionForce = forceCollideRects()
|
|
16
|
+
.size(d => ([d.width, d.height]));
|
|
17
|
+
const nodes = [
|
|
18
|
+
{
|
|
19
|
+
label: 'A', width: 100, height: 100, x: 2, y: 2,
|
|
20
|
+
},
|
|
21
|
+
{
|
|
22
|
+
label: 'B', width: 100, height: 100, x: 3, y: 3,
|
|
23
|
+
},
|
|
24
|
+
{
|
|
25
|
+
label: 'C', width: 100, height: 100, x: 3, y: 2,
|
|
26
|
+
},
|
|
27
|
+
];
|
|
28
|
+
forceSimulation()
|
|
29
|
+
.nodes(nodes)
|
|
30
|
+
.force('collision', collisionForce)
|
|
31
|
+
.tick(2);
|
|
32
|
+
const collisionAB = (Math.abs(nodes[0].x - nodes[1].x) < 100
|
|
33
|
+
&& Math.abs(nodes[0].y - nodes[1].y) < 100);
|
|
34
|
+
expect(collisionAB).toEqual(true);
|
|
35
|
+
});
|
|
36
|
+
it('can prevent a collision of rects after many iterations', () => {
|
|
37
|
+
const collisionForce = forceCollideRects()
|
|
38
|
+
.size(d => ([d.width, d.height]));
|
|
39
|
+
const nodes = [
|
|
40
|
+
{
|
|
41
|
+
label: 'A', width: 100, height: 100, x: 2, y: 2,
|
|
42
|
+
},
|
|
43
|
+
{
|
|
44
|
+
label: 'B', width: 100, height: 100, x: 3, y: 3,
|
|
45
|
+
},
|
|
46
|
+
{
|
|
47
|
+
label: 'C', width: 100, height: 100, x: 3, y: 2,
|
|
48
|
+
},
|
|
49
|
+
];
|
|
50
|
+
forceSimulation()
|
|
51
|
+
.nodes(nodes)
|
|
52
|
+
.force('collision', collisionForce)
|
|
53
|
+
.tick(50);
|
|
54
|
+
const collisionAB = (Math.abs(nodes[0].x - nodes[1].x) < 100
|
|
55
|
+
&& Math.abs(nodes[0].y - nodes[1].y) < 100);
|
|
56
|
+
expect(collisionAB).toEqual(false);
|
|
57
|
+
});
|
|
58
|
+
});
|
|
59
|
+
});
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export { default as AbstractSpatialOrScatterplot } from "./AbstractSpatialOrScatterplot.js";
|
|
2
|
+
export { createQuadTree } from "./quadtree.js";
|
|
3
|
+
export { forceCollideRects } from "./force-collide-rects.js";
|
|
4
|
+
export { getOnHoverCallback } from "./cursor.js";
|
|
5
|
+
export { getPointSizeDevicePixels, getPointOpacity } from "./dynamic-opacity.js";
|
|
6
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/shared-spatial-scatterplot/index.js"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
export { default as AbstractSpatialOrScatterplot } from './AbstractSpatialOrScatterplot.js';
|
|
2
|
+
export { createQuadTree } from './quadtree.js';
|
|
3
|
+
export { forceCollideRects } from './force-collide-rects.js';
|
|
4
|
+
export { getOnHoverCallback } from './cursor.js';
|
|
5
|
+
export { getPointSizeDevicePixels, getPointOpacity, } from './dynamic-opacity.js';
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Create a d3-quadtree object for cells data points.
|
|
3
|
+
* @param {array} cellsEntries Array of [cellId, cell] tuples,
|
|
4
|
+
* resulting from running Object.entries on the cells object.
|
|
5
|
+
* @param {function} getCellCoords Given a cell object, return the
|
|
6
|
+
* spatial/scatterplot coordinates [x, y].
|
|
7
|
+
* @returns {object} Quadtree instance.
|
|
8
|
+
*/
|
|
9
|
+
export function createQuadTree(obsEmbedding: any, getCellCoords: Function): object;
|
|
10
|
+
//# sourceMappingURL=quadtree.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"quadtree.d.ts","sourceRoot":"","sources":["../../src/shared-spatial-scatterplot/quadtree.js"],"names":[],"mappings":"AAGA;;;;;;;GAOG;AACH,4EAFa,MAAM,CAiBlB"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { quadtree } from 'd3-quadtree';
|
|
2
|
+
import { range } from 'lodash-es';
|
|
3
|
+
/**
|
|
4
|
+
* Create a d3-quadtree object for cells data points.
|
|
5
|
+
* @param {array} cellsEntries Array of [cellId, cell] tuples,
|
|
6
|
+
* resulting from running Object.entries on the cells object.
|
|
7
|
+
* @param {function} getCellCoords Given a cell object, return the
|
|
8
|
+
* spatial/scatterplot coordinates [x, y].
|
|
9
|
+
* @returns {object} Quadtree instance.
|
|
10
|
+
*/
|
|
11
|
+
export function createQuadTree(obsEmbedding, getCellCoords) {
|
|
12
|
+
// Use the cellsEntries variable since it is already
|
|
13
|
+
// an array, converted by Object.entries().
|
|
14
|
+
// Only use cellsEntries in quadtree calculation if there is
|
|
15
|
+
// centroid data in the cells (i.e not just ids).
|
|
16
|
+
// eslint-disable-next-line no-unused-vars
|
|
17
|
+
if (!obsEmbedding) {
|
|
18
|
+
// Abort if the cells data is not yet available.
|
|
19
|
+
return null;
|
|
20
|
+
}
|
|
21
|
+
const tree = quadtree()
|
|
22
|
+
.x(i => getCellCoords(i)[0])
|
|
23
|
+
.y(i => getCellCoords(i)[1])
|
|
24
|
+
.addAll(range(obsEmbedding.shape[1]));
|
|
25
|
+
return tree;
|
|
26
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vitessce/scatterplot",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "3.0.1",
|
|
4
4
|
"author": "Gehlenborg Lab",
|
|
5
5
|
"homepage": "http://vitessce.io",
|
|
6
6
|
"repository": {
|
|
@@ -8,34 +8,46 @@
|
|
|
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",
|
|
20
|
+
"@material-ui/icons": "~4.11.2",
|
|
18
21
|
"clsx": "^1.1.1",
|
|
19
22
|
"d3-force": "^2.1.1",
|
|
20
23
|
"d3-quadtree": "^1.0.7",
|
|
21
|
-
"lodash": "^4.17.21",
|
|
22
|
-
"@vitessce/constants-internal": "
|
|
23
|
-
"@vitessce/
|
|
24
|
-
"@vitessce/
|
|
25
|
-
"@vitessce/
|
|
26
|
-
"@vitessce/
|
|
27
|
-
"@vitessce/
|
|
24
|
+
"lodash-es": "^4.17.21",
|
|
25
|
+
"@vitessce/constants-internal": "3.0.1",
|
|
26
|
+
"@vitessce/icons": "3.0.1",
|
|
27
|
+
"@vitessce/tooltip": "3.0.1",
|
|
28
|
+
"@vitessce/utils": "3.0.1",
|
|
29
|
+
"@vitessce/vit-s": "3.0.1",
|
|
30
|
+
"@vitessce/gl": "3.0.1"
|
|
28
31
|
},
|
|
29
32
|
"devDependencies": {
|
|
33
|
+
"@testing-library/jest-dom": "^5.16.4",
|
|
34
|
+
"@testing-library/react": "^13.3.0",
|
|
30
35
|
"react": "^18.0.0",
|
|
31
|
-
"vite": "^3.0
|
|
32
|
-
"vitest": "^0.
|
|
36
|
+
"vite": "^4.3.0",
|
|
37
|
+
"vitest": "^0.32.2"
|
|
33
38
|
},
|
|
34
39
|
"peerDependencies": {
|
|
35
40
|
"react": "^16.8.0 || ^17.0.0 || ^18.0.0"
|
|
36
41
|
},
|
|
37
42
|
"scripts": {
|
|
38
43
|
"bundle": "pnpm exec vite build -c ../../../scripts/vite.config.js",
|
|
39
|
-
"test": "pnpm exec vitest --run
|
|
44
|
+
"test": "pnpm exec vitest --run"
|
|
45
|
+
},
|
|
46
|
+
"module": "dist/index.js",
|
|
47
|
+
"exports": {
|
|
48
|
+
".": {
|
|
49
|
+
"types": "./dist-tsc/index.d.ts",
|
|
50
|
+
"import": "./dist/index.js"
|
|
51
|
+
}
|
|
40
52
|
}
|
|
41
53
|
}
|
package/src/Scatterplot.js
CHANGED
|
@@ -7,7 +7,7 @@ import {
|
|
|
7
7
|
import { getDefaultColor } from '@vitessce/utils';
|
|
8
8
|
import {
|
|
9
9
|
AbstractSpatialOrScatterplot, createQuadTree, forceCollideRects, getOnHoverCallback,
|
|
10
|
-
} from './shared-spatial-scatterplot';
|
|
10
|
+
} from './shared-spatial-scatterplot/index.js';
|
|
11
11
|
|
|
12
12
|
const CELLS_LAYER_ID = 'scatterplot';
|
|
13
13
|
const LABEL_FONT_FAMILY = "-apple-system, 'Helvetica Neue', Arial, sans-serif";
|
|
@@ -64,8 +64,10 @@ const getPosition = (object, { index, data, target }) => {
|
|
|
64
64
|
* @param {function} props.setCellHighlight
|
|
65
65
|
* @param {function} props.updateViewInfo
|
|
66
66
|
* @param {function} props.onToolChange Callback for tool changes
|
|
67
|
-
* (lasso/pan
|
|
67
|
+
* (lasso/pan selection tools).
|
|
68
68
|
* @param {function} props.onCellClick Getter function for cell layer onClick.
|
|
69
|
+
* @param {object} props.originalViewState A viewState object to pass to
|
|
70
|
+
* setViewState upon clicking the recenter button.
|
|
69
71
|
*/
|
|
70
72
|
class Scatterplot extends AbstractSpatialOrScatterplot {
|
|
71
73
|
constructor(props) {
|
|
@@ -343,6 +345,7 @@ class Scatterplot extends AbstractSpatialOrScatterplot {
|
|
|
343
345
|
'obsEmbeddingIndex', 'obsEmbedding', 'cellFilter', 'cellSelection', 'cellColors',
|
|
344
346
|
'cellRadius', 'cellOpacity', 'cellRadiusMode', 'geneExpressionColormap',
|
|
345
347
|
'geneExpressionColormapRange', 'geneSelection', 'cellColorEncoding',
|
|
348
|
+
'getExpressionValue',
|
|
346
349
|
].some(shallowDiff)) {
|
|
347
350
|
// Cells layer props changed.
|
|
348
351
|
this.onUpdateCellsLayer();
|
|
@@ -366,6 +369,13 @@ class Scatterplot extends AbstractSpatialOrScatterplot {
|
|
|
366
369
|
}
|
|
367
370
|
}
|
|
368
371
|
|
|
372
|
+
recenter() {
|
|
373
|
+
const { originalViewState, setViewState } = this.props;
|
|
374
|
+
if (Array.isArray(originalViewState?.target) && typeof originalViewState?.zoom === 'number') {
|
|
375
|
+
setViewState(originalViewState);
|
|
376
|
+
}
|
|
377
|
+
}
|
|
378
|
+
|
|
369
379
|
// render() is implemented in the abstract parent class.
|
|
370
380
|
}
|
|
371
381
|
|
|
@@ -382,4 +392,5 @@ const ScatterplotWrapper = forwardRef((props, deckRef) => (
|
|
|
382
392
|
deckRef={deckRef}
|
|
383
393
|
/>
|
|
384
394
|
));
|
|
395
|
+
ScatterplotWrapper.displayName = 'ScatterplotWrapper';
|
|
385
396
|
export default ScatterplotWrapper;
|
|
@@ -1,9 +1,6 @@
|
|
|
1
1
|
import React, { useCallback } from 'react';
|
|
2
|
-
import debounce from 'lodash
|
|
3
|
-
import Checkbox from '@material-ui/core
|
|
4
|
-
import Slider from '@material-ui/core/Slider';
|
|
5
|
-
import TableCell from '@material-ui/core/TableCell';
|
|
6
|
-
import TableRow from '@material-ui/core/TableRow';
|
|
2
|
+
import { debounce } from 'lodash-es';
|
|
3
|
+
import { Checkbox, Slider, TableCell, TableRow } from '@material-ui/core';
|
|
7
4
|
import { capitalize } from '@vitessce/utils';
|
|
8
5
|
import {
|
|
9
6
|
usePlotOptionsStyles, CellColorEncodingOption, OptionsContainer, OptionSelect,
|
|
@@ -24,6 +21,8 @@ export default function ScatterplotOptions(props) {
|
|
|
24
21
|
setCellOpacityMode,
|
|
25
22
|
cellSetLabelsVisible,
|
|
26
23
|
setCellSetLabelsVisible,
|
|
24
|
+
tooltipsVisible,
|
|
25
|
+
setTooltipsVisible,
|
|
27
26
|
cellSetLabelSize,
|
|
28
27
|
setCellSetLabelSize,
|
|
29
28
|
cellSetPolygonsVisible,
|
|
@@ -60,6 +59,10 @@ export default function ScatterplotOptions(props) {
|
|
|
60
59
|
setCellSetLabelsVisible(event.target.checked);
|
|
61
60
|
}
|
|
62
61
|
|
|
62
|
+
function handleTooltipsVisibilityChange(event) {
|
|
63
|
+
setTooltipsVisible(event.target.checked);
|
|
64
|
+
}
|
|
65
|
+
|
|
63
66
|
function handleLabelSizeChange(event, value) {
|
|
64
67
|
setCellSetLabelSize(value);
|
|
65
68
|
}
|
|
@@ -102,6 +105,25 @@ export default function ScatterplotOptions(props) {
|
|
|
102
105
|
/>
|
|
103
106
|
</TableCell>
|
|
104
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>
|
|
105
127
|
<TableRow>
|
|
106
128
|
<TableCell className={classes.labelCell}>
|
|
107
129
|
{observationsLabelNice} Set Label Size
|
package/src/index.js
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
export { default as Scatterplot } from './Scatterplot';
|
|
2
|
-
export { default as ScatterplotOptions } from './ScatterplotOptions';
|
|
3
|
-
export { default as ScatterplotTooltipSubscriber } from './ScatterplotTooltipSubscriber';
|
|
4
|
-
export { default as EmptyMessage } from './EmptyMessage';
|
|
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
5
|
export {
|
|
6
6
|
getPointSizeDevicePixels,
|
|
7
7
|
getPointOpacity,
|
|
8
8
|
getOnHoverCallback,
|
|
9
9
|
createQuadTree,
|
|
10
10
|
AbstractSpatialOrScatterplot,
|
|
11
|
-
} from './shared-spatial-scatterplot/index';
|
|
11
|
+
} from './shared-spatial-scatterplot/index.js';
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import React, { PureComponent } from 'react';
|
|
2
2
|
import { deck, DEFAULT_GL_OPTIONS } from '@vitessce/gl';
|
|
3
|
-
import ToolMenu from './ToolMenu';
|
|
4
|
-
import { getCursor, getCursorWithTool } from './cursor';
|
|
3
|
+
import ToolMenu from './ToolMenu.js';
|
|
4
|
+
import { getCursor, getCursorWithTool } from './cursor.js';
|
|
5
5
|
|
|
6
6
|
/**
|
|
7
7
|
* Abstract class component intended to be inherited by
|
|
@@ -19,12 +19,12 @@ export default class AbstractSpatialOrScatterplot extends PureComponent {
|
|
|
19
19
|
};
|
|
20
20
|
|
|
21
21
|
this.viewport = null;
|
|
22
|
-
|
|
23
22
|
this.onViewStateChange = this.onViewStateChange.bind(this);
|
|
24
23
|
this.onInitializeViewInfo = this.onInitializeViewInfo.bind(this);
|
|
25
24
|
this.onWebGLInitialized = this.onWebGLInitialized.bind(this);
|
|
26
25
|
this.onToolChange = this.onToolChange.bind(this);
|
|
27
26
|
this.onHover = this.onHover.bind(this);
|
|
27
|
+
this.recenter = this.recenter.bind(this);
|
|
28
28
|
}
|
|
29
29
|
|
|
30
30
|
/**
|
|
@@ -198,6 +198,12 @@ export default class AbstractSpatialOrScatterplot extends PureComponent {
|
|
|
198
198
|
|
|
199
199
|
}
|
|
200
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
|
+
|
|
201
207
|
/**
|
|
202
208
|
* Intended to be overridden by descendants.
|
|
203
209
|
* @returns {boolean} Whether or not any layers are 3D.
|
|
@@ -238,9 +244,9 @@ export default class AbstractSpatialOrScatterplot extends PureComponent {
|
|
|
238
244
|
setActiveTool={this.onToolChange}
|
|
239
245
|
visibleTools={{
|
|
240
246
|
pan: showPanTool && !hideTools,
|
|
241
|
-
selectRectangle: showCellSelectionTools && !hideTools,
|
|
242
247
|
selectLasso: showCellSelectionTools && !hideTools,
|
|
243
248
|
}}
|
|
249
|
+
recenterOnClick={this.recenter}
|
|
244
250
|
/>
|
|
245
251
|
<deck.DeckGL
|
|
246
252
|
id={`deckgl-overlay-${uuid}`}
|
|
@@ -1,10 +1,18 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import clsx from 'clsx';
|
|
3
3
|
import { SELECTION_TYPE } from '@vitessce/gl';
|
|
4
|
-
import { PointerIconSVG,
|
|
4
|
+
import { PointerIconSVG, SelectLassoIconSVG } from '@vitessce/icons';
|
|
5
5
|
import { makeStyles } from '@material-ui/core';
|
|
6
|
+
import { CenterFocusStrong } from '@material-ui/icons';
|
|
6
7
|
|
|
7
8
|
const useStyles = makeStyles(() => ({
|
|
9
|
+
toolButton: {
|
|
10
|
+
display: 'inline-flex',
|
|
11
|
+
'&:active': {
|
|
12
|
+
opacity: '.65',
|
|
13
|
+
extend: 'iconClicked',
|
|
14
|
+
},
|
|
15
|
+
},
|
|
8
16
|
tool: {
|
|
9
17
|
position: 'absolute',
|
|
10
18
|
display: 'inline',
|
|
@@ -15,7 +23,12 @@ const useStyles = makeStyles(() => ({
|
|
|
15
23
|
opacity: '.90',
|
|
16
24
|
},
|
|
17
25
|
},
|
|
18
|
-
|
|
26
|
+
iconClicked: {
|
|
27
|
+
// Styles for the clicked state
|
|
28
|
+
boxShadow: 'none',
|
|
29
|
+
transform: 'scale(0.98)', // make the button slightly smaller
|
|
30
|
+
},
|
|
31
|
+
toolIcon: {
|
|
19
32
|
// btn btn-outline-secondary mr-2 icon
|
|
20
33
|
padding: '0',
|
|
21
34
|
height: '2em',
|
|
@@ -39,9 +52,14 @@ const useStyles = makeStyles(() => ({
|
|
|
39
52
|
|
|
40
53
|
'& > svg': {
|
|
41
54
|
verticalAlign: 'middle',
|
|
55
|
+
color: 'black',
|
|
56
|
+
},
|
|
57
|
+
'&:active': {
|
|
58
|
+
extend: 'iconClicked',
|
|
42
59
|
},
|
|
60
|
+
|
|
43
61
|
},
|
|
44
|
-
|
|
62
|
+
toolActive: {
|
|
45
63
|
// active
|
|
46
64
|
color: '#fff',
|
|
47
65
|
backgroundColor: '#6c757d',
|
|
@@ -50,14 +68,31 @@ const useStyles = makeStyles(() => ({
|
|
|
50
68
|
},
|
|
51
69
|
}));
|
|
52
70
|
|
|
53
|
-
export function
|
|
71
|
+
export function IconTool(props) {
|
|
54
72
|
const {
|
|
55
73
|
alt, onClick, isActive, children,
|
|
56
74
|
} = props;
|
|
57
75
|
const classes = useStyles();
|
|
58
76
|
return (
|
|
59
77
|
<button
|
|
60
|
-
className={clsx(classes.
|
|
78
|
+
className={clsx(classes.toolIcon, { [classes.toolActive]: isActive })}
|
|
79
|
+
onClick={onClick}
|
|
80
|
+
type="button"
|
|
81
|
+
title={alt}
|
|
82
|
+
>
|
|
83
|
+
{children}
|
|
84
|
+
</button>
|
|
85
|
+
);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
export function IconButton(props) {
|
|
89
|
+
const {
|
|
90
|
+
alt, onClick, children,
|
|
91
|
+
} = props;
|
|
92
|
+
const classes = useStyles();
|
|
93
|
+
return (
|
|
94
|
+
<button
|
|
95
|
+
className={clsx(classes.toolIcon, classes.toolButton)}
|
|
61
96
|
onClick={onClick}
|
|
62
97
|
type="button"
|
|
63
98
|
title={alt}
|
|
@@ -71,35 +106,38 @@ export default function ToolMenu(props) {
|
|
|
71
106
|
const {
|
|
72
107
|
setActiveTool,
|
|
73
108
|
activeTool,
|
|
74
|
-
visibleTools = { pan: true,
|
|
109
|
+
visibleTools = { pan: true, selectLasso: true },
|
|
110
|
+
recenterOnClick = () => {},
|
|
75
111
|
} = props;
|
|
76
112
|
const classes = useStyles();
|
|
113
|
+
|
|
114
|
+
const onRecenterButtonCLick = () => {
|
|
115
|
+
recenterOnClick();
|
|
116
|
+
};
|
|
117
|
+
|
|
77
118
|
return (
|
|
78
119
|
<div className={classes.tool}>
|
|
79
120
|
{visibleTools.pan && (
|
|
80
|
-
<
|
|
121
|
+
<IconTool
|
|
81
122
|
alt="pointer tool"
|
|
82
123
|
onClick={() => setActiveTool(null)}
|
|
83
124
|
isActive={activeTool === null}
|
|
84
125
|
><PointerIconSVG />
|
|
85
|
-
</
|
|
126
|
+
</IconTool>
|
|
86
127
|
)}
|
|
87
|
-
{visibleTools.selectRectangle ? (
|
|
88
|
-
<IconButton
|
|
89
|
-
alt="select rectangle"
|
|
90
|
-
onClick={() => setActiveTool(SELECTION_TYPE.RECTANGLE)}
|
|
91
|
-
isActive={activeTool === SELECTION_TYPE.RECTANGLE}
|
|
92
|
-
><SelectRectangleIconSVG />
|
|
93
|
-
</IconButton>
|
|
94
|
-
) : null}
|
|
95
128
|
{visibleTools.selectLasso ? (
|
|
96
|
-
<
|
|
129
|
+
<IconTool
|
|
97
130
|
alt="select lasso"
|
|
98
131
|
onClick={() => setActiveTool(SELECTION_TYPE.POLYGON)}
|
|
99
132
|
isActive={activeTool === SELECTION_TYPE.POLYGON}
|
|
100
133
|
><SelectLassoIconSVG />
|
|
101
|
-
</
|
|
134
|
+
</IconTool>
|
|
102
135
|
) : null}
|
|
136
|
+
<IconButton
|
|
137
|
+
alt="click to recenter"
|
|
138
|
+
onClick={() => onRecenterButtonCLick()}
|
|
139
|
+
><CenterFocusStrong />
|
|
140
|
+
</IconButton>
|
|
103
141
|
</div>
|
|
104
142
|
);
|
|
105
143
|
}
|
|
@@ -1,18 +1,26 @@
|
|
|
1
|
+
import { describe, it, expect, afterEach } from 'vitest';
|
|
1
2
|
import '@testing-library/jest-dom';
|
|
2
|
-
import { cleanup, render
|
|
3
|
-
import
|
|
3
|
+
import { cleanup, render } from '@testing-library/react';
|
|
4
|
+
import React from 'react';
|
|
4
5
|
|
|
5
|
-
import { IconButton } from './ToolMenu';
|
|
6
|
+
import { IconTool, IconButton } from './ToolMenu.js';
|
|
6
7
|
|
|
7
8
|
afterEach(() => {
|
|
8
|
-
cleanup()
|
|
9
|
+
cleanup();
|
|
9
10
|
});
|
|
10
11
|
|
|
11
12
|
describe('ToolMenu.js', () => {
|
|
12
|
-
describe('<
|
|
13
|
+
describe('<IconTool />', () => {
|
|
13
14
|
it('renders with title attribute', () => {
|
|
14
|
-
const { container } = render(<
|
|
15
|
+
const { container } = render(<IconTool isActive alt="Lasso" />);
|
|
15
16
|
expect(container.querySelectorAll('[title="Lasso"]').length).toEqual(1);
|
|
16
17
|
});
|
|
17
18
|
});
|
|
19
|
+
|
|
20
|
+
describe('<IconButton />', () => {
|
|
21
|
+
it('renders with title attribute', () => {
|
|
22
|
+
const { container } = render(<IconButton alt="click to recenter" />);
|
|
23
|
+
expect(container.querySelectorAll('[title="click to recenter"]').length).toEqual(1);
|
|
24
|
+
});
|
|
25
|
+
});
|
|
18
26
|
});
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { deck } from '@vitessce/gl';
|
|
2
|
-
import clamp from 'lodash
|
|
2
|
+
import { clamp } from 'lodash-es';
|
|
3
3
|
|
|
4
4
|
// Reference: https://observablehq.com/@rreusser/selecting-the-right-opacity-for-2d-point-clouds
|
|
5
5
|
// Reference: https://observablehq.com/@bmschmidt/dot-density-election-maps-with-webgl
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { describe, it, expect } from 'vitest';
|
|
2
|
+
import { getPointSizeDevicePixels, getPointOpacity } from './dynamic-opacity.js';
|
|
2
3
|
|
|
3
4
|
describe('dynamic-opacity.js', () => {
|
|
4
5
|
describe('getPointSizeDevicePixels', () => {
|
|
@@ -1,5 +1,6 @@
|
|
|
1
|
+
import { describe, it, expect } from 'vitest';
|
|
1
2
|
import { forceSimulation } from 'd3-force';
|
|
2
|
-
import { forceCollideRects } from './force-collide-rects';
|
|
3
|
+
import { forceCollideRects } from './force-collide-rects.js';
|
|
3
4
|
|
|
4
5
|
describe('force-collide-rects.js', () => {
|
|
5
6
|
describe('forceCollideRects', () => {
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
export { default as AbstractSpatialOrScatterplot } from './AbstractSpatialOrScatterplot';
|
|
2
|
-
export { createQuadTree } from './quadtree';
|
|
3
|
-
export { forceCollideRects } from './force-collide-rects';
|
|
4
|
-
export { getOnHoverCallback } from './cursor';
|
|
1
|
+
export { default as AbstractSpatialOrScatterplot } from './AbstractSpatialOrScatterplot.js';
|
|
2
|
+
export { createQuadTree } from './quadtree.js';
|
|
3
|
+
export { forceCollideRects } from './force-collide-rects.js';
|
|
4
|
+
export { getOnHoverCallback } from './cursor.js';
|
|
5
5
|
export {
|
|
6
6
|
getPointSizeDevicePixels,
|
|
7
7
|
getPointOpacity,
|
|
8
|
-
} from './dynamic-opacity';
|
|
8
|
+
} from './dynamic-opacity.js';
|