javascript - p5.j​​s 粒子在 2D 矢量场影响下的行为表现出较差的响应

标签 javascript p5.js

我用 p5js here 完成了我想要的 2D 物理矢量场表示的一半。 .另一半是让随机粒子动态地跟随矢量场的力,我遇到了很多问题。我尝试了多种方法来考虑粒子的环绕,以及我将绘图的原点翻译到 Canvas 中心的事实。然而,粒子似乎受场中各个矢量的影响最小,并最终沿着 x 轴行进,并带有轻微的颠簸。
enter image description here
我是 JS 的新手这一事实无助于将所有这些元素从在线可用的几个演示文稿中拼接起来,我将不胜感激任何关于可能出了什么问题的建议,以及我应该关注的地方。
这是我目前所拥有的:一个文件 sketch.js对应于我上面引用的我自己的答案:

scl = 35;
var cols,rows;
var fr;
var particles = [];
var flowfield;

function setup() {
  createCanvas(windowWidth, windowHeight);
  cols = floor(width/scl);
  rows = floor(height/scl);
  fr = createP("");
   
  flowfield = new Array(cols * rows);

  for (var i = 0; i < 1000; i++) {
    particles[i] = new Particle();
  }
  background(51);
}



function draw() {
  translate(height/2, height/2);  //moves the origin to bottom left
  scale(1, -1);  //flips the y values so y increases "up"
  background(255);
  loadPixels();
  for (var y = -rows; y < rows; y++) {
    for (var x = - cols; x < cols; x++) {
      var index = x + y * cols;
      //var v = createVector(sin(x)+cos(y),sin(x)*cos(y));
      var v = createVector(y,-x);
      flowfield[index] = v;
      fill('blue');
      stroke('blue');
      push();
      translate(x*scl,y*scl);
      rotate(v.heading());
      line(0,0,0.5*scl,0);
      let arrowSize = 7;
      translate(0.5*scl - arrowSize, 0);
      triangle(0, arrowSize / 2, 0, -arrowSize / 2, arrowSize, 0);
      pop();
    }
  }
    for (var i = 0; i < particles.length; i++) {
    particles[i].follow(flowfield);
    particles[i].update();
    particles[i].edges();
    particles[i].show();
  }
}
以及名为 particle.js 的第二个文件:
class Particle {
  constructor() {
    this.pos = createVector(random(-width,width), 
                            random(-height,height));
    this.vel = createVector(0, 0);
    this.acc = createVector(0, 0);
    this.maxspeed = 4;
    this.prevPos = this.pos.copy();
    this.size = 8;
  }

  update() {
    this.vel.add(this.acc);
    this.vel.limit(this.maxspeed);
    this.pos.add(this.vel);
    this.acc.mult(0);
  }

  follow(vectors) {
    var x = floor(this.pos.x / scl);
    var y = floor(this.pos.y / scl);
    var index = x + y * cols;
    var force = vectors[index];
    this.applyForce(force);
  }

  applyForce(force) {
    this.acc.add(force);
  }

  show() {
    noStroke();
    fill('rgba(100,0,255,.5)');
    circle(-(this.pos.x+width/2), -(this.pos.y-height/2), this.size);
    this.updatePrev();
  }

  updatePrev() {
    this.prevPos.x = this.pos.x;
    this.prevPos.y = this.pos.y;
  }

  edges() {
    if (this.pos.x > width) {
      this.pos.x = -width;
      this.updatePrev();
    }
    if (this.pos.x < -width) {
      this.pos.x = width;
      this.updatePrev();
    }
    if (this.pos.y > height) {
      this.pos.y = -height;
      this.updatePrev();
    }
    if (this.pos.y == -height) {
      this.pos.y = height;
      this.updatePrev();
    }

  }

}
使用此编辑更新代码的模拟开始还不错:
enter image description here
但很快所有粒子都与沿 x 轴的最后一行对齐。所以我想我需要一些帮助来理解 flow fields或缩小底部矢量的效果。

Ethan Hermsey 确实为我完美地解决了这个绘图问题。在这一点上,毫无疑问是由于代码中的一些故障或一些沟通不畅,接受答案中的代码实际上导致了与提出问题所需的输出不同的输出,以及 Ethan 自己为我解决的代码.所以仅供引用,这是预期的效果:
enter image description here
生成如下:
const scl = 35;
var cols, rows;
var particles = [];
var flowfield;

function setup() {

    createCanvas(750, 750);
    cols = ceil( width / scl );
    rows = ceil( height / scl );


    flowfield = new Array( cols * rows );

    for (var i = 0; i < 1000; i ++ ) {
        particles[i] = new Particle();
    }
}

function draw() {

    translate(height / 2, height / 2); //moves the origin to center
    scale( 1, - 1 ); //flips the y values so y increases "up"
    background( 255 );

    for ( var y = 0; y < rows; y ++ ) { 
        for ( var x = 0; x < cols; x ++ ) { 
      
      var index = x + y * cols;

      let vX = x * 2 - cols;
      let vY = y * 2 - rows;
                
     
      var v = createVector( vY, -vX );
      v.normalize();
          
      flowfield[index] = v;
      
      // The following push() / pull() affects only the arrows     
      push();
      fill( 'red' );
      stroke( 'red' );
      translate(x*scl-width/2,y*scl-height/2);
      rotate(v.heading());
      line(0,0,0.5*scl,0);
      let arrowSize = 7;
      translate(0.5*scl - arrowSize, 0);
      triangle(0, arrowSize / 2, 0, -arrowSize / 2, arrowSize, 0);
      pop();
// The preceding push() / pull() affects only the arrows     
    }// Closes inner loop
  }// Closes outer loop to create vectors and index.
  
//This next loop actually creates the desired particles:
    for (var i = 0; i < particles.length; i++) {
    particles[i].follow(flowfield);
    particles[i].update();
    particles[i].edges();
    particles[i].show();
  }
} // End of the function draw

class Particle {

    constructor() {

        // changed startpostion. Since the origin is in the center of the canvas,
        // the x goes from -width/2 to width/2
        // the y goes from -height/2 to height/2
        // i also changed this in this.edges().

        this.pos = createVector( random( - width / 2, width / 2 ),
            random( - height / 2, height / 2 ) );
        this.vel = createVector( 0, 0 );
        this.acc = createVector( 0, 0 );
        this.maxspeed = 4;
        this.steerStrength = 15;
        this.prevPos = this.pos.copy();
        this.size = 8;

    }

    update() {

        this.vel.add( this.acc );
        this.vel.limit( this.maxspeed );
        this.pos.add( this.vel );
        this.acc.mult( 0 );

    }

    follow( vectors ) {

        var x = floor( map( this.pos.x, - width / 2, width / 2, 0, cols - 1, true ) );
        var y = floor( map( this.pos.y, - height / 2, height / 2, 0, rows - 1, true ) );
        var index = ( y * cols ) + x;

        var force = vectors[ index ].copy();
        force.mult( this.steerStrength );
        this.applyForce( force );

    }

    applyForce( force ) {

        this.acc.add( force );

    }

    show() {

        noStroke();
        fill( 'rgba(100,0,255,.5)' );

        // you can just draw on the position.
        circle( this.pos.x, this.pos.y, this.size );

        this.updatePrev();

    }

    updatePrev() {

        this.prevPos.x = this.pos.x;
        this.prevPos.y = this.pos.y;

    }

    edges() {

        //clamp between -width/2 and width/2. -height/2 and height/2
        if ( this.pos.x > width / 2 ) {

            this.pos.x = - width / 2;
            this.updatePrev();

        }
        if ( this.pos.x < - width / 2 ) {

            this.pos.x = width / 2;
            this.updatePrev();

        }
        if ( this.pos.y > height / 2 ) {

            this.pos.y = - height / 2;
            this.updatePrev();

        }
        if ( this.pos.y < - height / 2 ) {

            this.pos.y = height / 2;
            this.updatePrev();

        }

    }

}

最佳答案

我们之前通过reddit联系过。我想在这里发布我的答案。
就像保罗在另一个答案中所说的那样,两个不同的坐标系令人困惑并导致了主要问题。

  • 当你生成流场时,从 [-cols, cols] 循环,而场只有 [0, cols] 大。这意味着 3/4 的向量放置在数组之外的无效位置上(或者,根本没有放置并且网格仅填充了公式的一个象限)。
  • 在 Particle.follow() 中,索引未正确计算,因此它会尝试访问不存在的无效位置上的流场数组,从而给出越界异常。

  • 如果需要保留 2 个坐标系,则必须在粒子类中以及在场生成循环中不断重新映射 x 和 y 值,以获得正确的结果。
    我喜欢 Paul 如何对向量进行归一化,并使用 map() 将粒子的坐标重新映射到流场坐标,我也这样做了。

    const arrowSize = 7;
    const inc = 0.1;
    const scl = 35;
    var cols, rows;
    var fr;
    var particles = [];
    var flowfield;
    
    function setup() {
    
        createCanvas( 500, 500 );
        cols = ceil( width / scl );
        rows = ceil( height / scl );
    
    
        flowfield = new Array( cols * rows );
    
    
        for ( var i = 0; i < 100; i ++ ) {
    
            particles[ i ] = new Particle();
    
        }
        background( 51 );
    
    }
    
    
    
    function draw() {
    
        translate( height / 2, height / 2 ); //moves the origin to center
        scale( 1, - 1 ); //flips the y values so y increases "up"
        background( 255 );
        fill( 'blue' );
        stroke( 'blue' );
        loadPixels();
    
    
        for ( var y = 0; y < rows; y ++ ) { // now loops from 0 to rows
    
            for ( var x = 0; x < cols; x ++ ) { //now loops from 0 to cols
    
                var index = ( y * cols ) + x;
    
                // because the formula assumes negative to positive values.
                // remap from range [0, cols] to [-cols/2, cols/2].
                // let vX = x * 2 - cols;
                // let vY = y * 2 - rows;
                
                // But more elegant would be to map to [-1, 1] 
                // ( with the exact same result )
                let vX = ( x / cols ) * 2 - 1;
                let vY = ( y / rows ) * 2 - 1;
    
                // normalize the vectors. It's common to multiply the vector in the
                // particle class later.
                var v = createVector( vY, - vX );
                v.normalize();
                flowfield[ index ] = v;
    
                push();
    
                translate( x * scl - width / 2, y * scl - height / 2 );
                rotate( v.heading() );
                line( 0, 0, 0.5 * scl, 0 );
                translate( 0.5 * scl - arrowSize, 0 );
                triangle( 0, arrowSize / 2, 0, - arrowSize / 2, arrowSize, 0 );
    
                pop();
    
            }
    
        }
    
    
        for ( var i = 0; i < particles.length; i ++ ) {
    
            particles[ i ].follow( flowfield );
            particles[ i ].update();
            particles[ i ].edges();
            particles[ i ].show();
    
        }
    
    }
    
    
    class Particle {
    
        constructor() {
    
            //changed startposition to be within screen space.
            this.pos = createVector(
                  random( - width / 2, width / 2 ),
              random( - height / 2, height / 2 )
            );
            this.vel = createVector( 0, 0 );
            this.acc = createVector( 0, 0 );
            this.maxspeed = 4;
            this.steerStrength = 15;
            this.prevPos = this.pos.copy();
            this.size = 8;
    
        }
    
        update() {
    
            this.vel.add( this.acc );
            this.vel.limit( this.maxspeed );
            this.pos.add( this.vel );
            this.acc.mult( 0 );
    
        }
    
        follow( vectors ) {
    
            var x = floor( map( this.pos.x, - width / 2, width / 2, 0, cols - 1, true ) );
            var y = floor( map( this.pos.y, - height / 2, height / 2, 0, rows - 1, true ) );
            var index = ( y * cols ) + x;
    
            //find and modify the steering strength.
            var force = vectors[ index ].copy();
            force.mult( this.steerStrength );
            this.applyForce( force );
    
        }
    
        applyForce( force ) {
    
            this.acc.add( force );
    
        }
    
        show() {
    
            noStroke();
            fill( 'rgba(100,0,255,.5)' );
            circle( this.pos.x, this.pos.y, this.size );
            this.updatePrev();
    
        }
    
        updatePrev() {
    
            this.prevPos.x = this.pos.x;
            this.prevPos.y = this.pos.y;
    
        }
    
        edges() {
    
            //clamp between -width/2 and width/2. -height/2 and height/2
            if ( this.pos.x > width / 2 ) {
    
                this.pos.x = - width / 2;
                this.updatePrev();
    
            }
            if ( this.pos.x < - width / 2 ) {
    
                this.pos.x = width / 2;
                this.updatePrev();
    
            }
            if ( this.pos.y > height / 2 ) {
    
                this.pos.y = - height / 2;
                this.updatePrev();
    
            }
            if ( this.pos.y < - height / 2 ) {
    
                this.pos.y = height / 2;
                this.updatePrev();
    
            }
    
        }
    
    }
    <script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.4.0/p5.js"></script>

    关于javascript - p5.j​​s 粒子在 2D 矢量场影响下的行为表现出较差的响应,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/70609081/

    相关文章:

    javascript - Dropzone js 使用 md5_file 检查重复文件

    javascript - 如何在 JavaScript 中为某些对象创建残像

    javascript - 如何使对象沿鼠标光标的方向旋转?

    javascript - 在同心圆上绘制点

    javascript - Monaco Editor 中的选项卡

    javascript - 如何保存两个嵌套属性的 id?

    javascript - Mysqli 在 jquery 中返回逗号分隔值不查看该值?

    p5.js - 从 p5js 中的 Canvas 图形中删除元素

    javascript - p5.j​​s 数组元素之间的碰撞

    javascript - 需要帮助将我的处理代码转换为 p5.js(ArrayList +其他!)