Stuck programming Conway's "Game of Life" in JS - javascript

We have to program a JavaScript version of Conway's Game of Life for a school project, but we're stuck on looping the edges. The whole thing works fine, but the function that calculates the number of neighbors doesn't work on the cells that are on the edges (because it has to evaluate values outside of the array, which are undefined). We've tried several options, but they all alter the functionality of the rest of the program.
What should we add for it to work on the edges of the grid?
var totalNeighbors = function(x, y) {
var total = 0;
if (x > 0 && cells[(x - 1)][y] == 1) {
total++;
}
if (x < (width - 1) && cells[x + 1][y] == 1) {
total++;
}
if (y > 0 && cells[x][y - 1] == 1) {
total++;
}
if (y < (height - 1) && cells[x][y + 1] == 1) {
total++;
}
if (y > 0 && x > 0 && cells[x - 1][y - 1] == 1) {
total++;
}
if (y > 0 && x < (width - 1) && cells[x + 1][y - 1] == 1) {
total++;
}
if (y < (height - 1) && x > 0 && cells[x - 1][y + 1] == 1) {
total++;
}
if (y < (height - 1) && x < (width - 1) && cells[x + 1][y + 1] == 1) {
total++;
}
return total;
};
Thanks!

I'd go with something more like this:
As you can see, I refactored a little bit.
var isvalid = function(x, y) {
/*
* This returns 1 if cells[x][y] == 1.
* Otherwise, we return 0.
* NOTE: If cells[x, y] is out of bounds, we return 0.
* GLOBALS USED: cells, width, and height.
*/
//This returns true if (index < size && index >= 0)
//Used to check that index is not an invalid index.
var inbounds = function (size, index) {
return (index >= 0 && index < size);
};
//given point is out of bounds
if (!inbounds(width, x) || !inbounds(height, y)) {
return 0;
}
//everything is good
return (cells[x][y] === 1) ? 1 : 0;
};
var totalNeighbors = function(x, y) {
var total = 0;
//cells[x-1][y]
total += isvalid(x-1, y);
//cells[x + 1][y]
total += isvalid(x+1, y);
//cells[x][y - 1]
total += isvalid(x, y-1);
//cells[x][y + 1]
total += isvalid(x, y+1);
//cells[x - 1][y - 1]
total += isvalid(x-1, y-1);
//cells[x + 1][y - 1]
total += isvalid(x+1, y-1);
//cells[x - 1][y + 1]
total += isvalid(x-1, y+1);
//cells[x + 1][y + 1]
total += isvalid(x+1, y+1);
return total;
};
PS: Your original code sample is 37 lines without comments. My code sample is 52 lines with comments and 33 lines without comments.
As near as I can figure, this way is cleaner and shorter. ;)

Related

Simplifying IF/ELSE condition

I want to ask how can I simplify my code? It seems hard to read and has too much if-else condition here. Any way to simplify the code?
if (e.shiftKey && this.idx > 0) {
this.idx= this.idx - 1;
} else if (!e.shiftKey && this.idx < trapFocus.length - 1) {
this.idx = this.idx + 1;
} else if (!e.shiftKey && this.idx < trapFocus.length + 1) {
this.idx= this.idx - 2;
} else if (e.shiftKey && this.idx > - 1) {
this.idx= this.idx + 2;
}
You can simply separate the condition using separate if-else
if (e.shiftKey){
if(this.idx > 0) this.idx = this.idx - 1;
else if(this.idx > -1) this.idx = this.idx + 2;
} else {
if(this.idx < trapFocus.length - 1)) this.idx = this.idx + 1;
else if(this.idx < trapFocus.length + 1) this.idx < trapFocus.length + 1
}
This first thing you could do is to factor out e.shiftKey and use += and -= operators
if(e.shiftKey)
{
if(this.idx > 0)
{
this.idx -= 1;
}
else if(this.idx > -1)
{
this.idx += 2;
}
}
else{
if(this.idx < trapFocus.length - 1)
{
this.idx += 1;
}
else if(this.idx < trapFocus.length + 1)
{
this.idx -= 2;
}
}
If you ever want to go with ternaries:
this.idx += e.shiftKey ? (
this.idx > 0 ? -1 :
this.idx > -1 ? 2 : 0
) : (
this.idx < trapFocus.length - 1 ? 1 :
this.idx < trapFocus.length + 1 ? -2 : 0
);
Note that this is not necessarily more readable, it just takes up less space.
You can get it a bit more succinct by using the fact that your if clauses logically imply each other partly.
if e.shiftKey is true you change something if this.idx is 0 or more and if e.shiftKey is false you change something only if this.idx < trapFocus.length + 1:
let offset = 0;
if (e.shiftKey){
if (this.idx >= 0) (this.idx ? offset = -1 : offset = 2)
} else {
if (this.idx < trapFocus.length + 1)
(this.idx < trapFocus.length - 1 ? offset = 1 : offset = -2)
}
this.idx += offset;
It is not necessarily much more readable.

Conways Game of Life p5js [closed]

Closed. This question is not reproducible or was caused by typos. It is not currently accepting answers.
This question was caused by a typo or a problem that can no longer be reproduced. While similar questions may be on-topic here, this one was resolved in a way less likely to help future readers.
Closed 4 years ago.
Improve this question
am trying to create conways game of life using p5js. (p5js website)
here is a link to a gif of the simulation: Google Drive Link
I have two 2d arrays, one to hold the current grid and one to hold the updated grid. the arrays hold a value of 0 = empty or 1 = active. I use an image to draw the grid with 1 pixel representing a cell in the grid. Now when I run the program it does something I cant explain (Explosive Growth.. See gif), but I think it has something to do with the rules to decide whether a cell should be active or not. Or it could possibly be something to do with me have multiple grids?
any help would be appreciated to help find whats wrong and why its producing these patterns
heres my logic to decide whether a cell should be active or not
for(var x = 0; x < width; x++){
for(var y = 0; y < height; y++){
//get cells neighbors
var n = getNeighbors(x,y);
//if cell is active
if(grid[x][y] == 1){
// #1 - Any live cell with fewer than two live neighbors dies, as if by underpopulation.
if(n < 2){
newGrid[x][y] = 0;
}else if(n > 3){// #3 -Any live cell with more than three live neighbors dies, as if by overpopulation.
newGrid[x][y] = 0;
}
// #2 - Any live cell with two or three live neighbors lives on to the next generation.
// this doesnt need a statement since if neighbors value is not < 2 or not > 3 then the value can only be 2 or 3
}else{
// #4 - Any dead cell with exactly three live neighbors becomes a live cell, as if by reproduction.
if(n == 3){
newGrid[x][y] = 1;
}
}
}
}
and just for reference here is the whole source with comments(135 lines)
var grid = [];
var newGrid = [];
var img;
function setup() {
createCanvas(400, 400);
frameRate(10);
//populate grid array
for (var i = 0; i < width; i++) {
grid[i] = [];
for (var j = 0; j < height; j++) {
var r = random(0, 1);
if (r > 0.9) {
grid[i][j] = 1;
} else {
grid[i][j] = 0;
}
}
}
//populate newgrid
newGrid = grid;
//create img object
img = createImage(width, height);
//populate img pixels
drawImageUsingGrid();
//draw img
image(img, 0, 0);
}
function draw() {
//loop through grid
for (var x = 0; x < width; x++) {
for (var y = 0; y < height; y++) {
//get cells neighbors
var n = getNeighbors(x, y);
//if cell is active
if (grid[x][y] == 1) {
// #1 - Any live cell with fewer than two live neighbors dies, as if by underpopulation.
if (n < 2) {
newGrid[x][y] = 0;
} else if (n > 3) { // #3 -Any live cell with more than three live neighbors dies, as if by overpopulation.
newGrid[x][y] = 0;
}
// #2 - Any live cell with two or three live neighbors lives on to the next generation.
// this doesnt need a statement since if neighbors value is not < 2 or not > 3 then the value can only be 2 or 3
} else {
// #4 - Any dead cell with exactly three live neighbors becomes a live cell, as if by reproduction.
if (n == 3) {
newGrid[x][y] = 1;
}
}
}
}
//set current grid to the new updated grid
grid = newGrid;
//set img pixels based upon grid
drawImageUsingGrid();
//draw img
image(img, 0, 0);
}
function getNeighbors(x, y) {
//hold # of neighbors
var neighbors = 0;
//check if neighbor exists at index and is active
if (grid[x] && grid[x][y - 1] && grid[x][y - 1] == 1) { //top
neighbors++;
}
if (grid[x + 1] && grid[x + 1][y] && grid[x + 1][y] == 1) { //right
neighbors++;
}
if (grid[x] && grid[x][y + 1] && grid[x][y + 1] == 1) { //bottom
neighbors++;
}
if (grid[x - 1] && grid[x - 1][y] && grid[x - 1][y] == 1) { //left
neighbors++;
}
//diagonal neighbors
if (grid[x - 1] && grid[x - 1][y - 1] && grid[x - 1][y - 1] == 1) { //topleft
neighbors++;
}
if (grid[x + 1] && grid[x + 1][y - 1] && grid[x + 1][y - 1] == 1) { //topright
neighbors++;
}
if (grid[x - 1] && grid[x - 1][y + 1] && grid[x - 1][y + 1] == 1) { //bottomleft
neighbors++;
}
if (grid[x + 1] && grid[x + 1][y + 1] && grid[x + 1][y + 1] == 1) { //bottomright
neighbors++;
}
return neighbors;
}
function drawImageUsingGrid() {
//load img pixels to be edited
img.loadPixels();
//2d for loop
for (var i = 0; i < img.width; i++) {
for (var j = 0; j < img.height; j++) {
if (grid[i][j] == 0) {
//get pixel at x,y
var pix = getPixelIndex(i, j);
//set pixel rgba
img.pixels[pix + 0] = 255;
img.pixels[pix + 1] = 255;
img.pixels[pix + 2] = 255;
img.pixels[pix + 3] = 255;
} else {
//get pixel at x,y
var pix = getPixelIndex(i, j);
//set pixel rgba
img.pixels[pix + 0] = 0;
img.pixels[pix + 1] = 0;
img.pixels[pix + 2] = 0;
img.pixels[pix + 3] = 255;
}
}
}
//update img pixels
img.updatePixels();
}
function getPixelIndex(x, y) {
return (x + y * width) * 4;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/0.6.1/p5.js"></script>

How to improve performance in this function?

I have a kind of "bouncing balls" project, where I draw 150 particles at a canvas and, at every redraw, it recalc particles position, and verify if any particles is at a corner, to invert its iterator.
But thing is that this project has a factor that not all "bouncing balls" projects have. The balls need to bounce within the bounds of a map.
So, when I create the canvas, I also use a SVG to iterate over the pixels and create a array of every bound in the x axis, left and right, so my particles will know exactly where they need to rebounce.
So good, so well, it is working nice, but my canvas is 500px tall, so it need to iterate 500 times, with a lot of conditionals to prevent weird behavior, this multiplied by 150 particles, and in every redraw.
It has became very performance greedy and I need to improve performance, so, here is my collision system code
const colisionSystem = state => {
for (var b=0, hs=state.bounds.length; b<hs; b++) {
if(
state.bounds[b][0]
&& state.x - state.radius < state.bounds[b][0].x
&& state.y + state.radius > state.bounds[b][0].y
&& state.y - state.radius < state.bounds[b][0].y
) {
if (
state.bounds[b][0].x > 0
&& state.bounds[b][0].x < (state.widgetSize.width * 0.33)
&& state.bounds[b][0].y > (state.widgetSize.height * 0.33)
&& state.bounds[b][0].y < (state.widgetSize.width * 0.45)
) {
// middle left bottom corner at acre
state.x = state.radius + state.bounds[b][0].x;
state.vy *= -1;
} else if (
state.bounds[b][0].x > 0
&& state.bounds[b][0].x < (state.widgetSize.width * 0.098)
&& state.bounds[b][0].y > (state.widgetSize.height * 0.167)
&& state.bounds[b][0].y < (state.widgetSize.width * 0.206)
) {
// middle left top corner at acre
state.y = state.radius + state.bounds[b][0].y + 1;
state.vx *= -1;
state.vy *= -1;
} else {
state.x = state.radius + state.bounds[b][0].x;
state.vx *= -1;
}
if(state.oldAxis === state.x) {
state.y = state.y - 1;
} else {
state.oldAxis = state.x;
}
state.antiRebounce = false;
}
if(
state.bounds[b][1]
&& state.x + state.radius > state.bounds[b][1].x
&& state.y + state.radius > state.bounds[b][1].y
&& state.y - state.radius < state.bounds[b][1].y
) {
if (
state.bounds[b][1].x > (state.widgetSize.width * 0.555)
&& state.bounds[b][1].x < (state.widgetSize.width * 0.983)
&& state.bounds[b][1].y > 0
&& state.bounds[b][1].y < (state.widgetSize.width * 0.2098)
) {
// Top right corner
if(state.antiRebounce) {
state.vy *= -1;
state.antiRebounce = false;
} else {
state.antiRebounce = true;
}
state.y = state.bounds[b][1].y + state.radius + 1;
state.vy *= -1;
}
if (
state.bounds[b][1].x > (state.widgetSize.width * 0.604)
&& state.bounds[b][1].x < (state.widgetSize.width * 0.827)
&& state.bounds[b][1].y > (state.widgetSize.width * 0.665)
&& state.bounds[b][1].y < (state.widgetSize.width * 0.778)
) {
// bottom right corner
state.vy *= -1;
} else {
state.vx *= -1;
state.x = state.bounds[b][1].x - state.radius;
}
if(state.oldAxis === state.x) {
state.y = state.y - 1;
} else {
state.oldAxis = state.x;
}
}
}
if (state.y + state.radius > state.widgetSize.height) {
state.vy *= -1;
state.y = state.widgetSize.height - state.radius;
}
if (state.y - state.radius < 0) {
state.vy *= -1;
state.y = state.radius;
}
return state;
}
export default colisionSystem;
So, question is, is there any practical advice to improve this code itself?
You have 500 * 150 particles (750000) that is a lot and frankly too much for a JS app to handle. (BUT you say 150 particles so I am somewhat confused as to what you are doing)
To increase the performance of the function provided you can use a few simple rules of thumb.
An array lookup is slower than a direct referance.
ie
// a compound referance
state.bounds[b][1].x // find state, then bounds, then index b, then 1,then x
// if you have the common parts used frequently
var b1 = state.bounds[b][1]; // does all the lookups
b1.x; // but now only needs one lookup to get x
Same is true for item properties. Each . means an additional lookup. You can get a lot of extra performance by creating temporary variables to hold the result of all the lookups.
Applying this to your code and you get
const colisionSystem = state => {
var w = state.widgetSize.width; // used many times in the loop
var h = state.widgetSize.height;
var x = state.x;
var y = state.y;
var r = state.radius;
for (var b = 0, hs = state.bounds.length; b < hs; b++) {
var bounds = state.bounds[b];
if (bounds[0]){
var b0 = bounds[0];
if( x - r < b0.x && y + r > b0.y && y - r < b0.y) {
if ( b0.x > 0 && b0.x < (w * 0.33) && b0.y > (h * 0.33) && b0.y < (w * 0.45)) {
x = r + b0.x; // middle left bottom corner at acre
state.vy *= -1;
} else if ( b0.x > 0 && b0.x < (w * 0.098) && b0.y > (h * 0.167) && b0.y < (w * 0.206)) {
y = r + b0.y + 1; // middle left top corner at acre
state.vx *= -1;
state.vy *= -1;
} else {
x = r + b0.x;
state.vx *= -1;
}
if (state.oldAxis === x) {
y -= 1;
} else {
state.oldAxis = x;
}
state.antiRebounce = false;
}
}
if (bounds[1]){
var b1 = bounds[1];
if( x + r > b1.x && y + r > b1.y && y - r < b1.y) {
if ( b1.x > (w * 0.555) && b1.x < (w * 0.983) && b1.y > 0 && b1.y < (w * 0.2098)) {
if (state.antiRebounce) { // Top right corner
state.vy *= -1;
state.antiRebounce = false;
} else {
state.antiRebounce = true;
}
y = b1.y + r + 1;
state.vy *= -1;
}
if (b1.x > (w * 0.604) && b1.x < (w * 0.827) && b1.y > (w * 0.665) && b1.y < (w * 0.778)) {
state.vy *= -1; // bottom right corner
} else {
state.vx *= -1;
x = b1.x - r;
}
if (state.oldAxis === x) {
y = y - 1;
} else {
state.oldAxis = x;
}
}
}
}
if (y + r > h) {
state.vy *= -1;
y = h - r;
} else if (y - r < 0) { // added else. Cant both happen at the same time?????
state.vy *= -1;
y = r;
}
state.y = y; // set state x,y to reflect any changes.
state.x = x;
return state;
}
By replacing the many compound references with direct references you can free up a lot of CPU time, but this will depend on the data. If the code above spends more time rejecting the conditional statements early you will not get much of a benefit, and if the bounds[1],bounds[0] conditions are only passed once a call to colisionSystem you will get no benefit, and maybe a slight decrease in performance. Same holds true for the number of items in the loop, if the loop is iterating many items you will see an improvement, if the loop is only 1 or 2 items you will see no benefit and for one item you will see a decrease in performance.
Note, I was not careful refactoring, there may be some typos in above code and is only an example.
You say you use SVG to iterate???
I also use a SVG to iterate over the pixels
Rule of thumb #2. The DOM is slow, SVG is part of the DOM and in my book SVG is really VSG (Very Slow Graphics) and I would say that the main part of the slow down is in whatever you do with the SVG.
Recommendations:
This function is doing way too much, consider breaking this up so that expressions like this
state.bounds[b][1].x > (state.widgetSize.width * 0.604)
&& state.bounds[b][1].x < (state.widgetSize.width * 0.827)
&& state.bounds[b][1].y > (state.widgetSize.width * 0.665)
&& state.bounds[b][1].y < (state.widgetSize.width * 0.778)
Are their own function
Consider using _.map instead of the for loop, you are return state anyway, so _.map would be more semantic.
Consider breaking up the nesting of ifs with functions. Every time you use a code comment, consider making it a function. For example
// Top right corner
if(state.antiRebounce) {
state.vy *= -1;
state.antiRebounce = false;
} else {
state.antiRebounce = true;
}
state.y = state.bounds[b][1].y + state.radius + 1;
state.vy *= -1;
could be
const topRightCorner = (state) => {
if(state.antiRebounce) {
state.vy *= -1;
state.antiRebounce = false;
} else {
state.antiRebounce = true;
}
state.y = state.bounds[b][1].y + state.radius + 1;
state.vy *= -1;
return state;
}
Once you broken this giant function into many smaller easier to understand functions. You can use chrome profiling tools to find bottlenecks for performance.
https://developers.google.com/web/tools/chrome-devtools/rendering-tools/
With the code broken up, it will be easy to see what part of the script is suffering from performance problems, at that point you can see about how to fix, without doing pre-mature optimization.
AS far as I know (but I don't know so much) your situation doesn't give us so much space of manovre because you need to "see" always in the screen all that bouncing objects. The bound system seems good for me checking for the bounds before a more accurate point collision detection.
How many fps are you trying to render? Maybe something could be tuned I guess.
Bye

Conway game of life in javascript( best sol [closed]

It's difficult to tell what is being asked here. This question is ambiguous, vague, incomplete, overly broad, or rhetorical and cannot be reasonably answered in its current form. For help clarifying this question so that it can be reopened, visit the help center.
Closed 10 years ago.
i am writing a code for conway game of life...... i am taking 2> arrays one fr old generation. and one for 2 nd genration.
**rules are : The universe of the Game of Life is an infinite two-dimensional orthogonal grid of square cells, each of which is in one of two possible states, alive or dead. Every cell interacts with its eight neighbours, which are the cells that are horizontally, vertically, or diagonally adjacent. At each step in time, the following transitions occur: **1.Any live cell with fewer than two live neighbours dies, as if caused by under-population.
2.Any live cell with two or three live neighbours lives on to the next generation.
3.Any live cell with more than three live neighbours dies, as if by overcrowding.
4.Any dead cell with exactly three live neighbours becomes a live cell, as if by reproduction.**
The initial pattern constitutes the seed of the system. The first generation is created by applying the above rules simultaneously to every cell in the seed—births and deaths occur simultaneously, and the discrete moment at which this happens is sometimes called a tick (in other words, each generation is a pure function of the preceding one). The rules continue to be applied repeatedly to create further generations.**
here is the code
I am getting a soln but i guess its not giving me the correct solution becuase its not checking the neighbors of the corners. i have marked that part
**
window.conway =
{
};
window.conway.maingame =
{
};
conway.maingame = function(width, height)
{
window.a = [];
this.width = width;
this.height = height;
this.map = new Array(width);
for( i = 0; i < this.width; i++)
{
this.map[i] = new Array(height);
}
console.log(this.map, "map")
}
conway.maingame.prototype.randomize = function()
{
for( y = 0; y < this.height; y++)
{
//console.log("enter for loop")
for( x = 0; x < this.width; x++)
{
if(Math.random() > .5)
{
i =true;
}
else
{
i = false;
}
//console.log("enter function")
this.set(x, y, i);
}
}
}
conway.maingame.prototype.set = function(x, y, val)
{
x = x % this.width;
y = y % this.height;
this.map[x][y] = val;
console.log(this.map, "map2");
}
conway.maingame.prototype.get = function(x, y)
{
x = x % this.width;
y = y % this.height;
return this.map[x][y];
}
*********************************************************************************
conway.maingame.prototype.neighbors = function(x, y)
{
count = 0;
if(x > 0 && y > 0 && this.get(x + 1, y + 1))
{
console.log(this.get(x + 1, y + 1), "value neighbor");
count++;
console.log(count);
}
if(x > 0 && y > 0 && this.get(x + 1, y))
{
console.log(this.get(x + 1, y), "vallue neighbor");
count++;
console.log(count);
}
if(x > 0 && y > 0 && this.get(x + 1, y - 1))
{
console.log(this.get(x + 1, y - 1), "vallue neighbor");
count++;
console.log(count);
}
if(x > 0 && y >=0 && this.get(x, y - 1))
{
console.log(this.get(x + 1, y - 1), "vallue neighbor");
count++;
console.log(count);
}
if(x > 0 && y > 0 && this.get(x - 1, y - 1))
{
console.log(this.get(x + 1, y - 1), "vallue neighbor");
count++;
console.log(count);
}
if(x > 0 && y > 0 && this.get(x - 1, y))
{
console.log(this.get(x + 1, y - 1), "vallue neighbor");
count++;
console.log(count);
}
if(x > 0 && y > 0 && this.get(x - 1, y + 1))
{
console.log(this.get(x + 1, y - 1), "vallue neighbor");
count++;
console.log(count);
}
if(x > 0 && y > 0 &&this.get(x, y + 1))
{
console.log(this.get(x + 1, y - 1), "vallue neighbor");
count++;
console.log(count);
}
return count;
}***
conway.maingame.prototype.newgeneration = function()
{
var newMap = new Array(this.width);
for( i = 0; i < this.width; i++)
{
newMap[i] = new Array(this.height);
}
for(var y = 0; y < this.height; y++)
{
for(var x = 0; x < this.width; x++)
{
console.log("enter all for");
newMap[x][y] = this.get(x, y);
console.log(newMap, "newarray");
//Rule 1: any live cell with fewer than two live neighbors dies
if(this.get(x, y) == true && this.neighbors(x, y) < 2)
{
newMap[x][y] = false;
console.log("rule1");
}
//Rule 2: Any live cell with two or three live neighbours lives on to the next generation
if(this.get(x, y) == true && this.neighbors(x, y) == 2 || this.neighbors(x, y) == 3)
{
newMap[x][y] = true
console.log("rule2");
}
//Rule 3: any live cell with more than three live neighbors dies
if(this.get(x, y) == true && this.neighbors(x, y) > 3)
{
newMap[x][y] = false;
console.log("rule3");
}
//Rule 4: any dead cell with exactly three live neighbors becomes a live cell
if(this.get(x, y) == false && this.neighbors(x, y) == 3)
{
newMap[x][y] = true;
console.log("rule4");
}
}
}
this.map = newMap;
console.log(this.map,"new generation")
}
**
I trawled through your code in JSHint and fixed all of the issues, wrote some glue to test it in the browser and here's the result: jsfiddle.
It looks like it's working to me, so I think the problem must have been due to one of the dozens of warnings that JSHint flagged up.
Re: your assertion that the issue was due to the corners, that's what these lines are for:
x = x % this.width;
y = y % this.height;
In my test case I'm using a 10 x 10, so when it comes to check the neighbours of (9,9) it looks at (10, 10) and (10 % 10, 10 % 10) is (0, 0), thus avoiding looking outside the array. I believe this is what's known as a toroidal array.
The lesson we learn from this? Keep on top of the small issues, and the big issues will take care of themselves.

correctness of in-place convolution filter?

I have a simple box blur function that takes an ImageData object, returning it when done. However, I have just realised that this implementation may be incorrect because the ImageData object is edited in place and convolution filters depend on surrounding pixels. Should I be reading from the original ImageData and writing to a new one so that each pixel doesn't depend on already-changed surrounding pixels? If so, I'll have to rework my web worker manager to supply a new ImageData for the convolution functions to write to.
expressive.boxBlur = function(data, options) {
var w = data.width, h = data.height, dataReal = data.data;
for (var i = 0; i < w; i++)
for (var j = 0; j < h; j++)
for (var k = 0; k < 4; k++) {
var total = 0, values = 0, temp = 0;
if (!(i == 0 && j == 0)) {
temp = dataReal[4 * w * (j - 1) + 4 * (i - 1) + k];
if (temp !== undefined) values++, total += temp;
}
if (!(i == w - 1 && j == 0)) {
temp = dataReal[4 * w * (j - 1) + 4 * (i + 1) + k];
if (temp !== undefined) values++, total += temp;
}
if (!(i == 0 && j == h - 1)) {
temp = dataReal[4 * w * (j + 1) + 4 * (i - 1) + k];
if (temp !== undefined) values++, total += temp;
}
if (!(i == w - 1 && j == h - 1)) {
temp = dataReal[4 * w * (j + 1) + 4 * (i + 1) + k];
if (temp !== undefined) values++, total += temp;
}
if (!(j == 0)) {
temp = dataReal[4 * w * (j - 1) + 4 * (i + 0) + k];
if (temp !== undefined) values++, total += temp;
}
if (!(j == h - 1)) {
temp = dataReal[4 * w * (j + 1) + 4 * (i + 0) + k];
if (temp !== undefined) values++, total += temp;
}
if (!(i == 0)) {
temp = dataReal[4 * w * (j + 0) + 4 * (i - 1) + k];
if (temp !== undefined) values++, total += temp;
}
if (!(i == w - 1)) {
temp = dataReal[4 * w * (j + 0) + 4 * (i + 1) + k];
if (temp !== undefined) values++, total += temp;
}
values++, total += dataReal[4 * w * j + 4 * i + k];
total /= values;
dataReal[4 * w * j + 4 * i + k] = total;
}
return data;
};
You're right, you need a separate image to put the convoluted result in. Unless the impuls response is a scaled dirac function. (i.e. it has only 1 point in the center)
However, you could do with a cache for only a few scanlines, saving a lot of memory.

Categories

Resources