我用 p5js here 完成了我想要的 2D 物理矢量场表示的一半。 .另一半是让随机粒子动态地跟随矢量场的力,我遇到了很多问题。我尝试了多种方法来考虑粒子的环绕,以及我将绘图的原点翻译到 Canvas 中心的事实。然而,粒子似乎受场中各个矢量的影响最小,并最终沿着 x 轴行进,并带有轻微的颠簸。
我是 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();
}
}
}
使用此编辑更新代码的模拟开始还不错:但很快所有粒子都与沿 x 轴的最后一行对齐。所以我想我需要一些帮助来理解 flow fields或缩小底部矢量的效果。
Ethan Hermsey 确实为我完美地解决了这个绘图问题。在这一点上,毫无疑问是由于代码中的一些故障或一些沟通不畅,接受答案中的代码实际上导致了与提出问题所需的输出不同的输出,以及 Ethan 自己为我解决的代码.所以仅供引用,这是预期的效果:
生成如下:
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联系过。我想在这里发布我的答案。
就像保罗在另一个答案中所说的那样,两个不同的坐标系令人困惑并导致了主要问题。
如果需要保留 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.js 粒子在 2D 矢量场影响下的行为表现出较差的响应,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/70609081/