// Processing source code : Braitenberg's Vehicles // Based on program written by william ngan // SensoryField class stub coded by Prof. Mateas and Mayhew Seavey in 2004... // ...and slightly-further hackery added by Jason Alderman in 2005. // Press UP and DOWN arrows to increase number of Braitenberg vehicles. // Press number keys 1-5 to change the number of sources on the sensory field. // Press SPACE to toggle the PImage ground visible or invisible. Vehicle[] robots; SensoryField lightsGround; boolean mouseDown; PImage ground; boolean bounded = true; // vehicles run into world boundary (if true), wrap around (if false) int numOfLights = 2; int numOfRobots = 2; float move_speed = PI/6; boolean groundIsDrawn; void setup() { size( 300, 300 ); framerate( 30 ); ellipseMode( CENTER ); rectMode( CENTER ); noStroke(); groundIsDrawn = false; // Don't draw the sensoryfield grounds on the background. ground = new PImage( width, height ); robots = new Vehicle[10]; // Create an array of the Braitenberg vehicles. for (int i=0; i='1' && key<='5') { numOfLights = 5-('5'-key); updateGround(); // updateGround so that it adds or removes the lights from the ArrayList! } if (key==' '){ groundIsDrawn= (!groundIsDrawn); println("ground-drawn boolean is now: "+groundIsDrawn); updateGround(); // updateGround so that it draws the PImage! (or doesn't, when it shouldn't!) } if (key==CODED) { if (keyCode==UP) { numOfRobots++; numOfRobots = min( numOfRobots, robots.length-1 ); } else if (keyCode==DOWN) { numOfRobots--; numOfRobots = max( numOfRobots, 1 ); } } } void updateGround() { float sum; int c; int px = 5; color cc; int temp; // if a keypress has changed the number of lights, then add or remove sources from the arraylist of lightsGround if(numOfLights!=lightsGround.sources.size()){ if(numOfLights < lightsGround.sources.size()){ // if the number of light sources has been decreased... temp = lightsGround.sources.size() - numOfLights; for(int i=0; iwidth) ? x-width : x ); y = ( y<0 ) ? height+y : ((y>height) ? y-height : y ); // If the above looks completely confusing to you, check out // http://processing.org/reference/conditional_.html } } void checkCollision() { float dx, dy, da; for (int i=0; iTWO_PI) angle -= TWO_PI; float temp = sin(angle)*HALF_PI+ainc; ellipse( x+cos(temp)*10*dir, y+sin(temp)*10*dir, 20, 20 ); } } class Sensor { float x, y; float maxReading; float sense; Sensor( float x, float y ) { this.x = x; this.y = y; maxReading = 1; } void setLocation( float x, float y ) { this.x = x; this.y = y; } /* float getSense(boolean plus) { float sum = red( ground.get( (int)x, (int)y ) )/255.0; sum = (plus) ? sum : 1-sum; sense = (specialSense) ? nonlinear( sum, maxReading ) : 1-sum; return sense; }*/ // This is an example of non-linear threshhold sensing. Returns 0 // until the normalized sensory value = 0.5, then returns a linear value // between 0.5 and 1. float getNonlinearSense() { // old code, reading from PImage ground, was: // float val = red( ground.get( (int)x, (int)y ) )/255.0; float val = lightsGround.get( (int)x, (int)y )/255.0; if (val < 0.5) return 0; else return val; } // Returns 0 when sensory value is maximum, 1 when it's minimum float getInverseSense() { float val = lightsGround.get( (int)x, (int)y )/255.0; sense = 1 - val; return sense; } // Returns 1 when sensory value is maximum, 0 when it's minimum float getSense() { sense = lightsGround.get( (int)x, (int)y )/255.0; return sense; } void drawMe() { fill(255); ellipse( x, y, 16, 16 ); fill(200*sense,0,0); ellipse( x, y, 7, 7 ); } } class Source { float x, y; float strength; // between 0 to 1 float max_radius; boolean dragging = true; int id; Source( float x, float y, float strength, float max_radius, int id ) { this.x = x; this.y = y; this.strength = strength; this.max_radius = max_radius; this.id = id; } void setLocation( float x, float y ) { this.x = x; this.y = y; } String getLocation(){ return ""+this.x+","+this.y; } void drawMe() { checkCollision(); // dragging? if (mouseDown && mouseX>x-10 && mouseXy-10 && mouseY width) x = width; if(x < 0) x = 0; if(y > height) y = height; if(y < 0) y = 0; //-- for (int i=0; i= max_radius) return ((plus) ? 0 : 1); // Strength of source falls of linearly in distance (up to max_radius) from source // d = strength*(d/max_radius); // Strength of source falls off as a function of the cosine of distance (up to max_radius) from source d = 1-nonlinear( d, max_radius ); return ((plus) ? 1-d : d ); } } // end Source // The function nonlinear(r, rmax) is used by several bits of code above, so it falls inside the main program. // Returns a value between 0 and 1 (assume r & rmax >= 0) // Returns 0 if r is >= rmax // Returns 1 if r = 0 float nonlinear(float r, float rmax) { float f = (rmax - Math.min(r, rmax)) / rmax; return 0.5 - 0.5*cos(f*PI); } // SensoryField is a class that contains our 2D array of sensory values (instead of using a PImage). class SensoryField { float[][] field; // A 2D array of summed sensory values. The 2D array should be the same size as the processing window. int w; // The width of the field int h; // The height of the field ArrayList sources; // The list of sources that produce values for this SensoryField int pixelSize; // Determines the block size at which we sample the field. Change the value (in the constructor argument) // to increase the sample size. The bigger the value, the more pixelated the field, but the less // computational work it takes to update the field. SensoryField(int w, int h, int ps) { field = new float[w+100][h+100]; // ...making a sensory field with 50 elements-worth // of buffer on each side, so that when a vehicle or source goes to the edge of the // screen, there aren't negative index values for the 2D array (something that // PImages don't care about, but arrays do!). this.w = w; this.h = h; sources = new ArrayList(); pixelSize = ps; } // Get the sensory value at a specific point. float get(int x, int y){ return field[x+50][y+50]; } // Add a source to the field. void addSource(Source s) { sources.add(s); } // Delete a source from the field. void deleteSource(int j) { sources.remove(j); } // Get a source from the sources arraylist. Source getSource(int i){ return (Source)sources.get(i); } // Update the field. For every point in the field, iterate over the sources to determine // the sensory value at each point. void update() { float sum; // We're going to iterate through the all of the elements of the sensory field, // but increment by the pixelSize in the both directions... for(int i = 0; i < w; i += pixelSize){ for(int k = 0; k < h; k += pixelSize){ // Now sum up the sensory readings for each source in the field... sum = 0; for(int m=0; m