Merge pull request #1 from KAymeric/balcony

Balcony
This commit is contained in:
KAymeric
2024-06-11 03:09:09 +02:00
committed by GitHub
22 changed files with 456 additions and 148 deletions

View File

@@ -1,30 +1,33 @@
import random as rd import random as rd
from utils.Enums import COLLUMN_STYLE
from buildings.Foundations import Foundations from buildings.Foundations import Foundations
from buildings.Facade import Facade from buildings.Facade import Facade
class Building: class Building:
def __init__(self, position : tuple[int,int], size : tuple[int, int], matrice : list[list[int]]): def __init__(self,rdata, position : tuple[int,int], size : tuple[int, int], matrice : list[list[int]], floors : int):
self.position = position self.position = position
self.length, self.width = size self.length, self.width = size
self.matrice = matrice self.matrice = matrice
self.floors = floors
# Generate every random components here # 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() tile_size = self.gen_tile_size()
floor_height = rd.randint(4, 7)
is_inner_or_outer = COLLUMN_STYLE.BOTH self.foundations = Foundations(rdata["foundations"], size, matrice, tile_size,)
self.facade = Facade(rdata["facade"], self.foundations.vertices, self.foundations.is_inner_or_outer)
self.foundations = Foundations(size, matrice, tile_size, is_collumn_full_tile, is_inner_or_outer) def build(self, editor, materials : list[str]):
self.facade = Facade(self.foundations.vertices, floor_height, is_inner_or_outer) for y in range(self.floors):
with editor.pushTransform((self.position[0], y*(self.foundations.floor_height+1), self.position[1])):
self.foundations.build(editor, materials)
self.facade.build(editor, materials)
def gen_tile_size(self) -> int: def gen_tile_size(self) -> int:
# Tiles are constant square units different for each buildings # Tiles are constant square units different for each buildings
return self.length
smaller_side = min(self.length, self.width) smaller_side = min(self.length, self.width)
# area is too small, will work but not very well # area is too small, will work but not very well
if smaller_side <= 5 : return smaller_side
if smaller_side <= 15 : return smaller_side // 5 if smaller_side <= 15 : return smaller_side // 5
return rd.randint(3, smaller_side // len(self.matrice)) return rd.randint(3, smaller_side // len(self.matrice))

0
buildings/Entrance.py Normal file
View File

View File

@@ -1,48 +1,86 @@
import random as rd import random as rd
from utils.Enums import COLLUMN_STYLE from utils.functions import *
from utils.Enums import COLLUMN_STYLE,DIRECTION,INTER_FLOOR_BORDER
from gdpc import Editor, Block, geometry, Transform from gdpc import Editor, Block, geometry, Transform
from buildings.geometry.Vertice import Vertice from buildings.geometry.Vertice import Vertice
from buildings.geometry.Point import Point
from buildings.elements.Window import Window from buildings.elements.Window import Window
from buildings.elements.Balcony import Balcony
class Facade: 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], collumn_style : COLLUMN_STYLE):
self.rdata = rdata self.rdata = rdata
self.vertices = vertices self.vertices = vertices
self.is_inner_or_outer = is_inner_or_outer self.collumn_style = collumn_style
self.height = height self.height, self.length = self.get_dimentions()
self.length = length
self.padding = 0 self.padding = 0
self.window = self.get_window() self.window = self.get_window()
self.has_balcony = self.has_balcony() self.has_balcony = self.has_balcony()
self.has_inter_floor = self.has_inter_floor() self.balcony = self.get_balcony()
self.has_inter_floor, self.inter_floor_border_style = self.has_inter_floor()
self.editor, self.materials = None,None self.editor, self.materials = None,None
def build(self, editor : Editor, materials : list[str]): def build(self, editor : Editor, materials : list[str]):
self.editor = editor self.editor = editor
self.materials = materials self.materials = materials
points = sum([[vertice.point1, vertice.point2] for vertice in self.vertices], [])
for vertice in self.vertices: for vertice in self.vertices:
flip=(vertice.facing == DIRECTION.WEST or vertice.facing == DIRECTION.SOUTH, False, False)
vertice.fill(editor, materials[0], self.height, xpadding = self.padding, zpadding = self.padding) vertice.fill(editor, materials[0], self.height, xpadding = self.padding, zpadding = self.padding)
with editor.pushTransform(Transform(vertice.point1.position,rotation = vertice.facing.value)): with editor.pushTransform(Transform(vertice.point1.position,rotation = vertice.facing.value, flip = flip)):
self.build_inter_floor(vertice) self.window.build(editor, materials)
self.window.build(editor, vertice.get_len(), self.height, materials) if self.has_inter_floor: self.build_inter_floor()
if self.has_balcony: self.balcony.build(editor, materials)
self.correct_corners(points,vertice)
def correct_corners(self,points : list[Point], v : Vertice):
if self.padding == 0:
if self.window.border_radius != 0 and self.window.width == self.length:
if v.point1 in points:
self.editor.placeBlock((0,self.window.ypadding,0), Block(self.materials[8]))
self.editor.placeBlock((0,self.window.ypadding+self.window.height,0), Block(self.materials[8], {"type": "top"}))
if v.point2 in points:
self.editor.placeBlock((self.length-1,self.window.ypadding,0), Block(self.materials[8]))
self.editor.placeBlock((self.length-1,self.window.ypadding+self.window.height,0), Block(self.materials[8], {"type": "top"}))
if self.has_inter_floor:
material = Block("air")
if self.inter_floor_border_style == INTER_FLOOR_BORDER.SLAB:
material = Block(self.materials[8], {"type": "top"})
elif self.inter_floor_border_style == INTER_FLOOR_BORDER.STAIRS:
material = Block(self.materials[4], {"facing": "south", "half": "top"})
if v.point1 in points:
self.editor.placeBlock((-1,self.height,-1), material)
if v.point2 in points:
self.editor.placeBlock((self.length,self.height,-1), material)
def get_window(self) -> Window: def get_window(self) -> Window:
if self.is_inner_or_outer == COLLUMN_STYLE.OUTER or self.is_inner_or_outer == COLLUMN_STYLE.BOTH: if self.collumn_style.value >= 2: # collumn_style >= 2 = outer collumns
self.padding = 1 self.padding = 1
max_width = self.length-2*self.padding max_width = self.length-2*self.padding
max_height = min(self.height, self.rdata["windows"]["size"]["max_height"]) 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 build_inter_floor(self, vertice : Vertice): def get_balcony(self) -> Balcony|None:
if self.has_inter_floor: if not self.has_balcony: return None
geometry.placeCuboid(self.editor,(0,self.height,-1),(self.length,self.height,-1),Block(self.materials[4], {"facing": "south", "half": "top"})) max_width = self.length-2*self.padding
return Balcony(self.rdata["balcony"], max_width, self.window, self.collumn_style)
def build_inter_floor(self):
geometry.placeCuboid(self.editor,(self.padding,self.height,0),(self.length-1-self.padding,self.height,0),Block(self.materials[0]))
geometry.placeCuboid(self.editor,(self.padding,self.height,-1),(self.length-1-self.padding,self.height,-1),Block(self.materials[4], {"facing": "south", "half": "top"}))
def has_balcony(self) -> bool: def has_balcony(self) -> bool:
return self.rdata["balcony"] >= rd.random() return self.rdata["balcony"]["proba"] >= rd.random()
def has_inter_floor(self) -> bool: def has_inter_floor(self) -> bool:
return self.rdata["inter_floor"] >= rd.random() return (self.rdata["inter_floor"]["proba"] >= rd.random(), select_random(self.rdata["inter_floor"]["border_style"], INTER_FLOOR_BORDER))
def get_dimentions(self) -> tuple[int]:
return ( self.vertices[0].get_height(), len(self.vertices[0]))

View File

@@ -1,28 +1,28 @@
import random as rd import random as rd
import numpy as np import numpy as np
import math import math
from utils.Enums import COLLUMN_STYLE from utils.Enums import COLLUMN_STYLE
from utils.functions import *
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.elements.Collumn import Collumn from buildings.elements.Collumn import Collumn
class Foundations: class Foundations:
# TODO : gérer les collones sur les tiles trop petites et les colones 1tile/2 + fulltile # TODO : gérer les collones sur les tiles trop petites et les colones 1tile/2
def __init__(self, def __init__(self,
rdata,
size : tuple[int, int], size : tuple[int, int],
matrice : list[list[int]], matrice : list[list[int]],
tile_size : 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 # Random components
self.tile_size = tile_size self.tile_size = tile_size
self.is_collumn_full_tile = is_collumn_full_tile self.is_inner_or_outer = select_random(rdata["collumn_style"], COLLUMN_STYLE)
self.is_inner_or_outer = is_inner_or_outer self.floor_height = rd.randint(rdata["floor"]["min_height"], rdata["floor"]["max_height"])-1
self.size = size self.size = size
self.length, self.width = size self.length, self.width = size
@@ -36,17 +36,27 @@ class Foundations:
self.polygon = self.get_polygon() self.polygon = self.get_polygon()
self.collumns = self.get_columns() self.collumns = self.get_columns()
def build(self, editor, materials : list[str]):
self.polygon.fill(editor, materials[5],0)
self.polygon.fill(editor, materials[6], self.floor_height)
self.build_collumns(editor, materials)
def build_collumns(self, editor, materials : list[str]):
for collumn in self.collumns:
if collumn.is_outer and self.is_inner_or_outer == COLLUMN_STYLE.INNER: continue
if not collumn.is_outer and self.is_inner_or_outer == COLLUMN_STYLE.OUTER: continue
collumn.fill(editor, materials[7], self.floor_height)
def add_tile(self, tile : Tile): def add_tile(self, tile : Tile):
self.tiles.append(tile) self.tiles.append(tile)
def get_polygon(self) -> Polygon: def get_polygon(self) -> Polygon:
## The polygon is a shape of tiles representing the foundation shape ## The polygon is a shape of tiles representing the foundation shape
polygon = Polygon(self.size) polygon = Polygon(self.size)
avaliable_space = (self.length_in_tiles, self.width_in_tiles)
# we save the distribution, usefull for the next steps # we save the distribution, usefull for the next steps
self.x_distribution = self.get_distribution(len(self.matrice), avaliable_space[0]) self.x_distribution = self.get_distribution(len(self.matrice), self.length_in_tiles)
self.z_distribution = self.get_distribution(len(self.matrice[0]), avaliable_space[1]) self.z_distribution = self.get_distribution(len(self.matrice[0]), self.width_in_tiles)
# this bullshit is to create tiles from the matrice and the distribution # this bullshit is to create tiles from the matrice and the distribution
x_padding = 0 x_padding = 0
@@ -61,7 +71,7 @@ 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, self.vertices) polygon.set_vertices_and_neighbors(self.tiles, self.vertices, self.floor_height)
polygon.compress(self.tiles, self.vertices) polygon.compress(self.tiles, self.vertices)
return polygon return polygon
@@ -106,6 +116,7 @@ class Foundations:
return sizes return sizes
def get_columns(self) -> list[Collumn]: def get_columns(self) -> list[Collumn]:
if self.is_inner_or_outer == COLLUMN_STYLE.NONE: return []
collumns = [] collumns = []
for tile in self.tiles: for tile in self.tiles:

13
buildings/TODO Normal file
View File

@@ -0,0 +1,13 @@
Encadrement fenêtre
toit de balcon avec/sans pilliers
border radius balcon
collumn style
rembard object
détails facade
rdc
toit
tiles 3d
textures object
opti textures
opti géométrique
opti gdpc

View File

@@ -1,4 +1,131 @@
import random as rd
from gdpc import Editor, Block, geometry
from utils.functions import *
from utils.Enums import BALCONY_BORDER_RADIUS,COLLUMN_STYLE
from buildings.geometry.Point import Point
from buildings.geometry.Vertice import Vertice
from buildings.elements.Window import Window
class Balcony: class Balcony:
def __init__(self, length, width): def __init__(self, rdata, max_width : int, windows : Window, collumn_style : COLLUMN_STYLE):
self.length = length self.rdata = rdata
self.width = width self.windows = windows
self.max_width = max_width
self.collumn_style = collumn_style
self.length = self.get_len()
self.has_multiple = self.has_multiple()
self.has_details = self.has_details()
self.border_radius = self.has_border_radius()
self.follow_window = self.follow_window()
self.structure = self.get_structures()
self.editor, self.materials = None,None
def build(self, editor : Editor, materials : list[str]):
self.editor = editor
self.materials = materials
for s in self.structure:
s.fill(editor, materials[0])
self.build_rembard(s)
self.build_details(s)
self.build_border_radius(s)
def build_rembard(self, s : Vertice):
geometry.placeCuboid(self.editor,(s.point1.x,1,-1),(s.point1.x,1,-self.length),Block(self.materials[3]))
geometry.placeCuboid(self.editor,(s.point2.x,1,-1),(s.point2.x,1,-self.length),Block(self.materials[3]))
geometry.placeCuboid(self.editor,(s.point1.x,1,-self.length),(s.point2.x,1,-self.length),Block(self.materials[3]))
def build_details(self, s : Vertice):
if not self.has_details: return
geometry.placeCuboid(self.editor,(s.point1.x,0,-1),(s.point1.x,0,-self.length),Block(self.materials[4], {"facing": "east", "half": "top"}))
geometry.placeCuboid(self.editor,(s.point2.x,0,-1),(s.point2.x,0,-self.length),Block(self.materials[4], {"facing": "west", "half": "top"}))
geometry.placeCuboid(self.editor,(s.point1.x,0,-self.length),(s.point2.x,0,-self.length),Block(self.materials[4], {"facing": "south", "half": "top"}))
def build_border_radius(self, s : Vertice):
if self.border_radius == BALCONY_BORDER_RADIUS.NONE: return
geometry.placeCuboid(self.editor,(s.point1.x,0,-self.length),(s.point1.x,1,-self.length),Block("air"))
geometry.placeCuboid(self.editor,(s.point2.x,0,-self.length),(s.point2.x,1,-self.length),Block("air"))
self.editor.placeBlock((s.point1.x+1,1,-self.length+1), Block(self.materials[3]))
self.editor.placeBlock((s.point2.x-1,1,-self.length+1), Block(self.materials[3]))
if self.has_details:
self.editor.placeBlock((s.point1.x,0,-self.length+1), Block(self.materials[4], {"facing": "south", "half": "top"}))
self.editor.placeBlock((s.point1.x+1,0,-self.length), Block(self.materials[4], {"facing": "east", "half": "top"}))
self.editor.placeBlock((s.point2.x,0,-self.length+1), Block(self.materials[4], {"facing": "south", "half": "top"}))
self.editor.placeBlock((s.point2.x-1,0,-self.length), Block(self.materials[4], {"facing": "west", "half": "top"}))
if self.border_radius == BALCONY_BORDER_RADIUS.FULL:
self.editor.placeBlock((s.point1.x+1,0,-self.length+1), Block(self.materials[4], {"facing": "east", "half": "top"}))
self.editor.placeBlock((s.point2.x-1,0,-self.length+1), Block(self.materials[4], {"facing": "west", "half": "top"}))
def get_structures(self) -> list[Vertice]:
# structures are the base shape of the balcony
attach_points = self.get_attach_points()
len_attach_points = len(attach_points)-1
min_wid = self.rdata["size"]["min_width"]
min_gap = self.rdata["multiple"]["min_gap"]
growth_chance = self.rdata["growth"]
midpoint = len_attach_points//2
x1,x2 = midpoint, len_attach_points - midpoint
structures = []
centered = True
while x1 > 0:
x1 -= 1
x2 += 1 if centered else 0
leng = attach_points[x2] - attach_points[x1] - 1
if x1 == 0:
if leng >= min_wid: self.append_structure(structures, x1, x2, attach_points, len_attach_points, centered)
break
if leng < min_wid: continue
if growth_chance < rd.random():
self.append_structure(structures, x1, x2, attach_points, len_attach_points, centered)
if not self.has_multiple: break
else:
centered = False
if attach_points[x1]-min_wid < min_gap: break
gap = rd.randint(min_gap, attach_points[x1]-min_wid)
x2 = x1-gap
x1 = x2-min_wid+1
return structures
def get_attach_points(self) -> list[int]:
# points where the structures can start/finish
padding = 0 if self.collumn_style.value < 2 else 1 # collumn_style < 2 = no outer collumns
points = [i + padding for i in range(self.max_width)]
if self.follow_window:
pad = self.windows.padding
for w in self.windows.windows:
for i in range(pad+w.x1, pad+w.x2+1):
points.remove(i)
return points
def create_structure(self, x1 : int, x2 : int) -> Vertice:
return Vertice(Point(x = x1), Point(x = x2,z = -self.length))
def append_structure(self, structures : list[Vertice], x1 : int, x2 : int, attach_points : list[int], len_attach_points : int, centered : bool):
structures.append(self.create_structure(attach_points[x1], attach_points[x2]))
if not centered:
structures.append(self.create_structure(attach_points[len_attach_points-x1], attach_points[len_attach_points-x2]))
def follow_window(self) -> bool:
return not self.windows.ypadding > 3
def has_multiple(self) -> bool:
if self.max_width < self.rdata["multiple"]["min_width"]: return False
return self.rdata["multiple"]["proba"] >= rd.random()
def has_details(self) -> bool:
return self.rdata["details"] >= rd.random()
def has_border_radius(self) -> bool:
if self.length < 2: return BALCONY_BORDER_RADIUS.NONE
return select_random(self.rdata["border_radius"], BALCONY_BORDER_RADIUS)
def get_len(self) -> int:
return rd.randint(self.rdata["size"]["min_len"], self.rdata["size"]["max_len"])

View File

@@ -9,3 +9,6 @@ class Collumn(Rectangle):
def set_is_outer(self, is_outer : bool): def set_is_outer(self, is_outer : bool):
self.is_outer = is_outer self.is_outer = is_outer
def __repr__(self):
return super().__repr__() + f"\nIs outer : {self.is_outer}\n\n"

View File

@@ -0,0 +1,19 @@
from buildings.geometry.Vertice import Vertice
class FacadeDetails:
def __init__(self,rdata , zones : list[Vertice]):
self.zones = zones
self.sizes = self.get_sizes()
def get_sizes(self) -> list[tuple[int]]:
# foreach different zone sizes in self.zones, we will gen different details
sizes = []
center_for_symetry = len(self.zones) // 2
for zone in self.zones:
size = zone.point2.position - zone.point1.position
if size not in sizes :
sizes.append(size)
return sizes

View File

@@ -0,0 +1,3 @@
class Buttons:
def __init__(self):
pass

View File

@@ -1,39 +1,61 @@
import random as rd import random as rd
import math import math
from gdpc import Editor, Block, geometry, Transform from gdpc import Editor, Block, geometry, Transform
from utils.Enums import COLLUMN_STYLE, BORDER_RADIUS from utils.Enums import WINDOW_BORDER_RADIUS
from utils.functions import * from utils.functions import *
from buildings.geometry.Point import Point from buildings.geometry.Point import Point
from buildings.geometry.Vertice import Vertice from buildings.geometry.Vertice import Vertice
from buildings.elements.WindowElt.Glass import Glass
class Window: 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.rdata = rdata
self.width, self.height = self.get_size(max_width, max_height) self.width, self.height = self.get_size(max_width, max_height)
self.is_grounded = self.is_grounded() self.is_grounded = self.is_grounded()
self.has_multiple_windows = self.has_multiple_windows()
self.is_alternate = self.is_alternate() self.is_alternate = self.is_alternate()
self.has_vertical_crossbar, self.has_horizontal_crossbar = self.has_crossbars()
self.border_radius = self.border_radius() 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 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.editor = editor
self.materials = materials self.materials = materials
with editor.pushTransform(Transform((self.padding,self.ypadding,0))):
for g in self.windows:
leng = len(g)
g.build(editor, materials[1], materials[2])
self.build_crossbars(g.x1, g.x2, leng)
if leng > 1: self.build_border_radius(g.x1, g.x2)
# correction to avoid asymetry def build_crossbars(self, x1 : int, x2 : int, len : int):
self.padding = (facade_len - self.width)//2 if self.has_vertical_crossbar and self.height+1 >= self.rdata["crossbars"]["min_height_for_vertical_crossbar"]:
self.width = facade_len - self.padding*2 y = self.height//2
geometry.placeCuboid(self.editor,(x1,y,0),(x2,y,0),Block(self.materials[3]))
if self.has_horizontal_crossbar and len >= self.rdata["crossbars"]["min_width_for_horizontal_crossbar"]:
x = len//2
geometry.placeCuboid(self.editor,(x1+x,0,0),(x2-x,self.height,0),Block(self.materials[3], {"up" : "true"}))
if not self.is_grounded: editor.transform @= Transform((0,(facade_height-self.height)//2,0)) def build_border_radius(self, x1 : int, x2 : int):
editor.transform @= Transform((self.padding,0,0)) if self.border_radius != WINDOW_BORDER_RADIUS.NONE:
self.editor.placeBlock((x1,self.height,0),Block(self.materials[4], {"facing": "west", "half": "top"}))
self.editor.placeBlock((x2,self.height,0),Block(self.materials[4], {"facing": "east", "half": "top"}))
if self.border_radius == WINDOW_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"}))
if self.has_multiple_windows: self.build_multiple_windows() def get_windows(self) -> list[Glass]:
else : windows = []
self.place_glasses(0, self.width) 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)
def build_multiple_windows(self): return windows
def get_multiple_windows(self) -> list[Glass]:
windows = []
slices = rd.randint(3, self.width//self.rdata["size"]["min_width"]) slices = rd.randint(3, self.width//self.rdata["size"]["min_width"])
mid = math.ceil(slices/2) mid = math.ceil(slices/2)
windows_count = mid windows_count = mid
@@ -44,63 +66,43 @@ class Window:
is_even= slices % 2 == 0 is_even= slices % 2 == 0
is_window, gap = True, 0 is_window, gap = True, 0
remainder = self.width - (window_size*windows_count + inter_size*inter_count) 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
for i in range(1,slices+1): for i in range(1,slices+1):
wsize,isize = window_size, inter_size wsize,isize = window_size, inter_size
if is_even and i == mid: wsize, isize = wsize*2, isize*2 if is_even and i == mid: wsize, isize = wsize*2, isize*2
if i == mid: wsize, isize = wsize + remainder, isize + remainder if i == mid: wsize, isize = wsize + remainder, isize + remainder
# kepp a spacing between windows, "is revert" is used to keep symetry
if is_window: if is_window:
self.place_glasses(gap, gap+wsize) windows.append(Glass(gap, gap+wsize-1,[self.create_window(gap, wsize)]))
gap += wsize gap += wsize
else : else :
gap += isize gap += isize
is_window = not is_window is_window = not is_window
def place_glasses(self, x1 : int, x2 : int): return windows
len = x2 - x1
if self.is_alternate:
mid = x1 + len//2
is_block, is_even = False, len % 2 == 0 def alternate(self, windows : list[Glass]):
for x in range(x1,x2): for g in windows:
g.reset_groups()
leng = len(g)
mid = g.x1 + leng//2
is_block, is_even = False, leng % 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_even and x == mid: is_block = not is_block # to keep symetry
id = 1 if not is_block else 2 if is_block: g.group2.append(self.create_window(x))
geometry.placeCuboid(self.editor,(x,0,0),(x,self.height,0),Block(self.materials[id])) else : g.group1.append(self.create_window(x))
is_block = not is_block is_block = not is_block
else: def create_window(self, x1 : int, length : int = None) -> Vertice:
geometry.placeCuboid(self.editor,(x1,0,0),(x2-1,self.height,0),Block(self.materials[1])) x2 = x1 if length is None else x1 + length -1
return Vertice(Point(x = x1), Point(x2,self.height))
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)
)
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
geometry.placeCuboid(self.editor,(x1,y,0),(x2,y,0),Block(self.materials[3]))
if self.has_horizontal_crossbar and len >= self.rdata["crossbars"]["min_width_for_horizontal_crossbar"]:
x = len//2
geometry.placeCuboid(self.editor,(x1+x,0,0),(x2-x,self.height,0),Block(self.materials[3], {"up" : "true"}))
def build_border_radius(self, x1 : int, x2 : int):
if self.border_radius != BORDER_RADIUS.NONE:
self.editor.placeBlock((x1,self.height,0),Block(self.materials[4], {"facing": "west", "half": "top"}))
self.editor.placeBlock((x2,self.height,0),Block(self.materials[4], {"facing": "east", "half": "top"}))
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 has_multiple_windows(self): def has_multiple_windows(self):
if self.width > self.rdata["size"]["max_width"]: return True 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 # if the window alternate between glass_blocks and glass_panes
return self.rdata["alternate"] >= rd.random() 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): def has_crossbars(self):
# if the window has crossbars # if the window has crossbars
data = self.rdata["crossbars"] data = self.rdata["crossbars"]
@@ -119,4 +142,4 @@ class Window:
return (data["vertical_crossbar"] >= rd.random(), data["horizontal_crossbar"] >= rd.random()) return (data["vertical_crossbar"] >= rd.random(), data["horizontal_crossbar"] >= rd.random())
def border_radius(self): def border_radius(self):
return select_random(self.rdata["border_radius"], BORDER_RADIUS) return select_random(self.rdata["border_radius"], WINDOW_BORDER_RADIUS)

View File

@@ -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 __len__(self):
return self.x2 - self.x1 + 1

View File

@@ -1,14 +1,17 @@
class Point: class Point:
def __init__(self, x : int = None, y : int = None, z : int = None, p : tuple[int] = None): def __init__(self, x : int = 0, y : int = 0, z : int = 0, p : tuple[int] = None):
if p != None: x,y,z = p if p != None: x,y,z = p
self.x = x self.x = x
self.y = y self.y = y
self.z = z self.z = z
self.position = (x,y,z) self.position = (x,y,z)
def set_position(self, x : int = None, y : int = None, z : int = None, p : tuple[int] = None): def set_position(self, x : int = 0, y : int = 0, z : int = 0, p : tuple[int] = None):
if p != None: x,y,z = p if p != None: x,y,z = p
self.x = x if x != None else self.x self.x = x if x != None else self.x
self.y = y if y != None else self.y self.y = y if y != None else self.y
self.z = z if z != None else self.z self.z = z if z != None else self.z
self.position = (self.x,self.y,self.z) self.position = (self.x,self.y,self.z)
def __repr__(self):
return f"Point({self.position})"

View File

@@ -1,5 +1,5 @@
from utils.Enums import DIRECTION from utils.Enums import DIRECTION
from gdpc import Editor, Block, geometry from gdpc import Editor, Block, geometry, Transform
from buildings.geometry.Tile import Tile from buildings.geometry.Tile import Tile
from buildings.geometry.Point import Point from buildings.geometry.Point import Point
from buildings.geometry.Rectangle import Rectangle from buildings.geometry.Rectangle import Rectangle
@@ -11,10 +11,11 @@ class Polygon:
self.shape = [] self.shape = []
self.vertices = [] self.vertices = []
def fill_polygon(self, editor : Editor, material : str, y : int, y2 : int = None): def fill(self, editor : Editor, material : str, y : int = 0, y2 : int = None):
if y2 == None: y2 = y if y2 == None: y2 = 0
for rect in self.shape: for rect in self.shape:
rect.fill(editor, material, y, y2) with editor.pushTransform(Transform((0,y,0))):
rect.fill(editor, material, 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
@@ -66,7 +67,7 @@ class Polygon:
if len(remaining_vertices) == 0: self.vertices.append(current) if len(remaining_vertices) == 0: self.vertices.append(current)
def set_vertices_and_neighbors(self, tiles : list[Tile], vertices : list[Vertice]): def set_vertices_and_neighbors(self, tiles : list[Tile], vertices : list[Vertice], height : int):
for tile in tiles: for tile in tiles:
targets = tile.get_neighbors_coords() targets = tile.get_neighbors_coords()
for vertice_num,target in enumerate(targets): for vertice_num,target in enumerate(targets):
@@ -74,7 +75,7 @@ class Polygon:
if not has_neighbor: if not has_neighbor:
vertice = tile.get_vertice(vertice_num) vertice = tile.get_vertice(vertice_num)
vertices.append(vertice) vertices.append(vertice)
tile.set_vertice(DIRECTION(vertice_num), vertice) tile.set_vertice(DIRECTION(vertice_num), vertice, height)
else : else :
tile.set_neighbor(vertice_num, has_neighbor) tile.set_neighbor(vertice_num, has_neighbor)

View File

@@ -9,8 +9,15 @@ class Rectangle:
def get_position(self): def get_position(self):
return (self.point1.position, self.point2.position) return (self.point1.position, self.point2.position)
def get_height(self):
return self.point2.y - self.point1.y
def fill(self,editor : Editor, material : str, y : int = None, xpadding : int = 0, zpadding : int = 0): 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.x - self.point1.x < 2*xpadding: xpadding = 0
if self.point2.z - self.point1.z < 2*zpadding: zpadding = 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)) geometry.placeCuboid(editor, (self.point1.x+xpadding, 0, self.point1.z+zpadding), (self.point2.x-xpadding, y, self.point2.z-zpadding), Block(material))
def __repr__(self):
return f"{type(self).__name__}\n1 : {str(self.point1)},\n2 : {str(self.point2)}"

View File

@@ -32,9 +32,9 @@ class Tile:
def get_neighbors_coords(self): def get_neighbors_coords(self):
return [Point(x = self.pos.x, z = self.pos.z - self.size), # north return [Point(x = self.pos.x, z = self.pos.z - self.size), # north
Point(x = self.pos.x - self.size, z = self.pos.z), # west Point(x = self.pos.x + self.size, z = self.pos.z), # east
Point(x = self.pos.x, z = self.pos.z + self.size), # south Point(x = self.pos.x, z = self.pos.z + self.size), # south
Point(x = self.pos.x + self.size, z = self.pos.z)] # east Point(x = self.pos.x - self.size, z = self.pos.z)] # west
def get_neighbor(self, direction) -> Point: def get_neighbor(self, direction) -> Point:
@@ -80,8 +80,9 @@ class Tile:
case DIRECTION.SOUTH : case DIRECTION.SOUTH :
return self.south_vertice return self.south_vertice
def set_vertice(self, direction : DIRECTION, vertice : Vertice): def set_vertice(self, direction : DIRECTION, vertice : Vertice, height : int):
self.has_vertice = True self.has_vertice = True
vertice.point2.y = height
match(direction): match(direction):
case DIRECTION.WEST : case DIRECTION.WEST :
self.west_vertice = vertice self.west_vertice = vertice

View File

@@ -3,7 +3,7 @@ from buildings.geometry.Point import Point
from buildings.geometry.Rectangle import Rectangle from buildings.geometry.Rectangle import Rectangle
class Vertice(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) Rectangle.__init__(self, point1, point2)
self.facing = facing self.facing = facing
@@ -16,6 +16,9 @@ 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_len(self): def __len__(self):
return self.point2.x - self.point1.x + self.point2.z - self.point1.z + 1 return self.point2.x - self.point1.x + self.point2.z - self.point1.z + 1
def __repr__(self):
return super().__repr__() + f"\nFacing : {self.facing} \n\n"

38
main.py
View File

@@ -5,11 +5,6 @@ from utils.JsonReader import JsonReader
from utils.YamlReader import YamlReader from utils.YamlReader import YamlReader
from buildings.Building import Building from buildings.Building import Building
from buildings.geometry.Vertice import Vertice
from buildings.geometry.Point import Point
from utils.Enums import DIRECTION,COLLUMN_STYLE,BORDER_RADIUS
from buildings.Facade import Facade
from utils.functions import * from utils.functions import *
editor = Editor(buffering=True) editor = Editor(buffering=True)
@@ -20,32 +15,23 @@ shapes = f.data
y = YamlReader('params.yml') y = YamlReader('params.yml')
random_data = y.data random_data = y.data
# transform = Transform((0,-60,-20),rotation = 0)
# editor.transform.push(transform)
# for i in range(4):
# with editor.pushTransform(Transform(rotation = i)):
# geometry.placeCuboid(editor, (0,0,0), (0,3,5), Block("stone"))
transform = Transform((0,-60,-5),rotation = 0) transform = Transform((0,-60,80),rotation = 0)
editor.transform.push(transform) editor.transform.push(transform)
geometry.placeCuboid(editor, (0,0,-1), (100,15,1), Block("air")) geometry.placeCuboid(editor, (-5,0,-8), (170,25,25), Block("air"))
x = 0 padd = 0
facade = [] for i in range(4,13):
for i in range(3,13): building = Building(random_data["buildings"], (padd, 0), (i,i), shapes[0]['matrice'], 3)
facade.append(Facade(random_data["buildings"]["facade"],[Vertice(Point(x,0,0), Point(x+i,0,0), DIRECTION.NORTH)],i,i,COLLUMN_STYLE.NONE)) building.build(editor, ["stone_bricks","glass_pane","glass","cobblestone_wall","stone_brick_stairs","oak_planks","white_concrete","cobblestone","stone_brick_slab"])
x += i+2 padd += i + 10
for f in facade:
f.build(editor, ["stone_bricks","glass_pane","glass","cobblestone_wall","stone_brick_stairs"])
# 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)

View File

@@ -4,7 +4,7 @@ buildings:
min_tile_size: 3 min_tile_size: 3
max_tile_size: 12 max_tile_size: 12
foudations: foundations:
collumn_style : collumn_style :
# proportion of each style # proportion of each style
none: 1 none: 1
@@ -16,6 +16,7 @@ buildings:
max_height: 7 max_height: 7
facade: facade:
windows: windows:
size: size:
min_height: 2 min_height: 2
@@ -39,5 +40,39 @@ buildings:
none: 2 none: 2
top: 1 top: 1
top_and_bottom: 1 top_and_bottom: 1
inter_floor: 0.5
balcony: 0.25 balcony:
proba : 0.25
growth: 0.5 # [growth]% chance to have min_width + 1 balcony length, [growth**2]% chance to have min_width + 2 balcony length, etc
size:
min_len : 1
max_len : 3
min_width : 3
multiple:
# probability to have multiple balcony IF POSSIBLE
# this feature need a very large facade
proba: 1
min_width: 5
min_gap: 1
details: 0.35
border_radius:
# proportion of each style
none: 6
# no difference if there is no details
medium: 1
full: 1
Entrance:
centered: 0.8
different_facade: 0.75
size:
min_height: 5
max_height: 9
inter_floor:
proba: 0.5
border_style:
# bloc used to fill the corner of the interfloor
none: 1
slab: 2
stairs: 2

View File

@@ -2,9 +2,9 @@ from enum import Enum
class DIRECTION(Enum): class DIRECTION(Enum):
NORTH = 0 NORTH = 0
WEST = 1 EAST = 1
SOUTH = 2 SOUTH = 2
EAST = 3 WEST = 3
class COLLUMN_STYLE(Enum): class COLLUMN_STYLE(Enum):
NONE = 0 NONE = 0
@@ -12,7 +12,17 @@ class COLLUMN_STYLE(Enum):
OUTER = 2 OUTER = 2
BOTH = 3 BOTH = 3
class BORDER_RADIUS(Enum): class WINDOW_BORDER_RADIUS(Enum):
NONE = 0 NONE = 0
TOP = 1 TOP = 1
TOP_AND_BOTTOM = 2 TOP_AND_BOTTOM = 2
class BALCONY_BORDER_RADIUS(Enum):
NONE = 0
MEDIUM = 1
FULL = 2
class INTER_FLOOR_BORDER(Enum):
NONE = 0
SLAB = 1
STAIRS = 2