/*This program generates a shadow puppet show of a field of grass
Program Structure:
draw->painter->move/blow/cycle
|
|-Bush->Blade
|-Grain->Stalk->Seed
|-Cluster->Lion->Bract
Way too much code. Didn't have time to optimize or comment correctly.
Sorry in advance.
*/
//Set up our background image, main arrays, and main parameters
PImage backdrop;
Bush[] grasses;
Grain[] grains;
Cluster[] clusters;
int vel, force;
void setup(){
size(400, 400);
backdrop = loadImage("backdrop.jpg");
//background(backdrop);
smooth();
noStroke();
fill(20,220);
frameRate(2); //As per the project
//Initialize our arrays with enough stuff to populate the screen
//Grass blades
grasses = new Bush[30];
for( int i = 0; i < grasses.length; i++){
grasses[i] = new Bush(int(random(-40,width+40)), int(random(2,12)));
}
//Tall grass
grains = new Grain[3];
for( int i = 0; i < grains.length; i++){
grains[i] = new Grain(int(random(-100,width+100)), int(random(2,5)));
}
//Dandy Lions
clusters = new Cluster[2];
for( int i = 0; i < clusters.length; i++){
clusters[i] = new Cluster(int(random(-100,width+100)), int(random(1,3)));
}
}
void draw(){
background(backdrop);
//Translate the matrix so the origin is in the bottom left.
translate(0,height);
scale(1,-1);
//Randomize the wind force and the scroll velocity, ideally,
//these would be mouse driven.
vel = int(random(-20, 0));
if( force > 0){
force = force + int(random(-10, 0));
}else if( force < -100){
force = force + int(random(0, 10));
}else {
force = force + int(random(-10, 10));
}
//Our main drawing alg.
painter(vel, force);
}
void painter(int speed, int wind){
move(speed); //move everything.
blow(wind); //pass the wind value to the elements in the arrays
cycle(); //push any object that goes too far left to the right.
//and reset it's values.
}
//move, blow, and cycle are just for loops
//In the order of grass, grain, lions
void move(int speed){
for( int i = 0; i < grasses.length; i++) {
grasses[i].display();
grasses[i].move(speed);
}
for( int i = 0; i < grains.length; i++) {
grains[i].display();
grains[i].move(speed);
}
for( int i = 0; i < clusters.length; i++) {
clusters[i].display();
clusters[i].move(speed);
}
}
void blow(int wind){
for( int i = 0; i < grasses.length; i++) {
grasses[i].blow(wind);
}
for( int i = 0; i < grains.length; i++) {
grains[i].blow(wind);
}
for( int i = 0; i < clusters.length; i++) {
clusters[i].blow(wind);
}
}
void cycle(){
for( int i = 0; i < grasses.length; i++) {
if (grasses[i].x < -40){
grasses[i].init(width+int(random(40,80)),int(random(2,12)));
}
}
for( int i = 0; i < grains.length; i++) {
if (grains[i].x < -100){
grains[i].init(width+int(random(100,180)),int(random(2,5)));
}
}
for( int i = 0; i < clusters.length; i++) {
if (clusters[i].x < -100){
clusters[i].init(width+int(random(100,180)),int(random(1,3)));
}
}
}
//Basic blade of grass
class Blade{
int x;
int pointXOff, pointYOff;
int wind;
Blade(int xPos, int xOff, int yOff){
x = xPos;
//assign the point for the tip based on parameters
pointXOff = xOff + x;
pointYOff = yOff;
}
void display(){
beginShape();
vertex(x,0);
bezierVertex(x-6,10, x, pointYOff/2, pointXOff+wind,pointYOff);
bezierVertex(x, pointYOff/2, x+12,10, x+6,0);
endShape();
}
}
//Bundle of grass
class Bush{
int x, bladeCount;
Blade[] bladeArray;
int wind;
Bush(int xpos, int count) {
init(xpos, count);
}
//Initialization rig.
void init(int xpos, int count) {
x = xpos;
bladeCount = count;
bladeArray = new Blade[bladeCount];
for( int i = 0; i < bladeCount; i++){
bladeArray[i] = new Blade(int(random(x-15, x+15)),int(random(-40,40)),int(random(60,180)));
}
}
//React to wind
void blow(int strength){
wind = strength;
for ( int i = 0; i < bladeCount; i++){
bladeArray[i].wind = strength;
}
}
//Scroll when told to by move()
void move(int offset) {
x = x + offset;
for ( int i = 0; i < bladeCount; i++){
bladeArray[i].x = bladeArray[i].x + offset;
bladeArray[i].pointXOff = bladeArray[i].pointXOff + offset;
}
}
//Walk through the array and show blades.
void display() {
for( int i = 0; i < bladeCount; i++){
bladeArray[i].display();
}
if (1 < wind || -1 > wind){
wind = (wind*4)/5;
blow(wind);
}
if (1 >= wind && -1 <= wind){
wind = 0;
blow(wind);
}
}
}
//Long leaf/seed at the end of a tall stalk of grass
class Seed{
int x, y, u, v, flipper;
Seed(int spawnX, int spawnY, int flip){
x = spawnX;
y = spawnY;
flipper = flip;
u = x + flipper*int(random(-80,-12));
v =spawnY+int(random(-30,30));
}
void display(int offset){
beginShape();
vertex(x+offset+2*flipper,y);
bezierVertex((x+u)/2+offset, (y+v)/2+3, (x+u)/2+offset, (y+v)/2+3, u+offset,v+offset/3);
bezierVertex((x+u)/2+offset, (y+v)/2-3, (x+u)/2+offset, (y+v)/2-3, x+offset+2*flipper,y);
endShape();
}
}
//Stalk that is parent to the seeds, nothing special.
class Stalk{
int x;
int pointXOff, pointYOff;
int wind;
int seedCount;
Seed[] seedArray;
Stalk(int xPos,int xOff,int yOff, int seeds){
init(xPos, xOff, yOff, seeds);
}
void init(int xPos,int xOff,int yOff, int seeds){
x = xPos;
seedCount = seeds;
pointXOff = xOff + x;
pointYOff = yOff;
int flipper = int(random(1,5));
if (0 == flipper % 2) {
flipper = -1;
} else {
flipper = 1;
}
seedArray = new Seed[seedCount];
for( int i = 0; i < seedCount; i++){
seedArray[i] = new Seed(pointXOff, pointYOff, flipper);
}
}
void move(int offset) {
x = x + offset;
pointXOff = pointXOff + offset;
for( int i = 0; i < seedCount; i++){
seedArray[i].x = seedArray[i].x+ offset;
seedArray[i].u = seedArray[i].u+ offset;
}
}
void blow(int strength){
wind = strength;
}
void display(){
for( int i = 0; i < seedCount; i++){
seedArray[i].display(wind);
}
stroke(20,220);
strokeWeight(2);
noFill();
bezier(x,0, x-6,pointYOff/2, x, pointYOff/2, pointXOff+wind,pointYOff);
fill(20,220);
noStroke();
if (-1 > wind){
wind = (wind*4)/5;
blow(wind);
}
}
}
//Bundle of stalks.
class Grain{
int x, stalkCount;
Stalk[] stalkArray;
int wind;
Grain(int xpos, int count) {
init(xpos, count);
}
void init(int xpos, int count) {
x = xpos;
stalkCount = count;
stalkArray = new Stalk[stalkCount];
for( int i = 0; i < stalkCount; i++){
stalkArray[i] = new Stalk(int(random(x-10, x+10)),int(random(-40,40)),int(random(80,220)),int(random(2,12)));
}
}
void blow(int strength){
wind = strength;
for ( int i = 0; i < stalkCount; i++){
stalkArray[i].wind = strength;
}
}
void move(int offset) {
x = x + offset;
for ( int i = 0; i < stalkCount; i++){
stalkArray[i].move(offset);
}
}
void display() {
for( int i = 0; i < stalkCount; i++){
stalkArray[i].display();
}
if (1 < wind || -1 > wind){
wind = (wind*4)/5;
blow(wind);
}
if (1 >= wind && -1 <= wind){
wind = 0;
blow(wind);
}
}
}
//Dandy Lion tuft. Fairly simple alone.
class Bract{
int x, y, u, v, flipper, dia;
Bract(int spawnX, int spawnY){
x = spawnX;
y = spawnY;
dia = int(random(15,25));
u = x + int(random(-8,8));
v = y + int(random(-3,8));
}
void display(int offset){
stroke(20,100);
strokeWeight(2);
noFill();
bezier(x+offset+2*flipper,y, (x+u)/2+offset, (y+v)/2+3, (x+u)/2+offset, (y+v)/2+3, u+offset,v);
noStroke();
fill(20,40);
ellipse(u+offset, v, dia, dia);
fill(20,220);
}
//Throw to the wind when told to
void scatter(int wind){
x = x + int(random(wind-25, 20));
y = y + int(random(-20, -wind));
u = x + int(random(-8,8));
v = y + int(random(-3,8));
}
}
//The dandy lion shaft. The Lion will loose its
//bracts if the wind is stronger than the threshold.
class Lion{
int x;
int pointXOff, pointYOff;
int wind;
int bractCount;
int thresh = 45;
boolean stuck;
Bract[] bractArray;
Lion(int xPos,int xOff,int yOff, int bracts){
init(xPos, xOff, yOff, bracts);
}
void init(int xPos,int xOff,int yOff, int bracts){
stuck = true;
x = xPos;
bractCount = bracts;
pointXOff = xOff + x;
pointYOff = yOff;
bractArray = new Bract[bractCount];
for( int i = 0; i < bractCount; i++){
bractArray[i] = new Bract(pointXOff, pointYOff);
}
}
void move(int offset) {
x = x + offset;
pointXOff = pointXOff + offset;
for( int i = 0; i < bractCount; i++){
bractArray[i].x = bractArray[i].x+ offset;
bractArray[i].u = bractArray[i].u+ offset;
}
}
void blow(int strength){
wind = strength;
}
void display(){
for( int i = 0; i < bractCount; i++){
bractArray[i].display(wind);
}
stroke(20,220);
strokeWeight(2);
noFill();
bezier(x,0, x-6,pointYOff/2, x, pointYOff/2, pointXOff+wind,pointYOff);
noStroke();
fill(20,220);
ellipse(pointXOff+wind,pointYOff, 8,4);
//If the tufts are attached, and the wind is strong,
//toss them
if( wind < -thresh && stuck){
for( int i = 0; i < bractCount; i++){
bractArray[i].scatter(wind);
stuck = false;
}
//if they're already tossed, keep tossing.
} else if (!stuck){
for( int i = 0; i < bractCount; i++){
bractArray[i].scatter(wind);
}
}
if (-1 > wind){
wind = (wind*4)/5;
blow(wind);
}
}
}
//Bundle of dandy lions, virtually identical to Grain, probably should
//Have merged the two.
class Cluster{
int x, lionCount;
Lion[] lionArray;
int wind;
Cluster(int xpos, int count) {
init(xpos, count);
}
void init(int xpos, int count) {
x = xpos;
lionCount = count;
lionArray = new Lion[lionCount];
for( int i = 0; i < lionCount; i++){
lionArray[i] = new Lion(int(random(x-10, x+10)),int(random(-20,20)),int(random(60,160)),int(random(12,24)));
}
}
void blow(int strength){
wind = strength;
for ( int i = 0; i < lionCount; i++){
lionArray[i].wind = strength;
}
}
void move(int offset) {
x = x + offset;
for ( int i = 0; i < lionCount; i++){
lionArray[i].move(offset);
}
}
void display() {
for( int i = 0; i < lionCount; i++){
lionArray[i].display();
}
if (1 < wind || -1 > wind){
wind = (wind*4)/5;
blow(wind);
}
if (1 >= wind && -1 <= wind){
wind = 0;
blow(wind);
}
}
}