fix collumns, start facade and clean shitty code

This commit is contained in:
KAymeric
2024-05-11 11:21:24 +02:00
parent 1da68576f4
commit 235d9be35d
10 changed files with 231 additions and 127 deletions

31
buildings/Building.py Normal file
View File

@@ -0,0 +1,31 @@
import random as rd
from Enums import COLLUMN_STYLE
from buildings.Foundations import Foundations
from buildings.Facade import Facade
class Building:
def __init__(self, position : tuple[int,int], size : tuple[int, int], matrice : list[list[int]]):
self.position = position
self.length, self.width = size
self.matrice = matrice
# Generate every random components here
is_collumn_full_tile = bool(rd.getrandbits(1))
is_inner_or_outer = rd.choice(list(COLLUMN_STYLE))
tile_size = self.gen_tile_size()
floor_height = rd.randint(4, 7)
is_inner_or_outer = COLLUMN_STYLE.BOTH
self.foundations = Foundations(position, size, matrice, tile_size, is_collumn_full_tile, is_inner_or_outer)
self.facade = Facade(self.foundations.vertices, floor_height, is_inner_or_outer)
def gen_tile_size(self) -> int:
# Tiles are constant square units different for each buildings
smaller_side = min(self.length, self.width)
# area is too small, will work but not very well
if smaller_side <= 15 : return smaller_side // 5
return rd.randint(3, smaller_side // len(self.matrice))

29
buildings/Facade.py Normal file
View File

@@ -0,0 +1,29 @@
from Enums import COLLUMN_STYLE
from buildings.geometry.Vertice import Vertice
from buildings.geometry.Rectangle import Rectangle
from buildings.elements.Window import Window
class Facade:
def __init__(self, vertices : list[Vertice], height : int, is_inner_or_outer : COLLUMN_STYLE):
self.vertices = vertices
self.is_inner_or_outer = is_inner_or_outer
self.height = height
self.window_size = self.get_window_size()
self.window = self.get_window()
self.has_balcony = self.has_balcony()
self.has_inter_floor = self.has_inter_floor()
def build_facade(self):
pass
def get_window_size(self) -> tuple[int,int]:
pass
def has_balcony(self) -> bool:
pass
def has_inter_floor(self) -> bool:
pass
def get_window(self) -> Window:
pass

View File

@@ -1,14 +1,30 @@
import random as rd import random as rd
import numpy as np import numpy as np
import math import math
from Enums import COLLUMN_STYLE
from buildings.geometry.Tile import Tile from buildings.geometry.Tile import Tile
from buildings.geometry.Polygon import Polygon from buildings.geometry.Polygon import Polygon
from buildings.geometry.Point import Point from buildings.geometry.Point import Point
from buildings.geometry.Rectangle import Rectangle from buildings.geometry.Rectangle import Rectangle
from buildings.elements.Collumn import Collumn
class Foundations: class Foundations:
def __init__(self, position : tuple[int,int], size : tuple[int, int], matrice : list[list[int]]): # TODO : gérer les collones sur les tiles trop petites et les colones 1tile/2 + fulltile
def __init__(self,
position : tuple[int,int],
size : tuple[int, int],
matrice : list[list[int]],
tile_size : int,
is_collumn_full_tile : bool,
is_inner_or_outer : COLLUMN_STYLE):
# Foundations are the base of the building, they are made of tiles and based on a matrice # Foundations are the base of the building, they are made of tiles and based on a matrice
# Random components
self.tile_size = tile_size
self.is_collumn_full_tile = is_collumn_full_tile
self.is_inner_or_outer = is_inner_or_outer
x,z = position x,z = position
self.position = Point(x = x, z = z) self.position = Point(x = x, z = z)
self.size = size self.size = size
@@ -16,7 +32,7 @@ class Foundations:
self.width = size[1] self.width = size[1]
self.matrice = matrice self.matrice = matrice
self.tiles = [] self.tiles = []
self.tile_size = self.define_tile_size() self.vertices = []
self.length_in_tiles = self.length // self.tile_size self.length_in_tiles = self.length // self.tile_size
self.width_in_tiles = self.width // self.tile_size self.width_in_tiles = self.width // self.tile_size
self.x_distribution = [] self.x_distribution = []
@@ -24,15 +40,6 @@ class Foundations:
self.polygon = self.get_polygon() self.polygon = self.get_polygon()
self.collumns = self.get_columns() self.collumns = self.get_columns()
def define_tile_size(self) -> int:
# Tiles are constant square units different for each buildings
smaller_side = min(self.length, self.width)
# area is too small, will work but not very well
if smaller_side <= 15 : return smaller_side // 5
return rd.randint(3, smaller_side // len(self.matrice))
def add_tile(self, tile : Tile): def add_tile(self, tile : Tile):
self.tiles.append(tile) self.tiles.append(tile)
@@ -58,8 +65,8 @@ class Foundations:
z_padding += zsize * self.tile_size z_padding += zsize * self.tile_size
x_padding += xsize * self.tile_size x_padding += xsize * self.tile_size
polygon.set_vertices_and_neighbors(self.tiles) polygon.set_vertices_and_neighbors(self.tiles, self.vertices)
polygon.compress(self.tiles) polygon.compress(self.tiles, self.vertices)
return polygon return polygon
@@ -68,6 +75,9 @@ class Foundations:
# foundations are based on a matrice, # foundations are based on a matrice,
# this function gives the number of tiles for each row/collumn of the matrice, giving a more random shape # this function gives the number of tiles for each row/collumn of the matrice, giving a more random shape
# The real shit start here # The real shit start here
if length == avaliable_tiles:
return [1 for i in range(avaliable_tiles)]
if length == 1: if length == 1:
return [avaliable_tiles] return [avaliable_tiles]
@@ -76,83 +86,59 @@ class Foundations:
return [l,avaliable_tiles-l] return [l,avaliable_tiles-l]
if length >= 3: if length >= 3:
is_len_even = length % 2 == 0
is_availiable_even = avaliable_tiles % 2 == 0
sizes = [] sizes = []
intersections_count = math.ceil(length/2)-1
# This is to keep symetry in case of an even matrice
if not is_len_even:
center = rd.randint(1,avaliable_tiles-length+1)
avaliable_tiles -= center
is_availiable_even = avaliable_tiles % 2 == 0
if not is_availiable_even: center += 1
sizes.append(center)
is_availiable_even = True
intersection_number = length // 2 - 1
tiles_per_side = avaliable_tiles//2 tiles_per_side = avaliable_tiles//2
# we keep symetry we randomize only one side correction = 0
intersect_values = np.random.choice(np.arange(1,tiles_per_side), size=intersection_number, replace=False)
# we duplicate the side for the symetry intersect_values = np.random.choice(np.arange(1,tiles_per_side), size=intersections_count, replace=False)
#we generate only half of the distribution
last_pos = 0 last_pos = 0
intersect_values = np.append(intersect_values,tiles_per_side) intersect_values = np.append(intersect_values,tiles_per_side)
for intersect in intersect_values: for intersect in intersect_values:
size = [intersect - last_pos] sizes.append(intersect - last_pos)
sizes = size + sizes + size
last_pos = intersect last_pos = intersect
# if there is a tile left, add it randomly # we duplicate the side for the symetry
if not is_availiable_even: sizes[rd.randint(0,len(sizes)-1)] += 1 symetry = sizes.copy()
symetry.reverse()
if avaliable_tiles%2 == 1: correction = 1 # if there is a tile left, add it randomly
if length%2 == 1 : sizes[-1], symetry = sizes[-1]*2 + correction, symetry[1:]
sizes += symetry
return sizes return sizes
def get_columns(self) -> list[Rectangle]: def get_columns(self) -> list[Collumn]:
collumns = [] collumns = []
is_full_tile = bool(rd.getrandbits(1))
x_padding = self.position.x
for x,row in enumerate(self.matrice): for tile in self.tiles:
z_padding = self.position.z north_west_collumn = Collumn(Point(x = tile.north_west.x-1, z = tile.north_west.z-1), tile.north_west)
lenx = self.x_distribution[x] north_east_collumn = Collumn(Point(x = tile.north_east.x, z = tile.north_east.z-1), Point(x = tile.north_east.x+1, z = tile.north_east.z))
south_west_collumn = Collumn(Point(x = tile.south_west.x-1, z = tile.south_west.z), Point(x = tile.south_west.x, z = tile.south_west.z+1))
south_east_collumn = Collumn(tile.south_east, Point(x = tile.south_east.x+1, z = tile.south_east.z+1))
for z,value in enumerate(row): if tile.north_vertice != None or tile.west_vertice != None: north_west_collumn.set_is_outer(True)
lenz = self.z_distribution[z]
# conditions to not make a collumn on the facade of the building (no outter collumns) if tile.north_vertice != None or tile.east_vertice != None: north_east_collumn.set_is_outer(True)
skip_first_x,skip_first_z = False,False if tile.south_vertice != None or tile.west_vertice != None: south_west_collumn.set_is_outer(True)
# if it's the first or last row/collumn
if x == 0 : skip_first_x = True
if z == 0 : skip_first_z = True
last_value_x,last_value_z = self.matrice[x-1][z],self.matrice[x][z-1] if tile.south_vertice != None or tile.east_vertice != None: south_east_collumn.set_is_outer(True)
# if the previous row/collumn is empty
if last_value_x == 0 : skip_first_x = True
if last_value_z == 0 : skip_first_z = True
next_value_x,next_value_z = 0,0 collumns.extend([north_west_collumn, north_east_collumn, south_west_collumn, south_east_collumn])
try : next_value_x = self.matrice[x+1][z]
except : pass
try : next_value_z = self.matrice[x][z+1]
except : pass
# if this part of the building is too tiny
if last_value_x == 0 and next_value_x == 0 and self.x_distribution[x] == 1: continue
if last_value_z == 0 and next_value_z == 0 and self.z_distribution[z] == 1: continue
if value == 1: return self._suppr_doubblons_collumns(collumns)
self.create_collumns(x_padding, z_padding, lenx, lenz, skip_first_x, skip_first_z, collumns)
z_padding += lenz * self.tile_size def _suppr_doubblons_collumns(self, collumns : list[Collumn]):
x_padding += lenx * self.tile_size for index,collumn in enumerate(collumns):
if index == len(collumns)-1: break
for compare in collumns[index+1:]:
if collumn.point1.position == compare.point1.position :
if compare.is_outer : collumn.set_is_outer(True)
collumns.remove(compare)
print(len(collumns))
return collumns return collumns
def create_collumns(self, basex : int, basez : int, lenx : int, lenz : int, skip_first_x : bool, skip_first_z : bool, collumns : list[Rectangle]):
for x in range(lenx):
if x==0 and skip_first_x: continue
for z in range(lenz):
if z==0 and skip_first_z: continue
collumns.append(Rectangle(Point(x = basex+x*self.tile_size, z = basez+z*self.tile_size), Point(x = basex+x*self.tile_size-1, z = basez+z*self.tile_size-1)))

View File

@@ -0,0 +1,11 @@
from buildings.geometry.Rectangle import Rectangle
from buildings.geometry.Point import Point
class Collumn(Rectangle):
def __init__(self, point1 : Point, point2 : Point, is_outer : bool = False) :
Rectangle.__init__(self, point1, point2)
self.is_outer = is_outer
def set_is_outer(self, is_outer : bool):
self.is_outer = is_outer

View File

@@ -0,0 +1,10 @@
class Window:
def __init__(self, size : tuple[int,int]):
self.size = size
def open(self):
pass
def close(self):
pass

View File

@@ -6,23 +6,23 @@ from buildings.geometry.Rectangle import Rectangle
from buildings.geometry.Vertice import Vertice from buildings.geometry.Vertice import Vertice
class Polygon: class Polygon:
def __init__(self, position : Point, size: tuple[int,int], vertices : list[Vertice] = []): def __init__(self, position : Point, size: tuple[int,int]):
self.position = position self.position = position
self.size = size self.size = size
self.compressed = {"shape":[], "vertices":[]} self.shape = []
self.vertices = vertices self.vertices = []
def fill_polygon(self, editor : Editor, material : str, y : int, y2 : int = None): def fill_polygon(self, editor : Editor, material : str, y : int, y2 : int = None):
if y2 == None: y2 = y if y2 == None: y2 = y
for rect in self.compressed["shape"]: for rect in self.shape:
rect.fill(editor, material, y, y2) rect.fill(editor, material, y, y2)
def fill_vertice(self, editor : Editor, material : str, y : int, y2 : int = None): def fill_vertice(self, editor : Editor, material : str, y : int, y2 : int = None):
if y2 == None: y2 = y if y2 == None: y2 = y
for vertice in self.compressed["vertices"]: for vertice in self.vertices:
vertice.fill(editor, Block(material), y, y2) vertice.fill(editor, Block(material), y, y2)
def compress(self, tiles : list[Tile]): def compress(self, tiles : list[Tile], vertices : list[Vertice]):
remaining_tiles = tiles.copy() remaining_tiles = tiles.copy()
while len(remaining_tiles) > 0: while len(remaining_tiles) > 0:
start = remaining_tiles[0] start = remaining_tiles[0]
@@ -43,31 +43,43 @@ class Polygon:
start = neightbor start = neightbor
# Find northern border # Find northern border
north_row = self.find_row_border(row.copy(), DIRECTION.NORTH, remaining_tiles) north_row = self._find_row_border(row.copy(), DIRECTION.NORTH, remaining_tiles)
# Find southern border # Find southern border
south_row = self.find_row_border(row.copy(), DIRECTION.SOUTH, remaining_tiles) south_row = self._find_row_border(row.copy(), DIRECTION.SOUTH, remaining_tiles)
area = Rectangle(north_row[0].north_west, south_row[-1].south_east) area = Rectangle(north_row[0].north_west, south_row[-1].south_east)
self.compressed["shape"].append(area) self.shape.append(area)
remaining_vertices = self.vertices.copy() remaining_vertices = vertices.copy()
current = remaining_vertices.pop() current = remaining_vertices.pop()
while len(remaining_vertices) > 0: while len(remaining_vertices) > 0:
neighbors = current.get_neighbors() neighbors = current.get_neighbors()
has_next1 = self.has_next(neighbors[0], current.facing, remaining_vertices) has_next1 = self._has_next(neighbors[0], current.facing, remaining_vertices)
has_next2 = self.has_next(neighbors[1], current.facing, remaining_vertices) has_next2 = self._has_next(neighbors[1], current.facing, remaining_vertices)
if has_next1: if has_next1:
current = Vertice(has_next1.point1, current.point2, current.facing) current = Vertice(has_next1.point1, current.point2, current.facing)
elif has_next2: elif has_next2:
current = Vertice(current.point1, has_next2.point2, current.facing) current = Vertice(current.point1, has_next2.point2, current.facing)
else: else:
self.compressed["vertices"].append(current) self.vertices.append(current)
current = remaining_vertices.pop() current = remaining_vertices.pop()
if len(remaining_vertices) == 0: self.compressed["vertices"].append(current) if len(remaining_vertices) == 0: self.vertices.append(current)
def find_row_border(self, line : list[Tile], direction : str, remaining_tiles : list[Tile]) -> list[Tile]: def set_vertices_and_neighbors(self, tiles : list[Tile], vertices : list[Vertice]):
for tile in tiles:
targets = tile.get_neighbors_coords()
for vertice_num,target in enumerate(targets):
has_neighbor = self._has_neighbor(target, tiles)
if not has_neighbor:
vertice = tile.get_vertice(vertice_num)
vertices.append(vertice)
tile.set_vertice(DIRECTION(vertice_num), vertice)
else :
tile.set_neighbor(vertice_num, has_neighbor)
def _find_row_border(self, line : list[Tile], direction : str, remaining_tiles : list[Tile]) -> list[Tile]:
while True: while True:
new_line = [] new_line = []
for tile in line: for tile in line:
@@ -77,23 +89,13 @@ class Polygon:
for tile in new_line: remaining_tiles.remove(tile) for tile in new_line: remaining_tiles.remove(tile)
line = new_line line = new_line
def set_vertices_and_neighbors(self, tiles : list[Tile]): def _has_neighbor(self, target : tuple[int], tiles : list[Tile]) -> bool|Tile:
for tile in tiles:
targets = tile.get_neighbors_coords()
for vertice_num,target in enumerate(targets):
has_neighbor = self.has_neighbor(target, tiles)
if not has_neighbor:
self.vertices.append(tile.get_vertice(vertice_num))
else :
tile.set_neighbor(vertice_num, has_neighbor)
def has_neighbor(self, target : tuple[int], tiles : list[Tile]) -> bool|Tile:
for tile in tiles: for tile in tiles:
if tile.pos.position == target.position: if tile.pos.position == target.position:
return tile return tile
return False return False
def has_next(self, target : Point, facing : str, remaining_vertices : list[Vertice]) -> bool|Vertice: def _has_next(self, target : Point, facing : str, remaining_vertices : list[Vertice]) -> bool|Vertice:
for vertice in remaining_vertices: for vertice in remaining_vertices:
if vertice.facing == facing: if vertice.facing == facing:
if vertice.point1.position == target.position or vertice.point2.position == target.position: if vertice.point1.position == target.position or vertice.point2.position == target.position:

View File

@@ -7,17 +7,26 @@ class Tile:
def __init__(self, size : int, position : tuple[int, int]): def __init__(self, size : int, position : tuple[int, int]):
self.size = size self.size = size
x,z = position x,z = position
self.pos = Point(x = x, z = z)
leng = self.size-1 leng = self.size-1
self.pos = Point(x = x, z = z)
self.has_vertice = False
self.north_west = self.pos self.north_west = self.pos
self.north_east = Point(x = self.pos.x + leng, z =self.pos.z) self.north_east = Point(x = self.pos.x + leng, z =self.pos.z)
self.south_west = Point(x = self.pos.x, z = self.pos.z + leng) self.south_west = Point(x = self.pos.x, z = self.pos.z + leng)
self.south_east = Point(x = self.pos.x + leng, z = self.pos.z + leng) self.south_east = Point(x = self.pos.x + leng, z = self.pos.z + leng)
self.west_neighbor = None self.west_neighbor = None
self.east_neighbor = None self.east_neighbor = None
self.north_neighbor = None self.north_neighbor = None
self.south_neighbor = None self.south_neighbor = None
self.west_vertice = None
self.east_vertice = None
self.north_vertice = None
self.south_vertice = None
def fill(self, editor : Editor, material : str, y : int, y2 : int = None) -> list[Point]: def fill(self, editor : Editor, material : str, y : int, y2 : int = None) -> list[Point]:
if y2 == None: y2 = y if y2 == None: y2 = y
geometry.placeCuboid(editor, (self.pos.x, y, self.pos.z), (self.pos.x+self.size-1, y2, self.pos.z+self.size-1), Block(material)) geometry.placeCuboid(editor, (self.pos.x, y, self.pos.z), (self.pos.x+self.size-1, y2, self.pos.z+self.size-1), Block(material))
@@ -28,18 +37,6 @@ class Tile:
Point(x = self.pos.x, z = self.pos.z - self.size), # north Point(x = self.pos.x, z = self.pos.z - self.size), # north
Point(x = self.pos.x, z = self.pos.z + self.size)] # south Point(x = self.pos.x, z = self.pos.z + self.size)] # south
def get_vertice(self,vertice : int) -> Vertice:
# gives the corresponding vertice :
# 0 = west, 1 = east, 2 = north, 3 = south
match(vertice):
case 0 :
return Vertice(self.north_west, self.south_west, DIRECTION.WEST)
case 1 :
return Vertice(self.north_east, self.south_east, DIRECTION.EAST)
case 2 :
return Vertice(self.north_west, self.north_east, DIRECTION.NORTH)
case 3 :
return Vertice(self.south_west, self.south_east, DIRECTION.SOUTH)
def get_neighbor(self, direction) -> Point: def get_neighbor(self, direction) -> Point:
match(direction): match(direction):
@@ -52,7 +49,7 @@ class Tile:
case DIRECTION.SOUTH: case DIRECTION.SOUTH:
return self.south_neighbor return self.south_neighbor
def set_neighbor(self, direction, neighbor : Point): def set_neighbor(self, direction, neighbor : 'Tile'):
match(direction): match(direction):
case DIRECTION.WEST: case DIRECTION.WEST:
self.west_neighbor = neighbor self.west_neighbor = neighbor
@@ -62,3 +59,36 @@ class Tile:
self.north_neighbor = neighbor self.north_neighbor = neighbor
case DIRECTION.SOUTH: case DIRECTION.SOUTH:
self.south_neighbor = neighbor self.south_neighbor = neighbor
def get_vertice(self,vertice : int|DIRECTION) -> Vertice:
# gives the corresponding vertice :
# 0 = west, 1 = east, 2 = north, 3 = south
match(vertice):
case 0 :
return Vertice(self.north_west, self.south_west, DIRECTION.WEST)
case 1 :
return Vertice(self.north_east, self.south_east, DIRECTION.EAST)
case 2 :
return Vertice(self.north_west, self.north_east, DIRECTION.NORTH)
case 3 :
return Vertice(self.south_west, self.south_east, DIRECTION.SOUTH)
case DIRECTION.WEST :
return self.west_vertice
case DIRECTION.EAST :
return self.east_vertice
case DIRECTION.NORTH :
return self.north_vertice
case DIRECTION.SOUTH :
return self.south_vertice
def set_vertice(self, direction : DIRECTION, vertice : Vertice):
self.has_vertice = True
match(direction):
case DIRECTION.WEST :
self.west_vertice = vertice
case DIRECTION.EAST :
self.east_vertice = vertice
case DIRECTION.NORTH :
self.north_vertice = vertice
case DIRECTION.SOUTH :
self.south_vertice = vertice

View File

@@ -16,3 +16,6 @@ class Vertice(Rectangle):
return [Point(x = self.point1.x, z = self.point1.z - 1), return [Point(x = self.point1.x, z = self.point1.z - 1),
Point(x = self.point2.x, z = self.point2.z + 1)] Point(x = self.point2.x, z = self.point2.z + 1)]
def get_size(self):
return self.point2.x - self.point1.x + self.point2.z - self.point1.z

13
main.py
View File

@@ -2,7 +2,7 @@ from gdpc import Editor, Block, geometry
import networks.curve as curve import networks.curve as curve
import numpy as np import numpy as np
import json import json
from buildings.Foundations import Foundations from buildings.Building import Building
editor = Editor(buffering=True) editor = Editor(buffering=True)
@@ -12,11 +12,12 @@ shapes = json.load(f)
# F = Foundations((0,0), (20,20), shapes[0]['matrice']) # F = Foundations((0,0), (20,20), shapes[0]['matrice'])
# F.polygon.fill_polygon(editor, "stone", -60) # F.polygon.fill_polygon(editor, "stone", -60)
geometry.placeCuboid(editor, (-10,-60,-10), (85,-55,85), Block("air")) geometry.placeCuboid(editor, (-10,-60,-10), (85,-55,85), Block("air"))
F = Foundations((0,0), (75,75), shapes[8]['matrice']) B = Building((0,0), (75,75), shapes[7]['matrice'])
F.polygon.fill_polygon(editor, "stone", -60) B.foundations.polygon.fill_vertice(editor, "pink_wool", -60)
F.polygon.fill_vertice(editor, "pink_wool", -60) for collumn in B.foundations.collumns:
for collumn in F.collumns: collumn.fill(editor, "white_concrete", -60, -55)
collumn.fill(editor, "stone", -60, -55) B.foundations.polygon.fill_polygon(editor, "white_concrete", -60)
# # Get a block # # Get a block
# block = editor.getBlock((0,48,0)) # block = editor.getBlock((0,48,0))

1
params.yml Normal file
View File

@@ -0,0 +1 @@
// contains all random variables