Skip to content

Shaping of the envelope

Pesudocode

# initialization
import topogenesis, numpy, pandas, ...

# load the interpolated lattice
read sun_blockage.csv & turn into sunblockage_lattice_hi

# create different lattice from threshold
n_frames = 25
for i in range(0,n_frames+1):
    frames.append(sunblockage_lattice_hi > i/25)

# calculate how many voxels inside
n_voxel = []
for frame in frames:
    n_voxel.append(sum(np.array(frame).flatten()))

# choose threshold based on how many voxel needed
base_lattice = frame[14]

# visualization and saving
visualize
save to csv

0. Initialization

0.1. importing packages and sunblockage

import os
import topogenesis as tg
import pyvista as pv
import trimesh as tm
import numpy as np
import pandas as pd

# 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
# Run this part after running 'w+4_interpolation_sun_blockage'
lattice_path = os.path.relpath('../data/dynamic output/sun_blockage.csv')
sunblockage_lattice_hi = tg.lattice_from_csv(lattice_path)

0.2. Visualizing the sun blockage

# 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 = sunblockage_lattice_hi.shape
# The bottom left corner of the data set
grid.origin = sunblockage_lattice_hi.minbound
# These are the cell sizes along each axis
grid.spacing = sunblockage_lattice_hi.unit

# Add the data values to the cell data
grid.point_arrays["Sun Blockage"] = sunblockage_lattice_hi.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=True)

# plotting
p.show()
[(269.61191187360555, 188.61191187360552, 251.61191187360552),
 (36.000000000000014, -45.0, 18.0),
 (0.0, 0.0, 1.0)]

1. Shaping

1.1. Calculate voxel size - sun blockage relationship

# Try cut the voxels based on shadow casted
n_frames = 25
frames = list()
for i in range(0,n_frames+1):
    frames.append(sunblockage_lattice_hi>i/25)

n_voxel = []
for frame in frames:
    n_voxel.append(sum(np.array(frame).flatten()))

1.2. Examine combined demand of spaces

sizes_complete = pd.read_csv("../Data/raw data/Agent_sizes.csv")
Area = sizes_complete['Area']
print("Will need larger than",sum(Area)/(3.6**2)*1.6, "of voxels")

print(n_voxel[14])
Will need larger than 5773.456790123457 of voxels
6870

base_lattice = frames[14]

1.3. Visualizing shaped envelope

p = pv.Plotter(notebook=True)

base_lattice.fast_vis(p)

# 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 axes
p.add_axes()
p.show_bounds(grid="back", location="back", color="#aaaaaa")
p.show()

2. Delete floating voxels

2.1. Defining neighbourhood that only counts up and down

# creating neighborhood definition
stencil = tg.create_stencil("von_neumann", 0, 1)
# setting the center to zero
stencil.set_index([0,0,0], 1)
stencil.set_index([0,0,1], 1)
stencil.set_index([0,0,-1], 1)

p = pv.Plotter(notebook=True)
grid = pv.UniformGrid()

grid.dimensions = np.array(stencil.shape) + 1
grid.origin = [0,0,0]
grid.spacing = [1,1,1]

grid.cell_arrays["values"] = stencil.flatten(order="F")
threshed = grid.threshold([0.9, 1.1])

p.add_mesh(threshed, show_edges=True, color="#ff8fa3", opacity=0.3)
p.show()

2.2. Delete floating voxels

insider = []
for i in base_lattice.find_neighbours(stencil):
    inside = False
    for n in i:
        neigh_3d_id = np.unravel_index(n, base_lattice.shape)
        if not base_lattice[neigh_3d_id]:
            inside = True
    if inside:
        insider.append(np.unravel_index(i[0], base_lattice.shape))

for i in insider:
    base_lattice[i[0],i[1],i[2]] = False

2.3. Visualizing final envelope

p = pv.Plotter(notebook=True)

base_lattice.fast_vis(p)

# 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 axes
p.add_axes()
p.show_bounds(grid="back", location="back", color="#aaaaaa")
p.show()

Saving to csv

csv_path = os.path.relpath('../data/dynamic output/voxelized_envelope_cut.csv')
base_lattice.to_csv(csv_path)