Skip to content

Commit 4fb417f

Browse files
committed
Add support to assign a color to a slicer, and show indicators in that color
1 parent 7d1807d commit 4fb417f

File tree

1 file changed

+64
-33
lines changed

1 file changed

+64
-33
lines changed

dash_slicer/slicer.py

Lines changed: 64 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ class VolumeSlicer:
3030
scene_id (str): the scene that this slicer is part of. Slicers
3131
that have the same scene-id show each-other's positions with
3232
line indicators. By default this is derived from ``id(volume)``.
33+
color (str): the color for this slicer.
3334
3435
This is a placeholder object, not a Dash component. The components
3536
that make up the slicer can be accessed as attributes. These must all
@@ -73,6 +74,7 @@ def __init__(
7374
axis=0,
7475
reverse_y=True,
7576
scene_id=None,
77+
color=None,
7678
):
7779

7880
if not isinstance(app, Dash):
@@ -93,9 +95,6 @@ def __init__(
9395
raise ValueError("The given axis must be 0, 1, or 2.")
9496
self._axis = int(axis)
9597
self._reverse_y = bool(reverse_y)
96-
# Select the *other* axii
97-
self._other_axii = [0, 1, 2]
98-
self._other_axii.pop(self._axis)
9998

10099
# Check and store scene id, and generate
101100
if scene_id is None:
@@ -116,6 +115,7 @@ def __init__(
116115
"size": shape3d_to_size2d(volume.shape, axis),
117116
"origin": shape3d_to_size2d(origin, axis),
118117
"spacing": shape3d_to_size2d(spacing, axis),
118+
"color": color or "#00ffff",
119119
}
120120

121121
# Build the slicer
@@ -338,9 +338,7 @@ def _create_dash_components(self):
338338

339339
# The (float) position (in scene coords) of the current slice,
340340
# used to publish our position to slicers with the same scene_id.
341-
self._pos = Store(
342-
id=self._subid("pos", True, axis=self._axis), data=initial_pos
343-
)
341+
self._pos = Store(id=self._subid("pos", True), data=initial_pos)
344342

345343
# Signal to set the position of other slicers with the same scene_id.
346344
self._setpos = Store(id=self._subid("setpos", True), data=None)
@@ -500,10 +498,17 @@ def _create_client_callbacks(self):
500498
# ----------------------------------------------------------------------
501499
# Callback to update position (in scene coordinates) from the index.
502500

501+
# todo: replace index store with this info, drop the pos store
502+
# todo: include info about axis range
503503
app.clientside_callback(
504504
"""
505505
function update_pos(index, info) {
506-
return info.origin[2] + index * info.spacing[2];
506+
return {
507+
index: index,
508+
pos: info.origin[2] + index * info.spacing[2],
509+
axis: info.axis,
510+
color: info.color,
511+
};
507512
}
508513
""",
509514
Output(self._pos.id, "data"),
@@ -577,32 +582,45 @@ def _create_client_callbacks(self):
577582

578583
app.clientside_callback(
579584
"""
580-
function update_indicator_traces(positions1, positions2, info, current) {
585+
function update_indicator_traces(states, info) {
581586
let x0 = info.origin[0], y0 = info.origin[1];
582587
let x1 = x0 + info.size[0] * info.spacing[0], y1 = y0 + info.size[1] * info.spacing[1];
583588
x0 = x0 - info.spacing[0], y0 = y0 - info.spacing[1];
584589
let d = ((x1 - x0) + (y1 - y0)) * 0.5 * 0.05;
585-
let version = (current.version || 0) + 1;
586-
let x = [], y = [];
587-
for (let pos of positions1) {
588-
// x relative to our slice, y in scene-coords
589-
x.push(...[x0 - d, x0, null, x1, x1 + d, null]);
590-
y.push(...[pos, pos, pos, pos, pos, pos]);
590+
591+
let axii = [0, 1, 2];
592+
axii.splice(info.axis, 1);
593+
let traces = [];
594+
595+
for (let state of states) {
596+
let pos = state.pos;
597+
if (state.axis == axii[0]) {
598+
// x relative to our slice, y in scene-coords
599+
traces.push({
600+
//x: [x0 - d, x0, null, x1, x1 + d, null],
601+
x: [x0, x1],
602+
y: [pos, pos],
603+
line: {color: state.color, width: 1}
604+
});
605+
} else if (state.axis == axii[1]) {
606+
// x in scene-coords, y relative to our slice
607+
traces.push({
608+
x: [pos, pos],
609+
y: [y0, y1],
610+
//y: [y0 - d, y0, null, y1, y1 + d, null],
611+
line: {color: state.color, width: 1}
612+
});
613+
}
591614
}
592-
for (let pos of positions2) {
593-
// x in scene-coords, y relative to our slice
594-
x.push(...[pos, pos, pos, pos, pos, pos]);
595-
y.push(...[y0 - d, y0, null, y1, y1 + d, null]);
615+
616+
for (let trace of traces) {
617+
trace.type = 'scatter';
618+
trace.mode = 'lines';
619+
trace.hoverinfo = 'skip';
620+
trace.showlegend = false;
596621
}
597-
return [{
598-
type: 'scatter',
599-
mode: 'lines',
600-
line: {color: '#ff00aa'},
601-
x: x,
602-
y: y,
603-
hoverinfo: 'skip',
604-
version: version
605-
}];
622+
623+
return traces;
606624
}
607625
""",
608626
Output(self._indicator_traces.id, "data"),
@@ -612,15 +630,12 @@ def _create_client_callbacks(self):
612630
"scene": self._scene_id,
613631
"context": ALL,
614632
"name": "pos",
615-
"axis": axis,
616633
},
617634
"data",
618635
)
619-
for axis in self._other_axii
620636
],
621637
[
622638
State(self._info.id, "data"),
623-
State(self._indicator_traces.id, "data"),
624639
],
625640
)
626641

@@ -629,26 +644,42 @@ def _create_client_callbacks(self):
629644

630645
app.clientside_callback(
631646
"""
632-
function update_figure(img_traces, indicators, ori_figure) {
647+
function update_figure(img_traces, indicators, ori_figure, info) {
633648
634649
// Collect traces
635650
let traces = [];
636651
for (let trace of img_traces) { traces.push(trace); }
637652
for (let trace of indicators) { traces.push(trace); }
638653
654+
// Show our own color as a rectangle around the image,
655+
// But only if there are multiple slicers with the same scene id.
656+
let shapes = [];
657+
if (indicators.length > 1) {
658+
shapes.push({
659+
type: 'rect',
660+
//xref: 'paper', yref: 'paper', x0: 0, y0: 0, x1: 1, y1: 1,
661+
xref: 'x', yref: 'y',
662+
x0: info.origin[0] - info.spacing[0]/2, y0: info.origin[1] - info.spacing[1]/2,
663+
x1: info.origin[0] + (info.size[0] - 0.5) * info.spacing[0], y1: info.origin[1] + (info.size[1] - 0.5) * info.spacing[1],
664+
line: {color: info.color, width: 3}
665+
});
666+
}
667+
639668
// Update figure
640669
let figure = {...ori_figure};
641670
figure.data = traces;
671+
figure.layout.shapes = shapes;
642672
643673
return figure;
644674
}
645675
""",
646-
Output(self.graph.id, "figure"),
676+
Output(self._graph.id, "figure"),
647677
[
648678
Input(self._img_traces.id, "data"),
649679
Input(self._indicator_traces.id, "data"),
650680
],
651681
[
652-
State(self.graph.id, "figure"),
682+
State(self._graph.id, "figure"),
683+
State(self._info.id, "data"),
653684
],
654685
)

0 commit comments

Comments
 (0)