This commit is contained in:
Andrej730
2024-12-23 11:35:48 +05:00
parent 6dfb25862c
commit 170b6fc67e
5 changed files with 38 additions and 104 deletions
@@ -1521,17 +1521,20 @@ class LoadLinkedProject(bpy.types.Operator):
) )
self.meshes = {} self.meshes = {}
self.blender_mats = {} self.blender_mats = {}
blender_mats = {} blender_mats: dict[tuple[float, float, float, float], bpy.types.Material] = {}
default_mat = np.array([[1, 1, 1, 1]], dtype=np.float32) default_mat = np.array([[1, 1, 1, 1]], dtype=np.float32)
chunked_guids = [] chunked_guids: list[str] = []
chunked_guid_ids = [] chunked_guid_ids: list[int] = []
chunked_verts = [] chunked_verts: list[np.ndarray] = []
chunked_faces = [] chunked_faces: list[np.ndarray] = []
chunked_materials = [] # List of material colors.
chunked_material_ids = [] chunked_materials: list[np.ndarray] = []
# List of material indices for each face.
chunked_material_ids: list[np.ndarray] = []
material_offset = 0 material_offset = 0
chunk_size = 10000 chunk_size = 10000
# Vertex offset.
offset = 0 offset = 0
ci = 0 ci = 0
@@ -1771,7 +1774,15 @@ class LoadLinkedProject(bpy.types.Operator):
self.collection.objects.link(obj) self.collection.objects.link(obj)
def create_object(self, verts, faces, materials: list[bpy.types.Material], material_ids, guids, guid_ids): def create_object(
self,
verts: np.ndarray,
faces: np.ndarray,
materials: list[bpy.types.Material],
material_ids: np.ndarray,
guids: list[str],
guid_ids: list[int],
) -> None:
num_vertices = len(verts) // 3 num_vertices = len(verts) // 3
if not num_vertices: if not num_vertices:
return return
+6 -3
View File
@@ -272,10 +272,10 @@ AttributeDataType = Literal["string", "integer", "float", "boolean", "enum", "fi
class Attribute(PropertyGroup): class Attribute(PropertyGroup):
tooltip = "`Right Click > IFC Description` to read the attribute description and online documentation" tooltip = "`Right Click > IFC Description` to read the attribute description and online documentation"
name: StringProperty(name="Name") name: StringProperty(name="Name") # type: ignore [reportRedeclaration]
display_name: StringProperty(name="Display Name", get=get_display_name) display_name: StringProperty(name="Display Name", get=get_display_name)
description: StringProperty(name="Description") description: StringProperty(name="Description") # type: ignore [reportRedeclaration]
ifc_class: StringProperty(name="Ifc Class") ifc_class: StringProperty(name="Ifc Class") # type: ignore [reportRedeclaration]
data_type: EnumProperty( # type: ignore [reportRedeclaration] data_type: EnumProperty( # type: ignore [reportRedeclaration]
name="Data Type", name="Data Type",
items=[(i, i, "") for i in get_args(AttributeDataType)], items=[(i, i, "") for i in get_args(AttributeDataType)],
@@ -316,6 +316,9 @@ class Attribute(PropertyGroup):
metadata: StringProperty(name="Metadata", description="For storing some additional information about the attribute") metadata: StringProperty(name="Metadata", description="For storing some additional information about the attribute")
if TYPE_CHECKING: if TYPE_CHECKING:
name: str
description: str
ifc_class: str
data_type: AttributeDataType data_type: AttributeDataType
def get_value(self) -> Union[str, float, int, bool, None]: def get_value(self) -> Union[str, float, int, bool, None]:
@@ -451,7 +451,7 @@ class Usecase:
mullion_thickness: float = lining_props["MullionThickness"] / 2 mullion_thickness: float = lining_props["MullionThickness"] / 2
first_mullion_offset: float = lining_props["FirstMullionOffset"] first_mullion_offset: float = lining_props["FirstMullionOffset"]
second_mullion_offset: flaot = lining_props["SecondMullionOffset"] second_mullion_offset: float = lining_props["SecondMullionOffset"]
transom_thickness: float = lining_props["TransomThickness"] / 2 transom_thickness: float = lining_props["TransomThickness"] / 2
first_transom_offset: float = lining_props["FirstTransomOffset"] first_transom_offset: float = lining_props["FirstTransomOffset"]
second_transom_offset: float = lining_props["SecondTransomOffset"] second_transom_offset: float = lining_props["SecondTransomOffset"]
+1 -1
View File
@@ -628,7 +628,7 @@ class file:
def __iter__(self) -> Generator[ifcopenshell.entity_instance, None, None]: def __iter__(self) -> Generator[ifcopenshell.entity_instance, None, None]:
return iter(self[id] for id in self.wrapped_data.entity_names()) return iter(self[id] for id in self.wrapped_data.entity_names())
def assign_header_from(self, other): def assign_header_from(self, other: ifcopenshell.file) -> None:
for k, vs in HEADER_FIELDS.items(): for k, vs in HEADER_FIELDS.items():
for v in vs: for v in vs:
setattr(getattr(self.header, k), v, getattr(getattr(other.header, k), v)) setattr(getattr(self.header, k), v, getattr(getattr(other.header, k), v))
@@ -41,13 +41,9 @@ def is_x(value: float, x: float, tolerance: Optional[float] = None) -> bool:
"""Checks whether a value is equivalent to X given a tolerance """Checks whether a value is equivalent to X given a tolerance
:param value: Input value :param value: Input value
:type value: float
:param x: The value to compare to :param x: The value to compare to
:type x: float
:param tolerance: The tolerance to use. Defaults to 1e-6. :param tolerance: The tolerance to use. Defaults to 1e-6.
:type tolerance: float
:return: True or false :return: True or false
:rtype: bool
""" """
if tolerance is None: if tolerance is None:
tolerance = tol tolerance = tol
@@ -60,9 +56,7 @@ def get_volume(geometry: ShapeType) -> float:
Volumes of non-manifold geometry will be unpredictable. Volumes of non-manifold geometry will be unpredictable.
:param geometry: Geometry output calculated by IfcOpenShell :param geometry: Geometry output calculated by IfcOpenShell
:type geometry: geometry
:return: The volume in m3 :return: The volume in m3
:rtype: float
""" """
# https://stackoverflow.com/questions/1406029/how-to-calculate-the-volume-of-a-3d-mesh-object-the-surface-of-which-is-made-up # https://stackoverflow.com/questions/1406029/how-to-calculate-the-volume-of-a-3d-mesh-object-the-surface-of-which-is-made-up
@@ -90,9 +84,7 @@ def get_x(geometry: ShapeType) -> float:
"""Calculates the X length of the geometry """Calculates the X length of the geometry
:param geometry: Geometry output calculated by IfcOpenShell :param geometry: Geometry output calculated by IfcOpenShell
:type geometry: geometry
:return: The X dimension :return: The X dimension
:rtype: float
""" """
verts_flat = get_vertices(geometry).ravel() verts_flat = get_vertices(geometry).ravel()
return np.max(verts_flat[0::3]) - np.min(verts_flat[0::3]) return np.max(verts_flat[0::3]) - np.min(verts_flat[0::3])
@@ -102,9 +94,7 @@ def get_y(geometry: ShapeType) -> float:
"""Calculates the Y length of the geometry """Calculates the Y length of the geometry
:param geometry: Geometry output calculated by IfcOpenShell :param geometry: Geometry output calculated by IfcOpenShell
:type geometry: geometry
:return: The Y dimension :return: The Y dimension
:rtype: float
""" """
verts_flat = get_vertices(geometry).ravel() verts_flat = get_vertices(geometry).ravel()
return np.max(verts_flat[1::3]) - np.min(verts_flat[1::3]) return np.max(verts_flat[1::3]) - np.min(verts_flat[1::3])
@@ -114,9 +104,7 @@ def get_z(geometry: ShapeType) -> float:
"""Calculates the Z length of the geometry """Calculates the Z length of the geometry
:param geometry: Geometry output calculated by IfcOpenShell :param geometry: Geometry output calculated by IfcOpenShell
:type geometry: geometry
:return: The Z dimension :return: The Z dimension
:rtype: float
""" """
verts_flat = get_vertices(geometry).ravel() verts_flat = get_vertices(geometry).ravel()
return np.max(verts_flat[2::3]) - np.min(verts_flat[2::3]) return np.max(verts_flat[2::3]) - np.min(verts_flat[2::3])
@@ -126,9 +114,7 @@ def get_max_xy(geometry: ShapeType) -> float:
"""Gets the maximum X or Y length of the geometry """Gets the maximum X or Y length of the geometry
:param geometry: Geometry output calculated by IfcOpenShell :param geometry: Geometry output calculated by IfcOpenShell
:type geometry: geometry
:return: The maximum possible value out of the X and Y dimension :return: The maximum possible value out of the X and Y dimension
:rtype: float
""" """
return max(get_x(geometry), get_y(geometry)) return max(get_x(geometry), get_y(geometry))
@@ -137,9 +123,7 @@ def get_max_xyz(geometry: ShapeType) -> float:
"""Gets the maximum X, Y, or Z length of the geometry """Gets the maximum X, Y, or Z length of the geometry
:param geometry: Geometry output calculated by IfcOpenShell :param geometry: Geometry output calculated by IfcOpenShell
:type geometry: geometry
:return: The maximum possible value out of the X, Y, and Z dimension :return: The maximum possible value out of the X, Y, and Z dimension
:rtype: float
""" """
return max(get_x(geometry), get_y(geometry), get_z(geometry)) return max(get_x(geometry), get_y(geometry), get_z(geometry))
@@ -148,9 +132,7 @@ def get_min_xyz(geometry: ShapeType) -> float:
"""Gets the minimum X, Y, or Z length of the geometry """Gets the minimum X, Y, or Z length of the geometry
:param geometry: Geometry output calculated by IfcOpenShell :param geometry: Geometry output calculated by IfcOpenShell
:type geometry: geometry
:return: The minimum possible value out of the X, Y, and Z dimension :return: The minimum possible value out of the X, Y, and Z dimension
:rtype: float
""" """
return min(get_x(geometry), get_y(geometry), get_z(geometry)) return min(get_x(geometry), get_y(geometry), get_z(geometry))
@@ -159,9 +141,7 @@ def get_shape_matrix(shape: ShapeElementType) -> MatrixType:
"""Formats the transformation matrix of a shape as a 4x4 numpy array """Formats the transformation matrix of a shape as a 4x4 numpy array
:param shape: Shape output calculated by IfcOpenShell :param shape: Shape output calculated by IfcOpenShell
:type shape: shape
:return: A 4x4 numpy array representing the transformation matrix :return: A 4x4 numpy array representing the transformation matrix
:rtype: MatrixType
""" """
return np.array(shape.transformation.matrix).reshape((4, 4), order="F") return np.array(shape.transformation.matrix).reshape((4, 4), order="F")
@@ -172,9 +152,7 @@ def get_bbox_centroid(geometry: ShapeType) -> tuple[float, float, float]:
The centroid is in local coordinates relative to the object's placement. The centroid is in local coordinates relative to the object's placement.
:param geometry: Geometry output calculated by IfcOpenShell :param geometry: Geometry output calculated by IfcOpenShell
:type geometry: geometry
:return: A tuple representing the XYZ centroid :return: A tuple representing the XYZ centroid
:rtype: tuple[float, float, float]
""" """
vertices_array = get_vertices(geometry) vertices_array = get_vertices(geometry)
return (np.min(vertices_array, axis=0) + np.max(vertices_array, axis=0)) / 2 return (np.min(vertices_array, axis=0) + np.max(vertices_array, axis=0)) / 2
@@ -186,9 +164,7 @@ def get_vert_centroid(geometry: ShapeType) -> tuple[float, float, float]:
The centroid is in local coordinates relative to the object's placement. The centroid is in local coordinates relative to the object's placement.
:param geometry: Geometry output calculated by IfcOpenShell :param geometry: Geometry output calculated by IfcOpenShell
:type geometry: geometry
:return: A tuple representing the XYZ centroid :return: A tuple representing the XYZ centroid
:rtype: tuple[float, float, float]
""" """
return np.mean(get_vertices(geometry), axis=0) return np.mean(get_vertices(geometry), axis=0)
@@ -200,11 +176,8 @@ def get_element_bbox_centroid(element: ifcopenshell.entity_instance, geometry) -
is more efficient to use ``get_shape_bbox_centroid``. is more efficient to use ``get_shape_bbox_centroid``.
:param element: The element occurrence :param element: The element occurrence
:type: ifcopenshell.entity_instance
:param geometry: Geometry output calculated by IfcOpenShell :param geometry: Geometry output calculated by IfcOpenShell
:type geometry: geometry
:return: A tuple representing the XYZ centroid :return: A tuple representing the XYZ centroid
:rtype: npt.NDArray[np.float64]
""" """
centroid = get_bbox_centroid(geometry) centroid = get_bbox_centroid(geometry)
if not element.ObjectPlacement or not element.ObjectPlacement.is_a("IfcLocalPlacement"): if not element.ObjectPlacement or not element.ObjectPlacement.is_a("IfcLocalPlacement"):
@@ -220,11 +193,8 @@ def get_shape_bbox_centroid(shape: ShapeType, geometry: ShapeType) -> npt.NDArra
shape, you can use ``get_element_bbox_centroid``. shape, you can use ``get_element_bbox_centroid``.
:param shape: Shape output calculated by IfcOpenShell :param shape: Shape output calculated by IfcOpenShell
:type shape: shape
:param geometry: Geometry output calculated by IfcOpenShell :param geometry: Geometry output calculated by IfcOpenShell
:type geometry: geometry
:return: A tuple representing the XYZ centroid :return: A tuple representing the XYZ centroid
:rtype: npt.NDArray[np.float64]
""" """
centroid = get_bbox_centroid(geometry) centroid = get_bbox_centroid(geometry)
return (get_shape_matrix(shape) @ np.array([*centroid, 1.0]))[0:3] return (get_shape_matrix(shape) @ np.array([*centroid, 1.0]))[0:3]
@@ -235,13 +205,10 @@ def get_vertices(geometry: ShapeType, is_2d: bool = False) -> npt.NDArray[np.flo
Vertices are in local coordinates. Vertices are in local coordinates.
Results are a nested numpy array e.g. [[v1x, v1y, v1z], [v2x, v2y, v2z], ...]
:param geometry: Geometry output calculated by IfcOpenShell :param geometry: Geometry output calculated by IfcOpenShell
:type geometry: geometry
:param is_2d: Set to True to to get XY coordinates only. :param is_2d: Set to True to to get XY coordinates only.
:return: A numpy array listing all the vertices. Each vertex is a numpy array with XYZ coordinates. :return: A numpy array listing all the vertices and their coordinates.
:rtype: np.array[np.array[float]] Array shape: (n, 3), where n - number of vertices.
""" """
if is_2d: if is_2d:
return np.frombuffer(geometry.verts_buffer, "d").reshape(-1, 3)[:, :2] return np.frombuffer(geometry.verts_buffer, "d").reshape(-1, 3)[:, :2]
@@ -258,9 +225,8 @@ def get_edges(geometry: ShapeType) -> npt.NDArray[np.int32]:
ngons. ngons.
:param geometry: Geometry output calculated by IfcOpenShell :param geometry: Geometry output calculated by IfcOpenShell
:type geometry: geometry :return: A numpy array listing all the edges.
:return: A numpy array listing all the edges. Each edge is a numpy array with two vertex indices. Array shape: (n, 2), where n - number of edges.
:rtype: np.array[np.array[int]]
""" """
return np.frombuffer(geometry.edges_buffer, dtype="i").reshape(-1, 2) return np.frombuffer(geometry.edges_buffer, dtype="i").reshape(-1, 2)
@@ -274,9 +240,8 @@ def get_faces(geometry: ShapeType) -> npt.NDArray[np.int32]:
Results are a nested numpy array e.g. [[f1v1, f1v2, f1v3], [f2v1, f2v2, f2v3], ...] Results are a nested numpy array e.g. [[f1v1, f1v2, f1v3], [f2v1, f2v2, f2v3], ...]
:param geometry: Geometry output calculated by IfcOpenShell :param geometry: Geometry output calculated by IfcOpenShell
:type geometry: geometry :return: A numpy array listing all the faces.
:return: A numpy array listing all the faces. Each face is a numpy array with three vertex indices. Array shape: (n, 3), where n - number of faces.
:rtype: np.array[np.array[int]]
""" """
return np.frombuffer(geometry.faces_buffer, dtype="i").reshape(-1, 3) return np.frombuffer(geometry.faces_buffer, dtype="i").reshape(-1, 3)
@@ -285,6 +250,7 @@ def get_material_colors(geometry: ShapeType) -> npt.NDArray[np.float64]:
"""Get material colors as a numpy array. """Get material colors as a numpy array.
:return: A numpy array listing RGBA color for each shape's material. :return: A numpy array listing RGBA color for each shape's material.
Array shape: (1, 4).
""" """
# colors_buffer comes from geometry.materials and doesn't account # colors_buffer comes from geometry.materials and doesn't account
# for colors that can be set by some other way (e.g. IfcIndexedColourMap). # for colors that can be set by some other way (e.g. IfcIndexedColourMap).
@@ -297,6 +263,7 @@ def get_normals(geometry: ShapeType) -> npt.NDArray[np.float64]:
See geometry settings documentation for settings that affect normals. See geometry settings documentation for settings that affect normals.
:return: A numpy array listing normal for each shape vertex. :return: A numpy array listing normal for each shape vertex.
Array shape: (1, 3).
""" """
return np.frombuffer(geometry.normals_buffer, dtype="d").reshape(-1, 3) return np.frombuffer(geometry.normals_buffer, dtype="d").reshape(-1, 3)
@@ -348,11 +315,9 @@ def get_shape_vertices(shape: ShapeType, geometry: ShapeType) -> npt.NDArray[np.
Results are a nested numpy array e.g. [[v1x, v1y, v1z], [v2x, v2y, v2z], ...] Results are a nested numpy array e.g. [[v1x, v1y, v1z], [v2x, v2y, v2z], ...]
:param shape: Shape output calculated by IfcOpenShell :param shape: Shape output calculated by IfcOpenShell
:type shape: shape
:param geometry: Geometry output calculated by IfcOpenShell :param geometry: Geometry output calculated by IfcOpenShell
:type geometry: geometry
:return: A numpy array listing all the vertices. Each vertex is a numpy array with XYZ coordinates. :return: A numpy array listing all the vertices. Each vertex is a numpy array with XYZ coordinates.
:rtype: np.array[np.array[float]] Array shape: (n, 3), where n - number of vertices.
""" """
verts = get_vertices(geometry) verts = get_vertices(geometry)
mat = get_shape_matrix(shape) mat = get_shape_matrix(shape)
@@ -368,11 +333,8 @@ def get_element_vertices(element: ifcopenshell.entity_instance, geometry: ShapeT
Results are a nested numpy array e.g. [[v1x, v1y, v1z], [v2x, v2y, v2z], ...] Results are a nested numpy array e.g. [[v1x, v1y, v1z], [v2x, v2y, v2z], ...]
:param element: The element occurrence :param element: The element occurrence
:type: ifcopenshell.entity_instance
:param geometry: Geometry output calculated by IfcOpenShell :param geometry: Geometry output calculated by IfcOpenShell
:type geometry: geometry
:return: A numpy array listing all the vertices. Each vertex is a numpy array with XYZ coordinates. :return: A numpy array listing all the vertices. Each vertex is a numpy array with XYZ coordinates.
:rtype: np.array[np.array[float]]
""" """
verts = get_vertices(geometry) verts = get_vertices(geometry)
if not element.ObjectPlacement or not element.ObjectPlacement.is_a("IfcLocalPlacement"): if not element.ObjectPlacement or not element.ObjectPlacement.is_a("IfcLocalPlacement"):
@@ -385,9 +347,7 @@ def get_bottom_elevation(geometry: ShapeType) -> float:
"""Gets the lowest local Z ordinate of the geometry """Gets the lowest local Z ordinate of the geometry
:param geometry: Geometry output calculated by IfcOpenShell :param geometry: Geometry output calculated by IfcOpenShell
:type geometry: geometry
:return: The Z value :return: The Z value
:rtype: float
""" """
z_values = [geometry.verts[i + 2] for i in range(0, len(geometry.verts), 3)] z_values = [geometry.verts[i + 2] for i in range(0, len(geometry.verts), 3)]
return min(z_values) return min(z_values)
@@ -397,9 +357,7 @@ def get_top_elevation(geometry: ShapeType) -> float:
"""Gets the highest local Z ordinate of the geometry """Gets the highest local Z ordinate of the geometry
:param geometry: Geometry output calculated by IfcOpenShell :param geometry: Geometry output calculated by IfcOpenShell
:type geometry: geometry
:return: The Z value :return: The Z value
:rtype: float
""" """
verts_flat = get_vertices(geometry).ravel() verts_flat = get_vertices(geometry).ravel()
return np.max(verts_flat[2::3]) return np.max(verts_flat[2::3])
@@ -412,11 +370,8 @@ def get_shape_bottom_elevation(shape: ShapeType, geometry: ShapeType) -> float:
instead. instead.
:param shape: Shape output calculated by IfcOpenShell :param shape: Shape output calculated by IfcOpenShell
:type shape: shape
:param geometry: Geometry output calculated by IfcOpenShell :param geometry: Geometry output calculated by IfcOpenShell
:type geometry: geometry
:return: The Z value :return: The Z value
:rtype: float
""" """
return min([v[2] for v in get_shape_vertices(shape, geometry)]) return min([v[2] for v in get_shape_vertices(shape, geometry)])
@@ -428,11 +383,8 @@ def get_shape_top_elevation(shape: ShapeType, geometry: ShapeType) -> float:
instead. instead.
:param shape: Shape output calculated by IfcOpenShell :param shape: Shape output calculated by IfcOpenShell
:type shape: shape
:param geometry: Geometry output calculated by IfcOpenShell :param geometry: Geometry output calculated by IfcOpenShell
:type geometry: geometry
:return: The Z value :return: The Z value
:rtype: float
""" """
return max([v[2] for v in get_shape_vertices(shape, geometry)]) return max([v[2] for v in get_shape_vertices(shape, geometry)])
@@ -444,11 +396,8 @@ def get_element_bottom_elevation(element: ifcopenshell.entity_instance, geometry
``get_shape_bottom_elevation``. ``get_shape_bottom_elevation``.
:param element: The element occurrence :param element: The element occurrence
:type: ifcopenshell.entity_instance
:param geometry: Geometry output calculated by IfcOpenShell :param geometry: Geometry output calculated by IfcOpenShell
:type geometry: geometry
:return: The Z value :return: The Z value
:rtype: float
""" """
return min([v[2] for v in get_element_vertices(element, geometry)]) return min([v[2] for v in get_element_vertices(element, geometry)])
@@ -460,11 +409,8 @@ def get_element_top_elevation(element: ifcopenshell.entity_instance, geometry: S
``get_shape_top_elevation``. ``get_shape_top_elevation``.
:param element: The element occurrence :param element: The element occurrence
:type: ifcopenshell.entity_instance
:param geometry: Geometry output calculated by IfcOpenShell :param geometry: Geometry output calculated by IfcOpenShell
:type geometry: geometry
:return: The Z value :return: The Z value
:rtype: float
""" """
return max([v[2] for v in get_element_vertices(element, geometry)]) return max([v[2] for v in get_element_vertices(element, geometry)])
@@ -473,12 +419,10 @@ def get_bbox(vertices: Iterable[VECTOR_3D]) -> tuple[npt.NDArray[np.float64], np
"""Gets the bounding box of vertices """Gets the bounding box of vertices
:param vertices: An iterable of vertices :param vertices: An iterable of vertices
:type: iterable
:return: The bounding box value represented as a tuple of two numpy arrays. :return: The bounding box value represented as a tuple of two numpy arrays.
The first holds the bottom left corner and the second holds the top The first holds the bottom left corner and the second holds the top
right. E.g. (np.array([minx, miny, minz]), np.array([maxx, maxy, right. E.g. (np.array([minx, miny, minz]), np.array([maxx, maxy,
maxz])) maxz]))
:rtype: tuple[np.array[float]]
""" """
x_values = [v[0] for v in vertices] x_values = [v[0] for v in vertices]
y_values = [v[1] for v in vertices] y_values = [v[1] for v in vertices]
@@ -496,11 +440,8 @@ def get_area_vf(vertices: npt.NDArray[np.float64], faces: npt.NDArray[np.int32])
"""Calculates the surface area given a list of vertices and triangulated faces """Calculates the surface area given a list of vertices and triangulated faces
:param vertices: A list of 3D vertices, such as returned from get_vertices. :param vertices: A list of 3D vertices, such as returned from get_vertices.
:type: np.array[iterable[float]]
:param faces: A list of faces, such as returned from get_faces. :param faces: A list of faces, such as returned from get_faces.
:type: np.array[iterable[int]]
:return: The surface area. :return: The surface area.
:rtype: float
""" """
# Calculate the triangle normal vectors # Calculate the triangle normal vectors
v1 = vertices[faces[:, 1]] - vertices[faces[:, 0]] v1 = vertices[faces[:, 1]] - vertices[faces[:, 0]]
@@ -520,9 +461,7 @@ def get_area(geometry: ShapeType) -> float:
"""Calculates the surface area of the geometry """Calculates the surface area of the geometry
:param geometry: Geometry output calculated by IfcOpenShell :param geometry: Geometry output calculated by IfcOpenShell
:type geometry: geometry
:return: The surface area. :return: The surface area.
:rtype: float
""" """
vertices = get_vertices(geometry) vertices = get_vertices(geometry)
faces = get_faces(geometry) faces = get_faces(geometry)
@@ -548,12 +487,9 @@ def get_side_area(
you want the projected area, use ``get_footprint_area``. you want the projected area, use ``get_footprint_area``.
:param geometry: Geometry output calculated by IfcOpenShell :param geometry: Geometry output calculated by IfcOpenShell
:type geometry: geometry
:param axis: Either X, Y, or Z. Defaults to Y, which is used for standard :param axis: Either X, Y, or Z. Defaults to Y, which is used for standard
walls. walls.
:type axis: str
:return: The surface area. :return: The surface area.
:rtype: float
""" """
if direction is None: if direction is None:
direction = {"X": (1.0, 0.0, 0.0), "Y": (0.0, 1.0, 0.0), "Z": (0.0, 0.0, 1.0)}[axis] direction = {"X": (1.0, 0.0, 0.0), "Y": (0.0, 1.0, 0.0), "Z": (0.0, 0.0, 1.0)}[axis]
@@ -585,9 +521,7 @@ def get_max_side_area(geometry: ShapeType) -> float:
See :func:`get_side_area` for how side area is calculated. See :func:`get_side_area` for how side area is calculated.
:param geometry: Geometry output calculated by IfcOpenShell :param geometry: Geometry output calculated by IfcOpenShell
:type geometry: geometry
:return: The maximum surface area from either the X, Y, or Z axis. :return: The maximum surface area from either the X, Y, or Z axis.
:rtype: float
""" """
return max(get_side_area(geometry, axis="X"), get_side_area(geometry, axis="Y"), get_side_area(geometry, axis="Z")) return max(get_side_area(geometry, axis="X"), get_side_area(geometry, axis="Y"), get_side_area(geometry, axis="Z"))
@@ -611,14 +545,10 @@ def get_footprint_area(
area. If you want the actual area, use ``get_side_area``. area. If you want the actual area, use ``get_side_area``.
:param geometry: Geometry output calculated by IfcOpenShell :param geometry: Geometry output calculated by IfcOpenShell
:type geometry: geometry
:param axis: Either X, Y, or Z. Defaults to Z. :param axis: Either X, Y, or Z. Defaults to Z.
:type axis: str,optional
:param direction: An XYZ iterable (e.g. (0., 0., 1.)). If a direction :param direction: An XYZ iterable (e.g. (0., 0., 1.)). If a direction
vector is specified, this overrides the axis argument. vector is specified, this overrides the axis argument.
:type axis: iterable[float],optional
:return: The surface area. :return: The surface area.
:rtype: float
""" """
if direction is None: if direction is None:
direction = {"X": (1.0, 0.0, 0.0), "Y": (0.0, 1.0, 0.0), "Z": (0.0, 0.0, 1.0)}[axis] direction = {"X": (1.0, 0.0, 0.0), "Y": (0.0, 1.0, 0.0), "Z": (0.0, 0.0, 1.0)}[axis]
@@ -682,9 +612,7 @@ def get_outer_surface_area(geometry: ShapeType) -> float:
exclude the end faces (at the minimum and maximum local Z). exclude the end faces (at the minimum and maximum local Z).
:param geometry: Geometry output calculated by IfcOpenShell :param geometry: Geometry output calculated by IfcOpenShell
:type geometry: geometry
:return: The surface area. :return: The surface area.
:rtype: float
""" """
vertices = get_vertices(geometry) vertices = get_vertices(geometry)
faces = get_faces(geometry) faces = get_faces(geometry)
@@ -710,9 +638,7 @@ def get_footprint_perimeter(geometry: ShapeType) -> float:
perimeter edges are totaled. perimeter edges are totaled.
:param geometry: Geometry output calculated by IfcOpenShell :param geometry: Geometry output calculated by IfcOpenShell
:type geometry: geometry
:return: The perimeter length :return: The perimeter length
:rtype: float
""" """
vertices = get_vertices(geometry) vertices = get_vertices(geometry)
faces = get_faces(geometry) faces = get_faces(geometry)
@@ -757,9 +683,7 @@ def get_profiles(element: ifcopenshell.entity_instance) -> list[ifcopenshell.ent
solid extrusions. This is useful for later doing 2D take-off from profiles. solid extrusions. This is useful for later doing 2D take-off from profiles.
:param element: The element occurrence :param element: The element occurrence
:type: ifcopenshell.entity_instance
:return: A list of profiles :return: A list of profiles
:rtype: list[ifcopenshell.entity_instance]
""" """
material = ifcopenshell.util.element.get_material(element, should_skip_usage=True) material = ifcopenshell.util.element.get_material(element, should_skip_usage=True)
if material and material.is_a("IfcMaterialProfileSet"): if material and material.is_a("IfcMaterialProfileSet"):
@@ -767,13 +691,11 @@ def get_profiles(element: ifcopenshell.entity_instance) -> list[ifcopenshell.ent
return [e.SweptArea for e in get_extrusions(element)] return [e.SweptArea for e in get_extrusions(element)]
def get_extrusions(element: ifcopenshell.entity_instance) -> list[ifcopenshell.entity_instance]: def get_extrusions(element: ifcopenshell.entity_instance) -> Union[list[ifcopenshell.entity_instance], None]:
"""Gets all extruded area solids used to define an element's model body geometry """Gets all extruded area solids used to define an element's model body geometry
:param element: The element occurrence :param element: The element occurrence
:type: ifcopenshell.entity_instance :return: A list of extrusion representation items or `None` if element has no representation.
:return: A list of extrusion representation items
:rtype: list[ifcopenshell.entity_instance]
""" """
representation = ifcopenshell.util.representation.get_representation(element, "Model", "Body", "MODEL_VIEW") representation = ifcopenshell.util.representation.get_representation(element, "Model", "Body", "MODEL_VIEW")
if not representation: if not representation:
@@ -796,9 +718,7 @@ def get_total_edge_length(geometry: ShapeType) -> float:
"""Calculates the total length of edges in a given geometry. """Calculates the total length of edges in a given geometry.
:param geometry: Geometry output calculated by IfcOpenShell :param geometry: Geometry output calculated by IfcOpenShell
:type geometry: geometry
:return: The total length of all edges in the geometry. :return: The total length of all edges in the geometry.
:rtype: float
""" """
vertices = get_vertices(geometry) vertices = get_vertices(geometry)
vertices = vertices[get_edges(geometry)] vertices = vertices[get_edges(geometry)]