A common early project for beginner programmers is a program to calculate and draw the Mandelbrot Set.
It’s a good project as the actual mathematics are simple, despite the fact that calculations are on the complex plane. Even if you’re not confident on basic algebra and geometry with complex numbers pretty well every programming language has libraries that will do all the real work for you anyway. Arguably, even if you don’t have an interest in the math involved itself, it’s still a good first project.
So, besides the actual calculation parts (I can say out of all the functions in my application, the actual calculation and value testing functions are the simplest by far) one has to work out how a user sets values they wish to change, how to write output to the screen and to files, including making and saving image files. It is a nice first complete application project.
It’s a project that covers the entire workflow, user input, data manipulation, output to user. Further, there are always improvements to try as you learn more. Most optimizing in one sense isn’t worth it (“Premature optimization is the root of all evil” – Donald Knuth. Take this advise to heart, you don’t know what needs optimizing until you have stable, working code to profile in the first place). However, a project like this isn’t for a client, so, feel free to break your code and resurrect it over and over. There are HUGE time savings to be found in there, and website after website discussing all sorts of tricks and tips. No reason for me to repeat what many have said far better than I could.
What I want to mention is data presentation. The Mandelbrot set image is actually communicating very little. If a pixel is black, the point it is representing is almost definitely in the set. If it is not black, the point it is representing is very likely not in the set. The only time membership is really in question is for points close to the boundary. How close do they have to be to the boundary to be questionable? Well, the more iterations you allow the program to try, the closer to the boundary you can go and consider the determination trustworthy. Even so, your computer is almost definitely using decimal approximations of the point in question (I guess if you had (eg) Maple do the symbolic manipulations the only time you touch an approximation is when you draw a pixel, but that is beyond the scope of this discussion), so there may be rounding errors leading your computer to present the wrong conclusion. Finally your pixel is a square region, but the point calculated is a singular location, so the farther from the centre of the pixel you go, the less reliable the colour of that pixel is. Clear as mud, I know.
So, hand-waving all that aside, you do have an image that is approximately correct. Since a pixel is just a number mapped to a colour, the colour mapped to is basically arbitrary.
Take RGB colour space for example. A colour is defined by three components, how much red, how much green and how much blue light to emit, each one taking a value from 0 to 255. You end up with a cube with area:
Since we can only have discrete values this give us our entire 24 bit colour space of 16,777,216 colours.
It would almost feel like you now have the only tool you need to do any colour manipulations you want. The space is well defined and closed under addition modulo 256 allowing cyclic behaviour. In fact one can further just choose a point within the space, use it to define one corner of a cube within the space centred around the colour space’s midpoint and you have all reflections and at least a few rotations available (or just allow reduction modulo 256 so any rotation that puts the point outside the colour space maps it back in. We have no need of a one to one mapping after all. The possibilities are endless (for finite values of endless) really). However, just ask an engineer about RGB space and they will tell you how happy they are that no display is actually built on that model. In the end, there is no natural way to navigate the space that is aesthetically pleasing, or even sensible.
Instead, and this is only one of several other colour spaces, let’s try a cylindrical coordinate system. A cylinder is made by taking a circular disk and drawing it through space perpendicular to its face. Let the hues all be represented as being at a given angle on the disk’s edge, so as you go around the curved surface you cycle through all available hues [Fun fact, you can do this since we perceive a colour we call magenta which does not actually exist as a discrete wavelength of light. The EM spectrum goes from red to violet, but if you present the human eye with a mix of red and voilet light we perceive magenta as if it were a distinct colour and further as one that fills the role of looping the colours around for us. Your brain is constantly lying to you and this fictional colour is one of my favourite examples]. Go up the cylinder, let it be as if you were turing up a light and the colours get lighter and eventualy wash out. Go down the cube, turn the light down until no illumination at the bottom and we have black. The the disk in the middle of the cylinder is optimum lighting. Finally, highest saturation is at the outside edge of the cylinder and saturation goes down as you approach the centre of the disk at the height you are at on the cylinder. So, all points in this space are defined by an angle (around the disk), a distance from the centre of the disk towards the edge, and a distance from the base to the desired height. This is a cartoon version of the HSL colour space (and I do mean cartoon since what I’m describing there is actually wrong since HSL is better understood as two cones rather than one cylinder, the wikipedia article is actually amazing and if you have the time and care, read through it).
Ok, now, we compare two images from the Mandelbrot set, both exact same region, same maximum iterations, same subsampling, same everything as I did one right after the other and all I did was change with colouring algorithms I used. The upper image is an RGB space colouring system that is sometimes ok, but, here really just does not give anything nice, and the bottom one is an HSL algorithm I wrote that ties magnitude to trigonometric functions with results that I think look neat.
Simple RGB cube, linear walk through space systematically. | |
HSL Mapping of colour space. Sinusoidal movement through space. Click on image for full size. | |
We have the exact same data, and the functions I built have the capacity to never cycle (as in if you want to make sure you don’t have a collision of two values to one colour, that is a setting I included) at which point the colours themselves do indeed have a one to one mapping for the data at the point in the centre of the pixel as my program calculated it. That is to say, both images are as information perfect as you can hope for here, it’s just one is far more compelling to the average viewer. Information preservation is just not enough to engage and inform.