Last active
May 1, 2017 23:04
-
-
Save wybo/82ef4f46d2a05838dc5f to your computer and use it in GitHub Desktop.
Flock
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# AgentBase is Free Software, available under GPL v3 or any later version. | |
# Original AgentScript code @ 2013, 2014 Owen Densmore and RedfishGroup LLC. | |
# AgentBase (c) 2014, Wybo Wiersma. | |
# Flock is the classic "boids" model where agents each follow | |
# three simple rules resulting in realistic flocking. This example | |
# uses the as.dat.gui.js extra. | |
u = ABM.util | |
class ABM.FlockModel extends ABM.Model | |
setVision: (@vision) -> | |
setMaxTurn: (@maxTurn) -> | |
@maxTurn = u.degreesToRadians(@maxTurn) | |
toggleConsole: () -> | |
@useConsole = not @useConsole | |
setup: -> | |
# u.randomSeed() # use for repeatable runs | |
@refreshPatches = false # <3fps->4fps agents larger part of drawing | |
# No optimizations: 4fps | |
# @patches.usePixels() # 4fps same as refresh off | |
# w/ refresh off and two optimizations, 22-23fps | |
@agents.setUseSprites() # 24 -> 36 | |
@animator.setRate 30, true # multistep | |
@useConsole = true | |
@population = 75 # agents | |
@setVision 3 # patches & set patch rect | |
@minSeparation = 0.75 # patches | |
@setMaxTurn(3.0) # degrees -> radians | |
# defaults | |
@agents.setDefault "size", 1.5 | |
@patches.create() | |
for patch in @patches | |
patch.color = u.color.random type: "gray", min: 0, max: 100 | |
for patch in @patches.sample(@population) | |
patch.sprout 1 | |
for agent in @agents | |
agent.color = u.color.random "map" | |
step: -> | |
if @useConsole and @animator.ticks % 25 is 0 | |
console.log @animator.toString(), | |
"flock alignment: #{@reportFlockVectorSize()}" | |
for agent in @agents | |
@flock agent | |
flock: (agent) -> | |
flockmates = agent.neighbors(@vision) | |
if flockmates.length isnt 0 | |
[nearestNeighbor, distance] = flockmates.min(((f) -> | |
f.distance agent.position), true) | |
if distance < @minSeparation | |
@separate agent, nearestNeighbor | |
else | |
@align agent, flockmates | |
@cohere agent, flockmates | |
agent.forward .5 # move partial patch for smooth animation. | |
separate: (agent, nearest) -> | |
angle = u.angle nearest.position, agent.position, @patches | |
@turnTowards agent, angle | |
align: (agent, flockmates) -> | |
@turnTowards agent, @averageHeading(flockmates) | |
cohere: (agent, flockmates) -> | |
@turnTowards agent, @averageHeadingTowards(agent, flockmates) | |
turnTowards: (agent, heading) -> | |
turn = u.substractRadians heading, agent.heading # angle from h to a | |
turn = u.clamp turn, -@maxTurn, @maxTurn # limit the turn | |
agent.rotate turn | |
averageHeading: (flockmates) -> | |
dx = (Math.sin f.heading for f in flockmates).reduce (x, y) -> x + y | |
dy = (Math.cos f.heading for f in flockmates).reduce (x, y) -> x + y | |
@safeAtan dy, dx | |
averageHeadingTowards: (agent, flockmates) -> | |
dx = (Math.sin u.angle(f.position, agent.position, | |
@patches) for f in flockmates).reduce (x, y) -> x + y | |
dy = (Math.cos u.angle(f.position, agent.position, | |
@patches) for f in flockmates).reduce (x, y) -> x + y | |
@safeAtan dy, dx | |
safeAtan: (x, y) -> | |
# if x is 0 and y is 0 then 0 else Math.atan2 y, x # atan ok if x 0 | |
Math.atan2 y, x | |
reportFlockVectorSize: -> | |
dx = (Math.sin a.heading for a in @agents).reduce (x, y) -> x + y | |
dy = (Math.cos a.heading for a in @agents).reduce (x, y) -> x + y | |
Math.sqrt(dx * dx + dy * dy) / @population | |
window.model = new ABM.FlockModel({ | |
div: "world", | |
patchSize: 15, | |
isTorus: true | |
}) | |
window.model.start() | |
# Uses the AgentBase library (03-02-2017 release) | |
# https://github.com/wybo/agentbase/commit/3501302567c018da82601cd858be2ea6d0b9c74d |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment