seqeyes-python 0.3.13__tar.gz → 0.3.14__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.
Files changed (21) hide show
  1. {seqeyes_python-0.3.13 → seqeyes_python-0.3.14}/PKG-INFO +1 -1
  2. {seqeyes_python-0.3.13 → seqeyes_python-0.3.14}/pyproject.toml +1 -1
  3. {seqeyes_python-0.3.13 → seqeyes_python-0.3.14}/src/seqeyes/__init__.py +1 -1
  4. {seqeyes_python-0.3.13 → seqeyes_python-0.3.14}/src/seqeyes/resources/pulseq-bundle.js +143 -88
  5. {seqeyes_python-0.3.13 → seqeyes_python-0.3.14}/.gitignore +0 -0
  6. {seqeyes_python-0.3.13 → seqeyes_python-0.3.14}/README.md +0 -0
  7. {seqeyes_python-0.3.13 → seqeyes_python-0.3.14}/examples/demo.ipynb +0 -0
  8. {seqeyes_python-0.3.13 → seqeyes_python-0.3.14}/examples/demo.py +0 -0
  9. {seqeyes_python-0.3.13 → seqeyes_python-0.3.14}/src/seqeyes/_plot.py +0 -0
  10. {seqeyes_python-0.3.13 → seqeyes_python-0.3.14}/src/seqeyes/_renderer.py +0 -0
  11. {seqeyes_python-0.3.13 → seqeyes_python-0.3.14}/src/seqeyes/resources/seqeyes-labels.js +0 -0
  12. {seqeyes_python-0.3.13 → seqeyes_python-0.3.14}/src/seqeyes/resources/viewer.html +0 -0
  13. {seqeyes_python-0.3.13 → seqeyes_python-0.3.14}/src/seqeyes/resources/webview-bundle.js +0 -0
  14. {seqeyes_python-0.3.13 → seqeyes_python-0.3.14}/tests/conftest.py +0 -0
  15. {seqeyes_python-0.3.13 → seqeyes_python-0.3.14}/tests/debug_full.html +0 -0
  16. {seqeyes_python-0.3.13 → seqeyes_python-0.3.14}/tests/debug_minimal.html +0 -0
  17. {seqeyes_python-0.3.13 → seqeyes_python-0.3.14}/tests/debug_viewer.html +0 -0
  18. {seqeyes_python-0.3.13 → seqeyes_python-0.3.14}/tests/debug_viewer.py +0 -0
  19. {seqeyes_python-0.3.13 → seqeyes_python-0.3.14}/tests/integration_test.py +0 -0
  20. {seqeyes_python-0.3.13 → seqeyes_python-0.3.14}/tests/test_plot.py +0 -0
  21. {seqeyes_python-0.3.13 → seqeyes_python-0.3.14}/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.13
3
+ Version: 0.3.14
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.13"
7
+ version = "0.3.14"
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"}
@@ -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.13"
32
+ __version__ = "0.3.14"
@@ -24,6 +24,7 @@ var Pulseq = (() => {
24
24
  var pulseq_browser_exports = {};
25
25
  __export(pulseq_browser_exports, {
26
26
  INTERACTIVE_COMPUTE_LIMITS: () => INTERACTIVE_COMPUTE_LIMITS,
27
+ MAX_KSPACE_OVERVIEW_POINTS: () => MAX_KSPACE_OVERVIEW_POINTS,
27
28
  MAX_RF_RESPONSE_BANDS: () => MAX_RF_RESPONSE_BANDS,
28
29
  MAX_RF_RESPONSE_FFT_POINTS: () => MAX_RF_RESPONSE_FFT_POINTS,
29
30
  MAX_RF_RESPONSE_SAMPLES: () => MAX_RF_RESPONSE_SAMPLES,
@@ -89,7 +90,7 @@ var Pulseq = (() => {
89
90
  });
90
91
 
91
92
  // package.json
92
- var version = "0.3.13";
93
+ var version = "0.3.14";
93
94
 
94
95
  // src/pulseq/decompressor.ts
95
96
  function decompressShape(compressed, numSamples) {
@@ -2326,8 +2327,8 @@ var Pulseq = (() => {
2326
2327
  };
2327
2328
  }
2328
2329
  function estimateKspacePeakMemoryBytes(estimate) {
2329
- const gridBytes = Math.max(0, estimate.gridCandidatePoints) * 96;
2330
- const adcAndTransferBytes = Math.max(0, estimate.adcSamples) * 104;
2330
+ const gridBytes = Math.max(0, estimate.gridCandidatePoints) * 11;
2331
+ const adcAndTransferBytes = Math.max(0, estimate.adcSamples) * 43;
2331
2332
  return Math.ceil(Math.min(Number.MAX_SAFE_INTEGER, (gridBytes + adcAndTransferBytes) * 1.25));
2332
2333
  }
2333
2334
  function kspaceExceedsInteractiveBudget(estimate) {
@@ -3102,9 +3103,20 @@ var Pulseq = (() => {
3102
3103
  }
3103
3104
  }
3104
3105
  const gradientSeries = buildGlobalGradientSeries(blocks, GR, totalDuration);
3105
- const cand = [];
3106
+ let candBound = 2;
3107
+ for (const series of gradientSeries) candBound += countSeriesSupport(series, gradientSupport);
3108
+ candBound += excT.length * 3 + refT.length * 2 + adcT.length;
3109
+ if (totalDuration > 0) candBound += Math.max(1, Math.round(totalDuration / GR)) + 1;
3110
+ const cand = new Float64Array(candBound);
3111
+ let candCount = 0;
3112
+ let candOverflow = false;
3106
3113
  const pushC = (t) => {
3107
- if (isFinite(t) && t >= -tacc) cand.push(Math.max(0, tacc * Math.round(t / tacc)));
3114
+ if (!(isFinite(t) && t >= -tacc)) return;
3115
+ if (candCount >= cand.length) {
3116
+ candOverflow = true;
3117
+ return;
3118
+ }
3119
+ cand[candCount++] = Math.max(0, tacc * Math.round(t / tacc));
3108
3120
  };
3109
3121
  for (const series of gradientSeries) collectSeriesSupport(series, gradientSupport, pushC);
3110
3122
  for (const t of excT) {
@@ -3123,23 +3135,17 @@ var Pulseq = (() => {
3123
3135
  const nS = Math.max(1, Math.round(totalDuration / GR));
3124
3136
  for (let i = 0; i <= nS; i++) pushC(i * GR);
3125
3137
  }
3126
- if (cand.length === 0) return null;
3127
- cand.sort((a, b) => a - b);
3128
- const grid = [];
3129
- for (let i = 0; i < cand.length; i++) {
3130
- if (i === 0 || cand[i] - cand[i - 1] > tacc * 0.5) grid.push(cand[i]);
3138
+ if (candOverflow) return null;
3139
+ if (candCount === 0) return null;
3140
+ cand.subarray(0, candCount).sort();
3141
+ let kept = 0;
3142
+ for (let i = 0; i < candCount; i++) {
3143
+ if (i === 0 || cand[i] - cand[i - 1] > tacc * 0.5) cand[kept++] = cand[i];
3131
3144
  }
3132
- const N = grid.length;
3145
+ const N = kept;
3133
3146
  if (N < 2) return null;
3134
3147
  if (_options?.maxGridPoints && N > _options.maxGridPoints) return null;
3135
- const gx = new Float64Array(N), gy = new Float64Array(N), gz = new Float64Array(N);
3136
- const cursors = [0, 0, 0];
3137
- for (let i = 0; i < N; i++) {
3138
- const t = grid[i];
3139
- gx[i] = sampleSeries(gradientSeries[0], t, cursors, 0);
3140
- gy[i] = sampleSeries(gradientSeries[1], t, cursors, 1);
3141
- gz[i] = sampleSeries(gradientSeries[2], t, cursors, 2);
3142
- }
3148
+ const grid = cand.subarray(0, N);
3143
3149
  const eIdx = [], rIdx = [];
3144
3150
  for (const t of excT) {
3145
3151
  const i = timeIdx(t, grid);
@@ -3149,70 +3155,132 @@ var Pulseq = (() => {
3149
3155
  const i = timeIdx(t, grid);
3150
3156
  if (i >= 0) rIdx.push(i);
3151
3157
  }
3152
- eIdx.sort((a, b) => a - b);
3153
- rIdx.sort((a, b) => a - b);
3158
+ eIdx.sort((a2, b) => a2 - b);
3159
+ rIdx.sort((a2, b) => a2 - b);
3154
3160
  const excitationAt = new Uint8Array(N);
3155
3161
  const refocusingAt = new Uint8Array(N);
3156
3162
  for (const i of eIdx) excitationAt[i] = 1;
3157
3163
  for (const i of rIdx) refocusingAt[i] = 1;
3158
- const kx = new Float64Array(N), ky = new Float64Array(N), kz = new Float64Array(N);
3164
+ const outCount = _options?.maxTrajectoryPoints && _options.maxTrajectoryPoints > 0 ? Math.min(_options.maxTrajectoryPoints, N) : N;
3165
+ const outStep = N / outCount;
3166
+ const outX = new Float64Array(outCount), outY = new Float64Array(outCount), outZ = new Float64Array(outCount);
3167
+ const outT = new Float64Array(outCount);
3168
+ const nA = adcT.length;
3169
+ const f32Adc = _options?.adcPrecision === "f32";
3170
+ const kxA = f32Adc ? new Float32Array(nA) : new Float64Array(nA);
3171
+ const kyA = f32Adc ? new Float32Array(nA) : new Float64Array(nA);
3172
+ const kzA = f32Adc ? new Float32Array(nA) : new Float64Array(nA);
3173
+ const cursors = [0, 0, 0];
3159
3174
  let cx = 0, cy = 0, cz = 0;
3175
+ let lx = 0, ly = 0, lz = 0;
3176
+ let a = 0;
3177
+ let out = 0;
3178
+ let nextKeep = 0;
3179
+ let ec = 0;
3180
+ let gxPrev = sampleSeries(gradientSeries[0], grid[0], cursors, 0);
3181
+ let gyPrev = sampleSeries(gradientSeries[1], grid[0], cursors, 1);
3182
+ let gzPrev = sampleSeries(gradientSeries[2], grid[0], cursors, 2);
3160
3183
  if (refocusingAt[0] && !excitationAt[0]) {
3161
- kx[0] = -kx[0];
3162
- ky[0] = -ky[0];
3163
- kz[0] = -kz[0];
3184
+ lx = -lx;
3185
+ ly = -ly;
3186
+ lz = -lz;
3187
+ }
3188
+ if (nextKeep === 0) {
3189
+ while (ec < eIdx.length && eIdx[ec] < 1) ec++;
3190
+ const brk = ec < eIdx.length && eIdx[ec] === 1;
3191
+ outT[0] = grid[0];
3192
+ outX[0] = brk ? NaN : lx;
3193
+ outY[0] = brk ? NaN : ly;
3194
+ outZ[0] = brk ? NaN : lz;
3195
+ out = 1;
3196
+ nextKeep = out < outCount ? Math.floor(out * outStep) : -1;
3197
+ }
3198
+ while (a < nA && adcT[a] <= grid[0]) {
3199
+ kxA[a] = lx;
3200
+ kyA[a] = ly;
3201
+ kzA[a] = lz;
3202
+ a++;
3164
3203
  }
3165
3204
  for (let i = 1; i < N; i++) {
3166
- const dt = grid[i] - grid[i - 1];
3167
- if (dt <= 0) {
3168
- kx[i] = kx[i - 1];
3169
- ky[i] = ky[i - 1];
3170
- kz[i] = kz[i - 1];
3171
- continue;
3205
+ const gxi = sampleSeries(gradientSeries[0], grid[i], cursors, 0);
3206
+ const gyi = sampleSeries(gradientSeries[1], grid[i], cursors, 1);
3207
+ const gzi = sampleSeries(gradientSeries[2], grid[i], cursors, 2);
3208
+ const tPrev = grid[i - 1], tCur = grid[i];
3209
+ const dt = tCur - tPrev;
3210
+ const px = lx, py = ly, pz = lz;
3211
+ if (dt > 0) {
3212
+ const dx = 0.5 * (gxPrev + gxi) * dt;
3213
+ const dy = 0.5 * (gyPrev + gyi) * dt;
3214
+ const dz = 0.5 * (gzPrev + gzi) * dt;
3215
+ const yx = dx - cx, yy = dy - cy, yz = dz - cz;
3216
+ const nx = lx + yx, ny = ly + yy, nz = lz + yz;
3217
+ cx = nx - lx - yx;
3218
+ cy = ny - ly - yy;
3219
+ cz = nz - lz - yz;
3220
+ lx = nx;
3221
+ ly = ny;
3222
+ lz = nz;
3223
+ if (excitationAt[i]) {
3224
+ lx = 0;
3225
+ ly = 0;
3226
+ lz = 0;
3227
+ cx = 0;
3228
+ cy = 0;
3229
+ cz = 0;
3230
+ } else if (refocusingAt[i]) {
3231
+ lx = -lx;
3232
+ ly = -ly;
3233
+ lz = -lz;
3234
+ cx = -cx;
3235
+ cy = -cy;
3236
+ cz = -cz;
3237
+ }
3172
3238
  }
3173
- const dx = 0.5 * (gx[i - 1] + gx[i]) * dt;
3174
- const dy = 0.5 * (gy[i - 1] + gy[i]) * dt;
3175
- const dz = 0.5 * (gz[i - 1] + gz[i]) * dt;
3176
- const yx = dx - cx, yy = dy - cy, yz = dz - cz;
3177
- const nx = kx[i - 1] + yx, ny = ky[i - 1] + yy, nz = kz[i - 1] + yz;
3178
- cx = nx - kx[i - 1] - yx;
3179
- cy = ny - ky[i - 1] - yy;
3180
- cz = nz - kz[i - 1] - yz;
3181
- kx[i] = nx;
3182
- ky[i] = ny;
3183
- kz[i] = nz;
3184
- if (excitationAt[i]) {
3185
- kx[i] = 0;
3186
- ky[i] = 0;
3187
- kz[i] = 0;
3188
- cx = 0;
3189
- cy = 0;
3190
- cz = 0;
3191
- } else if (refocusingAt[i]) {
3192
- kx[i] = -kx[i];
3193
- ky[i] = -ky[i];
3194
- kz[i] = -kz[i];
3195
- cx = -cx;
3196
- cy = -cy;
3197
- cz = -cz;
3198
- }
3199
- }
3200
- const kxP = new Float64Array(kx), kyP = new Float64Array(ky), kzP = new Float64Array(kz);
3201
- for (const i of eIdx) {
3202
- if (i > 0) {
3203
- kxP[i - 1] = NaN;
3204
- kyP[i - 1] = NaN;
3205
- kzP[i - 1] = NaN;
3239
+ if (i === nextKeep) {
3240
+ while (ec < eIdx.length && eIdx[ec] < i + 1) ec++;
3241
+ const brk = ec < eIdx.length && eIdx[ec] === i + 1;
3242
+ outT[out] = tCur;
3243
+ outX[out] = brk ? NaN : lx;
3244
+ outY[out] = brk ? NaN : ly;
3245
+ outZ[out] = brk ? NaN : lz;
3246
+ out++;
3247
+ nextKeep = out < outCount ? Math.floor(out * outStep) : -1;
3248
+ }
3249
+ while (a < nA && adcT[a] <= tCur) {
3250
+ const t = adcT[a];
3251
+ if (Math.abs(tCur - t) < 1e-12 || dt <= 0) {
3252
+ kxA[a] = lx;
3253
+ kyA[a] = ly;
3254
+ kzA[a] = lz;
3255
+ } else {
3256
+ kxA[a] = px + (lx - px) * (t - tPrev) / dt;
3257
+ kyA[a] = py + (ly - py) * (t - tPrev) / dt;
3258
+ kzA[a] = pz + (lz - pz) * (t - tPrev) / dt;
3259
+ }
3260
+ a++;
3206
3261
  }
3262
+ gxPrev = gxi;
3263
+ gyPrev = gyi;
3264
+ gzPrev = gzi;
3207
3265
  }
3208
- const nA = adcT.length;
3209
- const kxA = new Float64Array(nA), kyA = new Float64Array(nA), kzA = new Float64Array(nA);
3210
- for (let a = 0; a < nA; a++) {
3211
- kxA[a] = interp(kx, grid, adcT[a]);
3212
- kyA[a] = interp(ky, grid, adcT[a]);
3213
- kzA[a] = interp(kz, grid, adcT[a]);
3266
+ while (a < nA) {
3267
+ kxA[a] = lx;
3268
+ kyA[a] = ly;
3269
+ kzA[a] = lz;
3270
+ a++;
3214
3271
  }
3215
- return { ktraj: [kxP, kyP, kzP], t_ktraj: new Float64Array(grid), ktraj_adc: [kxA, kyA, kzA], t_adc: new Float64Array(adcT) };
3272
+ const tAdc = adcIdx === adcT.length ? adcT : adcT.slice(0, adcIdx);
3273
+ return {
3274
+ ktraj: [outX, outY, outZ],
3275
+ t_ktraj: outT,
3276
+ ktraj_adc: [kxA, kyA, kzA],
3277
+ t_adc: tAdc,
3278
+ rasterSampleCount: N
3279
+ };
3280
+ }
3281
+ function countSeriesSupport(series, mode) {
3282
+ if (series.times.length < 2) return 0;
3283
+ return mode === "all" ? series.times.length : series.requiredSupport.length;
3216
3284
  }
3217
3285
  function collectSeriesSupport(series, mode, push) {
3218
3286
  if (series.times.length < 2) return;
@@ -3312,22 +3380,9 @@ var Pulseq = (() => {
3312
3380
  }
3313
3381
  return lo < g.length ? lo : -1;
3314
3382
  }
3315
- function interp(d, g, t) {
3316
- const n = g.length;
3317
- if (n === 0) return 0;
3318
- let lo = 0, hi = n;
3319
- while (lo < hi) {
3320
- const m = lo + hi >> 1;
3321
- if (g[m] < t) lo = m + 1;
3322
- else hi = m;
3323
- }
3324
- if (lo === 0) return d[0];
3325
- if (lo >= n) return d[n - 1];
3326
- if (Math.abs(g[lo] - t) < 1e-12) return d[lo];
3327
- const i0 = lo - 1, i1 = lo, dt = g[i1] - g[i0];
3328
- if (dt <= 0) return d[i1];
3329
- return d[i0] + (d[i1] - d[i0]) * (t - g[i0]) / dt;
3330
- }
3383
+
3384
+ // src/editor/kspaceTransport.ts
3385
+ var MAX_KSPACE_OVERVIEW_POINTS = 3e4;
3331
3386
 
3332
3387
  // src/pulseq/labels.ts
3333
3388
  var COUNTER_ORDER = ["SLC", "SEG", "REP", "AVG", "SET", "ECO", "PHS", "LIN", "PAR", "ACQ", "TRID", "ONCE"];
@@ -5670,7 +5725,7 @@ var Pulseq = (() => {
5670
5725
  },
5671
5726
  totalDurationSec,
5672
5727
  adcSampleCount: kspace.t_adc.length,
5673
- trajectorySampleCount: kspace.t_ktraj.length,
5728
+ trajectorySampleCount: kspace.rasterSampleCount,
5674
5729
  units: {
5675
5730
  trajectory: "1/m",
5676
5731
  time: "s",