微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

分离行为无法正常工作 第 1 部分杂项清理第 2 部分修正总和计算第 3 部分可视化平均向量第 4 部分更好的粒子运动模型

如何解决分离行为无法正常工作 第 1 部分杂项清理第 2 部分修正总和计算第 3 部分可视化平均向量第 4 部分更好的粒子运动模型

enter image description here

我已经从单个粒子中获取每个粒子的平均位移矢量,然后反转方向并将其保存为所需的速度,也将其应用于其他每个粒子,但仍然是粒子而不是分离,而是在拐角处相互连接,留下大的中间有空格

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,220);
    ellipse(this.pos.x,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 类中的重复和潜在错误,我将去掉 x/y 属性,因为它们与 pos.xpos.y 的值相同。

由于未使用 Targets 类,我已将其删除。

最后,我将 draw 函数移到了 Spots 类声明之上。

第 2 部分。修正总和计算

Spots.seek 中的以下代码行对我来说真的没有任何意义:

sum = sum.div(d)

这会大大降低除最后一个方向矢量之外的所有矢量对平均矢量的影响。为了证明这一点,如果我们以扩展形式重写该公式,则该公式如下所示:

sum = (((v1 / d1) + v2) / d2) ... + vn) / dn

可以改写为:

sum =
  v1 / (d1 * d2 ... * dn) +
    v2 / (d2 ... * dn) +
    ... +
    vn / dn

如您所见,v 之前的每个 vn 都被除以所有距离的乘积,这是没有意义的。

也许您在将这些向量添加到总和之前尝试将它们归一化?

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 部分。可视化平均向量

现在,角落里所有的点都聚集在一起的问题是,当你取从一个给定粒子到所有其他粒子的所有向量的平均值时,你基本上得到了一个指向粒子质心的向量。整个粒子集。而且由于我们将其反转以计算所需的速度,因此随着粒子远离组中心移动,它们将开始具有相似的所需速度向量。

为了说明这一点,我添加了平均向量和速度向量的图形表示。

Particle Motion with Average Vectors

注意所有以红色显示的平均向量基本上都指向中心。这种趋势持续下去,最终您会在角落里看到成团的颗粒。

这是目前的代码:

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,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.x + this.avg.x * 100,this.pos.y + this.avg.y * 100);
        stroke('green');
        line(this.pos.x,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 在屏幕的相对两侧,它们确实不应该相互影响,但是如果粒子彼此紧邻,它们应该会产生相当大的排斥力。对此建模的合理选择是平方反比关系(力 = 最大力/距离²),它显示在重力和磁力的公式中。我们还可以添加优化以忽略大于一定距离的粒子。

注意:为了获得稳定的结果,我不得不限制最大速度 粒子。我相信这是谨慎的性质所必需的 更新(当两个粒子向彼此移动时,以及 他们的位置以谨慎的增量更新,他们能够 靠得很近,此时它们会对其施加巨大的排斥力 彼此。

这是最终代码:

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));
    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,5);
    push();
    strokeWeight(1);
    stroke('red');
    line(this.pos.x,this.pos.x + this.acc.x * 100,this.pos.y + this.acc.y * 100);
    stroke('green');
    line(this.pos.x,this.pos.y + this.vel.y * 10);
    pop();
  }
}
<script src="https://cdn.jsdelivr.net/npm/p5@v1.4.0/lib/p5.js"></script>

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。