@vitessce/heatmap 2.0.3 → 3.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (48) hide show
  1. package/dist/{deflate.1b70f605.mjs → deflate-16770a05.js} +2 -2
  2. package/dist/{index.35c24bfa.mjs → index-516800f9.js} +132612 -120210
  3. package/dist/{index.mjs → index.js} +2 -3
  4. package/dist/{jpeg.1b7865ea.mjs → jpeg-aaa6dcb6.js} +1 -1
  5. package/dist/{lerc.ff3140f6.mjs → lerc-4c94e66f.js} +76 -5
  6. package/dist/{lzw.1036ab46.mjs → lzw-072df1d0.js} +1 -1
  7. package/dist/{packbits.088a4e84.mjs → packbits-3dcafae7.js} +1 -1
  8. package/dist/{pako.esm.4b234125.mjs → pako.esm-68f84e2a.js} +97 -15
  9. package/dist/{raw.b3ce459e.mjs → raw-282aecb8.js} +1 -1
  10. package/dist/{webimage.4cecc301.mjs → webimage-0d9c3bfd.js} +1 -1
  11. package/dist-tsc/Heatmap.d.ts +43 -0
  12. package/dist-tsc/Heatmap.d.ts.map +1 -0
  13. package/dist-tsc/Heatmap.js +698 -0
  14. package/dist-tsc/Heatmap.test.d.ts +2 -0
  15. package/dist-tsc/Heatmap.test.d.ts.map +1 -0
  16. package/dist-tsc/Heatmap.test.fixtures.d.ts +7 -0
  17. package/dist-tsc/Heatmap.test.fixtures.d.ts.map +1 -0
  18. package/dist-tsc/Heatmap.test.fixtures.js +20 -0
  19. package/dist-tsc/Heatmap.test.js +17 -0
  20. package/dist-tsc/HeatmapOptions.d.ts +2 -0
  21. package/dist-tsc/HeatmapOptions.d.ts.map +1 -0
  22. package/dist-tsc/HeatmapOptions.js +29 -0
  23. package/dist-tsc/HeatmapSubscriber.d.ts +19 -0
  24. package/dist-tsc/HeatmapSubscriber.d.ts.map +1 -0
  25. package/dist-tsc/HeatmapSubscriber.js +120 -0
  26. package/dist-tsc/HeatmapTooltipSubscriber.d.ts +2 -0
  27. package/dist-tsc/HeatmapTooltipSubscriber.d.ts.map +1 -0
  28. package/dist-tsc/HeatmapTooltipSubscriber.js +24 -0
  29. package/dist-tsc/HeatmapWorkerPool.d.ts +28 -0
  30. package/dist-tsc/HeatmapWorkerPool.d.ts.map +1 -0
  31. package/dist-tsc/HeatmapWorkerPool.js +49 -0
  32. package/dist-tsc/index.d.ts +3 -0
  33. package/dist-tsc/index.d.ts.map +1 -0
  34. package/dist-tsc/index.js +2 -2
  35. package/dist-tsc/utils.d.ts +49 -0
  36. package/dist-tsc/utils.d.ts.map +1 -0
  37. package/dist-tsc/utils.js +181 -0
  38. package/dist-tsc/utils.test.d.ts +2 -0
  39. package/dist-tsc/utils.test.d.ts.map +1 -0
  40. package/dist-tsc/utils.test.js +71 -0
  41. package/package.json +26 -14
  42. package/src/Heatmap.js +45 -30
  43. package/src/Heatmap.test.jsx +7 -6
  44. package/src/HeatmapOptions.js +27 -4
  45. package/src/HeatmapSubscriber.js +62 -26
  46. package/src/index.js +2 -2
  47. package/src/utils.js +1 -1
  48. package/src/utils.test.js +1 -1
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=Heatmap.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Heatmap.test.d.ts","sourceRoot":"","sources":["../src/Heatmap.test.jsx"],"names":[],"mappings":""}
@@ -0,0 +1,7 @@
1
+ export namespace expressionMatrix {
2
+ const rows: string[];
3
+ const cols: string[];
4
+ const matrix: Uint8Array;
5
+ }
6
+ export const cellColors: Map<string, number[]>;
7
+ //# sourceMappingURL=Heatmap.test.fixtures.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Heatmap.test.fixtures.d.ts","sourceRoot":"","sources":["../src/Heatmap.test.fixtures.js"],"names":[],"mappings":";;;;;AAeA,+CAKG"}
@@ -0,0 +1,20 @@
1
+ export const expressionMatrix = {
2
+ rows: ['cell-0', 'cell-1', 'cell-2', 'cell-3', 'cell-4'],
3
+ cols: ['gene-0', 'gene-1', 'gene-2', 'gene-3'],
4
+ // "An image with an 'F' on it has a clear direction so it's easy to tell
5
+ // if it's turned or flipped etc when we use it as a texture."
6
+ // - https://webglfundamentals.org/webgl/lessons/webgl-3d-textures.html
7
+ matrix: Uint8Array.from([
8
+ 0, 255, 255, 0,
9
+ 0, 255, 0, 0,
10
+ 0, 255, 255, 0,
11
+ 0, 255, 0, 0,
12
+ 0, 255, 0, 0,
13
+ ]),
14
+ };
15
+ export const cellColors = new Map([
16
+ ['cell-1', [0, 0, 255]],
17
+ ['cell-0', [0, 0, 255]],
18
+ ['cell-3', [255, 0, 0]],
19
+ ['cell-2', [255, 0, 0]],
20
+ ]);
@@ -0,0 +1,17 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ /* eslint-disable func-names */
3
+ import '@testing-library/jest-dom';
4
+ import { cleanup, render } from '@testing-library/react';
5
+ import { afterEach } from 'vitest';
6
+ import React from 'react';
7
+ import Heatmap from './Heatmap.js';
8
+ import { expressionMatrix, cellColors } from './Heatmap.test.fixtures.js';
9
+ afterEach(() => {
10
+ cleanup();
11
+ });
12
+ describe('<Heatmap/>', () => {
13
+ it('renders a DeckGL element', () => {
14
+ const { container } = render(_jsx(Heatmap, { uuid: "heatmap-0", theme: "dark", width: 100, height: 100, colormap: "plasma", colormapRange: [0.0, 1.0], expressionMatrix: expressionMatrix, cellColors: cellColors, transpose: true, viewState: { zoom: 0, target: [0, 0] } }));
15
+ expect(container.querySelectorAll('#deckgl-overlay-heatmap-0-wrapper').length).toEqual(1);
16
+ });
17
+ });
@@ -0,0 +1,2 @@
1
+ export default function HeatmapOptions(props: any): JSX.Element;
2
+ //# sourceMappingURL=HeatmapOptions.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"HeatmapOptions.d.ts","sourceRoot":"","sources":["../src/HeatmapOptions.js"],"names":[],"mappings":"AAMA,gEAuFC"}
@@ -0,0 +1,29 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import React, { useCallback } from 'react';
3
+ import { debounce } from 'lodash-es';
4
+ import { Checkbox, Slider, TableCell, TableRow } from '@material-ui/core';
5
+ import { usePlotOptionsStyles, OptionsContainer, OptionSelect } from '@vitessce/vit-s';
6
+ import { GLSL_COLORMAPS } from '@vitessce/gl';
7
+ export default function HeatmapOptions(props) {
8
+ const { geneExpressionColormap, setGeneExpressionColormap, geneExpressionColormapRange, setGeneExpressionColormapRange, tooltipsVisible, setTooltipsVisible, } = props;
9
+ const classes = usePlotOptionsStyles();
10
+ function handleGeneExpressionColormapChange(event) {
11
+ setGeneExpressionColormap(event.target.value);
12
+ }
13
+ function handleTooltipsVisibilityChange(event) {
14
+ setTooltipsVisible(event.target.checked);
15
+ }
16
+ function handleColormapRangeChange(event, value) {
17
+ setGeneExpressionColormapRange(value);
18
+ }
19
+ const handleColormapRangeChangeDebounced = useCallback(debounce(handleColormapRangeChange, 5, { trailing: true }), [handleColormapRangeChange]);
20
+ return (_jsxs(OptionsContainer, { children: [_jsxs(TableRow, { children: [_jsx(TableCell, { className: classes.labelCell, htmlFor: "gene-expression-colormap-select", children: "Gene Expression Colormap" }), _jsx(TableCell, { className: classes.inputCell, children: _jsx(OptionSelect, { className: classes.select, value: geneExpressionColormap, onChange: handleGeneExpressionColormapChange, inputProps: {
21
+ id: 'gene-expression-colormap-select',
22
+ }, children: GLSL_COLORMAPS.map(cmap => (_jsx("option", { value: cmap, children: cmap }, cmap))) }) })] }), _jsxs(TableRow, { children: [_jsx(TableCell, { className: classes.labelCell, children: "Tooltips Visible" }), _jsx(TableCell, { className: classes.inputCell, children: _jsx(Checkbox, { className: classes.checkbox,
23
+ /**
24
+ * We have to use "checked" here, not "value".
25
+ * The checkbox state is not persisting with value.
26
+ * For reference, https://v4.mui.com/api/checkbox/
27
+ */
28
+ checked: tooltipsVisible, onChange: handleTooltipsVisibilityChange, name: "gene-expression-colormap-option-toltip-visibility", color: "default" }) })] }), _jsxs(TableRow, { children: [_jsx(TableCell, { className: classes.labelCell, children: "Gene Expression Colormap Range" }), _jsx(TableCell, { className: classes.inputCell, children: _jsx(Slider, { classes: { root: classes.slider, valueLabel: classes.sliderValueLabel }, value: geneExpressionColormapRange, onChange: handleColormapRangeChangeDebounced, "aria-labelledby": "gene-expression-colormap-range-slider", valueLabelDisplay: "auto", step: 0.005, min: 0.0, max: 1.0 }) })] })] }));
29
+ }
@@ -0,0 +1,19 @@
1
+ /**
2
+ * @param {object} props
3
+ * @param {number} props.uuid The unique identifier for this component.
4
+ * @param {object} props.coordinationScopes The mapping from coordination types to coordination
5
+ * scopes.
6
+ * @param {function} props.removeGridComponent The callback function to pass to TitleInfo,
7
+ * to call when the component has been removed from the grid.
8
+ * @param {string} props.title The component title.
9
+ * @param {boolean} props.transpose Whether to
10
+ * render as cell-by-gene or gene-by-cell.
11
+ */
12
+ export function HeatmapSubscriber(props: {
13
+ uuid: number;
14
+ coordinationScopes: object;
15
+ removeGridComponent: Function;
16
+ title: string;
17
+ transpose: boolean;
18
+ }): JSX.Element;
19
+ //# sourceMappingURL=HeatmapSubscriber.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"HeatmapSubscriber.d.ts","sourceRoot":"","sources":["../src/HeatmapSubscriber.js"],"names":[],"mappings":"AA4BA;;;;;;;;;;GAUG;AACH;IATyB,IAAI,EAAlB,MAAM;IACQ,kBAAkB,EAAhC,MAAM;IAEU,mBAAmB;IAErB,KAAK,EAAnB,MAAM;IACS,SAAS,EAAxB,OAAO;gBA4PjB"}
@@ -0,0 +1,120 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import React, { useState, useCallback, useMemo, } from 'react';
3
+ import plur from 'plur';
4
+ import { TitleInfo, useDeckCanvasSize, useGetObsMembership, useGetObsInfo, useReady, useUrls, useObsSetsData, useObsFeatureMatrixData, useUint8ObsFeatureMatrix, useMultiObsLabels, useFeatureLabelsData, useCoordination, useLoaders, useSetComponentHover, useSetComponentViewInfo, } from '@vitessce/vit-s';
5
+ import { capitalize, commaNumber, getCellColors } from '@vitessce/utils';
6
+ import { mergeObsSets, findLongestCommonPath } from '@vitessce/sets-utils';
7
+ import { COMPONENT_COORDINATION_TYPES, ViewType } from '@vitessce/constants-internal';
8
+ import { Legend } from '@vitessce/legend';
9
+ import Heatmap from './Heatmap.js';
10
+ import HeatmapTooltipSubscriber from './HeatmapTooltipSubscriber.js';
11
+ import HeatmapOptions from './HeatmapOptions.js';
12
+ /**
13
+ * @param {object} props
14
+ * @param {number} props.uuid The unique identifier for this component.
15
+ * @param {object} props.coordinationScopes The mapping from coordination types to coordination
16
+ * scopes.
17
+ * @param {function} props.removeGridComponent The callback function to pass to TitleInfo,
18
+ * to call when the component has been removed from the grid.
19
+ * @param {string} props.title The component title.
20
+ * @param {boolean} props.transpose Whether to
21
+ * render as cell-by-gene or gene-by-cell.
22
+ */
23
+ export function HeatmapSubscriber(props) {
24
+ const { uuid, coordinationScopes, removeGridComponent, theme, transpose, observationsLabelOverride, variablesLabelOverride, title = 'Heatmap', } = props;
25
+ const loaders = useLoaders();
26
+ const setComponentHover = useSetComponentHover();
27
+ const setComponentViewInfo = useSetComponentViewInfo(uuid);
28
+ // Get "props" from the coordination space.
29
+ const [{ dataset, obsType, featureType, featureValueType, heatmapZoomX: zoomX, heatmapTargetX: targetX, heatmapTargetY: targetY, featureSelection: geneSelection, obsHighlight: cellHighlight, featureHighlight: geneHighlight, obsSetSelection: cellSetSelection, obsSetColor: cellSetColor, additionalObsSets: additionalCellSets, featureValueColormap: geneExpressionColormap, featureValueColormapRange: geneExpressionColormapRange, tooltipsVisible, }, { setHeatmapZoomX: setZoomX, setHeatmapZoomY: setZoomY, setHeatmapTargetX: setTargetX, setHeatmapTargetY: setTargetY, setObsHighlight: setCellHighlight, setFeatureSelection: setGeneSelection, setObsColorEncoding: setCellColorEncoding, setFeatureHighlight: setGeneHighlight, setObsSetSelection: setCellSetSelection, setObsSetColor: setCellSetColor, setFeatureValueColormapRange: setGeneExpressionColormapRange, setFeatureValueColormap: setGeneExpressionColormap, setTooltipsVisible, }] = useCoordination(COMPONENT_COORDINATION_TYPES[ViewType.HEATMAP], coordinationScopes);
30
+ const observationsLabel = observationsLabelOverride || obsType;
31
+ const observationsPluralLabel = plur(observationsLabel);
32
+ const variablesLabel = variablesLabelOverride || featureType;
33
+ const variablesPluralLabel = plur(variablesLabel);
34
+ const observationsTitle = capitalize(observationsPluralLabel);
35
+ const variablesTitle = capitalize(variablesPluralLabel);
36
+ const [isRendering, setIsRendering] = useState(false);
37
+ // We need to know whether the user is currently hovering over the expression part
38
+ // of the heatmap vs. the color bar part, which will affect whether we call
39
+ // setObsColorEncoding with 'geneSelection' or 'cellSetSelection' upon a click.
40
+ const [hoveredColorEncoding, setHoveredColorEncoding] = useState('geneSelection');
41
+ const [urls, addUrl] = useUrls(loaders, dataset);
42
+ const [width, height, deckRef] = useDeckCanvasSize();
43
+ // Get data from loaders using the data hooks.
44
+ const [obsLabelsTypes, obsLabelsData] = useMultiObsLabels(coordinationScopes, obsType, loaders, dataset, addUrl);
45
+ // TODO: support multiple feature labels using featureLabelsType coordination values.
46
+ const [{ featureLabelsMap }, featureLabelsStatus] = useFeatureLabelsData(loaders, dataset, addUrl, false, {}, {}, { featureType });
47
+ const [{ obsIndex, featureIndex, obsFeatureMatrix }, matrixStatus] = useObsFeatureMatrixData(loaders, dataset, addUrl, true, {}, {}, { obsType, featureType, featureValueType });
48
+ const [{ obsSets: cellSets, obsSetsMembership }, obsSetsStatus] = useObsSetsData(loaders, dataset, addUrl, false, { setObsSetSelection: setCellSetSelection, setObsSetColor: setCellSetColor }, { obsSetSelection: cellSetSelection, obsSetColor: cellSetColor }, { obsType });
49
+ const isReady = useReady([
50
+ featureLabelsStatus,
51
+ matrixStatus,
52
+ obsSetsStatus,
53
+ ]);
54
+ const [uint8ObsFeatureMatrix, obsFeatureMatrixExtent] = useUint8ObsFeatureMatrix({ obsFeatureMatrix });
55
+ const mergedCellSets = useMemo(() => mergeObsSets(cellSets, additionalCellSets), [cellSets, additionalCellSets]);
56
+ const cellColors = useMemo(() => getCellColors({
57
+ cellSets: mergedCellSets,
58
+ cellSetSelection,
59
+ cellSetColor,
60
+ obsIndex,
61
+ theme,
62
+ }), [mergedCellSets, theme,
63
+ cellSetColor, cellSetSelection, obsIndex]);
64
+ const getObsInfo = useGetObsInfo(observationsLabel, obsLabelsTypes, obsLabelsData, obsSetsMembership);
65
+ const getObsMembership = useGetObsMembership(obsSetsMembership);
66
+ const getFeatureInfo = useCallback((featureId) => {
67
+ if (featureId) {
68
+ const featureLabel = featureLabelsMap?.get(featureId) || featureId;
69
+ return { [`${capitalize(variablesLabel)} ID`]: featureLabel };
70
+ }
71
+ return null;
72
+ }, [variablesLabel, featureLabelsMap]);
73
+ const expressionMatrix = useMemo(() => {
74
+ if (obsIndex && featureIndex && uint8ObsFeatureMatrix) {
75
+ return {
76
+ rows: obsIndex,
77
+ cols: (featureLabelsMap
78
+ ? featureIndex.map(key => featureLabelsMap.get(key) || key)
79
+ : featureIndex),
80
+ matrix: uint8ObsFeatureMatrix.data,
81
+ };
82
+ }
83
+ return null;
84
+ }, [obsIndex, featureIndex, uint8ObsFeatureMatrix, featureLabelsMap]);
85
+ const cellsCount = obsIndex ? obsIndex.length : 0;
86
+ const genesCount = featureIndex ? featureIndex.length : 0;
87
+ const setTrackHighlight = useCallback(() => {
88
+ // No-op, since the default handler
89
+ // logs in the console on every hover event.
90
+ }, []);
91
+ const onHeatmapClick = () => {
92
+ if (hoveredColorEncoding === 'geneSelection' && geneHighlight) {
93
+ setGeneSelection([geneHighlight]);
94
+ setCellColorEncoding('geneSelection');
95
+ }
96
+ else if (hoveredColorEncoding === 'cellSelection' && cellSetSelection) {
97
+ const selectionFullPath = getObsMembership(cellHighlight);
98
+ if (selectionFullPath?.length > 0) {
99
+ const selectionToHighlight = findLongestCommonPath(selectionFullPath, cellSetSelection);
100
+ if (selectionToHighlight) {
101
+ setCellSetSelection([selectionToHighlight]);
102
+ setCellColorEncoding('cellSelection');
103
+ }
104
+ }
105
+ }
106
+ };
107
+ const cellColorLabels = useMemo(() => ([
108
+ `${capitalize(observationsLabel)} Set`,
109
+ ]), [observationsLabel]);
110
+ const selectedCount = cellColors.size;
111
+ return (_jsxs(TitleInfo, { title: title, info: `${commaNumber(cellsCount)} ${plur(observationsLabel, cellsCount)} × ${commaNumber(genesCount)} ${plur(variablesLabel, genesCount)},
112
+ with ${commaNumber(selectedCount)} ${plur(observationsLabel, selectedCount)} selected`, urls: urls, theme: theme, removeGridComponent: removeGridComponent, isReady: isReady && !isRendering, options: (_jsx(HeatmapOptions, { geneExpressionColormap: geneExpressionColormap, setGeneExpressionColormap: setGeneExpressionColormap, geneExpressionColormapRange: geneExpressionColormapRange, setGeneExpressionColormapRange: setGeneExpressionColormapRange, tooltipsVisible: tooltipsVisible, setTooltipsVisible: setTooltipsVisible })), children: [_jsx(Heatmap, { ref: deckRef, transpose: transpose, viewState: { zoom: zoomX, target: [targetX, targetY] }, setViewState: ({ zoom, target }) => {
113
+ setZoomX(zoom);
114
+ setZoomY(zoom);
115
+ setTargetX(target[0]);
116
+ setTargetY(target[1]);
117
+ }, colormapRange: geneExpressionColormapRange, setColormapRange: setGeneExpressionColormapRange, height: height, width: width, theme: theme, uuid: uuid, expressionMatrix: expressionMatrix, cellColors: cellColors, colormap: geneExpressionColormap, setIsRendering: setIsRendering, setCellHighlight: setCellHighlight, setGeneHighlight: setGeneHighlight, featureIndex: featureIndex, setTrackHighlight: setTrackHighlight, setComponentHover: () => {
118
+ setComponentHover(uuid);
119
+ }, updateViewInfo: setComponentViewInfo, observationsTitle: observationsTitle, variablesTitle: variablesTitle, variablesDashes: false, observationsDashes: false, cellColorLabels: cellColorLabels, useDevicePixels: true, onHeatmapClick: onHeatmapClick, setColorEncoding: setHoveredColorEncoding }), tooltipsVisible && (_jsx(HeatmapTooltipSubscriber, { parentUuid: uuid, width: width, height: height, transpose: transpose, getObsInfo: getObsInfo, getFeatureInfo: getFeatureInfo, obsHighlight: cellHighlight, featureHighlight: geneHighlight })), _jsx(Legend, { visible: true, theme: theme, featureType: featureType, featureValueType: featureValueType, obsColorEncoding: "geneExpression", considerSelections: false, featureSelection: geneSelection, featureValueColormap: geneExpressionColormap, featureValueColormapRange: geneExpressionColormapRange, extent: obsFeatureMatrixExtent })] }));
120
+ }
@@ -0,0 +1,2 @@
1
+ export default function HeatmapTooltipSubscriber(props: any): JSX.Element | null;
2
+ //# sourceMappingURL=HeatmapTooltipSubscriber.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"HeatmapTooltipSubscriber.d.ts","sourceRoot":"","sources":["../src/HeatmapTooltipSubscriber.js"],"names":[],"mappings":"AAIA,iFA8CC"}
@@ -0,0 +1,24 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import React from 'react';
3
+ import { Tooltip2D, TooltipContent } from '@vitessce/tooltip';
4
+ import { useComponentHover, useComponentViewInfo } from '@vitessce/vit-s';
5
+ export default function HeatmapTooltipSubscriber(props) {
6
+ const { parentUuid, width, height, transpose, getObsInfo, getFeatureInfo, obsHighlight, featureHighlight, } = props;
7
+ const sourceUuid = useComponentHover();
8
+ const viewInfo = useComponentViewInfo(parentUuid);
9
+ const [cellInfo, cellCoord] = (obsHighlight && getObsInfo ? ([
10
+ getObsInfo(obsHighlight),
11
+ (viewInfo && viewInfo.project
12
+ ? viewInfo.project(obsHighlight, null)[(transpose ? 0 : 1)]
13
+ : null),
14
+ ]) : ([null, null]));
15
+ const [geneInfo, geneCoord] = (featureHighlight && getFeatureInfo ? ([
16
+ getFeatureInfo(featureHighlight),
17
+ (viewInfo && viewInfo.project
18
+ ? viewInfo.project(null, featureHighlight)[(transpose ? 1 : 0)]
19
+ : null),
20
+ ]) : ([null, null]));
21
+ const x = (transpose ? cellCoord : geneCoord);
22
+ const y = (transpose ? geneCoord : cellCoord);
23
+ return ((cellInfo || geneInfo ? (_jsx(Tooltip2D, { x: x, y: y, parentUuid: parentUuid, parentWidth: width, parentHeight: height, sourceUuid: sourceUuid, children: _jsx(TooltipContent, { info: { ...geneInfo, ...cellInfo } }) })) : null));
24
+ }
@@ -0,0 +1,28 @@
1
+ /**
2
+ * Pool for workers to decode chunks of the images.
3
+ * This is a line-for-line copy of GeoTIFFs old implementation: https://github.com/geotiffjs/geotiff.js/blob/v1.0.0-beta.6/src/pool.js
4
+ */
5
+ export default class HeatmapPool extends Pool {
6
+ constructor();
7
+ /**
8
+ * Process each heatmap tile
9
+ * @param {object} params The arguments passed to the heatmap worker.
10
+ * @param {string} params.curr The current task uuid.
11
+ * @param {number} params.xTiles How many tiles required in the x direction?
12
+ * @param {number} params.yTiles How many tiles required in the y direction?
13
+ * @param {number} params.tileSize How many entries along each tile axis?
14
+ * @param {string[]} params.cellOrdering The current ordering of cells.
15
+ * @param {string[]} params.rows The name of each row (cell ID).
16
+ * Does not take transpose into account (always cells).
17
+ * @param {string[]} params.cols The name of each column (gene ID).
18
+ * Does not take transpose into account (always genes).
19
+ * @param {ArrayBuffer} params.data The array buffer.
20
+ * Need to transfer back to main thread when done.
21
+ * @param {boolean} params.transpose Is the heatmap transposed?
22
+ * @returns {array} [message, transfers]
23
+ * @returns {Promise.<ArrayBuffer>} the decoded result as a `Promise`
24
+ */
25
+ process(args: any): array;
26
+ }
27
+ import { Pool } from "@vitessce/utils";
28
+ //# sourceMappingURL=HeatmapWorkerPool.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"HeatmapWorkerPool.d.ts","sourceRoot":"","sources":["../src/HeatmapWorkerPool.js"],"names":[],"mappings":"AAQA;;;GAGG;AACH;IACE,cAEC;IAED;;;;;;;;;;;;;;;;;OAiBG;IACH,0BAeC;CACF"}
@@ -0,0 +1,49 @@
1
+ import { HeatmapWorker } from '@vitessce/workers';
2
+ import { Pool } from '@vitessce/utils';
3
+ // Reference: https://github.com/developit/jsdom-worker/issues/14#issuecomment-1268070123
4
+ function createWorker() {
5
+ return new HeatmapWorker();
6
+ }
7
+ /**
8
+ * Pool for workers to decode chunks of the images.
9
+ * This is a line-for-line copy of GeoTIFFs old implementation: https://github.com/geotiffjs/geotiff.js/blob/v1.0.0-beta.6/src/pool.js
10
+ */
11
+ export default class HeatmapPool extends Pool {
12
+ constructor() {
13
+ super(createWorker);
14
+ }
15
+ /**
16
+ * Process each heatmap tile
17
+ * @param {object} params The arguments passed to the heatmap worker.
18
+ * @param {string} params.curr The current task uuid.
19
+ * @param {number} params.xTiles How many tiles required in the x direction?
20
+ * @param {number} params.yTiles How many tiles required in the y direction?
21
+ * @param {number} params.tileSize How many entries along each tile axis?
22
+ * @param {string[]} params.cellOrdering The current ordering of cells.
23
+ * @param {string[]} params.rows The name of each row (cell ID).
24
+ * Does not take transpose into account (always cells).
25
+ * @param {string[]} params.cols The name of each column (gene ID).
26
+ * Does not take transpose into account (always genes).
27
+ * @param {ArrayBuffer} params.data The array buffer.
28
+ * Need to transfer back to main thread when done.
29
+ * @param {boolean} params.transpose Is the heatmap transposed?
30
+ * @returns {array} [message, transfers]
31
+ * @returns {Promise.<ArrayBuffer>} the decoded result as a `Promise`
32
+ */
33
+ async process(args) {
34
+ const currentWorker = await this.waitForWorker();
35
+ return new Promise((resolve, reject) => {
36
+ currentWorker.onmessage = (event) => {
37
+ // this.workers.push(currentWorker);
38
+ this.finishTask(currentWorker);
39
+ resolve(event.data);
40
+ };
41
+ currentWorker.onerror = (error) => {
42
+ // this.workers.push(currentWorker);
43
+ this.finishTask(currentWorker);
44
+ reject(error);
45
+ };
46
+ currentWorker.postMessage(['getTile', args], [args.data]);
47
+ });
48
+ }
49
+ }
@@ -0,0 +1,3 @@
1
+ export { HeatmapSubscriber } from "./HeatmapSubscriber.js";
2
+ export { default as Heatmap } from "./Heatmap.js";
3
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.js"],"names":[],"mappings":""}
package/dist-tsc/index.js CHANGED
@@ -1,2 +1,2 @@
1
- export { HeatmapSubscriber, register } from './HeatmapSubscriber';
2
- export { default as Heatmap } from './Heatmap';
1
+ export { HeatmapSubscriber } from './HeatmapSubscriber.js';
2
+ export { default as Heatmap } from './Heatmap.js';
@@ -0,0 +1,49 @@
1
+ /**
2
+ * Called before a layer is drawn to determine whether it should be rendered.
3
+ * Reference: https://deck.gl/docs/api-reference/core/deck#layerfilter
4
+ * @param {object} params A viewport, layer pair.
5
+ * @param {object} params.layer The layer to check.
6
+ * @param {object} params.viewport The viewport to check.
7
+ * @returns {boolean} Should this layer be rendered in this viewport?
8
+ */
9
+ export function layerFilter({ layer, viewport }: {
10
+ layer: object;
11
+ viewport: object;
12
+ }): boolean;
13
+ /**
14
+ * Get the size of the left and top heatmap axes,
15
+ * taking into account the maximum label string lengths.
16
+ * @param {boolean} transpose Is the heatmap transposed?
17
+ * @param {String} longestGeneLabel longest gene label
18
+ * @param {String} longestCellLabel longest cell label
19
+ * @param {boolean} hideObservationLabels are cell labels hidden?
20
+ * @param {boolean} hideVariableLabels are gene labels hidden?
21
+ * Increases vertical space for heatmap
22
+ * @returns {number[]} [axisOffsetLeft, axisOffsetTop]
23
+ */
24
+ export function getAxisSizes(transpose: boolean, longestGeneLabel: string, longestCellLabel: string, hideObservationLabels: boolean, hideVariableLabels: boolean): number[];
25
+ /**
26
+ * Convert a mouse coordinate (x, y) to a heatmap coordinate (col index, row index).
27
+ * @param {number} mouseX The mouse X of interest.
28
+ * @param {number} mouseY The mouse Y of interest.
29
+ * @param {object} param2 An object containing current sizes and scale factors.
30
+ * @returns {number[]} [colI, rowI]
31
+ */
32
+ export function mouseToHeatmapPosition(mouseX: number, mouseY: number, { offsetLeft, offsetTop, targetX, targetY, scaleFactor, matrixWidth, matrixHeight, numRows, numCols, }: object): number[];
33
+ /**
34
+ * Convert a heatmap coordinate (col index, row index) to a mouse coordinate (x, y).
35
+ * @param {number} colI The column index of interest.
36
+ * @param {number} rowI The row index of interest.
37
+ * @param {object} param2 An object containing current sizes and scale factors.
38
+ * @returns {number[]} [x, y]
39
+ */
40
+ export function heatmapToMousePosition(colI: number, rowI: number, { offsetLeft, offsetTop, targetX, targetY, scaleFactor, matrixWidth, matrixHeight, numRows, numCols, }: object): number[];
41
+ /**
42
+ * Convert a mouse coordinate (x, y) to a heatmap color bar coordinate (cell index, track index).
43
+ * @param {number} mouseX The mouse X of interest.
44
+ * @param {number} mouseY The mouse Y of interest.
45
+ * @param {object} param2 An object containing current sizes and scale factors.
46
+ * @returns {number[]} [cellI, trackI]
47
+ */
48
+ export function mouseToCellColorPosition(mouseX: number, mouseY: number, { axisOffsetTop, axisOffsetLeft, offsetTop, offsetLeft, colorBarSize, numCellColorTracks, transpose, targetX, targetY, scaleFactor, matrixWidth, matrixHeight, numRows, numCols, }: object): number[];
49
+ //# sourceMappingURL=utils.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../src/utils.js"],"names":[],"mappings":"AASA;;;;;;;GAOG;AACH;IAJ0B,KAAK,EAApB,MAAM;IACS,QAAQ,EAAvB,MAAM;IACJ,OAAO,CA8BnB;AAqBD;;;;;;;;;;GAUG;AACH,wCARW,OAAO,6EAGP,OAAO,sBACP,OAAO,GAEL,MAAM,EAAE,CAuBpB;AAED;;;;;;GAMG;AACH,+CALW,MAAM,UACN,MAAM,0GACN,MAAM,GACJ,MAAM,EAAE,CAiCpB;AAED;;;;;;GAMG;AACH,6CALW,MAAM,QACN,MAAM,0GACN,MAAM,GACJ,MAAM,EAAE,CA0CpB;AAED;;;;;;GAMG;AACH,iDALW,MAAM,UACN,MAAM,sLACN,MAAM,GACJ,MAAM,EAAE,CAoDpB"}
@@ -0,0 +1,181 @@
1
+ import { clamp } from 'lodash-es';
2
+ import { AXIS_LABEL_TEXT_SIZE, AXIS_FONT_FAMILY, AXIS_PADDING, AXIS_MIN_SIZE, AXIS_MAX_SIZE, } from '@vitessce/gl';
3
+ /**
4
+ * Called before a layer is drawn to determine whether it should be rendered.
5
+ * Reference: https://deck.gl/docs/api-reference/core/deck#layerfilter
6
+ * @param {object} params A viewport, layer pair.
7
+ * @param {object} params.layer The layer to check.
8
+ * @param {object} params.viewport The viewport to check.
9
+ * @returns {boolean} Should this layer be rendered in this viewport?
10
+ */
11
+ export function layerFilter({ layer, viewport }) {
12
+ if (viewport.id === 'axisLeft') {
13
+ return layer.id.startsWith('axisLeft');
14
+ }
15
+ if (viewport.id === 'axisTop') {
16
+ return layer.id.startsWith('axisTop');
17
+ }
18
+ if (viewport.id === 'cellColorLabel') {
19
+ return layer.id.startsWith('cellColorLabel');
20
+ }
21
+ if (viewport.id === 'heatmap') {
22
+ return layer.id.startsWith('heatmap');
23
+ }
24
+ if (viewport.id.startsWith('colorsLeft')) {
25
+ const matches = viewport.id.match(/-(\d)/);
26
+ if (matches)
27
+ return layer.id.startsWith(`colorsLeftLayer-${matches[1]}`);
28
+ }
29
+ if (viewport.id.startsWith('colorsTop')) {
30
+ const matches = viewport.id.match(/-(\d)/);
31
+ if (matches)
32
+ return layer.id.startsWith(`colorsTopLayer-${matches[1]}`);
33
+ }
34
+ return false;
35
+ }
36
+ /**
37
+ * Uses canvas.measureText to compute and return the width of the given text
38
+ * of given font in pixels.
39
+ *
40
+ * @param {String} text The text to be rendered.
41
+ * @param {String} font The css font descriptor that text is to be rendered
42
+ * with (e.g. "bold 14px verdana").
43
+ *
44
+ * @see https://stackoverflow.com/questions/118241/calculate-text-width-with-javascript/21015393#21015393
45
+ */
46
+ function getTextWidth(text, font) {
47
+ // re-use canvas object for better performance
48
+ const canvas = getTextWidth.canvas || (getTextWidth.canvas = document.createElement('canvas'));
49
+ const context = canvas.getContext('2d');
50
+ context.font = font;
51
+ const metrics = context.measureText(text);
52
+ return metrics.width;
53
+ }
54
+ /**
55
+ * Get the size of the left and top heatmap axes,
56
+ * taking into account the maximum label string lengths.
57
+ * @param {boolean} transpose Is the heatmap transposed?
58
+ * @param {String} longestGeneLabel longest gene label
59
+ * @param {String} longestCellLabel longest cell label
60
+ * @param {boolean} hideObservationLabels are cell labels hidden?
61
+ * @param {boolean} hideVariableLabels are gene labels hidden?
62
+ * Increases vertical space for heatmap
63
+ * @returns {number[]} [axisOffsetLeft, axisOffsetTop]
64
+ */
65
+ export function getAxisSizes(transpose, longestGeneLabel, longestCellLabel, hideObservationLabels, hideVariableLabels) {
66
+ const font = `${AXIS_LABEL_TEXT_SIZE}pt ${AXIS_FONT_FAMILY}`;
67
+ const geneLabelMaxWidth = hideVariableLabels
68
+ ? 0 : getTextWidth(longestGeneLabel, font) + AXIS_PADDING;
69
+ const cellLabelMaxWidth = hideObservationLabels
70
+ ? 0 : getTextWidth(longestCellLabel, font) + AXIS_PADDING;
71
+ const axisOffsetLeft = clamp((transpose ? geneLabelMaxWidth : cellLabelMaxWidth), AXIS_MIN_SIZE, AXIS_MAX_SIZE);
72
+ const axisOffsetTop = clamp((transpose ? cellLabelMaxWidth : geneLabelMaxWidth), AXIS_MIN_SIZE, AXIS_MAX_SIZE);
73
+ return [axisOffsetLeft, axisOffsetTop];
74
+ }
75
+ /**
76
+ * Convert a mouse coordinate (x, y) to a heatmap coordinate (col index, row index).
77
+ * @param {number} mouseX The mouse X of interest.
78
+ * @param {number} mouseY The mouse Y of interest.
79
+ * @param {object} param2 An object containing current sizes and scale factors.
80
+ * @returns {number[]} [colI, rowI]
81
+ */
82
+ export function mouseToHeatmapPosition(mouseX, mouseY, { offsetLeft, offsetTop, targetX, targetY, scaleFactor, matrixWidth, matrixHeight, numRows, numCols, }) {
83
+ // TODO: use linear algebra
84
+ const viewMouseX = mouseX - offsetLeft;
85
+ const viewMouseY = mouseY - offsetTop;
86
+ if (viewMouseX < 0 || viewMouseY < 0) {
87
+ // The mouse is outside the heatmap.
88
+ return [null, null];
89
+ }
90
+ // Determine the rowI and colI values based on the current viewState.
91
+ const bboxTargetX = targetX * scaleFactor + matrixWidth * scaleFactor / 2;
92
+ const bboxTargetY = targetY * scaleFactor + matrixHeight * scaleFactor / 2;
93
+ const bboxLeft = bboxTargetX - matrixWidth / 2;
94
+ const bboxTop = bboxTargetY - matrixHeight / 2;
95
+ const zoomedOffsetLeft = bboxLeft / (matrixWidth * scaleFactor);
96
+ const zoomedOffsetTop = bboxTop / (matrixHeight * scaleFactor);
97
+ const zoomedViewMouseX = viewMouseX / (matrixWidth * scaleFactor);
98
+ const zoomedViewMouseY = viewMouseY / (matrixHeight * scaleFactor);
99
+ const zoomedMouseX = zoomedOffsetLeft + zoomedViewMouseX;
100
+ const zoomedMouseY = zoomedOffsetTop + zoomedViewMouseY;
101
+ const rowI = Math.floor(zoomedMouseY * numRows);
102
+ const colI = Math.floor(zoomedMouseX * numCols);
103
+ return [colI, rowI];
104
+ }
105
+ /**
106
+ * Convert a heatmap coordinate (col index, row index) to a mouse coordinate (x, y).
107
+ * @param {number} colI The column index of interest.
108
+ * @param {number} rowI The row index of interest.
109
+ * @param {object} param2 An object containing current sizes and scale factors.
110
+ * @returns {number[]} [x, y]
111
+ */
112
+ export function heatmapToMousePosition(colI, rowI, { offsetLeft, offsetTop, targetX, targetY, scaleFactor, matrixWidth, matrixHeight, numRows, numCols, }) {
113
+ // TODO: use linear algebra
114
+ let zoomedMouseY = null;
115
+ let zoomedMouseX = null;
116
+ if (rowI !== null) {
117
+ const minY = -matrixHeight * scaleFactor / 2;
118
+ const maxY = matrixHeight * scaleFactor / 2;
119
+ const totalHeight = maxY - minY;
120
+ const minInViewY = (targetY * scaleFactor) - (matrixHeight / 2);
121
+ const maxInViewY = (targetY * scaleFactor) + (matrixHeight / 2);
122
+ const inViewHeight = maxInViewY - minInViewY;
123
+ const normalizedRowY = (rowI + 0.5) / numRows;
124
+ const globalRowY = minY + (normalizedRowY * totalHeight);
125
+ if (minInViewY <= globalRowY && globalRowY <= maxInViewY) {
126
+ zoomedMouseY = offsetTop + ((globalRowY - minInViewY) / inViewHeight) * matrixHeight;
127
+ }
128
+ }
129
+ if (colI !== null) {
130
+ const minX = -matrixWidth * scaleFactor / 2;
131
+ const maxX = matrixWidth * scaleFactor / 2;
132
+ const totalWidth = maxX - minX;
133
+ const minInViewX = (targetX * scaleFactor) - (matrixWidth / 2);
134
+ const maxInViewX = (targetX * scaleFactor) + (matrixWidth / 2);
135
+ const inViewWidth = maxInViewX - minInViewX;
136
+ const normalizedRowX = (colI + 0.5) / numCols;
137
+ const globalRowX = minX + (normalizedRowX * totalWidth);
138
+ if (minInViewX <= globalRowX && globalRowX <= maxInViewX) {
139
+ zoomedMouseX = offsetLeft + ((globalRowX - minInViewX) / inViewWidth) * matrixWidth;
140
+ }
141
+ }
142
+ return [zoomedMouseX, zoomedMouseY];
143
+ }
144
+ /**
145
+ * Convert a mouse coordinate (x, y) to a heatmap color bar coordinate (cell index, track index).
146
+ * @param {number} mouseX The mouse X of interest.
147
+ * @param {number} mouseY The mouse Y of interest.
148
+ * @param {object} param2 An object containing current sizes and scale factors.
149
+ * @returns {number[]} [cellI, trackI]
150
+ */
151
+ export function mouseToCellColorPosition(mouseX, mouseY, { axisOffsetTop, axisOffsetLeft, offsetTop, offsetLeft, colorBarSize, numCellColorTracks, transpose, targetX, targetY, scaleFactor, matrixWidth, matrixHeight, numRows, numCols, }) {
152
+ const cellPosition = transpose ? mouseX - offsetLeft : mouseY - offsetTop;
153
+ const trackPosition = transpose ? mouseY - axisOffsetTop : mouseX - axisOffsetLeft;
154
+ const tracksWidth = numCellColorTracks * colorBarSize;
155
+ // outside of cell color tracks
156
+ if (cellPosition < 0 || trackPosition < 0 || trackPosition >= tracksWidth) {
157
+ return [null, null];
158
+ }
159
+ // Determine the trackI and cellI values based on the current viewState.
160
+ const trackI = Math.floor(trackPosition / colorBarSize);
161
+ let cellI;
162
+ if (transpose) {
163
+ const viewMouseX = mouseX - offsetLeft;
164
+ const bboxTargetX = targetX * scaleFactor + matrixWidth * scaleFactor / 2;
165
+ const bboxLeft = bboxTargetX - matrixWidth / 2;
166
+ const zoomedOffsetLeft = bboxLeft / (matrixWidth * scaleFactor);
167
+ const zoomedViewMouseX = viewMouseX / (matrixWidth * scaleFactor);
168
+ const zoomedMouseX = zoomedOffsetLeft + zoomedViewMouseX;
169
+ cellI = Math.floor(zoomedMouseX * numCols);
170
+ return [cellI, trackI];
171
+ }
172
+ // Not transposed
173
+ const viewMouseY = mouseY - axisOffsetTop;
174
+ const bboxTargetY = targetY * scaleFactor + matrixHeight * scaleFactor / 2;
175
+ const bboxTop = bboxTargetY - matrixHeight / 2;
176
+ const zoomedOffsetTop = bboxTop / (matrixHeight * scaleFactor);
177
+ const zoomedViewMouseY = viewMouseY / (matrixHeight * scaleFactor);
178
+ const zoomedMouseY = zoomedOffsetTop + zoomedViewMouseY;
179
+ cellI = Math.floor(zoomedMouseY * numRows);
180
+ return [cellI, trackI];
181
+ }
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=utils.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"utils.test.d.ts","sourceRoot":"","sources":["../src/utils.test.js"],"names":[],"mappings":""}