viewof sigE = Inputs.range([0.1, 10], {value: 1, step: 0.1, label: "Excitation width σₑ"})
viewof ampE = Inputs.range([0, 10], {value: 1, step: 0.1, label: "Excitation strength"})
viewof sigI = Inputs.range([0.1, 10], {value: 2, step: 0.1, label: "Inhibition width σᵢ"})
viewof ampI = Inputs.range([0, 2], {value: 1, step: 0.05, label: "Inhibition strength"})
gauss = (x, s) => Math.exp(-0.5 * x * x / (s * s)) / (Math.sqrt(2 * Math.PI) * s)
ghat = (f, s) => Math.exp(-2 * Math.PI * Math.PI * s * s * f * f)
xmax = 5 * Math.max(sigE, sigI)
space = Array.from({length: 401}, (_, i) => {
const x = -xmax + 2 * xmax * i / 400;
const e = ampE * gauss(x, sigE), inh = -ampI * gauss(x, sigI);
return {x, Excitation: e, Inhibition: inh, "Difference of Gaussians": e + inh};
})
spectrum = Array.from({length: 401}, (_, i) => { const f = 2 * i / 400; return {f, k: Math.abs(ampE * ghat(f, sigE) - ampI * ghat(f, sigI))}; })
peak = spectrum.reduce((a, b) => (b.k > a.k ? b : a))
html`<div style="display:flex;flex-wrap:wrap;gap:1rem">
${Plot.plot({
width: 370, height: 280, marginLeft: 40, style: {fontSize: "15px"},
x: {label: "space"}, y: {label: null, ticks: 0},
color: {legend: true, domain: ["Excitation", "Inhibition", "Difference of Gaussians"], range: ["#ff7f0e", "#1f77b4", "black"]},
marks: [Plot.ruleY([0], {stroke: "#999"}),
...["Excitation", "Inhibition", "Difference of Gaussians"].map(k => Plot.line(space, {x: "x", y: k, stroke: () => k, strokeWidth: 2}))]
})}
${Plot.plot({
width: 370, height: 280, marginLeft: 50, style: {fontSize: "15px"},
x: {label: "spatial frequency (cycles / unit length)"},
y: {label: "|kernel spectrum|", domain: [0, Math.max(0.05, ampE, ampI)]},
marks: [Plot.line(spectrum, {x: "f", y: "k", stroke: "gray", strokeWidth: 2}),
Plot.ruleX([peak.f], {stroke: "#ff7f0e", strokeDasharray: "4 4"}),
Plot.text([peak], {x: "f", y: "k", text: d => `peak at f = ${d.f.toFixed(2)}`, dx: 8, dy: -8, textAnchor: "start"})]
})}
</div>`