Atrium allocation and green path finding
More about atrium allocation and green path finding
In this notebook, we will first try to allocate the center of the building which will eventually represent the atrium of the building. Secondly we want to compute the shortest path from the three public green spaces in the nearby neighborhood of the plot. We will this with the astar algorithm. Finally we will edt the voxelized envelope with the help of the atrium and the green path by broadening both, and set every voxel above the path as unavailable.
0. Initialization
0.1. Load required libraries
import os
import topogenesis as tg
import pyvista as pv
import trimesh as tm
import numpy as np
import networkx as nx
import pandas as pd
from sklearn.cluster import KMeans
np.random.seed(0)
# extra import function
def lattice_from_csv(file_path):
# read metadata
meta_df = pd.read_csv(file_path, nrows=3)
shape = np.array(meta_df['shape'])
unit = np.array(meta_df['unit'])
minbound = np.array(meta_df['minbound'])
# read lattice
lattice_df = pd.read_csv(file_path, skiprows=5)
# create the buffer
buffer = np.array(lattice_df['value']).reshape(shape)
# create the lattice
l = tg.to_lattice(buffer, minbound=minbound, unit=unit)
return l
0.2. Define the Neighborhood (Stencil)
# creating neighborhood definition - center with 0 neighbours
stencil = tg.create_stencil("moore", 0, 1)
# setting the center to zero
stencil.set_index([0, 0, 0], 0)
stencil.set_index([1, 0, 0], 1)
stencil.set_index([-1, 0, 0], 1)
stencil.set_index([1, 1, 0], 1)
stencil.set_index([-1, 1, 0], 1)
stencil.set_index([1, -1, 0], 1)
stencil.set_index([-1, -1, 0], 1)
stencil.set_index([-1, 1, 0], 1)
stencil.set_index([0, 1, 0], 1)
stencil.set_index([0, -1, 0], 1)
0.3. Load the envelope lattice as the avialbility lattice
# loading the lattice from csv
lattice_path = os.path.relpath('../data/envelope_highres_new.csv')
avail_lattice = lattice_from_csv(lattice_path)
init_avail_lattice = tg.to_lattice(np.copy(avail_lattice), avail_lattice)
0.4. Load Agents Information
# loading program (agents information) from CSV
prgm_path = os.path.relpath('../data/program_atrium.csv')
agn_info = np.genfromtxt(prgm_path, delimiter=',')[1:, 1:]
# extract agent ids
agn_ids = agn_info[:, 0]
# extract agent preferences
agn_prefs = agn_info[:, 1:]
1. Creation of Vertical Shaft
1.1. Agent initialization
# initialize the occupation lattice
occ_lattice = avail_lattice * 0 - 1
# Finding the index of the available voxels in avail_lattice
avail_flat = avail_lattice.flatten()
avail_index = np.array(np.where(avail_lattice == 1)).T
# count the number of spaces (rows) and intiialize an agent for each space
agn_num = len(agn_info)
# adding the origins to the agents locations
agn_locs = []
agn_manual_input = {
0: [[20,10,1]],
1: [[32,19,1]],
2: [[1,1,2]],
3: [[39,0,1]],
}
# retrieving the entrance access value of the free neighbours
for a_id in agn_ids:
voxel_vals = []
pot_voxels = []
# retrieve agent preferences
a_pref = agn_prefs[int(a_id)]
if a_id in agn_manual_input:
# add the newly selected neighbour location to agent locations
agn_locs.append(agn_manual_input[a_id])
for loc in agn_manual_input[a_id]:
# set the newly selected neighbour as UNavailable (0) in the availability lattice
avail_lattice[tuple(loc)] = 0
# set the newly selected neighbour as OCCUPIED by current agent
# (-1 means not-occupied so a_id)
occ_lattice[tuple(loc)] = a_id
else:
# Voxel Evaluation Loop
for pot_vox in avail_index:
if avail_lattice[tuple(pot_vox)]:
global_vox_value = 1.0
# for every lattice in the environment informations
for i, info_lattice in enumerate(env_info):
vox_val = info_lattice[tuple(pot_vox)]
agn_vox_val = np.power(vox_val, a_pref[i])
global_vox_value *= agn_vox_val
# add the neighbour value to the list of values
voxel_vals.append(global_vox_value)
pot_voxels.append(pot_vox)
# convert to numpy array
voxel_vals = np.array(voxel_vals)
# convert to numpy array
pot_voxels = np.array(pot_voxels)
# select the neighbour with highest value
selected_int = np.argmax(voxel_vals)
# find 3D intiger index of selected neighbour
selected_neigh_3d_id = tuple(pot_voxels[selected_int].T)
# find the location of the newly selected neighbour
selected_neigh_loc = np.array(selected_neigh_3d_id).flatten()
# add the newly selected neighbour location to agent locations
agn_locs.append([selected_neigh_loc])
# set the newly selected neighbour as UNavailable (0) in the availability lattice
avail_lattice[selected_neigh_3d_id] = 0
# set the newly selected neighbour as OCCUPIED by current agent
# (-1 means not-occupied so a_id)
occ_lattice[selected_neigh_3d_id] = a_id
# extract the address of all occupied voxels
occ_ind = np.array(np.where(occ_lattice > -1)).T
1.4. Set the atrium agent as vertical shaft
# find the shaft location
shaft_loc = agn_locs[0]
# init shaft lattice
shft_lattice = occ_lattice * 0
# set the shafts
for sh_loc in shaft_loc:
shft_lattice[sh_loc[0],sh_loc[1],2:] = 1
shft_voxels = np.argwhere(shft_lattice==1)
for vox in shft_voxels:
VX, VY, VZ = tuple(vox)
shft_lattice[VX, VY, :VZ] = 1
for vox in shft_voxels:
VX, VY, VZ = tuple(vox)
shft_lattice[VX, VY, VZ-1:VZ-1] = 1
2. Creation of Horizontal Corridors
2.1. Extract the connectivity graph from the lattice based on the horizontal stencil
# find the number of all voxels
vox_count = avail_lattice.size
# initialize the adjacency matrix
adj_mtrx = np.zeros((vox_count,vox_count))
# Finding the index of the available voxels in avail_lattice
avail_index = np.array(np.where(avail_lattice == 1)).T
# fill the adjacency matrix using the list of all neighbours
for vox_loc in avail_index:
# find the 1D id
vox_id = np.ravel_multi_index(vox_loc, avail_lattice.shape)
# retrieve the list of neighbours of the voxel based on the stencil
vox_neighs = avail_lattice.find_neighbours_masked(stencil, loc = vox_loc)
# iterating over the neighbours
for neigh in vox_neighs:
# setting the entry to one
adj_mtrx[vox_id, neigh] = 1.0
# construct the graph
g = nx.from_numpy_array(adj_mtrx)
2.2. Find the shortest path and construct the corridor
# initialize corridor lattice
cor_lattice = shft_lattice * 0
cor_flat = cor_lattice.flatten()
all_cors = []
# for each voxel that needs to have access to shafts
for a_vox in occ_ind:
# slice the corridor lattice horizontally
cor_floor = shft_lattice[:,:,a_vox[2]]
# find the vertical shaft voxel indices
shaft_vox_inds = np.array(np.where(cor_floor > 0)).T
paths = []
path_lens = []
for shft_ind in shaft_vox_inds:
# construct the destination address
dst_vox = np.array([shft_ind[0],shft_ind[1],a_vox[2]])
# construct 1-dimensional indices
src_ind = np.ravel_multi_index(a_vox, shft_lattice.shape)
dst_ind = np.ravel_multi_index(dst_vox, shft_lattice.shape)
# find the shortest path
try:
path = nx.algorithms.shortest_paths.astar.astar_path(g, src_ind, dst_ind)
paths.append(path)
path_lens.append(len(path))
except:
pass
# find the shortest path
shortest_path = paths[np.array(path_lens).argmin()]
all_cors.append(shortest_path)
# set the shortest path occupied in the
cor_flat[shortest_path] = 1
# reshape the flat lattice
cor_lattice = cor_flat.reshape(cor_lattice.shape)
3.Editing Voxels
3.1 Corridor
for path in all_cors:
# for each voxel in the corridor ...
for vox_1d_ind in path:
# find the 3-dimensional index of the voxel
vox_3d_ind = np.unravel_index(vox_1d_ind, cor_lattice.shape)
VX, VY, VZ = vox_3d_ind
# mark the voxel as unavailable
#init_avail_lattice[VX, VY, VZ] = 0
# mark two voxels horizontal the voxel avilable
cor_lattice[VX, VY, VZ] = 1
cor_lattice[VX:VX+3, VY:VY+3, VZ] = 1
init_avail_lattice[VX:VX+3, VY:VY+3, VZ:] = 0
3.2 Shaft
shft_voxels = np.argwhere(shft_lattice==1)
for vox in shft_voxels:
VX, VY, VZ = tuple(vox)
shft_lattice[VX-1:VX+2, VY-1:VY+2, :VZ] = 1
3.3. Visualize the corridor lattice
context_path = os.path.relpath("../data/extended_context.obj")
# load the mesh from file
context_mesh = tm.load(context_path)
p = pv.Plotter(notebook=True)
# convert mesh to pv_mesh
def tri_to_pv(tri_mesh):
faces = np.pad(tri_mesh.faces, ((0, 0),(1,0)), 'constant', constant_values=3)
pv_mesh = pv.PolyData(tri_mesh.vertices, faces)
return pv_mesh
base_lattice = cor_lattice
# Set the grid dimensions: shape + 1 because we want to inject our values on the CELL data
grid = pv.UniformGrid()
grid.dimensions = np.array(base_lattice.shape) + 1
# The bottom left corner of the data set
grid.origin = base_lattice.minbound - base_lattice.unit * 0.5
# These are the cell sizes along each axis
grid.spacing = base_lattice.unit
# adding the meshes
p.add_mesh(tri_to_pv(context_mesh), opacity=1.0, style='surface')
# adding the boundingbox wireframe
p.add_mesh(grid.outline(), color="grey", label="Domain")
# adding the avilability lattice
init_avail_lattice.fast_vis(p)
# # adding axes
# p.add_axes()
# p.show_bounds(grid="back", location="back", color="#aaaaaa")
# Add the data values to the cell data
grid.cell_arrays["Agents"] = base_lattice.flatten(order="F").astype(int) # Flatten the array!
# filtering the voxels
threshed = grid.threshold([0.9, 2.1])
# adding the voxels
p.add_mesh(threshed, name='sphere', show_edges=True, opacity=1.0, show_scalar_bar=False)
# p.add_slider_widget(create_mesh, [0, n_frames], title='Time', value=0, event_type="always", style="classic", pointa=(0.1, 0.1), pointb=(0.9, 0.1))
p.show(use_ipyvtk=True)
4. Saving
# save the sun access latice to csv
csv_path = os.path.relpath('../data/atrium_3.6.csv')
shft_lattice.to_csv(csv_path)
Credits
__author__ = "Shervin Azadi and Pirouz Nourian"
__changes_made_by__: "Frank Vahstal"
__license__ = "MIT"
__version__ = "1.0"
__url__ = "https://github.com/shervinazadi/spatial_computing_workshops"
__summary__ = "Spatial Computing Design Studio Workshop on Path Finding and Corridorfor Generative Spatial Relations"