[Edit Linked Library] Removed no-longer-relevant comment
[blender-addons-contrib.git] / mesh_show_vgroup_weights.py
index 6dcba724a1bbbd7ac193fd6a249ea744fd145084..8227b87319a58930cee494c7c5cd6c17d5c0000b 100644 (file)
 
 bl_info = {
     "name": "Show Vertex Groups/Weights",
-    "author": "Jason van Gumster (Fweeb), Bartius Crouch",
-    "version": (0, 7, 1),
-    "blender": (2, 62, 3),
+    "author": "Jason van Gumster (Fweeb), Bartius Crouch, CoDEmanX",
+    "version": (0, 7, 2),
+    "blender": (2, 65, 4),
     "location": "3D View > Properties Region > Show Weights",
     "description": "Finds the vertex groups of a selected vertex and displays the corresponding weights",
-    "warning": "Requires bmesh",
-    "wiki_url": "http://wiki.blender.org/index.php?title=Extensions:2.6/Py/Scripts/Modeling/Show_Vertex_Group_Weights",
-    "tracker_url": "http://projects.blender.org/tracker/index.php?func=detail&aid=30609&group_id=153&atid=467",
+    "warning": "",
+    "wiki_url": "http://wiki.blender.org/index.php/Extensions:2.6/Py/"\
+        "Scripts/Modeling/Show_Vertex_Group_Weights",
+    "tracker_url": "http://projects.blender.org/tracker/index.php?"\
+        "func=detail&aid=30609",
     "category": "Mesh"}
 
 #TODO - Add button for selecting vertices with no groups
@@ -46,18 +48,18 @@ def calc_callback(self, context):
     # get color info from theme
     acol = context.user_preferences.themes[0].view_3d.editmesh_active
     tcol = (acol[0] * 0.85, acol[1] * 0.85, acol[2] * 0.85)
-    
+
     # get screen information
     mid_x = context.region.width / 2.0
     mid_y = context.region.height / 2.0
     width = context.region.width
     height = context.region.height
-    
+
     # get matrices
     view_mat = context.space_data.region_3d.perspective_matrix
     ob_mat = context.active_object.matrix_world
     total_mat = view_mat * ob_mat
-    
+
     # calculate location info
     texts = []
     locs = []
@@ -66,9 +68,28 @@ def calc_callback(self, context):
     bm = bmesh.from_edit_mesh(me)
     dvert_lay = bm.verts.layers.deform.active
 
+    verts_max = context.scene.show_vgroups_weights_limit
+    verts_used = []
+
+    for elem in reversed(bm.select_history):
+        if not isinstance(elem, bmesh.types.BMVert): #or elem.hide:
+            continue
+        if bm.select_history.active == elem:
+            locs.append([acol[0], acol[1], acol[2], elem.index, elem.co.to_4d()])
+        else:
+            locs.append([tcol[0], tcol[1], tcol[2], elem.index, elem.co.to_4d()])
+        dvert = elem[dvert_lay]
+        for vgroup in context.active_object.vertex_groups:
+            if vgroup.index in dvert.keys():
+                weights += [elem.index, vgroup.index, dvert[vgroup.index]]
+        verts_used.append(elem)
+        verts_max -= 1
+        if verts_max <= 0:
+            break
+
     for v in bm.verts:
-        if v.select: #XXX Should check v.hide here, but it doesn't work
-            if bm.select_mode == {'VERT'} and bm.select_history.active is not None and bm.select_history.active.index == v.index:
+        if v.select and v not in verts_used: #XXX Should check v.hide here, but it doesn't work
+            if isinstance(bm.select_history.active, bmesh.types.BMVert) and bm.select_history.active.index == v.index:
                 locs.append([acol[0], acol[1], acol[2], v.index, v.co.to_4d()])
             else:
                 locs.append([tcol[0], tcol[1], tcol[2], v.index, v.co.to_4d()])
@@ -76,6 +97,10 @@ def calc_callback(self, context):
             for vgroup in context.active_object.vertex_groups:
                 if vgroup.index in dvert.keys():
                     weights += [v.index, vgroup.index, dvert[vgroup.index]]
+            verts_max -= 1
+            if verts_max <= 0:
+                break
+
 
     for loc in locs:
         vec = total_mat * loc[4] # order is important
@@ -145,87 +170,61 @@ class ShowVGroupWeights(bpy.types.Operator):
     bl_idname = "view3d.show_vgroup_weights"
     bl_label = "Show Vertex Group Weights"
     bl_description = "Toggle the display of the vertex groups and weights for selected vertices"
-    
+
+    _handle_calc = None
+    _handle_draw = None
+
+    @staticmethod
+    def handle_add(self, context):
+        ShowVGroupWeights._handle_calc = bpy.types.SpaceView3D.draw_handler_add(
+            calc_callback, (self, context), 'WINDOW', 'POST_VIEW')
+        ShowVGroupWeights._handle_draw = bpy.types.SpaceView3D.draw_handler_add(
+            draw_callback, (self, context), 'WINDOW', 'POST_PIXEL')
+
+    @staticmethod
+    def handle_remove():
+        if ShowVGroupWeights._handle_calc is not None:
+            bpy.types.SpaceView3D.draw_handler_remove(ShowVGroupWeights._handle_calc, 'WINDOW')
+        if ShowVGroupWeights._handle_draw is not None:
+            bpy.types.SpaceView3D.draw_handler_remove(ShowVGroupWeights._handle_draw, 'WINDOW')
+        ShowVGroupWeights._handle_calc = None
+        ShowVGroupWeights._handle_draw = None
+
     @classmethod
     def poll(cls, context):
         return context.mode == 'EDIT_MESH'
-    
-    def __del__(self):
-        bpy.context.scene.display_indices = -1
-        clear_properties(full=False)
-    
-    def modal(self, context, event):
-        if context.area:
-            context.area.tag_redraw()
 
-        # removal of callbacks when operator is called again
-        if context.scene.display_indices == -1:
-            context.region.callback_remove(self.handle1)
-            context.region.callback_remove(self.handle2)
-            context.scene.display_indices = 0
-            return {'CANCELLED'}
-        
-        return {'PASS_THROUGH'}
-    
-    def invoke(self, context, event):
+    def execute(self, context):
         if context.area.type == 'VIEW_3D':
-            if context.scene.display_indices < 1:
-                # operator is called for the first time, start everything
-                context.scene.display_indices = 1
-                self.handle1 = context.region.callback_add(calc_callback,
-                    (self, context), 'POST_VIEW')
-                self.handle2 = context.region.callback_add(draw_callback,
-                    (self, context), 'POST_PIXEL')
-                context.window_manager.modal_handler_add(self)
-                return {'RUNNING_MODAL'}
+            if not context.scene.show_vgroups_weights:
+                # operator is called and not running, start everything
+                ShowVGroupWeights.handle_add(self, context)
+                context.scene.show_vgroups_weights = True
             else:
                 # operator is called again, stop displaying
-                context.scene.display_indices = -1
+                ShowVGroupWeights.handle_remove()
+                context.scene.show_vgroups_weights = False
                 clear_properties(full=False)
-                return {'RUNNING_MODAL'}
+            context.area.tag_redraw()
+            return {'FINISHED'}
         else:
             self.report({'WARNING'}, "View3D not found, can't run operator")
             return {'CANCELLED'}
 
-
-# properties used by the script
-class InitProperties(bpy.types.Operator):
-    bl_idname = "view3d.init_find_weights"
-    bl_label = "Initialize properties for vgroup weights finder"
-    
-    def execute(self, context):
-        bpy.types.Scene.display_indices = bpy.props.IntProperty(
-            name="Display indices",
-            default=0)
-        context.scene.display_indices = 0
-        return {'FINISHED'}
-
-
-# removal of ID-properties when script is disabled
-def clear_properties(full=True):
-    # can happen on reload
-    if bpy.context.scene is None:
-        return
-    
-    if "show_vgroup_verts" in bpy.context.active_object.data.keys():
-        del bpy.context.active_object.data["show_vgroup_verts"]
-    if "show_vgroup_weights" in bpy.context.active_object.data.keys():
-        del bpy.context.active_object.data["show_vgroup_weights"]
-    if full:
-        props = ["display_indices"]
-        for p in props:
-            if p in bpy.types.Scene.bl_rna.properties:
-                exec("del bpy.types.Scene." + p)
-            if p in bpy.context.scene.keys():
-                del bpy.context.scene[p]
-
+class VGroupsWeights(bpy.types.PropertyGroup):
+    vgroup = bpy.props.IntProperty()
+    weight = bpy.props.FloatProperty(min=0.0, max=1.0)
 
 class AssignVertexWeight(bpy.types.Operator):
     bl_idname = "mesh.vertex_group_assign"
     bl_label = "Assign Weights"
     bl_description = "Assign weights for all of the groups on a specific vertex"
 
-    vgroup_weights = bpy.props.StringProperty(name = "Vertex Group Weights")
+    index = bpy.props.IntProperty()
+
+    vgroup_weights = bpy.props.CollectionProperty(
+        description="Vertex Group Weights",
+        type=VGroupsWeights)
 
     @classmethod
     def poll(cls, context):
@@ -235,16 +234,17 @@ class AssignVertexWeight(bpy.types.Operator):
         me = context.active_object.data
         bm = bmesh.from_edit_mesh(me)
         dvert_lay = bm.verts.layers.deform.active
-        weights = eval(self.vgroup_weights) #XXX Would be nice if I didn't have to use an eval
+        weights = {}
+        for item in self.vgroup_weights:
+            weights[item.vgroup] = item.weight
 
         for v in bm.verts:
-            if v.index == weights["__index__"]:
-                del weights["__index__"]
+            if v.index == self.index:
                 dvert = v[dvert_lay]
                 for vgroup in dvert.keys():
                     dvert[vgroup] = weights[vgroup]
                 break
-
+        context.area.tag_redraw()
         return {'FINISHED'}
 
 
@@ -268,7 +268,7 @@ class RemoveFromVertexGroup(bpy.types.Operator):
         # Save current selection
         selected_verts = []
         for v in bm.verts:
-            if v.select == True:
+            if v.select is True:
                 selected_verts.append(v.index)
                 if v.index != self.vert_and_group[0]:
                     v.select = False
@@ -301,7 +301,9 @@ class AddToVertexGroup(bpy.types.Operator):
 
         dvert = bm.select_history.active[dvert_lay]
 
-        items.append(("-1", "New Vertex Group", "-1", -1))
+        #XXX since we need an identifier here, user won't be able to add a vgroup with that name ('-1')
+        #XXX could check against vgroup names and find an unused name, but it's a rare case after all.
+        items.append(("-1", "New Vertex Group", "Add a new vertex group to the active object", -1))
 
         for i in ob.vertex_groups:
             if i.index not in dvert.keys():
@@ -310,7 +312,7 @@ class AddToVertexGroup(bpy.types.Operator):
         return items
 
     vertex = bpy.props.IntProperty()
-    available_vgroups = bpy.props.EnumProperty(items = avail_vgroups, name = "Available Groups")
+    available_vgroups = bpy.props.EnumProperty(items=avail_vgroups, name="Available Groups")
 
     @classmethod
     def poll(cls, context):
@@ -325,7 +327,7 @@ class AddToVertexGroup(bpy.types.Operator):
         # Save current selection
         selected_verts = []
         for v in bm.verts:
-            if v.select == True:
+            if v.select is True:
                 selected_verts.append(v.index)
                 if v.index != self.vertex:
                     v.select = False
@@ -333,7 +335,7 @@ class AddToVertexGroup(bpy.types.Operator):
         weight = context.tool_settings.vertex_group_weight
         context.tool_settings.vertex_group_weight = 1.0
         if self.available_vgroups == "-1":
-            bpy.ops.object.vertex_group_assign(new = True) #XXX Assumes self.vertex is the active vertex
+            bpy.ops.object.vertex_group_assign(new=True) #XXX Assumes self.vertex is the active vertex
         else:
             bpy.ops.object.vertex_group_set_active(group = self.available_vgroups)
             bpy.ops.object.vertex_group_assign() #XXX Assumes self.vertex is the active vertex
@@ -367,10 +369,11 @@ class PanelShowWeights(bpy.types.Panel):
         bm = bmesh.from_edit_mesh(me)
         dvert_lay = bm.verts.layers.deform.active
 
-        if context.scene.display_indices < 1:
-            layout.operator(ShowVGroupWeights.bl_idname, text = "Show Weights Overlay")
-        else:
-            layout.operator(ShowVGroupWeights.bl_idname, text = "Hide Weights Overlay")
+        row = layout.row(align=True)
+
+        text = "Show" if not context.scene.show_vgroups_weights else "Hide"
+        row.operator(ShowVGroupWeights.bl_idname, text=text)
+        row.prop(context.scene, "show_vgroups_weights_limit")
 
         if len(ob.vertex_groups) > 0:
             # Active vertex
@@ -383,26 +386,33 @@ class PanelShowWeights(bpy.types.Panel):
                 row.label(text = "Vertex " + str(active_vert.index) + ":")
                 row.operator_menu_enum("mesh.vertex_group_add", "available_vgroups", text = "Add Group", icon = 'GROUP_VERTEX')
                 has_groups = False
-                vgroup_weights = {}
+                vgroup_weights = []
 
                 for i in me.vertices:
                     if i.index == active_vert.index:
-                        vgroup_weights["__index__"] = i.index
+                        vgroup_weights_index = i.index
                         for j in range(len(i.groups)):
                             for k in ob.vertex_groups:
                                 if k.index == i.groups[j].group:
                                     has_groups = True
                                     split = col.split(percentage = 0.90, align = True)
-                                    vgroup_weights[k.index] = i.groups[j].weight
+                                    vgroup_weights.append((k.index, i.groups[j].weight))
                                     row = split.row(align = True)
                                     row.prop(i.groups[j], "weight", text = k.name, slider = True, emboss = not k.lock_weight)
                                     row = split.row(align = True)
-                                    row.operator("mesh.vertex_group_remove", text = "R").vert_and_group = (i.index, k.index)
-                
+                                    row.operator("mesh.vertex_group_remove", text="", icon='X').vert_and_group = (i.index, k.index)
+
                 if not has_groups:
                     col.label(text = "    No Groups")
                 else:
-                    col.operator("mesh.vertex_group_assign").vgroup_weights = str(vgroup_weights)
+                    props = col.operator("mesh.vertex_group_assign")
+                    props.index = vgroup_weights_index
+
+                    for vgroup, weight in vgroup_weights:
+                        item = props.vgroup_weights.add()
+                        item.vgroup = vgroup
+                        item.weight = weight
+
                 layout.separator()
             else:
                 col.label(text = "No Active Vertex")
@@ -417,53 +427,80 @@ class PanelShowWeights(bpy.types.Panel):
                         col = sub.column(align = True)
                         col.label(text = "Vertex " + str(v.index) + ":")
                         has_groups = False
-                        vgroup_weights = {}
+                        vgroup_weights = []
                         for i in me.vertices:
                             if i.index == v.index:
-                                vgroup_weights["__index__"] = i.index
+                                vgroup_weights_index = i.index
                                 for j in range(len(i.groups)):
                                     for k in ob.vertex_groups:
                                         if k.index == i.groups[j].group:
                                             has_groups = True
                                             split = col.split(percentage = 0.90, align = True)
-                                            vgroup_weights[k.index] = i.groups[j].weight
+                                            vgroup_weights.append((k.index, i.groups[j].weight))
                                             row = split.row(align = True)
                                             row.prop(i.groups[j], "weight", text = k.name, slider = True, emboss = not k.lock_weight)
                                             row = split.row(align = True)
-                                            row.operator("mesh.vertex_group_remove", text = "R").vert_and_group = (i.index, k.index)
+                                            row.operator("mesh.vertex_group_remove", text="", icon='X').vert_and_group = (i.index, k.index)
                         if not has_groups:
                             col.label(text = "    No Groups")
                         else:
-                            col.operator("mesh.vertex_group_assign").vgroup_weights = str(vgroup_weights)
+                            props = col.operator("mesh.vertex_group_assign")
+                            props.index = vgroup_weights_index
+
+                            for vgroup, weight in vgroup_weights:
+                                item = props.vgroup_weights.add()
+                                item.vgroup = vgroup
+                                item.weight = weight
+
         else:
             layout.label(text = "No Groups")
 
 
-def register():
+def create_properties():
     bpy.types.WindowManager.show_vgroups_show_all = bpy.props.BoolProperty(
         name = "Show All Selected Vertices",
         description = "Show all vertices with vertex groups assigned to them",
-        default = False)
+        default=False)
+
     bpy.types.Mesh.assign_vgroup = bpy.props.StringProperty()
-    bpy.utils.register_class(ShowVGroupWeights)
-    bpy.utils.register_class(InitProperties)
-    bpy.ops.view3d.init_find_weights()
-    bpy.utils.register_class(AssignVertexWeight)
-    bpy.utils.register_class(RemoveFromVertexGroup)
-    bpy.utils.register_class(AddToVertexGroup)
-    bpy.utils.register_class(PanelShowWeights)
-    
+
+    bpy.types.Scene.show_vgroups_weights = bpy.props.BoolProperty(
+        name="Show Vertex Groups/Weights",
+        default=False)
+
+    bpy.types.Scene.show_vgroups_weights_limit = bpy.props.IntProperty(
+        name="Limit",
+        description="Maximum number of weight overlays to draw",
+        default=20,
+        min=1,
+        max=1000,
+        soft_max=100)
+
+# removal of ID-properties when script is disabled
+def clear_properties(full=True):
+
+    if bpy.context.active_object is not None:
+        me = bpy.context.active_object.data
+
+        if hasattr(me, "show_vgroup_verts"):
+            del me["show_vgroup_verts"]
+        if hasattr(me, "show_vgroup_weights"):
+            del me["show_vgroup_weights"]
+
+    if full:
+        del bpy.types.WindowManager.show_vgroups_show_all
+        del bpy.types.Mesh.assign_vgroup
+        del bpy.types.Scene.show_vgroups_weights
+        del bpy.types.Scene.show_vgroups_weights_limit
+
+def register():
+    bpy.utils.register_module(__name__)
+    create_properties()
 
 def unregister():
-    bpy.utils.unregister_class(ShowVGroupWeights)
-    bpy.utils.unregister_class(InitProperties)
+    ShowVGroupWeights.handle_remove()
+    bpy.utils.unregister_module(__name__)
     clear_properties()
-    bpy.utils.unregister_class(AssignVertexWeight)
-    bpy.utils.unregister_class(RemoveFromVertexGroup)
-    bpy.utils.unregister_class(AddToVertexGroup)
-    bpy.utils.unregister_class(PanelShowWeights)
-    del bpy.types.WindowManager.show_vgroups_show_all
-    del bpy.types.Mesh.assign_vgroup
 
 if __name__ == "__main__":
     register()