Search code examples
pythonmathturtle-graphicsfractals

Need help making Sierpinski triangle with turtle


I was watching a video by Aperture on youtube: https://youtu.be/1w40fxsyraE?t=325

At the provided timestamp (5:25), he begins talking about a way to create a fractal. I tried to replicate this in a python program, but I am getting a different output. I have no idea why I am getting this output, but the math seems right, so I don't know what to change. Can anyone explain why my output looks different than the one in the video?

import turtle as t

drawer = t.Turtle()
drawer.speed(1000)

# drawer.hideturtle()
drawer.penup()

#make dot A

dotAx = 125
dotAy = 150
drawer.goto(dotAx, dotAy)
drawer.dot(10, "red")
#

#make dot B

dotBx = 185
dotBy = 0
drawer.goto(dotBx, dotBy)
drawer.dot(10, "red")

#

#make dot C

dotCx = 0
dotCy = 0
drawer.goto(dotCx, dotCy)
drawer.dot(10, "red")

#

#make middle dot

dotPx = 100
dotPy = 75
drawer.goto(dotPx, dotPy)
drawer.dot(5,"yellow")

#

#draw dots v
x = 0
drawer.pendown()
while True:
  if x == 0:
    dotPx = (dotPx + dotAx)/2
    dotPy = (dotPy + dotAy)/2
    drawer.goto(dotPx, dotPy)
    drawer.dot(5,"black")
    print("A", dotPx, dotPy)
    x+=1
  if x == 1:
    dotPx = (dotPx + dotBx)/2
    dotPy = (dotPy + dotBy)/2
    drawer.goto(dotPx, dotPy)
    drawer.dot(5, "black")
    print("B", dotPx, dotPy)
    x+=1
  if x == 2:
    dotPx = (dotPx + dotCx)/2
    dotPy = (dotPy + dotCy)/2
    drawer.goto(dotPx, dotPy)
    drawer.dot(5, "black")
    print("C", dotPx, dotPy)
    x = 0
  

Solution

  • I watched the video and played with your code and can't see the inconsistency. But looking further afield, I found that by changing your x (corner selection) from being cyclic, to instead being random, it works fine:

    from turtle import Turtle
    from random import randint
    
    turtle = Turtle()
    turtle.speed('fastest')
    turtle.hideturtle()
    turtle.penup()
    
    # make dot A
    dotAx, dotAy = 0, 0
    turtle.goto(dotAx, dotAy)
    turtle.dot(10, 'red')
    
    # make dot B
    dotBx, dotBy = 150, 260
    turtle.goto(dotBx, dotBy)
    turtle.dot(10, 'red')
    
    # make dot C
    dotCx, dotCy = 300, 0
    turtle.goto(dotCx, dotCy)
    turtle.dot(10, 'red')
    
    # make random dot inside triangle
    dotPx, dotPy = 100, 75
    turtle.goto(dotPx, dotPy)
    turtle.dot(5, 'green')
    
    # draw dots
    
    while True:
        x = randint(0, 2)  # pick a random corner
    
        if x == 0:
            dotPx = (dotAx + dotPx)/2
            dotPy = (dotAy + dotPy)/2
            turtle.goto(dotPx, dotPy)
            turtle.dot(5)
        elif x == 1:
            dotPx = (dotBx + dotPx)/2
            dotPy = (dotBy + dotPy)/2
            turtle.goto(dotPx, dotPy)
            turtle.dot(5)
        elif x == 2:
            dotPx = (dotCx + dotPx)/2
            dotPy = (dotCy + dotPy)/2
            turtle.goto(dotPx, dotPy)
            turtle.dot(5)
    

    enter image description here

    Perhaps this will narrow your search as to why your orignal approach, as suggested by the video, failed. If I were writing this from scratch, and attempting to get finer detail (and speed), I might take greater advantage of turtle and Python by doing:

    from turtle import Screen, Turtle, Vec2D
    from random import choice
    
    VERTICES = [Vec2D(0, 0), Vec2D(150, 260), Vec2D(300, 0)]
    point = Vec2D(100, 75)  # random point inside triangle
    
    def doit():
        global point
        point = (choice(VERTICES) + point) * 0.5
    
        turtle.goto(point)
        turtle.dot(2)
    
        screen.update()
        screen.ontimer(doit)
    
    screen = Screen()
    screen.tracer(False)
    
    turtle = Turtle()
    turtle.hideturtle()
    turtle.penup()
    
    for vertex in VERTICES:
        turtle.goto(vertex)
        turtle.dot(5, 'red')
    
    turtle.goto(point)
    turtle.dot(5, 'green')
    
    doit()
    
    screen.mainloop()
    

    enter image description here