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