'''
Tree picture generation
Tests:
'''
import math
import random
import tkinter
def gencolours(x, min_col=0, max_col=255):
''' Colour generation - generate html colour strings using random numbers '''
colours = []
for _x in range(x):
r = random.randint(min_col, max_col)
g = random.randint(min_col, max_col)
b = random.randint(min_col, max_col)
colours.append(f'#{r:02x}{g:02x}{b:02x}')
return colours
class TreeBranch:
''' Branch object of a tree. '''
def __init__(self, length, angle, parent=None, level=1, fromtopofparent=0):
# calculate position of branch from top of parent.. as parent 'grows' from the bottom, this is an invariant quantity
self.fromtopofparent = fromtopofparent
self.length = length
self.angle = angle
self.branches = []
self.xcoords = []
self.ycoords = []
self.level = level
self.parent = parent
self.maxbranches = 2 # maximum number of sub-branches that can come off one branch per season
self.lengthinpix = 15 # conversion of lengths to pixels
self.maxnewbranchlength = 1 # max new branch size
self.minnewbranchlength = 1 # min new branch size
self.maximumangle = 60 # maximum angle the tree branch can droop
def lengthen(self):
''' Increase the length of all existing branches each growing season. '''
self.length = self.length + 1
for branch in self.branches:
branch.lengthen()
def growtree(self):
''' Grow the tree. Add branches. '''
for branch in self.branches:
branch.growtree()
branch.addbranches()
if self.level == 1:
self.addbranches()
def addbranches(self):
''' Add branches to the tree. '''
numbranches = random.randint(0, self.maxbranches)
while numbranches > 0:
new_tree_branch = TreeBranch(random.uniform(self.minnewbranchlength, self.maxnewbranchlength),
random.uniform(-self.maximumangle, self.maximumangle),
parent=self, level=self.level+1, fromtopofparent=random.uniform(0, self.length) )
self.branches.append(new_tree_branch)
numbranches = numbranches - 1
def printtreexytocanvus(self, canvus, seasons):
''' Print the tree to the canvas. '''
to_pix = self.lengthinpix
if self.level == 1:
self.xcoords = (float(canvus["width"]) / 2,
float(canvus["width"]) / 2)
self.ycoords = (float(canvus["height"]),
float(canvus["height"]) - (self.length * to_pix))
else:
p_length = self.parent.length
p_x = self.parent.xcoords[0]
p_y = self.parent.ycoords[0]
pi_2_180 = (2 * math.pi / 180)
self.xcoords = (p_x + to_pix * (p_length - self.fromtopofparent) * math.sin(self.parent.angle * pi_2_180),
p_x + to_pix * (p_length - self.fromtopofparent) * math.sin(self.parent.angle * pi_2_180) +
to_pix * self.length * math.sin(self.angle * pi_2_180))
self.ycoords = (p_y - to_pix * (p_length - self.fromtopofparent) * math.cos(self.parent.angle * pi_2_180),
p_y - to_pix * (p_length - self.fromtopofparent) * math.cos(self.parent.angle * pi_2_180) -
to_pix * self.length * math.cos(self.angle * pi_2_180))
canvus.create_line(self.xcoords[0], self.ycoords[0], self.xcoords[1], self.ycoords[1],
fill=canvus.master.colours[self.level], width=(seasons - self.level + 1) / 2)
for branch in self.branches:
branch.printtreexytocanvus(canvus, seasons)
class TreeApplication(tkinter.Frame):
''' Tkinter application for showing the tree. '''
def __init__(self, master=None, width=800, height=800, maxseasons=10, newtreelength=3):
super().__init__(master, width=width, height=height)
self.colours = []
self.maxseasons = maxseasons
self.newtreelength = newtreelength # size of the stump
self.grid()
self.quitButton = tkinter.Button(self, text='Quit', command=self.master.destroy)
self.quitButton.grid(row=0, column=0)
self.clearButton = tkinter.Button(self, text='Clear', command=self.clearlines)
self.clearButton.grid(row=1, column=0)
self.genButton = tkinter.Button(self, text='Generate', command=self.gentree)
self.genButton.grid(row=2, column=0)
screenbackground = '#FFFFFF' # For dark mode, try, '#000066'
self.canv = tkinter.Canvas(self, width=width, height=height, background=screenbackground)
self.gentree()
self.canv.grid(column=1, row=0, rowspan=20)
def clearlines(self):
''' Clear all lines on the canvas. '''
objlist = self.canv.find_all()
for obj in objlist:
self.canv.delete(obj)
def gentree(self):
''' Generate the tree '''
self.clearlines()
newtree = TreeBranch(self.newtreelength, 0)
newtree.addbranches()
# min_col=10 here stops the branch colour clashing with the canvas
# If in dark mode, flip to max_col=245
self.colours = gencolours(self.maxseasons + 5, min_col=10)
for _season in range(self.maxseasons):
newtree.lengthen()
newtree.growtree()
newtree.printtreexytocanvus(self.canv, self.maxseasons)
if __name__ == "__main__":
# run as python3 tree.py
root = tkinter.Tk()
app = TreeApplication(root)
app.master.title("Tree generation")
root.mainloop()