seqeyes-python 0.3.15__py3-none-any.whl → 0.3.17__py3-none-any.whl

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/__init__.py CHANGED
@@ -29,4 +29,4 @@ from seqeyes._plot import set, reset
29
29
  from seqeyes._renderer import SeqEyesViewer
30
30
 
31
31
  __all__ = ["set", "reset", "SeqEyesViewer"]
32
- __version__ = "0.3.15"
32
+ __version__ = "0.3.17"
@@ -90,7 +90,7 @@ var Pulseq = (() => {
90
90
  });
91
91
 
92
92
  // package.json
93
- var version = "0.3.15";
93
+ var version = "0.3.17";
94
94
 
95
95
  // src/pulseq/decompressor.ts
96
96
  function decompressShape(compressed, numSamples) {
@@ -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
- if(pnsData&&chVis[7]){
7334
- var p=[];pushDerivedPart(p,'X',pnsData.x,ct,'%');pushDerivedPart(p,'Y',pnsData.y,ct,'%');pushDerivedPart(p,'Z',pnsData.z,ct,'%');pushDerivedPart(p,'Norm',pnsData.n,ct,'%');
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
- var left=Math.min(Math.max(pad,cx+15),Math.max(pad,vw-r.width-pad));
7347
- var top=cy+12;if(top+r.height+pad>vh)top=cy-r.height-12;if(top<pad)top=pad;
7348
- /* `left`/`top` are client coordinates, but #tt is absolutely positioned
7349
- inside #cc, so its style offsets are measured from #cc's box. Writing
7350
- client coordinates straight out put the tooltip #cc's own offset too low
7351
- and made the bottom flip fire that far past the real bottom of the
7352
- window. */
7353
- var host=tt.offsetParent?tt.offsetParent.getBoundingClientRect():{left:0,top:0};
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.textContent=lines.join('\n');tt.style.display='block';
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);
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: seqeyes-python
3
- Version: 0.3.15
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
@@ -0,0 +1,10 @@
1
+ seqeyes/__init__.py,sha256=gmGUUepnFpBay_gnON97kGkTcv7MgGDN4c7RbKy3nOQ,945
2
+ seqeyes/_plot.py,sha256=v5cPAn3mO017lQlNR4bthC0QgIBK1YxjygJTAPFUz1E,8114
3
+ seqeyes/_renderer.py,sha256=wGLMnQvajBXFrbMBO5RPchnWNplxwAR0i5uv8FkNMcs,11100
4
+ seqeyes/resources/pulseq-bundle.js,sha256=RLvmFdpj7eGthrSIz_gzYwQ982fmrkBQHAsb8u4AjuA,227199
5
+ seqeyes/resources/seqeyes-labels.js,sha256=6L30ejjaMLFW-0RnjAEXLm2hXireKAioVkKSUh6TeXM,21986
6
+ seqeyes/resources/viewer.html,sha256=jtVGcMnnsL3csxuD-IA8BXvRYlTEney_o3mz0_wSJsY,95919
7
+ seqeyes/resources/webview-bundle.js,sha256=BCFlCKpdR5_RvxVvKC4FKIPOgau49hEJua58dKcYyjs,344378
8
+ seqeyes_python-0.3.17.dist-info/METADATA,sha256=BZEgwVXiriWR4dd_GbCMDKzSY_OzrTOoZAr4Go4Le3I,4503
9
+ seqeyes_python-0.3.17.dist-info/WHEEL,sha256=W3fkpkm7-wf9vBI5Z-7s0eWkeM-spu78I8Neb98DeEg,87
10
+ seqeyes_python-0.3.17.dist-info/RECORD,,
@@ -1,10 +0,0 @@
1
- seqeyes/__init__.py,sha256=C973CBYCEWHhxrUJFUDcGi9SkbpYxhdbRkWDVq8vtko,945
2
- seqeyes/_plot.py,sha256=v5cPAn3mO017lQlNR4bthC0QgIBK1YxjygJTAPFUz1E,8114
3
- seqeyes/_renderer.py,sha256=wGLMnQvajBXFrbMBO5RPchnWNplxwAR0i5uv8FkNMcs,11100
4
- seqeyes/resources/pulseq-bundle.js,sha256=V1Tn-0yu1fT2uXVlYaCkQXZgILGacz48Md1pfT5BgJs,227199
5
- seqeyes/resources/seqeyes-labels.js,sha256=6L30ejjaMLFW-0RnjAEXLm2hXireKAioVkKSUh6TeXM,21986
6
- seqeyes/resources/viewer.html,sha256=jtVGcMnnsL3csxuD-IA8BXvRYlTEney_o3mz0_wSJsY,95919
7
- seqeyes/resources/webview-bundle.js,sha256=cSYEInL_Paj6q3KQ4c_dQE6pPM_FA6E7t1MDaeyiZX8,341873
8
- seqeyes_python-0.3.15.dist-info/METADATA,sha256=OVDGVW1IUNw4Q7RVpmTIAhqjr156u5uanLjeLA2tKXc,4503
9
- seqeyes_python-0.3.15.dist-info/WHEEL,sha256=W3fkpkm7-wf9vBI5Z-7s0eWkeM-spu78I8Neb98DeEg,87
10
- seqeyes_python-0.3.15.dist-info/RECORD,,