The garden is beautiful. You can interactively plant trees, which grow and blow in the wind, and a flock of birds is present for a while and then goes away one by one.
float yoff = 0;
float xoff = 0;
class Seed {
PVector position;
PVector velocity;
boolean hitGround= false;
Seed(float x, float y) {
position = new PVector(x, y);
velocity = new PVector(0, 4);
}
void update() {
position.add(velocity);
if (position.y >= height) hitGround = true;
}
void display() {
if (!hitGround) {
fill(#9F8368);
stroke(#9F8368);
ellipse(position.x, position.y, 5, 5);
}
}
}
class Branch {
float length;
ArrayList<Branch> branches;
int maturity;
int generations;
int max;
int branchesCount;
float childAngleSeparation;
color c;
float xPos;
Seed seed;
Branch(float length_, color color_, float xPos_, Seed seed_) {
length = length_;
branches = new ArrayList<Branch>();
maturity = (int)random(80, 120);
generations = 0;
max = (int)random(1600, 4000);
branchesCount = (int)random(2, 4);
xPos = xPos_;
seed = seed_;
childAngleSeparation = radians(random(-25, 25));
if (color_ == color(0)) {
c = color(random(255), random(255), random(255));
} else {
c = color_;
}
}
void draw() {
yoff += 0.000005;
xoff += 0.000003;
// draw ourselves
stroke(#9F8368);
line(0, 0, 0, length);
noStroke();
// draw our children
pushMatrix();
translate(0, length);
rectMode(CENTER);
pushStyle();
fill(c);
ellipse(0, 0, 5, 5);
popStyle();
float childAngle = -1.0 * childAngleSeparation;
for (Branch child : branches) {
pushMatrix();
rotate(map(noise(xoff, yoff), 0, 1, -PI/4, PI/4));
rotate(childAngle);
child.draw();
popMatrix();
childAngle += childAngleSeparation;
}
popMatrix();
// we need to draw our children
}
void grow() {
if (generations < max) {
float newLength = length + 0.25;
if (newLength > maturity) {
// resize our children
for (Branch child : branches) {
child.grow();
}
} else if (newLength == maturity) {
for (int i = 0; i < branchesCount; ++i) {
branches.add(new Branch(0, c, xPos, seed));
}
length = newLength;
} else {
length = newLength;
}
}
generations++;
}
}
ArrayList<Branch> trees = new ArrayList<Branch>();
ArrayList<Seed> seeds = new ArrayList<Seed>();
Cluster cluster;
Stage stage;
Drop[] drops = new Drop[500];
boolean raining = false;
void setup() {
size(1000, 800);
cluster = new Cluster();
stage = new Stage(width/2, 200, 250);
for (int i = 0; i < 15; i++) {
cluster.addFworm(new Fworm(new PVector(width/2, height/2), 2.0f));
}
for (int i = 0; i < drops.length; i++) { // we create the drops
drops[i] = new Drop();
}
}
void draw() {
background(#60D2DE);
cluster.run();
for (Seed seed : seeds) {
seed.update();
seed.display();
}
if (trees.size() > 0) {
for (int i = 0; i < trees.size(); ++i) {
if (trees.get(i).seed.hitGround) {
pushMatrix();
translate(trees.get(i).xPos, height);
rotate(radians(180));
trees.get(i).draw();
popMatrix();
trees.get(i).grow();
}
}
}
if (raining) {
for (int i = 0; i < drops.length; i++) {
drops[i].fall(); // sets the shape and speed of drop
drops[i].show(); // render drop
}
}
pushStyle();
fill(#FAFF03);
noStroke();
ellipse(width-100, 75, 50, 50);
popStyle();
}
void mouseClicked() {
seeds.add(new Seed(mouseX, mouseY));
trees.add(new Branch(0.25, color(0), mouseX, seeds.get(seeds.size()-1)));
}
void keyPressed() {
if (key == ' ') {
raining = !raining;
}
}
class Drop {
float x; // x postion of drop
float y; // y position of drop
float z; // z position of drop , determines whether the drop is far or near
float len; // length of the drop
float yspeed; // speed of te drop
//near means closer to the screen , ie the higher the z value ,closer the drop is to the screen.
Drop() {
x = random(width); // random x position ie width because anywhere along the width of screen
y = random(-500, -50); // random y position, negative values because drop first begins off screen to give a realistic effect
z = random(0, 20); // z value is to give a perspective view , farther and nearer drops effect
len = map(z, 0, 20, 10, 20); // if z is near then drop is longer
yspeed = map(z, 0, 20, 1, 20); // if z is near drop is faster
}
void fall() { // function to determine the speed and shape of the drop
y = y + yspeed; // increment y position to give the effect of falling
float grav = map(z, 0, 20, 0, 0.2); // if z is near then gravity on drop is more
yspeed = yspeed + grav; // speed increases as gravity acts on the drop
if (y > height) { // repositions the drop after it has 'disappeared' from screen
y = random(-200, -100);
yspeed = map(z, 0, 20, 4, 10);
}
}
void show() { // function to render the drop onto the screen
float thick = map(z, 0, 20, 1, 3); //if z is near , drop is more thicker
strokeWeight(thick); // weight of the drop
stroke(138, 43, 226); // purple color
line(x, y, x, y+len); // draws the line with two points
}
}
class Cluster { //the 'flock' class from boids.
ArrayList fworms; // contains all objects
Cluster() {
fworms = new ArrayList();
}
void run() {
for (int i = 0; i < fworms.size(); i++) {
Fworm f = (Fworm) fworms.get(i);
f.run(fworms); //animates each individual object
}
}
void addFworm(Fworm f) { //the add object function
fworms.add(f);
}
}
class Fworm {
PVector loc;
PVector vel;
float max_wander_offset; //upper and lower ends of the range that generates a random number for changing
float wander_theta; //the randomized value assigned to the velocity
float wander_offset;
float maxspeed; //prevents the velocity from steadily increasing
float r;
Fworm(PVector l, float ms) {
loc = l.get();
max_wander_offset = 0.1;
wander_theta = random(TWO_PI);
vel = new PVector();
maxspeed = ms;
r = 6.0;
}
void run(ArrayList fworms) {
update();
render();
cohesion(fworms);
}
void update() {
wander_offset = random(-max_wander_offset, max_wander_offset);
wander_theta += wander_offset;
vel.x += cos(wander_theta);
vel.y += sin(wander_theta);
vel.limit(maxspeed);
if (dist(stage.x, stage.y, loc.x, loc.y) > 240) {
float ang = atan2(height/2-loc.y, width/2-loc.x);
wander_theta += 2*(ang-wander_theta)/20;
}
loc.add(vel);
}
PVector seek(PVector target) {
PVector desired = PVector.sub(target, loc); // A vector pointing from the position to the target
// Scale to maximum speed
desired.normalize();
desired.mult(maxspeed);
// Above two lines of code below could be condensed with new PVector setMag() method
// Not using this method until Processing.js catches up
// desired.setMag(maxspeed);
// Steering = Desired minus Velocity
PVector steer = PVector.sub(desired, vel);
steer.limit(maxspeed); // Limit to maximum steering force
return steer;
}
PVector cohesion (ArrayList <Fworm> fworms) {
float neighbordist = 50;
PVector sum = new PVector(0, 0); // Start with empty vector to accumulate all positions
int count = 0;
for (Fworm other: fworms) {
float d = PVector.dist(loc, other.loc);
if ((d > 0) && (d < neighbordist)) {
sum.add(other.loc); // Add position
count++;
}
}
if (count > 0) {
sum.div(count);
return seek(sum); // Steer towards the position
}
else {
return new PVector(0, 0);
}
}
void render() {
float direction = vel.heading2D() + PI/2;
fill(0); // the fill and stroke is only for the
stroke(255); // temporary placeholder triangle
pushMatrix();
translate(loc.x,loc.y);
rotate(direction);
//image(planarian, 0, 0);
beginShape(TRIANGLES); //I'm using the image above, but the triangles are just so anybody could test this
vertex(0, -r*2);
vertex(-r, r*2);
vertex(r, r*2);
endShape();
popMatrix();
}
}
class Stage {
float x;
float y;
float r;
Stage(float x_, float y_, float r_){
x = x_;
y = y_;
r = r_;
}
void display() {
fill(240);
noStroke();
ellipse(x, y, r*2, r*2);
}
}