import bpy, gpu, mathutils, math
from bpy.types import Panel, Gizmo, GizmoGroup, Operator
from mathutils import Matrix, Vector, Euler
from math import radians
from bl_ui.space_view3d import VIEW3D_HT_header
from bl_ui.space_toolsystem_toolbar import VIEW3D_PT_tools_active as view3d_tools
from bl_ui.space_statusbar import STATUSBAR_HT_header
from ... utility import addon
from ... utility import modifier
from ... utils.toggle_view3d_panels import collapse_3D_view_panels
from ... ui_framework.master import Master
from ... ui_framework.utils.mods_list import get_mods_list
from ... utility.base_modal_controls import Base_Modal_Controls
# Tracking props for UI
creating_empty = False
class HOPS_OT_MirrorExecuteFinal(Operator):
bl_idname = "hops.mirror_exit"
bl_label = "Mirror finish"
bl_description = "Mirror Finish"
bl_options = {'REGISTER', 'UNDO', 'INTERNAL'}
def execute(self, context):
preference = addon.preference()
preference.operator.mirror.running = False
return {'FINISHED'}
class HOPS_OT_MirrorOperator():
def invoke(self, context, event):
self.execute(context)
return {'FINISHED'}
def execute(self, context):
self.draw_wire = True # Display wire mesh
self.flip_obj = None # For wire fade on flip
preference = addon.preference()
self.active = context.active_object
selected = context.selected_objects
self.edit_mode = False
if context.mode == 'EDIT_MESH':
self.edit_mode = True
# FLIP
if preference.operator.mirror.mode in {"FLIP"}:
if len(selected) > 1:
if not preference.operator.mirror.include_active:
# Draw wire fade for object being flipped
objs = [o for o in context.selected_objects if o.type == 'MESH' and o != self.active]
if len(objs) > 0:
self.flip_obj = objs[0]
self.active.select_set(False)
else:
# Draw wire for active
if self.active and self.active.type == 'MESH':
self.flip_obj = self.active
self.flip(context)
self.finish(context)
# BISECT
if preference.operator.mirror.mode in {"BISECTMOD", "BISECT"}:
if self.active.type in {"MESH"}:
if all([obj.type in {"MESH"} for obj in selected]):
fill = False
if preference.operator.mirror.mode == "BISECT":
fill = True
self.bisect(context, fill)
# MODIFIER
if preference.operator.mirror.mode in {"MODIFIER", "NEWMODIFIER", "BISECTMOD", "MODIFIERAPPLY"}:
new = False
if preference.operator.mirror.mode in {"NEWMODIFIER", "MODIFIERAPPLY"}:
new = True
mirror_object = self.active
if preference.operator.mirror.orientation in {"GLOBAL", "VIEW", "CURSOR"} or preference.operator.mirror.pivot in {"MEDIAN", "CURSOR"}:
if preference.operator.mirror.advanced:
empty = self.create_empty(context)
mirror_object = empty
if preference.operator.mirror.orientation == "LOCAL" and preference.operator.mirror.pivot in {"INDIVIDUAL"}:
mirror_object = None
for obj in selected:
if obj.type in {"MESH", "CURVE", "GPENCIL", "GREASEPENCIL"}:
if obj == self.active:
if preference.operator.mirror.advanced:
if preference.operator.mirror.orientation == "LOCAL" and preference.operator.mirror.pivot in {"ACTIVE", "INDIVIDUAL"}:
if len(selected) == 1 or preference.operator.mirror.include_active:
self.mirror_mod(context, selected, obj, *self.data(), new=new)
else:
if (len(selected) == 1 or preference.operator.mirror.include_active) or preference.operator.mirror.pivot != 'ACTIVE':
self.mirror_mod(context, selected, obj, *self.data(), mirror_object=mirror_object, new=new)
else:
if len(selected) == 1 or preference.operator.mirror.include_active:
self.mirror_mod(context, selected, obj, *self.data(), new=new)
else:
self.mirror_mod(context, selected, obj, *self.data(), mirror_object=mirror_object, new=new)
if obj.type == "MESH" and not preference.operator.mirror.mode in {"SYMMETRY", "MODIFIERAPPLY"}:
modifier.sort(obj, types=['WEIGHTED_NORMAL'], last=True)
# SYMMETRY
if preference.operator.mirror.mode in {"SYMMETRY"}:
if self.active.type in {"MESH"}:
self.symmetry(context)
if preference.operator.mirror.advanced:
if preference.operator.mirror.orientation in {"GLOBAL", "VIEW", "CURSOR"} or preference.operator.mirror.pivot in {"MEDIAN", "CURSOR"}:
if preference.operator.mirror.mode == 'MODIFIERAPPLY':
bpy.data.objects.remove(empty, do_unlink=True)
self.finish(context)
def finish(self, context):
preference = addon.preference()
if preference.operator.mirror.close is True:
preference.operator.mirror.running = False
if preference.ui.Hops_extra_draw:
if self.draw_wire:
if self.flip_obj:
bpy.ops.hops.draw_wire_mesh_launcher('INVOKE_DEFAULT', object_name=self.flip_obj.name)
else:
if len(bpy.context.selected_objects) == 1:
bpy.ops.hops.draw_wire_mesh_launcher('INVOKE_DEFAULT', object_name=self.active.name)
return {'FINISHED'}
def xform(self, context):
preference = addon.preference()
active = context.active_object
selected = context.selected_objects
matrix_world = active.matrix_world.copy()
obloc, obrot, obscale = matrix_world.decompose()
if preference.operator.mirror.orientation == "GLOBAL":
rot = Euler((0, 0, 0), "XYZ")
elif preference.operator.mirror.orientation == "CURSOR":
rot = context.scene.cursor.rotation_euler
elif preference.operator.mirror.orientation == "LOCAL":
rot = obrot.to_euler()
elif preference.operator.mirror.orientation == "VIEW":
rot = context.region_data.view_rotation.to_euler()
if preference.operator.mirror.pivot in {"ACTIVE", "INDIVIDUAL"}:
loc = obloc
elif preference.operator.mirror.pivot == "CURSOR":
loc = context.scene.cursor.location
elif preference.operator.mirror.pivot == "MEDIAN":
if len(selected) > 0:
selected_loc = [obj.matrix_world.translation for obj in selected]
loc = sum(selected_loc, Vector()) / len(selected)
else:
loc = obloc
if preference.operator.mirror.mode == 'SYMMETRY' or not preference.operator.mirror.advanced:
rot, loc = obrot, obloc
return loc, rot
def symmetry(self, context):
preference = addon.preference()
normal = self.data()[0]
if normal == Vector((1, 0, 0)):
direction = "NEGATIVE_X"
elif normal == Vector((-1, 0, 0)):
direction = "POSITIVE_X"
elif normal == Vector((0, 1, 0)):
direction = "NEGATIVE_Y"
elif normal == Vector((0, -1, 0)):
direction = "POSITIVE_Y"
elif normal == Vector((0, 0, 1)):
direction = "NEGATIVE_Z"
elif normal == Vector((0, 0, -1)):
direction = "POSITIVE_Z"
mode = context.mode
if not self.edit_mode:
bpy.ops.object.mode_set(mode='EDIT')
if not self.edit_mode or not preference.operator.mirror.symmetrize_selected:
bpy.ops.mesh.select_all(action='SELECT')
bpy.ops.mesh.symmetrize(direction=direction, threshold=preference.operator.mirror.merge_threshold)
bpy.ops.mesh.select_all(action='DESELECT')
if not self.edit_mode:
bpy.ops.object.mode_set(mode=mode)
def flip(self, context):
preference = addon.preference()
normal = self.data()[0]
if normal == Vector((1, 0, 0)):
constraint = (True, False, False)
elif normal == Vector((-1, 0, 0)):
constraint = (True, False, False)
elif normal == Vector((0, 1, 0)):
constraint = (False, True, False)
elif normal == Vector((0, -1, 0)):
constraint = (False, True, False)
elif normal == Vector((0, 0, 1)):
constraint = (False, False, True)
elif normal == Vector((0, 0, -1)):
constraint = (False, False, True)
saved_pivot = context.scene.tool_settings.transform_pivot_point
if preference.operator.mirror.pivot == 'ACTIVE':
context.scene.tool_settings.transform_pivot_point = 'ACTIVE_ELEMENT'
elif preference.operator.mirror.pivot == 'MEDIAN':
context.scene.tool_settings.transform_pivot_point = 'MEDIAN_POINT'
elif preference.operator.mirror.pivot == 'CURSOR':
context.scene.tool_settings.transform_pivot_point = 'CURSOR'
elif preference.operator.mirror.pivot == 'INDIVIDUAL':
context.scene.tool_settings.transform_pivot_point = 'INDIVIDUAL_ORIGINS'
bpy.ops.transform.mirror(orient_type=preference.operator.mirror.orientation, orient_matrix_type=preference.operator.mirror.orientation, constraint_axis=constraint)
context.scene.tool_settings.transform_pivot_point = saved_pivot
def bisect(self, context, fill=False):
preference = addon.preference()
loc, rot = self.xform(context)
position = loc
normal = self.data()[0]
normal.rotate(rot)
if not self.edit_mode:
if not preference.operator.mirror.include_active:
self.active.select_set(False)
for obj in context.selected_objects:
context.view_layer.objects.active = obj
bpy.ops.object.mode_set(mode='EDIT')
bpy.ops.mesh.select_all(action='SELECT')
bpy.ops.mesh.bisect(plane_co=(position.x, position.y, position.z), plane_no=(normal.x, normal.y, normal.z), use_fill=fill, clear_inner=False, clear_outer=True)
obj = context.active_object
bpy.ops.mesh.select_all(action='DESELECT')
if not self.edit_mode:
bpy.ops.object.mode_set(mode='OBJECT')
if addon.preference().ui.Hops_extra_draw:
if obj:
self.draw_wire = False
mods = [mod for mod in obj.modifiers if mod.type == 'MIRROR']
activemod = [mod for mod in mods if mod.name == addon.preference().operator.mirror.modifier]
mod = None
if len(activemod) > 0:
mod = activemod[0]
if mod != None:
mod.show_viewport = False
bpy.ops.hops.draw_wire_mesh_launcher('INVOKE_DEFAULT', object_name=obj.name)
if mod != None:
mod.show_viewport = True
if not preference.operator.mirror.include_active:
context.view_layer.objects.active = self.active
self.active.select_set(True)
def create_empty(self, context):
preference = addon.preference()
active = context.active_object
add_empty = False
if preference.operator.mirror.mode == "NEWMODIFIER" and preference.operator.mirror.mode != "MODIFIERAPPLY":
add_empty = True
bpy.ops.hops.display_notification(info="Empty Created")
else:
mods = [mod for mod in active.modifiers if mod.type == "MIRROR"]
if len(mods) > 0:
activemod = [mod for mod in mods if mod.name == preference.operator.mirror.modifier]
if activemod:
mod = activemod[0]
else:
mod = [mod for mod in mods][-1]
if mod.mirror_object:
empty = mod.mirror_object
bpy.data.objects.remove(empty)
del empty
add_empty = True
if add_empty is True:
empty = bpy.data.objects.new("empty", None)
if preference.operator.mirror.mode != "MODIFIERAPPLY":
bpy.ops.hops.display_notification(info="Empty Created")
context.collection.objects.link(empty)
loc, rot = self.xform(context)
empty.rotation_euler = rot
empty.location = loc
if preference.operator.mirror.parent_empty:
empty.parent = active
empty.matrix_parent_inverse = active.matrix_world.inverted()
return empty
def mirror_mod(self, context, selected, object, normal, axisv, axism, axisx, axisy, axisz, mirror_object=None, new=False):
preference = addon.preference()
# GPENCIL
if object.type in {"GPENCIL", "GREASEPENCIL"}:
if object.type == 'GPENCIL':
mtype = "GP_MIRROR"
mods_s = 'grease_pencil_modifiers'
else:
mtype = "GREASE_PENCIL_MIRROR"
mods_s = 'modifiers'
if new:
mod = getattr(object, mods_s).new('Hops_Mirror', mtype)
bpy.ops.hops.display_notification(info="Modifier Added")
else:
mods = [mod for mod in getattr(object, mods_s) if mod.type == mtype]
if len(mods) > 0:
activemod = [mod for mod in mods if mod.name == preference.operator.mirror.modifier]
mod = activemod[0]
else:
new = True
mod = getattr(object, mods_s).new('Hops_Mirror', mtype)
if new:
if bpy.app.version > (2, 90, 0):
mod.use_axis_x = axisx[0]
mod.use_axis_y = axisy[0]
mod.use_axis_z = axisz[0]
else:
mod.x_axis = axisx[0]
mod.y_axis = axisy[0]
mod.z_axis = axisz[0]
else:
if bpy.app.version > (2, 90, 0):
if normal == Vector((1, 0, 0)) or normal == Vector((-1, 0, 0)):
if mod.use_axis_x is False:
mod.use_axis_x = True
else:
mod.use_axis_x = False
elif normal == Vector((0, 1, 0)) or normal == Vector((0, -1, 0)):
if mod.use_axis_y is False:
mod.use_axis_y = True
else:
mod.use_axis_y = False
elif normal == Vector((0, 0, 1)) or normal == Vector((0, 0, -1)):
if mod.use_axis_z is False:
mod.use_axis_z = True
else:
mod.use_axis_z = False
else:
if normal == Vector((1, 0, 0)) or normal == Vector((-1, 0, 0)):
if mod.x_axis is False:
mod.x_axis = True
else:
mod.x_axis = False
elif normal == Vector((0, 1, 0)) or normal == Vector((0, -1, 0)):
if mod.y_axis is False:
mod.y_axis = True
else:
mod.y_axis = False
elif normal == Vector((0, 0, 1)) or normal == Vector((0, 0, -1)):
if mod.z_axis is False:
mod.z_axis = True
else:
mod.z_axis = False
if mirror_object:
mod.object = mirror_object
# MESH CURVE
elif object.type in {"MESH", "CURVE"}:
if new:
mod = object.modifiers.new('Hops_Mirror', 'MIRROR')
else:
mods = [mod for mod in object.modifiers if mod.type == 'MIRROR']
if len(mods) > 0:
activemod = [mod for mod in mods if mod.name == preference.operator.mirror.modifier]
if len(activemod) > 0:
mod = activemod[0]
else:
mod = mods[-1]
else:
new = True
mod = object.modifiers.new('Hops_Mirror', 'MIRROR')
if new:
mod.use_axis[0], mod.use_bisect_axis[0], mod.use_bisect_flip_axis[0] = axisx
mod.use_axis[1], mod.use_bisect_axis[1], mod.use_bisect_flip_axis[1] = axisy
mod.use_axis[2], mod.use_bisect_axis[2], mod.use_bisect_flip_axis[2] = axisz
else:
if len(selected) == 1 or 2:
if mod.use_axis[axisv] is False:
mod.use_axis[axisv], mod.use_bisect_axis[axisv], mod.use_bisect_flip_axis[axisv] = axism
else:
mod.use_axis[axisv], mod.use_bisect_axis[axisv], mod.use_bisect_flip_axis[axisv] = (False, False, False)
else:
mod.use_axis[axisv], mod.use_bisect_axis[axisv], mod.use_bisect_flip_axis[axisv] = axism
mod.use_clip = True
if mirror_object:
mod.mirror_object = mirror_object
mod.use_mirror_u = preference.operator.mirror.mirror_u
mod.use_mirror_v = preference.operator.mirror.mirror_v
mod.merge_threshold = preference.operator.mirror.merge_threshold
mod.bisect_threshold = preference.operator.mirror.bisect_threshold
if preference.operator.mirror.mode == "MODIFIERAPPLY":
if object.type in {"MESH"}:
if self.edit_mode:
bpy.ops.object.mode_set(mode='OBJECT')
# Preserve blenders symetry
b3d_sym_x = context.object.use_mesh_mirror_x
b3d_sym_y = context.object.use_mesh_mirror_y
b3d_sym_z = context.object.use_mesh_mirror_z
modifier.apply(object, modifiers=[mod])
if context.object.type == 'MESH':
context.object.use_mesh_mirror_x = b3d_sym_x
context.object.use_mesh_mirror_y = b3d_sym_y
context.object.use_mesh_mirror_z = b3d_sym_z
if self.edit_mode:
bpy.ops.object.mode_set(mode='EDIT')
def data(self):
value = 0
x = (False, False, False)
y = (False, False, False)
z = (False, False, False)
main = x
normal = Vector((1, 0, 0))
return normal, value, main, x, y, z
class HOPS_OT_MirrorExecuteXGizmo(Operator, HOPS_OT_MirrorOperator):
bl_idname = "hops.mirror_execute_x_gizmo"
bl_label = "Mirror -X axis"
bl_description = "Mirror via -X"
bl_options = {'REGISTER', 'UNDO', 'INTERNAL'}
def data(self):
preference = addon.preference()
value = 0
x = (True, True, True)
y = (False, False, False)
z = (False, False, False)
normal = Vector((1, 0, 0))
if preference.operator.mirror.revert:
x = (True, True, False)
normal = Vector((-1, 0, 0))
main = x
return normal, value, main, x, y, z
class HOPS_OT_MirrorExecuteXmGizmo(Operator, HOPS_OT_MirrorOperator):
bl_idname = "hops.mirror_execute_xm_gizmo"
bl_label = "Mirror X axis"
bl_description = "Mirror via X"
bl_options = {'REGISTER', 'UNDO', 'INTERNAL'}
def data(self):
preference = addon.preference()
value = 0
x = (True, True, False)
y = (False, False, False)
z = (False, False, False)
normal = Vector((-1, 0, 0))
if preference.operator.mirror.revert:
x = (True, True, True)
normal = Vector((1, 0, 0))
main = x
return normal, value, main, x, y, z
class HOPS_OT_MirrorExecuteYGizmo(Operator, HOPS_OT_MirrorOperator):
bl_idname = "hops.mirror_execute_y_gizmo"
bl_label = "Mirror -Y axis"
bl_description = "Mirror via -Y"
bl_options = {'REGISTER', 'UNDO', 'INTERNAL'}
def data(self):
preference = addon.preference()
value = 1
x = (False, False, False)
y = (True, True, True)
z = (False, False, False)
normal = Vector((0, 1, 0))
if preference.operator.mirror.revert:
y = (True, True, False)
normal = Vector((0, -1, 0))
main = y
return normal, value, main, x, y, z
class HOPS_OT_MirrorExecuteYmGizmo(Operator, HOPS_OT_MirrorOperator):
bl_idname = "hops.mirror_execute_ym_gizmo"
bl_label = "Mirror Y axis"
bl_description = "Mirror via Y"
bl_options = {'REGISTER', 'UNDO', 'INTERNAL'}
def data(self):
preference = addon.preference()
value = 1
x = (False, False, False)
y = (True, True, False)
z = (False, False, False)
normal = Vector((0, -1, 0))
if preference.operator.mirror.revert:
y = (True, True, True)
normal = Vector((0, 1, 0))
main = y
return normal, value, main, x, y, z
class HOPS_OT_MirrorExecuteZGizmo(Operator, HOPS_OT_MirrorOperator):
bl_idname = "hops.mirror_execute_z_gizmo"
bl_label = "Mirror -Z axis"
bl_description = "Mirror via -Z"
bl_options = {'REGISTER', 'UNDO', 'INTERNAL'}
def data(self):
preference = addon.preference()
value = 2
x = (False, False, False)
y = (False, False, False)
z = (True, True, True)
normal = Vector((0, 0, 1))
if preference.operator.mirror.revert:
z = (True, True, False)
normal = Vector((0, 0, -1))
main = z
return normal, value, main, x, y, z
class HOPS_OT_MirrorExecuteZmGizmo(Operator, HOPS_OT_MirrorOperator):
bl_idname = "hops.mirror_execute_zm_gizmo"
bl_label = "Mirror Z axis"
bl_description = "Mirror via Z"
bl_options = {'REGISTER', 'UNDO', 'INTERNAL'}
def data(self):
preference = addon.preference()
value = 2
x = (False, False, False)
y = (False, False, False)
z = (True, True, False)
normal = Vector((0, 0, -1))
if preference.operator.mirror.revert:
z = (True, True, True)
normal = Vector((0, 0, 1))
main = z
return normal, value, main, x, y, z
class HOPS_OT_MirrorGizmo(Operator):
bl_idname = "hops.mirror_gizmo"
bl_label = "Mirror Gizmo"
bl_options = {"REGISTER"}
bl_description = """Interactive Mirror Gizmo - Mir3
Active tool. UI up top of 3d view for adjustment.
D - Menu for Adjustment
A - Add additional mirror
X - Reset To Default
"""
other_gizmos = False
close = False
@classmethod
def poll(cls, context):
ob = context.active_object
return getattr(ob, "type", "") in {"MESH", "EMPTY", "CURVE", "GPENCIL", "GREASEPENCIL"}
def invoke(self, context, event):
self.preference = addon.preference()
self.preference.property.toolshelf = 'MIRROR'
active = context.active_object
if len(context.selected_objects) == 2:
others = [obj for obj in context.selected_objects if obj != context.active_object]
active = others[0]
self.display_msg = self.preference.operator.mirror.mode
bpy.ops.hops.display_notification(info=self.display_msg)
mods = [mod for mod in active.modifiers if mod.type == "MIRROR"]
if len(mods) > 0:
self.preference.operator.mirror.modifier = mods[-1].name
bpy.context.space_data.show_gizmo_tool = True
bpy.context.space_data.show_gizmo = True
if bpy.context.space_data.show_gizmo_context:
self.other_gizmos = True
bpy.context.space_data.show_gizmo_context = False
current_tool = view3d_tools.tool_active_from_context(bpy.context).idname
self.current_tool = current_tool
if context.space_data.type == 'VIEW_3D':
wm = context.window_manager
wm.gizmo_group_type_ensure(HOPS_OT_MirrorGizmoGroup.bl_idname)
if context.mode == 'OBJECT':
bpy.ops.wm.tool_set_by_id(name="Hops", space_type='VIEW_3D')
elif context.mode == 'EDIT_MESH':
bpy.ops.wm.tool_set_by_id(name="Hopsedit", space_type='VIEW_3D')
self.preference.operator.mirror.running = True
if context.space_data.type == 'VIEW_3D':
context.area.tag_redraw()
VIEW3D_HT_header._orgin_draw = VIEW3D_HT_header.draw
VIEW3D_HT_header.draw = draw_header
STATUSBAR_HT_header.draw = infobar
context.window_manager.modal_handler_add(self)
self.master = Master(context=context)
self.master.only_use_fast_ui = True
self.original_tool_shelf, self.original_n_panel = collapse_3D_view_panels()
self.base_controls = Base_Modal_Controls(context, event)
self.base_controls.confirm_events.remove('LEFTMOUSE')
self.base_controls.pass_through_events.append('MOUSEMOVE')
self.base_controls.pass_through_events.append('LEFTMOUSE')
return {"RUNNING_MODAL"}
def modal(self, context, event):
self.master.receive_event(event=event)
self.base_controls.update(context, event)
if self.display_msg != self.preference.operator.mirror.mode:
self.display_msg = self.preference.operator.mirror.mode
bpy.ops.hops.display_notification(info=self.display_msg)
within_region_3d = False
regions = get_3d_viewport_regions(context)
if regions:
for region in regions:
if is_mouse_in_region(event, region):
within_region_3d = is_mouse_in_region(event, region)
if within_region_3d:
if event.shift:
if self.close is False:
if self.preference.operator.mirror.close is True:
self.preference.operator.mirror.close = False
self.close = True
context.area.tag_redraw()
if self.base_controls.cancel:
self.preference.operator.mirror.close = True
else:
if self.close:
self.preference.operator.mirror.close = True
self.close = False
context.area.tag_redraw()
if self.preference.operator.mirror.running is False or self.base_controls.confirm or self.base_controls.cancel:
self.preference.property.toolshelf = 'HOPSTOOL'
bpy.ops.wm.tool_set_by_id(name=self.current_tool, space_type='VIEW_3D')
context.window_manager.gizmo_group_type_unlink_delayed(HOPS_OT_MirrorGizmoGroup.bl_idname)
VIEW3D_HT_header.draw = VIEW3D_HT_header._orgin_draw
STATUSBAR_HT_header.draw = STATUSBAR_HT_header._draw_orig
if self.other_gizmos:
bpy.context.space_data.show_gizmo_context = True
# bpy.ops.hops.display_notification(info="Closed")
collapse_3D_view_panels(self.original_tool_shelf, self.original_n_panel)
self.master.run_fade()
context.area.tag_redraw()
return {"FINISHED"}
if event.type == "TAB":
if event.value == "PRESS":
self.preference.operator.mirror.advanced = not self.preference.operator.mirror.advanced
bpy.ops.hops.display_notification(info="Advanced" if self.preference.operator.mirror.advanced else "Simple")
if event.type == "D":
if event.value == "PRESS":
bpy.ops.wm.call_menu(name=HOPS_MT_MirrorMenu.bl_idname)
if event.type == "A":
if event.value == "PRESS":
self.preference.operator.mirror.mode = 'NEWMODIFIER' if self.preference.operator.mirror.mode == 'MODIFIER' else 'MODIFIER'
bpy.ops.hops.display_notification(info=self.preference.operator.mirror.mode)
if event.type == "V":
if event.value == "PRESS":
self.preference.operator.mirror.advanced = True
self.preference.operator.mirror.orientation = "VIEW"
bpy.ops.hops.display_notification(info=self.preference.operator.mirror.orientation)
if event.type == "F":
if event.value == "PRESS":
self.preference.operator.mirror.mode = "FLIP"
bpy.ops.hops.display_notification(info=self.preference.operator.mirror.orientation)
if self.preference.operator.mirror.advanced:
if event.type == "W":
if event.value == "PRESS":
orientation = ["LOCAL", "GLOBAL", "VIEW", "CURSOR"]
self.preference.operator.mirror.orientation = orientation[(orientation.index(self.preference.operator.mirror.orientation) + 1) % len(orientation)]
bpy.ops.hops.display_notification(info=self.preference.operator.mirror.orientation)
if event.type == "S":
if event.value == "PRESS":
pivot = ["ACTIVE", "MEDIAN", "CURSOR", "INDIVIDUAL"]
self.preference.operator.mirror.pivot = pivot[(pivot.index(self.preference.operator.mirror.pivot) + 1) % len(pivot)]
bpy.ops.hops.display_notification(info= self.preference.operator.mirror.pivot)
if event.type == "X":
if event.value == "PRESS":
self.preference.operator.mirror.mode = "MODIFIER"
self.preference.operator.mirror.orientation = "LOCAL"
self.preference.operator.mirror.pivot = "ACTIVE"
#bpy.ops.hops.display_notification(info="Reset To Default")
bpy.ops.hops.display_notification(info=self.preference.operator.mirror.mode)
if event.type == "H":
if event.value == "PRESS":
addon.preference().property.hops_modal_help = not addon.preference().property.hops_modal_help
if event.type in {'WHEELUPMOUSE', 'WHEELDOWNMOUSE'}:
if event.ctrl or event.alt:
if event.ctrl:
mode = ["MODIFIER", "MODIFIERAPPLY", "BISECTMOD"]
if event.alt:
mode = ["MODIFIER", "NEWMODIFIER", "MODIFIERAPPLY", "BISECT", 'BISECTMOD', 'SYMMETRY', 'FLIP']
option = self.preference.operator.mirror.mode if self.preference.operator.mirror.mode in mode else "MODIFIER"
if event.type == 'WHEELUPMOUSE':
self.preference.operator.mirror.mode = mode[(mode.index(option) + 1) % len(mode)]
else:
self.preference.operator.mirror.mode = mode[(mode.index(option) - 1) % len(mode)]
bpy.ops.hops.display_notification(info=self.preference.operator.mirror.mode)
else:
return {"PASS_THROUGH"}
if self.base_controls.pass_through:
return {'PASS_THROUGH'}
self.draw_ui(context=context)
context.area.tag_redraw()
return {"RUNNING_MODAL"}
def draw_ui(self, context):
# Start
self.master.setup()
if self.master.should_build_fast_ui():
# Help
preference = addon.preference()
help_items = {"GLOBAL" : [], "STANDARD" : []}
help_items["GLOBAL"] = [
("M", "Toggle mods list"),
("H", "Toggle help")
#("~", "Toggle UI Display Type"),
#("O", "Toggle viewport rendering")
]
#Empty Status
if self.preference.operator.mirror.orientation != "LOCAL" or self.preference.operator.mirror.pivot != "ACTIVE":
help_items["STANDARD"].append([" ", "X to Reset"])
help_items["STANDARD"].append([" ", " "])
if preference.operator.mirror.include_active and len(context.selected_objects) >= 2:
help_items["STANDARD"].append([" ", "Active selection included"])
if creating_empty == True and preference.operator.mirror.mode != "FLIP":
help_items["STANDARD"].append([" ", "Empty will be created (X)"])
else:
help_items["STANDARD"].append([" ", "No Empty Will be created"])
help_items["STANDARD"].append([" ", " "])
if self.preference.operator.mirror.advanced:
#Pivot
#pivot = ["ACTIVE", "MEDIAN", "CURSOR", "INDIVIDUAL"]
if self.preference.operator.mirror.pivot == "ACTIVE":
help_items["STANDARD"].append([" ", "Default - Local Origin"])
elif self.preference.operator.mirror.pivot == "MEDIAN":
help_items["STANDARD"].append([" ", "Median - Median Origin"])
elif self.preference.operator.mirror.pivot == "CURSOR":
help_items["STANDARD"].append([" ", "Cursor - Cursor Origin"])
elif self.preference.operator.mirror.pivot == "INDIVIDUAL":
help_items["STANDARD"].append([" ", "Individual - Individual Origin"])
#Orientation
if self.preference.operator.mirror.orientation == "LOCAL":
help_items["STANDARD"].append([" ", "Default - Local Orientation"])
elif self.preference.operator.mirror.orientation == "GLOBAL":
help_items["STANDARD"].append([" ", "Global - World Orientation"])
elif self.preference.operator.mirror.orientation == "VIEW":
help_items["STANDARD"].append([" ", "View - View Orientation"])
elif self.preference.operator.mirror.orientation == "CURSOR":
help_items["STANDARD"].append([" ", "Cursor - 3d Cursor Orientation"])
#Modes
if self.preference.operator.mirror.mode == "MODIFIER": #"NEWMODIFIER", "BISECTMOD", "MODIFIERAPPLY"}:
help_items["STANDARD"].append([" ", "Add / Adjust mirror mod"])
elif self.preference.operator.mirror.mode == "NEWMODIFIER":
help_items["STANDARD"].append([" ", "Add new mirror mod"])
elif self.preference.operator.mirror.mode == "BISECTMOD":
help_items["STANDARD"].append([" ", "Split mesh / mirror "])
elif self.preference.operator.mirror.mode == "MODIFIERAPPLY":
help_items["STANDARD"].append([" ", "Add & Apply mirror mod"])
elif self.preference.operator.mirror.mode == "SYMMETRY":
help_items["STANDARD"].append([" ", "Mesh Symetrization "])
elif self.preference.operator.mirror.mode == "BISECT":
help_items["STANDARD"].append([" ", "Split Mesh - No Mirror"])
help_items["STANDARD"].append([" ", " "])
help_items["STANDARD"].append(["RMB", "Exit"])
help_items["STANDARD"].append(["ALT + Scroll ", "Full List"])
help_items["STANDARD"].append(["CTRL + Scroll ", "Short List"])
if self.preference.operator.mirror.close:
help_items["STANDARD"].append(["Shift", "Append Axis"])
help_items["STANDARD"].append(["A", "Add New Mirror Modifier"])
if self.preference.operator.mirror.advanced:
help_items["STANDARD"].append(["S", f"Cycle Pivot Points - {self.preference.operator.mirror.pivot}"])
help_items["STANDARD"].append(["W", f"Cycle Orientations - {self.preference.operator.mirror.orientation}"])
help_items["STANDARD"].append(["V", "View Orientation - " + "True" if self.preference.operator.mirror.orientation == "VIEW" else "View Orientation - " + "False"])
help_items["STANDARD"].append(["X", "Reset To Default Options"])
help_items["STANDARD"].append(["D", "Mirror Menu"])
if self.preference.operator.mirror.mode != "FLIP":
help_items["STANDARD"].append(["F", "Flip"])
help_items["STANDARD"].append(["TAB ", "Simple Mode" if self.preference.operator.mirror.advanced else "Advanced Mode"])
help_items["STANDARD"].append([" ", " "])
#Modes
if self.preference.operator.mirror.mode == "MODIFIER": #"NEWMODIFIER", "BISECTMOD", "MODIFIERAPPLY"}:
if len([mod for mod in bpy.context.active_object.modifiers if mod.type == 'MIRROR']):
help_items["STANDARD"].append([" ", "Adjust mirror mod"])
else:
if len(context.selected_objects) >= 2:
if preference.operator.mirror.include_active:
help_items["STANDARD"].append([" ", "Mirror all across primary selection"])
else:
help_items["STANDARD"].append([" ", "Mirror across primary selection"])
else:
help_items["STANDARD"].append([" ", "Add mirror mod"])
elif self.preference.operator.mirror.mode == "NEWMODIFIER":
help_items["STANDARD"].append([" ", "Add new mirror mod"])
elif self.preference.operator.mirror.mode == "BISECTMOD":
help_items["STANDARD"].append([" ", "Delete half / Add mirror"])
elif self.preference.operator.mirror.mode == "MODIFIERAPPLY":
help_items["STANDARD"].append([" ", "Add & Apply mirror mod"])
elif self.preference.operator.mirror.mode == "SYMMETRY":
help_items["STANDARD"].append([" ", "Mesh Symetrization "])
elif self.preference.operator.mirror.mode == "BISECT":
help_items["STANDARD"].append([" ", "Split Mesh - No Mirror"])
elif self.preference.operator.mirror.mode == "FLIP":
help_items["STANDARD"].append([" ", "Flip Mesh / Selection - No Mirror"])
if len(bpy.context.selected_objects) >= 2 and self.preference.operator.mirror.mode == 'MODIFIER':
help_items["STANDARD"].append([" ", f"To_{self.preference.operator.mirror.mode}"])
else:
help_items["STANDARD"].append([" ", f"{self.preference.operator.mirror.mode}"])
# Mods
active_mod = ""
mods = context.active_object.modifiers
if preference.operator.mirror.mode == 'MODIFIER':
if preference.operator.mirror.modifier and preference.operator.mirror.modifier != 'new':
active_mod = preference.operator.mirror.modifier
# Show other objects mods for a selection of 2
objs = [obj for obj in context.selected_editable_objects if obj.type == 'MESH']
if len(objs) == 2:
for obj in objs:
if obj != context.active_object:
mods = obj.modifiers
for obj_mod in obj.modifiers:
if obj_mod.type == 'MIRROR':
active_mod = obj_mod.name
break
mods_list = get_mods_list(mods=mods)
self.master.receive_fast_ui(help_list=help_items, mods_list=mods_list, active_mod_name=active_mod)
# Finished
self.master.finished()
class HOPS_OT_MirrorGizmoGroup(GizmoGroup):
bl_idname = "hops.mirror_gizmogroup"
bl_label = "Mirror Gizmo Group"
bl_space_type = 'VIEW_3D'
bl_region_type = 'WINDOW'
bl_options = {'3D'}
@classmethod
def poll(cls, context):
ob = context.object
if len(context.selected_objects) > 0:
return getattr(ob, "type", "") in {"MESH", "EMPTY", "CURVE", "GPENCIL", "GREASEPENCIL"}
def setup(self, context):
selected = context.selected_objects
if len(selected) == 1:
self.multiselection = False
else:
self.multiselection = True
self.cross_p = self.gizmos.new("GIZMO_GT_arrow_3d")
self.draw_cross(context, self.cross_p, "hops.mirror_execute_z_gizmo", (0.157, 0.565, 1))
self.cross_n = self.gizmos.new("GIZMO_GT_arrow_3d")
self.draw_cross(context, self.cross_n, "hops.mirror_execute_z_gizmo", (0.157, 0.565, 1))
self.arrow_z_p = self.gizmos.new("GIZMO_GT_arrow_3d")
self.draw_arrow(context, self.arrow_z_p, "hops.mirror_execute_z_gizmo", (0.157, 0.565, 1))
self.arrow_z_n = self.gizmos.new("GIZMO_GT_arrow_3d")
self.draw_arrow(context, self.arrow_z_n, "hops.mirror_execute_zm_gizmo", (0.157, 0.565, 1))
self.arrow_x_p = self.gizmos.new("GIZMO_GT_arrow_3d")
self.draw_arrow(context, self.arrow_x_p, "hops.mirror_execute_x_gizmo", (1, 0.2, 0.322))
self.arrow_x_n = self.gizmos.new("GIZMO_GT_arrow_3d")
self.draw_arrow(context, self.arrow_x_n, "hops.mirror_execute_xm_gizmo", (1, 0.2, 0.322))
self.arrow_y_p = self.gizmos.new("GIZMO_GT_arrow_3d")
self.draw_arrow(context, self.arrow_y_p, "hops.mirror_execute_y_gizmo", (0.545, 0.863, 0))
self.arrow_y_n = self.gizmos.new("GIZMO_GT_arrow_3d")
self.draw_arrow(context, self.arrow_y_n, "hops.mirror_execute_ym_gizmo", (0.545, 0.863, 0))
def draw_prepare(self, context):
preference = addon.preference()
active = context.active_object
if len(context.selected_objects) == 2:
others = [obj for obj in context.selected_objects if obj != context.active_object]
active = others[0]
selected = context.selected_objects
if len(selected) == 1:
self.multiselection = False
else:
self.multiselection = True
ob = context.active_object
if preference.operator.mirror.mode == 'MODIFIER':
if self.multiselection is False:
active_mirror_mod = [mod for mod in active.modifiers if mod.type == "MIRROR" and mod.name == preference.operator.mirror.modifier and mod.mirror_object]
if active_mirror_mod:
ob = active_mirror_mod[0].mirror_object
matrix_world = ob.matrix_world.copy()
self.cross_p.matrix_basis = self.redraw_arrow(context, matrix_world, 180, 'X')
self.cross_n.matrix_basis = self.redraw_arrow(context, matrix_world, -90, 'Y')
self.arrow_z_p.matrix_basis = self.redraw_arrow(context, matrix_world, 180, 'X')
self.arrow_z_n.matrix_basis = self.redraw_arrow(context, matrix_world, 0, 'X')
self.arrow_x_p.matrix_basis = self.redraw_arrow(context, matrix_world, -90, 'Y')
self.arrow_x_n.matrix_basis = self.redraw_arrow(context, matrix_world, 90, 'Y')
self.arrow_y_p.matrix_basis = self.redraw_arrow(context, matrix_world, 90, 'X')
self.arrow_y_n.matrix_basis = self.redraw_arrow(context, matrix_world, -90, 'X')
if preference.operator.mirror.advanced:
self.arrow_z_p.hide = self.arrow_z_n.hide = True if preference.operator.mirror.orientation == "VIEW" and preference.operator.mirror.mode != 'SYMMETRY' else False
self.cross_p.hide = self.cross_n.hide = True if ((preference.operator.mirror.orientation == "LOCAL" and preference.operator.mirror.pivot in {"ACTIVE", "INDIVIDUAL"}) or preference.operator.mirror.mode in {'SYMMETRY', 'MODIFIERAPPLY'}) else False
else:
self.arrow_z_p.hide = self.arrow_z_n.hide = False
self.cross_p.hide = self.cross_n.hide = True
global creating_empty
if self.cross_p.hide == True:
creating_empty = False
else:
creating_empty = True
if preference.operator.mirror.mode == "MODIFIER":
if active.type in {"GPENCIL", "GREASEPENCIL"}:
if active.type == 'GPENCIL':
mtype = "GP_MIRROR"
mods_s = 'grease_pencil_modifiers'
else:
"GREASE_PENCIL_MIRROR"
mtype = "GREASE_PENCIL_MIRROR"
mods_s = 'modifiers'
mods = [mod for mod in getattr(active, mods_s) if mod.type == mtype]
if len(mods) > 0:
activemod = [mod for mod in mods if mod.name == preference.operator.mirror.modifier]
if activemod:
mod = activemod[0]
if bpy.app.version > (2, 90, 0):
use_axis_x = mod.use_axis_x
use_axis_y = mod.use_axis_y
use_axis_z = mod.use_axis_z
else:
use_axis_x = mod.x_axis
use_axis_y = mod.y_axis
use_axis_z = mod.z_axis
if use_axis_x:
self.arrow_x_p.color = 0.4, 0.4, 0.4
self.arrow_x_n.color = 0.4, 0.4, 0.4
else:
self.arrow_x_p.color = 1, 0.2, 0.322
self.arrow_x_n.color = 1, 0.2, 0.322
if use_axis_y:
self.arrow_y_p.color = 0.4, 0.4, 0.4
self.arrow_y_n.color = 0.4, 0.4, 0.4
else:
self.arrow_y_p.color = 0.545, 0.863, 0
self.arrow_y_n.color = 0.545, 0.863, 0
if use_axis_z:
self.arrow_z_p.color = 0.4, 0.4, 0.4
self.arrow_z_n.color = 0.4, 0.4, 0.4
else:
self.arrow_z_p.color = 0.157, 0.565, 1
self.arrow_z_n.color = 0.157, 0.565, 1
# No active modifier
else:
self.reset_colors_to_defualt()
else:
mods = [mod for mod in active.modifiers if mod.type == "MIRROR"]
objs = [obj for obj in context.selected_editable_objects if obj.type == 'MESH']
if len(mods) > 0:
activemod = [mod for mod in mods if mod.name == preference.operator.mirror.modifier]
if activemod:
mod = activemod[0]
if mod.use_axis[0]:
self.arrow_x_p.color = 0.4, 0.4, 0.4
self.arrow_x_n.color = 0.4, 0.4, 0.4
else:
self.arrow_x_p.color = 1, 0.2, 0.322
self.arrow_x_n.color = 1, 0.2, 0.322
if mod.use_axis[1]:
self.arrow_y_p.color = 0.4, 0.4, 0.4
self.arrow_y_n.color = 0.4, 0.4, 0.4
else:
self.arrow_y_p.color = 0.545, 0.863, 0
self.arrow_y_n.color = 0.545, 0.863, 0
if mod.use_axis[2]:
self.arrow_z_p.color = 0.4, 0.4, 0.4
self.arrow_z_n.color = 0.4, 0.4, 0.4
else:
self.arrow_z_p.color = 0.157, 0.565, 1
self.arrow_z_n.color = 0.157, 0.565, 1
# No active modifier
else:
self.reset_colors_to_defualt()
else:
self.reset_colors_to_defualt()
def draw_cross(self, context, cross, operaotr, color):
cross.target_set_operator(operaotr)
cross.line_width = 1
cross.draw_style = 'CROSS'
cross.hide_select = True
cross.color = 0.1, 0.1, 0.1
cross.alpha = 1
cross.scale_basis = 0.7
# cross.color_highlight = color[0] * 1.6, color[1] * 1.6, color[2] * 1.6
# cross.alpha_highlight = 0.8
def draw_arrow(self, context, arrow, operaotr, color,):
preference = addon.preference()
arrow.target_set_operator(operaotr)
arrow.line_width = preference.operator.mirror.width
arrow.draw_style = 'BOX'
arrow.color = color[0], color[1], color[2]
arrow.alpha = 0.5
arrow.scale_basis = preference.operator.mirror.scale
arrow.color_highlight = color[0] * 1.6, color[1] * 1.6, color[2] * 1.6
arrow.alpha_highlight = 0.8
def redraw_arrow(self, context, matrix, angle, axis):
preference = addon.preference()
obloc, obrot, obscale = matrix.decompose()
selected = context.selected_objects
if preference.operator.mirror.orientation == "GLOBAL":
rot = Euler((0, 0, 0), "XYZ").to_quaternion()
elif preference.operator.mirror.orientation == "CURSOR":
rot = context.scene.cursor.rotation_euler.to_quaternion()
elif preference.operator.mirror.orientation == "LOCAL":
rot = obrot
elif preference.operator.mirror.orientation == "VIEW":
rot = context.region_data.view_rotation
if preference.operator.mirror.pivot in {"ACTIVE", "INDIVIDUAL"}:
loc = obloc
elif preference.operator.mirror.pivot == "CURSOR":
loc = context.scene.cursor.location
elif preference.operator.mirror.pivot == "MEDIAN":
if len(selected) > 0:
selected_loc = [obj.matrix_world.translation for obj in selected]
loc = sum(selected_loc, Vector()) / len(selected)
else:
loc = obloc
if preference.operator.mirror.mode == 'SYMMETRY' or not preference.operator.mirror.advanced:
rot, loc = obrot, obloc
rotation = rot.to_matrix().to_4x4() @ Matrix.Rotation(radians(angle), 4, axis)
matrix_basis = Matrix.Translation(loc) @ rotation @ Matrix.Scale(1, 4, (1.0, 1.0, 1.0))
return matrix_basis.normalized()
def reset_colors_to_defualt(self):
self.arrow_x_p.color = 1, 0.2, 0.322
self.arrow_x_n.color = 1, 0.2, 0.322
self.arrow_y_p.color = 0.545, 0.863, 0
self.arrow_y_n.color = 0.545, 0.863, 0
self.arrow_z_p.color = 0.157, 0.565, 1
self.arrow_z_n.color = 0.157, 0.565, 1
class HOPS_MT_MirrorMenu(bpy.types.Menu):
bl_idname = "HOPS_MT_MirrorMenu"
bl_label = "Mirror Menu"
def draw(self, context):
preference = addon.preference()
layout = self.layout
layout.label(text="Mode")
layout.prop(preference.operator.mirror, 'mode', expand=True)
layout.separator()
if preference.operator.mirror.advanced:
layout.label(text="Orientation")
layout.prop(preference.operator.mirror, 'orientation', expand=True)
layout.separator()
layout.label(text="Pivot Point")
layout.prop(preference.operator.mirror, 'pivot', expand=True)
else:
layout.prop(preference.operator.mirror, 'advanced', text="Advanced", toggle=True, icon='ORIENTATION_GIMBAL')
class HOPS_PT_MirrorOptions(Panel):
bl_space_type = 'VIEW_3D'
bl_region_type = 'HEADER'
bl_label = "Mirror Options"
def draw(self, context):
preference = addon.preference()
layout = self.layout
layout.label(text="Mirror Settings")
layout.separator()
layout.prop(preference.operator.mirror, 'mirror_u', text="Textures Flip u")
layout.prop(preference.operator.mirror, 'mirror_v', text="Textures Flip v")
layout.separator()
layout.prop(preference.operator.mirror, 'include_active', text="Include active in multi mirror/flip")
layout.prop(preference.operator.mirror, 'parent_empty', text="Parent empty to active object")
layout.separator()
layout.prop(preference.operator.mirror, 'revert', text="Revert Gizmo handlers")
layout.separator()
layout.prop(preference.operator.mirror, 'symmetrize_selected', text="Symmetrize only selected mesh")
layout.separator()
class HOPS_PT_mirror_mode(Panel):
bl_space_type = 'VIEW_3D'
bl_region_type = 'HEADER'
bl_label = "Mirror Mode"
bl_ui_units_x = 8
def draw(self, context):
preference = addon.preference()
layout = self.layout
layout.label(text="Mirror Mode")
row = layout.row()
col = row.column()
col.prop(preference.operator.mirror, 'mode', expand=True)
class HOPS_PT_mirror_transform_orientations(Panel):
bl_space_type = 'VIEW_3D'
bl_region_type = 'HEADER'
bl_label = "Mirror Transform Orientations"
bl_ui_units_x = 8
def draw(self, context):
preference = addon.preference()
layout = self.layout
layout.label(text="Transform Orientations")
row = layout.row()
col = row.column()
col.prop(preference.operator.mirror, 'orientation', expand=True)
class HOPS_PT_mirror_pivot(Panel):
bl_space_type = 'VIEW_3D'
bl_region_type = 'HEADER'
bl_label = "Mirror Pivot Point"
bl_ui_units_x = 8
def draw(self, context):
preference = addon.preference()
layout = self.layout
layout.label(text="Mirror Pivot Point")
row = layout.row()
col = row.column()
col.prop(preference.operator.mirror, 'pivot', expand=True)
def draw_header(ht, context):
preference = addon.preference()
mir = preference.operator.mirror
selected = context.selected_objects
layout = ht.layout
mir_string = f'{"Empty will be created" if mir.orientation != "LOCAL" and mir.mode != "MODIFIERAPPLY" or mir.pivot != "ACTIVE" else "No Empty Will be created"}'
#if self.preference.operator.mirror.orientation != "LOCAL" and self.preference.operator.mirror.mode != "MODIFIERAPPLY" or self.preference.operator.mirror.pivot != "ACTIVE" else
if len(selected) < 1:
mir_string = f'No Selection - Please make selection'
layout.label(text=f'Mirror Gizmo - {mir_string}')
if bpy.app.version[:2] < (3, 0):
layout.separator_spacer()
layout.prop_with_popover(preference.operator.mirror, "mode", text="", panel="HOPS_PT_mirror_mode")
if preference.operator.mirror.mode == 'MODIFIER':
layout.prop(preference.operator.mirror, "modifier", text="")
layout.prop(preference.operator.mirror, 'advanced', text="", toggle=True, icon='ORIENTATION_GIMBAL')
if preference.operator.mirror.advanced:
if preference.operator.mirror.mode != 'SYMMETRY':
layout.prop_with_popover(preference.operator.mirror, "orientation", text="", panel="HOPS_PT_mirror_transform_orientations")
layout.prop_with_popover(preference.operator.mirror, "pivot", text="", panel="HOPS_PT_mirror_pivot")
layout.popover('HOPS_PT_MirrorOptions', text='', icon="SETTINGS")
layout.separator_spacer()
layout.prop(preference.operator.mirror, 'close', text="Close after operation", toggle=True)
layout.operator('hops.mirror_exit', text='Exit')
def infobar(self, context):
layout = self.layout
row = self.layout.row(align=True)
preference = addon.preference()
row.label(text="", icon='MOUSE_LMB')
row.label(text="Mirror")
row.separator(factor=8.0)
row.label(text="", icon='MOUSE_MMB')
row.label(text="Rotate View")
row.separator(factor=8.0)
row.label(text="", icon='MOUSE_RMB')
row.label(text="Exit")
row.separator(factor=12.0)
row.label(text="", icon='EVENT_SHIFT')
row.label(text="Multi Mirror")
row.separator(factor=2.0)
row.label(text="", icon='EVENT_TAB')
row.label(text="Advanced mode")
row.separator(factor=2.0)
row.label(text="", icon='EVENT_D')
row.label(text="Menu")
row.separator(factor=2.0)
row.label(text="", icon='EVENT_A')
row.label(text="Mod Modes")
if preference.operator.mirror.advanced:
row.separator(factor=2.0)
row.label(text="", icon='EVENT_S')
row.label(text="Cycle Pivot Points")
row.separator(factor=2.0)
row.label(text="", icon='EVENT_W')
row.label(text="Cycle Orientation")
row.separator(factor=2.0)
row.label(text="", icon='EVENT_V')
row.label(text="View Orientation")
row.separator(factor=2.0)
row.label(text="", icon='EVENT_X')
row.label(text="Reset")
row.separator(factor=2.0)
row.label(text="", icon='EVENT_H')
row.label(text="HELP")
row.separator(factor=2.0)
row.label(text="", icon='EVENT_M')
row.label(text="MODS")
layout.separator_spacer()
layout.template_reports_banner()
layout.template_running_jobs()
layout.separator_spacer()
scene = context.scene
view_layer = context.view_layer
if bpy.app.version < (2, 90, 0):
layout.label(text=scene.statistics(view_layer), translate=False)
def get_3d_viewport_regions(context):
regions = []
for area in context.screen.areas:
if area.type == 'VIEW_3D':
for region in area.regions:
if region.type == 'WINDOW':
regions.append(region)
return regions
def is_mouse_in_region(event, region):
mouse_x = event.mouse_x
mouse_y = event.mouse_y
within_region = (
region.x < mouse_x < region.x + region.width and
region.y < mouse_y < region.y + region.height
)
return within_region
# ------------- Not In Use -------------
class HOPS_OT_MirrorRemoveGizmo(Operator):
bl_idname = "hops.mirror_remove_gizmo"
bl_label = "Mirror Gizmo Remove"
bl_options = {'REGISTER', 'UNDO', 'INTERNAL'}
@classmethod
def poll(cls, context):
ob = context.object
return getattr(ob, "type", "") in {"MESH", "EMPTY", "CURVE"}
def execute(self, context):
preference = addon.preference()
preference.property.Hops_gizmo_mirror = False
context.area.tag_redraw()
bpy.types.WindowManager.gizmo_group_type_remove("Hops_mirror_gizmo")
# bpy.types.WindowManager.Hops_mirror_gizmo
return {'FINISHED'}