mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-08-11 10:06:47 +00:00
You can now assign parameterized profiles to material profiles. Thanks Jesusbill!
This commit is contained in:
@@ -10,6 +10,7 @@ classes = (
|
||||
operator.RemoveConstituent,
|
||||
operator.AddProfile,
|
||||
operator.RemoveProfile,
|
||||
operator.AssignParameterizedProfile,
|
||||
operator.AddLayer,
|
||||
operator.RemoveLayer,
|
||||
operator.ReorderMaterialSetItem,
|
||||
|
||||
@@ -1,8 +1,36 @@
|
||||
import bpy
|
||||
import json
|
||||
import ifcopenshell.api
|
||||
import ifcopenshell.util.attribute
|
||||
from blenderbim.bim.ifc import IfcStore
|
||||
from ifcopenshell.api.material.data import Data
|
||||
from ifcopenshell.api.profile.data import Data as ProfileData
|
||||
|
||||
|
||||
class AssignParameterizedProfile(bpy.types.Operator):
|
||||
bl_idname = "bim.assign_parameterized_profile"
|
||||
bl_label = "Assign Parameterized Profile"
|
||||
ifc_class: bpy.props.StringProperty()
|
||||
material_profile: bpy.props.IntProperty()
|
||||
obj: bpy.props.StringProperty()
|
||||
|
||||
def execute(self, context):
|
||||
obj = bpy.data.objects.get(self.obj) if self.obj else bpy.context.active_object
|
||||
self.file = IfcStore.get_file()
|
||||
profile = ifcopenshell.api.run(
|
||||
"profile.add_parameterized_profile",
|
||||
self.file,
|
||||
**{"ifc_class": self.ifc_class},
|
||||
)
|
||||
ifcopenshell.api.run(
|
||||
"material.assign_profile",
|
||||
self.file,
|
||||
**{"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"}
|
||||
|
||||
|
||||
class AddMaterial(bpy.types.Operator):
|
||||
@@ -370,8 +398,8 @@ 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 bpy.context.active_object
|
||||
props = obj.BIMObjectMaterialProperties
|
||||
props.active_material_set_item_id = self.material_set_item
|
||||
self.props = obj.BIMObjectMaterialProperties
|
||||
self.props.active_material_set_item_id = self.material_set_item
|
||||
product_data = Data.products[obj.BIMObjectProperties.ifc_definition_id]
|
||||
material_set_item = self.file.by_id(self.material_set_item)
|
||||
|
||||
@@ -384,17 +412,24 @@ class EnableEditingMaterialSetItem(bpy.types.Operator):
|
||||
else:
|
||||
material_set_item_data = {}
|
||||
|
||||
props.material_set_item_material = str(material_set_item_data["Material"])
|
||||
self.props.material_set_item_material = str(material_set_item_data["Material"])
|
||||
|
||||
while len(props.material_set_item_attributes) > 0:
|
||||
props.material_set_item_attributes.remove(0)
|
||||
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)
|
||||
|
||||
return {"FINISHED"}
|
||||
|
||||
def load_set_item_attributes(self, material_set_item, material_set_item_data):
|
||||
while len(self.props.material_set_item_attributes) > 0:
|
||||
self.props.material_set_item_attributes.remove(0)
|
||||
|
||||
for attribute in IfcStore.get_schema().declaration_by_name(material_set_item.is_a()).all_attributes():
|
||||
data_type = ifcopenshell.util.attribute.get_primitive_type(attribute)
|
||||
if data_type == "entity":
|
||||
continue
|
||||
if attribute.name() in material_set_item_data:
|
||||
new = props.material_set_item_attributes.add()
|
||||
new = self.props.material_set_item_attributes.add()
|
||||
new.name = attribute.name()
|
||||
new.is_null = material_set_item_data[attribute.name()] is None
|
||||
new.data_type = data_type
|
||||
@@ -406,7 +441,45 @@ class EnableEditingMaterialSetItem(bpy.types.Operator):
|
||||
new.int_value = 0 if new.is_null else material_set_item_data[attribute.name()]
|
||||
elif data_type == "boolean":
|
||||
new.bool_value = False if new.is_null else material_set_item_data[attribute.name()]
|
||||
return {"FINISHED"}
|
||||
|
||||
def load_profile_attributes(self, material_set_item, material_set_item_data):
|
||||
while len(self.props.material_set_item_profile_attributes) > 0:
|
||||
self.props.material_set_item_profile_attributes.remove(0)
|
||||
|
||||
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.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()]
|
||||
|
||||
# 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):
|
||||
@@ -468,16 +541,31 @@ class EditMaterialSetItem(bpy.types.Operator):
|
||||
)
|
||||
Data.load_layers()
|
||||
elif product_data["type"] == "IfcMaterialProfileSet":
|
||||
profile_attributes = {}
|
||||
for attribute in props.material_set_item_profile_attributes:
|
||||
if attribute.data_type == "string":
|
||||
value = attribute.string_value
|
||||
elif attribute.data_type == "float":
|
||||
value = attribute.float_value
|
||||
elif attribute.data_type == "integer":
|
||||
value = attribute.int_value
|
||||
elif attribute.data_type == "boolean":
|
||||
value = attribute.bool_value
|
||||
elif attribute.data_type == "enum":
|
||||
value = attribute.enum_value
|
||||
profile_attributes[attribute.name] = None if attribute.is_null else value
|
||||
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)),
|
||||
},
|
||||
)
|
||||
Data.load_profiles()
|
||||
ProfileData.load(self.file)
|
||||
else:
|
||||
pass
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import bpy
|
||||
import blenderbim.bim.schema # refactor
|
||||
import blenderbim.bim.schema # refactor
|
||||
from ifcopenshell.api.material.data import Data
|
||||
from blenderbim.bim.ifc import IfcStore
|
||||
from blenderbim.bim.prop import StrProperty, Attribute
|
||||
@@ -17,6 +17,29 @@ from bpy.props import (
|
||||
|
||||
materials_enum = []
|
||||
materialtypes_enum = []
|
||||
profileclasses_enum = []
|
||||
parameterizedprofileclasses_enum = []
|
||||
|
||||
|
||||
def getProfileClasses(self, context):
|
||||
global profileclasses_enum
|
||||
if len(profileclasses_enum) == 0 and IfcStore.get_schema():
|
||||
profileclasses_enum.clear()
|
||||
profileclasses_enum = [
|
||||
(t.name(), t.name(), "") for t in IfcStore.get_schema().declaration_by_name("IfcProfileDef").subtypes()
|
||||
]
|
||||
return profileclasses_enum
|
||||
|
||||
|
||||
def getParameterizedProfileClasses(self, context):
|
||||
global parameterizedprofileclasses_enum
|
||||
if len(parameterizedprofileclasses_enum) == 0 and IfcStore.get_schema():
|
||||
parameterizedprofileclasses_enum.clear()
|
||||
parameterizedprofileclasses_enum = [
|
||||
(t.name(), t.name(), "")
|
||||
for t in IfcStore.get_schema().declaration_by_name("IfcParameterizedProfileDef").subtypes()
|
||||
]
|
||||
return parameterizedprofileclasses_enum
|
||||
|
||||
|
||||
def getMaterials(self, context):
|
||||
@@ -52,4 +75,9 @@ 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=getMaterials, name="Material")
|
||||
profile_classes: EnumProperty(items=getProfileClasses, name="Profile Classes")
|
||||
parameterized_profile_classes: EnumProperty(
|
||||
items=getParameterizedProfileClasses, name="Parameterized Profile Classes"
|
||||
)
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
from bpy.types import Panel
|
||||
from ifcopenshell.api.material.data import Data
|
||||
from ifcopenshell.api.profile.data import Data as ProfileData
|
||||
from blenderbim.bim.ifc import IfcStore
|
||||
|
||||
|
||||
@@ -46,6 +47,8 @@ class BIM_PT_object_material(Panel):
|
||||
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 Data.materials:
|
||||
@@ -172,6 +175,39 @@ class BIM_PT_object_material(Panel):
|
||||
row.prop(attribute, "bool_value", text=attribute.name)
|
||||
row.prop(attribute, "is_null", icon="RADIOBUT_OFF" if attribute.is_null else "RADIOBUT_ON", text="")
|
||||
|
||||
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="X", text="")
|
||||
|
||||
def draw_editable_profile_ui(self, layout, item):
|
||||
for attribute in self.props.material_set_item_profile_attributes:
|
||||
row = layout.row(align=True)
|
||||
if attribute.data_type == "string":
|
||||
row.prop(attribute, "string_value", text=attribute.name)
|
||||
elif attribute.data_type == "integer":
|
||||
row.prop(attribute, "int_value", text=attribute.name)
|
||||
elif attribute.data_type == "float":
|
||||
row.prop(attribute, "float_value", text=attribute.name)
|
||||
elif attribute.data_type == "boolean":
|
||||
row.prop(attribute, "bool_value", text=attribute.name)
|
||||
elif attribute.data_type == "enum":
|
||||
row.prop(attribute, "enum_value", text=attribute.name)
|
||||
if attribute.is_optional:
|
||||
row.prop(attribute, "is_null", icon="RADIOBUT_OFF" if attribute.is_null else "RADIOBUT_ON", text="")
|
||||
|
||||
def draw_read_only_set_item_ui(self, set_item_id, index, is_first=False, is_last=False):
|
||||
if self.product_data["type"] == "IfcMaterialList":
|
||||
item = Data.materials[set_item_id]
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
class Usecase:
|
||||
def __init__(self, file, **settings):
|
||||
self.file = file
|
||||
self.settings = {"material_profile": None, "profile": None}
|
||||
for key, value in settings.items():
|
||||
self.settings[key] = value
|
||||
|
||||
def execute(self):
|
||||
if (
|
||||
self.settings["material_profile"].Profile
|
||||
and len(self.file.get_inverse(self.settings["material_profile"].Profile)) == 1
|
||||
):
|
||||
self.file.remove(self.settings["material_profile"].Profile)
|
||||
self.settings["material_profile"].Profile = self.settings["profile"]
|
||||
@@ -4,6 +4,7 @@ class Usecase():
|
||||
self.settings = {
|
||||
"profile": None,
|
||||
"attributes": {},
|
||||
"profile_attributes": {},
|
||||
"material": None
|
||||
}
|
||||
for key, value in settings.items():
|
||||
@@ -13,3 +14,5 @@ class 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)
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
class Usecase:
|
||||
def __init__(self, file, **settings):
|
||||
self.file = file
|
||||
self.settings = {"ifc_class": None}
|
||||
for key, value in settings.items():
|
||||
self.settings[key] = value
|
||||
|
||||
def execute(self):
|
||||
return self.file.create_entity(self.settings["ifc_class"])
|
||||
@@ -0,0 +1,17 @@
|
||||
class Data:
|
||||
is_loaded = False
|
||||
profiles = {}
|
||||
|
||||
@classmethod
|
||||
def purge(cls):
|
||||
cls.is_loaded = False
|
||||
cls.profiles = {}
|
||||
|
||||
@classmethod
|
||||
def load(cls, file):
|
||||
if not file:
|
||||
return
|
||||
cls.profiles = {}
|
||||
for profile in file.by_type("IfcProfileDef"):
|
||||
cls.profiles[profile.id()] = profile.get_info()
|
||||
cls.is_loaded = True
|
||||
Reference in New Issue
Block a user