分离行为无法正常工作
Separation Behaviour not working correctly
我已经从单个粒子中获取每个粒子的平均位移矢量,然后反转方向并将其保存为所需的速度,还将其应用于每个其他粒子但粒子仍然没有分开而不是在角落处相互连接在
之间留下大空space
seek方法从数组中获取当前正在处理的粒子的索引,然后计算它相对于所有其他粒子的平均位移矢量,然后除以除自身之外的粒子数。但是粒子的行为方式仍然与我预期的非常不同。
let spots = [];
let target;
function setup() {
createCanvas(530, 530);
for (let i = 0; i < 300; i++) {
spots[i] = new Spots();
}
}
class Spots {
constructor() {
this.x = random(0, 530);
this.y = random(0, 530)
this.pos = createVector(this.x, this.y);
this.vel = p5.Vector.random2D();
this.acc = createVector(0, 0);
this.desiredvel = createVector();
this.magn = 0;
this.steeringForce = createVector();
this.avg = createVector(0, 0);
}
seek(index) {
let sum = createVector();
let d;
for (let h = 0; h < spots.length; h++) {
d = dist(spots[h].pos.x, spots[h].pos.y, this.pos.x, this.pos.y)
//console.log(d.mag())
if ((h !== index)) {
sum = p5.Vector.add(sum, p5.Vector.sub(spots[h].pos, this.pos))
sum = sum.div(d)
}
}
this.avg = sum.div(spots.length - 1);
this.desiredvel = this.avg.mult(-2);
this.steeringForce = p5.Vector.sub(this.desiredvel, this.vel);
this.acc = this.steeringForce;
this.magn = this.acc.mag();
}
edge() {
if (this.pos.x < 0 || this.pos.x > 530) {
this.vel.x = this.vel.x * (-1);
} else if (this.pos.y < 0 || this.pos.y > 530) {
this.vel.y = this.vel.y * (-1);
}
}
move() {
//console.log(this.pos);
//console.log(this.vel);
this.pos = p5.Vector.add(this.pos, this.vel);
this.vel = p5.Vector.add(this.vel, this.acc);
this.vel.setMag(1);
}
show() {
stroke(255);
strokeWeight(2);
noFill();
ellipse(this.pos.x, this.pos.y, 5, 5);
}
}
class Targets {
constructor(x, y) {
this.pos = createVector(x, y);
}
show() {
stroke(255);
strokeWeight(4);
fill(200, 0, 220);
ellipse(this.pos.x, this.pos.y, 10, 10);
}
}
function draw() {
background(0);
//spot.seek(target);
for (let k = 0; k < spots.length; k++) {
spots[k].seek(k);
spots[k].edge();
spots[k].move();
spots[k].show();
}
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.4.0/p5.js"></script>
我 a tutorial 解决了当前草图的一些问题以及如何获得更好结果的一些想法。
这里是教程的内容,不过最好在OpenProcessing.org上看。
第 1 部分。杂项清理
我要为本教程做的第一件事是减少点数,这样草图就不会那么慢了。我还将为此值引入一个常数。
此外,为了减少 Spots
class 中的重复和潜在错误,我将删除 x/y 属性,因为它们的值与 pos.x
和 pos.y
.
由于未使用目标 class,我已将其删除。
最后,我将 draw
函数移到了 Spots
class 声明上方。
第 2 部分。修复求和计算
Spots.seek 中的以下代码行对我来说真的没有任何意义:
sum = sum.div(d)
这会大大减少除最后一个方向向量之外的所有方向向量对平均向量的影响。为了证明这一点,如果我们以扩展形式重写此公式,则如下所示:
sum = (((v1 / d1) + v2) / d2) ... + vn) / dn
这可以重写为:
sum =
v1 / (d1 * d2 ... * dn) +
v2 / (d2 ... * dn) +
... +
vn / dn
如您所见,vn
之前的每个 v
都除以所有距离的乘积,这没有意义。
也许您在将这些向量添加到总和之前试图对它们中的每一个进行归一化?
sum =
v1 / d1 +
v2 / d2 +
...
vn / dn
我已经使用 Vector.normalize
函数更正了代码:
sum = p5.Vector.add(
sum,
p5.Vector.sub(spots[h].pos, this.pos)
.normalize()
);
为了考虑到平均矢量增加的幅度(现在应该是幅度 1),我减少了计算时使用的乘数 desiredvel
。
第 3 部分。可视化平均向量
现在,所有斑点都聚集在角落里的问题是,当您对从一个给定粒子到所有其他粒子的所有矢量取平均值时,您基本上得到一个到质心的矢量整个粒子集。由于我们正在反转它来计算所需的速度,随着粒子从组的中心移动得更远,它们将开始具有相似的所需速度矢量。
为了举例说明这一点,我添加了平均向量和速度向量的图形表示。
请注意以红色显示的所有平均矢量基本上都指向中心。这种趋势继续下去,你最终会在角落里看到一团团的颗粒。
目前的代码如下:
const spotCount = 100;
let spots = [];
let target;
function setup() {
createCanvas(530, 530);
for (let i = 0; i < spotCount; i++) {
spots[i] = new Spots();
}
}
function draw() {
background(0);
//spot.seek(target);
for (let k = 0; k < spots.length; k++) {
spots[k].seek(k);
spots[k].edge();
spots[k].move();
spots[k].show();
}
}
class Spots {
constructor() {
this.pos = createVector(random(0, 530), random(0, 530));
this.vel = p5.Vector.random2D();
this.acc = createVector(0, 0);
this.desiredvel = createVector();
this.magn = 0;
this.steeringForce = createVector();
this.avg = createVector(0, 0);
}
seek(index) {
let sum = createVector();
for (let h = 0; h < spots.length; h++) {
if ((h !== index)) {
sum = p5.Vector.add(sum, p5.Vector.sub(spots[h].pos, this.pos).normalize());
}
}
this.avg = sum.div(spots.length - 1);
this.desiredvel = this.avg.copy().mult(-0.1);
this.steeringForce = p5.Vector.sub(this.desiredvel, this.vel);
this.acc = this.steeringForce;
this.magn = this.acc.mag();
}
edge() {
if (this.pos.x < 0 || this.pos.x > 530) {
this.vel.x = this.vel.x * (-1);
} else if (this.pos.y < 0 || this.pos.y > 530) {
this.vel.y = this.vel.y * (-1);
}
}
move() {
//console.log(this.pos);
//console.log(this.vel);
this.pos = p5.Vector.add(this.pos, this.vel);
this.vel = p5.Vector.add(this.vel, this.acc);
this.vel.setMag(1);
}
show() {
stroke(255);
strokeWeight(2);
noFill();
ellipse(this.pos.x, this.pos.y, 5, 5);
push();
strokeWeight(1);
stroke('red');
line(this.pos.x, this.pos.y, this.pos.x + this.avg.x * 100, this.pos.y + this.avg.y * 100);
stroke('green');
line(this.pos.x, this.pos.y, this.pos.x + this.vel.x * 10, this.pos.y + this.vel.y * 10);
pop();
}
}
<script src="https://cdn.jsdelivr.net/npm/p5@v1.4.0/lib/p5.js"></script>
第 4 部分。更好的粒子运动模型
为了改变这一点,我们需要根据粒子之间的距离来改变粒子之间的影响量。因此,如果粒子 A 和粒子 B 在屏幕的相对两侧,它们实际上不应该相互影响,但如果粒子彼此紧挨着,它们应该施加很大的排斥力。对此建模的一个合理选择是平方反比关系(力 = 最大力/距离²),它出现在重力和磁力的公式中。我们还可以添加一个优化来忽略大于一定距离的粒子。
Note: In order to get a stable result I had to limit the maximum speed of
the particles. I believe this is necessitated by the discreet nature
of the updates (when two particles are moving towards each other, and
their positions are updated in discreet increments, they are able to
get very close at which point they exert a massive repulsive force on
each other.
这是最终代码:
const spotCount = 100;
const maxForce = 1;
const particleMass = 10;
const pixelsPerMeter = 10;
const maxSpeed = 2;
let spots = [];
let target;
function setup() {
createCanvas(530, 530);
for (let i = 0; i < spotCount; i++) {
spots[i] = new Spots();
}
}
function draw() {
background(0);
//spot.seek(target);
for (let k = 0; k < spots.length; k++) {
spots[k].seek(k);
spots[k].edge();
spots[k].move();
spots[k].show();
}
}
class Spots {
constructor() {
this.pos = createVector(random(0, 530), random(0, 530));
this.vel = p5.Vector.random2D();
this.acc = createVector(0, 0);
}
seek(index) {
let force = createVector();
for (let h = 0; h < spots.length; h++) {
if ((h !== index)) {
// find the vector from the neighbor particle to the current particle
let v = p5.Vector.sub(this.pos, spots[h].pos);
let m = v.mag() / pixelsPerMeter;
// If it is within 20 units
if (m < 20) {
// Add force in that direction according to the inverse square law
force = force.add(v.normalize().mult(maxForce).div(m * m));
}
}
}
this.acc = force.div(particleMass);
}
edge() {
if (this.pos.x < 0 || this.pos.x > 530) {
this.vel.x = this.vel.x * (-1);
} else if (this.pos.y < 0 || this.pos.y > 530) {
this.vel.y = this.vel.y * (-1);
}
}
move() {
//console.log(this.pos);
//console.log(this.vel);
this.pos.add(this.vel);
this.vel.add(this.acc);
if (this.vel.magSq() > maxSpeed * maxSpeed) {
this.vel.setMag(2);
}
}
show() {
stroke(255);
strokeWeight(2);
noFill();
ellipse(this.pos.x, this.pos.y, 5, 5);
push();
strokeWeight(1);
stroke('red');
line(this.pos.x, this.pos.y, this.pos.x + this.acc.x * 100, this.pos.y + this.acc.y * 100);
stroke('green');
line(this.pos.x, this.pos.y, this.pos.x + this.vel.x * 10, this.pos.y + this.vel.y * 10);
pop();
}
}
<script src="https://cdn.jsdelivr.net/npm/p5@v1.4.0/lib/p5.js"></script>
我已经从单个粒子中获取每个粒子的平均位移矢量,然后反转方向并将其保存为所需的速度,还将其应用于每个其他粒子但粒子仍然没有分开而不是在角落处相互连接在
之间留下大空spaceseek方法从数组中获取当前正在处理的粒子的索引,然后计算它相对于所有其他粒子的平均位移矢量,然后除以除自身之外的粒子数。但是粒子的行为方式仍然与我预期的非常不同。
let spots = [];
let target;
function setup() {
createCanvas(530, 530);
for (let i = 0; i < 300; i++) {
spots[i] = new Spots();
}
}
class Spots {
constructor() {
this.x = random(0, 530);
this.y = random(0, 530)
this.pos = createVector(this.x, this.y);
this.vel = p5.Vector.random2D();
this.acc = createVector(0, 0);
this.desiredvel = createVector();
this.magn = 0;
this.steeringForce = createVector();
this.avg = createVector(0, 0);
}
seek(index) {
let sum = createVector();
let d;
for (let h = 0; h < spots.length; h++) {
d = dist(spots[h].pos.x, spots[h].pos.y, this.pos.x, this.pos.y)
//console.log(d.mag())
if ((h !== index)) {
sum = p5.Vector.add(sum, p5.Vector.sub(spots[h].pos, this.pos))
sum = sum.div(d)
}
}
this.avg = sum.div(spots.length - 1);
this.desiredvel = this.avg.mult(-2);
this.steeringForce = p5.Vector.sub(this.desiredvel, this.vel);
this.acc = this.steeringForce;
this.magn = this.acc.mag();
}
edge() {
if (this.pos.x < 0 || this.pos.x > 530) {
this.vel.x = this.vel.x * (-1);
} else if (this.pos.y < 0 || this.pos.y > 530) {
this.vel.y = this.vel.y * (-1);
}
}
move() {
//console.log(this.pos);
//console.log(this.vel);
this.pos = p5.Vector.add(this.pos, this.vel);
this.vel = p5.Vector.add(this.vel, this.acc);
this.vel.setMag(1);
}
show() {
stroke(255);
strokeWeight(2);
noFill();
ellipse(this.pos.x, this.pos.y, 5, 5);
}
}
class Targets {
constructor(x, y) {
this.pos = createVector(x, y);
}
show() {
stroke(255);
strokeWeight(4);
fill(200, 0, 220);
ellipse(this.pos.x, this.pos.y, 10, 10);
}
}
function draw() {
background(0);
//spot.seek(target);
for (let k = 0; k < spots.length; k++) {
spots[k].seek(k);
spots[k].edge();
spots[k].move();
spots[k].show();
}
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.4.0/p5.js"></script>
我 a tutorial 解决了当前草图的一些问题以及如何获得更好结果的一些想法。
这里是教程的内容,不过最好在OpenProcessing.org上看。
第 1 部分。杂项清理
我要为本教程做的第一件事是减少点数,这样草图就不会那么慢了。我还将为此值引入一个常数。
此外,为了减少 Spots
class 中的重复和潜在错误,我将删除 x/y 属性,因为它们的值与 pos.x
和 pos.y
.
由于未使用目标 class,我已将其删除。
最后,我将 draw
函数移到了 Spots
class 声明上方。
第 2 部分。修复求和计算
Spots.seek 中的以下代码行对我来说真的没有任何意义:
sum = sum.div(d)
这会大大减少除最后一个方向向量之外的所有方向向量对平均向量的影响。为了证明这一点,如果我们以扩展形式重写此公式,则如下所示:
sum = (((v1 / d1) + v2) / d2) ... + vn) / dn
这可以重写为:
sum =
v1 / (d1 * d2 ... * dn) +
v2 / (d2 ... * dn) +
... +
vn / dn
如您所见,vn
之前的每个 v
都除以所有距离的乘积,这没有意义。
也许您在将这些向量添加到总和之前试图对它们中的每一个进行归一化?
sum =
v1 / d1 +
v2 / d2 +
...
vn / dn
我已经使用 Vector.normalize
函数更正了代码:
sum = p5.Vector.add(
sum,
p5.Vector.sub(spots[h].pos, this.pos)
.normalize()
);
为了考虑到平均矢量增加的幅度(现在应该是幅度 1),我减少了计算时使用的乘数 desiredvel
。
第 3 部分。可视化平均向量
现在,所有斑点都聚集在角落里的问题是,当您对从一个给定粒子到所有其他粒子的所有矢量取平均值时,您基本上得到一个到质心的矢量整个粒子集。由于我们正在反转它来计算所需的速度,随着粒子从组的中心移动得更远,它们将开始具有相似的所需速度矢量。
为了举例说明这一点,我添加了平均向量和速度向量的图形表示。
请注意以红色显示的所有平均矢量基本上都指向中心。这种趋势继续下去,你最终会在角落里看到一团团的颗粒。
目前的代码如下:
const spotCount = 100;
let spots = [];
let target;
function setup() {
createCanvas(530, 530);
for (let i = 0; i < spotCount; i++) {
spots[i] = new Spots();
}
}
function draw() {
background(0);
//spot.seek(target);
for (let k = 0; k < spots.length; k++) {
spots[k].seek(k);
spots[k].edge();
spots[k].move();
spots[k].show();
}
}
class Spots {
constructor() {
this.pos = createVector(random(0, 530), random(0, 530));
this.vel = p5.Vector.random2D();
this.acc = createVector(0, 0);
this.desiredvel = createVector();
this.magn = 0;
this.steeringForce = createVector();
this.avg = createVector(0, 0);
}
seek(index) {
let sum = createVector();
for (let h = 0; h < spots.length; h++) {
if ((h !== index)) {
sum = p5.Vector.add(sum, p5.Vector.sub(spots[h].pos, this.pos).normalize());
}
}
this.avg = sum.div(spots.length - 1);
this.desiredvel = this.avg.copy().mult(-0.1);
this.steeringForce = p5.Vector.sub(this.desiredvel, this.vel);
this.acc = this.steeringForce;
this.magn = this.acc.mag();
}
edge() {
if (this.pos.x < 0 || this.pos.x > 530) {
this.vel.x = this.vel.x * (-1);
} else if (this.pos.y < 0 || this.pos.y > 530) {
this.vel.y = this.vel.y * (-1);
}
}
move() {
//console.log(this.pos);
//console.log(this.vel);
this.pos = p5.Vector.add(this.pos, this.vel);
this.vel = p5.Vector.add(this.vel, this.acc);
this.vel.setMag(1);
}
show() {
stroke(255);
strokeWeight(2);
noFill();
ellipse(this.pos.x, this.pos.y, 5, 5);
push();
strokeWeight(1);
stroke('red');
line(this.pos.x, this.pos.y, this.pos.x + this.avg.x * 100, this.pos.y + this.avg.y * 100);
stroke('green');
line(this.pos.x, this.pos.y, this.pos.x + this.vel.x * 10, this.pos.y + this.vel.y * 10);
pop();
}
}
<script src="https://cdn.jsdelivr.net/npm/p5@v1.4.0/lib/p5.js"></script>
第 4 部分。更好的粒子运动模型
为了改变这一点,我们需要根据粒子之间的距离来改变粒子之间的影响量。因此,如果粒子 A 和粒子 B 在屏幕的相对两侧,它们实际上不应该相互影响,但如果粒子彼此紧挨着,它们应该施加很大的排斥力。对此建模的一个合理选择是平方反比关系(力 = 最大力/距离²),它出现在重力和磁力的公式中。我们还可以添加一个优化来忽略大于一定距离的粒子。
Note: In order to get a stable result I had to limit the maximum speed of the particles. I believe this is necessitated by the discreet nature of the updates (when two particles are moving towards each other, and their positions are updated in discreet increments, they are able to get very close at which point they exert a massive repulsive force on each other.
这是最终代码:
const spotCount = 100;
const maxForce = 1;
const particleMass = 10;
const pixelsPerMeter = 10;
const maxSpeed = 2;
let spots = [];
let target;
function setup() {
createCanvas(530, 530);
for (let i = 0; i < spotCount; i++) {
spots[i] = new Spots();
}
}
function draw() {
background(0);
//spot.seek(target);
for (let k = 0; k < spots.length; k++) {
spots[k].seek(k);
spots[k].edge();
spots[k].move();
spots[k].show();
}
}
class Spots {
constructor() {
this.pos = createVector(random(0, 530), random(0, 530));
this.vel = p5.Vector.random2D();
this.acc = createVector(0, 0);
}
seek(index) {
let force = createVector();
for (let h = 0; h < spots.length; h++) {
if ((h !== index)) {
// find the vector from the neighbor particle to the current particle
let v = p5.Vector.sub(this.pos, spots[h].pos);
let m = v.mag() / pixelsPerMeter;
// If it is within 20 units
if (m < 20) {
// Add force in that direction according to the inverse square law
force = force.add(v.normalize().mult(maxForce).div(m * m));
}
}
}
this.acc = force.div(particleMass);
}
edge() {
if (this.pos.x < 0 || this.pos.x > 530) {
this.vel.x = this.vel.x * (-1);
} else if (this.pos.y < 0 || this.pos.y > 530) {
this.vel.y = this.vel.y * (-1);
}
}
move() {
//console.log(this.pos);
//console.log(this.vel);
this.pos.add(this.vel);
this.vel.add(this.acc);
if (this.vel.magSq() > maxSpeed * maxSpeed) {
this.vel.setMag(2);
}
}
show() {
stroke(255);
strokeWeight(2);
noFill();
ellipse(this.pos.x, this.pos.y, 5, 5);
push();
strokeWeight(1);
stroke('red');
line(this.pos.x, this.pos.y, this.pos.x + this.acc.x * 100, this.pos.y + this.acc.y * 100);
stroke('green');
line(this.pos.x, this.pos.y, this.pos.x + this.vel.x * 10, this.pos.y + this.vel.y * 10);
pop();
}
}
<script src="https://cdn.jsdelivr.net/npm/p5@v1.4.0/lib/p5.js"></script>