Skip to content

Instantly share code, notes, and snippets.

@evenam
Created August 27, 2017 02:50
Show Gist options
  • Save evenam/7c10eb00646fcc35cdbe5d3ec05f9a2a to your computer and use it in GitHub Desktop.
Save evenam/7c10eb00646fcc35cdbe5d3ec05f9a2a to your computer and use it in GitHub Desktop.
const Jimp = require('Jimp');
const cluster = require('cluster');
// png generator
const writeImage = (fname, data, next) => {
let width = 0;
let height = 0;
if (data instanceof Array) {
height = data.length;
if (data[0] instanceof Array)
width = data[0].length;
}
var img = {
data: data,
width: width,
height: height,
fname: fname,
next: next
};
let image = new Jimp(width, height, function(err, image) {
if (err) throw err;
this.data.forEach((row, y) => {
row.forEach((color, x) => {
image.setPixelColor(color, x, y);
});
});
image.write(this.fname, function(err) {
if (err) {
console.log(err);
throw err;
}
this.next();
}.bind(img));
}.bind(img));
}
// color generators
const fmod = (f, d) => {
let result = f;
while (result - d > 0) {
result -= d;
}
return result;
}
const convertToRGB = (h, s, v) => {
let c = s * v;
let x = c * (1 - Math.abs(fmod(h / 60, 2) - 1));
let m = v - c;
let rgb = [0, 0, 0, 1];
if (h < 60) {
rgb[0] = c;
rgb[1] = x;
} else if (h < 120) {
rgb[0] = x;
rgb[1] = c;
} else if (h < 180) {
rgb[1] = c;
rgb[2] = x;
} else if (h < 240) {
rgb[1] = x;
rgb[2] = c;
} else if (h < 300) {
rgb[2] = c;
rgb[0] = x;
} else {
rgb[2] = x;
rgb[0] = c;
}
scaledRGB = rgb.map(elem => ((elem + m) * 255.0) | 0);
let colorValue = scaledRGB.reduce((base, elem) => (base * 256 + elem), 0);
return colorValue;
}
const getColor = iterations => convertToRGB((iterations * 337) % 360, 1, 1);
// mandlebrot code
const lerp = (a, b, r) => (b - a) * r + a;
const getMandlebrotIterations = (px, py) => {
let x = 0.0;
let y = 0.0;
let iteration = 0;
while (x * x + y * y < 2 * 2 && iteration < 10000) {
let xtemp = x * x - y * y + px;
y = 2 * x * y + py;
x = xtemp;
iteration++;
}
return iteration;
}
const generateMandlebrotData = (size, range) => {
data = [];
for (let i = 0; i < size.height; i++) {
let row = [];
let py = lerp(range[1], range[3], (i + 1.0) / size.height);
for (let j = 0; j < size.width; j++) {
let px = lerp(range[0], range[2], (j + 1.0) / size.width);
let iterations = getMandlebrotIterations(px, py);
let color = getColor(iterations);
row.push(color);
}
data.push(row);
}
return data;
}
if (cluster.isMaster) {
let size = {
width: 400,
height: 400
};
let area = {
minx: -2.5,
maxx: 1.0,
miny: -1.0,
maxy: 1.0
};
if (process.argv.length === 6) {
area.minx = Number(process.argv[2]);
area.miny = Number(process.argv[3]);
area.maxx = Number(process.argv[4]);
area.maxy = Number(process.argv[5]);
}
let breakdown = {
rows: 5,
cols: 5
};
let totalData = [];
for (let i = 0; i < breakdown.rows * size.height; i++) {
totalData.push([]);
for (let j = 0; j < breakdown.cols * size.height; j++) {
totalData[i].push(0);
}
}
let workerCount = breakdown.rows * breakdown.cols;
console.log('workers: ', workerCount);
for (let i = 0; i < breakdown.rows; i++) {
for (let j = 0; j < breakdown.cols; j++) {
let sizex = (area.maxx - area.minx) / breakdown.rows;
let sizey = (area.maxy - area.miny) / breakdown.cols;
let range = [sizex * j + area.minx, sizey * i + area.miny, sizex * (j + 1) + area.minx, sizey * (i + 1) + area.miny];
let worker = cluster.fork();
worker.on('message', msg => {
if (msg.type === 'mandlebrot-complete') {
workerCount --;
console.log('worker finished ', msg.index);
let x = msg.x * size.width;
let y = msg.y * size.height;
for (let i = 0; i < size.height; i++) {
for (let j = 0; j < size.width; j++) {
totalData[y + i][x + j] = msg.data[i][j];
}
}
if (workerCount === 0) {
writeImage('multibrot.png', totalData, process.exit);
}
}
});
worker.send({ type: 'mandlebrot-part', size: size, range: range, x: j, y: i, index: i * breakdown.rows + j });
}
}
} else if (cluster.isWorker) {
process.on('message', msg => {
if (msg.type === 'mandlebrot-part') {
console.log('starting worker ', msg.index)
let x = msg.x;
let y = msg.y;
let data = generateMandlebrotData(msg.size, msg.range);
process.send({ type: 'mandlebrot-complete', x: x, y: y, data: data, index: msg.index })
}
})
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment