diff options
author | Pushkar Joshi | 2012-02-29 09:47:41 -0800 |
---|---|---|
committer | Pushkar Joshi | 2012-02-29 09:47:41 -0800 |
commit | f931c48a7d0bcf1222cf05787e3294839ed0b9fb (patch) | |
tree | 584e3dc113dff708fe2a40e889d3ecc6a5b0bb78 /js | |
parent | b43efdcbfa671a46d1edaeb1555f508fe2ad0338 (diff) | |
download | ninja-f931c48a7d0bcf1222cf05787e3294839ed0b9fb.tar.gz |
resample the brush stroke so we don't have gaps if the path is drawn rapidly,
and,
allow to change the stroke color,
and,
more efficient stroke rendering by drawing translated radial gradients (instead of creating new gradients for each stroke sample)
Diffstat (limited to 'js')
-rwxr-xr-x | js/helper-classes/RDGE/GLBrushStroke.js | 74 | ||||
-rw-r--r-- | js/tools/BrushTool.js | 12 |
2 files changed, 65 insertions, 21 deletions
diff --git a/js/helper-classes/RDGE/GLBrushStroke.js b/js/helper-classes/RDGE/GLBrushStroke.js index 8fb6ab25..52ac79f5 100755 --- a/js/helper-classes/RDGE/GLBrushStroke.js +++ b/js/helper-classes/RDGE/GLBrushStroke.js | |||
@@ -34,6 +34,9 @@ function GLBrushStroke() { | |||
34 | this._strokeMaterial; | 34 | this._strokeMaterial; |
35 | this._strokeStyle = "Solid"; | 35 | this._strokeStyle = "Solid"; |
36 | 36 | ||
37 | //the wetness of the brush (currently this is multiplied to the square of the stroke width, but todo should be changed | ||
38 | this._WETNESS_FACTOR = 0.010625;//0.0625; | ||
39 | |||
37 | //drawing context | 40 | //drawing context |
38 | this._world = null; | 41 | this._world = null; |
39 | 42 | ||
@@ -69,21 +72,23 @@ function GLBrushStroke() { | |||
69 | this.getPoint = function (index) { return this._Points[index]; } | 72 | this.getPoint = function (index) { return this._Points[index]; } |
70 | this.addPoint = function (pt) | 73 | this.addPoint = function (pt) |
71 | { | 74 | { |
72 | //add the point if it is some epsilon away from the previous point | 75 | //add the point only if it is some epsilon away from the previous point |
73 | var doAddPoint=true; | ||
74 | var numPoints = this._Points.length; | 76 | var numPoints = this._Points.length; |
75 | if (numPoints>0) { | 77 | if (numPoints>0) { |
78 | var threshold = this._WETNESS_FACTOR*this._strokeWidth*this._strokeWidth; | ||
76 | var prevPt = this._Points[numPoints-1]; | 79 | var prevPt = this._Points[numPoints-1]; |
77 | var diffPt = [prevPt[0]-pt[0], prevPt[1]-pt[1]]; | 80 | var diffPt = [prevPt[0]-pt[0], prevPt[1]-pt[1]]; |
78 | var diffPtMag = diffPt[0]*diffPt[0] + diffPt[1]*diffPt[1]; | 81 | var diffPtMag = diffPt[0]*diffPt[0] + diffPt[1]*diffPt[1]; |
79 | if (diffPtMag<16)//TODO hook this up to the variable that measures flow/wetness of the paint brush...a smaller number-> more samples | 82 | if (diffPtMag>threshold){ |
80 | doAddPoint=false; | 83 | this._Points.push(pt); |
81 | } | 84 | this._dirty=true; |
82 | if (doAddPoint) { | 85 | } |
86 | }else{ | ||
83 | this._Points.push(pt); | 87 | this._Points.push(pt); |
84 | this._dirty=true; | 88 | this._dirty=true; |
85 | } | 89 | } |
86 | } | 90 | } |
91 | |||
87 | this.insertPoint = function(pt, index){ this._Points.splice(index, 0, pt); this._dirty=true;} | 92 | this.insertPoint = function(pt, index){ this._Points.splice(index, 0, pt); this._dirty=true;} |
88 | this.isDirty = function(){return this._dirty;} | 93 | this.isDirty = function(){return this._dirty;} |
89 | this.makeDirty = function(){this._dirty=true;} | 94 | this.makeDirty = function(){this._dirty=true;} |
@@ -127,10 +132,39 @@ function GLBrushStroke() { | |||
127 | 132 | ||
128 | this.computeMetaGeometry = function(){ | 133 | this.computeMetaGeometry = function(){ |
129 | if (this._dirty){ | 134 | if (this._dirty){ |
135 | var numPoints = this._Points.length; | ||
136 | |||
137 | //**** add samples to the path if needed...linear interpolation for now | ||
138 | if (numPoints>1) { | ||
139 | var threshold = this._WETNESS_FACTOR*this._strokeWidth*this._strokeWidth; | ||
140 | var prevPt = this._Points[0]; | ||
141 | var prevIndex = 0; | ||
142 | for (var i=1;i<numPoints;i++){ | ||
143 | var pt = this._Points[i]; | ||
144 | var diff = [pt[0]-prevPt[0], pt[1]-prevPt[1]]; | ||
145 | var distance = Math.sqrt(diff[0]*diff[0]+diff[1]*diff[1]); | ||
146 | if (distance>threshold){ | ||
147 | //insert points along the prev. to current point | ||
148 | var numNewPoints = Math.floor(distance/threshold); | ||
149 | for (var j=0;j<numNewPoints;j++){ | ||
150 | var param = (j+1)/(numNewPoints+1); | ||
151 | var newpt = [prevPt[0]+ diff[0]*param, prevPt[1]+ diff[1]*param]; | ||
152 | //insert new point before point i | ||
153 | this._Points.splice(i, 0, [newpt[0], newpt[1], 0]); | ||
154 | i++; | ||
155 | } | ||
156 | this._dirty=true; | ||
157 | } | ||
158 | prevPt=pt; | ||
159 | //update numPoints to match the new length | ||
160 | numPoints = this._Points.length; | ||
161 | } | ||
162 | } | ||
163 | |||
130 | // *** compute the bounding box ********* | 164 | // *** compute the bounding box ********* |
131 | this._BBoxMin = [Infinity, Infinity, Infinity]; | 165 | this._BBoxMin = [Infinity, Infinity, Infinity]; |
132 | this._BBoxMax = [-Infinity, -Infinity, -Infinity]; | 166 | this._BBoxMax = [-Infinity, -Infinity, -Infinity]; |
133 | var numPoints = this._Points.length; | 167 | numPoints = this._Points.length; |
134 | if (numPoints === 0) { | 168 | if (numPoints === 0) { |
135 | this._BBoxMin = [0, 0, 0]; | 169 | this._BBoxMin = [0, 0, 0]; |
136 | this._BBoxMax = [0, 0, 0]; | 170 | this._BBoxMax = [0, 0, 0]; |
@@ -204,6 +238,7 @@ function GLBrushStroke() { | |||
204 | ctx.stroke(); | 238 | ctx.stroke(); |
205 | */ | 239 | */ |
206 | 240 | ||
241 | /* | ||
207 | var isDebug = false; | 242 | var isDebug = false; |
208 | var prevPt = this._Points[0]; | 243 | var prevPt = this._Points[0]; |
209 | var prevX = prevPt[0]-bboxMin[0]; | 244 | var prevX = prevPt[0]-bboxMin[0]; |
@@ -284,9 +319,9 @@ function GLBrushStroke() { | |||
284 | } | 319 | } |
285 | ctx.stroke(); | 320 | ctx.stroke(); |
286 | } | 321 | } |
322 | */ | ||
287 | 323 | ||
288 | 324 | ||
289 | /* | ||
290 | var R2 = this._strokeWidth; | 325 | var R2 = this._strokeWidth; |
291 | var R = R2*0.5; | 326 | var R = R2*0.5; |
292 | var hardness = 0.25; //for a pencil, this is always 1 //TODO get hardness parameter from user interface | 327 | var hardness = 0.25; //for a pencil, this is always 1 //TODO get hardness parameter from user interface |
@@ -294,23 +329,30 @@ function GLBrushStroke() { | |||
294 | if (innerRadius<1) | 329 | if (innerRadius<1) |
295 | innerRadius=1; | 330 | innerRadius=1; |
296 | 331 | ||
332 | var r = ctx.createRadialGradient(0,0,innerRadius, 0,0,R); | ||
333 | //r.addColorStop(0, 'rgba(255,0,0,0.5)'); | ||
334 | var midColor = "rgba("+parseInt(255*this._strokeColor[0])+","+parseInt(255*this._strokeColor[1])+","+parseInt(255*this._strokeColor[2])+",0.5)"; | ||
335 | r.addColorStop(0, midColor); | ||
336 | //r.addColorStop(0.5, 'rgba(255,0,0,0.5)'); // prevent aggregation of semi-opaque pixels | ||
337 | //r.addColorStop(1, 'rgba(255,0,0,0.0)'); | ||
338 | var endColor = "rgba("+parseInt(255*this._strokeColor[0])+","+parseInt(255*this._strokeColor[1])+","+parseInt(255*this._strokeColor[2])+",0.0)"; | ||
339 | r.addColorStop(1, endColor); | ||
340 | ctx.fillStyle = r; | ||
341 | |||
297 | for (var i = 0; i < numPoints; i++) { | 342 | for (var i = 0; i < numPoints; i++) { |
298 | var pt = this._Points[i]; | 343 | var pt = this._Points[i]; |
299 | ctx.globalCompositeOperation = 'source-over'; | 344 | ctx.globalCompositeOperation = 'source-over'; |
300 | var x = pt[0]-bboxMin[0]; | 345 | var x = pt[0]-bboxMin[0]; |
301 | var y = pt[1]-bboxMin[1]; | 346 | var y = pt[1]-bboxMin[1]; |
302 | var r = ctx.createRadialGradient(x, y, innerRadius, x, y, R); | 347 | ctx.save(); |
303 | r.addColorStop(0, 'rgba(255,0,0,0.5)'); | 348 | ctx.translate(x,y); |
304 | //r.addColorStop(0.5, 'rgba(255,0,0,0.5)'); // prevent aggregation of semi-opaque pixels | 349 | ctx.arc(0, 0, R, 0, 2 * Math.PI, false); |
305 | r.addColorStop(1, 'rgba(255,0,0,0.0)'); | ||
306 | ctx.fillStyle = r; | ||
307 | //ctx.fillRect(x-R, y-R, R2, R2); | ||
308 | ctx.arc(x, y, R, 0, 2 * Math.PI, false); | ||
309 | ctx.fill(); | 350 | ctx.fill(); |
351 | ctx.restore(); | ||
310 | //ctx.globalCompositeOperation = 'source-in'; | 352 | //ctx.globalCompositeOperation = 'source-in'; |
311 | //ctx.rect(x-R, y-R, R2, R2); | 353 | //ctx.rect(x-R, y-R, R2, R2); |
312 | } | 354 | } |
313 | */ | 355 | |
314 | ctx.restore(); | 356 | ctx.restore(); |
315 | } //render() | 357 | } //render() |
316 | 358 | ||
diff --git a/js/tools/BrushTool.js b/js/tools/BrushTool.js index 9a0ad583..776d914c 100644 --- a/js/tools/BrushTool.js +++ b/js/tools/BrushTool.js | |||
@@ -64,6 +64,11 @@ exports.BrushTool = Montage.create(ShapeTool, { | |||
64 | //start a new brush stroke | 64 | //start a new brush stroke |
65 | if (this._selectedBrushStroke === null){ | 65 | if (this._selectedBrushStroke === null){ |
66 | this._selectedBrushStroke = new GLBrushStroke(); | 66 | this._selectedBrushStroke = new GLBrushStroke(); |
67 | if (this.application.ninja.colorController.colorToolbar.stroke.webGlColor){ | ||
68 | this._selectedBrushStroke.setStrokeColor(this.application.ninja.colorController.colorToolbar.stroke.webGlColor); | ||
69 | } | ||
70 | //TODO get these values from the options | ||
71 | this._selectedBrushStroke.setStrokeWidth(20); | ||
67 | } | 72 | } |
68 | NJevent("enableStageMove");//stageManagerModule.stageManager.enableMouseMove(); | 73 | NJevent("enableStageMove");//stageManagerModule.stageManager.enableMouseMove(); |
69 | } //value: function (event) { | 74 | } //value: function (event) { |
@@ -132,10 +137,7 @@ exports.BrushTool = Montage.create(ShapeTool, { | |||
132 | this._isDrawing = false; | 137 | this._isDrawing = false; |
133 | this._hasDraw = false; | 138 | this._hasDraw = false; |
134 | 139 | ||
135 | //TODO get these values from the options | 140 | |
136 | if (this._selectedBrushStroke){ | ||
137 | this._selectedBrushStroke.setStrokeWidth(20); | ||
138 | } | ||
139 | //display the previously drawn stroke in a separate canvas | 141 | //display the previously drawn stroke in a separate canvas |
140 | this.RenderCurrentBrushStroke(); | 142 | this.RenderCurrentBrushStroke(); |
141 | 143 | ||
@@ -150,7 +152,7 @@ exports.BrushTool = Montage.create(ShapeTool, { | |||
150 | //clear the canvas before we draw anything else | 152 | //clear the canvas before we draw anything else |
151 | this.application.ninja.stage.clearDrawingCanvas(); | 153 | this.application.ninja.stage.clearDrawingCanvas(); |
152 | if (this._selectedBrushStroke && this._selectedBrushStroke.getNumPoints()>0){ | 154 | if (this._selectedBrushStroke && this._selectedBrushStroke.getNumPoints()>0){ |
153 | this._selectedBrushStroke.computeMetaGeometry(); | 155 | //this._selectedBrushStroke.computeMetaGeometry(); |
154 | var ctx = this.application.ninja.stage.drawingContext;//stageManagerModule.stageManager.drawingContext; | 156 | var ctx = this.application.ninja.stage.drawingContext;//stageManagerModule.stageManager.drawingContext; |
155 | if (ctx === null) | 157 | if (ctx === null) |
156 | throw ("null drawing context in Brushtool::ShowCurrentBrushStrokeOnStage"); | 158 | throw ("null drawing context in Brushtool::ShowCurrentBrushStrokeOnStage"); |