(n)certainties RMIT-UTS Spring 012

(n)certainties RMIT-UTS Spring 012 header image 3

1_locus; ethereal_empirica

/*
* Copyright (c) 2012 Gwyllim Jahn
* http://scripts.crida.net/gh

* Developed By Gwyllim Jahn, Cam Newnham and Mark-Henry Delacrauz
* 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.
*
*/

//—————————————————–OrbitingParticlesStrands ——————————————————–//

import processing.video.*;

import toxi.physics2d.constraints.*;
import toxi.physics.*;
import toxi.physics.constraints.*;
import toxi.physics2d.behaviors.*;
import toxi.physics.behaviors.*;
import toxi.physics2d.*;
import controlP5.*;
import processing.dxf.*;
import toxi.geom.*;
import org.apache.commons.collections.keyvalue.*;
import org.apache.commons.collections.set.*;
import org.apache.commons.collections.iterators.*;
import org.apache.commons.collections.map.*;
import org.apache.commons.collections.bag.*;
import org.apache.commons.collections.list.*;
import org.apache.commons.collections.bidimap.*;
import org.apache.commons.collections.collection.*;
import org.apache.commons.collections.*;
import org.apache.commons.collections.functors.*;
import peasy.org.apache.commons.math.*;
import peasy.*;
import peasy.org.apache.commons.math.geometry.*;
import processing.opengl.*;

// ———————————————————————————-GLOBALS
//accessable by anything in your script.
VerletPhysics physics = new VerletPhysics();

PeasyCam cam;
ControlP5 controlP5;
ControlWindow controlWindow;

int numTrees = 6;
//number of agents per tree
int numAgents = 75;
boolean record = false;
boolean agentsOn = true;
ArrayList forest = new ArrayList();
ArrayListfibrePop = new ArrayList();
MultiValueMap m = new MultiValueMap();
MultiValueMap particleMap = new MultiValueMap();

int gridRes = 15;

//add the pause variable
Boolean paused = false;
Boolean bundling = false;
//agent variables
int trailLength = 50;
float agentSpeed = 3.5;
int dropRate = 1;

//if a tree is less than this number away from the agent, then it will affect the path of the agent
float attractScale = 25;

//if the closest tree is less than this number away, the agent will try to move to the fringe of
//the trees crown
float fringeScale = 80;

//three variables for how much each type of movement affects the agent
float spin = 0.5;
float push = 0.12;
float pull = 0.18;

//maximum movement for a tree point
float maxTreePull = 3;
float treeSpring = 0.1;

//bundling variables
float cutoff = 20;
float stiffness = 0.1;
float numToMove = .8;
float nodeStiffness = 0.002;

//video
boolean filming = false;
MovieMaker mm;
// ———————————————————————————-SETUP

void setup(){
size(1600,900,OPENGL);

cam =new PeasyCam(this,800);
mm = new MovieMaker(this, width, height, “drawing.mov”, 30, MovieMaker.ANIMATION, MovieMaker.HIGH);
cam.rotateZ(1.57079633);

//setup the forest landscape

//setupRandomLandscape();
importLandscape();

setupP5();
background(255);
}

void draw(){
background(255);

if(record){
beginRaw(DXF,”test.dxf”);
}

if(!bundling){

//update the physics engine
physics.update();

for (TreeAttractor t :forest){
t.run();
}
}else{
for (Fibre f: fibrePop){
f.update();
}
}
if(filming)mm.addFrame();

if(record){
endRaw();
record = false;
}
}

void keyPressed(){

//save a dxf
if (key == ‘e’) {
record = !record;
}
//add the pause function
if (key == ‘p’) {
paused = !paused;
}
if (key == ‘a’) {
agentsOn = !agentsOn;
}
//save a screengrab
if (key == ‘s’) {
// saveFrame(“pic_####.png”);
if(!bundling){
savePts();
}else{
saveBundles();
}
}

if(key == ‘f’){
importFibres();
bundling =true;
}
if(key == ‘ ‘){
filming =!filming;
if(!filming)mm.finish();
}
//reset the sketch
if (key == ‘r’) {
background(255);
importLandscape();
physics = new VerletPhysics();
fibrePop=new ArrayList();
bundling = false;
//setupRandomLandscape();
}
}

//—————————————————–Agents——————————————————–//

//simple agent class to interact with the data
//field and other agents

class Agent {

Vec3D loc,vel;
TreeAttractor p;
float distToTree;
Strand trail;

Agent(Vec3D _loc, Vec3D _vel, TreeAttractor _p){
loc = _loc;
vel = _vel;
p = _p;
//add the agent to the map
m.put(p,this);
distToTree=0;
trail = new Strand(loc);
}

void run(){
//moves the particles based on proximity to trees
if(!paused){
getNearTrees();

constrainToCrown();

//limit height of the agent to roughly the crown height
limitHeight();

updatePos();
}

trail.run();
}

//————————————————————————————-

//Behaviours

//————————————————————————————-

void getNearTrees(){
//setup variables to set new parent
TreeAttractor n =p;
float minD = 10000;

for (TreeAttractor tree:forest){
//get the distance to the tree
float d = loc.distanceTo(tree.loc);

if(dminD=d;
n = tree;
}

if(d

//make the agent interact with the tree
//and colide with debris

vel.addSelf(getField(tree));
}

}
//re set the parent to the nearest tree
m.remove(p,this);
p = n;
m.put(p,this);

// set the node stiffness based on its distance to the tree point
distToTree = p.loc.distanceTo(loc)-p.crownRadius;
if(distToTree distToTree = map(distToTree,0,40,0.01,0.5);

// distToTree = m.getCollection(p).size();
//distToTree = map(distToTree,0,50,0.05,0.7);

//make sure we keep orbiting current tree
vel.addSelf(getField(p));
}

Vec3D getField(TreeAttractor t){
Vec3D toAttr = loc.sub(t.loc);
Vec3D perp = toAttr.cross(new Vec3D(0,0,1));

//create another vector to gently rotate the agents
Vec3D tangent = toAttr.cross(vel);

//random mod for tangent
float mod = random(-0.2,0.2);
tangent.scaleSelf(mod);
perp.addSelf(tangent);

float d = perp.magnitude();
float s = perp.magnitude();
perp.scaleSelf(spin/s);
//perp.normalize();
toAttr.scaleSelf(push/d);
return(perp.addSelf(toAttr));

}

void constrainToCrown(){
//get vector from agent to tree location
Vec3D toCrown = loc.sub(p.loc);
toCrown.invert();
//get distance to edge of crown
float diff = toCrown.magnitude()-p.crownRadius;

//constrain
diff = abs(constrain(diff,-fringeScale,fringeScale));

diff = (fringeScale – diff);
if(diff== 0) diff = 0.1;
//inverse falloff
toCrown.scaleSelf(pull/diff);

vel.addSelf(toCrown);
}

//————————————————————————————-

//Function for limiting the height of the agent. If the agent moves below the height
//of the tree crown, interpolate its vector upwards.

//————————————————————————————-

void limitHeight(){

//if agents z is less than closest trees z
if(loc.z vel.interpolateToSelf(new Vec3D(0,0,1),0.5);
}
}

//————————————————————————————-

//Functions for managing trails and rendering the agent.

//————————————————————————————-
void updatePos(){
//move the agent
vel.normalizeTo(agentSpeed);
loc.addSelf(vel);

trail.update(loc);

}

}

//—————————————————–Controllers——————————————————–//

void setupP5 (){
controlP5 = new ControlP5(this);
controlP5.setAutoDraw(false);
controlWindow = controlP5.addControlWindow(“controlP5window”,100,100,400,320);
controlWindow.hideCoordinates();

Controller mySlider1 = controlP5.addSlider(“spin”,0,1.5).linebreak();
mySlider1.setWindow(controlWindow);
Controller mySlider2 =controlP5.addSlider(“push”,0,0.201).linebreak();
mySlider2.setWindow(controlWindow);
Controller mySlider3 = controlP5.addSlider(“pull”,0,0.201).linebreak();
mySlider3.setWindow(controlWindow);
Controller mySlider4 =controlP5.addSlider(“attractScale”,0,200).linebreak();
mySlider4.setWindow(controlWindow);
Controller mySlider5 =controlP5.addSlider(“fringeScale”,0,200).linebreak();
mySlider5.setWindow(controlWindow);
Controller mySlider6 =controlP5.addSlider(“agentSpeed”,0.1,3.5).linebreak();
mySlider6.setWindow(controlWindow);
Controller mySlider7 =controlP5.addSlider(“maxTreePull”,0,15.00).linebreak();
mySlider7.setWindow(controlWindow);
Controller mySlider9 =controlP5.addSlider(“treeSpring”,0,0.150).linebreak();
mySlider9.setWindow(controlWindow);
Controller mySlider8 =controlP5.addSlider(“latcherGrowthRate”,0,10.5).linebreak();
mySlider8.setWindow(controlWindow);
Controller mySlider10 =controlP5.addSlider(“latcherRotate”,0,0.500).linebreak();
mySlider10.setWindow(controlWindow);
Textlabel field = controlP5.addTextlabel(“Instructions”,”press r to reset simulation with new parameters”,20,300);
field.setWindow(controlWindow);

controlWindow.setTitle(“Spring GUI”);

}
//—————————————————–Debris——————————————————–//

class Debris {

ArrayListchunks;
Vec3D loc;
Boolean stillAdding = true;

Debris (Vec3D _a){
loc = _a;
chunks = new ArrayList();
chunks.add(_a);

}

void render(){
strokeWeight(5);
point (loc.x,loc.y,loc.z);
}

}

//—————————————————–Fibre——————————————————–//

class Fibre{

ArrayListnodes;

//————————————–constructor
Fibre(){
nodes = new ArrayList();
}

//————————————–setters and getters
void addNode(Node n){
nodes.add(n);
}

void update(){
noFill();
beginShape();
for (Node n:nodes){
n.run();
vertex(n.loc.x,n.loc.y,n.loc.z);
}
endShape();
}
}

//—————————————————–Initialisation——————————————————–//

void setupRandomLandscape(){
m = new MultiValueMap();
forest = new ArrayList();
for (int i=0;i

//create a new tree like so:

//TreeAttractor (_loc (x,y,z), _strength, _crownRadius){
TreeAttractor tree = new TreeAttractor(new Vec3D(random(0,300),random(0,300),random(0,50)), random(1,2.5), random(50,70));
forest.add(tree);
}
}

//———————————import functions

void importLandscape() {
forest = new ArrayList();
//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, zz);
TreeAttractor tree = new TreeAttractor(tmpPt, 1, random(100,120));
forest.add(tree);
}

}

//—————————————————–Node——————————————————–//

class Node {

//location
Vec3D loc;
//parent fibre
Fibre p;
//siblings for spring behaviour
ArrayList siblings;
//neighbours to loop through
ArrayList neighbours;
//defines whether node is part of a bundle or not
Boolean active;
float nodeStiffness;

//————————————–constructor
Node(Vec3D LOC, Fibre P, float STIFF) {
loc = LOC;
p=P;
active = true;
neighbours = new ArrayList();
siblings = new ArrayList();
nodeStiffness = STIFF;
}

//————————————–setters and getters
void activate(Boolean a) {
active = a;
}
void addSibling (Node s) {
siblings.add(s);
}
void setNeighbours(ArrayList N) {
neighbours = N;
}

//————————————–main functions
void run() {
if(active){
attract();
}
//render();
}

void attract() {
ArrayList closest = new ArrayList();
float minD = 999999;

for (Node n: neighbours){
Vec3D diff = loc.sub(n.loc);
float d = diff.magnitude();
if(d>1){
if (d closest.add(diff);
if(closest.size()>numToMove) closest.remove(0);
minD =d;
}
}
}
for (Vec3D v:closest){
float d = v.magnitude();
v.scaleSelf(nodeStiffness/d);
loc.subSelf(v);
spring();
}
}

void spring() {
Vec3D avg = new Vec3D(0,0,0);
for (Node n: siblings){
Vec3D diff = loc.sub(n.loc);
avg.addSelf(diff);
}
avg.scaleSelf(stiffness);
loc.subSelf(avg);
}

void render() {
point(loc.x, loc.y, loc.z);
}
}

//—————————————————–Strand——————————————————–//

class Strand{

ArrayListtrail;
ArrayList pts;
float dropNum;

Strand(Vec3D _loc){

trail = new ArrayList();
pts = new ArrayList();
MapParticle p = new MapParticle(_loc,getCoord(_loc));
p.lock();
pts.add(p);
dropNum = 0;

}

void update(Vec3D _loc){

//update the trail geom every 5 moves
if(dropNum%dropRate==0){
if(trail.size()

MapParticle p = new MapParticle(_loc,getCoord(_loc));
p.lock();
String coord = getCoord(p);
particleMap.put(coord,p);

MapParticle lastP = pts.get(pts.size()-1);
lastP.unlock();

VerletSpring s = new VerletSpring(pts.get(pts.size()-1),p,(p.distanceTo(lastP)),0.5);
physics.addParticle(p);
physics.addSpring(s);

trail.add(s);
pts.add(p);
}else{

physics.removeSpringElements(trail.get(0));
trail.remove(0);
pts.remove(0);
}
dropNum=0;
}
dropNum++;
}

String getCoord(Vec3D p){
String s = “”+floor(p.x/gridRes)+floor(p.y/gridRes)+floor(p.z/gridRes);
return s;
}

void run(){
//render agent trail as lines
strokeWeight(1);
float c =1;
if(agentsOn){
for (VerletSpring l : trail){

float gs = ((trail.size()-c)/trail.size())*255;
stroke(gs);

MapParticle a = (MapParticle) l.a;

a.run();

line (l.a.x,l.a.y,l.a.z,l.b.x,l.b.y,l.b.z);
c+=1;
}
}
}

}

//—————————————————–TrailPt——————————————————–//

class TrailPt {

Vec3D loc;
float d2t;

TrailPt(Vec3D LOC, float D2T){

loc = LOC;
d2t = D2T;

}

}

//—————————————————–TreeAttractor——————————————————–//
class TreeAttractor {
Vec3D loc,initLoc;
float strength;
float crownRadius;
ArrayList bots;

TreeAttractor (Vec3D _loc, float _strength, float _crownRadius){
//assign properties
loc = _loc;
initLoc = loc.copy();;
strength = _strength;
crownRadius = _crownRadius;
bots = new ArrayList();

//spawn robot agents
spawn();
}

void run(){
for (Agent a:bots){
a.run();
}

//move the tree towards all nearby agents
crownSagging();
render();
}

//————————————————————————————-

//Function for creating agents randomly on the sphere of the tree crown (perhaps should be
//a hemisphere)

//————————————————————————————-

void spawn(){
for (int i = 0; i Vec3D fringe = new Vec3D(crownRadius + random(-(crownRadius/3),(crownRadius/3)),0,0);
fringe.rotateZ(random(0,2*PI));
fringe.rotateX(random(0,2*PI));
fringe.rotateY(random(0,2*PI));
fringe.addSelf(loc);
Vec3D vel = new Vec3D(random(-1,1),random(-1,1),random(-1,1));

Agent a = new Agent(fringe,vel,this);

bots.add(a);
}
}

//————————————————————————————-

//Function for moving the centre point of the tree towards the agents that currently have
//this tree as their parent.

//————————————————————————————-

void crownSagging(){
//create zero length default vector
Vec3D moveVel = new Vec3D(0,0,0);
//loop through agents if there are any
if(m.getCollection(this)!=null){
for(Agent a: (ArrayList) m.getCollection(this)){
Vec3D influence = a.loc.sub(loc);
influence.invert();
float d = influence.magnitude();

influence.normalizeTo(maxTreePull/d);
moveVel.addSelf(influence);
}
}
//reduce influence of movevel the further the tree is from original point
Vec3D toOriginalPos = initLoc.sub(loc);
float stretch = toOriginalPos.magnitude();
toOriginalPos.normalizeTo(treeSpring*stretch);

moveVel.addSelf(toOriginalPos);

loc.addSelf(moveVel);

}

void render(){
strokeWeight(0);
point (loc.x,loc.y,loc.z);

}
}

//—————————————————–BundlingInit——————————————————–//

//———————————import functions

void importFibres() {

/*
Set set = m.entrySet();
Iterator it = set.iterator();
while (it.hasNext()) {
Map.Entry entry = (Map.Entry) it.next();
for (Agent a :(ArrayList)entry.getValue()){

//creates a strand from the line in the txt file
Fibre fibre = new Fibre();

for (int j=0;j//create node
TrailPt tmpPt = a.trail.get(j);

Node n = new Node(tmpPt.loc.copy(), fibre, tmpPt.d2t);

//lock the node if its the start or end of the trail,
//or if its the 15th node, or if its attr value is less than 0.1
if (j==0 || j==a.trail.size()-1 || j%15 ==0 ||tmpPt.d2t n.activate(false);
}
fibre.addNode(n);
}
//add strand to draw loop
fibrePop.add(fibre);
}
}

//setup the graph of nodes
setupSiblings();
setupGraph();
*/
}

//———————————setup initial graph

void setupSiblings(){
//loop the fibres
for (Fibre f: fibrePop) {
//loop the nodes

for (int i = 1; iNode currentNode = f.nodes.get(i);
Node lastNode = f.nodes.get(i-1);
Node nextNode = f.nodes.get(i+1);
currentNode.addSibling(lastNode);
currentNode.addSibling(nextNode);
}

}
}

void setupGraph() {
//loop the fibres
int count=0;
for (Fibre f: fibrePop) {
//loop the nodes
for (Node n:f.nodes){
ArrayList neighbours = new ArrayList();

//find neighbours
for (Fibre otherF:fibrePop){
//dont use same fibre
if(otherF!= f){
for(Node otherN:otherF.nodes){

float d = n.loc.distanceTo(otherN.loc);
if(d neighbours.add(otherN);
}

}
}
}

n.setNeighbours(neighbours);
}

println(“Percentage Loaded: “+(float(count)/fibrePop.size())*100);
count++;
}
}

//—————————————————–mapParticle——————————————————–//

class MapParticle extends VerletParticle {

String mapCoord;

MapParticle(Vec3D _loc, String _s) {
super (_loc);
mapCoord = _s;
}

void run() {
//get the grid pos and update if necessary
String newMapCoord = getCoord();
if (newMapCoord != mapCoord) {
particleMap.remove(mapCoord, this);
particleMap.put(newMapCoord, this);
mapCoord = newMapCoord;
}
//attract to neighbouring particles
if (particleMap.getCollection(mapCoord)!=null) {
for (MapParticle p: (ArrayList)particleMap.getCollection(mapCoord)) {
attract(p);
}
}
}

String getCoord(){
String s = “”+Math.floor(x/gridRes)+Math.floor(y/gridRes)+Math.floor(z/gridRes);
return s;
}

void attract(MapParticle P) {
Vec3D diff = sub(P);
diff.invert();
float d = diff.magnitude();
if (d>1) {
diff.scaleSelf(nodeStiffness/d);
addVelocity(diff);
}
}
}

//—————————————————–Saving——————————————————–//

void savePts(){
/*
String[] lines = new String[m.totalSize()];
int c=0;

Set set = m.entrySet();
Iterator it = set.iterator();
while (it.hasNext()) {
Map.Entry entry = (Map.Entry) it.next();
for (Agent a :(ArrayList)entry.getValue()){
lines[c] = “”;

for(TrailPt t:a.trail){
lines[c] =lines[c]+ (t.loc.x +”,” + t.loc.y + “,” + t.loc.z + “/”);
}
c++;
}
}
saveStrings(“lines.txt”, lines);

*/
}

void saveBundles(){
String[] lines = new String[m.totalSize()];
int c=0;
for (Fibre f :fibrePop){
lines[c] = “”;

for(Node n:f.nodes){
lines[c] =lines[c]+ (n.loc.x +”,” + n.loc.y + “,” + n.loc.z + “/”);
}
c++;
}
saveStrings(“lines.txt”, lines);
}