From 04d7f7ec211d7fee47aa353309eca36864c50d53 Mon Sep 17 00:00:00 2001 From: Pushkar Joshi Date: Fri, 13 Apr 2012 17:28:03 -0700 Subject: Still buggy version of pen tool that can create polylines in 3D space correctly (except for incorrect canvas translation). Using cleaned up and re-factored version of the code. Not suitable for testing. --- js/lib/geom/sub-path.js | 940 +++++++----------------------------------------- js/tools/PenTool.js | 849 +++++++++++++++++++++---------------------- 2 files changed, 542 insertions(+), 1247 deletions(-) diff --git a/js/lib/geom/sub-path.js b/js/lib/geom/sub-path.js index 765316b1..761391b7 100755 --- a/js/lib/geom/sub-path.js +++ b/js/lib/geom/sub-path.js @@ -11,30 +11,6 @@ var GeomObj = require("js/lib/geom/geom-obj").GeomObj; var AnchorPoint = require("js/lib/geom/anchor-point").AnchorPoint; var MaterialsModel = require("js/models/materials-model").MaterialsModel; -// TODO Those function do not seems to be used. We should remove them -/* -function SubpathOffsetPoint(pos, mapPos) { - this.Pos = [pos[0],pos[1],pos[2]]; - this.CurveMapPos = [mapPos[0], mapPos[1], mapPos[2]]; -} - -function SubpathOffsetTriangle(v0, v1, v2) { - this.v0 = v0; - this.v1 = v1; - this.v2 = v2; - this.n = [0,0,1]; //replace with the actual cross product later -} - -function sortNumberAscending(a,b){ - return a-b; -} -function sortNumberDescending(a,b){ - return b-a; -} -function SegmentIntersections(){ - this.paramArray = []; -} -*/ /////////////////////////////////////////////////////////////////////// // Class GLSubpath // representation a sequence of cubic bezier curves. @@ -45,60 +21,44 @@ var GLSubpath = function GLSubpath() { /////////////////////////////////////////////////// // Instance variables /////////////////////////////////////////////////// + + // NOTE: + // This class contains functionality to store piecewise cubic bezier paths. + // The coordinates of the paths are always in local, canvas space. + // That is, the Z coordinate can be ignored (for now), and the paths are essentially in 2D. + // All coordinates of the '_Samples' should lie within [0,0] and [width, height], + // where width and height refer to the dimensions of the canvas for this path. + // Whenever the the canvas dimensions change, the coordinates of the anchor points + // and _Samples must be re-computed. + this._Anchors = []; this._BBoxMin = [0, 0, 0]; this._BBoxMax = [0, 0, 0]; this._isClosed = false; - this._samples = []; //polyline representation of this curve in stage world space + this._Samples = []; //polyline representation of this curve in canvas space this._sampleParam = []; //parametric distance of samples, within [0, N], where N is # of Bezier curves (=# of anchor points if closed, =#anchor pts -1 if open) - this._anchorSampleIndex = []; //index within _samples corresponding to anchor points + this._anchorSampleIndex = []; //index within _Samples corresponding to anchor points - this._LocalPoints = []; //polyline representation of this curve in canvas space - this._LocalBBoxMin = [0,0,0]; //bbox min point of _LocalPoints - this._LocalBBoxMax = [0,0,0]; //bbox max point of _LocalPoints - this._AnchorLocalCoords = []; //local coords for all the anchor points , stored as a triplet of 3D vectors (prev, curr, next, in order) - this._UnprojectedAnchors = []; - - //initially set the _dirty bit so we will construct samples + //initially set the _dirty bit so we will re-construct _Anchors and _Samples this._dirty = true; - //initially set the local dirty bit so we will construct local coordinates - this._isLocalDirty = true; - - //initially set the local dirty bit for the anchors so we will construct local coordinates - this._isAnchorLocalDirty = true; - - //whether or not to use the canvas drawing to stroke/fill - this._useCanvasDrawing = true; - - //the canvas that will draw this subpath - this._canvas = null; - - //the top left location of this subpath's canvas in screen space + //the top left location of this subpath's canvas in screen space (used to identify cases when location changes) this._canvasLeft = 0; this._canvasTop = 0; //stroke information this._strokeWidth = 1.0; this._strokeColor = [0.4, 0.4, 0.4, 1.0]; - this._strokeMaterial = null - this._strokeStyle = "Solid"; - this._materialAmbient = [0.2, 0.2, 0.2, 1.0]; - this._materialDiffuse = [0.4, 0.4, 0.4, 1.0]; - this._materialSpecular = [0.4, 0.4, 0.4, 1.0]; this._fillColor = [1.0, 1.0, 1.0, 0.0]; - this._fillMaterial = null; this._DISPLAY_ANCHOR_RADIUS = 5; + //drawing context this._world = null; + this._canvas = null; //todo this might be unnecessary (but faster) since we can get it from the world //tool that owns this subpath this._drawingTool = null; - this._planeMat = Matrix.I(4); - this._planeMatInv = Matrix.I(4); - this._planeCenter = null; - this._dragPlane = null; //used to query what the user selected, OR-able for future extensions this.SEL_NONE = 0; //nothing was selected @@ -110,8 +70,6 @@ var GLSubpath = function GLSubpath() { this._selectedAnchorIndex = -1; this._SAMPLING_EPSILON = 0.5; //epsilon used for sampling the curve - this._DEFAULT_STROKE_WIDTH = 20; //use only if stroke width not specified - this._MAX_OFFSET_ANGLE = 10; //max angle (in degrees) between consecutive vectors from curve to offset path // (current GeomObj complains if buildBuffers/render is added to GLSubpath prototype) //buildBuffers @@ -121,13 +79,6 @@ var GLSubpath = function GLSubpath() { // return; //no need to do anything for now }; - this._offsetLocalCoord = function(deltaW, deltaH){ - var numPoints = this._LocalPoints.length; - for (var i=0;ix || this._BBoxMin[1]>y || this._BBoxMin[2]>z){ + return false; } - for (var i=startIndex; i anchorIndex) {//this._anchorSampleIndex.length>anchorIndex && this._LocalPoints.length > this._anchorSampleIndex[anchorIndex]) { - //var localCoord = this._LocalPoints[this._anchorSampleIndex[anchorIndex]]; - var anchorLocalCoord = this._AnchorLocalCoords[anchorIndex]; - var distSq = VecUtils.vecDistSq(3, [pickX, pickY, pickZ], anchorLocalCoord[1]); - //check the anchor point - if (distSq < radSq && distSq= this._Anchors.length) { return this.SEL_NONE; } @@ -684,7 +552,7 @@ GLSubpath.prototype._checkAnchorIntersection = function(pickX, pickY, pickZ, rad return this.SEL_NONE; }; -GLSubpath.prototype.pickAnchor = function (pickX, pickY, pickZ, radius, useLocal) { +GLSubpath.prototype.pickAnchor = function (pickX, pickY, pickZ, radius) { var numAnchors = this._Anchors.length; var selAnchorIndex = -1; var retCode = this.SEL_NONE; @@ -692,15 +560,16 @@ GLSubpath.prototype.pickAnchor = function (pickX, pickY, pickZ, radius, useLocal var radSq = radius * radius; //check if the clicked location is close to the currently selected anchor position if (this._selectedAnchorIndex>=0 && this._selectedAnchorIndexx || this._BBoxMin[1]>y || this._BBoxMin[2]>z){ - return false; - } - if (this._BBoxMax[0]=0 && this._selectedAnchorIndex=0 && selAnchorIndex === -1) { - var distSq = this._Anchors[this._selectedAnchorIndex].getPrevDistanceSq(pickX, pickY, pickZ); - if (distSq < minDistance && distSq < radSq){ - selAnchorIndex = this._selectedAnchorIndex; - minDistance = distSq; - } else { - //check the next for this anchor point - distSq = this._Anchors[this._selectedAnchorIndex].getNextDistanceSq(pickX, pickY, pickZ); - if (distSq=0 && this._selectedAnchorIndex=0 && selAnchorIndex === -1) { - var distSq; - if (!useLocal) - distSq = this._Anchors[this._selectedAnchorIndex].getPrevDistanceSq(pickX, pickY, pickZ); - else - distSq = VecUtils.vecDistSq(3, this._AnchorLocalCoords[this._selectedAnchorIndex][0], [pickX, pickY, pickZ]); - - if (distSq < minDistance && distSq < radSq){ - selAnchorIndex = this._selectedAnchorIndex; - minDistance = distSq; - retCode = retCode | this.SEL_PREV; - } else { - //check the next for this anchor point - if (!useLocal) - distSq = this._Anchors[this._selectedAnchorIndex].getNextDistanceSq(pickX, pickY, pickZ); - else - distSq = VecUtils.vecDistSq(3, this._AnchorLocalCoords[this._selectedAnchorIndex][2], [pickX, pickY, pickZ]); - - if (distSq=1 || Math.abs(translateCanvasY)>=1){ this.setCanvasLeft(penCanvasCurrentLeft); this.setCanvasTop(penCanvasCurrentTop); - //todo does the canvas translation correspond to the translation in stage world space? - this.translateAnchors(translateCanvasX, translateCanvasY, 0); } this._dirty=true; }; @@ -1010,9 +687,11 @@ GLSubpath.prototype.computeLeftTopWidthHeight = function() { //build the 3D position of the plane center of this canvas by looking at midpoint of the bounding box in stage world coords bboxMin = this.getBBoxMin(); bboxMax = this.getBBoxMax(); - var bboxMid = [0.5 * (bboxMax[0] + bboxMin[0]), 0.5 * (bboxMax[1] + bboxMin[1]), 0.5 * (bboxMax[2] + bboxMin[2])]; - var left = Math.round(bboxMid[0] - 0.5 * bboxWidth); - var top = Math.round(bboxMid[1] - 0.5 * bboxHeight); + //var bboxMid = [0.5 * (bboxMax[0] + bboxMin[0]), 0.5 * (bboxMax[1] + bboxMin[1]), 0.5 * (bboxMax[2] + bboxMin[2])]; + //var left = Math.round(bboxMid[0] - 0.5 * bboxWidth); + //var top = Math.round(bboxMid[1] - 0.5 * bboxHeight); + var left = this._canvasLeft; + var top = this._canvasTop; return [left, top, bboxWidth, bboxHeight]; }; @@ -1027,41 +706,16 @@ GLSubpath.prototype.setStrokeWidth = function (w) { var ElementMediator = require("js/mediators/element-mediator").ElementMediator; //translate the subpath in case the actual canvas location does not match where subpath thinks the canvas should be - this.translateSubpathPerCanvas(ElementMediator); + this.setCanvasLeftTopPerElementMediator(ElementMediator); // **** adjust the left, top, width, and height to adjust for the change in stroke width **** this.createSamples(); //dirty bit is checked here - this.buildLocalCoord(); //local dirty bit is checked here -/* - //build the width and height of this canvas by looking at local coordinates - var bboxMin = this.getLocalBBoxMin(); - var bboxMax = this.getLocalBBoxMax(); - var bboxWidth = bboxMax[0] - bboxMin[0]; - var bboxHeight = bboxMax[1] - bboxMin[1]; - - //build the 3D position of the plane center of this canvas by looking at midpoint of the bounding box in stage world coords - bboxMin = this.getBBoxMin(); - bboxMax = this.getBBoxMax(); - var bboxMid = [0.5 * (bboxMax[0] + bboxMin[0]), 0.5 * (bboxMax[1] + bboxMin[1]), 0.5 * (bboxMax[2] + bboxMin[2])]; - var left = Math.round(bboxMid[0] - 0.5 * bboxWidth); - var top = Math.round(bboxMid[1] - 0.5 * bboxHeight); -*/ var ltwh = this.computeLeftTopWidthHeight(); var canvasArray=[this._canvas]; ElementMediator.setProperty(canvasArray, "width", [ltwh[2]+"px"], "Changing", "penTool");//canvas.width = w; ElementMediator.setProperty(canvasArray, "height", [ltwh[3]+"px"], "Changing", "penTool");//canvas.height = h; ElementMediator.setProperty(canvasArray, "left", [ltwh[0]+"px"],"Changing", "penTool");//DocumentControllerModule.DocumentController.SetElementStyle(canvas, "left", parseInt(left) + "px"); ElementMediator.setProperty(canvasArray, "top", [ltwh[1]+ "px"],"Changing", "penTool");//DocumentControllerModule.DocumentController.SetElementStyle(canvas, "top", parseInt(top) + "px"); - this.setCanvasLeft(ltwh[0]); - this.setCanvasTop(ltwh[1]); -}; - -GLSubpath.prototype.getStrokeMaterial = function () { - return this._strokeMaterial; -}; - -GLSubpath.prototype.setStrokeMaterial = function (m) { - this._strokeMaterial = m; }; GLSubpath.prototype.getStrokeColor = function () { @@ -1072,22 +726,6 @@ GLSubpath.prototype.setStrokeColor = function (c) { this._strokeColor = c; }; -GLSubpath.prototype.getStrokeStyle = function () { - return this._strokeStyle; -}; - -GLSubpath.prototype.setStrokeStyle = function (s) { - this._strokeStyle = s; -}; - -GLSubpath.prototype.getFillMaterial = function() { - return this._fillMaterial; -}; - -GLSubpath.prototype.setFillMaterial = function(m){ - this._fillMaterial = m; -}; - GLSubpath.prototype.getFillColor = function() { return this._fillColor; }; @@ -1096,20 +734,6 @@ GLSubpath.prototype.setFillColor = function(c){ this._fillColor = c; }; -GLSubpath.prototype.copyFromSubpath = function (subpath) { - this.clearAllAnchors(); - for (var i = 0; i < subpath.getNumAnchors(); i++) { - var oldAnchor = subpath.getAnchor(i); - var newAnchor = new AnchorPoint(); - newAnchor.setPos(oldAnchor.getPosX(), oldAnchor.getPosY(), oldAnchor.getPosZ()); - newAnchor.setPrevPos(oldAnchor.getPrevX(), oldAnchor.getPrevY(), oldAnchor.getPrevZ()); - newAnchor.setNextPos(oldAnchor.getNextX(), oldAnchor.getNextY(), oldAnchor.getNextZ()); - this.addAnchor(newAnchor); - } - this.setIsClosed(subpath.getIsClosed()); - this.setStrokeWidth(subpath.getStrokeWidth()); -}; - GLSubpath.prototype.translateAnchors = function (tx, ty, tz) { for (var i=0;i= this._Anchors.length){ - return null; - } - - if (index>= this._AnchorLocalCoords.length || this._isAnchorLocalDirty){ - this._isAnchorLocalDirty = true; - this.buildAnchorLocalCoord(); - } - - return this._AnchorLocalCoords[index]; - /* - if (index>=this._anchorSampleIndex.length){ - return null; - } - var anchorSampleIndex = this._anchorSampleIndex[index]; - if (anchorSampleIndex>=this._LocalPoints.length){ - return null; - } - var localCoord = this._LocalPoints[anchorSampleIndex].slice(0); - return localCoord; - */ -}; - -GLSubpath.prototype.buildAnchorLocalCoord = function() -{ - if (!this._isAnchorLocalDirty){ - return; //nothing to do - } - var ViewUtils = require("js/helper-classes/3D/view-utils").ViewUtils; - var SnapManager = require("js/helper-classes/3D/snap-manager").SnapManager; - - var widthAdjustment = -SnapManager.getStageWidth()*0.5; - var heightAdjustment = -SnapManager.getStageHeight()*0.5; - - var drawingCanvas = this.getCanvas(); - if (!drawingCanvas){ - drawingCanvas = ViewUtils.getStageElement(); - } - var localToStageWorldMat = ViewUtils.getLocalToStageWorldMatrix(drawingCanvas, true, false); - var stageWorldToLocalMat = glmat4.inverse(localToStageWorldMat, []); - var numAnchors = this._Anchors.length; - this._AnchorLocalCoords = []; - var i=0, currAnchor, prevLocalCoord, currLocalCoord, nextLocalCoord; - for (i=0;i pt[d]) { - bboxMin[d] = pt[d]; - } - if (bboxMax[d] < pt[d]) { - bboxMax[d] = pt[d]; - } - } - } - //save the center of the bbox for later use (while constructing the canvas) - var stageWorldCenter = VecUtils.vecInterpolate(3, bboxMin, bboxMax, 0.5); - - // ***** center the input stageworld data about the center of the bbox ***** - this._LocalPoints = []; - for (i=0;i 1) { - //start with the first anchor position (since the Bezier curve start point is not added in the sample function below) - //this._samples.push(this._Anchors[0].getPosX()); - //this._samples.push(this._Anchors[0].getPosY()); - //this._samples.push(this._Anchors[0].getPosZ()); - for (var i = 0; i < numAnchors - 1; i++) { //get the control points var C0X = this._Anchors[i].getPosX(); @@ -1418,7 +895,7 @@ GLSubpath.prototype.createSamples = function () { var beginParam = i; var endParam = i+1; - this._anchorSampleIndex.push(this._samples.length/3); //index of sample corresponding to anchor i + this._anchorSampleIndex.push(this._Samples.length); //index of sample corresponding to anchor i this._sampleCubicBezier(C0X, C0Y, C0Z, C1X, C1Y, C1Z, C2X, C2Y, C2Z, C3X, C3Y, C3Z, beginParam, endParam); } //for every anchor point i, except last @@ -1443,62 +920,41 @@ GLSubpath.prototype.createSamples = function () { var beginParam = i; var endParam = i+1; - this._anchorSampleIndex.push(this._samples.length/3); //index of sample corresponding to anchor i + this._anchorSampleIndex.push(this._Samples.length); //index of sample corresponding to anchor i this._sampleCubicBezier(C0X, C0Y, C0Z, C1X, C1Y, C1Z, C2X, C2Y, C2Z, C3X, C3Y, C3Z, beginParam, endParam); } else { - this._anchorSampleIndex.push((this._samples.length/3) - 1); //index of sample corresponding to last anchor + this._anchorSampleIndex.push((this._Samples.length) - 1); //index of sample corresponding to last anchor } } //if (numAnchors >== 2) { - //compute the //re-compute the bounding box (this also accounts for stroke width, so assume the stroke width is set) this.computeBoundingBox(true); - - //set the local dirty bit so we will re-create the local coords before rendering - this._isLocalDirty = true; } //if (this._dirty) this._dirty = false; }; - -GLSubpath.prototype._computeLocalBoundingBox = function() { - this._LocalBBoxMin = [Infinity, Infinity, Infinity]; - this._LocalBBoxMax = [-Infinity, -Infinity, -Infinity]; - var numPoints = this._LocalPoints.length; - if (numPoints === 0) { - this._LocalBBoxMin = [0, 0, 0]; - this._LocalBBoxMax = [0, 0, 0]; - } else { - for (var i=0;i pt[d]) { - this._LocalBBoxMin[d] = pt[d]; - } - if (this._LocalBBoxMax[d] < pt[d]) { - this._LocalBBoxMax[d] = pt[d]; - } - }//for every dimension d from 0 to 2 - } - } - - //increase the bbox given the stroke width - var halfSW = this._strokeWidth*0.5; - this._LocalBBoxMin[0]-= halfSW;this._LocalBBoxMin[1]-= halfSW; - this._LocalBBoxMax[0]+= halfSW;this._LocalBBoxMax[1]+= halfSW; +GLSubpath.prototype.offsetPerBBoxMin = function() +{ + //offset the anchor and sample coordinates such that the min point of the bbox is at [0,0,0] + this.translateAnchors(-this._BBoxMin[0], -this._BBoxMin[1], -this._BBoxMin[2]); + this.translateSamples(-this._BBoxMin[0], -this._BBoxMin[1], -this._BBoxMin[2]); + this._BBoxMax[0]-= this._BBoxMin[0]; + this._BBoxMax[1]-= this._BBoxMin[1]; + this._BBoxMax[2]-= this._BBoxMin[2]; + this._BBoxMin[0] = this._BBoxMin[1] = this._BBoxMin[2] = 0; }; GLSubpath.prototype.computeBoundingBox = function(useSamples){ this._BBoxMin = [Infinity, Infinity, Infinity]; this._BBoxMax = [-Infinity, -Infinity, -Infinity]; if (useSamples) { - var numPoints = this._samples.length/3; + var numPoints = this._Samples.length; if (numPoints === 0) { this._BBoxMin = [0, 0, 0]; this._BBoxMax = [0, 0, 0]; } else { for (var i=0;i pt[d]) { this._BBoxMin[d] = pt[d]; @@ -1557,135 +1013,10 @@ GLSubpath.prototype._clamp = function (v, min, max) { return v; }; - //input: point sIn in stage-world space, planeMidPt in stage-world space, matrix planeMat that rotates plane into XY (parallel to view plane), inverse of planeMat -//returns: sIn 'unprojected' -GLSubpath.prototype.unprojectPoint = function ( sIn, planeMidPt, planeMat, planeMatInv) { - var s = sIn.slice(0); - s[0] -= planeMidPt[0]; s[1] -= planeMidPt[1]; //bring s to the center of the plane - - // unproject the point s - var i; - var viewZ = 1400; - if (MathUtils.fpCmp(viewZ,-s[2]) !== 0){ - z = s[2]*viewZ/(viewZ + s[2]); - var x = s[0]*(viewZ - z)/viewZ, - y = s[1]*(viewZ - z)/viewZ; - s[0] = x; s[1] = y; s[2] = z; - } - - // add the translation back in - s[0] += planeMidPt[0]; s[1] += planeMidPt[1]; - - return s; -}; - -GLSubpath.prototype.computeUnprojectedNDC = function (pos, bboxMid, bboxDim, r, l, t, b, z, zn) { - //unproject pos from stageworld to unprojected state - var ppos = this.unprojectPoint(pos, this._planeCenter, this._planeMat, this._planeMatInv); - - //make the coordinates lie in [-1,1] - var x = (ppos[0] - bboxMid[0]) / bboxDim[0]; - var y = -(ppos[1] - bboxMid[1]) / bboxDim[1]; - - //x and y should never be outside the [-1,1] range - x = this._clamp(x, -1, 1); - y = this._clamp(y, -1, 1); - - //apply the perspective transform - x *= -z * (r - l) / (2.0 * zn); - y *= -z * (t - b) / (2.0 * zn); - - ppos[0] = x; - ppos[1] = y; - ppos[2] = 0; //z; - return ppos; -}; - -GLSubpath.prototype.makeStrokeMaterial = function() -{ - var strokeMaterial; - if (this.getStrokeMaterial()) { - strokeMaterial = this.getStrokeMaterial().dup(); - } else { - strokeMaterial = new FlatMaterial(); - } - - if (strokeMaterial) { - strokeMaterial.init(); - //if(!this.getStrokeMaterial() && this._strokeColor) - if(this._strokeColor) { - strokeMaterial.setProperty("color", this._strokeColor); - } - } - - this._materialArray.push( strokeMaterial ); - this._materialTypeArray.push( "stroke" ); - - return strokeMaterial; -}; - -GLSubpath.prototype.makeFillMaterial = function() { - var fillMaterial; - if (this.getFillMaterial()) { - fillMaterial = this.getFillMaterial().dup(); - } else { - fillMaterial = new FlatMaterial(); - } - - if (fillMaterial) { - fillMaterial.init(); - //if(!this.getFillMaterial() && this._fillColor) - if (this._fillColor) { - fillMaterial.setProperty("color", this._fillColor); - } - } - - this._materialArray.push( fillMaterial ); - this._materialTypeArray.push( "fill" ); - - return fillMaterial; -}; GLSubpath.prototype.getNearVertex = function( eyePt, dir ){ - //todo BUILD A BBOX USING LOCAL COORD. (NO z NEEDED) - - //get the parameters used for computing perspective transformation - var bboxDim = []; - var bboxMid = []; - bboxDim[0] = 0.5 * (this._BBoxMax[0] - this._BBoxMin[0]); - bboxMid[0] = 0.5 * (this._BBoxMax[0] + this._BBoxMin[0]); - bboxDim[1] = 0.5 * (this._BBoxMax[1] - this._BBoxMin[1]); - bboxMid[1] = 0.5 * (this._BBoxMax[1] + this._BBoxMin[1]); - bboxDim[2] = 0.5 * (this._BBoxMax[2] - this._BBoxMin[2]); - bboxMid[2] = 0.5 * (this._BBoxMax[2] + this._BBoxMin[2]); - - // convert the stroke vertices into normalized device coordinates - var world = this.getWorld(); - if (!world) return null; - var aspect = world.getAspect(); - var zn = world.getZNear(), zf = world.getZFar(); - var t = zn * Math.tan(world.getFOV() * Math.PI / 360.0), //top of the frustum - b = -t, //bottom - r = aspect * t, //right - l = -r; //left - - // calculate the object coordinates from their NDC coordinates - var z = -world.getViewDistance(); - - // the eyePt and dir are in WebGL space...we need to convert each anchor point into the WebGL space - var numAnchors = this._Anchors.length; - var selAnchorPpos = null; - var minDistance = Infinity; - for (var i = 0; i < numAnchors; i++) { - var anchorPos = [this._Anchors[i].getPosX(), this._Anchors[i].getPosY(), this._Anchors[i].getPosZ()]; - var ppos = this.computeUnprojectedNDC(anchorPos, bboxMid, bboxDim, r, l, t, b, z, zn); - var dist = MathUtils.distPointToRay(ppos, eyePt, dir); - if (dist < minDistance) { - selAnchorPpos = ppos; - minDistance = dist; - } - } - return selAnchorPpos; + //todo fill in this function + return null; }; GLSubpath.prototype.getNearPoint = function( eyePt, dir ){ @@ -1735,11 +1066,6 @@ GLSubpath.prototype.exportJSON = function() { retObject.canvasLeft = this._canvasLeft; retObject.canvasTop = this._canvasTop; - retObject.planeCenter = [this._planeCenter[0],this._planeCenter[1],this._planeCenter[2]]; - retObject.planeMat = this._planeMat; - retObject.planeMatInv = this._planeMatInv; - retObject.dragPlane = [this._dragPlane[0],this._dragPlane[1],this._dragPlane[2],this._dragPlane[3]]; - //stroke appearance properties retObject.strokeWidth = this._strokeWidth; retObject.strokeColor = this._strokeColor; @@ -1773,11 +1099,6 @@ GLSubpath.prototype.importJSON = function(jo) { //location of the canvas for this object this._canvasLeft = jo.canvasLeft; this._canvasTop = jo.canvasTop; - - this._planeCenter = [jo.planeCenter[0],jo.planeCenter[1],jo.planeCenter[2]]; - this._planeMat = jo.planeMat; - this._planeMatInv = jo.planeMatInv; - this._dragPlane = [jo.dragPlane[0],jo.dragPlane[1],jo.dragPlane[2],jo.dragPlane[3]]; //stroke appearance properties this._strokeWidth = jo.strokeWidth; @@ -1785,7 +1106,6 @@ GLSubpath.prototype.importJSON = function(jo) { this._fillColor = jo.fillColor; this._dirty = true; - this._isAnchorLocalDirty = true; }; GLSubpath.prototype.import = function( importStr ) { diff --git a/js/tools/PenTool.js b/js/tools/PenTool.js index 5145eb39..06aac46c 100755 --- a/js/tools/PenTool.js +++ b/js/tools/PenTool.js @@ -18,8 +18,6 @@ var ViewUtils = require("js/helper-classes/3D/view-utils").ViewUtils; var AnchorPoint = require("js/lib/geom/anchor-point").AnchorPoint; var SubPath = require("js/lib/geom/sub-path").SubPath; -//todo remove this global var -var g_DoPenToolMouseMove = true; exports.PenTool = Montage.create(ShapeTool, { @@ -35,14 +33,11 @@ exports.PenTool = Montage.create(ShapeTool, { _parentNode: { enumerable: false, value: null, writable: true }, _toolsPropertiesContainer: { enumerable: false, value: null, writable: true }, - // Need to keep track of current mouse position for KEY modifiers event which do not have mouse coordinates - _currentX: { value: 0, writable: true }, - _currentY: { value: 0, writable: true }, - - //the subpaths are what is displayed on the screen currently, with _selectedSubpath being the active one currently being edited - _selectedSubpath: { value: null, writable: true }, - _makeMultipleSubpaths: { value: true, writable: true }, //set this to true if you want to keep making subpaths after closing current subpath + //set this to true if you want to keep making subpaths after closing current subpath (debugging only) + _makeMultipleSubpaths: { value: true, writable: true }, + //set this to false if you don't want the mouse move handler being called when the mouse is not down (debugging only) + _trackMouseMoveWhenUp: {value: true, writable: false}, //whether the user has held down the Alt key _isAltDown: { value: false, writable: true }, @@ -50,7 +45,8 @@ exports.PenTool = Montage.create(ShapeTool, { //whether the user has held down the Esc key _isEscapeDown: {value: false, writable: true }, - //whether we have just started a new path (set true in mousedown, and set false in mouse up + //whether we have just started a new path (may set true in mousedown, and always set false in mouse up + //todo this seems to be unnecessary _isNewPath: {value: false, writable: true}, //whether we have clicked one of the endpoints after entering the pen tool in ENTRY_SELECT_PATH edit mode @@ -59,22 +55,26 @@ exports.PenTool = Montage.create(ShapeTool, { //when the user wants to place a selected anchor point on top of another point, this is the target where the point will be placed _snapTargetIndex: { value: -1, writable: true }, - //whether or not we're using webgl for drawing - _useWebGL: {value: false, writable: false }, + //index of the anchor point that the user has hovered over + _hoveredAnchorIndex: {value: -1, writable: true}, - //the canvas created by the pen tool...this is grown or shrunk with the path (if the canvas was not already provided) - _penCanvas: { value: null, writable: true }, + //whether or not we're using webgl for drawing (set to false until we have webgl-ready stroke and fill regions) + _useWebGL: {value: false, writable: false }, - //the plane matrix for the first click...so the entire path is on the same plane - _penPlaneMat: { value: null, writable: true }, + //the _selectedSubpath is the active subpath currently being edited + _selectedSubpath: { value: null, writable: true }, - //the plane equation (in stage world space) for the current path being drawn - _dragPlane: {value: null, writable: true}, + //the canvas for the selected subpath...this is grown or shrunk by the pen tool with the subpath (if the canvas was not already provided) + _selectedSubpathCanvas: { value: null, writable: true }, - //index of the anchor point that the user has hovered over - _hoveredAnchorIndex: {value: -1, writable: true}, + //the plane matrix for the first click...so the entire path is on the same plane + // todo this might be unnecessary as we can get this from element mediator (but that may be slow) + _selectedSubpathPlaneMat: { value: null, writable: true }, - //constants used for picking points --- NOTE: these should be user-settable parameters + //the center of the subpath center in stageworld space + _selectedSubpathCanvasCenter: {value: null, writable: true}, + + //constants used for picking points --- todo: these should be user-settable parameters _PICK_POINT_RADIUS: { value: 4, writable: false }, _DISPLAY_ANCHOR_RADIUS: { value: 5, writable: false }, _DISPLAY_SELECTED_ANCHOR_RADIUS: { value: 10, writable: false }, @@ -89,58 +89,46 @@ exports.PenTool = Montage.create(ShapeTool, { EDIT_PREV_NEXT: { value: 8, writable: false }, _editMode: { value: this.EDIT_NONE, writable: true }, - //constants used for selection modes on entry to pen tool (mutually exclusive) + //constants used for selection modes on entry to pen tool (mutually exclusive i.e. cannot be OR-ed) ENTRY_SELECT_NONE: { value: 0, writable: false}, ENTRY_SELECT_CANVAS: { value: 1, writable: false}, ENTRY_SELECT_PATH: { value: 2, writable: false}, _entryEditMode: {value: this.ENTRY_SELECT_NONE, writable: true}, - - _getUnsnappedPosition: { - value: function(x,y){ + // get the stage world position corresponding to the (x,y) mouse event position by querying the snap manager + // but temporarily turning off all snapping + _getMouseEventPosition : { + value: function(x,y, getStageWorld, doSnap){ var elemSnap = snapManager.elementSnapEnabled(); var gridSnap = snapManager.gridSnapEnabled(); var alignSnap = snapManager.snapAlignEnabled(); - snapManager.enableElementSnap(false); - snapManager.enableGridSnap(false); - snapManager.enableSnapAlign(false); + if (!doSnap){ + snapManager.enableElementSnap(false); + snapManager.enableGridSnap(false); + snapManager.enableSnapAlign(false); + } var point = webkitConvertPointFromPageToNode(this.application.ninja.stage.canvas, new WebKitPoint(x,y)); - var unsnappedpos = //snapManager.snap(point.x, point.y, false).calculateStageWorldPoint(); - DrawingToolBase.getHitRecPos(snapManager.snap(point.x, point.y, false)); - this._dragPlane = snapManager.getDragPlane(); + var pos; + if (getStageWorld){ + pos = (snapManager.snap(point.x, point.y, false)).calculateStageWorldPoint(); + } else { + pos = (snapManager.snap(point.x, point.y, false)).getScreenPoint(); + } + var dragPlane = snapManager.getDragPlane(); - snapManager.enableElementSnap(elemSnap); - snapManager.enableGridSnap(gridSnap); - snapManager.enableSnapAlign(alignSnap); + if (!doSnap){ + snapManager.enableElementSnap(elemSnap); + snapManager.enableGridSnap(gridSnap); + snapManager.enableSnapAlign(alignSnap); + } - return unsnappedpos; + return [pos, dragPlane]; } }, - _getUnsnappedScreenPosition: { - value: function(x,y){ - var elemSnap = snapManager.elementSnapEnabled(); - var gridSnap = snapManager.gridSnapEnabled(); - var alignSnap = snapManager.snapAlignEnabled(); - - snapManager.enableElementSnap(false); - snapManager.enableGridSnap(false); - snapManager.enableSnapAlign(false); - - var point = webkitConvertPointFromPageToNode(this.application.ninja.stage.canvas, new WebKitPoint(x,y)); - var unsnappedpos = (snapManager.snap(point.x, point.y, false)).getScreenPoint(); - this._dragPlane = snapManager.getDragPlane(); - - snapManager.enableElementSnap(elemSnap); - snapManager.enableGridSnap(gridSnap); - snapManager.enableSnapAlign(alignSnap); - - return unsnappedpos; - } - }, ShowToolProperties: { value: function () { this._penView = PenView.create(); @@ -152,6 +140,43 @@ exports.PenTool = Montage.create(ShapeTool, { }, + //use the snap manager to build