__Previous Results__

Previously, my best 3D DLA movies were these two;

I had two main issues with those movies. Firstly, I used software based OpenGL which does not support shadows or any advanced lighting techniques. Secondly, I was launching the moving particles on the surface of a sphere that was biased to the poles, which meant the resulting growth tended to grow more in the “up” and “down” directions rather than a nice spherical shape. This is especially noticable in the first movie above that grows more in the up direction than the other directions.

__64-bit__

Now that Visions of Chaos is 64-bit I wanted to push the limits of the DLA animations even further. In the past the 32-bit executable limited me to around 3GB of usable memory which restricted the size of the arrays that I could grow DLA structures within to around 750x750x750. With a 64-bit executable Visions of Chaos can now use as much memory as the PC has available.

__Bugs in old code__

I have been experimenting with 2D and 3D DLA for many years now and my exisiting code reflected that. When I started expanding the grid size and particle counts I was getting strange crashes/hangs/poor behavior. As any programmer knows, you think “this will be an easy fix”. Well, 3 or 4 days later after trying to fix these simple bugs and rendering lengthy test runs I was ready to chuck my PC out the window. Having to wait a few hours to see if a fix works or not really tests the patience. In the end I bit the bullet and woke up one morning and rewrote the code from scratch. It took a few hours and I now have a much more stable and faster version. I also added support for Mitsuba so I get nicely shaded images. Back on track.

__Latest results__

With the extra memory available from a 64-bit Visions of Chaos executable and being able to use Mitsuba to render each frame it was time to let my PC churn away for a few days rendering DLA frames. The few days expanded into a few weeks as I kept tweaking settings and code and started to render the DLA movie parts again from scratch. But finally, the following movie was complete.

I only have 32 GB memory in my main PC so those sample movies run out of RAM when around 10 million particles are reached. This is double the maximum particles I achieved in the past. I need to look at maxing out my memory to 128 GB so I can create even larger DLA structures.

Something to observe in that movie is how once the particle count gets beyond around one million the overall structure remains the same as it continues to grow. This is a great example of the self-similarity properties of fractals. With more memory and more particles the overall structure would not suddenly change beyond 10 million particles. The individual spheres would shrink into sub pixel sizes, but the overall growing shapes would remain self-similar (as render times continued to increased per frame). This is also noticable in most fo the sample images in this post.

__Some DLA coding tips for programmers__

If you are not programming 3D DLA then this next bit will be of little use to you and you can feel free to skip it.

When launching particles into the DLA space use random points on a sphere surrounding the current growth. If you use random points on a cube surrounding the growth it will be biased to grow along the axiis. Using a sphere helps maintain a more spherical growth. Set the radius of the sphere slightly larger than the furthest distance the dla structure has grown. ie if the furthest particle is 10 units of length from the origin, then set the sphere size to 13. Also make sure you use an even distribution of points on the sphere surface. My original code was biased to the poles and this is why the first sample movie above grows towards the up/north direction more than evenly in all directions. Some quick code to evenly distribute random points on a sphere is

```
theta:=2*pi*random;
phi:=arccos(1-2*random);
px:=round(sin(phi)*cos(theta)*launchradius)+centerx;
py:=round(sin(phi)*sin(theta)*launchradius)+centery;
pz:=round(cos(phi)*launchradius)+centerz;
```

The usual DLA method is to wait until a moving particle goes “off screen” before a new particle is launched. For 3D, off screen is the array dimensions. Waiting for particles to move off the existing DLA grid can really slow down the process (especially for large sized grids). Rather than wait for the edge of the grid, use a few grid units distance beyond the launch sphere radius. So if the launch sphere is radius 13 then moving particles get discarded if they travel more than 16 distance from the origin.

Calculating distances involves costly sqrt calculations. If you are doing a sqrt each time the particle is moving they quicly add up and slow down the simulation. To speed up distance calcs I fill an array once at the start of the simulation that contains the distance from the origin (grid center) to each array location. This makes it much quicker when you want to know how far a moving particle is from the origin. All it takes is a single array lookup rather than a sqrt distance calculation.

Another thing you want to know for moving particles is how many neighbor particles is it touching. For example if the current settings make a particle stick to 3 or more existing neighbors then you usually do a quick loop of neighbor cells adding them up. Again, doing this every time the moving particles move adds up and slows everything down. I use another array that holds the neighbor count for each grid location. When a new particle attaches to the existing DLA structure, you add 1 to the surrounding cells in the neighbors array. Much faster.

If you are rendering a very dense DLA then there will be a lot of particles within the middle that remain hidden. Doing a quick check to see if a particle is surrounded (ie using the above neighbors array means if neighbors=26) means it can be skipped and not sent to Mitsuba for rendering. On the densest DLA structures this cut down the number of spheres passed to OpenGL and/or Mitsuba to only 7% of the total spheres. A huge speed up in terms of time per frame.

You need to auto-increase the hits per update each frame. ie if you set the display to update every time 10 particles get stuck to the growing structure it will look fine at the start, but once the structure starts growing you will have a long wait to get to a million particles and beyond. I used a simple formula of increasing the stuck particles per frame as the number of total stuck particles div 40. Once you get to 100000 particles, keep the update at 25,000 particles per frame. This allows a nice smooth increase in stuck particles and gets you to the more interesting million+ range quicker.

Using the above tricks you will find the particle movements and sticking part of the code takes seconds rather than minutes per frame. The main slowdown is now in the display code.

Jason.

]]>

Back then a video with a resolution of 240p (426×240 pixels) like the following was considered OK.

These days I can upload a 4K video (nine times the vertical and horizontal resolution of that 240p video) and once YouTube puts it through their internal conversion routines it will usually come out looking excellent.

Jason.

]]>

Wenzel is one of the co-authors of the seminal PBRT book, so he knows his stuff. Mitsuba uses an XML file format for the scene files that you can pass to the renderer as a command line parameter. This makes it easy for me to build a compatible XML file and get Mitsuba to render it each frame.

Here are some sample 4K images created with Visions of Chaos and rendered with Mitsuba using the constant lighting model. Constant lighting means that light is simulated hitting surfaces from all directions evenly. This means there are no shadows, but crevices within structures and corners are shaded darker because of ambient occlusion.

Using Mitsuba really gives clean, nicely shaded results and the examples above only using the most basic Mitsuba lighting/material setups. Mitsuba has handled the multi-gigabyte sized scene files with millions of spheres and/or cubes scenes with ease. All the end user needs to do is download/unzip Mitsuba and point Visions of Chaos to the main executable.

Jason.

]]>

__Physics Engine__

This time I am using the Kraft Physics Engine by Benjamin Rosseaux. Like Box2D in 2D, Kraft handles all the 3D object collisions and joints for me.

__Types of starting creature setups__

The virtual creatures are based on 3D boxes joined together by motor driven joints. Nothing more complex is required. No neural nets, no sensors, just a series of connected body parts with joints rotating at various angles and speeds. Even with these few simple parts some interesting behaviour can occur.

For my experiments I used the following base creature types.

Snake – A series of 2 or more body segments lying in a row connected by joints. Segment sizes and joint directions and strengths are all random.

Cross – A body with 4 arms linked out in the X and Z directions.

Jack – A body with arms linked out in all 6 XYZ directions. Named because the initial layout shape is like the pieces in the old jacks game.

__Building an initial gene pool__

To evolve creatures you need a set of existing creatures to mutate and evolve. To create a gene pool I repeatedly create random creatures (random body segments, segment sizes, joint speeds etc).

The creatures are then dropped onto a virtual ground and monitored over a set amount of time. If the creature stops moving or explodes (happens if the physcis engine math goes crazy) it is ignored. If it survives for the amount of testing time it is added to a temporary list. The search can be left to run for hours or overnight. When the search is stopped the best 10 are found and saved. Best creatures are selected based on how far they travel from their starting point across the virtual ground.

__Evolving the gene pool__

Once you have a set of reasonable performing creatures in the gene pool evolution can tweak and improve them further.

Each creature has its attributes slightly changed and the new mutated creature is tested. If it goes further then it replaces the old creature.

For these mutations I only mutate the joint speeds, torque and rotation axiis using the following code

```
mutationpercentage:=;
mutationfactor2:=mutationpercentage/100;
mutationfactor1:=1-mutationfactor2/2;
JointSpeed:=JointSpeed*(mutationfactor1+random*mutationfactor2);
```

If the initial mutationpercentage is specified to be 20% then the joint speed is randomly changed + or – 10% of its current amount. Same for the torque and rotation axis vector xyz components.

__Results__

Nothing too spectacular at this time. Maybe if I let the mutations churn for weeks it may fluke a really interesting setup. The following movie shows some of the creatures that the current methods discovered.

Jason.

]]>

The latest version of Visions of Chaos now includes both a 32-bit and a 64-bit version. You will need to have a 64-bit version of Windows to use the 64-bit version, but if you still use a 32-bit version of Windows then the 32-bit version of Visions of Chaos will continue to work for you. If you are not sure what “bitness” (bititude?) your Windows is, press Windows-Pause and look next to “System type” on the dialog that appears. Both versions are included with the same install exe to avoid confusion and the 64-bit version only installs if you are running 64-bit Windows.

The main advantage of the 64-bit version over the 32-bit is that there is no longer a 3 GB memory limit. As screen sizes have increased the amount of memory that Visions of Chaos requires to render some of its modes at these higher resolutions was hitting the 32 bit application memory limits. 64-bit Visions of Chaos can now use as much memory as you have physically installed in your PC.

Jason.

]]>

At that time I managed to translate some (probably Fortran) LBM source code provided by the now defunct “LB Method” website (here is how LB Method looked around that time). The algorithms worked and did give me some nice results, but there were problems like lack of detail and pulsating colors due to my display routines scaling minimum and maximum velocities to a color palette.

Yesterday I was looking around for some new LBM source code and found Daniel Schroeder‘s LBM page here. Daniel graciously shares the source code for his applet so I was able to convert his main LBM algorithms into something I could use in Visions of Chaos. Many thanks Dan!

Using Dan’s code/algorithms was much faster than my older code. It also allows me to render much more finer detailed fluids without causing the system to blow out. I can push the simulation parameters further. Dan’s method of coloring solved the pulsing colors issue my older code had and includes a really nice way of visualizing the “curl” of the flowing fluid. Tracer particles are also used to follow the velocity of the underlying fluid to give another way of visualizing the fluid flow. Once particles leave the right side of the screen they are buffered up until they fill up and can be reinjected to the left side of the flow. Tracer particles help seeing the vortices easier than shading alone.

With less memory requirements (another plus from Dan’s code) I was able to render some nice 4K resolution LBM flows. This movie must be watched at 4K if possible as the compression of lower resolutions cannot handle displaying the tracer particles.

The new LBM code is now included with Visions of Chaos.

Jason.

]]>

Back in 1994 Karl Sims developed his Evolved Virtual Creatures. More info here and here.

I have always found these sort of simulations fascinating, using the principals of genetics to evolve better solutions to a problem. For years I have wanted to try writing my own evolved creatures, but coding a physics engine to handle the movements and joints was beyond me so it was yet another entry on my to do list (until now).

__2D Physics__

For my virtual creatures I decided to start with 2D. I needed a general physics engine that takes care of all the individual world parts interacting and moving. Erin Catto’s Box2D has all the physics simulation features that I need to finally start experimenting with a 2D environment. Box2D is great. You only need some relatively simple coding to get the physics setup and then Box2D handles all the collisions etc for you.

__Random Creatures__

The creatures I am using are the simplest structures I could come up with that hopefully lead to some interesting movements. Creatures consist of three segments (rectangles) joined together by rotating (revolute joints in Box2D) joints. The size of the rectangle segments and joint rotation speeds are picked at random. Once the random creature is created it is set free into a virtual world to see what it does.

Many of them crunch up and go nowhere,

but some setups result in jumping and crawling creatures.

In only a few minutes thousands of random creatures can be setup and simulated. From these thousands the creatures that perform well are saved.

__Performance Criteria__

Once thousands of random virtual creatures have been created you need a way to pick the best ones. For these creatures I used three different criteria;

1. Distance traveled. The maximum X distance the creature travels in 5,000 time steps.

2. Distance crawled. The maximum X distance the creature travels in 5,000 time steps, but with a height restriction to weed out creatures that jump too high.

3. Height reached. The maximum Y height the creature reaches in 5,000 time steps.

The best picks become a set of creatures in the saved “gene pool”. If you have a large enough random set of creatures (say 10,000) and only take the top 10 performers then you do tend to get a set of creatures that perform the task well.

__Mutations__

Mutation takes a current “good creature” that passed criteria searching and scales segment length, segment width, joint rotation torque and joint rotation speed by a random percentage. The mutated creature is then run through 5,000 time steps and checked if it performs better than the original. If so, it is saved over the original and mutations continue. This process can be left to churn away for hours hands free and when the mutations are stopped you have a new set of best creatures.

For the creatures described here the features I randomly change are the segment widths and heights, the joint motor torques and the joint motor speeds (for 10 total attributes that can be tweaked by mutation). The user specifies a max mutation percentage and then each of the creature values are changed by

```
changepercentage:=maxmutationpercentage/100*random;
amount:=(MaxSegmentWidth-MinSegmentWidth)*changepercentage;
if random(2)=0 then amount:=-amount;
segmentwidth:=segmentwidth+amount;
```

The new attribute is clamped to a min and max value so as not to suddenly grow extremely long segments or super fast motors. You can also randomly mutate only 1 of the attributes rather than all 10 each mutation.

Choosing the right mutation amount can be tricky. Too high a random percentage and you may as well be randomly picking creatures. Too low a percentage and you will get very few mutations that beat the current creature. After some experimenting I am now using a mutation rate of 15% and mutating 3 of the attributes (ie a segment length, a motor’s torque, etc) each mutation.

Running on an i7-6800K CPU my current code can churn through up to 21 mutation tests per second. This screen shot shows 9 copies of Visions of Chaos running, each looking for mutations of different creature types, ie 3 segment distance, 4 segment height reached, etc etc.

A mutation test requires the new mutated creature to be run for 5000 time steps and then comparing against its “parent” to see if it is better in the required fitness criteria (distance traveled, distance crawled or height reached).

__Mutation Results__

After mutating the best randomly found creatures for a while, this movie shows the best creature for distance traveled, distance crawled and height reached.

I will have to run the mutation searches overnight or for a few days to see if even better results are evolved.

__4 Segment Creatures__

Here are some results from evolving (mutating) 4 segment creatures. Same criteria of distance, crawl distance and height for best creatures. Note how only the white “arms” collide with each other. The grey “body” segments are set to pass through each other.

__5 Segment Creatures__

And finally, using 5 segments per creature. Only the 2 end arms collide with each other (otherwise the creatures always bunched up in a virtual knot and moved nowhere).

__Availability__

These Virtual Creatures are now included in the latest version of Visions of Chaos. I have also included the Box2D test bed to show some of the extra potential that I can use Box2D for in future creatures.

__To Do__

This is only the beginning. I have plenty of ideas for future improvements and expansions;

1. Using more than just mutations when evolving creatures. With more complex creatures crossover breeding could be experimented with.

2. Use more of the features of Box2D to create more complex creature setups. Arms and legs that “wave” back and forth like a fish tail rather than just spinning around.

3. 3D creatures and environments. I will need to find another physics engine supporting 3D hopefully as easily as Box2D supports 2D.

Jason.

]]>

I recently revisited my old strange attractor code in Visions of Chaos to add some new variations. This post will show many of the strange attractor formulas and some 4K resolution sample images they create. The images were created using over 1 billion points each. They have also been oversampled at least 3×3 pixels to reduce aliasing artifacts.

__Clifford Attractor__

Discovered by Clifford A Pickover. I found them explained on Paul Bourke‘s page here.

```
x and y both start at 0.1
xnew=sin(a*y)+c*cos(a*x)
ynew=sin(b*x)+d*cos(b*y)
Variables a,b,c and d are floating point values bewteen -3 and +3
```

A=-1.7 B=1.3 C=-0.1 D=-1.21

A=-1.7 B=1.8 C=-0.9 D=-0.4

A=1.5 B=-1.8 C=1.6 D=2

A=-2.239 B=-2.956 C=1.272 D=1.419

A=-1.7 B=1.8 C=-1.9 D=-0.4

__Fractal Dream Attractor__

Discovered by Clifford A Pickover and discussed in his book “Chaos In Wonderland”.

```
x and y both start at 0.1
xnew=sin(y*b)+c*sin(x*b)
ynew=sin(x*a)+d*sin(y*a)
Variables a and b are floating point values bewteen -3 and +3
Variables c and d are floating point values between -0.5 and +1.5
```

A=-0.966918 B=2.879879 C=0.765145 D=0.744728

A=-2.9585 B=-2.2965 C=-2.8829 D=-0.1622

A=-2.8276 B=1.2813 C=1.9655 D=0.597

A=-1.1554 B=-2.3419 C=-1.9799 D=2.1828

A=-1.9956 B=-1.4528 C=-2.6206 D=0.8517

__Gumowski-Mira Attractor__

The Gumowski-Mira equation was developed in 1980 at CERN by I. Gumowski and C. Mira to calculate the trajectories of sub-atomic particles. It can also be used to create attractor images.

```
x and y both start at any floating point value between -20 and +20
t=x
xnew=b*y+w
w=a*x+(1-a)*2*x*x/(1+x*x)
ynew=w-t
The a and b parameters can be any floating point value between -1 and +1.
```

Initial X=0 Initial Y=0.5 A=0.008 B=-0.7

Initial X=-0.723135391715914 Initial Y=-0.327585775405169 A=0.79253300698474 B=0.345703079365194

Initial X=-0.312847771216184 Initial Y=-0.710899183526635 A=0.579161538276821 B=-0.820410779677331

Initial X=-0.325819793157279 Initial Y=0.48573582014069 A=0.062683217227459 B=-0.436713613104075

Initial X=0.78662442881614 Initial Y=0.919355855789036 A=0.900278024375439 B=0.661233567167073

__Hopalong Attractor__

The Hopalong attractor was discovered by Barry Martin.

```
x and y both start at 0
xnew=y-1-sqrt(abs(b*x-1-c))*sign(x-1)
ynew=a-x-1
The parameters a, b and c can be any floating point value between 0 and +10.
```

A=7.16878197155893 B=8.43659746693447 C=2.55983412731439

A=7.7867514709942 B=0.132189802825451 C=8.14610984409228

A=9.74546888144687 B=1.56320227775723 C=7.86818214459345

A=9.8724800767377 B=8.66862616268918 C=8.66950439289212

A=9.7671244922094 B=4.10973468795419 C=3.78332691499963

__Jason Rampe 1__

A variation I discovered while trying random formula changes.

```
x and y both start at 0.1
xnew=cos(y*b)+c*sin(x*b)
ynew=cos(x*a)+d*sin(y*a)
Variables a, b, c and d are floating point values between -3 and +3
```

A=2.6 B=-2.5995 C=-2.9007 D=0.3565

A=1.8285 B=-1.8539 C=0.3816 D=1.9765

A=2.5425 B=2.8358 C=-0.8721 D=2.7044

A=-1.8669 B=1.2768 C=-2.9296 D=-0.4121

A=-2.7918 B=2.1196 C=1.0284 D=0.1384

__Jason Rampe 2__

Another variation I discovered while trying random formula changes.

```
x and y both start at 0.1
xnew=cos(y*b)+c*cos(x*b)
ynew=cos(x*a)+d*cos(y*a)
Variables a, b, c and d are floating point values between -3 and +3
```

A=1.546 B=1.929 C=1.09 D=1.41

A=2.907 B=-1.9472 C=1.2833 D=1.3206

A=0.8875 B=0.7821 C=-2.3262 D=1.5379

A=-2.4121 B=-1.0028 C=-2.2386 D=0.274

A=-2.9581 B=0.927 C=2.7842 D=0.6267

__Jason Rampe 3__

Yet another variation I discovered while trying random formula changes.

```
x and y both start at 0.1
xnew=sin(y*b)+c*cos(x*b)
ynew=cos(x*a)+d*sin(y*a)
Variables a, b, c and d are floating point values between -3 and +3
```

A=2.0246 B=-1.323 C=-2.8151 D=0.2277

A=1.4662 B=-2.3632 C=-0.4167 D=2.4162

A=-2.7564 B=-1.8234 C=2.8514 D=-0.8745

A=-2.218 B=1.4318 C=-0.3346 D=2.4993

A=1.2418 B=-2.4174 C=-0.7112 D=-1.9802

__Johnny Svensson Attractor__

See here.

```
x and y both start at 0.1
xnew=d*sin(x*a)-sin(y*b)
ynew=c*cos(x*a)+cos(y*b)
Variables a, b, c and d are floating point values between -3 and +3
```

A=1.40 B=1.56 C=1.40 D=-6.56

A=-2.538 B=1.362 C=1.315 D=0.513

A=1.913 B=2.796 C=1.468 D=1.01

A=-2.337 B=-2.337 C=0.533 D=1.378

A=-2.722 B=2.574 C=1.284 D=1.043

__Peter DeJong Attractor__

See here.

```
x and y both start at 0.1
xnew=sin(y*a)-cos(x*b)
ynew=sin(x*c)-cos(y*d)
Variables a, b, c and d are floating point values between -3 and +3
```

A=0.970 B=-1.899 C=1.381 D=-1.506

A=1.4 B=-2.3 C=2.4 D=-2.1

A=2.01 B=-2.53 C=1.61 D=-0.33

A=-2.7 B=-0.09 C=-0.86 D=-2.2

A=-0.827 B=-1.637 C=1.659 D=-0.943

A=-2 B=-2 C=-1.2 D=2

A=-0.709 B=1.638 C=0.452 D=1.740

__Symmetric Icon Attractor__

These attractors came from the book “Symmetry in Chaos” by Michael Field and Martin Golubitsky. They give symmetric results to the attractors formed.

```
x and y both start at 0.01
zzbar=sqr(x)+sqr(y)
p=alpha*zzbar+lambda
zreal=x
zimag=y
for i=1 to degree-2 do
begin
za=zreal*x-zimag*y
zb=zimag*x+zreal*y
zreal=za
zimag=zb
end
zn=x*zreal-y*zimag
p=p+beta*zn
xnew=p*x+gamma*zreal-omega*y
ynew=p*y-gamma*zimag+omega*x
x=xnew
y=ynew
The Lambda, Alpha, Beta, Gamma, Omega and Degree parameters can be changed to create new plot shapes.
```

These sample images all come from paramters in the “Symmetry in Chaos” book.

L=-2.5 A=5 B=-1.9 G=1 O=0.188 D=5

L=1.56 A=-1 B=0.1 G=-0.82 O=0.12 D=3

L=-1.806 A=1.806 B=0 G=1 O=0 D=5

L=-2.195 A=10 B=-12 G=1 O=0 D=3

L=2.5 A=-2.5 B=0 G=0.9 O=0 D=3

L=-2.05 A=3 B=-16.79 G=1 O=0 D=9

L=-2.7 A=5 B=1.5 G=1.0 O=0 D=6

L=2.409 A=-2.5 B=0 G=0.9 O=0 D=23

L=-2.08 A=1 B=-0.1 G=0.167 O=0 D=7

L=-2.32 A=2.32 B=0 G=0.75 O=0 D=5

L=2.6 A=-2 B=0 G=-0.5 O=0 D=5

L=-2.34 A=2 B=0.2 G=0.1 O=0 D=5

L=-1.86 A=2 B=0 G=1 O=0.1 D=4

L=1.56 A=-1 B=0.1 G=-0.82 O=0 D=3

L=1.5 A=-1 B=0.1 G=-0.805 O=0 D=3

L=1.455 A=-1 B=0.03 G=-0.8 O=0 D=3

L=2.39 A=-2.5 B=-0.1 G=0.9 O=-0.15 D=16

__3D Alternatives__

Strange Attractors can also be extended into three dimensions. See here and here for my previous experiments with 3D Strange Attractors.

All of the images in this post were created using Visions of Chaos.

Jason.

]]>

Daniel Shiffman has been making YouTube movies for some time now. His videos focus on programming and include coding challenges in which he writes code for a target idea from scratch. If you are a coder I recommend Dan’s videos for entertainment and inspiration.

His latest live stream focused on Fractal Spirographs.

If you prefer to watch a shorter edited version, here it is.

He was inspired by the following image from the Benice Equation blog.

Fractal Spirographs (aka Fractal Routlette) are generated by tracking a series (or chain) of circles rotating around each other as shown in the above gif animation. You track the chain of 10 or so circles and plot the path the final smallest circle takes. Changing the number of circles, the size ratio between circles, the speed of angle change, and the constant “k” changes the resulting plots and images.

__How I Coded It__

As I watched Daniel’s video I coded my own version. For my code (Delphi/pascal) I used a dynamic array of records to hold the details of each circle/orbit. This seemed the simplest approach to me for keeping track of a list of the linked circles.

```
type orbit=record
x,y:double;
radius:double;
angle:double;
speed:double;
end;
```

Before the main loop you fill the array;

```
//parent orbit
orbits[0].x:=destimage.width/2;
orbits[0].y:=min(destimage.width,destimage.height)/2;
orbits[0].radius:=orbits[0].y/2.5;
orbits[0].angle:=0;
orbits[0].speed:=0;
rsum:=orbits[0].radius;
//children orbits
for loop:=1 to numorbits-1 do
begin
newr:=orbits[loop-1].radius/orbitsizeratio;
newx:=orbits[loop-1].x+orbits[loop-1].radius+newr;
newy:=orbits[loop-1].y;
orbits[loop].x:=newx;
orbits[loop].y:=newy;
orbits[loop].radius:=newr;
orbits[loop].angle:=orbits[loop-1].angle;
orbits[loop].speed:=power(k,loop-1)/sqr(k*k);
end;
```

Then inside the main loop, you update the orbits;

```
//update orbits
for loop:=1 to numorbits-1 do
begin
orbits[loop].angle:=orbits[loop].angle+orbits[loop].speed;
rsum:=orbits[loop-1].radius+orbits[loop].radius;
orbits[loop].x:=orbits[loop-1].x+rsum*cos(orbits[loop].angle*pi/180);
orbits[loop].y:=orbits[loop-1].y+rsum*sin(orbits[loop].angle*pi/180);
end;
```

and then you use the last orbit positions to plot the line, ie

```
canvas.lineto(round(orbits[numorbits-1].x),round(orbits[numorbits-1].y));
```

__Results__

Once the code was working I rendered the following images and movie. They are all 4K resolution to see the details. Click the images to see them full size.

Here is a 4K movie showing how these curves are built up.

Fractal Spirographs are now included with the latest version of Visions of Chaos.

Finally, here is an 8K Fulldome 8192×8192 pixel resolution image. Must be seen full size to see the fine detailed plot line.

__To Do__

Experiment with more changes in the circle sizes. The original blog post links to another 4 posts here, here, here and here and even this sumo wrestler

Plenty of inspiration for future enhancements.

I have already experimented with 3D Spirographs in the past, but they are using spheres rotating within other spheres. Plotting the sqheres rotating around the outside of other spheres should give more new unique results.

Jason.

]]>

The basic Mandelbrot Fractal formula is z=z^2+c. The Burning Ship Fractal formula is z=abs(z)^2+c.

The following image is the standard power 2 Burning Ship Fractal rendered using CPM smooth coloring.

Zooming in to the right *antenna* part of the fractal shows why it was named the Burning Ship.

The next 3 images change the exponent 2 in z=abs(z)^2+c to 3, 4 and 5.

The same power 2 through power 5 Burning Ships but this time using Triangle Inequality Average (TIA) coloring

The next 4K resolution movie shows a series of zooms into Burning Ship Fractals between power 2 and power 5 colored using CPM coloring

and finally another 4K movie showing more Burning Ship zooms colored using TIA coloring

All of the above images and movies were created with Visions of Chaos.

Jason.

]]>