[package]
name = "mandelbrot"
version = "0.1.0"
authors = ["taki"]
edition = "2018"
[lib]
crate-type = ["cdylib", "rlib"]
[features]
default = ["console_error_panic_hook"]
[dependencies]
js-sys = "0.3.60"
wasm-bindgen = "0.2.63"
# The `console_error_panic_hook` crate provides better debugging of panics by
# logging them with `console.error`. This is great for development, but requires
# all the `std::fmt` and `std::panicking` infrastructure, so isn't great for
# code size when deploying.
console_error_panic_hook = { version = "0.1.6", optional = true }
# `wee_alloc` is a tiny allocator for wasm that is only ~1K in code size
# compared to the default allocator's ~10K. It is slower than the default
# allocator, however.
#
# Unfortunately, `wee_alloc` requires nightly Rust when targeting wasm for now.
wee_alloc = { version = "0.4.5", optional = true }
[dependencies.web-sys]
version = "0.3.60"
features = [
'CanvasRenderingContext2d',
'Document',
'Element',
'HtmlCanvasElement',
'ImageData',
'Performance',
'Window',
]
[dev-dependencies]
wasm-bindgen-test = "0.3.13"
[profile.release]
# Tell `rustc` to optimize for small code size.
opt-level = "s"
mod utils;
mod logic;
use wasm_bindgen::prelude::*;
use wasm_bindgen::{Clamped, JsCast};
use web_sys::ImageData;
// When the `wee_alloc` feature is enabled, use `wee_alloc` as the global
// allocator.
#[cfg(feature = "wee_alloc")]
#[global_allocator]
static ALLOC: wee_alloc::WeeAlloc = wee_alloc::WeeAlloc::INIT;
#[wasm_bindgen]
extern "C" {
#[wasm_bindgen(js_namespace = console)]
fn log(a: &str);
}
macro_rules! console_log {
($($t:tt)*) => (log(&format_args!($($t)*).to_string()))
}
macro_rules! measure_elapsed_time {
($t:tt,$s:block) => {{
let window = web_sys::window().expect("should have a window in this context");
let performance = window
.performance()
.expect("performance should be available");
let start = performance.now();
let result = { $s };
let end = performance.now();
console_log!("{}:{}[ms]", $t, end - start);
result
}};
}
#[wasm_bindgen]
pub fn generate_mandelbrot_set(
canvas_w: usize,
canvas_h: usize,
x_min: f64,
x_max: f64,
y_min: f64,
y_max: f64,
max_iter: usize,
) -> Vec<u8> {
measure_elapsed_time!("generate:wasm\telapsed:", {
logic::generate_mandelbrot_set(canvas_w, canvas_h,
x_min, x_max, y_min, y_max, max_iter)
})
}
#[wasm_bindgen]
pub fn draw_mandelbrot_set() {
const CANVAS_ID: &str = "canvas_wasm";
let document = web_sys::window().unwrap().document().unwrap();
let canvas = document.get_element_by_id(CANVAS_ID).unwrap();
let canvas: web_sys::HtmlCanvasElement = canvas
.dyn_into::<web_sys::HtmlCanvasElement>()
.map_err(|_| ())
.unwrap();
let context = canvas
.get_context("2d")
.unwrap()
.unwrap()
.dyn_into::<web_sys::CanvasRenderingContext2d>()
.unwrap();
let canvas_w = canvas.width() as usize;
let canvas_h = canvas.height() as usize;
const X_MIN: f64 = -1.5;
const X_MAX: f64 = 0.5;
const Y_MIN: f64 = -1.0;
const Y_MAX: f64 = 1.0;
const MAX_ITER: usize = 64;
let mut result = measure_elapsed_time!("generate:wasm\telapsed:", {
logic::generate_mandelbrot_set(canvas_w, canvas_h,
X_MIN, X_MAX, Y_MIN, Y_MAX, MAX_ITER)
});
measure_elapsed_time!("draw:wasm\telapsed:", {
let data = ImageData::new_with_u8_clamped_array_and_sh(
Clamped(&mut result),
canvas.width(),
canvas.height(),
);
if let Ok(data) = data {
let _ = context.put_image_data(&data, 0.0, 0.0);
}
})
}
/**
* nCrを求める
*
* @param n
* @param r
* @return nCr
*/
public long C(long n, long r)
{
return P(n, r) / Factorial(r);
}
/**
* nの階乗を求める
*
* @param n
* @return nPr
*/
public long Factorial(long n)
{
long ret = 1;
if(1 < n)
{
ret = n * Factorial(n - 1);
}
return ret;
}
/**
* nPrを求める
*
* @param n
* @param r
* @return nPr
*/
public long P(long n, long r)
{
long ret = 1;
if(0 < n - r)
{
ret = n * P(n - 1, r);
}
return ret;
}
fn get_n_driverged(x0: f64, y0: f64, max_iter: usize) -> u8 {
let mut xn = 0.0;
let mut yn = 0.0;
for i in 1..max_iter {
let x_next = xn * xn - yn * yn + x0;
let y_next = 2.0 * xn * yn + y0;
xn = x_next;
yn = y_next;
if yn * yn + xn * xn > 4.0 {
return i as u8;
}
}
max_iter as u8
}
pub fn generate_mandalbrot_set(
canvas_w: usize,
canvas_h: usize,
x_min: f64,
x_max: f64,
y_min: f64,
y_max: f64,
max_iter: usize,
) -> Vec<u8> {
let canvas_w_f64 = canvas_w as f64;
let canvas_h_f64 = canvas_h as f64;
let mut data = vec![];
for i in 0..canvas_h {
let i_f64 = i as f64;
let y = y_min + (y_max - y_min) * i_f64 / canvas_h_f64;
for j in 0..canvas_w {
let x = x_min + (x_max - x_min) * j as f64 / canvas_w_f64;
let iter_index = get_n_driverged(x, y, max_iter);
let v = iter_index % 8 * 32;
data.push(v);
data.push(v);
data.push(v);
data.push(255);
}
}
data
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_get_n_diverged() {
let max_iter = 10;
assert_eq!(get_n_driverged(1.0, 0.0, max_iter), 3);
assert_eq!(get_n_driverged(0.0, 0.0, max_iter), max_iter as u8);
assert_eq!(get_n_driverged(0.0, 1.0, max_iter), max_iter as u8);
}
#[test]
fn test_generate_mandelbrot_set() {
let canvas_w = 2;
let canvas_h = 2;
let x_min = -1.0;
let x_max = 1.0;
let y_min = -1.0;
let y_max = 1.0;
let max_iter = 8;
assert_eq!(
generate_mandalbrot_set(canvas_w, canvas_h, x_min, x_max, y_min, y_max, max_iter),
vec![96, 96, 96, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255]
);
}
}
:~/rust/mandelbrot$ cargo test
Compiling mandelbrot v0.1.0 (/home/taki/rust/mandelbrot)
warning: function `set_panic_hook` is never used
--> src/utils.rs:1:8
|
1 | pub fn set_panic_hook() {
| ^^^^^^^^^^^^^^
|
= note: `#[warn(dead_code)]` on by default
warning: function `get_n_driverged` is never used
--> src/logic.rs:1:4
|
1 | fn get_n_driverged(x0: f64, y0: f64, max_iter: usize) -> u8 {
| ^^^^^^^^^^^^^^^
warning: function `generate_mandalbrot_set` is never used
--> src/logic.rs:16:8
|
16 | pub fn generate_mandalbrot_set(
| ^^^^^^^^^^^^^^^^^^^^^^^
warning: `mandelbrot` (lib) generated 3 warnings
warning: `mandelbrot` (lib test) generated 1 warning (1 duplicate)
Finished test [unoptimized + debuginfo] target(s) in 0.46s
Running unittests src/lib.rs (target/debug/deps/mandelbrot-c2f466aeb790a116)
running 2 tests
test logic::tests::test_generate_mandelbrot_set ... ok
test logic::tests::test_get_n_diverged ... ok
test result: ok. 2 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
Running tests/web.rs (target/debug/deps/web-ca9c3937d615c7ed)
running 0 tests
test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
Doc-tests mandelbrot
running 0 tests
test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s