mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-08-13 10:57:49 +00:00
You can now assign and unassign styles to materials
This commit is contained in:
@@ -40,10 +40,12 @@ classes = (
|
||||
operator.EditMaterial,
|
||||
operator.EditMaterialSetItem,
|
||||
operator.EditMaterialSetItemProfile,
|
||||
operator.EditMaterialStyle,
|
||||
operator.EnableEditingAssignedMaterial,
|
||||
operator.EnableEditingMaterial,
|
||||
operator.EnableEditingMaterialSetItem,
|
||||
operator.EnableEditingMaterialSetItemProfile,
|
||||
operator.EnableEditingMaterialStyle,
|
||||
operator.ExpandMaterialCategory,
|
||||
operator.LoadMaterials,
|
||||
operator.RemoveConstituent,
|
||||
@@ -55,6 +57,7 @@ classes = (
|
||||
operator.ReorderMaterialSetItem,
|
||||
operator.SelectByMaterial,
|
||||
operator.UnassignMaterial,
|
||||
operator.UnassignMaterialStyle,
|
||||
operator.UnlinkMaterial,
|
||||
prop.Material,
|
||||
prop.BIMMaterialProperties,
|
||||
|
||||
@@ -38,6 +38,9 @@ class MaterialsData:
|
||||
"total_materials": cls.total_materials(),
|
||||
"material_types": cls.material_types(),
|
||||
"profiles": cls.profiles(),
|
||||
"styles": cls.styles(),
|
||||
"contexts": cls.contexts(),
|
||||
"active_styles": cls.active_styles(),
|
||||
}
|
||||
cls.is_loaded = True
|
||||
|
||||
@@ -79,6 +82,58 @@ class MaterialsData:
|
||||
if p.ProfileName
|
||||
]
|
||||
|
||||
@classmethod
|
||||
def contexts(cls):
|
||||
results = []
|
||||
for element in tool.Ifc.get().by_type("IfcGeometricRepresentationContext", include_subtypes=False):
|
||||
results.append((str(element.id()), element.ContextType or "Unnamed", ""))
|
||||
for element in tool.Ifc.get().by_type("IfcGeometricRepresentationSubContext", include_subtypes=False):
|
||||
results.append(
|
||||
(
|
||||
str(element.id()),
|
||||
"{}/{}/{}".format(
|
||||
element.ContextType or "Unnamed",
|
||||
element.ContextIdentifier or "Unnamed",
|
||||
element.TargetView or "Unnamed",
|
||||
),
|
||||
"",
|
||||
)
|
||||
)
|
||||
return results
|
||||
|
||||
@classmethod
|
||||
def styles(cls):
|
||||
return [(str(s.id()), s.Name or "Unnamed", "") for s in tool.Ifc.get().by_type("IfcSurfaceStyle") if s.Name]
|
||||
|
||||
@classmethod
|
||||
def active_styles(cls):
|
||||
props = bpy.context.scene.BIMMaterialProperties
|
||||
results = []
|
||||
if props.materials and props.active_material_index < len(props.materials):
|
||||
material = props.materials[props.active_material_index].ifc_definition_id
|
||||
if not material:
|
||||
return results
|
||||
material = tool.Ifc.get().by_id(material)
|
||||
for definition in material.HasRepresentation:
|
||||
for representation in definition.Representations:
|
||||
if not representation.is_a("IfcStyledRepresentation"):
|
||||
continue
|
||||
context = representation.ContextOfItems
|
||||
for item in representation.Items:
|
||||
if not item.is_a("IfcStyledItem"):
|
||||
continue
|
||||
for style in item.Styles:
|
||||
if style.is_a("IfcSurfaceStyle"):
|
||||
results.append({
|
||||
"context_type": context.ContextType,
|
||||
"context_identifier": getattr(context, "ContextIdentifier", ""),
|
||||
"target_view": getattr(context, "TargetView", ""),
|
||||
"name": style.Name or "Unnamed",
|
||||
"id": style.id(),
|
||||
"context_id": context.id(),
|
||||
})
|
||||
return results
|
||||
|
||||
|
||||
class ObjectMaterialData:
|
||||
data = {}
|
||||
|
||||
@@ -715,3 +715,50 @@ class ContractMaterialCategory(bpy.types.Operator):
|
||||
category.is_expanded = False
|
||||
core.load_materials(tool.Material, props.material_type)
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
class EnableEditingMaterialStyle(bpy.types.Operator, tool.Ifc.Operator):
|
||||
bl_idname = "bim.enable_editing_material_style"
|
||||
bl_label = "Enable Editing Material Style"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
material: bpy.props.IntProperty()
|
||||
|
||||
def _execute(self, context):
|
||||
props = bpy.context.scene.BIMMaterialProperties
|
||||
props.active_material_id = self.material
|
||||
props.editing_material_type = "STYLE"
|
||||
|
||||
|
||||
class EditMaterialStyle(bpy.types.Operator, tool.Ifc.Operator):
|
||||
bl_idname = "bim.edit_material_style"
|
||||
bl_label = "Edit Material Style"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
|
||||
def _execute(self, context):
|
||||
props = bpy.context.scene.BIMMaterialProperties
|
||||
material = tool.Ifc.get().by_id(props.active_material_id)
|
||||
style = tool.Ifc.get().by_id(int(props.styles))
|
||||
context = tool.Ifc.get().by_id(int(props.contexts))
|
||||
ifcopenshell.api.run(
|
||||
"style.assign_material_style", tool.Ifc.get(), material=material, style=style, context=context
|
||||
)
|
||||
tool.Material.disable_editing_material()
|
||||
core.load_materials(tool.Material, props.material_type)
|
||||
|
||||
|
||||
class UnassignMaterialStyle(bpy.types.Operator, tool.Ifc.Operator):
|
||||
bl_idname = "bim.unassign_material_style"
|
||||
bl_label = "Unassign Material Style"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
style: bpy.props.IntProperty()
|
||||
context: bpy.props.IntProperty()
|
||||
|
||||
def _execute(self, context):
|
||||
props = bpy.context.scene.BIMMaterialProperties
|
||||
material = tool.Ifc.get().by_id(props.materials[props.active_material_index].ifc_definition_id)
|
||||
style = tool.Ifc.get().by_id(self.style)
|
||||
context = tool.Ifc.get().by_id(self.context)
|
||||
ifcopenshell.api.run(
|
||||
"style.unassign_material_style", tool.Ifc.get(), material=material, style=style, context=context
|
||||
)
|
||||
core.load_materials(tool.Material, props.material_type)
|
||||
|
||||
@@ -103,11 +103,24 @@ def get_profiles(self, context):
|
||||
return MaterialsData.data["profiles"]
|
||||
|
||||
|
||||
def get_styles(self, context):
|
||||
if not MaterialsData.is_loaded:
|
||||
MaterialsData.load()
|
||||
return MaterialsData.data["styles"]
|
||||
|
||||
|
||||
def get_contexts(self, context):
|
||||
if not MaterialsData.is_loaded:
|
||||
MaterialsData.load()
|
||||
return MaterialsData.data["contexts"]
|
||||
|
||||
|
||||
class Material(PropertyGroup):
|
||||
name: StringProperty(name="Name")
|
||||
ifc_definition_id: IntProperty(name="IFC Definition ID")
|
||||
is_category: BoolProperty(name="Is Category", default=False)
|
||||
is_expanded: BoolProperty(name="Is Expanded", default=True)
|
||||
has_style: BoolProperty(name="Has Style", default=True)
|
||||
total_elements: IntProperty(name="Total Elements")
|
||||
|
||||
|
||||
@@ -119,7 +132,9 @@ class BIMMaterialProperties(PropertyGroup):
|
||||
profiles: EnumProperty(items=get_profiles, name="Profiles")
|
||||
active_material_id: IntProperty(name="Active Material ID")
|
||||
material_attributes: CollectionProperty(name="Material Attributes", type=Attribute)
|
||||
editing_material_type = StringProperty(name="Editing Material Type")
|
||||
editing_material_type: StringProperty(name="Editing Material Type")
|
||||
styles: EnumProperty(items=get_styles, name="Styles")
|
||||
contexts: EnumProperty(items=get_contexts, name="Contexts")
|
||||
|
||||
|
||||
class BIMObjectMaterialProperties(PropertyGroup):
|
||||
|
||||
@@ -57,26 +57,19 @@ class BIM_PT_materials(Panel):
|
||||
row.alignment = "RIGHT"
|
||||
|
||||
if self.props.material_type == "IfcMaterial":
|
||||
if not self.props.active_material_id:
|
||||
row.operator("bim.add_material", text="", icon="ADD")
|
||||
row.operator("bim.add_material", text="", icon="ADD")
|
||||
if self.props.materials and self.props.active_material_index < len(self.props.materials):
|
||||
material = self.props.materials[self.props.active_material_index]
|
||||
if material.ifc_definition_id:
|
||||
if self.props.active_material_id:
|
||||
row.operator(
|
||||
"bim.edit_material", text="", icon="CHECKMARK"
|
||||
).material = material.ifc_definition_id
|
||||
row.operator(
|
||||
"bim.disable_editing_material", text="", icon="CANCEL"
|
||||
).material = material.ifc_definition_id
|
||||
self.draw_editable_material_attributes_ui()
|
||||
else:
|
||||
op = row.operator("bim.select_by_material", text="", icon="RESTRICT_SELECT_OFF")
|
||||
op.material = material.ifc_definition_id
|
||||
row.operator(
|
||||
"bim.enable_editing_material", text="", icon="GREASEPENCIL"
|
||||
).material = material.ifc_definition_id
|
||||
row.operator("bim.remove_material", text="", icon="X").material = material.ifc_definition_id
|
||||
op = row.operator("bim.select_by_material", text="", icon="RESTRICT_SELECT_OFF")
|
||||
op.material = material.ifc_definition_id
|
||||
op = row.operator("bim.enable_editing_material", text="", icon="GREASEPENCIL")
|
||||
op.material = material.ifc_definition_id
|
||||
op = row.operator("bim.enable_editing_material_style", text="", icon="SHADING_RENDERED")
|
||||
op.material = material.ifc_definition_id
|
||||
row.operator("bim.remove_material", text="", icon="X").material = material.ifc_definition_id
|
||||
|
||||
self.draw_editing_ui()
|
||||
else:
|
||||
row.operator("bim.add_material_set", text="", icon="ADD").set_type = self.props.material_type
|
||||
if self.props.materials and self.props.active_material_index < len(self.props.materials):
|
||||
@@ -88,8 +81,33 @@ class BIM_PT_materials(Panel):
|
||||
|
||||
self.layout.template_list("BIM_UL_materials", "", self.props, "materials", self.props, "active_material_index")
|
||||
|
||||
def draw_editable_material_attributes_ui(self):
|
||||
blenderbim.bim.helper.draw_attributes(self.props.material_attributes, self.layout)
|
||||
for style in MaterialsData.data["active_styles"]:
|
||||
row = self.layout.row(align=True)
|
||||
row.label(text="", icon="SHADING_RENDERED")
|
||||
row.label(text=style["context_type"])
|
||||
row.label(text=style["context_identifier"])
|
||||
row.label(text=style["target_view"])
|
||||
row.label(text=style["name"])
|
||||
op = row.operator("bim.unassign_material_style", text="", icon="X")
|
||||
op.style = style["id"]
|
||||
op.context = style["context_id"]
|
||||
|
||||
def draw_editing_ui(self):
|
||||
if not self.props.active_material_id:
|
||||
return
|
||||
ifc_definition_id = self.props.active_material_id
|
||||
if self.props.editing_material_type == "ATTRIBUTES":
|
||||
blenderbim.bim.helper.draw_attributes(self.props.material_attributes, self.layout)
|
||||
row = self.layout.row(align=True)
|
||||
row.operator("bim.edit_material", text="Save Material", icon="CHECKMARK").material = ifc_definition_id
|
||||
row.operator("bim.disable_editing_material", text="", icon="CANCEL")
|
||||
elif self.props.editing_material_type == "STYLE":
|
||||
row = self.layout.row(align=True)
|
||||
row.prop(self.props, "contexts", text="")
|
||||
row.prop(self.props, "styles", text="")
|
||||
row = self.layout.row(align=True)
|
||||
row.operator("bim.edit_material_style", text="Assign Style", icon="CHECKMARK")
|
||||
row.operator("bim.disable_editing_material", text="", icon="CANCEL")
|
||||
|
||||
|
||||
class BIM_PT_material(Panel):
|
||||
@@ -343,3 +361,6 @@ class BIM_UL_materials(UIList):
|
||||
row2 = row.row()
|
||||
row2.alignment = "RIGHT"
|
||||
row2.label(text=str(item.total_elements))
|
||||
|
||||
if item.has_style:
|
||||
row2.label(text="", icon="SHADING_RENDERED")
|
||||
|
||||
@@ -80,6 +80,7 @@ class Material(blenderbim.core.tool.Material):
|
||||
new.total_elements = len(
|
||||
ifcopenshell.util.element.get_elements_by_material(tool.Ifc.get(), material)
|
||||
)
|
||||
new.has_style = bool(material.HasRepresentation)
|
||||
return
|
||||
for material in materials:
|
||||
new = props.materials.add()
|
||||
|
||||
@@ -0,0 +1,54 @@
|
||||
# IfcOpenShell - IFC toolkit and geometry engine
|
||||
# Copyright (C) 2023 Dion Moult <dion@thinkmoult.com>
|
||||
#
|
||||
# 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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
|
||||
class Usecase:
|
||||
def __init__(self, file, representation=None):
|
||||
"""Removes a styled representation
|
||||
|
||||
Styled representations are typically associated with materials. This
|
||||
removes the representation but not the underlying styles.
|
||||
|
||||
:param representation: The IfcStyledRepresentation to remove.
|
||||
:type representation: ifcopenshell.entity_instance.entity_instance
|
||||
:return: None
|
||||
:rtype: None
|
||||
|
||||
Example:
|
||||
|
||||
.. code:: python
|
||||
|
||||
# Remove a styled representation
|
||||
ifcopenshell.api.run("style.remove_styled_representation", model, representation=representation)
|
||||
"""
|
||||
self.file = file
|
||||
self.settings = {"representation": representation}
|
||||
|
||||
def execute(self):
|
||||
for inverse in self.file.get_inverse(self.settings["representation"]):
|
||||
if inverse.is_a("IfcMaterialDefinitionRepresentation") and len(inverse.Representations) == 1:
|
||||
self.file.remove(inverse)
|
||||
|
||||
for item in self.settings["representation"].Items:
|
||||
if item.is_a("IfcStyledItem") and self.file.get_total_inverses(item) == 1:
|
||||
for style in item.Styles:
|
||||
if style.is_a("IfcPresentationStyleAssignment"):
|
||||
self.file.remove(style)
|
||||
self.file.remove(item)
|
||||
|
||||
self.file.remove(self.settings["representation"])
|
||||
@@ -0,0 +1,70 @@
|
||||
# IfcOpenShell - IFC toolkit and geometry engine
|
||||
# Copyright (C) 2023 Dion Moult <dion@thinkmoult.com>
|
||||
#
|
||||
# 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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
|
||||
class Usecase:
|
||||
def __init__(self, file, material=None, style=None, context=None):
|
||||
"""Unassigns a style to a material
|
||||
|
||||
This does the inverse of assign_material_style.
|
||||
|
||||
:param material: The IfcMaterial which you want to unassign the style from.
|
||||
:type material: ifcopenshell.entity_instance.entity_instance
|
||||
:param style: The IfcPresentationStyle (typically IfcSurfaceStyle) that
|
||||
you want to unassign from material. This will then be applied to all
|
||||
objects that have that material.
|
||||
:type style: ifcopenshell.entity_instance.entity_instance
|
||||
:param context: The IfcGeometricRepresentationSubContext at which this
|
||||
style should be unassigned. Typically this is the Model BODY context.
|
||||
:type context: ifcopenshell.entity_instance.entity_instance
|
||||
:return: None
|
||||
:rtype: None
|
||||
|
||||
Example:
|
||||
|
||||
.. code:: python
|
||||
|
||||
ifcopenshell.api.run("style.unassign_material_style", model, material=concrete, style=style, context=body)
|
||||
"""
|
||||
self.file = file
|
||||
self.settings = {
|
||||
"material": material,
|
||||
"style": style,
|
||||
"context": context,
|
||||
}
|
||||
|
||||
def execute(self):
|
||||
to_delete = set()
|
||||
for definition in self.settings["material"].HasRepresentation:
|
||||
for representation in definition.Representations:
|
||||
if not representation.is_a("IfcStyledRepresentation"):
|
||||
continue
|
||||
if representation.ContextOfItems != self.settings["context"]:
|
||||
continue
|
||||
for item in representation.Items:
|
||||
if not item.is_a("IfcStyledItem"):
|
||||
continue
|
||||
styles = [s for s in item.Styles if s != self.settings["style"]]
|
||||
if not styles:
|
||||
self.file.remove(item)
|
||||
elif len(styles) != len(item.Styles):
|
||||
item.Styles = styles
|
||||
if not representation.Items:
|
||||
self.file.remove(representation)
|
||||
if not definition.Representations:
|
||||
self.file.remove(definition)
|
||||
Reference in New Issue
Block a user