seqeyes-python 0.3.15__tar.gz → 0.3.17__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {seqeyes_python-0.3.15 → seqeyes_python-0.3.17}/PKG-INFO +1 -1
- {seqeyes_python-0.3.15 → seqeyes_python-0.3.17}/pyproject.toml +1 -1
- {seqeyes_python-0.3.15 → seqeyes_python-0.3.17}/src/seqeyes/__init__.py +1 -1
- {seqeyes_python-0.3.15 → seqeyes_python-0.3.17}/src/seqeyes/resources/pulseq-bundle.js +1 -1
- {seqeyes_python-0.3.15 → seqeyes_python-0.3.17}/src/seqeyes/resources/webview-bundle.js +68 -11
- {seqeyes_python-0.3.15 → seqeyes_python-0.3.17}/.gitignore +0 -0
- {seqeyes_python-0.3.15 → seqeyes_python-0.3.17}/README.md +0 -0
- {seqeyes_python-0.3.15 → seqeyes_python-0.3.17}/examples/demo.ipynb +0 -0
- {seqeyes_python-0.3.15 → seqeyes_python-0.3.17}/examples/demo.py +0 -0
- {seqeyes_python-0.3.15 → seqeyes_python-0.3.17}/src/seqeyes/_plot.py +0 -0
- {seqeyes_python-0.3.15 → seqeyes_python-0.3.17}/src/seqeyes/_renderer.py +0 -0
- {seqeyes_python-0.3.15 → seqeyes_python-0.3.17}/src/seqeyes/resources/seqeyes-labels.js +0 -0
- {seqeyes_python-0.3.15 → seqeyes_python-0.3.17}/src/seqeyes/resources/viewer.html +0 -0
- {seqeyes_python-0.3.15 → seqeyes_python-0.3.17}/tests/conftest.py +0 -0
- {seqeyes_python-0.3.15 → seqeyes_python-0.3.17}/tests/debug_full.html +0 -0
- {seqeyes_python-0.3.15 → seqeyes_python-0.3.17}/tests/debug_minimal.html +0 -0
- {seqeyes_python-0.3.15 → seqeyes_python-0.3.17}/tests/debug_viewer.html +0 -0
- {seqeyes_python-0.3.15 → seqeyes_python-0.3.17}/tests/debug_viewer.py +0 -0
- {seqeyes_python-0.3.15 → seqeyes_python-0.3.17}/tests/integration_test.py +0 -0
- {seqeyes_python-0.3.15 → seqeyes_python-0.3.17}/tests/test_plot.py +0 -0
- {seqeyes_python-0.3.15 → seqeyes_python-0.3.17}/tests/test_renderer.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: seqeyes-python
|
|
3
|
-
Version: 0.3.
|
|
3
|
+
Version: 0.3.17
|
|
4
4
|
Summary: Interactive Pulseq MRI sequence viewer for Jupyter — lightweight, beautiful, replaces seq.plot() in pypulseq.
|
|
5
5
|
Project-URL: Homepage, https://github.com/bughht/seqeyes_plugin
|
|
6
6
|
Project-URL: Repository, https://github.com/bughht/seqeyes_plugin
|
|
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "seqeyes-python"
|
|
7
|
-
version = "0.3.
|
|
7
|
+
version = "0.3.17"
|
|
8
8
|
description = "Interactive Pulseq MRI sequence viewer for Jupyter — lightweight, beautiful, replaces seq.plot() in pypulseq."
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
license = {text = "MIT"}
|
|
@@ -1313,6 +1313,33 @@ buildLegend();
|
|
|
1313
1313
|
tuSel.value=timeUnit;guSel.value=gradUnit;
|
|
1314
1314
|
tuSel.onchange=function(){timeUnit=tuSel.value;SeqEyesPrefs.set(SeqEyesPrefs.KEYS.timeUnit,timeUnit);draw();};
|
|
1315
1315
|
guSel.onchange=function(){gradUnit=guSel.value;SeqEyesPrefs.set(SeqEyesPrefs.KEYS.gradUnit,gradUnit);draw();};
|
|
1316
|
+
/**
|
|
1317
|
+
* Fill a hover readout, one row per line, hanging each wrapped row under the
|
|
1318
|
+
* content rather than the margin.
|
|
1319
|
+
*
|
|
1320
|
+
* A row like "Labels: SLC=30 SEG=5 ..." that does not fit continues under the
|
|
1321
|
+
* "SLC=30", not under the "Labels:", so a continuation cannot be mistaken for a
|
|
1322
|
+
* new field. The indent is that line's own label width — four columns for
|
|
1323
|
+
* "RF: ", eight for "Labels: " — which `ch` gives exactly, the readout being
|
|
1324
|
+
* monospace.
|
|
1325
|
+
*
|
|
1326
|
+
* It takes the element rather than reaching for one, because the standalone
|
|
1327
|
+
* viewer calls this with its own `tt` from inside its own scope.
|
|
1328
|
+
*/
|
|
1329
|
+
function renderTooltipLines(target,lines){
|
|
1330
|
+
target.textContent='';
|
|
1331
|
+
for(var i=0;i<lines.length;i++){
|
|
1332
|
+
var row=document.createElement('div');
|
|
1333
|
+
row.textContent=lines[i];
|
|
1334
|
+
var marker=lines[i].indexOf(': ');
|
|
1335
|
+
if(marker>0){
|
|
1336
|
+
var indent=(marker+2)+'ch';
|
|
1337
|
+
row.style.paddingLeft=indent;
|
|
1338
|
+
row.style.textIndent='-'+indent;
|
|
1339
|
+
}
|
|
1340
|
+
target.appendChild(row);
|
|
1341
|
+
}
|
|
1342
|
+
}
|
|
1316
1343
|
function setExportButtonEnabled(enabled){if(exportBtn)exportBtn.disabled=!enabled;}
|
|
1317
1344
|
/* Throws rather than drawing an empty panel when a trajectory arrives without
|
|
1318
1345
|
its ADC samples. A reply that lost its arrays in transit used to clear the
|
|
@@ -1651,6 +1678,18 @@ function requestPnsWindow(vs,ve){
|
|
|
1651
1678
|
vscApi.postMessage({command:'calculatePnsWindow',requestId:pnsWindowPending.requestId,startSec:start,endSec:end,maxPoints:120000});
|
|
1652
1679
|
}
|
|
1653
1680
|
|
|
1681
|
+
/**
|
|
1682
|
+
* The PNS series the plot is currently drawing, choosing it exactly as
|
|
1683
|
+
* `pnsSeriesForView` does but without asking for a window or touching the
|
|
1684
|
+
* notices. The readout runs on every mouse move and must report what is on
|
|
1685
|
+
* screen, not what a hover happened to request.
|
|
1686
|
+
*/
|
|
1687
|
+
function pnsSeriesShown(vs,ve){
|
|
1688
|
+
if(!pnsData)return null;
|
|
1689
|
+
if(shouldRequestFinePnsWindow(vs,ve)&&pnsWindowCovers(pnsWindowData,vs,ve))return pnsWindowData;
|
|
1690
|
+
return pnsData;
|
|
1691
|
+
}
|
|
1692
|
+
|
|
1654
1693
|
function pnsSeriesForView(vs,ve){
|
|
1655
1694
|
if(!pnsData)return null;
|
|
1656
1695
|
if(shouldRequestFinePnsWindow(vs,ve)){
|
|
@@ -7329,9 +7368,24 @@ function pushM1DerivedPart(parts,label,series,t){
|
|
|
7329
7368
|
}
|
|
7330
7369
|
pushDerivedPart(parts,label,series,t,'s/m');
|
|
7331
7370
|
}
|
|
7371
|
+
/* Coarse PNS buckets span many TRs, so there is no single value to report: the
|
|
7372
|
+
plot draws the band and the readout has to say the same thing. Interpolating
|
|
7373
|
+
first to last, as the point sampler does, lands near the baseline and
|
|
7374
|
+
disagrees with a band reaching several times higher. */
|
|
7375
|
+
function pushPnsDerivedPart(parts,label,series,t){
|
|
7376
|
+
if(series&&series.kind==='envelope'){
|
|
7377
|
+
var range=sampleEnvelopeRangeAtTime(series,t);if(!range)return;
|
|
7378
|
+
if(range.min===range.max)parts.push(label+'='+fmtDerivedValue(range.min,'%'));
|
|
7379
|
+
else parts.push(label+'\u2208['+range.min.toFixed(1)+', '+range.max.toFixed(1)+']%');
|
|
7380
|
+
return;
|
|
7381
|
+
}
|
|
7382
|
+
pushDerivedPart(parts,label,series,t,'%');
|
|
7383
|
+
}
|
|
7332
7384
|
function appendDerivedTooltipLines(lines,ct){
|
|
7333
|
-
|
|
7334
|
-
|
|
7385
|
+
// The series the plot is showing, not the full-sequence one behind it.
|
|
7386
|
+
var activePns=pnsSeriesShown(ox,ox+visibleDuration());
|
|
7387
|
+
if(activePns&&chVis[7]){
|
|
7388
|
+
var p=[];pushPnsDerivedPart(p,'X',activePns.x,ct);pushPnsDerivedPart(p,'Y',activePns.y,ct);pushPnsDerivedPart(p,'Z',activePns.z,ct);pushPnsDerivedPart(p,'Norm',activePns.n,ct);
|
|
7335
7389
|
if(p.length)lines.push('PNS: '+p.join(' '));
|
|
7336
7390
|
}
|
|
7337
7391
|
var activeM1=m1SeriesForView(ox,ox+visibleDuration());
|
|
@@ -7343,14 +7397,17 @@ function appendDerivedTooltipLines(lines,ct){
|
|
|
7343
7397
|
function placeTooltip(cx,cy){
|
|
7344
7398
|
var pad=8;tt.style.left='0px';tt.style.top='0px';
|
|
7345
7399
|
var r=tt.getBoundingClientRect(),vw=window.innerWidth,vh=window.innerHeight;
|
|
7346
|
-
|
|
7347
|
-
|
|
7348
|
-
|
|
7349
|
-
|
|
7350
|
-
|
|
7351
|
-
|
|
7352
|
-
|
|
7353
|
-
var
|
|
7400
|
+
/* #tt is absolutely positioned inside #cc, which hides its overflow, so the
|
|
7401
|
+
usable area is the plot box and not the window: with the analysis panel
|
|
7402
|
+
open the window extends well past #cc, and a readout clamped to the window
|
|
7403
|
+
edge is cut off where the panel begins. Its style offsets are measured
|
|
7404
|
+
from #cc's box too, which is why they are written relative to it. */
|
|
7405
|
+
var host=tt.offsetParent?tt.offsetParent.getBoundingClientRect()
|
|
7406
|
+
:{left:0,top:0,right:vw,bottom:vh};
|
|
7407
|
+
var minX=Math.max(pad,host.left+pad),maxX=Math.min(vw,host.right)-pad-r.width;
|
|
7408
|
+
var minY=Math.max(pad,host.top+pad),maxY=Math.min(vh,host.bottom)-pad-r.height;
|
|
7409
|
+
var left=Math.min(Math.max(minX,cx+15),Math.max(minX,maxX));
|
|
7410
|
+
var top=cy+12;if(top>maxY)top=cy-r.height-12;if(top<minY)top=minY;
|
|
7354
7411
|
tt.style.left=(left-host.left)+'px';tt.style.top=(top-host.top)+'px';
|
|
7355
7412
|
}
|
|
7356
7413
|
function fmtFlipAngle(value){
|
|
@@ -7421,7 +7478,7 @@ function showTooltipAt(cx,cy,ct){
|
|
|
7421
7478
|
}
|
|
7422
7479
|
appendDerivedTooltipLines(lines,ct);
|
|
7423
7480
|
if(lines.length>1||found){
|
|
7424
|
-
tt
|
|
7481
|
+
renderTooltipLines(tt,lines);tt.style.display='block';
|
|
7425
7482
|
placeTooltip(cx,cy);
|
|
7426
7483
|
// Auto-dismiss on touch devices
|
|
7427
7484
|
if(_touchTooltipTimer)clearTimeout(_touchTooltipTimer);
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|