From 350a181265cbb44d2dcb6ea60b72f02adc4fb0e0 Mon Sep 17 00:00:00 2001 From: Amanda Ghassaei <amandaghassaei@gmail.com> Date: Wed, 21 Jan 2015 19:22:47 -0500 Subject: [PATCH] z axis is up --- dependencies/OrbitControls.js | 262 +++++++++++++++++++++++----------- js/models/lattice.js | 5 + js/models/threeModel.js | 2 + js/threeViews/latticeView.js | 16 +-- 4 files changed, 192 insertions(+), 93 deletions(-) diff --git a/dependencies/OrbitControls.js b/dependencies/OrbitControls.js index 936ffacb..4af88f78 100644 --- a/dependencies/OrbitControls.js +++ b/dependencies/OrbitControls.js @@ -34,6 +34,7 @@ THREE.OrbitControls = function ( object, domElement ) { // "target" sets the location of focus, where the control orbits around // and where it pans with respect to. this.target = new THREE.Vector3(); + // center is old, deprecated; use "target" instead this.center = this.target; @@ -41,6 +42,7 @@ THREE.OrbitControls = function ( object, domElement ) { // backwards compatibility this.noZoom = false; this.zoomSpeed = 1.0; + // Limits to how far you can dolly in and out this.minDistance = 0; this.maxDistance = Infinity; @@ -62,11 +64,20 @@ THREE.OrbitControls = function ( object, domElement ) { this.minPolarAngle = 0; // radians this.maxPolarAngle = Math.PI; // radians + // How far you can orbit horizontally, upper and lower limits. + // If set, must be a sub-interval of the interval [ - Math.PI, Math.PI ]. + this.minAzimuthAngle = - Infinity; // radians + this.maxAzimuthAngle = Infinity; // radians + // Set to true to disable use of the keys this.noKeys = false; + // The four arrow keys this.keys = { LEFT: 37, UP: 38, RIGHT: 39, BOTTOM: 40 }; + // Mouse buttons + this.mouseButtons = { ORBIT: THREE.MOUSE.LEFT, ZOOM: THREE.MOUSE.MIDDLE, PAN: THREE.MOUSE.RIGHT }; + //////////// // internals @@ -81,25 +92,43 @@ THREE.OrbitControls = function ( object, domElement ) { var panStart = new THREE.Vector2(); var panEnd = new THREE.Vector2(); var panDelta = new THREE.Vector2(); + var panOffset = new THREE.Vector3(); + + var offset = new THREE.Vector3(); var dollyStart = new THREE.Vector2(); var dollyEnd = new THREE.Vector2(); var dollyDelta = new THREE.Vector2(); + var theta; + var phi; var phiDelta = 0; var thetaDelta = 0; var scale = 1; var pan = new THREE.Vector3(); var lastPosition = new THREE.Vector3(); + var lastQuaternion = new THREE.Quaternion(); var STATE = { NONE : -1, ROTATE : 0, DOLLY : 1, PAN : 2, TOUCH_ROTATE : 3, TOUCH_DOLLY : 4, TOUCH_PAN : 5 }; + var state = STATE.NONE; + // for reset + + this.target0 = this.target.clone(); + this.position0 = this.object.position.clone(); + + // so camera.up is the orbit axis + + var quat = new THREE.Quaternion().setFromUnitVectors( object.up, new THREE.Vector3( 0, 1, 0 ) ); + var quatInverse = quat.clone().inverse(); + // events var changeEvent = { type: 'change' }; - + var startEvent = { type: 'start'}; + var endEvent = { type: 'end'}; this.rotateLeft = function ( angle ) { @@ -128,12 +157,12 @@ THREE.OrbitControls = function ( object, domElement ) { // pass in distance in world space to move left this.panLeft = function ( distance ) { - var panOffset = new THREE.Vector3(); var te = this.object.matrix.elements; + // get X column of matrix - panOffset.set( te[0], te[1], te[2] ); - panOffset.multiplyScalar(-distance); - + panOffset.set( te[ 0 ], te[ 1 ], te[ 2 ] ); + panOffset.multiplyScalar( - distance ); + pan.add( panOffset ); }; @@ -141,18 +170,19 @@ THREE.OrbitControls = function ( object, domElement ) { // pass in distance in world space to move up this.panUp = function ( distance ) { - var panOffset = new THREE.Vector3(); var te = this.object.matrix.elements; + // get Y column of matrix - panOffset.set( te[4], te[5], te[6] ); - panOffset.multiplyScalar(distance); - + panOffset.set( te[ 4 ], te[ 5 ], te[ 6 ] ); + panOffset.multiplyScalar( distance ); + pan.add( panOffset ); + }; - - // main entry point; pass in Vector2 of change desired in pixel space, + + // pass in x,y of change desired in pixel space, // right and down are positive - this.pan = function ( delta ) { + this.pan = function ( deltaX, deltaY ) { var element = scope.domElement === document ? scope.domElement.body : scope.domElement; @@ -164,20 +194,21 @@ THREE.OrbitControls = function ( object, domElement ) { var targetDistance = offset.length(); // half of the fov is center to top of screen - targetDistance *= Math.tan( (scope.object.fov/2) * Math.PI / 180.0 ); + targetDistance *= Math.tan( ( scope.object.fov / 2 ) * Math.PI / 180.0 ); + // we actually don't use screenWidth, since perspective camera is fixed to screen height - scope.panLeft( 2 * delta.x * targetDistance / element.clientHeight ); - scope.panUp( 2 * delta.y * targetDistance / element.clientHeight ); + scope.panLeft( 2 * deltaX * targetDistance / element.clientHeight ); + scope.panUp( 2 * deltaY * targetDistance / element.clientHeight ); } else if ( scope.object.top !== undefined ) { // orthographic - scope.panLeft( delta.x * (scope.object.right - scope.object.left) / element.clientWidth ); - scope.panUp( delta.y * (scope.object.top - scope.object.bottom) / element.clientHeight ); + scope.panLeft( deltaX * (scope.object.right - scope.object.left) / element.clientWidth ); + scope.panUp( deltaY * (scope.object.top - scope.object.bottom) / element.clientHeight ); } else { - // camera neither orthographic or perspective - warn user + // camera neither orthographic or perspective console.warn( 'WARNING: OrbitControls.js encountered an unknown camera type - pan disabled.' ); } @@ -211,17 +242,21 @@ THREE.OrbitControls = function ( object, domElement ) { this.update = function () { var position = this.object.position; - var offset = position.clone().sub( this.target ); + + offset.copy( position ).sub( this.target ); + + // rotate offset to "y-axis-is-up" space + offset.applyQuaternion( quat ); // angle from z-axis around y-axis - var theta = Math.atan2( offset.x, offset.z ); + theta = Math.atan2( offset.x, offset.z ); // angle from y-axis - var phi = Math.atan2( Math.sqrt( offset.x * offset.x + offset.z * offset.z ), offset.y ); + phi = Math.atan2( Math.sqrt( offset.x * offset.x + offset.z * offset.z ), offset.y ); - if ( this.autoRotate ) { + if ( this.autoRotate && state === STATE.NONE ) { this.rotateLeft( getAutoRotationAngle() ); @@ -230,6 +265,9 @@ THREE.OrbitControls = function ( object, domElement ) { theta += thetaDelta; phi += phiDelta; + // restrict theta to be between desired limits + theta = Math.max( this.minAzimuthAngle, Math.min( this.maxAzimuthAngle, theta ) ); + // restrict phi to be between desired limits phi = Math.max( this.minPolarAngle, Math.min( this.maxPolarAngle, phi ) ); @@ -240,7 +278,7 @@ THREE.OrbitControls = function ( object, domElement ) { // restrict radius to be between desired limits radius = Math.max( this.minDistance, Math.min( this.maxDistance, radius ) ); - + // move target to panned location this.target.add( pan ); @@ -248,6 +286,9 @@ THREE.OrbitControls = function ( object, domElement ) { offset.y = radius * Math.cos( phi ); offset.z = radius * Math.sin( phi ) * Math.cos( theta ); + // rotate offset back to "camera-up-vector-is-up" space + offset.applyQuaternion( quatInverse ); + position.copy( this.target ).add( offset ); this.object.lookAt( this.target ); @@ -255,19 +296,48 @@ THREE.OrbitControls = function ( object, domElement ) { thetaDelta = 0; phiDelta = 0; scale = 1; - pan.set(0,0,0); + pan.set( 0, 0, 0 ); - if ( lastPosition.distanceTo( this.object.position ) > 0 ) { + // update condition is: + // min(camera displacement, camera rotation in radians)^2 > EPS + // using small-angle approximation cos(x/2) = 1 - x^2 / 8 + + if ( lastPosition.distanceToSquared( this.object.position ) > EPS + || 8 * (1 - lastQuaternion.dot(this.object.quaternion)) > EPS ) { this.dispatchEvent( changeEvent ); lastPosition.copy( this.object.position ); + lastQuaternion.copy (this.object.quaternion ); } }; + this.reset = function () { + + state = STATE.NONE; + + this.target.copy( this.target0 ); + this.object.position.copy( this.position0 ); + + this.update(); + + }; + + this.getPolarAngle = function () { + + return phi; + + }; + + this.getAzimuthalAngle = function () { + + return theta + + }; + function getAutoRotationAngle() { return 2 * Math.PI / 60 / 60 * scope.autoRotateSpeed; @@ -282,25 +352,25 @@ THREE.OrbitControls = function ( object, domElement ) { function onMouseDown( event ) { - if ( scope.enabled === false ) { return; } + if ( scope.enabled === false ) return; event.preventDefault(); - if ( event.button === 0 ) { - if ( scope.noRotate === true ) { return; } + if ( event.button === scope.mouseButtons.ORBIT ) { + if ( scope.noRotate === true ) return; state = STATE.ROTATE; rotateStart.set( event.clientX, event.clientY ); - } else if ( event.button === 1 ) { - if ( scope.noZoom === true ) { return; } + } else if ( event.button === scope.mouseButtons.ZOOM ) { + if ( scope.noZoom === true ) return; state = STATE.DOLLY; dollyStart.set( event.clientX, event.clientY ); - } else if ( event.button === 2 ) { - if ( scope.noPan === true ) { return; } + } else if ( event.button === scope.mouseButtons.PAN ) { + if ( scope.noPan === true ) return; state = STATE.PAN; @@ -308,9 +378,11 @@ THREE.OrbitControls = function ( object, domElement ) { } - // Greggman fix: https://github.com/greggman/three.js/commit/fde9f9917d6d8381f06bf22cdff766029d1761be - scope.domElement.addEventListener( 'mousemove', onMouseMove, false ); - scope.domElement.addEventListener( 'mouseup', onMouseUp, false ); + if ( state !== STATE.NONE ) { + document.addEventListener( 'mousemove', onMouseMove, false ); + document.addEventListener( 'mouseup', onMouseUp, false ); + scope.dispatchEvent( startEvent ); + } } @@ -331,6 +403,7 @@ THREE.OrbitControls = function ( object, domElement ) { // rotating across whole screen goes 360 degrees around scope.rotateLeft( 2 * Math.PI * rotateDelta.x / element.clientWidth * scope.rotateSpeed ); + // rotating up and down along whole screen attempts to go 360, but limited to 180 scope.rotateUp( 2 * Math.PI * rotateDelta.y / element.clientHeight * scope.rotateSpeed ); @@ -361,15 +434,14 @@ THREE.OrbitControls = function ( object, domElement ) { panEnd.set( event.clientX, event.clientY ); panDelta.subVectors( panEnd, panStart ); - - scope.pan( panDelta ); + + scope.pan( panDelta.x, panDelta.y ); panStart.copy( panEnd ); } - // Greggman fix: https://github.com/greggman/three.js/commit/fde9f9917d6d8381f06bf22cdff766029d1761be - scope.update(); + if ( state !== STATE.NONE ) scope.update(); } @@ -377,25 +449,27 @@ THREE.OrbitControls = function ( object, domElement ) { if ( scope.enabled === false ) return; - // Greggman fix: https://github.com/greggman/three.js/commit/fde9f9917d6d8381f06bf22cdff766029d1761be - scope.domElement.removeEventListener( 'mousemove', onMouseMove, false ); - scope.domElement.removeEventListener( 'mouseup', onMouseUp, false ); - + document.removeEventListener( 'mousemove', onMouseMove, false ); + document.removeEventListener( 'mouseup', onMouseUp, false ); + scope.dispatchEvent( endEvent ); state = STATE.NONE; } function onMouseWheel( event ) { - if ( scope.enabled === false || scope.noZoom === true ) return; + if ( scope.enabled === false || scope.noZoom === true || state !== STATE.NONE ) return; + + event.preventDefault(); + event.stopPropagation(); var delta = 0; - if ( event.wheelDelta ) { // WebKit / Opera / Explorer 9 + if ( event.wheelDelta !== undefined ) { // WebKit / Opera / Explorer 9 delta = event.wheelDelta; - } else if ( event.detail ) { // Firefox + } else if ( event.detail !== undefined ) { // Firefox delta = - event.detail; @@ -411,55 +485,51 @@ THREE.OrbitControls = function ( object, domElement ) { } + scope.update(); + scope.dispatchEvent( startEvent ); + scope.dispatchEvent( endEvent ); + } function onKeyDown( event ) { - if ( scope.enabled === false ) { return; } - if ( scope.noKeys === true ) { return; } - if ( scope.noPan === true ) { return; } + if ( scope.enabled === false || scope.noKeys === true || scope.noPan === true ) return; - // pan a pixel - I guess for precise positioning? - // Greggman fix: https://github.com/greggman/three.js/commit/fde9f9917d6d8381f06bf22cdff766029d1761be - var needUpdate = false; - switch ( event.keyCode ) { case scope.keys.UP: - scope.pan( new THREE.Vector2( 0, scope.keyPanSpeed ) ); - needUpdate = true; + scope.pan( 0, scope.keyPanSpeed ); + scope.update(); break; + case scope.keys.BOTTOM: - scope.pan( new THREE.Vector2( 0, -scope.keyPanSpeed ) ); - needUpdate = true; + scope.pan( 0, - scope.keyPanSpeed ); + scope.update(); break; + case scope.keys.LEFT: - scope.pan( new THREE.Vector2( scope.keyPanSpeed, 0 ) ); - needUpdate = true; + scope.pan( scope.keyPanSpeed, 0 ); + scope.update(); break; + case scope.keys.RIGHT: - scope.pan( new THREE.Vector2( -scope.keyPanSpeed, 0 ) ); - needUpdate = true; + scope.pan( - scope.keyPanSpeed, 0 ); + scope.update(); break; - } - - // Greggman fix: https://github.com/greggman/three.js/commit/fde9f9917d6d8381f06bf22cdff766029d1761be - if ( needUpdate ) { - - scope.update(); } } - + function touchstart( event ) { - if ( scope.enabled === false ) { return; } + if ( scope.enabled === false ) return; switch ( event.touches.length ) { case 1: // one-fingered touch: rotate - if ( scope.noRotate === true ) { return; } + + if ( scope.noRotate === true ) return; state = STATE.TOUCH_ROTATE; @@ -467,7 +537,8 @@ THREE.OrbitControls = function ( object, domElement ) { break; case 2: // two-fingered touch: dolly - if ( scope.noZoom === true ) { return; } + + if ( scope.noZoom === true ) return; state = STATE.TOUCH_DOLLY; @@ -478,7 +549,8 @@ THREE.OrbitControls = function ( object, domElement ) { break; case 3: // three-fingered touch: pan - if ( scope.noPan === true ) { return; } + + if ( scope.noPan === true ) return; state = STATE.TOUCH_PAN; @@ -486,14 +558,18 @@ THREE.OrbitControls = function ( object, domElement ) { break; default: + state = STATE.NONE; } + + if ( state !== STATE.NONE ) scope.dispatchEvent( startEvent ); + } function touchmove( event ) { - if ( scope.enabled === false ) { return; } + if ( scope.enabled === false ) return; event.preventDefault(); event.stopPropagation(); @@ -503,8 +579,9 @@ THREE.OrbitControls = function ( object, domElement ) { switch ( event.touches.length ) { case 1: // one-fingered touch: rotate - if ( scope.noRotate === true ) { return; } - if ( state !== STATE.TOUCH_ROTATE ) { return; } + + if ( scope.noRotate === true ) return; + if ( state !== STATE.TOUCH_ROTATE ) return; rotateEnd.set( event.touches[ 0 ].pageX, event.touches[ 0 ].pageY ); rotateDelta.subVectors( rotateEnd, rotateStart ); @@ -515,11 +592,14 @@ THREE.OrbitControls = function ( object, domElement ) { scope.rotateUp( 2 * Math.PI * rotateDelta.y / element.clientHeight * scope.rotateSpeed ); rotateStart.copy( rotateEnd ); + + scope.update(); break; case 2: // two-fingered touch: dolly - if ( scope.noZoom === true ) { return; } - if ( state !== STATE.TOUCH_DOLLY ) { return; } + + if ( scope.noZoom === true ) return; + if ( state !== STATE.TOUCH_DOLLY ) return; var dx = event.touches[ 0 ].pageX - event.touches[ 1 ].pageX; var dy = event.touches[ 0 ].pageY - event.touches[ 1 ].pageY; @@ -539,21 +619,27 @@ THREE.OrbitControls = function ( object, domElement ) { } dollyStart.copy( dollyEnd ); + + scope.update(); break; case 3: // three-fingered touch: pan - if ( scope.noPan === true ) { return; } - if ( state !== STATE.TOUCH_PAN ) { return; } + + if ( scope.noPan === true ) return; + if ( state !== STATE.TOUCH_PAN ) return; panEnd.set( event.touches[ 0 ].pageX, event.touches[ 0 ].pageY ); panDelta.subVectors( panEnd, panStart ); - - scope.pan( panDelta ); + + scope.pan( panDelta.x, panDelta.y ); panStart.copy( panEnd ); + + scope.update(); break; default: + state = STATE.NONE; } @@ -562,9 +648,11 @@ THREE.OrbitControls = function ( object, domElement ) { function touchend( /* event */ ) { - if ( scope.enabled === false ) { return; } + if ( scope.enabled === false ) return; + scope.dispatchEvent( endEvent ); state = STATE.NONE; + } this.domElement.addEventListener( 'contextmenu', function ( event ) { event.preventDefault(); }, false ); @@ -572,12 +660,16 @@ THREE.OrbitControls = function ( object, domElement ) { this.domElement.addEventListener( 'mousewheel', onMouseWheel, false ); this.domElement.addEventListener( 'DOMMouseScroll', onMouseWheel, false ); // firefox - this.domElement.addEventListener( 'keydown', onKeyDown, false ); - this.domElement.addEventListener( 'touchstart', touchstart, false ); this.domElement.addEventListener( 'touchend', touchend, false ); this.domElement.addEventListener( 'touchmove', touchmove, false ); + window.addEventListener( 'keydown', onKeyDown, false ); + + // force an update at start + this.update(); + }; -THREE.OrbitControls.prototype = Object.create( THREE.EventDispatcher.prototype ); \ No newline at end of file +THREE.OrbitControls.prototype = Object.create( THREE.EventDispatcher.prototype ); +THREE.OrbitControls.prototype.constructor = THREE.OrbitControls; diff --git a/js/models/lattice.js b/js/models/lattice.js index 1d87d74b..93fd06f4 100644 --- a/js/models/lattice.js +++ b/js/models/lattice.js @@ -70,6 +70,11 @@ Lattice = Backbone.Model.extend({ } } return nodes; + }, + + fillWithParts: function(){ + + } }); \ No newline at end of file diff --git a/js/models/threeModel.js b/js/models/threeModel.js index 91ee142a..ae00b876 100644 --- a/js/models/threeModel.js +++ b/js/models/threeModel.js @@ -17,6 +17,8 @@ function ThreeModel(){ camera.position.x = 125; camera.position.y = 100; camera.position.z = 165; + camera.up.set(0,0,1);//set z axis as "up" + scene.fog = new THREE.FogExp2(0xcccccc, 0.002); // lights diff --git a/js/threeViews/latticeView.js b/js/threeViews/latticeView.js index 0c26bdfe..04c91590 100644 --- a/js/threeViews/latticeView.js +++ b/js/threeViews/latticeView.js @@ -35,20 +35,20 @@ LatticeView = Backbone.View.extend({ var size=50, step = 5; for (var i=-size;i<=size;i+=step){ - xyPlaneGeo.vertices.push(new THREE.Vector3(-size, -size, i)); - xyPlaneGeo.vertices.push(new THREE.Vector3(size, -size, i)); + xyPlaneGeo.vertices.push(new THREE.Vector3(-size, i, -size)); + xyPlaneGeo.vertices.push(new THREE.Vector3(size, i, -size)); xyPlaneGeo.vertices.push(new THREE.Vector3(i, -size, -size)); - xyPlaneGeo.vertices.push(new THREE.Vector3(i, -size, size)); + xyPlaneGeo.vertices.push(new THREE.Vector3(i, size, -size)); - xzPlaneGeo.vertices.push(new THREE.Vector3(-size, -size, i)); - xzPlaneGeo.vertices.push(new THREE.Vector3(-size, size, i)); xzPlaneGeo.vertices.push(new THREE.Vector3(-size, i, -size)); xzPlaneGeo.vertices.push(new THREE.Vector3(-size, i, size)); + xzPlaneGeo.vertices.push(new THREE.Vector3(-size, -size, i)); + xzPlaneGeo.vertices.push(new THREE.Vector3(-size, size, i)); - yzPlaneGeo.vertices.push(new THREE.Vector3(-size, i, -size)); - yzPlaneGeo.vertices.push(new THREE.Vector3(size, i, -size)); + yzPlaneGeo.vertices.push(new THREE.Vector3(-size, -size, i)); + yzPlaneGeo.vertices.push(new THREE.Vector3(size, -size, i)); yzPlaneGeo.vertices.push(new THREE.Vector3(i, -size, -size)); - yzPlaneGeo.vertices.push(new THREE.Vector3(i, size, -size)); + yzPlaneGeo.vertices.push(new THREE.Vector3(i, -size, size)); } this.addPlane(xyPlaneGeo); -- GitLab