@edsilv/ami.js
Version:
<p align="center"> <img src="https://user-images.githubusercontent.com/214063/46479857-4cd66e80-c7f0-11e8-9585-5748409c9490.png" width="60%"> </p>
515 lines (438 loc) • 15.1 kB
JavaScript
/* globals Stats, dat*/
import ControlsTrackball from 'base/controls/controls.trackball';
import HelpersLut from 'base/helpers/helpers.lut';
import HelpersVR from 'base/helpers/helpers.volumerendering';
import HelpersStack from 'base/helpers/helpers.stack';
import LoadersVolume from 'base/loaders/loaders.volume';
import VRUniforms from 'base/shaders/shaders.vr.secondpass.uniform';
import VRFragment from 'base/shaders/shaders.vr.secondpass.fragment';
import VRVertex from 'base/shaders/shaders.vr.secondpass.vertex';
// standard global letiables
let controls;
let threeD;
let renderer;
let stats;
let camera;
let scene;
let sceneT;
let vrHelper;
let lut;
let ready = false;
let modified = false;
let wheel = null;
let wheelTO = null;
let points = [];
let baseMesh = null;
let boxMeshSecondPass = null;
let containerMesh = null;
let materialFirstPass = null;
let uniformsSecondPass = null;
let materialSecondPass = null;
let rtTexture = null;
let myStack = {
lut: 'random',
opacity: 'random',
steps: 256,
alphaCorrection: 0.5,
frequence: 0,
amplitude: 0,
interpolation: 1,
};
let raycaster = new THREE.Raycaster();
let mouse = new THREE.Vector2();
function onMouseMove(event) {
// calculate mouse position in normalized device coordinates
// (-1 to +1) for both components
mouse.x = (event.clientX / window.innerWidth) * 2 - 1;
mouse.y = -(event.clientY / window.innerHeight) * 2 + 1;
}
window.addEventListener('mousemove', onMouseMove, false);
function onStart(event) {
// compute intersections
// update the picking ray with the camera and mouse position
console.log(mouse);
console.log(scene);
raycaster.setFromCamera(mouse, camera);
// calculate objects intersecting the picking ray
let intersects = raycaster.intersectObject(containerMesh, false);
console.log(intersects);
for (let i = 0; i < intersects.length; i++) {
console.log(intersects[i]);
let geometry = new THREE.SphereGeometry(5, 32, 32);
let material = new THREE.MeshBasicMaterial({ color: 0xffff00 });
let sphere = new THREE.Mesh(geometry, material);
let point = intersects[i].point;
points.push(point);
sphere.position.set(point.x, point.y, point.z);
scene.add(sphere);
// intersects[i].object.material.color.set(0xff0000);
}
// smae in othre direction
raycaster.ray.direction.multiplyScalar(-1);
intersects = raycaster.intersectObject(containerMesh, false);
console.log(intersects);
for (let i = 0; i < intersects.length; i++) {
console.log(intersects[i]);
let geometry = new THREE.SphereGeometry(5, 32, 32);
let material = new THREE.MeshBasicMaterial({ color: 0xffff00 });
let sphere = new THREE.Mesh(geometry, material);
let point = intersects[i].point;
points.push(point);
sphere.position.set(point.x, point.y, point.z);
scene.add(sphere);
// intersects[i].object.material.color.set(0xff0000);
}
if (points.length === 10) {
console.log(points);
let geometry = new THREE.ConvexGeometry(points);
let material = new THREE.MeshBasicMaterial({ color: 0x00ff00 });
let mesh = new THREE.Mesh(geometry, material);
let sphere_bsp = new ThreeBSP(mesh);
const currentMesh = baseMesh;
let base_bsp = new ThreeBSP(currentMesh);
let subtract_bsp = base_bsp.intersect(sphere_bsp);
let result = subtract_bsp.toMesh(materialFirstPass);
result.geometry.computeVertexNormals();
// sceneT.remove(currentMesh);
// baseMesh = result;
// sceneT.add(result);
scene.remove(boxMeshSecondPass);
const currentMesh2 = boxMeshSecondPass;
let base_bsp2 = new ThreeBSP(currentMesh2);
let subtract_bsp2 = base_bsp2.subtract(sphere_bsp);
let result2 = subtract_bsp2.toMesh(materialSecondPass);
result2.geometry.computeVertexNormals();
scene.remove(currentMesh2);
boxMeshSecondPass = result2;
scene.add(result2);
points = [];
// scene.add(mesh);
}
if (vrHelper && uniformsSecondPass && !wheel) {
uniformsSecondPass.uSteps.value = Math.floor(myStack.steps / 2);
vrHelper.interpolation = 0;
modified = true;
}
}
function onEnd(event) {
if (vrHelper && uniformsSecondPass && !wheel) {
uniformsSecondPass.uSteps.value = myStack.steps;
vrHelper.interpolation = myStack.interpolation;
modified = true;
}
}
function onWheel() {
if (!wheel) {
uniformsSecondPass.uSteps.value = Math.floor(myStack.steps / 2);
vrHelper.interpolation = 0;
wheel = Date.now();
}
if (Date.now() - wheel < 300) {
clearTimeout(wheelTO);
wheelTO = setTimeout(function() {
uniformsSecondPass.uSteps.value = myStack.steps;
vrHelper.interpolation = myStack.interpolation;
wheel = null;
modified = true;
}, 300);
}
modified = true;
}
function onWindowResize() {
// update the camera
camera.aspect = threeD.offsetWidth / threeD.offsetHeight;
camera.updateProjectionMatrix();
// notify the renderer of the size change
renderer.setSize(threeD.offsetWidth, threeD.offsetHeight);
modified = true;
}
function buildGUI() {
let gui = new dat.GUI({
autoPlace: false,
});
let customContainer = document.getElementById('my-gui-container');
customContainer.appendChild(gui.domElement);
let stackFolder = gui.addFolder('Settings');
let lutUpdate = stackFolder.add(myStack, 'lut', lut.lutsAvailable());
lutUpdate.onChange(function(value) {
lut.lut = value;
uniformsSecondPass.uTextureLUT.value.dispose();
uniformsSecondPass.uTextureLUT.value = lut.texture;
modified = true;
});
// init LUT
lut.lut = myStack.lut;
uniformsSecondPass.uTextureLUT.value.dispose();
uniformsSecondPass.uTextureLUT.value = lut.texture;
let opacityUpdate = stackFolder.add(myStack, 'opacity', lut.lutsAvailable('opacity'));
opacityUpdate.onChange(function(value) {
lut.lutO = value;
uniformsSecondPass.uTextureLUT.value.dispose();
uniformsSecondPass.uTextureLUT.value = lut.texture;
modified = true;
});
let stepsUpdate = stackFolder.add(myStack, 'steps', 0, 512).step(1);
stepsUpdate.onChange(function(value) {
if (uniformsSecondPass) {
uniformsSecondPass.uSteps.value = value;
modified = true;
}
});
let alphaCorrrectionUpdate = stackFolder.add(myStack, 'alphaCorrection', 0, 1).step(0.01);
alphaCorrrectionUpdate.onChange(function(value) {
if (uniformsSecondPass) {
uniformsSecondPass.uAlphaCorrection.value = value;
modified = true;
}
});
let interpolationUpdate = stackFolder.add(vrHelper, 'interpolation', 0, 1).step(1);
interpolationUpdate.onChange(function(value) {
if (uniformsSecondPass) {
modified = true;
}
});
stackFolder.open();
}
function init() {
// this function is executed on each animation frame
function animate() {
// render
controls.update();
if (ready && modified) {
renderer.render(sceneT, camera, rtTexture, true);
renderer.render(scene, camera);
modified = false;
}
stats.update();
// request new frame
requestAnimationFrame(function() {
animate();
});
}
// renderer
threeD = document.getElementById('r3d');
renderer = new THREE.WebGLRenderer({
alpha: true,
});
renderer.setSize(threeD.offsetWidth, threeD.offsetHeight);
threeD.appendChild(renderer.domElement);
// scene
scene = new THREE.Scene();
sceneT = new THREE.Scene();
// stats
stats = new Stats();
threeD.appendChild(stats.domElement);
// camera
camera = new THREE.PerspectiveCamera(45, threeD.offsetWidth / threeD.offsetHeight, 0.1, 100000);
camera.position.x = 166;
camera.position.y = -471;
camera.position.z = 153;
camera.up.set(-0.42, 0.86, 0.26);
// controls
controls = new ControlsTrackball(camera, threeD);
controls.rotateSpeed = 5.5;
controls.zoomSpeed = 1.2;
controls.panSpeed = 0.8;
controls.staticMoving = true;
controls.dynamicDampingFactor = 0.3;
controls.addEventListener('change', () => {
modified = true;
});
renderer.domElement.addEventListener('mousedown', onStart);
controls.addEventListener('end', onEnd);
window.addEventListener('resize', onWindowResize, false);
renderer.domElement.addEventListener('wheel', onWheel);
// start rendering loop
animate();
}
window.onload = function() {
// init threeJS
init();
let filename = 'https://cdn.rawgit.com/FNNDSC/data/master/nifti/eun_brain/eun_uchar_8.nii.gz';
// load sequence for each file
// instantiate the loader
let loader = new LoadersVolume(threeD);
loader
.load(filename)
.then(() => {
let series = loader.data[0].mergeSeries(loader.data)[0];
loader.free();
loader = null;
// get first stack from series
let stack = series.stack[0];
let stackHelper = new HelpersStack(stack);
vrHelper = new HelpersVR(stack);
// scene
console.log(stackHelper._bBox._meshStack);
// Convenience vars
const dimensions = stack.dimensionsIJK;
const halfDimensions = stack.halfDimensionsIJK;
const offset = new THREE.Vector3(-0.5, -0.5, -0.5);
// Geometry
const geometry = new THREE.BoxGeometry(dimensions.x, dimensions.y, dimensions.z);
geometry.applyMatrix(
new THREE.Matrix4().makeTranslation(
halfDimensions.x + offset.x,
halfDimensions.y + offset.y,
halfDimensions.z + offset.z
)
);
// Material
let material = new THREE.MeshBasicMaterial({
// wireframe: true,
});
material.side = THREE.DoubleSide;
let uniformsFirstPass = {
uWorldBBox: {
type: 'fv1',
value: [0.0, 0.0, 0.0, 0.0, 0.0, 0.0],
},
};
uniformsFirstPass.uWorldBBox.value = stack.worldBoundingBox();
materialFirstPass = new THREE.ShaderMaterial({
uniforms: uniformsFirstPass,
side: THREE.BackSide,
vertexShader: `
varying vec4 vPos;
//
// main
//
void main() {
vPos = modelMatrix * vec4(position, 1.0 );
gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0 );
}
`,
fragmentShader: `
uniform float uWorldBBox[6];
varying vec4 vPos;
void main(void) {
// NORMALIZE LPS VALUES
gl_FragColor = vec4((vPos.x - uWorldBBox[0])/(uWorldBBox[1] - uWorldBBox[0]),
(vPos.y - uWorldBBox[2])/(uWorldBBox[3] - uWorldBBox[2]),
(vPos.z - uWorldBBox[4])/(uWorldBBox[5] - uWorldBBox[4]),
1.0);
}
`,
});
baseMesh = new THREE.Mesh(geometry, materialFirstPass);
baseMesh.applyMatrix(stack.ijk2LPS);
let baseMaterial = new THREE.MeshBasicMaterial({
wireframe: true,
});
let othermesh = new THREE.Mesh(geometry, baseMaterial);
othermesh.applyMatrix(stack.ijk2LPS);
scene.add(othermesh);
rtTexture = new THREE.WebGLRenderTarget(window.innerWidth, window.innerHeight, {
minFilter: THREE.LinearFilter,
magFilter: THREE.NearestFilter,
format: THREE.RGBFormat,
});
sceneT.add(baseMesh);
let _textures = [];
for (let m = 0; m < stack._rawData.length; m++) {
let tex = new THREE.DataTexture(
stack.rawData[m],
stack.textureSize,
stack.textureSize,
stack.textureType,
THREE.UnsignedByteType,
THREE.UVMapping,
THREE.ClampToEdgeWrapping,
THREE.ClampToEdgeWrapping,
THREE.NearestFilter,
THREE.NearestFilter
);
tex.needsUpdate = true;
tex.flipY = true;
_textures.push(tex);
}
uniformsSecondPass = VRUniforms.uniforms();
uniformsSecondPass.uTextureSize.value = stack.textureSize;
uniformsSecondPass.uTextureContainer.value = _textures;
uniformsSecondPass.uWorldToData.value = stack.lps2IJK;
uniformsSecondPass.uNumberOfChannels.value = stack.numberOfChannels;
uniformsSecondPass.uBitsAllocated.value = stack.bitsAllocated;
uniformsSecondPass.uWindowCenterWidth.value = [stack.windowCenter, stack.windowWidth * 0.8];
uniformsSecondPass.uRescaleSlopeIntercept.value = [
stack.rescaleSlope,
stack.rescaleIntercept,
];
uniformsSecondPass.uTextureBack.value = rtTexture.texture;
uniformsSecondPass.uWorldBBox.value = stack.worldBoundingBox();
uniformsSecondPass.uDataDimensions.value = [
stack.dimensionsIJK.x,
stack.dimensionsIJK.y,
stack.dimensionsIJK.z,
];
uniformsSecondPass.uSteps.value = myStack.steps;
console.log(uniformsSecondPass);
// Geometry
const scale = 4;
let newGeometry = new THREE.BoxGeometry(
scale * dimensions.x,
scale * dimensions.y,
scale * dimensions.z
);
newGeometry.applyMatrix(
new THREE.Matrix4().makeTranslation(
halfDimensions.x + offset.x,
halfDimensions.y + offset.y,
halfDimensions.z + offset.z
)
);
// Material
let newMaterial = new THREE.MeshBasicMaterial({
wireframe: true,
});
newMaterial.side = THREE.DoubleSide;
containerMesh = new THREE.Mesh(newGeometry, newMaterial);
containerMesh.applyMatrix(stack.ijk2LPS);
scene.add(containerMesh);
// CREATE LUT
lut = new HelpersLut('my-lut-canvases');
lut.luts = HelpersLut.presetLuts();
lut.lutsO = HelpersLut.presetLutsO();
// update related uniforms
uniformsSecondPass.uTextureLUT.value = lut.texture;
uniformsSecondPass.uLut.value = 1;
uniformsSecondPass.uAlphaCorrection.value = myStack.alphaCorrection;
//
let fs = new VRFragment(uniformsSecondPass);
let vs = new VRVertex();
materialSecondPass = new THREE.ShaderMaterial({
uniforms: uniformsSecondPass,
vertexShader: vs.compute(),
fragmentShader: fs.compute(),
side: THREE.FrontSide,
transparent: true,
});
console.log(VRFragment);
// mesh
boxMeshSecondPass = new THREE.Mesh(geometry, materialSecondPass);
// go the LPS space
boxMeshSecondPass.applyMatrix(stack._ijk2LPS);
scene.add(boxMeshSecondPass);
// update camrea's and interactor's target
let centerLPS = stack.worldCenter();
camera.lookAt(centerLPS.x, centerLPS.y, centerLPS.z);
camera.updateProjectionMatrix();
controls.target.set(centerLPS.x, centerLPS.y, centerLPS.z);
// create GUI
buildGUI();
// screenshot experiment
let screenshotElt = document.getElementById('screenshot');
screenshotElt.addEventListener('click', function() {
controls.update();
if (ready) {
renderer.render(scene, camera);
}
let screenshot = renderer.domElement.toDataURL();
screenshotElt.download = 'AMI-' + Date.now() + '.png';
screenshotElt.href = screenshot;
});
// good to go
ready = true;
modified = true;
})
.catch(error => window.console.log(error));
};