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
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 *
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)
buildArea = editor.getBuildArea()
print(skeleton_mountain.lines)
set_roads(skeleton_mountain)
set_roads(skeleton_highway)
set_roads_grids(road_grid)
blocks = {
"wall": "blackstone",
@@ -43,6 +49,46 @@ def main():
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__':
main()

View File

@@ -28,6 +28,9 @@ class Polyline:
self.length_polyline = len(self.points_array)
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.")
self.vectors = [None] * self.length_polyline # v

View File

@@ -22,6 +22,8 @@ class Segment3D:
>>> Segment3D(Point3D(0, 0, 0), Point3D(10, 10, 15))
"""
start = self.start.copy()
end = self.end.copy()
self.output_points.append(start.copy())
dx = abs(self.end.x - self.start.x)
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.Point2D import Point2D
from networks.geometry.Segment2D import Segment2D
from networks.geometry.Segment3D import Segment3D
from networks.geometry.Circle import Circle
from Enums import LINE_THICKNESS_MODE
from gdpc import Block, Editor
@@ -11,21 +13,45 @@ from gdpc import Block, Editor
class Road:
def __init__(self, coordinates: List[Point3D], width: int):
self.coordinates = coordinates
self.coordinates = self._remove_collinear_points(coordinates)
self.output_block = []
# with open(road_configuration) as f:
# self.road_configuration = json.load(f)
# self.width = self.road_configuration["width"]
self.width = width
self.polyline_height = None
self.polyline = Polyline(Point3D.to_2d(coordinates, 'y'))
self.polyline_total_line_output = [
[] for _ in range(len(self.polyline.total_line_output))]
self.polyline_total_line_output = None
self.segment_total_line_output = None
self.index_factor = 0
self._projection()
self._surface()
if len(self._remove_collinear_points(self.coordinates)) >= 4:
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):
# Segments
@@ -60,7 +86,7 @@ class Road:
(Point3D.insert_3d([circle.points_thick[j]], 'y', [
self.polyline_total_line_output[nearest[0]].y])[0].coordinates, Block("white_concrete")))
def _projection(self):
def _projection_polyline(self):
nearest_points_to_reference = []
for i in range(len(self.coordinates)):
# 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(
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.polyline_height.total_line_output)/len(self.polyline.total_line_output)
self.index_factor = len(
self.polyline_height.total_line_output)/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].x, self.polyline_height.total_line_output[round(i*self.index_factor)].y, self.polyline.total_line_output[i].y)
for i in range(len(self.polyline.total_line_output)):
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 = self.polyline_total_line_output[0].optimized_path(
self.polyline_total_line_output)
self._surface()
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):
editor = Editor(buffering=True)

View File

@@ -3,11 +3,11 @@ from collections import Counter
from typing import List, Union
import numpy as np
from gdpc import Editor
from PIL import Image, ImageDraw
from skan.csr import skeleton_to_csgraph
from skimage.morphology import skeletonize
from gdpc import Editor
from networks.geometry.Point3D import Point3D
def handle_import_image(image: Union[str, Image]) -> Image:
@@ -16,6 +16,51 @@ def handle_import_image(image: Union[str, Image]) -> 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:
def __init__(self, data: np.ndarray = None):
self.lines = []
@@ -26,19 +71,6 @@ class Skeleton:
if data is not None:
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):
print("[Skeleton] Start skeletonization...")
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 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, \
smooth_sobel_water, subtract_map, detect_mountain)
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)
from world_maker.City import City
from world_maker.Position import Position
from random import randint
from world_maker.pack_rectangle import generate_building
def world_maker():
world = 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()
skeleton_highway = skeleton_highway_map(highway_map())
city = City()
city.generate_district()
city.loop_expend_district()
city.district_draw_map()
city.district_generate_road()
road_grid = city.district_generate_road()
image_mountain_map = city.get_district_mountain_map()
road = city.draw_roads(4)
road.save('./world_maker/data/roadmap.png')
subtract_map(smooth_sobel_water_map, road).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')
subtract_map(smooth_sobel_water_map, road).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 = rectangle_2D_to_3D(rectangle_building)
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(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')
subtract_map('./world_maker/data/mountain_map.png',
'./world_maker/data/skeleton_mountain_area.png').save('./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)
return rectangle_mountain, rectangle_building, skeleton_highway, skeleton_mountain
return rectangle_mountain, rectangle_building, skeleton_highway, skeleton_mountain, road_grid