-
Notifications
You must be signed in to change notification settings - Fork 23
Expand file tree
/
Copy pathArrow.pde
More file actions
186 lines (160 loc) · 4.44 KB
/
Arrow.pde
File metadata and controls
186 lines (160 loc) · 4.44 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
// Arrow class implementing flocking behavior (Boids algorithm)
class Arrow {
PVector position;
PVector velocity;
PVector acceleration;
float maxForce; // Maximum steering force
float maxSpeed; // Maximum speed
float arrowSize;
color arrowColor;
Arrow(float x, float y) {
position = new PVector(x, y);
velocity = PVector.random2D();
velocity.setMag(random(2, 4));
acceleration = new PVector(0, 0);
maxForce = 0.2;
maxSpeed = 4;
arrowSize = 12;
arrowColor = color(random(200, 255), random(100, 200), random(150, 255), 200);
}
// Apply force to acceleration
void applyForce(PVector force) {
acceleration.add(force);
}
// Main flocking behavior
void flock(ArrayList<Arrow> arrows) {
PVector separation = separate(arrows);
PVector alignment = align(arrows);
PVector cohesion = cohere(arrows);
// Weight these forces
separation.mult(1.5);
alignment.mult(1.0);
cohesion.mult(1.0);
// Apply all forces
applyForce(separation);
applyForce(alignment);
applyForce(cohesion);
}
// Separation: steer to avoid crowding
PVector separate(ArrayList<Arrow> arrows) {
float desiredSeparation = 25.0;
PVector steer = new PVector(0, 0);
int count = 0;
for (Arrow other : arrows) {
float d = PVector.dist(position, other.position);
if ((d > 0) && (d < desiredSeparation)) {
PVector diff = PVector.sub(position, other.position);
diff.normalize();
diff.div(d); // Weight by distance
steer.add(diff);
count++;
}
}
if (count > 0) {
steer.div(count);
}
if (steer.mag() > 0) {
steer.setMag(maxSpeed);
steer.sub(velocity);
steer.limit(maxForce);
}
return steer;
}
// Alignment: steer towards average heading
PVector align(ArrayList<Arrow> arrows) {
float neighborDist = 50;
PVector sum = new PVector(0, 0);
int count = 0;
for (Arrow other : arrows) {
float d = PVector.dist(position, other.position);
if ((d > 0) && (d < neighborDist)) {
sum.add(other.velocity);
count++;
}
}
if (count > 0) {
sum.div(count);
sum.setMag(maxSpeed);
PVector steer = PVector.sub(sum, velocity);
steer.limit(maxForce);
return steer;
}
return new PVector(0, 0);
}
// Cohesion: steer towards average position
PVector cohere(ArrayList<Arrow> arrows) {
float neighborDist = 50;
PVector sum = new PVector(0, 0);
int count = 0;
for (Arrow other : arrows) {
float d = PVector.dist(position, other.position);
if ((d > 0) && (d < neighborDist)) {
sum.add(other.position);
count++;
}
}
if (count > 0) {
sum.div(count);
return seek(sum);
}
return new PVector(0, 0);
}
// Seek a target position
PVector seek(PVector target) {
PVector desired = PVector.sub(target, position);
desired.setMag(maxSpeed);
PVector steer = PVector.sub(desired, velocity);
steer.limit(maxForce);
return steer;
}
// Flee from a position (for mouse avoidance)
PVector flee(PVector target) {
PVector desired = PVector.sub(position, target);
float d = desired.mag();
if (d < 100) { // Only flee if mouse is close
desired.setMag(maxSpeed);
PVector steer = PVector.sub(desired, velocity);
steer.limit(maxForce);
return steer;
}
return new PVector(0, 0);
}
// Update position
void update() {
velocity.add(acceleration);
velocity.limit(maxSpeed);
position.add(velocity);
acceleration.mult(0); // Reset acceleration
}
// Wrap around screen edges
void borders() {
if (position.x < -arrowSize) position.x = width + arrowSize;
if (position.y < -arrowSize) position.y = height + arrowSize;
if (position.x > width + arrowSize) position.x = -arrowSize;
if (position.y > height + arrowSize) position.y = -arrowSize;
}
// Display the arrow
void display() {
float theta = velocity.heading() + PI/2;
pushMatrix();
translate(position.x, position.y);
rotate(theta);
fill(arrowColor);
noStroke();
// Draw arrow shape
beginShape();
vertex(0, -arrowSize);
vertex(-arrowSize/3, arrowSize/2);
vertex(0, arrowSize/4);
vertex(arrowSize/3, arrowSize/2);
endShape(CLOSE);
popMatrix();
}
// Run the full arrow behavior
void run(ArrayList<Arrow> arrows) {
flock(arrows);
update();
borders();
display();
}
}