Monthly Archive for January, 2009

Why not Operator Overloading in JavaScript?

Disclaimer: Some of this post arguments are inspired by the great talk by Guy Steele: Growing a Language.

So I was checking out the new JavaScript spec. (ECMAScript 4) lately, and I was very surprised (and disappointed) to see that Operator Overloading wasn’t even accepted as a proposal for ECMAScript 4.
I’ve noticed last year that the proposal was denied by all browser vendors, as you can see in the ECMAScript 4 progress spreadsheet created by John Resig, under the name of “Operator Overloading”.

This is heartbreaking for me… let me explain you why.

Why Operator Overloading (in general)?

Operator Overloading is a special case of polymorphism, in which operators like +, /, *, … can be overloaded by the user.
This is very useful in languages where the user can define special types (or classes), since it allows the developer to use notation that is closer to the target domain, and allows user types to look like types built into the language.

What Guy Steele said about Operator Overloading is that:

“If we grow the language in these few ways, then we will not need to grow it in a hundred other ways; the users can take on the rest of the task.”

To see why, think about these structures:

  • 2D, 3D Vectors
  • Matrices
  • Intervals
  • Complex Numbers
  • Polar Coordinates
  • Rational Numbers

One thing all these structures have in common is that their sum and product operations have a different definition than the one we give to integers.

There’s also an interesting fact about these structures: a lot of people use them, but a lot of people don’t.
For example, Matrices might be used by OpenGL/Direct3D developers, but it’s not very common to use this structure for manipulating the DOM or building AJAX applications.

However, each of these structures must be considered in any programming language.
Adding them all into some standard library might be too much. However, sooner or later, some developer will want to use them.

Here’s the dilemma as exposed by Guy Steele:
I might say “yes” to each one of these, but it is clear that I must say “no” to all of them!“.

Operator Overloading solves this problem by giving the user the power to overload built-in operations and turn a non-domain specific language into a perfect tool for a developer’s specific needs.

By using Operator Overloading a developer can define operations on any custom type and its use will blend perfectly well into the language.

Wouldn’t be just beatifull if a and b beeing Complex Numbers, we could do this:

a + b * c

instead of this? :

add (a, multiply (b,c))

Ok, so now that I made my point about the power of operator overloading, let me explain you why I think this should be more carefully considered by the new JavaScript spec.

Why Operator Overloading in JavaScript?

To see why we should take seriously Operator Overloading in JavaScript, we first have to take a look at what happened to JavaScript in the last few years.

Only one thing happened to JavaScript in the last few years, AJAX.

The XMLHTTPRequest concept was first created by Microsoft, and wasn’t part of the web standards. They first used it in the year 2000, but a couple of years later Mozilla, Safari and Opera were also implementing it.
The standards covered that topic a lot of time after it was already implemented in all major browsers, and AJAX was already in the wild.

One strong aspect of JavaScript that was pretty important and that, IMHO, made the adoption of the “AJAX paradigm” a lot easier, is that JavaScript was already compliant with the “event-driven” programming paradigm, and the use of the XMLHTTPRequest object didn’t add anything new to this paradigm.
Just as callbacks were added to respond to DOM Element’s events, callbacks were used to handle server side answers. Although AJAX changed the way web applications were created, it did that by mantaining the event-driven programming paradigm.

Oh, another thing happened to JavaScript in the last couple of years, Canvas.

Canvas is following the same trend the XMLHTTPRequest object followed. It has been adopted by most browsers (Opera, Gecko, Webkit) and despite Internet Explorer’s efforts to not adopt this new feature, libraries already exist to make Canvas compatible with IE (to a certain point).
A couple of nice libraries that used Canvas were released last year: John Resig published Processing JS. Some other libraries that make use of canvas were also released, one of them is the JavaScript InfoVis Toolkit (my library! :) ).
The number of Canvas related stories really grew at Ajaxian, and there were also new breakthroughs by Opera and Firefox on the implementation of Canvas 3D.

There is one caveat to this Canvas thing though, it doesn’t use extensively the event-driven paradigm. Since all these Canvas implementations are related to Cairo and also take things from OpenGL and Direct3D (this is notably the case for the Canvas’ 3D API), IMO these concepts are more related to a functional programming paradigm than anything else.
I mean, 2D/3D graphics are all about geometrical operations. And geometrical operations are mathematical functions. These concepts might be harder to grasp than the “”AJAX paradigm”", and that’s why the adoption curve won’t be as high as the AJAX one, but there’s no doubt that Canvas developers are a niche.

And do you know which objects/types/classes are related to geometrical computation, Canvas, Canvas 3D, Cairo, OpenGL and Direct3D?

  • 2D, 3D Vectors
  • Matrices
  • Intervals
  • Complex Numbers
  • Polar Coordinates

That’s exactly what I mean. The next logical step to bring a easier use for Canvas related programming should be operator overloading. This is the one feature that will be extensively used by JavaScript Canvas developers, since they have to deal with points, 2D and 3D coordinates all day.

For a last example, consider an interpolation function between two complex numbers

(to.$add(from.scale(-1))).$scale(delta).$add(from)

The same thing with operator overloading would be:

from + (to - from) * delta

You would make a developer’s life happier.

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 :)