Xaos-like Fractal Generator

My semester project in Computer Science Principles was to create a program in Berkeley’s “Snap!” programming language, which we had learned to use in this semester. It needed to be more complex than our previous projects, and also have some social significance.

I decided to create a simplified and readable demonstration of the XaoS algorithm.

GNU XaoS is perhaps the fastest open source iterative fractal generator in the world. It inserts new rows and columns of pixels to refine the image without repeating any old calculations. My program demonstrates how this can be accomplished in a simple way.


Slide2



Slide3



Slide4



Slide5



Slide6



Slide7



Slide8



Slide9


The project (fully interactive) can be downloaded here:
https://github.com/JohnDorsey/FractalGeneratorFA2017

Then it can be run by uploading it to Snap:
snap.berkeley.edu/

Click the green flag (or green lightning bolt) above the stage to start the program.

Press and hold the Q key to add rows and columns.

use W/A/S/D to pan and X/C to zoom. Press these just once, without holding them down.

New style of programming / Langton’s Ant

2 days ago, I imagined the programming styles that would be possible with a ‘can’ statement, which allows methods of a class to be declared outside of a class at any time during execution. My goal was to allow programs to be written in the order that you have new ideas, or in the order that they are explained in a summarized writing.

I outlined two python projects of identical function: a normally structured project and a can-statement project. The function of each project was to simulate and show Langton’s Ant.

original version:

#there is a Board class
# a Board can prepare(ruleset) its Ants' AI
# a Board can advance() its Ants
# a board can inform & control its Ants with step()
# a Board can display() itself
#there is an Ant class
# an Ant can move()
# an Ant can turn()
# an Ant can bound()
#there is an AI class
# an AI can decide(condition) how to turn
# an AI can influence(condition) the current square
#create a Board called "world"
#tell world to prepare(ruleset)
#loop:
# tell world to advance()
# tell world to display()

‘can’ statement version:

#there is a Board class.
#there is an Ant class.
#an Ant can move()
#an Ant can turn()
#there is an AI class.
#an AI can decide(condition) ho to turn
#an AI can influence(condition) the current square
#Boards can prepare(ruleset) their Ants' AI
#an Ant which is the child of a Board can step()
#an Ant which is the child of a Board can bound()
#a Board can advance() its Ants
#a Board can display() itself
#create a Board called "world"
#tell world to prepare(ruleset) its Ants.
#loop:
# tell world to advance()
# tell world to display()

It might be good for drafts, but it isn’t helping if it causes you to delay memorization of the structure of the project. It also becomes messier and harder to jump into. Perhaps it can be used to write first drafts of classes, and then converted to regular python before classes get very large.

In combination with ‘can’ statements, the example uses ‘of’ statements, which allow methods to be created in the context of both an object and its parent.

Ant of Board can Bound(self,parent):
  self.x %= parent.width
  self.y %= parent.height

 

I took both outlines and created real python projects out of them. The ‘can’ statement version isn’t runnable, but for a while, I updated it to fix errors I found in the working version. It would work if I could compile it. I’ll clean up both projects and publish them soon.

langtons-ant-LR-2-highway-nearWrap-clean
My program’s output after running the first 11208 iterations of the basic LR Langton’s ant.
The board wraps around, so the Ant isn’t able to build an infinite highway.

Projecting features from Seahorse Valley with a Buddhabrot generator

In my earlier experiments, I placed the starting points of a Buddhabrot generator along a line through the Mandelbrot set. The points starting below the real axis produced a mirror image of those that started above, and a lot of detail was wiped out when the two images were blended. From now on, any vertical lines will be cut off at the real axis, and the asymmetry will be visible.

Here’s the non-mirrored Buddhabrot of seahorse valley:

verticals-focus-quarter-x2560-128
Starting points placed along -0.75

(This is the same coloration method as was outlined in Vertical slices of the Buddhabrot)

When moved slightly to the right, the line of starting points begins to cut into the seahorses and into the cardioid itself. The Buddhabrot then forms the image of a seahorse Julia set.

verticals-focus-quarter-x2560-129
Starting points lined up at -0.75+1/4096  (-0.749 755 859 375)

 

Most pixels in the image are pure black, so I wanted to interpolate all color data from the non-black pixels before starting to refine the image.

I will eventually automate this, because I know I’ll want to do it again. But for now, I’m working with Photoshop. I deleted all black pixels, and tessellated the remaining pixels to fill the gaps. I did this by duplicating the layer 5 times, arranging the duplicates into a tiny pinwheel, merging them, and repeating.

manual-tesselation-demo
First 2 steps of manual tessellation
tesselation-final-product
The result after 5 steps

When the green channel is divided by the red channel, it yields the original measurements of progression of a point.

red-green-div
Luminosity = progress towards escaping

Perhaps the most interesting thing about this fractal is that it isn’t simply composed of points that started very near the real axis. The blue channel divided by red is not very distinct:

red-blue-div
Luminosity = closeness of starting point to the real axis

 

I’ll continue to work on explaining exactly how this type of fractal can be found, and also, how different it is from the basic seahorse Julia set.

seahorse-julia-075-005
C=-0.75+0.05i: Seahorse valley Julia set, generated with XaoS.

Vertical slices of the Buddhabrot

I read somewhere recently – as you approach the real axis of the Mandelbrot set near -0.75 (seahorse valley), the number of iterations before a point escapes, multiplied by the distance to the axis, approaches pi.
It’s a cool fact about the Mandelbrot set. but it isn’t a practical way to calculate pi; it takes a 2*10^n calculations to calculate n digits.

I wanted to know what points in the Mandelbrot set actually did in this area. So I rendered a Buddhabrot with starting points placed along a vertical slice at -0.75:

pizone-x3840-image

This is zoomed out a ways. Here’s the Mandelbrot set at the same zoom level for reference:

directRasterTest-x240-image

Each line represents a common iteration level for the points of the starting line. each line is completely continuous, because the input function is continuous and the iterated function is too.

As soon as I saw this, I decided to do vertical slices of the entire set.

In the following images,

  • The blue channel indicates how close to the real axis a point started. The bluer it appears, the smaller the imaginary component of c is.
  • The green channel indicates how far a point has moved through it’s lifetime. If a point takes 500 iterations to escape, it will be drawn with 80% green channel brightness on its 400th iteration.
  • The red channel is the reference for the others – it is simply brighter when a pixel is visited more often, and represents the highest value any of the other channels could be for that pixel. (At rasterization time, other channels are multiplied by red and divided by 255.)
verticals-add-sr-x1280-213
slice 213/1280 (near the end of the spire)
verticals-add-sr-x1280-336
slice 336/1280 (through the spire, near a mini-Mandelbrot which is causing the lines to curl)
verticals-add-sr-x1280-476
slice 476/1280 (through the period 2 bulb, just to the left of seahorse valley)
verticals-add-sr-x1280-485
slice 485/1280 (through the cardioid, just to the right of seahorse valley)

This sequence could be turned into an animation, but it’s quite large for a gif, So I’m looking to post it in a video. I also have several other discoveries to write about soon.

Mandelbrot set experiments

Over spring break, I decided I still wanted a better understanding of how points in the Mandelbrot set moved. I also wanted a more flexible fractal generator than my current one.

So I stripped my ‘Mandelphone’ project down to nearly nothing, and rebuilt it into yet another program that can’t be moved back to my phone because it includes Pygame. I hope to clean up my code and post it here soon.

one of my first experiments was to generate the Buddhabrot; Not to answer my real questions, but as a topic for my code – a way to work out a good design. I settled on a large class FractalPanel with several global parallel arrays, and switches to enable/disable certain actions on them.

My explanation (excuse) for using many parallel arrays is that I thought it was faster. After asking about python’s performance in such situations on Quora, I got a clear and concise answer from Simon Lundberg: pointer dereferencing overhead is negligible, because nothing in python is ever in the cache. The next thing I do will be to refactor everything.

But many things will stay the same. The two most important arrays are bucket[][] and raster[][].

Bucket[][] stores the complex starting positions for every pixel in the screen. By storing them in an array, I can easily update them (e.g. to iterate them all and show where every point is after n iterations, then n+1, then n+2, whatever.)

Any methods I have for drawing an image write their results to raster[][] which is copied to a pygame.Surface and drawn at the end of a frame. I have separate methods to rasterize a Buddhabrot from the starting coordinates in bucket[][], or to rasterize the coordinates in bucket[][] itself in various ways.

I started by creating a simple Buddhabrot:

BB-sequenceB-0
The Buddhabrot

These images of the Buddhabrot represent all pixels visited by every Z which started at a coordinate in the bucket. To get more starting points for a more detailed image, I simply iterated the points in the bucket, to get new buckets. (While the images show only the motion of unbounded points, the bucket of starting positions contained both bounded and unbounded points. And YES, as they travel, many bounded points regularly visit coordinates that aren’t bounded when used as starting positions.)

Near the end of the sequence of images, the Buddhabrot is much dimmer, as many unbounded points in the bucket have escaped and aren’t being used as starting points anymore:

BB-sequenceB-423
Buddhabrot after 423 iterations of the bucket.

Finally, I combined my 430-frame PNG sequence into a single image:

BLENDED_4_BB-sequenceB-
A composite of all images in the sequence

Coloration:

  • red = percentage of the time a pixel is ever occupied.
  • green = root-mean-square of pixel brightness, multiplied by 2.5
  • blue = mean of pixel brightness, multiplied by 1.5

The original images were colored using an Arctangent normalized to 255. Here’s the relation between times visited and luminosity:

conversionRate
input is horizontal (it wraps around), output is vertical (distance from top)

I regret how much information is lost by the steep increase in output brightness for single-digit inputs, but my goal was for such points to be visible to the naked eye, with no need to brighten the image in Photoshop. And there’s little need to extract actual numbers from these images, as it would be faster to just regenerate them.

Using points visited by bounded paths as the starting points for unbounded ones gave me some interesting patterns that I’ve never seen before, which I might continue to investigate. but honestly, I’ve always felt that the Buddhabrot was like looking for shapes in the clouds – it doesn’t provide much insight into how points are actually moving. The Anti-Buddhabrot accomplishes this better, I feel, because it helps you see the periodicity of densely traveled paths. (the Anti-Buddhabrot consists of the pixels visited by all bounded points)

fold-sequence-A-periodicity-demo-x1280
iterations 167-170 of a composite Buddhabrot and Anti-Buddhabrot, which is basically just an Anti-Buddhabrot by now because most of the points in the Buddhabrot have escaped

The left bulb, with a periodicity of 2, moved can be seen moving in and out of the cardioid in a simple 1 – 2 – 1 – 2 fashion. The top and bottom bulbs, however, are moving in a triangle. 1 – 2 – 3 – 1.

I wanted to see more of this, which led me to my next experiment.

Drawing lines instead of points.

lineFixTest-sequenceA1-x1920-359
Iteration 359: The cycles from many major bulbs suddenly line up! They simultaneously exit the cardioid and return to the area they started in.

The above image is simply the paths taken by all points as they transform from this:

bucket-sequenceA-358
Rasterization of all points at 358 iterations

…to this:

bucket-sequenceA-359
Rasterization of all points at 359 iterations

In fact, in the image of all points at 358 iterations, two bulbs can be seen that haven’t coincided with all the others. Their movement during the 259th iteration forms the two lines that are perpendicular to the spokes of the linear movement image, near the left of the cardioid.

Mandelbrot path illustrations

A couple months ago, I used my Mandelbrot generator to create several animations. The purpose of these animations was to gain a better understanding of the paths that an iterated point takes before escaping. I knew that they didn’t travel very far around the number plane – but besides that, I didn’t know much. This project shows how the Mandelbrot set behaves when the escape threshold is not a circle.

The ideal tool to understand how the points move would be a Buddhabrot generator that can limit displayed paths to show only the points around the mouse pointer. I’m still interested in doing that, but this provides other useful information.

In all of these image, yellow is a low escape angle, magenta is a high escape angle, and higher iteration counts shift the color through blue, green, and red. This is very faint around the edges of the set. Whenever I would need to divide by zero to find the escape angle, I instead used magenta – this resulted in the dark lines through the middle of yellow regions.

escapeshapeelipserot

In the rotating parabola experiment, I mainly wanted to see how far a point could travel in a straight line. If I used two planes as the escape shape, certain points would never escape (any point where i=0 for example.) The parabola guarantees that points will escape eventually. In this test I actually rotate the points before testing, while the parabola stands still – but for the same effect.

The parts of the set that are near the center of the parabola are the most defined, taking the longest to escape. Each shape within the parabola has a purple strip which is offset from the purple strip in the next shape. To me this implies that the longer a point is iterated, the larger the step that finally takes it out of the parabola.

escapeshapeexp

I created several other animations with different escape shapes. They demonstrate another property of the Mandelbrot set – No matter how you define “escaped,” as long as it is outside of a radius 2, no new region added will join the opposite sides of the set; The set remains connected.

escapeshapehyp

escapeshapehyprot

escapeshapesquarerot

The set contains the same points no matter what the escape shape is – so using a square escape is actually a decent shortcut to remove the distance equation from each iteration.

Pygame mandelbrot

I’d like to share my progress on a new project. I’ve had a couple of breakthroughs, but I haven’t found the time to post them. I’ve created a Mandelbrot generator in python. It was based on the successful ASCII-output Mandelbrot generator, and it still uses a console window to receive input.

It started with the ancient tradition of using screen coordinates as color values because your code is producing a blank screen and you don’t know why.

screenshot_2016-11-08_09-35-43
(using x+y as a color index)

This was also a great way to test my color palette, which I eventually refined into a very simple Red->violet->Blue->Green sequence that I’ve never seen in another generator:

MandelPy-3-color-palette

MandelPy-3-color-palette-2

I’ve performed many experiments with this generator, which I hope to post someday. I’ve replaced the input handler with a loop that modifies certain parameters, and I’ve produced some attempts at a slowing expression that uses z -> z^(m+1) + (m*C) with m moving from 1 towards 0 as |z| increases. The point of that is to produce a final resting place of the process without using a simple escape threshold or limit. It cuts off when m drops below a certain value. Here’s what that looks like:

mandelpy-m-decays-then-angle-is-found
Color = termination angle

I’ve also created some sequences with varying escape threshold size and shape, to help demonstrate the areas that certain groups of points mostly live in. Here’s a frame from a sequence with an expanding threshold:

mandelpy-animated-expanding-threshold-crucifix-angle-iter
Colored with angle and iteration count

I’ll convert these PNG sequences to GIF’s and upload them here soon.

Scratch Mandelbrot Again

I love Scratch, and Python. They are so slow, yet they are so fast to develop with… When I started experimenting with c++, it was weeks before I even saw my fractal on the screen. (then it was calculated about 4,000 times as quickly as these images were)

The scratch Mandelbrot generator series isn’t meant to be fast. It is ideal – It will have all of my favorite features. It will allow me to test new mathematical concepts easily (more of this later). It may be a place to draft these ideas before I implement them in Java or something.

I started by improving the colors in Mandelbrot 2. This was a huge task, because I acted on the misconception that the full color wheel of the pen was only available in scratch projects created after a certain date – so I re-created the entire program in another project.

I later disproved this theory about the pen, and I can’t really come up with a new one. it breaks pretty easily if I change the color with a numeric value before a using a color constant, or within too short a time after starting the project, and in other odd situations.

screenshot-2016-10-21-at-17-26-46
https://scratch.mit.edu/projects/126896952/

I focused mostly on improving efficiency and readability – I got rid of the global “running” variable, and made it possible to stop and start the program without restarting generation progress.

screenshot-2016-10-21-at-17-08-09

But all of this is moving towards an ultimate goal of inventing new ways of visualizing and measuring the Mandelbrot set and other Julia sets. I’ve dreamed of creating a set without edges of any kind. and not just by blurring the lines like Xaos does… I want a set that’s not quantized at all.

and angular coloring seems like a good place to start. You can see this is the header image at the top of my site, which I generated with Xaos.

Here’s me using atan(zi/zr) and atan((zi-zio)/(zr-zro)) as the color:

screenshot-2016-10-28-at-09-03-44-edited-angularscreenshot-2016-10-28-at-09-50-47-edited-angular

The second image has the angular result multiplied by 0.2, and added together with the iteration count. This gives the glow around the edges of the set.

Here’s when I simply use zi as the color:

screenshot-2016-10-29-at-09-38-12-edited-zi

The yellow escapes above the real axis, the purple escapes below the real axis.

All of this has been done before. In fact, Xaos combines it all into one application. I’m going to try something new. This is the beginning of an attempt at an unquantized set…

When a group of points is moving towards a higher iteration level, their escape point is only slowly dropping towards the origin. When they finally drop below escape radius, and the equation must be iterated one more time, the new escape point is pretty far away – and the set is quantized no matter what math you use on the escape point.

So I’m going to measure the real point of escape: where the last line segment formed by the last two points crosses the escape circle.

Here’s my implementation of the quadratic formula:

screenshot-2016-10-29-at-11-23-59-edited

Where zr is the real component of the escape point, zi is the imaginary component, and zro and zio are the previous point.

Where m is the slope of the escape line segment, and b is the y intercept of the escape line segment, and A, B, and C are the three coefficients of the quadratic formula.

A more detailed explanation of the math is embedded in the project.

https://scratch.mit.edu/projects/128071393/

Before I started testing this math, I made a few tweaks to allow the generation other Julia sets. So here’s a Julia Set from the seed -0.3-0.9i

I went from this:

screenshot-2016-10-28-at-19-53-07-edited-angular

to this:

screenshot-2016-10-28-at-19-55-01-edited-exact

The yellow strips, corresponding to higher angles relative to the origin, now tilt towards the nearest tips of the prominent features of high iteration count.

I would characterize good outcoloring as that which helps you identify features you would have otherwise missed. So I can’t wait to test this algorithm in a compiled language.

screenshot-2016-10-28-at-17-18-35-edited-angular

screenshot-2016-10-28-at-17-15-24-edited-exact

It really reminds me of a block of ray-traced ripply glass inside a red and yellow world.

screenshot-2016-10-28-at-20-05-31-edited-angular

screenshot-2016-10-28-at-20-01-22-edited-exact

Now that I have two new outcoloring modes, I’ll divide one by the other just for giggles:

screenshot-2016-10-28-at-20-11-10-edited-quotient

Obviously, none of these are images of a truly unquantized set. This is because a point from an area which almost escapes will still travel quite a distance upon their next iteration. And, rather than gracefully leave the escape radius at a point not far along the next line segment (as they would if the next line segment had nearly the same slope), they fall back away from the escape circle at an acute angle – actually dropping in absolute value before they escape a couple radians away.

I have a few more ideas.

I’m also taking major steps in the direction of using clones for a multithreaded approach, as well as rendering a simple Julia set based on the context of the Mandelbrot set.

You can view all of my code here

Mandelbrot Set in Python

On the 20th of July 2016, I visited Cedar Point.

 

Near noon, I found myself standing in line with an estimated 1:15 wait. And I was bored. Lucky for me, I had just installed this neat android app called QPython that allowed me to work in python on my phone.

Before the sun set on that day, I created a Mandelbrot generator. I continued to refine it until almost midnight. I was nearly out of data, so I created this only with the knowledge I had gained at Operation Catapult. Some of my solutions are crazy.

class FractalViewer:

    def __init__(self):
        self.raster = []
        self.raster = ""
        self.pos = (0.0,0.0)
        self.zoom = 0.22
        self.ar = 2
        self.res = 48
        self.ycro = 14
        self.escape = 9.0
        self.limit = 128
        self.chars = ['.', '-', '=', '#', '&']
        self.charScale = 4

    def resolveDisplay(self):
        self.raster = ""
        for y in range(self.ycro, self.res-self.ycro):
            self.raster += "\n["
        for x in range(self.res * self.ar):
            self.raster += (self.getPixel((x,y)))
            self.raster += ']'

    def display(self):
        print(self.raster)

    def getPixel(self, xy):
        ri = self.getCoordsOf(xy)
        return self.toChar(self.solvePoint(ri))

    def getCoordsOf(self, xy):
        return(((((xy[0]/(1.0*self.res*self.ar))-0.5)/self.zoom)+self.pos[0],(((xy[1]/(1.0*self.res))-0.5)/self.zoom)+self.pos[1]))

    def solvePoint(self, pt):
        z = [pt[0], pt[1]]
        c = [pt[0], pt[1]]
        for iter in range(self.limit):
            z = [z[0]**2-1*z[1]**2+c[0],2*z[0]*z[1]+c[1]]
        if (z[0]**2 + z[1]**2) > self.escape:
            return iter
        return self.limit + 2

    def toChar(self, value):
        return self.chars[int((value / self.charScale) % 5)] if value < self.limit else " "

fractView = FractalViewer()
sensZoom = 0.5
sensMove = 0.25

while (1==1):
    fractView.resolveDisplay()
    fractView.display()
    print("move:wasd; zoom:tu; zoom scale:op; move scale:kl; new iter count:i###")
    control = (input(">"))
    test = control[0]
    if test=='i':
        fractView.limit = int(control[1:len(control)])
    else:
        for index in range(len(control)):
            test = control[index]
            if test=='u':
                fractView.zoom *= 1 + sensZoom
            elif test=='t':
                fractView.zoom /= 1 + sensZoom
            elif test=='w':
                fractView.pos = (fractView.pos[0], fractView.pos[1] - sensMove/fractView.zoom)
            elif test=='a':
                fractView.pos = (fractView.pos[0] - sensMove/fractView.zoom, fractView.pos[1])
            elif test=='s':
                fractView.pos = (fractView.pos[0], fractView.pos[1] + sensMove/fractView.zoom)
            elif test=='d':
                fractView.pos = (fractView.pos[0] + sensMove/fractView.zoom, fractView.pos[1])
            elif test=='k':
                sensMove -= 0.05
            elif test=='l':
                sensMove += 0.05
            elif test=='o':
                sensZoom -= 0.05
            elif test=='p':
                sensZoom += 0.05

 

features:

  • move and zoom
  • adjustable sensitivity for movement
  • adjustable sensitivity for zoom controls
  • input multiple controls before calculating
  • set any iteration limit with the control i###
  • low memory usage
  • pixels come in 5 vibrant colors:   .  –  =  #  &