Merge branch 'Xeon0X:main' into main

This commit is contained in:
Kévin Angelier-Leplan
2024-06-10 23:28:40 +02:00
committed by GitHub
14 changed files with 567 additions and 7 deletions

12
Enums.py Normal file
View File

@@ -0,0 +1,12 @@
from enum import Enum
class DIRECTION(Enum):
WEST = 0
EAST = 1
NORTH = 2
SOUTH = 3
class COLLUMN_STYLE(Enum):
INNER = 1
OUTER = 2
BOTH = 3

31
buildings/Building.py Normal file
View File

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

29
buildings/Facade.py Normal file
View File

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

144
buildings/Foundations.py Normal file
View File

@@ -0,0 +1,144 @@
import random as rd
import numpy as np
import math
from Enums import COLLUMN_STYLE
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
def __init__(self,
position : tuple[int,int],
size : tuple[int, int],
matrice : list[list[int]],
tile_size : int,
is_collumn_full_tile : bool,
is_inner_or_outer : COLLUMN_STYLE):
# Foundations are the base of the building, they are made of tiles and based on a matrice
# Random components
self.tile_size = tile_size
self.is_collumn_full_tile = is_collumn_full_tile
self.is_inner_or_outer = is_inner_or_outer
x,z = position
self.position = Point(x = x, z = z)
self.size = size
self.length = size[0]
self.width = size[1]
self.matrice = matrice
self.tiles = []
self.vertices = []
self.length_in_tiles = self.length // self.tile_size
self.width_in_tiles = self.width // self.tile_size
self.x_distribution = []
self.z_distribution = []
self.polygon = self.get_polygon()
self.collumns = self.get_columns()
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.position, 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])
# 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):
z_padding = self.position.z
for z,zsize in enumerate(self.z_distribution):
if self.matrice[x][z] == 1:
for xi in range(xsize):
for zi in range(zsize):
tile = Tile(self.tile_size, (x_padding + xi*self.tile_size, z_padding + zi*self.tile_size))
self.add_tile(tile)
z_padding += zsize * self.tile_size
x_padding += xsize * self.tile_size
polygon.set_vertices_and_neighbors(self.tiles, self.vertices)
polygon.compress(self.tiles, self.vertices)
return polygon
def get_distribution(self,length,avaliable_tiles):
# foundations are based on a matrice,
# this function gives the number of tiles for each row/collumn of the matrice, giving a more random shape
# The real shit start here
if length == avaliable_tiles:
return [1 for i in range(avaliable_tiles)]
if length == 1:
return [avaliable_tiles]
if length == 2:
l = rd.randint(1,avaliable_tiles-1)
return [l,avaliable_tiles-l]
if length >= 3:
sizes = []
intersections_count = math.ceil(length/2)-1
tiles_per_side = avaliable_tiles//2
correction = 0
intersect_values = np.random.choice(np.arange(1,tiles_per_side), size=intersections_count, replace=False)
#we generate only half of the distribution
last_pos = 0
intersect_values = np.append(intersect_values,tiles_per_side)
for intersect in intersect_values:
sizes.append(intersect - last_pos)
last_pos = intersect
# we duplicate the side for the symetry
symetry = sizes.copy()
symetry.reverse()
if avaliable_tiles%2 == 1: correction = 1 # if there is a tile left, add it randomly
if length%2 == 1 : sizes[-1], symetry = sizes[-1]*2 + correction, symetry[1:]
sizes += symetry
return sizes
def get_columns(self) -> list[Collumn]:
collumns = []
for tile in self.tiles:
north_west_collumn = Collumn(Point(x = tile.north_west.x-1, z = tile.north_west.z-1), tile.north_west)
north_east_collumn = Collumn(Point(x = tile.north_east.x, z = tile.north_east.z-1), Point(x = tile.north_east.x+1, z = tile.north_east.z))
south_west_collumn = Collumn(Point(x = tile.south_west.x-1, z = tile.south_west.z), Point(x = tile.south_west.x, z = tile.south_west.z+1))
south_east_collumn = Collumn(tile.south_east, Point(x = tile.south_east.x+1, z = tile.south_east.z+1))
if tile.north_vertice != None or tile.west_vertice != None: north_west_collumn.set_is_outer(True)
if tile.north_vertice != None or tile.east_vertice != None: north_east_collumn.set_is_outer(True)
if tile.south_vertice != None or tile.west_vertice != None: south_west_collumn.set_is_outer(True)
if tile.south_vertice != None or tile.east_vertice != None: south_east_collumn.set_is_outer(True)
collumns.extend([north_west_collumn, north_east_collumn, south_west_collumn, south_east_collumn])
return self._suppr_doubblons_collumns(collumns)
def _suppr_doubblons_collumns(self, collumns : list[Collumn]):
for index,collumn in enumerate(collumns):
if index == len(collumns)-1: break
for compare in collumns[index+1:]:
if collumn.point1.position == compare.point1.position :
if compare.is_outer : collumn.set_is_outer(True)
collumns.remove(compare)
print(len(collumns))
return collumns

View File

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

View File

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

View File

@@ -0,0 +1,14 @@
class Point:
def __init__(self, x : int = None, y : int = None, z : int = None, 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):
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)

View File

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

View File

@@ -0,0 +1,14 @@
from gdpc import Editor, Block, geometry
from buildings.geometry.Point import Point
class Rectangle:
def __init__(self, point1 : Point, point2 : Point):
self.point1 = point1
self.point2 = point2
def get_position(self):
return (self.point1.position, self.point2.position)
def fill(self,editor : Editor, material : str, y : int, y2 : int = None):
if y2 == None: y2 = y
geometry.placeCuboid(editor, (self.point1.x, y, self.point1.z), (self.point2.x, y2, self.point2.z), Block(material))

View File

@@ -0,0 +1,94 @@
from gdpc import Editor, Block, geometry
from Enums import DIRECTION
from buildings.geometry.Point import Point
from buildings.geometry.Vertice import Vertice
class Tile:
def __init__(self, size : int, position : tuple[int, int]):
self.size = size
x,z = position
leng = self.size-1
self.pos = Point(x = x, z = z)
self.has_vertice = False
self.north_west = self.pos
self.north_east = Point(x = self.pos.x + leng, z =self.pos.z)
self.south_west = Point(x = self.pos.x, z = self.pos.z + leng)
self.south_east = Point(x = self.pos.x + leng, z = self.pos.z + leng)
self.west_neighbor = None
self.east_neighbor = None
self.north_neighbor = None
self.south_neighbor = None
self.west_vertice = None
self.east_vertice = None
self.north_vertice = None
self.south_vertice = None
def fill(self, editor : Editor, material : str, y : int, y2 : int = None) -> list[Point]:
if y2 == None: y2 = y
geometry.placeCuboid(editor, (self.pos.x, y, self.pos.z), (self.pos.x+self.size-1, y2, self.pos.z+self.size-1), Block(material))
def get_neighbors_coords(self):
return [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), # north
Point(x = self.pos.x, z = self.pos.z + self.size)] # south
def get_neighbor(self, direction) -> Point:
match(direction):
case DIRECTION.WEST:
return self.west_neighbor
case DIRECTION.EAST:
return self.east_neighbor
case DIRECTION.NORTH:
return self.north_neighbor
case DIRECTION.SOUTH:
return self.south_neighbor
def set_neighbor(self, direction, neighbor : 'Tile'):
match(direction):
case DIRECTION.WEST:
self.west_neighbor = neighbor
case DIRECTION.EAST:
self.east_neighbor = neighbor
case DIRECTION.NORTH:
self.north_neighbor = neighbor
case DIRECTION.SOUTH:
self.south_neighbor = neighbor
def get_vertice(self,vertice : int|DIRECTION) -> Vertice:
# gives the corresponding vertice :
# 0 = west, 1 = east, 2 = north, 3 = south
match(vertice):
case 0 :
return Vertice(self.north_west, self.south_west, DIRECTION.WEST)
case 1 :
return Vertice(self.north_east, self.south_east, DIRECTION.EAST)
case 2 :
return Vertice(self.north_west, self.north_east, DIRECTION.NORTH)
case 3 :
return Vertice(self.south_west, self.south_east, DIRECTION.SOUTH)
case DIRECTION.WEST :
return self.west_vertice
case DIRECTION.EAST :
return self.east_vertice
case DIRECTION.NORTH :
return self.north_vertice
case DIRECTION.SOUTH :
return self.south_vertice
def set_vertice(self, direction : DIRECTION, vertice : Vertice):
self.has_vertice = True
match(direction):
case DIRECTION.WEST :
self.west_vertice = vertice
case DIRECTION.EAST :
self.east_vertice = vertice
case DIRECTION.NORTH :
self.north_vertice = vertice
case DIRECTION.SOUTH :
self.south_vertice = vertice

View File

@@ -0,0 +1,21 @@
from 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):
Rectangle.__init__(self, point1, point2)
self.facing = facing
def get_neighbors(self):
match self.facing:
case DIRECTION.NORTH | DIRECTION.SOUTH:
return [Point(x = self.point1.x - 1, z = self.point1.z),
Point(x = self.point2.x + 1, z = self.point2.z)]
case DIRECTION.EAST | DIRECTION.WEST:
return [Point(x = self.point1.x, z = self.point1.z - 1),
Point(x = self.point2.x, z = self.point2.z + 1)]
def get_size(self):
return self.point2.x - self.point1.x + self.point2.z - self.point1.z

59
buildings/shapes.json Normal file
View File

@@ -0,0 +1,59 @@
[
{
"id": 0,
"name": "basic_shape",
"matrice":[[1]]
},
{
"id": 1,
"name": "long_shape",
"matrice":[[0,1]]
},
{
"id": 2,
"name": "double_long_shape",
"matrice":[[1,0,1]]
},
{
"id": 3,
"name": "L_shape",
"matrice":[[1,0],
[1,1]]
},
{
"id": 4,
"name": "U_shape",
"matrice":[[1,0,1],
[1,1,1]]
},
{
"id": 5,
"name": "H_shape",
"matrice":[[1,0,1],
[1,1,1],
[1,0,1]]
},
{
"id": 6,
"name": "X_shape",
"matrice":[[0,1,0],
[1,1,1],
[0,1,0]]
},
{
"id": 7,
"name": "O_shape",
"matrice":[[1,1,1],
[1,0,1],
[1,1,1]]
},
{
"id": 8,
"name": "E_shape",
"matrice":[[1,1,1],
[1,0,0],
[1,1,1],
[1,0,0],
[1,1,1]]
}
]

28
main.py
View File

@@ -1,22 +1,36 @@
from gdpc import Editor, Block, geometry
import networks.curve as curve
import numpy as np
import json
from buildings.Building import Building
editor = Editor(buffering=True)
f = open('buildings\shapes.json')
shapes = json.load(f)
# 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)
# # Get a block
# block = editor.getBlock((0,48,0))
# # Place a block
# editor.placeBlock((394, 132, 741), Block("stone"))
#editor.placeBlock((0 , 5, 0), Block("stone"))
# # Build a cube
# geometry.placeCuboid(editor, (458, 92, 488), (468, 99, 471), Block("oak_planks"))
curve = curve.Curve([(396, 132, 740), (435, 138, 730),
(443, 161, 758), (417, 73, 729)])
curve.compute_curve()
# curve = curve.Curve([(396, 132, 740), (435, 138, 730),
# (443, 161, 758), (417, 73, 729)])
# curve.compute_curve()
for point in curve.computed_points:
print(point)
editor.placeBlock(point, Block("stone"))
# for point in curve.computed_points:
# print(point)
# editor.placeBlock(point, Block("stone"))

1
params.yml Normal file
View File

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