diff --git a/buildings/Facade.py b/buildings/Facade.py index 715e88d..63b3fa9 100644 --- a/buildings/Facade.py +++ b/buildings/Facade.py @@ -6,12 +6,11 @@ from buildings.elements.Window import Window from buildings.elements.Balcony import Balcony class Facade: - def __init__(self, rdata, vertices : list[Vertice], height : int, length : int, is_inner_or_outer : COLLUMN_STYLE): + def __init__(self, rdata, vertices : list[Vertice], 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.length = length + self.height, self.length = self.get_dimentions() self.padding = 0 self.window = self.get_window() self.has_balcony = self.has_balcony() @@ -25,8 +24,8 @@ class Facade: for vertice in self.vertices: vertice.fill(editor, materials[0], self.height, xpadding = self.padding, zpadding = self.padding) with editor.pushTransform(Transform(vertice.point1.position,rotation = vertice.facing.value)): - self.build_inter_floor(vertice) - self.window.build(editor, vertice.get_len(), self.height, materials) + self.window.build(editor, materials) + self.build_inter_floor() def get_window(self) -> Window: if self.is_inner_or_outer == COLLUMN_STYLE.OUTER or self.is_inner_or_outer == COLLUMN_STYLE.BOTH: @@ -35,20 +34,23 @@ class Facade: max_width = self.length-2*self.padding max_height = min(self.height, self.rdata["windows"]["size"]["max_height"]) - return Window(self.rdata["windows"] ,max_width, max_height) + return Window(self.rdata["windows"] ,max_width, max_height, self.length, self.height) def get_balcony(self) -> Balcony: - len = rd.randint(self.rdata["balcony"]["size"]["min_len"], self.rdata["balcony"]["size"]["max_len"]) max_width = self.length-2*self.padding - return Balcony(len, max_width, self.window) + return Balcony(self.rdata["balcony"], max_width, self.window) def build_inter_floor(self): if self.has_inter_floor: - geometry.placeCuboid(self.editor,(0,self.height,-1),(self.length,self.height,-1),Block(self.materials[4], {"facing": "south", "half": "top"})) + geometry.placeCuboid(self.editor,(0,self.height,0),(self.length-1,self.height,0),Block(self.materials[0])) + geometry.placeCuboid(self.editor,(0,self.height,-1),(self.length-1,self.height,-1),Block(self.materials[4], {"facing": "south", "half": "top"})) def has_balcony(self) -> bool: - return self.rdata["balcony"] >= rd.random() + return self.rdata["balcony"]["proba"] >= rd.random() def has_inter_floor(self) -> bool: return self.rdata["inter_floor"] >= rd.random() + + def get_dimentions(self) -> tuple[int]: + return ( self.vertices[0].get_height(), self.vertices[0].get_len()) \ No newline at end of file diff --git a/buildings/elements/Balcony.py b/buildings/elements/Balcony.py index fb13129..f5d8d18 100644 --- a/buildings/elements/Balcony.py +++ b/buildings/elements/Balcony.py @@ -2,12 +2,20 @@ import random as rd from buildings.elements.Window import Window class Balcony: - def __init__(self, rdata, length : int, max_width : int, windows : Window): + def __init__(self, rdata, max_width : int, windows : Window): self.rdata = rdata - self.length = length self.max_width = max_width self.windows = windows + self.length = self.get_len() + self.has_multiple = self.has_multiple_balcony() - def has_multiple_balcony(self): + def follow_window(self) -> bool: + pass + + + def has_multiple_balcony(self) -> bool: if self.max_width < self.rdata["balcony"]["multiple"]["min_width"]: return False - return self.rdata["balcony"]["multiple"]["proba"] >= rd.random() \ No newline at end of file + return self.rdata["balcony"]["multiple"]["proba"] >= rd.random() + + def get_len(self) -> int: + return rd.randint(self.rdata["balcony"]["size"]["min_len"], self.rdata["balcony"]["size"]["max_len"]) \ No newline at end of file diff --git a/buildings/elements/Glass.py b/buildings/elements/Glass.py new file mode 100644 index 0000000..b7e1be3 --- /dev/null +++ b/buildings/elements/Glass.py @@ -0,0 +1,22 @@ +from gdpc import Editor +from buildings.geometry.Vertice import Vertice + +class Glass: + def __init__(self, x1 : int, x2 : int, group1 : list[Vertice], group2 : list[Vertice] = None): + self.x1, self.x2 = x1, x2 + self.group1, self.group2 = group1, group2 + + + def build(self, editor : Editor, material1 : str, material2 : str): + for elt in self.group1: + elt.fill(editor, material1) + if self.group2 is None: return + for elt in self.group2: + elt.fill(editor, material2) + + def reset_groups(self): + self.group1, self.group2 = [], [] + + def get_len(self): + return self.x2 - self.x1 + 1 + \ No newline at end of file diff --git a/buildings/elements/Window.py b/buildings/elements/Window.py index 4d211da..6719db7 100644 --- a/buildings/elements/Window.py +++ b/buildings/elements/Window.py @@ -5,83 +5,31 @@ from utils.Enums import COLLUMN_STYLE, BORDER_RADIUS from utils.functions import * from buildings.geometry.Point import Point from buildings.geometry.Vertice import Vertice +from buildings.elements.Glass import Glass class Window: - def __init__(self, rdata, max_width : int, max_height : int): + def __init__(self, rdata, max_width : int, max_height : int, facade_len : int, facade_height : int): self.rdata = rdata self.width, self.height = self.get_size(max_width, max_height) self.is_grounded = self.is_grounded() - self.has_multiple_windows = self.has_multiple_windows() self.is_alternate = self.is_alternate() - self.has_vertical_crossbar, self.has_horizontal_crossbar = self.has_crossbars() self.border_radius = self.border_radius() - self.padding = 0 + self.has_multiple = self.has_multiple_windows() + self.has_vertical_crossbar, self.has_horizontal_crossbar = self.has_crossbars() + self.padding, self.ypadding = self.get_padding(facade_len, facade_height) + self.windows = self.get_windows() self.editor, self.materials = None,None - def build(self, editor : Editor, facade_len : int, facade_height : int, materials : list[str]): + def build(self, editor : Editor, materials : list[str]): self.editor = editor self.materials = materials - - # correction to avoid asymetry - self.padding = (facade_len - self.width)//2 - self.width = facade_len - self.padding*2 - - if not self.is_grounded: editor.transform @= Transform((0,(facade_height-self.height)//2,0)) - editor.transform @= Transform((self.padding,0,0)) - - if self.has_multiple_windows: self.build_multiple_windows() - else : - self.place_glasses(0, self.width) - - def build_multiple_windows(self): - slices = rd.randint(3, self.width//self.rdata["size"]["min_width"]) - mid = math.ceil(slices/2) - windows_count = mid - 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 - - is_even= slices % 2 == 0 - is_window, gap = True, 0 - remainder = self.width - (window_size*windows_count + inter_size*inter_count) - for i in range(1,slices+1): - wsize,isize = window_size, inter_size - if is_even and i == mid: wsize, isize = wsize*2, isize*2 - if i == mid: wsize, isize = wsize + remainder, isize + remainder - - # kepp a spacing between windows, "is revert" is used to keep symetry - if is_window: - self.place_glasses(gap, gap+wsize) - gap += wsize - else : - gap += isize - - is_window = not is_window - - def place_glasses(self, x1 : int, x2 : int): - len = x2 - x1 - if self.is_alternate: - mid = x1 + len//2 - - is_block, is_even = False, len % 2 == 0 - for x in range(x1,x2): - if is_even and x == mid: is_block = not is_block # to keep symetry - id = 1 if not is_block else 2 - geometry.placeCuboid(self.editor,(x,0,0),(x,self.height,0),Block(self.materials[id])) - is_block = not is_block - - else: - geometry.placeCuboid(self.editor,(x1,0,0),(x2-1,self.height,0),Block(self.materials[1])) - - self.build_crossbars(x1, x2-1, len) - if len > 1: self.build_border_radius(x1, x2-1) - - def get_size(self, max_width : int ,max_height : int) -> tuple[int,int]: - return ( - rd.randint(self.rdata["size"]["min_width"],max_width), - rd.randint(self.rdata["size"]["min_height"],max_height) - ) - + with editor.pushTransform(Transform((self.padding,self.ypadding,0))): + for g in self.windows: + len = g.get_len() + g.build(editor, materials[1], materials[2]) + self.build_crossbars(g.x1, g.x2, len) + if len > 1: self.build_border_radius(g.x1, g.x2) + def build_crossbars(self, x1 : int, x2 : int, len : int): if self.has_vertical_crossbar and self.height >= self.rdata["crossbars"]["min_height_for_vertical_crossbar"]: y = self.height//2 @@ -97,10 +45,64 @@ class Window: if self.border_radius == BORDER_RADIUS.TOP_AND_BOTTOM: self.editor.placeBlock((x1,0,0),Block(self.materials[4], {"facing": "west"})) self.editor.placeBlock((x2,0,0),Block(self.materials[4], {"facing": "east"})) - - def is_grounded(self): - # if the window is grounded or if there is a padding between the window and the ground - return self.rdata["grounded"] >= rd.random() + + + def get_windows(self) -> list[Glass]: + windows = [] + if not self.has_multiple: windows = [Glass(0,self.width-1,[self.create_window(0, self.width)])] + else: windows = self.get_multiple_windows() + if self.is_alternate: self.alternate(windows) + + return windows + + def get_multiple_windows(self) -> list[Glass]: + windows = [] + slices = rd.randint(3, self.width//self.rdata["size"]["min_width"]) + mid = math.ceil(slices/2) + windows_count = mid + 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 + + is_even= slices % 2 == 0 + is_window, gap = True, 0 + remainder = self.width - (window_size*windows_count + inter_size*inter_count) + if windows_count % 2 == 1 and inter_count % 2 == 1: + inter_count -= 1 + remainder += inter_size + is_even = not is_even + print(window_size,windows_count,inter_size,inter_count,remainder,self.width,"\n\n") + for i in range(1,slices+1): + wsize,isize = window_size, inter_size + if is_even and i == mid: wsize, isize = wsize*2, isize*2 + if i == mid: wsize, isize = wsize + remainder, isize + remainder + + if is_window: + windows.append(Glass(gap, gap+wsize-1,[self.create_window(gap, wsize)])) + gap += wsize + else : + gap += isize + + is_window = not is_window + + return windows + + def alternate(self, windows : list[Glass]): + for g in windows: + g.reset_groups() + len = g.get_len() + mid = g.x1 + len//2 + + is_block, is_even = False, len % 2 == 0 + for x in range(g.x1,g.x2+1): + if is_even and x == mid: is_block = not is_block # to keep symetry + if is_block: g.group2.append(self.create_window(x)) + else : g.group1.append(self.create_window(x)) + is_block = not is_block + + def create_window(self, x1 : int, length : int = None) -> Vertice: + x2 = x1 if length is None else x1 + length -1 + return Vertice(Point(x1,0,0), Point(x2,self.height,0)) def has_multiple_windows(self): if self.width > self.rdata["size"]["max_width"]: return True @@ -112,6 +114,27 @@ class Window: # if the window alternate between glass_blocks and glass_panes return self.rdata["alternate"] >= rd.random() + + def get_size(self, max_width : int ,max_height : int) -> tuple[int,int]: + return ( + rd.randint(self.rdata["size"]["min_width"],max_width), + rd.randint(self.rdata["size"]["min_height"],max_height) + ) + + def get_padding(self, facade_len : int, facade_height : int) -> tuple[int]: + padding,ypadding = 0,0 + if not self.is_grounded: ypadding = (facade_height - self.height)//2 + + # correction to avoid asymetry + padding = (facade_len - self.width)//2 + self.width = facade_len - padding*2 + + return (padding, ypadding) + + def is_grounded(self): + # if the window is grounded or if there is a padding between the window and the ground + return self.rdata["grounded"] >= rd.random() + def has_crossbars(self): # if the window has crossbars data = self.rdata["crossbars"] diff --git a/buildings/geometry/Rectangle.py b/buildings/geometry/Rectangle.py index a199982..827ba41 100644 --- a/buildings/geometry/Rectangle.py +++ b/buildings/geometry/Rectangle.py @@ -9,8 +9,12 @@ class Rectangle: def get_position(self): return (self.point1.position, self.point2.position) + def get_height(self): + return self.point2.y - self.point1.y + 1 + def fill(self,editor : Editor, material : str, y : 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 y is None: y = self.point2.y geometry.placeCuboid(editor, (self.point1.x+xpadding, 0, self.point1.z+zpadding), (self.point2.x-xpadding, y, self.point2.z-zpadding), Block(material)) diff --git a/buildings/geometry/Vertice.py b/buildings/geometry/Vertice.py index c576af5..691f6b5 100644 --- a/buildings/geometry/Vertice.py +++ b/buildings/geometry/Vertice.py @@ -3,7 +3,7 @@ from buildings.geometry.Point import Point from buildings.geometry.Rectangle import Rectangle class Vertice(Rectangle): - def __init__(self, point1 : Point, point2 : Point, facing : DIRECTION): + def __init__(self, point1 : Point, point2 : Point, facing : DIRECTION = None): Rectangle.__init__(self, point1, point2) self.facing = facing diff --git a/main.py b/main.py index 9dd676e..55c4506 100644 --- a/main.py +++ b/main.py @@ -30,7 +30,7 @@ geometry.placeCuboid(editor, (0,0,-1), (100,15,1), Block("air")) x = 0 facade = [] for i in range(3,13): - facade.append(Facade(random_data["buildings"]["facade"],[Vertice(Point(x,0,0), Point(x+i,0,0), DIRECTION.NORTH)],i,i,COLLUMN_STYLE.NONE)) + facade.append(Facade(random_data["buildings"]["facade"],[Vertice(Point(x,0,0), Point(x+i,i,0), DIRECTION.NORTH)],COLLUMN_STYLE.NONE)) x += i+2 for f in facade: