Permalink
Switch branches/tags
Nothing to show
Find file Copy path
0f5e368 Jan 27, 2015
204 lines (159 sloc) 9.4 KB
######################################################################################################
# An simple add-on to auto cut in two and mirror an object #
# Actualy partialy uncommented (see further version) #
# Author: Lapineige #
# License: GPL v3 #
######################################################################################################
############# Add-on description (used by Blender)
bl_info = {
"name": "Auto Mirror",
"description": "Super fast cutting and mirroring for mesh",
"author": "Lapineige",
"version": (2, 4),
"blender": (2, 7, 1),
"location": "View 3D > Toolbar > Tools tab > AutoMirror (panel)",
"warning": "",
"wiki_url": "http://www.le-terrier-de-lapineige.over-blog.com",
"tracker_url": "http://blenderlounge.fr/forum/viewtopic.php?f=18&p=7103#p7103",
"category": "Mesh"}
#############
import bpy
from mathutils import Vector
bpy.types.Scene.AutoMirror_axis = bpy.props.EnumProperty(items = [("x", "X", "", 1),("y", "Y", "", 2),("z", "Z", "", 3)], description="Axis used by the mirror modifier")
bpy.types.Scene.AutoMirror_orientation = bpy.props.EnumProperty(items = [("positive", "Positive", "", 1),("negative", "Negative", "", 2)], description="Choose the side along the axis of the editable part (+/- coordinates)")
bpy.types.Scene.AutoMirror_threshold = bpy.props.FloatProperty(default= 0.001, min= 0.001, description="Vertices closer than this distance are merged on the loopcut")
bpy.types.Scene.AutoMirror_toggle_edit = bpy.props.BoolProperty(default= True, description="If not in edit mode, change mode to edit")
bpy.types.Scene.AutoMirror_cut = bpy.props.BoolProperty(default= True, description="If enabeled, cut the mesh in two parts and mirror it. If not, just make a loopcut")
bpy.types.Scene.AutoMirror_clipping = bpy.props.BoolProperty(default=True)
bpy.types.Scene.AutoMirror_use_clip = bpy.props.BoolProperty(default=True, description="Use clipping for the mirror modifier")
bpy.types.Scene.AutoMirror_show_on_cage = bpy.props.BoolProperty(default=True, description="Enable to edit the cage (it's the classical modifier's option)")
bpy.types.Scene.AutoMirror_apply_mirror = bpy.props.BoolProperty(description="Apply the mirror modifier (useful to symmetrise the mesh)")
############### Operator
class AlignVertices(bpy.types.Operator):
""" """
bl_idname = "object.align_vertices"
bl_label = "Align Vertices on 1 Axis"
@classmethod
def poll(cls, context):
return True
def execute(self, context):
bpy.ops.object.mode_set(mode = 'OBJECT')
x1,y1,z1 = bpy.context.scene.cursor_location
bpy.ops.view3d.snap_cursor_to_selected()
x2,y2,z2 = bpy.context.scene.cursor_location
bpy.context.scene.cursor_location[0],bpy.context.scene.cursor_location[1],bpy.context.scene.cursor_location[2] = 0,0,0
#Vertices coordinate to 0 (local coordinate, so on the origin)
for vert in bpy.context.object.data.vertices:
if vert.select:
if bpy.context.scene.AutoMirror_axis == 'x':
axis = 0
elif bpy.context.scene.AutoMirror_axis == 'y':
axis = 1
elif bpy.context.scene.AutoMirror_axis == 'z':
axis = 2
vert.co[axis] = 0
#
bpy.context.scene.cursor_location = x2,y2,z2
bpy.ops.object.origin_set(type='ORIGIN_CURSOR')
bpy.context.scene.cursor_location = x1,y1,z1
bpy.ops.object.mode_set(mode = 'EDIT')
return {'FINISHED'}
class AutoMirror(bpy.types.Operator):
""" Automatically cut an object along an axis """
bl_idname = "object.automirror"
bl_label = "AutoMirror"
bl_options = {'REGISTER'} # 'UNDO' ?
@classmethod
def poll(cls, context):
return True
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):
loc = context.object.location
bpy.ops.object.mode_set(mode="OBJECT") # Needed to avoid to translate vertices
v1 = Vector((loc[0],loc[1],loc[2]))
bpy.ops.transform.translate(value=(X*orientation, Y*orientation, Z*orientation), constraint_axis=((X==1), (Y==1), (Z==1)), constraint_orientation='LOCAL')
v2 = Vector((loc[0],loc[1],loc[2]))
bpy.ops.transform.translate(value=(-X*orientation, -Y*orientation, -Z*orientation), constraint_axis=((X==1), (Y==1), (Z==1)), constraint_orientation='LOCAL')
bpy.ops.object.mode_set(mode="EDIT")
return v2-v1
def execute(self, context):
X,Y,Z = 0,0,0
if bpy.context.scene.AutoMirror_axis == 'x':
X = 1
elif bpy.context.scene.AutoMirror_axis == 'y':
Y = 1
elif bpy.context.scene.AutoMirror_axis == 'z':
Z = 1
current_mode = bpy.context.object.mode # Save the current mode
if bpy.context.object.mode != "EDIT":
bpy.ops.object.mode_set(mode="EDIT") # Go to edit mode
bpy.ops.mesh.select_all(action='SELECT') # Select all the vertices
if bpy.context.scene.AutoMirror_orientation == 'positive':
orientation = 1
else:
orientation = -1
cut_normal = self.get_local_axis_vector(context, X, Y, Z, orientation)
bpy.ops.mesh.bisect(plane_co=(bpy.context.object.location[0], bpy.context.object.location[1], bpy.context.object.location[2]), plane_no=cut_normal, use_fill= False, clear_inner= bpy.context.scene.AutoMirror_cut, clear_outer= 0, threshold= bpy.context.scene.AutoMirror_threshold) # Cut the mesh
bpy.ops.object.align_vertices() # Use to align the vertices on the origin, needed by the "threshold"
if not bpy.context.scene.AutoMirror_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
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_y = Y
bpy.context.object.modifiers[-1].use_z = Z
bpy.context.object.modifiers[-1].use_clip = context.scene.AutoMirror_use_clip
bpy.context.object.modifiers[-1].show_on_cage = context.scene.AutoMirror_show_on_cage
if bpy.context.scene.AutoMirror_apply_mirror:
bpy.ops.object.mode_set(mode='OBJECT')
bpy.ops.object.modifier_apply(apply_as= 'DATA', modifier= bpy.context.object.modifiers[-1].name)
if bpy.context.scene.AutoMirror_toggle_edit:
bpy.ops.object.mode_set(mode='EDIT')
else:
bpy.ops.object.mode_set(mode=current_mode)
return {'FINISHED'}
#################### Panel
class BisectMirror(bpy.types.Panel):
""" The AutoMirror panel on the toolbar tab 'Tools' """
bl_label = "Auto Mirror"
bl_space_type = 'VIEW_3D'
bl_region_type = 'TOOLS'
bl_category = "Tools"
def draw(self, context):
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:
row = layout.row()
row.prop(context.scene, "AutoMirror_use_clip", text="Use Clip")
row.prop(context.scene, "AutoMirror_show_on_cage", text="Editable")
layout.prop(context.scene, "AutoMirror_apply_mirror", text="Apply Mirror")
else:
layout.label(icon="ERROR", text="No mesh selected")
def register():
bpy.utils.register_class(BisectMirror)
bpy.utils.register_class(AutoMirror)
bpy.utils.register_class(AlignVertices)
def unregister():
bpy.utils.unregister_class(BisectMirror)
bpy.utils.unregister_class(AutoMirror)
bpy.utils.unregister_class(AlignVertices)
if __name__ == "__main__":
register()