diff --git a/src/webgl/p5.Camera.js b/src/webgl/p5.Camera.js index 66a55ffa24..5c07cb4daa 100644 --- a/src/webgl/p5.Camera.js +++ b/src/webgl/p5.Camera.js @@ -1720,23 +1720,21 @@ p5.Camera.prototype._orbit = function(dTheta, dPhi, dRadius) { let camTheta = Math.atan2(diffX, diffZ); // equatorial angle let camPhi = Math.acos(Math.max(-1, Math.min(1, diffY / camRadius))); // polar angle - // add change - camTheta += dTheta; - camPhi += dPhi; - camRadius += dRadius; + let newUpY = this.upY > 0 ? 1 : -1; + // add change according to the direction of newupY + camTheta += newUpY * dTheta; + camPhi += newUpY * dPhi; + // if camPhi becomes >= PI or <= 0, + // upY of camera need to be flipped to the other side + if (camPhi <= 0 || camPhi >= Math.PI) { + newUpY *= -1; + } + camRadius += dRadius; // prevent zooming through the center: if (camRadius < 0) { camRadius = 0.1; } - - // prevent rotation over the zenith / under bottom - if (camPhi > Math.PI) { - camPhi = Math.PI; - } else if (camPhi <= 0) { - camPhi = 0.001; - } - // from https://github.com/mrdoob/three.js/blob/dev/src/math/Vector3.js#L628-L632 const _x = Math.sin(camPhi) * camRadius * Math.sin(camTheta); const _y = Math.cos(camPhi) * camRadius; @@ -1750,7 +1748,7 @@ p5.Camera.prototype._orbit = function(dTheta, dPhi, dRadius) { this.centerY, this.centerZ, 0, - 1, + newUpY, 0 ); }; diff --git a/test/unit/webgl/p5.Camera.js b/test/unit/webgl/p5.Camera.js index 9fff7e1fcc..655a1bae08 100644 --- a/test/unit/webgl/p5.Camera.js +++ b/test/unit/webgl/p5.Camera.js @@ -449,34 +449,59 @@ suite('p5.Camera', function() { assert.deepEqual(myCam.cameraMatrix.mat4, expectedMatrix); }); - test('_orbit() ensures altitude phi <= PI', function() { + test('_orbit() ensures myCam.upY switches direction (from 1 to -1) at camPhi <= 0', function() { + // the following should produce the upY with inverted direction(from 1 to -1) + // when camPhi changes from positive to negative or zero + myCam._orbit(0, -Math.PI, 0); + // upY should switch from 1(dPhi=0) to -1 (dPhi=-PI) + // myCam.upY should be -1 + assert(myCam.upY === -1); + }); + test('_orbit() ensures myCam.upY switches direction (from -1 to 1) at camPhi <= 0', function() { + // the following should produce the upY with inverted direction(from -1 to 1) + // when camPhi changes from negative to positive or zero + myCam._orbit(0, -Math.PI, 0); + myCam._orbit(0, Math.PI, 0); + // upY should switch from -1(dPhi=-PI) to 1 (dPhi=PI) + // myCam.upY should be 1 + assert(myCam.upY === 1); + }); + test('_orbit() ensures myCam.upY switches direction (from 1 to -1) at camPhi >= PI', function() { + // the following should produce the upY with inverted direction(from 1 to -1) + // when camPhi reaches PI + myCam._orbit(0, Math.PI, 0); + // upY should switch from 1(dPhi=0) to -1 (dPhi=PI) + // myCam.upY should be -1 + assert(myCam.upY === -1); + }); + test('_orbit() ensures myCam.upY switches direction (from -1 to 1) at camPhi >= PI', function() { + // the following should produce the upY with inverted direction(from -1 to 1) + // when camPhi reaches PI + myCam._orbit(0, Math.PI, 0); + myCam._orbit(0, -Math.PI, 0); + // upY should switch from -1(dPhi=PI) to 1 (dPhi=-PI) + // myCam.upY should be 1 + assert(myCam.upY === 1); + }); + test('_orbit() ensures camera can do multiple continuous 360deg rotations', function() { + // the following should produce two camera objects having same properties. + myCam._orbit(0, Math.PI, 0); var myCamCopy = myCam.copy(); - - // the following should produce the same values because phi is capped at PI - myCamCopy._orbit(0, 10, 0); - myCam._orbit(0, 20, 0); - - assert.deepEqual(myCam.cameraMatrix.mat4, myCamCopy.cameraMatrix.mat4); - }); - test('_orbit() ensures altitude phi > 0', function() { - var myCamCopy = myCam.copy(); - - // the following should produce the same values because phi is restricted - // to > 0 - myCamCopy._orbit(0, -10, 0); - myCam._orbit(0, -20, 0); - - assert.deepEqual(myCam.cameraMatrix.mat4, myCamCopy.cameraMatrix.mat4); + myCamCopy._orbit(0, Math.PI, 0); + myCamCopy._orbit(0, Math.PI, 0); + for (let i = 0; i < myCamCopy.cameraMatrix.mat4.length; i++) { + expect( + myCamCopy.cameraMatrix.mat4[i]).to.be.closeTo( + myCam.cameraMatrix.mat4[i], 0.001); + } }); test('_orbit() ensures radius > 0', function() { + // the following should produce two camera objects having same properties. + myCam._orbit(0, Math.PI, 0); var myCamCopy = myCam.copy(); - - // the following should produce the same values because radius is - // restricted to > 0 - myCamCopy._orbit(0, 0, -200); - myCam._orbit(0, 0, -300); - - assert.deepEqual(myCam.cameraMatrix.mat4, myCamCopy.cameraMatrix.mat4); + myCamCopy._orbit(0, 0, -100); + myCam._orbit(0, 0, -250); + assert.deepEqual(myCam.cameraMatrix.mat4, myCamCopy.cameraMatrix.mat4, 'deep equal is failing'); }); });