1 # ########################################################### #
2 # An simple add-on to auto cut in two and mirror an object #
3 # Actualy partialy uncommented (see further version) #
6 # ########################################################### #
10 "description": "Super fast cutting and mirroring for Mesh objects",
11 "author": "Lapineige",
14 "location": "View 3D > Toolbar > Tools tab > AutoMirror (panel)",
16 "wiki_url": "https://wiki.blender.org/index.php/Extensions:2.6/"
17 "Py/Scripts/Modeling/AutoMirror",
22 from bpy
.props
import (
28 from bpy
.types
import (
33 from mathutils
import Vector
37 class AlignVertices(Operator
):
38 bl_idname
= "object.align_vertices"
39 bl_label
= "Align Vertices on an Axis"
40 bl_description
= ("Align Vertices on an Axis\n"
41 "Needs an Active Mesh Object")
44 def poll(cls
, context
):
45 obj
= context
.active_object
46 return obj
and obj
.type == "MESH"
48 def execute(self
, context
):
49 auto_m
= context
.scene
.auto_mirror
50 bpy
.ops
.object.mode_set(mode
='OBJECT')
52 x1
, y1
, z1
= bpy
.context
.scene
.cursor_location
53 bpy
.ops
.view3d
.snap_cursor_to_selected()
55 x2
, y2
, z2
= bpy
.context
.scene
.cursor_location
57 bpy
.context
.scene
.cursor_location
[0], \
58 bpy
.context
.scene
.cursor_location
[1], \
59 bpy
.context
.scene
.cursor_location
[2] = 0, 0, 0
61 # Vertices coordinate to 0 (local coordinate, so on the origin)
62 for vert
in bpy
.context
.object.data
.vertices
:
64 if auto_m
.axis
== 'x':
66 elif auto_m
.axis
== 'y':
68 elif auto_m
.axis
== 'z':
72 bpy
.context
.scene
.cursor_location
= x2
, y2
, z2
73 bpy
.ops
.object.origin_set(type='ORIGIN_CURSOR')
75 bpy
.context
.scene
.cursor_location
= x1
, y1
, z1
76 bpy
.ops
.object.mode_set(mode
='EDIT')
81 class AutoMirror(Operator
):
82 bl_idname
= "object.automirror"
83 bl_label
= "AutoMirror"
84 bl_description
= ("Automatically cut an object along an axis\n"
85 "Needs an Active Mesh Object")
86 bl_options
= {'REGISTER'}
89 def poll(cls
, context
):
90 obj
= context
.active_object
91 return obj
and obj
.type == "MESH"
93 def get_local_axis_vector(self
, context
, X
, Y
, Z
, orientation
):
94 loc
= context
.object.location
95 bpy
.ops
.object.mode_set(mode
="OBJECT") # Needed to avoid to translate vertices
97 v1
= Vector((loc
[0], loc
[1], loc
[2]))
98 bpy
.ops
.transform
.translate(
99 value
=(X
* orientation
, Y
* orientation
, Z
* orientation
),
100 constraint_axis
=((X
== 1), (Y
== 1), (Z
== 1)),
101 constraint_orientation
='LOCAL'
103 v2
= Vector((loc
[0], loc
[1], loc
[2]))
104 bpy
.ops
.transform
.translate(
105 value
=(-X
* orientation
, -Y
* orientation
, -Z
* orientation
),
106 constraint_axis
=((X
== 1), (Y
== 1), (Z
== 1)),
107 constraint_orientation
='LOCAL'
110 bpy
.ops
.object.mode_set(mode
="EDIT")
113 def execute(self
, context
):
114 auto_m
= context
.scene
.auto_mirror
117 if auto_m
.axis
== 'x':
119 elif auto_m
.axis
== 'y':
121 elif auto_m
.axis
== 'z':
124 current_mode
= bpy
.context
.object.mode
# Save the current mode
126 if bpy
.context
.object.mode
!= "EDIT":
127 bpy
.ops
.object.mode_set(mode
="EDIT") # Go to edit mode
129 bpy
.ops
.mesh
.select_all(action
='SELECT') # Select all the vertices
130 if auto_m
.orientation
== 'positive':
134 cut_normal
= self
.get_local_axis_vector(context
, X
, Y
, Z
, orientation
)
139 bpy
.context
.object.location
[0],
140 bpy
.context
.object.location
[1],
141 bpy
.context
.object.location
[2]
145 clear_inner
=auto_m
.cut
,
147 threshold
=auto_m
.threshold
150 # Use to align the vertices on the origin, needed by the "threshold"
151 bpy
.ops
.object.align_vertices()
153 if not auto_m
.toggle_edit
:
154 bpy
.ops
.object.mode_set(mode
=current_mode
) # Reload previous mode
157 bpy
.ops
.object.modifier_add(type='MIRROR') # Add a mirror modifier
158 bpy
.context
.object.modifiers
[-1].use_x
= X
# Choose the axis to use, based on the cut's axis
159 bpy
.context
.object.modifiers
[-1].use_y
= Y
160 bpy
.context
.object.modifiers
[-1].use_z
= Z
161 bpy
.context
.object.modifiers
[-1].use_clip
= auto_m
.use_clip
162 bpy
.context
.object.modifiers
[-1].show_on_cage
= auto_m
.show_on_cage
164 if auto_m
.apply_mirror
:
165 bpy
.ops
.object.mode_set(mode
='OBJECT')
166 bpy
.ops
.object.modifier_apply(
168 modifier
=bpy
.context
.object.modifiers
[-1].name
170 if auto_m
.toggle_edit
:
171 bpy
.ops
.object.mode_set(mode
='EDIT')
173 bpy
.ops
.object.mode_set(mode
=current_mode
)
179 class BisectMirror(Panel
):
180 bl_label
= "Auto Mirror"
181 bl_space_type
= 'VIEW_3D'
182 bl_region_type
= 'TOOLS'
183 bl_category
= "Tools"
184 bl_options
= {"DEFAULT_CLOSED"}
186 def draw(self
, context
):
188 auto_m
= context
.scene
.auto_mirror
189 obj
= context
.active_object
191 if obj
and obj
.type == 'MESH':
192 layout
.operator("object.automirror", icon
="MOD_MIRROR")
193 layout
.label("Options:")
194 layout
.prop(auto_m
, "axis", text
="Mirror Axis", expand
=True)
195 layout
.prop(auto_m
, "orientation", text
="Orientation")
196 layout
.prop(auto_m
, "threshold", text
="Threshold")
197 layout
.prop(auto_m
, "toggle_edit", text
="Toggle Edit")
198 layout
.prop(auto_m
, "cut", text
="Cut and Mirror", toggle
=True, icon
="MOD_REMESH")
201 col
= layout
.column(align
=True)
202 row
= col
.row(align
=True)
203 row
.prop(auto_m
, "use_clip", text
="Use Clip", toggle
=True)
204 row
.prop(auto_m
, "show_on_cage", text
="Editable", toggle
=True)
205 col
.prop(auto_m
, "apply_mirror", text
="Apply Mirror", toggle
=True)
207 layout
.label(icon
="INFO", text
="No Mesh selected")
210 class AutoMirrorProperties(PropertyGroup
):
218 description
="Axis used by the mirror modifier"
220 orientation
= EnumProperty(
223 ("positive", "Positive", "", 1),
224 ("negative", "Negative", "", 2)
226 description
="Choose the side along the axis of the editable part (+/- coordinates)"
228 threshold
= FloatProperty(
231 description
="Vertices closer than this distance are merged on the loopcut"
233 toggle_edit
= BoolProperty(
234 name
="Toggle Edit Mode",
236 description
="If not in Edit mode, change mode to it"
241 description
="If enabled, cut the mesh in two parts and mirror it\n"
242 "If not, just make a loopcut"
244 clipping
= BoolProperty(
247 use_clip
= BoolProperty(
249 description
="Use clipping for the mirror modifier"
251 show_on_cage
= BoolProperty(
253 description
="Enable editing the cage (it's the classical modifier's option)"
255 apply_mirror
= BoolProperty(
256 description
="Apply the mirror modifier (useful to symmetrise the mesh)"
261 bpy
.utils
.register_class(BisectMirror
)
262 bpy
.utils
.register_class(AutoMirror
)
263 bpy
.utils
.register_class(AlignVertices
)
264 bpy
.utils
.register_class(AutoMirrorProperties
)
265 bpy
.types
.Scene
.auto_mirror
= PointerProperty(
266 type=AutoMirrorProperties
271 bpy
.utils
.unregister_class(BisectMirror
)
272 bpy
.utils
.unregister_class(AutoMirror
)
273 bpy
.utils
.unregister_class(AlignVertices
)
274 bpy
.utils
.unregister_class(AutoMirrorProperties
)
275 del bpy
.types
.Scene
.auto_mirror
278 if __name__
== "__main__":