# Collatz fractal in Rust

Among the many fractals, there is Collatz Fractal based on a complex extension of: $$f(x) = \left\{ \begin{array}{ll} \frac{x}{2} \space \text{if even} \\ 3x + 1 \space \text{if odd} \end{array} \right.$$

To generate the fractal, you pick a bunch of points and repeatedly apply f over and over again a large number of times. Morally, f(f(f(f(....f(x)....)))) In this case, however, it is not a real number x but instead a complex number (often denoted z). The end result is plotted by giving it a color that "corresponds" to the size of the result.

The resulting image is:

Here is the code:

# lib.rs

/// A fractal generation module.
pub mod fractal_gen {
extern crate image;
extern crate num_complex;

use self::num_complex::Complex;
use std;

/// Complex extension of the Collatz function.
///
/// # Arguments
/// * z - A complex number.
///
/// # Returns
/// * The Collatz function applied to z. Where the Collatz function foll-
/// ows the definition from here: http://yozh.org/2012/01/12/the_collatz_fractal/
pub fn collatz(z: Complex<f32>) -> Complex<f32> {
let comp_pi = Complex::new(std::f32::consts::PI, 0.0);
return ((7.0 * z + 2.0)
- (comp_pi * z).cos()
* (5.0 * z + 2.0)) / 4.0;
}

/// Generate a fractal picture.
///
/// # Arguments
/// * buf - Image buffer that will store the result image.
/// * f - Complex function that returns a complex number.
/// * scalex - Horizontal scaling factor.
/// * scaley - Vertical scaling factor.
/// * max_iterations - Maximum number of applications of f.
pub fn populate_image(buf: &mut image::GrayImage,
f: fn(Complex<f32>) -> Complex<f32>,
scalex: f32, scaley: f32,
max_iterations: u16) {
for (x, y, pixel) in buf.enumerate_pixels_mut() {
let cy = y as f32 * scaley - 2.0;
let cx = x as f32 * scalex - 2.0;

let mut z = Complex::new(cx, cy);

let mut i = 0;

for t in 0..max_iterations {
if z.norm() > 1000.0 {
break
}
z = f(z);
i = t;
}
*pixel = image::Luma([i as u8]);
}
}
}


# main.rs

extern crate fraclib;
extern crate argparse;
extern crate image;

use argparse::{ArgumentParser, Store};
use fraclib::fractal_gen;

/// Generates and exports a fractal.png. fractal.png is a picture of the Colla-
/// tz fractal.
///
/// # Flags
///
/// * --maxiter iteration depth for successive iteration on the Collatz func-
/// tion.
/// * --xdim width of the image.
/// * --ydim height of the image.
/// * --scalewidth horizontal scaling factor.
/// * --scaleheight vertical scaling factor.
fn main() {
let mut max_iterations = 1024u16;

let mut imgx = 800;
let mut imgy = 800;

let mut scalex = 4.0 / imgx as f32;
let mut scaley = 4.0 / imgy as f32;

{
let mut ap = ArgumentParser::new();
ap.set_description("Create a Collatz Fractal.");
ap.refer(&mut max_iterations)
"Maximum number of iterations on Collatz function");
ap.refer(&mut imgx)
"The x dimension of the generated image");
ap.refer(&mut imgy)
"The y dimension of the generated image");
ap.refer(&mut scalex)
"The width scaling factor");
ap.refer(&mut scaley)
"The height scaling factor");
ap.parse_args_or_exit();
}

let mut imgbuf = image::GrayImage::new(imgx, imgy);

fractal_gen::populate_image(&mut imgbuf,
fractal_gen::collatz,
scalex, scaley,
max_iterations);

// Save the image as “fractal.png”, the format is deduced from the path
imgbuf.save("fractal.png").unwrap();
}


Github can be found here. Heavily modified variant of the Mandelbrot example given here.