# In this simulation I will give a single instruction to each individual in the swarm.
# Choose another individual who is not too close, then accelerate towards that individual.
# I also control momentum causing the previous movement and direction to only decay at a small rate.
# TO SEE Original Script
# Critters are initially distributed randomly on a 1 x 1 grid.
ncritters = 40
xypos = matrix(runif(ncritters*2),ncol=2)
plot(xypos, main="Critters are Initially Distributed Randomly"
, xlab="X", ylab="Y")
# Now let's imagine that each critter has an ideal safe distance from each other critter.
safe.dist = .3
critter.speed = .001
# If another critter is not at that safe distance than the critter will move towards the closest nearby critter.
# Let's see how this works.
# First let's check how close each critter is to each other critter.
# We will accomplish this by going through each critter and checking how far away each other critter is.
distances = NULL
for (i in 1:ncritters) distances = rbind(distances, apply((xypos[i,]-t(xypos))^2,2,sum))
# In order to prevent critters from always chasing whatever is closest to them (and themselves) we drop anything which is closer than the safe.distance.
distances[abs(distances)
# The apply command will apply the order command to each row whiel the [1,] selects only the critter that is closes.
# Plot the
plot(xypos, xlab = "X", ylab = "Y")
for (i in 1:ncritters) arrows(x0=xypos[i,1], y0=xypos[i,2],
x1=xypos[closest,][i,1],
y1=xypos[closest,][i,2],
length=.1)
# This calculates the difference between the current position of each critter and that of the closest critter.
ab = xypos-xypos[closest,]
# To see how this is calculated, see my previous post simulating a werewolf attack.
# Now calculate the difference in the horizontal and vertical axes that the critters will move as a projection into the direction of the closest critter outside of the safe zone.
a.prime = critter.speed/(1 + (ab[,2]^2)/(ab[,1]^2))^.5
b.prime = (critter.speed^2-a.prime^2)^.5
# This corrects the movement to ensure that the critters are flying at each other rather than away from each other.
movement = cbind(a.prime * sign(ab[,2]), b.prime * sign(ab[,1]))
between = function(xy1,xy2,point) (point>xy1&point
# Set the new xypos
xypos1 = xypos+movement
points(xypos1, col="red")
# ------------------------------------------------------
# Let's turn this into an animation.
library(animation)
# loopnum = 100; ncritters=40; inertia = .5; show.grid=T; ani.pause=F; plot.fixed=F; plot.centered=F; brownian = F; arrow = T
flocking <- ani.pause="F," arrow="T)" brownian="F," function="" inertia=".5," loopnum="100," ncritters="40," p="" plot.centered="F," plot.fixed="F," show.grid="T,">
# Generate xy initial positions.
# xypos will hold the current critter position while
# xypos0 will hold the position of the critters the previous time.
xypos = xypos0 = matrix(runif(ncritters*2),ncol=2)-.5
movement0 = 0
# Loop though all of the loops.
for (i in 1:loopnum) {
# This specifies the range to be graphed.
if (plot.fixed) rangex=rangey = -.5:.5
if (!plot.fixed) {
rangex = c(min(xypos[,1]), max(xypos[,1]))
rangey = c(min(xypos[,2]), max(xypos[,2]))
}
# This handles the grid size when
if (plot.centered&!plot.fixed) {
rangex=c(-max(abs(xypos[,1])), max(abs(xypos[,1])))
rangey=c(-max(abs(xypos[,2])), max(abs(xypos[,2])))
}
# This centers the plot at the middle (0,0) if the plot width is also set to be fixed.
if (plot.centered&plot.fixed) rangex=rangex-mean(rangex)
if (plot.centered&plot.fixed) rangey=rangey-mean(rangey)
# Draw critters
plot(xypos, main="Swarming Animation", xlab="X", ylab="Y", axes=F, ylim=rangey, xlim=rangex, type="p")
# Draw arrows. The start of the arrows is the previous periods location.
if (arrow&i>1) arrows(x0=xypos0[,1],y0=xypos0[,2],x1=xypos[,1],y1=xypos[,2], length = .1)
# Show the grid in the background.
if (show.grid) {
abline(v=seq(-10,10,.1))
abline(h=seq(-10,10,.1))
}
# Show the origin
text(0,0, "(0,0)")
# Calculate each critters distance from each other
distances = NULL
for (i in 1:ncritters) distances = rbind(distances, apply((xypos[i,]-t(xypos))^2,2,sum))
# Drop those within the safe zone.
distances[abs(distances)
# This selects the critter closest to the selected critter.
closest = matrix(1:ncritters, ncol=ncritters, nrow=ncritters)[apply(abs(distances), 1, order)[1,]]
# distances[as.apply(!apply(distances, 1, is.na),1,sum)==0,]=0
# As done above
ab = xypos-xypos[closest,]
a.prime = critter.speed/(1 + (ab[,2]^2)/(ab[,1]^2))^.5
b.prime = (critter.speed^2-a.prime^2)^.5
movement = cbind(a.prime * sign(ab[,2]), b.prime * sign(ab[,1]))
between = function(xy1,xy2,point) (point>xy1&point
movement = movement*(-1)^between(xypos,xypos[closest,], xypos-movement)
movement[is.na(movement)]=0
movement0 = movement0*inertia + movement
# This fancy dodad allows half of the change in movement to be due to random variation.
if (brownian) movement0=movement0+matrix(rnorm(ncritters*2),ncol=2)*critter.speed/2
# Set the previous round's xy position to be equal to the current round's.
xypos0 = xypos
# Update the current round's.
xypos = xypos+movement0
# This is only used in the event that the animate package is in use.
if (ani.pause) ani.pause()
}
}
# This generates a GIF animation demonstrating smoothly how these GIFs can be incorper
ani.options(ani.width=400, ani.height=400, interval=.1)
# You must have imagemagick installed for this to work.
saveGIF(flocking(300,100,.999, ani.pause=T), movie.name = "Swarming.gif", replace=T)
# Here are two different graphs generated by the previous command(though the one on the bottom uses 200 frames while the one on the top uses 300)
# Let's see how this works.
flocking()
flocking(400,100,.99)
flocking(400,100,.99, plot.fixed=T)
No comments:
Post a Comment