mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-09-22 15:48:04 +00:00
Fix potential instability when assigning types and synchronising Blender geometry
This commit is contained in:
@@ -4,8 +4,6 @@ from . import ui, prop, operator
|
|||||||
classes = (
|
classes = (
|
||||||
operator.EditObjectPlacement,
|
operator.EditObjectPlacement,
|
||||||
operator.AddRepresentation,
|
operator.AddRepresentation,
|
||||||
operator.MapRepresentations,
|
|
||||||
operator.MapRepresentation,
|
|
||||||
operator.SwitchRepresentation,
|
operator.SwitchRepresentation,
|
||||||
operator.RemoveRepresentation,
|
operator.RemoveRepresentation,
|
||||||
operator.UpdateMeshRepresentation,
|
operator.UpdateMeshRepresentation,
|
||||||
|
|||||||
@@ -156,23 +156,35 @@ class AddRepresentation(bpy.types.Operator):
|
|||||||
class SwitchRepresentation(bpy.types.Operator):
|
class SwitchRepresentation(bpy.types.Operator):
|
||||||
bl_idname = "bim.switch_representation"
|
bl_idname = "bim.switch_representation"
|
||||||
bl_label = "Switch Representation"
|
bl_label = "Switch Representation"
|
||||||
|
obj: bpy.props.StringProperty()
|
||||||
ifc_definition_id: bpy.props.IntProperty()
|
ifc_definition_id: bpy.props.IntProperty()
|
||||||
|
should_reload: bpy.props.BoolProperty()
|
||||||
disable_opening_subtractions: bpy.props.BoolProperty()
|
disable_opening_subtractions: bpy.props.BoolProperty()
|
||||||
|
|
||||||
def execute(self, context):
|
def execute(self, context):
|
||||||
self.obj = bpy.context.active_object
|
self.element_obj = bpy.data.objects.get(self.obj) if self.obj else bpy.context.active_object
|
||||||
self.oprops = self.obj.BIMObjectProperties
|
self.oprops = self.element_obj.BIMObjectProperties
|
||||||
|
|
||||||
self.file = IfcStore.get_file()
|
self.file = IfcStore.get_file()
|
||||||
self.context_of_items = self.file.by_id(self.ifc_definition_id).ContextOfItems
|
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)
|
mesh = bpy.data.meshes.get(self.mesh_name)
|
||||||
if mesh:
|
if mesh:
|
||||||
self.obj.data.user_remap(mesh)
|
self.element_obj.data.user_remap(mesh)
|
||||||
self.pull_mesh_from_ifc()
|
if not mesh or self.should_reload:
|
||||||
|
self.pull_mesh_from_ifc()
|
||||||
return {"FINISHED"}
|
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):
|
def pull_mesh_from_ifc(self):
|
||||||
self.file = IfcStore.get_file()
|
self.file = IfcStore.get_file()
|
||||||
logger = logging.getLogger("ImportIFC")
|
logger = logging.getLogger("ImportIFC")
|
||||||
@@ -194,9 +206,9 @@ class SwitchRepresentation(bpy.types.Operator):
|
|||||||
mesh = ifc_importer.create_mesh(element, shape)
|
mesh = ifc_importer.create_mesh(element, shape)
|
||||||
mesh.name = self.mesh_name
|
mesh.name = self.mesh_name
|
||||||
mesh.BIMMeshProperties.ifc_definition_id = self.ifc_definition_id
|
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 = 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.disable_opening_subtractions and self.context_of_items.ContextIdentifier == "Body":
|
||||||
if self.oprops.ifc_definition_id not in VoidData.products:
|
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]:
|
for opening_id in VoidData.products[self.oprops.ifc_definition_id]:
|
||||||
if opening_id in IfcStore.id_map:
|
if opening_id in IfcStore.id_map:
|
||||||
opening = IfcStore.id_map[opening_id]
|
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.operation = "DIFFERENCE"
|
||||||
modifier.object = opening
|
modifier.object = opening
|
||||||
else:
|
else:
|
||||||
for modifier in self.obj.modifiers:
|
for modifier in self.element_obj.modifiers:
|
||||||
if modifier.type == "BOOLEAN" and "IfcOpeningElement" in modifier.name:
|
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):
|
class RemoveRepresentation(bpy.types.Operator):
|
||||||
@@ -249,61 +261,6 @@ class RemoveRepresentation(bpy.types.Operator):
|
|||||||
return {"FINISHED"}
|
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):
|
class UpdateMeshRepresentation(bpy.types.Operator):
|
||||||
bl_idname = "bim.update_mesh_representation"
|
bl_idname = "bim.update_mesh_representation"
|
||||||
bl_label = "Update Mesh Representation"
|
bl_label = "Update Mesh Representation"
|
||||||
@@ -407,7 +364,7 @@ class UpdateParametricRepresentation(bpy.types.Operator):
|
|||||||
props = obj.data.BIMMeshProperties
|
props = obj.data.BIMMeshProperties
|
||||||
parameter = props.ifc_parameters[self.index]
|
parameter = props.ifc_parameters[self.index]
|
||||||
element = IfcStore.get_file().by_id(parameter.step_id)[parameter.index] = parameter.value
|
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"}
|
return {"FINISHED"}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -39,6 +39,7 @@ class BIM_PT_representations(Panel):
|
|||||||
row.label(text=representation["ContextOfItems"]["TargetView"])
|
row.label(text=representation["ContextOfItems"]["TargetView"])
|
||||||
row.label(text=representation["RepresentationType"])
|
row.label(text=representation["RepresentationType"])
|
||||||
op = row.operator("bim.switch_representation", icon="OUTLINER_DATA_MESH", text="")
|
op = row.operator("bim.switch_representation", icon="OUTLINER_DATA_MESH", text="")
|
||||||
|
op.should_reload = True
|
||||||
op.ifc_definition_id = ifc_definition_id
|
op.ifc_definition_id = ifc_definition_id
|
||||||
op.disable_opening_subtractions = False
|
op.disable_opening_subtractions = False
|
||||||
row.operator("bim.remove_representation", icon="X", text="").representation_id = ifc_definition_id
|
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)
|
row = layout.row(align=True)
|
||||||
op = row.operator("bim.switch_representation", text="Bake Voids", icon="SELECT_SUBTRACT")
|
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.ifc_definition_id = props.ifc_definition_id
|
||||||
op.disable_opening_subtractions = False
|
op.disable_opening_subtractions = False
|
||||||
op = row.operator("bim.switch_representation", text="Dynamic Voids", icon="SELECT_INTERSECT")
|
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.ifc_definition_id = props.ifc_definition_id
|
||||||
op.disable_opening_subtractions = True
|
op.disable_opening_subtractions = True
|
||||||
|
|
||||||
|
|||||||
@@ -245,9 +245,7 @@ class CopyClass(bpy.types.Operator):
|
|||||||
IfcStore.link_element(result, obj)
|
IfcStore.link_element(result, obj)
|
||||||
relating_type = ifcopenshell.util.element.get_type(result)
|
relating_type = ifcopenshell.util.element.get_type(result)
|
||||||
if relating_type and relating_type.RepresentationMaps:
|
if relating_type and relating_type.RepresentationMaps:
|
||||||
bpy.ops.bim.map_representations(
|
bpy.ops.bim.assign_type(relating_type=relating_type.id(), related_object=obj.name)
|
||||||
product_id=result.id(), type_product_id=ifcopenshell.util.element.get_type(result).id()
|
|
||||||
)
|
|
||||||
else:
|
else:
|
||||||
bpy.ops.bim.add_representation(obj=obj.name)
|
bpy.ops.bim.add_representation(obj=obj.name)
|
||||||
return {"FINISHED"}
|
return {"FINISHED"}
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import ifcopenshell.util.type
|
|||||||
import ifcopenshell.api
|
import ifcopenshell.api
|
||||||
from blenderbim.bim.ifc import IfcStore
|
from blenderbim.bim.ifc import IfcStore
|
||||||
from ifcopenshell.api.type.data import Data
|
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 blenderbim.bim.module.type.prop import getIfcTypes, getAvailableTypes, updateTypeInstanceIfcClass
|
||||||
from mathutils import Vector
|
from mathutils import Vector
|
||||||
|
|
||||||
@@ -31,8 +32,18 @@ class AssignType(bpy.types.Operator):
|
|||||||
},
|
},
|
||||||
)
|
)
|
||||||
Data.load(IfcStore.get_file(), oprops.ifc_definition_id)
|
Data.load(IfcStore.get_file(), oprops.ifc_definition_id)
|
||||||
if self.file.by_id(relating_type).RepresentationMaps:
|
GeometryData.load(IfcStore.get_file(), oprops.ifc_definition_id)
|
||||||
bpy.ops.bim.map_representations(product_id=oprops.ifc_definition_id, type_product_id=relating_type)
|
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)
|
bpy.ops.bim.disable_editing_type(obj=related_object.name)
|
||||||
return {"FINISHED"}
|
return {"FINISHED"}
|
||||||
|
|||||||
@@ -14,11 +14,8 @@ class Data:
|
|||||||
cls.products[product_id] = []
|
cls.products[product_id] = []
|
||||||
product = file.by_id(product_id)
|
product = file.by_id(product_id)
|
||||||
representations = []
|
representations = []
|
||||||
if product.is_a("IfcProduct"):
|
if product.is_a("IfcProduct") and product.Representation:
|
||||||
if product.Representation:
|
representations = product.Representation.Representations
|
||||||
representations = product.Representation.Representations
|
|
||||||
else:
|
|
||||||
representations = []
|
|
||||||
elif product.is_a("IfcTypeProduct"):
|
elif product.is_a("IfcTypeProduct"):
|
||||||
representations = [rm.MappedRepresentation for rm in product.RepresentationMaps or []]
|
representations = [rm.MappedRepresentation for rm in product.RepresentationMaps or []]
|
||||||
for representation in representations:
|
for representation in representations:
|
||||||
|
|||||||
@@ -52,3 +52,30 @@ class Usecase:
|
|||||||
"RelatingType": self.settings["relating_type"],
|
"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}
|
||||||
|
)
|
||||||
|
|||||||
Reference in New Issue
Block a user