Source code for viiapackage.geometry.viia_fstrips

### ===================================================================================================================
###   Functionality for information and perform geometrical operations on foundation strips
### ===================================================================================================================
# Copyright ©VIIA 2024

### ===================================================================================================================
###    1. Import modules
### ===================================================================================================================

# General imports
from __future__ import annotations
from typing import TYPE_CHECKING, List, Optional
import itertools
from copy import deepcopy
import warnings

# References for functions in the datafusr_py_base package
from datafusr_py_base.deprecation import rename_argument

# References for functions and classes in the rhdhv_fem package
from rhdhv_fem.fem_math import fem_parallel_vectors, fem_distance_point_to_line, fem_intersection_of_two_line_segments,\
    fem_intersection_of_two_lines, fem_point_on_contour_surface, fem_vector_2_points, fem_point_in_surface, \
    fem_mid_point, fem_point_in_circle, fem_distance_coordinates, fem_points_in_line, fem_unit_vector, \
    fem_cross_product_vector, fem_compare_coordinates, fem_min_max_point_list, fem_longest_distance, \
    fem_ordered_coordinates_list, fem_dot_product_vector, fem_compare_values, fem_point_on_infinite_line, \
    fem_compare_vectors, fem_corner_points_surface, fem_point_on_line
from rhdhv_fem.shape_geometries import Line
from rhdhv_fem.shapes import Fstrip, Surfaces, Wall
from rhdhv_fem.fem_tools import fem_find_object

# References for functions and classes in the viiaPackage
if TYPE_CHECKING:
    from viiapackage.viiaStatus import ViiaProject
from viiapackage.shape_operations import viia_get_wall_horizontal_direction


### ===================================================================================================================
###    2. Get fstrip properties
### ===================================================================================================================

[docs]def get_fstrip_direction(fstrip: Fstrip) -> List[float]: """ Determines the direction by either looking in the metadata for the supported wall, or by determining the direction in which the longest lines are.""" # First try to use the supported wall to obtain direction supported_wall = fstrip.meta_data.get("supported_wall") if fstrip.meta_data else None if supported_wall: bottom_line = supported_wall.get_bottom_edges()[0] return bottom_line.get_direction().vector # If no supported wall is found, determine direction by longest lines warnings.warn( f"WARNING: Fstrip direction for {fstrip.name} is guessed based on direction with the longest lines. Please " f"verify that the direction is correct.") length_per_direction = [] for line in fstrip.get_lines(): existing_direction = False for direction in length_per_direction: if fem_parallel_vectors(line.get_direction().vector, direction[0]): direction[1] += line.get_length() existing_direction = True if not existing_direction: length_per_direction.append([line.get_direction().vector, line.get_length()]) return sorted(length_per_direction, key=lambda x: x[1])[-1][0]
[docs]def get_fstrip_average_length(fstrip: Fstrip): """ In case of trapezoid fstrip, this method determines average length for correct area calculation by geo.""" def _is_connecting_line(line, line_list) -> bool: """ Function that determines if 'line' is connected to any lines in 'line_list'""" for existing_line in line_list: for exiting_point in existing_line.get_points(): for point in line.get_points(): if fem_compare_coordinates(exiting_point, point): return True return False direction = get_fstrip_direction(fstrip) longitudinal_lines = fstrip.get_lines_in_direction(direction) lines_side_1, lines_side_2 = [], [] for line in longitudinal_lines: # Add to first list if both lists are still empty if not lines_side_1 and not lines_side_2: lines_side_1.append(line) continue # Check if current line is connected to any lines in lines_side_1, if so add it if _is_connecting_line(line, lines_side_1): lines_side_1.append(line) continue # Check if current line is connected to any lines in lines_side_2, if so add it if _is_connecting_line(line, lines_side_2): lines_side_2.append(line) continue # Add it to lines_side_2 when it is not added to either list lines_side_2.append(line) length_side_1 = 0 for line in lines_side_1: length_side_1 += line.get_length() length_side_2 = 0 for line in lines_side_2: length_side_2 += line.get_length() return (length_side_1 + length_side_2) / 2
[docs]def get_fstrip_width(fstrip: Fstrip): """ Function to obtain the width of a fstrip. If no direction is specified, it will be automatically determined.""" direction = get_fstrip_direction(fstrip) perpendicular_distance_list = [] for line_1, line_2 in itertools.combinations(fstrip.get_lines_in_direction(direction), 2): perpendicular_distance_list.append(fem_distance_point_to_line(line_1.get_points()[0], line_2.get_points())[0]) return sorted(perpendicular_distance_list)[-1]
[docs]def get_fstrip_placement_point_for_geo(fstrip: Fstrip): """ Function to determine the 2D placement point of the fstrip. In general, this is the middle point of the bottom line of the supported wall. If no information about this wall is found in the meta-data of the strip, the average x- and y-coordinates are returned. The output is given as a list.""" supported_wall = fstrip.meta_data.get("supported_wall") if fstrip.meta_data else None if isinstance(supported_wall, str): supported_wall = fstrip.project.find(description=supported_wall, collection='walls') if not supported_wall: # Return average x- and y-coordinate if no information about a wall is found min_max = fem_min_max_point_list(fstrip.get_points()) return [(min_max['x-max'] + min_max['x-min']) / 2.0, (min_max['y-max'] + min_max['y-min']) / 2.0] bottom_points = [] for line in supported_wall.get_bottom_edges(): for point in line.get_points(): bottom_points.append(point) extreme_points = fem_longest_distance(bottom_points) mid_point = fem_mid_point(*extreme_points) return mid_point[:-1]
### =================================================================================================================== ### 3. Adjust fstrips ### ===================================================================================================================
[docs]@rename_argument(old='fstrip_objects', new='fstrips', since='69.3.1', until='70.0', package='viiapackage') def viia_adjust_fstrips( project: ViiaProject, fstrips: Optional[List[Fstrip]] = None, fstrip_directions: Optional[List[List[float]]] = None, fwalls: Optional[List[Wall]] = None) -> None: """ Function to automatically adjust the geometry of the fstrip in relation to the adjacent strip. A list of fstrips should be provided, along with a list of corresponding directions. Multiple types of connections can be adjusted, for instance T-connections (in which the upper part of the "T" is continuous), perpendicular directions and corner connections with an arbitrary angle. More complex connections where multiple fstrips meet may raise errors, if no suitable adjustments can be made. The function ends with a check in which an error is raised if any overlaps still exist. Input: - project (obj): VIIA project object containing collections of fem objects and project variables. - fstrips (list of obj): List of object references of fstrips. Default value None, in which case all fstrips in the project are applied for the adjustment for the fstrips. - fstrip_directions (list of list of float): List of corresponding directions for each fstrip in fstrips. Should have same length as the number of fstrip objects. The directions are to be provided as list of 3 floats. Default value None, in which case it is tried to retrieve the direction from the supporting wall or foundation wall. If this fails an error is raised, in which case the directions should be provided manually. - fwalls (list of obj): List of wall objects that have been created for the foundation. These are used to connect to the foundation strips. Output: - Function adjusts the geometry of the fstrips and connects them as requested for the implemented connection options. The function does not return anything. """ def _get_longitudinal_fstrip_points( lines: List[Line], fstrip_name: str, connecting_fstrip_name: Optional[str] = None) -> List[List[float]]: """ This sub-function collects all longitudinal oriented lines of the foundation strip, then it selects the outer ones. The start and endpoints of these lines are used in the further process.""" line_groups = [[lines[0]]] for j in range(1, len(lines)): for k in range(len(line_groups)): if fem_point_on_infinite_line( *line_groups[k][0].get_points(), lines[j].node_start.coordinates, precision=project.check_precision): line_groups[k].append(lines[j]) break else: line_groups.append([lines[j]]) if len(line_groups) > 2: # Filter the outer lines absolute_distances = [] distances = [ fem_distance_point_to_line(point=line_groups[0][0].node_start.coordinates, line=line_group[0]) for j, line_group in enumerate(line_groups) if j != 0] for j, line_group_distance in enumerate(distances): if not fem_compare_vectors( vector1=distances[0][1], vector2=line_group_distance[1], unit_vectors=True, precision=project.check_precision): absolute_distances.append(-1 * distances[j][0]) else: absolute_distances.append(distances[j][0]) index_min = 0 if min(absolute_distances) < 0.0: index_min = absolute_distances.index(min(absolute_distances)) + 1 index_max = 0 if max(absolute_distances) > 0.0: index_max = absolute_distances.index(max(absolute_distances)) + 1 line_groups = [line_groups[index_min], line_groups[index_max]] # Convert to two lists with start and end point of the longitudinal outer lines points_group = [] for line_group in line_groups: nodes = [] for _line in line_group: for node in _line.get_nodes(): if node not in nodes: nodes.append(node) else: nodes.remove(node) if len(nodes) != 2: text = fstrip_name if connecting_fstrip_name: text += f' with {connecting_fstrip_name}' raise NotImplementedError( f"ERROR: The longitudinal lines of the fstrip are not continuous. Issue is caused while connecting " f"{text}. This is not yet implemented. Please report your specific situation to the automation " f"team!") points_group.append([node.coordinates for node in nodes]) return points_group # Input check for the fstrips if fstrips is None: fstrips = project.collections.fstrips # Check if the strip directions are provided if fstrip_directions is None: fstrip_directions = [] for fstrip in fstrips: if fstrip.meta_data is None: raise ValueError( f"ERROR: The direction for the foundation strip '{fstrip.name}' could not be retrieved. You " f"can provide the direction manually.") wall = fstrip.meta_data.get('supported_wall') if wall is None: wall = fstrip.meta_data.get('foundation_wall') if wall is None: raise ValueError( f"ERROR: The direction for the foundation strip '{fstrip.name}' could not be retrieved. You " f"can provide the direction manually.") fstrip_directions.append(viia_get_wall_horizontal_direction(wall=wall).vector) # Check if the input is all foundation strips if not all([isinstance(fstrip, Fstrip) for fstrip in fstrips]): raise ValueError( "ERROR: The input for the fstrips in viia_adjust_fstrips function should be all instances of Fstrip class. " "Please correct your input.") # Check length of input argument lists if len(fstrips) != len(fstrip_directions): raise ValueError("ERROR: The viia_adjust_fstrips function expects two lists of equal length to work properly.") # Initiate list to keep track of adjusted connections checked_connections = [] # Loop over all fstrips for i, fstrip in enumerate(fstrips): fstrip_direction = fstrip_directions[i] connecting_fstrips = _identify_surface_connections(fstrip, fstrips) fstrip_longitudinal_lines = fstrip.get_lines_in_direction(fstrip_direction) fstrip_longitudinal_grouped_points = _get_longitudinal_fstrip_points( lines=fstrip_longitudinal_lines, fstrip_name=fstrip.name) # Loop over all connections to this fstrip for connecting_fstrip in connecting_fstrips: # If this connection has already been checked, skip it for entry in checked_connections: if fstrip.id in entry and connecting_fstrip.id in entry: continue # Update connections, may have changed due to previous adjustment connecting_fstrips = _identify_surface_connections(fstrip, fstrips) # Select contour lines in the direction of the strip connecting_fstrip_direction = fstrip_directions[fstrips.index(connecting_fstrip)] connecting_fstrip_longitudinal_lines = connecting_fstrip.get_lines_in_direction(connecting_fstrip_direction) # Filter for the points on the longitudinal lines of the connecting strip connecting_fstrip_longitudinal_points = _get_longitudinal_fstrip_points( lines=connecting_fstrip_longitudinal_lines, fstrip_name=fstrip.name, connecting_fstrip_name=connecting_fstrip.name) # Initiate the required parameters to adjust the corner outer_lines = [None, None] inner_corner_point = None # Loop over the longitudinal lines of one fstrip, and see if they have intersections with the longitudinal # lines of the other fstrip. In this way, the inner and outer corner points can be identified. for fstrip_longitudinal_points in fstrip_longitudinal_grouped_points: intersection_1 = fem_intersection_of_two_line_segments( line1=fstrip_longitudinal_points, line2=connecting_fstrip_longitudinal_points[0], return_intersection=True) intersection_2 = fem_intersection_of_two_line_segments( line1=fstrip_longitudinal_points, line2=connecting_fstrip_longitudinal_points[1], return_intersection=True) if intersection_1 != 'NoIntersection' and intersection_2 != 'NoIntersection': # This is an intermediate connection _adjust_intermediate_connection(project, fstrip, connecting_fstrip, intersection_1, intersection_2) break elif intersection_1 != 'NoIntersection': # This is an inner corner point inner_corner_point = intersection_1 outer_lines[1] = connecting_fstrip_longitudinal_points[1] elif intersection_2 != 'NoIntersection': # This is also an inner corner point inner_corner_point = intersection_2 outer_lines[1] = connecting_fstrip_longitudinal_points[0] else: # This is an outer corner outer_lines[0] = fstrip_longitudinal_points # Only proceed if this is not an intermediate connection if not inner_corner_point: checked_connections.append([fstrip.id, connecting_fstrip.id]) continue # Only proceed if the fstrips are not parallel if len(inner_corner_point) == 2: checked_connections.append([fstrip.id, connecting_fstrip.id]) project.write_log( f"WARNING: The fstrips {fstrip.name} and {connecting_fstrip.name} are parallel and cannot be " f"adjusted.") continue # Only proceed if outer_lines[0] exists. It may not exist in cases when there are more than two lines in the # contour that are not longitudinal if outer_lines[0] is None: break # Find the outer corner point fstrip_outer_line = outer_lines[0] connecting_fstrip_outer_line = outer_lines[1] outer_corner_point = fem_intersection_of_two_lines( coordinate1=fstrip_outer_line[0], vector1=fem_vector_2_points(fstrip_outer_line[0], fstrip_outer_line[1]), coordinate2=connecting_fstrip_outer_line[0], vector2=fem_vector_2_points(connecting_fstrip_outer_line[0], connecting_fstrip_outer_line[1])) if outer_corner_point == 'Parallel': checked_connections.append([fstrip.id, connecting_fstrip.id]) project.write_log( f"WARNING: The fstrips {fstrip.name} and {connecting_fstrip.name} are parallel and cannot be " f"adjusted.") continue _adjust_corner_connection(project, fstrip, connecting_fstrip, inner_corner_point, outer_corner_point) checked_connections.append([fstrip.id, connecting_fstrip.id]) # Check if the z-axis are still pointing down for fstrip in fstrips: if not fem_compare_coordinates( coordinate1=fstrip.z_axis_direction().vector, coordinate2=[0, 0, -1], precision=project.check_precision): fstrip.flip() # Remove redundant internal lines if fwalls: for wall in fwalls: if wall is None: continue for fstrip in fstrips: wall.connect_shape(shape=fstrip) fstrip.connect_shape(shape=wall) for fstrip in fstrips: if fstrip.internal_lines: _viia_remove_redundant_internal_geometries(project=project, fstrip=fstrip) # When finished adjusting, check if no overlaps still exist if _viia_check_overlapping_surfaces(fstrips): project.write_log( f"WARNING: Script was not successful in adjusting {len(fstrips)} Fstrips: " f"{', '.join([fs.name for fs in fstrips])}.", True) else: project.write_log(f"Successfully adjusted {len(fstrips)} Fstrips.", False)
[docs]def _identify_surface_connections(surface: Fstrip, surface_objects: List[Fstrip]): """ Function that returns a list of surface objects that intersect with the given surface.""" connecting_surfaces = [] for surface_object in surface_objects: # Skip if compared to itself if surface_object.id == surface.id: continue # Check if any of the points of the contour of the other surface are contained in this surface for point in surface_object.contour.get_points(): if fem_point_in_surface(point, surface.contour.get_points()): if not fem_point_on_contour_surface(point, surface.contour.get_points()): connecting_surfaces.append(surface_object) break return connecting_surfaces
[docs]def identify_surface_common_nodes(surface: Fstrip, surface_objects: List[Fstrip]): """ Function that return a list of surface objects that share nodes.""" node_sharing_surfaces = [] for surface_object in surface_objects: # Skip if compared to itself if surface_object.id == surface.id: continue # check if any of the points of the contour of the other surface are touching in this surface for point in surface_object.contour.get_points(): if fem_point_on_contour_surface(point, surface.contour.get_points()): node_sharing_surfaces.append(surface_object) break return node_sharing_surfaces
[docs]def _adjust_intermediate_connection( project: ViiaProject, surface: Fstrip, connecting_surface: Fstrip, intersection_1: List[float], intersection_2: List[float]): """ Function that clips 'connecting_surface' at the given intersection points with another surface.""" connecting_surface_contour_points = connecting_surface.contour.get_points() points_to_keep = [] for point in connecting_surface_contour_points: if not fem_point_in_surface(point, surface.contour.get_points()): points_to_keep.append(point) # Surface most likely already adjusted, try drawing circle around middle of intersections instead if len(points_to_keep) != 2: points_to_keep.clear() mid_point_circle = fem_mid_point(intersection_1, intersection_2) for point in connecting_surface_contour_points: if not fem_point_in_circle( point, [0, 0, 1], mid_point_circle, fem_distance_coordinates(intersection_1, intersection_2)): points_to_keep.append(point) # Adjust the contour of the connecting surface return _set_new_contour_fstrips( project=project, surface=connecting_surface, new_contour_points=points_to_keep + [intersection_1, intersection_2])
[docs]def _adjust_corner_connection( project: ViiaProject, surface: Fstrip, connecting_surface: Fstrip, inner_corner_point: List[float], outer_corner_point: List[float]): """ Function that clips both surface shapes of a connection between two surfaces in a corner.""" # Find the middle of the line between inner and outer corner point, this is the connection point connection_point = fem_mid_point(inner_corner_point, outer_corner_point) # Extra points on the contour of fstrips extra_contour_points = [inner_corner_point, outer_corner_point] # Check if the foundation strip is part of a strip foundation or a stepped foundation. If part of a strip # foundation, check if it is underneath cavity walls if all(key in surface.meta_data for key in ('strip_foundation', 'foundation_wall', 'foundation_cavity_wall')) and \ surface.meta_data['foundation_cavity_wall'] is not None and \ surface.meta_data['foundation_wall'] is not None: # Find the points on the contour of the foundation wall to keep by drawing circle around connection point wall_points_to_keep = [] corner_points = fem_corner_points_surface( surface=surface.meta_data['foundation_cavity_wall'].contour.get_points(), precision=project.check_precision) for point in corner_points: if fem_point_in_circle( point=point, normal_direction=[0, 0, 1], mid_point=connection_point, radius=fem_distance_coordinates(inner_corner_point, outer_corner_point) / 2): wall_points_to_keep.append(point) corner_points = fem_corner_points_surface( surface=surface.meta_data['foundation_wall'].contour.get_points(), precision=project.check_precision) for point in corner_points: if fem_point_in_circle( point=point, normal_direction=[0, 0, 1], mid_point=connection_point, radius=fem_distance_coordinates(inner_corner_point, outer_corner_point) / 2): wall_points_to_keep.append(point) wall_points_ordered = \ fem_ordered_coordinates_list(coordinates_list=wall_points_to_keep, reference_point=inner_corner_point) # Add wall points to the contour if line formed by the wall points is not perpendicular to the walls dot_product_wall_wall_points = \ fem_dot_product_vector( a=fem_vector_2_points(wall_points_ordered[0], wall_points_ordered[1]), b=viia_get_wall_horizontal_direction(wall=surface.meta_data['foundation_wall']).vector) # Add wall points to the contour if they do not lie in a line with corner points if not fem_points_in_line([inner_corner_point, outer_corner_point] + wall_points_ordered) and \ not fem_compare_values(dot_product_wall_wall_points, 0): extra_contour_points[1:1] = wall_points_ordered elif all(key in connecting_surface.meta_data for key in ('strip_foundation', 'foundation_wall', 'foundation_cavity_wall')) and \ connecting_surface.meta_data['foundation_cavity_wall'] is not None and \ connecting_surface.meta_data['foundation_wall'] is not None: # Find the points on the contour of the foundation wall to keep by drawing circle around connection point wall_points_to_keep = [] for point in connecting_surface.meta_data['foundation_cavity_wall'].contour.get_points(): if fem_point_in_circle( point=point, normal_direction=[0, 0, 1], mid_point=connection_point, radius=fem_distance_coordinates(inner_corner_point, outer_corner_point) / 2): wall_points_to_keep.append(point) for point in connecting_surface.meta_data['foundation_wall'].contour.get_points(): if fem_point_in_circle( point=point, normal_direction=[0, 0, 1], mid_point=connection_point, radius=fem_distance_coordinates(inner_corner_point, outer_corner_point) / 2): wall_points_to_keep.append(point) wall_points_ordered = \ fem_ordered_coordinates_list(coordinates_list=wall_points_to_keep, reference_point=inner_corner_point) # Add wall points to the contour if line formed by the wall points is not perpendicular to the walls dot_product_wall_wall_points = \ fem_dot_product_vector( a=fem_vector_2_points(wall_points_ordered[0], wall_points_ordered[1]), b=viia_get_wall_horizontal_direction(wall=connecting_surface.meta_data['foundation_wall']).vector) # Add wall points to the contour if they do not lie in a line with corner points if not fem_points_in_line( points=[inner_corner_point, outer_corner_point] + wall_points_ordered, precision=project.check_precision) and not fem_compare_values( value1=dot_product_wall_wall_points, value2=0, precision=project.check_precision): extra_contour_points[1:1] = wall_points_ordered # Find the points on the contour of the surface to keep by drawing circle around connection point surface_points_to_keep = [] for point in surface.contour.get_points(): if not fem_point_in_circle( point=point, normal_direction=[0, 0, 1], mid_point=connection_point, radius=fem_distance_coordinates(inner_corner_point, outer_corner_point) / 2): surface_points_to_keep.append(point) # Adjust the contour of the surface surface = _set_new_contour_fstrips( project=project, surface=surface, new_contour_points=surface_points_to_keep + extra_contour_points) # Find the points on the contour of the connecting surface to keep by drawing circle around connection point connecting_surface_points_to_keep = [] for point in connecting_surface.contour.get_points(): if not fem_point_in_circle( point, [0, 0, 1], connection_point, fem_distance_coordinates(inner_corner_point, outer_corner_point) / 2): connecting_surface_points_to_keep.append(point) # Adjust the contour of the connecting surface _set_new_contour_fstrips( project=project, surface=connecting_surface, new_contour_points=connecting_surface_points_to_keep + extra_contour_points) # Return the foundation strip return surface
[docs]def _viia_check_overlapping_surfaces(surface_objects: List[Surfaces]): """ Function that returns True if any overlapping surfaces are present in the list of surface objects.""" for surface in surface_objects: for other_surface in surface_objects: # Don't check against itself if surface.id == other_surface.id: continue # Loop over contour points of other surface to check if they are contained in surface for contour_point in other_surface.contour.get_points(): # Check if the contour point is in or on the surface if fem_point_in_surface(contour_point, surface.contour.get_points()): # Check if the contour point is on the contour of the surface if not fem_point_on_contour_surface(contour_point, surface.contour.get_points()): return True # If no overlaps are found, return False return False
def _set_new_contour_fstrips(project: ViiaProject, surface: Fstrip, new_contour_points: List[List[float]]) -> Fstrip: """ This function sets the new contour of the foundation strip. It removes all old internal lines first.""" new_contour = project.create_polyline(new_contour_points) if surface.internal_lines: for internal_line in reversed(surface.internal_lines): surface.remove_internal_line(line=internal_line) surface.contour = new_contour # This renews the proper internal lines return surface def _viia_remove_redundant_internal_geometries(project: ViiaProject, fstrip: Fstrip) -> None: """ This function removes the internal lines and internal points on the foundation walls and strips that were added in the creation process.""" # Remove all internal points if fstrip.internal_points: for internal_point in fstrip.internal_points: if fstrip.internal_points and internal_point in fstrip.internal_points: fstrip.remove_internal_point(internal_point) # Collect the corners of the contour of the foundation strip # Any other point that is on the contour and not part of a foundation wall will be removed corner_nodes = fem_corner_points_surface(surface=fstrip.contour.get_points(), precision=project.check_precision) for node in fstrip.contour.get_nodes(): if any([fem_compare_coordinates( coordinate1=node.coordinates, coordinate2=corner_node, precision=project.check_precision) for corner_node in corner_nodes]): continue if any([isinstance(shape, Wall) for shape in node.get_shapes()]): continue surrounding_lines = node.get_lines() if len(node.get_lines()) != 2: continue surrounding_nodes = [_node for line in surrounding_lines for _node in line.get_nodes() if _node is not node] new_line = project.create_line(point_list=surrounding_nodes) polylines = node.get_polylines() for polyline in polylines: index = polyline.lines.index(surrounding_lines[0]) polyline.lines.insert(index, new_line) for line in surrounding_lines: polyline.lines.remove(line) # Remove all internal lines without connection to a wall if fstrip.internal_lines: for internal_line in reversed(fstrip.internal_lines): shapes = internal_line.get_shapes() if not any([isinstance(sh, Wall) for sh in shapes]): fstrip.remove_internal_line(line=internal_line) # Process is finished if there are no internal lines present if not fstrip.internal_lines: return # Collect dictionary for all walls on fstrip with their connecting lines connections = {} for internal_line in fstrip.internal_lines: shapes = [shape for shape in internal_line.get_shapes() if shape is not fstrip and isinstance(shape, Wall)] if len(shapes) != 1: continue if shapes[0] not in connections: connections[shapes[0]] = [] connections[shapes[0]].append(internal_line) # For every wall the connecting lines are assessed for wall, lines in connections.items(): if len(lines) < 2: # No update required continue # Filter the nodes that are double in the list of lines underneath the foundation wall # These can be removed unless they connect to multiple walls essential_nodes = [] duplicate_nodes = [] for line in lines: for node in line.get_nodes(): if node not in essential_nodes: essential_nodes.append(node) else: duplicate_nodes.append(node) if line in fstrip.internal_lines: fstrip.remove_internal_line(line=line) for duplicate_node in duplicate_nodes: # Check if the duplicate node is not in multiple walls shapes = [shape for shape in duplicate_node.get_shapes() if shape is not fstrip] if len(shapes) < 2 and duplicate_node in essential_nodes: essential_nodes.remove(duplicate_node) # In case of multiple lines the list must be properly ordered first if len(essential_nodes) > 2: for i in range(len(essential_nodes)): if essential_nodes[i] not in duplicate_nodes: ref_node = essential_nodes[i] break else: raise RuntimeError( "ERROR: The first node of the connection could not be found. This is an unexpected situation, " "please report to the VIIA automation team.") essential_nodes = sorted( essential_nodes, key=lambda x: fem_distance_coordinates(coordinate1=x, coordinate2=ref_node.coordinates)) # Replace the lines with one single one per wall segment for i in range(1, len(essential_nodes)): new_line = project.create_line(point_list=[essential_nodes[i-1], essential_nodes[i]]) sub_lines = [] for line in lines: l1 = fem_point_on_line( point=line.node_start.coordinates, line_points=new_line.get_points(), precision=project.check_precision) l2 = fem_point_on_line( point=line.node_end.coordinates, line_points=new_line.get_points(), precision=project.check_precision) if not l1 or not l2: continue sub_lines.append(line) for j in range(len(sub_lines)): if sub_lines[j] in wall.contour.lines: index = wall.contour.lines.index(sub_lines[j]) break else: raise RuntimeError( "ERROR: The index to add the new line could not be found. This is an unexpected situation, " "please report to the VIIA automation team.") if fstrip.internal_lines: for internal_line in fstrip.internal_lines: fstrip.remove_internal_line(line=internal_line) if fstrip.internal_points: for internal_point in fstrip.internal_points: if internal_point in fstrip.internal_points: fstrip.remove_internal_point(node=internal_point) wall.contour.lines.insert(index, new_line) for line in sub_lines: wall.contour.lines.remove(line) # Add the line also as internal line on the foundation strip fstrip.add_internal_line(line=new_line) # Setting the contour will perform all checks related to the new contour wall.contour = wall.contour ### ==================================================================================================================== ### 4. Merge foundation strips (obsolete) ### ====================================================================================================================
[docs]def _viia_get_non_selfintersection_point_list(project: ViiaProject, line_list: List[Line]): """ Gets a list of points to create a contour and openings for two shapes to be merged based on a list of lines. Input: - project (obj): VIIA project object containing collections of fem objects and project variables. - line_list (List of lines): List of lines which should form the contour and openings of a shape - precision (int): Level of precision. Output: - Returns a coordinate list which should be the new contour and a list of coordinates which should be the new openings of a shape. Both lists are a list of lists of coordinates. """ def remove_points_in_line(point_list: List[List[float]]) -> Optional[List[List[float]]]: """ Function removes the middle point if three points are in line.""" points_to_remove = [] for j in range(len(point_list)-2): index_1 = j index_2 = index_1 + 1 index_3 = index_1 + 2 if fem_points_in_line(points=[point_list[index_1], point_list[index_2], point_list[index_3]]): points_to_remove.append(point_list[index_2]) if fem_points_in_line(points=[point_list[-2], point_list[-1], point_list[0]]): points_to_remove.append(point_list[-1]) if fem_points_in_line(points=[point_list[-1], point_list[0], point_list[1]]): points_to_remove.append(point_list[0]) for remove_point in points_to_remove: point_list.remove(remove_point) return point_list def find_contour(point_lists_check: List[List[List[float]]]) -> Optional[List[List[float]]]: """ Finds the contour point list out of a list of list with coordinates.""" if len(point_lists_check) == 1: return point_lists_check[0] for point_list_1 in point_lists_check: for point_list_2 in point_lists_check: if point_list_1 != point_list_2: for point in point_list_1: if fem_point_in_surface(point=point, surface=point_list_2): return point_list_2 return None if not isinstance(line_list, list): project.write_log( "ERROR: No list as input for line_list argument of _viia_get_non_selfintersection_point_list.") return if len(line_list) < 3: project.write_log("ERROR: Line list has less than 3 line, this is a not a contour.") return node_list = [] processed_lines = [] i = 0 node_list.append(line_list[0].get_startnode()) node_list.append(line_list[0].get_endnode()) processed_lines.append(line_list[0]) polylines = [] while len(line_list) != len(processed_lines): new_polyline = False if i > 10000: project.write_log("ERROR: Not able to make the ascending point list in 10000 attempts.") break for line in line_list: if line not in processed_lines: if node_list[-1] == line.get_startnode(): if node_list[0] == line.get_endnode(): # Last line processed_lines.append(line) new_polyline = True break else: node_list.append(line.get_endnode()) processed_lines.append(line) break elif node_list[-1] == line.get_endnode(): if node_list[0] == line.get_startnode(): # Last line processed_lines.append(line) new_polyline = True break else: node_list.append(line.get_startnode()) processed_lines.append(line) break if new_polyline: polylines.append(node_list) node_list = [] for line in line_list: if line not in processed_lines: node_list.append(line.get_startnode()) node_list.append(line.get_endnode()) processed_lines.append(line) break i += 1 point_lists = [] for polyline in polylines: dummy_list = [] for node in polyline: dummy_list.append(deepcopy(node.coordinates)) point_lists.append(remove_points_in_line(point_list=dummy_list)) # Check if a list with points should be the contour of an opening contour = find_contour(point_lists_check=point_lists) if contour is not None: point_lists.remove(contour) return contour, point_lists else: return None, None
[docs]def _check_common_contour_line(project: ViiaProject, line: Line, surface_1: Surfaces, surface_2: Surfaces) -> bool: """ Checks if a common contour line should be considered during making the new contour line for the merged surfaces. A contour line where there is at both sides a shape will not be considered and so False will be returned. If both shapes are at one side of the line True is returned. Input: - project (obj): VIIA project object containing collections of fem objects and project variables. - line (obj): Object reference of line (shape-geometry) which has to be checked. - surface_1 (obj): Object reference of first surface for which the check has to be performed. - surface_2 (obj): Object reference of second surface for which the check has to be performed. Output: - Returns a boolean to indicate if line should be considered. """ line_direction_vector = line.get_direction().vector surface_normal = surface_1.normal_vector() perpendicular_direction = fem_unit_vector(fem_cross_product_vector(line_direction_vector, surface_normal)) mid_point = line.get_center_point() check_point_1 = \ [mid_point[i] + 2*perpendicular_direction[i]*(10**(-1*project.rounding_precision)) for i in range(3)] if (fem_point_in_surface(point=check_point_1, surface=surface_1.contour.get_points()) and fem_point_in_surface(point=check_point_1, surface=surface_2.contour.get_points())) or \ (not fem_point_in_surface(point=check_point_1, surface=surface_1.contour.get_points()) and not fem_point_in_surface(point=check_point_1, surface=surface_2.contour.get_points())): return True return False
[docs]def _get_surfaces_to_merge(project: ViiaProject, surface_shape_names: List[str]): """ Gets surfaces that should be merged out of a static name list. Input: - project (obj): VIIA project object containing collections of fem objects and project variables. - surface_shape_names (list with str): List of names of the shapes. Output - Returns two surface shape object references that should be merged. """ surface_shape_1 = None for i, surface_shape_name in enumerate(surface_shape_names): surface_shape_1 = fem_find_object(reference=surface_shape_name, collection=project.collections.surfaces) if surface_shape_1 is not None: for j in range(i, len(surface_shape_names)): surface_shape_2 = fem_find_object( reference=surface_shape_names[j], collection=project.collections.surfaces) if surface_shape_2 is not None and surface_shape_1 is not surface_shape_2: if len(surface_shape_1.get_connecting_lines(shape=surface_shape_2, include_openings=False)) > 0: return surface_shape_1, surface_shape_2 return surface_shape_1, 'FINISHED'
### =================================================================================================================== ### 5. End of script ### ===================================================================================================================