Tutorials | (back to the list of tutorials) |
Agents and AttractorsTo control a large number of agents, an attractor is often introduced. You define attractors to be located somewhere in the space and agents are oriented towards the location of the attractor.
![]()
![]()
![]()
![]()
import processing.opengl.*;
import igeo.*;
void setup(){
size(480,360,IG.GL);
IG.duration(500);
for(int i=0; i < 3; i++){
new MyAttractor(IRand.pt(0, 0, 20, 100, 100, 20));
}
//agents in a matrix
for(int i=0; i < 10; i++){
for(int j=0; j < 10; j++){
new MyLineAgent(new IVec(i*10, j*10, 0),
new IVec(0,0,0.5)).clr(.5,i*0.1,j*0.1);
}
}
}
static class MyAttractor extends IAgent{
IVec pos;
IPoint point;
MyAttractor(IVec p){
pos = p;
point = new IPoint(pos).clr(1.0,0,0);
}
void update(){
// random walk
pos.add(IRandom.pt(-5,5));
}
}
static class MyLineAgent extends IAgent{
IVec pos, dir;
MyAttractor attractor = null;
double minDist = -1;
MyLineAgent(IVec p, IVec d){
pos = p;
dir = d;
}
void interact(IDynamics agent){
//searching the closest attractor
if(agent instanceof MyAttractor){
MyAttractor attr = (MyAttractor)agent;
double dist = attr.pos.dist(pos);
//first attractor to check
if(attractor == null){
attractor = attr;
minDist = dist;
}
//if less than minimum, it's new minimum
else if(dist < minDist){
attractor = attr;
minDist = dist;
}
}
}
void update(){
//if any closest attractor found, attractor is not null
if(attractor!=null){
IVec dif = attractor.pos.dif(pos);
dif.len(dir.len());
dir = dif;
//reset attractor and minDist
attractor = null;
minDist = -1;
}
new ICurve(pos.dup(), pos.dup().add(dir)).clr(clr());
pos.add(dir);
}
}
One important part of the code is
the code to find the closest attractor out of
multiple attractors in the space.
This is a typical algorithm to search data
which have a minimum or maximum value.
Each time
Simple Boundary of Agents![]()
![]()
![]()
![]()
import processing.opengl.*;
import igeo.*;
void setup(){
size(480, 360, IG.GL);
IG.duration(1000);
new MyBoundary(-200,-200,200,200);
new MyHexAgent(new IVec(0,0,0), new IVec(0,10,0));
}
class MyBoundary extends IAgent{
double minx, maxx, miny, maxy;
MyBoundary(double x1, double y1, double x2, double y2){
minx = x1;
miny = y1;
maxx = x2;
maxy = y2;
IG.rect(new IVec(minx, miny, 0), maxx-minx, maxy-miny);
}
}
class MyHexAgent extends IAgent{
IVec pos, dir;
double depth=0, depthInc=0.5, hue=0;
MyHexAgent(IVec p, IVec d){ pos = p; dir = d; }
void interact(IDynamics agent){
if(agent instanceof MyBoundary){
MyBoundary boundary = (MyBoundary)agent;
//checking if next position is out of the boundary
IVec nextPos = pos.cp(dir);
if(nextPos.x < boundary.minx){
dir.ref(IG.xaxis); //reflect on x-plane
}
else if(nextPos.x > boundary.maxx){
dir.ref(IG.xaxis); //reflect on x-plane
}
if(nextPos.y < boundary.miny){
dir.ref(IG.yaxis); //reflect on y-plane
}
else if(nextPos.y > boundary.maxy){
dir.ref(IG.yaxis); //reflect on y-plane
}
}
}
void update(){
createHexGeometry();
pos.add(dir);
//random shift of direction
if(IRand.percent(10)){ dir.rot(IG.zaxis,PI/3); }
else if(IRand.percent(10)){ dir.rot(IG.zaxis,-PI/3); }
//random shift of height
if(IRandom.percent(10)){ depthInc *= -1; }
depth += depthInc;
hue += 0.002;
}
void createHexGeometry(){
//creating hexagonal extrusion
IVec[] cpts = new IVec[6];
for(int i=0; i < 6; i++){
cpts[i] =
dir.dup().rot(IG.zaxis,PI/3*i+PI/6).div(2).add(pos);
}
IG.extrude(cpts, 1, true, depth).hsb(hue,1,1);
}
}
Blocking Agents![]()
![]()
![]()
![]()
import processing.opengl.*;
import igeo.*;
void setup(){
size(480, 360, IG.GL);
IG.duration(150);
new LineAgent(new IVec(0,0,0), new IVec(1,0,0));
for(int i=-1; i<=1; i+=2){
for(int j=-1; j<=1; j+=2){
for(int k=-1; k<=1; k+=2){
new LineBlockAgent(new IVec(40*i,40*j,40*k), 40);
}
}
}
}
static class LineBlockAgent extends IAgent{
IVec pos;
double radius;
LineBlockAgent(IVec p, double rad){
pos = p; radius = rad;
}
void interact(IDynamics agent){
if(agent instanceof LineAgent){
LineAgent lineAgent = (LineAgent)agent;
if(lineAgent.pt2.dist(pos) < radius){
lineAgent.del();
}
}
}
void update(){
if(time==0){ new ISphere(pos,radius).clr(0,1,1,0.2); }
}
}
static class LineAgent extends IAgent{
static double length = 2;
static double clearance = 1.99; //less than length
IVec pt1, pt2;
boolean isColliding=false;
LineAgent(IVec pt, IVec dir){
pt1 = pt;
pt2 = pt.dup().add(dir.dup().len(length));
}
void interact(IDynamics agent){
if(time == 0){ //only in the first time
if(agent instanceof LineAgent){
LineAgent lineAgent = (LineAgent)agent;
// checking clearance of end point
if(lineAgent.pt2.dist(pt2) < clearance){
isColliding=true;
}
}
}
}
void update(){
if(isColliding){
del();
}
else if(time == 0){ //if not colliding
new ICurve(pt1,pt2).clr(0);
IVec dir = pt2.dif(pt1);
//rotation axis with random direction
IVec axis = IRandom.pt(-1,1).len(1);
if(IRandom.percent(50)){ //bend
new LineAgent(pt2, dir.dup().rot(axis,
IRandom.get(PI/3,PI/3*2)));
}
if(IRandom.percent(50)){ //bend the other way
new LineAgent(pt2, dir.dup().rot(axis,
-IRandom.get(PI/3,PI/3*2)));
}
if(IRandom.percent(90)){ //straight
new LineAgent(pt2, dir.dup());
}
}
}
}
HOME
FOR PROCESSING
DOWNLOAD
DOCUMENTS
TUTORIALS (Java /
Python)
GALLERY
SOURCE CODE(GitHub)
PRIVACY POLICY
ABOUT/CONTACT