xxxxxxxxxx
468
// const VARIATIONS_MAX = 1;
// const VARIATIONS_MIN = 1;
// const PAUSE = 100;
// const SAVE = true;
// let WIDTH = 1024 * 2;
// let HEIGHT = 1024 * 2;
const VARIATIONS_MAX = 10;
const VARIATIONS_MIN = 3;
const PAUSE = 100;
const SAVE = false;
let WIDTH = 512;
let HEIGHT = 512;
const ZOOM_FACTOR = 0.85;
let WIDTH_TEX, HEIGHT_TEX, COUNT, VARIATIONS,
SCALAR, OFFSET_X, OFFSET_Y,
TARGET_REGIONS, WEIGHTS, SORT_WEIGHTS,
IN_REGION_COLOR_FACTOR, IN_REGION_SHAPE, IN_REGION_SHAPE_DIST,
IN_REGION_DIRS, IN_REGION_ITS_PLUS, IN_REGION_MAX_LENGTH,
IN_REGION_LERP_POW, IN_REGION_OUTLINE_REGIONS, IN_REGION_OUTLINE_REGIONS_FLIP, IN_REGION_SORT_BLEND,
OUT_REGION_COLOR_FACTOR, OUT_REGION_SHAPE, OUT_REGION_SHAPE_DIST,
OUT_REGION_DIRS, OUT_REGION_ITS_PLUS, OUT_REGION_MAX_LENGTH,
OUT_REGION_LERP_POW, OUT_REGION_OUTLINE_REGIONS, OUT_REGION_OUTLINE_REGIONS_FLIP, OUT_REGION_SORT_BLEND,
STEP_IT;
function setup() {
createCanvas(200, 200);
restart();
}
function windowResized() {
resizeCanvas(windowWidth, windowHeight);
background(0);
WIDTH = WIDTH || windowWidth;
HEIGHT = HEIGHT || windowHeight;
SCALAR = min(width / WIDTH, height / HEIGHT) * ZOOM_FACTOR;
OFFSET_X = SCALAR * (width / SCALAR - WIDTH) * 0.5;
OFFSET_Y = SCALAR * (height / SCALAR - HEIGHT) * 0.5;
}
function restart() {
windowResized();
fill("black");
noStroke();
noSmooth();
background(0);
STEP_IT = step();
}
function toggleFullscreen() {
fullscreen(!fullscreen());
if (fullscreen())
cursor()
else
noCursor();
}
function keyPressed() {
if (key === 'f')
toggleFullscreen()
if (key === 'r')
restart()
}
let textures;
function getGraphics(w, h, index) {
if (textures == null)
textures = new Map();
const id = `${w}x${h}`;
let size_textures = textures.get(id);
if (size_textures == null) {
size_textures = [];
textures.set(id, size_textures);
}
let tex = size_textures[index];
if (tex == null) {
tex = createGraphics(w, h);
tex.pixelDensity(1);
size_textures[index] = tex;
}
return tex;
}
function* step() {
background(0);
WIDTH_TEX = WIDTH;
HEIGHT_TEX = HEIGHT;
// COUNT = COUNT_OFFSET;
COUNT = 0;
while(WIDTH_TEX > 1 && HEIGHT_TEX > 1) {
WIDTH_TEX /= 2;
HEIGHT_TEX /= 2;
COUNT++;
}
let tex = getGraphics(ceil(WIDTH_TEX), ceil(HEIGHT_TEX));
tex.background(128);
TARGET_REGIONS = ceil(pow(random(), 2) * 100);
WEIGHTS = [random(-1, 1), random(-1, 1), random(-1, 1)];
SORT_WEIGHTS = [random(-1, 1), random(-1, 1), random(-1, 1)];
SORT_WEIGHTS = random([SORT_WEIGHTS, WEIGHTS]);
const in_region_dirs_count = ceil(random(0, 8));
const in_offset_range = random(1, 4);
IN_REGION_DIRS = generateRegionDirs(in_region_dirs_count, in_offset_range)
IN_REGION_ITS_PLUS = random(in_region_dirs_count);
IN_REGION_MAX_LENGTH = ceil(pow(random(), 1) * WIDTH * HEIGHT);
IN_REGION_SHAPE = random(2);
IN_REGION_SHAPE_DIST = random(2);
IN_REGION_LERP_POW = random() * 2;
IN_REGION_OUTLINE_REGIONS = ceil(pow(random(), 1) * 1000);
IN_REGION_OUTLINE_REGIONS_FLIP = random([true, false]);
IN_REGION_SORT_BLEND = random([0, pow(random(), 2)]);
IN_REGION_COLOR_FACTOR = random();
const out_region_dirs_count = ceil(random(1, 4));
const out_offset_range = 1;
OUT_REGION_DIRS = generateRegionDirs(out_region_dirs_count, out_offset_range)
OUT_REGION_ITS_PLUS = random(out_region_dirs_count);
OUT_REGION_MAX_LENGTH = ceil(pow(random(), 2) * WIDTH * HEIGHT / 25);
OUT_REGION_SHAPE = random(2);
OUT_REGION_SHAPE_DIST = random(2);
OUT_REGION_LERP_POW = random() * 2;
OUT_REGION_OUTLINE_REGIONS = ceil(pow(random(), 1) * 1000);
OUT_REGION_OUTLINE_REGIONS_FLIP = random([true, false]);
OUT_REGION_SORT_BLEND = random([0, pow(random(), 2)]);
// OUT_REGION_COLOR_FACTOR = random();
OUT_REGION_COLOR_FACTOR = 1 - IN_REGION_COLOR_FACTOR;
// OUT_REGION_DIRS = random([OUT_REGION_DIRS, IN_REGION_DIRS])
// OUT_REGION_ITS_PLUS = random([OUT_REGION_ITS_PLUS, IN_REGION_ITS_PLUS])
OUT_REGION_MAX_LENGTH =random([OUT_REGION_MAX_LENGTH, IN_REGION_MAX_LENGTH])
OUT_REGION_SHAPE = random([OUT_REGION_SHAPE, IN_REGION_SHAPE])
OUT_REGION_SHAPE_DIST =random([OUT_REGION_SHAPE_DIST, IN_REGION_SHAPE_DIST])
OUT_REGION_LERP_POW =random([OUT_REGION_LERP_POW, IN_REGION_LERP_POW])
OUT_REGION_OUTLINE_REGIONS =random([OUT_REGION_OUTLINE_REGIONS, IN_REGION_OUTLINE_REGIONS])
OUT_REGION_OUTLINE_REGIONS_FLIP = random([OUT_REGION_OUTLINE_REGIONS_FLIP, IN_REGION_OUTLINE_REGIONS_FLIP])
OUT_REGION_SORT_BLEND = random([OUT_REGION_SORT_BLEND, IN_REGION_SORT_BLEND])
for (let i=0; i<COUNT; i++) {
const f = i / COUNT;
const ff = COUNT == 1 ? 0 : i / (COUNT - 1);
const cols = round(lerp(VARIATIONS_MAX, VARIATIONS_MIN, ff));
VARIATIONS = cols ** 2
WIDTH_TEX *= 2;
HEIGHT_TEX *= 2;
const w = SCALAR * WIDTH / sqrt(VARIATIONS);
const h = SCALAR * HEIGHT / sqrt(VARIATIONS);
const range = 1 - f;
const variations = [];
const variationIt = generateVariations(tex, range)
for (const variation of variationIt) {
const i = variations.length;
const x = (i % cols) * w + OFFSET_X
const y = floor(i / cols) * h + OFFSET_Y;
variation.x = x;
variation.y = y;
rect(x - 1, y - 1, w + 1, h + 1);
image(variation.tex, x, y, w - 1, h - 1);
variations.push(variation)
yield true;
}
for (const variation of variations) {
const { x, y } = variation;
variation.tex = renderRegions(variation.tex, variation.regions, f)
image(variation.tex, x, y, w - 1, h - 1);
yield true;
}
for (let j = 0; j<PAUSE && !mouseIsPressed; j++)
yield true;
const best_score = max(variations.map(v => v.score))
random(variations.filter(v => v.score == best_score)).score += 1;
variations.sort((v1, v2) => v1.score - v2.score);
const best_variation = variations.pop();
tex = best_variation.tex;
for (let [variation_index, variation] of variations.entries()) {
rect(variation.x-1, variation.y-1, w + 1, h + 1);
if (i == COUNT - 1) {
const f = variation_index / variations.length;
const wait = lerp(variations.length * 0.1, variations.length, f)
* (PAUSE / variations.length);
for (let j = 0; j<wait && !mouseIsPressed; j++)
yield true;
}
yield true;
};
for (let j = 0; j<PAUSE && !mouseIsPressed; j++)
yield true;
background(i < COUNT -1 ? 0 : 255);
rect(OFFSET_X - 1, OFFSET_Y - 1, SCALAR * WIDTH + 2, SCALAR * HEIGHT + 2);
image(tex, OFFSET_X, OFFSET_Y, SCALAR * WIDTH, SCALAR * HEIGHT);
for (let j = 0; j<PAUSE && !mouseIsPressed; j++)
yield true;
}
for (let j = 0; j<PAUSE * 10 && !mouseIsPressed; j++)
yield true;
if (SAVE)
tex.save();
}
function generateRegionDirs(region_dirs_count, offset_range) {
const res = [];
while(res.length < region_dirs_count) {
const offset = randXYoffset(offset_range);
if (!res.some(r => r.x == offset.x && r.y == offset.y))
res.push(offset);
}
return res;
}
function randXYoffset(range) {
let res;
do {
res = {
x: round(random(-range, range)),
y: round(random(-range, range))
}
} while(res.x == 0 && res.y == 0)
return res;
}
function* generateVariations(tex, range) {
for (let j=0; j<VARIATIONS; j++) {
let variation = generate(tex, range, j);
const regions = generateRegions(variation);
regions.sort((r1, r2) => r2.length - r1.length);
const index = TARGET_REGIONS - 1;
const score = regions.length > 1
? regions[min(regions.length - 1, index)].length
: 0
yield { tex: variation, regions, score }
}
}
function generate(tex, range, index) {
const new_tex = getGraphics(ceil(WIDTH_TEX), ceil(HEIGHT_TEX), index);
new_tex.image(tex, 0, 0, new_tex.width, new_tex.height);
new_tex.loadPixels();
const _range = range * 256;
for (let x=0; x<new_tex.width; x++)
for (let y=0; y<new_tex.height; y++)
for (let i=0; i<3; i++) {
const index = (x + y * new_tex.width) * 4 + i
new_tex.pixels[index] += (random() * 2 - 1) * _range
}
new_tex.updatePixels();
return new_tex
}
function generateRegions(tex) {
let grid = [];
for (let x=0; x<tex.width; x++) {
grid[x] = [];
for (let y=0; y<tex.height; y++) {
const p = color(tex.get(x, y));
const inRegion =
p.levels[0] * WEIGHTS[0]
+ p.levels[1] * WEIGHTS[1]
+ p.levels[2] * WEIGHTS[2]
>=
128 * WEIGHTS[0]
+ 128 * WEIGHTS[1]
+ 128 * WEIGHTS[2];
grid[x][y] = { p, x, y, visited: false, inRegion };
}
}
let pixs = [];
for (let x=0; x<tex.width; x++)
for (let y=0; y<tex.height; y++)
pixs.push({x, y});
shuffle(pixs, true);
const regions = [];
pixs.forEach(({x, y}) => {
if (!grid[x][y].visited)
regions.push(fillRegion(grid, x, y));
});
return regions;
}
function fillRegion(grid, x, y) {
const start_item = grid[x][y];
const inRegion = start_item.inRegion;
const REGION_DIRS = inRegion ? IN_REGION_DIRS : OUT_REGION_DIRS;
const REGION_ITS_PLUS = inRegion ? IN_REGION_ITS_PLUS : OUT_REGION_ITS_PLUS;
const REGION_MAX_LENGTH = inRegion ? IN_REGION_MAX_LENGTH : OUT_REGION_MAX_LENGTH;
const REGION_SHAPE = inRegion ? IN_REGION_SHAPE : OUT_REGION_SHAPE;
const REGION_SHAPE_DIST = inRegion ? IN_REGION_SHAPE_DIST : OUT_REGION_SHAPE_DIST;
const w = grid.length;
const h = grid[0].length;
const open = [start_item];
let region = []
let its = 0;
while (open.length) {
const item = open.pop();
const {x, y} = item;
item.visited = true;
region.push(item);
if (region.length >= REGION_MAX_LENGTH)
break;
const checkItem = (new_item) => {
if (new_item.inRegion != inRegion) {
item.edge = true;
return;
}
const dist1 = sqrt(pow(new_item.x - start_item.x, 2) + pow(new_item.y - start_item.y, 2));
const dist2 = (abs(new_item.x - start_item.x) + abs(new_item.y - start_item.y));
const dist3 = max(abs(new_item.x - start_item.x), abs(new_item.y - start_item.y));
const dist = REGION_SHAPE < 1
? lerp(dist1, dist2, REGION_SHAPE)
: lerp(dist2, dist3, REGION_SHAPE - 1);
if (dist > REGION_SHAPE_DIST * grid.length)
return;
if (!new_item.visited && new_item.inRegion == inRegion) {
new_item.visited = true;
open.push(new_item);
}
}
for (let i=0; i<REGION_DIRS.length; i++) {
const dir = REGION_DIRS[floor(i + its) % REGION_DIRS.length];
const rx = x + dir.x;
const ry = y + dir.y;
if (rx >= 0 && rx < w
&& ry >= 0 && ry < h)
checkItem(grid[rx][ry]);
};
its += REGION_ITS_PLUS;
}
return region;
}
function renderRegions(tex, regions, f) {
const count = tex.width * tex.height;
let i=0;
tex.loadPixels();
for (const region of regions) {
const COLOR_FACTOR = region[0].inRegion ? IN_REGION_COLOR_FACTOR : OUT_REGION_COLOR_FACTOR
const OUTLINE_REGIONS = region[0].inRegion ? IN_REGION_OUTLINE_REGIONS : OUT_REGION_OUTLINE_REGIONS
const OUTLINE_REGIONS_FLIP = region[0].inRegion ? IN_REGION_OUTLINE_REGIONS_FLIP : OUT_REGION_OUTLINE_REGIONS_FLIP
const LERP_POW = region[0].inRegion ? IN_REGION_LERP_POW : OUT_REGION_LERP_POW
const SORT_BLEND = region[0].inRegion ? IN_REGION_SORT_BLEND : OUT_REGION_SORT_BLEND
const sorted_region = [region].sort((i1, i2) =>
(i1.p.levels[0] * SORT_WEIGHTS[0]
+ i1.p.levels[1] * SORT_WEIGHTS[1]
+ i1.p.levels[2] * SORT_WEIGHTS[2])
-
(i2.p.levels[0] * SORT_WEIGHTS[0]
+ i2.p.levels[1] * SORT_WEIGHTS[1]
+ i2.p.levels[2] * SORT_WEIGHTS[2])
);
const region_color = sorted_region[
floor((sorted_region.length - 1) * COLOR_FACTOR)
].p
const outline = OUTLINE_REGIONS_FLIP
? i < OUTLINE_REGIONS
: i > OUTLINE_REGIONS
const outline_lerp = pow(
abs(sorted_region.length - count / TARGET_REGIONS) / count,
LERP_POW
);
const outline_shade = sorted_region[0].inRegion
? sorted_region.at(-1).p
: sorted_region[0].p
const outline_color = lerpColor(region_color, outline_shade, outline_lerp)
region.forEach((item, i) => {
const sorted_region_color = SORT_BLEND > 0
? lerpColor(region_color, sorted_region[i].p, SORT_BLEND)
: region_color;
const clr = item.edge && outline ? outline_color : sorted_region_color;
// tex.set(item.x, item.y, clr);
for (let j=0; j<3; j++) {
const pos_index = (item.x + item.y * tex.width) * 4 + j;
tex.pixels[pos_index] = clr.levels[j];
}
});
i++;
};
tex.updatePixels();
return tex;
}
function draw() {
if (STEP_IT.next().done)
restart();
}