# Visualizing Polynomials in the Complex Plane

## Generating the Images

The source code for this project can be found at my GitHub: https://github.com/SSODelta/PolynomialVisualizer

There are many ways of visualizing numbers in the complex plane, as seen in my previous post on Newton’s Fractals. This post will detail another method of getting pretty pictures from equations.

The basic idea of this method is to visualize where each complex number is mapped to by some polynomial. The color will depend on the direction the complex number goes after having been through the polynomial, and the distance it travels. Alright, let’s get formal!

Like the post on Newton’s Fractals we start off with a polynomial of some degree. Call this $q$.

To generate a fractal, we first need to specify how large the image we need to make is, call this $[w, h]$, where $w$ is the width in pixels, and $h$ is the height (also in pixels). Next up, we have to specify which numbers we’ll use, so we decide on a resolution $r$ – we’ll only consider complex numbers $a + bi$, where $a,b \in [-r,r]$.

Now, we’ll iterate over every single pixel $p(x, y)$ in the image. Then, the associated complex number will be $C =i \cdot (\dfrac{2 \cdot r \cdot y}{h} - r) +$ $\dfrac{2 \cdot r \cdot x}{w} - r$. To make it more clear why this is the case, take a look at the real and imaginary parts individually:

• Re:  $-r + 2 \cdot r \cdot \dfrac{x}{w}$
• Im: $-r + 2 \cdot r \cdot \dfrac{y}{h}$

Anyway, we now have a complex number $C$. Now, we’ll calculate $D = q(C)$, where $q$ was the polynomial we chose. Next up, we’ll construct a vector $\vec{V} = <\mathbf{re}[C] - \mathbf{re}[D], \mathbf{im}[C] - \mathbf{im}[D]>$.

From this vector, we’ll calculate the angle between a vector pointing to the right of the screen – this is the just the $\mathbf{atan2}$ function of the vector. So, we get an angle $\theta = \mathbf{atan2}(\vec{V})$.

For the next part, we’ll start by calculating a value $\zeta$ $=$ $\dfrac{|\vec{V}|}{\sqrt{w^2 + h^2}}$, and two values $a = \sin(\zeta)$, and $b$ $=$ $\cos(\zeta)$.

We’ll treat these three values $\theta$, $a$, and $b$ as the three color components of the HSV-color type. We’ll now use the same conversion process as described in my post on psychedelic animations to convert HSV to RGB.

Then we’ll paint the pixel at $p(x, y)$ with the color information.

If we do this process for all pixels, we get impressive images:

To see an album with 78 of these images, check out my Imgur-album here: http://imgur.com/a/Ii5VM

## Analyzing the images

One of the first things I noticed with these images is obviously the yellow shape in the middle of the layers of circles. What I think is interesting is that if we project the yellow pixels in the central shape over the central x-axis of the image, they don’t encounter another yellow pixel.

Said in another way, if you cut the image horizontally and overlay the two halves, the yellow pixels in the central shape don’t touch. They are disjoint, in other words.

We can also specify this (somewhat) formally. If a pixel $p(x,y)$ is yellow (and in the entral blob), then $p(x, h-y)$ won’t be yellow.

The sad thing is that I don’t have the slightest clue why this happens. If someone smarter than I knows why the images look like they do, feel free to use the comment section down below to enlighten me!

## Making the images even cooler

If we have an image $I_ {pre}$ of size $[w, h]$ (the images we’ve generated), we construct a new image $I_{post}$ of size $[w, \dfrac{h}{2}]$. And for each pixel $p(x, y)$ in $I_{post}$, we paint it with color $I_{pre}(x, y)$ $\oplus$ $I_{pre}(x, h-y)$, where $I(x,y)$ represents the color information in image $I$ at position $(x,y)$.

My original idea was that this would make it evident that the yellow parts are disjoint when reflected over the central x axis, but it turns out it doesn’t.

Original Image $I_{pre}$:

Processed Image $I_{post}$:

Clearly, this does not convey much information about the central yellow  blobs, but it looks really, really cool.

Here are some more images that have been processed:

Original Image $I_{pre}$:

Processed Image $I_{post}$:

Original Image $I_{pre}$:

Processed Image $I_{post}$:

Unfortunately, I don’t have an album with lots of processed images, but if you want you can try running the code yourself.

## 3 thoughts on “Visualizing Polynomials in the Complex Plane”

1. Martijn

I was very happy to see this blog post. I did a good deal of dabbling around with making mathematical images like these (more like the pyschedelic animations ones actually) and I had some of the same problems.

The yellow patches might be happening because there’s a bug in your code or the input values to your colorizer are not in the range you expect. I say this because I had yellow patches in my HSV-based (or HSI-based, not sure) images, and it was caused when the input was out of bounds. I used a similar implementation based on the Wikipedia article, so I’d wager that you have a similar issue. Here’s how my hue-to-RGB converter function behaves as the hue crosses the expected maximum of 1.0:

([R,G,B], hue), order swapped for formatting reasons
([255,0,61],0.9200001)
([255,0,45],0.94000006)
([255,0,30],0.96000004)
([255,0,15],0.98)
([255,255,0],1.0)
([255,255,0],1.02)
([255,255,0],1.04)
([255,255,0],1.06)
([255,255,0],1.0799999)
([255,255,0],1.0999999)
([255,255,0],1.1199999)

See? it gets stuck on yellow.

You can actually see my yellow patches on my best image here:

I fixed the bug and re-rendered the image, but I actually liked the buggy version better, so that’s the one that I put on a T-shirt. :) I have some other stuff to say about this but I’ll say it on the psychedelic post.

2. The only problem here is that I’m using the sine and cosine functions to limit the hue to the range $[0, 1]$, so I highly doubt that’s the issue here :)

Personally, I had expected there to be a deeper mathematical explanation, but I’ll check with my implementation to see whether or not it’s broken. It’s just that I’ve used the exact same algorithm as in my psychedelic animations post, and I didn’t have any issues with it back then.

Your image looks really, really trippy, by the way. What algorithm do you use to generate it?

• Martijn

Hmm, I’d still check if I were you. On looking closer, my function failed to handle 1.0 correctly too (it renders as pure yellow), although that doesn’t explain why I had entire regions of pure yellow, instead of individual pixels that happened to be exactly 1.0.

Maybe there were multiple bugs at work for me. Regardless, check 1.0. I needed to add an epsilon if memory serves (although clearly I’m not remembering everything perfectly):

h = (hue + 1.0)*(180.0 – epsilon)

The algorithm was based on a college homework assignment, so I don’t want to post the whole source code (maybe I could be convinced), but the essentials are:

first, randomly generate a formula by selecting from the following options:

X
Y
SinPi e // sin(pi*expr)
CosPi e
AVG e, f // (expr1 + expr2)/2
Times e, f // e*f
Step e // round e to one of {-0.9, 0.9}
Extract e f // Shift the decimal expansion of f by n places, where
// n is the first digit of e after the decimal
// (this one makes the little squares – my friend wrote it)
Tri e f g // something wacky I made up
Mand e f // used to be mandelbrot with 100 iterations
// but that looked grainy and took too long so
//now it’s a crater shape

[TRI(a,b,c) = 1+(0.5*(a*b + b*c + c*a – a*a – b*b – c*c))]

The formula that produced that image is here:

Avg (Times (CosPi (Avg (SinPi (Tri (Avg (Avg (Extract X Y) (Times X Y)) X)
(CosPi (Avg (Extract X X) (CosPi X))) (Tri (Times (SinPi X) (CosPi X))
(Times (Times Y X) Y) (Times (Avg Y X) (Avg X Y)))))
(SinPi (Times Y (SinPi (Step (Avg Y Y)))))))
(Step (Avg (Step (Times (Times (Times (SinPi Y) (CosPi X)) (SinPi (CosPi X)))
(Avg (Avg (Extract X Y) (Times X X)) (SinPi (Times X X)))))
(Times (SinPi (Avg (Times (Avg Y X) (Avg X X)) (CosPi (Times Y X))))
(Times (SinPi (CosPi Y)) Y)))))
(Step (Times (Avg (Tri (CosPi (Avg (Tri (Avg X Y) (Times Y Y) (Avg X X))
(CosPi (Mandel X Y)))) (CosPi (SinPi (Times (SinPi Y) (Extract Y Y))))
(SinPi (SinPi (Times (Avg X Y) (Extract X Y))))) Y)
(Times (Times (CosPi X) Y) (Avg (Extract (Tri (Times (Times Y X)
(Times Y Y)) X (SinPi (Tri X X Y))) Y) Y))))