NatureOfCodeの遺伝的アルゴリズムをすこし関数型プログラミングのように実装

NatureOfCodeのサンプルは、初心者にもわかりやすいif文やfor文が使われている文法ですが、最近のモダンな言語はmap/reduceのように関数型プログラミングの文法やAPIを取り入れているので、JavaScriptでまずはfor文を使わない実装を試してみました。

実装する前は、for文をforEachやmapに置き換えるのは大変と考えていましたが、実際にやってみると特に大きな躓きはありませんでした。関数型プログラミングに対する否定的な先入観が大きかったようで、実践的なプログラミングで試してみてそれが大きな間違いだと気づきました。

また、以下を少し意識するだけで、此処の関数がテストしやすくなり実践的なプログラミングで関数型プログラミングの良さを感じられました。

  • メソッドチェーン
  • 小さな関数に分割していく
  • constキーワードによる変数の定数化

以前F#を勉強したときは、あくまでも関数型プログラミングの勉強で演習問題を解くだけだったのですが、新しいプログラミングを勉強した際には、まとめとして実践的なプログラミングを書くことも大切なのを痛感しました。

p5.jsで2Dパーリンノイズを動かすにはコツがいります。

書籍Nature of Codeを買ったので最初の章をp5.jsで試してみました。2Dパーリンノイズを作ってみる課題があったのですが、書籍の書き方ではうまくいきませんでした。githubのサンプルコード(https://github.com/shiffman/The-Nature-of-Code-Examples-p5.js/tree/master/chp00_introduction)にも、サンプルはありませんが、Youtube動画でコーディングしていました。

See the Pen 2D perlin noise p5.js by dev001hajipro (@dev001hajipro) on CodePen.0

考え方

1.単純な二次元配列の要素の取得を考える。

横幅(width)を100px、縦幅(height)を100pxで考えると、2重のfor文で二次元配列の座標(X,Y)を求める事ができます。これはよくある実装方法なので難しくないです。

2.一行はRGBAが並んでいる事を考慮する

p5.jsのpixels配列は、1つの要素にRGBAがオブジェクトとして含まれるのではなく、R、G、B、A、がpixels配列の要素で連続で並んでいます。例えば、横幅5pixelで、縦幅2pixelの場合は、以下のような感じです。

このため、ピクセル単位で処理をしたい場合は、4単位で移動させるように、for文のステップ数を変更します。

3.画像は通常一次元配列

私たちはXY座標で操作するほうが簡単ですが、画像データは一次元配列でpixels配列も同様です。そのため、(1行の幅 * 行数) で現在行までの要素数を求めて、そこに現在のX座標を足します。

(1行の幅 * 行数) + 現在のX

p5jsが予約しているwidthグローバル変数はキャンバスの横幅、heightグローバル変数はキャンバスの縦幅を取得できます。これはちょうどcreateCanvasで指定した値です。また、今回の一行はRGBARGBARGBAのようになっているため4倍にする必要があります。

此処までで、各pixels要素であるRGBAにアクセスし、直接色を指定できるようになりました。以下はRGBA(255,0,0,255)を指定して、全てのピクセルを赤で染めるサンプルです。

4.Densityを考慮する

Retinaディスプレイや携帯電話の場合、より綺麗な画像を表示するためにPiexl per Inch(https://ja.wikipedia.org/wiki/Ppi)、所謂、画素密度が異なります。そのためpixelDensityで値を取得して、縦幅、横幅に掛けます。1行は、width*4*dになります。よって、(width*4*d)*yで、現在行数の要素数を求めて、それにxを加えることで、現在のピクセル位置が求まります。ゆっくり考えれば問題ありませんが、(width*4*d)*y*dみたいにしないように注意してください。

此処までで、高解像度ディスプレイの場合でも各RGBAを修正することができるようになりました。

5.横のノイズをリセットする

p5.jsでは、noise(x,y)を設定すると二次元のパーリンノイズを使うことができます。y=1の固定にして以下のコードを書きました。注意してほしいのは、noise(xoff)を1行単位でリセットすることです。つまり3行目のlet xoff = 0;のように書きます。

二重ループの前にxoffの変数定義をすると、パーリンノイズ関数は、xがずっと続いていると判断するため、意図したノイズになりません。

xoffが正しくない例

yoffも定義する

ここまでくればもう理解できたと思います。あとは、yoffを指定するだけです。

 

まとめ

p5.jsでのピクセル操作と2Dパーリンノイズを実装することができました。まずは、RGBA単位のピクセル操作をできるようにして、そのあとに高解像度を考慮して、最後に2Dパーリンノイズを実装しました。いちどにすべてをやろうとすると、どこが原因で表示できないのかが分からなくなるので、このような実装は、段階的にやったほうが良いようです。