From b89a7ee8b956c96a1dcee995ea840feddc5d4b27 Mon Sep 17 00:00:00 2001 From: Pierre Frisch Date: Thu, 22 Dec 2011 07:25:50 -0800 Subject: First commit of Ninja to ninja-internal Signed-off-by: Valerio Virgillito --- js/tools/drawing-tool-base.js | 673 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 673 insertions(+) create mode 100644 js/tools/drawing-tool-base.js (limited to 'js/tools/drawing-tool-base.js') diff --git a/js/tools/drawing-tool-base.js b/js/tools/drawing-tool-base.js new file mode 100644 index 00000000..6fc34911 --- /dev/null +++ b/js/tools/drawing-tool-base.js @@ -0,0 +1,673 @@ +/* +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. +
*/ + +var Montage = require("montage/core/core").Montage; +var Component = require("montage/ui/component").Component; + +var snapManager = require("js/helper-classes/3D/snap-manager").SnapManager; +var viewUtils = require("js/helper-classes/3D/view-utils").ViewUtils; +var vecUtils = require("js/helper-classes/3D/vec-utils").VecUtils; +var Properties3D = require("js/helper-classes/Properties3D").Properties3D; +var drawUtils = require("js/helper-classes/3D/draw-utils").DrawUtils; + +exports.DrawingToolBase = Montage.create(Montage, { + + //!!!! HACK + // TODO: Need to find a better way to address this + stage: { + value: null + }, + + stageComponent: { + value: null + }, + + /** + * Used on the initial MouseDown for Drawing Tools + * + * Returns: An array containing: + * 0 - HitRec point + * 1 - X value converted to screen point + * 2 - Y value converted to screen point + */ + getInitialSnapPoint: { + value: function(x,y) { + // update the snap settings + snapManager.enableSnapAlign( snapManager.snapAlignEnabledAppLevel() ); + snapManager.enableElementSnap( snapManager.elementSnapEnabledAppLevel() ); + snapManager.enableGridSnap( snapManager.gridSnapEnabledAppLevel() ); + + // do the snap + var hitRec = snapManager.snap(x, y, true); + if (hitRec) { + // set up the working plane and convert the hit record to be working plane relative + var dragPlane = snapManager.setupDragPlanes( hitRec ); + var wpHitRec = hitRec.convertToWorkingPlane( dragPlane ); + + var pt = hitRec.getScreenPoint(); + + return( [wpHitRec, pt[0], pt[1]] ); + } + } + }, + + /** + * Used on the Mouse Move to calculate new snap point. + */ + getUpdatedSnapPoint: { + value: function(x,y, snap3d, downHitRec) { + // update the snap settings + snapManager.enableSnapAlign( snapManager.snapAlignEnabledAppLevel() ); + snapManager.enableElementSnap( snapManager.elementSnapEnabledAppLevel() ); + snapManager.enableGridSnap( snapManager.gridSnapEnabledAppLevel() ); + + + // do the first snap + var hitRec = snapManager.snap(x, y, snap3d ); + if (hitRec) { + if ((hitRec.getType() !== hitRec.SNAP_TYPE_STAGE) && !hitRec.isSomeGridTypeSnap()) { + hitRec = hitRec.convertToWorkingPlane( snapManager.getDragPlane() ); + } + + if(downHitRec !== null) { + // if we are working off-plane, do a snap to the projected locations of the geometry + var thePlane = workingPlane; + if (snapManager.hasDragPlane()) + { + thePlane = snapManager.getDragPlane(); + } + + // Return the up HitRec + return hitRec; + } else { + return null; + } + } + } + }, + + getUpdatedSnapPointNoAppLevelEnabling: { + value: function(x,y, snap3d, downHitRec) { + + + // do the first snap + var hitRec = snapManager.snap(x, y, snap3d ); + if (hitRec) { + if ((hitRec.getType() !== hitRec.SNAP_TYPE_STAGE) && !hitRec.isSomeGridTypeSnap()) { + hitRec = hitRec.convertToWorkingPlane( snapManager.getDragPlane() ); + } + + if(downHitRec !== null) { + // if we are working off-plane, do a snap to the projected locations of the geometry + var thePlane = workingPlane; + if (snapManager.hasDragPlane()) + { + thePlane = snapManager.getDragPlane(); + } + + // Return the up HitRec + return hitRec; + } else { + return null; + } + } + } + }, + + + setDownHitRec: { + value: function (x, y, do3DSnap) { + var hitRec = snapManager.snap(x, y, do3DSnap ); + if (hitRec) { + if ((hitRec.getType() != hitRec.SNAP_TYPE_STAGE) && !hitRec.isSomeGridTypeSnap()) { + //hitRec = hitRec.convertToWorkingPlane( workingPlane ); + snapManager.setupDragPlanes(hitRec); + hitRec = hitRec.convertToWorkingPlane( snapManager.getDragPlane() ); + } + + return hitRec; + + } + } + }, + + drawSnapLastHit: { + value: function() { + snapManager.drawLastHit(); + } + }, + + getHitRecPos: { + value: function (hitRec) { + if (!hitRec) + return null; + + // get the hit rec. points in plane space + var psPos = hitRec.getLocalPoint(); + + var stage = this.stage; + var stageOffset = viewUtils.getElementOffset(stage); + viewUtils.setViewportObj(stage); + + // get the matrix taking the local hit point in plane space + // to world space of whatever element it is in. + var planeMat = hitRec.getPlaneMatrix(); + + // get the center of the circle in stage world space + var swPos = viewUtils.postViewToStageWorld(MathUtils.transformPoint(psPos, hitRec.getPlaneMatrix()), hitRec.getElt()); + + // the stage world space point is now relative to the center of the 3D space. To + // calculate the left and top offsets, this must be offset by the stage dimensions + swPos[0] += snapManager.getStageWidth() / 2.0; + swPos[1] += snapManager.getStageHeight() / 2.0; + + return swPos; + } + }, + + getCompletePoints: { + value: function(hitRec0, hitRec1) { + if (hitRec0 && hitRec1) { + + // get the 2 snap points in plane space + var p0 = hitRec0.getLocalPoint(), + p1 = hitRec1.getLocalPoint(); + + var stage = this.stage; + var stageOffset = viewUtils.getElementOffset(stage); + viewUtils.setViewportObj(stage); + + // get the matrix taking the local hit point in plane space + // to world space of whatever element it is in. + var planeMat = hitRec0.getPlaneMatrix(); + + // get the center of the circle in stage world space + var s0 = viewUtils.postViewToStageWorld( MathUtils.transformPoint(p0,hitRec0.getPlaneMatrix()), hitRec0.getElt() ), + s1 = viewUtils.postViewToStageWorld( MathUtils.transformPoint(p1,hitRec1.getPlaneMatrix()), hitRec1.getElt() ); + + // apply the projected snap points + var s0Proj = false, s1Proj = false; + + // find a "reasonable" plane + var thePlane = workingPlane.slice(0); + if (snapManager.hasDragPlane()) { + thePlane = snapManager.getDragPlane(); + } + + var d0 = vecUtils.vecDot( 3, thePlane, s0 ) + thePlane[3], + d1 = vecUtils.vecDot( 3, thePlane, s1 ) + thePlane[3]; + var sign0 = MathUtils.fpSign( d0 ), sign1 = MathUtils.fpSign( d1 ); + if ((sign0 !== 0) || (sign1 !== 0)) { + // we need to pick a different plane + if ( MathUtils.fpCmp(d0,d1) === 0 ) { + thePlane[3] = -vecUtils.vecDot(3, thePlane, s0); + } else { + var vec = vecUtils.vecSubtract(3, s1, s0 ); + var yAxis = Vector.create([0,1,0]); + var tmp = vecUtils.vecCross( 3, vec, yAxis ); + var mag = vecUtils.vecMag(3, tmp); + if (MathUtils.fpSign(mag) === 0) { + thePlane = Vector.create( [0,0,1] ); + thePlane[3] = -vecUtils.vecDot(3, thePlane, s0); + } else { + var xAxis = vecUtils.vecCross( 3, yAxis, tmp ); + thePlane = vecUtils.vecCross( 3, xAxis, yAxis ); + vecUtils.vecNormalize(3, thePlane, 1.0 ); + thePlane[3] = -vecUtils.vecDot(3, thePlane, s0); + } + } + + // recompute the plane matrix + planeMat = drawUtils.getPlaneToWorldMatrix(thePlane, MathUtils.getPointOnPlane(thePlane)); + } + + // unproject the bounds + //var planeMatInv = planeMat.inverse(); + var planeMatInv = glmat4.inverse( planeMat, [] ); + //var midPt = this.unprojectPoints( s0, s1, planeMat, planeMatInv, s1Proj ); + var midPt = this.unprojectPoints( s0, s1, planeMat, planeMatInv, true ); + + // get the 4 points of the bounding box in 2D space + p0 = MathUtils.transformPoint( s0, planeMatInv ); + p1 = MathUtils.transformPoint( s1, planeMatInv ); + + // determine the midpoint of the oval + //midPt = s0.add(s1); + //midPt = midPt.multiply(0.5); + //midPt = MathUtils.makeDimension3(midPt); + midPt = vec3.add(s0, s1, []); + midPt = vecUtils.vecScale( 3, midPt, 0.5 ); + + // the mid point is now relative to the center of the 3D space. To + // calculate the left and top offsets, this must be offset by the stage dimensions + midPt[0] += snapManager.getStageWidth() / 2.0; + midPt[1] += snapManager.getStageHeight() / 2.0; + + // calculate the width and height. + var left = p0[0]; var top = p0[1]; + var right = p1[0]; var bottom = p1[1]; + var w = Math.abs(right - left), + h = Math.abs(bottom - top); + +// return ({"width":w, "height":h, "planeMat":planeMat, "midPt":midPt}); + var s0Offset = s0.slice(0); + var s1Offset = s1.slice(0); + + s0Offset[0] += snapManager.getStageWidth() / 2.0; + s0Offset[1] += snapManager.getStageHeight() / 2.0; + s1Offset[0] += snapManager.getStageWidth() / 2.0; + s1Offset[1] += snapManager.getStageHeight() / 2.0; + return ({ "width": w, "height": h, "planeMat": planeMat, "midPt": midPt, "mouseDownPos": s0Offset, "mouseUpPos": s1Offset }); + } else { + return null + } + } + }, + + cleanupSnap: { + value: function() { + // set the drag plane to the working plane + snapManager.clear2DCache(); + snapManager.setupDragPlaneFromPlane( workingPlane ); + + // update the snap settings + snapManager.enableSnapAlign( snapManager.snapAlignEnabledAppLevel() ); + snapManager.enableElementSnap( snapManager.elementSnapEnabledAppLevel() ); + snapManager.enableGridSnap( snapManager.gridSnapEnabledAppLevel() ); + + } + }, + + draw2DRectangle: { + value: function(x0, y0, x1, y1) { + var drawingContext = this.stageComponent.drawingContext, + drawingPrefs = this.stageComponent.drawingContextPreferences; + + this.stageComponent.clearDrawingCanvas(); + //TODO Save and restore state + drawingContext.strokeStyle = drawingPrefs.color; + drawingContext.lineWidth = drawingPrefs.thickness; + drawingContext.strokeRect(x0 - 0.5 ,y0 - 0.5 ,x1 ,y1); + } + }, + + /** + * Feedback drawing functions + */ + drawRectangle: { + value: function(hitRec0, hitRec1) { + var p0 = hitRec0.getLocalPoint(), + p1 = hitRec1.getLocalPoint(); + + var stage = this.stage; + var stageMat = viewUtils.getMatrixFromElement( stage ); + var elt = hitRec0.getElt(); + if (!elt) { elt = hitRec1.getElt(); } + if (!elt) { elt = stage; } + if (elt) + { + viewUtils.pushViewportObj(elt); + var offset = viewUtils.getElementOffset(elt); + offset[2] = 0; + + // check the plane to draw the rectangle on + var s0 = viewUtils.postViewToStageWorld( MathUtils.transformPoint(p0,hitRec0.getPlaneMatrix()), hitRec0.getElt() ), + s1 = viewUtils.postViewToStageWorld( MathUtils.transformPoint(p1,hitRec1.getPlaneMatrix()), hitRec1.getElt() ); + + // find a "reasonable" plane + var planeMat = hitRec0.getPlaneMatrix(); + var planeMatInv; + var thePlane = workingPlane.slice(0); + if (snapManager.hasDragPlane()) + { + thePlane = snapManager.getDragPlane(); + } + var d0 = vecUtils.vecDot( 3, thePlane, s0 ) + thePlane[3], + d1 = vecUtils.vecDot( 3, thePlane, s1 ) + thePlane[3]; + var sign0 = MathUtils.fpSign( d0 ), sign1 = MathUtils.fpSign( d1 ); + if ((sign0 !== 0) || (sign1 !== 0)) + { + // we need to pick a different plane + if ( MathUtils.fpCmp(d0,d1) === 0 ){ + thePlane[3] = -vecUtils.vecDot(3, thePlane, s0); + } + else + { + var vec = vecUtils.vecSubtract(3, s1, s0 ); + var yAxis = Vector.create([0,1,0]); + var tmp = vecUtils.vecCross( 3, vec, yAxis ); + var mag = vecUtils.vecMag(3, tmp); + if (MathUtils.fpSign(mag) === 0) + { + thePlane = Vector.create( [0,0,1] ); + thePlane[3] = -vecUtils.vecDot(3, thePlane, s0); + } + else + { + var xAxis = vecUtils.vecCross( 3, yAxis, tmp ); + thePlane = vecUtils.vecCross( 3, xAxis, yAxis ); + vecUtils.vecNormalize(3, thePlane, 1.0 ); + thePlane[3] = -vecUtils.vecDot(3, thePlane, s0); + } + } + + // recompute the plane matrix + planeMat = drawUtils.getPlaneToWorldMatrix(thePlane, MathUtils.getPointOnPlane(thePlane)); + //planeMatInv = planeMat.inverse(); + planeMatInv = glmat4.inverse( planeMat, [] ); + + p0 = MathUtils.transformPoint(p0,hitRec0.getPlaneMatrix()); + p0 = MathUtils.transformPoint(p0, planeMatInv); + p1 = MathUtils.transformPoint(p1,hitRec1.getPlaneMatrix()); + p1 = MathUtils.transformPoint(p1, planeMatInv); + } + else { + //planeMatInv = planeMat.inverse(); + planeMatInv = glmat4.inverse( planeMat, [] ); + } + + // determine if the geometry is going to be projected. If so a second projected rectangle is drawn + var isProjected = ((MathUtils.fpCmp(thePlane[2],1.0) !== 0) || (MathUtils.fpSign(thePlane[3]) !== 0)); + + // get and draw the unprojected object points + var projPtArr = []; + if (isProjected) + { + this.getProjectedObjectPoints( s0, s1, planeMat, planeMatInv, elt, stageMat, offset, projPtArr ); + + } + + var localPt = Vector.create([p0[0], p0[1], 0.0, 1.0]); + s0 = viewUtils.postViewToStageWorld( MathUtils.transformPoint(localPt,planeMat), elt ); + s0 = vecUtils.vecAdd(3, viewUtils.viewToScreen( MathUtils.transformPoint(s0, stageMat) ), offset ); + + localPt[1] = p1[1]; + s1 = viewUtils.postViewToStageWorld( MathUtils.transformPoint(localPt,planeMat), elt ); + s1 = vecUtils.vecAdd(3, viewUtils.viewToScreen( MathUtils.transformPoint(s1, stageMat) ), offset ); + + localPt[0] = p1[0]; + var s2 = viewUtils.postViewToStageWorld( MathUtils.transformPoint(localPt,planeMat), elt ); + s2 = vecUtils.vecAdd(3, viewUtils.viewToScreen( MathUtils.transformPoint(s2, stageMat) ), offset ); + + localPt[1] = p0[1]; + var s3 = viewUtils.postViewToStageWorld( MathUtils.transformPoint(localPt,planeMat), elt ); + s3 = vecUtils.vecAdd(3, viewUtils.viewToScreen( MathUtils.transformPoint(s3, stageMat) ), offset ); + + if ( isProjected) + { + var unprojPtArr = [s0, s1, s2, s3]; + this.stageComponent.draw3DProjectedAndUnprojectedRectangles( unprojPtArr, projPtArr ); + } + else + { + this.stageComponent.draw3DSelectionRectangle(s0[0], s0[1], + s1[0], s1[1], + s2[0], s2[1], + s3[0], s3[1], + s0[0], s0[1]); + } + + viewUtils.popViewportObj(); + } + } + }, + + drawLine: { + value: function (hitRec0, hitRec1, strokeSize, strokeColor) { + var p0 = hitRec0.getScreenPoint(), + p1 = hitRec1.getScreenPoint(); + this.stageComponent.drawLine(p0[0], p0[1], p1[0], p1[1], strokeSize, strokeColor); + } + }, + + /** + * Get the matrix for the actual element being added to the user document. + */ + getElementMatrix: { + value: function(planeMat, midPt) { + var divMat, flatMat, flatMatSafe; + // calculate the matrix for the element. + // we should not need to worry about divide by zero below since we snapped to the point + divMat = planeMat.slice(0); + divMat[12] = 0.0; + divMat[13] = 0.0; + //divMat[14] = 0.0; + divMat[14] = midPt[2]; + + // set the left and top of the element such that the center of the rectangle is at the mid point + viewUtils.setViewportObj(this.stage); + + flatMat = divMat; + flatMatSafe = MathUtils.scientificToDecimal(flatMat, 10); + + return "perspective(" + 1400 + ") matrix3d( " + flatMatSafe + ")"; + } + }, + + /** + * Draw Helper Functions + */ + + /** + * Returns a perfect square using the top/left/bottom/right values. + */ + toSquare: { + value: function(x0,x1,y0,y1) { + var dw = 1; + var dh = 1; + + var w = x1 - x0, + h = y1 - y0; + + if(w < 0) dw = -1; + if(h < 0) dh = -1; + + if(Math.abs(w) >= Math.abs(h)) { + h = (Math.abs(w) * dh); + } else { + w = (Math.abs(h) * dw); + } + + return [x0,y0,w,h]; + } + }, + + toCenterRectangle: { + value: function(x0,x1,y0,y1) { + var x,y,w,h = 0; + + x = x0 - (x1 - x0); + y = y0 - (y1 - y0); + w = x1 - x; + h = y1 - y; + + return [x,y,w,h]; + } + }, + + + + + /** + * Helper Functions + */ + + unprojectPoints: + { + value: function( s0In, s1In, planeMat, planeMatInv, fixedS1 ) { + var s0 = s0In.slice(0); + var s2 = s1In.slice(0); + + + // calculate the mid point of the rectangle + var midPt = vecUtils.vecAdd(3, s0, s2); + vecUtils.vecScale(3, midPt, 0.5); + s0[0] -= midPt[0]; s0[1] -= midPt[1]; + s2[0] -= midPt[0]; s2[1] -= midPt[1]; + + // convert the 2 world space points to plane space + var p0 = MathUtils.transformPoint( s0, planeMatInv ), + p2 = MathUtils.transformPoint( s2, planeMatInv ); + var z = p0[2]; + + // fill in the other 2 points on the plane to complete the 4 points + var p1 = Vector.create( [p0[0], p2[1], z] ), + p3 = Vector.create( [p2[0], p0[1], z] ); + + // convert back to 3D space + s0 = MathUtils.transformPoint( p0, planeMat ); + var s1 = MathUtils.transformPoint( p1, planeMat ); + s2 = MathUtils.transformPoint( p2, planeMat ); + var s3 = MathUtils.transformPoint( p3, planeMat ); + + // unproject the 4 points + var i; + var p = 1400; + var ptArr = [ s0, s1, s2, s3 ]; + for (i=0; i<4; i++) + { + pt = ptArr[i]; + if (MathUtils.fpCmp(p,-pt[2]) !== 0){ + z = pt[2]*p/(p + pt[2]); + var x = pt[0]*(p - z)/p, + y = pt[1]*(p - z)/p; + + + pt[0] = x; pt[1] = y; pt[2] = z; + } + } + + // back to 2D space... + for (i=0; i<4; i++) + ptArr[i] = MathUtils.transformPoint( ptArr[i], planeMatInv ); + + // find the 2 diagonal points to use + if (fixedS1) + { + p0 = ptArr[0]; p1 = ptArr[1]; p2 = ptArr[2]; p3 = ptArr[3]; + pt0 = p0.slice(0); pt1 = p2.slice(0); + z = pt0[2]; + } + else + { + p0 = ptArr[0]; p1 = ptArr[1]; p2 = ptArr[2]; p3 = ptArr[3]; + z = p0[2]; + var pt0 = p0.slice(0), pt1 = p2.slice(0); + if (p0[0] < p2[0]){ + pt0[0] = Math.max(p0[0],p1[0]); + pt1[0] = Math.min(p2[0],p3[0]); + } + else { + pt0[0] = Math.min(p0[0],p1[0]); + pt1[0] = Math.max(p2[0],p3[0]); + } + if (p0[1] < p2[1]){ + pt0[1] = Math.max(p0[1],p3[1]); + pt1[1] = Math.min(p1[1],p2[1]); + } + else { + pt0[1] = Math.min(p0[1],p3[1]); + pt1[1] = Math.max(p1[1],p2[1]); + } + } + pt0[2] = z; pt1[2] = z; + + var ctr = vecUtils.vecAdd(3, pt0, pt1); + vecUtils.vecScale( 3, ctr, 0.5 ); + + // put the diagonal points back in 3D space + s0 = MathUtils.transformPoint( pt0, planeMat ); + s1 = MathUtils.transformPoint( pt1, planeMat ); + ctr = MathUtils.transformPoint( ctr, planeMat ); + + // add the translation back in + s0[0] += midPt[0]; s0[1] += midPt[1]; + s1[0] += midPt[0]; s1[1] += midPt[1]; + ctr[0] += midPt[0]; ctr[1] += midPt[1]; + + // set the returned values + s0In[0] = s0[0]; s0In[1] = s0[1]; s0In[2] = s0[2]; + s1In[0] = s1[0]; s1In[1] = s1[1]; s1In[2] = s1[2]; + + return ctr; + } + }, + + getProjectedObjectPoints: + { + value: function( u0, u1, planeMat, planeMatInv, elt, stageMat, offset, rtnScrPts ){ + var s0 = u0.slice(0); + var s2 = u1.slice(0); + + var i, z; + + // calculate the mid point of the rectangle + var midPt = vecUtils.vecAdd(3, s0, s2); + vecUtils.vecScale(3, midPt, 0.5); + s0[0] -= midPt[0]; s0[1] -= midPt[1]; + s2[0] -= midPt[0]; s2[1] -= midPt[1]; + + // unproject the 2 points + var p = 1400; + var ptArr = [ s0, s2 ]; + for (i=0; i<2; i++) + { + pt = ptArr[i]; + if (MathUtils.fpCmp(p,-pt[2]) !== 0) { + z = pt[2]*p/(p + pt[2]); + var x = pt[0]*(p - z)/p, + y = pt[1]*(p - z)/p; + y = pt[1]*(p - z)/p; + + pt[0] = x; pt[1] = y; pt[2] = z; + } + } + + // back to 2D space... + for (i=0; i<2; i++) + ptArr[i] = MathUtils.transformPoint( ptArr[i], planeMatInv ); + + // fill in the other 2 points on the plane to complete the 4 points + var pt0 = ptArr[0], pt2 = ptArr[1]; + z = pt0[2]; + pt0[2] = z; pt2[2] = z; + var pt1 = Vector.create( [pt0[0], pt2[1], z] ), + pt3 = Vector.create( [pt2[0], pt0[1], z] ); + + // + ptArr = [pt0, pt1, pt2, pt3]; + var pt; + var dist, scale, pDist = 1400; // elt.webkitTransform is not always defined. + for (i=0; i<4; i++) + { + // put the point back in 3D space + pt = MathUtils.transformPoint( ptArr[i], planeMat ); + + // apply the perspective + dist = pDist - pt[2]; + if (MathUtils.fpSign(dist) !== 0) + { + scale = pDist / dist; + pt[0] *= scale; + pt[1] *= scale; + pt[2] *= scale; + } + + // add the translation back in + pt[0] += midPt[0]; pt[1] += midPt[1]; + + // to screen coordinates + pt = viewUtils.postViewToStageWorld( pt, elt ); + pt = vecUtils.vecAdd(3, viewUtils.viewToScreen( MathUtils.transformPoint(pt, stageMat) ), offset ); + + // save the final result + rtnScrPts[i] = pt; + } + } + } + +}); + -- cgit v1.2.3