Skip to content

Instantly share code, notes, and snippets.

@efekarakus
Last active March 5, 2016 03:00
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 efekarakus/9e5d933195dee8b4a882 to your computer and use it in GitHub Desktop.
Save efekarakus/9e5d933195dee8b4a882 to your computer and use it in GitHub Desktop.
Convolution
(function (global, factory) {
typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) :
typeof define === 'function' && define.amd ? define(['exports'], factory) :
(factory((global.d3_peaks = global.d3_peaks || {})));
}(this, function (exports) { 'use strict';
/**
* See https://en.wikipedia.org/wiki/Mexican_hat_wavelet
*/
function ricker() {
var σ = 1;
var ricker = function(t) {
var t2 = t*t,
variance = σ*σ;
var C = 2.0 / ( Math.sqrt(3 * σ) * (Math.pow(Math.PI, 0.25)) );
var norm = (1.0 - (t2)/(variance));
var gauss = Math.exp( -(t2) / (2*variance) );
return C*norm*gauss;
}
ricker.std = function(_) {
return arguments.length ? (σ = _, ricker) : σ;
}
return ricker;
};
function convolve() {
var kernel = ricker();
var reach = kernel.std * 5;
/**
* y[n] = Sum_k{x[k] * h[n-k]}
* y: output
* x: input
* h: smoother
*/
var convolve = function(signal) {
var size = signal.length,
n = -1,
convolution = new Array(size);
while (++n < size) {
var y = 0;
var box = boundingBox(n, reach, 0, size - 1);
box.forEach(function(δ) {
var k = n + δ;
y += signal[k] * kernel(δ);
});
convolution[n] = y;
}
return convolution;
};
convolve.kernel = function(_) {
return arguments.length ? (kernel = _, convolve) : kernel;
}
/**
* Mid-range of the kernel we want to sample from.
*/
convolve.reach = function(_) {
return arguments.length ? (reach = _, convolve) : reach;
}
function range(reach) {
reach = +reach;
var i = -1,
n = 2*reach + 1,
range = new Array(n);
while(++i < n) {
range[i] = (-reach) + i;
}
return range;
}
function boundingBox(n, reach, lo, hi) {
for (var i = 1; i <= reach; i++) {
var left = n - i,
right = n + i;
if (left >= lo && right <= hi) continue;
return range(i - 1);
}
return range(reach);
}
return convolve;
};
var version = "0.0.1";
exports.version = version;
exports.ricker = ricker;
exports.convolve = convolve;
}));
<!DOCTYPE html>
<meta charset="utf-8">
<title>Convolution</title>
<style>
.axis path,
.axis line {
fill: none;
stroke: black;
shape-rendering: crispEdges;
}
text {
font-family: sans-serif;
font-size: 11px;
}
.axis.y path,
.axis.y line {
stroke: white;
}
path {
fill: none;
stroke: black;
stroke-width: 1.5;
}
circle {
fill: black;
}
path.convolution {
stroke: crimson;
}
circle.convolution {
fill: crimson;
}
</style>
<body>
<script src="//d3js.org/d3.v3.min.js" charset="utf-8"></script>
<script src="d3-peaks.js" charset="utf-8"></script>
<script>
var width = 800,
height = 470,
margin = {top: 10, left: 30, bottom: 20, right: 10};
var signal = [0,0,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0];
var ricker = d3_peaks.ricker()
.std(1);
var convolve = d3_peaks.convolve()
.reach(5)
.kernel(ricker);
var convolution = convolve(signal);
signal = signal.map(function(d, i) { return {x: i, y: d, type: 'signal'} });
convolution = convolution.map(function(d, i) { return {x: i, y: d, type: 'convolution'} });
data = signal.concat(convolution);
var svg = d3.select("body").append("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
var g = svg.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
var x = d3.scale.linear()
.domain([0, signal.length])
.range([20, width])
var y = d3.scale.linear()
.domain([-1,1])
.range([height, 0]);
var xAxis = d3.svg.axis()
.scale(x)
.tickValues(d3.range(0, signal.length));
var yAxis = d3.svg.axis()
.scale(y)
.tickValues([
d3.min(data, function(d) { return d.y; }),
0,
d3.max(data, function(d) { return d.y; })
])
.orient("left");
g.append("g")
.attr("class", "axis")
.attr("transform", "translate(0," + y(0) + ")")
.call(xAxis);
g.append("g")
.attr("class", "axis y")
.call(yAxis);
var pin = g.selectAll(".pin")
.data(data)
.enter().append("g")
.attr("class", "pin");
pin.append("path")
.attr("d", function(d) {
return "M" + x(d.x) + "," + y(0) +
"V" + y(d.y);
})
.attr("class", function(d) { return d.type });
pin.append("circle")
.attr("cx", function(d) { return x(d.x); })
.attr("cy", function(d) { return y(d.y); })
.attr("r", 4)
.attr("class", function(d) { return d.type });
var legend = g.append("g")
.attr("transform", "translate(" + (width - 110) + ", " + margin.top + ")")
.attr("class", "legend");
legend.append("path")
.attr("d", "M0,10H30");
legend.append("text")
.attr("x", 35)
.attr("y", 10)
.attr("dy", 3)
.text("signal");
legend.append("path")
.attr("d", "M0,30H30")
.attr("class", "convolution");
legend.append("text")
.attr("x", 35)
.attr("y", 30)
.attr("dy", 3)
.attr("fill", "crimson")
.text("convolution");
</script>
</body>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment