Open in App
Not now

# Fractal using Spirograph in Python

• Last Updated : 29 Sep, 2022

Introduction

Spirograph toy that is used to produce complex patterns using plastic cogs and colored pens. A fractal is a curve, that is developed using a recurring pattern that repeats itself infinitely on a low scale. Fractals are used for modeling structures (such as snowflakes) or for describing partly chaotic phenomena.
Spirograph can be used to draw various fractals. Some of them are given below

You can visit benice-equation-blogspot.in for more fractals design with their parametric equation. Some of them are given below

Mathematics behind the curtain

These are the two parametric equation to form a spirograph fractals, to understand these equations you have to consider a generalized figure of spirograph.

For the mathematics part you can refer to Wiki although i’ll try to explain a little of that mathematics in a short here. If we are interested behind the maths then you can check out the referred links. So as of now, these various curve can be drawn by using a parametric equation and varying some values of that equation we can get different fractals. So here’s the parametric equation:

where,

R is a scaling parameter and does not affect the structure of the Spirograph.

and,

So, now let’s try to implement this in code.

## Recommended: Please try your approach on {IDE} first, before moving on to the solution.

 #importing the required libraries import random, argparse import math import turtle from PIL import Image from datetime import datetime     from fractions import gcd    # A class that draws a spirograph class Spiro:     # constructor     def __init__(self, xc, yc, col, R, r, l):            # create own turtle         self.t = turtle.Turtle()         # set cursor shape         self.t.shape('turtle')         # set step in degrees         self.step = 5         # set drawing complete flag         self.drawingComplete = False            # set parameters         self.setparams(xc, yc, col, R, r, l)            # initiatize drawing         self.restart()        # set parameters     def setparams(self, xc, yc, col, R, r, l):         # spirograph parameters         self.xc = xc         self.yc = yc         self.R = int(R)         self.r = int(r)         self.l = l         self.col = col         # reduce r/R to smallest form by dividing with GCD         gcdVal = gcd(self.r, self.R)         self.nRot = self.r//gcdVal         # get ratio of radii         self.k = r/float(R)         # set color         self.t.color(*col)         # current angle         self.a = 0        # restart drawing     def restart(self):         # set flag         self.drawingComplete = False         # show turtle         self.t.showturtle()         # go to first point         self.t.up()         R, k, l = self.R, self.k, self.l         a = 0.0         x = R*((1-k)*math.cos(a) + l*k*math.cos((1-k)*a/k))         y = R*((1-k)*math.sin(a) - l*k*math.sin((1-k)*a/k))         self.t.setpos(self.xc + x, self.yc + y)         self.t.down()        # draw the whole thing     def draw(self):         # draw rest of points         R, k, l = self.R, self.k, self.l         for i in range(0, 360*self.nRot + 1, self.step):             a = math.radians(i)             x = R*((1-k)*math.cos(a) + l*k*math.cos((1-k)*a/k))             y = R*((1-k)*math.sin(a) - l*k*math.sin((1-k)*a/k))             self.t.setpos(self.xc + x, self.yc + y)         # done - hide turtle         self.t.hideturtle()            # update by one step     def update(self):         # skip if done         if self.drawingComplete:             return         # increment angle         self.a += self.step         # draw step         R, k, l = self.R, self.k, self.l         # set angle         a = math.radians(self.a)         x = self.R*((1-k)*math.cos(a) + l*k*math.cos((1-k)*a/k))         y = self.R*((1-k)*math.sin(a) - l*k*math.sin((1-k)*a/k))         self.t.setpos(self.xc + x, self.yc + y)         # check if drawing is complete and set flag         if self.a >= 360*self.nRot:             self.drawingComplete = True             # done - hide turtle             self.t.hideturtle()        # clear everything     def clear(self):         self.t.clear()    # A class for animating spirographs class SpiroAnimator:     # constructor     def __init__(self, N):         # timer value in milliseconds         self.deltaT = 10         # get window dimensions         self.width = turtle.window_width()         self.height = turtle.window_height()         # create spiro objects         self.spiros = []         for i in range(N):             # generate random parameters             rparams = self.genRandomParams()             # set spiro params             spiro = Spiro(*rparams)             self.spiros.append(spiro)         # call timer         turtle.ontimer(self.update, self.deltaT)            # restart sprio drawing     def restart(self):         for spiro in self.spiros:             # clear             spiro.clear()             # generate random parameters             rparams = self.genRandomParams()             # set spiro params             spiro.setparams(*rparams)             # restart drawing             spiro.restart()        # generate random parameters     def genRandomParams(self):         width, height = self.width, self.height         R = random.randint(50, min(width, height)//2)         r = random.randint(10, 9*R//10)         l = random.uniform(0.1, 0.9)         xc = random.randint(-width//2, width//2)         yc = random.randint(-height//2, height//2)         col = (random.random(),                random.random(),                random.random())         return (xc, yc, col, R, r, l)        def update(self):         # update all spiros         nComplete = 0         for spiro in self.spiros:             # update             spiro.update()             # count completed ones             if spiro.drawingComplete:                 nComplete+= 1         # if all spiros are complete, restart         if nComplete == len(self.spiros):             self.restart()         # call timer         turtle.ontimer(self.update, self.deltaT)        # toggle turtle on/off     def toggleTurtles(self):         for spiro in self.spiros:             if spiro.t.isvisible():                 spiro.t.hideturtle()             else:                 spiro.t.showturtle()                # save spiros to image def saveDrawing():     # hide turtle     turtle.hideturtle()     # generate unique file name     dateStr = (datetime.now()).strftime("%d%b%Y-%H%M%S")     fileName = 'spiro-' + dateStr      print('saving drawing to %s.eps/png' % fileName)     # get tkinter canvas     canvas = turtle.getcanvas()     # save postscipt image     canvas.postscript(file = fileName + '.eps')     # use PIL to convert to PNG     img = Image.open(fileName + '.eps')     img.save(fileName + '.png', 'png')     # show turtle     turtle.showturtle()    # main() function def main():     # use sys.argv if needed     print('generating spirograph...')     # create parser     descStr = """This program draws spirographs using the Turtle module.      When run with no arguments, this program draws random spirographs.            Terminology:        R: radius of outer circle.     r: radius of inner circle.     l: ratio of hole distance to r.     """     parser = argparse.ArgumentParser(description=descStr)          # add expected arguments     parser.add_argument('--sparams', nargs=3, dest='sparams', required=False,                          help="The three arguments in sparams: R, r, l.")                                   # parse args     args = parser.parse_args()        # set to 80% screen width     turtle.setup(width=0.8)        # set cursor shape     turtle.shape('turtle')        # set title     turtle.title("Spirographs!")     # add key handler for saving images     turtle.onkey(saveDrawing, "s")     # start listening      turtle.listen()        # hide main turtle cursor     turtle.hideturtle()        # checks args and draw     if args.sparams:         params = [float(x) for x in args.sparams]         # draw spirograph with given parameters         # black by default         col = (0.0, 0.0, 0.0)         spiro = Spiro(0, 0, col, *params)         spiro.draw()     else:         # create animator object         spiroAnim = SpiroAnimator(4)         # add key handler to toggle turtle cursor         turtle.onkey(spiroAnim.toggleTurtles, "t")         # add key handler to restart animation         turtle.onkey(spiroAnim.restart, "space")        # start turtle main loop     turtle.mainloop()    # call main if __name__ == '__main__':     main()

Output:

The above program draws 4 different kinds of spirograph fractals, try to generate other fractals and then upload your github links in the comment. I’ll be happy to help you out if any error comes up.

This article is contributed by Subhajit Saha. If you like GeeksforGeeks and would like to contribute, you can also write an article using write.geeksforgeeks.org or mail your article to review-team@geeksforgeeks.org. See your article appearing on the GeeksforGeeks main page and help other Geeks.

Please write comments if you find anything incorrect, or you want to share more information about the topic discussed above.

My Personal Notes arrow_drop_up
Related Articles