-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathboid.js
More file actions
180 lines (158 loc) · 5.96 KB
/
boid.js
File metadata and controls
180 lines (158 loc) · 5.96 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
import { distance, random } from "./math";
import { Vector } from "./vector";
export class Boid {
constructor(ctx) {
this.ctx = ctx;
this.position = Vector(random(ctx.width), random(ctx.height)); // losowa pozycja
this.velocity = Vector.random2D() // wektor predkosci
.setMag(random(2, 4)); // losowa długość wektora z przedzialu
this.acceleration = Vector(); // wektor przyspieszenia
this.maxForce = 0.2;
this.maxRepelForce = 1;
this.maxSpeed = 4;
this.neighbourhoodRadius = 50;
}
edges() {
// jesli boid wypada poza obszar -> przeniesienie go na druga strone
const { width, height } = this.ctx;
if (this.position.x > width) {
this.position.x = 0;
} else if (this.position.x < 0) {
this.position.x = width;
}
if (this.position.y > height) {
this.position.y = 0;
} else if (this.position.y < 0) {
this.position.y = height;
}
}
distance(other) {
return distance(this.position.x, this.position.y, other.x, other.y);
}
findNeighbours(boids) {
return boids.filter(
(boid) =>
boid !== this && this.distance(boid.position) < this.neighbourhoodRadius
);
}
align(boids) {
const neighbours = this.findNeighbours(boids);
if (neighbours.length === 0) {
return Vector();
}
let alignVector = neighbours.reduce((acc, boid) => {
return Vector.add(acc, boid.velocity);
}, Vector());
alignVector = Vector.div(alignVector, neighbours.length); // srednia
alignVector = Vector.setMagnitude(alignVector, this.maxSpeed); // ustawienie dlugosci na max
alignVector = Vector.sub(alignVector, this.velocity); // docelowa predkosc = aktualna - srednia
alignVector = Vector.limit(alignVector, this.maxForce); // nie wieksza sila niz max
return alignVector;
}
cohesion(boids) {
const neighbours = this.findNeighbours(boids);
if (neighbours.length === 0) {
return Vector();
}
let cohesionVector = neighbours.reduce((acc, boid) => {
return Vector.add(acc, boid.position);
}, Vector());
cohesionVector = Vector.div(cohesionVector, neighbours.length); // srednia
cohesionVector = Vector.sub(cohesionVector, this.position); // minus pozycja
cohesionVector = Vector.setMagnitude(cohesionVector, this.maxSpeed); // ustawienie dlugosci na maksymalna
cohesionVector = Vector.sub(cohesionVector, this.velocity); // minus predkosc
cohesionVector = Vector.limit(cohesionVector, this.maxForce); // nie wieksza sila niz max
return cohesionVector;
}
separation(boids) {
const neighbours = this.findNeighbours(boids);
if (neighbours.length === 0) {
return Vector();
}
let separationVector = neighbours.reduce((acc, boid) => {
const distance = this.distance(boid.position);
let diff = Vector.sub(this.position, boid.position); // wektor roznica pozycji boida i jego sasiada
diff = Vector.div(diff, distance ** 2); // sila odwrotnie proporcjonalna do odleglosci
return Vector.add(acc, diff);
}, Vector());
separationVector = Vector.div(separationVector, neighbours.length); // srednia
separationVector = Vector.setMagnitude(separationVector, this.maxSpeed); // ustawienie dlugosci na max
separationVector = Vector.sub(separationVector, this.velocity); // minus predkosc
separationVector = Vector.limit(separationVector, this.maxForce); // nie wieksza niz sila max
return separationVector;
}
flock(boids) {
let alignment = this.align(boids);
let cohesion = this.cohesion(boids);
let separation = this.separation(boids);
const {
alignMultiplier,
cohesionMultiplier,
separationMultiplier,
} = this.ctx.state();
alignment = Vector.mult(alignment, alignMultiplier);
cohesion = Vector.mult(cohesion, cohesionMultiplier);
separation = Vector.mult(separation, separationMultiplier);
this.acceleration = Vector.add(
this.acceleration,
alignment,
cohesion,
separation
);
}
repealment(obstacle, { obstacleRadius = 60 } = {}) {
let repelVector = Vector();
const distance = this.distance(obstacle);
if (distance < obstacleRadius) {
const scale = Math.max(1 / Math.sqrt(distance), 0.4); // proporcjonalnie im blizej tym z wieksza sila ucieka
repelVector = Vector.sub(obstacle, this.position);
repelVector = Vector.normalize(repelVector);
repelVector = Vector.mult(repelVector, this.maxRepelForce * scale);
}
return repelVector;
}
repel(obstacles) {
const repelVector = obstacles.reduce(
(acc, obstacle) => Vector.sub(acc, this.repealment(obstacle)),
Vector()
);
this.acceleration = Vector.add(this.acceleration, repelVector);
}
update() {
this.position = Vector.add(this.position, this.velocity); // ruch o predkosc
this.velocity = Vector.add(this.velocity, this.acceleration); // zmiana predkosci o przyspieszenie
this.velocity = Vector.limit(this.velocity, this.maxSpeed); // ograniczenie do max predkosci
this.acceleration = Vector(); // zerowanie co krok
this.edges();
}
}
Boid.createFlock = (length, ctx) =>
Array(length)
.fill()
.map(() => new Boid(ctx));
Boid.render = (() => {
const colors = new Map();
return (boid, ctx) => {
// ctx.stroke(1,1,1, 10);
// ctx.fill(255,255,255, 10);
// ctx.strokeWeight(1);
// ctx.circle(boid.position.x, boid.position.y, boid.neighbourhoodRadius * 2);
const color = colors.has(boid)
? colors.get(boid)
: (() => {
const randomColor = [random(0, 255), random(0, 255), random(0, 255)];
colors.set(boid, randomColor);
return randomColor;
})();
// https://p5js.org/reference/#/p5.Vector/magSq
ctx.push();
ctx.stroke(...color);
ctx.fill(...color);
ctx.translate(boid.position.x, boid.position.y);
ctx.rotate(boid.velocity.heading());
let size = 7;
ctx.translate(boid.velocity.mag() - size, 0);
ctx.triangle(0, size / 2, 0, -size / 2, size, 0);
ctx.pop();
};
})();