diff --git a/buildings/Building.py b/buildings/Building.py index befeb87..5a7b740 100644 --- a/buildings/Building.py +++ b/buildings/Building.py @@ -1,5 +1,5 @@ import random as rd -from Enums import COLLUMN_STYLE +from utils.Enums import COLLUMN_STYLE from buildings.Foundations import Foundations from buildings.Facade import Facade diff --git a/buildings/Facade.py b/buildings/Facade.py index 25b07ad..a4bddce 100644 --- a/buildings/Facade.py +++ b/buildings/Facade.py @@ -1,23 +1,46 @@ -from Enums import COLLUMN_STYLE +import random as rd +from utils.Enums import COLLUMN_STYLE, DIRECTION +from gdpc import Editor 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): + def __init__(self, rdata, vertices : list[Vertice], height : int, lenght : int, is_inner_or_outer : COLLUMN_STYLE): + self.rdata = rdata self.vertices = vertices self.is_inner_or_outer = is_inner_or_outer self.height = height + self.lenght = lenght 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 build(self, editor : Editor, materials : list[str], y : int): + padding = 0 + if self.is_inner_or_outer == COLLUMN_STYLE.OUTER or self.is_inner_or_outer == COLLUMN_STYLE.BOTH: + padding = 1 + + for vertice in self.vertices: + xpadding, zpadding = 0, 0 + if vertice.facing == DIRECTION.NORTH or vertice.facing == DIRECTION.SOUTH: + xpadding = padding + else: zpadding = padding + + vertice.fill(editor, materials[0], y, y + self.height, xpadding = xpadding, zpadding = zpadding) + self.window.build(editor, vertice, self.height, y, materials) def get_window_size(self) -> tuple[int,int]: - pass + max_width = self.lenght + max_height = min(self.height, self.rdata["windows"]["size"]["max_height"]) + if self.is_inner_or_outer == COLLUMN_STYLE.OUTER or self.is_inner_or_outer == COLLUMN_STYLE.BOTH: + max_width -= 2 + + return ( + rd.randint(self.rdata["windows"]["size"]["min_width"],max_width), + rd.randint(self.rdata["windows"]["size"]["min_height"],max_height) + ) def has_balcony(self) -> bool: pass @@ -26,4 +49,4 @@ class Facade: pass def get_window(self) -> Window: - pass \ No newline at end of file + return Window(self.rdata["windows"] ,self.window_size) \ No newline at end of file diff --git a/buildings/Foundations.py b/buildings/Foundations.py index b36c610..76c5eab 100644 --- a/buildings/Foundations.py +++ b/buildings/Foundations.py @@ -1,7 +1,7 @@ import random as rd import numpy as np import math -from Enums import COLLUMN_STYLE +from utils.Enums import COLLUMN_STYLE from buildings.geometry.Tile import Tile from buildings.geometry.Polygon import Polygon from buildings.geometry.Point import Point @@ -54,9 +54,9 @@ class Foundations: # this bullshit is to create tiles from the matrice and the distribution x_padding = self.position.x - for x,xsize in enumerate(self.x_distribution): + for x,xsize in utils.Enumerate(self.x_distribution): z_padding = self.position.z - for z,zsize in enumerate(self.z_distribution): + for z,zsize in utils.Enumerate(self.z_distribution): if self.matrice[x][z] == 1: for xi in range(xsize): for zi in range(zsize): @@ -131,14 +131,13 @@ class Foundations: return self._suppr_doubblons_collumns(collumns) def _suppr_doubblons_collumns(self, collumns : list[Collumn]): - for index,collumn in enumerate(collumns): + for index,collumn in utils.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 \ No newline at end of file diff --git a/buildings/elements/Window.py b/buildings/elements/Window.py index 81edaff..dfd15de 100644 --- a/buildings/elements/Window.py +++ b/buildings/elements/Window.py @@ -1,7 +1,63 @@ +import random as rd +import math +from gdpc import Editor, Block, geometry +from utils.Enums import DIRECTION +from buildings.geometry.Vertice import Vertice + class Window: - def __init__(self, size : tuple[int,int]): - self.size = size + def __init__(self, rdata, size : tuple[int,int]): + self.rdata = rdata + self.width, self.height = size + self.is_grounded = self.is_grounded() + self.has_multiple_windows = self.has_multiple_windows() + self.padding = 0 + def build(self, editor : Editor, vertice : Vertice, height : int, y : int, materials : list[str]): + self.padding = (vertice.get_size() - self.width)//2 + if not self.is_grounded: y += (height - self.height)//2 + + if self.has_multiple_windows: self.build_multiple_windows(editor, vertice, self.padding, self.height, y, materials) + else : vertice.fill(editor, materials[1], y, y + self.height, xpadding = self.padding, zpadding = self.padding) + + def build_multiple_windows(self, editor : Editor, vertice : Vertice, padding : int, height : int, y : int, materials : list[str]): + slices = rd.randint(2, self.width//self.rdata["size"]["min_width"]) + windows_count = math.ceil(slices/2) + inter_count = slices - windows_count + window_size = rd.randint(self.rdata["size"]["min_width"], self.width-inter_count // windows_count) + inter_size = (self.width - window_size*windows_count) // inter_count + + revert, switching = slices % 2 == 0, math.ceil(slices/2) + is_revert, gap = False, 0 + for i in range(1,slices+1): + modulo = i % 2 + if revert and i == switching: is_revert = True + + # kepp a spacing between windows, "is revert" is used to keep symetry + if modulo == 0 or (modulo == 1 and is_revert): + #set the values to orient windows in x or z axis + xpadding,xlen,zpadding,zlen = 0,0,0,0 + if vertice.facing == DIRECTION.NORTH or vertice.facing == DIRECTION.SOUTH: + xpadding,xlen = self.padding + gap, window_size + else: zpadding,zlen = self.padding + gap, window_size + + geometry.placeCuboid(editor, + (vertice.point1.x+xpadding, y, vertice.point1.z+zpadding), + (vertice.point1.x+xpadding+xlen, y+self.height, vertice.point1.z+zpadding+zlen), + Block(materials[1])) + gap += window_size + else : + gap += inter_size + + def is_grounded(self): + # if the window is grounded or if there is a padding between the window and the ground + if self.rdata["grounded"] >= rd.random(): return True + return False + + def has_multiple_windows(self): + if self.width > self.rdata["size"]["max_width"]: return True + if self.width >= self.rdata["multiple"]["min_width"]: + if self.rdata["multiple"]["proba"] >= rd.random(): return True + return False def open(self): pass diff --git a/buildings/geometry/Polygon.py b/buildings/geometry/Polygon.py index 9e7ea8e..16a03bd 100644 --- a/buildings/geometry/Polygon.py +++ b/buildings/geometry/Polygon.py @@ -1,4 +1,4 @@ -from Enums import DIRECTION +from utils.Enums import DIRECTION from gdpc import Editor, Block, geometry from buildings.geometry.Tile import Tile from buildings.geometry.Point import Point @@ -70,7 +70,7 @@ class Polygon: 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): + for vertice_num,target in utils.Enumerate(targets): has_neighbor = self._has_neighbor(target, tiles) if not has_neighbor: vertice = tile.get_vertice(vertice_num) diff --git a/buildings/geometry/Rectangle.py b/buildings/geometry/Rectangle.py index 8c7ea24..784d1fc 100644 --- a/buildings/geometry/Rectangle.py +++ b/buildings/geometry/Rectangle.py @@ -9,6 +9,9 @@ class Rectangle: def get_position(self): return (self.point1.position, self.point2.position) - def fill(self,editor : Editor, material : str, y : int, y2 : int = None): + def fill(self,editor : Editor, material : str, y : int, y2 : int = None, xpadding : int = 0, zpadding : int = 0): + if self.point2.x - self.point1.x < 2*xpadding: xpadding = 0 + if self.point2.z - self.point1.z < 2*zpadding: zpadding = 0 if y2 == None: y2 = y - geometry.placeCuboid(editor, (self.point1.x, y, self.point1.z), (self.point2.x, y2, self.point2.z), Block(material)) + + geometry.placeCuboid(editor, (self.point1.x+xpadding, y, self.point1.z+zpadding), (self.point2.x-xpadding, y2, self.point2.z-zpadding), Block(material)) diff --git a/buildings/geometry/Tile.py b/buildings/geometry/Tile.py index f1e2167..de864da 100644 --- a/buildings/geometry/Tile.py +++ b/buildings/geometry/Tile.py @@ -1,5 +1,5 @@ from gdpc import Editor, Block, geometry -from Enums import DIRECTION +from utils.Enums import DIRECTION from buildings.geometry.Point import Point from buildings.geometry.Vertice import Vertice diff --git a/buildings/geometry/Vertice.py b/buildings/geometry/Vertice.py index df58b40..edca28a 100644 --- a/buildings/geometry/Vertice.py +++ b/buildings/geometry/Vertice.py @@ -1,9 +1,9 @@ -from Enums import DIRECTION +from utils.Enums import DIRECTION from buildings.geometry.Point import Point from buildings.geometry.Rectangle import Rectangle class Vertice(Rectangle): - def __init__(self, point1 : Point, point2 : Point, facing : str): + def __init__(self, point1 : Point, point2 : Point, facing : DIRECTION): Rectangle.__init__(self, point1, point2) self.facing = facing diff --git a/main.py b/main.py index 818d02d..3020b3a 100644 --- a/main.py +++ b/main.py @@ -1,22 +1,45 @@ from gdpc import Editor, Block, geometry import networks.curve as curve import numpy as np -import json +from utils.JsonReader import JsonReader +from utils.YamlReader import YamlReader from buildings.Building import Building +from buildings.geometry.Vertice import Vertice +from buildings.geometry.Point import Point +from utils.Enums import DIRECTION,COLLUMN_STYLE +from buildings.Facade import Facade + editor = Editor(buffering=True) -f = open('buildings\shapes.json') -shapes = json.load(f) +f = JsonReader('buildings\shapes.json') +shapes = f.data + +y = YamlReader('params.yml') +random_data = y.data + +geometry.placeCuboid(editor, (0,-60,-5), (100,-45,-5), Block("air")) + +x = 0 +facade = [] +for i in range(3,13): + facade.append(Facade(random_data["buildings"]["facade"],[Vertice(Point(x,0,-5), Point(x+i,0,-5), DIRECTION.NORTH)],i,i,COLLUMN_STYLE.NONE)) + x += i+2 + +for f in facade: + f.build(editor, ["stone_bricks", "glass_pane"], -60) # F = Foundations((0,0), (20,20), shapes[0]['matrice']) # F.polygon.fill_polygon(editor, "stone", -60) -geometry.placeCuboid(editor, (-10,-60,-10), (85,-55,85), Block("air")) -B = Building((0,0), (75,75), shapes[7]['matrice']) -B.foundations.polygon.fill_vertice(editor, "pink_wool", -60) -for collumn in B.foundations.collumns: - collumn.fill(editor, "white_concrete", -60, -55) -B.foundations.polygon.fill_polygon(editor, "white_concrete", -60) + +# geometry.placeCuboid(editor, (-10,-60,-10), (85,-55,85), Block("air")) +# B = Building((0,0), (75,75), shapes[7]['matrice']) +# B.foundations.polygon.fill_vertice(editor, "pink_wool", -60) +# for collumn in B.foundations.collumns: +# collumn.fill(editor, "white_concrete", -60, -55) +# B.foundations.polygon.fill_polygon(editor, "white_concrete", -60) + + # # Get a block # block = editor.getBlock((0,48,0)) diff --git a/params.yml b/params.yml index f879b5d..d5a3875 100644 --- a/params.yml +++ b/params.yml @@ -1 +1,36 @@ -// contains all random variables +# contains all random variables +buildings: + tile_size: + min_tile_size: 3 + max_tile_size: 12 + + foudations: + collumn_style : + # proportion of each style + - none: 1 + - inner: 1 + - outer: 1 + - both: 1 + floor: + min_height: 4 + max_height: 7 + + facade: + windows: + size: + min_height: 2 + max_height: 6 + min_width: 1 + max_width: 12 + crossbars: + min_height_for_vertical_crossbar: 3 + vertical_crossbar: 0.25 + min_width_for_horizontal_crossbar: 3 + horizontal_crossbar: 0.25 + grounded: 0.5 + multiple: + # min size and probability of multiple windows on the same vertice + min_width: 5 + proba: 0.5 + inter_floor: 0.5 + balcony: 0.25 \ No newline at end of file diff --git a/requirements.txt b/requirements.txt index 1ef7339..346c05f 100644 --- a/requirements.txt +++ b/requirements.txt @@ -2,3 +2,4 @@ gdpc==7.1.0 matplotlib==3.8.2 numpy==1.26.4 scipy==1.13.0 +pyyaml==6.0.1 diff --git a/Enums.py b/utils/Enums.py similarity index 92% rename from Enums.py rename to utils/Enums.py index 33c7487..37a15fa 100644 --- a/Enums.py +++ b/utils/Enums.py @@ -7,6 +7,7 @@ class DIRECTION(Enum): SOUTH = 3 class COLLUMN_STYLE(Enum): + NONE = 0 INNER = 1 OUTER = 2 BOTH = 3 \ No newline at end of file diff --git a/utils/JsonReader.py b/utils/JsonReader.py new file mode 100644 index 0000000..c2b462c --- /dev/null +++ b/utils/JsonReader.py @@ -0,0 +1,11 @@ +import json + +class JsonReader: + def __init__(self, json_file): + self.data = self._load_json(json_file) + + def _load_json(self, json_file : str): + f = open(json_file) + js = json.load(f) + + return js \ No newline at end of file diff --git a/utils/YamlReader.py b/utils/YamlReader.py new file mode 100644 index 0000000..4b2e883 --- /dev/null +++ b/utils/YamlReader.py @@ -0,0 +1,11 @@ +import yaml + +class YamlReader: + def __init__(self, yaml_file): + self.data = self._load_yaml(yaml_file) + + def _load_yaml(self, yaml_file : str): + with open(yaml_file, 'r') as stream: + data_loaded = yaml.safe_load(stream) + + return data_loaded \ No newline at end of file