A new Canvas Element

Posted in: javascript , javascript infovis toolkit
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: 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: 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: 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 :)
Comments
blog comments powered by Disqus