Tutorials | (back to the list of tutorials) |
To 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
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); } }
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()); } } } }