All of loon
's plots can be linked so that some of their n-dimensional states are synchronized. For a linked scatterplot, the states that are synchronized by default (i.e. linked states) are the color
, size
, selected
and active
states. Two or more plots are linked if they share the same string in their linkingGroup
state.
loon
's standard linking model is fairly flexible and the user can choose
n
dimensional states should be synchronizedn
dimensional states from one display to another display.We allow only one-to-one synchronization. For example, selecting a point and have it's k
nearest neighbors automatically selected in the same display is not possible. Also, it is not possible with the standard linking mechanism to have one selected point in display A result in selecting multiple points in display B. For cases where the standard model is not sufficient it is possible to implement custom linking rules with state bindings.
The standard linking model depends on the two states linkingGroup
and linkingKey
.
Displays are linked if their linkingGroup
state contains the same string.
none
is a keyword; if linkingGroup
equals none
then the plot is not linked.For example, for
source iris.tcl
set p1 [plot -x $SepalWidth -y $PetalWidth -linkingGroup iris]
set p2 [plot -x $PetalLength -y $PetalWidth -linkingGroup iris]
set p3 [plot -x $SepalLength -y $PetalLength -linkingGroup none]
set h [histogram -x $SepalLength -linkingGroup iris]
the plots p1
, p2
, and h
are linked to each other and p3
is not linked to any other display.
The states of a plot that are linked can be queried with
$p1 getLinkedStates
Usually the linked states are color
, selected
, active
and size
, if they exist for the particular plot. Histograms, for example, have no size
state. Note that between displays only states with the same state name can be linked. To set the (n-dimensional!) linked states use
$p1 setLinkedStates {color active}
If display A
is linked with display B
then
A
and B
share the same linking group (i.e. string in the linkingGroup
state)A
and B
are synchronizedi
'th elements of the states in A
are synchronized with the i
'th elements of the same states in display B
for all i
in the intersection of the point indices of display A
and B
Hence, for the above example where n=150
(i.e. the number of data points) when selecting the i
'th point in p1
then loon
will set the selected
state for the i
'th point in p2
and h
to TRUE
, for any i
in {1,...,150}
. If a further display gets created with linkingGroup
iris
but a different value for n
, say n=4
, then only the first for elements of that plot get synchronized between that display and p1
, p2
, and h
. For example, for
set p4 [plot -x {1 2 3 4} -y {1 2 3 4} -linkingGroup iris]
any change in p4
for any of the linked states will only affect the first 4 elements in p1
, p2
and h
. Or vice versa, only the states of the first for elements in p1
, p2
and h
will ever have an effect on p4
.
It is possible to change the mapping of which elements should be synchronized between displays. The n
dimensional linkingKey
state controls which elements get synchronized between the linked displays. That is for two linked displays A and B
element i
for the linked states in display A is kept in sync with element j
for the linked states in display B if there is an i
and j
for which the i
'th element in the linkingKey
state of display A contains exactly the same string as the element j
in the linkingKey
state of display B
the linkingKey
state for a display must contain unique elements
By default, the linkingKey
contains an n
dimensional vector with 0,1,...,n-1
.
Both, the linkingGroup
state and the linkingKey
state can be changed at run time. Changing the p2
in the above example to have linkingGroup
contain the iris2
string, you can run
$p2 configure -linkingGroup iris2
now only p1
and h
are linked.
If a plot's, say display A, linkingGroup
is changed to a linking group that already has linked members, say display B, C, and D, then one must specify whether the initial synchronization of the linked states should be a push
, i.e. A > B, C, D, or a pull
, i.e. A < B, C, D. This is done with the sync
argument:
$p2 configure -linkingGroup iris2 -sync pull
The sync
argument must also be used if the linkingKey
state gets changed of a plot that has linked displays. For example, to link the points in p4
with the last 4 points in the iris data use
$p4 configure -linkingKey {146 147 148 149} -sync push
Note that using the default linkingKey
results in the fastest linking.
If more flexible linking rules are needed, one can implement state bindings.
For example, for the following displays pa
and pb
set pa [plot -x {1 2 3 4 5 6 7} -y {1 2 3 4 5 6 7} -title A]
set pb [plot -x {1 2 3 4 5} -y {1 2 3 4 5} -title B]
Assume the linking:
proc any {booleans} {
return [expr [join $booleans ||]]
}
proc pa2pb {pa pb} {
set sa [$pa cget -selected]
set sb [$pb cget -selected]
$pb configure -selected [list\
[any [lrange $sa 0 2]]\
[lindex $sa 3]\
[any [lrange $sa 4 5]]\
[lindex $sa 6]\
[lindex $sb 4]]
}
proc pb2pa {pa pb} {
set sb [$pb cget -selected]
$pa configure -selected [concat\
[lrepeat 3 [lindex $sb 0]]\
[lindex $sb 1]\
[lrepeat 2 [lindex $sb 2]]\
[lindex $sb 3]]
}
$pa bind state selected "pa2pb $pa $pb"
$pb bind state selected "pb2pa $pa $pb"
This will not end in an endless loop of evaluating state bindings as after the sequence pa2pb - pb2pa - pa2pb
or the pb2pa - pa2pb - pb2pa
the system will be in equilibrium with respect to these functions. Remember that state bindings do not get evaluated if a configure
call does not effectively change the state. If, however, the custom linking does not result in an equilibrium after the first change, then you may need to add a further variable, say busy
, to avoid multiple iterations -- or an infinite loop--. Assume the linking
set pa2 [plot -x {1 2 3 4 5 6 7} -y {1 2 3 4 5 6 7} -title "A 2"]
set pb2 [plot -x {1 2 3 4 5} -y {1 2 3 4 5} -title "B 2"]
set busy FALSE
proc a2b {pa2 pb2} {
if {!$::busy} {
set ::busy TRUE
$pb2 configure\
-selected [expr ![lindex [$pa2 cget -selected] 0]]\
-which_n 0
set ::busy FALSE
}
}
proc b2a {pa2 pb2} {
if {!$::busy} {
set ::busy TRUE
$pa2 configure\
-selected [expr ![lindex [$pb2 cget -selected] 0]]\
-which_n 0
set ::busy FALSE
}
}
$pa2 bind state selected "a2b $pa2 $pb2"
$pb2 bind state selected "b2a $pa2 $pb2"
which_n
will not be changed in the configure
call.Without the variable busy
this would end in an infinite loop one the selected
state gets changed of either display.