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>

509 lines (405 loc) 13.1 kB
import WidgetsBase from './widgets.base'; import GeometriesVoxel from '../geometries/geometries.voxel'; import ModelsStack from '../models/models.stack'; import ModelsVoxel from '../models/models.voxel'; import CoreIntersections from '../core/core.intersections'; import {Vector2, Vector3} from 'three'; /** * @module widgets/voxelProbe */ export default class WidgetsVoxelProbe extends WidgetsBase { constructor(stack, targetMesh, controls, camera, container) { super(container); this._stack = stack; this._targetMesh = targetMesh; this._controls = controls; this._camera = camera; // if no target mesh, use plane for FREE dragging. this._plane = { position: new Vector3(), direction: new Vector3(), }; this._offset = new Vector3(); this._raycaster = new THREE.Raycaster(); this._tracking = false; this._mouse = new Vector2(); this._lastEvent = null; // world (LPS) position of the center this._worldPosition = new Vector3(); // screen position of the center this._screenPosition = new Vector2(); // mesh stuff this._material = null; this._geometry = null; this._mesh = null; this._meshDisplayed = true; this._meshHovered = false; this._meshStyle = 'sphere'; // cube, etc. // dom stuff this._dom = null; this._domDisplayed = true; this._domHovered = false; this._domStyle = 'circle'; // square, triangle if (this._targetMesh !== null) { this._worldPosition.copy(this._targetMesh.position); } this._screenPosition = this.worldToScreen(this._worldPosition, this._camera, this._container); // create handle this.create(); this.initOffsets(); // event listeners this.onMove = this.onMove.bind(this); this.onHover = this.onHover.bind(this); this.onEndControl = this.onEndControl.bind(this); this.addEventListeners(); } addEventListeners() { this._dom.addEventListener('mouseenter', this.onHover); this._dom.addEventListener('mouseleave', this.onHover); this._container.addEventListener('mousewheel', this.onMove); this._container.addEventListener('DOMMouseScroll', this.onMove); this._controls.addEventListener('end', this.onEndControl); } removeEventListeners() { this._dom.removeEventListener('mouseenter', this.onHover); this._dom.removeEventListener('mouseleave', this.onHover); this._container.removeEventListener('mousewheel', this.onMove); this._container.removeEventListener('DOMMouseScroll', this.onMove); this._controls.removeEventListener('end', this.onEndControl); } onStart(evt) { this._lastEvent = evt; evt.preventDefault(); const offsets = this.getMouseOffsets(evt, this._container); this._mouse.set(offsets.x, offsets.y); // update raycaster this._raycaster.setFromCamera(this._mouse, this._camera); this._raycaster.ray.position = this._raycaster.ray.origin; if (this._hovered) { this._active = true; this._controls.enabled = false; if (this._targetMesh) { let intersectsTarget = this._raycaster.intersectObject(this._targetMesh); if (intersectsTarget.length > 0) { this._offset.copy(intersectsTarget[0].point).sub(this._worldPosition); } } else { this._plane.position.copy(this._worldPosition); this._plane.direction.copy(this._camera.getWorldDirection()); let intersection = CoreIntersections.rayPlane(this._raycaster.ray, this._plane); if (intersection !== null) { this._offset.copy(intersection).sub(this._plane.position); } } this.update(); } } onEnd(evt) { this._lastEvent = evt; evt.preventDefault(); // stay active and keep controls disabled if (this._tracking === true) { return; } // unselect if go up without moving if (!this._dragged && this._active) { // change state if was not dragging this._selected = !this._selected; } this._active = false; this._dragged = false; this._controls.enabled = true; this.update(); } onEndControl() { if (!this._lastEvent) { return; } window.requestAnimationFrame(() => { this.onMove(this._lastEvent); }); } onMove(evt) { this._lastEvent = evt; evt.preventDefault(); const offsets = this.getMouseOffsets(evt, this._container); this._mouse.set(offsets.x, offsets.y); // update raycaster // set ray.position to satisfy CoreIntersections::rayPlane API this._raycaster.setFromCamera(this._mouse, this._camera); this._raycaster.ray.position = this._raycaster.ray.origin; if (this._active) { this._dragged = true; if (this._targetMesh !== null) { let intersectsTarget = this._raycaster.intersectObject(this._targetMesh); if (intersectsTarget.length > 0) { this._worldPosition.copy(intersectsTarget[0].point.sub(this._offset)); } } else { if (this._plane.direction.length() === 0) { // free mode!this._targetMesh this._plane.position.copy(this._worldPosition); this._plane.direction.copy(this._camera.getWorldDirection()); } let intersection = CoreIntersections.rayPlane(this._raycaster.ray, this._plane); if (intersection !== null) { this._worldPosition.copy(intersection.sub(this._offset)); } } } else { this.onHover(null); } this.update(); } onHover(evt) { if (evt) { this._lastEvent = evt; evt.preventDefault(); this.hoverDom(evt); } this.hoverMesh(); this._hovered = this._meshHovered || this._domHovered; this._container.style.cursor = this._hovered ? 'pointer' : 'default'; } hoverMesh() { // check raycast intersection, do we want to hover on mesh or just css? let intersectsHandle = this._raycaster.intersectObject(this._mesh); this._meshHovered = (intersectsHandle.length > 0); } hoverDom(evt) { this._domHovered = (evt.type === 'mouseenter'); } worldToScreen(worldCoordinate, camera, canvas) { let screenCoordinates = worldCoordinate.clone(); screenCoordinates.project(camera); screenCoordinates.x = Math.round((screenCoordinates.x + 1) * canvas.offsetWidth / 2); screenCoordinates.y = Math.round((-screenCoordinates.y + 1) * canvas.offsetHeight / 2); screenCoordinates.z = 0; return screenCoordinates; } create() { this.createVoxel(); this.createMesh(); this.createDOM(); } createVoxel() { this._voxel = new ModelsVoxel(); this._voxel.id = this.id; this._voxel.worldCoordinates = this._worldCoordinates; } createMesh() { const dataCoordinates = ModelsStack.worldToData( this._stack, this._worldPosition); this._geometry = new GeometriesVoxel(dataCoordinates); this._material = new THREE.MeshBasicMaterial({ wireframe: true, wireframeLinewidth: 1, }); this._mesh = new THREE.Mesh(this._geometry, this._material); this._mesh.applyMatrix(this._stack.ijk2LPS); this._mesh.visible = true; this.updateMeshColor(); this.add(this._mesh); } updateMeshColor() { if (this._material) { this._material.color.set(this._color); } } createDOM() { // dom this._dom = document.createElement('div'); this._dom.setAttribute('id', this.uuid); this._dom.setAttribute('class', 'AMI Widget VoxelProbe'); this._dom.style.border = '2px solid #000'; this._dom.style.backgroundColor = 'rgb(249, 249, 249)'; this._dom.style.color = '#212121'; this._dom.style.position = 'absolute'; this._dom.style.transformOrigin = '0px 100% 0px'; // measurenents let measurementsContainer = document.createElement('div'); // LPS let lpsContainer = document.createElement('div'); lpsContainer.setAttribute('id', 'lpsPosition'); measurementsContainer.appendChild(lpsContainer); // IJK let ijkContainer = document.createElement('div'); ijkContainer.setAttribute('id', 'ijkPosition'); measurementsContainer.appendChild(ijkContainer); // Value let valueContainer = document.createElement('div'); valueContainer.setAttribute('id', 'value'); measurementsContainer.appendChild(valueContainer); this.updateDOMColor(); this._dom.appendChild(measurementsContainer); // add it! this._container.appendChild(this._dom); } updateDOMContent() { const rasContainer = this._dom.querySelector('#lpsPosition'); rasContainer.innerHTML = `LPS: ${this._voxel.worldCoordinates.x.toFixed(2)} : ${this._voxel.worldCoordinates.y.toFixed(2)} : ${this._voxel.worldCoordinates.z.toFixed(2)}`; const ijkContainer = this._dom.querySelector('#ijkPosition'); ijkContainer.innerHTML = `IJK: ${this._voxel.dataCoordinates.x} : ${this._voxel.dataCoordinates.y} : ${this._voxel.dataCoordinates.z}`; const valueContainer = this._dom.querySelector('#value'); valueContainer.innerHTML = `Value: ${this._voxel.value}`; } update() { // general update this.updateColor(); this._screenPosition = this.worldToScreen(this._worldPosition, this._camera, this._container); // set data coordinates && value this.updateVoxel(this._worldPosition); // update mesh position this.updateMeshColor(); if (this._mesh && this._mesh.geometry) { this._mesh.geometry.location = this._voxel.dataCoordinates; this._mesh.updateMatrix(); } // update dom this.updateDOMContent(); this.updateDOMColor(); this.updateDOMPosition(); } updateVoxel(worldCoordinates) { // update world coordinates this._voxel.worldCoordinates = worldCoordinates; // update data coordinates this._voxel.dataCoordinates = ModelsStack.worldToData( this._stack, this._voxel.worldCoordinates); // update value let value = ModelsStack.value( this._stack, this._voxel.dataCoordinates); this._voxel.value = ModelsStack.valueRescaleSlopeIntercept( value, this._stack.rescaleSlope, this._stack.rescaleIntercept); } updateDOMPosition() { if (this._dom) { let posY = this._screenPosition.y - this._container.offsetHeight; this._dom.style.transform = `translate3D(${this._screenPosition.x}px, ${posY}px, 0)`; } } updateDOMColor() { this._dom.style.borderColor = `${this._color}`; } free() { this._container. removeEventListener('mouseup', this.onMouseUpHandler, false); this._container. removeEventListener('mousemove', this.onMouseMoveHandler, false); this._container. removeEventListener('mousewheel', this.onMouseMoveHandler, false); this._container. removeEventListener('DOMMouseScroll', this.onMouseMoveHandler, false); this._voxel.removeTest(); this.remove(this._voxel); this._voxel = null; super.free(); } hoverVoxel(mouseScreenCoordinates, currentDataCoordinates) { // update distance mouse/this._voxel let dx = mouseScreenCoordinates.screenX - this._voxel.voxel.screenCoordinates.x; let dy = mouseScreenCoordinates.screenY - this._voxel.voxel.screenCoordinates.y; let distance = Math.sqrt(dx * dx + dy * dy); this._voxel.distance = distance; if (distance >= 0 && distance < 10) { this._hover = true; } else { this._hover = false; } } set worldPosition(worldPosition) { this._worldPosition.copy(worldPosition); this.update(); } set defaultColor(defaultColor) { this._defaultColor = defaultColor; this.update(); } get defaultColor() { return this._defaultColor; } set activeColor(activeColor) { this._activeColor = activeColor; this.update(); } get activeColor() { return this._activeColor; } set hoverColor(hoverColor) { this._hoverColor = hoverColor; this.update(); } get hoverColor() { return this._hoverColor; } set selectedColor(selectedColor) { this._selectedColor = selectedColor; this.update(); } get selectedColor() { return this._selectedColor; } set showVoxel(showVoxel) { this._showVoxel = showVoxel; this.update(); } get showVoxel() { return this._showVoxel; } set showDomSVG(showDomSVG) { this._showDomSVG = showDomSVG; this.update(); } get showDomSVG() { return this._showDomSVG; } set showDomMeasurements(showDomMeasurements) { this._showDomMeasurements = showDomMeasurements; this.update(); } get showDomMeasurements() { return this._showDomMeasurements; } hideDOM() { this._dom.style.display = 'none'; } showDOM() { this._dom.style.display = ''; } hideMesh() { this.visible = false; } showMesh() { this.visible = true; } show() { this.showDOM(); this.showMesh(); } hide() { this.hideDOM(); this.hideMesh(); } }