From 76f2021618c0a6a99a1b855233e353e84ca99467 Mon Sep 17 00:00:00 2001 From: Pushkar Joshi Date: Tue, 13 Mar 2012 11:23:32 -0700 Subject: Add a smoothing amount parameter, and hide options based on checkboxes --- js/lib/geom/brush-stroke.js | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) (limited to 'js/lib/geom/brush-stroke.js') diff --git a/js/lib/geom/brush-stroke.js b/js/lib/geom/brush-stroke.js index 4c42539a..52f81ffe 100755 --- a/js/lib/geom/brush-stroke.js +++ b/js/lib/geom/brush-stroke.js @@ -39,10 +39,7 @@ var BrushStroke = function GLBrushStroke() { this._strokeDoSmoothing = false; this._strokeUseCalligraphic = false; this._strokeAngle = 0; - - //the wetness of the brush (currently this is multiplied to the square of the stroke width, but todo should be changed to not depend on stroke width entirely - //smaller value means more samples for the path - this._WETNESS_FACTOR = 0.25; + this._strokeAmountSmoothing = 0; //threshold that tells us whether two samples are too far apart this._MAX_SAMPLE_DISTANCE_THRESHOLD = 5; @@ -125,7 +122,7 @@ var BrushStroke = function GLBrushStroke() { //add the point only if it is some epsilon away from the previous point var numPoints = this._Points.length; if (numPoints>0) { - var threshold = this._MIN_SAMPLE_DISTANCE_THRESHOLD;//this._WETNESS_FACTOR*this._strokeWidth; + var threshold = this._MIN_SAMPLE_DISTANCE_THRESHOLD; var prevPt = this._Points[numPoints-1]; var diffPt = [prevPt[0]-pt[0], prevPt[1]-pt[1]]; var diffPtMag = Math.sqrt(diffPt[0]*diffPt[0] + diffPt[1]*diffPt[1]); @@ -196,6 +193,10 @@ var BrushStroke = function GLBrushStroke() { this._strokeDoSmoothing = s; } + this.setSmoothingAmount = function(a){ + this._strokeAmountSmoothing = a; + } + this.setStrokeUseCalligraphic = function(c){ this._strokeUseCalligraphic = c; } @@ -252,7 +253,7 @@ var BrushStroke = function GLBrushStroke() { //**** add samples to the path if needed...linear interpolation for now //if (numPoints>1) { if (0){ - var threshold = this._WETNESS_FACTOR*this._strokeWidth; + var threshold = this._MAX_SAMPLE_DISTANCE_THRESHOLD; var prevPt = this._Points[0]; var prevIndex = 0; for (var i=1;i1) { @@ -330,7 +331,7 @@ var BrushStroke = function GLBrushStroke() { console.log("Inserted "+numInsertedPoints+" additional CatmullRom points"); //now do 3-4 iterations of Laplacian smoothing (setting the points to the average of their neighbors) - var numLaplacianIterations = 3; //todo figure out the proper number of Laplacian iterations (perhaps as a function of stroke width) + var numLaplacianIterations = this._strokeAmountSmoothing; for (var n=0;n1) { - if (0){ - var threshold = this._MAX_SAMPLE_DISTANCE_THRESHOLD; - var prevPt = this._Points[0]; - var prevIndex = 0; - for (var i=1;ithreshold){ - //insert points along the prev. to current point - var numNewPoints = Math.floor(distance/threshold); - for (var j=0;j this._MAX_ALLOWED_SAMPLES){ - console.log("leaving the resampling because numPoints is greater than limit:"+this._MAX_ALLOWED_SAMPLES); - break; + var numPoints = this._Points.length; + if (this._addedSamples === false){ + //**** add samples to the long sections of the path --- Catmull-Rom spline interpolation ***** + // instead of the following, may use 4-point subdivision iterations over continuous regions of 'long' segments + // look at http://www.gvu.gatech.edu/~jarek/Split&Tweak/ for formula + + var numInsertedPoints = 0; + var newSampledPoints = []; + var threshold = this._MAX_SAMPLE_DISTANCE_THRESHOLD;//this determines whether a segment between two sample is long enough to warrant checking for angle + var prevPt = this._Points[0]; + newSampledPoints.push(this._Points[0]); + for (var i=1;ithreshold){ + //build the control polygon for the Catmull-Rom spline (prev. 2 points and next 2 points) + var prev = (i===1) ? i-1 : i-2; + var next = (i===numPoints-1) ? i : i+1; + var ctrlPts = [this._Points[prev], this._Points[i-1], this._Points[i], this._Points[next]]; + //insert points along the prev. to current point + var numNewPoints = Math.floor(distance/threshold); + for (var j=0;j this._MAX_ALLOWED_SAMPLES){ + // console.log("leaving the resampling because numPoints is greater than limit:"+this._MAX_ALLOWED_SAMPLES); + // break; + //} } + this._Points = newSampledPoints.slice(0); + newSampledPoints = []; + console.log("Inserted "+numInsertedPoints+" additional CatmullRom points"); + this._addedSamples = true; + this._dirty=true; + } + //build a copy of the original points...this should be done only once + if (this._storedOrigPoints === false) { + this._OrigPoints = this._Points.slice(0); + this._storedOrigPoints = true; + } - //instead of the following, may use 4-point subdivision iterations over continuous regions of 'long' segments - // look at http://www.gvu.gatech.edu/~jarek/Split&Tweak/ for formula - //**** add samples to the long sections of the path --- Catmull-Rom spline interpolation + if (this._dirty) { + this._Points = this._OrigPoints.slice(0); + numPoints = this._Points.length; if (this._strokeDoSmoothing && numPoints>1) { - var numInsertedPoints = 0; - var newPoints = []; - var threshold = this._MAX_SAMPLE_DISTANCE_THRESHOLD;//this determines whether a segment between two sample is long enough to warrant checking for angle - var prevPt = this._Points[0]; - newPoints.push(this._Points[0]); - for (var i=1;ithreshold){ - //build the control polygon for the Catmull-Rom spline (prev. 2 points and next 2 points) - var prev = (i===1) ? i-1 : i-2; - var next = (i===numPoints-1) ? i : i+1; - var ctrlPts = [this._Points[prev], this._Points[i-1], this._Points[i], this._Points[next]]; - //insert points along the prev. to current point - var numNewPoints = Math.floor(distance/threshold); - for (var j=0;j this._MAX_ALLOWED_SAMPLES){ - console.log("leaving the resampling because numPoints is greater than limit:"+this._MAX_ALLOWED_SAMPLES); - break; - } - } - this._Points = newPoints; - numPoints = this._Points.length; - console.log("Inserted "+numInsertedPoints+" additional CatmullRom points"); - - //now do 3-4 iterations of Laplacian smoothing (setting the points to the average of their neighbors) - var numLaplacianIterations = this._strokeAmountSmoothing; + //iterations of Laplacian smoothing (setting the points to the average of their neighbors) + var numLaplacianIterations = this._strokeAmountSmoothing; for (var n=0;n */ -// Todo: This entire class should be converted to a module var VecUtils = require("js/helper-classes/3D/vec-utils").VecUtils; var GeomObj = require("js/lib/geom/geom-obj").GeomObj; +var CanvasController = require("js/controllers/elements/canvas-controller").CanvasController; + +// Todo: This entire class should be converted to a module /////////////////////////////////////////////////////////////////////// // Class GLBrushStroke @@ -28,7 +30,10 @@ var BrushStroke = function GLBrushStroke() { //whether or not to use the canvas drawing to stroke/fill this._useCanvasDrawing = true; - //the X and Y location of this subpath's canvas in stage world space of Ninja + //the HTML5 canvas that holds this brush stroke + this._canvas = null; + + //the X and Y location of this brush stroke canvas in stage world space of Ninja this._canvasX = 0; this._canvasY = 0; @@ -65,6 +70,10 @@ var BrushStroke = function GLBrushStroke() { ///////////////////////////////////////////////////////// // Property Accessors/Setters ///////////////////////////////////////////////////////// + this.setCanvas = function(c) { + this._canvas = c; + } + this.setWorld = function (world) { this._world = world; }; @@ -195,6 +204,9 @@ var BrushStroke = function GLBrushStroke() { this._dirty = true; } } + this.getStrokeHardness = function(){ + return this._strokeHardness; + } this.setDoSmoothing = function(s){ if (this._strokeDoSmoothing!==s) { @@ -203,12 +215,19 @@ var BrushStroke = function GLBrushStroke() { } } + this.getDoSmoothing = function(){ + return this._strokeDoSmoothing; + } + this.setSmoothingAmount = function(a){ if (this._strokeAmountSmoothing!==a) { this._strokeAmountSmoothing = a; this._dirty = true; } + } + this.getSmoothingAmount = function(){ + return this._strokeAmountSmoothing; } this.setStrokeUseCalligraphic = function(c){ @@ -225,6 +244,14 @@ var BrushStroke = function GLBrushStroke() { }; } + this.getStrokeUseCalligraphic = function(){ + return this._strokeUseCalligraphic; + } + + this.getStrokeAngle = function(){ + this._strokeAngle = a; + } + this.getStrokeStyle = function () { return this._strokeStyle; }; @@ -301,12 +328,13 @@ var BrushStroke = function GLBrushStroke() { console.log("Inserted "+numInsertedPoints+" additional CatmullRom points"); this._addedSamples = true; this._dirty=true; - } + } //if we need to add samples to this curve (done only once) + //build a copy of the original points...this should be done only once if (this._storedOrigPoints === false) { this._OrigPoints = this._Points.slice(0); this._storedOrigPoints = true; - } + } //if we need to store a copy of the original points (done only once) if (this._dirty) { this._Points = this._OrigPoints.slice(0); @@ -377,7 +405,7 @@ var BrushStroke = function GLBrushStroke() { // get the context var ctx = world.get2DContext(); - if (!ctx) throw ("null context in brushstroke render") + if (!ctx) throw ("null context in brushstroke render"); var numPoints = this.getNumPoints(); if (numPoints === 0) { @@ -391,6 +419,14 @@ var BrushStroke = function GLBrushStroke() { var bboxMax = this.getBBoxMax(); var bboxWidth = bboxMax[0] - bboxMin[0]; var bboxHeight = bboxMax[1] - bboxMin[1]; + + //assign the new width and height as the canvas dimensions through the canvas controller + if (this._canvas) { + CanvasController.setProperty(this._canvas, "width", bboxWidth+"px"); + CanvasController.setProperty(this._canvas, "height", bboxHeight+"px"); + this._canvas.elementModel.shapeModel.GLWorld.setViewportFromCanvas(this._canvas); + } + ctx.clearRect(0, 0, bboxWidth, bboxHeight); if (this._strokeUseCalligraphic) { -- cgit v1.2.3 From 8e29172990126b3d3857be5d18bb6985d4cf57ca Mon Sep 17 00:00:00 2001 From: Pushkar Joshi Date: Mon, 19 Mar 2012 10:07:47 -0700 Subject: first pass for file IO of brush strokes --- js/lib/geom/brush-stroke.js | 50 ++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 47 insertions(+), 3 deletions(-) (limited to 'js/lib/geom/brush-stroke.js') diff --git a/js/lib/geom/brush-stroke.js b/js/lib/geom/brush-stroke.js index 02a39ccd..ec2521ab 100755 --- a/js/lib/geom/brush-stroke.js +++ b/js/lib/geom/brush-stroke.js @@ -388,7 +388,8 @@ var BrushStroke = function GLBrushStroke() { this._BBoxMax[d]+= bboxPadding; }//for every dimension d from 0 to 2 } - } + + } //if this was dirty this._dirty = false; }; @@ -512,12 +513,55 @@ var BrushStroke = function GLBrushStroke() { }; //render() + this.exportJSON = function(){ + var retObject= new Object(); + retObject.geomType = this.geomType(); + retObject.points = this._Points; + retObject.planeCenter = this._planeCenter; + retObject.planeMat = this._planeMat; + retObject.planeMatInv = this._planeMatInv; + retObject.strokeWidth = this._strokeWidth; + retObject.strokeColor = this._strokeColor; + retObject.secondStrokeColor = this._secondStrokeColor; + retObject.strokeHardness = this._strokeHardness; + retObject.strokeDoSmoothing = this._strokeDoSmoothing; + retObject.strokeUseCalligraphic = this._strokeUseCalligraphic; + retObject.strokeAngle = this._strokeAngle; + retObject.strokeAmountSmoothing = this._strokeAmountSmoothing; + return retObject; + }; + + this.importJSON = function(jo){ + if (this.geomType()!== jo.geomType){ + return; + } + this._Points = jo.points.splice(0); //todo is this splice necessary? + this._planeCenter = jo.planeCenter; + this._planeMat = jo.planeMat; + this._planeMatInv = jo.planeMatInv ; + this._strokeWidth = jo.strokeWidth; + this._strokeColor = jo.strokeColor; + this._secondStrokeColor = jo.secondStrokeColor; + this._strokeHardness = jo.strokeHardness; + this._strokeDoSmoothing = jo.strokeDoSmoothing; + this._strokeUseCalligraphic = jo.strokeUseCalligraphic; + this._strokeAngle = jo.strokeAngle; + this._strokeAmountSmoothing = jo.strokeAmountSmoothing; + + //force a re-computation of meta-geometry before rendering + this._isDirty = true; + }; + + this.export = function() { - return "type: " + this.geomType() + "\n"; + var jsonObject = this.exportJSON(); + var stringified = JSON.stringify(jsonObject); + return "type: " + this.geomType() + "\n" + stringified; }; this.import = function( importStr ) { - + var jsonObject = JSON.parse(importStr); + this.importJSON(jsonObject); } this.collidesWithPoint = function (x, y, z) { -- cgit v1.2.3 From c0bd8d8efcc501d4efa116a7cc0e802fd4e9f3e9 Mon Sep 17 00:00:00 2001 From: Pushkar Joshi Date: Mon, 19 Mar 2012 11:05:05 -0700 Subject: draw the brush stroke in realtime --- js/lib/geom/brush-stroke.js | 31 ++++++++++++++++--------------- 1 file changed, 16 insertions(+), 15 deletions(-) (limited to 'js/lib/geom/brush-stroke.js') diff --git a/js/lib/geom/brush-stroke.js b/js/lib/geom/brush-stroke.js index ec2521ab..68b9110f 100755 --- a/js/lib/geom/brush-stroke.js +++ b/js/lib/geom/brush-stroke.js @@ -249,7 +249,7 @@ var BrushStroke = function GLBrushStroke() { } this.getStrokeAngle = function(){ - this._strokeAngle = a; + return this._strokeAngle; } this.getStrokeStyle = function () { @@ -429,7 +429,12 @@ var BrushStroke = function GLBrushStroke() { } ctx.clearRect(0, 0, bboxWidth, bboxHeight); + this.drawToContext(ctx, bboxMin[0], bboxMin[1]); + ctx.restore(); + } //this.render() + this.drawToContext = function(ctx, origX, origY){ + var numPoints = this.getNumPoints(); if (this._strokeUseCalligraphic) { //build the stamp for the brush stroke var t=0; @@ -453,13 +458,11 @@ var BrushStroke = function GLBrushStroke() { ctx.lineCap="butt"; ctx.globalCompositeOperation = 'source-over'; ctx.globalAlpha = this._strokeColor[3]; - //ctx.lineWidth=this._strokeWidth/10;//todo figure out the correct formula for the line width - //if (ctx.lineWidth<2) + //todo figure out the correct formula for the line width ctx.lineWidth=2; if (t===numTraces-1){ ctx.lineWidth = 1; } - for (t=0;tthreshold){ this._Points.push(pt); - this._dirty=true; + this._isDirty=true; + this._isInit = false; } } else { this._Points.push(pt); - this._dirty=true; + this._isDirty=true; + this._isInit = false; } }; this.insertPoint = function(pt, index){ this._Points.splice(index, 0, pt); - this._dirty=true; + this._isDirty=true; + this._isInit = false; }; this.isDirty = function(){ - return this._dirty; + return this._isDirty; }; this.makeDirty = function(){ - this._dirty=true; + this._isDirty=true; + }; + + this.getStageWorldCenter = function() { + return this._stageWorldCenter; }; this.getBBoxMin = function () { @@ -176,7 +190,7 @@ var BrushStroke = function GLBrushStroke() { this.setStrokeWidth = function (w) { this._strokeWidth = w; - this._dirty=true; + this._isDirty=true; }; this.getStrokeMaterial = function () { @@ -184,7 +198,7 @@ var BrushStroke = function GLBrushStroke() { }; this.setStrokeMaterial = function (m) { - this._strokeMaterial = m; this._dirty = true; + this._strokeMaterial = m; this._isDirty = true; }; this.getStrokeColor = function () { @@ -192,17 +206,17 @@ var BrushStroke = function GLBrushStroke() { }; this.setStrokeColor = function (c) { - this._strokeColor = c; this._dirty = true; + this._strokeColor = c; this._isDirty = true; }; this.setSecondStrokeColor = function(c){ - this._secondStrokeColor=c; this._dirty = true; + this._secondStrokeColor=c; this._isDirty = true; } this.setStrokeHardness = function(h){ if (this._strokeHardness!==h){ this._strokeHardness=h; - this._dirty = true; + this._isDirty = true; } } this.getStrokeHardness = function(){ @@ -212,7 +226,7 @@ var BrushStroke = function GLBrushStroke() { this.setDoSmoothing = function(s){ if (this._strokeDoSmoothing!==s) { this._strokeDoSmoothing = s; - this._dirty = true; + this._isDirty = true; } } @@ -223,7 +237,7 @@ var BrushStroke = function GLBrushStroke() { this.setSmoothingAmount = function(a){ if (this._strokeAmountSmoothing!==a) { this._strokeAmountSmoothing = a; - this._dirty = true; + this._isDirty = true; } } @@ -234,14 +248,14 @@ var BrushStroke = function GLBrushStroke() { this.setStrokeUseCalligraphic = function(c){ if (this._strokeUseCalligraphic!==c){ this._strokeUseCalligraphic = c; - this._dirty = true; + this._isDirty = true; } } this.setStrokeAngle = function(a){ if (this._strokeAngle!==a){ this._strokeAngle = a; - this._dirty = true; + this._isDirty = true; }; } @@ -269,127 +283,276 @@ var BrushStroke = function GLBrushStroke() { };//NO-OP for now + this.getWidth = function() { + if (this._isDirty){ + this.update(); + } + return this._BBoxMax[0]-this._BBoxMin[0]; + }; + + this.getHeight = function() { + if (this._isDirty){ + this.update(); + } + return this._BBoxMax[1]-this._BBoxMin[1]; + }; + //remove all the points this.clear = function () { this._Points = []; this._OrigPoints = []; - this._dirty=true; - } + this._isDirty=true; + this._isInit = false; + }; + + this._addSamples = function() { + //**** add samples to the long sections of the path --- Catmull-Rom spline interpolation ***** + // instead of the following, may use 4-point subdivision iterations over continuous regions of 'long' segments + // look at http://www.gvu.gatech.edu/~jarek/Split&Tweak/ for formula + + var numPoints = this._Points.length; + var numInsertedPoints = 0; + var newSampledPoints = []; + var threshold = this._MAX_SAMPLE_DISTANCE_THRESHOLD;//this determines whether a segment between two sample too long + var prevPt = this._Points[0]; + newSampledPoints.push(this._Points[0]); + for (var i=1;ithreshold){ + //build the control polygon for the Catmull-Rom spline (prev. 2 points and next 2 points) + var prev = (i===1) ? i-1 : i-2; + var next = (i===numPoints-1) ? i : i+1; + var ctrlPts = [this._Points[prev], this._Points[i-1], this._Points[i], this._Points[next]]; + //insert points along the prev. to current point + var numNewPoints = Math.floor(distance/threshold); + for (var j=0;j this._MAX_ALLOWED_SAMPLES){ + console.log("leaving the resampling because numPoints is greater than limit:"+this._MAX_ALLOWED_SAMPLES); + break; + } + } + this._Points = newSampledPoints.slice(0); + newSampledPoints = []; + }; + + this.init = function(){ + if (!this._isInit){ + // **** add samples to the _Points in stageworld space **** + this._addSamples(); + + // **** compute the 2D (canvas space) coord. of the _Points **** + this._buildLocalCoordFromStageWorldCoord(); + + // **** turn off the init. flag **** + this._isInit = true; + this._isDirty= true; + } + + // **** update the current brush stroke **** + // smoothing, re-compute bounding box, etc. + this.update(); + }; + + this._unprojectPt = function(pt, pespectiveDist){ + var retPt = pt.slice(0); + if (MathUtils.fpCmp(pespectiveDist,-pt[2]) !== 0){ + z = pt[2]*pespectiveDist/(pespectiveDist + pt[2]); + var x = pt[0]*(pespectiveDist - z)/pespectiveDist, + y = pt[1]*(pespectiveDist - z)/pespectiveDist; + retPt[0] = x; retPt[1] = y; retPt[2] = z; + } + return retPt; + }; + + this._buildLocalCoordFromStageWorldCoord = function() { + var stage = ViewUtils.getStage(); + var stageOffset = ViewUtils.getElementOffset(stage); + ViewUtils.setViewportObj(stage); + + var numPoints = this._Points.length; + var i; + + // ***** compute center of bbox based on stage world coords ***** + var bboxMin = [Infinity, Infinity, Infinity]; + var bboxMax = [-Infinity, -Infinity, -Infinity]; + 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) + this._stageWorldCenter = VecUtils.vecInterpolate(3, bboxMin, bboxMax, 0.5); + + // ***** center the input stageworld data about the center of the bbox ***** + this._LocalPoints = this._Points.slice(0); + for (i=0;ithreshold){ - //build the control polygon for the Catmull-Rom spline (prev. 2 points and next 2 points) - var prev = (i===1) ? i-1 : i-2; - var next = (i===numPoints-1) ? i : i+1; - var ctrlPts = [this._Points[prev], this._Points[i-1], this._Points[i], this._Points[next]]; - //insert points along the prev. to current point - var numNewPoints = Math.floor(distance/threshold); - for (var j=0;j this._MAX_ALLOWED_SAMPLES){ - // console.log("leaving the resampling because numPoints is greater than limit:"+this._MAX_ALLOWED_SAMPLES); - // break; - //} + //build coordinates for the brush stroke in local space of the canvas...will assume that the canvas width, height, left and top will not be changed now + this.buildLocalCoord = function(){ + /* + if (this._isLocalDirty) { + //DEBUGGING + //confirm that localToStageWorld produces the same coordinates as those I used for rendering currently + var numPoints = this._Points.length; + var objToStageWorldMat = ViewUtils.getObjToStageWorldMatrix(this._canvas, true); + var stageworldToObjMat = glmat4.inverse(objToStageWorldMat, []); + var wh = ViewUtils.getStageDimension(); + for (var i=0;i1) { - //iterations of Laplacian smoothing (setting the points to the average of their neighbors) - var numLaplacianIterations = this._strokeAmountSmoothing; - for (var n=0;n pt[d]) { - this._BBoxMin[d] = pt[d]; - } - if (this._BBoxMax[d] < pt[d]) { - this._BBoxMax[d] = pt[d]; - } - }//for every dimension d from 0 to 2 + + //end DEBUGGING + this._isLocalDirty=false; + } //if this._isLocalDirty + */ + }; + + + + this.update = function() { + if (this._isDirty){ + // **** do smoothing if necessary **** + this._doSmoothing(); + + // **** recompute the bounding box **** + this._updateBoundingBox(); + + // **** turn off the dirty flag **** + this._isDirty = false; + } + }; + + this._doSmoothing = function() { + var numPoints = this._LocalPoints.length; + if (this._strokeDoSmoothing && numPoints>1) { + this._LocalPoints = this._OrigPoints.slice(0); + //iterations of Laplacian smoothing (setting the points to the average of their neighbors) + var numLaplacianIterations = this._strokeAmountSmoothing; + for (var n=0;n pt[d]) { + this._BBoxMin[d] = pt[d]; + } + if (this._BBoxMax[d] < pt[d]) { + this._BBoxMax[d] = pt[d]; + } }//for every dimension d from 0 to 2 } - } //if this was dirty - this._dirty = false; + } + //increase the bbox given the stroke width and the angle (in case of calligraphic brush) + var bboxPadding = this._strokeWidth/2; + //todo TEMP! + //bboxPadding = 0; //for now, ignore the effect of stroke width on bounding box + //end todo TEMP + //if (this._strokeUseCalligraphic) { + //todo re-enable this if check once we are able to change the left and top of the brush canvas + if (false){ + this._BBoxMin[0]-= bboxPadding*Math.cos(this._strokeAngle); + this._BBoxMin[1]-= bboxPadding*Math.sin(this._strokeAngle); + this._BBoxMax[0]+= bboxPadding*Math.cos(this._strokeAngle); + this._BBoxMax[1]+= bboxPadding*Math.sin(this._strokeAngle); + } else { + for (var d = 0; d < 3; d++) { + this._BBoxMin[d]-= bboxPadding; + this._BBoxMax[d]+= bboxPadding; + }//for every dimension d from 0 to 2 + } }; this.buildBuffers = function () { @@ -410,32 +573,34 @@ var BrushStroke = function GLBrushStroke() { return; //nothing to do for empty paths } - this.computeMetaGeometry(); + if (this._isDirty){ + this.update(); + } var bboxMin = this.getBBoxMin(); var bboxMax = this.getBBoxMax(); var bboxWidth = bboxMax[0] - bboxMin[0]; var bboxHeight = bboxMax[1] - bboxMin[1]; - if (this._canvas) { - // this seems to produce drift as the stroke size is changed smoothly...bug due to floating point round off + // this seems to produce drift as the stroke size is changed smoothly...bug due to floating point round off? //get the old left, top, width, and height -// var oldLeft = parseInt(CanvasController.getProperty(this._canvas, "left")); -// var oldTop = parseInt(CanvasController.getProperty(this._canvas, "top")); -// var oldWidth = parseInt(CanvasController.getProperty(this._canvas, "width")); -// var oldHeight = parseInt(CanvasController.getProperty(this._canvas, "height")); -// var newLeft = oldLeft - parseInt((bboxWidth-oldWidth)*0.5); -// var newTop = oldTop - parseInt((bboxHeight-oldHeight)*0.5); -// -// //assign the new width and height as the canvas dimensions through the canvas controller -// CanvasController.setProperty(this._canvas, "left", newLeft+"px"); -// CanvasController.setProperty(this._canvas, "top", newTop+"px");*/ - CanvasController.setProperty(this._canvas, "width", bboxWidth+"px"); - CanvasController.setProperty(this._canvas, "height", bboxHeight+"px"); + var oldLeft = parseInt(CanvasController.getProperty(this._canvas, "left")); + var oldTop = parseInt(CanvasController.getProperty(this._canvas, "top")); + var oldWidth = parseInt(CanvasController.getProperty(this._canvas, "width")); + var oldHeight = parseInt(CanvasController.getProperty(this._canvas, "height")); + var newLeft = oldLeft - ((bboxWidth-oldWidth)*0.5); + var newTop = oldTop - ((bboxHeight-oldHeight)*0.5); + + //assign the new width and height as the canvas dimensions through the canvas controller + //CanvasController.setProperty(this._canvas, "left", newLeft+"px"); + //CanvasController.setProperty(this._canvas, "top", newTop+"px"); + + //CanvasController.setProperty(this._canvas, "width", bboxWidth+"px"); + //CanvasController.setProperty(this._canvas, "height", bboxHeight+"px"); this._canvas.elementModel.shapeModel.GLWorld.setViewportFromCanvas(this._canvas); - } + //get the context var ctx = world.get2DContext(); if (!ctx) { @@ -443,12 +608,17 @@ var BrushStroke = function GLBrushStroke() { } ctx.save(); ctx.clearRect(0, 0, bboxWidth, bboxHeight); - this.drawToContext(ctx, bboxMin[0], bboxMin[1]); + this.drawToContext(ctx, 0, 0, false); ctx.restore(); } //this.render() - this.drawToContext = function(ctx, origX, origY){ - var numPoints = this.getNumPoints(); + this.drawToContext = function(ctx, origX, origY, drawStageWorldPts){ + var points = this._LocalPoints; + if (drawStageWorldPts){ + points = this._Points; + } + var numPoints = points.length; + if (this._strokeUseCalligraphic) { //build the stamp for the brush stroke var t=0; @@ -457,6 +627,7 @@ var BrushStroke = function GLBrushStroke() { var opaqueRegionHalfWidth = 0.5*this._strokeHardness*numTraces*0.01; //the 0.01 is to convert the strokeHardness from [0,100] to [0,1] var maxTransparentRegionHalfWidth = halfNumTraces-opaqueRegionHalfWidth; + //todo this brush stamp should be created outside of this function //build an angled (calligraphic) brush stamp var deltaDisplacement = [Math.cos(this._strokeAngle),Math.sin(this._strokeAngle)]; deltaDisplacement = VecUtils.vecNormalize(2, deltaDisplacement, 1); @@ -485,19 +656,16 @@ var BrushStroke = function GLBrushStroke() { alphaVal = 1.0 - distFromOpaqueRegion/maxTransparentRegionHalfWidth; alphaVal *= 1.0/ctx.lineWidth; //factor that accounts for lineWidth !== 1 } - ctx.save(); ctx.strokeStyle="rgba("+parseInt(255*this._strokeColor[0])+","+parseInt(255*this._strokeColor[1])+","+parseInt(255*this._strokeColor[2])+","+alphaVal+")"; - //linearly interpolate between the two stroke colors var currStrokeColor = VecUtils.vecInterpolate(4, this._strokeColor, this._secondStrokeColor, t/numTraces); //ctx.strokeStyle="rgba("+parseInt(255*currStrokeColor[0])+","+parseInt(255*currStrokeColor[1])+","+parseInt(255*currStrokeColor[2])+","+alphaVal+")"; - ctx.translate(disp[0],disp[1]); ctx.beginPath(); - ctx.moveTo(this._Points[0][0]-origX, this._Points[0][1]-origY); + ctx.moveTo(points[0]