Published
Edited
Jul 25, 2019
1 fork
7 stars
Insert cell
Insert cell
Insert cell
Insert cell
N = 6
Insert cell
Insert cell
unit_with_angle = (theta) => new ComplexNumber({re: Math.cos(theta), im: Math.sin(theta)})
Insert cell
Insert cell
sample_raw = (n) => // returns the contribution of each complex coefficient at the sample point 'n'
coeffs.map((c,k) => k < LOW_PASS ?
c.multiply( unit_with_angle(k*n * TWO_PI/N) )
: new ComplexNumber() )
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
// WIP: trace of the frequency-space representation in complex space, marked at sample points.

// FIXME: 'low-pass filter' is clobbering all the conjugate pairs
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
setArg( ift(coeffs) ) // bind samples to coefficients
// vega-lite charts also depend on coeffs, via sample_raw
Insert cell
setArg = (arg_) => {
samples.forEach((d,i) => {d.v = arg_[i]} )

// redraw samples (do not rebroadcast)
d3.select(viewof samples).selectAll('circle')
.attr('cy', (d) => yTime(d.v))
d3.select(viewof samples).select('path').attr('d', lerpArgLinear(samples))
return arg_
}
Insert cell
Insert cell
Insert cell
viewof samples = {
let node = DOM.svg(w, h),
svg = d3.select(node)
svg.style('overflow', 'visible')
let y = yTime
let ret = initSignal()
let x = xTime
let line = lerpArgLinear
let bind = function(d,i) { // redraw & broadcast when a sample point is dragged
let [_,yIn] = d3.mouse(this)
ret[i].v = y.invert(yIn)
svg.selectAll('circle')
.attr('cy', (d) => y(d.v))
svg.select('path').attr('d', line(ret))
node.dispatchEvent(new CustomEvent('input'))
}
svg.append('path')
.attr('d', line(ret))
.style('fill', 'none').style('stroke', blues[N-1])
.style('stroke-width', 3)
svg.selectAll('circle')
.data(ret).join('circle')
.attr('cx', (_,i) => x(i)) // constant
.attr('cy', (d) => y(d.v))
.attr('r', 10)
.style('fill', blues[N-2])
.call(d3.drag().on('drag', bind))
node.value = ret
return node
}
Insert cell
viewof coeffs = {
let node = DOM.svg(w, h),
svg = d3.select(node)
svg.style('overflow', 'visible')
let ret = dft(samples.map(d => d.v)) // coefficients depend on the sampled signal
ret.forEach((c,k) => c.index = k) // annotate each coefficient by its frequency
let y = yFreq, x = xFreq
let bind = function(d,i) { // redraw & broadcast when a coefficient is dragged
let [xIn,yIn] = d3.mouse(this)
let j = ret.length - i
if (i != 0 && i != j) {
ret[i].re = x.invert(xIn)
ret[i].im = y.invert(yIn)

ret[j].re = ret[i].re
ret[j].im = -ret[i].im
}
else {
ret[i].re = x.invert(xIn)
}
svg.selectAll('circle.coeff')
.attr('cx', (d) => x(d.re))
.attr('cy', (d) => y(d.im))
node.dispatchEvent(new CustomEvent('input'))
}
let r = (mag) => xFreq(mag - .5) // BRITTLE
svg.selectAll('circle')
.data([ .5, .3, .2, .1 ]).join('circle')
.attr('cx', x(0)).attr('cy', y(0))
.attr('r', d => r(d) )
.style('fill', 'none').style('stroke', 'lightgray')
svg.selectAll('circle.coeff')
.data(ret).join('circle')
.attr('class', 'coeff')
.attr('cx', (d) => x(d.re))
.attr('cy', (d) => y(d.im))
.attr('r', 5)
.style('fill', 'white')
.style('stroke', (_,i) => blues[i])
.style('stroke-width', 3)
.call(d3.drag().on('drag', bind))
node.value = ret
return node
}
Insert cell
Insert cell
Insert cell
time_value = vegalite({
height: h,
layer: [
{
data: {values: Array(N*LERP).fill().map((_,i) => ({
time: i/LERP,
value: sample(i/LERP).re //* N
}) )},
mark: {
type: "line",
// interpolate: "cardinal",
},
encoding: {
x: {field: "time", type: "quantitative"},
y: {field: "value", type: "quantitative"},
}
},
{
data: {values: samples.map((d,i) => ({time: i, value: d.v}) )},
mark: 'point',
encoding: {
x: {field: "time", type: "quantitative"},
y: {field: "value", type: "quantitative"},
}
},
]
})
Insert cell
Insert cell
amplitude_time = vegalite({
height: h,
data: {
values: values
},
mark: {
type: "line",
interpolate: "cardinal"
},
encoding: {
x: {field: "time", type: "quantitative"},
y: {field: "amplitude", type: "quantitative"},
color: {field: "frequency", type: "quantitative", legend: null}
}
})
Insert cell
Insert cell
Insert cell
// embed = require('vega-embed@4')
Insert cell
vegalite = require('@observablehq/vega-lite@0.1')
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell

Purpose-built for displays of data

Observable is your go-to platform for exploring data and creating expressive data visualizations. Use reactive JavaScript notebooks for prototyping and a collaborative canvas for visual data exploration and dashboard creation.
Learn more