Fix potential instability when assigning types and synchronising Blender geometry

This commit is contained in:
Dion Moult
2021-05-18 14:36:12 +10:00
parent 30c31d694a
commit e6bf32b832
7 changed files with 69 additions and 78 deletions
@@ -4,8 +4,6 @@ from . import ui, prop, operator
classes = (
operator.EditObjectPlacement,
operator.AddRepresentation,
operator.MapRepresentations,
operator.MapRepresentation,
operator.SwitchRepresentation,
operator.RemoveRepresentation,
operator.UpdateMeshRepresentation,
@@ -156,23 +156,35 @@ class AddRepresentation(bpy.types.Operator):
class SwitchRepresentation(bpy.types.Operator):
bl_idname = "bim.switch_representation"
bl_label = "Switch Representation"
obj: bpy.props.StringProperty()
ifc_definition_id: bpy.props.IntProperty()
should_reload: bpy.props.BoolProperty()
disable_opening_subtractions: bpy.props.BoolProperty()
def execute(self, context):
self.obj = bpy.context.active_object
self.oprops = self.obj.BIMObjectProperties
self.element_obj = bpy.data.objects.get(self.obj) if self.obj else bpy.context.active_object
self.oprops = self.element_obj.BIMObjectProperties
self.file = IfcStore.get_file()
self.context_of_items = self.file.by_id(self.ifc_definition_id).ContextOfItems
self.mesh_name = "{}/{}".format(self.context_of_items.id(), self.ifc_definition_id)
self.mesh_name = self.get_mesh_name()
mesh = bpy.data.meshes.get(self.mesh_name)
if mesh:
self.obj.data.user_remap(mesh)
self.pull_mesh_from_ifc()
self.element_obj.data.user_remap(mesh)
if not mesh or self.should_reload:
self.pull_mesh_from_ifc()
return {"FINISHED"}
def get_mesh_name(self):
representation = self.resolve_mapped_representation(self.file.by_id(self.ifc_definition_id))
return "{}/{}".format(self.context_of_items.id(), representation.id())
def resolve_mapped_representation(self, representation):
if representation.RepresentationType == "MappedRepresentation":
return self.resolve_mapped_representation(representation.Items[0].MappingSource.MappedRepresentation)
return representation
def pull_mesh_from_ifc(self):
self.file = IfcStore.get_file()
logger = logging.getLogger("ImportIFC")
@@ -194,9 +206,9 @@ class SwitchRepresentation(bpy.types.Operator):
mesh = ifc_importer.create_mesh(element, shape)
mesh.name = self.mesh_name
mesh.BIMMeshProperties.ifc_definition_id = self.ifc_definition_id
self.obj.data.user_remap(mesh)
self.element_obj.data.user_remap(mesh)
material_creator = import_ifc.MaterialCreator(ifc_import_settings, ifc_importer)
material_creator.create(element, self.obj, mesh)
material_creator.create(element, self.element_obj, mesh)
if self.disable_opening_subtractions and self.context_of_items.ContextIdentifier == "Body":
if self.oprops.ifc_definition_id not in VoidData.products:
@@ -204,13 +216,13 @@ class SwitchRepresentation(bpy.types.Operator):
for opening_id in VoidData.products[self.oprops.ifc_definition_id]:
if opening_id in IfcStore.id_map:
opening = IfcStore.id_map[opening_id]
modifier = self.obj.modifiers.new("IfcOpeningElement", "BOOLEAN")
modifier = self.element_obj.modifiers.new("IfcOpeningElement", "BOOLEAN")
modifier.operation = "DIFFERENCE"
modifier.object = opening
else:
for modifier in self.obj.modifiers:
for modifier in self.element_obj.modifiers:
if modifier.type == "BOOLEAN" and "IfcOpeningElement" in modifier.name:
self.obj.modifiers.remove(modifier)
self.element_obj.modifiers.remove(modifier)
class RemoveRepresentation(bpy.types.Operator):
@@ -249,61 +261,6 @@ class RemoveRepresentation(bpy.types.Operator):
return {"FINISHED"}
class MapRepresentations(bpy.types.Operator):
bl_idname = "bim.map_representations"
bl_label = "Map Representations"
product_id: bpy.props.IntProperty()
type_product_id: bpy.props.IntProperty()
def execute(self, context):
related_object = IfcStore.id_map[self.product_id]
if self.product_id not in Data.products:
Data.load(IfcStore.get_file(), self.product_id)
for representation_id in Data.products[self.product_id]:
bpy.ops.bim.remove_representation(obj=related_object.name, representation_id=representation_id)
if self.type_product_id not in Data.products:
Data.load(IfcStore.get_file(), self.type_product_id)
for representation_id in Data.products[self.type_product_id]:
bpy.ops.bim.map_representation(
obj=related_object.name,
representation_id=representation_id,
obj_data=IfcStore.id_map[self.type_product_id].data.name,
)
return {"FINISHED"}
class MapRepresentation(bpy.types.Operator):
bl_idname = "bim.map_representation"
bl_label = "Map Representation"
obj: bpy.props.StringProperty()
representation_id: bpy.props.IntProperty()
obj_data: bpy.props.StringProperty()
def execute(self, context):
objs = [bpy.data.objects.get(self.obj)] if self.obj else bpy.context.selected_objects
obj_data = bpy.data.meshes.get(self.obj_data) if self.obj_data else None
self.file = IfcStore.get_file()
for obj in objs:
bpy.ops.bim.edit_object_placement(obj=obj.name)
product = self.file.by_id(obj.BIMObjectProperties.ifc_definition_id)
if obj_data:
obj.data = obj_data
result = ifcopenshell.api.run(
"geometry.map_representation", self.file, **{"representation": self.file.by_id(self.representation_id)}
)
ifcopenshell.api.run(
"geometry.assign_representation", self.file, **{"product": product, "representation": result}
)
Data.load(IfcStore.get_file(), obj.BIMObjectProperties.ifc_definition_id)
return {"FINISHED"}
class UpdateMeshRepresentation(bpy.types.Operator):
bl_idname = "bim.update_mesh_representation"
bl_label = "Update Mesh Representation"
@@ -407,7 +364,7 @@ class UpdateParametricRepresentation(bpy.types.Operator):
props = obj.data.BIMMeshProperties
parameter = props.ifc_parameters[self.index]
element = IfcStore.get_file().by_id(parameter.step_id)[parameter.index] = parameter.value
bpy.ops.bim.switch_representation(ifc_definition_id=props.ifc_definition_id)
bpy.ops.bim.switch_representation(ifc_definition_id=props.ifc_definition_id, should_reload=True)
return {"FINISHED"}
@@ -39,6 +39,7 @@ class BIM_PT_representations(Panel):
row.label(text=representation["ContextOfItems"]["TargetView"])
row.label(text=representation["RepresentationType"])
op = row.operator("bim.switch_representation", icon="OUTLINER_DATA_MESH", text="")
op.should_reload = True
op.ifc_definition_id = ifc_definition_id
op.disable_opening_subtractions = False
row.operator("bim.remove_representation", icon="X", text="").representation_id = ifc_definition_id
@@ -68,9 +69,11 @@ class BIM_PT_mesh(Panel):
row = layout.row(align=True)
op = row.operator("bim.switch_representation", text="Bake Voids", icon="SELECT_SUBTRACT")
op.should_reload = True
op.ifc_definition_id = props.ifc_definition_id
op.disable_opening_subtractions = False
op = row.operator("bim.switch_representation", text="Dynamic Voids", icon="SELECT_INTERSECT")
op.should_reload = True
op.ifc_definition_id = props.ifc_definition_id
op.disable_opening_subtractions = True
@@ -245,9 +245,7 @@ class CopyClass(bpy.types.Operator):
IfcStore.link_element(result, obj)
relating_type = ifcopenshell.util.element.get_type(result)
if relating_type and relating_type.RepresentationMaps:
bpy.ops.bim.map_representations(
product_id=result.id(), type_product_id=ifcopenshell.util.element.get_type(result).id()
)
bpy.ops.bim.assign_type(relating_type=relating_type.id(), related_object=obj.name)
else:
bpy.ops.bim.add_representation(obj=obj.name)
return {"FINISHED"}
@@ -4,6 +4,7 @@ import ifcopenshell.util.type
import ifcopenshell.api
from blenderbim.bim.ifc import IfcStore
from ifcopenshell.api.type.data import Data
from ifcopenshell.api.geometry.data import Data as GeometryData
from blenderbim.bim.module.type.prop import getIfcTypes, getAvailableTypes, updateTypeInstanceIfcClass
from mathutils import Vector
@@ -31,8 +32,18 @@ class AssignType(bpy.types.Operator):
},
)
Data.load(IfcStore.get_file(), oprops.ifc_definition_id)
if self.file.by_id(relating_type).RepresentationMaps:
bpy.ops.bim.map_representations(product_id=oprops.ifc_definition_id, type_product_id=relating_type)
GeometryData.load(IfcStore.get_file(), oprops.ifc_definition_id)
representation_ids = GeometryData.products[oprops.ifc_definition_id]
if not representation_ids:
pass # TODO: clear geometry? Make void? Make none type?
has_switched = False
for representation_id in representation_ids:
representation = GeometryData.representations[representation_id]
if representation["ContextOfItems"]["ContextIdentifier"] == "Body":
bpy.ops.bim.switch_representation(obj=related_object.name, ifc_definition_id=representation_id)
has_switched = True
if not has_switched and representation_ids:
bpy.ops.bim.switch_representation(obj=related_object.name, ifc_definition_id=representation_id)
bpy.ops.bim.disable_editing_type(obj=related_object.name)
return {"FINISHED"}
@@ -14,11 +14,8 @@ class Data:
cls.products[product_id] = []
product = file.by_id(product_id)
representations = []
if product.is_a("IfcProduct"):
if product.Representation:
representations = product.Representation.Representations
else:
representations = []
if product.is_a("IfcProduct") and product.Representation:
representations = product.Representation.Representations
elif product.is_a("IfcTypeProduct"):
representations = [rm.MappedRepresentation for rm in product.RepresentationMaps or []]
for representation in representations:
@@ -52,3 +52,30 @@ class Usecase:
"RelatingType": self.settings["relating_type"],
}
)
self.map_representations()
def map_representations(self):
if not self.settings["relating_type"].RepresentationMaps:
return
representations = []
if self.settings["related_object"].Representation:
representations = self.settings["related_object"].Representation.Representations
for representation in representations:
# TODO: check if this is right? Surely this can be a single usecase?
ifcopenshell.api.run(
"geometry.unassign_representation",
self.file,
**{"product": self.settings["related_object"], "representation": representation}
)
ifcopenshell.api.run("geometry.remove_representation", self.file, **{"representation": representation})
for representation_map in self.settings["relating_type"].RepresentationMaps:
representation = representation_map.MappedRepresentation
result = ifcopenshell.api.run(
"geometry.map_representation", self.file, **{"representation": representation}
)
ifcopenshell.api.run(
"geometry.assign_representation",
self.file,
**{"product": self.settings["related_object"], "representation": result}
)