From 648ee61ae84216d0236e0dbc211addc13b2cfa3a Mon Sep 17 00:00:00 2001 From: Kris Kowal Date: Fri, 6 Jul 2012 11:52:06 -0700 Subject: Expand tabs --- js/helper-classes/3D/snap-manager.js | 4042 +++++++++++++++++----------------- 1 file changed, 2021 insertions(+), 2021 deletions(-) (limited to 'js/helper-classes/3D/snap-manager.js') diff --git a/js/helper-classes/3D/snap-manager.js b/js/helper-classes/3D/snap-manager.js index ab6d106d..2289f963 100755 --- a/js/helper-classes/3D/snap-manager.js +++ b/js/helper-classes/3D/snap-manager.js @@ -42,109 +42,109 @@ var Montage = require("montage/core/core").Montage, NJUtils = require("js/lib/NJUtils").NJUtils; var SnapManager = exports.SnapManager = Montage.create(Component, { - /////////////////////////////////////////////////////////////////////// - // Instance variables - /////////////////////////////////////////////////////////////////////// + /////////////////////////////////////////////////////////////////////// + // Instance variables + /////////////////////////////////////////////////////////////////////// currentStage: { value: null }, - // we keep a stack of working planes to facilitate working on other planes temporarily - _workingPlaneStack : { value: [], writable: true }, + // we keep a stack of working planes to facilitate working on other planes temporarily + _workingPlaneStack : { value: [], writable: true }, - // snapping radii relative to a 25 pixel grid - GRID_VERTEX_HIT_RAD : { value: 10, writable: true }, - GRID_EDGE_HIT_RAD : { value: 6, writable: true}, + // snapping radii relative to a 25 pixel grid + GRID_VERTEX_HIT_RAD : { value: 10, writable: true }, + GRID_EDGE_HIT_RAD : { value: 6, writable: true}, - // these are the grid snapping tolerances scaled to the current grid spacing - _gridVertexHitRad : { value: this.GRID_VERTEX_HIT_RAD, writable: true }, - _gridEdgeHitRad : { value: this.GRID_EDGE_HIT_RAD, writable: true }, + // these are the grid snapping tolerances scaled to the current grid spacing + _gridVertexHitRad : { value: this.GRID_VERTEX_HIT_RAD, writable: true }, + _gridEdgeHitRad : { value: this.GRID_EDGE_HIT_RAD, writable: true }, - ELEMENT_VERTEX_HIT_RAD : { value: 18, writable: true }, - ELEMENT_EDGE_HIT_RAD : { value: 14, writable: true }, + ELEMENT_VERTEX_HIT_RAD : { value: 18, writable: true }, + ELEMENT_EDGE_HIT_RAD : { value: 14, writable: true }, - // keep a reference to the most recent hitRecord. Used for drawing feedback on the stage - _lastHit : { value: null, writable: true }, - _hitRecords : { value: [], writable: true }, + // keep a reference to the most recent hitRecord. Used for drawing feedback on the stage + _lastHit : { value: null, writable: true }, + _hitRecords : { value: [], writable: true }, - // keep a list of objects to avoid snapping to - _avoidList : { value: [], writable: true }, + // keep a list of objects to avoid snapping to + _avoidList : { value: [], writable: true }, - // keep a cache of 2D elements to snap to - _elementCache : { value: null, writable: true }, - _isCacheInvalid : { value: false, writable: true }, + // keep a cache of 2D elements to snap to + _elementCache : { value: null, writable: true }, + _isCacheInvalid : { value: false, writable: true }, - // the snap manager can handle a 2D plane for dragging. - // A call to initDragPlane sets these variables. - // a call to clearDragPlane MUST be called on the completion of a drag - _hasDragPlane : {value: false, writable: true }, - _dragPlane : { value: null, writable: true }, - _dragPlaneToWorld : { value: Matrix.I(4), writable: true }, - _worldToDragPlane : { value: Matrix.I(4), writable: true }, - _dragPlaneActive : {value: false, writable: true }, + // the snap manager can handle a 2D plane for dragging. + // A call to initDragPlane sets these variables. + // a call to clearDragPlane MUST be called on the completion of a drag + _hasDragPlane : {value: false, writable: true }, + _dragPlane : { value: null, writable: true }, + _dragPlaneToWorld : { value: Matrix.I(4), writable: true }, + _worldToDragPlane : { value: Matrix.I(4), writable: true }, + _dragPlaneActive : {value: false, writable: true }, - // cache the matrix linking stage world and global spaces - _stageWorldToGlobalMat : { value: Matrix.I(4), writable: true }, - _globalToStageWorldMat : { value: Matrix.I(4), writable: true }, + // cache the matrix linking stage world and global spaces + _stageWorldToGlobalMat : { value: Matrix.I(4), writable: true }, + _globalToStageWorldMat : { value: Matrix.I(4), writable: true }, - // various flags to enable snapping - _snapAlignEnabled : {value: true, writable: true }, - _elementSnapEnabled : {value: true, writable: true }, - _gridSnapEnabled : {value: true, writable: true }, + // various flags to enable snapping + _snapAlignEnabled : {value: true, writable: true }, + _elementSnapEnabled : {value: true, writable: true }, + _gridSnapEnabled : {value: true, writable: true }, - // these represent the app level snap settings as set by the end user through - // the menus. These should be stored somewhere else and serialized. Putting them here for now... - _snapAlignEnabledAppLevel : {value: true, writable: true }, - _elementSnapEnabledAppLevel : {value: true, writable: true }, - _gridSnapEnabledAppLevel : {value: true, writable: true }, + // these represent the app level snap settings as set by the end user through + // the menus. These should be stored somewhere else and serialized. Putting them here for now... + _snapAlignEnabledAppLevel : {value: true, writable: true }, + _elementSnapEnabledAppLevel : {value: true, writable: true }, + _gridSnapEnabledAppLevel : {value: true, writable: true }, // App Model pointer appModel: { value: null }, - /////////////////////////////////////////////////////////////////////// - // Property accessors - /////////////////////////////////////////////////////////////////////// - pushWorkingPlane : { value: function (p) { this._workingPlaneStack.push(workingPlane.slice(0)); workingPlane = p.slice(0); }}, - popWorkingPlane : { value: function () { workingPlane = this._workingPlaneStack.pop(); return workingPlane; }}, + /////////////////////////////////////////////////////////////////////// + // Property accessors + /////////////////////////////////////////////////////////////////////// + pushWorkingPlane : { value: function (p) { this._workingPlaneStack.push(workingPlane.slice(0)); workingPlane = p.slice(0); }}, + popWorkingPlane : { value: function () { workingPlane = this._workingPlaneStack.pop(); return workingPlane; }}, - getStageWidth : { value: function () { - return parseInt(this.currentStage.offsetWidth); - }}, + getStageWidth : { value: function () { + return parseInt(this.currentStage.offsetWidth); + }}, - getStageHeight : { value: function () { - return parseInt(this.currentStage.offsetHeight); - }}, + getStageHeight : { value: function () { + return parseInt(this.currentStage.offsetHeight); + }}, - getStage : { value: function() { return this.currentStage; }}, + getStage : { value: function() { return this.currentStage; }}, - getGridVertexHitRad : { value: function() { return this._gridVertexHitRad; }}, - getGridEdgeHitRad : { value: function() { return this._gridEdgeHitRad; }}, + getGridVertexHitRad : { value: function() { return this._gridVertexHitRad; }}, + getGridEdgeHitRad : { value: function() { return this._gridEdgeHitRad; }}, - getLastHit : { value: function() { return this._lastHit; }}, - setLastHit : { value: function(h) { this._lastHit = h; }}, + getLastHit : { value: function() { return this._lastHit; }}, + setLastHit : { value: function(h) { this._lastHit = h; }}, - hasDragPlane : { value: function() { return this._hasDragPlane; }}, - getDragPlane : { value: function() { return this._dragPlane.slice(0); }}, + hasDragPlane : { value: function() { return this._hasDragPlane; }}, + getDragPlane : { value: function() { return this._dragPlane.slice(0); }}, - has2DCache : { value: function() { return (this._elementCache && !this._isCacheInvalid); }}, + has2DCache : { value: function() { return (this._elementCache && !this._isCacheInvalid); }}, - enableSnapAlign : { value: function(e) { this._snapAlignEnabled = e; }}, - snapAlignEnabled : { value: function() { return this._snapAlignEnabled; }}, - enableElementSnap : { value: function(e) { this._elementSnapEnabled = e; }}, - elementSnapEnabled : { value: function() { return this._elementSnapEnabled; }}, - enableGridSnap : { value: function(e) { this._gridSnapEnabled = e; }}, - gridSnapEnabled : { value: function() { return this._gridSnapEnabled; }}, + enableSnapAlign : { value: function(e) { this._snapAlignEnabled = e; }}, + snapAlignEnabled : { value: function() { return this._snapAlignEnabled; }}, + enableElementSnap : { value: function(e) { this._elementSnapEnabled = e; }}, + elementSnapEnabled : { value: function() { return this._elementSnapEnabled; }}, + enableGridSnap : { value: function(e) { this._gridSnapEnabled = e; }}, + gridSnapEnabled : { value: function() { return this._gridSnapEnabled; }}, - enableSnapAlignAppLevel : { value: function(e) { this._snapAlignEnabledAppLevel = e; }}, - snapAlignEnabledAppLevel : { value: function() { return this._snapAlignEnabledAppLevel; }}, - enableElementSnapAppLevel : { value: function(e) { this._elementSnapEnabledAppLevel = e; }}, - elementSnapEnabledAppLevel : { value: function() { return this._elementSnapEnabledAppLevel; }}, - enableGridSnapAppLevel : { value: function(e) { this._gridSnapEnabledAppLevel = e; }}, - gridSnapEnabledAppLevel : { value: function() { return this._gridSnapEnabledAppLevel; }}, + enableSnapAlignAppLevel : { value: function(e) { this._snapAlignEnabledAppLevel = e; }}, + snapAlignEnabledAppLevel : { value: function() { return this._snapAlignEnabledAppLevel; }}, + enableElementSnapAppLevel : { value: function(e) { this._elementSnapEnabledAppLevel = e; }}, + elementSnapEnabledAppLevel : { value: function() { return this._elementSnapEnabledAppLevel; }}, + enableGridSnapAppLevel : { value: function(e) { this._gridSnapEnabledAppLevel = e; }}, + gridSnapEnabledAppLevel : { value: function() { return this._gridSnapEnabledAppLevel; }}, - /////////////////////////////////////////////////////////////////////// - // Methods - /////////////////////////////////////////////////////////////////////// + /////////////////////////////////////////////////////////////////////// + // Methods + /////////////////////////////////////////////////////////////////////// initialize: { value: function() { this.eventManager.addEventListener("elementsRemoved", this, false); @@ -209,734 +209,734 @@ var SnapManager = exports.SnapManager = Montage.create(Component, { } }, - snap : { - value: function (xScreen, yScreen, snap3D, quadPt) - { - // force a 3D snap if a 2D snap is requested but the 2D cache has not been initialized - if (!snap3D && !this._elementCache) snap3D = true; + snap : { + value: function (xScreen, yScreen, snap3D, quadPt) + { + // force a 3D snap if a 2D snap is requested but the 2D cache has not been initialized + if (!snap3D && !this._elementCache) snap3D = true; - // clear out the last hit record - this.setLastHit( null ); + // clear out the last hit record + this.setLastHit( null ); - // snap to elements first, then the working plane - var screenPt = [xScreen, yScreen]; - var hitRecArray = new Array(); - if (this.elementSnapEnabled()) - { - if (snap3D) - this.snapToElements( screenPt, hitRecArray ); + // snap to elements first, then the working plane + var screenPt = [xScreen, yScreen]; + var hitRecArray = new Array(); + if (this.elementSnapEnabled()) + { + if (snap3D) + this.snapToElements( screenPt, hitRecArray ); - // now always doing a 2D snap - this.snapToCached2DElements( screenPt, hitRecArray ); - } + // now always doing a 2D snap + this.snapToCached2DElements( screenPt, hitRecArray ); + } - // if we did not hit anything, and we are in 2D mode, try a snap align - if (this.snapAlignEnabled()) - { - //if (hitRecArray.length == 0) - this.snapAlign( screenPt, hitRecArray ); - } + // if we did not hit anything, and we are in 2D mode, try a snap align + if (this.snapAlignEnabled()) + { + //if (hitRecArray.length == 0) + this.snapAlign( screenPt, hitRecArray ); + } - // if we did not find any objects to snap to, snap to the working plane and/or grid - //if (hitRecArray.length == 0) - { - var stage = this.getStage(); - var parentPt; - if (quadPt) - parentPt = [quadPt[0], quadPt[1], 0.0]; - else - parentPt = [xScreen, yScreen, 0.0]; + // if we did not find any objects to snap to, snap to the working plane and/or grid + //if (hitRecArray.length == 0) + { + var stage = this.getStage(); + var parentPt; + if (quadPt) + parentPt = [quadPt[0], quadPt[1], 0.0]; + else + parentPt = [xScreen, yScreen, 0.0]; - if (!snap3D && this._hasDragPlane) - this.activateDragPlane(); + if (!snap3D && this._hasDragPlane) + this.activateDragPlane(); - var hitRec = this.snapToStage( parentPt, quadPt ); + var hitRec = this.snapToStage( parentPt, quadPt ); - // try snapping to the 3D grid, or to the stage boundaries if the grid is not displayed - if (this.gridSnapEnabled()) - this.snapToGrid( hitRec ); + // try snapping to the 3D grid, or to the stage boundaries if the grid is not displayed + if (this.gridSnapEnabled()) + this.snapToGrid( hitRec ); - // save the hit record - hitRecArray.push( hitRec ); + // save the hit record + hitRecArray.push( hitRec ); - // restore the original working plane - if (!snap3D && this.hasDragPlane()) - this.deactivateDragPlane(); + // restore the original working plane + if (!snap3D && this.hasDragPlane()) + this.deactivateDragPlane(); - } //if (hitRecArray.length == 0) + } //if (hitRecArray.length == 0) - var rtnHit; + var rtnHit; // Save reference to hit records to verify last hit record's element matches browser's elementFromPoint this._hitRecords.length = 0; this._hitRecords = hitRecArray; - if (hitRecArray.length > 0) - { - this.sortHitRecords( hitRecArray ); - rtnHit = hitRecArray[0]; - } - - // catch-all to turn off drag plane snapping - this.deactivateDragPlane(); - - this.setLastHit( rtnHit ); - - //rtnHit.test(); // DEBUG CODE. REMOVE THIS - return rtnHit; - } - }, - - snapToStage: - { - value: function( scrPt, quadPt ) - { - var stage = this.getStage(); - var l2g = viewUtils.getLocalToGlobalMatrix( stage ); - var g2l = glmat4.inverse( l2g, [] ); - - var pt0 = scrPt.slice(), pt1 = scrPt.slice(); - pt0[2] = 0.0; pt1[2] = 10; - - var localPt0 = MathUtils.transformAndDivideHomogeneousPoint( pt0, g2l ), - localPt1 = MathUtils.transformAndDivideHomogeneousPoint( pt1, g2l ); - - var stageWorldPt0 = viewUtils.localToStageWorld( localPt0, stage ), - stageWorldPt1 = viewUtils.localToStageWorld( localPt1, stage ); - var vec = vecUtils.vecSubtract( 3, stageWorldPt1, stageWorldPt0 ); - - var ptOnWorkingPlane = MathUtils.vecIntersectPlane(stageWorldPt0, vec, workingPlane); - - var wpMat = drawUtils.getPlaneToWorldMatrix(workingPlane, MathUtils.getPointOnPlane(workingPlane)), - wpMatInv = glmat4.inverse( wpMat, [] ); - var localPt = MathUtils.transformPoint( ptOnWorkingPlane, wpMatInv ); - - // create the hit record - var hitRec = Object.create(HitRecord); - hitRec.setLocalPoint( localPt ); - hitRec.setPlaneMatrix( wpMat ); - hitRec.setScreenPoint(scrPt); - hitRec.setPlane(workingPlane); - hitRec.setType( hitRec.SNAP_TYPE_STAGE ); - hitRec.setElt( stage ); - if (quadPt) hitRec.setUseQuadPoint( true ); - - // DEBUG CODE - // check that the point is on the working plane - var tmpStageWorldPt = hitRec.calculateStageWorldPoint(); - var err = vecUtils.vecDot(3, tmpStageWorldPt, workingPlane) + workingPlane[3]; - if (MathUtils.fpSign(err) !== 0) - console.log( "snapToStage (function) not on working plane: " + err ); - ////////////////////////////////////////////////////////////////////// - - var calculatedScreenPt = hitRec.calculateScreenPoint(); - hitRec.setScreenPoint(calculatedScreenPt); - - // DEBUG CODE - // check that the point is on the working plane - var err2 = vecUtils.vecDist(2, calculatedScreenPt, scrPt ); - if (MathUtils.fpSign(err2) !== 0) - console.log( "snapToStage (function) error in screen point: " + err2 ); - ////////////////////////////////////////////////////////////////////// - - return hitRec; - } - }, - - snapToGrid : { - value: function( hitRec ) - { - this.calculateGridHitRadii(); - - var stage = this.getStage(); - var stageMat = viewUtils.getMatrixFromElement( stage ); - var offset = viewUtils.getElementOffset( stage ); - MathUtils.makeDimension3( offset ); - viewUtils.setViewportObj( stage ); - var scrPt = hitRec.getScreenPoint(); - - // get the grid spacing - var dx = drawUtils.getGridVerticalSpacing(), - dy = drawUtils.getGridHorizontalSpacing(); - var verticalLineCount = drawUtils.getGridVerticalLineCount(), - horizontalLineCount = drawUtils.getGridHorizontalLineCount(); - if (!drawUtils.isDrawingGrid()) - { - dx = this.getStageWidth(); - dy = this.getStageHeight(); - verticalLineCount = 2; - horizontalLineCount = 2; - } - - // get the point to the lower left of the plane point and - // see if it falls within the snap distance - var origin = [-0.5*this.getStageWidth(), -0.5*this.getStageHeight()]; - var planePt = hitRec.getLocalPoint(); - var dToOrigin = MathUtils.vecSubtract(planePt, origin); - var nx = Math.floor( dToOrigin[0]/dx), - ny = Math.floor( dToOrigin[1]/dy ); - - if ((nx < 0) || (nx >= (verticalLineCount-1))) - { - //console.log( "off the vertical end" ); - return false; - } - if ((ny < 0) || (ny >= (horizontalLineCount-1))) - { - //console.log( "off the horizontal end" ); - return false; - } - - var pt00 = [ origin[0] + nx*dx, origin[1] + ny*dy, 0.0 ]; - var planeMat = hitRec.getPlaneMatrix(); - var scrPt2 = viewUtils.postViewToStageWorld( MathUtils.transformPoint(pt00,planeMat), stage ); - scrPt2 = MathUtils.makeDimension3( scrPt2 ); - scrPt2 = vecUtils.vecAdd(3, viewUtils.viewToScreen( MathUtils.transformPoint(scrPt2, stageMat) ), offset ); - - var dist = MathUtils.vecDist( scrPt, scrPt2 ); - if (dist <= this.getGridVertexHitRad() ) - { - hitRec.setLocalPoint( pt00 ); - hitRec.setScreenPoint( scrPt2 ); - hitRec.setType( hitRec.SNAP_TYPE_GRID_VERTEX ); - return true; - } - - // check the next point and the edge connecting them - if (this.snapToGridEdge( planePt, scrPt, pt00, scrPt2, origin, nx+1, ny, hitRec, hitRec.SNAP_TYPE_GRID_HORIZONTAL )) - return true; - - // check the other edge - if (this.snapToGridEdge( planePt, scrPt, pt00, scrPt2, origin, nx, ny+1, hitRec, hitRec.SNAP_TYPE_GRID_VERTICAL )) - return true; - - // check the far corner point and 2 edges out from it - var pt11 = [ origin[0] + (nx+1)*dx, origin[1] + (ny+1)*dy, 0.0 ]; - var scrPt4 = viewUtils.postViewToStageWorld( MathUtils.transformPoint(pt11,planeMat), stage ); - scrPt4 = MathUtils.makeDimension3( scrPt4 ); - scrPt4 = vecUtils.vecAdd(3, viewUtils.viewToScreen( MathUtils.transformPoint(scrPt4, stageMat) ), offset ); - var dist = MathUtils.vecDist( scrPt, scrPt4 ); - nx++; ny++; - if (dist <= this.getGridVertexHitRad() ) - { - hitRec.setLocalPoint( pt11 ); - hitRec.setScreenPoint( scrPt4 ); - hitRec.setType( hitRec.SNAP_TYPE_GRID_VERTEX ); - return true; - } - - // check the next point and the edge connecting them - if (this.snapToGridEdge( planePt, scrPt, pt11, scrPt4, origin, nx-1, ny, hitRec, hitRec.SNAP_TYPE_GRID_HORIZONTAL )) - return true; - - // check the other edge - if (this.snapToGridEdge( planePt, scrPt, pt11, scrPt4, origin, nx, ny-1, hitRec, hitRec.SNAP_TYPE_GRID_VERTICAL )) - return true; - - return false; - } - }, - - - snapToGridEdge : - { - value : function( pt, scrPt, gridPt, gridPtScr, gridOrigin, nx, ny, hitRec, hitType ) - { - var stage = this.getStage(); - var stageMat = viewUtils.getMatrixFromElement( stage ); - var offset = viewUtils.getElementOffset( stage ); - MathUtils.makeDimension3( offset ); - var planePt = hitRec.getLocalPoint(); - var planeMat = hitRec.getPlaneMatrix(); - - var dx = drawUtils.getGridVerticalSpacing(), - dy = drawUtils.getGridHorizontalSpacing(); - var verticalLineCount = drawUtils.getGridVerticalLineCount(), - horizontalLineCount = drawUtils.getGridHorizontalLineCount(); - if (!drawUtils.isDrawingGrid()) - { - dx = this.getStageWidth(); - dy = this.getStageHeight(); - verticalLineCount = 2; - horizontalLineCount = 2; - } - - var edgePt = [ gridOrigin[0] + nx*dx, gridOrigin[1] + ny*dy, 0.0 ]; - var scrPt2 = viewUtils.postViewToStageWorld( MathUtils.transformPoint(edgePt,planeMat), stage ); - scrPt2 = MathUtils.makeDimension3( scrPt2 ); - scrPt2 = vecUtils.vecAdd(3, viewUtils.viewToScreen( MathUtils.transformPoint(scrPt2, stageMat) ), offset ); - var dist = MathUtils.vecDist( scrPt, scrPt2 ); - if (dist <= this.getGridVertexHitRad() ) - { - hitRec.setLocalPoint( edgePt ); - hitRec.setScreenPoint( scrPt2 ); - hitRec.setType( hitRec.SNAP_TYPE_GRID_VERTEX ); - return true; - } - - // check the line between the 2 previous points - var nearPt = MathUtils.nearestPointOnLine2D( gridPt, MathUtils.vecSubtract(edgePt,gridPt), pt ); - MathUtils.makeDimension3( nearPt ); - var tmpPt = MathUtils.transformPoint(nearPt,planeMat); - var scrPt3 = viewUtils.postViewToStageWorld( tmpPt, stage ); - scrPt3 = MathUtils.makeDimension3( scrPt3 ); - scrPt3 = vecUtils.vecAdd(3, viewUtils.viewToScreen( MathUtils.transformPoint(scrPt3, stageMat) ), offset ); - var edgeDist = MathUtils.vecDist( scrPt, scrPt3 ); - if (edgeDist <= this.getGridEdgeHitRad() ) - { - hitRec.setLocalPoint( nearPt ); - hitRec.setScreenPoint( scrPt3 ); - hitRec.setType( hitType ); - return true; - } - - return false; - } - }, - - clear2DCache : { - value : function() { - // clear the 2D cache flags in the objects - if (this._elementCache) - { - var n = this._elementCache.length; - for (var i=0; i 0) + { + this.sortHitRecords( hitRecArray ); + rtnHit = hitRecArray[0]; + } + + // catch-all to turn off drag plane snapping + this.deactivateDragPlane(); + + this.setLastHit( rtnHit ); + + //rtnHit.test(); // DEBUG CODE. REMOVE THIS + return rtnHit; + } + }, + + snapToStage: + { + value: function( scrPt, quadPt ) + { + var stage = this.getStage(); + var l2g = viewUtils.getLocalToGlobalMatrix( stage ); + var g2l = glmat4.inverse( l2g, [] ); + + var pt0 = scrPt.slice(), pt1 = scrPt.slice(); + pt0[2] = 0.0; pt1[2] = 10; + + var localPt0 = MathUtils.transformAndDivideHomogeneousPoint( pt0, g2l ), + localPt1 = MathUtils.transformAndDivideHomogeneousPoint( pt1, g2l ); + + var stageWorldPt0 = viewUtils.localToStageWorld( localPt0, stage ), + stageWorldPt1 = viewUtils.localToStageWorld( localPt1, stage ); + var vec = vecUtils.vecSubtract( 3, stageWorldPt1, stageWorldPt0 ); + + var ptOnWorkingPlane = MathUtils.vecIntersectPlane(stageWorldPt0, vec, workingPlane); + + var wpMat = drawUtils.getPlaneToWorldMatrix(workingPlane, MathUtils.getPointOnPlane(workingPlane)), + wpMatInv = glmat4.inverse( wpMat, [] ); + var localPt = MathUtils.transformPoint( ptOnWorkingPlane, wpMatInv ); + + // create the hit record + var hitRec = Object.create(HitRecord); + hitRec.setLocalPoint( localPt ); + hitRec.setPlaneMatrix( wpMat ); + hitRec.setScreenPoint(scrPt); + hitRec.setPlane(workingPlane); + hitRec.setType( hitRec.SNAP_TYPE_STAGE ); + hitRec.setElt( stage ); + if (quadPt) hitRec.setUseQuadPoint( true ); + + // DEBUG CODE + // check that the point is on the working plane + var tmpStageWorldPt = hitRec.calculateStageWorldPoint(); + var err = vecUtils.vecDot(3, tmpStageWorldPt, workingPlane) + workingPlane[3]; + if (MathUtils.fpSign(err) !== 0) + console.log( "snapToStage (function) not on working plane: " + err ); + ////////////////////////////////////////////////////////////////////// + + var calculatedScreenPt = hitRec.calculateScreenPoint(); + hitRec.setScreenPoint(calculatedScreenPt); + + // DEBUG CODE + // check that the point is on the working plane + var err2 = vecUtils.vecDist(2, calculatedScreenPt, scrPt ); + if (MathUtils.fpSign(err2) !== 0) + console.log( "snapToStage (function) error in screen point: " + err2 ); + ////////////////////////////////////////////////////////////////////// + + return hitRec; + } + }, + + snapToGrid : { + value: function( hitRec ) + { + this.calculateGridHitRadii(); + + var stage = this.getStage(); + var stageMat = viewUtils.getMatrixFromElement( stage ); + var offset = viewUtils.getElementOffset( stage ); + MathUtils.makeDimension3( offset ); + viewUtils.setViewportObj( stage ); + var scrPt = hitRec.getScreenPoint(); + + // get the grid spacing + var dx = drawUtils.getGridVerticalSpacing(), + dy = drawUtils.getGridHorizontalSpacing(); + var verticalLineCount = drawUtils.getGridVerticalLineCount(), + horizontalLineCount = drawUtils.getGridHorizontalLineCount(); + if (!drawUtils.isDrawingGrid()) + { + dx = this.getStageWidth(); + dy = this.getStageHeight(); + verticalLineCount = 2; + horizontalLineCount = 2; + } + + // get the point to the lower left of the plane point and + // see if it falls within the snap distance + var origin = [-0.5*this.getStageWidth(), -0.5*this.getStageHeight()]; + var planePt = hitRec.getLocalPoint(); + var dToOrigin = MathUtils.vecSubtract(planePt, origin); + var nx = Math.floor( dToOrigin[0]/dx), + ny = Math.floor( dToOrigin[1]/dy ); + + if ((nx < 0) || (nx >= (verticalLineCount-1))) + { + //console.log( "off the vertical end" ); + return false; + } + if ((ny < 0) || (ny >= (horizontalLineCount-1))) + { + //console.log( "off the horizontal end" ); + return false; + } + + var pt00 = [ origin[0] + nx*dx, origin[1] + ny*dy, 0.0 ]; + var planeMat = hitRec.getPlaneMatrix(); + var scrPt2 = viewUtils.postViewToStageWorld( MathUtils.transformPoint(pt00,planeMat), stage ); + scrPt2 = MathUtils.makeDimension3( scrPt2 ); + scrPt2 = vecUtils.vecAdd(3, viewUtils.viewToScreen( MathUtils.transformPoint(scrPt2, stageMat) ), offset ); + + var dist = MathUtils.vecDist( scrPt, scrPt2 ); + if (dist <= this.getGridVertexHitRad() ) + { + hitRec.setLocalPoint( pt00 ); + hitRec.setScreenPoint( scrPt2 ); + hitRec.setType( hitRec.SNAP_TYPE_GRID_VERTEX ); + return true; + } + + // check the next point and the edge connecting them + if (this.snapToGridEdge( planePt, scrPt, pt00, scrPt2, origin, nx+1, ny, hitRec, hitRec.SNAP_TYPE_GRID_HORIZONTAL )) + return true; + + // check the other edge + if (this.snapToGridEdge( planePt, scrPt, pt00, scrPt2, origin, nx, ny+1, hitRec, hitRec.SNAP_TYPE_GRID_VERTICAL )) + return true; + + // check the far corner point and 2 edges out from it + var pt11 = [ origin[0] + (nx+1)*dx, origin[1] + (ny+1)*dy, 0.0 ]; + var scrPt4 = viewUtils.postViewToStageWorld( MathUtils.transformPoint(pt11,planeMat), stage ); + scrPt4 = MathUtils.makeDimension3( scrPt4 ); + scrPt4 = vecUtils.vecAdd(3, viewUtils.viewToScreen( MathUtils.transformPoint(scrPt4, stageMat) ), offset ); + var dist = MathUtils.vecDist( scrPt, scrPt4 ); + nx++; ny++; + if (dist <= this.getGridVertexHitRad() ) + { + hitRec.setLocalPoint( pt11 ); + hitRec.setScreenPoint( scrPt4 ); + hitRec.setType( hitRec.SNAP_TYPE_GRID_VERTEX ); + return true; + } + + // check the next point and the edge connecting them + if (this.snapToGridEdge( planePt, scrPt, pt11, scrPt4, origin, nx-1, ny, hitRec, hitRec.SNAP_TYPE_GRID_HORIZONTAL )) + return true; + + // check the other edge + if (this.snapToGridEdge( planePt, scrPt, pt11, scrPt4, origin, nx, ny-1, hitRec, hitRec.SNAP_TYPE_GRID_VERTICAL )) + return true; + + return false; + } + }, + + + snapToGridEdge : + { + value : function( pt, scrPt, gridPt, gridPtScr, gridOrigin, nx, ny, hitRec, hitType ) + { + var stage = this.getStage(); + var stageMat = viewUtils.getMatrixFromElement( stage ); + var offset = viewUtils.getElementOffset( stage ); + MathUtils.makeDimension3( offset ); + var planePt = hitRec.getLocalPoint(); + var planeMat = hitRec.getPlaneMatrix(); + + var dx = drawUtils.getGridVerticalSpacing(), + dy = drawUtils.getGridHorizontalSpacing(); + var verticalLineCount = drawUtils.getGridVerticalLineCount(), + horizontalLineCount = drawUtils.getGridHorizontalLineCount(); + if (!drawUtils.isDrawingGrid()) + { + dx = this.getStageWidth(); + dy = this.getStageHeight(); + verticalLineCount = 2; + horizontalLineCount = 2; + } + + var edgePt = [ gridOrigin[0] + nx*dx, gridOrigin[1] + ny*dy, 0.0 ]; + var scrPt2 = viewUtils.postViewToStageWorld( MathUtils.transformPoint(edgePt,planeMat), stage ); + scrPt2 = MathUtils.makeDimension3( scrPt2 ); + scrPt2 = vecUtils.vecAdd(3, viewUtils.viewToScreen( MathUtils.transformPoint(scrPt2, stageMat) ), offset ); + var dist = MathUtils.vecDist( scrPt, scrPt2 ); + if (dist <= this.getGridVertexHitRad() ) + { + hitRec.setLocalPoint( edgePt ); + hitRec.setScreenPoint( scrPt2 ); + hitRec.setType( hitRec.SNAP_TYPE_GRID_VERTEX ); + return true; + } + + // check the line between the 2 previous points + var nearPt = MathUtils.nearestPointOnLine2D( gridPt, MathUtils.vecSubtract(edgePt,gridPt), pt ); + MathUtils.makeDimension3( nearPt ); + var tmpPt = MathUtils.transformPoint(nearPt,planeMat); + var scrPt3 = viewUtils.postViewToStageWorld( tmpPt, stage ); + scrPt3 = MathUtils.makeDimension3( scrPt3 ); + scrPt3 = vecUtils.vecAdd(3, viewUtils.viewToScreen( MathUtils.transformPoint(scrPt3, stageMat) ), offset ); + var edgeDist = MathUtils.vecDist( scrPt, scrPt3 ); + if (edgeDist <= this.getGridEdgeHitRad() ) + { + hitRec.setLocalPoint( nearPt ); + hitRec.setScreenPoint( scrPt3 ); + hitRec.setType( hitType ); + return true; + } + + return false; + } + }, + + clear2DCache : { + value : function() { + // clear the 2D cache flags in the objects + if (this._elementCache) + { + var n = this._elementCache.length; + for (var i=0; i= 0) - { - var n = this._elementCache.length; - this._elementCache[index] = this._elementCache[n-1]; - this._elementCache.pop(); + //console.log( "2D cache loaded with " + this._elementCache.length + " elements" ); + } + }, + + removeElementFrom2DCache : { + value: function( target ) { + var found = false; + var index = this.findElementIn2DCache( target ); + if (index >= 0) + { + var n = this._elementCache.length; + this._elementCache[index] = this._elementCache[n-1]; + this._elementCache.pop(); target.elementModel.isIn2DSnapCache = false; - found = true; - } - - return found; - } - }, - - addElementTo2DCache : { - value: function( elt ) { - var added = false; - if (this.hasDragPlane()) - { - // make sure the element is not already in there - if (this.findElementIn2DCache( elt ) == -1) - { - var plane = this.getDragPlane(); - var onPlane = this.elementIsOnPlane( elt, plane ); - if (onPlane) - { - added = true; - - var snapRec = new Snap2DRecord(); - snapRec.init( elt ); - this._elementCache.push( snapRec ); - - elt.elementModel.isIn2DSnapCache = true; - } - else if (elt.elementModel) + found = true; + } + + return found; + } + }, + + addElementTo2DCache : { + value: function( elt ) { + var added = false; + if (this.hasDragPlane()) + { + // make sure the element is not already in there + if (this.findElementIn2DCache( elt ) == -1) + { + var plane = this.getDragPlane(); + var onPlane = this.elementIsOnPlane( elt, plane ); + if (onPlane) + { + added = true; + + var snapRec = new Snap2DRecord(); + snapRec.init( elt ); + this._elementCache.push( snapRec ); + + elt.elementModel.isIn2DSnapCache = true; + } + else if (elt.elementModel) elt.elementModel.isIn2DSnapCache = false; - } - } - - return added; - } - }, - - elementIsIn2DCache : { - value: function( target ) { - var found = false; - var index = this.findElementIn2DCache( target ); - if (index >= 0) - found = true; - - return found; - } - }, - - findElementIn2DCache : { - value: function( target ) { - var rtnIndex = -1; - if (this._elementCache) - { - var n = this._elementCache.length; - for (var i=0; i= 0) + found = true; + + return found; + } + }, + + findElementIn2DCache : { + value: function( target ) { + var rtnIndex = -1; + if (this._elementCache) + { + var n = this._elementCache.length; + for (var i=0; i 1) { return; } - if (depth > 0) - { - // check if the element is on the specified plane - var onPlane = this.elementIsOnPlane( elt, plane ); - if (onPlane) - { - var snapRec = Object.create(Snap2DRecord);//new Snap2DRecord(); - snapRec.init( elt ); - this._elementCache.push( snapRec ); - - elt.elementModel.isIn2DSnapCache = true; - } - else if (elt.elementModel) + if (depth > 0) + { + // check if the element is on the specified plane + var onPlane = this.elementIsOnPlane( elt, plane ); + if (onPlane) + { + var snapRec = Object.create(Snap2DRecord);//new Snap2DRecord(); + snapRec.init( elt ); + this._elementCache.push( snapRec ); + + elt.elementModel.isIn2DSnapCache = true; + } + else if (elt.elementModel) elt.elementModel.isIn2DSnapCache = false; - } + } // TODO - Don't traverse svg and components' children if(elt.nodeName.toLowerCase() === "svg" || (elt.elementModel && (elt.elementModel.isComponent || (elt.elementModel.selection === "SVG")))) { return; } - var n = elt.childElementCount; - if (n > 0) - { - for (var i=0; i 1) - { - var tmpArray = new Array; - var both; - for (var i=0; i 0) - this.setLastHit( hitRecArray[hitRecArray.length-1] ); - } - }, - - getPlaneToViewMat : - { - value : function() - { - var wasActive = this._dragPlaneActive; - if (!wasActive) this.activateDragPlane(); - var stage = this.getStage(); - var wp = workingPlane.slice(0); - var mat = viewUtils.getMatrixFromElement(stage); - var wpMat = drawUtils.getPlaneToWorldMatrix(wp, MathUtils.getPointOnPlane(wp)); - //var planeToViewMat = mat.multiply(wpMat); - var planeToViewMat = glmat4.multiply( mat, wpMat, []); - if (!wasActive) this.deactivateDragPlane(); - - return planeToViewMat; - } - }, - - snapAlign : { - value: function( scrPt, hitRecArray ) { - var didHit = false; - - if (!this._elementCache) return false; - var n = this._elementCache.length; - if (n > 0) - { - // project the screen point to the working plane - this.activateDragPlane(); - var gPt = scrPt.slice(0); - var stage = this.getStage(); - //var stageOffset = viewUtils.getElementOffset( stage ); - //MathUtils.makeDimension3( stageOffset ); - var currentWorkingPlane = workingPlane.slice(0); - var wp = currentWorkingPlane.slice(0); - var mat = viewUtils.getMatrixFromElement(stage); - wp = MathUtils.transformPlane(wp, mat); + var n = elt.childElementCount; + if (n > 0) + { + for (var i=0; i 1) + { + var tmpArray = new Array; + var both; + for (var i=0; i 0) + this.setLastHit( hitRecArray[hitRecArray.length-1] ); + } + }, + + getPlaneToViewMat : + { + value : function() + { + var wasActive = this._dragPlaneActive; + if (!wasActive) this.activateDragPlane(); + var stage = this.getStage(); + var wp = workingPlane.slice(0); + var mat = viewUtils.getMatrixFromElement(stage); + var wpMat = drawUtils.getPlaneToWorldMatrix(wp, MathUtils.getPointOnPlane(wp)); + //var planeToViewMat = mat.multiply(wpMat); + var planeToViewMat = glmat4.multiply( mat, wpMat, []); + if (!wasActive) this.deactivateDragPlane(); + + return planeToViewMat; + } + }, + + snapAlign : { + value: function( scrPt, hitRecArray ) { + var didHit = false; + + if (!this._elementCache) return false; + var n = this._elementCache.length; + if (n > 0) + { + // project the screen point to the working plane + this.activateDragPlane(); + var gPt = scrPt.slice(0); + var stage = this.getStage(); + //var stageOffset = viewUtils.getElementOffset( stage ); + //MathUtils.makeDimension3( stageOffset ); + var currentWorkingPlane = workingPlane.slice(0); + var wp = currentWorkingPlane.slice(0); + var mat = viewUtils.getMatrixFromElement(stage); + wp = MathUtils.transformPlane(wp, mat); var eyePt = []; - var vec = viewUtils.parentToChildVec(gPt, stage, eyePt); - var projPt = MathUtils.vecIntersectPlane(eyePt, vec, wp); - var wpMat = drawUtils.getPlaneToWorldMatrix(currentWorkingPlane, MathUtils.getPointOnPlane(currentWorkingPlane)); - projPt[3] = 1.0; - //var planeToViewMat = mat.multiply(wpMat); - var planeToViewMat = glmat4.multiply( mat, wpMat, []); - //var viewToPlaneMat = planeToViewMat.inverse(); - var viewToPlaneMat = glmat4.inverse( planeToViewMat, [] ); - var planePt = projPt.slice(0); - planePt[3] = 1.0; - planePt = MathUtils.transformPoint( planePt, viewToPlaneMat ); - this.deactivateDragPlane(); - - var hitRec; - var nHits = hitRecArray.length; - var hr = Object.create(HitRecord);//new HitRecord(); - for (var i=0; i 0); - - if (nHits > 1) - { - var tmpArray = new Array; - for (var i=0; i dist) assocPt = ap2; - } - hitRec.setAssociatedScreenPoint( assocPt ); - } - } - - return hitRec; - } - }, - - coalesceSnapAlignHits : { - value: function( hitArray, planeToViewMat ) { - var nHits = hitArray.length; - if (nHits < 2) return; - - var hSnap, vSnap, hitRec; - for (var i=0; i 0); + + if (nHits > 1) + { + var tmpArray = new Array; + for (var i=0; i dist) assocPt = ap2; + } + hitRec.setAssociatedScreenPoint( assocPt ); + } + } + + return hitRec; + } + }, + + coalesceSnapAlignHits : { + value: function( hitArray, planeToViewMat ) { + var nHits = hitArray.length; + if (nHits < 2) return; + + var hSnap, vSnap, hitRec; + for (var i=0; i