1 2 3 /** 4 Polynomial interpolation demo 5 @authors - Aaron R Elligsen, and Sahil Diwan 6 UNIVERSITY OF OREGON 7 MATH 351 - FALL 2012 8 **/ 9 "use strict"; 10 11 /** 12 A class representing a 2-axis graph 13 @class 14 @constructor 15 @param {double} xlow/xhigh The high and low values of the x-range. 16 @param {double} ylow/yhigh The high and low values of the y-range. 17 @param {double} points The array of points added. 18 @param {double} functions The array of functions added/formed. 19 **/ 20 function graph(xlow,xhigh, ylow,yhigh,counter,points,functions) { 21 this.xlow = xlow; 22 this.xhigh = xhigh; 23 this.ylow = ylow; 24 this.yhigh = yhigh; 25 this.counter = counter; 26 //var points = []; 27 this.points =points; 28 //var functions = []; 29 this.functions = functions; 30 var lsq = function(points) { 31 var linear = [new Term(1,0,'x'),new Term(1,1,'x')]; 32 return Polynomial.prototype.leastSquare(points,linear); 33 } 34 35 var qsq = function(points) { 36 var quadratic =[new Term(1,0,'x'),new Term(1,1,'x'),new Term(1,2,'x')]; 37 return Polynomial.prototype.leastSquare(points,quadratic); 38 } 39 this.datamethods = [Polynomial.prototype.LagrangeInterpolation,PiecewiseFunction.prototype.createFirstDegSpline,PiecewiseFunction.prototype.createSecondDegSpline,PiecewiseFunction.prototype.createThirdDegSpline,lsq,qsq] 40 //Todo: redo this selection system, less hardcoding 41 this.currentinterpolator = 0; 42 this.changeInterpolation = function(value) { 43 if(value == "poly") { 44 this.currentinterpolator = 0; 45 }else if(value == "spline1") { 46 this.currentinterpolator = 1; 47 }else if(value == "spline2") { 48 this.currentinterpolator = 2; 49 }else if(value == "spline3") { 50 this.currentinterpolator = 3; 51 }else if(value == "lslinear") { 52 this.currentinterpolator = 4; 53 }else if(value == "lsquadratic") { 54 this.currentinterpolator = 5; 55 } 56 57 58 59 } 60 this.workers = [new Worker("sidecalc.js"),new Worker("sidecalc.js"),new Worker("sidecalc.js"),new Worker("sidecalc.js")]; 61 //this.workers = []; 62 for(var i=0; i < this.workers.length; i++) 63 { 64 this.workers[i].onmessage = function (e) { 65 ourGraph.plotAndConnectPoints(e.data); 66 ourGraph.drawPoints(); 67 }; 68 } 69 } 70 71 /** 72 Read and parse input of x-range and updates the x-range. 73 @param {string} rangestring The values for xhigh and xlow as a string 74 **/ 75 graph.prototype.changeX = function(rangestring) { 76 var pointRe = /([-]*\d*(\.\d*)*),([-]*\d*(\.\d*)*)/; 77 var pointValues = pointRe.exec(rangestring); 78 this.xlow =parseFloat( pointValues[1]); 79 this.xhigh =parseFloat( pointValues[3]); 80 this.redraw(); 81 82 } 83 84 /** 85 Read and parse input of y-range and updates the y-range. 86 @param {string} rangestring The values for yhigh and ylow as a string 87 **/ 88 graph.prototype.changeY = function (rangestring) { 89 var pointRe = /([-]*\d*(\.\d*)*),([-]*\d*(\.\d*)*)/; 90 var pointValues = pointRe.exec(rangestring); 91 this.ylow =parseFloat( pointValues[1]); 92 this.yhigh =parseFloat( pointValues[3]); 93 this.redraw(); 94 } 95 96 /** 97 Calulates the X pixels per unit ratio 98 @return {double} xpu Returns the xpu value 99 **/ 100 graph.prototype.xpu = function() { 101 102 return canvas.width/(this.xhigh - this.xlow); 103 } 104 105 106 /** 107 Calulates the Y pixels per unit ratio 108 @return {double} ypu Returns the ypu value 109 **/ 110 graph.prototype.ypu = function() { 111 112 return canvas.height/(this.yhigh - this.ylow); 113 } 114 115 /** 116 Adds a point to the array of points to be interpolated. 117 It also checks if the point is outside the current viewing window and 118 adjusts if necessary. 119 @param {Point} newpoint The newpoint to be drawn 120 **/ 121 graph.prototype.addPoint = function(newpoint) 122 { 123 var previouspoint =this.points[this.points.length-1]; 124 if(previouspoint != undefined) { 125 if(newpoint.x == previouspoint.x && newpoint.y == previouspoint.y) 126 return; 127 } 128 //This ^ handles a common failure case of the graph object and double clicking. 129 this.points.push(newpoint); 130 var redrawrequired = false; 131 if(newpoint.x >= this.xhigh) 132 { 133 this.xhigh = newpoint.x + (newpoint.x-this.xlow)/4; 134 redrawrequired = true; 135 } 136 137 if(newpoint.x <= this.xlow) 138 { 139 this.xlow = newpoint.x - (this.xhigh-newpoint.x)/4; 140 redrawrequired = true; 141 } 142 143 if(newpoint.y >= this.yhigh) 144 { 145 this.yhigh = newpoint.y +(newpoint.y-this.ylow)/4; 146 redrawrequired = true; 147 } 148 if(newpoint.y <= this.ylow) 149 { 150 this.ylow = newpoint.y -(this.yhigh-newpoint.y)/4; 151 redrawrequired = true; 152 } 153 $("#x_range").val(this.xlow+","+this.xhigh); 154 $("#y_range").val(this.ylow+","+this.yhigh); 155 $("#s_points").append('<option>'+newpoint+'</option>'); 156 if(this.points.length >1) 157 { 158 var newpoly = this.datamethods[this.currentinterpolator](this.points); 159 this.functions.push(newpoly); 160 $("#s_functionlist").append('<option>'+newpoly+'</option>'); 161 } 162 if(redrawrequired) { 163 this.redraw(); 164 }else{ 165 this.drawLatest(); 166 } 167 168 169 } 170 171 /** 172 Draws all points currently in array onto graph 173 **/ 174 graph.prototype.drawPoints = function() 175 { 176 var c = canvas.getContext('2d'); 177 c.fillStyle = "red"; 178 var xpu = this.xpu(); 179 var ypu = this.ypu(); 180 181 for (var i = 0; i < this.points.length; i++) { 182 var pixelx =(this.points[i].x-this.xlow) * xpu ; 183 var pixely =canvas.height -(this.points[i].y-this.ylow)* ypu; 184 c.fillRect(pixelx-2,pixely-2,5,5); 185 } 186 } 187 188 /** 189 Draws the axes of the graph along with the gridlines within 190 **/ 191 graph.prototype.drawAxes = function() 192 { 193 var c = canvas.getContext('2d'); 194 var xsituation =0; 195 var xpu = this.xpu(); 196 var ypu = this.ypu(); 197 198 // draws vertical lines on grid 199 c.strokeStyle = "#ccc"; 200 c.beginPath(); 201 c.lineWidth =1; 202 var xdivision = xpu; 203 while((canvas.width/xdivision) > 32) 204 { 205 xdivision *=2; 206 } 207 for(var x = 0; x < canvas.width; x += xdivision) { 208 var x2 = this.xlow+x/xpu; 209 if(x2.toString().length >5 ) 210 { 211 x2 = x2.toFixed(1); 212 } 213 214 c.moveTo(x, 0); 215 c.lineTo(x, canvas.height); 216 c.stroke(); 217 c.fillStyle = "black"; 218 c.font = "bold 10pt Times"; 219 c.fillText(x2, x+2, canvas.height - 5); 220 } 221 222 223 // draws horizontal lines on grid 224 var ydivision = ypu; 225 while((canvas.width/ydivision) > 32) 226 { 227 ydivision *=2; 228 } 229 c.strokeStyle = "#ccc"; 230 c.beginPath(); 231 for(var y = 0; y < canvas.height; y += ydivision) { 232 var y2 = this.ylow+(canvas.height-y)/ypu; 233 if(y2.toString().length > 5) 234 { 235 y2 = y2.toFixed(1); 236 } 237 238 c.moveTo(0, y); 239 c.lineTo(canvas.width, y); 240 c.stroke(); 241 c.fillStyle = "black"; 242 c.font = "bold 10pt Times"; 243 c.fillText(y2, 5, y-3); 244 } 245 c.strokeStyle="black"; 246 c.beginPath(); 247 c.lineWidth =5; 248 c.moveTo(canvas.width,canvas.height); 249 c.lineTo(0,canvas.height); 250 c.stroke(); 251 c.moveTo(0,0); 252 c.lineTo(0,canvas.height); 253 c.stroke(); 254 } 255 256 /** 257 Draws the backdrop of the graph 258 **/ 259 graph.prototype.redraw = function() 260 { 261 var c = canvas.getContext('2d'); 262 c.fillStyle= "white"; 263 c.fillRect(0,0,canvas.width,canvas.height); 264 this.counter =0; 265 this.drawAxes(); 266 var workersupport = true; 267 if(typeof(this.workers[0]) === "undefined") { 268 workersupport = false; 269 } 270 if(this.functions.length >0) 271 { 272 for(var i=0; i <this.functions.length; i++) 273 { 274 if(workersupport) { 275 this.workers[i%this.workers.length].postMessage({ 276 "cmd":"calc", 277 "startx":this.xlow, 278 "ylow":this.ylow, 279 "yhigh":this.yhigh, 280 "range":this.xhigh-this.xlow, 281 "fn":this.functions[i].toWebWorker() 282 }); 283 }else{ 284 this.drawFunction(this.functions[i]); 285 } 286 } 287 } 288 this.drawPoints(); 289 290 291 } 292 293 /** 294 Draw the latest function added 295 **/ 296 graph.prototype.drawLatest = function() 297 { 298 var c = canvas.getContext('2d'); 299 if(this.functions.length >0) 300 { 301 302 this.drawFunction(this.functions[this.functions.length-1]); 303 } 304 this.drawPoints(); 305 306 307 } 308 /** 309 Draws a function on the graph by drawing lines between calculated points. 310 @param {Polynomial|Term} interpolated The interpolated function 311 **/ 312 graph.prototype.drawFunction = function(interpolated) 313 { 314 var COLOR = new Array('Blue','LimeGreen','Gold','Sienna','DarkRed','LightSlateGray','Purple','Black'); 315 316 var range = this.xhigh - this.xlow; 317 var plist = []; 318 // for(var i=0; i < range; i+=range/128) 319 var ingraph = false; 320 var oldery; 321 for(var i=0; i < range; i+=range/512) 322 { 323 var x = this.xlow+i; 324 var newy = interpolated.resolve(x); 325 if(newy != undefined) { 326 if(newy >= this.ylow && newy <= this.yhigh) { 327 if(ingraph == false && oldery != undefined) { 328 plist.push(oldery); 329 } 330 plist.push(new Point(x,newy)); 331 ingraph = true; 332 }else if(ingraph == true) { 333 plist.push(new Point(x,newy)); 334 ingraph = false; 335 } 336 oldery = new Point(x,newy); 337 } 338 } 339 //console.log(plist); 340 plist.sort(function(a,b) { 341 if(a.x > b.x) 342 { 343 return 1; 344 } 345 if(a.x < b.x) 346 { 347 return -1; 348 } 349 return 0; 350 }); 351 352 //console.log(plist); 353 var xpu = this.xpu(); 354 var ypu = this.ypu(); 355 var c = canvas.getContext('2d'); 356 c.beginPath(); 357 c.strokeStyle=COLOR[this.counter%COLOR.length]; 358 this.counter++; 359 c.lineWidth = 2; 360 var pixelx =(plist[0].x-this.xlow) * xpu ; 361 var pixely =canvas.height -(plist[0].y-this.ylow)* ypu; 362 c.moveTo(pixelx,pixely); 363 for(var i=1; i < plist.length; i++) 364 { 365 var oldx = pixelx; 366 var oldy = pixely; 367 pixelx =(plist[i].x-this.xlow) * xpu ; 368 pixely =canvas.height -(plist[i].y-this.ylow)* ypu; 369 c.lineTo(pixelx,pixely) ; 370 //c.bezierCurveTo(oldx,pixely,pixelx,oldy,pixelx,pixely); 371 c.stroke(); 372 } 373 } 374 375 /** 376 Draws a curve defined by points 377 @param {Point[]} points The points to be plotted and connected; 378 **/ 379 graph.prototype.plotAndConnectPoints = function(points) { 380 var COLOR = new Array('Blue','LimeGreen','Gold','Sienna','DarkRed','LightSlateGray','Purple','Black'); 381 points.sort(function(a,b) { 382 if(a.x > b.x) 383 { 384 return 1; 385 } 386 if(a.x < b.x) 387 { 388 return -1; 389 } 390 return 0; 391 }); 392 393 var xpu = this.xpu(); 394 var ypu = this.ypu(); 395 var c = canvas.getContext('2d'); 396 c.beginPath(); 397 c.strokeStyle=COLOR[this.counter%COLOR.length]; 398 this.counter++; 399 c.lineWidth = 2; 400 var pixelx =(points[0].x-this.xlow) * xpu ; 401 var pixely =canvas.height -(points[0].y-this.ylow)* ypu; 402 c.moveTo(pixelx,pixely); 403 for(var i=1; i < points.length; i++) 404 { 405 var oldx = pixelx; 406 var oldy = pixely; 407 pixelx =(points[i].x-this.xlow) * xpu ; 408 pixely =canvas.height -(points[i].y-this.ylow)* ypu; 409 c.lineTo(pixelx,pixely) ; 410 c.stroke(); 411 } 412 } 413 414 /** 415 Removes the chosen point from the graph 416 @param {String} p The point to be removed 417 **/ 418 graph.prototype.removePoint = function(p) 419 { 420 for(var i=0; i < this.points.length; i++) 421 { 422 if(p == this.points[i].toString()) 423 { 424 this.points.splice(i,1); 425 } 426 427 } 428 $("#s_points :selected").remove(); 429 this.redraw(); 430 431 } 432 433 /** 434 Removes all points from the graph 435 **/ 436 graph.prototype.removeAllPoints = function() 437 { 438 this.points = []; 439 $("#s_points").empty(); 440 this.redraw(); 441 } 442 443 /** 444 Removes the chosen function from the graph 445 @param {String} f The function to be removed 446 **/ 447 graph.prototype.removeFunction = function(f) 448 { 449 for(var i=0; i < this.functions.length; i++) 450 { 451 if(f == this.functions[i].toString()) 452 { 453 this.functions.splice(i,1); 454 } 455 456 } 457 $("#s_functionlist :selected").remove(); 458 this.redraw(); 459 460 } 461 462 /** 463 Removes all functions from the graph 464 **/ 465 graph.prototype.removeAllFunctions = function() 466 { 467 this.functions = []; 468 $("#s_functionlist").empty(); 469 this.redraw(); 470 } 471 472 /** 473 Clears all points and functions from the graph 474 **/ 475 graph.prototype.clear = function() 476 { 477 this.functions = []; 478 $("#s_functionlist").empty(); 479 this.points = []; 480 $("#s_points").empty(); 481 this.redraw(); 482 } 483 484 /** 485 Provide graphs current x-range 486 **/ 487 graph.prototype.xrange = function() 488 { 489 return new Range("["+this.xlow+","+this.xhigh+"]"); 490 } 491 492 /** 493 Provide graphs current y-range 494 **/ 495 graph.prototype.yrange = function() 496 { 497 return new Range("["+this.ylow+","+this.yhigh+"]"); 498 } 499 500 /** 501 Convert units to pixels 502 **/ 503 graph.prototype.pointToPixel = function(point) { 504 var xpu = this.xpu(); 505 var ypu = this.ypu(); 506 var pixelx =(point.x-this.xlow) * xpu ; 507 var pixely =canvas.height -(point.y-this.ylow)* ypu; 508 return new Point(pixelx,pixely); 509 510 } 511 /* 512 var newresult = []; 513 for(var i=0; i < result.length; i++) { 514 var newps = []; 515 for(var k=0; k < result[i].length; k++) { 516 newps.push(ourGraph.pointToPixel(result[i][k])); 517 } 518 } 519 */ 520