Assignment 3: Generative Art!

Concept

For the Third Assignment, my aim was to incorporate everything we have learned in class such as Arrays, collisions, and Classes, and use it to create an engaging piece of art. Initially, I planned to create a intricate animation, but realized that it was beyond the scope of the assignment. So, I settled on creating an animation that would pique the viewer’s interest and leave them wondering about how it was created.

I started off with replicating an in-class example of dropping snowflakes, which were reproduced, and I thought of replacing that with colored circles over a black background. This did look interesting, but seemed a bit basic – the circles were originally only rebounding from the walls (edges of the canvas). So I developed it further such that they collide with each other and exchange their speeds to seem interesting. Then, developing it further, I added a Neon Image on top of it and exchanged the images on collision as well.

I achieved a mixture of Chaos, but order. Everything had its set path – an order to follow. However, a lot of order among multiple objects that were not coordinated amongst each other caused Chaos. This is something I drew as an inspiration from an in-class discussion.

Embedded Canvas

The assignment outcome is attached as an embedded sketch as follows:

Try pressing the ‘m’ key! (In case it does not work, make sure to have clicked on the canvas)

Code

// Note: Press 'm' to view a messy functionality

let circles = []; //Array to store the circles
let noCircles = 20; //Number of circles that appear on the screen
let messup=false; //To enable the messy functionality

//Function to preload the graphics. This would load all the graphics before the setup function is run
function preload() {
  purple_img = loadImage('purple.png');
  green_img = loadImage('green.png');
  red_img = loadImage('red.png');

}

function setup() {
  createCanvas(400, 400);

  // Create objects of the class Circle and store them in an array
  for (let i = 0; i < noCircles; i++) {
    // A random positioning and size of each circle     
    let r = random(10, 30);
    let x = random(0, width-2*r);
    let y = random(0, height-2*r);


    // Check if a circle with the same position and size already exists
    let overlap = false;
    for (let j = 0; j < circles.length; j++) {
      let distance = dist(x, y, circles[j].x, circles[j].y);
      if (distance < r + circles[j].r) {
        overlap = true;
        break;
      }
    }

    // If there is no overlap, create a new circle
    if (!overlap) {
      circles[i] = new Circle(x, y, r);
    } else {
      i--;
    }
    
    // The above code blocks avoid any colliding circles being drawn in the initial stages
    
  }
}

function draw() {
  background(0);

  // Display objects in the array and update their position
  for (let i = 0; i < circles.length; i++) {
    circles[i].update();
    circles[i].display();

    // Check for collisions between circles
    for (let j = i + 1; j < circles.length; j++) {
      if (circles[i].collidesWith(circles[j])) {
        // If the messup functionality has been set, a different collision resolve function is called
        if (messup==true){
          circles[i].resolveCollisionWeird(circles[j]);
        }
        else{
          circles[i].resolveCollision(circles[j]); 
        }
      }
    }
  }
}

// Circle object
class Circle {
  constructor(x, y, r) {
    this.x = x;
    this.y = y;
    this.r = r;
    this.speed = random(1, 5);
    this.dx = random(-this.speed, this.speed);
    this.dy = random(-this.speed, this.speed);
    // A Number that checks the image_type to choose from the 3 types that we have
    this.image_type = int(random(1,4));
  }

  
  display() {
    // The circle image is displayed, depending on the image it got allotted in the constructor
    if(this.image_type%3==1){
      image(green_img, this.x, this.y, this.r*2, this.r*2);
    }
    else if(this.image_type%3==2){
      image(purple_img, this.x, this.y, this.r*2, this.r*2);
    }
    else{
      image(red_img, this.x, this.y, this.r*2, this.r*2);
    }
  }

  update() {
    this.x += this.dx;
    this.y += this.dy;

    // Check for collisions with the edges of the canvas
    if (this.x + this.r*2 > width || this.x < 0) {
      this.dx = -this.dx;
    }
    if (this.y + this.r*2 > height || this.y < 0) {
      this.dy = -this.dy;
    }
  }

  // Checks for collision with one another
  collidesWith(other) {
    // Calculates the distance between the current object and other objects
    let distance = dist(this.x, this.y, other.x, other.y);
    // If it is less than a threshold, it has collided!
    return distance < this.r + other.r;
  }

  // Once a collision is detected from the above function, the following function is called. It changes the speed of each, changes the image type with the other
  resolveCollision(other) {
    let tempX = this.dx;
    let tempY = this.dy;
    this.dx = other.dx;
    this.dy = other.dy;
    other.dx = tempX;
    other.dy = tempY;
    other.image_type = this.image_type;
  }
  
  // The following block of code was a modification of the above function which did not seem to work as I wanted it to, however it actually did something more artsy and interesting that I wanted to keep it as a hidden functionality to play with!  
  resolveCollisionWeird(other) {
    let angle = atan2(other.y - this.y, other.x - this.x);
    let overlap = 
        (this.r + other.r) - dist(this.x, this.y, other.x, other.y);
    this.x += cos(angle) * overlap / 2;
    this.y += sin(angle) * overlap / 2;
    other.x -= cos(angle) * overlap / 2;
    other.y -= sin(angle) * overlap / 2;
    
    let tempX = this.dx;
    let tempY = this.dy;
    this.dx = other.dx;
    this.dy = other.dy;
    other.dx = tempX;
    other.dy = tempY;
  }
  
}

// In the case that the key 'm' is pressed, the collision works differently.
function keyPressed(){
  if (key == 'm'){
   messup=true; 
  }
}

 

Problems

A small problem that I ran into was an issue where some of the images were colliding up to a point where an overlap could be observed. Since I started off with ellipses instead of the images, I was using their centre as a reference point, however, changing that to an image caused a shift in the reference point, which was now the top left of the image. This caused multiple issues including the one mentioned in the beginning of this paragraph.

It may not be noticeable to everyone, however I tried to resolve the issue and instead came up with something chaotic. You can press ‘m’ to see that!

The full canvas is linked here.

Author: Zunair

Senior @ NYU Abu Dhabi

Leave a Reply