From e065244ac75d1d0f25fd5c75cb58e714a13fe16b Mon Sep 17 00:00:00 2001 From: Jonathan Duran Date: Wed, 14 Mar 2012 08:59:17 -0700 Subject: Squashed commit of the following: merge master into timeline Signed-off-by: Jonathan Duran --- js/lib/geom/brush-stroke.js | 329 ++++++++++++++++++++++++++------------------ 1 file changed, 196 insertions(+), 133 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 9a934928..4c42539a 100755 --- a/js/lib/geom/brush-stroke.js +++ b/js/lib/geom/brush-stroke.js @@ -32,13 +32,24 @@ var BrushStroke = function GLBrushStroke() { //stroke information this._strokeWidth = 0.0; this._strokeColor = [0.4, 0.4, 0.4, 1.0]; + this._secondStrokeColor = [1, 0.4, 0.4, 1.0]; + this._strokeHardness = 100; this._strokeMaterial = null; this._strokeStyle = "Solid"; + 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; + //threshold that tells us whether two samples are too far apart + this._MAX_SAMPLE_DISTANCE_THRESHOLD = 5; + + //threshold that tells us whether two samples are too close + this._MIN_SAMPLE_DISTANCE_THRESHOLD = 2; + //prevent extremely long paths that can take a long time to render this._MAX_ALLOWED_SAMPLES = 500; @@ -114,7 +125,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._WETNESS_FACTOR*this._strokeWidth; + var threshold = this._MIN_SAMPLE_DISTANCE_THRESHOLD;//this._WETNESS_FACTOR*this._strokeWidth; 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]); @@ -173,6 +184,26 @@ var BrushStroke = function GLBrushStroke() { this._strokeColor = c; }; + this.setSecondStrokeColor = function(c){ + this._secondStrokeColor=c; + } + + this.setStrokeHardness = function(h){ + this._strokeHardness=h; + } + + this.setDoSmoothing = function(s){ + this._strokeDoSmoothing = s; + } + + this.setStrokeUseCalligraphic = function(c){ + this._strokeUseCalligraphic = c; + } + + this.setStrokeAngle = function(a){ + this._strokeAngle = a; + } + this.getStrokeStyle = function () { return this._strokeStyle; }; @@ -219,7 +250,8 @@ var BrushStroke = function GLBrushStroke() { var numPoints = this._Points.length; //**** add samples to the path if needed...linear interpolation for now - if (numPoints>1) { + //if (numPoints>1) { + if (0){ var threshold = this._WETNESS_FACTOR*this._strokeWidth; var prevPt = this._Points[0]; var prevIndex = 0; @@ -251,6 +283,66 @@ var BrushStroke = function GLBrushStroke() { } } + //todo 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._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 = 3; //todo figure out the proper number of Laplacian iterations (perhaps as a function of stroke width) + for (var n=0;n0) { + alphaVal = 1.0 - distFromOpaqueRegion/maxTransparentRegionHalfWidth; + alphaVal *= 1.0/ctx.lineWidth; //factor that accounts for lineWidth !== 1 + } - var R2 = this._strokeWidth; - var R = R2*0.5; - var hardness = 0; //for a pencil, this is always 1 //TODO get hardness parameter from user interface - var innerRadius = (hardness*R)-1; - if (innerRadius<1) - innerRadius=1; + ctx.save(); - var r = ctx.createRadialGradient(0,0,innerRadius, 0,0,R); - var midColor = "rgba("+parseInt(255*this._strokeColor[0])+","+parseInt(255*this._strokeColor[1])+","+parseInt(255*this._strokeColor[2])+",1)"; - r.addColorStop(0, midColor); - var endColor = "rgba("+parseInt(255*this._strokeColor[0])+","+parseInt(255*this._strokeColor[1])+","+parseInt(255*this._strokeColor[2])+",0.0)"; - r.addColorStop(1, endColor); - ctx.fillStyle = r; + 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+")"; - for (var i = 0; i < numPoints; i++) { - var pt = this._Points[i]; - ctx.globalCompositeOperation = 'source-over'; - var x = pt[0]-bboxMin[0]; - var y = pt[1]-bboxMin[1]; - ctx.save(); - ctx.translate(x,y); - ctx.arc(0, 0, R, 0, 2 * Math.PI, false); - ctx.fill(); - ctx.restore(); - //ctx.globalCompositeOperation = 'source-in'; - //ctx.rect(x-R, y-R, R2, R2); + ctx.translate(disp[0],disp[1]); + ctx.beginPath(); + ctx.moveTo(this._Points[0][0]-bboxMin[0], this._Points[0][1]-bboxMin[1]); + for (var i=0;i