1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
|
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software Foundation,
# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
"""
Symmetry Tools: Find Asymmetric + Make Symmetric (by Sergey Sharybin)
Our character wasn’t completely symmetric in some parts where it was
supposed to, this could be by moving vertices by mistake or just reasons.
To fix this in a fast way, Sergey coded this two super useful tools:
* Find Asymmetric:
Selects vertices that don’t have the same position on the opposite side.
* Make Symmetric:
Move selected vertices to match the position of those on the other side.
This tools may not apply on every single model out there, but I tried it
in many different characters and it worked. So probably better use it on
those models that were already symmetric at some point, modeled with a
mirror modifier or so.
Search (spacebar) for "Find Asymmetric", and "Make Symmetric""Settings".
> Developed during Caminandes Open Movie Project
"""
import bpy
import bmesh
from mathutils import Vector
class AMTH_MESH_OT_find_asymmetric(bpy.types.Operator):
"""
Find asymmetric vertices
"""
bl_idname = "mesh.find_asymmetric"
bl_label = "Find Asymmetric"
bl_options = {"UNDO", "REGISTER"}
@classmethod
def poll(cls, context):
object = context.object
if object:
return object.mode == "EDIT" and object.type == "MESH"
return False
def execute(self, context):
threshold = 1e-6
object = context.object
bm = bmesh.from_edit_mesh(object.data)
# Deselect all the vertices
for v in bm.verts:
v.select = False
for v1 in bm.verts:
if abs(v1.co[0]) < threshold:
continue
mirror_found = False
for v2 in bm.verts:
if v1 == v2:
continue
if v1.co[0] * v2.co[0] > 0.0:
continue
mirror_coord = Vector(v2.co)
mirror_coord[0] *= -1
if (mirror_coord - v1.co).length_squared < threshold:
mirror_found = True
break
if not mirror_found:
v1.select = True
bm.select_flush_mode()
bmesh.update_edit_mesh(object.data)
return {"FINISHED"}
class AMTH_MESH_OT_make_symmetric(bpy.types.Operator):
"""
Make symmetric
"""
bl_idname = "mesh.make_symmetric"
bl_label = "Make Symmetric"
bl_options = {"UNDO", "REGISTER"}
@classmethod
def poll(cls, context):
object = context.object
if object:
return object.mode == "EDIT" and object.type == "MESH"
return False
def execute(self, context):
threshold = 1e-6
object = context.object
bm = bmesh.from_edit_mesh(object.data)
for v1 in bm.verts:
if v1.co[0] < threshold:
continue
if not v1.select:
continue
closest_vert = None
closest_distance = -1
for v2 in bm.verts:
if v1 == v2:
continue
if v2.co[0] > threshold:
continue
if not v2.select:
continue
mirror_coord = Vector(v2.co)
mirror_coord[0] *= -1
distance = (mirror_coord - v1.co).length_squared
if closest_vert is None or distance < closest_distance:
closest_distance = distance
closest_vert = v2
if closest_vert:
closest_vert.select = False
closest_vert.co = Vector(v1.co)
closest_vert.co[0] *= -1
v1.select = False
for v1 in bm.verts:
if v1.select:
closest_vert = None
closest_distance = -1
for v2 in bm.verts:
if v1 != v2:
mirror_coord = Vector(v2.co)
mirror_coord[0] *= -1
distance = (mirror_coord - v1.co).length_squared
if closest_vert is None or distance < closest_distance:
closest_distance = distance
closest_vert = v2
if closest_vert:
v1.select = False
v1.co = Vector(closest_vert.co)
v1.co[0] *= -1
bm.select_flush_mode()
bmesh.update_edit_mesh(object.data)
return {"FINISHED"}
def ui_symmetry_tools(self, context):
if bpy.context.mode == 'EDIT_MESH':
self.layout.separator()
self.layout.operator(
AMTH_MESH_OT_find_asymmetric.bl_idname,
icon="ALIGN_CENTER", text="Find Asymmetric")
self.layout.operator(
AMTH_MESH_OT_make_symmetric.bl_idname,
icon="ALIGN_JUSTIFY", text="Make Symmetric")
def register():
bpy.utils.register_class(AMTH_MESH_OT_find_asymmetric)
bpy.utils.register_class(AMTH_MESH_OT_make_symmetric)
bpy.types.VIEW3D_MT_edit_mesh.append(ui_symmetry_tools)
def unregister():
bpy.utils.unregister_class(AMTH_MESH_OT_find_asymmetric)
bpy.utils.unregister_class(AMTH_MESH_OT_make_symmetric)
bpy.types.VIEW3D_MT_edit_mesh.remove(ui_symmetry_tools)
|