Calculate sky view factor
0. Import
import os
import topogenesis as tg
import pyvista as pv
import trimesh as tm
import numpy as np
from scipy.interpolate import RegularGridInterpolator
import functions
# 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
envelope_path = os.path.relpath('../data/meshes/compulsory_envelope.obj')
context_path = os.path.relpath('../data/meshes/immediate_context.obj')
# load the mesh from file
envelope_mesh = tm.load(envelope_path)
context_mesh = tm.load(context_path)
# Check if the mesh is watertight
print(envelope_mesh.is_watertight)
print(context_mesh.is_watertight)
# initiating the plotter
p = pv.Plotter(notebook=True)
# adding the meshes
p.add_mesh(tri_to_pv(envelope_mesh), color='#abd8ff')
p.add_mesh(tri_to_pv(context_mesh), color='#aaaaaa')
# plotting
p.show()
# load lattices from before
lattice_path = os.path.relpath('../data/meshes/voxelized_envelope.csv')
envelope_lattice = tg.lattice_from_csv(lattice_path)
lattice_path = os.path.relpath('../data/meshes/voxelized_envelope_highres.csv')
envelope_lattice_highres = tg.lattice_from_csv(lattice_path)
# initiating the plotter
p = pv.Plotter(notebook=True)
# adding the meshes
envelope_lattice_highres.fast_vis(p)
# plotting
p.show()
1. Creating points to represent the sky
1.1 A sphere of points
# creating a sphere mesh
sphere_mesh = tm.creation.icosphere(subdivisions=2, radius=1000, color=None)
1.2 Take only the hemisphere
# take only the points that lie on the upper hemisphere and turn them into point cloud
sky_cloud = tg.cloud(sphere_mesh.vertices[np.where(sphere_mesh.vertices[:,2] >= 0)])
1.3 Plot the sky points
# plotting the points
p = pv.Plotter(notebook=True)
# adding the meshes
sky_cloud.fast_notebook_vis(p)
envelope_lattice.fast_vis(p)
#p.add_mesh(tri_to_pv(sphere_mesh), color='#aaaaaa', style='wireframe')
p.add_mesh(tri_to_pv(context_mesh), color='#aaaaaa')
# plotting
p.show(use_ipyvtk=True)
2. Calculate the intersection between the voxels and sky points
2.1 Preparing the ray origins and directions
full_lattice = envelope_lattice * 0 + 1
#extract centroids
origin = full_lattice.centroids
# all the ray origins for each sky directions (vectorization format)
ray_srcs = np.tile(origin, [1, len(sky_cloud)]).reshape(-1, 3)
# all the ray directions for each centroid
ray_dirs = np.tile(sky_cloud, [len(origin), 1])
2.2 Intersect with the context mesh
# calculate all intersections to the context mesh
tri_id, ray_id = context_mesh.ray.intersects_id(ray_origins=ray_srcs, ray_directions=ray_dirs, multiple_hits=False)
2.3 Accumulate all the intersections to the context mesh per centroid
# initialize the 'hits' array with 0
hits = np.full((len(ray_dirs)), 0)
# rays that hit the context mesh are set to 1
hits[ray_id] = 1
# reshape the 'hits' array to (len(centroids), len(directions))
hits = hits.reshape(len(full_lattice.centroids), -1)
# sum up all the intersections per centroid
vox_hits = np.sum(hits, axis=1)
# calculate the percentage that doesn't hit the context mesh
vox_sky_acc = 1.0 - vox_hits / len(sky_cloud)
2.4 Store sky accessiblity into lattices
# # take the indices of voxels that are in the mesh
# env_in_lattices_id = envelope_lattice.indices.flatten()[envelope_lattice.flatten()]
# # initialize the sky access values for all centroids with 0s
# all_vox_sky_acc = np.full(len(envelope_lattice.flatten()), 0.0)
# # all voxels inside the mesh will be with the values of 'vox_sky_acc' calculated in the cell above
# all_vox_sky_acc[env_in_lattices_id] = vox_sky_acc
# # reshape it back
all_vox_sky_acc = vox_sky_acc.reshape(envelope_lattice.shape)
# turn them into lattices for later plotting
all_vox_sky_acc_lattices = tg.to_lattice(all_vox_sky_acc, envelope_lattice)
2.5 Plot the sky accessibility
# initiating the plotter
p = pv.Plotter(notebook=True)
l = all_vox_sky_acc_lattices
# Create the spatial reference
grid = pv.UniformGrid()
# Set the grid dimensions: shape because we want to inject our values
grid.dimensions = l.shape
# The bottom left corner of the data set
grid.origin = l.minbound
# These are the cell sizes along each axis
grid.spacing = l.unit
# Add the data values to the cell data
grid.point_arrays["Sky View Factor"] = l.flatten(order="F") # Flatten the Lattice
# adding the volume
opacity = np.array([0,0.6,0.6,0.6,0.6,0.6,0.6])*1.5
p.add_volume(grid, cmap="RdYlBu", clim=[0.5, 1.0],opacity=opacity, shade=True)
# plotting
p.show(use_ipyvtk=True)
sky_acc_highres = functions.interpolate(all_vox_sky_acc_lattices, envelope_lattice_highres)
# sky_acc_highres=sky_acc_highres*envelope_lattice_highres
# initiating the plotter
p = pv.Plotter(notebook=True)
vis_lattice = sky_acc_highres
# Create the spatial reference
grid = pv.UniformGrid()
# Set the grid dimensions: shape because we want to inject our values
grid.dimensions = vis_lattice.shape
# The bottom left corner of the data set
grid.origin = vis_lattice.minbound
# These are the cell sizes along each axis
grid.spacing = vis_lattice.unit
# Add the data values to the cell data
grid.point_arrays["Sky access Highres"] = vis_lattice.flatten(order="F") # Flatten the Lattice
# adding the volume
opacity = np.array([0,0.6,0.6,0.6,0.6,0.6,0.6])*1.5
p.add_volume(grid, cmap="RdYlBu", clim=[0.5, 1.0],opacity=opacity, shade=True)
# plotting
p.show(use_ipyvtk=True)
4. Save to CSV
sky_acc_highres.to_csv(os.path.relpath('../data/fields/sky_view_factor.csv'))
lattice_path = os.path.relpath('../data/fields/sky_view_factor.csv')
envelope_lattice_32 = tg.lattice_from_csv(lattice_path)
envelope_lattice_32.shape