Add Easy_Weight to Addons #47

Merged
Nick Alberelli merged 48 commits from feature/easy_weights into main 2023-05-17 22:13:57 +02:00
Showing only changes of commit 131f43eb21 - Show all commits

View File

@ -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,