mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-09-25 09:32:37 +00:00
You can now assign and unassign styles to materials
This commit is contained in:
@@ -40,10 +40,12 @@ classes = (
|
|||||||
operator.EditMaterial,
|
operator.EditMaterial,
|
||||||
operator.EditMaterialSetItem,
|
operator.EditMaterialSetItem,
|
||||||
operator.EditMaterialSetItemProfile,
|
operator.EditMaterialSetItemProfile,
|
||||||
|
operator.EditMaterialStyle,
|
||||||
operator.EnableEditingAssignedMaterial,
|
operator.EnableEditingAssignedMaterial,
|
||||||
operator.EnableEditingMaterial,
|
operator.EnableEditingMaterial,
|
||||||
operator.EnableEditingMaterialSetItem,
|
operator.EnableEditingMaterialSetItem,
|
||||||
operator.EnableEditingMaterialSetItemProfile,
|
operator.EnableEditingMaterialSetItemProfile,
|
||||||
|
operator.EnableEditingMaterialStyle,
|
||||||
operator.ExpandMaterialCategory,
|
operator.ExpandMaterialCategory,
|
||||||
operator.LoadMaterials,
|
operator.LoadMaterials,
|
||||||
operator.RemoveConstituent,
|
operator.RemoveConstituent,
|
||||||
@@ -55,6 +57,7 @@ classes = (
|
|||||||
operator.ReorderMaterialSetItem,
|
operator.ReorderMaterialSetItem,
|
||||||
operator.SelectByMaterial,
|
operator.SelectByMaterial,
|
||||||
operator.UnassignMaterial,
|
operator.UnassignMaterial,
|
||||||
|
operator.UnassignMaterialStyle,
|
||||||
operator.UnlinkMaterial,
|
operator.UnlinkMaterial,
|
||||||
prop.Material,
|
prop.Material,
|
||||||
prop.BIMMaterialProperties,
|
prop.BIMMaterialProperties,
|
||||||
|
|||||||
@@ -38,6 +38,9 @@ class MaterialsData:
|
|||||||
"total_materials": cls.total_materials(),
|
"total_materials": cls.total_materials(),
|
||||||
"material_types": cls.material_types(),
|
"material_types": cls.material_types(),
|
||||||
"profiles": cls.profiles(),
|
"profiles": cls.profiles(),
|
||||||
|
"styles": cls.styles(),
|
||||||
|
"contexts": cls.contexts(),
|
||||||
|
"active_styles": cls.active_styles(),
|
||||||
}
|
}
|
||||||
cls.is_loaded = True
|
cls.is_loaded = True
|
||||||
|
|
||||||
@@ -79,6 +82,58 @@ class MaterialsData:
|
|||||||
if p.ProfileName
|
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:
|
class ObjectMaterialData:
|
||||||
data = {}
|
data = {}
|
||||||
|
|||||||
@@ -715,3 +715,50 @@ class ContractMaterialCategory(bpy.types.Operator):
|
|||||||
category.is_expanded = False
|
category.is_expanded = False
|
||||||
core.load_materials(tool.Material, props.material_type)
|
core.load_materials(tool.Material, props.material_type)
|
||||||
return {"FINISHED"}
|
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"]
|
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):
|
class Material(PropertyGroup):
|
||||||
name: StringProperty(name="Name")
|
name: StringProperty(name="Name")
|
||||||
ifc_definition_id: IntProperty(name="IFC Definition ID")
|
ifc_definition_id: IntProperty(name="IFC Definition ID")
|
||||||
is_category: BoolProperty(name="Is Category", default=False)
|
is_category: BoolProperty(name="Is Category", default=False)
|
||||||
is_expanded: BoolProperty(name="Is Expanded", default=True)
|
is_expanded: BoolProperty(name="Is Expanded", default=True)
|
||||||
|
has_style: BoolProperty(name="Has Style", default=True)
|
||||||
total_elements: IntProperty(name="Total Elements")
|
total_elements: IntProperty(name="Total Elements")
|
||||||
|
|
||||||
|
|
||||||
@@ -119,7 +132,9 @@ class BIMMaterialProperties(PropertyGroup):
|
|||||||
profiles: EnumProperty(items=get_profiles, name="Profiles")
|
profiles: EnumProperty(items=get_profiles, name="Profiles")
|
||||||
active_material_id: IntProperty(name="Active Material ID")
|
active_material_id: IntProperty(name="Active Material ID")
|
||||||
material_attributes: CollectionProperty(name="Material Attributes", type=Attribute)
|
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):
|
class BIMObjectMaterialProperties(PropertyGroup):
|
||||||
|
|||||||
@@ -57,26 +57,19 @@ class BIM_PT_materials(Panel):
|
|||||||
row.alignment = "RIGHT"
|
row.alignment = "RIGHT"
|
||||||
|
|
||||||
if self.props.material_type == "IfcMaterial":
|
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):
|
if self.props.materials and self.props.active_material_index < len(self.props.materials):
|
||||||
material = self.props.materials[self.props.active_material_index]
|
material = self.props.materials[self.props.active_material_index]
|
||||||
if material.ifc_definition_id:
|
if material.ifc_definition_id:
|
||||||
if self.props.active_material_id:
|
op = row.operator("bim.select_by_material", text="", icon="RESTRICT_SELECT_OFF")
|
||||||
row.operator(
|
op.material = material.ifc_definition_id
|
||||||
"bim.edit_material", text="", icon="CHECKMARK"
|
op = row.operator("bim.enable_editing_material", text="", icon="GREASEPENCIL")
|
||||||
).material = material.ifc_definition_id
|
op.material = material.ifc_definition_id
|
||||||
row.operator(
|
op = row.operator("bim.enable_editing_material_style", text="", icon="SHADING_RENDERED")
|
||||||
"bim.disable_editing_material", text="", icon="CANCEL"
|
op.material = material.ifc_definition_id
|
||||||
).material = material.ifc_definition_id
|
row.operator("bim.remove_material", text="", icon="X").material = material.ifc_definition_id
|
||||||
self.draw_editable_material_attributes_ui()
|
|
||||||
else:
|
self.draw_editing_ui()
|
||||||
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
|
|
||||||
else:
|
else:
|
||||||
row.operator("bim.add_material_set", text="", icon="ADD").set_type = self.props.material_type
|
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):
|
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")
|
self.layout.template_list("BIM_UL_materials", "", self.props, "materials", self.props, "active_material_index")
|
||||||
|
|
||||||
def draw_editable_material_attributes_ui(self):
|
for style in MaterialsData.data["active_styles"]:
|
||||||
blenderbim.bim.helper.draw_attributes(self.props.material_attributes, self.layout)
|
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):
|
class BIM_PT_material(Panel):
|
||||||
@@ -343,3 +361,6 @@ class BIM_UL_materials(UIList):
|
|||||||
row2 = row.row()
|
row2 = row.row()
|
||||||
row2.alignment = "RIGHT"
|
row2.alignment = "RIGHT"
|
||||||
row2.label(text=str(item.total_elements))
|
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(
|
new.total_elements = len(
|
||||||
ifcopenshell.util.element.get_elements_by_material(tool.Ifc.get(), material)
|
ifcopenshell.util.element.get_elements_by_material(tool.Ifc.get(), material)
|
||||||
)
|
)
|
||||||
|
new.has_style = bool(material.HasRepresentation)
|
||||||
return
|
return
|
||||||
for material in materials:
|
for material in materials:
|
||||||
new = props.materials.add()
|
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