aboutsummaryrefslogtreecommitdiff
path: root/js/lib/geom/brush-stroke.js
diff options
context:
space:
mode:
authorPushkar Joshi2012-03-08 16:03:17 -0800
committerPushkar Joshi2012-03-08 16:03:17 -0800
commitfcab2a14f8de302948d38f55db41e2c1c92baa34 (patch)
tree992992a43e0cff5fab528a84448fd01328077392 /js/lib/geom/brush-stroke.js
parent703fb3d06e88257ac73c1d1a0ec6ca33a64f4371 (diff)
downloadninja-fcab2a14f8de302948d38f55db41e2c1c92baa34.tar.gz
brush stroke options for calligraphic brush style
Diffstat (limited to 'js/lib/geom/brush-stroke.js')
-rwxr-xr-xjs/lib/geom/brush-stroke.js147
1 files changed, 84 insertions, 63 deletions
diff --git a/js/lib/geom/brush-stroke.js b/js/lib/geom/brush-stroke.js
index 3e64e730..a92657a4 100755
--- a/js/lib/geom/brush-stroke.js
+++ b/js/lib/geom/brush-stroke.js
@@ -37,6 +37,8 @@ var BrushStroke = function GLBrushStroke() {
37 this._strokeMaterial = null; 37 this._strokeMaterial = null;
38 this._strokeStyle = "Solid"; 38 this._strokeStyle = "Solid";
39 this._strokeDoSmoothing = false; 39 this._strokeDoSmoothing = false;
40 this._strokeUseCalligraphic = false;
41 this._strokeAngle = 0;
40 42
41 //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 43 //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
42 //smaller value means more samples for the path 44 //smaller value means more samples for the path
@@ -117,7 +119,7 @@ var BrushStroke = function GLBrushStroke() {
117 //add the point only if it is some epsilon away from the previous point 119 //add the point only if it is some epsilon away from the previous point
118 var numPoints = this._Points.length; 120 var numPoints = this._Points.length;
119 if (numPoints>0) { 121 if (numPoints>0) {
120 var threshold = 1;//this._WETNESS_FACTOR*this._strokeWidth; 122 var threshold = 2;//this._WETNESS_FACTOR*this._strokeWidth;
121 var prevPt = this._Points[numPoints-1]; 123 var prevPt = this._Points[numPoints-1];
122 var diffPt = [prevPt[0]-pt[0], prevPt[1]-pt[1]]; 124 var diffPt = [prevPt[0]-pt[0], prevPt[1]-pt[1]];
123 var diffPtMag = Math.sqrt(diffPt[0]*diffPt[0] + diffPt[1]*diffPt[1]); 125 var diffPtMag = Math.sqrt(diffPt[0]*diffPt[0] + diffPt[1]*diffPt[1]);
@@ -188,6 +190,14 @@ var BrushStroke = function GLBrushStroke() {
188 this._strokeDoSmoothing = s; 190 this._strokeDoSmoothing = s;
189 } 191 }
190 192
193 this.setStrokeUseCalligraphic = function(c){
194 this._strokeUseCalligraphic = c;
195 }
196
197 this.setStrokeAngle = function(a){
198 this._strokeAngle = a;
199 }
200
191 this.getStrokeStyle = function () { 201 this.getStrokeStyle = function () {
192 return this._strokeStyle; 202 return this._strokeStyle;
193 }; 203 };
@@ -266,6 +276,8 @@ var BrushStroke = function GLBrushStroke() {
266 } 276 }
267 } 277 }
268 } 278 }
279 //todo 4-point subdivision iterations over continuous regions of 'long' segments
280 // look at http://www.gvu.gatech.edu/~jarek/Split&Tweak/ for formula
269 //**** add samples to the long sections of the path --- Catmull-Rom spline interpolation 281 //**** add samples to the long sections of the path --- Catmull-Rom spline interpolation
270 if (this._strokeDoSmoothing && numPoints>1) { 282 if (this._strokeDoSmoothing && numPoints>1) {
271 var numInsertedPoints = 0; 283 var numInsertedPoints = 0;
@@ -496,75 +508,84 @@ var BrushStroke = function GLBrushStroke() {
496 } 508 }
497 */ 509 */
498 510
499 /*
500 //build the stamp for the brush stroke
501 //todo get this directly from the UI
502 var t=0;
503 var numTraces = this._strokeWidth;
504 var halfNumTraces = numTraces/2;
505 var startPos = [-this._strokeWidth/2,0];
506 var brushStamp = [];
507
508 //build an angled (calligraphic) brush stamp
509 var deltaDisplacement = [1,1];//[this._strokeWidth/numTraces, 0]; //a horizontal line brush
510 for (t=0;t<numTraces;t++){
511 var brushPt = [startPos[0]+t*deltaDisplacement[0], startPos[1]+t*deltaDisplacement[1]];
512 brushStamp.push(brushPt);
513 }
514 511
515 for (t=0;t<numTraces;t++){ 512 if (this._strokeUseCalligraphic) {
516 var disp = [brushStamp[t][0], brushStamp[t][1]]; 513 //build the stamp for the brush stroke
517 //ctx.globalCompositeOperation = 'source-over'; 514 var t=0;
518 var distFromMiddle = Math.abs(halfNumTraces-t); 515 var numTraces = this._strokeWidth;
519 var alphaVal = 1.0 - (100-this._strokeHardness)*(distFromMiddle/halfNumTraces)/100; 516 var halfNumTraces = numTraces/2;
520 alphaVal = 0.2; 517 var opaqueRegionHalfWidth = 0.5*this._strokeHardness*numTraces*0.01;
521 ctx.save(); 518 var maxTransparentRegionHalfWidth = halfNumTraces-opaqueRegionHalfWidth;
522 ctx.lineWidth=this._strokeWidth/10;//todo figure out the correct formula for the line width 519 var startPos = [-this._strokeWidth/2,0];
523 if (ctx.lineWidth<2) 520 var brushStamp = [];
524 ctx.lineWidth=2;
525 if (t===numTraces-1){
526 ctx.lineWidth = 1;
527 }
528 ctx.lineJoin="bevel";
529 ctx.lineCap="butt";
530 //if (t<numTraces/2)
531 ctx.strokeStyle="rgba("+parseInt(255*this._strokeColor[0])+","+parseInt(255*this._strokeColor[1])+","+parseInt(255*this._strokeColor[2])+","+alphaVal+")";
532 //else
533 // ctx.strokeStyle="rgba("+parseInt(255*this._secondStrokeColor[0])+","+parseInt(255*this._secondStrokeColor[1])+","+parseInt(255*this._secondStrokeColor[2])+","+alphaVal+")";
534 ctx.translate(disp[0],disp[1]);
535 ctx.beginPath();
536 ctx.moveTo(this._Points[0][0]-bboxMin[0], this._Points[0][1]-bboxMin[1]);
537 for (var i=0;i<numPoints;i++){
538 ctx.lineTo(this._Points[i][0]-bboxMin[0], this._Points[i][1]-bboxMin[1]);
539 }
540 ctx.stroke();
541 ctx.restore();
542 }
543 */
544 521
522 //build an angled (calligraphic) brush stamp
523 var deltaDisplacement = [Math.cos(this._strokeAngle),Math.sin(this._strokeAngle)];
524 deltaDisplacement = VecUtils.vecNormalize(2, deltaDisplacement, 1);
545 525
546 var minStrokeWidth = (this._strokeHardness*this._strokeWidth)/100; //the hardness is the percentage of the stroke width that's fully opaque 526 for (t=0;t<numTraces;t++){
547 var numlayers = 1 + (this._strokeWidth-minStrokeWidth)/2; 527 var brushPt = [startPos[0]+t*deltaDisplacement[0], startPos[1]+t*deltaDisplacement[1]];
548 var alphaVal = 1.0/(numlayers); 528 brushStamp.push(brushPt);
549 ctx.lineCap = "round";
550 ctx.lineJoin="round";
551 ctx.strokeStyle="rgba("+parseInt(255*this._strokeColor[0])+","+parseInt(255*this._strokeColor[1])+","+parseInt(255*this._strokeColor[2])+","+alphaVal+")";
552 ctx.globalCompositeOperation = 'lighter'; //we wish to add up the colors
553 ctx.globalAlpha = this._strokeColor[3];
554 for (var l=0;l<numlayers;l++){
555 ctx.beginPath();
556 ctx.moveTo(this._Points[0][0]-bboxMin[0], this._Points[0][1]-bboxMin[1]);
557 if (numPoints===1){
558 ctx.lineTo(this._Points[0][0]-bboxMin[0], this._Points[0][1]-bboxMin[1]+0.01);
559 } 529 }
560 for (var i=1;i<numPoints;i++){ 530
561 ctx.lineTo(this._Points[i][0]-bboxMin[0], this._Points[i][1]-bboxMin[1]); 531 for (t=0;t<numTraces;t++){
532 var disp = [brushStamp[t][0], brushStamp[t][1]];
533 //ctx.globalCompositeOperation = 'source-over';
534 var alphaVal = 1.0;
535 var distFromOpaqueRegion = Math.abs(t-halfNumTraces) - opaqueRegionHalfWidth;
536 if (distFromOpaqueRegion>0) {
537 alphaVal = 1.0 - distFromOpaqueRegion/maxTransparentRegionHalfWidth;
538 }
539
540 ctx.save();
541 ctx.lineWidth=this._strokeWidth/10;//todo figure out the correct formula for the line width
542 if (ctx.lineWidth<2)
543 ctx.lineWidth=2;
544 if (t===numTraces-1){
545 ctx.lineWidth = 1;
546 }
547 ctx.lineJoin="bevel";
548 ctx.lineCap="butt";
549 ctx.globalAlpha = this._strokeColor[3];
550
551 //if (t<numTraces/2)
552 ctx.strokeStyle="rgba("+parseInt(255*this._strokeColor[0])+","+parseInt(255*this._strokeColor[1])+","+parseInt(255*this._strokeColor[2])+","+alphaVal+")";
553 //else
554 // ctx.strokeStyle="rgba("+parseInt(255*this._secondStrokeColor[0])+","+parseInt(255*this._secondStrokeColor[1])+","+parseInt(255*this._secondStrokeColor[2])+","+alphaVal+")";
555 ctx.translate(disp[0],disp[1]);
556 ctx.beginPath();
557 ctx.moveTo(this._Points[0][0]-bboxMin[0], this._Points[0][1]-bboxMin[1]);
558 for (var i=0;i<numPoints;i++){
559 ctx.lineTo(this._Points[i][0]-bboxMin[0], this._Points[i][1]-bboxMin[1]);
560 }
561 ctx.stroke();
562 ctx.restore();
562 } 563 }
564 } else {
563 565
564 ctx.lineWidth=2*l+minStrokeWidth; 566 ctx.globalCompositeOperation = 'lighter'; //we wish to add up the colors
565 ctx.stroke(); 567 ctx.globalAlpha = this._strokeColor[3];
568 ctx.lineCap = "round";
569 ctx.lineJoin="round";
570 var minStrokeWidth = (this._strokeHardness*this._strokeWidth)/100; //the hardness is the percentage of the stroke width that's fully opaque
571 var numlayers = 1 + (this._strokeWidth-minStrokeWidth)/2;
572 var alphaVal = 1.0/(numlayers); //this way the alpha at the first path will be 1
573 ctx.strokeStyle="rgba("+parseInt(255*this._strokeColor[0])+","+parseInt(255*this._strokeColor[1])+","+parseInt(255*this._strokeColor[2])+","+alphaVal+")";
574 for (var l=0;l<numlayers;l++){
575 ctx.beginPath();
576 ctx.moveTo(this._Points[0][0]-bboxMin[0], this._Points[0][1]-bboxMin[1]);
577 if (numPoints===1){
578 //display a tiny segment as a single point
579 ctx.lineTo(this._Points[0][0]-bboxMin[0], this._Points[0][1]-bboxMin[1]+0.01);
580 }
581 for (var i=1;i<numPoints;i++){
582 ctx.lineTo(this._Points[i][0]-bboxMin[0], this._Points[i][1]-bboxMin[1]);
583 }
584 ctx.lineWidth=2*l+minStrokeWidth;
585 ctx.stroke();
586 }
566 } 587 }
567 588
568 ctx.restore(); 589 ctx.restore();
569 }; //render() 590 }; //render()
570 591