Skip to content

Commit 871252f

Browse files
committed
rename volume-id -> scene-id, and clarify example
1 parent c283fa2 commit 871252f

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)
@@ -136,7 +136,7 @@ def _subid(self, name):
136136
# todo: is there a penalty for using a dict-id vs a string-id?
137137
return {
138138
"context": self.context_id,
139-
"volume-id": self.volume_id,
139+
"scene": self.scene_id,
140140
"axis": self._axis,
141141
"name": name,
142142
}
@@ -216,7 +216,6 @@ def _create_client_callbacks(self):
216216
let x0 = 0, y0 = 0, dx = 1, dy = 1;
217217
//slice_cache[new_index] = undefined; // todo: disabled cache for now!
218218
// Maybe we do not need an update
219-
console.log(slice_size)
220219
if (!data) {
221220
data = lowres[index];
222221
// Scale the image to take the exact same space as the full-res
@@ -287,14 +286,14 @@ def _create_client_callbacks(self):
287286
y: y,
288287
hoverinfo: 'skip',
289288
version: version
290-
}
289+
};
291290
}
292291
""",
293292
Output(self._subid("_indicators"), "data"),
294293
[
295294
Input(
296295
{
297-
"volume-id": self.volume_id,
296+
"scene": self.scene_id,
298297
"context": ALL,
299298
"name": "index",
300299
"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)