#2111 New feature to select by style and see style usage

This commit is contained in:
Dion Moult
2022-05-08 18:03:18 +10:00
parent 910d189f35
commit 93fd3f97e3
12 changed files with 242 additions and 51 deletions
@@ -92,8 +92,10 @@ class ObjectMaterialData:
element_type = ifcopenshell.util.element.get_type(element)
if element_type != element:
material = ifcopenshell.util.element.get_material(element_type)
if not material:
return
if material.is_a() in ("IfcMaterialLayerSetUsage", "IfcMaterialLayerSet"):
name_attr = "LayerSetName"
else:
name_attr = "Name"
return getattr(material, name_attr, "Unnamed")
return getattr(material, name_attr, "Unnamed") or "Unnamed"
@@ -27,6 +27,7 @@ classes = (
operator.EnableEditingStyle,
operator.LoadStyles,
operator.RemoveStyle,
operator.SelectByStyle,
operator.UnlinkStyle,
operator.UpdateStyleColours,
operator.UpdateStyleTextures,
@@ -24,14 +24,7 @@ import ifcopenshell.util.representation
from blenderbim.bim.ifc import IfcStore
class Operator:
def execute(self, context):
IfcStore.execute_ifc_operator(self, context)
blenderbim.bim.handler.refresh_ui_data()
return {"FINISHED"}
class UpdateStyleColours(bpy.types.Operator, Operator):
class UpdateStyleColours(bpy.types.Operator, tool.Ifc.Operator):
bl_idname = "bim.update_style_colours"
bl_label = "Update Style Colours"
bl_options = {"REGISTER", "UNDO"}
@@ -40,7 +33,7 @@ class UpdateStyleColours(bpy.types.Operator, Operator):
core.update_style_colours(tool.Ifc, tool.Style, obj=context.active_object.active_material)
class UpdateStyleTextures(bpy.types.Operator, Operator):
class UpdateStyleTextures(bpy.types.Operator, tool.Ifc.Operator):
bl_idname = "bim.update_style_textures"
bl_label = "Update Style Textures"
bl_options = {"REGISTER", "UNDO"}
@@ -55,7 +48,7 @@ class UpdateStyleTextures(bpy.types.Operator, Operator):
)
class RemoveStyle(bpy.types.Operator, Operator):
class RemoveStyle(bpy.types.Operator, tool.Ifc.Operator):
bl_idname = "bim.remove_style"
bl_label = "Remove Style"
bl_options = {"REGISTER", "UNDO"}
@@ -65,7 +58,7 @@ class RemoveStyle(bpy.types.Operator, Operator):
core.remove_style(tool.Ifc, tool.Material, tool.Style, style=tool.Ifc.get().by_id(self.style))
class AddStyle(bpy.types.Operator, Operator):
class AddStyle(bpy.types.Operator, tool.Ifc.Operator):
bl_idname = "bim.add_style"
bl_label = "Add Style"
bl_options = {"REGISTER", "UNDO"}
@@ -74,7 +67,7 @@ class AddStyle(bpy.types.Operator, Operator):
core.add_style(tool.Ifc, tool.Style, obj=context.active_object.active_material)
class UnlinkStyle(bpy.types.Operator, Operator):
class UnlinkStyle(bpy.types.Operator, tool.Ifc.Operator):
bl_idname = "bim.unlink_style"
bl_label = "Unlink Style"
bl_options = {"REGISTER", "UNDO"}
@@ -83,7 +76,7 @@ class UnlinkStyle(bpy.types.Operator, Operator):
core.unlink_style(tool.Ifc, tool.Style, obj=context.active_object.active_material)
class EnableEditingStyle(bpy.types.Operator, Operator):
class EnableEditingStyle(bpy.types.Operator, tool.Ifc.Operator):
bl_idname = "bim.enable_editing_style"
bl_label = "Enable Editing Style"
bl_options = {"REGISTER", "UNDO"}
@@ -92,7 +85,7 @@ class EnableEditingStyle(bpy.types.Operator, Operator):
core.enable_editing_style(tool.Style, obj=context.active_object.active_material)
class DisableEditingStyle(bpy.types.Operator, Operator):
class DisableEditingStyle(bpy.types.Operator, tool.Ifc.Operator):
bl_idname = "bim.disable_editing_style"
bl_options = {"REGISTER", "UNDO"}
bl_label = "Disable Editing Style"
@@ -101,7 +94,7 @@ class DisableEditingStyle(bpy.types.Operator, Operator):
core.disable_editing_style(tool.Style, obj=context.active_object.active_material)
class EditStyle(bpy.types.Operator, Operator):
class EditStyle(bpy.types.Operator, tool.Ifc.Operator):
bl_idname = "bim.edit_style"
bl_label = "Edit Style"
bl_options = {"REGISTER", "UNDO"}
@@ -110,7 +103,7 @@ class EditStyle(bpy.types.Operator, Operator):
core.edit_style(tool.Ifc, tool.Style, obj=context.active_object.active_material)
class DisableEditingStyles(bpy.types.Operator, Operator):
class DisableEditingStyles(bpy.types.Operator, tool.Ifc.Operator):
bl_idname = "bim.disable_editing_styles"
bl_options = {"REGISTER", "UNDO"}
bl_label = "Disable Editing Styles"
@@ -119,7 +112,7 @@ class DisableEditingStyles(bpy.types.Operator, Operator):
core.disable_editing_styles(tool.Style)
class LoadStyles(bpy.types.Operator, Operator):
class LoadStyles(bpy.types.Operator, tool.Ifc.Operator):
bl_idname = "bim.load_styles"
bl_label = "Load Styles"
bl_options = {"REGISTER", "UNDO"}
@@ -127,3 +120,13 @@ class LoadStyles(bpy.types.Operator, Operator):
def _execute(self, context):
core.load_styles(tool.Style, style_type=self.style_type)
class SelectByStyle(bpy.types.Operator, tool.Ifc.Operator):
bl_idname = "bim.select_by_style"
bl_label = "Select By Material"
bl_options = {"REGISTER", "UNDO"}
style: bpy.props.IntProperty()
def _execute(self, context):
core.select_by_style(tool.Style, style=tool.Ifc.get().by_id(self.style))
@@ -57,8 +57,8 @@ class BIM_PT_styles(Panel):
#row.operator("bim.add_presentation_style", text="", icon="ADD")
if self.props.styles and self.props.active_style_index < len(self.props.styles):
style = self.props.styles[self.props.active_style_index]
# op = row.operator("bim.select_by_style", text="", icon="RESTRICT_SELECT_OFF")
# op.style = style.ifc_definition_id
op = row.operator("bim.select_by_style", text="", icon="RESTRICT_SELECT_OFF")
op.style = style.ifc_definition_id
row.operator("bim.remove_style", text="", icon="X").style = style.ifc_definition_id
self.layout.template_list("BIM_UL_styles", "", self.props, "styles", self.props, "active_style_index")
@@ -138,6 +138,6 @@ class BIM_UL_styles(UIList):
if item:
row = layout.row(align=True)
row.label(text=item.name)
#row2 = row.row()
#row2.alignment = "RIGHT"
#row2.label(text=str(item.total_elements))
row2 = row.row()
row2.alignment = "RIGHT"
row2.label(text=str(item.total_elements))
+4
View File
@@ -110,3 +110,7 @@ def load_styles(style, style_type=None):
def disable_editing_styles(style):
style.disable_editing_styles()
def select_by_style(style_tool, style=None):
style_tool.select_elements(style_tool.get_elements_by_style(style))
+2
View File
@@ -443,6 +443,7 @@ class Style:
def export_surface_attributes(cls, obj): pass
def get_active_style_type(cls): pass
def get_context(cls, obj): pass
def get_elements_by_style(cls, style): pass
def get_name(cls, obj): pass
def get_style(cls, obj): pass
def get_surface_rendering_attributes(cls, obj): pass
@@ -454,6 +455,7 @@ class Style:
def import_presentation_styles(cls, style_type): pass
def import_surface_attributes(cls, style, obj): pass
def is_editing_styles(cls): pass
def select_elements(cls, elements): pass
@interface
+8 -8
View File
@@ -46,7 +46,7 @@ class Material(blenderbim.core.tool.Material):
@classmethod
def get_elements_by_material(cls, material):
return set(ifcopenshell.util.element.get_elements_by_material(tool.Ifc.get(), material))
return ifcopenshell.util.element.get_elements_by_material(tool.Ifc.get(), material)
@classmethod
def get_name(cls, obj):
@@ -75,13 +75,13 @@ class Material(blenderbim.core.tool.Material):
new = props.materials.add()
new.ifc_definition_id = material.id()
new.name = get_name(material)
new.total_elements = len(set(ifcopenshell.util.element.get_elements_by_material(tool.Ifc.get(), material)))
else:
for material in materials:
new = props.materials.add()
new.ifc_definition_id = material.id()
new.name = get_name(material)
new.total_elements = len(set(ifcopenshell.util.element.get_elements_by_material(tool.Ifc.get(), material)))
new.total_elements = len(ifcopenshell.util.element.get_elements_by_material(tool.Ifc.get(), material))
return
for material in materials:
new = props.materials.add()
new.ifc_definition_id = material.id()
new.name = get_name(material)
new.total_elements = len(ifcopenshell.util.element.get_elements_by_material(tool.Ifc.get(), material))
@classmethod
def is_editing_materials(cls):
+12
View File
@@ -56,6 +56,10 @@ class Style(blenderbim.core.tool.Style):
def get_context(cls, obj):
return ifcopenshell.util.representation.get_context(tool.Ifc.get(), "Model", "Body", "MODEL_VIEW")
@classmethod
def get_elements_by_style(cls, style):
return ifcopenshell.util.element.get_elements_by_style(tool.Ifc.get(), style)
@classmethod
def get_name(cls, obj):
return obj.name
@@ -188,6 +192,7 @@ class Style(blenderbim.core.tool.Style):
new = props.styles.add()
new.ifc_definition_id = style.id()
new.name = style.Name or "Unnamed"
new.total_elements = len(ifcopenshell.util.element.get_elements_by_style(tool.Ifc.get(), style))
@classmethod
def import_surface_attributes(cls, style, obj):
@@ -197,3 +202,10 @@ class Style(blenderbim.core.tool.Style):
@classmethod
def is_editing_styles(cls):
return bpy.context.scene.BIMStylesProperties.is_editing
@classmethod
def select_elements(cls, elements):
for element in elements:
obj = tool.Ifc.get_object(element)
if obj:
obj.select_set(True)
@@ -75,3 +75,13 @@ Scenario: Disable editing styles
And I press "bim.load_styles(style_type='IfcSurfaceStyle')"
When I press "bim.disable_editing_style"
Then nothing happens
Scenario: Select by style
Given an empty IFC project
And I add a cube
And I add a material
And I press "bim.add_style"
And I press "bim.load_styles(style_type='IfcSurfaceStyle')"
And the variable "style" is "{ifc}.by_type('IfcSurfaceStyle')[0].id()"
When I press "bim.select_by_style(style={style})"
Then nothing happens
+26
View File
@@ -95,6 +95,20 @@ class TestGetContext(NewFile):
assert subject.get_context("obj") == context
class TestGetElementsByStyle(NewFile):
def test_run(self):
ifc = ifcopenshell.file()
tool.Ifc.set(ifc)
element = ifcopenshell.api.run("root.create_entity", ifc, ifc_class="IfcWall")
style = ifc.createIfcSurfaceStyle()
item = ifc.createIfcExtrudedAreaSolid()
ifc.createIfcStyledItem(Item=item, Styles=[style])
element.Representation = ifc.createIfcProductDefinitionShape(
Representations=[ifc.createIfcShapeRepresentation(Items=[item])]
)
assert subject.get_elements_by_style(style) == {element}
class TestGetName(NewFile):
def test_run(self):
assert subject.get_name(bpy.data.materials.new("Material")) == "Material"
@@ -426,3 +440,15 @@ class TestIsEditingStyles(NewFile):
subject.is_editing_styles() is False
bpy.context.scene.BIMStylesProperties.is_editing = True
subject.is_editing_styles() is True
class TestSelectElements(NewFile):
def test_run(self):
ifc = ifcopenshell.file()
tool.Ifc().set(ifc)
element = ifcopenshell.api.run("root.create_entity", ifc, ifc_class="IfcPump")
obj = bpy.data.objects.new("Object", None)
bpy.context.scene.collection.objects.link(obj)
tool.Ifc.link(element, obj)
subject.select_elements([element])
assert obj in bpy.context.selected_objects
@@ -134,25 +134,65 @@ def get_material(element, should_skip_usage=False):
def get_elements_by_material(ifc_file, material):
results = []
results = set()
for inverse in ifc_file.get_inverse(material):
if inverse.is_a("IfcRelAssociatesMaterial"):
results.extend(inverse.RelatedObjects)
results.update(inverse.RelatedObjects)
elif inverse.is_a("IfcMaterialLayer"):
for material_set in inverse.ToMaterialLayerSet:
results.extend(get_elements_by_material(ifc_file, material_set))
results.update(get_elements_by_material(ifc_file, material_set))
elif inverse.is_a("IfcMaterialProfile"):
for material_set in inverse.ToMaterialProfileSet:
results.extend(get_elements_by_material(ifc_file, material_set))
results.update(get_elements_by_material(ifc_file, material_set))
elif inverse.is_a("IfcMaterialConstituent"):
for material_set in inverse.ToMaterialConstituentSet:
results.extend(get_elements_by_material(ifc_file, material_set))
results.update(get_elements_by_material(ifc_file, material_set))
elif inverse.is_a("IfcMaterialLayerSetUsage"):
results.extend(get_elements_by_material(ifc_file, inverse))
results.update(get_elements_by_material(ifc_file, inverse))
elif inverse.is_a("IfcMaterialProfileSetUsage"):
results.extend(get_elements_by_material(ifc_file, inverse))
results.update(get_elements_by_material(ifc_file, inverse))
elif inverse.is_a("IfcMaterialList"):
results.extend(get_elements_by_material(ifc_file, inverse))
results.update(get_elements_by_material(ifc_file, inverse))
return results
def get_elements_by_style(ifc_file, style):
results = set()
inverses = list(ifc_file.get_inverse(style))
while inverses:
inverse = inverses.pop()
if inverse.is_a("IfcPresentationStyleAssignment"):
inverses.extend(ifc_file.get_inverse(inverse))
continue
if not inverse.is_a("IfcStyledItem"):
continue
if inverse.Item:
[
results.update(get_elements_by_representation(ifc_file, i))
for i in ifc_file.get_inverse(inverse.Item)
if i.is_a("IfcShapeRepresentation")
]
else:
styled_reps = [i for i in ifc_file.get_inverse(inverse) if i.is_a("IfcStyledRepresentation")]
for styled_rep in styled_reps:
for material_def_rep in styled_rep.OfProductRepresentation:
results.update(get_elements_by_material(ifc_file, material_def_rep.RepresentedMaterial))
return results
def get_elements_by_representation(ifc_file, representation):
results = set()
[results.update(pr.ShapeOfProduct) for pr in representation.OfProductRepresentation]
for rep_map in representation.RepresentationMap:
for inverse in ifc_file.get_inverse(rep_map):
if inverse.is_a("IfcTypeProduct"):
results.add(inverse)
elif inverse.is_a("IfcMappedItem"):
[
results.update(get_elements_by_representation(ifc_file, rep))
for rep in ifc_file.get_inverse(inverse)
if rep.is_a("IfcShapeRepresentation")
]
return results
+103 -12
View File
@@ -278,7 +278,7 @@ class TestGetElementsByMaterial(test.bootstrap.IFC4):
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_elements_by_material(self.file, material) == [element]
assert subject.get_elements_by_material(self.file, material) == {element}
def test_getting_elements_of_a_material_layer_set(self):
element = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcWall")
@@ -290,9 +290,9 @@ class TestGetElementsByMaterial(test.bootstrap.IFC4):
ifcopenshell.api.run("material.assign_material", self.file, product=element_type, material=material_set)
ifcopenshell.api.run("material.assign_material", self.file, product=element, type="IfcMaterialLayerSetUsage")
usage = self.file.by_type("IfcMaterialLayerSetUsage")[0]
assert set(subject.get_elements_by_material(self.file, material)) == {element, element_type}
assert set(subject.get_elements_by_material(self.file, material_set)) == {element, element_type}
assert set(subject.get_elements_by_material(self.file, usage)) == {element}
assert subject.get_elements_by_material(self.file, material) == {element, element_type}
assert subject.get_elements_by_material(self.file, material_set) == {element, element_type}
assert subject.get_elements_by_material(self.file, usage) == {element}
def test_getting_elements_of_a_material_profile_set(self):
element = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcWall")
@@ -304,18 +304,20 @@ class TestGetElementsByMaterial(test.bootstrap.IFC4):
ifcopenshell.api.run("material.assign_material", self.file, product=element_type, material=material_set)
ifcopenshell.api.run("material.assign_material", self.file, product=element, type="IfcMaterialProfileSetUsage")
usage = self.file.by_type("IfcMaterialProfileSetUsage")[0]
assert set(subject.get_elements_by_material(self.file, material)) == {element, element_type}
assert set(subject.get_elements_by_material(self.file, material_set)) == {element, element_type}
assert set(subject.get_elements_by_material(self.file, usage)) == {element}
assert subject.get_elements_by_material(self.file, material) == {element, element_type}
assert subject.get_elements_by_material(self.file, material_set) == {element, element_type}
assert subject.get_elements_by_material(self.file, usage) == {element}
def test_getting_elements_of_a_material_constituent_set(self):
element = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcWall")
material = ifcopenshell.api.run("material.add_material", self.file)
material_set = ifcopenshell.api.run("material.add_material_set", self.file, set_type="IfcMaterialConstituentSet")
material_set = ifcopenshell.api.run(
"material.add_material_set", self.file, set_type="IfcMaterialConstituentSet"
)
ifcopenshell.api.run("material.add_constituent", self.file, constituent_set=material_set, material=material)
ifcopenshell.api.run("material.assign_material", self.file, product=element, material=material_set)
assert set(subject.get_elements_by_material(self.file, material)) == {element}
assert set(subject.get_elements_by_material(self.file, material_set)) == {element}
assert subject.get_elements_by_material(self.file, material) == {element}
assert subject.get_elements_by_material(self.file, material_set) == {element}
def test_getting_elements_of_a_material_list(self):
element = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcWall")
@@ -323,8 +325,97 @@ class TestGetElementsByMaterial(test.bootstrap.IFC4):
material_set = ifcopenshell.api.run("material.add_material_set", self.file, set_type="IfcMaterialList")
ifcopenshell.api.run("material.add_list_item", self.file, material_list=material_set, material=material)
ifcopenshell.api.run("material.assign_material", self.file, product=element, material=material_set)
assert set(subject.get_elements_by_material(self.file, material)) == {element}
assert set(subject.get_elements_by_material(self.file, material_set)) == {element}
assert subject.get_elements_by_material(self.file, material) == {element}
assert subject.get_elements_by_material(self.file, material_set) == {element}
class TestGetElementsByStyle(test.bootstrap.IFC4):
def test_getting_elements_of_a_styled_representation_item(self):
element = self.file.createIfcWall()
style = self.file.createIfcSurfaceStyle()
item = self.file.createIfcExtrudedAreaSolid()
self.file.createIfcStyledItem(Item=item, Styles=[style])
element.Representation = self.file.createIfcProductDefinitionShape(
Representations=[self.file.createIfcShapeRepresentation(Items=[item])]
)
assert subject.get_elements_by_style(self.file, style) == {element}
def test_getting_elements_of_a_styled_mapped_representation_item(self):
element = self.file.createIfcWall()
style = self.file.createIfcSurfaceStyle()
item = self.file.createIfcExtrudedAreaSolid()
self.file.createIfcStyledItem(Item=item, Styles=[style])
element.Representation = self.file.createIfcProductDefinitionShape(
Representations=[
self.file.createIfcShapeRepresentation(
Items=[
self.file.createIfcMappedItem(
MappingSource=self.file.createIfcRepresentationMap(
MappedRepresentation=self.file.createIfcShapeRepresentation(Items=[item])
)
)
]
)
]
)
assert subject.get_elements_by_style(self.file, style) == {element}
def test_getting_type_elements_of_a_styled_mapped_representation_item(self):
element = self.file.createIfcWallType()
style = self.file.createIfcSurfaceStyle()
item = self.file.createIfcExtrudedAreaSolid()
self.file.createIfcStyledItem(Item=item, Styles=[style])
element.RepresentationMaps = [
self.file.createIfcRepresentationMap(
MappedRepresentation=self.file.createIfcShapeRepresentation(Items=[item])
)
]
assert subject.get_elements_by_style(self.file, style) == {element}
def test_getting_elements_of_a_styled_material(self):
element = self.file.createIfcWall()
material = ifcopenshell.api.run("material.add_material", self.file)
ifcopenshell.api.run("material.assign_material", self.file, product=element, material=material)
style = self.file.createIfcSurfaceStyle()
self.file.createIfcMaterialDefinitionRepresentation(
RepresentedMaterial=material,
Representations=[
self.file.createIfcStyledRepresentation(Items=[self.file.createIfcStyledItem(Styles=[style])])
],
)
assert subject.get_elements_by_style(self.file, style) == {element}
class TestGetElementsByRepresentation(test.bootstrap.IFC4):
def test_getting_elements_of_a_styled_representation_item(self):
element = self.file.createIfcWall()
representation = self.file.createIfcShapeRepresentation()
element.Representation = self.file.createIfcProductDefinitionShape(Representations=[representation])
assert subject.get_elements_by_representation(self.file, representation) == {element}
def test_getting_elements_of_a_styled_mapped_representation_item(self):
element = self.file.createIfcWall()
representation = self.file.createIfcShapeRepresentation()
element.Representation = self.file.createIfcProductDefinitionShape(
Representations=[
self.file.createIfcShapeRepresentation(
Items=[
self.file.createIfcMappedItem(
MappingSource=self.file.createIfcRepresentationMap(
MappedRepresentation=representation
)
)
]
)
]
)
assert subject.get_elements_by_representation(self.file, representation) == {element}
def test_getting_type_elements_of_a_styled_mapped_representation_item(self):
element = self.file.createIfcWallType()
representation = self.file.createIfcShapeRepresentation()
element.RepresentationMaps = [self.file.createIfcRepresentationMap(MappedRepresentation=representation)]
assert subject.get_elements_by_representation(self.file, representation) == {element}
class TestGetlayers(test.bootstrap.IFC4):