Add Easy_Weight
to Addons
#47
195
rogue_weights.py
195
rogue_weights.py
@ -17,6 +17,40 @@ All functionality can be found in the Sidebar->EasyWeight->Weight Islands panel.
|
||||
# TODO:
|
||||
# UIList: Filtering options, explanations as to what the numbers mean. Maybe a warning for Calculate Islands operator when the mesh has a lot of verts or vgroups.
|
||||
|
||||
class VertIndex(PropertyGroup):
|
||||
index: IntProperty()
|
||||
|
||||
class WeightIsland(PropertyGroup):
|
||||
vert_indicies: CollectionProperty(type=VertIndex) # TODO: Is this really needed?? Why can't a CollectionProperty(type=IntProperty) be fine??
|
||||
|
||||
class IslandGroup(PropertyGroup):
|
||||
name: StringProperty() # Name of the vertex group this set of island is associated with
|
||||
islands: CollectionProperty(type=WeightIsland)
|
||||
num_expected_islands: IntProperty(
|
||||
name="Expected Islands",
|
||||
default=1,
|
||||
min=1,
|
||||
description="Number of weight islands that have been marked as the expected amount by the user. If the real amount differs from this value, a warning appears"
|
||||
)
|
||||
index: IntProperty()
|
||||
|
||||
def update_vgroup_islands(mesh, vgroup, vert_index_map, island_groups, island_group=None) -> IslandGroup:
|
||||
islands = get_islands_of_vgroup(mesh, vgroup, vert_index_map)
|
||||
|
||||
if not island_group:
|
||||
island_group = island_groups.add()
|
||||
island_group.index = len(island_groups)-1
|
||||
island_group.name = vgroup.name
|
||||
else:
|
||||
island_group.islands.clear()
|
||||
for island in islands:
|
||||
island_storage = island_group.islands.add()
|
||||
for v_idx in island:
|
||||
v_idx_storage = island_storage.vert_indicies.add()
|
||||
v_idx_storage.index = v_idx
|
||||
|
||||
return island_group
|
||||
|
||||
def build_vert_index_map(mesh) -> dict:
|
||||
"""Build a dictionary of vertex indicies pointing to a list of other vertex indicies that the vertex is connected to by an edge."""
|
||||
|
||||
@ -81,45 +115,11 @@ def get_islands_of_vgroup(mesh: Mesh, vgroup: VertexGroup, vert_index_map: dict)
|
||||
islands.append(island)
|
||||
return islands
|
||||
|
||||
def update_vgroup_islands(mesh, vgroup, vert_index_map, island_groups, island_group=None) -> IslandGroup:
|
||||
islands = get_islands_of_vgroup(mesh, vgroup, vert_index_map)
|
||||
|
||||
if not island_group:
|
||||
island_group = island_groups.add()
|
||||
island_group.index = len(island_groups)-1
|
||||
island_group.name = vgroup.name
|
||||
else:
|
||||
island_group.islands.clear()
|
||||
for island in islands:
|
||||
island_storage = island_group.islands.add()
|
||||
for v_idx in island:
|
||||
v_idx_storage = island_storage.vert_indicies.add()
|
||||
v_idx_storage.index = v_idx
|
||||
|
||||
return island_group
|
||||
|
||||
def select_vertices(mesh: Mesh, vert_indicies: List[int]):
|
||||
assert bpy.context.mode != 'EDIT_MESH', "Object must not be in edit mode, otherwise vertex selection doesn't work!"
|
||||
for vi in vert_indicies:
|
||||
mesh.vertices[vi].select = True
|
||||
|
||||
class VertIndex(PropertyGroup):
|
||||
index: IntProperty()
|
||||
|
||||
class WeightIsland(PropertyGroup):
|
||||
vert_indicies: CollectionProperty(type=VertIndex) # TODO: Is this really needed?? Why can't a CollectionProperty(type=IntProperty) be fine??
|
||||
|
||||
class IslandGroup(PropertyGroup):
|
||||
name: StringProperty() # Name of the vertex group this set of island is associated with
|
||||
islands: CollectionProperty(type=WeightIsland)
|
||||
num_expected_islands: IntProperty(
|
||||
name="Expected Islands",
|
||||
default=1,
|
||||
min=1,
|
||||
description="Number of weight islands that have been marked as the expected amount by the user. If the real amount differs from this value, a warning appears"
|
||||
)
|
||||
index: IntProperty()
|
||||
|
||||
class MarkIslandsAsOkay(Operator):
|
||||
"""Mark this number of vertex islands to be the intended amount. Vertex group will be hidden from the list until this number changes"""
|
||||
bl_idname = "object.set_expected_island_count"
|
||||
@ -286,6 +286,83 @@ class CalculateWeightIslands(Operator):
|
||||
bpy.ops.object.mode_set(mode=org_mode)
|
||||
return {'FINISHED'}
|
||||
|
||||
class EASYWEIGHT_UL_weight_island_groups(UIList):
|
||||
@staticmethod
|
||||
def draw_header(layout):
|
||||
row = layout.row()
|
||||
split1 = row.split(factor=0.5)
|
||||
row1 = split1.row()
|
||||
row1.label(text="Vertex Group")
|
||||
row1.alignment = 'RIGHT'
|
||||
row1.label(text="|")
|
||||
row2 = split1.row()
|
||||
row2.label(text="Islands")
|
||||
|
||||
def filter_items(self, context, data, propname):
|
||||
flt_flags = []
|
||||
flt_neworder = []
|
||||
list_items = getattr(data, propname)
|
||||
|
||||
island_groups = getattr(data, propname)
|
||||
|
||||
helper_funcs = bpy.types.UI_UL_list
|
||||
|
||||
if self.filter_name:
|
||||
flt_flags = helper_funcs.filter_items_by_name(self.filter_name, self.bitflag_filter_item, island_groups, "name",
|
||||
reverse=self.use_filter_sort_reverse)
|
||||
|
||||
if not flt_flags:
|
||||
flt_flags = [self.bitflag_filter_item] * len(island_groups)
|
||||
|
||||
if self.use_filter_invert:
|
||||
for idx, flag in enumerate(flt_flags):
|
||||
flt_flags[idx] = 0 if flag else self.bitflag_filter_item
|
||||
|
||||
for idx, island_group in enumerate(island_groups):
|
||||
if len(island_group.islands) < 1:
|
||||
# Filter island groups with only 1 or 0 islands in them
|
||||
flt_flags[idx] = 0
|
||||
elif len(island_group.islands) == island_group.num_expected_islands:
|
||||
# Filter island groups with the expected number of islands in them
|
||||
flt_flags[idx] = 0
|
||||
|
||||
return flt_flags, flt_neworder
|
||||
|
||||
def draw_filter(self, context, layout):
|
||||
# Nothing much to say here, it's usual UI code...
|
||||
main_row = layout.row()
|
||||
row = main_row.row(align=True)
|
||||
|
||||
row.prop(self, 'filter_name', text="")
|
||||
row.prop(self, 'use_filter_invert', toggle=True, text="", icon='ARROW_LEFTRIGHT')
|
||||
|
||||
row = main_row.row(align=True)
|
||||
row.use_property_split=True
|
||||
row.use_property_decorate=False
|
||||
row.prop(self, 'use_filter_sort_alpha', toggle=True, text="")
|
||||
row.prop(self, 'use_filter_sort_reverse', toggle=True, text="", icon='SORT_ASC')
|
||||
|
||||
def draw_item(self, context, layout, data, item, icon, active_data, active_propname):
|
||||
island_group = item
|
||||
if self.layout_type in {'DEFAULT', 'COMPACT'}:
|
||||
icon = 'ERROR'
|
||||
num_islands = len(island_group.islands)
|
||||
if num_islands == island_group.num_expected_islands:
|
||||
icon = 'CHECKMARK'
|
||||
row = layout.row()
|
||||
split = row.split(factor=0.5)
|
||||
row1 = split.row()
|
||||
row1.label(text=island_group.name)
|
||||
row1.alignment = 'RIGHT'
|
||||
row1.label(text="|")
|
||||
row2 = split.row()
|
||||
row2.label(text=str(num_islands), icon=icon)
|
||||
op = row2.operator(FocusSmallestIsland.bl_idname, text="", icon='VIEWZOOM').vgroup = island_group.name
|
||||
row2.operator(MarkIslandsAsOkay.bl_idname, text="", icon='CHECKMARK').vgroup = island_group.name
|
||||
# TODO: Operator to mark current number of islands as the expected amount
|
||||
elif self.layout_type in {'GRID'}:
|
||||
pass
|
||||
|
||||
class EASYWEIGHT_PT_WeightIslands(Panel):
|
||||
"""Panel with utilities for detecting rogue weights."""
|
||||
bl_space_type = 'VIEW_3D'
|
||||
@ -306,8 +383,9 @@ class EASYWEIGHT_PT_WeightIslands(Panel):
|
||||
if len(island_groups)==0: return
|
||||
active_weight_islands = obj.island_groups[obj.active_islands_index]
|
||||
|
||||
row = layout.row()
|
||||
EASYWEIGHT_UL_weight_island_groups.draw_header(layout)
|
||||
|
||||
row = layout.row()
|
||||
row.template_list(
|
||||
'EASYWEIGHT_UL_weight_island_groups',
|
||||
'',
|
||||
@ -317,53 +395,6 @@ class EASYWEIGHT_PT_WeightIslands(Panel):
|
||||
'active_islands_index',
|
||||
)
|
||||
|
||||
class EASYWEIGHT_UL_weight_island_groups(UIList):
|
||||
def filter_items(self, context, data, propname):
|
||||
flt_flags = []
|
||||
flt_neworder = []
|
||||
list_items = getattr(data, propname)
|
||||
|
||||
island_groups = getattr(data, propname)
|
||||
|
||||
helper_funcs = bpy.types.UI_UL_list
|
||||
|
||||
if self.filter_name:
|
||||
flt_flags = helper_funcs.filter_items_by_name(self.filter_name, self.bitflag_filter_item, island_groups, "name",
|
||||
reverse=self.use_filter_name_reverse)
|
||||
|
||||
if not flt_flags:
|
||||
flt_flags = [self.bitflag_filter_item] * len(island_groups)
|
||||
|
||||
if self.use_filter_invert:
|
||||
for idx, flag in enumerate(flt_flags):
|
||||
flt_flags[idx] = 0 if flag else self.bitflag_filter_item
|
||||
|
||||
for idx, island_group in enumerate(island_groups):
|
||||
if len(island_group.islands) < 1:
|
||||
# Filter island groups with only 1 or 0 islands in them
|
||||
flt_flags[idx] = 0
|
||||
elif len(island_group.islands) == island_group.num_expected_islands:
|
||||
# Filter island groups with the expected number of islands in them
|
||||
flt_flags[idx] = 0
|
||||
|
||||
return flt_flags, flt_neworder
|
||||
|
||||
def draw_item(self, context, layout, data, item, icon, active_data, active_propname):
|
||||
island_group = item
|
||||
if self.layout_type in {'DEFAULT', 'COMPACT'}:
|
||||
icon = 'ERROR'
|
||||
num_islands = len(island_group.islands)
|
||||
if num_islands == island_group.num_expected_islands:
|
||||
icon = 'CHECKMARK'
|
||||
row = layout.row()
|
||||
row.label(text=island_group.name)
|
||||
row.label(text=str(num_islands), icon=icon)
|
||||
op = row.operator(FocusSmallestIsland.bl_idname, text="", icon='VIEWZOOM').vgroup = island_group.name
|
||||
row.operator(MarkIslandsAsOkay.bl_idname, text="", icon='CHECKMARK').vgroup = island_group.name
|
||||
# TODO: Operator to mark current number of islands as the expected amount
|
||||
elif self.layout_type in {'GRID'}:
|
||||
pass
|
||||
|
||||
classes = [
|
||||
VertIndex,
|
||||
WeightIsland,
|
||||
|
Loading…
Reference in New Issue
Block a user