158 lines
6.8 KiB
Python
158 lines
6.8 KiB
Python
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)))
|
|
|