Newer
Older
# ########################################################### #
# An simple add-on to auto cut in two and mirror an object #
# Actually partially uncommented (see further version) #
# Author: Lapineige #
# License: GPL v3 #
# ########################################################### #
bl_info = {
"name": "Auto Mirror",
"description": "Super fast cutting and mirroring for Mesh objects",
"location": "View 3D > Toolbar > Tools tab > AutoMirror (panel)",
"warning": "",
"wiki_url": "https://wiki.blender.org/index.php/Extensions:2.6/"
"Py/Scripts/Modeling/AutoMirror",
"category": "Mesh"}
import bpy
from bpy.props import (
BoolProperty,
EnumProperty,
FloatProperty,
PointerProperty,
)
from bpy.types import (
Operator,
Panel,
PropertyGroup,
)
from mathutils import Vector
# Operators
class AlignVertices(Operator):
bl_idname = "object.align_vertices"
bl_label = "Align Vertices on an Axis"
bl_description = ("Align Vertices on an Axis\n"
"Needs an Active Mesh Object")
@classmethod
def poll(cls, context):
obj = context.active_object
return obj and obj.type == "MESH"
def execute(self, context):
auto_m = context.scene.auto_mirror
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:
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'}
bl_idname = "object.automirror"
bl_label = "AutoMirror"
bl_description = ("Automatically cut an object along an axis\n"
"Needs an Active Mesh Object")
bl_options = {'REGISTER'}
@classmethod
def poll(cls, context):
obj = context.active_object
return obj and obj.type == "MESH"
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]))
value=(X * orientation, Y * orientation, Z * orientation),
constraint_axis=((X == 1), (Y == 1), (Z == 1)),
orient_type='LOCAL'
)
v2 = Vector((loc[0], loc[1], loc[2]))
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")
def execute(self, context):
auto_m = context.scene.auto_mirror
X, Y, Z = 0, 0, 0
if auto_m.axis == 'x':
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 auto_m.orientation == 'positive':
orientation = 1
else:
orientation = -1
cut_normal = self.get_local_axis_vector(context, X, Y, Z, orientation)
# Cut the mesh
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=auto_m.cut,
clear_outer=0,
threshold=auto_m.threshold
)
# Use to align the vertices on the origin, needed by the "threshold"
bpy.ops.object.align_vertices()
if not auto_m.toggle_edit:
bpy.ops.object.mode_set(mode=current_mode) # Reload previous mode
if auto_m.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 = auto_m.use_clip
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.modifier_apply(
apply_as='DATA',
modifier=bpy.context.object.modifiers[-1].name
)
if auto_m.toggle_edit:
bpy.ops.object.mode_set(mode='EDIT')
else:
bpy.ops.object.mode_set(mode=current_mode)
return {'FINISHED'}
# Panel
class BisectMirror(Panel):
bl_label = "Auto Mirror"
bl_space_type = 'VIEW_3D'
bl_region_type = 'TOOLS'
bl_category = "Tools"
def draw(self, context):
layout = self.layout
auto_m = context.scene.auto_mirror
obj = context.active_object
if obj and obj.type == 'MESH':
layout.operator("object.automirror", icon="MOD_MIRROR")
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)
layout.label(icon="INFO", text="No Mesh selected")
class AutoMirrorProperties(PropertyGroup):
name="Axis",
items=[
("x", "X", "", 1),
("y", "Y", "", 2),
("z", "Z", "", 3)
],
description="Axis used by the mirror modifier"
)
name="Orientation",
items=[
("positive", "Positive", "", 1),
("negative", "Negative", "", 2)
],
description="Choose the side along the axis of the editable part (+/- coordinates)"
)
default=0.001,
min=0.001,
description="Vertices closer than this distance are merged on the loopcut"
)
name="Toggle Edit Mode",
default=True,
description="If not in Edit mode, change mode to it"
)
name="Cut",
default=True,
description="If enabled, cut the mesh in two parts and mirror it\n"
"If not, just make a loopcut"
)
default=True,
description="Use clipping for the mirror modifier"
)
default=True,
description="Enable editing the cage (it's the classical modifier's option)"
)
description="Apply the mirror modifier (useful to symmetrise the mesh)"
)
def register():
bpy.utils.register_class(BisectMirror)
bpy.utils.register_class(AutoMirror)
bpy.utils.register_class(AlignVertices)
bpy.utils.register_class(AutoMirrorProperties)
bpy.types.Scene.auto_mirror = PointerProperty(
type=AutoMirrorProperties
)
def unregister():
bpy.utils.unregister_class(BisectMirror)
bpy.utils.unregister_class(AutoMirror)
bpy.utils.unregister_class(AlignVertices)
bpy.utils.unregister_class(AutoMirrorProperties)
del bpy.types.Scene.auto_mirror
if __name__ == "__main__":
register()