Pixel Art Using a Square Class

This was a challenging assignment! Mainly because I had a game I wanted to do in mind, and after spending a lot of time on getting the basics to work I realized it might need more practice and time than what I have now, so I changed the direction of my assignment and decided to go with a more “artwork” focused approach.

Recently I’ve been thinking about the ‘paint by numbers notebooks’ I used to have as a child and how accessible they made creating artwork that is above my artistic skills. This made me want to buy a ‘paint by numbers’ set, but also I asked if I could use the same approach to create artwork on processing.

example is taken from https://tomyumtumweb.com/ 

I realized I can do a similar approach but with pixels! I made a class called “square” which represents each square in a grid, and then used a 2D array list that contains different combinations of numbers of squares to be filled.

My class has a display function, and a fill function that relies on a boolean variable.

For the drawing, I decided to go for a heart that is filling gradually with every number the user inputs from their keyboard. At the final level, when the heart is full, it goes from black to a mix of random shades of red and pink which is controlled by the mouseX movement to a certain extent (to avoid the colors being too random).

Inspiration Image

After finalizing the logic, which depends on creating an array of square objects and then displaying and referencing them as a grid. I went to figure out the square numbers needed in my 2D array for each heart stage. For this part, I used pixilart.com  and created a grid of the same size as mine then compared the square values.

 

Taking values from sketch to my code
Trying different grid sizes 

I settled on bigger pixels, but since the code contains, row, column, and square number variables, it is easy to change the grid appearance and square size.

I think the program has the potential to be more complex or contain more user interaction, I would love any suggestions!

Have a look at my main code:

Square[] allSquares; //declaring an array of objects that consists of the squares that will be in the grid

// 2D Array of the different heart level drawings, each inner list contains the number of the pixel that should be filled
int[][] heartLevels = { {2,3,4,11,15,21,26,32,37,43,48,53,58,62,67,71,76,81,85,92,93,94}, //empty heart
                   {2,3,4,11,15,21,26,32,37,43,47,48,53,57,58,62,67,71,76,81,85,92,93,94}, //initially filled heart (Level 1)
                   {2,3,4,11,15,21,25,26,32,35,36,37,43,45,46,47,48,53,55,56,57,58,62,65,66,67,71,75,76,81,85,92,93,94}, // medium full heart (Level 2) 
                   {2,3,4,11,13,14,15,21,23,24,25,26,32,33,34,35,36,37,43,44,45,46,47,48,53,54,55,56,57,58,62,63,64,65,66,67,71,73,74,75,76,81,83,84,85,92,93,94}, 
// almost full heart (Level 3)
                   {2,3,4,11,12,13,14,15,21,22,23,24,25,26,32,33,34,35,36,37,43,44,45,46,47,48,53,54,55,56,57,58,62,63,64,65,66,67,71,72,73,74,75,76,81,82,83,84,85,92,93,94}}; // full heart
                   
// a boolean to create the color effect for the full heart
boolean fullHeart; 
                   
 // declaring and initializing the column and row number for the grid                  
int cols = 10; 
int rows = 10;



void setup(){
 size(500,500);
 background(255);
 //creating the grid array, the array size corresponds to the dimension of the grid
 allSquares = new Square[cols*rows]; 
 //initializing and declaring an index variable to create different objects every time the  nested loop runs
 int index = 0; 
 //a nested loop that goes through each column and row to create a grid of objects
 for (int c = 0; c < cols; c++){
  for (int r = 0; r < rows; r++){
    // creating the objects, the arguments of the class are X location, Y location, Width, Height, and index

//we multiply c and r by 50 because it corresponds to the square dimension, so if my x is 2 and y is 2, the square will be at 100,100.
   allSquares[index] = new Square(c*50,r*50,50,50,index); 


   index+=1;
   
  }
 }
 fullHeart = false; //initializing the color effect variable with false
}



void draw(){
  //a loop that displays the grid
  for(int i = 0; i < cols*rows; i++){
   allSquares[i].display();
   
   //an if statement that checks which heart we are creating by checking if the fullHeart bool is true, it is changed to true below, when the key is pressed.
   if (fullHeart == true){
   for (int j = 0; j < heartLevels[4].length; j++){

     //accessing the heart color variable defined in the square class, and altering it for the color effect

//using random within a small range to generate the different shades of red, and applying interaction through mouseX
    allSquares[heartLevels[4][j]].heartColor = color(random(mouseX,255), random(0,90),random(90,100));  //interacting with user through changing the R random bottom range using mouseX. 
 
                                             
                                             
   //filling the squares, it keeps changing because it is in the draw function
    allSquares[heartLevels[4][j]].fillingSquare(true); 
   
  }
   }
  }
}
  
  
  
  void keyPressed(){
  //key pressed function that displays different heart shapes based on the number pressed  
    
  if (key == '0'){ //zero gives an empty screen
      for(int i = 0; i < cols*rows; i++){
        fullHeart = false; //this boolean is set to false for every key that does not correspond to a full heart

   allSquares[i].fillingSquare(false); //First loop goes through all the squares in the object list and unfills them to remove the effect of other shapes, this loop is repeated for all the other keys
   
      }
  }
  if (key == '1'){
      for(int i = 0; i < cols*rows; i++){ //same loop
   allSquares[i].fillingSquare(false); 
  }
      for (int i = 0; i < heartLevels[0].length; i++){ 
        fullHeart = false;
       allSquares[heartLevels[0][i]].heartColor = color(0); //sets the color variable from the class to black

       allSquares[heartLevels[0][i]].fillingSquare(true); ////fills every pixel that corresponds to values in the first heart shape list with black
       
  }
  }
  
  //the same repeats for the next if conditions
  else if (key == '2'){
    for(int i = 0; i < cols*rows; i++){
   allSquares[i].fillingSquare(false);
  }
    for (int i = 0; i < heartLevels[1].length; i++){
      fullHeart = false;
     allSquares[heartLevels[1][i]].heartColor = color(0);
     allSquares[heartLevels[1][i]].fillingSquare(true); 
  }
 

  
 
  }
  else if(key=='3'){
   for(int i = 0; i < cols*rows; i++){
   allSquares[i].fillingSquare(false); 
  }
    for (int i = 0; i < heartLevels[2].length; i++){
      fullHeart = false;
     allSquares[heartLevels[2][i]].heartColor = color(0);
     allSquares[heartLevels[2][i]].fillingSquare(true);
   
  }
  }
  else if(key=='4'){
     for(int i = 0; i < cols*rows; i++){
   allSquares[i].fillingSquare(false);
  }
     for (int i = 0; i < heartLevels[3].length; i++){
       fullHeart = false;
     allSquares[heartLevels[3][i]].heartColor = color(0); 
     allSquares[heartLevels[3][i]].fillingSquare(true);
   
  }
  }
  else if(key=='5'){
    for(int i = 0; i < cols*rows; i++){
  allSquares[i].fillingSquare(false);
  }
    for (int i = 0; i < heartLevels[4].length; i++){
     fullHeart = true; //this turns on the random color fill in the draw function
    allSquares[heartLevels[4][i]].fillingSquare(true); //and based on those colors fills the squares
    
    
    }
  }
  }

and my Square Class:

class Square{
//initializing variables that will be used throughout the class  
int squareX , squareY;  //square x and y location
int squareW , squareH; 
int squareNo; //the number of the pixel on the grid
boolean filled; //the boolean that determines which pixels on the screen are filled
color heartColor; //a color variable to control the color of the pixel
color gridStroke = color(203,197,197); // color of grid lines

Square(int tempX, int tempY, int tempW, int tempH, int tempSquareNoMinus1){ //constructor, takes X location, Y location, Width, Height, and the index as arguments
 //setting the constructor arguments to permanent variables
 squareX = tempX;
 squareY = tempY;
 squareW = tempW;
 squareH = tempH;
 squareNo = tempSquareNoMinus1 + 1; // 1 is added to the index as the counting of squares starts at 1, not zero. 
 
}



void display(){
 //function that displays the square on the screen
 stroke(gridStroke);
 strokeWeight(0.25);
 noFill();
 rect(squareX , squareY , squareW , squareH);
 
 
}


void fillingSquare(boolean filled){
  //a function that fills the pixels based on a filled? boolean
  if (filled == true){
    
   fill(heartColor); 
  }
  else{
  //if the boolean is false the square is filled with white and redrawn  
   fill(255); 
  }
  rect(squareX,squareY,squareW,squareH);
  }
}

 

 

Leave a Reply