はじめに
この作品は 2020 年 4 月 1 日から 5 月 14 日にかけて開催された、 #dailycodingchallenge の 5 月 2 日のお題「ミキサー」についての作品です。
お題:ミキサー
— Koji Saito (@KojiSaito) May 2, 2020
40 文字程オーバーで、つぶやきProcessing できず。
code golf の技術も向上してきているので、過去作品をシェイプアップして、新作が作れるかと思いましたが、ダメでした(if 文の除去とか頑張ったのに…)。
このコードも順次 web で公開する予定です。#dailycodingchallenge https://t.co/0XXltaXqmc pic.twitter.com/QIrbuY4YNy
ソースコードおよび解説
この作品は見ての通り、赤と青の 2 つの色で色分けされた粒子が 動きつつ回転する、という作品です。
構成要素としては、
- ノイズによる運動
- 色付け
- 回転
- 残像効果
- ぼかすための全画面ブラー
に分けられます。
色付け以外の部分については、こちらの作品で実現しているものです。
#つぶやきProcessing 解説編
— Koji Saito (@KojiSaito) February 23, 2020
描画モードを blend にして、透明度の高い黒色で全画面を塗りつぶしつつ、描画を繰り返しています。そのため、過去に描いたものほど黒くなっていき、フェードアウトしていきます。それ以外はノイズで適当に位置を決め(x,y)、関数 g で回転処理を行っています。 https://t.co/GVUoN5fGxR
今回の作品のソースコードは以下のようになります:
M=500
def setup():
size(M,M)
noStroke()
background(-1)
G=lambda p,q,t:cos(t)*p+sin(t)*q+M/2
N=noise
C=[255,50,20] # 色指定のための配列
def draw():
t=frameCount*.03
fill(-1,5);
rect(0,0,M,M);
for i in range(98):
C[0],C[2]=C[2],C[0] # 色の入替え処理
fill(*C)
x=(N(i,t/2)-.5)*M;
y=(N(t/2,i)-.5)*M;
circle(G(x,y,t),G(y,-x,t),20)
filter(BLUR,3)
色の入替え処理
現在処理中の粒子(パーティクル)の色は帯域変数 C に格納されています。 初期値は C=[255,50,20] であり、つまり (R,G,B)=(255,50,20) の赤色です。
C[0],C[2]=C[2],C[0]
で R チャネルの値と B チャネルの値を入替えています。 つまり、この処理を一度行うと、C の値は C=[20,50,255] となります。
当然、再度この入替え処理を行うと、C=[255,50,20] と初期値に戻り、 結果として
fill(255,50, 20)
fill( 20,50,255)
fill(255,50, 20)
fill( 20,50,255)
:
と赤色と青色が繰り返し指定されることとなります。
ひとつの関数で行う回転処理
各パーティクルを角度 t にて回転させることで、全体的に回転させています。
点 (x,y) を点 (a,b) を中心に t だけ回転させた座標 (x’,y’) は
$$ \left( \begin{array}{c} x’ \newline y’ \end{array} \right) = \left( \begin{array}{cc} \cos t & -\sin t \newline \sin t & \cos t \end{array} \right) \left( \begin{array}{c} x \newline y \end{array} \right) + \left( \begin{array}{c} a \newline b \end{array} \right) $$
で表されます。
これを行列を使わずに書き下すと次のようになります:
$$ \left\{ \begin{array}{l} x'=\cos(t) x - \sin(t) y +a \newline y'=\sin(t) x + \cos(t) y +b \end{array} \right. $$
code golfing のため、この式を変形すると次のようにも書き表せます:
$$ \left\{ \begin{array}{l} x'=\cos(t) x + \sin(t) \cdot (-y) +a \newline y'=\cos(t) y + \sin(t) x +b \end{array} \right. $$
これはつまり、関数 G を次のように定義すると、
$$ G(p,q,t,c) = \cos(t) p + \sin(t) q + c $$
(x’,y’) は次のように書けます:
$$ \left\{ \begin{array}{l} x'=G(x,-y,t,a) \newline y'=G(y,x,t,b) \end{array} \right. $$
Processing で反時計回りにする場合
実際のソースコードでの使用法をみてみると、 関数 G の実装は同じ(a=b=M/2)であるにも関わらず、 circle 関数の引数として使用されているのは、
circle(G(x,y,t),G(y,-x,t),20)
であり、いわば
$$ \left\{ \begin{array}{l} x'=G(x,y,t,M/2) \newline y'=G(y,-x,t,M/2) \end{array} \right. $$
となっており、上で示した方法とは異なっています。
これは Processing の座標系が数学でよく使う座標系と異なり、 下の方に y 軸のプラス方向が向いているからです。
まず話を簡単にするために、回転の中心を原点とします (説明をした後に、+b だけ並行移動させます)。
これまでの議論の結果を利用するためには、 まず回転処理を行う入力としての (x,y) を、 y 軸方向だけ反転させる必要があります。
つまり、回転処理を行うための座標を (x,y) ではなく (x,-y) とする必要があります。
で、計算の結果得られる (x’,y’) ですが、 これも数学での座標系においての位置ですので、 得られた (x’,y’) も y 軸方向に反転して (x’,-y’) としなければ Processing において意図した位置にはなりません。
原点を中心とする(つまり b=0 とした場合の) y’ の計算式は、
$$ y'=\cos(t) y + \sin(t) x $$
でしたので、Processing の座標系に合わせるためには、 y と y’ をそれぞれ -1 倍したものとします。 すると、
$$ -y'=\cos(t) (-y) + \sin(t) x $$
という式が得られます。
この式の両辺に -1 を掛けて、整理すると
$$ y'=-\cos(t) (-y) - \sin(t) x = \cos(t)y+\sin(t)(-x) = G(y,-x,t,0) $$
を得ます。
この作品では、最終的に点 (M/2,M/2) を中心に回転させていますので、 結局
$$ y'=G(y,-x,t,M/2) $$
となります。
x’ の計算では、G の引数として -y を利用するだけで良いので、 結果として、ソースコードの形となります。