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,42 @@
# 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/>.
"""Handles the definition of cross sectional profiles
Maintaining a clean profile library is important for structural simulations and
identification of standardised profiles for fabrication and carbon counting.
"""
from .. import wrap_usecases
from .add_arbitrary_profile import add_arbitrary_profile
from .add_arbitrary_profile_with_voids import add_arbitrary_profile_with_voids
from .add_parameterized_profile import add_parameterized_profile
from .copy_profile import copy_profile
from .edit_profile import edit_profile
from .remove_profile import remove_profile
wrap_usecases(__path__, __name__)
__all__ = [
"add_arbitrary_profile",
"add_arbitrary_profile_with_voids",
"add_parameterized_profile",
"copy_profile",
"edit_profile",
"remove_profile",
]
@@ -0,0 +1,83 @@
# 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/>.
from typing import Optional, Union
import numpy.typing as npt
import ifcopenshell.util.unit
from ifcopenshell.util.shape_builder import SequenceOfVectors, V, ifc_safe_vector_type
def add_arbitrary_profile(
file: ifcopenshell.file, profile: SequenceOfVectors, name: Optional[str] = None
) -> ifcopenshell.entity_instance:
"""Adds a new arbitrary polyline-based profile
The profile is represented as a polyline defined by a list of
coordinates. Only straight segments are allowed. Coordinates must be
provided in SI meters.
To represent a closed curve, the first and last coordinate must be
identical.
:param profile: A list of coordinates
:param name: If the profile is semantically significant (i.e. to be
managed and reused by the user) then it must be named. Otherwise,
this may be left as none.
:return: The newly created IfcArbitraryClosedProfileDef
Example:
.. code:: python
# A 10mm by 100mm rectangle, such that might be used as a wooden
# skirting board or kick plate.
square = ifcopenshell.api.profile.add_arbitrary_profile(model,
profile=[(0., 0.), (.01, 0.), (.01, .1), (0., .1), (0., 0.)],
name="SK01 Profile")
"""
usecase = Usecase()
usecase.file = file
return usecase.execute(V(profile), name)
class Usecase:
file: ifcopenshell.file
def execute(self, profile: npt.NDArray, name: Union[str, None]):
self.unit_scale = ifcopenshell.util.unit.calculate_unit_scale(self.file)
points = self.convert_si_to_unit(profile)
if self.file.schema == "IFC2X3":
curve = self.file.create_entity(
"IfcPolyline",
[self.file.create_entity("IfcCartesianPoint", ifc_safe_vector_type(p)) for p in points],
)
else:
dimensions = points.shape[1]
if dimensions == 2:
ifc_points = self.file.create_entity("IfcCartesianPointList2D", ifc_safe_vector_type(points))
elif dimensions == 3:
ifc_points = self.file.create_entity("IfcCartesianPointList3D", ifc_safe_vector_type(points))
else:
assert False, f"Invalid dimensions: {dimensions}."
curve = self.file.create_entity("IfcIndexedPolyCurve", ifc_points)
return self.file.create_entity("IfcArbitraryClosedProfileDef", "AREA", name, curve)
def convert_si_to_unit(self, co: npt.NDArray) -> npt.NDArray:
return co / self.unit_scale
@@ -0,0 +1,114 @@
# 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/>.
from typing import Optional, Union
import numpy.typing as npt
import ifcopenshell.util.unit
from ifcopenshell.util.shape_builder import SequenceOfVectors, V, ifc_safe_vector_type
def add_arbitrary_profile_with_voids(
file: ifcopenshell.file,
outer_profile: SequenceOfVectors,
inner_profiles: list[SequenceOfVectors],
name: Optional[str] = None,
) -> ifcopenshell.entity_instance:
"""Adds a new arbitrary polyline-based profile with voids
The outer profile is represented as a polyline defined by a list of
coordinates. Only straight segments are allowed. Coordinates must be
provided in SI meters.
To represent a closed curve, the first and last coordinate must be
identical.
The inner profiles are represented as a list of polylines.
Every polyline in defined by a list of coordinates.
Only straight segments are allowed. Coordinates must be
provided in SI meters.
:param outer_profile: A list of coordinates
:param inner_profiles: A list of polylines
:param name: If the profile is semantically significant (i.e. to be
managed and reused by the user) then it must be named. Otherwise,
this may be left as none.
:return: The newly created IfcArbitraryProfileDefWithVoids
Example:
.. code:: python
# A 400mm by 400mm square with a 200mm by 200mm hole in it.
square_with_hole = ifcopenshell.api.profile.add_arbitrary_profile_with_voids(model,
outer_profile=[(0., 0.), (.4, 0.), (.4, .4), (0., .4), (0., 0.)],
inner_profiles=[[(0.1, 0.1), (0.3, 0.1), (0.3, 0.3), (0.1, 0.3), (0.1, 0.1)]],
name="SK01 Hole Profile")
"""
usecase = Usecase()
usecase.file = file
return usecase.execute(V(outer_profile), [V(p) for p in inner_profiles], name)
class Usecase:
file: ifcopenshell.file
def execute(
self,
outer_profile: npt.NDArray,
inner_profiles: list[npt.NDArray],
name: Union[str, None],
):
self.unit_scale = ifcopenshell.util.unit.calculate_unit_scale(self.file)
outer_points = self.convert_si_to_unit(outer_profile)
inner_points: list[npt.NDArray] = []
for inner_profile in inner_profiles:
inner_points.append(self.convert_si_to_unit(inner_profile))
inner_curves: list[ifcopenshell.entity_instance] = []
if self.file.schema == "IFC2X3":
outer_curve = self.file.create_entity(
"IfcPolyline",
[self.file.create_entity("IfcCartesianPoint", ifc_safe_vector_type(p)) for p in outer_points],
)
for inner_point in inner_points:
inner_curves.append(
self.file.create_entity(
"IfcPolyline",
[self.file.create_entity("IfcCartesianPoint", ifc_safe_vector_type(p)) for p in inner_point],
)
)
else:
outer_curve = self.file.create_entity(
"IfcIndexedPolyCurve",
(self.file.create_entity("IfcCartesianPointList3D", ifc_safe_vector_type(outer_points))),
)
for inner_point in inner_points:
dimensions = inner_point.shape[1]
if dimensions == 2:
ifc_points = self.file.create_entity("IfcCartesianPointList2D", ifc_safe_vector_type(inner_point))
elif dimensions == 3:
ifc_points = self.file.create_entity("IfcCartesianPointList3D", ifc_safe_vector_type(inner_point))
else:
assert False, f"Invalid dimensions: {dimensions}."
inner_curves.append(self.file.create_entity("IfcIndexedPolyCurve", ifc_points))
return self.file.create_entity("IfcArbitraryProfileDefWithVoids", "AREA", name, outer_curve, inner_curves)
def convert_si_to_unit(self, co: npt.NDArray) -> npt.NDArray:
return co / self.unit_scale
@@ -0,0 +1,50 @@
# 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 Literal
import ifcopenshell
ProfileType = Literal["AREA", "CURVE"]
def add_parameterized_profile(
file: ifcopenshell.file, ifc_class: str, profile_type: str = "AREA"
) -> ifcopenshell.entity_instance:
"""Adds a new parameterised profile
IFC offers parameterised profiles for common standardised hot roll
steel sections and common concrete forms. A full list is available on
the IFC documentation as subclasses of IfcParameterizedProfileDef.
Currently, this API has no benefit over directly calling
ifcopenshell.file.create_entity.
:param ifc_class: The subclass of IfcParameterizedProfileDef that you'd
like to create.
:param profile_type:
:return: The newly created element depending on the specified ifc_class.
Example:
.. code:: python
circle = ifcopenshell.api.profile.add_parameterized_profile(model,
ifc_class="IfcCircleProfileDef")
circle.Radius = 1.
"""
return file.create_entity(ifc_class, ProfileType=profile_type)
@@ -0,0 +1,47 @@
# 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/>.
import ifcopenshell
import ifcopenshell.util.element
def copy_profile(file: ifcopenshell.file, profile: ifcopenshell.entity_instance) -> ifcopenshell.entity_instance:
"""Copies a profile
All profile's psets are copied. The copied profile is not
associated to any elements.
:param profile: The IfcProfileDef to copy
:return: The new copy of the profile
Example:
.. code:: python
profile = ifcopenshell.api.profile.add_profile(model, ifc_class="IfcRectangleProfileDef")
# Let's duplicate the rectangle profile
profile_copy = ifcopenshell.api.profile.copy_profile(model, profile=profile)
"""
new_profile = ifcopenshell.util.element.copy_deep(file, profile)
inverses = file.get_inverse(profile)
psets = [i for i in inverses if i.is_a("IfcProfileProperties")]
for pset in psets:
new_pset = ifcopenshell.util.element.copy(file, pset)
new_pset.ProfileDefinition = new_profile
return new_profile
@@ -0,0 +1,45 @@
# 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 Any
import ifcopenshell
def edit_profile(file: ifcopenshell.file, profile: ifcopenshell.entity_instance, attributes: dict[str, Any]) -> None:
"""Edits the attributes of an IfcProfileDef
For more information about the attributes and data types of an
IfcProfileDef, consult the IFC documentation.
:param profile: The IfcProfileDef entity you want to edit
:param attributes: a dictionary of attribute names and values.
:return: None
Example:
.. code:: python
circle = ifcopenshell.api.profile.add_parameterized_profile(model,
ifc_class="IfcCircleProfileDef")
circle = 1.
ifcopenshell.api.profile.edit_profile(model,
profile=circle, attributes={"ProfileName": "1000mm Dia"})
"""
for name, value in attributes.items():
setattr(profile, name, value)
@@ -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/>.
import ifcopenshell
import ifcopenshell.api.pset
import ifcopenshell.util.element
def remove_profile(file: ifcopenshell.file, profile: ifcopenshell.entity_instance) -> None:
"""Removes a profile
:param profile: The IfcProfileDef to remove.
Example:
.. code:: python
circle = ifcopenshell.api.profile.add_parameterized_profile(model,
ifc_class="IfcCircleProfileDef")
circle = 1.
ifcopenshell.api.profile.remove_profile(model, profile=circle)
"""
is_ifc2x3 = file.schema == "IFC2X3"
subelements = set()
for attribute in profile:
if isinstance(attribute, ifcopenshell.entity_instance):
subelements.add(attribute)
# Clean up profile proprty sets.
profile_psets = []
if is_ifc2x3:
for pset in file.by_type("IfcProfileProperties"):
if pset.ProfileDefinition != profile:
continue
profile_psets.append(pset)
else:
profile_psets = profile.HasProperties
for pset in profile_psets:
ifcopenshell.api.pset.remove_pset(file, product=profile, pset=pset)
file.remove(profile)
for subelement in subelements:
ifcopenshell.util.element.remove_deep2(file, subelement)