xxxxxxxxxx
/*
FOUR SEASONS
By Tram Tran (ttra6156)
September 9th, 2018
*/
//////////////////////////////////////////////
//////////// VARIABLE DEFINITION /////////////
//////////////////////////////////////////////
// Variables related to the branches and the leaves
var leaves = [];
var firstCycle = true;
var joints = [];
var diff;
var xoff;
var yoff = 0; // This is used to create Perlin noise effect
var seed = 5; // The value of randomSeed
// Variables related to season change
var counter = 0;
var timer;
var seasonColor;
var myFont;
//////////////////////////////////////////////
//////////////// CREATING LEAF ///////////////
//////////////////////////////////////////////
// To create a leaf, the function needs several parameters as follows:
// (mass) Every leaf has a mass with arbitrary unit of measurement
// (id) ID of the leaf
// (joint_id) ID of the branch joint where the leaf is (very loosely) attached to
// (len) The length of the branch
function Leaf(m, id, joint_id, len) {
this.mass = m; // The mass of the leaf
this.id = id; // ID of the leaf
this.joint_id = joint_id; // ID of the branch joint
this.velocity = createVector(0, 0); // Velocity of the leaf
this.acceleration = createVector(0, 0); // Acceleration of the leaf
// Position of the leaf is defined by the position of the branch joint, with some random differences
// This reset function helps the leaves to grow again as they would fall down during winter
this.reset = function(){
this.pos_diff = createVector(random(-len/2, len/2), random(-len/2, len/2));
this.position = createVector(joints[this.joint_id][0], joints[this.joint_id][1]).add(this.pos_diff);
}
// Position of the leaf would be updated once there are changes to its acceleration
this.update = function() {
this.velocity.add(this.acceleration);
this.position.add(this.velocity);
this.acceleration.mult(0);
this.velocity.limit(0.03);
}
// There are several physical forces that make the leaf move such as wind and gravity
// These forces would then be added to the acceleration (accumulated forces)
this.applyForce = function(force) {
force.div(this.mass);
this.acceleration.add(force);
}
// After falling down from the tree, the leaf would stay on the ground (canvas edge)
this.checkEdges = function() {
if (this.position.y > (height - 10)) {
this.position.y = height - 10;
}
}
// Drawing the leaf
this.display = function() {
noStroke(); // To create the water painting effect, no stroke is needed
fill(seasonColor); // The color of the leaf will be changed in every season
ellipse(this.position.x, this.position.y, this.mass * 5, this.mass * 5); // Assuming that the size is relative to the weight
}
}
//////////////////////////////////////////////
//////////////// CREATING TREE ///////////////
//////////////////////////////////////////////
// To create a tree with different branches, the function requires several parameters as follows:
// Position of the very branch (x, y)
// Rotation angle of the branch (rot)
// The differences of the branch's length (diff is a random number between 0 and 5)
// The horizontal offset of branch's movement (xoff)
function Branch(x, y, rot, len, diff, xoff) {
joint_id = joints.length; // The total number of joints in the array
var sw = map(len, 0, 200, 1, 20); // The thickness of the branch is relative to its size
strokeWeight(sw); // Specify the thickness
stroke(90, 49, 57); // Specify the color
var nPoint = p5.Vector.fromAngle(rot).mult(len); // Make a new 2D vector from an angle
var nx = x + nPoint.x; // Get the coordinate-x from the vector nPoint
var ny = y + nPoint.y; // Get the cooridnate-y from the vector nPoint
rot = p5.Vector.fromAngle(rot).mult(2).heading(); // Change the rotation angle (heading help to calculate the angle of rotation for this 2D vector)
line(x, y, nx, ny); // Draw the branch
joints.push([nx, ny]); // Keep track of all the joints by an array
// Run a for loop to create the leaves
// Condition of the loop: (1) the length of the branch smaller than 100 and (2) firstCycle is true.
if (firstCycle && len < 100) {
for (var i = 0; i < len / 5 * 2; i++) {
leaves.push(new Leaf(random(2, 5) * 0.35, leaves.length, joint_id, len));
}
}
// Set the angle of the branch with Perlin noise values
for (var j = 0; j < joints.length; j++) {
angle = map(noise(xoff + j / 10, yoff), 0, 1, radians(0), radians(60));
}
// The length of the branch will get shorter and shorter
len = len * 2 / 3 * map(noise(diff), 0, 1, 0.5, 1.5);
// Using recursion to create the right and left branch
if (len > 10) {
Branch(nx, ny, rot + angle, len, diff + 0.3, xoff);
Branch(nx, ny, rot - angle, len, diff + 1, xoff);
}
}
//////////////////////////////////////////////
//////////////// TIME COUNTER ////////////////
//////////////////////////////////////////////
// This function increases the value of counter after every interval
// It is use as a parameter in the setInterval(timeIt, 5500) in the Setup
function timeIt() {
counter++;
}
//////////////////////////////////////////////
//////////////// CHANGE SEASON ///////////////
//////////////////////////////////////////////
// This function determines how every season looks like (leaves' color, background color and Haiku poem)
// It works based on the modulo operation with 0-Spring, 1-Summer, 2-Autumn, 3-Winter 4-Falling Leaves
function changeSeason() {
// Setting typography of the season name (Spring/Summer/Autumn/Winter)
textSize(64);
textFont(myFont);
fill(0);
transparent = map(noise(diff), 0, 1, 0, 255); // To create different shades of color for the leaves
var white = color(255,255,255); // Every season has a gradient background with White as one of two colors in the spectrum
//Spring
if (counter % 5 == 0) {
seasonColor = color(231, 84, 128, transparent); // Leaves' color in Spring
var lightPink = color(249, 210, 212); // Spring's background color
setGradient(0, 0, windowWidth, windowHeight, white, lightPink); // Gradient spectrum from White to Light Pink
fill(0);
noStroke();
text("Spring", 100, 150);
textSize(31);
text("The fragrant orchid", 100, 190);
text("Into a butterfly’s wings", 100, 220);
text("It breathes the incense", 100, 250);
text("Basho (1958)", 130, 290);
//Summer
} else if (counter % 5 == 1) {
seasonColor=color(136,176,75,transparent); // Leaves' color in Summer
var lightGreen = color(245, 253, 233); // Summer's background color
setGradient(0, 0, windowWidth, windowHeight, white, lightGreen); // Gradient spectrum from White to Light Green
fill(0);
noStroke();
text("Summer", 100, 150);
textSize(31);
text("Toward the sun’s path", 100, 190);
text("Hollyhock flowers turning", 100, 220);
text("In the rains of summer", 100, 250);
text("Basho (1958)", 130, 290);
// Autumn
} else if (counter % 5 == 2) {
seasonColor=color(243,174,0,transparent); // Leaves' color in Autumn
var lightOrange = color(251, 243, 195); // Autumn's background color
setGradient(0, 0, windowWidth, windowHeight, white, lightOrange); // Gradient spectrum from White to Light Orange
fill(0);
noStroke();
text("Autumn", 100, 150);
textSize(31);
text("On a bare branch", 100, 190);
text("A crow is perched", 100, 220);
text("Autumn evening", 100, 250);
text("Basho (1958)", 130, 290);
// Winter last longer than other seasons as it has falling leaves
} else if (counter % 5 == 3||counter % 5 == 4) {
seasonColor=color(255,255,255,transparent); // Leaves' color in Winter
var black = color(0); // Winter's background color
setGradient(0, 0, windowWidth, windowHeight, white, black); // Gradient spectrum from White to Black
fill(0);
noStroke();
text("Winter", 100, 150);
textSize(31);
text("The winter sun", 100, 190);
text("Frozen on the horse", 100, 220);
text("My shadow", 100, 250);
text("Basho (1958)", 130, 290);
}
}
//////////////////////////////////////////////
//////////////// SET GRADIENT ////////////////
//////////////////////////////////////////////
// Forked from the example in the reference
// https://p5js.org/examples/color-linear-gradient.html
function setGradient(x, y, w, h, c1, c2) {
noFill();
for (let i = y; i <= y+h; i++) {
var inter = map(i, y, y+h, 0, 1);
var c = lerpColor(c1, c2, inter);
stroke(c);
line(x, i, x+w, i);
}
}
//////////////////////////////////////////////
////////// PRELOAD, SETUP AND DRAW ///////////
//////////////////////////////////////////////
//Preload
function preload() {
myFont = loadFont('ReenieBeanie.ttf');
}
// Setup
function setup() {
createCanvas(windowWidth, windowHeight);
diff = random(0, 5);
setInterval(timeIt, 5500);
}
// Start drawing
function draw() {
joints = []; // Empty the joints array
randomSeed(seed); // Stablize the randomness of the leaves
yoff += 0.002; // Increase the Perlin noise offset after every frame
changeSeason(); // Set season background and define leaves's color
Branch(width / 2, height, 3* PI/2, 160, diff, 0); // Draw the tree, initial rotation angle is 270 degrees (which is upwards)
firstCycle = false; // firstCycle set to false to avoid further creation of the leaves
for (var i = 0; i < leaves.length; i++) {
wind = createVector(random(4, 6), 0); // Create wind force
gravity = createVector(0, 6 * leaves[i].mass); // Gravity varies based on mass
/* According to Daniel Shiffman (2012), "friction points in the opposite direction of velocity
this would mean taking the velocity vector, normalizing it, and multiplying by -1.
The coefficient (c) establishes the strength of a friction force for a particular surface".
I don't actually notice the difference between applying friction and not applying friction
in this project but let's just keep it for now */
var c = 0.01; // Arbitrary constant
friction = leaves[i].velocity.copy();
friction.normalize();
friction.mult(-1);
friction.mult(c);
/* At the end of winter, the leaves will fall down from the tree and land on the ground
We apply some basic forces on the leaves, which include air friction, gravity and wind
When the leaves are on the tree they're supposed to get affected by forces and rustle.
However I decide the leave that out in this project */
if (counter % 5 == 4) { // Apply forces on the leaves
leaves[i].applyForce(friction);
leaves[i].applyForce(gravity);
leaves[i].applyForce(wind);
} else { // Regrow the leaves
leaves[i].reset();
}
leaves[i].update(); // Update position of the leaves
leaves[i].display(); // Draw the leaves
leaves[i].checkEdges(); // Ensure the leaves stay on the ground
}
}