2016-02-12 10:41:41 +00:00
|
|
|
import EventEmitter from 'eventemitter3';
|
|
|
|
import THREE from 'three';
|
2016-02-19 09:48:43 +00:00
|
|
|
import OrbitControls from '../vendor/OrbitControls';
|
2016-03-15 19:50:04 +00:00
|
|
|
import TweenLite from 'TweenLite';
|
|
|
|
|
|
|
|
// Prevent animation from pausing when tab is inactive
|
|
|
|
TweenLite.lagSmoothing(0);
|
2016-02-12 10:41:41 +00:00
|
|
|
|
|
|
|
class Orbit extends EventEmitter {
|
|
|
|
constructor() {
|
|
|
|
super();
|
|
|
|
}
|
|
|
|
|
2016-02-12 19:23:53 +00:00
|
|
|
// Proxy control events
|
2016-02-12 22:28:31 +00:00
|
|
|
//
|
|
|
|
// There's currently no distinction between pan, orbit and zoom events
|
2016-02-12 19:23:53 +00:00
|
|
|
_initEvents() {
|
|
|
|
this._controls.addEventListener('start', (event) => {
|
2016-02-19 09:48:43 +00:00
|
|
|
this._world.emit('controlsMoveStart', event.target.target);
|
2016-02-12 19:23:53 +00:00
|
|
|
});
|
|
|
|
|
|
|
|
this._controls.addEventListener('change', (event) => {
|
2016-02-19 09:48:43 +00:00
|
|
|
this._world.emit('controlsMove', event.target.target);
|
2016-02-12 19:23:53 +00:00
|
|
|
});
|
|
|
|
|
|
|
|
this._controls.addEventListener('end', (event) => {
|
2016-02-19 09:48:43 +00:00
|
|
|
this._world.emit('controlsMoveEnd', event.target.target);
|
2016-02-12 19:23:53 +00:00
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2016-02-12 10:41:41 +00:00
|
|
|
// Moving the camera along the [x,y,z] axis based on a target position
|
2016-03-15 19:50:04 +00:00
|
|
|
panTo(point, animate) {}
|
|
|
|
panBy(pointDelta, animate) {}
|
2016-02-12 10:41:41 +00:00
|
|
|
|
|
|
|
// Zooming the camera in and out
|
2016-03-15 19:50:04 +00:00
|
|
|
zoomTo(metres, animate) {}
|
|
|
|
zoomBy(metresDelta, animate) {}
|
2016-02-12 10:41:41 +00:00
|
|
|
|
|
|
|
// Force camera to look at something other than the target
|
2016-03-15 19:50:04 +00:00
|
|
|
lookAt(point, animate) {}
|
2016-02-12 10:41:41 +00:00
|
|
|
|
|
|
|
// Make camera look at the target
|
2016-03-15 19:50:04 +00:00
|
|
|
lookAtTarget() {}
|
2016-02-12 10:41:41 +00:00
|
|
|
|
|
|
|
// Tilt (up and down)
|
2016-03-15 19:50:04 +00:00
|
|
|
tiltTo(angle, animate) {}
|
|
|
|
tiltBy(angleDelta, animate) {}
|
2016-02-12 10:41:41 +00:00
|
|
|
|
|
|
|
// Rotate (left and right)
|
2016-03-15 19:50:04 +00:00
|
|
|
rotateTo(angle, animate) {}
|
|
|
|
rotateBy(angleDelta, animate) {}
|
2016-02-12 10:41:41 +00:00
|
|
|
|
|
|
|
// Fly to the given point, animating pan and tilt/rotation to final position
|
|
|
|
// with nice zoom out and in
|
|
|
|
//
|
2016-03-15 19:50:04 +00:00
|
|
|
// TODO: Calling flyTo a second time before the previous animation has
|
|
|
|
// completed should immediately start the new animation from wherever the
|
|
|
|
// previous one has got to
|
|
|
|
//
|
|
|
|
// TODO: Long-distance pans should prevent the quadtree grid from trying to
|
|
|
|
// update by not firing the control update events every frame until the
|
|
|
|
// pan velocity calms down a bit
|
|
|
|
flyToPoint(point, duration, noZoom) {
|
|
|
|
// Animation time in seconds
|
|
|
|
var animationTime = duration || 3;
|
|
|
|
|
|
|
|
this._flyTarget = new THREE.Vector3(point.x, 0, point.y);
|
|
|
|
|
|
|
|
// Calculate delta from current position to fly target
|
|
|
|
var diff = new THREE.Vector3().subVectors(this._controls.target, this._flyTarget);
|
|
|
|
|
|
|
|
this._flyTween = new TweenLite(
|
|
|
|
{
|
|
|
|
x: 0,
|
|
|
|
z: 0,
|
|
|
|
prev: {
|
|
|
|
x: 0,
|
|
|
|
z: 0
|
|
|
|
}
|
|
|
|
},
|
|
|
|
animationTime,
|
|
|
|
{
|
|
|
|
x: diff.x,
|
|
|
|
z: diff.z,
|
|
|
|
onUpdate: function(tween) {
|
|
|
|
var controls = this._controls;
|
|
|
|
|
|
|
|
// Work out difference since last frame
|
|
|
|
var deltaX = tween.target.x - tween.target.prev.x;
|
|
|
|
var deltaZ = tween.target.z - tween.target.prev.z;
|
|
|
|
|
|
|
|
// Move some fraction toward the target point
|
|
|
|
controls.panLeft(deltaX, controls.object.matrix);
|
|
|
|
controls.panUp(deltaZ, controls.object.matrix);
|
|
|
|
|
|
|
|
tween.target.prev.x = tween.target.x;
|
|
|
|
tween.target.prev.z = tween.target.z;
|
|
|
|
|
|
|
|
// TODO: Get zoom to dolly in and out on pan
|
|
|
|
// controls.object.zoom += Math.sin(Math.PI / tween.target.zoom);
|
|
|
|
// controls.object.updateProjectionMatrix();
|
|
|
|
},
|
|
|
|
onComplete: function(tween) {
|
|
|
|
console.log(`Arrived at flyTarget`);
|
|
|
|
this._flyTarget = null;
|
|
|
|
},
|
|
|
|
onUpdateParams: ['{self}'],
|
|
|
|
onCompleteParams: ['{self}'],
|
|
|
|
callbackScope: this,
|
|
|
|
ease: Power1.easeInOut
|
|
|
|
}
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
flyToLatLon(latlon, duration, noZoom) {
|
|
|
|
var point = this._world.latLonToPoint(latlon);
|
|
|
|
this.flyToPoint(point, duration, noZoom);
|
|
|
|
}
|
|
|
|
|
|
|
|
// TODO: Make this animate over a user-defined period of time
|
|
|
|
//
|
|
|
|
// Perhaps use TweenMax for now and implement as a more lightweight solution
|
|
|
|
// later on once it all works
|
|
|
|
// _animateFlyTo(delta) {
|
|
|
|
// var controls = this._controls;
|
|
|
|
//
|
|
|
|
// // this._controls.panLeft(50, controls._controls.object.matrix);
|
|
|
|
// // this._controls.panUp(50, controls._controls.object.matrix);
|
|
|
|
// // this._controls.dollyIn(this._controls.getZoomScale());
|
|
|
|
// // this._controls.dollyOut(this._controls.getZoomScale());
|
|
|
|
//
|
|
|
|
// // Calculate delta from current position to fly target
|
|
|
|
// var diff = new THREE.Vector3().subVectors(this._controls.target, this._flyTarget);
|
|
|
|
//
|
|
|
|
// // 1000 units per second
|
|
|
|
// var speed = 1000 * (delta / 1000);
|
|
|
|
//
|
|
|
|
// // Remove fly target after arrival and snap to target
|
|
|
|
// if (diff.length() < 0.01) {
|
|
|
|
// console.log(`Arrived at flyTarget`);
|
|
|
|
// this._flyTarget = null;
|
|
|
|
// speed = 1;
|
|
|
|
// }
|
|
|
|
//
|
|
|
|
// // Move some fraction toward the target point
|
|
|
|
// controls.panLeft(diff.x * speed, controls.object.matrix);
|
|
|
|
// controls.panUp(diff.z * speed, controls.object.matrix);
|
|
|
|
// }
|
2016-02-12 10:41:41 +00:00
|
|
|
|
2016-02-12 19:23:53 +00:00
|
|
|
// Proxy to OrbitControls.update()
|
2016-03-15 19:50:04 +00:00
|
|
|
update(delta) {
|
|
|
|
this._controls.update(delta);
|
2016-02-12 19:23:53 +00:00
|
|
|
}
|
|
|
|
|
2016-02-12 10:41:41 +00:00
|
|
|
// Add controls to world instance and store world reference
|
|
|
|
addTo(world) {
|
|
|
|
world.addControls(this);
|
|
|
|
return this;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Internal method called by World.addControls to actually add the controls
|
|
|
|
_addToWorld(world) {
|
|
|
|
this._world = world;
|
|
|
|
|
|
|
|
// TODO: Override panLeft and panUp methods to prevent panning on Y axis
|
|
|
|
// See: http://stackoverflow.com/a/26188674/997339
|
2016-02-19 09:48:43 +00:00
|
|
|
this._controls = new OrbitControls(world._engine._camera, world._container);
|
2016-02-12 10:41:41 +00:00
|
|
|
|
2016-02-13 13:56:58 +00:00
|
|
|
// Disable keys for now as no events are fired for them anyway
|
|
|
|
this._controls.keys = false;
|
|
|
|
|
|
|
|
// 89 degrees
|
|
|
|
this._controls.maxPolarAngle = 1.5533;
|
2016-02-12 21:51:41 +00:00
|
|
|
|
2016-02-12 22:28:31 +00:00
|
|
|
// this._controls.enableDamping = true;
|
|
|
|
// this._controls.dampingFactor = 0.25;
|
2016-02-12 21:51:41 +00:00
|
|
|
|
2016-02-12 19:23:53 +00:00
|
|
|
this._initEvents();
|
2016-02-12 10:41:41 +00:00
|
|
|
|
2016-02-12 19:23:53 +00:00
|
|
|
this.emit('added');
|
2016-02-12 10:41:41 +00:00
|
|
|
}
|
2016-02-29 09:17:22 +00:00
|
|
|
|
|
|
|
// Destroys the controls and removes them from memory
|
|
|
|
destroy() {
|
|
|
|
// TODO: Remove event listeners
|
|
|
|
|
|
|
|
this._controls.dispose();
|
|
|
|
|
|
|
|
this._world = null;
|
|
|
|
this._controls = null;
|
|
|
|
}
|
2016-02-12 10:41:41 +00:00
|
|
|
}
|
|
|
|
|
2016-03-01 20:34:53 +00:00
|
|
|
export default Orbit;
|
|
|
|
|
|
|
|
var noNew = function() {
|
2016-02-12 10:41:41 +00:00
|
|
|
return new Orbit();
|
|
|
|
};
|
2016-03-01 20:34:53 +00:00
|
|
|
|
|
|
|
// Initialise without requiring new keyword
|
|
|
|
export {noNew as orbit};
|