From 76f2021618c0a6a99a1b855233e353e84ca99467 Mon Sep 17 00:00:00 2001 From: Pushkar Joshi Date: Tue, 13 Mar 2012 11:23:32 -0700 Subject: Add a smoothing amount parameter, and hide options based on checkboxes --- js/lib/geom/brush-stroke.js | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) (limited to 'js/lib') diff --git a/js/lib/geom/brush-stroke.js b/js/lib/geom/brush-stroke.js index 4c42539a..52f81ffe 100755 --- a/js/lib/geom/brush-stroke.js +++ b/js/lib/geom/brush-stroke.js @@ -39,10 +39,7 @@ var BrushStroke = function GLBrushStroke() { this._strokeDoSmoothing = false; this._strokeUseCalligraphic = false; this._strokeAngle = 0; - - //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; + this._strokeAmountSmoothing = 0; //threshold that tells us whether two samples are too far apart this._MAX_SAMPLE_DISTANCE_THRESHOLD = 5; @@ -125,7 +122,7 @@ var BrushStroke = function GLBrushStroke() { //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._MIN_SAMPLE_DISTANCE_THRESHOLD;//this._WETNESS_FACTOR*this._strokeWidth; + var threshold = this._MIN_SAMPLE_DISTANCE_THRESHOLD; 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]); @@ -196,6 +193,10 @@ var BrushStroke = function GLBrushStroke() { this._strokeDoSmoothing = s; } + this.setSmoothingAmount = function(a){ + this._strokeAmountSmoothing = a; + } + this.setStrokeUseCalligraphic = function(c){ this._strokeUseCalligraphic = c; } @@ -252,7 +253,7 @@ var BrushStroke = function GLBrushStroke() { //**** add samples to the path if needed...linear interpolation for now //if (numPoints>1) { if (0){ - var threshold = this._WETNESS_FACTOR*this._strokeWidth; + var threshold = this._MAX_SAMPLE_DISTANCE_THRESHOLD; var prevPt = this._Points[0]; var prevIndex = 0; for (var i=1;i1) { @@ -330,7 +331,7 @@ var BrushStroke = function GLBrushStroke() { console.log("Inserted "+numInsertedPoints+" additional CatmullRom points"); //now do 3-4 iterations of Laplacian smoothing (setting the points to the average of their neighbors) - var numLaplacianIterations = 3; //todo figure out the proper number of Laplacian iterations (perhaps as a function of stroke width) + var numLaplacianIterations = this._strokeAmountSmoothing; for (var n=0;n1) { - if (0){ - var threshold = this._MAX_SAMPLE_DISTANCE_THRESHOLD; - 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; + var numPoints = this._Points.length; + if (this._addedSamples === false){ + //**** add samples to the long sections of the path --- Catmull-Rom spline interpolation ***** + // instead of the following, may use 4-point subdivision iterations over continuous regions of 'long' segments + // look at http://www.gvu.gatech.edu/~jarek/Split&Tweak/ for formula + + var numInsertedPoints = 0; + var newSampledPoints = []; + var threshold = this._MAX_SAMPLE_DISTANCE_THRESHOLD;//this determines whether a segment between two sample is long enough to warrant checking for angle + var prevPt = this._Points[0]; + newSampledPoints.push(this._Points[0]); + for (var i=1;ithreshold){ + //build the control polygon for the Catmull-Rom spline (prev. 2 points and next 2 points) + var prev = (i===1) ? i-1 : i-2; + var next = (i===numPoints-1) ? i : i+1; + var ctrlPts = [this._Points[prev], this._Points[i-1], this._Points[i], this._Points[next]]; + //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; + //} } + this._Points = newSampledPoints.slice(0); + newSampledPoints = []; + console.log("Inserted "+numInsertedPoints+" additional CatmullRom points"); + this._addedSamples = true; + this._dirty=true; + } + //build a copy of the original points...this should be done only once + if (this._storedOrigPoints === false) { + this._OrigPoints = this._Points.slice(0); + this._storedOrigPoints = true; + } - //instead of the following, may use 4-point subdivision iterations over continuous regions of 'long' segments - // look at http://www.gvu.gatech.edu/~jarek/Split&Tweak/ for formula - //**** add samples to the long sections of the path --- Catmull-Rom spline interpolation + if (this._dirty) { + this._Points = this._OrigPoints.slice(0); + numPoints = this._Points.length; if (this._strokeDoSmoothing && numPoints>1) { - var numInsertedPoints = 0; - var newPoints = []; - var threshold = this._MAX_SAMPLE_DISTANCE_THRESHOLD;//this determines whether a segment between two sample is long enough to warrant checking for angle - var prevPt = this._Points[0]; - newPoints.push(this._Points[0]); - for (var i=1;ithreshold){ - //build the control polygon for the Catmull-Rom spline (prev. 2 points and next 2 points) - var prev = (i===1) ? i-1 : i-2; - var next = (i===numPoints-1) ? i : i+1; - var ctrlPts = [this._Points[prev], this._Points[i-1], this._Points[i], this._Points[next]]; - //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; - } - } - this._Points = newPoints; - numPoints = this._Points.length; - console.log("Inserted "+numInsertedPoints+" additional CatmullRom points"); - - //now do 3-4 iterations of Laplacian smoothing (setting the points to the average of their neighbors) - var numLaplacianIterations = this._strokeAmountSmoothing; + //iterations of Laplacian smoothing (setting the points to the average of their neighbors) + var numLaplacianIterations = this._strokeAmountSmoothing; for (var n=0;n */ -// 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; +var CanvasController = require("js/controllers/elements/canvas-controller").CanvasController; + +// Todo: This entire class should be converted to a module /////////////////////////////////////////////////////////////////////// // Class GLBrushStroke @@ -28,7 +30,10 @@ var BrushStroke = function GLBrushStroke() { //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 + //the HTML5 canvas that holds this brush stroke + this._canvas = null; + + //the X and Y location of this brush stroke canvas in stage world space of Ninja this._canvasX = 0; this._canvasY = 0; @@ -65,6 +70,10 @@ var BrushStroke = function GLBrushStroke() { ///////////////////////////////////////////////////////// // Property Accessors/Setters ///////////////////////////////////////////////////////// + this.setCanvas = function(c) { + this._canvas = c; + } + this.setWorld = function (world) { this._world = world; }; @@ -195,6 +204,9 @@ var BrushStroke = function GLBrushStroke() { this._dirty = true; } } + this.getStrokeHardness = function(){ + return this._strokeHardness; + } this.setDoSmoothing = function(s){ if (this._strokeDoSmoothing!==s) { @@ -203,12 +215,19 @@ var BrushStroke = function GLBrushStroke() { } } + this.getDoSmoothing = function(){ + return this._strokeDoSmoothing; + } + this.setSmoothingAmount = function(a){ if (this._strokeAmountSmoothing!==a) { this._strokeAmountSmoothing = a; this._dirty = true; } + } + this.getSmoothingAmount = function(){ + return this._strokeAmountSmoothing; } this.setStrokeUseCalligraphic = function(c){ @@ -225,6 +244,14 @@ var BrushStroke = function GLBrushStroke() { }; } + this.getStrokeUseCalligraphic = function(){ + return this._strokeUseCalligraphic; + } + + this.getStrokeAngle = function(){ + this._strokeAngle = a; + } + this.getStrokeStyle = function () { return this._strokeStyle; }; @@ -301,12 +328,13 @@ var BrushStroke = function GLBrushStroke() { console.log("Inserted "+numInsertedPoints+" additional CatmullRom points"); this._addedSamples = true; this._dirty=true; - } + } //if we need to add samples to this curve (done only once) + //build a copy of the original points...this should be done only once if (this._storedOrigPoints === false) { this._OrigPoints = this._Points.slice(0); this._storedOrigPoints = true; - } + } //if we need to store a copy of the original points (done only once) if (this._dirty) { this._Points = this._OrigPoints.slice(0); @@ -377,7 +405,7 @@ var BrushStroke = function GLBrushStroke() { // get the context var ctx = world.get2DContext(); - if (!ctx) throw ("null context in brushstroke render") + if (!ctx) throw ("null context in brushstroke render"); var numPoints = this.getNumPoints(); if (numPoints === 0) { @@ -391,6 +419,14 @@ var BrushStroke = function GLBrushStroke() { var bboxMax = this.getBBoxMax(); var bboxWidth = bboxMax[0] - bboxMin[0]; var bboxHeight = bboxMax[1] - bboxMin[1]; + + //assign the new width and height as the canvas dimensions through the canvas controller + if (this._canvas) { + CanvasController.setProperty(this._canvas, "width", bboxWidth+"px"); + CanvasController.setProperty(this._canvas, "height", bboxHeight+"px"); + this._canvas.elementModel.shapeModel.GLWorld.setViewportFromCanvas(this._canvas); + } + ctx.clearRect(0, 0, bboxWidth, bboxHeight); if (this._strokeUseCalligraphic) { diff --git a/js/lib/geom/sub-path.js b/js/lib/geom/sub-path.js index 017cca92..e95be0e7 100755 --- a/js/lib/geom/sub-path.js +++ b/js/lib/geom/sub-path.js @@ -6,7 +6,7 @@ No rights, expressed or implied, whatsoever to this software are provided by Mot var VecUtils = require("js/helper-classes/3D/vec-utils").VecUtils; - +var CanvasController = require("js/controllers/elements/canvas-controller").CanvasController; var GeomObj = require("js/lib/geom/geom-obj").GeomObj; var AnchorPoint = require("js/lib/geom/anchor-point").AnchorPoint; var MaterialsModel = require("js/models/materials-model").MaterialsModel; @@ -61,6 +61,9 @@ var GLSubpath = function GLSubpath() { //whether or not to use the canvas drawing to stroke/fill this._useCanvasDrawing = true; + //the canvas that will draw this subpath + this._canvas = null; + //the X and Y location of this subpath's canvas in stage world space of Ninja this._canvasX = 0; this._canvasY = 0; @@ -131,6 +134,11 @@ var GLSubpath = function GLSubpath() { var bboxHeight = bboxMax[1] - bboxMin[1]; var bboxMid = [0.5 * (bboxMax[0] + bboxMin[0]), 0.5 * (bboxMax[1] + bboxMin[1]), 0.5 * (bboxMax[2] + bboxMin[2])]; + if (this._canvas) { + CanvasController.setProperty(this._canvas, "width", bboxWidth+"px"); + CanvasController.setProperty(this._canvas, "height", bboxHeight+"px"); + this._canvas.elementModel.shapeModel.GLWorld.setViewportFromCanvas(this._canvas); + } ctx.clearRect(0, 0, bboxWidth, bboxHeight); ctx.lineWidth = this._strokeWidth; @@ -144,6 +152,7 @@ var GLSubpath = function GLSubpath() { //ctx.fillStyle = MathUtils.colorToHex( this._fillColor ); var fillColorStr = "rgba("+parseInt(255*this._fillColor[0])+","+parseInt(255*this._fillColor[1])+","+parseInt(255*this._fillColor[2])+","+this._fillColor[3]+")"; ctx.fillStyle = fillColorStr; + console.log("Fill color:" + fillColorStr); } var lineCap = ['butt','round','square']; ctx.lineCap = lineCap[1]; @@ -256,6 +265,10 @@ GLSubpath.prototype = new GeomObj(); ///////////////////////////////////////////////////////// // Property Accessors/Setters ///////////////////////////////////////////////////////// +GLSubpath.prototype.setCanvas = function (c) { + this._canvas = c; +}; + GLSubpath.prototype.setWorld = function (world) { this._world = world; }; @@ -773,6 +786,7 @@ GLSubpath.prototype.getStrokeWidth = function () { GLSubpath.prototype.setStrokeWidth = function (w) { this._strokeWidth = w; + this._dirty=true; }; GLSubpath.prototype.getStrokeMaterial = function () { -- cgit v1.2.3 From 8e29172990126b3d3857be5d18bb6985d4cf57ca Mon Sep 17 00:00:00 2001 From: Pushkar Joshi Date: Mon, 19 Mar 2012 10:07:47 -0700 Subject: first pass for file IO of brush strokes --- js/lib/geom/brush-stroke.js | 50 ++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 47 insertions(+), 3 deletions(-) (limited to 'js/lib') diff --git a/js/lib/geom/brush-stroke.js b/js/lib/geom/brush-stroke.js index 02a39ccd..ec2521ab 100755 --- a/js/lib/geom/brush-stroke.js +++ b/js/lib/geom/brush-stroke.js @@ -388,7 +388,8 @@ var BrushStroke = function GLBrushStroke() { this._BBoxMax[d]+= bboxPadding; }//for every dimension d from 0 to 2 } - } + + } //if this was dirty this._dirty = false; }; @@ -512,12 +513,55 @@ var BrushStroke = function GLBrushStroke() { }; //render() + this.exportJSON = function(){ + var retObject= new Object(); + retObject.geomType = this.geomType(); + retObject.points = this._Points; + retObject.planeCenter = this._planeCenter; + retObject.planeMat = this._planeMat; + retObject.planeMatInv = this._planeMatInv; + retObject.strokeWidth = this._strokeWidth; + retObject.strokeColor = this._strokeColor; + retObject.secondStrokeColor = this._secondStrokeColor; + retObject.strokeHardness = this._strokeHardness; + retObject.strokeDoSmoothing = this._strokeDoSmoothing; + retObject.strokeUseCalligraphic = this._strokeUseCalligraphic; + retObject.strokeAngle = this._strokeAngle; + retObject.strokeAmountSmoothing = this._strokeAmountSmoothing; + return retObject; + }; + + this.importJSON = function(jo){ + if (this.geomType()!== jo.geomType){ + return; + } + this._Points = jo.points.splice(0); //todo is this splice necessary? + this._planeCenter = jo.planeCenter; + this._planeMat = jo.planeMat; + this._planeMatInv = jo.planeMatInv ; + this._strokeWidth = jo.strokeWidth; + this._strokeColor = jo.strokeColor; + this._secondStrokeColor = jo.secondStrokeColor; + this._strokeHardness = jo.strokeHardness; + this._strokeDoSmoothing = jo.strokeDoSmoothing; + this._strokeUseCalligraphic = jo.strokeUseCalligraphic; + this._strokeAngle = jo.strokeAngle; + this._strokeAmountSmoothing = jo.strokeAmountSmoothing; + + //force a re-computation of meta-geometry before rendering + this._isDirty = true; + }; + + this.export = function() { - return "type: " + this.geomType() + "\n"; + var jsonObject = this.exportJSON(); + var stringified = JSON.stringify(jsonObject); + return "type: " + this.geomType() + "\n" + stringified; }; this.import = function( importStr ) { - + var jsonObject = JSON.parse(importStr); + this.importJSON(jsonObject); } this.collidesWithPoint = function (x, y, z) { -- cgit v1.2.3 From c0bd8d8efcc501d4efa116a7cc0e802fd4e9f3e9 Mon Sep 17 00:00:00 2001 From: Pushkar Joshi Date: Mon, 19 Mar 2012 11:05:05 -0700 Subject: draw the brush stroke in realtime --- js/lib/geom/brush-stroke.js | 31 ++++++++++++++++--------------- 1 file changed, 16 insertions(+), 15 deletions(-) (limited to 'js/lib') diff --git a/js/lib/geom/brush-stroke.js b/js/lib/geom/brush-stroke.js index ec2521ab..68b9110f 100755 --- a/js/lib/geom/brush-stroke.js +++ b/js/lib/geom/brush-stroke.js @@ -249,7 +249,7 @@ var BrushStroke = function GLBrushStroke() { } this.getStrokeAngle = function(){ - this._strokeAngle = a; + return this._strokeAngle; } this.getStrokeStyle = function () { @@ -429,7 +429,12 @@ var BrushStroke = function GLBrushStroke() { } ctx.clearRect(0, 0, bboxWidth, bboxHeight); + this.drawToContext(ctx, bboxMin[0], bboxMin[1]); + ctx.restore(); + } //this.render() + this.drawToContext = function(ctx, origX, origY){ + var numPoints = this.getNumPoints(); if (this._strokeUseCalligraphic) { //build the stamp for the brush stroke var t=0; @@ -453,13 +458,11 @@ var BrushStroke = function GLBrushStroke() { ctx.lineCap="butt"; ctx.globalCompositeOperation = 'source-over'; ctx.globalAlpha = this._strokeColor[3]; - //ctx.lineWidth=this._strokeWidth/10;//todo figure out the correct formula for the line width - //if (ctx.lineWidth<2) + //todo figure out the correct formula for the line width ctx.lineWidth=2; if (t===numTraces-1){ ctx.lineWidth = 1; } - for (t=0;t --- js/lib/drawing/world.js | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) (limited to 'js/lib') diff --git a/js/lib/drawing/world.js b/js/lib/drawing/world.js index 049145ce..c1ee0cd0 100755 --- a/js/lib/drawing/world.js +++ b/js/lib/drawing/world.js @@ -810,14 +810,17 @@ World.prototype.findTransformNodeByMaterial = function( materialNode, trNode ) return rtnNode; }; -World.prototype.import = function( importStr ) { +World.prototype.import = function( importStr, fromToggle ) { // import the worldattributes - not currently used // determine if the data was written for export (no Ninja objects) // or for save/restore //var index = importStr.indexOf( "scenedata: " ); - var index = importStr.indexOf( "webGL: " ); - this._useWebGL = (index >= 0) + // Skip if we are toggling between canvas2d and WebGL since importStr doesn't have the correct webGL value yet + if(!fromToggle) { + var index = importStr.indexOf( "webGL: " ); + this._useWebGL = (index >= 0) + } if (this._useWebGL) { // start RDGE -- cgit v1.2.3 From 52fac825174d16e3ff6875fc497d3f3cfaf4812c Mon Sep 17 00:00:00 2001 From: Nivesh Rajbhandari Date: Wed, 21 Mar 2012 12:02:36 -0700 Subject: Get matrix and perspective distance from styles controller. Signed-off-by: Nivesh Rajbhandari --- js/lib/NJUtils.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'js/lib') diff --git a/js/lib/NJUtils.js b/js/lib/NJUtils.js index 4f1082f9..f611052b 100755 --- a/js/lib/NJUtils.js +++ b/js/lib/NJUtils.js @@ -94,7 +94,7 @@ exports.NJUtils = Object.create(Object.prototype, { ///// TODO: find a different place for this function makeElementModel: { value: function(el, selection, controller, isShape) { - var p3d = Montage.create(Properties3D).init(el); + var p3d = Montage.create(Properties3D).init(el, (selection === "Stage")); var shapeProps = null; if(isShape) { shapeProps = Montage.create(ShapeModel); -- cgit v1.2.3 From 3987b73569e58843f2a91c0c6c4e4132f51ac247 Mon Sep 17 00:00:00 2001 From: Nivesh Rajbhandari Date: Wed, 21 Mar 2012 17:13:56 -0700 Subject: Do not set webkit-transform styles on elements unless 3d is used on them. Signed-off-by: Nivesh Rajbhandari --- js/lib/NJUtils.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'js/lib') diff --git a/js/lib/NJUtils.js b/js/lib/NJUtils.js index f611052b..cb877591 100755 --- a/js/lib/NJUtils.js +++ b/js/lib/NJUtils.js @@ -94,7 +94,10 @@ exports.NJUtils = Object.create(Object.prototype, { ///// TODO: find a different place for this function makeElementModel: { value: function(el, selection, controller, isShape) { - var p3d = Montage.create(Properties3D).init(el, (selection === "Stage")); + var p3d = Montage.create(Properties3D); + if(selection === "Stage") { + p3d.init(el, true); + } var shapeProps = null; if(isShape) { shapeProps = Montage.create(ShapeModel); -- cgit v1.2.3 From 9b7dac9215fbd7c0fe7a80d3e8f1ff378332fec3 Mon Sep 17 00:00:00 2001 From: Pushkar Joshi Date: Fri, 23 Mar 2012 14:32:46 -0700 Subject: Almost working version of brush tool that uses only local coordinates to store the brush stroke points. Current version does not yet update the width and height of the brush stroke canvas upon changing the brush stroke through the PI. Also, current version does not obtain 3D position of points properly from the drawing tool base (see BrushTool _getUnsnappedPosition) --- js/lib/geom/brush-stroke.js | 460 ++++++++++++++++++++++++++++++-------------- js/lib/geom/sub-path.js | 2 +- 2 files changed, 315 insertions(+), 147 deletions(-) (limited to 'js/lib') diff --git a/js/lib/geom/brush-stroke.js b/js/lib/geom/brush-stroke.js index afeaf6e6..a3a5ed9a 100755 --- a/js/lib/geom/brush-stroke.js +++ b/js/lib/geom/brush-stroke.js @@ -7,6 +7,7 @@ No rights, expressed or implied, whatsoever to this software are provided by Mot var VecUtils = require("js/helper-classes/3D/vec-utils").VecUtils; var GeomObj = require("js/lib/geom/geom-obj").GeomObj; var CanvasController = require("js/controllers/elements/canvas-controller").CanvasController; +var ViewUtils = require("js/helper-classes/3D/view-utils").ViewUtils; // Todo: This entire class should be converted to a module @@ -19,14 +20,15 @@ var BrushStroke = function GLBrushStroke() { /////////////////////////////////////////////////// // Instance variables /////////////////////////////////////////////////// - this._Points = []; - this._OrigPoints = []; + this._Points = []; //current state of points in stage-world space (may be different from input) + this._OrigPoints = []; //copy of input points without any smoothing + this._LocalPoints = []; //_Points in local coordinates...do this before rendering the points in the canvas + this._stageWorldCenter = [0,0,0]; //coordinate for the canvas midPoint: a 3D vector in stage world space this._BBoxMin = [0, 0, 0]; this._BBoxMax = [0, 0, 0]; - this._dirty = true; + this._isDirty = true; this._isInit = false; - this._addedSamples = false; - this._storedOrigPoints = false; + //whether or not to use the canvas drawing to stroke/fill this._useCanvasDrawing = true; @@ -57,7 +59,7 @@ var BrushStroke = function GLBrushStroke() { this._MIN_SAMPLE_DISTANCE_THRESHOLD = 2; //prevent extremely long paths that can take a long time to render - this._MAX_ALLOWED_SAMPLES = 500; + this._MAX_ALLOWED_SAMPLES = 5000; //drawing context this._world = null; @@ -67,6 +69,7 @@ var BrushStroke = function GLBrushStroke() { this._planeMat = null; this._planeMatInv = null; this._planeCenter = null; + this._dragPlane = null; ///////////////////////////////////////////////////////// // Property Accessors/Setters @@ -107,6 +110,10 @@ var BrushStroke = function GLBrushStroke() { this._planeCenter = pc; }; + this.setDragPlane = function(p){ + this._dragPlane = p; + }; + this.getCanvasX = function(){ return this._canvasX; }; @@ -141,25 +148,32 @@ var BrushStroke = function GLBrushStroke() { var diffPtMag = Math.sqrt(diffPt[0]*diffPt[0] + diffPt[1]*diffPt[1]); if (diffPtMag>threshold){ this._Points.push(pt); - this._dirty=true; + this._isDirty=true; + this._isInit = false; } } else { this._Points.push(pt); - this._dirty=true; + this._isDirty=true; + this._isInit = false; } }; this.insertPoint = function(pt, index){ this._Points.splice(index, 0, pt); - this._dirty=true; + this._isDirty=true; + this._isInit = false; }; this.isDirty = function(){ - return this._dirty; + return this._isDirty; }; this.makeDirty = function(){ - this._dirty=true; + this._isDirty=true; + }; + + this.getStageWorldCenter = function() { + return this._stageWorldCenter; }; this.getBBoxMin = function () { @@ -176,7 +190,7 @@ var BrushStroke = function GLBrushStroke() { this.setStrokeWidth = function (w) { this._strokeWidth = w; - this._dirty=true; + this._isDirty=true; }; this.getStrokeMaterial = function () { @@ -184,7 +198,7 @@ var BrushStroke = function GLBrushStroke() { }; this.setStrokeMaterial = function (m) { - this._strokeMaterial = m; this._dirty = true; + this._strokeMaterial = m; this._isDirty = true; }; this.getStrokeColor = function () { @@ -192,17 +206,17 @@ var BrushStroke = function GLBrushStroke() { }; this.setStrokeColor = function (c) { - this._strokeColor = c; this._dirty = true; + this._strokeColor = c; this._isDirty = true; }; this.setSecondStrokeColor = function(c){ - this._secondStrokeColor=c; this._dirty = true; + this._secondStrokeColor=c; this._isDirty = true; } this.setStrokeHardness = function(h){ if (this._strokeHardness!==h){ this._strokeHardness=h; - this._dirty = true; + this._isDirty = true; } } this.getStrokeHardness = function(){ @@ -212,7 +226,7 @@ var BrushStroke = function GLBrushStroke() { this.setDoSmoothing = function(s){ if (this._strokeDoSmoothing!==s) { this._strokeDoSmoothing = s; - this._dirty = true; + this._isDirty = true; } } @@ -223,7 +237,7 @@ var BrushStroke = function GLBrushStroke() { this.setSmoothingAmount = function(a){ if (this._strokeAmountSmoothing!==a) { this._strokeAmountSmoothing = a; - this._dirty = true; + this._isDirty = true; } } @@ -234,14 +248,14 @@ var BrushStroke = function GLBrushStroke() { this.setStrokeUseCalligraphic = function(c){ if (this._strokeUseCalligraphic!==c){ this._strokeUseCalligraphic = c; - this._dirty = true; + this._isDirty = true; } } this.setStrokeAngle = function(a){ if (this._strokeAngle!==a){ this._strokeAngle = a; - this._dirty = true; + this._isDirty = true; }; } @@ -269,127 +283,276 @@ var BrushStroke = function GLBrushStroke() { };//NO-OP for now + this.getWidth = function() { + if (this._isDirty){ + this.update(); + } + return this._BBoxMax[0]-this._BBoxMin[0]; + }; + + this.getHeight = function() { + if (this._isDirty){ + this.update(); + } + return this._BBoxMax[1]-this._BBoxMin[1]; + }; + //remove all the points this.clear = function () { this._Points = []; this._OrigPoints = []; - this._dirty=true; - } + this._isDirty=true; + this._isInit = false; + }; + + this._addSamples = function() { + //**** add samples to the long sections of the path --- Catmull-Rom spline interpolation ***** + // instead of the following, may use 4-point subdivision iterations over continuous regions of 'long' segments + // look at http://www.gvu.gatech.edu/~jarek/Split&Tweak/ for formula + + var numPoints = this._Points.length; + var numInsertedPoints = 0; + var newSampledPoints = []; + var threshold = this._MAX_SAMPLE_DISTANCE_THRESHOLD;//this determines whether a segment between two sample too long + var prevPt = this._Points[0]; + newSampledPoints.push(this._Points[0]); + for (var i=1;ithreshold){ + //build the control polygon for the Catmull-Rom spline (prev. 2 points and next 2 points) + var prev = (i===1) ? i-1 : i-2; + var next = (i===numPoints-1) ? i : i+1; + var ctrlPts = [this._Points[prev], this._Points[i-1], this._Points[i], this._Points[next]]; + //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; + } + } + this._Points = newSampledPoints.slice(0); + newSampledPoints = []; + }; + + this.init = function(){ + if (!this._isInit){ + // **** add samples to the _Points in stageworld space **** + this._addSamples(); + + // **** compute the 2D (canvas space) coord. of the _Points **** + this._buildLocalCoordFromStageWorldCoord(); + + // **** turn off the init. flag **** + this._isInit = true; + this._isDirty= true; + } + + // **** update the current brush stroke **** + // smoothing, re-compute bounding box, etc. + this.update(); + }; + + this._unprojectPt = function(pt, pespectiveDist){ + var retPt = pt.slice(0); + if (MathUtils.fpCmp(pespectiveDist,-pt[2]) !== 0){ + z = pt[2]*pespectiveDist/(pespectiveDist + pt[2]); + var x = pt[0]*(pespectiveDist - z)/pespectiveDist, + y = pt[1]*(pespectiveDist - z)/pespectiveDist; + retPt[0] = x; retPt[1] = y; retPt[2] = z; + } + return retPt; + }; + + this._buildLocalCoordFromStageWorldCoord = function() { + var stage = ViewUtils.getStage(); + var stageOffset = ViewUtils.getElementOffset(stage); + ViewUtils.setViewportObj(stage); + + var numPoints = this._Points.length; + var i; + + // ***** compute center of bbox based on stage world coords ***** + var bboxMin = [Infinity, Infinity, Infinity]; + var bboxMax = [-Infinity, -Infinity, -Infinity]; + for (i=0;i pt[d]) { + bboxMin[d] = pt[d]; + } + if (bboxMax[d] < pt[d]) { + bboxMax[d] = pt[d]; + } + } + } + //save the center of the bbox for later use (while constructing the canvas) + this._stageWorldCenter = VecUtils.vecInterpolate(3, bboxMin, bboxMax, 0.5); + + // ***** center the input stageworld data about the center of the bbox ***** + this._LocalPoints = this._Points.slice(0); + for (i=0;ithreshold){ - //build the control polygon for the Catmull-Rom spline (prev. 2 points and next 2 points) - var prev = (i===1) ? i-1 : i-2; - var next = (i===numPoints-1) ? i : i+1; - var ctrlPts = [this._Points[prev], this._Points[i-1], this._Points[i], this._Points[next]]; - //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; - //} + //build coordinates for the brush stroke in local space of the canvas...will assume that the canvas width, height, left and top will not be changed now + this.buildLocalCoord = function(){ + /* + if (this._isLocalDirty) { + //DEBUGGING + //confirm that localToStageWorld produces the same coordinates as those I used for rendering currently + var numPoints = this._Points.length; + var objToStageWorldMat = ViewUtils.getObjToStageWorldMatrix(this._canvas, true); + var stageworldToObjMat = glmat4.inverse(objToStageWorldMat, []); + var wh = ViewUtils.getStageDimension(); + for (var i=0;i1) { - //iterations of Laplacian smoothing (setting the points to the average of their neighbors) - var numLaplacianIterations = this._strokeAmountSmoothing; - for (var n=0;n 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 + + //end DEBUGGING + this._isLocalDirty=false; + } //if this._isLocalDirty + */ + }; + + + + this.update = function() { + if (this._isDirty){ + // **** do smoothing if necessary **** + this._doSmoothing(); + + // **** recompute the bounding box **** + this._updateBoundingBox(); + + // **** turn off the dirty flag **** + this._isDirty = false; + } + }; + + this._doSmoothing = function() { + var numPoints = this._LocalPoints.length; + if (this._strokeDoSmoothing && numPoints>1) { + this._LocalPoints = this._OrigPoints.slice(0); + //iterations of Laplacian smoothing (setting the points to the average of their neighbors) + var numLaplacianIterations = this._strokeAmountSmoothing; + for (var n=0;n 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 } - } //if this was dirty - this._dirty = false; + } + //increase the bbox given the stroke width and the angle (in case of ca