seqeyes-python 0.3.14__tar.gz → 0.3.15__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.14 → seqeyes_python-0.3.15}/PKG-INFO +1 -1
  2. {seqeyes_python-0.3.14 → seqeyes_python-0.3.15}/pyproject.toml +1 -1
  3. {seqeyes_python-0.3.14 → seqeyes_python-0.3.15}/src/seqeyes/__init__.py +1 -1
  4. {seqeyes_python-0.3.14 → seqeyes_python-0.3.15}/src/seqeyes/resources/pulseq-bundle.js +104 -47
  5. {seqeyes_python-0.3.14 → seqeyes_python-0.3.15}/.gitignore +0 -0
  6. {seqeyes_python-0.3.14 → seqeyes_python-0.3.15}/README.md +0 -0
  7. {seqeyes_python-0.3.14 → seqeyes_python-0.3.15}/examples/demo.ipynb +0 -0
  8. {seqeyes_python-0.3.14 → seqeyes_python-0.3.15}/examples/demo.py +0 -0
  9. {seqeyes_python-0.3.14 → seqeyes_python-0.3.15}/src/seqeyes/_plot.py +0 -0
  10. {seqeyes_python-0.3.14 → seqeyes_python-0.3.15}/src/seqeyes/_renderer.py +0 -0
  11. {seqeyes_python-0.3.14 → seqeyes_python-0.3.15}/src/seqeyes/resources/seqeyes-labels.js +0 -0
  12. {seqeyes_python-0.3.14 → seqeyes_python-0.3.15}/src/seqeyes/resources/viewer.html +0 -0
  13. {seqeyes_python-0.3.14 → seqeyes_python-0.3.15}/src/seqeyes/resources/webview-bundle.js +0 -0
  14. {seqeyes_python-0.3.14 → seqeyes_python-0.3.15}/tests/conftest.py +0 -0
  15. {seqeyes_python-0.3.14 → seqeyes_python-0.3.15}/tests/debug_full.html +0 -0
  16. {seqeyes_python-0.3.14 → seqeyes_python-0.3.15}/tests/debug_minimal.html +0 -0
  17. {seqeyes_python-0.3.14 → seqeyes_python-0.3.15}/tests/debug_viewer.html +0 -0
  18. {seqeyes_python-0.3.14 → seqeyes_python-0.3.15}/tests/debug_viewer.py +0 -0
  19. {seqeyes_python-0.3.14 → seqeyes_python-0.3.15}/tests/integration_test.py +0 -0
  20. {seqeyes_python-0.3.14 → seqeyes_python-0.3.15}/tests/test_plot.py +0 -0
  21. {seqeyes_python-0.3.14 → seqeyes_python-0.3.15}/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.14
3
+ Version: 0.3.15
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.14"
7
+ version = "0.3.15"
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.14"
32
+ __version__ = "0.3.15"
@@ -90,7 +90,7 @@ var Pulseq = (() => {
90
90
  });
91
91
 
92
92
  // package.json
93
- var version = "0.3.14";
93
+ var version = "0.3.15";
94
94
 
95
95
  // src/pulseq/decompressor.ts
96
96
  function decompressShape(compressed, numSamples) {
@@ -2327,9 +2327,13 @@ var Pulseq = (() => {
2327
2327
  };
2328
2328
  }
2329
2329
  function estimateKspacePeakMemoryBytes(estimate) {
2330
- const gridBytes = Math.max(0, estimate.gridCandidatePoints) * 11;
2330
+ const gridBytes = Math.max(0, estimate.gridCandidatePoints) * 1;
2331
2331
  const adcAndTransferBytes = Math.max(0, estimate.adcSamples) * 43;
2332
- return Math.ceil(Math.min(Number.MAX_SAFE_INTEGER, (gridBytes + adcAndTransferBytes) * 1.25));
2332
+ const fixedOverheadBytes = 2 * 1024 * 1024;
2333
+ return Math.ceil(Math.min(
2334
+ Number.MAX_SAFE_INTEGER,
2335
+ (gridBytes + adcAndTransferBytes) * 1.25 + fixedOverheadBytes
2336
+ ));
2333
2337
  }
2334
2338
  function kspaceExceedsInteractiveBudget(estimate) {
2335
2339
  return estimate.rasterSamples > INTERACTIVE_COMPUTE_LIMITS.kspaceRasterSamples || estimate.adcSamples > INTERACTIVE_COMPUTE_LIMITS.kspaceAdcSamples || estimate.gridCandidatePoints > INTERACTIVE_COMPUTE_LIMITS.kspaceGridCandidates || estimateKspacePeakMemoryBytes(estimate) >= KSPACE_CONFIRMATION_MEMORY_BYTES;
@@ -3106,7 +3110,6 @@ var Pulseq = (() => {
3106
3110
  let candBound = 2;
3107
3111
  for (const series of gradientSeries) candBound += countSeriesSupport(series, gradientSupport);
3108
3112
  candBound += excT.length * 3 + refT.length * 2 + adcT.length;
3109
- if (totalDuration > 0) candBound += Math.max(1, Math.round(totalDuration / GR)) + 1;
3110
3113
  const cand = new Float64Array(candBound);
3111
3114
  let candCount = 0;
3112
3115
  let candOverflow = false;
@@ -3131,10 +3134,6 @@ var Pulseq = (() => {
3131
3134
  for (const t of adcT) pushC(t);
3132
3135
  pushC(0);
3133
3136
  pushC(totalDuration);
3134
- if (totalDuration > 0) {
3135
- const nS = Math.max(1, Math.round(totalDuration / GR));
3136
- for (let i = 0; i <= nS; i++) pushC(i * GR);
3137
- }
3138
3137
  if (candOverflow) return null;
3139
3138
  if (candCount === 0) return null;
3140
3139
  cand.subarray(0, candCount).sort();
@@ -3142,25 +3141,78 @@ var Pulseq = (() => {
3142
3141
  for (let i = 0; i < candCount; i++) {
3143
3142
  if (i === 0 || cand[i] - cand[i - 1] > tacc * 0.5) cand[kept++] = cand[i];
3144
3143
  }
3145
- const N = kept;
3146
- if (N < 2) return null;
3147
- if (_options?.maxGridPoints && N > _options.maxGridPoints) return null;
3148
- const grid = cand.subarray(0, N);
3144
+ const storedCount = kept;
3145
+ const rasterSteps = totalDuration > 0 ? Math.max(1, Math.round(totalDuration / GR)) : -1;
3146
+ const rasterAt = (i) => Math.max(0, tacc * Math.round(i * GR / tacc));
3147
+ const makeGridWalk = () => {
3148
+ let storedIndex = 0;
3149
+ let rasterIndex = 0;
3150
+ let previous = 0;
3151
+ let started = false;
3152
+ return () => {
3153
+ for (; ; ) {
3154
+ const haveStored = storedIndex < storedCount;
3155
+ const haveRaster = rasterIndex <= rasterSteps;
3156
+ if (!haveStored && !haveRaster) return NaN;
3157
+ let value;
3158
+ if (!haveRaster) {
3159
+ value = cand[storedIndex++];
3160
+ } else if (!haveStored) {
3161
+ value = rasterAt(rasterIndex++);
3162
+ } else {
3163
+ const stored = cand[storedIndex];
3164
+ const raster = rasterAt(rasterIndex);
3165
+ if (stored <= raster) {
3166
+ value = stored;
3167
+ storedIndex++;
3168
+ if (stored === raster) rasterIndex++;
3169
+ } else {
3170
+ value = raster;
3171
+ rasterIndex++;
3172
+ }
3173
+ }
3174
+ if (!started || value !== previous) {
3175
+ previous = value;
3176
+ started = true;
3177
+ return value;
3178
+ }
3179
+ }
3180
+ };
3181
+ };
3182
+ const excSorted = Float64Array.from(excT);
3183
+ excSorted.sort();
3184
+ const refSorted = Float64Array.from(refT);
3185
+ refSorted.sort();
3149
3186
  const eIdx = [], rIdx = [];
3150
- for (const t of excT) {
3151
- const i = timeIdx(t, grid);
3152
- if (i >= 0) eIdx.push(i);
3187
+ let excSeen = 0, refSeen = 0;
3188
+ let N = 0;
3189
+ {
3190
+ const nextGridTime2 = makeGridWalk();
3191
+ for (; ; ) {
3192
+ const t = nextGridTime2();
3193
+ if (Number.isNaN(t)) break;
3194
+ while (excSeen < excSorted.length && t >= excSorted[excSeen] - 1e-12) {
3195
+ eIdx.push(N);
3196
+ excSeen++;
3197
+ }
3198
+ while (refSeen < refSorted.length && t >= refSorted[refSeen] - 1e-12) {
3199
+ rIdx.push(N);
3200
+ refSeen++;
3201
+ }
3202
+ N++;
3203
+ }
3153
3204
  }
3154
- for (const t of refT) {
3155
- const i = timeIdx(t, grid);
3156
- if (i >= 0) rIdx.push(i);
3157
- }
3158
- eIdx.sort((a2, b) => a2 - b);
3159
- rIdx.sort((a2, b) => a2 - b);
3160
- const excitationAt = new Uint8Array(N);
3161
- const refocusingAt = new Uint8Array(N);
3162
- for (const i of eIdx) excitationAt[i] = 1;
3163
- for (const i of rIdx) refocusingAt[i] = 1;
3205
+ if (N < 2) return null;
3206
+ if (_options?.maxGridPoints && N > _options.maxGridPoints) return null;
3207
+ let excCursor = 0, refCursor = 0;
3208
+ const excitationAtIndex = (i) => {
3209
+ while (excCursor < eIdx.length && eIdx[excCursor] < i) excCursor++;
3210
+ return excCursor < eIdx.length && eIdx[excCursor] === i;
3211
+ };
3212
+ const refocusingAtIndex = (i) => {
3213
+ while (refCursor < rIdx.length && rIdx[refCursor] < i) refCursor++;
3214
+ return refCursor < rIdx.length && rIdx[refCursor] === i;
3215
+ };
3164
3216
  const outCount = _options?.maxTrajectoryPoints && _options.maxTrajectoryPoints > 0 ? Math.min(_options.maxTrajectoryPoints, N) : N;
3165
3217
  const outStep = N / outCount;
3166
3218
  const outX = new Float64Array(outCount), outY = new Float64Array(outCount), outZ = new Float64Array(outCount);
@@ -3177,10 +3229,12 @@ var Pulseq = (() => {
3177
3229
  let out = 0;
3178
3230
  let nextKeep = 0;
3179
3231
  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);
3183
- if (refocusingAt[0] && !excitationAt[0]) {
3232
+ const nextGridTime = makeGridWalk();
3233
+ let tPrev = nextGridTime();
3234
+ let gxPrev = sampleSeries(gradientSeries[0], tPrev, cursors, 0);
3235
+ let gyPrev = sampleSeries(gradientSeries[1], tPrev, cursors, 1);
3236
+ let gzPrev = sampleSeries(gradientSeries[2], tPrev, cursors, 2);
3237
+ if (refocusingAtIndex(0) && !excitationAtIndex(0)) {
3184
3238
  lx = -lx;
3185
3239
  ly = -ly;
3186
3240
  lz = -lz;
@@ -3188,24 +3242,24 @@ var Pulseq = (() => {
3188
3242
  if (nextKeep === 0) {
3189
3243
  while (ec < eIdx.length && eIdx[ec] < 1) ec++;
3190
3244
  const brk = ec < eIdx.length && eIdx[ec] === 1;
3191
- outT[0] = grid[0];
3245
+ outT[0] = tPrev;
3192
3246
  outX[0] = brk ? NaN : lx;
3193
3247
  outY[0] = brk ? NaN : ly;
3194
3248
  outZ[0] = brk ? NaN : lz;
3195
3249
  out = 1;
3196
3250
  nextKeep = out < outCount ? Math.floor(out * outStep) : -1;
3197
3251
  }
3198
- while (a < nA && adcT[a] <= grid[0]) {
3252
+ while (a < nA && adcT[a] <= tPrev) {
3199
3253
  kxA[a] = lx;
3200
3254
  kyA[a] = ly;
3201
3255
  kzA[a] = lz;
3202
3256
  a++;
3203
3257
  }
3204
3258
  for (let i = 1; i < N; i++) {
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];
3259
+ const tCur = nextGridTime();
3260
+ const gxi = sampleSeries(gradientSeries[0], tCur, cursors, 0);
3261
+ const gyi = sampleSeries(gradientSeries[1], tCur, cursors, 1);
3262
+ const gzi = sampleSeries(gradientSeries[2], tCur, cursors, 2);
3209
3263
  const dt = tCur - tPrev;
3210
3264
  const px = lx, py = ly, pz = lz;
3211
3265
  if (dt > 0) {
@@ -3220,14 +3274,14 @@ var Pulseq = (() => {
3220
3274
  lx = nx;
3221
3275
  ly = ny;
3222
3276
  lz = nz;
3223
- if (excitationAt[i]) {
3277
+ if (excitationAtIndex(i)) {
3224
3278
  lx = 0;
3225
3279
  ly = 0;
3226
3280
  lz = 0;
3227
3281
  cx = 0;
3228
3282
  cy = 0;
3229
3283
  cz = 0;
3230
- } else if (refocusingAt[i]) {
3284
+ } else if (refocusingAtIndex(i)) {
3231
3285
  lx = -lx;
3232
3286
  ly = -ly;
3233
3287
  lz = -lz;
@@ -3262,6 +3316,7 @@ var Pulseq = (() => {
3262
3316
  gxPrev = gxi;
3263
3317
  gyPrev = gyi;
3264
3318
  gzPrev = gzi;
3319
+ tPrev = tCur;
3265
3320
  }
3266
3321
  while (a < nA) {
3267
3322
  kxA[a] = lx;
@@ -3290,6 +3345,17 @@ var Pulseq = (() => {
3290
3345
  }
3291
3346
  for (const time of series.requiredSupport) push(time);
3292
3347
  }
3348
+ function freezeSeries(series) {
3349
+ const frozen = {
3350
+ times: Float64Array.from(series.times),
3351
+ values: Float64Array.from(series.values),
3352
+ requiredSupport: Float64Array.from(series.requiredSupport)
3353
+ };
3354
+ series.times.length = 0;
3355
+ series.values.length = 0;
3356
+ series.requiredSupport.length = 0;
3357
+ return frozen;
3358
+ }
3293
3359
  function buildGlobalGradientSeries(blocks, gradientRaster, totalDuration) {
3294
3360
  const output = [
3295
3361
  { times: [], values: [], requiredSupport: [] },
@@ -3317,7 +3383,7 @@ var Pulseq = (() => {
3317
3383
  series.requiredSupport.push(last + POLYNOMIAL_SUPPORT_EPSILON_SEC, totalDuration + POLYNOMIAL_SUPPORT_EPSILON_SEC);
3318
3384
  }
3319
3385
  }
3320
- return output;
3386
+ return [freezeSeries(output[0]), freezeSeries(output[1]), freezeSeries(output[2])];
3321
3387
  }
3322
3388
  function appendGradientPiece(target, piece, gradientRaster) {
3323
3389
  if (!piece.times.length) return;
@@ -3371,15 +3437,6 @@ var Pulseq = (() => {
3371
3437
  if (time >= t1) return v1;
3372
3438
  return v0 + (v1 - v0) * (time - t0) / (t1 - t0);
3373
3439
  }
3374
- function timeIdx(t, g) {
3375
- let lo = 0, hi = g.length;
3376
- while (lo < hi) {
3377
- const m = lo + hi >> 1;
3378
- if (g[m] < t - 1e-12) lo = m + 1;
3379
- else hi = m;
3380
- }
3381
- return lo < g.length ? lo : -1;
3382
- }
3383
3440
 
3384
3441
  // src/editor/kspaceTransport.ts
3385
3442
  var MAX_KSPACE_OVERVIEW_POINTS = 3e4;