Skip to content

Commit 4432a4a

Browse files
authored
Merge pull request #197 from silx-kit/fix-custom-domain
Fix custom domain resetting
2 parents ef04aae + 07ac258 commit 4432a4a

File tree

10 files changed

+55
-50
lines changed

10 files changed

+55
-50
lines changed

src/h5web/visualizations/heatmap/ColorBar.tsx

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ import type { ColorMap } from './models';
99
import { INTERPOLATORS } from './interpolators';
1010

1111
interface Props {
12-
domain?: Domain;
12+
domain: Domain;
1313
scaleType: ScaleType;
1414
colorMap: ColorMap;
1515
}
@@ -19,10 +19,6 @@ function ColorBar(props: Props): JSX.Element {
1919
const interpolator = INTERPOLATORS[colorMap];
2020
const [gradientRef, { height: gradientHeight }] = useMeasure();
2121

22-
if (!domain) {
23-
return <></>;
24-
}
25-
2622
const axisScale = SCALE_FUNCTIONS[scaleType]();
2723
axisScale.domain(domain);
2824
axisScale.range([gradientHeight, 0]);

src/h5web/visualizations/heatmap/DomainSlider.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ function DomainSlider(props: Props): ReactElement {
2222
const [extendedMin, extendedMax] = extendDomain(dataDomain, EXTEND_FACTOR);
2323
const step = Math.max((extendedMax - extendedMin) / 100, 10 ** -NB_DECIMALS);
2424

25-
const updateCustomDomain = debounce((bounds) => {
25+
const updateDomain = debounce((bounds) => {
2626
const roundedDomain = (bounds as number[]).map((val) =>
2727
round(val, NB_DECIMALS)
2828
) as Domain;
@@ -54,7 +54,7 @@ function DomainSlider(props: Props): ReactElement {
5454
</div>
5555
)}
5656
value={[...(value || dataDomain)]}
57-
onChange={updateCustomDomain}
57+
onChange={updateDomain}
5858
min={extendedMin}
5959
max={extendedMax}
6060
step={step}

src/h5web/visualizations/heatmap/HeatmapToolbar.tsx

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,8 @@ import ScaleSelector from '../shared/ScaleSelector';
1212
function HeatmapToolbar(): ReactElement {
1313
const {
1414
dataDomain,
15-
customDomain,
16-
setCustomDomain,
15+
requestedDomain,
16+
setRequestedDomain,
1717
colorMap,
1818
setColorMap,
1919
scaleType,
@@ -29,8 +29,8 @@ function HeatmapToolbar(): ReactElement {
2929
{dataDomain && (
3030
<DomainSlider
3131
dataDomain={dataDomain}
32-
value={customDomain}
33-
onChange={setCustomDomain}
32+
value={requestedDomain}
33+
onChange={setRequestedDomain}
3434
/>
3535
)}
3636

src/h5web/visualizations/heatmap/HeatmapVis.module.css

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,3 +47,8 @@
4747
flex: 1 0 auto;
4848
border: 1px solid black;
4949
}
50+
51+
.domainError {
52+
padding: 1em;
53+
color: var(--danger);
54+
}

src/h5web/visualizations/heatmap/HeatmapVis.tsx

Lines changed: 25 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,17 @@
1-
import React, { useEffect } from 'react';
1+
import React, { useMemo, useEffect } from 'react';
22
import { format } from 'd3-format';
33
import type ndarray from 'ndarray';
4+
import { usePrevious } from 'react-use';
45
import styles from './HeatmapVis.module.css';
56
import ColorBar from './ColorBar';
67
import Mesh from './Mesh';
78
import TooltipMesh from '../shared/TooltipMesh';
89
import { useHeatmapConfig } from './config';
910
import PanZoomMesh from '../shared/PanZoomMesh';
1011
import VisCanvas from '../shared/VisCanvas';
11-
import { getDims, useMemoColorScaleDomain } from './utils';
12+
import { getDims } from './utils';
13+
import { findDomain } from '../shared/utils';
14+
import { useMemoColorScaleDomain } from './hooks';
1215

1316
interface Props {
1417
dataArray: ndarray<number>;
@@ -21,21 +24,27 @@ function HeatmapVis(props: Props): JSX.Element {
2124
const values = dataArray.data as number[];
2225

2326
const {
24-
dataDomain,
25-
customDomain,
27+
requestedDomain,
2628
scaleType,
2729
colorMap,
2830
keepAspectRatio,
2931
showGrid,
30-
initDataDomain,
32+
resetDomains,
3133
} = useHeatmapConfig();
3234

33-
// width / height <=> cols / rows
34-
const aspectRatio = keepAspectRatio ? cols / rows : undefined;
35+
const dataDomain = useMemo(() => findDomain(values), [values]);
36+
const prevDataDomain = usePrevious(dataDomain);
37+
38+
// If `dataDomain` just changed, use `undefined` custom domain for this render until config's `requestedDomain` is updated through `useEffect` below
39+
const customDomain =
40+
dataDomain !== prevDataDomain ? undefined : requestedDomain;
3541

3642
useEffect(() => {
37-
initDataDomain(values);
38-
}, [initDataDomain, values]);
43+
// When `dataDomain` changes:
44+
// - set data domain in config so toolbar can render domain slider with correct bounds;
45+
// - reset requested domain in config so toolbar can reset value of domain slider, and so Heatmap can use in its next render.
46+
resetDomains(dataDomain);
47+
}, [dataDomain, resetDomains]);
3948

4049
const domain = useMemoColorScaleDomain(
4150
scaleType,
@@ -44,6 +53,13 @@ function HeatmapVis(props: Props): JSX.Element {
4453
customDomain
4554
);
4655

56+
// width / height <=> cols / rows
57+
const aspectRatio = keepAspectRatio ? cols / rows : undefined;
58+
59+
if (!domain) {
60+
return <p className={styles.domainError}>Unable to compute domain</p>;
61+
}
62+
4763
return (
4864
<div className={styles.root}>
4965
<VisCanvas

src/h5web/visualizations/heatmap/Mesh.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ interface Props {
1111
rows: number;
1212
cols: number;
1313
values: number[];
14-
domain: Domain | undefined;
14+
domain: Domain;
1515
scaleType: ScaleType;
1616
colorMap: ColorMap;
1717
}

src/h5web/visualizations/heatmap/config.ts

Lines changed: 9 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,13 @@
11
import type { ColorMap } from './models';
22
import { Domain, ScaleType } from '../shared/models';
33
import { StorageConfig, createPersistableState } from '../../storage-utils';
4-
import { findDomain } from '../shared/utils';
54

65
interface HeatmapConfig {
76
dataDomain: Domain | undefined;
8-
initDataDomain: (values: number[]) => void;
7+
resetDomains: (dataDomain: Domain | undefined) => void;
98

10-
customDomain: Domain | undefined;
11-
setCustomDomain: (customDomain: Domain | undefined) => void;
9+
requestedDomain: Domain | undefined;
10+
setRequestedDomain: (requestedDomain: Domain | undefined) => void;
1211

1312
colorMap: ColorMap;
1413
setColorMap: (colorMap: ColorMap) => void;
@@ -32,16 +31,16 @@ export const [useHeatmapConfig] = createPersistableState<HeatmapConfig>(
3231
STORAGE_CONFIG,
3332
(set) => ({
3433
dataDomain: undefined,
35-
initDataDomain: (values: number[]) => {
34+
resetDomains: (dataDomain: Domain | undefined) => {
3635
set({
37-
dataDomain: findDomain(values),
38-
customDomain: undefined, // reset custom domain
36+
dataDomain,
37+
requestedDomain: undefined,
3938
});
4039
},
4140

42-
customDomain: undefined,
43-
setCustomDomain: (customDomain: Domain | undefined) =>
44-
set({ customDomain }),
41+
requestedDomain: undefined,
42+
setRequestedDomain: (requestedDomain: Domain | undefined) =>
43+
set({ requestedDomain }),
4544

4645
colorMap: 'Viridis',
4746
setColorMap: (colorMap: ColorMap) => set({ colorMap }),

src/h5web/visualizations/heatmap/hooks.ts

Lines changed: 6 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,15 @@
11
import { useEffect } from 'react';
22
import { transfer } from 'comlink';
33
import { useComlink } from 'react-use-comlink';
4-
import { useSetState } from 'react-use';
4+
import { useSetState, createMemo } from 'react-use';
55

66
// @ts-ignore
77
import Worker from 'worker-loader!./worker';
88

99
import type { TextureWorker } from './worker';
1010
import type { Domain, ScaleType } from '../shared/models';
1111
import type { ColorMap } from './models';
12+
import { getColorScaleDomain } from './utils';
1213

1314
export interface TextureDataState {
1415
loading?: boolean;
@@ -20,26 +21,14 @@ export function useTextureData(
2021
rows: number,
2122
cols: number,
2223
values: number[],
23-
domain: Domain | undefined,
24+
domain: Domain,
2425
scaleType: ScaleType,
2526
colorMap: ColorMap
2627
): TextureDataState {
2728
const { proxy } = useComlink<TextureWorker>(() => new Worker(), []);
2829
const [state, mergeState] = useSetState<TextureDataState>({});
2930

30-
/*
31-
* Dependencies that trigger a recomputation of the texture.
32-
* > Note that `values` is purposely not included. When `values` changes, a first render
33-
* > is triggered, during which `dataDomain` has not yet been recomputed. We must wait for
34-
* > `domain` to be updated before recomputing the texture.
35-
*/
36-
const deps = [colorMap, domain, scaleType, proxy, mergeState];
37-
3831
useEffect(() => {
39-
if (!domain) {
40-
return;
41-
}
42-
4332
// Keep existing texture data, if any
4433
mergeState({ loading: true });
4534

@@ -57,7 +46,7 @@ export function useTextureData(
5746
),
5847
});
5948
})();
60-
}, deps); // eslint-disable-line react-hooks/exhaustive-deps
49+
}, [colorMap, domain, scaleType, proxy, mergeState, values]);
6150

6251
// Reset texture when dimensions change to avoid rendering glitch while computing new texture
6352
const dims = `${rows}x${cols}`;
@@ -67,3 +56,5 @@ export function useTextureData(
6756

6857
return state;
6958
}
59+
60+
export const useMemoColorScaleDomain = createMemo(getColorScaleDomain);

src/h5web/visualizations/heatmap/utils.ts

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
import { range } from 'lodash-es';
2-
import { createMemo } from 'react-use';
32
import type { D3Interpolator, Dims } from './models';
43
import type { DataArray } from '../../dataset-visualizer/models';
54
import { ScaleType, Domain } from '../shared/models';
@@ -21,7 +20,7 @@ export function generateCSSLinearGradient(
2120
return `linear-gradient(to ${direction},${gradientColors})`;
2221
}
2322

24-
function getColorScaleDomain(
23+
export function getColorScaleDomain(
2524
scaleType: ScaleType,
2625
values: number[],
2726
dataDomain?: Domain,
@@ -47,5 +46,3 @@ function getColorScaleDomain(
4746

4847
return supportedDomain;
4948
}
50-
51-
export const useMemoColorScaleDomain = createMemo(getColorScaleDomain);

src/styles/vars.css

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
--secondary-darker: #0e5846;
1313
--secondary-bg: #d9f4ec;
1414
--secondary-light-bg: #ecfaf6;
15+
--danger: #99231b;
1516
--monospace: 'SFMono-Regular', Consolas, 'Liberation Mono', Menlo, Courier,
1617
monospace;
1718
--sans-serif: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto',

0 commit comments

Comments
 (0)