mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-08-09 17:31:45 +00:00
bim.flip_object #3803
Added new operator bim.flip_object that flips the object by flipping two of its axes and keeps the original placement. Currently it can be found either from F3 search or from Shift-F hotkey for IfcBeam and IfcColumn in BIM tools.
This commit is contained in:
@@ -23,6 +23,7 @@ classes = (
|
||||
operator.AddRepresentation,
|
||||
operator.CopyRepresentation,
|
||||
operator.EditObjectPlacement,
|
||||
operator.FlipObject,
|
||||
operator.GetRepresentationIfcParameters,
|
||||
operator.OverrideDelete,
|
||||
operator.OverrideDuplicateMove,
|
||||
|
||||
@@ -1723,3 +1723,19 @@ class OverrideModeSetObject(bpy.types.Operator):
|
||||
if self.edited_objs:
|
||||
return context.window_manager.invoke_props_dialog(self)
|
||||
return self.execute(context)
|
||||
|
||||
|
||||
class FlipObject(bpy.types.Operator):
|
||||
bl_idname = "bim.flip_object"
|
||||
bl_label = "Flip Object"
|
||||
bl_description = "Flip object's local axes, keep the position"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
|
||||
flip_local_axes: bpy.props.EnumProperty(
|
||||
name="Flip Local Axes", items=(("XY", "XY", ""), ("YZ", "YZ", ""), ("XZ", "XZ", "")), default="XY"
|
||||
)
|
||||
|
||||
def execute(self, context):
|
||||
for obj in context.selected_objects:
|
||||
tool.Geometry.flip_object(obj, self.flip_local_axes)
|
||||
return {"FINISHED"}
|
||||
|
||||
@@ -393,18 +393,7 @@ class FlipFill(bpy.types.Operator, tool.Ifc.Operator):
|
||||
element = tool.Ifc.get_entity(obj)
|
||||
if not element or not element.FillsVoids:
|
||||
continue
|
||||
|
||||
flip_matrix = Matrix.Rotation(pi, 4, "Z")
|
||||
|
||||
bottom_left = obj.matrix_world @ Vector(obj.bound_box[0])
|
||||
top_right = obj.matrix_world @ Vector(obj.bound_box[6])
|
||||
center = obj.matrix_world.translation.copy()
|
||||
center_offset = center - bottom_left
|
||||
flipped_center = top_right - center_offset
|
||||
|
||||
obj.matrix_world = obj.matrix_world @ flip_matrix
|
||||
obj.matrix_world.translation.xy = flipped_center.xy
|
||||
bpy.context.view_layer.update()
|
||||
tool.Geometry.flip_object(obj, "XY")
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
|
||||
@@ -358,6 +358,7 @@ class BimToolUI:
|
||||
cls.layout.operator("bim.mep_connect_elements")
|
||||
else:
|
||||
add_layout_hotkey_operator(cls.layout, "Edit Axis", "A_E", "")
|
||||
add_layout_hotkey_operator(cls.layout, "Flip", "S_F", bpy.ops.bim.flip_object.__doc__)
|
||||
add_layout_hotkey_operator(cls.layout, "Butt", "S_T", "")
|
||||
add_layout_hotkey_operator(cls.layout, "Mitre", "S_Y", "")
|
||||
add_layout_hotkey_operator(cls.layout, "Rotate 90", "S_R", bpy.ops.bim.rotate_90.__doc__)
|
||||
@@ -668,6 +669,8 @@ class Hotkey(bpy.types.Operator, tool.Ifc.Operator):
|
||||
bpy.ops.bim.flip_wall()
|
||||
elif self.active_class in ("IfcWindow", "IfcWindowStandardCase", "IfcDoor", "IfcDoorStandardCase"):
|
||||
bpy.ops.bim.flip_fill()
|
||||
elif self.active_class in ("IfcBeam", "IfcColumn"):
|
||||
bpy.ops.bim.flip_object(flip_local_axes="XZ")
|
||||
elif self.active_class in ("IfcDuctSegment", "IfcPipeSegment", "IfcCableCarrierSegment", "IfcCableSegment"):
|
||||
bpy.ops.bim.fit_flow_segments()
|
||||
|
||||
|
||||
@@ -343,6 +343,8 @@ class Blender:
|
||||
"max_y": bound_box[6][1],
|
||||
"min_z": bound_box[0][2],
|
||||
"max_z": bound_box[6][2],
|
||||
"min_point": Vector(bound_box[0]),
|
||||
"max_point": Vector(bound_box[6]),
|
||||
"center": (Vector(bound_box[6]) + Vector(bound_box[0])) / 2,
|
||||
}
|
||||
return bbox_dict
|
||||
|
||||
@@ -28,7 +28,7 @@ import blenderbim.core.style
|
||||
import blenderbim.core.spatial
|
||||
import blenderbim.tool as tool
|
||||
import blenderbim.bim.import_ifc
|
||||
from math import radians
|
||||
from math import radians, pi
|
||||
from mathutils import Vector, Matrix
|
||||
from blenderbim.bim.ifc import IfcStore
|
||||
|
||||
@@ -641,3 +641,25 @@ class Geometry(blenderbim.core.tool.Geometry):
|
||||
@classmethod
|
||||
def get_model_representations(cls):
|
||||
return tool.Ifc.get().by_type("IfcShapeRepresentation")
|
||||
|
||||
@classmethod
|
||||
def flip_object(cls, obj, flip_local_axes):
|
||||
assert len(flip_local_axes) == 2, "flip_local_axes must be two axes to flip"
|
||||
rotation_axis = next(i for i in "XYZ" if i not in flip_local_axes)
|
||||
rotation_axis_i = "XYZ".index(rotation_axis)
|
||||
|
||||
bb_data = tool.Blender.get_object_bounding_box(obj)
|
||||
# min max points of rotated plane of origin based bounding box
|
||||
min_point = Vector([min(i, 0) for i in bb_data["min_point"]])
|
||||
max_point = Vector([max(i, 0) for i in bb_data["max_point"]])
|
||||
# keep it in rotated plane only
|
||||
max_point[rotation_axis_i] = min_point[rotation_axis_i]
|
||||
|
||||
# to compensate for flipped two axes
|
||||
# we adjust new max point to match previous min point (or vice versa)
|
||||
original_min_point = obj.matrix_world @ min_point
|
||||
obj.matrix_world = obj.matrix_world @ Matrix.Rotation(pi, 4, rotation_axis)
|
||||
new_max_point = obj.matrix_world @ max_point
|
||||
obj.matrix_world.translation += original_min_point - new_max_point
|
||||
|
||||
bpy.context.view_layer.update()
|
||||
|
||||
Reference in New Issue
Block a user