P5.js上每个点都有经典箭头的静态向量场

2022-08-23 00:00:00 p5.js javascript

目标是使用p5.js在2D或3D网格上的每个点上显示一个带有箭头的矢量场(即尖端带有人字形的线段)。原因是我看到了很多创造性的艺术使用它,而流场(?)看起来很酷,那么为什么不用它们来描述有或没有运动的物理矢量场。

我对p5.js一无所知,但快速的在线搜索显示,我可以生成从左上角开始的位置向量,以及将原点连接到其终点或从一个位置向量到另一个位置向量的线(段):

function setup() {
  createCanvas(500, 500);
}

function draw() {
  background(0);
  
  let vec1 = createVector(100,100);
  let vec2 = createVector(30,150);
  

    //First vector
      strokeWeight(10);
      stroke(250,250,250);
      line(0,0,vec1.x,vec1.y);
    
    //Second vector
      strokeWeight(10);
      stroke(250,0,250);
      line(0,0,vec2.x,vec2.y);
      
    //Difference vector
      strokeWeight(5);
      stroke(0,250,250);
      line(vec1.x,vec1.y,vec2.x,vec2.y);
    }

现在我想添加箭头,并将其美化。理想的、长期的完美将是3Blue1Brown:


解决方案

我找到了一个答案here,例如,它允许在两点(或两个位置向量的末端)之间绘制一个向量:

function setup() {
  createCanvas(500, 500);
}


function draw() {
  background(240);

  let v0 = createVector(250,250); //Beginning point at center canvas.
  let v1 = createVector(50,50);   //Ending point at the tip of this positional vec.

  drawArrow(v0, v1, 'red'); //Function that draws a red vector from vec0 to vec1.
}


// draw an arrow for a vector at a given base position
function drawArrow(base, vec, myColor) {
  stroke(myColor);
  strokeWeight(4);
  fill(myColor);
  translate(base.x, base.y); //Will transport the object line (below) to the tip of the positional vector v1
  line(0, 0, vec.x, vec.y);  //The line from the O to the tip of v1
  rotate(vec.heading()); //Rotates the following triangle the angle of v1
  let arrowSize = 7; // Determines size of the vector arrowhead (triangle).
  translate(vec.mag() - arrowSize, 0); //Will translate a triangle below by the modulus of v1
  triangle(0, arrowSize / 2, 0, -arrowSize / 2, arrowSize, 0);
}

或此处是从画布中心到鼠标尖端的矢量:

function setup() {
  createCanvas(500, 500);
}


function draw() {
  background(240);

  let v0 = createVector(250,250);
  let v1 = createVector(mouseX - 250, mouseY - 250);

  drawArrow(v0, v1, 'red');
}


// draw an arrow for a vector at a given base position
function drawArrow(base, vec, myColor) {
  stroke(myColor);
  strokeWeight(4);
  fill(myColor);
  translate(base.x, base.y);
  line(0, 0, vec.x, vec.y);
  rotate(vec.heading());
  let arrowSize = 7;
  translate(vec.mag() - arrowSize, 0);
  triangle(0, arrowSize / 2, 0, -arrowSize / 2, arrowSize, 0);
}

这里不只是一个向量,而是用上面的代码创建的实际(随机)向量场,调整了Daniel Shiffman的行here:

var inc = 0.1;
scl = 35;
var cols,rows;

function setup() {
  createCanvas(500,500);
  cols = floor(width/scl);
  rows = floor(height/scl);
  }



function draw() {
  background(255);
  var yoff = 0;
  loadPixels();
  for (var y = 0; y < rows; y++) {
    var xoff = 0;
    for (var x = 0; x < cols; x++) {
      var index = (x + y * width)*4;
      var angle = noise(xoff,yoff) * TWO_PI;
      var v = p5.Vector.fromAngle(angle);
      xoff +- inc;
      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();
    }
    yoff += inc;
  }
}

遗憾的是,左上角的原点使得用数学方式表示的物理场相当误导,传达了场逆时针流动的直觉:

var inc = 0.1;
scl = 35;
var cols,rows;

function setup() {
  createCanvas(500,500);
  cols = floor(width/scl);
  rows = floor(height/scl);
  }



function draw() {
  background(255);
  var yoff = 0;
  loadPixels();
  for (var y = 0; y < rows; y++) {
    var xoff = 0;
    for (var x = 0; x < cols; x++) {
      var index = (x + y * width)*4;
      var angle = noise(xoff,yoff) * TWO_PI;
      //var v = createVector(sin(x)+cos(y),sin(x)*cos(y));
      var v = createVector(y,-x);
      xoff +- inc;
      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();
    }
    yoff += inc;
  }
}

最常见的具有负值和正值以及四个象限的笛卡尔坐标将显示该场实际上是如何顺时针流动的:

但这似乎有一个几乎很简单的修复方法,将计数器重置为从减去行数(和列数)到加上行数(和列数),而不是从零开始。此外,需要在y轴上反转递增值的方向,原点需要平移到画布的中间(height/2,height/2)如果是方形的:

var inc = 0.1;
scl = 35;
var cols,rows;

function setup() {
  createCanvas(500,500);
  cols = floor(width/scl);
  rows = floor(height/scl);
  }



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);
  var yoff = 0;
  loadPixels();
  for (var y = -rows; y < rows; y++) {
    var xoff = 0;
    for (var x =- cols; x < cols; x++) {
      var index = (x + y * width)*4;
      var angle = noise(xoff,yoff) * TWO_PI;
      //var v = createVector(sin(x)+cos(y),sin(x)*cos(y));
      var v = createVector(y,-x);
      xoff +- inc;
      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();
    }
    yoff += inc;
  }
}

相关文章