TkInter python - creating points on a canvas to obtain a Sierpinsky triangle

2.4k Views Asked by At

I want to make a program which plots a Sierpinsky triangle (of any modulo). In order to do it I've used TkInter. The program generates the fractal by moving a point randomly, always keeping it in the sides. After repeating the process many times, the fractal appears.

However, there's a problem. I don't know how to plot points on a canvas in TkInter. The rest of the program is OK, but I had to "cheat" in order to plot the points by drawing small lines instead of points. It works more or less, but it doesn't have as much resolution as it could have.

Is there a function to plot points on a canvas, or another tool to do it (using Python)? Ideas for improving the rest of the program are also welcome.

Thanks. Here's what I have:

from tkinter import *
import random
import math
def plotpoint(x, y):
    global canvas
    point = canvas.create_line(x-1, y-1, x+1, y+1, fill = "#000000")
x = 0 #Initial coordinates
y = 0
#x and y will always be in the interval [0, 1]
mod = int(input("What is the modulo of the Sierpinsky triangle that you want to generate? "))
points = int(input("How many points do you want the triangle to have? "))
tkengine = Tk() #Window in which the triangle will be generated
window = Frame(tkengine)
window.pack()
canvas = Canvas(window, height = 700, width = 808, bg = "#FFFFFF") #The dimensions of the canvas make the triangle look equilateral
canvas.pack()
for t in range(points):
    #Procedure for placing the points
    while True:
        #First, randomly choose one of the mod(mod+1)/2 triangles of the first step. a and b are two vectors which point to the chosen triangle. a goes one triangle to the right and b one up-right. The algorithm gives the same probability to every triangle, although it's not efficient.
        a = random.randint(0,mod-1)
        b = random.randint(0,mod-1)
        if a + b < mod:
            break
    #The previous point is dilated towards the origin of coordinates so that the big triangle of step 0 becomes the small one at the bottom-left of step one (divide by modulus). Then the vectors are added in order to move the point to the same place in another triangle.
    x = x / mod + a / mod + b / 2 / mod
    y = y / mod + b / mod
    #Coordinates [0,1] converted to pixels, for plotting in the canvas.
    X = math.floor(x * 808)
    Y = math.floor((1-y) * 700)
    plotpoint(X, Y)
tkengine.mainloop()
3

There are 3 best solutions below

2
On BEST ANSWER

If you are wanting to plot pixels, a canvas is probably the wrong choice. You can create a PhotoImage and modify individual pixels. It's a little slow if you plot each individual pixel, but you can get dramatic speedups if you only call the put method once for each row of the image.

Here's a complete example:

from tkinter import *
import random
import math

def plotpoint(x, y):
    global the_image
    the_image.put(('#000000',), to=(x,y))

x = 0
y = 0
mod = 3
points = 100000
tkengine = Tk() #Window in which the triangle will be generated
window = Frame(tkengine)
window.pack()
the_image = PhotoImage(width=809, height=700)
label = Label(window, image=the_image, borderwidth=2, relief="raised")
label.pack(fill="both", expand=True)

for t in range(points):
    while True:
        a = random.randint(0,mod-1)
        b = random.randint(0,mod-1)
        if a + b < mod:
            break
    x = x / mod + a / mod + b / 2 / mod
    y = y / mod + b / mod

    X = math.floor(x * 808)
    Y = math.floor((1-y) * 700)
    plotpoint(X, Y)

tkengine.mainloop()
0
On

Finally found a solution: if a 1x1 point is to be placed in pixel (x,y), a command which does it exactly is:

point = canvas.create_line(x, y, x+1, y+1, fill = "colour")

The oval is a good idea for 2x2 points.

Something remarkable about the original program is that it uses a lot of RAM if every point is treated as a separate object.

2
On

You can use canvas.create_oval with the same coordinates for the two corners of the bounding box:

from tkinter import *
import random
import math
def plotpoint(x, y):
    global canvas
#     point = canvas.create_line(x-1, y-1, x+1, y+1, fill = "#000000")
    point = canvas.create_oval(x, y, x, y, fill="#000000", outline="#000000")
x = 0 #Initial coordinates
y = 0
#x and y will always be in the interval [0, 1]
mod = int(input("What is the modulo of the Sierpinsky triangle that you want to generate? "))
points = int(input("How many points do you want the triangle to have? "))
tkengine = Tk() #Window in which the triangle will be generated
window = Frame(tkengine)
window.pack()
canvas = Canvas(window, height = 700, width = 808, bg = "#FFFFFF") #The dimensions of the canvas make the triangle look equilateral
canvas.pack()
for t in range(points):
    #Procedure for placing the points
    while True:
        #First, randomly choose one of the mod(mod+1)/2 triangles of the first step. a and b are two vectors which point to the chosen triangle. a goes one triangle to the right and b one up-right. The algorithm gives the same probability to every triangle, although it's not efficient.
        a = random.randint(0,mod-1)
        b = random.randint(0,mod-1)
        if a + b < mod:
            break
    #The previous point is dilated towards the origin of coordinates so that the big triangle of step 0 becomes the small one at the bottom-left of step one (divide by modulus). Then the vectors are added in order to move the point to the same place in another triangle.
    x = x / mod + a / mod + b / 2 / mod
    y = y / mod + b / mod
    #Coordinates [0,1] converted to pixels, for plotting in the canvas.
    X = math.floor(x * 808)
    Y = math.floor((1-y) * 700)
    plotpoint(X, Y)
tkengine.mainloop()

with a depth of 3 and 100,000 points, this gives:

enter image description here