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
JavaScript
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();
}
}