processing - 使用处理 (p5.js) 的二次曲线上的点

标签 processing p5.js quadratic-curve

我正在使用这个公式来计算二次曲线上的点:

  cPx2 = (1-t)*(1-t)* x1+2 * (1-t)*t*qcX + t*t*x2;
  cPy2 = (1-t)*(1-t)* y1+2 * (1-t)*t*qcY + t*t*y2;
当我设置 t = 10 并遍历曲线时,我得到了:
points on the quad curve
看起来它得到了曲线上的点(花形),但也得到了“控制点”上的所有点。
我用这个公式来生成点:
    flowerArray=[]
    for(let i = 0; i < numVertices+1; i++) {
    angle = i * spacing;
    x = centerX + cos(radians(angle)) * 180;
    y = centerY+ sin(radians(angle)) * 180;

    if(i == 0) {
      flowerArray.push(x,y);
    }else {
        cAngle = angle - spacing/2;
          cX = centerX + cos(radians(cAngle)) * 100;
          cY = centerY+  sin(radians(cAngle)) * 100;
      
    flowerArray.push(cX,cY,x,y)
    }
   }
问题:是否有可能只得到“花”上的点而不是外形?
我尝试了几种不同的方式跳过数组,但我无法让它按照我希望的方式工作。
更新
我正在使用它来绘制要点:
    for (i = 0; i < flowerArray.length; i+=2){
        x1=flowerArray[i] 
        y1=flowerArray[i+1]  
        qcX=flowerArray[i+2] 
        qcY=flowerArray[i+3] 
        x2=flowerArray[i+4]
        y2=flowerArray[i+5] 
    for (k=0; k<= steps; k++) {   
      t = k/steps
      cPx2 = (1-t)*(1-t)* x1+2 * (1-t)*t*qcX + t*t*x2;
      cPy2 = (1-t)*(1-t)* y1+2 * (1-t)*t*qcY + t*t*y2;
        circle(cPx2, cPy2,3);    
}
}

最佳答案

多么可爱的问题。
突出的一件事是这部分:

if(i == 0) {
      flowerArray.push(x,y);
    }else {
        cAngle = angle - spacing/2;
          cX = centerX + cos(radians(cAngle)) * 100;
          cY = centerY+  sin(radians(cAngle)) * 100;
      
    flowerArray.push(cX,cY,x,y)
    }
请注意,您调用 flowerArray.push(x,y);在任何其他情况下,您按下 4 而不是两个值:flowerArray.push(cX,cY,x,y) .不清楚为什么首先需要这种条件:if(i == 0)没有它,代码按预期工作:

function setup() {
  
  createCanvas(512, 512);
  background(226, 255, 204);
  
  let flowerArray = [];
  let centerX = 256;
  let centerY = 256;
  let numVertices = 7;
  let steps = 11;
  let spacing = 360 / numVertices;

  
  for (let i = 0; i < numVertices + 1; i++) {
    
    angle = i * spacing;
    
    x = centerX + cos(radians(angle)) * 180;
    y = centerY + sin(radians(angle)) * 180;
  
    cAngle = angle - spacing/2;
      
    cX = centerX + cos(radians(cAngle)) * 100;
    cY = centerY+  sin(radians(cAngle)) * 100;
  
    flowerArray.push(cX, cY, x, y);
  }

  for (i = 0; i < flowerArray.length; i+=2) {
    
    x1=flowerArray[i];
    y1=flowerArray[i+1];  
    
    qcX=flowerArray[i+2];
    qcY=flowerArray[i+3];
    
    x2=flowerArray[i+4];
    y2=flowerArray[i+5];
    
    for (k=0; k <= steps; k++) {
      t = k/steps;
      cPx2 = (1-t)*(1-t)* x1+2 * (1-t)*t*qcX + t*t*x2;
      cPy2 = (1-t)*(1-t)* y1+2 * (1-t)*t*qcY + t*t*y2;
      
      circle(cPx2, cPy2, 3);
    }
  }
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.1.9/p5.min.js"></script>

quadratic curves rendered between points of a 7 pointed star as circles interpolated between points
我个人建议养成格式化代码的习惯:它使阅读代码和发现问题更容易。您编写的程序越多,程序越大,您花在阅读代码上的时间就越多,因此使代码可读肯定会得到返回。
另一个建议是封装 quadratic bezier formula在一个函数中:
function quadLerp(p0, p1, p2, t){
  return ((1-t)*(1-t)) * p0 + 2 * ((1-t) * t * p1) + t * t * p2;
}
像这样称呼它:
  cPx2 = quadLerp(x1, qcX, x2, t);
  cPy2 = quadLerp(y1, qcY, y2, t);
关于二次贝塞尔曲线的一件很酷的事情是,您可以通过插值两个线性插值来计算它们:
Illustration of quadratic Bézier curves in string art. In each case, end points marked with black circles and the control point marked with an X define the quadratic Bézier curve shown as a dotted line.
弦乐艺术中的二次贝塞尔曲线的插图。在每种情况下,用黑色圆圈标记的端点和用 X 标记的控制点定义了二次贝塞尔曲线,显示为 Wikipedia user Cmglee 的虚线。
假设您可以通过 lerp() 在 p5.js 中计算线性插值您可以将二次插值计算为:
function quadLerp(p0, p1, p2, t){
  return lerp(lerp(p0, p1, t),
              lerp(p1, p2, t),
              t);
}
不错,p5.js支持 bezier() 等各种曲线绘制功能或 curve() (以及类似的函数,例如 bezierPoint()/curvePoint() 来计算可用于自定义渲染的插值)
更新
根据您的评论,我了解您只想绘制内部形状。
您的代码正在处理正多边形的外部点和内部中点,绘制星形形状和下一个外部点,并将它们用作 anchor /控制点,以在这些点之间的二次贝塞尔曲线上绘制圆。好像这还不够复杂,有一个数组将所有 anchor 和控制点混合到一个列表中,您必须跟踪索引才能正确绘制。哦,而且您首先使用极坐标到笛卡尔坐标系转换来绘制正多边形/星形。
发生了很多事情,所以让我们试着分解一下。
从画星星和它背后的数学开始:这类似于 islia 的问题,你可以看到我的 detailed answer here .
注意 star example在她的问题中:这不是一个糟糕的起点,因为我们不必担心二次贝塞尔点。它确实介绍了push()/pop()你可能还不熟悉。知道它很有用,但现在可以跳过它。让我们看一下该片段的简化版本:

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

function draw() {
  background(102);

  star(width * 0.5, height * 0.5, 80, 100, 7);
}

function star(x, y, radius1, radius2, npoints) {
  let angle = TWO_PI / npoints;
  let halfAngle = angle / 2.0;
  beginShape();
  for (let a = 0; a < TWO_PI; a += angle) {
    let sx = x + cos(a) * radius2;
    let sy = y + sin(a) * radius2;
    vertex(sx, sy);
    sx = x + cos(a + halfAngle) * radius1;
    sy = y + sin(a + halfAngle) * radius1;
    vertex(sx, sy);
  }
  endShape(CLOSE);
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.1.9/p5.min.js"></script>

现在让我们看看同样的东西更明显的变量名:

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

function draw() {
  background(102);

  star(width * 0.5, height * 0.5, 80, 100, 7);
}

function star(x, y, innerRadius, outerRadius, npoints) {
  let angle = TWO_PI / npoints;
  let halfAngle = angle / 2.0;
  beginShape();
  
  for (let a = 0; a < TWO_PI; a += angle) {
    
    let xOuter = x + cos(a) * outerRadius;
    let yOuter = y + sin(a) * outerRadius;
    vertex(xOuter, yOuter);
    
    let xInner = x + cos(a + halfAngle) * innerRadius;
    let yInner = y + sin(a + halfAngle) * innerRadius;
    vertex(xInner, yInner);
  }
  
  endShape();
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.1.9/p5.min.js"></script>

希望这可以更容易地理解哪一点是哪一点。
要绘制二次贝塞尔点,您需要当前外点和下一个外点作为 anchor ,当前内点(在它们之间,半径较小)作为控制点。
这是草图的修改版本,其中 star()函数被重新用于绘制花朵:

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

function draw() {
  background(226, 255, 204);

  flower(width * 0.5, height * 0.5, mouseX, 100, 7);
  
  text("innerRadius = " + mouseX, 10, 15);  
}

function flower(x, y, innerRadius, outerRadius, npoints) {
  let angleIncrement = TWO_PI / npoints;
  let halfAngle = angleIncrement / 2.0;
  // increment by point index
  for (let i = 0; i < npoints; i++) {
    // calculate the current angle around the circle
    let angle = angleIncrement * i;
    // calculate current outer point
    let xOuter = x + cos(angle) * outerRadius;
    let yOuter = y + sin(angle) * outerRadius;
    // calculate current inner point
    let xInner = x + cos(angle + halfAngle) * innerRadius;
    let yInner = y + sin(angle + halfAngle) * innerRadius;
    
    // next angle increment
    let angleNext = angleIncrement * (i+1);
    // calculate next outer point
    let xOuterNext = x + cos(angleNext) * outerRadius;
    let yOuterNext = y + sin(angleNext) * outerRadius;
    // draw quad bezier between current and outer points with inner point as control point
    quadBezierCircles(xOuter, yOuter, xInner, yInner, xOuterNext, yOuterNext, 11);
    
    // for debug purposes only: render 
    if(mouseIsPressed){
      circle(xInner,yInner,9);
      circle(xOuter,yOuter,9);
    }
  }
}

function quadBezierCircles(anchorX1, anchorY1, controlX, controlY, anchorX2, anchorY2, steps){
  for (let k = 0 ; k <= steps; k++) {
    
    t = k / steps;
    
    x = quadLerp(anchorX1, controlX, anchorX2, t);
    y = quadLerp(anchorY1, controlY, anchorY2, t);
      
    circle(x, y, 3);
  }
}

function quadLerp(p0, p1, p2, t){
  return lerp(lerp(p0, p1, t),
              lerp(p1, p2, t),
              t);
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.1.9/p5.min.js"></script>

您可以移动鼠标来控制内半径。
如果您按住鼠标,您可以看到 anchor /控制点。
bezier points
bezier points with visualised anchor and control points
同样可以绘制为当前和下一个内部点之间的四边形贝塞尔点作为 anchor ,当前外部点也作为 anchor 。

关于processing - 使用处理 (p5.js) 的二次曲线上的点,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/63525491/

相关文章:

javascript - 在 P5.js 中改变物体高度的正弦函数

java - 确定贝塞尔曲线是否与圆重叠的最佳方法是什么?

r - 非线性回归 'abline'

javascript - 将文本放在 p5.js 中的草图之上

javascript - 像轮子一样围绕中心点旋转三 Angular 形

javascript - 如何使用抛物线方程绘制图形

javascript - 计数增量加速的二次函数

java - 将整个 kinect 骨架放置在 3D 空间的中心

java - 写入串行端口时处理 block 绘图功能

javascript - 将函数的结果作为链接的文本