### ===================================================================================================================
### CREATE ROOFS
### ===================================================================================================================
# Copyright ©2026 Haskoning Nederland B.V.
# For use by VIIA
### ===================================================================================================================
### 1. Import modules
### ===================================================================================================================
# General imports
from __future__ import annotations
from typing import TYPE_CHECKING, List, Optional, Union
from copy import deepcopy
# References for functions and classes in the haskoning-datafusr-py-base package
from haskoning_datafusr_py_base.math import (
average_point,
cross_product_vector,
dot_product_vector,
is_point_in_surface,
is_point_on_surface_edge,
is_self_intersecting,
project_point_on_plane,
unit_vector,
vector_between_points,
)
# References for functions and classes in the haskoning-structural package
from haskoning_structural.shape_geometries import Polyline, Line
from haskoning_structural.shapes import Shapes
from haskoning_structural.groups import Layer
# References for functions and classes in the viiaPackage
if TYPE_CHECKING:
from viiapackage.viiaStatus import ViiaProject
### ===================================================================================================================
### 2. Functions to create gable roof
### ===================================================================================================================
[docs]def viia_create_gable_roof(
project: ViiaProject, layer: Union[Layer, str], wall_points: List, material_equivalent_plate: str,
geometry_equivalent_plate: Union[int, float], height_ridge: Union[int, float], material_ridge: str = 'LIN-HOUT',
geometry_ridge: str = None, material_wall_plate: str = 'LIN-HOUT', geometry_wall_plate: str = None) \
-> List[Shapes]:
"""
Function to create basic roof structure based on two wall plates and height of ridge.
.. note:: The material of the collar tie can be modelled implicitly, or explicit. Select the appropriate material.
Input:
- project (obj): VIIA project object containing collections of fem objects and project variables.
- layer (str or Layer): Name of the layer as a string, or the Layer object itself, to which the newly created
roof should be added.
- wall_points (list with lists with lists of 3 floats): A list which contains two lists with both two
coordinates. The coordinates represent the start and endpoint of the wall plate.
- material_equivalent_plate (str): Name of the material following VIIA naming convention.
- geometry_equivalent_plate (int, float): Thickness of the equivalent plate.
- material_ridge (str): Name of the material of the ridge following VIIA naming convention. Default value:
'LIN-HOUT'.
- geometry_ridge (str): Name of the geometry of the ridge following VIIA naming convention. Default value: None.
- material_wall_plate (str): Name of the material of the wall plates following VIIA naming convention. Default
value: 'LIN-HOUT'.
- geometry_wall_plate (str): Name of the geometry of the wall plates following VIIA naming convention. Default
value: None.
Output:
- Creation of the shapes for gable roof (sheeting, optionally ridge beam, wall plates).
A list with all the newly created shapes is returned.
"""
# Revert second pair of wall points if wall points are in opposite direction
if dot_product_vector(
a=unit_vector(a=vector_between_points(point1=wall_points[0][0], point2=wall_points[0][1])),
b=unit_vector(a=vector_between_points(point1=wall_points[1][0], point2=wall_points[1][1]))) < 0:
wall_points[1].reverse()
# Get start and end point of ridge, as average of the wall points
start_point_ridge = average_point(point_list=[wall_points[0][0], wall_points[1][0]])
end_point_ridge = average_point(point_list=[wall_points[0][1], wall_points[1][1]])
# Adjust height
start_point_ridge[2] = height_ridge
end_point_ridge[2] = height_ridge
# Create line that forms the ridge
ridge = project.create_line([start_point_ridge, end_point_ridge])
# Call base function
return _viia_create_general_roof(
project=project, layer=layer, wall_points=wall_points, material_equivalent_plate=material_equivalent_plate,
geometry_equivalent_plate=geometry_equivalent_plate, ridge=ridge, material_ridge=material_ridge,
geometry_ridge=geometry_ridge, material_wall_plate=material_wall_plate, geometry_wall_plate=geometry_wall_plate)
### ===================================================================================================================
### 3. Functions to create hip roof
### ===================================================================================================================
[docs]def viia_create_hip_roof(
project: ViiaProject, layer: Union[Layer, str], wall_points: List, material_equivalent_plate: str,
geometry_equivalent_plate: Union[int, float], ridge: Union[List[List[Union[int, float]]], 'Line'],
material_ridge: str = 'LIN-HOUT', geometry_ridge: str = None, material_wall_plate: str = 'LIN-HOUT',
geometry_wall_plate: str = None) -> List[Shapes]:
"""
Function to create basic roof structure based on two wall plates and height of ridge.
Input:
- project (obj): VIIA project object containing collections of fem objects and project variables.
- layer (str or Layer): Name of the layer as a string, or the Layer object itself, to which the newly created
roof should be added.
- wall_points (list with lists with lists of 3 floats): A list which contains two lists with both two
coordinates. The coordinates represent the start and endpoint of the wall plate
- material_equivalent_plate (str): Name of the material following VIIA naming convention.
- geometry_equivalent_plate (int, float): Thickness of the equivalent plate.
- ridge (obj): Object reference of the line which represents the ridge.
Alternative (list with 2 lists of 3 floats): List with 2 coordinates of line, which represents the ridge.
- material_ridge (str): Name of the material of the ridge following VIIA naming convention. Default value:
'LIN-HOUT'.
- geometry_ridge (str): Name of the geometry of the ridge following VIIA naming convention. Default value: None.
- material_wall_plate (str): Name of the material of the wall plates following VIIA naming convention. Default
value: 'LIN-HOUT'.
- geometry_wall_plate (str): Name of the geometry of the wall plates following VIIA naming convention. Default
value: None.
Output:
- Creation of the shapes for hip roof (sheeting, optionally ridge beam, wall plates). A list of all newly
created objects is returned.
"""
# If ridge is not yet a Line, make it a line
if isinstance(ridge, Line):
pass
elif isinstance(ridge, list) and len(ridge) == 2:
ridge = project.create_line([ridge[0], ridge[1]])
else:
raise ValueError(
"ERROR: Input for ridge for function viia_create_hip_roof is provided wrongly. Function not executed.")
# Revert wall points in if ridge is in opposite direction
if dot_product_vector(
a=unit_vector(a=vector_between_points(point1=wall_points[0][0], point2=wall_points[0][1])),
b=ridge.get_direction().vector) < 0:
wall_points[0].reverse()
if dot_product_vector(
a=unit_vector(a=vector_between_points(point1=wall_points[1][0], point2=wall_points[1][1])),
b=ridge.get_direction().vector) < 0:
wall_points[1].reverse()
# Call base function
return _viia_create_general_roof(
project=project, layer=layer, wall_points=wall_points, material_equivalent_plate=material_equivalent_plate,
geometry_equivalent_plate=geometry_equivalent_plate, ridge=ridge, material_ridge=material_ridge,
geometry_ridge=geometry_ridge, material_wall_plate=material_wall_plate, geometry_wall_plate=geometry_wall_plate)
### ===================================================================================================================
### 4. Base function for creating roofs
### ===================================================================================================================
[docs]def _viia_create_general_roof(
project: ViiaProject, layer: Union[Layer, str], wall_points: List, material_equivalent_plate: str,
geometry_equivalent_plate: Union[int, float], ridge: Union['Line', 'Polyline'],
material_ridge: Optional[str] = None, geometry_ridge: Optional[str] = None,
material_wall_plate: Optional[str] = None, geometry_wall_plate: Optional[str] = None) -> List[Shapes]:
"""
Function to create basic roof structure based on two wall plates and height of ridge.
Input:
- project (obj): VIIA project object containing collections of fem objects and project variables.
- layer (str or Layer): Name of the layer as a string, or the Layer object itself, to which the newly created
roof should be added.
- wall_points (list with lists with lists of 3 floats): A list which contains two lists with both two
coordinates. The coordinates represent the start and endpoint of the wall plate
- material_equivalent_plate (str): String describing the name of the material of the equivalent roof plates,
following VIIA naming convention.
- geometry_equivalent_plate (str): Thickness of the equivalent plate.
- ridge (obj): Object reference of the line which represents the ridge.
Alternative (list with 2 lists of 3 floats): List with 2 coordinates of line, which represents the ridge.
- material_ridge (str): Name of the material of the ridge following VIIA naming convention. Default value:
'LIN-HOUT'.
- geometry_ridge (str): Name of the geometry of the ridge following VIIA naming convention. Default value: None.
- material_wall_plate (str): Name of the material of the wall plates following VIIA naming convention. Default
value: 'LIN-HOUT'.
- geometry_wall_plate (str): Name of the geometry of the wall plates following VIIA naming convention. Default
value: None.
Output:
- Creation of the shapes for the roof (sheeting, optionally ridge beam, wall plates). A list with
all the newly created shapes is returned.
"""
# Wall points should already be ordered, nevertheless check again
# Revert second pair of wall points if wall points are in opposite direction
if dot_product_vector(
a=unit_vector(a=vector_between_points(point1=wall_points[0][0], point2=wall_points[0][1])),
b=unit_vector(a=vector_between_points(point1=wall_points[1][0], point2=wall_points[1][1]))) < 0:
wall_points[1].reverse()
start_point_1 = wall_points[0][0]
end_point_1 = wall_points[0][1]
start_point_2 = wall_points[1][0]
end_point_2 = wall_points[1][1]
start_point_ridge = ridge.get_startnode().coordinates
end_point_ridge = ridge.get_endnode().coordinates
# Make ridge beam if properties are provided
ridge_beam = None
if material_ridge is not None and geometry_ridge is not None:
ridge_beam = project.viia_create_beam(
name=layer, material=material_ridge, geometry=geometry_ridge, points=ridge.get_points(), is_roof_beam=True)
# Make wall plates if properties are provided
wall_plate_1 = None
wall_plate_2 = None
if material_wall_plate is not None and geometry_wall_plate is not None:
wall_plate_1 = project.viia_create_beam(
name=layer, points=[start_point_1, end_point_1], material=material_wall_plate, geometry=geometry_wall_plate,
is_roof_beam=True)
wall_plate_2 = project.viia_create_beam(
name=layer, points=[start_point_2, end_point_2], material=material_wall_plate, geometry=geometry_wall_plate,
is_roof_beam=True)
# Make roof plates
roof_1 = project.viia_create_roof(
name=layer, material=material_equivalent_plate, geometry=geometry_equivalent_plate,
points=[[start_point_1, start_point_ridge, end_point_ridge, end_point_1]])
roof_2 = project.viia_create_roof(
name=layer, material=material_equivalent_plate, geometry=geometry_equivalent_plate,
points=[[start_point_2, start_point_ridge, end_point_ridge, end_point_2]])
# Update local element axes
ridge_direction = vector_between_points(point1=start_point_ridge, point2=end_point_ridge)
roof_1.update_local_x_axis(local_x_axis=cross_product_vector(a=roof_1.normal_vector(), b=ridge_direction))
roof_2.update_local_x_axis(local_x_axis=cross_product_vector(a=roof_2.normal_vector(), b=ridge_direction))
# Create horizontal plane of wall points
horizontal_wall_points = deepcopy(wall_points[0]) + deepcopy(wall_points[1])
for i in range(len(horizontal_wall_points)):
horizontal_wall_points[i][2] = 0
# Revert last two points if the plane is self intersecting
if is_self_intersecting(surface=horizontal_wall_points):
horizontal_wall_points.append(horizontal_wall_points[2])
horizontal_wall_points.pop(2)
# Project start and end point ridge to horizontal plane
projected_start_point = project_point_on_plane(
point=start_point_ridge, plane=horizontal_wall_points, direction=[0, 0, 1], precision=project.check_precision)
projected_end_point = project_point_on_plane(
point=end_point_ridge, plane=horizontal_wall_points, direction=[0, 0, 1], precision=project.check_precision)
roof_3 = None
roof_4 = None
wall_plate_3 = None
wall_plate_4 = None
# If projected point within surface and not on contour, create roof plate
if is_point_in_surface(
point=projected_start_point, surface=horizontal_wall_points, precision=project.check_precision) and not \
is_point_on_surface_edge(
point=projected_start_point, surface=horizontal_wall_points, precision=project.check_precision):
roof_3 = project.viia_create_roof(
name=layer, material=material_equivalent_plate, geometry=geometry_equivalent_plate,
points=[[start_point_1, start_point_2, start_point_ridge]])
wall_direction = vector_between_points(point1=start_point_1, point2=start_point_2)
roof_3.update_local_x_axis(cross_product_vector(a=roof_3.normal_vector(), b=wall_direction))
# Also create wall plate if properties are provided
if material_wall_plate is not None and geometry_wall_plate is not None:
wall_plate_3 = project.viia_create_beam(
name=layer, material=material_wall_plate, geometry=geometry_wall_plate,
points=[start_point_1, start_point_2], is_roof_beam=True)
# If projected point within surface and not on contour, create roof plate
if is_point_in_surface(
point=projected_end_point, surface=horizontal_wall_points, precision=project.check_precision) and not \
is_point_on_surface_edge(
point=projected_end_point, surface=horizontal_wall_points, precision=project.check_precision):
roof_4 = project.viia_create_roof(
name=layer, material=material_equivalent_plate, geometry=geometry_equivalent_plate,
points=[[end_point_1, end_point_2, end_point_ridge]])
wall_direction = vector_between_points(point1=end_point_1, point2=end_point_2)
roof_4.update_local_x_axis(cross_product_vector(a=roof_4.normal_vector(), b=wall_direction))
# Also create wall plate if properties are provided
if material_wall_plate is not None and geometry_wall_plate is not None:
wall_plate_4 = project.viia_create_beam(
name=layer, material=material_wall_plate, geometry=geometry_wall_plate,
points=[end_point_1, end_point_2], is_roof_beam=True)
# Return created shapes
return [item for item in [
ridge_beam, wall_plate_1, wall_plate_2, wall_plate_3, wall_plate_4, roof_1, roof_2, roof_3, roof_4] if item]
### ===================================================================================================================
### 5. End of script
### ===================================================================================================================