xxxxxxxxxx
287
class flowerLily {
constructor(lilyX, lilyY, lilySize){
this.x = lilyX;
this.y = lilyY;
// size factored to 20%-40% of the lilypad size
this.flowerSize = lilySize * random(0.2, 0.4);
this.petals = random(8, 12); // number of petals
this.petalLayers = random(6,8) // petal laters
this.petalColors = ['lightyellow', '#F5D983','#ECC3BA']
// unique combination of colors
// why need? so a seperate function so the colors do not constantly change
this.layerColors = [];
// unique rotations for each layer
// why need? so it does not constantly rotate due to framerate
this.layerRotations = [];
// generating the different combination of colors
for (let i = 0; i < this.petalLayers; i+= 1) {
let colorIndex = int(random(0, this.petalColors.length)); // random color from petalColors array
this.layerColors.push(this.petalColors[colorIndex]); // store the selected color for each layer
}
// generate the rotations
for (let i = 0; i < this.petalLayers; i += 1){
this.layerRotations.push(radians(random(-15,15)));
}
}
drawFlower(){
push(); // saves the transformation currently on the canvas
// in terms of the lily's center (x,y) coords to rotate/ place
translate(this.x, this.y)
// for each layer
for (let layer = 0; layer < this.petalLayers; layer += 1) {
fill(this.layerColors[layer]) // color of the layer
push(); // saves the color
rotate(this.layerRotations[layer]); // set the rotation
// for each petal
for (let i = 0; i < this.petals; i+= 1){
push(); // saves the current rotation
rotate((TWO_PI/ this.petals) * i); // rotates each individual petal
// scaling factor decreased by 20% for each later
let scale = 1 - layer * 0.2;
ellipse(0, - this.flowerSize / 2 * scale, this.flowerSize / 3 * scale, this.flowerSize * scale);
pop(); // resets rotation and color for the next iteration
}
pop(); // resets the center position
}
// Flower center
fill('rgba(225,160,71,0.88)');
ellipse(0, 0, this.flowerSize / 2); // outer orange circle
fill('rgba(165,165,31,0.71)');
ellipse(0, 0, this.flowerSize / 3); // inner yellow circle
pop();
}
}
class floatingWaterLily{
constructor(xLily, yLily, xLilySpeed, yLilySpeed, lilySize, angle1, angle2, rotateSpeed){
// x-axis & y-axis
this.lilyX = xLily;
this.lilyY = yLily;
this.lilySpeedX = xLilySpeed;
this.lilySpeedY = yLilySpeed;
// width and height (for clarity)
this.lilyWid = lilySize;
this.lilyHei = lilySize;
// upper and lower degrees
this.lilyAngle1 = angle1;
this.lilyAngle2 = angle2;
this.lilyRotate = rotateSpeed;
this.radius = this.lilyWid / 2
// lily shade
this.lilyShadeArc = this.lilyWid * 0.9;
// flower instance
this.flower = new flowerLily(this.lilyX, this.lilyY, this.lilyWid);
}
update() {
// update the movement and rotation speed
this.lilyX += this.lilySpeedX;
this.lilyY += this.lilySpeedY;
this.lilyAngle1 += this.lilyRotate;
this.lilyAngle2 += this.lilyRotate;
// updates the flower position as the lily moves
this.flower.x = this.lilyX;
this.flower.y = this.lilyY;
}
drawLily() {
// outter darker ring of the lilypad
fill('rgb(42,140,42)');
noStroke();
arc(this.lilyX,
this.lilyY,
this.lilyWid,
this.lilyHei,
this.lilyAngle1,
this.lilyAngle2)
// inner lighter ring of the lilypad
fill("rgb(34,161,34)");
arc(this.lilyX,
this.lilyY,
this.lilyShadeArc,
this.lilyShadeArc,
this.lilyAngle1,
this.lilyAngle2)
// calls the Flower class to draw the flower
this.flower.drawFlower();
}
// collisions with the wall
checkWallCollision() {
if (this.lilyX < this.radius || width - this.lilyX < this.radius){
this.lilySpeedX *= -1
}
if (this.lilyY < this.radius || width - this.lilyY < this.radius){
this.lilySpeedY *= -1
}
}
// collisions with other lilies
checkLilyCollision(otherLily) {
// compute the distance between the current lily with the other lily
let distance = dist(this.lilyX, this.lilyY, otherLily.lilyX, otherLily.lilyY);
// if the distances between two lilys are less than the sum of the radius
if (distance < this.radius + otherLily.radius){
let tempSpeedX = this.lilySpeedX;
let tempSpeedY = this.lilySpeedY;
// exchange direction and speed of the colliding lilies
this.lilySpeedX = otherLily.lilySpeedX;
this.lilySpeedY = otherLily.lilySpeedY;
otherLily.lilySpeedX = tempSpeedX;
otherLily.lilySpeedY = tempSpeedY;
}
}
}
// ===================
// global values
let gLilyArr = [];
let gMaxSize = 130;
let numLily = 10;
let maxAttempts = 1000;
function setup() {
// at the top so there are values for width and height (that are used for the inital x-y positioning)
createCanvas(600, 600);
smooth();
generateLily();
}
// attempts to generate a number of lily (numLily) to fit the screens for 1000 attempts (not optimatized)
// Written with the help of CHATGPT
function generateLily(){
for (let i = 1; i < numLily + 1; i+= 1){
let validPosition = false;
let attempts = 0;
// while the maxAttemps and there is not a value position....
while (attempts < maxAttempts && !(validPosition)){
// initalizations:
let lilySize = random(80, 130)
let xLily = random(70, width - 70)
let yLily = random(70, height - 70)
// using smaller decemials to slow the turning without changing the framerate
let xLilySpeed = random() < 0.1 ? 0.02 : random(-0.2, 0.2);
let yLilySpeed = random() < 0.1 ? 0.03 : random(-0.3, 0.3);
let rotateSpeed = random() < 0.1 ? 0.009 : random(-0.009, 0.0009);
// inital ANGLES (start, end)
let degree1 = Math.random() * TWO_PI; // angle from 0 (inclusive) to 2pi (exclusive)
let degree2 = degree1 + random(PI/12, PI / 4); // control the cutout size
// need angle1 max and angle2 min so the arc will fill in the direction of a lilypad
let angle1 = max(degree1, degree2);
let angle2 = min(degree1, degree2);
// create a new instance
let newLilyObject = new floatingWaterLily(
xLily,
yLily,
xLilySpeed,
yLilySpeed,
lilySize,
angle1,
angle2,
rotateSpeed);
// call isLilyPositionValid function to check if any of lilypad overlap
// if the function returns True --> push the instance to the array and set validPosition to true
// if return False --> increment attempts
if (isLilyPositionValid(newLilyObject)){
gLilyArr.push(newLilyObject);
validPosition = true;
}
attempts += 1;
}
}
}
function isLilyPositionValid(newLilyObject){
let initalSpacing = random(3,9)
// console.log(initalSpacing)
// for each existing lily within the lily array
for (let existingLily of gLilyArr) {
/* Check if the distance is less than the sum of their radii (overlap condition). + initalSpacing because I wanted some inital distance between each of the lily pad*/
let distance = dist(newLilyObject.lilyX, newLilyObject.lilyY, existingLily.lilyX, existingLily.lilyY);
if (distance < newLilyObject.radius + existingLily.radius + initalSpacing) {
return false; // Overlap detected
}
}
return true; // No overlap detected
}
function lilyCollision(){
/* checks each lily with every other lily in the array to see if there are any collisions -- not optimized :/
*/
for (let i = 0; i < gLilyArr.length; i++){
for(let j = i + 1; j < gLilyArr.length; j++){
gLilyArr[i].checkLilyCollision(gLilyArr[j]);
}
}
}
function draw() {
background('#0CA3A6');
for (let i = 0; i < gLilyArr.length; i++){
gLilyArr[i].update();
gLilyArr[i].checkWallCollision();
gLilyArr[i].drawLily();
}
lilyCollision()
}
// ======================
// function waterLily() {
// fill('rgb(42,140,42)'); // color of the lilypad
// arc(100, 100, 80, 80, 0, 11 * PI/6);
// }
// function waterRipple(){
// return;
// }