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