diff --git a/Procfile b/Procfile index ce1bd4f..b1eebc0 100644 --- a/Procfile +++ b/Procfile @@ -1 +1 @@ -web: gunicorn examples.slicer_with_3_views:server +web: gunicorn examples.all_features:server diff --git a/examples/all_features.py b/examples/all_features.py new file mode 100644 index 0000000..8c8997b --- /dev/null +++ b/examples/all_features.py @@ -0,0 +1,121 @@ +""" +An example that is (tries to be) a demo of all features. +""" + +import plotly.graph_objects as go +import dash +import dash_html_components as html +import dash_core_components as dcc +from dash_slicer import VolumeSlicer +from dash.dependencies import Input, Output, State, ALL +import imageio + + +app = dash.Dash(__name__, update_title=None) +server = app.server + +# Read volume +vol = imageio.volread("imageio:stent.npz") +vol = vol[::3, :, :] +spacing = 3, 1, 1 +ori = 1000, 2000, 3000 + +# Create slicer objects +slicer0 = VolumeSlicer(app, vol, spacing=spacing, origin=ori, axis=0, thumbnail=False) +slicer1 = VolumeSlicer( + app, vol, spacing=spacing, origin=ori, axis=1, thumbnail=8, reverse_y=False +) +slicer2 = VolumeSlicer(app, vol, spacing=spacing, origin=ori, axis=2, color="#00ff99") + +# Put everything together in a 2x2 grid +app.layout = html.Div( + style={ + "display": "grid", + "gridTemplateColumns": "33% 33% 33%", + }, + children=[ + html.Div( + [ + html.Center(html.H1("Axis 0")), + slicer0.graph, + html.Br(), + slicer0.slider, + *slicer0.stores, + ] + ), + html.Div( + [ + html.Center(html.H1("Axis 1")), + slicer1.graph, + html.Br(), + slicer1.slider, + *slicer1.stores, + ] + ), + html.Div( + [ + html.Center(html.H1("Axis 2")), + slicer2.graph, + html.Br(), + slicer2.slider, + *slicer2.stores, + ] + ), + html.Div( + [ + html.Center(html.H1("3D")), + dcc.Graph(id="3Dgraph", figure=go.Figure()), + ] + ), + dcc.Markdown( + """ + Take note of: + * Full-res thumbnails for axis 0. + * Very low-res thumbnails for axis 1. + * Default low-res thumbnails for axis 2. + * The `reverse_y` is false for axis 1. + * Elongated voxels for axis 1 and 2. + * An origin in the thousands, visible in the 3D view. + * A custom brighter green for axis 2. + """ + ), + ], +) + + +# Callback to display slicer view positions in the 3D view +app.clientside_callback( + """ +function update_3d_figure(states, ori_figure) { + let traces = []; + for (let state of states) { + if (!state) continue; + let xrange = state.xrange; + let yrange = state.yrange; + let xyz = [ + [xrange[0], xrange[1], xrange[1], xrange[0], xrange[0]], + [yrange[0], yrange[0], yrange[1], yrange[1], yrange[0]], + [state.zpos, state.zpos, state.zpos, state.zpos, state.zpos] + ]; + xyz.splice(2 - state.axis, 0, xyz.pop()); + let s = { + type: 'scatter3d', + x: xyz[0], y: xyz[1], z: xyz[2], + mode: 'lines', line: {color: state.color} + }; + traces.push(s); + } + let figure = {...ori_figure}; + figure.data = traces; + return figure; +} + """, + Output("3Dgraph", "figure"), + [Input({"scene": slicer0.scene_id, "context": ALL, "name": "state"}, "data")], + [State("3Dgraph", "figure")], +) + + +if __name__ == "__main__": + # Note: dev_tools_props_check negatively affects the performance of VolumeSlicer + app.run_server(debug=True, dev_tools_props_check=False) diff --git a/examples/slicer_with_3_views.py b/examples/slicer_with_3_views.py index 2462f20..c5ff1f1 100644 --- a/examples/slicer_with_3_views.py +++ b/examples/slicer_with_3_views.py @@ -17,9 +17,9 @@ # Read volumes and create slicer objects vol = imageio.volread("imageio:stent.npz") -slicer1 = VolumeSlicer(app, vol, axis=0) -slicer2 = VolumeSlicer(app, vol, axis=1) -slicer3 = VolumeSlicer(app, vol, axis=2) +slicer0 = VolumeSlicer(app, vol, axis=0) +slicer1 = VolumeSlicer(app, vol, axis=1) +slicer2 = VolumeSlicer(app, vol, axis=2) # Calculate isosurface and create a figure with a mesh object verts, faces, _, _ = marching_cubes(vol, 300, step_size=4) @@ -40,28 +40,28 @@ html.Div( [ html.Center(html.H1("Transversal")), - slicer1.graph, + slicer0.graph, html.Br(), - slicer1.slider, - *slicer1.stores, + slicer0.slider, + *slicer0.stores, ] ), html.Div( [ html.Center(html.H1("Coronal")), - slicer2.graph, + slicer1.graph, html.Br(), - slicer2.slider, - *slicer2.stores, + slicer1.slider, + *slicer1.stores, ] ), html.Div( [ html.Center(html.H1("Sagittal")), - slicer3.graph, + slicer2.graph, html.Br(), - slicer3.slider, - *slicer3.stores, + slicer2.slider, + *slicer2.stores, ] ), html.Div( @@ -102,7 +102,7 @@ } """, Output("3Dgraph", "figure"), - [Input({"scene": slicer1.scene_id, "context": ALL, "name": "state"}, "data")], + [Input({"scene": slicer0.scene_id, "context": ALL, "name": "state"}, "data")], [State("3Dgraph", "figure")], )