使用处理 (p5.js) 的二次曲线上的点 [英] Points on a Quadratic Curve using Processing (p5.js)

查看:132
本文介绍了使用处理 (p5.js) 的二次曲线上的点的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

 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 并遍历曲线时,我得到:

看起来它正在获取曲线上的点(花朵形状),但同时也获取了控制点"上的所有点.

我使用这个公式来生成点:

 flowerArray=[]for(let i = 0; i 

问题:是否可以只在花"上获得积分?而不是外形?

我尝试通过几种不同的方式跳过数组,但我无法让它按照我希望的方式工作.

更新我正在使用它来绘制点:

 for (i = 0; i 

解决方案

好可爱的问题.

突出的是这一部分:

if(i == 0) {FlowerArray.push(x,y);}别的 {cAngle = 角度 - 间距/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() {创建画布(512、512);背景(226、255、204);让flowerArray = [];让 centerX = 256;让 centerY = 256;让 numVertices = 7;让步数 = 11;让间距 = 360/numVertices;for (让 i = 0; i 

<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.1.9/p5.min.js"></script>

我个人建议养成格式化代码的习惯:这样可以更轻松地阅读代码和发现问题.你编程的越多,程序越大,你花在阅读代码上的时间就越多,因此让代码可读肯定会得到回报.

另一个建议是封装

弦乐艺术中二次贝塞尔曲线的插图.在每种情况下,用黑色圆圈标记的端点和用 X 标记的控制点定义了二次贝塞尔曲线,由

同样可以绘制为当前和下一个内部点之间的四边形贝塞尔点作为锚点,当前外部点也作为锚点.

I am using this formula to calculate the points on a 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;

When I set t = 10 and iterate through the curves, I get this:

It looks like it's getting the points on the curve (the flower shape), but also all the points on the "control points" as well.

I used this formula to generate the points:

    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)
    }
   }

Question: Is it possible to just get the points on the "flower" and not the outer shape?

I tried skipping through the array several different ways, but I wasn't able to get it to work the way I was hoping.

UPDATE I am using this to draw the points:

    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);    
}
}

解决方案

What a lovely question.

The one thing that stands out is this part:

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)
    }

Notice that you call flowerArray.push(x,y); where as in any other case you push 4 instead of two values: flowerArray.push(cX,cY,x,y). It's unclear why this condition is necessary in the first place: if(i == 0)

The code works as expected without it:

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>

Personally I recommend getting into the habit of formatting code: it makes it easier to read code and spot issues. The more you program and the larger the programs get the more time you spend reading code hence making code readable will definitely pay off.

Another suggestion is encapsulating the quadratic bezier formula in a function:

function quadLerp(p0, p1, p2, t){
  return ((1-t)*(1-t)) * p0 + 2 * ((1-t) * t * p1) + t * t * p2;
}

calling it like so:

  cPx2 = quadLerp(x1, qcX, x2, t);
  cPy2 = quadLerp(y1, qcY, y2, t);

One cool thing about quadratic bezier curves is that you can compute them by interpolating two linear interpolations:

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 by Wikipedia user Cmglee

Given that you can compute linear interpolation in p5.js via lerp() you can compute quadratic interpolation as:

function quadLerp(p0, p1, p2, t){
  return lerp(lerp(p0, p1, t),
              lerp(p1, p2, t),
              t);
}

It's nice that p5.js supports various curve drawing functions such as bezier() or curve() (and similar functions such as bezierPoint()/curvePoint() to compute the interpolated value you could use for custom rendering)

Update Based on your comment I understand you want to only draw the inner shape.

Your code is handling outer points of a regular polygon and the inner mid points, drawing a star like shape and the next outer points and using them as anchor / control points to draw circles on quadratic bezier curves between these points. As if this wasn't enough complexity, there is one array that stores all the anchor and control points mixed into a single list and you must keep track of the indices to draw correctly. Oh, and also you're using polar to cartesian coordinate system conversion to draw the regular polygon/star in the first place.

There's a lot going on, so let's try to break it down.

Starting of with drawing the star and math behind it: this is similar to islia's question and you can see my detailed answer here.

Notice the star example in her question: it's not a bad place to start since we don't have to worry about quadratic bezier points. It does introduce push()/pop() which you may not be familiar yet. It's useful to know, but can skip it for now. Let's look at a simplified version of that snippet:

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>

Now let's look at the same thing is more obvious variable names:

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>

Hopefully this makes it easier to understand which point is which.

To draw the quadratic bezier points you'd need the current outer point and next outer point as anchor points and the current inner point (in between them with the smaller radius) as the control point.

Here's a modified version of the sketch where star() function is repurposed to draw the flower:

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>

You can move the mouse to control the inner radius. If you hold the mouse pressed you can see the anchor/control points.

The same could've have been drawn as quad bezier points between current and next inner points as anchor points with the current outer point as an anchor as well.

这篇关于使用处理 (p5.js) 的二次曲线上的点的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆