Merge remote-tracking branch 'origin/main' into road
This commit is contained in:
44
main.py
44
main.py
@@ -1,4 +1,5 @@
|
|||||||
import random
|
import random
|
||||||
|
from math import exp, sqrt
|
||||||
|
|
||||||
from gdpc import Editor, Block
|
from gdpc import Editor, Block
|
||||||
|
|
||||||
@@ -21,6 +22,9 @@ def main():
|
|||||||
editor = Editor(buffering=True)
|
editor = Editor(buffering=True)
|
||||||
buildArea = editor.getBuildArea()
|
buildArea = editor.getBuildArea()
|
||||||
origin = ((buildArea.begin).x, (buildArea.begin).z)
|
origin = ((buildArea.begin).x, (buildArea.begin).z)
|
||||||
|
center = (abs(buildArea.begin.x - buildArea.end.x) / 2,
|
||||||
|
abs(buildArea.begin.z - buildArea.end.z) / 2)
|
||||||
|
length_world = sqrt((center[0]*2) ** 2 + (center[1]*2) ** 2)
|
||||||
|
|
||||||
remove_trees('./world_maker/data/heightmap.png', './world_maker/data/treemap.png',
|
remove_trees('./world_maker/data/heightmap.png', './world_maker/data/treemap.png',
|
||||||
'./world_maker/data/smooth_sobel_watermap.png')
|
'./world_maker/data/smooth_sobel_watermap.png')
|
||||||
@@ -52,30 +56,42 @@ def main():
|
|||||||
entranceDirection = ["N", "S", "E", "W"]
|
entranceDirection = ["N", "S", "E", "W"]
|
||||||
|
|
||||||
for houses in rectangle_building:
|
for houses in rectangle_building:
|
||||||
start = (houses[0][0]+buildArea.begin[0], houses[0]
|
height = get_height_building_from_center(
|
||||||
[1], houses[0][2]+buildArea.begin[2])
|
center, (houses[0][0], houses[0][2]), length_world)
|
||||||
end = (houses[1][0]+buildArea.begin[0], houses[1]
|
start = (houses[0][0] + origin[0], houses[0]
|
||||||
[1], houses[1][2]+buildArea.begin[2])
|
[1], houses[0][2] + origin[1])
|
||||||
|
end = (houses[1][0] + origin[0], houses[1]
|
||||||
|
[1] + height, houses[1][2] + origin[1])
|
||||||
house = House(editor, start, end,
|
house = House(editor, start, end,
|
||||||
entranceDirection[random.randint(0, 3)], blocks)
|
entranceDirection[random.randint(0, 3)], blocks)
|
||||||
house.build()
|
house.build()
|
||||||
|
|
||||||
for houses in rectangle_house_mountain:
|
for houses in rectangle_house_mountain:
|
||||||
start = (houses[0][0]+buildArea.begin[0], houses[0]
|
start = (houses[0][0] + origin[0], houses[0]
|
||||||
[1], houses[0][2]+buildArea.begin[2])
|
[1], houses[0][2] + origin[1])
|
||||||
end = (houses[1][0]+buildArea.begin[0], houses[1]
|
end = (houses[1][0] + origin[0], houses[1]
|
||||||
[1], houses[1][2]+buildArea.begin[2])
|
[1], houses[1][2] + origin[1])
|
||||||
house = House(editor, start, end,
|
house = House(editor, start, end,
|
||||||
entranceDirection[random.randint(0, 3)], blocks)
|
entranceDirection[random.randint(0, 3)], blocks)
|
||||||
house.build()
|
house.build()
|
||||||
|
|
||||||
|
|
||||||
|
def get_height_building_from_center(center, position, length_world):
|
||||||
|
length = abs(
|
||||||
|
sqrt(((center[0] - position[0]) ** 2 + (center[1] - position[1]) ** 2)))
|
||||||
|
print(length, length_world)
|
||||||
|
return int(exp(-(length / (length_world / 4)) ** 2) * 75 + 30)
|
||||||
|
|
||||||
|
|
||||||
def set_roads_grids(road_grid: Road_grid, origin):
|
def set_roads_grids(road_grid: Road_grid, origin):
|
||||||
for i in range(len(road_grid)):
|
for i in range(len(road_grid)):
|
||||||
if road_grid[i].border:
|
if road_grid[i].border:
|
||||||
for j in range(len(road_grid)):
|
for j in range(len(road_grid)):
|
||||||
# Same line
|
# Same line
|
||||||
if (road_grid[i].position.x == road_grid[j].position.x and road_grid[i].position.y != road_grid[j].position.y) or (road_grid[i].position.x != road_grid[j].position.x and road_grid[i].position.y == road_grid[j].position.y):
|
if (road_grid[i].position.x == road_grid[j].position.x and road_grid[i].position.y != road_grid[
|
||||||
|
j].position.y) or (
|
||||||
|
road_grid[i].position.x != road_grid[j].position.x and road_grid[i].position.y == road_grid[
|
||||||
|
j].position.y):
|
||||||
point_1 = transpose_form_heightmap(
|
point_1 = transpose_form_heightmap(
|
||||||
'./world_maker/data/heightmap.png', (road_grid[i].position.x, road_grid[i].position.y), origin)
|
'./world_maker/data/heightmap.png', (road_grid[i].position.x, road_grid[i].position.y), origin)
|
||||||
point_2 = transpose_form_heightmap(
|
point_2 = transpose_form_heightmap(
|
||||||
@@ -88,7 +104,7 @@ def set_roads(skeleton: Skeleton, origin):
|
|||||||
# Parsing
|
# Parsing
|
||||||
print("[Roads] Start parsing...")
|
print("[Roads] Start parsing...")
|
||||||
for i in range(len(skeleton.lines)):
|
for i in range(len(skeleton.lines)):
|
||||||
print(f"[Roads] Parsing skeleton {i+1}/{len(skeleton.lines)}.")
|
print(f"[Roads] Parsing skeleton {i + 1}/{len(skeleton.lines)}.")
|
||||||
for j in range(len(skeleton.lines[i])):
|
for j in range(len(skeleton.lines[i])):
|
||||||
xyz = transpose_form_heightmap('./world_maker/data/heightmap.png',
|
xyz = transpose_form_heightmap('./world_maker/data/heightmap.png',
|
||||||
skeleton.coordinates[skeleton.lines[i][j]], origin)
|
skeleton.coordinates[skeleton.lines[i][j]], origin)
|
||||||
@@ -97,17 +113,17 @@ def set_roads(skeleton: Skeleton, origin):
|
|||||||
print("[Roads] Start simplification...")
|
print("[Roads] Start simplification...")
|
||||||
# Simplification
|
# Simplification
|
||||||
for i in range(len(skeleton.lines)):
|
for i in range(len(skeleton.lines)):
|
||||||
print(f"[Roads] Simplify skelton {i+1}/{len(skeleton.lines)}")
|
print(f"[Roads] Simplify skelton {i + 1}/{len(skeleton.lines)}")
|
||||||
skeleton.lines[i] = simplify_coordinates(skeleton.lines[i], 40)
|
skeleton.lines[i] = simplify_coordinates(skeleton.lines[i], 10)
|
||||||
|
|
||||||
print("[Roads] Start generation...")
|
print("[Roads] Start generation...")
|
||||||
for i in range(len(skeleton.lines)):
|
for i in range(len(skeleton.lines)):
|
||||||
print(f"[Roads] Generating roads {i+1}/{len(skeleton.lines)}.")
|
print(f"[Roads] Generating roads {i + 1}/{len(skeleton.lines)}.")
|
||||||
if len(skeleton.lines[i]) >= 4:
|
if len(skeleton.lines[i]) >= 4:
|
||||||
Road(Point3D.from_arrays(skeleton.lines[i]), 9)
|
Road(Point3D.from_arrays(skeleton.lines[i]), 9)
|
||||||
else:
|
else:
|
||||||
print(
|
print(
|
||||||
f"[Roads] Ignore roads {i+1} with {len(skeleton.lines[i])} coordinates between {skeleton.lines[i][1]} and {skeleton.lines[i][-1]}.")
|
f"[Roads] Ignore roads {i + 1} with {len(skeleton.lines[i])} coordinates between {skeleton.lines[i][1]} and {skeleton.lines[i][-1]}.")
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
|
|||||||
@@ -170,14 +170,15 @@ class City:
|
|||||||
def generate_district(self):
|
def generate_district(self):
|
||||||
image = handle_import_image('./world_maker/data/smooth_sobel_watermap.png').convert('L')
|
image = handle_import_image('./world_maker/data/smooth_sobel_watermap.png').convert('L')
|
||||||
array = np.array(image)
|
array = np.array(image)
|
||||||
mountain_coo = detect_mountain()
|
mountain = detect_mountain()
|
||||||
self.add_district(Position(mountain_coo[0], mountain_coo[1]), "mountain")
|
for mountain_coo in mountain:
|
||||||
print("[City] District added.")
|
self.add_district(mountain_coo, "mountain")
|
||||||
remove_circle_data(array, mountain_coo)
|
print("[City] Mountain district added.")
|
||||||
|
remove_circle_data(array, (mountain_coo.x, mountain_coo.y))
|
||||||
area = get_area_array(array)
|
area = get_area_array(array)
|
||||||
sizeX, sizeY = len(array[0]), len(array)
|
size_x, size_y = len(array[0]), len(array)
|
||||||
while area > sizeX * sizeY * 0.1:
|
while area > size_x * size_y * 0.1:
|
||||||
x, y = randint(0, sizeX - 1), randint(0, sizeY - 1)
|
x, y = randint(0, size_x - 1), randint(0, size_y - 1)
|
||||||
if array[y][x]:
|
if array[y][x]:
|
||||||
self.add_district(Position(x, y))
|
self.add_district(Position(x, y))
|
||||||
remove_circle_data(array, (x, y))
|
remove_circle_data(array, (x, y))
|
||||||
|
|||||||
@@ -135,7 +135,6 @@ class District:
|
|||||||
self.roads = [self.roads_expend[0]]
|
self.roads = [self.roads_expend[0]]
|
||||||
while len(self.roads_expend) > 0:
|
while len(self.roads_expend) > 0:
|
||||||
road = self.roads_expend.pop(0)
|
road = self.roads_expend.pop(0)
|
||||||
print(road.position)
|
|
||||||
for id_width in [-1, 1]:
|
for id_width in [-1, 1]:
|
||||||
if road.id_width + id_width not in width:
|
if road.id_width + id_width not in width:
|
||||||
width[road.id_width + id_width] = width[road.id_width] + randint(random_range[0],
|
width[road.id_width + id_width] = width[road.id_width] + randint(random_range[0],
|
||||||
|
|||||||
@@ -3,8 +3,8 @@ from PIL import Image, ImageFilter
|
|||||||
import numpy as np
|
import numpy as np
|
||||||
from scipy import ndimage
|
from scipy import ndimage
|
||||||
from world_maker.Skeleton import Skeleton
|
from world_maker.Skeleton import Skeleton
|
||||||
from typing import Union
|
from world_maker.Position import Position
|
||||||
from random import randint
|
from random import randint, choice
|
||||||
import cv2
|
import cv2
|
||||||
|
|
||||||
|
|
||||||
@@ -18,13 +18,13 @@ def get_data(world: World):
|
|||||||
return heightmap, watermap, treemap
|
return heightmap, watermap, treemap
|
||||||
|
|
||||||
|
|
||||||
def handle_import_image(image: Union[str, Image]) -> Image:
|
def handle_import_image(image: str | Image.Image) -> Image.Image:
|
||||||
if isinstance(image, str):
|
if isinstance(image, str):
|
||||||
return Image.open(image)
|
return Image.open(image)
|
||||||
return image
|
return image
|
||||||
|
|
||||||
|
|
||||||
def filter_negative(image: Union[str, Image]) -> Image:
|
def filter_negative(image: str | Image.Image) -> Image.Image:
|
||||||
"""
|
"""
|
||||||
Invert the colors of an image.
|
Invert the colors of an image.
|
||||||
|
|
||||||
@@ -35,7 +35,7 @@ def filter_negative(image: Union[str, Image]) -> Image:
|
|||||||
return Image.fromarray(np.invert(np.array(image)))
|
return Image.fromarray(np.invert(np.array(image)))
|
||||||
|
|
||||||
|
|
||||||
def filter_sobel(image: Union[str, Image]) -> Image:
|
def filter_sobel(image: str | Image.Image) -> Image.Image:
|
||||||
"""
|
"""
|
||||||
Edge detection algorithms from an image.
|
Edge detection algorithms from an image.
|
||||||
|
|
||||||
@@ -68,29 +68,29 @@ def filter_sobel(image: Union[str, Image]) -> Image:
|
|||||||
for i in range(1, h - 1):
|
for i in range(1, h - 1):
|
||||||
for j in range(1, w - 1):
|
for j in range(1, w - 1):
|
||||||
horizontalGrad = (
|
horizontalGrad = (
|
||||||
(horizontal[0, 0] * gray_img[i - 1, j - 1])
|
(horizontal[0, 0] * gray_img[i - 1, j - 1])
|
||||||
+ (horizontal[0, 1] * gray_img[i - 1, j])
|
+ (horizontal[0, 1] * gray_img[i - 1, j])
|
||||||
+ (horizontal[0, 2] * gray_img[i - 1, j + 1])
|
+ (horizontal[0, 2] * gray_img[i - 1, j + 1])
|
||||||
+ (horizontal[1, 0] * gray_img[i, j - 1])
|
+ (horizontal[1, 0] * gray_img[i, j - 1])
|
||||||
+ (horizontal[1, 1] * gray_img[i, j])
|
+ (horizontal[1, 1] * gray_img[i, j])
|
||||||
+ (horizontal[1, 2] * gray_img[i, j + 1])
|
+ (horizontal[1, 2] * gray_img[i, j + 1])
|
||||||
+ (horizontal[2, 0] * gray_img[i + 1, j - 1])
|
+ (horizontal[2, 0] * gray_img[i + 1, j - 1])
|
||||||
+ (horizontal[2, 1] * gray_img[i + 1, j])
|
+ (horizontal[2, 1] * gray_img[i + 1, j])
|
||||||
+ (horizontal[2, 2] * gray_img[i + 1, j + 1])
|
+ (horizontal[2, 2] * gray_img[i + 1, j + 1])
|
||||||
)
|
)
|
||||||
|
|
||||||
newhorizontalImage[i - 1, j - 1] = abs(horizontalGrad)
|
newhorizontalImage[i - 1, j - 1] = abs(horizontalGrad)
|
||||||
|
|
||||||
verticalGrad = (
|
verticalGrad = (
|
||||||
(vertical[0, 0] * gray_img[i - 1, j - 1])
|
(vertical[0, 0] * gray_img[i - 1, j - 1])
|
||||||
+ (vertical[0, 1] * gray_img[i - 1, j])
|
+ (vertical[0, 1] * gray_img[i - 1, j])
|
||||||
+ (vertical[0, 2] * gray_img[i - 1, j + 1])
|
+ (vertical[0, 2] * gray_img[i - 1, j + 1])
|
||||||
+ (vertical[1, 0] * gray_img[i, j - 1])
|
+ (vertical[1, 0] * gray_img[i, j - 1])
|
||||||
+ (vertical[1, 1] * gray_img[i, j])
|
+ (vertical[1, 1] * gray_img[i, j])
|
||||||
+ (vertical[1, 2] * gray_img[i, j + 1])
|
+ (vertical[1, 2] * gray_img[i, j + 1])
|
||||||
+ (vertical[2, 0] * gray_img[i + 1, j - 1])
|
+ (vertical[2, 0] * gray_img[i + 1, j - 1])
|
||||||
+ (vertical[2, 1] * gray_img[i + 1, j])
|
+ (vertical[2, 1] * gray_img[i + 1, j])
|
||||||
+ (vertical[2, 2] * gray_img[i + 1, j + 1])
|
+ (vertical[2, 2] * gray_img[i + 1, j + 1])
|
||||||
)
|
)
|
||||||
|
|
||||||
newverticalImage[i - 1, j - 1] = abs(verticalGrad)
|
newverticalImage[i - 1, j - 1] = abs(verticalGrad)
|
||||||
@@ -105,7 +105,7 @@ def filter_sobel(image: Union[str, Image]) -> Image:
|
|||||||
return image
|
return image
|
||||||
|
|
||||||
|
|
||||||
def filter_smooth_theshold(image: Union[str, Image], radius: int = 3):
|
def filter_smooth_theshold(image: str | Image.Image, radius: int = 3):
|
||||||
"""
|
"""
|
||||||
:param image: white and black image representing the derivative of the terrain (sobel), where black is flat and white is very steep.
|
:param image: white and black image representing the derivative of the terrain (sobel), where black is flat and white is very steep.
|
||||||
:param radius: Radius of the Gaussian blur.
|
:param radius: Radius of the Gaussian blur.
|
||||||
@@ -135,15 +135,14 @@ def filter_smooth_theshold(image: Union[str, Image], radius: int = 3):
|
|||||||
return Image.fromarray(bool_array)
|
return Image.fromarray(bool_array)
|
||||||
|
|
||||||
|
|
||||||
def filter_smooth(image: Union[str, Image], radius: int = 3):
|
def filter_smooth(image: str | Image.Image, radius: int = 3):
|
||||||
|
|
||||||
image = handle_import_image(image)
|
image = handle_import_image(image)
|
||||||
image = image.convert('L')
|
image = image.convert('L')
|
||||||
image = image.filter(ImageFilter.GaussianBlur(radius))
|
image = image.filter(ImageFilter.GaussianBlur(radius))
|
||||||
return image
|
return image
|
||||||
|
|
||||||
|
|
||||||
def subtract_map(image: Union[str, Image], substractImage: Union[str, Image]) -> Image:
|
def subtract_map(image: str | Image.Image, substractImage: str | Image.Image) -> Image.Image:
|
||||||
image = handle_import_image(image)
|
image = handle_import_image(image)
|
||||||
substractImage = handle_import_image(substractImage).convert('L')
|
substractImage = handle_import_image(substractImage).convert('L')
|
||||||
|
|
||||||
@@ -156,7 +155,7 @@ def subtract_map(image: Union[str, Image], substractImage: Union[str, Image]) ->
|
|||||||
return Image.fromarray(array_heightmap)
|
return Image.fromarray(array_heightmap)
|
||||||
|
|
||||||
|
|
||||||
def overide_map(base: Image, top: Image) -> Image:
|
def overide_map(base: Image, top: Image) -> Image.Image:
|
||||||
base = handle_import_image(base).convert('L')
|
base = handle_import_image(base).convert('L')
|
||||||
top = handle_import_image(top).convert('L')
|
top = handle_import_image(top).convert('L')
|
||||||
|
|
||||||
@@ -180,7 +179,7 @@ def overide_map(base: Image, top: Image) -> Image:
|
|||||||
return result_image
|
return result_image
|
||||||
|
|
||||||
|
|
||||||
def group_map(image1: Union[str, Image], image2: Union[str, Image]) -> Image:
|
def group_map(image1: str | Image.Image, image2: str | Image.Image) -> Image.Image:
|
||||||
image1 = handle_import_image(image1).convert('L')
|
image1 = handle_import_image(image1).convert('L')
|
||||||
image2 = handle_import_image(image2).convert('L')
|
image2 = handle_import_image(image2).convert('L')
|
||||||
|
|
||||||
@@ -200,7 +199,7 @@ def filter_smooth_array(array: np.ndarray, radius: int = 3) -> np.ndarray:
|
|||||||
return array
|
return array
|
||||||
|
|
||||||
|
|
||||||
def filter_remove_details(image: Union[str, Image], n: int = 20) -> Image:
|
def filter_remove_details(image: str | Image.Image, n: int = 20) -> Image.Image:
|
||||||
image = handle_import_image(image)
|
image = handle_import_image(image)
|
||||||
array = np.array(image)
|
array = np.array(image)
|
||||||
for _ in range(n):
|
for _ in range(n):
|
||||||
@@ -212,7 +211,7 @@ def filter_remove_details(image: Union[str, Image], n: int = 20) -> Image:
|
|||||||
return image
|
return image
|
||||||
|
|
||||||
|
|
||||||
def highway_map() -> Image:
|
def highway_map() -> Image.Image:
|
||||||
print("[Data Analysis] Generating highway map...")
|
print("[Data Analysis] Generating highway map...")
|
||||||
smooth_sobel = filter_smooth_theshold("./world_maker/data/sobelmap.png", 1)
|
smooth_sobel = filter_smooth_theshold("./world_maker/data/sobelmap.png", 1)
|
||||||
negative_smooth_sobel = filter_negative(smooth_sobel)
|
negative_smooth_sobel = filter_negative(smooth_sobel)
|
||||||
@@ -245,7 +244,7 @@ def create_volume(surface: np.ndarray, heightmap: np.ndarray, make_it_flat: bool
|
|||||||
return volume
|
return volume
|
||||||
|
|
||||||
|
|
||||||
def convert_2D_to_3D(image: Union[str, Image], make_it_flat: bool = False) -> np.ndarray:
|
def convert_2D_to_3D(image: str | Image.Image, make_it_flat: bool = False) -> np.ndarray:
|
||||||
image = handle_import_image(image)
|
image = handle_import_image(image)
|
||||||
heightmap = Image.open(
|
heightmap = Image.open(
|
||||||
'./world_maker/data/heightmap_smooth.png').convert('L')
|
'./world_maker/data/heightmap_smooth.png').convert('L')
|
||||||
@@ -255,7 +254,7 @@ def convert_2D_to_3D(image: Union[str, Image], make_it_flat: bool = False) -> np
|
|||||||
return volume
|
return volume
|
||||||
|
|
||||||
|
|
||||||
def skeleton_highway_map(image: Union[str, Image] = './world_maker/data/highwaymap.png') -> Skeleton:
|
def skeleton_highway_map(image: str | Image.Image = './world_maker/data/highwaymap.png') -> Skeleton:
|
||||||
image_array = convert_2D_to_3D(image, True)
|
image_array = convert_2D_to_3D(image, True)
|
||||||
skeleton = Skeleton(image_array)
|
skeleton = Skeleton(image_array)
|
||||||
skeleton.parse_graph(True)
|
skeleton.parse_graph(True)
|
||||||
@@ -265,7 +264,7 @@ def skeleton_highway_map(image: Union[str, Image] = './world_maker/data/highwaym
|
|||||||
return skeleton
|
return skeleton
|
||||||
|
|
||||||
|
|
||||||
def skeleton_mountain_map(image: Union[str, Image] = './world_maker/data/mountain_map.png') -> Skeleton:
|
def skeleton_mountain_map(image: str | Image.Image = './world_maker/data/mountain_map.png') -> Skeleton:
|
||||||
image_array = convert_2D_to_3D(image, True)
|
image_array = convert_2D_to_3D(image, True)
|
||||||
skeleton = Skeleton(image_array)
|
skeleton = Skeleton(image_array)
|
||||||
skeleton.parse_graph()
|
skeleton.parse_graph()
|
||||||
@@ -275,7 +274,7 @@ def skeleton_mountain_map(image: Union[str, Image] = './world_maker/data/mountai
|
|||||||
return skeleton
|
return skeleton
|
||||||
|
|
||||||
|
|
||||||
def smooth_sobel_water() -> Image:
|
def smooth_sobel_water() -> Image.Image:
|
||||||
watermap = handle_import_image("./world_maker/data/watermap.png")
|
watermap = handle_import_image("./world_maker/data/watermap.png")
|
||||||
watermap = filter_negative(
|
watermap = filter_negative(
|
||||||
filter_remove_details(filter_negative(watermap), 5))
|
filter_remove_details(filter_negative(watermap), 5))
|
||||||
@@ -287,32 +286,104 @@ def smooth_sobel_water() -> Image:
|
|||||||
return group
|
return group
|
||||||
|
|
||||||
|
|
||||||
def detect_mountain(image: Union[str, Image] = './world_maker/data/sobelmap.png') -> Image:
|
def mountain_map_expend(mountain_map: list[list[int]], starting_point: tuple[int, int], value: int):
|
||||||
image = handle_import_image(image)
|
explore_points = [starting_point]
|
||||||
sobel = np.array(image)
|
while len(explore_points) > 0:
|
||||||
pixels = sobel.reshape((-1, 1))
|
x, y = explore_points.pop(0)
|
||||||
pixels = np.float32(pixels)
|
mountain_map[y][x] = value
|
||||||
|
for i in range(-1, 2):
|
||||||
|
for j in range(-1, 2):
|
||||||
|
if (0 <= x + i < len(mountain_map[0]) and 0 <= y + j < len(mountain_map) and
|
||||||
|
mountain_map[y + j][x + i] == 0):
|
||||||
|
if (x + i, y + j) not in explore_points:
|
||||||
|
explore_points.append((x + i, y + j))
|
||||||
|
|
||||||
criteria = (cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_MAX_ITER, 100, 0.2)
|
|
||||||
k = 3
|
|
||||||
_, labels, centers = cv2.kmeans(
|
|
||||||
pixels, k, None, criteria, 10, cv2.KMEANS_RANDOM_CENTERS)
|
|
||||||
|
|
||||||
centers = np.uint8(centers)
|
def set_values_of_building_mountain(mountain_map: list[list[int]], area_mountain: list[int],
|
||||||
segmented_image = centers[labels.flatten()]
|
building_map: str | Image.Image = "./world_maker/data/smooth_sobel_watermap.png"):
|
||||||
segmented_image = segmented_image.reshape(sobel.shape)
|
building_map = handle_import_image(building_map).convert('L')
|
||||||
mountain = segmented_image == segmented_image.max()
|
for y in range(building_map.size[1]):
|
||||||
|
for x in range(building_map.size[0]):
|
||||||
|
if building_map.getpixel((x, y)) > 144:
|
||||||
|
if mountain_map[y][x] != -1:
|
||||||
|
area_mountain[mountain_map[y][x] - 1] += 1
|
||||||
|
|
||||||
contours, _ = cv2.findContours(mountain.astype(
|
|
||||||
np.uint8), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
|
|
||||||
|
|
||||||
max_contour = max(contours, key=cv2.contourArea)
|
def get_index_of_biggest_area_mountain(area_mountain: list[int], exception: list[int]) -> int:
|
||||||
M = cv2.moments(max_contour)
|
max_value = -1
|
||||||
cX = int(M["m10"] / M["m00"])
|
index = -1
|
||||||
cY = int(M["m01"] / M["m00"])
|
for i in range(len(area_mountain)):
|
||||||
|
if i not in exception and area_mountain[i] > max_value:
|
||||||
|
max_value = area_mountain[i]
|
||||||
|
index = i
|
||||||
|
return index
|
||||||
|
|
||||||
print(f"[Data Analysis] The center of the mountain is at ({cX}, {cY})")
|
def get_random_point_in_area_mountain(mountain_map: list[list[int]], index: int) -> Position | None:
|
||||||
return (cX, cY)
|
points = []
|
||||||
|
for y in range(len(mountain_map)):
|
||||||
|
for x in range(len(mountain_map[0])):
|
||||||
|
if mountain_map[y][x] == index + 1:
|
||||||
|
points.append(Position(x, y))
|
||||||
|
if not points:
|
||||||
|
return None
|
||||||
|
return choice(points)
|
||||||
|
|
||||||
|
def get_center_of_area_mountain(mountain_map: list[list[int]], index: int) -> Position:
|
||||||
|
sum_x = 0
|
||||||
|
sum_y = 0
|
||||||
|
count = 0
|
||||||
|
for y in range(len(mountain_map)):
|
||||||
|
for x in range(len(mountain_map[0])):
|
||||||
|
if mountain_map[y][x] == index + 1:
|
||||||
|
sum_x += x
|
||||||
|
sum_y += y
|
||||||
|
count += 1
|
||||||
|
center = Position(sum_x // count, sum_y // count)
|
||||||
|
if mountain_map[center.y][center.x] != index + 1:
|
||||||
|
return get_random_point_in_area_mountain(mountain_map, index)
|
||||||
|
return center
|
||||||
|
|
||||||
|
|
||||||
|
def detect_mountain(number_of_mountain: int = 2, height_threshold: int = 10,
|
||||||
|
image_heightmap: str | Image.Image = './world_maker/data/heightmap.png') -> list[Position]:
|
||||||
|
print("[Data Analysis] Detecting mountains...")
|
||||||
|
image_heightmap = handle_import_image(image_heightmap).convert('L')
|
||||||
|
|
||||||
|
avg_height = 0
|
||||||
|
for y in range(image_heightmap.size[1]):
|
||||||
|
for x in range(image_heightmap.size[0]):
|
||||||
|
avg_height += image_heightmap.getpixel((x, y))
|
||||||
|
avg_height = int(avg_height / (image_heightmap.size[0] * image_heightmap.size[1]))
|
||||||
|
print("[Data Analysis] Average height:", avg_height)
|
||||||
|
|
||||||
|
mountain_map = [[-1 if image_heightmap.getpixel((x, y)) < (avg_height + height_threshold) else 0 for x in
|
||||||
|
range(image_heightmap.size[0])] for y in
|
||||||
|
range(image_heightmap.size[1])]
|
||||||
|
|
||||||
|
area_mountain = []
|
||||||
|
for y in range(image_heightmap.size[1]):
|
||||||
|
for x in range(image_heightmap.size[0]):
|
||||||
|
if mountain_map[y][x] == 0:
|
||||||
|
area_mountain.append(0)
|
||||||
|
mountain_map_expend(mountain_map, (x, y), len(area_mountain))
|
||||||
|
|
||||||
|
if not area_mountain:
|
||||||
|
print("[Data Analysis] No mountain detected.")
|
||||||
|
return []
|
||||||
|
|
||||||
|
set_values_of_building_mountain(mountain_map, area_mountain)
|
||||||
|
if number_of_mountain < len(area_mountain):
|
||||||
|
index_mountain = []
|
||||||
|
for n in range(number_of_mountain):
|
||||||
|
index_mountain.append(get_index_of_biggest_area_mountain(area_mountain, index_mountain))
|
||||||
|
else:
|
||||||
|
index_mountain = [i for i in range(len(area_mountain))]
|
||||||
|
|
||||||
|
position_mountain = []
|
||||||
|
for i in range(len(index_mountain)):
|
||||||
|
position_mountain.append(get_center_of_area_mountain(mountain_map, index_mountain[i]))
|
||||||
|
|
||||||
|
return position_mountain
|
||||||
|
|
||||||
|
|
||||||
def rectangle_2D_to_3D(rectangle: list[tuple[tuple[int, int], tuple[int, int]]],
|
def rectangle_2D_to_3D(rectangle: list[tuple[tuple[int, int], tuple[int, int]]],
|
||||||
@@ -323,18 +394,21 @@ def rectangle_2D_to_3D(rectangle: list[tuple[tuple[int, int], tuple[int, int]]],
|
|||||||
new_rectangle = []
|
new_rectangle = []
|
||||||
for rect in rectangle:
|
for rect in rectangle:
|
||||||
start, end = rect
|
start, end = rect
|
||||||
avg_height = 0
|
height = {}
|
||||||
for x in range(start[0], end[0]):
|
for x in range(start[0], end[0]):
|
||||||
for y in range(start[1], end[1]):
|
for y in range(start[1], end[1]):
|
||||||
avg_height += image.getpixel((x, y))
|
if image.getpixel((x, y)) not in height:
|
||||||
avg_height = int(
|
height[image.getpixel((x, y))] = 1
|
||||||
avg_height / ((end[0] - start[0]) * (end[1] - start[1]))) + 1
|
else:
|
||||||
|
height[image.getpixel((x, y))] += 1
|
||||||
|
max_height = max(height, key=height.get)
|
||||||
new_rectangle.append(
|
new_rectangle.append(
|
||||||
((start[0], avg_height, start[1]), (end[0], avg_height + randint(height_min, height_max), end[1])))
|
((start[0], max_height, start[1]), (end[0], max_height + randint(height_min, height_max), end[1])))
|
||||||
return new_rectangle
|
return new_rectangle
|
||||||
|
|
||||||
|
|
||||||
def transpose_form_heightmap(heightmap: Union[str, Image], coordinates, origin: tuple[int, int]) -> tuple[int, int, int]:
|
def transpose_form_heightmap(heightmap: str | Image.Image, coordinates, origin: tuple[int, int]) -> tuple[
|
||||||
|
int, int, int]:
|
||||||
heightmap = handle_import_image(heightmap).convert('L')
|
heightmap = handle_import_image(heightmap).convert('L')
|
||||||
|
|
||||||
xMin, zMin = origin
|
xMin, zMin = origin
|
||||||
|
|||||||
@@ -69,13 +69,12 @@ def pack_rectangles(grid, min_width: int = 10, max_width: int = 25):
|
|||||||
rectangle = generate_rectangle(min_width, max_width)
|
rectangle = generate_rectangle(min_width, max_width)
|
||||||
if not bin.place_rectangle(rectangle):
|
if not bin.place_rectangle(rectangle):
|
||||||
break
|
break
|
||||||
print(len(bin.rectangles))
|
|
||||||
return bin.rectangles
|
return bin.rectangles
|
||||||
|
|
||||||
|
|
||||||
def draw_rectangles(rectangles, grid, heightmap):
|
def draw_rectangles(rectangles, grid, heightmap):
|
||||||
heightmap = handle_import_image(heightmap).convert('L')
|
heightmap = handle_import_image(heightmap).convert('L')
|
||||||
image = Image.new('L', (len(grid[0]), len(grid)), (0))
|
image = Image.new('L', (len(grid[0]), len(grid)), 0)
|
||||||
for rectangle in rectangles:
|
for rectangle in rectangles:
|
||||||
start, end = rectangle
|
start, end = rectangle
|
||||||
height = []
|
height = []
|
||||||
@@ -88,13 +87,29 @@ def draw_rectangles(rectangles, grid, heightmap):
|
|||||||
image.putpixel((x, y), round(height_average))
|
image.putpixel((x, y), round(height_average))
|
||||||
return image
|
return image
|
||||||
|
|
||||||
|
def area_of_rectangles(rectangles):
|
||||||
|
area = 0
|
||||||
|
for rectangle in rectangles:
|
||||||
|
start, end = rectangle
|
||||||
|
area += abs((end[0] - start[0]) * (end[1] - start[1]))
|
||||||
|
return area
|
||||||
|
|
||||||
def generate_building(image: Union[str, Image], heightmap: Union[str, Image], output: str = './world_maker/data/building.png', min_width: int = 10, max_width: int = 25):
|
|
||||||
|
|
||||||
|
def generate_building(image: str | Image.Image, heightmap: str | Image.Image, output: str = './world_maker/data/building.png',
|
||||||
|
number_of_try: int = 3, min_width: int = 10, max_width: int = 25):
|
||||||
|
print("[Building] Start generating building position...")
|
||||||
image = handle_import_image(image).convert('L')
|
image = handle_import_image(image).convert('L')
|
||||||
grid = np.array(image)
|
rectangles_output = []
|
||||||
rectangles = pack_rectangles(grid, min_width, max_width)
|
for n in range(number_of_try):
|
||||||
draw_rectangles(rectangles, grid, heightmap).save(
|
print("[Building] Try", n+1)
|
||||||
output)
|
grid = np.array(image)
|
||||||
return rectangles
|
rectangles = pack_rectangles(grid, min_width, max_width)
|
||||||
|
print("[Building] Number of building:", len(rectangles))
|
||||||
|
print("[Building] Area of building:", area_of_rectangles(rectangles))
|
||||||
|
if area_of_rectangles(rectangles) > area_of_rectangles(rectangles_output):
|
||||||
|
rectangles_output = rectangles
|
||||||
|
draw_rectangles(rectangles_output, grid, heightmap).save(output)
|
||||||
|
return rectangles_output
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -17,10 +17,12 @@ def world_maker():
|
|||||||
|
|
||||||
smooth_sobel_water_map = smooth_sobel_water()
|
smooth_sobel_water_map = smooth_sobel_water()
|
||||||
skeleton_highway = skeleton_highway_map(highway_map())
|
skeleton_highway = skeleton_highway_map(highway_map())
|
||||||
|
|
||||||
city = City()
|
city = City()
|
||||||
city.generate_district()
|
city.generate_district()
|
||||||
city.loop_expend_district()
|
city.loop_expend_district()
|
||||||
city.district_draw_map()
|
city.district_draw_map()
|
||||||
|
|
||||||
road_grid = city.district_generate_road()
|
road_grid = city.district_generate_road()
|
||||||
image_mountain_map = city.get_district_mountain_map()
|
image_mountain_map = city.get_district_mountain_map()
|
||||||
road = city.draw_roads(4)
|
road = city.draw_roads(4)
|
||||||
|
|||||||
Reference in New Issue
Block a user