Skip to content

Commit f7bc7b8

Browse files
committed
basic flocking algo by Dan Shiffman
1 parent 65bcfec commit f7bc7b8

File tree

5 files changed

+375
-0
lines changed

5 files changed

+375
-0
lines changed
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
import traceback
2+
3+
import py5_tools
4+
import py5
5+
from py5 import object_conversion
6+
7+
8+
def cluster_boids():
9+
pass
10+
11+
12+
py5_tools.register_processing_mode_key("cluster_boids", cluster_boids)
13+
14+
15+
# run the sketch in processing mode, specifying the Java class to instantiate
16+
py5.run_sketch(jclassname='test.Example3Sketch')
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
3+
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
4+
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
5+
<modelVersion>4.0.0</modelVersion>
6+
7+
<groupId>py5</groupId>
8+
<artifactId>py5-processing-mode-example3</artifactId>
9+
<version>0.1</version>
10+
11+
<name>py5-processing-mode-example3</name>
12+
<url>https://py5coding.org/</url>
13+
<properties>
14+
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
15+
<maven.compiler.source>17</maven.compiler.source>
16+
<maven.compiler.target>17</maven.compiler.target>
17+
<jarlocation>${env.CONDA_PREFIX}/lib/python3.10/site-packages/py5/jars</jarlocation>
18+
</properties>
19+
20+
<dependencies>
21+
<dependency>
22+
<groupId>py5</groupId>
23+
<artifactId>py5-processing4</artifactId>
24+
<version>0.9.0.dev0</version>
25+
<scope>system</scope>
26+
<systemPath>${jarlocation}/core.jar</systemPath>
27+
</dependency>
28+
<dependency>
29+
<groupId>py5</groupId>
30+
<artifactId>py5-jogl</artifactId>
31+
<version>0.9.0.dev0</version>
32+
<scope>system</scope>
33+
<systemPath>${jarlocation}/jogl-all.jar</systemPath>
34+
</dependency>
35+
<dependency>
36+
<groupId>py5</groupId>
37+
<artifactId>py5</artifactId>
38+
<version>0.9.0.dev0</version>
39+
<scope>system</scope>
40+
<systemPath>${jarlocation}/py5.jar</systemPath>
41+
</dependency>
42+
</dependencies>
43+
44+
<build>
45+
<plugins>
46+
<plugin>
47+
<groupId>org.apache.maven.plugins</groupId>
48+
<artifactId>maven-dependency-plugin</artifactId>
49+
<version>3.2.0</version>
50+
<executions>
51+
<execution>
52+
<id>copy</id>
53+
<phase>package</phase>
54+
<goals>
55+
<goal>copy</goal>
56+
</goals>
57+
</execution>
58+
</executions>
59+
<configuration>
60+
<artifactItems>
61+
<artifactItem>
62+
<groupId>py5</groupId>
63+
<artifactId>py5-processing-mode-example3</artifactId>
64+
<version>0.1</version>
65+
<type>jar</type>
66+
<overWrite>true</overWrite>
67+
<outputDirectory>${project.basedir}/../jars</outputDirectory>
68+
<destFileName>py5-processing-mode-example3.jar</destFileName>
69+
</artifactItem>
70+
</artifactItems>
71+
</configuration>
72+
</plugin>
73+
</plugins>
74+
</build>
75+
76+
</project>
Lines changed: 217 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,217 @@
1+
package test;
2+
3+
import java.util.ArrayList;
4+
5+
import processing.core.PVector;
6+
import py5.core.SketchBase;
7+
8+
// The Boid class
9+
10+
class Boid {
11+
12+
protected SketchBase sketch;
13+
14+
protected PVector position;
15+
protected PVector velocity;
16+
protected PVector acceleration;
17+
protected float r;
18+
protected float maxforce; // Maximum steering force
19+
protected float maxspeed; // Maximum speed
20+
21+
Boid(SketchBase sketch, float x, float y) {
22+
this.sketch = sketch;
23+
acceleration = new PVector(0, 0);
24+
25+
// This is a new PVector method not yet implemented in JS
26+
// velocity = PVector.random2D();
27+
28+
// Leaving the code temporarily this way so that this example runs in JS
29+
float angle = sketch.random(SketchBase.TWO_PI);
30+
velocity = new PVector(SketchBase.cos(angle), SketchBase.sin(angle));
31+
32+
position = new PVector(x, y);
33+
r = 2.0f;
34+
maxspeed = 2;
35+
maxforce = 0.03f;
36+
}
37+
38+
void run(ArrayList<Boid> boids) {
39+
flock(boids);
40+
update();
41+
borders();
42+
render();
43+
}
44+
45+
void applyForce(PVector force) {
46+
// We could add mass here if we want A = F / M
47+
acceleration.add(force);
48+
}
49+
50+
// We accumulate a new acceleration each time based on three rules
51+
void flock(ArrayList<Boid> boids) {
52+
PVector sep = separate(boids); // Separation
53+
PVector ali = align(boids); // Alignment
54+
PVector coh = cohesion(boids); // Cohesion
55+
// Arbitrarily weight these forces
56+
sep.mult(1.5f);
57+
ali.mult(1.0f);
58+
coh.mult(1.0f);
59+
// Add the force vectors to acceleration
60+
applyForce(sep);
61+
applyForce(ali);
62+
applyForce(coh);
63+
}
64+
65+
// Method to update position
66+
void update() {
67+
// Update velocity
68+
velocity.add(acceleration);
69+
// Limit speed
70+
velocity.limit(maxspeed);
71+
position.add(velocity);
72+
// Reset accelertion to 0 each cycle
73+
acceleration.mult(0);
74+
}
75+
76+
// A method that calculates and applies a steering force towards a target
77+
// STEER = DESIRED MINUS VELOCITY
78+
PVector seek(PVector target) {
79+
PVector desired = PVector.sub(target, position); // A vector pointing from the position to the target
80+
// Scale to maximum speed
81+
desired.normalize();
82+
desired.mult(maxspeed);
83+
84+
// Above two lines of code below could be condensed with new PVector setMag()
85+
// method
86+
// Not using this method until Processing.js catches up
87+
// desired.setMag(maxspeed);
88+
89+
// Steering = Desired minus Velocity
90+
PVector steer = PVector.sub(desired, velocity);
91+
steer.limit(maxforce); // Limit to maximum steering force
92+
return steer;
93+
}
94+
95+
void render() {
96+
// Draw a triangle rotated in the direction of velocity
97+
float theta = velocity.heading() + SketchBase.radians(90);
98+
99+
sketch.fill(200, 100);
100+
sketch.stroke(255);
101+
sketch.pushMatrix();
102+
sketch.translate(position.x, position.y);
103+
sketch.rotate(theta);
104+
sketch.beginShape(SketchBase.TRIANGLES);
105+
sketch.vertex(0, -r * 2);
106+
sketch.vertex(-r, r * 2);
107+
sketch.vertex(r, r * 2);
108+
sketch.endShape();
109+
sketch.popMatrix();
110+
}
111+
112+
// Wraparound
113+
void borders() {
114+
if (position.x < -r)
115+
position.x = sketch.width + r;
116+
if (position.y < -r)
117+
position.y = sketch.height + r;
118+
if (position.x > sketch.width + r)
119+
position.x = -r;
120+
if (position.y > sketch.height + r)
121+
position.y = -r;
122+
}
123+
124+
// Separation
125+
// Method checks for nearby boids and steers away
126+
PVector separate(ArrayList<Boid> boids) {
127+
float desiredseparation = 25.0f;
128+
PVector steer = new PVector(0, 0, 0);
129+
int count = 0;
130+
// For every boid in the system, check if it's too close
131+
for (Boid other : boids) {
132+
float d = PVector.dist(position, other.position);
133+
// If the distance is greater than 0 and less than an arbitrary amount (0 when
134+
// you are yourself)
135+
if ((d > 0) && (d < desiredseparation)) {
136+
// Calculate vector pointing away from neighbor
137+
PVector diff = PVector.sub(position, other.position);
138+
diff.normalize();
139+
diff.div(d); // Weight by distance
140+
steer.add(diff);
141+
count++; // Keep track of how many
142+
}
143+
}
144+
// Average -- divide by how many
145+
if (count > 0) {
146+
steer.div((float) count);
147+
}
148+
149+
// As long as the vector is greater than 0
150+
if (steer.mag() > 0) {
151+
// First two lines of code below could be condensed with new PVector setMag()
152+
// method
153+
// Not using this method until Processing.js catches up
154+
// steer.setMag(maxspeed);
155+
156+
// Implement Reynolds: Steering = Desired - Velocity
157+
steer.normalize();
158+
steer.mult(maxspeed);
159+
steer.sub(velocity);
160+
steer.limit(maxforce);
161+
}
162+
return steer;
163+
}
164+
165+
// Alignment
166+
// For every nearby boid in the system, calculate the average velocity
167+
PVector align(ArrayList<Boid> boids) {
168+
float neighbordist = 50;
169+
PVector sum = new PVector(0, 0);
170+
int count = 0;
171+
for (Boid other : boids) {
172+
float d = PVector.dist(position, other.position);
173+
if ((d > 0) && (d < neighbordist)) {
174+
sum.add(other.velocity);
175+
count++;
176+
}
177+
}
178+
if (count > 0) {
179+
sum.div((float) count);
180+
// First two lines of code below could be condensed with new PVector setMag()
181+
// method
182+
// Not using this method until Processing.js catches up
183+
// sum.setMag(maxspeed);
184+
185+
// Implement Reynolds: Steering = Desired - Velocity
186+
sum.normalize();
187+
sum.mult(maxspeed);
188+
PVector steer = PVector.sub(sum, velocity);
189+
steer.limit(maxforce);
190+
return steer;
191+
} else {
192+
return new PVector(0, 0);
193+
}
194+
}
195+
196+
// Cohesion
197+
// For the average position (i.e. center) of all nearby boids, calculate
198+
// steering vector towards that position
199+
PVector cohesion(ArrayList<Boid> boids) {
200+
float neighbordist = 50;
201+
PVector sum = new PVector(0, 0); // Start with empty vector to accumulate all positions
202+
int count = 0;
203+
for (Boid other : boids) {
204+
float d = PVector.dist(position, other.position);
205+
if ((d > 0) && (d < neighbordist)) {
206+
sum.add(other.position); // Add position
207+
count++;
208+
}
209+
}
210+
if (count > 0) {
211+
sum.div(count);
212+
return seek(sum); // Steer towards the position
213+
} else {
214+
return new PVector(0, 0);
215+
}
216+
}
217+
}
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
package test;
2+
3+
import py5.core.SketchBase;
4+
5+
/**
6+
* Flocking + Clustering
7+
* original Flocking Sketch by Daniel Shiffman.
8+
*
9+
* An implementation of Craig Reynold's Boids program to simulate
10+
* the flocking behavior of birds. Each boid steers itself based on
11+
* rules of avoidance, alignment, and coherence.
12+
*
13+
* Click the mouse to add a new boid.
14+
*/
15+
16+
public class Example3Sketch extends SketchBase {
17+
18+
protected Flock flock;
19+
20+
public void settings() {
21+
size(640, 360);
22+
}
23+
24+
public void setup() {
25+
flock = new Flock();
26+
// Add an initial set of boids into the system
27+
for (int i = 0; i < 150; i++) {
28+
flock.addBoid(new Boid(this, width / 2, height / 2));
29+
}
30+
}
31+
32+
public void draw() {
33+
background(50);
34+
flock.run();
35+
}
36+
37+
// Add a new boid into the System
38+
public void mousePressed() {
39+
flock.addBoid(new Boid(this, mouseX, mouseY));
40+
}
41+
}
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
package test;
2+
3+
import java.util.ArrayList;
4+
5+
// The Flock (a list of Boid objects)
6+
7+
class Flock {
8+
9+
protected ArrayList<Boid> boids; // An ArrayList for all the boids
10+
11+
Flock() {
12+
boids = new ArrayList<Boid>(); // Initialize the ArrayList
13+
}
14+
15+
void run() {
16+
for (Boid b : boids) {
17+
b.run(boids); // Passing the entire list of boids to each boid individually
18+
}
19+
}
20+
21+
void addBoid(Boid b) {
22+
boids.add(b);
23+
}
24+
25+
}

0 commit comments

Comments
 (0)