Added Road map and Buildable map

This commit is contained in:
NichiHachi
2024-06-15 04:55:01 +02:00
parent 18ce4872dd
commit 88632f9c8e
15 changed files with 252 additions and 21 deletions

View File

@@ -1,7 +1,9 @@
from District import District from District import District, Road
from Position import Position from Position import Position
from PIL import Image from PIL import Image
import random import random
from data_analysis import handle_import_image
from typing import Union
class City: class City:
@@ -72,7 +74,6 @@ class City:
index_district_chosen = index index_district_chosen = index
else: else:
self.districts[index].area_expend.remove(point) self.districts[index].area_expend.remove(point)
self.districts[index_district_chosen].area.append(point)
self.districts[index_district_chosen].area_expend_from_point.append(point) self.districts[index_district_chosen].area_expend_from_point.append(point)
self.districts[index_district_chosen].area_expend.remove(point) self.districts[index_district_chosen].area_expend.remove(point)
self.map_data[point.y][point.x] = index_district_chosen + 1 self.map_data[point.y][point.x] = index_district_chosen + 1
@@ -81,7 +82,6 @@ class City:
""" """
Update the expansion points of all districts in the city. Update the expansion points of all districts in the city.
""" """
for district in self.districts: for district in self.districts:
if len(district.area_expend_from_point) > 0: if len(district.area_expend_from_point) > 0:
district.update_expend_points(district.area_expend_from_point[0], self.map_data, self.height_map) district.update_expend_points(district.area_expend_from_point[0], self.map_data, self.height_map)
@@ -117,10 +117,37 @@ class City:
img.save('./data/district.png') img.save('./data/district.png')
print("[City] District map created.") print("[City] District map created.")
def draw_roads(self, image: Union[str, Image], size: int = 1) -> Image:
"""
Draw the roads of the city on the image.
:param size:
:param image: The image to draw the roads on.
"""
image = handle_import_image(image)
for district in self.districts:
district.draw_roads(image, size)
return image
def district_generate_road(self) -> list[Road]:
"""
Generate the roads of the city for each district.
:return: The list of roads of the city.
"""
roads = []
for district in self.districts:
district.generate_roads(self.map_data)
roads.extend(district.roads)
return roads
if __name__ == '__main__': if __name__ == '__main__':
city = City() city = City()
for i in range(10): for i in range(10):
city.add_district(Position(random.randint(0, 800), random.randint(0, 800))) city.add_district(Position(random.randint(0, 400), random.randint(0, 400)))
city.loop_expend_district() city.loop_expend_district()
city.district_draw_map() city.district_draw_map()
city.district_generate_road()
image = city.draw_roads(Image.new('RGB', (401, 401)),3)
image.save('./data/roadmap.png')

View File

@@ -1,8 +1,23 @@
from Position import Position from Position import Position
from typing import Union
from random import randint
from PIL import Image
class Road:
def __init__(self, position: Position, id_height: int, id_width: int, border: bool = False):
self.position: Position = position
self.north: Union[Road, None] = None
self.south: Union[Road, None] = None
self.east: Union[Road, None] = None
self.west: Union[Road, None] = None
self.id_height = id_height
self.id_width = id_width
self.border = border
class District: class District:
""" """
The CustomDistrict class represents a district that can be expanded. The District class represents a district that can be expanded.
Attributes: Attributes:
center_expend (Position): The center position from which the district expands. center_expend (Position): The center position from which the district expands.
@@ -10,6 +25,7 @@ class District:
area_expend_from_point (list): The list of positions from which the district can expand. area_expend_from_point (list): The list of positions from which the district can expand.
area_expend (list): The list of positions to which the district will maybe expand. area_expend (list): The list of positions to which the district will maybe expand.
""" """
def __init__(self, tile_id: int, center: Position, district_type: str = ""): def __init__(self, tile_id: int, center: Position, district_type: str = ""):
""" """
The constructor for the District class. The constructor for the District class.
@@ -23,12 +39,14 @@ class District:
self.tile_id = tile_id self.tile_id = tile_id
self.type = district_type self.type = district_type
self.center_expend = center self.center_expend = center
self.area = [center]
self.area_expend_from_point = [center] self.area_expend_from_point = [center]
self.area_expend = [] self.area_expend = []
self.roads: list[Road] = []
self.roads_expend = []
def verify_point(self, point: Position, point_new: Position, map_data: list[list[int]], height_map: list[list[int]]): def verify_point(self, point: Position, point_new: Position, map_data: list[list[int]],
""" height_map: list[list[int]]):
"""
Verify if a new point can be added to a district extend area list. Verify if a new point can be added to a district extend area list.
:param point: The current point. :param point: The current point.
@@ -37,11 +55,34 @@ class District:
:param height_map: The 2D list representing the height map. :param height_map: The 2D list representing the height map.
:return: True if the new point can be added, False otherwise. :return: True if the new point can be added, False otherwise.
""" """
return (0 <= point_new.x < len(map_data[0]) and return (0 <= point_new.x < len(map_data[0]) and
0 <= point_new.y < len(map_data) and 0 <= point_new.y < len(map_data) and
map_data[point_new.y][point_new.x] == 0 and map_data[point_new.y][point_new.x] == 0 and
(self.type == "Mountain" or (self.type == "Mountain" or
abs(height_map[point_new.y][point_new.x] - height_map[point.y][point.x]) < 2)) abs(height_map[point_new.y][point_new.x] - height_map[point.y][point.x]) < 2))
def is_point_inside(self, point: Position, map_data) -> bool:
"""
Check if a point is inside the district.
:param point: The point to be checked.
:return: True if the point is inside the district, False otherwise.
"""
if not (0 <= point.x < len(map_data[0]) and 0 <= point.y < len(map_data)):
return False
return map_data[point.y][point.x] == self.tile_id
def is_position_in_area_expend(self, position: Position) -> bool:
"""
Check if a position is inside the district.
:param position: The position to be checked.
:return: True if the position is inside the district, False otherwise.
"""
for point in self.area_expend:
if point == position:
return True
return False
def update_expend_points(self, point: Position, map_data: list[list[int]], height_map: list[list[int]]): def update_expend_points(self, point: Position, map_data: list[list[int]], height_map: list[list[int]]):
""" """
@@ -53,6 +94,142 @@ class District:
""" """
for pos in [Position(1, 0), Position(-1, 0), Position(0, 1), Position(0, -1)]: for pos in [Position(1, 0), Position(-1, 0), Position(0, 1), Position(0, -1)]:
if self.verify_point(point, point + pos, map_data, height_map): if self.verify_point(point, point + pos, map_data, height_map):
if point + pos not in self.area_expend: if not self.is_position_in_area_expend(point + pos):
self.area_expend.append(point + pos) self.area_expend.append(point + pos)
self.area_expend_from_point.remove(point) self.area_expend_from_point.remove(point)
def move_point_to_area(self, point: Position, vector: Position, map_data) -> Position:
while not self.is_point_inside(point + vector, map_data):
point += vector
return point + vector
def get_road_from_point(self, point: Position) -> Union[Road, None]:
"""
Get the road that contains a specific point.
:param point: The point to be checked.
:return: The road that contains the point.
"""
for road in self.roads:
if point == road.position:
return road
return None
def get_road_expend_from_point(self, point: Position) -> Union[Road, None]:
"""
Get the road that contains a specific point.
:param point: The point to be checked.
:return: The road that contains the point.
"""
for road in self.roads_expend:
if point == road.position:
return road
return None
def generate_roads(self, map_data, random_range=(20, 40)):
width = {0: self.center_expend.x}
height = {0: self.center_expend.y}
self.roads_expend = [Road(self.center_expend, 0, 0)]
self.roads = [self.roads_expend[0]]
while len(self.roads_expend) > 0:
road = self.roads_expend.pop(0)
print(road.position)
for id_width in [-1, 1]:
if road.id_width + id_width not in width:
width[road.id_width + id_width] = width[road.id_width] + randint(random_range[0],
random_range[1]) * id_width
road_new = Road(Position(width[road.id_width + id_width], road.position.y),
road.id_height, road.id_width + id_width)
if self.is_point_inside(road_new.position, map_data):
road_search = self.get_road_from_point(road_new.position)
road_expend_search = self.get_road_expend_from_point(road_new.position)
if road_search is not None:
road_new = road_search
if id_width == -1:
road.west = road_new
road_new.east = road
else:
road.east = road_new
road_new.west = road
if road_search is None:
self.roads.append(road_new)
self.roads_expend.append(road_new)
else:
self.roads[self.roads.index(road_search)] = road_new
if road_expend_search is not None:
self.roads_expend[self.roads_expend.index(road_expend_search)] = road_new
else:
point_new = self.move_point_to_area(road_new.position, Position(-id_width, 0), map_data)
road_new = Road(point_new, road.id_height, road.id_width + id_width, True)
if id_width == -1:
road.west = road_new
road_new.east = road
else:
road.east = road_new
road_new.west = road
self.roads.append(road_new)
for id_height in [-1, 1]:
if road.id_height + id_height not in height:
height[road.id_height + id_height] = height[road.id_height] + randint(random_range[0],
random_range[1]) * id_height
road_new = Road(Position(road.position.x, height[road.id_height + id_height]),
road.id_height + id_height, road.id_width)
if self.is_point_inside(road_new.position, map_data):
road_search = self.get_road_from_point(road_new.position)
road_expend_search = self.get_road_expend_from_point(road_new.position)
if road_search is not None:
road_new = road_search
if id_height == -1:
road.north = road_new
road_new.south = road
else:
road.south = road_new
road_new.north = road
if road_search is None:
self.roads.append(road_new)
self.roads_expend.append(road_new)
else:
self.roads[self.roads.index(road_search)] = road_new
if road_expend_search is not None:
self.roads_expend[self.roads_expend.index(road_expend_search)] = road_new
else:
pass
point_new = self.move_point_to_area(road_new.position, Position(0, -id_height), map_data)
road_new = Road(point_new, road.id_height + id_height, road.id_width, True)
if id_height == -1:
road.north = road_new
road_new.south = road
else:
road.south = road_new
road_new.north = road
self.roads.append(road_new)
def draw_roads(self, image: Image, size: int = 1):
for road in self.roads:
image.putpixel((road.position.x, road.position.y), (255, 255, 255))
if road.north is not None:
for y in range(road.position.y, road.north.position.y):
image = draw_square(image, Position(road.position.x, y), size)
if road.south is not None:
for y in range(road.position.y, road.south.position.y):
image = draw_square(image, Position(road.position.x, y), size)
if road.east is not None:
for x in range(road.position.x, road.east.position.x):
image = draw_square(image, Position(x, road.position.y), size)
if road.west is not None:
for x in range(road.position.x, road.west.position.x):
image = draw_square(image, Position(x, road.position.y), size)
def draw_square(image, center: Position, size: int) -> Image:
for x in range(center.x - size, center.x + size):
for y in range(center.y - size, center.y + size):
if 0 <= x < image.width and 0 <= y < image.height:
image.putpixel((x, y), (255, 255, 255))
return image

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 112 KiB

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.0 KiB

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 127 KiB

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 148 KiB

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 79 KiB

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

After

Width:  |  Height:  |  Size: 3.4 KiB

View File

@@ -146,8 +146,8 @@ def subtract_map(image: Union[str, Image], substractImage: Union[str, Image]) ->
def group_map(image1: Union[str, Image], image2: Union[str, Image]) -> Image: def group_map(image1: Union[str, Image], image2: Union[str, Image]) -> Image:
image1 = handle_import_image(image1) image1 = handle_import_image(image1).convert('L')
image2 = handle_import_image(image2) image2 = handle_import_image(image2).convert('L')
array1 = np.array(image1) array1 = np.array(image1)
array2 = np.array(image2) array2 = np.array(image2)
@@ -221,3 +221,17 @@ def skeleton_highway_map(image: Union[str, Image] = './data/highwaymap.png'):
skeleton.parse_graph(True) skeleton.parse_graph(True)
heightmap_skeleton = skeleton.map() heightmap_skeleton = skeleton.map()
heightmap_skeleton.save('./data/skeleton_highway.png') heightmap_skeleton.save('./data/skeleton_highway.png')
def smooth_sobel_water() -> Image:
watermap = handle_import_image("./data/watermap.png")
watermap = filter_negative(filter_remove_details(filter_negative(watermap), 5))
sobel = handle_import_image("./data/sobelmap.png")
sobel = filter_remove_details(filter_smooth(sobel, 1), 2)
group = group_map(watermap, sobel)
group = filter_negative(group)
group.save('./data/smooth_sobel_watermap.png')
return group

View File

@@ -1,9 +1,22 @@
import World import World
from PIL import Image from PIL import Image
from data_analysis import get_data, highway_map, filter_sobel, skeleton_highway_map from data_analysis import get_data, highway_map, filter_sobel, skeleton_highway_map, smooth_sobel_water, subtract_map
from City import City
from Position import Position
from random import randint
if __name__ == '__main__': if __name__ == '__main__':
world = World.World() #world = World.World()
heightmap, watermap, treemap = get_data(world) #heightmap, watermap, treemap = get_data(world)
filter_sobel("./data/heightmap.png").save('./data/sobelmap.png') #filter_sobel("./data/heightmap.png").save('./data/sobelmap.png')
skeleton_highway_map(highway_map()) smooth_sobel_water = smooth_sobel_water()
#skeleton_highway_map(highway_map())
city = City()
for i in range(10):
city.add_district(Position(randint(0, 400), randint(0, 400)))
city.loop_expend_district()
city.district_draw_map()
city.district_generate_road()
road = city.draw_roads(Image.new('RGB', (401, 401)),3)
road.save('./data/roadmap.png')
subtract_map(smooth_sobel_water,road).save('./data/roadmap2.png')