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

1
params.yml Normal file
View File

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