mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-09-12 06:32:09 +00:00
Option to convert custom properties to ifc psets when assigning an IFC class (#6921)
Feature location - https://files.catbox.moe/cuvdan.png (assign class with alt+click modifier) See #6921 for an example of converted props
This commit is contained in:
@@ -18,6 +18,7 @@
|
|||||||
|
|
||||||
import bpy
|
import bpy
|
||||||
import bmesh
|
import bmesh
|
||||||
|
import idprop
|
||||||
import ifcopenshell
|
import ifcopenshell
|
||||||
import ifcopenshell.api
|
import ifcopenshell.api
|
||||||
import ifcopenshell.api.geometry
|
import ifcopenshell.api.geometry
|
||||||
@@ -180,12 +181,16 @@ class AssignClass(bpy.types.Operator, tool.Ifc.Operator):
|
|||||||
bl_idname = "bim.assign_class"
|
bl_idname = "bim.assign_class"
|
||||||
bl_label = "Assign IFC Class"
|
bl_label = "Assign IFC Class"
|
||||||
bl_options = {"REGISTER", "UNDO"}
|
bl_options = {"REGISTER", "UNDO"}
|
||||||
bl_description = "Assign the IFC Class to the selected non-ifc objects."
|
bl_description = (
|
||||||
|
"Assign the IFC Class to the selected non-ifc objects.\n\n"
|
||||||
|
+ "ALT+CLICK to also convert object's custom properties to custom Pset."
|
||||||
|
)
|
||||||
obj: bpy.props.StringProperty()
|
obj: bpy.props.StringProperty()
|
||||||
ifc_class: bpy.props.StringProperty()
|
ifc_class: bpy.props.StringProperty()
|
||||||
predefined_type: bpy.props.StringProperty()
|
predefined_type: bpy.props.StringProperty()
|
||||||
userdefined_type: bpy.props.StringProperty()
|
userdefined_type: bpy.props.StringProperty()
|
||||||
context_id: bpy.props.IntProperty()
|
context_id: bpy.props.IntProperty()
|
||||||
|
props_to_pset: bpy.props.BoolProperty(options={"SKIP_SAVE"})
|
||||||
|
|
||||||
# TODO: is never used?
|
# TODO: is never used?
|
||||||
should_add_representation: bpy.props.BoolProperty(default=True)
|
should_add_representation: bpy.props.BoolProperty(default=True)
|
||||||
@@ -212,6 +217,10 @@ class AssignClass(bpy.types.Operator, tool.Ifc.Operator):
|
|||||||
return False
|
return False
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
def invoke(self, context, event):
|
||||||
|
self.props_to_pset = event.alt
|
||||||
|
return self.execute(context)
|
||||||
|
|
||||||
def _execute(self, context):
|
def _execute(self, context):
|
||||||
ifc_file = tool.Ifc.get()
|
ifc_file = tool.Ifc.get()
|
||||||
props = tool.Root.get_root_props()
|
props = tool.Root.get_root_props()
|
||||||
@@ -360,6 +369,19 @@ class AssignClass(bpy.types.Operator, tool.Ifc.Operator):
|
|||||||
elif obj.data is not None:
|
elif obj.data is not None:
|
||||||
new_obj = tool.Geometry.recreate_object_with_data(obj, None)
|
new_obj = tool.Geometry.recreate_object_with_data(obj, None)
|
||||||
|
|
||||||
|
# Accomodate existing importers to Blender from other formats that set custom props
|
||||||
|
if self.props_to_pset:
|
||||||
|
custom_props = {}
|
||||||
|
for k, v in obj.items():
|
||||||
|
if type(v) in [bool, int, float, str]:
|
||||||
|
custom_props[k] = v
|
||||||
|
elif type(v) is idprop.types.IDPropertyArray:
|
||||||
|
for idx in range(len(v)):
|
||||||
|
custom_props["{}.{}".format(k, idx + 1)] = v[idx]
|
||||||
|
|
||||||
|
pset = ifcopenshell.api.pset.add_pset(ifc_file, product=element, name="BBIM_ImportedBlenderProps")
|
||||||
|
ifcopenshell.api.pset.edit_pset(ifc_file, pset=pset, properties=custom_props)
|
||||||
|
|
||||||
# TODO: reload representation might lead to the object being replaced by object of the other type.
|
# TODO: reload representation might lead to the object being replaced by object of the other type.
|
||||||
# We probably should track it somehow and keep the original selection.
|
# We probably should track it somehow and keep the original selection.
|
||||||
|
|
||||||
|
|||||||
@@ -238,3 +238,77 @@ class TestReassignClass(NewFile):
|
|||||||
assert len(ifc_file.by_type("IfcSlab")) == 0
|
assert len(ifc_file.by_type("IfcSlab")) == 0
|
||||||
assert len(ifc_file.by_type("IfcWallType")) == n_wall_types + 1
|
assert len(ifc_file.by_type("IfcWallType")) == n_wall_types + 1
|
||||||
assert len(ifc_file.by_type("IfcSlabType")) == n_slab_types - 1
|
assert len(ifc_file.by_type("IfcSlabType")) == n_slab_types - 1
|
||||||
|
|
||||||
|
|
||||||
|
class TestAssignClass(NewFile):
|
||||||
|
def create_objects(self, context):
|
||||||
|
# Create blender cylinder
|
||||||
|
bpy.ops.mesh.primitive_cylinder_add(vertices=10, location=(0, 4, 0))
|
||||||
|
datablock_obj = bpy.data.objects["Cylinder"]
|
||||||
|
|
||||||
|
# Create blender cube w/ props
|
||||||
|
bpy.ops.mesh.primitive_cube_add(location=(0, 0, 0))
|
||||||
|
obj = bpy.data.objects["Cube"]
|
||||||
|
# Set all the custom properties on the obj
|
||||||
|
obj["01_float"] = 3.14159
|
||||||
|
obj["02_float_array"] = [3.14159, 1.61803, 2.71828]
|
||||||
|
obj["03_integer"] = 2
|
||||||
|
obj["04_integer_array"] = [2, 3, 5, 7]
|
||||||
|
obj["05_boolean"] = True
|
||||||
|
obj["06_boolean_array"] = [True, False]
|
||||||
|
obj["07_string"] = "Bonsai!"
|
||||||
|
# Data Block is not proper Pointer in UI. Probably doesn't matter.
|
||||||
|
obj["08_data_block"] = datablock_obj
|
||||||
|
# Python expressions can also be stored, i.e. dictionary
|
||||||
|
obj["09_python"] = {"test": 12}
|
||||||
|
# Or a list of things, i.e. objects: [bpy.data.objects['IfcBuildingElementProxy/Cube']]
|
||||||
|
obj["10_python"] = bpy.context.selected_objects
|
||||||
|
|
||||||
|
return obj, datablock_obj
|
||||||
|
|
||||||
|
def test_normal_assign_ifc_class(self):
|
||||||
|
# Setup project
|
||||||
|
tool.Project.get_project_props().template_file = "IFC4 Demo Template.ifc"
|
||||||
|
bpy.ops.bim.create_project()
|
||||||
|
ifc_file = tool.Ifc.get()
|
||||||
|
context = bpy.context
|
||||||
|
obj, datablock_obj = self.create_objects(context)
|
||||||
|
|
||||||
|
# Assign IfcClass
|
||||||
|
bpy.ops.bim.assign_class(ifc_class="IfcBuildingElementProxy", predefined_type="ELEMENT", userdefined_type="")
|
||||||
|
element = tool.Ifc.get_entity(obj)
|
||||||
|
assert element
|
||||||
|
|
||||||
|
# Get Psets
|
||||||
|
psets = ifcopenshell.util.element.get_psets(element, psets_only=True)
|
||||||
|
assert psets == {}
|
||||||
|
|
||||||
|
def test_alternative_assign_ifc_class(self):
|
||||||
|
# Setup project
|
||||||
|
tool.Project.get_project_props().template_file = "IFC4 Demo Template.ifc"
|
||||||
|
bpy.ops.bim.create_project()
|
||||||
|
ifc_file = tool.Ifc.get()
|
||||||
|
context = bpy.context
|
||||||
|
obj, datablock_obj = self.create_objects(context)
|
||||||
|
|
||||||
|
# Assign IfcClass
|
||||||
|
bpy.ops.bim.assign_class(
|
||||||
|
ifc_class="IfcBuildingElementProxy", predefined_type="ELEMENT", userdefined_type="", props_to_pset=True
|
||||||
|
)
|
||||||
|
element = tool.Ifc.get_entity(obj)
|
||||||
|
assert element
|
||||||
|
|
||||||
|
# Get Psets
|
||||||
|
psets = ifcopenshell.util.element.get_psets(element, psets_only=True)
|
||||||
|
assert "BBIM_ImportedBlenderProps" in psets
|
||||||
|
pset = psets["BBIM_ImportedBlenderProps"]
|
||||||
|
assert "01_float" in pset and type(pset["01_float"]) is float
|
||||||
|
assert "02_float_array.1" in pset and type(pset["02_float_array.1"]) is float
|
||||||
|
assert "03_integer" in pset and type(pset["03_integer"]) is int
|
||||||
|
assert "04_integer_array.1" in pset and type(pset["04_integer_array.1"]) is int
|
||||||
|
assert "05_boolean" in pset and type(pset["05_boolean"]) is bool
|
||||||
|
assert "06_boolean_array.1" in pset and type(pset["06_boolean_array.1"]) is bool
|
||||||
|
assert "07_string" in pset and type(pset["07_string"]) is str
|
||||||
|
assert "08_data_block" not in pset
|
||||||
|
assert "09_python" not in pset
|
||||||
|
assert "10_python" not in pset
|
||||||
|
|||||||
Reference in New Issue
Block a user