Skip to content

Commit 8d91b7b

Browse files
committed
rename volume-id -> scene-id, and clarify example
1 parent e83af5f commit 8d91b7b

File tree

2 files changed

+79
-16
lines changed

2 files changed

+79
-16
lines changed

dash_3d_viewer/slicer.py

Lines changed: 15 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,9 @@ class DashVolumeSlicer:
1414
app (dash.Dash): the Dash application instance.
1515
volume (ndarray): the 3D numpy array to slice through.
1616
axis (int): the dimension to slice in. Default 0.
17-
volume_id (str): the id to use for the volume. By default this is a
18-
hash of ``id(volume)``. Slicers that have the same volume-id show
19-
each-other's positions with line indicators.
17+
scene_id (str): the scene that this slicer is part of. Slicers
18+
that have the same scene-id show each-other's positions with
19+
line indicators. By default this is a hash of ``id(volume)``.
2020
2121
This is a placeholder object, not a Dash component. The components
2222
that make up the slicer can be accessed as attributes:
@@ -29,16 +29,16 @@ class DashVolumeSlicer:
2929
Each component is given a dict-id with the following keys:
3030
3131
* "context": a unique string id for this slicer instance.
32-
* "volume": the volume_id.
32+
* "scene": the scene_id.
3333
* "axis": the int axis.
34-
* "name": the name of the component.
34+
* "name": the name of the (sub) component.
3535
3636
TODO: iron out these details, list the stores that are public
3737
"""
3838

3939
_global_slicer_counter = 0
4040

41-
def __init__(self, app, volume, axis=0, volume_id=None):
41+
def __init__(self, app, volume, axis=0, scene_id=None):
4242
if not isinstance(app, Dash):
4343
raise TypeError("Expect first arg to be a Dash app.")
4444
self._app = app
@@ -51,14 +51,14 @@ def __init__(self, app, volume, axis=0, volume_id=None):
5151
raise ValueError("The given axis must be 0, 1, or 2.")
5252
self._axis = int(axis)
5353
# Check and store id
54-
if volume_id is None:
55-
volume_id = hex(id(volume))
56-
elif not isinstance(volume_id, str):
57-
raise TypeError("volume_id must be a string")
58-
self.volume_id = volume_id
54+
if scene_id is None:
55+
scene_id = "volume_" + hex(id(volume))[2:]
56+
elif not isinstance(scene_id, str):
57+
raise TypeError("scene_id must be a string")
58+
self.scene_id = scene_id
5959
# Get unique id scoped to this slicer object
6060
DashVolumeSlicer._global_slicer_counter += 1
61-
self.context_id = "slicer" + str(DashVolumeSlicer._global_slicer_counter)
61+
self.context_id = "slicer_" + str(DashVolumeSlicer._global_slicer_counter)
6262

6363
# Get the slice size (width, height), and max index
6464
arr_shape = list(volume.shape)
@@ -137,7 +137,7 @@ def _subid(self, name):
137137
# todo: is there a penalty for using a dict-id vs a string-id?
138138
return {
139139
"context": self.context_id,
140-
"volume-id": self.volume_id,
140+
"scene": self.scene_id,
141141
"axis": self._axis,
142142
"name": name,
143143
}
@@ -217,7 +217,6 @@ def _create_client_callbacks(self):
217217
let x0 = 0, y0 = 0, dx = 1, dy = 1;
218218
//slice_cache[new_index] = undefined; // todo: disabled cache for now!
219219
// Maybe we do not need an update
220-
console.log(slice_size)
221220
if (!data) {
222221
data = lowres[index];
223222
// Scale the image to take the exact same space as the full-res
@@ -288,14 +287,14 @@ def _create_client_callbacks(self):
288287
y: y,
289288
hoverinfo: 'skip',
290289
version: version
291-
}
290+
};
292291
}
293292
""",
294293
Output(self._subid("_indicators"), "data"),
295294
[
296295
Input(
297296
{
298-
"volume-id": self.volume_id,
297+
"scene": self.scene_id,
299298
"context": ALL,
300299
"name": "index",
301300
"axis": axis,
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
"""
2+
An example with two slicers at the same axis, and one on another axis.
3+
This demonstrates how multiple indicators can be shown per axis.
4+
5+
Sharing the same scene_id is enough for the slicers to show each-others
6+
position. If the same volume object is given, it works by default,
7+
because the default scene_id is a hash of the volume object. Specifying
8+
a scene_id provides slice position indicators even when slicing through
9+
different volumes.
10+
"""
11+
12+
import dash
13+
import dash_html_components as html
14+
from dash_3d_viewer import DashVolumeSlicer
15+
import imageio
16+
17+
18+
app = dash.Dash(__name__)
19+
20+
vol = imageio.volread("imageio:stent.npz")
21+
slicer1 = DashVolumeSlicer(app, vol, axis=1, scene_id="myscene")
22+
slicer2 = DashVolumeSlicer(app, vol, axis=0, scene_id="myscene")
23+
slicer3 = DashVolumeSlicer(app, vol, axis=0, scene_id="myscene")
24+
25+
app.layout = html.Div(
26+
style={
27+
"display": "grid",
28+
"grid-template-columns": "40% 40%",
29+
},
30+
children=[
31+
html.Div(
32+
[
33+
html.H1("Coronal"),
34+
slicer1.graph,
35+
html.Br(),
36+
slicer1.slider,
37+
*slicer1.stores,
38+
]
39+
),
40+
html.Div(
41+
[
42+
html.H1("Transversal 1"),
43+
slicer2.graph,
44+
html.Br(),
45+
slicer2.slider,
46+
*slicer2.stores,
47+
]
48+
),
49+
html.Div(),
50+
html.Div(
51+
[
52+
html.H1("Transversal 2"),
53+
slicer3.graph,
54+
html.Br(),
55+
slicer3.slider,
56+
*slicer3.stores,
57+
]
58+
),
59+
],
60+
)
61+
62+
63+
if __name__ == "__main__":
64+
app.run_server(debug=True)

0 commit comments

Comments
 (0)