diff --git a/world_maker/City.py b/world_maker/City.py index 7fe0b4e..8611be7 100644 --- a/world_maker/City.py +++ b/world_maker/City.py @@ -149,5 +149,5 @@ if __name__ == '__main__': city.loop_expend_district() city.district_draw_map() city.district_generate_road() - image = city.draw_roads(Image.new('RGB', (401, 401)),3) + image = city.draw_roads(Image.new('RGB', (401, 401)),4) image.save('./data/roadmap.png') diff --git a/world_maker/data/building.png b/world_maker/data/building.png new file mode 100644 index 0000000..48e51dc Binary files /dev/null and b/world_maker/data/building.png differ diff --git a/world_maker/data/district.png b/world_maker/data/district.png index 68fd1b2..fc00abf 100644 Binary files a/world_maker/data/district.png and b/world_maker/data/district.png differ diff --git a/world_maker/data/roadmap.png b/world_maker/data/roadmap.png index a80bf1c..841ca64 100644 Binary files a/world_maker/data/roadmap.png and b/world_maker/data/roadmap.png differ diff --git a/world_maker/data/roadmap2.png b/world_maker/data/roadmap2.png index 7209b05..c084d6e 100644 Binary files a/world_maker/data/roadmap2.png and b/world_maker/data/roadmap2.png differ diff --git a/world_maker/data_analysis.py b/world_maker/data_analysis.py index 37d440b..56e061f 100644 --- a/world_maker/data_analysis.py +++ b/world_maker/data_analysis.py @@ -232,6 +232,3 @@ def smooth_sobel_water() -> Image: group = filter_negative(group) group.save('./data/smooth_sobel_watermap.png') return group - - - diff --git a/world_maker/pack_rectangle.py b/world_maker/pack_rectangle.py new file mode 100644 index 0000000..879ae8c --- /dev/null +++ b/world_maker/pack_rectangle.py @@ -0,0 +1,115 @@ +from PIL import Image +import numpy as np +from typing import Union +from data_analysis import handle_import_image + +class Rectangle: + def __init__(self, width, height): + self.width = width + self.height = height + + +class Bin: + def __init__(self, grid): + self.grid = grid + self.rectangles = [] + + def place_rectangle(self, rectangle): + best_spot = None + best_spot_empty_area = float('inf') + + for i in range(len(self.grid[0]) - rectangle.width + 1): # Swap usage of x and y + for j in range(len(self.grid) - rectangle.height + 1): + if self.can_place(rectangle, i, j): + empty_area = self.calculate_empty_area(rectangle, i, j) + if empty_area < best_spot_empty_area: + best_spot = (i, j) + best_spot_empty_area = empty_area + + if best_spot is not None: + self.rectangles.append((best_spot, (best_spot[0]+rectangle.width, best_spot[1]+rectangle.height))) + self.update_grid(rectangle, *best_spot) + return True + + return False + + def calculate_empty_area(self, rectangle, x, y): + empty_area = 0 + for rect_x in range(x, x + rectangle.width): + for rect_y in range(y, y + rectangle.height): + if self.grid[rect_y][rect_x]: # Swap usage of x and y + empty_area += 1 + return empty_area + + def can_place(self, rectangle, x, y): + for rect_x in range(x, x + rectangle.width): + for rect_y in range(y, y + rectangle.height): + if not self.grid[rect_y][rect_x]: # Swap usage of x and y + return False + return True + + def update_grid(self, rectangle, x, y): + for rect_x in range(x, x + rectangle.width): + for rect_y in range(y, y + rectangle.height): + self.grid[rect_y][rect_x] = False # Swap usage of x and y + + +def pack_rectangles(rectangles, grid): + rectangles = sorted(rectangles, key=lambda r: r.width * r.height, reverse=True) + bins = [Bin(grid)] + + for rectangle in rectangles: + for bin in bins: + if bin.place_rectangle(rectangle): + break + else: # No break, meaning rectangle couldn't be placed in any bin + new_bin = Bin(grid) + if new_bin.place_rectangle(rectangle): + bins.append(new_bin) + else: + return False # If a rectangle can't be placed even in a new bin, return False + + return True # If all rectangles can be placed, return True + + +import random + + +def generate_rectangle(max_width, max_height): + width = random.randint(6, 20) + height = random.randint(6, 20) + return Rectangle(width, height) + + +def pack_rectangles(grid): + max_width = len(grid[0]) + max_height = len(grid) + bin = Bin(grid) + + while True: + rectangle = generate_rectangle(max_width // 2, max_height // 2) + if not bin.place_rectangle(rectangle): + break # Stop when a rectangle can't be placed + print(len(bin.rectangles)) + return bin.rectangles # Return the list of rectangles that were placed + + +def draw_rectangles(rectangles, grid): + image = Image.new('RGB', (len(grid[0]), len(grid)), (0, 0, 0)) + for rectangle in rectangles: + start, end = rectangle + for x in range(start[0], end[0]): + for y in range(start[1], end[1]): + image.putpixel((x, y), (144, 255, 144)) + return image + + +def generate_building(image: Union[str, Image] = './data/roadmap2.png'): + image = handle_import_image(image).convert('L') + grid = np.array(image) + rectangles = pack_rectangles(grid) + draw_rectangles(rectangles, grid).save('./data/building.png') + return rectangles + +if __name__ == '__main__': + generate_building() diff --git a/world_maker/world_maker.py b/world_maker/world_maker.py index c52eb13..c86e021 100644 --- a/world_maker/world_maker.py +++ b/world_maker/world_maker.py @@ -17,6 +17,6 @@ if __name__ == '__main__': city.loop_expend_district() city.district_draw_map() city.district_generate_road() - road = city.draw_roads(Image.new('RGB', (401, 401)),3) + road = city.draw_roads(Image.new('RGB', (401, 401)), 4) road.save('./data/roadmap.png') - subtract_map(smooth_sobel_water,road).save('./data/roadmap2.png') + subtract_map(smooth_sobel_water, road).save('./data/roadmap2.png')