F#をマスターする:ナビゲーションとC#統合を使用してカラフルなマンデルブロ集合を構築する

はじめに



この記事は、少なくともC#およびF#言語に少なくとも少し慣れている人を対象としています。 いずれにせよ、コードをできるだけ読みやすくし、各フラグメントの説明を提供しようとしました。 次の記事でF#言語に慣れることができます。



Habréには、F#言語(その歴史、起源、特徴)についてすでに多くの一般的な言葉が書かれています。 繰り返したくないので、要点に直行することを提案します。 したがって、アクションプランは次のとおりです。

  1. マンデルブロ集合の構築。
  2. 結果の視覚化;
  3. それだけでなく、C#との統合。
行きましょう。





マンデルブロ集合の構築



F#言語でマンデルブロ集合を構築する例はすでにHabré検討されていますが、少し異なるアプローチを取り、いくつかのポイントをより詳細に検討します。



マンデルブロ集合はどのように決定されますか?


次の式で生成される一連の数値を考えてみましょう。

C n + 1 = C n 2 + c 0

そのようなシーケンスが2つの複素数C 1 (1、1i)およびC 2 (-1、-1i)を超えない場合、複素数c 0はマンデルブロ集合に属します。 したがって、マンデルブロ集合はそのようなすべての数値c 0の集合であるため、上記のシーケンスはC 1およびC 2のフレームワーク内にとどまります。



マンデルブロ集合とF#


F#で複素数を処理するために、Microsoft.FSharp.Mathライブラリがあります。これは、FsharpPowerPackパッケージで利用できます (リンクには、インストール手順やこのパッケージに関するその他の有用な情報も記載されています)。 このライブラリをプロジェクトに追加して指定します。



open Microsoft.FSharp.Math
      
      





これで、プログラムで複素数を操作できます。 最小および最大境界を定義します。



 let cMax = complex 1.0 1.0 let cMin = complex -1.0 -1.0
      
      





関数に直接進みます。 次のように、セットのメンバーシップをチェックする機能を実装します。



 let rec isInMandelbrotSet (z, c, iter, count) = if (cMin < z) && (z < cMax) && (count < iter) then isInMandelbrotSet ( ((z * z) + c), c, iter, (count + 1) ) else count
      
      





再帰関数isInMandelbrotSet



は、チェック対象の数がcMaxとcMinの境界を超え、再帰の深さが反復数を超えないまで機能します。 完了すると、関数は完全な再帰ステップの数を返します(これは将来、セットを「色付け」するときに役立ちます)。



結果の可視化



私たちはすでに非常に多くを構築しましたが、それを表示するだけです。 しかし、ここから楽しみが始まります。



複素数は2つの部分で構成されるため、2次元平面上に表示を作成できます。 マンデルブロ集合はC 1 (1、1i)とC 2 (-1、-1i)の間に存在するため、必要な座標系の中心は(0、0)になり、横座標と縦座標は-1.0と1.0に制限されます。

画像



したがって、ポイントの座標を、複雑な平面から画像の構築に使用される平面に転送する必要があります。



次のようにやってみましょう:



 let scalingFactor s = s * 1.0 / 200.0 let mapPlane (x, y, s, mx, my) = let fx = ((float x) * scalingFactor s) + mx let fy = ((float y) * scalingFactor s) + my complex fx fy
      
      





この関数は、「なじみのある」平面の各点に対応する複素数を返します。 今後、mxおよびmyの値を使用して、画像にナビゲーションを実装します。



これで、セット全体を使い慣れた平面に簡単に描画できます。 これを行うには、平面のすべての座標に沿って「歩き」、各ポイント(より正確には対応する複素数)がマンデルブロ集合に属していることを確認する必要があります。 ポイントがセットに属する場合、それを黒にします。それ以外の場合は、別の色で色付けします。 これを行うために、特別な「カラーリング」機能を作成します。



 let colorize c = let r = (4 * c) % 255 let g = (6 * c) % 255 let b = (8 * c) % 255 Color.FromArgb(r,g,b)
      
      





この関数は、isInMandelbrotSet関数から取得した反復回数を取得し、色のRGB値を決定します。 数値係数は任意に設定できますが、色のスムーズな移行を実現するために、わずかな差で設定することをお勧めします。 また、ここではSystem.Drawingライブラリが必要です。



 open System.Drawing
      
      





このライブラリを使用して、新しいビットマップイメージを作成し、特定の色のポイントを追加します。 したがって、レンダリング関数は次のようになります。



 let createImage (s, mx, my, iter) = let image = new Bitmap(400, 400) for x = 0 to image.Width - 1 do for y = 0 to image.Height - 1 do let count = isInMandelbrotSet( Complex.Zero, (mapPlane (x, y, s, mx, my)), iter, 0) if count = iter then image.SetPixel(x,y, Color.Black) else image.SetPixel(x,y, colorize( count ) ) let temp = new Form() in temp.Paint.Add(fun e -> e.Graphics.DrawImage(image, 0, 0)) temp
      
      





ここでは、System.Windows.Formsコンポーネントを使用します。



 open System.Windows.Forms
      
      





プログラムを実行し、結果を楽しむだけです。 これは次のように実行できます。



 do Application.Run(createImage (1.5, -1.5, -1.5, 20))
      
      





初期パラメータを示します:スケール、XとYのオフセット、および反復回数。 反復回数が多いほど、画像はより詳細になります(したがって、作成に時間がかかります)。 このような何かが仕事になるはずです:

画像



したがって、プログラムの完全なリスト:



 #light open Microsoft.FSharp.Math open System open System.Drawing open System.Windows.Forms let cMax = complex 1.0 1.0 let cMin = complex -1.0 -1.0 let rec isInMandelbrotSet (z, c, iter, count) = if (cMin < z) && (z < cMax) && (count < iter) then isInMandelbrotSet ( ((z * z) + c), c, iter, (count + 1) ) else count let scalingFactor s = s * 1.0 / 200.0 let offsetX = -1.0 let offsetY = -1.0 let mapPlane (x, y, s, mx, my) = let fx = ((float x) * scalingFactor s) + mx let fy = ((float y) * scalingFactor s) + my complex fx fy let colorize c = let r = (4 * c) % 255 let g = (6 * c) % 255 let b = (8 * c) % 255 Color.FromArgb(r,g,b) let createImage (s, mx, my, iter) = let image = new Bitmap(400, 400) for x = 0 to image.Width - 1 do for y = 0 to image.Height - 1 do let count = isInMandelbrotSet( Complex.Zero, (mapPlane (x, y, s, mx, my)), iter, 0) if count = iter then image.SetPixel(x,y, Color.Black) else image.SetPixel(x,y, colorize( count ) ) let temp = new Form() in temp.Paint.Add(fun e -> e.Graphics.DrawImage(image, 0, 0)) temp do Application.Run(createImage (1.5, -1.5, -1.5, 20))
      
      







小計


ご覧のとおり、「色」のマンデルブロ集合を作成するのに40行強かかり、それほど時間はかかりませんでした。 しかし、私たちはまだフラクタル画像の美しさを完全に楽しむことはできません(より正確には、できますが、各編集の前に画像スケールを変更することは完全に不便です)。 この問題を克服するには、インターフェース要素を追加する必要があります-ナビゲーションキーと画像の増減、できれば詳細の変更。



もちろん、System.Windows.Formsライブラリを使用してフォーム自体を作成したのと同じ方法で、F#内にすべてのインターフェイス要素を作成できます。 しかし一方で、本格的なWindows Formsアプリケーションのフレームワークでこれを行う方がはるかに興味深い(そして、おそらくより論理的です)でしょう! さて、それでは今やろう。



C#との統合(それだけではありません)



F#アプリケーションをアセンブルした後、出力で、必要なすべての機能を含むライブラリを取得します。 このライブラリの必要な機能に簡単にアクセスするには、F#コードで名前空間を宣言します。 これは次のように行われます。コードの最初に、以下を追加します



 module Fractal
      
      





さらに、フォームとそれを実行するコードも不要になりました。 したがって、外部から呼び出す関数は次のようになります。



 let createImage (s, mx, my, iter) = let image = new Bitmap(400, 400) for x = 0 to image.Width - 1 do for y = 0 to image.Height - 1 do let count = isInMandelbrotSet( Complex.Zero, (mapPlane (x, y, s, mx, my)), iter, 0) if count = iter then image.SetPixel(x,y, Color.Black) else image.SetPixel(x,y, colorize( count ) ) image
      
      





呼び出された関数createImageは、マンデルブロ集合を含む作成されたビットマップ画像を返します。



WindowsフォームでF#ライブラリを使用する


実際、すべてが非常に簡単です。完成したライブラリを新しいプロジェクト(Windows Formsなど)に追加するだけです。 プロジェクトを作成したら、MS Visual Studioでこれを行うには、ソリューションエクスプローラーウィンドウの[参照の追加]コマンドを使用して、必要なライブラリを選択します。 ライブラリで名前空間を指定したため( module Fractal



)、新しいプロジェクトではこのライブラリのすべての機能にアクセスできます。 次のように、完成したイメージを生成する関数を呼び出します。



 Bitmap image = Fractal.createImage(1.5, -1.5, -1.5, 20);
      
      





結果のビットマップイメージは、既に任意の方法で使用できます-たとえば、PictureBox要素に背景として追加します。



ナビゲーション要素の追加


ご覧のとおり、スケール、反復回数、XおよびYに対するオフセットのさまざまなパラメーターを使用して画像生成を「要求」できます。つまり、ナビゲーションを追加することは難しくありません。 PictureBoxを画像のコンテナとして使用します。6つのボタンを使用してナビゲートします:ズームイン/ズームアウト、上/下、左/右に移動します。



便宜上、現在のフラクタルの状態を格納する別のクラスFractalClassを作成します:スケール、中心からのオフセット、近似レベル。 クラスのメインメソッドは、現在のパラメーターでセットのイメージを要求します。



  private Bitmap Draw() { int iters = iterations + 2 * steps; return Fractal.createImage(currSc, currMvX, currMvY, iters); }
      
      





クラスの残りのメソッドは、フラクタルの状態を変更し、Draw()メソッドを参照します。 したがって、たとえば、近似方法は次のようになります。



  public Bitmap ZoomIn() { scale = 0.9 * scale; zoomSteps++; return Draw(); }
      
      





FractalClassクラスの対応するメソッドへの呼び出しを、ナビゲーションのためのキーストロークの処理に追加するだけです。 結果は次のようになります。

画像



必要な反復回数を関数に渡す機能により、フラクタルが近づくにつれて改善できます。 したがって、各ステップで、画像はますます興味深いものになります。

画像



Windowsフォームだけではありません


接続されたライブラリの機能をWindows Formsアプリケーションに適用したのと同じ方法で、既製のライブラリを.NETプラットフォームの他のアプリケーションで使用できます。Silverlight、Webアプリケーションなどです。



使用した材料:



ご清聴ありがとうございました。おもしろかったです!



PS:テキストのエラー/不正確さのリクエストは、個人的なメッセージで通知されるべきです。



All Articles