### ===================================================================================================================
### FUNCTION: Find to locate the piers in the building
### ===================================================================================================================
# Copyright ©VIIA 2024
### ===================================================================================================================
### 1. Import modules
### ===================================================================================================================
# General imports
from __future__ import annotations
from typing import TYPE_CHECKING, List
from copy import deepcopy
# References for functions and classes in the rhdhv_fem package
from rhdhv_fem.fem_math import fem_normal_vector
from rhdhv_fem.shapes import Wall
from rhdhv_fem.fem_math import fem_greater, fem_smaller
# References for functions and classes in the viiaPackage
if TYPE_CHECKING:
from viiapackage.viiaStatus import ViiaProject
### ===================================================================================================================
### 2. Function viia_locate_piers
### ===================================================================================================================
[docs]def viia_locate_piers(
project: ViiaProject, walls: List[Wall], allow_reduction_bottom: bool = True,
allow_reduction_top: bool = True, tolerance: float = 0.001) -> List[dict]:
"""
Function to identify piers within walls and collect relevant coordinates, positioning and workplane for further
processing.
Input:
- project (obj): VIIA project object containing collections of fem objects and project variables.
- walls (list): List of object references of walls to be considered.
- allow_reduction_bottom (bool): Select to take into account a reduction of the effective wall height due to
the thickness of the supporting floor at the bottom of the wall. Default value True.
- allow_reduction_top (bool): Select to take into account a reduction of the effective wall height due to the
thickness of the supporting floor at the top of the wall. Default value True.
- tolerance (float): Tolerance value for determining whether wall coordinates are constant in a direction.
Default value is 0.001.
Output:
- Returns a list of piers, containing relevant coordinates, positioning and workplane for further
processing.
"""
def my_sort(my_list):
""" Sorts a list with floats and stores their corresponding indices."""
sorted_list = [my_list[0]]
index_list = [0]
for outer_index, outer_item in enumerate(my_list):
if outer_index != 0:
if fem_smaller(outer_item, sorted_list[0]):
sorted_list.insert(0, outer_item)
index_list.insert(0, outer_index)
elif fem_greater(outer_item, sorted_list[-1]):
sorted_list.append(outer_item)
index_list.append(outer_index)
else:
for inner_index, inner_item in enumerate(sorted_list):
if fem_smaller(inner_item, outer_item) and \
fem_smaller(outer_item, sorted_list[inner_index + 1]):
sorted_list.insert(inner_index + 1, outer_item)
index_list.insert(inner_index + 1, outer_index)
break
return sorted_list, index_list
def copy_points(surface):
""" Create list of copies of points on the contiour of the requested surface and its openings."""
# Deepcopy points on contour of surface
copied_points = [deepcopy(surface.contour.get_points())]
# Add deepcopies of points on contour of openings in the surface
for opening in surface.openings:
copied_points.append(deepcopy(opening.get_points()))
# Return list
return copied_points
piers = []
for wall in walls:
# Determine the convenient work direction
# Assume the worst, until proven differently
x_is_constant = True
y_is_constant = True
z_is_constant = True
# Collect points
points = copy_points(wall)
for i in range(0, len(points[0]) - 1):
if fem_greater(abs(points[0][i][0] - points[0][i+1][0]), tolerance):
x_is_constant = False
if fem_greater(abs(points[0][i][1] - points[0][i+1][1]), tolerance):
y_is_constant = False
if fem_greater(abs(points[0][i][2] - points[0][i+1][2]), tolerance):
z_is_constant = False
# Check for nonsensical cases
if z_is_constant:
project.write_log(
f"WARNING: z-coordinates of {wall.name} are constant. It may not act as a pier.")
if x_is_constant and y_is_constant:
project.write_log(
f"WARNING: Coordinates of {wall.name} in both x- and y-direction are constant. "
f"It may not act as a pier.")
if fem_normal_vector(points[0])[2] != 0.0:
project.write_log(
f"WARNING: {wall.name} appears to be inclined. It's contributions will not be taken into account.")
# creating a list of all opening points for wall in consideration
opening_points_list = []
for opening in wall.openings:
for point in opening.get_points():
opening_points_list.append(point)
# get four corner points of the wall in consideration, min and max.
if y_is_constant:
# For the case when wall is along x axis, get x_min, x_max, z_min, and z_max
x_coord_list = []
z_coord_list = []
for item in wall.contour.get_points():
x_coord_list.append(item[0])
z_coord_list.append(item[2])
x_min = min(x_coord_list)
x_max = max(x_coord_list)
z_min = min(z_coord_list)
z_max = max(z_coord_list)
elif x_is_constant:
# write code for the case when wall is along y axis, get y_min, y_max, z_min and z_max
y_coord_list = []
z_coord_list = []
for item in wall.contour.get_points():
y_coord_list.append(item[1])
z_coord_list.append(item[2])
y_min = min(y_coord_list)
y_max = max(y_coord_list)
z_min = min(z_coord_list)
z_max = max(z_coord_list)
# check if any of the openings is touching the wall corners, initialize boolean 'wall_corners' to False
wall_corners = False
if y_is_constant:
for opening in wall.openings:
for i in range(len(opening.get_points())):
if opening.get_points()[i][0] in list([x_min, x_max]) and \
opening.get_points()[i][2] in list([z_min, z_max]):
wall_corners = True
elif x_is_constant:
for opening in wall.openings:
for i in range(len(opening.get_points())):
if opening.get_points()[i][1] in list([y_min, y_max]) and \
opening.get_points()[i][2] in list([z_min, z_max]):
wall_corners = True
# This if else will remove nodes of 'wall openings' from 'points[0]'
if not wall_corners:
if len(points[0]) == 4:
pass
else:
# collecting all points of openings in a list
opening_points_list = []
for opening in wall.openings:
for point in opening.get_points():
opening_points_list.append(point)
# taking intersection of wall points and opening points
intersection_list = [value for value in wall.contour.get_points() if value in opening_points_list]
# removing the items in intersection list from points[0], this will leave only 4 nodes in points[0]
# and the for loop after this one will execute properly.
for item in intersection_list:
points[0].remove(item)
# Code to handle the situation when openings are touching the wall corners, for example the situation in
# object 917A, for the washing area west side wall.
# if wall_corners is True, it means that there's an opening that is sharing one of its nodes with the corner of
# our wall. Then we can't remove this opening node from points[0]
else:
openings_inclusion = []
if y_is_constant:
# add code for walls that are in xz plane
for opening in wall.openings:
for i in range(len(opening.get_points())):
if opening.get_points()[i][0] in list([x_min, x_max]) and \
opening.get_points()[i][2] in list([z_min, z_max]):
openings_inclusion.append(opening.get_points()[i])
elif x_is_constant:
# add code for walls that are in yz plane
for opening in wall.openings:
for i in range(len(opening.get_points())):
if opening.get_points()[i][1] in list([y_min, y_max]) and \
opening.get_points()[i][2] in list([z_min, z_max]):
openings_inclusion.append(opening.get_points()[i])
intersection_list = [value for value in wall.contour.get_points() if value in opening_points_list \
if value not in openings_inclusion]
# removing points that are in 'intersection_list' but not in 'openings_inclusion'
for item in intersection_list:
points[0].remove(item)
if len(points[0]) == 4:
if not x_is_constant:
workplane = 'xz'
x_or_y = 0
elif not y_is_constant:
workplane = 'yz'
x_or_y = 1
sort_xy, index_xy = my_sort(
[points[0][0][x_or_y], points[0][1][x_or_y], points[0][2][x_or_y], points[0][3][x_or_y]])
sort_z, index_z = my_sort(
[points[0][0][2], points[0][1][2], points[0][2][2], points[0][3][2]])
# Assign the status of each point of the wall
for pointnr, point in enumerate(points[0]):
if (pointnr == index_xy[0] or pointnr == index_xy[1]) and (
pointnr == index_z[0] or pointnr == index_z[1]):
lower_left_nr = pointnr
lower_left_xyz = point
elif (pointnr == index_xy[2] or pointnr == index_xy[3]) and (
pointnr == index_z[0] or pointnr == index_z[1]):
lower_right_nr = pointnr
lower_right_xyz = point
elif (pointnr == index_xy[0] or pointnr == index_xy[1]) and (
pointnr == index_z[2] or pointnr == index_z[3]):
upper_left_nr = pointnr
upper_left_xyz = point
elif (pointnr == index_xy[2] or pointnr == index_xy[3]) and (
pointnr == index_z[2] or pointnr == index_z[3]):
upper_right_nr = pointnr
upper_right_xyz = point
try:
_ = lower_left_nr
_ = lower_right_nr
_ = upper_left_nr
_ = upper_right_nr
except NameError:
project.write_log(
f"WARNING: The shape of {wall.name} is not supported."
f"Its contributions are not taken into account.")
# Reduce the effective height of the wall based on the connected floor thickness
if allow_reduction_bottom:
deduct_low = False
for floor in project.collections.floors:
deduction_ll = False
deduction_lr = False
floor_points = copy_points(floor)
for point in floor_points[0]:
if lower_left_xyz == point:
deduction_ll = True
break
if deduction_ll:
for point in floor_points[0]:
if lower_right_xyz == point:
deduction_lr = True
if deduction_ll and deduction_lr:
if 'PLANKEN' in floor.name:
thickness_found = float(floor.name.split('PLANKEN-')[1].split('-')[0])
elif 'PLATEN' in floor.name:
thickness_found = float(floor.name.split('PLATEN-')[1].split('-')[0])
else:
thickness_found = floor.geometry.geometry_model.thickness
if deduct_low:
deduct_thickness_low = max(deduct_thickness_low, thickness_found)
else:
deduct_low = True
deduct_thickness_low = thickness_found
if deduct_low:
lower_left_xyz[2] += deduct_thickness_low/2
lower_right_xyz[2] += deduct_thickness_low/2
if allow_reduction_top:
deduct_up = False
for floor in project.collections.floors:
deduction_ul = False
deduction_ur = False
floor_points = copy_points(floor)
for point in floor_points[0]:
if upper_left_xyz == point:
deduction_ul = True
break
if deduction_ul:
for point in floor_points[0]:
if upper_right_xyz == point:
deduction_ur = True
if deduction_ul and deduction_ur:
if 'PLANKEN' in floor.name:
thickness_found = float(floor.name.split('PLANKEN-')[1].split('-')[0])
elif 'PLATEN' in floor.name:
thickness_found = float(floor.name.split('PLATEN-')[1].split('-')[0])
else:
thickness_found = floor.geometry.geometry_model.thickness
if deduct_up:
deduct_thickness_up = max(deduct_thickness_up, thickness_found)
else:
deduct_up = True
deduct_thickness_up = thickness_found
if deduct_up:
upper_left_xyz[2] -= deduct_thickness_up/2
upper_right_xyz[2] -= deduct_thickness_up/2
# Assign work coordinates for walls without openings
if len(points) == 1:
piers.append({
'coordinates': {
'RHS': {
'LLH': lower_left_xyz,
'LLL': lower_left_xyz,
'LRH': lower_right_xyz,
'LRL': lower_right_xyz,
'ULH': upper_left_xyz,
'ULL': upper_left_xyz,
'URH': upper_right_xyz,
'URL': upper_right_xyz},
'LHS': {
'LLH': lower_left_xyz,
'LLL': lower_left_xyz,
'LRH': lower_right_xyz,
'LRL': lower_right_xyz,
'ULH': upper_left_xyz,
'ULL': upper_left_xyz,
'URH': upper_right_xyz,
'URL': upper_right_xyz}},
'parent wall': wall,
'workplane': workplane,
'side': None})
else:
# Sort coordinates of openings in wall
list_min_xy = []
list_max_xy = []
for pointlist in range(1, len(points)):
all_xy = []
for point in points[pointlist]:
all_xy.append(point[x_or_y])
list_min_xy.append(min(all_xy))
list_max_xy.append(max(all_xy))
sort_min_xy, index_min_xy = my_sort(list_min_xy)
sort_max_xy, index_max_xy = my_sort(list_max_xy)
# Check for overlapping openings
for i in range(len(index_min_xy)):
if index_min_xy[i] == index_max_xy[i]:
if i != len(index_min_xy) - 1:
if index_max_xy[i] < index_min_xy[i + 1]:
pass
else:
project.write_log(
f"WARNING: Overlap between openings detected in x-direction. This case is not supported. "
f"The contributions of {wall.name} are not taken into account.")
for openingnr in range(0,len(points) - 1):
if openingnr == 0:
side = 'left'
else:
side = 'middle'
openingnr_rhs = index_min_xy[openingnr] + 1
sort_xy, index_xy = my_sort(
[points[openingnr_rhs][0][x_or_y], points[openingnr_rhs][1][x_or_y],
points[openingnr_rhs][2][x_or_y], points[openingnr_rhs][3][x_or_y]])
sort_z, index_z = my_sort(
[points[openingnr_rhs][0][2], points[openingnr_rhs][1][2],
points[openingnr_rhs][2][2], points[openingnr_rhs][3][2]])
# Assign the status of each point of the opening
for pointnr, point in enumerate(points[openingnr_rhs]):
if (pointnr == index_xy[0] or pointnr == index_xy[1]) and (
pointnr == index_z[0] or pointnr == index_z[1]):
lower_left_opening_right_nr = pointnr
lower_left_opening_right_xyz = point
elif (pointnr == index_xy[2] or pointnr == index_xy[3]) and (
pointnr == index_z[0] or pointnr == index_z[1]):
lower_right_opening_right_nr = pointnr
lower_right_opening_right_xyz = point
elif (pointnr == index_xy[0] or pointnr == index_xy[1]) and (
pointnr == index_z[2] or pointnr == index_z[3]):
upper_left_opening_right_nr = pointnr
upper_left_opening_right_xyz = point
elif (pointnr == index_xy[2] or pointnr == index_xy[3]) and (
pointnr == index_z[2] or pointnr == index_z[3]):
upper_right_opening_right_nr = pointnr
upper_right_opening_right_xyz = point
try:
_ = lower_left_opening_right_nr
_ = lower_right_opening_right_nr
_ = upper_left_opening_right_nr
_ = upper_right_opening_right_nr
except NameError:
project.write_log(
f"WARNING: the shape of {wall.name} is not supported. "
f"Its contributions are not taken into account.")
if side == 'middle':
openingnr_lhs = index_min_xy[openingnr - 1] + 1
sort_xy, index_xy = my_sort(
[points[openingnr_lhs][0][x_or_y], points[openingnr_lhs][1][x_or_y],
points[openingnr_lhs][2][x_or_y], points[openingnr_lhs][3][x_or_y]])
sort_z, index_z = my_sort(
[points[openingnr_lhs][0][2], points[openingnr_lhs][1][2],
points[openingnr_lhs][2][2], points[openingnr_lhs][3][2]])
# Assign the status of each point of the opening
for pointnr, point in enumerate(points[openingnr_lhs]):
if (pointnr == index_xy[0] or pointnr == index_xy[1]) and (
pointnr == index_z[0] or pointnr == index_z[1]):
lower_left_opening_left_nr = pointnr
lower_left_opening_left_xyz = point
elif (pointnr == index_xy[2] or pointnr == index_xy[3]) and (
pointnr == index_z[0] or pointnr == index_z[1]):
lower_right_opening_left_nr = pointnr
lower_right_opening_left_xyz = point
elif (pointnr == index_xy[0] or pointnr == index_xy[1]) and (
pointnr == index_z[2] or pointnr == index_z[3]):
upper_left_opening_left_nr = pointnr
upper_left_opening_left_xyz = point
elif (pointnr == index_xy[2] or pointnr == index_xy[3]) and (
pointnr == index_z[2] or pointnr == index_z[3]):
upper_right_opening_left_nr = pointnr
upper_right_opening_left_xyz = point
try:
_ = lower_left_opening_left_nr
_ = lower_right_opening_left_nr
_ = upper_left_opening_left_nr
_ = upper_right_opening_left_nr
except NameError:
project.write_log(
f"WARNING: the shape of {wall.name} is not supported. "
f"Its contributions are not taken into account.")
lower_left_high_left_xyz = [lower_left_xyz[0],lower_left_xyz[1],lower_left_opening_left_xyz[2]]
lower_right_high_left_xyz = [lower_left_xyz[0], lower_left_xyz[1], lower_left_opening_left_xyz[2]]
upper_left_low_left_xyz = [upper_left_xyz[0], upper_left_xyz[1], upper_left_opening_left_xyz[2]]
upper_right_low_left_xyz = [upper_left_xyz[0], upper_left_xyz[1], upper_left_opening_left_xyz[2]]
# Find interpolated z-coordinates for upper_right_high and lower_right_low
z_upper_right_high = (upper_right_xyz[x_or_y] - upper_left_opening_right_xyz[x_or_y]) / \
(upper_right_xyz[x_or_y] - upper_left_xyz[x_or_y]) *\
(upper_right_xyz[2] - upper_left_xyz[2]) + upper_left_xyz[2]
z_lower_right_low = (lower_right_xyz[x_or_y] - lower_left_opening_right_xyz[x_or_y]) / \
(lower_right_xyz[x_or_y] - lower_left_xyz[x_or_y]) * \
(lower_right_xyz[2] - lower_left_xyz[2]) + lower_left_xyz[2]
# Find points on pier
lower_left_high_right_xyz = [
lower_left_xyz[0], lower_left_xyz[1], lower_left_opening_right_xyz[2]]
lower_left_low_xyz = [
lower_left_xyz[0], lower_left_xyz[1], lower_left_xyz[2]]
lower_right_high_right_xyz = [
lower_left_opening_right_xyz[0], lower_left_opening_right_xyz[1],
lower_left_opening_right_xyz[2]]
lower_right_low_xyz = [
lower_left_opening_right_xyz[0], lower_left_opening_right_xyz[1], z_lower_right_low]
upper_left_high_xyz = [
upper_left_xyz[0], upper_left_xyz[1], upper_left_xyz[2]]
upper_left_low_right_xyz = [
upper_left_xyz[0], upper_left_xyz[1], upper_left_opening_right_xyz[2]]
upper_right_high_xyz = [
upper_left_opening_right_xyz[0], upper_left_opening_right_xyz[1], z_upper_right_high]
upper_right_low_right_xyz = [
upper_left_opening_right_xyz[0], upper_left_opening_right_xyz[1],
upper_left_opening_right_xyz[2]]
# Save pier
if side == 'left':
# The loop below will ensure that there are no line piers (i.e. piers with no thickness and for
# which LLH=LRH, LLL=LRL, ULH=URH and ULL=URL). This situation mighr arise when there is an opening
# flush with a wall edge.
if lower_left_high_right_xyz != lower_right_high_right_xyz and \
lower_left_low_xyz != lower_right_low_xyz and \
upper_left_high_xyz != upper_right_high_xyz and \
upper_left_low_right_xyz != upper_right_low_right_xyz:
piers.append({
'coordinates': {
'RHS': {
'LLH': lower_left_high_right_xyz,
'LLL': lower_left_low_xyz,
'LRH': lower_right_high_right_xyz,
'LRL': lower_right_low_xyz,
'ULH': upper_left_high_xyz,
'ULL': upper_left_low_right_xyz,
'URH': upper_right_high_xyz,
'URL': upper_right_low_right_xyz},
'LHS': {
'LLH': None,
'LLL': None,
'LRH': None,
'LRL': None,
'ULH': None,
'ULL': None,
'URH': None,
'URL': None}},
'parent wall': wall,
'workplane': workplane,
'side': side})
elif side == 'middle':
# the loop belowwill ensure that there are no line piers (i.e. piers with no thickness and for which
# LLH=LRH, LLL=LRL, ULH=URH and ULL=URL). This situation mighr arise when there is an opening flush
# with a wall edge.
if lower_left_high_right_xyz != lower_right_high_right_xyz and \
lower_left_low_xyz != lower_right_low_xyz and \
upper_left_high_xyz != upper_right_high_xyz and \
upper_left_low_right_xyz != upper_right_low_right_xyz:
piers.append({
'coordinates': {
'RHS': {
'LLH': lower_left_high_right_xyz,
'LLL': lower_left_low_xyz,
'LRH': lower_right_high_right_xyz,
'LRL': lower_right_low_xyz,
'ULH': upper_left_high_xyz,
'ULL': upper_left_low_right_xyz,
'URH': upper_right_high_xyz,
'URL': upper_right_low_right_xyz},
'LHS': {
'LLH': lower_left_high_left_xyz,
'LLL': lower_left_low_xyz,
'LRH': lower_right_high_left_xyz,
'LRL': lower_right_low_xyz,
'ULH': upper_left_high_xyz,
'ULL': upper_left_low_left_xyz,
'URH': upper_right_high_xyz,
'URL': upper_right_low_left_xyz}},
'parent wall': wall,
'workplane': workplane,
'side': side})
# Update geometry of "remainder" of wall
z_upper_left_high = (upper_right_xyz[x_or_y] - upper_right_opening_right_xyz[x_or_y]) / \
(upper_right_xyz[x_or_y] - upper_left_xyz[x_or_y]) *\
(upper_right_xyz[2] - upper_left_xyz[2]) + upper_left_xyz[2]
z_lower_left_high = (lower_right_xyz[x_or_y] - lower_right_opening_right_xyz[x_or_y]) / \
(lower_right_xyz[x_or_y] - lower_left_xyz[x_or_y]) * \
(lower_right_xyz[2] - lower_left_xyz[2]) + lower_left_xyz[2]
lower_left_xyz = [
lower_right_opening_right_xyz[0], lower_right_opening_right_xyz[1], z_lower_left_high]
upper_left_xyz = [
upper_right_opening_right_xyz[0], upper_right_opening_right_xyz[1], z_upper_left_high]
# The remaining pier has to be approached from the right-side of the looped opening,
# thus uses a different approach:
# Find points on pier
lower_left_high_right_xyz = [lower_left_xyz[0], lower_left_xyz[1], lower_right_opening_right_xyz[2]]
lower_left_low_xyz = [lower_left_xyz[0], lower_left_xyz[1], lower_left_xyz[2]]
lower_right_high_right_xyz = [lower_right_xyz[0], lower_right_xyz[1], lower_right_opening_right_xyz[2]]
lower_right_low_xyz = [lower_right_xyz[0], lower_right_xyz[1], lower_right_xyz[2]]
upper_left_high_xyz = [upper_left_xyz[0], upper_left_xyz[1], upper_left_xyz[2]]
upper_left_low_right_xyz = [upper_left_xyz[0], upper_left_xyz[1], upper_right_opening_right_xyz[2]]
upper_right_high_xyz = [upper_right_xyz[0], upper_right_xyz[1], upper_right_xyz[2]]
upper_right_low_right_xyz = [upper_right_xyz[0], upper_right_xyz[1], upper_right_opening_right_xyz[2]]
# the loop belowwill ensure that there are no line piers (i.e. piers with no thickness and for which
# LLH=LRH, LLL=LRL, ULH=URH and ULL=URL). This situation mighr arise when there is an opening flush with
# a wall edge.
if lower_left_high_right_xyz != lower_right_high_right_xyz and \
lower_left_low_xyz != lower_right_low_xyz and \
upper_left_high_xyz != upper_right_high_xyz and \
upper_left_low_right_xyz != upper_right_low_right_xyz:
piers.append({
'coordinates': {
'RHS': {
'LLH': None,
'LLL': None,
'LRH': None,
'LRL': None,
'ULH': None,
'ULL': None,
'URH': None,
'URL': None},
'LHS': {
'LLH': lower_left_high_right_xyz,
'LLL': lower_left_low_xyz,
'LRH': lower_right_high_right_xyz,
'LRL': lower_right_low_xyz,
'ULH': upper_left_high_xyz,
'ULL': upper_left_low_right_xyz,
'URH': upper_right_high_xyz,
'URL': upper_right_low_right_xyz}},
'parent wall': wall,
'workplane': workplane,
'side': 'right'})
else:
project.write_log(
"The function failed to identify piers because the functionality is limited to four point openings. "
"The function works, but to get results please offset all openings from wall edges.")
return piers
### ===================================================================================================================
### 3. End of script
### ===================================================================================================================