Loon: An Interactive Statistical Visualization Toolkit
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 

Graph

Good To Know

Navigation Graphs

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.

Interaction with the Navigators

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

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

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}

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}

Substitution

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

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:

Geodesic 2d

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

Context 2d

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 "-"]

The graph can be switched as follows

set LGnot [complement {*}$LG]

$g configure {*}[interleave {-nodes -from -to -isDirected} $LGnot]

Graph Switch Widget

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

Working with the Graph Switch

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

Graph Utilities