diff options
-rwxr-xr-x | js/helper-classes/3D/math-utils.js | 11 | ||||
-rwxr-xr-x | js/lib/geom/brush-stroke.js | 93 | ||||
-rwxr-xr-x | js/lib/geom/sub-path.js | 5 | ||||
-rw-r--r-- | js/tools/BrushTool.js | 8 | ||||
-rwxr-xr-x | js/tools/PenTool.js | 257 |
5 files changed, 291 insertions, 83 deletions
diff --git a/js/helper-classes/3D/math-utils.js b/js/helper-classes/3D/math-utils.js index 2f0283a9..35ee8112 100755 --- a/js/helper-classes/3D/math-utils.js +++ b/js/helper-classes/3D/math-utils.js | |||
@@ -928,17 +928,18 @@ var MathUtilsClass = exports.MathUtilsClass = Object.create(Object.prototype, { | |||
928 | return 0; | 928 | return 0; |
929 | } | 929 | } |
930 | //TODO testing...remove this block | 930 | //TODO testing...remove this block |
931 | console.log("getAxisAngleBetween3DVectors Angle: "+angle); | ||
932 | if (isNaN(angle)){ | 931 | if (isNaN(angle)){ |
933 | console.log("getAxisAngleBetween3DVectors Angle is NaN"); | 932 | console.log("Warning! getAxisAngleBetween3DVectors Angle is NaN"); |
934 | } | 933 | } |
935 | //TODO end testing block | 934 | //TODO end testing block |
936 | //optionally, if axis is provided, create the axis of rotation as well | 935 | //optionally, if axis is provided, create the axis of rotation as well |
937 | var rotAxis = VecUtils.vecCross(3, v1n, v2n); | 936 | var rotAxis = VecUtils.vecCross(3, v1n, v2n); |
938 | rotAxis = VecUtils.vecNormalize(3, rotAxis, 1); | 937 | rotAxis = VecUtils.vecNormalize(3, rotAxis, 1); |
939 | axis[0] = rotAxis[0]; | 938 | if (axis){ |
940 | axis[1] = rotAxis[1]; | 939 | axis[0] = rotAxis[0]; |
941 | axis[2] = rotAxis[2]; | 940 | axis[1] = rotAxis[1]; |
941 | axis[2] = rotAxis[2]; | ||
942 | } | ||
942 | return angle; | 943 | return angle; |
943 | } | 944 | } |
944 | }, | 945 | }, |
diff --git a/js/lib/geom/brush-stroke.js b/js/lib/geom/brush-stroke.js index 1fae0c1d..519e0433 100755 --- a/js/lib/geom/brush-stroke.js +++ b/js/lib/geom/brush-stroke.js | |||
@@ -264,13 +264,67 @@ var BrushStroke = function GLBrushStroke() { | |||
264 | this._strokeStyle = s; | 264 | this._strokeStyle = s; |
265 | }; | 265 | }; |
266 | 266 | ||
267 | this.setWidth = function () { | 267 | this.setWidth = function (newW) { |
268 | if (newW<1) { | ||
269 | newW=1; //clamp minimum width to 1 | ||
270 | } | ||
271 | |||
272 | //scale the contents of this subpath to lie within this width | ||
273 | //determine the scale factor by comparing with the old width | ||
274 | var oldWidth = this._BBoxMax[0]-this._BBoxMin[0]; | ||
275 | if (oldWidth<1) { | ||
276 | oldWidth=1; | ||
277 | } | ||
278 | |||
279 | var scaleX = newW/oldWidth; | ||
280 | if (scaleX===1) { | ||
281 | return; //no need to do anything | ||
282 | } | ||
283 | |||
284 | //scale the local point positions such that the width of the bbox is the newW | ||
285 | var origX = this._BBoxMin[0]; | ||
286 | var numPoints = this._LocalPoints.length; | ||
287 | for (var i=0;i<numPoints;i++){ | ||
288 | //compute the distance from the bboxMin | ||
289 | var oldW = this._LocalPoints[i][0] - origX; | ||
290 | this._LocalPoints[i] = [(origX + oldW*scaleX),this._LocalPoints[i][1],this._LocalPoints[i][2]]; | ||
291 | |||
292 | oldW = this._OrigLocalPoints[i][0] - origX; | ||
293 | this._OrigLocalPoints[i] = [(origX + oldW*scaleX),this._OrigLocalPoints[i][1],this._OrigLocalPoints[i][2]]; | ||
294 | } | ||
295 | this._isDirty = true; | ||
296 | }; | ||
297 | |||
298 | this.setHeight = function (newH) { | ||
299 | if (newH<1) { | ||
300 | newH=1; //clamp minimum width to 1 | ||
301 | } | ||
268 | 302 | ||
269 | };//NO-OP for now | 303 | //scale the contents of this subpath to lie within this height |
304 | //determine the scale factor by comparing with the old height | ||
305 | var oldHeight = this._BBoxMax[1]-this._BBoxMin[1]; | ||
306 | if (oldHeight<1) { | ||
307 | oldHeight=1; | ||
308 | } | ||
309 | |||
310 | var scaleY = newH/oldHeight; | ||
311 | if (scaleY===1) { | ||
312 | return; //no need to do anything | ||
313 | } | ||
270 | 314 | ||
271 | this.setHeight = function () { | 315 | //scale the local point positions such that the width of the bbox is the newW |
316 | var origY = this._BBoxMin[1]; | ||
317 | var numPoints = this._LocalPoints.length; | ||
318 | for (var i=0;i<numPoints;i++){ | ||
319 | //compute the distance from the bboxMin | ||
320 | var oldH = this._LocalPoints[i][1] - origY; | ||
321 | this._LocalPoints[i] = [this._LocalPoints[i][0],(origY + oldH*scaleY),this._LocalPoints[i][2]]; | ||
272 | 322 | ||
273 | };//NO-OP for now | 323 | oldH = this._OrigLocalPoints[i][1] - origY; |
324 | this._OrigLocalPoints[i] = [this._OrigLocalPoints[i][0],(origY + oldH*scaleY),this._OrigLocalPoints[i][2]]; | ||
325 | } | ||
326 | this._isDirty = true; | ||
327 | }; | ||
274 | 328 | ||
275 | this.getWidth = function() { | 329 | this.getWidth = function() { |
276 | if (this._isDirty){ | 330 | if (this._isDirty){ |
@@ -583,7 +637,7 @@ var BrushStroke = function GLBrushStroke() { | |||
583 | //build the stamp for the brush stroke | 637 | //build the stamp for the brush stroke |
584 | var t=0; | 638 | var t=0; |
585 | var numTraces = this._strokeWidth; | 639 | var numTraces = this._strokeWidth; |
586 | var halfNumTraces = numTraces/2; | 640 | var halfNumTraces = numTraces*0.5; |
587 | var opaqueRegionHalfWidth = 0.5*this._strokeHardness*numTraces*0.01; //the 0.01 is to convert the strokeHardness from [0,100] to [0,1] | 641 | var opaqueRegionHalfWidth = 0.5*this._strokeHardness*numTraces*0.01; //the 0.01 is to convert the strokeHardness from [0,100] to [0,1] |
588 | var maxTransparentRegionHalfWidth = halfNumTraces-opaqueRegionHalfWidth; | 642 | var maxTransparentRegionHalfWidth = halfNumTraces-opaqueRegionHalfWidth; |
589 | 643 | ||
@@ -603,20 +657,23 @@ var BrushStroke = function GLBrushStroke() { | |||
603 | ctx.lineCap="butt"; | 657 | ctx.lineCap="butt"; |
604 | ctx.globalCompositeOperation = 'source-over'; | 658 | ctx.globalCompositeOperation = 'source-over'; |
605 | ctx.globalAlpha = this._strokeColor[3]; | 659 | ctx.globalAlpha = this._strokeColor[3]; |
606 | //todo figure out the correct formula for the line width | 660 | |
607 | ctx.lineWidth=2; | ||
608 | if (t===numTraces-1){ | ||
609 | ctx.lineWidth = 1; | ||
610 | } | ||
611 | for (t=0;t<numTraces;t++){ | 661 | for (t=0;t<numTraces;t++){ |
612 | var disp = [brushStamp[t][0], brushStamp[t][1]]; | 662 | var disp = [brushStamp[t][0], brushStamp[t][1]]; |
613 | var alphaVal = 1.0; | 663 | var alphaVal = 1.0; |
614 | var distFromOpaqueRegion = Math.abs(t-halfNumTraces) - opaqueRegionHalfWidth; | 664 | var distFromOpaqueRegion = Math.abs(t-halfNumTraces) - opaqueRegionHalfWidth; |
615 | if (distFromOpaqueRegion>0) { | 665 | if (distFromOpaqueRegion>0) { |
616 | alphaVal = 1.0 - distFromOpaqueRegion/maxTransparentRegionHalfWidth; | 666 | var transparencyFactor = distFromOpaqueRegion/maxTransparentRegionHalfWidth; |
617 | alphaVal *= 1.0/ctx.lineWidth; //factor that accounts for lineWidth !== 1 | 667 | alphaVal = 1.0 - transparencyFactor;//(transparencyFactor*transparencyFactor);//the square term produces nonlinearly varying alpha values |
668 | alphaVal *= 0.5; //factor that accounts for lineWidth == 2 | ||
618 | } | 669 | } |
619 | ctx.save(); | 670 | ctx.save(); |
671 | if (t === (numTraces-1) || t === 0){ | ||
672 | ctx.lineWidth = 1; | ||
673 | } else { | ||
674 | //todo figure out the correct formula for the line width | ||
675 | ctx.lineWidth=2; | ||
676 | } | ||
620 | ctx.strokeStyle="rgba("+parseInt(255*this._strokeColor[0])+","+parseInt(255*this._strokeColor[1])+","+parseInt(255*this._strokeColor[2])+","+alphaVal+")"; | 677 | ctx.strokeStyle="rgba("+parseInt(255*this._strokeColor[0])+","+parseInt(255*this._strokeColor[1])+","+parseInt(255*this._strokeColor[2])+","+alphaVal+")"; |
621 | //linearly interpolate between the two stroke colors | 678 | //linearly interpolate between the two stroke colors |
622 | var currStrokeColor = VecUtils.vecInterpolate(4, this._strokeColor, this._secondStrokeColor, t/numTraces); | 679 | var currStrokeColor = VecUtils.vecInterpolate(4, this._strokeColor, this._secondStrokeColor, t/numTraces); |
@@ -650,7 +707,7 @@ var BrushStroke = function GLBrushStroke() { | |||
650 | ctx.lineCap = "round"; | 707 | ctx.lineCap = "round"; |
651 | ctx.lineJoin="round"; | 708 | ctx.lineJoin="round"; |
652 | var minStrokeWidth = (this._strokeHardness*this._strokeWidth)/100; //the hardness is the percentage of the stroke width that's fully opaque | 709 | var minStrokeWidth = (this._strokeHardness*this._strokeWidth)/100; //the hardness is the percentage of the stroke width that's fully opaque |
653 | var numlayers = 1 + (this._strokeWidth-minStrokeWidth)/2; | 710 | var numlayers = 1 + Math.ceil((this._strokeWidth-minStrokeWidth)*0.5); |
654 | var alphaVal = 1.0/(numlayers); //this way the alpha at the first path will be 1 | 711 | var alphaVal = 1.0/(numlayers); //this way the alpha at the first path will be 1 |
655 | ctx.strokeStyle="rgba("+parseInt(255*this._strokeColor[0])+","+parseInt(255*this._strokeColor[1])+","+parseInt(255*this._strokeColor[2])+","+alphaVal+")"; | 712 | ctx.strokeStyle="rgba("+parseInt(255*this._strokeColor[0])+","+parseInt(255*this._strokeColor[1])+","+parseInt(255*this._strokeColor[2])+","+alphaVal+")"; |
656 | for (var l=0;l<numlayers;l++){ | 713 | for (var l=0;l<numlayers;l++){ |
@@ -678,6 +735,16 @@ var BrushStroke = function GLBrushStroke() { | |||
678 | ctx.lineTo(p[0],p[1]); | 735 | ctx.lineTo(p[0],p[1]); |
679 | } | 736 | } |
680 | ctx.lineWidth=2*l+minStrokeWidth; | 737 | ctx.lineWidth=2*l+minStrokeWidth; |
738 | |||
739 | |||
740 | //experiments with shadows | ||
741 | /* | ||
742 | ctx.shadowOffsetX = 10; | ||
743 | ctx.shadowOffsetY = 10; | ||
744 | ctx.shadowBlur = 10; | ||
745 | ctx.shadowColor = //"rgb("+parseInt(255*this._strokeColor[0])+","+parseInt(255*this._strokeColor[1])+","+parseInt(255*this._strokeColor[2])+")"; | ||
746 | "#FF6666"; //or use rgb(red, green, blue) | ||
747 | */ | ||
681 | ctx.stroke(); | 748 | ctx.stroke(); |
682 | }//for every layer l | 749 | }//for every layer l |
683 | } //if there is no calligraphic stroke | 750 | } //if there is no calligraphic stroke |
diff --git a/js/lib/geom/sub-path.js b/js/lib/geom/sub-path.js index 56c94df3..24acb2b0 100755 --- a/js/lib/geom/sub-path.js +++ b/js/lib/geom/sub-path.js | |||
@@ -166,6 +166,8 @@ var GLSubpath = function GLSubpath() { | |||
166 | }; | 166 | }; |
167 | 167 | ||
168 | this.setWidth = function (newW) { | 168 | this.setWidth = function (newW) { |
169 | var strokeWidth = this._strokeWidth; | ||
170 | var halfStrokeWidth = strokeWidth*0.5; | ||
169 | if (newW<1) { | 171 | if (newW<1) { |
170 | newW=1; //clamp minimum width to 1 | 172 | newW=1; //clamp minimum width to 1 |
171 | } | 173 | } |
@@ -183,7 +185,7 @@ var GLSubpath = function GLSubpath() { | |||
183 | } | 185 | } |
184 | 186 | ||
185 | //scale the anchor point positions such that the width of the bbox is the newW | 187 | //scale the anchor point positions such that the width of the bbox is the newW |
186 | var origX = this._BBoxMin[0]; | 188 | var origX = this._BBoxMin[0]; //this should always be zero since we only deal with local coordinates |
187 | var numAnchors = this._Anchors.length; | 189 | var numAnchors = this._Anchors.length; |
188 | for (var i=0;i<numAnchors;i++){ | 190 | for (var i=0;i<numAnchors;i++){ |
189 | //compute the distance from the bboxMin | 191 | //compute the distance from the bboxMin |
@@ -196,6 +198,7 @@ var GLSubpath = function GLSubpath() { | |||
196 | this._Anchors[i].setNextPos(origX + nextW*scaleX,this._Anchors[i].getNextY(),this._Anchors[i].getNextZ()); | 198 | this._Anchors[i].setNextPos(origX + nextW*scaleX,this._Anchors[i].getNextY(),this._Anchors[i].getNextZ()); |
197 | } | 199 | } |
198 | this.makeDirty(); | 200 | this.makeDirty(); |
201 | this.computeBoundingBox(true, false); | ||
199 | }; | 202 | }; |
200 | 203 | ||
201 | this.setHeight = function (newH) { | 204 | this.setHeight = function (newH) { |
diff --git a/js/tools/BrushTool.js b/js/tools/BrushTool.js index 5c334b92..5d4e8288 100644 --- a/js/tools/BrushTool.js +++ b/js/tools/BrushTool.js | |||
@@ -225,9 +225,11 @@ exports.BrushTool = Montage.create(ShapeTool, { | |||
225 | this._hasDraw = false; | 225 | this._hasDraw = false; |