New API function to set shape aspect constituents

This commit is contained in:
Dion Moult
2025-02-15 18:26:13 +11:00
parent a0fa861b92
commit 81b7f3dc30
2 changed files with 124 additions and 0 deletions
@@ -55,6 +55,7 @@ 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__)
@@ -83,5 +84,6 @@ __all__ = [
"remove_material_set",
"remove_profile",
"reorder_set_item",
"set_shape_aspect_constituents",
"unassign_material",
]
@@ -0,0 +1,122 @@
# 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.style
import ifcopenshell.api.material
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()}
print('styels are', styles)
representation = ifcopenshell.util.representation.get_representation(element, context=context)
print('rep is', representation)
representation = ifcopenshell.util.representation.resolve_representation(representation)
print('rrep is', representation)
for item in representation.Items:
print('checking item', item)
if aspect := ifcopenshell.util.representation.get_item_shape_aspect(representation, item):
print('found', aspect.Name)
if style := styles.get(aspect.Name, None):
print('... and correlating style', style)
ifcopenshell.api.style.assign_item_style(file, item=item, style=style)