niivue / ipyniivue

A WebGL-powered Jupyter Widget for Niivue based on anywidget
BSD 2-Clause "Simplified" License
25 stars 8 forks source link

☂️ Volume #52

Open manzt opened 6 months ago

manzt commented 6 months ago

Umbrella issue tracking "reactive" properties for Volume:

kolibril13 commented 6 months ago
image

here's how the notebook could look like, based on https://niivue.github.io/niivue/features/additive.voxels.html

manzt commented 6 months ago

Please try to avoid pasting large images without code. It makes it very hard to reproduce. Would be great to have the code snippet you shared, and just an image of the expected notebook output.

kolibril13 commented 6 months ago

I know, I really wish there was a feature to drag and drop notebooks directly into issues! Feel free to upvote this discussion for that -> https://github.com/orgs/community/discussions/50144

here's the notebook, just committed it to the main branch https://github.com/niivue/ipyniivue-experimental/blob/main/demo/additive_voxels.ipynb

manzt commented 6 months ago

I will certainly up vote. However, in lieu of this feature it would be great for collaboration and reproduction purposes to share the code that you can as markdown/text. I will use the notebook you shared. thanks!

kolibril13 commented 6 months ago

great to hear that, here we go!

from ipyniivue_experimental import AnyNiivue, SliceType
# based on https://niivue.github.io/niivue/features/additive.voxels.html

volumes = [
        { "path": "../images/mni152.nii.gz" },
        {
          "path": "../images/narps-4965_9U7M-hypo1_unthresh.nii.gz",
          "colormap": "red",
          "cal_min": 2,
          "cal_max": 4,
        },
        {
          "path": "../images/narps-4735_50GV-hypo1_unthresh.nii.gz",
          "colormap": "green",
          "cal_min": 2,
          "cal_max": 4,
        },
      ]
nv = AnyNiivue()
nv.load_volumes(volumes)
nv
nv.volumes[0].colorbarVisible = False
nv.volumes[1].cal_min = 1
manzt commented 6 months ago

I don't see volume.visible used anywhere in the codebase, or a way to set the visibility of an existing volume in the viewer. In fact, setting "visible": false in the JS Viewer doesn't seem to do anything?

kolibril13 commented 6 months ago

I found it used like this in basic.multiplanar.html

image
var volumeList1 = [
    { 
      url: "../images/mni152.nii.gz",
      colormap: "gray",
      visible: false,
      opacity: 1
    }, 
    {
      url: "../images/hippo.nii.gz", 
      colormap: "red",
      visible: true,
      opacity: 1
    }
  ];

and I found it here https://niivue.github.io/niivue/features/mesh.layers.html

image
var checkbox1 = document.getElementById("meshCheckbox1");
  checkbox1.onclick = function () {
    nv1.setMeshProperty(nv1.meshes[0].id, 'visible', checkbox1.checked);
    console.log(`visible=${nv1.meshes[0].visible}`);
  }
hanayik commented 6 months ago

I don't see volume.visible used anywhere in the codebase, or a way to set the visibility of an existing volume in the viewer. In fact, setting "visible": false in the JS Viewer doesn't seem to do anything?

@manzt , right. visible as used with NVImage is a relic that should be removed in a future version of niivue. However, visible is used in drawMesh3D.

So, perhaps it is best to omit visible from ipyniivue volumes. We hide images by setting their opacity to zero.

What do you think?

kolibril13 commented 6 months ago

great that min and max is working now! What do you think would be a good next example to implement? I think that staying with Volumes and going for https://niivue.github.io/niivue/features/colormaps.html would be a good choice.

import * as niivue from "../dist/index.js";

  var slider = document.getElementById("gammaSlider");
  slider.oninput = function () {
    nv1.setGamma(this.value * 0.01);
  };
  invertCheck.onchange = function () {
    nv1.volumes[0].colormapInvert = this.checked;
    nv1.updateGLVolume();
  }
  selectCheck.onchange = function () {
    if (this.checked)
      nv1.setSelectionBoxColor([0, 1, 0, 0.7]);
    else
      nv1.setSelectionBoxColor([1, 1, 1, 0.5]);
  }
  crossCheck.onchange = function () {
    if (this.checked)
      nv1.setCrosshairColor([0, 1, 0, 1]);
    else
      nv1.setCrosshairColor([1, 0, 0, 1]);
  }
  wideCheck.onchange = function () {
    if (this.checked)
      nv1.setCrosshairWidth(3);
    else
      nv1.setCrosshairWidth(1);
  }
  document.getElementById("custom").addEventListener("click", doCustom);
  function doCustom() {
    var val = document.getElementById("scriptText").value;
    val += ';let key = "Custom"; nv1.addColormap(key, cmap); nv1.volumes[0].colormap = key;nv1.updateGLVolume();';
    val && eval(val);
  }
  document.getElementById("saveBmp").addEventListener("click", doSaveBmp);
  function doSaveBmp() {
    nv1.saveScene("ScreenShot.png");
  }
  var volumeList1 = [
    {
      url: "../images/mni152.nii.gz",
      colormap: "gray",
      opacity: 1,
      visible: true,
    },
  ];
  var nv1 = new niivue.Niivue({backColor: [0.3, 0.3, 0.3, 1]});
  nv1.attachTo("gl1");
  nv1.loadVolumes(volumeList1);
  nv1.opts.multiplanarForceRender = true;
  nv1.opts.isColorbar = true;
  let cmaps = nv1.colormaps();
  let cmapEl = document.getElementById("colormaps");
  for (let i = 0; i < cmaps.length; i++) {
    let btn = document.createElement("button");
    btn.innerHTML = cmaps[i];
    btn.onclick = function () {
      nv1.setColormap(nv1.volumes[0].id, cmaps[i]);
    };
    cmapEl.appendChild(btn);
  }

I think the two new functions here are nv1.saveScene and nv1.volumes[0].colormapInvert