Skip to content

Instantly share code, notes, and snippets.

@jameshenegan
Last active September 28, 2019 16:09
Show Gist options
  • Save jameshenegan/1cd25c95f75d16cd2083ed5434bd33a0 to your computer and use it in GitHub Desktop.
Save jameshenegan/1cd25c95f75d16cd2083ed5434bd33a0 to your computer and use it in GitHub Desktop.
Approximating a Square Wave
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Fourier Approximation</title>
<script src="https://d3js.org/d3.v5.min.js"></script>
</head>
<body>
<div id="viz">
</div>
<script src="index.js"></script>
</body>
</html>
// Time-length (in milliseconds) before graph updates
const intervalLength = 1000
// Number of fourier approximations before resetting
const numIterations = 15
var margin = { top: 10, right: 30, bottom: 30, left: 60 },
height = 500 - margin.top - margin.bottom;
var width = 1.618 * height - margin.left - margin.right
// append the svg object to the body of the page
var svg = d3.select("#viz")
.append("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.append("g")
.attr("transform",
"translate(" + margin.left + "," + margin.top + ")");
// Create a linear space
// Start: xMin, Stop: xMax, n: numSteps
function linSpace(start, stop, n) {
var arr = [];
const step = (stop - start) / n
for (var loc = start; loc <= stop; loc = loc + step) {
arr.push(loc)
}
return arr;
}
var myDomain = linSpace(-2 * Math.PI, 2 * Math.PI, 1000)
// Scales and Axes
var x = d3.scaleLinear()
.domain(d3.extent(myDomain))
.range([0, width]);
svg.append("g")
.attr("transform", `translate(0,${height / 2})`)
.call(d3.axisBottom(x).ticks(2));
var y = d3.scaleLinear()
.domain([-1.1, 1.1])
.range([height, 0]);
svg.append("g")
.attr("transform", `translate(${width / 2},0)`)
.call(d3.axisLeft(y).ticks(4));
// ###########################
// construct the k-th approximation of the square wave
function approxSquare(k, x) {
if (k == 0) {
return Math.sin(x)
} else {
return Math.sin((2 * k + 1) * x) / (2 * k + 1) + approxSquare(k - 1, x)
}
}
function drawSquareWave() {
const myFunction = x => approxSquare(1000, x)
var myImage = myDomain.map(d => myFunction(d, 0))
var data = d3.zip(myDomain, myImage)
svg.append("path")
.datum(data)
.attr("fill", "none")
.attr("stroke", "maroon")
.attr("stroke-width", 1.5)
.attr("d", d3.line()
.x(d => x(d[0]))
.y(d => y(d[1]))
)
.style("opacity", "0.5")
}
function drawFirstGraph() {
const myFunction = x => approxSquare(0, x)
var myImage = myDomain.map(d => myFunction(d, 0))
var data = d3.zip(myDomain, myImage)
svg.append("path")
.datum(data)
.attr("fill", "none")
.attr("stroke", "steelblue")
.attr("stroke-width", 1.5)
.attr("id", "mainPath")
.attr("d", d3.line()
.x(d => x(d[0]))
.y(d => y(d[1]))
)
}
function updateGraph(k) {
const myFunction = x => approxSquare(k, x)
const myImage = myDomain.map(d => myFunction(d))
const localData = d3.zip(myDomain, myImage)
const path = d3.selectAll("#mainPath").datum(localData)
path.transition()
.duration(intervalLength)
.attr("d", d3.line()
.x(d => x(d[0]))
.y(d => y(d[1]))
)
}
drawFirstGraph()
drawSquareWave()
var iteration = 0
d3.interval(() => {
if (iteration < numIterations) {
iteration = iteration + 1
updateGraph(iteration)
}
else {
iteration = 0
updateGraph(iteration)
}
}, intervalLength)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment