Created
September 2, 2016 16:46
-
-
Save kchapelier/f88199b73e2ea165e0d301b72eda2854 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// all code under the DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE Version 2 | |
// all code written by Carl Olsson (surt) | |
// simple moore neighbourhod | |
var neighbourOffsets = [[-1, -1], [0, -1], [+1, -1], [+1, 0], [+1, +1], [0, +1], [-1, +1], [-1, 0]]; | |
// retrieve the colours of all the pixels in moore neighbourhood of the given coordinates | |
function getNeighbours(data, x, y) { | |
var neighbours = new Array(); | |
for(var i = 0; i < 8; i++) { | |
try { | |
neighbours[i] = getPixel(data, x + neighbourOffsets[i][0], y + neighbourOffsets[i][1]); | |
} | |
catch (e) {} | |
} | |
return neighbours; | |
} | |
function lerp(start, end, pos) { | |
return start + (end - start) * pos; | |
} | |
function bias(bias, value) { | |
// y(x) = x^(log(B)/log(0.5)) | |
return Math.pow(value, Math.log(bias) / Math.log(0.5)); | |
} | |
function gain(gain, value) { | |
// y(x) = Bias(2*x, 1-G)/2 if x<0.5 | |
// 1-Bias(2-2*x, 1-G)/2 if x>0.5 | |
return value <= 0.5 ? bias(2 * value, 1 - gain) / 2 : 1 - bias(2 - 2 * value, 1 - gain) / 2; | |
} | |
// this method is the meat of the generation, the rest of the code is mostly UI initialization, utility functions, color palettes and scaling functions | |
SpriteGenerator.prototype.generateTile = function() { | |
// create data holder a single tile | |
var tile = this.context.createImageData(values["size"], values["size"]); | |
// define whether we have to apply a mirror Horizontally and/or vertically | |
var doMirrorH = (random() <= values["mirrorh"]); | |
var doMirrorV = (random() <= values["mirrorv"]); | |
var xRange = tile.width / 2; | |
var yRange = tile.height / 2; | |
// limit the actual tile generation to only half or a quarter of a tile if mirrors are to be applied (generation range) | |
var xLimit = ( doMirrorH ? Math.ceil(xRange) : tile.width); | |
var yLimit = ( doMirrorV ? Math.ceil(yRange) : tile.height); | |
// generate a color palette by randomly picks colors from the user selected palette | |
var spritePal = []; | |
for (var i = 0; i < values["colours"]; i++) { | |
spritePal[i] = pickRandom(palettes[values["pal"]].colours); | |
} | |
for(var y = 0; y < yLimit; y++) { | |
for(var x = 0; x < xLimit; x++) { | |
// for each single pixel in the generation range | |
// calculate some kind of distorted distance (seems to vary wildly when mirrors are applied, that's probably not intended ?) | |
var falloffX = falloffs[values["falloff"]].func(1.0 - Math.abs((xRange - (x + 0.5)) / xRange)); | |
var falloffY = falloffs[values["falloff"]].func(1.0 - Math.abs((yRange - (y + 0.5)) / yRange)); | |
// calculate the probability of having a sprite pixel instead of a background here here | |
// do a linear interpolation between the user provided minimum probabily and maximum probality with a ratio calculated from the previous distance | |
var prob = lerp(values["probmin"], values["probmax"], bias(values["bias"], gain(values["gain"], falloffX * falloffY))); | |
// decide whether the current pixel is a background pixel or if it is a sprite pixel | |
// if it is a sprite pixel pick a random colour from the generated palette | |
var rand = random(); | |
if(rand <= prob) | |
putPixel(tile, x, y, rgbToRgba(pickRandom(spritePal))); | |
else | |
putPixel(tile, x, y, backgroundColour); | |
} | |
} | |
// the generation range is now filled with sprite pixels and background pixels | |
// post processing | |
// some kind of stochastic cellular automation to smoothen the sprite and limit the number of 'rogue' single pixels | |
for(var y = 0; y < yLimit; y++) { | |
for(var x = 0; x < xLimit; x++) { | |
// for each single pixel in the generation range | |
// count how many of their neighbours (moore neighbourhood) is filled (not a background pixel) | |
var neighbours = getNeighbours(tile, x, y); | |
var count = 0; | |
for(var i = 0; i < neighbours.length; i++) { | |
if(neighbours[i] != null && !coloursEqual(backgroundColour, neighbours[i])) | |
count++; | |
} | |
// if there is only one filled/alive neighboor, randomly check if it has to be replaced by a background pixel, using a user provided probabilty (values["despur"]) | |
if(count == 1) { | |
// despur | |
if(random() <= values["despur"]) { | |
putPixel(tile, x, y, backgroundColour); | |
} | |
// if there is no filled/alive neighboor, randomly check if it has to be replaced by a background pixel, using another user provided probabilty (values["despeckle"]) | |
} else if(count == 0) { | |
// despeckle | |
if(random() <= values["despeckle"]) { | |
putPixel(tile, x, y, backgroundColour); | |
} | |
} | |
} | |
} | |
// that's about it | |
// copy mirrors, if mirrors are to be applied => mirror the generated content to the empty quarters of the tile | |
xLimit = ( doMirrorH ? Math.floor(xRange + 0.5) : tile.width); | |
yLimit = ( doMirrorV ? Math.floor(yRange + 0.5) : tile.height); | |
for(var y = 0; y < yLimit; y++) { | |
for(var x = 0; x < xLimit; x++) { | |
var colour = getPixel(tile, x, y); | |
if(doMirrorH) | |
putPixel(tile, tile.width - 1 - x, y, colour); | |
if(doMirrorV) | |
putPixel(tile, x, tile.height - 1 - y, colour); | |
if(doMirrorH && doMirrorV) | |
putPixel(tile, tile.width - 1 - x, tile.height - 1 - y, colour); | |
} | |
} | |
var out = tile; | |
// some scaling with user selected function, not part of the actual tile generation | |
/* | |
if (values["scaler0"] != "none") { | |
var scaler = scalers[values["scaler0"]]; | |
var scaled = this.context.createImageData(out.width * scaler.factor, out.height * scaler.factor); | |
scale(out, scaled, scaler); | |
out = scaled; | |
} | |
if (values["scaler1"] != "none") { | |
var scaler = scalers[values["scaler1"]]; | |
var scaled = this.context.createImageData(out.width * scaler.factor, out.height * scaler.factor); | |
scale(out, scaled, scaler); | |
out = scaled; | |
} | |
*/ | |
return out; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment