base foudations

This commit is contained in:
KAymeric
2024-05-06 08:54:27 +02:00
parent 0e5e6a9313
commit 1da68576f4
9 changed files with 463 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

158
buildings/Foundations.py Normal file
View File

@@ -0,0 +1,158 @@
import random as rd
import numpy as np
import math
from buildings.geometry.Tile import Tile
from buildings.geometry.Polygon import Polygon
from buildings.geometry.Point import Point
from buildings.geometry.Rectangle import Rectangle
class Foundations:
def __init__(self, position : tuple[int,int], size : tuple[int, int], matrice : list[list[int]]):
# Foundations are the base of the building, they are made of tiles and based on a matrice
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.tile_size = self.define_tile_size()
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 define_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))
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)
polygon.compress(self.tiles)
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 == 1:
return [avaliable_tiles]
if length == 2:
l = rd.randint(1,avaliable_tiles-1)
return [l,avaliable_tiles-l]
if length >= 3:
is_len_even = length % 2 == 0
is_availiable_even = avaliable_tiles % 2 == 0
sizes = []
# This is to keep symetry in case of an even matrice
if not is_len_even:
center = rd.randint(1,avaliable_tiles-length+1)
avaliable_tiles -= center
is_availiable_even = avaliable_tiles % 2 == 0
if not is_availiable_even: center += 1
sizes.append(center)
is_availiable_even = True
intersection_number = length // 2 - 1
tiles_per_side = avaliable_tiles // 2
# we keep symetry we randomize only one side
intersect_values = np.random.choice(np.arange(1,tiles_per_side), size=intersection_number, replace=False)
# we duplicate the side for the symetry
last_pos = 0
intersect_values = np.append(intersect_values,tiles_per_side)
for intersect in intersect_values:
size = [intersect - last_pos]
sizes = size + sizes + size
last_pos = intersect
# if there is a tile left, add it randomly
if not is_availiable_even: sizes[rd.randint(0,len(sizes)-1)] += 1
return sizes
def get_columns(self) -> list[Rectangle]:
collumns = []
is_full_tile = bool(rd.getrandbits(1))
x_padding = self.position.x
for x,row in enumerate(self.matrice):
z_padding = self.position.z
lenx = self.x_distribution[x]
for z,value in enumerate(row):
lenz = self.z_distribution[z]
# conditions to not make a collumn on the facade of the building (no outter collumns)
skip_first_x,skip_first_z = False,False
# if it's the first or last row/collumn
if x == 0 : skip_first_x = True
if z == 0 : skip_first_z = True
last_value_x,last_value_z = self.matrice[x-1][z],self.matrice[x][z-1]
# if the previous row/collumn is empty
if last_value_x == 0 : skip_first_x = True
if last_value_z == 0 : skip_first_z = True
next_value_x,next_value_z = 0,0
try : next_value_x = self.matrice[x+1][z]
except : pass
try : next_value_z = self.matrice[x][z+1]
except : pass
# if this part of the building is too tiny
if last_value_x == 0 and next_value_x == 0 and self.x_distribution[x] == 1: continue
if last_value_z == 0 and next_value_z == 0 and self.z_distribution[z] == 1: continue
if value == 1:
self.create_collumns(x_padding, z_padding, lenx, lenz, skip_first_x, skip_first_z, collumns)
z_padding += lenz * self.tile_size
x_padding += lenx * self.tile_size
return collumns
def create_collumns(self, basex : int, basez : int, lenx : int, lenz : int, skip_first_x : bool, skip_first_z : bool, collumns : list[Rectangle]):
for x in range(lenx):
if x==0 and skip_first_x: continue
for z in range(lenz):
if z==0 and skip_first_z: continue
collumns.append(Rectangle(Point(x = basex+x*self.tile_size, z = basez+z*self.tile_size), Point(x = basex+x*self.tile_size-1, z = basez+z*self.tile_size-1)))

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,104 @@
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], vertices : list[Vertice] = []):
self.position = position
self.size = size
self.compressed = {"shape":[], "vertices":[]}
self.vertices = vertices
def fill_polygon(self, editor : Editor, material : str, y : int, y2 : int = None):
if y2 == None: y2 = y
for rect in self.compressed["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.compressed["vertices"]:
vertice.fill(editor, Block(material), y, y2)
def compress(self, tiles : list[Tile]):
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.compressed["shape"].append(area)
remaining_vertices = self.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.compressed["vertices"].append(current)
current = remaining_vertices.pop()
if len(remaining_vertices) == 0: self.compressed["vertices"].append(current)
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 set_vertices_and_neighbors(self, tiles : list[Tile]):
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:
self.vertices.append(tile.get_vertice(vertice_num))
else :
tile.set_neighbor(vertice_num, has_neighbor)
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,64 @@
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
self.pos = Point(x = x, z = z)
leng = self.size-1
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
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_vertice(self,vertice : int) -> 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)
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 : Point):
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

View File

@@ -0,0 +1,18 @@
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)]

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]]
}
]

27
main.py
View File

@@ -1,22 +1,35 @@
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.Foundations import Foundations
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"))
F = Foundations((0,0), (75,75), shapes[8]['matrice'])
F.polygon.fill_polygon(editor, "stone", -60)
F.polygon.fill_vertice(editor, "pink_wool", -60)
for collumn in F.collumns:
collumn.fill(editor, "stone", -60, -55)
# # 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"))