Final angle colouring.
authorClaude Heiland-Allen <claude@mathr.co.uk>
Sat, 21 Dec 2013 11:01:56 +0000 (11:01 +0000)
committerClaude Heiland-Allen <claude@mathr.co.uk>
Sat, 21 Dec 2013 11:01:56 +0000 (11:01 +0000)
commit99e73f934ddd79ebdbbb2f6d929e6ffd3562df9a
treeae237dd2fadf3d3d7e2c2deb1acf7b9918b7ecba
parent9dfc508d2c4af17bcd951c419487411ebca4b8d7
Final angle colouring.

First we coloured according to whether the point escaped or not.  Then
we augmented with extra information that we had previously discarded:
the escape time.  There's more information that we are discarding:
maybe we can use the final value of the iterate z in some way.  We modify
our pixel type to include this, choosing a single precision type because
our aim is colouring and colours have limited precision.  We modify our
calculate() function to write to a pixel passed as a pointer, as functions
have at most one return value.  We no longer need to return anything, so
we give a void return type.  We change our render function to pass the
address (the ``&'' operator) of the pixel.

When saving the image, we now have more information to write.  We're already
abusing PPM to store 24bit values packed into 3 8bit colour channels, perhaps
we can use the same technique for the final z.  We know that the magnitude of
the final z is greater than our escape radius 2, but we don't have an upper
bound (suppose we have a very zoomed out image).  Any rescaling to a 24bit
integer that we can pack into bytes would have an arbitrary bound.

Complex numbers expressed as a sum of real and imaginary parts are in
rectangular (Cartesian) form.  We have the magnitude by Pythagoras Theorem,
\( |x + i y|^2 = x^2 + y^2 \), and considering the angle that this triangle
makes with the positive real axis gives the phase (also called argument):
\( arg (x + i y) = atan (y / x) \).  Angles are unique up to multiples of
a whole turn (2 pi), so maths conventionalized the prinicipal argument to
be in [-pi,pi].  Given a fixed range, we can rescale it to the fixed range
for our PPM output: first we normalize to [-0.5,0.5], we shift it by
1 to [0.5,1.5], and then we use the fmod() function to take the fractional
part, giving us a range of [0,1].  Multiplying by the number of values in
our 24bit range gives us an integer that we can pack into bytes as before.

So far we've been writing to the standard output stream and redirecting it
to a file with the shell.  Now we have two images to write.  Our render
program now takes a stem string on the command line, which we use to
generate filenames in our image_save() function (modified from image_write()).
To form the filenames, we use the snprintf() function -- similar to printf
but stores the output in a string.  We need some memory to store the output,
so we use calloc(), and we need to know how much space to allocate, so we
use the length function strlen() defined in string.h.  We need more space
than the length of the stem, because we'll add a suffix to distinguish our
two images.

We open the files for writing binary data using fopen(), and print the
image header with fprintf(), similar to printf() but directing output to
a particular file handle.  Similarly we use fputc() instead of putchar().
We must fclose() the files when we have finished writing.

In our colour program, we generate filenames from a stem in the same way.
We open them for reading binary data, and use fscanf() and fgetc() to read
from the file handles instead of the standard input stream.  We check that
both images have the same dimensions.  Then the same loop as before, only
this time we read both files, and rescale the angle data to [0,1].  Our
colour function uses both sourcesinformation now: hue coming from the escape
time, and value from the final angle.
colour.c
mandelbrot.h
mandelbrot_imp.c
render.c