Getting layers to work with SVG

At a high level, layers allows you to define a set of graphs where node and edge positioning stays constant but individual nodes and edges may not be visible in any graph within the set. (whew, the Graphviz documentation explains this better)
While Graphviz’s SVG output supports layers as best it can, SVG only shows you the “first” graph/layer (think of it as the graph on the top page of a stack of pages).
I’ve dusted off some javascript that in theory can be added to any SVG output file that contains layers to add the ability to see each layer, one-at-a-time.
Three examples:
https://cdn.statically.io/gist/steveroush/23478aefac88d1f15e10529202ff4832/raw/58d980330904c46cb71a016f5ab6819c8970e3da/Layer3.svg
https://cdn.statically.io/gist/steveroush/aedb1c04acddc1311b2d8d4bd72d4e46/raw/e4e08dca72b0cc4a1e5d7414c69d051f761572e1/unixLayers.svg
https://cdn.statically.io/gist/steveroush/3ca7a088b8bc08eb6ab09e6d10e7d36d/raw/ba5491b66dcd0b7bda15d0c63e61dd42b5d9b482/SLshow.svg

Questions:

  • Does anybody care?

  • Is there a better user interface

  • What features are missing?

My javascript skill is mediocre (at best) and antiquated, so improvements are appreciated.

Q1: yes
Q2: yes, buttons to play, pause, stop, forward, backward, repeat etc and a slider for speed.
Q3: If you provide the DOT source code, I can give you a hint on what d3-graphviz could provide.

Do you have a preference for how this should be used?

  1. As it is now, i.e. serve a self-contained application embedded in the SVG from somewhere. This means the user must prepare the SVG offline (inlcuding embedding the JavaScript) and upload it somewhere and then link to it from somewhere else.
  2. A stand-alone web application where the user supplies the DOT source code and gets this new functionality. This could be built into the Graphviz Visual Editor.
  3. A library where the user can supply the DOT source code and gets the SVG rendered and some support functions to easily produce this new functionality, but add their own user interface and build their web own application? This could be built in to d3-graphviz.
  4. Directly generate the animated transition from the DOT source code in a post here on the forum. This could be built into the D3 Graphviz Discourse Theme Component that I’m currently developing.
  5. Something completely different.

I guess I’m asking what your ultimate use case is.

I like buttons & sliders, but they are kind of a pain in SVG. However if embedding the SVG inside HTML is OK, then those controls are easy.

One ultimate use case is adding an improved version of the javascript to Graphviz’s SVG generator gvrender_core_svg.c as an option or default whenever the input included layers.

Here is the javascript (blush) - just add it to the SVG output just before the last line:

<script type="text/javascript" >
<![CDATA[ 
                                               	
  var forward=1;
   var backward=-1;
   var intervalMilliseconds=0;       					
   var timer;       					
   var currId=-1; 
   var oldId;      					
   var layer = new Array();   

   window.addEventListener("load", function() {
     //alert("loaded");
     initialize();
     showHelp();
   });

  document.onkeydown = function(e) {
    switch (e.keyCode) {
        case 37:
            direction=backward;
            break;
        case 38:
            intervalMilliseconds-=1000;
            changeSpeed();
            break;
        case 39:
            direction=forward;
            break;
        case 40:
            intervalMilliseconds+=1000;
            changeSpeed();
            break;
        default:
            showHelp();
            break;
    }
  };

   function showHelp(){
     //alert('up arrow:  \tfaster\ndown arrow:\tslower\nleft arrow:\tbackward\nright arrow:\tforward');
     alert('left arrow:\tbackward\nright arrow:\tforward\nup arrow:  \tfaster\ndown arrow:\tslower');
   }

   function initialize(){                            	
     // copy the "live" nodelist into a real array 
     var layerList = document.getElementsByClassName('layer');
     for (var i =0;i<layerList.length;i++){
       layer.push(layerList[i]);
     }
     // make all layers invisible, except the 1st
     for (var i=1;i<layer.length;i++){ 	                        
       target=layer[i]; 
       target.setAttribute("display", "none");
     }                                                      

     currId=0;
     intervalSeconds=4;
     direction=forward;
     intervalMilliseconds=intervalSeconds*1000;        
     timer=setInterval("show()",intervalMilliseconds);     
   }                                                      
      	                                                        
   function changeSpeed(){
     clearInterval(timer);
     timer=setInterval("show()",intervalMilliseconds);     
   }
   function show(){
 
     oldId=currId;                                                      
     if (direction==forward){	                                        
       if (currId==layer.length - 1) {                           
         //currId=0; 
         clearInterval(timer);
         return;                                          
       }else{                                                     
         currId++;                                              
       }
     } else if (direction==backward){                                 
       if (currId==0){                                           
         //currId=layer.length - 1;                              
         clearInterval(timer);
         return;                                          
       }else{                                           
         currId--;                                              
       }
     }
     // make previous layer invisible
     if (oldId>=0){ 	                                        
       target=layer[oldId];	       
       target.setAttribute("display", "none");          
     }
     // make this layer visible                             
     target=layer[currId];    		
     target.setAttribute("display", "inline");    		
   }	                     
  
  ]]>
</script>

I already have the JavaScript (extracted from your SVGs). I wanted the DOT source code for your three examples.

Here they are:
https://gist.github.com/steveroush/fbf005465cd8d30e2f6488ff7e248e3e

https://gist.github.com/steveroush/4458de9beac9a3be8af424316bf7930e

https://gist.github.com/steveroush/686ac93dd5708f11252b498faf204f5e

If d3-graphviz supported layers, this is what you would get. EDIT: No, you wouldn’t. The next reply shows that.

The extra features are:

  • Smooth animated transitions between the layers.
  • Fade-in and fade-out of entering and exiting components
  • Growing edges (when they are new in a layer)
  • Pan & zoom
  • Play, pause, stop & repeat button. Those are specific to this forum and not built into d3-graphviz.

[dot verbose=true width=600 height=200]

digraph G {
#	layers="local:pvt:test:new:ofc";
        node [style=filled,color=lightblue];
#	node1  [layer="pvt"];
	node2  [layer="all",color=red];
#	node3  [layer="pvt:ofc"];		/* pvt, test, new, and ofc */
#	node2 -> node3  [layer="pvt:all"];	/* same as pvt:ofc */
#	node2 -> node4 [layer=3];		/* same as test */
}

[/dot]
[dot verbose=true]

digraph G {
#	layers="local:pvt:test:new:ofc";
        node [style=filled,color=lightblue];
	node1  [layer="pvt"];
	node2  [layer="all",color=red];
	node3  [layer="pvt:ofc"];		/* pvt, test, new, and ofc */
	node2 -> node3  [layer="pvt:all"];	/* same as pvt:ofc */
#	node2 -> node4 [layer=3];		/* same as test */
}

[/dot]
[dot verbose=true]

digraph G {
#	layers="local:pvt:test:new:ofc";
        node [style=filled,color=lightblue];
#	node1  [layer="pvt"];
	node2  [layer="all",color=red];
	node3  [layer="pvt:ofc"];		/* pvt, test, new, and ofc */
	node2 -> node3  [layer="pvt:all"];	/* same as pvt:ofc */
	node2 -> node4 [layer=3];		/* same as test */
}

[/dot]
[dot verbose=true]

digraph G {
#	layers="local:pvt:test:new:ofc";
        node [style=filled,color=lightblue];
#	node1  [layer="pvt"];
	node2  [layer="all",color=red];
  	node3  [layer="pvt:ofc"];		/* pvt, test, new, and ofc */
	node2 -> node3  [layer="pvt:all"];	/* same as pvt:ofc */
#	node2 -> node4 [layer=3];		/* same as test */
}

[/dot]
[dot verbose=true]

digraph G {
#	layers="local:pvt:test:new:ofc";
        node [style=filled,color=lightblue];
#	node1  [layer="pvt"];
	node2  [layer="all",color=red];
	node3  [layer="pvt:ofc"];		/* pvt, test, new, and ofc */
	node2 -> node3  [layer="pvt:all"];	/* same as pvt:ofc */
#	node2 -> node4 [layer=3];		/* same as test */
}

[/dot]

As you can see, I’ve cheated by commenting out components that are not visible in a layer. The idea is just to show what you would get if layers were supported.

Although the above was kind of funny, it wasn’t at all like your true layered version. Here is one that is:

[dot verbose=true width=600 height=200]

digraph G {
#       layers="local:pvt:test:new:ofc";
        node [style=filled,color=lightblue];
        node1  [layer="pvt" style=invis];
        node2  [layer="all",color=red];
        node3  [layer="pvt:ofc" style=invis];          /* pvt, test, new, and ofc */
        node2 -> node3  [layer="pvt:all" style=invis]; /* same as pvt:ofc */
        node2 -> node4 [layer=3 style=invis];          /* same as test */
        node4 [layer=3 style=invis];                   /* same as test */
}

[/dot]
[dot verbose=true]

digraph G {
#       layers="local:pvt:test:new:ofc";
        node [style=filled,color=lightblue];
        node1  [layer="pvt"];
        node2  [layer="all",color=red];
        node3  [layer="pvt:ofc"];                      /* pvt, test, new, and ofc */
        node2 -> node3  [layer="pvt:all"];             /* same as pvt:ofc */
        node2 -> node4 [layer=3 style=invis];          /* same as test */
        node4 [layer=3 style=invis];                   /* same as test */
}

[/dot]
[dot verbose=true]

digraph G {
#       layers="local:pvt:test:new:ofc";
        node [style=filled,color=lightblue];
        node1  [layer="pvt" style=invis];
        node2  [layer="all",color=red];
        node3  [layer="pvt:ofc"];                      /* pvt, test, new, and ofc */
        node2 -> node3  [layer="pvt:all"];             /* same as pvt:ofc */
        node2 -> node4 [layer=3];                      /* same as test */
}

[/dot]
[dot verbose=true]

digraph G {
#       layers="local:pvt:test:new:ofc";
        node [style=filled,color=lightblue];
        node1  [layer="pvt" style=invis];
        node2  [layer="all",color=red];
        node3  [layer="pvt:ofc"];                      /* pvt, test, new, and ofc */
        node2 -> node3  [layer="pvt:all"];             /* same as pvt:ofc */
        node2 -> node4 [layer=3 style=invis];          /* same as test */
        node4 [layer=3 style=invis];                   /* same as test */
}

[/dot]
[dot verbose=true]

digraph G {
#       layers="local:pvt:test:new:ofc";
        node [style=filled,color=lightblue];
        node1  [layer="pvt" style=invis];
        node2  [layer="all",color=red];
        node3  [layer="pvt:ofc"];                      /* pvt, test, new, and ofc */
        node2 -> node3  [layer="pvt:all"];             /* same as pvt:ofc */
        node2 -> node4 [layer=3, style=invis];         /* same as test */
        node4 [layer=3 style=invis];                   /* same as test */
}

[/dot]

Instead of commenting out components that are not visible in a layer, I’ve made in them… guess what? (a brilliant idea :rofl:) … invisible.

I’ve filed Add support for layers · Issue #164 · magjac/d3-graphviz · GitHub. Some day I might actually work on it.