(n)certainties RMIT-UTS Spring 012

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

brico_uric equilibria coding

/*
 * Copyright (c) 2012 Gwyllim Jahn
 
 * Developed By Gwyllim Jahn, Javed de Costa, and Intje Siswandi
 * 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.
 *
 *
 * 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.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 */

 

///// 2.5 d reaction diffusion: custom grey scott as voxel array with agent interaction. iterative, cyclic model – diffusion and agent interaction alternates

MAIN:

import processing.video.*;

import processing.dxf.*;

import processing.opengl.*;
import javax.media.opengl.*;
import peasy.org.apache.commons.math.*;
import peasy.*;
import peasy.org.apache.commons.math.geometry.*;

import toxi.math.conversion.*;
import toxi.geom.*;
import toxi.geom.mesh2d.*;
import toxi.util.datatypes.*;
import toxi.util.events.*;
import toxi.geom.mesh.subdiv.*;
import toxi.geom.mesh.*;
import toxi.util.*;
import toxi.math.noise.*;
import toxi.volume.*;

/**

*/

import toxi.sim.grayscott.*;
import toxi.math.*;
import controlP5.*;
import toxi.color.*;
//——————————Volume Params

int DX=120;
int DY=60;
int DZ=60;
OscillatingVolume volumeU;
OscillatingVolume volumeV;
OscillatingVolume volumeF;
VolumetricBrush brushU;
VolumetricBrush brushV;
VolumetricBrush brushF;
Vec3D SCALE=new Vec3D(DX,DY,DZ);
int sX = int(SCALE.x/DX);
int sY = int(SCALE.y/DY);
int sZ = int(SCALE.z/DZ);

//——————————Agent params

//==============================
//Trail length and agent speed – obviously if you want
//to run the agents for ages, you will need longer trails.
//agent speed also has a significant effect on the root geom.
int trailLength = 200;
float agentSpeed = 1;
//ammount to confine to a particular threshold
float confineToThreshold = 0.1;
//distance between trail points
int dropSpacing = 2;
//number of agents
int numAgents = 50;

//if the agent cant find a value in its neighbouring
//voxels that is less than this number from the ideal
//threshold (by default 0.5) =dead. LOWER=MORE DEATH
float cutoffToDie = 0.2;
//if the agent finds a value in its neighbouring
//voxels that is less than this number from the ideal
//threshold (by defauly 0.5), then it starts counting
//towards branching LOWER=LESS BRANCHING
float cutoffToThinkAboutBranching = 0.0;
//The agent needs to have been within its branching cutoff
//for this number of steps, then it branches.
int numEasyMovesBeforeBranch =10;
//number of new agents to make.
int splitIntoAgents=3;

//==============================
//parameters for how the agents modify the field…
//how big the brush is that modifies the diffusion f parameter.
float fBrushSize = 2;
float rootRadius = 2;
//density value for the agents – a negative
//number removes material from the diffusion,
//numbers closer to 0 mean that more agents need to fly
//down a similar path to actually remove the material …
float density = -0.01;
//controls the strength of the diffusion parameter.
float fDensity = -0.05;
Boolean drawAgents = true;
Boolean saveRaw = false;
MovieMaker mm;
Boolean filming =true;

//——————————Diffusion params
int NUM_ITERATIONS = 10;
ControlP5 controlP5;
ControlWindow controlWindow;
GrayScott gs;
ToneMap toneMap;
PImage img;

float f = 0.0210;
float k = 0.076;
float dU = 0.0976;
float dV = 0.0653;

PeasyCam cam;
World world;
int currentLevel =0;
Boolean building = true;
Boolean firstRun = true;
Boolean cycle = true;

//==============================
//make this false to make the simution
//reset to the ground plane rather than oscillating
Boolean oscillate = false;

//==============================
//sets how long the agents and the diffusion
//run for. REMEMBER cant be larger than the actual field.
int agentSteps = 50;
int numSteps = DZ;
int cycleIterator = numSteps;

void setup() {
size(500,500,OPENGL);
cam = new PeasyCam(this,200);
mm = new MovieMaker(this, width, height, “drawing.mov”,30, MovieMaker.ANIMATION, MovieMaker.HIGH);

gs=new PatternedGrayScott(DX,DY,false);
img=loadImage(“r1_60.png”);
gs.setCoefficients(f,k,dU,dV);
// create a color gradient for 256 values
ColorGradient grad=new ColorGradient();
// NamedColors are preset colors, but any TColor can be added
// see javadocs for list of names:
// http://toxiclibs.org/docs/colorutils/toxi/color/NamedColor.html
grad.addColorAt(0,NamedColor.WHITE);
grad.addColorAt(16,NamedColor.CORNSILK);
grad.addColorAt(128,NamedColor.PINK);
grad.addColorAt(192,NamedColor.PURPLE);
grad.addColorAt(255,NamedColor.BLACK);
// this gradient is used to map simulation values to colors
// the first 2 parameters define the min/max values of the
// input range (Gray-Scott produces values in the interval of 0.0 – 0.5)
// setting the max = 0.33 increases the contrast
toneMap=new ToneMap(0,0.33,grad);
gs.seedImage(img.pixels,img.width,img.height);

//setup voxels
volumeU=new OscillatingVolume(SCALE,DX,DY,DZ);
volumeV=new OscillatingVolume(SCALE,DX,DY,DZ);
volumeF=new OscillatingVolume(SCALE,DX,DY,DZ);
brushU=new RoundBrush(volumeU,5);
brushV=new RoundBrush(volumeV,5);
brushF=new RoundBrush(volumeF,5);

volumeU.setLandscape(gs.u,0);
volumeV.setLandscape(gs.v,0);

setupP5();

world = new World();
}

void draw() {
background(255);
gs.setCoefficients(f,k,dU,dV);
//diffusion cycle
if(building){

if(!firstRun){
//read in the layer
gs.u = volumeU.getLayer(currentLevel);
gs.v = volumeV.getLayer(currentLevel);

}

// update the simulation a few time steps
for(int i=0; i<NUM_ITERATIONS; i++) {
gs.update(1);
}

//if first run then just write the values, otherwise
//modify the values.
if(firstRun){
volumeU.setLandscape(gs.u,currentLevel);
volumeV.setLandscape(gs.v,currentLevel);
}else{
volumeU.modLandscape(gs.u,currentLevel);
volumeV.modLandscape(gs.v,currentLevel);
}

//toggle moving up or down and change the level
if(oscillate){
if(cycle){
currentLevel++;
}else{
currentLevel–;
}
}else{
currentLevel++;
}
cycleIterator–;
//completed the cycle
if(cycleIterator <=0){
building = false;
if(firstRun) {
firstRun = false;
world.spawnOnThreshold();
//world.spawn();
}
cycle = !cycle;
cycleIterator = agentSteps;
if(!oscillate) currentLevel = 0;
}

}else{

//setup the oscilation
world.update();

cycleIterator–;
if(cycleIterator<=0){
building = true;
cycleIterator = numSteps;
}
}

volumeV.drawPoints();

if(filming) mm.addFrame();
}

void keyPressed() {
if(key == ‘r’){
gs.reset();
}

if(key == ‘q’){
GsaveData(volumeV,”G:/ncertainties/export/”+day()+”_”+hour()+”_”+minute()+”_”+second()+”voxels.raw”);
}

if(key == ‘w’){
savePts();
}

if(key == ‘e’){
saveParams();
}

if(key == ‘ ‘){
mm.finish();
}
}

 

AGENTS:

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

class Agent {

Vec3D loc,vel;
ArrayList<Vec3D> trail;
float gVal;
float dropNum;
boolean alive;
int id;
float aX,aY,aZ,lastBest;
int easy;

Agent(Vec3D _loc, Vec3D _vel,int _id){
loc = _loc;
vel = _vel;
trail = new ArrayList<Vec3D>();
gVal = 255;
dropNum = 0;
alive = true;
id = _id;
easy = 0;
lastBest = 0;
}

void run(){
if (alive){
getGridPos();
pushPullToData(true,0.0005);

branch();

volBrushLocation();

update();
}
render();
}

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

//Behaviour for avoiding the worst (highest greyscale value) data within a proximity,
//and interpolating the agent velocity away that point.

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

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);
if(loc.x>SCALE.x || loc.y >SCALE.y || loc.z>SCALE.z)vel.invert();
if(loc.x<0 || loc.y <0 || loc.z<0)vel.invert();
}

void pushPullToData(Boolean push, 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 = volumeV.getVoxelAt(int(aX+i),int(aY+j),int(aZ+k));
if(val>0.0001 &&val<0.01){
float threshVal = abs(val-t);

if(threshVal <bestThreshDiff) bestThreshDiff=threshVal;
//float threshVal = val;
vec.scaleSelf(1-(threshVal*threshVal));
bestThreshPt.addSelf(vec);
}
}
}
}
}

//attract to threshold
vel.interpolateToSelf(bestThreshPt,confineToThreshold);

//SOME VERY TOUCHY VALUES HERE BECAUSE OF THE NOISE

// bestThreshDiff*=10;

//die if in a really crappy possy
if(bestThreshDiff>cutoffToDie)alive=false;

//if in a good posy, see if we should branch
if(bestThreshDiff<cutoffToThinkAboutBranching){
easy++;
}else{
easy = 0;
}
}

Boolean moveToThreshold(int searchRad){

getGridPos();

for (int i =-searchRad;i<=searchRad;i++){
for (int j =-searchRad;j<=searchRad;j++){
for (int k =-searchRad;k<=searchRad;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 = volumeV.getVoxelAt(int(aX+i),int(aY+j),int(aZ+k))*200;

if(val>=1&&val<5){
//move
loc.addSelf(vec);
return true;
}
}
}
}
}
//if cant find anything then die
loc = new Vec3D(random(0,DX),random(0,DY),random(10,DZ));
return false;

}
//————————————————————————————-

//creates new agents from this one if the agent path is easy.

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

void branch(){

if(easy>numEasyMovesBeforeBranch){
easy =0;
for (int i = 1;i<splitIntoAgents;i++){

Vec3D aVel = vel.copy();
aVel.rotateX(random(-0.5,0.5));
aVel.rotateY(random(-0.5,0.5));
aVel.rotateZ(random(-0.5,0.5));

Agent a = new Agent(loc.copy(),aVel,id);
world.newPop.add(a);
}
alive = false;
}

}

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

//function for constraining agent movement to only z if they are moving within
//very dense material

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

void volBrushLocation(){
brushF.setSize(fBrushSize);
brushF.drawAtGridPos(aX,aY,aZ,fDensity);
brushV.setSize(rootRadius);
brushV.drawAtGridPos(aX,aY,aZ,(density));
if(aZ-5>0) brushV.drawAtGridPos(aX,aY,aZ-5,-density*2);
// brushV.drawAtGridPos(aX,aY,aZ,density);
}

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

//Functions for managing trails and rendering the agent.

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

void update(){
//move the agent randomly
vel.normalizeTo(agentSpeed);
//vel.interpolateToSelf(new Vec3D (0,0,1),vert);
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;
for (Vec3D l : trail){
if(ls !=null){
stroke(0);
line (l.x,l.y,l.z,ls.x,ls.y,ls.z);
}
ls = l;
}
}

}

 

ARRAY OSCILLATIONS

class OscillatingVolume extends VolumetricSpaceArray {

int z;

public OscillatingVolume (Vec3D _S, int _DX, int _DY,int _DZ) {
super(_S,_DX,_DY,_DZ);
z = 0;
}

//—————————

void setLandscape(float[] d,int l){

for (int i=0;i<resX;i+=1){
for (int j =0;j<resY;j+=1){
//set the voxel to the pixel
setVoxelAt(i,j,l,d[(j*resX)+i]);
}
}
//set the current layer
z = l;
}

void modLandscape(float[] d,int l){

for (int i=0;i<resX;i+=1){
for (int j =0;j<resY;j+=1){
//set the voxel to the pixel
float mod = d[(j*resX)+i];
float existing = getVoxelAt(i,j,l);
mod = (mod+existing)/2;
setVoxelAt(i,j,l,mod);
}
}
z = l;
}

float[] getLayer(int level){

float[] d = new float[resX*resY];

for (int i=0;i<resX;i+=1){
for (int j =0;j<resY;j+=1){
//set the voxel to the pixel
d[(j*resX)+i] = getVoxelAt(i,j,level);
}
}
return d;
}

void drawPoints() {
int r=1;
for(int i=0;i<resX;i+=r) {
for(int j=0;j<resY;j+=r) {
for(int k=0;k<resZ;k+=r) {
float v = getVoxelAt(i,j,k)*2000;
if(v>=1&&v<5){
stroke(100);
point(i,j,k);
}
}
}
}

}

}

 

VOXEL FUNCTIONS

public void GsaveData(OscillatingVolume 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();
}
}

WORLD

//Class to manage addition and subtraction of agents from
//the environment

class World {

ArrayList<Agent> agentPop;
ArrayList<Agent> newPop;

World(){

agentPop = new ArrayList<Agent>();
newPop = new ArrayList<Agent>();

}

//————————–main function

void update(){
newPop = new ArrayList<Agent>();

runAgents();

if(newPop.size()>0) addAgents();
}

void runAgents(){
int c = 0;
for (Agent a :agentPop){
a.run();
}
}

void addAgents(){
agentPop.addAll(newPop);
}
void spawn(){
newPop = new ArrayList();
agentPop = new ArrayList();
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(0,DX),random(0,DY),3);
Vec3D vel = new Vec3D(0,0,3);
Agent a = new Agent(loc,vel,i);
agentPop.add(a);
}
}

void spawnOnThreshold(){
newPop = new ArrayList();
agentPop = new ArrayList();
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(0,DX),random(0,DY),random(10,DZ));
Vec3D vel = new Vec3D(0,0,3);
Agent a = new Agent(loc.copy(),vel,i);

//try to find a threshold 5 times
boolean searching =true;
int c= 0;

while(searching==true) {
if(a.moveToThreshold(5)){
searching = false;
agentPop.add(a);
}else{
c++;
}
if(c>50) searching = false;
}

}
}
}

DIFFUSION

class PatternedGrayScott extends GrayScott {
public PatternedGrayScott(int w, int h, boolean tiling) {
super(w,h,tiling);
}

public float getFCoeffAt(int x, int y) {
if(x*y*currentLevel<volumeF.getData().length){
return (f+volumeF.getVoxelAt(x,y,currentLevel));
}else{
return f;
}
}
/*
public float getKCoeffAt(int x, int y) {
Vec3D val = new Vec3D(mouseX-x,mouseY-y,0);
float d = val.magnitude();
d = k-(map(d,0,height,0,1))*0.04;
return d;
}

*/
}