Fix #3578. Support querying surface styles in tools like IfcCSV.

This commit is contained in:
Dion Moult
2023-08-14 11:55:15 +10:00
parent 8bf55ab46c
commit ed4abacd1e
3 changed files with 100 additions and 0 deletions
@@ -339,6 +339,8 @@ def get_material(element, should_skip_usage=False, should_inherit=True):
The material may be a single material, material set (layered, profiled, or The material may be a single material, material set (layered, profiled, or
constituent), or a material set usage. constituent), or a material set usage.
:param element: The element to get the material of.
:type element: ifcopenshell.entity_instance.entity_instance
:param should_skip_usage: If set to True, if the material is a material set :param should_skip_usage: If set to True, if the material is a material set
usage, the material set itself will be returned. Useful if you don't usage, the material set itself will be returned. Useful if you don't
care about occurrence usage parameters. If False, the usage will be care about occurrence usage parameters. If False, the usage will be
@@ -378,6 +380,8 @@ def get_materials(element, should_inherit=True):
If the element has a material set, the individual materials of that set are If the element has a material set, the individual materials of that set are
returned as a list. returned as a list.
:param element: The element to get the materials of.
:type element: ifcopenshell.entity_instance.entity_instance
:param should_inherit: If True, any inherited materials from associated :param should_inherit: If True, any inherited materials from associated
types will be considered. types will be considered.
:return: The associated materials of the element. :return: The associated materials of the element.
@@ -403,6 +407,51 @@ def get_materials(element, should_inherit=True):
return [c.Material for c in material.MaterialConstituents] return [c.Material for c in material.MaterialConstituents]
def get_styles(element):
"""Retrieves the styles used in an element's representation.
Styles may be retreived from the material or the body representation.
:param element: The element to get the styles of.
:type element: ifcopenshell.entity_instance.entity_instance
:return: A list of surface styles
:rtype: list[ifcopenshell.entity_instance.entity_instance]
Example:
.. code:: python
wall = file.by_type("IfcWall")[0]
styles = ifcopenshell.util.element.get_styles(wall)
"""
styles = []
materials = ifcopenshell.util.element.get_materials(element)
for material in materials:
for material_definition_representation in material.HasRepresentation or []:
for representation in material_definition_representation.Representations:
for item in representation.Items:
styles.extend([s for s in item.Styles if s.is_a("IfcSurfaceStyle")])
body = ifcopenshell.util.representation.get_representation(element, "Model", "Body", "MODEL_VIEW")
if not body:
return styles
for representation in [body]:
queue = list(representation.Items)
while queue:
item = queue.pop()
if item.is_a("IfcMappedItem"):
queue.extend(item.MappingSource.MappedRepresentation.Items)
if item.is_a("IfcBooleanResult"):
queue.append(item.FirstOperand)
queue.append(item.SecondOperand)
if item.StyledByItem:
styles.extend([s for s in item.StyledByItem[0].Styles if s.is_a("IfcSurfaceStyle")])
return styles
def get_elements_by_material(ifc_file, material): def get_elements_by_material(ifc_file, material):
"""Retrieves the elements related to a material. """Retrieves the elements related to a material.
@@ -520,6 +520,10 @@ class Selector:
value = ifcopenshell.util.element.get_type(value) value = ifcopenshell.util.element.get_type(value)
elif key in ("material", "mat"): elif key in ("material", "mat"):
value = ifcopenshell.util.element.get_material(value, should_skip_usage=True) value = ifcopenshell.util.element.get_material(value, should_skip_usage=True)
elif key in ("materials", "mats"):
value = ifcopenshell.util.element.get_materials(value)
elif key == "styles":
value = ifcopenshell.util.element.get_styles(value)
elif key in ("item", "i"): elif key in ("item", "i"):
if value.is_a("IfcMaterialLayerSet"): if value.is_a("IfcMaterialLayerSet"):
value = value.MaterialLayers value = value.MaterialLayers
@@ -386,6 +386,53 @@ class TestGetMaterial(test.bootstrap.IFC4):
assert subject.get_material(element, should_inherit=False) is None assert subject.get_material(element, should_inherit=False) is None
class TestGetMaterials(test.bootstrap.IFC4):
def test_getting_the_materials_of_a_product(self):
element = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcWall")
material = ifcopenshell.api.run("material.add_material", self.file)
ifcopenshell.api.run("material.assign_material", self.file, product=element, material=material)
assert subject.get_materials(element) == [material]
class TestGetStyles(test.bootstrap.IFC4):
def test_getting_the_styles_of_a_product(self):
ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcProject")
element = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcWall")
assert subject.get_styles(element) == []
model = ifcopenshell.api.run("context.add_context", self.file, context_type="Model")
body = ifcopenshell.api.run("context.add_context", self.file,
context_type="Model", context_identifier="Body", target_view="MODEL_VIEW", parent=model)
material = ifcopenshell.api.run("material.add_material", self.file)
ifcopenshell.api.run("material.assign_material", self.file, product=element, material=material)
style = ifcopenshell.api.run("style.add_style", self.file)
ifcopenshell.api.run("style.add_surface_style", self.file,
style=style, ifc_class="IfcSurfaceStyleShading", attributes={
"SurfaceColour": { "Name": None, "Red": 1.0, "Green": 0.8, "Blue": 0.8 },
"Transparency": 0., # 0 is opaque, 1 is transparent
})
ifcopenshell.api.run("style.assign_material_style", self.file, material=material, style=style, context=body)
assert subject.get_styles(element) == [style]
style2 = ifcopenshell.api.run("style.add_style", self.file)
ifcopenshell.api.run("style.add_surface_style", self.file,
style=style2, ifc_class="IfcSurfaceStyleShading", attributes={
"SurfaceColour": { "Name": None, "Red": 1.0, "Green": 0.8, "Blue": 0.8 },
"Transparency": 0., # 0 is opaque, 1 is transparent
})
representation = ifcopenshell.api.run("geometry.add_wall_representation", self.file,
context=body, length=5, height=3, thickness=0.118)
ifcopenshell.api.run("geometry.assign_representation", self.file, product=element, representation=representation)
ifcopenshell.api.run("style.assign_representation_styles", self.file, shape_representation=representation, styles=[style2])
assert subject.get_styles(element) == [style, style2]
class TestGetElementsByMaterial(test.bootstrap.IFC4): class TestGetElementsByMaterial(test.bootstrap.IFC4):
def test_getting_elements_of_a_material(self): def test_getting_elements_of_a_material(self):
element = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcWall") element = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcWall")