JavaScript: Web Workers und Conway's Game of Life in 3D

Exkurse

Inhaltsverzeichnis
var Universum = function(groesse) {
var zellenArray = new Uint8Array((groesse*groesse*groesse)>>3)

var berechneOffset = function(x, y, z) {
return
((z+groesse)%groesse)*groesse*groesse +
((y+groesse)%groesse)*groesse +
(x+groesse)%groesse;
};

this.init = function(fuellrate) {
var zellen = fuellrate*groesse*groesse*groesse;

for(var i=0; i<zellen; i++) {
var fertig = false;

do {
var position = [
Math.floor(Math.random()*groesse),
Math.floor(Math.random()*groesse),
Math.floor(Math.random()*groesse) ];

if(!this.get(position[0], position[1], position[2])) {
this.set(position[0], position[1], position[2], 1);
fertig = true;
}
} while(!fertig);
}
};

this.set = function(x, y, z, value) {
var offset = berechneOffset(x, y, z);

zellenArray[offset>>3] &= 255 ^ (1 << (offset&7));
zellenArray[offset>>3] |= value << (offset&7);
};

this.get = function(x, y, z) {
var offset = berechneOffset(x, y, z);

return (zellenArray[offset>>3] >> (offset&7)) & 1;
};

this.zaehleNachbarn = function(x, y, z) {
return (
this.get(x-1, y-1, z-1) +

...

this.get(x+1, y+1, z+1));
};

this.generierePositionsArray = function(positionsArrayBuffer) {
var positionsArray = new Float32Array(positionsArrayBuffer);
var index = 0;

for(var z=0; z<groesse; z++) {
for(var y=0; y<groesse; y++) {
for(var x=0; x<groesse; x++) {
if(this.get(x, y, z) == 0)
continue;

positionsArray[index] = x / (groesse-1) - 0.5;
positionsArray[index+1] = y / (groesse-1) - 0.5;
positionsArray[index+2] = z / (groesse-1) - 0.5;
positionsArray[index+3] = this.zaehleNachbarn(x, y, z)*2.0;

index += 4;
}
}
}

return positionsArray.subarray(0, index);
};
};
//Universum mit angepasstem Konstruktor, Zugriff auf die ArrayBuffers 
//und geänderten Settern und Gettern

var Universum = function(groesse, zellenArrayBuffers, zOffset) {
var zellenArray = [];

if(typeof zellenArrayBuffers !== 'undefined') {
if(zellenArrayBuffers.length == 1) {
zellenArray.push(new Uint8Array(zellenArrayBuffers[0]));
} else if(zellenArrayBuffers.length == 3) {
zellenArray.push(new Uint8Array(zellenArrayBuffers[0]));
zellenArray.push(new Uint8Array(zellenArrayBuffers[1]));
zellenArray.push(new Uint8Array(zellenArrayBuffers[2]));
zOffset--;
} else {
for(var z=0; z<groesse; z++)
zellenArray.push(new Uint8Array(zellenArrayBuffers[z]));
}
} else {
for(var z=0; z<groesse; z++)
zellenArray.push(new Uint8Array((groesse*groesse)>>3));
}

if(zOffset === undefined)
zOffset = 0;

var berechneOffset = function(x, y) {
return ((y+groesse)%groesse)*groesse + (x+groesse)%groesse;
};

var berechneZOffset = function(z) {
return (z-zOffset+groesse)%groesse;
};

this.buffer = function(z, zellenArrayBuffer) {
if(zellenArrayBuffer !== undefined) {
zellenArray[berechneZOffset(z)] =
new Uint8Array(zellenArrayBuffer);
} else {
return zellenArray[berechneZOffset(z)].buffer;
}
};

this.buffers = function(zellenArrayBuffer) {
if(zellenArrayBuffer !== undefined) {
for(var i=0; i<zellenArrayBuffer.length; i++)
zellenArray[i] = new Uint8Array(zellenArrayBuffer[i]);
} else {
var zellenArrayBuffer = [];

for(var i=0; i<zellenArray.length; i++)
zellenArrayBuffer.push(zellenArray[i].buffer);

return zellenArrayBuffer;
}
};

...

this.set = function(x, y, z, value) {
var offset = berechneOffset(x, y);

zellenArray[berechneZOffset(z)][offset>>3] &=
255 ^ (1 << (offset&7));
zellenArray[berechneZOffset(z)][offset>>3] |=
value << (offset&7);
};

this.get = function(x, y, z) {
var offset = berechneOffset(x, y);

return (zellenArray[berechneZOffset(z)][offset>>3]>>(offset&7))&1;
};

...
// erstellt einen ParallelWorker mit 8 Web-Worker
var worker = new WorkerUtils.ParallelWorker('gol3d-worker.js', 8);

// sendet eine Nachricht an alle Web-Worker
worker.postBroadcastMessage({action:'init',optionen:optionen});

// zur Verarbeitung der restlichen Ebenen
var gameOfLife3dEbene = new GameOfLife3dEbene(optionen);

this.multiversum = [
new Universum(optionen.groesse),
new Universum(optionen.groesse)];
this.multiversum[0].init(optionen.initaleFuellrate);

this.berechneNaechsteGeneration = function(quelle, ziel, callback) {
var runde = 0;

var berechneNaechsteRunde = function() {
for(var z=runde,i=0; i<Math.floor(optionen.groesse/3); z+=3,i++) {
worker.postMessage({
action: 'ebene',
quelle:
[quelle.buffer(z-1),quelle.buffer(z),quelle.buffer(z+1)],
ziel: ziel.buffer(z),
z: z
}, [
quelle.buffer(z-1),
quelle.buffer(z),
quelle.buffer(z+1),
ziel.buffer(z)
]);
}
};

var behandleNachricht = function(event) {
quelle.buffer(event.data.z-1, event.data.quelle[0]);
quelle.buffer(event.data.z, event.data.quelle[1]);
quelle.buffer(event.data.z+1, event.data.quelle[2]);
ziel.buffer(event.data.z, event.data.ziel);
};

var behandleLeereQueue = function(event) {
runde++;

if(runde >= 3) {
worker.removeEventListener('message', behandleNachricht);
worker.removeEventListener('emptyqueue', behandleLeereQueue);

var start = Math.floor(optionen.groesse/3)*3;

for(var z=start; z<optionen.groesse; z++)
gameOfLife3dEbene.berechneNaechsteGeneration(
quelle,
ziel,
z,
function(){});

callback();
} else {
berechneNaechsteRunde();
}
};

worker.addEventListener('message', behandleNachricht);
worker.addEventListener('emptyqueue', behandleLeereQueue);

berechneNaechsteRunde();
}; (jul)