You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
586 lines
22 KiB
Python
586 lines
22 KiB
Python
#!/bin/python
|
|
"""
|
|
A module for generating tree sorting layout
|
|
based on pyFPDF.
|
|
GPL3 License, Mara Karayanni December 2022
|
|
"""
|
|
import glob
|
|
import json
|
|
import random
|
|
import re
|
|
import subprocess
|
|
|
|
|
|
class Tree:
|
|
def __init__(self, zine):
|
|
self.zine = zine
|
|
|
|
def design_tree(self, title, size, margin, steps):
|
|
""" Simple layout of branching with black color"""
|
|
initial_steps = steps
|
|
for letter in title:
|
|
|
|
if self.zine.y + size > self.zine.page_break_trigger: # self.zine.h:
|
|
print(self.zine.h)
|
|
print(self.zine.get_y())
|
|
self.zine.add_page()
|
|
center_x = self.zine.w/2 + margin
|
|
self.zine.set_xy(center_x, margin)
|
|
steps = initial_steps
|
|
|
|
point_a = self.zine.get_x()
|
|
point_b = self.zine.get_y()
|
|
print(point_a, point_b)
|
|
self.zine.cell(size, size, letter) # letter here is sorted by the tree
|
|
node_right = self.zine.get_x() + steps / 2
|
|
node_left = point_a - steps / 2
|
|
point_bb = point_b + steps
|
|
self.zine.line(point_a, point_b, node_left, point_bb)
|
|
self.zine.line(point_a, point_b, node_right, point_bb)
|
|
self.zine.set_xy(node_left, point_bb)
|
|
steps *= 2
|
|
|
|
def keys_sorting(self, data):
|
|
""" Make a list of sorted keys using Python's sorted function"""
|
|
global res
|
|
if type(data) is dict:
|
|
for key in sorted(data.keys()):
|
|
res.append(key)
|
|
else:
|
|
for key in sorted(data):
|
|
res.append()
|
|
return res
|
|
|
|
def tree_sorting(self, data):
|
|
""" Make a list of keys, which are first sorted
|
|
by the insert_node function
|
|
"""
|
|
global node
|
|
if type(data) is dict:
|
|
for key in data.keys():
|
|
node = self.insert_node(node, key)
|
|
else:
|
|
for key in data:
|
|
node = self.insert_node(node, key)
|
|
return node
|
|
|
|
def insert_node(self, node, key):
|
|
""" Sorting key elements by recursive call of the function
|
|
and the use of the class Node, which has left and right keys
|
|
"""
|
|
|
|
if node is None:
|
|
node = Node(key)
|
|
return node
|
|
if (key < node.key):
|
|
|
|
# recurse
|
|
node.left = self.insert_node(node.left, key)
|
|
|
|
elif (key > node.key):
|
|
# recurse
|
|
node.right = self.insert_node(node.right, key)
|
|
|
|
return node
|
|
|
|
def print_nodes(self, node):
|
|
""" Print left and right keys of an instance of the class Node,
|
|
mostly for debugging purposes
|
|
"""
|
|
global res
|
|
if (node is not None):
|
|
self.print_nodes(node.left)
|
|
print("LEFT SORTED KEY IS", node.key, end="\n")
|
|
res.append(node.key)
|
|
self.print_nodes(node.right)
|
|
print("RIGHT SORTED KEY IS", node.key, end="\n")
|
|
return res
|
|
|
|
def draw_year(self, data, root, x, y,
|
|
page_height, width, margin, steps=30):
|
|
""" Tree method draw_year layout:
|
|
Simple year stamps in vertical position and progressively
|
|
horizontally to the left side
|
|
"""
|
|
letter_size = 8
|
|
right_margin = margin
|
|
parent_x = x
|
|
parent_y = y
|
|
|
|
if root is not None:
|
|
sorted_root = self.keys_sorting(data)
|
|
print("SORTED ROOT", sorted_root)
|
|
for key in sorted_root:
|
|
|
|
if parent_y >= page_height-margin*2:
|
|
self.zine.add_page()
|
|
self.zine.set_xy(width/2, margin)
|
|
print("NEW PAGE", "Y", self.zine.get_y())
|
|
if parent_x >= width or parent_x <= margin:
|
|
self.zine.set_xy(width/2, parent_y)
|
|
|
|
parent_x = self.zine.get_x()
|
|
parent_y = self.zine.get_y()
|
|
|
|
# draw the year
|
|
self.zine.cell(letter_size, letter_size, str(key)) # draw the root
|
|
print("YEAR", key, "YEAR X Y", parent_x, parent_y)
|
|
|
|
parent_x -= steps/5
|
|
parent_y += steps
|
|
self.zine.set_xy(parent_x, parent_y)
|
|
|
|
def draw_year_name(self, data, root, page_height, margin, steps=30):
|
|
""" Tree method draw_year_name layout:
|
|
Simple year and name/letters stamps in vertical position
|
|
and progressively horizontally to the left side
|
|
"""
|
|
letter_size = 8
|
|
width = self.zine.w
|
|
parent_x = self.zine.get_x()
|
|
parent_y = self.zine.get_y()
|
|
|
|
if root is not None:
|
|
sorted_root = self.keys_sorting(data)
|
|
print("SORTED ROOT", sorted_root)
|
|
for key in sorted_root:
|
|
|
|
if parent_y >= page_height-margin*2:
|
|
self.zine.add_page()
|
|
self.zine.set_xy(width/2, margin)
|
|
print("NEW PAGE", "Y", self.zine.get_y())
|
|
if parent_x >= width or parent_x <= margin:
|
|
self.zine.set_xy(width/2, parent_y)
|
|
|
|
parent_x = self.zine.get_x()
|
|
parent_y = self.zine.get_y()
|
|
|
|
# draw the year
|
|
self.zine.cell(letter_size, letter_size, str(key)) # draw the root
|
|
print("YEAR", key, "YEAR X Y", parent_x, parent_y)
|
|
|
|
# draw the tree names
|
|
val = data[key]["scientificName"]
|
|
child_x = parent_x - steps
|
|
child_y = parent_y + letter_size
|
|
if child_x <= margin:
|
|
print("CHILD OFFSET X", "PARENT X Y", parent_x, parent_y, "STEPS", steps)
|
|
if child_y >= page_height:
|
|
print("CHILD OFFSET Y", "PARENT X Y", parent_x, parent_y, "STEPS", steps)
|
|
if type(val) is list:
|
|
for v in val:
|
|
self.zine.set_draw_color(r=255, g=128, b=0)
|
|
self.zine.line(parent_x, parent_y, child_x, child_y)
|
|
self.zine.set_xy(child_x, child_y)
|
|
self.zine.cell(letter_size, letter_size, str(v)) # draw child
|
|
child_y +=5
|
|
child_x += self.zine.get_x() + 5
|
|
else:
|
|
self.zine.line(parent_x, parent_y, child_x, child_y)
|
|
self.zine.set_xy(child_x, child_y)
|
|
self.zine.cell(letter_size, letter_size, str(val)) # draw child
|
|
child_x += self.zine.get_x() + 5
|
|
|
|
previous_x = parent_x
|
|
previous_y = parent_y
|
|
parent_x -= steps/5
|
|
parent_y += steps
|
|
self.zine.set_xy(parent_x, parent_y)
|
|
self.zine.set_draw_color(r=128, g=255, b=0)
|
|
self.zine.line(
|
|
previous_x, previous_y,
|
|
self.zine.get_x(), self.zine.get_y()
|
|
)
|
|
|
|
def draw_year_name_reddit(self, tree_json, reddit_json, root, page_height,
|
|
width, margin, steps=30):
|
|
""" Tree method draw_year_name_reddit layout:
|
|
Branching sorted year followed by trees named that year on the
|
|
left side and reddit blog titles related to those trees
|
|
on the right side"""
|
|
|
|
letter_size = 8
|
|
right_margin = margin
|
|
parent_x = self.zine.get_x()
|
|
parent_y = self.zine.get_y()
|
|
|
|
if root is not None:
|
|
sorted_root = self.keys_sorting(tree_json)
|
|
print("SORTED ROOT", sorted_root)
|
|
for key in sorted_root:
|
|
|
|
if parent_y >= page_height-margin*2:
|
|
self.zine.add_page()
|
|
self.zine.set_xy(width/2, margin)
|
|
print("NEW PAGE", "Y", self.zine.get_y())
|
|
if parent_x >= width or parent_x <= margin:
|
|
self.zine.set_xy(width/2, parent_y)
|
|
|
|
parent_x = self.zine.get_x()
|
|
parent_y = self.zine.get_y()
|
|
|
|
# draw the year
|
|
self.zine.cell(letter_size, letter_size, str(key)) # draw the root
|
|
print("YEAR", key, "YEAR X Y", parent_x, parent_y)
|
|
|
|
# draw the tree names
|
|
val = tree_json[key]["scientificName"]
|
|
family = tree_json[key]["family"]
|
|
child_x = parent_x - steps
|
|
child_y = parent_y + letter_size
|
|
if type(val) is list:
|
|
for v in val:
|
|
self.zine.set_draw_color(r=255, g=0, b=0)
|
|
self.zine.line(parent_x, parent_y, child_x, child_y)
|
|
self.zine.set_xy(child_x, child_y)
|
|
self.zine.cell(letter_size, letter_size, str(v)) # draw child
|
|
|
|
# draw reddit
|
|
self.zine.set_draw_color(r=0, g=0, b=255)
|
|
for r in reddit_json.keys():
|
|
if r in family:
|
|
reddit_list = reddit_json[r]
|
|
print("REDIT", reddit_list)
|
|
reddit_x = width -2*margin - child_x
|
|
reddit_y = child_y
|
|
for reddit in reddit_list:
|
|
reddit_y += steps/5
|
|
self.zine.set_xy(reddit_x, reddit_y)
|
|
self.zine.cell(letter_size, letter_size, str(reddit)) # draw child
|
|
self.zine.line(child_x, child_y, reddit_x, reddit_y)
|
|
self.zine.set_xy(child_x, child_y)
|
|
|
|
child_y +=5
|
|
child_x += self.zine.get_x() + 5
|
|
else:
|
|
self.zine.line(parent_x, parent_y, child_x, child_y)
|
|
self.zine.set_xy(child_x, child_y)
|
|
self.zine.cell(letter_size, letter_size, str(val)) # draw child
|
|
child_x += self.zine.get_x() + 5
|
|
|
|
previous_x = parent_x
|
|
previous_y = parent_y
|
|
parent_x -= steps/5
|
|
parent_y += steps
|
|
self.zine.set_xy(parent_x, parent_y)
|
|
self.zine.set_draw_color(r=128, g=255, b=0)
|
|
self.zine.line(previous_x, previous_y, self.zine.get_x(), self.zine.get_y())
|
|
|
|
def preorder_position_nodes(self, data, root, page_height,
|
|
width, margin, steps=30):
|
|
letter_size = 5
|
|
right_margin = margin
|
|
parent_x = self.zine.get_x()
|
|
parent_y = self.zine.get_y()
|
|
|
|
if root is not None:
|
|
sorted_root = self.keys_sorting(data)
|
|
print("SORTED ROOT", sorted_root)
|
|
for key in sorted_root:
|
|
|
|
if parent_y >= page_height:
|
|
self.zine.add_page()
|
|
self.zine.set_xy(width/2, margin)
|
|
print("NEW PAGE", "Y", self.zine.get_y())
|
|
if parent_x >= width or parent_x <= margin:
|
|
self.zine.set_xy(width/2, parent_y)
|
|
|
|
parent_x = self.zine.get_x()
|
|
parent_y = self.zine.get_y()
|
|
|
|
# draw the year
|
|
self.zine.cell(letter_size, letter_size, str(key)) # draw the root
|
|
print("YEAR", key, "YEAR X Y", parent_x, parent_y)
|
|
|
|
# draw the tree names
|
|
val = data[key]["scientificName"]
|
|
child_x = parent_x - steps
|
|
child_y = parent_y + letter_size
|
|
if child_x >= right_margin:
|
|
print("CHILD OFFSET", "PARENT X Y", parent_x, parent_y, "STEPS", steps)
|
|
print("VALUE", val, "X", child_x, "Y", child_y)
|
|
if type(val) is list:
|
|
for v in val:
|
|
self.zine.set_draw_color(r=255, g=128, b=0)
|
|
self.zine.line(parent_x, parent_y, child_x, child_y)
|
|
self.zine.set_xy(child_x, child_y)
|
|
self.zine.cell(letter_size, letter_size, str(v)) # draw child
|
|
child_y +=5
|
|
child_x += self.zine.get_x() + 5
|
|
else:
|
|
self.zine.line(parent_x, parent_y, child_x, child_y)
|
|
self.zine.set_xy(child_x, child_y)
|
|
self.zine.cell(letter_size, letter_size, str(val)) # draw child
|
|
child_x += self.zine.get_x() + 5
|
|
|
|
# parent_x -= steps/5
|
|
# parent_y += steps*2
|
|
# self.zine.set_draw_color(r=128, g=255, b=0)
|
|
# self.zine.line(self.zine.get_x(), self.zine.get_y(), parent_x, parent_y)
|
|
|
|
|
|
def weird_position_nodes(self, data, root, page_height, width, margin, steps=30):
|
|
letter_size = 5
|
|
right_margin = margin
|
|
|
|
x = self.zine.get_x()
|
|
y = self.zine.get_y()
|
|
|
|
|
|
if root is None:
|
|
root = self.keys_sorting(data)
|
|
print("SORTED ROOT", root)
|
|
for key in root:
|
|
parent_x = x
|
|
parent_y = y
|
|
|
|
# draw the year
|
|
self.zine.cell(letter_size, letter_size, str(key)) # draw the root
|
|
print("YEAR", key, "YEAR X Y", parent_x, parent_y)
|
|
|
|
# draw the tree names
|
|
val = data[key]["scientificName"]
|
|
child_x = parent_x - steps
|
|
child_y = parent_y + letter_size
|
|
if child_x >= right_margin:
|
|
print("CHILD OFFSET", "PARENT X Y", parent_x, parent_y, "STEPS", steps)
|
|
print("VALUE", val, "X", child_x, "Y", child_y)
|
|
if type(val) is list:
|
|
for v in val:
|
|
self.zine.set_draw_color(r=255, g=128, b=0)
|
|
self.zine.line(parent_x, parent_y, child_x, child_y)
|
|
self.zine.set_xy(child_x, child_y)
|
|
self.zine.cell(letter_size, letter_size, str(v)) # draw child
|
|
child_y +=5
|
|
child_x += self.zine.get_x() + 5
|
|
else:
|
|
self.zine.line(parent_x, parent_y, child_x, child_y)
|
|
self.zine.set_xy(child_x, child_y)
|
|
self.zine.cell(letter_size, letter_size, str(val)) # draw child
|
|
child_x += self.zine.get_x() + 5
|
|
|
|
x -= steps/5
|
|
y += steps
|
|
self.zine.set_draw_color(r=128, g=255, b=0)
|
|
self.zine.line(parent_x, parent_y, x, y)
|
|
# self.zine.set_xy(x, y)
|
|
|
|
if y >= page_height:
|
|
print("NEW PAGE")
|
|
self.zine.add_page()
|
|
self.zine.set_xy(x, margin)
|
|
print("Y", self.zine.get_y())
|
|
if x >= width or x <= margin:
|
|
self.zine.set_xy(width/2, y)
|
|
# else:
|
|
# self.zine.set_xy(x, y)
|
|
|
|
# self.wrap_page(x, y, page_height, width, margin)
|
|
|
|
def position_nodes(self, root, x, y, page_height, margin, steps=26):
|
|
letter_size = 10
|
|
if self.zine.get_y() >= page_height:
|
|
self.zine.add_page()
|
|
self.zine.set_xy(margin, margin)
|
|
x = self.zine.get_x()
|
|
y = self.zine.get_y()
|
|
else:
|
|
self.zine.set_xy(x, y)
|
|
root_x = x
|
|
root_y = y
|
|
|
|
if root is not None:
|
|
# if root.left: # left display node name
|
|
|
|
# draw left branch
|
|
self.zine.set_draw_color(r=255, g=128, b=0)
|
|
node_left_x = root_x - steps / 2
|
|
node_left_y = root_y + steps
|
|
self.zine.cell(letter_size, letter_size, str(root.key)) # letter is sorted
|
|
self.zine.line(root_x, root_y, node_left_x, node_left_y)
|
|
print("LEFT NODE", root.key, node_left_x, node_left_y)
|
|
|
|
# is there more nodes at left side?
|
|
steps *= 2
|
|
self.zine.set_xy(node_left_x, node_left_y)
|
|
self.position_nodes(root.left, node_left_x, node_left_y, page_height, margin)
|
|
|
|
# if root.right: # right display node name
|
|
|
|
# draw right branch
|
|
self.zine.set_draw_color(r=128, g=255, b=0)
|
|
node_right_x = root_x + steps / 2
|
|
node_right_y = root_y + steps
|
|
self.zine.cell(letter_size, letter_size, str(root.key)) # letter is sorted
|
|
self.zine.line(root_x, root_y, node_right_x, node_right_y)
|
|
print("RIGHT NODE", root.key, node_right_x, node_right_y)
|
|
|
|
# is there more nodes at right side?
|
|
steps *= 2
|
|
self.zine.set_xy(node_right_x, node_right_y)
|
|
self.position_nodes(root.right, node_right_x, node_right_y, page_height, margin)
|
|
|
|
def print_2d_tree(self, root, space=0, LEVEL_SPACE = 5):
|
|
if (root == None): return
|
|
space += LEVEL_SPACE
|
|
self.print_2d_tree(root.right, space)
|
|
# print() # neighbor space
|
|
for i in range(LEVEL_SPACE, space): print(end = " ")
|
|
print("|" + str(root.key) + "|<")
|
|
self.print_2d_tree(root.left, space)
|
|
|
|
def draw_tree(self, node, level=0, res=[]):
|
|
if level < len(res):
|
|
if node:
|
|
res[level].append(node.key)
|
|
else:
|
|
res[level].append(" ")
|
|
else:
|
|
if node:
|
|
res.append([node.key])
|
|
else:
|
|
res.append([" "])
|
|
if not node:
|
|
return
|
|
self.draw_tree(node.left, level+1, res)
|
|
self.draw_tree(node.right, level+1, res)
|
|
print("RES from draw tree is", res)
|
|
return res
|
|
|
|
def print_tree(self, node, x, y, width, margin, page_height, steps=5):
|
|
letter_size = 20
|
|
|
|
tree_array = self.draw_tree(node)
|
|
h = len(tree_array)
|
|
white_spaces = (2**h)-1
|
|
|
|
def print_spaces(n, x, i):
|
|
for i in range(n):
|
|
if i % 2 == 0:
|
|
x += float(steps/2)
|
|
else:
|
|
x -= float(steps/2)
|
|
self.zine.set_xy(x, y)
|
|
|
|
prev_x = None
|
|
prev_y = None
|
|
for level in tree_array:
|
|
print("LEVEL is", level, "AND OF TYPE", type(level))
|
|
for i, key in enumerate(level):
|
|
if i==0 and len(level) == 1:
|
|
self.zine.cell(letter_size, letter_size, str(key)) # letter is sorted
|
|
print("FIRST NODE", key, "POSITION OF KEY", x, y, "i is", i)
|
|
prev_x = x
|
|
prev_y = y
|
|
self.zine.set_draw_color(r=0, g=255, b=0)
|
|
print_spaces(1+2*white_spaces, i, x)
|
|
# self.zine.line(prev_x, prev_y, self.zine.get_x(), self.zine.get_y())
|
|
print("NEW xy after first node", self.zine.get_x(), self.zine.get_y())
|
|
else:
|
|
if key == " ":
|
|
pass
|
|
else:
|
|
print("POSITION OF KEY", key, self.zine.get_x(), self.zine.get_y(), "i is", i)
|
|
self.zine.line(prev_x, prev_y, self.zine.get_x(), self.zine.get_y())
|
|
self.zine.cell(letter_size, letter_size, str(key)) # letter is sorted
|
|
print_spaces(1+2*white_spaces, i, x)
|
|
y += steps/2
|
|
if i % 2 == 0:
|
|
x += float(steps)
|
|
else:
|
|
x -= float(steps)
|
|
self.zine.set_xy(x, y)
|
|
print()
|
|
|
|
def print_bin_tree(self, node, x, y, width, margin, page_height, steps=5):
|
|
letter_size = 20
|
|
|
|
tree_array = self.draw_tree(node)
|
|
h = len(tree_array)
|
|
white_spaces = (2**h)-1
|
|
|
|
def print_spaces(n, x, i):
|
|
for i in range(n):
|
|
if i % 2 == 0:
|
|
x += float(steps/2)
|
|
else:
|
|
x -= float(steps/2)
|
|
self.zine.set_xy(x, y)
|
|
|
|
prev_x = None
|
|
prev_y = None
|
|
count = 0
|
|
pos_level = {}
|
|
for level in tree_array:
|
|
# TODO: count the levels and revisit them, keep a dict of level and
|
|
# x,y
|
|
count += 1
|
|
pos_level[count] = (x, y)
|
|
print("LEVEL is", level, "AND OF TYPE", type(level))
|
|
for i, key in enumerate(level):
|
|
if i==0 and len(level) == 1:
|
|
prev_x = x
|
|
prev_y = y
|
|
self.zine.cell(letter_size, letter_size, str(key)) # letter is sorted
|
|
print("FIRST NODE", key, "POSITION OF KEY", x, y, "i is", i)
|
|
print_spaces(1+2*white_spaces, i, x)
|
|
print("NEW xy after first node", self.zine.get_x(), self.zine.get_y())
|
|
else:
|
|
if key == " ":
|
|
pass
|
|
else:
|
|
print("POSITION OF KEY", key, self.zine.get_x(), self.zine.get_y(), "i is", i)
|
|
self.zine.cell(letter_size, letter_size, str(key)) # letter is sorted
|
|
print_spaces(1+2*white_spaces, i, x)
|
|
y += steps/2
|
|
if i % 2 == 0:
|
|
x += float(steps)
|
|
else:
|
|
x -= float(steps)
|
|
self.zine.set_xy(x, y)
|
|
print("LEVELS ARE", pos_level)
|
|
for count, coord in pos_level.items():
|
|
self.zine.set_draw_color(r=0, g=255, b=0)
|
|
try:
|
|
self.zine.line(coord[0], coord[1], pos_level[count+1][0], pos_level[count+1][1])
|
|
except Exception as e:
|
|
print(e)
|
|
|
|
def print_tree_terminal(self, node):
|
|
|
|
tree_array = self.draw_tree(node)
|
|
h = len(tree_array)
|
|
white_spaces = (2**h)-1
|
|
|
|
def print_spaces(n):
|
|
for i in range(n):
|
|
print(" ",end="")
|
|
|
|
for level in tree_array:
|
|
white_spaces = white_spaces//2
|
|
for i, key in enumerate(level):
|
|
if i==0:
|
|
print_spaces(white_spaces)
|
|
print(key, end="")
|
|
print_spaces(1+2*white_spaces)
|
|
print()
|
|
|
|
def wrap_page(self, x, y, page_height, page_width, margin):
|
|
right_margin = page_width/2
|
|
if y >= page_height:
|
|
self.zine.add_page()
|
|
self.zine.set_xy(right_margin, margin)
|
|
elif x >= page_width:
|
|
self.zine.set_xy(right_margin, y)
|
|
else:
|
|
self.zine.set_xy(x, y)
|
|
|
|
class Node:
|
|
|
|
def __init__(self, item = 0):
|
|
self.key = item
|
|
self.left, self.right = None, None
|
|
|
|
root = None
|
|
res = []
|