Skip to content

Commit 96aea96

Browse files
committed
Add support to assign a color to a slicer, and show indicators in that color
1 parent 4550229 commit 96aea96

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
@@ -340,9 +340,7 @@ def _create_dash_components(self):
340340

341341
# The (float) position (in scene coords) of the current slice,
342342
# used to publish our position to slicers with the same scene_id.
343-
self._pos = Store(
344-
id=self._subid("pos", True, axis=self._axis), data=initial_pos
345-
)
343+
self._pos = Store(id=self._subid("pos", True), data=initial_pos)
346344

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

503+
# todo: replace index store with this info, drop the pos store
504+
# todo: include info about axis range
505505
app.clientside_callback(
506506
"""
507507
function update_pos(index, info) {
508-
return info.origin[2] + index * info.spacing[2];
508+
return {
509+
index: index,
510+
pos: info.origin[2] + index * info.spacing[2],
511+
axis: info.axis,
512+
color: info.color,
513+
};
509514
}
510515
""",
511516
Output(self._pos.id, "data"),
@@ -579,32 +584,45 @@ def _create_client_callbacks(self):
579584

580585
app.clientside_callback(
581586
"""
582-
function update_indicator_traces(positions1, positions2, info, current) {
587+
function update_indicator_traces(states, info) {
583588
let x0 = info.origin[0], y0 = info.origin[1];
584589
let x1 = x0 + info.size[0] * info.spacing[0], y1 = y0 + info.size[1] * info.spacing[1];
585590
x0 = x0 - info.spacing[0], y0 = y0 - info.spacing[1];
586591
let d = ((x1 - x0) + (y1 - y0)) * 0.5 * 0.05;
587-
let version = (current.version || 0) + 1;
588-
let x = [], y = [];
589-
for (let pos of positions1) {
590-
// x relative to our slice, y in scene-coords
591-
x.push(...[x0 - d, x0, null, x1, x1 + d, null]);
592-
y.push(...[pos, pos, pos, pos, pos, pos]);
592+
593+
let axii = [0, 1, 2];
594+
axii.splice(info.axis, 1);
595+
let traces = [];
596+
597+
for (let state of states) {
598+
let pos = state.pos;
599+
if (state.axis == axii[0]) {
600+
// x relative to our slice, y in scene-coords
601+
traces.push({
602+
//x: [x0 - d, x0, null, x1, x1 + d, null],
603+
x: [x0, x1],
604+
y: [pos, pos],
605+
line: {color: state.color, width: 1}
606+
});
607+
} else if (state.axis == axii[1]) {
608+
// x in scene-coords, y relative to our slice
609+
traces.push({
610+
x: [pos, pos],
611+
y: [y0, y1],
612+
//y: [y0 - d, y0, null, y1, y1 + d, null],
613+
line: {color: state.color, width: 1}
614+
});
615+
}
593616
}
594-
for (let pos of positions2) {
595-
// x in scene-coords, y relative to our slice
596-
x.push(...[pos, pos, pos, pos, pos, pos]);
597-
y.push(...[y0 - d, y0, null, y1, y1 + d, null]);
617+
618+
for (let trace of traces) {
619+
trace.type = 'scatter';
620+
trace.mode = 'lines';
621+
trace.hoverinfo = 'skip';
622+
trace.showlegend = false;
598623
}
599-
return [{
600-
type: 'scatter',
601-
mode: 'lines',
602-
line: {color: '#ff00aa'},
603-
x: x,
604-
y: y,
605-
hoverinfo: 'skip',
606-
version: version
607-
}];
624+
625+
return traces;
608626
}
609627
""",
610628
Output(self._indicator_traces.id, "data"),
@@ -614,15 +632,12 @@ def _create_client_callbacks(self):
614632
"scene": self._scene_id,
615633
"context": ALL,
616634
"name": "pos",
617-
"axis": axis,
618635
},
619636
"data",
620637
)
621-
for axis in self._other_axii
622638
],
623639
[
624640
State(self._info.id, "data"),
625-
State(self._indicator_traces.id, "data"),
626641
],
627642
)
628643

@@ -631,26 +646,42 @@ def _create_client_callbacks(self):
631646

632647
app.clientside_callback(
633648
"""
634-
function update_figure(img_traces, indicators, ori_figure) {
649+
function update_figure(img_traces, indicators, ori_figure, info) {
635650
636651
// Collect traces
637652
let traces = [];
638653
for (let trace of img_traces) { traces.push(trace); }
639654
for (let trace of indicators) { traces.push(trace); }
640655
656+
// Show our own color as a rectangle around the image,
657+
// But only if there are multiple slicers with the same scene id.
658+
let shapes = [];
659+
if (indicators.length > 1) {
660+
shapes.push({
661+
type: 'rect',
662+
//xref: 'paper', yref: 'paper', x0: 0, y0: 0, x1: 1, y1: 1,
663+
xref: 'x', yref: 'y',
664+
x0: info.origin[0] - info.spacing[0]/2, y0: info.origin[1] - info.spacing[1]/2,
665+
x1: info.origin[0] + (info.size[0] - 0.5) * info.spacing[0], y1: info.origin[1] + (info.size[1] - 0.5) * info.spacing[1],
666+
line: {color: info.color, width: 3}
667+
});
668+
}
669+
641670
// Update figure
642671
let figure = {...ori_figure};
643672
figure.data = traces;
673+
figure.layout.shapes = shapes;
644674
645675
return figure;
646676
}
647677
""",
648-
Output(self.graph.id, "figure"),
678+
Output(self._graph.id, "figure"),
649679
[
650680
Input(self._img_traces.id, "data"),
651681
Input(self._indicator_traces.id, "data"),
652682
],
653683
[
654-
State(self.graph.id, "figure"),
684+
State(self._graph.id, "figure"),
685+
State(self._info.id, "data"),
655686
],
656687
)

0 commit comments

Comments
 (0)