extern crate js_sys; extern crate wasm_bindgen; extern crate web_sys; use std::ops::Add; use wasm_bindgen::JsCast; use wasm_bindgen::prelude::*; use web_sys::CanvasRenderingContext2d; use js_sys::{WebAssembly, Uint8ClampedArray}; // Unfortunately `web-sys` at this time doesn't bind APIs with // `Uint8ClampedArray`. For more information see rustwasm/wasm-bindgen#421. // // For now we just bind it ourselves and do some manual frobbing below. #[wasm_bindgen] extern "C" { type ImageData; #[wasm_bindgen(constructor)] fn new(arr: &Uint8ClampedArray, width: u32, height: u32) -> ImageData; } #[wasm_bindgen] pub fn draw( ctx: &CanvasRenderingContext2d, width: u32, height: u32, real: f64, imaginary: f64, ) -> Result<(), JsValue> { // The real workhorse of this algorithm, generating pixel data let c = Complex { real, imaginary, }; let data = get_julia_set(width, height, c); // And now that we've got some pixels, let's create an `ImageData` with the // pixels and then ship it off to the canvas. // // See notes in the `wasm-in-wasm` example for why this is a bit dangerous let my_memory = wasm_bindgen::memory() .dyn_into::() .unwrap(); let uint8_array = Uint8ClampedArray::new(&my_memory.buffer()) .subarray( data.as_ptr() as u32, data.as_ptr() as u32 + data.len() as u32, ); let data = ImageData::new(&uint8_array, width, height); ctx.put_image_data(data.unchecked_ref(), 0.0, 0.0) } fn get_julia_set(width: u32, height: u32, c: Complex) -> Vec { let mut data = Vec::new(); let param_i = 1.5; let param_r = 1.5; let scale = 0.005; for x in 0..width { for y in 0..height { let z = Complex { real: y as f64 * scale - param_r, imaginary: x as f64 * scale - param_i, }; let iter_index = get_iter_index(z, c); data.push((iter_index / 4) as u8); data.push((iter_index / 2) as u8); data.push(iter_index as u8); data.push(255); } } data } fn get_iter_index(z: Complex, c: Complex) -> u32 { let mut iter_index: u32 = 0; let mut z = z; while iter_index < 900 { if z.norm() > 2.0 { break } z = z.square() + c; iter_index += 1; } iter_index } #[derive(Clone, Copy, Debug)] struct Complex { real: f64, imaginary: f64, } impl Complex { fn square(self) -> Complex { let real = (self.real * self.real) - (self.imaginary * self.imaginary); let imaginary = 2.0 * self.real * self.imaginary; Complex { real, imaginary } } fn norm(&self) -> f64 { (self.real * self.real) + (self.imaginary * self.imaginary) } } impl Add for Complex { type Output = Complex; fn add(self, rhs: Complex) -> Complex { Complex { real: self.real + rhs.real, imaginary: self.imaginary + rhs.imaginary, } } }