def0b8a31d4cd174aa8da5031e4d102886bf30dc
[blender-addons-contrib.git] / mesh_fix_vertex_groups.py
1 # ##### BEGIN GPL LICENSE BLOCK #####
2 #
3 #  This program is free software; you can redistribute it and/or
4 #  modify it under the terms of the GNU General Public License
5 #  as published by the Free Software Foundation; either version 2
6 #  of the License, or (at your option) any later version.
7 #
8 #  This program is distributed in the hope that it will be useful,
9 #  but WITHOUT ANY WARRANTY; without even the implied warranty of
10 #  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11 #  GNU General Public License for more details.
12 #
13 #  You should have received a copy of the GNU General Public License
14 #  along with this program; if not, write to the Free Software Foundation,
15 #  Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
16 #
17 # ##### END GPL LICENSE BLOCK #####
18
19 """
20 This script finds all the vertex groups that chosen vertexes are in, and any vertexes that are not in any vertex group.
21
22 This is useful for cleaning up a mesh if vertex groups under animation go off to the wrong place (because they are in a vertex group that they should not be in, or not in the vertex group that they should be in).
23
24 How to use:
25 1. select a mesh and get into edit mode.
26 2. by default it will use all vertexes; alternatively, select vertexes of interest and click on 'use selected vertexes'. Note that subsequent selections and deselections of vertexes will not change the set of vertexes to be used, you need to click on these buttons again to do that.
27 3. click on 'select' and 'deselect' buttons for listed vertex groups, and for no vertex group, to select and deselect vertexes.
28
29 This only lists vertex groups that have used vertexes.
30
31 You may want to use the mesh select/deselect all (keyboard A) operator to start.
32
33 Once you have the right vertexes selected, you can use the standard vertex groups property editor to add them to or remove them from the desired vertex groups.
34 """
35
36 bl_addon_info = {
37     'name': 'Fix Vertex Groups',
38     'author': 'Martin Ellison',
39     'version': (1, 0),
40     'blender': (2, 5, 4),
41     'api': 32516,
42     'location': 'Toolbox',
43     'description': 'Finds all the vertex groups that chosen verts are in, & any verts that are not in any group',
44     'warning': '', # used for warning icon and text in addons panel
45     'wiki_url': '',
46     'tracker_url': 'https://projects.blender.org/tracker/index.php?'\
47         'func=detail&aid=22025&group_id=153&atid=468',
48     'category': 'Mesh'}
49
50 import bpy
51 from bpy.props import *
52
53 global use_selected_only, used_vertexes, the_mesh, vertex_usage
54 use_selected_only = False
55 used_vertexes = set()
56 the_mesh = None
57 vertex_usage = ''
58
59 class UseAll(bpy.types.Operator):
60     bl_idname = "mesh.primitive_fvg_useall"
61     bl_label = "Use all Vertexes"
62     bl_register = True
63     bl_undo = True
64     #limit = FloatProperty(name="limit", description="Ignore weights under this limit.", default= 0.01, min = 0.0, max = 1.0, soft_min=0.0, soft_max=1.0)
65
66     def execute(self, context):
67         global use_selected_only
68         use_selected_only = False
69         bpy.ops.object.editmode_toggle()
70         set_used()
71         bpy.ops.object.editmode_toggle()
72         return {'FINISHED'}
73
74 class UseSelected(bpy.types.Operator):
75     bl_idname = "mesh.primitive_fvg_useselected"
76     bl_label = "Use Selected Vertexes"
77     bl_register = True
78     bl_undo = True
79
80     def execute(self, context):
81         global use_selected_only
82         use_selected_only = True
83         bpy.ops.object.editmode_toggle()
84         set_used()
85         bpy.ops.object.editmode_toggle()
86         return {'FINISHED'}
87
88 class SelectFound(bpy.types.Operator):
89     bl_idname = "mesh.primitive_fvg_selfound"
90     bl_label = "Select"
91     bl_register = True
92     bl_undo = True
93     vertexgroup = bpy.props.StringProperty(name = 'vertexgroup', description = 'vertexgroup', default = '', options = set())
94
95     def execute(self, context):
96         global the_mesh
97         bpy.ops.object.editmode_toggle()
98         vertexgroup = self.properties.vertexgroup
99         fv = found_verts(vertexgroup)
100         for v in fv: v.select = True
101         bpy.ops.object.editmode_toggle()
102         return {'FINISHED'}
103
104 class DeselectFound(bpy.types.Operator):
105     bl_idname = "mesh.primitive_fvg_deselfound"
106     bl_label = "Deselect"
107     bl_register = True
108     bl_undo = True
109     vertexgroup = bpy.props.StringProperty(name = 'vertexgroup', description = 'vertexgroup', default = '', options = set())
110
111     def execute(self, context):
112         global the_mesh
113         bpy.ops.object.editmode_toggle()
114         vertexgroup = self.properties.vertexgroup
115         fv = found_verts(vertexgroup)
116         for v in fv: v.select = False
117         bpy.ops.object.editmode_toggle()
118         return {'FINISHED'}
119
120 def set_used():
121         global use_selected_only, used_vertexes, the_mesh, vertex_usage
122         obj = bpy.context.active_object
123         used_vertexes = set()
124         if use_selected_only:
125                 for v in obj.data.vertices:
126                         if v.select: used_vertexes.add(v.index)
127         else:
128                 for v in obj.data.vertices: used_vertexes.add(v.index)
129         the_mesh = obj
130         vertex_usage = '%d vertexes used' % (len(used_vertexes))
131
132
133 def make_groups(limit):
134         global used_vertexes
135         vgp = []
136         vgdict = {}
137         vgused = {}
138         obj = bpy.context.active_object
139         all_in_group = True
140         for vg in obj.vertex_groups:
141                 vgdict[vg.index] = vg.name
142         for v in obj.data.vertices:
143                 in_group = False
144                 if v.index in used_vertexes:
145                         for g in v.groups:
146                                 gr = g.group
147                                 w = g.weight
148                                 if w > limit:
149                                         if not gr in vgused: vgused[gr] = 0
150                                         vgused[gr] += 1
151                                         in_group = True
152                 if not in_group: all_in_group = False
153         if not all_in_group:
154                 vgp.append(("no group", "(No group)"))
155         for gn in vgused.keys():
156                 name = vgdict[gn]
157                 vgp.append((name, '%s has %d vertexes' % (name, vgused[gn]) ))
158         print("%d groups found\n" % len(vgp))
159         return vgp
160
161 def found_verts(vertex_group):
162         global used_vertexes
163         vgfound = []
164         obj = bpy.context.active_object
165         if vertex_group == 'no group':
166                 for v in obj.data.vertices:
167                         if v.index in used_vertexes and len(v.groups) == 0:
168                                 vgfound.append(v)
169         else:
170                 vgnum = -1
171                 for vg in obj.vertex_groups:
172                         if vg.name == vertex_group: vgnum = vg.index
173                 for v in obj.data.vertices:
174                         if v.index in used_vertexes:
175                                 found = False
176                                 for g in v.groups:
177                                         if g.group == vgnum: found = True
178                                 if found: vgfound.append(v)
179         print('%d vertexes found for %s' % (len(vgfound), vertex_group))
180         return vgfound
181
182
183 class VIEW3D_PT_FixVertexGroups(bpy.types.Panel):
184     bl_space_type = "VIEW_3D"
185     bl_region_type = "TOOLS"
186     bl_label = "Fix Vertex Groups"
187
188     @classmethod
189     def poll(self, context):
190         if bpy.context.active_object:
191            obj = bpy.context.active_object
192            if obj.type == 'MESH' and obj.mode == 'EDIT': return True
193         return False
194
195     def draw(self, context):
196         global use_selected_only, used_vertexes, the_mesh, vertex_usage
197
198         if bpy.context.active_object:
199            obj = bpy.context.active_object
200            if obj.type == 'MESH' and obj.mode == 'EDIT':
201                 layout = self.layout
202                 use_all = layout.operator("mesh.primitive_fvg_useall", "Use all vertexes")
203                 layout.operator("mesh.primitive_fvg_useselected", "Use selected vertexes")
204                 if use_selected_only:
205                     layout.label(text = 'Using selected vertexes.')
206                 else:
207                     layout.label(text = 'Using all vertexes.')
208                 layout.label(vertex_usage)
209                 if len(used_vertexes) == 0 or obj is not the_mesh: set_used()
210                 #layout.prop(use_all, 'limit', slider = True)
211                 #groups = make_groups(use_all.limitval)
212                 groups = make_groups(0.01)
213                 for gp in groups:
214                         layout.label(text = gp[1])
215                         row = layout.row()
216                         sel_op = row.operator("mesh.primitive_fvg_selfound", "Select")
217                         sel_op.vertexgroup = gp[0]
218                         desel_op = row.operator("mesh.primitive_fvg_deselfound", "Deselect")
219                         desel_op.vertexgroup = gp[0]
220
221 classes = [UseAll, UseSelected, SelectFound, DeselectFound]
222
223 def register():
224     pass
225
226 def unregister():
227     pass
228
229 if __name__ == "__main__":
230     print('------ executing --------')
231     register()