I'm trying to design a simulation of physical gravity with the D3 library, but I'm not having a lot of luck. The 'layout' API reference states that physical gravity can be implemented through a positive 'charge' parameter, but I'm unsure of how this would work.
What I'm attempting to implement at the moment is a single SVG element that contains multiple variably-weighted and -sized rects rising at different speeds eventually going out of the viewport -- their weights will define the velocity at which they rise. (Basically, I'm just trying to implement a global gravitational pull from beyond the top of the viewport.)
Is there a feasible way of doing this in accordance with the D3 force layout? I'm just looking for conceptual solutions, but examples and code snippets are appreciated as well.
Thanks in advance!
Here is couple ideas:
You can directly modify vertical position of node in tick event handler:
force.on("tick", function(e) {
nodes.forEach(function(o, i) {
o.y -= o.weight / 30;
});
node.attr("cx", function(d) { return d.x; })
.attr("cy", function(d) { return d.y; });
});
You need to set force.gavity(0) for this approach.
Or you can use force.gravity: it pulls nodes towards layout center, you can specify svg transfromation to shift layout center above viewport
Related
I am trying to implement a radial force layout in D3.js , I saw a similar example but i am stuck on how to initiate the node positions in the layout.
http://bl.ocks.org/vlandham/5087480
Thanks in Advance
Initialising a position is just done by setting the cx and cy positions. The most logical place is where the radius is currently being set i.e.
.attr("r", 10)
.attr("cx", 5) //added
.attr("cy", 5) //added
Of course, you can do something more exotic if you are using the bound data to initialise position.
This will only set the starting point though - the force layout will then take over and position elements. The advantage is that you can potentially reduce some of the initial node movement if you get it right.
I have a d3 visualisation on a web page that shows a collection of plot points on a chart.
It's all very simple really with just two axis and sometimes maybe 30 points to plot. As I render these plots I perform a transition on the r attributes of the SVG circles. The code that does this is:
g.append("svg:circle")
.attr("class", "plot-circle")
.attr("cx", xCo)
.attr("cy", yCo)
.attr("r", 0)
.transition()
.delay(function() { return delayInput * 100; })
.duration(plotExplosionDuration)
.ease(Math.sqrt)
.attr("r", 6);
All that occurs is that the circles are initially set to r=0 which means they aren't rendered at all. Then I start a transition on the appended circle to take this radius up to 6.
The problem is that it appears on some machines these transitions don't stop at r=6 and some plots end up being much bigger than the value set after the transition.
I simply cannot duplicate this on my main development machine (PC), my iPad nor my MacBook Pro which leads me to think it might be performance or machine load causing this?
has anyone got any ideas on how to ensure the transition stops at the defined final r value?
I have a zoomable map of the world with a single point on it (in reality there are multiple points from a separate resource but I have simplified it). The block is here.
When I try and zoom in it jumps to a certain scale and then usually doesn't allow any more zooming movements. I have experimented with various different values for the transition, scale and scaleExtent taken from this example and this one (with the latter being very close to what I want overall) but nothing has worked. It seems to get quite close to the actual size at height/6 for minimum zoom but still behaves badly.
I suspect the main problem is with scaleExtent. I actually want the minimum zoom to be the size of the map and so it isn't possible to pan around unless zoomed in.
The other problem is, as you can see in the bl.ock that the circle disappears when you zoom. I want the circle to maintain position and size (so it doesn't get bigger when I zoom).
Can any one help with
The zoom problem on the map, so the map minimum zoom is the actual size map and I can zoom in to about 6x that
Preventing the map from panning unless zoomed in
Maintaining the size and position of the circle on the map
I've put an example of what I think you're after on this bl.ock which is based on the first example you pointed to. It looks as though this line .scaleExtent([height, height*6]) is limiting the scale (well that the purpose of it) to something that was incompatible with your expectations and the initial scale you set, so when you zoom in past a certain level (in this case height) you get stick between height and height * 6.
If you set your minimum zoom and your initial zoom I think you'll get around some of your issues.
The issue with the dots was that they weren't referenced in the redraw function, so when you zoomed d3 / the browser didn't know what to do with them. In my example I've put the following snippet in to address this:
g.selectAll("circle")
.attr("cx", function (d,i) { return projection(d)[0]; })
.attr("cy", function (d,i) { return projection(d)[1]; })
.attr("r", "10px")
.style("fill", "red");
I'm attempting to integrate AngularJS with d3 for dragging and resizing. I've managed to create a rect object that is draggable in an SVG element, and resizable using resize handles. The resize handles work as they should, but resizing is choppy when I try to resize in the north or east direction. I created the following Plunk as a demo of the issue: http://plnkr.co/tG19vpyyw0OHMetLOu2U. (I've simplified it to show the issue I've run into, so there's only one resize handle.)
Dragging works as it should, and resizing in the west and south directions works as well (not shown in the demo).
Figured I'd ask the community and see if anyone had run into this before. Thank you all.
The problem is that you're modifying the rect element itself and the enclosing g element. There's a very short delay between setting the size of the rect and the position of the g simply because this has to be done with two separate commands. During this delay, the cursor position relative the the drag rectangle changes, firing a new drag event with values that correspond to the inconsistent intermediate state. This is fixed immediately afterwards (as soon as the attributes of both elements have been adjusted) and a new drag event is fired that fixes the inconsistency, but it is noticeable as a flicker.
The easiest way to fix this is to change both size and position for the rect and nothing for the g element. This means adjusting the position of the drag rectangle as well and makes the code less nice, but avoids the timing/inconsistency problem.
So myrect becomes
var myRect = d3.select(document.createElementNS("http://www.w3.org/2000/svg", "rect"))
.attr("data-ng-width", "{{square.w}}")
.attr("data-ng-height", "{{square.h}}")
.attr("stroke", "yellow")
.attr("stroke-width", 3)
.attr("fill-opacity", 0)
.attr("data-ng-x", "{{square.x}}")
.attr("data-ng-y", "{{square.y}}");
and resizer
var resizer = myGroup.append("rect")
.attr("width", 5)
.attr("height", 5)
.attr("stroke", "blue")
.attr("stroke-width", 1)
.attr("fill-opacity", 0)
.attr("cursor", "nw-resize")
.attr("x", "{{square.x-2.5}}")
.attr("y", "{{square.y-2.5}}")
.call(nresize);
I've updated your code with this solution here.
I'm fairly sure if I can find an example showing what I'm trying to do, I can reverse engineer/reimplement it. Has anyone seen an example showing a smooth/animated transition between a linear and log scale in D3JS?
I have both scales working independently, but I have to reload the page to change the scale.
My Google skills have failed me!
Thanks so much.
Here's a proof of concept jsfiddle. You simply reselect the data points and redraw them with the new scale. For the axis labels, the transition is even simpler -- you just need to call the axis function again. Relevant excerpt below.
// change to log scale...
yScale = d3.scale.log().domain([1, 100]).range([dim-padding,padding]);
svg.selectAll("circle").data(data)
.transition().delay(1000).duration(1000)
.attr("cx", function(d) { return xScale(d); })
.attr("cy", function(d) { return yScale(d); });
svg.selectAll(".y")
.transition().delay(1000).duration(1000)
.call(yAxis.scale(yScale));
You might need to play around with how the labels are generated to make it look "nice", but in principle d3 will take care of the entire transition.