From c7ca87f9c47c9b1dcbecc5dd8d89270bc3d35664 Mon Sep 17 00:00:00 2001 From: Andrej730 Date: Wed, 21 Feb 2024 14:33:01 +0500 Subject: [PATCH] api.unassign_representation_styles --- .../style/unassign_representation_styles.py | 87 ++++++++++++++ .../test_unassign_representation_styles.py | 111 ++++++++++++++++++ 2 files changed, 198 insertions(+) create mode 100644 src/ifcopenshell-python/ifcopenshell/api/style/unassign_representation_styles.py create mode 100644 src/ifcopenshell-python/test/api/style/test_unassign_representation_styles.py diff --git a/src/ifcopenshell-python/ifcopenshell/api/style/unassign_representation_styles.py b/src/ifcopenshell-python/ifcopenshell/api/style/unassign_representation_styles.py new file mode 100644 index 0000000000..6211a9f4dd --- /dev/null +++ b/src/ifcopenshell-python/ifcopenshell/api/style/unassign_representation_styles.py @@ -0,0 +1,87 @@ +# IfcOpenShell - IFC toolkit and geometry engine +# Copyright (C) 2021 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 . + + +class Usecase: + def __init__(self, file, shape_representation=None, styles=None, should_use_presentation_style_assignment=False): + """Unassigns styles directly assigned to an object representation + + This does the inverse of assign_representation_styles. + + :param shape_representation: The IfcShapeRepresentation of the object + that you want to unassign styles from. + :type shape_representation: ifcopenshell.entity_instance.entity_instance + :param styles: A list of presentation styles, typically IfcSurfaceStyle. + The number of items in the list should correlate with the number of + items in the shape_representation's Items attribute. If you have + more items than styles, the last style is used. + :type styles: list[ifcopenshell.entity_instance.entity_instance] + :param should_use_presentation_style_assignment: This is a technical + detail to accomodate a bug in Revit. This should always be left as + the default of False, unless you are finding that colours aren't + 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 + + Example: + + .. code:: python + + ifcopenshell.api.run("style.unassign_representation_styles", model, + shape_representation=representation, styles=[style]) + """ + self.file = file + self.settings = { + "shape_representation": shape_representation, + "styles": styles or [], + "should_use_presentation_style_assignment": should_use_presentation_style_assignment, + } + + def execute(self): + if not self.settings["styles"]: + return [] + 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 + for item in element.Items: + if not item.is_a("IfcGeometricRepresentationItem"): + continue + + if not item.StyledByItem: + continue + + item = item.StyledByItem[0] + if use_style_assignment: + for style_ in item.Styles: + if style_.is_a("IfcPresentationStyleAssignment"): + self.remove_styles(style_) + self.remove_styles(item) + + def remove_styles(self, item): + """Removes styles from a styled item or a style assignment + and purges item if doesn't have any styles after""" + styles = [s for s in item.Styles if s not in self.settings["styles"]] + if not styles: + self.file.remove(item) + elif len(styles) != len(item.Styles): + item.Styles = styles diff --git a/src/ifcopenshell-python/test/api/style/test_unassign_representation_styles.py b/src/ifcopenshell-python/test/api/style/test_unassign_representation_styles.py new file mode 100644 index 0000000000..4c28a17e44 --- /dev/null +++ b/src/ifcopenshell-python/test/api/style/test_unassign_representation_styles.py @@ -0,0 +1,111 @@ +# 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 TestUnassignRepresentationStyles(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 + ) + 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) + + ifcopenshell.api.run( + "style.unassign_representation_styles", self.file, styles=[style], shape_representation=representation + ) + assert item.StyledByItem[0].Styles == (style2,) + + # unassigning last style removes styled item + ifcopenshell.api.run( + "style.unassign_representation_styles", self.file, styles=[style2], shape_representation=representation + ) + assert len(item.StyledByItem) == 0 + assert len(self.file.by_type("IfcStyledItem")) == 0 + + 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, + ) + style_assignment = self.file.by_type("IfcPresentationStyleAssignment")[0] + + # 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 style_assignment.Styles == (style, style2) + + ifcopenshell.api.run( + "style.unassign_representation_styles", + self.file, + styles=[style], + shape_representation=representation, + should_use_presentation_style_assignment=True, + ) + assert style_assignment.Styles == (style2,) + + # unassigning last style removes styled item and presentation style + ifcopenshell.api.run( + "style.unassign_representation_styles", + self.file, + styles=[style2], + shape_representation=representation, + should_use_presentation_style_assignment=True, + ) + assert len(item.StyledByItem) == 0 + assert len(self.file.by_type("IfcStyledItem")) == 0 + assert len(self.file.by_type("IfcPresentationStyleAssignment")) == 0 + + +class TestUnassignRepresentationStylesIFC2X3(test.bootstrap.IFC2X3): + def test_run(self): + TestUnassignRepresentationStyles.test_assign_using_style_assignment(self)