@niivue/niivue 0.44.2-dev.1 → 0.45.0

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/src/nvdocument.ts CHANGED
@@ -46,8 +46,7 @@ export enum DRAG_MODE {
46
46
  measurement = 2,
47
47
  pan = 3,
48
48
  slicer3D = 4,
49
- callbackOnly = 5,
50
- roiSelection = 6
49
+ callbackOnly = 5
51
50
  }
52
51
 
53
52
  // make mutable type
@@ -141,18 +140,10 @@ export type NVConfigOptions = {
141
140
  renderOverlayBlend: number
142
141
  sliceMosaicString: string
143
142
  centerMosaic: boolean
144
- penSize: number
145
143
  clickToSegment: boolean
146
144
  clickToSegmentRadius: number
145
+ clickToSegmentSteps: number
147
146
  clickToSegmentBright: boolean
148
- clickToSegmentAutoIntensity: boolean // new option, but keep clickToSegmentBright for backwards compatibility
149
- clickToSegmentIntensityMax: number // also covers NaN
150
- clickToSegmentIntensityMin: number // also covers NaN
151
- clickToSegmentPercent: number
152
- clickToSegmentMaxDistanceMM: number // max distance in mm to consider for click to segment flood fill
153
- // selection box outline thickness
154
- selectionBoxLineThickness: number
155
- selectionBoxIsOutline: boolean
156
147
  }
157
148
 
158
149
  export const DEFAULT_OPTIONS: NVConfigOptions = {
@@ -221,21 +212,10 @@ export const DEFAULT_OPTIONS: NVConfigOptions = {
221
212
  renderOverlayBlend: 1.0,
222
213
  sliceMosaicString: '',
223
214
  centerMosaic: false,
224
- penSize: 1, // in voxels, since all drawing is done using bitmap indices
225
215
  clickToSegment: false,
226
- clickToSegmentRadius: 3, // in mm
227
- clickToSegmentBright: true,
228
- clickToSegmentAutoIntensity: false, // new option, but keep clickToSegmentBright for backwards compatibility
229
- clickToSegmentIntensityMax: NaN, // NaN will use auto threshold (default flood fill behavior from before)
230
- clickToSegmentIntensityMin: NaN, // NaN will use auto threshold (default flood fill behavior from before)
231
- // 0 will use auto threshold (default flood fill behavior from before)
232
- // Take the voxel intensity at the click point and use this percentage +/- to threshold the flood fill operation.
233
- // If greater than 0, clickedVoxelIntensity +/- clickedVoxelIntensity * clickToSegmentPercent will be used
234
- // for the clickToSegmentIntensityMin and clickToSegmentIntensityMax values.
235
- clickToSegmentPercent: 0,
236
- clickToSegmentMaxDistanceMM: Number.POSITIVE_INFINITY, // default value is infinity for backwards compatibility with flood fill routine.
237
- selectionBoxLineThickness: 4,
238
- selectionBoxIsOutline: false
216
+ clickToSegmentRadius: 2,
217
+ clickToSegmentSteps: 10,
218
+ clickToSegmentBright: true
239
219
  }
240
220
 
241
221
  type SceneData = {
@@ -719,47 +719,6 @@ void main() {
719
719
  color = lineColor;
720
720
  }`
721
721
 
722
- export const fragRectOutlineShader = `#version 300 es
723
- #line 723
724
- precision highp int;
725
- precision highp float;
726
-
727
- uniform vec4 lineColor;
728
- uniform vec4 leftTopWidthHeight;
729
- uniform float thickness; // line thickness in pixels
730
- uniform vec2 canvasWidthHeight;
731
-
732
- out vec4 color;
733
-
734
- void main() {
735
- // fragment position in screen coordinates
736
- vec2 fragCoord = gl_FragCoord.xy;
737
-
738
- // canvas height
739
- float canvasHeight = canvasWidthHeight.y;
740
-
741
- // 'top' and 'bottom' to match gl_FragCoord.y coordinate system
742
- float top = canvasHeight - leftTopWidthHeight.y;
743
- float bottom = top - leftTopWidthHeight.w;
744
-
745
- // left and right edges
746
- float left = leftTopWidthHeight.x;
747
- float right = left + leftTopWidthHeight.z;
748
-
749
- bool withinLeft = fragCoord.x >= left && fragCoord.x <= left + thickness;
750
- bool withinRight = fragCoord.x <= right && fragCoord.x >= right - thickness;
751
- bool withinTop = fragCoord.y <= top && fragCoord.y >= top - thickness;
752
- bool withinBottom = fragCoord.y >= bottom && fragCoord.y <= bottom + thickness;
753
-
754
- bool isOutline = withinLeft || withinRight || withinTop || withinBottom;
755
-
756
- if (isOutline) {
757
- color = lineColor;
758
- } else {
759
- discard;
760
- }
761
- }`
762
-
763
722
  export const vertColorbarShader = `#version 300 es
764
723
  #line 490
765
724
  layout(location=0) in vec3 pos;
package/src/types.ts CHANGED
@@ -152,16 +152,3 @@ export type NiiVueLocation = {
152
152
  vox: vec3
153
153
  xy: [number, number]
154
154
  }
155
-
156
- export type SyncOpts = {
157
- '3d'?: boolean
158
- '2d'?: boolean
159
- zoomPan?: boolean
160
- cal_min?: boolean
161
- cal_max?: boolean
162
- gamma?: boolean
163
- useSliceOffset?: boolean
164
- sliceType?: boolean
165
- crosshair?: boolean
166
- clipPlane?: boolean
167
- }
@@ -1,165 +0,0 @@
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>NiiVue</title>
8
- <link rel="stylesheet" href="niivue.css" />
9
- <link rel="icon" href="data:;base64,iVBORw0KGgo=" />
10
- </head>
11
- <body>
12
- <noscript>
13
- <strong>niivue requires JavaScript.</strong>
14
- </noscript>
15
- <header>
16
- <button id="openBtn">Choose an image or mesh with a dialog</button>
17
- <label for="layoutSelect">Layout</label>
18
- <select id="layoutSelect">
19
- <option value="0" selected>Auto</option>
20
- <option value="1">Column</option>
21
- <option value="2">Grid</option>
22
- <option value="3">Row</option>
23
- </select>
24
- <label for="renderingSelect">MultiplanarRendering</label>
25
- <select id="renderingSelect">
26
- <option value="0">Never</option>
27
- <option value="1">Always</option>
28
- <option value="2" selected>Auto</option>
29
- </select>
30
- <select id="sliceType">
31
- <option value="0">Axial</option>
32
- <option value="1">Coronal</option>
33
- <option value="2">Sagittal</option>
34
- <option value="4">Render</option>
35
- <option value="3" selected>Multiplanar</option>
36
- </select>
37
- <label for="equalCheck">EqualSize</label>
38
- <input type="checkbox" id="equalCheck" checked />
39
- <label for="cornerCheck">CornerText</label>
40
- <input type="checkbox" id="cornerCheck" unchecked />
41
- <!-- undo button for drawing -->
42
- <button id="undoBtn">Undo</button>
43
- <!-- draw toggle button -->
44
- <button id="drawBtn">Draw</button>
45
- <!-- grow button -->
46
- <button id="growBtn">Grow</button>
47
- <!-- pen color button cycler -->
48
- <button id="penColorBtn">Pen color</button>
49
- </header>
50
- <main id="canvas-container">
51
- <canvas id="gl1"></canvas>
52
- </main>
53
-
54
- <script type="module" async>
55
- import {
56
- Niivue,
57
- NVImage,
58
- NVMesh,
59
- NVMeshLoaders,
60
- SHOW_RENDER,
61
- DRAG_MODE
62
- } from "./niivue/index.ts";
63
- async function addVolumeFromFiles(f) {
64
- console.log("attempting to open ", f[0].name);
65
- console.log("details", f[0]);
66
- nv1.loadFromFile(f[0]);
67
- }
68
- openBtn.onclick = function () {
69
- let input = document.createElement("input");
70
- input.style.display = "none";
71
- input.type = "file";
72
- document.body.appendChild(input);
73
- input.onchange = function (event) {
74
- addVolumeFromFiles(event.target.files);
75
- };
76
- input.click();
77
- };
78
-
79
- // pen colors can be 1, 2, 3, 4
80
- let penColor = 1;
81
-
82
- layoutSelect.onchange = function () {
83
- nv1.setMultiplanarLayout(Number(this.value));
84
- }
85
- renderingSelect.onchange = function () {
86
- nv1.opts.multiplanarShowRender = Number(this.value);
87
- nv1.drawScene();
88
- }
89
- equalCheck.onchange = function () {
90
- nv1.opts.multiplanarEqualSize = this.checked
91
- nv1.drawScene()
92
- }
93
- cornerCheck.onchange = function () {
94
- //nv1.opts.isCornerOrientationText = this.checked\
95
- nv1.setCornerOrientationText(this.checked)
96
- }
97
- sliceType.onchange = function () {
98
- let st = parseInt(document.getElementById("sliceType").value);
99
- nv1.setSliceType(st)
100
- }
101
- undoBtn.onclick = function () {
102
- nv1.drawUndo()
103
- }
104
- drawBtn.onclick = function () {
105
- nv1.setDrawingEnabled(!nv1.opts.drawingEnabled)
106
- if (nv1.opts.drawingEnabled) {
107
- nv1.opts.clickToSegment = true;
108
- nv1.setPenValue(penColor, false) // red and filled
109
- } else {
110
- nv1.opts.clickToSegment = false;
111
- }
112
- }
113
- growBtn.onclick = function () {
114
- nv1.drawingBinaryDilationWithSeed(
115
- nv1.frac2vox(nv1.scene.crosshairPos)
116
- )
117
- }
118
- penColorBtn.onclick = function () {
119
- penColor = penColor % 4 + 1;
120
- nv1.setPenValue(penColor, false)
121
- }
122
-
123
- const onDragRelease = (data) => {
124
- console.log("drag release", data);
125
- let obj = nv1.getDescriptives({
126
- layer: 0,
127
- startVox: data.voxStart,
128
- endVox: data.voxEnd,
129
- roiIsMask: true,
130
- });
131
- console.log(obj);
132
- };
133
-
134
- var volumeList1 = [{ url: "../demos/images/FLAIR.nii.gz" }];
135
- let defaults = {
136
- backColor: [0, 0, 0, 1],
137
- show3Dcrosshair: true,
138
- loglevel: 'debug',
139
- isRuler: true,
140
- clickToSegmentRadius: 3, // in mm
141
- clickToSegmentPercent: 0.2,
142
- clickToSegmentAutoIntensity: true,
143
- clickToSegmentMaxDistanceMM: 10,
144
- penSize: 3, // in voxels, but overridden by clickToSegmentRadius
145
- maxDrawUndoBitmaps: 30,
146
- onDragRelease: onDragRelease,
147
- selectionBoxColor: [1, 0, 0, 0.5],
148
- dragMode: DRAG_MODE.roiSelection,
149
- selectionBoxIsOutline: true
150
- };
151
- var nv1 = new Niivue(defaults);
152
- nv1.attachToCanvas(gl1);
153
- // multiplanarShowRender is the preferred option now,
154
- // but multiplanarForceRender is still available for backwards compatibility
155
- // previous use was: nv1.opts.multiplanarForceRender = true;
156
- nv1.opts.multiplanarShowRender = SHOW_RENDER.AUTO
157
- await nv1.loadVolumes(volumeList1);
158
- equalCheck.onchange()
159
-
160
- // set drawing enabled
161
- // nv1.setDrawingEnabled(true);
162
- // nv1.setPenValue(1, true) // red and filled
163
- </script>
164
- </body>
165
- </html>
@@ -1,289 +0,0 @@
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="0">Sync Disabled</option>
74
- <option value="1">Sync 2D</option>
75
- <option value="2">Sync 3D</option>
76
- <option value="3" selected>Sync 2D and 3D</option>
77
- </select>
78
- <!-- slider for gamma -->
79
- <label for="gammaSlider">Gamma</label>
80
- <input
81
- type="range"
82
- min="10"
83
- max="400"
84
- value="100"
85
- class="slider"
86
- id="gammaSlider"
87
- />
88
- <!-- sliceType select -->
89
- <label for="sliceType">Slice Type</label>
90
- <select id="sliceType">
91
- <option value="0">Axial</option>
92
- <option value="1">Coronal</option>
93
- <option value="2">Sagittal</option>
94
- <option value="3" selected>Multi</option>
95
- </select>
96
-
97
- <!-- slider for cal_min -->
98
- <label for="calMin">min</label>
99
- <input
100
- type="range"
101
- min="0"
102
- max="100"
103
- value="0"
104
- class="slider"
105
- id="calMin"
106
- />
107
- <!-- slider for cal_max -->
108
- <label for="calMax">max</label>
109
- <input
110
- type="range"
111
- min="0"
112
- max="100"
113
- value="100"
114
- class="slider"
115
- id="calMax"
116
- />
117
- </header>
118
- <main>
119
- <div class='page-wrapper'>
120
- <div class='row'>
121
- <div class='column'>
122
- <div>
123
- <canvas id="gl1" height="512">
124
- </div>
125
- </div>
126
- <div class='column'>
127
- <div>
128
- <canvas id="gl2" height="512">
129
- </div>
130
- </div>
131
- <div class='column'>
132
- <div>
133
- <canvas id="gl3" height="512">
134
- </div>
135
- </div>
136
- </div>
137
- </div>
138
- </main>
139
- <footer>
140
- <x id="intensity"> &nbsp;</x>
141
- <x id="intensity2"> &nbsp;</x>
142
- <x id="intensity3"> &nbsp;</x>
143
- </footer>
144
- <script type="module" async>
145
- import {
146
- Niivue,
147
- NVImage,
148
- NVMesh,
149
- NVMeshLoaders,
150
- SHOW_RENDER
151
- } from "./niivue/index.ts";
152
- layout.onchange = function () {
153
- nv1.setMultiplanarLayout(parseInt(this.value));
154
- nv2.setMultiplanarLayout(parseInt(this.value));
155
- nv3.setMultiplanarLayout(parseInt(this.value));
156
- }
157
- canvasHeight.onchange = function () {
158
- gl1.height = this.value;
159
- gl2.height = this.value;
160
- gl3.height = this.value;
161
- nv1.resizeListener();
162
- nv2.resizeListener();
163
- nv3.resizeListener();
164
- }
165
-
166
- gammaSlider.onchange = function() {
167
- const v = parseFloat(gammaSlider.value) * 0.01
168
- nv1.setGamma(v)
169
- nv1.gl.canvas.focus()
170
- nv1.sync()
171
- nv1.gl.canvas.blur()
172
- }
173
-
174
- syncDrop.onchange = function() {
175
- const v = parseInt(syncDrop.value)
176
- let is2D = false
177
- let is3D = false
178
- if (v % 2) {
179
- is2D = true
180
- }
181
- if (v > 1) {
182
- is3D = true
183
- }
184
- nv1.broadcastTo([nv2, nv3], { "2d": is2D, "3d": is3D })
185
- nv2.broadcastTo([nv1, nv3], { "2d": is2D, "3d": is3D })
186
- nv3.broadcastTo([nv1, nv2], { "2d": is2D, "3d": is3D })
187
- }
188
-
189
- calMin.onchange = function() {
190
- const v = parseFloat(calMin.value)
191
- nv2.volumes[0].cal_min = v
192
- // set focus to the canvas
193
- nv2.gl.canvas.focus()
194
- nv2.updateGLVolume()
195
- // remove focus from the canvas
196
- nv2.gl.canvas.blur()
197
- }
198
-
199
- calMax.onchange = function() {
200
- const v = parseFloat(calMax.value)
201
- nv2.volumes[0].cal_max = v
202
- // set focus to the canvas
203
- nv2.gl.canvas.focus()
204
- nv2.updateGLVolume()
205
- // remove focus from the canvas
206
- nv2.gl.canvas.blur()
207
- }
208
-
209
- sliceType.onchange = function() {
210
- const v = parseInt(sliceType.value)
211
- nv1.setSliceType(v)
212
- nv2.setSliceType(v)
213
- nv3.setSliceType(v)
214
- }
215
-
216
- function handleIntensityChange(data) {
217
- document.getElementById("intensity").innerHTML =
218
- "&nbsp;&nbsp;" + data.string;
219
- }
220
- function handleIntensityChange2(data) {
221
- document.getElementById("intensity2").innerHTML =
222
- "&nbsp;&nbsp;" + data.string;
223
- }
224
- function handleIntensityChange3(data) {
225
- document.getElementById("intensity3").innerHTML =
226
- "&nbsp;&nbsp;" + data.string;
227
- }
228
- var nv1 = new Niivue({
229
- show3Dcrosshair: true,
230
- onLocationChange: handleIntensityChange,
231
- backColor: [1, 1, 1, 1],
232
- // dragMode: 3,
233
- sliceType: 3,
234
- multiplanarShowRender: SHOW_RENDER.ALWAYS
235
- });
236
- nv1.attachTo("gl1");
237
- var volumeList1 = [{ url: "../demos/images/pcasl.nii.gz" }];
238
- await nv1.loadVolumes(volumeList1);
239
- var nv2 = new Niivue({
240
- show3Dcrosshair: true,
241
- onLocationChange: handleIntensityChange2,
242
- backColor: [1, 1, 1, 1],
243
- // dragMode: 3,
244
- sliceType: 3,
245
- multiplanarShowRender: SHOW_RENDER.ALWAYS
246
- });
247
- nv2.attachTo("gl2");
248
- var nv3 = new Niivue({
249
- show3Dcrosshair: true,
250
- onLocationChange: handleIntensityChange3,
251
- backColor: [1, 1, 1, 1],
252
- // dragMode: 0,
253
- sliceType: 3,
254
- multiplanarShowRender: SHOW_RENDER.ALWAYS
255
- });
256
- nv3.attachTo("gl3");
257
- var volumeList2 = [{ url: "../demos/images/mni152.nii.gz" }];
258
- var volumeList3 = [{ url: "../demos/images/mni152.nii.gz" }];
259
- await nv2.loadVolumes(volumeList2);
260
- // once the volumes are loaded for nv2, set the min and max values for
261
- // the calMin and calMax sliders to the min and max values of the volume
262
- calMin.min = nv2.volumes[0].global_min
263
- calMin.max = nv2.volumes[0].global_max
264
- calMin.value = nv2.volumes[0].cal_min
265
- calMax.min = nv2.volumes[0].global_min
266
- calMax.max = nv2.volumes[0].global_max
267
- calMax.value = nv2.volumes[0].cal_max
268
-
269
- await nv3.loadVolumes(volumeList3);
270
- const syncOpts = {
271
- // "2d": true, // legacy option
272
- // "3d": true, // legacy option
273
- // new options allowing more fine-grained control below.
274
- crosshair: true,
275
- zoomPan: true,
276
- gamma: true,
277
- sliceType: true,
278
- clipPlane: true,
279
- cal_min: false,
280
- cal_max: false,
281
- }
282
- // make sure all views can control each other
283
- nv1.broadcastTo([nv2, nv3], syncOpts);
284
- nv2.broadcastTo([nv1, nv3], syncOpts);
285
- nv3.broadcastTo([nv1, nv2], syncOpts);
286
-
287
- </script>
288
- </body>
289
- </html>