|
<!DOCTYPE html> |
|
<meta charset="utf-8"> |
|
<style> |
|
|
|
html, body { |
|
margin: 0; |
|
} |
|
|
|
circle { |
|
fill: #666; |
|
} |
|
|
|
path { |
|
fill: none; |
|
stroke: teal; |
|
stroke-width: 2px; |
|
opacity: 0.5; |
|
} |
|
|
|
</style> |
|
|
|
<div> |
|
Bandwidth: <span id="current-bandwidth">15</span><br> |
|
1<input type="range" min="1" max="200" value="15" class="slider" id="bandwidth-slider">200 |
|
</div> |
|
<svg></svg> |
|
|
|
<script src="https://d3js.org/d3.v4.min.js"></script> |
|
<script> |
|
|
|
var margin = {left: 10, top: 10, right: 10, bottom: 10}; |
|
var width = 960 - margin.left - margin.right; |
|
var height = 220 - margin.top - margin.bottom; |
|
|
|
// Generate source particles in a sine wave with random noise |
|
var numbers = d3.range(0, width, 10).map(function (d,i) { |
|
return [d, height * (0.4 * Math.sin(d/(0.075*width)) + 0.4 + Math.random()/5)]; |
|
}); |
|
|
|
var line = d3.line(); |
|
|
|
var svg = d3.select('svg') |
|
.attr('width', width + margin.left + margin.right) |
|
.attr('height', height + margin.top + margin.bottom) |
|
.append('g') |
|
.attr('transform', 'translate(' + margin.left +',' + margin.top + ')'); |
|
|
|
// Plot a circle for each source particle |
|
svg.append('g') |
|
.attr('class', 'data') |
|
.selectAll('circle').data(numbers) |
|
.enter().append('circle') |
|
.attr('r', 2) |
|
.attr('cx', function (d) { return d[0]; }) |
|
.attr('cy', function (d) { return d[1]; }); |
|
|
|
svg.append('g') |
|
.attr('class', 'smoothed') |
|
.append('path'); |
|
|
|
d3.select('#bandwidth-slider') |
|
.on('input', renderLine); |
|
|
|
renderLine(); |
|
|
|
function renderLine() { |
|
// Get the value of the slider |
|
var bandwidth = +document.getElementById('bandwidth-slider').value; |
|
|
|
d3.select('#current-bandwidth').text(bandwidth) |
|
|
|
svg.select('.smoothed path') |
|
.datum(smoothedNumbers(d3.range(0, width, 10), numbers, bandwidth)) |
|
.attr('d', line); |
|
} |
|
|
|
// Compute estimated value at each target x coordinate using the |
|
// source particles (the samples). |
|
function smoothedNumbers (targets, sources, bandwidth) { |
|
return targets.map(function (t) { |
|
var numerator = d3.sum(sources, function (s) { |
|
return gaussian(s[0], t, bandwidth) * s[1]; |
|
}); |
|
|
|
var denominator = d3.sum(sources, function (s) { |
|
return gaussian(s[0], t, bandwidth); |
|
}); |
|
|
|
return [t, numerator / denominator]; |
|
}); |
|
} |
|
|
|
function gaussian (target, source, bandwidth) { |
|
return Math.exp(-Math.pow(target - source, 2) / (2*bandwidth*bandwidth)); |
|
} |
|
|
|
</script> |