Clique nos botões para ativar os grids e alterar o número de vizinhanças. Aperte "s" para salvar uma imagem do padrão desejado.
A fork of Digital Doilie by AndréSilva
xxxxxxxxxx
/**
* Crochê Digital 2_0.
* por Marília Bergamo & André Luiz Silva.
* Este código é baseado em um automato celular.
* Versão utilizada como ferramenta na disciplina "tópicos em moda - renda de crochê" Design de Moda - UFMG, 2023.
* Versão P5js.
*/
let e_radius;
let e_steps;
let e_gauge;
let e_counter;
let b_radius;
let circles;
let neighborhoods;
let changeNeighborhoodsButton; // Variável para o botão
let firstCirclePoints = 30; // para definir quantidade de pontos iniciais 30=12 pontos, 60=6 pontos
let circleArr = [];
let img1, img2;
let cross1Visible = false; // Variável para controlar a visibilidade da primeira cruz
let cross2Visible = false; // Variável para controlar a visibilidade da segunda cruz
let circleCounts = [];
let pointOrderList = []; // Lista para armazenar a ordem dos pontos por círculo
function preload() {
img1 = loadImage("pontobaixo.png");
img2 = loadImage("pontoalto.png");
img3 = loadImage("conteudo.png");
img4 = loadImage("logo.png")
img5 = loadImage("receita.png")
img6 = loadImage("legenda1.png")
}
function keyPressed() {
if (key === 's' || key === 'S') {
saveCanvas('doilie', 'png');
}
}
function setup() {
createCanvas(windowWidth, windowHeight);
background(255);
// Configuração da interface
setupUI();
// DEFINIR VALORES
circles = 6; // QUANTIDADE DE CARREIRAS
e_steps =19; // NÃO MUDAR
e_radius = 48; // RAIO DO CÍRCULO INICIAL DE CORRENTINHAS
e_gauge = 52; // DISTÂNCIAS ENTRE CARREIRAS
e_counter = 0; // NÃO MUDAR
frameRate(0.3); // VELOCIDADE EM QUE OS GRÁFICOS APARECEM
drawCircles();
// Crie um botão para ativar apenas a primeira cruz
let rectw = windowWidth / 9;
let recth = windowHeight-40;
// Crie um botão para trocar o número de vizinhanças
let changeNeighborhoodsButton = createButton('Nº de vizinhanças');
changeNeighborhoodsButton.position(rectw/2.5, windowHeight * 0.35 ); // Posição do botão
changeNeighborhoodsButton.mousePressed(changeNeighborhoods); // Função de callback quando o botão é clicado
changeNeighborhoodsButton.style('background-color', 'red');
changeNeighborhoodsButton.style('color', 'white');
changeNeighborhoodsButton.style('text-align', 'center');
changeNeighborhoodsButton.style('text-size', '16px');
changeNeighborhoodsButton.style('border', 'none');
changeNeighborhoodsButton.style('border-radius', '10px'); // Cantos arredondados
changeNeighborhoodsButton.style('padding', '5px 12px'); // Preenchimento interno
let button1 = createButton('Grid 4 vizinhanças');
button1.position(rectw/2.5, windowHeight * 0.41); // Posição do botão
button1.mousePressed(toggleCross1Visibility); // Função de callback quando o botão é clicado
button1.style('background-color', 'red');
button1.style('color', 'white');
button1.style('text-align', 'center');
button1.style('text-size', '16px');
button1.style('border', 'none');
button1.style('border-radius', '10px');
button1.style('padding', '5px 10px'); // Preenchimento interno
// Crie um botão para ativar ambas as cruzes
let button2 = createButton('Grid 8 vizinhanças');
button2.position(rectw/2.5, windowHeight * 0.47); // Posição do botão
button2.mousePressed(activateBothCrosses); // Função de callback quando o botão é clicado
button2.style('background-color', 'red');
button2.style('color', 'white');
button2.style('text-align', 'center');
button2.style('text-size', '16px');
button2.style('border', 'none');
button2.style('border-radius', '10px'); // Cantos arredondados
button2.style('padding', '5px 10px' ); // Preenchimento interno
// Crie um botão para desativar ambas as cruzes
let button3 = createButton('Sem Grid');
button3.position(rectw/2.5, windowHeight * 0.53); // Posição do botão
button3.mousePressed(deactivateCrosses); // Função de callback quando o botão é clicado
button3.style('background-color', 'red');
button3.style('color', 'white');
button3.style('text-align', 'center');
button3.style('text-size', '16px');
button3.style('border', 'none');
button3.style('border-radius', '10px'); // Cantos arredondados
button3.style('padding', '5px 38px'); // Preenchimento interno
}
function toggleCross1Visibility() {
cross1Visible = !cross1Visible; // Inverte o valor da variável cross1Visible
if (cross1Visible) {
cross2Visible = false; // Certifique-se de que apenas uma cruz está ativada
}
}
function activateBothCrosses() {
cross1Visible = true;
cross2Visible = true;
}
// Função de callback para desativar ambas as cruzes
function deactivateCrosses() {
cross1Visible = false;
cross2Visible = false;
}
function changeNeighborhoods() {
// Solicite ao usuário que insira o número de vizinhanças
let input = prompt("Digite o número de vizinhanças desejado:");
// Verifique se o usuário inseriu um valor válido
if (input !== null && !isNaN(input) && input >= 0) {
// Converta o valor de entrada para um número inteiro
neighborhoods = int(input);
// Imprima a quantidade de vizinhanças atual no console
print(`Número de vizinhanças atual: ${neighborhoods}`);
// Redesenhe as vizinhanças com o novo número
redrawNeighborhoods();
} else {
alert("Por favor, insira um número válido de vizinhanças.");
}
}
function redrawNeighborhoods() {
for (let i = 0; i < circleArr.length; i++) {
let thisCirc = circleArr[i];
thisCirc.numberOfNeighborhoods = neighborhoods; // Atualiza o número de vizinhanças para cada círculo
}
// Redesenhe os círculos com base no novo número de vizinhanças
background(255);
for (let i = 0; i < circleArr.length; i++) {
let thisCirc = circleArr[i];
thisCirc.updateMe(); // Atualiza os círculos com o novo número de vizinhanças
}
}
function draw() {
let crossSize = 800; // Tamanho da cruz
let rotationAngle = radians(45); // Ângulo de rotação em radianos
let rectw = windowWidth / 8;
let recth = windowHeight-40;
background(255);
// Atualize os círculos
for (let i = 0; i < circleArr.length; i++) {
let thisCirc = circleArr[i];
thisCirc.updateMe();
}
// Atualize as contagens para a geração atual
updateCircleCounts();
// Exiba as contagens da geração atual na tela
displayCircleCounts();
// Configuração da interface
setupUI();
if (cross1Visible) {
// Desenha a primeira cruz no centro
push();
stroke(0, 255, 50);
strokeWeight(1.5);
line(width / 2, height / 2 - crossSize / 2, width / 2, height / 2 + crossSize / 2);
line(width / 2 - crossSize / 2, height / 2, width / 2 + crossSize / 2, height / 2);
pop();
}
if (cross2Visible) {
// Desenha a segunda cruz rotacionada
push(); // Salva a matriz de transformação atual
stroke(0, 255, 50);
strokeWeight(1.5);
translate(width / 2, height / 2); // Move o ponto de origem para o centro
rotate(rotationAngle); // Aplica a rotação
line(-crossSize / 2, 0, crossSize / 2, 0); // Desenha a linha horizontal rotacionada
line(0, -crossSize / 2, 0, crossSize / 2); // Desenha a linha vertical rotacionada
pop(); // Restaura a matriz de transformação original
}
// EXIBE O NÚMERO DE VIZINHANÇAS DE CADA CÍRCULO
fill(50); // Define a cor do texto para preto
textSize(8);
textAlign(CENTER);
for (let i = 0; i < circleArr.length; i++) {
let thisCirc = circleArr[i];
let x = rectw * 7.35;
let y = 50 + i * 22; // Posição vertical separada para cada círculo
text(`Círculo ${i + 1}: ${thisCirc.numberOfNeighborhoods} vizinhanças`, x, y);
}
}
function updateCircleCounts() {
// Limpe o array de contagens
circleCounts = [];
pointOrderList = []; // Limpe a lista de ordem dos pontos
// Percorra cada círculo
for (let i = 0; i < circleArr.length; i++) {
let circle = circleArr[i];
let counts = { 0: 0, 1: 0, 2: 0 }; // Inicialize as contagens para cada tipo
// Percorra os pontos do círculo e conte os elementos em ordem cronológica
let pointOrder = []; // Lista para armazenar a ordem dos pontos neste círculo
for (let j = 0; j < circle.pointArr.length; j++) {
let point = circle.pointArr[j];
counts[point.state]++;
pointOrder.push(point.state); // Adicione o estado do ponto à lista de ordem
}
// Converta as contagens em uma matriz ordenada por tipo
let orderedCounts = [];
for (let type = 0; type <= 2; type++) {
orderedCounts.push(counts[type]);
}
// Adicione as contagens ordenadas do círculo ao array de contagens
circleCounts.push(orderedCounts);
// Adicione a lista de ordem dos pontos para este círculo à lista principal
pointOrderList.push(pointOrder);
}
}
// MOSTRA NA TELA A QUANTIDADE DE PONTOS
function displayCircleCounts() {
textSize(8);
fill(0);
let rectw = windowWidth / 8;
let recth = windowHeight - 40;
// Percorra cada círculo e exiba as contagens e a ordem dos pontos
for (let i = 0; i < circleCounts.length; i++) {
let counts = circleCounts[i];
let formattedCounts = `CÍRCULO ${i + 1}: `;
formattedCounts += `${counts[0]} Tipo0, ${counts[1]} Tipo1, ${counts[2]} Tipo2`;
// Exiba a string formatada na tela
let x = windowWidth - 30;
let y = recth * 0.70 + i * 35; // Posição vertical separada para cada círculo
textAlign(RIGHT); // Alinhe o texto à direita
text(formattedCounts, x, y);
// Exiba a ordem dos pontos para este círculo
let pointOrder = pointOrderList[i];
let pointOrderText = `ORDEM: ${pointOrder.join(", ")}`;
y += 15; // Aumente a posição vertical para a ordem dos pontos
text(pointOrderText, x, y);
}
}
function windowResized() {
// Redimensiona o canvas quando a janela do navegador for redimensionada
resizeCanvas(windowWidth, windowHeight);
}
function drawCircles() {
// MADE UP FUNCTION
for (let i = 0; i < circles; i++) {
if (e_counter == 0) {
// Create the first circle with a different number of points
let thisCirc = new Circle(firstCirclePoints, e_radius, true, neighborhoods);
thisCirc.drawMe();
circleArr.push(thisCirc);
} else if (e_counter < circles) {
// Create all the other circles
let thisCirc = new Circle(e_steps, e_radius, false, neighborhoods);
thisCirc.drawMe();
circleArr.push(thisCirc);
e_radius += e_gauge;
b_radius = e_radius - e_gauge;
e_steps -= 3;
}
e_counter++;
}
}
class Circle {
constructor(s, r, f, n) {
this.steps = s;
this.radius = r;
this.first = f;
this.numberOfNeighborhoods = n;
this.pointArr = []; // Array de Celular Automata
// Aplique um offset negativo para o primeiro círculo
if (this.first) {
this.radius += -25; // Ajuste esse valor conforme necessário
}
}
updateMe() {
let dis = map(sin(radians(0)), 0, 1, this.radius, width);
push();
translate(width / 2, height / 2);
fill(0);
ellipse(dis, 0, 6, 6); // Closing point of the piece
noFill();
smooth();
let count = 0;
for (let deg = 0; deg < 360; deg += this.steps) {
if (this.first && count < this.pointArr.length) {
this.pointArr[count].drawMe(0); // state 0 == correntinha
} else if (!this.first && count < this.pointArr.length) {
let numberOfCarreirinhas = 0;
let numberOfPontosBaixos = 0;
let numberOfPontosAltos = 0;
//-------------------------------*-----------------------------------//
// LÓGICA GENERATIVA
//-------------------------------*-----------------------------------//
// CONFERE O ESTADO DOS VIZINHOS
this.pointArr[count].checkMyNeighbours();
// Count the type of neighbors
let thisNeighborhood = this.pointArr[count].myNeighbours;
for (let x = 0; x < thisNeighborhood.length; x++) {
if (thisNeighborhood[x] == 0) {
numberOfCarreirinhas++;
} else if (thisNeighborhood[x] == 1) {
numberOfPontosBaixos++;
} else if (thisNeighborhood[x] == 2) {
numberOfPontosAltos++;
}
}
// AUTOMATO CELULAR
if (numberOfPontosBaixos + numberOfPontosAltos > (3 * this.pointArr[count].myNeighbours.length) / 4) {
this.pointArr[count].drawMe(0); // O PONTO VIRA PONTO CORRENTINHA SE A QUANTIDADE VIZINHOS PONTOS BAIXOS + PONTOS ALTOR FOR MAIOR QUE 75% (3/4= 0,75%)
} else if (numberOfCarreirinhas < (2 * this.pointArr[count].myNeighbours.length) / 3) {
this.pointArr[count].drawMe(1); // O PONTO VIRA PONTO BAIXO, SE A QUANTIDADE VIZINHOS CORRENTINHAS FOR MENOR QUE 66% (2/3= 0,66%)
} else if (numberOfCarreirinhas + numberOfPontosBaixos > (2 * this.pointArr[count].myNeighbours.length) / 3) {
this.pointArr[count].drawMe(2); // O PONTO VIRA PONTO ALTO, SE A QUANTIDADE VIZINHOS CORRENTINHAS + PONTOS BAIXOS FOR MAIOR QUE 66% (2/3= 0,66%)
} else {
// PARA FAZER ELE MUDAR AUTOMÁTICO
this.pointArr[count].drawMe(floor(random(3)));
}
}
rotate(radians(this.steps));
count++;
}
pop();
}
drawMe() {
let dis = map(sin(radians(0)), 0, 1, this.radius, width);
let angulo = 360 / this.numberOfNeighborhoods;
let thisAngulo = 0;
push();
translate(width / 2, height / 2);
fill(0);
ellipse(dis, 0, 6, 6); // Closing point of the piece
smooth();
for (let deg = 0; deg < 360; deg += this.steps) {
if (this.first) {
let thisPoint = new Point(dis, 0);
thisPoint.drawMe(0);
this.pointArr.push(thisPoint);
} else {
if (deg <= thisAngulo) {
// Creates point by petals
let thisPoint = new Point(dis, this.numberOfNeighborhoods);
this.pointArr.push(thisPoint);
thisPoint.addMyNeighbours();
thisPoint.drawMe(floor(random(3)));
} else {
thisAngulo += angulo;
this.numberOfNeighborhoods--;
let thisPoint = new Point(dis, this.numberOfNeighborhoods);
this.pointArr.push(thisPoint);
thisPoint.addMyNeighbours();
thisPoint.drawMe(floor(random(3)));
}
}
rotate(radians(this.steps));
}
pop();
}
}
class Point {
constructor(d, n) {
this.state = 0;
this.dis = d;
this.neighborhood = n;
this.myNeighbours = []; // Array of states of neighbors close to me
}
addMyNeighbours() {
for (let i = 0; i < circleArr.length; i++) {
let thisCirc = circleArr[i];
for (let j = 0; j < thisCirc.pointArr.length; j++) {
let thisPoint = thisCirc.pointArr[j];
if (thisPoint !== this && thisPoint.neighborhood == this.neighborhood) {
this.myNeighbours.push(thisPoint.state);
}
}
}
}
checkMyNeighbours() {
let count = 0;
for (let i = 0; i < circleArr.length; i++) {
let thisCirc = circleArr[i];
for (let j = 0; j < thisCirc.pointArr.length; j++) {
let thisPoint = thisCirc.pointArr[j];
if (thisPoint !== this && thisPoint.neighborhood == this.neighborhood && count < this.myNeighbours.length) {
this.myNeighbours[count] = thisPoint.state;
count++;
}
}
}
}
stopMe() {
switch (this.state) {
case 0:
noFill();
ellipse(this.dis, 0, 8, 8);
break;
case 1:
image(img1, this.dis, -5);
break;
case 2:
image(img2, this.dis, -5);
break;
}
}
drawMe(s) {
this.state = s;
switch (this.state) {
case 0:
noFill();
ellipse(this.dis, 0, 8, 8);
break;
case 1:
image(img1, this.dis, -6);
break;
case 2:
image(img2, this.dis, -7);
break;
}
}
}