Фрактал с использованием спирографа в Python

Опубликовано: 14 Июля, 2021

Вступление

Игрушка-спирограф, которая используется для создания сложных узоров с помощью пластиковых шестеренок и цветных ручек. Фрактал - это кривая, которая создается с использованием повторяющегося шаблона, который бесконечно повторяется в низком масштабе. Фракталы используются для моделирования структур (например, снежинок) или для описания частично хаотических явлений.
Спирограф можно использовать для рисования различных фракталов. Некоторые из них приведены ниже.

Вы можете посетить Benice-Equation-blogspot.in, чтобы узнать больше о дизайне фракталов с параметрическим уравнением. Некоторые из них приведены ниже.

Математика за кулисами

Это два параметрических уравнения для формирования фракталов спирографа. Чтобы понять эти уравнения, вы должны рассмотреть обобщенную фигуру спирографа.

Что касается математической части, вы можете обратиться к Wiki, хотя я постараюсь вкратце объяснить немного этой математики здесь. Если нас интересует математика, вы можете проверить указанные ссылки. Итак, на данный момент эти различные кривые можно нарисовать с помощью параметрического уравнения и варьируя некоторые значения этого уравнения, мы можем получить разные фракталы. Итак, вот параметрическое уравнение:

где,

R является параметром масштабирования и не влияет на структуру спирографа.

а также,

Итак, теперь попробуем реализовать это в коде.

Рекомендуется: сначала попробуйте свой подход в {IDE}, прежде чем переходить к решению.




#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()

Выход:


Вышеупомянутая программа рисует 4 разных типа фракталов спирографа, пытается сгенерировать другие фракталы, а затем загружает ссылки на github в комментарии. Я буду рад помочь вам, если возникнет какая-либо ошибка.

Эта статья предоставлена Субхаджитом Саха. Если вам нравится GeeksforGeeks, и вы хотели бы внести свой вклад, вы также можете написать статью с помощью provide.geeksforgeeks.org или отправить ее по электронной почте на deposit@geeksforgeeks.org. Посмотрите, как ваша статья появляется на главной странице GeeksforGeeks, и помогите другим гикам.

Пожалуйста, напишите комментарии, если вы обнаружите что-то неправильное, или вы хотите поделиться дополнительной информацией по теме, обсужденной выше.