@niivue/niivue 0.67.1-dev.0 → 0.67.1-dev.2
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.
- package/build/drawing/index.d.ts +1 -1
- package/build/drawing/index.js.map +1 -1
- package/build/index.min.js +1 -1
- package/build/metafile-esm.json +1 -1
- package/build/niivue/index.d.ts +3 -2
- package/build/niivue/index.js +171 -26
- package/build/niivue/index.js.map +1 -1
- package/build/{nvdocument-CcP6istx.d.ts → nvdocument-DrX6RoEG.d.ts} +32 -2
- package/build/utils/index.d.ts +1 -1
- package/dist/index.d.ts +33 -2
- package/dist/index.js +171 -26
- package/dist/index.js.map +1 -1
- package/dist/index.min.js +1 -1
- package/dist/metafile-esm.json +1 -1
- package/dist/niivue.umd.js +31 -31
- package/package.json +108 -108
- package/src/index_zarr_viewer.html +255 -68
- package/src/niivue/descriptives.ts +1 -1
- package/src/niivue/index.ts +40 -3
- package/src/nvimage/index.ts +11 -3
- package/src/nvimage/utils.ts +2 -0
- package/src/nvimage/zarr/NVZarrHelper.ts +174 -25
- package/src/index_zarr.html +0 -152
|
@@ -12,30 +12,50 @@
|
|
|
12
12
|
display: flex;
|
|
13
13
|
gap: 16px;
|
|
14
14
|
align-items: center;
|
|
15
|
-
padding:
|
|
16
|
-
background: #
|
|
15
|
+
padding: 10px 12px;
|
|
16
|
+
background: #111;
|
|
17
17
|
flex-wrap: wrap;
|
|
18
|
+
border-bottom: 1px solid #222;
|
|
18
19
|
}
|
|
19
20
|
#controls label {
|
|
20
|
-
color: #
|
|
21
|
+
color: #999;
|
|
22
|
+
font-size: 13px;
|
|
21
23
|
}
|
|
22
24
|
#controls input,
|
|
23
25
|
#controls select {
|
|
24
|
-
padding:
|
|
26
|
+
padding: 5px 8px;
|
|
27
|
+
background: #222;
|
|
28
|
+
color: #eee;
|
|
29
|
+
border: 1px solid #444;
|
|
30
|
+
border-radius: 3px;
|
|
31
|
+
}
|
|
32
|
+
#datasetSelect {
|
|
33
|
+
max-width: 360px;
|
|
34
|
+
}
|
|
35
|
+
#datasetSelect option {
|
|
36
|
+
padding: 2px 4px;
|
|
37
|
+
}
|
|
38
|
+
#datasetSelect optgroup {
|
|
39
|
+
color: #aaa;
|
|
40
|
+
font-style: normal;
|
|
25
41
|
}
|
|
26
42
|
#loadBtn {
|
|
27
|
-
background: #
|
|
43
|
+
background: #fff;
|
|
28
44
|
border: none;
|
|
29
|
-
color:
|
|
45
|
+
color: #000;
|
|
30
46
|
padding: 8px 16px;
|
|
31
47
|
cursor: pointer;
|
|
32
48
|
border-radius: 4px;
|
|
49
|
+
font-weight: 600;
|
|
50
|
+
font-size: 13px;
|
|
51
|
+
letter-spacing: 0.01em;
|
|
33
52
|
}
|
|
34
53
|
#loadBtn:hover {
|
|
35
|
-
background: #
|
|
54
|
+
background: #ddd;
|
|
36
55
|
}
|
|
37
56
|
#loadBtn:disabled {
|
|
38
|
-
background: #
|
|
57
|
+
background: #555;
|
|
58
|
+
color: #888;
|
|
39
59
|
cursor: not-allowed;
|
|
40
60
|
}
|
|
41
61
|
#navControls {
|
|
@@ -44,15 +64,15 @@
|
|
|
44
64
|
align-items: center;
|
|
45
65
|
}
|
|
46
66
|
#navControls button {
|
|
47
|
-
background: #
|
|
48
|
-
border: 1px solid #
|
|
49
|
-
color:
|
|
67
|
+
background: #222;
|
|
68
|
+
border: 1px solid #444;
|
|
69
|
+
color: #eee;
|
|
50
70
|
padding: 4px 12px;
|
|
51
71
|
cursor: pointer;
|
|
52
|
-
border-radius:
|
|
72
|
+
border-radius: 3px;
|
|
53
73
|
}
|
|
54
74
|
#navControls button:hover {
|
|
55
|
-
background: #
|
|
75
|
+
background: #333;
|
|
56
76
|
}
|
|
57
77
|
#levelSelectorsContainer {
|
|
58
78
|
display: flex;
|
|
@@ -64,38 +84,44 @@
|
|
|
64
84
|
display: flex;
|
|
65
85
|
gap: 4px;
|
|
66
86
|
align-items: center;
|
|
67
|
-
background: #
|
|
87
|
+
background: #1a1a1a;
|
|
68
88
|
padding: 4px 8px;
|
|
69
|
-
border-radius:
|
|
89
|
+
border-radius: 3px;
|
|
90
|
+
border: 1px solid #333;
|
|
70
91
|
}
|
|
71
92
|
.level-selector label {
|
|
72
|
-
color: #
|
|
93
|
+
color: #777;
|
|
73
94
|
font-size: 11px;
|
|
74
95
|
}
|
|
75
96
|
.level-selector select {
|
|
76
97
|
padding: 2px 4px;
|
|
98
|
+
background: #222;
|
|
99
|
+
color: #eee;
|
|
100
|
+
border: 1px solid #444;
|
|
101
|
+
border-radius: 3px;
|
|
77
102
|
}
|
|
78
103
|
#infoPanel {
|
|
79
|
-
padding: 8px;
|
|
80
|
-
background: #
|
|
81
|
-
color: #
|
|
104
|
+
padding: 8px 12px;
|
|
105
|
+
background: #0a0a0a;
|
|
106
|
+
color: #999;
|
|
82
107
|
font-family: monospace;
|
|
83
108
|
font-size: 12px;
|
|
84
109
|
display: none;
|
|
110
|
+
border-bottom: 1px solid #222;
|
|
85
111
|
}
|
|
86
112
|
.hint {
|
|
87
|
-
color: #
|
|
113
|
+
color: #666;
|
|
88
114
|
font-size: 11px;
|
|
89
115
|
font-style: italic;
|
|
90
116
|
}
|
|
91
117
|
#affineControls {
|
|
92
118
|
display: none;
|
|
93
119
|
padding: 8px 12px;
|
|
94
|
-
background: #
|
|
95
|
-
border-top: 1px solid #
|
|
120
|
+
background: #0d0d0d;
|
|
121
|
+
border-top: 1px solid #222;
|
|
96
122
|
}
|
|
97
123
|
#affineControls h3 {
|
|
98
|
-
color: #
|
|
124
|
+
color: #bbb;
|
|
99
125
|
margin: 0 0 8px 0;
|
|
100
126
|
font-size: 13px;
|
|
101
127
|
display: flex;
|
|
@@ -103,22 +129,26 @@
|
|
|
103
129
|
gap: 12px;
|
|
104
130
|
}
|
|
105
131
|
#affineControls h3 button {
|
|
106
|
-
background: #
|
|
107
|
-
border:
|
|
108
|
-
color:
|
|
132
|
+
background: #333;
|
|
133
|
+
border: 1px solid #555;
|
|
134
|
+
color: #eee;
|
|
109
135
|
padding: 2px 10px;
|
|
110
136
|
cursor: pointer;
|
|
111
137
|
border-radius: 3px;
|
|
112
138
|
font-size: 11px;
|
|
113
139
|
}
|
|
114
140
|
#affineControls h3 button:hover {
|
|
115
|
-
background: #
|
|
141
|
+
background: #444;
|
|
116
142
|
}
|
|
117
143
|
#affineSaveBtn, #affineLoadBtn {
|
|
118
|
-
background: #
|
|
144
|
+
background: #fff !important;
|
|
145
|
+
color: #000 !important;
|
|
146
|
+
border-color: #fff !important;
|
|
147
|
+
font-weight: 600;
|
|
119
148
|
}
|
|
120
149
|
#affineSaveBtn:hover, #affineLoadBtn:hover {
|
|
121
|
-
background: #
|
|
150
|
+
background: #ddd !important;
|
|
151
|
+
border-color: #ddd !important;
|
|
122
152
|
}
|
|
123
153
|
.affine-group {
|
|
124
154
|
display: flex;
|
|
@@ -127,7 +157,7 @@
|
|
|
127
157
|
margin-bottom: 4px;
|
|
128
158
|
}
|
|
129
159
|
.affine-group label {
|
|
130
|
-
color: #
|
|
160
|
+
color: #777;
|
|
131
161
|
font-size: 11px;
|
|
132
162
|
font-family: monospace;
|
|
133
163
|
}
|
|
@@ -140,7 +170,7 @@
|
|
|
140
170
|
width: 120px;
|
|
141
171
|
}
|
|
142
172
|
.affine-slider .val {
|
|
143
|
-
color: #
|
|
173
|
+
color: #ccc;
|
|
144
174
|
font-family: monospace;
|
|
145
175
|
font-size: 11px;
|
|
146
176
|
min-width: 50px;
|
|
@@ -151,15 +181,79 @@
|
|
|
151
181
|
<body>
|
|
152
182
|
<header>
|
|
153
183
|
<div id="controls">
|
|
154
|
-
<label for="
|
|
184
|
+
<label for="datasetSelect"><a href="https://github.com/InsightSoftwareConsortium/OMEZarrOpenSciVisDatasets" target="_blank" rel="noopener" style="color:#999;text-decoration:underline">Dataset</a>:</label>
|
|
185
|
+
<select id="datasetSelect">
|
|
186
|
+
<option value="">-- Custom URL --</option>
|
|
187
|
+
<optgroup label="CT Scans">
|
|
188
|
+
<option value="https://ome-zarr-scivis.s3.us-east-1.amazonaws.com/v0.5/96x2/aneurism.ome.zarr">Aneurism - head arteries, 256³, uint8</option>
|
|
189
|
+
<option value="https://ome-zarr-scivis.s3.us-east-1.amazonaws.com/v0.5/96x2/backpack.ome.zarr">Backpack - CT of backpack, 512x512x373, uint16</option>
|
|
190
|
+
<option value="https://ome-zarr-scivis.s3.us-east-1.amazonaws.com/v0.5/96x2/beechnut.ome.zarr">Beechnut - microCT, 1024x1024x1546, uint16</option>
|
|
191
|
+
<option value="https://ome-zarr-scivis.s3.us-east-1.amazonaws.com/v0.5/96x2/bonsai.ome.zarr">Bonsai - CT of tree, 256³, uint8</option>
|
|
192
|
+
<option value="https://ome-zarr-scivis.s3.us-east-1.amazonaws.com/v0.5/96x2/boston_teapot.ome.zarr">Boston Teapot - SIGGRAPH teapot, 256x256x178, uint8</option>
|
|
193
|
+
<option value="https://ome-zarr-scivis.s3.us-east-1.amazonaws.com/v0.5/96x2/bunny.ome.zarr">Bunny - Stanford Bunny CT, 512x512x361, uint16</option>
|
|
194
|
+
<option value="https://ome-zarr-scivis.s3.us-east-1.amazonaws.com/v0.5/96x2/carp.ome.zarr">Carp - fish CT, 256x256x512, uint16</option>
|
|
195
|
+
<option value="https://ome-zarr-scivis.s3.us-east-1.amazonaws.com/v0.5/96x2/chameleon.ome.zarr">Chameleon - CT, 1024x1024x1080, uint16</option>
|
|
196
|
+
<option value="https://ome-zarr-scivis.s3.us-east-1.amazonaws.com/v0.5/96x2/christmas_tree.ome.zarr">Christmas Tree - CT, 512x499x512, uint16</option>
|
|
197
|
+
<option value="https://ome-zarr-scivis.s3.us-east-1.amazonaws.com/v0.5/96x2/engine.ome.zarr">Engine - engine block CT, 256x256x128, uint8</option>
|
|
198
|
+
<option value="https://ome-zarr-scivis.s3.us-east-1.amazonaws.com/v0.5/96x2/foot.ome.zarr">Foot - human foot, 256³, uint8</option>
|
|
199
|
+
<option value="https://ome-zarr-scivis.s3.us-east-1.amazonaws.com/v0.5/96x2/kingsnake.ome.zarr">Kingsnake - snake egg CT, 1024x1024x795, uint8</option>
|
|
200
|
+
<option value="https://ome-zarr-scivis.s3.us-east-1.amazonaws.com/v0.5/96x2/lobster.ome.zarr">Lobster - CT in resin, 301x324x56, uint8</option>
|
|
201
|
+
<option value="https://ome-zarr-scivis.s3.us-east-1.amazonaws.com/v0.5/96x2/pancreas.ome.zarr">Pancreas - abdominal CT, 240x512x512, int16</option>
|
|
202
|
+
<option value="https://ome-zarr-scivis.s3.us-east-1.amazonaws.com/v0.5/96x2/pawpawsaurus.ome.zarr" selected>Pawpawsaurus - fossil CT, 958x646x1088, uint16</option>
|
|
203
|
+
<option value="https://ome-zarr-scivis.s3.us-east-1.amazonaws.com/v0.5/96x2/pig_heart.ome.zarr">Pig Heart - microCT, 2048x2048x2612, int16</option>
|
|
204
|
+
<option value="https://ome-zarr-scivis.s3.us-east-1.amazonaws.com/v0.5/96x2/present.ome.zarr">Present - industrial CT, 492x492x442, uint16</option>
|
|
205
|
+
<option value="https://ome-zarr-scivis.s3.us-east-1.amazonaws.com/v0.5/96x2/prone.ome.zarr">Prone - abdomen CT, 512x512x463, uint16</option>
|
|
206
|
+
<option value="https://ome-zarr-scivis.s3.us-east-1.amazonaws.com/v0.5/96x2/skull.ome.zarr">Skull - phantom skull, 256³, uint8</option>
|
|
207
|
+
<option value="https://ome-zarr-scivis.s3.us-east-1.amazonaws.com/v0.5/96x2/spathorhynchus.ome.zarr">Spathorhynchus - fossil CT, 1024x1024x750, uint16</option>
|
|
208
|
+
<option value="https://ome-zarr-scivis.s3.us-east-1.amazonaws.com/v0.5/96x2/stag_beetle.ome.zarr">Stag Beetle - industrial CT, 832x832x494, uint16</option>
|
|
209
|
+
<option value="https://ome-zarr-scivis.s3.us-east-1.amazonaws.com/v0.5/96x2/statue_leg.ome.zarr">Statue Leg - bronze statue CT, 341x341x93, uint8</option>
|
|
210
|
+
<option value="https://ome-zarr-scivis.s3.us-east-1.amazonaws.com/v0.5/96x2/stent.ome.zarr">Stent - abdomen with stent, 512x512x174, uint16</option>
|
|
211
|
+
<option value="https://ome-zarr-scivis.s3.us-east-1.amazonaws.com/v0.5/96x2/synthetic_truss_with_five_defects.ome.zarr">Synthetic Truss - simulated CT, 1200³, float32</option>
|
|
212
|
+
<option value="https://ome-zarr-scivis.s3.us-east-1.amazonaws.com/v0.5/96x2/vertebra.ome.zarr">Vertebra - angiography, 512³, uint16</option>
|
|
213
|
+
<option value="https://ome-zarr-scivis.s3.us-east-1.amazonaws.com/v0.5/96x2/vis_male.ome.zarr">Visible Male - head scan, 128x256x256, uint8</option>
|
|
214
|
+
<option value="https://ome-zarr-scivis.s3.us-east-1.amazonaws.com/v0.5/96x2/woodbranch.ome.zarr">Woodbranch - microCT, 2048³, uint16</option>
|
|
215
|
+
<option value="https://ome-zarr-scivis.s3.us-east-1.amazonaws.com/v0.5/96x2/zeiss.ome.zarr">Zeiss - car part CT, 680³, uint8</option>
|
|
216
|
+
</optgroup>
|
|
217
|
+
<optgroup label="MRI Scans">
|
|
218
|
+
<option value="https://ome-zarr-scivis.s3.us-east-1.amazonaws.com/v0.5/96x2/frog.ome.zarr">Frog - MRI, 256x256x44, uint8</option>
|
|
219
|
+
<option value="https://ome-zarr-scivis.s3.us-east-1.amazonaws.com/v0.5/96x2/mri_ventricles.ome.zarr">MRI Ventricles - head CSF, 256x256x124, uint8</option>
|
|
220
|
+
<option value="https://ome-zarr-scivis.s3.us-east-1.amazonaws.com/v0.5/96x2/mri_woman.ome.zarr">MRI Woman - head MRI, 256x256x109, uint16</option>
|
|
221
|
+
<option value="https://ome-zarr-scivis.s3.us-east-1.amazonaws.com/v0.5/96x2/mrt_angio.ome.zarr">MRT Angio - head angiography, 416x512x112, uint16</option>
|
|
222
|
+
</optgroup>
|
|
223
|
+
<optgroup label="Microscopy">
|
|
224
|
+
<option value="https://ome-zarr-scivis.s3.us-east-1.amazonaws.com/v0.5/96x2/marmoset_neurons.ome.zarr">Marmoset Neurons - V1 cortex, 1024x1024x314, uint8</option>
|
|
225
|
+
<option value="https://ome-zarr-scivis.s3.us-east-1.amazonaws.com/v0.5/96x2/neocortical_layer_1_axons.ome.zarr">Neocortical Axons - barrel cortex, 1464x1033x76, uint8</option>
|
|
226
|
+
</optgroup>
|
|
227
|
+
<optgroup label="Simulations">
|
|
228
|
+
<option value="https://ome-zarr-scivis.s3.us-east-1.amazonaws.com/v0.5/96x2/blunt_fin.ome.zarr">Blunt Fin - flow sim, 256x128x64, uint8</option>
|
|
229
|
+
<option value="https://ome-zarr-scivis.s3.us-east-1.amazonaws.com/v0.5/96x2/csafe_heptane.ome.zarr">CSAFE Heptane - combustion sim, 302³, uint8</option>
|
|
230
|
+
<option value="https://ome-zarr-scivis.s3.us-east-1.amazonaws.com/v0.5/96x2/duct.ome.zarr">Duct - wall-bounded flow, 193x194x1000, float32</option>
|
|
231
|
+
<option value="https://ome-zarr-scivis.s3.us-east-1.amazonaws.com/v0.5/96x2/fuel.ome.zarr">Fuel - fuel injection sim, 64³, uint8</option>
|
|
232
|
+
<option value="https://ome-zarr-scivis.s3.us-east-1.amazonaws.com/v0.5/96x2/hcci_oh.ome.zarr">HCCI OH - autoignition sim, 560³, float32</option>
|
|
233
|
+
<option value="https://ome-zarr-scivis.s3.us-east-1.amazonaws.com/v0.5/96x2/hydrogen_atom.ome.zarr">Hydrogen Atom - electron dist, 128³, uint8</option>
|
|
234
|
+
<option value="https://ome-zarr-scivis.s3.us-east-1.amazonaws.com/v0.5/96x2/jicf_q.ome.zarr">JICF Q - jet crossflow, 1408x1080x1100, float32</option>
|
|
235
|
+
<option value="https://ome-zarr-scivis.s3.us-east-1.amazonaws.com/v0.5/96x2/magnetic_reconnection.ome.zarr">Magnetic Reconnection - sim, 512³, float32</option>
|
|
236
|
+
<option value="https://ome-zarr-scivis.s3.us-east-1.amazonaws.com/v0.5/96x2/marschner_lobb.ome.zarr">Marschner-Lobb - high freq test, 41³, uint8</option>
|
|
237
|
+
<option value="https://ome-zarr-scivis.s3.us-east-1.amazonaws.com/v0.5/96x2/miranda.ome.zarr">Miranda - Rayleigh-Taylor sim, 1024³, float32</option>
|
|
238
|
+
<option value="https://ome-zarr-scivis.s3.us-east-1.amazonaws.com/v0.5/96x2/neghip.ome.zarr">Neghip - protein electron dist, 64³, uint8</option>
|
|
239
|
+
<option value="https://ome-zarr-scivis.s3.us-east-1.amazonaws.com/v0.5/96x2/nucleon.ome.zarr">Nucleon - nuclear sim, 41³, uint8</option>
|
|
240
|
+
<option value="https://ome-zarr-scivis.s3.us-east-1.amazonaws.com/v0.5/96x2/richtmyer_meshkov.ome.zarr">Richtmyer-Meshkov - instability, 2048x2048x1920, uint8</option>
|
|
241
|
+
<option value="https://ome-zarr-scivis.s3.us-east-1.amazonaws.com/v0.5/96x2/shockwave.ome.zarr">Shockwave - planar shock sim, 64x64x512, uint8</option>
|
|
242
|
+
<option value="https://ome-zarr-scivis.s3.us-east-1.amazonaws.com/v0.5/96x2/silicium.ome.zarr">Silicium - grid sim, 98x34x34, uint8</option>
|
|
243
|
+
<option value="https://ome-zarr-scivis.s3.us-east-1.amazonaws.com/v0.5/96x2/tacc_turbulence.ome.zarr">TACC Turbulence - enstrophy, 256³, float32</option>
|
|
244
|
+
</optgroup>
|
|
245
|
+
<optgroup label="Other">
|
|
246
|
+
<option value="https://ome-zarr-scivis.s3.us-east-1.amazonaws.com/v0.5/96x2/tooth.ome.zarr">Tooth - 103x94x161, uint8</option>
|
|
247
|
+
</optgroup>
|
|
248
|
+
</select>
|
|
249
|
+
|
|
250
|
+
<label for="storeUrl">URL:</label>
|
|
155
251
|
<input
|
|
156
252
|
type="text"
|
|
157
253
|
id="storeUrl"
|
|
158
254
|
value="https://ome-zarr-scivis.s3.us-east-1.amazonaws.com/v0.5/96x2/pawpawsaurus.ome.zarr"
|
|
159
255
|
style="width: 400px"
|
|
160
256
|
/>
|
|
161
|
-
<!-- https://ome-zarr-scivis.s3.us-east-1.amazonaws.com/v0.5/96x2/pawpawsaurus.ome.zarr -->
|
|
162
|
-
<!-- https://ome-zarr-scivis.s3.us-east-1.amazonaws.com/v0.5/96x2/chameleon.ome.zarr -->
|
|
163
257
|
|
|
164
258
|
<label for="maxVolSize">Max Volume Size:</label>
|
|
165
259
|
<select id="maxVolSize">
|
|
@@ -178,7 +272,26 @@
|
|
|
178
272
|
<option value="5">5</option>
|
|
179
273
|
</select>
|
|
180
274
|
|
|
275
|
+
<label for="colormapSelect">Colormap:</label>
|
|
276
|
+
<select id="colormapSelect"></select>
|
|
277
|
+
|
|
278
|
+
<label for="primaryDrag">Left Drag:</label>
|
|
279
|
+
<select id="primaryDrag">
|
|
280
|
+
<option value="crosshair">crosshair</option>
|
|
281
|
+
<option value="contrast">brightness/contrast</option>
|
|
282
|
+
</select>
|
|
283
|
+
|
|
284
|
+
<label for="secondaryDrag">Right Drag:</label>
|
|
285
|
+
<select id="secondaryDrag">
|
|
286
|
+
<option value="contrast">contrast</option>
|
|
287
|
+
<option value="pan" selected>pan</option>
|
|
288
|
+
<option value="measurement">measurement</option>
|
|
289
|
+
<option value="slicer3D">slicer3D</option>
|
|
290
|
+
<option value="none">none</option>
|
|
291
|
+
</select>
|
|
292
|
+
|
|
181
293
|
<button id="loadBtn">Load Zarr</button>
|
|
294
|
+
<button id="toggleAffineBtn" style="display:none;background:#222;border:1px solid #444;color:#eee;padding:8px 12px;cursor:pointer;border-radius:4px;font-size:13px;">Registration</button>
|
|
182
295
|
|
|
183
296
|
<div id="navControls">
|
|
184
297
|
<span class="hint">Drag to pan</span>
|
|
@@ -262,6 +375,7 @@
|
|
|
262
375
|
<script type="module" async>
|
|
263
376
|
import { Niivue, DRAG_MODE, SLICE_TYPE, SHOW_RENDER, MULTIPLANAR_TYPE, copyAffine } from './niivue/index.ts'
|
|
264
377
|
|
|
378
|
+
const datasetSelect = document.getElementById('datasetSelect')
|
|
265
379
|
const storeUrlInput = document.getElementById('storeUrl')
|
|
266
380
|
const maxVolSizeSelect = document.getElementById('maxVolSize')
|
|
267
381
|
const zarrLevelSelect = document.getElementById('zarrLevel')
|
|
@@ -274,6 +388,14 @@
|
|
|
274
388
|
const affineResetBtn = document.getElementById('affineResetBtn')
|
|
275
389
|
const affineSaveBtn = document.getElementById('affineSaveBtn')
|
|
276
390
|
const affineLoadBtn = document.getElementById('affineLoadBtn')
|
|
391
|
+
const toggleAffineBtn = document.getElementById('toggleAffineBtn')
|
|
392
|
+
|
|
393
|
+
toggleAffineBtn.addEventListener('click', () => {
|
|
394
|
+
const visible = affineControls.style.display === 'block'
|
|
395
|
+
affineControls.style.display = visible ? 'none' : 'block'
|
|
396
|
+
toggleAffineBtn.textContent = visible ? 'Registration' : 'Hide Registration'
|
|
397
|
+
nv.resizeListener()
|
|
398
|
+
})
|
|
277
399
|
|
|
278
400
|
// Affine slider elements
|
|
279
401
|
const sliders = {
|
|
@@ -299,6 +421,19 @@
|
|
|
299
421
|
sz: document.getElementById('szVal'),
|
|
300
422
|
}
|
|
301
423
|
|
|
424
|
+
// Sync dataset select with URL input
|
|
425
|
+
datasetSelect.addEventListener('change', () => {
|
|
426
|
+
if (datasetSelect.value) {
|
|
427
|
+
storeUrlInput.value = datasetSelect.value
|
|
428
|
+
}
|
|
429
|
+
})
|
|
430
|
+
|
|
431
|
+
// When URL input is manually edited, switch dropdown to "Custom URL"
|
|
432
|
+
storeUrlInput.addEventListener('input', () => {
|
|
433
|
+
const match = [...datasetSelect.options].find(o => o.value === storeUrlInput.value.trim())
|
|
434
|
+
datasetSelect.value = match ? match.value : ''
|
|
435
|
+
})
|
|
436
|
+
|
|
302
437
|
const ZARR_VOL_IDX = 1
|
|
303
438
|
|
|
304
439
|
function applyAffineFromSliders() {
|
|
@@ -399,11 +534,55 @@
|
|
|
399
534
|
multiplanarLayout: MULTIPLANAR_TYPE.GRID,
|
|
400
535
|
// isSliceMM: true
|
|
401
536
|
})
|
|
402
|
-
|
|
403
537
|
await nv.attachToCanvas(document.getElementById('gl1'))
|
|
404
538
|
nv.opts.multiplanarShowRender = SHOW_RENDER.ALWAYS
|
|
405
539
|
window.nv = nv
|
|
406
540
|
|
|
541
|
+
nv.onLocationChange = (data) => {
|
|
542
|
+
let text = data.string
|
|
543
|
+
const zarrVolumes = nv.getZarrVolumes()
|
|
544
|
+
for (const vol of zarrVolumes) {
|
|
545
|
+
const helper = vol.zarrHelper
|
|
546
|
+
if (!helper) continue
|
|
547
|
+
const coords = helper.mmToLevelCoords(data.mm[0], data.mm[1], data.mm[2])
|
|
548
|
+
const dims = coords.levelDims
|
|
549
|
+
const inBounds = coords.width >= 0 && coords.width < dims.width &&
|
|
550
|
+
coords.height >= 0 && coords.height < dims.height &&
|
|
551
|
+
coords.depth >= 0 && coords.depth < dims.depth
|
|
552
|
+
const coordStr = `L${coords.level}: ${coords.width}, ${coords.height}, ${coords.depth} / ${dims.width}x${dims.height}x${dims.depth}`
|
|
553
|
+
text += ` [${coordStr}${inBounds ? '' : ' OOB'}]`
|
|
554
|
+
}
|
|
555
|
+
statusBar.textContent = text
|
|
556
|
+
}
|
|
557
|
+
|
|
558
|
+
// Populate colormap select
|
|
559
|
+
const colormapSelect = document.getElementById('colormapSelect')
|
|
560
|
+
const maps = nv.colormaps()
|
|
561
|
+
for (const name of maps) {
|
|
562
|
+
const opt = document.createElement('option')
|
|
563
|
+
opt.value = name
|
|
564
|
+
opt.textContent = name
|
|
565
|
+
if (name === 'gray') opt.selected = true
|
|
566
|
+
colormapSelect.appendChild(opt)
|
|
567
|
+
}
|
|
568
|
+
colormapSelect.addEventListener('change', () => {
|
|
569
|
+
if (nv.volumes.length > 0) {
|
|
570
|
+
nv.setColormap(nv.volumes[0].id, colormapSelect.value)
|
|
571
|
+
}
|
|
572
|
+
})
|
|
573
|
+
|
|
574
|
+
// Primary drag (left mouse button)
|
|
575
|
+
document.getElementById('primaryDrag').addEventListener('change', (e) => {
|
|
576
|
+
nv.opts.dragModePrimary = e.target.value === 'contrast' ? 1 : 8
|
|
577
|
+
})
|
|
578
|
+
|
|
579
|
+
// Secondary drag (right mouse button)
|
|
580
|
+
document.getElementById('secondaryDrag').addEventListener('change', (e) => {
|
|
581
|
+
if (nv.dragModes[e.target.value] !== undefined) {
|
|
582
|
+
nv.opts.dragMode = nv.dragModes[e.target.value]
|
|
583
|
+
}
|
|
584
|
+
})
|
|
585
|
+
|
|
407
586
|
function updateInfoPanel() {
|
|
408
587
|
const helper = nv.getZarrVolume()?.zarrHelper
|
|
409
588
|
if (!helper) {
|
|
@@ -507,55 +686,62 @@
|
|
|
507
686
|
|
|
508
687
|
try {
|
|
509
688
|
await nv.loadVolumes([
|
|
510
|
-
// mni zarr (LSM simulated)
|
|
511
|
-
{
|
|
512
|
-
// url: storeUrl,
|
|
513
|
-
url: "http://localhost:8080/mni152.ome.zarr",
|
|
514
|
-
zarrLevel: level,
|
|
515
|
-
zarrMaxVolumeSize: maxVolSize,
|
|
516
|
-
opacity: 1.0,
|
|
517
|
-
colormap: "grey"
|
|
518
|
-
},
|
|
519
689
|
// mni nifti
|
|
520
|
-
{ url: "../demos/images/mni152.nii.gz", opacity: 1,colormap: "gray" },
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
// url: storeUrl,
|
|
524
|
-
url: "http://localhost:8080/mni152_axial.ome.zarr",
|
|
525
|
-
// url: "http://localhost:3000/K_HE_2_ome.zarr",
|
|
526
|
-
// url: "http://localhost:3000/E23191_25-1A.1.ome.zarr",
|
|
527
|
-
zarrLevel: level,
|
|
528
|
-
zarrMaxVolumeSize: maxVolSize,
|
|
529
|
-
channel: 0,
|
|
530
|
-
opacity: 1.0,
|
|
531
|
-
colormap: "red"
|
|
532
|
-
},
|
|
690
|
+
// { url: "../demos/images/mni152.nii.gz", opacity: 1,colormap: "gray" },
|
|
691
|
+
|
|
692
|
+
// mni zarr (LSM simulated)
|
|
533
693
|
// {
|
|
534
|
-
// url:
|
|
535
|
-
//
|
|
536
|
-
//
|
|
537
|
-
//
|
|
694
|
+
// // url: storeUrl,
|
|
695
|
+
// url: "http://localhost:8080/K_HE_2_multiscale.zarr",
|
|
696
|
+
// zarrLevel: level,
|
|
697
|
+
// zarrMaxVolumeSize: maxVolSize,
|
|
698
|
+
// opacity: 1.0,
|
|
699
|
+
// channel:1,
|
|
700
|
+
// colormap: "gray",
|
|
701
|
+
// cal_min:150,
|
|
702
|
+
// cal_max: 261
|
|
703
|
+
// },
|
|
704
|
+
// mni WSI image
|
|
538
705
|
// {
|
|
539
706
|
// // url: storeUrl,
|
|
540
|
-
//
|
|
541
|
-
// url: "http://localhost:3000/K_HE_2_ome.zarr",
|
|
542
|
-
// // url: "https://dandiarchive.s3.amazonaws.com/zarr/7723d02f-1f71-4553-a7b0-47bda1ae8b42",
|
|
707
|
+
// url: "http://localhost:8080/mni152_axial.ome.zarr",
|
|
708
|
+
// // url: "http://localhost:3000/K_HE_2_ome.zarr",
|
|
543
709
|
// // url: "http://localhost:3000/E23191_25-1A.1.ome.zarr",
|
|
544
|
-
// // url: "http://localhost:3000/test-image.zarr",
|
|
545
710
|
// zarrLevel: level,
|
|
546
711
|
// zarrMaxVolumeSize: maxVolSize,
|
|
712
|
+
// channel: 0,
|
|
547
713
|
// opacity: 1.0,
|
|
548
|
-
// colormap: "
|
|
714
|
+
// colormap: "red"
|
|
549
715
|
// },
|
|
716
|
+
// {
|
|
717
|
+
// url: "http://localhost:3000/export/nifti?zarr_path=K_HE_2_ome.zarr&scale=virtual_scale1&c=0&z=0&y=0&x=0&xdim=128&ydim=128&zdim=128",
|
|
718
|
+
// name: "nifti.nii.gz"
|
|
719
|
+
// }
|
|
720
|
+
// LSM image
|
|
721
|
+
{
|
|
722
|
+
url: storeUrl,
|
|
723
|
+
// url: "http://localhost:8080/mni152_axial.ome.zarr",
|
|
724
|
+
// url: "http://localhost:3000/K_HE_2_ome.zarr",
|
|
725
|
+
// url: "https://dandiarchive.s3.amazonaws.com/zarr/7723d02f-1f71-4553-a7b0-47bda1ae8b42",
|
|
726
|
+
// url: "http://localhost:3000/E23191_25-1A.1.ome.zarr",
|
|
727
|
+
// url: "http://localhost:3000/test-image.zarr",
|
|
728
|
+
zarrLevel: level,
|
|
729
|
+
zarrMaxVolumeSize: maxVolSize,
|
|
730
|
+
opacity: 1.0,
|
|
731
|
+
colormap: "gray",
|
|
732
|
+
name: "demo.ome.zarr"
|
|
733
|
+
},
|
|
550
734
|
|
|
551
735
|
|
|
552
736
|
])
|
|
553
737
|
// await nv.addVolumeFromUrl({ url: "../demos/images/mni152.nii.gz", opacity: 0.4,colormap: "red" })
|
|
554
738
|
// nv.setColormap(nv.volumes[0].id, 'gray')
|
|
555
739
|
|
|
556
|
-
// Show navigation and
|
|
740
|
+
// Show navigation controls and registration toggle
|
|
557
741
|
navControls.style.display = 'flex'
|
|
558
|
-
|
|
742
|
+
toggleAffineBtn.style.display = 'inline-block'
|
|
743
|
+
affineControls.style.display = 'none'
|
|
744
|
+
toggleAffineBtn.textContent = 'Registration'
|
|
559
745
|
resetAffineSliders()
|
|
560
746
|
|
|
561
747
|
// Create level selectors for all zarr volumes
|
|
@@ -581,6 +767,7 @@
|
|
|
581
767
|
navControls.style.display = 'none'
|
|
582
768
|
infoPanel.style.display = 'none'
|
|
583
769
|
affineControls.style.display = 'none'
|
|
770
|
+
toggleAffineBtn.style.display = 'none'
|
|
584
771
|
} finally {
|
|
585
772
|
loadBtn.disabled = false
|
|
586
773
|
loadBtn.textContent = 'Load Zarr'
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { NiftiHeader } from '@/types'
|
|
2
2
|
|
|
3
3
|
/** Typed array types supported for voxel data */
|
|
4
|
-
export type TypedVoxelArray = Float32Array | Uint8Array | Int16Array | Float64Array | Uint16Array
|
|
4
|
+
export type TypedVoxelArray = Float32Array | Uint8Array | Int16Array | Float64Array | Uint16Array | Int32Array | Uint32Array
|
|
5
5
|
|
|
6
6
|
/**
|
|
7
7
|
* Options for creating a mask array
|
package/src/niivue/index.ts
CHANGED
|
@@ -258,6 +258,8 @@ export class Niivue {
|
|
|
258
258
|
|
|
259
259
|
readyForSync = false
|
|
260
260
|
|
|
261
|
+
private _skipDragInDraw = false
|
|
262
|
+
|
|
261
263
|
// UI Data
|
|
262
264
|
uiData: UIData = {
|
|
263
265
|
mousedown: false,
|
|
@@ -1528,6 +1530,9 @@ export class Niivue {
|
|
|
1528
1530
|
}
|
|
1529
1531
|
if (this.uiData.isDragging) {
|
|
1530
1532
|
this.uiData.isDragging = false
|
|
1533
|
+
for (const vol of this.getZarrVolumes()) {
|
|
1534
|
+
vol.zarrHelper?.endDrag()
|
|
1535
|
+
}
|
|
1531
1536
|
|
|
1532
1537
|
// Handle angle measurement workflow
|
|
1533
1538
|
if (currentDragMode === DRAG_MODE.angle) {
|
|
@@ -1713,6 +1718,9 @@ export class Niivue {
|
|
|
1713
1718
|
// Handle drag completion
|
|
1714
1719
|
if (this.uiData.isDragging) {
|
|
1715
1720
|
this.uiData.isDragging = false
|
|
1721
|
+
for (const vol of this.getZarrVolumes()) {
|
|
1722
|
+
vol.zarrHelper?.endDrag()
|
|
1723
|
+
}
|
|
1716
1724
|
// if drag mode is contrast, and the user double taps and drags...
|
|
1717
1725
|
if (this.getCurrentDragMode() === DRAG_MODE.contrast) {
|
|
1718
1726
|
this.calculateNewRange()
|
|
@@ -1791,6 +1799,9 @@ export class Niivue {
|
|
|
1791
1799
|
if (this.uiData.isDragging || this.uiData.mousedown) {
|
|
1792
1800
|
log.debug('Mouse left canvas during drag, resetting drag state.')
|
|
1793
1801
|
this.uiData.isDragging = false
|
|
1802
|
+
for (const vol of this.getZarrVolumes()) {
|
|
1803
|
+
vol.zarrHelper?.endDrag()
|
|
1804
|
+
}
|
|
1794
1805
|
const resetState = MouseController.createResetButtonState()
|
|
1795
1806
|
this.uiData.mouseButtonLeftDown = resetState.mouseButtonLeftDown
|
|
1796
1807
|
this.uiData.mouseButtonCenterDown = resetState.mouseButtonCenterDown
|
|
@@ -2497,7 +2508,8 @@ export class Niivue {
|
|
|
2497
2508
|
zarrLevel: imageItem.zarrLevel,
|
|
2498
2509
|
zarrMaxVolumeSize: imageItem.zarrMaxVolumeSize,
|
|
2499
2510
|
zarrChannel: imageItem.zarrChannel,
|
|
2500
|
-
zarrConvertUnits: imageItem.zarrConvertUnits
|
|
2511
|
+
zarrConvertUnits: imageItem.zarrConvertUnits,
|
|
2512
|
+
zarrCenterMM: imageItem.zarrCenterMM
|
|
2501
2513
|
}
|
|
2502
2514
|
const volume = await NVImage.loadFromUrl(imageOptions)
|
|
2503
2515
|
this.document.addImageOptions(volume, imageOptions)
|
|
@@ -3150,12 +3162,35 @@ export class Niivue {
|
|
|
3150
3162
|
this.volumes = result.volumes
|
|
3151
3163
|
this.setVolume(volume, result.index)
|
|
3152
3164
|
this.onImageLoaded(volume)
|
|
3153
|
-
// Hook zarr chunk updates to trigger GL refresh
|
|
3165
|
+
// Hook zarr chunk updates to trigger GL refresh.
|
|
3166
|
+
// Register the callback BEFORE loading initial chunks so the first
|
|
3167
|
+
// batch of data triggers a proper GPU upload and render.
|
|
3154
3168
|
if (volume.zarrHelper) {
|
|
3169
|
+
// Auto-center zarr overlay on background volume if no explicit position was given
|
|
3170
|
+
if (!volume._hasExplicitZarrCenter && this.back && this.back !== volume && this.back.hdr?.dims) {
|
|
3171
|
+
const bg = this.back
|
|
3172
|
+
const dims = bg.hdr.dims
|
|
3173
|
+
const affine = bg.hdr.affine
|
|
3174
|
+
const ci = (dims[1] - 1) / 2
|
|
3175
|
+
const cj = (dims[2] - 1) / 2
|
|
3176
|
+
const ck = (dims[3] - 1) / 2
|
|
3177
|
+
const bgCenter: [number, number, number] = [
|
|
3178
|
+
affine[0][0] * ci + affine[0][1] * cj + affine[0][2] * ck + affine[0][3],
|
|
3179
|
+
affine[1][0] * ci + affine[1][1] * cj + affine[1][2] * ck + affine[1][3],
|
|
3180
|
+
affine[2][0] * ci + affine[2][1] * cj + affine[2][2] * ck + affine[2][3]
|
|
3181
|
+
]
|
|
3182
|
+
volume.zarrHelper.setWorldCenter(bgCenter)
|
|
3183
|
+
}
|
|
3184
|
+
|
|
3155
3185
|
volume.zarrHelper.onChunksUpdated = (): void => {
|
|
3156
3186
|
this.updateGLVolume()
|
|
3187
|
+
this._skipDragInDraw = true
|
|
3157
3188
|
this.drawScene()
|
|
3189
|
+
this._skipDragInDraw = false
|
|
3158
3190
|
}
|
|
3191
|
+
// Load initial chunks now (with callback registered).
|
|
3192
|
+
// Not awaited — progressive rendering via onChunksUpdated handles GPU updates.
|
|
3193
|
+
volume.zarrHelper.loadInitialChunks().catch((err) => log.error('Failed to load initial zarr chunks', err))
|
|
3159
3194
|
}
|
|
3160
3195
|
log.debug('loaded volume', volume.name)
|
|
3161
3196
|
log.debug(volume)
|
|
@@ -8012,7 +8047,9 @@ export class Niivue {
|
|
|
8012
8047
|
if (panPromises.length > 0) {
|
|
8013
8048
|
Promise.all(panPromises)
|
|
8014
8049
|
.then(() => {
|
|
8050
|
+
this._skipDragInDraw = true
|
|
8015
8051
|
this.drawScene()
|
|
8052
|
+
this._skipDragInDraw = false
|
|
8016
8053
|
})
|
|
8017
8054
|
.catch((err) => {
|
|
8018
8055
|
log.error('Failed to pan Zarr:', err)
|
|
@@ -11800,7 +11837,7 @@ export class Niivue {
|
|
|
11800
11837
|
this.dragForSlicer3D([this.uiData.dragStart[0], this.uiData.dragStart[1], this.uiData.dragEnd[0], this.uiData.dragEnd[1]])
|
|
11801
11838
|
return
|
|
11802
11839
|
}
|
|
11803
|
-
if (this.getCurrentDragMode() === DRAG_MODE.pan) {
|
|
11840
|
+
if (this.getCurrentDragMode() === DRAG_MODE.pan && !this._skipDragInDraw) {
|
|
11804
11841
|
this.dragForPanZoom([this.uiData.dragStart[0], this.uiData.dragStart[1], this.uiData.dragEnd[0], this.uiData.dragEnd[1]])
|
|
11805
11842
|
return
|
|
11806
11843
|
}
|
package/src/nvimage/index.ts
CHANGED
|
@@ -22,7 +22,7 @@ import { NVZarrHelper } from '@/nvimage/zarr/NVZarrHelper'
|
|
|
22
22
|
import { AffineTransform, copyAffine, createTransformMatrix, multiplyAffine } from '@/nvimage/affineUtils'
|
|
23
23
|
|
|
24
24
|
export * from '@/nvimage/utils'
|
|
25
|
-
export type TypedVoxelArray = Float32Array | Uint8Array | Int16Array | Float64Array | Uint16Array
|
|
25
|
+
export type TypedVoxelArray = Float32Array | Uint8Array | Int16Array | Float64Array | Uint16Array | Int32Array | Uint32Array
|
|
26
26
|
|
|
27
27
|
/**
|
|
28
28
|
* a NVImage encapsulates some image data and provides methods to query and operate on images
|
|
@@ -89,6 +89,7 @@ export class NVImage {
|
|
|
89
89
|
onOpacityChange: (img: NVImage) => void = () => {}
|
|
90
90
|
|
|
91
91
|
zarrHelper: NVZarrHelper | null = null
|
|
92
|
+
_hasExplicitZarrCenter = false
|
|
92
93
|
|
|
93
94
|
mm000?: vec3
|
|
94
95
|
mm100?: vec3
|
|
@@ -865,7 +866,8 @@ export class NVImage {
|
|
|
865
866
|
zarrLevel,
|
|
866
867
|
zarrMaxVolumeSize,
|
|
867
868
|
zarrChannel,
|
|
868
|
-
zarrConvertUnits
|
|
869
|
+
zarrConvertUnits,
|
|
870
|
+
zarrCenterMM
|
|
869
871
|
}: Partial<Omit<ImageFromUrlOptions, 'url'>> & { url?: string | Uint8Array | ArrayBuffer } = {}): Promise<NVImage> {
|
|
870
872
|
if (url === '') {
|
|
871
873
|
throw Error('url must not be empty')
|
|
@@ -924,7 +926,8 @@ export class NVImage {
|
|
|
924
926
|
channel: zarrChannel,
|
|
925
927
|
convertUnitsToMm: zarrConvertUnits,
|
|
926
928
|
colormap,
|
|
927
|
-
opacity
|
|
929
|
+
opacity,
|
|
930
|
+
zarrCenterMM
|
|
928
931
|
})
|
|
929
932
|
}
|
|
930
933
|
// Light path: load entire array (unchanged)
|
|
@@ -1014,6 +1017,7 @@ export class NVImage {
|
|
|
1014
1017
|
convertUnitsToMm?: boolean
|
|
1015
1018
|
colormap?: string
|
|
1016
1019
|
opacity?: number
|
|
1020
|
+
zarrCenterMM?: [number, number, number]
|
|
1017
1021
|
}
|
|
1018
1022
|
): Promise<NVImage> {
|
|
1019
1023
|
const nvimage = new NVImage()
|
|
@@ -1026,6 +1030,10 @@ export class NVImage {
|
|
|
1026
1030
|
cacheSize: options.cacheSize,
|
|
1027
1031
|
convertUnitsToMm: options.convertUnitsToMm
|
|
1028
1032
|
})
|
|
1033
|
+
if (options.zarrCenterMM) {
|
|
1034
|
+
nvimage.zarrHelper.setWorldCenter(options.zarrCenterMM)
|
|
1035
|
+
nvimage._hasExplicitZarrCenter = true
|
|
1036
|
+
}
|
|
1029
1037
|
if (options.colormap) {
|
|
1030
1038
|
nvimage._colormap = options.colormap
|
|
1031
1039
|
}
|