Skip to content

Instantly share code, notes, and snippets.

@topologicallytony
Last active August 18, 2017 16:54
Show Gist options
  • Save topologicallytony/84996757de5c01c6155bcd67955b89c6 to your computer and use it in GitHub Desktop.
Save topologicallytony/84996757de5c01c6155bcd67955b89c6 to your computer and use it in GitHub Desktop.
Wave 3
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Wave 3</title>
<!-- D3.js -->
<script src="https://d3js.org/d3.v4.min.js"></script>
<!-- Latest compiled and minified CSS -->
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.2/css/bootstrap.min.css">
<style type="text/css">
</style>
</head>
<body>
<div id=svgContainer></div>
<script type="text/javascript">
//This visualization demonstrates the importance of proper sampling
//The visual starts off with a sine wave sampled by a fixed number of points
//The amplitude of the sine wave slowly increases
//This is visually interesting because of all the patterns that emerge, and the visual will eventually restart (even though the function it is sampled off of is completely different!)
//Width and height of the visualization. Smaller numbers will zoom in, larger numbers zoom out
var w = 600;
var h = 450;
//padding creates a buffer of white space around the chart to make it a little easier to look at
var padding = 20;
//This will hold the sampled function's data
var dataset = [];
//How finely to partition interval
//Fun examples - 729 points, 128/256/512 amp with interval of either 2,3,4,6,12
// 729 points, 243 amp with interval of 2pi
//600 points, 81 amp with interval of pi
//how finely will the function be sampled
var numDataPoints = 100;
//duration of a single transition in ms (for Sine this is the period)
var time = 2000;
//how fast will the amplitude increase - another way to adjust time
var step = 1;
//initialize amplitude of the function
var amp = 0;
//size of the circles, more data points should have smaller radii
var radius = 3;
//define y axis (domain of function)
//Note: if domain is 2pi then animation will reset after [numDataPoints] iterations
var y_min = 0;
var y_max = 2 * Math.PI;
//define x axis domain (range of function)
var x_min = 2;
var x_max = -2;
//Generate a random sample of the space
for (var i = 0; i < numDataPoints; i++) {
//Partition interval
var curr_y = ((i * (y_max - y_min)) / (numDataPoints - 1)) + y_min;
//determine function's value at current point
//var curr_x = Math.exp(curr_y / 2) * Math.sin(amp*curr_y);
var curr_x = Math.sin(curr_y / 2) * Math.sin(amp * curr_y);
//var curr_x = Math.exp(Math.abs(curr_y) - 5) * Math.sin(amp*curr_y);
//var curr_x = Math.sin(amp*curr_y);
dataset.push([curr_x, curr_y]);
}
//Create x & y scales to convert points to proper svg canvas positions
var xScale = d3.scaleLinear()
.domain([x_min, x_max])
.range([padding, w + padding]);
var yScale = d3.scaleLinear()
.domain([y_min, y_max])
.range([h + padding, padding]);
//Define the format for the number
var format = d3.format(",.1f");
//Create a canvas to display the chart on
var svg = d3.select("body")
.append("svg")
.attr("width", w)
.attr("height", h);
svg.append("g")
.call(d3.zoom().scaleExtent([1,8]).on("zoom", zoom));
//Make the background of the canvas white
svg.append("rect")
.attr("width", "100%")
.attr("height", "100%")
.attr("fill", "#fff");
//Create group elements to layer the svg
//This is an easy way to make sure things you want on top are on top, and things you want behind stay behind
var layer1 = svg.append('g');
var layer2 = svg.append('g');
//'draw' the function using points sampled at a regular interval
function drawPts() {
var pts = layer2
.selectAll(".points")
.data(dataset);
pts
.enter()
.append("circle")
.attr("class", "points")
.attr("cy", function(d){ return yScale(d[1]); })
.attr("cx", function(d){ return xScale(d[0]); })
.attr("r", radius)
.attr("fill","steelblue");
pts
.transition()
.duration(0)
.on("start", function repeat() {
d3.active(this)
.transition()
.ease(d3.easeLinear)
.duration(time)
//transition the x position (aka the function value) from its current value to to its new value (the value after incrementing the amplitude). The transition path is determined by linearly interpolating the amplitude of the sine function between the two points
.attrTween('cx', function(d){
return function(t) {
//mult step
//return xScale(Math.sin(d[1]) * Math.sin((amp * (step - 1) * t + amp) * d[1]));
//add step
//return xScale(Math.exp(d[1] / 2) * Math.sin(d[1] * (amp + t * step)));
return xScale(Math.sin(d[1] / 2) * Math.sin(d[1] * (amp + t * step)));
//return xScale(Math.sin(d[1] * (amp + t * step)));
}
})
});
}
//display a counter to show the current amplitude of sine function
function counter(){
var amp_ctr = layer2.selectAll(".amp")
.data([0]);
amp_ctr
.transition()
.ease(d3.easeLinear)
.duration(time)
.on("start", function repeat() {
d3.active(this)
.tween("text", function() {
var that = d3.select(this),
i = d3.interpolateNumber(amp, amp + step);
return function(t) { that.text(format(i(t))); };
});
});
amp_ctr.enter().append("text")
.attr("x", 2 * padding)
.attr("y", 2 * padding)
.text(amp)
.attr("font-size", "12px")
.attr("class", "amp")
.transition()
.ease(d3.easeLinear)
.duration(time)
.on("start", function repeat() {
d3.active(this)
.tween("text", function() {
var that = d3.select(this),
i = d3.interpolateNumber(amp, amp + step);
return function(t) { that.text(format(i(t))); };
});
});
amp_ctr.exit().remove();
}
//initialize the animation
drawPts();
counter();
setInterval(function() {
//mult step
//amp = amp * step;
//add step
amp = amp + step;
drawPts();
counter();
}, time);
drawPts();
counter();
function zoom() {
svg.attr("transform", "translate(" + d3.event.translate + ")scale(" + d3.event.scale + ")");
}
</script>
</body>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment