This example is an implementation of a simple flocking algorithm that uses the dynamics of repulsion, attraction, and orientation zones. This example is based off of the algorithm described by Couzin.
The basic principle is that each agent has three regions in a growing radius around it: a zone of repulsion, a zone of orientation, and a zone of attraction respectively. By editing the sizes of these zones you can expect to experience different behaviour.
This example allows you to see the behavior when two types of agents are used, allowing you to specify parameters for each group.
There are two basic types of parameters used: initial conditions and agent parameters. Agent parameters can be updated any time, including while in play. Initial conditions however require a reset before they take effect.
Note: If an initial condition is changed then the
Reset
button will turn yellow, indicating that a reset is required before some changes take effect.
Group 1
and Group 2
agents. Agents from the second group are rendered as the larger dots.k
nearest neighbors outside of the zone of orientation for their attraction vector.[0, 180]
) where the agent can not adjust their direction vector greater than this number for an individual iterationPlay
button to start animationPause
button to stop animationNext
button to do one time step of the algorithm.Dragging/Zooming behavior forked from mbostock's block: Drag & Zoom II
forked from lwthatcher's block: Directional Forces
forked from lwthatcher's block: Flocking
forked from lwthatcher's block: Flocking II
forked from lwthatcher's block: Flocking III
forked from lwthatcher's block: Flocking IV
xxxxxxxxxx
<meta charset="utf-8">
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css">
<link href="https://gitcdn.github.io/bootstrap-toggle/2.2.2/css/bootstrap-toggle.min.css" rel="stylesheet">
<style> /* set the CSS */
.point {
fill: darkslategrey;
}
.repulsed {
fill: red;
stroke: red;
}
.attracted {
fill: blue;
stroke: blue;
}
.oriented {
fill: darkmagenta;
stroke: darkmagenta;
}
.ao {
fill: DarkTurquoise;
stroke: DarkTurquoise;
}
.moved {
stroke: black;
fill: black;
}
.R_r {
fill: lightsalmon;
}
.R_o {
fill: Chartreuse;
}
.R_a {
fill: lightskyblue;
}
.num {
float: left;
}
.counter {
width: 50px;
float: right;
margin-right: 40px;
}
.counter-small {
width: 50px;
float: right;
margin-right: 0px;
}
.Group {
font-size: 12px;
font-weight: bold;
}
hr {
margin-top: 2px;
margin-bottom: 2px;
}
input[type=range] {
width: 85%;
margin-left: 17px;
}
.g1 {
background-color: lightcyan;
}
.g2 {
background-color: lightgray;
}
</style>
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.11.0/jquery.min.js"></script>
<script src="https://www.numericjs.com/lib/numeric-1.2.6.js"></script>
<script src="https://gitcdn.github.io/bootstrap-toggle/2.2.2/js/bootstrap-toggle.min.js"></script>
<div class="row">
<div class="col-sm-9">
<svg id="svg" width="740" height="500"></svg>
</div>
<div class="col-sm-3">
<div class="row">
<b>Start Conditions:</b>
</div>
<div class="row">
<div class="num">Agents: </div>
<input class="counter" id="agents" type="number" onchange="updateNumAgents(this.value)" value=30></input>
</div>
<div class="row">
<div class="num">Sparsity: </div>
<input class="counter" id="sparsity" type="number" onchange="updateSparsity(this.value)" value=45></input>
</div>
<div class="row">
<div class="num">Distribution: </div><br>
<input class="range" id="dist" type="range" value=0 onchange="updateDistribution(this.value)"></input>
</div>
<div class="row">
<div class="col-sm-6 g1">
<span id="d-g1">100%</span>
</div>
<div class="col-sm-6 g2">
<span id="d-g2">0%</span>
</div>
</div>
<hr>
<div class="row">
<div class="col-sm-6 g1">
<input id="metric-1" type="checkbox" data-on="distance" onchange="changeMetric(this.checked, 0)" data-off="knn" checked data-toggle="toggle">
</div>
<div class="col-sm-6 g2">
<input id="metric-2" type="checkbox" onchange="changeMetric(this.checked, 1)" data-on="distance" data-off="knn" data-toggle="toggle">
</div>
</div>
<hr>
<div class="row">
<div class="col-sm-6 g1">
<span class="Group">Group 1:</span><br>
<div class="num">Zr:</div>
<input class="counter-small" id="zr-1" type="number" onchange="updateR_r(this.value, 0)" value=20></input>
</div>
<div class="col-sm-6 g2">
<span class="Group">Group 2:</span><br>
<div class="num">Zr:</div>
<input class="counter-small" id="zr-2" type="number" onchange="updateR_r(this.value, 1)" value=50></input>
</div>
</div>
<div class="row">
<div class="col-sm-6 g1">
<span class="Group"></span>
<div class="num">Zo:</div>
<input class="counter-small" id="zo-1" type="number" onchange="updateR_o(this.value, 0)" value=100></input>
</div>
<div class="col-sm-6 g2">
<div class="num">Zo:</div>
<input class="counter-small" id="zo-2" type="number" onchange="updateR_o(this.value, 1)" value=100></input>
</div>
</div>
<div class="row">
<div class="col-sm-6 g1" id="ak-box_1a">
<span class="Group"></span>
<div class="num">Za:</div>
<input class="counter-small" id="za-1" type="number" onchange="updateR_a(this.value, 0)" value=150></input>
</div>
<div class="col-sm-6 g1" id="ak-box_1k" hidden="hidden">
<span class="Group"></span>
<div class="num">k:</div>
<input class="counter-small" id="k-1" type="number" onchange="updateK(this.value, 0)" value=5></input>
</div>
<div class="col-sm-6 g2" id="ak-box_2a" hidden="hidden">
<div class="num">Za:</div>
<input class="counter-small" id="za-2" type="number" onchange="updateR_a(this.value, 1)" value=150></input>
</div>
<div class="col-sm-6 g2" id="ak-box_2k">
<span class="Group"></span>
<div class="num">k:</div>
<input class="counter-small" id="k-2" type="number" onchange="updateK(this.value, 1)" value=5></input>
</div>
</div>
<div class="row">
<div class="col-sm-6 g1">
<span class="Group"></span>
<div class="num">s:</div>
<input class="counter-small" id="speed-1" type="number" onchange="updateSpeed(this.value, 0)" min=0 value=15></input>
</div>
<div class="col-sm-6 g2">
<div class="num">s:</div>
<input class="counter-small" id="speed-2" type="number" onchange="updateSpeed(this.value, 1)" min=0 value=15></input>
</div>
</div>
<div class="row">
<div class="col-sm-6 g1">
<span class="Group"></span>
<div class="num">θ:</div>
<input class="counter-small" id="theta-1" type="number" onchange="updateTurnRate(this.value, 0)" min=0 max=180 value=100></input>
</div>
<div class="col-sm-6 g2">
<div class="num">θ:</div>
<input class="counter-small" id="theta-2" type="number" onchange="updateTurnRate(this.value, 1)" min=0 max=180 value=100></input>
</div>
</div>
<hr>
<div class="row">
<b>Controls:</b>
<br>
<button onclick="play()"><span class="glyphicon glyphicon-play"></button>
<button onclick="pause()"><span class="glyphicon glyphicon-pause"></button>
<button onclick="next()"><span class="glyphicon glyphicon-step-forward"></button>
</div>
<hr>
<div class="row">
<button id="reset" class="btn" onclick="reset()">Reset</button>
</div>
<hr>
<div class="row">
<button class="btn btn-primary" onclick="printEigenvalues()">Get Eigenvalues</button>
</div>
<div class="row">
Fiedler: <span id="eigs"></span>
</br>
# Components: <span id="num_coms"></span>
</div>
</div>
</div>
<script src="https://d3js.org/d3.v4.min.js"></script>
<script src="flocking.js"></script>
Modified http://www.numericjs.com/lib/numeric-1.2.6.js to a secure url
https://ajax.googleapis.com/ajax/libs/jquery/1.11.0/jquery.min.js
https://www.numericjs.com/lib/numeric-1.2.6.js
https://gitcdn.github.io/bootstrap-toggle/2.2.2/js/bootstrap-toggle.min.js
https://d3js.org/d3.v4.min.js