/*
* Copyright (c) 2012 Gwyllim Jahn
* http://scripts.crida.net/gh
* Developed By Gwyllim Jahn, Kathryn McKenzie and Sarah Papadopoullos
* during the 2012 RMIT nCertainties studio.
*
* This code makes extensive use of several freely distributed
* processing libraries – thankyou especially to toxi.
*This code is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* http://creativecommons.org/licenses/LGPL/2.1/
*
* This code is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
*/
- //—————————————————–Bansia Pumps v2——————————————————–//
import processing.pdf.*;
import processing.video.*;
import controlP5.*;
import processing.dxf.*;
import toxi.geom.*;
import toxi.volume.*;
import peasy.org.apache.commons.math.*;
import peasy.*;
import peasy.org.apache.commons.math.geometry.*;
import processing.opengl.*;
// ———————————————————————————-GLOBALS
int DX=900;
int DY=900;
int DZ=300;
float density = 0.3;
VolumetricSpaceArray volume;
VolumetricBrush brush;
boolean saveRaw = false;
boolean paused = false;
PeasyCam cam;
ControlP5 controlP5;
ControlWindow controlWindow;
MovieMaker mm;
int numAgents = 40;
boolean record = false;
boolean filming = false;
//HashMap<String, ArrayList<Agent>> aMap =new HashMap<String, ArrayList<Agent>> ();
//MultiMap agentPop = MultiValueMap.decorate(aMap);
ArrayList<Agent> agentPop = new ArrayList<Agent>();
ArrayList<SapMonster> sapPop = new ArrayList<SapMonster>();
ArrayList<TreeAttractor>forest = new ArrayList<TreeAttractor>();
ArrayList<Agent>addList = new ArrayList<Agent>();
Boolean drawPts = false;
Boolean drawSingle = false;
int ct =0;
//agent variables
//number of points to draw a trail with
int trailLength = 100;
float agentSpeed = 0.5;
//distance between trail points
int dropSpacing = 5;
//size of field to spawn agents
float spawnSizeXY = 10;
float aAttract = 1;
float aAlign = 0.03;
float aRepel = 3;
float aAccel = 0.02;
//NEW VARIABLES
float maxAgentSize = 15;
float increaseSize = 0.3;
float distToTree = 10;
int firstTree = 7;
//set iso density to spawn size
Vec3D SCALE=new Vec3D(450,450,300);
float sX = SCALE.x/DX;
float sY = SCALE.y/DY;
float sZ = SCALE.z/DZ;
//camera look at point
float camx =200;
float camy = 200;
float camz = 0;
//resolution of the dots
int r = 2;
// ———————————————————————————-SETUP
void setup(){
size(700,700,P3D);
cam = new PeasyCam(this,camx,camy,camz, 400);
stroke(255);
mm = new MovieMaker(this, width, height, “drawing.mov”, 30, MovieMaker.ANIMATION, MovieMaker.HIGH);
//load in the tree locations
importLandscape();
//spawn all the agents
spawn();
//setup the controllers
setupP5();
//volume brush stuff
volume=new VolumetricSpaceArray(SCALE,DX,DY,DZ);
brush=new RoundBrush(volume,5);
}
// ———————————————————————————-DRAW LOOP
void draw(){
background(0);
//this draws the actual voxels as points – very slow…
//only useful for checking if things are working.
if(record){
beginRaw(PDF,”test_####.pdf”);
}
if(drawPts){
drawVox();
}
//—————————————————GUTS
for (Agent a :agentPop){
a.run();
}
for (TreeAttractor t :forest){
t.render();
}
ArrayList<Integer> removeList = new ArrayList<Integer>();
int c =0;
for (SapMonster s :sapPop){
if(s.alive){
s.run();
}else{
removeList.add(c);
}
c++;
}
sapPop.removeAll(removeList);
if(addList.size()>0){
for (Agent a:addList){
agentPop.add(a);
}
addList = new ArrayList<Agent>();
}
//—————————————————GUTS
if(record){
endRaw();
record = false;
}
if(filming)mm.addFrame();
if (saveRaw) {
// save mesh as STL or OBJ file
GsaveData(volume,”F:/gsave”+ct+”.raw”);
saveRaw=false;
ct++;
}
}
void keyPressed(){
//s saves a dxf
if (key == ‘s’) {
record = !record;
}
if(key == ‘v’){
saveRaw = !saveRaw;
}
//c screen capture.
if (key == ‘c’){
saveFrame(“pic_####.png”);
}
if(key == ‘m’){
if(!filming) {
filming = true;
}else{
filming = false;
mm.finish(); // Finish the movie if space bar is pressed!
}
}
if (key == ‘r’){
//reset the sketch
importLandscape();
spawn();
}
if (key == ‘d’){
drawPts = !drawPts;
}
if (key == ‘p’){
paused = !paused;
}
if (key == ‘1’){
drawSingle = !drawSingle;
print(drawSingle);
}
}
//————————————————— Agent ——————————————————–//
//simple agent class to interact with the data
//field and other agents
class Agent {
Vec3D loc,vel,acc;
ArrayList<Vec3D> trail;
float aSize;
float dropNum;
boolean ruptured;
TreeAttractor parent;
float aX,aY,aZ;
Agent(Vec3D _loc, Vec3D _vel, TreeAttractor _parent){
loc = _loc;
vel = _vel;
acc = new Vec3D(0,0,0);
trail = new ArrayList<Vec3D>();
dropNum = 0;
ruptured = false;
aSize =1;
parent = _parent;
aX = aY= aZ=0;
}
void run(){
if(!paused){
//acc = new Vec3D(0,0,0);
getGridPos();
if(parent.volume>0){
aSize = 1;
searchAgents();
}else{
if(aSize>1)aSize-=0.2;
searchTrees();
}
update();
volBrushLocation();
}
if(!drawPts){
render();
}
}
//————————————————————————————-
//Basic Flock Function.
//————————————————————————————-
void searchAgents(){
//reset accel
for (Agent a:agentPop){
//get essential variables
Vec3D diff = a.loc.sub(loc.copy());
float d = diff.magnitude();
if(d<80&&d>0){
attract(diff.copy(),d); //attract to neighbour
align(a.vel.copy(),d); //align with neighbour
if(d<10) {
repel(diff.copy(),d); //avoid neighbour
if(loc.z>15) {
if(aSize<maxAgentSize) aSize+=increaseSize; //increase size of agent
}
}
}
}
//explode if too fat…
if(aSize>8){
// rupture();
}else{
ruptured = false;
}
//pull back to the threshold
pushPullToData(0.1);
}
void attract (Vec3D v, float d){
acc.interpolateToSelf(v,(aAttract/d));
}
void align(Vec3D v, float d){
acc.interpolateToSelf(v,aAlign);
}
void repel (Vec3D v, float d){
v.invert();
acc.interpolateToSelf(v,(aRepel/d));
}
//————————————————————————————-
//random tree search and respawn
//————————————————————————————-
void searchTrees(){
if(loc.z<10){
Vec3D searchDir = new Vec3D(random(-1,1),random(-1,1),random(0));
vel.set(vel.x,vel.y,vel.z/10);
acc.interpolateToSelf(searchDir,0.1);
}else{
vel.addSelf(new Vec3D(0,0,-0.5));
pushPullToData(0.1);
}
//if(loc.z<10)vel.set(vel.x,vel.y,vel.z/10);
//check for nearby trees
for (TreeAttractor t:forest){
if(t.volume>0){
if(loc.distanceTo(t.loc)<distToTree){
parent = t;
respawn(t);
}
}
}
}
void respawn(TreeAttractor t){
for (int i=0;i<20;i++){
// Vec3D loc = new Vec3D(random(0,gW*gRes),random(0,gH*gRes),0);
Vec3D loc = new Vec3D(random(-spawnSizeXY,spawnSizeXY),random(-spawnSizeXY,spawnSizeXY),0);
loc.addSelf(t.loc);
Vec3D vel = new Vec3D(0,0,1);
Agent a = new Agent(loc,vel,t);
addList.add(a);
}
}
//————————————————————————————-
//Spawning/rupture functions
//————————————————————————————-
void rupture(){
if(!ruptured){
for (int i=0;i<10;i++){
// Vec3D sVel = new Vec3D(random(-1,1),random(-1,1),random(-1,1));
Vec3D sVel = vel.cross(new Vec3D(0,0,1));
sVel.rotateZ(random(-0.8,0.8));
SapMonster a = new SapMonster(loc.add(sVel),sVel,this);
sapPop.add(a);
}
}
ruptured = true;
}
//————————————————————————————-
//Voxel and volume brush functions
//————————————————————————————-
void volBrushLocation(){
brush.setSize(aSize);
float aX = constrain(loc.x,0,SCALE.x);
aX = map(aX,0,SCALE.x,0,DX);
float aY = constrain(loc.y,0,SCALE.y);
aY = map(aY,0,SCALE.y,0,DY);
float aZ = constrain(loc.z,0,SCALE.z);
aZ = map(aZ,0,SCALE.z,0,DZ);
brush.drawAtGridPos(aX,aY,aZ,density);
parent.volume-=0.1;
}
void getGridPos(){
aX = constrain(loc.x,0,SCALE.x);
aX = map(aX,0,SCALE.x,0,DX);
aY = constrain(loc.y,0,SCALE.y);
aY = map(aY,0,SCALE.y,0,DY);
aZ = constrain(loc.z,0,SCALE.z);
aZ = map(aZ,0,SCALE.z,0,DZ);
}
void pushPullToData(float t){
float bestThreshDiff = 100;
Vec3D bestThreshPt = new Vec3D(0,0,0);
//iterate over neighbouring grid pos
for (int i =-5;i<=5;i++){
for (int j =-5;j<=5;j++){
for (int k =-5; k<=5;k++){
//if the value of this noisepoints better than the others, then update variables
//scale val by distance
Vec3D vec = new Vec3D(i,j,k);
if((aX+i)>=0&&(aX+i)<DX&&(aY+j)>=0&&(aY+j)<DY&&(aZ+k)>=0&&(aZ+k)<DZ){
float val = volume.getVoxelAt(int(aX+i),int(aY+j),int(aZ+k));
//only use pts within field of view
float angleDiff = vel.angleBetween(vec,true);
angleDiff = map(angleDiff,0,PI*2,0,1);
if(angleDiff<0.5){
float threshVal = abs(val-t);
if(threshVal <bestThreshDiff) bestThreshDiff=threshVal;
//float threshVal = val;
vec.scaleSelf(1-((threshVal)*(1-angleDiff)));
bestThreshPt.addSelf(vec);
if(threshVal<bestThreshDiff)bestThreshDiff=threshVal;
}
}
}
}
}
if(bestThreshDiff<0.01){
//attract to threshold
bestThreshPt.scaleSelf(0.05);
acc.addSelf(bestThreshPt);
}
}
//————————————————————————————-
//Functions for managing trails and rendering the agent.
//————————————————————————————-
void update(){
//move the agent
acc.limit(aAccel);
vel.addSelf(acc);
vel.limit(agentSpeed);
loc.addSelf(vel);
//update the trail geom every 5 moves
if(dropNum%dropSpacing==0){
if(trail.size()<trailLength){
trail.add(new Vec3D(loc.x, loc.y, loc.z));
}else{
trail.remove(0);
}
dropNum=0;
}
dropNum++;
}
void render(){
//render agent trail as lines
Vec3D ls = null;
stroke(255);
for (Vec3D l : trail){
if(ls !=null){
line (l.x,l.y,l.z,ls.x,ls.y,ls.z);
}
ls = l;
}
}
void renderPts(){
//render agent trail as lines
for (Vec3D l : trail){
point (l.x,l.y,l.z);
}
}
}
- //—————————————————–Controller——————————————————–//
void setupP5 (){
controlP5 = new ControlP5(this);
controlP5.setAutoDraw(false);
controlWindow = controlP5.addControlWindow(“controlP5window”,100,100,400,300);
controlWindow.hideCoordinates();
Controller mySlider1 = controlP5.addSlider(“agentSpeed”,0,3.5).linebreak();
mySlider1.setWindow(controlWindow);
Controller mySlider2 = controlP5.addSlider(“aAttract”,0,5.00).linebreak();
mySlider2.setWindow(controlWindow);
Controller mySlider3 = controlP5.addSlider(“aAlign”,0,0.10).linebreak();
mySlider3.setWindow(controlWindow);
Controller mySlider4 = controlP5.addSlider(“aRepel”,0,5.00).linebreak();
mySlider4.setWindow(controlWindow);
Controller mySlider8 = controlP5.addSlider(“aAccel”,0,0.100).linebreak();
mySlider8.setWindow(controlWindow);
Controller mySlider5 =controlP5.addSlider(“dropSpacing”,1,20).linebreak();
mySlider5.setWindow(controlWindow);
Controller mySlider6 =controlP5.addSlider(“trailLength”,1,200).linebreak();
mySlider6.setWindow(controlWindow);
Controller mySlider7 =controlP5.addSlider(“spawnSizeXY”,10,500).linebreak();
mySlider7.setWindow(controlWindow);
Controller mySlider9 =controlP5.addSlider(“maxAgentSize”,5,30).linebreak();
mySlider9.setWindow(controlWindow);
Controller mySlider10 =controlP5.addSlider(“increaseSize”,0.1,0.6).linebreak();
mySlider10.setWindow(controlWindow);
controlWindow.setTitle(“Spring GUI”);
}
- //—————————————————–Initialisation——————————————————–//
// this is the function that creates all of the agents that fly through the
// noisefield. So this is where you control the agents initial location
// and also their initial velocity.
void spawn(){
agentPop = new ArrayList();
TreeAttractor p = forest.get(firstTree);
for (int i=0;i<numAgents;i++){
// Vec3D loc = new Vec3D(random(0,gW*gRes),random(0,gH*gRes),0);
Vec3D loc = new Vec3D(random(spawnSizeXY/2,spawnSizeXY*1.5),random(spawnSizeXY/2,spawnSizeXY*1.5),0);
loc.addSelf(p.loc);
Vec3D vel = new Vec3D(0,0,1);
Agent a = new Agent(loc,vel,p);
agentPop.add(a);
}
}
//a couple of voxel functions
void drawVox(){
for (int i=0;i<DX;i+=r){
for (int j =0;j<DY;j+=r){
for (int k=0;k<DZ;k+=r){
//make a point at the current coordinate,
//use the value of the noise at that point
//as the points colour.
float v = volume.getVoxelAt(i,j,k)*200;
if(v>1){
stroke(v);
line(i*sX,j*sY,k*sZ,i*sX,j*sY,k*sZ+1);
}else if(v<-1){
stroke(abs(v),0,0);
line(i*sX,j*sY,k*sZ,i*sX+2,j*sY,k*sZ);
}
}
}
}
}
public void GsaveData(VolumetricSpaceArray v,String fn) {
print(“saving volume data…”);
int c=0;
int tot = v.getData().length;
try {
BufferedOutputStream ds = new BufferedOutputStream(new FileOutputStream(fn));
// ds.writeInt(volumeData.length);
for (float element : v.getData()) {
if(element<0)element=0;
ds.write((int)(element*100));
}
ds.flush();
ds.close();
} catch (IOException e) {
e.printStackTrace();
}
}
void importLandscape() {
forest = new ArrayList<TreeAttractor>();
//load text
String[] txtLines = loadStrings(“trees.txt”);
//splits strand points
String[] arrCoords = split(txtLines[0], ‘_’);
for (int j = 0; j < arrCoords.length; ++j) {
//separates coords
String[] arrToks = split(arrCoords[j], ‘,’);
float xx = Float.valueOf(arrToks[0]);
float yy = Float.valueOf(arrToks[1]);
float zz = Float.valueOf(arrToks[2]);
//create node
Vec3D tmpPt = new Vec3D(xx, yy, 0);
TreeAttractor tree = new TreeAttractor(tmpPt);
forest.add(tree);
}
}
- //—————————————————–Point——————————————————–//
class TreeAttractor {
Vec3D loc;
float volume;
TreeAttractor(Vec3D LOC){
loc = LOC;
volume = 1000;
}
void render(){
point(loc.x,loc.y,loc.z);
}
}
- //—————————————————–Sap Monster ——————————————————–//
//simple agent class to interact with the data
//field and other agents
class SapMonster {
Vec3D loc,vel,acc;
float aSize;
float age;
boolean alive;
Agent a;
SapMonster(Vec3D _loc, Vec3D _vel, Agent _a){
loc = _loc;
acc = new Vec3D(0,0,0);
vel = _vel;
aSize =2;
a = _a;
age = 0;
alive = true;
}
void run(){
searchAgents();
updatePos();
volBrushLocation();
render();
if(age>5) alive= false;
}
//————————————————————————————-
//Basic Lace Function. Draws an arc with the agent.
//————————————————————————————-
void updatePos(){
acc.interpolateToSelf(a.vel,0.1);
acc.limit(0.2);
vel.addSelf(acc);
vel.limit(1);
loc.addSelf(vel);
age+=0.1;
}
//————————————————————————————-
//Global Attraction functions
//————————————————————————————-
void searchAgents(){
//reset accel
for (SapMonster s:sapPop){
//get essential variables
Vec3D diff = s.loc.sub(loc.copy());
float d = diff.magnitude();
if(d>30){
attract(diff.copy(),d); //attract to neighbour
}else if (d>1 &&d<30){
repel(diff.copy(),d); //repel
}
}
}
void attract (Vec3D v, float d){
// v.normalizeTo(0.01/d);
acc.interpolateToSelf(v,0.01/d);
}
void repel (Vec3D v, float d){
v.invert();
//v.normalizeTo(0.01/d);
acc.interpolateToSelf(v,1/d);
}
//————————————————————————————-
//Voxel and volume brush functions
//————————————————————————————-
void volBrushLocation(){
brush.setSize(aSize);
float aX = constrain(loc.x,0,SCALE.x);
aX = map(aX,0,SCALE.x,0,DX);
float aY = constrain(loc.y,0,SCALE.y);
aY = map(aY,0,SCALE.y,0,DY);
float aZ = constrain(loc.z,0,SCALE.z);
aZ = map(aZ,0,SCALE.z,0,DZ);
brush.drawAtGridPos(aX,aY,aZ,-density);
}
//————————————————————————————-
//Functions for managing trails and rendering the agent.
//————————————————————————————-
void render(){
point (loc.x,loc.y,loc.z);
}
}