Julia Fractals

By Ralf Flicker.

image j2-2.png
Figure 1. Connected Julia set at c=(-0.7,-0.15i).

image j1-2.png
Figure 2. Disconnected Julia set at c=(-0.78,-0.15i).

image m1-2.png
Figure 3. Standard Mandelbrot set.

image m2-2.png
Figure 4. Detail from Fig. 3 enlarged.

This article looks at how the Julia and JuliaN variations in Oxidizer/flam3 relate to the classical Julia fractal, and what can be learned from the comparison. Note that this is not a tutorial about how to make the so-called "Grand Julia" fractals (but see Further Reading below).

The Julia sets

The theory of Julia sets was originally worked out by the French soldier and mathematician Gaston Julia (1893-1978) and published in 1918. The Julia set emerges from a quadratic iterator in the complex plane, of the form:
zn+1 = zn2+c,        (1)
where the variable z=x+yi becomes a function over the complex plane, and c is a constant (complex) parameter. The exponent "2" on the z variable is the polynomial degree, and the subscript n denotes the current iteration number. In computer terms (and this is how it is actually calculated), think of this procedure as having a two-dimensional array of complex numbers z: you square the whole array, and add the constant number c, and then overwrite the old z with this new array. Lather, rinse, and repeat for as long as you like. When you iterate this calculation, two things can happen:

  1. points in z either start growing without limit,
  2. or they remain bounded.

Based on this dichotomy, we can divide all points in the complex plane into two different classes, or "sets" as mathematicians call it: the escape set E of points that grow toward infinity, and the prisoner set P of the points that remain bounded. The Julia set is the boundary between E and P. This boundary is the fractal: it has built-in self-similarity, its dimension is generally fractal, and the dynamics are chaotic. See Fig. 1 for an example: the dark blue interior region is the prisoner set P, and all the rest (green, red, etc.) are part of the escape set E that will keep escalating toward infinity with every iteration. In practice of course one does not calculate points out to infinity, but only up to a certain (large) limiting value beyond which we can know for certain that the point must be part of E. We can then color code these points according to how long they took to reach this limiting value.

The Mandelbrot set

In 1979 Benoit Mandelbrot discovered that this simple quadratic iterator had some other interesting properties. He found that the Julia prisoner set P could be further characterized by a second property:

  1. those P that are connected,
  2. and those that are not.

The difference is easy to see by comparing the Julia fractals in figures 1 and 2. In the second figure, the Julia set no longer encircles a single connected P region, but has turned into a number of disconnected points (sometimes called "Fatou dust"). Since the shape of P depends on the parameter c, we might get the crazy idea to generate Julia sets for a whole family of c values, covering the complex plane, determine for each whether it is connected or not, and plot the results as an image. This is what Mandelbrot did, and obtained the image in Fig. 3 which is now called the Mandelbrot fractal.

It turns out the Mandelbrot set also has a fractal boundary, and it can also be calculated as a complex quadratic iterator
zn+1 = zn2+z0        (2)
by replacing the parameter c with a fixed array representing the complex plane (think of it as "doing all c values at once"). This iterator will also have an escape set, which we can color code as before. The prisoner set now represents the set of all c that have connected Julia sets, and it is this that is the actual Mandelbrot set.

Although the Mandelbrot fractal is not available as a variation in Oxidizer/flam3, it can still be useful to keep it in mind when working with the Julia variation. The Mandelbrot set serves as a roadmap to the Julia sets, and can tell us at a glance roughly what sort of Julia fractal to expect for different c parameters. Values of c far out in the Mandelbrot escape set E are going to be "dust", and c values deep within the prisoner set P are going to generate simple (and boring) connected geometrical shapes; for instance, the trivial case c=0 gives the unit circle. The complex region close to the boundary separating the Mandelbrot P and E is where the most interesting Julia fractals are going to be found. Fig. 4 illustrates this property by marking the two c values that were used for generating the two Julia sets in figures 1 (x) and 2 (x).
 

Julia variations in Oxidizer

Julia
a  = atan2(X,Y)/2;
if ( random_bit )
  a += PI;
r  = W * (X^2 + Y^2)^0.25;
Vx = r * cos(a);
Vy = r * sin(a);
Oxidizer does not compute the Julia fractal in the way just described, however. The iterated function system (IFS) that is the method of image encoding in Oxidizer/flam3 works in the real plane, not the complex plane. But in the case of the Julia fractal, it turns out that it is possible to convert it to a transform (or "variation") that can be used in Oxidizer. What we need is essentially the inverse of the quadratic iterator, that is:
z → (z-c)1/2.        (3)
Here z and c are still complex numbers, so the transform we need is one that emulates taking the square root of a complex number and mapping it onto the real plane. This is exactly what the Julia variation in Oxidizer does, as shown in the box on the right (formulas taken from the electric sheep wiki).

It looks slightly more involved than a square-root calculation because it is evaluated in polar coordinates, which is how it is most commonly done for complex numbers. The "random" part has to do with the fact that there are multiple solutions to the square-root of a complex number, and the chaos game should alternate between these randomly. More importantly (for us), we see that the Julia parameter c is not mentioned anywhere in this formula, which means that it has to be embedded into the (X,Y) coordinates before applying the variation. The only place to do that is in the affine part of the Xform, by a shift of the origin corresponding to the real and imaginary parts of the c parameter, as shown in Fig. 5 below (note the sign reversal of the x-component).
 

xform.png
Figure 5. Oxidizer Transform Editor. Origin shift corresponding to c=(-0.7,-0.15i).

Oxidizer-2.jpg Oxidizer-1.jpg
Figure 6. Oxidizer Julia renderings for c=(-0.7,-0.15i) (left) and c=(-0.78,-0.15i) (right).

Attractors

If we use Oxidizer to render the same Julia fractals as in figures 1 and 2, we get the rather disappointing looking images shown in Fig. 6 above. The problem (or "feature," depending on your view) is that the two types of calculations work in completely different ways. The original calculation (1) produces a visualization of the escape set E, which becomes the "artistic" aspect of the fractal. In Oxidizer we get more or less the opposite, where it is the Julia set itself that becomes visualized. The reason for this is that in the IFS formulation, the Julia set becomes an attractor.

An attractor in a dynamical system is a point or a set of points toward which the system converges over time. In the original Julia calculation, infinity is an attractor for the escape set E, and the prisoner set P may contain another attractor, but we normally get no visualization of it. In this system the Julia set is not an attractor, but an unstable fixed set that remains invariant under the iteration. When we invert this formula to obtain the inverse iteration (3), however, the roles are reversed, and the Julia set becomes an attractor for both E and P. This means that the chaos game is pretty quickly going to converge on the Julia attractor (that's what the chaos game does, it seeks out the attractors of the system), and therefore not produce many points in either E or P, which is why we get the rather boring and spindly looking fractals as in Fig. 6.

Generalized JuliaN

JuliaN (parameters: julian_power, julian_dist)
rN = abs(julian_power);
cn = julian_dist / julian_power / 2;
sina = sin((arctan2(Y, X) + 2 * PI * random(rN)) / N);
cosa = cos((arctan2(Y, X) + 2 * PI * random(rN)) / N);
r = W * Power(X * X + Y * Y, cn);
Vx = r * cosa;
Vy = r * sina;
Fortunately this is not the end of the story. Since we now know that in Oxidizer (and other IFS codes) it is the attractor of the system that is visualized, we just have to work on creating more interesting looking attractors, by adding more transforms and variations. But there are a few more points of interest while on the topic of pure Julia fractals. Flam3 also contains a generalized JuliaN variation, as shown in the box on the right. We see that:


The first point means that we can now visualize non-quadratic iterators (keeping julian_dist=1 fixed). For instance, setting julian_power=3 would reproduce the attractor of the cubic iterator
zn+1 = zn3+c,
which is similar to the original Julia set, but with a 3-fold symmetry structure instead. Setting julian_power=2 reproduces the original Julia variation. Choosing other values for the julian_dist parameter messes with the radial behavior, which in most cases turns the JuliaN variation into something that might not have any direct relation to a complex iterator, but can make very interesting attractors. One curious example is julian_dist=-1 and julian_power=1 which turns the JuliaN into a spherical variation! In case you feel like having two spherical variations in one Xform.

By playing with the julian_dist parameter one can discover "sweet spots" that produce dense and interesting looking attractors in a single Xform, which can be excellent starting points for designing more complex genomes. I close this article with two such examples that I came across by accident (or rather, thanks to the QuickView feature in Oxidizer, which makes it easy to explore ranges in the parameter space). Enjoy!

Oxidizer-3.jpg Oxidizer-4.jpg
Figure 6. JuliaN variation with julian_dist=-0.9375, julian_power=2, and O=(0.8,0).
Left: pure JuliaN in a single Xform. Right: second Xform added to fill in the pattern.

Oxidizer-5.jpg Oxidizer-6.jpg Oxidizer-7.jpg
Figure 7. JuliaN variation with julian_dist=-1.75, julian_power=4, and O=(0.7,0).
Left: pure JuliaN in a single Xform. Center: one xform added. Right: two xforms added.

Further reading

There are no comments on this page. [Add comment]

Valid XHTML 1.0 Transitional :: Valid CSS :: Powered by WikkaWiki . Install WikkaWiki