D3 - partly force directed graph - javascript

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/

Related

Find 'view' co-ordinates in vis.js

I'm working on a modification to vis.js's Graph3d to do a filled line graph, like this:
The hard part - unsurprisingly - is working out the rendering order for the polygons. I think I can do this by checking whether a ray from the viewer to a given line B crosses line A:
In this example, since line A is "in the way" of line B, we should draw line A first. I'll use a snippet of code from How do you detect where two line segments intersect? to check whether the lines cross.
However, I haven't figured how to find the position of the user's view. I kind of assumed this would be the camera object, so wrote a little bit of debug code to draw the camera on the graph:
var camera = this._convert3Dto2D(this.camera.getCameraLocation());
ctx.strokeStyle = Math.random()>0.5 ? 'ff0000' : '00ff00';
ctx.beginPath();
ctx.moveTo(camera.x, camera.y);
ctx.lineTo(camera.x, camera.y+5);
ctx.stroke();
In fact, the camera co-ordinates as measured by this are always at 0,0,0 on the graph (which would be the far top right on the above screengrab). What I need, I think, is effectively the bottom of the screen.
How can I find this? Or is there a better way to achieve what I'm trying to do?
I don't know if this is still an active issue, but FWIW, Graph3D has internal handling of the sort ordering.
All graph points are sorted with respect to the viewpoint, using a representative coordinate called point.bottom. The rendering is then done using this ordering, with the most distant elements drawn first. This works fine as long as none of the elements intersect; in that case, you can expect artefacts.
Basically, all you need to do, is define point.bottom per graph polygon, and Graph3D will then pick it up from there.
If you are still interested in working on this:
This happens in Graph3d.js, method Graph3d.prototype._calcTranslations(). For an example, have a look at how the Grid and Surface graph elements are initialized in Graph3d.prototype._getDataPoints(). The relevant code is:
obj = {};
obj.point = point3d;
obj.trans = undefined;
obj.screen = undefined;
obj.bottom = new Point3d(x, y, this.zRange.min);

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

Avoid collision between nodes and edges in D3 force layout

In this example: http://bl.ocks.org/mbostock/1747543:
...Mike shows us how to avoid collision among nodes so that no two nodes overlap each other.
I wonder if it is possible to avoid collision between nodes and edges so that no node 'clips' or overlaps an edge unless it is connected by that edge.
The following example using D3 force-direct shows that node L overlaps with the edge connecting I and A, and similarly, node M overlaps with the edge connecting L and D. How do we prevent such cases?
If your graph doesn't have too many nodes, you can fake it. Just insert one or more nodes for each link, and set their position along the link in the tick handler. Check out http://bl.ocks.org/couchand/7190660 for an example, but the changes to Mike Bostock's version amount to basically just:
var linkNodes = [];
graph.links.forEach(function(link) {
linkNodes.push({
source: graph.nodes[link.source],
target: graph.nodes[link.target]
});
});
and
// force.on('tick', function() {
linkNodes.forEach(function(node) {
node.x = (node.source.x + node.target.x) * 0.5;
node.y = (node.source.y + node.target.y) * 0.5;
});
This will introduce a pretty serious performance overhead if you have very many nodes and edges, but if your graph doesn't get much larger than your example it would hardly be noticed.
You may also want to fiddle with the relative force of the real nodes versus the link nodes.
Take this one step further and you get the nice curved links of http://bl.ocks.org/mbostock/4600693.

Detecting irregular Shape

Leading up from this question Detecting mouse coordinates with precision, I have learnt quite a bit in the past few days. Here are what I picked as best learning resources on this topic:
http://gamedev.tutsplus.com/tutorials/implementation/quick-tip-use-quadtrees-to-detect-likely-collisions-in-2d-space/
http://www.gamedev.net/page/resources/_/technical/graphics-programming-and-theory/quadtrees-r1303
http://jsfiddle.net/2dchA/2/
The code in (3) works in JSFiddle but breaks at this section in my testing environment (VS2012):
var myTree = new Quadtree({
x: 0,
y: 0,
width: 400,
height: 300
});
with the message Quadtree is undefined in IE. FF & Chrome just gloss over it and display an empty page. I couldn't sort it out. Question 1: Can someone help out with that?
My main question:
I have a region (parcels of land like a map) with about 1500 parcels drawn in html5, not jpg or png images. It is a lot of lines of code to complete that but the rendering is great, so I am keeping it that way. I intend to have a mouseover event tell me which parcel I am standing on when the mouse stops. As you will see in the previous question referred my previous attempts were not impressive. Based on the learning I have been doing, and thanks to Ken J's answer/comments, I would like to go with this new approach of slicing up my canvas into say 15 quads of 100 objects each. However, I would like some guidance before I take another wild dive the wrong way.
Question 2: Should I slice it up at creation or should the slicing happen when the mouse is over a region, ie, trail the mouse? The latter sounds better to me but I think I can do with some advice and, if possible, some start out code. The quadtree concept is completely new to me. Thanks.
Can't help with question 1.
You should definitely build the tree as early as possible, given that the objective is to get the page to respond as quick as possible once the user clicks somewhere.
Keep the tree for as long as the user interacts with the 2d area. Updating a quad tree shouldn't be too hard, so even if the area changes contents, you should be able to reuse the existing tree (just update it).
Given the fact that your draw area is well know i see no advantage in a QuadTree over a spacial hash function. This function will give you an integer out of an (x,y) point.
var blocWidth = 20;
var blocHeight = 20;
var blocsPerLine = ( 0 | ( worldWidth / blocWidth) ) + 1 ;
function hashPoint(x,y) {
return ( 0 | (x/blocWidth)) + blocsPerLine*(0|(y/blocHeight));
}
once you built that, hash all your parcels within an array :
parcelHash = [];
function addHash(i,p) {
if (!parcelHash[i]) { parcelHash[i]=[ p ]; return; }
if (parcelHash[i].indexOf(p) != -1 ) return;
parcelHash[i].push(p);
}
function hashParcel (p) {
var thisHash = hashPoint(p.x,p.y); // upper left
addHash( thisHash, p);
thisHash = hashPoint(p.x+width, p.y); // upper right
addHash(thisHash, p);
thisHash = hashPoint(p.x, p.y+p.height); // lower left
addHash(thisHash, p);
thisHash = hashPoint(p.x+width, p.y+p.height); // lower right
addHash(thisHash, p);
};
for (var i=0; i<allParcels.length; i++) { hashParcel(allParcels[i]) };
now if you have a mouse position, you can retrieve all the parcels in the
same block with :
function getParcels(x,y) {
var thisHash = hashPoint(x,y);
return parcelHash[thisHash];
}
I'll just give you few tips in addition to what others have said.
... have a mouseover event tell me which parcel I am standing on ...
From your other messages I conclude that parcels will have irregular shapes. Quadtrees in general work with rectangles, so you'd have to calculate the bounding rectangle around the shape of the parcel and insert that rectangle in the quadtree. Then are when you want to determine whether mouse is over a parcel, you'll query the quadtree which will give you a set of parcels that might be under the mouse, but you'll have to then do a more precise check on your own to see if it indeed is.
... when the mouse stops.
From your other questions I saw that you try to detect when the mouse has "stopped". Maybe you should look at it this way: mouse cursor is never moving, it's teleporting around the screen from previous point to next. It's always stopped, never moving. This might seem a bit philosophical, but it'll keep your code simpler. You should definitely be able to achieve what you intended without any setTimeout checks.
... slicing up my canvas into say 15 quads of 100 objects each.
... Should I slice it up at creation or should the slicing happen when the mouse is over a region
You won't (and can't) do slicing, quadtree implementation does that automatically (that's its purpose) when you insert or remove items from it (note that moving the item is actually removing then re-inserting it).
I didn't look into the implementation of quadtree that you're using, but here are two MX-CIF quadtree implementations in case that one doesn't work out for you:
https://github.com/pdehn/jsQuad
https://github.com/bjornharrtell/jsts/tree/master/src/jsts/index/quadtree
The problem in question 1 probably happens because jsfiddle (http) page is trying access quadtree.js which is on https

How can I stop elements overlapping using JavaScript and the Raphael JavaScript library

I’m generating multiple, random sized, circular elements using the Raphael JavaScript library but because it’s random a lot of the circular elements being generate overlap or cover each other. What I wanted to know, is there any way with JavaScript to tell if one element is in already in particular position so to avoid the overlapping? Essentially, I want to create random elements on a canvas, of a random size that don’t overlap or cover each other.
There's a couple of test files I created here to give you an idea of what I'm doing. The first one generates random objects and the second link sets them to a grid to stop the overlapping.
http://files.nicklowman.co.uk/movies/raphael_test_01/
http://files.nicklowman.co.uk/movies/raphael_test_03/
The easiest way is to create an object and give it a repulsive force that degrades towards zero at it's edge. As you drop these objects onto the canvas the objects will push away from each other until they reach a point of equilibrium.
Your examples aren't working for me, so I cannot visualize your exact scenario.
Before you "drop" an element on the canvas, you could query the positions of your other elements and do some calculations to check if the new element will overlap.
A very simple example of this concept using circle elements might look like this:
function overlap(circ1, circ2) {
var attrs = ["cx", "cy", "r"];
var c1 = circ1.attr(attrs);
var c2 = circ2.attr(attrs);
var dist = Math.sqrt(Math.pow(c1.cx - c2.cx ,2) + Math.pow(c1.cy - c2.cy, 2));
return (dist < (c1.r + c2.r));
}
var next_drop = paper.circle(x, y, r);
for (var i in circles) {
if (overlap(next_drop, circles[i])) {
// do something
}
}
Of course calculating just where you're going to place a circle after you've determined it overlaps with others is a little more complicated.

Categories

Resources