
ジェネラティブアート作成時の頭のなか #AltEdu2022 13 日目
今日のお題は、他の人と話してコードを書きましょう、とのこと。
明日2/1(火)から開催される「#AltEdu2022」は2022年の2月中,毎日お題に合わせてクリエイティブコーディングに関わる活動をわいわい行う、コーディングチャレンジのお祭りです.毎日のお題はコチラ!https://t.co/bLzPfoLyqW pic.twitter.com/GTRdeT6rTT
— Processing Community Japan (@PCD_Tokyo) January 31, 2022
娘が隣で GarageBand で遊んでいるので、今日のお題の趣旨を説明して、さてどうしようかと言っていると、昨日の続きの時計が良いとのこと。実は昨日、少し Processing でのプログラミングを説明していて、娘の希望を聞きながら時計のコードを書いていた。下のプログラムは、その時計から派生したものである。画面上部の左の方に 12 と書かれているのはその名残である。
def setup():
size(800,500)
colorMode(HSB,360)
def draw():
if frameCount%360==1:
background(360,0,360)
t=frameCount%360/180.0*PI
R=100+frameCount%200
p=R/2*cos(t)+400
q=R/2*sin(t)+250
stroke(0,360,360)
line(p,q,400,250)
noFill()
circle(400,250,R)
fill(0)
text('12',250,15)

時計でも良いけど、昨日と同じになるのもちょっと…ということで、他のが良いと伝えると、それなら動物が良いとのこと。ペンギンとか、らしい。#つぶやきProcessing でペンギンを描くのは難しそうだな、と思っていたら気を利かせてコップでも良いよ、という話になる。
コップかペンギンか。コップはすぐに書けそうなので、今日はペンギンにチャレンジしてみよう。ペンギンと言えばクールミントガムのシルエットのようなペンギンが思い浮かんだ。さっそく書いてみよう。
とは言うものの、ペンギンのシルエットの曲線をどう書いたものか。curve や bezier で繋いでいっては座標値だけで 280 文字を使い切ってしまいそうだ。悩んでいると、ペンギンのシルエットの曲線がシグモイド関数で表現できそうだと、ふと思いついた。
シグモイド曲線とは人工神経回路網 - ニューラルネットワークで使われる関数である。y=1/(1+exp(-x)) で表現され、S 字型なのでシグモイドと呼ばれている(確か)。これを活用すればペンギンのシルエットは書けそうだ。
size(500,500)
for i in range(-50,50):
y=100/(1+exp(-i*.1))
circle(i+200,y+100,5)

上の図形は係数を少し調整したものである。なかなか良さそうだ。あとはこの曲線を 2 つ使って、挟まれた領域を塗りつぶすコードを書けばペンギンのシルエットになる…気がする。早速書いてみよう。
コードを書くに辺り、要はスキャンライン単位で塗りつぶす必要があるので、x を変動させるのではなく、y 方向を変動させて x を求めたい。私は、こちらの方がプログラムを書きなれているからだ。というわけで、y=1/(1+exp(-1)) の式を変形をして、x=log(y/(1-y)) という式を得る。
size(500,500)
for i in range(5,95):
y=i*.01
x=log(y/(1-y))
v=y*150+100
line(x*5+150,v,x*20+200,v)
line(x*5+150,v+1,x*20+200,v+1)

…ペンギンというようりもオットセイのようなシルエットになってしまった。シグモイド関数のグラフをプロットしている時に、なんとなく「もしかして」という予感はあったけれども、予感通りオットセイっぽくなってしまった。まあでも、これも一興と思い、今日はこれを作品に活用していこう。
ペンギン…じゃなかった、オットセイも 1 体だけよりも、何体もあった方が楽しそうなので、このシルエットを描画する処理を関数化する。関数化するにあたり、位置やスケールなども指定できるようにする。そしてこの関数を 2 度呼び出して、2 匹描画してみる。
def D(p,q,s):
for i in range(5,95):
y=i*.01
x=log(y/(1-y))
v=y*150*s+q
line(x*5*s+p-50*s,v,x*20*s+p,v)
line(x*5*s+p-50*s,v+s,x*20*s+p,v+s)
size(500,500)
D(150,200,1)
D(200,280,.5)

ここで一度娘に確認してもらうと、ペンギンはもちろんのことオットセイにも見えない、とのこと。そばに居た妻も同意見。やっぱりコップの方が良いのではという助言ももらうが、私の感覚ではそんなに悪くはないと思うのでこのままでゆく。今日のお題は「話しながら」であり、「他人の言うことを聞く」ではないので、これで良いのだ。
画面の比率や色などを調整して、当初思いついたクールミントガムのような絵に仕立てる。
def D(p,q,s):
for i in range(5,95):
y=i*.01
x=log(y/(1-y))
v=y*150*s+q
line(x*5*s+p-50*s,v,x*20*s+p,v)
line(x*5*s+p-50*s,v+s,x*20*s+p,v+s)
size(500,200)
B=[50,80,150]
background(*B)
stroke(-1)
D(400,50,1)
D(450,100,.5)
fill(-100)
circle(90,80,80)
fill(*B);noStroke()
circle(110,70,80)

いい感じなってきたので、これを#つぶやきProcessing 化してみる。まず、line 関数を 2 度呼んでいるのをなんとかしよう。これは、i の値が 1 づつ増えており、y の計算式により y は 0.01 づつ増加する。スケール指定を担う引数 s が 1 の場合(つまり、大きなオットセイを描く時は)この y の値を 150 倍した v の値が水平線の垂直方向の位置になる。この v の増加量は 0.01*150 なので 1.5 づつ増加することになり、2 回のループで 3 増えるため隙間が空いてしまう。
そこで、ループの範囲を 10 倍とし、y=i*0.001 とする。これで line 関数の呼び出しは 1 回で済むようになる。また、x*5*s という項も複数回出てきているので、これも直接計算して活用することにする。
def D(p,q,s):
for i in range(50,950):
y=i*.001
u=5*s*log(y/(1-y))
v=y*150*s+q
line(u+p-50*s,v,u*4+p,v)
size(500,200)
B=[50,80,150]
background(*B)
stroke(-1)
D(400,50,1)
D(450,100,.5)
fill(-100)
circle(90,80,80)
fill(*B);stroke(0,0)
circle(110,70,80)
あとはインデントを削除して、tweet 欄に貼り付けてみると、1 文字多い。そこで fill(-100) を fill(-99) として、無事、タグ込みで 280 文字に収めることができた。
#つぶやきProcessing#AltEdu2022
— Koji Saito (@KojiSaito) February 13, 2022
def D(p,q,s):
for i in range(50,950):y=i*.001;u=5*s*log(y/(1-y));v=y*150*s+q;line(u+p-50*s,v,u*4+p,v)
size(500,200)
B=[50,80,150]
background(*B)
stroke(-1)
D(400,50,1)
D(450,100,.5)
fill(-99)
circle(90,80,80)
fill(*B);stroke(0,0)
circle(110,70,80) pic.twitter.com/X2jOjj97Nf