処理を使用した2次曲線上の点(p5.js)

Aug 22 2020

この式を使用して、2次曲線上の点を計算しています。

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

回答

4 GeorgeProfenza Aug 22 2020 at 06:19

なんて素敵な質問でしょう。

目立つのはこの部分です。

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);他の場合と同様に、2つの値の代わりに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>

個人的には、コードをフォーマットする習慣を身につけることをお勧めします。コードを読みやすく、問題を見つけるのが簡単になります。プログラムが多ければ多いほど、またプログラムが大きくなるほど、コードの読み取りに費やす時間が長くなるため、コードを読み取り可能にすることで確実に成果が得られます。

もう1つの提案は、2次ベジェ式を関数にカプセル化することです。

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

二次ベジェ曲線の優れた点の1つは、2つの線形補間を補間することでそれらを計算できることです。

ストリングアートの二次ベジエ曲線のイラスト。いずれの場合も、黒い円でマークされた終点とXでマークされた制御点は、ウィキペディアのユーザーCmgleeによって点線で示される2次ベジェ曲線を定義します。

p5.j​​sで線形補間をlerp()計算できるとすると、次のように2次補間を計算できます。

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

p5.j​​sがbezier()orなどのさまざまな曲線描画関数をサポートしているのは素晴らしいことですcurve()(カスタムレンダリングに使用できる補間値を計算するためのbezierPoint()/などの同様の関数curvePoint()

更新あなたのコメントに基づいて、あなたは内側の形だけを描きたいと思います。

あなたのコードは、正多角形の外側の点と内側の中点を処理し、星のような形と次の外側の点を描画し、それらをアンカー/制御点として使用して、これらの点の間の2次ベジェ曲線上に円を描画します。これが十分な複雑さではなかったかのように、すべてのアンカーポイントとコントロールポイントを1つのリストに混合して格納する配列が1つあり、正しく描画するにはインデックスを追跡する必要があります。ああ、そしてまた、あなたは最初に正多角形/星を描くために極座標からデカルト座標系への変換を使用しています。

たくさんのことが起こっているので、それを分解してみましょう。

星とその背後にある数学を描くことから始めます。これはisliaの質問に似ており、私の詳細な答えをここで見ることができます。

彼女の質問のスターの例に注目してください。2次ベジェポイントについて心配する必要がないため、開始するのに悪い場所ではありません。まだなじみのないかもしれない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>

うまくいけば、これにより、どのポイントがどれであるかを理解しやすくなります。

二次ベジェ点を描画するには、現在の外側の点と次の外側の点をアンカーポイントとして、現在の内側の点(半径が小さい方の中間)を制御点として使用する必要があります。

これ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>

マウスを動かして、内側の半径を制御できます。マウスを押したままにすると、アンカー/コントロールポイントが表示されます。

同じことが、現在の外側のポイントをアンカーとして、現在の内側のポイントと次の内側のポイントの間のクワッドベジェポイントとしてアンカーポイントとして描画された可能性があります。