aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPushkar Joshi2012-02-29 09:47:41 -0800
committerPushkar Joshi2012-02-29 09:47:41 -0800
commitf931c48a7d0bcf1222cf05787e3294839ed0b9fb (patch)
tree584e3dc113dff708fe2a40e889d3ecc6a5b0bb78
parentb43efdcbfa671a46d1edaeb1555f508fe2ad0338 (diff)
downloadninja-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)
-rwxr-xr-xjs/helper-classes/RDGE/GLBrushStroke.js74
-rw-r--r--js/tools/BrushTool.js12
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");