@niivue/niivue 0.15.0 → 0.18.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/README.md CHANGED
@@ -38,7 +38,7 @@ We host many NiiVue examples via github pages. These are updated and deployed au
38
38
 
39
39
  ## existing html page
40
40
  ```html
41
- <script src="https://unpkg.com/@niivue/niivue@0.15.0/dist/niivue.umd.js"></script>
41
+ <script src="https://unpkg.com/@niivue/niivue@0.18.0/dist/niivue.umd.js"></script>
42
42
 
43
43
  <canvas id="gl" height=480 width=640></canvas>
44
44
 
package/dist/niivue.es.js CHANGED
@@ -714,16 +714,22 @@ var fragSliceShader = `#version 300 es
714
714
  precision highp int;
715
715
  precision highp float;
716
716
  uniform highp sampler3D volume, overlay;
717
+ uniform float overlays;
717
718
  uniform float opacity;
718
719
  in vec3 texPos;
719
720
  out vec4 color;
720
721
  void main() {
721
722
  color = vec4(texture(volume, texPos).rgb, opacity);
722
- vec4 ocolor = texture(overlay, texPos);
723
- float aout = ocolor.a + (1.0 - ocolor.a) * color.a;
724
- if (aout <= 0.0) return;
725
- color.rgb = ((ocolor.rgb * ocolor.a) + (color.rgb * color.a * (1.0 - ocolor.a))) / aout;
726
- color.a = aout;
723
+ vec4 ocolor = vec4(0.0);
724
+ if (overlays < 1.0) {
725
+ ocolor = vec4(0.0, 0.0, 0.0, 0.0);
726
+ } else {
727
+ ocolor = texture(overlay, texPos);
728
+ }
729
+ float aout = ocolor.a + (1.0 - ocolor.a) * color.a;
730
+ if (aout <= 0.0) return;
731
+ color.rgb = ((ocolor.rgb * ocolor.a) + (color.rgb * color.a * (1.0 - ocolor.a))) / aout;
732
+ color.a = aout;
727
733
  }`;
728
734
  var fragLineShader = `#version 300 es
729
735
  #line 189
@@ -10559,6 +10565,16 @@ NVImage.prototype.vox2mm = function(XYZ, mtx) {
10559
10565
  let pos3 = fromValues$1(pos[0], pos[1], pos[2]);
10560
10566
  return pos3;
10561
10567
  };
10568
+ NVImage.prototype.mm2vox = function(mm) {
10569
+ let sform = fromValues$2(...this.hdr.affine.flat());
10570
+ let out = clone(sform);
10571
+ transpose(out, sform);
10572
+ invert(out, out);
10573
+ let pos = fromValues(mm[0], mm[1], mm[2], 1);
10574
+ transformMat4(pos, pos, out);
10575
+ let pos3 = fromValues$1(pos[0], pos[1], pos[2]);
10576
+ return [Math.round(pos3[0]), Math.round(pos3[1]), Math.round(pos3[2])];
10577
+ };
10562
10578
  NVImage.prototype.arrayEquals = function(a, b) {
10563
10579
  return Array.isArray(a) && Array.isArray(b) && a.length === b.length && a.every((val, index) => val === b[index]);
10564
10580
  };
@@ -10785,35 +10801,37 @@ String.prototype.getBytes = function() {
10785
10801
  }
10786
10802
  return bytes;
10787
10803
  };
10804
+ NVImage.prototype.getValue = function(x, y, z) {
10805
+ const { nx, ny } = this.getImageMetadata();
10806
+ return this.img[x + y * nx + z * nx * ny];
10807
+ };
10788
10808
  const Log = function(logLevel) {
10789
- this.DEBUG = "debug";
10790
- this.INFO = "info";
10791
- this.WARN = "warn";
10792
- this.ERROR = "error";
10793
- this.LOG_PREFIX = "NiiVue: ";
10809
+ this.LOGGING_ON = true;
10810
+ this.LOGGING_OFF = false;
10811
+ this.LOG_PREFIX = "NiiVue:";
10794
10812
  this.logLevel = logLevel;
10795
10813
  };
10796
10814
  Log.prototype.getTimeStamp = function() {
10797
- return `${this.LOG_PREFIX}${Date.now()} `;
10815
+ return `${this.LOG_PREFIX} `;
10798
10816
  };
10799
10817
  Log.prototype.debug = function() {
10800
- if (this.logLevel === this.DEBUG) {
10801
- console.log(this.getTimeStamp(), ...arguments);
10818
+ if (this.logLevel === this.LOGGING_ON) {
10819
+ console.log(this.getTimeStamp(), "DEBUG", ...arguments);
10802
10820
  }
10803
10821
  };
10804
10822
  Log.prototype.info = function() {
10805
- if (this.logLevel === this.INFO) {
10806
- console.log(this.getTimeStamp(), ...arguments);
10823
+ if (this.logLevel === this.LOGGING_ON) {
10824
+ console.log(this.getTimeStamp(), "INFO", ...arguments);
10807
10825
  }
10808
10826
  };
10809
10827
  Log.prototype.warn = function() {
10810
- if (this.logLevel === this.WARN) {
10811
- console.warn(this.getTimeStamp(), ...arguments);
10828
+ if (this.logLevel === this.LOGGING_ON) {
10829
+ console.warn(this.getTimeStamp(), "WARN", ...arguments);
10812
10830
  }
10813
10831
  };
10814
10832
  Log.prototype.error = function() {
10815
- if (this.logLevel === this.ERROR) {
10816
- console.error(this.getTimeStamp(), ...arguments);
10833
+ if (this.logLevel === this.LOGGING_ON) {
10834
+ console.error(this.getTimeStamp(), "ERROR", ...arguments);
10817
10835
  }
10818
10836
  };
10819
10837
  Log.prototype.setLogLevel = function(logLevel) {
@@ -12353,12 +12371,6 @@ var defaultFontMetrics = {
12353
12371
  glyphs,
12354
12372
  kerning
12355
12373
  };
12356
- function WorkerWrapper() {
12357
- return new Worker("/assets/niimathWorker.1f52eda8.js", {
12358
- "type": "module"
12359
- });
12360
- }
12361
- var niiMathWorker = new WorkerWrapper();
12362
12374
  const log = new Log();
12363
12375
  const Niivue = function(options = {}) {
12364
12376
  this.opts = {};
@@ -12376,7 +12388,7 @@ const Niivue = function(options = {}) {
12376
12388
  viewModeHotKey: "KeyV",
12377
12389
  keyDebounceTime: 50,
12378
12390
  isRadiologicalConvention: false,
12379
- logLevel: "info"
12391
+ logging: false
12380
12392
  };
12381
12393
  this.canvas = null;
12382
12394
  this.gl = null;
@@ -12446,7 +12458,7 @@ const Niivue = function(options = {}) {
12446
12458
  this.intensityRange$ = new Subject();
12447
12459
  this.scene.location$ = new Subject();
12448
12460
  this.scene.loading$ = new Subject();
12449
- this.loadingText = "drag and drop to start";
12461
+ this.loadingText = "waiting for images...";
12450
12462
  this.currentClipPlaneIndex = 0;
12451
12463
  this.lastCalled = new Date().getTime();
12452
12464
  this.multiTouchGesture = false;
@@ -12459,7 +12471,7 @@ const Niivue = function(options = {}) {
12459
12471
  for (let prop in this.defaults) {
12460
12472
  this.opts[prop] = options[prop] === void 0 ? this.defaults[prop] : options[prop];
12461
12473
  }
12462
- log.setLogLevel(this.opts.logLevel);
12474
+ log.setLogLevel(this.opts.logging);
12463
12475
  this.eventsToSubjects = {
12464
12476
  location: this.scene.location$,
12465
12477
  loading: this.scene.loading$
@@ -12468,7 +12480,7 @@ const Niivue = function(options = {}) {
12468
12480
  };
12469
12481
  Niivue.prototype.attachTo = async function(id) {
12470
12482
  await this.attachToCanvas(document.getElementById(id));
12471
- log.debug("attached to canvas", [1, 0, 1], false);
12483
+ log.debug("attached to element with id: ", id);
12472
12484
  return this;
12473
12485
  };
12474
12486
  Niivue.prototype.on = function(event, callback) {
@@ -12593,14 +12605,14 @@ function intensityRaw2Scaled(hdr, raw) {
12593
12605
  hdr.scl_slope = 1;
12594
12606
  return raw * hdr.scl_slope + hdr.scl_inter;
12595
12607
  }
12596
- Niivue.prototype.calculateNewRange = function() {
12608
+ Niivue.prototype.calculateNewRange = function(volIdx = 0) {
12597
12609
  if (this.sliceType === this.sliceTypeRender) {
12598
12610
  return;
12599
12611
  }
12600
12612
  let frac = this.canvasPos2frac([this.dragStart[0], this.dragStart[1]]);
12601
- let startVox = this.frac2vox(frac);
12613
+ let startVox = this.frac2vox(frac, volIdx);
12602
12614
  frac = this.canvasPos2frac([this.dragEnd[0], this.dragEnd[1]]);
12603
- let endVox = this.frac2vox(frac);
12615
+ let endVox = this.frac2vox(frac, volIdx);
12604
12616
  let hi = -Number.MAX_VALUE, lo = Number.MAX_VALUE;
12605
12617
  let xrange;
12606
12618
  let yrange;
@@ -12615,8 +12627,8 @@ Niivue.prototype.calculateNewRange = function() {
12615
12627
  } else if (startVox[2] - endVox[2] === 0) {
12616
12628
  zrange[1] = startVox[2] + 1;
12617
12629
  }
12618
- const hdr = this.volumes[0].hdr;
12619
- const img = this.volumes[0].img;
12630
+ const hdr = this.volumes[volIdx].hdr;
12631
+ const img = this.volumes[volIdx].img;
12620
12632
  const xdim = hdr.dims[1];
12621
12633
  const ydim = hdr.dims[2];
12622
12634
  for (let z = zrange[0]; z < zrange[1]; z++) {
@@ -12636,8 +12648,8 @@ Niivue.prototype.calculateNewRange = function() {
12636
12648
  }
12637
12649
  var mnScale = intensityRaw2Scaled(hdr, lo);
12638
12650
  var mxScale = intensityRaw2Scaled(hdr, hi);
12639
- this.volumes[0].cal_min = mnScale;
12640
- this.volumes[0].cal_max = mxScale;
12651
+ this.volumes[volIdx].cal_min = mnScale;
12652
+ this.volumes[volIdx].cal_max = mxScale;
12641
12653
  this.intensityRange$.next([mnScale, mxScale]);
12642
12654
  };
12643
12655
  Niivue.prototype.mouseUpListener = function() {
@@ -12848,22 +12860,37 @@ Niivue.prototype.getOverlayIndexByID = function(id) {
12848
12860
  return -1;
12849
12861
  };
12850
12862
  Niivue.prototype.setVolume = function(volume, toIndex = 0) {
12863
+ this.volumes.map((v) => {
12864
+ log.debug(v.name);
12865
+ });
12851
12866
  let numberOfLoadedImages = this.volumes.length;
12852
12867
  if (toIndex > numberOfLoadedImages) {
12853
12868
  return;
12854
12869
  }
12855
- let volIndex = this.getVolumeIndexByID(volume.id);
12856
- if (volIndex >= 0) {
12857
- this.volumes.splice(volIndex, 1);
12858
- }
12870
+ this.getVolumeIndexByID(volume.id);
12859
12871
  if (toIndex === 0) {
12860
12872
  this.volumes.unshift(volume);
12861
12873
  this.back = this.volumes[0];
12874
+ this.overlays = this.volumes.slice(1);
12875
+ } else if (toIndex < 0) {
12876
+ this.volumes.splice(this.getVolumeIndexByID(volume.id), 1);
12877
+ this.back = this.volumes[0];
12878
+ if (this.volumes.length > 1) {
12879
+ this.overlays = this.volumes.slice(1);
12880
+ } else {
12881
+ this.overlays = [];
12882
+ }
12862
12883
  } else {
12863
12884
  this.volumes.splice(toIndex, 0, volume);
12864
12885
  this.overlays = this.volumes.slice(1);
12865
12886
  }
12866
12887
  this.updateGLVolume();
12888
+ this.volumes.map((v) => {
12889
+ log.debug(v.name);
12890
+ });
12891
+ };
12892
+ Niivue.prototype.removeVolume = function(volume) {
12893
+ this.setVolume(volume, -1);
12867
12894
  };
12868
12895
  Niivue.prototype.moveVolumeToBottom = function(volume) {
12869
12896
  this.setVolume(volume, 0);
@@ -12992,12 +13019,11 @@ Niivue.prototype.loadVolumes = async function(volumeList) {
12992
13019
  this.loadingText = "loading...";
12993
13020
  this.drawScene();
12994
13021
  } else {
12995
- this.loadingText = "drag and drop to start";
13022
+ this.loadingText = "waiting for images...";
12996
13023
  }
12997
13024
  });
12998
- if (!this.initialized) {
12999
- await this.init();
13000
- }
13025
+ if (!this.initialized)
13026
+ ;
13001
13027
  this.volumes = [];
13002
13028
  this.gl.clearColor(0, 0, 0, 1);
13003
13029
  this.gl.clear(this.gl.COLOR_BUFFER_BIT);
@@ -13112,65 +13138,12 @@ Niivue.prototype.initText = async function() {
13112
13138
  await this.loadDefaultFont();
13113
13139
  this.drawLoadingText(this.loadingText);
13114
13140
  };
13115
- Niivue.prototype.processImage = function(imageIndex, cmd, isNewLayer = true) {
13116
- let image = this.volumes[imageIndex].clone();
13117
- let metadata = image.getImageMetadata();
13118
- this.worker.postMessage([metadata, image.img.buffer, cmd, isNewLayer]);
13119
- };
13120
- Niivue.prototype.initWasm = async function() {
13121
- this.worker = niiMathWorker;
13122
- this.worker.onmessage = (e) => {
13123
- const id = e.data.id;
13124
- let processedImage = this.volumes.find((image) => image.id == id);
13125
- if (!processedImage) {
13126
- console.log("image not found");
13127
- return;
13128
- }
13129
- const isNewLayer = e.data.isNewLayer;
13130
- if (isNewLayer) {
13131
- processedImage = processedImage.clone();
13132
- processedImage.id = v4();
13133
- }
13134
- let imageBytes = e.data.imageBytes;
13135
- switch (processedImage.hdr.datatypeCode) {
13136
- case processedImage.DT_UNSIGNED_CHAR:
13137
- processedImage.img = new Uint8Array(imageBytes);
13138
- break;
13139
- case processedImage.DT_SIGNED_SHORT:
13140
- processedImage.img = new Int16Array(imageBytes);
13141
- break;
13142
- case processedImage.DT_FLOAT:
13143
- processedImage.img = new Float32Array(imageBytes);
13144
- break;
13145
- case processedImage.DT_DOUBLE:
13146
- throw "datatype " + processedImage.hdr.datatypeCode + " not supported";
13147
- case processedImage.DT_RGB:
13148
- processedImage.img = new Uint8Array(imageBytes);
13149
- break;
13150
- case processedImage.DT_UINT16:
13151
- processedImage.img = new Uint16Array(imageBytes);
13152
- break;
13153
- case processedImage.DT_RGBA32:
13154
- processedImage.img = new Uint8Array(imageBytes);
13155
- break;
13156
- default:
13157
- throw "datatype " + processedImage.hdr.datatypeCode + " not supported";
13158
- }
13159
- processedImage.trustCalMinMax = false;
13160
- processedImage.calMinMax();
13161
- let imageIndex = this.volumes.length;
13162
- if (isNewLayer) {
13163
- this.setVolume(processedImage, this.volumes.length);
13164
- } else {
13165
- imageIndex = this.volumes.indexOf(processedImage);
13166
- }
13167
- this.refreshLayers(processedImage, imageIndex, this.volumes.length);
13168
- this.drawScene();
13169
- this.updateGLVolume();
13170
- };
13171
- };
13172
13141
  Niivue.prototype.init = async function() {
13173
- await this.initWasm();
13142
+ let rendererInfo = this.gl.getExtension("WEBGL_debug_renderer_info");
13143
+ let vendor = this.gl.getParameter(rendererInfo.UNMASKED_VENDOR_WEBGL);
13144
+ let renderer = this.gl.getParameter(rendererInfo.UNMASKED_RENDERER_WEBGL);
13145
+ log.info("renderer vendor: ", vendor);
13146
+ log.info("renderer: ", renderer);
13174
13147
  this.gl.enable(this.gl.CULL_FACE);
13175
13148
  this.gl.cullFace(this.gl.FRONT);
13176
13149
  this.gl.enable(this.gl.BLEND);
@@ -13446,6 +13419,8 @@ Niivue.prototype.refreshLayers = function(overlayItem, layer, numLayers) {
13446
13419
  this.gl.deleteTexture(blendTexture);
13447
13420
  this.gl.viewport(0, 0, this.gl.canvas.width, this.gl.canvas.height);
13448
13421
  this.gl.deleteFramebuffer(fb);
13422
+ this.sliceShader.use(this.gl);
13423
+ this.gl.uniform1f(this.sliceShader.uniforms["overlays"], this.overlays);
13449
13424
  this.renderShader.use(this.gl);
13450
13425
  let slicescl = this.sliceScale();
13451
13426
  let vox = slicescl.vox;
@@ -13598,7 +13573,13 @@ Niivue.prototype.mouseClick = function(x, y, posChange = 0, isDelta = true) {
13598
13573
  this.scene.location$.next({
13599
13574
  mm: this.frac2mm(this.scene.crosshairPos),
13600
13575
  vox: this.frac2vox(this.scene.crosshairPos),
13601
- frac: this.scene.crosshairPos
13576
+ frac: this.scene.crosshairPos,
13577
+ values: this.volumes.map((v, index) => {
13578
+ let mm = this.frac2mm(this.scene.crosshairPos);
13579
+ let vox = v.mm2vox(mm);
13580
+ let val = v.getValue(...vox);
13581
+ return val;
13582
+ })
13602
13583
  });
13603
13584
  return;
13604
13585
  }
@@ -13618,7 +13599,13 @@ Niivue.prototype.mouseClick = function(x, y, posChange = 0, isDelta = true) {
13618
13599
  this.scene.location$.next({
13619
13600
  mm: this.frac2mm(this.scene.crosshairPos),
13620
13601
  vox: this.frac2vox(this.scene.crosshairPos),
13621
- frac: this.scene.crosshairPos
13602
+ frac: this.scene.crosshairPos,
13603
+ values: this.volumes.map((v, index) => {
13604
+ let mm = this.frac2mm(this.scene.crosshairPos);
13605
+ let vox = v.mm2vox(mm);
13606
+ let val = v.getValue(...vox);
13607
+ return val;
13608
+ })
13622
13609
  });
13623
13610
  return;
13624
13611
  } else {
@@ -13912,16 +13899,16 @@ Niivue.prototype.draw3D = function() {
13912
13899
  this.sync();
13913
13900
  return posString;
13914
13901
  };
13915
- Niivue.prototype.mm2frac = function(mm) {
13902
+ Niivue.prototype.mm2frac = function(mm, volIdx = 0) {
13916
13903
  let mm4 = fromValues(mm[0], mm[1], mm[2], 1);
13917
- let d = this.back.dims;
13904
+ let d = this.volumes[volIdx].hdr.dims;
13918
13905
  let frac = [0, 0, 0];
13919
13906
  if (typeof d === "undefined") {
13920
13907
  return frac;
13921
13908
  }
13922
13909
  if (d[1] < 1 || d[2] < 1 || d[3] < 1)
13923
13910
  return frac;
13924
- let sform = clone(this.back.matRAS);
13911
+ let sform = clone(this.volumes[volIdx].matRAS);
13925
13912
  transpose(sform, sform);
13926
13913
  invert(sform, sform);
13927
13914
  transformMat4(mm4, mm4, sform);
@@ -13930,32 +13917,31 @@ Niivue.prototype.mm2frac = function(mm) {
13930
13917
  frac[2] = (mm4[2] + 0.5) / d[3];
13931
13918
  return frac;
13932
13919
  };
13933
- Niivue.prototype.vox2frac = function(vox) {
13920
+ Niivue.prototype.vox2frac = function(vox, volIdx = 0) {
13934
13921
  let frac = [
13935
- (vox[0] + 0.5) / this.back.dims[1],
13936
- (vox[1] + 0.5) / this.back.dims[2],
13937
- (vox[2] + 0.5) / this.back.dims[3]
13922
+ (vox[0] + 0.5) / this.volumes[volIdx].hdr.dims[1],
13923
+ (vox[1] + 0.5) / this.volumes[volIdx].hdr.dims[2],
13924
+ (vox[2] + 0.5) / this.volumes[volIdx].hdr.dims[3]
13938
13925
  ];
13939
13926
  return frac;
13940
13927
  };
13941
- Niivue.prototype.frac2vox = function(frac) {
13928
+ Niivue.prototype.frac2vox = function(frac, volIdx = 0) {
13942
13929
  let vox = [
13943
- Math.round(frac[0] * this.back.dims[1] - 0.5),
13944
- Math.round(frac[1] * this.back.dims[2] - 0.5),
13945
- Math.round(frac[2] * this.back.dims[3] - 0.5)
13930
+ Math.round(frac[0] * this.volumes[volIdx].hdr.dims[1] - 0.5),
13931
+ Math.round(frac[1] * this.volumes[volIdx].hdr.dims[2] - 0.5),
13932
+ Math.round(frac[2] * this.volumes[volIdx].hdr.dims[3] - 0.5)
13946
13933
  ];
13947
13934
  return vox;
13948
13935
  };
13949
- Niivue.prototype.frac2mm = function(frac) {
13936
+ Niivue.prototype.frac2mm = function(frac, volIdx = 0) {
13950
13937
  let pos = fromValues(frac[0], frac[1], frac[2], 1);
13951
- let dim = fromValues(this.back.dims[1], this.back.dims[2], this.back.dims[3], 1);
13952
- let sform = clone(this.back.matRAS);
13938
+ let dim = fromValues(this.volumes[volIdx].hdr.dims[1], this.volumes[volIdx].hdr.dims[2], this.volumes[volIdx].hdr.dims[3], 1);
13939
+ let sform = clone(this.volumes[volIdx].matRAS);
13953
13940
  transpose(sform, sform);
13954
13941
  mul(pos, pos, dim);
13955
13942
  let shim = fromValues(-0.5, -0.5, -0.5, 0);
13956
13943
  add(pos, pos, shim);
13957
13944
  transformMat4(pos, pos, sform);
13958
- this.mm2frac(pos);
13959
13945
  return pos;
13960
13946
  };
13961
13947
  Niivue.prototype.canvasPos2frac = function(canvasPos) {