mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-09-18 06:21:40 +00:00
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:
@@ -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,21 +2012,24 @@ 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[
|
||||||
#print(selected_smart_group.number)
|
bpy.context.scene.BIMProperties.active_smart_group_index
|
||||||
|
]
|
||||||
|
# print(selected_smart_group.number)
|
||||||
|
|
||||||
for obj in bpy.context.visible_objects:
|
for obj in bpy.context.visible_objects:
|
||||||
global_id = obj.BIMObjectProperties.attributes.get("GlobalId")
|
global_id = obj.BIMObjectProperties.attributes.get("GlobalId")
|
||||||
if global_id:
|
if global_id:
|
||||||
for id in selected_smart_group.global_ids:
|
for id in selected_smart_group.global_ids:
|
||||||
#print("Id: ", id)
|
# print("Id: ", id)
|
||||||
#print("Global id: ", global_id.string_value)
|
# print("Global id: ", global_id.string_value)
|
||||||
if global_id.string_value in id.name:
|
if global_id.string_value in id.name:
|
||||||
#print("object match: ", global_id)
|
# print("object match: ", global_id)
|
||||||
obj.select_set(True)
|
obj.select_set(True)
|
||||||
|
|
||||||
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 = {}
|
||||||
@@ -70,13 +80,13 @@ class Migrator:
|
|||||||
return new_file.by_id(self.migrated_ids[element.id()])
|
return new_file.by_id(self.migrated_ids[element.id()])
|
||||||
except:
|
except:
|
||||||
pass
|
pass
|
||||||
#print("Migrating", element)
|
# print("Migrating", element)
|
||||||
schema = ifcopenshell.ifcopenshell_wrapper.schema_by_name(new_file.schema)
|
schema = ifcopenshell.ifcopenshell_wrapper.schema_by_name(new_file.schema)
|
||||||
new_element = self.migrate_class(element, new_file)
|
new_element = self.migrate_class(element, new_file)
|
||||||
#print("Migrated class from {} to {}".format(element, new_element))
|
# print("Migrated class from {} to {}".format(element, new_element))
|
||||||
new_element_schema = schema.declaration_by_name(new_element.is_a())
|
new_element_schema = schema.declaration_by_name(new_element.is_a())
|
||||||
if not hasattr(new_element_schema, "all_attributes"):
|
if not hasattr(new_element_schema, "all_attributes"):
|
||||||
return element # The element has no attributes, so migration is done
|
return element # The element has no attributes, so migration is done
|
||||||
new_element = self.migrate_attributes(element, new_file, new_element, new_element_schema)
|
new_element = self.migrate_attributes(element, new_file, new_element, new_element_schema)
|
||||||
self.migrated_ids[element.id()] = new_element.id()
|
self.migrated_ids[element.id()] = new_element.id()
|
||||||
return new_element
|
return new_element
|
||||||
@@ -102,39 +112,47 @@ class Migrator:
|
|||||||
return new_element
|
return new_element
|
||||||
|
|
||||||
def migrate_attribute(self, attribute, element, new_file, new_element, new_element_schema):
|
def migrate_attribute(self, attribute, element, new_file, new_element, new_element_schema):
|
||||||
#print("Migrating attribute", element, new_element, attribute.name())
|
# print("Migrating attribute", element, new_element, attribute.name())
|
||||||
if hasattr(element, attribute.name()):
|
if hasattr(element, attribute.name()):
|
||||||
value = getattr(element, attribute.name())
|
value = getattr(element, attribute.name())
|
||||||
#print("Attribute names matched", value)
|
# print("Attribute names matched", value)
|
||||||
elif new_file.schema == "IFC2X3":
|
elif new_file.schema == "IFC2X3":
|
||||||
# IFC4 to IFC2X3: We know the IFC2X3 attribute name, but not its IFC4 equivalent
|
# IFC4 to IFC2X3: We know the IFC2X3 attribute name, but not its IFC4 equivalent
|
||||||
#print("Searching for an equivalent", new_element, attribute.name())
|
# print("Searching for an equivalent", new_element, attribute.name())
|
||||||
try:
|
try:
|
||||||
equivalent_map = self.attribute_4_to_2x3[new_element.is_a()]
|
equivalent_map = self.attribute_4_to_2x3[new_element.is_a()]
|
||||||
equivalent = list(equivalent_map.keys())[list(equivalent_map.values()).index(attribute.name())]
|
equivalent = list(equivalent_map.keys())[list(equivalent_map.values()).index(attribute.name())]
|
||||||
if hasattr(element, equivalent):
|
if hasattr(element, equivalent):
|
||||||
#print("Equivalent found", equivalent)
|
# print("Equivalent found", equivalent)
|
||||||
value = getattr(element, equivalent)
|
value = getattr(element, equivalent)
|
||||||
else:
|
else:
|
||||||
return
|
return
|
||||||
except:
|
except:
|
||||||
print("Unable to find equivalent attribute of {} to migrate from {} to {}".format(attribute.name(), element, new_element))
|
print(
|
||||||
return # We tried our best
|
"Unable to find equivalent attribute of {} to migrate from {} to {}".format(
|
||||||
|
attribute.name(), element, new_element
|
||||||
|
)
|
||||||
|
)
|
||||||
|
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
|
||||||
#print("Searching for an equivalent", element, new_element, attribute.name())
|
# print("Searching for an equivalent", element, new_element, attribute.name())
|
||||||
try:
|
try:
|
||||||
equivalent = self.attribute_4_to_2x3[new_element.is_a()][attribute.name()]
|
equivalent = self.attribute_4_to_2x3[new_element.is_a()][attribute.name()]
|
||||||
#print("Searching for equivalent", equivalent)
|
# print("Searching for equivalent", equivalent)
|
||||||
if hasattr(element, equivalent):
|
if hasattr(element, equivalent):
|
||||||
value = getattr(element, equivalent)
|
value = getattr(element, equivalent)
|
||||||
else:
|
else:
|
||||||
return
|
return
|
||||||
except:
|
except:
|
||||||
print("Unable to find equivalent attribute of {} to migrate from {} to {}".format(attribute.name(), element, new_element))
|
print(
|
||||||
return # We tried our best
|
"Unable to find equivalent attribute of {} to migrate from {} to {}".format(
|
||||||
|
attribute.name(), element, new_element
|
||||||
|
)
|
||||||
|
)
|
||||||
|
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))
|
||||||
if value is None and not attribute.optional():
|
if value is None and not attribute.optional():
|
||||||
value = self.generate_default_value(attribute, new_file)
|
value = self.generate_default_value(attribute, new_file)
|
||||||
if value is None:
|
if value is None:
|
||||||
@@ -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",
|
||||||
"ThePerson": new_file.create_entity("IfcPerson"),
|
**{
|
||||||
"TheOrganization": new_file.create_entity("IfcOrganization", **{
|
"OwningUser": new_file.create_entity(
|
||||||
"Name": "IfcOpenShell Migrator"
|
"IfcPersonAndOrganization",
|
||||||
})
|
**{
|
||||||
}),
|
"ThePerson": new_file.create_entity("IfcPerson"),
|
||||||
"OwningApplication": new_file.create_entity("IfcApplication", **{
|
"TheOrganization": new_file.create_entity(
|
||||||
"ApplicationDeveloper": new_file.create_entity("IfcOrganization", **{
|
"IfcOrganization", **{"Name": "IfcOpenShell Migrator"}
|
||||||
"Name": "IfcOpenShell Migrator"
|
),
|
||||||
}),
|
}
|
||||||
"Version": "Works for me",
|
),
|
||||||
"ApplicationFullName": "IfcOpenShell Migrator",
|
"OwningApplication": new_file.create_entity(
|
||||||
"ApplicationIdentifier": "IfcOpenShell Migrator",
|
"IfcApplication",
|
||||||
}),
|
**{
|
||||||
"ChangeAction": "NOCHANGE",
|
"ApplicationDeveloper": new_file.create_entity(
|
||||||
"CreationDate": int(time.time())
|
"IfcOrganization", **{"Name": "IfcOpenShell Migrator"}
|
||||||
})
|
),
|
||||||
|
"Version": "Works for me",
|
||||||
|
"ApplicationFullName": "IfcOpenShell Migrator",
|
||||||
|
"ApplicationIdentifier": "IfcOpenShell Migrator",
|
||||||
|
}
|
||||||
|
),
|
||||||
|
"ChangeAction": "NOCHANGE",
|
||||||
|
"CreationDate": int(time.time()),
|
||||||
|
}
|
||||||
|
)
|
||||||
return self.default_entities[attribute.name()]
|
return self.default_entities[attribute.name()]
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user