Zero dependency images (of chaos) in Rust

Most recent update: 13th February 2021 - 03:28:58 - 3902 characters

One of the most upvoted posts on the Rust subreddit has been "A fractal I rendered with rust without any external libraries". Whilst the output is beautiful there was a minor complaint - zero dependencies still included Tokio, image, and rand!

In recent days I had a desire to recreate my experience of an early foray into numerical computing. Rust seemed like an obvious candidate!

I decided to use the opportunity to replicate two aspects with zero dependencies: parallel computation and image export. The approaches used will be anything but optimal but may be of use to others writing fractal visualizers, scanline renderers, raytracers, and other graphical projects that benefit from being "from the ground up".

In brief: chaos and the logistic map

Rather than fractals we'll be playing with chaos by producing a plot - a bifurcation diagram, specifically the logistic map. This diagram represents the behaviour of a simple equation, \(x_{t+1} = rx_t(1 - x_t)\), repeated over and over.

For certain values of r (such as between 2.4 and 3) the resulting series of values for x converges to a single number. Beyond 3 however the result gets quite interesting. The series of values for x dance around a small but growing set of points.

With the help of the Python terminal:

>>> r = 3.2
>>> 0.5
0.5
>>> r * _ * (1 - _)
0.8
>>> r * _ * (1 - _)
0.512
>>> r * _ * (1 - _)
0.7995392
>>> r * _ * (1 - _)
0.512884056522752
>>> 
>>> r * _ * (1 - _)
0.7994688034800593
>>> r * _ * (1 - _)
0.5130189943751092
>>> r * _ * (1 - _)
0.7994576185134749
>>> r * _ * (1 - _)
0.5130404310855622

Here we see that for r = 3.2 the series of values for x bifurcates (forks or splits) between 0.799 and 0.513.

What might this look like if we sweep across the range of values for r and keep track of a heat map of where the resulting points end up?

Exporting images: the Portable GrayMap (PGM)

Exporting images as either grayscale or RGB is a basic requirement for any graphical application.

The PGM format is the simplest way of producing an image, representing the pixels in a standard space delimited text format. If you're interested in colour you can look to the Portable PixMap (PPM) as well.

Rather than performing anti-aliasing in the application itself we'll be exporting at a higher resolution and then downsampling.

Converting from PGM to PNG

To convert a PGM image to a more standard format such as PNG we can use ImageMagick. The file names are what set the format and we can specify downsampling but setting a lower resolution than what we produce.

convert -resize 2160x1440 img.pgm img.png

Depending on your image viewer this might not be necessary for local use but will be definitely needed if you intend on sharing the images you produce with others.

Multi-threading for speed

Threads. Threads are shiny and make many cores nice.

Is this better than tokio? Absolutely not. Does it work well enough, for a varying definition of well enough? Absolutely.

What about rand?

In this we've provided (minimal) replacements for the tokio and imagecrates. What about rand however?

Assuming you don't need a strong random number generated, and especially if you don't have any intent of letting it even within a billion bytes of usage in cryptography, you have a number of options.