Here’s another famous fractal that I wanted to try on Arduino: the * Newton fractal*. It’s named after Isaac Newton’s iterative method for finding roots of functions. The classic example applies the method to function

*, where*

**f(z) = z**^{3}– 1*z*is a complex number. This function has three roots in the complex plane.

The sketch that produced this picture loops over the pixels of a 480×320 display, mapping each of them to a complex number *z _{0}*, that will serve as the initial value for Newton’s iteration process:

*z _{n+1} = z_{n} – f(z_{n}) / f'(z_{n})*

The basic *color* of a pixel (red, green or blue) depends on to which of the three roots the process converges. Its color *intensity* depends on the number of iterations that were needed to reach that root within a pre-defined precision. It’s just that simple!

Apart from playing with different color mappings (always essential for producing visually appealing fractals), I wanted to use modified versions of Newton’s method, as well as to apply them to different functions. The Arduino core has no support for complex calculus, and a library that I found didn’t even compile. So I wrote a couple of basic complex functions and put them in a *functions.h* file. There must better ways, but it works for me.

Once you have a basic sketch, the canvas of complexity is all yours!

**f(z) = z ^{4} – 1**

*This is my functions.h file. It must be in the same folder as the fractal sketch.
*

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 |
struct complex { double im; double re; }; struct complex create(double real, double imag){ struct complex t; t.re = real; t.im = imag; return t; }; struct complex cpow2(complex z){ struct complex t; t.re = z.re*z.re - z.im*z.im; t.im = 2.0*z.re*z.im; return t; }; struct complex cpow3(complex z){ struct complex t; t.re = pow(z.re,3) - 3.0*z.re*z.im*z.im; t.im = 3.0*z.re*z.re*z.im - pow(z.im,3); return t; }; struct complex cpow4(complex z){ struct complex t; t.re = pow(z.re,4) + pow(z.im,4) - 6.0*z.re*z.re*z.im*z.im; t.im = 4.0*(pow(z.re,3)*z.im - pow(z.im,3)*z.re); return t; }; struct complex cadd(complex c, complex z) { struct complex t; t.re = c.re + z.re; t.im = c.im + z.im; return t; } struct complex csubs(complex c, complex z) { // c minus z struct complex t; t.re = c.re - z.re; t.im = c.im - z.im; return t; } struct complex cmult(complex c, complex z) { struct complex t; t.re = c.re*z.re - c.im*z.im; t.im = c.re*z.im + c.im*z.re; return t; } struct complex cdiv(complex c, complex z) { // c divided by z struct complex t; t.re = (c.re*z.re + c.im*z.im)/(z.re*z.re + z.im*z.im); t.im = (c.im*z.re - c.re*z.im)/(z.re*z.re + z.im*z.im); return t; } double cabs(complex z) { return sqrt(z.re*z.re + z.im*z.im); } |

*And here’s the Newton fractal sketch. Note the #include “functions.h” on the first line.
*

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 |
#include "functions.h" #include <SPI.h> #include "Adafruit_GFX.h" #include "Adafruit_HX8357.h" #define TFT_CS D8 #define TFT_DC D1 #define TFT_RST -1 Adafruit_HX8357 tft = Adafruit_HX8357(TFT_CS, TFT_DC, TFT_RST); #define BLACK 0x0000 #define BLUE 0x001F #define RED 0xF800 #define GREEN 0x07E0 // r1, r2 and r3 are the complex roots of z^3 - 1 = 0 struct complex r1 = create(1.0,0.0); struct complex r2 = create(-0.5,0.8660254038); struct complex r3 = create(-0.5,-0.8660254038); struct complex real1 = create(1.0,0.0); struct complex real3 = create(3.0,0.0); int max_count = 128; float precision = 0.0001; int col_factor = 14; struct complex z; void setup() { tft.begin(HX8357D); tft.setRotation(3); // landscape tft.fillScreen(BLACK); yield(); for(int y=0;y<320;y++) { for(int x=0;x<480;x++) { int count = 0; z.re = (x-240.0)/80.0; // -3 <= z.re < +3 z.im = (y-160.0)/80.0; // -2 <= z.im < +2 while( (count < max_count) && (cabs(csubs(z, r1)) >= precision) && (cabs(csubs(z, r2)) >= precision) && (cabs(csubs(z, r3)) >= precision) ) { if(cabs(z) > 0) { z = csubs(z,cdiv((csubs(cpow3(z), real1)),(cmult(cpow2(z), real3)))); } yield(); count++; } unsigned int kleur = max(0,255-col_factor*count); if (cabs(csubs(z, r1)) < precision) tft.drawPixel(x,y,rgb24to16(0,0,kleur)); if (cabs(csubs(z, r3)) < precision) tft.drawPixel(x,y,rgb24to16(kleur,0,0)); if (cabs(csubs(z, r2)) < precision) tft.drawPixel(x,y,rgb24to16(0,kleur,0)); yield(); } yield(); } } void loop() { delay(100); yield(); } unsigned int rgb24to16(byte r_value, byte g_value, byte b_value) { unsigned int tint = ((((r_value>>3)<<11) | ((g_value>>2)<<5) | (b_value>>3))); return tint; } |