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>

410 lines (323 loc) 9.93 kB
import WidgetsBase from './widgets.base'; import CoreIntersections from '../core/core.intersections'; import {Vector2, Vector3} from 'three'; /** * @module widgets/handle * */ export default class WidgetsHandle extends WidgetsBase { constructor(targetMesh, controls, camera, container) { super(container); 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 this handle this._worldPosition = new Vector3(); // screen position of this handle 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); } create() { this.createMesh(); this.createDOM(); } 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'; } update() { // general update this.updateColor(); // update screen position of handle this._screenPosition = this.worldToScreen(this._worldPosition, this._camera, this._container); // mesh stuff this.updateMeshColor(); this.updateMeshPosition(); // DOM stuff this.updateDOMColor(); this.updateDOMPosition(); } // updateMeshColor() { if (this._material) { this._material.color.set(this._color); } } updateMeshPosition() { if (this._mesh) { this._mesh.position.x = this._worldPosition.x; this._mesh.position.y = this._worldPosition.y; this._mesh.position.z = this._worldPosition.z; } } 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; } createMesh() { // geometry this._geometry = new THREE.SphereGeometry(1, 16, 16); // material this._material = new THREE.MeshBasicMaterial({ wireframe: true, wireframeLinewidth: 2, }); // mesh this._mesh = new THREE.Mesh(this._geometry, this._material); this._mesh.position.x = this._worldPosition.x; this._mesh.position.y = this._worldPosition.y; this._mesh.position.z = this._worldPosition.z; this._mesh.visible = true; this.updateMeshColor(); // add it! this.add(this._mesh); } createDOM() { // dom this._dom = document.createElement('div'); this._dom.setAttribute('id', this.uuid); this._dom.setAttribute('class', 'AMI Widget Handle'); this._dom.style.border = '2px solid'; this._dom.style.backgroundColor = '#F9F9F9'; this._dom.style.color = '#F9F9F9'; this._dom.style.position = 'absolute'; this._dom.style.width = '12px'; this._dom.style.height = '12px'; this._dom.style.margin = '-6px'; this._dom.style.borderRadius = '50%'; this._dom.style.transformOrigin = '0 100%'; let posY = this._screenPosition.y - this._container.offsetHeight; this._dom.style.transform = `translate3D(${this._screenPosition.x}px, ${posY}px, 0)`; this.updateDOMColor(); // add it! this._container.appendChild(this._dom); } 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() { // dom this._container.removeChild(this._dom); // event this.removeEventListeners(); super.free(); } set worldPosition(worldPosition) { this._worldPosition.copy(worldPosition); this.update(); } get worldPosition() { return this._worldPosition; } set screenPosition(screenPosition) { this._screenPosition = screenPosition; } get screenPosition() { return this._screenPosition; } get active() { return this._active; } set active(active) { this._active = active; // this._tracking = this._active; this._controls.enabled = !this._active; this.update(); } get tracking() { return this._tracking; } set tracking(tracking) { this._tracking = tracking; this.update(); } 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(); } }