@niivue/niivue 0.22.2 → 0.23.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@niivue/niivue",
3
- "version": "0.22.2",
3
+ "version": "0.23.0",
4
4
  "description": "minimal webgl2 nifti image viewer",
5
5
  "main": "./src/niivue.js",
6
6
  "unpkg": "./dist/niivue.umd.js",
@@ -42,6 +42,9 @@
42
42
  },
43
43
  "homepage": "https://github.com/niivue/niivue#readme",
44
44
  "dependencies": {
45
+ "daikon": "^1.2.42",
46
+ "fflate": "^0.7.3",
47
+ "gifti-reader-js": "^0.4.4",
45
48
  "gl-matrix": "^3.4.3",
46
49
  "nifti-reader-js": "^0.5.4",
47
50
  "rxjs": "^7.4.0",
@@ -51,7 +54,7 @@
51
54
  "clean-jsdoc-theme": "^3.3.1",
52
55
  "eslint": "^8.4.1",
53
56
  "express": "^4.17.1",
54
- "http-server": "^14.0.0",
57
+ "http-server": "^14.1.0",
55
58
  "jest": "^27.4.4",
56
59
  "jest-image-snapshot": "^4.5.1",
57
60
  "jest-puppeteer": "^6.0.2",
@@ -0,0 +1,73 @@
1
+ import * as cmaps from "./cmaps";
2
+
3
+ export const colortables = function () {
4
+ this.version = 0.1;
5
+ };
6
+
7
+ colortables.prototype.colorMaps = function (sort = true) {
8
+ let cm = [];
9
+ for (const [key] of Object.entries(cmaps)) {
10
+ cm.push(key);
11
+ }
12
+ return sort === true ? cm.sort() : cm;
13
+ };
14
+
15
+ // not included in public docs
16
+ colortables.prototype.colormap = function (lutName = "") {
17
+ //function colormap(lutName = "") {
18
+ let defaultLutName = "gray";
19
+ let availMaps = this.colorMaps();
20
+ for (let i = 0; i < availMaps.length; i++) {
21
+ let key = availMaps[i];
22
+ if (lutName.toLowerCase() === key.toLowerCase()) {
23
+ return this.makeLut(
24
+ cmaps[key].R,
25
+ cmaps[key].G,
26
+ cmaps[key].B,
27
+ cmaps[key].A,
28
+ cmaps[key].I
29
+ );
30
+ }
31
+ }
32
+ // if no match the return the default gray lut
33
+ return this.makeLut(
34
+ cmaps[defaultLutName].R,
35
+ cmaps[defaultLutName].G,
36
+ cmaps[defaultLutName].B,
37
+ cmaps[defaultLutName].A,
38
+ cmaps[defaultLutName].I
39
+ );
40
+ }; // colormap()
41
+
42
+ colortables.prototype.colormapFromKey = function (name) {
43
+ let availMaps = luts.colorMaps(); //DoDo let availMaps = this.colorMaps();
44
+ for (let i = 0; i < availMaps.length; i++) {
45
+ let key = availMaps[i];
46
+ if (name.toLowerCase() === key.toLowerCase()) {
47
+ return cmaps[key];
48
+ }
49
+ }
50
+ };
51
+
52
+ // not included in public docs
53
+ colortables.prototype.makeLut = function (Rs, Gs, Bs, As, Is) {
54
+ //create color lookup table provided arrays of reds, greens, blues, alphas and intensity indices
55
+ //intensity indices should be in increasing order with the first value 0 and the last 255.
56
+ // this.makeLut([0, 255], [0, 0], [0,0], [0,128],[0,255]); //red gradient
57
+ var lut = new Uint8ClampedArray(256 * 4);
58
+ for (var i = 0; i < Is.length - 1; i++) {
59
+ //return a + f * (b - a);
60
+ var idxLo = Is[i];
61
+ var idxHi = Is[i + 1];
62
+ var idxRng = idxHi - idxLo;
63
+ var k = idxLo * 4;
64
+ for (var j = idxLo; j <= idxHi; j++) {
65
+ var f = (j - idxLo) / idxRng;
66
+ lut[k++] = Rs[i] + f * (Rs[i + 1] - Rs[i]); //Red
67
+ lut[k++] = Gs[i] + f * (Gs[i + 1] - Gs[i]); //Green
68
+ lut[k++] = Bs[i] + f * (Bs[i + 1] - Bs[i]); //Blue
69
+ lut[k++] = As[i] + f * (As[i + 1] - As[i]); //Alpha
70
+ }
71
+ }
72
+ return lut;
73
+ }; // makeLut()
package/src/index.html CHANGED
@@ -23,23 +23,11 @@
23
23
  <h1>
24
24
  hot reloadable development preview
25
25
  </h1>
26
- <p>
27
- <h3>Controls</h3>
28
- <ul>
29
- <li>move the crosshairs: left mouse click and drag (or click around)</li>
30
- <li>slice scrolling: mouse or touch pad scroll up and down (pinch zoom for mobile)</li>
31
- <li>change intensity range: right mouse click and drag a region (mobile not supported)</li>
32
- <li>reset intensity: left mouse click double click (mobile not supported)</li>
33
- <li><b>change the view mode by pressing the "v" key on your keyboard (mobile not supported yet)</b></li>
34
- <li><b>cycle through clip planes by pressing the "c" key on your keyboard (mobile not supported yet)</b></li>
35
- </ul>
36
- </p>
37
26
  </section>
38
-
39
27
  <!-- demo 1 -->
40
28
  <section>
41
- <div id="demo1" style="width:90%; height:400px;">
42
- <canvas id="gl1" height=480 width=640>
29
+ <div id="demo1" style="width:90%; height:640px;">
30
+ <canvas id="gl1" height=640 width=640>
43
31
  </canvas>
44
32
  </div>
45
33
  </section>
@@ -50,48 +38,74 @@
50
38
 
51
39
  <!-- <script type="module" src="./index.js">
52
40
  </script> -->
53
- <script type="module">
41
+ <script type="module" async>
54
42
  import { Niivue } from './niivue.js'
55
43
  import {NVImage} from './nvimage.js'
44
+ import {NVMesh} from './nvmesh.js'
56
45
  let query = window.location.search
57
46
  const params = new URLSearchParams(query);
58
47
  let url = params.get('url')
59
- console.log('url is: ',params.get('url'))
60
- // console.log(Niivue)
61
48
  var volumeList1 = [
62
49
  // first item is brackground image
63
50
  {
64
- url: url ? url : "./mni152.nii.gz",//"./AIL.nii.gz",//"./images/RAS.nii.gz", "./images/spm152.nii.gz",
51
+ //url: url ? url : "../demos/images/example4d+orig.HEAD",//"./AIL.nii.gz",//"./images/RAS.nii.gz", "./images/spm152.nii.gz",
52
+ url: url ? url : "../tests/images/mni152.nii.gz",//"./AIL.nii.gz",//"./images/RAS.nii.gz", "./images/spm152.nii.gz",
65
53
  //url: "./mni152.nii.gz",
66
- colorMap: "gray",
54
+ colorMap: "gray",
55
+ //urlImgData : "../demos/images/example4d+orig.BRIK.gz",
67
56
  opacity: 1,
68
57
  visible: true,
69
58
  },
70
- {
71
- url: "./hippo.nii.gz",
72
- colorMap: 'red'
73
- }
74
59
  ]
75
60
  var nv1 = new Niivue({
76
- logging: true,
61
+ logging: false,
77
62
  show3Dcrosshair: true,
78
63
  loadingText: 'there are no images',
79
- dragAndDropEnabled: true
64
+ dragAndDropEnabled: true,
65
+ backColor: [0.3, 0.2, 0.4, 1],
66
+ //thumbnail: "../demos/images/DoG.png"
80
67
  })
81
68
  nv1.setRadiologicalConvention(false)
82
69
  nv1.attachTo('gl1')
83
- nv1.loadVolumes(volumeList1)
84
- //nv1.setSliceType(nv1.sliceTypeMultiplanar)
70
+
85
71
  nv1.setSliceType(nv1.sliceTypeRender)
72
+ nv1.setClipPlane([-0.1, 270, 0])
73
+ nv1.setRenderAzimuthElevation(90,10)
74
+
75
+ await nv1.loadVolumes(volumeList1)
76
+
77
+ var meshLayersList1 = [
78
+ //{url: "../demos/images/PJM-stc-lh.stc", colorMap: "rocket", opacity: 0.7},
79
+ //{url: "../demos/images/boggle.lh.annot", colorMap: "rocket", opacity: 0.9},
80
+ ]
81
+
82
+ await nv1.loadMeshes([
83
+ {url: "../demos/images/dpsv.trx", rgba255 : [255, 255, 255, 255], layers : meshLayersList1},
84
+ //{url: "../demos/images/mni152_2009AO.mz3", rgba255 : [255, 255, 255, 255]},
85
+ //{url: "../demos/images/BrainMesh_ICBM152.lh.mz3", rgba255 : [255, 255, 255, 255], layers : meshLayersList1},
86
+ //{url: "../demos/images/PJM-example-lh.gii", rgba255 : [255, 255, 255, 255], layers : meshLayersList1},
87
+ ])
88
+
86
89
  // example of using the 'location' change event with a callback function
87
90
  nv1.on('location', (data) => {
88
91
  // data is an object with properties: {mm: [N N N], vox: [N N N], frac: [N N N]}
89
92
  document.getElementById('location').innerHTML = 'voxel location: ' + data.vox + ' ' + data.values
90
-
91
93
  })
92
94
 
93
- </script>
95
+ nv1.on('imageLoaded', (image) => {
96
+ console.log('imageLoaded', image.name)
97
+ })
98
+
99
+ nv1.on('intensityRange', (image) => {
100
+ console.log(image.cal_min, image.cal_max)
101
+ })
102
+
103
+ setTimeout(() => {
104
+ //nv1.saveImage('test.nii.gz')
105
+ }, 2000)
106
+
107
+ </script>
94
108
  </div>
95
109
  </body>
96
110
 
97
- </html>
111
+ </html>
@@ -0,0 +1,119 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+
4
+ <head>
5
+ <meta charset="utf-8">
6
+ <meta http-equiv="X-UA-Compatible" content="IE=edge">
7
+ <meta name="viewport" content="width=device-width,initial-scale=1.0">
8
+ <title>NiiVue</title>
9
+ <style>
10
+ section {
11
+ margin: 20px;
12
+ }
13
+ </style>
14
+ </head>
15
+
16
+ <body style="font-family: sans-serif;">
17
+ <div id="app">
18
+ <noscript>
19
+ <strong>niivue doesn't work properly without JavaScript enabled. Please enable it to continue.</strong>
20
+ </noscript>
21
+
22
+ <section>
23
+ <h1>
24
+ hot reloadable development preview
25
+ </h1>
26
+ </section>
27
+ <section>
28
+ <div>
29
+ <label for="drawPen">Draw color:</label>
30
+ <select name="drawPen" id="drawPen">
31
+ <option value="-1">Off</option>
32
+ <option value="0">Erase</option>
33
+ <option value="1">Red</option>
34
+ <option value="2">Green</option>
35
+ <option value="3">Blue</option>
36
+ </select>
37
+ <button id="random">random lines</button>
38
+ <button id="save">save drawing</button>
39
+ <div class="slidecontainer">
40
+ drawing opacity <input type="range" min="0" max="100" value="100" class="slider" id="drawOpacity">
41
+ </div>
42
+ </div>
43
+ </section>
44
+ <!-- demo 1 -->
45
+ <section>
46
+ <div id="demo1" style="width:90%; height:640px;">
47
+ <canvas id="gl1" height=640 width=640>
48
+ </canvas>
49
+ </div>
50
+ </section>
51
+
52
+ <section>
53
+ <p id="location"></p>
54
+ </section>
55
+
56
+ <!-- <script type="module" src="./index.js">
57
+ </script> -->
58
+ <script type="module" async>
59
+
60
+ document.getElementById("drawOpacity").addEventListener("change", doDrawOpacity);
61
+ function doDrawOpacity(){
62
+ nv1.setDrawOpacity(this.value * 0.01);
63
+ }
64
+ document.getElementById("drawPen").addEventListener("change", doDrawPen);
65
+ function doDrawPen(){
66
+ const mode = document.getElementById("drawPen").value;
67
+ nv1.setDrawingEnabled(mode >= 0);
68
+ if (mode >= 0)
69
+ nv1.opts.penValue = mode;
70
+ }
71
+ document.getElementById("random").addEventListener("click", doRandom);
72
+ function doRandom(){
73
+ nv1.createRandomDrawing();
74
+ }
75
+
76
+ document.getElementById("save").addEventListener("click", doSave);
77
+ function doSave(){
78
+ nv1.saveImage('test.nii', true);
79
+ }
80
+ import { Niivue } from './niivue.js'
81
+ import {NVImage} from './nvimage.js'
82
+ import {NVMesh} from './nvmesh.js'
83
+ let query = window.location.search
84
+ const params = new URLSearchParams(query);
85
+ let url = params.get('url')
86
+ var volumeList1 = [
87
+ // first item is brackground image
88
+ {
89
+ //url: url ? url : "../demos/images/example4d+orig.HEAD",//"./AIL.nii.gz",//"./images/RAS.nii.gz", "./images/spm152.nii.gz",
90
+ url: url ? url : "../demos/images/AIL.nii.gz",//"./AIL.nii.gz",//"./images/RAS.nii.gz", "./images/spm152.nii.gz",
91
+ //url: "./mni152.nii.gz",
92
+ colorMap: "gray",
93
+ //urlImgData : "../demos/images/example4d+orig.BRIK.gz",
94
+ opacity: 1,
95
+ visible: true,
96
+ },
97
+ ]
98
+ var nv1 = new Niivue({
99
+ logging: false,
100
+ show3Dcrosshair: true,
101
+ loadingText: 'there are no images',
102
+ dragAndDropEnabled: true,
103
+ backColor: [0.3, 0.2, 0.4, 1],
104
+ //thumbnail: "../demos/images/DoG.png"
105
+ })
106
+ nv1.setRadiologicalConvention(false)
107
+ nv1.attachTo('gl1')
108
+
109
+ await nv1.loadVolumes(volumeList1)
110
+ // example of using the 'location' change event with a callback function
111
+ nv1.on('location', (data) => {
112
+ // data is an object with properties: {mm: [N N N], vox: [N N N], frac: [N N N]}
113
+ document.getElementById('location').innerHTML = 'voxel location: ' + data.vox + ' ' + data.values
114
+ })
115
+ </script>
116
+ </div>
117
+ </body>
118
+
119
+ </html>
package/src/logger.js CHANGED
@@ -1,3 +1,8 @@
1
+ /**
2
+ * @class Log
3
+ * @type Log
4
+ * @param {number} logLevel
5
+ */
1
6
  export const Log = function (logLevel) {
2
7
  // log levels:
3
8
  // - 'debug'
@@ -0,0 +1,161 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+
4
+ <head>
5
+ <meta charset="utf-8">
6
+ <meta http-equiv="X-UA-Compatible" content="IE=edge">
7
+ <meta name="viewport" content="width=device-width,initial-scale=1.0">
8
+ <title>NiiVue</title>
9
+ <style>
10
+ section {
11
+ margin: 20px;
12
+ }
13
+ </style>
14
+ </head>
15
+
16
+ <body style="font-family: sans-serif;">
17
+ <div id="app">
18
+ <noscript>
19
+ <strong>niivue doesn't work properly without JavaScript enabled. Please enable it to continue.</strong>
20
+ </noscript>
21
+
22
+ <section>
23
+ <h1>
24
+ hot reloadable development preview
25
+ </h1>
26
+ </section>
27
+ <div class="slidecontainer">
28
+ intensity Max <input type="range" min="1" max="12" value="3" class="slider" id="fiberLengthSlider">
29
+ </div>
30
+ <div class="slidecontainer">
31
+ fiber dither <input type="range" min="0" max="10" value="1" class="slider" id="fiberDitherSlider">
32
+ </div>
33
+
34
+ <!-- demo 1 -->
35
+ <section>
36
+ <div id="demo1" style="width:90%; height:640px;">
37
+ <canvas id="gl1" height=640 width=640>
38
+ </canvas>
39
+ </div>
40
+ </section>
41
+
42
+ <section>
43
+ <p id="location"></p>
44
+ </section>
45
+
46
+ <!-- <script type="module" src="./index.js">
47
+ </script> -->
48
+ <script type="module" async>
49
+ var slider = document.getElementById("fiberLengthSlider");
50
+ var sliderD = document.getElementById("fiberDitherSlider");
51
+ // Update the current slider value (each time you drag the slider handle)
52
+ slider.oninput = function() {
53
+ nv1.setMeshLayerProperty(nv1.meshes[0].id, 0, 'cal_min', this.value)
54
+ nv1.setMeshLayerProperty(nv1.meshes[0].id, 0, 'cal_max', this.value + 2)
55
+ }
56
+ sliderD.oninput = function() {
57
+ nv1.setMeshProperty(nv1.meshes[0].id, 'fiberDither', this.value * 0.1)
58
+ }
59
+ import { Niivue } from './niivue.js'
60
+ import {NVImage} from './nvimage.js'
61
+ import {NVMesh} from './nvmesh.js'
62
+ let query = window.location.search
63
+ const params = new URLSearchParams(query);
64
+ let url = params.get('url')
65
+ console.log('url is: ',params.get('url'))
66
+ // console.log(Niivue)
67
+ let connectome = {
68
+ "name": "simpleConnectome",
69
+ "nodeColormap": "viridis",
70
+ "nodeColormapNegative": "viridis",
71
+ "nodeMinColor": 2,
72
+ "nodeMaxColor": 4,
73
+ "nodeScale": 3, //scale factor for node, e.g. if 2 and a node has size 3, a 6mm ball is drawn
74
+ "edgeColormap": "warm",
75
+ "edgeColormapNegative": "winter",
76
+ "edgeMin": 2,
77
+ "edgeMax": 4,
78
+ "edgeScale": 1,
79
+ "nodes": {
80
+ "names":["RF", "LF", "RP","LP"], //currently unused
81
+ "X":[40, -40, 40, -40], //Xmm for each node
82
+ "Y":[40, 40, -40, -40], //Ymm for each node
83
+ "Z":[30, 20, 50, 50], //Zmm for each node
84
+ "Color":[2, 2, 3, 4], //Used to interpolate color
85
+ "Size":[2, 2, 3, 4], //Size of node
86
+ },
87
+ "edges": [1, 2, -3, 4,
88
+ 0, 1, 0, 6,
89
+ 0, 0, 1, 0,
90
+ 0, 0, 0, 1,],
91
+ }
92
+ var volumeList1 = [
93
+ // first item is brackground image
94
+ {
95
+ //url: url ? url : "../demos/images/example4d+orig.HEAD",//"./AIL.nii.gz",//"./images/RAS.nii.gz", "./images/spm152.nii.gz",
96
+ url: url ? url : "../tests/images/mni152.nii.gz",//"./AIL.nii.gz",//"./images/RAS.nii.gz", "./images/spm152.nii.gz",
97
+ //url: "./mni152.nii.gz",
98
+ colorMap: "gray",
99
+ //urlImgData : "../demos/images/example4d+orig.BRIK.gz",
100
+ opacity: 1,
101
+ visible: true,
102
+ },
103
+ {
104
+ url: "../demos/images/hippo.nii.gz",
105
+ colorMap: 'red'
106
+ }
107
+ ]
108
+ var nv1 = new Niivue({
109
+ logging: true,
110
+ show3Dcrosshair: true,
111
+ loadingText: 'there are no images',
112
+ dragAndDropEnabled: true,
113
+ backColor: [0.3, 0.2, 0.4, 1],
114
+ //thumbnail: "../demos/images/DoG.png"
115
+ })
116
+ nv1.setRadiologicalConvention(false)
117
+ nv1.attachTo('gl1')
118
+ //await nv1.loadVolumes(volumeList1)
119
+
120
+ // nv1.createEmptyDrawing()
121
+
122
+ var meshLayersList1 = [
123
+ {url: "../demos/images/lh.curv", colorMap: "rocket", opacity: 0.7},
124
+ //{url: "../demos/images/boggle.lh.annot", colorMap: "rocket", opacity: 0.9},
125
+ ]
126
+
127
+
128
+ nv1.loadMeshes([
129
+ {url: "../demos/images/lh.pial", rgba255 : [255, 255, 255, 255], layers : meshLayersList1},
130
+ //{url: "../demos/images/BrainMesh_ICBM152.lh.mz3", rgba255 : [255, 255, 255, 255], layers : meshLayersList1},
131
+ //{url: "../demos/images/PJM-example-lh.gii", rgba255 : [255, 255, 255, 255], layers : meshLayersList1},
132
+ ])
133
+
134
+ //nv1.loadConnectome(connectome)
135
+ //nv1.setSliceType(nv1.sliceTypeMultiplanar)
136
+ nv1.setSliceType(nv1.sliceTypeRender)
137
+ nv1.setClipPlane([-0.1, 90, 0])
138
+ nv1.setRenderAzimuthElevation(90,10)
139
+ //nv1.volumes[0].frame4D = 5;
140
+
141
+ //nv1.updateGLVolume()
142
+ // example of using the 'location' change event with a callback function
143
+ nv1.on('location', (data) => {
144
+ // data is an object with properties: {mm: [N N N], vox: [N N N], frac: [N N N]}
145
+ document.getElementById('location').innerHTML = 'voxel location: ' + data.vox + ' ' + data.values
146
+ //nv1.volumes[0].frame4D = Math.floor(Math.random() * 9);
147
+ //nv1.updateGLVolume()
148
+ })
149
+
150
+ nv1.on('imageLoaded', (image) => {
151
+ console.log('imageLoaded', image.name)
152
+ })
153
+
154
+ nv1.on('intensityRange', (image) => {
155
+ console.log(image.cal_min, image.cal_max)
156
+ })
157
+ </script>
158
+ </div>
159
+ </body>
160
+
161
+ </html>