Skip to content
Snippets Groups Projects
Commit db8ee9e3 authored by meta-androcto's avatar meta-androcto
Browse files

mesh_auto_mirror: initial port to 2.9 T62797

parent 60321780
Branches
Tags
No related merge requests found
# ########################################################### # ######################################################################################################
# An simple add-on to auto cut in two and mirror an object # # An simple add-on to auto cut in two and mirror an object #
# Actually partially uncommented (see further version) # # Actualy partialy uncommented (see further version) #
# Author: Lapineige # # Author: Lapineige, Bookyakuno #
# License: GPL v3 # # License: GPL v3 #
# ########################################################### # ######################################################################################################
# 2.8 update by Bookyakuno, meta-androcto
bl_info = { bl_info = {
"name": "Auto Mirror", "name": "Auto Mirror",
"description": "Super fast cutting and mirroring for Mesh objects", "description": "Super fast cutting and mirroring for mesh",
"author": "Lapineige", "author": "Lapineige",
"version": (2, 4, 2), "version": (2, 5, 2),
"blender": (2, 71, 0), "blender": (2, 80, 0),
"location": "View 3D > Toolbar > Tools tab > AutoMirror (panel)", "location": "View 3D > Sidebar > Tools Tab > AutoMirror (panel)",
"warning": "", "warning": "",
"wiki_url": "https://wiki.blender.org/index.php/Extensions:2.6/" "wiki_url": "https://wiki.blender.org/index.php/Extensions:2.6/"
"Py/Scripts/Modeling/AutoMirror", "Py/Scripts/Modeling/AutoMirror",
...@@ -19,26 +20,38 @@ bl_info = { ...@@ -19,26 +20,38 @@ bl_info = {
import bpy import bpy
from mathutils import Vector
import bmesh
import bpy
import collections
import mathutils
import math
from bpy_extras import view3d_utils
from bpy.types import (
Operator,
Menu,
Panel,
AddonPreferences,
)
from bpy.props import ( from bpy.props import (
BoolProperty, BoolProperty,
EnumProperty, EnumProperty,
FloatProperty, FloatProperty,
IntProperty,
PointerProperty, PointerProperty,
StringProperty,
) )
from bpy.types import (
Operator,
Panel,
PropertyGroup,
)
from mathutils import Vector
# Operators # Operator
class AlignVertices(Operator):
class AlignVertices(bpy.types.Operator):
""" Automatically cut an object along an axis """
bl_idname = "object.align_vertices" bl_idname = "object.align_vertices"
bl_label = "Align Vertices on an Axis" bl_label = "Align Vertices on 1 Axis"
bl_description = ("Align Vertices on an Axis\n"
"Needs an Active Mesh Object")
@classmethod @classmethod
def poll(cls, context): def poll(cls, context):
...@@ -46,88 +59,95 @@ class AlignVertices(Operator): ...@@ -46,88 +59,95 @@ class AlignVertices(Operator):
return obj and obj.type == "MESH" return obj and obj.type == "MESH"
def execute(self, context): def execute(self, context):
auto_m = context.scene.auto_mirror bpy.ops.object.mode_set(mode = 'OBJECT')
bpy.ops.object.mode_set(mode='OBJECT')
x1, y1, z1 = bpy.context.scene.cursor.location x1,y1,z1 = bpy.context.scene.cursor.location
bpy.ops.view3d.snap_cursor_to_selected() bpy.ops.view3d.snap_cursor_to_selected()
x2, y2, z2 = bpy.context.scene.cursor.location x2,y2,z2 = bpy.context.scene.cursor.location
bpy.context.scene.cursor.location[0], \ bpy.context.scene.cursor.location[0], \
bpy.context.scene.cursor.location[1], \ bpy.context.scene.cursor.location[1], \
bpy.context.scene.cursor.location[2] = 0, 0, 0 bpy.context.scene.cursor.location[2] = 0, 0, 0
# Vertices coordinate to 0 (local coordinate, so on the origin) #Vertices coordinate to 0 (local coordinate, so on the origin)
for vert in bpy.context.object.data.vertices: for vert in bpy.context.object.data.vertices:
if vert.select: if vert.select:
if auto_m.axis == 'x': if bpy.context.scene.AutoMirror_axis == 'x':
axis = 0 axis = 0
elif auto_m.axis == 'y': elif bpy.context.scene.AutoMirror_axis == 'y':
axis = 1 axis = 1
elif auto_m.axis == 'z': elif bpy.context.scene.AutoMirror_axis == 'z':
axis = 2 axis = 2
vert.co[axis] = 0 vert.co[axis] = 0
#
bpy.context.scene.cursor.location = x2,y2,z2
bpy.context.scene.cursor.location = x2, y2, z2
bpy.ops.object.origin_set(type='ORIGIN_CURSOR') bpy.ops.object.origin_set(type='ORIGIN_CURSOR')
bpy.context.scene.cursor.location = x1, y1, z1 bpy.context.scene.cursor.location = x1,y1,z1
bpy.ops.object.mode_set(mode='EDIT')
bpy.ops.object.mode_set(mode = 'EDIT')
return {'FINISHED'} return {'FINISHED'}
class AutoMirror(Operator): class AutoMirror(bpy.types.Operator):
""" Automatically cut an object along an axis """
bl_idname = "object.automirror" bl_idname = "object.automirror"
bl_label = "AutoMirror" bl_label = "AutoMirror"
bl_description = ("Automatically cut an object along an axis\n" bl_options = {'REGISTER'} # 'UNDO' ?
"Needs an Active Mesh Object")
bl_options = {'REGISTER'}
@classmethod @classmethod
def poll(cls, context): def poll(cls, context):
obj = context.active_object obj = context.active_object
return obj and obj.type == "MESH" return obj and obj.type == "MESH"
def draw(self, context):
layout = self.layout
if bpy.context.object and bpy.context.object.type == 'MESH':
layout.prop(context.scene, "AutoMirror_axis", text="Mirror axis")
layout.prop(context.scene, "AutoMirror_orientation", text="Orientation")
layout.prop(context.scene, "AutoMirror_threshold", text="Threshold")
layout.prop(context.scene, "AutoMirror_toggle_edit", text="Toggle edit")
layout.prop(context.scene, "AutoMirror_cut", text="Cut and mirror")
if bpy.context.scene.AutoMirror_cut:
layout.prop(context.scene, "AutoMirror_clipping", text="Clipping")
layout.prop(context.scene, "AutoMirror_apply_mirror", text="Apply mirror")
else:
layout.label(icon="ERROR", text="No mesh selected")
def get_local_axis_vector(self, context, X, Y, Z, orientation): def get_local_axis_vector(self, context, X, Y, Z, orientation):
loc = context.object.location loc = context.object.location
bpy.ops.object.mode_set(mode="OBJECT") # Needed to avoid to translate vertices bpy.ops.object.mode_set(mode="OBJECT") # Needed to avoid to translate vertices
v1 = Vector((loc[0], loc[1], loc[2])) v1 = Vector((loc[0],loc[1],loc[2]))
bpy.ops.transform.translate( bpy.ops.transform.translate(value=(X*orientation, Y*orientation, Z*orientation),
value=(X * orientation, Y * orientation, Z * orientation), constraint_axis=((X==1), (Y==1), (Z==1)),
constraint_axis=((X == 1), (Y == 1), (Z == 1)), orient_type='LOCAL')
orient_type='LOCAL' v2 = Vector((loc[0],loc[1],loc[2]))
) bpy.ops.transform.translate(value=(-X*orientation, -Y*orientation, -Z*orientation),
v2 = Vector((loc[0], loc[1], loc[2])) constraint_axis=((X==1), (Y==1), (Z==1)),
bpy.ops.transform.translate( orient_type='LOCAL')
value=(-X * orientation, -Y * orientation, -Z * orientation),
constraint_axis=((X == 1), (Y == 1), (Z == 1)),
orient_type='LOCAL'
)
bpy.ops.object.mode_set(mode="EDIT") bpy.ops.object.mode_set(mode="EDIT")
return v2 - v1 return v2-v1
def execute(self, context): def execute(self, context):
auto_m = context.scene.auto_mirror X,Y,Z = 0,0,0
if bpy.context.scene.AutoMirror_axis == 'x':
X, Y, Z = 0, 0, 0
if auto_m.axis == 'x':
X = 1 X = 1
elif auto_m.axis == 'y': elif bpy.context.scene.AutoMirror_axis == 'y':
Y = 1 Y = 1
elif auto_m.axis == 'z': elif bpy.context.scene.AutoMirror_axis == 'z':
Z = 1 Z = 1
current_mode = bpy.context.object.mode # Save the current mode current_mode = bpy.context.object.mode # Save the current mode
if bpy.context.object.mode != "EDIT": if bpy.context.object.mode != "EDIT":
bpy.ops.object.mode_set(mode="EDIT") # Go to edit mode bpy.ops.object.mode_set(mode="EDIT") # Go to edit mode
bpy.ops.mesh.select_all(action='SELECT') # Select all the vertices
bpy.ops.mesh.select_all(action='SELECT') # Select all the vertices if bpy.context.scene.AutoMirror_orientation == 'positive':
if auto_m.orientation == 'positive':
orientation = 1 orientation = 1
else: else:
orientation = -1 orientation = -1
...@@ -136,38 +156,32 @@ class AutoMirror(Operator): ...@@ -136,38 +156,32 @@ class AutoMirror(Operator):
# Cut the mesh # Cut the mesh
bpy.ops.mesh.bisect( bpy.ops.mesh.bisect(
plane_co=( plane_co=(
bpy.context.object.location[0], bpy.context.object.location[0],
bpy.context.object.location[1], bpy.context.object.location[1],
bpy.context.object.location[2] bpy.context.object.location[2]
), ),
plane_no=cut_normal, plane_no=cut_normal,
use_fill=False, use_fill= False,
clear_inner=auto_m.cut, clear_inner= bpy.context.scene.AutoMirror_cut,
clear_outer=0, clear_outer= 0,
threshold=auto_m.threshold threshold= bpy.context.scene.AutoMirror_threshold)
)
bpy.ops.object.align_vertices() # Use to align the vertices on the origin, needed by the "threshold"
# Use to align the vertices on the origin, needed by the "threshold"
bpy.ops.object.align_vertices() if not bpy.context.scene.AutoMirror_toggle_edit:
bpy.ops.object.mode_set(mode=current_mode) # Reload previous mode
if not auto_m.toggle_edit:
bpy.ops.object.mode_set(mode=current_mode) # Reload previous mode if bpy.context.scene.AutoMirror_cut:
bpy.ops.object.modifier_add(type='MIRROR') # Add a mirror modifier
if auto_m.cut: bpy.context.object.modifiers[-1].use_axis[0] = X # Choose the axis to use, based on the cut's axis
bpy.ops.object.modifier_add(type='MIRROR') # Add a mirror modifier bpy.context.object.modifiers[-1].use_axis[1] = Y
bpy.context.object.modifiers[-1].use_x = X # Choose the axis to use, based on the cut's axis bpy.context.object.modifiers[-1].use_axis[2] = Z
bpy.context.object.modifiers[-1].use_y = Y bpy.context.object.modifiers[-1].use_clip = context.scene.Use_Matcap
bpy.context.object.modifiers[-1].use_z = Z bpy.context.object.modifiers[-1].show_on_cage = context.scene.AutoMirror_show_on_cage
bpy.context.object.modifiers[-1].use_clip = auto_m.use_clip if bpy.context.scene.AutoMirror_apply_mirror:
bpy.context.object.modifiers[-1].show_on_cage = auto_m.show_on_cage
if auto_m.apply_mirror:
bpy.ops.object.mode_set(mode='OBJECT') bpy.ops.object.mode_set(mode='OBJECT')
bpy.ops.object.modifier_apply( bpy.ops.object.modifier_apply(apply_as= 'DATA', modifier= bpy.context.object.modifiers[-1].name)
apply_as='DATA', if bpy.context.scene.AutoMirror_toggle_edit:
modifier=bpy.context.object.modifiers[-1].name
)
if auto_m.toggle_edit:
bpy.ops.object.mode_set(mode='EDIT') bpy.ops.object.mode_set(mode='EDIT')
else: else:
bpy.ops.object.mode_set(mode=current_mode) bpy.ops.object.mode_set(mode=current_mode)
...@@ -176,104 +190,135 @@ class AutoMirror(Operator): ...@@ -176,104 +190,135 @@ class AutoMirror(Operator):
# Panel # Panel
class BisectMirror(Panel):
bl_label = "Auto Mirror" class VIEW3D_PT_BisectMirror(Panel):
bl_space_type = 'VIEW_3D' bl_space_type = 'VIEW_3D'
bl_region_type = 'TOOLS' bl_region_type = 'UI'
bl_category = "Tools" bl_label = "Auto Mirror"
bl_options = {"DEFAULT_CLOSED"} bl_category = 'Tools'
bl_options = {'DEFAULT_CLOSED'}
def draw(self, context): def draw(self, context):
layout = self.layout layout = self.layout
auto_m = context.scene.auto_mirror col = layout.column(align=True)
obj = context.active_object
layout = self.layout
if bpy.context.object and bpy.context.object.type == 'MESH':
layout.operator("object.automirror")
layout.prop(context.scene, "AutoMirror_axis", text="Mirror Axis", expand=True)
layout.prop(context.scene, "AutoMirror_orientation", text="Orientation")
layout.prop(context.scene, "AutoMirror_threshold", text="Threshold")
layout.prop(context.scene, "AutoMirror_toggle_edit", text="Toggle Edit")
layout.prop(context.scene, "AutoMirror_cut", text="Cut and Mirror")
if bpy.context.scene.AutoMirror_cut:
layout.prop(context.scene, "Use_Matcap", text="Use Clip")
layout.prop(context.scene, "AutoMirror_show_on_cage", text="Editable")
layout.prop(context.scene, "AutoMirror_apply_mirror", text="Apply Mirror")
if obj and obj.type == 'MESH':
layout.operator("object.automirror", icon="MOD_MIRROR")
layout.label(text="Options:")
layout.prop(auto_m, "axis", text="Mirror Axis", expand=True)
layout.prop(auto_m, "orientation", text="Orientation")
layout.prop(auto_m, "threshold", text="Threshold")
layout.prop(auto_m, "toggle_edit", text="Toggle Edit")
layout.prop(auto_m, "cut", text="Cut and Mirror", toggle=True, icon="MOD_REMESH")
if auto_m.cut:
col = layout.column(align=True)
row = col.row(align=True)
row.prop(auto_m, "use_clip", text="Use Clip", toggle=True)
row.prop(auto_m, "show_on_cage", text="Editable", toggle=True)
col.prop(auto_m, "apply_mirror", text="Apply Mirror", toggle=True)
else: else:
layout.label(icon="INFO", text="No Mesh selected") layout.label(icon="ERROR", text="No mesh selected")
# Properties
class AutoMirrorProperties(PropertyGroup):
axis: EnumProperty( bpy.types.Scene.AutoMirror_axis = bpy.props.EnumProperty(
name="Axis", items = [("x", "X", "", 1),("y", "Y", "", 2),("z", "Z", "", 3)],
items=[ description="Axis used by the mirror modifier")
("x", "X", "", 1),
("y", "Y", "", 2), bpy.types.Scene.AutoMirror_orientation = bpy.props.EnumProperty(
("z", "Z", "", 3) items = [("positive", "Positive", "", 1),("negative", "Negative", "", 2)],
], description="Choose the side along the axis of the editable part (+/- coordinates)")
description="Axis used by the mirror modifier"
) bpy.types.Scene.AutoMirror_threshold = bpy.props.FloatProperty(
orientation: EnumProperty( default= 0.001, min= 0.001,
name="Orientation", description="Vertices closer than this distance are merged on the loopcut")
items=[
("positive", "Positive", "", 1), bpy.types.Scene.AutoMirror_toggle_edit = bpy.props.BoolProperty(
("negative", "Negative", "", 2) default= False,
], description="If not in edit mode, change mode to edit")
description="Choose the side along the axis of the editable part (+/- coordinates)"
) bpy.types.Scene.AutoMirror_cut = bpy.props.BoolProperty(
threshold: FloatProperty( default= True,
default=0.001, description="If enabeled, cut the mesh in two parts and mirror it. If not, just make a loopcut")
min=0.001,
description="Vertices closer than this distance are merged on the loopcut" bpy.types.Scene.AutoMirror_clipping = bpy.props.BoolProperty(
) default=True)
toggle_edit: BoolProperty( bpy.types.Scene.Use_Matcap = bpy.props.BoolProperty(default=True,
name="Toggle Edit Mode", description="Use clipping for the mirror modifier")
default=True,
description="If not in Edit mode, change mode to it" bpy.types.Scene.AutoMirror_show_on_cage = bpy.props.BoolProperty(
) default=False,
cut: BoolProperty( description="Enable to edit the cage (it's the classical modifier's option)")
name="Cut",
default=True, bpy.types.Scene.AutoMirror_apply_mirror = bpy.props.BoolProperty(
description="If enabled, cut the mesh in two parts and mirror it\n"
"If not, just make a loopcut" description="Apply the mirror modifier (useful to symmetrise the mesh)")
)
clipping: BoolProperty(
default=True # Add-ons Preferences Update Panel
)
use_clip: BoolProperty( # Define Panel classes for updating
default=True, panels = (
description="Use clipping for the mirror modifier" VIEW3D_PT_BisectMirror,
) )
show_on_cage: BoolProperty(
default=True,
description="Enable editing the cage (it's the classical modifier's option)" def update_panel(self, context):
) message = ": Updating Panel locations has failed"
apply_mirror: BoolProperty( try:
description="Apply the mirror modifier (useful to symmetrise the mesh)" for panel in panels:
if "bl_rna" in panel.__dict__:
bpy.utils.unregister_class(panel)
for panel in panels:
panel.bl_category = context.preferences.addons[__name__].preferences.category
bpy.utils.register_class(panel)
except Exception as e:
print("\n[{}]\n{}\n\nError:\n{}".format(__name__, message, e))
pass
class AutoMirrorAddonPreferences(AddonPreferences):
# this must match the addon name, use '__package__'
# when defining this in a submodule of a python package.
bl_idname = __name__
category: StringProperty(
name="Tab Category",
description="Choose a name for the category of the panel",
default="Tools",
update=update_panel
) )
def draw(self, context):
layout = self.layout
row = layout.row()
col = row.column()
col.label(text="Tab Category:")
col.prop(self, "category", text="")
# define classes for registration
classes = (
VIEW3D_PT_BisectMirror,
AutoMirror,
AlignVertices,
AutoMirrorAddonPreferences
)
# registering and menu integration
def register(): def register():
bpy.utils.register_class(BisectMirror) for cls in classes:
bpy.utils.register_class(AutoMirror) bpy.utils.register_class(cls)
bpy.utils.register_class(AlignVertices) update_panel(None, bpy.context)
bpy.utils.register_class(AutoMirrorProperties)
bpy.types.Scene.auto_mirror = PointerProperty(
type=AutoMirrorProperties
)
# unregistering and removing menus
def unregister(): def unregister():
bpy.utils.unregister_class(BisectMirror) for cls in reversed(classes):
bpy.utils.unregister_class(AutoMirror) bpy.utils.unregister_class(cls)
bpy.utils.unregister_class(AlignVertices)
bpy.utils.unregister_class(AutoMirrorProperties)
del bpy.types.Scene.auto_mirror
if __name__ == "__main__": if __name__ == "__main__":
register() register()
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment