Methods

ここには,Gallery1~5で説明を省略した事項を載せています。数式を用いた記述を含みますが,用いている数学は理工系の知識のある方にとっては初歩的なものだと思います。ご興味のある方はご覧ください。ご意見や間違いの指摘等も歓迎します。

歪んだ同心円の描き方

当工房では「点描」の方法で絵を構成していくことが多いです。それは,当工房では幾何学的な絵よりも人間が描いた古典的な意味での絵に近いものを描きたいと思っているからです。当方で使用しているPythonのtkinterをはじめ,コンピュータで描画する環境には必ずと言っていいほど座標を指定して任意の大きさの円(または矩形等)を描く機能がありますから,これを利用すれば「点(小さい円)を打って絵を構成していく」ことは可能です。ただし,当工房で行っていることは最初から決まった色(知覚させたい色)のついた点を打っていくことだけであり,印象派の画家が行ったような複数の色の点を打ち見る人にその混合色を知覚させるような技法は用いていません。

以下に例として,Gallery 1に載せた「セザンヌのリンゴ」や「アボリジニアート風」の中の同心円を描く方法を説明します。
これは以下のように中心から外側に輪帯を描いていくというやり方です。

(1)同心円の中心の座標を(Xc,Yc)(X_{\mathrm{c}}, Y_{\mathrm{c}})とする。
(2)何層の同心円を描くかを決める。層の数をMMとする。
(3)一つの層の厚みをdrdrとする。
(4)一つの層の周方向に打っていく点(小さい円)の半径の「基準値」を,決める。それをR0R_{\mathrm{0}}とする。
(5)中心からmm番目の層に打つ点の個数NNを,その層の周の長さを点(小さい円)の直径で割った値と決める。N=int(2πr/2R0)N=\mathrm{int}(2πr/ 2R_{\mathrm{0}})となる。ここでint()\mathrm{int}()は整数化を意味し,r=mdrr=mdrである。
(6)打つべき点の座標は(Xc+rcosθ,Yc+rsinθ)(X_{\mathrm{c}}+r\mathrm{cos}θ, Y_{\mathrm{c}}+r\mathrm{sin}θ)となる。ただしθ=ndθθ=ndθdθ=2π/N=2R0/rdθ=2π/N=2R_{\mathrm{0}}/rであり,nn0N0~Nまでの整数である。

同心円を層状に描く場合の各量の関係(drと2Roは等しい必要はない)

以上を,nn0N0~Nまで,mm1M1~Mまで変化させる二重ループに入れれば描画されます。
色は層ごとに(場合によっては点ごとに)RGB値または色名のテキスト(*)で指定します。
通常,描く段階での一つの点の半径RdR_{\mathrm{d}}は,基準値R0R_{\mathrm{0}}とは異なる値に指定します。RdR_{\mathrm{d}}R0R_{\mathrm{0}}とほぼ同じにすれば点描の一つ一つの点が見える絵に,RdR_{\mathrm{d}}R0R_{\mathrm{0}}よりかなり大きくすれば点同士がつながり筆で塗っていった感じの絵になります。
層の厚みdrdrを大きく,層の数MMを小さくすれば粗な同心円となり,その逆にすれば密な同心円となります。

(*)Pythonのtkinterやmatplotlibで用いることのできる定義済み色名(named colors)のことです。

上記で描かれるのは,幾何学的な同心円(真円)です。
これに「手で描いたような味」を加えるために,次のことを行いました。

(A)同心円自体の形にゆるやかな(場合によっては大きな)歪をつける。
(B)(A)によって定まった点をさらに乱数によりごくわずかに動かす。
(C)点(小さい円)の半径も乱数により少しずつ変える。

(A)は,次のように行いました。
同心円を描く場合,その円の半径を次のように変調します。

r(θ)=r(1+f(θ))r(θ)=r(1+f(θ))

ここでrrは真円の半径,f(θ)f(θ)は角度θθによってゆるやかに変わる何らかの関数です。この関数としてはたとえば次のようなものを用います。

f(θ)=Aexp[(θθc)2R2]f(θ)=A\mathrm{exp}[-\frac{(θ-θ_{\mathrm{c}})^2}{R^2}]

これはガウス関数と呼ばれる関数で,θcθ_{\mathrm{c}}を中心として釣り鐘型の曲線をなし,RRはその広がりを,AAはピーク値を決めます。これを1+f(θ)1+f(θ)の形にして真円半径rrに掛け,上記の手続き(6)のrrr(θ)r(θ)に置き換えれば,描く円を歪ませることができます。AAを正にすると引っ張った形,負にすると押し込んだ形となります。

ガウス関数の例(A=1,R=100の場合)
角度により円を歪ませる場合


AAθcθ_{\mathrm{c}}RRはパラメータとして数字で与えます。一つの円に対し複数の歪を与える場合は

fi(θ)=Aiexp[(θθci)2Ri2]f_{i}(θ)=A_{i}\mathrm{exp}[-\frac{(θ-θ_{\mathrm{c}i})^2}{R_{i}^2}]
f(θ)=ifi(θ)f(θ)=\sum_{i}f_{i}(θ)

とします。パラメータAiA_{i}θciθ_{\mathrm{c}i}RiR_{i}は好きな個数だけ好きな値を指定します。これらを乱数で決めるのも一つの方法ですが,当工房では描く図形ごとに一つ一つ数字で指定しており,今までに描いた画像ではそのほうがよい結果が得られると感じています。
歪ませる形を決めるf(θ)f(θ)はガウス関数である必要はありませんが,ガウス関数は変化がゆるやかで値が一定範囲内に収まること,ピーク値AAや広がり幅RRと絵の関係を把握しやすいことなど便利な点が多いので,当工房では多用しています。

以上の説明から判るとおり,この方法は,「点描のステップに比べて大きな長さスケールで変化する関数を用いて描く線の形を変調する」と一般化できるので,同心円だけでなく,いろいろな形を描く際に適用できます。

(B)は,上記(A)によって定まった点(小さい円)の中心座標を,さらにごくわずかだけ乱数によって動かすということです。これは,(A)によってできる大きなスケールでの歪でなく,点1個ごとのわずかな位置のゆらぎを加えるということです。

(C)は,描く点(小さい円)の半径RdR_{\mathrm{d}}も,乱数によって変えていくということです。

(B),(C)により,絵を描く際のチリチリとした手の動き(ぶれ)に近いものができるように思います。(B),(C)はいずれも乱数を用いて行いますが,乱す度合は(B)ではごくわずか,(C)ではそこそこの大きさ,といった程度です。

今までにいろいろ描いてみた経験からは,(A),(B),(C)いずれも効果がありますが,人間が描いた感じを出すのに大きく関わっているのは(A)の「大きなスケールで歪をつける」ということであると感じます。

なお,「アボリジニアート風」の絵の中の一部の図形では点描の点同士がくっつきあっていません。これは,dθ=2π/N=2R0/rdθ=2π/N=2R_{\mathrm{0}}/rと,描く角度ステップdθを真円の半径rrを用いて定義しているからです。歪ませた後の局所的周長から角度ステップを決めればくっつき合わせることも可能ですが,離れていた方が面白いと感じましたのでそのままとしてあります。

塗り残し・貼り残しをなくする

「小領域法」は平面を多数の領域に分け,各領域に異なる色や画像を配置する方法です。
その方法の大筋はGallery 2で説明した通りですが,実はある「問題」が発生することがあります。
たとえば,下に示した2種の枠絵に対して処理をする場合を考えます。
各小領域の中には色を付けた小さな点が打ってあります。この点が,色を塗ったり別の画像を貼り付ける場合の「起点」となります。
Gallery 2で説明した「塗り」または「貼り」の処理を1回だけ作用させると,「うまくいく場合」と「うまく行かない場合」が出てきます。
うまく行く例が図Aの枠絵の場合で,これの各起点から縦・横に塗りの処理を施すと図Bのような絵が出来上がります。
うまく行かない例は図Cの枠絵の場合で,同じ処理をしても結果は図Dのようになります。これは,領域の輪郭が湾曲しているため,領域内の起点から一度だけ縦・横に塗って行っただけでは塗り残される部分が発生するためです。回り込んで塗って行かないと領域内を全部塗れないような枠絵でこの問題が発生します。図Aの枠の輪郭は単純で水平または垂直に近いので,このような問題は起こりません。

A 比較的単純な枠絵
B 左の枠絵を1回塗った結果
C 少し複雑な枠絵
D 左の枠絵を1回塗った結果

この「塗り残し」を回避するため,小領域法では実際には次の過程を含んだ処理を行っています。

(1)塗りのチェックを行う
領域内の起点から一度だけ縦・横に塗った後で「塗り残し」がある場合,次のようなピクセルが存在しています。
①あるピクセル[i][j]は塗られているがその右隣のピクセル[i+1][j]は塗られていない。
②あるピクセル[i][j]は塗られているがその左隣のピクセル[i-1][j]は塗られていない。
③あるピクセル[i][j]は塗られているがその上のピクセル[i][j-1]は塗られていない。
④あるピクセル[i][j]は塗られているがその下のピクセル[i][j+1]は塗られていない。

注)Pythonのtkinterではi軸は画面の右方向,j軸は画面の下方向が正の方向です。

1回目の「塗り」を行った後,画像内の全ピクセルに対して,上記①~④のようなピクセルがあるかどうかをチェックします。もしあった場合,次の処理を行います。

(2)追加の塗りを行う
①に対して:ピクセル[i+1][j]から右方向にピクセル[i][j]と同じ色を塗っていき,境界が来たら止まる。
②に対して:ピクセル[i-1][j]から左方向にピクセル[i][j]と同じ色を塗っていき,境界が来たら止まる。
③に対して:ピクセル[i][j-1]から上方向にピクセル[i][j]と同じ色を塗っていき,境界が来たら止まる。
④に対して:ピクセル[i][j+1]から下方向にピクセル[i][j]と同じ色を塗っていき,境界が来たら止まる。

上記の処理を行うためには,次のような配列を用意しておくことが必要です。

Q[i][j]:ピクセル[i][j]に色を塗ったかまだ塗っていないかを記憶する配列
W[i][j]:ピクセル[i][j]に塗った色または画像を記憶する配列

たとえば,次のようにします。

最初はQ[i][j]=0と初期化する。
1回目の塗りで色を塗ったピクセルに対してはQ[i][j]=1と書き換える。
同時に,そのピクセルのW[i][j]の値として「何らかの色・絵の情報」を与える。
「何らかの色・絵の情報」とは,たとえば単色であればその色名またはRGB値,別の画像(元絵)であればその画像を示す変数名などです。
塗りのチェックによってこの二つの情報を記録した後,2回目の塗り(追加の塗り)を行います。これは具体的には,Q[i][j]=1であるピクセルの座標[i][j]を起点として,W[i][j]に記憶されている色または画像で塗り残した部分を上述した方法で塗っていきます。

たとえば,図Dに対して塗りのチェックを行ってみます。たとえば,図E内の白矢印の領域であれば,図中で①~④と示した線上のピクセルが上記①~④の性質を持つピクセルであることが判ります。他の領域にも同様のピクセルが沢山あります。この画像に追加塗りの処理を行うと,図Fのように,領域内すべてが塗られた画像が出来上がります。

E 塗りのチェックを行った結果
F 追加の塗りを行った結果

上記の「塗りチェック」と「追加塗り」は,必要な回数だけ行います。
枠絵の境界線が非常に込み入った形をしていると,一度の追加塗りではまだ塗り残しがある場合も発生します。枠の複雑さに応じて必要な回数だけ塗りチェックと追加塗りを行うことになります。

以上,文章で書くと煩雑ですが,追加塗りの部分までを含んだ処理を一つのプログラム内にコード化しているので,プログラムは一度動作させるだけで完全に塗られた画像が出来上がります。

フィールド法に用いる関数とその配置法

フィールド法は,多数の「山」を配置して凸凹の面(フィールド)を作り,それに基づいて境界線を作っていく方法です。その順序についてはGallery 2で説明しました。
ここでは特に,配置する山の関数の数式表現と配置法について説明します。

フィールド(多数の凹凸を持つ2次元パターン)を作るために配置する山の形としては,やはり中央で最大値をとり,周囲に向かってなだらかに減少していく形が望ましく,次の2次元ガウス関数はこの条件を満たします。

f(x,y)=Aexp[x2+y2R2]f(x, y)=A\mathrm{exp}[-\frac{x^2+y^2}{R^2}]

この関数はxxyyに対して同じ依存性を持ち,中心対称の形をしています。
引数内の分母のRRは裾野の広がりを決めるパラメータで,RRが大きければ裾野は広く,小さければ狭くなります。
フィールド法ではこの山を多数の場所に配置します。配置する中心点を(Xm,Ym)(X_{\mathrm{m}}, Y_{\mathrm{m}})とすると,実際に用いるのが次式となります。ここで(Xm,Ym)(X_{\mathrm{m}}, Y_{\mathrm{m}})は,フィールドを作っている途中の各段階における,平面内でフィールドが最小値を取る座標(次の山を置く座標)です。

f(x,y)=Aexp[(xXm)2+(yYm)2R2]f(x, y)=A\mathrm{exp}[-\frac{(x-X_{m})^2+(y-Y_{m})^2}{R^2}]

この関数は,「異方性のない」セルの集合を作る際に用います。個々のセルは角ばっているが,特にどこかの方向に伸びているとか,何か方向性を持って配置されているとか,そういうことのないセル構造です。
この関数で変えることのできるパラメータはRRAAです。

パラメータRRは裾野の広がりを決めますが,一般に次のことが言えます。
RRを隣り合う山の平均的な距離よりずっと小さくすると,境界線は直線となる。できるセル構造はボロノイ図に近いものとなる。これは,隣り合う二つの山の間の領域の関数の値はその二つの山が持つガウス関数の寄与が支配的で,それ以外の山からの寄与はごく小さいからです。それ以外の山からの寄与をゼロとみなした場合に,この二つの山の間の境界線は山の頂点を結ぶ線分の垂直二等分線となるので,できる構造はボロノイ図となります。
RRを隣り合う山の平均的な距離と同程度とするか,大きくすると,境界線が曲線となったり,山の間に複数の境界線が出来たりする。これは,考えている二つの山の間の領域に,その二つの山以外の周辺の山がつくる値が寄与してくるからです。

RRはすべての山で同じ値にすることも,山ごとに変えることもできます。
RRをすべての山で同じにすると,平均的には同じ大きさのセルでできた構造が作られます。
山ごとに変えると,それぞれの山の「勢力」が変わってくるので,セルの大きさにばらつきがあるような構造ができます。

パラメータAAははガウス関数の頂点の高さを決める量ですが,これを山ごとに変えることもできます。そうするとやはり,セルの大きさにばらつきがあるような構造ができます。

この山を「細長い」形にするには次式を用います。

f(x,y)=Aexp[(x2Rx2+y2Ry2)]f(x, y)=A\mathrm{exp}[-(\frac{x^2}{R_{x}^2}+\frac{y^2}{R_{y}^2})]

ここでRxR_{x}xx方向への裾野の広がり,RyR_{y}yy方向への裾野の広がりを決めるパラメータです。

この関数をそのままフィールドを作る際の(Xm,Ym)(X_{\mathrm{m}}, Y_{\mathrm{m}})に配置していってできるのが,たとえば下の図Cの画像です。単純にxx方向に長く,yy方向に短いセルの集合ができています。

これだけでは面白くないので,この細長い山を回転して配置することを考えます。
山の原型として下図Aのf(x,y)f(x, y)が与えられたとします。この関数を原点を中心にして半時計回りにθだけ回転した山の形を表す関数をfr(x,y)f_{r}(x, y)と書きます。fr(x,y)f_{r}(x, y)f(x,y)f(x, y)の間には次の関係があるはずです。

fr(x,y)=f(x,y)f_{r}(x, y)=f(x’, y’)

x=cosθx+sinθyx’=\mathrm{cos}θ x+\mathrm{sin}θ y

y=sinθx+cosθyy’=\mathrm{-sin}θ x+\mathrm{cos}θ y

これは原点を中心にした関数形なので,これを(Xm,Ym)(X_{\mathrm{m}}, Y_{\mathrm{m}})を中心に配置するにはfr(xXm,yYm)f_{r}(x-X_{\mathrm{m}}, y-Y_{\mathrm{m}})を用いればよいことになります。

問題は角度θの決め方です。
ある目標としているパターン(絵)が先にあり,そのパターンを細長いセルで表現するために山をどのように配置すればよいかということを考えます。つまり,描きたい絵柄があるのだが,それを筆で描くのではなく,細長いセルの集合で描いてみたい,という場合です。
簡単のため,円形のパターンを描く場合について考えます。
実現したい円形のパターンの円の中心を(Xc,Yc)(X_{\mathrm{c}}, Y_{\mathrm{c}})とし,山を配置したい点を(Xm,Ym)(X_{\mathrm{m}}, Y_{\mathrm{m}})とします。(Xc,Yc)(X_{\mathrm{c}}, Y_{\mathrm{c}})を中心とし(Xm,Ym)(X_{\mathrm{m}}, Y_{\mathrm{m}})を通る円は次式で表されます。

(xXc)2+(yYc)2=(XmXc)2+(YmYc)2(x-X_{c})^2+(y-Y_{c})^2=(X_{m}-X_{c})^2+(Y_{m}-Y_{c})^2

(Xm,Ym)(X_{\mathrm{m}}, Y_{\mathrm{m}})における円の接線の傾きは

p=(XmXc)/(YmYc)p=-(X_{\mathrm{m}}-X_{\mathrm{c}})/(Y_{\mathrm{m}}-Y_{\mathrm{c}})

となりますから,このppに対し

θ=atan(p)θ=\mathrm{atan}(p)

とすれば配置点(Xm,Ym)(X_{\mathrm{m}}, Y_{\mathrm{m}})において回転すべき角度θが求まります。

A 原点に細長い山を配置し,角度θだけ回転し,別の場所に移動する
B 細長い山を円周上,円の接線方向に配置する

以上は円形のパターンを作る場合で,これを用いると図Dのようなパターンができます。同様の考え方で任意のパターンを作ることが可能で,たとえば波の形なら正弦関数を用いればよく,図Eのようなパターンができます。

C 細長いセルを水平に配置
D 細長いセルを円周状に配置
E 細長いセルを波状に配置

他のもろもろの関数や,あるいはきれいな数式で書けないパターンであっても,任意の座標での値の微分を求めることができれば,上記の考え方でパターンを作ることが可能です。
別の言い方をすれば,「遠目に見たときに何かが流れているような形」を関数または数字の集合で表現すればよく,これは流体力学における流線のようなものです。ある点における流線の接線を求めれば,その点において細長い山を配置すべき角度が決まります。これを平面上の多くの点で行えば,「何かが流れているような形」を細長い有限個のセルで絵画的に表すことができます。

タイトルとURLをコピーしました