/* 3DHOP - 3D Heritage Online Presenter Copyright (c) 2014-2020, Visual Computing Lab, ISTI - CNR All rights reserved. This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ /** * Constructs a SphereTrackball object. * @class Interactor which implements a full spherical trackball controller. */ function SphereTrackball() { } SphereTrackball.prototype = { setup : function (options) { options = options || {}; var opt = sglGetDefaultObject({ startCenter : [ 0.0, 0.0, 0.0 ], startDistance : 2.0, minMaxDist : [0.2, 4.0], }, options); this._action = SGL_TRACKBALL_NO_ACTION; this._new_action = true; this._matrix = SglMat4.identity(); this._sphereMatrix = SglMat4.identity(); // starting/default parameters this._startDistance = opt.startDistance; //distance // current parameters this._distance = this._startDistance; //limits this._minMaxDist = opt.minMaxDist; this._pts = [ [0.0, 0.0], [0.0, 0.0] ]; this._start = [0.0, 0.0]; this.reset(); }, clamp : function(value, low, high) { if(value < low) return low; if(value > high) return high; return value; }, _computeMatrix: function() { var m = SglMat4.identity(); // zoom m = SglMat4.mul(m, SglMat4.translation([0.0, 0.0, -this._distance])); // spheretrack m = SglMat4.mul(m, this._sphereMatrix); this._matrix = m; if(typeof onTrackballUpdate != "undefined") onTrackballUpdate(this.getState()); }, _projectOnSphere : function(x, y) { var r = 1.0; var z = 0.0; var d = sglSqrt(x*x + y*y); if (d < (r * 0.70710678118654752440)) { /* Inside sphere */ z = sglSqrt(r*r - d*d); } else { /* On hyperbola */ t = r / 1.41421356237309504880; z = t*t / d; } return z; }, _transform : function(m, x, y, z) { return SglMat4.mul4(m, [x, y, z, 0.0]); }, _transformOnSphere : function(m, x, y) { var z = this._projectOnSphere(x, y); //get z value return this._transform(m, x, y, z); }, _translate : function(offset, f) { var invMat = SglMat4.inverse(this._sphereMatrix); var t = SglVec3.to4(offset, 0.0); t = SglMat4.mul4(invMat, t); t = SglVec4.muls(t, f); var trMat = SglMat4.translation(t); this._sphereMatrix = SglMat4.mul(this._sphereMatrix, trMat); }, getState : function () { return this._sphereMatrix; }, setState : function (newstate) { this._sphereMatrix = newstate; this._computeMatrix(); }, animateToState : function (newstate) { this._sphereMatrix = newstate; this._computeMatrix(); }, recenter : function (newpoint) { var newpanX = (newpoint[0]-presenter.sceneCenter[0]) * presenter.sceneRadiusInv; var newpanY = (newpoint[1]-presenter.sceneCenter[1]) * presenter.sceneRadiusInv; var newpanZ = (newpoint[2]-presenter.sceneCenter[2]) * presenter.sceneRadiusInv; this._sphereMatrix[12] = -newpanX; this._sphereMatrix[13] = -newpanY; this._sphereMatrix[14] = -newpanZ; this._distance *= 0.6; this._distance = this.clamp(this._distance, this._minMaxDist[0], this._minMaxDist[1]); this._computeMatrix(); }, tick : function (dt) { return false; }, set action(a) { if(this._action != a) this._new_action = true; this._action = a; }, get action() { return this._action; }, get matrix() { this._computeMatrix(); return this._matrix; }, get distance() { return this._distance; }, reset : function () { this._matrix = SglMat4.identity(); this._sphereMatrix = SglMat4.identity(); this._action = SGL_TRACKBALL_NO_ACTION; this._new_action = true; this._distance = this._startDistance; this._pts = [ [0.0, 0.0], [0.0, 0.0] ]; this._computeMatrix(); }, track : function(m, x, y, z) { if(this._new_action) { this._start[0] = x; this._start[1] = y; this._new_action = false; } var dx = this._start[0] - x; var dy = this._start[1] - y; this._start[0] = x; this._start[1] = y; this._pts[0][0] = this._pts[1][0] + dx; this._pts[0][1] = this._pts[1][1] + dy; this._pts[1][0] = dx; this._pts[1][1] = dy; switch (this._action) { case SGL_TRACKBALL_ROTATE: this.rotate(m); break; case SGL_TRACKBALL_PAN: this.pan(m); break; case SGL_TRACKBALL_DOLLY: this.dolly(m, z); break; case SGL_TRACKBALL_SCALE: this.scale(m, z); break; default: break; } }, rotate : function(m) { if ((this._pts[0][0] == this._pts[1][0]) && (this._pts[0][1] == this._pts[1][1])) return; //if Xold == Xnew && Yold ==Ynew return var mInv = SglMat4.inverse(m); var v0 = this._transformOnSphere(mInv, this._pts[0][0], this._pts[0][1]); //project on sphere (Xold, Yold) var v1 = this._transformOnSphere(mInv, this._pts[1][0], this._pts[1][1]); //project on sphere (Xnew, Ynew) var axis = SglVec3.cross(v0, v1); //axis of rotation var angle = SglVec3.length(axis); //angle of rotation var rotMat = SglMat4.rotationAngleAxis(angle, axis); this._sphereMatrix = SglMat4.mul(rotMat, this._sphereMatrix); this._computeMatrix(); }, pan : function(m) { var mInv = SglMat4.inverse(m); var v0 = this._transform(mInv, this._pts[0][0], this._pts[0][1], -1.0); var v1 = this._transform(mInv, this._pts[1][0], this._pts[1][1], -1.0); var offset = SglVec3.sub(v1, v0); this._translate(offset, 2.0); this._computeMatrix(); }, dolly : function(m, dz) { var mInv = SglMat4.inverse(m); var offset = this._transform(mInv, 0.0, 0.0, dz); this._translate(offset, 1.0); this._computeMatrix(); }, scale : function(m, s) { this._distance *= s; this._distance = this.clamp(this._distance, this._minMaxDist[0], this._minMaxDist[1]); this._computeMatrix(); } }; /***********************************************************************/