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.

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).

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.


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);
}
}