@ledsun blog

無味の味は佳境に入らざればすなわち知れず

rustでマンデルブロー集合を描く

「2.7 プロジェクト:マンデルブロ集合のレンダリング」です。

use num::complex::Complex;

fn calculate_mandelbrot(
    max_iters: usize,
    x_min: f64,
    x_max: f64,
    y_min: f64,
    y_max: f64,
    width: usize,
    height: usize,
) -> Vec<Vec<usize>> {
    let mut rows: Vec<_> = Vec::with_capacity(width);
    for img_y in 0..height {
        let mut row: Vec<_> = Vec::with_capacity(height);
        for img_x in 0..width {
            let x_percent = img_x as f64 / width as f64;
            let y_percent = img_y as f64 / height as f64;
            let cx = x_min + (x_max - x_min) * x_percent;
            let cy = y_min + (y_max - y_min) * y_percent;
            let escaped_at = mandelbrot_at_point(cx, cy, max_iters);
            row.push(escaped_at);
        }
        rows.push(row);
    }
    rows
}

fn mandelbrot_at_point(cx: f64, cy: f64, max_iters: usize) -> usize {
    let mut z = Complex { re: 0.0, im: 0.0 };
    let c = Complex::new(cx, cy);
    for i in 0..=max_iters {
        if z.norm() > 2.0 {
            return i;
        }
        z = z * z + c;
    }
    return max_iters;
}

fn render_mandelbrot(escape_vals: Vec<Vec<usize>>) {
    for row in escape_vals {
        let mut line = String::with_capacity(row.len());
        for column in row {
            let val = match column {
                0..=2 => ' ',
                3..=5 => '.',
                4..=10 => ':',
                11..=30 => '*',
                31..=100 => '+',
                101..=200 => 'x',
                201..=400 => '$',
                401..=700 => '#',
                _ => '%',
            };
            line.push(val)
        }
        println!("{}", line);
    }
}

fn main() {
    let mandelbrot = calculate_mandelbrot(1000, -2.0, 1.0, -1.0, 1.0, 80, 24);

    render_mandelbrot(mandelbrot);
}

60行と長くなってきましたが、Github copilot君も手伝ってくれるので、写経する分にはサクサクで楽しい感じです。 「rustはコンパイルを通すまでがつれえ」という噂はまだ味わえないようです。

f:id:ledsun:20220114231126p:plain
マンデルブロー集合を表示したところ

本に載っている実行サンプルだとみたいな真ん中に点が表示されています。 英語にそんな記号ないと思うのですか、何を表示しているのでしょうか? ソースコード?を指定していて、雰囲気が違うので:にしてみました。

警告

19行目の()が冗長だと警告が出ました。

warning: unnecessary parentheses around assigned value
  --> src\main.rs:16:29
   |
16 |             let x_percent = (img_x as f64 / width as f64);
   |                             ^                           ^
   |
   = note: `#[warn(unused_parens)]` on by default
help: remove these parentheses
   |
16 -             let x_percent = (img_x as f64 / width as f64);
16 +             let x_percent = img_x as f64 / width as f64;
   |

パターンマッチング部分も範囲に重複がある警告がでました。

warning: multiple patterns overlap on their endpoints
  --> src\main.rs:47:17
   |
46 |                 3..=5 => '.',
   |                 ----- this range overlaps on `5_usize`...
47 |                 5..=10 => ':',
   |                 ^^^^^^ ... with this range
   |
   = note: `#[warn(overlapping_range_endpoints)]` on by default
   = note: you likely meant to write mutually exclusive ranges

rustのバージョンの違いでしょうか?ただの宗派の違いでしょうか?謎です。

引数

最後の80, 24は描画サイズのようです。 それ以外はさっぱりわかりません。

マンデルブロ集合 - Wikipediaをみると、なるほど

z = z * z + c;

と対応してそうな式が書いてあります。 各引数がどういう役割なのかは、やはり、よくわかりません。

パラメータをかえたら部分が拡大されて「フラクタルだ!」みたいな体験を期待していたのですが、そういうものではなさそうです。 書いたプログラムの楽しさがわかりません。 今のところ使える道具が限られているせいだと思って、先に進みます。