Archive for the 'JavaScript information visualization toolkit (JIT)' CategoryPage 2 of 4

Force Directed Layouts

I’m currently working on three new visualizations for the JavaScript InfoVis Toolkit that will be added in version 1.2.

I started the development of these new visualizations a couple of weeks ago, and while trying to incorporate these new visualizations harmoniously into the Toolkit I’ve found myself considering new design and code abstractions I haven’t thought about before. That’s good for the Toolkit, and also good for my brain :)

One of the visualizations I’ll be incorporating in the next major release is a Force-Directed visualization. I first want to thank Marcus Cobden for donating a lot of code related to this algorithm that you can find here.

My code is based in his donation and also in a nice overview of Force-Directed layout algorithms that I’ve found here.

So what are Force-Directed Layout Algorithms?

Force-Directed Layout algorithms are graph drawing algorithms based only on information contained within the structure of the graph itself rather than relying on contextual information. The most straightforward Force-Directed algorithm uses repulsive forces between nodes and attractive forces between adjacent nodes. This physical model produces some local minima in which the graph, well, gets to a stable configuration and is drawn in an aesthetically pleasing way.

The main algorithm consists on a main loop that simulates the system for some iterations and then plots the graph. The system will consist on repulsive forces between all graph nodes and attractive forces between adjacent nodes. The force models considered correspond to Hooke’s law and Coulomb’s law.

Here’s an example with some JSON data I also use for other examples at the demos page:

Force Directed Image

There are lots of refinements and different methods for making Force-Directed Layouts. Some are just based on the model I described before, others are based on what’s called Graph Theoretic Distances: If for two adjacent nodes their ideal distance is set as d, then for nodes with shortest path distance of n their ideal layout distance should be n * d. Attractive and repulsive forces are used to make a graph system with random nodes positions get to a local minima based on these rules.

I implemented the first model I described. For each iteration the computePositionStep method is called to update all nodes positions based on attractive and repulsive forces. The formal parameters passed to the method are property which is an array of node properties which contain positions to be updated and opt which is an object containing layout options like generic repulsive and attractive force functions. $C creates a new Complex Number.

  computePositionStep: function(property, opt) {
    var graph = this.graph, GUtil = Graph.Util;
    var min = Math.min, max = Math.max;
    var dpos = $C(0, 0);
    //calculate repulsive forces
    GUtil.eachNode(graph, function(v) {
      //initialize disp
      $each(property, function(p) {
        v.disp[p].x = 0; v.disp[p].y = 0;
      });
      GUtil.eachNode(graph, function(u) {
        if(u.id != v.id) {
          $each(property, function(p) {
            var vp = v.getPos(p), up = u.getPos(p);
            dpos.x = vp.x - up.x;
            dpos.y = vp.y - up.y;
            var norm = dpos.norm() || 1;
            v.disp[p].$add(dpos
                .$scale(opt.nodef(norm) / norm));
          });
        }
      });
    });
    //calculate attractive forces
    var T = !!graph.getNode(this.root).visited;
    GUtil.eachNode(graph, function(node) {
      GUtil.eachAdjacency(node, function(adj) {
        var nodeTo = adj.nodeTo;
        if(!!nodeTo.visited === T) {
          $each(property, function(p) {
            var vp = node.getPos(p), up = nodeTo.getPos(p);
            dpos.x = vp.x - up.x;
            dpos.y = vp.y - up.y;
            var norm = dpos.norm() || 1;
            node.disp[p].$add(dpos.$scale(-opt.edgef(norm) / norm));
            nodeTo.disp[p].$add(dpos.$scale(-1));
          });
        }
      });
      node.visited = !T;
    });
    //arrange positions to fit the canvas
    var t = opt.t, w2 = opt.width / 2, h2 = opt.height / 2;
    GUtil.eachNode(graph, function(u) {
      $each(property, function(p) {
        var disp = u.disp[p];
        var norm = disp.norm() || 1;
        var p = u.getPos(p);
        p.$add($C(disp.x * min(Math.abs(disp.x), t) / norm,
            disp.y * min(Math.abs(disp.y), t) / norm));
        p.x = min(w2, max(-w2, p.x));
        p.y = min(h2, max(-h2, p.y));
      });
    });
  }

You can find more information about this algorithm in this overview of Force-Directed algorithms.
This method is called multiple times to provide a final layout, for example like this:

      for(var i=0; i < times; i++) {
        opt.t = opt.tstart * (1 - i/(times -1));
        this.computePositionStep(property, opt);
      }

Where times corresponds to the number of iterations of the simulation, and is generally > 50 and t can be thought as the global temperature of a system that gets cooler with each iteration.

Performance

Although there are a couple of methods to reduce the complexity of this algorithm, this implementation runs like O(V^3), which can be considered as really bad performance.

This is not desirable for real-time interactive visualizations, in which everlasting computation algorithms can lead to blocking browser popups like “This Script is Taking too long…” or things like that.

One way you can avoid these popups is by making incremental computations. This can be done by splitting the main algorithm that has for example 100 iterations into smaller pieces of 20 iterations each. The main iteration loop could then be changed into something like this:

  computePositions: function(property, opt, incremental) {
    var times = this.config.iterations, i = 0, that = this;
    if(incremental) {
      (function iter() {
        for(var total=incremental.iter, j=0; j<total; j++) {
          opt.t = opt.tstart * (1 - i++/(times -1));
          that.computePositionStep(property, opt);
          if(i >= times) {
            incremental.onComplete();
            return;
          }
        }
        incremental.onStep(Math.round(i / (times -1) * 100));
        setTimeout(iter, 1);
      })();
    } else {
      for(; i < times; i++) {
        opt.t = opt.tstart * (1 - i/(times -1));
        this.computePositionStep(property, opt);
      }
    }
  }

This method could be called for example like this:

    //compute positions incrementally and animate.
    fd.computePositions(property, opt, {
      iter: 20,
      onStep: function(perc) {
        Log.write(perc + '% loaded...');
      },
      onComplete: function() {
        Log.write('done');
       fd.animate();
      }
    });

And the main computation would be split into smaller parts calling on each step of the computation the onStep callback.

Graph Operations

For graph operations (Adding/Removing nodes and edges, Graph Sum and Graph Morphing) I make all iterations in the first layout and then only apply 20 iterations after some operation has been completed. Here's a video:

When (*not*) to use Force-Directed Layouts

Force-Directed Layouts can be a good choice when you're drawing general graphs and you don't have domain specific (or any other topological) information about them.
Tree structures however are a special case of graphs: this means that tree structures have more constrains and therefore there's more contextual information for drawing a Tree than for drawing a general graph.
Also, Trees are easier to grasp for the human eye than "general graphs". There's that intrinsic notion of hierarchy that can be used to draw aesthetically pleasing graph layouts. Think about the "general graph" layouts that start with the drawing of a spanning tree and then add the "extra edges" to complete the graph structure (for example here and here). The Multitrees method I mentioned a couple of posts ago was also developed to try to extract more contextual information about a graph and draw it based on a notion of partial hierarchy.

Because of this, in my opinion, it wouldn't be wise to plot Trees that have many nodes with this Force-Directed algorithm: this extra-information about trees isn't used in this layout. However, if you're planning on drawing general graphs and you don't have any other information about them, then this algorithm can detect interesting symmetries and make aesthetically pleasing drawings.

I'll keep you updated with the other visualizations I'm working on in the next posts.

HTML, SVG or Canvas Labels?

As you might know, the JavaScript InfoVis Toolkit uses the HTML5 Canvas element for plotting and animating graphs. This is all very nice, Canvas performance compared to other techniques for plotting these things (SVG for example) is by far superior. But of course, there are drawbacks.

Canvas better performance is due to the fact that there are no tracked elements: the Canvas is simply an image and you’re drawing there just like you’d be drawing something in paint. One big problem is that there’s no native possibility to add events to what’s drawn in Canvas, like a plotted node, edge or label.

As opposed to Canvas, SVG has a DOM/XML like spec: you have all these tags (<g> <text> <rect>) and each of them is just like a DOM element: you can add click event handlers, individual styling with CSS, etc.
Having to keep track of all these elements and handling a DOM-tree makes the performance of SVG not suitable for visualizing (and animating) medium to large datasets on the web.

Using HTML labels

Just like SVG, HTML is a DOM/XML-like spec, where you can add event handlers to each element. Also, every web developer knows HTML so exposing HTML labels through user-defined controller methods in the library seemed to me like a good choice. For controller methods like onCreateLabel or onPlaceLabel an HTML element is passed and the user can style or add event-handlers to it.

For example, here’s a fragment of the code used in the RGraph demo. You can see the rest of the code here:

        //Add the name of the node in the correponding label
        //and a click handler to move the graph.
        //This method is called once, on label creation.
        onCreateLabel: function(domElement, node){
            domElement.innerHTML = node.name;
            domElement.onclick = function(){
                rgraph.onClick(node.id);
            };
        },
        //Change some label dom properties.
        //This method is called each time a label is plotted.
        onPlaceLabel: function(domElement, node){
            var style = domElement.style;
            style.display = '';
            style.cursor = 'pointer';

            if (node._depth <= 1) {
                style.fontSize = "0.8em";
                style.color = "#ccc";

            } else if(node._depth == 2){
                style.fontSize = "0.7em";
                style.color = "#494949";

            } else {
                style.display = 'none';
            }

            var left = parseInt(style.left);
            var w = domElement.offsetWidth;
            style.left = (left - w / 2) + 'px';
        }

In my opinion this is a good approach, good points are:

  1. I'm using well known HTML elements.
  2. Dealing with DOM elements let's you add event handlers, individual styling and things like that.

Weak points are:

  1. I'm using a DOM tree, which means that if labels are plotted at all times I'm exhaustively updating the DOM and this might lead to performance problems.
  2. HTML is good for structuring pages, but for example you might want to apply transformations to HTML elements (like rotating labels, etc), and these aren't supported by all browsers yet.

    So one of the problems that might arise is, for example, the fact that in radial layouts labels might be occluded:

    occluded labels

Using SVG labels

So I began exploring other possibilities to create labels. For this I abstracted the Label interface I had and split it into:

  • Graph.Label.Native (for native canvas labels)
  • Graph.Label.DOM(abstract class for dom elements)
  • Graph.Label.HTML(extends the DOM interface with HTML specific stuff)
  • Graph.Label.SVG(extends the DOM interface with SVG specific stuff)

I also modified the Canvas class so you can specify the type of labels you want to use, labels:'HTML', labels:'SVG' or labels:'Native'. Default's HTML.

The same RGraph example code now would look like this:

        //Add the name of the node in the correponding label
        //and a click handler to move the graph.
        //This method is called once, on label creation.
        onCreateLabel: function(domElement, node){
            domElement.firstChild
              .appendChild(document
                .createTextNode(node.name));
            domElement.onclick = function(){
                rgraph.onClick(node.id, {
                  hideLabels: false
                });
            };
        },
        //Change some label dom properties.
        //This method is called each time a label is plotted.
        onPlaceLabel: function(domElement, node){
            var bb = domElement.getBBox();
            if(bb) {
              //center the label
              var x = domElement.getAttribute('x');
              var y = domElement.getAttribute('y');
              //get polar coordinates
              var p = node.pos.getp(true);
              //get angle in degrees
              var pi = Math.PI;
              var cond = (p.theta > pi/2 && p.theta < 3* pi /2);
              if(cond) {
                domElement.setAttribute('x', x - bb.width );
                domElement.setAttribute('y', y - bb.height );
              } else if(node.id == rgraph.root) {
                domElement.setAttribute('x', x - bb.width/2);
              }

              var thetap =  cond? p.theta + pi : p.theta;
                domElement.setAttribute('transform', 'rotate('
                + thetap * 360 / (2 * pi) + ' ' + x + ' ' + y + ')');
            }

This code does a little bit more than just plotting the label, it rotates the labels so they're not occluded:

Good points of this approach are:

  • Just like with any other DOM element, you can add event handlers.
  • You can apply transformations to labels.

Weak points:

  • Performance, for the same reasons as HTML.
  • IE does not support SVG.

Bonus good point: Google is making work SVG in IE with some open source library that works apparently the same as the ExCanvas library. Here's the Open Source project that will be presented here.

That's like the main reason why I've been considering a different approach for labels ;)

Native Canvas labels

Native Canvas labels make use of the HTML5 Canvas text API to plot labels.
Since the labels are just painted in the Canvas there's no DOM tree to update, and performance is good.
The Canvas text API has fillText, strokeText and measureText as methods. You can read more about the Canvas Text API here.

This is the code I added to the Graph.Label.Native class:

Graph.Label.Native = new Class({

    plotLabel: function(canvas, node, controller) {
        var ctx = canvas.getCtx();
        var coord = node.pos.getc(true);
        ctx.fillText(node.name, coord.x, coord.y);
    },

    hideLabel: $empty
});

A very good point about this approach is performance. Also, the code is simpler. You don't have to keep a labelContainer and update DOM labels each time you're making an animation.

Weak points are:

  • Opera does not support this feature.
  • You can't natively add event handlers to labels. I think I've seen someone do something similar for text in processing, but I'm not sure there's a good way of doing this without keeping track of the position of each label and perform a check each time a click is triggered in the canvas element.
  • I should change the way I define controller methods, in order to be able to pass a custom label object with x, y, theta, rho, width, height properties that could be modified on the fly, and then translate these changes into translate and rotate native canvas calls to be able to plot the text the way the user wants it. This seems just to damn complicated.... But I'll consider it.

Anyway, these are the methods I've found to plot labels into graphs.

Which one do you think is the best?
Do you know about any other approaches I could take to solve this problem?

Version 1.1.3

I just tagged the JavaScript InfoVis Toolkit with version 1.1.3. It’s been some time since the last release, and I wanted to use this post to make a summary of the changes and to describe some of the new features that have been added to the library.

I’ll start with the new features:

SpaceTree: SwitchAlignment

I added some new global configuration properties to the SpaceTree: align and indent.

Align sets the alignment of the tree to center, left and right:



The indent parameter sets an offset between a parent and its children when the alignment is left or right. You can also use the switchAlignment method for changing the alignment of the tree with an animation.

SpaceTree: Multiple nodes in path

I added two new methods to the SpaceTree: addNodeInPath and clearNodesInPath.
These two methods allow you to add a node to the “selected-nodes” path. When a node belongs to the “selected-nodes” path it remains always visible (as in always expanded).

I made this small video to show the feature:

SpaceTree: MultiTree

I added a SpaceTree configuration property called multitree.
If multitree=true, the visualization will search for the $orn data property in each node and display the subtrees according to their orientation.

In this example I set multitree=true and set $orn=’left’ for some nodes and $orn=’right’ for others. This way I create a partition of the tree:

I also use the setRoot method to set the clicked node as root for the visualization. This way the clicked node is centered and a centrifugal view from that node is drawn.

Bug Fixes

I’ve been fixing a couple of bugs also, most of them have to do with Treemaps:

I also want to thank Guido Schmidt for his interest in the library and work on the GWT JIT component.

There’s also been some JIT development for Grails done by Bertrand Goetzmann.

Anyway, things are looking good! :)

The JavaScript InfoVis Toolkit 1.1 is Out!

After several months of hard work I can finally announce version 1.1 of the JavaScript InfoVis Toolkit.

What’s the JavaScript InfoVis Toolkit?

The JavaScript InfoVis Toolkit provides tools for creating Interactive Data Visualizations for the Web.

What’s new in this version?

Code-Related

  • The library has been split into modules for code reuse.
  • All visualizations are packaged in the same file. You can create multiple instances of any visualization. Moreover, you can combine and compose visualizations. If you want to know more take a look at the Advanced Demos.
  • This Toolkit is library agnostic. This means that you can combine this toolkit with your favorite DOM/Events/Ajax framework such as Prototype, MooTools, ExtJS, YUI, JQuery, etc.
  • You can extend this library in many ways by adding or overriding class methods. The JavaScript InfoVis Toolkit has a robust (and private) class system, heavily inspired by MooTools’, that allows you to implement new methods in the same class without having to define any new Class extension. By creating mutable classes you can add new custom Node and Edge rendering functions pretty easily.
  • Custom visualizations are created by adding or changing Node/Edge colors, shapes, rendering functions, etc. You can also implement many controller methods that are triggered at different stages of the animation, like onBefore/AfterPlotLine, onBefore/AfterCompute, onBefore/AfterPlotNode, request, etc.
    You can also add new Animation transitions like Elastic or Back with easeIn/Out transitions.
    If you want to know more about these features please take a look at the Demos code.

As you can see, this new version has been built with four concepts/goals in mind: Modularity, Customization, Composition and Extensibility. I already explained some of these things in the previous post.

Hope you enjoy it.

More about the JavaScript InfoVis Toolkit 1.1

I’ve been putting a lot of effort in the upcoming version of the JavaScript InfoVis Toolkit lately, and I though it would be a good idea to show some of the new features I came up with.

The new version isn’t finished yet, but I’ve come pretty far and wanted to make a sort of checkpoint for the things I’ve done, the things I’ll be doing and the things I’m thinking about doing.

So what have I been working on?

  • A new project page design.
  • A complete documentation. I made an API documentation that is also mixed with some narrative documentation for each Class, so you can learn how the visualizations are implemented and how to use them.
  • Packaging All visualizations will be packaged in the same file, and you’ll be able to make your own build based on what you’re going to use (Treemaps, Hypertrees, RGraphs, Spacetrees or any combination of those). This is a cool aspect of making modular code.
    The other good thing about modular code is that the size of the full package will drop in ~30% compared to version 1.0.8a
  • Examples I’ve been coding some new visualization examples that will be packaged with the library. Some of them are very similar to the ones found in 1.0.8a, but adapted for version 1.1. Other examples show some of the new features of the library, and others try to expose some features of version 1.0.8a that were not properly documented.

Library features

I’ve been building this library with four things in mind:

  • Extensibility The library has multiple access points where it can be extended in different ways. For example, all main Classes are mutable objects, so you can extend or implement any method of any class in-place, like for example re-implement the nodes coloring method in the Squarified treemap:

       //TM.Strip, TM.SliceAndDice also work
       TM.Squarified.implement({
         'setColor': function(json) {
           return json.data.$color;
         }
       });
    

    …or adding new node/edge plotting methods in the Hypertree, ST or RGraph:

    Hypertree.Plot.NodeTypes.implement({
      'my-node-rendering-method': function(node, canvas) {
        //implement node rendering here
      }
    });
    

    etc.

  • Customization The library provides many ways for customizing the visualizations. There are controller methods that determine the behavior of the visualization, and configuration parameters like node and edge types, color and dimensions. Node shapes can be square, rectangle, circle, ellipse, etc. and edge shapes: line, hyperline and arrow. I also added transition effects like Quart, Bounce, Elastic, Back, etc. for the animations.
  • Modularity As explained above, the code has been divided into modules, providing a way for making custom builds of the library. Modularity also takes care of namespacing: I only add Classes that are meant to be accessed by the user and I don’t pollute the window object with unnecessary global objects.
  • Composition A major improvement in this version is that all visualizations can co-exist in the same namespace. That means that multiple instances of different visualizations can be used and composed to make new visualizations. I haven’t explored this feature of the library yet, but this would mean that for example I can make a Treemap that has Hypertrees rendered as leaves, or a Spacetree that has Treemaps as nodes, or… well, any other combination of things.

Examples

As you might know, I don’t have the most suited computer for making screencasts, so sorry if you see some performance problems.

This is a short video I made of a RGraph example.

The main idea behind this example is Customization.
That can be seen for example in the different node types, edge types and colors used, as well as in the Elastic transition effect for the animation.

This is just an example to expose as much features as I can in one visualization, so don’t take this as a “useful” visualization example please.

Here is another short video: it illustrates how Graph Operations can be made with the Hypertree visualization.

You’ll see 4 consecutive operations:

  1. Removing a subtree The bottom right subtree will be removed with an animation.
  2. Removing edges Edges from the top left subtree will be removed with an animation.
  3. Adding a graph A graph will be added with an animation
  4. Morphing The graph will transform into another graph -with an animation

Enjoy.

JavaScript InfoVis Toolkit 1.1 Preview

In case you’re wondering what I’m up to…

I’ve been adding more features to the JavaScript InfoVis Toolkit, to be released I-don’t-know-when-yet (still a lot of work to do regarding documentation, hosting, scripts, etc.).

Anyway, this video shows only some of the features to be included:

  • Custom nodes: built-in shapes are none, circle, square, rectangle, ellipse, among others
  • Custom edges: built-in shapes are none, line, quadratic, bezier, arrow, among others
  • Custom Animations: linear, Quart, Bounce, Elastic, Back, etc.
  • Change tree orientation: already possible in 1.0.8a.

Unfortunately my video card isn’t very good, so the video quality and fps aren’t as good as I’d wanted.
Animations are pretty smooth though, as you can see for yourself, so don’t blame the library, blame my computer!

Anyway, here’s the video:

Another cool thing is that you can also create custom node and edge rendering functions :)

Stay tuned, there are more features to come!

Cushion Treemaps

Remember Treemaps?

There was a thread at the Google Group for the JIT asking for Gradients in Treemaps.

Actually they’re called cushion treemaps, and they have been created by Ph.Ds Jarke J. van Wijk and Huub van de Wetering. Cushion Treemaps have been used in successful applications like SequoiaView and companies like MagnaView are building very interesting visualizations with cushion treemaps.

The paper is quite interesting: cushion treemaps were created by using shading to show a tree’s structure:

“How can we use shading to show the tree structure? The human visual system is trained to interpret variations in shade as illuminated surfaces [6]. Hence, we can answer the question by constructing a surface which shape encodes the tree structure.”

Shadowing is created by adding bumps to rectangles.

In the JavaScript InfoVis Toolkit, this can be done by overriding the leafBox method of the Treemap class. This method renders a leaf node (nodes which are generally colored in the Treemap).
By adding an image of a radial gradient inside that div we can emulate cushion treemaps.

Instead of creating a new class that extends TM and overrides that method, we can take advantage of JavaScript’s object mutability feature and re-implement the method in the same class.


    TM.Squarified.implement({
       leafBox: function(json, coord) {
        var config = this.config;
        var backgroundColor = config.Color.allow && this.setColor(json),
        offst = config.offset,
        width = coord.width - offst,
        height = coord.height - offst;
        var c = {
         'top': (offst / 2) + "px",
         'height':height + "px",
         'width': width + "px",
         'left': (offst / 2) + "px"
        };
        if(backgroundColor) c['background-color'] = backgroundColor;
       //Add an image to our leaf node to create a cushion treemap.
        var img = "<img src='gradient.png' style='position:absolute; z-index:2; top:0; left:0; width:" + c.width + "; height:"+ c.height +"'; />"; 

        return "<div class='leaf' style=" + this.toStyle(c) + ">" + img + json.name + "</div>";
       }
    });

And that’s it, now we have Squarified Cushion Treemaps. I must say that I love cushion treemaps, they look a lot more cool than simple treemaps.

I made a small POC where you can enable/disable the cushion feature, among other interesting things:

cushion treemap

A new Canvas Element

A couple of days ago I released version 1.0.8a of the JavaScript InfoVis Toolkit, that introduces some API changes and nice features.
This version focus mainly on two things:

  • A more stable and usable Canvas class.
  • Finishing last features included in the Spacetree, Treemap and RGraph InfoVis papers.

I’m quite happy with this version of the library, since it implements all lasting features in my TODO list.
This doesn’t mean much for the library, since I’m still having lots of ideas for next releases, but at least I finished something I put up to and that makes me happy :) .

Canvas

I implemented a new Canvas class, focusing on performance and usability.

The Canvas class is more like a Canvas Widget, since it creates a cross-browser canvas tag and a label div container, wrapped in a main div element.
This way, labels are relative to the canvas element and not absolute positioned, like they were on previous versions. I’d like to thank the people in this thread for providing nice ideas for implementing the Canvas class.

This canvas class makes also the Spacetree visualization cross browser, working perfectly well in IE6+.

Prior to version 1.0.8a, you had to put a canvas tag and a div label container in your html to create a new visualization. From version 1.0.8a this is no longer needed: you just have to include a visualization div container, like this:

<div id="infovis"></div>

A simple canvas instantiation could be something like this:

      //Create a new canvas instance.
      var canvas = new Canvas('mycanvas', {
         //Where to inject canvas. Any HTML container will do.
         'injectInto':'infovis',
         //Set width and height, default's to 200.
         'width': 900,
         'height': 500,
        //Set canvas styles.
        'styles': {
            'fillStyle': '#ccb',
            'strokeStyle': '#ccb'
        }
      });

The first parameter in the canvas constructor is the id of the canvas widget.
This id will be the main wrapper div id, and it will serve as prefix for the ids of the other DOM elements created.
The second parameter is a canvas configuration object. Some of the object’s properties are:

  • injectInto: The id of the div where you want to inject the canvas widget
  • width, height: Width and height of the canvas widget. Default’s to 200
  • styles: an object containing specific canvas styles. If you want to know more about canvas styles you can read this article.

The html generated by this call will be appended in the div container (#infovis) previously defined:

<div id="infovis">
  <div id="mycanvas" style="position:relative;">
    <canvas id="mycanvas-canvas" width=900 height=500
    style="position:absolute; top:0; left:0; width:900px; height:500px;" />
    <div id="mycanvas-label"
    style="overflow:visible; position:absolute; top:0; left:0; width:900px; height:0px">
    </div>
  </div>
</div>

Notice how the Canvas id is used as the id of the main div container and also as prefix for the actual canvas element and the div label container element.
If we were using the Spacetree in IE, we could use an extra backgroundColor parameter as IE hack, since excanvas does not support clipping paths, which are used by the Spacetree visualization:

      //Create a new canvas instance.
      var canvas = new Canvas('mycanvas', {
         //Where to inject canvas. Any HTML container will do.
         'injectInto':'infovis',
         //Set width and height, default's to 200.
         'width': 900,
         'height': 500,
         //Set a background color in case the browser
         //does not support clearing a specific area.
        'backgroundColor': '#222',
        //Set canvas styles.
        'styles': {
            'fillStyle': '#ccb',
            'strokeStyle': '#ccb'
        }
      });

We can define also a background Canvas.
Take for example the RGraph example, in which we plot concentric circles as background for the visualization.
Prior to version 1.0.8a, the background was rendered at each frame, since at each frame of an animation the canvas is fully cleared to plot the graph’s next position. This wasn’t very good for performance.

Defining a background canvas was the sanest choice. That way the background is rendered only once:

  //Create a new canvas instance.
  var canvas = new Canvas('mycanvas', {
    //Where to inject the canvas. Any div container will do.
    'injectInto':'infovis',
    //width and height for canvas. Default's to 200.
    'width': 900,
    'height':500,
    //Canvas styles
    'styles': {
        'fillStyle': '#ccddee',
        'strokeStyle': '#772277'
    },
    //Add a background canvas for plotting
    //concentric circles.
    'backgroundCanvas': {
        //Add Canvas styles for the bck canvas.
      'styles': {
            'fillStyle': '#444',
            'strokeStyle': '#444'
        },
        //Add the initialization and plotting functions.
        'impl': {
            'init': $empty,
            'plot': function(canvas, ctx) {
                var times = 6, d = Config.levelDistance;
                var pi2 = Math.PI*2;
                for(var i=1; i<=times; i++) {
                    ctx.beginPath();
                    ctx.arc(0, 0, i * d, 0, pi2, true);
                    ctx.stroke();
                    ctx.closePath();
                }
            }
        }
    }
 });

The background canvas created will have mycanvas-bkcanvas as id.
For more information about the canvas class you can check its object reference, the examples provided with the library and the updated quick tutorials which you can find in the documentation.

Treemap

I implemented the Strip layout for the Treemap, in addition to the Squarified and Slice and Dice layout algorithms provided in previous versions of the library.
I updated the treemap example to impement different tiling algorithms. Use the dropdown box at the left of the screen to change the current layout.

Why another tiling algorithm?
Well, as the Wikipedia explains:
To create a treemap, one must define a tiling algorithm, that is, a way to divide a rectangle into sub-rectangles of specified areas. Ideally, a treemap algorithm would create rectangles of aspect ratio close to one; would preserve some sense of the ordering of input data; and would change only slowly when the underlying data changes slowly. Unfortunately, these properties have an inverse relationship.

The Strip tiling algorithm provides a good compromise between order, stability and aspect ratio values.
More precisely, the three techniques implemented in the JIT can be classified as follows:

  • Slice and Dice: Ordered, very high aspect ratios, stable
  • Squarified: Unordered, lowest aspect ratios, medium stability
  • Strip: Ordered, medium aspect ratios, medium stability

So the Strip algorithm is a good complement to the tiling algorithms provided.

Spacetree

I implemented two new layouts for the Spacetree, bottom and right layouts.
You can change the Spacetree layouts by using the dropdown box at the left of the visualization.

The bottom layout could be pretty useful for making family trees or things like that :) .
Anyway, another good thing of the Spacetree is that it works for IE6+ now (thanks to the new Canvas implementation).
Some cleanup regarding the plotting algorithms and how labels were created was done, please check the ST quick tutorial to understand exactly what changed.

RGraph

I implemented the second animation constraint mentioned in the RGraph paper: child ordering.
This constraint decreases edge crossing during animations, making animations more intuitive and graspable by the viewer.
The child ordering constraint consists in mantaining child ordering for the parent of the clicked node, that way we can decrease the edge crossing cases during animations:

I didn't make a new example for this, but you should see the difference when comparing it with the examples packaged in previous versions of the library.

Hypertree

I did some Cleanup of the Hypertree code, and stripped off the Mouse class and the prepareCanvasEvents method.
Those kind of things can be easily implemented with DOM/AJAX frameworks like Mootools or JQuery.
The hypertree example packaged with the library shows a possible workaround for prepareCanvasEvents:

  //optional: set an "onclick" event handler on the canvas tag to animate the tree.
  var mycanvas = $('mycanvas');
  var size = canvas.getSize();
  mycanvas.addEvent('click', function(e) {
    var pos = mycanvas.getPosition();
    var s = Math.min(size.width, size.height) / 2;
    ht.move({
      'x':  (e.page.x - pos.x - size.width  / 2) / s,
      'y':  (e.page.y - pos.y - size.height / 2) / s
    });
  });

Anyway, that's all for now. Please feel free to file bugs if you spot one.
Remember also that the main project page has links to documentation, google groups, browser support, updated examples and some other things :)

Thank you Platform Computing!

A couple of weeks ago -before the 1.0.7a release- I got an e-mail from Fanbin Kong, a Platform Computing software developer, reporting a few bugs on the Treemap visualization.
These bugs mainly had to do with memory leaks and rendering performance.
Together with Fanbin Kong and Yi Zheng we tried to fix these bugs, and came up with a new algorithm that increases rendering performance and solves (almost) all memory leak issues.
The changes were applied on version 1.0.7a of the JavaScript Infovis Tookit.

The other good news is that they gave me an ipod touch for this!

ipod

They also wrote an inscription at the back of the ipod: “Appreciation to Nico. A gift from Platform”.

Thanks a lot guys, it really feels good to know that you appreciate my work :)

Visualizing Linux package dependencies

I’ve been building a Linux package dependency visualizer with Python and the JavaScript Infovis Toolkit that gathers all dependencies for a linux package and displays them in an interactive tree visualization.

So, let’s say your query is wine and you want to see dependencies for that package. The visualization will display wine as the centered node, laying its dependencies on outer concentric circles like this:

rg1

By clicking on xbase-clients you’ll set this node as root:

rg2

Then, the visualization will query for xbase-clients dependencies, morphing its state into the new node’s perspective:

rg3

You can play with the example here.

I’ll explain how to build this in case you want your own at home.
I guess this is going to be also a nice tutorial on how to configure the RGraph visualization to run advanced examples, including the new morphing animations in version 1.0.7a.

Server Side

Server side we need to build a service that can transform the apt-rdepends output for package dependencies into a JSON tree structure.

The apt-rdepends is a linux tool (which you can install with apt-get install apt-rdepends) that displays a hierarchy of package dependencies for a given package. Here’s an example when querying for erlang:

cmd1

You can either use popen2 or commands.getoutput to fetch the output for a system call in Python, I’ll do the latter.
The main function that makes the system call and returns the answer could be something like this:

def get_dependency_tree(package=''):
    out = commands.getoutput("apt-rdepends " + package).split("\n")
    ans = []
    #if dependencies were found for this package.
    if len(out) > 3 and out[3].strip() == package:
        ans = out[3:]
    else:
        ans = [package]
    return make_tree(package=ans[0].strip(), source=ans, level=2)

The make_tree function will create the tree structure that will then be serialized into JSON to be processed client side.

We will first need a make_tree_node function that creates a tree node structure from a package’s name:

#returns a tree node
def make_tree_node(id, node_name):
    node_name = node_name.strip()
    return {
            'id': id,
            'name': node_name,
            'children': [],
            'data': []
    }

As you can see, this is the same tree node as the JSON tree structure defined for the JIT:

var json = {
	"id": "aUniqueIdentifier",
	"name": "usually a nodes name",
	"data": [
	    {key:"some key",       value: "some value"},
		{key:"some other key", value: "some other value"}
	],
	children: [/* other nodes or empty */]
};

Our make_tree function will receive as formal parameters the root package, the response from the apt-rdepends call, an integer that will specify the max depth for the tree (in case we want to prune it to some level) and an id prefix that will be set for each node:

def make_tree(package='', source=[], level=1, prefix=''):
    node = make_tree_node(package + '_' + prefix, package)
    if level > 0:
        deps = get_package_deps(package, source)
        [node['children'].append(make_tree(elem, source, level -1, package)) for elem in deps]
    return node

As you can see, make_tree recursively creates nodes and appends them to their parent children property.

Finally, I also made a get_package_deps function that retrieves all children for a given package, parsing source:

def get_package_deps(package_name='', source=[]):
    ans, found_package_name = [], False
    #test if is a dependency line
    dependency = lambda package: package.strip().startswith('Depends:')
    for line in source:
        #package name line
        if not found_package_name and package_name == line.strip():
            found_package_name = True
        #it's a package dependency, add its name to the answer
        elif found_package_name and dependency(line):
            ans.append(line.split("Depends: ")[1].split("(")[0].strip())
        #end of dependency lines
        elif found_package_name and not dependency(line):
            return ans
    return ans

If you used Django, then you could expose your service in the views.py file like this:

def apt_dependencies(request, mode, package):
    json = aptdependencies.get_dependency_tree(package)
    json_string = simplejson.dumps(json)
    return render_to_response('raw.html', { 'json' : json_string })

Client Side

All the JavaScript Infovis Toolkit visualizations are customizable via controller methods.
If this is the first time you use this library, perhaps it would be better to start with the RGraph quick tutorial first.

First we define a simple Log object, that will write the current state of the graph to a label (like loading… or stuff like that).

I’ll use Mootools, but you can use whatever you want.

var Log = {
	elem: false,
	getElem: function() {
		return this.elem? this.elem : this.elem = $('log');
	},

	write: function(text) {
		var elem = this.getElem();
		elem.set('html', text);
	}
};

Then we can define an init function, that instanciates the RGraph object and returns it.
We will pass a controller to this object, that implements the onBeforeCompute, onAfterCompute, onPlaceLabel and onCreateLabel methods.
I’ll also define some utility methods, like requestGraph and preprocessTree:

function init() {
  //Set node radius to 3 pixels.
  Config.nodeRadius = 3;

  //Create a canvas object.
  var canvas= new Canvas('infovis', '#ccddee', '#772277');

  //Instanciate the RGraph
  var rgraph= new RGraph(canvas,  {
	//Here will be stored the
	//clicked node name and id
  	nodeId: "",
  	nodeName: "",

  	//Refresh the clicked node name
	//and id values before computing
	//an animation.
	onBeforeCompute: function(node) {
  		Log.write("centering " + node.name + "...");
		this.nodeId = node.id;
  		this.nodeName = node.name;
  	},

  //Add a controller to assign the node's name
  //and some extra events to the created label.
  	onCreateLabel: function(domElement, node) {
  		var d = $(domElement);
  		d.setOpacity(0.6).set('html', node.name).addEvents({
  			'mouseenter': function() {
  				d.setOpacity(1);
  			},
  			'mouseleave': function() {
  				d.setOpacity(0.6);
  			},
  			'click': function() {
				if(Log.elem.innerHTML == "done") rgraph.onClick(d.id);
  			}
  		});
  	},

	//Once the label is placed we slightly
	//change the positioning values in order
	//to center or hide the label
  	onPlaceLabel: function(domElement, node) {
		var d = $(domElement);
		d.setStyle('display', 'none');
		 if(node._depth <= 1) {
			d.set('html', node.name).setStyles({
				'width': '',
				'height': '',
				'display':''
			}).setStyle('left', (d.getStyle('left').toInt()
				- domElement.offsetWidth / 2) + 'px');
		}
	},

	//Once the node is centered we
	//can request for the new dependency
	//graph.
	onAfterCompute: function() {
		Log.write("done");
		this.requestGraph();
	},

	//We make our call to the service in order
	//to fetch the new dependency tree for
	//this package.
   	requestGraph: function() {
  		var that = this, id = this.nodeId, name = this.nodeName;
  		Log.write("requesting info...");
  		var jsonRequest = new Request.JSON({
  			'url': '/service/apt-dependencies/tree/'
					+ encodeURIComponent(name) + '/',

  			onSuccess: function(json) {
  				Log.write("morphing...");
				//Once me received the data
				//we preprocess the ids of the nodes
				//received to match existing nodes
				//in the graph and perform a morphing
				//operation.
  				that.preprocessTree(json);
				GraphOp.morph(rgraph, json, {
  					'id': id,
  					'type': 'fade',
  					'duration':2000,
  					hideLabels:true,
  					onComplete: function() {
						Log.write('done');
					},
  					onAfterCompute: $empty,
  					onBeforeCompute: $empty
  				});
  			},

  			onFailure: function() {
  				Log.write("sorry, the request failed");
  			}
  		}).get();
  	},

	//This method searches for nodes that already
	//existed in the visualization and sets the new node's
	//id to the previous one. That way, all existing nodes
	//that exist also in the new data won't be deleted.
 	preprocessTree: function(json) {
  		var ch = json.children;
  		var getNode = function(nodeName) {
  			for(var i=0; i<ch.length; i++) {
  				if(ch[i].name == nodeName) return ch[i];
  			}
  			return false;
  		};
  		json.id = rgraph.root;
		var root = rgraph.graph.getNode(rgraph.root);
  		GraphUtil.eachAdjacency(root, function(elem) {
  			var nodeTo = elem.nodeTo, jsonNode = getNode(nodeTo.name);
  			if(jsonNode) jsonNode.id = nodeTo.id;
  		});
  	}

  });

  return rgraph;
}

I did say advanced example.
You can always go to a simpler example to begin here.

Finally we have to initialize the visualization when the page loads, so we’ll attach an initialization function like this:

window.addEvent('domready', function() {
	var rgraph = init();
	new Request.JSON({
	  	'url':'/service/apt-dependencies/tree/wine/',
	  	onSuccess: function(json) {
			  //load wine dependency tree.
			 rgraph.loadTreeFromJSON(json);
			  //compute positions
			  rgraph.compute();
			  //make first plot
			  rgraph.plot();
			  Log.write("done");
			  rgraph.controller.nodeName = name;
	  	},

	  	onFailure: function() {
	  		Log.write("failed!");
	  	}
	}).get();

HTML and CSS

These are the HTML and CSS files I used to make this example/tutorial.
The HTML:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<title>

Linux package dependency visualizer

</title>
<link type="text/css" href="/static/css/style.css" rel="stylesheet" />
<script type="text/javascript" src="/static/js/mootools-1.2.js"></script>

<!--[if IE]>
<script language="javascript" type="text/javascript" src="/static/js/excanvas.js"></script>
<![endif]-->
<script language="javascript" type="text/javascript" src="/static/js/core/RGraph.js"></script>
<script language="javascript" type="text/javascript" src="/static/js/example/example-rgraph.js"></script>

</head>

<body onload="">

<canvas id="infovis" width="900" height="500"></canvas>
<div id="label_container"></div>

</body>
</html>
<div id="log"></div>

Note: You’ll probably have to change the path to the CSS and JavaScript files.

and the CSS file:

html,body {
	width:100%;
	height:100%;
	margin:0;padding:0;
	background-color:#333;
	text-align:center;
	font-size:0.94em;
	font-family:"Trebuchet MS",Verdana,sans-serif;
}

#infovis {
	width:900px;
	height:500px;
	background-color:#222;

}

.node {
	color: #fff;
	background-color:#222;
	font-weight:bold;
	padding:1px;
	cursor:pointer;
	font-size:0.8em;
}

.hidden {
	display:none;
}

Remarks

Although still in alpha, the JavaScript Infovis Toolkit can be used to perform advanced animations, customizing your visualization via a controller and not messing with the code.
This example also shows that it can be used to do more advanced things that only plotting static animations, interacting with services and handling pretty well visualizations where the dataset changes over time.
You can download the library here, latest version is 1.0.7a.
You can also go to the main project page to know more.

Hope it was useful.
Feel free to post any comment or questions.
Bye!