Skip to content

Commit 4676680

Browse files
committed
Adding new post: flocking simulation
1 parent fe865f1 commit 4676680

File tree

3 files changed

+261
-0
lines changed

3 files changed

+261
-0
lines changed

P5/Flocking/agent.js

Lines changed: 185 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,185 @@
1+
class Agent {
2+
3+
constructor() {
4+
this.size = 5;
5+
this.perceptionRadius = 100;
6+
this.minSeparation = 100;
7+
this.boundarySeparation = 60;
8+
this.maxAccel = 0.4;
9+
this.minVel = 4;
10+
this.maxVel = 8;
11+
12+
this.position = createVector(random(width), random(height));
13+
this.velocity = p5.Vector.random2D();
14+
this.velocity.mult(random(this.minVel, this.maxVel));
15+
this.acceleration = createVector(0, 0);
16+
17+
}
18+
19+
boundary() {
20+
if (this.position.x >= width) {
21+
this.position.x -= width;
22+
} else if (this.position.x <= 0) {
23+
this.position.x += width;
24+
}
25+
if (this.position.y >= height) {
26+
this.position.y -= height;
27+
} else if (this.position.y <= 0) {
28+
this.position.y += height;
29+
}
30+
}
31+
32+
align(flock) {
33+
let averageHeading = createVector();
34+
let numNeighbours = 0;
35+
for (let neighbour of flock) {
36+
if (!this.position.equals(neighbour.position)) {
37+
if (this.position.dist(neighbour.position) < this.perceptionRadius) {
38+
numNeighbours += 1;
39+
averageHeading.add(neighbour.velocity);
40+
}
41+
}
42+
}
43+
averageHeading.div(numNeighbours);
44+
averageHeading.setMag(this.maxVel);
45+
46+
averageHeading.sub(this.velocity);
47+
48+
return averageHeading;
49+
}
50+
51+
cohesion(flock) {
52+
let averagePosition = createVector();
53+
let numNeighbours = 0;
54+
for (let neighbour of flock) {
55+
if (!this.position.equals(neighbour.position)) {
56+
if (this.position.dist(neighbour.position) < this.perceptionRadius) {
57+
numNeighbours += 1;
58+
averagePosition.add(neighbour.position);
59+
}
60+
}
61+
}
62+
averagePosition.div(numNeighbours);
63+
averagePosition.sub(this.position);
64+
averagePosition.setMag(this.maxVel);
65+
66+
averagePosition.sub(this.velocity);
67+
68+
return averagePosition;
69+
}
70+
71+
separation(flock) {
72+
let separationVelocity = createVector();
73+
let numNeighbours = 0;
74+
for (let neighbour of flock) {
75+
if (!this.position.equals(neighbour.position)) {
76+
if (this.position.dist(neighbour.position) < this.minSeparation) {
77+
let tempVel = this.position.copy();
78+
tempVel.sub(neighbour.position);
79+
let tempMag = 1.0 / tempVel.mag();
80+
tempVel.setMag(tempMag);
81+
82+
separationVelocity.add(tempVel);
83+
numNeighbours += 1;
84+
}
85+
}
86+
}
87+
separationVelocity.div(numNeighbours);
88+
separationVelocity.setMag(this.maxVel);
89+
90+
separationVelocity.sub(this.velocity);
91+
92+
return separationVelocity;
93+
}
94+
95+
minSpeed() {
96+
let heading = createVector();
97+
if (this.velocity.mag() < this.minVel) {
98+
heading = this.velocity.copy();
99+
heading.setMag(this.maxVel);
100+
heading.sub(this.velocity);
101+
}
102+
return heading;
103+
}
104+
105+
avoidBoundary() {
106+
let desiredVel = createVector();
107+
if (this.position.x >= width - this.boundarySeparation && this.velocity.x > 0) {
108+
desiredVel = createVector(-this.velocity.x, this.velocity.y);
109+
desiredVel.setMag(this.maxVel);
110+
desiredVel.sub(this.velocity);
111+
} else if (this.position.x <= this.boundarySeparation && this.velocity.x < 0) {
112+
desiredVel = createVector(-this.velocity.x, this.velocity.y);
113+
desiredVel.setMag(this.maxVel);
114+
desiredVel.sub(this.velocity);
115+
}
116+
if (this.position.y >= height - this.boundarySeparation && this.velocity.y > 0) {
117+
desiredVel = createVector(this.velocity.x, -this.velocity.y);
118+
desiredVel.setMag(this.maxVel);
119+
desiredVel.sub(this.velocity);
120+
} else if (this.position.y <= this.boundarySeparation && this.velocity.y < 0) {
121+
desiredVel = createVector(this.velocity.x, -this.velocity.y);
122+
desiredVel.setMag(this.maxVel);
123+
desiredVel.sub(this.velocity);
124+
}
125+
126+
return desiredVel;
127+
}
128+
129+
flocking(flock) {
130+
let alignment = this.align(flock);
131+
let cohesion = this.cohesion(flock);
132+
let separation = this.separation(flock);
133+
134+
alignment.mult(alignSlider.value());
135+
cohesion.mult(cohesionSlider.value());
136+
separation.mult(separationSlider.value());
137+
138+
this.acceleration.add(alignment);
139+
this.acceleration.add(cohesion);
140+
this.acceleration.add(separation);
141+
this.acceleration.add(this.minSpeed());
142+
this.acceleration.add(this.avoidBoundary());
143+
144+
this.acceleration.limit(this.maxAccel);
145+
}
146+
147+
update() {
148+
this.position.add(this.velocity);
149+
this.velocity.add(this.acceleration);
150+
this.velocity.limit(this.maxVel);
151+
152+
this.acceleration.mult(0);
153+
this.boundary();
154+
}
155+
156+
show(showPR, showSR, showVV) {
157+
fill(0);
158+
stroke(0, 0, 0);
159+
ellipse(this.position.x, this.position.y, this.size, this.size);
160+
161+
// Draw Perception Radius
162+
if (showPR) {
163+
noFill();
164+
stroke(100, 0, 0);
165+
ellipse(this.position.x, this.position.y, 2*this.perceptionRadius, 2*this.perceptionRadius);
166+
}
167+
168+
// Draw Separation Radius
169+
if (showSR) {
170+
noFill();
171+
stroke(0, 0, 100);
172+
ellipse(this.position.x, this.position.y, 2*this.minSeparation, 2*this.minSeparation);
173+
}
174+
175+
// Draw Velocity Vector
176+
if (showVV) {
177+
stroke(0, 200, 0);
178+
if (this.velocity.mag() < this.minVel) {
179+
stroke(200, 0, 0);
180+
}
181+
let scale = 50;
182+
line(this.position.x, this.position.y, this.position.x + scale * this.velocity.x, this.position.y + scale * this.velocity.y);
183+
}
184+
}
185+
}

P5/Flocking/flocking.js

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
let width = 2500;
2+
let height = 1000;
3+
4+
let numAgents = 120;
5+
var flock = [];
6+
7+
let alignSlider, cohesionSlider, separationSlider;
8+
let showPR = false;
9+
let showSR = false;
10+
let showVV = false;
11+
12+
function setup(){
13+
var canvas = createCanvas(width, height);
14+
canvas.parent('flock_div');
15+
16+
alignSlider = createSlider(0, 5, 1, 0.1)
17+
cohesionSlider = createSlider(0, 5, 1, 0.1)
18+
separationSlider = createSlider(0, 5, 1, 0.1)
19+
20+
alignSlider.position(220, 1095);
21+
cohesionSlider.position(220, 1135);
22+
separationSlider.position(220, 1175);
23+
textSize(32);
24+
25+
showPRCheckbox = createCheckbox('Show Perception Radius', showPR);
26+
showPRCheckbox.position(40, 970);
27+
showPRCheckbox.changed(() => {showPR = !showPR;})
28+
29+
showSRCheckbox = createCheckbox('Show Separation Radius', showSR);
30+
showSRCheckbox.position(40, 1010);
31+
showSRCheckbox.changed(() => {showSR = !showSR;})
32+
33+
showVVCheckbox = createCheckbox('Show Velocity Vector', showVV);
34+
showVVCheckbox.position(40, 1050);
35+
showVVCheckbox.changed(() => {showVV = !showVV;})
36+
37+
for (let i=0; i<numAgents; i++) {
38+
flock.push(new Agent());
39+
}
40+
}
41+
42+
function draw() {
43+
background(245);
44+
45+
for (let agent of flock) {
46+
agent.flocking(flock);
47+
}
48+
49+
for (let agent of flock) {
50+
agent.update();
51+
agent.show(showPR, showSR, showVV);
52+
}
53+
54+
fill(0, 102, 153);
55+
text('Alignment', 10, 900);
56+
text('Cohesion', 10, 940);
57+
text('Separation', 10, 980);
58+
}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
---
2+
layout: post
3+
title: "Flocking simulation"
4+
date: 2019-03-01 13:00:00
5+
categories: jekyll update
6+
js_source: ../../../../../P5/Flocking/flocking.js
7+
---
8+
9+
<script src="../../../../../P5/Flocking/agent.js"></script>
10+
11+
<center>
12+
<div
13+
class="drawing" id="flock_div" style="
14+
margin-left: calc(-100vw / 2 + 500px / 2);
15+
margin-right: calc(-100vw / 2 + 500px / 2);
16+
"></div>
17+
</center>
18+

0 commit comments

Comments
 (0)