xxxxxxxxxx
340
let cols, rows, grid;
let size =25;
let mouseHold = false;
let startActive = 0;
let paddingX = 0;
let paddingY = 0;
let startX, startY;
let capture;
let facing = "environment";
let threshold = 50;
//these two function check if the value is 0 or 1, they're used to check
//active objects through the direction and cardinal array
const isOne = (currentValue) => currentValue == 1;
const isZero = (currentValue) => currentValue == 0;
//this function create the data structure for the 2D array.
//2D Array is needed to have x and y indexes for each cell
function make2DArray(cols, rows) {
let arr = new Array(cols);
for (let i = 0; i < arr.length; i++) {
arr[i] = new Array(rows);
}
return arr;
}
function updateCapture() {
capture = createCapture(VIDEO, {
audio: false,
video: {
facingMode: facing,
},
flipped: false,
});
capture.size(width, height);
capture.hide();
}
//this function is a shorthand for calling class methods instead of a for loop or a foreach
function executeMethod(grid, methodName, optionalArgument = 0) {
grid.forEach((row) =>
row.forEach((element) => element[methodName](optionalArgument))
);
}
function mouseWheel() {
if (event.delta > 0) {
threshold = max(threshold-1,0)
} else {
threshold = min(threshold+1,100)
}
}
function setup() {
createCanvas(windowWidth, windowHeight);
cols = Math.ceil(width / size) + 1;
rows = Math.ceil(height / size) + 1;
grid = make2DArray(cols, rows);
for (let i = 0; i < cols; i++) {
for (let j = 0; j < rows; j++) {
grid[i][j] = new Tile(i, j, size);
}
}
updateCapture();
}
function keyPressed() {
if (key == "i") {
executeMethod(grid, "invert");
}
}
function draw() {
frameRate(15);
background(255, 255, 0);
capture.loadPixels();
executeMethod(grid, "show", grid);
executeMethod(grid, "getColor", capture.pixels);
}
class Tile {
constructor(x, y, size) {
this.x = x;
this.y = y;
this.size = size;
this.active = startActive;
this.sum = 0;
this.col = color(0, 0, 0);
this.x1 = this.x * this.size;
this.y1 = this.y * this.size;
}
getColor(array) {
let r = 0;
let g = 0;
let b = 0;
let a = 0;
let counter = 0;
for (let i = 0; i < cols; i++) {
for (let j = 0; j < rows; j++) {
let index = 4 * ((this.y1 + j) * capture.width + (this.x1 + i));
r += array[index];
g += array[index + 1];
b += array[index + 2];
a += array[index + 3];
counter++;
}
}
if (r && g && b && a) {
this.col = color(r / counter, g / counter, b / counter, a / counter);
} else {
this.col = color(0, 0, 0, 0);
}
}
//this is the main function of the class, it determines what the object is drawing
show(grid) {
let b = brightness(this.col);
//these variables are all the possibile neighbors of the object inside the 2d array
let up = grid[this.x][max(this.y - 1, 0)];
let down = grid[this.x][min(this.y + 1, rows - 1)];
let left = grid[max(this.x - 1, 0)][this.y];
let right = grid[min(this.x + 1, cols - 1)][this.y];
let upRight = grid[min(this.x + 1, cols - 1)][max(this.y - 1, 0)];
let upLeft = grid[max(this.x - 1, 0)][max(this.y - 1, 0)];
let downRight = grid[min(this.x + 1, cols - 1)][min(this.y + 1, rows - 1)];
let downLeft = grid[max(this.x - 1, 0)][min(this.y + 1, rows - 1)];
// i've created 2 array: one that contain every neighbor and one for the cardinal neighbord
let directions = [
up.active,
down.active,
left.active,
right.active,
upRight.active,
upLeft.active,
downRight.active,
downLeft.active,
];
let cardinal = [up.active, down.active, right.active, left.active];
if (b < threshold) {
this.active = 0;
} else {
this.active = 1;
}
//this conditional statement is used for adding or removing fill if the
//object is active or not, it also add a mouseover effect
noStroke();
if (this.active == 0) {
noFill();
} else {
if (this.mouseOver()) {
fill(255, 0, 255);
} else {
fill(0);
}
}
//these are the conditional statements for checking what cells are
//active and each condition lead to a specific function that contains
//the drawing
rectMode(CENTER);
if (directions.every(isOne)) {
this.internal();
} else if (up.active == 1 && cardinal.toSpliced(0, 1).every(isZero)) {
this.terminalDown();
} else if (down.active == 1 && cardinal.toSpliced(1, 1).every(isZero)) {
this.terminalUp();
} else if (right.active == 1 && cardinal.toSpliced(2, 1).every(isZero)) {
this.terminalLeft();
} else if (left.active == 1 && cardinal.toSpliced(3, 1).every(isZero)) {
this.terminalRight();
} else if (
(up.active == 1 && down.active == 1) ||
(left.active == 1 && right.active == 1)
) {
this.betweenBlocks();
} else if (up.active == 1 && left.active == 1) {
this.cornerDownRight();
} else if (up.active == 1 && right.active == 1) {
this.cornerDownLeft();
} else if (down.active == 1 && right.active == 1) {
this.cornerUpLeft();
} else if (down.active == 1 && left.active == 1) {
this.cornerUpRight();
} else {
this.isolated();
}
}
//Shapes
//each of these function contains the drawing for the combination of
//active neighbors
isolated() {
rect(this.x1, this.y1, this.size, this.size);
}
internal() {
circle(this.x1, this.y1, this.size);
}
betweenBlocks() {
push();
beginClip({ invert: true });
circle(this.x1, this.y1, this.size / 2);
endClip();
rect(this.x1, this.y1, this.size, this.size);
pop();
}
terminalLeft() {
push();
beginClip({ invert: true });
circle(this.x1 - this.size / 2, this.y1, this.size / 2);
endClip();
rect(this.x1, this.y1, this.size, this.size);
pop();
}
terminalRight() {
push();
beginClip({ invert: true });
circle(this.x1 + this.size / 2, this.y1, this.size / 2);
endClip();
rect(this.x1, this.y1, this.size, this.size);
pop();
}
terminalUp() {
push();
beginClip({ invert: true });
circle(this.x1, this.y1 - this.size / 2, this.size / 2);
endClip();
rect(this.x1, this.y1, this.size, this.size);
pop();
}
terminalDown() {
push();
beginClip({ invert: true });
circle(this.x1, this.y1 + this.size / 2, this.size / 2);
endClip();
rect(this.x1, this.y1, this.size, this.size);
pop();
}
cornerUpLeft() {
triangle(
this.x1 + this.size / 2,
this.y1 - this.size / 2,
this.x1 + this.size / 2,
this.y1 + this.size / 2,
this.x1 - this.size / 2,
this.y1 + this.size / 2
);
}
cornerUpRight() {
triangle(
this.x1 - this.size / 2,
this.y1 - this.size / 2,
this.x1 - this.size / 2,
this.y1 + this.size / 2,
this.x1 + this.size / 2,
this.y1 + this.size / 2
);
}
cornerDownLeft() {
triangle(
this.x1 - this.size / 2,
this.y1 - this.size / 2,
this.x1 + this.size / 2,
this.y1 - this.size / 2,
this.x1 + this.size / 2,
this.y1 + this.size / 2
);
}
cornerDownRight() {
triangle(
this.x1 - this.size / 2,
this.y1 - this.size / 2,
this.x1 + this.size / 2,
this.y1 - this.size / 2,
this.x1 - this.size / 2,
this.y1 + this.size / 2
);
}
//this function se the mouseHold variable to true, it is needed to keep the behaviour of drawing constant so if mouse is clicked on a active cell it starts to deactivate, if instead mouse is clicked on a non-active cell then it start to activate
changeState() {
if (this.mouseOver()) {
if (this.active == 0) {
mouseHold = true;
} else {
mouseHold = false;
}
}
}
//this is the function that flip the active value
changeTile() {
if (this.mouseOver()) {
if (mouseHold) {
this.active = 1;
} else {
this.active = 0;
}
}
}
//invert the active value of all the objects
invert() {
this.active = abs(this.active - 1);
}
//mouseover function to check if the mouse is hovering the object
mouseOver() {
if (
mouseX > this.x1 - this.size / 2 &&
mouseX < this.x1 + this.size / 2 &&
mouseY > this.y1 - this.size / 2 &&
mouseY < this.y1 + this.size / 2
) {
return true;
} else {
return false;
}
}
}