2D genetic fractals makers guide (part 2): plotting fractals

Previous article: Implementing the Genetic Equation

Next article: Accessory functions

In the last post on 2D genetic fractals, we introduced the Genetic Equation without branches and saw that using the driver functions we generate any path that we want. The Excel example was useful for messing around with the driver functions. But for branches, in particular fractal branches, Excel is not the right tool. We need some sort of programmable canvas. There are thousands to choose from and there is no right choice. Here at planet Genetic Fractals we (as in I) use Ruby and TK graphical toolkit. I don’t know if Ruby is growing or shrinking in popularity but I love it. And TK is simple; enough said.

A bit of theory first. The genetic equation does two things:

• It generates arbitrary paths based on driver functions and
• It allows for smooth and continuous branches.

The first point we saw illustrated in the previous post. The second is what makes the Genetic Equation so special. It allows the creation of smooth, continuous and analytic branches in fractals,  i.e. we can differentiate these fractals analytically. (I promise to post on this!). You should perhaps have a glance at the section “Multi-varied driver functions” on the Genetic Equation page to understand how and why this works. For our purposes here, it is enough to say that these fractals here split into two branches anytime $D_{\varphi+}$ =  $D_{\varphi-}$ = 0.

In the Ruby code below, we read $D_{r}$$D_{\varphi+}$ and $D_{varphi-}$ from an external data.txt file in the same directory. This is produced by exporting an Excel file as tab delimited CSV. Some usable examples at the bottom of this post.

The Ruby code for this GeneticFractal2D class is here on GitHub.

I won’t comment on all the code here, there are some boring bits that are commented in the source file. Let’s just look at the main elements.

First we declare the class and in initialize it. Here we just create an empty array for the driver functions.

class GeneticFractal2D
#create an instance variable to keep the driver functions
attr_accessor :drivers

#create an empty array for the driver function
def initialize
@drivers = Array.new
end


Then we read the external CSV file. This CSV file is named “data.txt” and is created using excel and saved in CSV format. There are 4 colomns: s,  $D_{r}$$D_{varphi+}$ and $D_{varphi-}$. At the bottom of this post there are sample excel and data.txt files for you to change and experiment with.

&amp;lt;pre&amp;gt; #read and parse CSV file in one line. Don't you love ruby!?
CSV.foreach(dfile,:col_sep=&amp;gt;&amp;quot;t&amp;quot;) do |row| @drivers&amp;lt;&amp;lt;row end
end


Next we have the Genetic Equation. We use it in its trigonometric form in 2D:

$C(s)=\left\{\begin{matrix}x(s)\\ y(s)\end{matrix}\right.=\left \{\begin{matrix} \int_s D_{r}\textrm{sin}(\int_s D_{\varphi}ds)ds\\\int_s D_{r}\textrm{cos}(\int_s D_{\varphi}ds)ds\end{matrix} \right.$

There are two integrals here. They both serve as a “memory”, i.e. the first integral remembers the coordinates of the last point on the fractal path and the second integral remembers what the direction was at that point. In this example, we approximate the integral by a simple summation, a Riemann Sum where $Delta s=1$.


#the Genetic Equation is implemented in its trigonemtric form for simplicity

def geneticEquation(dR,dPhi, lastPoint, lastPhi)
newPhi=lastPhi+dPhi #integrate over phi (it's summation really)
return [lastPoint[0]+dR*sin(newPhi),lastPoint[1]+dR*cos(newPhi)],newPhi
end



For plotting we use the RCL/TK library. It’s easy to use, so we’re good. We create a canvas, call the method that calculates the fractal and we hand off to the Tk.mainloop. This Tk.mainloop would allow interaction with the canvas which we don’t actually need. But that is how it works.

The plotPoint simply plots a line segment using one of the TK primitives TkcLine.

def mainPlot
root = TkRoot.new(:title =&amp;gt; &amp;quot;Genetic Fractals rule!&amp;quot;)
canvas = TkCanvas.new(root, :height =&amp;gt; $canvasWidth, :width =&amp;gt;$canvasHeight)
evaluate(canvas,[0,0],3.141,0,2)
canvas.pack
Tk.mainloop
end
def plotPoint(canvas,beginPoint,endPoint)
#plot the line segment between the points.
#the coordinates are scaled and shifted to center it on the canvas
#offset and scale are defined above
TkcLine.new(
canvas,
beginPoint[0]*$scale+$xc+$canvasWidth/2, beginPoint[1]*$scale-$yc+$canvasHeight/2,
endPoint[0]*$scale+$xc+$canvasWidth/2, endPoint[1]*$scale-$yc+$canvasHeight/2,
'width' =&amp;gt; '1',
'fill' =&amp;gt; 'black')
end


Now we get to the core of the class: the part where we calculate the fractal. The first part calculates the points of a branch (wherever we are on the fractal, we’re always on a branch). If it encounters a branch point (i.e. vertex on the fractal) which occurs when  $D_{varphi+}$ =  $D_{varphi-}$ = 0, then we leave the while loop and assuming that we haven’t reached the maximum path length that we have set,  we can recursively evaluate the two branches that follow this node: one left branch and one right branch.


def evaluate(canvas,lastPoint,lastPhi,s,b)
#get the driver values for Dr and Dphi
dR=@drivers[s][1].to_f
dPhi=@drivers[s][b].to_f

#first calculate the points of a branch until we hit a branch point
while dPhi!=0 and s&amp;lt;$maxS# i.e. while it is not a branch point #calculate next point using the Genetic Equation nextPoint, lastPhi = geneticEquation(dR, dPhi, lastPoint, lastPhi) #plot it on the canvas plotPoint(canvas,lastPoint,nextPoint) #for the next iteration, what was the next point is now the last point lastPoint = nextPoint.dup #get the next driver values for Dr and Dphi and increment s dR=@drivers[s][1].to_f dPhi=@drivers[s][b].to_f s+=1 end # if we are here, # =&amp;gt; either we have a branch point so we recursively create two branches; # =&amp;gt; or we reach the maximum value of s that we allowed and will skip this. if s&amp;lt;$maxS #check s hasn't reached the maximum
evaluate(canvas,nextPoint, lastPhi,s,2) # right branch uses column 2 of the data.txt file
evaluate(canvas,nextPoint, lastPhi,s,3) # left branch uses column 3 of the data.txt file
end
end


Now that the class has been implemented, we can instantiate it and trigger the plotting of the fracta.


# instantiate a fractal
niceFractal=GeneticFractal2D.new

# create a canvas and start evaluating the fractal
niceFractal.mainPlot

# all done


Below are a few examples of fractals created with this little program. The links to source data.txt files are shown as well. It would be seriously brilliant if you tried to run the code yourself! If you do, leave a comment and “make my day”.
[You’ll see that the source data.txt has more than 4 columns. I used these to calculate the first 4 columns and the program will ignore them]

Note how these are all continuous smooth and analytic fractals. Seriously cool, right!
Fractal f1.jpg from data-1.txt

Fractal f2.jpg from data-2.txt

Fractal f3.jpg from data-3.txt

Fractal f4.jpg from data-4.txt

Fractal f5.jpg from data-5.txt

Fractal f5.jpg from data-6.txt

Previous article: Implementing the Genetic Equation

Next article: Accessory functions

7 thoughts on “2D genetic fractals makers guide (part 2): plotting fractals”

• My feeling exactly. Takes me back to my magazine days working for a hobby magazine. Similar stuff minus the typos.

Liked by 1 person

• Typos are mutations, right… random acts of creativity, broadening and deepening the life experience…

Like