class Hammer extends Flock{
float headSize;
PVector posHead, posBody, posMed, posTail, posEnd;
float angHead, angBody, angMed, angTail;
Hammer(PVector tempPos, PVector tempVel, color tempCol, float tempHeadSize){
super(tempPos,tempVel,tempCol);
headSize = tempHeadSize;
posHead = pos.get();
posBody = PVector.add(posHead, new PVector(0,2.1*headSize));
posMed = PVector.add(posBody, new PVector(0,5.5*headSize));
posTail = PVector.add(posMed, new PVector(0,2.2*headSize));
posEnd = PVector.add(posTail, new PVector(0,1.8*headSize));
angHead = 0;
angBody = 0;
angMed = 0;
angTail = 0;
}
void update(){
super.update();
posHead = new PVector(pos.x,pos.y);
angHead = atan2(vel.y,vel.x);
if(isClose){
angHead += 0.5*sin(0.4*frameCount);
isClose = false;
}
angHead += 0.2*sin(0.2*frameCount);
posBody = PVector.sub(posHead,new PVector(2.1*headSize*cos(angHead),2.1*headSize*sin(angHead)));
angBody = atan2(posBody.y-posMed.y,posBody.x-posMed.x);
posMed = PVector.sub(posBody, new PVector(5.5*headSize*cos(angBody),5.5*headSize*sin(angBody)));
angMed = atan2(posMed.y-posTail.y,posMed.x-posTail.x);
posTail = PVector.sub(posMed, new PVector(2.2*headSize*cos(angMed),2.2*headSize*sin(angMed)));
angTail = atan2(posTail.y-posEnd.y,posTail.x-posEnd.x);
posEnd = PVector.sub(posTail, new PVector(1.8*headSize*cos(angTail),1.8*headSize*sin(angTail)));
}
void paintDetail(){
noStroke();
fill(col);
paintHead(posHead,angHead);
paintBody(posBody,angBody);
paintMed(posMed,angMed);
paintTail(posTail,angTail);
}
void paintHead(PVector cen, float ang){
pushMatrix();
translate(cen.x,cen.y);
rotate(HALF_PI + ang);
beginShape();
curveVertex(0,-0.5*headSize);
curveVertex(1.1*headSize,-0.4*headSize);
curveVertex(2.0*headSize,-0.3*headSize);
curveVertex(2.3*headSize,-0.3*headSize);
curveVertex(2.25*headSize,0.4*headSize);
curveVertex(2.1*headSize,0.5*headSize);
curveVertex(1.5*headSize,0.3*headSize);
curveVertex(0.9*headSize,0.4*headSize);
curveVertex(0.8*headSize,0.5*headSize);
curveVertex(0.9*headSize,1.3*headSize);
curveVertex(0.8*headSize,2.1*headSize);
curveVertex(0.4*headSize,1.9*headSize);
curveVertex(0,1.8*headSize);
curveVertex(-0.4*headSize,1.9*headSize);
curveVertex(-0.8*headSize,2.1*headSize);
curveVertex(-0.9*headSize,1.3*headSize);
curveVertex(-0.8*headSize,0.5*headSize);
curveVertex(-0.9*headSize,0.4*headSize);
curveVertex(-1.5*headSize,0.3*headSize);
curveVertex(-2.1*headSize,0.5*headSize);
curveVertex(-2.25*headSize,0.4*headSize);
curveVertex(-2.3*headSize,-0.3*headSize);
curveVertex(-2.0*headSize,-0.3*headSize);
curveVertex(-1.1*headSize,-0.4*headSize);
curveVertex(0,-0.5*headSize);
curveVertex(1.1*headSize,-0.4*headSize);
curveVertex(2.0*headSize,-0.3*headSize);
endShape();
popMatrix();
}
void paintBody(PVector cen, float ang){
pushMatrix();
translate(cen.x,cen.y);
rotate(HALF_PI + ang);
beginShape();
curveVertex(0,-0.5*headSize);
curveVertex(0.7*headSize,-0.2*headSize);
curveVertex(0.9*headSize,0.0*headSize);
curveVertex(1.1*headSize,0.3*headSize);
curveVertex(2.4*headSize,0.7*headSize);
curveVertex(3.3*headSize,1.1*headSize);
curveVertex(3.1*headSize,1.1*headSize);
curveVertex(1.5*headSize,1.1*headSize);
curveVertex(1.0*headSize,1.3*headSize);
curveVertex(0.9*headSize,1.1*headSize);
curveVertex(0.85*headSize,2.8*headSize);
curveVertex(0.95*headSize,3.2*headSize);
curveVertex(1.2*headSize,3.55*headSize);
curveVertex(1.5*headSize,4.0*headSize);
curveVertex(1.1*headSize,3.95*headSize);
curveVertex(0.9*headSize,3.95*headSize);
curveVertex(0.7*headSize,4.1*headSize);
curveVertex(0.4*headSize,5.3*headSize);
curveVertex(0,5.2*headSize);
curveVertex(-0.4*headSize,5.3*headSize);
curveVertex(-0.7*headSize,4.1*headSize);
curveVertex(-0.9*headSize,3.95*headSize);
curveVertex(-1.1*headSize,3.95*headSize);
curveVertex(-1.5*headSize,4.0*headSize);
curveVertex(-1.2*headSize,3.55*headSize);
curveVertex(-0.95*headSize,3.2*headSize);
curveVertex(-0.85*headSize,2.8*headSize);
curveVertex(-0.9*headSize,1.1*headSize);
curveVertex(-1.0*headSize,1.3*headSize);
curveVertex(-1.5*headSize,1.1*headSize);
curveVertex(-3.1*headSize,1.1*headSize);
curveVertex(-3.3*headSize,1.1*headSize);
curveVertex(-2.4*headSize,0.7*headSize);
curveVertex(-1.1*headSize,0.3*headSize);
curveVertex(-0.9*headSize,0.0*headSize);
curveVertex(-0.7*headSize,-0.2*headSize);
curveVertex(0,-0.5*headSize);
curveVertex(0.7*headSize,-0.2*headSize);
curveVertex(0.9*headSize,0.0*headSize);
endShape();
popMatrix();
}
void paintMed(PVector cen, float ang){
pushMatrix();
translate(cen.x,cen.y);
rotate(HALF_PI + ang);
beginShape();
curveVertex(0,-0.5*headSize);
curveVertex(0.4*headSize,-0.1*headSize);
curveVertex(0.2*headSize,1.8*headSize);
curveVertex(0,2*headSize);
curveVertex(-0.2*headSize,1.8*headSize);
curveVertex(-0.4*headSize,-0.1*headSize);
curveVertex(0,-0.5*headSize);
curveVertex(0.4*headSize,-0.1*headSize);
curveVertex(0.2*headSize,1.8*headSize);
endShape();
popMatrix();
}
void paintTail(PVector cen, float ang){
pushMatrix();
translate(cen.x,cen.y);
rotate(HALF_PI + ang);
beginShape();
curveVertex(0,-0.5*headSize);
curveVertex(0.2*headSize,-0.05*headSize);
curveVertex(0,2.8*headSize);
curveVertex(-0.2*headSize,-0.05*headSize);
curveVertex(0,-0.5*headSize);
curveVertex(0.2*headSize,-0.05*headSize);
curveVertex(0,2.8*headSize);
endShape();
popMatrix();
}
}
import controlP5.*;
public ControlP5 cp5;
public int nFishes = 300;
public int nJellys = 100;
public int nWhales = 1;
public int nHammers = 3;
public float repulsionDist = 30;
public float alignDist = 50;
public float attractionDist = 150;
public float huntDist = 200;
public float limitDist = 300;
public float repulsionDistSqr = sq(repulsionDist);
public float alignDistSqr = sq(alignDist);
public float attractionDistSqr = sq(attractionDist);
public float huntDistSqr = sq(huntDist);
public float limitDistSqr = sq(limitDist);
public float maxVel = 5;
public boolean paintLines = false;
public boolean paintLimits = false;
public boolean paintDetails = true;
ArrayList fishes = new ArrayList(nFishes);
ArrayList jellys = new ArrayList(nJellys);
ArrayList whales = new ArrayList(nWhales);
ArrayList hammers = new ArrayList(nHammers);
PImage bgImg;
void setup(){
size(900,800);
smooth();
// Fishes
for(int i = 0; i < nFishes; i++){
float posAngle = random(0,TWO_PI);
float posMagnitude = random(0,limitDist);
PVector pos = new PVector(0.5*width+posMagnitude*cos(posAngle),0.5*height+posMagnitude*sin(posAngle));
float velAngle = random(0,TWO_PI);
float velMagnitude = 1;
PVector vel = new PVector(velMagnitude*cos(velAngle),velMagnitude*sin(velAngle));
color col = color(50,50,155);
float headSize = 7;
Fish f = new Fish(pos,vel,col,headSize);
fishes.add(f);
}
// Jellyfishes
for(int i = 0; i < nJellys; i++){
float posAngle = random(0,TWO_PI);
float posMagnitude = random(0,limitDist);
PVector pos = new PVector(0.5*width+posMagnitude*cos(posAngle),0.5*height+posMagnitude*sin(posAngle));
float velAngle = random(0,TWO_PI);
float velMagnitude = 1;
PVector vel = new PVector(velMagnitude*cos(velAngle),velMagnitude*sin(velAngle));
color col = color(200,200,230,150);
float headSize = random(8,11);
int nArms = 4;
int lengthArms = int(random(headSize*0.5,headSize*0.6));
Jellyfish j = new Jellyfish(pos,vel,col,headSize,nArms,lengthArms);
jellys.add(j);
}
// Killer whales
for(int i = 0; i < nWhales; i++){
float posAngle = random(0,TWO_PI);
float posMagnitude = random(0,limitDist);
PVector pos = new PVector(0.5*width+posMagnitude*cos(posAngle),0.5*height+posMagnitude*sin(posAngle));
float velAngle = random(0,TWO_PI);
float velMagnitude = 0;
PVector vel = new PVector(velMagnitude*cos(velAngle),velMagnitude*sin(velAngle));
color col = color(0);
float headSize = random(8,11);
Whale w = new Whale(pos,vel,col,headSize);
whales.add(w);
}
// Hammer sharks
for(int i = 0; i < nHammers; i++){
float posAngle = random(0,TWO_PI);
float posMagnitude = random(0,limitDist);
PVector pos = new PVector(0.5*width+posMagnitude*cos(posAngle),0.5*height+posMagnitude*sin(posAngle));
float velAngle = random(0,TWO_PI);
float velMagnitude = 0;
PVector vel = new PVector(velMagnitude*cos(velAngle),velMagnitude*sin(velAngle));
color col = color(100,100,100);
float headSize = random(5,6);
Hammer h = new Hammer(pos,vel,col,headSize);
hammers.add(h);
}
// Circular pool
bgImg = water();
// ControlP5 controls
controlPanel();
}
void draw(){
background(bgImg);
// Fishes
for(int i = 0; i < nFishes; i++){
Fish f = (Fish) fishes.get(i);
f.update();
if(paintDetails){
f.paintDetail();
}
else{
f.paint();
}
if(paintLimits && i == 0){
f.paintLimits();
}
}
for(int i = 0; i < nFishes; i++){
Fish f = (Fish) fishes.get(i);
f.evaluateInteractions(fishes,nFishes,true,true,true,false,false);
f.evaluateInteractions(whales,nWhales,false,false,false,true,false);
f.evaluateInteractions(hammers,nHammers,false,false,false,true,false);
f.keepClose();
}
// Jellyfishes
for(int i = 0; i < nJellys; i++){
Jellyfish j = (Jellyfish) jellys.get(i);
j.update();
if(paintDetails){
j.paintDetail();
}
else{
j.paint();
}
}
for(int i = 0; i < nJellys; i++){
Jellyfish j = (Jellyfish) jellys.get(i);
j.evaluateInteractions(jellys,nJellys,true,true,true,false,false);
j.evaluateInteractions(whales,nWhales,false,false,false,true,false);
j.evaluateInteractions(hammers,nHammers,false,false,false,true,false);
j.keepClose();
}
// Killer whales
for(int i = 0; i < nWhales; i++){
Whale w = (Whale) whales.get(i);
w.update();
if(paintDetails){
w.paintDetail();
}
else{
w.paint();
}
}
for(int i = 0; i < nWhales; i++){
Whale w = (Whale) whales.get(i);
w.evaluateInteractions(whales,nWhales,true,false,false,false,false);
w.evaluateInteractions(fishes,nFishes,false,false,false,false,true);
w.evaluateInteractions(jellys,nJellys,false,false,false,false,true);
w.evaluateInteractions(hammers,nHammers,true,false,false,false,false);
w.keepClose();
}
// Hammer sharks
for(int i = 0; i < nHammers; i++){
Hammer h = (Hammer) hammers.get(i);
h.update();
if(paintDetails){
h.paintDetail();
}
else{
h.paint();
}
}
for(int i = 0; i < nHammers; i++){
Hammer h = (Hammer) hammers.get(i);
h.evaluateInteractions(hammers,nHammers,true,true,true,false,false);
h.evaluateInteractions(fishes,nFishes,false,false,false,false,true);
h.evaluateInteractions(jellys,nJellys,false,false,false,false,true);
h.evaluateInteractions(whales,nWhales,true,false,false,false,false);
h.keepClose();
}
}
void controlPanel(){
int deltaX = 10;
int deltaY = 10;
int stepX = 85;
int stepY = 20;
cp5 = new ControlP5(this);
cp5.hide();
Slider nFishesSlider = cp5.addSlider("nFishes",0,500,nFishes,deltaX,deltaY,100,15);
nFishesSlider.setCaptionLabel("Number or fishes");
nFishesSlider.getCaptionLabel().align(ControlP5.RIGHT_OUTSIDE,CENTER).setPaddingX(10);
Slider nJellysSlider = cp5.addSlider("nJellys",0,500,nJellys,deltaX,deltaY+stepY,100,15);
nJellysSlider.setCaptionLabel("Number or jellys");
nJellysSlider.getCaptionLabel().align(ControlP5.RIGHT_OUTSIDE,CENTER).setPaddingX(10);
Slider nWhalesSlider = cp5.addSlider("nWhales",0,100,nWhales,deltaX,deltaY+2*stepY,100,15);
nWhalesSlider.setCaptionLabel("Number or whales");
nWhalesSlider.getCaptionLabel().align(ControlP5.RIGHT_OUTSIDE,CENTER).setPaddingX(10);
Slider nHammersSlider = cp5.addSlider("nHammers",0,100,nHammers,deltaX,deltaY+3*stepY,100,15);
nHammersSlider.setCaptionLabel("Number or hammers");
nHammersSlider.getCaptionLabel().align(ControlP5.RIGHT_OUTSIDE,CENTER).setPaddingX(10);
Slider repulsionDistSlider = cp5.addSlider("repulsionDist",0,300,repulsionDist,deltaX,deltaY+4*stepY,100,15);
repulsionDistSlider.setCaptionLabel("Repulsion distance");
repulsionDistSlider.getCaptionLabel().align(ControlP5.RIGHT_OUTSIDE,CENTER).setPaddingX(10);
Slider alignDistSlider = cp5.addSlider("alignDist",0,300,alignDist,deltaX,deltaY+5*stepY,100,15);
alignDistSlider.setCaptionLabel("Align distance");
alignDistSlider.getCaptionLabel().align(ControlP5.RIGHT_OUTSIDE,CENTER).setPaddingX(10);
Slider attractionDistSlider = cp5.addSlider("attractionDist",0,300,attractionDist,deltaX,deltaY+6*stepY,100,15);
attractionDistSlider.setCaptionLabel("Attraction distance");
attractionDistSlider.getCaptionLabel().align(ControlP5.RIGHT_OUTSIDE,CENTER).setPaddingX(10);
Slider huntDistSlider = cp5.addSlider("huntDist",0,300,huntDist,deltaX,deltaY+7*stepY,100,15);
huntDistSlider.setCaptionLabel("Hunt distance");
huntDistSlider.getCaptionLabel().align(ControlP5.RIGHT_OUTSIDE,CENTER).setPaddingX(10);
Slider limitDistSlider = cp5.addSlider("limitDist",0,300,limitDist,deltaX,deltaY+8*stepY,100,15);
limitDistSlider.setCaptionLabel("Limit distance");
limitDistSlider.getCaptionLabel().align(ControlP5.RIGHT_OUTSIDE,CENTER).setPaddingX(10);
Slider maxVelSlider = cp5.addSlider("maxVel",0,10,maxVel,deltaX,deltaY+9*stepY,100,15);
maxVelSlider.setCaptionLabel("Maximum velocity");
maxVelSlider.getCaptionLabel().align(ControlP5.RIGHT_OUTSIDE,CENTER).setPaddingX(10);
Toggle paintLimitsToggle = cp5.addToggle("paintLimits",paintLimits,deltaX,deltaY+10*stepY,15,15);
paintLimitsToggle.setCaptionLabel("Show limits");
paintLimitsToggle.getCaptionLabel().align(ControlP5.RIGHT_OUTSIDE,CENTER).setPaddingX(10);
Toggle paintLinesToggle = cp5.addToggle("paintLines",paintLines,deltaX,deltaY+11*stepY,15,15);
paintLinesToggle.setCaptionLabel("Show lines");
paintLinesToggle.getCaptionLabel().align(ControlP5.RIGHT_OUTSIDE,CENTER).setPaddingX(10);
Toggle paintDetailsToggle = cp5.addToggle("paintDetails",paintDetails,deltaX,deltaY+12*stepY,15,15);
paintDetailsToggle.setCaptionLabel("Paint details");
paintDetailsToggle.getCaptionLabel().align(ControlP5.RIGHT_OUTSIDE,CENTER).setPaddingX(10);
}
void controlEvent(ControlEvent theControlEvent){
if(theControlEvent.isFrom("nFishes")){
while(nFishes > fishes.size()){
float posAngle = random(0,TWO_PI);
float posMagnitude = random(0,limitDist);
PVector pos = new PVector(0.5*width+posMagnitude*cos(posAngle),0.5*height+posMagnitude*sin(posAngle));
float velAngle = random(0,TWO_PI);
float velMagnitude = 1;
PVector vel = new PVector(velMagnitude*cos(velAngle),velMagnitude*sin(velAngle));
color col = color(50,50,155);
float headSize = 7;
Fish f = new Fish(pos,vel,col,headSize);
fishes.add(f);
}
}
if(theControlEvent.isFrom("nJellys")){
while(nJellys > jellys.size()){
float posAngle = random(0,TWO_PI);
float posMagnitude = random(0,limitDist);
PVector pos = new PVector(0.5*width+posMagnitude*cos(posAngle),0.5*height+posMagnitude*sin(posAngle));
float velAngle = random(0,TWO_PI);
float velMagnitude = 1;
PVector vel = new PVector(velMagnitude*cos(velAngle),velMagnitude*sin(velAngle));
color col = color(200,200,230,150);
float headSize = random(8,11);
int nArms = 4;
int lengthArms = int(random(headSize*0.5,headSize*0.6));
Jellyfish j = new Jellyfish(pos,vel,col,headSize,nArms,lengthArms);
jellys.add(j);
}
}
if(theControlEvent.isFrom("nWhales")){
while(nWhales > whales.size()){
float posAngle = random(0,TWO_PI);
float posMagnitude = random(0,limitDist);
PVector pos = new PVector(0.5*width+posMagnitude*cos(posAngle),0.5*height+posMagnitude*sin(posAngle));
float velAngle = random(0,TWO_PI);
float velMagnitude = 1;
PVector vel = new PVector(velMagnitude*cos(velAngle),velMagnitude*sin(velAngle));
color col = color(0);
float headSize = random(8,11);
Whale w = new Whale(pos,vel,col,headSize);
whales.add(w);
}
}
if(theControlEvent.isFrom("nHammers")){
while(nHammers > hammers.size()){
float posAngle = random(0,TWO_PI);
float posMagnitude = random(0,limitDist);
PVector pos = new PVector(0.5*width+posMagnitude*cos(posAngle),0.5*height+posMagnitude*sin(posAngle));
float velAngle = random(0,TWO_PI);
float velMagnitude = 1;
PVector vel = new PVector(velMagnitude*cos(velAngle),velMagnitude*sin(velAngle));
color col = color(100,100,100);
float headSize = random(5,6);
Hammer h = new Hammer(pos,vel,col,headSize);
hammers.add(h);
}
}
if(theControlEvent.isFrom("repulsionDist")){
repulsionDistSqr = sq(repulsionDist);
}
if(theControlEvent.isFrom("alignDist")){
alignDistSqr = sq(alignDist);
}
if(theControlEvent.isFrom("attractionDist")){
attractionDistSqr = sq(attractionDist);
}
if(theControlEvent.isFrom("huntDist")){
huntDistSqr = sq(huntDist);
}
if(theControlEvent.isFrom("limitDist")){
limitDistSqr = sq(limitDist);
}
}
void mousePressed() {
cp5.show();
}
PImage water(){
PImage img = createImage(width,height,RGB);
for (int y = 0; y < height; y++){
for (int x = 0; x < width; x++){
int loc = x + y*width;
float rad = sqrt(sq(x-0.5*width) + sq(y-0.5*height));
float ang = abs(atan2(y-0.5*height,x-0.5*width));
float g = 15*noise(ang,0.01*rad) + 0.07*rad + 0;
float b = 20*noise(ang,0.03*rad) + 0.07*rad + 20;
float rand = random(3+0.04*rad);
img.pixels[loc] = color(rand,rand+g,rand+b);
}
}
return img;
}
class Flock{
PVector pos;
PVector vel;
PVector acc;
color col;
boolean isClose;
Flock(PVector tempPos, PVector tempVel, color tempCol){
pos = tempPos.get();
vel = tempVel.get();
acc = new PVector(0,0);
col = tempCol;
isClose = false;
}
void update(){
vel.add(acc);
vel.limit(maxVel);
pos.add(vel);
acc.set(new PVector(0,0));
vel.mult(0.99);
if(vel.mag() < 1){
vel.normalize();
}
//isClose = false;
}
void evaluateInteractions(ArrayList flocks, int n, boolean repulsionCond, boolean alignCond, boolean attractionCond, boolean huntedCond, boolean huntCond){
for(int i = 0; i < n; i++){
Flock f = (Flock) flocks.get(i);
PVector posDir = PVector.sub(f.pos,pos);
float distanceSqr = sq(posDir.x) + sq(posDir.y);
if(repulsionCond){
repulsion(posDir,distanceSqr);
}
if(alignCond){
PVector velDir = f.vel.get();
align(velDir,distanceSqr);
}
if(attractionCond){
attraction(posDir,distanceSqr);
}
if(huntedCond){
hunted(posDir,distanceSqr);
}
if(huntCond){
hunt(posDir,distanceSqr);
}
}
}
void repulsion(PVector forceDir, float distSqr){
if(distSqr <= repulsionDistSqr && distSqr != 0){
float distance = sqrt(distSqr);
PVector force = forceDir.get();
force.div(distance);
force.mult(-1*(repulsionDist/distance - 1));
acc.add(force);
if(paintLines){
strokeWeight(1);
stroke(color(255,0,0));
line(pos.x,pos.y,pos.x+forceDir.x,pos.y+forceDir.y);
noStroke();
}
}
}
void align(PVector forceDir, float distSqr){
if(distSqr > repulsionDistSqr && distSqr <= alignDistSqr){
float distance = sqrt(distSqr);
PVector force = forceDir.get();
force.normalize();
force.mult(0.1*0.5*(1-cos(TWO_PI*(distance-repulsionDist)/(alignDist-repulsionDist))));
acc.add(force);
}
}
void attraction(PVector forceDir, float distSqr){
if(distSqr > alignDistSqr && distSqr < attractionDistSqr){
float distance = sqrt(distSqr);
PVector force = forceDir.get();
force.div(distance);
force.mult(0.01*0.5*(1-cos(TWO_PI*(distance-alignDist)/(attractionDist-alignDist))));
acc.add(force);
}
}
void hunted(PVector forceDir, float distSqr){
if(distSqr < huntDistSqr){
float distance = sqrt(distSqr);
PVector force = forceDir.get();
force.div(distance);
force.mult(-0.5*(huntDist/distance - 1));
acc.add(force);
if(distance < 15){
isClose = true;
}
}
}
void hunt(PVector forceDir, float distSqr){
if(distSqr < huntDistSqr){
float distance = sqrt(distSqr);
PVector force = forceDir.get();
force.div(distance);
force.mult(0.2*(huntDist/distance - 1));
acc.add(force);
if(distance < 15){
isClose = true;
}
}
}
void keepClose(){
PVector force = new PVector((0.5*width)-pos.x,(0.5*height)-pos.y);
float distSqr = sq(force.x) + sq(force.y);
if(distSqr > limitDistSqr){
float distance = sqrt(distSqr);
force.div(distance);
force.mult(1*(distance/limitDist));
acc.add(force);
}
}
void paint(){
noStroke();
fill(col);
float ang = atan2(vel.y,vel.x);
PVector v1 = new PVector(pos.x+8*cos(ang),pos.y+8*sin(ang));
PVector v2 = new PVector(pos.x+4*cos(ang+radians(90+45)),pos.y+4*sin(ang+radians(90+45)));
PVector v3 = new PVector(pos.x+4*cos(ang+radians(270-45)),pos.y+4*sin(ang+radians(270-45)));
triangle(v1.x,v1.y,v2.x,v2.y,v3.x,v3.y);
}
void paintPoint(){
stroke(col);
strokeWeight(4);
point(pos.x,pos.y);
strokeWeight(1);
noStroke();
}
void paintLimits(){
noFill();
strokeWeight(1);
stroke(color(255,0,0));
ellipse(pos.x,pos.y,2*repulsionDist,2*repulsionDist);
stroke(color(0,255,0));
ellipse(pos.x,pos.y,2*alignDist,2*alignDist);
stroke(color(0,0,255));
ellipse(pos.x,pos.y,2*attractionDist,2*attractionDist);
stroke(color(255));
ellipse(0.5*width,0.5*height,2*limitDist,2*limitDist);
}
}
class Jellyfish extends Flock{
float headSize;
int nArms;
float lengthArms;
Arm[] arms;
float ang;
Jellyfish(PVector tempPos, PVector tempVel, color tempCol, float tempHeadSize, int tempNArms, int tempLengthArms){
super(tempPos,tempVel,tempCol);
headSize = tempHeadSize;
nArms = tempNArms;
lengthArms = tempLengthArms;
arms = new Arm[nArms];
for(int i = 0; i < nArms; i++){
float offset = 0;
if(nArms > 1){
offset = -0.3*headSize + 0.6*headSize*i/(nArms-1);
}
int armSize = int(random(0.7*lengthArms,lengthArms));
arms[i] = new Arm(offset,armSize,col);
}
ang = 0;
}
void update(){
super.update();
ang = atan2(vel.y,vel.x);
for(int i = 0; i < nArms; i++){
arms[i].update(pos,ang);
}
/*
if(isClose){
float r = red(col);
float g = green(col);
float b = blue(col);
float a = alpha(col);
r += 10;
b -= 10;
col = color(r,g,b,a);
for(int i = 0; i < nArms; i++){
arms[i].changeColor(col);
}
isClose = false;
}
*/
}
void paintDetail(){
paintHead(pos,ang,col);
for(int i = 0; i < nArms; i++){
arms[i].paint();
}
}
void paintHead(PVector cen, float ang, color c){
noStroke();
fill(c);
pushMatrix();
translate(cen.x,cen.y);
rotate(HALF_PI + ang);
beginShape();
curveVertex(-headSize,0);
curveVertex(0,-0.7*headSize);
curveVertex(headSize,0);
curveVertex(0,0.3*headSize);
curveVertex(-headSize,0);
curveVertex(0,-0.7*headSize);
curveVertex(headSize,0);
endShape();
popMatrix();
}
}
class Arm{
PVector topLink;
float offset;
int nLinks;
Link[] links;
color col;
Arm(float tempOffset, int tempNLinks, color tempCol){
topLink = new PVector(0,0,0);
offset = tempOffset;
nLinks = tempNLinks;
links = new Link[nLinks];
col = tempCol;
for(int i = 0; i < nLinks; i++){
float sep = (nLinks-i) + 5;
float diam = ((nLinks-i) + 2)/1.4;
links[i] = new Link(sep,diam,col);
}
}
void update(PVector newPos, float ang){
topLink = PVector.add(newPos,new PVector(offset*cos(HALF_PI + ang),offset*sin(HALF_PI + ang)));
links[0].update(topLink);
for(int i = 1; i < nLinks; i++){
links[i].update(links[i-1].pos);
}
}
void paint(){
links[0].paint(topLink);
for(int i = 1; i < nLinks; i++){
links[i].paint(links[i-1].pos);
}
}
void changeColor(color c){
for(int i = 0; i < nLinks; i++){
links[i].col = c;
}
}
}
class Link{
PVector pos;
float sep;
float diam;
color col;
Link(float tempSep, float tempDiam, color tempCol){
pos = new PVector(0,0,0);
sep = tempSep;
diam = tempDiam;
col = tempCol;
}
void update(PVector prev){
float ang = atan2(prev.y-pos.y,prev.x-pos.x);
pos.x = prev.x - sep*cos(ang);
pos.y = prev.y - sep*sin(ang);
}
void paint(PVector prev){
stroke(col);
strokeWeight(diam);
line(prev.x,prev.y,pos.x,pos.y);
}
}
class Whale extends Flock{
float headSize;
PVector posHead, posBody, posMed, posTail, posEnd;
float angHead, angBody, angMed, angTail;
Whale(PVector tempPos, PVector tempVel, color tempCol, float tempHeadSize){
super(tempPos,tempVel,tempCol);
headSize = tempHeadSize;
posHead = pos.get();
posBody = PVector.add(posHead, new PVector(0,0.6*headSize));
posMed = PVector.add(posBody, new PVector(0,4.5*headSize));
posTail = PVector.add(posMed, new PVector(0,2.4*headSize));
posEnd = PVector.add(posTail, new PVector(0,1.8*headSize));
angHead = 0;
angBody = 0;
angMed = 0;
angTail = 0;
}
void update(){
super.update();
posHead = new PVector(pos.x,pos.y);
angHead = atan2(vel.y,vel.x);
if(isClose){
angHead += 0.5*sin(0.4*frameCount);
isClose = false;
}
posBody = PVector.sub(posHead,new PVector(0.6*headSize*cos(angHead),0.6*headSize*sin(angHead)));
angBody = atan2(posBody.y-posMed.y,posBody.x-posMed.x);
posMed = PVector.sub(posBody, new PVector(4.5*headSize*cos(angBody),4.5*headSize*sin(angBody)));
angMed = atan2(posMed.y-posTail.y,posMed.x-posTail.x);
posTail = PVector.sub(posMed, new PVector(2.4*headSize*cos(angMed),2.4*headSize*sin(angMed)));
angTail = atan2(posTail.y-posEnd.y,posTail.x-posEnd.x);
posEnd = PVector.sub(posTail, new PVector(1.8*headSize*cos(angTail),1.8*headSize*sin(angTail)));
}
void paintDetail(){
noStroke();
paintHead(posHead,angHead,col);
paintBody(posBody,angBody,col);
paintMed(posMed,angMed,col);
paintTail(posTail,angTail,col);
}
void paintHead(PVector cen, float ang, color c){
fill(c);
pushMatrix();
translate(cen.x,cen.y);
rotate(HALF_PI + ang);
beginShape();
curveVertex(0,-1.5*headSize);
curveVertex(0.4*headSize,-1.1*headSize);
curveVertex(0.85*headSize,-0.3*headSize);
curveVertex(1.0*headSize,0.6*headSize);
curveVertex(0.6*headSize,0.5*headSize);
curveVertex(0,0.2*headSize);
curveVertex(-0.6*headSize,0.5*headSize);
curveVertex(-1.0*headSize,0.6*headSize);
curveVertex(-0.85*headSize,-0.3*headSize);
curveVertex(-0.4*headSize,-1.1*headSize);
curveVertex(0,-1.5*headSize);
curveVertex(0.4*headSize,-1.1*headSize);
curveVertex(0.85*headSize,-0.3*headSize);
endShape();
fill(255,150);
beginShape();
curveVertex(0.4*headSize,-0.7*headSize);
curveVertex(0.65*headSize,-0.4*headSize);
curveVertex(0.85*headSize,0.2*headSize);
curveVertex(0.6*headSize,-0.1*headSize);
curveVertex(0.5*headSize,-0.4*headSize);
curveVertex(0.4*headSize,-0.7*headSize);
curveVertex(0.65*headSize,-0.4*headSize);
endShape();
beginShape();
curveVertex(-0.4*headSize,-0.7*headSize);
curveVertex(-0.65*headSize,-0.4*headSize);
curveVertex(-0.85*headSize,0.2*headSize);
curveVertex(-0.6*headSize,-0.1*headSize);
curveVertex(-0.5*headSize,-0.4*headSize);
curveVertex(-0.4*headSize,-0.7*headSize);
curveVertex(-0.65*headSize,-0.4*headSize);
endShape();
popMatrix();
}
void paintBody(PVector cen, float ang, color c){
fill(c);
pushMatrix();
translate(cen.x,cen.y);
rotate(HALF_PI + ang);
beginShape();
curveVertex(0,-0.5*headSize);
curveVertex(0.9*headSize,-0.2*headSize);
curveVertex(1.05*headSize,0.2*headSize);
curveVertex(1.25*headSize,0.3*headSize);
curveVertex(1.85*headSize,0.7*headSize);
curveVertex(2.2*headSize,1.3*headSize);
curveVertex(2.1*headSize,1.9*headSize);
curveVertex(1.7*headSize,2.1*headSize);
curveVertex(1.2*headSize,2.0*headSize);
curveVertex(1.1*headSize,2.0*headSize);
curveVertex(0.85*headSize,4.4*headSize);
curveVertex(0,4.2*headSize);
curveVertex(-0.85*headSize,4.4*headSize);
curveVertex(-1.1*headSize,2.0*headSize);
curveVertex(-1.2*headSize,2.0*headSize);
curveVertex(-1.7*headSize,2.1*headSize);
curveVertex(-2.1*headSize,1.9*headSize);
curveVertex(-2.2*headSize,1.3*headSize);
curveVertex(-1.85*headSize,0.7*headSize);
curveVertex(-1.25*headSize,0.3*headSize);
curveVertex(-1.05*headSize,0.2*headSize);
curveVertex(-0.9*headSize,-0.2*headSize);
curveVertex(0,-0.5*headSize);
curveVertex(0.9*headSize,-0.2*headSize);
curveVertex(1.05*headSize,0.2*headSize);
endShape();
fill(255,150);
beginShape();
curveVertex(0.6*headSize,2.7*headSize);
curveVertex(0.9*headSize,3.6*headSize);
curveVertex(0.8*headSize,4.1*headSize);
curveVertex(0.6*headSize,3.8*headSize);
curveVertex(0.4*headSize,3.3*headSize);
curveVertex(0.6*headSize,2.7*headSize);
curveVertex(0.9*headSize,3.6*headSize);
endShape();
beginShape();
curveVertex(-0.6*headSize,2.7*headSize);
curveVertex(-0.9*headSize,3.6*headSize);
curveVertex(-0.8*headSize,4.1*headSize);
curveVertex(-0.6*headSize,3.8*headSize);
curveVertex(-0.4*headSize,3.3*headSize);
curveVertex(-0.6*headSize,2.7*headSize);
curveVertex(-0.9*headSize,3.6*headSize);
endShape();
popMatrix();
}
void paintMed(PVector cen, float ang, color c){
fill(c);
pushMatrix();
translate(cen.x,cen.y);
rotate(HALF_PI + ang);
beginShape();
curveVertex(0,-0.5*headSize);
curveVertex(0.8*headSize,-0.1*headSize);
curveVertex(0.6*headSize,1.2*headSize);
curveVertex(0.3*headSize,2.2*headSize);
curveVertex(0,2.1*headSize);
curveVertex(-0.3*headSize,2.2*headSize);
curveVertex(-0.6*headSize,1.2*headSize);
curveVertex(-0.8*headSize,-0.1*headSize);
curveVertex(0,-0.5*headSize);
curveVertex(0.8*headSize,-0.1*headSize);
curveVertex(0.6*headSize,1.2*headSize);
endShape();
popMatrix();
}
void paintTail(PVector cen, float ang, color c){
fill(c);
pushMatrix();
translate(cen.x,cen.y);
rotate(HALF_PI + ang);
beginShape();
curveVertex(0,-0.5*headSize);
curveVertex(0.25*headSize,-0.2*headSize);
curveVertex(0.3*headSize,0.6*headSize);
curveVertex(1.3*headSize,1.2*headSize);
curveVertex(1.6*headSize,1.8*headSize);
curveVertex(1.2*headSize,1.7*headSize);
curveVertex(0.3*headSize,1.7*headSize);
curveVertex(0.0*headSize,1.55*headSize);
curveVertex(0,1.5*headSize);
curveVertex(-0.0*headSize,1.55*headSize);
curveVertex(-0.3*headSize,1.7*headSize);
curveVertex(-1.2*headSize,1.7*headSize);
curveVertex(-1.6*headSize,1.8*headSize);
curveVertex(-1.3*headSize,1.2*headSize);
curveVertex(-0.3*headSize,0.6*headSize);
curveVertex(-0.25*headSize,-0.2*headSize);
curveVertex(0,-0.5*headSize);
curveVertex(0.25*headSize,-0.2*headSize);
curveVertex(0.3*headSize,0.6*headSize);
endShape();
popMatrix();
}
}
class Fish extends Flock{
float headSize;
Fish(PVector tempPos, PVector tempVel, color tempCol, float tempHeadSize){
super(tempPos,tempVel,tempCol);
headSize = tempHeadSize;
}
void paintDetail(){
noStroke();
fill(col);
pushMatrix();
translate(pos.x,pos.y);
rotate(HALF_PI + atan2(vel.y,vel.x));
beginShape();
curveVertex(0,-0.5*headSize);
curveVertex(0.2*headSize,0);
curveVertex(0,1.5*headSize);
curveVertex(-0.2*headSize,0);
curveVertex(0,-0.5*headSize);
curveVertex(0.2*headSize,0);
curveVertex(0,1.5*headSize);
endShape();
popMatrix();
}
}
My first flocking experiment.
Everything started from this flocking tutorial:
http://www.flight404.com/blog/?p=459
http://libcinder.org/docs/dev/flocking_chapter1.html
Click on the screen to make the controls appear.
Javier Graciá Carpio
Javier Graciá Carpio