diff options
Diffstat (limited to 'js/helper-classes/RDGE')
-rwxr-xr-x | js/helper-classes/RDGE/GLBrushStroke.js | 175 |
1 files changed, 163 insertions, 12 deletions
diff --git a/js/helper-classes/RDGE/GLBrushStroke.js b/js/helper-classes/RDGE/GLBrushStroke.js index fdf1595c..5d773c2d 100755 --- a/js/helper-classes/RDGE/GLBrushStroke.js +++ b/js/helper-classes/RDGE/GLBrushStroke.js | |||
@@ -34,6 +34,13 @@ function GLBrushStroke() { | |||
34 | this._strokeMaterial; | 34 | this._strokeMaterial; |
35 | this._strokeStyle = "Solid"; | 35 | this._strokeStyle = "Solid"; |
36 | 36 | ||
37 | //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 | ||
38 | //smaller value means more samples for the path | ||
39 | this._WETNESS_FACTOR = 0.25; | ||
40 | |||
41 | //prevent extremely long paths that can take a long time to render | ||
42 | this._MAX_ALLOWED_SAMPLES = 500; | ||
43 | |||
37 | //drawing context | 44 | //drawing context |
38 | this._world = null; | 45 | this._world = null; |
39 | 46 | ||
@@ -67,7 +74,25 @@ function GLBrushStroke() { | |||
67 | 74 | ||
68 | this.getNumPoints = function () { return this._Points.length; } | 75 | this.getNumPoints = function () { return this._Points.length; } |
69 | this.getPoint = function (index) { return this._Points[index]; } | 76 | this.getPoint = function (index) { return this._Points[index]; } |
70 | this.addPoint = function (anchorPt) { this._Points.push(anchorPt); this._dirty=true; } | 77 | this.addPoint = function (pt) |
78 | { | ||
79 | //add the point only if it is some epsilon away from the previous point | ||
80 | var numPoints = this._Points.length; | ||
81 | if (numPoints>0) { | ||
82 | var threshold = this._WETNESS_FACTOR*this._strokeWidth; | ||
83 | var prevPt = this._Points[numPoints-1]; | ||
84 | var diffPt = [prevPt[0]-pt[0], prevPt[1]-pt[1]]; | ||
85 | var diffPtMag = Math.sqrt(diffPt[0]*diffPt[0] + diffPt[1]*diffPt[1]); | ||
86 | if (diffPtMag>threshold){ | ||
87 | this._Points.push(pt); | ||
88 | this._dirty=true; | ||
89 | } | ||
90 | }else{ | ||
91 | this._Points.push(pt); | ||
92 | this._dirty=true; | ||
93 | } | ||
94 | } | ||
95 | |||
71 | this.insertPoint = function(pt, index){ this._Points.splice(index, 0, pt); this._dirty=true;} | 96 | this.insertPoint = function(pt, index){ this._Points.splice(index, 0, pt); this._dirty=true;} |
72 | this.isDirty = function(){return this._dirty;} | 97 | this.isDirty = function(){return this._dirty;} |
73 | this.makeDirty = function(){this._dirty=true;} | 98 | this.makeDirty = function(){this._dirty=true;} |
@@ -111,10 +136,45 @@ function GLBrushStroke() { | |||
111 | 136 | ||
112 | this.computeMetaGeometry = function(){ | 137 | this.computeMetaGeometry = function(){ |
113 | if (this._dirty){ | 138 | if (this._dirty){ |
139 | var numPoints = this._Points.length; | ||
140 | |||
141 | //**** add samples to the path if needed...linear interpolation for now | ||
142 | if (numPoints>1) { | ||
143 | var threshold = this._WETNESS_FACTOR*this._strokeWidth; | ||
144 | var prevPt = this._Points[0]; | ||
145 | var prevIndex = 0; | ||
146 | for (var i=1;i<numPoints;i++){ | ||
147 | var pt = this._Points[i]; | ||
148 | var diff = [pt[0]-prevPt[0], pt[1]-prevPt[1]]; | ||
149 | var distance = Math.sqrt(diff[0]*diff[0]+diff[1]*diff[1]); | ||
150 | if (distance>threshold){ | ||
151 | //insert points along the prev. to current point | ||
152 | var numNewPoints = Math.floor(distance/threshold); | ||
153 | for (var j=0;j<numNewPoints;j++){ | ||
154 | var param = (j+1)/(numNewPoints+1); | ||
155 | var newpt = [prevPt[0]+ diff[0]*param, prevPt[1]+ diff[1]*param]; | ||
156 | //insert new point before point i | ||
157 | this._Points.splice(i, 0, [newpt[0], newpt[1], 0]); | ||
158 | i++; | ||
159 | } | ||
160 | this._dirty=true; | ||
161 | } | ||
162 | prevPt=pt; | ||
163 | //update numPoints to match the new length | ||
164 | numPoints = this._Points.length; | ||
165 | |||
166 | //end this function if the numPoints has gone above the max. size specified | ||
167 | if (numPoints> this._MAX_ALLOWED_SAMPLES){ | ||
168 | console.log("leaving the resampling because numPoints is greater than limit:"+this._MAX_ALLOWED_SAMPLES); | ||
169 | break; | ||
170 | } | ||
171 | } | ||
172 | } | ||
173 | |||
114 | // *** compute the bounding box ********* | 174 | // *** compute the bounding box ********* |
115 | this._BBoxMin = [Infinity, Infinity, Infinity]; | 175 | this._BBoxMin = [Infinity, Infinity, Infinity]; |
116 | this._BBoxMax = [-Infinity, -Infinity, -Infinity]; | 176 | this._BBoxMax = [-Infinity, -Infinity, -Infinity]; |
117 | var numPoints = this._Points.length; | 177 | numPoints = this._Points.length; |
118 | if (numPoints === 0) { | 178 | if (numPoints === 0) { |
119 | this._BBoxMin = [0, 0, 0]; | 179 | this._BBoxMin = [0, 0, 0]; |
120 | this._BBoxMax = [0, 0, 0]; | 180 | this._BBoxMax = [0, 0, 0]; |
@@ -167,7 +227,8 @@ function GLBrushStroke() { | |||
167 | var bboxWidth = bboxMax[0] - bboxMin[0]; | 227 | var bboxWidth = bboxMax[0] - bboxMin[0]; |
168 | var bboxHeight = bboxMax[1] - bboxMin[1]; | 228 | var bboxHeight = bboxMax[1] - bboxMin[1]; |
169 | ctx.clearRect(0, 0, bboxWidth, bboxHeight); | 229 | ctx.clearRect(0, 0, bboxWidth, bboxHeight); |
170 | /* | 230 | |
231 | /* | ||
171 | ctx.lineWidth = this._strokeWidth; | 232 | ctx.lineWidth = this._strokeWidth; |
172 | ctx.strokeStyle = "black"; | 233 | ctx.strokeStyle = "black"; |
173 | if (this._strokeColor) | 234 | if (this._strokeColor) |
@@ -185,30 +246,120 @@ function GLBrushStroke() { | |||
185 | ctx.lineTo(pt[0]-bboxMin[0], pt[1]-bboxMin[1]); | 246 | ctx.lineTo(pt[0]-bboxMin[0], pt[1]-bboxMin[1]); |
186 | } | 247 | } |
187 | ctx.stroke(); | 248 | ctx.stroke(); |
188 | */ | 249 | */ |
250 | |||
251 | /* | ||
252 | var isDebug = false; | ||
253 | var prevPt = this._Points[0]; | ||
254 | var prevX = prevPt[0]-bboxMin[0]; | ||
255 | var prevY = prevPt[1]-bboxMin[1]; | ||
256 | prevPt = [prevX,prevY]; | ||
257 | for (var i = 1; i < numPoints; i++) { | ||
258 | var pt = this._Points[i]; | ||
259 | ctx.globalCompositeOperation = 'source-over'; | ||
260 | var x = pt[0]-bboxMin[0]; | ||
261 | var y = pt[1]-bboxMin[1]; | ||
262 | pt = [x,y]; | ||
263 | |||
264 | //vector from prev to current pt | ||
265 | var seg = VecUtils.vecSubtract(2, pt, prevPt); | ||
266 | var segDir = VecUtils.vecNormalize(2, seg, 1.0); | ||
267 | |||
268 | var segMidPt = VecUtils.vecInterpolate(2, pt, prevPt, 0.5); | ||
269 | var w2 = this._strokeWidth*0.5; | ||
270 | var segDirOrtho = [w2*segDir[1], -w2*segDir[0]]; | ||
271 | |||
272 | //add half the strokewidth to the segMidPt | ||
273 | var lgStart = VecUtils.vecAdd(2, segMidPt, segDirOrtho); | ||
274 | var lgEnd = VecUtils.vecSubtract(2, segMidPt, segDirOrtho); | ||
275 | |||
276 | ctx.save(); | ||
277 | ctx.beginPath(); | ||
278 | |||
279 | if (isDebug) { | ||
280 | ctx.strokeStyle="black"; | ||
281 | ctx.lineWidth = 1; | ||
282 | |||
283 | ctx.moveTo(lgStart[0], lgStart[1]); | ||
284 | ctx.lineTo(lgEnd[0], lgEnd[1]); | ||
285 | ctx.stroke(); | ||
286 | } | ||
287 | |||
288 | var lg = ctx.createLinearGradient(lgStart[0], lgStart[1], lgEnd[0], lgEnd[1]); | ||
289 | lg.addColorStop(1, 'rgba(0,0,0,0.0)'); | ||
290 | lg.addColorStop(0.5,'rgba(255,0,0,1.0)'); | ||
291 | lg.addColorStop(0, 'rgba(0,0,0,0.0)'); | ||
292 | ctx.fillStyle = lg; | ||
293 | |||
294 | if (isDebug){ | ||
295 | ctx.strokeStyle="blue"; | ||
296 | ctx.lineWidth=0.5; | ||
297 | } | ||
298 | ctx.moveTo(prevX-w2, prevY); | ||
299 | ctx.lineTo(prevX+w2, prevY); | ||
300 | ctx.lineTo(x+w2, y); | ||
301 | ctx.lineTo(x-w2, y); | ||
302 | ctx.lineTo(prevX-w2, prevY); | ||
303 | ctx.fill(); | ||
304 | ctx.closePath(); | ||
305 | |||
306 | ctx.restore(); | ||
307 | |||
308 | prevPt = pt; | ||
309 | prevX = x; | ||
310 | prevY = y; | ||
311 | } | ||
312 | |||
313 | |||
314 | if (isDebug) | ||
315 | ctx.stroke(); | ||
316 | |||
317 | if (isDebug){ | ||
318 | //draw the skeleton of this stroke | ||
319 | ctx.lineWidth = 1; | ||
320 | ctx.strokeStyle = "black"; | ||
321 | var pt = this._Points[0]; | ||
322 | ctx.beginPath(); | ||
323 | ctx.moveTo(pt[0]-bboxMin[0],pt[1]-bboxMin[1]); | ||
324 | for (var i = 1; i < numPoints; i++) { | ||
325 | pt = this._Points[i]; | ||
326 | var x = pt[0]-bboxMin[0]; | ||
327 | var y = pt[1]-bboxMin[1]; | ||
328 | ctx.lineTo(x,y); | ||
329 | } | ||
330 | ctx.stroke(); | ||
331 | } | ||
332 | */ | ||
333 | |||
334 | |||
189 | var R2 = this._strokeWidth; | 335 | var R2 = this._strokeWidth; |
190 | var R = R2*0.5; | 336 | var R = R2*0.5; |
191 | var hardness = 0.25; //for a pencil, this is always 1 //TODO get hardness parameter from user interface | 337 | var hardness = 0; //for a pencil, this is always 1 //TODO get hardness parameter from user interface |
192 | var innerRadius = (hardness*R)-1; | 338 | var innerRadius = (hardness*R)-1; |
193 | if (innerRadius<1) | 339 | if (innerRadius<1) |
194 | innerRadius=1; | 340 | innerRadius=1; |
195 | 341 | ||
342 | var r = ctx.createRadialGradient(0,0,innerRadius, 0,0,R); | ||
343 | var midColor = "rgba("+parseInt(255*this._strokeColor[0])+","+parseInt(255*this._strokeColor[1])+","+parseInt(255*this._strokeColor[2])+",1)"; | ||
344 | r.addColorStop(0, midColor); | ||
345 | var endColor = "rgba("+parseInt(255*this._strokeColor[0])+","+parseInt(255*this._strokeColor[1])+","+parseInt(255*this._strokeColor[2])+",0.0)"; | ||
346 | r.addColorStop(1, endColor); | ||
347 | ctx.fillStyle = r; | ||
348 | |||
196 | for (var i = 0; i < numPoints; i++) { | 349 | for (var i = 0; i < numPoints; i++) { |
197 | var pt = this._Points[i]; | 350 | var pt = this._Points[i]; |
198 | ctx.globalCompositeOperation = 'source-over'; | 351 | ctx.globalCompositeOp |