set nodes [list A B C D E]
set G [completegraph $nodes]
set LG [linegraph {*}$G]
set g [graph -nodes [lindex $LG 0] -from [lindex $LG 1]\
-to [lindex $LG 2] -isDirected [lindex $LG 3]]
$g navigator add
A (mathematical) graph in loon
is defined by a list of node names, the from-to list of node names that define the edges and a Boolean value whether the graph is directed or not. This translates into the states nodes
, from
, to
and isDirected
.
The graph layout is defined with the x
and y
states.
Get the state names with
set states [$g info states]
dict keys $states
Query a state, say background
, as follows
$g cget -background
Change a state, say again background
, foreground
, and colorEdge
, as follows
$g configure -background gray20 -foreground gray90 -colorEdge red
When creating a graph display you may specify any state at plot creation
set nodes [list a b c]
set G [completegraph $nodes]
set LG [linegraph {*}$G]
set g1 [graph -nodes [lindex $LG 0] -from [lindex $LG 1]\
-to [lindex $LG 2] -isDirected [lindex $LG 3]] -colorEdge red
details on a state, say background
, is easily had with
set states [$g info states]
dict get $states background
and a particular field
dict get $states background description
The graph has n
dimensional states that are associated to nodes and p
dimensional states that are associated with edges. To query which states are p
dimensional use
set states [$g info states]
set pstates [dict filter $states script {k v} {
set dim [dict get $v dimension]
expr {$dim eq "p"}
}]
dict keys $pstates
To change the layout of the graph use a graph layout algorithm and set the x
and y
states of the loon
graph accordingly. loon
supports straight lines for edges only.
To turn a graph into a navigation graph you need to add one or more navigators. Navigators have their own set of states such as from
, to
and proportion
. You can create state bindings for the navigator that call a function when a navigator changes its position on the graph. States and state bindings for navigators provide the facility to implement any graph semantic. However, certain graph semantics (e.g. the default semantic with 2d projection along a geodesic path between spaces) involve lots of logic and control over plots and, hence, it makes sense to en encapsulate them. We do this by providing contexts. A context is added to a navigator and will do a specific task if the navigator's position on the graph changes.
We use the example at the beginning of this section:
set nodes [list A B C D E]
set G [completegraph $nodes]
set LG [linegraph {*}$G]
set g [graph -nodes [lindex $LG 0] -from [lindex $LG 1]\
-to [lindex $LG 2] -isDirected [lindex $LG 3]]
The following code ads a navigator to the graph g
set nav [$g navigator add -color orange]
The navigator with the id stored in the nav
has its own states that can be listed as follows
set nstates [$g navigator use $nav info states]
dict keys $nstates
The position of the navigator on the graph is defined by the states from
, to
and proportion
. The states from
and to
hold lists of node names of the graph. The proportion
state is a number between and including 0
and 1
and defines how far the navigator is between the last element of from
and the first element of to
. The to
state can also be an empty list if there is no further node to go to. Hence, the concatenation of from
and to
define a path on the graph.
The position of the navigator on the graph can be controlled programatically as follows:
$g navigator use $nav configure\
-from {A:B B:C C:D A:D} -to {D:E B:E}\
-proportion 0.2
The elements related to the navigator you see in the plot above are
proprtion
between the last node in from
and the first node in to
. If to
is empty then the navigator sits on the last node of from
.from
and the proportion
that has been traversed on the current edge.to
.The graph display supports direct interaction with the navigator and navigator path using the mouse and keyboard. To move the navigator with the mouse you must first click on it to select it which will set the activeNavigator
graph state to the navigator id and causes the navigator to be highlighted with the navigator outline in the selection color.
In this state the following interactions are possible
to=''
) then you can extend the path by dragging the navigator towards a new connected node. Note that
scrollProportionIncrement
state of the navigator.Note that the highlighting of the adjacent nodes of a navigator and the edge selection circle are mouse interaction states and have no equivalent display states. That is, they are all transient and are undone as soon as the Shift key and/or mouse button press gets released.
The animation of the navigator can also be done programatically with any of the following commands
$g navigator use $nav walk forward
$g navigator use $nav walk backward
$g navigator use $nav walk forward C:D
$g navigator use $nav walk backward B:C
$g navigator use $nav walk path {D:E B:E B:D A:D}
animationPause
and animationProportionIncrement
to control the animation speed.Navigators support state bindings. You can use state bindings to implement your custom navigation graph semantic.
We use the following graph and navigator for our example:
set nodes [list A B C D E]
set G [completegraph $nodes]
set LG [linegraph {*}$G]
set g [graph -nodes [lindex $LG 0] -from [lindex $LG 1]\
-to [lindex $LG 2] -isDirected [lindex $LG 3]]
set nav [$g navigator add]
To add a state binding use
proc my_semantic {widget navigator} {
set p [$widget navigator use $navigator cget -proportion]
set nf [lindex [$widget navigator use $navigator cget -from] end]
set nt [lindex [$widget navigator use $navigator cget -to] 0]
puts [format "do stuff: %s, %s, %s-%.2f-%s" $widget $navigator $nf $p $nt]
}
$g navigator use $nav bind state add {from to proportion}\
{my_semantic %W %nav}
The current substitutions for navigator state bindings are
argument name | substituted value |
---|---|
%W |
widget path name |
%nav |
navigator id |
%e |
events (states changed) |
%b |
binding id |
%O |
canvas path, useful for debugging |
Contexts implement standard graph semantics. Common to all contexts is that they sign up to the navigators state changes and will evaluate its (i.e. the context's) command
state. The contexts add substitution in the command evaluation that are meaningful for the particular context. Currently the following contexts are implemented:
Context2d maps every location on a 2d space graph to a list of xvars
and a list of yvars
such that, while moving the navigator along the graph, as few changes as possible take place in xvars
and yvars
, see the image:
Geodesic2d maps every location on the graph as an orthogonal projection of the data onto a two-dimensional subspace. The nodes then represent the sub-space spanned by a pair of variates and the edges either a 3d- or 4d-transition of one scatterplot into another, depending on how many variates the two nodes connected by the edge share (see Hurley and Oldford 2011). The geodesic2d
context inherits from the context2d
context.
The following code adds a geodesic2d context to a navigator:
source iris.tcl
set G [completegraph {SepalLength SepalWidth PetalLength PetalWidth}]
set LG [linegraph {*}$G]
set g [graph {*}[interleave {-nodes -from -to -isDirected} $LG]]
set nav [$g navigator add]
set con [$g navigator use $nav context add geodesic2d\
-data [dict filter $iris key Se* Pe*]]
This will open a new scatterplot showing the projection defined by the navigator location. Every navigator position change will evaluate the command in the command
state of the context. The default command state is
$g navigator use $nav context use $con cget -command
#% .l2.plot configure -x %x -y %y -xlabel %xlabel -ylabel %ylabel
where .l2.plot
is the widget path name of the newly created scatterplot. If the command
state is specified at context creation time then no scatterplot will be created. The command
state supports substitutions similar to bindings. The substitution table is
string | substituted value |
---|---|
%W |
widget path name (i.e. the graph) |
%nav |
navigator id |
%con |
context id |
%x |
x coordinates of projection |
%y |
y coordinates of projection |
%xlabel |
suitable x label for projection |
%ylabel |
suitable y label for projection |
%from |
from state of navigator |
%to |
to state of navigator |
%p |
proportion state of navigator |
The context2d has a couple of noteworthy states, use the info states
approach to learn more about them:
set statesInfo [$g use navigator $nav context use $con info states]
dict keys $statesInfo
dict get $statesInfo scaling
The context2d substitutions are
string | substituted value |
---|---|
%W |
widget path name (i.e. the graph) |
%nav |
navigator id |
%con |
context id |
%xvars |
x variables |
%yvars |
y variables |
%from |
from state of navigator |
%to |
to state of navigator |
%p |
proportion state of navigator |
If the context2d description above wasn't clear enough use the following code to get a sense of how xvars
and yvars
change.
set G [completegraph {A B C D E F G}]
set LG [linegraph {*}$G "-"]
set g [graph {*}[interleave {-nodes -from -to -isDirected} $LG]]
set nav [$g navigator add]
set con [$g navigator use $nav context add context2d\
-command {puts "%xvars to %yvars: [format %.3f %p]"} -separator "-"]
:
but it can be changed to any string in the linegraph
function and as a context2d state.The graph can be switched as follows
set LGnot [complement {*}$LG]
$g configure {*}[interleave {-nodes -from -to -isDirected} $LGnot]
Sometimes it is useful to easily switch between different graphs on a graph display. The graph switch widget maintains a list of graphs and updates the activewidget
if a graph in its list gets selected.
For this example we pack a graph switch widget next to a graph display. More on widget layouts can be read here.
set tt [toplevel .example]
wm title .example "Loon graph example with a graph switch"
set g [graph .example.graph]
set gs [graphswitch .example.graphswitch -activewidget $g]
pack $g -side left -fill both -expand TRUE
pack $gs -side left -fill y
Graphs are added to the graph switch as follows
set G1 [completegraph {A B C D}]
set G2 [list {a b c} {a a} {b c} FALSE]
set G3 [linegraph {*}$G1]
set G4 [complement {*}$G2]
set idG1 [$gs add $G1 "G1"]
set idG2 [$gs add $G2 "G2"]
set idG3 [$gs add $G3 "G3=linegraph(G1)"]
set idG4 [$gs add $G4 "complement(G3)"]
$gs set $idG3
The API of the graph switch is similar to that of the layers, except that graphs are arranged in a flat list and layers are arranged in a tree structure.
For the graph switch widget the graphs are defined as for the graph widget with a list of node names and a list with from
node names and a list with to
node names that define the edges, and a logical value whether the edges are directed or not.
set gs [graphswitch]
set graphId [$gs add <graph> <label> <index>]
where index
is the position in the list. The arguments for <label>
and <index>
are optional.
The add
method returns an id for the added graph.
Currently the activewidget
state of gs
is not set to any graph widget. Selecting a graph will throw an error saying that the graphswitch has no activewidget set. To set an activewidget (i.e. a graph widget) use
set g [graph]
$gs configure -activewidget $g
Now, to push a graph in gs
to the graph widget g
you can either mouse select a graph on the graphswitch widget or set it programmatically as follows
$gs set $graphId
We continue by adding a few more graphs in order to introduce the other graphswitch related functions.
$gs add {{A B C D} {A A A} {B C D} FALSE}
$gs add {{A B C D E} {A E A} {B C D} TRUE}
$gs add {{A B C} {A A} {B C} TRUE}
To list the ids of all graph in the graphswitch use
$gs ids
If you have followed this example then the ids
method should return a list with the ids graph0
, graph1
, graph2
and graph3
, where the order of the ids is how they appear in the graphswitch widget. To move a graph to a different position in the list do as follows
$gs move graph0 2
to move graph0
to the second last place. To reorder all graphs use
$gs reorder {graph1 graph0 graph3 graph2}
To get the label of a graph use
$gs getLabel graph1
To relabel a graph use
$gs relabel graph1 "A special graph"
To delete a graph use
$gs delete graph2
And to get the graph use
$gs get graph1