The Boids model can be used to simulate the flocking behavior of birds.
Created
September 14, 2017 16:54
-
-
Save jeremycflin/7a987f309bd2faaccde94d353b2904a0 to your computer and use it in GitHub Desktop.
Boids
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
license: mit |
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
<!DOCTYPE html> | |
<head> | |
<meta charset="utf-8"> | |
<script src="https://d3js.org/d3.v4.min.js"></script> | |
<script src="https://d3js.org/d3-scale-chromatic.v1.min.js"></script> | |
</head> | |
<body> | |
<script> | |
var width = 960, | |
height = 500; | |
var svg = d3.select("body") | |
.append("svg") | |
.attr("width", width) | |
.attr("height", height); | |
var n = 500; | |
var cohesionCoeff = 0.05, | |
alignmentCoeff = 0.05, | |
separationCoeff = 0.05, | |
separationDistance = 30, | |
neighborDistance = 60, | |
maxVelocity = 2, | |
maxAcceleration = 0.02; | |
var line = d3.line(), | |
color = d3.scaleSequential(d3.interpolateYlGnBu).domain([0, maxVelocity]); | |
function Vec(x, y) { | |
this.x = x || 0; | |
this.y = y || 0; | |
return this; | |
} | |
Vec.prototype.clone = function() { | |
return new Vec(this.x, this.y); | |
}; | |
Vec.prototype.length = function() { | |
return Math.sqrt(this.x*this.x + this.y*this.y); | |
}; | |
Vec.prototype.plus = function(v) { | |
this.x += v.x; | |
this.y += v.y; | |
return this; | |
}; | |
Vec.prototype.minus = function(v) { | |
this.x -= v.x; | |
this.y -= v.y; | |
return this; | |
}; | |
Vec.prototype.scale = function(x) { | |
this.x *= x; | |
this.y *= x; | |
return this; | |
}; | |
Vec.prototype.normalize = function(x) { | |
x = typeof x !== "undefined" ? x : 1; | |
var length = this.length(); | |
if (length > 0) { | |
return this.scale(x/length); | |
} | |
else { | |
return this; | |
} | |
}; | |
Vec.prototype.truncate = function(x) { | |
var length = this.length(); | |
if (length > x) { | |
return this.normalize(x); | |
} | |
else { | |
return this; | |
} | |
}; | |
var randomX = d3.randomUniform(0, width), | |
randomY = d3.randomUniform(0, height), | |
randomVx = d3.randomUniform(0, maxVelocity), | |
randomVy = d3.randomNormal(0, maxVelocity/4); | |
var boids = d3.range(n).map(function() { | |
return { | |
pos: new Vec(randomX(), randomY()), | |
vel: new Vec(randomVx(), randomVy()), | |
acc: new Vec() | |
}; | |
}); | |
function tick(t) { | |
boids = boids.filter(function(b) { | |
return (b.pos.x > 0) & (b.pos.x < width) | |
& (b.pos.y > 0) & (b.pos.y < height); | |
}); | |
boids.push({ | |
pos: new Vec(0, randomY()), | |
vel: new Vec(randomVx(), randomVy()), | |
acc: new Vec() | |
}); | |
boids.forEach(function(b1) { | |
var cohesionForce = new Vec(), | |
alignmentForce = new Vec(), | |
separationForce = new Vec(); | |
boids.forEach(function(b2) { | |
if (b1 === b2) return; | |
var separation = b2.pos.clone().minus(b1.pos), | |
distance = separation.length(); | |
if (distance < separationDistance) { | |
separationForce.minus(separation); | |
} | |
else if (distance < neighborDistance) { | |
cohesionForce.plus(separation); | |
alignmentForce.plus(b2.vel.clone().minus(b1.vel)); | |
} | |
}); | |
cohesionForce.normalize(cohesionCoeff); | |
alignmentForce.normalize(alignmentCoeff); | |
separationForce.normalize(separationCoeff); | |
b1.acc = new Vec(); | |
b1.acc.plus(cohesionForce).plus(alignmentForce).plus(separationForce).truncate(maxAcceleration); | |
b1.vel.plus(b1.acc).truncate(maxVelocity); | |
b1.pos.plus(b1.vel); | |
}); | |
var lines = svg.selectAll(".boid").data(boids); | |
lines.exit().remove(); | |
lines.enter().append("path") | |
.attr("class", "boid") | |
.style("stroke", color(0)) | |
.style("stroke-width", 2) | |
.merge(lines) | |
.style("stroke", function(b) { return color(b.vel.length()); }) | |
.attr("d", function(b) { | |
var v = b.vel.clone().normalize(20); | |
return line([ | |
[b.pos.x - v.x/2, b.pos.y - v.y/2], | |
[b.pos.x + v.x/2, b.pos.y + v.y/2]]); | |
}); | |
} | |
var timer = d3.interval(tick, 20); | |
</script> | |
</body> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment