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.
- {seqeyes_python-0.3.14 → seqeyes_python-0.3.15}/PKG-INFO +1 -1
- {seqeyes_python-0.3.14 → seqeyes_python-0.3.15}/pyproject.toml +1 -1
- {seqeyes_python-0.3.14 → seqeyes_python-0.3.15}/src/seqeyes/__init__.py +1 -1
- {seqeyes_python-0.3.14 → seqeyes_python-0.3.15}/src/seqeyes/resources/pulseq-bundle.js +104 -47
- {seqeyes_python-0.3.14 → seqeyes_python-0.3.15}/.gitignore +0 -0
- {seqeyes_python-0.3.14 → seqeyes_python-0.3.15}/README.md +0 -0
- {seqeyes_python-0.3.14 → seqeyes_python-0.3.15}/examples/demo.ipynb +0 -0
- {seqeyes_python-0.3.14 → seqeyes_python-0.3.15}/examples/demo.py +0 -0
- {seqeyes_python-0.3.14 → seqeyes_python-0.3.15}/src/seqeyes/_plot.py +0 -0
- {seqeyes_python-0.3.14 → seqeyes_python-0.3.15}/src/seqeyes/_renderer.py +0 -0
- {seqeyes_python-0.3.14 → seqeyes_python-0.3.15}/src/seqeyes/resources/seqeyes-labels.js +0 -0
- {seqeyes_python-0.3.14 → seqeyes_python-0.3.15}/src/seqeyes/resources/viewer.html +0 -0
- {seqeyes_python-0.3.14 → seqeyes_python-0.3.15}/src/seqeyes/resources/webview-bundle.js +0 -0
- {seqeyes_python-0.3.14 → seqeyes_python-0.3.15}/tests/conftest.py +0 -0
- {seqeyes_python-0.3.14 → seqeyes_python-0.3.15}/tests/debug_full.html +0 -0
- {seqeyes_python-0.3.14 → seqeyes_python-0.3.15}/tests/debug_minimal.html +0 -0
- {seqeyes_python-0.3.14 → seqeyes_python-0.3.15}/tests/debug_viewer.html +0 -0
- {seqeyes_python-0.3.14 → seqeyes_python-0.3.15}/tests/debug_viewer.py +0 -0
- {seqeyes_python-0.3.14 → seqeyes_python-0.3.15}/tests/integration_test.py +0 -0
- {seqeyes_python-0.3.14 → seqeyes_python-0.3.15}/tests/test_plot.py +0 -0
- {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.
|
|
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.
|
|
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"}
|
|
@@ -90,7 +90,7 @@ var Pulseq = (() => {
|
|
|
90
90
|
});
|
|
91
91
|
|
|
92
92
|
// package.json
|
|
93
|
-
var version = "0.3.
|
|
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) *
|
|
2330
|
+
const gridBytes = Math.max(0, estimate.gridCandidatePoints) * 1;
|
|
2331
2331
|
const adcAndTransferBytes = Math.max(0, estimate.adcSamples) * 43;
|
|
2332
|
-
|
|
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
|
|
3146
|
-
|
|
3147
|
-
|
|
3148
|
-
const
|
|
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
|
-
|
|
3151
|
-
|
|
3152
|
-
|
|
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
|
-
|
|
3155
|
-
|
|
3156
|
-
|
|
3157
|
-
|
|
3158
|
-
|
|
3159
|
-
|
|
3160
|
-
|
|
3161
|
-
const
|
|
3162
|
-
|
|
3163
|
-
|
|
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
|
-
|
|
3181
|
-
let
|
|
3182
|
-
let
|
|
3183
|
-
|
|
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] =
|
|
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] <=
|
|
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
|
|
3206
|
-
const
|
|
3207
|
-
const
|
|
3208
|
-
const
|
|
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 (
|
|
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 (
|
|
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;
|
|
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
|
|
File without changes
|