diff --git a/.python-version b/.python-version new file mode 100644 index 0000000..e4fba21 --- /dev/null +++ b/.python-version @@ -0,0 +1 @@ +3.12 diff --git a/__init__.py b/__init__.py index 2479b1b..c59a4aa 100644 --- a/__init__.py +++ b/__init__.py @@ -8,18 +8,12 @@ bl_info = { "warning": "", "wiki_url": "", "tracker_url": "", - "category": "Rigging"} + "category": "Rigging", +} import importlib -modules = ( - '.operators', - '.properties', - '.ui', - '.area', - '.gizmo', - '.draw_handlers' -) +modules = (".operators", ".properties", ".ui", ".area", ".gizmo", ".draw_handlers") import bpy @@ -29,6 +23,7 @@ if "bpy" in locals(): module = importlib.import_module(name, __name__) importlib.reload(module) + def register(): if bpy.app.background: return @@ -36,6 +31,7 @@ def register(): module = importlib.import_module(name, __name__) module.register() + def unregister(): if bpy.app.background: return diff --git a/area.py b/area.py index 9add478..eeed75a 100644 --- a/area.py +++ b/area.py @@ -6,34 +6,35 @@ from .constants import PICKERS # Derived from the NodeTree base type, similar to Menu, Operator, Panel, etc. class RigPickerTree(NodeTree): # Description string - '''A custom node tree type that will show up in the editor type list''' + """A custom node tree type that will show up in the editor type list""" # Optional identifier string. If not explicitly defined, the python class name is used. # Label for nice name display bl_label = "Rig Picker" # Icon identifier - bl_icon = 'OUTLINER_DATA_ARMATURE' + bl_icon = "OUTLINER_DATA_ARMATURE" class RP_MT_picker(Menu): """Picker""" + bl_label = "Picker" def draw(self, context): layout = self.layout scn = context.scene - - layout.operator('rigpicker.reload_picker', icon='FILE_REFRESH') + layout.operator("rigpicker.reload_picker", icon="FILE_REFRESH") row = layout.row(align=True) # Has at least one picker collection in the scene if not [c.rig_picker.enabled for c in scn.collection.children_recursive]: row.enabled = False - row.prop(scn.rig_picker, 'use_pick_bone', text='Auto Bone Assign') + row.prop(scn.rig_picker, "use_pick_bone", text="Auto Bone Assign") class RP_MT_animation(Menu): """Picker""" + bl_label = "Animation" def draw(self, context): @@ -47,51 +48,62 @@ class RP_MT_animation(Menu): row.enabled = False layout.separator() - op = layout.operator("rigpicker.call_operator", text='Select All') + op = layout.operator("rigpicker.call_operator", text="Select All") op.operator = "pose.select_all" op.arguments = '{"action": "SELECT"}' - op = layout.operator("rigpicker.call_operator", text='Select All') + op = layout.operator("rigpicker.call_operator", text="Select All") op.operator = "pose.select_all" op.arguments = '{"action": "DESELECT"}' - op = layout.operator("rigpicker.call_operator", text='Frame Selected') + op = layout.operator("rigpicker.call_operator", text="Frame Selected") op.operator = "view3d.view_selected" op.view_3d = True layout.separator() - layout.operator("rigpicker.call_operator", text='Insert Keyframe').operator="animtoolbox.insert_keyframe" - layout.operator("anim.keyframe_delete_v3d", text='Delete Keyframe') + layout.operator("rigpicker.call_operator", text="Insert Keyframe").operator = ( + "animtoolbox.insert_keyframe" + ) + layout.operator("anim.keyframe_delete_v3d", text="Delete Keyframe") layout.separator() - op = layout.operator("rigpicker.call_operator", text='Move') - op.operator="transform.translate" + op = layout.operator("rigpicker.call_operator", text="Move") + op.operator = "transform.translate" op.invoke = True op.view_3d = True - - layout.operator("node.picker_transform", text='Rotate').mode='ROTATE' - layout.operator("node.picker_transform", text='Scale').mode='SCALE' + + layout.operator("node.picker_transform", text="Rotate").mode = "ROTATE" + layout.operator("node.picker_transform", text="Scale").mode = "SCALE" layout.separator() - layout.operator("rigpicker.call_operator", text='Reset Bone').operator="animtoolbox.reset_bone" - layout.operator("rigpicker.call_operator", text='Clear Location').operator='pose.loc_clear' - layout.operator("rigpicker.call_operator", text='Clear Rotation').operator='pose.rot_clear' - layout.operator("rigpicker.call_operator", text='Clear Scale').operator='pose.scale_clear' + layout.operator("rigpicker.call_operator", text="Reset Bone").operator = ( + "animtoolbox.reset_bone" + ) + layout.operator("rigpicker.call_operator", text="Clear Location").operator = ( + "pose.loc_clear" + ) + layout.operator("rigpicker.call_operator", text="Clear Rotation").operator = ( + "pose.rot_clear" + ) + layout.operator("rigpicker.call_operator", text="Clear Scale").operator = ( + "pose.scale_clear" + ) + def draw_header(self, context): - if not context.space_data.tree_type == 'RigPickerTree': + if not context.space_data.tree_type == "RigPickerTree": self._draw(context) return - + scn = context.scene layout = self.layout layout.template_header() if not context.space_data.node_tree: - ntree = bpy.data.node_groups.get('.rig_picker') + ntree = bpy.data.node_groups.get(".rig_picker") if not ntree: - ntree = bpy.data.node_groups.new('.rig_picker', 'RigPickerTree') - + ntree = bpy.data.node_groups.new(".rig_picker", "RigPickerTree") + context.space_data.node_tree = ntree row = layout.row(align=True) @@ -99,17 +111,19 @@ def draw_header(self, context): row.menu("RP_MT_animation") ob = context.object - if not ob or not ob.type == 'ARMATURE': + if not ob or not ob.type == "ARMATURE": return - + picker_group = PICKERS.get(ob) if not picker_group: return if scn.rig_picker.use_pick_bone: layout.alert = True - layout.label(text='Auto Bone Assign') - layout.prop(scn.rig_picker, 'use_pick_bone', icon='PANEL_CLOSE', text='', emboss=False) + layout.label(text="Auto Bone Assign") + layout.prop( + scn.rig_picker, "use_pick_bone", icon="PANEL_CLOSE", text="", emboss=False + ) layout.separator_spacer() row = layout.row() @@ -120,43 +134,44 @@ def draw_header(self, context): row = layout.row(align=True) for i, picker in enumerate(picker_group.pickers): - row.operator('rigpicker.fit_picker', text=f'{i+1}').index=i + row.operator("rigpicker.fit_picker", text=f"{i+1}").index = i + + row.operator("rigpicker.fit_picker", text="", icon="FULLSCREEN_ENTER").index = -1 - row.operator('rigpicker.fit_picker', text='', icon='FULLSCREEN_ENTER').index = -1 - def tools_from_context(context, mode=None): sp = context.space_data - if sp and sp.type == 'NODE_EDITOR' and sp.tree_type == 'RigPickerTree': + if sp and sp.type == "NODE_EDITOR" and sp.tree_type == "RigPickerTree": return [] else: return NODE_PT_tools_active._tools_from_context(context, mode) + def tool_set_by_id(self, context): sd = context.space_data - if sd and sd.type == 'NODE_EDITOR' and sd.tree_type == 'RigPickerTree': + if sd and sd.type == "NODE_EDITOR" and sd.tree_type == "RigPickerTree": return {"FINISHED"} else: return self._execute(context) + @classmethod def poll(cls, context): sp = context.space_data - if sp and sp.type == 'NODE_EDITOR' and sp.tree_type == 'RigPickerTree': + if sp and sp.type == "NODE_EDITOR" and sp.tree_type == "RigPickerTree": return False else: return self._poll(context) -classes = ( - RigPickerTree, - RP_MT_picker, - RP_MT_animation -) + +classes = (RigPickerTree, RP_MT_picker, RP_MT_animation) def register(): # Remove the tools inside the picker space - bpy.types.WM_OT_tool_set_by_id._execute = bpy.types.WM_OT_tool_set_by_id.execute #tool_set_by_id + bpy.types.WM_OT_tool_set_by_id._execute = ( + bpy.types.WM_OT_tool_set_by_id.execute + ) # tool_set_by_id bpy.types.WM_OT_tool_set_by_id.execute = tool_set_by_id NODE_PT_tools_active._tools_from_context = NODE_PT_tools_active.tools_from_context @@ -168,15 +183,16 @@ def register(): for cls in classes: bpy.utils.register_class(cls) + def unregister(): for cls in reversed(classes): bpy.utils.unregister_class(cls) - - bpy.types.WM_OT_tool_set_by_id.execute = bpy.types.WM_OT_tool_set_by_id._execute + + bpy.types.WM_OT_tool_set_by_id.execute = bpy.types.WM_OT_tool_set_by_id._execute del bpy.types.WM_OT_tool_set_by_id._execute NODE_PT_tools_active.tools_from_context = NODE_PT_tools_active._tools_from_context del NODE_PT_tools_active._tools_from_context NODE_HT_header.draw = NODE_HT_header._draw - del NODE_HT_header._draw \ No newline at end of file + del NODE_HT_header._draw diff --git a/constants.py b/constants.py index 36fecdc..88b41a2 100644 --- a/constants.py +++ b/constants.py @@ -1,6 +1,7 @@ import gpu from pathlib import Path + class LazyDict(dict): def __getitem__(self, k): v = super().__getitem__(k) @@ -19,11 +20,11 @@ PICKERS = {} MODULE_DIR = Path(__file__).parent -SHADER_DIR = MODULE_DIR / 'shaders' +SHADER_DIR = MODULE_DIR / "shaders" SHADERS = LazyDict() -vertex_shader = Path(SHADER_DIR, "dash_shader.vert").read_text(encoding='utf-8') -fragment_shader = Path(SHADER_DIR, "dash_shader.frag").read_text(encoding='utf-8') +vertex_shader = Path(SHADER_DIR, "dash_shader.vert").read_text(encoding="utf-8") +fragment_shader = Path(SHADER_DIR, "dash_shader.frag").read_text(encoding="utf-8") -SHADERS['dashed_line'] = lambda : gpu.types.GPUShader(vertex_shader, fragment_shader) +SHADERS["dashed_line"] = lambda: gpu.types.GPUShader(vertex_shader, fragment_shader) diff --git a/core/addon_utils.py b/core/addon_utils.py index 0dfdba7..7bfcd7f 100644 --- a/core/addon_utils.py +++ b/core/addon_utils.py @@ -1,4 +1,3 @@ - import bpy from .bl_utils import get_mat, get_collection_parents @@ -12,8 +11,10 @@ def get_picker_collection(ob=None): for col in ob.users_collection: if col.rig_picker.enabled: return col - - if picker_col := next((c for c in get_collection_parents(col) if c.rig_picker.enabled), None): + + if picker_col := next( + (c for c in get_collection_parents(col) if c.rig_picker.enabled), None + ): return picker_col @@ -22,10 +23,10 @@ def is_shape(ob): canvas = scn.rig_picker.canvas if not canvas or ob.hide_render: return False - + shapes = {ob for col in canvas.users_collection for ob in col.all_objects} - if ob.type in ('MESH', 'CURVE', 'FONT') and ob in shapes: + if ob.type in ("MESH", "CURVE", "FONT") and ob in shapes: return True return False @@ -34,20 +35,20 @@ def is_shape(ob): def get_object_color(ob): if not ob.data.materials: return - + mat = get_mat(ob) if not mat or not mat.node_tree or not mat.node_tree.nodes: return - emit_node = mat.node_tree.nodes.get('Emission') + emit_node = mat.node_tree.nodes.get("Emission") if not emit_node: return - - return emit_node.inputs['Color'].default_value + + return emit_node.inputs["Color"].default_value def get_operator_from_id(idname): - if not '.' in idname: + if not "." in idname: return m, o = idname.split(".") @@ -56,5 +57,5 @@ def get_operator_from_id(idname): op.get_rna_type() except Exception: return - - return op \ No newline at end of file + + return op diff --git a/core/bl_utils.py b/core/bl_utils.py index 7fd4619..9b16bbf 100644 --- a/core/bl_utils.py +++ b/core/bl_utils.py @@ -1,4 +1,3 @@ - import bpy @@ -30,94 +29,103 @@ def get_collection_parents(col, root=None, cols=None): cols = get_collection_parents(col, root=sub, cols=cols) return cols + def get_view_3d_override(): windows = bpy.context.window_manager.windows - areas = [a for w in windows for a in w.screen.areas if a.type == 'VIEW_3D'] + areas = [a for w in windows for a in w.screen.areas if a.type == "VIEW_3D"] if not areas: - print('No view 3d found') + print("No view 3d found") return view_3d = None for area in areas: if area.spaces.active.camera: view_3d = area - - if not view_3d: - view_3d = max(areas, key=lambda x :x.width) - return {'area': view_3d, 'region': view_3d.regions[-1]} + if not view_3d: + view_3d = max(areas, key=lambda x: x.width) + + return {"area": view_3d, "region": view_3d.regions[-1]} + def get_mat(ob): for sl in ob.material_slots: if sl.material: return sl.material + def link_mat_to_object(ob): for sl in ob.material_slots: m = sl.material - sl.link = 'OBJECT' + sl.link = "OBJECT" sl.material = m + def eval_attr(ob, name): resolved = ob for o in name.split("."): resolved = getattr(resolved, o) return resolved + def flip_name(name): if not name: return - if name.startswith('[') and name.endswith(']'): #It's a custom property + if name.startswith("[") and name.endswith("]"): # It's a custom property flipped_name = bpy.utils.flip_name(name[:-2][2:]) return f'["{flipped_name}"]' else: return bpy.utils.flip_name(name) -def split_path(path) : - try : + +def split_path(path): + try: bone_name = path.split('["')[1].split('"]')[0] except Exception: bone_name = None - try : + try: prop_name = path.split('["')[2].split('"]')[0] except Exception: prop_name = None return bone_name, prop_name + def hide_layers(args): """ """ ob = bpy.context.object layers = [] for bone in [b for b in ob.pose.bones if b.bone.select]: - for i,l in enumerate(bone.bone.layers): + for i, l in enumerate(bone.bone.layers): if l and i not in layers: layers.append(i) for i in layers: ob.data.layers[i] = not ob.data.layers[i] + def select_layer(args): ob = bpy.context.object - layers =[] + layers = [] for bone in [b for b in ob.pose.bones if b.bone.select]: - bone_layers = [i for i,l in enumerate(bone.bone.layers) if l] + bone_layers = [i for i, l in enumerate(bone.bone.layers) if l] for l in bone_layers: if l not in layers: layers.append(l) for bone in ob.pose.bones: - bone_layers = [i for i,l in enumerate(bone.bone.layers) if l] + bone_layers = [i for i, l in enumerate(bone.bone.layers) if l] if len(set(bone_layers).intersection(layers)): bone.bone.select = True + def hide_bones(args): ob = bpy.context.object selected_bone = [b for b in ob.pose.bones if b.bone.select] @@ -129,10 +137,11 @@ def hide_bones(args): for bone in selected_bone: bone.bone.hide = visibility + def select_all(args): ob = bpy.context.object - shapes = ob.data.rig_picker['shapes'] - bones = [s['bone'] for s in shapes if s['shape_type']=='BONE'] + shapes = ob.data.rig_picker["shapes"] + bones = [s["bone"] for s in shapes if s["shape_type"] == "BONE"] for bone_name in bones: bone = ob.pose.bones.get(bone_name) diff --git a/core/geometry_utils.py b/core/geometry_utils.py index ec2ea5c..2dca341 100644 --- a/core/geometry_utils.py +++ b/core/geometry_utils.py @@ -1,28 +1,31 @@ - import bpy from mathutils import Vector from mathutils.geometry import intersect_line_line_2d -def is_over_region(self,context,event): - inside = 2 < event.mouse_region_x < context.region.width -2 and \ - 2 < event.mouse_region_y < context.region.height -2 and \ - [a for a in context.screen.areas if a.as_pointer()==self.adress] and \ - not context.screen.show_fullscreen +def is_over_region(self, context, event): + inside = ( + 2 < event.mouse_region_x < context.region.width - 2 + and 2 < event.mouse_region_y < context.region.height - 2 + and [a for a in context.screen.areas if a.as_pointer() == self.adress] + and not context.screen.show_fullscreen + ) return inside + def bound_box_center(ob): - points = [ob.matrix_world@Vector(p) for p in ob.bound_box] + points = [ob.matrix_world @ Vector(p) for p in ob.bound_box] x = [v[0] for v in points] y = [v[1] for v in points] z = [v[2] for v in points] - return (sum(x) / len(points), sum(y) / len(points),sum(z) / len(points)) + return (sum(x) / len(points), sum(y) / len(points), sum(z) / len(points)) + def bounding_rect(points): - x_points = sorted(p[0] for p in points) + x_points = sorted(p[0] for p in points) y_points = sorted(p[1] for p in points) return [ @@ -32,28 +35,31 @@ def bounding_rect(points): Vector((x_points[0], y_points[0])), ] -def intersect_rects(rect, border): # returns None if rectangles don't intersect - dx = min(border[1][0],rect[1][0]) - max(border[0][0],rect[0][0]) - dy = min(border[0][1],rect[0][1]) - max(border[2][1],rect[2][1]) - if (dx>=0) and (dy>=0): - return dx*dy +def intersect_rects(rect, border): # returns None if rectangles don't intersect + dx = min(border[1][0], rect[1][0]) - max(border[0][0], rect[0][0]) + dy = min(border[0][1], rect[0][1]) - max(border[2][1], rect[2][1]) + + if (dx >= 0) and (dy >= 0): + return dx * dy + def point_inside_rect(point, rect): - return rect[0][0]< point[0]< rect[1][0] and rect[2][1]< point[1]< rect[0][1] + return rect[0][0] < point[0] < rect[1][0] and rect[2][1] < point[1] < rect[0][1] -def point_over_shape(point,verts,loops,outside_point=(-1,-1)): + +def point_over_shape(point, verts, loops, outside_point=(-1, -1)): out = Vector(outside_point) pt = Vector(point) intersections = 0 for loop in loops: - for i,p in enumerate(loop): - a = Vector(verts[loop[i-1]]) + for i, p in enumerate(loop): + a = Vector(verts[loop[i - 1]]) b = Vector(verts[p]) - if intersect_line_line_2d(pt,out,a,b): + if intersect_line_line_2d(pt, out, a, b): intersections += 1 - if intersections%2 == 1: #chek if the nb of intersection is odd - return True \ No newline at end of file + if intersections % 2 == 1: # chek if the nb of intersection is odd + return True diff --git a/core/picker.py b/core/picker.py index 1af7bef..79156f7 100644 --- a/core/picker.py +++ b/core/picker.py @@ -1,11 +1,13 @@ - import bpy import gpu from gpu_extras.batch import batch_for_shader import blf from mathutils import bvhtree, Vector, Color -from mathutils.geometry import (intersect_point_quad_2d, intersect_point_tri_2d, - intersect_tri_tri_2d) +from mathutils.geometry import ( + intersect_point_quad_2d, + intersect_point_tri_2d, + intersect_tri_tri_2d, +) from ..constants import PICKERS from .addon_utils import get_operator_from_id from .geometry_utils import bounding_rect @@ -15,13 +17,22 @@ import re import json import os -import threading +import threading class Shape: - def __init__(self, picker, points, polygons=None, edges=None, tooltip='', color=None, source_name=''): + def __init__( + self, + picker, + points, + polygons=None, + edges=None, + tooltip="", + color=None, + source_name="", + ): - self.type = 'display' + self.type = "display" self.picker = picker self.rig = picker.rig self.source_name = source_name @@ -29,11 +40,11 @@ class Shape: self.hover = False self.press = False - self.shader = gpu.shader.from_builtin('UNIFORM_COLOR') + self.shader = gpu.shader.from_builtin("UNIFORM_COLOR") self.shader.bind() - #self.hover_shader = gpu.shader.from_builtin('UNIFORM_COLOR') - #self.hover_shader.uniform_float("color", [1, 1, 1, 0.1]) + # self.hover_shader = gpu.shader.from_builtin('UNIFORM_COLOR') + # self.hover_shader.uniform_float("color", [1, 1, 1, 0.1]) self.color = color self.hover_color = self.brighten_color(self.color) @@ -43,15 +54,19 @@ class Shape: self.polygons = polygons or [] self.edges = edges or [] - self.p_batch = batch_for_shader(self.shader, 'TRIS', {"pos": self.points}, indices=self.polygons) - self.e_batch = batch_for_shader(self.shader, 'LINES', {"pos": self.points}, indices=self.edges) + self.p_batch = batch_for_shader( + self.shader, "TRIS", {"pos": self.points}, indices=self.polygons + ) + self.e_batch = batch_for_shader( + self.shader, "LINES", {"pos": self.points}, indices=self.edges + ) self.rect = bounding_rect(points) @property def color(self): return self._color - + def brighten_color(self, color): brighten_color = Color(color[:3]) brighten_color.v += 0.05 @@ -70,16 +85,16 @@ class Shape: elif len(color) == 4: color = list(color) else: - raise Exception('color must have a len of 3 or 4') + raise Exception("color must have a len of 3 or 4") else: - raise Exception(f'color is {type(color)} must be None or (tuple, list)') - - #self.shader.uniform_float("color", color) + raise Exception(f"color is {type(color)} must be None or (tuple, list)") + + # self.shader.uniform_float("color", color) self._color = color def draw(self): - #self.shader.bind() + # self.shader.bind() self.shader.uniform_float("color", self.color) if self.polygons: @@ -92,28 +107,45 @@ class Shape: if not intersect_point_quad_2d(location, *self.rect): self.hover = False return False - + for p in self.polygons: if intersect_point_tri_2d(location, *[self.points[i] for i in p]): self.hover = True return True - + self.hover = False return False - - def press_event(self, mode='SET'): + + def press_event(self, mode="SET"): self.press = True - def release_event(self, mode='SET'): + def release_event(self, mode="SET"): self.press = False class BoneShape(Shape): - def __init__(self, picker, points, polygons, edges, bone, tooltip='', color=None, source_name=''): - super().__init__(picker, points=points, polygons=polygons, edges=edges, - tooltip=tooltip, color=color, source_name=source_name) + def __init__( + self, + picker, + points, + polygons, + edges, + bone, + tooltip="", + color=None, + source_name="", + ): + super().__init__( + picker, + points=points, + polygons=polygons, + edges=edges, + tooltip=tooltip, + color=color, + source_name=source_name, + ) - self.type = 'bone' + self.type = "bone" self.bone = bone self.active_color = [1, 1, 1, 0.1] self.select_color = self.brighten_color(self.hover_color) @@ -125,23 +157,29 @@ class BoneShape(Shape): if not self.bone: return False - return self.bone in (bpy.context.selected_pose_bones or []) #self.bone.bone.select + return self.bone in ( + bpy.context.selected_pose_bones or [] + ) # self.bone.bone.select @property def active(self): if not self.bone: return False - return self.bone == bpy.context.active_pose_bone #self.rig.data.bones.active == self.bone.bone - + return ( + self.bone == bpy.context.active_pose_bone + ) # self.rig.data.bones.active == self.bone.bone + @property def hide(self): if not self.bone: return False - #return self.bone not in (bpy.context.visible_pose_bones or []) - - return self.bone.bone.hide or not any(l.is_visible for l in self.bone.bone.collections) + # return self.bone not in (bpy.context.visible_pose_bones or []) + + return self.bone.bone.hide or not any( + l.is_visible for l in self.bone.bone.collections + ) @property def bone_color(self): @@ -149,45 +187,45 @@ class BoneShape(Shape): return [0, 0, 0, 1] if self.select and self.active: - return self.bone_colors['active'] + return self.bone_colors["active"] elif self.select: - return self.bone_colors['select'] + return self.bone_colors["select"] elif self.hide: - return self.bone_colors['hide'] + return self.bone_colors["hide"] else: - return self.bone_colors['normal'] + return self.bone_colors["normal"] def get_bone_colors(self): - theme = bpy.context.preferences.themes['Default'] + theme = bpy.context.preferences.themes["Default"] bone_colors = { - 'select': [*theme.view_3d.bone_pose, 1], - 'normal': [0.05, 0.05, 0.05, 1], - 'active': [*theme.view_3d.bone_pose_active, 1], - 'hide': [0.85, 0.85, 0.85, 0.2], + "select": [*theme.view_3d.bone_pose, 1], + "normal": [0.05, 0.05, 0.05, 1], + "active": [*theme.view_3d.bone_pose_active, 1], + "hide": [0.85, 0.85, 0.85, 0.2], } if not self.bone: return bone_colors - if self.bone.color.palette == 'CUSTOM': + if self.bone.color.palette == "CUSTOM": bone_color = self.bone - elif self.bone.color.palette == 'DEFAULT': + elif self.bone.color.palette == "DEFAULT": bone_color = self.bone.bone normal_color = bone_color.color.custom.normal.copy() normal_color.s *= 0.75 - bone_colors['normal'] = [*normal_color, 1] - bone_colors['select'] = [*bone_color.color.custom.select, 1] - bone_colors['active'] = [*bone_color.color.custom.active, 1] - bone_colors['hide'] = [*normal_color, 0.1] + bone_colors["normal"] = [*normal_color, 1] + bone_colors["select"] = [*bone_color.color.custom.select, 1] + bone_colors["active"] = [*bone_color.color.custom.active, 1] + bone_colors["hide"] = [*normal_color, 0.1] return bone_colors def draw_hided(self): - gpu.state.blend_set('ALPHA') + gpu.state.blend_set("ALPHA") gpu.state.line_width_set(1.0) line_color = (1, 1, 1, 0.1) @@ -200,17 +238,17 @@ class BoneShape(Shape): self.shader.uniform_float("color", (*color[:3], 0.5)) self.p_batch.draw(self.shader) - + self.shader.uniform_float("color", line_color) self.e_batch.draw(self.shader) - gpu.state.blend_set('NONE') + gpu.state.blend_set("NONE") def draw_zero_scaled(self): - gpu.state.blend_set('ALPHA') + gpu.state.blend_set("ALPHA") gpu.state.line_width_set(1.0) - line_color = (*self.bone_colors['normal'][:3], 0.2) + line_color = (*self.bone_colors["normal"][:3], 0.2) color = Color(self.color[:3]) if self.hover or self.select: color.v += 0.05 @@ -219,14 +257,14 @@ class BoneShape(Shape): self.shader.uniform_float("color", (*color[:3], 0.5)) self.p_batch.draw(self.shader) - + self.shader.uniform_float("color", line_color) self.e_batch.draw(self.shader) - gpu.state.blend_set('NONE') + gpu.state.blend_set("NONE") def draw(self): - gpu.state.blend_set('ALPHA') + gpu.state.blend_set("ALPHA") gpu.state.line_width_set(1.0) if self.hide: @@ -244,9 +282,9 @@ class BoneShape(Shape): self.shader.uniform_float("color", color) self.p_batch.draw(self.shader) - + # Draw Outline - gpu.state.blend_set('NONE') + gpu.state.blend_set("NONE") if self.select or self.active: gpu.state.line_width_set(2.0) @@ -254,46 +292,45 @@ class BoneShape(Shape): self.e_batch.draw(self.shader) gpu.state.line_width_set(1.0) - + def assign_bone_event(self): - #print('assign_bone_event', self) + # print('assign_bone_event', self) scn = bpy.context.scene rig = self.picker.rig source_object = scn.objects.get(self.source_name) if not source_object: - print(f'Source object {self.source_name} not found') - return - - active_bone = rig.data.bones.active - if not active_bone: - print('You need to have an active bone') + print(f"Source object {self.source_name} not found") return - #print(active_bone, source_object) - #print(rig) + active_bone = rig.data.bones.active + if not active_bone: + print("You need to have an active bone") + return + + # print(active_bone, source_object) + # print(rig) source_object.name = rig.data.bones.active.name source_object.rig_picker.name = rig.data.bones.active.name - def release_event(self, mode='SET'): + def release_event(self, mode="SET"): super().release_event(mode) if self.hide or not self.bone: return select = True - if mode == 'SUBSTRACT': + if mode == "SUBSTRACT": select = False self.bone.bone.select = select if self.hover: - if mode != 'SUBSTRACT': + if mode != "SUBSTRACT": self.rig.data.bones.active = self.bone.bone - - def border_select(self, border, mode='SET'): + def border_select(self, border, mode="SET"): if not self.bone: return @@ -307,28 +344,30 @@ class BoneShape(Shape): border_tri1 = border[0], border[1], border[2] border_tri2 = border[2], border[3], border[0] - if (not intersect_tri_tri_2d(*border_tri1, *rect_tri1) and - not intersect_tri_tri_2d(*border_tri1, *rect_tri2) and - not intersect_tri_tri_2d(*border_tri2, *rect_tri1) and - not intersect_tri_tri_2d(*border_tri2, *rect_tri2)): + if ( + not intersect_tri_tri_2d(*border_tri1, *rect_tri1) + and not intersect_tri_tri_2d(*border_tri1, *rect_tri2) + and not intersect_tri_tri_2d(*border_tri2, *rect_tri1) + and not intersect_tri_tri_2d(*border_tri2, *rect_tri2) + ): return select = True - if mode == 'SUBSTRACT': + if mode == "SUBSTRACT": select = False - + for polygon in self.polygons: points = [self.points[i] for i in polygon] if intersect_tri_tri_2d(*border_tri1, *points): self.bone.bone.select = select return - + if intersect_tri_tri_2d(*border_tri2, *points): self.bone.bone.select = select return - ''' + """ for b in border: if intersect_point_tri_2d(b, *points): self.bone.bone.select = select @@ -338,46 +377,58 @@ class BoneShape(Shape): if intersect_point_quad_2d(p, *border): self.bone.bone.select = select return - ''' + """ + class OperatorShape(Shape): - def __init__(self, picker, points, polygons, operator, tooltip='', color=None, source_name=''): - super().__init__(picker, points=points, polygons=polygons, tooltip=tooltip, - color=color, source_name=source_name) + def __init__( + self, picker, points, polygons, operator, tooltip="", color=None, source_name="" + ): + super().__init__( + picker, + points=points, + polygons=polygons, + tooltip=tooltip, + color=color, + source_name=source_name, + ) - self.type = 'operator' + self.type = "operator" self.active_color = [1, 1, 1, 0.15] self.press_color = [0, 0, 0, 0.25] self.operator = operator - #self.arguments = arguments#{k:eval(v)} - #self.operator = get_operator_from_id(self.operator) + # self.arguments = arguments#{k:eval(v)} + # self.operator = get_operator_from_id(self.operator) if not tooltip: - self.tooltip = self.operator.replace('bpy.ops.', '').replace("'INVOKE_DEFAULT', ", '') + self.tooltip = self.operator.replace("bpy.ops.", "").replace( + "'INVOKE_DEFAULT', ", "" + ) - #self.reg_args = re.compile(r'(\w+)=') - ''' + # self.reg_args = re.compile(r'(\w+)=') + + """ def parse_args(self): args = self.reg_args.split(self.arguments)[1:] #print(args, zip(args[::2], args[1::2])) return {k: eval(v) for k, v in zip(args[::2], args[1::2])} #return {k:eval(v) for k, v in self.reg_args.split(self.arguments)} - ''' + """ - def release_event(self, mode='SET'): + def release_event(self, mode="SET"): super().release_event(mode) - #args = self.parse_args() + # args = self.parse_args() if not self.operator: return exec(self.operator) - #f'bpy.ops;{idname}' + # f'bpy.ops;{idname}' - #print(self.idname) - #print(self.arguments) - #else: + # print(self.idname) + # print(self.arguments) + # else: # self.bone.bone.select = False def draw(self): @@ -390,10 +441,10 @@ class OperatorShape(Shape): else: return - gpu.state.blend_set('ALPHA') + gpu.state.blend_set("ALPHA") self.shader.uniform_float("color", color) self.p_batch.draw(self.shader) - gpu.state.blend_set('NONE') + gpu.state.blend_set("NONE") class Picker: @@ -406,48 +457,48 @@ class Picker: self.shapes = [] self.box_select = None self.hover_shape = None - self.shader = gpu.shader.from_builtin('UNIFORM_COLOR') + self.shader = gpu.shader.from_builtin("UNIFORM_COLOR") self.mouse = None for shape_data in shapes: - if not shape_data['points']: + if not shape_data["points"]: continue - - if shape_data['type'] in ('CANVAS', 'DISPLAY'): + + if shape_data["type"] in ("CANVAS", "DISPLAY"): shape = Shape( self, - points=shape_data['points'], - polygons=shape_data['polygons'], - edges=shape_data['edges'], - color=shape_data['color'] + points=shape_data["points"], + polygons=shape_data["polygons"], + edges=shape_data["edges"], + color=shape_data["color"], ) - elif shape_data['type'] == 'BONE': - bone = rig.pose.bones.get(shape_data['bone']) - #if not bone: + elif shape_data["type"] == "BONE": + bone = rig.pose.bones.get(shape_data["bone"]) + # if not bone: # print(f'Bone {shape_data["bone"]} not exist') # continue shape = BoneShape( self, - source_name=shape_data['source_name'], - points=shape_data['points'], - polygons=shape_data['polygons'], - edges=shape_data['edges'], - bone=bone, - color=shape_data['color'] + source_name=shape_data["source_name"], + points=shape_data["points"], + polygons=shape_data["polygons"], + edges=shape_data["edges"], + bone=bone, + color=shape_data["color"], ) - elif shape_data['type'] == 'OPERATOR': + elif shape_data["type"] == "OPERATOR": shape = OperatorShape( self, - source_name=shape_data['source_name'], - points=shape_data['points'], - polygons=shape_data['polygons'], - operator=shape_data['operator'], - color=shape_data['color'], - tooltip=shape_data['tooltip'], + source_name=shape_data["source_name"], + points=shape_data["points"], + polygons=shape_data["polygons"], + operator=shape_data["operator"], + color=shape_data["color"], + tooltip=shape_data["tooltip"], ) self.shapes.append(shape) @@ -456,36 +507,35 @@ class Picker: def assign_bone_event(self): for shape in self.shapes: - if shape.type == 'bone' and shape.hover: + if shape.type == "bone" and shape.hover: shape.assign_bone_event() - + bpy.ops.rigpicker.save_picker(index=self.index) - def press_event(self, mode='SET'): + def press_event(self, mode="SET"): for shape in self.shapes: - #print(s) + # print(s) if shape.hover: shape.press_event(mode) else: shape.press = False - def release_event(self, mode='SET'): + def release_event(self, mode="SET"): + # bpy.ops.pose.select_all(action='DESELECT') - #bpy.ops.pose.select_all(action='DESELECT') - - #print('PICKER release event', mode) - #print(f'type={event.type}, value={event.value}, ctrl={event.ctrl}, shift={event.shift}, alt={event.alt}') + # print('PICKER release event', mode) + # print(f'type={event.type}, value={event.value}, ctrl={event.ctrl}, shift={event.shift}, alt={event.alt}') for shape in self.shapes: if shape.hover: shape.release_event(mode) - - shape.press = False - - #bpy.context.area.tag_redraw() - ''' + shape.press = False + + # bpy.context.area.tag_redraw() + + """ picker.tooltip_event(event='SHOW') region.tag_redraw() @@ -495,8 +545,9 @@ class Picker: picker.tooltip_event(event='HIDE') bpy.app.timers.register(partial(tooltip, context.region), first_interval=1) - ''' - ''' + """ + + """ def tooltip_event(self, event): @@ -508,7 +559,7 @@ class Picker: self.tooltip = self.hover_shape.bone.name #bpy.context.region.tag_redraw() - ''' + """ def border_select(self, border, mode): border = [Vector(b) - Vector(self.translation) for b in border] @@ -516,7 +567,7 @@ class Picker: for shape in self.shapes: shape.press = False shape.hover = False - if shape.type != 'bone': + if shape.type != "bone": continue shape.border_select(border, mode) @@ -531,16 +582,16 @@ class Picker: elif shape.move_event(self.mouse): self.hover_shape = shape - #if point_inside_rect(self.end, rect): + # if point_inside_rect(self.end, rect): # over = point_over_shape(self.end,points, edges) - #if bpy.app.timers.is_registered(self.tooltip_event): - #try: + # if bpy.app.timers.is_registered(self.tooltip_event): + # try: # bpy.app.timers.unregister(self.tooltip_event) - #except: + # except: # pass - #bpy.app.timers.register(self.tooltip_event, first_interval=1) + # bpy.app.timers.register(self.tooltip_event, first_interval=1) def draw(self): with gpu.matrix.push_pop(): @@ -565,7 +616,7 @@ class Picker: class PickerGroup: def __init__(self, rig, pickers): - #self.view_location = Vector((0, 0)) + # self.view_location = Vector((0, 0)) self.region = bpy.context.region self.rig = rig self.pickers = [] @@ -577,13 +628,13 @@ class PickerGroup: self.tooltip_shape = None self.tooltip_mouse = Vector((0, 0)) - self.tooltip = '' + self.tooltip = "" self.timer = None for picker in pickers: self.add_picker(picker) - + def add_picker(self, picker): self.pickers.append(Picker(self, self.rig, picker)) @@ -592,12 +643,12 @@ class PickerGroup: for picker in self.pickers: height = picker.rect[1][1] - picker.rect[-1][1] - picker.translation = Vector((0, -y-height*0.5)) + picker.translation = Vector((0, -y - height * 0.5)) picker.draw() y += height + 50 - #break #TODO for now only draw first picker + # break #TODO for now only draw first picker def move_event(self, mouse): self.mouse = mouse @@ -618,14 +669,14 @@ class PickerGroup: continue picker.move_event(self.location) - + if picker.hover_shape: hover_shape = picker.hover_shape self.hover_shape = hover_shape if self.tooltip_shape is not self.hover_shape: - self.tooltip = '' + self.tooltip = "" if self.timer: self.timer.cancel() @@ -633,8 +684,8 @@ class PickerGroup: self.timer = threading.Timer(0.4, self.tooltip_event) self.timer.start() - def press_event(self, mode='SET'): - #self.clear_tooltip() + def press_event(self, mode="SET"): + # self.clear_tooltip() for picker in self.pickers: if picker.under_mouse(self.location): @@ -643,8 +694,8 @@ class PickerGroup: for shape in picker.shapes: shape.press = False - def release_event(self, mode='SET'): - if mode == 'SET': + def release_event(self, mode="SET"): + if mode == "SET": for bone in self.rig.data.bones: bone.select = False @@ -663,7 +714,7 @@ class PickerGroup: def border_select(self, border, mode): border = [self.region.view2d.region_to_view(*b) for b in border] - if mode == 'SET': + if mode == "SET": for bone in self.rig.data.bones: bone.select = False @@ -671,37 +722,38 @@ class PickerGroup: picker.border_select(border, mode) def clear_tooltip(self): - self.tooltip = '' + self.tooltip = "" self.tooltip_shape = None self.timer.cancel() self.region.tag_redraw() - + def tooltip_event(self): - #print('Tooltip Event', self) + # print('Tooltip Event', self) - #print(self.hover_shape, self.hover_shape.type) + # print(self.hover_shape, self.hover_shape.type) - if self.hover_shape and self.hover_shape.type != 'display': - if self.hover_shape.type == 'bone' and self.hover_shape.bone: + if self.hover_shape and self.hover_shape.type != "display": + if self.hover_shape.type == "bone" and self.hover_shape.bone: self.tooltip = self.hover_shape.bone.name else: self.tooltip = self.hover_shape.tooltip self.tooltip_shape = self.hover_shape else: return self.clear_tooltip() - self.tooltip_mouse = self.mouse self.timer.cancel() - #print(self.tooltip) + # print(self.tooltip) self.region.tag_redraw() @property def rect(self): - return bounding_rect([co - Vector(p.translation) for p in self.pickers for co in p.rect]) + return bounding_rect( + [co - Vector(p.translation) for p in self.pickers for co in p.rect] + ) @property def center(self): @@ -711,22 +763,26 @@ class PickerGroup: def load_picker_data(rig): - if 'pickers' in rig.data.rig_picker: - picker_datas = [[s.to_dict() for s in p] for p in rig.data.rig_picker['pickers']] + if "pickers" in rig.data.rig_picker: + picker_datas = [ + [s.to_dict() for s in p] for p in rig.data.rig_picker["pickers"] + ] else: picker_datas = [] for picker in rig.data.rig_picker.sources: - picker_path = Path(bpy.path.abspath(picker.source, library=rig.data.library)) + picker_path = Path( + bpy.path.abspath(picker.source, library=rig.data.library) + ) if not picker_path.exists(): - print(f'Picker path not exists: {picker_path.resolve()}') + print(f"Picker path not exists: {picker_path.resolve()}") continue - print('Load picker from', picker_path.resolve()) - picker_data = json.loads(picker_path.read_text(encoding='utf-8')) + print("Load picker from", picker_path.resolve()) + picker_data = json.loads(picker_path.read_text(encoding="utf-8")) picker_datas.append(picker_data) - + PICKERS[rig] = PickerGroup(rig, picker_datas) @@ -736,24 +792,24 @@ def get_picker_path(rig, source, start=None): def pack_picker(rig, start=None): - if not 'rig_picker' in rig.data: + if not "rig_picker" in rig.data: return pickers = [] - for picker_source in rig.data['rig_picker'].get('sources', []): - picker_path = get_picker_path(rig, picker_source['source'], start) + for picker_source in rig.data["rig_picker"].get("sources", []): + picker_path = get_picker_path(rig, picker_source["source"], start) if not picker_path.exists(): - print(f'{picker_path} not exists') + print(f"{picker_path} not exists") continue - picker_data = json.loads(picker_path.read_text(encoding='utf-8')) + picker_data = json.loads(picker_path.read_text(encoding="utf-8")) pickers.append(picker_data) - rig.data['rig_picker']['pickers'] = pickers + rig.data["rig_picker"]["pickers"] = pickers def unpack_picker(rig): - if 'rig_picker' not in rig.data.keys(): + if "rig_picker" not in rig.data.keys(): return - if 'pickers' in rig.data['rig_picker'].keys(): - del rig.data['rig_picker']['pickers'] \ No newline at end of file + if "pickers" in rig.data["rig_picker"].keys(): + del rig.data["rig_picker"]["pickers"] diff --git a/core/shape.py b/core/shape.py index be38a9a..503c02b 100644 --- a/core/shape.py +++ b/core/shape.py @@ -6,30 +6,29 @@ from .geometry_utils import bound_box_center from .addon_utils import get_object_color - -def border_over_shape(border,verts,loops): +def border_over_shape(border, verts, loops): for loop in loops: - for i,p in enumerate(loop): - a = Vector(verts[loop[i-1]]) + for i, p in enumerate(loop): + a = Vector(verts[loop[i - 1]]) b = Vector(verts[p]) - for j in range(0,4): - c = border[j-1] + for j in range(0, 4): + c = border[j - 1] d = border[j] - if intersect_line_line_2d(a,b,c,d): + if intersect_line_line_2d(a, b, c, d): return True for point in verts: - if point_inside_rect(point,border): + if point_inside_rect(point, border): return True for point in border: - if point_over_shape(point,verts,loops): + if point_over_shape(point, verts, loops): return True def border_loop(vert, loop): - border_edge =[e for e in vert.link_edges if e.is_rectary] + border_edge = [e for e in vert.link_edges if e.is_rectary] if border_edge: for edge in border_edge: @@ -43,6 +42,7 @@ def border_loop(vert, loop): else: return [vert] + def contour_loops(bm, vert_index=0, loops=None, vert_indices=None): loops = loops or [] vert_indices = vert_indices or [v.index for v in bm.verts] @@ -50,7 +50,7 @@ def contour_loops(bm, vert_index=0, loops=None, vert_indices=None): bm.verts.ensure_lookup_table() loop = border_loop(bm.verts[vert_index], [bm.verts[vert_index]]) - if len(loop) >1: + if len(loop) > 1: loops.append(loop) for v in loop: @@ -75,14 +75,13 @@ def get_shape_data(ob, matrix=None, depsgraph=None): bm = bmesh.new() bm.from_mesh(mesh) - bmesh.ops.remove_doubles(bm, verts=bm.verts, dist=0.002) bmesh.ops.dissolve_limit(bm, angle_limit=0.001745, verts=bm.verts, edges=bm.edges) bmesh.ops.connect_verts_concave(bm, faces=bm.faces) bmesh.ops.triangulate(bm, faces=bm.faces) - edges = [[v.index for v in e.verts] for e in bm.edges if len(e.link_faces)<=1] + edges = [[v.index for v in e.verts] for e in bm.edges if len(e.link_faces) <= 1] bm.to_mesh(mesh) mesh.update() bm.clear() @@ -96,11 +95,11 @@ def get_shape_data(ob, matrix=None, depsgraph=None): points.append([round(co[0], 1), round(co[1], 1)]) depths += [co[2]] - + if depths: depth = max(depths) else: - print(f'{ob.name} has no vertices') + print(f"{ob.name} has no vertices") depth = 0 for face in mesh.polygons: @@ -113,67 +112,70 @@ def get_shape_data(ob, matrix=None, depsgraph=None): color = [0.5, 0.5, 0.5] shape = { - 'source_name': ob.name, - 'tooltip': ob.rig_picker.name, - 'depth': depth, - 'points': points, - 'polygons': polygons, - 'edges': edges, - 'color': color, - 'type': ob.rig_picker.shape_type + "source_name": ob.name, + "tooltip": ob.rig_picker.name, + "depth": depth, + "points": points, + "polygons": polygons, + "edges": edges, + "color": color, + "type": ob.rig_picker.shape_type, } - if shape['type'] =='OPERATOR': - shape['operator'] = ob.rig_picker.operator - shape['shortcut'] = ob.rig_picker.shortcut + if shape["type"] == "OPERATOR": + shape["operator"] = ob.rig_picker.operator + shape["shortcut"] = ob.rig_picker.shortcut + + elif shape["type"] == "BONE": + shape["bone"] = ob.rig_picker.name - elif shape['type'] =='BONE': - shape['bone'] = ob.rig_picker.name - eval_ob.to_mesh_clear() - + return shape def get_picker_data(collection): picker_data = [] - + depsgraph = bpy.context.evaluated_depsgraph_get() canvas = collection.rig_picker.canvas - if canvas.type == 'CURVE': + if canvas.type == "CURVE": canvas_points = canvas.data.splines[0].points else: canvas_points = canvas.data.vertices - - canvas_coords = [canvas.matrix_world@Vector((p.co)) for p in canvas_points] + + canvas_coords = [canvas.matrix_world @ Vector((p.co)) for p in canvas_points] height = abs(max(co.y for co in canvas_coords) - min(co.y for co in canvas_coords)) width = abs(max(co.x for co in canvas_coords) - min(co.x for co in canvas_coords)) center = sum(canvas_coords, Vector()) / len(canvas_coords) - scale = 2048 / max(height, width)# Reference height for the canvas + scale = 2048 / max(height, width) # Reference height for the canvas matrix = Matrix.Translation(-center) matrix = Matrix.Scale(scale, 4) @ matrix - #sorted by their z axes + # sorted by their z axes for ob in collection.all_objects: if ob.instance_collection: for shape in ob.instance_collection.all_objects: - picker_data.append(get_shape_data(shape, matrix=matrix@ob.matrix_world, depsgraph=depsgraph)) - - elif ob.type in ('MESH', 'CURVE', 'FONT'): + picker_data.append( + get_shape_data( + shape, matrix=matrix @ ob.matrix_world, depsgraph=depsgraph + ) + ) + + elif ob.type in ("MESH", "CURVE", "FONT"): picker_data.append(get_shape_data(ob, matrix=matrix, depsgraph=depsgraph)) else: - #print(f'{ob.name} of type {ob.type} not supported') + # print(f'{ob.name} of type {ob.type} not supported') continue - - picker_data.sort(key=lambda x : x['depth']) + picker_data.sort(key=lambda x: x["depth"]) - #print(picker_datas) + # print(picker_datas) return picker_data - #rig.data.rig_picker['shapes'] = picker_datas + # rig.data.rig_picker['shapes'] = picker_datas diff --git a/draw_handlers.py b/draw_handlers.py index 6f19ae1..79d627f 100644 --- a/draw_handlers.py +++ b/draw_handlers.py @@ -1,4 +1,3 @@ - import bpy import gpu from gpu_extras.batch import batch_for_shader @@ -20,12 +19,13 @@ def draw_rect_2d(position, width, height, color): :arg height: Height of the rect. :type height: float """ - + coords = ((0, 0), (1, 0), (1, 1), (0, 1)) - shader = gpu.shader.from_builtin('UNIFORM_COLOR') + shader = gpu.shader.from_builtin("UNIFORM_COLOR") batch = batch_for_shader( - shader, 'TRI_FAN', + shader, + "TRI_FAN", {"pos": coords}, ) @@ -40,7 +40,7 @@ def draw_rect_2d(position, width, height, color): def draw_callback_view(): space_data = bpy.context.space_data - if not space_data or not space_data.tree_type == 'RigPickerTree': + if not space_data or not space_data.tree_type == "RigPickerTree": return # Use the pin to know if this is the first time this picker window in created to hide the n panel @@ -49,19 +49,19 @@ def draw_callback_view(): space_data.show_region_ui = False ob = bpy.context.object - if not ob or ob.type !='ARMATURE' or not ob.data.rig_picker.sources: + if not ob or ob.type != "ARMATURE" or not ob.data.rig_picker.sources: return if ob not in PICKERS: load_picker_data(ob) - picker_group = PICKERS[ob] + picker_group = PICKERS[ob] picker_group.draw() def draw_callback_px(): sp = bpy.context.space_data - if not sp.tree_type == 'RigPickerTree': + if not sp.tree_type == "RigPickerTree": return ob = bpy.context.object @@ -74,37 +74,36 @@ def draw_callback_px(): text = picker_group.tooltip ui_scale = bpy.context.preferences.system.ui_scale - #print('Draw text', text) + # print('Draw text', text) font_id = 0 blf.size(font_id, int(13 * ui_scale)) margins = [12, 5] text_size = blf.dimensions(font_id, text) - text_pos = (region.width - text_size[0]-margins[0], margins[1]) + text_pos = (region.width - text_size[0] - margins[0], margins[1]) - bg_pos = (text_pos[0] - margins[0], text_pos[1] - margins[1]-1) - bg_size = (text_size[0] + 2*margins[0], text_size[1] + 2*margins[1]) + bg_pos = (text_pos[0] - margins[0], text_pos[1] - margins[1] - 1) + bg_size = (text_size[0] + 2 * margins[0], text_size[1] + 2 * margins[1]) - gpu.state.blend_set('ALPHA') + gpu.state.blend_set("ALPHA") draw_rect_2d(bg_pos, *bg_size, (0.1, 0.1, 0.1, 0.66)) - gpu.state.blend_set('NONE') + gpu.state.blend_set("NONE") - - #blf.dimensions(font_id, text) + # blf.dimensions(font_id, text) blf.enable(font_id, blf.SHADOW) - # BLF drawing routine blf.position(font_id, round(text_pos[0]), round(text_pos[1]), 0) blf.color(font_id, 0.85, 0.85, 0.85, 1) - blf.shadow(font_id , 5, 0.0, 0.0, 0.0, 1) + blf.shadow(font_id, 5, 0.0, 0.0, 0.0, 1) blf.shadow_offset(font_id, 2, -2) blf.draw(font_id, text) blf.disable(font_id, blf.SHADOW) + handle_view = None handle_pixel = None @@ -113,15 +112,20 @@ def register(): global handle_view global handle_pixel - handle_view = bpy.types.SpaceNodeEditor.draw_handler_add(draw_callback_view, (), 'WINDOW', 'POST_VIEW') - handle_pixel = bpy.types.SpaceNodeEditor.draw_handler_add(draw_callback_px, (), 'WINDOW', 'POST_PIXEL') + handle_view = bpy.types.SpaceNodeEditor.draw_handler_add( + draw_callback_view, (), "WINDOW", "POST_VIEW" + ) + handle_pixel = bpy.types.SpaceNodeEditor.draw_handler_add( + draw_callback_px, (), "WINDOW", "POST_PIXEL" + ) + def unregister(): global handle_view global handle_pixel - bpy.types.SpaceNodeEditor.draw_handler_remove(handle_view, 'WINDOW') - bpy.types.SpaceNodeEditor.draw_handler_remove(handle_pixel, 'WINDOW') + bpy.types.SpaceNodeEditor.draw_handler_remove(handle_view, "WINDOW") + bpy.types.SpaceNodeEditor.draw_handler_remove(handle_pixel, "WINDOW") handle_view = None - handle_pixel = None \ No newline at end of file + handle_pixel = None diff --git a/gizmo.py b/gizmo.py index 17f5721..be4968e 100644 --- a/gizmo.py +++ b/gizmo.py @@ -1,16 +1,13 @@ - import bpy -from bpy.props import (IntProperty, EnumProperty, BoolProperty) -from bpy.types import (AddonPreferences, GizmoGroup, Operator, Gizmo) +from bpy.props import IntProperty, EnumProperty, BoolProperty +from bpy.types import AddonPreferences, GizmoGroup, Operator, Gizmo from mathutils import Vector, Matrix, Euler from .constants import PICKERS from .core.picker import Picker - - ''' class RP_OT_simple_operator(bpy.types.Operator): """Tooltip""" @@ -40,10 +37,6 @@ class RP_OT_simple_operator(bpy.types.Operator): ''' - - - - class RP_GT_gizmo(Gizmo): def test_select(self, context, location): @@ -55,27 +48,25 @@ class RP_GT_gizmo(Gizmo): picker.move_event(location) - #if bpy.app.timers.is_registered(tooltip): + # if bpy.app.timers.is_registered(tooltip): # bpy.app.timers.unregister(tooltip) - #context.region.tag_redraw() - - #picker.tooltip_event(event='START') - #bpy.app.timers.register(partial(tooltip, context.region), first_interval=1) - - #print(location) + # context.region.tag_redraw() + # picker.tooltip_event(event='START') + # bpy.app.timers.register(partial(tooltip, context.region), first_interval=1) + # print(location) context.region.tag_redraw() - #print(location) + # print(location) return -1 - def draw(self, context): return - ''' + + """ def invoke(self, context, event): print(f'invoke: type={event.type}, value={event.value}, ctrl={event.ctrl}, shift={event.shift}, alt={event.alt}') return {'RUNNING_MODAL'} @@ -88,39 +79,35 @@ class RP_GT_gizmo(Gizmo): def exit(self, context, cancel): print('EXIT') - ''' - - + """ + class RP_GT_gizmogroup(GizmoGroup): - """ test gizmo button 2d """ + """test gizmo button 2d""" + bl_idname = "view3d.gizmo_button_2d" bl_label = "Test button 2d" - bl_space_type = 'NODE_EDITOR' - bl_region_type = 'WINDOW' - bl_options = {'PERSISTENT'} - + bl_space_type = "NODE_EDITOR" + bl_region_type = "WINDOW" + bl_options = {"PERSISTENT"} + @classmethod def poll(cls, context): - return context.space_data.tree_type == 'RigPickerTree' + return context.space_data.tree_type == "RigPickerTree" def setup(self, context): self.gizmo = self.gizmos.new("RP_GT_gizmo") +classes = (RP_GT_gizmo, RP_GT_gizmogroup) -classes = ( - RP_GT_gizmo, - RP_GT_gizmogroup -) - def register(): for cls in classes: bpy.utils.register_class(cls) - + def unregister(): - + for cls in reversed(classes): - bpy.utils.unregister_class(cls) \ No newline at end of file + bpy.utils.unregister_class(cls) diff --git a/operators/__init__.py b/operators/__init__.py index 0e34465..e7bd021 100644 --- a/operators/__init__.py +++ b/operators/__init__.py @@ -1,20 +1,16 @@ - - from . import material from . import picker from . import shape -modules = ( - material, - picker, - shape -) +modules = (material, picker, shape) + def register(): for mod in modules: mod.register() + def unregister(): for mod in reversed(modules): - mod.unregister() \ No newline at end of file + mod.unregister() diff --git a/operators/material.py b/operators/material.py index 499dddb..669476c 100644 --- a/operators/material.py +++ b/operators/material.py @@ -1,4 +1,3 @@ - import bpy from bpy_extras import view3d_utils @@ -6,56 +5,55 @@ from bpy_extras import view3d_utils class RP_OT_add_material(bpy.types.Operator): bl_label = "Add Ui Material" bl_idname = "rigpicker.add_mat" - #bl_options = {'REGISTER', 'UNDO'} + # bl_options = {'REGISTER', 'UNDO'} - def execute(self,context): + def execute(self, context): scene = context.scene - mat = bpy.data.materials.new('UI') + mat = bpy.data.materials.new("UI") mat.use_nodes = True for node in mat.node_tree.nodes: - if node.type == 'OUTPUT_MATERIAL': + if node.type == "OUTPUT_MATERIAL": mat_output = node else: mat.node_tree.nodes.remove(node) - emission = mat.node_tree.nodes.new('ShaderNodeEmission') - mat.node_tree.links.new(emission.outputs[0],mat_output.inputs[0]) - + emission = mat.node_tree.nodes.new("ShaderNodeEmission") + mat.node_tree.links.new(emission.outputs[0], mat_output.inputs[0]) if not context.object.data.materials: context.object.data.materials.append(mat) else: context.object.material_slots[0].material = mat - return {'FINISHED'} + return {"FINISHED"} class RP_OT_remove_material(bpy.types.Operator): bl_label = "Remove Ui Material" bl_idname = "rigpicker.remove_mat" - #bl_options = {'REGISTER', 'UNDO'} + # bl_options = {'REGISTER', 'UNDO'} - def execute(self,context): + def execute(self, context): scene = context.scene - #print(self.shape_type) + # print(self.shape_type) for mat in context.object.data.materials: bpy.data.materials.remove(mat) context.area.tag_redraw() - return {'FINISHED'} + return {"FINISHED"} class RP_OT_eyedropper_material(bpy.types.Operator): """Tooltip""" + bl_idname = "rigpicker.eyedropper_mat" bl_label = "Eye Dropper mat" - #bl_options = {'REGISTER', 'UNDO'} - - #first_mouse_x = IntProperty() - #first_value = FloatProperty() + # bl_options = {'REGISTER', 'UNDO'} + # first_mouse_x = IntProperty() + # first_value = FloatProperty() def modal(self, context, event): context.area.tag_redraw() @@ -66,7 +64,7 @@ class RP_OT_eyedropper_material(bpy.types.Operator): region = context.region rv3d = context.region_data - if event.type == 'LEFTMOUSE' and event.value == 'RELEASE': + if event.type == "LEFTMOUSE" and event.value == "RELEASE": self.mouse = event.mouse_region_x, event.mouse_region_y dg = context.evaluated_depsgraph_get() @@ -76,47 +74,48 @@ class RP_OT_eyedropper_material(bpy.types.Operator): raycast = scene.ray_cast(dg, ray_origin, view_vector) - if raycast[0]==True: + if raycast[0] == True: ob = raycast[4] if ob.data.materials: mat = ob.data.materials[0] - for shape in [o for o in context.selected_objects if o.type in ('MESH','CURVE','FONT')]: + for shape in [ + o + for o in context.selected_objects + if o.type in ("MESH", "CURVE", "FONT") + ]: if not shape.data.materials: shape.data.materials.append(mat) else: shape.material_slots[0].material = mat - - #context.space_data.draw_handler_remove(self._handle, 'WINDOW') + # context.space_data.draw_handler_remove(self._handle, 'WINDOW') context.window.cursor_modal_restore() for ob in self.temp_ob: bpy.data.objects.remove(ob) - return {'FINISHED'} + return {"FINISHED"} + # return {'FINISHED'} - #return {'FINISHED'} - - elif event.type in {'RIGHTMOUSE', 'ESC'}: - #context.object.location.x = self.first_value + elif event.type in {"RIGHTMOUSE", "ESC"}: + # context.object.location.x = self.first_value context.window.cursor_modal_restore() for ob in self.temp_ob: bpy.data.objects.remove(ob) - return {'CANCELLED'} - - return {'RUNNING_MODAL'} + return {"CANCELLED"} + return {"RUNNING_MODAL"} def invoke(self, context, event): scene = context.scene - #self.local_cursor = tuple(context.space_data.cursor.location) - #self.cursor = tuple(context.scene.cursor.location) + # self.local_cursor = tuple(context.space_data.cursor.location) + # self.cursor = tuple(context.scene.cursor.location) - curves =[o for o in context.visible_objects if o.type in ('CURVE', 'FONT')] + curves = [o for o in context.visible_objects if o.type in ("CURVE", "FONT")] self.temp_ob = [] @@ -124,17 +123,17 @@ class RP_OT_eyedropper_material(bpy.types.Operator): for c in curves: mesh = bpy.data.meshes.new_from_object(c.evaluated_get(dg)) - copy = bpy.data.objects.new(c.name+'_tmp', mesh) + copy = bpy.data.objects.new(c.name + "_tmp", mesh) copy.matrix_world = c.matrix_world for mat in c.data.materials: copy.data.materials.append(mat) scene.collection.objects.link(copy) self.temp_ob.append(copy) - #args = (self,context) - #self._handle = context.space_data.draw_handler_add(draw_callback_px, args, 'WINDOW', 'POST_PIXEL') + # args = (self,context) + # self._handle = context.space_data.draw_handler_add(draw_callback_px, args, 'WINDOW', 'POST_PIXEL') context.window_manager.modal_handler_add(self) - return {'RUNNING_MODAL'} + return {"RUNNING_MODAL"} classes = ( @@ -143,4 +142,4 @@ classes = ( RP_OT_eyedropper_material, ) -register, unregister = bpy.utils.register_classes_factory(classes) \ No newline at end of file +register, unregister = bpy.utils.register_classes_factory(classes) diff --git a/operators/picker.py b/operators/picker.py index 658b184..1a9a42a 100644 --- a/operators/picker.py +++ b/operators/picker.py @@ -6,10 +6,11 @@ from ..core.addon_utils import get_picker_collection from ..core.bl_utils import get_view_3d_override, split_path, eval_attr from ..core.geometry_utils import bounding_rect from ..core.picker import get_picker_path, pack_picker, unpack_picker, load_picker_data -#from .func_bgl import draw_callback_px -#from .func_bgl import select_bone -#from core.picker import * -#from .utils import is_over_region + +# from .func_bgl import draw_callback_px +# from .func_bgl import select_bone +# from core.picker import * +# from .utils import is_over_region import gpu from mathutils import Vector, Euler, Matrix from gpu_extras.batch import batch_for_shader @@ -18,34 +19,36 @@ import json import os - - - def is_picker_space(space_data=None): if not space_data: space_data = bpy.context.space_data - if space_data and (space_data.type == 'NODE_EDITOR' and space_data.tree_type == 'RigPickerTree'): + if space_data and ( + space_data.type == "NODE_EDITOR" and space_data.tree_type == "RigPickerTree" + ): return True - + return False class RP_OT_box_select(Operator): """Box Select bones in the picker view""" + bl_idname = "node.rp_box_select" bl_label = "Picker Box Select" - mode: EnumProperty(items=[(i, i.title(), '') for i in ('SET', 'EXTEND', 'SUBSTRACT')]) + mode: EnumProperty( + items=[(i, i.title(), "") for i in ("SET", "EXTEND", "SUBSTRACT")] + ) @classmethod def poll(cls, context): if not is_picker_space(context.space_data): return - + ob = context.object return ob and ob in PICKERS - ''' + """ def mode_from_event(self, event): if event.alt: return 'SUBSTRACT' @@ -53,17 +56,17 @@ class RP_OT_box_select(Operator): return 'EXTEND' else: return 'SET' - ''' + """ def draw_callback(self): - #print('draw callback border') + # print('draw callback border') if not self.draw_border: return - gpu.state.blend_set('ALPHA') + gpu.state.blend_set("ALPHA") + + # print('DRAW BORDER') - #print('DRAW BORDER') - self.color_shader.bind() self.color_shader.uniform_float("color", self.bg_color) self.bg_batch.draw(self.color_shader) @@ -75,93 +78,102 @@ class RP_OT_box_select(Operator): self.dash_shader.uniform_float("viewMatrix", matrix) self.dash_shader.uniform_float("dashSize", 5) self.dash_shader.uniform_float("gapSize", 4) - + self.contour_batch.draw(self.dash_shader) - gpu.state.blend_set('NONE') + gpu.state.blend_set("NONE") def invoke(self, context, event): - #print(f'invoke: type={event.type}, value={event.value}, ctrl={event.ctrl}, shift={event.shift}, alt={event.alt}') + # print(f'invoke: type={event.type}, value={event.value}, ctrl={event.ctrl}, shift={event.shift}, alt={event.alt}') - if context.object.mode != 'POSE': + if context.object.mode != "POSE": bpy.ops.object.posemode_toggle() self.timer = None - #self.mode = self.mode_from_event(event) - #self.invoke_event = event.copy() + # self.mode = self.mode_from_event(event) + # self.invoke_event = event.copy() self.region = context.region self.draw_border = False self.picker = PICKERS[context.object] self.start_mouse = event.mouse_region_x, event.mouse_region_y - #self.shader = line_strip_shader + # self.shader = line_strip_shader self.border_color = [1, 1, 1, 1] self.bg_color = [1, 1, 1, 0.05] - #args = (self, context) - self.color_shader = gpu.shader.from_builtin('UNIFORM_COLOR') - self.dash_shader = SHADERS['dashed_line'] - self._handle = bpy.types.SpaceNodeEditor.draw_handler_add(self.draw_callback, (), 'WINDOW', 'POST_PIXEL') - + # args = (self, context) + self.color_shader = gpu.shader.from_builtin("UNIFORM_COLOR") + self.dash_shader = SHADERS["dashed_line"] + self._handle = bpy.types.SpaceNodeEditor.draw_handler_add( + self.draw_callback, (), "WINDOW", "POST_PIXEL" + ) + context.window_manager.modal_handler_add(self) self.picker.press_event(self.mode) self.region.tag_redraw() - return {'RUNNING_MODAL'} - + return {"RUNNING_MODAL"} + def modal(self, context, event): self.mouse = event.mouse_region_x, event.mouse_region_y self.border = bounding_rect((self.start_mouse, self.mouse)) - self.bg_batch = batch_for_shader(self.color_shader, 'TRI_FAN', {"pos": self.border}) - self.contour_batch = batch_for_shader(self.dash_shader, 'LINE_LOOP', {"pos": self.border}) + self.bg_batch = batch_for_shader( + self.color_shader, "TRI_FAN", {"pos": self.border} + ) + self.contour_batch = batch_for_shader( + self.dash_shader, "LINE_LOOP", {"pos": self.border} + ) self.draw_border = True self.region.tag_redraw() - if event.value == 'RELEASE': + if event.value == "RELEASE": return self.release_event(context) - - return {'RUNNING_MODAL'} + + return {"RUNNING_MODAL"} def release_event(self, context): if get_picker_collection(): self.picker.assign_bone_event() - elif (self.start_mouse[0] != self.mouse[0] or self.start_mouse[1] != self.mouse[1]): + elif ( + self.start_mouse[0] != self.mouse[0] or self.start_mouse[1] != self.mouse[1] + ): self.picker.border_select(self.border, self.mode) else: self.picker.move_event(self.mouse) self.picker.release_event(self.mode) - + bpy.ops.ed.undo_push(message="Box Select") - + return self.exit(context) def exit(self, context): - bpy.types.SpaceNodeEditor.draw_handler_remove(self._handle, 'WINDOW') + bpy.types.SpaceNodeEditor.draw_handler_remove(self._handle, "WINDOW") context.region.tag_redraw() - return {'FINISHED'} + return {"FINISHED"} class RP_OT_picker_transform(Operator): """Transform Bones in the picker view""" + bl_idname = "node.picker_transform" bl_label = "Transform Bone in Picker View" - - mode : EnumProperty(items=[(m, m.title(), '') for m in ('ROTATE', 'SCALE')]) + + mode: EnumProperty(items=[(m, m.title(), "") for m in ("ROTATE", "SCALE")]) @classmethod def poll(cls, context): return context.selected_pose_bones and is_picker_space(context.space_data) - ''' + """ def draw_callback(self): gpu.state.blend_set('ALPHA') gpu.state.line_width_set(2) @@ -179,18 +191,20 @@ class RP_OT_picker_transform(Operator): gpu.state.line_width_set(1) gpu.state.blend_set('NONE') - ''' + """ def invoke(self, context, event): self.override = get_view_3d_override() - self.view_center = Vector((int(context.region.width / 2), int(context.region.height / 2))) + self.view_center = Vector( + (int(context.region.width / 2), int(context.region.height / 2)) + ) self.mouse_start = Vector((event.mouse_region_x, event.mouse_region_y)) self.mouse = Vector((0, 0)) transform_type = context.scene.tool_settings.transform_pivot_point self.center = context.active_pose_bone.matrix.to_translation() - if transform_type == 'MEDIAN_POINT': + if transform_type == "MEDIAN_POINT": origins = [b.matrix.to_translation() for b in context.selected_pose_bones] self.center = sum(origins, Vector()) / len(context.selected_pose_bones) # self.bone_data = {} @@ -198,38 +212,36 @@ class RP_OT_picker_transform(Operator): # self.bone_data[bone] = bone.matrix.copy() self.bone_data = {b: b.matrix.copy() for b in context.selected_pose_bones} - space = self.override['area'].spaces.active + space = self.override["area"].spaces.active view_matrix = space.region_3d.view_matrix - if space.region_3d.view_perspective == 'CAMERA': + if space.region_3d.view_perspective == "CAMERA": view_matrix = context.scene.camera.matrix_world - context.window.cursor_modal_set('MOVE_X') + context.window.cursor_modal_set("MOVE_X") self.view_vector = Vector((0, 0, 1)) - if self.mode == 'ROTATE': + if self.mode == "ROTATE": self.view_vector.rotate(view_matrix) - - elif self.mode == 'SCALE': + elif self.mode == "SCALE": self.view_vector = Vector((0, 0, 0)) - + self.transform_type = "VIEW" - self.transform_types = ["VIEW", 'GLOBAL', 'LOCAL'] - if context.scene.transform_orientation_slots[0].type == 'LOCAL': - self.transform_types = ["VIEW", 'LOCAL', 'GLOBAL'] + self.transform_types = ["VIEW", "GLOBAL", "LOCAL"] + if context.scene.transform_orientation_slots[0].type == "LOCAL": + self.transform_types = ["VIEW", "LOCAL", "GLOBAL"] self.transform_orientation = context.scene.transform_orientation_slots[0].type - #self.dash_shader = SHADERS['dashed_line'] - + # self.dash_shader = SHADERS['dashed_line'] - #self._handle = bpy.types.SpaceNodeEditor.draw_handler_add(self.draw_callback, (), 'WINDOW', 'POST_PIXEL') + # self._handle = bpy.types.SpaceNodeEditor.draw_handler_add(self.draw_callback, (), 'WINDOW', 'POST_PIXEL') context.window_manager.modal_handler_add(self) - return {'RUNNING_MODAL'} + return {"RUNNING_MODAL"} def change_transform_type(self): - indices = {0: 1, 1:2, 2:1} + indices = {0: 1, 1: 2, 2: 1} index = self.transform_types.index(self.transform_type) new_index = indices[index] self.transform_type = self.transform_types[new_index] @@ -237,73 +249,74 @@ class RP_OT_picker_transform(Operator): def release_event(self, context): scn = bpy.context.scene - # Insert keyframe - #bpy.ops.ed.undo_push(message="Transform") + # Insert keyframe + # bpy.ops.ed.undo_push(message="Transform") if scn.tool_settings.use_keyframe_insert_auto: try: bpy.ops.animtoolbox.insert_keyframe() except Exception: - self.report({"WARNING"}, 'You need animtoolbox addon for inserting keyframe') + self.report( + {"WARNING"}, "You need animtoolbox addon for inserting keyframe" + ) self.exit(context) def exit(self, context): - #bpy.types.SpaceNodeEditor.draw_handler_remove(self._handle, 'WINDOW') + # bpy.types.SpaceNodeEditor.draw_handler_remove(self._handle, 'WINDOW') context.window.cursor_modal_restore() - #context.region.tag_redraw() - + # context.region.tag_redraw() + def modal(self, context, event): scn = context.scene self.mouse = Vector((event.mouse_region_x, event.mouse_region_y)) - if self.mode == 'ROTATE': + if self.mode == "ROTATE": delta = (event.mouse_region_x - self.mouse_start[0]) / 100 - elif self.mode == 'SCALE': - delta = 1 + (event.mouse_region_x - self.mouse_start[0]) / 100 - #delta = (self.mouse - self.view_center).length / (self.mouse_start - self.view_center).length - + elif self.mode == "SCALE": + delta = 1 + (event.mouse_region_x - self.mouse_start[0]) / 100 + # delta = (self.mouse - self.view_center).length / (self.mouse_start - self.view_center).length transform_type = self.transform_type - #self.batch = batch_for_shader(self.dash_shader, 'LINE_STRIP', {"pos": [self.mouse_start, self.mouse]}) - #context.area.tag_redraw() - - #print(event.type, event.value) - if self.mode == 'ROTATE' and event.type == "R" and event.value == 'PRESS': + # self.batch = batch_for_shader(self.dash_shader, 'LINE_STRIP', {"pos": [self.mouse_start, self.mouse]}) + # context.area.tag_redraw() + + # print(event.type, event.value) + if self.mode == "ROTATE" and event.type == "R" and event.value == "PRESS": with context.temp_override(**self.override): self.exit(context) - bpy.ops.transform.trackball('INVOKE_DEFAULT') - return {'FINISHED'} + bpy.ops.transform.trackball("INVOKE_DEFAULT") + return {"FINISHED"} - if event.type == "LEFTMOUSE" and event.value == 'RELEASE': + if event.type == "LEFTMOUSE" and event.value == "RELEASE": self.release_event(context) - return {'FINISHED'} + return {"FINISHED"} if event.type in {"RIGHTMOUSE", "ESC"}: for bone, matrix in self.bone_data.items(): bone.matrix = matrix self.exit(context) - return {'CANCELLED'} + return {"CANCELLED"} - elif event.type == "X" and event.value == 'PRESS': + elif event.type == "X" and event.value == "PRESS": self.change_transform_type() self.view_vector = Vector((1, 0, 0)) - #transform_type = self.transform_orientation + # transform_type = self.transform_orientation - elif event.type == "Y" and event.value == 'PRESS': + elif event.type == "Y" and event.value == "PRESS": self.change_transform_type() self.view_vector = Vector((0, 1, 0)) - #transform_type = self.transform_orientation + # transform_type = self.transform_orientation - elif event.type == "Z" and event.value == 'PRESS': + elif event.type == "Z" and event.value == "PRESS": self.change_transform_type() self.view_vector = Vector((0, 0, 1)) - #transform_type = self.transform_orientation + # transform_type = self.transform_orientation elif event.type == "MOUSEMOVE": - - if self.mode == 'ROTATE': + + if self.mode == "ROTATE": transform_matrix = Matrix.Rotation(delta, 4, self.view_vector) - elif self.mode == 'SCALE': + elif self.mode == "SCALE": if self.view_vector.length: transform_matrix = Matrix.Scale(delta, 4, self.view_vector) else: @@ -314,14 +327,14 @@ class RP_OT_picker_transform(Operator): if scn.tool_settings.transform_pivot_point == "INDIVIDUAL_ORIGINS": center = matrix.to_translation() - if self.mode == 'ROTATE': - if transform_type == 'LOCAL': + if self.mode == "ROTATE": + if transform_type == "LOCAL": view_vector = self.view_vector.copy() view_vector.rotate(matrix) transform_matrix = Matrix.Rotation(delta, 4, view_vector) - elif self.mode == 'SCALE': - if transform_type == 'LOCAL': + elif self.mode == "SCALE": + if transform_type == "LOCAL": view_vector = self.view_vector.copy() view_vector.rotate(matrix) transform_matrix = Matrix.Scale(delta, 4, view_vector) @@ -335,40 +348,40 @@ class RP_OT_picker_transform(Operator): bone.matrix = mat - return {'RUNNING_MODAL'} + return {"RUNNING_MODAL"} class RP_OT_toogle_property(Operator): """Invert a bone custom property""" bl_idname = "rigpicker.toogle_property" - bl_label = 'Toogle Property' + bl_label = "Toogle Property" - data_path : StringProperty() + data_path: StringProperty() @classmethod def poll(cls, context): return is_picker_space(context.space_data) - def execute(self,context): + def execute(self, context): ob = context.object - + try: value = ob.path_resolve(self.data_path) except Exception: - return {'CANCELLED'} + return {"CANCELLED"} data = ob prop_name = self.data_path - #if '.' in self.data_path: + # if '.' in self.data_path: # data, prop = prop_name.rsplit('.', 1) - - value = type(value)(not value) - exec(f'{repr(ob)}.{self.data_path} = {value}') - #setattr(data, prop_name, value) - bpy.ops.object.mode_set(mode='OBJECT') - bpy.ops.object.mode_set(mode='POSE') + value = type(value)(not value) + exec(f"{repr(ob)}.{self.data_path} = {value}") + + # setattr(data, prop_name, value) + bpy.ops.object.mode_set(mode="OBJECT") + bpy.ops.object.mode_set(mode="POSE") if context.scene.tool_settings.use_keyframe_insert_auto: bone_name, _ = split_path(self.data_path) @@ -376,42 +389,42 @@ class RP_OT_toogle_property(Operator): context.area.tag_redraw() - return {'FINISHED'} + return {"FINISHED"} class RP_OT_reload_picker(Operator): """Reload the picker shapes""" bl_idname = "rigpicker.reload_picker" - bl_label = 'Reload Picker' + bl_label = "Reload Picker" - #@classmethod - #def poll(cls, context): + # @classmethod + # def poll(cls, context): # if not is_picker_space(context): # return def execute(self, context): - if context.object.type == 'ARMATURE': + if context.object.type == "ARMATURE": rig = context.object else: collection = get_picker_collection(context.object) rig = collection.rig_picker.rig - #print('Reload', rig) + # print('Reload', rig) load_picker_data(rig) for area in context.screen.areas: if is_picker_space(area.spaces.active): area.tag_redraw() - return {"FINISHED"} class RP_OT_toogle_bone_layer(Operator): """Toogle bone layer visibility when double clicking on a bone""" + bl_idname = "rigpicker.toogle_bone_layer" - bl_label = 'Toogle Bone Layer' + bl_label = "Toogle Bone Layer" @classmethod def poll(cls, context): @@ -421,7 +434,7 @@ class RP_OT_toogle_bone_layer(Operator): ob = context.object picker = PICKERS.get(ob) - if picker.hover_shape and picker.hover_shape.type == 'bone': + if picker.hover_shape and picker.hover_shape.type == "bone": return True def execute(self, context): @@ -442,62 +455,64 @@ class RP_OT_toogle_bone_layer(Operator): class RP_OT_context_menu_picker(Operator): """Display Menu with the custom properties of the hovered bone if any or the active bone""" + bl_idname = "node.context_menu_picker" - bl_label = 'Context Menu Picker' + bl_label = "Context Menu Picker" @classmethod def poll(cls, context): return is_picker_space(context.space_data) def execute(self, context): - bpy.ops.wm.call_menu(name='RP_MT_context_menu') + bpy.ops.wm.call_menu(name="RP_MT_context_menu") return {"FINISHED"} class RP_OT_pack_picker(Operator): """Pack Unpack the picker on the rig""" + bl_idname = "rigpicker.pack_picker" - bl_label = 'Pack Picker' + bl_label = "Pack Picker" @classmethod def poll(cls, context): ob = context.object - return (ob and ob.type == 'ARMATURE' and ob.data.rig_picker.sources) + return ob and ob.type == "ARMATURE" and ob.data.rig_picker.sources def execute(self, context): - print('Pack Pickers...') - + print("Pack Pickers...") + rig = context.object - if 'pickers' in rig.data.rig_picker.keys(): + if "pickers" in rig.data.rig_picker.keys(): unpack_picker(rig) - self.report({"INFO"}, f'The picker is unpacked') + self.report({"INFO"}, f"The picker is unpacked") return {"FINISHED"} pack_picker(rig) - if not rig.data.rig_picker['pickers']: - self.report({"ERROR"}, f'No picker have been packed') - return {"CANCELLED"} + if not rig.data.rig_picker["pickers"]: + self.report({"ERROR"}, f"No picker have been packed") + return {"CANCELLED"} - elif len(rig.data.rig_picker['pickers']) < len(rig.data.rig_picker.sources): - self.report({"WARNING"}, f'No all pickers have been packed') - return {"FINISHED"} + elif len(rig.data.rig_picker["pickers"]) < len(rig.data.rig_picker.sources): + self.report({"WARNING"}, f"No all pickers have been packed") + return {"FINISHED"} - self.report({"INFO"}, f'The picker is packed') + self.report({"INFO"}, f"The picker is packed") return {"FINISHED"} class RP_OT_call_operator(Operator): bl_idname = "rigpicker.call_operator" - bl_label = 'Call operator' + bl_label = "Call operator" - operator : StringProperty() - arguments : StringProperty() + operator: StringProperty() + arguments: StringProperty() invoke: BoolProperty() - view_3d : BoolProperty() + view_3d: BoolProperty() @classmethod def poll(cls, context): @@ -509,30 +524,30 @@ class RP_OT_call_operator(Operator): op = eval_attr(bpy.ops, properties.operator).get_rna_type() return op.description except AttributeError: - return 'Call operator' + return "Call operator" - def execute(self, context): - #context.area.tag_redraw() + def execute(self, context): + # context.area.tag_redraw() override = {} if self.view_3d: override = get_view_3d_override() - + arguments = json.loads(self.arguments or "{}") with context.temp_override(**override): try: ops = eval_attr(bpy.ops, self.operator) if self.invoke: - ops('INVOKE_DEFAULT', **arguments) + ops("INVOKE_DEFAULT", **arguments) else: ops(**arguments) except Exception as e: print(e) - self.report({"ERROR"}, f'The operator {self.operator} cannot be called') + self.report({"ERROR"}, f"The operator {self.operator} cannot be called") return {"CANCELLED"} - + context.area.tag_redraw() return {"FINISHED"} @@ -545,72 +560,78 @@ class RP_MT_context_menu(Menu): def draw(self, context): layout = self.layout col = layout.column() - col.operator_context = 'INVOKE_DEFAULT' - #col.use_property_split = True + col.operator_context = "INVOKE_DEFAULT" + # col.use_property_split = True ob = context.object picker = PICKERS.get(ob) - if picker.hover_shape and picker.hover_shape.type == 'bone': + if picker.hover_shape and picker.hover_shape.type == "bone": bone = picker.hover_shape.bone else: bone = context.active_pose_bone # Draw Space Switch Operator - if getattr(ob.data, 'space_switch'): + if getattr(ob.data, "space_switch"): space_switch = ob.data.space_switch data_paths = [f'pose.bones["{bone.name}"]["{k}"]' for k in bone.keys()] - space_bone = next((s for s in space_switch.bones if s.data_path in data_paths), None) + space_bone = next( + (s for s in space_switch.bones if s.data_path in data_paths), None + ) if space_bone: index = list(space_switch.bones).index(space_bone) value = ob.path_resolve(space_bone.data_path) - space = next((s.name for s in space_bone.spaces if s.value == value), None) + space = next( + (s.name for s in space_bone.spaces if s.value == value), None + ) - op = col.operator("spaceswitch.change_space", text=f'({space})', icon='PINNED') - op.index=index + op = col.operator( + "spaceswitch.change_space", text=f"({space})", icon="PINNED" + ) + op.index = index col.separator() - if bone: + if bone: for key in bone.keys(): - col.prop(bone,f'["{key}"]', slider=True) + col.prop(bone, f'["{key}"]', slider=True) - #layout.operator("rigpicker.show_bone_layer", text="Show Bone Layer", ).type = 'ACTIVE' - #layout.operator("rigidbody.objects_add", text="B Add Passive").type = 'PASSIVE' + # layout.operator("rigpicker.show_bone_layer", text="Show Bone Layer", ).type = 'ACTIVE' + # layout.operator("rigidbody.objects_add", text="B Add Passive").type = 'PASSIVE' class RP_OT_add_picker_collection(Operator): bl_idname = "rigpicker.add_picker_collection" bl_label = "Add a Picker Collection" bl_description = "Add a Picker Collection" - bl_options = {'UNDO', 'REGISTER'} + bl_options = {"UNDO", "REGISTER"} @classmethod def poll(cls, context): ob = context.object - return (ob and ob.type == 'ARMATURE') + return ob and ob.type == "ARMATURE" def execute(self, context): scn = context.scene ob = context.object name = ob.name - if name.endswith('_rig'): + if name.endswith("_rig"): name = name[:-4] - canvas_name = f'canevas_{name}' + canvas_name = f"canevas_{name}" canvas_points = [(-0.5, 0.5, 0), (0.5, 0.5, 0), (0.5, -0.5, 0), (-0.5, -0.5, 0)] canvas_faces = [(0, 1, 2, 3)] - + canvas_mesh = bpy.data.meshes.new(canvas_name) canvas_mesh.from_pydata(canvas_points, [], canvas_faces) canvas_mesh.update(calc_edges=True) canvas_ob = bpy.data.objects.new(canvas_name, canvas_mesh) - canvas_ob.rig_picker.shape_type = 'DISPLAY' + canvas_ob.rig_picker.shape_type = "DISPLAY" - col = bpy.data.collections.new(f'Picker {name}') + col = bpy.data.collections.new(f"Picker {name}") col.rig_picker.enabled = True col.rig_picker.rig = ob col.rig_picker.canvas = canvas_ob @@ -621,44 +642,44 @@ class RP_OT_add_picker_collection(Operator): self.report({"INFO"}, f"New Picker Collection {col.name} Created") - return {'FINISHED'} + return {"FINISHED"} class RP_OT_add_picker_source(Operator): bl_idname = "rigpicker.add_picker_source" bl_label = "Add a Picker source" bl_description = "Add a Picker source" - bl_options = {'UNDO', 'REGISTER'} + bl_options = {"UNDO", "REGISTER"} def execute(self, context): arm = context.object.data arm.rig_picker.sources.add() - return {'FINISHED'} + return {"FINISHED"} class RP_OT_remove_picker_source(Operator): bl_idname = "rigpicker.remove_picker_source" bl_label = "Delete a Picker source" bl_description = "Delete a Picker source" - bl_options = {'UNDO', 'REGISTER'} + bl_options = {"UNDO", "REGISTER"} - index : IntProperty(default=-1) + index: IntProperty(default=-1) def execute(self, context): arm = context.object.data arm.rig_picker.sources.remove(self.index) - return {'FINISHED'} + return {"FINISHED"} class RP_OT_fit_picker(Operator): bl_idname = "rigpicker.fit_picker" bl_label = "Fit Picker" bl_description = "Fit Picker in 2d view" - bl_options = {'UNDO', 'REGISTER'} + bl_options = {"UNDO", "REGISTER"} - index : IntProperty() + index: IntProperty() @classmethod def poll(cls, context): @@ -675,17 +696,22 @@ class RP_OT_fit_picker(Operator): view2d = context.region.view2d if self.index == -1: - #print('Picker Group') + # print('Picker Group') picker = picker_group view_rect = [view2d.view_to_region(*co, clip=False) for co in picker.rect] bpy.ops.view2d.zoom_border( - xmin=round(view_rect[3][0]), xmax=round(view_rect[1][0]), - ymin=round(view_rect[3][1]), ymax=round(view_rect[1][1]), - wait_for_input=False, zoom_out=False + xmin=round(view_rect[3][0]), + xmax=round(view_rect[1][0]), + ymin=round(view_rect[3][1]), + ymax=round(view_rect[1][1]), + wait_for_input=False, + zoom_out=False, ) - region_center = Vector((context.region.width * 0.5, context.region.height * 0.5)) + region_center = Vector( + (context.region.width * 0.5, context.region.height * 0.5) + ) view_center = view2d.region_to_view(*region_center) view_offset = Vector(view_center) + (picker.center - Vector(view_center)) @@ -695,21 +721,27 @@ class RP_OT_fit_picker(Operator): bpy.ops.view2d.pan(deltax=round(delta[0]), deltay=round(delta[1])) - return {'FINISHED'} + return {"FINISHED"} keymaps = [] + + def register_keymaps(): wm = bpy.context.window_manager km = wm.keyconfigs.addon.keymaps.new(name="Node Editor", space_type="NODE_EDITOR") for i in range(10): - kmi = km.keymap_items.new("rigpicker.fit_picker", type=f"NUMPAD_{i}", value="PRESS") + kmi = km.keymap_items.new( + "rigpicker.fit_picker", type=f"NUMPAD_{i}", value="PRESS" + ) kmi.properties.index = i - 1 keymaps.append((km, kmi)) - kmi = km.keymap_items.new("rigpicker.call_operator", type="NUMPAD_PERIOD", value="PRESS") + kmi = km.keymap_items.new( + "rigpicker.call_operator", type="NUMPAD_PERIOD", value="PRESS" + ) kmi.properties.operator = "view3d.view_selected" kmi.properties.view_3d = True keymaps.append((km, kmi)) @@ -719,12 +751,16 @@ def register_keymaps(): kmi.properties.arguments = '{"action": "SELECT"}' keymaps.append((km, kmi)) - kmi = km.keymap_items.new("rigpicker.call_operator", type="A", value="PRESS", alt=True) + kmi = km.keymap_items.new( + "rigpicker.call_operator", type="A", value="PRESS", alt=True + ) kmi.properties.operator = "pose.select_all" kmi.properties.arguments = '{"action": "DESELECT"}' keymaps.append((km, kmi)) - kmi = km.keymap_items.new("rigpicker.toogle_bone_layer", type="LEFTMOUSE", value="DOUBLE_CLICK") + kmi = km.keymap_items.new( + "rigpicker.toogle_bone_layer", type="LEFTMOUSE", value="DOUBLE_CLICK" + ) keymaps.append((km, kmi)) kmi = km.keymap_items.new("rigpicker.call_operator", type="X", value="PRESS") @@ -735,55 +771,69 @@ def register_keymaps(): kmi.properties.operator = "animtoolbox.insert_keyframe" keymaps.append((km, kmi)) - kmi = km.keymap_items.new("anim.keyframe_delete_v3d", type="K", value="PRESS", alt=True) + kmi = km.keymap_items.new( + "anim.keyframe_delete_v3d", type="K", value="PRESS", alt=True + ) keymaps.append((km, kmi)) kmi = km.keymap_items.new("rigpicker.call_operator", type="G", value="PRESS") - kmi.properties.operator = 'transform.translate' + kmi.properties.operator = "transform.translate" kmi.properties.view_3d = True kmi.properties.invoke = True keymaps.append((km, kmi)) - kmi = km.keymap_items.new("rigpicker.call_operator", type="G", value="PRESS", alt=True) - kmi.properties.operator = 'pose.loc_clear' + kmi = km.keymap_items.new( + "rigpicker.call_operator", type="G", value="PRESS", alt=True + ) + kmi.properties.operator = "pose.loc_clear" keymaps.append((km, kmi)) kmi = km.keymap_items.new("node.picker_transform", type="R", value="PRESS") - kmi.properties.mode = 'ROTATE' + kmi.properties.mode = "ROTATE" keymaps.append((km, kmi)) - kmi = km.keymap_items.new("rigpicker.call_operator", type="R", value="PRESS", alt=True) - kmi.properties.operator = 'pose.rot_clear' + kmi = km.keymap_items.new( + "rigpicker.call_operator", type="R", value="PRESS", alt=True + ) + kmi.properties.operator = "pose.rot_clear" keymaps.append((km, kmi)) kmi = km.keymap_items.new("node.picker_transform", type="S", value="PRESS") - kmi.properties.mode = 'SCALE' + kmi.properties.mode = "SCALE" keymaps.append((km, kmi)) - kmi = km.keymap_items.new("rigpicker.call_operator", type="S", value="PRESS", alt=True) - kmi.properties.operator = 'pose.scale_clear' + kmi = km.keymap_items.new( + "rigpicker.call_operator", type="S", value="PRESS", alt=True + ) + kmi.properties.operator = "pose.scale_clear" keymaps.append((km, kmi)) - kmi = km.keymap_items.new("node.context_menu_picker", type="RIGHTMOUSE", value="PRESS") + kmi = km.keymap_items.new( + "node.context_menu_picker", type="RIGHTMOUSE", value="PRESS" + ) keymaps.append((km, kmi)) kmi = km.keymap_items.new("node.rp_box_select", type="LEFTMOUSE", value="PRESS") - kmi.properties.mode = 'SET' + kmi.properties.mode = "SET" keymaps.append((km, kmi)) - kmi = km.keymap_items.new("node.rp_box_select", type="LEFTMOUSE", value="PRESS", shift=True) - kmi.properties.mode = 'EXTEND' + kmi = km.keymap_items.new( + "node.rp_box_select", type="LEFTMOUSE", value="PRESS", shift=True + ) + kmi.properties.mode = "EXTEND" keymaps.append((km, kmi)) - #kmi = km.keymap_items.new("node.rp_box_select", type="LEFTMOUSE", value="PRESS", ctrl=True) - kmi = km.keymap_items.new("node.rp_box_select", type="LEFTMOUSE", value="PRESS", alt=True) - kmi.properties.mode = 'SUBSTRACT' + # kmi = km.keymap_items.new("node.rp_box_select", type="LEFTMOUSE", value="PRESS", ctrl=True) + kmi = km.keymap_items.new( + "node.rp_box_select", type="LEFTMOUSE", value="PRESS", alt=True + ) + kmi.properties.mode = "SUBSTRACT" keymaps.append((km, kmi)) - #km = wm.keyconfigs.addon.keymaps.new(name="View2D") - #kmi = km.keymap_items.new("rigpicker.call_operator", type="MIDDLEMOUSE", value="PRESS") - #kmi.properties.operator = "bpy.ops.view2d.pan('INVOKE_DEFAULT')" - #keymaps.append((km, kmi)) + # km = wm.keyconfigs.addon.keymaps.new(name="View2D") + # kmi = km.keymap_items.new("rigpicker.call_operator", type="MIDDLEMOUSE", value="PRESS") + # kmi.properties.operator = "bpy.ops.view2d.pan('INVOKE_DEFAULT')" + # keymaps.append((km, kmi)) def unregister_keymaps(): @@ -791,6 +841,7 @@ def unregister_keymaps(): km.keymap_items.remove(kmi) keymaps.clear() + classes = ( RP_OT_box_select, RP_OT_toogle_property, @@ -804,19 +855,19 @@ classes = ( RP_OT_add_picker_source, RP_OT_remove_picker_source, RP_OT_fit_picker, - RP_OT_add_picker_collection - #RP_OT_ui_draw + RP_OT_add_picker_collection, + # RP_OT_ui_draw ) def register(): for cls in classes: bpy.utils.register_class(cls) - + register_keymaps() def unregister(): unregister_keymaps() for cls in reversed(classes): - bpy.utils.unregister_class(cls) \ No newline at end of file + bpy.utils.unregister_class(cls) diff --git a/operators/shape.py b/operators/shape.py index a8861ce..932a5c8 100644 --- a/operators/shape.py +++ b/operators/shape.py @@ -1,4 +1,3 @@ - import json from os.path import abspath from pathlib import Path @@ -13,23 +12,23 @@ from ..core.bl_utils import link_mat_to_object, flip_name class RP_OT_create_shape(Operator): - bl_label = 'Create UI shape' - bl_idname = 'rigpicker.create_shape' - #bl_options = {'REGISTER', 'UNDO'} + bl_label = "Create UI shape" + bl_idname = "rigpicker.create_shape" + # bl_options = {'REGISTER', 'UNDO'} @classmethod def poll(cls, context): - return (context.object and context.object.mode == 'POSE') - + return context.object and context.object.mode == "POSE" + def invoke(self, context, event): return context.window_manager.invoke_props_dialog(self) - + def draw(self, context): for col in bpy.data.collections: if col.rig_picker.enabled: - self.layout.prop(col.rig_picker, 'link_shape', text=col.name) + self.layout.prop(col.rig_picker, "link_shape", text=col.name) - def execute(self,context): + def execute(self, context): scn = context.scene vl = context.view_layer @@ -42,13 +41,15 @@ class RP_OT_create_shape(Operator): ob.rig_picker.name = name - verts=[(0,0,0)] + verts = [(0, 0, 0)] edges = [] - faces =[] + faces = [] mesh.from_pydata(verts, edges, faces) - picker_selected_cols = [col for col in bpy.data.collections if col.rig_picker.link_shape] + picker_selected_cols = [ + col for col in bpy.data.collections if col.rig_picker.link_shape + ] for col in picker_selected_cols or [scn.collection]: col.objects.link(ob) @@ -64,36 +65,36 @@ class RP_OT_create_shape(Operator): offset += 0.05 - return {'FINISHED'} + return {"FINISHED"} class RP_OT_name_from_bone(Operator): - bl_label = 'Name Shape from selected bones' - bl_idname = 'rigpicker.name_from_bone' - bl_description = 'Rename all shapes from related bones name' - #bl_options = {'REGISTER', 'UNDO'} + bl_label = "Name Shape from selected bones" + bl_idname = "rigpicker.name_from_bone" + bl_description = "Rename all shapes from related bones name" + # bl_options = {'REGISTER', 'UNDO'} - def execute(self,context): + def execute(self, context): col = get_picker_collection(context.object) for ob in col.all_objects: - if ob.rig_picker.shape_type == 'BONE': + if ob.rig_picker.shape_type == "BONE": ob.name = ob.rig_picker.name - return {'FINISHED'} + return {"FINISHED"} class RP_OT_mirror_shape(Operator): - bl_label = 'Mirror UI shape' - bl_idname = 'rigpicker.mirror_shape' - #bl_options = g + bl_label = "Mirror UI shape" + bl_idname = "rigpicker.mirror_shape" + # bl_options = g @classmethod def poll(cls, context): - return (context.object and context.object.type in ('MESH', 'CURVE', 'TEXT')) + return context.object and context.object.type in ("MESH", "CURVE", "TEXT") - def execute(self,context): + def execute(self, context): collection = get_picker_collection(context.object) objects = context.selected_objects @@ -103,9 +104,13 @@ class RP_OT_mirror_shape(Operator): continue for mod in ob.modifiers: - if (mod.type == 'NODES' and mod.node_group and mod.node_group.name == 'Symmetrize' and - mod.get('Socket_2') in objects): - + if ( + mod.type == "NODES" + and mod.node_group + and mod.node_group.name == "Symmetrize" + and mod.get("Socket_2") in objects + ): + bpy.data.objects.remove(ob) if collection.rig_picker.symmetry: @@ -116,14 +121,14 @@ class RP_OT_mirror_shape(Operator): for ob in objects: flipped_name = flip_name(ob.name) if flipped_name == ob.name: - suffix = '.L' - flipped_suffix = '.R' + suffix = ".L" + flipped_suffix = ".R" # Determine side of the object if ob.matrix_world.to_translation()[0] < x_axis: suffix, flipped_suffix = flipped_suffix, suffix - - flipped_name = f'{ob.name}{flipped_suffix}' - ob.name = f'{ob.name}{suffix}' + + flipped_name = f"{ob.name}{flipped_suffix}" + ob.name = f"{ob.name}{suffix}" flipped_object = ob.copy() flipped_object.name = flipped_name @@ -133,20 +138,20 @@ class RP_OT_mirror_shape(Operator): flipped_object.modifiers.remove(mod) # Add symmetrize modifier TODO add it into a resource - mod = flipped_object.modifiers.new(name='Symmetrize', type='NODES') - mod.node_group = bpy.data.node_groups['Symmetrize'] - mod['Socket_2'] = ob - mod['Socket_3'] = collection.rig_picker.symmetry + mod = flipped_object.modifiers.new(name="Symmetrize", type="NODES") + mod.node_group = bpy.data.node_groups["Symmetrize"] + mod["Socket_2"] = ob + mod["Socket_3"] = collection.rig_picker.symmetry for col in ob.users_collection: col.objects.link(flipped_object) - if ob.rig_picker.shape_type == 'BONE': + if ob.rig_picker.shape_type == "BONE": flipped_object.rig_picker.name = flip_name(ob.rig_picker.name) - elif ob.rig_picker.shape_type == 'FUNCTION': + elif ob.rig_picker.shape_type == "FUNCTION": args = {} - for key,value in eval(ob.rig_picker.arguments).items(): + for key, value in eval(ob.rig_picker.arguments).items(): if type(value) == list: mirrored_value = [] for item in value: @@ -158,32 +163,37 @@ class RP_OT_mirror_shape(Operator): flipped_object.rig_picker.arguments = str(args) - return {'FINISHED'} + return {"FINISHED"} class RP_OT_select_shape_type(Operator): - bl_label = 'Select Shape by Type' - bl_idname = 'rigpicker.select_shape_type' - #bl_options = {'REGISTER', 'UNDO'} + bl_label = "Select Shape by Type" + bl_idname = "rigpicker.select_shape_type" + # bl_options = {'REGISTER', 'UNDO'} - shape_type = bpy.props.EnumProperty(items =[(i.upper(), i, '') for i in ('Bone', 'Display', 'Function')]) + shape_type = bpy.props.EnumProperty( + items=[(i.upper(), i, "") for i in ("Bone", "Display", "Function")] + ) - def draw(self,context): + def draw(self, context): layout = self.layout col = layout.column() - col.prop(self,'shape_type', expand=True) + col.prop(self, "shape_type", expand=True) - def execute(self,context): + def execute(self, context): scene = context.scene - #print(self.shape_type) + # print(self.shape_type) canvas = context.scene.rig_picker.canvas if canvas: - for ob in [o for o in bpy.data.objects if o.layers==canvas.layers]: - if ob.type in ['MESH', 'CURVE', 'FONT'] and ob.rig_picker.shape_type == self.shape_type: + for ob in [o for o in bpy.data.objects if o.layers == canvas.layers]: + if ( + ob.type in ["MESH", "CURVE", "FONT"] + and ob.rig_picker.shape_type == self.shape_type + ): ob.select = True - return {'FINISHED'} + return {"FINISHED"} def invoke(self, context, event): wm = context.window_manager @@ -191,10 +201,10 @@ class RP_OT_select_shape_type(Operator): class RP_OT_save_picker(Operator): - bl_label = 'Store UI Data' - bl_idname = 'rigpicker.save_picker' + bl_label = "Store UI Data" + bl_idname = "rigpicker.save_picker" - index : IntProperty(default=-1) + index: IntProperty(default=-1) def execute(self, context): scn = context.scene @@ -205,39 +215,51 @@ class RP_OT_save_picker(Operator): else: source = context.active_object.data.rig_picker.sources[self.index].source source = Path(bpy.path.abspath(source, library=ob.data.library)).resolve() - collection = next((c for c in set(scn.collection.children_recursive) if c.rig_picker.enabled - and Path(bpy.path.abspath(c.rig_picker.destination)).resolve() == source), None) - + collection = next( + ( + c + for c in set(scn.collection.children_recursive) + if c.rig_picker.enabled + and Path(bpy.path.abspath(c.rig_picker.destination)).resolve() + == source + ), + None, + ) + if not collection: - self.report({"ERROR"}, 'No Picker found') - return {'CANCELLED'} + self.report({"ERROR"}, "No Picker found") + return {"CANCELLED"} canvas = collection.rig_picker.canvas - #rig = collection.rig_picker.rig - shapes = [o for o in collection.all_objects if o != canvas and o.type in ('MESH', 'CURVE', 'FONT')] + # rig = collection.rig_picker.rig + shapes = [ + o + for o in collection.all_objects + if o != canvas and o.type in ("MESH", "CURVE", "FONT") + ] if not canvas: - self.report({'ERROR'}, 'Choose a Canvas') - return {'CANCELLED'} - + self.report({"ERROR"}, "Choose a Canvas") + return {"CANCELLED"} picker_data = get_picker_data(collection) picker_path = Path(bpy.path.abspath(collection.rig_picker.destination)) - print(f'Save Picker to {picker_path}') + print(f"Save Picker to {picker_path}") picker_path.write_text(json.dumps(picker_data)) bpy.ops.rigpicker.reload_picker() - return {'FINISHED'} + return {"FINISHED"} + classes = ( RP_OT_create_shape, RP_OT_name_from_bone, RP_OT_mirror_shape, RP_OT_select_shape_type, - RP_OT_save_picker + RP_OT_save_picker, ) -register, unregister = bpy.utils.register_classes_factory(classes) \ No newline at end of file +register, unregister = bpy.utils.register_classes_factory(classes) diff --git a/properties.py b/properties.py index d514cd7..9d45f41 100644 --- a/properties.py +++ b/properties.py @@ -1,10 +1,16 @@ import bpy -from bpy.props import (EnumProperty, StringProperty, PointerProperty, BoolProperty, - CollectionProperty, IntProperty) +from bpy.props import ( + EnumProperty, + StringProperty, + PointerProperty, + BoolProperty, + CollectionProperty, + IntProperty, +) import inspect -''' +""" def get_operator_items(self,context): items = [] from . import func_picker as mod @@ -14,55 +20,58 @@ def get_operator_items(self,context): items.append((name,name,"")) return items -''' +""" -def bones_item(self,context): + +def bones_item(self, context): items = [] if context.scene.rig_picker.rig: for bone in context.scene.rig_picker.rig.pose.bones: - items.append((bone.name,bone.name,'')) + items.append((bone.name, bone.name, "")) return items class RP_PG_picker_source(bpy.types.PropertyGroup): - source : StringProperty(subtype='FILE_PATH') + source: StringProperty(subtype="FILE_PATH") class RP_PG_armature_ui_settings(bpy.types.PropertyGroup): name: StringProperty() - source: StringProperty(subtype='FILE_PATH') + source: StringProperty(subtype="FILE_PATH") sources: CollectionProperty(type=RP_PG_picker_source) class RP_PG_object_ui_settings(bpy.types.PropertyGroup): - shape_type: EnumProperty(items=[(i.upper(), i, "") for i in ('Bone', 'Display', 'Operator')]) - #idname: StringProperty() - #arguments: StringProperty() + shape_type: EnumProperty( + items=[(i.upper(), i, "") for i in ("Bone", "Display", "Operator")] + ) + # idname: StringProperty() + # arguments: StringProperty() operator: StringProperty() shortcut: StringProperty() name: StringProperty() class RP_PG_collection_ui_settings(bpy.types.PropertyGroup): - enabled : BoolProperty(default=False) + enabled: BoolProperty(default=False) rig: PointerProperty(type=bpy.types.Object) canvas: PointerProperty(type=bpy.types.Object) symmetry: PointerProperty(type=bpy.types.Object) - destination: StringProperty(subtype='FILE_PATH') - use_pick_bone : BoolProperty(default=False) - link_shape : BoolProperty(default=False) + destination: StringProperty(subtype="FILE_PATH") + use_pick_bone: BoolProperty(default=False) + link_shape: BoolProperty(default=False) class RP_PG_scene_ui_settings(bpy.types.PropertyGroup): - use_pick_bone : BoolProperty(default=False) + use_pick_bone: BoolProperty(default=False) class RP_OT_operator_selector(bpy.types.Operator): bl_label = "Select function" bl_idname = "rigpicker.operator_selector" bl_property = "idname" - #bl_options = {'REGISTER', 'UNDO'} + # bl_options = {'REGISTER', 'UNDO'} idname: EnumProperty(items=[]) @@ -70,12 +79,12 @@ class RP_OT_operator_selector(bpy.types.Operator): ob = context.object ob.rig_picker.idname = self.idname context.area.tag_redraw() - return {'FINISHED'} + return {"FINISHED"} def invoke(self, context, event): wm = context.window_manager wm.invoke_search_popup(self) - return {'FINISHED'} + return {"FINISHED"} classes = ( @@ -87,15 +96,23 @@ classes = ( RP_OT_operator_selector, ) + def register(): for cls in classes: bpy.utils.register_class(cls) - bpy.types.Armature.rig_picker = bpy.props.PointerProperty(type=RP_PG_armature_ui_settings) - bpy.types.Object.rig_picker = bpy.props.PointerProperty(type=RP_PG_object_ui_settings) - bpy.types.Collection.rig_picker = bpy.props.PointerProperty(type=RP_PG_collection_ui_settings) + bpy.types.Armature.rig_picker = bpy.props.PointerProperty( + type=RP_PG_armature_ui_settings + ) + bpy.types.Object.rig_picker = bpy.props.PointerProperty( + type=RP_PG_object_ui_settings + ) + bpy.types.Collection.rig_picker = bpy.props.PointerProperty( + type=RP_PG_collection_ui_settings + ) bpy.types.Scene.rig_picker = bpy.props.PointerProperty(type=RP_PG_scene_ui_settings) + def unregister(): del bpy.types.Scene.rig_picker del bpy.types.Collection.rig_picker @@ -104,4 +121,3 @@ def unregister(): for cls in reversed(classes): bpy.utils.unregister_class(cls) - diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..4b8370e --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,12 @@ +[project] +name = "rig-picker" +version = "0.1.0" +description = "Add your description here" +readme = "README.md" +requires-python = ">=3.12" +dependencies = [] + +[dependency-groups] +dev = [ + "black>=25.11.0", +] diff --git a/ui.py b/ui.py index aa9ce00..aa36fd1 100644 --- a/ui.py +++ b/ui.py @@ -1,7 +1,8 @@ import bpy from bpy.types import UIList -#import collections -#import inspect + +# import collections +# import inspect from .core.addon_utils import get_operator_from_id, get_picker_collection from .core.bl_utils import get_mat @@ -20,17 +21,19 @@ import re class RP_PT_picker_maker_panel(bpy.types.Panel): - bl_label = 'Rig Picker' - bl_category = 'Rigging' - bl_space_type = 'VIEW_3D' - bl_region_type = 'UI' + bl_label = "Rig Picker" + bl_category = "Rigging" + bl_space_type = "VIEW_3D" + bl_region_type = "UI" @classmethod def poll(cls, context): return context.object def draw_header_preset(self, context): - self.layout.operator('rigpicker.add_picker_collection', text="", icon='ADD', emboss=False) + self.layout.operator( + "rigpicker.add_picker_collection", text="", icon="ADD", emboss=False + ) def draw(self, context): ob = context.object @@ -41,11 +44,15 @@ class RP_PT_picker_maker_panel(bpy.types.Panel): col = layout.column(align=False) if collection: - col.prop_search(collection.rig_picker, 'rig', scn, 'objects', text='Rig ') - col.prop_search(collection.rig_picker, 'canvas', scn, 'objects', text='Canvas ') - col.prop_search(collection.rig_picker, 'symmetry', scn, 'objects', text='Symmetry ') + col.prop_search(collection.rig_picker, "rig", scn, "objects", text="Rig ") + col.prop_search( + collection.rig_picker, "canvas", scn, "objects", text="Canvas " + ) + col.prop_search( + collection.rig_picker, "symmetry", scn, "objects", text="Symmetry " + ) - if ob.type == 'ARMATURE': + if ob.type == "ARMATURE": box = col.box() box.enabled = not ob.data.library col = box.column(align=False) @@ -54,27 +61,41 @@ class RP_PT_picker_maker_panel(bpy.types.Panel): row.separator(factor=0.5) row.label(text="Sources") - is_packed = ('pickers' in ob.data.rig_picker.keys()) - row.operator('rigpicker.pack_picker', icon='PACKAGE' if is_packed else 'UGLYPACKAGE', text='', emboss=False) - row.operator("rigpicker.add_picker_source", icon ='ADD', text="", emboss=False) + is_packed = "pickers" in ob.data.rig_picker.keys() + row.operator( + "rigpicker.pack_picker", + icon="PACKAGE" if is_packed else "UGLYPACKAGE", + text="", + emboss=False, + ) + row.operator( + "rigpicker.add_picker_source", icon="ADD", text="", emboss=False + ) for i, item in enumerate(ob.data.rig_picker.sources): row = col.row(align=True) row.enabled = not is_packed - row.prop(item, 'source', text='') - row.operator("rigpicker.remove_picker_source", icon ='PANEL_CLOSE', text="", emboss=False).index=i + row.prop(item, "source", text="") + row.operator( + "rigpicker.remove_picker_source", + icon="PANEL_CLOSE", + text="", + emboss=False, + ).index = i if collection: - #layout.separator() - layout.prop(collection.rig_picker, 'destination', text='Filepath') - layout.operator('rigpicker.save_picker', icon='PASTEDOWN', text='Save Picker') + # layout.separator() + layout.prop(collection.rig_picker, "destination", text="Filepath") + layout.operator( + "rigpicker.save_picker", icon="PASTEDOWN", text="Save Picker" + ) class RP_PT_shape(bpy.types.Panel): - bl_label = 'Shape' - bl_category = 'Rigging' - bl_space_type = 'VIEW_3D' - bl_region_type = 'UI' + bl_label = "Shape" + bl_category = "Rigging" + bl_space_type = "VIEW_3D" + bl_region_type = "UI" bl_parent_id = "RP_PT_picker_maker_panel" def draw(self, context): @@ -86,63 +107,92 @@ class RP_PT_shape(bpy.types.Panel): col = layout.column(align=False) if collection: - #if context.collection and context.collection.rig_picker.enabled: + # if context.collection and context.collection.rig_picker.enabled: col = layout.column(align=True) row = col.row(align=True) - row.operator('rigpicker.create_shape', icon='MESH_DATA', text='Create Shape') - row.operator('rigpicker.mirror_shape', icon='MOD_MIRROR', text='Mirror Shape') - col.prop(scn.rig_picker, 'use_pick_bone', icon='EYEDROPPER', text='Auto Bone Assign') - col.operator('rigpicker.name_from_bone', icon='SORTALPHA' , text='Name From Bones') + row.operator( + "rigpicker.create_shape", icon="MESH_DATA", text="Create Shape" + ) + row.operator( + "rigpicker.mirror_shape", icon="MOD_MIRROR", text="Mirror Shape" + ) + col.prop( + scn.rig_picker, + "use_pick_bone", + icon="EYEDROPPER", + text="Auto Bone Assign", + ) + col.operator( + "rigpicker.name_from_bone", icon="SORTALPHA", text="Name From Bones" + ) else: col = layout.column(align=True) row = col.row(align=True) - row.operator('rigpicker.create_shape', icon='MESH_DATA', text='Create Shape') + row.operator( + "rigpicker.create_shape", icon="MESH_DATA", text="Create Shape" + ) - if ob.type != 'ARMATURE': + if ob.type != "ARMATURE": box = layout.box() col = box.column(align=False) material_row = col.row(align=True) - material_row.operator('rigpicker.remove_mat', icon='REMOVE', text='') - material_row.operator('rigpicker.add_mat', icon='ADD', text='') + material_row.operator("rigpicker.remove_mat", icon="REMOVE", text="") + material_row.operator("rigpicker.add_mat", icon="ADD", text="") mat = False - if ob.type in ('MESH', 'CURVE', 'FONT') and ob.data.materials: + if ob.type in ("MESH", "CURVE", "FONT") and ob.data.materials: mat = get_mat(ob) if mat and mat.node_tree: - emission_nodes = [n for n in mat.node_tree.nodes if n.type =='EMISSION'] + emission_nodes = [ + n for n in mat.node_tree.nodes if n.type == "EMISSION" + ] if emission_nodes: - material_row.prop(emission_nodes[0].inputs[0], 'default_value', text='') + material_row.prop( + emission_nodes[0].inputs[0], "default_value", text="" + ) mat = True if not mat: - material_row.label(text='No Material') - material_row.operator('rigpicker.eyedropper_mat', icon='EYEDROPPER', text='') + material_row.label(text="No Material") + material_row.operator( + "rigpicker.eyedropper_mat", icon="EYEDROPPER", text="" + ) shape_type_row = col.row(align=True) - shape_type_row.prop(ob.rig_picker,'shape_type',expand = True) - shape_type_row.operator('rigpicker.select_shape_type', text='', icon='RESTRICT_SELECT_OFF') + shape_type_row.prop(ob.rig_picker, "shape_type", expand=True) + shape_type_row.operator( + "rigpicker.select_shape_type", text="", icon="RESTRICT_SELECT_OFF" + ) + if ob.rig_picker.shape_type == "OPERATOR": - if ob.rig_picker.shape_type == 'OPERATOR': - op_row = col.row(align=True) - op_row.prop(ob.rig_picker, 'operator', text='') - op_row.operator('rigpicker.operator_selector', text='', icon='COLLAPSEMENU') + op_row.prop(ob.rig_picker, "operator", text="") + op_row.operator( + "rigpicker.operator_selector", text="", icon="COLLAPSEMENU" + ) if ob.rig_picker.operator: - col.prop(ob.rig_picker, 'name', text='Tooltip') - col.prop(ob.rig_picker,'shortcut', text='Shortcut') + col.prop(ob.rig_picker, "name", text="Tooltip") + col.prop(ob.rig_picker, "shortcut", text="Shortcut") else: - col.prop(ob.rig_picker, 'name', text='Tooltip') + col.prop(ob.rig_picker, "name", text="Tooltip") - elif ob.rig_picker.shape_type == 'BONE': + elif ob.rig_picker.shape_type == "BONE": if collection and collection.rig_picker.rig: - col.prop_search(ob.rig_picker, 'name', collection.rig_picker.rig.pose, 'bones', text='Bone') + col.prop_search( + ob.rig_picker, + "name", + collection.rig_picker.rig.pose, + "bones", + text="Bone", + ) + classes = ( - #RP_UL_picker_source, + # RP_UL_picker_source, RP_PT_picker_maker_panel, - RP_PT_shape + RP_PT_shape, ) -register, unregister = bpy.utils.register_classes_factory(classes) \ No newline at end of file +register, unregister = bpy.utils.register_classes_factory(classes) diff --git a/uv.lock b/uv.lock new file mode 100644 index 0000000..bdbf3af --- /dev/null +++ b/uv.lock @@ -0,0 +1,113 @@ +version = 1 +revision = 3 +requires-python = ">=3.12" + +[[package]] +name = "black" +version = "25.11.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "click" }, + { name = "mypy-extensions" }, + { name = "packaging" }, + { name = "pathspec" }, + { name = "platformdirs" }, + { name = "pytokens" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/8c/ad/33adf4708633d047950ff2dfdea2e215d84ac50ef95aff14a614e4b6e9b2/black-25.11.0.tar.gz", hash = "sha256:9a323ac32f5dc75ce7470501b887250be5005a01602e931a15e45593f70f6e08", size = 655669, upload-time = "2025-11-10T01:53:50.558Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/7f/12/5c35e600b515f35ffd737da7febdb2ab66bb8c24d88560d5e3ef3d28c3fd/black-25.11.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:80e7486ad3535636657aa180ad32a7d67d7c273a80e12f1b4bfa0823d54e8fac", size = 1772831, upload-time = "2025-11-10T02:03:47Z" }, + { url = "https://files.pythonhosted.org/packages/1a/75/b3896bec5a2bb9ed2f989a970ea40e7062f8936f95425879bbe162746fe5/black-25.11.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:6cced12b747c4c76bc09b4db057c319d8545307266f41aaee665540bc0e04e96", size = 1608520, upload-time = "2025-11-10T01:58:46.895Z" }, + { url = "https://files.pythonhosted.org/packages/f3/b5/2bfc18330eddbcfb5aab8d2d720663cd410f51b2ed01375f5be3751595b0/black-25.11.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6cb2d54a39e0ef021d6c5eef442e10fd71fcb491be6413d083a320ee768329dd", size = 1682719, upload-time = "2025-11-10T01:56:55.24Z" }, + { url = "https://files.pythonhosted.org/packages/96/fb/f7dc2793a22cdf74a72114b5ed77fe3349a2e09ef34565857a2f917abdf2/black-25.11.0-cp312-cp312-win_amd64.whl", hash = "sha256:ae263af2f496940438e5be1a0c1020e13b09154f3af4df0835ea7f9fe7bfa409", size = 1362684, upload-time = "2025-11-10T01:57:07.639Z" }, + { url = "https://files.pythonhosted.org/packages/ad/47/3378d6a2ddefe18553d1115e36aea98f4a90de53b6a3017ed861ba1bd3bc/black-25.11.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:0a1d40348b6621cc20d3d7530a5b8d67e9714906dfd7346338249ad9c6cedf2b", size = 1772446, upload-time = "2025-11-10T02:02:16.181Z" }, + { url = "https://files.pythonhosted.org/packages/ba/4b/0f00bfb3d1f7e05e25bfc7c363f54dc523bb6ba502f98f4ad3acf01ab2e4/black-25.11.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:51c65d7d60bb25429ea2bf0731c32b2a2442eb4bd3b2afcb47830f0b13e58bfd", size = 1607983, upload-time = "2025-11-10T02:02:52.502Z" }, + { url = "https://files.pythonhosted.org/packages/99/fe/49b0768f8c9ae57eb74cc10a1f87b4c70453551d8ad498959721cc345cb7/black-25.11.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:936c4dd07669269f40b497440159a221ee435e3fddcf668e0c05244a9be71993", size = 1682481, upload-time = "2025-11-10T01:57:12.35Z" }, + { url = "https://files.pythonhosted.org/packages/55/17/7e10ff1267bfa950cc16f0a411d457cdff79678fbb77a6c73b73a5317904/black-25.11.0-cp313-cp313-win_amd64.whl", hash = "sha256:f42c0ea7f59994490f4dccd64e6b2dd49ac57c7c84f38b8faab50f8759db245c", size = 1363869, upload-time = "2025-11-10T01:58:24.608Z" }, + { url = "https://files.pythonhosted.org/packages/67/c0/cc865ce594d09e4cd4dfca5e11994ebb51604328489f3ca3ae7bb38a7db5/black-25.11.0-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:35690a383f22dd3e468c85dc4b915217f87667ad9cce781d7b42678ce63c4170", size = 1771358, upload-time = "2025-11-10T02:03:33.331Z" }, + { url = "https://files.pythonhosted.org/packages/37/77/4297114d9e2fd2fc8ab0ab87192643cd49409eb059e2940391e7d2340e57/black-25.11.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:dae49ef7369c6caa1a1833fd5efb7c3024bb7e4499bf64833f65ad27791b1545", size = 1612902, upload-time = "2025-11-10T01:59:33.382Z" }, + { url = "https://files.pythonhosted.org/packages/de/63/d45ef97ada84111e330b2b2d45e1dd163e90bd116f00ac55927fb6bf8adb/black-25.11.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5bd4a22a0b37401c8e492e994bce79e614f91b14d9ea911f44f36e262195fdda", size = 1680571, upload-time = "2025-11-10T01:57:04.239Z" }, + { url = "https://files.pythonhosted.org/packages/ff/4b/5604710d61cdff613584028b4cb4607e56e148801ed9b38ee7970799dab6/black-25.11.0-cp314-cp314-win_amd64.whl", hash = "sha256:aa211411e94fdf86519996b7f5f05e71ba34835d8f0c0f03c00a26271da02664", size = 1382599, upload-time = "2025-11-10T01:57:57.427Z" }, + { url = "https://files.pythonhosted.org/packages/00/5d/aed32636ed30a6e7f9efd6ad14e2a0b0d687ae7c8c7ec4e4a557174b895c/black-25.11.0-py3-none-any.whl", hash = "sha256:e3f562da087791e96cefcd9dda058380a442ab322a02e222add53736451f604b", size = 204918, upload-time = "2025-11-10T01:53:48.917Z" }, +] + +[[package]] +name = "click" +version = "8.3.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "colorama", marker = "sys_platform == 'win32'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/3d/fa/656b739db8587d7b5dfa22e22ed02566950fbfbcdc20311993483657a5c0/click-8.3.1.tar.gz", hash = "sha256:12ff4785d337a1bb490bb7e9c2b1ee5da3112e94a8622f26a6c77f5d2fc6842a", size = 295065, upload-time = "2025-11-15T20:45:42.706Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/98/78/01c019cdb5d6498122777c1a43056ebb3ebfeef2076d9d026bfe15583b2b/click-8.3.1-py3-none-any.whl", hash = "sha256:981153a64e25f12d547d3426c367a4857371575ee7ad18df2a6183ab0545b2a6", size = 108274, upload-time = "2025-11-15T20:45:41.139Z" }, +] + +[[package]] +name = "colorama" +version = "0.4.6" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/d8/53/6f443c9a4a8358a93a6792e2acffb9d9d5cb0a5cfd8802644b7b1c9a02e4/colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44", size = 27697, upload-time = "2022-10-25T02:36:22.414Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", size = 25335, upload-time = "2022-10-25T02:36:20.889Z" }, +] + +[[package]] +name = "mypy-extensions" +version = "1.1.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/a2/6e/371856a3fb9d31ca8dac321cda606860fa4548858c0cc45d9d1d4ca2628b/mypy_extensions-1.1.0.tar.gz", hash = "sha256:52e68efc3284861e772bbcd66823fde5ae21fd2fdb51c62a211403730b916558", size = 6343, upload-time = "2025-04-22T14:54:24.164Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/79/7b/2c79738432f5c924bef5071f933bcc9efd0473bac3b4aa584a6f7c1c8df8/mypy_extensions-1.1.0-py3-none-any.whl", hash = "sha256:1be4cccdb0f2482337c4743e60421de3a356cd97508abadd57d47403e94f5505", size = 4963, upload-time = "2025-04-22T14:54:22.983Z" }, +] + +[[package]] +name = "packaging" +version = "25.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/a1/d4/1fc4078c65507b51b96ca8f8c3ba19e6a61c8253c72794544580a7b6c24d/packaging-25.0.tar.gz", hash = "sha256:d443872c98d677bf60f6a1f2f8c1cb748e8fe762d2bf9d3148b5599295b0fc4f", size = 165727, upload-time = "2025-04-19T11:48:59.673Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/20/12/38679034af332785aac8774540895e234f4d07f7545804097de4b666afd8/packaging-25.0-py3-none-any.whl", hash = "sha256:29572ef2b1f17581046b3a2227d5c611fb25ec70ca1ba8554b24b0e69331a484", size = 66469, upload-time = "2025-04-19T11:48:57.875Z" }, +] + +[[package]] +name = "pathspec" +version = "0.12.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/ca/bc/f35b8446f4531a7cb215605d100cd88b7ac6f44ab3fc94870c120ab3adbf/pathspec-0.12.1.tar.gz", hash = "sha256:a482d51503a1ab33b1c67a6c3813a26953dbdc71c31dacaef9a838c4e29f5712", size = 51043, upload-time = "2023-12-10T22:30:45Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/cc/20/ff623b09d963f88bfde16306a54e12ee5ea43e9b597108672ff3a408aad6/pathspec-0.12.1-py3-none-any.whl", hash = "sha256:a0d503e138a4c123b27490a4f7beda6a01c6f288df0e4a8b79c7eb0dc7b4cc08", size = 31191, upload-time = "2023-12-10T22:30:43.14Z" }, +] + +[[package]] +name = "platformdirs" +version = "4.5.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/61/33/9611380c2bdb1225fdef633e2a9610622310fed35ab11dac9620972ee088/platformdirs-4.5.0.tar.gz", hash = "sha256:70ddccdd7c99fc5942e9fc25636a8b34d04c24b335100223152c2803e4063312", size = 21632, upload-time = "2025-10-08T17:44:48.791Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/73/cb/ac7874b3e5d58441674fb70742e6c374b28b0c7cb988d37d991cde47166c/platformdirs-4.5.0-py3-none-any.whl", hash = "sha256:e578a81bb873cbb89a41fcc904c7ef523cc18284b7e3b3ccf06aca1403b7ebd3", size = 18651, upload-time = "2025-10-08T17:44:47.223Z" }, +] + +[[package]] +name = "pytokens" +version = "0.3.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/4e/8d/a762be14dae1c3bf280202ba3172020b2b0b4c537f94427435f19c413b72/pytokens-0.3.0.tar.gz", hash = "sha256:2f932b14ed08de5fcf0b391ace2642f858f1394c0857202959000b68ed7a458a", size = 17644, upload-time = "2025-11-05T13:36:35.34Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/84/25/d9db8be44e205a124f6c98bc0324b2bb149b7431c53877fc6d1038dddaf5/pytokens-0.3.0-py3-none-any.whl", hash = "sha256:95b2b5eaf832e469d141a378872480ede3f251a5a5041b8ec6e581d3ac71bbf3", size = 12195, upload-time = "2025-11-05T13:36:33.183Z" }, +] + +[[package]] +name = "rig-picker" +version = "0.1.0" +source = { virtual = "." } + +[package.dev-dependencies] +dev = [ + { name = "black" }, +] + +[package.metadata] + +[package.metadata.requires-dev] +dev = [{ name = "black", specifier = ">=25.11.0" }]