クリエイティブ・コーディングで、間違いも作品にしてしまおう

2021年6月21日月曜日

Processing 作例 静止画 中級者向け

t f B! P L
A bent shape like a peacock.

コードの間違いで生まれた形

四角形を何がしかの規則で並べて面白いものができないかなと試していました。

A generative art with rim rectangles.

幅と高さ、色に規則性をもたせた小さい四角形をグリッド状に並べるのもいいなと思い、この方向で制作を進めてみました。

A generative art with small colored rectangles pattern.
(今これを見ると、なんだかリズムが感じられ、これで完成でもよかった気もしてきます)

その試行錯誤の途中で間違えて偶然できた形がこれです。


コードはこう書いてしまったのですが、


pushMatrix();
rotate(r);
rect(x, y, w, h);
popMatrix();


本当は個々の四角形を斜めにしようと、本来ならこのように書くべきでした。


pushMatrix();
translate(x, y);
rotate(r);
rect(0, 0, w, h);
popMatrix();


やろうとしたことより偶然出来た形の方が面白かったので、'rotate(r)' の 'r' を求める式をいろいろと変えて試し、今回は下記 3つを採用してそれぞれをクラスとして実装しました。

private class Peacock implements DrawingWay {

A bent shape like a peacock.
Peacock.

private class Concave implements DrawingWay {

A bent shape like a concave.
Concave.

private class Flag implements DrawingWay {

A bent shape like a flag.
Flag.

今回は背景にも凝ってみました。


  • 前景の歪んだ曲線との対比として直線の格子を描画。
  • 中央に置いた形の存在感を出すために上下と中央で明度に差をつけました。これにより格子の線の明るさが中央では暗く、上下端では明るく見えるようになりました。
  • 中央の形を歪ませた何かしらの力をノイズ・フィールドで表現。

背景と前景は順番に描画するだけでもよいのですが、今回は後ろが透ける効果を入れたくて、背景と全景を別々に PGraphic で作ってレイヤー的に重ねました。


実は透けている

アクシデンタル・プログラミング・メソッド

'rotate()' と 'translate()' の組み合わせはちょいちょい間違えることがあり、今回のように間違ってしまったほうが面白い結果が出ることもままあります。

そこで、わざと 'translate()' を抜いたりとか、'pushMatrix()/popMatrix()' を入れなかったりすることにより、間違いから作品を作るということを行うようになり、私はこれを 'Accidental Programming Method' と呼び、偶然の間違いからの創造ではなく、クリエイティブ・コーディングにおいての確立された手法での必然とすることを目指しています。

これについてはまたおいおい。

"Processing" による作例コード

GPL で公開します。どうぞご自由にお使いください。このコードを利用して何か作品を作ってもらえるととても嬉しいです。

本コードはスクリーン上には何も描画しません。描画の結果は静止画のファイルとして保存されます。


/**
 * Above the Clouds.
 * Bending a meaningless symmetrical dot pattern.
 *
 * @author @deconbatch
 * @version 0.1
 * @license GPL Version 3 http://www.gnu.org/licenses/
 * Processing 3.5.3
 * 2021.04.24
 */

void setup() {
  size(980, 980);
  colorMode(HSB, 360.0, 100.0, 100.0, 100.0);
  smooth();
  noLoop();
}

void draw() {

  // class of the parameters to draw foreground image
  ArrayList<DrawingWay> dws = new ArrayList();
  dws.add(new Peacock(width, height));
  dws.add(new Concave(width, height));
  dws.add(new Flag(width, height));
  
  int imgCnt = 0;
  for (DrawingWay dw : dws) {

    image(getBackground(width, height), 0.0, 0.0);
    image(getForeground(width, height, dw), 0.0, 0.0);
    casing();

    imgCnt++;
    saveFrame("frames/" + String.format("%04d", imgCnt) + ".png");

  }
  exit();
}

/**
 * getBackground : returns noise field + matrix image
 */
private PImage getBackground(int _w, int _h) {

  PGraphics p = createGraphics(_w, _h);
  p.beginDraw();
  p.colorMode(HSB, 360.0, 100.0, 100.0, 100.0);
  p.background(0.0, 0.0, 90.0, 100.0);

  // noise marble
  float noiseStep = 0.025;
  int   stepSize  = 2;
  p.noStroke();
	for (int x = 0; x < width; x += stepSize) {
		float nX = x * noiseStep;
		for (int y = 0; y < height; y += stepSize) {
      float yRatio = map(y, 0, height, 0.0, 1.0);
			float nY = y * noiseStep;
			float nVal = noise(nX, nY, noise(nX, nY) * 10.0);
			float nBri = 50.0 + 20.0 * sin(PI * yRatio) + 20.0 * nVal;
			p.fill(0.0, 0.0, nBri, 100.0);
      p.rect(x, y, stepSize, stepSize);
		}
	}

  // matrix
  int divNum    = 20;
  int divSizX   = floor(width / divNum);
  int divSizY   = floor(height / divNum);
  int selection = floor(random(divNum));
  p.noFill();
  p.strokeWeight(2.0);
  for (int x = 0; x < divNum; x++) {
    for (int y = 0; y < divNum; y++) {
      if ((x * y + x + y) % divNum == selection) {
        p.stroke(0.0, 0.0, random(70.0, 80.0), 100.0);
        p.line(x * divSizX, 0, x * divSizX, height);
        p.line(0, y * divSizY, width, y * divSizY);
      }
    }
  }

  p.endDraw();
  return p;
}

/**
 * getForeground : returns foreground image with the parametes of _dw
 */
private PImage getForeground(int _w, int _h, DrawingWay _dw) {

  int   divNum   = _dw.getNumber();
  int   divSiz   = _dw.getSize();
  int   shapeSiz = divSiz * divNum;
  float rimWidth = min(_w, _h) * 0.04 / divNum;
  float pattern  = random(1.0, 2.0);
  float hueBase  = random(360.0);
  
  PGraphics p = createGraphics(_w, _h);
  p.beginDraw();
  p.colorMode(HSB, 360.0, 100.0, 100.0, 100.0);
  p.rectMode(CENTER);

  p.blendMode(REPLACE);
  p.background(0.0, 0.0, 0.0, 0.0);
  p.pushMatrix();
  p.translate(width * 0.5, height * 0.5);
  _dw.rotateCanvas(p);
  p.noStroke();
  for (int x = -shapeSiz; x <= shapeSiz; x += divSiz) {
    for (int y = -shapeSiz; y <= shapeSiz; y += divSiz) {

      float xRatio = x * 1.0 / shapeSiz;
      float yRatio = y * 1.0 / shapeSiz;
        
      float s = abs((((abs(x * x) + abs(y * x)) / divSiz) % pattern) / pattern);
      float t = abs((((x * y + y) / divSiz) % pattern) / pattern);

      float theta  = PI * s;
      float radius = shapeSiz * t;
			
      float rX   = radius * cos(theta);
      float rY   = radius * sin(theta);
      float rHue = hueBase + s * 60.0 + t * 60.0 + xRatio * yRatio * 90.0;

      float sizMin  = divSiz / 20.0 + abs(PI * radius / shapeSiz);
      float wdPlate = constrain(s * divSiz, sizMin, divSiz);
      float htPlate = constrain(t * divSiz, sizMin, divSiz);
      float wdRim   = abs(wdPlate - divSiz * 0.5);
      float htRim   = abs(htPlate - divSiz * 0.5);
      float sizDot  = sizMin * 1.75;

      p.pushMatrix();
      _dw.rotateParts(p, x, y);
      // plate
      p.noStroke();
      p.fill(rHue % 360.0, 40.0, 60.0, 100.0);
      p.rect(x, y, wdPlate, htPlate);
      // rim
      p.stroke((rHue + 60.0) % 360.0, 80.0, 30.0, 100.0);
      p.strokeWeight(rimWidth);
      p.fill(0.0, 0.0, 0.0, 0.0);
      p.rect(x, y, wdRim, htRim);
      // dot
      if (random(1.0) < 0.2) {
        p.stroke((rHue + 30.0) % 360.0, 80.0, 30.0, 100.0);
        p.strokeWeight(random(1.0, 2.0) * rimWidth);
        p.fill(rHue % 360.0, 90.0, 30.0, 100.0);
        p.ellipse(x, y, sizDot, sizDot);
      }
      p.popMatrix();
    }
  }
  p.popMatrix();

  p.endDraw();
  return p;
}

/**
 * casing : draw fancy casing
 */
private void casing() {
  float w = min(width, height) * 0.05;
  fill(0.0, 0.0, 0.0, 0.0);
  strokeWeight(w + 4.0);
  stroke(0.0, 0.0, 0.0, 100.0);
  rect(0.0, 0.0, width, height);
  strokeWeight(w);
  stroke(0.0, 0.0, 100.0, 100.0);
  rect(0.0, 0.0, width, height);
}

/**
 * DrawingWay : drawing parameters.
 */
interface DrawingWay {
  void rotateCanvas(PGraphics _p);
  void rotateParts(PGraphics _p, float _x, float _y);
  int  getNumber();
  int  getSize();
}

private class Peacock implements DrawingWay {
  float w, h;
  Peacock(float _w, float _h) {
    w = _w;
    h = _h;
  }
  void rotateCanvas(PGraphics _p) {
    _p.rotate(-PI * 0.25);
  }
  void rotateParts(PGraphics _p, float _x, float _y) {
    _p.rotate((_x + _y) * PI * 0.0005);
  }
  int  getNumber() {
    return 7;
  }
  int  getSize() {
    return 42;
  }
}

private class Concave implements DrawingWay {
  float w, h;
  float rndParts;
  Concave(float _w, float _h) {
    w = _w;
    h = _h;
  }
  void rotateCanvas(PGraphics _p) {
    // nop
  }
  void rotateParts(PGraphics _p, float _x, float _y) {
    _p.rotate(sin(PI * (_x * _y / w / h)) * PI * 0.08);
  }
  int  getNumber() {
    return 16;
  }
  int  getSize() {
    return 22;
  }
}

private class Flag implements DrawingWay {
  float w, h;
  Flag(float _w, float _h) {
    w = _w;
    h = _h;
  }
  void rotateCanvas(PGraphics _p) {
    _p.rotate(random(-0.01, 0.01) * PI);
  }
  void rotateParts(PGraphics _p, float _x, float _y) {
    _p.rotate((cos(_x * PI / w) + sin(_y * PI / h)) * PI * 0.06);
  }
  int  getNumber() {
    return 8;
  }
  int  getSize() {
    return 39;
  }
}


/*
Copyright (C) 2021- deconbatch

This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 3 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program.  If not, see <http://www.gnu.org/licenses/>
*/

QooQ