mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-09-24 15:07:38 +00:00
Added bim.mirror_geometry() operator for truly mirroring products / types
This commit is contained in:
committed by
Ryan Schultz
parent
9674cedf47
commit
521fc63c48
@@ -82,8 +82,8 @@ classes = (
|
|||||||
product.DrawOccurrence,
|
product.DrawOccurrence,
|
||||||
product.LoadTypeThumbnails,
|
product.LoadTypeThumbnails,
|
||||||
product.MirrorElements,
|
product.MirrorElements,
|
||||||
|
product.TrueMirrorElements,
|
||||||
product.SetActiveType,
|
product.SetActiveType,
|
||||||
product.ChangeSwingDirection,
|
|
||||||
workspace.Hotkey,
|
workspace.Hotkey,
|
||||||
workspace.BIM_MT_add_representation_item,
|
workspace.BIM_MT_add_representation_item,
|
||||||
wall.AddPerpendicularWall,
|
wall.AddPerpendicularWall,
|
||||||
|
|||||||
@@ -608,8 +608,70 @@ class FinishEditingDoor(_DoorEditMixin, bpy.types.Operator, tool.Ifc.Operator):
|
|||||||
bl_description = "Apply changes and finish editing door parameters"
|
bl_description = "Apply changes and finish editing door parameters"
|
||||||
bl_options = {"REGISTER", "UNDO"}
|
bl_options = {"REGISTER", "UNDO"}
|
||||||
|
|
||||||
def _execute(self, context: bpy.types.Context) -> set[str]:
|
def finish_editing_door_on_object(self, obj: bpy.types.Object) -> None:
|
||||||
return self._finish_targets(context)
|
element = tool.Ifc.get_entity(obj)
|
||||||
|
assert element
|
||||||
|
if not tool.Blender.Modifier.is_door(element):
|
||||||
|
return
|
||||||
|
props = tool.Model.get_door_props(obj)
|
||||||
|
|
||||||
|
door_data = props.get_general_kwargs(convert_to_project_units=True)
|
||||||
|
lining_props = props.get_lining_kwargs(convert_to_project_units=True)
|
||||||
|
panel_props = props.get_panel_kwargs(convert_to_project_units=True)
|
||||||
|
|
||||||
|
door_data["lining_properties"] = lining_props
|
||||||
|
door_data["panel_properties"] = panel_props
|
||||||
|
|
||||||
|
props.is_editing = False
|
||||||
|
|
||||||
|
update_door_modifier_representation(obj)
|
||||||
|
element_type = ifcopenshell.util.element.get_type(element)
|
||||||
|
if element_type:
|
||||||
|
tool.Model.mark_thumbnail_for_update(element_type)
|
||||||
|
|
||||||
|
pset = tool.Pset.get_element_pset(element, "BBIM_Door")
|
||||||
|
door_data_str = tool.Ifc.get().createIfcText(json.dumps(door_data, default=list))
|
||||||
|
ifcopenshell.api.pset.edit_pset(tool.Ifc.get(), pset=pset, properties={"Data": door_data_str})
|
||||||
|
|
||||||
|
if inverted_pset := ifcopenshell.util.element.get_pset(element, "BBIM_InvertedSwingType", "Data"):
|
||||||
|
inverted_data = json.loads(inverted_pset)
|
||||||
|
if "inverted_swing_type" in inverted_data and (inverted_type := tool.Ifc.get_entity_by_id(int(inverted_data["inverted_swing_type"]))):
|
||||||
|
# object has mirrored repr, update it as well
|
||||||
|
self.copy_door_params(door_data, inverted_type)
|
||||||
|
|
||||||
|
def copy_door_params(self, from_data, to_elem):
|
||||||
|
if "RIGHT" in from_data["door_type"]:
|
||||||
|
from_data["door_type"] = from_data["door_type"].replace("RIGHT", "LEFT")
|
||||||
|
else:
|
||||||
|
from_data["door_type"] = from_data["door_type"].replace("LEFT", "RIGHT")
|
||||||
|
|
||||||
|
to_pset = tool.Pset.get_element_pset(to_elem, "BBIM_Door")
|
||||||
|
ifcopenshell.api.pset.edit_pset(tool.Ifc.get(), pset=to_pset, properties={"Data": json.dumps(from_data)})
|
||||||
|
|
||||||
|
# reload door representation
|
||||||
|
from_data.update(from_data.pop("lining_properties"))
|
||||||
|
from_data.update(from_data.pop("panel_properties"))
|
||||||
|
from_data.update(tool.Model.get_constituents_props_data(to_elem))
|
||||||
|
|
||||||
|
to_obj = tool.Ifc.get_object(to_elem)
|
||||||
|
props = tool.Model.get_door_props(to_obj)
|
||||||
|
|
||||||
|
# we need this workaround because set_props_kwargs_from_ifc_data will
|
||||||
|
# "update" the mesh of the active object, which will switch its representation
|
||||||
|
prev_active = bpy.context.view_layer.objects.active
|
||||||
|
bpy.context.view_layer.objects.active = to_obj
|
||||||
|
|
||||||
|
props.set_props_kwargs_from_ifc_data(from_data)
|
||||||
|
|
||||||
|
bpy.context.view_layer.objects.active = prev_active
|
||||||
|
|
||||||
|
update_door_modifier_representation(to_obj)
|
||||||
|
tool.Model.mark_thumbnail_for_update(to_elem)
|
||||||
|
|
||||||
|
def _execute(self, context: bpy.types.Context) -> set[str]: # noqa: ARG002
|
||||||
|
for obj in tool.Blender.get_selected_objects():
|
||||||
|
self.finish_editing_door_on_object(obj)
|
||||||
|
return {"FINISHED"}
|
||||||
|
|
||||||
|
|
||||||
class EnableEditingDoor(_DoorEditMixin, bpy.types.Operator, tool.Ifc.Operator):
|
class EnableEditingDoor(_DoorEditMixin, bpy.types.Operator, tool.Ifc.Operator):
|
||||||
|
|||||||
@@ -679,11 +679,11 @@ class MirrorElements(bpy.types.Operator, tool.Ifc.Operator):
|
|||||||
|
|
||||||
obj.matrix_world = newmat
|
obj.matrix_world = newmat
|
||||||
|
|
||||||
class ChangeSwingDirection(bpy.types.Operator, tool.Ifc.Operator):
|
class TrueMirrorElements(bpy.types.Operator, tool.Ifc.Operator):
|
||||||
bl_idname = "bim.change_swing_direction"
|
bl_idname = "bim.mirror_geometry"
|
||||||
bl_label = "Change door / window swing direction"
|
bl_label = "Mirror Element Geometry"
|
||||||
bl_options = {"REGISTER", "UNDO"}
|
bl_options = {"REGISTER", "UNDO"}
|
||||||
bl_description = "Changes the swing direction of the selection door / window"
|
bl_description = "Mirrors the selected objects by mirroring their representation (or their types representation)"
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def poll(cls, context):
|
def poll(cls, context):
|
||||||
@@ -703,14 +703,11 @@ class ChangeSwingDirection(bpy.types.Operator, tool.Ifc.Operator):
|
|||||||
active_context = tool.Geometry.get_active_representation_context(obj)
|
active_context = tool.Geometry.get_active_representation_context(obj)
|
||||||
|
|
||||||
if type_element:
|
if type_element:
|
||||||
# obj has a type, try to change swing direction of type
|
# obj has a type, use / create inverted type and assign it
|
||||||
if (type_pset := ifcopenshell.util.element.get_pset(type_element, "BBIM_Door", "Data")):
|
self.assign_inverted_type(element)
|
||||||
# get or create inverted type and assign it
|
|
||||||
self.assign_inverted_door_type(element)
|
|
||||||
else:
|
else:
|
||||||
if (obj_pset := ifcopenshell.util.element.get_pset(element, "BBIM_Door", "Data", should_inherit=False)):
|
# invert representation of entity directly
|
||||||
# obj has an own geometric door representation, simply flip the door_type
|
self.invert_representation(element)
|
||||||
self.invert_door_swing(element)
|
|
||||||
|
|
||||||
# bonsai does not automatically switch to the representation that should be active in the given context
|
# bonsai does not automatically switch to the representation that should be active in the given context
|
||||||
# when switching to a type that was previously viewed in another context (e.g. plan view),
|
# when switching to a type that was previously viewed in another context (e.g. plan view),
|
||||||
@@ -725,6 +722,23 @@ class ChangeSwingDirection(bpy.types.Operator, tool.Ifc.Operator):
|
|||||||
should_sync_changes_first=False,
|
should_sync_changes_first=False,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
def invert_general_object(self, element):
|
||||||
|
if element.is_a("IfcProduct"):
|
||||||
|
if not element.Representation:
|
||||||
|
return
|
||||||
|
|
||||||
|
for representation in element.Representation.Representations:
|
||||||
|
for item in representation.Items:
|
||||||
|
builder = ifcopenshell.util.shape_builder.ShapeBuilder(tool.Ifc.get())
|
||||||
|
builder.mirror(item, (1, 0), create_copy=False)
|
||||||
|
elif element.is_a("IfcTypeProduct"):
|
||||||
|
for representation_map in element.RepresentationMaps:
|
||||||
|
for item in representation_map.MappedRepresentation.Items:
|
||||||
|
builder = ifcopenshell.util.shape_builder.ShapeBuilder(tool.Ifc.get())
|
||||||
|
builder.mirror(item, (1, 0), create_copy=False)
|
||||||
|
|
||||||
|
tool.Geometry.reload_representation(tool.Ifc.get_object(element))
|
||||||
|
|
||||||
def invert_door_swing(self, element):
|
def invert_door_swing(self, element):
|
||||||
obj = tool.Ifc.get_object(element)
|
obj = tool.Ifc.get_object(element)
|
||||||
|
|
||||||
@@ -758,6 +772,12 @@ class ChangeSwingDirection(bpy.types.Operator, tool.Ifc.Operator):
|
|||||||
|
|
||||||
tool.Model.mark_thumbnail_for_update(element)
|
tool.Model.mark_thumbnail_for_update(element)
|
||||||
|
|
||||||
|
def invert_representation(self, element):
|
||||||
|
if ifcopenshell.util.element.get_pset(element, "BBIM_Door", "Data"):
|
||||||
|
self.invert_door_swing(element)
|
||||||
|
else:
|
||||||
|
self.invert_general_object(element)
|
||||||
|
|
||||||
def get_inverted_type(self, type_element):
|
def get_inverted_type(self, type_element):
|
||||||
inverted_pset = ifcopenshell.util.element.get_pset(type_element, "BBIM_InvertedSwingType", "Data")
|
inverted_pset = ifcopenshell.util.element.get_pset(type_element, "BBIM_InvertedSwingType", "Data")
|
||||||
if not inverted_pset:
|
if not inverted_pset:
|
||||||
@@ -775,17 +795,17 @@ class ChangeSwingDirection(bpy.types.Operator, tool.Ifc.Operator):
|
|||||||
|
|
||||||
ifcopenshell.api.pset.edit_pset(tool.Ifc.get(), inverted_pset, "BBIM_InvertedSwingType", { "Data": json.dumps({ "inverted_swing_type": inverted_type_element.id() }) })
|
ifcopenshell.api.pset.edit_pset(tool.Ifc.get(), inverted_pset, "BBIM_InvertedSwingType", { "Data": json.dumps({ "inverted_swing_type": inverted_type_element.id() }) })
|
||||||
|
|
||||||
def assign_inverted_door_type(self, element):
|
def assign_inverted_type(self, element):
|
||||||
type_element = ifcopenshell.util.element.get_type(element)
|
type_element = ifcopenshell.util.element.get_type(element)
|
||||||
|
|
||||||
inverted_type = self.get_inverted_type(type_element)
|
inverted_type = self.get_inverted_type(type_element)
|
||||||
if not inverted_type:
|
if not inverted_type:
|
||||||
old_to_new, _ = tool.Geometry.duplicate_ifc_objects([ tool.Ifc.get_object(type_element) ])
|
old_to_new, _ = tool.Geometry.duplicate_ifc_objects([ tool.Ifc.get_object(type_element) ])
|
||||||
inverted_type = old_to_new[type_element][0]
|
inverted_type = old_to_new[type_element][0]
|
||||||
self.invert_door_swing(inverted_type)
|
self.invert_representation(inverted_type)
|
||||||
self.set_inverted_type(inverted_type, type_element)
|
self.set_inverted_type(inverted_type, type_element)
|
||||||
self.set_inverted_type(type_element, inverted_type)
|
self.set_inverted_type(type_element, inverted_type)
|
||||||
|
|
||||||
bonsai.core.type.assign_type(tool.Ifc, tool.Type, element, inverted_type)
|
bonsai.core.type.assign_type(tool.Ifc, tool.Type, element, inverted_type)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1296,7 +1296,7 @@ class Hotkey(bpy.types.Operator, tool.Ifc.Operator):
|
|||||||
def hotkey_S_C_F(self):
|
def hotkey_S_C_F(self):
|
||||||
if not bpy.context.selected_objects:
|
if not bpy.context.selected_objects:
|
||||||
return
|
return
|
||||||
bpy.ops.bim.change_swing_direction()
|
bpy.ops.bim.mirror_geometry()
|
||||||
|
|
||||||
def hotkey_S_G(self):
|
def hotkey_S_G(self):
|
||||||
obj = bpy.context.active_object
|
obj = bpy.context.active_object
|
||||||
|
|||||||
Reference in New Issue
Block a user