aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--docs/prototypes/panel-widget-gallery.html84
1 files changed, 84 insertions, 0 deletions
diff --git a/docs/prototypes/panel-widget-gallery.html b/docs/prototypes/panel-widget-gallery.html
index 890c862..8268935 100644
--- a/docs/prototypes/panel-widget-gallery.html
+++ b/docs/prototypes/panel-widget-gallery.html
@@ -706,6 +706,12 @@ card(C,'R06','Chicken-head selector',
card(C,'R12','Chrome slot fader',
`<svg id="slotf" class="rsvg drag" viewBox="0 0 90 150" width="90" height="150"></svg>`,
'<b>the console gain slide.</b> A chrome T-handle rides a recessed slot, the dB scale engraved on the plate — gain above zero, cut below. Drag; readout shows signed dB. After a vintage channel-strip output fader.');
+card(C,'R14','Spade-pointer tuning knob',
+ `<svg id="spade" class="rsvg drag" viewBox="0 0 140 124" width="140" height="124"></svg>`,
+ '<b>the pointer rides the knob.</b> A knurled bakelite knob carries a bright spade pointer over a scale engraved into the panel itself. Drag up/down to tune. After a 1920s radio receiver dial.');
+card(C,'R15','Multi-band dial',
+ `<svg id="mband" class="rsvg" viewBox="0 0 190 110" width="190" height="110"></svg>`,
+ '<b>four scales, one needle.</b> Nested band arcs on a cream face; the active ring reads bold. Drag the left dial to tune; click the bandspread dial to switch bands. After a shortwave receiver dial pair.');
/* ============ METERS & GAUGES ============ */
const M=$('meters');
@@ -1470,6 +1476,84 @@ function setEdge(f){edgeFrac=Math.max(.08,Math.min(.94,f));
setRd('R13','−'+db.toFixed(1)+' dB');}
setEdge(.62);
+/* R14 spade-pointer tuning knob: engraved panel arc, pointer rides the knob */
+let spadeV=8.3,spadeGrp=null;
+(function(){const s=$('spade'),cx=70,cy=82,sweep=v=>-80+v/10*160;
+ const defs=svgEl(s,'defs',{});
+ const bk=svgEl(defs,'radialGradient',{id:'spadeKnob',cx:'42%',cy:'34%',r:'80%'});
+ svgEl(bk,'stop',{offset:'0','stop-color':'#33302a'});
+ svgEl(bk,'stop',{offset:'1','stop-color':'#080706'});
+ const mt=svgEl(defs,'linearGradient',{id:'spadeMetal',x1:0,y1:0,x2:1,y2:1});
+ svgEl(mt,'stop',{offset:'0','stop-color':'#e9e7e0'});
+ svgEl(mt,'stop',{offset:'1','stop-color':'#8d897f'});
+ // engraved arc: every stroke carries a faint light twin offset below (incised relief)
+ const engrave=(x1,y1,x2,y2,w)=>{
+ svgEl(s,'line',{x1,y1:y1+.8,x2,y2:y2+.8,stroke:'rgba(255,255,255,.13)','stroke-width':w});
+ svgEl(s,'line',{x1,y1,x2,y2,stroke:'#050404','stroke-width':w});};
+ for(let i=0;i<=40;i++){const v=i/4,a=sweep(v),major=i%4===0;
+ const [x1,y1]=polar(cx,cy,major?50:52,a),[x2,y2]=polar(cx,cy,58,a);
+ engrave(x1,y1,x2,y2,major?1.6:.9);
+ if(major){const [nx,ny]=polar(cx,cy,65,a);
+ svgEl(s,'text',{x:nx,y:ny+3.2,'text-anchor':'middle','font-size':7.5,'font-family':'var(--mono)',
+ fill:'rgba(255,255,255,.13)'}).textContent=v;
+ svgEl(s,'text',{x:nx,y:ny+2.4,'text-anchor':'middle','font-size':7.5,'font-family':'var(--mono)',
+ fill:'#0a0908'}).textContent=v;}}
+ spadeGrp=svgEl(s,'g',{});
+ svgEl(spadeGrp,'path',{d:`M ${cx-2} ${cy-28} L ${cx-4.5} ${cy-42} L ${cx} ${cy-54} L ${cx+4.5} ${cy-42} L ${cx+2} ${cy-28} Z`,
+ fill:'url(#spadeMetal)',stroke:'#55524a','stroke-width':.6});
+ svgEl(spadeGrp,'circle',{cx,cy,r:30,fill:'url(#spadeKnob)',stroke:'#000','stroke-width':.8});
+ svgEl(spadeGrp,'circle',{cx,cy,r:28.5,fill:'none',stroke:'#0a0908','stroke-width':3,'stroke-dasharray':'2.2 2.2'});
+ svgEl(spadeGrp,'circle',{cx,cy,r:22,fill:'url(#spadeKnob)'});
+ svgEl(spadeGrp,'ellipse',{cx:cx-8,cy:cy-9,rx:8,ry:5,fill:'rgba(255,255,255,.08)',transform:`rotate(-30,${cx-8},${cy-9})`});})();
+function setSpade(v){spadeV=Math.max(0,Math.min(10,v));
+ spadeGrp.setAttribute('transform',`rotate(${-80+spadeV/10*160},70,82)`);
+ setRd('R14',spadeV.toFixed(1));}
+dragDelta($('spade'),()=>spadeV,setSpade,{min:0,max:10,sens:.05});setSpade(8.3);
+
+/* R15 multi-band dial: nested arcs, one needle; bandspread selects the ring */
+const MBAND_RANGES=[[0.54,1.6],[1.6,5.1],[5.1,15.5],[15.5,30.5]];
+let mbandV=45,mbandB=2,mbNeedle=null,mbRings=[],mbSpread=null;
+(function(){const s=$('mband'),cx=62,cy=62,sweep=t=>-70+t*140;
+ const defs=svgEl(s,'defs',{});
+ const pf=svgEl(defs,'linearGradient',{id:'mbFace',x1:0,y1:0,x2:1,y2:1});
+ svgEl(pf,'stop',{offset:'0','stop-color':'#eee5c8'});
+ svgEl(pf,'stop',{offset:'1','stop-color':'#dcd2ae'});
+ svgEl(s,'rect',{x:4,y:4,width:182,height:102,rx:12,fill:'#171412',stroke:'#060505'});
+ svgEl(s,'rect',{x:9,y:9,width:172,height:92,rx:9,fill:'url(#mbFace)',stroke:'#0a0908'});
+ const INK='#2c2318';
+ [18,26,34,42].forEach((r,bi)=>{const ring=svgEl(s,'g',{});
+ const [x1,y1]=polar(cx,cy,r,-70),[x2,y2]=polar(cx,cy,r,70);
+ svgEl(ring,'path',{d:`M ${x1} ${y1} A ${r} ${r} 0 0 1 ${x2} ${y2}`,fill:'none',stroke:INK,'stroke-width':.9});
+ for(let i=0;i<=8;i++){const a=sweep(i/8),[tx1,ty1]=polar(cx,cy,r,a),[tx2,ty2]=polar(cx,cy,r+3,a);
+ svgEl(ring,'line',{x1:tx1,y1:ty1,x2:tx2,y2:ty2,stroke:INK,'stroke-width':.8});}
+ mbRings.push(ring);});
+ mbNeedle=svgEl(s,'line',{x1:cx,y1:cy,x2:cx,y2:cy-46,stroke:'#1a1613','stroke-width':1.6,'stroke-linecap':'round'});
+ svgEl(s,'circle',{cx,cy,r:7,fill:'url(#spadeKnob)',stroke:'#000','stroke-width':.6});
+ svgEl(s,'text',{x:14,y:100,'font-size':5,'letter-spacing':'.12em','font-family':'var(--mono)',
+ fill:INK,opacity:.8}).textContent='MEGACYCLES';
+ // bandspread dial (click = band select)
+ const bx=142,by=56;
+ for(let i=0;i<12;i++){const a=i*30,[x1,y1]=polar(bx,by,24,a),[x2,y2]=polar(bx,by,21,a);
+ svgEl(s,'line',{x1,y1,x2,y2,stroke:INK,'stroke-width':.8});}
+ svgEl(s,'circle',{cx:bx,cy:by,r:17,fill:'none',stroke:INK,'stroke-width':.7,opacity:.5});
+ mbSpread=svgEl(s,'line',{x1:bx,y1:by,x2:bx,y2:by-22,stroke:'#1a1613','stroke-width':1.4,'stroke-linecap':'round'});
+ svgEl(s,'circle',{cx:bx,cy:by,r:6,fill:'url(#spadeKnob)',stroke:'#000','stroke-width':.6});
+ svgEl(s,'text',{x:bx,y:96,'text-anchor':'middle','font-size':5,'letter-spacing':'.12em',
+ 'font-family':'var(--mono)',fill:INK,opacity:.8}).textContent='BANDSPREAD';
+ const dragHit=svgEl(s,'rect',{x:9,y:9,width:100,height:92,fill:'transparent'});
+ dragHit.style.cursor='ns-resize';
+ dragDelta(dragHit,()=>mbandV,v=>setMband(v,mbandB),{min:0,max:100});
+ const clickHit=svgEl(s,'rect',{x:112,y:9,width:69,height:92,fill:'transparent'});
+ clickHit.style.cursor='pointer';
+ clickHit.addEventListener('click',()=>setMband(mbandV,(mbandB+1)%4));})();
+function setMband(v,b){mbandV=Math.max(0,Math.min(100,v));mbandB=b;
+ mbNeedle.setAttribute('transform',`rotate(${-70+mbandV/100*140},62,62)`);
+ mbSpread.setAttribute('transform',`rotate(${-45+b*30},142,56)`);
+ mbRings.forEach((g,i)=>g.setAttribute('opacity',i===b?'1':'.4'));
+ const [lo,hi]=MBAND_RANGES[b];const mc=lo+mbandV/100*(hi-lo);
+ setRd('R15',`B${b+1} · ${mc.toFixed(2)} Mc`);}
+setMband(45,2);
+
/* ============ LIVE SIGNAL LOOPS ============ */
buildBars($('vuL'),16);buildBars($('vuR'),16);$('mini').innerHTML='<i></i><i></i><i></i><i></i>';
(function(){const eq=$('eq');for(let b=0;b<11;b++){const band=document.createElement('span');band.className='band';