From 84332ab81c1b445195f1d9be8bbeae0725c8e758 Mon Sep 17 00:00:00 2001 From: Valerio Virgillito Date: Tue, 6 Mar 2012 10:58:25 -0800 Subject: Squashed commit of preload-fix into Master - Requiring all the previously pre-loaded files - RDGE, Codemirror and gl-matrix are not included via a script tag. Signed-off-by: Valerio Virgillito --- js/lib/geom/brush-stroke.js | 482 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 482 insertions(+) create mode 100755 js/lib/geom/brush-stroke.js (limited to 'js/lib/geom/brush-stroke.js') diff --git a/js/lib/geom/brush-stroke.js b/js/lib/geom/brush-stroke.js new file mode 100755 index 00000000..9a934928 --- /dev/null +++ b/js/lib/geom/brush-stroke.js @@ -0,0 +1,482 @@ +/* +This file contains proprietary software owned by Motorola Mobility, Inc.
+No rights, expressed or implied, whatsoever to this software are provided by Motorola Mobility, Inc. hereunder.
+(c) Copyright 2011 Motorola Mobility, Inc. All Rights Reserved. +
*/ + +// Todo: This entire class should be converted to a module +var VecUtils = require("js/helper-classes/3D/vec-utils").VecUtils; +var GeomObj = require("js/lib/geom/geom-obj").GeomObj; + +/////////////////////////////////////////////////////////////////////// +// Class GLBrushStroke +// representation a sequence points (polyline) created by brush tool. +// Derived from class GLGeomObj +/////////////////////////////////////////////////////////////////////// +var BrushStroke = function GLBrushStroke() { + /////////////////////////////////////////////////// + // Instance variables + /////////////////////////////////////////////////// + this._Points = []; + this._BBoxMin = [0, 0, 0]; + this._BBoxMax = [0, 0, 0]; + this._dirty = true; + + //whether or not to use the canvas drawing to stroke/fill + this._useCanvasDrawing = true; + + //the X and Y location of this subpath's canvas in stage world space of Ninja + this._canvasX = 0; + this._canvasY = 0; + + //stroke information + this._strokeWidth = 0.0; + this._strokeColor = [0.4, 0.4, 0.4, 1.0]; + this._strokeMaterial = null; + this._strokeStyle = "Solid"; + + //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 + //smaller value means more samples for the path + this._WETNESS_FACTOR = 0.25; + + //prevent extremely long paths that can take a long time to render + this._MAX_ALLOWED_SAMPLES = 500; + + //drawing context + this._world = null; + + //tool that owns this brushstroke + this._drawingTool = null; + this._planeMat = null; + this._planeMatInv = null; + this._planeCenter = null; + + ///////////////////////////////////////////////////////// + // Property Accessors/Setters + ///////////////////////////////////////////////////////// + this.setWorld = function (world) { + this._world = world; + }; + + this.getWorld = function () { + return this._world; + }; + + this.geomType = function () { + return this.GEOM_TYPE_CUBIC_BEZIER; + }; + + this.setDrawingTool = function (tool) { + this._drawingTool = tool; + }; + + this.getDrawingTool = function () { + return this._drawingTool; + }; + + this.setPlaneMatrix = function(planeMat){ + this._planeMat = planeMat; + }; + + this.setPlaneMatrixInverse = function(planeMatInv){ + this._planeMatInv = planeMatInv; + }; + + this.setPlaneCenter = function(pc){ + this._planeCenter = pc; + }; + + this.getCanvasX = function(){ + return this._canvasX; + }; + + this.getCanvasY = function(){ + return this._canvasY; + }; + + this.setCanvasX = function(cx){ + this._canvasX=cx; + }; + + this.setCanvasY = function(cy){ + this._canvasY=cy; + }; + + this.getNumPoints = function () { + return this._Points.length; + }; + + this.getPoint = function (index) { + return this._Points[index]; + }; + + this.addPoint = function (pt) { + //add the point only if it is some epsilon away from the previous point + var numPoints = this._Points.length; + if (numPoints>0) { + var threshold = this._WETNESS_FACTOR*this._strokeWidth; + var prevPt = this._Points[numPoints-1]; + var diffPt = [prevPt[0]-pt[0], prevPt[1]-pt[1]]; + var diffPtMag = Math.sqrt(diffPt[0]*diffPt[0] + diffPt[1]*diffPt[1]); + if (diffPtMag>threshold){ + this._Points.push(pt); + this._dirty=true; + } + } else { + this._Points.push(pt); + this._dirty=true; + } + }; + + this.insertPoint = function(pt, index){ + this._Points.splice(index, 0, pt); this._dirty=true; + }; + + this.isDirty = function(){ + return this._dirty; + }; + + this.makeDirty = function(){ + this._dirty=true; + }; + + this.getBBoxMin = function () { + return this._BBoxMin; + }; + + this.getBBoxMax = function () { + return this._BBoxMax; + }; + + this.getStrokeWidth = function () { + return this._strokeWidth; + }; + + this.setStrokeWidth = function (w) { + this._strokeWidth = w; + this._dirty=true; + }; + + this.getStrokeMaterial = function () { + return this._strokeMaterial; + }; + + this.setStrokeMaterial = function (m) { + this._strokeMaterial = m; + }; + + this.getStrokeColor = function () { + return this._strokeColor; + }; + + this.setStrokeColor = function (c) { + this._strokeColor = c; + }; + + this.getStrokeStyle = function () { + return this._strokeStyle; + }; + + this.setStrokeStyle = function (s) { + this._strokeStyle = s; + }; + + this.setWidth = function () { + + };//NO-OP for now + + this.setHeight = function () { + + };//NO-OP for now + + + //remove and return anchor at specified index, return null on error + this.removePoint = function (index) { + var retAnchor = null; + if (index < this._Points.length) { + retPt = this._Points.splice(index, 1); + this._dirty=true; + } + return retPoint; + }; + + //remove all the points + this.clear = function () { + this._Points = []; + this._dirty=true; + } + + this.translate = function (tx, ty, tz) { + for (var i=0;i1) { + var threshold = this._WETNESS_FACTOR*this._strokeWidth; + var prevPt = this._Points[0]; + var prevIndex = 0; + for (var i=1;ithreshold){ + //insert points along the prev. to current point + var numNewPoints = Math.floor(distance/threshold); + for (var j=0;j this._MAX_ALLOWED_SAMPLES){ + console.log("leaving the resampling because numPoints is greater than limit:"+this._MAX_ALLOWED_SAMPLES); + break; + } + } + } + + // *** compute the bounding box ********* + this._BBoxMin = [Infinity, Infinity, Infinity]; + this._BBoxMax = [-Infinity, -Infinity, -Infinity]; + numPoints = this._Points.length; + if (numPoints === 0) { + this._BBoxMin = [0, 0, 0]; + this._BBoxMax = [0, 0, 0]; + } else { + for (var i=0;i pt[d]) { + this._BBoxMin[d] = pt[d]; + } + if (this._BBoxMax[d] < pt[d]) { + this._BBoxMax[d] = pt[d]; + } + }//for every dimension d from 0 to 2 + } + } + //increase the bbox given the stroke width + for (var d = 0; d < 3; d++) { + this._BBoxMin[d]-= this._strokeWidth/2; + this._BBoxMax[d]+= this._strokeWidth/2; + }//for every dimension d from 0 to 2 + } + this._dirty = false; + }; + + this.buildBuffers = function () { + //return; //no need to do anything for now + };//buildBuffers() + + //render + // specify how to render the subpath in Canvas2D + this.render = function () { + // get the world + var world = this.getWorld(); + if (!world) throw( "null world in brushstroke render" ); + + // get the context + var ctx = world.get2DContext(); + if (!ctx) throw ("null context in brushstroke render") + + var numPoints = this.getNumPoints(); + if (numPoints === 0) { + return; //nothing to do for empty paths + } + + ctx.save(); + + this.computeMetaGeometry(); + var bboxMin = this.getBBoxMin(); + var bboxMax = this.getBBoxMax(); + var bboxWidth = bboxMax[0] - bboxMin[0]; + var bboxHeight = bboxMax[1] - bboxMin[1]; + ctx.clearRect(0, 0, bboxWidth, bboxHeight); + + /* + ctx.lineWidth = this._strokeWidth; + ctx.strokeStyle = "black"; + if (this._strokeColor) + ctx.strokeStyle = MathUtils.colorToHex( this._strokeColor ); + ctx.fillStyle = "blue"; + if (this._fillColor) + ctx.fillStyle = MathUtils.colorToHex( this._fillColor ); + var lineCap = ['butt','round','square']; + ctx.lineCap = lineCap[1]; + ctx.beginPath(); + var firstPoint = this._Points[0]; + ctx.moveTo(firstPoint[0]-bboxMin[0], firstPoint[1]-bboxMin[1]); + for (var i = 1; i < numPoints; i++) { + var pt = this._Points[i]; + ctx.lineTo(pt[0]-bboxMin[0], pt[1]-bboxMin[1]); + } + ctx.stroke(); + */ + + /* + var isDebug = false; + var prevPt = this._Points[0]; + var prevX = prevPt[0]-bboxMin[0]; + var prevY = prevPt[1]-bboxMin[1]; + prevPt = [prevX,prevY]; + for (var i = 1; i < numPoints; i++) { + var pt = this._Points[i]; + ctx.globalCompositeOperation = 'source-over'; + var x = pt[0]-bboxMin[0]; + var y = pt[1]-bboxMin[1]; + pt = [x,y]; + + //vector from prev to current pt + var seg = VecUtils.vecSubtract(2, pt, prevPt); + var segDir = VecUtils.vecNormalize(2, seg, 1.0); + + var segMidPt = VecUtils.vecInterpolate(2, pt, prevPt, 0.5); + var w2 = this._strokeWidth*0.5; + var segDirOrtho = [w2*segDir[1], -w2*segDir[0]]; + + //add half the strokewidth to the segMidPt + var lgStart = VecUtils.vecAdd(2, segMidPt, segDirOrtho); + var lgEnd = VecUtils.vecSubtract(2, segMidPt, segDirOrtho); + + ctx.save(); + ctx.beginPath(); + + if (isDebug) { + ctx.strokeStyle="black"; + ctx.lineWidth = 1; + + ctx.moveTo(lgStart[0], lgStart[1]); + ctx.lineTo(lgEnd[0], lgEnd[1]); + ctx.stroke(); + } + + var lg = ctx.createLinearGradient(lgStart[0], lgStart[1], lgEnd[0], lgEnd[1]); + lg.addColorStop(1, 'rgba(0,0,0,0.0)'); + lg.addColorStop(0.5,'rgba(255,0,0,1.0)'); + lg.addColorStop(0, 'rgba(0,0,0,0.0)'); + ctx.fillStyle = lg; + + if (isDebug){ + ctx.strokeStyle="blue"; + ctx.lineWidth=0.5; + } + ctx.moveTo(prevX-w2, prevY); + ctx.lineTo(prevX+w2, prevY); + ctx.lineTo(x+w2, y); + ctx.lineTo(x-w2, y); + ctx.lineTo(prevX-w2, prevY); + ctx.fill(); + ctx.closePath(); + + ctx.restore(); + + prevPt = pt; + prevX = x; + prevY = y; + } + + + if (isDebug) + ctx.stroke(); + + if (isDebug){ + //draw the skeleton of this stroke + ctx.lineWidth = 1; + ctx.strokeStyle = "black"; + var pt = this._Points[0]; + ctx.beginPath(); + ctx.moveTo(pt[0]-bboxMin[0],pt[1]-bboxMin[1]); + for (var i = 1; i < numPoints; i++) { + pt = this._Points[i]; + var x = pt[0]-bboxMin[0]; + var y = pt[1]-bboxMin[1]; + ctx.lineTo(x,y); + } + ctx.stroke(); + } + */ + + + var R2 = this._strokeWidth; + var R = R2*0.5; + var hardness = 0; //for a pencil, this is always 1 //TODO get hardness parameter from user interface + var innerRadius = (hardness*R)-1; + if (innerRadius<1) + innerRadius=1; + + var r = ctx.createRadialGradient(0,0,innerRadius, 0,0,R); + var midColor = "rgba("+parseInt(255*this._strokeColor[0])+","+parseInt(255*this._strokeColor[1])+","+parseInt(255*this._strokeColor[2])+",1)"; + r.addColorStop(0, midColor); + var endColor = "rgba("+parseInt(255*this._strokeColor[0])+","+parseInt(255*this._strokeColor[1])+","+parseInt(255*this._strokeColor[2])+",0.0)"; + r.addColorStop(1, endColor); + ctx.fillStyle = r; + + for (var i = 0; i < numPoints; i++) { + var pt = this._Points[i]; + ctx.globalCompositeOperation = 'source-over'; + var x = pt[0]-bboxMin[0]; + var y = pt[1]-bboxMin[1]; + ctx.save(); + ctx.translate(x,y); + ctx.arc(0, 0, R, 0, 2 * Math.PI, false); + ctx.fill(); + ctx.restore(); + //ctx.globalCompositeOperation = 'source-in'; + //ctx.rect(x-R, y-R, R2, R2); + } + + ctx.restore(); + }; //render() + + + this.export = function() { + return "type: " + this.geomType() + "\n"; + }; + + this.import = function( importStr ) { + + } + + this.collidesWithPoint = function (x, y, z) { + if (x < this._BBoxMin[0]) return false; + if (x > this._BBoxMax[0]) return false; + if (y < this._BBoxMin[1]) return false; + if (y > this._BBoxMax[1]) return false; + if (z < this._BBoxMin[2]) return false; + if (z > this._BBoxMax[2]) return false; + + return true; + }; + + this.collidesWithPoint = function (x, y) { + if (x < this._BBoxMin[0]) return false; + if (x > this._BBoxMax[0]) return false; + if (y < this._BBoxMin[1]) return false; + if (y > this._BBoxMax[1]) return false; + + return true; + }; + +}; //function GLSubpath ...class definition + +BrushStroke.prototype = new GeomObj(); + +if (typeof exports === "object") { + exports.BrushStroke = BrushStroke; +} \ No newline at end of file -- cgit v1.2.3