Rustでマンデルブロ集合を作成する方法 その1 (Rust インストールから必要なクレートの準備、基本画像出力まで)

いままでC#、C/C++のC系？でプログラムを作成してきたわけであるが、`関数型の言語も学習すると良いことがある`という眉唾ものの記事を読んだ影響で純粋関数型言語Haskellもかじってみた。
そうこうしているうちにRustなる比較的新しい言語が登場していることにはうすうす感づいていた。どうせC++の延長のような言語なんだろうと少々高を括っていたのだか、どれどれ、と重い腰を上げて学習にとりかかってみると、どうもおかしい、変だ、難しい、一筋縄ではいかない言語だと痛感する。
### これは！

関数型言語に近いと感じ、しかも高速に動作する。難しいかもしれないが、習得するのに値する言語と判断した。Rustを使いこなせるようになればC++もHaskellも不要になるかもしれない。(独断です)

:::note info
先程、眉唾ものと書いたが記事は正しかった
Rustの出現は脅威ではあるが、それでもHaskell(関数型言語系)を学習することの重要性は変わらない。むしろRustを学ぶにおいて、事前にHaskellをやっていたことが理解を早めたといってよい。
:::

**新しい言語を学ぶには、なにより沢山のコードを書いてみるのが早道。といわけで定番？のマンデルブロ集合の画像を作成してみたわけである。**

> 作成したコードは若干冗長であり、改善の余地は大いに存在する。

 ## まずはなによりRustのセットアップ
すでにインストール済みの方は次のセクションに進むのだ。それ以外の方は... 
Rustの公式 `https://www.rust-lang.org/ja/tools/install`に沿って
Rustをインストールだ。

``` sh
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
```

### 次に `cargo init` コマンドを使って初期のパッケージを作成

``` sh
mkdir -p rust-mandelbrot
cd rust-mandelbrot
cargo init
```

### vscodeを使ってプロジェクトを開く
現在のところ最もRustの拡張機能が整っているIDEがvscodeなので、vscode利用を前提に進む。

``` sh
code .
```
![スクリーンショット 2023-07-08 16.07.50.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2810146/76d25a49-1431-f934-4f8d-cbd1b37030e5.png)

`cargo`は自動的に`git`の設定までやってくれる。
自動で作成された`main.rs`を試しに以下のように修正する。(コピー&ペーストでもよい)
>  println!("Hello, world!"); なる行が存在するが、消してしまってかまわない

``` rust
use image::{ImageBuffer, Rgb, RgbImage};
use irospace::{converter::*, ColorConverterBuilder, HsvColor, RgbColor};
use itertools::Itertools;
use num::complex::Complex;

// 画像のサイズ 1000x1000
static SIZE: u32 = 1000;

fn main() {
    // 複素数が使えるかのテスト
    let test: num::Complex<f64> = Complex::new(0.0, 0.0);
    println!("複素数 {}", test);

    // 1000x1000の画像データを作成
    let plot_data: Vec<u32> = plot(SIZE);

    // PNG画像を作成
    let mut img: ImageBuffer<image::Rgb<u8>, Vec<u8>> = RgbImage::new(SIZE, SIZE);
    // HSVからRGBへの変換器を作成
    let converter = ColorConverterBuilder::new().from_hsv().to_rgb().build();

    // 1000x1000の x,y座標の組み合わせを作成
    let grid = (0..SIZE).cartesian_product(0..SIZE);
    // x,y座標に色を割り当てる
    grid.for_each(|x| {
        // 色を取得
        let color = plot_data[(x.0 * SIZE as u32 + x.1) as usize];
        // HSVの H に色を割り当てる
        let color_code = HsvColor::new(color as u16, 255, 255);
        // HSVからRGBへ変換
        let rgb: RgbColor = converter.convert(&color_code).unwrap();
        // 画像に色を割り当てる
        img.put_pixel(x.0, x.1, Rgb([rgb.r(), rgb.g(), rgb.b()]));
    });

    // 画像を保存
    img.save("out_image.png").unwrap();
}

// プロットするデータを作成
fn plot(size: u32) -> Vec<u32> {
    // 360色の色を割り当てる
    let ret: Vec<u32> = (0..(size * size)).map(|n| (n / size) % 360).collect();
    ret
}

```

次に上記で利用する外部クレートをADDするため以下のコマンドを実行する。

``` bash
cargo add image add irospace itertools num
```

必要とした外部クレートは以下
- image
    - png ファイルを作成するため
- irospace
    - 色空間を操作するためHSVからRGBなど
- itertools
    - 直積集合を表現するため (1000ｘ1000のグリッド作成)
- num 
    - 今後利用する複素数を扱うため

準備ができたら`vscode`のコンソールから以下のコマンドを実行すると
`out_image.png`が出来上がる。

```
cargo run
```

> out_image.png

![out_image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2810146/d6c08af2-b46e-63ce-7cd9-eb9ba65aa294.png)

### いまは虹色だが `plot` 関数を修正していくことで最終的に以下の画像を生成する。

![fractal.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2810146/b212bbb1-26e6-18cf-f99d-0bbb8f38537f.png)


### 次回へ
https://qiita.com/Guarneri001/items/1129e74e62017a792352