Skip to content

Instantly share code, notes, and snippets.

@ubershmekel
Created February 20, 2013 08:01
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save ubershmekel/4993799 to your computer and use it in GitHub Desktop.
Save ubershmekel/4993799 to your computer and use it in GitHub Desktop.
A d3js animation of sine and cosine with different base shapes.
<!DOCTYPE html>
<html manifest="main.appcache">
<meta charset="utf-8">
<style>
.circle, .wave {
fill: none;
stroke: steelblue;
stroke-width: 1.5px;
}
.circle {
stroke: black;
}
#myshape {
stroke: black;
stroke-width: 1.5px;
fill: none;
}
.axis {
stroke: black;
stroke-width: 1px;
}
.edge {
stroke: #ccc;
stroke-width: 1px;
}
#xline {
stroke: #f00;
opacity: 0.5;
stroke-width: 2.0px;
}
#yline {
stroke: #00f;
opacity: 0.5;
stroke-width: 2.0px;
}
#dot {
fill: black;
}
#radius {
stroke: #0f0;
opacity: 0.5;
stroke-width: 2.0px;
}
.filler {
fill: white;
}
#ywave .wave {
stroke: #00f;
}
#xwave .wave {
stroke: #f00;
}
</style>
<body>
<script src="http://d3js.org/d3.v3.js"></script>
<div id="vis">
</div>
<script>
var INTERVAL = Math.PI / 60;
var INTERVALS_OF_FOUR_CYCLES = Math.ceil(Math.PI * 8 / INTERVAL);
// Precompute sin and tan waves
//var d = d3.range(0, Math.PI / 2 + INTERVAL, INTERVAL),
//sinWave = d.map(Math.sin),
//tanWave = d.map(Math.tan);
// Remove problematic "infinite" point
//tanWave.pop();
var w = 600, h = 400,
x = d3.scale.linear().domain([-2, 10]).range([0, w]),
y = x,
r = (function(a, b) {
return Math.sqrt(a * a + b * b);
})(x.invert(w), y.invert(h));
var vis = d3.select("#vis").append("svg")
.attr("width", w).attr("height", h);
var jaggyCos = function(t) {
// we just want to define from 0 to 2*pi
var part = (t / Math.PI) % 2.0;
if (part < 1.0) {
return 2 * (0.5 - part);
}
if (part < 2.0) {
return 2 * (-1.5 + part);
}
console.log('Error, should be unreachable ' + t + ' - ' + part);
return part;
};
var jaggySin = function(t) {
var part = (t / Math.PI) % 2.0;
if (part < 0.5) {
return 2 * part;
}
if (part < 1.5) {
return 2 * (1.0 - part);
}
if (part < 2.0) {
return 2 * (-2.0 + part);
}
console.log('Error, should be unreachable ' + t + ' - ' + part);
//return part;
};
var sincify = function(t) {
// [0, 1] -> [0, 1] like a sin function
return (1 + Math.sin(-Math.PI / 2 + t * Math.PI)) / 2;
}
var squareCos = function(t) {
// square Y
var part = (t / Math.PI) % 2.0;
if (part < 0.25) {
return 1.0;
}
if (part < 0.75) {
var segment = (part - 0.25) * 2;
return 1.0 - sincify(segment) * 2;
}
if (part < 1.25) {
//var segment = (part - 0.75) * 2;
return -1.0;
}
if (part < 1.75) {
var segment = (part - 1.25) * 2;
return -1.0 + sincify(segment) * 2;
}
if (part < 2.0) {
return 1.0;
}
console.log('Error, should be unreachable ' + t + ' - ' + part);
return part;
}
var squareSin = function(t) {
// X
var part = (t / Math.PI) % 2.0;
if (part < 0.25) {
return 4 * part;
}
if (part < 0.75) {
return 1.0;
}
if (part < 1.25) {
return 1.0 + 4 * (0.75 - part);
}
if (part < 1.75) {
return -1.0;
}
if (part < 2.0) {
return -1.0 + 4 * (part - 1.75);
}
console.log('Error, should be unreachable ' + t + ' - ' + part);
};
// Imagine that alpha is from the Y Axis towards the positive X axis
var xFunc = Math.sin;
var yFunc = Math.cos;
//yFunc = Math.acos;
//xFunc = jaggySin;
//yFunc = jaggyCos;
//xFunc = squareSin;
//yFunc = squareCos;
var polarCircle = function(t) {
return 1;
}
var quarterCircle = Math.PI / 2;
var eigthCircle = Math.PI / 4;
var squareDiagonal = Math.sqrt(2) / 2;
var polarDiamond = function(t) {
var part = (t % quarterCircle) - quarterCircle / 2;
return squareDiagonal / Math.cos(part);
}
var polarSquare = function(t) {
var part = ((t + eigthCircle) % quarterCircle) - eigthCircle;
return squareDiagonal / Math.cos(part);
}
xPolar = function(polarFunc) {
return function(t) {
return polarFunc(t) * Math.sin(t)
}
}
yPolar = function(polarFunc) {
return function(t) {
return polarFunc(t) * Math.cos(t)
}
}
var polarFunc;
polarActive = polarCircle;
polarActive = polarSquare;
xFunc = xPolar(polarActive);
yFunc = yPolar(polarActive);
/*
vis.append("g")
.attr("id", "xwave")
.attr("width", w)
.attr("height", h)
//.attr("transform", "translate("+x(1)+")")
.selectAll("path")
.data([d3.range(0, 8 * Math.PI + INTERVAL, INTERVAL).map(yFunc)])
.enter().append("path")
.attr("class", "wave")
.attr("d", d3.svg.line()
.x(function(d, i) { return x(i * INTERVAL) })
.y(function(d) { return y(d) }));
*/
/*var yWave = vis.append("g")
.attr("id", "ywave")
.attr("width", w)
.attr("height", h);
yWave.selectAll("path")
.data([d3.range(0, 8 * Math.PI + INTERVAL, INTERVAL).map(xFunc)])
.enter().append("path")
.attr("class", "wave")
.attr("d", d3.svg.line()
.x(function(d) { return x(d) })
.y(function(d, i) { return y(i * INTERVAL) - y(0) }))*/
var yWave = vis.append("g")
.attr("id", "ywave")
.attr("width", w)
.attr("height", h);
var yWaveData = [];
var yPath = yWave.selectAll("path")
.data([yWaveData]);
yPath.enter().append("path")
.attr("class", "wave");
var xWave = vis.append("g")
.attr("id", "xwave")
.attr("width", w)
.attr("height", h);
var xWaveData = [];
var xPath = xWave.selectAll("path")
.data([xWaveData]);
xPath.enter().append("path")
.attr("class", "wave");
// hides the waves under the circle shape
var filler = function(w, h) {
return vis.append("rect")
.attr("class", "filler")
.attr("width", w)
.attr("height", h);
}
var lineWidth = 1.5;
//filler(x(1) + lineWidth, y(1) + lineWidth);
vis.append("g")
.attr("id", "myshape")
.attr("width", w)
.attr("height", h)
.selectAll("path")
.data([d3.range(0, 2 * Math.PI, INTERVAL)])
.enter().append("path")
.attr("class", "myshape")
.attr("d", d3.svg.line()
.x(function(d, i) { return x(xFunc(d)) })
.y(function(d) { return y(yFunc(d)) }));
var line = function(e, x1, y1, x2, y2) {
return e.append("line")
.attr("class", "line")
.attr("x1", x1)
.attr("y1", y1)
.attr("x2", x2)
.attr("y2", y2);
}
var axes = function(cx, cy, cls) {
cx = x(cx); cy = y(cy);
line(vis, cx, 0, cx, h).attr("class", cls || "line")
line(vis, 0, cy, w, cy).attr("class", cls || "line")
}
axes(0, 0, "axis");
axes(1, 1, "edge");
var dotRadius = 5
line(vis, 0, y(0), w, y(0))
.attr("id", "xline")
line(vis, x(0), 0, x(0), h)
.attr("id", "yline")
var radiusLine = line(vis, x(0), y(0), x(0), y(1))
.attr("id", "radius")
vis.append("circle")
.attr("class", "circle")
.attr("id", "dot")
.attr("cx", x(0))
.attr("cy", y(0))
.attr("r", dotRadius)
var offset = 0;
var elapsedRadians = 0;
var lastTime = 0;
var milisecPerRadian = 500;
d3.timer(function(elapsed) {
elapsedRadians = elapsed / milisecPerRadian;
offset = elapsedRadians % (2 * Math.PI);
/*lastTime = elapsed;
if (offset > 2 * Math.PI) {
offset = 0;
}*/
vis.selectAll("#xwave") //, #tanwave")
.attr("transform", "translate(" + x(elapsedRadians - 1) + ",0)")
vis.selectAll("#ywave")
.attr("transform", "translate(0," + y(elapsedRadians - 1) + ")")
var xline = y(yFunc(offset)) - y(0);
var yline = x(xFunc(offset)) - x(0);
var nowX = x(xFunc(offset));
var nowY = y(yFunc(offset));
yWaveData.push({x: nowX, y: y(-elapsedRadians)});
xWaveData.push({x: x(-elapsedRadians), y: nowY});
if(yWaveData.length > INTERVALS_OF_FOUR_CYCLES) {
yWaveData.shift();
xWaveData.shift();
}
xPath.attr("d", d3.svg.line()
.x(function(d) { return d.x })
.y(function(d) { return d.y }));
yPath.attr("d", d3.svg.line()
.x(function(d) { return d.x })
.y(function(d) { return d.y }));
radiusLine.attr("x2", nowX);
radiusLine.attr("y2", nowY);
vis.selectAll("#dot") //, #tanline")
.attr("transform", "translate(" + yline + "," + xline + ")")
//.attr("transform", "rotate(" + (180 - offset * 180 / Math.PI) + ","+x(0)+","+y(0)+")")
vis.select("#xline")
.attr("transform", "translate(0," + xline + ")");
vis.select("#yline")
.attr("transform", "translate(" + yline + ",0)");
});
</script>
</body>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment