UNPKG

ami-cjs.js

Version:

<p align="center"> <img src="https://cloud.githubusercontent.com/assets/214063/23213764/78ade038-f90c-11e6-8208-4fcade5f3832.png" width="60%"> </p>

403 lines (315 loc) 10.7 kB
import WidgetsBase from '../widgets/widgets.base'; import CoreIntersections from '../core/core.intersections'; /** * @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 THREE.Vector3(), direction: new THREE.Vector3(), }; this._offset = new THREE.Vector3(); this._raycaster = new THREE.Raycaster(); this._tracking = false; this._mouse = new THREE.Vector2(); // world (LPS) position of this handle this._worldPosition = new THREE.Vector3(); // screen position of this handle this._screenPosition = new THREE.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.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); } 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); } create() { this.createMesh(); this.createDOM(); } onStart(evt) { evt.preventDefault(); var 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._mesh.position); } } else{ // update raycaster let intersection = CoreIntersections.rayPlane(this._raycaster.ray, this._plane); if(intersection !== null) { this._offset.copy(intersection).sub(this._plane.position); } } this.update(); } } onEnd(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(); } /** * * */ onMove(evt) { evt.preventDefault(); var offsets = this.getMouseOffsets(evt, this._container); this._mouse.set(offsets.x, offsets.y); // update screen position of handle this._screenPosition = this.worldToScreen(this._worldPosition, this._camera, this._container); // 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); if(this._targetMesh === null) { // free mode!this._targetMesh this._plane.position.copy(this._worldPosition); this._plane.direction.copy(this._camera.getWorldDirection()); } } this.update(); } onHover(evt) { if(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(); // 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(2, 32, 32); // 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', 'widgets handle'); // this._domStyles.circle(); // this._domStyles.cross(); 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() { // threejs stuff // dom this._container.removeChild(this._dom); // event this.removeEventListeners(); super.free(); } set worldPosition(worldPosition) { this._worldPosition.copy(worldPosition); this._screenPosition = this.worldToScreen(this._worldPosition, this._camera, this._container); 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(); } } // maybe just a string... // this._domStyles = { // circle: function(){ // this._dom.style.border = '2px solid #353535'; // this._dom.style.backgroundColor = '#F9F9F9'; // // this._dom.style.backgroundColor = 'rgba(230, 230, 230, 0.7)'; // 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%'; // }, // cross: function(){ // }, // triangle: `` // }; // <svg height="12" width="12"> // <circle cx="6" cy="6" r="5" stroke="#353535" stroke-opacity="0.9" stroke-width="2" fill="#F9F9F9" fill-opacity="0.7" /> // Sorry, your browser does not support inline SVG. // </svg> // <svg height="12" width="12"> // <line x1="0" y1="0" x2="12" y2="12" stroke="#353535" stroke-linecap="square" stroke-width="2" /> // <line x1="0" y1="12" x2="12" y2="0" stroke="#353535" stroke-linecap="square" stroke-width="2" /> // </svg> // <svg height="12" width="12"> // <line x1="0" y1="12" x2="6" y2="6" stroke="#353535" stroke-linecap="square" stroke-width="2" /> // <line x1="6" y1="6" x2="12" y2="12" stroke="#353535" stroke-linecap="square" stroke-width="2" /> // </svg> //