factory/v2012/script.js
author Eugen Sawin <sawine@me73.com>
Thu, 05 Jan 2012 15:32:41 +0100
changeset 79 72b5c635d6df
parent 78 1e4ba37124e3
child 80 ba69edadfc4c
permissions -rw-r--r--
Fixed game of life.
     1 var QUOTES_NUMBER = 39;
     2 var SERVER = "http://" + window.location.hostname + ":" + window.location.port;
     3 var simulation;
     4 
     5 $(document).ready(function() {   
     6     simulation = new Simulation(document.getElementById("sim"));  
     7     var image = new Image();
     8     image.onload = init_logo;
     9     image.src = "images/logobase.png";
    10     $("#logo").click(handle_click);   
    11     $("#logo").hover(function() {
    12         $(this).css("cursor", "pointer");        
    13     }, function() {
    14         $(this).css("cursor", "auto");
    15     });
    16     $("#sim").mousemove(function(event) { simulation.mouse_moved(mouse_pos(event, "#sim")); });
    17     var onEachFrame;
    18     if (window.webkitRequestAnimationFrame) {
    19         onEachFrame = function(cb) {
    20             var _cb = function() { cb(); webkitRequestAnimationFrame(_cb); }
    21             _cb();
    22         };
    23     } else if (window.mozRequestAnimationFrame) {
    24         onEachFrame = function(cb) {
    25             var _cb = function() { cb(); mozRequestAnimationFrame(_cb); }
    26             _cb();
    27         };
    28     } else {
    29         onEachFrame = function(cb) {
    30             setInterval(cb, 1000 / 30);
    31         }
    32     }    
    33     window.onEachFrame = onEachFrame;   
    34     window.onEachFrame(update);    
    35 });
    36 
    37 $(document).keypress(function(event) {
    38     if (event.which == 13) {      
    39 	event.preventDefault();
    40         update_logo();	    
    41     }
    42 });
    43 
    44 var menu_width = 670;
    45 var menu_height = 100;
    46 var menu_splits = new Array(180, 350, 505);
    47 
    48 var click_events = new Array();
    49 click_events["logo"] = new Array();
    50 click_events["logo"][0] = new Object();
    51 click_events["logo"][0]["min_x"] = 0;
    52 click_events["logo"][0]["max_x"] = menu_splits[0];
    53 click_events["logo"][0]["min_y"] = 0;
    54 click_events["logo"][0]["max_y"] = menu_height;
    55 click_events["logo"][0]["func"] = switch_page;
    56 click_events["logo"][0]["args"] = new Array("personalwork.html");
    57 click_events["logo"][1] = new Object();
    58 click_events["logo"][1]["min_x"] = menu_splits[0] + 1;
    59 click_events["logo"][1]["max_x"] = menu_splits[1];
    60 click_events["logo"][1]["min_y"] = 0;
    61 click_events["logo"][1]["max_y"] = menu_height;
    62 click_events["logo"][1]["func"] = switch_page;
    63 click_events["logo"][1]["args"] = new Array("howiwork.html");
    64 click_events["logo"][2] = new Object();
    65 click_events["logo"][2]["min_x"] = menu_splits[1] + 1;
    66 click_events["logo"][2]["max_x"] = menu_splits[2];
    67 click_events["logo"][2]["min_y"] = 0;
    68 click_events["logo"][2]["max_y"] = menu_height;
    69 click_events["logo"][2]["func"] = switch_page;
    70 click_events["logo"][2]["args"] = new Array("books.html");
    71 click_events["logo"][3] = new Object();
    72 click_events["logo"][3]["min_x"] = menu_splits[2] + 1;
    73 click_events["logo"][3]["max_x"] = menu_width - 1;
    74 click_events["logo"][3]["min_y"] = 0;
    75 click_events["logo"][3]["max_y"] = menu_height;
    76 click_events["logo"][3]["func"] = switch_page;
    77 click_events["logo"][3]["args"] = new Array("links.html", "linksend.html");
    78 
    79 function mouse_pos(event, id) { 
    80     var offset = $(id).offset();   
    81     return new Array(event.pageX - offset.left, 
    82                      event.pageY - offset.top);
    83 }
    84 
    85 function handle_click(event) {
    86     var xy = mouse_pos(event, "#logo");
    87     var x = xy[0];
    88     var y = xy[1];
    89     for (var i in click_events[event.target.id]) {
    90         var ces = click_events[event.target.id][i];
    91         if (ces && x >= ces["min_x"] && x <= ces["max_x"]
    92             && y >= ces["min_y"] && y <= ces["max_y"]) {    
    93             ces["func"](event.target.id, ces);
    94             break;
    95         }
    96     }
    97 }
    98 
    99 function switch_page(id, params) {
   100     var page = params["args"][0];
   101     $("body").load(page);
   102     document.location.href = page;    
   103 }
   104 
   105 function colour_area(image, min_x, max_x, min_y, max_y, colour) {   
   106     var pixels = image.data;    
   107     for (var y = min_y; y <= max_y; y += 1) {
   108         for (var x = min_x; x <= max_x; x += 1) {
   109             var pix_colour = getPixel(image, x, y);
   110             if (pix_colour.a > 0) {
   111                 colour.a = pix_colour.a;
   112                 setPixel(image, x, y, colour);
   113             }
   114         }
   115     }    
   116 }
   117 
   118 function init_logo(event) {
   119     var canvas = document.getElementById("logo");
   120     var context = canvas.getContext("2d");    
   121     canvas.width = menu_width;
   122     canvas.height = menu_height;
   123     context.drawImage(event.target, 0, 0);
   124     var image = context.getImageData(0, 0, menu_width, menu_height);    
   125     for (var i in click_events["logo"]) {
   126         var p = click_events["logo"][i]; 
   127         var min_x = p["min_x"];
   128         var max_x = p["max_x"];
   129         var min_y = p["min_y"];
   130         var max_y = p["max_y"];
   131         var pos = document.location.href.lastIndexOf("/") + 1; 
   132         var page = document.location.href.substr(pos);        
   133         for (var j in p["args"]) {
   134             if (page == p["args"][j]) {         
   135                 colour_area(image, min_x, max_x, 0, 60, black);
   136                 colour_area(image, min_x, max_x, 60, max_y, white);
   137                 break;
   138             }
   139             else {
   140                 colour_area(image, min_x, max_x, 0, 60, white);
   141                 colour_area(image, min_x, max_x, 60, max_y,  orange);  
   142             }
   143         }       
   144     } 
   145     context.putImageData(image, 0, 0);      
   146     // context.fillStyle = green.str();
   147     // console.log((found["max_x"] - found["min_x"]) / 2 + found["min_x"] - 2);
   148     // context.fillRect((found["max_x"]-found["min_x"])/2+found["min_x"]-1, 95, 2, 5);  
   149     drawMenuLines();   
   150 }
   151 
   152 function drawMenuLines() {
   153     var canvas = document.getElementById("logo");
   154     var context = canvas.getContext("2d");  
   155     var image = context.getImageData(0, 0, menu_width, menu_height);
   156     var pixels = image.data;
   157     context.lineCap = "round";
   158     context.lineWidth = 1;
   159     var colour1a = new Colour(0, 0, 0, 0.1);
   160     context.beginPath();   
   161     context.moveTo(0, menu_height - 1);
   162     context.lineTo(menu_width - 1, menu_height - 1);   
   163     context.closePath();  
   164     context.strokeStyle = colour1a.str();
   165     context.stroke(); 
   166     var colour1e = new Colour(0, 0, 0, 0.05);  
   167     context.beginPath();   
   168     context.moveTo(70, menu_height - 1);
   169     context.lineTo(menu_width - 71, menu_height - 1);   
   170     context.closePath();  
   171     context.strokeStyle = colour1e.str();
   172     context.stroke();  
   173     var colour1b = new Colour(0, 0, 0, 0.05);  
   174     context.beginPath();   
   175     context.moveTo(50, menu_height - 1);
   176     context.lineTo(menu_width - 51, menu_height - 1);   
   177     context.closePath();  
   178     context.strokeStyle = colour1b.str();
   179     context.stroke();
   180     var colour1c = new Colour(0, 0, 0, 0.05);  
   181     context.beginPath();   
   182     context.moveTo(25, menu_height - 1);
   183     context.lineTo(menu_width - 26, menu_height - 1);   
   184     context.closePath();  
   185     context.strokeStyle = colour1c.str();
   186     context.stroke();
   187     var colour1d = new Colour(0, 0, 0, 0.05);  
   188     context.beginPath();   
   189     context.moveTo(10, menu_height - 1);
   190     context.lineTo(menu_width - 11, menu_height - 1);   
   191     context.closePath();  
   192     context.strokeStyle = colour1d.str();
   193     context.stroke();  
   194     
   195     var colour2 = new Colour(0, 0, 0, 0.2);
   196     context.beginPath();  
   197     context.moveTo(menu_splits[0], 0); 
   198     context.lineTo(menu_splits[0], menu_height - 1);
   199     context.moveTo(menu_splits[1], 0);
   200     context.lineTo(menu_splits[1], menu_height - 1);
   201     context.moveTo(menu_splits[2], 0);
   202     context.lineTo(menu_splits[2], menu_height - 1);
   203     context.closePath();
   204     context.strokeStyle = colour2.str();
   205     context.stroke();
   206 
   207     context.clearRect(menu_splits[0] - 1, menu_height - 1, 2, 2);
   208     context.clearRect(menu_splits[1] - 1, menu_height - 1, 2, 2);
   209     context.clearRect(menu_splits[2] - 1, menu_height - 1, 2, 2);
   210 
   211     context.clearRect(menu_splits[0] - 1, menu_height - 2, 1, 1);
   212     context.clearRect(menu_splits[1] - 1, menu_height - 2, 1, 1);
   213     context.clearRect(menu_splits[2] - 1, menu_height - 2, 1, 1);
   214 }
   215 
   216 function clearMenuLines() {
   217     var canvas = document.getElementById("logo");
   218     var context = canvas.getContext("2d");  
   219     var image = context.getImageData(0, 0, menu_width, menu_height);
   220     var pixels = image.data;
   221     context.clearRect(0, menu_height - 2, menu_width - 1, 2);
   222     context.clearRect(menu_splits[0] - 1, 0, 2, menu_height - 1);
   223     context.clearRect(menu_splits[1] - 1, 0, 2, menu_height - 1);
   224     context.clearRect(menu_splits[2] - 1, 0, 2, menu_height - 1);
   225 }
   226 
   227 function update_logo() {
   228     var canvas = document.getElementById("logo");
   229     var context = canvas.getContext("2d");  
   230     var image = context.getImageData(0, 0, menu_width, menu_height);
   231     var pixels = image.data;
   232     var white = new Colour(255, 255, 255);
   233     for (var y = 0; y < menu_height; y += 1) {
   234         for (var x = 0; x < menu_width; x += 1) {
   235             var pix_colour = getPixel(image, x, y);
   236             if (pix_colour.a > 0) {
   237                 setPixel(image, x, y, new Colour(90, 215, 21, pix_colour.a));
   238             }
   239         }
   240     }   
   241     context.putImageData(image, 0, 0);
   242 }
   243 
   244 function Colour(r, g, b, a) {
   245     this.r = r;
   246     this.g = g;
   247     this.b = b;
   248     this.a = a == undefined ? 255 : a;
   249 }
   250 Colour.prototype.equals = function(rhs) {
   251     for (p in this) {
   252         if (typeof(rhs[p]) == undefined
   253            || this[p] != rhs[p]) {           
   254             return false;
   255         }
   256     }
   257     return true;
   258 }
   259 Colour.prototype.str = function() {
   260     return "rgba(" + this.r + "," + this.g + "," + this.b + "," + this.a + ")";
   261 }
   262 
   263 var white = new Colour(255, 255, 255);
   264 var black = new Colour(0, 0, 0);
   265 var orange = new Colour(201, 87, 35);
   266 var green = new Colour(90, 215, 21);
   267 var background = new Colour(34, 34, 34);
   268 
   269 function setPixel(image, x, y, colour) {
   270     var index = 4 * (x + y * image.width);
   271     image.data[index] = colour.r;
   272     image.data[index + 1] = colour.g;
   273     image.data[index + 2] = colour.b;
   274     image.data[index + 3] = colour.a;
   275 }
   276 
   277 function getPixel(image, x, y) {   
   278     var index = 4 * (x + y * image.width);
   279     var r = image.data[index];
   280     var g = image.data[index + 1];
   281     var b = image.data[index + 2];
   282     var a = image.data[index + 3];
   283     return new Colour(r, g, b, a);
   284 }
   285 
   286 function load_random_quote() {
   287     var file = "/quotes/quote" + Math.floor(Math.random() * QUOTES_NUMBER + 1) + ".html";
   288     $.ajax({url: SERVER + file,
   289             success: write_quote});
   290 }
   291 
   292 function write_quote(data, status, xhr) {   
   293     $("#random_quote").html(data);
   294 }
   295 
   296 function load_footer()
   297 {
   298 	var file = "/footer.html";
   299 	var currentFile = self.location.hostname + self.location.pathname;		
   300 	var request = http_request_object();
   301 	var url  = "http://" + self.location.hostname + file;	
   302 	request.open("GET", url, false);
   303 	request.setRequestHeader("User-Agent", navigator.userAgent);
   304 	request.send(null)
   305 	// if (oRequest.status == 200) alert(oRequest.responseText);
   306 	// else alert("Error executing XMLHttpRequest call!");	
   307 	//document.write(url);
   308 	//document.write(request.responseText);
   309     document.getElementById('footer').innerHTML = request.responseText;
   310 }
   311 
   312 
   313 
   314 // Mandelbrot functions
   315 function Complex(real, imag) 
   316 {
   317 	this.real = real;
   318 	this.imag = imag;
   319 }
   320 
   321 var MIN_C = new Complex(-2.2, -1.4);
   322 var MAX_C = new Complex(1.0, 1.4);
   323 var min_c = MIN_C;
   324 var max_c = MAX_C;
   325 var MIN_ITER = 100;
   326 var max_iter = MIN_ITER;
   327 var zoom = 1.0;
   328 var resolution = 3;
   329 var bailout = 4.0;
   330 
   331 function Result(z, iter) 
   332 {
   333 	this.z = z;
   334 	this.iter = iter;
   335 }
   336 
   337 function complex_quad(c)
   338 {
   339 	return new Complex(Math.pow(c.real, 2) - Math.pow(c.imag, 2), 
   340 		2.0 * c.real * c.imag);
   341 }
   342 
   343 function complex_quad_value(c)
   344 {
   345 	return Math.pow(c.real, 2) + Math.pow(c.imag, 2);
   346 }
   347 
   348 function complex_add(c1, c2)
   349 {
   350 	return new Complex(c1.real + c2.real, c1.imag + c2.imag);
   351 }
   352 
   353 function complex_equal(c1, c2)
   354 {	
   355 	return (c1.real == c2.real) && (c1.imag == c2.imag);
   356 }
   357 
   358 function iterate(z, c) 
   359 {
   360 	z_quad = complex_quad(z);
   361 	return new Complex(z_quad.real + c.real, z_quad.imag + c.imag);
   362 }
   363 
   364 function test(c, max_iter) 
   365 {
   366 	var iter = 0;
   367 	var z = new Complex(0.0, 0.0);
   368 	var last_z = new Complex(-1.0, 0.0);
   369 	var quad_z = complex_quad_value(z);
   370 	
   371 	while (iter < max_iter
   372 		&& !complex_equal(z, last_z)
   373 		&& quad_z <= bailout)
   374 	{
   375 		last_z = z;
   376 		z = iterate(z, c);
   377 		quad_z = complex_quad_value(z);
   378 		iter++;
   379 	}
   380 	return new Result(quad_z, iter);
   381 }
   382 
   383 function draw(diter, dx, dy, dz, dres)
   384 {  
   385 	var canvas = document.getElementById('mandelbrot'); 
   386 	
   387 	if (canvas.getContext)
   388 	{  
   389 		zoom += dz;
   390 		var ctx = canvas.getContext('2d');
   391 
   392 		if (dres != 0)
   393 		{		
   394 			resolution = Math.max(1, resolution + dres);
   395 		}
   396 
   397 		if (diter != 0)
   398 		{
   399 			max_iter = Math.max(MIN_ITER, max_iter + diter);
   400 		}
   401 		
   402 		var red = "rgb(255, 0, 0)";
   403 		var white = "rgb(255, 255, 255)";
   404 		var width = canvas.width;
   405 		var height = canvas.height;
   406 		var dim = Math.max(width, height);
   407 		var dim_ratio = Math.round(width / height);	
   408 		var diff_c = new Complex(max_c.real - min_c.real,
   409 			max_c.imag - min_c.imag);
   410 		dx_min = diff_c.real / 100 * (dx + dz);
   411 		dx_max = diff_c.real / 100 * (dx - dz);
   412 
   413 		dy_min = diff_c.imag / 100 * (dy + dz);
   414 		dy_max = diff_c.imag / 100 * (dy - dz);
   415 
   416 		var min_inc = new Complex(dx_min * dim_ratio / 2.0, dy_min);
   417 		var max_inc = new Complex(dx_max * dim_ratio / 2.0, dy_max);
   418 		min_c = complex_add(min_c, min_inc);
   419 		max_c = complex_add(max_c, max_inc);
   420 		diff_c = new Complex(max_c.real - min_c.real,
   421 			max_c.imag - min_c.imag);
   422 		
   423 		for (var y = 0; y < height; y += resolution) 
   424 		{
   425 			for (var x = 0; x < width; x += resolution) 
   426 			{    			
   427 				var c = new Complex(min_c.real + diff_c.real / dim * x, 
   428 					min_c.imag + diff_c.imag / dim * y);  	
   429 			 	var result = test(c, max_iter);	
   430 		 		var r = Math.min(255, Math.pow(Math.max(0, 
   431 		 			(result.iter - max_iter / 20.0)), 2));
   432 				var g = Math.min(255, Math.pow(Math.max(0, 
   433 					(result.iter - max_iter / 25.0)), 2));			
   434 		 		var b = Math.min(255, Math.pow(Math.max(0, 
   435 					(result.iter - max_iter / 20.0)), 2));
   436 		 		var colour = "rgb(" + r + "," + g + "," + b + ")";
   437 		 		ctx.fillStyle = colour; 
   438 				ctx.fillRect(x, y, resolution, resolution);			
   439 			}
   440   		}
   441 	}  
   442 } 
   443 
   444 function getEventOffsetX(evt)
   445 {
   446 	if (evt.offsetX != null)
   447 		return evt.offsetX;
   448  
   449     var obj = evt.target || evt.srcElement;
   450    	setPageTopLeft(obj);
   451     return (evt.clientX - obj.pageLeft);
   452 }
   453 
   454 function getEventOffsetY(evt)
   455 {
   456 	if (evt.offsetY != null)
   457 		return evt.offsetY;
   458  
   459     var obj = evt.target || evt.srcElement;
   460    	setPageTopLeft(obj);
   461     return (evt.clientY - obj.pageTop);
   462 }
   463  
   464 function setPageTopLeft( o )
   465 {
   466     var top = 0,
   467     left = 0,
   468     obj = o;
   469  
   470     while (o.offsetParent)
   471      {
   472          left += o.offsetLeft ;
   473          top += o.offsetTop ;
   474          o = o.offsetParent ;
   475     };
   476  
   477     obj.pageTop = top;
   478     obj.pageLeft = left; 
   479 }
   480  
   481 function draw2(evt) {
   482     var iter = 0;
   483     var res = 0;
   484     var x = (getEventOffsetX(evt) - 335) / 167.5;
   485     var y = (getEventOffsetY(evt) - 140) / 70;
   486     var z = 0;
   487     draw(iter, x, y, z, res);
   488 }
   489 
   490 function update() {
   491     simulation.update();
   492     simulation.draw();
   493 }
   494 
   495 function Grid(width, height, cell_size) {
   496     this.width = parseInt(width / cell_size);
   497     this.height = parseInt(height / cell_size);
   498     this.canvas_width = width;
   499     this.canvas_height = height;
   500     this.cell_size = cell_size;  
   501     this.cells = new Array();
   502     for (var y = 0; y < this.height; ++y) { 
   503         this.cells[y] = new Array();
   504         for (var x = 0; x < this.width; ++x) {
   505             this.cells[y][x] = new Cell(x, y, 0, this);
   506         }
   507     }
   508 }
   509 Grid.prototype.cell = function(x, y) {   
   510     return this.cells[y][x];
   511 }
   512 Grid.prototype.pick_cell = function(x, y) {
   513     var lx = parseInt(x / this.cell_size);
   514     var ly = parseInt(y / this.cell_size);
   515     return this.cells[ly][lx];
   516 }
   517 
   518 function Cell(x, y, value, grid) {
   519     this.x = x;
   520     this.y = y;  
   521     this.canvas_x = x * grid.cell_size;
   522     this.canvas_y = y * grid.cell_size;
   523     this.value = value;
   524     this.grid = grid;
   525     this._neighbours = new Array();
   526     this.last_mod = 0;
   527 }
   528 Cell.prototype.hash = function() {
   529     return this.x + ", " + this.y;
   530 }
   531 Cell.prototype.set_value = function(value) {
   532     this.value = value;
   533     this.last_mod = $.now();
   534 }
   535 Cell.prototype.neighbours = function() {
   536     if (this._neighbours.length) {
   537         return this._neighbours;
   538     }
   539     if (this.x > 0) {
   540         this._neighbours.push(this.grid.cell(this.x-1, this.y));
   541         if (this.y > 0) {
   542             this._neighbours.push(this.grid.cell(this.x-1, this.y-1));
   543             this._neighbours.push(this.grid.cell(this.x, this.y-1));
   544         }
   545         if (this.y + 1 < this.grid.height) {
   546             this._neighbours.push(this.grid.cell(this.x-1, this.y+1));
   547         }
   548     }
   549     if (this.x + 1 < this.grid.width) {
   550         this._neighbours.push(this.grid.cell(this.x+1, this.y));
   551         if (this.y + 1 < this.grid.height) {
   552             this._neighbours.push(this.grid.cell(this.x+1, this.y+1));
   553             this._neighbours.push(this.grid.cell(this.x, this.y+1));
   554         }
   555         if (this.y > 0) {
   556             this._neighbours.push(this.grid.cell(this.x+1, this.y-1));
   557         }
   558     }
   559     return this._neighbours;
   560 }
   561 Cell.prototype.density = function() {
   562     var d = 0;
   563     var neighbours = this.neighbours();
   564     for (var n in neighbours) {
   565         d += neighbours[n].value;
   566     }
   567     return d;
   568 }
   569 
   570 function Simulation(canvas) {
   571     this.last_update = 0;
   572     this.last_draw = 0;
   573     this.last_mouse_moved = 0;
   574     this.redraw = new Array();   
   575     this.canvas = canvas;
   576     this.canvas.width = 670;
   577     this.canvas.height = 30;
   578     this.context = canvas.getContext("2d");    
   579     this.pos_queue = new Array();
   580     this.cell_queue = new Array();  
   581     var cell_size = 3;
   582     this.grid = new Grid(this.canvas.width, this.canvas.height, cell_size);
   583 }
   584 Simulation.prototype.draw = function() {
   585     var now = $.now();
   586     if (this.redraw.length) {
   587         var cell = this.redraw.pop();
   588         this.context.fillStyle = cell.value == 1 ? green.str() : background.str();
   589         this.context.fillRect(cell.canvas_x, cell.canvas_y, 
   590                               this.grid.cell_size, this.grid.cell_size);
   591         this.last_draw = $.now();
   592     }
   593 }
   594 Simulation.prototype.update = function() {
   595     var now = $.now();
   596     if (this.pos_queue.length) {
   597         var pos = this.pos_queue.pop();
   598         if (pos[0] < this.canvas.width
   599             && pos[1] < this.canvas.height) {
   600             var cell = this.grid.pick_cell(pos[0], pos[1]);
   601             cell.set_value(1);
   602             this.cell_queue.push(cell);
   603             this.cell_queue.concat(cell.neighbours()); 
   604             this.redraw.push(cell);          
   605         }
   606     }
   607     if (this.last_update + 1000 > now) {
   608         return;
   609     }
   610     var changed = new Array();
   611     var next_cell_queue = new Array();
   612     while (this.cell_queue.length) {
   613         var cell = this.cell_queue.pop();
   614         if (changed[cell.hash()]) {           
   615             continue;
   616         }
   617         var d = cell.density();
   618         if (d == 3 && cell.value == 0) {           
   619             changed[cell.hash()] = cell;            
   620             next_cell_queue = next_cell_queue.concat(cell.neighbours());               
   621         } else if (cell.value == 1) {
   622             if (d < 2 || d > 3) {              
   623                 changed[cell.hash()] = cell;                
   624                 next_cell_queue = next_cell_queue.concat(cell.neighbours());               
   625             }
   626         }       
   627     }
   628     for (var pos in changed) {
   629         var cell = changed[pos];      
   630         cell.set_value(1 - cell.value);
   631         this.redraw.push(cell);
   632     }
   633     this.cell_queue = this.cell_queue.concat(next_cell_queue);
   634     this.last_update = now;  
   635 }
   636 Simulation.prototype.mouse_moved = function(pos) {
   637     var now = $.now();    
   638     if (this.last_mouse_moved + 50 < now) {
   639         this.pos_queue.push(pos);
   640         this.last_mouse_moved = now;       
   641     }
   642 }
   643 
   644