First Commit
This commit is contained in:
@@ -0,0 +1,89 @@
|
||||
# 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/>.
|
||||
|
||||
"""Manage physical materials (concrete, steel, etc) and their association to
|
||||
elements
|
||||
|
||||
IFC supports both simple materials and parametric materials (materials that
|
||||
have layered thicknesses or cross sectional profiles).
|
||||
|
||||
Parametric materials will include parametric constraints on the geometry of
|
||||
the element. These API functions do not cover that responsibility. See
|
||||
:mod:`ifcopenshell.api.geometry`.
|
||||
|
||||
Note that this API only covers physical materials, not visual styles. If you
|
||||
want to look at visual styles such as colours, transparency, shading, or
|
||||
rendering options, see :mod:`ifcopenshell.api.style`.
|
||||
"""
|
||||
|
||||
from .. import wrap_usecases
|
||||
from .add_constituent import add_constituent
|
||||
from .add_layer import add_layer
|
||||
from .add_list_item import add_list_item
|
||||
from .add_material import add_material
|
||||
from .add_material_set import add_material_set
|
||||
from .add_profile import add_profile
|
||||
from .assign_material import assign_material
|
||||
from .assign_profile import assign_profile
|
||||
from .copy_material import copy_material
|
||||
from .edit_assigned_material import edit_assigned_material
|
||||
from .edit_constituent import edit_constituent
|
||||
from .edit_layer import edit_layer
|
||||
from .edit_layer_usage import edit_layer_usage
|
||||
from .edit_material import edit_material
|
||||
from .edit_profile import edit_profile
|
||||
from .edit_profile_usage import edit_profile_usage
|
||||
from .remove_constituent import remove_constituent
|
||||
from .remove_layer import remove_layer
|
||||
from .remove_list_item import remove_list_item
|
||||
from .remove_material import remove_material
|
||||
from .remove_material_set import remove_material_set
|
||||
from .remove_profile import remove_profile
|
||||
from .reorder_set_item import reorder_set_item
|
||||
from .set_shape_aspect_constituents import set_shape_aspect_constituents
|
||||
from .unassign_material import unassign_material
|
||||
|
||||
wrap_usecases(__path__, __name__)
|
||||
|
||||
__all__ = [
|
||||
"add_constituent",
|
||||
"add_layer",
|
||||
"add_list_item",
|
||||
"add_material",
|
||||
"add_material_set",
|
||||
"add_profile",
|
||||
"assign_material",
|
||||
"assign_profile",
|
||||
"copy_material",
|
||||
"edit_assigned_material",
|
||||
"edit_constituent",
|
||||
"edit_layer",
|
||||
"edit_layer_usage",
|
||||
"edit_material",
|
||||
"edit_profile",
|
||||
"edit_profile_usage",
|
||||
"remove_constituent",
|
||||
"remove_layer",
|
||||
"remove_list_item",
|
||||
"remove_material",
|
||||
"remove_material_set",
|
||||
"remove_profile",
|
||||
"reorder_set_item",
|
||||
"set_shape_aspect_constituents",
|
||||
"unassign_material",
|
||||
]
|
||||
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
@@ -0,0 +1,92 @@
|
||||
# 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 add_constituent(
|
||||
file: ifcopenshell.file,
|
||||
constituent_set: ifcopenshell.entity_instance,
|
||||
material: ifcopenshell.entity_instance,
|
||||
name: Optional[str] = None,
|
||||
) -> ifcopenshell.entity_instance:
|
||||
"""Adds a new constituent to a constituent set
|
||||
|
||||
A constituent describes how a portion of an object is made out of a
|
||||
material whereas other portions of the object is made out of other
|
||||
materials. For example, a window might be made out of an aluminium frame
|
||||
and a glass panel. The aluminium used for the frame is one constituent
|
||||
of the material, and glass would be another constituent. Another example
|
||||
might be concrete, where one constituent might be cement, and another
|
||||
constituent might be binder. In the case of the window, the constituent
|
||||
is represented explicitly by the geometry of the window frame and the
|
||||
geometry of the window panel. In the case of a concrete slab, the
|
||||
constituents might be represented in terms of percentages.
|
||||
|
||||
Constituents are not available in IFC2X3.
|
||||
|
||||
:param constituent_set: The IfcMaterialConstituentSet that the
|
||||
constituent is part of. The constituent set represents a group of
|
||||
constituents. See ifcopenshell.api.material.add_material_set for
|
||||
information on how to add a constituent set.
|
||||
:param material: The IfcMaterial that the constituent is made out of.
|
||||
:param name: An optional name of the constituent.
|
||||
:return: The newly created IfcMaterialConstituent
|
||||
|
||||
Example:
|
||||
|
||||
.. code:: python
|
||||
|
||||
# Let's imagine we have a window type that has an aluminium frame
|
||||
# and a glass glazing panel. Notice we are assigning to the type
|
||||
# only, as all occurrences of that type will automatically inherit
|
||||
# the material.
|
||||
window_type = ifcopenshell.api.root.create_entity(model, ifc_class="IfcWindowType")
|
||||
|
||||
# First, let's create a constituent set. This will later be assigned
|
||||
# to our window element.
|
||||
material_set = ifcopenshell.api.material.add_material_set(model,
|
||||
name="Window", set_type="IfcMaterialConstituentSet")
|
||||
|
||||
# Let's create a few materials, it's important to also give them
|
||||
# categories. This makes it easy for model recipients to do things
|
||||
# like "show me everything made out of aluminium / concrete / steel
|
||||
# / glass / etc". The IFC specification states a list of categories
|
||||
# you can use.
|
||||
aluminium = ifcopenshell.api.material.add_material(model, name="AL01", category="aluminium")
|
||||
glass = ifcopenshell.api.material.add_material(model, name="GLZ01", category="glass")
|
||||
|
||||
# Now let's use those materials as two constituents in our set.
|
||||
ifcopenshell.api.material.add_constituent(model,
|
||||
constituent_set=material_set, material=aluminium, name="Framing")
|
||||
ifcopenshell.api.material.add_constituent(model,
|
||||
constituent_set=material_set, material=glass, name="Glazing")
|
||||
|
||||
# Great! Let's assign our material set to our window type.
|
||||
# We're technically not done here, we might want to add geometry to
|
||||
# our window too, but to keep this example simple, geometry is
|
||||
# optional and it is enough to say that this window is made out of
|
||||
# aluminium and glass.
|
||||
ifcopenshell.api.material.assign_material(model, products=[window_type], material=material_set)
|
||||
"""
|
||||
constituents = list(constituent_set.MaterialConstituents or [])
|
||||
constituent = file.create_entity("IfcMaterialConstituent", Material=material, Name=name)
|
||||
constituents.append(constituent)
|
||||
constituent_set.MaterialConstituents = constituents
|
||||
return constituent
|
||||
@@ -0,0 +1,95 @@
|
||||
# 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
|
||||
import ifcopenshell.util.unit
|
||||
|
||||
|
||||
def add_layer(
|
||||
file: ifcopenshell.file,
|
||||
layer_set: ifcopenshell.entity_instance,
|
||||
material: ifcopenshell.entity_instance,
|
||||
name: Optional[str] = None,
|
||||
) -> ifcopenshell.entity_instance:
|
||||
"""Adds a new layer to a layer set
|
||||
|
||||
A layer represents a portion of material within a layered build up,
|
||||
defined by a thickness. Typical layered construction includes walls and
|
||||
slabs, where a wall might include a layer of finish, a layer of
|
||||
structure, a layer of insulation, and so on. It is recommended to define
|
||||
layered construction this way where it is unnecessary to define the
|
||||
exact geometry of how the wall or slab will be built, and it will
|
||||
instead be determined on site by a trade.
|
||||
|
||||
Layers are defined in a particular order and thickness, so that it is
|
||||
clear which layer comes next.
|
||||
|
||||
:param layer_set: The IfcMaterialLayerSet that the layer is part of. The
|
||||
layer set represents a group of layers. See
|
||||
ifcopenshell.api.material.add_material_set for more information on
|
||||
how to add a layer set.
|
||||
:param material: The IfcMaterial that the layer is made out of.
|
||||
:param name: An optional name of the layer.
|
||||
:return: The newly created IfcMaterialLayer
|
||||
|
||||
Example:
|
||||
|
||||
.. code:: python
|
||||
|
||||
# Let's imagine we have a wall type that has two layers of
|
||||
# gypsum with steel studs inside. Notice we are assigning to
|
||||
# the type only, as all occurrences of that type will automatically
|
||||
# inherit the material.
|
||||
wall_type = ifcopenshell.api.root.create_entity(model, ifc_class="IfcWallType", name="WAL01")
|
||||
|
||||
# First, let's create a material set. This will later be assigned
|
||||
# to our wall type element.
|
||||
material_set = ifcopenshell.api.material.add_material_set(model,
|
||||
name="GYP-ST-GYP", set_type="IfcMaterialLayerSet")
|
||||
|
||||
# Let's create a few materials, it's important to also give them
|
||||
# categories. This makes it easy for model recipients to do things
|
||||
# like "show me everything made out of aluminium / concrete / steel
|
||||
# / glass / etc". The IFC specification states a list of categories
|
||||
# you can use.
|
||||
gypsum = ifcopenshell.api.material.add_material(model, name="PB01", category="gypsum")
|
||||
steel = ifcopenshell.api.material.add_material(model, name="ST01", category="steel")
|
||||
|
||||
# Now let's use those materials as three layers in our set, such
|
||||
# that the steel studs are sandwiched by the gypsum. Let's imagine
|
||||
# we're setting the layer thickness in millimeters.
|
||||
layer = ifcopenshell.api.material.add_layer(model, layer_set=material_set, material=gypsum)
|
||||
ifcopenshell.api.material.edit_layer(model, layer=layer, attributes={"LayerThickness": 13})
|
||||
layer = ifcopenshell.api.material.add_layer(model, layer_set=material_set, material=steel)
|
||||
ifcopenshell.api.material.edit_layer(model, layer=layer, attributes={"LayerThickness": 92})
|
||||
layer = ifcopenshell.api.material.add_layer(model, layer_set=material_set, material=gypsum)
|
||||
ifcopenshell.api.material.edit_layer(model, layer=layer, attributes={"LayerThickness": 13})
|
||||
|
||||
# Great! Let's assign our material set to our wall type.
|
||||
ifcopenshell.api.material.assign_material(model, products=[wall_type], material=material_set)
|
||||
"""
|
||||
unit_scale = ifcopenshell.util.unit.calculate_unit_scale(file)
|
||||
layers = list(layer_set.MaterialLayers or [])
|
||||
if file.schema == "IFC2X3":
|
||||
layer = file.create_entity("IfcMaterialLayer", Material=material, LayerThickness=0.1 / unit_scale)
|
||||
else:
|
||||
layer = file.create_entity("IfcMaterialLayer", Material=material, LayerThickness=0.1 / unit_scale, Name=name)
|
||||
layers.append(layer)
|
||||
layer_set.MaterialLayers = layers
|
||||
return layer
|
||||
@@ -0,0 +1,82 @@
|
||||
# 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
|
||||
|
||||
|
||||
def add_list_item(
|
||||
file: ifcopenshell.file, material_list: ifcopenshell.entity_instance, material: ifcopenshell.entity_instance
|
||||
) -> None:
|
||||
"""Adds a new material in a list of materials
|
||||
|
||||
In IFC2X3, if you wanted an object to have multiple materials (i.e. a
|
||||
composite material) you would assign the object to a material list,
|
||||
which would contain a list of materials. For example, a window might
|
||||
have a list of 2 materials, one being aluminium for the frame, and
|
||||
another being glass for the panel.
|
||||
|
||||
In IFC4 and above, this is deprecated and should not be used. Instead,
|
||||
you should use constituent sets instead, which achieve the same thing
|
||||
but are more powerful as they allow you to define the properties of the
|
||||
constituents too.
|
||||
|
||||
However if you're stuck on IFC2X3, you have my condolences as well as
|
||||
this function.
|
||||
|
||||
:param material_list: The IfcMaterialList the material should be added
|
||||
to.
|
||||
:param material: The IfcMaterial to add to the list
|
||||
:return: None
|
||||
|
||||
Example:
|
||||
|
||||
.. code:: python
|
||||
|
||||
# Let's imagine we have a window type that has an aluminium frame
|
||||
# and a glass glazing panel. Notice we are assigning to the type
|
||||
# only, as all occurrences of that type will automatically inherit
|
||||
# the material.
|
||||
window_type = ifcopenshell.api.root.create_entity(model, ifc_class="IfcWindowType")
|
||||
|
||||
# First, let's create a list. This will later be assigned to our
|
||||
# window element.
|
||||
material_set = ifcopenshell.api.material.add_material_set(model,
|
||||
name="Window", set_type="IfcMaterialList")
|
||||
|
||||
# Let's create a few materials, it's important to also give them
|
||||
# categories. This makes it easy for model recipients to do things
|
||||
# like "show me everything made out of aluminium / concrete / steel
|
||||
# / glass / etc". The IFC specification states a list of categories
|
||||
# you can use.
|
||||
aluminium = ifcopenshell.api.material.add_material(model, name="AL01", category="aluminium")
|
||||
glass = ifcopenshell.api.material.add_material(model, name="GLZ01", category="glass")
|
||||
|
||||
# Now let's use those materials as two items in our list.
|
||||
ifcopenshell.api.material.add_list_item(model, material_list=material_set, material=aluminium)
|
||||
ifcopenshell.api.material.add_list_item(model, material_list=material_set, material=glass)
|
||||
|
||||
# Great! Let's assign our material set to our window type.
|
||||
# We're technically not done here, we might want to add geometry to
|
||||
# our window too, but to keep this example simple, geometry is
|
||||
# optional and it is enough to say that this window is made out of
|
||||
# aluminium and glass.
|
||||
ifcopenshell.api.material.assign_material(model, products=[window_type], material=material_set)
|
||||
"""
|
||||
materials = list(material_list.Materials or [])
|
||||
materials.append(material)
|
||||
material_list.Materials = materials
|
||||
@@ -0,0 +1,86 @@
|
||||
# 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 add_material(
|
||||
file: ifcopenshell.file,
|
||||
name: Optional[str] = None,
|
||||
category: Optional[str] = None,
|
||||
description: Optional[str] = None,
|
||||
) -> ifcopenshell.entity_instance:
|
||||
"""Adds a new material
|
||||
|
||||
A material in IFC represents a physical material, such as timber, steel,
|
||||
concrete, aluminium, etc. It may also contain physical properties used
|
||||
for structural or lighting simulation. Note that unlike the computer
|
||||
graphics industry, a material by itself does not define any colour or
|
||||
lighting information. Colours in IFC are known as "styles", and an IFC
|
||||
material may or may not have any style information associated with it.
|
||||
See ifcopenshell.api.style for more information.
|
||||
|
||||
A material is typically given a code name which is used by architects in
|
||||
elevations and details when tagging finishes. Materials are also useful
|
||||
to structural engineers in specifying the exact types of concrete and
|
||||
steel to be used in structural simulations.
|
||||
|
||||
In addition, materials can belong to a category. Specifying this
|
||||
category is critical to allow model recipients to make simple queries
|
||||
like "show me all concrete / steel" elements in the model. Without
|
||||
standardised category naming of all materials, this type of query
|
||||
becomes a bespoke and inefficient task. A list of categories are:
|
||||
'concrete', 'steel', 'aluminium', 'block', 'brick', 'stone', 'wood',
|
||||
'glass', 'gypsum', 'plastic', and 'earth'. The user is allowed to
|
||||
specify their own category instead if none of these categories are
|
||||
appropriate.
|
||||
|
||||
Note that categories are not available in IFC2X3. This shortcoming is
|
||||
one of the big reasons projects should upgrade to IFC4.
|
||||
|
||||
Additionally, a material's description provides more information beyond
|
||||
its name or category.
|
||||
|
||||
:param name: The name of the material, typically tagged in a finishes
|
||||
drawing or schedule.
|
||||
:param category: The category of the material.
|
||||
:param description: A description of the material.
|
||||
:return: The newly created IfcMaterial
|
||||
|
||||
Example:
|
||||
|
||||
.. code:: python
|
||||
|
||||
# Let's create two materials with their respective categories
|
||||
concrete = ifcopenshell.api.material.add_material(model, name="CON01", category="concrete", description="Garage Slab")
|
||||
steel = ifcopenshell.api.material.add_material(model, name="ST01", category="steel", description="Corten Steel")
|
||||
|
||||
# Let's imagine an urban concrete bench which is purely made out of concrete
|
||||
concrete_bench = ifcopenshell.api.root.create_entity(model, ifc_class="IfcFurnitureType")
|
||||
|
||||
# Assign the concrete material to that bench. Note that no colour
|
||||
# "Style" has been specified.
|
||||
ifcopenshell.api.material.assign_material(model, products=[concrete_bench], material=concrete)
|
||||
"""
|
||||
material = file.create_entity("IfcMaterial", **{"Name": name or "Unnamed"})
|
||||
if category:
|
||||
material.Category = category
|
||||
if description:
|
||||
material.Description = description
|
||||
return material
|
||||
@@ -0,0 +1,117 @@
|
||||
# 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 Literal
|
||||
|
||||
import ifcopenshell
|
||||
|
||||
MATERIAL_SET_TYPE = Literal[
|
||||
"IfcMaterialLayerSet",
|
||||
"IfcMaterialProfileSet",
|
||||
"IfcMaterialConstituentSet",
|
||||
"IfcMaterialList",
|
||||
]
|
||||
|
||||
|
||||
def add_material_set(
|
||||
file: ifcopenshell.file, name: str = "Unnamed", set_type: MATERIAL_SET_TYPE = "IfcMaterialConstituentSet"
|
||||
) -> ifcopenshell.entity_instance:
|
||||
"""Adds a new material set
|
||||
|
||||
IFC allows you to state that objects are made out of multiple materials.
|
||||
These are known generically as material sets, but may also be called
|
||||
layered materials, composite materials, or other names in software.
|
||||
|
||||
There are three types of material sets:
|
||||
|
||||
- A layer set, used for layered construction such as walls, where the
|
||||
element is parametrically made out of extruded layers, each layer
|
||||
having a thickness defined. Even though this is known as a layer
|
||||
"set" it is still recommended to use it for all standared layered
|
||||
construction as it describes the intent of the element to be layered
|
||||
construction and thus can be used for parametric editing.
|
||||
- A profile set, used for profiled construction such as beams or
|
||||
columns, where the element is parametrically made out of one or more
|
||||
extruded profiles, where each profile may be parametric from a
|
||||
standard section (e.g. standardised steel profile) or an arbitrary
|
||||
shape (e.g. cold rolled sections, or skirtings, moldings, etc). Note
|
||||
that even though this is called a profile "set", it should still be
|
||||
used even if there is only a single profile. This is not available in
|
||||
IFC2X3.
|
||||
- A constituent set, used for arbitrary composite construction where
|
||||
the object is made out of multiple materials. The constituents may be
|
||||
explicitly defined via a shape, such as a window where the frame
|
||||
geometry is made from one material and the panel geometry is made
|
||||
from another material. Alternatively, the constituents may be
|
||||
represented in terms of percentages, such as in mixtures like
|
||||
concrete where there might be a percentage constituent of cement and
|
||||
another percentage constituent of binder. This is not available in
|
||||
IFC2X3.
|
||||
|
||||
There is also a fourth material set known as a material list, which is a
|
||||
legacy type of set used by IFC2X3. It should not be used on IFC4 and
|
||||
above, and constituent sets should be used instead.
|
||||
|
||||
:param name: The name of the material set, which may be purely
|
||||
descriptive or annotated in drawings. Defaults to "Unnamed".
|
||||
:param set_type: What type of set you want to create, chosen from
|
||||
IfcMaterialLayerSet, IfcMaterialProfileSet,
|
||||
IfcMaterialConstituentSet, or IfcMaterialList. Defaults to
|
||||
IfcMaterialConstituentSet.
|
||||
:return: The newly created material set element
|
||||
|
||||
Example:
|
||||
|
||||
.. code:: python
|
||||
|
||||
# Let's imagine we have a wall type that has two layers of
|
||||
# gypsum with steel studs inside. Notice we are assigning to
|
||||
# the type only, as all occurrences of that type will automatically
|
||||
# inherit the material.
|
||||
wall_type = ifcopenshell.api.root.create_entity(model, ifc_class="IfcWallType", name="WAL01")
|
||||
|
||||
# First, let's create a material set. This will later be assigned
|
||||
# to our wall type element.
|
||||
material_set = ifcopenshell.api.material.add_material_set(model,
|
||||
name="GYP-ST-GYP", set_type="IfcMaterialLayerSet")
|
||||
|
||||
# Let's create a few materials, it's important to also give them
|
||||
# categories. This makes it easy for model recipients to do things
|
||||
# like "show me everything made out of aluminium / concrete / steel
|
||||
# / glass / etc". The IFC specification states a list of categories
|
||||
# you can use.
|
||||
gypsum = ifcopenshell.api.material.add_material(model, name="PB01", category="gypsum")
|
||||
steel = ifcopenshell.api.material.add_material(model, name="ST01", category="steel")
|
||||
|
||||
# Now let's use those materials as three layers in our set, such
|
||||
# that the steel studs are sandwiched by the gypsum. Let's imagine
|
||||
# we're setting the layer thickness in millimeters.
|
||||
layer = ifcopenshell.api.material.add_layer(model, layer_set=material_set, material=gypsum)
|
||||
ifcopenshell.api.material.edit_layer(model, layer=layer, attributes={"LayerThickness": 13})
|
||||
layer = ifcopenshell.api.material.add_layer(model, layer_set=material_set, material=steel)
|
||||
ifcopenshell.api.material.edit_layer(model, layer=layer, attributes={"LayerThickness": 92})
|
||||
layer = ifcopenshell.api.material.add_layer(model, layer_set=material_set, material=gypsum)
|
||||
ifcopenshell.api.material.edit_layer(model, layer=layer, attributes={"LayerThickness": 13})
|
||||
|
||||
# Great! Let's assign our material set to our wall type.
|
||||
ifcopenshell.api.material.assign_material(model, products=[wall_type], material=material_set)
|
||||
"""
|
||||
if set_type == "IfcMaterialLayerSet":
|
||||
return file.create_entity("IfcMaterialLayerSet", LayerSetName=name or "Unnamed")
|
||||
elif set_type == "IfcMaterialList":
|
||||
return file.create_entity("IfcMaterialList")
|
||||
return file.create_entity(set_type, Name=name or "Unnamed")
|
||||
@@ -0,0 +1,101 @@
|
||||
# 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 add_profile(
|
||||
file: ifcopenshell.file,
|
||||
profile_set: ifcopenshell.entity_instance,
|
||||
material: Optional[ifcopenshell.entity_instance] = None,
|
||||
profile: Optional[ifcopenshell.entity_instance] = None,
|
||||
name: Optional[str] = None,
|
||||
) -> ifcopenshell.entity_instance:
|
||||
"""Add a new profile item to a profile set
|
||||
|
||||
A profile item in a profile set represents an extruded 2D profile curve
|
||||
that is extruded along the axis of the element. Most commonly there will
|
||||
only be a single profile item in a profile set. For example, a beam will
|
||||
have a material profile set containing a single profile item, which may
|
||||
have a steel material and a I-beam shaped profile curve.
|
||||
|
||||
Note that the "profile item" represents a single extrusion in the
|
||||
profile set, whereas the "profile curve" represents a 2D curve used by a
|
||||
"profile item".
|
||||
|
||||
Profile is not optional for IfcMaterialProfile but it is optional for this API
|
||||
call and can be assigned later with material.assign_profile.
|
||||
|
||||
In some cases, a profiled element (i.e. beam, column) may be a composite
|
||||
beam or column and include multiple extrusions. This is rare. The order
|
||||
of the profiles does not matter.
|
||||
|
||||
:param profile_set: The IfcMaterialProfileSet that the profile is part of. The
|
||||
profile set represents a group of profile items. See
|
||||
ifcopenshell.api.material.add_material_set for more information on
|
||||
how to add a profile set.
|
||||
:param material: The IfcMaterial that the profile item is made out of.
|
||||
:param profile: The IfcProfileDef that represents the 2D cross section
|
||||
of the the profile item.
|
||||
:param name: An optional name of the material profile (not the geometric
|
||||
profile).
|
||||
:return: The newly created IfcMaterialProfile
|
||||
|
||||
Example:
|
||||
|
||||
.. code:: python
|
||||
|
||||
# Let's imagine we have a steel I-beam. Notice we are assigning to
|
||||
# the type only, as all occurrences of that type will automatically
|
||||
# inherit the material.
|
||||
beam_type = ifcopenshell.api.root.create_entity(model, ifc_class="IfcBeamType", name="B1")
|
||||
|
||||
# First, let's create a material set. This will later be assigned
|
||||
# to our beam type element.
|
||||
material_set = ifcopenshell.api.material.add_material_set(model,
|
||||
name="B1", set_type="IfcMaterialProfileSet")
|
||||
|
||||
# Create a steel material.
|
||||
steel = ifcopenshell.api.material.add_material(model, name="ST01", category="steel")
|
||||
|
||||
# Create an I-beam profile curve. Notice how we name our profiles
|
||||
# based on standardised steel profile names.
|
||||
hea100 = file.create_entity(
|
||||
"IfcIShapeProfileDef", ProfileName="HEA100", ProfileType="AREA",
|
||||
OverallWidth=100, OverallDepth=96, WebThickness=5, FlangeThickness=8, FilletRadius=12,
|
||||
)
|
||||
|
||||
# Define that steel material and cross section as a single profile
|
||||
# item. If this were a composite beam, we might add multiple profile
|
||||
# items instead, but this is rarely the case in most construction.
|
||||
ifcopenshell.api.material.add_profile(model,
|
||||
profile_set=material_set, material=steel, profile=hea100)
|
||||
|
||||
# Great! Let's assign our material set to our beam type.
|
||||
ifcopenshell.api.material.assign_material(model, products=[beam_type], material=material_set)
|
||||
"""
|
||||
profiles = list(profile_set.MaterialProfiles or [])
|
||||
mat_profile = file.create_entity("IfcMaterialProfile", Name=name)
|
||||
if material:
|
||||
mat_profile.Material = material
|
||||
if profile:
|
||||
mat_profile.Profile = profile
|
||||
profiles.append(mat_profile)
|
||||
profile_set.MaterialProfiles = profiles
|
||||
return mat_profile
|
||||
@@ -0,0 +1,364 @@
|
||||
# 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 collections import defaultdict
|
||||
from typing import Any, Optional, Union
|
||||
|
||||
import ifcopenshell
|
||||
import ifcopenshell.api.material
|
||||
import ifcopenshell.api.owner
|
||||
import ifcopenshell.guid
|
||||
import ifcopenshell.util.element
|
||||
import ifcopenshell.util.representation
|
||||
|
||||
|
||||
def assign_material(
|
||||
file: ifcopenshell.file,
|
||||
products: list[ifcopenshell.entity_instance],
|
||||
type: ifcopenshell.util.element.MATERIAL_TYPE = "IfcMaterial",
|
||||
material: Optional[ifcopenshell.entity_instance] = None,
|
||||
) -> Union[ifcopenshell.entity_instance, list[ifcopenshell.entity_instance], None]:
|
||||
"""Assigns a material to the list of products
|
||||
|
||||
Will unassign previously assigned material.
|
||||
|
||||
When a material is assigned to a product, it means that the product is
|
||||
made out of that material. In its simplest form, a single material may
|
||||
be assigned to a product, meaning that the entire product is made out of
|
||||
that one material. Alternatively, a material set may be assigned to a
|
||||
product, meaning that the product is made out of a set of materials.
|
||||
There are three types of sets, including layered construction, profiled
|
||||
materials, and arbitrary material constituents. See
|
||||
ifcopenshell.api.material.add_material_set for details.
|
||||
|
||||
Materials are typically assigned to the element types rather than
|
||||
individual occurrences of elements. Individual occurrences would then
|
||||
inherit the material from the type.
|
||||
|
||||
If the type has a material set, then the geometry of the occurrences
|
||||
must comply with the material set. For example, if the type has a
|
||||
constituent set, then it is expected that all occurrences also inherit
|
||||
the geometry of the type, which is made out of those constituents.
|
||||
Alternatively, if the type has a layer set, then all occurrences must
|
||||
have geometry that has a thickness equal to the sum of all layers. If a
|
||||
type has a profile set, then all occurrences must has the same profile
|
||||
extruded along its axis.
|
||||
|
||||
For layers and profiles assigned to types, the occurrences must be
|
||||
assigned an IfcMaterialLayerSetUsage or an IfcMaterialProfileSetUsage.
|
||||
This allows individual occurrences to override the layered or profiled
|
||||
construction offset from a reference line.
|
||||
|
||||
:param products: The list of IfcProducts to assign the material or material set
|
||||
to.
|
||||
:param type: Choose from "IfcMaterial", "IfcMaterialConstituentSet",
|
||||
"IfcMaterialLayerSet", "IfcMaterialLayerSetUsage",
|
||||
"IfcMaterialProfileSet", "IfcMaterialProfileSetUsage", or
|
||||
"IfcMaterialList". Note that "Set Usages" may only be assigned to
|
||||
occurrences, not types. Defaults to "IfcMaterial".
|
||||
:param material: The IfcMaterial or material set you are assigning here.
|
||||
If type is Usage then no need to provide `material`, it will be deduced
|
||||
from the element type automatically.
|
||||
If IfcMaterial is provided as material and type is not IfcMaterial,
|
||||
provided material will be ignored except for IfcMaterialList
|
||||
where it will be used as part of the list.
|
||||
:return: IfcRelAssociatesMaterial entity
|
||||
or a list of IfcRelAssociatesMaterial entities
|
||||
(possible if `type` is Usage
|
||||
and `products` require different Usages)
|
||||
or `None` if `products` was empty list.
|
||||
|
||||
Example:
|
||||
|
||||
.. code:: python
|
||||
|
||||
# Let's start with a simple concrete material
|
||||
concrete = ifcopenshell.api.material.add_material(model, name="CON01", category="concrete")
|
||||
|
||||
# Let's imagine a concrete bench made out of a single concrete
|
||||
# material. Let's assign it to the type.
|
||||
bench_type = ifcopenshell.api.root.create_entity(model, ifc_class="IfcFurnitureType")
|
||||
ifcopenshell.api.material.assign_material(model,
|
||||
products=[bench_type], type="IfcMaterial", material=concrete)
|
||||
|
||||
# Let's imagine there are a two occurrences of this bench. It's not
|
||||
# necessary to assign any material to these benches as they
|
||||
# automatically inherit the material from the type.
|
||||
bench1 = ifcopenshell.api.root.create_entity(model, ifc_class="IfcFurniture")
|
||||
bench2 = ifcopenshell.api.root.create_entity(model, ifc_class="IfcFurniture")
|
||||
ifcopenshell.api.type.assign_type(model, related_objects=[bench1], relating_type=bench_type)
|
||||
ifcopenshell.api.type.assign_type(model, related_objects=[bench2], relating_type=bench_type)
|
||||
|
||||
# If we have a concrete wall, we should use a layer set. Again,
|
||||
# let's start with a wall type, not occurrences.
|
||||
wall_type = ifcopenshell.api.root.create_entity(model, ifc_class="IfcWallType", name="WAL01")
|
||||
|
||||
# Even though there is only one layer in our layer set, we still use
|
||||
# a layer set because it makes it clear that this is a layered
|
||||
# construction. Let's say it's a 200mm thick concrete layer.
|
||||
material_set = ifcopenshell.api.material.add_material_set(model,
|
||||
name="CON200", set_type="IfcMaterialLayerSet")
|
||||
layer = ifcopenshell.api.material.add_layer(model, layer_set=material_set, material=steel)
|
||||
ifcopenshell.api.material.edit_layer(model, layer=layer, attributes={"LayerThickness": 200})
|
||||
|
||||
# Our wall type now has the layer set assigned to it
|
||||
ifcopenshell.api.material.assign_material(model,
|
||||
products=[wall_type], type="IfcMaterialLayerSet", material=material_set)
|
||||
|
||||
# Let's imagine an occurrence of this wall type.
|
||||
wall = ifcopenshell.api.root.create_entity(model, ifc_class="IfcWall")
|
||||
ifcopenshell.api.type.assign_type(model, related_objects=[wall], relating_type=wall_type)
|
||||
|
||||
# Our wall occurrence needs to have a "set usage" which describes
|
||||
# how the layers relate to a reference line (typically a 2D line
|
||||
# representing the extents of the wall). Usages are special since
|
||||
# they automatically detect the inherited material set from the
|
||||
# type. You'd write similar code for a profile set.
|
||||
ifcopenshell.api.material.assign_material(model,
|
||||
products=[wall], type="IfcMaterialLayerSetUsage")
|
||||
|
||||
# To be complete, let's create the wall's axis and body
|
||||
# representation. Notice how the axis guides the walls "reference
|
||||
# line" which determines where layers are extruded from, and the
|
||||
# body has a thickness of 200mm, same as our total layer set
|
||||
# thickness.
|
||||
axis = ifcopenshell.api.geometry.add_axis_representation(model,
|
||||
context=axis_context, axis=[(0.0, 0.0), (5000.0, 0.0)])
|
||||
body = ifcopenshell.api.geometry.add_wall_representation(model,
|
||||
context=body_context, length=5000, height=3000, thickness=200)
|
||||
ifcopenshell.api.geometry.assign_representation(model, product=wall, representation=axis)
|
||||
ifcopenshell.api.geometry.assign_representation(model, product=wall, representation=body)
|
||||
ifcopenshell.api.geometry.edit_object_placement(model, product=wall)
|
||||
"""
|
||||
usecase = Usecase()
|
||||
usecase.file = file
|
||||
usecase.settings = {"products": products, "type": type, "material": material}
|
||||
return usecase.execute()
|
||||
|
||||
|
||||
class Usecase:
|
||||
file: ifcopenshell.file
|
||||
settings: dict[str, Any]
|
||||
|
||||
def execute(self):
|
||||
self.products: set[ifcopenshell.entity_instance] = set(self.settings["products"])
|
||||
if not self.products:
|
||||
return
|
||||
|
||||
# NOTE: we always reassign material, even if it might be assigned before
|
||||
products_to_unassign_material = [p for p in self.products if ifcopenshell.util.element.get_material(p)]
|
||||
if products_to_unassign_material:
|
||||
ifcopenshell.api.material.unassign_material(self.file, products=products_to_unassign_material)
|
||||
|
||||
if self.settings["type"] == "IfcMaterial" or (
|
||||
self.settings["material"]
|
||||
and not self.settings["material"].is_a("IfcMaterial")
|
||||
and not self.settings["type"].endswith("Usage")
|
||||
):
|
||||
return self.assign_ifc_material()
|
||||
|
||||
elif self.settings["type"] == "IfcMaterialConstituentSet":
|
||||
material_set = self.file.create_entity(self.settings["type"])
|
||||
return self.create_material_association(material_set)
|
||||
|
||||
elif self.settings["type"] == "IfcMaterialLayerSet":
|
||||
material_set = self.file.create_entity(self.settings["type"])
|
||||
return self.create_material_association(material_set)
|
||||
|
||||
elif self.settings["type"] == "IfcMaterialLayerSetUsage":
|
||||
AXIS3_CLASSES = [
|
||||
"IfcSlab",
|
||||
"IfcSlabStandardCase",
|
||||
"IfcSlabElementedCase",
|
||||
"IfcRoof",
|
||||
"IfcRamp",
|
||||
"IfcPlate",
|
||||
"IfcPlateStandardCase",
|
||||
"IfcCovering",
|
||||
"IfcFurniture",
|
||||
]
|
||||
|
||||
provided_material_set = None
|
||||
if self.settings["material"]:
|
||||
provided_material_set = self.settings["material"]
|
||||
material_set_class = provided_material_set.is_a()
|
||||
assert (
|
||||
material_set_class == "IfcMaterialLayerSet"
|
||||
), f"{material_set_class} cannot be assiged as a IfcMaterialLayerSetUsage."
|
||||
|
||||
layer_types_to_products: defaultdict[
|
||||
tuple[ifcopenshell.entity_instance, str], list[ifcopenshell.entity_instance]
|
||||
]
|
||||
layer_types_to_products = defaultdict(list)
|
||||
types_to_material_sets: dict[Union[ifcopenshell.entity_instance, None], ifcopenshell.entity_instance]
|
||||
types_to_material_sets = {}
|
||||
|
||||
for product in self.products:
|
||||
# Figure what material set to assign.
|
||||
if provided_material_set is not None:
|
||||
material_set = provided_material_set
|
||||
else:
|
||||
# If material set is not provided, derive it from the type.
|
||||
element_type = ifcopenshell.util.element.get_type(product)
|
||||
if element_type in types_to_material_sets:
|
||||
material_set = types_to_material_sets[element_type]
|
||||
else:
|
||||
element_type_material = None
|
||||
if element_type is not None:
|
||||
element_type_material = ifcopenshell.util.element.get_material(element_type)
|
||||
if element_type_material and element_type_material.is_a("IfcMaterialLayerSet"):
|
||||
material_set = element_type_material
|
||||
else:
|
||||
material_set = self.file.create_entity("IfcMaterialLayerSet")
|
||||
|
||||
layer_set_direction = "AXIS3" if product.is_a() in AXIS3_CLASSES else "AXIS2"
|
||||
material_layer_type = (material_set, layer_set_direction)
|
||||
layer_types_to_products[material_layer_type].append(product)
|
||||
|
||||
rels = [
|
||||
self.create_layer_set_usage(material_set, layer_set_direction, products)
|
||||
for (material_set, layer_set_direction), products in layer_types_to_products.items()
|
||||
]
|
||||
return rels[0] if len(rels) == 1 else rels
|
||||
|
||||
elif self.settings["type"] == "IfcMaterialProfileSet":
|
||||
material_set = self.file.create_entity(self.settings["type"])
|
||||
return self.create_material_association(material_set)
|
||||
|
||||
elif self.settings["type"] == "IfcMaterialProfileSetUsage":
|
||||
provided_material_set = None
|
||||
if self.settings["material"]:
|
||||
provided_material_set = self.settings["material"]
|
||||
material_set_class = provided_material_set.is_a()
|
||||
assert (
|
||||
material_set_class == "IfcMaterialProfileSet"
|
||||
), f"{material_set_class} cannot be assiged as a IfcMaterialProfileSetUsage."
|
||||
|
||||
material_sets_to_products: dict[ifcopenshell.entity_instance, list[ifcopenshell.entity_instance]]
|
||||
material_sets_to_products = defaultdict(list)
|
||||
types_to_material_sets: dict[Union[ifcopenshell.entity_instance, None], ifcopenshell.entity_instance]
|
||||
types_to_material_sets = {}
|
||||
|
||||
for product in self.products:
|
||||
# Figure what material set to assign.
|
||||
if provided_material_set is not None:
|
||||
material_set = provided_material_set
|
||||
else:
|
||||
# If material set is not provided, derive it from the type.
|
||||
element_type = ifcopenshell.util.element.get_type(product)
|
||||
if element_type in types_to_material_sets:
|
||||
material_set = types_to_material_sets[element_type]
|
||||
else:
|
||||
element_type_material = None
|
||||
if element_type is not None:
|
||||
element_type_material = ifcopenshell.util.element.get_material(element_type)
|
||||
if element_type_material and element_type_material.is_a("IfcMaterialProfileSet"):
|
||||
material_set = element_type_material
|
||||
else:
|
||||
material_set = self.file.create_entity("IfcMaterialProfileSet")
|
||||
|
||||
material_sets_to_products[material_set].append(product)
|
||||
|
||||
rels: list[ifcopenshell.entity_instance] = []
|
||||
for material_set, products in material_sets_to_products.items():
|
||||
self.update_representation_profile(material_set, products)
|
||||
material_set_usage = self.create_profile_set_usage(material_set)
|
||||
rels.append(self.create_material_association(material_set_usage, products))
|
||||
return rels[0] if len(rels) == 1 else rels
|
||||
|
||||
elif self.settings["type"] == "IfcMaterialList":
|
||||
material_set = self.file.create_entity(self.settings["type"])
|
||||
material_set.Materials = [self.settings["material"]]
|
||||
return self.create_material_association(material_set)
|
||||
|
||||
def update_representation_profile(
|
||||
self, material_set: ifcopenshell.entity_instance, products: list[ifcopenshell.entity_instance]
|
||||
) -> None:
|
||||
profile = material_set.CompositeProfile
|
||||
if not profile and material_set.MaterialProfiles:
|
||||
profile = material_set.MaterialProfiles[0].Profile
|
||||
if not profile:
|
||||
return
|
||||
for product in products:
|
||||
representation = ifcopenshell.util.representation.get_representation(product, "Model", "Body", "MODEL_VIEW")
|
||||
if not representation:
|
||||
return
|
||||
for subelement in self.file.traverse(representation):
|
||||
if subelement.is_a("IfcSweptAreaSolid"):
|
||||
subelement.SweptArea = profile
|
||||
|
||||
def create_layer_set_usage(
|
||||
self,
|
||||
material_set: ifcopenshell.entity_instance,
|
||||
layer_set_direction: str,
|
||||
products: list[ifcopenshell.entity_instance],
|
||||
) -> ifcopenshell.entity_instance:
|
||||
usage = self.file.create_entity(
|
||||
"IfcMaterialLayerSetUsage",
|
||||
**{
|
||||
"ForLayerSet": material_set,
|
||||
"LayerSetDirection": layer_set_direction,
|
||||
"DirectionSense": "POSITIVE",
|
||||
"OffsetFromReferenceLine": 0,
|
||||
},
|
||||
)
|
||||
return self.create_material_association(usage, products)
|
||||
|
||||
def create_profile_set_usage(self, material_set: ifcopenshell.entity_instance) -> ifcopenshell.entity_instance:
|
||||
return self.file.create_entity("IfcMaterialProfileSetUsage", **{"ForProfileSet": material_set})
|
||||
|
||||
def assign_ifc_material(self) -> ifcopenshell.entity_instance:
|
||||
material = self.settings["material"] or self.file.create_entity("IfcMaterial")
|
||||
rel = self.get_rel_associates_material(material)
|
||||
if not rel:
|
||||
return self.create_material_association(material)
|
||||
previous_related_objects = set(rel.RelatedObjects)
|
||||
rel.RelatedObjects = list(previous_related_objects | self.products)
|
||||
ifcopenshell.api.owner.update_owner_history(self.file, element=rel)
|
||||
return rel
|
||||
|
||||
def create_material_association(
|
||||
self,
|
||||
relating_material: ifcopenshell.entity_instance,
|
||||
products: Optional[list[ifcopenshell.entity_instance]] = None,
|
||||
) -> ifcopenshell.entity_instance:
|
||||
if products is None:
|
||||
products = list(self.products)
|
||||
return self.file.create_entity(
|
||||
"IfcRelAssociatesMaterial",
|
||||
**{
|
||||
"GlobalId": ifcopenshell.guid.new(),
|
||||
"OwnerHistory": ifcopenshell.api.owner.create_owner_history(self.file),
|
||||
"RelatedObjects": products,
|
||||
"RelatingMaterial": relating_material,
|
||||
},
|
||||
)
|
||||
|
||||
def get_rel_associates_material(
|
||||
self, material: ifcopenshell.entity_instance
|
||||
) -> Union[ifcopenshell.entity_instance, None]:
|
||||
if self.file.schema == "IFC2X3" or material.is_a("IfcMaterialList"):
|
||||
return next(
|
||||
(
|
||||
r
|
||||
for r in self.file.by_type("IfcRelAssociatesMaterial")
|
||||
if r.RelatingMaterial == self.settings["material"]
|
||||
),
|
||||
None,
|
||||
)
|
||||
return next(iter(material.AssociatedTo), None)
|
||||
@@ -0,0 +1,127 @@
|
||||
# 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.util.representation
|
||||
|
||||
|
||||
def assign_profile(
|
||||
file: ifcopenshell.file, material_profile: ifcopenshell.entity_instance, profile: ifcopenshell.entity_instance
|
||||
) -> None:
|
||||
"""Changes the profile curve of a material profile item in a profile set
|
||||
|
||||
In addition to changing the profile curve, it will also change the
|
||||
profile curve used in any body representation extrusions.
|
||||
|
||||
:param material_profile: The IfcMaterialProfile to change the profile
|
||||
curve of. See ifcopenshell.api.material.add_profile to see how to
|
||||
create profiles.
|
||||
:param profile: The IfcProfileDef to set the profile item's curve to.
|
||||
:return: None
|
||||
|
||||
Example:
|
||||
|
||||
.. code:: python
|
||||
|
||||
# Let's imagine we have a steel I-beam. Notice we are assigning to
|
||||
# the type only, as all occurrences of that type will automatically
|
||||
# inherit the material.
|
||||
beam_type = ifcopenshell.api.root.create_entity(model, ifc_class="IfcBeamType", name="B1")
|
||||
|
||||
# First, let's create a material set. This will later be assigned
|
||||
# to our beam type element.
|
||||
material_set = ifcopenshell.api.material.add_material_set(model,
|
||||
name="B1", set_type="IfcMaterialProfileSet")
|
||||
|
||||
# Create a steel material.
|
||||
steel = ifcopenshell.api.material.add_material(model, name="ST01", category="steel")
|
||||
|
||||
# Create an I-beam profile curve. Notice how we name our profiles
|
||||
# based on standardised steel profile names.
|
||||
hea100 = model.create_entity(
|
||||
"IfcIShapeProfileDef", ProfileName="HEA100", ProfileType="AREA",
|
||||
OverallWidth=100, OverallDepth=96, WebThickness=5, FlangeThickness=8, FilletRadius=12,
|
||||
)
|
||||
|
||||
# Define that steel material and cross section as a single profile
|
||||
# item. If this were a composite beam, we might add multiple profile
|
||||
# items instead, but this is rarely the case in most construction.
|
||||
profile_item = ifcopenshell.api.material.add_profile(model,
|
||||
profile_set=material_set, material=steel, profile=hea100)
|
||||
|
||||
# Great! Let's assign our material set to our beam type.
|
||||
ifcopenshell.api.material.assign_material(model, products=[beam_type], material=material_set)
|
||||
|
||||
# Let's create an occurrence of this beam.
|
||||
beam = ifcopenshell.api.root.create_entity(model, ifc_class="IfcBeam", name="B1.01")
|
||||
ifcopenshell.api.material.assign_material(model,
|
||||
products=[beam], type="IfcMaterialProfileSetUsage")
|
||||
|
||||
# Let's give a 1000mm long beam body representation.
|
||||
body = ifcopenshell.api.geometry.add_profile_representation(
|
||||
context=body_context, profile=hea100, depth=1000)
|
||||
ifcopenshell.api.geometry.assign_representation(model, product=beam, representation=body)
|
||||
ifcopenshell.api.geometry.edit_object_placement(model, product=beam)
|
||||
|
||||
# Now let's change the profile to a HEA200 standard profile instead.
|
||||
# This will automatically change the body representation that we
|
||||
# just added as well to a HEA200 profile.
|
||||
hea200 = model.create_entity(
|
||||
"IfcIShapeProfileDef", ProfileName="HEA200", ProfileType="AREA",
|
||||
OverallWidth=200, OverallDepth=190, WebThickness=6.5, FlangeThickness=10, FilletRadius=18,
|
||||
)
|
||||
ifcopenshell.api.material.assign_profile(model, material_profile=profile_item, profile=hea200)
|
||||
"""
|
||||
usecase = Usecase()
|
||||
usecase.file = file
|
||||
return usecase.execute(material_profile, profile)
|
||||
|
||||
|
||||
class Usecase:
|
||||
file: ifcopenshell.file
|
||||
|
||||
def execute(self, material_profile: ifcopenshell.entity_instance, profile: ifcopenshell.entity_instance) -> None:
|
||||
# TODO: handle composite profiles
|
||||
old_profile = material_profile.Profile
|
||||
material_profile.Profile = profile
|
||||
for profile_set in material_profile.ToMaterialProfileSet:
|
||||
for inverse in self.file.get_inverse(profile_set):
|
||||
if not inverse.is_a("IfcMaterialProfileSetUsage"):
|
||||
continue
|
||||
if self.file.schema == "IFC2X3":
|
||||
for rel in self.file.get_inverse(inverse):
|
||||
if not rel.is_a("IfcRelAssociatesMaterial"):
|
||||
continue
|
||||
for element in rel.RelatedObjects:
|
||||
self.change_profile(element, profile)
|
||||
else:
|
||||
for rel in inverse.AssociatedTo:
|
||||
for element in rel.RelatedObjects:
|
||||
self.change_profile(element, profile)
|
||||
|
||||
if old_profile and self.file.get_total_inverses(old_profile) == 0:
|
||||
# TODO: check remove deep
|
||||
self.file.remove(old_profile)
|
||||
|
||||
def change_profile(self, element: ifcopenshell.entity_instance, profile: ifcopenshell.entity_instance) -> None:
|
||||
representation = ifcopenshell.util.representation.get_representation(element, "Model", "Body", "MODEL_VIEW")
|
||||
if not representation:
|
||||
return
|
||||
for subelement in self.file.traverse(representation):
|
||||
if subelement.is_a("IfcSweptAreaSolid"):
|
||||
subelement.SweptArea = profile
|
||||
@@ -0,0 +1,103 @@
|
||||
# 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_material(file: ifcopenshell.file, material: ifcopenshell.entity_instance) -> ifcopenshell.entity_instance:
|
||||
"""Copies a material or material set
|
||||
|
||||
All material psets and styles are copied. The copied material is not
|
||||
associated to any elements.
|
||||
|
||||
If a material set is copied, the set items are also copied. However the
|
||||
underlying materials (and profiles) used within the set items are reused.
|
||||
|
||||
If a material is associated with a presentation style, that presentation
|
||||
style is reused.
|
||||
|
||||
:param material: The IfcMaterialDefinition to copy
|
||||
:return: The new copy of the material
|
||||
|
||||
Example:
|
||||
|
||||
.. code:: python
|
||||
|
||||
concrete = ifcopenshell.api.material.add_material(model, name="CON01", category="concrete")
|
||||
|
||||
# Let's duplicate the concrete material
|
||||
concrete_copy = ifcopenshell.api.material.copy_material(model, material=concrete)
|
||||
"""
|
||||
if material.is_a("IfcMaterial"):
|
||||
return _copy_material_with_inverses(file, material)
|
||||
elif material.is_a("IfcMaterialConstituentSet"):
|
||||
new = _copy_material_with_inverses(file, material)
|
||||
new.MaterialConstituents = [copy_material(file, i) for i in material.MaterialConstituents]
|
||||
return new
|
||||
elif material.is_a("IfcMaterialConstituent"):
|
||||
return _copy_material_with_inverses(file, material)
|
||||
elif material.is_a("IfcMaterialLayerSet"):
|
||||
new = _copy_material_with_inverses(file, material)
|
||||
new.MaterialLayers = [copy_material(file, i) for i in material.MaterialLayers]
|
||||
return new
|
||||
elif material.is_a("IfcMaterialLayer"):
|
||||
return _copy_material_with_inverses(file, material)
|
||||
elif material.is_a("IfcMaterialProfileSet"):
|
||||
new = _copy_material_with_inverses(file, material)
|
||||
new.MaterialProfiles = [copy_material(file, i) for i in material.MaterialProfiles]
|
||||
return new
|
||||
elif material.is_a("IfcMaterialProfile"):
|
||||
return _copy_material_with_inverses(file, material)
|
||||
elif material.is_a("IfcMaterialList"):
|
||||
return _copy_material_with_inverses(file, material)
|
||||
else:
|
||||
raise Exception(f"Unexpected material type: '{material.is_a()}' ({material}).")
|
||||
|
||||
|
||||
def _copy_material_with_inverses(
|
||||
file: ifcopenshell.file, material: ifcopenshell.entity_instance
|
||||
) -> ifcopenshell.entity_instance:
|
||||
new = ifcopenshell.util.element.copy(file, material)
|
||||
for inverse in file.get_inverse(material):
|
||||
if inverse.is_a("IfcMaterialProperties"):
|
||||
# Properties must not be shared between objects for convenience of authoring
|
||||
inverse = ifcopenshell.util.element.copy(file, inverse)
|
||||
inverse.Material = new
|
||||
|
||||
props_attribute = "Properties"
|
||||
if file.schema == "IFC2X3":
|
||||
if not inverse.is_a("IfcExtendedMaterialProperties"):
|
||||
continue
|
||||
props_attribute = "ExtendedProperties"
|
||||
|
||||
props = getattr(inverse, props_attribute)
|
||||
if not props:
|
||||
continue
|
||||
|
||||
copied_props = []
|
||||
for pset in props:
|
||||
copied_props.append(ifcopenshell.util.element.copy_deep(file, pset))
|
||||
setattr(inverse, props_attribute, copied_props)
|
||||
|
||||
elif inverse.is_a("IfcMaterialDefinitionRepresentation"):
|
||||
inverse = ifcopenshell.util.element.copy_deep(
|
||||
file, inverse, exclude=["IfcRepresentationContext", "IfcMaterial", "IfcPresentationStyle"]
|
||||
)
|
||||
inverse.RepresentedMaterial = new
|
||||
return new
|
||||
@@ -0,0 +1,44 @@
|
||||
# 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_assigned_material(
|
||||
file: ifcopenshell.file, element: ifcopenshell.entity_instance, attributes: dict[str, Any]
|
||||
) -> None:
|
||||
"""Edits the attributes of an IfcMaterial
|
||||
|
||||
For more information about the attributes and data types of an
|
||||
IfcMaterial, consult the IFC documentation.
|
||||
|
||||
:param element: The IfcMaterial entity you want to edit
|
||||
:param attributes: a dictionary of attribute names and values.
|
||||
:return: None
|
||||
|
||||
Example:
|
||||
|
||||
.. code:: python
|
||||
|
||||
concrete = ifcopenshell.api.material.add_material(model, name="CON01", category="concrete")
|
||||
ifcopenshell.api.material.edit_assigned_material(model,
|
||||
element=concrete, attributes={"Description": "40MPA concrete with broom finish"})
|
||||
"""
|
||||
for name, value in attributes.items():
|
||||
setattr(element, name, value)
|
||||
@@ -0,0 +1,67 @@
|
||||
# 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, Optional
|
||||
|
||||
import ifcopenshell
|
||||
|
||||
|
||||
def edit_constituent(
|
||||
file: ifcopenshell.file,
|
||||
constituent: ifcopenshell.entity_instance,
|
||||
attributes: Optional[dict[str, Any]] = None,
|
||||
material: Optional[ifcopenshell.entity_instance] = None,
|
||||
) -> None:
|
||||
"""Edits the attributes of an IfcMaterialConstituent
|
||||
|
||||
For more information about the attributes and data types of an
|
||||
IfcMaterialConstituent, consult the IFC documentation.
|
||||
|
||||
:param constituent: The IfcMaterialConstituent entity you want to edit
|
||||
:param attributes: a dictionary of attribute names and values.
|
||||
:param material: The IfcMaterial entity you want to change the constituent to
|
||||
:return: None
|
||||
|
||||
Example:
|
||||
|
||||
.. code:: python
|
||||
|
||||
# Let's add two materials
|
||||
aluminium1 = ifcopenshell.api.material.add_material(model, name="AL01", category="aluminium")
|
||||
aluminium2 = ifcopenshell.api.material.add_material(model, name="AL02", category="aluminium")
|
||||
glass = ifcopenshell.api.material.add_material(model, name="GLZ01", category="glass")
|
||||
|
||||
material_set = ifcopenshell.api.material.add_material_set(model,
|
||||
name="Window", set_type="IfcMaterialConstituentSet")
|
||||
|
||||
# Set up two constituents, one for the frame and the other for the glazing.
|
||||
framing = ifcopenshell.api.material.add_constituent(model,
|
||||
constituent_set=material_set, material=aluminium1)
|
||||
glazing = ifcopenshell.api.material.add_constituent(model,
|
||||
constituent_set=material_set, material=glass)
|
||||
|
||||
# Let's make sure this constituent refers to the framing of the
|
||||
# window and uses the second aluminium material instead.
|
||||
ifcopenshell.api.material.edit_constituent(model,
|
||||
constituent=framing, attributes={"Name": "Framing"}, material=aluminium2)
|
||||
|
||||
ifcopenshell.api.material.edit_constituent(model,
|
||||
constituent=constituent, attributes={"Name": "Glazing"})
|
||||
"""
|
||||
for name, value in (attributes or {}).items():
|
||||
setattr(constituent, name, value)
|
||||
constituent.Material = material
|
||||
@@ -0,0 +1,66 @@
|
||||
# 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, Optional
|
||||
|
||||
import ifcopenshell
|
||||
|
||||
|
||||
def edit_layer(
|
||||
file: ifcopenshell.file,
|
||||
layer: ifcopenshell.entity_instance,
|
||||
attributes: Optional[dict[str, Any]] = None,
|
||||
material: Optional[ifcopenshell.entity_instance] = None,
|
||||
) -> None:
|
||||
"""Edits the attributes of an IfcMaterialLayer
|
||||
|
||||
For more information about the attributes and data types of an
|
||||
IfcMaterialLayer, consult the IFC documentation.
|
||||
|
||||
:param layer: The IfcMaterialLayer entity you want to edit
|
||||
:param attributes: a dictionary of attribute names and values.
|
||||
:param material: The IfcMaterial entity you want the layer to be made
|
||||
from.
|
||||
:return: None
|
||||
|
||||
Example:
|
||||
|
||||
.. code:: python
|
||||
|
||||
# Let's create two materials typically used for steel stud partition
|
||||
# walls with gypsum lining.
|
||||
gypsum = ifcopenshell.api.material.add_material(model, name="PB01", category="gypsum")
|
||||
steel = ifcopenshell.api.material.add_material(model, name="ST01", category="steel")
|
||||
|
||||
# Create a material layer set to contain our layers.
|
||||
material_set = ifcopenshell.api.material.add_material_set(model,
|
||||
name="GYP-ST-GYP", set_type="IfcMaterialLayerSet")
|
||||
|
||||
# Now let's use those materials as three layers in our set, such
|
||||
# that the steel studs are sandwiched by the gypsum. Let's imagine
|
||||
# we're setting the layer thickness in millimeters.
|
||||
layer = ifcopenshell.api.material.add_layer(model, layer_set=material_set, material=gypsum)
|
||||
ifcopenshell.api.material.edit_layer(model, layer=layer, attributes={"LayerThickness": 13})
|
||||
layer = ifcopenshell.api.material.add_layer(model, layer_set=material_set, material=steel)
|
||||
ifcopenshell.api.material.edit_layer(model, layer=layer, attributes={"LayerThickness": 92})
|
||||
layer = ifcopenshell.api.material.add_layer(model, layer_set=material_set, material=gypsum)
|
||||
ifcopenshell.api.material.edit_layer(model, layer=layer, attributes={"LayerThickness": 13})
|
||||
"""
|
||||
for name, value in (attributes or {}).items():
|
||||
setattr(layer, name, value)
|
||||
if material:
|
||||
layer.Material = material
|
||||
@@ -0,0 +1,77 @@
|
||||
# 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_layer_usage(file: ifcopenshell.file, usage: ifcopenshell.entity_instance, attributes: dict[str, Any]) -> None:
|
||||
"""Edits the attributes of an IfcMaterialLayerSetUsage
|
||||
|
||||
This is typically used to change the offset from the reference line to
|
||||
the layers.
|
||||
|
||||
For more information about the attributes and data types of an
|
||||
IfcMaterialLayerSetUsage, consult the IFC documentation.
|
||||
|
||||
:param usage: The IfcMaterialLayerSetUsage entity you want to edit
|
||||
:param attributes: a dictionary of attribute names and values.
|
||||
:return: None
|
||||
|
||||
Example:
|
||||
|
||||
.. code:: python
|
||||
|
||||
# Let's start with a simple concrete material
|
||||
concrete = ifcopenshell.api.material.add_material(model, name="CON01", category="concrete")
|
||||
|
||||
# If we have a concrete wall, we should use a layer set. Again,
|
||||
# let's start with a wall type, not occurrences.
|
||||
wall_type = ifcopenshell.api.root.create_entity(model, ifc_class="IfcWallType", name="WAL01")
|
||||
|
||||
# Even though there is only one layer in our layer set, we still use
|
||||
# a layer set because it makes it clear that this is a layered
|
||||
# construction. Let's say it's a 200mm thick concrete layer.
|
||||
material_set = ifcopenshell.api.material.add_material_set(model,
|
||||
name="CON200", set_type="IfcMaterialLayerSet")
|
||||
layer = ifcopenshell.api.material.add_layer(model, layer_set=material_set, material=steel)
|
||||
ifcopenshell.api.material.edit_layer(model, layer=layer, attributes={"LayerThickness": 200})
|
||||
|
||||
# Our wall type now has the layer set assigned to it
|
||||
ifcopenshell.api.material.assign_material(model,
|
||||
products=[wall_type], type="IfcMaterialLayerSet", material=material_set)
|
||||
|
||||
# Let's imagine an occurrence of this wall type.
|
||||
wall = ifcopenshell.api.root.create_entity(model, ifc_class="IfcWall")
|
||||
ifcopenshell.api.type.assign_type(model, related_objects=[wall], relating_type=wall_type)
|
||||
|
||||
# Our wall occurrence needs to have a "set usage" which describes
|
||||
# how the layers relate to a reference line (typically a 2D line
|
||||
# representing the extents of the wall). Usages are special since
|
||||
# they automatically detect the inherited material set from the
|
||||
# type. You'd write similar code for a profile set.
|
||||
rel = ifcopenshell.api.material.assign_material(model,
|
||||
products=[wall], type="IfcMaterialLayerSetUsage")
|
||||
|
||||
# Let's change the offset from the reference line to be 200mm
|
||||
# instead of the default of 0mm.
|
||||
ifcopenshell.api.material.edit_layer_usage(model,
|
||||
usage=rel.RelatingMaterial, attributes={"OffsetFromReferenceLine": 200})
|
||||
"""
|
||||
for name, value in attributes.items():
|
||||
setattr(usage, name, value)
|
||||
@@ -0,0 +1,27 @@
|
||||
# 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_material(file: ifcopenshell.file, material: ifcopenshell.entity_instance, attributes: dict[str, Any]) -> None:
|
||||
"""Edits the attributes of an IfcMaterial"""
|
||||
|
||||
for name, value in attributes.items():
|
||||
setattr(material, name, value)
|
||||
@@ -0,0 +1,84 @@
|
||||
# IfcOpenShell - IFC toolkit and geometry engine
|
||||
# Copyright (C) 2021, 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 Any, Optional
|
||||
|
||||
import ifcopenshell
|
||||
|
||||
|
||||
def edit_profile(
|
||||
file: ifcopenshell.file,
|
||||
profile: ifcopenshell.entity_instance,
|
||||
attributes: Optional[dict[str, Any]] = None,
|
||||
profile_def: Optional[ifcopenshell.entity_instance] = None,
|
||||
material: Optional[ifcopenshell.entity_instance] = None,
|
||||
) -> None:
|
||||
"""Edits the attributes of an IfcMaterialProfile
|
||||
|
||||
For more information about the attributes and data types of an
|
||||
IfcMaterialProfile, consult the IFC documentation.
|
||||
|
||||
:param profile: The IfcMaterialProfile entity you want to edit
|
||||
:param attributes: a dictionary of attribute names and values.
|
||||
:param profile_def: The IfcProfileDef entity the profile curve should be
|
||||
extruded from.
|
||||
:param material: The IfcMaterial entity you want to change the profile
|
||||
to be made from.
|
||||
:return: None
|
||||
|
||||
Example:
|
||||
|
||||
.. code:: python
|
||||
|
||||
# Let's create a material set to store our profiles.
|
||||
material_set = ifcopenshell.api.material.add_material_set(model,
|
||||
name="B1", set_type="IfcMaterialProfileSet")
|
||||
|
||||
# Create a couple steel materials.
|
||||
steel1 = ifcopenshell.api.material.add_material(model, name="ST01", category="steel")
|
||||
steel2 = ifcopenshell.api.material.add_material(model, name="ST01", category="steel")
|
||||
|
||||
# Create some I-shaped profiles. Notice how we name our profiles based
|
||||
# on standardised steel profile names.
|
||||
hea100 = file.create_entity(
|
||||
"IfcIShapeProfileDef", ProfileName="HEA100", ProfileType="AREA",
|
||||
OverallWidth=100, OverallDepth=96, WebThickness=5, FlangeThickness=8, FilletRadius=12,
|
||||
)
|
||||
hea200 = file.create_entity(
|
||||
"IfcIShapeProfileDef", ProfileName="HEA200", ProfileType="AREA",
|
||||
OverallWidth=200, OverallDepth=190, WebThickness=6.5, FlangeThickness=10, FilletRadius=18,
|
||||
)
|
||||
|
||||
# Define that steel material and cross section as a single profile
|
||||
# item. If this were a composite beam, we might add multiple profile
|
||||
# items instead, but this is rarely the case in most construction.
|
||||
profile_item = ifcopenshell.api.material.add_profile(model,
|
||||
profile_set=material_set, material=steel1, profile=hea100)
|
||||
|
||||
# Edit our profile item to use a HEA200 profile instead made out of
|
||||
# another type of steel.
|
||||
ifcopenshell.api.material.edit_profile(model,
|
||||
profile=profile_item, profile_def=hea200, material=steel2)
|
||||
"""
|
||||
for name, value in (attributes or {}).items():
|
||||
setattr(profile, name, value)
|
||||
if material:
|
||||
profile.Material = material
|
||||
if profile_def:
|
||||
profile.Profile = profile_def
|
||||
@@ -0,0 +1,223 @@
|
||||
# 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.geom
|
||||
import ifcopenshell.util.representation
|
||||
import ifcopenshell.util.shape
|
||||
from ifcopenshell.geom import ShapeType
|
||||
|
||||
|
||||
def edit_profile_usage(
|
||||
file: ifcopenshell.file, usage: ifcopenshell.entity_instance, attributes: dict[str, Any]
|
||||
) -> None:
|
||||
"""Edits the attributes of an IfcMaterialProfileSetUsage
|
||||
|
||||
This is typically used to change the cardinal point of the profile.
|
||||
The cardinal point represents whether the profile is extruded along the
|
||||
center of the axis line, at a corner, at a shear center, at the bottom,
|
||||
top, etc.
|
||||
|
||||
For more information about the attributes and data types of an
|
||||
IfcMaterialProfileSetUsage, consult the IFC documentation.
|
||||
|
||||
:param usage: The IfcMaterialProfileSetUsage entity you want to edit
|
||||
:param attributes: a dictionary of attribute names and values.
|
||||
:return: None
|
||||
|
||||
Example:
|
||||
|
||||
.. code:: python
|
||||
|
||||
# Let's imagine we have a steel I-beam. Notice we are assigning to
|
||||
# the type only, as all occurrences of that type will automatically
|
||||
# inherit the material.
|
||||
beam_type = ifcopenshell.api.root.create_entity(model, ifc_class="IfcBeamType", name="B1")
|
||||
|
||||
# First, let's create a material set. This will later be assigned
|
||||
# to our beam type element.
|
||||
material_set = ifcopenshell.api.material.add_material_set(model,
|
||||
name="B1", set_type="IfcMaterialProfileSet")
|
||||
|
||||
# Create a steel material.
|
||||
steel = ifcopenshell.api.material.add_material(model, name="ST01", category="steel")
|
||||
|
||||
# Create an I-beam profile curve. Notice how we name our profiles
|
||||
# based on standardised steel profile names.
|
||||
hea100 = model.create_entity(
|
||||
"IfcIShapeProfileDef", ProfileName="HEA100", ProfileType="AREA",
|
||||
OverallWidth=100, OverallDepth=96, WebThickness=5, FlangeThickness=8, FilletRadius=12,
|
||||
)
|
||||
|
||||
# Define that steel material and cross section as a single profile
|
||||
# item. If this were a composite beam, we might add multiple profile
|
||||
# items instead, but this is rarely the case in most construction.
|
||||
profile_item = ifcopenshell.api.material.add_profile(model,
|
||||
profile_set=material_set, material=steel, profile=hea100)
|
||||
|
||||
# Great! Let's assign our material set to our beam type.
|
||||
ifcopenshell.api.material.assign_material(model, products=[beam_type], material=material_set)
|
||||
|
||||
# Let's create an occurrence of this beam.
|
||||
beam = ifcopenshell.api.root.create_entity(model, ifc_class="IfcBeam", name="B1.01")
|
||||
rel = ifcopenshell.api.material.assign_material(model, material=material_set,
|
||||
products=[beam], type="IfcMaterialProfileSetUsage")
|
||||
|
||||
# Let's give a 1000mm long beam body representation.
|
||||
body = ifcopenshell.api.geometry.add_profile_representation(
|
||||
context=body_context, profile=hea100, depth=1000)
|
||||
ifcopenshell.api.geometry.assign_representation(model, product=beam, representation=body)
|
||||
ifcopenshell.api.geometry.edit_object_placement(model, product=beam)
|
||||
|
||||
# Let's change the cardinal point to be the top center of the axis
|
||||
# line. This is represented by the number "8". Consult the IFC
|
||||
# documentation for all the numbers you can use.
|
||||
ifcopenshell.api.material.edit_profile_usage(model,
|
||||
usage=rel.RelatingMaterial, attributes={"CardinalPoint": 8})
|
||||
"""
|
||||
usecase = Usecase()
|
||||
|
||||
usecase.file = file
|
||||
return usecase.execute(usage, attributes)
|
||||
|
||||
|
||||
class Usecase:
|
||||
file: ifcopenshell.file
|
||||
|
||||
def execute(self, usage: ifcopenshell.entity_instance, attributes: dict[str, Any]) -> None:
|
||||
self.usage = usage
|
||||
self.attributes = attributes
|
||||
self.cardinal_point = attributes.get("CardinalPoint")
|
||||
if self.cardinal_point and self.cardinal_point != usage.CardinalPoint:
|
||||
self.update_cardinal_point()
|
||||
|
||||
for name, value in attributes.items():
|
||||
setattr(usage, name, value)
|
||||
|
||||
def update_cardinal_point(self):
|
||||
material_set = self.usage.ForProfileSet
|
||||
self.profile = material_set.CompositeProfile
|
||||
if not self.profile and material_set.MaterialProfiles:
|
||||
self.profile = material_set.MaterialProfiles[0].Profile
|
||||
if not self.profile:
|
||||
return
|
||||
|
||||
self.position = self.calculate_position()
|
||||
|
||||
if self.file.schema == "IFC2X3":
|
||||
for rel in self.file.get_inverse(self.usage):
|
||||
if not rel.is_a("IfcRelAssociatesMaterial"):
|
||||
continue
|
||||
for element in rel.RelatedObjects:
|
||||
self.update_representation(element)
|
||||
else:
|
||||
for rel in self.usage.AssociatedTo:
|
||||
for element in rel.RelatedObjects:
|
||||
self.update_representation(element)
|
||||
|
||||
def calculate_position(self):
|
||||
self.dummy = ifcopenshell.file(schema=self.file.schema)
|
||||
dummy_profile = self.dummy.add(self.profile)
|
||||
# We clear all radiuses so that we can calculate geometric centroid easily
|
||||
for i, attribute in enumerate(dummy_profile):
|
||||
name = dummy_profile.attribute_name(i)
|
||||
if "Radius" in name and name != "RoundingRadius":
|
||||
dummy_profile[i] = None
|
||||
dummy_solid = self.dummy.create_entity(
|
||||
"IfcExtrudedAreaSolid",
|
||||
**{
|
||||
"SweptArea": dummy_profile,
|
||||
"ExtrudedDirection": self.dummy.createIfcDirection((0.0, 0.0, 1.0)),
|
||||
"Depth": 1,
|
||||
}
|
||||
)
|
||||
self.settings_2d = ifcopenshell.geom.settings()
|
||||
self.settings_2d.set("dimensionality", ifcopenshell.ifcopenshell_wrapper.CURVES_SURFACES_AND_SOLIDS)
|
||||
shape = ifcopenshell.geom.create_shape(self.settings_2d, dummy_solid)
|
||||
|
||||
# NOTE: points do not need unit conversion
|
||||
# as dummy file is inherently using project units.
|
||||
if self.cardinal_point == 1:
|
||||
return self.get_bottom_left(shape)
|
||||
elif self.cardinal_point == 2:
|
||||
return self.get_bottom_centre(shape)
|
||||
elif self.cardinal_point == 3:
|
||||
return self.get_bottom_right(shape)
|
||||
elif self.cardinal_point == 4:
|
||||
return self.get_mid_depth_left(shape)
|
||||
elif self.cardinal_point == 5:
|
||||
return self.get_mid_depth_centre(shape)
|
||||
elif self.cardinal_point == 6:
|
||||
return self.get_mid_depth_right(shape)
|
||||
elif self.cardinal_point == 7:
|
||||
return self.get_top_left(shape)
|
||||
elif self.cardinal_point == 8:
|
||||
return self.get_top_centre(shape)
|
||||
elif self.cardinal_point == 9:
|
||||
return self.get_top_right(shape)
|
||||
|
||||
def get_bottom_left(self, shape: ShapeType) -> ifcopenshell.entity_instance:
|
||||
width = ifcopenshell.util.shape.get_x(shape)
|
||||
height = ifcopenshell.util.shape.get_y(shape)
|
||||
return self.file.createIfcAxis2Placement3D(self.file.createIfcCartesianPoint((-width / 2, height / 2, 0.0)))
|
||||
|
||||
def get_bottom_centre(self, shape: ShapeType) -> ifcopenshell.entity_instance:
|
||||
height = ifcopenshell.util.shape.get_y(shape)
|
||||
return self.file.createIfcAxis2Placement3D(self.file.createIfcCartesianPoint((0.0, height / 2, 0.0)))
|
||||
|
||||
def get_bottom_right(self, shape: ShapeType) -> ifcopenshell.entity_instance:
|
||||
width = ifcopenshell.util.shape.get_x(shape)
|
||||
height = ifcopenshell.util.shape.get_y(shape)
|
||||
return self.file.createIfcAxis2Placement3D(self.file.createIfcCartesianPoint((width / 2, height / 2, 0.0)))
|
||||
|
||||
def get_mid_depth_left(self, shape: ShapeType) -> ifcopenshell.entity_instance:
|
||||
width = ifcopenshell.util.shape.get_x(shape)
|
||||
return self.file.createIfcAxis2Placement3D(self.file.createIfcCartesianPoint((-width / 2, 0.0, 0.0)))
|
||||
|
||||
def get_mid_depth_centre(self, shape: ShapeType) -> ifcopenshell.entity_instance:
|
||||
return self.file.createIfcAxis2Placement3D(self.file.createIfcCartesianPoint((0.0, 0.0, 0.0)))
|
||||
|
||||
def get_mid_depth_right(self, shape: ShapeType) -> ifcopenshell.entity_instance:
|
||||
width = ifcopenshell.util.shape.get_x(shape)
|
||||
return self.file.createIfcAxis2Placement3D(self.file.createIfcCartesianPoint((width / 2, 0.0, 0.0)))
|
||||
|
||||
def get_top_left(self, shape: ShapeType) -> ifcopenshell.entity_instance:
|
||||
width = ifcopenshell.util.shape.get_x(shape)
|
||||
height = ifcopenshell.util.shape.get_y(shape)
|
||||
return self.file.createIfcAxis2Placement3D(self.file.createIfcCartesianPoint((-width / 2, -height / 2, 0.0)))
|
||||
|
||||
def get_top_centre(self, shape: ShapeType) -> ifcopenshell.entity_instance:
|
||||
height = ifcopenshell.util.shape.get_y(shape)
|
||||
return self.file.createIfcAxis2Placement3D(self.file.createIfcCartesianPoint((0.0, -height / 2, 0.0)))
|
||||
|
||||
def get_top_right(self, shape: ShapeType) -> ifcopenshell.entity_instance:
|
||||
width = ifcopenshell.util.shape.get_x(shape)
|
||||
height = ifcopenshell.util.shape.get_y(shape)
|
||||
return self.file.createIfcAxis2Placement3D(self.file.createIfcCartesianPoint((width / 2, -height / 2, 0.0)))
|
||||
|
||||
def update_representation(self, element: ifcopenshell.entity_instance) -> None:
|
||||
representation = ifcopenshell.util.representation.get_representation(element, "Model", "Body", "MODEL_VIEW")
|
||||
if not representation:
|
||||
return
|
||||
|
||||
for subelement in self.file.traverse(representation):
|
||||
if subelement.is_a("IfcSweptAreaSolid") and subelement.SweptArea == self.profile:
|
||||
self.update_swept_area_solid(subelement)
|
||||
|
||||
def update_swept_area_solid(self, element: ifcopenshell.entity_instance) -> None:
|
||||
element.Position = self.position
|
||||
@@ -0,0 +1,58 @@
|
||||
# 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 remove_constituent(
|
||||
file: ifcopenshell.file, constituent: ifcopenshell.entity_instance, should_remove_material: bool = False
|
||||
) -> None:
|
||||
"""Removes a constituent from a constituent set
|
||||
|
||||
Note that it is invalid to have zero items in a set, so you should leave
|
||||
at least one constituent to ensure a valid IFC dataset.
|
||||
|
||||
:param constituent: The IfcMaterialConstituent entity you want to remove
|
||||
:param should_remove_material: If true, materials with no users will be removed
|
||||
|
||||
Example:
|
||||
|
||||
.. code:: python
|
||||
|
||||
# Create a material set for windows made out of aluminium and glass.
|
||||
material_set = ifcopenshell.api.material.add_material_set(model,
|
||||
name="Window", set_type="IfcMaterialConstituentSet")
|
||||
|
||||
aluminium = ifcopenshell.api.material.add_material(model, name="AL01", category="aluminium")
|
||||
glass = ifcopenshell.api.material.add_material(model, name="GLZ01", category="glass")
|
||||
|
||||
# Now let's use those materials as two constituents in our set.
|
||||
framing = ifcopenshell.api.material.add_constituent(model,
|
||||
constituent_set=material_set, material=aluminium)
|
||||
glazing = ifcopenshell.api.material.add_constituent(model,
|
||||
constituent_set=material_set, material=glass)
|
||||
|
||||
# Let's remove the glass constituent. Note that we should not remove
|
||||
# the framing, at this would mean there are no constituents which is
|
||||
# invalid.
|
||||
ifcopenshell.api.material.remove_constituent(model, constituent=glazing)
|
||||
"""
|
||||
material = constituent.Material
|
||||
file.remove(constituent)
|
||||
if material and should_remove_material:
|
||||
ifcopenshell.util.element.remove_deep2(file, material)
|
||||
@@ -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.util.element
|
||||
|
||||
|
||||
def remove_layer(
|
||||
file: ifcopenshell.file, layer: ifcopenshell.entity_instance, should_remove_material: bool = False
|
||||
) -> None:
|
||||
"""Removes a layer from a layer set
|
||||
|
||||
Note that it is invalid to have zero items in a set, so you should leave
|
||||
at least one layer to ensure a valid IFC dataset.
|
||||
|
||||
:param layer: The IfcMaterialLayer entity you want to remove
|
||||
:param should_remove_material: If true, materials with no users will be removed
|
||||
|
||||
Example:
|
||||
|
||||
.. code:: python
|
||||
|
||||
# Create a material set for steel stud partition walls.
|
||||
material_set = ifcopenshell.api.material.add_material_set(model,
|
||||
name="Window", set_type="IfcMaterialConstituentSet")
|
||||
|
||||
gypsum = ifcopenshell.api.material.add_material(model, name="PB01", category="gypsum")
|
||||
steel = ifcopenshell.api.material.add_material(model, name="ST01", category="steel")
|
||||
|
||||
# Now let's use those materials as three layers in our set, such
|
||||
# that the steel studs are sandwiched by the gypsum. Let's imagine
|
||||
# we're setting the layer thickness in millimeters.
|
||||
layer1 = ifcopenshell.api.material.add_layer(model, layer_set=material_set, material=gypsum)
|
||||
ifcopenshell.api.material.edit_layer(model, layer=layer1, attributes={"LayerThickness": 13})
|
||||
layer2 = ifcopenshell.api.material.add_layer(model, layer_set=material_set, material=steel)
|
||||
ifcopenshell.api.material.edit_layer(model, layer=layer2, attributes={"LayerThickness": 92})
|
||||
layer3 = ifcopenshell.api.material.add_layer(model, layer_set=material_set, material=gypsum)
|
||||
ifcopenshell.api.material.edit_layer(model, layer=layer3, attributes={"LayerThickness": 13})
|
||||
|
||||
# Let's remove the last layer, such that the wall might be clad only
|
||||
# one one side such as to line a services riser.
|
||||
ifcopenshell.api.material.remove_layer(model, layer=layer3)
|
||||
"""
|
||||
material = layer.Material
|
||||
file.remove(layer)
|
||||
if material and should_remove_material:
|
||||
ifcopenshell.util.element.remove_deep2(file, material)
|
||||
@@ -0,0 +1,56 @@
|
||||
# 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
|
||||
|
||||
|
||||
def remove_list_item(
|
||||
file: ifcopenshell.file, material_list: ifcopenshell.entity_instance, material_index: int = 0
|
||||
) -> None:
|
||||
"""Removes an item in an material list
|
||||
|
||||
Note that it is invalid to have zero items in a list, so you should leave
|
||||
at least one item to ensure a valid IFC dataset.
|
||||
|
||||
:param material_list: The IfcMaterialList entity you want to remove an
|
||||
item from.
|
||||
:param material_index: The index of the material you want to remove from
|
||||
the list. Starts counting at 0. Defaults to 0.
|
||||
:return: None
|
||||
|
||||
Example:
|
||||
|
||||
.. code:: python
|
||||
|
||||
# Create a material list for aluminium windows.
|
||||
material_set = ifcopenshell.api.material.add_material_set(model,
|
||||
name="Window", set_type="IfcMaterialMaterialList")
|
||||
|
||||
aluminium = ifcopenshell.api.material.add_material(model, name="AL01", category="aluminium")
|
||||
glass = ifcopenshell.api.material.add_material(model, name="GLZ01", category="glass")
|
||||
|
||||
# Now let's use those materials as two items in our list.
|
||||
ifcopenshell.api.material.add_list_item(model, material_list=material_set, material=aluminium)
|
||||
ifcopenshell.api.material.add_list_item(model, material_list=material_set, material=glass)
|
||||
|
||||
# Let's remove the glass
|
||||
ifcopenshell.api.material.remove_list_item(model, material_list=material_set, material_index=1)
|
||||
"""
|
||||
materials = list(material_list.Materials)
|
||||
materials.pop(material_index)
|
||||
material_list.Materials = materials
|
||||
@@ -0,0 +1,75 @@
|
||||
# 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 remove_material(file: ifcopenshell.file, material: ifcopenshell.entity_instance) -> None:
|
||||
"""Removes a material
|
||||
|
||||
If the material is used in a material set, the corresponding layer,
|
||||
profile, or constituent is also removed. Note that this may result in a
|
||||
material set with zero items in it, which is invalid, so the user must
|
||||
take care of this situation themselves.
|
||||
|
||||
:param material: The IfcMaterial entity you want to remove
|
||||
:return: None
|
||||
|
||||
Example:
|
||||
|
||||
.. code:: python
|
||||
|
||||
# Create a material
|
||||
aluminium = ifcopenshell.api.material.add_material(model, name="AL01", category="aluminium")
|
||||
|
||||
# ... and remove it
|
||||
ifcopenshell.api.material.remove_material(model, material=aluminium)
|
||||
"""
|
||||
inverse_elements = file.get_inverse(material)
|
||||
file.remove(material)
|
||||
# TODO: Right now, we we choose only to delete set items (e.g. a layer) but not the material set
|
||||
# This can lead to invalid material sets, but we assume the user will deal with it
|
||||
for inverse in inverse_elements:
|
||||
if inverse.is_a("IfcMaterialConstituent"):
|
||||
file.remove(inverse)
|
||||
elif inverse.is_a("IfcMaterialLayer"):
|
||||
file.remove(inverse)
|
||||
elif inverse.is_a("IfcMaterialProfile"):
|
||||
file.remove(inverse)
|
||||
elif inverse.is_a("IfcRelAssociatesMaterial"):
|
||||
history = inverse.OwnerHistory
|
||||
file.remove(inverse)
|
||||
if history:
|
||||
ifcopenshell.util.element.remove_deep2(file, history)
|
||||
elif inverse.is_a("IfcMaterialProperties"):
|
||||
if file.schema != "IFC2X3":
|
||||
props = inverse.Properties
|
||||
else:
|
||||
# only IfcExtendedMaterialProperties have properties in IFC2X3
|
||||
props = getattr(inverse, "ExtendedProperties", None)
|
||||
props = props or []
|
||||
for prop in props:
|
||||
file.remove(prop)
|
||||
file.remove(inverse)
|
||||
elif inverse.is_a("IfcMaterialDefinitionRepresentation"):
|
||||
for representation in inverse.Representations:
|
||||
for item in representation.Items:
|
||||
file.remove(item)
|
||||
file.remove(representation)
|
||||
file.remove(inverse)
|
||||
@@ -0,0 +1,94 @@
|
||||
# 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/>.
|
||||
|
||||
import ifcopenshell
|
||||
import ifcopenshell.api.material
|
||||
import ifcopenshell.util.element
|
||||
|
||||
|
||||
def remove_material_set(file: ifcopenshell.file, material: ifcopenshell.entity_instance) -> None:
|
||||
"""Removes a material set
|
||||
|
||||
All set items, such as layers, profiles, or constituents will also be
|
||||
removed. All set usages are also removed.
|
||||
|
||||
However, the materials and profile curves used by the layers,
|
||||
profiles and constituents will not be removed.
|
||||
|
||||
:param material: The IfcMaterialLayerSet, IfcMaterialConstituentSet,
|
||||
IfcMaterialProfileSet entity you want to remove.
|
||||
:return: None
|
||||
|
||||
Example:
|
||||
|
||||
.. code:: python
|
||||
|
||||
# Create a material set
|
||||
material_set = ifcopenshell.api.material.add_material_set(model,
|
||||
name="GYP-ST-GYP", set_type="IfcMaterialLayerSet")
|
||||
|
||||
# Create some materials
|
||||
gypsum = ifcopenshell.api.material.add_material(model, name="PB01", category="gypsum")
|
||||
steel = ifcopenshell.api.material.add_material(model, name="ST01", category="steel")
|
||||
|
||||
# Add some layers
|
||||
layer = ifcopenshell.api.material.add_layer(model, layer_set=material_set, material=gypsum)
|
||||
layer = ifcopenshell.api.material.add_layer(model, layer_set=material_set, material=steel)
|
||||
layer = ifcopenshell.api.material.add_layer(model, layer_set=material_set, material=gypsum)
|
||||
|
||||
# Completely delete the set and all layers. The gypsum and steel
|
||||
# material still exist, though.
|
||||
ifcopenshell.api.material.remove_material_set(model, material=material_set)
|
||||
"""
|
||||
|
||||
# Remove all usages for sets.
|
||||
has_usages = material.is_a("IfcMaterialLayerSet") or material.is_a("IfcMaterialProfileSet")
|
||||
if has_usages:
|
||||
# Usage is invalid if it is not associated with some element,
|
||||
# so we can remove usages through unassignment.
|
||||
elements = ifcopenshell.util.element.get_elements_by_material(file, material)
|
||||
if elements:
|
||||
ifcopenshell.api.material.unassign_material(file, products=list(elements))
|
||||
|
||||
if material.is_a("IfcMaterialLayerSet"):
|
||||
set_items = material.MaterialLayers or []
|
||||
elif material.is_a("IfcMaterialProfileSet"):
|
||||
set_items = material.MaterialProfiles or []
|
||||
elif material.is_a("IfcMaterialConstituentSet"):
|
||||
set_items = material.MaterialConstituents or []
|
||||
elif material.is_a("IfcMaterialList"):
|
||||
set_items = []
|
||||
else:
|
||||
raise ValueError(f"Unknown material set type: {material.is_a()}")
|
||||
for set_item in set_items:
|
||||
file.remove(set_item)
|
||||
|
||||
inverse_elements = file.get_inverse(material)
|
||||
file.remove(material)
|
||||
|
||||
for inverse in inverse_elements:
|
||||
if inverse.is_a("IfcRelAssociatesMaterial"):
|
||||
# NOTE: for has_usages already handled by unassign_material.
|
||||
history = inverse.OwnerHistory
|
||||
file.remove(inverse)
|
||||
if history:
|
||||
ifcopenshell.util.element.remove_deep2(file, history)
|
||||
elif inverse.is_a("IfcMaterialProperties"):
|
||||
for prop in inverse.Properties or []:
|
||||
file.remove(prop)
|
||||
file.remove(inverse)
|
||||
@@ -0,0 +1,80 @@
|
||||
# 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.util.element
|
||||
|
||||
|
||||
def remove_profile(
|
||||
file: ifcopenshell.file,
|
||||
profile: ifcopenshell.entity_instance,
|
||||
should_remove_profile_def: bool = False,
|
||||
should_remove_material: bool = False,
|
||||
) -> None:
|
||||
"""Removes a profile item from a profile set
|
||||
|
||||
Note that it is invalid to have zero items in a set, so you should leave
|
||||
at least one profile to ensure a valid IFC dataset.
|
||||
|
||||
:param profile: The IfcMaterialProfile entity you want to remove
|
||||
:param should_remove_profile_def: If true, profile defs with no users will be removed
|
||||
:param should_remove_material: If true, materials with no users will be removed
|
||||
|
||||
Example:
|
||||
|
||||
.. code:: python
|
||||
|
||||
# First, let's create a material set.
|
||||
material_set = ifcopenshell.api.material.add_material_set(model,
|
||||
name="B1", set_type="IfcMaterialProfileSet")
|
||||
|
||||
# Create a steel material.
|
||||
steel = ifcopenshell.api.material.add_material(model, name="ST01", category="steel")
|
||||
|
||||
# Create an I-beam profile curve. Notice how we name our profiles
|
||||
# based on standardised steel profile names.
|
||||
hea100 = file.create_entity(
|
||||
"IfcIShapeProfileDef", ProfileName="HEA100", ProfileType="AREA",
|
||||
OverallWidth=100, OverallDepth=96, WebThickness=5, FlangeThickness=8, FilletRadius=12,
|
||||
)
|
||||
|
||||
# Define that steel material and cross section as a single profile item.
|
||||
ifcopenshell.api.material.add_profile(model,
|
||||
profile_set=material_set, material=steel, profile=hea100)
|
||||
|
||||
# Imagine a welded square along the length of the profile.
|
||||
welded_square = ifcopenshell.api.profile.add_arbitrary_profile(model,
|
||||
profile=[(.0025, .0025), (.0325, .0025), (.0325, -.0025), (.0025, -.0025), (.0025, .0025)])
|
||||
weld_profile = ifcopenshell.api.material.add_profile(model,
|
||||
profile_set=material_set, material=steel, profile=welded_square)
|
||||
|
||||
# Let's remove our welded square.
|
||||
ifcopenshell.api.material.remove_profile(model, profile=weld_profile)
|
||||
"""
|
||||
|
||||
subelements = set()
|
||||
for attribute in profile:
|
||||
if isinstance(attribute, ifcopenshell.entity_instance):
|
||||
subelements.add(attribute)
|
||||
file.remove(profile)
|
||||
for subelement in subelements:
|
||||
if subelement.is_a("IfcMaterial") and not should_remove_material:
|
||||
continue
|
||||
elif subelement.is_a("IfcProfileDef") and not should_remove_profile_def:
|
||||
continue
|
||||
ifcopenshell.util.element.remove_deep2(file, subelement)
|
||||
@@ -0,0 +1,69 @@
|
||||
# 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
|
||||
|
||||
|
||||
def reorder_set_item(
|
||||
file: ifcopenshell.file, material_set: ifcopenshell.entity_instance, old_index: int = 0, new_index: int = 0
|
||||
) -> None:
|
||||
"""Reorders an item in a material set
|
||||
|
||||
In some material sets, the order have meaning, like in a layer set. In
|
||||
other cases, it is purely for human convenience.
|
||||
|
||||
:param material_set: The IfcMaterialSet which you want to reorder an
|
||||
item in.
|
||||
:param old_index: The index of the item you want to move. This starts
|
||||
counting from 0.
|
||||
:param new_index: The index of the new position the item will move to.
|
||||
This starts counting from 0.
|
||||
:return: None
|
||||
|
||||
Example:
|
||||
|
||||
.. code:: python
|
||||
|
||||
material_set = ifcopenshell.api.material.add_material_set(model,
|
||||
name="Window", set_type="IfcMaterialList")
|
||||
|
||||
aluminium = ifcopenshell.api.material.add_material(model, name="AL01", category="aluminium")
|
||||
glass = ifcopenshell.api.material.add_material(model, name="GLZ01", category="glass")
|
||||
|
||||
# Now let's use those materials as two items in our list.
|
||||
ifcopenshell.api.material.add_list_item(model, material_list=material_set, material=aluminium)
|
||||
ifcopenshell.api.material.add_list_item(model, material_list=material_set, material=glass)
|
||||
|
||||
# Switch the order around, this has no meaning for a list, so this
|
||||
# is just for fun.
|
||||
ifcopenshell.api.material.reorder_set_item(model,
|
||||
material_set=material_set, old_index=0, new_index=1)
|
||||
"""
|
||||
if material_set.is_a("IfcMaterialConstituentSet"):
|
||||
set_name = "MaterialConstituents"
|
||||
elif material_set.is_a("IfcMaterialLayerSet"):
|
||||
set_name = "MaterialLayers"
|
||||
elif material_set.is_a("IfcMaterialProfileSet"):
|
||||
set_name = "MaterialProfiles"
|
||||
elif material_set.is_a("IfcMaterialList"):
|
||||
set_name = "Materials"
|
||||
else:
|
||||
raise ValueError(f"Unexpected material set type: '{material_set.is_a()}'.")
|
||||
|
||||
items = list(getattr(material_set, set_name) or [])
|
||||
items.insert(new_index, items.pop(old_index))
|
||||
setattr(material_set, set_name, items)
|
||||
@@ -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/>.
|
||||
|
||||
import ifcopenshell.api.material
|
||||
import ifcopenshell.api.style
|
||||
import ifcopenshell.util.element
|
||||
import ifcopenshell.util.representation
|
||||
|
||||
|
||||
def set_shape_aspect_constituents(
|
||||
file: ifcopenshell.file,
|
||||
*,
|
||||
element: ifcopenshell.entity_instance,
|
||||
context: ifcopenshell.entity_instance,
|
||||
materials: dict[str, ifcopenshell.entity_instance],
|
||||
) -> None:
|
||||
"""Assigns a material constituent set and sets styles based on shape aspects
|
||||
|
||||
An IFC element may be assigned to a set of material constituents. For
|
||||
example, a window may have a framing material and a glazing material. Each
|
||||
constituent may have a name, such as "Framing" (which may be assigned to an
|
||||
"Aluminium" material), and "Glazing" (assigned to a "Laminated Low-e Glass"
|
||||
material).
|
||||
|
||||
An IFC element's geometry may be composed of multiple geometric items.
|
||||
These geometric items may have names, known as "Shape Aspects". For
|
||||
example a solid extrusion for the framing named "Framing" and a solid
|
||||
extrusion for the glass panel named "Glazing".
|
||||
|
||||
A material may be associated with a style (i.e. colour). For example, a
|
||||
grey style for the "Aluminium" material and a transparent blue style for
|
||||
the "Laminated Low-e Glass" material.
|
||||
|
||||
These three concepts of material constituents, shape aspects, and
|
||||
associated styles are correlated. For example, if the name (e.g. "Framing")
|
||||
of a material constituent and a shape aspect correlate, that means that the
|
||||
geometric item inherits the style (i.e. grey).
|
||||
|
||||
This function lets you specify named material constituents, and it'll
|
||||
create a constituent set assigned to the element with those names. It'll
|
||||
then find any geometric representation items with shape aspects matching
|
||||
those names, and assign the correlating style.
|
||||
|
||||
If an assigned material constituent set already exists matching those
|
||||
values, it will be reused. If the values do not match, the existing
|
||||
material constituent set will be removed if it is not used by anything
|
||||
else.
|
||||
|
||||
:param element: The IfcProduct or IfcTypeProduct
|
||||
:param context: The IfcGeometricRepresentationContext, typically the body
|
||||
context. You can get this via
|
||||
:func:`ifcopenshell.util.representation.get_context`.
|
||||
:param materials: The key is the name of the constituent, and the value is
|
||||
the IfcMaterial.
|
||||
|
||||
Example:
|
||||
|
||||
.. code:: python
|
||||
|
||||
# Create two materials
|
||||
aluminium = ifcopenshell.api.material.add_material(model, name="AL01", category="aluminium")
|
||||
glass = ifcopenshell.api.material.add_material(model, name="GLZ01", category="glass")
|
||||
|
||||
# Auto assign material constituents and styles to items based on shape aspects
|
||||
ifcopenshell.api.material.set_shape_aspect_constituents(
|
||||
model, element=window, context=body, materials={
|
||||
"Framing": aluminium
|
||||
"Lining": aluminium
|
||||
"Glazing": glass
|
||||
})
|
||||
"""
|
||||
should_create_new_material_set = False
|
||||
if material := ifcopenshell.util.element.get_material(element):
|
||||
if (
|
||||
material.is_a("IfcMaterialConstituent")
|
||||
and len(names := [c.Name for c in material.MaterialConstituents]) == len(materials)
|
||||
and set(names) == set(materials.keys())
|
||||
):
|
||||
should_create_new_material_set = False
|
||||
else:
|
||||
should_create_new_material_set = True
|
||||
ifcopenshell.api.material.unassign_material(file, products=[element])
|
||||
if not material.is_a("IfcMaterial") and not file.get_total_inverses(material):
|
||||
ifcopenshell.api.material.remove_material_set(file, material=material)
|
||||
else:
|
||||
should_create_new_material_set = True
|
||||
if should_create_new_material_set:
|
||||
material_set = ifcopenshell.api.material.add_material_set(file, set_type="IfcMaterialConstituentSet")
|
||||
for name, material in materials.items():
|
||||
ifcopenshell.api.material.add_constituent(file, constituent_set=material_set, material=material, name=name)
|
||||
ifcopenshell.api.material.assign_material(file, products=[element], material=material_set)
|
||||
|
||||
styles = {n: ifcopenshell.util.representation.get_material_style(m, context) for n, m in materials.items()}
|
||||
representation = ifcopenshell.util.representation.get_representation(element, context=context)
|
||||
representation = ifcopenshell.util.representation.resolve_representation(representation)
|
||||
for item in representation.Items:
|
||||
if aspect := ifcopenshell.util.representation.get_item_shape_aspect(representation, item):
|
||||
if style := styles.get(aspect.Name, None):
|
||||
ifcopenshell.api.style.assign_item_style(file, item=item, style=style)
|
||||
@@ -0,0 +1,135 @@
|
||||
# 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.owner
|
||||
import ifcopenshell.util.element
|
||||
|
||||
|
||||
def unassign_material(file: ifcopenshell.file, products: list[ifcopenshell.entity_instance]) -> None:
|
||||
"""Removes any material relationship with the list of products
|
||||
|
||||
A product can only have one material assigned to it, which is why it is
|
||||
not necessary to specify the material to unassign. The material is not
|
||||
removed, only the relationship is removed.
|
||||
|
||||
If the product does not have a material, nothing happens.
|
||||
|
||||
Unassigning a LayerSet or ProfileSet from the product type will also
|
||||
remove all Usages of the set.
|
||||
|
||||
:param products: The list IfcProducts that may or may not have a material
|
||||
:return: None
|
||||
|
||||
Example:
|
||||
|
||||
.. code:: python
|
||||
|
||||
concrete = ifcopenshell.api.material.add_material(model, name="CON01", category="concrete")
|
||||
|
||||
# Let's imagine a concrete bench made out of concrete.
|
||||
bench_type = ifcopenshell.api.root.create_entity(model, ifc_class="IfcFurnitureType")
|
||||
ifcopenshell.api.material.assign_material(model,
|
||||
products=[bench_type], type="IfcMaterial", material=concrete)
|
||||
|
||||
# Let's change our mind and remove the concrete assignment. The
|
||||
# concrete material still exists, but the bench is no longer made
|
||||
# out of concrete now.
|
||||
ifcopenshell.api.material.unassign_material(model, products=[bench_type])
|
||||
"""
|
||||
usecase = Usecase()
|
||||
usecase.file = file
|
||||
return usecase.execute(products)
|
||||
|
||||
|
||||
class Usecase:
|
||||
file: ifcopenshell.file
|
||||
|
||||
def execute(self, products: list[ifcopenshell.entity_instance]) -> None:
|
||||
if not products:
|
||||
return
|
||||
self.products = set(products)
|
||||
|
||||
self.remove_material_usages_from_types()
|
||||
self.unassign_materials()
|
||||
|
||||
def remove_material_usages_from_types(self) -> None:
|
||||
# remove material usages from types
|
||||
for product in self.products:
|
||||
if not product.is_a("IfcTypeObject"):
|
||||
continue
|
||||
material = ifcopenshell.util.element.get_material(product)
|
||||
if not material:
|
||||
continue
|
||||
if material.is_a() in ["IfcMaterialLayerSet", "IfcMaterialProfileSet"]:
|
||||
# Remove set usages
|
||||
# TODO: be more considerate and remove only usages
|
||||
# associated with the set + product type, not all usages?
|
||||
for inverse in self.file.get_inverse(material):
|
||||
if self.file.schema == "IFC2X3":
|
||||
if not inverse.is_a("IfcMaterialLayerSetUsage"):
|
||||
continue
|
||||
# in IFC2X3 there is no .AssociatedTo
|
||||
for inverse2 in self.file.get_inverse(inverse):
|
||||
if inverse2.is_a("IfcRelAssociatesMaterial"):
|
||||
history = inverse2.OwnerHistory
|
||||
self.file.remove(inverse2)
|
||||
if history:
|
||||
ifcopenshell.util.element.remove_deep2(self.file, history)
|
||||
else:
|
||||
if not inverse.is_a("IfcMaterialUsageDefinition"):
|
||||
continue
|
||||
for rel in inverse.AssociatedTo:
|
||||
history = rel.OwnerHistory
|
||||
self.file.remove(rel)
|
||||
if history:
|
||||
ifcopenshell.util.element.remove_deep2(self.file, history)
|
||||
self.file.remove(inverse)
|
||||
|
||||
def unassign_materials(self) -> None:
|
||||
associations: set[ifcopenshell.entity_instance] = set()
|
||||
for product in self.products:
|
||||
associations.update(product.HasAssociations)
|
||||
|
||||
# we ensure that `associations` won't have removed elements
|
||||
# to avoid crash during `material_inverses.issubset(associations)`
|
||||
while associations:
|
||||
rel = next(iter(associations))
|
||||
|
||||
if not rel.is_a("IfcRelAssociatesMaterial"):
|
||||
associations.remove(rel)
|
||||
else:
|
||||
material = rel.RelatingMaterial
|
||||
related_objects = set(rel.RelatedObjects) - self.products
|
||||
|
||||
if material.is_a() in ["IfcMaterialLayerSetUsage", "IfcMaterialProfileSetUsage"]:
|
||||
# Warning: this may leave the model in a non-compliant state.
|
||||
material_inverses = set(self.file.get_inverse(material))
|
||||
if material_inverses.issubset(associations) and not related_objects:
|
||||
self.file.remove(material)
|
||||
associations.remove(rel)
|
||||
|
||||
if not related_objects:
|
||||
history = rel.OwnerHistory
|
||||
self.file.remove(rel)
|
||||
if history:
|
||||
ifcopenshell.util.element.remove_deep2(self.file, history)
|
||||
continue
|
||||
rel.RelatedObjects = list(related_objects)
|
||||
ifcopenshell.api.owner.update_owner_history(self.file, element=rel)
|
||||
Reference in New Issue
Block a user