Skip to content

Commit 9de8661

Browse files
committed
Derive materials from selected objects in select_by_material
Previously only the explicit material prop was used. Now all selected objects' materials are collected, with layer set usages resolved to the specific layer index matching the clicked material. Results are joined with " + " in the clipboard query. Generated with the assistance of an AI coding tool.
1 parent 9ac1f61 commit 9de8661

1 file changed

Lines changed: 86 additions & 8 deletions

File tree

src/bonsai/bonsai/bim/module/material/operator.py

Lines changed: 86 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -71,20 +71,98 @@ def invoke(self, context, event):
7171
return self.execute(context)
7272

7373
def execute(self, context):
74-
material = tool.Ifc.get().by_id(self.material)
75-
core.select_by_material(tool.Material, tool.Spatial, material=material, should_unhide=self.should_unhide)
74+
# Determine the layer index hint from the explicit material prop, if any.
75+
# When the user clicks a specific layer in the UI, self.material is that
76+
# layer's IfcMaterial. We find its index so we can pull the same layer
77+
# from every other selected object's layer set.
78+
layer_index = None
79+
if self.material:
80+
ref_mat = tool.Ifc.get().by_id(self.material)
81+
layer_index = self._get_layer_index(ref_mat)
82+
83+
materials = {}
84+
for obj in context.selected_objects:
85+
element = tool.Ifc.get_entity(obj)
86+
if not element:
87+
continue
88+
mat = ifcopenshell.util.element.get_material(element)
89+
if not mat:
90+
continue
91+
92+
resolved = self._resolve_material(mat, layer_index)
93+
if resolved:
94+
materials[resolved.id()] = resolved
95+
96+
# Fall back to the explicit material prop if selection yields nothing
97+
if not materials and self.material:
98+
materials = {self.material: tool.Ifc.get().by_id(self.material)}
99+
100+
if not materials:
101+
return {"FINISHED"}
76102

77-
# copy selection query to clipboard
78-
if material.is_a("IfcMaterialLayerSet"):
79-
material_name = material.LayerSetName
80-
else:
81-
material_name = material.Name
82-
result = f'material="{material_name}"'
103+
for mat in materials.values():
104+
core.select_by_material(tool.Material, tool.Spatial, material=mat, should_unhide=self.should_unhide)
105+
106+
result = " + ".join(f'material = "{self._get_name(m)}"' for m in materials.values())
83107
bpy.context.window_manager.clipboard = result
84108
self.report({"INFO"}, f"({result}) was copied to the clipboard.")
85109

86110
return {"FINISHED"}
87111

112+
def _get_layer_index(self, material):
113+
"""Return the 0-based layer index if material is an IfcMaterial inside a layer set."""
114+
if not material.is_a("IfcMaterial"):
115+
return None
116+
ifc = tool.Ifc.get()
117+
for layer in ifc.get_inverse(material):
118+
if not layer.is_a("IfcMaterialLayer"):
119+
continue
120+
for layer_set in ifc.get_inverse(layer):
121+
if not layer_set.is_a("IfcMaterialLayerSet"):
122+
continue
123+
layers = list(layer_set.MaterialLayers)
124+
if layer in layers:
125+
return layers.index(layer)
126+
return None
127+
128+
def _resolve_material(self, mat, layer_index):
129+
"""Resolve an assigned material to the specific entity to select/name by.
130+
131+
When layer_index is set, drills into the layer set and returns the
132+
IfcMaterial at that index (or None if the set has fewer layers).
133+
Otherwise returns the layer set / profile set / constituent set itself.
134+
"""
135+
if mat.is_a("IfcMaterialLayerSetUsage"):
136+
mat = mat.ForLayerSet
137+
elif mat.is_a("IfcMaterialProfileSetUsage"):
138+
mat = mat.ForProfileSet
139+
140+
if layer_index is not None and mat.is_a("IfcMaterialLayerSet"):
141+
layers = list(mat.MaterialLayers)
142+
if layer_index < len(layers):
143+
return layers[layer_index].Material
144+
return None
145+
146+
return mat
147+
148+
def _get_name(self, material):
149+
if material.is_a("IfcMaterialLayerSet"):
150+
if material.LayerSetName:
151+
return material.LayerSetName
152+
names = [l.Material.Name for l in (material.MaterialLayers or []) if l.Material and l.Material.Name]
153+
return ", ".join(names) if names else material.is_a()
154+
if material.is_a("IfcMaterialProfileSet"):
155+
if material.Name:
156+
return material.Name
157+
names = [p.Material.Name for p in (material.MaterialProfiles or []) if p.Material and p.Material.Name]
158+
return ", ".join(names) if names else material.is_a()
159+
if material.is_a("IfcMaterialConstituentSet"):
160+
if material.Name:
161+
return material.Name
162+
names = [c.Material.Name for c in (material.MaterialConstituents or []) if c.Material and c.Material.Name]
163+
return ", ".join(names) if names else material.is_a()
164+
return getattr(material, "Name", None) or material.is_a()
165+
88166

89167
class EnableEditingMaterial(bpy.types.Operator):
90168
bl_idname = "bim.enable_editing_material"

0 commit comments

Comments
 (0)