Fix #1111 - issue where sometimes class and product dropdown gets out of sync. Also new utility function to query is_a() in the schema.

This commit is contained in:
Dion Moult
2020-12-02 09:51:00 +11:00
parent c70b41f21a
commit 4e62c276ee
2 changed files with 103 additions and 46 deletions
@@ -10,6 +10,7 @@ import ifcopenshell
import ifcopenshell.util.selector import ifcopenshell.util.selector
import ifcopenshell.util.geolocation import ifcopenshell.util.geolocation
import ifcopenshell.util.pset import ifcopenshell.util.pset
import ifcopenshell.util.schema
import tempfile import tempfile
from . import export_ifc from . import export_ifc
from . import import_ifc from . import import_ifc
@@ -220,7 +221,22 @@ class ReassignClass(bpy.types.Operator):
def execute(self, context): def execute(self, context):
obj = bpy.context.active_object obj = bpy.context.active_object
ifc_class = obj.name.split("/")[0]
ifc_schema = ifcopenshell.ifcopenshell_wrapper.schema_by_name(bpy.context.scene.BIMProperties.export_schema)
bpy.context.active_object.BIMObjectProperties.is_reassigning_class = True bpy.context.active_object.BIMObjectProperties.is_reassigning_class = True
ifc_products = [
"IfcElement",
"IfcElementType",
"IfcSpatialElement",
"IfcGroup",
"IfcStructural",
"IfcPositioningElement",
"IfcContext",
"IfcAnnotation",
]
for ifc_product in ifc_products:
if ifcopenshell.util.schema.is_a(ifc_schema.declaration_by_name(ifc_class), ifc_product):
bpy.context.scene.BIMProperties.ifc_product = ifc_product
bpy.context.scene.BIMProperties.ifc_class = obj.name.split("/")[0] bpy.context.scene.BIMProperties.ifc_class = obj.name.split("/")[0]
predefined_type = obj.BIMObjectProperties.attributes.get("PredefinedType") predefined_type = obj.BIMObjectProperties.attributes.get("PredefinedType")
if predefined_type: if predefined_type:
@@ -1801,6 +1817,7 @@ class SelectClashSource(bpy.types.Operator):
context.window_manager.fileselect_add(self) context.window_manager.fileselect_add(self)
return {"RUNNING_MODAL"} return {"RUNNING_MODAL"}
class SelectClashResults(bpy.types.Operator): class SelectClashResults(bpy.types.Operator):
bl_idname = "bim.select_clash_results" bl_idname = "bim.select_clash_results"
bl_label = "Select Clash Results" bl_label = "Select Clash Results"
@@ -1814,6 +1831,7 @@ class SelectClashResults(bpy.types.Operator):
context.window_manager.fileselect_add(self) context.window_manager.fileselect_add(self)
return {"RUNNING_MODAL"} return {"RUNNING_MODAL"}
class SelectSmartGroupedClashesPath(bpy.types.Operator): class SelectSmartGroupedClashesPath(bpy.types.Operator):
bl_idname = "bim.select_smart_grouped_clashes_path" bl_idname = "bim.select_smart_grouped_clashes_path"
bl_label = "Select Smart-Grouped Clashes Path" bl_label = "Select Smart-Grouped Clashes Path"
@@ -1827,6 +1845,7 @@ class SelectSmartGroupedClashesPath(bpy.types.Operator):
context.window_manager.fileselect_add(self) context.window_manager.fileselect_add(self)
return {"RUNNING_MODAL"} return {"RUNNING_MODAL"}
class ExecuteIfcClash(bpy.types.Operator): class ExecuteIfcClash(bpy.types.Operator):
bl_idname = "bim.execute_ifc_clash" bl_idname = "bim.execute_ifc_clash"
bl_label = "Execute IFC Clash" bl_label = "Execute IFC Clash"
@@ -1901,6 +1920,7 @@ class SelectIfcClashResults(bpy.types.Operator):
obj.select_set(True) obj.select_set(True)
return {"FINISHED"} return {"FINISHED"}
class SmartClashGroup(bpy.types.Operator): class SmartClashGroup(bpy.types.Operator):
bl_idname = "bim.smart_clash_group" bl_idname = "bim.smart_clash_group"
bl_label = "Smart Group Clashes" bl_label = "Smart Group Clashes"
@@ -1921,10 +1941,12 @@ class SmartClashGroup(bpy.types.Operator):
# execute the smart grouping # execute the smart grouping
save_path = bpy.path.ensure_ext(bpy.context.scene.BIMProperties.smart_grouped_clashes_path, ".json") save_path = bpy.path.ensure_ext(bpy.context.scene.BIMProperties.smart_grouped_clashes_path, ".json")
smart_grouped_clashes = ifc_clasher.smart_group_clashes(clash_sets, bpy.context.scene.BIMProperties.smart_clash_grouping_max_distance) smart_grouped_clashes = ifc_clasher.smart_group_clashes(
clash_sets, bpy.context.scene.BIMProperties.smart_clash_grouping_max_distance
)
# save smart_groups to json # save smart_groups to json
with open(save_path, 'w') as f: with open(save_path, "w") as f:
f.write(json.dumps(smart_grouped_clashes)) f.write(json.dumps(smart_grouped_clashes))
clash_set_name = bpy.context.scene.BIMProperties.clash_sets[ clash_set_name = bpy.context.scene.BIMProperties.clash_sets[
@@ -1990,7 +2012,9 @@ class SelectSmartGroup(bpy.types.Operator):
def execute(self, context): def execute(self, context):
# Select smart group in view # Select smart group in view
selected_smart_group = bpy.context.scene.BIMProperties.smart_clash_groups[bpy.context.scene.BIMProperties.active_smart_group_index] selected_smart_group = bpy.context.scene.BIMProperties.smart_clash_groups[
bpy.context.scene.BIMProperties.active_smart_group_index
]
# print(selected_smart_group.number) # print(selected_smart_group.number)
for obj in bpy.context.visible_objects: for obj in bpy.context.visible_objects:
@@ -2005,6 +2029,7 @@ class SelectSmartGroup(bpy.types.Operator):
return {"FINISHED"} return {"FINISHED"}
class SelectBcfFile(bpy.types.Operator): class SelectBcfFile(bpy.types.Operator):
bl_idname = "bim.select_bcf_file" bl_idname = "bim.select_bcf_file"
bl_label = "Select BCF File" bl_label = "Select BCF File"
@@ -2555,7 +2580,8 @@ class CutSection(bpy.types.Operator):
def does_obj_have_target_view_representation(self, obj, camera): def does_obj_have_target_view_representation(self, obj, camera):
return camera.data.BIMCameraProperties.target_view in [ return camera.data.BIMCameraProperties.target_view in [
c.target_view for c in obj.BIMObjectProperties.representation_contexts c.target_view
for c in obj.BIMObjectProperties.representation_contexts
if c.context == "Plan" and c.name == "Annotation" if c.context == "Plan" and c.name == "Annotation"
] ]
@@ -2713,7 +2739,11 @@ class SwitchContext(bpy.types.Operator):
self.obj.data.name = "Model/Body/MODEL_VIEW/" + self.obj.data.name self.obj.data.name = "Model/Body/MODEL_VIEW/" + self.obj.data.name
has_default_context = False has_default_context = False
for subcontext in self.obj.BIMObjectProperties.representation_contexts: for subcontext in self.obj.BIMObjectProperties.representation_contexts:
if subcontext.context == "Model" and subcontext.name == "Body" and subcontext.target_view == "MODEL_VIEW": if (
subcontext.context == "Model"
and subcontext.name == "Body"
and subcontext.target_view == "MODEL_VIEW"
):
has_default_context = True has_default_context = True
break break
if not has_default_context: if not has_default_context:
@@ -8,6 +8,16 @@ import ifcopenshell
cwd = os.path.dirname(os.path.realpath(__file__)) cwd = os.path.dirname(os.path.realpath(__file__))
def is_a(entity, ifc_class):
ifc_class = ifc_class.lower()
if entity.name_lc() == ifc_class:
return True
if entity.supertype():
return is_a(entity.supertype(), ifc_class)
return False
class Migrator: class Migrator:
def __init__(self): def __init__(self):
self.migrated_ids = {} self.migrated_ids = {}
@@ -118,7 +128,11 @@ class Migrator:
else: else:
return return
except: except:
print("Unable to find equivalent attribute of {} to migrate from {} to {}".format(attribute.name(), element, new_element)) print(
"Unable to find equivalent attribute of {} to migrate from {} to {}".format(
attribute.name(), element, new_element
)
)
return # We tried our best return # We tried our best
elif new_file.schema == "IFC4": elif new_file.schema == "IFC4":
# IFC2X3 to IFC4: We know the IFC4 attribute name, but not its IFC2X3 equivalent # IFC2X3 to IFC4: We know the IFC4 attribute name, but not its IFC2X3 equivalent
@@ -131,7 +145,11 @@ class Migrator:
else: else:
return return
except: except:
print("Unable to find equivalent attribute of {} to migrate from {} to {}".format(attribute.name(), element, new_element)) print(
"Unable to find equivalent attribute of {} to migrate from {} to {}".format(
attribute.name(), element, new_element
)
)
return # We tried our best return # We tried our best
# print("Continuing migration of {} to migrate from {} to {}".format(attribute.name(), element, new_element)) # print("Continuing migration of {} to migrate from {} to {}".format(attribute.name(), element, new_element))
@@ -155,24 +173,33 @@ class Migrator:
elif self.default_entities[attribute.name()]: elif self.default_entities[attribute.name()]:
return self.default_entities[attribute.name()] return self.default_entities[attribute.name()]
elif attribute.name() == "OwnerHistory": elif attribute.name() == "OwnerHistory":
self.default_entities[attribute.name()] = new_file.create_entity("IfcOwnerHistory", **{ self.default_entities[attribute.name()] = new_file.create_entity(
"OwningUser": new_file.create_entity("IfcPersonAndOrganization", **{ "IfcOwnerHistory",
**{
"OwningUser": new_file.create_entity(
"IfcPersonAndOrganization",
**{
"ThePerson": new_file.create_entity("IfcPerson"), "ThePerson": new_file.create_entity("IfcPerson"),
"TheOrganization": new_file.create_entity("IfcOrganization", **{ "TheOrganization": new_file.create_entity(
"Name": "IfcOpenShell Migrator" "IfcOrganization", **{"Name": "IfcOpenShell Migrator"}
}) ),
}), }
"OwningApplication": new_file.create_entity("IfcApplication", **{ ),
"ApplicationDeveloper": new_file.create_entity("IfcOrganization", **{ "OwningApplication": new_file.create_entity(
"Name": "IfcOpenShell Migrator" "IfcApplication",
}), **{
"ApplicationDeveloper": new_file.create_entity(
"IfcOrganization", **{"Name": "IfcOpenShell Migrator"}
),
"Version": "Works for me", "Version": "Works for me",
"ApplicationFullName": "IfcOpenShell Migrator", "ApplicationFullName": "IfcOpenShell Migrator",
"ApplicationIdentifier": "IfcOpenShell Migrator", "ApplicationIdentifier": "IfcOpenShell Migrator",
}), }
),
"ChangeAction": "NOCHANGE", "ChangeAction": "NOCHANGE",
"CreationDate": int(time.time()) "CreationDate": int(time.time()),
}) }
)
return self.default_entities[attribute.name()] return self.default_entities[attribute.name()]