如何让这个圆阵列出现在平行四边形的边界内?

How do I get this array of circles to appear within the boundary of a parallelogram?

我知道如何让一组圆圈出现在给定的正方形 space 内,但我如何让它们出现在不规则形状的 space 内。我有一个平行四边形轮廓,它定义了我试图让圆圈出现的位置的边界,但不知道如何实现这一点。谢谢!

来自 p5.js 的代码:

let circles = [];

function setup() {
  createCanvas(1200, 1200);
  colorMode(HSB,360,100,100,1);
  
  
  for (let i = 0; i < 500; i++) {
    x = random(400, 800);
    y = random(400, 800);
    d = 10;
    circles[i] = new circleClass(x, y, d);
  }
}


function draw() {
  
  background(35,13,90,1);
  
  noLoop();

  for (let i = 0; i < circles.length; i++) {
    circles[i].show();
  }
  
  
  noFill();
  stroke(0);
  quad(400,600,800,200,800,600,400,1000);
  
}



class circleClass {
  constructor(x, y, d) {
    this.x = x;
    this.y = y;
    this.d = d;
  }
  show() {
    noStroke();
    fill(27, 71, 73, 1);
    ellipse(this.x, this.y, this.d);
  }
}

您似乎想在平行四边形内的随机位置生成小圆圈。

取平行四边形边的两个向量,并使用这些向量的线性组合生成随机点:

ax = v[1].x - v[0].x  (800-400 for your quad)
ay = v[1].y - v[0].y  (200-600 for your quad)
bx = v[2].x - v[0].x   (and so on)
by = v[2].y - v[0].y

t = random(0..1)
u = random(0..1)
x = v[0].x + ax * t + bx * u
y = v[0].y + ay * t + by * u

其中 v[] 是平行四边形顶点数组

let v = [400,600,800,200,800,600,400,1000]
let ax = v[2] - v[0]
let ay = v[3] - v[1]
let bx = v[4] - v[0]
let by = v[5] - v[0]

for loop {
    let t = Math.random()
    let u = Math.random()
    let x = v[0] + ax * t + bx * u
    let y = v[1] + ay * t + by * u
    set circle center in x, y

这是一个 p5.js 版本:

function setup() {
  createCanvas(600, 600);
  background(255);
  
  let v = [200, 300,   // top left     array indices: [x=0, y=1]
           400, 100,   // top right    array indices: [x=2, y=3]
           400, 300,   // bottom right array indices: [x=4, y=5]
           200, 500];  // bottom left  array indices: [x=6, y=7]
  
  // top right - top left
  let ax = v[2] - v[0];
  let ay = v[3] - v[1];
  // bottom left - top left
  let bx = v[6] - v[0];
  let by = v[7] - v[1];

  for(let i = 0; i < 500; i++) {
      let t = random(); // interpolation amount on 1 axis
      let u = random(); // interpolation amount on the other
      // offset by top left point (v[0], v[1])
      // interpolate along top left to top right (imagine X axis aligned with parallelogram sides)
      // and top left to bottom left (imagine Y axis aligned with parallelogram sides)
      let x = v[0] + (ax * t + bx * u);
      let y = v[1] + (ay * t + by * u);
      // render point
      circle(x, y, 10);
  }
  
  noFill();
  // pass the v array as arguments using the spread operator
  quad(...v);
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.4.0/p5.min.js"></script>

这是对 MBo 上述 (+1) 回答的补充。

这部分的要点是:

let x = v[0] + ax * t + bx * u
let y = v[1] + ay * t + by * u

这可能看起来更熟悉 equation of a line

在 p5.js 中已经有一个非常有用的 lerp() 函数,它接受两个数字和第三个参数 t 介于 0.0 和 1.0 之间, returns 一个介于前两个之间的数字(例如,如果 t 是 0.5,则结果将是数字之间的 1/2,0.25 将是那里的 1/4,而 .75 将是 3/4,等等)。 这是一个基本示例:

let v1, v2, v3, v4;

function setup() {
  createCanvas(600, 600);
  
  v1 = createVector(200, 300);
  v2 = createVector(400, 100);
  v3 = createVector(400, 300);
  v4 = createVector(200, 500);
}

function draw(){
  background(255);
  
  let t = map(constrain(mouseX, 0, width), 0, width, 0.0, 1.0);
  // interpolate each component, x,y between v1 and v2 using horizontal mouse movement
  let x = lerp(v1.x, v2.x, t);
  let y = lerp(v1.y, v2.y, t);
  // preview interpolated position
  circle(x, y, 10);
  
  noFill();
  stroke(0);
  quad(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v4.x, v4.y);
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.4.0/p5.min.js"></script>

我们可以直接使用它,但是做 8 次会很乏味(2 个坐标 (x,y) 用于 4 个点(四边形))。

p5 中还有一个非常有用的 p5.Vector class p5.Vector.lerp() 可以帮助轻松地在两点之间进行插值:

let v1, v2, v3, v4;

function setup() {
  createCanvas(600, 600);
  
  v1 = createVector(200, 300);
  v2 = createVector(400, 100);
  v3 = createVector(400, 300);
  v4 = createVector(200, 500);
}

function draw(){
  background(255);
  
  let t = map(constrain(mouseX, 0, width), 0, width, 0.0, 1.0);
  // interpolate each component, x,y between v1 and v2 using horizontal mouse movement
  let l = p5.Vector.lerp(v1, v2, t);
  // preview interpolated position
  circle(l.x, l.y, 10);
  
  noFill();
  stroke(0);
  quad(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v4.x, v4.y);
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.4.0/p5.min.js"></script>

如果我们将使用两个不同插值量的点对之间的插值分组到一个可重用函数中,我们实际上实现了一种形式 bilinear interpolation:

/**
* Bilinear interpolation: interpolates a point on a between two lines (defined by 4 points)
* @param xt: traversal on first axis (0.0 -> 1.0)
* @param yt: traversal on second axis (0.0 -> 1.0)
**/
function quadLerp(v1, v2, v3, v4, xt, yt){
    let v1to2 = p5.Vector.lerp(v1, v2, yt);
    let v3to4 = p5.Vector.lerp(v3, v4, yt);
    return p5.Vector.lerp(v1to2, v3to4, xt);
}

演示:

let v1, v2, v3, v4;

function setup() {
  createCanvas(600, 600);
  
  v1 = createVector(200, 300);
  v2 = createVector(400, 100);
  v3 = createVector(400, 300);
  v4 = createVector(200, 500);
}

/**
* Bilinear interpolation: interpolates a point on a between two lines (defined by 4 points)
* @param xt: traversal on first axis (0.0 -> 1.0)
* @param yt: traversal on second axis (0.0 -> 1.0)
**/
function quadLerp(v1, v2, v3, v4, xt, yt){
    let v1to2 = p5.Vector.lerp(v1, v2, yt);
    let v3to4 = p5.Vector.lerp(v3, v4, yt);
    // text() is simply for debugging/visualisation purposed: not actually required
    text("v1to2", v1to2.x, v1to2.y);
    text("v3to4", v3to4.x, v3to4.y);
    return p5.Vector.lerp(v1to2, v3to4, xt);
}

function draw(){
  background(192, 255, 192);
  
  let u = map(constrain(mouseX, 0, width), 0, width, 0.0, 1.0);
  let v = map(constrain(mouseY, 0, height), 0, height, 0.0, 1.0);
  // vertex order (winding) is important
  let l = quadLerp(v1, v2, v4, v3, u, v);
  // preview interpolated position
  circle(l.x, l.y, 10);
  
  noFill();
  stroke(0);
  quad(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v4.x, v4.y);
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.4.0/p5.min.js"></script>

注意当鼠标在整个草图上移动时canvas圆总是在四边形的边界内。

这几乎就是解决方案:只需将映射的鼠标坐标交换为 0.0 和 1.0 之间的随机数。

function setup() {
  createCanvas(1200, 1200);
  background(192, 255, 192);
  
  let v1 = createVector(400, 600);
  let v2 = createVector(800, 200);
  let v3 = createVector(800, 600);
  let v4 = createVector(400, 1000);
  
  for (let i = 0 ; i < 500; i++) {
      let u = random();
      let v = random();
      // vertex order (winding) is important
      let l = quadLerp(v1, v2, v4, v3, u, v);
      circle(l.x, l.y, 10);
  }
  
  noFill();
  stroke(0);
  quad(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v4.x, v4.y);
}

/**
* Bilinear interpolation: interpolates a point on a between two lines (defined by 4 points)
* @param xt: traversal on first axis (0.0 -> 1.0)
* @param yt: traversal on second axis (0.0 -> 1.0)
**/
function quadLerp(v1, v2, v3, v4, xt, yt){
    let v1to2 = p5.Vector.lerp(v1, v2, yt);
    let v3to4 = p5.Vector.lerp(v3, v4, yt);
    return p5.Vector.lerp(v1to2, v3to4, xt);
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.4.0/p5.min.js"></script>

这里有一个更接近您的代码的变体:

let circles = [];

function setup() {
  createCanvas(1200, 1200);
  colorMode(HSB,360,100,100,1);
  background(35,13,90,1);
  
  let v1 = createVector(400, 600);
  let v2 = createVector(800, 200);
  let v3 = createVector(800, 600);
  let v4 = createVector(400, 1000);
  
  for (let i = 0 ; i < 500; i++) {
      let u = random();
      let v = random();
      // vertex order (winding) is important
      let l = quadLerp(v1, v2, v4, v3, u, v);
      circles[i] = new Circle(l.x, l.y, 10);
      circles[i].show();
  }
  
  noFill();
  stroke(0);
  quad(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v4.x, v4.y);
}

/**
* Bilinear interpolation: interpolates a point on a between two lines (defined by 4 points)
* @param xt: traversal on first axis (0.0 -> 1.0)
* @param yt: traversal on second axis (0.0 -> 1.0)
**/
function quadLerp(v1, v2, v3, v4, xt, yt){
    let v1to2 = p5.Vector.lerp(v1, v2, yt);
    let v3to4 = p5.Vector.lerp(v3, v4, yt);
    return p5.Vector.lerp(v1to2, v3to4, xt);
}

class Circle {
  constructor(x, y, d) {
    this.x = x;
    this.y = y;
    this.d = d;
  }
  show() {
    noStroke();
    fill(27, 71, 73, 1);
    ellipse(this.x, this.y, this.d);
  }
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.4.0/p5.min.js"></script>

我做了一些调整:

  • circleClass 重命名为 Circle 保持标题大小写 class 名称的命名约定。我删除了 Class 部分以避免在实例化圆圈时出现潜在的混淆(例如 new circleClass()
  • 我删除了 draw(),因此不再需要 noLoop();
  • circles 数组在这个基本示例中也有点多余,但在这个草图的更详细的动画版本中可能有用。

花点时间read/tweak/break/fix上面的代码。 了解基础知识将为您节省大量 运行 的时间。 这可能看起来违反直觉,但有时放慢速度可以帮助您加快速度。玩得开心!