Efficiently calculating convex hulls in D3 force-directed graph - javascript

I've got something similar to the force-directed graph example. The main difference is there is no force -- the layout is static except for user drag interactions. I've added code that draws convex hulls (as svg:path elements) around user-defined groups of nodes. As the nodes are moved (that is, .on("drag")) the code that calculates the hulls is fired. That means it's fired constantly when the nodes are dragged. There are typically 10 to 30 hulls; a node may be in one or more hull. See below:
I'm trying to figure out if there's a more efficient (higher performance) way to do what I'm doing. Keeping this at high-level for now:
On drag, update all hull graphics:
For each hull, create an array of the coordinates of the constituent nodes.
Replace each coordinate pair in the above array with 8 points that are some distance r away from the original point. This is to pad/dilate the hull so it's not actually touching any nodes.
Feed the calculated coordinates to d3.geom.hull.
I'm getting about 50 fps in Chrome, which isn't bad at all, but it seems like a dreadfully inefficient setup.
My only clear idea is to store the ID(s) of the hull(s) in which a node is contained in the nodes array, and only update that/those hulls instead of all of the hulls. I'm also wondering about more efficient data structures to store the hull data (not the paths themselves). Currently the data structure looks like this:
hulls = {1:{nodeIDs:[1,2,3,4,5], name:"hull1"}, 2:{...}, ...};
Pardon the open-ended question, but I'm hoping there's some programming technique that I'm not familiar with that would work well here.

The most efficient approach would be to only recalculate the position of the 8-tuple of coordinates orbiting the node that is being dragged, then propagating that information in the parent hull(s) for redrawing.
You can do this by making a few changes.
To each node's data structure, add the following elements.
References to all parent hulls (as you suggested)
The 8-tuple of "padding" coordinates orbiting that node (cache them)
On drag, consider only the node being dragged.
Update that node's 8-tuple.
Collect that node's parent hull's child nodes and collapse together their 8-tuples and feed them to hull (destroying the previous hull, if necessary).
I'm not familar with d3.js's API, but I'm sure you can fill in any blanks that I've left.

Related

Summarise many nodes in Cytoscape.js

I am building a web app where the user can create a sort of bipartite graph like this one:
Sometimes the user would like to build a huge graph, for example a graph in which the topmost layer has 784 nodes, as in the next picture. My application can handle the computation, but the result is ugly and meaningless:
Do you have any idea for rendering a huge layer without just drawing all the nodes but, instead, summarising them with another, prettier representation?
Until now I have thought about putting all the nodes of a "huge" layer in an empty compound node, but of course then it is not possible to draw some edges (so the huge layer would seem as it were disconnected from the graph). Another solution would be to have all layers with more than 100 nodes have exactly 100 nodes, and put them inside a compound node with z-index greater than each node's z-index; but I haven't tried this yet.
If you have some other ideas, or if Cytoscape.js provides a way to summarise large graphs, please let me know.
Thank you.
you could group nodes when the amount in a layer exceeds a certain number (e.g. if you have 100 nodes, you combine them into groups of 25)
I'd do this by iterating over the nodes in the layer, making a new node N for each subgroup (inserting all relevant information needed), and then replacing any mention of the replaced nodes by N in all relevant edges (finding connected edges).
As I personally generate layouts/nodes in python before sending them to cytoscape for visualization, I'll refrain from posting a potentially ineffecient/incorrect javascript/cytoscape example :)

A scatterplot with links between points using d3?

I am trying to make a visualization using d3 which is basically a scatter plot with links between the points. (I have attached a .gif of the existing java based visualization)
The points can be added by double clicking other points. On hovering over a point, I wish to have links drawn between the point and all its partners on screen.
I have the part where on double clicking a node, its partners are added. What I need help with is drawing the links (primarily I am not able to understand how can I get the x1,y1,x2,y2 values required to draw the links).
This is what my DOM looks like:
I have seen a lot of examples online but somehow not able to figure the solution - if anyone could link me to a similar visualization or share a fiddle/ give some pointers on how this can be achieved I would be really grateful.
First the simple stuff: here are 2 mechanisms for drawing the lines.
Next, in terms of the data representation of the lines, check out how links are typically drawn when working with the force directed layout.
Important: Do not get distracted by the existence of the force layout in this example and by the fact that the force layout works with these links (which are passed into it by calling force.links(links)). That aspect of the example probably doesn't have an equivalent in what you're trying to achieve.
However, do notice how the links array is constructed —— with each element of the array being an object with pointers to source and target datums. In your case, you'll want to work with a similar links array, where source is the node under the mouse and target is a node that's connected to it. So you'll end up with an array of links who all have the same source datum but unique target datums.
You can then bind the links array (via the usual .data() method) to a d3 selection of line or path elements. Once you bind, you can use the usual enter, update, exit pattern to append, update and remove (on mouse out) the drawn lines.
Given a source and target datums, you can calculate the x and y of the endpoints in the same way you currently calculate the translation of each <g> element, presumably using a d3 scale.

Updating node labels to avoid clutter in JavaScript InfoVis Toolkit (JIT)

I am doing visualization with the JavaScript InfoVis Toolkit, in particular the hypertree. I am loading data dynamically and sometimes the labels around the nodes overlap and clutter. I would like to avoid this clutter by altering the label positions.
Here is an example of cluttering (the top and bottom nodes):
I imagine that I would loop through each x,y coordinate, give it some bounding box and do basic collision detection, and update positions accordingly.
For this library, I see the demo shows a onPlaceLabel() function, but (if I understand correctly) at that moment I wouldn't know the position of every other node's label. So, I am looking at onComplete(), where I see I can access each node as follows:
onComplete: function(){
ht.graph.eachNode(function(n) {
console.log(n);
}
}
But the node information does not include its label positions, only their positions relative to the center node. Is there a way to access the labels in this way and be able to update their positions?

Merge two svg path elements programmatically

I am rendering a map out of SVG paths (using jVectormap).
There are cases where one region has to be merged with the neighboring region.
Unfortunately both regions don't touch each other and I have to interpolate to fill the space in between.
jVectormap uses very simple SVG paths with M to set the the absolute startpoint and l to connect relative points.
Does any of the SVG libraries cover such an operation?
I haven't tried this, but you may get around it by running the converter at jVectormap with the following parameters:
--buffer_distance=0
--where="ISO='region_1' OR ISO='region_2'"
Where region_1 and region_2 are the two regions that you need to merge.
Solving the problem this way also means that the generated SVG paths are true to the original coordinates, whereas a following fix may lead to some (probably minor) inconsistencies.
This might not be the kind of answer you're looking for, but using Raphael.js you could loop over the entire length of the path of one region getPointAtLength(), comparing it with all points of the second region. If the coordinates are closer than n pixels from any coordinates on the second region and the previous coordinates weren't, than that could be regarded a "glue" point. You would then jump to the second regio and start looping over it, if the next point is still closer than n points, than go in the opposite direction, if still closer change direction and go farther along the path till finding a point that's farther away from the original region than n pixels. Continue looping in that direction till once again finding a new "glue" point, where once again you will switch to the original region in the manner described and all points which weren't covered in this final loop could be discarded (or you could simply create a new shape based on the points you came across whilst looping over the length of the original region.
True enough, it's not the easiest script to make, but it should be quite do-able I believe, especially when you can use a function like getPointAtLength to find the points between the defined svg points (though you need to only 'record' the defined points, and that's sort of the hard path as Raphael.js doesn't excitedly have any functions which would help with this, still even that shouldn't be too hard to match up by hand (in code of course)).

Algorithm to draw connections between nodes without overlapping nodes

I have a series of nodes in a graph. The nodes are placed by the user in specific spots. The nodes are guaranteed to not overlap and, in fact, to have a buffer of space between them. These nodes are connected and each edge joins to a node at a specific point. I need to draw the edges between the nodes such that the edges:
(required) do not overlap the parent nodes
(ideally) would not overlap any node
I am not worried about edge crossings. Bonus points if there's an implementation of this in Javascript. I am unable to use any libraries outside of Javascript.
One solution could be using Bézier Curves:
"A Bézier curve is defined by a set of control points P0 through Pn,
where n is called its order (n = 1 for linear, 2 for quadratic, etc.).
The first and last control points are always the end points of the
curve; however, the intermediate control points (if any) generally do
not lie on the curve."
So the basic idea is to use parent node(s) as intermediate control points. You may also use points of the edges as intermediate control points to avoid edges overlapping.
In the wiki article you can find nice animations explaining it.
For javascript implementation I took a look at the followings libs:
jsdraw2d (LGPL license) with a nice demo and well referenced. Implemented it also using HTML5 and SVG for performance (jsdraw2dX).
jsbezier on google code
But if you google "javascript bezier library" you can find more.
If you are familiar with C# and .NET you can explore Microsoft.GLEE library (description is here and here) via ILSpy, or even theoretically save this sources to .csproj, modify and recompile it with Script# to JavaScript.

Categories

Resources