seqeyes-python 0.3.14__py3-none-any.whl → 0.3.16__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 +1 -1
- seqeyes/resources/pulseq-bundle.js +104 -47
- seqeyes/resources/webview-bundle.js +28 -1
- {seqeyes_python-0.3.14.dist-info → seqeyes_python-0.3.16.dist-info}/METADATA +1 -1
- seqeyes_python-0.3.16.dist-info/RECORD +10 -0
- seqeyes_python-0.3.14.dist-info/RECORD +0 -10
- {seqeyes_python-0.3.14.dist-info → seqeyes_python-0.3.16.dist-info}/WHEEL +0 -0
seqeyes/__init__.py
CHANGED
|
@@ -90,7 +90,7 @@ var Pulseq = (() => {
|
|
|
90
90
|
});
|
|
91
91
|
|
|
92
92
|
// package.json
|
|
93
|
-
var version = "0.3.
|
|
93
|
+
var version = "0.3.16";
|
|
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;
|
|
@@ -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
|
|
@@ -7421,7 +7448,7 @@ function showTooltipAt(cx,cy,ct){
|
|
|
7421
7448
|
}
|
|
7422
7449
|
appendDerivedTooltipLines(lines,ct);
|
|
7423
7450
|
if(lines.length>1||found){
|
|
7424
|
-
tt
|
|
7451
|
+
renderTooltipLines(tt,lines);tt.style.display='block';
|
|
7425
7452
|
placeTooltip(cx,cy);
|
|
7426
7453
|
// Auto-dismiss on touch devices
|
|
7427
7454
|
if(_touchTooltipTimer)clearTimeout(_touchTooltipTimer);
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: seqeyes-python
|
|
3
|
-
Version: 0.3.
|
|
3
|
+
Version: 0.3.16
|
|
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=5RLKLf76BMFAcXE_TlB80SCBRZD91BMEbXG1SJGk5tQ,945
|
|
2
|
+
seqeyes/_plot.py,sha256=v5cPAn3mO017lQlNR4bthC0QgIBK1YxjygJTAPFUz1E,8114
|
|
3
|
+
seqeyes/_renderer.py,sha256=wGLMnQvajBXFrbMBO5RPchnWNplxwAR0i5uv8FkNMcs,11100
|
|
4
|
+
seqeyes/resources/pulseq-bundle.js,sha256=cuIycmnOARm6iDjoLPAnkn0MWZORzrWCQDhGTbNQ8Mk,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=S1381m8x66n9m0d9znk5VEZyWfJVM6hEueCsqKryvWQ,342856
|
|
8
|
+
seqeyes_python-0.3.16.dist-info/METADATA,sha256=FUMv9dX22SfWxkklraqgqjlTg-Jm0pOodUICxIhSwaQ,4503
|
|
9
|
+
seqeyes_python-0.3.16.dist-info/WHEEL,sha256=W3fkpkm7-wf9vBI5Z-7s0eWkeM-spu78I8Neb98DeEg,87
|
|
10
|
+
seqeyes_python-0.3.16.dist-info/RECORD,,
|
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
seqeyes/__init__.py,sha256=EqP3fnPayUaip8RgoRSyG5IhWeJwzJ-GBVYvUljftEM,945
|
|
2
|
-
seqeyes/_plot.py,sha256=v5cPAn3mO017lQlNR4bthC0QgIBK1YxjygJTAPFUz1E,8114
|
|
3
|
-
seqeyes/_renderer.py,sha256=wGLMnQvajBXFrbMBO5RPchnWNplxwAR0i5uv8FkNMcs,11100
|
|
4
|
-
seqeyes/resources/pulseq-bundle.js,sha256=s80GrP7fYUyYEixr0YN7F54u5WLjFYNghY-SzuIspRI,225298
|
|
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.14.dist-info/METADATA,sha256=0TvmLn1OXDwPgNv4ssQanWYtT_IWYPa2qhN-q-aFB00,4503
|
|
9
|
-
seqeyes_python-0.3.14.dist-info/WHEEL,sha256=W3fkpkm7-wf9vBI5Z-7s0eWkeM-spu78I8Neb98DeEg,87
|
|
10
|
-
seqeyes_python-0.3.14.dist-info/RECORD,,
|
|
File without changes
|