Processing

13. 描画の単位を考える

円や四角形を繰返し描く場合、1つ1つの円や四角を描画の単位と捉えることができます。

自分の作品を制作する際には、描画の単位を工夫してみることも必要でしょう。

ここでは「12. 関数で処理をまとめる」をふまえ、描画の単位を関数として考えてみます。

描画の単位を関数として定義する

以下は「11. プログラムを汎用的にする」で縦横に円を描画したプログラムを、Processing定番のsetup()とdraw()部分に分け、なおかつ円の描画を独自の関数にしたものです。

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

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

  colorMode( HSB, 360, 100, 100, 100 );
  stroke( 45, 100, 90, 100 );
  fill( 45, 100, 90, 50 );

  for( int i = 0; i < repeatNum; i++ ) {
    for( int j = 0; j < repeatNum; j++ ) {
      float drawPointX = leftX + j * shapePicth;
      float drawPointY = leftX + i * shapePicth;
      drawShape( drawPointX, drawPointY, shapeSize );
    }
  }
}


void drawShape( float centerX, float centerY, int drawSize ) {
  noStroke();
  ellipse( centerX, centerY, drawSize, drawSize );
}

26行目、今までのようにellipse()を書いても同じ処理は実現できますが、ここではdrawShape()という関数を別個に作り、その中にellipse()を記しています。

drawShape()は円を描画するだけで、呼び出し元が処理結果の値を利用したりはしません。つまり値を返したりはしないので、戻り値はvoidと書かれています。

引数は必要です。26行目でdrawShape()関数を呼び出していますが、drawPointX(図形を描くx座標)、drawPointY(y座標)、shapeSize(円のサイズ)を関数に渡しています。

32〜35行目のdrawShape()の処理では、drawPointXがcenterXに、drawPointYがcenterYに、shapeSizeがdrawSizeにそれぞれ引き継がれ、ellipse()によって利用され円が描かれます。

描画の単位は円

次にdrawShape()以外のプログラムは変えずに、drawShape()の中の円を描く記述だけを変えてみます。つまりsetup()やdraw()はいじらずに、drawShape()だけを変更します。

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

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

  colorMode( HSB, 360, 100, 100, 100 );
  stroke( 45, 100, 90, 100 );
  fill( 45, 100, 90, 50 );

  for( int i = 0; i < repeatNum; i++ ) {
    for( int j = 0; j < repeatNum; j++ ) {
      float drawPointX = leftX + j * shapePicth;
      float drawPointY = leftX + i * shapePicth;
      drawShape( drawPointX, drawPointY, shapeSize );
    }
  }
}


void drawShape( float centerX, float centerY, int drawSize ) {
  noStroke();
  rect( centerX, centerY, drawSize, drawSize );
}

34行目、先ほどは円を描く命令だった部分を、矩形を描く命令に変えてみました。以下のようになります。

描画の単位は矩形

つまりこのように記述すると、描画単位として円や四角を選ぶということと、それをどのように描くかという描画処理とを関数として分けたことになります。drawShape()内に描かれた図形が繰返し描かれるわけですから、円や四角以外の独自の描画単位を作ろうと試みる場合は、その処理をdrawShape()内に記述すれば良いということになります。

円や矩形だけではなく、どのような描画の単位とするのが自分が望むプログラムとして相応しいのか、工夫を試みてみると良いでしょう。

ページの先頭へ↑

いくつかのサンプル

以下、プログラムは関数drawShape()の部分のみ記載します。それ以外(loadFunc()の内容)は前述に同じです。

描画単位:単位内のランダムな地点を経由する折れ線
void drawShape( float centerX, float centerY, int drawSize ) {
  noFill();
  int drawRepeat = 20;
  
  float preX = centerX;
  float preY = centerY;
  int lineArea = drawSize/2;
  
  for( int i = 0; i < drawRepeat; i++ ) {
    float postX = centerX + random( -lineArea, lineArea );
    float postY = centerY + random( -lineArea, lineArea );
    line( preX, preY, postX, postY );
    preX = postX;
    preY = postY;
  }
}

描画単位の中心からランダムな場所に続けて折れ線を20本描いています。

preX、preYは線の始点のx座標、y座標を保存する変数、postX、postYは線の終点のx座標、y座標を保存する変数です。

drawShape()内5、6行目、描画処理で計算したcenterXとcenterYの値をpreXとpreYに一端格納しています。つまり折れ線は必ず描画単位の中心から描かれ始めます。

10、11行目、postXとpostYの値を、centerXとcenterYにランダムな値を足すことで決めています。ランダムな値は描画単位の-1/2〜1/2の範囲になります。

12行目、始点( preX, preY )から終点( postX, postY )に線を引いています。

13、14行目、折れ線の場合、1つの線の終点が次の始点になりますので、preXにpostXを、preYにpostYをそれぞれ代入しています。次の繰り返しの10、11行目で、新しいpostX、postYが決まります。

描画単位サンプル1

ページの先頭へ↑

描画単位:単位内の端から端まで、範囲内の中心を通る複数の線
void drawShape( float centerX, float centerY, int drawSize ) {
  noFill();
  int drawRepeat = 6;

  int harfShapeSize = drawSize/2;  //描画単位の半分のサイズ
  float topPointY = centerY - harfShapeSize;    //描画単位の上端のy座標
  float bottomPointY = centerY + harfShapeSize; //描画単位の下端のy座標
  float leftPointX = centerX - harfShapeSize;   //描画単位の左端のx座標
  float rightPointX = centerX + harfShapeSize;  //描画単位の右端のx座標

  for( int i = 0; i < drawRepeat; i++ ) {
    float tmpX = random( -harfShapeSize, harfShapeSize );  //描画範囲座標内でランダムな数
    float tmpY = random( -harfShapeSize, harfShapeSize );  //描画範囲座標内でランダムな数
    
    line( centerX - tmpX, topPointY, centerX + tmpX, bottomPointY ); //x座標は中央からランダム、y座標は固定
    line( leftPointX, centerY - tmpY, rightPointX, centerY + tmpY ); //y座標は中央からランダム、x座標は固定
  }
}

描画単位サンプル2

ページの先頭へ↑

描画単位:ランダムな描画角度の円弧
void drawShape( float centerX, float centerY, int drawSize ) {
  noFill();

  float tmpSize = random( drawSize/2, drawSize );	//描画範囲サイズの半分〜描画範囲サイズのランダムな値
  float startAngle = random( 0, 180 );  //0〜180までのランダムな値
  float endAngle = random( 180, 360 );  //180〜360までのランダムな値

  arc( centerX, centerY, tmpSize, tmpSize, radians( startAngle ), radians( endAngle ) );
}

drawShape内で、描き始めの範囲が0〜180度(0〜πラジアン)、描き終わりの範囲が180度〜360度(πラジアン〜2πラジアン)の円弧を描画。

描画単位サンプル3

ページの先頭へ↑

描画単位:複数の円
void drawShape( float centerX, float centerY, int drawSize ) {
  
  noStroke();

  float tmpSize = random( drawSize/4, drawSize/2 );

  float tmpX = random( -tmpSize/2, tmpSize/2 );
  float tmpY = random( -tmpSize/2, tmpSize/2 );
  
  int drawRepeat = 4;

  ellipse( centerX + tmpX, centerY + tmpY, tmpSize, tmpSize );
  
  for( int i = 0; i < drawRepeat; i++ ) {

    tmpSize = random( drawSize/8, drawSize/4 );
    
    tmpX = random( -drawSize/4, drawSize/4 );
    tmpY = random( -drawSize/4, drawSize/4 );
    
    ellipse( centerX + tmpX, centerY + tmpY, tmpSize, tmpSize );

  }

}

5行目、大きな円のサイズ。drawSize/4からdrawSize/2(サイズの4分の1からサイズの2分の1)までの間でランダム。

7、8行目、大きな円の中心がcenterX、centerYで指定された値からずれる範囲。-drawSize/2からdrawSize/2までの間でランダム。

14〜23行目、小さな円をdrawRepeatの数だけ描画。小さな円のサイズはdrawSize/8からdrawSize/4(サイズの8分の1からサイズの4分の1)までの間でランダム。中心がずれる位置は、-drawSize/4からdrawSize/4までの間でランダム。

描画単位サンプル4

ページの先頭へ↑

< 12. 関数で処理をまとめる

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