Skip to content

Commit 7a3daa0

Browse files
authored
Merge pull request #26 from BioNGFF/feat/axis-slider
add axis sliders
2 parents 8d06596 + 7515505 commit 7a3daa0

File tree

7 files changed

+157
-23
lines changed

7 files changed

+157
-23
lines changed

pnpm-lock.yaml

Lines changed: 3 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

sites/app/package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,8 @@
1414
"dependencies": {
1515
"@biongff/viewer": "workspace:*",
1616
"react": "^18.2.0",
17-
"react-dom": "^18.2.0"
17+
"react-dom": "^18.2.0",
18+
"@mui/material": "^7.2.0"
1819
},
1920
"devDependencies": {
2021
"@types/react": "^18.2.15",

sites/app/src/App.jsx

Lines changed: 21 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,17 @@
11
import React from 'react';
2+
3+
import CssBaseline from '@mui/material/CssBaseline';
4+
import { ThemeProvider, createTheme } from '@mui/material/styles';
5+
26
import './App.css';
37
import { Viewer, parseMatrix } from '@biongff/viewer';
48

9+
const darkTheme = createTheme({
10+
palette: {
11+
mode: 'dark',
12+
},
13+
});
14+
515
function App() {
616
const url = new URL(window.location.href);
717

@@ -15,14 +25,17 @@ function App() {
1525
.map((v) => parseMatrix(v));
1626

1727
return (
18-
<>
19-
<Viewer
20-
sources={sources}
21-
channelAxis={channelAxis}
22-
isLabel={isLabel}
23-
modelMatrices={modelMatrices}
24-
/>
25-
</>
28+
<ThemeProvider theme={darkTheme}>
29+
<CssBaseline />
30+
<div className="App">
31+
<Viewer
32+
sources={sources}
33+
channelAxis={channelAxis}
34+
isLabel={isLabel}
35+
modelMatrices={modelMatrices}
36+
/>
37+
</div>
38+
</ThemeProvider>
2639
);
2740
}
2841

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
import React, { useEffect, useState } from 'react';
2+
3+
import Grid from '@mui/material/Grid';
4+
import Input from '@mui/material/Input';
5+
import Slider from '@mui/material/Slider';
6+
import Typography from '@mui/material/Typography';
7+
8+
const AxisSlider = ({ axis_labels, axisIndex, selections, max, onChange }) => {
9+
const [value, setValue] = useState(0);
10+
const axisLabel = axis_labels[axisIndex];
11+
12+
useEffect(() => {
13+
setValue(selections[0] ? selections[0][axisIndex] : 1);
14+
}, [selections, axisIndex]);
15+
16+
const setSelections = (v = value) => {
17+
onChange(
18+
selections.map((s) => {
19+
s[axisIndex] = v;
20+
return s;
21+
}),
22+
);
23+
};
24+
25+
return (
26+
<>
27+
<Grid container spacing={2} sx={{ justifyContent: 'space-between' }}>
28+
<Typography>{axisLabel}</Typography>
29+
<Input
30+
value={value}
31+
size="small"
32+
onChange={(e) => {
33+
setSelections(Number(e.target.value));
34+
}}
35+
inputProps={{
36+
step: 1,
37+
min: 0,
38+
max: max,
39+
type: 'number',
40+
}}
41+
/>
42+
</Grid>
43+
<Slider
44+
size="small"
45+
min={0}
46+
max={max}
47+
value={value}
48+
onChange={(_e, v) => setValue(v)}
49+
onChangeCommitted={() => setSelections()}
50+
/>
51+
</>
52+
);
53+
};
54+
55+
export const AxisSliders = ({
56+
axis_labels,
57+
channel_axis,
58+
loader,
59+
selections,
60+
onChange,
61+
}) => {
62+
// from vizarr AxisSliders
63+
const sliders = axis_labels
64+
.slice(0, -2) // ignore last two axes, [y,x]
65+
.map((name, i) => [name, i, loader[0].shape[i]]) // capture the name, index, and size of non-yx dims
66+
.filter((d) => {
67+
if (d[1] === channel_axis) return false; // ignore channel_axis (for OME-Zarr channel_axis === 1)
68+
if (d[2] > 1) return true; // keep if size > 1
69+
return false; // otherwise ignore as well
70+
})
71+
.map(([name, i, size]) => (
72+
<AxisSlider
73+
selections={selections}
74+
key={name}
75+
axis_labels={axis_labels}
76+
axisIndex={i}
77+
max={size - 1}
78+
step={1}
79+
onChange={onChange}
80+
/>
81+
));
82+
83+
return <>{sliders}</>;
84+
};

viewer/src/components/Controller.jsx renamed to viewer/src/components/Controller/Controller.jsx

Lines changed: 11 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -5,25 +5,18 @@ import VisibilityOffIcon from '@mui/icons-material/VisibilityOff';
55
import Checkbox from '@mui/material/Checkbox';
66
import FormControlLabel from '@mui/material/FormControlLabel';
77
import FormGroup from '@mui/material/FormGroup';
8-
import Slider from '@mui/material/Slider';
98
import Stack from '@mui/material/Stack';
109

11-
const OpactiySlider = ({ value, onChange }) => (
12-
<Slider
13-
size="small"
14-
min={0}
15-
max={1}
16-
step={0.01}
17-
value={value}
18-
onChange={onChange}
19-
/>
20-
);
10+
import { AxisSliders } from './AxisSliders';
11+
import { OpacitySlider } from './OpacitySlider';
2112

2213
export const Controller = ({
14+
sourceData,
2315
layerStates,
2416
resetViewState,
2517
toggleVisibility,
2618
setLayerOpacity,
19+
setLayerSelections,
2720
}) => {
2821
const controls = layerStates.map((layerState, index) => {
2922
if (!layerState) {
@@ -45,7 +38,12 @@ export const Controller = ({
4538
/>
4639
}
4740
/>
48-
<OpactiySlider
41+
<AxisSliders
42+
{...sourceData[index]}
43+
selections={layerState.layerProps.selections}
44+
onChange={(selections) => setLayerSelections(index, selections)}
45+
/>
46+
<OpacitySlider
4947
value={layerState.layerProps.opacity}
5048
onChange={(e, value) => setLayerOpacity(index, null, value)}
5149
/>
@@ -64,7 +62,7 @@ export const Controller = ({
6462
/>
6563
}
6664
/>
67-
<OpactiySlider
65+
<OpacitySlider
6866
value={label.layerProps.opacity}
6967
onChange={(e, value) =>
7068
setLayerOpacity(index, label.layerProps.id, value)
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
import React from 'react';
2+
3+
import Grid from '@mui/material/Grid';
4+
import Slider from '@mui/material/Slider';
5+
import Typography from '@mui/material/Typography';
6+
7+
export const OpacitySlider = ({ value, onChange }) => (
8+
<Grid container>
9+
<Typography>Opacity</Typography>
10+
<Slider
11+
size="small"
12+
min={0}
13+
max={1}
14+
step={0.01}
15+
value={value}
16+
onChange={onChange}
17+
/>
18+
</Grid>
19+
);

viewer/src/components/Viewer.jsx

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ import DeckGL, { OrthographicView } from 'deck.gl';
1919
import { Matrix4 } from 'math.gl';
2020

2121
import { useSourceData } from '../hooks';
22-
import { Controller } from './Controller';
22+
import { Controller } from './Controller/Controller';
2323
import { LabelLayer } from '../layers/label-layer';
2424

2525
const LayerStateMap = {
@@ -234,6 +234,20 @@ export const Viewer = ({
234234
}
235235
};
236236

237+
const setLayerSelections = (index, selections) => {
238+
setLayerStates((prev) => {
239+
return prev.map((state, i) => {
240+
if (i !== index) return state;
241+
return {
242+
...state,
243+
layerProps: {
244+
...state.layerProps,
245+
selections: selections,
246+
},
247+
};
248+
});
249+
});
250+
};
237251
const { near, far } = useMemo(() => {
238252
if (!layers?.length) {
239253
return { near: 0.1, far: 1000 };
@@ -279,10 +293,12 @@ export const Viewer = ({
279293
return (
280294
<div>
281295
<Controller
296+
sourceData={sourceData}
282297
layerStates={layerStates}
283298
resetViewState={resetViewState}
284299
toggleVisibility={toggleVisibility}
285300
setLayerOpacity={setLayerOpacity}
301+
setLayerSelections={setLayerSelections}
286302
/>
287303
<DeckGL
288304
ref={deckRef}

0 commit comments

Comments
 (0)