矩形分割

提供:kuhalaboWiki
移動: 案内, 検索
現代アート作家 モンドリアンの代表作品、Composition

目次

最大公約数

  • 4と6の最大公約数は2
  • 12と18の最大公約数は6
  • 24と36の最大公約数は12
  • 123と456の最大公約数は…3
  • 912と1368の最大公約数は???…456
ユークリッドの互除法
  • 自然数x0,x1の最大公約数の求め方(x0 > x1)
    1. x0をx1で割り、余りをx2とする。
    2. x1をx2で割り、余りをx3とする。
      • 割り切れるまで、この操作を繰り返す。
    3. xNで割り切れたら、xNが最大公約数である。
//aとbに対してユークリッド互除法を行う
int a = 10;
int b = 6;
int c;  //商のための変数
int d = b;  //余りのための変数
int itr = 0;  //繰り返しの回数
//繰り返し処理
while (d > 0){    //余りが0以上のとき以下の処理を実行
  itr++;  //繰り返し回数を1増やす
  c = a / b;  //cに商を代入
  d = a % b ;  //dに余りを代入
  println(itr, ":", a, "/", b, "=", c, "...", d);  //計算結果を表示
  a = b;  //aにbを代入
  b = d;  //bに余りを代入
}
println("GCD is", a);  //最大公約数を表示

長方形の分割

ユークリッドの互除法を可視化してみる。
  • 自然数x0,x1を2辺とする長方形を正方形で分割する。
    • 最も小さい正方形の1辺が最大公約数
  • 復習:点、線、図形の描画
DivRect
//横縦比がnumA:numBの長方形を正方形によって分割
int numA = 10;
int numB = 6;
int scalar = 50;   //長方形の拡大倍率
numA *= scalar;       //数値の大きさを拡大
numB *= scalar;
//プログラム実行中に動く変数
int wd = numB;    //分割に使う正方形の幅の大きさ(初期値numB)
int xPos = 0;    //正方形のx位置(初期値0)
int yPos = 0;    //正方形のy位置(初期値0)
int itr = 0;  //分割の繰り返し回数(初期値0)
//描画
size(500, 500);    //描画ウィンドウサイズ
//繰り返し処理
while (wd > 0){ //幅が0になるまで以下を実行
  itr++;              //繰り返し回数を1増やす
  if (itr % 2 == 1){      //繰り返し回数が奇数のとき,x軸方向へ正方形を増やす
    while (xPos + wd <= numA){    //幅を足したとき,長方形を超えなければ以下を実行
      rect(xPos, yPos, wd, wd);      //(xPos,yPos)を左上の頂点とする1辺wdの正方形を描画
      xPos += wd;                //x位置を更新
    }
    wd = numA - xPos;             //幅を更新
  } else {              //繰り返し回数が偶数のとき,y軸方向へ正方形を加える
    while (yPos + wd <= numB){    //幅を足したとき,長方形を超えなければ以下を実行
      rect(xPos, yPos, wd, wd);      //(xPos,yPos)を左上の頂点とする1辺wdの正方形を描画
      yPos += wd;                //y位置を更新
    }
    wd = numB - yPos;            //幅を更新
  }
}
正方形に色を付けてみる
int numA = 10;
int numB = 6;
int scalar = 50;
numA *= scalar;
numB *= scalar;
int wd = numB;
int xPos = 0;
int yPos = 0;
int itr = 0;
//描画
size(500, 500);
color col;  //色のための変数
colorMode(HSB, 1);  //01区間をパラメータとするHSB色形式を使用
//ループ
while (wd > 0) {
  itr++;
  if (itr % 2 ==1) {
    while (xPos + wd <= numA) {
      col = color(random(1), 1, 1);  //色相のみを01区間でランダムに変える
      fill(col);
      rect(xPos, yPos, wd, wd);
      xPos += wd;
    }
    wd = numA - xPos;
  } else {
    while (yPos + wd <= numB) {
      col = color(random(1), 1, 1);
      fill(col);
      rect(xPos, yPos, wd, wd);
      yPos += wd;
    }
    wd = numB - yPos;
  }
}

正方形の分割

自然数x0とx1の縦横比x0:x1を使って、長方形を正方形に変形すると、正方形を長方形に分割することになる。

  • x0×x1の横長の長方形を x1/x0 に横方向に圧縮して正方形にする。
DivSquare
//縦横比がnumA:numBの長方形によって正方形の描画ウィンドウを分割
int numA = 10;
int numB = 6;
float ratio = (float) numB / numA;  //比率
float xPos = 0;
float yPos = 0;
int itr = 0;
//描画
size(500, 500);
colorMode(HSB, 1);
float wd = width;    //描画ウィンドウの横幅サイズを初期値とする
//繰り返し処理
while (wd > 0.1){   //幅が許容誤差より大きければ以下を実行
  itr++;
  if (itr % 2 == 1){  //縦幅がwdの長方形をx軸方向へ加える
    while (xPos + wd * ratio < width + 0.1){
      //幅を足したとき,横幅がウィンドウを超えなければ以下の処理を実行
      fill(color(random(1), 1, 1));
      rect(xPos, yPos, wd * ratio, wd);      //縦幅wd,縦横比がnumA:numBの長方形を描画
      xPos += wd * ratio;                //x位置を更新
    }
    wd = width - xPos;
  } else {  //横幅がwdの長方形をy軸方向へ加える
    while (yPos + wd / ratio < width + 0.1){
      //幅を足したとき,縦幅がウィンドウを超えなければ以下の処理を実行
      fill(color(random(1), 1, 1));  //ランダムに色を指定
      rect(xPos, yPos, wd, wd / ratio);      //横幅wd,縦横比がnumA:numBの長方形を描画
      yPos += wd / ratio;                //y位置を更新
    }
    wd = width - yPos;
  }
}

矩形の再帰的分割

長方形を正方形に分割し、その正方形を長方形に分割する。

//縦横比がnumB:numAの長方形を逆の比の長方形によって分割
int numA = 10;
int numB = 6;
float ratio = (float) numB / numA;
void setup(){ //最初に1度だけ実行する関数
  size(500, 500);
  colorMode(HSB, 1);
  //この関数内だけのローカル変数
  int itr = 0;
  float xPos = 0;
  float yPos = 0;
  float wd = width * ratio;
  while (wd > 0.1){
    itr++;
    if (itr % 2 == 1){
      while (xPos + wd < width + 0.1){
        divSquare(xPos, yPos, wd);  //正方形を分割する関数の呼び出し
        xPos += wd;
      }
      wd = width - xPos;
    } else {
      while (yPos + wd < width * ratio + 0.1){
        divSquare(xPos, yPos, wd);  //正方形を分割する関数の呼び出し
        yPos += wd;
      }
      wd = width * ratio - yPos;
    }
  }
}

正方形を長方形に分割する関数

//位置(xPos,yPos)にある1辺がwdの正方形を縦横比がnumA:numBの長方形で分割する
void divSquare(float xPos, float yPos, float wd){
  //この関数内だけのローカル変数
  int itr = 0;
  float xEndPos = wd + xPos;  //正方形の右下の頂点のx座標
  float yEndPos = wd + yPos;  //正方形の右下の頂点のy座標
  //繰り返し処理
  while (wd > 0.1){
    itr++;
    if (itr % 2 == 1){
      while (xPos + wd * ratio < xEndPos + 0.1){  //ratioはグローバル変数
        fill(color(random(1), 1, 1));
        rect(xPos, yPos, wd * ratio, wd);
        xPos += wd * ratio;
      }
      wd = xEndPos - xPos;
    } else {
      while (yPos + wd / ratio < yEndPos + 0.1){
        fill(color(random(1), 1, 1));
        rect(xPos, yPos, wd, wd / ratio);
        yPos += wd / ratio;
      }
      wd = yEndPos - yPos;
    }
  }
}


再帰的呼び出しの使用

  • 正方形を長方形に分割し、その長方形を正方形に分割し、その正方形を長方形に分割し、・・・
  • 再帰的に関数を呼び出す。再帰的呼び出し(Recursive Call)
    • 再帰呼び出しを止めるしきい値(Threshold)を設定する。
int numA = 10;
int numB = 6;
float ratio = (float) numB / numA;
float thr = 160;  //しきい値
void setup(){
  size(500, 500);
  colorMode(HSB, 1);
  divSquare(0, 0, width); //正方形の分割
}

divSquare

//位置(xPos,yPos)にある1辺がwdの正方形を縦横比がnumA:numBの長方形で分割する
void divSquare(float xPos, float yPos, float wd){
  int itr = 0;
  float xEndPos = wd + xPos;
  float yEndPos = wd + yPos;
  fill(color(random(1), 1, 1));
  rect(xPos, yPos, wd, wd);
  while (wd > thr){  //wdがしきい値以上の場合に処理を行う
    itr++;
    if (itr % 2 == 1){
      while (xPos + wd * ratio < xEndPos + 0.1){
        divRect(xPos, yPos, wd * ratio);  //長方形を分割する関数の呼び出し
        xPos += wd * ratio;
      }
      wd = xEndPos - xPos;
    } else {
      while (yPos + wd / ratio < yEndPos + 0.1){
        divRect(xPos, yPos, wd);  //長方形を分割する関数の呼び出し
        yPos += wd / ratio;
      }
      wd = yEndPos - yPos;
    }
  }
}

divRect

//位置(xPos,yPos)にある横幅wdで縦横比がnumA:numBの長方形を正方形によって分割する
void divRect(float xPos, float yPos, float wd){
  int itr = 0;
  float xEndPos = xPos + wd;
  float yEndPos = yPos + wd / ratio;
  fill(color(random(1), 1, 1));
  rect(xPos, yPos, wd, wd / ratio);
  while (wd > thr){   //長方形の幅がしきい値以上の場合に処理を行う
    itr++;
    if (itr % 2 == 0){
      while (xPos + wd < xEndPos + 0.1){
        divSquare(xPos, yPos, wd);  //正方形を分割する関数の呼び出し
        xPos += wd;
      }
      wd = xEndPos - xPos;
    } else {
      while (yPos + wd < yEndPos + 0.1){
        divSquare(xPos, yPos, wd);  //正方形を分割する関数の呼び出し
        yPos += wd;
      }
      wd = yEndPos - yPos;
    }
  }
}

マウスクリックでx0,x1,thresholdをランダムに設定して描画する。

void mouseClicked(){
  numA = int(random(1, 20));  //1以上20以下のランダムな整数を代入
  numB = int(random(1, 20));
  while (numA == numB){ //numAとnumBが異なるようにする
    numB = int(random(1, 20));
  }
  thr = int(random(10,300));
  println("numA =", numA, "numB =", numB,"thr =", thr);  //numA,numB,thrの値を表示
  ratio = (float) numA / numB;
  background(0, 0, 1);  //背景を白で消去
  divSquare(0, 0, width);
}
void draw(){} //プログラムを実行している間,繰り返し実行する関数

無理数比の矩形分割

モンドリアンの再現
//float ratio = sqrt(2);  //白銀比
float ratio = (sqrt(5) + 1) / 2;  //黄金比
//float ratio = (3 + sqrt(13) ) / 2;  //青銅比
float thr = 40;  //分割する大きさに関するしきい値
float thr2 = 0.5; //確率を決定するしきい値
void setup(){
  size(500, 500);
  colorMode(HSB, 1);
  colorRect(0, 0, width, width);
  divSquare(0, 0, width);
}

モンドリアン風に配色を決める

void colorRect(float xPos, float yPos, float wd, float ht){
  color col;
  float val = random(1);
  if (val < 0.15){  //15%の確率
    col = color(0, 1, 1); //赤
  }else if (val < 0.3){ //15%の確率
    col = color(2.0 / 3, 1, 1); //青
  }else if (val < 0.45){  //15%の確率
    col = color(1.0 / 6, 1, 1); //黄
  }else if (val < 0.5){ //5%の確率
    col = color(0, 1, 0); //黒
  } else if (val < 0.7){  //20%の確率
    col = color(0, 0, 0.9); //灰
  } else {  //30%の確率
    col = color(0, 0, 1); //白
  }
  fill(col);
  strokeWeight(5);  //長方形の枠線の太さ
  rect(xPos, yPos, wd, ht);
}

矩形分割

void divRect(float xPos, float yPos, float wd){  //長方形を分割する関数
  int itr = 0;
  float xEndPos = xPos + wd;  //長方形の横の長さ
  float yEndPos = yPos + wd / ratio;   //長方形の縦の長さ
  while (wd > thr){   //wdがしきい値以上の場合に処理を行う
    itr++;
    if (itr % 2 == 0){
      while (xPos + wd < xEndPos + 0.1){
        colorRect(xPos, yPos, wd, wd);  //正方形を描く
        if (random(1) < thr2){
          divSquare(xPos, yPos, wd);  //正方形を分割する関数の呼び出し
        }
        xPos += wd;
      }
      wd = xEndPos - xPos;
    } else {
      while (yPos + wd < yEndPos + 0.1){
        colorRect(xPos, yPos, wd, wd);  //正方形を描く
        if (random(1) < thr2){
          divSquare(xPos, yPos, wd);  //正方形を分割する関数の呼び出し
        }
        yPos += wd;
      }
      wd = yEndPos - yPos;
    }
  }
}

void divSquare(float xPos, float yPos, float wd){  //正方形を分割する関数
  int itr = 0;
  float xEndPos = wd + xPos;  //正方形の横の長さ
  float yEndPos = wd + yPos;  //正方形の縦の長さ
  while (wd > thr){  //正方形の幅がしきい値以上の場合に実行
    itr++;
    if (itr % 2 ==1){
      while (xPos + wd * ratio < xEndPos + 0.1){
        colorRect(xPos, yPos, wd * ratio, wd);  //長方形を描く
        if (random(1) < thr2){  //thr2の確率で再分割
          divRect(xPos, yPos, wd * ratio);  //長方形を分割する関数の呼び出し
        }
        xPos += wd * ratio;
      }
      wd = xEndPos - xPos;
    } else {
      while (yPos + wd / ratio < yEndPos + 0.1){
        colorRect(xPos, yPos, wd, wd / ratio);  //長方形を描く
        if (random(1) < thr2){  //thr2の確率で再分割
          divRect(xPos, yPos, wd);  //長方形を分割する関数の呼び出し
        }
        yPos += wd / ratio;
      }
      wd = yEndPos - yPos;
    }
  }
}

マウスクリックでランダムに再構成

void mouseClicked(){
  thr = int(random(10, 50));
  thr2 = random(0,1);
  println("thr =", thr, "thr2 =", thr2);
  colorRect(0, 0, width, width);
  divSquare(0, 0, width);
}
void draw(){}


リンク

AI Composition, Y.Kuhara, The Faculty of Arts Festival 2022

フィボナッチ数列

スケーラブルアート論

個人用ツール
名前空間

変種
操作
案内
ツールボックス