@vitessce/scatterplot-gating 3.8.0 → 3.8.2
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-B6pWJWB8.js → deflate-CKHV0khu.js} +1 -1
- package/dist/{index-B4FTJe_L.js → index-BZoxwGBy.js} +97 -45
- package/dist/index.js +1 -1
- package/dist/{jpeg-G-ewKEbP.js → jpeg-DnXWckxj.js} +1 -1
- package/dist/{lerc-CoSyfsZl.js → lerc-DnC1d7LQ.js} +1 -1
- package/dist/{lzw-DiaKhPZe.js → lzw-DpnaIz4v.js} +1 -1
- package/dist/{packbits-CT7DC7Iq.js → packbits-DlOGmdZv.js} +1 -1
- package/dist/{raw-BFCCI-Mm.js → raw-C5EHR8Uv.js} +1 -1
- package/dist/{webimage-BtSqKc33.js → webimage-CXmDbYs_.js} +1 -1
- package/dist-tsc/GatingScatterplotOptions.d.ts.map +1 -1
- package/dist-tsc/GatingScatterplotOptions.js +15 -24
- package/dist-tsc/GatingSubscriber.d.ts.map +1 -1
- package/dist-tsc/GatingSubscriber.js +27 -14
- package/package.json +7 -7
- package/src/GatingScatterplotOptions.js +45 -25
- package/src/GatingSubscriber.js +38 -12
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { i as inflate_1 } from "./pako.esm-SxljTded.js";
|
|
2
|
-
import { B as BaseDecoder } from "./index-
|
|
2
|
+
import { B as BaseDecoder } from "./index-BZoxwGBy.js";
|
|
3
3
|
class DeflateDecoder extends BaseDecoder {
|
|
4
4
|
decodeBlock(buffer) {
|
|
5
5
|
return inflate_1(new Uint8Array(buffer)).buffer;
|
|
@@ -4,7 +4,7 @@ var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "sy
|
|
|
4
4
|
var _a2, _b;
|
|
5
5
|
import * as React from "react";
|
|
6
6
|
import React__default, { useLayoutEffect, useEffect, cloneElement, createElement, forwardRef, useState, useRef, useMemo, useImperativeHandle, useContext, createContext, isValidElement, Children, PureComponent, useCallback } from "react";
|
|
7
|
-
import { usePlotOptionsStyles, OptionsContainer, CellColorEncodingOption, OptionSelect, useVitessceContainer, useComponentHover, useComponentViewInfo, useLoaders, useCoordinationScopes, useSetComponentHover, useSetComponentViewInfo, useCoordination, useDeckCanvasSize, useObsSetsData, useFeatureSelection, useObsFeatureMatrixIndices, useReady, useUrls, useUint8FeatureSelection, useExpressionValueGetter, TitleInfo } from "@vitessce/vit-s";
|
|
7
|
+
import { usePlotOptionsStyles, OptionsContainer, CellColorEncodingOption, OptionSelect, useVitessceContainer, useComponentHover, useComponentViewInfo, useLoaders, useCoordinationScopes, useSetComponentHover, useSetComponentViewInfo, useCoordination, useInitialCoordination, useDeckCanvasSize, useObsSetsData, useFeatureSelection, useObsFeatureMatrixIndices, useReady, useUrls, useUint8FeatureSelection, useExpressionValueGetter, TitleInfo } from "@vitessce/vit-s";
|
|
8
8
|
import * as ReactDOM from "react-dom";
|
|
9
9
|
import ReactDOM__default from "react-dom";
|
|
10
10
|
function _mergeNamespaces(n2, m2) {
|
|
@@ -9492,7 +9492,7 @@ const ViewType$1 = {
|
|
|
9492
9492
|
SCATTERPLOT: "scatterplot",
|
|
9493
9493
|
SPATIAL: "spatial",
|
|
9494
9494
|
SPATIAL_BETA: "spatialBeta",
|
|
9495
|
-
SPATIAL_ACCELERATED:
|
|
9495
|
+
// SPATIAL_ACCELERATED: 'spatialAccelerated',
|
|
9496
9496
|
HEATMAP: "heatmap",
|
|
9497
9497
|
LAYER_CONTROLLER: "layerController",
|
|
9498
9498
|
LAYER_CONTROLLER_BETA: "layerControllerBeta",
|
|
@@ -9650,6 +9650,9 @@ const FileType$1 = {
|
|
|
9650
9650
|
OBS_SEGMENTATIONS_OME_TIFF: "obsSegmentations.ome-tiff",
|
|
9651
9651
|
// GLB
|
|
9652
9652
|
OBS_SEGMENTATIONS_GLB: "obsSegmentations.glb",
|
|
9653
|
+
// Neuroglancer
|
|
9654
|
+
// Precomputed (mesh) format
|
|
9655
|
+
OBS_SEGMENTATIONS_NG_PRECOMPUTED: "obsSegmentations.ng-precomputed",
|
|
9653
9656
|
// New file types to support old file types:
|
|
9654
9657
|
// - cells.json
|
|
9655
9658
|
OBS_EMBEDDING_CELLS_JSON: "obsEmbedding.cells.json",
|
|
@@ -9877,6 +9880,7 @@ const ViewHelpMapping = {
|
|
|
9877
9880
|
[FileType$1.OBS_LABELS_MUDATA_ZARR]: DataType$2.OBS_LABELS,
|
|
9878
9881
|
[FileType$1.FEATURE_LABELS_MUDATA_ZARR]: DataType$2.FEATURE_LABELS,
|
|
9879
9882
|
[FileType$1.OBS_SEGMENTATIONS_GLB]: DataType$2.OBS_SEGMENTATIONS,
|
|
9883
|
+
[FileType$1.OBS_SEGMENTATIONS_NG_PRECOMPUTED]: DataType$2.OBS_SEGMENTATIONS,
|
|
9880
9884
|
[FileType$1.IMAGE_SPATIALDATA_ZARR]: DataType$2.IMAGE,
|
|
9881
9885
|
[FileType$1.LABELS_SPATIALDATA_ZARR]: DataType$2.OBS_SEGMENTATIONS,
|
|
9882
9886
|
[FileType$1.SHAPES_SPATIALDATA_ZARR]: DataType$2.OBS_SEGMENTATIONS,
|
|
@@ -11140,6 +11144,18 @@ z.object({
|
|
|
11140
11144
|
sceneScaleZ: z.number(),
|
|
11141
11145
|
materialSide: z.enum(["front", "back"])
|
|
11142
11146
|
}).partial().nullable();
|
|
11147
|
+
z.object({
|
|
11148
|
+
// TODO: Should this explicitly specify sharded vs. unsharded?
|
|
11149
|
+
// Or can/should that be inferred from the data?
|
|
11150
|
+
dimensionX: z.number(),
|
|
11151
|
+
dimensionY: z.number(),
|
|
11152
|
+
dimensionZ: z.number(),
|
|
11153
|
+
dimensionUnit: z.enum(["nm", "um", "µm", "mm", "cm", "m"]),
|
|
11154
|
+
// TODO: should the following be passed via coordination types instead?
|
|
11155
|
+
projectionScale: z.number(),
|
|
11156
|
+
position: z.array(z.number()).length(3),
|
|
11157
|
+
projectionOrientation: z.array(z.number()).length(4)
|
|
11158
|
+
}).partial().nullable();
|
|
11143
11159
|
z.object({
|
|
11144
11160
|
obsIndex: z.string(),
|
|
11145
11161
|
obsEmbedding: z.array(z.string()).length(2)
|
|
@@ -123129,22 +123145,22 @@ function addDecoder(cases, importFn) {
|
|
|
123129
123145
|
}
|
|
123130
123146
|
cases.forEach((c2) => registry$1.set(c2, importFn));
|
|
123131
123147
|
}
|
|
123132
|
-
addDecoder([void 0, 1], () => import("./raw-
|
|
123133
|
-
addDecoder(5, () => import("./lzw-
|
|
123148
|
+
addDecoder([void 0, 1], () => import("./raw-C5EHR8Uv.js").then((m2) => m2.default));
|
|
123149
|
+
addDecoder(5, () => import("./lzw-DpnaIz4v.js").then((m2) => m2.default));
|
|
123134
123150
|
addDecoder(6, () => {
|
|
123135
123151
|
throw new Error("old style JPEG compression is not supported.");
|
|
123136
123152
|
});
|
|
123137
|
-
addDecoder(7, () => import("./jpeg-
|
|
123138
|
-
addDecoder([8, 32946], () => import("./deflate-
|
|
123139
|
-
addDecoder(32773, () => import("./packbits-
|
|
123153
|
+
addDecoder(7, () => import("./jpeg-DnXWckxj.js").then((m2) => m2.default));
|
|
123154
|
+
addDecoder([8, 32946], () => import("./deflate-CKHV0khu.js").then((m2) => m2.default));
|
|
123155
|
+
addDecoder(32773, () => import("./packbits-DlOGmdZv.js").then((m2) => m2.default));
|
|
123140
123156
|
addDecoder(
|
|
123141
123157
|
34887,
|
|
123142
|
-
() => import("./lerc-
|
|
123158
|
+
() => import("./lerc-DnC1d7LQ.js").then(async (m2) => {
|
|
123143
123159
|
await m2.zstd.init();
|
|
123144
123160
|
return m2;
|
|
123145
123161
|
}).then((m2) => m2.default)
|
|
123146
123162
|
);
|
|
123147
|
-
addDecoder(50001, () => import("./webimage-
|
|
123163
|
+
addDecoder(50001, () => import("./webimage-CXmDbYs_.js").then((m2) => m2.default));
|
|
123148
123164
|
function decodeRowAcc(row, stride) {
|
|
123149
123165
|
let length2 = row.length - stride;
|
|
123150
123166
|
let offset2 = 0;
|
|
@@ -158139,13 +158155,13 @@ function IconButton(props) {
|
|
|
158139
158155
|
function ToolMenu(props) {
|
|
158140
158156
|
const pointerIconAltText = "Pointer tool";
|
|
158141
158157
|
const lassoIconAltText = "Select lasso";
|
|
158142
|
-
const { setActiveTool, activeTool, visibleTools = { pan: true, selectLasso: true }, recenterOnClick = () => {
|
|
158158
|
+
const { setActiveTool, activeTool, visibleTools = { pan: true, selectLasso: true, recenter: true }, recenterOnClick = () => {
|
|
158143
158159
|
} } = props;
|
|
158144
158160
|
const { classes } = useStyles$2();
|
|
158145
158161
|
const onRecenterButtonCLick = () => {
|
|
158146
158162
|
recenterOnClick();
|
|
158147
158163
|
};
|
|
158148
|
-
return jsxRuntimeExports.jsxs("div", { className: classes.tool, children: [visibleTools.pan && jsxRuntimeExports.jsx(IconTool, { alt: pointerIconAltText, onClick: () => setActiveTool(null), isActive: activeTool === null, children: jsxRuntimeExports.jsx(SvgNearMe, {}) }), visibleTools.selectLasso ? jsxRuntimeExports.jsx(IconTool, { alt: lassoIconAltText, onClick: () => setActiveTool(distEs6Exports$2.SELECTION_TYPE.POLYGON), isActive: activeTool === distEs6Exports$2.SELECTION_TYPE.POLYGON, children: jsxRuntimeExports.jsx(SvgSelectionLasso, {}) }) : null, jsxRuntimeExports.jsx(IconButton, { alt: "click to recenter", onClick: () => onRecenterButtonCLick(), "aria-label": "Recenter scatterplot view", children: jsxRuntimeExports.jsx(CenterFocusStrong, {}) })] });
|
|
158164
|
+
return jsxRuntimeExports.jsxs("div", { className: classes.tool, children: [visibleTools.pan && jsxRuntimeExports.jsx(IconTool, { alt: pointerIconAltText, onClick: () => setActiveTool(null), isActive: activeTool === null, children: jsxRuntimeExports.jsx(SvgNearMe, {}) }), visibleTools.selectLasso ? jsxRuntimeExports.jsx(IconTool, { alt: lassoIconAltText, onClick: () => setActiveTool(distEs6Exports$2.SELECTION_TYPE.POLYGON), isActive: activeTool === distEs6Exports$2.SELECTION_TYPE.POLYGON, children: jsxRuntimeExports.jsx(SvgSelectionLasso, {}) }) : null, visibleTools.recenter ? jsxRuntimeExports.jsx(IconButton, { alt: "click to recenter", onClick: () => onRecenterButtonCLick(), "aria-label": "Recenter scatterplot view", children: jsxRuntimeExports.jsx(CenterFocusStrong, {}) }) : null] });
|
|
158149
158165
|
}
|
|
158150
158166
|
const getCursorWithTool = () => "crosshair";
|
|
158151
158167
|
const getCursor = (interactionState) => interactionState.isDragging ? "grabbing" : "default";
|
|
@@ -158380,7 +158396,7 @@ class AbstractSpatialOrScatterplot extends PureComponent {
|
|
|
158380
158396
|
*/
|
|
158381
158397
|
render() {
|
|
158382
158398
|
var _a3, _b2, _c, _d;
|
|
158383
|
-
const { deckRef, viewState, uuid, hideTools, orbitAxis } = this.props;
|
|
158399
|
+
const { deckRef, viewState, uuid, hideTools, hideRecenter, orbitAxis } = this.props;
|
|
158384
158400
|
const { gl, tool } = this.state;
|
|
158385
158401
|
const layers = this.getLayers();
|
|
158386
158402
|
const use3d = this.use3d();
|
|
@@ -158389,7 +158405,8 @@ class AbstractSpatialOrScatterplot extends PureComponent {
|
|
|
158389
158405
|
const useDevicePixels = !use3d && (((_b2 = (_a3 = this.obsSegmentationsData) == null ? void 0 : _a3.shape) == null ? void 0 : _b2[0]) < 1e5 || ((_d = (_c = this.obsLocationsData) == null ? void 0 : _c.shape) == null ? void 0 : _d[1]) < 1e5);
|
|
158390
158406
|
return jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [jsxRuntimeExports.jsx(ToolMenu, { activeTool: tool, setActiveTool: this.onToolChange, visibleTools: {
|
|
158391
158407
|
pan: showPanTool && !hideTools,
|
|
158392
|
-
selectLasso: showCellSelectionTools && !hideTools
|
|
158408
|
+
selectLasso: showCellSelectionTools && !hideTools,
|
|
158409
|
+
recenter: !hideRecenter
|
|
158393
158410
|
}, recenterOnClick: this.recenter }), jsxRuntimeExports.jsx(DeckGL, { id: `deckgl-overlay-${uuid}`, ref: deckRef, views: [
|
|
158394
158411
|
use3d ? new OrbitView({ id: "orbit", controller: true, orbitAxis }) : new OrthographicView({
|
|
158395
158412
|
id: "ortho"
|
|
@@ -159341,21 +159358,14 @@ function GatingScatterplotOptions(props) {
|
|
|
159341
159358
|
transformOptions
|
|
159342
159359
|
} = props;
|
|
159343
159360
|
const gatingScatterplotOptionsId = $bdb11010cef70236$export$f680877a34711e37();
|
|
159361
|
+
const gatingScatterplotGeneXId = $bdb11010cef70236$export$f680877a34711e37();
|
|
159362
|
+
const gatingScatterplotGeneYId = $bdb11010cef70236$export$f680877a34711e37();
|
|
159344
159363
|
const { classes } = usePlotOptionsStyles();
|
|
159345
|
-
const
|
|
159346
|
-
|
|
159347
|
-
|
|
159348
|
-
|
|
159349
|
-
|
|
159350
|
-
newValues.push(options[i2].value);
|
|
159351
|
-
}
|
|
159352
|
-
}
|
|
159353
|
-
if (newValues.length === 1 && gatingFeatureSelectionX && !gatingFeatureSelectionY && newValues[0] !== gatingFeatureSelectionX) {
|
|
159354
|
-
setGatingFeatureSelectionY(newValues[0]);
|
|
159355
|
-
} else if (newValues.length <= 2) {
|
|
159356
|
-
setGatingFeatureSelectionX(newValues[0]);
|
|
159357
|
-
setGatingFeatureSelectionY(newValues[1]);
|
|
159358
|
-
}
|
|
159364
|
+
const handleSelectionX = (event) => {
|
|
159365
|
+
setGatingFeatureSelectionX(event.target.value === "" ? null : event.target.value);
|
|
159366
|
+
};
|
|
159367
|
+
const handleSelectionY = (event) => {
|
|
159368
|
+
setGatingFeatureSelectionY(event.target.value === "" ? null : event.target.value);
|
|
159359
159369
|
};
|
|
159360
159370
|
const handleTransformChange = (event) => {
|
|
159361
159371
|
setGatingFeatureValueTransform(event.target.value === "" ? null : event.target.value);
|
|
@@ -159374,20 +159384,40 @@ function GatingScatterplotOptions(props) {
|
|
|
159374
159384
|
return /* @__PURE__ */ React__default.createElement(React__default.Fragment, null, /* @__PURE__ */ React__default.createElement(TableRow, null, /* @__PURE__ */ React__default.createElement(TableCell, { className: classes.labelCell, variant: "head", scope: "row" }, /* @__PURE__ */ React__default.createElement(
|
|
159375
159385
|
"label",
|
|
159376
159386
|
{
|
|
159377
|
-
htmlFor: `scatterplot-gating-gene-select-${
|
|
159387
|
+
htmlFor: `scatterplot-gating-gene-select-${gatingScatterplotGeneXId}`
|
|
159388
|
+
},
|
|
159389
|
+
capitalize$2(featureType),
|
|
159390
|
+
" along X"
|
|
159391
|
+
)), /* @__PURE__ */ React__default.createElement(TableCell, { className: classes.inputCell, variant: "body" }, /* @__PURE__ */ React__default.createElement(
|
|
159392
|
+
OptionSelect,
|
|
159393
|
+
{
|
|
159394
|
+
className: classes.select,
|
|
159395
|
+
value: gatingFeatureSelectionX ?? "",
|
|
159396
|
+
onChange: handleSelectionX,
|
|
159397
|
+
inputProps: {
|
|
159398
|
+
id: `scatterplot-gating-gene-select-${gatingScatterplotGeneXId}`
|
|
159399
|
+
}
|
|
159400
|
+
},
|
|
159401
|
+
/* @__PURE__ */ React__default.createElement("option", { value: "" }, "None"),
|
|
159402
|
+
geneSelectOptions.map((name2) => /* @__PURE__ */ React__default.createElement("option", { key: name2, value: name2 }, name2))
|
|
159403
|
+
))), /* @__PURE__ */ React__default.createElement(TableRow, null, /* @__PURE__ */ React__default.createElement(TableCell, { className: classes.labelCell, variant: "head", scope: "row" }, /* @__PURE__ */ React__default.createElement(
|
|
159404
|
+
"label",
|
|
159405
|
+
{
|
|
159406
|
+
htmlFor: `scatterplot-gating-gene-select-${gatingScatterplotGeneYId}`
|
|
159378
159407
|
},
|
|
159379
|
-
capitalize$2(
|
|
159408
|
+
capitalize$2(featureType),
|
|
159409
|
+
" along Y"
|
|
159380
159410
|
)), /* @__PURE__ */ React__default.createElement(TableCell, { className: classes.inputCell, variant: "body" }, /* @__PURE__ */ React__default.createElement(
|
|
159381
159411
|
OptionSelect,
|
|
159382
159412
|
{
|
|
159383
|
-
multiple: true,
|
|
159384
159413
|
className: classes.select,
|
|
159385
|
-
value:
|
|
159386
|
-
onChange:
|
|
159414
|
+
value: gatingFeatureSelectionY ?? "",
|
|
159415
|
+
onChange: handleSelectionY,
|
|
159387
159416
|
inputProps: {
|
|
159388
|
-
id: `scatterplot-gating-gene-select-${
|
|
159417
|
+
id: `scatterplot-gating-gene-select-${gatingScatterplotGeneYId}`
|
|
159389
159418
|
}
|
|
159390
159419
|
},
|
|
159420
|
+
/* @__PURE__ */ React__default.createElement("option", { value: "" }, "None"),
|
|
159391
159421
|
geneSelectOptions.map((name2) => /* @__PURE__ */ React__default.createElement("option", { key: name2, value: name2 }, name2))
|
|
159392
159422
|
))), /* @__PURE__ */ React__default.createElement(TableRow, null, /* @__PURE__ */ React__default.createElement(TableCell, { className: classes.labelCell, variant: "head", scope: "row" }, /* @__PURE__ */ React__default.createElement(
|
|
159393
159423
|
"label",
|
|
@@ -159419,7 +159449,7 @@ function GatingScatterplotOptions(props) {
|
|
|
159419
159449
|
type: "number",
|
|
159420
159450
|
onChange: handleTransformCoefficientChange,
|
|
159421
159451
|
value: gatingFeatureValueTransformCoefficient,
|
|
159422
|
-
slotProps: {
|
|
159452
|
+
slotProps: { inputLabel: { shrink: true } },
|
|
159423
159453
|
id: `scatterplot-gating-transform-coefficient-${gatingScatterplotOptionsId}`
|
|
159424
159454
|
}
|
|
159425
159455
|
))));
|
|
@@ -159499,6 +159529,14 @@ function GatingSubscriber(props) {
|
|
|
159499
159529
|
COMPONENT_COORDINATION_TYPES[ViewType$1.GATING],
|
|
159500
159530
|
coordinationScopes
|
|
159501
159531
|
);
|
|
159532
|
+
const {
|
|
159533
|
+
embeddingZoom: initialZoom,
|
|
159534
|
+
embeddingTargetX: initialTargetX,
|
|
159535
|
+
embeddingTargetY: initialTargetY
|
|
159536
|
+
} = useInitialCoordination(
|
|
159537
|
+
COMPONENT_COORDINATION_TYPES[ViewType$1.GATING],
|
|
159538
|
+
coordinationScopes
|
|
159539
|
+
);
|
|
159502
159540
|
const [width2, height2, deckRef] = useDeckCanvasSize();
|
|
159503
159541
|
const title = useMemo(() => {
|
|
159504
159542
|
if (titleOverride) {
|
|
@@ -159622,6 +159660,7 @@ function GatingSubscriber(props) {
|
|
|
159622
159660
|
]);
|
|
159623
159661
|
const [dynamicCellRadius, setDynamicCellRadius] = useState(cellRadiusFixed);
|
|
159624
159662
|
const [dynamicCellOpacity, setDynamicCellOpacity] = useState(cellOpacityFixed);
|
|
159663
|
+
const [originalViewState, setOriginalViewState] = useState(null);
|
|
159625
159664
|
const mergedCellSets = useMemo(() => mergeObsSets(
|
|
159626
159665
|
cellSets,
|
|
159627
159666
|
additionalCellSets
|
|
@@ -159722,13 +159761,20 @@ function GatingSubscriber(props) {
|
|
|
159722
159761
|
averageFillDensity
|
|
159723
159762
|
);
|
|
159724
159763
|
setDynamicCellOpacity(nextCellOpacityScale);
|
|
159725
|
-
if (typeof
|
|
159764
|
+
if (typeof initialTargetX !== "number" || typeof initialTargetY !== "number") {
|
|
159726
159765
|
const newTargetX = xExtent[0] + xRange / 2;
|
|
159727
159766
|
const newTargetY = yExtent[0] + yRange / 2;
|
|
159728
159767
|
const newZoom = Math.log2(Math.min(width2 / xRange, height2 / yRange));
|
|
159729
|
-
|
|
159730
|
-
|
|
159731
|
-
|
|
159768
|
+
const notYetInitialized = typeof targetX !== "number" || typeof targetY !== "number";
|
|
159769
|
+
const stillDefaultInitialized = targetX === newTargetX && targetY === -newTargetY;
|
|
159770
|
+
if (notYetInitialized || stillDefaultInitialized) {
|
|
159771
|
+
setTargetX(newTargetX);
|
|
159772
|
+
setTargetY(-newTargetY);
|
|
159773
|
+
setZoom(newZoom);
|
|
159774
|
+
}
|
|
159775
|
+
setOriginalViewState({ target: [newTargetX, -newTargetY, 0], zoom: newZoom });
|
|
159776
|
+
} else if (!originalViewState) {
|
|
159777
|
+
setOriginalViewState({ target: [initialTargetX, initialTargetY, 0], zoom: initialZoom });
|
|
159732
159778
|
}
|
|
159733
159779
|
}
|
|
159734
159780
|
}, [
|
|
@@ -159740,6 +159786,8 @@ function GatingSubscriber(props) {
|
|
|
159740
159786
|
width2,
|
|
159741
159787
|
height2,
|
|
159742
159788
|
zoom,
|
|
159789
|
+
initialTargetX,
|
|
159790
|
+
initialTargetY,
|
|
159743
159791
|
averageFillDensity
|
|
159744
159792
|
]);
|
|
159745
159793
|
const cellSelectionSet = useMemo(() => new Set(cellSelection), [cellSelection]);
|
|
@@ -159773,6 +159821,12 @@ function GatingSubscriber(props) {
|
|
|
159773
159821
|
gatingFeatureSelectionY,
|
|
159774
159822
|
obsType
|
|
159775
159823
|
]);
|
|
159824
|
+
const setViewState = ({ zoom: newZoom, target }) => {
|
|
159825
|
+
setZoom(newZoom);
|
|
159826
|
+
setTargetX(target[0]);
|
|
159827
|
+
setTargetY(target[1]);
|
|
159828
|
+
setTargetZ(target[2] || 0);
|
|
159829
|
+
};
|
|
159776
159830
|
return /* @__PURE__ */ React__default.createElement(
|
|
159777
159831
|
TitleInfo,
|
|
159778
159832
|
{
|
|
@@ -159848,13 +159902,10 @@ function GatingSubscriber(props) {
|
|
|
159848
159902
|
uuid,
|
|
159849
159903
|
theme,
|
|
159850
159904
|
hideTools: !(gatingFeatureSelectionX && gatingFeatureSelectionY),
|
|
159905
|
+
hideRecenter: !(gatingFeatureSelectionX && gatingFeatureSelectionY),
|
|
159851
159906
|
viewState: { zoom, target: [targetX, targetY, targetZ] },
|
|
159852
|
-
setViewState
|
|
159853
|
-
|
|
159854
|
-
setTargetX(target[0]);
|
|
159855
|
-
setTargetY(target[1]);
|
|
159856
|
-
setTargetZ(target[2] || 0);
|
|
159857
|
-
},
|
|
159907
|
+
setViewState,
|
|
159908
|
+
originalViewState,
|
|
159858
159909
|
obsEmbeddingIndex: obsIndex,
|
|
159859
159910
|
obsEmbedding: obsXY,
|
|
159860
159911
|
cellFilter,
|
|
@@ -159878,7 +159929,8 @@ function GatingSubscriber(props) {
|
|
|
159878
159929
|
},
|
|
159879
159930
|
updateViewInfo: setComponentViewInfo,
|
|
159880
159931
|
getExpressionValue,
|
|
159881
|
-
getCellIsSelected
|
|
159932
|
+
getCellIsSelected,
|
|
159933
|
+
embeddingPointsVisible: true
|
|
159882
159934
|
}
|
|
159883
159935
|
),
|
|
159884
159936
|
!disableTooltip && /* @__PURE__ */ React__default.createElement(
|
package/dist/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { i as inflate_1 } from "./pako.esm-SxljTded.js";
|
|
2
|
-
import { g as getDefaultExportFromCjs, B as BaseDecoder } from "./index-
|
|
2
|
+
import { g as getDefaultExportFromCjs, B as BaseDecoder } from "./index-BZoxwGBy.js";
|
|
3
3
|
const LercParameters = {
|
|
4
4
|
AddCompression: 1
|
|
5
5
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"GatingScatterplotOptions.d.ts","sourceRoot":"","sources":["../src/GatingScatterplotOptions.js"],"names":[],"mappings":"AAMA,
|
|
1
|
+
{"version":3,"file":"GatingScatterplotOptions.d.ts","sourceRoot":"","sources":["../src/GatingScatterplotOptions.js"],"names":[],"mappings":"AAMA,0EAwJC"}
|
|
@@ -1,32 +1,21 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { jsxs as _jsxs, jsx as _jsx, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
2
|
import React from 'react';
|
|
3
3
|
import { useId } from 'react-aria';
|
|
4
4
|
import { TableCell, TableRow, TextField } from '@vitessce/styles';
|
|
5
5
|
import { usePlotOptionsStyles, OptionSelect } from '@vitessce/vit-s';
|
|
6
|
-
import { capitalize
|
|
6
|
+
import { capitalize } from '@vitessce/utils';
|
|
7
7
|
export default function GatingScatterplotOptions(props) {
|
|
8
8
|
const { featureType, gatingFeatureSelectionX, setGatingFeatureSelectionX, gatingFeatureSelectionY, setGatingFeatureSelectionY, gatingFeatureValueTransform, setGatingFeatureValueTransform, gatingFeatureValueTransformCoefficient, setGatingFeatureValueTransformCoefficient, geneSelectOptions, transformOptions, } = props;
|
|
9
9
|
const gatingScatterplotOptionsId = useId();
|
|
10
|
+
const gatingScatterplotGeneXId = useId();
|
|
11
|
+
const gatingScatterplotGeneYId = useId();
|
|
10
12
|
const { classes } = usePlotOptionsStyles();
|
|
11
13
|
// Handlers for custom option field changes.
|
|
12
|
-
const
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
newValues.push(options[i].value);
|
|
18
|
-
}
|
|
19
|
-
}
|
|
20
|
-
if (newValues.length === 1
|
|
21
|
-
&& gatingFeatureSelectionX
|
|
22
|
-
&& !gatingFeatureSelectionY
|
|
23
|
-
&& newValues[0] !== gatingFeatureSelectionX) {
|
|
24
|
-
setGatingFeatureSelectionY(newValues[0]);
|
|
25
|
-
}
|
|
26
|
-
else if (newValues.length <= 2) {
|
|
27
|
-
setGatingFeatureSelectionX(newValues[0]);
|
|
28
|
-
setGatingFeatureSelectionY(newValues[1]);
|
|
29
|
-
}
|
|
14
|
+
const handleSelectionX = (event) => {
|
|
15
|
+
setGatingFeatureSelectionX(event.target.value === '' ? null : event.target.value);
|
|
16
|
+
};
|
|
17
|
+
const handleSelectionY = (event) => {
|
|
18
|
+
setGatingFeatureSelectionY(event.target.value === '' ? null : event.target.value);
|
|
30
19
|
};
|
|
31
20
|
const handleTransformChange = (event) => {
|
|
32
21
|
setGatingFeatureValueTransform(event.target.value === '' ? null : event.target.value);
|
|
@@ -45,9 +34,11 @@ export default function GatingScatterplotOptions(props) {
|
|
|
45
34
|
}
|
|
46
35
|
}
|
|
47
36
|
};
|
|
48
|
-
return (_jsxs(_Fragment, { children: [_jsxs(TableRow, { children: [_jsx(TableCell, { className: classes.labelCell, variant: "head", scope: "row", children:
|
|
49
|
-
id: `scatterplot-gating-gene-select-${
|
|
50
|
-
}, children: geneSelectOptions.map(name => (_jsx("option", { value: name, children: name }, name))) }) })] }), _jsxs(TableRow, { children: [_jsx(TableCell, { className: classes.labelCell, variant: "head", scope: "row", children:
|
|
37
|
+
return (_jsxs(_Fragment, { children: [_jsxs(TableRow, { children: [_jsx(TableCell, { className: classes.labelCell, variant: "head", scope: "row", children: _jsxs("label", { htmlFor: `scatterplot-gating-gene-select-${gatingScatterplotGeneXId}`, children: [capitalize(featureType), " along X"] }) }), _jsx(TableCell, { className: classes.inputCell, variant: "body", children: _jsxs(OptionSelect, { className: classes.select, value: gatingFeatureSelectionX ?? '', onChange: handleSelectionX, inputProps: {
|
|
38
|
+
id: `scatterplot-gating-gene-select-${gatingScatterplotGeneXId}`,
|
|
39
|
+
}, children: [_jsx("option", { value: "", children: "None" }), geneSelectOptions.map(name => (_jsx("option", { value: name, children: name }, name)))] }) })] }), _jsxs(TableRow, { children: [_jsx(TableCell, { className: classes.labelCell, variant: "head", scope: "row", children: _jsxs("label", { htmlFor: `scatterplot-gating-gene-select-${gatingScatterplotGeneYId}`, children: [capitalize(featureType), " along Y"] }) }), _jsx(TableCell, { className: classes.inputCell, variant: "body", children: _jsxs(OptionSelect, { className: classes.select, value: gatingFeatureSelectionY ?? '', onChange: handleSelectionY, inputProps: {
|
|
40
|
+
id: `scatterplot-gating-gene-select-${gatingScatterplotGeneYId}`,
|
|
41
|
+
}, children: [_jsx("option", { value: "", children: "None" }), geneSelectOptions.map(name => (_jsx("option", { value: name, children: name }, name)))] }) })] }), _jsxs(TableRow, { children: [_jsx(TableCell, { className: classes.labelCell, variant: "head", scope: "row", children: _jsx("label", { htmlFor: `scatterplot-gating-transform-select-${gatingScatterplotOptionsId}`, children: "Transform" }) }), _jsx(TableCell, { className: classes.inputCell, variant: "body", children: _jsx(OptionSelect, { className: classes.select, value: gatingFeatureValueTransform === null ? '' : gatingFeatureValueTransform, onChange: handleTransformChange, inputProps: {
|
|
51
42
|
id: `scatterplot-gating-transform-select-${gatingScatterplotOptionsId}`,
|
|
52
|
-
}, children: transformOptions.map(opt => (_jsx("option", { value: opt.value === null ? '' : opt.value, children: opt.name }, opt.name))) }) })] }), _jsxs(TableRow, { children: [_jsx(TableCell, { className: classes.labelCell, variant: "head", scope: "row", children: _jsx("label", { htmlFor: `scatterplot-gating-transform-coefficient-${gatingScatterplotOptionsId}`, children: "Transform Coefficient" }) }), _jsx(TableCell, { className: classes.inputCell, variant: "body", children: _jsx(TextField, { label: "Transform Coefficient", type: "number", onChange: handleTransformCoefficientChange, value: gatingFeatureValueTransformCoefficient, slotProps: {
|
|
43
|
+
}, children: transformOptions.map(opt => (_jsx("option", { value: opt.value === null ? '' : opt.value, children: opt.name }, opt.name))) }) })] }), _jsxs(TableRow, { children: [_jsx(TableCell, { className: classes.labelCell, variant: "head", scope: "row", children: _jsx("label", { htmlFor: `scatterplot-gating-transform-coefficient-${gatingScatterplotOptionsId}`, children: "Transform Coefficient" }) }), _jsx(TableCell, { className: classes.inputCell, variant: "body", children: _jsx(TextField, { label: "Transform Coefficient", type: "number", onChange: handleTransformCoefficientChange, value: gatingFeatureValueTransformCoefficient, slotProps: { inputLabel: { shrink: true } }, id: `scatterplot-gating-transform-coefficient-${gatingScatterplotOptionsId}` }) })] })] }));
|
|
53
44
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"GatingSubscriber.d.ts","sourceRoot":"","sources":["../src/GatingSubscriber.js"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"GatingSubscriber.d.ts","sourceRoot":"","sources":["../src/GatingSubscriber.js"],"names":[],"mappings":"AAqCA;;;;;;;;;;;;KAYK;AACL,wCAVK;IAAsB,IAAI,EAAlB,MAAM;IACQ,KAAK,EAAnB,MAAM;IACQ,kBAAkB,EAAhC,MAAM;IAES,cAAc,EAA7B,OAAO;IACS,mBAAmB;IAErB,kBAAkB,EAAhC,MAAM;CAEhB,eA6cF"}
|
|
@@ -3,7 +3,7 @@ import React, { useState, useEffect, useCallback, useMemo, } from 'react';
|
|
|
3
3
|
import { extent } from 'd3-array';
|
|
4
4
|
import { isEqual } from 'lodash-es';
|
|
5
5
|
import { pluralize as plur, capitalize, commaNumber, getValueTransformFunction, VALUE_TRANSFORM_OPTIONS, } from '@vitessce/utils';
|
|
6
|
-
import { TitleInfo, useDeckCanvasSize, useReady, useUrls, useUint8FeatureSelection, useExpressionValueGetter, useObsSetsData, useFeatureSelection, useObsFeatureMatrixIndices, useCoordination, useLoaders, useSetComponentHover, useSetComponentViewInfo, useCoordinationScopes, } from '@vitessce/vit-s';
|
|
6
|
+
import { TitleInfo, useDeckCanvasSize, useReady, useUrls, useUint8FeatureSelection, useExpressionValueGetter, useObsSetsData, useFeatureSelection, useObsFeatureMatrixIndices, useCoordination, useInitialCoordination, useLoaders, useSetComponentHover, useSetComponentViewInfo, useCoordinationScopes, } from '@vitessce/vit-s';
|
|
7
7
|
import { getCellSetPolygons, mergeObsSets, setObsSelection, getCellColors, } from '@vitessce/sets-utils';
|
|
8
8
|
import { Scatterplot, ScatterplotTooltipSubscriber, ScatterplotOptions, getPointSizeDevicePixels, getPointOpacity, EmptyMessage, } from '@vitessce/scatterplot';
|
|
9
9
|
import { ViewType, COMPONENT_COORDINATION_TYPES, ViewHelpMapping } from '@vitessce/constants-internal';
|
|
@@ -31,6 +31,7 @@ export function GatingSubscriber(props) {
|
|
|
31
31
|
const setComponentViewInfo = useSetComponentViewInfo(uuid);
|
|
32
32
|
// Get "props" from the coordination space.
|
|
33
33
|
const [{ dataset, obsType, featureType, featureValueType, embeddingZoom: zoom, embeddingTargetX: targetX, embeddingTargetY: targetY, embeddingTargetZ: targetZ, obsFilter: cellFilter, obsHighlight: cellHighlight, 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, featureSelection: gatingFeatureSelectionColor, featureValueTransform, featureValueTransformCoefficient, gatingFeatureSelectionX, gatingFeatureSelectionY, }, { 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, setFeatureValueTransform, setFeatureValueTransformCoefficient, setGatingFeatureSelectionX, setGatingFeatureSelectionY, }] = useCoordination(COMPONENT_COORDINATION_TYPES[ViewType.GATING], coordinationScopes);
|
|
34
|
+
const { embeddingZoom: initialZoom, embeddingTargetX: initialTargetX, embeddingTargetY: initialTargetY, } = useInitialCoordination(COMPONENT_COORDINATION_TYPES[ViewType.GATING], coordinationScopes);
|
|
34
35
|
const [width, height, deckRef] = useDeckCanvasSize();
|
|
35
36
|
const title = useMemo(() => {
|
|
36
37
|
if (titleOverride) {
|
|
@@ -105,6 +106,7 @@ export function GatingSubscriber(props) {
|
|
|
105
106
|
]);
|
|
106
107
|
const [dynamicCellRadius, setDynamicCellRadius] = useState(cellRadiusFixed);
|
|
107
108
|
const [dynamicCellOpacity, setDynamicCellOpacity] = useState(cellOpacityFixed);
|
|
109
|
+
const [originalViewState, setOriginalViewState] = useState(null);
|
|
108
110
|
const mergedCellSets = useMemo(() => mergeObsSets(cellSets, additionalCellSets), [cellSets, additionalCellSets]);
|
|
109
111
|
const setCellSelectionProp = useCallback((v) => {
|
|
110
112
|
setObsSelection(v, additionalCellSets, cellSetColor, setCellSetSelection, setAdditionalCellSets, setCellSetColor, setCellColorEncoding);
|
|
@@ -165,19 +167,29 @@ export function GatingSubscriber(props) {
|
|
|
165
167
|
setDynamicCellRadius(pointSizeDevicePixels);
|
|
166
168
|
const nextCellOpacityScale = getPointOpacity(zoom, xRange, yRange, width, height, numCells, averageFillDensity);
|
|
167
169
|
setDynamicCellOpacity(nextCellOpacityScale);
|
|
168
|
-
if (typeof
|
|
170
|
+
if (typeof initialTargetX !== 'number' || typeof initialTargetY !== 'number') {
|
|
169
171
|
const newTargetX = xExtent[0] + xRange / 2;
|
|
170
172
|
const newTargetY = yExtent[0] + yRange / 2;
|
|
171
173
|
const newZoom = Math.log2(Math.min(width / xRange, height / yRange));
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
174
|
+
const notYetInitialized = (typeof targetX !== 'number' || typeof targetY !== 'number');
|
|
175
|
+
const stillDefaultInitialized = (targetX === newTargetX && targetY === -newTargetY);
|
|
176
|
+
if (notYetInitialized || stillDefaultInitialized) {
|
|
177
|
+
setTargetX(newTargetX);
|
|
178
|
+
// Graphics rendering has the y-axis going south so we need to multiply by negative one.
|
|
179
|
+
setTargetY(-newTargetY);
|
|
180
|
+
setZoom(newZoom);
|
|
181
|
+
}
|
|
182
|
+
setOriginalViewState({ target: [newTargetX, -newTargetY, 0], zoom: newZoom });
|
|
183
|
+
}
|
|
184
|
+
else if (!originalViewState) {
|
|
185
|
+
// originalViewState has not yet been set and
|
|
186
|
+
// the view config defined an initial viewState.
|
|
187
|
+
setOriginalViewState({ target: [initialTargetX, initialTargetY, 0], zoom: initialZoom });
|
|
176
188
|
}
|
|
177
189
|
}
|
|
178
190
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
179
191
|
}, [xRange, yRange, xExtent, yExtent, numCells,
|
|
180
|
-
width, height, zoom, averageFillDensity]);
|
|
192
|
+
width, height, zoom, initialTargetX, initialTargetY, averageFillDensity]);
|
|
181
193
|
const cellSelectionSet = useMemo(() => new Set(cellSelection), [cellSelection]);
|
|
182
194
|
const getCellIsSelected = useCallback((object, { index }) => ((cellSelectionSet || new Set([])).has(obsIndex[index]) ? 1.0 : 0.0), [cellSelectionSet, obsIndex]);
|
|
183
195
|
const cellRadius = (cellRadiusMode === 'manual' ? cellRadiusFixed : dynamicCellRadius);
|
|
@@ -204,17 +216,18 @@ export function GatingSubscriber(props) {
|
|
|
204
216
|
}, [obsXY, obsIndex, featureValueTransform,
|
|
205
217
|
gatingFeatureSelectionX, gatingFeatureSelectionY, obsType,
|
|
206
218
|
]);
|
|
219
|
+
const setViewState = ({ zoom: newZoom, target }) => {
|
|
220
|
+
setZoom(newZoom);
|
|
221
|
+
setTargetX(target[0]);
|
|
222
|
+
setTargetY(target[1]);
|
|
223
|
+
setTargetZ(target[2] || 0);
|
|
224
|
+
};
|
|
207
225
|
return (_jsxs(TitleInfo, { title: title, info: `${commaNumber(cellsCount)} ${plur(obsType, cellsCount)}`, closeButtonVisible: closeButtonVisible, downloadButtonVisible: downloadButtonVisible, removeGridComponent: removeGridComponent, urls: urls, theme: theme, isReady: isReady, helpText: helpText, errors: errors, options: (_jsx(ScatterplotOptions, { observationsLabel: obsType, cellRadius: cellRadiusFixed, setCellRadius: setCellRadiusFixed, cellRadiusMode: cellRadiusMode, setCellRadiusMode: setCellRadiusMode, cellOpacity: cellOpacityFixed, setCellOpacity: setCellOpacityFixed, cellOpacityMode: cellOpacityMode, setCellOpacityMode: setCellOpacityMode, cellSetLabelsVisible: cellSetLabelsVisible, setCellSetLabelsVisible: setCellSetLabelsVisible, cellSetLabelSize: cellSetLabelSize, setCellSetLabelSize: setCellSetLabelSize, cellSetPolygonsVisible: cellSetPolygonsVisible, setCellSetPolygonsVisible: setCellSetPolygonsVisible, cellColorEncoding: cellColorEncoding, setCellColorEncoding: setCellColorEncoding, geneExpressionColormap: geneExpressionColormap, setGeneExpressionColormap: setGeneExpressionColormap, geneExpressionColormapRange: geneExpressionColormapRange, setGeneExpressionColormapRange: setGeneExpressionColormapRange, children: _jsx(GatingScatterplotOptions, { featureType: featureType, gatingFeatureSelectionX: gatingFeatureSelectionX, setGatingFeatureSelectionX: setGatingFeatureSelectionX, gatingFeatureSelectionY: gatingFeatureSelectionY, setGatingFeatureSelectionY: setGatingFeatureSelectionY, gatingFeatureValueTransform: featureValueTransform, setGatingFeatureValueTransform: (newValue) => {
|
|
208
226
|
setFeatureValueTransform(newValue);
|
|
209
227
|
setTargetX(null);
|
|
210
228
|
setTargetY(null);
|
|
211
229
|
setZoom(null);
|
|
212
|
-
}, gatingFeatureValueTransformCoefficient: featureValueTransformCoefficient, setGatingFeatureValueTransformCoefficient: setFeatureValueTransformCoefficient, geneSelectOptions: featureIndex, transformOptions: VALUE_TRANSFORM_OPTIONS }) })), children: [_jsx(EmptyMessage, { visible: !(gatingFeatureSelectionX && gatingFeatureSelectionY), message: `Select two ${plur(featureType, 2)} in the plot settings.` }), _jsx(Scatterplot, { ref: deckRef, uuid: uuid, theme: theme, hideTools: !(gatingFeatureSelectionX && gatingFeatureSelectionY), viewState: { zoom, target: [targetX, targetY, targetZ] }, setViewState:
|
|
213
|
-
setZoom(newZoom);
|
|
214
|
-
setTargetX(target[0]);
|
|
215
|
-
setTargetY(target[1]);
|
|
216
|
-
setTargetZ(target[2] || 0);
|
|
217
|
-
}, obsEmbeddingIndex: obsIndex, obsEmbedding: obsXY, 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: () => {
|
|
230
|
+
}, gatingFeatureValueTransformCoefficient: featureValueTransformCoefficient, setGatingFeatureValueTransformCoefficient: setFeatureValueTransformCoefficient, geneSelectOptions: featureIndex, transformOptions: VALUE_TRANSFORM_OPTIONS }) })), children: [_jsx(EmptyMessage, { visible: !(gatingFeatureSelectionX && gatingFeatureSelectionY), message: `Select two ${plur(featureType, 2)} in the plot settings.` }), _jsx(Scatterplot, { ref: deckRef, uuid: uuid, theme: theme, hideTools: !(gatingFeatureSelectionX && gatingFeatureSelectionY), hideRecenter: !(gatingFeatureSelectionX && gatingFeatureSelectionY), viewState: { zoom, target: [targetX, targetY, targetZ] }, setViewState: setViewState, originalViewState: originalViewState, obsEmbeddingIndex: obsIndex, obsEmbedding: obsXY, 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: () => {
|
|
218
231
|
setComponentHover(uuid);
|
|
219
|
-
}, updateViewInfo: setComponentViewInfo, getExpressionValue: getExpressionValue, getCellIsSelected: getCellIsSelected }), !disableTooltip && (_jsx(ScatterplotTooltipSubscriber, { parentUuid: uuid, obsHighlight: cellHighlight, width: width, height: height, getObsInfo: getObsInfo }))] }));
|
|
232
|
+
}, updateViewInfo: setComponentViewInfo, getExpressionValue: getExpressionValue, getCellIsSelected: getCellIsSelected, embeddingPointsVisible: true }), !disableTooltip && (_jsx(ScatterplotTooltipSubscriber, { parentUuid: uuid, obsHighlight: cellHighlight, width: width, height: height, getObsInfo: getObsInfo }))] }));
|
|
220
233
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vitessce/scatterplot-gating",
|
|
3
|
-
"version": "3.8.
|
|
3
|
+
"version": "3.8.2",
|
|
4
4
|
"author": "HIDIVE Lab at HMS",
|
|
5
5
|
"homepage": "http://vitessce.io",
|
|
6
6
|
"repository": {
|
|
@@ -19,12 +19,12 @@
|
|
|
19
19
|
"d3-array": "^2.4.0",
|
|
20
20
|
"lodash-es": "^4.17.21",
|
|
21
21
|
"react-aria": "^3.28.0",
|
|
22
|
-
"@vitessce/styles": "3.8.
|
|
23
|
-
"@vitessce/constants-internal": "3.8.
|
|
24
|
-
"@vitessce/scatterplot": "3.8.
|
|
25
|
-
"@vitessce/sets-utils": "3.8.
|
|
26
|
-
"@vitessce/utils": "3.8.
|
|
27
|
-
"@vitessce/vit-s": "3.8.
|
|
22
|
+
"@vitessce/styles": "3.8.2",
|
|
23
|
+
"@vitessce/constants-internal": "3.8.2",
|
|
24
|
+
"@vitessce/scatterplot": "3.8.2",
|
|
25
|
+
"@vitessce/sets-utils": "3.8.2",
|
|
26
|
+
"@vitessce/utils": "3.8.2",
|
|
27
|
+
"@vitessce/vit-s": "3.8.2"
|
|
28
28
|
},
|
|
29
29
|
"devDependencies": {
|
|
30
30
|
"react": "18.3.1",
|
|
@@ -2,7 +2,7 @@ import React from 'react';
|
|
|
2
2
|
import { useId } from 'react-aria';
|
|
3
3
|
import { TableCell, TableRow, TextField } from '@vitessce/styles';
|
|
4
4
|
import { usePlotOptionsStyles, OptionSelect } from '@vitessce/vit-s';
|
|
5
|
-
import { capitalize
|
|
5
|
+
import { capitalize } from '@vitessce/utils';
|
|
6
6
|
|
|
7
7
|
export default function GatingScatterplotOptions(props) {
|
|
8
8
|
const {
|
|
@@ -20,28 +20,18 @@ export default function GatingScatterplotOptions(props) {
|
|
|
20
20
|
} = props;
|
|
21
21
|
|
|
22
22
|
const gatingScatterplotOptionsId = useId();
|
|
23
|
+
const gatingScatterplotGeneXId = useId();
|
|
24
|
+
const gatingScatterplotGeneYId = useId();
|
|
23
25
|
|
|
24
26
|
const { classes } = usePlotOptionsStyles();
|
|
25
27
|
|
|
26
28
|
// Handlers for custom option field changes.
|
|
27
|
-
const
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
for (let i = 0, l = options.length; i < l; i += 1) {
|
|
31
|
-
if (options[i].selected) {
|
|
32
|
-
newValues.push(options[i].value);
|
|
33
|
-
}
|
|
34
|
-
}
|
|
29
|
+
const handleSelectionX = (event) => {
|
|
30
|
+
setGatingFeatureSelectionX(event.target.value === '' ? null : event.target.value);
|
|
31
|
+
};
|
|
35
32
|
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
&& !gatingFeatureSelectionY
|
|
39
|
-
&& newValues[0] !== gatingFeatureSelectionX) {
|
|
40
|
-
setGatingFeatureSelectionY(newValues[0]);
|
|
41
|
-
} else if (newValues.length <= 2) {
|
|
42
|
-
setGatingFeatureSelectionX(newValues[0]);
|
|
43
|
-
setGatingFeatureSelectionY(newValues[1]);
|
|
44
|
-
}
|
|
33
|
+
const handleSelectionY = (event) => {
|
|
34
|
+
setGatingFeatureSelectionY(event.target.value === '' ? null : event.target.value);
|
|
45
35
|
};
|
|
46
36
|
|
|
47
37
|
const handleTransformChange = (event) => {
|
|
@@ -67,21 +57,51 @@ export default function GatingScatterplotOptions(props) {
|
|
|
67
57
|
<TableRow>
|
|
68
58
|
<TableCell className={classes.labelCell} variant="head" scope="row">
|
|
69
59
|
<label
|
|
70
|
-
htmlFor={`scatterplot-gating-gene-select-${
|
|
60
|
+
htmlFor={`scatterplot-gating-gene-select-${gatingScatterplotGeneXId}`}
|
|
61
|
+
>
|
|
62
|
+
{capitalize(featureType)} along X
|
|
63
|
+
</label>
|
|
64
|
+
</TableCell>
|
|
65
|
+
<TableCell className={classes.inputCell} variant="body">
|
|
66
|
+
<OptionSelect
|
|
67
|
+
className={classes.select}
|
|
68
|
+
value={gatingFeatureSelectionX ?? ''}
|
|
69
|
+
onChange={handleSelectionX}
|
|
70
|
+
inputProps={{
|
|
71
|
+
id: `scatterplot-gating-gene-select-${gatingScatterplotGeneXId}`,
|
|
72
|
+
}}
|
|
73
|
+
>
|
|
74
|
+
<option value="">
|
|
75
|
+
None
|
|
76
|
+
</option>
|
|
77
|
+
{geneSelectOptions.map(name => (
|
|
78
|
+
<option key={name} value={name}>
|
|
79
|
+
{name}
|
|
80
|
+
</option>
|
|
81
|
+
))}
|
|
82
|
+
</OptionSelect>
|
|
83
|
+
</TableCell>
|
|
84
|
+
</TableRow>
|
|
85
|
+
<TableRow>
|
|
86
|
+
<TableCell className={classes.labelCell} variant="head" scope="row">
|
|
87
|
+
<label
|
|
88
|
+
htmlFor={`scatterplot-gating-gene-select-${gatingScatterplotGeneYId}`}
|
|
71
89
|
>
|
|
72
|
-
{capitalize(
|
|
90
|
+
{capitalize(featureType)} along Y
|
|
73
91
|
</label>
|
|
74
92
|
</TableCell>
|
|
75
93
|
<TableCell className={classes.inputCell} variant="body">
|
|
76
94
|
<OptionSelect
|
|
77
|
-
multiple
|
|
78
95
|
className={classes.select}
|
|
79
|
-
value={
|
|
80
|
-
onChange={
|
|
96
|
+
value={gatingFeatureSelectionY ?? ''}
|
|
97
|
+
onChange={handleSelectionY}
|
|
81
98
|
inputProps={{
|
|
82
|
-
id: `scatterplot-gating-gene-select-${
|
|
99
|
+
id: `scatterplot-gating-gene-select-${gatingScatterplotGeneYId}`,
|
|
83
100
|
}}
|
|
84
101
|
>
|
|
102
|
+
<option value="">
|
|
103
|
+
None
|
|
104
|
+
</option>
|
|
85
105
|
{geneSelectOptions.map(name => (
|
|
86
106
|
<option key={name} value={name}>
|
|
87
107
|
{name}
|
|
@@ -129,7 +149,7 @@ export default function GatingScatterplotOptions(props) {
|
|
|
129
149
|
type="number"
|
|
130
150
|
onChange={handleTransformCoefficientChange}
|
|
131
151
|
value={gatingFeatureValueTransformCoefficient}
|
|
132
|
-
slotProps={{
|
|
152
|
+
slotProps={{ inputLabel: { shrink: true } }}
|
|
133
153
|
id={`scatterplot-gating-transform-coefficient-${gatingScatterplotOptionsId}`}
|
|
134
154
|
/>
|
|
135
155
|
</TableCell>
|
package/src/GatingSubscriber.js
CHANGED
|
@@ -17,6 +17,7 @@ import {
|
|
|
17
17
|
useFeatureSelection,
|
|
18
18
|
useObsFeatureMatrixIndices,
|
|
19
19
|
useCoordination,
|
|
20
|
+
useInitialCoordination,
|
|
20
21
|
useLoaders,
|
|
21
22
|
useSetComponentHover,
|
|
22
23
|
useSetComponentViewInfo,
|
|
@@ -126,6 +127,14 @@ export function GatingSubscriber(props) {
|
|
|
126
127
|
coordinationScopes,
|
|
127
128
|
);
|
|
128
129
|
|
|
130
|
+
const {
|
|
131
|
+
embeddingZoom: initialZoom,
|
|
132
|
+
embeddingTargetX: initialTargetX,
|
|
133
|
+
embeddingTargetY: initialTargetY,
|
|
134
|
+
} = useInitialCoordination(
|
|
135
|
+
COMPONENT_COORDINATION_TYPES[ViewType.GATING], coordinationScopes,
|
|
136
|
+
);
|
|
137
|
+
|
|
129
138
|
const [width, height, deckRef] = useDeckCanvasSize();
|
|
130
139
|
|
|
131
140
|
const title = useMemo(() => {
|
|
@@ -244,6 +253,8 @@ export function GatingSubscriber(props) {
|
|
|
244
253
|
const [dynamicCellRadius, setDynamicCellRadius] = useState(cellRadiusFixed);
|
|
245
254
|
const [dynamicCellOpacity, setDynamicCellOpacity] = useState(cellOpacityFixed);
|
|
246
255
|
|
|
256
|
+
const [originalViewState, setOriginalViewState] = useState(null);
|
|
257
|
+
|
|
247
258
|
const mergedCellSets = useMemo(() => mergeObsSets(
|
|
248
259
|
cellSets, additionalCellSets,
|
|
249
260
|
), [cellSets, additionalCellSets]);
|
|
@@ -323,19 +334,28 @@ export function GatingSubscriber(props) {
|
|
|
323
334
|
);
|
|
324
335
|
setDynamicCellOpacity(nextCellOpacityScale);
|
|
325
336
|
|
|
326
|
-
if (typeof
|
|
337
|
+
if (typeof initialTargetX !== 'number' || typeof initialTargetY !== 'number') {
|
|
327
338
|
const newTargetX = xExtent[0] + xRange / 2;
|
|
328
339
|
const newTargetY = yExtent[0] + yRange / 2;
|
|
329
340
|
const newZoom = Math.log2(Math.min(width / xRange, height / yRange));
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
341
|
+
const notYetInitialized = (typeof targetX !== 'number' || typeof targetY !== 'number');
|
|
342
|
+
const stillDefaultInitialized = (targetX === newTargetX && targetY === -newTargetY);
|
|
343
|
+
if (notYetInitialized || stillDefaultInitialized) {
|
|
344
|
+
setTargetX(newTargetX);
|
|
345
|
+
// Graphics rendering has the y-axis going south so we need to multiply by negative one.
|
|
346
|
+
setTargetY(-newTargetY);
|
|
347
|
+
setZoom(newZoom);
|
|
348
|
+
}
|
|
349
|
+
setOriginalViewState({ target: [newTargetX, -newTargetY, 0], zoom: newZoom });
|
|
350
|
+
} else if (!originalViewState) {
|
|
351
|
+
// originalViewState has not yet been set and
|
|
352
|
+
// the view config defined an initial viewState.
|
|
353
|
+
setOriginalViewState({ target: [initialTargetX, initialTargetY, 0], zoom: initialZoom });
|
|
334
354
|
}
|
|
335
355
|
}
|
|
336
356
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
337
357
|
}, [xRange, yRange, xExtent, yExtent, numCells,
|
|
338
|
-
width, height, zoom, averageFillDensity]);
|
|
358
|
+
width, height, zoom, initialTargetX, initialTargetY, averageFillDensity]);
|
|
339
359
|
|
|
340
360
|
const cellSelectionSet = useMemo(() => new Set(cellSelection), [cellSelection]);
|
|
341
361
|
const getCellIsSelected = useCallback((object, { index }) => (
|
|
@@ -372,6 +392,13 @@ export function GatingSubscriber(props) {
|
|
|
372
392
|
gatingFeatureSelectionX, gatingFeatureSelectionY, obsType,
|
|
373
393
|
]);
|
|
374
394
|
|
|
395
|
+
const setViewState = ({ zoom: newZoom, target }) => {
|
|
396
|
+
setZoom(newZoom);
|
|
397
|
+
setTargetX(target[0]);
|
|
398
|
+
setTargetY(target[1]);
|
|
399
|
+
setTargetZ(target[2] || 0);
|
|
400
|
+
};
|
|
401
|
+
|
|
375
402
|
return (
|
|
376
403
|
<TitleInfo
|
|
377
404
|
title={title}
|
|
@@ -438,13 +465,10 @@ export function GatingSubscriber(props) {
|
|
|
438
465
|
uuid={uuid}
|
|
439
466
|
theme={theme}
|
|
440
467
|
hideTools={!(gatingFeatureSelectionX && gatingFeatureSelectionY)}
|
|
468
|
+
hideRecenter={!(gatingFeatureSelectionX && gatingFeatureSelectionY)}
|
|
441
469
|
viewState={{ zoom, target: [targetX, targetY, targetZ] }}
|
|
442
|
-
setViewState={
|
|
443
|
-
|
|
444
|
-
setTargetX(target[0]);
|
|
445
|
-
setTargetY(target[1]);
|
|
446
|
-
setTargetZ(target[2] || 0);
|
|
447
|
-
}}
|
|
470
|
+
setViewState={setViewState}
|
|
471
|
+
originalViewState={originalViewState}
|
|
448
472
|
obsEmbeddingIndex={obsIndex}
|
|
449
473
|
obsEmbedding={obsXY}
|
|
450
474
|
cellFilter={cellFilter}
|
|
@@ -470,6 +494,8 @@ export function GatingSubscriber(props) {
|
|
|
470
494
|
getExpressionValue={getExpressionValue}
|
|
471
495
|
getCellIsSelected={getCellIsSelected}
|
|
472
496
|
|
|
497
|
+
embeddingPointsVisible
|
|
498
|
+
|
|
473
499
|
/>
|
|
474
500
|
{!disableTooltip && (
|
|
475
501
|
<ScatterplotTooltipSubscriber
|