From 863a8248af79078749bacafba8950a4b589379cf Mon Sep 17 00:00:00 2001 From: Andrej730 Date: Wed, 21 Feb 2024 10:55:54 +0500 Subject: [PATCH] assign_representation_styles to reuse existing styled item + tests --- .../api/style/assign_representation_styles.py | 36 +++++++- .../test_assign_representation_styles.py | 89 +++++++++++++++++++ 2 files changed, 122 insertions(+), 3 deletions(-) create mode 100644 src/ifcopenshell-python/test/api/style/test_assign_representation_styles.py diff --git a/src/ifcopenshell-python/ifcopenshell/api/style/assign_representation_styles.py b/src/ifcopenshell-python/ifcopenshell/api/style/assign_representation_styles.py index d8d787ec6d..3b318d272c 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/style/assign_representation_styles.py +++ b/src/ifcopenshell-python/ifcopenshell/api/style/assign_representation_styles.py @@ -47,8 +47,8 @@ class Usecase: showing up in Revit. In that case, set it to True, but keep in mind that this is no longer a valid IFC. Blame Autodesk. :type should_use_presentation_style_assignment: bool - :return: None - :rtype: None + :return: List of created IfcStyledItems + :rtype: ifcopenshell.entity_instance.entity_instance Example: @@ -101,7 +101,10 @@ class Usecase: def execute(self): if not self.settings["styles"]: return [] + self.settings["styles"] = self.settings["styles"].copy() self.results = [] + use_style_assignment = self.file.schema == "IFC2X3" or self.settings["should_use_presentation_style_assignment"] + for element in self.file.traverse(self.settings["shape_representation"]): if not element.is_a("IfcShapeRepresentation"): continue @@ -112,7 +115,34 @@ class Usecase: # If there are more items than styles, fallback to using the last style style = self.settings["styles"].pop(0) name = style.Name - if self.file.schema == "IFC2X3" or self.settings["should_use_presentation_style_assignment"]: + + # item may had previous styled item + prev_styled_item = next((i for i in item.StyledByItem), None) + if prev_styled_item is not None: + # collect previously assigned styles + assigned_styles = [] + style_assignment = None # try to find some style assignment to reuse + for style_ in prev_styled_item.Styles: + if style_.is_a("IfcPresentationStyleAssignment"): + if style_assignment is None: + style_assignment = style_ + assigned_styles.extend(style_.Styles) + else: # IfcPresentationStyle + assigned_styles.append(style_) + + if style not in assigned_styles: + if use_style_assignment: + if style_assignment is not None: + style_assignment.Styles = style_assignment.Styles + (style,) + else: + style_assignment = self.file.createIfcPresentationStyleAssignment([style]) + prev_styled_item.Styles = prev_styled_item.Styles + (style_assignment,) + else: + prev_styled_item.Styles = prev_styled_item.Styles + (style,) + # if style is in previous styles we also continue + continue + + if use_style_assignment: style_assignment = self.file.createIfcPresentationStyleAssignment([style]) self.results.append(self.file.createIfcStyledItem(item, [style_assignment], name)) else: diff --git a/src/ifcopenshell-python/test/api/style/test_assign_representation_styles.py b/src/ifcopenshell-python/test/api/style/test_assign_representation_styles.py new file mode 100644 index 0000000000..5e4b93626c --- /dev/null +++ b/src/ifcopenshell-python/test/api/style/test_assign_representation_styles.py @@ -0,0 +1,89 @@ +# IfcOpenShell - IFC toolkit and geometry engine +# Copyright (C) 2022 Dion Moult +# +# 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 . + +import pytest +import test.bootstrap +import ifcopenshell +import ifcopenshell.api + + +class TestAssignRepresentationStyles(test.bootstrap.IFC4): + def test_run(self): + element = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcWall") + product_shape = self.file.createIfcProductDefinitionShape() + element.Representation = product_shape + + representation = self.file.createIfcShapeRepresentation() + item = self.file.createIfcExtrudedAreaSolid() + representation.Items = [item] + + style = self.file.createIfcSurfaceStyle() + ifcopenshell.api.run( + "style.assign_representation_styles", self.file, styles=[style], shape_representation=representation + ) + assert item.StyledByItem[0].Styles == (style,) + + # reusing existing styled item + style2 = self.file.createIfcSurfaceStyle() + ifcopenshell.api.run( + "style.assign_representation_styles", self.file, styles=[style2], shape_representation=representation + ) + assert item.StyledByItem[0].Styles == (style, style2) + assert len(self.file.by_type("IfcStyledItem")) == 1 + + def test_assign_using_style_assignment(self): + element = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcWall") + product_shape = self.file.createIfcProductDefinitionShape() + element.Representation = product_shape + + representation = self.file.createIfcShapeRepresentation() + item = self.file.createIfcExtrudedAreaSolid() + representation.Items = [item] + + style = self.file.createIfcSurfaceStyle() + ifcopenshell.api.run( + "style.assign_representation_styles", + self.file, + styles=[style], + shape_representation=representation, + should_use_presentation_style_assignment=True, + ) + assert len(self.file.by_type("IfcPresentationStyleAssignment")) == 1 + style_assignment = self.file.by_type("IfcPresentationStyleAssignment")[0] + assert style_assignment.Styles == (style,) + assert item.StyledByItem[0].Styles == (style_assignment,) + + # reusing existing styled item and style assignment + style2 = self.file.createIfcSurfaceStyle() + ifcopenshell.api.run( + "style.assign_representation_styles", + self.file, + styles=[style2], + shape_representation=representation, + should_use_presentation_style_assignment=True, + ) + + assert len(self.file.by_type("IfcPresentationStyleAssignment")) == 1 + assert item.StyledByItem[0].Styles == (style_assignment,) + assert item.StyledByItem[0].Styles[0].Styles == (style, style2) + assert len(self.file.by_type("IfcStyledItem")) == 1 + + +class TestAssignRepresentationStylesIFC2X3(test.bootstrap.IFC2X3): + def test_run(self): + TestAssignRepresentationStyles.test_assign_using_style_assignment(self)