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