mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-08-17 10:59:17 +00:00
Material profile sets UI cleanup and now lets you choose from a profile in your library instead of creating new profiles on the fly all the time
This commit is contained in:
@@ -37,6 +37,7 @@ class MaterialsData:
|
||||
cls.data = {
|
||||
"total_materials": cls.total_materials(),
|
||||
"material_types": cls.material_types(),
|
||||
"profiles": cls.profiles(),
|
||||
}
|
||||
cls.is_loaded = True
|
||||
|
||||
@@ -72,6 +73,10 @@ class MaterialsData:
|
||||
(m, m, ifcopenshell.util.doc.get_entity_doc(version, m).get("description", "")) for m in material_types
|
||||
]
|
||||
|
||||
@classmethod
|
||||
def profiles(cls):
|
||||
return [(str(p.id()), p.ProfileName or "Unnamed", "") for p in tool.Ifc.get().by_type("IfcProfileDef")]
|
||||
|
||||
|
||||
class ObjectMaterialData:
|
||||
data = {}
|
||||
|
||||
@@ -28,7 +28,6 @@ import blenderbim.core.material as core
|
||||
from blenderbim.bim.module.material.prop import purge as material_prop_purge
|
||||
from blenderbim.bim.ifc import IfcStore
|
||||
from ifcopenshell.api.material.data import Data
|
||||
from ifcopenshell.api.profile.data import Data as ProfileData
|
||||
|
||||
|
||||
class LoadMaterials(bpy.types.Operator, tool.Ifc.Operator):
|
||||
@@ -84,7 +83,6 @@ class AssignParameterizedProfile(bpy.types.Operator):
|
||||
**{"material_profile": self.file.by_id(self.material_profile), "profile": profile},
|
||||
)
|
||||
Data.load_profiles()
|
||||
ProfileData.load(self.file)
|
||||
bpy.ops.bim.enable_editing_material_set_item(obj=obj.name, material_set_item=self.material_profile)
|
||||
return {"FINISHED"}
|
||||
|
||||
@@ -256,13 +254,11 @@ class AddProfile(bpy.types.Operator):
|
||||
ifcopenshell.api.run(
|
||||
"material.add_profile",
|
||||
self.file,
|
||||
**{
|
||||
"profile_set": self.file.by_id(self.profile_set),
|
||||
"material": self.file.by_id(int(obj.BIMObjectMaterialProperties.material)),
|
||||
},
|
||||
profile_set=self.file.by_id(self.profile_set),
|
||||
material=self.file.by_id(int(obj.BIMObjectMaterialProperties.material)),
|
||||
profile=self.file.by_id(int(context.scene.BIMMaterialProperties.profiles)),
|
||||
)
|
||||
Data.load_profiles()
|
||||
ProfileData.load(self.file)
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
@@ -281,7 +277,6 @@ class RemoveProfile(bpy.types.Operator):
|
||||
self.file = IfcStore.get_file()
|
||||
ifcopenshell.api.run("material.remove_profile", self.file, **{"profile": self.file.by_id(self.profile)})
|
||||
Data.load_profiles()
|
||||
ProfileData.load(self.file)
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
@@ -341,7 +336,6 @@ class ReorderMaterialSetItem(bpy.types.Operator):
|
||||
Data.load_layers()
|
||||
elif material_set.is_a("IfcMaterialProfileSet"):
|
||||
Data.load_profiles()
|
||||
ProfileData.load(self.file)
|
||||
elif material_set.is_a("IfcMaterialList"):
|
||||
Data.load_lists()
|
||||
return {"FINISHED"}
|
||||
@@ -586,6 +580,7 @@ class EnableEditingMaterialSetItem(bpy.types.Operator):
|
||||
def execute(self, context):
|
||||
self.file = IfcStore.get_file()
|
||||
obj = bpy.data.objects.get(self.obj) if self.obj else context.active_object
|
||||
self.mprops = context.scene.BIMMaterialProperties
|
||||
self.props = obj.BIMObjectMaterialProperties
|
||||
self.props.active_material_set_item_id = self.material_set_item
|
||||
product_data = Data.products[obj.BIMObjectProperties.ifc_definition_id]
|
||||
@@ -604,7 +599,8 @@ class EnableEditingMaterialSetItem(bpy.types.Operator):
|
||||
|
||||
self.load_set_item_attributes(material_set_item, material_set_item_data)
|
||||
if material_set_item.is_a("IfcMaterialProfile"):
|
||||
self.load_profile_attributes(material_set_item, material_set_item_data)
|
||||
if material_set_item.Profile:
|
||||
self.mprops.profiles = str(material_set_item.Profile.id())
|
||||
|
||||
return {"FINISHED"}
|
||||
|
||||
@@ -630,47 +626,6 @@ class EnableEditingMaterialSetItem(bpy.types.Operator):
|
||||
elif data_type == "boolean":
|
||||
new.bool_value = False if new.is_null else material_set_item_data[attribute.name()]
|
||||
blenderbim.bim.helper.add_attribute_description(new)
|
||||
|
||||
def load_profile_attributes(self, material_set_item, material_set_item_data):
|
||||
self.props.material_set_item_profile_attributes.clear()
|
||||
|
||||
if not material_set_item_data["Profile"]:
|
||||
return
|
||||
|
||||
profile = self.file.by_id(material_set_item_data["Profile"])
|
||||
profile_data = ProfileData.profiles[material_set_item_data["Profile"]]
|
||||
|
||||
for attribute in IfcStore.get_schema().declaration_by_name(profile.is_a()).all_attributes():
|
||||
data_type = ifcopenshell.util.attribute.get_primitive_type(attribute)
|
||||
if data_type == "entity":
|
||||
continue
|
||||
if attribute.name() in profile_data:
|
||||
new = self.props.material_set_item_profile_attributes.add()
|
||||
new.name = attribute.name()
|
||||
new.ifc_class = profile.is_a()
|
||||
new.is_null = profile_data[attribute.name()] is None
|
||||
new.is_optional = attribute.optional()
|
||||
new.data_type = data_type
|
||||
if data_type == "string":
|
||||
new.string_value = "" if new.is_null else profile_data[attribute.name()]
|
||||
elif data_type == "float":
|
||||
new.float_value = 0.0 if new.is_null else profile_data[attribute.name()]
|
||||
elif data_type == "integer":
|
||||
new.int_value = 0 if new.is_null else profile_data[attribute.name()]
|
||||
elif data_type == "boolean":
|
||||
new.bool_value = False if new.is_null else profile_data[attribute.name()]
|
||||
elif data_type == "enum":
|
||||
new.enum_items = json.dumps(ifcopenshell.util.attribute.get_enum_items(attribute))
|
||||
if profile_data[attribute.name()]:
|
||||
new.enum_value = profile_data[attribute.name()]
|
||||
|
||||
blenderbim.bim.helper.add_attribute_description(new)
|
||||
|
||||
# Force null to be false if the attribute is mandatory because when we first assign a profile, all of
|
||||
# its fields are null (which is illegal).
|
||||
# TODO: find a better solution.
|
||||
if not new.is_optional:
|
||||
new.is_null = False
|
||||
|
||||
|
||||
class DisableEditingMaterialSetItem(bpy.types.Operator):
|
||||
@@ -700,6 +655,7 @@ class EditMaterialSetItem(bpy.types.Operator):
|
||||
obj = bpy.data.objects.get(self.obj) if self.obj else context.active_object
|
||||
self.file = IfcStore.get_file()
|
||||
props = obj.BIMObjectMaterialProperties
|
||||
mprops = context.scene.BIMMaterialProperties
|
||||
product_data = Data.products[obj.BIMObjectProperties.ifc_definition_id]
|
||||
|
||||
attributes = blenderbim.bim.helper.export_attributes(props.material_set_item_attributes)
|
||||
@@ -727,19 +683,19 @@ class EditMaterialSetItem(bpy.types.Operator):
|
||||
)
|
||||
Data.load_layers()
|
||||
elif product_data["type"] == "IfcMaterialProfileSet" or product_data["type"] == "IfcMaterialProfileSetUsage":
|
||||
profile_attributes = blenderbim.bim.helper.export_attributes(props.material_set_item_profile_attributes)
|
||||
profile_def = None
|
||||
if mprops.profiles:
|
||||
profile_def = tool.Ifc.get().by_id(int(mprops.profiles))
|
||||
|
||||
ifcopenshell.api.run(
|
||||
"material.edit_profile",
|
||||
self.file,
|
||||
**{
|
||||
"profile": self.file.by_id(self.material_set_item),
|
||||
"attributes": attributes,
|
||||
"profile_attributes": profile_attributes,
|
||||
"material": self.file.by_id(int(obj.BIMObjectMaterialProperties.material_set_item_material)),
|
||||
},
|
||||
profile=self.file.by_id(self.material_set_item),
|
||||
attributes=attributes,
|
||||
profile_def=profile_def,
|
||||
material=self.file.by_id(int(obj.BIMObjectMaterialProperties.material_set_item_material)),
|
||||
)
|
||||
Data.load_profiles()
|
||||
ProfileData.load(self.file)
|
||||
else:
|
||||
pass
|
||||
|
||||
|
||||
@@ -112,6 +112,12 @@ def get_material_types(self, context):
|
||||
return MaterialsData.data["material_types"]
|
||||
|
||||
|
||||
def get_profiles(self, context):
|
||||
if not MaterialsData.is_loaded:
|
||||
MaterialsData.load()
|
||||
return MaterialsData.data["profiles"]
|
||||
|
||||
|
||||
class Material(PropertyGroup):
|
||||
name: StringProperty(name="Name")
|
||||
ifc_definition_id: IntProperty(name="IFC Definition ID")
|
||||
@@ -125,6 +131,7 @@ class BIMMaterialProperties(PropertyGroup):
|
||||
material_type: EnumProperty(items=get_material_types, name="Material Type")
|
||||
materials: CollectionProperty(name="Materials", type=Material)
|
||||
active_material_index: IntProperty(name="Active Material Index")
|
||||
profiles: EnumProperty(items=get_profiles, name="Profiles")
|
||||
|
||||
|
||||
class BIMObjectMaterialProperties(PropertyGroup):
|
||||
@@ -135,9 +142,6 @@ class BIMObjectMaterialProperties(PropertyGroup):
|
||||
material_set_attributes: CollectionProperty(name="Material Set Attributes", type=Attribute)
|
||||
active_material_set_item_id: IntProperty(name="Active Material Set ID")
|
||||
material_set_item_attributes: CollectionProperty(name="Material Set Item Attributes", type=Attribute)
|
||||
material_set_item_profile_attributes: CollectionProperty(
|
||||
name="Material Set Item Profile Attributes", type=Attribute
|
||||
)
|
||||
material_set_item_material: EnumProperty(items=get_materials, name="Material")
|
||||
profile_classes: EnumProperty(items=get_profile_classes, name="Profile Classes")
|
||||
parameterized_profile_classes: EnumProperty(
|
||||
|
||||
@@ -19,7 +19,6 @@
|
||||
import blenderbim.bim.helper
|
||||
from bpy.types import Panel, UIList
|
||||
from ifcopenshell.api.material.data import Data
|
||||
from ifcopenshell.api.profile.data import Data as ProfileData
|
||||
from blenderbim.bim.ifc import IfcStore
|
||||
from blenderbim.bim.helper import draw_attributes
|
||||
from blenderbim.bim.helper import prop_with_search
|
||||
@@ -128,12 +127,11 @@ class BIM_PT_object_material(Panel):
|
||||
self.file = IfcStore.get_file()
|
||||
self.oprops = context.active_object.BIMObjectProperties
|
||||
self.props = context.active_object.BIMObjectMaterialProperties
|
||||
self.mprops = context.scene.BIMMaterialProperties
|
||||
if not Data.is_loaded:
|
||||
Data.load(IfcStore.get_file())
|
||||
if self.oprops.ifc_definition_id not in Data.products:
|
||||
Data.load(IfcStore.get_file(), self.oprops.ifc_definition_id)
|
||||
if not ProfileData.is_loaded:
|
||||
ProfileData.load(self.file)
|
||||
self.product_data = Data.products[self.oprops.ifc_definition_id]
|
||||
|
||||
if not ObjectMaterialData.data["materials"]:
|
||||
@@ -235,15 +233,13 @@ class BIM_PT_object_material(Panel):
|
||||
self.draw_read_only_set_ui()
|
||||
|
||||
def draw_editable_set_ui(self):
|
||||
blenderbim.bim.helper.draw_attributes(self.props.material_set_attributes, self.layout)
|
||||
blenderbim.bim.helper.draw_attributes(self.props.material_set_usage_attributes, self.layout)
|
||||
|
||||
for attribute in self.props.material_set_attributes:
|
||||
row = self.layout.row(align=True)
|
||||
row.prop(attribute, "string_value", text=attribute.name)
|
||||
row.prop(attribute, "is_null", icon="RADIOBUT_OFF" if attribute.is_null else "RADIOBUT_ON", text="")
|
||||
row = self.layout.row(align=True)
|
||||
prop_with_search(row, self.props, "material", text="")
|
||||
|
||||
if self.set_item_name == "profile":
|
||||
row.prop(self.mprops, "profiles", icon="ITALIC", text="")
|
||||
prop_with_search(row, self.props, "material", icon="MATERIAL", text="")
|
||||
op = row.operator(f"bim.add_{self.set_item_name}", icon="ADD", text="")
|
||||
setattr(op, f"{self.set_item_name}_set", self.material_set_id)
|
||||
|
||||
@@ -262,34 +258,18 @@ class BIM_PT_object_material(Panel):
|
||||
|
||||
box = self.layout.box()
|
||||
row = box.row(align=True)
|
||||
row.prop(self.props, "material_set_item_material", icon="MATERIAL")
|
||||
op = row.operator("bim.edit_material_set_item", icon="CHECKMARK", text="")
|
||||
op = row.operator("bim.edit_material_set_item", icon="CHECKMARK", text="Save Changes")
|
||||
op.material_set_item = set_item_id
|
||||
row.operator("bim.disable_editing_material_set_item", icon="CANCEL", text="")
|
||||
|
||||
draw_attributes(self.props.material_set_item_attributes, self.layout)
|
||||
draw_attributes(self.props.material_set_item_attributes, box)
|
||||
|
||||
row = box.row()
|
||||
row.prop(self.props, "material_set_item_material", icon="MATERIAL", text="Material")
|
||||
|
||||
if self.set_item_name == "profile":
|
||||
self.draw_assign_profile_ui(box, item)
|
||||
self.draw_editable_profile_ui(box, item)
|
||||
|
||||
def draw_assign_profile_ui(self, layout, item):
|
||||
row = layout.row(align=True)
|
||||
row.prop(self.props, "profile_classes", text="")
|
||||
if self.props.profile_classes == "IfcParameterizedProfileDef":
|
||||
row.prop(self.props, "parameterized_profile_classes", text="")
|
||||
op = row.operator(
|
||||
"bim.assign_parameterized_profile", icon="GREASEPENCIL" if item["Profile"] else "ADD", text=""
|
||||
)
|
||||
op.ifc_class = self.props.parameterized_profile_classes
|
||||
op.material_profile = item["id"]
|
||||
else:
|
||||
# TODO: support non parametric profiles by showing a list of named profiles to select from, or an
|
||||
# eyedropper to pick profile geometry from the scene
|
||||
row.operator("bim.disable_editing_material_set_item", icon="CANCEL", text="")
|
||||
|
||||
def draw_editable_profile_ui(self, layout, item):
|
||||
draw_attributes(self.props.material_set_item_profile_attributes, self.layout)
|
||||
row = box.row()
|
||||
row.prop(self.mprops, "profiles", icon="ITALIC", text="Profile")
|
||||
|
||||
def draw_read_only_set_item_ui(self, set_item_id, index, is_first=False, is_last=False):
|
||||
if self.product_data["type"] == "IfcMaterialList":
|
||||
|
||||
@@ -199,6 +199,17 @@ class DumbProfileGenerator:
|
||||
|
||||
|
||||
class DumbProfileRegenerator:
|
||||
def regenerate_from_profile_def(self, profile):
|
||||
self.file = tool.Ifc.get()
|
||||
objs = []
|
||||
if not profile:
|
||||
return
|
||||
for element in self.get_elements_using_profile(profile):
|
||||
obj = tool.Ifc.get_object(element)
|
||||
if obj:
|
||||
objs.append(obj)
|
||||
DumbProfileRecalculator().recalculate(objs)
|
||||
|
||||
def regenerate_from_profile(self, usecase_path, ifc_file, settings):
|
||||
self.file = ifc_file
|
||||
objs = []
|
||||
|
||||
@@ -20,6 +20,7 @@ import bpy
|
||||
import ifcopenshell.api
|
||||
import blenderbim.bim.helper
|
||||
import blenderbim.tool as tool
|
||||
import blenderbim.bim.module.model.profile as model_profile
|
||||
from blenderbim.bim.module.model.slab import DecorationsHandler
|
||||
|
||||
|
||||
@@ -84,6 +85,7 @@ class DisableEditingProfile(bpy.types.Operator):
|
||||
|
||||
def execute(self, context):
|
||||
context.scene.BIMProfileProperties.active_profile_id = 0
|
||||
bpy.ops.bim.disable_editing_arbitrary_profile()
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
@@ -123,6 +125,8 @@ class EnableEditingArbitraryProfile(bpy.types.Operator, tool.Ifc.Operator):
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
|
||||
def _execute(self, context):
|
||||
for obj in context.selected_objects:
|
||||
obj.select_set(False)
|
||||
props = context.scene.BIMProfileProperties
|
||||
profile = tool.Ifc.get().by_id(props.active_profile_id)
|
||||
obj = tool.Model.import_profile(profile)
|
||||
@@ -140,9 +144,10 @@ class DisableEditingArbitraryProfile(bpy.types.Operator, tool.Ifc.Operator):
|
||||
|
||||
def _execute(self, context):
|
||||
obj = context.active_object
|
||||
DecorationsHandler.uninstall()
|
||||
bpy.ops.object.mode_set(mode="OBJECT")
|
||||
bpy.data.objects.remove(obj)
|
||||
if obj and obj.data and obj.data.BIMMeshProperties.is_profile:
|
||||
DecorationsHandler.uninstall()
|
||||
bpy.ops.object.mode_set(mode="OBJECT")
|
||||
bpy.data.objects.remove(obj)
|
||||
|
||||
|
||||
class EditArbitraryProfile(bpy.types.Operator, tool.Ifc.Operator):
|
||||
@@ -172,6 +177,10 @@ class EditArbitraryProfile(bpy.types.Operator, tool.Ifc.Operator):
|
||||
bpy.data.objects.remove(obj)
|
||||
profile.ProfileType = old_profile.ProfileType
|
||||
profile.ProfileName = old_profile.ProfileName
|
||||
for inverse in tool.Ifc.get().get_inverse(old_profile):
|
||||
ifcopenshell.util.element.replace_attribute(inverse, old_profile, profile)
|
||||
ifcopenshell.util.element.remove_deep2(tool.Ifc.get(), old_profile)
|
||||
bpy.ops.bim.load_profiles()
|
||||
props.active_profile_id = profile.id()
|
||||
|
||||
model_profile.DumbProfileRegenerator().regenerate_from_profile_def(profile)
|
||||
|
||||
@@ -20,13 +20,17 @@
|
||||
class Usecase:
|
||||
def __init__(self, file, **settings):
|
||||
self.file = file
|
||||
self.settings = {"profile_set": None, "material": None}
|
||||
self.settings = {"profile_set": None, "material": None, "profile": None}
|
||||
for key, value in settings.items():
|
||||
self.settings[key] = value
|
||||
|
||||
def execute(self):
|
||||
profiles = list(self.settings["profile_set"].MaterialProfiles or [])
|
||||
profile = self.file.create_entity("IfcMaterialProfile", **{"Material": self.settings["material"]})
|
||||
profile = self.file.create_entity("IfcMaterialProfile")
|
||||
if self.settings["material"]:
|
||||
profile.Material = self.settings["material"]
|
||||
if self.settings["profile"]:
|
||||
profile.Profile = self.settings["profile"]
|
||||
profiles.append(profile)
|
||||
self.settings["profile_set"].MaterialProfiles = profiles
|
||||
return profile
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
# IfcOpenShell - IFC toolkit and geometry engine
|
||||
# Copyright (C) 2021 Dion Moult <dion@thinkmoult.com>
|
||||
# Copyright (C) 2021, 2022 Dion Moult <dion@thinkmoult.com>
|
||||
#
|
||||
# This file is part of IfcOpenShell.
|
||||
#
|
||||
@@ -20,14 +20,14 @@
|
||||
class Usecase:
|
||||
def __init__(self, file, **settings):
|
||||
self.file = file
|
||||
self.settings = {"profile": None, "attributes": {}, "profile_attributes": {}, "material": None}
|
||||
self.settings = {"profile": None, "attributes": {}, "profile_def": None, "material": None}
|
||||
for key, value in settings.items():
|
||||
self.settings[key] = value
|
||||
|
||||
def execute(self):
|
||||
# TODO: don't also edit the profile def in this usecase
|
||||
for name, value in self.settings["attributes"].items():
|
||||
setattr(self.settings["profile"], name, value)
|
||||
self.settings["profile"].Material = self.settings["material"]
|
||||
for name, value in self.settings["profile_attributes"].items():
|
||||
setattr(self.settings["profile"].Profile, name, value)
|
||||
if self.settings["material"]:
|
||||
self.settings["profile"].Material = self.settings["material"]
|
||||
if self.settings["profile_def"]:
|
||||
self.settings["profile"].Profile = self.settings["profile_def"]
|
||||
|
||||
Reference in New Issue
Block a user