Julia Set
View full source code or view the compiled example online
While not showing off a lot of web_sys
API surface area, this example shows a
neat fractal that you can make!
index.js
A small bit of glue is added for this example
import('./pkg')
.then(wasm => {
const canvas = document.getElementById('drawing');
const ctx = canvas.getContext('2d');
const realInput = document.getElementById('real');
const imaginaryInput = document.getElementById('imaginary');
const renderBtn = document.getElementById('render');
renderBtn.addEventListener('click', () => {
const real = parseFloat(realInput.value) || 0;
const imaginary = parseFloat(imaginaryInput.value) || 0;
wasm.draw(ctx, 600, 600, real, imaginary);
});
wasm.draw(ctx, 600, 600, -0.15, 0.65);
})
.catch(console.error);
src/lib.rs
The bulk of the logic is in the generation of the fractal
#![allow(unused)] fn main() { use std::ops::Add; use wasm_bindgen::prelude::*; use wasm_bindgen::Clamped; use web_sys::{CanvasRenderingContext2d, 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); let data = ImageData::new_with_u8_clamped_array_and_sh(Clamped(&data), width, height)?; ctx.put_image_data(&data, 0.0, 0.0) } fn get_julia_set(width: u32, height: u32, c: Complex) -> Vec<u8> { 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<Complex> for Complex { type Output = Complex; fn add(self, rhs: Complex) -> Complex { Complex { real: self.real + rhs.real, imaginary: self.imaginary + rhs.imaginary, } } } }