diff options
author | Pushkar Joshi | 2012-03-08 16:03:17 -0800 |
---|---|---|
committer | Pushkar Joshi | 2012-03-08 16:03:17 -0800 |
commit | fcab2a14f8de302948d38f55db41e2c1c92baa34 (patch) | |
tree | 992992a43e0cff5fab528a84448fd01328077392 /js/lib/geom | |
parent | 703fb3d06e88257ac73c1d1a0ec6ca33a64f4371 (diff) | |
download | ninja-fcab2a14f8de302948d38f55db41e2c1c92baa34.tar.gz |
brush stroke options for calligraphic brush style
Diffstat (limited to 'js/lib/geom')
-rwxr-xr-x | js/lib/geom/brush-stroke.js | 147 |
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 | ||