From 3fd098981077e40841c013a8ac305036d08a215b Mon Sep 17 00:00:00 2001 From: Pushkar Joshi Date: Tue, 27 Mar 2012 15:40:37 -0700 Subject: added cursors to the pen tool UI AND change cursor when the user hovers over an anchor point or handles of thte selected anchor --- js/lib/geom/sub-path.js | 51 +++++++++++++++++++++++++++++++------------------ js/tools/PenTool.js | 11 ++++++----- 2 files changed, 38 insertions(+), 24 deletions(-) (limited to 'js') diff --git a/js/lib/geom/sub-path.js b/js/lib/geom/sub-path.js index 7046673e..9bd9ad9a 100755 --- a/js/lib/geom/sub-path.js +++ b/js/lib/geom/sub-path.js @@ -567,37 +567,50 @@ GLSubpath.prototype._isWithinBoundingBox = function(point, ctrlPts, radius) { return true; }; +GLSubpath.prototype._checkAnchorIntersection = function(pickX, pickY, pickZ, radSq, anchorIndex, minDistance) { + var distSq = this._Anchors[anchorIndex].getDistanceSq(pickX, pickY, pickZ); + //check the anchor point + if (distSq < radSq && distSq=0 && this._selectedAnchorIndexx || this._BBoxMin[1]>y || this._BBoxMin[2]>z){ return false; } diff --git a/js/tools/PenTool.js b/js/tools/PenTool.js index 779b7f16..39a1b15a 100755 --- a/js/tools/PenTool.js +++ b/js/tools/PenTool.js @@ -348,16 +348,17 @@ exports.PenTool = Montage.create(ShapeTool, { var currMousePos = this._getUnsnappedPosition(event.pageX, event.pageY); if (currMousePos && this._selectedSubpath ){ - var selAnchor = this._selectedSubpath.pickAnchor(currMousePos[0], currMousePos[1], currMousePos[2], this._PICK_POINT_RADIUS); - if (selAnchor >=0) { - this._hoveredAnchorIndex = selAnchor; + var selAnchorRetCode = this._selectedSubpath.pickAnchor(currMousePos[0], currMousePos[1], currMousePos[2], this._PICK_POINT_RADIUS); + if (selAnchorRetCode[0] >=0) { + this._hoveredAnchorIndex = selAnchorRetCode[0]; + var cursor = "url('images/cursors/penCursors/Pen_anchorSelect.png') 0 0, default"; + this.application.ninja.stage.drawingCanvas.style.cursor = cursor; } else { //detect if the current mouse position will hit the path var pathHitTestData = this._selectedSubpath.pathHitTest(currMousePos[0], currMousePos[1], currMousePos[2], this._PICK_POINT_RADIUS); if (pathHitTestData[0]!==-1){ //change the cursor - var cursor = "url('images/cursors/penAdd.png') 10 10,default"; - this.application.ninja.stage.drawingCanvas.style.cursor = cursor; + } } } -- cgit v1.2.3 From f0433bb5dfc0e4cd759efd398f84e9cb7ca548b0 Mon Sep 17 00:00:00 2001 From: Pushkar Joshi Date: Wed, 28 Mar 2012 14:14:34 -0700 Subject: change cursors for pen tool realtime feedback....still need to position the cursors correctly over the exact mouse position AND reduce the radius (distance threshold) for picking paths --- js/tools/PenTool.js | 22 +++++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) (limited to 'js') diff --git a/js/tools/PenTool.js b/js/tools/PenTool.js index 39a1b15a..57da4698 100755 --- a/js/tools/PenTool.js +++ b/js/tools/PenTool.js @@ -72,7 +72,7 @@ exports.PenTool = Montage.create(ShapeTool, { _hoveredAnchorIndex: {value: -1, writable: true}, //constants used for picking points --- NOTE: these should be user-settable parameters - _PICK_POINT_RADIUS: { value: 10, writable: false }, + _PICK_POINT_RADIUS: { value: 4, writable: false }, _DISPLAY_ANCHOR_RADIUS: { value: 5, writable: false }, _DISPLAY_SELECTED_ANCHOR_RADIUS: { value: 10, writable: false }, _DISPLAY_SELECTED_ANCHOR_PREV_RADIUS: { value: 2, writable: false }, @@ -279,7 +279,8 @@ exports.PenTool = Montage.create(ShapeTool, { this._hoveredAnchorIndex = -1; //set the cursor to be the default cursor - this.application.ninja.stage.drawingCanvas.style.cursor = "auto"; + this.application.ninja.stage.drawingCanvas.style.cursor = //"auto"; + "url('images/cursors/penCursors/Pen_newPath.png') 0 0, default"; if (this._isDrawing) { var point = webkitConvertPointFromPageToNode(this.application.ninja.stage.canvas, new WebKitPoint(event.pageX, event.pageY)); @@ -351,14 +352,25 @@ exports.PenTool = Montage.create(ShapeTool, { var selAnchorRetCode = this._selectedSubpath.pickAnchor(currMousePos[0], currMousePos[1], currMousePos[2], this._PICK_POINT_RADIUS); if (selAnchorRetCode[0] >=0) { this._hoveredAnchorIndex = selAnchorRetCode[0]; + var lastAnchorIndex = this._selectedSubpath.getNumAnchors()-1; var cursor = "url('images/cursors/penCursors/Pen_anchorSelect.png') 0 0, default"; + if (this._selectedSubpath.getIsClosed()===false){ + if (this._entryEditMode === this.ENTRY_SELECT_PATH && !this._isPickedEndPointInSelectPathMode && (this._hoveredAnchorIndex===0 || this._hoveredAnchorIndex===lastAnchorIndex)){ + //if we're in SELECT_PATH mode, have not yet clicked on the end anchors, AND we hovered over one of the end anchors + cursor = "url('images/cursors/penCursors/Pen_append.png') 0 0, default"; + } else if ( this._selectedSubpath.getSelectedAnchorIndex()===lastAnchorIndex && this._hoveredAnchorIndex===0) { + //if we've selected the last anchor and hover over the first anchor + cursor = "url('images/cursors/penCursors/Pen_closePath.png') 0 0, default"; + } + } //if path is not closed this.application.ninja.stage.drawingCanvas.style.cursor = cursor; } else { - //detect if the current mouse position will hit the path - var pathHitTestData = this._selectedSubpath.pathHitTest(currMousePos[0], currMousePos[1], currMousePos[2], this._PICK_POINT_RADIUS); + //detect if the current mouse position will hit the path (such that clicking here will insert a new anchor) + var pathHitTestData = this._selectedSubpath.pathHitTest(currMousePos[0], currMousePos[1], currMousePos[2], this._PICK_POINT_RADIUS*0.5); if (pathHitTestData[0]!==-1){ //change the cursor - + var cursor = "url('images/cursors/penCursors/Pen_plus.png') 0 0, default"; + this.application.ninja.stage.drawingCanvas.style.cursor = cursor; } } } -- cgit v1.2.3 From defde265799bb8d6826bb368f04168612e7feb5a Mon Sep 17 00:00:00 2001 From: Pushkar Joshi Date: Wed, 28 Mar 2012 15:29:57 -0700 Subject: adjust the position for the pen tool feedback cursor AND fix a bug where everything selected gets deleted (even if we have a selected subpath) --- js/tools/PenTool.js | 98 +++++++++++++++++++++++++++++------------------------ 1 file changed, 53 insertions(+), 45 deletions(-) (limited to 'js') diff --git a/js/tools/PenTool.js b/js/tools/PenTool.js index 57da4698..31dadb80 100755 --- a/js/tools/PenTool.js +++ b/js/tools/PenTool.js @@ -278,9 +278,15 @@ exports.PenTool = Montage.create(ShapeTool, { this.application.ninja.stage.clearDrawingCanvas(); this._hoveredAnchorIndex = -1; - //set the cursor to be the default cursor - this.application.ninja.stage.drawingCanvas.style.cursor = //"auto"; - "url('images/cursors/penCursors/Pen_newPath.png') 0 0, default"; + //set the cursor to be the default cursor (depending on whether the selected subpath has any points yet) + if (this._selectedSubpath && this._selectedSubpath.getNumAnchors()>0){ + this.application.ninja.stage.drawingCanvas.style.cursor = //"auto"; + "url('images/cursors/penCursors/Pen_.png') 5 1, default"; + } + else { + this.application.ninja.stage.drawingCanvas.style.cursor = //"auto"; + "url('images/cursors/penCursors/Pen_newPath.png') 5 1, default"; + } if (this._isDrawing) { var point = webkitConvertPointFromPageToNode(this.application.ninja.stage.canvas, new WebKitPoint(event.pageX, event.pageY)); @@ -353,14 +359,14 @@ exports.PenTool = Montage.create(ShapeTool, { if (selAnchorRetCode[0] >=0) { this._hoveredAnchorIndex = selAnchorRetCode[0]; var lastAnchorIndex = this._selectedSubpath.getNumAnchors()-1; - var cursor = "url('images/cursors/penCursors/Pen_anchorSelect.png') 0 0, default"; + var cursor = "url('images/cursors/penCursors/Pen_anchorSelect.png') 5 1, default"; if (this._selectedSubpath.getIsClosed()===false){ if (this._entryEditMode === this.ENTRY_SELECT_PATH && !this._isPickedEndPointInSelectPathMode && (this._hoveredAnchorIndex===0 || this._hoveredAnchorIndex===lastAnchorIndex)){ //if we're in SELECT_PATH mode, have not yet clicked on the end anchors, AND we hovered over one of the end anchors - cursor = "url('images/cursors/penCursors/Pen_append.png') 0 0, default"; + cursor = "url('images/cursors/penCursors/Pen_append.png') 5 1, default"; } else if ( this._selectedSubpath.getSelectedAnchorIndex()===lastAnchorIndex && this._hoveredAnchorIndex===0) { //if we've selected the last anchor and hover over the first anchor - cursor = "url('images/cursors/penCursors/Pen_closePath.png') 0 0, default"; + cursor = "url('images/cursors/penCursors/Pen_closePath.png') 5 1, default"; } } //if path is not closed this.application.ninja.stage.drawingCanvas.style.cursor = cursor; @@ -369,7 +375,7 @@ exports.PenTool = Montage.create(ShapeTool, { var pathHitTestData = this._selectedSubpath.pathHitTest(currMousePos[0], currMousePos[1], currMousePos[2], this._PICK_POINT_RADIUS*0.5); if (pathHitTestData[0]!==-1){ //change the cursor - var cursor = "url('images/cursors/penCursors/Pen_plus.png') 0 0, default"; + var cursor = "url('images/cursors/penCursors/Pen_plus.png') 5 1, default"; this.application.ninja.stage.drawingCanvas.style.cursor = cursor; } } @@ -638,7 +644,8 @@ exports.PenTool = Montage.create(ShapeTool, { // TODO - update the shape's info only. shapeModel will likely need an array of shapes. } - if(newCanvas.elementModel.isShape) + //if(newCanvas.elementModel.isShape) + if (true) { this.application.ninja.selectionController.selectElement(newCanvas); } @@ -681,6 +688,12 @@ exports.PenTool = Montage.create(ShapeTool, { //TODO this will not work if there are multiple shapes in the same canvas canvas.elementModel.shapeModel.GLGeomObj = subpath; + + //if(newCanvas.elementModel.isShape) + if (true) + { + this.application.ninja.selectionController.selectElement(canvas); + } } //else of if (!canvas) { } //value: function (w, h, planeMat, midPt, canvas) { }, //RenderShape: { @@ -1047,37 +1060,32 @@ exports.PenTool = Montage.create(ShapeTool, { handleDelete:{ value: function(event){ - var len = this.application.ninja.selectedElements.length; - if (len===0) { - //clear the selected subpath...the only new additions to this function w.r.t. ToolBase - if (this._selectedSubpath){ - if (this._selectedSubpath.getSelectedAnchorIndex()>=0){ - this._hoveredAnchorIndex=-1; - this._selectedSubpath.removeAnchor(this._selectedSubpath.getSelectedAnchorIndex()); - this._selectedSubpath.createSamples(); - //clear the canvas - this.application.ninja.stage.clearDrawingCanvas();//stageManagerModule.stageManager.clearDrawingCanvas(); - this.DrawSubpathAnchors(this._selectedSubpath); - this.ShowSelectedSubpath(); - } - else { - this._selectedSubpath.clearAllAnchors(); //perhaps unnecessary - this._selectedSubpath = null; - //clear the canvas - this.application.ninja.stage.clearDrawingCanvas();//stageManagerModule.stageManager.clearDrawingCanvas(); - - //undo/redo...go through ElementController and NJEvent - var els = []; - ElementController.removeElement(this._penCanvas); - els.push(this._penCanvas); - NJevent( "deleteSelection", els ); - this._penCanvas = null; - } + //clear the selected subpath...the only new additions to this function w.r.t. ToolBase + if (this._selectedSubpath){ + if (this._selectedSubpath.getSelectedAnchorIndex()>=0){ + this._hoveredAnchorIndex=-1; + this._selectedSubpath.removeAnchor(this._selectedSubpath.getSelectedAnchorIndex()); + this._selectedSubpath.createSamples(); + //clear the canvas + this.application.ninja.stage.clearDrawingCanvas();//stageManagerModule.stageManager.clearDrawingCanvas(); + this.DrawSubpathAnchors(this._selectedSubpath); + this.ShowSelectedSubpath(); } - //do nothing if there was no selected subpath and if there was no selection + else { + this._selectedSubpath.clearAllAnchors(); //perhaps unnecessary + this._selectedSubpath = null; + //clear the canvas + this.application.ninja.stage.clearDrawingCanvas();//stageManagerModule.stageManager.clearDrawingCanvas(); + + //undo/redo...go through ElementController and NJEvent + var els = []; + ElementController.removeElement(this._penCanvas); + els.push(this._penCanvas); + NJevent( "deleteSelection", els ); + this._penCanvas = null; + } } else { - //undo/redo...go through ElementMediator (see ElementMediator.handleDeleting() from where the much of this function is copied) //clear the canvas this.application.ninja.stage.clearDrawingCanvas();//stageManagerModule.stageManager.clearDrawingCanvas(); @@ -1090,15 +1098,15 @@ exports.PenTool = Montage.create(ShapeTool, { } NJevent( "deleteSelection", els ); - //clear out the selected path if it exists - if (this._selectedSubpath) { - this._selectedSubpath.clearAllAnchors(); - this._selectedSubpath = null; - if (this._entryEditMode === this.ENTRY_SELECT_PATH){ - this._entryEditMode = this.ENTRY_SELECT_NONE; - } - this._penCanvas = null; - } + //clear out the selected path if it exists + if (this._selectedSubpath) { + this._selectedSubpath.clearAllAnchors(); + this._selectedSubpath = null; + if (this._entryEditMode === this.ENTRY_SELECT_PATH){ + this._entryEditMode = this.ENTRY_SELECT_NONE; + } + this._penCanvas = null; + } } //redraw the stage to update it this.application.ninja.stage.draw(); -- cgit v1.2.3 From 878743cbbb75f2fc84855ca27779597b67ab1a95 Mon Sep 17 00:00:00 2001 From: Pushkar Joshi Date: Mon, 2 Apr 2012 15:25:00 -0700 Subject: render the pen path with local coordinates, but use stage world coordinates to position the canvas on which the path is rendered AND add data-montage- to the ids in the pen and brush reels AND fix a bug with pen stroke transparency not working --- .../brush-properties.reel/brush-properties.html | 2 +- .../pen-properties.reel/pen-properties.html | 2 +- js/helper-classes/3D/view-utils.js | 1 - js/lib/geom/brush-stroke.js | 2 +- js/lib/geom/sub-path.js | 264 +++++++++++++++++++-- js/tools/PenTool.js | 44 ++-- 6 files changed, 269 insertions(+), 46 deletions(-) (limited to 'js') diff --git a/js/components/tools-properties/brush-properties.reel/brush-properties.html b/js/components/tools-properties/brush-properties.reel/brush-properties.html index 98442164..d9c35dc2 100755 --- a/js/components/tools-properties/brush-properties.reel/brush-properties.html +++ b/js/components/tools-properties/brush-properties.reel/brush-properties.html @@ -84,7 +84,7 @@ -
+
diff --git a/js/components/tools-properties/pen-properties.reel/pen-properties.html b/js/components/tools-properties/pen-properties.reel/pen-properties.html index 74a3a747..acbedcac 100755 --- a/js/components/tools-properties/pen-properties.reel/pen-properties.html +++ b/js/components/tools-properties/pen-properties.reel/pen-properties.html @@ -39,7 +39,7 @@ -
+
diff --git a/js/helper-classes/3D/view-utils.js b/js/helper-classes/3D/view-utils.js index a72b7906..f60b953a 100755 --- a/js/helper-classes/3D/view-utils.js +++ b/js/helper-classes/3D/view-utils.js @@ -242,7 +242,6 @@ exports.ViewUtils = Montage.create(Component, { var worldPt = MathUtils.transformPoint( viewPt, mat ); var stageWorldPt = this.postViewToStageWorld( worldPt, elt ); this.popViewportObj(); - return stageWorldPt; } }, diff --git a/js/lib/geom/brush-stroke.js b/js/lib/geom/brush-stroke.js index 22209815..e93c9382 100755 --- a/js/lib/geom/brush-stroke.js +++ b/js/lib/geom/brush-stroke.js @@ -413,7 +413,7 @@ var BrushStroke = function GLBrushStroke() { this._LocalPoints[i][1]+= halfheight; //store the original points - this._OrigLocalPoints .push([this._LocalPoints[i][0],this._LocalPoints[i][1],this._LocalPoints[i][2]]); + this._OrigLocalPoints.push([this._LocalPoints[i][0],this._LocalPoints[i][1],this._LocalPoints[i][2]]); } //update the bbox with the same adjustment as was made for the local points above this._BBoxMax[0]+= halfwidth;this._BBoxMin[0]+= halfwidth; diff --git a/js/lib/geom/sub-path.js b/js/lib/geom/sub-path.js index 9bd9ad9a..19a1da3b 100755 --- a/js/lib/geom/sub-path.js +++ b/js/lib/geom/sub-path.js @@ -12,6 +12,7 @@ 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]]; @@ -33,7 +34,7 @@ function sortNumberDescending(a,b){ function SegmentIntersections(){ this.paramArray = []; } - +*/ /////////////////////////////////////////////////////////////////////// // Class GLSubpath // representation a sequence of cubic bezier curves. @@ -49,15 +50,22 @@ var GLSubpath = function GLSubpath() { this._BBoxMax = [0, 0, 0]; this._isClosed = false; - this._samples = []; //polyline representation of this curve - 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._samples = []; //polyline representation of this curve in stage world 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._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._UnprojectedAnchors = []; //initially set the _dirty bit so we will construct samples this._dirty = true; + //initially set the local dirty bit so we will construct local coordinates + this._isLocalDirty = true; + //whether or not to use the canvas drawing to stroke/fill this._useCanvasDrawing = true; @@ -69,7 +77,7 @@ var GLSubpath = function GLSubpath() { this._canvasY = 0; //stroke information - this._strokeWidth = 0.0; + this._strokeWidth = 1.0; this._strokeColor = [0.4, 0.4, 0.4, 1.0]; this._strokeMaterial = null this._strokeStyle = "Solid"; @@ -109,6 +117,13 @@ 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;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== 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.getLocalBBoxMidInStageWorld = function() { + var ViewUtils = require("js/helper-classes/3D/view-utils").ViewUtils; + //compute the plane center as the midpoint of the local bbox converted to stage world space + var bboxWidth=0, bboxHeight=0; + var bboxMid=[0,0,0]; + bboxWidth = this._LocalBBoxMax[0] - this._LocalBBoxMin[0]; + bboxHeight = this._LocalBBoxMax[1] - this._LocalBBoxMin[1]; + bboxMid = [0.5 * (this._LocalBBoxMax[0] + this._LocalBBoxMin[0]), 0.5 * (this._LocalBBoxMax[1] + this._LocalBBoxMin[1]), 0.5 * (this._LocalBBoxMax[2] + this._LocalBBoxMin[2])]; + var planeCenter = ViewUtils.localToStageWorld(bboxMid, this._canvas); + planeCenter[0]+=400; planeCenter[1]+=300; //todo replace these lines with the correct call for the offset + console.log("PEN: local midPt: "+ bboxMid +", stageWorld midPt: "+planeCenter); + return planeCenter; +} + +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.computeBoundingBox = function(useSamples){ this._BBoxMin = [Infinity, Infinity, Infinity]; this._BBoxMax = [-Infinity, -Infinity, -Infinity]; @@ -1102,7 +1314,7 @@ GLSubpath.prototype.computeBoundingBox = function(useSamples){ for (var d = 0; d < 3; d++) { this._BBoxMin[d]-= this._strokeWidth/2; this._BBoxMax[d]+= this._strokeWidth/2; - }//for every dimension d from 0 to 2 + }//for every dimension d from 0 to 3 }; //returns v such that it is in [min,max] diff --git a/js/tools/PenTool.js b/js/tools/PenTool.js index 31dadb80..98423113 100755 --- a/js/tools/PenTool.js +++ b/js/tools/PenTool.js @@ -416,18 +416,30 @@ exports.PenTool = Montage.create(ShapeTool, { ShowSelectedSubpath:{ value: function() { if (this._selectedSubpath){ + this._selectedSubpath.setPlaneMatrix(this._penPlaneMat); + var planeMatInv = glmat4.inverse( this._penPlaneMat, [] ); + this._selectedSubpath.setPlaneMatrixInverse(planeMatInv); + this._selectedSubpath.createSamples(); //dirty bit is checked here - var bboxMin = this._selectedSubpath.getBBoxMin(); - var bboxMax = this._selectedSubpath.getBBoxMax(); + this._selectedSubpath.buildLocalCoord(); //local dirty bit is checked here + + //build the width and height of this canvas by looking at local coordinates (X and Y needed only) + var bboxMin = this._selectedSubpath.getLocalBBoxMin(); + var bboxMax = this._selectedSubpath.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._selectedSubpath.getBBoxMin(); + bboxMax = this._selectedSubpath.getBBoxMax(); var bboxMid = [0.5 * (bboxMax[0] + bboxMin[0]), 0.5 * (bboxMax[1] + bboxMin[1]), 0.5 * (bboxMax[2] + bboxMin[2])]; + this._selectedSubpath.setPlaneCenter(bboxMid); this._selectedSubpath.setCanvasX(bboxMid[0]); this._selectedSubpath.setCanvasY(bboxMid[1]); //call render shape with the bbox width and height - this.RenderShape(bboxWidth, bboxHeight, this._penPlaneMat, bboxMid, this._penCanvas); + this.RenderShape(bboxWidth, bboxHeight, bboxMid, this._penPlaneMat, this._penCanvas); } } }, @@ -592,7 +604,7 @@ exports.PenTool = Montage.create(ShapeTool, { }, RenderShape: { - value: function (w, h, planeMat, midPt, canvas) { + value: function (w, h, midPt, planeMat, canvas) { if ((Math.floor(w) === 0) || (Math.floor(h) === 0)) { return; } @@ -614,10 +626,6 @@ exports.PenTool = Montage.create(ShapeTool, { var subpath = this._selectedSubpath; //new GLSubpath(); subpath.setWorld(world); subpath.setCanvas(newCanvas); - subpath.setPlaneMatrix(planeMat); - var planeMatInv = glmat4.inverse( planeMat, [] ); - subpath.setPlaneMatrixInverse(planeMatInv); - subpath.setPlaneCenter(midPt); world.addObject(subpath); world.render(); @@ -662,11 +670,19 @@ exports.PenTool = Montage.create(ShapeTool, { if (this._entryEditMode !== this.ENTRY_SELECT_CANVAS){ //update the left and top of the canvas element var canvasArray=[canvas]; - ElementMediator.setProperty(canvasArray, "left", [parseInt(left)+"px"],"Changing", "penTool");//DocumentControllerModule.DocumentController.SetElementStyle(canvas, "left", parseInt(left) + "px"); - ElementMediator.setProperty(canvasArray, "top", [parseInt(top) + "px"],"Changing", "penTool");//DocumentControllerModule.DocumentController.SetElementStyle(canvas, "top", parseInt(top) + "px"); + w= Math.round(w); + h = Math.round(h); ElementMediator.setProperty(canvasArray, "width", [w+"px"], "Changing", "penTool");//canvas.width = w; ElementMediator.setProperty(canvasArray, "height", [h+"px"], "Changing", "penTool");//canvas.height = h; - //update the viewport and projection to reflect the new canvas width and height + + //var bboxMid = this._selectedSubpath.getLocalBBoxMidInStageWorld(); + //left = Math.round(bboxMid[0] - 0.5 * w); + //top = Math.round(bboxMid[1] - 0.5 * h); + + ElementMediator.setProperty(canvasArray, "left", [left+"px"],"Changing", "penTool");//DocumentControllerModule.DocumentController.SetElementStyle(canvas, "left", parseInt(left) + "px"); + ElementMediator.setProperty(canvasArray, "top", [top + "px"],"Changing", "penTool");//DocumentControllerModule.DocumentController.SetElementStyle(canvas, "top", parseInt(top) + "px"); + + //update the viewport and projection to reflect the new canvas width and height (todo might be unnecessary since we don't use RDGE for now) world.setViewportFromCanvas(canvas); if (this._useWebGL){ var cam = world.renderer.cameraManager().getActiveCamera(); @@ -677,10 +693,6 @@ exports.PenTool = Montage.create(ShapeTool, { var subpath = this._selectedSubpath; subpath.setDrawingTool(this); - subpath.setPlaneMatrix(planeMat); - var planeMatInv = glmat4.inverse( planeMat, [] ); - subpath.setPlaneMatrixInverse(planeMatInv); - subpath.setPlaneCenter(midPt); subpath.setWorld(world); world.addIfNewObject(subpath); @@ -829,6 +841,7 @@ exports.PenTool = Montage.create(ShapeTool, { ctx.strokeStyle = "green"; //if (subpath.getStrokeColor()) // ctx.strokeStyle = MathUtils.colorToHex( subpath.getStrokeColor() ); + ctx.beginPath(); var p0x = subpath.getAnchor(0).getPosX()+ horizontalOffset; var p0y = subpath.getAnchor(0).getPosY()+ verticalOffset; @@ -881,7 +894,6 @@ exports.PenTool = Montage.create(ShapeTool, { ctx.strokeStyle = "green"; var anchorDelta = 2; var selAnchorDelta = 4; - for (var i = 0; i < numAnchors; i++) { var px = subpath.getAnchor(i).getPosX(); var py = subpath.getAnchor(i).getPosY(); -- cgit v1.2.3 From c838f85d28acbf2fe208a4358aef9cac73b65fbc Mon Sep 17 00:00:00 2001 From: Pushkar Joshi Date: Tue, 3 Apr 2012 10:14:49 -0700 Subject: First attempt at preventing the drifting of the canvas due to floating point roundoff errors when constantly changing stroke width --- js/lib/geom/sub-path.js | 27 +++++- js/tools/PenTool.js | 231 ++++++++++++++++++++++-------------------------- 2 files changed, 133 insertions(+), 125 deletions(-) (limited to 'js') diff --git a/js/lib/geom/sub-path.js b/js/lib/geom/sub-path.js index 19a1da3b..4ded360c 100755 --- a/js/lib/geom/sub-path.js +++ b/js/lib/geom/sub-path.js @@ -866,6 +866,27 @@ GLSubpath.prototype.getStrokeWidth = function () { return this._strokeWidth; }; +GLSubpath.prototype.translateSubpathPerCanvas = function(elemMediator){ + //check if the canvas was translated + var penCanvasLeft = parseInt(elemMediator.getProperty(this._canvas, "left"));//parseFloat(DocumentControllerModule.DocumentController.GetElementStyle(this._penCanvas, "left")); + var penCanvasTop = parseInt(elemMediator.getProperty(this._canvas, "top"));//parseFloat(DocumentControllerModule.DocumentController.GetElementStyle(this._penCanvas, "top")); + var penCanvasWidth = parseInt(elemMediator.getProperty(this._canvas, "width"));//this._penCanvas.width; + var penCanvasHeight = parseInt(elemMediator.getProperty(this._canvas, "height"));//this._penCanvas.height; + var penCanvasOldX = penCanvasLeft + 0.5 * penCanvasWidth; + var penCanvasOldY = penCanvasTop + 0.5 * penCanvasHeight; + + var translateCanvasX = penCanvasOldX - this._canvasX; + var translateCanvasY = penCanvasOldY - this._canvasY; + + //update the canvasX and canvasY parameters for this subpath and also translate the subpath points (since they're stored in stage world space) + if (Math.abs(translateCanvasX)>=1 || Math.abs(translateCanvasY)>=1){ + this.setCanvasX(translateCanvasX + this._canvasX); + this.setCanvasY(translateCanvasY + this._canvasY); + this.translateAnchors(translateCanvasX, translateCanvasY, 0); + } + this._dirty=true; +}; + GLSubpath.prototype.setStrokeWidth = function (w) { var diffStrokeWidth = w-Math.floor(this._strokeWidth);//if positive, then stroke width grew, else shrunk if (diffStrokeWidth === 0) @@ -874,11 +895,14 @@ GLSubpath.prototype.setStrokeWidth = function (w) { this._strokeWidth = w; this._dirty=true; + var ElementMediator = require("js/mediators/element-mediator").ElementMediator; + this.translateSubpathPerCanvas(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 (X and Y needed only) + //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]; @@ -892,7 +916,6 @@ GLSubpath.prototype.setStrokeWidth = function (w) { var top = Math.round(bboxMid[1] - 0.5 * bboxHeight); var canvasArray=[this._canvas]; - var ElementMediator = require("js/mediators/element-mediator").ElementMediator; ElementMediator.setProperty(canvasArray, "width", [bboxWidth+"px"], "Changing", "penTool");//canvas.width = w; ElementMediator.setProperty(canvasArray, "height", [bboxHeight+"px"], "Changing", "penTool");//canvas.height = h; ElementMediator.setProperty(canvasArray, "left", [left+"px"],"Changing", "penTool");//DocumentControllerModule.DocumentController.SetElementStyle(canvas, "left", parseInt(left) + "px"); diff --git a/js/tools/PenTool.js b/js/tools/PenTool.js index 98423113..0dbefd16 100755 --- a/js/tools/PenTool.js +++ b/js/tools/PenTool.js @@ -393,22 +393,7 @@ exports.PenTool = Montage.create(ShapeTool, { TranslateSelectedSubpathPerPenCanvas:{ value: function() { if (this._penCanvas!==null) { - //obtain the 2D translation of the canvas due to the Selection tool...assuming this is called in Configure - var penCanvasLeft = parseInt(ElementMediator.getProperty(this._penCanvas, "left"));//parseFloat(DocumentControllerModule.DocumentController.GetElementStyle(this._penCanvas, "left")); - var penCanvasTop = parseInt(ElementMediator.getProperty(this._penCanvas, "top"));//parseFloat(DocumentControllerModule.DocumentController.GetElementStyle(this._penCanvas, "top")); - var penCanvasWidth = parseInt(ElementMediator.getProperty(this._penCanvas, "width"));//this._penCanvas.width; - var penCanvasHeight = parseInt(ElementMediator.getProperty(this._penCanvas, "height"));//this._penCanvas.height; - var penCanvasOldX = penCanvasLeft + 0.5 * penCanvasWidth; - var penCanvasOldY = penCanvasTop + 0.5 * penCanvasHeight; - - var translateCanvasX = penCanvasOldX - this._selectedSubpath.getCanvasX(); - var translateCanvasY = penCanvasOldY - this._selectedSubpath.getCanvasY(); - - //update the canvasX and canvasY parameters for this subpath and also translate the subpath points (since they're stored in stage world space) - this._selectedSubpath.setCanvasX(translateCanvasX + this._selectedSubpath.getCanvasX()); - this._selectedSubpath.setCanvasY(translateCanvasY + this._selectedSubpath.getCanvasY()); - this._selectedSubpath.translateAnchors(translateCanvasX, translateCanvasY, 0); - this._selectedSubpath.createSamples(); //updates the bounding box + this._selectedSubpath.translateSubpathPerCanvas(ElementMediator); } } }, @@ -444,6 +429,113 @@ exports.PenTool = Montage.create(ShapeTool, { } }, + RenderShape: { + value: function (w, h, midPt, planeMat, canvas) { + if ((Math.floor(w) === 0) || (Math.floor(h) === 0)) { + return; + } + + var left = Math.round(midPt[0] - 0.5 * w); + var top = Math.round(midPt[1] - 0.5 * h); + + if (!canvas) { + var newCanvas = null; + newCanvas = NJUtils.makeNJElement("canvas", "Subpath", "shape", {"data-RDGE-id": NJUtils.generateRandom()}, true); + var elementModel = TagTool.makeElement(parseInt(w), parseInt(h), planeMat, midPt, newCanvas); + ElementMediator.addElement(newCanvas, elementModel.data, true); + + // create all the GL stuff + var world = this.getGLWorld(newCanvas, this._useWebGL);//this.options.use3D);//this.CreateGLWorld(planeMat, midPt, newCanvas, this._useWebGL);//fillMaterial, strokeMaterial); + //store a reference to this newly created canvas + this._penCanvas = newCanvas; + + var subpath = this._selectedSubpath; //new GLSubpath(); + subpath.setWorld(world); + subpath.setCanvas(newCanvas); + + world.addObject(subpath); + world.render(); + //TODO this will not work if there are multiple shapes in the same canvas + newCanvas.elementModel.shapeModel.GLGeomObj = subpath; + newCanvas.elementModel.shapeModel.shapeCount++; + if(newCanvas.elementModel.shapeModel.shapeCount === 1) + { + newCanvas.elementModel.selection = "Subpath"; + newCanvas.elementModel.pi = "SubpathPi"; + newCanvas.elementModel.shapeModel.strokeSize = this.options.strokeSize.value + " " + this.options.strokeSize.units; + var strokeColor = subpath.getStrokeColor(); + newCanvas.elementModel.shapeModel.stroke = strokeColor; + if(strokeColor) { + newCanvas.elementModel.shapeModel.border = this.application.ninja.colorController.colorToolbar.stroke; + } + newCanvas.elementModel.shapeModel.strokeMaterial = subpath.getStrokeMaterial(); + + newCanvas.elementModel.shapeModel.GLGeomObj = subpath; + newCanvas.elementModel.shapeModel.useWebGl = this.options.use3D; + } + else + { + // TODO - update the shape's info only. shapeModel will likely need an array of shapes. + } + + //if(newCanvas.elementModel.isShape) + if (true) + { + this.application.ninja.selectionController.selectElement(newCanvas); + } + } //if (!canvas) { + else { + + var world = null; + if (canvas.elementModel.shapeModel && canvas.elementModel.shapeModel.GLWorld) { + world = canvas.elementModel.shapeModel.GLWorld; + } else { + world = this.getGLWorld(canvas, this._useWebGL);//this.options.use3D);//this.CreateGLWorld(planeMat, midPt, canvas, this._useWebGL);//fillMaterial, strokeMaterial); + } + + if (this._entryEditMode !== this.ENTRY_SELECT_CANVAS){ + //update the left and top of the canvas element + var canvasArray=[canvas]; + w= Math.round(w); + h = Math.round(h); + ElementMediator.setProperty(canvasArray, "width", [w+"px"], "Changing", "penTool");//canvas.width = w; + ElementMediator.setProperty(canvasArray, "height", [h+"px"], "Changing", "penTool");//canvas.height = h; + + //var bboxMid = this._selectedSubpath.getLocalBBoxMidInStageWorld(); + //left = Math.round(bboxMid[0] - 0.5 * w); + //top = Math.round(bboxMid[1] - 0.5 * h); + + ElementMediator.setProperty(canvasArray, "left", [left+"px"],"Changing", "penTool");//DocumentControllerModule.DocumentController.SetElementStyle(canvas, "left", parseInt(left) + "px"); + ElementMediator.setProperty(canvasArray, "top", [top + "px"],"Changing", "penTool");//DocumentControllerModule.DocumentController.SetElementStyle(canvas, "top", parseInt(top) + "px"); + + //update the viewport and projection to reflect the new canvas width and height (todo might be unnecessary since we don't use RDGE for now) + world.setViewportFromCanvas(canvas); + if (this._useWebGL){ + var cam = world.renderer.cameraManager().getActiveCamera(); + cam.setPerspective(world.getFOV(), world.getAspect(), world.getZNear(), world.getZFar()); + } + } + + var subpath = this._selectedSubpath; + + subpath.setDrawingTool(this); + subpath.setWorld(world); + + world.addIfNewObject(subpath); + world.render(); + + //TODO this will not work if there are multiple shapes in the same canvas + canvas.elementModel.shapeModel.GLGeomObj = subpath; + + //if(newCanvas.elementModel.isShape) + if (true) + { + this.application.ninja.selectionController.selectElement(canvas); + } + } //else of if (!canvas) { + } //value: function (w, h, planeMat, midPt, canvas) { + }, //RenderShape: { + HandleLeftButtonUp: { value: function (event) { if (this._isAltDown) { @@ -603,113 +695,6 @@ exports.PenTool = Montage.create(ShapeTool, { } }, - RenderShape: { - value: function (w, h, midPt, planeMat, canvas) { - if ((Math.floor(w) === 0) || (Math.floor(h) === 0)) { - return; - } - - var left = Math.round(midPt[0] - 0.5 * w); - var top = Math.round(midPt[1] - 0.5 * h); - - if (!canvas) { - var newCanvas = null; - newCanvas = NJUtils.makeNJElement("canvas", "Subpath", "shape", {"data-RDGE-id": NJUtils.generateRandom()}, true); - var elementModel = TagTool.makeElement(parseInt(w), parseInt(h), planeMat, midPt, newCanvas); - ElementMediator.addElement(newCanvas, elementModel.data, true); - - // create all the GL stuff - var world = this.getGLWorld(newCanvas, this._useWebGL);//this.options.use3D);//this.CreateGLWorld(planeMat, midPt, newCanvas, this._useWebGL);//fillMaterial, strokeMaterial); - //store a reference to this newly created canvas - this._penCanvas = newCanvas; - - var subpath = this._selectedSubpath; //new GLSubpath(); - subpath.setWorld(world); - subpath.setCanvas(newCanvas); - - world.addObject(subpath); - world.render(); - //TODO this will not work if there are multiple shapes in the same canvas - newCanvas.elementModel.shapeModel.GLGeomObj = subpath; - newCanvas.elementModel.shapeModel.shapeCount++; - if(newCanvas.elementModel.shapeModel.shapeCount === 1) - { - newCanvas.elementModel.selection = "Subpath"; - newCanvas.elementModel.pi = "SubpathPi"; - newCanvas.elementModel.shapeModel.strokeSize = this.options.strokeSize.value + " " + this.options.strokeSize.units; - var strokeColor = subpath.getStrokeColor(); - newCanvas.elementModel.shapeModel.stroke = strokeColor; - if(strokeColor) { - newCanvas.elementModel.shapeModel.border = this.application.ninja.colorController.colorToolbar.stroke; - } - newCanvas.elementModel.shapeModel.strokeMaterial = subpath.getStrokeMaterial(); - - newCanvas.elementModel.shapeModel.GLGeomObj = subpath; - newCanvas.elementModel.shapeModel.useWebGl = this.options.use3D; - } - else - { - // TODO - update the shape's info only. shapeModel will likely need an array of shapes. - } - - //if(newCanvas.elementModel.isShape) - if (true) - { - this.application.ninja.selectionController.selectElement(newCanvas); - } - } //if (!canvas) { - else { - - var world = null; - if (canvas.elementModel.shapeModel && canvas.elementModel.shapeModel.GLWorld) { - world = canvas.elementModel.shapeModel.GLWorld; - } else { - world = this.getGLWorld(canvas, this._useWebGL);//this.options.use3D);//this.CreateGLWorld(planeMat, midPt, canvas, this._useWebGL);//fillMaterial, strokeMaterial); - } - - if (this._entryEditMode !== this.ENTRY_SELECT_CANVAS){ - //update the left and top of the canvas element - var canvasArray=[canvas]; - w= Math.round(w); - h = Math.round(h); - ElementMediator.setProperty(canvasArray, "width", [w+"px"], "Changing", "penTool");//canvas.width = w; - ElementMediator.setProperty(canvasArray, "height", [h+"px"], "Changing", "penTool");//canvas.height = h; - - //var bboxMid = this._selectedSubpath.getLocalBBoxMidInStageWorld(); - //left = Math.round(bboxMid[0] - 0.5 * w); - //top = Math.round(bboxMid[1] - 0.5 * h); - - ElementMediator.setProperty(canvasArray, "left", [left+"px"],"Changing", "penTool");//DocumentControllerModule.DocumentController.SetElementStyle(canvas, "left", parseInt(left) + "px"); - ElementMediator.setProperty(canvasArray, "top", [top + "px"],"Changing", "penTool");//DocumentControllerModule.DocumentController.SetElementStyle(canvas, "top", parseInt(top) + "px"); - - //update the viewport and projection to reflect the new canvas width and height (todo might be unnecessary since we don't use RDGE for now) - world.setViewportFromCanvas(canvas); - if (this._useWebGL){ - var cam = world.renderer.cameraManager().getActiveCamera(); - cam.setPerspective(world.getFOV(), world.getAspect(), world.getZNear(), world.getZFar()); - } - } - - var subpath = this._selectedSubpath; - - subpath.setDrawingTool(this); - subpath.setWorld(world); - - world.addIfNewObject(subpath); - world.render(); - - //TODO this will not work if there are multiple shapes in the same canvas - canvas.elementModel.shapeModel.GLGeomObj = subpath; - - //if(newCanvas.elementModel.isShape) - if (true) - { -