Upload index.html
Browse files- index.html +139 -19
index.html
CHANGED
|
@@ -1,19 +1,139 @@
|
|
| 1 |
-
|
| 2 |
-
<
|
| 3 |
-
|
| 4 |
-
|
| 5 |
-
|
| 6 |
-
|
| 7 |
-
|
| 8 |
-
|
| 9 |
-
|
| 10 |
-
|
| 11 |
-
|
| 12 |
-
|
| 13 |
-
|
| 14 |
-
|
| 15 |
-
|
| 16 |
-
|
| 17 |
-
|
| 18 |
-
|
| 19 |
-
<
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
|
| 2 |
+
<style>
|
| 3 |
+
.wave-wrap { font-family: var(--font-sans); padding: 0.5rem 0 1rem; }
|
| 4 |
+
.wave-controls { display: flex; gap: 20px; flex-wrap: wrap; align-items: center; margin-bottom: 12px; }
|
| 5 |
+
.wave-label { font-size: 12px; color: var(--color-text-secondary); min-width: 120px; }
|
| 6 |
+
.wave-val { font-size: 12px; font-weight: 500; color: var(--color-text-primary); min-width: 28px; }
|
| 7 |
+
.panel-row { display: flex; gap: 12px; }
|
| 8 |
+
.panel { flex: 1; background: var(--color-background-secondary); border-radius: 8px; padding: 10px 12px; }
|
| 9 |
+
.panel-head { font-size: 12px; font-weight: 500; color: var(--color-text-primary); margin-bottom: 6px; }
|
| 10 |
+
.panel-note { font-size: 11px; color: var(--color-text-secondary); margin-top: 4px; }
|
| 11 |
+
</style>
|
| 12 |
+
<div class="wave-wrap">
|
| 13 |
+
<div class="wave-controls">
|
| 14 |
+
<div style="display:flex;align-items:center;gap:8px">
|
| 15 |
+
<span class="wave-label" style="color:#1D9E75;font-weight:500">pos (word index)</span>
|
| 16 |
+
<input type="range" min="0" max="49" value="10" id="sl-pos" style="width:120px" oninput="update()">
|
| 17 |
+
<span class="wave-val" id="lbl-pos">10</span>
|
| 18 |
+
</div>
|
| 19 |
+
<div style="display:flex;align-items:center;gap:8px">
|
| 20 |
+
<span class="wave-label" style="color:#BA7517;font-weight:500">highlight dim i</span>
|
| 21 |
+
<input type="range" min="0" max="255" value="0" id="sl-dim" style="width:120px" oninput="update()">
|
| 22 |
+
<span class="wave-val" id="lbl-dim">0</span>
|
| 23 |
+
</div>
|
| 24 |
+
</div>
|
| 25 |
+
|
| 26 |
+
<div class="panel-row" style="margin-bottom:10px">
|
| 27 |
+
<div class="panel">
|
| 28 |
+
<div class="panel-head">Looking <em>across</em> one row → (horizontal slice at selected pos)</div>
|
| 29 |
+
<canvas id="hcv" style="width:100%;display:block"></canvas>
|
| 30 |
+
<div class="panel-note" id="hnote"></div>
|
| 31 |
+
</div>
|
| 32 |
+
<div class="panel">
|
| 33 |
+
<div class="panel-head">Looking <em>down</em> one column ↓ (vertical slice at selected dim i)</div>
|
| 34 |
+
<canvas id="vcv" style="width:100%;display:block"></canvas>
|
| 35 |
+
<div class="panel-note" id="vnote"></div>
|
| 36 |
+
</div>
|
| 37 |
+
</div>
|
| 38 |
+
</div>
|
| 39 |
+
|
| 40 |
+
<script>
|
| 41 |
+
const ROWS=50, COLS=256, D=512;
|
| 42 |
+
function pe(pos,i){
|
| 43 |
+
const d=Math.pow(10000,(2*Math.floor(i/2))/D);
|
| 44 |
+
return (i%2===0)?Math.sin(pos/d):Math.cos(pos/d);
|
| 45 |
+
}
|
| 46 |
+
|
| 47 |
+
const isDark = matchMedia('(prefers-color-scheme: dark)').matches;
|
| 48 |
+
const teal = '#1D9E75', amber = '#BA7517';
|
| 49 |
+
const textCol = isDark ? 'rgba(200,198,190,0.9)' : 'rgba(60,60,58,0.9)';
|
| 50 |
+
const gridCol = isDark ? 'rgba(255,255,255,0.07)' : 'rgba(0,0,0,0.07)';
|
| 51 |
+
|
| 52 |
+
function drawLine(cvs, data, color, hlIdx, xLabel, showFreq) {
|
| 53 |
+
const dpr = window.devicePixelRatio||1;
|
| 54 |
+
const w = cvs.offsetWidth||300;
|
| 55 |
+
const h = 110;
|
| 56 |
+
cvs.width = w*dpr; cvs.height = h*dpr;
|
| 57 |
+
cvs.style.height = h+'px';
|
| 58 |
+
const ctx = cvs.getContext('2d');
|
| 59 |
+
ctx.scale(dpr, dpr);
|
| 60 |
+
|
| 61 |
+
const pad = {l:6, r:6, t:10, b:20};
|
| 62 |
+
const pw = w - pad.l - pad.r;
|
| 63 |
+
const ph = h - pad.t - pad.b;
|
| 64 |
+
const n = data.length;
|
| 65 |
+
|
| 66 |
+
ctx.clearRect(0,0,w,h);
|
| 67 |
+
|
| 68 |
+
// grid
|
| 69 |
+
ctx.strokeStyle = gridCol; ctx.lineWidth = 0.5;
|
| 70 |
+
[0.25, 0.5, 0.75].forEach(f => {
|
| 71 |
+
const y = pad.t + f*ph;
|
| 72 |
+
ctx.beginPath(); ctx.moveTo(pad.l, y); ctx.lineTo(pad.l+pw, y); ctx.stroke();
|
| 73 |
+
});
|
| 74 |
+
// zero line
|
| 75 |
+
ctx.strokeStyle = isDark ? 'rgba(255,255,255,0.18)' : 'rgba(0,0,0,0.18)';
|
| 76 |
+
ctx.lineWidth = 1;
|
| 77 |
+
ctx.beginPath(); ctx.moveTo(pad.l, pad.t+ph/2); ctx.lineTo(pad.l+pw, pad.t+ph/2); ctx.stroke();
|
| 78 |
+
|
| 79 |
+
// data path
|
| 80 |
+
ctx.strokeStyle = color; ctx.lineWidth = 1.5; ctx.lineJoin = 'round';
|
| 81 |
+
ctx.beginPath();
|
| 82 |
+
data.forEach((v, i) => {
|
| 83 |
+
const x = pad.l + (i/(n-1))*pw;
|
| 84 |
+
const y = pad.t + (1-(v+1)/2)*ph;
|
| 85 |
+
i===0 ? ctx.moveTo(x,y) : ctx.lineTo(x,y);
|
| 86 |
+
});
|
| 87 |
+
ctx.stroke();
|
| 88 |
+
|
| 89 |
+
// highlight dot
|
| 90 |
+
if (hlIdx >= 0 && hlIdx < n) {
|
| 91 |
+
const x = pad.l + (hlIdx/(n-1))*pw;
|
| 92 |
+
const y = pad.t + (1-(data[hlIdx]+1)/2)*ph;
|
| 93 |
+
ctx.fillStyle = amber;
|
| 94 |
+
ctx.beginPath(); ctx.arc(x,y,4,0,Math.PI*2); ctx.fill();
|
| 95 |
+
// value label
|
| 96 |
+
ctx.fillStyle = amber;
|
| 97 |
+
ctx.font = `500 11px var(--font-sans)`;
|
| 98 |
+
ctx.textAlign = 'center';
|
| 99 |
+
ctx.fillText(data[hlIdx].toFixed(3), Math.min(Math.max(x,30),pw-20), y > pad.t+12 ? y-7 : y+14);
|
| 100 |
+
}
|
| 101 |
+
|
| 102 |
+
// x-axis ticks
|
| 103 |
+
ctx.fillStyle = textCol; ctx.font = `10px var(--font-sans)`;
|
| 104 |
+
ctx.textAlign = 'center';
|
| 105 |
+
[0, Math.floor(n/2), n-1].forEach(i => {
|
| 106 |
+
const x = pad.l + (i/(n-1))*pw;
|
| 107 |
+
ctx.fillText(xLabel(i), x, h-4);
|
| 108 |
+
});
|
| 109 |
+
|
| 110 |
+
// y labels
|
| 111 |
+
ctx.textAlign = 'right';
|
| 112 |
+
ctx.fillText('+1', pad.l-1, pad.t+5);
|
| 113 |
+
ctx.fillText('−1', pad.l-1, pad.t+ph+4);
|
| 114 |
+
}
|
| 115 |
+
|
| 116 |
+
function update() {
|
| 117 |
+
const pos = +document.getElementById('sl-pos').value;
|
| 118 |
+
const dim = +document.getElementById('sl-dim').value;
|
| 119 |
+
document.getElementById('lbl-pos').textContent = pos;
|
| 120 |
+
document.getElementById('lbl-dim').textContent = dim;
|
| 121 |
+
|
| 122 |
+
// horizontal: values across dims for fixed pos
|
| 123 |
+
const hdata = Array.from({length:COLS}, (_,i) => pe(pos,i));
|
| 124 |
+
drawLine(document.getElementById('hcv'), hdata, teal, dim, i => i===0?'i=0':i===127?'128':i===255?'255':'', true);
|
| 125 |
+
|
| 126 |
+
// vertical: values across positions for fixed dim
|
| 127 |
+
const vdata = Array.from({length:ROWS}, (_,r) => pe(r,dim));
|
| 128 |
+
drawLine(document.getElementById('vcv'), vdata, amber, pos, r => r===0?'pos=0':r===24?'24':r===49?'49':'', false);
|
| 129 |
+
|
| 130 |
+
// notes
|
| 131 |
+
const wl = (2*Math.PI*Math.pow(10000, dim/D)).toFixed(0);
|
| 132 |
+
const speed = dim < 20 ? '⚡ very fast' : dim < 80 ? '⚡ fast' : dim < 160 ? '~ moderate' : '🐢 slow';
|
| 133 |
+
document.getElementById('hnote').innerHTML = `Left = high-frequency oscillations. Right = nearly flat. Highlighted dot at <b>i=${dim}</b> = ${hdata[dim].toFixed(3)}.`;
|
| 134 |
+
document.getElementById('vnote').innerHTML = `Dimension i=${dim} (${speed}, wavelength≈${wl}). Highlighted dot at <b>pos=${pos}</b> = ${vdata[pos].toFixed(3)}.`;
|
| 135 |
+
}
|
| 136 |
+
|
| 137 |
+
setTimeout(update, 60);
|
| 138 |
+
window.addEventListener('resize', update);
|
| 139 |
+
</script>
|