Python Tutorials | (back to the list of tutorials) |
Each of those three rules has different distance range. Typically cohesion has the largest range, alignment has the second largest and the separation has the smallest, but depending on the intended behaviors of swarm agents, this order can be changed.
For more description about the algorithm of boids, please see Craig Reynolds's boids page .
Other agents are counted as neighbor agents if the distance to the agent is less than the threshold distance of cohesion (cohesionDist). The center of neighbors is calculated by adding all position of them and then divided by the number of the neighbors (count). The force is calculated by taking difference vector between the current agent and the center. The amount of the force is adjusted by the coefficient cohesionRatio.
add_library('igeo') def setup() : size(480, 360, IG.GL) IG.duration(30) for i in range(100) : MyBoid(IRand.pt(100,100,0), IRand.pt(-10,-10,0,10,10,0)).clr(IRand.clr()) class MyBoid(IParticle) : cohesionDist = 50 cohesionRatio = 5 def __init__(self, p, v) : IParticle.__init__(self,p,v) self.prevPos = None def interact(self, agents) : center = IVec() #zero vector count = 0 for agent in agents : if isinstance(agent, MyBoid) : if agent is not self : if agent.pos().dist(self.pos()) < MyBoid.cohesionDist : center.add(agent.pos()) count+=1 if count > 0 : center.div(count) #center of neighbors force = center.sub(self.pos()) #difference of position self.push(force.mul(MyBoid.cohesionRatio)) def update(self) : #drawing line curPos = self.pos().cp() if self.prevPos is not None : IG.crv(self.prevPos, curPos).clr(self.clr()) self.prevPos = curPos
In the same way with cohesion rule,
the threshold distance of separation (separationDist)
is used to determine if
the other agent is close enough to
get away from.
The force is calculated by a summantion
of each force from close agents.
The amount individual force is reset by
this formula
force.len( separationDist - dist )
to make the closer agent's force larger and
farther force smaller (and the agent at the distance of separationDist,
the force is zero).
The total force for the current agent to move away
(separationForce) is adjusted by the
coefficient separationRatio.
There is one additional note about this conditional statement dist != 0 in the in the if-condition. It is to avoid the case when the two agents' positions are identical and the direction to push out the agent cannot be defined from the difference. To be more precise, the agent should be pushed out in some default direction when they are in a same position but in the following example, this case is just excluded to keep it simple.
add_library('igeo') def setup() : size(480, 360, IG.GL) IG.duration(60) for i in range(100) : MyBoid(IRand.pt(100,100,0),IRand.pt(-5,-5,0,20,20,0)).clr(IRand.clr()) class MyBoid(IParticle) : separationDist = 30 separationRatio = 5 def __init__(self, p, v) : IParticle.__init__(self,p,v) self.prevPos = None def interact(self, agents) : separationForce = IVec() #zero vector count = 0 for agent in agents : if isinstance(agent, MyBoid) : if agent is not self : dist = agent.pos().dist(self.pos()) if dist < MyBoid.separationDist and dist!=0 : force = self.pos().dif(agent.pos()) force.len(MyBoid.separationDist - dist) #the closer the larger separationForce.add(force) count+=1 if count > 0 : separationForce.div(count) #average force separationForce.mul(MyBoid.separationRatio) self.push(separationForce) def update(self) : #drawing line curPos = self.pos().cp() if self.prevPos is not None : IG.crv(self.prevPos, curPos).clr(self.clr()) self.prevPos = curPos
Other agents within the range of the threshold distance of alignment (alignmentDist) are counted as neighbors and the average of their velocities is calculated. Then the difference between the average velocity and the current agent's are measured and the force is added to the direction of the vector difference of these two velocities. The amount of force is adjusted by the coefficient alignmentRatio.
add_library('igeo') def setup() : size(480, 360, IG.GL) IG.duration(100) for i in range(100) : MyBoid(IRand.pt(100,100,0),IRand.pt(-5,-5,0,20,20,0)).clr(IRand.clr()) class MyBoid(IParticle) : alignmentDist = 40 alignmentRatio = 5 def __init__(self, p, v) : IParticle.__init__(self,p,v) self.prevPos = None def interact(self, agents) : averageVelocity = IVec() #zero vector count = 0 for agent in agents : if isinstance(agent, MyBoid) : if agent is not self : if agent.pos().dist(self.pos()) < MyBoid.alignmentDist : averageVelocity.add(agent.vel()) count+=1 if count > 0 : averageVelocity.div(count) force = averageVelocity.sub(self.vel()) #difference of velocity self.push(force.mul(MyBoid.alignmentRatio)) def update(self) : #drawing line curPos = self.pos().cp() if self.prevPos is not None : IG.crv(self.prevPos, curPos).clr(self.clr()) self.prevPos = curPos
add_library('igeo') def setup() : size(480, 360, IG.GL) IG.duration(150) for i in range(100) : MyBoid(IRand.pt(100,100,0),IRand.pt(-5,-5,0,20,20,0)).clr(IRand.clr()) class MyBoid(IParticle) : cohesionDist = 50.0 cohesionRatio = 5.0 separationDist = 30.0 separationRatio = 5.0 alignmentDist = 40.0 alignmentRatio = 5.0 def __init__(self, p, v) : IParticle.__init__(self,p,v) self.prevPos = None def cohere(self, agents) : center = IVec() #zero vector count = 0 for agent in agents : if isinstance(agent, MyBoid) and agent is not self : if agent.pos().dist(self.pos()) < MyBoid.cohesionDist : center.add(agent.pos()) count+=1 if count > 0 : self.push(center.div(count).sub(self.pos()).mul(MyBoid.cohesionRatio)) def separate(self, agents) : separationForce = IVec() #zero vector count = 0 for agent in agents : if isinstance(agent, MyBoid) and agent is not self : dist = agent.pos().dist(self.pos()) if dist < MyBoid.separationDist and dist!=0 : separationForce.add(self.pos().dif(agent.pos()).len(MyBoid.separationDist - dist)) count+=1 if count > 0 : self.push(separationForce.mul(MyBoid.separationRatio/count)) def align(self, agents) : averageVelocity = IVec() #zero vector count = 0 for agent in agents : if isinstance(agent, MyBoid) and agent is not self : if agent.pos().dist(self.pos()) < MyBoid.alignmentDist : averageVelocity.add(agent.vel()) count+=1 if count > 0 : self.push(averageVelocity.div(count).sub(self.vel()).mul(MyBoid.alignmentRatio)) def interact(self, agents) : self.cohere(agents) self.separate(agents) self.align(agents) def update(self) : #drawing line curPos = self.pos().cp() if self.prevPos is not None : IG.crv(self.prevPos, curPos).clr(self.clr()) self.prevPos = curPos
add_library('igeo') def setup() : size(480, 360, IG.GL) IG.duration(150) for i in range(100) : MyBoid(IRand.pt(100,100,0),IRand.pt(-5,-5,0,20,20,0)).clr(IRand.clr()) class MyBoid(IParticle) : cohDist = 50 cohRatio = 0.05 sepDist = 30 sepRatio = 0.05 aliDist = 40 aliRatio = 0.05 def __init__(self, p, v) : IParticle.__init__(self,p,v) self.prevPos = None def interact(self, agents) : for agent in agents : if isinstance(agent, MyBoid) and agent is not self : if self.dist(agent) < MyBoid.cohDist : self.push(agent.dif(self).mul(MyBoid.cohRatio)) if self.dist(agent) < MyBoid.sepDist : self.push(self.dif(agent).len((MyBoid.sepDist-self.dist(agent))*MyBoid.sepRatio)) if self.dist(agent) < MyBoid.aliDist : self.push(agent.vel().dif(self.vel()).mul(MyBoid.aliRatio)) def update(self) : #drawing line curPos = self.pos().cp() if self.prevPos is not None : IG.crv(self.prevPos, curPos).clr(self.clr()) self.prevPos = curPos
add_library('igeo') def setup() : size(480, 360, IG.GL) IG.duration(150) for i in range(100) : b = MyBoid(IRand.pt(100,100,0),IRand.pt(-5,-5,0,20,20,0)) b.clr(IRand.clr()) b.cohesionDist(50) b.cohesionRatio(5) b.separationDist(30) b.separationRatio(5) b.alignmentDist(40) b.alignmentRatio(5) class MyBoid(IBoid) : def __init__(self, p, v) : IParticle.__init__(self,p,v) self.prevPos = None def update(self) : #drawing line curPos = self.pos().cp() if self.prevPos is not None : IG.crv(self.prevPos, curPos).clr(self.clr()) self.prevPos = curPos
The first setting below have setting of smaller threshold distance for each rule. As result it creates smaller clusters of swarms.
add_library('igeo') def setup() : size(480, 360, IG.GL) IG.duration(100) for i in range(100) : b = MyBoid(IRand.pt(100,100,0),IRand.pt(-5,-5,0,20,20,0)) b.clr(IRand.clr()) b.cohesionDist(21) b.cohesionRatio(5) b.separationDist(10) b.separationRatio(5) b.alignmentDist(15) b.alignmentRatio(5) class MyBoid(IBoid) : def __init__(self, p, v) : IParticle.__init__(self,p,v) self.prevPos = None def update(self) : #drawing line curPos = self.pos().cp() if self.prevPos is not None : IG.crv(self.prevPos, curPos).clr(self.clr()) self.prevPos = curPos
The next setting below has a smaller force ratio in the alignment rule. Because the force to align the velocities of agents are weak, it oscillates around the center with the attraction and repulsion forces from the cohesion and separation rule, not converging into the same velocity for a while.
add_library('igeo') def setup() : size(480, 360, IG.GL) IG.duration(300) for i in range(100) : b = MyBoid(IRand.pt(100,100,0),IRand.pt(-5,-5,0,20,20,0)) b.clr(IRand.clr()) b.cohesionDist(50) b.cohesionRatio(5) b.separationDist(30) b.separationRatio(5) b.alignmentDist(40) b.alignmentRatio(0.5) class MyBoid(IBoid) : def __init__(self, p, v) : IParticle.__init__(self,p,v) self.prevPos = None def update(self) : #drawing line curPos = self.pos().cp() if self.prevPos is not None : IG.crv(self.prevPos, curPos).clr(self.clr()) self.prevPos = curPos
The setting below has a larger threshold distance for the separation rule and a smaller threshold distance for the cohesion rule. Because of this inverted order of cohesion and separation, agents try to go away but still creating smaller clusters with the alignment rule.
add_library('igeo') def setup() : size(480, 360, IG.GL) IG.duration(800) for i in range(100) : b = MyBoid(IRand.pt(100,100,0),IRand.pt(-5,-5,0,20,20,0)) b.clr(IRand.clr()) b.cohesionDist(30) b.cohesionRatio(5) b.separationDist(50) b.separationRatio(5) b.alignmentDist(40) b.alignmentRatio(5) class MyBoid(IBoid) : def __init__(self, p, v) : IParticle.__init__(self,p,v) self.prevPos = None def update(self) : #drawing line curPos = self.pos().cp() if self.prevPos is not None : IG.crv(self.prevPos, curPos).clr(self.clr()) self.prevPos = curPos