Transforming text – Geomerative

General Information

As I was working on this, I realized that I haven’t understood the Geomerative library as much as I thought I did, as I struggled a lot while using it.
Therefore, I wanted to make this week’s assignment, an opportunity for me to get a better grasp of most things that have to do with Geomerative and implement something quite basic.

I got the idea to make a program that displays words from a string one at a time, with a transition transforming each word into the one following it. (When we reach the final word of the string, it starts again from the first word.)
It turns out that this has been done a lot before, and to be honest, since I was still trying to learn about the Geomerative library, I ended up needing a lot of help and inspiration from the pre-existing codes I found online.
(PS: I didn’t copy any code. I used what I found to understand some of the main concepts of the program, but I made sure to code everything myself.)

The process

The first thing I did was use the geomerative_basic example, that Professor has provided us with, as my base. (It’s on GitHub if anyone was wondering.)
This program displays one string using ellipses.
In order to make it more suitable for what I wanted to create, I made a bunch of changes:

      • I turned everything into arrays (or array of arrays) so that it can be used on multiple strings instead of one.
      • Instead of using RGroup, I used RShape. This is because I needed access to each point individually so I needed to use getPoint() instead of getPoints(), which doesn’t work on RGroup.
      • Instead of drawing ellipses, I drew points. I made the number of points quite big so that it appears as a line which makes the transition effect look a bit cooler.
      • I added another array of arrays of RPoint called “changing_pnts” such that for every word, there are multiple elements:  changing_pnts[…], each containing the positions of all the points making up that word but at a different stage of the transition.
      • I added a function that populates changing_pnts. It has a role in changing each word into the next one and the last word back into the first one.

This is the code in the tab containing the function:

float temp1,temp2;

void populate_changing_pnts()
{
  //for each word
  for (int i=0; i<myStringArr.length; i++) 
  {
      //for the number of positions taken during the transition
      for (int j=0; j<num; j++) 
      {
          //for the number of points in this word (word of index i)
          for (int k=0; k<pnts[i].length; k++) 
          {
              //if we're at the last word, make the transition back to the first word
              if (i == myStringArr.length -1){
                temp1 = lerp( pnts[i][k].x, pnts[0][k].x, j*(1.0/num));
                temp2 = lerp( pnts[i][k].y, pnts[0][k].y, j*(1.0/num));
                changing_pnts[index][k] = new RPoint(temp1, temp2);
              }
              //if we're not at the last word, make the transition to the next word
              else {
                temp1 = lerp( pnts[i][k].x, pnts[i+1][k].x, j*(1.0/num));
                temp2 = lerp( pnts[i][k].y, pnts[i+1][k].y, j*(1.0/num));
                changing_pnts[index][k] = new RPoint(temp1, temp2);
              }  
          }
          index += 1; /*this is here because when the counter j goes back to 0, we want index to keep 
                      incrementing to reach the size of changing_pnts*/
      }
  }
  index = 0; //setting back index to 0
}

 

And this is the code in the main tab:

import geomerative.*;

RFont font;
int diameter = 15;
int pointNum = 2500; //this how many points we want the words to be made up of

String myString = "this wasn't as easy as it seemed in my head"; 
String[] myStringArr; //this is a string of strings that will contain all words from myString 
RPoint[][] pnts; //an array of arrays of points. For each word i, pnts[i] contains all points that form that word

RShape[] words; //an array of groups
float xOffset[]; //an array of xOffsets

int num = 60; //This is the number of positions that each point will go through while it's transitioning
              //This means it will move through 60 positions to get from its start to its destination

RPoint[][] changing_pnts; //an array of arrays of points.
                          //its size will be num * the size of the array of arrays pnts.
                          /*because for each word, there exist many elements changing_pnts[] 
                          each containing the positions of the points at a different stage of the transition*/

int index = 0; //this variable will be the one used to iterate through changing_pnts without a for loop


void setup() {
  size(640, 640);
  stroke(0);
  strokeWeight(4);
  
  RG.init(this);
  font = new RFont("Franklin Goth Ext Condensed.ttf", 200, RFont.CENTER);
  
  myStringArr = split(myString, ' ');
  
  //initializing all of the arrays according to the size of myStringArr
  pnts = new RPoint[myStringArr.length][pointNum];
  words = new RShape[myStringArr.length];
  xOffset = new float[myStringArr.length];
  changing_pnts = new RPoint[(myStringArr.length)*num][pointNum]; //size of this is = nums * size of pnts

  
  // load all strings from the myStringArr into geomerative
  for(int i=0; i<myStringArr.length; i++){
    words[i] = font.toShape(myStringArr[i]);
    //create the same number of points for each word
    for (int j=0; j<pointNum; j++){
      pnts[i][j]= words[i].getPoint(j*(1.0/pointNum));
    }

     //to set each word in the middle of the screen
     //get the size of the string from myStringArr susbtracted from the width of the screen
     //this is the amount left over
    xOffset[i] = width - words[i].getBottomRight().x - words[i].getBottomLeft().x;
    // divide that by two to center it
    xOffset[i] /= 2;
  }
  
  //this function fills changing_pnts with all the required points
  populate_changing_pnts(); 
}

void draw() {
  background(255);
  for (int j = 0; j< pointNum; j++) {
    float x = changing_pnts[index][j].x + xOffset[0];
    float y = changing_pnts[index][j].y + height/2;
    point(x, y);
  }
  index+= 1;
  if (index == (myStringArr.length)*num){
    index=0;
  }
  
}

final outcome

angry w no rights- Meera

 

PFont f;
// int for the gradient (r,g,b).
int r = 0;
int g = 0;
int b= 0;
String feels;
int x;
float t = 400;
void setup(){
  size(800,800);
 //i went to the tools craeted a font and saved it and just wrote the name in loadFont.
  f =loadFont("Herculanum-60.vlw");
  textFont(f);
feels = "angry w no right";
x = width;

}

 
 void draw(){
  
   //Gradient background
   for (int i = 0; i<width; i++){
     stroke(i,i,i);
     line(0,i,width,i);
   }
  // text i chose to write, had to fill the text to be able to see it; couldnt find it intially.
 fill(0);
// what makes my sentence move back and forth is the wave and the random is making it go up and down 
float wave = sin (radians(frameCount));
 
 text(feels,90+wave*200,height/2+random(-100,100));

 }

 

Epileptic spiral

int y;
int size; 
int hundred;
float end;
void setup() {

  size(640, 400);
  background(255);

};
void draw() {

  noFill();
  stroke(0);
  
  //ellipse(width/2-25,height/2,50,50);
hundred = 100; 
  for (int i=0; i<width; i+=100) {
    strokeWeight(20);
    
    arc(320, 200, hundred+i, hundred+i, radians(180), radians(360) );
    arc(320-25, 200, hundred*1.5+i, hundred*1.5+i, radians(0), radians(180) );
     //interesting effect here
     filter(INVERT);
     
    ;
  };
  
  
};
void keyPressed() {
  fill(#FFE200);
  textSize(random(20,200));
  text(key,random(300),random(100,400));
  print(key);
};


Cardio Time

For this week’s assignment I decided to use that was being collected by the running app that I am using to track my running progress. The app records the distance per run, walking pace, running pace and the average pace.

For this assignment I took the average pace per run [given in minutes/km] and point plotted it against the days.

The data was between 0 and 9 so I have to get that data in the range such that I could plot it on the screen. For this I used the following formula
data*(maxDrawableHeight)/9.

This gave me the height of the points from the axis but since the drawing points are given from top left corner going down I inverted this by subtracting this from the drawable height.

 

Table data;
float[] numericData;
float [] plotData;
int numCols;
int axisOffset = 100;
void setup() {
  size(1000, 500);
  loadData();
  dataTransformation();
  drawAxes();
  drawGrid();
  printLabels();
  plotPoints();
  noLoop();
}

void draw() {
}

void drawAxes() {
  axisOffset = 50;
  pushStyle();
  strokeWeight(2);
  //y axis
  line(axisOffset, 0, axisOffset, height-axisOffset);
  //x-axis
  line(0+axisOffset, height-axisOffset, width, height-axisOffset);
  popStyle();
}

void loadData() {
  data = loadTable("data.csv");
  TableRow row = data.getRow(0);
  numCols = row.getColumnCount();
  numericData = new float[numCols];
  for (int i=0; i<numCols; i++) {
    numericData[i] = Float.parseFloat(row.getString(i));
  }
  //println();
}

void printData() {
  for (int i=0; i<numericData.length; i++)
  {
    print(numericData[i] + " | ");
  }
  println();
}

void drawGrid()
{
  // data will be in the range of 0 < 9*(height-axisOffset)
  pushStyle();
  stroke(0,0,0,40);
  // for drawing Horizontal grid lines
  float xLineGap = (height-axisOffset)/9;
  for (float y=height-axisOffset; y>=0; y=y-xLineGap){
    line(axisOffset,y,width,y);
  }
  //println(numCols);
  float yLineGap = (width-axisOffset)/numCols;
  for (float x=axisOffset+yLineGap;x<=width; x+=yLineGap)
  {
    line(x,0,x,height-axisOffset);
  }
  popStyle();
}

void dataTransformation(){
  // data will be in the range of 0 < 9*(height-axisOffset)
  //transform the data
  plotData = new float[numCols];
  for(int i=0;i<numCols;i++)
  {
    plotData[i] = numericData[i]*(height-axisOffset)/9;
  }
  //println(plotData);
}

void plotPoints(){
  float yLineGap = (width-axisOffset)/numCols;
  for(int i=0;i<numCols;i++){
    strokeWeight(6);
    float yCoord = height-(plotData[i]+axisOffset+yLineGap);
    //println(plotData[i]);
    point(axisOffset+yLineGap+(yLineGap)*i,yCoord);
  }
}

void printLabels(){
  //title
  fill(0,0,0);
  textSize(20);
  textAlign(CENTER);
  String title = "Graph Of Average Pace [minutes/km] Against Run Days";
  text(title,width/2, height-(axisOffset/5));
  
  
  //x-axis labels
  float yLineGap = (width-axisOffset)/numCols;
  for(int i=0;i<numCols;i++)
  {
    text(i+1,axisOffset+yLineGap+(yLineGap)*i, height-(axisOffset*3/5));
  }
  
  //y-axis labels
  float xLineGap = (height-axisOffset)/9;
  int text = 0;
  for(float y=height-axisOffset; y>=0; y=y-xLineGap)
  {
    text(text,axisOffset-10,y);
    text++;
    //println(text);
  }
    
  for (float y=height-axisOffset; y>=0; y=y-xLineGap){
    line(axisOffset,y,width,y);
  }
}

 

Week 4: Generative text

For this weeks exercise, I liked the ‘Word made of Circles’ example that we looked at in class so I tried out what it could look like with a rectangle instead of ellipses, and to enhance that ‘edgy’ look, I imported a new font with letter composed of little squares. For the colors, I gave the little rectangles that compose the letters a white outline and a yellow, slightly transparent fill that matches the yellow of the background. This creates somewhat of a gradient from the yellow inside of the letters to the white edge that only becomes  visible when hovering over the letters.

This screenshot shows the color gradient from yellow to white inside the letters.

Take a look at the processing code below adapted from the class example.

import geomerative.*;
RFont font;
String phrase = "susanne";
float xOffset = 0;

RPoint[] pnts;

void setup() {
  size(800, 800);
  RG.init(this);
  font = new RFont("PressStart2P-Regular.ttf", 100, RFont.LEFT);

  // load the phrase into geomerative
  // the first line sets how much distance should there by between each point 
  RCommand.setSegmentLength(1);
  //RCommand.setSegmentator(RCommand.UNIFORMLENGTH);
  RCommand.setSegmentator(RCommand.UNIFORMLENGTH );
  RGroup grp;
  grp = font.toGroup(phrase);
  grp = grp.toPolygonGroup();
  pnts = grp.getPoints();

  // to set the word in the middle of the screen
  // get the size of the phrase subtracted from the width of the screen
  // this is the amount left over
  xOffset = width - grp.getBottomRight().x - grp.getBottomLeft().x;
  // divide that by two to center it
  xOffset = xOffset/2;

  fill(251, 252, 163, 5);
  stroke(255, 255, 255, 40);
}

void draw() {
  background(226, 227, 172);

  for (int i=0; i< pnts.length; i++) {
    float moveIt = (noise(frameCount*.01+i*.01)-.5)*10;
    float x = pnts[i].x + moveIt + xOffset;
    float y = pnts[i].y + moveIt + height/2;

    // dividing makes it invert it's action
    float diam = 2500/dist(x, y, mouseX, mouseY);
    diam = constrain(diam, 7, 70);

    rect(x, y, diam, diam);
  }
}

 

 

Survival of the fittest

***current works in progress.

Inspired by evolution, my object oriented programming sketch is centered around different colored circles that represent different species, emerging at random and when clicked on duplicate exponentially. However, one circle from each species “dies off ” (removed) at random. Once one full row of circles fill up the top of the window, they “die off” by having the whole row disappear , this represents the death of older generations in rise of newer ones, and the cycle continues.

// still work in progress
Bubble b; 
void setup() {
  size(640, 360);
  b = new Bubble(64); // b is a new instance a bubble object, Bubble() is a constructor.
};
void draw() {
  background(255);
    //backdrop();
  b.top();
  b.display();
  b.ascend();//dot syntax, calling a function on that object.

};

//__________________
class Bubble {
  float x;
  float y;
  float diameter;

  Bubble(float tempD) { 
    x=width/2;
    y=height/2;
    diameter=tempD;
  };

  void display() {
    stroke(0);
    fill(127);
    ellipse(x, y, diameter, diameter);
  };

  void top() {
    if (y<diameter/2) {
      y=diameter/2;
    };
  };
  void ascend() {
    y--;
    x=x+random(-2, 2);
  };
};

______________________________________

Updated version: Box Game 🙂

float x = width/6;
float y = height/6;
// Click within the image to change 
// the value of the rectangle
void setup() {
  background(#FFA500);
  size(600, 600);
  ellipseMode(CENTER);
};
void draw() {
   keyPressed() ;

  if (mousePressed == true) {
    //background(100);
    fill(0);
    x+=1;

    //} else {
    //  fill(255);
  }
  //rect(25, 25, 50, 50);
  rect(x, y, 100, 100); //rect(x+random(-1, 1), y, 100, 100);
  if (keyPressed == true) {
    //background(100);
    fill(255);
    y+=1;
  };

};



  void keyPressed() {
    if(key == 's'){ 
    rect(x+100, y+100, 100, 100);
    
  };
   if(key == 's'){ 
    rect(x+200, y+200, 100, 100);
    
  };

  };

OOP Version: I lost some of the serendipity in the OOP version of this box interaction, versus in the one above. This assignment was harder the last but taught me a lot in code organization.

Box b;
Box b2;
void setup() {
  size(600, 600);
  b= new Box();
  b2= new Box();//change color and xy position.
};
void draw() {
  background(#FFA500);
  b.display();
  b.moveRight();
  b.moveLeft();
  b.leftWall();
  b.move();
  b.duplicate();
  //b.mirror();
  //b.addNewObjToArray();
  //b.exponentialGrowth();
  //b.rightWall():
  //b.bounceEdge();
  //b.bottom
  //b.top
}
//_____________
class Box {
  float x = width/2;
  float y = height/2;
  float xpos = random(100, 100);
  float ypos =random(200, 200);

  float xspeed=5;


  void  display() {

    rect(x, y, 100, 100);
    rectMode(CORNER);
  };
  void moveRight() {
    if (keyPressed == true) {
      fill(255);
      x+=1;
    };
  };
  void moveLeft() {
    if (keyPressed == false) {
      fill(255);

      x-=1;
    };
  };
  void leftWall() { //will rest at the edge
    if (x>width||x<0) {
      x=x*-0.1;
    };
  };
  //void rightWall() {
  //    if (y>width||x<0) {
  //    y=y*0.1;
  //  };
  void move() {
    if (keyPressed == true) {
      //background(100);
      fill(255);
      y+=1;
    };
  }
  void duplicate() {

    if (key == 's') { 
      rect(x+100, y+100, 100, 100);
    };
    if (key == 's') {
    };
  };
 
}

 

 

OOP Asteroids Game

For this week’s assignment I decided to make an arcade game. The goal of the game is to avoid all the falling mysterious asteroids and be the LAST SURVIVING SQUARE on the surface of the earth! The game is definitely not the final version and I am planning on adding some more features to it. But this could be considered the prototype of what I have imagined first. Below is the logo of the game I got inspired by initially.

Here is the video of the prototype of the game:

Below is the code:

// Asteroids Game

int i;
float x, y;
float velocity;
float acceleration;
int startTime = 0;
int time = 0;
Asteroid[] a = new Asteroid[20];

public void settings() {
  size(640, 640);
}

void setup(){
  //player position
  x = width/2;
  y = height-32;
  velocity = 0;
  acceleration = random(8,24);
  
  for (int i=0; i<a.length; i++){
    a[i] = new Asteroid();
  }
}

void draw(){
  background(0);
  
  pushMatrix();
  i = 0;
  while (i<a.length){
    a[i].display();
    a[i].update();
    if (a[i].posy > 12){
      i++;
    }
  }
  popMatrix();
  
  pushMatrix();
  player();
  popMatrix();
  
  textSize(25);
  fill(255);
  text("Level 1", width-100, 50);
  time = (millis() - startTime) / 1000;
  textSize(15);
  text("Time alive : " + time, width-115, 75);
}

void player(){
  stroke(255);
  strokeWeight(2);
  fill(127);
  rect(x, y, 30, 30);
}

void keyPressed() {
    if (keyCode == LEFT && x-15>= 0){
      velocity += acceleration;
      x -= velocity;
      acceleration *=0;
    }
    if (keyCode == RIGHT && x+15 <= width ){
      velocity += acceleration;
      x += velocity;
      acceleration *=0;
    }
}

class Asteroid {

  float posx, posy;
  float speed;

  Asteroid () {
    smooth();
    //asteroid position
    posx = random(width);
    posy = -15;
    speed = random(6);
  }

  void update() {
    posy += speed;

    if (posy > height+6) {
      posy = -6*2;
      posx = random(width);
    }
  }


  void display() {
    pushMatrix();
    stroke(255);
    strokeWeight(2);
    fill(127);
    ellipse(posx, posy, 24, 24);
    popMatrix();
  }
}


 

One More Time: Rolly Vortex (the Addictive Game) – Beethoven

Still hooked up by the mobile app game Rolly Vortex since last week’s assignment, I decided to take a shot at creating the game.

The mobile app game

I started out by trying to create the grey hoops which give the feeling of you being inside a vortex. I struggled with making them because I wanted to create white spaces between each of them, and because I wanted each hoop to restart from the middle after it expands more than the height of the screen. I wanted to do 3D where the z-dimension increases but I wasn’t comfortable enough at the start.

This was how they looked before fixing the distances between each of them, their thickness, and how they restart.

picture of the improved hoops

This is the better version where the hoops actually look like a vortex and are quite similar to the game.

I then started to create the ball and I wanted it to be 3D so a sphere instead of an ellipse. But I struggled at first with making sure the ball doesn’t enter the center of the vortex and did it manually, which was still unsuccessful.

I then did the blocks, where I started out by creating them using rectangles and moving their z-coordinate towards the screen to give the feeling of a vortex. However, after so many trial and error, I realized that the pushMatrix() and popMatrix() are quite dangerous if not used properly and that I had so many issues because of them. or example, I couldn’t move the blocks properly while moving the ball as in the video.

After many attempts I was finally able to create 3D boxes which get closer to the screen and to successfully move the ball without touching the center and to properly make a collide function that checks if the ball touches any block. I was able to end and restart the game without errors and to keep score while playing and at the end of the game.

The code for the overall game:

Hoop[] hoops;    //list of grey vortex circles
float x, y, z;
Ball ball;       // ball of the player
ArrayList<Block> blocks;    //array list of obstacle blocks
boolean gameOn;      //condition under which the game operates
PFont f;         //text for ending game
int score;        //score of user, increases with each block block they


void setup () {
  
  size(1280, 720, P3D);
  
  hoops = new Hoop[200];    //instances of the three main classes
  ball = new Ball();
  blocks = new ArrayList<Block>();
  
  blocks.add(new Block(60));    //initial theta in argument
  
  gameOn = true;
  score = 0;
  
  f = createFont("Arial",72,true);
  
  for (int i =0; i < hoops.length; i++) {      //creates the continuous hoops
    hoops[i] = new Hoop();
  }
  
  
}

void draw () {
  
  background(255);
  
  if (gameOn == true) {
    
    for (int i = 0; i < hoops.length; i+= 20) {      //draws the hoops and calls the related functions
      hoops[i].drawHoop(i*10);
      hoops[i].update();
      hoops[i].checkEdge();
    }
    
    ball.drawBall();          // draws the ball
    
    for (int i = blocks.size()-1; i >= 0; i--) {       //draws block for each element in the array list
      Block block = blocks.get(i);
      block.drawBlock();
      
    }
    
    if (frameCount % 60 == 0){                      //creates instances of the blocks of number between 1 and 6 randomly
      for(int i = 0; i < int(random(1,6)); i++) {
        blocks.add(new Block(int(random(1,12)*30)));
        
        if (gameOn == false) 
          {break;}
        
      }
    }
    score = blocks.size();          //updates the value of score to be number of elements in array list
    
    for (int i = blocks.size()-1; i >= 0; i--) {            //checks if the ball collided with any block, if true the game stops
      if (ball.checkCollide(blocks.get(i).posX, blocks.get(i).posY) == true) {
          gameOn = false;
       };
    }
    
    textAlign(CORNER);
    textFont(f,24);
    fill(0);
    text("Score: "+ score,width -120,height -30);        //score text bottom right
    
    
  }
  
  else {
    textFont(f,72);               
    fill(#000080);
    textAlign(CENTER);
    text("GAME OVER!",width/2,height/2 - 30);
    textFont(f,24);
    fill(0);
    text("CLICK ANYWHERE TO RESTART",width/2,height/2 +30);      //game over text
    
    textAlign(CORNER);
    textFont(f,24);
    fill(0);
    text("Score: "+ score,width -120,height -30);      //final score text
    
    
    
  }
}

void mouseClicked() {
  if (gameOn == false) {
    score = 0;
    gameOn = true;
    for (int i = blocks.size()-1; i >= 0; i--) { 
      Block block = blocks.remove(i);
      block.drawBlock(); 
    }
  }
}

Code for the Ball() class:

class Ball {
  float ballWidth, ballHeight;
  float locX, locY;
  color ballColor;
  float speed;
  int radius;
  PVector m;
  PVector circle;
  float z;
  float easing = 0.95;

  
  Ball() {
  
    locX = width/2;
    locY = height/2;

    ballWidth = 100;
    ballHeight = ballWidth;
    
    speed = 15;
  
    ballColor = color(random(255), random(255), random(255)); 
    
    radius = 150;
    
    z= 0;
  
  }
  
  void drawBall() {
     //idea inspired from ellipse constraint code from https://forum.processing.org/one/topic/ellipse-constrain-shape.html
   
    fill(#F5F5F5);
    noStroke();
    m = new PVector(mouseX, mouseY, z);      //vector of movement of ball
    circle = new PVector(width/2, height/2, z); //vector circle around which ball is constrained
    ellipse(circle.x, circle.y, radius, radius);     //circle of cosnstrain
    

    if (dist(m.x, m.y, circle.x, circle.y) < radius) {
      m.sub(circle);
      m.normalize();
      m.mult(radius);
      m.add(circle);      //constrains movement of ball to be everywhere except at a distance from teh center of the vortex
    }
    locX = locX + (m.x - locX) * easing;
    locY = locY + (m.y - locY) * easing;     //sets the x and y coordinates such that the ball is updated properly and doesn't disappear or enter middle 
    
    noStroke();
    lights();
    pushMatrix();
      translate(locX, locY - 30, z);
      fill(ballColor);
      sphereDetail(36, 36);
      sphere(30);
    popMatrix();      //draws the 3D sphere at the updated location
    //z+= 0.5;
  }
  
  boolean checkCollide(float posX, float posY) {

    if (dist(locX, locY - 30, posX - 30, posY + 25) < 55) {    //if ball touches the x,y coordinates of the block it collides and returns true to end game
      gameOn = false;
      return true;
    }
    
    else {
      return false;
    }
  }
}

Code for the Block() class:

class Block {
  float blockWidth, blockHeight, blockDepth;
  float locXB, locYB, locZ;
  color a, g, b;
  float posX, posY;
  float theta;
  float speed;

  Block(int _theta ) {
    
    locXB = width/2;
    locYB = height/2;
    locZ = 0;
  
    blockWidth = 60;
    blockHeight = 50;
    blockDepth = 30;
    
    a = int(random(255));
    g = int(random(255));
    b = int(random(255));    //randomized color
    
     posX = width/2;
     posY = height/2;
     theta = _theta;
    
    speed = 6;    //speed of shift
  
  }
  
  void drawBlock() {
   fill(a, g, b);
   noStroke();
   pushMatrix();
     
     translate(posX, posY, locZ);
     rotate(radians(theta));      //rotates blocks before moving
     posX += speed*sin(theta);    // horizontal shift
     posY += speed*cos(theta);    //vertical shift
     box(blockWidth, blockHeight, blockDepth);
     
     locZ+= 8;      //z-dimension shift towards the screen

   popMatrix();
   
  }
}

Code for the Hoop() class:

class Hoop {
  float outerHoopWidth, outerHoopHeight;
  float innerHoopWidth, innerHoopHeight;
  float locXH, locYH;
  float speed, acceleration;

  Hoop () {

    locXH = width/2;
    locYH = height/2;

    outerHoopWidth = 100;      //width of circle
    outerHoopHeight = outerHoopWidth;

    speed = 5;

  }

  void update() {
    
    if (outerHoopHeight + 400 == height) {
      speed = 10;
    }
    
    outerHoopWidth += speed;
    outerHoopHeight += speed;
    
  }

  void checkEdge() {
    if (outerHoopHeight + 300 == height) {    //if hoop is out of screen then restart it at the middle of the screen
      outerHoopWidth = 200;
      outerHoopHeight = 200;
    }

  }

  void drawHoop(int xFactor) {

    float circle1w = outerHoopWidth + xFactor;    //xFactor is to space out the different hoops

    float circle1h = outerHoopHeight + xFactor;

    noFill(); 
    stroke(#F5F5F5);
    strokeWeight(55);
    ellipse(locXH, locYH, circle1w, circle1h);
    
    
    
  }
}

 

 

Ryan’s Failed Brick Breaker Game

So, initially for this week’s assignment, I wanted to replicate my childhood game, which was called the Brick Breaker, as I really loved this game and played it almost everyday since I was small with my mom’s old Nokia phone.

Arkadroid Brick Breaker - Apps on Google Play

Initially, I sort of drew a sketch for myself to better follow along and code, as shown below. (Excuse the poor quality)

At first, I had no difficulty creating my user bar, the bouncing balls, and the blocks at the top. However, the main obstacle for me was being able to make the balls to be able to bounce off my user bar and also the rectangles (and causing them to disappear upon contact). I tried searching up tutorials, videos, websites, and even other online projects made before in order to help me understand collision detection and how to code it. However, in the end, I still wasn’t able to code the collision detection for my ellipses because I could not quite grasp and fully understand the mechanics behind coding for interactions between two different classes, which in this case would be the ellipses with the user bar and the rectangles. Furthermore, you will notice a lot of hardcoding because I wasn’t quite sure how to use arrays yet and I did not want to mess with something I wasn’t familiar with yet.

So, in the end, I decided to just add a bit more ellipses to make it look less boring as it did.

/***************MAIN SKETCH **************/
int x;
int y;
int blockx;
int blocky;
int rectWidth=50;
int rectHeight=50;


Bar userbar; 
RandomBlocks blocks;
RandomBlocks blocks2;
RandomBlocks blocks3;
RandomBlocks blocks4;
BouncingBall ball;
BouncingBall ball2;
BouncingBall ball3;
BouncingBall ball4;
BouncingBall ball5;
void setup() {
  size(1280, 720); 
  userbar= new Bar(); //calling for a constructor 
  blocks= new RandomBlocks();//constructor always needs two paranthesis ()
  ball= new BouncingBall(random(width), random(height), 50);
  ball2= new BouncingBall(random(width),random(height),50);
  ball3= new BouncingBall(random(width),random(height),50);//ALL OF THIS IS HARDCODING SORRY
  ball4= new BouncingBall(random(width),random(height),50);
  ball5= new BouncingBall(random(width),random(height),50);
  blocks2= new RandomBlocks();  blocks3= new RandomBlocks();
  blocks4= new RandomBlocks();
}



void draw() { 
  background(255);

  userbar.drawbar(); //using dot syntax, it calls a function for the object to do sth built by constructor 
  //Random blocks at the top
  blocks.drawblock();
  blocks2.drawblock2();
  blocks3.drawblock3();
  blocks4.drawblock4();
  //All the five balls 
  ball.bounceBall();
  ball2.bounceBall();
  ball3.bounceBall();
  ball4.bounceBall();
  ball5.bounceBall();
}

/***********Code for the controllable user blue bar***********/ 
class Bar {
  

  Bar() { //this is a constructor with paranthesis ()
  // in a constructor, it doesn't necessarily have to contain anything inside it, it just represents an object
    
  }
    void drawbar(){
    stroke(255, 0, 0);
    fill(0, 0, 255);
    rect(mouseX-250, height-50, 300, 20);
    }
}

/************Code for the five ellipses***************/
class BouncingBall {
  //float ballx=random(width);
  //float bally=random(height);
  float diameter;
  float xspeed;
  float yspeed;
  float x;
  float y;
  color c;

  BouncingBall(float tempX, float tempY, float tempDiameter) {//constructor
    x=tempX;
    y=tempY;    diameter=tempDiameter;
    xspeed=10;
    yspeed=10;
  }
  void bounceBall() {
    stroke(0);
    fill(211, 123, 231);
    ellipse(x, y, 50,50);

    if (x>=width || x<=0) {
      xspeed*=-1;
    }


    if (y>=height || y<=0) {
      yspeed*=-1;
    }

    x=x+xspeed;
    y=y+yspeed;
  }
 
}

/************Code for the rows of shiny blocks at the top**********/ 

class RandomBlocks {//for a class, the first letter has to be capitalized 

  RandomBlocks() {//a constructor
  }
  void drawblock() {

    for (x=0; x<width; x+=rectWidth+50) {
      stroke(0);
      fill(random(255), random(255), random(255));
      rect(x, y, rectWidth, rectHeight);
    }
  }

  void drawblock2() {
    for (x=0; x<width; x+=rectWidth+50) {
      stroke(0);
      fill(random(255), random(255), random(255));
      rect(x+50, y+60, rectWidth, rectHeight);
    }
  }

  void drawblock3() {
    for (x=0; x<width; x+=rectWidth+50) {
      stroke(0);
      fill(random(255), random(255), random(255));
      rect(x+100, y+120, rectWidth, rectHeight);
    }
  }

  void drawblock4() {    for (x=0; x<width; x+=rectWidth+50) {
      stroke(0);
      fill(random(255), random(255), random(255));
      rect(x+50, y+180, rectWidth, rectHeight);
    }
  }
}

 

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