Search code examples
javascriptjointjsdagrerappid

Layout DirectedGraph (dagre) only on a subset of nodes


I'm looking for a way to layout only a subset of nodes of a directed graph with JointJS / Rappid diagramming library.

I need some "fixed" nodes in the graph and layout the "others", assuming that they can be connected between each other or with some of the fixed nodes (the graph is already added into the paper).

Since the joint.layout.DirectedGraph.layout API must be used on a graph object, I was wondering if there is any mechanism to have some nodes of the graph "fixed" during the layout calculation (some proprety to add in the cell object, for example). Also something like this can be fine, but no incoming and outgoing links should be retrieved by the getSubgraph API

var subGraph = graph.getSubgraph([A, B]);
joint.layout.DirectedGraph.layout(subGraph, layoutOpt);

Looking at the docs I was not able to identify this kind of feature. If this feature is not supported, there is any other approach that I could use to achieve my goal? (Of course I can also layout the entire graph and apply my fixed chords when the operation ends, but I was looking for something better than this).


Solution

  • So I'm pretty sure that the auto layout functionality using Dagre is now only available in the Rappid version of joint js (i.e. paid for). What I do is use Dagre separately to perform the layout calculations and then iterate back through the elements and use the Dagre output to change their position manually. Not ideal, however it does allow you to do whatever you want in terms of only looking at a subset of nodes. Basic code below (graphObj is the jointjs graph object), you should be able to use this as a starting point if you wanted to work with a subset of elements and links:

        var nodes = [];
        var edges = [];
    
        var elements = graphObj.getElements();
        elements.forEach(function(element){
            element.label = element.id;
            element.width = element.attributes.size.width;
            element.height = element.attributes.size.height;
        });
        var links = graphObj.getLinks();
        links.forEach(function(link){
            edges.push({source: link.getSourceElement(), target: link.getTargetElement()});
        });
        dagre.layout()
            .nodeSep(150)
            .edgeSep(100)
            .rankSep(150)
            .rankDir("LR")
            .nodes(elements)
            .edges(edges)
            .run(); 
    
        elements.forEach(function(element){
           element.position(element.dagre.x, element.dagre.y);
           element.attributes.prop.metadata.x = element.dagre.x;
           element.attributes.prop.metadata.y = element.dagre.y;
    
        });