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

#!/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 = []