Layout DirectedGraph (dagre) only on a subset of nodes - javascript

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

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;
});

Related

how to generate a network graph with edge weight using D3.js force layout

I read many codes about force layout with D3.js and I found that edges elements are only 'source' and 'target'. I want to generate a graph whose layout is like the 'Altas' in Gephi. In my data, edge has elements of 'weight', which describe the correlation of nodes it links, and this is supposed to be took into consideration when start the force layout. So that the similar nodes can gather together. Is there a way to implement so or the physics model used in force layout is irrelevant with the weight of edges?
Yes, that is possible in D3.js, however, I recommend the webcola library since it is more easy and faster and works very well with D3.js.
Each edge may contain other information besides source and target. So, it is easy to add a weight attribute, e.g.:
let edge = {
source: node1,
target: node2,
weight: 2
};
When using webcola (https://github.com/tgdwyer/WebCola/), you can add some constraints to use your weights, or you can use the linkDistance as a function, explained here: https://github.com/tgdwyer/WebCola/wiki/link-lengths, e.g.:
let weightFactor = 10;
let d3cola = cola.d3adaptor(d3)
.size([500, 400])
.linkDistance(function (l) { return l.weight * weightFactor; };
So, the steps are:
Build your graph with D3.js, however,
Simulate it with webcola
You can create an instance of a webcola simulation for d3.js like that:
d3cola.avoidOverlaps(true)
.handleDisconnected(false)
.start(30);
let simulation = this.d3cola
.nodes(graph.nodes) // graph is your graph
.links(graph.edges)
.start();

How to manipulate cytoscape.js graph data before rendering

I have relatively big amount of graph data (over 1000 nodes, over 2000 edges). Cytoscape.js is failing to render this amount of data, so I decided I will cook some interactive graph exploration. I want to start with graph roots (it is DAG) and user will be able to explore nodes after clicking on them.
I know I can do it outside of cytoscape.js, but I wanted to know if I can create a node/edge collection that I would manipulate (remove all nodes but roots and their neighbours) and then use as data source (get child nodes of nodes). Cytoscape makes graph processing quite easy and it would be great to not need to reinvent the wheel.
I tried creating cy.collection based on the JSON data I was feeding to the graph, but it seems like it doesn't accept the same data format as cy.load.
I was able to work around that by creating two cytoscape objects. I am loading all my graph data to the variable I called allcy:
var allcy = cytoscape({
headless: true,
});
Notice that it's headless, so it won't try to render nodes in any way.
Then I am creating normal "working" cy var
var cy = cytoscape({
container: document.getElementById('thegraph'),
});
I load all my data to allcy
allcy.load(response); //response is json graph data I got from xmlhttp request
I add roots and their neighbourhood to cy and reload the layout.
cy.add(allcy.nodes().roots().closedNeighborhood());
cy.layout(layoutSettings);
Voila! I have only root nodes in my rendered cy graph. Now I need to add other nodes from allcy as needed, but I think I will manage to do it. Hope it comes in handy to someone.

Point coordinate translate to specific Surface in Famo.us

We have a pretty complex web app built in meteor. The UI is mainly in nested HTML elements. Now we are trying to rewrite the UI with Famo.us so we can have better performance as well as adding great animation effects. One feature in our app is, when user drag on top of an element A, we need to draw a new element B based on the precise position of the mouse events in B. That is, we need to calculate the coordinate of a point in any elements, even the element has complex transforms. We were using the 'webkitConvertPointFromPageToNode' function in webkit browsers(we only support webkit.) to do the job. Does Famo.us has a similar function so I can calculate a point coordinate in a specific Surface? Or do you have any suggestions on how to accomplish such features with current API?
Thanks
Given that the transforms in Famo.us are all backed by absolute positioning, finding the coordinates in any given surface is pretty straightforward. In the Event object you can grab the offsetX and offsetY of the target surface.
Check out this example..
Hope it helps!
var Engine = require('famous/core/Engine');
var Surface = require('famous/core/Surface');
var StateModifier = require('famous/modifiers/StateModifier');
var Transform = require('famous/core/Transform');
var context = Engine.createContext();
var surface = new Surface({
size:[200,200],
properties: {
backgroundColor:'green',
color:'white',
textAlign:'center',
lineHeight:'200px'
}
})
surface.on('mousemove',function(e){
surface.setContent("x: "+e.offsetX+", y: "+e.offsetY);
})
surface.state = new StateModifier({
transform: Transform.translate(100,100,0)
})
context.add(surface.state).add(surface);
I have found the right way to do this.
First, I dug into the problem mentioned in my comment that the offsetX/offsetY value is actually based on the child surfaces. Because offsetX/offsetY values are generated by DOM's MouseEvent and copied into famo.us with no modification. DOM doesn't provide the coordinate of the mouse point on the 'currentTarget'. It only provide the value for 'target', which is the element the event occurs. So we can only use the clientX/clientY coordinate in the viewport, then calculate the coordinate of that point on the target element. No official API to do the calculation either. Only webkit provide the 'webkitConvertPointFromPageToNode' api to do it because the layout engine knows all about the position and transforms on a specific element.
But then I realise that with Famo.us, we know the transforms of each surface! In the render tree, all the modifiers on the path from root context to a RenderNode form the transform for that node and the nodes below. We can multiply them to get one transform matrix M. Then we can do a coordinate system transformation to calculate the point's right coordinate in the node's local coordinate system.
But Famo.us doesn't have direct API to get all the modifiers for a node, I did it myself in my code. I would suggest Famo.us to add a 'parent' reference on each RenderNode, then we can get them easily for any node.
It took me a while but this work for me:
var myX=event.clientX;
var myY=event.clientY;
for(var i=0;i<event.path.length;i++)
{
if(event.path[i].style===undefined)
continue;
var matrix=event.path[i].style.transform;
var matrixPattern = /^\w*\((((\d+)|(\d*\.\d+)),\s*)*((\d+)|(\d*\.\d+))\)/i;
if (matrixPattern.test(matrix)) {
var matrixCopy = matrix.replace(/^\w*\(/, '').replace(')', '');
myX-=matrixCopy.split(/\s*,\s*/)[12];
myY-=matrixCopy.split(/\s*,\s*/)[13];
}
}
Tested with align and size modifier

D3 - partly force directed graph

As I'm a beginner, here's another question about D3.js.
I have constructed a force directed graph, just as a lot did.
All the nodes are stored in one array and all links in another.
Now I want the central node to be fixed in the middle of the svg and the others lingering around it.
Is there any possibility to cut only one node out of the force layout without it falling out of my net? Has anybody already made it?
You can set the fixed property of a node to true, then place it where you like - see this related question.
// explicity set node position
var fixedNode = graph.nodes[0];
fixedNode.fixed = true;
fixedNode.x = width/2;
fixedNode.y = height/2;
Working fiddle: http://jsfiddle.net/nrabinowitz/z2cye/

Examples of JS hierarchical tree with mixed canvas/DIV approach

I am wishing to provide a visualisation of groups of data on a website, each containing multiple fields. The groups are related to other groups in a largely hierarchical fashion.
The Spacetree examples from the JavaScript InfoVis toolkit provide almost all functionality, with the major caveat that the entire graph is rendered to a canvas. Node types are therefore visually restricted to canvas drawing elements.
Instead, I'm looking for a library that allows <div>s to be rendered (each with my multiple fields, icons, Javascript functionality, etc.) and visually linked in a similar fashion to the Spacetree examples. Essentially, the general concept is similar to UML or database diagrams.
I suppose that I could just use the InfoVis toolkit, overlay my <div>s and limit interactivity, but I'm wondering if anyone has come across a library that does this out of the box (and preferably for free).
It's already doing just that! Looking at the example on the InvoVis site, there is a chunk of javascript which is actually constructing html nodes for the nodes shown on the screen. All you need to do, it seems, is modify that section to acquire your html chunk:
//This method is called on DOM label creation.
//Use this method to add event handlers and styles to
//your node.
onCreateLabel: function(label, node){
label.id = node.id;
label.innerHTML = node.name;
label.onclick = function(){
st.onClick(node.id);
};
//set label styles
var style = label.style;
style.width = 40 + 'px';
style.height = 17 + 'px';
style.cursor = 'pointer';
style.color = '#fff';
//style.backgroundColor = '#1a1a1a';
style.fontSize = '0.8em';
style.textAlign= 'center';
style.textDecoration = 'underline';
style.paddingTop = '3px';
},
The important line is
label.innerHTML = node.name;

Categories

Resources