Add grid generation

This commit is contained in:
2024-06-16 04:49:41 +02:00
parent 1329e442d2
commit 2fa3aeba1d
21 changed files with 174 additions and 44 deletions

50
main.py
View File

@@ -3,17 +3,23 @@ import random
import gdpc.exceptions import gdpc.exceptions
from world_maker.world_maker import * from world_maker.world_maker import *
from world_maker.Skeleton import Skeleton, transpose_form_heightmap, simplify_coordinates
from networks.geometry.Point3D import Point3D
from networks.roads_2.Road import Road
from world_maker.District import Road as Road_grid
from House import * from House import *
def main(): def main():
rectangle_house_mountain, rectangle_building, skeleton_highway, skeleton_mountain = world_maker() rectangle_house_mountain, rectangle_building, skeleton_highway, skeleton_mountain, road_grid = world_maker()
editor = Editor(buffering=True) editor = Editor(buffering=True)
buildArea = editor.getBuildArea() buildArea = editor.getBuildArea()
print(skeleton_mountain.lines) set_roads(skeleton_mountain)
set_roads(skeleton_highway)
set_roads_grids(road_grid)
blocks = { blocks = {
"wall": "blackstone", "wall": "blackstone",
@@ -43,6 +49,46 @@ def main():
house.build() house.build()
def set_roads_grids(road_grid: Road_grid):
for i in range(len(road_grid)):
if road_grid[i].border:
for j in range(len(road_grid)):
# 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):
point_1 = transpose_form_heightmap(
'./world_maker/data/heightmap.png', (road_grid[i].position.x, road_grid[i].position.y))
point_2 = transpose_form_heightmap(
'./world_maker/data/heightmap.png', (road_grid[j].position.x, road_grid[j].position.y))
Road(
[Point3D(point_1[0], point_1[1], point_1[2]), Point3D(point_2[0], point_2[1], point_2[2])], 9)
def set_roads(skeleton: Skeleton):
# Parsing
print("[Roads] Start parsing...")
for i in range(len(skeleton.lines)):
print(f"[Roads] Parsing skeleton {i+1}/{len(skeleton.lines)}.")
for j in range(len(skeleton.lines[i])):
xyz = transpose_form_heightmap('./world_maker/data/heightmap.png',
skeleton.coordinates[skeleton.lines[i][j]])
skeleton.lines[i][j] = xyz
print("[Roads] Start simplification...")
# Simplification
for i in range(len(skeleton.lines)):
print(f"[Roads] Simplify skelton {i+1}/{len(skeleton.lines)}")
skeleton.lines[i] = simplify_coordinates(skeleton.lines[i], 10)
print("[Roads] Start generation...")
for i in range(len(skeleton.lines)):
print(f"[Roads] Generating roads {i+1}/{len(skeleton.lines)}.")
if len(skeleton.lines[i]) >= 4:
Road(Point3D.from_arrays(skeleton.lines[i]), 25)
else:
print(
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__':
main() main()

View File

@@ -28,6 +28,9 @@ class Polyline:
self.length_polyline = len(self.points_array) self.length_polyline = len(self.points_array)
if self.length_polyline < 4: if self.length_polyline < 4:
print(self.length_polyline)
print(self.points_array)
print(self.output_points)
raise ValueError("The list must contain at least 4 elements.") raise ValueError("The list must contain at least 4 elements.")
self.vectors = [None] * self.length_polyline # v self.vectors = [None] * self.length_polyline # v

View File

@@ -22,6 +22,8 @@ class Segment3D:
>>> Segment3D(Point3D(0, 0, 0), Point3D(10, 10, 15)) >>> Segment3D(Point3D(0, 0, 0), Point3D(10, 10, 15))
""" """
start = self.start.copy()
end = self.end.copy()
self.output_points.append(start.copy()) self.output_points.append(start.copy())
dx = abs(self.end.x - self.start.x) dx = abs(self.end.x - self.start.x)
dy = abs(self.end.y - self.start.y) dy = abs(self.end.y - self.start.y)

View File

@@ -4,6 +4,8 @@ from networks.geometry.Polyline import Polyline
from networks.geometry.Point3D import Point3D from networks.geometry.Point3D import Point3D
from networks.geometry.Point2D import Point2D from networks.geometry.Point2D import Point2D
from networks.geometry.Segment2D import Segment2D
from networks.geometry.Segment3D import Segment3D
from networks.geometry.Circle import Circle from networks.geometry.Circle import Circle
from Enums import LINE_THICKNESS_MODE from Enums import LINE_THICKNESS_MODE
from gdpc import Block, Editor from gdpc import Block, Editor
@@ -11,21 +13,45 @@ from gdpc import Block, Editor
class Road: class Road:
def __init__(self, coordinates: List[Point3D], width: int): def __init__(self, coordinates: List[Point3D], width: int):
self.coordinates = coordinates self.coordinates = self._remove_collinear_points(coordinates)
self.output_block = [] self.output_block = []
# with open(road_configuration) as f: # with open(road_configuration) as f:
# self.road_configuration = json.load(f) # self.road_configuration = json.load(f)
# self.width = self.road_configuration["width"] # self.width = self.road_configuration["width"]
self.width = width self.width = width
self.polyline_height = None self.polyline_height = None
self.polyline_total_line_output = None
self.polyline = Polyline(Point3D.to_2d(coordinates, 'y')) self.segment_total_line_output = None
self.polyline_total_line_output = [
[] for _ in range(len(self.polyline.total_line_output))]
self.index_factor = 0 self.index_factor = 0
self._projection() if len(self._remove_collinear_points(self.coordinates)) >= 4:
self._surface() self.polyline = Polyline(Point3D.to_2d(coordinates, 'y'))
self.polyline_total_line_output = [
[] for _ in range(len(self.polyline.total_line_output))]
self._projection_polyline()
if len(self.coordinates) == 2:
self.segment_total_line_output = Segment2D(
Point3D.to_2d([self.coordinates[0]], 'y')[0], Point3D.to_2d([self.coordinates[1]], 'y')[0]).segment_thick(self.width, LINE_THICKNESS_MODE.MIDDLE)
self._projection_segment()
self.place()
@staticmethod
def _remove_collinear_points(points):
output_points = [points[0]]
for i in range(1, len(points) - 1):
if isinstance(points[0], Point3D):
if not Point2D.collinear(
Point3D.to_2d([points[i-1]], 'y')[0], Point3D.to_2d([points[i]], 'y')[0], Point3D.to_2d([points[i+1]], 'y')[0]):
output_points.append(points[i])
else:
if not Point2D.collinear(points[i-1], points[i], points[i+1]):
output_points.append(points[i])
output_points.append(points[-1])
return output_points
def _surface(self): def _surface(self):
# Segments # Segments
@@ -60,7 +86,7 @@ class Road:
(Point3D.insert_3d([circle.points_thick[j]], 'y', [ (Point3D.insert_3d([circle.points_thick[j]], 'y', [
self.polyline_total_line_output[nearest[0]].y])[0].coordinates, Block("white_concrete"))) self.polyline_total_line_output[nearest[0]].y])[0].coordinates, Block("white_concrete")))
def _projection(self): def _projection_polyline(self):
nearest_points_to_reference = [] nearest_points_to_reference = []
for i in range(len(self.coordinates)): for i in range(len(self.coordinates)):
# nearest_points_to_reference.append(Point3D.insert_3d([Point3D.to_2d([self.coordinates[i]], 'y')[0].nearest( # nearest_points_to_reference.append(Point3D.insert_3d([Point3D.to_2d([self.coordinates[i]], 'y')[0].nearest(
@@ -70,17 +96,30 @@ class Road:
nearest_points_to_reference.append( nearest_points_to_reference.append(
Point2D(index, self.coordinates[i].y)) Point2D(index, self.coordinates[i].y))
self.polyline_height = Polyline(nearest_points_to_reference) if len(self._remove_collinear_points(nearest_points_to_reference)) >= 4:
self.polyline_height = Polyline(nearest_points_to_reference)
self.index_factor = len( self.index_factor = len(
self.polyline_height.total_line_output)/len(self.polyline.total_line_output) self.polyline_height.total_line_output)/len(self.polyline.total_line_output)
for i in range(len(self.polyline.total_line_output)): for i in range(len(self.polyline.total_line_output)):
self.polyline_total_line_output[i] = Point3D( self.polyline_total_line_output[i] = Point3D(
self.polyline.total_line_output[i].x, self.polyline_height.total_line_output[round(i*self.index_factor)].y, self.polyline.total_line_output[i].y) self.polyline.total_line_output[i].x, self.polyline_height.total_line_output[round(i*self.index_factor)].y, self.polyline.total_line_output[i].y)
self.polyline_total_line_output = self.polyline_total_line_output[0].optimized_path( self._surface()
self.polyline_total_line_output) self.place()
# self.polyline_total_line_output = self.polyline_total_line_output[0].optimized_path(
# self.polyline_total_line_output)
def _projection_segment(self):
s = Segment3D(
self.coordinates[0], self.coordinates[1])
reference = s.segment()
for i in range(len(self.segment_total_line_output)):
self.output_block.append(((
self.segment_total_line_output[i].x, reference[self.segment_total_line_output[i].nearest(Point3D.to_2d(reference, 'y'), True)[0]].y, self.segment_total_line_output[i].y), Block("black_concrete")))
def place(self): def place(self):
editor = Editor(buffering=True) editor = Editor(buffering=True)

View File

@@ -3,11 +3,11 @@ from collections import Counter
from typing import List, Union from typing import List, Union
import numpy as np import numpy as np
from gdpc import Editor
from PIL import Image, ImageDraw from PIL import Image, ImageDraw
from skan.csr import skeleton_to_csgraph from skan.csr import skeleton_to_csgraph
from skimage.morphology import skeletonize from skimage.morphology import skeletonize
from networks.geometry.Point3D import Point3D
from gdpc import Editor
def handle_import_image(image: Union[str, Image]) -> Image: def handle_import_image(image: Union[str, Image]) -> Image:
@@ -16,6 +16,51 @@ def handle_import_image(image: Union[str, Image]) -> Image:
return image return image
def transpose_form_heightmap(heightmap: Union[str, Image], coordinates):
heightmap = handle_import_image(heightmap).convert('L')
editor = Editor()
xMin = (editor.getBuildArea().begin).x
zMin = (editor.getBuildArea().begin).z
return (coordinates[0] + xMin, heightmap.getpixel(
(coordinates[0], coordinates[-1])), coordinates[-1] + zMin)
def simplify_coordinates(coordinates, epsilon):
if len(coordinates) < 3:
return coordinates
# Find the point with the maximum distance
max_distance = 0
max_index = 0
end_index = len(coordinates) - 1
for i in range(1, end_index):
distance = Point3D(coordinates[i][0], coordinates[i][1], coordinates[i][2]).distance(
Point3D(coordinates[0][0], coordinates[0][1], coordinates[0][2]))
if distance > max_distance:
max_distance = distance
max_index = i
simplified_coordinates = []
# If the maximum distance is greater than epsilon, recursively simplify
if max_distance > epsilon:
rec_results1 = simplify_coordinates(coordinates[:max_index+1], epsilon)
rec_results2 = simplify_coordinates(coordinates[max_index:], epsilon)
# Combine the simplified sub-results
simplified_coordinates.extend(rec_results1[:-1])
simplified_coordinates.extend(rec_results2)
else:
# The maximum distance is less than epsilon, retain the endpoints
simplified_coordinates.append(coordinates[0])
simplified_coordinates.append(coordinates[end_index])
return simplified_coordinates
class Skeleton: class Skeleton:
def __init__(self, data: np.ndarray = None): def __init__(self, data: np.ndarray = None):
self.lines = [] self.lines = []
@@ -26,19 +71,6 @@ class Skeleton:
if data is not None: if data is not None:
self.set_skeleton(data) self.set_skeleton(data)
def transpose_form_heightmap(heightmap: Union[str, Image], coordinates):
heightmap = handle_import_image(heightmap).convert('L')
editor = Editor()
xMin = (editor.getBuildArea().begin).x
zMin = (editor.getBuildArea().begin).z
coordinates_final = []
return coordinates_final(coordinates[0] + xMin, heightmap.getpixel(
(coordinates[0], coordinates[2]))[0], coordinates[2] + zMin)
def set_skeleton(self, data: np.ndarray): def set_skeleton(self, data: np.ndarray):
print("[Skeleton] Start skeletonization...") print("[Skeleton] Start skeletonization...")
binary_skeleton = skeletonize(data, method="lee") binary_skeleton = skeletonize(data, method="lee")

Binary file not shown.

Before

Width:  |  Height:  |  Size: 396 B

After

Width:  |  Height:  |  Size: 344 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.2 KiB

After

Width:  |  Height:  |  Size: 3.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.0 KiB

After

Width:  |  Height:  |  Size: 5.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 30 KiB

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 707 B

After

Width:  |  Height:  |  Size: 843 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.0 KiB

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.6 KiB

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 30 KiB

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 750 B

After

Width:  |  Height:  |  Size: 876 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 30 KiB

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 900 B

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.1 KiB

After

Width:  |  Height:  |  Size: 3.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 35 KiB

After

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 10 KiB

After

Width:  |  Height:  |  Size: 813 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.0 KiB

After

Width:  |  Height:  |  Size: 2.4 KiB

View File

@@ -1,35 +1,43 @@
from world_maker.World import World from world_maker.World import World
from PIL import Image from PIL import Image
from world_maker.data_analysis import (get_data,filter_negative, rectangle_2D_to_3D, skeleton_mountain_map, highway_map, filter_sobel, skeleton_highway_map, \ from world_maker.data_analysis import (get_data, filter_negative, rectangle_2D_to_3D, skeleton_mountain_map, highway_map, filter_sobel, skeleton_highway_map,
smooth_sobel_water, subtract_map, detect_mountain) smooth_sobel_water, subtract_map, detect_mountain)
from world_maker.City import City from world_maker.City import City
from world_maker.Position import Position from world_maker.Position import Position
from random import randint from random import randint
from world_maker.pack_rectangle import generate_building from world_maker.pack_rectangle import generate_building
def world_maker(): def world_maker():
world = World() world = World()
heightmap, watermap, treemap = get_data(world) heightmap, watermap, treemap = get_data(world)
filter_sobel("./world_maker/data/heightmap.png").save('./world_maker/data/sobelmap.png') filter_sobel(
"./world_maker/data/heightmap.png").save('./world_maker/data/sobelmap.png')
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()
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)
road.save('./world_maker/data/roadmap.png') road.save('./world_maker/data/roadmap.png')
subtract_map(smooth_sobel_water_map, road).save('./world_maker/data/city_map.png') subtract_map(smooth_sobel_water_map, road).save(
subtract_map('./world_maker/data/city_map.png', './world_maker/data/skeleton_highway_area.png').save('./world_maker/data/city_map.png') './world_maker/data/city_map.png')
subtract_map('./world_maker/data/city_map.png', './world_maker/data/mountain_map.png').save('./world_maker/data/city_map.png') subtract_map('./world_maker/data/city_map.png',
'./world_maker/data/skeleton_highway_area.png').save('./world_maker/data/city_map.png')
subtract_map('./world_maker/data/city_map.png',
'./world_maker/data/mountain_map.png').save('./world_maker/data/city_map.png')
rectangle_building = generate_building('./world_maker/data/city_map.png') rectangle_building = generate_building('./world_maker/data/city_map.png')
rectangle_building = rectangle_2D_to_3D(rectangle_building) rectangle_building = rectangle_2D_to_3D(rectangle_building)
skeleton_mountain = skeleton_mountain_map(image_mountain_map) skeleton_mountain = skeleton_mountain_map(image_mountain_map)
subtract_map('./world_maker/data/mountain_map.png', './world_maker/data/skeleton_mountain_area.png').save('./world_maker/data/mountain_map.png') subtract_map('./world_maker/data/mountain_map.png',
subtract_map(smooth_sobel_water_map, filter_negative('./world_maker/data/mountain_map.png')).save('./world_maker/data/mountain_map.png') './world_maker/data/skeleton_mountain_area.png').save('./world_maker/data/mountain_map.png')
rectangle_mountain = generate_building('./world_maker/data/mountain_map.png') subtract_map(smooth_sobel_water_map, filter_negative(
'./world_maker/data/mountain_map.png')).save('./world_maker/data/mountain_map.png')
rectangle_mountain = generate_building(
'./world_maker/data/mountain_map.png')
rectangle_mountain = rectangle_2D_to_3D(rectangle_mountain) rectangle_mountain = rectangle_2D_to_3D(rectangle_mountain)
return rectangle_mountain, rectangle_building, skeleton_highway, skeleton_mountain return rectangle_mountain, rectangle_building, skeleton_highway, skeleton_mountain, road_grid