Creating an entrance acces lattice
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
from scipy.interpolate import RegularGridInterpolator
np.random.seed(0)
0.2. Define the Neighborhood (Stencil)
# creating neighborhood definition
stencil = tg.create_stencil("von_neumann", 1, 1)
# setting the center to zero
stencil.set_index([0,0,0], 0)
print(stencil)
0.3. Load the envelope lattice as the avialbility lattice
# loading the lattice from csv
lattice_path = os.path.relpath('../data/lattice972.csv')
avail_lattice = tg.lattice_from_csv(lattice_path)
init_avail_lattice = tg.to_lattice(np.copy(avail_lattice), avail_lattice)
full_lattice = avail_lattice * 0 + 1
1. Distance Field Construction
1.1. Extract the connectivity graph from the lattice based on the defined stencil
# find the number of all voxels
vox_count = full_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(full_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, full_lattice.shape)
# retrieve the list of neighbours of the voxel based on the stencil
vox_neighs = full_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)
1.2. Compute distances on the graph
# compute the distance of all voxels to all voxels using floyd warshal algorithm
dist_mtrx = nx.floyd_warshall_numpy(g)
# this is not an efficient way of making thios calculation, but it works and
# now we can easily make multiple different entrance acces lattices.
1.3. Select the entrance voxel
pv.set_plot_theme("document")
p = pv.Plotter(notebook=True)
# initialize the selection lattice
base_lattice = avail_lattice * 0 - 1
# init base flat
base_flat = base_lattice.flatten().astype(int)
# 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
init_avail_lattice.fast_vis(p)
def create_mesh(value):
i = int(value)
# init base flat
base_flat = base_lattice.flatten().astype(int)
base_flat = base_flat * 0 - 1
base_flat[i] = 0
base_new = base_flat.reshape(base_lattice.shape)
# Add the data values to the cell data
grid.cell_arrays["Selection"] = base_new.flatten(order="F").astype(int) # Flatten the array!
# filtering the voxels
threshed = grid.threshold([-0.1, 0.9])
# adding the voxels
p.add_mesh(threshed, name='sphere', show_edges=True, opacity=1.0, show_scalar_bar=False)
return
p.add_slider_widget(create_mesh, [0, len(base_flat)], title='1D Index', value=929, event_type="always", style="classic", pointa=(0.1, 0.1), pointb=(0.9, 0.1))
# create_mesh(928)
# adding the meshes
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
#loading in the context
context_path = os.path.relpath('../data/immediate_context.obj')
context_mesh = tm.load(context_path)
p.add_mesh(tri_to_pv(context_mesh), opacity=0.1, style='wireframe')
cpos = [(785.8704805788776, 708.4540755788776, 741.8613927288776),
(65.08283250000001, -12.333572500000002, 21.07374465),
(0.0, 0.0, 1.0)]
p.camera_position = cpos
p.window_size = 2000, 2000
p.show(use_ipyvtk=True)
# p.screenshot("entrance_1")
print(p.camera_position)
1.4. Construct Distance to Entrance Lattice
# select the corresponding row in the matrix
ent_dist = dist_mtrx[929]
# find the maximum valid value
max_valid = np.ma.masked_invalid(ent_dist).max()
# set the infinities to one more than the maximum valid values
ent_dist[ent_dist == np.inf] = max_valid + 1
# mapping the values from (0, max) to (1, 0)
ent_flat = 1 - ent_dist / np.max(ent_dist)
# constructing the lattice
ent_acc_lattice = tg.to_lattice(ent_flat.reshape(avail_lattice.shape), avail_lattice)
1.5. Visualize the distance 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
pv.set_plot_theme("document")
# load the mesh from file
context_path = os.path.relpath('../data/immediate_context.obj')
context_mesh = tm.load(context_path)
# initiating the plotter
p = pv.Plotter(notebook=True)
# Create the spatial reference
grid = pv.UniformGrid()
# Set the grid dimensions: shape because we want to inject our values
grid.dimensions = ent_acc_lattice.shape
# The bottom left corner of the data set
grid.origin = ent_acc_lattice.minbound
# These are the cell sizes along each axis
grid.spacing = ent_acc_lattice.unit
# Add the data values to the cell data
grid.point_arrays["Entrance Access"] = ent_acc_lattice.flatten(order="F") # Flatten the Lattice
# adding the meshes
p.add_mesh(tri_to_pv(context_mesh), opacity=0.1, style='wireframe')
# adding the volume
opacity = np.array([0.0,0.6,0.6,0.6,0.6,0.6,0.6])
p.add_volume(grid, cmap="coolwarm", clim=[0.0, 1.0] ,opacity=opacity)
# plotting
cpos = [(785.8704805788776, 708.4540755788776, 741.8613927288776),
(65.08283250000001, -12.333572500000002, 21.07374465),
(0.0, 0.0, 1.0)]
p.camera_position = cpos
p.window_size = 2000, 2000
p.show(use_ipyvtk=True)
# p.screenshot("entrance_2")
print(p.camera_position)
2. Distance field interpolation
2.1. Loading the highres lattice
# loading the lattice from csv
lattice_path = os.path.relpath('../data/solar_envelope_324.csv')
highres_env_lattice = tg.lattice_from_csv(lattice_path)
2.2. Interpolating the lattice
#loading the interpolation function
def interpolate(lowres_lattice, env_lattice):
# line spaces
x_space = np.linspace(lowres_lattice.minbound[0], lowres_lattice.maxbound[0],lowres_lattice.shape[0])
y_space = np.linspace(lowres_lattice.minbound[1], lowres_lattice.maxbound[1],lowres_lattice.shape[1])
z_space = np.linspace(lowres_lattice.minbound[2], lowres_lattice.maxbound[2],lowres_lattice.shape[2])
# interpolation function
interpolating_function = RegularGridInterpolator((x_space, y_space, z_space), lowres_lattice, bounds_error=False, fill_value=None)
# high_res lattice
full_lattice = env_lattice + 1
# sample point
sample_points = full_lattice.centroids
# interpolation
interpolated_values = interpolating_function(sample_points)
# lattice construction
interpolated_lattice = tg.to_lattice(interpolated_values.reshape(env_lattice.shape), env_lattice)
# nulling the unavailable cells
interpolated_lattice *= env_lattice
return interpolated_lattice
#interpolating
highres_ent_acc_lattice = interpolate(ent_acc_lattice,highres_env_lattice)
2.3. Visualizing the interpolated 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
base_lattice = highres_ent_acc_lattice
pv.set_plot_theme("document")
# initiating the plotter
p = pv.Plotter(notebook=True)
# Create the spatial reference
grid = pv.UniformGrid()
# Set the grid dimensions: shape because we want to inject our values
grid.dimensions = base_lattice.shape
# The bottom left corner of the data set
grid.origin = base_lattice.minbound
# These are the cell sizes along each axis
grid.spacing = base_lattice.unit
# Add the data values to the cell data
grid.point_arrays["Entrance Access"] = base_lattice.flatten(order="F") # Flatten the Lattice
# adding the meshes
p.add_mesh(tri_to_pv(context_mesh), opacity=0.1, style='wireframe')
# adding the volume
opacity = np.array([0,0.6,0.6,0.6,0.6,0.6,0.6])
p.add_volume(grid, cmap="coolwarm", clim=[0, 1.0],opacity=opacity, shade=False)
# plotting
cpos = [(785.8704805788776, 708.4540755788776, 741.8613927288776),
(65.08283250000001, -12.333572500000002, 21.07374465),
(0.0, 0.0, 1.0)]
p.camera_position = cpos
p.window_size = 2000, 2000
p.show(use_ipyvtk=True)
# p.screenshot("entrance_3")
print(p.camera_position)
2.4. Save Entrance Access Lattice to CSV
# save the sun access latice to csv
csv_path = os.path.relpath('../data/closeness_car_park_entrance_324.csv')
highres_ent_acc_lattice.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"