Friday, March 15, 2013

Fun with JIT, part2

Here's some more neat things I've done with my GWT JIT integration.  The code snippets here are to represent what is possible and your mileage may vary.

Dragging Objects

In many of the JIT renderings, you can drag objects around the screen and can have full control over what you allow to be dragged.  You start by getting a callback when dragging is occurring:

onDragMove : function(node, eventInfo, e) {

where the "node" is the object that is being dragged and "eventInfo" has position information.  For a Force Directed JIT graph type, to allow dragging of your objects, you simply do this inside the onDragMove function:

var pos = eventInfo.getPos();
node.pos.setc(pos.x, pos.y);
fd.plot();

By the way, "fd" is an instance of the Force Directed graph.

As you drag the object around the screen, it will be moved and the graph will be replotted to show the update.  If the object you are dragging has any edges attached to it, they will automatically be moved as well.  But what if you have relationships on your display?  What if you have a parent object that has several children connected to it and when you drag the parent, you want the children to be dragged as well.  At Plexxi, we display a RING object with Plexxi Switches connected to the ring.  If a user drags our ring object, we want the switches to go with it.  Here's how:

onDragMove : function(node, eventInfo, e) {
    var pos = eventInfo.getPos();
    if (node.getData('type') == "plexxiring") {
        node.eachAdjacency(function(a) {
            childnode = fd.graph.getNode(a.nodeTo.id);
            childnode.pos.setc(childnode.getPos().x - (node.getPos().x - pos.x),
                                          childnode.getPos().y - (node.getPos().y - pos.y));
        });

    node.pos.setc(pos.x, pos.y);
    fd.plot();

What we do is get all the nodes connected to our dragged node and move them as well.  This performs surprisingly well in our application.  You can take this further and re-curse through all your objects moving even more when a single one is dragged.

Mouseover Tool Tips

On some occasions, you may want a tooltip or a text box to show up on the screen as the user moves the mouse over your nodes.  To insure good performance, you will want to add the tooltip text to a private JSON data object so that this text is instantly available to you at any time.  If you have to go back to your database of make some other calls to get your tooltip text, you may suffer from bad performance in rendering them. 

Here's how I did Tips with my Force Directed graph:

Tips : {
    enable : true,
    type : 'Native',
    offsetX : 10,
    offsetY : 10,
    onShow : function(tip, node) {
        tip.innerHTML = node.getData('tooltip');
   },


Auto Refreshing

One of the biggest issues with any graphical application is refreshing.  Does your user have to press a refresh button?  Does that waste time retrieving data from your database, destroying whatever you are currently displaying and rebuilding the display with new data? 

By using GWT and the built-in RPC mechanism, I am able to ask my GWT Server if any data has changed and then act of it.  This is a topic for another blog....but let's say my Force Directed graph is showing 5 different types of objects on it.  I ask the GWT Server to coordinate with my backend database to keep a cache of the objects I'm displaying and if any of these objects is modified, or deleted, let me know.  When I find out something that I am displaying has changed, then I take action.  The most common events are creating or deleting a link (edge), deleting an object or changing the name of an object.  Here's how they are handled as called by my GWT code:

private native void createLink(JavaScriptObject nativeGraph, String link, String from, String to)/*-{
        if (nativeGraph.graph.getAdjacence(from, to) == null) {
            var fromNode = nativeGraph.graph.getNode(from);
            var toNode = nativeGraph.graph.getNode(to);
            var adj = nativeGraph.graph.addAdjacence(fromNode, toNode,
                            {
                                'linkUuid' : link,
                                'selected' : 'false',
                                'linkcolor' : '#FF0000'
                            });
              
        nativeGraph.plot();
    }
}-*/;

private native void deleteLink(JavaScriptObject nativeGraph, String linkUuid)/*-{
        nativeGraph.graph.eachNode(function(n) {
            n.eachAdjacency(function(a) {
                if (a.data.linkUuid == linkUuid) {
                    a.setData('alpha', '0');
                    nativeGraph.graph.removeAdjacence(a.nodeFrom.id,
                            a.nodeTo.id);

                    nativeGraph.plot();
                    return;
                }
            });
        });
    }-*/;

private native void deleteNode(JavaScriptObject nativeGraph, String nodeUuid)/*-{
        nativeGraph.graph.eachNode(function(n) {
            if (n.id == nodeUuid) {
                n.setData('alpha', 0, 'end');
               nativeGraph.removeNode(nodeUuid, {
                     type: 'fade:seq'
                    duration: 1000, 
                    hideLabels: false 
                    transition: $jit.Trans.Quart.easeOut   
               });
            }
        });
 }-*/;


private native void updateNodeName(JavaScriptObject nativeGraph, String uuid, String newName)/*-{
        var node = nativeGraph.graph.getNode(uuid);
        if (node != null) {
            node.name = newName;
            nativeGraph.plot();
        }
}-*/;

fun with JIT part3 might be in the works......enjoy.





12 comments:

  1. Hi Eric,
    in my main html I used this button to illustrate my zoom in and zoom out button. (a tags and img tags in English due to hmtl restriction here)

    "a tag href='#' onclick='plus();'> end of a tag".
    "a tag href='#' onclick='minus();'> end of a tag".

    then in my js example file I did this

    function plus()
    {

    fd.canvas.scale(1.25, 1.25);
    fd.refresh();
    }

    function minus()
    {
    fd.canvas.scale(.8, .8);
    fd.refresh();
    }

    however it doesnt do anything, what did I do wrong? Thank you so much for your help.
    -Eric .A

    ReplyDelete
  2. Two comments right off. First, are your functions getting called and second what is the value of "fd"? Inside your function plus(), add window.alert("Plus is called, fd is: " + fd);

    ReplyDelete
  3. the functions are getting called to my main js file, and fd. means the forcedDirected graph, so I call forceDirected.refresh(); it calls the graph to refresh the canvas.

    ReplyDelete
  4. I only call fd.graph.scale(1.25, 1.25); and that's it. I don't make a call to refresh.

    ReplyDelete
  5. It doesnt do anything when I just do fd.graph.scale(1.25, 1.25);
    Im wondering if my button is correct?

    ReplyDelete
  6. Make sure you don't have zooming already defined in your Navigation section. In my ForceDirected setup I have this:

    Navigation : {
    },

    with nothing in this section.

    ReplyDelete
  7. Hi Eric, I finally got my zoom funtion work. However do you know how to resize the graph canvas? I increase the width in the CSS but the graph itself won't resize. I tried doing this but it wont work.


    $(window).resize(function() {
    fd.canvas.resize($('#infovis').width(), $('#infovis').height());
    replot();
    });

    ReplyDelete
  8. Unfortunately, from what I've read, you can not change the size of the canvas. You need to destroy and recreate the force directed graph in order to change the canvas size.

    ReplyDelete
  9. Eric do you know any creative ways to scale/navigate the graph when the amount of nodes get cluttered? I have a search function currently but Im trying to find out ways to make the graph less clutter when theres many nodes and relationships, maybe like a branch out on click method. Just wanted to see how you dealt with this problem and if you have any ideas, Thanks.

    ReplyDelete
  10. Thank you for these entries Eric Schott, I look forward to more of them. Eric2, I am also looking into the same issues you are having with un-cluttering a large network. Our networks often have 100s of nodes so I'll also need to come up with something to keep the clutter to a minimum.

    ReplyDelete
  11. If you look at my Fun with JIT part 1, you'll see a screenshot showing VHost objects. The label says something like (10/40), which means this one node represents 10 VHost object with 40 virtual machines behind them. Rather than draw these extra 50 nodes on the display, we something collect up "like" objects and combine them. Next step would be to let the user "open" or "expand" these collapsed objects.

    ReplyDelete
  12. Eric are you hinting at a possible part 3 blog? where you possibly bestow us with a method of collecting like objects and expanding objects? that would be really really interesting to learn how to go about doing that!

    ReplyDelete