{"size": 13139, "ext": "js", "max_stars_repo_path": "src/widgets/widgets.voxelProbe.js", "max_stars_repo_name": "tymiao1220/ami", "max_stars_repo_stars_event_min_datetime": "2020-12-10T12:16:37.000Z", "max_stars_repo_stars_event_max_datetime": "2020-12-10T12:16:37.000Z", "max_issues_repo_path": "src/widgets/widgets.voxelProbe.js", "max_issues_repo_name": "tymiao1220/ami", "max_issues_repo_issues_event_min_datetime": null, "max_issues_repo_issues_event_max_datetime": null, "max_forks_repo_path": "src/widgets/widgets.voxelProbe.js", "max_forks_repo_name": "tymiao1220/ami", "max_forks_repo_forks_event_min_datetime": "2020-01-09T14:51:09.000Z", "max_forks_repo_forks_event_max_datetime": "2020-11-21T17:41:36.000Z"}
{"max_stars_count": 1, "max_issues_count": null, "max_forks_count": 2, "avg_line_length": 25.8133595285, "max_line_length": 80, "alphanum_fraction": 0.6585737119}

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