7
\$\begingroup\$

So, I have my code here:

import turtle

class fractal:

    """A class containing methods that will replicate
    various fractals.

    Attributes:
        num: An integer representing the number of iterations
        t: A turtle graphics object imported from Turtle.py
    """

    def __init__(self, num, t=turtle.Turtle()):
        self.num = num
        self.t = t

    def kochCurve(self):
        axiom = 'F'
        for _ in range(self.num):
            linSys = 'F+F--F+F'
            axiom = axiom.replace('F',linSys)
        for i in axiom:
            if i == 'F':
                self.t.forward(10)
            elif i == '+':
                self.t.left(60)
            elif i == '-':
                self.t.right(60)

    def kochIsland(self):
        axiom = 'F-F-F-F'
        for _ in range(self.num):
            linSys = 'F+FF-FF-F-F+F+F'
            axiom = axiom.replace('F',linSys)
        for i in axiom:
            if i == 'F':
                self.t.forward(10)
            elif i == '+':
                self.t.left(90)
            elif i == '-':
                self.t.right(90)

    def tree(self):
        axiom = '[X]'
        for _ in range(self.num):
            linSys = 'F-[[X]+X]+F[+FX]-X'
            axiom = axiom.replace('X',linSys)
            axiom = axiom.replace('F','FF')

        self.t.setheading(90)
        self.t.setposition(0,-300)
        stack = []
        for i in axiom:
            if i == 'F':
                self.t.forward(10)
            elif i == '+':
                self.t.right(25)
            elif i == '-':
                self.t.left(25)
            elif i == '[':
                pos = self.t.position()
                head = self.t.heading()
                stack.append((pos,head))
            elif i == ']':
                (pos,head) = stack.pop()
                self.t.penup()
                self.t.setposition(pos)
                self.t.setheading(head)
                self.t.pendown()

fractal(3).kochCurve()

Overall, the code just seems a bit redundant and LONG. I'm creating this class to share on a repository for some others to view. However, I don't particularly enjoy the way it looks.

Essentially, this class makes a turtle from Turtle Graphics and draws fractals with it. So far, I have the three different fractals. All that's needed to call into the object is an integer that will be the number of iterations.

What improvements can I make here?

\$\endgroup\$
4
\$\begingroup\$

Couple performance and code style related notes:

  • if you can define something outside/before a loop, do so. For example, there is no need to define linSys inside the loops
  • follow the PEP8 naming guidelines - in particular, the fractal class name should start with a capital letter - Capital; linSys should be lin_sys
  • I would also improve on the way you define which direction to go to and use a dictionary instead of multiple if/elif/else checks. Something like:

    class Fractal:
        def __init__(self, num, t=turtle.Turtle()):
            # ...
            self.directions = {
                'F': self.t.forward,
                '+': self.t.left, 
                '-': self.t.right
            } 
    
            self.offsets = {
                'curve': {
                    'F': 10, 
                    '+': 60, 
                    '-': 60
                }, 
                # TODO: island and tree
            }
    

    Then, you can use self.directions to apply the direction move:

    for direction in axiom:
        if direction in self.directions:
            move = self.directions[direction]
            offset = self.offsets['curve'][direction]
    
            move(offset)
    

    For the complex moves, like you have for an "island", I would wrap the ] and [ moves logic into separate "macro" methods and configure them inside the self.directions.

  • put the main execution logic to under if __name__ == '__main__': to avoid it being executed on import

\$\endgroup\$
  • \$\begingroup\$ This was absolutely insightful! Thanks for sharing such quality links and pointing me in the correct direction! \$\endgroup\$ – ThomasTaylor Mar 24 '17 at 15:05
  • \$\begingroup\$ Also worth noting: you probably have three subclasses of a single Fractal class. Something like f = KochCurve(3); f.draw() \$\endgroup\$ – Austin Hastings Mar 26 '17 at 2:39
4
\$\begingroup\$

For each shape, you have this kind of code to generate the movement "recipe":

axiom = ...
for _ in range(self.num):
    replacement = 'some fixed string'
    axiom = axiom.replace(pattern, replacement)
    axiom = axiom.replace(pattern2, replacement2)

This can be generalized with a helper function that takes as parameters axiom and a list of pattern-replacement pairs. All shapes will be able to use this helper and thereby reduce duplication.

The execution of the movement commands is also repetitive. The code for the most complex shape would be able to draw all the simpler​ shapes, with a single parameter of the turn degrees.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.