Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
77 changes: 62 additions & 15 deletions src/editor/webview/assets/drawing.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ function drawCursorOverlay(){
var color=getComputedStyle(document.body).getPropertyValue('--cr').trim();
moctx.strokeStyle=color;moctx.lineWidth=0.8;moctx.setLineDash([4,3]);
moctx.beginPath();moctx.moveTo(cx,M.t);moctx.lineTo(cx,h-M.b);moctx.stroke();moctx.setLineDash([]);
moctx.fillStyle=color;moctx.font='10px monospace';moctx.textAlign='center';
moctx.fillStyle=color;moctx.font=fontSpec(10,'monospace');moctx.textAlign='center';
var label=fmtT(timeConv(cursorT)),tw=moctx.measureText(label).width;
moctx.fillText(label,Math.max(M.l+tw/2+4,Math.min(cx,w-M.r-tw/2-4)),M.t-1);
}
Expand All @@ -45,7 +45,7 @@ function drawBlockBounds(w,h,vs,ve,s){
for(var i=range.start;i<range.end;i++){
var x=t2x(BL[i].s);if(x<M.l||x>w-M.r)continue;
ctx.beginPath();ctx.moveTo(x,M.t);ctx.lineTo(x,h-M.b);ctx.stroke();
ctx.fillStyle=s.getPropertyValue('--ax').trim();ctx.font='9px monospace';ctx.textAlign='center';
ctx.fillStyle=s.getPropertyValue('--ax').trim();ctx.font=fontSpec(9,'monospace');ctx.textAlign='center';
ctx.fillText('#'+BL[i].i,x,M.t-2);
}
ctx.setLineDash([]);
Expand All @@ -68,6 +68,53 @@ function channelRange(ci){
return 1;
}
function fmtPhase(v){if(Math.abs(v-6.28318)<1e-3)return'2\u03c0';if(Math.abs(v-3.14159)<1e-3)return'\u03c0';if(v>=1)return v.toFixed(2);if(v>=0.01)return v.toFixed(3);return v.toExponential(1);}
function axisTickLabels(ci){
if(ci===0)return [fmtAmp(channelRange(0))+'Hz','0'];
if(ci===1){var ph=channelRange(1);return [fmtPhase(ph),fmtPhase(ph/2),'0'];}
if(ci>=2&&ci<=4)return ['\u00b1'+fmtAmp(gradConv(channelRange(ci)))+gradUnitStr(),'0'];
if(ci===5)return ['on'];
if(ci===6)return ['ch'];
if(ci===7)return [fmtAmp(channelRange(7))+'%','0'];
if(ci>=8&&ci<=10)return ['\u00b1'+fmtAmp(channelRange(ci))+'s/m','0'];
return [];
}
function axisTickLabelOffset(){return Math.round(14*textScaleFactor());}
function axisUnitLabelOffset(){return Math.round(4*textScaleFactor());}
function canvasTextWidth(font,text){
ctx.save();ctx.font=font;
var width=ctx.measureText(String(text||'')).width;
ctx.restore();return width;
}
function canvasTextDescent(font,text){
ctx.save();ctx.font=font;
var metrics=ctx.measureText(String(text||'0123456789'));
ctx.restore();
return metrics.actualBoundingBoxDescent||Math.max(2,scaledPx(2));
}
function maxCanvasTextWidth(font,labels){
var max=0;
for(var i=0;i<labels.length;i++)max=Math.max(max,canvasTextWidth(font,labels[i]));
return max;
}
function applyAdaptiveCanvasMargins(w,narrow){
var axFont=fontSpec(narrow?9:10,'monospace');
var chFont=fontSpec(narrow?14:18,'monospace','bold');
var vc=visChannels(),maxTickWidth=0,maxChannelWidth=0;
for(var vi=0;vi<vc.length;vi++){
var ci=vc[vi];
maxChannelWidth=Math.max(maxChannelWidth,canvasTextWidth(chFont,CH[ci]||''));
maxTickWidth=Math.max(maxTickWidth,maxCanvasTextWidth(axFont,axisTickLabels(ci)));
}
var neededLeft=narrow
? Math.ceil(Math.max(4+maxTickWidth,4+maxChannelWidth)+6)
: Math.ceil(Math.max(12+maxTickWidth,40+maxChannelWidth)+6);
var maxLeft=Math.max(M.l,Math.floor(w-M.r-80));
M.l=Math.max(M.l,Math.min(maxLeft,neededLeft));
if(textScaleFactor()>1){
var neededBottom=Math.ceil(axisTickLabelOffset()+canvasTextDescent(axFont,'000.000')+3);
M.b=Math.max(M.b,neededBottom);
}
}
function rowClip(vi,ch,fn){
var w=mc.width/(window.devicePixelRatio||1),top=M.t+vi*ch,bottom=M.t+(vi+1)*ch,rw=Math.max(0,w-M.l-M.r);
if(rw<=0||bottom<=top)return;
Expand All @@ -78,16 +125,16 @@ function rowClip(vi,ch,fn){
/* ── Axes (X time + Y per channel) ────────────────────────────────────── */
function drawAxes(w,h,vs,ve,s){
var narrow=(typeof layoutMode!=='undefined'&&layoutMode==='vertical')||w<600;
var axFont=narrow?'9px monospace':'10px monospace';
var chFont=narrow?'bold 14px monospace':'bold 18px monospace';
var axFont=fontSpec(narrow?9:10,'monospace');
var chFont=fontSpec(narrow?14:18,'monospace','bold');
// X-axis
ctx.fillStyle=s.getPropertyValue('--lb').trim();ctx.font=axFont;ctx.textAlign='center';
ctx.strokeStyle=s.getPropertyValue('--ax').trim();ctx.lineWidth=1;
ctx.beginPath();ctx.moveTo(M.l,h-M.b);ctx.lineTo(w-M.r,h-M.b);ctx.stroke();
var st=nice((ve-vs)/8),t=Math.floor(vs/st)*st;
while(t<=ve){ctx.fillText(fmtT(timeConv(t)),t2x(t),h-M.b+14);t+=st;}
while(t<=ve){ctx.fillText(fmtT(timeConv(t)),t2x(t),h-M.b+axisTickLabelOffset());t+=st;}
ctx.textAlign='right';
ctx.fillText('time ('+timeUnitStr()+')',w-4,h-M.b+4);
ctx.fillText('time ('+timeUnitStr()+')',w-4,h-M.b+axisUnitLabelOffset());

// Y-axes — one per visible channel
var vc=visChannels(),ch=cH();
Expand All @@ -105,13 +152,13 @@ function drawAxes(w,h,vs,ve,s){
ctx.fillStyle=s.getPropertyValue('--lb').trim();ctx.font=axFont;
var lblX=narrow?4:M.l-12;
ctx.textAlign=narrow?'left':'right';
if(ci===0){ctx.fillText(fmtAmp(channelRange(0))+'Hz',lblX,M.t+vi*ch+12);ctx.fillText('0',lblX,y0+ch/2-2);}
else if(ci===1){var ph=channelRange(1);ctx.fillText(fmtPhase(ph),lblX,M.t+vi*ch+12);ctx.fillText(fmtPhase(ph/2),lblX,y0+4);ctx.fillText('0',lblX,y0+ch/2-2);}
else if(ci>=2&&ci<=4){var d=gradConv(channelRange(ci));ctx.fillText('\u00b1'+fmtAmp(d)+gradUnitStr(),lblX,M.t+vi*ch+12);ctx.fillText('0',lblX,y0+4);}
else if(ci===5){ctx.fillText('on',lblX,M.t+vi*ch+12);}
else if(ci===6){ctx.fillText('ch',lblX,M.t+vi*ch+12);}
else if(ci===7){ctx.fillText(fmtAmp(channelRange(7))+'%',lblX,M.t+vi*ch+12);ctx.fillText('0',lblX,M.t+(vi+1)*ch-4);}
else if(ci>=8&&ci<=10){ctx.fillText('\u00b1'+fmtAmp(channelRange(ci))+'s/m',lblX,M.t+vi*ch+12);ctx.fillText('0',lblX,y0+4);}
var tickLabels=axisTickLabels(ci);
if(ci===0){ctx.fillText(tickLabels[0],lblX,M.t+vi*ch+12);ctx.fillText(tickLabels[1],lblX,y0+ch/2-2);}
else if(ci===1){ctx.fillText(tickLabels[0],lblX,M.t+vi*ch+12);ctx.fillText(tickLabels[1],lblX,y0+4);ctx.fillText(tickLabels[2],lblX,y0+ch/2-2);}
else if(ci>=2&&ci<=4){ctx.fillText(tickLabels[0],lblX,M.t+vi*ch+12);ctx.fillText(tickLabels[1],lblX,y0+4);}
else if(ci===5||ci===6){ctx.fillText(tickLabels[0],lblX,M.t+vi*ch+12);}
else if(ci===7){ctx.fillText(tickLabels[0],lblX,M.t+vi*ch+12);ctx.fillText(tickLabels[1],lblX,M.t+(vi+1)*ch-4);}
else if(ci>=8&&ci<=10){ctx.fillText(tickLabels[0],lblX,M.t+vi*ch+12);ctx.fillText(tickLabels[1],lblX,y0+4);}

// Small tick marks
ctx.strokeStyle=s.getPropertyValue('--ax').trim();ctx.lineWidth=0.5;
Expand Down Expand Up @@ -481,7 +528,7 @@ function drawAdcBlocks(start,end,vi,ch,colors,vs,ve){
if(x1-x0>30)labels.push({text:adc.n+'pts',x:(x0+x1)/2});
}
if(hasRect){ctx.fillStyle=colors.adf;ctx.fill();ctx.strokeStyle=colors.adc;ctx.lineWidth=1;ctx.stroke();}
ctx.fillStyle=colors.fg;ctx.font='9px monospace';ctx.textAlign='center';
ctx.fillStyle=colors.fg;ctx.font=fontSpec(9,'monospace');ctx.textAlign='center';
for(var li=0;li<labels.length;li++)ctx.fillText(labels[li].text,labels[li].x,y+3);
});
}
Expand All @@ -500,7 +547,7 @@ function drawTriggerBlocks(start,end,vi,ch,colors,vs,ve){
labels.push({text:'ch'+tg.c,x:x});hasTrigger=true;
}
}
if(hasTrigger)ctx.fill();ctx.font='8px monospace';ctx.textAlign='center';
if(hasTrigger)ctx.fill();ctx.font=fontSpec(8,'monospace');ctx.textAlign='center';
for(var li=0;li<labels.length;li++)ctx.fillText(labels[li].text,labels[li].x,y+ch*.28);
});
}
10 changes: 5 additions & 5 deletions src/editor/webview/assets/kspace.js
Original file line number Diff line number Diff line change
Expand Up @@ -324,7 +324,7 @@ function drawKCursorMarker(ctx,proj,cs,W,H){
ctx.fillStyle="rgba(255,215,0,0.90)";
ctx.beginPath();ctx.arc(p.x,p.y,Math.max(5,kDotSize+4),0,6.283);ctx.fill();ctx.stroke();
ctx.fillStyle=cs.getPropertyValue("--fg").trim();
ctx.font="10px monospace";
ctx.font=fontSpec(10,"monospace");
ctx.textAlign="left";
ctx.fillText(s.source==="adc"?"ADC":"ktraj",p.x+8,p.y-8);
ctx.restore();
Expand Down Expand Up @@ -554,7 +554,7 @@ function drawKs_core(W,H,dpr){

// ── Check data ──
if(!kAdc||!kAdcTime||!kAdc[0]||kAdc[0].length===0){
ctx.fillStyle="#f00";ctx.font="11px monospace";ctx.fillText("NO ADC data",10,20);return;
ctx.fillStyle="#f00";ctx.font=fontSpec(11,"monospace");ctx.fillText("NO ADC data",10,20);return;
}
var adcX=kAdc[0],adcY=kAdc[1],adcZ=kAdc[2], nAdc=adcX.length;

Expand All @@ -567,7 +567,7 @@ function drawKs_core(W,H,dpr){
_kBrng=Math.max(xmax-xmin,ymax-ymin,zmax-zmin,1e-6);
_kBoundsDirty=false;
}
if(!isFinite(_kBxmin)){ctx.fillStyle="#f00";ctx.font="11px monospace";ctx.fillText("ALL NaN",10,20);return;}
if(!isFinite(_kBxmin)){ctx.fillStyle="#f00";ctx.font=fontSpec(11,"monospace");ctx.fillText("ALL NaN",10,20);return;}

// ── Auto-fit (initial open only) ──
if(kAutoFit){
Expand Down Expand Up @@ -645,8 +645,8 @@ function drawKsOverlay(W,H,dpr,cs){
ctx.strokeStyle=col;ctx.lineWidth=1.6;ctx.setLineDash([]);
ctx.beginPath();ctx.moveTo(t2.x,t2.y);ctx.lineTo(t1.x,t1.y);ctx.stroke();
ctx.fillStyle=col;ctx.beginPath();ctx.arc(t1.x,t1.y,4,0,6.283);ctx.fill();
ctx.fillStyle=col;ctx.font="bold 11px monospace";ctx.fillText(label,t1.x+5,t1.y-5);
ctx.fillStyle=cs.getPropertyValue("--lb").trim();ctx.font="8px monospace";
ctx.fillStyle=col;ctx.font=fontSpec(11,"monospace","bold");ctx.fillText(label,t1.x+5,t1.y-5);
ctx.fillStyle=cs.getPropertyValue("--lb").trim();ctx.font=fontSpec(8,"monospace");
for(var v=tick;v<=al;v+=tick){
var tp=proj(fx*v,fy*v,fz*v);
var tm=proj(-fx*v,-fy*v,-fz*v);
Expand Down
55 changes: 50 additions & 5 deletions src/editor/webview/assets/state.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
var cc=document.getElementById('cc'),mc=document.getElementById('mc'),ctx=mc.getContext('2d'),
moc=document.getElementById('moc'),moctx=moc.getContext('2d'),
tt=document.getElementById('tt'),curEl=document.getElementById('cur'),legend=document.getElementById('legend'),
tuSel=document.getElementById('tu'),guSel=document.getElementById('gu');
tuSel=document.getElementById('tu'),guSel=document.getElementById('gu'),textScaleSel=document.getElementById('textScale');
var exportBtn=document.getElementById('exportKspaceBtn');
var pnsBtn=document.getElementById('pnsBtn');
var BL=[],waveformOverview=null,TD=0,GR=1e-5,RR=1e-6,AR=1e-7,BR=1e-5; // blocks, duration, rasters [s]
Expand All @@ -29,6 +29,51 @@ var viewerNotices={};
var kspaceSafetyWarning=null,kspaceSafetyBusy=false,kspaceSafetyPopupTimer=0;
var derivedRenderPointCount=0,derivedEnvelopeCurveCount=0,derivedRawCurveCount=0,waveformOverviewActive=false,rfRenderPointCount=0,rfRawCurveCount=0,rfReducedCurveCount=0,rfOverviewBucketCount=0,lastDrawDurationMs=0,viewerDrawCount=0,viewerCursorDrawCount=0;
var viewerDrawFrame=0,viewerDrawMinimap=false;
var textScale=readTextScalePercent();

function normalizeTextScalePercent(value){
var n=parseInt(value,10);
if(!isFinite(n))n=100;
n=Math.max(80,Math.min(200,n));
var steps=[80,100,125,150,175,200],best=100,bestDist=Infinity;
for(var i=0;i<steps.length;i++){
var d=Math.abs(steps[i]-n);
if(d<bestDist){bestDist=d;best=steps[i];}
}
return best;
}
function readTextScalePercent(){
try{return normalizeTextScalePercent(localStorage.getItem('seqeyes.textScalePercent'));}catch(_){return 100;}
}
function writeTextScalePercent(value){
try{localStorage.setItem('seqeyes.textScalePercent',String(normalizeTextScalePercent(value)));}catch(_){}
}
function textScaleFactor(){return textScale/100;}
function scaledPx(px){
return Math.max(1,Math.round(px*textScaleFactor()*10)/10);
}
function fontPx(px){return scaledPx(px)+'px';}
function fontSpec(px,family,weight){
return (weight?weight+' ':'')+fontPx(px)+' '+(family||'monospace');
}
function applyTextScaleCss(){
var b=document.body;if(!b)return;
b.style.setProperty('--font-body',fontPx(13));
}
function setTextScalePercent(value,persist,redraw){
textScale=normalizeTextScalePercent(value);
if(textScaleSel&&textScaleSel.value!==String(textScale))textScaleSel.value=String(textScale);
if(persist)writeTextScalePercent(textScale);
applyTextScaleCss();
if(redraw){
if(typeof rs==='function')rs();
else if(typeof draw==='function')draw();
if(typeof drawKs==='function')drawKs();
}
}
setTextScalePercent(textScale,false,false);
if(textScaleSel)textScaleSel.onchange=function(){setTextScalePercent(this.value,true,true);};

function isMobileSafetyLayout(){return !!(window.matchMedia&&window.matchMedia('(max-width: 768px), (pointer: coarse)').matches);}
function renderViewerNotices(){
var el=document.getElementById('viewerNotice');if(!el)return;el.textContent='';
Expand Down Expand Up @@ -641,7 +686,7 @@ function drawMinimap(){
if(seqTiming.trCount>1)info+=seqTiming.trCount+' TRs';
}
if(info){
mmCtx.fillStyle='rgba(0,0,0,0.35)';mmCtx.font='9px monospace';mmCtx.textAlign='left';
mmCtx.fillStyle='rgba(0,0,0,0.35)';mmCtx.font=fontSpec(9,'monospace');mmCtx.textAlign='left';
mmCtx.fillText(info,5,H-3);
mmCtx.fillStyle=s.getPropertyValue('--fg').trim();mmCtx.globalAlpha=0.9;
mmCtx.fillText(info,4,H-4);
Expand All @@ -664,11 +709,11 @@ function rs(){
moc.width=r.width*dpr;moc.height=r.height*dpr;
moc.style.width=r.width+'px';moc.style.height=r.height+'px';
moctx.setTransform(dpr,0,0,dpr,0,0);
// Adaptive margins for small/narrow screens
// Keep legacy margins by default; expand only when scaled axis labels need room.
if(layoutMode==='vertical'||r.width<600){
M.l=60;M.r=3;M.t=5;M.b=18;
M.l=60;M.r=3;M.t=5;M.b=18;if(typeof applyAdaptiveCanvasMargins==='function')applyAdaptiveCanvasMargins(r.width,true);
}else{
M.l=92;M.r=4;M.t=8;M.b=22;
M.l=92;M.r=4;M.t=8;M.b=22;if(typeof applyAdaptiveCanvasMargins==='function')applyAdaptiveCanvasMargins(r.width,false);
}
// Resize minimap canvas too
var mr=document.getElementById('mmap').getBoundingClientRect();
Expand Down
Loading
Loading