oop & arrays

THE SKETCH 

The completed sketch that utilizes both arrays and object oriented programming is shown below. The link to the sketch on the p5.js web editor can be found here.

A screen capture that I particularly like is also shown below:

 

INSPIRATION & DESIGN: 

The inspiration for this piece came to me when I came across a time lapse video showcasing the growth of different crystals. I was surprised at how how beautiful and calming the video was; one would think that there isn’t anything too special about the process of crystallization, but when looked at closely, it is revealed how mesmerizing and almost fantastical it is, and you can’t help but admire how amazing nature is. I also think that crystal growth works quite well for being translated to something that is generative: crystals tend to start off small and exponentially grow, but even after they are fully “bloomed”, the process still continues so long the environment remains unchanged.

For this project, I wanted to create something that mimicked the process of water turning into ice or snowflakes forming. I found this sketch to be very challenging in terms of making the crystallization to look as biologically accurate as possible, as well just generally making the sketch look aesthetically pleasing. However, I was really interested and committed to the sketch because using code to mimic processes found in nature, such as the golden ratio, trees, and Fibonacci numbers, has always sparked my curiosity.

 

CODING SPECIFICS

The sketch is built using a user defined Crystal class, and instances of this class are put into a single array (in the setup() function), in which methods of the class are applied on each object in the array (in the draw() function).

In setup(): 

function setup() {
  createCanvas(700, 500); 
  
 //initializing an array of little crystals 
 for (let i = 0; i < 300; i++) {
    start_x = random(-100, 100);   //give each line random points
    start_y = random(-100, 100);
    end_x = random(-50, 50); 
    end_y = random(-50, 50);
    max_opacity = random(20, 100);  //set each line to have a maximum opacity to make it look more natural  
    cluster.push(new Crystal(start_x,start_y, end_x, end_y, max_opacity));
  }

}

In draw(): 

function draw() {
  translate(width/2, height/2);
  background(116, 150, 183);
  //background(0); 

  //iterate over each object in the array and apply methods to it
  for (let i = 0; i < cluster.length; i++) {
    cluster[i].move();
    cluster[i].display();
  }
  
}

Each individual crystal is actually just a single line, with semi-randomized starting and ending (x,y) positions, a randomized opacity, a jitter variable, and a direction variable (this.direction). These variables define where each line starts and ends, how opaque the stroke color is, and how much each line moves, and in what direction the line lengthens over time. It also has a a time keeper value (this.age) and a boolean value that defines when to draw more lines (this.has_child). These variables are what creates the “growth” effect are described in more detail later on in this post.

Crystal class constructor:

constructor(star_x, start_y, end_x, end_y, max_opacity) { 
  this.start_x = start_x;  //start x pos
  this.start_y = start_y; //start y pos
  this.end_x = end_x; //end x pos
  this.end_y = end_y; //end y pos 
  
  this.max_opacity = max_opacity; 
  this.jitter=0.2;  //variable that causes it to move slightly
  this.opacity = 0; 
  
  //variable that helps lengthen the lines 
  this.direction = atan2(this.end_y-this.start_y, this.end_x-this.start_x);
  this.age = frameCount; //time keeper 
  this.has_child = false; //boole value 
}

In the beginning of the draw() function, we iterate over the array of objects and call methods move() and display(), which do exactly as they sound: they move each line based on the jitter value and display them by increasing the opacity from 0 to the max opacity value each frame.

However, I created an additional method called grow_children() that is only called inside the Crystal class, which I think is the most interesting part of the code. This method essentially keeps initializing and adding new objects into the cluster array, where the move(), display(), and grow_children() functions are then applied to these new objects, and so on. Here is how it works:

Inside move() method, we create an if statement that evaluates to true only when the object that was just created has not drawn any children and when 20 frames have passed since the object was initialized (defined by this.age). If both these conditions are met, we call grow_children():

if(!this.hasChild && frameCount-this.age > 20){
  this.grow_children(); 
}

Then, inside grow_children(), we redefine the parameters start_x, start_y, end_x, and end_y so that the growth of the lines will spread outwards towards the edge of the canvas. Using these new parameters, we then push a new Crystal object into the cluster array:

grow_children(){ 
  //adjusting the x and y positions of the child lines
  start_x = this.end_x; 
  start_y = this.end_y;
  end_x = random(start_x-100, start_x+100); 
  end_y = random(start_y-100, start_y+100);
 
  max_opacity = random(20, 100); 
    
    //initiate new objects and set this.hasChild to True
    cluster.push(new Crystal(start_x,start_y, end_x, end_y, max_opacity));
    this.hasChild = true;
}

Finally, this.hasChild is set to true, and the process repeats.

REFLECTIONS

As I was making this sketch, I realize that what I was envisioning in my head was a little ambitious given the time frame. I became very wrapped up in trying to make it look realistic as possible, and I still don’t believe I have accomplished that. I would love to continue working on this project, and possibly make different variations of it too. Some improvements that I would like to work on are the shapes of the crystals. I chose to use lines for simplicity, but I would like to experiment with long triangles, skinny rectangles, or even my own custom shape. Another issue I want to tackle is how there is only 1 array of objects. Initially I envisioned having several clusters that would appear on different parts of the screen at different times, which would more accurately mirror how water crystallizes. Unfortunately, making one “cluster” was already very complex, and in the end I decided to just focus on one.

One thought on “oop & arrays”

  1. Oh this one looks really nice! And good code! It’s kind of a hypnotic effect as it moves. I would try playing with the density of the lines (how many there are) and maybe try colouring some of the lines to see if you get more of the shape coming out. Excellent!

Leave a Reply