Friday, January 18, 2013

Fun with JIT, part1

Over the past year, I've been integrating JIT  (thejit.org) with my GWT application.  Javascript is not something for the faint of heart and personally I try to stay far away from it instead allowing  GWT to generate the Javascript for me based on my Java implementation code.  But to make use of, extend, and better control the canvas based layout tool JIT, I had to take on some Javascript.  So here's some things I did with it.  Any code snippets here should be considered pseudo-code and are for representation only.


Saving & Restoring Node Positions

The Force-Directed Graph has a built in layout algorithm that draws a graph as best as it can.  However, each time the graph is rendered, the layout algorithm produces a different layout.  We offer the user the ability to control the layout somewhat, specifying spacing between nodes and how many iterations the algorithm should use based on a complexity setting and some others:


Once the layout has been run, we then let the user drag the nodes and links around the screen to better position items to their liking.  To fully make this work, we need to save the node positions so that the next time we view the graph, we put all the nodes back into their original positions.  This also provides a big performance boost, in that we never run the layout algorithm a second time, but instead tell JIT where we want the nodes.

Save Positions:

// We store the node positions using our string object id, and the x,y values.
HashMap<String, ArrayList<Integer>> nodePositions;

// We iterate over the graph, getting the positions of each node.
mygraph.graph.eachNode(function(n) {
saveNodeData(n.id, Math.floor(n.getPos().x), Math.floor(n.getPos().y));
});

saveNodeData(String id, int x, int y)
{
    ArrayList<Integer> locations = new ArrayList<Integer>();
    locations.add(x);
    locations.add(y);
    nodePositions.put(id, locations);
}

Restore Positions:

// Lood our JSON data
nativeGraph.loadJSON(data);

// Put the nodes at the positions we want. No need to run the layout algorithm.

iterate over the saved node positions data and call this:
setNodePosition(JavaScriptObject nativeGraph, String id, int x, int y)
{
    node = nativeGraph.graph.getNode(id);
    if (node != null) {
        node.setPos(new $wnd.$jit.Complex(x,y), 'current');
        nativeGraph.plot();
    }
}


Resizing the Graph

JIT comes with some built-in functionality on many of their graphs that allows the user to scroll their mouse wheel which then causes the canvas to scale in and out, making the drawn objects grow or shrink.  I found this functionality to be somewhat difficult to control and also I wanted to control how large and how small I would allow the screen to scale to.  



I implemented two push buttons on my graph that allow the user to scale up or down as they are pressed.  One jumps 125% and the other jumps 80% which provides an even up/down scaling so that they user can always return to the default, 100%.

Scaling In:

Scaling in makes the objects larger.  Each time the user presses the "+" button, I ask JIT to:

nativeGraph.canvas.scale(1.25, 1.25);


Scaling Out:

Scaling out makes the objects smaller.  Each time the user presses the "-" button, I ask JIT to:

nativeGraph.canvas.scale(.8, .8);


Searching the Graph

Since my graph can get quite large with many of the objects off the edges of the viewable area, I implemented the ability to search for any node by name.  Once found, the node would be selected and the graph would be centered on the object, bringing the object into view for the user.  Here's how.


Find Node:

findNode(JavaScriptObject nativeGraph, String name)
{
    var p = new RegExp(name.replace("*", ".*"));
    
    nativeGraph.graph.eachNode(function(node) {
        if (p.test(node.name)) {
            // Found the node, we can get the x,y positions of this node.
    centerScreen(node.getPos().x, node.getPos().y);
}
}

centerScreen(String x, String y)
{
    // We are using a scroll panel with scroll bars as the parent of our JIT canvas
    // so we just adjust these scrollbars.
    graphContainer.setScrollLeft(((graphWidth / 2) -
(getOffsetWidth() / 2)) + x);
    graphContainer.setScrollTop(((graphHeight / 2) -
(getOffsetHeight() / 2) + y);
}

...More Fun with JIT, part2 coming soon.






2 comments:

  1. Hi,

    Does this Find Node code works for spacetree. What else I have to do with html file? Also, I need to search node which is not necessarily shown in the window screen.

    ReplyDelete
  2. I have no experience with the SpaceTree. The Find node is traversing through all nodes currently in the JIT data store (as put there by loading JSON into it) and finding the matching node(s). It should work for all view types.

    ReplyDelete