D3: Tumbling dice (unwantedly) - animation

I want - with help of D3 - to rotate an object around its center with varying speed. Everything works fine as long as the rotation speed isn't too high. But eventually the object starts tumbling even though the rotation center is (visibly) fixed.
Find the tumbling dice at http://jsfiddle.net/mcqnpgn6/`
// initialize rotation angles
var dAngle = 10
var angles = []
var angle = 0;
for (i = 0; i < T; i++) {
angles[i] = angle
angle += dAngle > 0 ? dAngle : i
}
// create dice ....
// rotate dice
for (i = 0; i < T; i++) {
dice.transition()
.delay(i * dt)
.duration(dt)
.ease("linear")
.attr("transform", "rotate(" + angles[i] + "," + cx + "," + cy + ")")
}
What did I do or understand wrongly? How would you solve this task?

Elaborating on my comment: http://jsfiddle.net/mcqnpgn6/25/. Key section below:
var theta = dAngle;
function rotate(){
theta += dAngle;
dice.attr("transform", "rotate(" + theta + "," + cx + "," + cy + ")")
window.requestAnimationFrame(rotate);
}
rotate();

Related

JavaScript: how to perform a movement and rotation of an object like in RTS game?

The problem is follow:
when the object has a positive angle and if negative angle is clicked - it follows logic and makes a counterclockwise rotation instead of clockwise rotation like it requires and vice versa. It doesn`t look like rotation of a unit in RTS games. How to fix it?
`
var object = document.createElement("div");
object.style.backgroundColor = "red";
object.style.width = "50px";
object.style.height = "50px";
object.style.position = "absolute";
object.style.transition = "transform 3s";
document.body.appendChild(object);
function action(event){
mousePosition = {
x : event.clientX - (object.offsetWidth/2),
y : event.clientY - (object.offsetHeight/2)
};
let unitBoundingRect = object.getBoundingClientRect();
let unitCenter = {
x: unitBoundingRect.left + unitBoundingRect.width/2,
y: unitBoundingRect.top + unitBoundingRect.height/2
}
let angle = Math.atan2(event.pageX - unitCenter.x, - (event.pageY - unitCenter.y))*(180 / Math.PI);
object.style.transform = "translate(" + mousePosition.x + "px," + mousePosition.y + "px) rotate(" + angle + "deg)";
object.innerText = Math.floor(angle);
}
window.addEventListener("click",action,false);
`

How to make a spiral rotate?

I have successfully coded a static spiral using lines, and now I'm supposed to make the spiral rotate from frame to frame. I tried incrementing the angle used for the x and y positions of the end of the lines with each frame, but the spiral doesn't move at all.
void draw() {
for (int i = 0; i < 15 * NUM_LINES; i++) {
float lineEndX = width / 2 + radius * cos(angle + startAngle);
float lineEndY = height / 2 + radius * sin(angle + startAngle);
line (lineStartX, lineStartY, lineEndX, lineEndY);
lineStartX = lineEndX;
lineStartY = lineEndY;
radius = radius + 0.047;
angle += 0.01 % (TWO_PI * NUM_TURNS);
}
startAngle += START_ANGLE_CHANGE;
angle = 0;
}
Add background(255); to your draw function. Also define lineStartX, lineStartY and radius there so their values are reset every time the function is called.
void draw() {
background(255);
float lineEndX = width / 2;
float lineEndY = height / 2;
float radius = 5;
for (int i = 0; i < 15 * NUM_LINES; i++) {
float lineEndX = width / 2 + radius * cos(angle + startAngle);
float lineEndY = height / 2 + radius * sin(angle + startAngle);
line (lineStartX, lineStartY, lineEndX, lineEndY);
lineStartX = lineEndX;
lineStartY = lineEndY;
radius = radius + 0.047;
angle += 0.01 % (TWO_PI * NUM_TURNS);
}
startAngle += START_ANGLE_CHANGE;
angle = 0;
}
Working example here.

Rotate every arc of pie chart 180 (like sun) with D3 JS. How to calculate translate parameters

I am working on pie chart with d3 js. I want to rotate every arc of my pie chart 180. I know that I am unable to explain completely show here is my fiddle link.
[fiddle]: https://jsfiddle.net/dsLonquL/
How can i get dynamic parameters for translate() function.
Basically you need to work out the centre point of the edge of each arc. I used this example for help : How to get coordinates of slices along the edge of a pie chart?
This works okay, but I needed to rotate the points to get them in the correct positions. As it is in radians the rotation is the following :
var rotationInRadians = 1.5708 * 1.5;
Now using the example before I used the data for the paths, so the start and end angle and got the center points like so :
var thisAngle = (d.startAngle + rotationInRadians + (d.endAngle + rotationInRadians - d.startAngle + rotationInRadians) / 2);
var x = centreOfPie[0] + radius * 2 * Math.cos(thisAngle)
var y = centreOfPie[1] + radius * 2 * Math.sin(thisAngle)
I created a function to show circles at these points to clarify :
function drawCircle(points, colour) {
svg.append('circle')
.attr('cx', points[0])
.attr('cy', points[1])
.attr('r', 5)
.attr('fill', colour);
}
Called it inside the current function like so :
drawCircle([x, y], color(d.data.label))
And then translated and rotated accordingly :
return 'translate(' + (x) + ',' + y + ') rotate(180)';
I added a transition so you can see it working. Here is the final fiddle :
https://jsfiddle.net/thatOneGuy/dsLonquL/7/
EDIT
In your comments you say you want the biggest segment to be kept in the middle. So we need to run through the segments and get the biggest. I have also taken care of duplicates, i.e if two or more segments are the same size.
Here is the added code :
var biggestSegment = {
angle: 0,
index: []
};
path.each(function(d, i) {
var thisAngle = (d.endAngle - d.startAngle).toFixed(6);//i had to round them as the numbers after around the 7th or 8th decimal point tend to differ tet theyre suppose to be the same value
if (i == 0) {
biggestSegment.angle = thisAngle
} else {
if (biggestSegment.angle < thisAngle) {
biggestSegment.angle = thisAngle;
biggestSegment.index = [i];
} else if (biggestSegment.angle == thisAngle) {
console.log('push')
biggestSegment.index.push(i);
}
}
})
Now this goes through each path checks if its bigger than the current value, if it is overwrite the biggest value and make note of the index. If its the same, add index to index array.
Now when translating the paths, you need to check the current index against the index array above to see if it needs rotating. Like so :
if (biggestSegment.index.indexOf(i) > -1) {
return 'translate(' + (centreOfPie[0]) + ',' + (centreOfPie[1]) + ')' // rotate(180)';
} else {
return 'translate(' + (x) + ',' + y + ') rotate(180)';
}
Updated fiddle : https://jsfiddle.net/thatOneGuy/dsLonquL/8/
I have editted 3 values to be different to the rest. Go ahead and change these, see what you think :)
This is a pure middle school geometry job.
CASE 1: The vertex of each sector rotation is on the outer line of the circle
fiddle
// ... previous code there
.attr('fill', function(d, i) {
return color(d.data.label);
})
.attr("transform", function(d, i) {
var a = (d.endAngle + d.startAngle) / 2, // angle of vertex
dx = 2 * radius * Math.sin(a), // shift/translate is two times of the vertex coordinate
dy = - 2 * radius * Math.cos(a); // the same
return ("translate(" + dx + " " + dy + ") rotate(180)"); // output
});
CASE 2: The vertex on the center of the chord
fiddle
// ... previous code there
.attr('fill', function(d, i) {
return color(d.data.label);
})
.attr("transform", function(d, i) {
var dx = radius * (Math.sin(d.endAngle) + Math.sin(d.startAngle)), // shift/translation as coordinate of vertex
dy = - radius * (Math.cos(d.endAngle) + Math.cos(d.startAngle)); // the same for Y
return ("translate(" + dx + " " + dy + ") rotate(180)"); // output
});

Trying to get only one tooltip to show on mouseover using d3

I am trying get some tooltips working on my d3 code. I've simplified this code as shown below.
Currently the code produces 6 moving circles with names associated to them, and a console.log shows that when I mouseover over each circle, it's associated name is logged.
Also, when I mouseover a circle, the name labels appear near to the circles. However, the labels appear over all of the circles, and I only want a label to appear over the circle that the cursor is hovering over.
I'm not interested in adding a mouseout or anything just yet, just want to get it so that only the hovered over circle gets a label displayed. Any advice on how to do this?
Here is what my code currently looks like:
<!DOCTYPE html>
<meta charset="utf-8">
<body>
<script src="//d3js.org/d3.v3.min.js"></script>
<script type="text/javascript">
var names = ["Jack","Anne","Jacob","Mary","Michael","Lisa"];
var width = 960,
height = 500;
var n = names.length,
m = 12,
degrees = 180 / Math.PI;
var bubbles = d3.range(n).map(function() {
var x = Math.random() * width,
y = Math.random() * height;
return {
vx: Math.random() * 2 - 1,
vy: Math.random() * 2 - 1,
path: d3.range(m).map(function() { return [x, y]; }),
count: 0
};
});
var svg = d3.select("body").append("svg")
.attr("width", width)
.attr("height", height);
var g = svg.selectAll("g")
.data(bubbles)
.enter().append("g")
.on("mouseover", function(d,i){console.log(names[i])});
var labels = g.selectAll("text")
.data(bubbles)
.enter().append("text")
.attr("dy",".35em")
.attr("class", "tooltip")
.style("visibility", "hidden")
.text(function(d,i){return names[i]})
var head = g.selectAll("circle")
.data(bubbles)
.enter().append("circle")
.attr("r", 6)
.on("mouseover", function(d,i) {
labels.style("visibility","visible")
})
d3.timer(function() {
for (var i = -1; ++i < n;) {
var bubble = bubbles[i],
path = bubble.path,
dx = bubble.vx,
dy = bubble.vy,
x = path[0][0] += dx,
y = path[0][1] += dy,
speed = Math.sqrt(dx * dx + dy * dy),
count = speed * 10,
k1 = -5 - speed / 3;
if (x < 0 || x > width) bubble.vx *= -1;
if (y < 0 || y > height) bubble.vy *= -1;
for (var j = 0; ++j < m;) {
var vx = x - path[j][0],
vy = y - path[j][1],
k2 = Math.sin(((bubble.count += count) + j * 3) / 300) / speed;
path[j][0] = (x += dx / speed * k1) - dy * k2;
path[j][1] = (y += dy / speed * k1) + dx * k2;
speed = Math.sqrt((dx = vx) * dx + (dy = vy) * dy);
}
}
labels.attr("transform", labelsTransform);
head.attr("transform", headTransform);
});
function headTransform(d) {
return "translate(" + d.path[0] + ")rotate(" + Math.atan2(d.vy, d.vx) * degrees + ")";
}
function labelsTransform(d) {
return "translate(" + d.path[0] + ")translate(10)";
}
</script>
<body>
</html>
This is my first question on Stack Overflow, so sorry if my question is badly formatted or presented! Any help would be much appreciated, even if it's about how to better present my question!
Welcome to Stack Overflow! :)
The issue you're having can be traced back to this section of code:
var head = g.selectAll("circle")
.data(bubbles)
.enter().append("circle")
.attr("r", 6)
.on("mouseover", function(d,i) {
labels.style("visibility","visible")
})
What this says is that when the user mouseovers any circle, make all labels visible. You probably want something like this
.on("mouseover", function(d,i) {
// only make visible the current mouseover-ed point
labels.filter(function(p){
if(p === d) d3.select(this).style("visibility","visible");
else d3.select(this).style("visibility","hidden");
});
})

D3 Force Layout Graph - Self linking node

Using a force directed graph, how do you get a link to actually show up when the target and source are the same node. So basically just a nice little loop indicating that such an edge exists.
There are two D3 examples that I already used or tried to use:
I'm using http://bl.ocks.org/d3noob/5155181 to show direction, and
the little endpoint arrow will show up pointing at itself, but no
link line.
http://bl.ocks.org/GerHobbelt/3616279 does does allow for self
referencing and I even sort of got it to work with my data, but it is
crazy complicated.
The trick is to draw the self link as a path with an arc in it. It took me a bit of fiddling with the arc parameter syntax to get things working and the key seemed to be that the arc could not start and end at the same point. Here is the relevant code that draws the edges at each update.
function tick() {
link.attr("d", function(d) {
var x1 = d.source.x,
y1 = d.source.y,
x2 = d.target.x,
y2 = d.target.y,
dx = x2 - x1,
dy = y2 - y1,
dr = Math.sqrt(dx * dx + dy * dy),
// Defaults for normal edge.
drx = dr,
dry = dr,
xRotation = 0, // degrees
largeArc = 0, // 1 or 0
sweep = 1; // 1 or 0
// Self edge.
if ( x1 === x2 && y1 === y2 ) {
// Fiddle with this angle to get loop oriented.
xRotation = -45;
// Needs to be 1.
largeArc = 1;
// Change sweep to change orientation of loop.
//sweep = 0;
// Make drx and dry different to get an ellipse
// instead of a circle.
drx = 30;
dry = 20;
// For whatever reason the arc collapses to a point if the beginning
// and ending points of the arc are the same, so kludge it.
x2 = x2 + 1;
y2 = y2 + 1;
}
return "M" + x1 + "," + y1 + "A" + drx + "," + dry + " " + xRotation + "," + largeArc + "," + sweep + " " + x2 + "," + y2;
});
And here is a jsfiddle that demonstrates the whole thing, and a screenshot:

Resources