xxxxxxxxxx
304
// Dynamic ID Design
// City Renewal Authority
// Jack Connell and Tilda Blackbourn-Rooney
//
// Generates a customisable set connected tiles.
// Note the code does not work on safari version
// 14.1.2
//
// Controls
// Left click: randomise type of selected tile
// Right click: remove tile
// c: randomise colour of selected tile
// t: create a text box
// s: save image
// Variations
let numTiles = 5; // tiles per row or column
let numPixels = 500; // pixels per row or column
// Global variables
let lineWidth = (7 * 5 * numPixels) / 500 / numTiles;
let buffer = lineWidth;
let tiles = [];
let angle = 0;
let tileSize;
let bold;
let regular;
let img;
let typeChances = [5, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 5]; // chances of each type of tile
let colors = [];
let backClr;
function setup() {
img = createCanvas(numPixels, numPixels);
colorMode(RGB);
angleMode(DEGREES);
strokeCap(SQUARE);
backClr = color(255);
tileSize = width / numTiles;
generateColors();
// generate every type of connection
types = [];
for (let n = 0b0000; n <= 0b1111; n++) {
// create an object containing connections based on binary value representing NSEW, where 1 is a connection
let type = {
N: n >> 3,
S: (0b0100 & n) >> 2,
E: (0b0010 & n) >> 1,
W: 0b0001 & n,
};
// increase frequency of higher chance tiles
let chance = typeChances[n];
for (c = 0; c < chance; c++) {
types.push(type);
}
}
// Pregenerate 2D tile array
for (let c = 0; c < numTiles; c++) {
tiles[c] = [];
}
// Fill tiles array with empty tiles
for (let i = 0; i < numTiles; i++) {
for (let j = 0; j < numTiles; j++) {
let type = {
N: 2,
S: 2,
E: 2,
W: 2,
};
tiles[i][j] = new tile(i, j, type, random(colors), random(colors));
}
}
// Make random tiles
for (let i = 0; i < numTiles; i++) {
for (let j = 0; j < numTiles; j++) {
fitTile(i, j, random(colors), random(colors));
}
}
}
function generateColors() {
let colorChances = [
[color(2, 64, 77), 6],
[color(29, 150, 151), 1],
[color(134, 180, 134), 2],
[color(240, 56, 116), 1],
[color(255, 175, 106), 1],
]; // array of each colour and respective frequency
for (let i = 0; i < colorChances.length; i++) {
let clr = colorChances[i][0];
let chance = colorChances[i][1];
for (let c = 0; c < chance; c++) {
colors.push(clr);
}
}
}
function fitTile(x, y, clr, fill) {
/// Decide the tile type based on the connections to the neighbours
let candidates = getCandidates(x, y);
// Chose randomly from tiles that can fit
let type = random(candidates);
let typeNum = getTypeNum(type);
// add tile to tiles
let currentClass = classArray[typeNum];
tiles[x][y] = new currentClass(x, y, type, clr, fill);
}
function getCandidates(x, y) {
// Deep copy of connection types so the array can be modified
let candidates = structuredClone(types);
let N, S, E, W;
// Get the connections based on the neighbours
if (y - 1 >= 0) {
N = tiles[x][y - 1].type.S;
} else N = 2;
if (y + 1 < numTiles) {
S = tiles[x][y + 1].type.N;
} else S = 2;
if (x + 1 < numTiles) {
E = tiles[x + 1][y].type.W;
} else E = 2;
if (x - 1 >= 0) {
W = tiles[x - 1][y].type.E;
} else W = 2;
// Loop over every candidate and decide if it could not fit
let k = 0;
while (k < candidates.length) {
let cand = candidates[k];
// flip the candidates (0 <--> 1)
let flipCand = {
N: -(cand.N - 1),
S: -(cand.S - 1),
E: -(cand.E - 1),
W: -(cand.W - 1),
};
// remove candidate if it doesn't fit there
if (
flipCand.N == N ||
flipCand.S == S ||
flipCand.E == E ||
flipCand.W == W
) {
candidates.splice(k, 1);
} else k += 1;
}
return candidates;
}
function draw() {
background(backClr);
// draw tiles
for (let i = 0; i < numTiles; i++) {
for (let j = 0; j < numTiles; j++) {
let t = tiles[i][j];
if (!isBlank(t.type)) {
push();
t.trans();
t.show();
pop();
}
}
}
}
function mousePressed() {
let mX = floor(mouseX / tileSize);
let mY = floor(mouseY / tileSize);
if (mouseButton == LEFT && inCanvas(mX, mY)) {
leftMouse(mX, mY);
} else if (mouseButton == RIGHT && inCanvas(mX, mY)) {
// "delete" tile by placing blank tile
let type = {
N: 2,
S: 2,
E: 2,
W: 2,
};
tiles[mX][mY] = new tile(mX, mY, type, random(colors), random(colors));
}
}
function leftMouse(mX, mY) {
let oldType = tiles[mX][mY].type;
// Randomise the selected tile
let type = random(types);
let typeNum = getTypeNum(type);
let currentClass = classArray[typeNum];
let clr = tiles[mX][mY].clr;
let fill = tiles[mX][mY].fill;
let tile = new currentClass(mX, mY, type, clr, fill);
tiles[mX][mY] = tile;
// Update the neighbours to fit the new tile
// Leaves existing colours
// Leaves whole tile if the connection doesnt change
updateNeighbour(mX - 1, mY, oldType.W, type.W);
updateNeighbour(mX + 1, mY, oldType.E, type.E);
updateNeighbour(mX, mY - 1, oldType.N, type.N);
updateNeighbour(mX, mY + 1, oldType.S, type.S);
}
function updateNeighbour(x, y, oldConnection, newConnection) {
let isDifferent = oldConnection != newConnection;
// change the tile only if needed. ignore blank tiles and text boxes
if (inCanvas(x, y) && isDifferent) {
let t = tiles[x][y];
let isTextBlock = t.tb;
if (!isBlank(t.type) && !isTextBlock) {
fitTile(x, y, t.clr, t.fill);
}
}
}
function keyPressed() {
let mX = floor(mouseX / tileSize);
let mY = floor(mouseY / tileSize);
switch (key) {
case "s":
save(img, "CRA.jpg");
print("Image saved");
break;
case "t":
if (inCanvas(mX, mY)) {
let t = tiles[mX][mY];
tiles[mX][mY] = new textBlock(mX, mY, t.type, t.clr, t.fill);
}
break;
case "c":
if (inCanvas(mX, mY)) {
let p = tiles[mX][mY];
// leave blank tiles and text blocks
if (!isBlank(p.type) && !p.tb) {
randomiseColor(mX, mY);
}
}
break;
}
}
function randomiseColor(mX, mY) {
let t = tiles[mX][mY];
let clr = random(colors);
let fill = random(colors);
// create array of random integers
let arr = [];
for (let i = 0; i < colors.length; i++) {
arr[i] = i;
}
arr = shuffle(arr);
// Loop over random array and change colour
for (let i = 0; i < arr.length; i++) {
// Primary colour must change
if (clr != t.clr) {
break;
}
// if colour unchanged, change colour
if (clr == t.clr) {
clr = colors[i];
}
if (fill == t.fill) {
fill = colors[i];
}
}
t.clr = clr;
t.fill = fill;
}
function isBlank(type) {
return type.N == 2 && type.S == 2 && type.E == 2 && type.W == 2;
}
function inCanvas(x, y) {
return x >= 0 && x < tiles.length && y >= 0 && y < tiles.length;
}
function getTypeNum(type) {
return 8 * type.N + 4 * type.S + 2 * type.E + 1 * type.W;
}