This is an old project of mine, but I really like it, so it’s gone through many revisions.
The current python source is a new version I typed up recently, past versions included a few extra bits and pieces, one had the ability to erase as well as add dots, usually this effect can be achieved with good colouring anyway.

Code

By defult this version of the code produces the frames for animation, for single images, remove the second line and unindent everything after.

Or just change the frame count to “for frame in range(1):”

from math import cos,sin,pi
for frame in range(100):
    a=sin(frame/1600.0*pi*2)
    """#child=(angle,scale,child,child)
    testSectionA=(.1,1,(.2,1,(.25,1,(.2,.9,(-pi/2,.8,"B"),(pi/2,.8,"C")))))
    testSectionB=(.1,1,(.2,1,(.2,1,(.2,1,(2,.85,"B"),(0,.85,"A")))))
    testSectionC=(.15,.95,(.3,.95,(.3,1,(.3,1,(4,.8,"C"),(0,.85,"A")))))"""
    testSectionA=(a,1,(a,1,(a,1,(a,.9,(a,1,(-pi/2,.7+.2*sin(frame/200.0*pi),"A"),(pi/2,.7+.2*sin(frame/200.0*pi),"A"),(a,.9,(a,.9,"A")))))))
    class dotfractal:
        def __init__(self,startpos=(0,400),startradius=80,startangle=-pi/2.0,
                     startsection="A",sections={"A":testSectionA},
                     maxdepth=8):
            self.startpos=startpos
            self.startradius=startradius
            self.startangle=startangle
            self.startsection=startsection
            self.sections=sections
            self.maxdepth=maxdepth
            self.dots=[[] for i in range(maxdepth+1)]
        def render(self):
            self.sections[self.startsection]
            self.drawdot(self.sections[self.startsection],self.startpos,self.startradius,self.startangle,self.maxdepth,0)
            return self.draw()
        def draw(self):
            from PIL import Image,ImageDraw
            imres=(1080,720)
            hw=int(imres[0]*.5)
            hh=int(imres[1]*.5)
            im=Image.new("RGB",imres,(0,0,0))
            dr=ImageDraw.Draw(im)
            for j in self.dots:
                for i in j:
                    position,radius,depth=i
                    left=position[0]-radius+hw
                    top=position[1]-radius+hh
                    right=position[0]+radius+hw
                    bottom=position[1]+radius+hh
                    dr.ellipse((left,top,right,bottom),fill=(depth*32,128-depth*10,128-depth*10))
                    #dr.ellipse((left,top,right,bottom),fill=(255,0,depth*24))
            return im
        def drawdot(self,sequence,position,radius,angle,depth,iteration):
            currentdot=position,radius,depth
            self.dots[depth].append(currentdot)
            ##produce children
            children=sequence[2:]
            if depth>0:
                for i in children:
                    if isinstance(i,str):
                        self.drawdot(self.sections[i],position,radius,angle,depth-1,iteration+1)
                    else:
                        childseq=i
                        childradius=radius*i[1]
                        childangle=angle+i[0]
                        w=radius+childradius
                        childpos=(position[0]+cos(childangle)*w,position[1]+sin(childangle)*w)
                        self.drawdot(childseq,childpos,childradius,childangle,depth,iteration)
    df=dotfractal(maxdepth=7,startsection="A",startradius=30,sections={"A":testSectionA})
            
    im=df.render()

    im.save("%04d.png"%frame)

Back to top