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
from utils.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]]):
def __init__(self,rdata, position : tuple[int,int], size : tuple[int, int], matrice : list[list[int]], floors : int):
self.position = position
self.length, self.width = size
self.matrice = matrice
self.floors = floors
# 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(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)
self.facade = Facade(self.foundations.vertices, floor_height, is_inner_or_outer)
def build(self, editor, materials : list[str]):
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:
# Tiles are constant square units different for each buildings
return self.length
smaller_side = min(self.length, self.width)
# 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
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
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 buildings.geometry.Vertice import Vertice
from buildings.geometry.Point import Point
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], collumn_style : COLLUMN_STYLE):
self.rdata = rdata
self.vertices = vertices
self.is_inner_or_outer = is_inner_or_outer
self.height = height
self.length = length
self.collumn_style = collumn_style
self.height, self.length = self.get_dimentions()
self.padding = 0
self.window = self.get_window()
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
def build(self, editor : Editor, materials : list[str]):
self.editor = editor
self.materials = materials
points = sum([[vertice.point1, vertice.point2] 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)
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)
with editor.pushTransform(Transform(vertice.point1.position,rotation = vertice.facing.value, flip = flip)):
self.window.build(editor, 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:
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
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 build_inter_floor(self, vertice : Vertice):
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"}))
def get_balcony(self) -> Balcony|None:
if not self.has_balcony: return None
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:
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()
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 numpy as np
import math
from utils.Enums import COLLUMN_STYLE
from utils.functions import *
from buildings.geometry.Tile import Tile
from buildings.geometry.Polygon import Polygon
from buildings.geometry.Point import Point
from buildings.geometry.Rectangle import Rectangle
from buildings.elements.Collumn import Collumn
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,
rdata,
size : tuple[int, int],
matrice : list[list[int]],
tile_size : int,
is_collumn_full_tile : bool,
is_inner_or_outer : COLLUMN_STYLE):
tile_size : int):
# 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
self.is_inner_or_outer = select_random(rdata["collumn_style"], COLLUMN_STYLE)
self.floor_height = rd.randint(rdata["floor"]["min_height"], rdata["floor"]["max_height"])-1
self.size = size
self.length, self.width = size
@@ -36,17 +36,27 @@ class Foundations:
self.polygon = self.get_polygon()
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):
self.tiles.append(tile)
def get_polygon(self) -> Polygon:
## The polygon is a shape of tiles representing the foundation shape
polygon = Polygon(self.size)
avaliable_space = (self.length_in_tiles, self.width_in_tiles)
# we save the distribution, usefull for the next steps
self.x_distribution = self.get_distribution(len(self.matrice), avaliable_space[0])
self.z_distribution = self.get_distribution(len(self.matrice[0]), avaliable_space[1])
self.x_distribution = self.get_distribution(len(self.matrice), self.length_in_tiles)
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
x_padding = 0
@@ -61,7 +71,7 @@ class Foundations:
z_padding += zsize * 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)
return polygon
@@ -106,6 +116,7 @@ class Foundations:
return sizes
def get_columns(self) -> list[Collumn]:
if self.is_inner_or_outer == COLLUMN_STYLE.NONE: return []
collumns = []
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:
def __init__(self, length, width):
self.length = length
self.width = width
def __init__(self, rdata, max_width : int, windows : Window, collumn_style : COLLUMN_STYLE):
self.rdata = rdata
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):
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 math
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 buildings.geometry.Point import Point
from buildings.geometry.Vertice import Vertice
from buildings.elements.WindowElt.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
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
self.padding = (facade_len - self.width)//2
self.width = facade_len - self.padding*2
def build_crossbars(self, x1 : int, x2 : int, len : int):
if self.has_vertical_crossbar and self.height+1 >= 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"}))
if not self.is_grounded: editor.transform @= Transform((0,(facade_height-self.height)//2,0))
editor.transform @= Transform((self.padding,0,0))
def build_border_radius(self, x1 : int, x2 : int):
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()
else :
self.place_glasses(0, self.width)
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)
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"])
mid = math.ceil(slices/2)
windows_count = mid
@@ -44,63 +66,43 @@ class Window:
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
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)
windows.append(Glass(gap, gap+wsize-1,[self.create_window(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
return windows
is_block, is_even = False, len % 2 == 0
for x in range(x1,x2):
def alternate(self, windows : list[Glass]):
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
id = 1 if not is_block else 2
geometry.placeCuboid(self.editor,(x,0,0),(x,self.height,0),Block(self.materials[id]))
if is_block: g.group2.append(self.create_window(x))
else : g.group1.append(self.create_window(x))
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)
)
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 create_window(self, x1 : int, length : int = None) -> Vertice:
x2 = x1 if length is None else x1 + length -1
return Vertice(Point(x = x1), Point(x2,self.height))
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"]
@@ -119,4 +142,4 @@ class Window:
return (data["vertical_crossbar"] >= rd.random(), data["horizontal_crossbar"] >= rd.random())
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:
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
self.x = x
self.y = y
self.z = 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
self.x = x if x != None else self.x
self.y = y if y != None else self.y
self.z = z if z != None else 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 gdpc import Editor, Block, geometry
from gdpc import Editor, Block, geometry, Transform
from buildings.geometry.Tile import Tile
from buildings.geometry.Point import Point
from buildings.geometry.Rectangle import Rectangle
@@ -11,10 +11,11 @@ class Polygon:
self.shape = []
self.vertices = []
def fill_polygon(self, editor : Editor, material : str, y : int, y2 : int = None):
if y2 == None: y2 = y
def fill(self, editor : Editor, material : str, y : int = 0, y2 : int = None):
if y2 == None: y2 = 0
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):
if y2 == None: y2 = y
@@ -66,7 +67,7 @@ class Polygon:
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:
targets = tile.get_neighbors_coords()
for vertice_num,target in enumerate(targets):
@@ -74,7 +75,7 @@ class Polygon:
if not has_neighbor:
vertice = tile.get_vertice(vertice_num)
vertices.append(vertice)
tile.set_vertice(DIRECTION(vertice_num), vertice)
tile.set_vertice(DIRECTION(vertice_num), vertice, height)
else :
tile.set_neighbor(vertice_num, has_neighbor)

View File

@@ -9,8 +9,15 @@ class Rectangle:
def get_position(self):
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):
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))
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):
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 + 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:
@@ -80,8 +80,9 @@ class Tile:
case DIRECTION.SOUTH :
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
vertice.point2.y = height
match(direction):
case DIRECTION.WEST :
self.west_vertice = vertice

View File

@@ -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
@@ -16,6 +16,9 @@ class Vertice(Rectangle):
return [Point(x = self.point1.x, z = self.point1.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
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 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 *
editor = Editor(buffering=True)
@@ -20,32 +15,23 @@ shapes = f.data
y = YamlReader('params.yml')
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)
geometry.placeCuboid(editor, (0,0,-1), (100,15,1), Block("air"))
geometry.placeCuboid(editor, (-5,0,-8), (170,25,25), 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))
x += i+2
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)
padd = 0
for i in range(4,13):
building = Building(random_data["buildings"], (padd, 0), (i,i), shapes[0]['matrice'], 3)
building.build(editor, ["stone_bricks","glass_pane","glass","cobblestone_wall","stone_brick_stairs","oak_planks","white_concrete","cobblestone","stone_brick_slab"])
padd += i + 10

View File

@@ -4,7 +4,7 @@ buildings:
min_tile_size: 3
max_tile_size: 12
foudations:
foundations:
collumn_style :
# proportion of each style
none: 1
@@ -16,6 +16,7 @@ buildings:
max_height: 7
facade:
windows:
size:
min_height: 2
@@ -39,5 +40,39 @@ buildings:
none: 2
top: 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):
NORTH = 0
WEST = 1
EAST = 1
SOUTH = 2
EAST = 3
WEST = 3
class COLLUMN_STYLE(Enum):
NONE = 0
@@ -12,7 +12,17 @@ class COLLUMN_STYLE(Enum):
OUTER = 2
BOTH = 3
class BORDER_RADIUS(Enum):
class WINDOW_BORDER_RADIUS(Enum):
NONE = 0
TOP = 1
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