xxxxxxxxxx
605
// class for cloud objects
// each cloud has x, y, size and display method
class cloud {
constructor(x, y, size) {
// store positions and size
this.x = x;
this.y = y;
this.size = size;
}
// show the cloud on screen
display() {
push();
fill(255, 255, 255, 240);
noStroke();
translate(this.x, this.y);
ellipse(0, 0, this.size, this.size * 0.6);
ellipse(this.size * 0.3, -this.size * 0.2, this.size * 0.8, this.size * 0.6);
ellipse(-this.size * 0.3, -this.size * 0.2, this.size * 0.8, this.size * 0.6);
pop();
}
}
// class for stone objects on walkway
// each stone has position and width/height
class stone {
constructor(x, y, w, h) {
this.x = x;
this.y = y;
this.w = w;
this.h = h;
}
// draw the stone
display() {
push();
fill(200, 200, 190);
noStroke();
translate(this.x, this.y);
ellipse(0, 0, this.w, this.h);
pop();
}
}
// class for fallen blossoms on walkway
// they are small circles
class fallenblossom {
constructor(x, y, r) {
this.x = x;
this.y = y;
this.r = r;
}
// display each blossom
display() {
noStroke();
fill(255, 180, 200);
ellipse(this.x, this.y, this.r, this.r);
}
}
// class for floating petals in the air
// petals move down slowly
class petal {
constructor(x, y, size, speedy, driftx, col) {
this.x = x;
this.y = y;
this.size = size;
this.speedy = speedy;
this.driftx = driftx;
this.col = col;
}
// move petals and wrap them if they go too far down
update() {
this.y += this.speedy;
this.x += this.driftx;
if (this.y > height + 10) {
this.y = -10;
this.x = random(width);
}
}
// draw petals as small ellipses
display() {
fill(this.col);
noStroke();
ellipse(this.x, this.y, this.size, this.size * 0.7);
}
}
// class for one branch of a cherry tree
// stores start and end points and thickness
class branch {
constructor(x1, y1, x2, y2, thick) {
this.x1 = x1;
this.y1 = y1;
this.x2 = x2;
this.y2 = y2;
this.thick = thick;
}
// draw one branch as a line
display() {
strokeWeight(this.thick);
stroke(100, 60, 20);
line(this.x1, this.y1, this.x2, this.y2);
}
}
// class for a single flower (cluster) on the cherry tree
// has a position, size, and angle
class flower {
constructor(x, y, size, angle) {
this.x = x;
this.y = y;
this.size = size;
this.angle = angle;
}
// draw the flower with 5 petals
display() {
push();
translate(this.x, this.y);
rotate(this.angle);
fill(255, 180, 200);
for (let i = 0; i < 5; i++) {
ellipse(0, -this.size * 0.3, this.size * 0.45, this.size);
rotate(TWO_PI / 5);
}
fill(255, 230, 240);
ellipse(0, 0, this.size * 0.4);
pop();
}
}
// class for a cherry tree
// it has branches and flowers in a fractal style
class cherrytree {
constructor(x, y, scalefactor) {
this.x = x;
this.y = y;
this.scalefactor = scalefactor;
this.branches = [];
this.flowers = [];
// generate full tree at creation
this.generateTree();
}
// start with a trunk and recursively add branches
generateTree() {
let trunklen = random(35, 50);
let trunkthick = random(5, 8);
let depth = 3;
this.growBranches(0, 0, -PI / 2, trunklen, trunkthick, depth);
}
// this recursive function makes branches until depth=0
// then adds flowers
growBranches(x1, y1, angle, length, thick, depth) {
let x2 = x1 + length * cos(angle);
let y2 = y1 + length * sin(angle);
// store this branch
this.branches.push(new branch(x1, y1, x2, y2, thick));
if (depth <= 0) {
this.addFlowerCluster(x2, y2);
return;
}
let branchcount = floor(random(2, 3));
for (let i = 0; i < branchcount; i++) {
let newangle = angle + random(-0.6, 0.6);
let newlen = length * random(0.6, 0.8);
let newthick = thick * random(0.6, 0.8);
this.growBranches(x2, y2, newangle, newlen, newthick, depth - 1);
}
}
// add a random cluster of flowers at a branch tip
addFlowerCluster(xcenter, ycenter) {
let numflowers = floor(random(4, 8));
for (let i = 0; i < numflowers; i++) {
let offsetx = random(-20, 20);
let offsety = random(-20, 20);
let size = random(6, 10);
let rot = random(TWO_PI);
this.flowers.push(new flower(xcenter + offsetx, ycenter + offsety, size, rot));
}
}
// display all branches and flowers
display() {
push();
translate(this.x, this.y);
scale(this.scalefactor);
// draw branches
for (let b of this.branches) {
b.display();
}
// draw flowers
noStroke();
for (let f of this.flowers) {
f.display();
}
pop();
}
}
// class for boy character
// has position and draws body parts
class boy {
constructor(x, y) {
this.x = x;
this.y = y;
}
// display boy with moving arms
display(armwave) {
push();
translate(this.x, this.y);
// head
fill(255, 210, 180);
ellipse(0, -40, 30, 36);
fill(100, 60, 20);
arc(0, -45, 32, 20, PI, 0);
ellipse(0, -50, 8, 6);
// eyes and mouth
fill(0);
ellipse(-5, -42, 3, 3);
ellipse(5, -42, 3, 3);
stroke(0);
strokeWeight(1);
noFill();
arc(0, -35, 7, 4, 0, PI);
noStroke();
// shirt
fill(130, 200, 255);
rect(-13, -25, 26, 35, 5);
// left arm
push();
translate(-13, -25);
rotate(sin(armwave) * 0.2);
rect(-5, 0, 8, 22, 5);
pop();
// right arm
push();
translate(13, -25);
rotate(cos(armwave) * 0.1);
rect(-3, 0, 8, 22, 5);
pop();
// legs
fill(80, 80, 80);
rect(-10, 10, 8, 25, 3);
rect(2, 10, 8, 25, 3);
pop();
}
}
// class for girl character
// has position and also draws her parts
class girl {
constructor(x, y) {
this.x = x;
this.y = y;
}
// display girl with moving arms
display(armwave) {
push();
translate(this.x, this.y);
// hair behind
fill(150, 80, 20);
ellipse(0, -42, 42, 48);
// head
fill(255, 220, 200);
ellipse(0, -40, 30, 36);
// top hair
fill(150, 80, 20);
arc(0, -46, 35, 26, PI, 0);
rect(-15, -48, 30, 5, 3);
// eyes and mouth
fill(0);
ellipse(-5, -42, 3, 3);
ellipse(5, -42, 3, 3);
stroke(0);
strokeWeight(1);
noFill();
arc(0, -35, 7, 4, 0, PI);
noStroke();
// dress
fill(255, 170, 200);
rect(-13, -25, 26, 35, 5);
// arms
push();
translate(-13, -25);
rotate(cos(armwave) * 0.1);
rect(-5, 0, 8, 22, 5);
pop();
push();
translate(13, -25);
rotate(sin(armwave) * 0.2);
rect(-3, 0, 8, 22, 5);
pop();
// legs
fill(100, 90, 150);
rect(-10, 10, 8, 25, 3);
rect(2, 10, 8, 25, 3);
pop();
}
}
// class for rose objects that move left
// each rose has a speed and small drift
class rose {
constructor(x, y) {
this.x = x;
this.y = y;
this.speed = random(2, 3);
this.offsety = random(-1, 1);
}
// update rose position
update() {
this.x -= this.speed;
this.y += this.offsety;
}
// draw the rose as a circle with a green stem
display() {
push();
translate(this.x, this.y);
fill(255, 0, 100);
ellipse(0, 0, 10, 10);
fill(0, 150, 0);
rect(-1, 0, 2, 10);
pop();
}
}
// now we declare global variables
// arrays of objects for the scene
let roses = [];
let petals = [];
let clouds = [];
let walkwayStones = [];
let fallenBlossoms = [];
let realisticTrees = [];
// spawn interval for roses
let spawnInterval = 40;
let frameCounter = 0;
// boy and girl characters
let boyCharacter;
let girlCharacter;
// angle for arm waving
let angle = 0;
// p5 setup function
function setup() {
createCanvas(600, 400);
// make clouds
generateClouds();
// walkway stones and fallen petals
generateWalkwayDetails();
// cherry trees
generateCherryBlossomTrees();
// floating petals in background
for (let i = 0; i < 40; i++) {
let c = random(1) < 0.5 ? color(255, 180, 200) : color(255, 240, 245);
petals.push(
new petal(
random(width),
random(height),
random(6, 12),
random(0.3, 1),
random(-0.3, 0.3),
c
)
);
}
// make boy and girl
boyCharacter = new boy(270, 300);
girlCharacter = new girl(330, 300);
noStroke();
}
// p5 draw function
function draw() {
// draw the sky
drawSunriseSky();
// show clouds
for (let c of clouds) {
c.display();
}
// forest floor
drawForestFloor();
// walkway
drawWalkway();
// torii gate
drawToriiGate();
// draw stones
for (let st of walkwayStones) {
st.display();
}
// draw fallen blossoms
for (let fb of fallenBlossoms) {
fb.display();
}
// draw cherry trees
for (let t of realisticTrees) {
t.display();
}
// move and draw petals
for (let p of petals) {
p.update();
p.display();
}
// text at top
fill(200, 0, 0);
textAlign(CENTER, CENTER);
textSize(30);
text("Happy Valentine's Day", width / 2, 50);
// wave arms
angle += 0.02;
// show boy and girl
boyCharacter.display(angle);
girlCharacter.display(angle);
// check if it's time to create a new rose
frameCounter++;
if (frameCounter % spawnInterval === 0) {
spawnRose();
}
// update and display each rose
for (let i = roses.length - 1; i >= 0; i--) {
roses[i].update();
roses[i].display();
// remove rose if off screen
if (roses[i].x < -30) {
roses.splice(i, 1);
}
}
}
// draw the gradient sky
function drawSunriseSky() {
let topColor = color(255, 150, 100);
let bottomColor = color(255, 220, 180);
let skyHeight = 200;
setGradient(0, 0, width, skyHeight, topColor, bottomColor);
}
// helper function to draw a vertical gradient
function setGradient(x, y, w, h, c1, c2) {
noFill();
for (let i = y; i <= y + h; i++) {
let inter = map(i, y, y + h, 0, 1);
let col = lerpColor(c1, c2, inter);
stroke(col);
line(x, i, x + w, i);
}
noStroke();
}
// make random clouds
function generateClouds() {
for (let i = 0; i < 8; i++) {
clouds.push(new cloud(random(width), random(20, 120), random(40, 80)));
}
}
// draw green ground
function drawForestFloor() {
fill(180, 240, 180);
rect(0, 200, width, 200);
}
// draw the walkway with wavy edges
function drawWalkway() {
fill(220, 220, 210);
noStroke();
beginShape();
for (let py = 200; py <= 400; py += 10) {
let wave = 12 * sin(py * 0.03);
vertex(210 + wave, py);
}
for (let py = 400; py >= 200; py -= 10) {
let wave = 12 * sin(py * 0.03 + 50);
vertex(390 + wave, py);
}
endShape(CLOSE);
stroke(140);
strokeWeight(2);
noFill();
beginShape();
for (let py = 200; py <= 400; py += 10) {
let wave = 12 * sin(py * 0.03);
vertex(210 + wave, py);
}
endShape();
beginShape();
for (let py = 200; py <= 400; py += 10) {
let wave = 12 * sin(py * 0.03 + 50);
vertex(390 + wave, py);
}
endShape();
noStroke();
}
// draw torii gate
function drawToriiGate() {
push();
translate(400, 220);
fill(200, 0, 0);
rectMode(CENTER);
rect(0, -30, 120, 12, 3);
rect(0, -40, 140, 8, 4);
fill(220, 0, 0);
rect(-40, 0, 15, 60);
rect(40, 0, 15, 60);
fill(180, 0, 0);
rect(0, -10, 90, 6);
pop();
}
// create stones and fallen blossoms
function generateWalkwayDetails() {
let numStones = 30;
for (let i = 0; i < numStones; i++) {
let sx = random(210, 390);
let sy = random(200, 400);
let w = random(10, 20);
let h = random(8, 14);
walkwayStones.push(new stone(sx, sy, w, h));
}
let numFallen = 40;
for (let i = 0; i < numFallen; i++) {
let fx = random(210, 390);
let fy = random(200, 400);
let size = random(5, 10);
fallenBlossoms.push(new fallenblossom(fx, fy, size));
}
}
// create random cherry blossom trees
function generateCherryBlossomTrees() {
let numTrees = 6;
for (let i = 0; i < numTrees; i++) {
let tx = random(width);
// avoid walkway area
while (tx > 190 && tx < 410) {
tx = random(width);
}
let ty = random(230, 385);
let s = random(0.8, 1.4);
realisticTrees.push(new cherrytree(tx, ty, s));
}
}
// spawn a rose near the girl's hand
function spawnRose() {
roses.push(new rose(girlCharacter.x + 25, girlCharacter.y - 12));
}