[processing]伝統模様を描いてみよう(Processing Advent Calendar 2011 – 9日目)

Processingの記事を書くのは本当に久しぶりです。
Processing Advent Calendar 2011に参加しておりまして、9日目の担当ということで執筆いたします。

みなさんは、伝統模様と聞いてパッとイメージが浮かぶでしょうか?
たぶん着物やハンカチなどの絵柄に使われているのを見たことがあるかもしれません。

伝統模様は

  • 同じパターンが繰り返し使われている
  • 幾何学的な模様が多い

といった観点からとても不思議な模様で、またProcessingの練習で描いてみる題材としても適していると思っています。

百聞は一見にしかず、いくつか私の描いたものをコード付きでご紹介します。

青海波(せいがいは)

[p5code]
// 青海波(せいがいは)

int r = 80;
int r2 = r * 2;

void setup() {
size(400, 400);
background(255, 255, 255);
smooth();
noLoop();
}

void draw() {
noStroke();
background(255, 255, 255);

boolean flag = true;
for (int y = 0; y < height + r2; y += r) { int x_base = flag ? 0 : r / 2 ; for (int x = x_base; x < width + r2; x += r) { drawPattern(x, y); } flag = !flag; } } void drawPattern(float cx, float cy) { ellipseMode(CENTER_RADIUS); int script[][] = { {80, 0, 0, 0}, {75, 255, 255, 255}, {65, 0, 0, 0}, {60, 255, 255, 255}, {50, 0, 0, 0}, {45, 255, 255, 255}, {35, 0, 0, 0}, {30, 255, 255, 255}, {20, 0, 0, 0}, {15, 255, 255, 255}, {5, 0, 0, 0} }; for (int i = 0; i < script.length; i++) { int len = script[i][0]; fill(script[i][1], script[i][2], script[i][3]); ellipse(cx, cy, len, len); } } [/p5code] 青海波は文字通り波をイメージした模様です。私のコードでは、2重のfor文でx, y座標をずらしながら、 パターン化した白黒の円を上から塗り重ねることで、青海波っぽく描いています。

星七宝繋ぎ(ほししっぽう)

[p5code]
// 星七宝繋ぎ(ほししっぽう)

int r = 80;
int r2 = r * 2;

float ct[] = new float[360];
float st[] = new float[360];

void setup() {
size(400, 400);
background(0, 0, 0);
smooth();
noLoop();

ellipseMode(CENTER_RADIUS);

for (int d = 0; d < 360; d++) { ct[d] = cos(radians(d)); st[d] = sin(radians(d)); } } void draw() { background(0, 0, 0); strokeWeight(2); stroke(255, 255, 255); noFill(); boolean flag = true; for (int y = -20; y < height + r2; y += r) { int x_base = -20 + (flag ? 0 : r); for (int x = x_base; x < width + r2; x += r2) { drawPattern(x, y); } flag = !flag; } fill(0, 0, 0); flag = true; for (int y = -20; y < height + r2; y += r) { int x_base = -20 + (flag ? 0 : r); for (int x = x_base; x < width + r2; x += r2) { drawPattern2(x, y); } flag = !flag; } } void drawPattern(float cx, float cy) { float p[][] = { // {cx + r, cy - r, 0, 90, 180, 270}, // {cx - r, cy - r, 90, 180, 270, 360}, {cx - r, cy + r, 180, 270, 0, 90}, {cx + r, cy + r, 270, 360, 90, 180} }; for (int i = 0; i < p.length; i++) { float tx = p[i][0]; float ty = p[i][1]; int crad_begin = (int)p[i][2]; int crad_end = (int)p[i][3]; int trad_begin = (int)p[i][4]; int trad_end = (int)p[i][5]; beginShape(LINES); for (int d = crad_begin; d < crad_end; d++) { vertex(cx + ct[d] * r, cy - st[d] * r); } for (int d = trad_begin; d < trad_end; d++) { vertex(tx + ct[d] * r, ty - st[d] * r); } endShape(CLOSE); } } void drawPattern2(float cx, float cy) { ellipse(cx + r, cy, 10, 10); ellipse(cx, cy + r, 10, 10); } [/p5code] 七宝模様は、円を重ね合わせることで、円の弧が星のように見える模様です。 私のコードはちょっと複雑ですね。たしか円を並べただけだと七宝模様に近づかなかったので、 円弧をvertex()で描いていったのだと記憶しています。

花亀甲(はなきっこう)

[p5code]
// 花亀甲(はなきっこう)

float ROOT_3 = (float)Math.sqrt(3);
float ROOT_3_DIV_2 = ROOT_3 / 2.0;

float ROOT_2 = (float)Math.sqrt(2);
float ONE_DIV_ROOT_2 = 1 / ROOT_2;

int ix = -20;
int iy = -20;
int r = 50;
int r2 = r * 2;
int in_r = r – 8;

float ct[] = new float[360];
float st[] = new float[360];

void setup() {
size(400, 400);
background(255, 255, 255);
smooth();
noLoop();

for (int d = 0; d < 360; d++) { ct[d] = cos(radians(d)); st[d] = sin(radians(d)); } } void draw() { background(255, 255, 255); boolean flag = true; for (float y = iy; y < height + r2; y += r * 3 / 2) { float x_base = ix + (flag ? 0 : r * ROOT_3_DIV_2); for (float x = x_base; x < width + r2; x += r * ROOT_3) { drawPattern(x, y); } flag = !flag; } } void drawPattern(float cx, float cy) { float out_len = r; float in_len = r - 5; int deg_list[] = {30, 330, 270, 210}; int deg_list2[] = {30, 330, 270, 210, 150, 90, 30}; // 外側の亀甲 stroke(0, 0, 0); strokeWeight(3); noFill(); drawTortoiseshell(cx, cy, r, deg_list); // 内側の亀甲 strokeWeight(1); drawTortoiseshell(cx, cy, in_r, deg_list2); // 中央文様 drawFlower(cx, cy, 7, 30, 20); } void drawTortoiseshell(float cx, float cy, float r, int[] deg_list) { float ox = ct[deg_list[0]] * r; float oy = -st[deg_list[0]] * r; for (int i = 1; i < deg_list.length; i++) { float nx = ct[deg_list[i]] * r; float ny = -st[deg_list[i]] * r; line(cx + ox, cy + oy, cx + nx, cy + ny); ox = nx; oy = ny; } } void drawFlower(float cx, float cy, int thin, int len, int len2) { noStroke(); fill(0, 0, 0); // 上の葉(90〜270) // 下の葉(-90〜90) beginShape(); for (int i = 0; i < 360; i++) { float x = - st[safe_degrees(i * 2)] * len / thin; float y = ct[safe_degrees(i)] * len; vertex(cx + x, cy + y); } endShape(CLOSE); // 右の葉(-90〜90) // 左の葉(90〜270) beginShape(); for (int i = 0; i < 360; i++) { float x = ct[safe_degrees(i)] * len; float y = - st[safe_degrees(i * 2)] * len / thin; vertex(cx + x, cy + y); } endShape(CLOSE); // 左下の葉(90〜270) // 右上の葉(-90〜90) beginShape(); for (int i = 0; i < 360; i++) { float x = - st[safe_degrees(i * 2)] * len2 / thin; float y = ct[safe_degrees(i)] * len2; float dx = -1 * ONE_DIV_ROOT_2 * (x - y); float dy = -1 * ONE_DIV_ROOT_2 * (x + y); vertex(cx + dx, cy + dy); } endShape(CLOSE); // 右下の葉(90〜270) // 左上の葉(-90〜90) beginShape(); for (int i = 0; i < 360; i++) { float x = ct[safe_degrees(i)] * len2; float y = - st[safe_degrees(i * 2)] * len2 / thin; float dx = -1 * ONE_DIV_ROOT_2 * (x - y); float dy = -1 * ONE_DIV_ROOT_2 * (x + y); vertex(cx + dx, cy + dy); } endShape(CLOSE); } int safe_degrees(int deg) { while (deg >= 360) deg-=360;
while (deg < 0) deg += 360; return deg; } [/p5code] 亀甲模様に花柄がついた、花亀甲という模様です。コードもだいぶ複雑になって来ました。プログラムは太い亀甲を描くルーチン、細い内側の亀甲を描くルーチン、そして花柄模様を描くルーチンの3種類に分かれています。そしてそれらを1つのぱたーんとしてまとめあげて、亀甲の中心点を移動させることでパターン化しています。花柄を描くときやcos()やsin()に係数を掛けて、円を歪ませることで花びらの1枚1枚を描いています。 先ほどの星七宝のコードでも出てきたのですが、私のコードでは、cos()とsin()を0〜359度分、予め計算した状態で配列に確保しています。 これはコードの内部で使用する角度をを0〜359の整数に限定することで、描画中にcos()関数、sin()関数の結果がそれぞれ360通りに限定されるので、予め計算しておくことが出来るようになっています。小さな静止画を描くときはそこまで効力を発揮しないのですが、動く物体を描くときや、大量にcos()、sin()関数を使うときは非常に高速に計算ができます(関数を呼び出して計算させるより、予め計算結果を溜めておいたものを使用したほうが速のです)。

武田菱(たけだびし)

武田氏の家紋である「四つ割菱」の入った模様です。

[p5code]
// 武田菱(たけだびし)

float ROOT_3 = (float)Math.sqrt(3);
float ROOT_3_DIV_2 = ROOT_3 / 2.0;

int ix = -60;
int iy = -60;
int block_w = 100;
int block_h = 60;
float ey = 10;
float ex = ey * ROOT_3;

float ct[] = new float[360];
float st[] = new float[360];

void setup() {
size(400, 400);
background(0, 0, 0);
smooth();
noLoop();

for (int d = 0; d < 360; d++) { ct[d] = cos(radians(d)); st[d] = sin(radians(d)); } } void draw() { background(0, 0, 0); strokeJoin(MITER); boolean flag = true; for (float y = iy; y < height + block_h; y += (block_h + ey) / 2) { float x_base = ix + (flag ? (block_w + ex) / 2 : 0); for (float x = x_base; x < width + block_w; x += block_w + ex) { drawPattern(x, y, block_w, block_h); } flag = !flag; } } void drawPattern(float x, float y, float block_w, float block_h) { float subblock_w = block_w / 2; float subblock_h = block_h / 2; float cx = x + subblock_w; float cy = y + subblock_h; int m = 8; stroke(255, 255, 255); strokeWeight(3); noFill(); for (int i = m; i >= 4; i-=2) {
float w = lerp(0, subblock_w, i / (float)m);
float h = lerp(0, subblock_h, i / (float)m);
quad(
cx, cy – h,
cx – w, cy,
cx, cy + h,
cx + w, cy
);
}
float w = lerp(0, subblock_w, 4 / (float)m);
float h = lerp(0, subblock_h, 4 / (float)m);
line(cx – w / 2, cy – h / 2, cx + w / 2, cy + h / 2);
line(cx + w / 2, cy – h / 2, cx – w / 2, cy + h / 2);
}

int safe_degrees(int deg) {
while (deg >= 360) deg-=360;
while (deg < 0) deg += 360; return deg; } [/p5code] こちらのコードはそこまで複雑ではありません。四角形を描くquad()関数を多用して、上手く菱形を描いています。

おわりに

簡単ではありますが、伝統模様をお見せしました。
これ以外にも伝統模様はたくさんありますので、調べてみて、「この模様はどうやってProcessingで描いたら上手く書けるかな?」なんて考えてみてはいかがでしょうか。伝統模様に限らず、身近なデザインでも「Processingでどうやって書いたらいいかな?」って考えてみると、Processingの腕が上達するかもしれません。

それでは、次回は@reona396さんです。よろしくお願いします!

About: uechoco


One thought on “[processing]伝統模様を描いてみよう(Processing Advent Calendar 2011 – 9日目)”

Comments are closed.