Strange path/line behaviour in d3 radial graph - javascript

I'm trying to modify d3 radial tree graph example I found out and abouton the internet. Original had a diagonal, curved paths between nodes and I needed it to be straight lines.
After hours of struggle I finaly managed to do just that. Well, kinda.
As you can see in this pen, the lines connecting nodes are indeed straight but also they are not originating from the same, central point. For the central node and first level of links, line starting points are converging on the edges of said node, and higher level nodes are grouping lines in sort of rows instead of one, central point I crave and desire so much.
The part of code responsible for drawing the links in the pen starts with line 252and ends with 276, looking like this:
var link = svg.selectAll("link")
.data(links)
.enter().append("path")
.attr("class", "link");
var lines = svg.selectAll('line')
.data(links)
.enter()
.append('line')
.attr('stroke',function(d) {
if(d.source.depth === 0) {
return 'red'
} else {
return '#ccc';
}
});
lines.attr('x1',function(d){return d.source.y})
.attr('y1',function(d){return d.source.x/180*Math.PI})
.attr('x2',function(d){return d.target.y })
.attr('y2',function(d){return d.target.x/180*Math.PI});
lines.attr("transform", function(d) {
return "rotate(" + (d.target.x - 90 ) + ")";
});
I've already tried everything I could come up with, as far as drawing x1, x2, y1, y2 goes, with switching them around, changing the maths in there and whatnot. Also, tried to alter rotation and forcing the coordinates onto the links. Everytime coming back empty. Could You, good people of SO, take a peek and point me in the right direction?

Not enough reputation to comment, so posting as answer. I've done that a long time ago, but don't remember it now. Your x1, y1, x2, y2 values seems wrong.
I gave that x1 = d.source.x, x2 = d.target.x, y1 = d.source.y, y2 = d.target.y. And then, rotated that line with x1, y1 as it's center to the angle(which I forgot sorry).
You should be doing rotation with translation

Related

D3 drag event behaviour: DragEvent.x and DragEvent.y values source

I don't understand the behaviour of the D3 DragEvent object in Mike Bostock’s examples for D3.
Those are the two examples I don't understand:
Circle Dragging I
Drag + Zoom
This is the code that I don't understand:
function dragged(d) { d3.select(this).attr("cx", d.x =
d3.event.x).attr("cy", d.y = d3.event.y); }
In this example, d.x and d.y are part of a data object that contains the last centre of the circle.
What I expect to happen with this code, is that the attributes cx and cy of the circle change to the current value of d3.event.x and d3.event.y
In my view, that should be the coordinates where the mouse is when the user start dragging. If the user is not starting to drag exactly in the centre of the circle, the behaviour I expect is the circle to jump to the place where the user start dragging and, then, a normal dragging behaviour.
My problem is that the initial jump doesn't happen and I don't know why.
I checked the values of d3.event.x and d3.event.y when the code is running and they are exactly the same values of d.x and d.y, never mind where the user click in the circle. I don't understand why that is the case.
Finally, I changed the name (and only the name) in the object (and whenever was necessary in the code), from d.x and d.y to d.m and d.n and the code started to do what I was expecting.
function dragged(d) { d3.select(this).attr("cx", d.m =
d3.event.x).attr("cy", d.n = d3.event.y); }
So, the code works differently when the data object has the properties d.x and d.y that when the data object has the properties d.m and d.n.
It seems like the object DragEvent is expecting a data object with concrete proprieties but that sounds weird to me and I can't find it documented.
I was expecting to avoid checking D3 source code if somebody know the answer already.
What I though a weird behaviour is the intended behaviour.
All DragEvent objects have a 'subject'. The subject is not the SVG shape that is producing the dragging event, but, if it exist, the datum associated to that SVG.
More information available in: https://github.com/d3/d3-drag#drag_subject
So, yes, if the datum object have the the properties x and y, the value would be assigned to the properties dx and dy of the DragEvent object.
As we can see in the d3 source, in the drag.js file:
dx = s.x - p[0] || 0;
dy = s.y - p[1] || 0;

How to connect two d3 js svg circles with a line or path? [duplicate]

I'm very new to both JS and D3, and I've googled this a tonne but only found examples that are a bit too advanced.
I'm making a simple decision graph implementation, and I'm stuck trying to connect 2 nodes with a line / path. The objects can be moved around with the mouse, and the path should always update to reflect the positions of the objects.
This is my base source of knowledge: https://github.com/mbostock/d3/wiki/SVG-Shapes, but I don't quite understand how to do something smart with it.
Here is what I have so far: http://jsbin.com/AXEFERo/5/edit
Don't need the fancy stuff, just need to understand how to create connectors and have them update dynamically when the objects are being dragged around. Big thanks!
To draw a line between the circles, you don't need anything special -- just the line element.
var line = svg.append("line")
.style("stroke", "black")
.attr("x1", 150)
.attr("y1", 100)
.attr("x2", 250)
.attr("y2", 300);
Updating the position dynamically is a bit more difficult. At the moment, you have no means of distinguishing which of the circles is being dragged. One way of doing this is to add a distinguishing class to the g elements.
var g1 = svg.append("g")
.attr("transform", "translate(" + 150 + "," + 100 + ")")
.attr("class", "first")
...
and similarly for the other one. Now you can switch on the class in your dragmove function and update either the start or the end coordinates of the line.
if(d3.select(this).attr("class") == "first") {
line.attr("x1", x);
line.attr("y1", y);
} else {
line.attr("x2", x);
line.attr("y2", y);
}
Complete example here. There are other, more elegant ways of achieving this. In a real application, you would have data bound to the elements and could use that to distinguish between the different circles.

d3.js: How to rotate an SVG around its center while translating?

The Problem:
I have been trying, to no avail, to find a general solution for simultaneously rotating and translating an SVG with d3.js v4.2.2.
I can get the SVG starting and ending in the correct positions, but the easing in-between is all wrong. Below is a JSFiddle where I have done a rotate+translate on many instances of the same SVG pulled from the DOM, each to a different amount rotation according to the datapoint.
A key thing to note is that I need this solution to work when translating from (100,100) to (500,500) just as well as when I start from (0,0).
https://jsfiddle.net/3jbv23em/
var serializer = new XMLSerializer();
var svgNode = serializer.serializeToString(document.querySelector('#svgs svg defs svg'));
var dataset = [1,8,15,22,29,36,43,50,57,64,71,78,85,92,99,106,113,120,127,134,141,148,155,162,169,176,183,190,197,204,211,218,225,232,239,246,253,260,267,274,281,288,295,302,309,316,323,330,337,344,351,358]
var wrapper = d3
.select('#game svg')
.attr('width', window.innerWidth)
.attr('height', window.innerHeight)
.selectAll('g')
.data(dataset)
.enter()
.append('g')
.html(function(d) { return svgNode; })
.transition()
.duration(1500)
.attr('transform', function(d) {
return 'translate(500,300)' +
'rotate(' + d * 1.8 + ', 63, 54.77)';
});
(Unfortunately, I wasn't able to get D3 v4 working in JSFiddle, so JSFiddle is using v3 here. However, the problem is the same).
My expected behaviour: All of the chickens rotate around the same center, never extending past the dimensions of the circle seen at the end of the animation.
The actual result: All chickens rotate around the translated original position of the top left corner before returning to the correct position at the end.
Solutions That Didn't Work for Me:
How to rotate an object around the center in d3.js
This is about rotating around a fixed point further away, not around a point on the object one is trying to rotate
SVG rotation anchorpoint
This might work, but completely takes me out of the D3 ecosystem
How do I rotate or scale (transform) an SVG path relative to its center point?
I need the solution to start from an arbitrary point, not always the origin # (0,0)
In Conclusion:
I'm pretty bewildered at the moment, and any help is greatly appreciated. Thanks for your time.
Because D3 transitions applies both the translate and the rotate together you get some awkward looking animations even though the end result is what you are looking for.
D3 provides a d3.interpolateString which handles executing the animation the way you would like. d3.interpolateString requires a starting and ending transform. d3.interpolateString is used within attrTween.
Replace
.attr('transform', function(d) {
return 'translate(500,300)' +
'rotate(' + d * 1.8 + ', 63, 54.77)';
});
With
.attrTween('transform', function(d, i, a) {
return d3.interpolateString('translate(0,0) rotate(0)',
'translate(500,300)' +
'rotate(' + d * 1.8 + ', 63, 54.77)');
});
Here is an updated jsfiddle with a tweak to the translation for better viewability for smaller screens.
https://jsfiddle.net/3jbv23em/16/
A helpful link with the same issue is "D3.js animate rotation".

D3.js Dynamic connector between objects

I'm very new to both JS and D3, and I've googled this a tonne but only found examples that are a bit too advanced.
I'm making a simple decision graph implementation, and I'm stuck trying to connect 2 nodes with a line / path. The objects can be moved around with the mouse, and the path should always update to reflect the positions of the objects.
This is my base source of knowledge: https://github.com/mbostock/d3/wiki/SVG-Shapes, but I don't quite understand how to do something smart with it.
Here is what I have so far: http://jsbin.com/AXEFERo/5/edit
Don't need the fancy stuff, just need to understand how to create connectors and have them update dynamically when the objects are being dragged around. Big thanks!
To draw a line between the circles, you don't need anything special -- just the line element.
var line = svg.append("line")
.style("stroke", "black")
.attr("x1", 150)
.attr("y1", 100)
.attr("x2", 250)
.attr("y2", 300);
Updating the position dynamically is a bit more difficult. At the moment, you have no means of distinguishing which of the circles is being dragged. One way of doing this is to add a distinguishing class to the g elements.
var g1 = svg.append("g")
.attr("transform", "translate(" + 150 + "," + 100 + ")")
.attr("class", "first")
...
and similarly for the other one. Now you can switch on the class in your dragmove function and update either the start or the end coordinates of the line.
if(d3.select(this).attr("class") == "first") {
line.attr("x1", x);
line.attr("y1", y);
} else {
line.attr("x2", x);
line.attr("y2", y);
}
Complete example here. There are other, more elegant ways of achieving this. In a real application, you would have data bound to the elements and could use that to distinguish between the different circles.

How to extend the Diagonal Object in D3.js? [duplicate]

I'm very new to d3.js (and SVG in general), and I want to do something simple: a tree/dendrogram with angled connectors.
I have cannibalised the d3 example from here:http://mbostock.github.com/d3/ex/cluster.html
and I want to make it more like the protovis examples here:
http://mbostock.github.com/protovis/ex/indent.html
http://mbostock.github.com/protovis/ex/dendrogram.html
I have made a start here: http://jsbin.com/ugacud/2/edit#javascript,html and I think it's the following snippet that's wrong:
var diagonal = d3.svg.diagonal()
.projection(function(d) { return [d.y, d.x]; });
However there's no obvious replacement, I could use d3.svg.line, but I don't know how to integrate it properly, and ideally I'd like an elbow connector....although I am wondering if I am using the wrong library for this, as a lot of the d3 examples I've seen are using the gravitational force to do graphs of objects instead of trees.
Replace the diagonal function with a custom path generator, using SVG's "H" and "V" path commands.
function elbow(d, i) {
return "M" + d.source.y + "," + d.source.x
+ "V" + d.target.x + "H" + d.target.y;
}
Note that the source and target's coordinates (x and y) are swapped. This example displays the layout with a horizontal orientation, however the layout always uses the same coordinate system: x is the breadth of the tree, and y is the depth of the tree. So, if you want to display the tree with the leaf (bottommost) nodes on the right edge, then you need to swap x and y. That's what the diagonal's projection function does, but in the above elbow implementation I just hard-coded the behavior rather than using a configurable function.
As in:
svg.selectAll("path.link")
.data(cluster.links(nodes))
.enter().append("path")
.attr("class", "link")
.attr("d", elbow);
And a working example:
http://bl.ocks.org/d/2429963/

Categories