First Commit

This commit is contained in:
2026-05-31 10:17:09 +07:00
commit 17a9c69379
4547 changed files with 1170384 additions and 0 deletions
@@ -0,0 +1,39 @@
# IfcOpenShell - IFC toolkit and geometry engine
# Copyright (C) 2022 Dion Moult <dion@thinkmoult.com>
#
# This file is part of IfcOpenShell.
#
# IfcOpenShell is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# IfcOpenShell is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with IfcOpenShell. If not, see <http://www.gnu.org/licenses/>.
"""Boundaries are primarily used for representing virtual interfaces between
spaces for energy analysis.
Boundaries may be associated with spaces or physical elements that enclose
spaces such as walls, doors, and windows.
"""
from .. import wrap_usecases
from .assign_connection_geometry import assign_connection_geometry
from .copy_boundary import copy_boundary
from .edit_attributes import edit_attributes
from .remove_boundary import remove_boundary
wrap_usecases(__path__, __name__)
__all__ = [
"assign_connection_geometry",
"copy_boundary",
"edit_attributes",
"remove_boundary",
]
@@ -0,0 +1,133 @@
# IfcOpenShell - IFC toolkit and geometry engine
# Copyright (C) 2021 Dion Moult <dion@thinkmoult.com>
#
# This file is part of IfcOpenShell.
#
# IfcOpenShell is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# IfcOpenShell is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with IfcOpenShell. If not, see <http://www.gnu.org/licenses/>.
from typing import Optional
import numpy as np
import numpy.typing as npt
import ifcopenshell.util.unit
from ifcopenshell.util.shape_builder import SequenceOfVectors, V, ifc_safe_vector_type
def assign_connection_geometry(
file: ifcopenshell.file,
rel_space_boundary: ifcopenshell.entity_instance,
outer_boundary: SequenceOfVectors,
location: tuple[float, float, float],
axis: tuple[float, float, float],
ref_direction: tuple[float, float, float],
inner_boundaries: Optional[SequenceOfVectors] = None,
unit_scale: Optional[float] = None,
) -> None:
"""Create and assign a connection geometry to a space boundary relationship
A space boundary may optionally have a plane that represents how that
space is adjacent to another space, known as the connection geometry.
You may specify this plane in terms of an outer boundary polyline, zero
or more inner boundaries (such as for windows), and a positional matrix
for the orientation of the plane.
:param rel_space_boundary: The space boundary relationship to assign the
connection geometry to.
:param outer_boundary: A list of 2D points representing an open
polyline. The last point will connect to the first point. Each
point is represented by an interable of 2 floats. The coordinates of
the points are relative to the positional matrix arguments.
:param inner_boundaries: A list of zero or more inner boundaries to use
for the plane. Each boundary is represented by an open polyline, as
defined by the outer_boundary argument.
:param location: The local origin of the connection geometry, defined as
an XYZ coordinate relative to the placement of the space that is
being bounded.
:param axis: The local X axis of the connection geometry, defined as an
XYZ vector relative to the placement of the space that is being
bounded.
:param ref_direction: The local Z axis of the connection geometry,
defined as an XYZ vector relative to the placement of the space that
is being bounded. The Y vector is automatically derived using the
right hand rule.
:param unit_scale: The unit scale as calculated by
ifcopenshell.util.unit.calculate_unit_scale. If not provided, it
will be automatically calculated for you.
:return: None
Example:
.. code:: python
ifcopenshell.api.boundary.assign_connection_geometry(model,
rel_space_boundary=element,
outer_boundary=[(0., 0.), (1., 0.), (1., 1.), (0., 1.)],
location=[0., 0., 0.], axis=[1., 0., 0.], ref_direction=[0., 0., 1.],
)
"""
usecase = Usecase()
usecase.file = file
usecase.rel_space_boundary = rel_space_boundary
usecase.outer_boundary = V(outer_boundary)
usecase.inner_boundaries = V(inner_boundaries or [])
usecase.location = V(location)
usecase.axis = V(axis)
usecase.ref_direction = V(ref_direction)
usecase.unit_scale = unit_scale if unit_scale is not None else ifcopenshell.util.unit.calculate_unit_scale(file)
return usecase.execute()
class Usecase:
file: ifcopenshell.file
rel_space_boundary: ifcopenshell.entity_instance
outer_boundary: npt.NDArray
inner_boundaries: npt.NDArray
location: npt.NDArray
axis: npt.NDArray
ref_direction: npt.NDArray
unit_scale: float
def execute(self):
outer_boundary = self.create_polyline(self.outer_boundary)
inner_boundaries = tuple(self.create_polyline(boundary) for boundary in self.inner_boundaries)
plane = self.create_plane(self.location, self.axis, self.ref_direction)
curve_bounded_plane = self.file.createIfcCurveBoundedPlane(plane, outer_boundary, inner_boundaries)
connection_geometry = self.file.createIfcConnectionSurfaceGeometry(curve_bounded_plane)
self.rel_space_boundary.ConnectionGeometry = connection_geometry
def create_point(self, point: npt.NDArray) -> ifcopenshell.entity_instance:
return self.file.create_entity("IfcCartesianPoint", ifc_safe_vector_type(point / self.unit_scale))
def close_polyline(
self, points: tuple[ifcopenshell.entity_instance, ...]
) -> tuple[ifcopenshell.entity_instance, ...]:
return points + (points[0],)
def create_polyline(self, points: npt.NDArray) -> ifcopenshell.entity_instance:
if np.allclose(points[0], points[-1]):
points = points[0 : len(points) - 1]
ifc_points = tuple(self.create_point(point) for point in points)
return self.file.createIfcPolyline(self.close_polyline(ifc_points))
def create_plane(
self, location: npt.NDArray, axis: npt.NDArray, ref_direction: npt.NDArray
) -> ifcopenshell.entity_instance:
return self.file.createIfcPlane(
self.file.createIfcAxis2Placement3D(
self.create_point(location),
self.file.createIfcDirection(ifc_safe_vector_type(axis)),
self.file.createIfcDirection(ifc_safe_vector_type(ref_direction)),
)
)
@@ -0,0 +1,40 @@
# IfcOpenShell - IFC toolkit and geometry engine
# Copyright (C) 2023 Dion Moult <dion@thinkmoult.com>
#
# This file is part of IfcOpenShell.
#
# IfcOpenShell is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# IfcOpenShell is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with IfcOpenShell. If not, see <http://www.gnu.org/licenses/>.
import ifcopenshell.util.element
def copy_boundary(file: ifcopenshell.file, boundary: ifcopenshell.entity_instance) -> ifcopenshell.entity_instance:
"""Copies a space boundary
:param boundary: The IfcRelSpaceBoundary you want to copy.
:return: Duplicate of the IfcRelSpaceBoundary
Example:
# A boring boundary with no geometry. Note that this boundary is
# invalid and does not relate to any space or building element.
boundary = ifcopenshell.api.root.create_entity(model, ifc_class="IfcRelSpaceBoundary")
# And now we have two
boundary_copy = ifcopenshell.api.boundary.copy_boundary(model, boundary=boundary)
"""
result = ifcopenshell.util.element.copy(file, boundary)
if result.ConnectionGeometry:
result.ConnectionGeometry = ifcopenshell.util.element.copy_deep(file, result.ConnectionGeometry)
return result
@@ -0,0 +1,60 @@
# IfcOpenShell - IFC toolkit and geometry engine
# Copyright (C) 2021 Dion Moult <dion@thinkmoult.com>
#
# This file is part of IfcOpenShell.
#
# IfcOpenShell is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# IfcOpenShell is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with IfcOpenShell. If not, see <http://www.gnu.org/licenses/>.
from typing import Optional
import ifcopenshell
def edit_attributes(
file: ifcopenshell.file,
entity: ifcopenshell.entity_instance,
relating_space: ifcopenshell.entity_instance,
related_building_element: ifcopenshell.entity_instance,
parent_boundary: Optional[ifcopenshell.entity_instance] = None,
corresponding_boundary: Optional[ifcopenshell.entity_instance] = None,
physical_or_virtual: str = "NOTDEFINED",
internal_or_external: str = "NOTDEFINED",
) -> None:
"""Modify the relationships of a space boundary relationship
:param entity: The IfcRelSpaceBoundary to modify
:param relating_space: The IfcSpace or IfcExternalSpatialElement that
the space boundary is related to.
:param related_building_element: The IfcElement that defines the
boundary, typically an IfcWall.
:param parent_boundary: A parent IfcRelSpaceBoundary, only provided if
this is an inner boundary. This can apply to 1st and 2nd level
boundaries.
:param corresponding_boundary: The other IfcRelSpaceBoundary on the
other side of the related element. The pair together represents a
thermal boundary. This only applies to 2nd level boundaries.
:param physical_or_virtual: IfcPhysicalOrVirtualEnum value: "PHYSICAL",
"VIRTUAL", or "NOTDEFINED".
:param internal_or_external: IfcInternalOrExternalEnum value:
"INTERNAL", "EXTERNAL", "EXTERNAL_EARTH", "EXTERNAL_WATER",
"EXTERNAL_FIRE", or "NOTDEFINED".
:return: None
"""
entity.RelatingSpace = relating_space
entity.RelatedBuildingElement = related_building_element
if hasattr(entity, "ParentBoundary"):
entity.ParentBoundary = parent_boundary
if hasattr(entity, "CorrespondingBoundary"):
entity.CorrespondingBoundary = corresponding_boundary
entity.PhysicalOrVirtualBoundary = physical_or_virtual
entity.InternalOrExternalBoundary = internal_or_external
@@ -0,0 +1,48 @@
# IfcOpenShell - IFC toolkit and geometry engine
# Copyright (C) 2023 Dion Moult <dion@thinkmoult.com>
#
# This file is part of IfcOpenShell.
#
# IfcOpenShell is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# IfcOpenShell is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with IfcOpenShell. If not, see <http://www.gnu.org/licenses/>.
import ifcopenshell
import ifcopenshell.util.element
def remove_boundary(file: ifcopenshell.file, boundary: ifcopenshell.entity_instance) -> None:
"""Removes a space boundary
The relating space or related building element is untouched. Only the
boundary and its connection geometry is removed.
:param boundary: The IfcRelSpaceBoundary you want to remove.
:return: None
Example:
# A boring boundary with no geometry. Note that this boundary is
# invalid and does not relate to any space or building element.
boundary = ifcopenshell.api.root.create_entity(model, ifc_class="IfcRelSpaceBoundary")
# Let's remove it!
ifcopenshell.api.boundary.remove_boundary(model, boundary=boundary)
"""
geometry = boundary.ConnectionGeometry
if geometry:
boundary.ConnectionGeometry = None
ifcopenshell.util.element.remove_deep2(file, geometry)
history = boundary.OwnerHistory
file.remove(boundary)
if history:
ifcopenshell.util.element.remove_deep2(file, history)