Welcome to mirror list, hosted at ThFree Co, Russian Federation.

symmetry_tools.py « modeling « amaranth - git.blender.org/blender-addons.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
blob: d23b4fb9b6f93fc26dbfddbaead61c4b148b2ed4 (plain)
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)