Skip to content

Instantly share code, notes, and snippets.

@anbnyc
Last active July 26, 2017 17:52
Show Gist options
  • Save anbnyc/1d8d1f9ba3f9d236ed1df3160d4f1d8b to your computer and use it in GitHub Desktop.
Save anbnyc/1d8d1f9ba3f9d236ed1df3160d4f1d8b to your computer and use it in GitHub Desktop.
Exploring d3.timer and trigonometry

d3.timer and trigonometry

This gist uses d3.timer to simulate motion by continuously updating the positions of elements. Two points move along the circumference of two circles each, lines connect the points on the left circle with the points on the right circle, and the bar graph below shows the full-scale length of the lines in real time. Together, the bars follow an almost rhythmic cyclical pattern, all following from the movement of the points along simple sine/cosine curves.

This project was loosely inspired by Baroque.me.

<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/4.2.6/d3.min.js"></script>
<script src="https://code.jquery.com/jquery-3.1.1.min.js" integrity="sha256-hVVnYaiADRTO2PzUGmuLJr8BLUSjGIZsDYGmIJLv2b8=" crossorigin="anonymous"></script>
<form>Speed factor <input id="speed" name="speed" type="number" value="1"></input></form>
<div id="viz"></div>
<script>
var dim = 600;
var lookup = [[0,1],[0,3],[2,1],[2,3]];
var colors = {
bars: ["#800080","#D55555","#008000","#D5D555"],
circles: ["#ff0000","#0000ff","#ffff00","#aaaaaa"]
};
var svg = d3.select("#viz")
.append("svg")
.attr("height",.75*dim)
.attr("width",dim)
.append("g");
var little = svg.selectAll("g.little")
.data([0,1,2,3]).enter()
.append("g")
.attr("class","little")
.attr("transform",(d) => "translate("+(dim * (1+d%2)/3)+","+(dim/3)+")");
little
.append("circle")
.attr("fill",(d) => colors.circles[d])
.attr("r",5);
little
.append("text")
.style("font-size",12)
.text((d) => d);
var big = svg.selectAll("circle.big")
.data([0,1]).enter()
.append("circle")
.attr("stroke","black")
.attr("fill-opacity",0)
.attr("class","big")
.attr("r",dim/6)
.attr("transform",(d,i) => "translate("+(dim * (1+i%2)/3)+","+(dim/3)+")")
.attr("cx",0)
.attr("cy",0);
var bar = svg.selectAll("g.bar")
.data([0,1,2,3]).enter()
.append("g")
.attr("class","bar")
.attr("transform","translate(0,"+.55*dim+")")
bar
.append("rect")
.attr("height",20)
.attr("fill","black")
.attr("x",35)
.attr("y",(d,i) => i*25);
bar
.append("text")
.attr("y",(d,i) => i*25 + 15);
var lines = svg.selectAll("path")
.data([0,1,2,3]).enter()
.append("path")
.attr("class","line")
.attr("stroke-width",3)
.attr("transform",(d) => "translate("+(dim/3)+","+(dim/3)+")");
var t = d3.timer(function(elapsed){
speed = $('input#speed').val()/1000;
little
.selectAll("text")
.attr("x",(d) => 5 + trigX(elapsed,d))
.attr("y",(d) => trigY(elapsed,d) - 5)
little
.selectAll("circle")
.attr("cx",(d) => trigX(elapsed,d))
.attr("cy",(d) => trigY(elapsed,d));
bar.selectAll("rect")
.attr("width",(d) => getD(lookup[d%4][0],lookup[d%4][1]))
.attr("fill",(d) => colors.bars[d%4]);
bar.selectAll("text")
.text((d) => JSON.stringify(lookup[d%4]));
lines
.attr("stroke",(d) => colors.bars[d%4])
.attr("d",(d) => {
return "M"+trigX(elapsed,lookup[d%4][0])+","+
trigY(elapsed,lookup[d%4][0])+
"L"+((dim/3)+trigX(elapsed,lookup[d%4][1]))+","+
trigY(elapsed,lookup[d%4][1]);
});
});
function distance(a,b){
var d = Math.sqrt(
((a.attr("cx")-b.attr("cx")-(dim/3))*
(a.attr("cx")-b.attr("cx")-(dim/3))) +
((a.attr("cy")-b.attr("cy"))*
(a.attr("cy")-b.attr("cy")))
);
return d;
}
function getD(a,b){
return distance(little.filter((d) => d==a).select("circle"),
little.filter((d) => d==b).select("circle"));
}
function trigX(input,d){
// flip 2,3
var flip = (d >= 2 & d%2 === 1) | (d >= 2 & d%2 === 0) ? -1 : 1;
var phase = (d%2 === 0) ? .5*Math.PI : 0;
return flip*dim/6*Math.cos(speed*input - phase);
}
function trigY(input,d){
// flip 1,2
var flip = (d < 2 & d%2 === 1) | (d >= 2 & d%2 === 0) ? 1 : -1;
var phase = (d%2 === 0) ? .5*Math.PI : 0;
return flip*dim/6*Math.sin(speed*input - phase);
}
</script>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment