/* This file contains proprietary software owned by Motorola Mobility, Inc.
No rights, expressed or implied, whatsoever to this software are provided by Motorola Mobility, Inc. hereunder.
(c) Copyright 2011 Motorola Mobility, Inc. All Rights Reserved.
*/ /* Base class for the 3D rotation tools Subclass RotateObject3DTool will rotate the object that was clicked. Subclass RotateStage3DTool will rotate the stage. */ var Montage = require("montage/core/core").Montage, ModifierToolBase = require("js/tools/modifier-tool-base").ModifierToolBase, snapManager = require("js/helper-classes/3D/snap-manager").SnapManager, viewUtils = require("js/helper-classes/3D/view-utils").ViewUtils, vecUtils = require("js/helper-classes/3D/vec-utils").VecUtils, drawUtils = require("js/helper-classes/3D/draw-utils").DrawUtils, ElementsMediator = require("js/mediators/element-mediator").ElementMediator; exports.Rotate3DToolBase = Montage.create(ModifierToolBase, { _canSnap: { value: false }, _inLocalMode: { value: true, enumerable: true }, drawWithoutSnapping: { value: function(event) { if(this._handleMode !== null) { this._matL = this._handles[this._handleMode]._matL.slice(0); this._planeEq = this._handles[this._handleMode]._planeEq.slice(0); this._dirVecL = this._handles[this._handleMode]._dirVecL.slice(0); this._startPoint = MathUtils.getLocalPoint(this.downPoint.x, this.downPoint.y, this._planeEq, this._matL); if(!this._startPoint) { this._startPoint = [this.downPoint.x, this.downPoint.y]; } } } }, modifyElements: { value: function(data, event) { var mat, angle, pt0 = data.pt0, pt1 = data.pt1; if(this._handleMode !== null) { if(this._activateOriginHandle) { // move the transform origin handle var dx = pt1.x - pt0.x; var dy = pt1.y - pt0.y; var dz = pt1.z - pt0.z; this._origin[0] += dx; this._origin[1] += dy; this._origin[2] += dz; var len = this._targets.length; if(len === 1) { this._startOriginArray[0][0] += dx; this._startOriginArray[0][1] += dy; this._startOriginArray[0][2] += dz; console.log( "Rotate3DToolBase.modifyElements, _startOriginArray[0]: " + this._startOriginArray[0] ); } this.downPoint.x = pt1.x; this.downPoint.y = pt1.y; this.downPoint.z = pt1.z; this.DrawHandles(); return; } angle = this._getAngleToRotate(pt1.x, pt1.y); if(event.shiftKey) { var f = Math.floor(angle/(Math.PI/4)); angle = f*Math.PI/4; } this._delta = angle; switch(this._handleMode) { case 0: // Rotate X; mat = Matrix.RotationX(angle); break; case 1: // Rotate Y mat = Matrix.RotationY(angle); break; case 2: // Rotate Z mat = Matrix.RotationZ(angle); break; default: break; } } else { if (event.ctrlKey || event.metaKey) { var zAngle = this._mouseSpeed * (pt1.y - this.downPoint.y) * Math.PI / 180.0; if (zAngle === 0) { zAngle = 0.01 * Math.PI / 180.0; } if(event.shiftKey) { var f = Math.floor(zAngle/(Math.PI/4)); zAngle = f*Math.PI/4; } mat = Matrix.RotationZ(zAngle); } else { var yAngle = this._mouseSpeed * (pt1.x - this.downPoint.x) * Math.PI / 180.0; var xAngle = -this._mouseSpeed * (pt1.y - this.downPoint.y) * Math.PI / 180.0; if(event.shiftKey) { var f = Math.floor(yAngle/(Math.PI/4)); yAngle = f*Math.PI/4; f = Math.floor(xAngle/(Math.PI/4)); xAngle = f*Math.PI/4; } // check the orientation of the X axis //if (drawUtils.drawYZ) xAngle = -xAngle; var yMat = Matrix.RotationY( yAngle ), xMat = Matrix.RotationX( xAngle ); mat = glmat4.multiply(yMat, xMat, []); } } if(this._inLocalMode && (this._targets.length === 1) ) { this._rotateLocally(mat); } else { this._rotateGlobally(mat); } // this.UpdateSelection(); NJevent("elementChanging", {type : "Changing", redraw: false}); } }, _rotateLocally: { value: function (rotMat) { var len = this._targets.length; for(var i = 0; i < len; i++) { var item = this._targets[i]; var elt = item.elt; var curMat = item.mat; // pre-translate by the transformation center var tMat = Matrix.I(4); // _startOriginArray is the location of the center of rotation // in view space of the element. It is currently calculated during the drag // using delta's in global screen space, hence wrong. // We can put the transformCenter back in element view space here, however // it would be (much) more efficient to cache the localToGlobal and // globalToLocal matrices. var transformCtr = this._startOriginArray[i]; var localToGlobalMat = viewUtils.getLocalToGlobalMatrix( elt ); var globalToLocalMat = glmat4.inverse( localToGlobalMat, [] ); var newTransformCtr = MathUtils.transformAndDivideHomogeneousPoint( this._origin, globalToLocalMat ); viewUtils.pushViewportObj( elt ); newTransformCtr = viewUtils.screenToView( newTransformCtr[0], newTransformCtr[1], newTransformCtr[2] ); viewUtils.popViewportObj( elt ); //console.log( "transformCtr: " + newTransformCtr ); transformCtr = newTransformCtr; tMat[12] = transformCtr[0]; tMat[13] = transformCtr[1]; tMat[14] = transformCtr[2]; var mat = glmat4.multiply(curMat, tMat, []); // translate back tMat[12] = -transformCtr[0]; tMat[13] = -transformCtr[1]; tMat[14] = -transformCtr[2]; glmat4.multiply(mat, rotMat, mat); glmat4.multiply(mat, tMat, mat); // while moving, set inline style to improve performance viewUtils.setMatrixForElement( elt, mat, true ); } } }, _rotateGlobally: { value: function (rotMat) { var len = this._targets.length; for(var i = 0; i < len; i++) { var item = this._targets[i]; var elt = item.elt; var curMat = item.mat; // pre-translate by the transformation center var tMat = Matrix.I(4); var transformCtr = this._startOriginArray[i].slice(0); transformCtr = MathUtils.transformPoint(transformCtr, curMat); tMat[12] = transformCtr[0]; tMat[13] = transformCtr[1]; tMat[14] = transformCtr[2]; var mat = glmat4.multiply(tMat, rotMat, []); // translate back tMat[12] = -transformCtr[0]; tMat[13] = -transformCtr[1]; tMat[14] = -transformCtr[2]; glmat4.multiply(mat, tMat, mat); glmat4.multiply(mat, curMat, mat); viewUtils.setMatrixForElement( elt, mat, true ); } } }, _getAngleToRotate: { value: function (x, y) { var angle; var pt = MathUtils.getLocalPoint(x, y, this._planeEq, this._matL); if(!pt) { //TODO - should this be _startPoint.x/y instead of downPoint.x/y? var st = [this.downPoint.x, this.downPoint.y]; pt = [x, y]; var sub = vecUtils.vecSubtract(2, pt, st); var dot = vecUtils.vecDot(2, sub, this._dirVecL); angle = vecUtils.vecDist(2, pt, st) * 0.1; if (dot < 0) { angle = -angle; } } else { angle = MathUtils.getAngleBetweenPoints(this._startPoint, pt); } return angle; } }, captureSelectionChange: { value: function(event){ this.eventManager.addEventListener("selectionDrawn", this, true); } }, captureSelectionDrawn: { value: function(event){ this._origin = null; this._targets = []; this._startOriginArray = null; var len = this.application.ninja.selectedElements.length; if(len) { if(len === 1) { this.target = this.application.ninja.selectedElements[0]; drawUtils.addElement(this.target); viewUtils.pushViewportObj( this.target ); var eltCtr = viewUtils.getCenterOfProjection(); eltCtr[2] = 0; viewUtils.popViewportObj(); var ctrOffset = this.target.elementModel.props3D.m_transformCtr; if(ctrOffset) { eltCtr[2] = 0; eltCtr = vecUtils.vecAdd(3, eltCtr, ctrOffset); } this._origin = viewUtils.localToGlobal(eltCtr, this.target); console.log( "Rotate3DToolBase.captureSelectionDrawn _origin: " + this._origin ); this._updateTargets(); this._setTransformOrigin(false); } else { this.target = this.application.ninja.currentDocument.documentRoot; this._origin = drawUtils._selectionCtr.slice(0); this._origin[0] += this.application.ninja.stage.userContentLeft; this._origin[1] += this.application.ninja.stage.userContentTop; this._updateTargets(); this._setTransformOrigin(true); } } else { this.target = null; } this.DrawHandles(); if(event) { this.eventManager.removeEventListener("selectionDrawn", this, true); } } }, _updateTargets: { value: function(addToUndoStack) { var newStyles = [], previousStyles = [], len = this.application.ninja.selectedElements.length; this._targets = []; for(var i = 0; i < len; i++) { var elt = this.application.ninja.selectedElements[i]; // this._initProps3D(elt); var curMat = viewUtils.getMatrixFromElement(elt); var curMatInv = glmat4.inverse(curMat, []); viewUtils.pushViewportObj( elt ); var eltCtr = viewUtils.getCenterOfProjection(); viewUtils.popViewportObj(); eltCtr = viewUtils.localToGlobal(eltCtr, elt); this._targets.push({elt:elt, mat:curMat, matInv:curMatInv, ctr:eltCtr}); if(addToUndoStack) { var previousStyleStr = {dist:this._undoArray[i].dist, mat:MathUtils.scientificToDecimal(this._undoArray[i].mat.slice(0), 5)}; var newStyleStr = {dist:viewUtils.getPerspectiveDistFromElement(elt), mat:MathUtils.scientificToDecimal(curMat, 5)}; previousStyles.push(previousStyleStr); newStyles.push(newStyleStr); } } if(addToUndoStack) { ElementsMediator.set3DProperties(this.application.ninja.selectedElements, newStyles, "Change", "rotateTool", previousStyles ); } // Save previous value for undo/redo this._undoArray = []; for(i = 0, len = this._targets.length; i < len; i++) { var elt = this._targets[i].elt; var _mat = viewUtils.getMatrixFromElement(elt); var _dist = viewUtils.getPerspectiveDistFromElement(elt); this._undoArray.push({mat:_mat, dist:_dist}); } } }, _setTransformOrigin: { value: function(shouldUpdateCenter) { if(!this._origin) { return; } var len = this._targets.length; var elt, eltCtr, ctrOffset, matInv; if( len === 1) { elt = this._target; if(shouldUpdateCenter) { eltCtr = this._targets[0].ctr; ctrOffset = vecUtils.vecSubtract(3, this._origin, eltCtr); matInv = this._targets[0].matInv; ctrOffset = MathUtils.transformVector(ctrOffset, matInv); elt.elementModel.props3D.m_transformCtr = ctrOffset; } else { this._startOriginArray = []; var ctrOffset = this._target.elementModel.props3D.m_transformCtr; if(!ctrOffset) { ctrOffset = [0,0,0]; } } this._startOriginArray[0] = ctrOffset; } else { // Update transform ctr for all elements if transform origin was modified this._startOriginArray = []; for (var i = 0; i < len; i++) { elt = this._targets[i].elt; eltCtr = this._targets[i].ctr; ctrOffset = vecUtils.vecSubtract(3, this._origin, eltCtr); matInv = this._targets[i].matInv; ctrOffset = MathUtils.transformVector(ctrOffset, matInv); this._startOriginArray[i] = ctrOffset; } } } }, HandleDoubleClick: { value: function () { if(!this._target) { return; } if(this._activateOriginHandle) { var len = this.application.ninja.selectedElements.length; if( (len === 1) || (this._toolID === "rotateStage3DTool") ) { this._target.elementModel.props3D.m_transformCtr = null; } this._handleMode = null; this._activateOriginHandle = false; this.application.ninja.stage.drawingCanvas.style.cursor = this._cursor; this.captureSelectionDrawn(null); } } }, Reset : { value : function() { var item, elt, mat, dist, newStyles = [], previousStyles = [], len = this._targets.length, iMat; for(var i = 0; i < len; i++) { // Reset to the identity matrix item = this._targets[i]; iMat = Matrix.I(4); mat = item.mat; // iMat[12] = mat[12]; // iMat[13] = mat[13]; // iMat[14] = mat[14]; dist = this._undoArray[i].dist; var previousStyleStr = {dist:dist, mat:mat}; var newStyleStr = {dist:dist, mat:iMat}; previousStyles.push(previousStyleStr); newStyles.push(newStyleStr); } ElementsMediator.set3DProperties(this.application.ninja.selectedElements, newStyles, "Change", "rotateTool", previousStyles ); this.isDrawing = false; this.endDraw(event); // this.UpdateSelection(true); this.Configure(true); } }, /** * SHIFT/ALT/SPACE Key Handlers */ HandleShiftKeyDown: { value: function (event) { } }, HandleShiftKeyUp: { value: function () { } }, HandleSpaceKeyDown: { value: function () { } }, HandleSpaceUp: { value: function () { } }, HandleAltKeyDown: { value: function(event) { this._inLocalMode = !this._inLocalMode; this.DrawHandles(); } }, HandleAltKeyUp: { value: function(event) { this._inLocalMode = !this._inLocalMode; this.DrawHandles(); } }, handleScroll: { value: function(event) { this.captureSelectionDrawn(null); } }, /** * This function is for specifying custom feedback routine * upon mouse over. * For example, the drawing tools will add a glow when mousing * over existing canvas elements to signal to the user that * the drawing operation will act on the targeted canvas. */ _showFeedbackOnMouseMove : { value: function (event) { this._canSnap = false; if(this._target && this._handles) { var len = this._handles.length; var i = 0, toolHandle, c, point = webkitConvertPointFromPageToNode(this.application.ninja.stage.canvas, new WebKitPoint(event.pageX, event.pageY)); for (i=0; i