style.assign_representation_styles to replace previously assigned styles

as probably in the most cases user would want 1 style per 1 representation item

After this it's finally possible in BBIM to assign styles to representation items through the materials: https://imgur.com/a/cgiJ1KV
This commit is contained in:
Andrej730
2024-02-22 13:41:34 +05:00
parent f872906e0c
commit a971860094
2 changed files with 91 additions and 25 deletions
@@ -18,7 +18,14 @@
class Usecase: class Usecase:
def __init__(self, file, shape_representation=None, styles=None, should_use_presentation_style_assignment=False): def __init__(
self,
file,
shape_representation=None,
styles=None,
replace_previous_same_type_style=True,
should_use_presentation_style_assignment=False,
):
"""Assigns a style directly to an object representation """Assigns a style directly to an object representation
A style may either be assigned directly to an object's representation, A style may either be assigned directly to an object's representation,
@@ -41,6 +48,9 @@ class Usecase:
items in the shape_representation's Items attribute. If you have items in the shape_representation's Items attribute. If you have
more items than styles, the last style is used. more items than styles, the last style is used.
:type styles: list[ifcopenshell.entity_instance.entity_instance] :type styles: list[ifcopenshell.entity_instance.entity_instance]
:param replace_previous_same_type_style: Remove previously assigned styles
of the same type as currently assign style`. Defaults to `True`.
:type replace_previous_same_type_style: bool
:param should_use_presentation_style_assignment: This is a technical :param should_use_presentation_style_assignment: This is a technical
detail to accomodate a bug in Revit. This should always be left as 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 the default of False, unless you are finding that colours aren't
@@ -95,6 +105,7 @@ class Usecase:
self.settings = { self.settings = {
"shape_representation": shape_representation, "shape_representation": shape_representation,
"styles": styles or [], "styles": styles or [],
"replace_previous_same_type_style": replace_previous_same_type_style,
"should_use_presentation_style_assignment": should_use_presentation_style_assignment, "should_use_presentation_style_assignment": should_use_presentation_style_assignment,
} }
@@ -104,6 +115,7 @@ class Usecase:
self.settings["styles"] = self.settings["styles"].copy() self.settings["styles"] = self.settings["styles"].copy()
self.results = [] self.results = []
use_style_assignment = self.file.schema == "IFC2X3" or self.settings["should_use_presentation_style_assignment"] use_style_assignment = self.file.schema == "IFC2X3" or self.settings["should_use_presentation_style_assignment"]
replace_previous_same_type_style = self.settings["replace_previous_same_type_style"]
for element in self.file.traverse(self.settings["shape_representation"]): for element in self.file.traverse(self.settings["shape_representation"]):
if not element.is_a("IfcShapeRepresentation"): if not element.is_a("IfcShapeRepresentation"):
@@ -115,36 +127,67 @@ class Usecase:
# If there are more items than styles, fallback to using the last style # If there are more items than styles, fallback to using the last style
style = self.settings["styles"].pop(0) style = self.settings["styles"].pop(0)
name = style.Name name = style.Name
current_style_type = style.is_a()
# item may had previous styled item # item may had previous styled item
prev_styled_item = next((i for i in item.StyledByItem), None) prev_styled_item = next((i for i in item.StyledByItem), None)
if prev_styled_item is not None: style_assignment = None # try to find some style assignment to reuse
# collect previously assigned styles
assigned_styles = [] if prev_styled_item is None:
style_assignment = None # try to find some style assignment to reuse if use_style_assignment:
style_assignment = self.file.createIfcPresentationStyleAssignment([style])
self.results.append(self.file.createIfcStyledItem(item, [style_assignment], name))
else:
self.results.append(self.file.createIfcStyledItem(item, [style], name))
continue
if replace_previous_same_type_style:
self.remove_same_type_styles(prev_styled_item, current_style_type, remove_item=False)
for style_ in prev_styled_item.Styles: for style_ in prev_styled_item.Styles:
if style_.is_a("IfcPresentationStyleAssignment"): if style_.is_a("IfcPresentationStyleAssignment"):
if style_assignment is None: if use_style_assignment and style_assignment is None:
style_assignment = style_ style_assignment = style_
assigned_styles.extend(style_.Styles) self.remove_same_type_styles(style_assignment, current_style_type, remove_item=False)
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: else:
style_assignment = self.file.createIfcPresentationStyleAssignment([style]) self.remove_same_type_styles(style_assignment, current_style_type, remove_item=True)
prev_styled_item.Styles = prev_styled_item.Styles + (style_assignment,)
if use_style_assignment:
if style_assignment:
style_assignment.Styles = style_assignment.Styles + (style,)
else: else:
prev_styled_item.Styles = prev_styled_item.Styles + (style,) style_assignment = self.file.createIfcPresentationStyleAssignment([style])
# if style is in previous styles we also continue prev_styled_item.Styles = prev_styled_item.Styles + (style_assignment,)
else:
prev_styled_item.Styles = prev_styled_item.Styles + (style,)
continue
# collect previously assigned styles
assigned_styles = []
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 in assigned_styles:
continue continue
if use_style_assignment: if use_style_assignment:
style_assignment = self.file.createIfcPresentationStyleAssignment([style]) if style_assignment is not None:
self.results.append(self.file.createIfcStyledItem(item, [style_assignment], name)) 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: else:
self.results.append(self.file.createIfcStyledItem(item, [style], name)) prev_styled_item.Styles = prev_styled_item.Styles + (style,)
return self.results return self.results
def remove_same_type_styles(self, style_item, current_style_type: str, remove_item: bool) -> None:
styles = [s for s in style_item.Styles if s.is_a() != current_style_type]
if remove_item and not styles:
self.file.remove(style_item)
else:
style_item.Styles = styles
@@ -38,14 +38,22 @@ class TestAssignRepresentationStyles(test.bootstrap.IFC4):
) )
assert item.StyledByItem[0].Styles == (style,) assert item.StyledByItem[0].Styles == (style,)
# reusing existing styled item # reusing existing styled item for different style type
style2 = self.file.createIfcSurfaceStyle() style2 = self.file.createIfcFillAreaStyle()
ifcopenshell.api.run( ifcopenshell.api.run(
"style.assign_representation_styles", self.file, styles=[style2], shape_representation=representation "style.assign_representation_styles", self.file, styles=[style2], shape_representation=representation
) )
assert item.StyledByItem[0].Styles == (style, style2) assert item.StyledByItem[0].Styles == (style, style2)
assert len(self.file.by_type("IfcStyledItem")) == 1 assert len(self.file.by_type("IfcStyledItem")) == 1
# replacing existing style of the same type
style3 = self.file.createIfcSurfaceStyle()
ifcopenshell.api.run(
"style.assign_representation_styles", self.file, styles=[style3], shape_representation=representation
)
assert item.StyledByItem[0].Styles == (style2, style3)
assert len(self.file.by_type("IfcStyledItem")) == 1
def test_assign_using_style_assignment(self): def test_assign_using_style_assignment(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")
product_shape = self.file.createIfcProductDefinitionShape() product_shape = self.file.createIfcProductDefinitionShape()
@@ -68,8 +76,8 @@ class TestAssignRepresentationStyles(test.bootstrap.IFC4):
assert style_assignment.Styles == (style,) assert style_assignment.Styles == (style,)
assert item.StyledByItem[0].Styles == (style_assignment,) assert item.StyledByItem[0].Styles == (style_assignment,)
# reusing existing styled item and style assignment # reusing existing styled item for different style type
style2 = self.file.createIfcSurfaceStyle() style2 = self.file.createIfcFillAreaStyle()
ifcopenshell.api.run( ifcopenshell.api.run(
"style.assign_representation_styles", "style.assign_representation_styles",
self.file, self.file,
@@ -83,6 +91,21 @@ class TestAssignRepresentationStyles(test.bootstrap.IFC4):
assert item.StyledByItem[0].Styles[0].Styles == (style, style2) assert item.StyledByItem[0].Styles[0].Styles == (style, style2)
assert len(self.file.by_type("IfcStyledItem")) == 1 assert len(self.file.by_type("IfcStyledItem")) == 1
# replacing existing style of the same type
style3 = self.file.createIfcSurfaceStyle()
ifcopenshell.api.run(
"style.assign_representation_styles",
self.file,
styles=[style3],
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 == (style2, style3)
assert len(self.file.by_type("IfcStyledItem")) == 1
class TestAssignRepresentationStylesIFC2X3(test.bootstrap.IFC2X3): class TestAssignRepresentationStylesIFC2X3(test.bootstrap.IFC2X3):
def test_run(self): def test_run(self):