Step 7 Shafts and corridors
Pseudocode
import packages such as kmeans, numpy, networkx...
import lattice
create stencil
# calculate cluster centers
for each agent:
agent_locations = np.where
number_clusters = len(agent_locations / 100 +1)
kmeans = kmeans(nnumber_clusters)
cluster_centers.append()
# make shafts
again, kmeans = kmeans(n_clusters=5)
for cluster:
shaft_lattice = 1
# make adjacency matrix
for all voxels:
voxel_neighbours = avail_latt.find_neighbours_masked(stencil)
for each neighbour:
if avail_latt[neighbour] == 1:
adjacency_list.append()
Make connectivity graph
# corridor growth
for all clusters:
slice horizontally
for each shaft:
find shortest path to cluster with connectivity graph
for each level:
find the shafts
for each shaft:
construct destination and source
for each destination:
find shortest path to a source(shaft) with connectivity graph
</div>
</div>
</div>
<div class="cell border-box-sizing text_cell rendered" markdown="1">
<div class="inner_cell" markdown="1">
<div class="text_cell_render border-box-sizing rendered_html" markdown="1">
#### Load required libraries
</div>
</div>
</div>
<div class="cell border-box-sizing code_cell rendered" markdown="1">
<div class="input">
```python
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
import scipy as sp
from scipy.sparse import csr_matrix
import pickle
from sklearn.cluster import KMeans
np.random.seed(0)
import copy
Loading the lattice
# 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
last_frame = lattice_from_csv('../Data/dynamic output/abm_animation/abm_f_1500.csv')
# loading the lattice from csv
lattice_path = os.path.relpath('../Data/dynamic output/voxelized_envelope_cut.csv')
avail_lattice = tg.lattice_from_csv(lattice_path)
init_avail_lattice = tg.to_lattice(np.copy(avail_lattice), avail_lattice)
# creating neighborhood definition
stencil = tg.create_stencil("von_neumann", 1, 1)
# setting the center to zero
stencil.set_index([0,0,0], 0)
# creating neighborhood definition
stencil_flat = tg.create_stencil("von_neumann", 1, 1)
# setting the center to zero
stencil_flat.set_index([0,0,0], 0)
stencil_flat.set_index([0,0,1], 0)
stencil_flat.set_index([0,0,-1], 0)
print(stencil_flat)
avail_index_bool = np.array((last_frame >= 0).tolist()) # making a new availability boolean lattice
#lattice creation
avail_lattice_bool = tg.to_lattice(avail_index_bool.reshape(avail_lattice.shape),avail_lattice)
# 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
# Visualize the mesh using pyvista plotter
# initiating the plotter
p = pv.Plotter(notebook=True)
# fast visualization of the lattice
avail_lattice_bool.fast_vis(p)
# plotting
p.window_size = 1000, 1000
# p.screenshot("corridors_1")
p.show(use_ipyvtk=True)
Calculate cluster centers
a = np.array(np.where(last_frame == 1)).T
print(a)
print(len(a))
nclusters = int(len(a) / 100 + 1)
#kmeans_model = KMeans(n_clusters= nclusters, random_state=0).fit(a)
#print(kmeans_model)
cluster_centers = []
for func_id in range(0, int(np.max(last_frame)) + 1):
if not func_id in [1]: #taking out a bug in one of the agents
function_locs = np.array(np.where(last_frame == func_id)).T
# for every multiple of 100 voxels a cluster center is created and at least 1 per agent
nclusters = int(len(function_locs) / 100 + 1)
kmeans_model = KMeans(n_clusters= nclusters, random_state=0).fit(function_locs)
cluster_centers.append(np.round(kmeans_model.cluster_centers_).astype(np.int8))
# making sure it's no longer a nested cluster center list
cluster_center_list = []
for i in range(len(cluster_centers)):
cluster_center_list.append(cluster_centers[i][0])
#creating a lattice that shows all cluster centers
cluster_center_lattice = avail_lattice_bool * 0
for cluster_center in cluster_center_list:
cluster_center_lattice[cluster_center[0], cluster_center[1], cluster_center[2]] = 1
Visualizing cluster
p = pv.Plotter(notebook=True)
base_lattice = cluster_center_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 boundingbox wireframe
p.add_mesh(grid.outline(), color="grey", label="Domain")
# adding the avilability lattice
avail_lattice_bool.fast_vis(p)
# 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, 1.1])
# adding the voxels
p.add_mesh(threshed, name='sphere', show_edges=True, opacity=1.0, show_scalar_bar=False, color = [1,1,0])
p.window_size = 1000, 1000
p.screenshot("clusters")
p.show(use_ipyvtk=True)
Make shafts
kmeans_model = KMeans(n_clusters=5, random_state=0).fit(cluster_center_list) # select how many shafts
cluster_centers_total = np.round(kmeans_model.cluster_centers_).astype(np.int8)
# init shaft lattice
shft_lattice = avail_lattice_bool * 0
# setting the shafts to 1
for cl_cen in cluster_centers_total:
shft_lattice[cl_cen[0],cl_cen[1],:] = 1
Visualize
p = pv.Plotter(notebook=True)
base_lattice = shft_lattice * avail_lattice_bool
# 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 boundingbox wireframe
p.add_mesh(grid.outline(), color="grey", label="Domain")
# adding the avilability lattice
avail_lattice_bool.fast_vis(p)
# 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, 1.1])
# adding the voxels
p.add_mesh(threshed, name='sphere', show_edges=True, opacity=1.0, show_scalar_bar=False)
p.window_size = 1000, 1000
p.screenshot("shafts")
p.show(use_ipyvtk=True)
Make adjacency matrix
avail_lattice_flat = avail_lattice_bool.flatten()
# find the number of all voxels
vox_count = avail_lattice_bool.size
# initialize the adjacency matrix
adj_list = []
# Finding the index of the available voxels in avail_lattice
avail_index = np.array(np.where(avail_lattice_bool == 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_bool.shape)
# retrieve the list of neighbours of the voxel based on the stencil
vox_neighs = init_avail_lattice.find_neighbours_masked(stencil, loc = vox_loc)
# iterating over the neighbours
for neigh in vox_neighs:
if avail_lattice_flat[neigh] == 1:
adj_list.append([1.0, vox_id, neigh])
#list to array
adj_array = np.array(adj_list).T
#array to sparce matrix
adj_matrix_sparse = csr_matrix((adj_array[0], (adj_array[1], adj_array[2])), shape=(vox_count,vox_count))
#sparce matrix to nx connectivity graph
g = nx.from_scipy_sparse_matrix(adj_matrix_sparse)
Corridor growth
occ_ind = cluster_center_list
# initialize corridor lattice
cor_lattice = shft_lattice * 0
cor_flat = cor_lattice.flatten()
# 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
if nx.algorithms.shortest_paths.generic.has_path(g, src_ind, dst_ind):
path = nx.algorithms.shortest_paths.astar.astar_path(g, src_ind, dst_ind)
paths.append(path)
path_lens.append(len(path))
# find the number of shortest path
for shortest_path_index in np.array(path_lens).argsort()[:1]: #select how many paths to connect to closest shafts
cor_flat[paths[shortest_path_index]] = 1
#set the floor level the shafts need to be connected
connected_floor_levels = [2]
for level in connected_floor_levels:
cur_floor_level = shft_lattice[:,:,level] #finding floor level
shaft_vox_inds = np.array(np.where(cur_floor_level > 0)).T #finding shafts
dst_ind_list = []
source_ind_list = []
for shft_ind in shaft_vox_inds:
# construct the destination address
dst_vox = np.array([shft_ind[0],shft_ind[1],level])
index = np.ravel_multi_index(dst_vox, shft_lattice.shape) #finding the 1D index
#setting the indices as both src and dst
dst_ind_list.append(index)
source_ind_list.append(index)
for dst_ind in dst_ind_list:
paths = []
path_lens = []
for source_ind in source_ind_list: #checking if path to itself
if dst_ind == source_ind:
continue
if nx.algorithms.shortest_paths.generic.has_path(g, source_ind, dst_ind):
path = nx.algorithms.shortest_paths.astar.astar_path(g, source_ind, dst_ind) #finding the closest paths
paths.append(path)
path_lens.append(len(path))
# find the 2 shortest paths
for shortest_path_index in np.array(path_lens).argsort()[:2]: # selecting how many paths to connect to closests shafts
cor_flat[paths[shortest_path_index]] = 1
# reshape the flat lattice
cor_lattice = cor_flat.reshape(cor_lattice.shape)
Visualize
p = pv.Plotter(notebook=True)
base_lattice = (shft_lattice + cor_lattice) * avail_lattice_bool
# 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 boundingbox wireframe
p.add_mesh(grid.outline(), color="grey", label="Domain")
# adding the avilability lattice
avail_lattice_bool.fast_vis(p)
# 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.window_size = 1000, 1000
p.screenshot("corridors")
p.show(use_ipyvtk=True)
shafts_and_corridors_lattice = avail_lattice_bool * 0
shafts_and_corridors_lattice[np.where(cor_lattice == 1)] = 2
shafts_and_corridors_lattice[np.where(shft_lattice == 1)] = 1
shafts_and_corridors_lattice *= avail_lattice_bool
csv_path = os.path.relpath('../data/dynamic output/shafts_and_corridors_lattice.csv')
shafts_and_corridors_lattice.to_csv(csv_path)
csv_path = os.path.relpath('../data/dynamic output/avail_lattice_bool1.csv')
avail_lattice_bool.to_csv(csv_path)
Credits
__author__ = "Shervin Azadi and Pirouz Nourian"
__license__ = "MIT"
__version__ = "1.0"
__url__ = "https://github.com/shervinazadi/spatial_computing_workshops"
__summary__ = "Spatial Computing Design Studio Workshop on MCDA and Path Finding for Generative Spatial Relations"