Ha’ana Final Project Plan

April 21 Project Ideas

Final Project Idea 1

Final Project Idea 2

Final Project Idea 3

April 25

Committed features

Stretch Features

Status as of April 28

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.

Garden Screenshot

Implementation

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);
  }
}