UNPKG

ami.js

Version:

<p align="center"> <img src="https://cloud.githubusercontent.com/assets/214063/23213764/78ade038-f90c-11e6-8208-4fcade5f3832.png" width="60%"> </p>

480 lines (409 loc) 13.8 kB
/* globals Stats, dat*/ import CamerasOrthographic from 'base/cameras/cameras.orthographic'; import ControlsOrthographic from 'base/controls/controls.trackballortho'; import HelpersLut from 'base/helpers/helpers.lut'; import HelpersStack from 'base/helpers/helpers.stack'; import LoadersVolume from 'base/loaders/loaders.volume'; import ShadersLayerUniform from 'base/shaders/shaders.layer.uniform'; import ShadersLayerVertex from 'base/shaders/shaders.layer.vertex'; import ShadersLayerFragment from 'base/shaders/shaders.layer.fragment'; import ShadersUniform from 'base/shaders/shaders.data.uniform'; import ShadersVertex from 'base/shaders/shaders.data.vertex'; import ShadersFragment from 'base/shaders/shaders.data.fragment'; // standard global letiables let controls; let renderer; let camera; let statsyay; let threeD; // let mouse = { x: 0, y: 0, }; function onMouseMove(event) { // calculate mouse position in normalized device coordinates // (-1 to +1) for both components mouse.x = (event.clientX / threeD.clientWidth) * 2 - 1; mouse.y = -(event.clientY / threeD.clientHeight) * 2 + 1; // push to shaders uniformsLayerMix.uMouse.value = new THREE.Vector2(mouse.x, mouse.y); } // let sceneLayer0TextureTarget; let sceneLayer1TextureTarget; // let sceneLayer0; // let lutLayer0; let sceneLayer1; let meshLayer1; let uniformsLayer1; let materialLayer1; let lutLayer1; let sceneLayerMix; let meshLayerMix; let uniformsLayerMix; let materialLayerMix; let layer1 = { opacity: 1.0, lut: null, interpolation: 1, }; let layerMix = { opacity0: 1.0, opacity1: 1.0, type0: 0, type1: 1, trackMouse: true, }; // FUNCTIONS function init() { // this function is executed on each animation frame function animate() { // render controls.update(); // render first layer offscreen renderer.render(sceneLayer0, camera, sceneLayer0TextureTarget, true); // render second layer offscreen renderer.render(sceneLayer1, camera, sceneLayer1TextureTarget, true); // mix the layers and render it ON screen! renderer.render(sceneLayerMix, camera); statsyay.update(); // request new frame requestAnimationFrame(function() { animate(); }); } // renderer threeD = document.getElementById('r3d'); renderer = new THREE.WebGLRenderer({ antialias: true, alpha: true, }); renderer.setSize(threeD.clientWidth, threeD.clientHeight); renderer.setClearColor(0x3F51B5, 1); threeD.appendChild(renderer.domElement); // stats statsyay = new Stats(); threeD.appendChild(statsyay.domElement); // scene sceneLayer0 = new THREE.Scene(); sceneLayer1 = new THREE.Scene(); sceneLayerMix = new THREE.Scene(); // render to texture!!!! sceneLayer0TextureTarget = new THREE.WebGLRenderTarget( threeD.clientWidth, threeD.clientHeight, {minFilter: THREE.LinearFilter, magFilter: THREE.NearestFilter, format: THREE.RGBAFormat, }); sceneLayer1TextureTarget = new THREE.WebGLRenderTarget( threeD.clientWidth, threeD.clientHeight, {minFilter: THREE.LinearFilter, magFilter: THREE.NearestFilter, format: THREE.RGBAFormat, }); // camera camera = new CamerasOrthographic( threeD.clientWidth / -2, threeD.clientWidth / 2, threeD.clientHeight / 2, threeD.clientHeight / -2, 0.1, 10000); // controls controls = new ControlsOrthographic(camera, threeD); controls.staticMoving = true; controls.noRotate = true; camera.controls = controls; animate(); } window.onload = function() { // init threeJS... init(); let data = [ 'patient1/7001_t1_average_BRAINSABC.nii.gz', 'patient2/7002_t1_average_BRAINSABC.nii.gz', ]; let files = data.map(function(v) { return 'https://cdn.rawgit.com/FNNDSC/data/master/nifti/slicer_brain/' + v; }); function buildGUI(stackHelper) { function updateLayer1() { // update layer1 geometry... if (meshLayer1) { meshLayer1.geometry.dispose(); meshLayer1.geometry = stackHelper.slice.geometry; meshLayer1.geometry.verticesNeedUpdate = true; } } function updateLayerMix() { // update layer1 geometry... if (meshLayerMix) { sceneLayerMix.remove(meshLayerMix); meshLayerMix.material.dispose(); // meshLayerMix.material = null; meshLayerMix.geometry.dispose(); // meshLayerMix.geometry = null; // add mesh in this scene with right shaders... meshLayerMix = new THREE.Mesh(stackHelper.slice.geometry, materialLayerMix); // go the LPS space meshLayerMix.applyMatrix(stackHelper.stack._ijk2LPS); sceneLayerMix.add(meshLayerMix); } } let stack = stackHelper._stack; let gui = new dat.GUI({ autoPlace: false, }); let customContainer = document.getElementById('my-gui-container'); customContainer.appendChild(gui.domElement); // // layer 0 folder // let layer0Folder = gui.addFolder('Layer 0 (Base)'); layer0Folder.add( stackHelper.slice, 'windowWidth', 1, stack.minMax[1]).step(1).listen(); layer0Folder.add( stackHelper.slice, 'windowCenter', stack.minMax[0], stack.minMax[1]).step(1).listen(); layer0Folder.add(stackHelper.slice, 'intensityAuto'); layer0Folder.add(stackHelper.slice, 'invert'); let lutUpdate = layer0Folder.add( stackHelper.slice, 'lut', lutLayer0.lutsAvailable()); lutUpdate.onChange(function(value) { lutLayer0.lut = value; stackHelper.slice.lutTexture = lutLayer0.texture; }); let indexUpdate = layer0Folder.add( stackHelper, 'index', 0, stack.dimensionsIJK.z - 1).step(1).listen(); indexUpdate.onChange(function() { updateLayer1(); updateLayerMix(); }); layer0Folder.add(stackHelper.slice, 'interpolation', 0, 1).step(1).listen(); layer0Folder.open(); // // layer 1 folder // let layer1Folder = gui.addFolder('Layer 1'); let interpolationLayer1 = layer1Folder.add(layer1, 'interpolation', 0, 1).step(1).listen(); interpolationLayer1.onChange(function(value) { uniformsLayer1.uInterpolation.value = value; // re-compute shaders let fs = new ShadersFragment(uniformsLayer1); materialLayer1.fragmentShader = fs.compute(); materialLayer1.needsUpdate = true; }); let layer1LutUpdate = layer1Folder.add(layer1, 'lut', lutLayer1.lutsAvailable()); layer1LutUpdate.onChange(function(value) { lutLayer1.lut = value; // propagate to shaders uniformsLayer1.uLut.value = 1; uniformsLayer1.uTextureLUT.value = lutLayer1.texture; }); layer1Folder.open(); // // layer mix folder // let layerMixFolder = gui.addFolder('Layer Mix'); let opacityLayerMix = layerMixFolder.add(layerMix, 'opacity1', 0, 1).step(0.01).listen(); opacityLayerMix.onChange(function(value) { uniformsLayerMix.uOpacity1.value = value; }); let layerMixTrackMouseUpdate = layerMixFolder.add(layerMix, 'trackMouse'); layerMixTrackMouseUpdate.onChange(function(value) { if (value) { uniformsLayerMix.uTrackMouse.value = 1; } else { uniformsLayerMix.uTrackMouse.value = 0; } }); layerMixFolder.open(); // hook up callbacks controls.addEventListener('OnScroll', function(e) { if (e.delta > 0) { if (stackHelper.index >= stack.dimensionsIJK.z - 1) { return false; } stackHelper.index += 1; } else { if (stackHelper.index <= 0) { return false; } stackHelper.index -= 1; } updateLayer1(); updateLayerMix(); }); updateLayer1(); updateLayerMix(); function onWindowResize() { let threeD = document.getElementById('r3d'); camera.canvas = { width: threeD.clientWidth, height: threeD.clientHeight, }; camera.fitBox(2); sceneLayer0TextureTarget.setSize(threeD.clientWidth, threeD.clientHeight); sceneLayer1TextureTarget.setSize(threeD.clientWidth, threeD.clientHeight); renderer.setSize(threeD.clientWidth, threeD.clientHeight); } window.addEventListener('resize', onWindowResize, false); onWindowResize(); // mouse move cb window.addEventListener('mousemove', onMouseMove, false); } let loader = new LoadersVolume(threeD); function handleSeries() { // // first stack of first series let mergedSeries = loader.data[0].mergeSeries(loader.data); loader.free(); loader = null; let stack = null; let stack2 = null; if (mergedSeries[0].seriesInstanceUID !== 'https://cdn.rawgit.com/FNNDSC/data/master/nifti/slicer_brain/patient1/7001_t1_average_BRAINSABC.nii.gz') { stack = mergedSeries[1].stack[0]; stack2 = mergedSeries[0].stack[0]; } else { stack = mergedSeries[0].stack[0]; stack2 = mergedSeries[1].stack[0]; } stack = mergedSeries[0].stack[0]; stack2 = mergedSeries[1].stack[0]; let stackHelper = new HelpersStack(stack); stackHelper.bbox.visible = false; stackHelper.border.visible = false; sceneLayer0.add(stackHelper); // // create layer 1.... // prepare it // * ijk2LPS transforms // * Z spacing // * etc. // stack2.prepare(); // pixels packing for the fragment shaders now happens there stack2.pack(); let textures2 = []; for (let m = 0; m < stack2._rawData.length; m++) { let tex = new THREE.DataTexture( stack2.rawData[m], stack2.textureSize, stack2.textureSize, stack2.textureType, THREE.UnsignedByteType, THREE.UVMapping, THREE.ClampToEdgeWrapping, THREE.ClampToEdgeWrapping, THREE.NearestFilter, THREE.NearestFilter); tex.needsUpdate = true; tex.flipY = true; textures2.push(tex); } // // create material && mesh then add it to sceneLayer1 uniformsLayer1 = ShadersUniform.uniforms(); uniformsLayer1.uTextureSize.value = stack2.textureSize; uniformsLayer1.uTextureContainer.value = textures2; uniformsLayer1.uWorldToData.value = stack2.lps2IJK; uniformsLayer1.uNumberOfChannels.value = stack2.numberOfChannels; uniformsLayer1.uBitsAllocated.value = stack2.bitsAllocated; uniformsLayer1.uPackedPerPixel.value = stack2.packedPerPixel; uniformsLayer1.uWindowCenterWidth.value = [stack2.windowCenter, stack2.windowWidth]; uniformsLayer1.uRescaleSlopeIntercept.value = [stack2.rescaleSlope, stack2.rescaleIntercept]; uniformsLayer1.uDataDimensions.value = [stack2.dimensionsIJK.x, stack2.dimensionsIJK.y, stack2.dimensionsIJK.z]; // generate shaders on-demand! let fs = new ShadersFragment(uniformsLayer1); let vs = new ShadersVertex(); materialLayer1 = new THREE.ShaderMaterial( {side: THREE.DoubleSide, uniforms: uniformsLayer1, vertexShader: vs.compute(), fragmentShader: fs.compute(), }); // add mesh in this scene with right shaders... meshLayer1 = new THREE.Mesh(stackHelper.slice.geometry, materialLayer1); // go the LPS space meshLayer1.applyMatrix(stack2._ijk2LPS); sceneLayer1.add(meshLayer1); // // Create the Mix layer uniformsLayerMix = ShadersLayerUniform.uniforms(); uniformsLayerMix.uTextureBackTest0.value = sceneLayer0TextureTarget.texture; uniformsLayerMix.uTextureBackTest1.value = sceneLayer1TextureTarget.texture; uniformsLayerMix.uTrackMouse.value = 1; uniformsLayerMix.uMouse.value = new THREE.Vector2(0, 0); // generate shaders on-demand! let fls = new ShadersLayerFragment(uniformsLayerMix); let vls = new ShadersLayerVertex(); materialLayerMix = new THREE.ShaderMaterial( {side: THREE.DoubleSide, uniforms: uniformsLayerMix, vertexShader: vls.compute(), fragmentShader: fls.compute(), transparent: true, }); // add mesh in this scene with right shaders... meshLayerMix = new THREE.Mesh(stackHelper.slice.geometry, materialLayerMix); // go the LPS space meshLayerMix.applyMatrix(stack2._ijk2LPS); sceneLayerMix.add(meshLayerMix); // set camera let worldbb = stack.worldBoundingBox(); let lpsDims = new THREE.Vector3( worldbb[1] - worldbb[0], worldbb[3] - worldbb[2], worldbb[5] - worldbb[4] ); // box: {halfDimensions, center} let box = { center: stack.worldCenter().clone(), halfDimensions: new THREE.Vector3(lpsDims.x + 50, lpsDims.y + 50, lpsDims.z + 50), }; // init and zoom let canvas = { width: threeD.clientWidth, height: threeD.clientHeight, }; camera.directions = [stack.xCosine, stack.yCosine, stack.zCosine]; camera.box = box; camera.canvas = canvas; camera.update(); camera.fitBox(2); // CREATE LUT lutLayer0 = new HelpersLut( 'my-lut-canvases-l0', 'default', 'linear', [[0, 0, 0, 0], [1, 1, 1, 1]], [[0, 1], [1, 1]]); lutLayer0.luts = HelpersLut.presetLuts(); lutLayer1 = new HelpersLut( 'my-lut-canvases-l1', 'default', 'linear', [[0, 0, 0, 0], [1, 1, 1, 1]], [[0, 1], [1, 1]]); lutLayer1.luts = HelpersLut.presetLuts(); layer1.lut = lutLayer1; buildGUI(stackHelper); } // load sequence for each file loader.load(files) .then(function() { handleSeries(); }) .catch(function(error) { window.console.log('oops... something went wrong...'); window.console.log(error); }); };