From 14ffc19da81e114b0278ba1d55c113c85a524e10 Mon Sep 17 00:00:00 2001 From: josch Date: Wed, 25 Apr 2012 11:45:57 +0200 Subject: [PATCH] spreading is working --- arrange_spread.py | 413 +++++++++++++++++++++++++--------------------- 1 file changed, 228 insertions(+), 185 deletions(-) diff --git a/arrange_spread.py b/arrange_spread.py index 83e75bd..e482669 100644 --- a/arrange_spread.py +++ b/arrange_spread.py @@ -1,6 +1,7 @@ import svg import random +# rounds to the next biggest even number def roundeven(num): return (num+1)/2*2 @@ -11,42 +12,75 @@ def arrange_in_layer(abin, pwidth, pheight): layer = list() rest = list() - root = {'x': 0, 'y': 0, 'width': pwidth, 'height': pheight, 'article': None, 'down': None, 'right': None} - + root = {'x': 0, 'y': 0, + 'width': pwidth, 'height': pheight, + 'article': None, + 'down': None, + 'right': None} + + # traverse the tree until a node is found that is big enough for article + # with size width x height and return this node or None if not found def find_node(root, width, height): - if root['article']: - return find_node(root['right'], width, height) or find_node(root['down'], width, height) + if root is None: + return None + elif root['article']: + return (find_node(root['right'], width, height) + or find_node(root['down'], width, height)) elif width <= root['width'] and height <= root['height']: return root else: return None + # after finding a node where an article fits, put article into the node and + # create childnodes def split_node(node, width, height, article): node['article'] = article - node['article']['pos'] = node['x'], node['y'] - node['down'] = {'x': node['x'], 'y': node['y']+height, 'width': node['width'], 'height': node['height']-height, 'article': None, 'down': None, 'right': None} - node['right'] = {'x': node['x']+width, 'y': node['y'], 'width': node['width']-width, 'height': height, 'article': None, 'down': None, 'right': None} + node['article']['x'] = node['x'] + node['article']['y'] = node['y'] + if node['width'] > 0 and node['height']-height > 0: + node['down'] = {'x': node['x'], 'y': node['y']+height, + 'width': node['width'], + 'height': node['height']-height, + 'article': None, + 'down': None, + 'right': None} + else: + node['down'] = None + if node['width']-width > 0 and height > 0: + node['right'] = {'x': node['x']+width, 'y': node['y'], + 'width': node['width']-width, + 'height': height, + 'article': None, + 'down': None, + 'right': None} + else: + node['right'] = None return node + # for each article in abin, check and place article at a node. If it doesnt + # fit, try to rotate. If it still doesnt fit, append to rest for article in abin: - # output format only accepts integer positions, round package sizes up to even numbers - width, height = article['size'] - width, height = roundeven(width), roundeven(height) + # output format only accepts integer positions, round article sizes up + # to even numbers + owidth, oheight = article['width'], article['height'] + width, height = roundeven(owidth), roundeven(oheight) node = find_node(root, width, height) if (node): node = split_node(node, width, height, article) else: # rotate article - article['size'] = height, width - width, height = article['size'] + article['width'], article['height'] = oheight, owidth + # output format only accepts integer positions, round article sizes up + # to even numbers + width, height = roundeven(oheight), roundeven(owidth) node = find_node(root, width, height) if (node): node = split_node(node, width, height, article) else: rest.append(article) - # traverse tree to find articles + # gather all articles that were def find_articles(node): if not node['article']: return @@ -60,6 +94,10 @@ def arrange_in_layer(abin, pwidth, pheight): return root, layer, rest +# generate a list of random articles of three different type +# each type is of the same size and color +# numbers of articles of each type linearly depend on their area +# articles are generated with more width that height def generate_bin(): abin = [] for i in 1,2,3: @@ -68,182 +106,187 @@ def generate_bin(): w, h = h, w color = random.randint(0,255), random.randint(0,255), random.randint(0,255) for j in range(200000/(w*h)): - abin.append({'pos':(0,0), 'size':(w,h), 'color':color}) + abin.append({'x':0, 'y':0, 'width':w, 'height':h, 'color':color}) return abin +# given a node tree with articles inside, spread them out over the full +# available area evenly +def spread_articles(root): + # get only nodes on the left branch of the tree. This is all nodes below + def get_down_nodes(node): + if node is None or not node['article']: + return [] + else: + return [node] + get_down_nodes(node['down']) -#print abin - -abin = generate_bin() - -#abin = [{'color': (172, 111, 210), 'pos': (0, 0), 'size': (103, 65)}, {'color': (172, 111, 210), 'pos': (0, 0), 'size': (103, 65)}, {'color': (172, 111, 210), 'pos': (0, 0), 'size': (103, 65)}, {'color': (172, 111, 210), 'pos': (0, 0), 'size': (103, 65)}, {'color': (172, 111, 210), 'pos': (0, 0), 'size': (103, 65)}, {'color': (172, 111, 210), 'pos': (0, 0), 'size': (103, 65)}, {'color': (172, 111, 210), 'pos': (0, 0), 'size': (103, 65)}, {'color': (172, 111, 210), 'pos': (0, 0), 'size': (103, 65)}, {'color': (172, 111, 210), 'pos': (0, 0), 'size': (103, 65)}, {'color': (172, 111, 210), 'pos': (0, 0), 'size': (103, 65)}, {'color': (172, 111, 210), 'pos': (0, 0), 'size': (103, 65)}, {'color': (172, 111, 210), 'pos': (0, 0), 'size': (103, 65)}, {'color': (172, 111, 210), 'pos': (0, 0), 'size': (103, 65)}, {'color': (172, 111, 210), 'pos': (0, 0), 'size': (103, 65)}, {'color': (172, 111, 210), 'pos': (0, 0), 'size': (103, 65)}, {'color': (172, 111, 210), 'pos': (0, 0), 'size': (103, 65)}, {'color': (172, 111, 210), 'pos': (0, 0), 'size': (103, 65)}, {'color': (172, 111, 210), 'pos': (0, 0), 'size': (103, 65)}, {'color': (172, 111, 210), 'pos': (0, 0), 'size': (103, 65)}, {'color': (172, 111, 210), 'pos': (0, 0), 'size': (103, 65)}, {'color': (172, 111, 210), 'pos': (0, 0), 'size': (103, 65)}, {'color': (172, 111, 210), 'pos': (0, 0), 'size': (103, 65)}, {'color': (172, 111, 210), 'pos': (0, 0), 'size': (103, 65)}, {'color': (172, 111, 210), 'pos': (0, 0), 'size': (103, 65)}, {'color': (172, 111, 210), 'pos': (0, 0), 'size': (103, 65)}, {'color': (172, 111, 210), 'pos': (0, 0), 'size': (103, 65)}, {'color': (172, 111, 210), 'pos': (0, 0), 'size': (103, 65)}, {'color': (172, 111, 210), 'pos': (0, 0), 'size': (103, 65)}, {'color': (172, 111, 210), 'pos': (0, 0), 'size': (103, 65)}, {'color': (12, 155, 145), 'pos': (0, 0), 'size': (123, 97)}, {'color': (12, 155, 145), 'pos': (0, 0), 'size': (123, 97)}, {'color': (12, 155, 145), 'pos': (0, 0), 'size': (123, 97)}, {'color': (12, 155, 145), 'pos': (0, 0), 'size': (123, 97)}, {'color': (12, 155, 145), 'pos': (0, 0), 'size': (123, 97)}, {'color': (12, 155, 145), 'pos': (0, 0), 'size': (123, 97)}, {'color': (12, 155, 145), 'pos': (0, 0), 'size': (123, 97)}, {'color': (12, 155, 145), 'pos': (0, 0), 'size': (123, 97)}, {'color': (12, 155, 145), 'pos': (0, 0), 'size': (123, 97)}, {'color': (12, 155, 145), 'pos': (0, 0), 'size': (123, 97)}, {'color': (12, 155, 145), 'pos': (0, 0), 'size': (123, 97)}, {'color': (12, 155, 145), 'pos': (0, 0), 'size': (123, 97)}, {'color': (12, 155, 145), 'pos': (0, 0), 'size': (123, 97)}, {'color': (12, 155, 145), 'pos': (0, 0), 'size': (123, 97)}, {'color': (12, 155, 145), 'pos': (0, 0), 'size': (123, 97)}, {'color': (12, 155, 145), 'pos': (0, 0), 'size': (123, 97)}, {'color': (253, 124, 189), 'pos': (0, 0), 'size': (118, 29)}, {'color': (253, 124, 189), 'pos': (0, 0), 'size': (118, 29)}, {'color': (253, 124, 189), 'pos': (0, 0), 'size': (118, 29)}, {'color': (253, 124, 189), 'pos': (0, 0), 'size': (118, 29)}, {'color': (253, 124, 189), 'pos': (0, 0), 'size': (118, 29)}, {'color': (253, 124, 189), 'pos': (0, 0), 'size': (118, 29)}, {'color': (253, 124, 189), 'pos': (0, 0), 'size': (118, 29)}, {'color': (253, 124, 189), 'pos': (0, 0), 'size': (118, 29)}, {'color': (253, 124, 189), 'pos': (0, 0), 'size': (118, 29)}, {'color': (253, 124, 189), 'pos': (0, 0), 'size': (118, 29)}, {'color': (253, 124, 189), 'pos': (0, 0), 'size': (118, 29)}, {'color': (253, 124, 189), 'pos': (0, 0), 'size': (118, 29)}, {'color': (253, 124, 189), 'pos': (0, 0), 'size': (118, 29)}, {'color': (253, 124, 189), 'pos': (0, 0), 'size': (118, 29)}, {'color': (253, 124, 189), 'pos': (0, 0), 'size': (118, 29)}, {'color': (253, 124, 189), 'pos': (0, 0), 'size': (118, 29)}, {'color': (253, 124, 189), 'pos': (0, 0), 'size': (118, 29)}, {'color': (253, 124, 189), 'pos': (0, 0), 'size': (118, 29)}, {'color': (253, 124, 189), 'pos': (0, 0), 'size': (118, 29)}, {'color': (253, 124, 189), 'pos': (0, 0), 'size': (118, 29)}, {'color': (253, 124, 189), 'pos': (0, 0), 'size': (118, 29)}, {'color': (253, 124, 189), 'pos': (0, 0), 'size': (118, 29)}, {'color': (253, 124, 189), 'pos': (0, 0), 'size': (118, 29)}, {'color': (253, 124, 189), 'pos': (0, 0), 'size': (118, 29)}, {'color': (253, 124, 189), 'pos': (0, 0), 'size': (118, 29)}, {'color': (253, 124, 189), 'pos': (0, 0), 'size': (118, 29)}, {'color': (253, 124, 189), 'pos': (0, 0), 'size': (118, 29)}, {'color': (253, 124, 189), 'pos': (0, 0), 'size': (118, 29)}, {'color': (253, 124, 189), 'pos': (0, 0), 'size': (118, 29)}, {'color': (253, 124, 189), 'pos': (0, 0), 'size': (118, 29)}, {'color': (253, 124, 189), 'pos': (0, 0), 'size': (118, 29)}, {'color': (253, 124, 189), 'pos': (0, 0), 'size': (118, 29)}, {'color': (253, 124, 189), 'pos': (0, 0), 'size': (118, 29)}, {'color': (253, 124, 189), 'pos': (0, 0), 'size': (118, 29)}, {'color': (253, 124, 189), 'pos': (0, 0), 'size': (118, 29)}, {'color': (253, 124, 189), 'pos': (0, 0), 'size': (118, 29)}, {'color': (253, 124, 189), 'pos': (0, 0), 'size': (118, 29)}, {'color': (253, 124, 189), 'pos': (0, 0), 'size': (118, 29)}, {'color': (253, 124, 189), 'pos': (0, 0), 'size': (118, 29)}, {'color': (253, 124, 189), 'pos': (0, 0), 'size': (118, 29)}, {'color': (253, 124, 189), 'pos': (0, 0), 'size': (118, 29)}, {'color': (253, 124, 189), 'pos': (0, 0), 'size': (118, 29)}, {'color': (253, 124, 189), 'pos': (0, 0), 'size': (118, 29)}, {'color': (253, 124, 189), 'pos': (0, 0), 'size': (118, 29)}, {'color': (253, 124, 189), 'pos': (0, 0), 'size': (118, 29)}, {'color': (253, 124, 189), 'pos': (0, 0), 'size': (118, 29)}, {'color': (253, 124, 189), 'pos': (0, 0), 'size': (118, 29)}, {'color': (253, 124, 189), 'pos': (0, 0), 'size': (118, 29)}, {'color': (253, 124, 189), 'pos': (0, 0), 'size': (118, 29)}, {'color': (253, 124, 189), 'pos': (0, 0), 'size': (118, 29)}, {'color': (253, 124, 189), 'pos': (0, 0), 'size': (118, 29)}, {'color': (253, 124, 189), 'pos': (0, 0), 'size': (118, 29)}, {'color': (253, 124, 189), 'pos': (0, 0), 'size': (118, 29)}, {'color': (253, 124, 189), 'pos': (0, 0), 'size': (118, 29)}, {'color': (253, 124, 189), 'pos': (0, 0), 'size': (118, 29)}, {'color': (253, 124, 189), 'pos': (0, 0), 'size': (118, 29)}, {'color': (253, 124, 189), 'pos': (0, 0), 'size': (118, 29)}, {'color': (253, 124, 189), 'pos': (0, 0), 'size': (118, 29)}] -#abin = [{'color': (32, 64, 32), 'pos': (0, 0), 'size': (133, 117)}, {'color': (32, 64, 32), 'pos': (144, 0), 'size': (133, 117)}, {'color': (32, 64, 32), 'pos': (286, 0), 'size': (133, 117)}, {'color': (32, 64, 32), 'pos': (428, 0), 'size': (133, 117)}, {'color': (32, 64, 32), 'pos': (570, 0), 'size': (133, 117)}, {'color': (32, 64, 32), 'pos': (0, 120), 'size': (133, 117)}, {'color': (32, 64, 32), 'pos': (144, 120), 'size': (133, 117)}, {'color': (32, 64, 32), 'pos': (286, 120), 'size': (133, 117)}, {'color': (32, 64, 32), 'pos': (428, 120), 'size': (133, 117)}, {'color': (32, 64, 32), 'pos': (570, 120), 'size': (133, 117)}, {'color': (32, 64, 32), 'pos': (0, 238), 'size': (133, 117)}, {'color': (32, 64, 32), 'pos': (136, 238), 'size': (133, 117)}, {'color': (62, 226, 228), 'pos': (712, 0), 'size': (87, 39)}, {'color': (62, 226, 228), 'pos': (712, 48), 'size': (87, 39)}, {'color': (62, 226, 228), 'pos': (712, 120), 'size': (87, 39)}, {'color': (62, 226, 228), 'pos': (712, 168), 'size': (87, 39)}, {'color': (62, 226, 228), 'pos': (272, 238), 'size': (87, 39)}, {'color': (62, 226, 228), 'pos': (360, 238), 'size': (87, 39)}, {'color': (62, 226, 228), 'pos': (448, 238), 'size': (87, 39)}, {'color': (62, 226, 228), 'pos': (536, 238), 'size': (87, 39)}, {'color': (62, 226, 228), 'pos': (624, 238), 'size': (87, 39)}, {'color': (62, 226, 228), 'pos': (712, 238), 'size': (87, 39)}, {'color': (62, 226, 228), 'pos': (272, 286), 'size': (87, 39)}, {'color': (62, 226, 228), 'pos': (360, 286), 'size': (87, 39)}, {'color': (62, 226, 228), 'pos': (448, 286), 'size': (87, 39)}, {'color': (62, 226, 228), 'pos': (536, 286), 'size': (87, 39)}, {'color': (62, 226, 228), 'pos': (624, 286), 'size': (87, 39)}, {'color': (62, 226, 228), 'pos': (712, 286), 'size': (87, 39)}, {'color': (62, 226, 228), 'pos': (0, 356), 'size': (87, 39)}, {'color': (62, 226, 228), 'pos': (90, 356), 'size': (87, 39)}, {'color': (62, 226, 228), 'pos': (180, 356), 'size': (87, 39)}, {'color': (62, 226, 228), 'pos': (270, 356), 'size': (87, 39)}, {'color': (62, 226, 228), 'pos': (360, 356), 'size': (87, 39)}, {'color': (62, 226, 228), 'pos': (448, 356), 'size': (87, 39)}, {'color': (62, 226, 228), 'pos': (536, 356), 'size': (87, 39)}, {'color': (62, 226, 228), 'pos': (624, 356), 'size': (87, 39)}, {'color': (62, 226, 228), 'pos': (712, 356), 'size': (87, 39)}, {'color': (62, 226, 228), 'pos': (0, 396), 'size': (87, 39)}, {'color': (62, 226, 228), 'pos': (90, 396), 'size': (87, 39)}, {'color': (62, 226, 228), 'pos': (180, 396), 'size': (87, 39)}, {'color': (62, 226, 228), 'pos': (270, 396), 'size': (87, 39)}, {'color': (62, 226, 228), 'pos': (360, 396), 'size': (87, 39)}, {'color': (62, 226, 228), 'pos': (448, 396), 'size': (87, 39)}, {'color': (62, 226, 228), 'pos': (536, 396), 'size': (87, 39)}, {'color': (62, 226, 228), 'pos': (624, 396), 'size': (87, 39)}, {'color': (62, 226, 228), 'pos': (712, 396), 'size': (87, 39)}, {'color': (62, 226, 228), 'pos': (0, 436), 'size': (87, 39)}, {'color': (62, 226, 228), 'pos': (90, 436), 'size': (87, 39)}, {'color': (62, 226, 228), 'pos': (180, 436), 'size': (87, 39)}, {'color': (62, 226, 228), 'pos': (270, 436), 'size': (87, 39)}, {'color': (62, 226, 228), 'pos': (360, 436), 'size': (87, 39)}, {'color': (62, 226, 228), 'pos': (448, 436), 'size': (87, 39)}, {'color': (62, 226, 228), 'pos': (536, 436), 'size': (87, 39)}, {'color': (62, 226, 228), 'pos': (624, 436), 'size': (87, 39)}, {'color': (62, 226, 228), 'pos': (712, 436), 'size': (87, 39)}, {'color': (62, 226, 228), 'pos': (0, 476), 'size': (87, 39)}, {'color': (62, 226, 228), 'pos': (90, 476), 'size': (87, 39)}, {'color': (62, 226, 228), 'pos': (180, 476), 'size': (87, 39)}, {'color': (62, 226, 228), 'pos': (270, 476), 'size': (87, 39)}, {'color': (62, 226, 228), 'pos': (360, 476), 'size': (87, 39)}, {'color': (62, 226, 228), 'pos': (448, 476), 'size': (87, 39)}, {'color': (62, 226, 228), 'pos': (536, 476), 'size': (87, 39)}, {'color': (62, 226, 228), 'pos': (624, 476), 'size': (87, 39)}, {'color': (62, 226, 228), 'pos': (712, 476), 'size': (87, 39)}, {'color': (62, 226, 228), 'pos': (0, 516), 'size': (87, 39)}, {'color': (62, 226, 228), 'pos': (94, 516), 'size': (87, 39)}, {'color': (62, 226, 228), 'pos': (188, 516), 'size': (87, 39)}, {'color': (62, 226, 228), 'pos': (282, 516), 'size': (87, 39)}, {'color': (62, 226, 228), 'pos': (376, 516), 'size': (87, 39)}, {'color': (62, 226, 228), 'pos': (470, 516), 'size': (87, 39)}, {'color': (12, 175, 0), 'pos': (712, 96), 'size': (115, 22)}, {'color': (12, 175, 0), 'pos': (712, 216), 'size': (115, 22)}, {'color': (12, 175, 0), 'pos': (272, 334), 'size': (115, 22)}, {'color': (12, 175, 0), 'pos': (410, 334), 'size': (115, 22)}, {'color': (12, 175, 0), 'pos': (548, 334), 'size': (115, 22)}, {'color': (12, 175, 0), 'pos': (684, 334), 'size': (115, 22)}, {'color': (12, 175, 0), 'pos': (564, 524), 'size': (115, 22)}, {'color': (12, 175, 0), 'pos': (684, 524), 'size': (115, 22)}, {'color': (12, 175, 0), 'pos': (0, 556), 'size': (115, 22)}, {'color': (12, 175, 0), 'pos': (138, 556), 'size': (115, 22)}, {'color': (12, 175, 0), 'pos': (276, 556), 'size': (115, 22)}, {'color': (12, 175, 0), 'pos': (412, 556), 'size': (115, 22)}, {'color': (12, 175, 0), 'pos': (548, 556), 'size': (115, 22)}, {'color': (12, 175, 0), 'pos': (684, 556), 'size': (115, 22)}, {'color': (12, 175, 0), 'pos': (0, 578), 'size': (115, 22)}, {'color': (12, 175, 0), 'pos': (138, 578), 'size': (115, 22)}, {'color': (12, 175, 0), 'pos': (276, 578), 'size': (115, 22)}, {'color': (12, 175, 0), 'pos': (412, 578), 'size': (115, 22)}, {'color': (12, 175, 0), 'pos': (548, 578), 'size': (115, 22)}, {'color': (12, 175, 0), 'pos': (684, 578), 'size': (115, 22)}, {'color': (12, 175, 0), 'pos': (0, 0), 'size': (22, 116)}, {'color': (12, 175, 0), 'pos': (0, 0), 'size': (22, 116)}, {'color': (12, 175, 0), 'pos': (0, 0), 'size': (22, 116)}, {'color': (12, 175, 0), 'pos': (0, 0), 'size': (22, 116)}, {'color': (12, 175, 0), 'pos': (0, 0), 'size': (22, 116)}, {'color': (12, 175, 0), 'pos': (0, 0), 'size': (22, 116)}, {'color': (12, 175, 0), 'pos': (0, 0), 'size': (22, 116)}, {'color': (12, 175, 0), 'pos': (0, 0), 'size': (22, 116)}, {'color': (12, 175, 0), 'pos': (0, 0), 'size': (22, 116)}, {'color': (12, 175, 0), 'pos': (0, 0), 'size': (22, 116)}, {'color': (12, 175, 0), 'pos': (0, 0), 'size': (22, 116)}, {'color': (12, 175, 0), 'pos': (0, 0), 'size': (22, 116)}, {'color': (12, 175, 0), 'pos': (0, 0), 'size': (22, 116)}, {'color': (12, 175, 0), 'pos': (0, 0), 'size': (22, 116)}, {'color': (12, 175, 0), 'pos': (0, 0), 'size': (22, 116)}, {'color': (12, 175, 0), 'pos': (0, 0), 'size': (22, 116)}, {'color': (12, 175, 0), 'pos': (0, 0), 'size': (22, 116)}, {'color': (12, 175, 0), 'pos': (0, 0), 'size': (22, 116)}, {'color': (12, 175, 0), 'pos': (0, 0), 'size': (22, 116)}, {'color': (12, 175, 0), 'pos': (0, 0), 'size': (22, 116)}, {'color': (12, 175, 0), 'pos': (0, 0), 'size': (22, 116)}, {'color': (12, 175, 0), 'pos': (0, 0), 'size': (22, 116)}, {'color': (12, 175, 0), 'pos': (0, 0), 'size': (22, 116)}, {'color': (12, 175, 0), 'pos': (0, 0), 'size': (22, 116)}, {'color': (12, 175, 0), 'pos': (0, 0), 'size': (22, 116)}, {'color': (12, 175, 0), 'pos': (0, 0), 'size': (22, 116)}, {'color': (12, 175, 0), 'pos': (0, 0), 'size': (22, 116)}, {'color': (12, 175, 0), 'pos': (0, 0), 'size': (22, 116)}, {'color': (12, 175, 0), 'pos': (0, 0), 'size': (22, 116)}, {'color': (12, 175, 0), 'pos': (0, 0), 'size': (22, 116)}, {'color': (12, 175, 0), 'pos': (0, 0), 'size': (22, 116)}, {'color': (12, 175, 0), 'pos': (0, 0), 'size': (22, 116)}, {'color': (12, 175, 0), 'pos': (0, 0), 'size': (22, 116)}, {'color': (12, 175, 0), 'pos': (0, 0), 'size': (22, 116)}, {'color': (12, 175, 0), 'pos': (0, 0), 'size': (22, 116)}, {'color': (12, 175, 0), 'pos': (0, 0), 'size': (22, 116)}, {'color': (12, 175, 0), 'pos': (0, 0), 'size': (22, 116)}, {'color': (12, 175, 0), 'pos': (0, 0), 'size': (22, 116)}, {'color': (12, 175, 0), 'pos': (0, 0), 'size': (22, 116)}, {'color': (12, 175, 0), 'pos': (0, 0), 'size': (22, 116)}, {'color': (12, 175, 0), 'pos': (0, 0), 'size': (22, 116)}, {'color': (12, 175, 0), 'pos': (0, 0), 'size': (22, 116)}, {'color': (12, 175, 0), 'pos': (0, 0), 'size': (22, 116)}, {'color': (12, 175, 0), 'pos': (0, 0), 'size': (22, 116)}, {'color': (12, 175, 0), 'pos': (0, 0), 'size': (22, 116)}, {'color': (12, 175, 0), 'pos': (0, 0), 'size': (22, 116)}, {'color': (12, 175, 0), 'pos': (0, 0), 'size': (22, 116)}, {'color': (12, 175, 0), 'pos': (0, 0), 'size': (22, 116)}, {'color': (12, 175, 0), 'pos': (0, 0), 'size': (22, 116)}, {'color': (12, 175, 0), 'pos': (0, 0), 'size': (22, 116)}, {'color': (12, 175, 0), 'pos': (0, 0), 'size': (22, 116)}, {'color': (12, 175, 0), 'pos': (0, 0), 'size': (22, 116)}, {'color': (12, 175, 0), 'pos': (0, 0), 'size': (22, 116)}, {'color': (12, 175, 0), 'pos': (0, 0), 'size': (22, 116)}, {'color': (12, 175, 0), 'pos': (0, 0), 'size': (22, 116)}, {'color': (12, 175, 0), 'pos': (0, 0), 'size': (22, 116)}, {'color': (12, 175, 0), 'pos': (0, 0), 'size': (22, 116)}, {'color': (12, 175, 0), 'pos': (0, 0), 'size': (22, 116)}, {'color': (12, 175, 0), 'pos': (0, 0), 'size': (22, 116)}] -#abin = [{'color': (10, 42, 199), 'pos': (0, 0), 'size': (147, 92)}, {'color': (10, 42, 199), 'pos': (164, 0), 'size': (147, 92)}, {'color': (10, 42, 199), 'pos': (328, 0), 'size': (147, 92)}, {'color': (10, 42, 199), 'pos': (490, 0), 'size': (147, 92)}, {'color': (10, 42, 199), 'pos': (652, 0), 'size': (147, 92)}, {'color': (10, 42, 199), 'pos': (0, 94), 'size': (147, 92)}, {'color': (10, 42, 199), 'pos': (164, 94), 'size': (147, 92)}, {'color': (10, 42, 199), 'pos': (328, 94), 'size': (147, 92)}, {'color': (10, 42, 199), 'pos': (490, 94), 'size': (147, 92)}, {'color': (10, 42, 199), 'pos': (652, 94), 'size': (147, 92)}, {'color': (10, 42, 199), 'pos': (0, 188), 'size': (147, 92)}, {'color': (10, 42, 199), 'pos': (152, 188), 'size': (147, 92)}, {'color': (10, 42, 199), 'pos': (304, 188), 'size': (147, 92)}, {'color': (10, 42, 199), 'pos': (456, 188), 'size': (147, 92)}, {'color': (81, 151, 58), 'pos': (606, 194), 'size': (96, 78)}, {'color': (81, 151, 58), 'pos': (704, 194), 'size': (96, 78)}, {'color': (81, 151, 58), 'pos': (0, 282), 'size': (96, 78)}, {'color': (81, 151, 58), 'pos': (102, 282), 'size': (96, 78)}, {'color': (81, 151, 58), 'pos': (204, 282), 'size': (96, 78)}, {'color': (81, 151, 58), 'pos': (304, 282), 'size': (96, 78)}, {'color': (81, 151, 58), 'pos': (404, 282), 'size': (96, 78)}, {'color': (81, 151, 58), 'pos': (504, 282), 'size': (96, 78)}, {'color': (81, 151, 58), 'pos': (604, 282), 'size': (96, 78)}, {'color': (81, 151, 58), 'pos': (704, 282), 'size': (96, 78)}, {'color': (81, 151, 58), 'pos': (0, 362), 'size': (96, 78)}, {'color': (81, 151, 58), 'pos': (102, 362), 'size': (96, 78)}, {'color': (81, 151, 58), 'pos': (204, 362), 'size': (96, 78)}, {'color': (81, 151, 58), 'pos': (304, 362), 'size': (96, 78)}, {'color': (81, 151, 58), 'pos': (404, 362), 'size': (96, 78)}, {'color': (81, 151, 58), 'pos': (504, 362), 'size': (96, 78)}, {'color': (81, 151, 58), 'pos': (604, 362), 'size': (96, 78)}, {'color': (81, 151, 58), 'pos': (704, 362), 'size': (96, 78)}, {'color': (81, 151, 58), 'pos': (0, 442), 'size': (96, 78)}, {'color': (81, 151, 58), 'pos': (102, 442), 'size': (96, 78)}, {'color': (81, 151, 58), 'pos': (204, 442), 'size': (96, 78)}, {'color': (81, 151, 58), 'pos': (304, 442), 'size': (96, 78)}, {'color': (81, 151, 58), 'pos': (404, 442), 'size': (96, 78)}, {'color': (81, 151, 58), 'pos': (504, 442), 'size': (96, 78)}, {'color': (81, 151, 58), 'pos': (604, 442), 'size': (96, 78)}, {'color': (81, 151, 58), 'pos': (704, 442), 'size': (96, 78)}, {'color': (58, 217, 71), 'pos': (0, 520), 'size': (94, 39)}, {'color': (58, 217, 71), 'pos': (102, 520), 'size': (94, 39)}, {'color': (58, 217, 71), 'pos': (204, 520), 'size': (94, 39)}, {'color': (58, 217, 71), 'pos': (306, 520), 'size': (94, 39)}, {'color': (58, 217, 71), 'pos': (406, 520), 'size': (94, 39)}, {'color': (58, 217, 71), 'pos': (506, 520), 'size': (94, 39)}, {'color': (58, 217, 71), 'pos': (606, 520), 'size': (94, 39)}, {'color': (58, 217, 71), 'pos': (706, 520), 'size': (94, 39)}, {'color': (58, 217, 71), 'pos': (0, 560), 'size': (94, 39)}, {'color': (58, 217, 71), 'pos': (102, 560), 'size': (94, 39)}, {'color': (58, 217, 71), 'pos': (204, 560), 'size': (94, 39)}, {'color': (58, 217, 71), 'pos': (306, 560), 'size': (94, 39)}, {'color': (58, 217, 71), 'pos': (406, 560), 'size': (94, 39)}, {'color': (58, 217, 71), 'pos': (506, 560), 'size': (94, 39)}, {'color': (58, 217, 71), 'pos': (606, 560), 'size': (94, 39)}, {'color': (58, 217, 71), 'pos': (706, 560), 'size': (94, 39)}, {'color': (58, 217, 71), 'pos': (0, 0), 'size': (40, 94)}, {'color': (58, 217, 71), 'pos': (0, 0), 'size': (40, 94)}, {'color': (58, 217, 71), 'pos': (0, 0), 'size': (40, 94)}, {'color': (58, 217, 71), 'pos': (0, 0), 'size': (40, 94)}, {'color': (58, 217, 71), 'pos': (0, 0), 'size': (40, 94)}, {'color': (58, 217, 71), 'pos': (0, 0), 'size': (40, 94)}, {'color': (58, 217, 71), 'pos': (0, 0), 'size': (40, 94)}, {'color': (58, 217, 71), 'pos': (0, 0), 'size': (40, 94)}, {'color': (58, 217, 71), 'pos': (0, 0), 'size': (40, 94)}, {'color': (58, 217, 71), 'pos': (0, 0), 'size': (40, 94)}, {'color': (58, 217, 71), 'pos': (0, 0), 'size': (40, 94)}, {'color': (58, 217, 71), 'pos': (0, 0), 'size': (40, 94)}, {'color': (58, 217, 71), 'pos': (0, 0), 'size': (40, 94)}, {'color': (58, 217, 71), 'pos': (0, 0), 'size': (40, 94)}, {'color': (58, 217, 71), 'pos': (0, 0), 'size': (40, 94)}, {'color': (58, 217, 71), 'pos': (0, 0), 'size': (40, 94)}, {'color': (58, 217, 71), 'pos': (0, 0), 'size': (40, 94)}, {'color': (58, 217, 71), 'pos': (0, 0), 'size': (40, 94)}, {'color': (58, 217, 71), 'pos': (0, 0), 'size': (40, 94)}, {'color': (58, 217, 71), 'pos': (0, 0), 'size': (40, 94)}, {'color': (58, 217, 71), 'pos': (0, 0), 'size': (40, 94)}, {'color': (58, 217, 71), 'pos': (0, 0), 'size': (40, 94)}, {'color': (58, 217, 71), 'pos': (0, 0), 'size': (40, 94)}, {'color': (58, 217, 71), 'pos': (0, 0), 'size': (40, 94)}, {'color': (58, 217, 71), 'pos': (0, 0), 'size': (40, 94)}, {'color': (58, 217, 71), 'pos': (0, 0), 'size': (40, 94)}, {'color': (58, 217, 71), 'pos': (0, 0), 'size': (40, 94)}, {'color': (58, 217, 71), 'pos': (0, 0), 'size': (40, 94)}, {'color': (58, 217, 71), 'pos': (0, 0), 'size': (40, 94)}, {'color': (58, 217, 71), 'pos': (0, 0), 'size': (40, 94)}, {'color': (58, 217, 71), 'pos': (0, 0), 'size': (40, 94)}, {'color': (58, 217, 71), 'pos': (0, 0), 'size': (40, 94)}, {'color': (58, 217, 71), 'pos': (0, 0), 'size': (40, 94)}, {'color': (58, 217, 71), 'pos': (0, 0), 'size': (40, 94)}, {'color': (58, 217, 71), 'pos': (0, 0), 'size': (40, 94)}, {'color': (58, 217, 71), 'pos': (0, 0), 'size': (40, 94)}, {'color': (58, 217, 71), 'pos': (0, 0), 'size': (40, 94)}, {'color': (58, 217, 71), 'pos': (0, 0), 'size': (40, 94)}] - -#pwidth, pheight = 400,600 -pwidth, pheight = 800, 600 -abin = sorted(abin, key=lambda article: article['size'][0]*article['size'][1], reverse=True) -root, layer, rest = arrange_in_layer(abin, pwidth, pheight) - -#if rest: -# print "rest!" - -scene = svg.Scene('test1', (pwidth, pheight)) -for a in layer: - scene.add(svg.Rectangle(a['pos'],a['size'],a['color'])) -scene.write() - -# spread vertically - -def get_left_children(node): - if not node['article']: - return [] - else: - return [node] + get_left_children(node['down']) - -def move_tree_down(node, y): - if not node['article']: - return - pos = node['article']['pos'] - node['article']['pos'] = pos[0], pos[1]+y - #if node['down']: # only increase height if not the last node - # node['height'] += y - if node['right']: - move_tree_down(node['right'], y) - if node['down']: - move_tree_down(node['down'], y) - -# for each child on the very left, spread vertically and adjust subtree y accordingly - -# calculate gap size: - -def spread_vertically(node): - leftnodes = get_left_children(node) - for n in leftnodes: - if n['right']: - spread_vertically(n['right']) - if len(leftnodes) == 0: - return - elif len(leftnodes) == 1: - # arrange them in the center of parent - gap = (node['height']-roundeven(leftnodes[0]['article']['size'][1]))/4*2 - move_tree_down(node, gap) - else: - sumleftnodes = sum([roundeven(n['article']['size'][1]) for n in leftnodes]) - d, m = divmod((node['height']-sumleftnodes)/2, len(leftnodes)-1) - gaps = (m)*[(d+1)*2]+((len(leftnodes)-1)-m)*[d*2] - #vgap = max(0, vgap-1) - - #leftnodes[0]['height'] += vgap - # iteratively move trees down by vgap except for first row - for node, gap in zip(leftnodes[1:], gaps): + # move this node and its whole subtree down + def move_tree_down(node, y): + if not node['article']: + return + node['article']['y'] += y + if node['right']: + move_tree_down(node['right'], y) + if node['down']: + move_tree_down(node['down'], y) + + # for each child on the very left, spread vertically and adjust subtree y + # position accordingly + def spread_vertically(node): + downnodes = get_down_nodes(node) + # process innermost nodes before outer nodes + for n in downnodes: + if n['right']: + spread_vertically(n['right']) + if len(downnodes) == 0: + return + elif len(downnodes) == 1: + # arrange them in the center of parent + # treat the article height as even and round the gap size to the + # next smallest even number + gap = (node['height']-roundeven(downnodes[0]['article']['height']))/4*2 move_tree_down(node, gap) + else: + # get the sum of all heights of the leftmodes articles as if they + # had even heights + sumdownnodes = sum([roundeven(n['article']['height']) for n in downnodes]) + # do some fancy math to figure out even gap sizes between + # the nodes + d, m = divmod((node['height']-sumdownnodes)/2, len(downnodes)-1) + gaps = (m)*[(d+1)*2]+((len(downnodes)-1)-m)*[d*2] + # iteratively move trees down by vgap except for first row + for node, gap in zip(downnodes[1:], gaps): + move_tree_down(node, gap) + + # for a given node, return a tuple consisting of a list of nodes that + # make out the longest row in horizontal direction and a list of nodes that + # start a shorter end + def get_max_horiz_nodes(node): + if node is None or not node['article']: + return [], [] + elif node['down'] and node['down']['article']: + # if the node has an article below, check out the rightbranch + rightbranch, sr = get_max_horiz_nodes(node['right']) + rightbranch = [node] + rightbranch + # as well as the down branch + downbranch, sd = get_max_horiz_nodes(node['down']) + # get information about the last article in each branch + ar = rightbranch[len(rightbranch)-1]['article'] + ad = downbranch[len(downbranch)-1]['article'] + # and return as the first tuple entry the branch that stretches the + # longest while having as the second tuple entry the nodes that + # were dismissed as starting shorter branches + if ar['x']+ar['width'] > ad['x']+ad['width']: + return rightbranch, sr+[downbranch[0]] + else: + return downbranch, sd+[rightbranch[0]] + else: + # if there is no article below, just recursively call itself on the + # next node to the right + rightbranch, short = get_max_horiz_nodes(node['right']) + return [node] + rightbranch, short + + # move a node and the article inside to the right and reduce node width + # recursively call for children + def move_tree_right(node, x): + if not node['article']: + return + node['article']['x'] += x + node['x'] += x + node['width'] -= x + if node['right']: + move_tree_right(node['right'], x) + if node['down']: + move_tree_right(node['down'], x) + + # for each child on the very right, spread horizontally and adjust subtree + # x position accordingly + def spread_horizontally(node): + maxhoriznodes, short = get_max_horiz_nodes(node['right']) + maxhoriznodes = [node] + maxhoriznodes + if len(maxhoriznodes) == 0: + return + elif len(maxhoriznodes) == 1: + # arrange them in the center of parent + # treat article width as even and round the gap size to the next + # smallest even number + gap = (node['width']-roundeven(maxhoriznodes[0]['article']['width']))/4*2 + maxhoriznodes[0]['article']['x'] += gap + else: + # get the sum of all widths of the articles that make the longest + # row of articles as if they had even widths + summaxhoriznodes= sum([roundeven(n['article']['width']) for n in maxhoriznodes]) + # do some fancy math to figure out even gap sizes between the nodes + d, m = divmod((node['width']-summaxhoriznodes)/2, len(maxhoriznodes)-1) + gaps = (m)*[(d+1)*2]+((len(maxhoriznodes)-1)-m)*[d*2] + # iteratively move trees right by hgap except for first node + for node, gap in zip(maxhoriznodes[1:], gaps): + move_tree_right(node, gap) + # recursively call for all nodes starting a shorter subtree + for node in short: + spread_horizontally(node) + + # spread nodes vertically + spread_vertically(root) + + # and horizontally + for node in get_down_nodes(root): + spread_horizontally(node) -spread_vertically(root) - -#spread horizontically - -def get_right_children(node): - if not node['article']: +# sanity checks +def sanity_check(layer): + def intersects(a1, a2): + return (a1['x'] < a2['x']+a2['width'] + and a1['x']+a1['width'] > a2['x'] + and a1['y'] < a2['y']+a2['height'] + and a1['y']+a1['height'] > a2['y']) + odds = list() + overhangs = list() + inters = list() + for article1 in layer: + if (article1['x']%2 != 0 + or article1['y']%2 != 0): + odds.append(article1) + if (article1['x'] < 0 + or article1['y'] < 0 + or article1['x']+article1['width'] > pwidth + or article1['y']+article1['height'] > pheight): + overhangs.append(article1) + for article2 in layer: + if article1 == article2: + continue + if intersects(article1, article2): + inters.append((article1, article2)) + for odd in odds: + print "odd:", odd + for overhang in overhangs: + print "overhang:", overhang + for inter in inters: + print "intersect:", inter + if len(odds) or len(overhangs) or len(inters): + print layer + exit(1) + +# draw layer of articles +def draw_layer(filename, layer): + scene = svg.Scene(filename, (pwidth, pheight)) + for a in layer: + scene.add(svg.Rectangle((a['x'], a['y']),(a['width'], a['height']),a['color'])) + scene.write() + +# return all articles in a node tree +def find_articles(node): + if node is None or not node['article']: return [] else: - return [node] + get_right_children(node['right']) - -def move_tree_right(node, x): - if not node['article']: - return - pos = node['article']['pos'] - node['article']['pos'] = pos[0]+x, pos[1] - #if node['right']: # only increase width if not the last node - # node['width'] += x - node['width'] -= x - if node['right']: - move_tree_right(node['right'], x) - if node['down']: - move_tree_right(node['down'], x) - -# for each child on the very right, spread horizontically and adjust subtree y accordingly - -# calculate gap size: - -def spread_horizontically(node): - rightnodes = get_right_children(node) - if len(rightnodes) == 0: - return - elif len(rightnodes) == 1: - # arrange them in the center of parent - gap = (node['width']-roundeven(rightnodes[0]['article']['size'][0]))/4*2 - pos = rightnodes[0]['article']['pos'] - rightnodes[0]['article']['pos'] = pos[0]+gap, pos[1] - else: - # TODO: the uppermost row might not be the longest - sumrightnodes = sum([roundeven(n['article']['size'][0]) for n in rightnodes]) - d, m = divmod((node['width']-sumrightnodes)/2, len(rightnodes)-1) - gaps = (m)*[(d+1)*2]+((len(rightnodes)-1)-m)*[d*2] - #hgap = max(0, hgap-1) - - #rightnodes[0]['width'] += hgap - # iteratively move trees right by hgap except for first - for node, gap in zip(rightnodes[1:], gaps): - move_tree_right(node, gap) - - # process inner nodes as well - for n in rightnodes: - if n['down']: - spread_horizontically(n['down']) - -spread_horizontically(root) - -layer = list() - -def find_articles(node): - if not node['article']: - return - layer.append(node['article']) - if node['right']: - find_articles(node['right']) - if node['down']: - find_articles(node['down']) - -find_articles(root) - -def intersects(a1, a2): - return ( - a1['pos'][0] <= a2['pos'][0]+a2['size'][0] - and a1['pos'][0]+a1['size'][0] >= a2['pos'][0] - and a1['pos'][1] <= a2['pos'][1]+a2['size'][1] - and a1['pos'][1]+a1['size'][1] >= a2['pos'][1] - ) - -scene = svg.Scene('test2', (pwidth, pheight)) -for a in layer: - scene.add(svg.Rectangle(a['pos'],a['size'],a['color'])) -scene.write() - -# sanity checks -odds = list() -overhangs = list() -inters = list() -for article1 in layer: - if (article1['pos'][0]%2 != 0 - or article1['pos'][1]%2 != 0): - odds.append((article1['pos'][0], article1['pos'][1])) - if (article1['pos'][0] < 0 - or article1['pos'][1] < 0 - or article1['pos'][0]+article1['size'][0] > pwidth - or article1['pos'][1]+article1['size'][1] > pheight): - overhangs.append((article1['pos'][0], article1['pos'][1])) - for article2 in layer: - if article1 == article2: - continue - if intersects(article1, article2): - inters.append((article1['pos'][0], article1['pos'][1])) -for odd in odds: - print "odd:", odd -for overhang in overhangs: - print "overhang:", overhang -for inter in inters: - print "intersect:", inter -if len(odds) or len(overhangs) or len(inters): - print abin - exit(1) + return [node['article']] + find_articles(node['right']) + find_articles(node['down']) + +if __name__ == "__main__": + abin = generate_bin() + pwidth, pheight = 800, 600 + abin = sorted(abin, key=lambda article: article['width']*article['height'], reverse=True) + root, layer, rest = arrange_in_layer(abin, pwidth, pheight) + draw_layer('test1', layer) + spread_articles(root) + layer = find_articles(root) + draw_layer('test2', layer) + sanity_check(layer)