@niivue/niivue 0.44.1 → 0.44.2-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/README.md +9 -0
- package/build/index.d.ts +31 -6
- package/build/index.js +531 -135
- package/build/index.js.map +1 -1
- package/build/index.min.js +1 -1
- package/dist/index.d.ts +106 -18
- package/dist/index.js +26377 -30692
- package/dist/index.js.map +1 -1
- package/dist/index.min.js +1 -1
- package/package.json +18 -17
- package/src/index.html +46 -7
- package/src/index_segment.html +233 -0
- package/src/index_sync.html +333 -0
- package/src/niivue/index.ts +1054 -147
- package/src/niivue.css +0 -1
- package/src/nvdocument.ts +40 -5
- package/src/nvimage/index.ts +4 -1
- package/src/nvimage/utils.ts +61 -42
- package/src/nvlabel.ts +34 -1
- package/src/nvmesh-loaders.ts +24 -1
- package/src/shader-srcs.ts +84 -3
- package/src/types.ts +13 -0
- package/dist/niivue.umd.js +0 -1792
|
@@ -0,0 +1,333 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="utf-8" />
|
|
5
|
+
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
|
|
6
|
+
<meta name="viewport" content="width=device-width,initial-scale=1.0" />
|
|
7
|
+
<title>Bidirectional Sync</title>
|
|
8
|
+
<style>
|
|
9
|
+
html {
|
|
10
|
+
height: auto;
|
|
11
|
+
min-height: 100%;
|
|
12
|
+
margin: 0;
|
|
13
|
+
}
|
|
14
|
+
body {
|
|
15
|
+
display: flex;
|
|
16
|
+
flex-direction: column;
|
|
17
|
+
margin: 0;
|
|
18
|
+
min-height: 100%;
|
|
19
|
+
width: 100%;
|
|
20
|
+
position: absolute;
|
|
21
|
+
font-family: system-ui, Arial, Helvetica, sans-serif;
|
|
22
|
+
user-select: none; /* Standard syntax */
|
|
23
|
+
color: white;
|
|
24
|
+
background: #202020;
|
|
25
|
+
}
|
|
26
|
+
header {
|
|
27
|
+
margin: 10px;
|
|
28
|
+
}
|
|
29
|
+
footer {
|
|
30
|
+
margin: 5px;
|
|
31
|
+
}
|
|
32
|
+
.page-wrapper {
|
|
33
|
+
margin: 2px;
|
|
34
|
+
}
|
|
35
|
+
.row {
|
|
36
|
+
display: flex;
|
|
37
|
+
flex-direction: row;
|
|
38
|
+
flex-wrap: wrap;
|
|
39
|
+
width: 100%;
|
|
40
|
+
min-height: 100%;
|
|
41
|
+
margin: 2px;
|
|
42
|
+
}
|
|
43
|
+
.column {
|
|
44
|
+
display: flex;
|
|
45
|
+
flex-direction: column;
|
|
46
|
+
flex-basis: 100%;
|
|
47
|
+
flex: 1;
|
|
48
|
+
background: #404040;
|
|
49
|
+
}
|
|
50
|
+
canvas:focus {
|
|
51
|
+
outline: red 1px solid;
|
|
52
|
+
}
|
|
53
|
+
</style>
|
|
54
|
+
</head>
|
|
55
|
+
<body>
|
|
56
|
+
<noscript>niivue requires JavaScript.</noscript>
|
|
57
|
+
<header>
|
|
58
|
+
<label for="layout">Layout</label>
|
|
59
|
+
<select id="layout">
|
|
60
|
+
<option value="0" selected>Auto</option>
|
|
61
|
+
<option value="1">Column</option>
|
|
62
|
+
<option value="2">Grid</option>
|
|
63
|
+
<option value="3">Row</option>
|
|
64
|
+
</select>
|
|
65
|
+
<label for="canvasHeight">Height</label>
|
|
66
|
+
<select id="canvasHeight">
|
|
67
|
+
<option value="256">256</option>
|
|
68
|
+
<option value="640" selected>512</option>
|
|
69
|
+
<option value="768">768</option>
|
|
70
|
+
</select>
|
|
71
|
+
<label for="syncDrop"> Broadcast</label>
|
|
72
|
+
<select id="syncDrop">
|
|
73
|
+
<option value="disabled">Sync Disabled</option>
|
|
74
|
+
<option value="2d">Sync 2D</option>
|
|
75
|
+
<option value="3d">Sync 3D</option>
|
|
76
|
+
<option value="crosshair" selected>crosshair</option>
|
|
77
|
+
<option value="zoomPan">zoomPan</option>
|
|
78
|
+
<option value="gamma">gamma</option>
|
|
79
|
+
<option value="sliceType">sliceType</option>
|
|
80
|
+
<option value="clipPlane">clipPlane</option>
|
|
81
|
+
<option value="cal_min">cal_min</option>
|
|
82
|
+
<option value="cal_max">cal_max</option>
|
|
83
|
+
<!-- everything -->
|
|
84
|
+
<option value="all">all except intensity</option>
|
|
85
|
+
</select>
|
|
86
|
+
<!-- slider for gamma -->
|
|
87
|
+
<label for="gammaSlider">Gamma</label>
|
|
88
|
+
<input
|
|
89
|
+
type="range"
|
|
90
|
+
min="10"
|
|
91
|
+
max="400"
|
|
92
|
+
value="100"
|
|
93
|
+
class="slider"
|
|
94
|
+
id="gammaSlider"
|
|
95
|
+
/>
|
|
96
|
+
<!-- sliceType select -->
|
|
97
|
+
<label for="sliceType">Slice Type</label>
|
|
98
|
+
<select id="sliceType">
|
|
99
|
+
<option value="0">Axial</option>
|
|
100
|
+
<option value="1">Coronal</option>
|
|
101
|
+
<option value="2">Sagittal</option>
|
|
102
|
+
<option value="3" selected>Multi</option>
|
|
103
|
+
</select>
|
|
104
|
+
|
|
105
|
+
<!-- slider for cal_min -->
|
|
106
|
+
<label for="calMin">min</label>
|
|
107
|
+
<input
|
|
108
|
+
type="range"
|
|
109
|
+
min="0"
|
|
110
|
+
max="100"
|
|
111
|
+
value="0"
|
|
112
|
+
class="slider"
|
|
113
|
+
id="calMin"
|
|
114
|
+
/>
|
|
115
|
+
<!-- slider for cal_max -->
|
|
116
|
+
<label for="calMax">max</label>
|
|
117
|
+
<input
|
|
118
|
+
type="range"
|
|
119
|
+
min="0"
|
|
120
|
+
max="100"
|
|
121
|
+
value="100"
|
|
122
|
+
class="slider"
|
|
123
|
+
id="calMax"
|
|
124
|
+
/>
|
|
125
|
+
</header>
|
|
126
|
+
<main>
|
|
127
|
+
<div class='page-wrapper'>
|
|
128
|
+
<div class='row'>
|
|
129
|
+
<div class='column'>
|
|
130
|
+
<div>
|
|
131
|
+
<canvas id="gl1" height="512">
|
|
132
|
+
</div>
|
|
133
|
+
</div>
|
|
134
|
+
<div class='column'>
|
|
135
|
+
<div>
|
|
136
|
+
<canvas id="gl2" height="512">
|
|
137
|
+
</div>
|
|
138
|
+
</div>
|
|
139
|
+
<div class='column'>
|
|
140
|
+
<div>
|
|
141
|
+
<canvas id="gl3" height="512">
|
|
142
|
+
</div>
|
|
143
|
+
</div>
|
|
144
|
+
</div>
|
|
145
|
+
</div>
|
|
146
|
+
</main>
|
|
147
|
+
<footer>
|
|
148
|
+
<x id="intensity"> </x>
|
|
149
|
+
<x id="intensity2"> </x>
|
|
150
|
+
<x id="intensity3"> </x>
|
|
151
|
+
</footer>
|
|
152
|
+
<script type="module" async>
|
|
153
|
+
import {
|
|
154
|
+
Niivue,
|
|
155
|
+
NVImage,
|
|
156
|
+
NVMesh,
|
|
157
|
+
NVMeshLoaders,
|
|
158
|
+
SHOW_RENDER
|
|
159
|
+
} from "./niivue/index.ts";
|
|
160
|
+
layout.onchange = function () {
|
|
161
|
+
nv1.setMultiplanarLayout(parseInt(this.value));
|
|
162
|
+
nv2.setMultiplanarLayout(parseInt(this.value));
|
|
163
|
+
nv3.setMultiplanarLayout(parseInt(this.value));
|
|
164
|
+
}
|
|
165
|
+
canvasHeight.onchange = function () {
|
|
166
|
+
gl1.height = this.value;
|
|
167
|
+
gl2.height = this.value;
|
|
168
|
+
gl3.height = this.value;
|
|
169
|
+
nv1.resizeListener();
|
|
170
|
+
nv2.resizeListener();
|
|
171
|
+
nv3.resizeListener();
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
gammaSlider.onchange = function() {
|
|
175
|
+
const v = parseFloat(gammaSlider.value) * 0.01
|
|
176
|
+
nv1.setGamma(v)
|
|
177
|
+
nv1.gl.canvas.focus()
|
|
178
|
+
nv1.sync()
|
|
179
|
+
nv1.gl.canvas.blur()
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
syncDrop.onchange = function(e) {
|
|
183
|
+
const v = e.target.value
|
|
184
|
+
// let is2D = false
|
|
185
|
+
// let is3D = false
|
|
186
|
+
// if (v % 2) {
|
|
187
|
+
// is2D = true
|
|
188
|
+
// }
|
|
189
|
+
// if (v > 1) {
|
|
190
|
+
// is3D = true
|
|
191
|
+
// }
|
|
192
|
+
|
|
193
|
+
// check if v === "all", if so, add everything
|
|
194
|
+
if (v === "all") {
|
|
195
|
+
nv1.broadcastTo([nv2, nv3], {
|
|
196
|
+
['3d']: true,
|
|
197
|
+
crosshair: true,
|
|
198
|
+
zoomPan: true,
|
|
199
|
+
gamma: true,
|
|
200
|
+
sliceType: true,
|
|
201
|
+
clipPlane: true,
|
|
202
|
+
// cal_min: true,
|
|
203
|
+
// cal_max: true,
|
|
204
|
+
})
|
|
205
|
+
nv2.broadcastTo([nv1, nv3], {
|
|
206
|
+
['3d']: true,
|
|
207
|
+
crosshair: true,
|
|
208
|
+
zoomPan: true,
|
|
209
|
+
gamma: true,
|
|
210
|
+
sliceType: true,
|
|
211
|
+
clipPlane: true,
|
|
212
|
+
// cal_min: true,
|
|
213
|
+
// cal_max: true,
|
|
214
|
+
})
|
|
215
|
+
nv3.broadcastTo([nv1, nv2], {
|
|
216
|
+
['3d']: true,
|
|
217
|
+
crosshair: true,
|
|
218
|
+
zoomPan: true,
|
|
219
|
+
gamma: true,
|
|
220
|
+
sliceType: true,
|
|
221
|
+
clipPlane: true,
|
|
222
|
+
// cal_min: true,
|
|
223
|
+
// cal_max: true,
|
|
224
|
+
})
|
|
225
|
+
return
|
|
226
|
+
}
|
|
227
|
+
nv1.broadcastTo([nv2, nv3], { [v]: true })
|
|
228
|
+
nv2.broadcastTo([nv1, nv3], { [v]: true })
|
|
229
|
+
nv3.broadcastTo([nv1, nv2], { [v]: true })
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
calMin.onchange = function() {
|
|
233
|
+
const v = parseFloat(calMin.value)
|
|
234
|
+
nv2.volumes[0].cal_min = v
|
|
235
|
+
// set focus to the canvas
|
|
236
|
+
nv2.gl.canvas.focus()
|
|
237
|
+
nv2.updateGLVolume()
|
|
238
|
+
// remove focus from the canvas
|
|
239
|
+
nv2.gl.canvas.blur()
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
calMax.onchange = function() {
|
|
243
|
+
const v = parseFloat(calMax.value)
|
|
244
|
+
nv2.volumes[0].cal_max = v
|
|
245
|
+
// set focus to the canvas
|
|
246
|
+
nv2.gl.canvas.focus()
|
|
247
|
+
nv2.updateGLVolume()
|
|
248
|
+
// remove focus from the canvas
|
|
249
|
+
nv2.gl.canvas.blur()
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
sliceType.onchange = function() {
|
|
253
|
+
const v = parseInt(sliceType.value)
|
|
254
|
+
nv1.setSliceType(v)
|
|
255
|
+
nv2.setSliceType(v)
|
|
256
|
+
nv3.setSliceType(v)
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
function handleIntensityChange(data) {
|
|
260
|
+
document.getElementById("intensity").innerHTML =
|
|
261
|
+
" " + data.string;
|
|
262
|
+
}
|
|
263
|
+
function handleIntensityChange2(data) {
|
|
264
|
+
document.getElementById("intensity2").innerHTML =
|
|
265
|
+
" " + data.string;
|
|
266
|
+
}
|
|
267
|
+
function handleIntensityChange3(data) {
|
|
268
|
+
document.getElementById("intensity3").innerHTML =
|
|
269
|
+
" " + data.string;
|
|
270
|
+
}
|
|
271
|
+
var nv1 = new Niivue({
|
|
272
|
+
show3Dcrosshair: true,
|
|
273
|
+
onLocationChange: handleIntensityChange,
|
|
274
|
+
backColor: [1, 1, 1, 1],
|
|
275
|
+
dragMode: 3,
|
|
276
|
+
sliceType: 3,
|
|
277
|
+
multiplanarShowRender: SHOW_RENDER.ALWAYS,
|
|
278
|
+
isRadiologicalConvention: true
|
|
279
|
+
});
|
|
280
|
+
nv1.attachTo("gl1");
|
|
281
|
+
var volumeList1 = [{ url: "../demos/images/pcasl.nii.gz" }];
|
|
282
|
+
await nv1.loadVolumes(volumeList1);
|
|
283
|
+
var nv2 = new Niivue({
|
|
284
|
+
show3Dcrosshair: true,
|
|
285
|
+
onLocationChange: handleIntensityChange2,
|
|
286
|
+
backColor: [1, 1, 1, 1],
|
|
287
|
+
dragMode: 3,
|
|
288
|
+
sliceType: 3,
|
|
289
|
+
multiplanarShowRender: SHOW_RENDER.ALWAYS
|
|
290
|
+
});
|
|
291
|
+
nv2.attachTo("gl2");
|
|
292
|
+
var nv3 = new Niivue({
|
|
293
|
+
show3Dcrosshair: true,
|
|
294
|
+
onLocationChange: handleIntensityChange3,
|
|
295
|
+
backColor: [1, 1, 1, 1],
|
|
296
|
+
dragMode: 0,
|
|
297
|
+
sliceType: 3,
|
|
298
|
+
multiplanarShowRender: SHOW_RENDER.ALWAYS
|
|
299
|
+
});
|
|
300
|
+
nv3.attachTo("gl3");
|
|
301
|
+
var volumeList2 = [{ url: "../demos/images/mni152.nii.gz" }];
|
|
302
|
+
var volumeList3 = [{ url: "../demos/images/mni152.nii.gz" }];
|
|
303
|
+
await nv2.loadVolumes(volumeList2);
|
|
304
|
+
// once the volumes are loaded for nv2, set the min and max values for
|
|
305
|
+
// the calMin and calMax sliders to the min and max values of the volume
|
|
306
|
+
calMin.min = nv2.volumes[0].global_min
|
|
307
|
+
calMin.max = nv2.volumes[0].global_max
|
|
308
|
+
calMin.value = nv2.volumes[0].cal_min
|
|
309
|
+
calMax.min = nv2.volumes[0].global_min
|
|
310
|
+
calMax.max = nv2.volumes[0].global_max
|
|
311
|
+
calMax.value = nv2.volumes[0].cal_max
|
|
312
|
+
|
|
313
|
+
await nv3.loadVolumes(volumeList3);
|
|
314
|
+
const syncOpts = {
|
|
315
|
+
// "2d": true, // legacy option
|
|
316
|
+
// "3d": true, // legacy option
|
|
317
|
+
// new options allowing more fine-grained control below.
|
|
318
|
+
crosshair: true,
|
|
319
|
+
zoomPan: true,
|
|
320
|
+
gamma: true,
|
|
321
|
+
sliceType: true,
|
|
322
|
+
clipPlane: true,
|
|
323
|
+
cal_min: false,
|
|
324
|
+
cal_max: false,
|
|
325
|
+
}
|
|
326
|
+
// make sure all views can control each other
|
|
327
|
+
nv1.broadcastTo([nv2, nv3], syncOpts);
|
|
328
|
+
nv2.broadcastTo([nv1, nv3], syncOpts);
|
|
329
|
+
nv3.broadcastTo([nv1, nv2], syncOpts);
|
|
330
|
+
|
|
331
|
+
</script>
|
|
332
|
+
</body>
|
|
333
|
+
</html>
|