Processing

14. ベジエ曲線を利用する

ベジエ曲線は、直線や円弧とは異なる複雑な図形の描画も可能です。

指定する座標が多く、また描かれる曲線の姿が想像しにくい面もありますが、上手く利用すると多様な表情の図形を描くことができます。

制御点を規則的に変化させる

void setup() {
  size( 500, 500 );
  background( 255 );
  smooth();
  noLoop();
  rectMode( CENTER );
}

void draw() {
  int leftX = 50;  //始点のx座標
  int topY = 50;   //始点のy座標

  int rightX = 450;  //終点のx座標
  int bottomY = 450; //終点のy座標

  int repeatNum = 50;  //繰返しの回数
  float controlPitch = ( rightX - leftX ) / repeatNum;  //制御点を移動させる幅を、横幅を繰返しの回数で割って求める
  int controlSpan = 100;

  stroke( 204, 51, 0 );
  noFill();

  for( int i = 0; i < repeatNum; i++ ) {
    float controlX = leftX + i * controlPitch;
    float controlY = bottomY - i * controlPitch;
    bezier( leftX, topY,
            controlX, controlY - controlSpan,
            controlX, controlY + controlSpan,
            rightX, bottomY );
    //rect( controlX, controlY - controlSpan, 4, 4 );
    //rect( controlX, controlY + controlSpan, 4, 4 );
  }
}

ベジエ曲線を練習する時、始点や終点、制御点を規則的に変化させると、どのような性質を持った曲線なのかが理解しやすくなります。

26行目〜29行目、長くなるので改行していますが、1行に書いても同じことです。

26行目、ベジエ曲線の始点は、x座標leftX、y座標topYに固定です。29行目、ベジエ曲線の描き終わりもx座標rightX、y座標bottomYに固定です。

24、25行目で、ベジエ曲線の2つの制御点の座標の基準を計算で求めていますが、画面左下から画面右上に等間隔で変化するようにしています。

27、28行目、21、22行目で求めた点から実際の制御点の位置を、y方向に-controlSpan、+ controlSpanしています。つまり上下に100ピクセルずらしています。

制御点の間隔は、描画全体の幅( rightX - leftX )を繰返しの回数で割って求めていますので、repeatNumの値さえ変えればベジエ曲線の本数を変えることができます。

30、31行目をコメントではなくすると、制御点の位置に小さな四角が描かれます。

制御点を規則的に変化させたベジエ曲線1

ベジエ曲線は、始点、終点、制御点を変えると、ちょっとした変更でも大きくその表情を変えます。色々試してみて欲しいと思います。

void setup() {
  size( 500, 500 );
  background( 255 );
  smooth();
  noLoop();
  rectMode( CENTER );
}

void draw() {
  int leftX = 50;  //始点のx座標
  int topY = 50;   //始点のy座標

  int rightX = 450;  //終点のx座標
  int bottomY = 450; //終点のy座標

  int repeatNum = 50;  //繰返しの回数
  float controlPitch = ( rightX - leftX ) / repeatNum;  //制御点を移動させる幅を、横幅を繰返しの回数で割って求める

  stroke( 204, 51, 0 );
  noFill();

  for( int i = 0; i < repeatNum; i++ ) {
    float controlX1 = leftX + i * controlPitch;
    float controlX2 = rightX - i * controlPitch;
    bezier( controlX1, topY,
            controlX1, 1000,
            controlX2, -500,
            controlX2, bottomY );
  }
}

制御点を規則的に変化させたベジエ曲線2

ページの先頭へ↑

ベジエ曲線を描画単位に利用する

「13. 描画の単位を考える」で行ったような描画単位にベジエ曲線を利用してみます。

どのような描画単位しようか考える際には、いきなり数多くの繰返しを行うのではなく(例えば以下のrepeatNumを1にしておくなどして)描画単位がある程度作り、その上で繰返しを行った方が良いでしょう。

描画単位:異なる大きさの波形の線
void setup() {
  size( 500, 500 );
  background( 255 );
  smooth();
  noLoop();
  colorMode( HSB, 360, 100, 100, 100 );
}

void draw() {
  
  int repeatNum = 9;
  int shapePitch = 50;
  int shapeSize = 50;
  
  int pitchWidth = shapePitch * ( repeatNum - 1 );
  int leftX = ( width - pitchWidth ) / 2;

  for( int i = 0; i < repeatNum; i++ ) {
    for( int j = 0; j < repeatNum; j++ ) {
      float drawPointX = leftX + j * shapePitch;
      float drawPointY = leftX + i * shapePitch;
      
      float lineAlpha = random( 50, 100 );
      stroke( 240, 80, 70, lineAlpha );
      strokeWeight( random( 1, 4 ) );
      noFill();

      drawShape( drawPointX, drawPointY, shapeSize );
    }
  }

}

void drawShape( float centerX, float centerY, int drawSize ) {

  int repeatBezNum = 5;
  float ctlPointPitch = drawSize / repeatBezNum;
  float preX = centerX - drawSize / 2;
  
  for( int i = 0; i < repeatBezNum; i++ ) {
    float yuragiX = random( -ctlPointPitch/4, ctlPointPitch/4 );
    float yuragiY1 = random( drawSize, drawSize*3 );
    float yuragiY2 = random( drawSize, drawSize*3 );
    float postX = preX + ctlPointPitch;
    bezier( preX, centerY,
            postX - ctlPointPitch/2 - yuragiX, centerY - yuragiY1,
            postX - ctlPointPitch/2 + yuragiX, centerY + yuragiY2,
            postX, centerY );
    preX = postX;
  }

}

23、24行目、繰返しの中で線の透明度に指定する変数の値をランダムに求め、色の透明度を変えています。同様に、25行目、線の太さもランダムにしています。

40〜50行目、drawShape()内で、さらに繰返しを記述し、その回数に応じて波の形になるように、ベジエ曲線に必要な座標を変化させています。

描画単位をベジエ曲線で作る1

ランダムを多用すれば良いというものでもないのですが、プログラムで手描きらしさをどのように出すか、ということも考えてみる方向の1つとしては面白いかもしれません。

ページの先頭へ↑

描画単位:ベジエ曲線でつくる形
void setup() {
  size( 500, 500 );
  background( 255 );
  smooth();
  noLoop();
  colorMode( HSB, 360, 100, 100, 100 );
}

void draw() {
  
  int repeatNum = 20;
  int shapePitch = 20;
  int shapeSize = 100;
  
  int pitchWidth = shapePitch * ( repeatNum - 1 );
  int leftX = ( width - pitchWidth ) / 2;

  for( int i = 0; i < repeatNum; i++ ) {
    for( int j = 0; j < repeatNum; j++ ) {
      float drawPointX = leftX + j * shapePitch;
      float drawPointY = leftX + i * shapePitch;
      
      float colorHue = random( 360 );
      fill( colorHue, 70, 100, 60 );
      noStroke();
      drawShape( drawPointX, drawPointY, shapeSize );
    }
  }

}

void drawShape( float centerX, float centerY, int drawSize ) {

  float tmpX = random( -drawSize/4, drawSize/4 );
  float yuragiX = random( 5 );
  
  float topX = centerX + tmpX;
  float bottomX = centerX - tmpX;

  beginShape();
  vertex( topX, centerY - drawSize/2 );
  bezierVertex( topX + yuragiX, centerY - drawSize/4,
                topX + yuragiX, centerY,
                bottomX, centerY + drawSize/2 );
  bezierVertex( topX - yuragiX, centerY,
                topX - yuragiX, centerY - drawSize/4,
                topX, centerY - drawSize/2 );
  endShape();

}

ベジエ曲線で形を描く場合、40行目、beginShape();で始め、48行目、endShape();で終わります。

41行目、図形の始点へvertex()を使って移動し、その後はbezierVertex()で図形を描きます。

bezierVertex()は直前の描画の終点がそのまま始点になります。

つまり45行目の時点では、直前のベジエ曲線の終点の座標が、自動的に次の曲線の始点になります。したがって、bezierVertex()をつなげて書くことで、ベジエ曲線で囲まれた図形を描くことができます。

描画単位をベジエ曲線で作る2

ページの先頭へ↑

< 13. 描画の単位を考える

15. 使用する色を考える >