def test_max_width_height_diag(self): for i in range(1, 101): sizes = [(j, i + 1 - j) for j in range(1, i + 1)] pos = rpack.pack(sizes, max_width=i) w, _ = rpack.bbox_size(sizes, pos) self.assertEqual(w, i) pos = rpack.pack(sizes, max_height=i) _, h = rpack.bbox_size(sizes, pos) self.assertEqual(h, i)
def test_max_width_height_both2(self): sizes = [(2736, 3648), (2736, 3648), (3648, 2736), (2736, 3648), (2736, 3648)] max_height = max_width = 14130 pos = rpack.pack(sizes, max_width=max_width, max_height=max_height) self.assertLessEqual(max(*rpack.bbox_size(sizes, pos)), max_width) index = rpack._core.overlapping(sizes, pos) self.assertFalse(index)
def _bbox_rpack(component_sizes, pad_x=1.0, pad_y=1.0): """Compute bounding boxes for individual components by arranging them as densly as possible. Depends on `rectangle-packer`. """ try: import rpack except ImportError: raise ImportError( "Using the 'components layout' requires the installation of " "the `rectangle-packer`. You can install it with " "`pip install rectangle-packer`.") dimensions = [_get_bbox_dimensions(n, power=0.8) for n in component_sizes] # rpack only works on integers; sizes should be in descending order dimensions = [(int(width + pad_x), int(height + pad_y)) for (width, height) in dimensions[::-1]] origins = rpack.pack(dimensions) outer_dimensions = rpack.enclosing_size(dimensions, origins) aspect_ratio = outer_dimensions[0] / outer_dimensions[1] if aspect_ratio > 1: scale_width, scale_height = 1, aspect_ratio else: scale_width, scale_height = aspect_ratio, 1 bboxes = [( x, y, width * scale_width - pad_x, height * scale_height - pad_y, ) for (x, y), (width, height) in zip(origins, dimensions)] return bboxes[::-1]
def test_max_width_height_both3(self): sizes = [(3, 4), (3, 4), (4, 3), (3, 4), (3, 4)] max_height = max_width = 9 pos = rpack.pack(sizes, max_width=max_width, max_height=max_height) self.assertLessEqual(max(*rpack.bbox_size(sizes, pos)), max_width) index = rpack._core.overlapping(sizes, pos) self.assertFalse(index)
def get_packed(boxes): sizes = [(int(box[2] - box[0]), int(box[3] - box[1])) for box in boxes] heights = [int(box[3] - box[1]) for box in boxes] #arr = np.array(sizes, dtype=[('w', int), ('h', int)]) arr = np.array(heights) sorted_ind = np.argsort(arr) sorted_ind = list(sorted_ind[::-1]) sorted_sizes = sorted(sizes, key=lambda x: x[1], reverse=True) #sorted_sizes = np.sort(arr, order='h') #sorted_sizes = sorted_sizes[::-1] #sorted_sizes = list(sorted_sizes) #from IPython import embed #embed() positions = rpack.pack(sorted_sizes) shape = rpack.enclosing_size(sorted_sizes, positions) new_img = np.zeros((shape[1], shape[0], 3)) for i in range(len(positions)): x, y = positions[i] w, h = sorted_sizes[i] box = boxes[sorted_ind[i]] img = cv2.imread(box[4]) img = np.array(img) new_img[y:y + h, x:x + w, :] = img[int(box[1]):int(box[1]) + h, int(box[0]):int(box[0] + w)] return new_img
def _get_component_bboxes(component_sizes, pad_x=1., pad_y=1.): dimensions = [_get_bbox_dimensions(n, power=0.8) for n in component_sizes] # rpack only works on integers; sizes should be in descending order dimensions = [(int(width + pad_x), int(height + pad_y)) for (width, height) in dimensions[::-1]] origins = rpack.pack(dimensions) bboxes = [(x, y, width-pad_x, height-pad_y) for (x,y), (width, height) in zip(origins, dimensions)] return bboxes[::-1]
def pack(allRect, canvasSize): #stores width and height from allRect which was given rects = [(rect.width, rect.height) for rect in allRect] location = rpack.pack(rects) #zip of tuple iterators to place in appropriate locations when drawn for locat, rect in zip(location, allRect): rect.x = locat[0] rect.y = locat[1]
def pack(allRect, canvasSize): # uses rectangle-packer to sort rectangles rects = [(rect.width, rect.height) for rect in allRect] available = rpack.pack(rects) for position, rect in zip(available, allRect): # sets positions for rectangles rect.x = position[0] rect.y = position[1]
def pack(allRect, canvasSize): rects = [(rect.width, rect.height) for rect in allRect] positions = rpack.pack(rects) for position, rect in zip(positions, allRect): rect.x = position[0] rect.y = position[1]
def test(n): random.seed(0) sizes = [(random.randint(1, 1000), random.randint(1, 1000)) for _ in range(n)] pos = rpack.pack(sizes) assert not rpack._core.overlapping(sizes, pos) w, h = rpack._core.bbox_size(sizes, pos) p = rpack._core.packing_density(sizes, pos) print(f'encl (w={w} x h={h}), area={w*h}, rho={p}')
def average_time(nr_rectangles): N = 10 t0 = time.time() for i in range(N): sizes = [(randint(10, 120), randint(10, 120)) for _ in range(nr_rectangles)] positions = rpack.pack(sizes) t1 = time.time() return (t1 - t0) / N
def test_max_width_height_both4(self): sizes = [(3, 4), (3, 4), (4, 3), (3, 4), (3, 4)] max_width, max_height = (16, 4) pos = rpack.pack(sizes, max_width=max_width, max_height=max_height) w, h = rpack.bbox_size(sizes, pos) self.assertLessEqual(w, max_width) self.assertLessEqual(h, max_height) index = rpack._core.overlapping(sizes, pos) self.assertFalse(index)
def test_no_overlap(self): """Make sure no rectangles overlap""" for i in range(10, 101, 10): with self.subTest(seed=i): random.seed(i) sizes = [(random.randint(1, i), random.randint(1, i)) for _ in range(110 - i)] pos = rpack.pack(sizes) self.assertFalse(rpack._core.overlapping(sizes, pos))
def pack(rect_list): tempList = [] pos = [] for i in rect_list: tempList.append((int(i.get_h()), int(i.get_w()))) pos = rpack.pack(tempList) return pos
def test_backwards_compatible(self): for i in range(10): random.seed(i) sizes = [(random.randint(1, 50), random.randint(1, 50)) for _ in range(20)] pos1 = rpack._rpack.pack(sizes) self.assertFalse(rpack._core.overlapping(sizes, pos1)) pos2 = rpack.pack(sizes) self.assertFalse(rpack._core.overlapping(sizes, pos2)) self.assertLessEqual( rpack._core.packing_density(sizes, pos1), rpack._core.packing_density(sizes, pos2))
def _get_component_bboxes(component_sizes, pad_x=1., pad_y=1.): dimensions = [_get_bbox_dimensions(n, power=0.8) for n in component_sizes] # rpack only works on integers dimensions = [(int(width + pad_x), int(height + pad_y)) for (width, height) in dimensions] # rpack works best if the heights are in descending order dimensions = dimensions[::-1] origins = rpack.pack(dimensions) bboxes = [(x, y, width-pad_x, height-pad_y) for (x,y), (width, height) in zip(origins, dimensions)] # reverse order to match order of components bboxes = bboxes[::-1] return bboxes
def test_no_overlap(): """Make sure no rectangles overlap""" random.seed(123) rectangles = [(random.randint(50, 100), random.randint(50, 100)) for _ in range(40)] positions = rpack.pack(rectangles) for i, ((x1, y1), (w1, h1)) in enumerate(zip(positions, rectangles)): for j, ((x2, y2), (w2, h2)) in enumerate(zip(positions, rectangles)): if i != j: disjoint_in_x = (x1 + w1 <= x2 or x2 + w2 <= x1) disjoint_in_y = (y1 + h1 <= y2 or y2 + h2 <= y1) assert disjoint_in_x or disjoint_in_y
def test_perfect_pack(): """Pack rectangles to perfect rectangle Like this:: aaa bb cc --> aaabb aaa bb aaabb aaa aaacc """ rectangles = [(3, 3), (2, 2), (2, 1)] positions = [(0, 0), (3, 0), (3, 2)] assert rpack.pack(rectangles) == positions
def test_perfect_pack(self): """Pack rectangles to perfect rectangle Like this:: aaa bb cc --> aaabb aaa bb aaabb aaa aaacc """ sizes = [(3, 3), (2, 2), (2, 1)] pos = [(0, 0), (3, 0), (3, 2)] self.assertListEqual(rpack.pack(sizes), pos)
def _pack_components(G, layout, **kwargs): """Attempts to pack components using `layout` to compute component layouts, rotates those component layouts to minimum-area bounding rectangles, and then uses a rectangle packing algorithm to minimize the area of the overall layout. If `scale` is an argument, we rescale the layout after the above. If `dim` is an argument and not equal to 2, we don't have an implementation, so we pass the graph directly into `layout` and bypass the packing procedure """ if kwargs.get('dim', 2) != 2: return layout(G, **kwargs) layouts = [] rectangles = [] if 'scale' in kwargs: subkwargs = dict(kwargs) scale = subkwargs['scale'] del subkwargs['scale'] else: subkwargs = kwargs scale = None for i, c in enumerate(nx.connected_components(G)): if len(c) > 2: cpos = layout(G.subgraph(c), scale=len(c)**.5, **subkwargs) apos = np.array(list(cpos.values())) rpos, dims = _rotate_to_minimize_area(apos) cpos = dict(zip(cpos, rpos)) dims = [int(ceil(z) + .001) for z in dims] elif len(c) == 2: cpos = dict(zip(c, [(-.5, 0), (.5, 0)])) dims = [2, 1] else: cpos = dict(zip(c, [(0, 0)])) dims = [1, 1] layouts.append(cpos) rectangles.append(dims) positions = rpack.pack(rectangles) layout = { v: (vx + rx + w / 2, vy + ry + h / 2) for (rx, ry), (w, h), cpos in zip(positions, rectangles, layouts) for v, (vx, vy) in cpos.items() } if scale is not None: layout_array = np.array(list(layout[v] for v in G)) return dict(zip(G, _scale_layout(layout_array, scale))) else: return layout
def run_once(cls): clk = Clock() results = [] for test in cls.tests: test.build() for cmp in cls.cmps: clk.stop() done = pack(test.rects, rect_cmp=cmp[1]) runtime = clk.get_ticks() er = get_enclosing_rect(done) cv = coverage(done, er) results.append(TestResult(test, cmp, runtime, cv, er)) return results
def test_negative(): """Negative number should raise ValueError""" with pytest.raises(ValueError): rpack.pack([(3, -5)]) with pytest.raises(ValueError): rpack.pack([(-3, 5)]) with pytest.raises(ValueError): rpack.pack([(-3, -5)])
def test_negative(self): """Negative number should raise ValueError""" with self.assertRaises(ValueError): rpack.pack([(3, -5)]) with self.assertRaises(ValueError): rpack.pack([(-3, 5)]) with self.assertRaises(ValueError): rpack.pack([(-3, -5)])
def main(args): random.seed(0) t0 = time.time() while time.time() - t0 < args.duration: func = random.choices([randrec, randneg, randtype], [0.8, 0.1, 0.1]) sizes = func[0]() try: pos = rpack.pack(sizes) except Exception: pass else: if pos: rpack.packing_density(sizes, pos) rpack.bbox_size(sizes, pos) rpack.overlapping(sizes, pos)
def draw_pseudopage(paragraph_texts): paragraphs = [] for paragraph_text in paragraph_texts: paragraphs.append(draw_pseudoparagraph(paragraph_text)) sizes = [img.size for img in paragraphs] placements = rpack.pack(sizes) new_image_width = max( [sizes[n][0] + placements[n][0] for n in range(len(sizes))]) new_image_height = max( [sizes[n][1] + placements[n][1] for n in range(len(sizes))]) new_image = Image.new('RGB', (new_image_width, new_image_height), color=(255, 255, 255)) for n in range(len(paragraphs)): left, top, right, bottom = placements[n][0], placements[n][ 1], placements[n][0] + sizes[n][0], placements[n][1] + sizes[n][1] new_image.paste(paragraphs[n], (left, top, right, bottom)) return new_image
def arrange_images(normalized_posters, blur_factor, blur_radius): """ Arranges images to create a collage. Arguments: norm_time_posters: tuple(float, PIL.Image) Normalized instances of time and area for time and posters respectively. blur_factor: Number of times to apply a blurring operation to diffuse wasted space. blur_radius: Radius of neighbourhood for use as Gaussian blurring parameter. Returns: collage: np.array A collage of images heuristically packed together. """ # as a greedy heuristic sort by size first to minimize wasted area normalized_posters = sorted( normalized_posters, key=lambda x: x.size[0] * x.size[1], reverse=True ) sizes = [x.size for x in normalized_posters] positions = rpack.pack(sizes) max_width = max(a[0] + b[0] for a, b in zip(positions, sizes)) max_height = max(a[1] + b[1] for a, b in zip(positions, sizes)) collage = np.full([max_height + 1, max_width + 1, 3], 255, dtype=np.uint8) deadspace = np.full_like(collage, True) # place images for (x, y), img in zip(positions, normalized_posters): dx, dy = img.size collage[y : y + dy, x : x + dx] = img deadspace[y : y + dy, x : x + dx] = False # identify all deadspace which looks harsh on the eyes deadspace = np.where(deadspace) # diffuse deadspace to get a softer background gaussian_blur = ImageFilter.GaussianBlur(radius=blur_radius) for _ in range(blur_factor): blurred = Image.fromarray(collage).filter(gaussian_blur) collage[deadspace] = np.array(blurred)[deadspace] return collage
def candidates(): """Find bbox with golden ratio proportions""" cands = list() try: i = 0 while True: i += 1 random.seed(i) sizes = [(random.randint(50, 1000), random.randint(50, 1000)) for _ in range(random.randint(30, 40))] pos = rpack.pack(sizes) rho = rpack.packing_density(sizes, pos) w, h = rpack.bbox_size(sizes, pos) if abs(w/h - 1.61803398875) < 0.01: print('Found candidate:', rho, 'seed', i) cands.append((rho, sizes, pos, i)) except KeyboardInterrupt: pass cands.sort(reverse=True) return cands[0:10]
def test_max_width_height_long_running(self): random.seed(123) try: while True: n = random.randint(1, 20) m = 20 sizes = [(random.randint(1, m), random.randint(1, m)) for _ in range(n)] max_width = random.randint(1, m*n + 10) max_height = random.randint(1, m*n + 10) try: pos = rpack.pack(sizes, max_width=max_width, max_height=max_height) except rpack.PackingImpossibleError: continue else: w, h = rpack.bbox_size(sizes, pos) assert w <= max_width assert h <= max_height assert not rpack.overlapping(sizes, pos) except KeyboardInterrupt: print("Stopped.")
def proportional(self, min_size: int = 32, album_count: int = 100) -> None: """ :param min_size: the smallest size for an album in pixels :param album_count: the number of albums to include in the poster :return: None """ from rpack import pack, enclosing_size if self.album_data is None or len(self.album_data.index) < album_count: album_data = get_album_data(album_count, genre_tags=self.genre_tags) else: album_data = self.album_data album_sizes = album_data["msPlayed"].astype(float) album_sizes = album_sizes / min(album_sizes) * min_size album_sizes = [(int(n), int(n)) for n in album_sizes] new_poss = pack(album_sizes) self.album_data = album_data self.positions = new_poss self.album_sizes = album_sizes self.size = enclosing_size(album_sizes, new_poss)
def logo(output_dir='.'): """Create GIF logo used in sphinx documentation""" random.seed(232460) sizes = [(random.randint(50, 1000), random.randint(50, 1000)) for _ in range(random.randint(30, 40))] pos = rpack.pack(sizes) # Sort the rectangles so the animation will plot them from left to # right. sizes = [s for s, _ in sorted(zip(sizes, pos), key=lambda x: x[1])] pos.sort() # Create animation p = PlotPacking(sizes, pos, trim=True) for spine in p.ax.spines.values(): spine.set_linewidth(0) p.animation(os.path.join(output_dir, f'logo'), 1, 0, dpi=40) p = PlotPacking(sizes, pos, trim=True) for spine in p.ax.spines.values(): spine.set_linewidth(0) while p.feed(): pass p.save(os.path.join(output_dir, f'logo')) # Post process gif to only loop animation one time. subprocess.run('convert -loop 1 logo.gif logo.gif', shell=True, check=True)
def plot(sizes, append=''): t0 = time.time() positions = rpack.pack(sizes) t1 = time.time() (width, height), rectangles = enclosing_size(sizes, positions) norm = max(width, height) color = cm.rainbow(np.linspace(0, 1, len(rectangles))) fig1 = plt.figure() ax1 = fig1.add_subplot(111, aspect='equal') ax1.set_xlim([0, norm]) ax1.set_ylim([0, norm]) for r, c in zip(rectangles, color): ax1.add_patch( patches.Rectangle((r.x, r.y), r.width, r.height, facecolor=c, edgecolor='k')) ax1.add_patch( patches.Rectangle((0, 0), width, height, fill=False, linewidth=2)) ax1.invert_yaxis() coverage = sum(w * h for w, h in sizes) / (width * height) ax1.set_title(f'Area coverage: {coverage:.3f}%, t = {(t1 - t0):.3f}s') ax1.yaxis.set_label_position("right") ax1.set_ylabel(f'Enclosing height: {height}') ax1.set_xlabel(f'Enclosing width: {width}') timestamp = '{:%Y-%m-%d_%H%M%S}'.format(datetime.datetime.now()) branch = Repository('..').head.shorthand plt.savefig(f'{branch}_{timestamp}_{append}.png') plt.clf()
other = [Rect( random.randint(from_size, to_size), random.randint(from_size, to_size), i ) for i in range(ocount)] rects.extend(other) colors = [] for r in rects: colors.append(( random.uniform(0.1, 1), random.uniform(0.1, 1), random.uniform(0.1, 1) )) done = pack(rects) cover, size = coverage(done) print cover, size window = pyglet.window.Window(1400, 800, resizable=True) delta_x = 0 delta_y = 0 @window.event def on_draw(): window.clear() gl.glLoadIdentity() gl.glTranslatef(delta_x, delta_y, 0) gl.glScalef(0.5, 0.5, 1.0) draw_rects(done, colors)