Help with splitting cell


#1

Hi everyone!

I’ve been stuck for a while on this sketch and hope to get some help. What I’m trying to achieve is to create a grid cells (I’ll start my sketch with 4 cells). If clicked, each cell will split into 4 new cells, clicking any of these repeats the patterns.

My problems are:

  1. Only clicks in the top right cell produces a result closer to the desired outcome
  2. The closer you click to the 0,0 coordinate, the more cells are being generated (can’t figure out why)
  3. A disproportionate large amount of cells are being generated

Any pointers would be helpful!
I’ve added comments to my code. Helpful this gives some more clarity.

let cells = [];

function setup(){
  createCanvas(windowWidth, windowHeight);
  background(225);
  for (let x = 0; x < windowWidth; x += windowWidth/2){
    for (let y = 0; y < windowHeight; y += windowHeight/2) {
      cells.push(new Cell(x,y,windowWidth/2,windowHeight/2));
      console.log(cells.length);
    }
  }
}

//Draws new cells based on length of array
function draw(){
  for (let i = 0; i < cells.length; i++){
   cells[i].drawCell();
  }
}

//Function to listen for click events
function mouseClicked(){
  for (let i = 0; i < cells.length; i++){
    cells[i].clickCell();
  }
}

/* CELL CLASS */
class Cell {
  constructor(cellX, cellY, cellWidth, cellHeight) {
    this.x = cellX;
    this.y = cellY; 
    this.width = cellWidth;
    this.height = cellHeight;
  }
  
  drawCell(){
    stroke(255);
    strokeWeight(2);
    fill('#eb008d');
    rect(this.x, this.y, this.width, this.height);
  }
  
  clickCell(){
    //Detect if click is inside a specifc cell
    if((mouseX > this.x) && (mouseX <  this.x + this.width) && (mouseY > this.y) && (mouseY < this.y + this.height)){
      
      //Run twice on given cell width
      for (let x = this.x; x < this.width; x += this.width/2){
        
        // Run twice on given cell height
        for (let y = this.y; y < this.height; y += this.height/2) {
          
          // Add cells to array
          cells.push(new Cell(x,y,this.width/2,this.height/2));
          console.log(cells.length);
          }   
        }  
      }
    }
  }

#2

Here is a quick Processing(Java) version of what you are trying to implement.

// https://discourse.processing.org/t/help-with-splitting-cell/7726
// 2019-01-27 Processing 3.4

ArrayList<Cell> cells;

void setup() {
  size(400, 400);
  cells = new ArrayList<Cell>();
  cells.add(new Cell(10, 10, 380, 380));
}

void draw() {
  background(0);
  for (int i = 0; i < cells.size(); i++){
   cells.get(i).drawCell();
  }
}

void mouseReleased(){
  boolean hit;
  for (int i = cells.size()-1; i >= 0; i--){
    hit = cells.get(i).clickCell(2);
    if(hit){
      break;
    }
  }
}

class Cell {
  float x, y, width, height;
  Cell(float x, float y, float w, float h) {
    this.x = x;
    this.y = y;
    this.width = w;
    this.height = h;
  }
  void drawCell() {
    stroke(255);
    strokeWeight(2);
    fill(color(240, 0, 192));
    rect(this.x, this.y, this.width, this.height);
  }
  boolean clickCell(int steps) {
    //Detect if click is inside a specifc cell
    if ((mouseX > this.x) && (mouseX <  this.x + this.width) && (mouseY > this.y) && (mouseY < this.y + this.height)) {
      float newWidth = this.width/(float)steps;
      float newHeight = this.height/(float)steps;
      for (int row = 0; row < steps; row++) {
        for (int col = 0; col < steps; col++) {
          float newx = this.x + col * newWidth;
          float newy = this.y + row * newHeight;
          cells.add(new Cell(newx, newy, newWidth, newHeight));
        }
      }
      return true;
    }
    return false;
  }
}

A few observations:

  1. You are layering on new cells – not pop-ing / removing the old ones.
  2. This means that you can cascade through new cells, adding them, detecting them, and adding more. That doesn’t seem to be what you intend.
  3. If you don’t want to remove the old cells, then you should loop through your cell list backwards – that will find the newest / smallest / top-most cells first.
  4. When you hit a cell, split it and then bail out (break) – otherwise you will continue to re-split the underlying cell again and again. In order to know if you have a hit, return a boolean from your click method.

There are other ways of tackling this problem, but I’ve started with something as close as possible to your prototype. Hope this helps – let me know if you have any questions.

Also, note that your splitting code (2x2) is a special case of nxn – I modified your function slightly to accept the dimension as an argument. Rather than looping over float steps, loop over step counters and calculate the positions internally.


#3

Hey Jeremy. Thank you for the great comments and help you’ve provided. I was able to solve the problem (with help from a friend of mine :slight_smile:). We added hasBeenSplit and allowSplit and hasBeenSplit to ensure the same cell can’t get split twice and to control the loop the draws the cells.

It would perhaps be better to pop the parent cell as you mentioned, though.

Thanks

let cells = [];
let allowSplit = true;


function setup(){
  createCanvas(windowWidth, windowHeight);
  background(225);
  noStroke();
  for (let x = 0; x < windowWidth; x += windowWidth/2){
    for (let y = 0; y < windowHeight; y += windowHeight/2) {
      cells.push(new Cell(x,y,windowWidth/2,windowHeight/2));
      console.log(cells.length);
    }
  }
}

//Draws new cells based on length of array
function draw(){
  for (let i = 0; i < cells.length; i++){
   cells[i].drawCell();
  }
  color(0);
  textSize(12);
  text(cells.length, 16, 16);
}

//Function to listen for click events
function mouseClicked(){
  for (let i = 0; i < cells.length; i++){
	  if (allowSplit) {
    	cells[i].clickCell();
	  }
  }
	allowSplit = true;
}

/* CELL CLASS */
class Cell {
  constructor(cellX, cellY, cellWidth, cellHeight) {
    this.x = cellX;
    this.y = cellY; 
    this.width = cellWidth;
    this.height = cellHeight;
    this.hasBeenSplit = false;
    this.randomRed = random(0, 255);
    this.randomBlue = random(20, 200);
    this.randomGreen = random(10, 15);    
  }
  
  
  drawCell(){
    fill(this.randomRed, this.randomBlue, this.randomGreen)
    rect(this.x, this.y, this.width, this.height);
  }
  
  clickCell(){
    //Detect if click is inside a specifc cell
    if(!this.hasBeenSplit && (mouseX > this.x) && (mouseX <  this.x + this.width) && (mouseY > this.y) && (mouseY < this.y + this.height)){
      this.hasBeenSplit = true
			allowSplit = false
      //Run twice on given cell width
      for (let x = this.x; x < this.x + this.width; x += this.width/4){
        
        // Run twice on given cell height
        for (let y = this.y; y < this.y + this.height; y += this.height/4) {
          // Add cells to array
          cells.push(new Cell(x,y,this.width/4,this.height/4));
          }   
        }  
      }
    }
	
  }

#4

Glad that you worked it out. Thanks so much for sharing your solution with the forum!