@niivue/niivue 0.15.1 → 0.18.1

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.1/dist/niivue.umd.js"></script>
41
+ <script src="https://unpkg.com/@niivue/niivue@0.18.1/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,11 +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
12374
  const log = new Log();
12362
12375
  const Niivue = function(options = {}) {
12363
12376
  this.opts = {};
@@ -12375,7 +12388,7 @@ const Niivue = function(options = {}) {
12375
12388
  viewModeHotKey: "KeyV",
12376
12389
  keyDebounceTime: 50,
12377
12390
  isRadiologicalConvention: false,
12378
- logLevel: "info"
12391
+ logging: false
12379
12392
  };
12380
12393
  this.canvas = null;
12381
12394
  this.gl = null;
@@ -12445,7 +12458,7 @@ const Niivue = function(options = {}) {
12445
12458
  this.intensityRange$ = new Subject();
12446
12459
  this.scene.location$ = new Subject();
12447
12460
  this.scene.loading$ = new Subject();
12448
- this.loadingText = "drag and drop to start";
12461
+ this.loadingText = "waiting for images...";
12449
12462
  this.currentClipPlaneIndex = 0;
12450
12463
  this.lastCalled = new Date().getTime();
12451
12464
  this.multiTouchGesture = false;
@@ -12458,7 +12471,7 @@ const Niivue = function(options = {}) {
12458
12471
  for (let prop in this.defaults) {
12459
12472
  this.opts[prop] = options[prop] === void 0 ? this.defaults[prop] : options[prop];
12460
12473
  }
12461
- log.setLogLevel(this.opts.logLevel);
12474
+ log.setLogLevel(this.opts.logging);
12462
12475
  this.eventsToSubjects = {
12463
12476
  location: this.scene.location$,
12464
12477
  loading: this.scene.loading$
@@ -12467,7 +12480,7 @@ const Niivue = function(options = {}) {
12467
12480
  };
12468
12481
  Niivue.prototype.attachTo = async function(id) {
12469
12482
  await this.attachToCanvas(document.getElementById(id));
12470
- log.debug("attached to canvas", [1, 0, 1], false);
12483
+ log.debug("attached to element with id: ", id);
12471
12484
  return this;
12472
12485
  };
12473
12486
  Niivue.prototype.on = function(event, callback) {
@@ -12592,14 +12605,14 @@ function intensityRaw2Scaled(hdr, raw) {
12592
12605
  hdr.scl_slope = 1;
12593
12606
  return raw * hdr.scl_slope + hdr.scl_inter;
12594
12607
  }
12595
- Niivue.prototype.calculateNewRange = function() {
12608
+ Niivue.prototype.calculateNewRange = function(volIdx = 0) {
12596
12609
  if (this.sliceType === this.sliceTypeRender) {
12597
12610
  return;
12598
12611
  }
12599
12612
  let frac = this.canvasPos2frac([this.dragStart[0], this.dragStart[1]]);
12600
- let startVox = this.frac2vox(frac);
12613
+ let startVox = this.frac2vox(frac, volIdx);
12601
12614
  frac = this.canvasPos2frac([this.dragEnd[0], this.dragEnd[1]]);
12602
- let endVox = this.frac2vox(frac);
12615
+ let endVox = this.frac2vox(frac, volIdx);
12603
12616
  let hi = -Number.MAX_VALUE, lo = Number.MAX_VALUE;
12604
12617
  let xrange;
12605
12618
  let yrange;
@@ -12614,8 +12627,8 @@ Niivue.prototype.calculateNewRange = function() {
12614
12627
  } else if (startVox[2] - endVox[2] === 0) {
12615
12628
  zrange[1] = startVox[2] + 1;
12616
12629
  }
12617
- const hdr = this.volumes[0].hdr;
12618
- const img = this.volumes[0].img;
12630
+ const hdr = this.volumes[volIdx].hdr;
12631
+ const img = this.volumes[volIdx].img;
12619
12632
  const xdim = hdr.dims[1];
12620
12633
  const ydim = hdr.dims[2];
12621
12634
  for (let z = zrange[0]; z < zrange[1]; z++) {
@@ -12635,8 +12648,8 @@ Niivue.prototype.calculateNewRange = function() {
12635
12648
  }
12636
12649
  var mnScale = intensityRaw2Scaled(hdr, lo);
12637
12650
  var mxScale = intensityRaw2Scaled(hdr, hi);
12638
- this.volumes[0].cal_min = mnScale;
12639
- this.volumes[0].cal_max = mxScale;
12651
+ this.volumes[volIdx].cal_min = mnScale;
12652
+ this.volumes[volIdx].cal_max = mxScale;
12640
12653
  this.intensityRange$.next([mnScale, mxScale]);
12641
12654
  };
12642
12655
  Niivue.prototype.mouseUpListener = function() {
@@ -12847,22 +12860,40 @@ Niivue.prototype.getOverlayIndexByID = function(id) {
12847
12860
  return -1;
12848
12861
  };
12849
12862
  Niivue.prototype.setVolume = function(volume, toIndex = 0) {
12863
+ this.volumes.map((v) => {
12864
+ log.debug(v.name);
12865
+ });
12850
12866
  let numberOfLoadedImages = this.volumes.length;
12851
12867
  if (toIndex > numberOfLoadedImages) {
12852
12868
  return;
12853
12869
  }
12854
12870
  let volIndex = this.getVolumeIndexByID(volume.id);
12855
- if (volIndex >= 0) {
12856
- this.volumes.splice(volIndex, 1);
12857
- }
12858
12871
  if (toIndex === 0) {
12872
+ this.volumes.splice(volIndex, 1);
12859
12873
  this.volumes.unshift(volume);
12860
12874
  this.back = this.volumes[0];
12875
+ this.overlays = this.volumes.slice(1);
12876
+ } else if (toIndex < 0) {
12877
+ this.volumes.splice(this.getVolumeIndexByID(volume.id), 1);
12878
+ this.back = this.volumes[0];
12879
+ if (this.volumes.length > 1) {
12880
+ this.overlays = this.volumes.slice(1);
12881
+ } else {
12882
+ this.overlays = [];
12883
+ }
12861
12884
  } else {
12885
+ this.volumes.splice(volIndex, 1);
12862
12886
  this.volumes.splice(toIndex, 0, volume);
12863
12887
  this.overlays = this.volumes.slice(1);
12888
+ this.back = this.volumes[0];
12864
12889
  }
12865
12890
  this.updateGLVolume();
12891
+ this.volumes.map((v) => {
12892
+ log.debug(v.name);
12893
+ });
12894
+ };
12895
+ Niivue.prototype.removeVolume = function(volume) {
12896
+ this.setVolume(volume, -1);
12866
12897
  };
12867
12898
  Niivue.prototype.moveVolumeToBottom = function(volume) {
12868
12899
  this.setVolume(volume, 0);
@@ -12991,12 +13022,11 @@ Niivue.prototype.loadVolumes = async function(volumeList) {
12991
13022
  this.loadingText = "loading...";
12992
13023
  this.drawScene();
12993
13024
  } else {
12994
- this.loadingText = "drag and drop to start";
13025
+ this.loadingText = "waiting for images...";
12995
13026
  }
12996
13027
  });
12997
- if (!this.initialized) {
12998
- await this.init();
12999
- }
13028
+ if (!this.initialized)
13029
+ ;
13000
13030
  this.volumes = [];
13001
13031
  this.gl.clearColor(0, 0, 0, 1);
13002
13032
  this.gl.clear(this.gl.COLOR_BUFFER_BIT);
@@ -13111,65 +13141,12 @@ Niivue.prototype.initText = async function() {
13111
13141
  await this.loadDefaultFont();
13112
13142
  this.drawLoadingText(this.loadingText);
13113
13143
  };
13114
- Niivue.prototype.processImage = function(imageIndex, cmd, isNewLayer = true) {
13115
- let image = this.volumes[imageIndex].clone();
13116
- let metadata = image.getImageMetadata();
13117
- this.worker.postMessage([metadata, image.img.buffer, cmd, isNewLayer]);
13118
- };
13119
- Niivue.prototype.initWasm = async function() {
13120
- this.worker = WorkerWrapper;
13121
- this.worker.onmessage = (e) => {
13122
- const id = e.data.id;
13123
- let processedImage = this.volumes.find((image) => image.id == id);
13124
- if (!processedImage) {
13125
- console.log("image not found");
13126
- return;
13127
- }
13128
- const isNewLayer = e.data.isNewLayer;
13129
- if (isNewLayer) {
13130
- processedImage = processedImage.clone();
13131
- processedImage.id = v4();
13132
- }
13133
- let imageBytes = e.data.imageBytes;
13134
- switch (processedImage.hdr.datatypeCode) {
13135
- case processedImage.DT_UNSIGNED_CHAR:
13136
- processedImage.img = new Uint8Array(imageBytes);
13137
- break;
13138
- case processedImage.DT_SIGNED_SHORT:
13139
- processedImage.img = new Int16Array(imageBytes);
13140
- break;
13141
- case processedImage.DT_FLOAT:
13142
- processedImage.img = new Float32Array(imageBytes);
13143
- break;
13144
- case processedImage.DT_DOUBLE:
13145
- throw "datatype " + processedImage.hdr.datatypeCode + " not supported";
13146
- case processedImage.DT_RGB:
13147
- processedImage.img = new Uint8Array(imageBytes);
13148
- break;
13149
- case processedImage.DT_UINT16:
13150
- processedImage.img = new Uint16Array(imageBytes);
13151
- break;
13152
- case processedImage.DT_RGBA32:
13153
- processedImage.img = new Uint8Array(imageBytes);
13154
- break;
13155
- default:
13156
- throw "datatype " + processedImage.hdr.datatypeCode + " not supported";
13157
- }
13158
- processedImage.trustCalMinMax = false;
13159
- processedImage.calMinMax();
13160
- let imageIndex = this.volumes.length;
13161
- if (isNewLayer) {
13162
- this.setVolume(processedImage, this.volumes.length);
13163
- } else {
13164
- imageIndex = this.volumes.indexOf(processedImage);
13165
- }
13166
- this.refreshLayers(processedImage, imageIndex, this.volumes.length);
13167
- this.drawScene();
13168
- this.updateGLVolume();
13169
- };
13170
- };
13171
13144
  Niivue.prototype.init = async function() {
13172
- await this.initWasm();
13145
+ let rendererInfo = this.gl.getExtension("WEBGL_debug_renderer_info");
13146
+ let vendor = this.gl.getParameter(rendererInfo.UNMASKED_VENDOR_WEBGL);
13147
+ let renderer = this.gl.getParameter(rendererInfo.UNMASKED_RENDERER_WEBGL);
13148
+ log.info("renderer vendor: ", vendor);
13149
+ log.info("renderer: ", renderer);
13173
13150
  this.gl.enable(this.gl.CULL_FACE);
13174
13151
  this.gl.cullFace(this.gl.FRONT);
13175
13152
  this.gl.enable(this.gl.BLEND);
@@ -13445,6 +13422,8 @@ Niivue.prototype.refreshLayers = function(overlayItem, layer, numLayers) {
13445
13422
  this.gl.deleteTexture(blendTexture);
13446
13423
  this.gl.viewport(0, 0, this.gl.canvas.width, this.gl.canvas.height);
13447
13424
  this.gl.deleteFramebuffer(fb);
13425
+ this.sliceShader.use(this.gl);
13426
+ this.gl.uniform1f(this.sliceShader.uniforms["overlays"], this.overlays);
13448
13427
  this.renderShader.use(this.gl);
13449
13428
  let slicescl = this.sliceScale();
13450
13429
  let vox = slicescl.vox;
@@ -13597,7 +13576,13 @@ Niivue.prototype.mouseClick = function(x, y, posChange = 0, isDelta = true) {
13597
13576
  this.scene.location$.next({
13598
13577
  mm: this.frac2mm(this.scene.crosshairPos),
13599
13578
  vox: this.frac2vox(this.scene.crosshairPos),
13600
- frac: this.scene.crosshairPos
13579
+ frac: this.scene.crosshairPos,
13580
+ values: this.volumes.map((v, index) => {
13581
+ let mm = this.frac2mm(this.scene.crosshairPos);
13582
+ let vox = v.mm2vox(mm);
13583
+ let val = v.getValue(...vox);
13584
+ return val;
13585
+ })
13601
13586
  });
13602
13587
  return;
13603
13588
  }
@@ -13617,7 +13602,13 @@ Niivue.prototype.mouseClick = function(x, y, posChange = 0, isDelta = true) {
13617
13602
  this.scene.location$.next({
13618
13603
  mm: this.frac2mm(this.scene.crosshairPos),
13619
13604
  vox: this.frac2vox(this.scene.crosshairPos),
13620
- frac: this.scene.crosshairPos
13605
+ frac: this.scene.crosshairPos,
13606
+ values: this.volumes.map((v, index) => {
13607
+ let mm = this.frac2mm(this.scene.crosshairPos);
13608
+ let vox = v.mm2vox(mm);
13609
+ let val = v.getValue(...vox);
13610
+ return val;
13611
+ })
13621
13612
  });
13622
13613
  return;
13623
13614
  } else {
@@ -13911,16 +13902,16 @@ Niivue.prototype.draw3D = function() {
13911
13902
  this.sync();
13912
13903
  return posString;
13913
13904
  };
13914
- Niivue.prototype.mm2frac = function(mm) {
13905
+ Niivue.prototype.mm2frac = function(mm, volIdx = 0) {
13915
13906
  let mm4 = fromValues(mm[0], mm[1], mm[2], 1);
13916
- let d = this.back.dims;
13907
+ let d = this.volumes[volIdx].hdr.dims;
13917
13908
  let frac = [0, 0, 0];
13918
13909
  if (typeof d === "undefined") {
13919
13910
  return frac;
13920
13911
  }
13921
13912
  if (d[1] < 1 || d[2] < 1 || d[3] < 1)
13922
13913
  return frac;
13923
- let sform = clone(this.back.matRAS);
13914
+ let sform = clone(this.volumes[volIdx].matRAS);
13924
13915
  transpose(sform, sform);
13925
13916
  invert(sform, sform);
13926
13917
  transformMat4(mm4, mm4, sform);
@@ -13929,32 +13920,31 @@ Niivue.prototype.mm2frac = function(mm) {
13929
13920
  frac[2] = (mm4[2] + 0.5) / d[3];
13930
13921
  return frac;
13931
13922
  };
13932
- Niivue.prototype.vox2frac = function(vox) {
13923
+ Niivue.prototype.vox2frac = function(vox, volIdx = 0) {
13933
13924
  let frac = [
13934
- (vox[0] + 0.5) / this.back.dims[1],
13935
- (vox[1] + 0.5) / this.back.dims[2],
13936
- (vox[2] + 0.5) / this.back.dims[3]
13925
+ (vox[0] + 0.5) / this.volumes[volIdx].hdr.dims[1],
13926
+ (vox[1] + 0.5) / this.volumes[volIdx].hdr.dims[2],
13927
+ (vox[2] + 0.5) / this.volumes[volIdx].hdr.dims[3]
13937
13928
  ];
13938
13929
  return frac;
13939
13930
  };
13940
- Niivue.prototype.frac2vox = function(frac) {
13931
+ Niivue.prototype.frac2vox = function(frac, volIdx = 0) {
13941
13932
  let vox = [
13942
- Math.round(frac[0] * this.back.dims[1] - 0.5),
13943
- Math.round(frac[1] * this.back.dims[2] - 0.5),
13944
- Math.round(frac[2] * this.back.dims[3] - 0.5)
13933
+ Math.round(frac[0] * this.volumes[volIdx].hdr.dims[1] - 0.5),
13934
+ Math.round(frac[1] * this.volumes[volIdx].hdr.dims[2] - 0.5),
13935
+ Math.round(frac[2] * this.volumes[volIdx].hdr.dims[3] - 0.5)
13945
13936
  ];
13946
13937
  return vox;
13947
13938
  };
13948
- Niivue.prototype.frac2mm = function(frac) {
13939
+ Niivue.prototype.frac2mm = function(frac, volIdx = 0) {
13949
13940
  let pos = fromValues(frac[0], frac[1], frac[2], 1);
13950
- let dim = fromValues(this.back.dims[1], this.back.dims[2], this.back.dims[3], 1);
13951
- let sform = clone(this.back.matRAS);
13941
+ let dim = fromValues(this.volumes[volIdx].hdr.dims[1], this.volumes[volIdx].hdr.dims[2], this.volumes[volIdx].hdr.dims[3], 1);
13942
+ let sform = clone(this.volumes[volIdx].matRAS);
13952
13943
  transpose(sform, sform);
13953
13944
  mul(pos, pos, dim);
13954
13945
  let shim = fromValues(-0.5, -0.5, -0.5, 0);
13955
13946
  add(pos, pos, shim);
13956
13947
  transformMat4(pos, pos, sform);
13957
- this.mm2frac(pos);
13958
13948
  return pos;
13959
13949
  };
13960
13950
  Niivue.prototype.canvasPos2frac = function(canvasPos) {
@@ -14023,7 +14013,7 @@ Niivue.prototype.drawScene = function() {
14023
14013
  this.gl.clearColor(this.opts.backColor[0], this.opts.backColor[1], this.opts.backColor[2], this.opts.backColor[3]);
14024
14014
  this.gl.clear(this.gl.COLOR_BUFFER_BIT);
14025
14015
  let posString = "";
14026
- if (!this.back.dims) {
14016
+ if (this.volumes.length === 0 || typeof this.volumes[0].dims === "undefined") {
14027
14017
  this.drawLoadingText(this.loadingText);
14028
14018
  return;
14029
14019
  }