Compare commits

...

6 Commits

Author SHA1 Message Date
d90fccb014 refactor shader constants 2025-12-08 17:15:02 +01:00
d09119f9d7 fix pose bones 2025-12-08 11:51:08 +01:00
465a22d5a9 fix bone select 2025-12-05 17:59:06 +01:00
cc60a25aca add fake-bpy-module 2025-12-05 17:58:51 +01:00
355e7b23cf git ignore revs 2025-12-05 10:05:55 +01:00
622048d573 black format 2025-12-05 10:05:33 +01:00
22 changed files with 1188 additions and 831 deletions

1
.git-blame-ignore-revs Normal file
View File

@ -0,0 +1 @@
622048d573ad87c5be84f65e64a7a3290c9305d1

1
.python-version Normal file
View File

@ -0,0 +1 @@
3.12

View File

@ -8,18 +8,12 @@ bl_info = {
"warning": "", "warning": "",
"wiki_url": "", "wiki_url": "",
"tracker_url": "", "tracker_url": "",
"category": "Rigging"} "category": "Rigging",
}
import importlib import importlib
modules = ( modules = (".operators", ".properties", ".ui", ".area", ".gizmo", ".draw_handlers")
'.operators',
'.properties',
'.ui',
'.area',
'.gizmo',
'.draw_handlers'
)
import bpy import bpy
@ -29,6 +23,7 @@ if "bpy" in locals():
module = importlib.import_module(name, __name__) module = importlib.import_module(name, __name__)
importlib.reload(module) importlib.reload(module)
def register(): def register():
if bpy.app.background: if bpy.app.background:
return return
@ -36,6 +31,7 @@ def register():
module = importlib.import_module(name, __name__) module = importlib.import_module(name, __name__)
module.register() module.register()
def unregister(): def unregister():
if bpy.app.background: if bpy.app.background:
return return

86
area.py
View File

@ -6,34 +6,35 @@ from .constants import PICKERS
# Derived from the NodeTree base type, similar to Menu, Operator, Panel, etc. # Derived from the NodeTree base type, similar to Menu, Operator, Panel, etc.
class RigPickerTree(NodeTree): class RigPickerTree(NodeTree):
# Description string # 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. # Optional identifier string. If not explicitly defined, the python class name is used.
# Label for nice name display # Label for nice name display
bl_label = "Rig Picker" bl_label = "Rig Picker"
# Icon identifier # Icon identifier
bl_icon = 'OUTLINER_DATA_ARMATURE' bl_icon = "OUTLINER_DATA_ARMATURE"
class RP_MT_picker(Menu): class RP_MT_picker(Menu):
"""Picker""" """Picker"""
bl_label = "Picker" bl_label = "Picker"
def draw(self, context): def draw(self, context):
layout = self.layout layout = self.layout
scn = context.scene scn = context.scene
layout.operator("rigpicker.reload_picker", icon="FILE_REFRESH")
layout.operator('rigpicker.reload_picker', icon='FILE_REFRESH')
row = layout.row(align=True) row = layout.row(align=True)
# Has at least one picker collection in the scene # Has at least one picker collection in the scene
if not [c.rig_picker.enabled for c in scn.collection.children_recursive]: if not [c.rig_picker.enabled for c in scn.collection.children_recursive]:
row.enabled = False 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): class RP_MT_animation(Menu):
"""Picker""" """Picker"""
bl_label = "Animation" bl_label = "Animation"
def draw(self, context): def draw(self, context):
@ -47,38 +48,49 @@ class RP_MT_animation(Menu):
row.enabled = False row.enabled = False
layout.separator() 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.operator = "pose.select_all"
op.arguments = '{"action": "SELECT"}' 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.operator = "pose.select_all"
op.arguments = '{"action": "DESELECT"}' 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.operator = "view3d.view_selected"
op.view_3d = True op.view_3d = True
layout.separator() layout.separator()
layout.operator("rigpicker.call_operator", text='Insert Keyframe').operator="animtoolbox.insert_keyframe" layout.operator("rigpicker.call_operator", text="Insert Keyframe").operator = (
layout.operator("anim.keyframe_delete_v3d", text='Delete Keyframe') "animtoolbox.insert_keyframe"
)
layout.operator("anim.keyframe_delete_v3d", text="Delete Keyframe")
layout.separator() layout.separator()
op = layout.operator("rigpicker.call_operator", text='Move') op = layout.operator("rigpicker.call_operator", text="Move")
op.operator="transform.translate" op.operator = "transform.translate"
op.invoke = True op.invoke = True
op.view_3d = True op.view_3d = True
layout.operator("node.picker_transform", text='Rotate').mode='ROTATE' layout.operator("node.picker_transform", text="Rotate").mode = "ROTATE"
layout.operator("node.picker_transform", text='Scale').mode='SCALE' layout.operator("node.picker_transform", text="Scale").mode = "SCALE"
layout.separator() layout.separator()
layout.operator("rigpicker.call_operator", text='Reset Bone').operator="animtoolbox.reset_bone" layout.operator("rigpicker.call_operator", text="Reset Bone").operator = (
layout.operator("rigpicker.call_operator", text='Clear Location').operator='pose.loc_clear' "animtoolbox.reset_bone"
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="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): def draw_header(self, context):
if not context.space_data.tree_type == 'RigPickerTree': if not context.space_data.tree_type == "RigPickerTree":
self._draw(context) self._draw(context)
return return
@ -88,9 +100,9 @@ def draw_header(self, context):
layout.template_header() layout.template_header()
if not context.space_data.node_tree: 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: 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 context.space_data.node_tree = ntree
@ -99,7 +111,7 @@ def draw_header(self, context):
row.menu("RP_MT_animation") row.menu("RP_MT_animation")
ob = context.object ob = context.object
if not ob or not ob.type == 'ARMATURE': if not ob or not ob.type == "ARMATURE":
return return
picker_group = PICKERS.get(ob) picker_group = PICKERS.get(ob)
@ -108,8 +120,10 @@ def draw_header(self, context):
if scn.rig_picker.use_pick_bone: if scn.rig_picker.use_pick_bone:
layout.alert = True layout.alert = True
layout.label(text='Auto Bone Assign') layout.label(text="Auto Bone Assign")
layout.prop(scn.rig_picker, 'use_pick_bone', icon='PANEL_CLOSE', text='', emboss=False) layout.prop(
scn.rig_picker, "use_pick_bone", icon="PANEL_CLOSE", text="", emboss=False
)
layout.separator_spacer() layout.separator_spacer()
row = layout.row() row = layout.row()
@ -120,43 +134,44 @@ def draw_header(self, context):
row = layout.row(align=True) row = layout.row(align=True)
for i, picker in enumerate(picker_group.pickers): 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): def tools_from_context(context, mode=None):
sp = context.space_data 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 [] return []
else: else:
return NODE_PT_tools_active._tools_from_context(context, mode) return NODE_PT_tools_active._tools_from_context(context, mode)
def tool_set_by_id(self, context): def tool_set_by_id(self, context):
sd = context.space_data 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"} return {"FINISHED"}
else: else:
return self._execute(context) return self._execute(context)
@classmethod @classmethod
def poll(cls, context): def poll(cls, context):
sp = context.space_data 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 return False
else: else:
return self._poll(context) return self._poll(context)
classes = (
RigPickerTree, classes = (RigPickerTree, RP_MT_picker, RP_MT_animation)
RP_MT_picker,
RP_MT_animation
)
def register(): def register():
# Remove the tools inside the picker space # 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 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 NODE_PT_tools_active._tools_from_context = NODE_PT_tools_active.tools_from_context
@ -168,6 +183,7 @@ def register():
for cls in classes: for cls in classes:
bpy.utils.register_class(cls) bpy.utils.register_class(cls)
def unregister(): def unregister():
for cls in reversed(classes): for cls in reversed(classes):
bpy.utils.unregister_class(cls) bpy.utils.unregister_class(cls)

View File

@ -1,6 +1,7 @@
import gpu import gpu
from pathlib import Path from pathlib import Path
class LazyDict(dict): class LazyDict(dict):
def __getitem__(self, k): def __getitem__(self, k):
v = super().__getitem__(k) v = super().__getitem__(k)
@ -19,11 +20,32 @@ PICKERS = {}
MODULE_DIR = Path(__file__).parent MODULE_DIR = Path(__file__).parent
SHADER_DIR = MODULE_DIR / 'shaders' SHADER_DIR = MODULE_DIR / "shaders"
SHADERS = LazyDict() SHADERS = LazyDict()
vertex_shader = Path(SHADER_DIR, "dash_shader.vert").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') fragment_shader = Path(SHADER_DIR, "dash_shader.frag").read_text(encoding="utf-8")
SHADERS['dashed_line'] = lambda : gpu.types.GPUShader(vertex_shader, fragment_shader)
def create_dashed_line_shader() -> gpu.types.GPUShader:
shader_info = gpu.types.GPUShaderCreateInfo()
shader_info.vertex_in(0, "VEC2", "pos")
shader_info.fragment_out(0, "VEC4", "fragColor")
shader_info.push_constant("VEC4", "color")
shader_info.push_constant("MAT4", "viewMatrix")
shader_info.push_constant("FLOAT", "dashSize")
shader_info.push_constant("FLOAT", "gapSize")
shader_info.vertex_source(vertex_shader)
shader_info.fragment_source(fragment_shader)
shader = gpu.shader.create_from_info(shader_info)
del shader_info
return shader
SHADERS["dashed_line"] = (
create_dashed_line_shader # lambda: gpu.types.GPUShader(vertex_shader, fragment_shader)
)

View File

@ -1,19 +1,23 @@
import bpy import bpy
from .bl_utils import get_mat, get_collection_parents from .bl_utils import get_mat, get_collection_parents
def get_picker_collection(ob=None): def get_picker_collection(ob: bpy.types.Object | None = None):
"""Return the picker collection of an object""" """Return the picker collection of an object"""
if not ob: if not ob:
ob = bpy.context.object ob = bpy.context.object
if not ob:
raise Exception("No object in context")
for col in ob.users_collection: for col in ob.users_collection:
if col.rig_picker.enabled: if col.rig_picker.enabled:
return col 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 return picker_col
@ -25,7 +29,7 @@ def is_shape(ob):
shapes = {ob for col in canvas.users_collection for ob in col.all_objects} 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 True
return False return False
@ -39,15 +43,15 @@ def get_object_color(ob):
if not mat or not mat.node_tree or not mat.node_tree.nodes: if not mat or not mat.node_tree or not mat.node_tree.nodes:
return return
emit_node = mat.node_tree.nodes.get('Emission') emit_node = mat.node_tree.nodes.get("Emission")
if not emit_node: if not emit_node:
return return
return emit_node.inputs['Color'].default_value return emit_node.inputs["Color"].default_value
def get_operator_from_id(idname): def get_operator_from_id(idname):
if not '.' in idname: if not "." in idname:
return return
m, o = idname.split(".") m, o = idname.split(".")

View File

@ -1,4 +1,3 @@
import bpy import bpy
@ -30,12 +29,13 @@ def get_collection_parents(col, root=None, cols=None):
cols = get_collection_parents(col, root=sub, cols=cols) cols = get_collection_parents(col, root=sub, cols=cols)
return cols return cols
def get_view_3d_override(): def get_view_3d_override():
windows = bpy.context.window_manager.windows 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: if not areas:
print('No view 3d found') print("No view 3d found")
return return
view_3d = None view_3d = None
@ -44,80 +44,88 @@ def get_view_3d_override():
view_3d = area view_3d = area
if not view_3d: if not view_3d:
view_3d = max(areas, key=lambda x :x.width) view_3d = max(areas, key=lambda x: x.width)
return {"area": view_3d, "region": view_3d.regions[-1]}
return {'area': view_3d, 'region': view_3d.regions[-1]}
def get_mat(ob): def get_mat(ob):
for sl in ob.material_slots: for sl in ob.material_slots:
if sl.material: if sl.material:
return sl.material return sl.material
def link_mat_to_object(ob): def link_mat_to_object(ob):
for sl in ob.material_slots: for sl in ob.material_slots:
m = sl.material m = sl.material
sl.link = 'OBJECT' sl.link = "OBJECT"
sl.material = m sl.material = m
def eval_attr(ob, name): def eval_attr(ob, name):
resolved = ob resolved = ob
for o in name.split("."): for o in name.split("."):
resolved = getattr(resolved, o) resolved = getattr(resolved, o)
return resolved return resolved
def flip_name(name): def flip_name(name):
if not name: if not name:
return 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:]) flipped_name = bpy.utils.flip_name(name[:-2][2:])
return f'["{flipped_name}"]' return f'["{flipped_name}"]'
else: else:
return bpy.utils.flip_name(name) return bpy.utils.flip_name(name)
def split_path(path) :
try : def split_path(path):
try:
bone_name = path.split('["')[1].split('"]')[0] bone_name = path.split('["')[1].split('"]')[0]
except Exception: except Exception:
bone_name = None bone_name = None
try : try:
prop_name = path.split('["')[2].split('"]')[0] prop_name = path.split('["')[2].split('"]')[0]
except Exception: except Exception:
prop_name = None prop_name = None
return bone_name, prop_name return bone_name, prop_name
def hide_layers(args): def hide_layers(args):
""" """ """ """
ob = bpy.context.object ob = bpy.context.object
layers = [] layers = []
for bone in [b for b in ob.pose.bones if b.bone.select]: 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: if l and i not in layers:
layers.append(i) layers.append(i)
for i in layers: for i in layers:
ob.data.layers[i] = not ob.data.layers[i] ob.data.layers[i] = not ob.data.layers[i]
def select_layer(args): def select_layer(args):
ob = bpy.context.object ob = bpy.context.object
layers =[] layers = []
for bone in [b for b in ob.pose.bones if b.bone.select]: 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: for l in bone_layers:
if l not in layers: if l not in layers:
layers.append(l) layers.append(l)
for bone in ob.pose.bones: 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)): if len(set(bone_layers).intersection(layers)):
bone.bone.select = True bone.bone.select = True
def hide_bones(args): def hide_bones(args):
ob = bpy.context.object ob = bpy.context.object
selected_bone = [b for b in ob.pose.bones if b.bone.select] 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: for bone in selected_bone:
bone.bone.hide = visibility bone.bone.hide = visibility
def select_all(args): def select_all(args):
ob = bpy.context.object ob = bpy.context.object
shapes = ob.data.rig_picker['shapes'] shapes = ob.data.rig_picker["shapes"]
bones = [s['bone'] for s in shapes if s['shape_type']=='BONE'] bones = [s["bone"] for s in shapes if s["shape_type"] == "BONE"]
for bone_name in bones: for bone_name in bones:
bone = ob.pose.bones.get(bone_name) bone = ob.pose.bones.get(bone_name)

View File

@ -1,28 +1,31 @@
import bpy import bpy
from mathutils import Vector from mathutils import Vector
from mathutils.geometry import intersect_line_line_2d from mathutils.geometry import intersect_line_line_2d
def is_over_region(self,context,event): def is_over_region(self, context, event):
inside = 2 < event.mouse_region_x < context.region.width -2 and \ inside = (
2 < event.mouse_region_y < context.region.height -2 and \ 2 < event.mouse_region_x < context.region.width - 2
[a for a in context.screen.areas if a.as_pointer()==self.adress] and \ and 2 < event.mouse_region_y < context.region.height - 2
not context.screen.show_fullscreen and [a for a in context.screen.areas if a.as_pointer() == self.adress]
and not context.screen.show_fullscreen
)
return inside return inside
def bound_box_center(ob): 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] x = [v[0] for v in points]
y = [v[1] for v in points] y = [v[1] for v in points]
z = [v[2] 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): 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) y_points = sorted(p[1] for p in points)
return [ return [
@ -32,28 +35,31 @@ def bounding_rect(points):
Vector((x_points[0], y_points[0])), 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): def intersect_rects(rect, border): # returns None if rectangles don't intersect
return dx*dy 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): 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) out = Vector(outside_point)
pt = Vector(point) pt = Vector(point)
intersections = 0 intersections = 0
for loop in loops: for loop in loops:
for i,p in enumerate(loop): for i, p in enumerate(loop):
a = Vector(verts[loop[i-1]]) a = Vector(verts[loop[i - 1]])
b = Vector(verts[p]) b = Vector(verts[p])
if intersect_line_line_2d(pt,out,a,b): if intersect_line_line_2d(pt, out, a, b):
intersections += 1 intersections += 1
if intersections%2 == 1: #chek if the nb of intersection is odd if intersections % 2 == 1: # chek if the nb of intersection is odd
return True return True

View File

@ -1,11 +1,13 @@
import bpy import bpy
import gpu import gpu
from gpu_extras.batch import batch_for_shader from gpu_extras.batch import batch_for_shader
import blf import blf
from mathutils import bvhtree, Vector, Color from mathutils import bvhtree, Vector, Color
from mathutils.geometry import (intersect_point_quad_2d, intersect_point_tri_2d, from mathutils.geometry import (
intersect_tri_tri_2d) intersect_point_quad_2d,
intersect_point_tri_2d,
intersect_tri_tri_2d,
)
from ..constants import PICKERS from ..constants import PICKERS
from .addon_utils import get_operator_from_id from .addon_utils import get_operator_from_id
from .geometry_utils import bounding_rect from .geometry_utils import bounding_rect
@ -19,9 +21,17 @@ import threading
class Shape: class Shape:
def __init__(self, picker, points, polygons=None, edges=None, tooltip='', color=None, source_name=''): def __init__(
self,
self.type = 'display' picker,
points,
polygons=None,
edges=None,
tooltip="",
color=None,
source_name="",
):
self.type = "display"
self.picker = picker self.picker = picker
self.rig = picker.rig self.rig = picker.rig
self.source_name = source_name self.source_name = source_name
@ -29,11 +39,11 @@ class Shape:
self.hover = False self.hover = False
self.press = False self.press = False
self.shader = gpu.shader.from_builtin('UNIFORM_COLOR') self.shader = gpu.shader.from_builtin("UNIFORM_COLOR")
self.shader.bind() self.shader.bind()
#self.hover_shader = gpu.shader.from_builtin('UNIFORM_COLOR') # self.hover_shader = gpu.shader.from_builtin('UNIFORM_COLOR')
#self.hover_shader.uniform_float("color", [1, 1, 1, 0.1]) # self.hover_shader.uniform_float("color", [1, 1, 1, 0.1])
self.color = color self.color = color
self.hover_color = self.brighten_color(self.color) self.hover_color = self.brighten_color(self.color)
@ -43,8 +53,12 @@ class Shape:
self.polygons = polygons or [] self.polygons = polygons or []
self.edges = edges or [] self.edges = edges or []
self.p_batch = batch_for_shader(self.shader, 'TRIS', {"pos": self.points}, indices=self.polygons) self.p_batch = batch_for_shader(
self.e_batch = batch_for_shader(self.shader, 'LINES', {"pos": self.points}, indices=self.edges) 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) self.rect = bounding_rect(points)
@ -70,16 +84,15 @@ class Shape:
elif len(color) == 4: elif len(color) == 4:
color = list(color) color = list(color)
else: else:
raise Exception('color must have a len of 3 or 4') raise Exception("color must have a len of 3 or 4")
else: else:
raise Exception(f'color is {type(color)} must be None or (tuple, list)') raise Exception(f"color is {type(color)} must be None or (tuple, list)")
#self.shader.uniform_float("color", color) # self.shader.uniform_float("color", color)
self._color = color self._color = color
def draw(self): def draw(self):
# self.shader.bind()
#self.shader.bind()
self.shader.uniform_float("color", self.color) self.shader.uniform_float("color", self.color)
if self.polygons: if self.polygons:
@ -101,19 +114,36 @@ class Shape:
self.hover = False self.hover = False
return False return False
def press_event(self, mode='SET'): def press_event(self, mode="SET"):
self.press = True self.press = True
def release_event(self, mode='SET'): def release_event(self, mode="SET"):
self.press = False self.press = False
class BoneShape(Shape): class BoneShape(Shape):
def __init__(self, picker, points, polygons, edges, bone, tooltip='', color=None, source_name=''): def __init__(
super().__init__(picker, points=points, polygons=polygons, edges=edges, self,
tooltip=tooltip, color=color, source_name=source_name) 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.bone = bone
self.active_color = [1, 1, 1, 0.1] self.active_color = [1, 1, 1, 0.1]
self.select_color = self.brighten_color(self.hover_color) self.select_color = self.brighten_color(self.hover_color)
@ -125,23 +155,29 @@ class BoneShape(Shape):
if not self.bone: if not self.bone:
return False 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 @property
def active(self): def active(self):
if not self.bone: if not self.bone:
return False 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 @property
def hide(self): def hide(self):
if not self.bone: if not self.bone:
return False return False
#return self.bone not in (bpy.context.visible_pose_bones or []) # 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.bone.hide or not any(
l.is_visible for l in self.bone.bone.collections
)
@property @property
def bone_color(self): def bone_color(self):
@ -149,45 +185,44 @@ class BoneShape(Shape):
return [0, 0, 0, 1] return [0, 0, 0, 1]
if self.select and self.active: if self.select and self.active:
return self.bone_colors['active'] return self.bone_colors["active"]
elif self.select: elif self.select:
return self.bone_colors['select'] return self.bone_colors["select"]
elif self.hide: elif self.hide:
return self.bone_colors['hide'] return self.bone_colors["hide"]
else: else:
return self.bone_colors['normal'] return self.bone_colors["normal"]
def get_bone_colors(self): def get_bone_colors(self):
theme = bpy.context.preferences.themes["Default"]
theme = bpy.context.preferences.themes['Default']
bone_colors = { bone_colors = {
'select': [*theme.view_3d.bone_pose, 1], "select": [*theme.view_3d.bone_pose, 1],
'normal': [0.05, 0.05, 0.05, 1], "normal": [0.05, 0.05, 0.05, 1],
'active': [*theme.view_3d.bone_pose_active, 1], "active": [*theme.view_3d.bone_pose_active, 1],
'hide': [0.85, 0.85, 0.85, 0.2], "hide": [0.85, 0.85, 0.85, 0.2],
} }
if not self.bone: if not self.bone:
return bone_colors return bone_colors
if self.bone.color.palette == 'CUSTOM': if self.bone.color.palette == "CUSTOM":
bone_color = self.bone bone_color = self.bone
elif self.bone.color.palette == 'DEFAULT': elif self.bone.color.palette == "DEFAULT":
bone_color = self.bone.bone bone_color = self.bone.bone
normal_color = bone_color.color.custom.normal.copy() normal_color = bone_color.color.custom.normal.copy()
normal_color.s *= 0.75 normal_color.s *= 0.75
bone_colors['normal'] = [*normal_color, 1] bone_colors["normal"] = [*normal_color, 1]
bone_colors['select'] = [*bone_color.color.custom.select, 1] bone_colors["select"] = [*bone_color.color.custom.select, 1]
bone_colors['active'] = [*bone_color.color.custom.active, 1] bone_colors["active"] = [*bone_color.color.custom.active, 1]
bone_colors['hide'] = [*normal_color, 0.1] bone_colors["hide"] = [*normal_color, 0.1]
return bone_colors return bone_colors
def draw_hided(self): def draw_hided(self):
gpu.state.blend_set('ALPHA') gpu.state.blend_set("ALPHA")
gpu.state.line_width_set(1.0) gpu.state.line_width_set(1.0)
line_color = (1, 1, 1, 0.1) line_color = (1, 1, 1, 0.1)
@ -204,13 +239,13 @@ class BoneShape(Shape):
self.shader.uniform_float("color", line_color) self.shader.uniform_float("color", line_color)
self.e_batch.draw(self.shader) self.e_batch.draw(self.shader)
gpu.state.blend_set('NONE') gpu.state.blend_set("NONE")
def draw_zero_scaled(self): def draw_zero_scaled(self):
gpu.state.blend_set('ALPHA') gpu.state.blend_set("ALPHA")
gpu.state.line_width_set(1.0) 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]) color = Color(self.color[:3])
if self.hover or self.select: if self.hover or self.select:
color.v += 0.05 color.v += 0.05
@ -223,10 +258,10 @@ class BoneShape(Shape):
self.shader.uniform_float("color", line_color) self.shader.uniform_float("color", line_color)
self.e_batch.draw(self.shader) self.e_batch.draw(self.shader)
gpu.state.blend_set('NONE') gpu.state.blend_set("NONE")
def draw(self): def draw(self):
gpu.state.blend_set('ALPHA') gpu.state.blend_set("ALPHA")
gpu.state.line_width_set(1.0) gpu.state.line_width_set(1.0)
if self.hide: if self.hide:
@ -246,7 +281,7 @@ class BoneShape(Shape):
self.p_batch.draw(self.shader) self.p_batch.draw(self.shader)
# Draw Outline # Draw Outline
gpu.state.blend_set('NONE') gpu.state.blend_set("NONE")
if self.select or self.active: if self.select or self.active:
gpu.state.line_width_set(2.0) gpu.state.line_width_set(2.0)
@ -256,49 +291,46 @@ class BoneShape(Shape):
gpu.state.line_width_set(1.0) gpu.state.line_width_set(1.0)
def assign_bone_event(self): def assign_bone_event(self):
# print('assign_bone_event', self)
#print('assign_bone_event', self)
scn = bpy.context.scene scn = bpy.context.scene
rig = self.picker.rig rig = self.picker.rig
source_object = scn.objects.get(self.source_name) source_object = scn.objects.get(self.source_name)
if not source_object: if not source_object:
print(f'Source object {self.source_name} not found') print(f"Source object {self.source_name} not found")
return return
active_bone = rig.data.bones.active active_bone = rig.data.bones.active
if not active_bone: if not active_bone:
print('You need to have an active bone') print("You need to have an active bone")
return return
#print(active_bone, source_object) # print(active_bone, source_object)
#print(rig) # print(rig)
source_object.name = rig.data.bones.active.name source_object.name = rig.data.bones.active.name
source_object.rig_picker.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) super().release_event(mode)
if self.hide or not self.bone: if self.hide or not self.bone:
return return
select = True select = True
if mode == 'SUBSTRACT': if mode == "SUBSTRACT":
select = False select = False
self.bone.bone.select = select self.rig.pose.bones.get(self.bone.bone.name).select = select
if self.hover: if self.hover:
if mode != "SUBSTRACT":
if mode != 'SUBSTRACT':
self.rig.data.bones.active = self.bone.bone 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: if not self.bone:
return return
if self.hide: if self.hide:
self.bone.bone.select = False self.rig.pose.bones.get(self.bone.bone.name).select = False
return return
rect_tri1 = self.rect[0], self.rect[1], self.rect[2] rect_tri1 = self.rect[0], self.rect[1], self.rect[2]
@ -307,77 +339,79 @@ class BoneShape(Shape):
border_tri1 = border[0], border[1], border[2] border_tri1 = border[0], border[1], border[2]
border_tri2 = border[2], border[3], border[0] border_tri2 = border[2], border[3], border[0]
if (not intersect_tri_tri_2d(*border_tri1, *rect_tri1) and if (
not intersect_tri_tri_2d(*border_tri1, *rect_tri2) and not intersect_tri_tri_2d(*border_tri1, *rect_tri1)
not intersect_tri_tri_2d(*border_tri2, *rect_tri1) and and not intersect_tri_tri_2d(*border_tri1, *rect_tri2)
not intersect_tri_tri_2d(*border_tri2, *rect_tri2)): and not intersect_tri_tri_2d(*border_tri2, *rect_tri1)
and not intersect_tri_tri_2d(*border_tri2, *rect_tri2)
):
return return
select = True select = True
if mode == 'SUBSTRACT': if mode == "SUBSTRACT":
select = False select = False
for polygon in self.polygons: for polygon in self.polygons:
points = [self.points[i] for i in polygon] points = [self.points[i] for i in polygon]
if intersect_tri_tri_2d(*border_tri1, *points): if intersect_tri_tri_2d(*border_tri1, *points):
self.bone.bone.select = select self.rig.pose.bones.get(self.bone.bone.name).select = select
return return
if intersect_tri_tri_2d(*border_tri2, *points): if intersect_tri_tri_2d(*border_tri2, *points):
self.bone.bone.select = select self.rig.pose.bones.get(self.bone.bone.name).select = select
return return
'''
for b in border:
if intersect_point_tri_2d(b, *points):
self.bone.bone.select = select
return
for p in points:
if intersect_point_quad_2d(p, *border):
self.bone.bone.select = select
return
'''
class OperatorShape(Shape): class OperatorShape(Shape):
def __init__(self, picker, points, polygons, operator, tooltip='', color=None, source_name=''): def __init__(
super().__init__(picker, points=points, polygons=polygons, tooltip=tooltip, self, picker, points, polygons, operator, tooltip="", color=None, source_name=""
color=color, source_name=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.active_color = [1, 1, 1, 0.15]
self.press_color = [0, 0, 0, 0.25] self.press_color = [0, 0, 0, 0.25]
self.operator = operator self.operator = operator
#self.arguments = arguments#{k:eval(v)} # self.arguments = arguments#{k:eval(v)}
#self.operator = get_operator_from_id(self.operator) # self.operator = get_operator_from_id(self.operator)
if not tooltip: 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): def parse_args(self):
args = self.reg_args.split(self.arguments)[1:] args = self.reg_args.split(self.arguments)[1:]
#print(args, zip(args[::2], args[1::2])) #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 zip(args[::2], args[1::2])}
#return {k:eval(v) for k, v in self.reg_args.split(self.arguments)} #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) super().release_event(mode)
#args = self.parse_args() # args = self.parse_args()
if not self.operator: if not self.operator:
return return
exec(self.operator) exec(self.operator)
#f'bpy.ops;{idname}' # f'bpy.ops;{idname}'
#print(self.idname) # print(self.idname)
#print(self.arguments) # print(self.arguments)
#else: # else:
# self.bone.bone.select = False # self.bone.bone.select = False
def draw(self): def draw(self):
@ -390,10 +424,10 @@ class OperatorShape(Shape):
else: else:
return return
gpu.state.blend_set('ALPHA') gpu.state.blend_set("ALPHA")
self.shader.uniform_float("color", color) self.shader.uniform_float("color", color)
self.p_batch.draw(self.shader) self.p_batch.draw(self.shader)
gpu.state.blend_set('NONE') gpu.state.blend_set("NONE")
class Picker: class Picker:
@ -406,48 +440,48 @@ class Picker:
self.shapes = [] self.shapes = []
self.box_select = None self.box_select = None
self.hover_shape = None self.hover_shape = None
self.shader = gpu.shader.from_builtin('UNIFORM_COLOR') self.shader = gpu.shader.from_builtin("UNIFORM_COLOR")
self.mouse = None self.mouse = None
for shape_data in shapes: for shape_data in shapes:
if not shape_data['points']: if not shape_data["points"]:
continue continue
if shape_data['type'] in ('CANVAS', 'DISPLAY'): if shape_data["type"] in ("CANVAS", "DISPLAY"):
shape = Shape( shape = Shape(
self, self,
points=shape_data['points'], points=shape_data["points"],
polygons=shape_data['polygons'], polygons=shape_data["polygons"],
edges=shape_data['edges'], edges=shape_data["edges"],
color=shape_data['color'] color=shape_data["color"],
) )
elif shape_data['type'] == 'BONE': elif shape_data["type"] == "BONE":
bone = rig.pose.bones.get(shape_data['bone']) bone = rig.pose.bones.get(shape_data["bone"])
#if not bone: # if not bone:
# print(f'Bone {shape_data["bone"]} not exist') # print(f'Bone {shape_data["bone"]} not exist')
# continue # continue
shape = BoneShape( shape = BoneShape(
self, self,
source_name=shape_data['source_name'], source_name=shape_data["source_name"],
points=shape_data['points'], points=shape_data["points"],
polygons=shape_data['polygons'], polygons=shape_data["polygons"],
edges=shape_data['edges'], edges=shape_data["edges"],
bone=bone, bone=bone,
color=shape_data['color'] color=shape_data["color"],
) )
elif shape_data['type'] == 'OPERATOR': elif shape_data["type"] == "OPERATOR":
shape = OperatorShape( shape = OperatorShape(
self, self,
source_name=shape_data['source_name'], source_name=shape_data["source_name"],
points=shape_data['points'], points=shape_data["points"],
polygons=shape_data['polygons'], polygons=shape_data["polygons"],
operator=shape_data['operator'], operator=shape_data["operator"],
color=shape_data['color'], color=shape_data["color"],
tooltip=shape_data['tooltip'], tooltip=shape_data["tooltip"],
) )
self.shapes.append(shape) self.shapes.append(shape)
@ -456,26 +490,24 @@ class Picker:
def assign_bone_event(self): def assign_bone_event(self):
for shape in self.shapes: for shape in self.shapes:
if shape.type == 'bone' and shape.hover: if shape.type == "bone" and shape.hover:
shape.assign_bone_event() shape.assign_bone_event()
bpy.ops.rigpicker.save_picker(index=self.index) 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: for shape in self.shapes:
#print(s) # print(s)
if shape.hover: if shape.hover:
shape.press_event(mode) shape.press_event(mode)
else: else:
shape.press = False shape.press = False
def release_event(self, mode='SET'): def release_event(self, mode="SET"):
# bpy.ops.pose.select_all(action='DESELECT')
# print('PICKER release event', mode)
#bpy.ops.pose.select_all(action='DESELECT') # 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: for shape in self.shapes:
if shape.hover: if shape.hover:
@ -483,9 +515,9 @@ class Picker:
shape.press = False shape.press = False
#bpy.context.area.tag_redraw() # bpy.context.area.tag_redraw()
''' """
picker.tooltip_event(event='SHOW') picker.tooltip_event(event='SHOW')
region.tag_redraw() region.tag_redraw()
@ -495,8 +527,9 @@ class Picker:
picker.tooltip_event(event='HIDE') picker.tooltip_event(event='HIDE')
bpy.app.timers.register(partial(tooltip, context.region), first_interval=1) bpy.app.timers.register(partial(tooltip, context.region), first_interval=1)
''' """
'''
"""
def tooltip_event(self, event): def tooltip_event(self, event):
@ -508,7 +541,7 @@ class Picker:
self.tooltip = self.hover_shape.bone.name self.tooltip = self.hover_shape.bone.name
#bpy.context.region.tag_redraw() #bpy.context.region.tag_redraw()
''' """
def border_select(self, border, mode): def border_select(self, border, mode):
border = [Vector(b) - Vector(self.translation) for b in border] border = [Vector(b) - Vector(self.translation) for b in border]
@ -516,7 +549,7 @@ class Picker:
for shape in self.shapes: for shape in self.shapes:
shape.press = False shape.press = False
shape.hover = False shape.hover = False
if shape.type != 'bone': if shape.type != "bone":
continue continue
shape.border_select(border, mode) shape.border_select(border, mode)
@ -531,16 +564,16 @@ class Picker:
elif shape.move_event(self.mouse): elif shape.move_event(self.mouse):
self.hover_shape = shape 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) # over = point_over_shape(self.end,points, edges)
#if bpy.app.timers.is_registered(self.tooltip_event): # if bpy.app.timers.is_registered(self.tooltip_event):
#try: # try:
# bpy.app.timers.unregister(self.tooltip_event) # bpy.app.timers.unregister(self.tooltip_event)
#except: # except:
# pass # pass
#bpy.app.timers.register(self.tooltip_event, first_interval=1) # bpy.app.timers.register(self.tooltip_event, first_interval=1)
def draw(self): def draw(self):
with gpu.matrix.push_pop(): with gpu.matrix.push_pop():
@ -564,8 +597,7 @@ class Picker:
class PickerGroup: class PickerGroup:
def __init__(self, rig, pickers): def __init__(self, rig, pickers):
# self.view_location = Vector((0, 0))
#self.view_location = Vector((0, 0))
self.region = bpy.context.region self.region = bpy.context.region
self.rig = rig self.rig = rig
self.pickers = [] self.pickers = []
@ -577,7 +609,7 @@ class PickerGroup:
self.tooltip_shape = None self.tooltip_shape = None
self.tooltip_mouse = Vector((0, 0)) self.tooltip_mouse = Vector((0, 0))
self.tooltip = '' self.tooltip = ""
self.timer = None self.timer = None
@ -592,12 +624,12 @@ class PickerGroup:
for picker in self.pickers: for picker in self.pickers:
height = picker.rect[1][1] - picker.rect[-1][1] 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() picker.draw()
y += height + 50 y += height + 50
#break #TODO for now only draw first picker # break #TODO for now only draw first picker
def move_event(self, mouse): def move_event(self, mouse):
self.mouse = mouse self.mouse = mouse
@ -625,7 +657,7 @@ class PickerGroup:
self.hover_shape = hover_shape self.hover_shape = hover_shape
if self.tooltip_shape is not self.hover_shape: if self.tooltip_shape is not self.hover_shape:
self.tooltip = '' self.tooltip = ""
if self.timer: if self.timer:
self.timer.cancel() self.timer.cancel()
@ -633,8 +665,8 @@ class PickerGroup:
self.timer = threading.Timer(0.4, self.tooltip_event) self.timer = threading.Timer(0.4, self.tooltip_event)
self.timer.start() self.timer.start()
def press_event(self, mode='SET'): def press_event(self, mode="SET"):
#self.clear_tooltip() # self.clear_tooltip()
for picker in self.pickers: for picker in self.pickers:
if picker.under_mouse(self.location): if picker.under_mouse(self.location):
@ -643,9 +675,9 @@ class PickerGroup:
for shape in picker.shapes: for shape in picker.shapes:
shape.press = False shape.press = False
def release_event(self, mode='SET'): def release_event(self, mode="SET"):
if mode == 'SET': if mode == "SET":
for bone in self.rig.data.bones: for bone in self.rig.pose.bones:
bone.select = False bone.select = False
for picker in self.pickers: for picker in self.pickers:
@ -663,26 +695,26 @@ class PickerGroup:
def border_select(self, border, mode): def border_select(self, border, mode):
border = [self.region.view2d.region_to_view(*b) for b in border] 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: for bone in self.rig.pose.bones:
bone.select = False bone.select = False
for picker in self.pickers: for picker in self.pickers:
picker.border_select(border, mode) picker.border_select(border, mode)
def clear_tooltip(self): def clear_tooltip(self):
self.tooltip = '' self.tooltip = ""
self.tooltip_shape = None self.tooltip_shape = None
self.timer.cancel() self.timer.cancel()
self.region.tag_redraw() self.region.tag_redraw()
def tooltip_event(self): 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 and self.hover_shape.type != "display":
if self.hover_shape.type == 'bone' and self.hover_shape.bone: if self.hover_shape.type == "bone" and self.hover_shape.bone:
self.tooltip = self.hover_shape.bone.name self.tooltip = self.hover_shape.bone.name
else: else:
self.tooltip = self.hover_shape.tooltip self.tooltip = self.hover_shape.tooltip
@ -690,18 +722,19 @@ class PickerGroup:
else: else:
return self.clear_tooltip() return self.clear_tooltip()
self.tooltip_mouse = self.mouse self.tooltip_mouse = self.mouse
self.timer.cancel() self.timer.cancel()
#print(self.tooltip) # print(self.tooltip)
self.region.tag_redraw() self.region.tag_redraw()
@property @property
def rect(self): 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 @property
def center(self): def center(self):
@ -710,21 +743,25 @@ class PickerGroup:
return center return center
def load_picker_data(rig): def load_picker_data(rig: bpy.types.Object):
if 'pickers' in rig.data.rig_picker: if "pickers" in rig.data.rig_picker:
picker_datas = [[s.to_dict() for s in p] for p in rig.data.rig_picker['pickers']] picker_datas = [
[s.to_dict() for s in p] for p in rig.data.rig_picker["pickers"]
]
else: else:
picker_datas = [] picker_datas = []
for picker in rig.data.rig_picker.sources: 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(): if not picker_path.exists():
print(f'Picker path not exists: {picker_path.resolve()}') print(f"Picker path not exists: {picker_path.resolve()}")
continue continue
print('Load picker from', picker_path.resolve()) print("Load picker from", picker_path.resolve())
picker_data = json.loads(picker_path.read_text(encoding='utf-8')) picker_data = json.loads(picker_path.read_text(encoding="utf-8"))
picker_datas.append(picker_data) picker_datas.append(picker_data)
PICKERS[rig] = PickerGroup(rig, picker_datas) PICKERS[rig] = PickerGroup(rig, picker_datas)
@ -736,24 +773,24 @@ def get_picker_path(rig, source, start=None):
def pack_picker(rig, start=None): def pack_picker(rig, start=None):
if not 'rig_picker' in rig.data: if not "rig_picker" in rig.data:
return return
pickers = [] pickers = []
for picker_source in rig.data['rig_picker'].get('sources', []): for picker_source in rig.data["rig_picker"].get("sources", []):
picker_path = get_picker_path(rig, picker_source['source'], start) picker_path = get_picker_path(rig, picker_source["source"], start)
if not picker_path.exists(): if not picker_path.exists():
print(f'{picker_path} not exists') print(f"{picker_path} not exists")
continue 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) pickers.append(picker_data)
rig.data['rig_picker']['pickers'] = pickers rig.data["rig_picker"]["pickers"] = pickers
def unpack_picker(rig): def unpack_picker(rig):
if 'rig_picker' not in rig.data.keys(): if "rig_picker" not in rig.data.keys():
return return
if 'pickers' in rig.data['rig_picker'].keys(): if "pickers" in rig.data["rig_picker"].keys():
del rig.data['rig_picker']['pickers'] del rig.data["rig_picker"]["pickers"]

View File

@ -6,30 +6,29 @@ from .geometry_utils import bound_box_center
from .addon_utils import get_object_color 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 loop in loops:
for i,p in enumerate(loop): for i, p in enumerate(loop):
a = Vector(verts[loop[i-1]]) a = Vector(verts[loop[i - 1]])
b = Vector(verts[p]) b = Vector(verts[p])
for j in range(0,4): for j in range(0, 4):
c = border[j-1] c = border[j - 1]
d = border[j] d = border[j]
if intersect_line_line_2d(a,b,c,d): if intersect_line_line_2d(a, b, c, d):
return True return True
for point in verts: for point in verts:
if point_inside_rect(point,border): if point_inside_rect(point, border):
return True return True
for point in border: for point in border:
if point_over_shape(point,verts,loops): if point_over_shape(point, verts, loops):
return True return True
def border_loop(vert, loop): 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: if border_edge:
for edge in border_edge: for edge in border_edge:
@ -43,6 +42,7 @@ def border_loop(vert, loop):
else: else:
return [vert] return [vert]
def contour_loops(bm, vert_index=0, loops=None, vert_indices=None): def contour_loops(bm, vert_index=0, loops=None, vert_indices=None):
loops = loops or [] loops = loops or []
vert_indices = vert_indices or [v.index for v in bm.verts] 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() bm.verts.ensure_lookup_table()
loop = border_loop(bm.verts[vert_index], [bm.verts[vert_index]]) loop = border_loop(bm.verts[vert_index], [bm.verts[vert_index]])
if len(loop) >1: if len(loop) > 1:
loops.append(loop) loops.append(loop)
for v in loop: for v in loop:
@ -75,14 +75,13 @@ def get_shape_data(ob, matrix=None, depsgraph=None):
bm = bmesh.new() bm = bmesh.new()
bm.from_mesh(mesh) bm.from_mesh(mesh)
bmesh.ops.remove_doubles(bm, verts=bm.verts, dist=0.002) 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.dissolve_limit(bm, angle_limit=0.001745, verts=bm.verts, edges=bm.edges)
bmesh.ops.connect_verts_concave(bm, faces=bm.faces) bmesh.ops.connect_verts_concave(bm, faces=bm.faces)
bmesh.ops.triangulate(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) bm.to_mesh(mesh)
mesh.update() mesh.update()
bm.clear() bm.clear()
@ -100,7 +99,7 @@ def get_shape_data(ob, matrix=None, depsgraph=None):
if depths: if depths:
depth = max(depths) depth = max(depths)
else: else:
print(f'{ob.name} has no vertices') print(f"{ob.name} has no vertices")
depth = 0 depth = 0
for face in mesh.polygons: for face in mesh.polygons:
@ -113,22 +112,22 @@ def get_shape_data(ob, matrix=None, depsgraph=None):
color = [0.5, 0.5, 0.5] color = [0.5, 0.5, 0.5]
shape = { shape = {
'source_name': ob.name, "source_name": ob.name,
'tooltip': ob.rig_picker.name, "tooltip": ob.rig_picker.name,
'depth': depth, "depth": depth,
'points': points, "points": points,
'polygons': polygons, "polygons": polygons,
'edges': edges, "edges": edges,
'color': color, "color": color,
'type': ob.rig_picker.shape_type "type": ob.rig_picker.shape_type,
} }
if shape['type'] =='OPERATOR': if shape["type"] == "OPERATOR":
shape['operator'] = ob.rig_picker.operator shape["operator"] = ob.rig_picker.operator
shape['shortcut'] = ob.rig_picker.shortcut shape["shortcut"] = ob.rig_picker.shortcut
elif shape['type'] =='BONE': elif shape["type"] == "BONE":
shape['bone'] = ob.rig_picker.name shape["bone"] = ob.rig_picker.name
eval_ob.to_mesh_clear() eval_ob.to_mesh_clear()
@ -141,39 +140,42 @@ def get_picker_data(collection):
depsgraph = bpy.context.evaluated_depsgraph_get() depsgraph = bpy.context.evaluated_depsgraph_get()
canvas = collection.rig_picker.canvas canvas = collection.rig_picker.canvas
if canvas.type == 'CURVE': if canvas.type == "CURVE":
canvas_points = canvas.data.splines[0].points canvas_points = canvas.data.splines[0].points
else: else:
canvas_points = canvas.data.vertices 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)) 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)) 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) 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.Translation(-center)
matrix = Matrix.Scale(scale, 4) @ matrix matrix = Matrix.Scale(scale, 4) @ matrix
#sorted by their z axes # sorted by their z axes
for ob in collection.all_objects: for ob in collection.all_objects:
if ob.instance_collection: if ob.instance_collection:
for shape in ob.instance_collection.all_objects: for shape in ob.instance_collection.all_objects:
picker_data.append(get_shape_data(shape, matrix=matrix@ob.matrix_world, depsgraph=depsgraph)) picker_data.append(
get_shape_data(
shape, matrix=matrix @ ob.matrix_world, depsgraph=depsgraph
)
)
elif ob.type in ('MESH', 'CURVE', 'FONT'): elif ob.type in ("MESH", "CURVE", "FONT"):
picker_data.append(get_shape_data(ob, matrix=matrix, depsgraph=depsgraph)) picker_data.append(get_shape_data(ob, matrix=matrix, depsgraph=depsgraph))
else: else:
#print(f'{ob.name} of type {ob.type} not supported') # print(f'{ob.name} of type {ob.type} not supported')
continue 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 return picker_data
#rig.data.rig_picker['shapes'] = picker_datas # rig.data.rig_picker['shapes'] = picker_datas

View File

@ -1,4 +1,3 @@
import bpy import bpy
import gpu import gpu
from gpu_extras.batch import batch_for_shader from gpu_extras.batch import batch_for_shader
@ -23,9 +22,10 @@ def draw_rect_2d(position, width, height, color):
coords = ((0, 0), (1, 0), (1, 1), (0, 1)) 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( batch = batch_for_shader(
shader, 'TRI_FAN', shader,
"TRI_FAN",
{"pos": coords}, {"pos": coords},
) )
@ -40,7 +40,7 @@ def draw_rect_2d(position, width, height, color):
def draw_callback_view(): def draw_callback_view():
space_data = bpy.context.space_data 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 return
# Use the pin to know if this is the first time this picker window in created to hide the n panel # Use the pin to know if this is the first time this picker window in created to hide the n panel
@ -49,7 +49,7 @@ def draw_callback_view():
space_data.show_region_ui = False space_data.show_region_ui = False
ob = bpy.context.object 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 return
if ob not in PICKERS: if ob not in PICKERS:
@ -61,7 +61,7 @@ def draw_callback_view():
def draw_callback_px(): def draw_callback_px():
sp = bpy.context.space_data sp = bpy.context.space_data
if not sp.tree_type == 'RigPickerTree': if not sp.tree_type == "RigPickerTree":
return return
ob = bpy.context.object ob = bpy.context.object
@ -74,37 +74,36 @@ def draw_callback_px():
text = picker_group.tooltip text = picker_group.tooltip
ui_scale = bpy.context.preferences.system.ui_scale ui_scale = bpy.context.preferences.system.ui_scale
#print('Draw text', text) # print('Draw text', text)
font_id = 0 font_id = 0
blf.size(font_id, int(13 * ui_scale)) blf.size(font_id, int(13 * ui_scale))
margins = [12, 5] margins = [12, 5]
text_size = blf.dimensions(font_id, text) 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_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_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)) 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.enable(font_id, blf.SHADOW)
# BLF drawing routine # BLF drawing routine
blf.position(font_id, round(text_pos[0]), round(text_pos[1]), 0) 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.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.shadow_offset(font_id, 2, -2)
blf.draw(font_id, text) blf.draw(font_id, text)
blf.disable(font_id, blf.SHADOW) blf.disable(font_id, blf.SHADOW)
handle_view = None handle_view = None
handle_pixel = None handle_pixel = None
@ -113,15 +112,20 @@ def register():
global handle_view global handle_view
global handle_pixel global handle_pixel
handle_view = bpy.types.SpaceNodeEditor.draw_handler_add(draw_callback_view, (), 'WINDOW', 'POST_VIEW') handle_view = bpy.types.SpaceNodeEditor.draw_handler_add(
handle_pixel = bpy.types.SpaceNodeEditor.draw_handler_add(draw_callback_px, (), 'WINDOW', 'POST_PIXEL') draw_callback_view, (), "WINDOW", "POST_VIEW"
)
handle_pixel = bpy.types.SpaceNodeEditor.draw_handler_add(
draw_callback_px, (), "WINDOW", "POST_PIXEL"
)
def unregister(): def unregister():
global handle_view global handle_view
global handle_pixel global handle_pixel
bpy.types.SpaceNodeEditor.draw_handler_remove(handle_view, 'WINDOW') 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_pixel, "WINDOW")
handle_view = None handle_view = None
handle_pixel = None handle_pixel = None

View File

@ -1,16 +1,13 @@
import bpy import bpy
from bpy.props import (IntProperty, EnumProperty, BoolProperty) from bpy.props import IntProperty, EnumProperty, BoolProperty
from bpy.types import (AddonPreferences, GizmoGroup, Operator, Gizmo) from bpy.types import AddonPreferences, GizmoGroup, Operator, Gizmo
from mathutils import Vector, Matrix, Euler from mathutils import Vector, Matrix, Euler
from .constants import PICKERS from .constants import PICKERS
from .core.picker import Picker from .core.picker import Picker
''' '''
class RP_OT_simple_operator(bpy.types.Operator): class RP_OT_simple_operator(bpy.types.Operator):
"""Tooltip""" """Tooltip"""
@ -40,10 +37,6 @@ class RP_OT_simple_operator(bpy.types.Operator):
''' '''
class RP_GT_gizmo(Gizmo): class RP_GT_gizmo(Gizmo):
def test_select(self, context, location): def test_select(self, context, location):
@ -55,27 +48,25 @@ class RP_GT_gizmo(Gizmo):
picker.move_event(location) picker.move_event(location)
#if bpy.app.timers.is_registered(tooltip): # if bpy.app.timers.is_registered(tooltip):
# bpy.app.timers.unregister(tooltip) # bpy.app.timers.unregister(tooltip)
#context.region.tag_redraw() # context.region.tag_redraw()
#picker.tooltip_event(event='START')
#bpy.app.timers.register(partial(tooltip, context.region), first_interval=1)
#print(location)
# picker.tooltip_event(event='START')
# bpy.app.timers.register(partial(tooltip, context.region), first_interval=1)
# print(location)
context.region.tag_redraw() context.region.tag_redraw()
#print(location) # print(location)
return -1 return -1
def draw(self, context): def draw(self, context):
return return
'''
"""
def invoke(self, context, event): 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}')
return {'RUNNING_MODAL'} return {'RUNNING_MODAL'}
@ -88,33 +79,29 @@ class RP_GT_gizmo(Gizmo):
def exit(self, context, cancel): def exit(self, context, cancel):
print('EXIT') print('EXIT')
''' """
class RP_GT_gizmogroup(GizmoGroup): class RP_GT_gizmogroup(GizmoGroup):
""" test gizmo button 2d """ """test gizmo button 2d"""
bl_idname = "view3d.gizmo_button_2d" bl_idname = "view3d.gizmo_button_2d"
bl_label = "Test button 2d" bl_label = "Test button 2d"
bl_space_type = 'NODE_EDITOR' bl_space_type = "NODE_EDITOR"
bl_region_type = 'WINDOW' bl_region_type = "WINDOW"
bl_options = {'PERSISTENT'} bl_options = {"PERSISTENT"}
@classmethod @classmethod
def poll(cls, context): def poll(cls, context):
return context.space_data.tree_type == 'RigPickerTree' return context.space_data.tree_type == "RigPickerTree"
def setup(self, context): def setup(self, context):
self.gizmo = self.gizmos.new("RP_GT_gizmo") self.gizmo = self.gizmos.new("RP_GT_gizmo")
classes = (RP_GT_gizmo, RP_GT_gizmogroup)
classes = (
RP_GT_gizmo,
RP_GT_gizmogroup
)
def register(): def register():
for cls in classes: for cls in classes:
bpy.utils.register_class(cls) bpy.utils.register_class(cls)

View File

@ -1,20 +1,16 @@
from . import material from . import material
from . import picker from . import picker
from . import shape from . import shape
modules = ( modules = (material, picker, shape)
material,
picker,
shape
)
def register(): def register():
for mod in modules: for mod in modules:
mod.register() mod.register()
def unregister(): def unregister():
for mod in reversed(modules): for mod in reversed(modules):
mod.unregister() mod.unregister()

View File

@ -1,4 +1,3 @@
import bpy import bpy
from bpy_extras import view3d_utils from bpy_extras import view3d_utils
@ -6,56 +5,55 @@ from bpy_extras import view3d_utils
class RP_OT_add_material(bpy.types.Operator): class RP_OT_add_material(bpy.types.Operator):
bl_label = "Add Ui Material" bl_label = "Add Ui Material"
bl_idname = "rigpicker.add_mat" bl_idname = "rigpicker.add_mat"
#bl_options = {'REGISTER', 'UNDO'} # bl_options = {'REGISTER', 'UNDO'}
def execute(self,context): def execute(self, context):
scene = context.scene scene = context.scene
mat = bpy.data.materials.new('UI') mat = bpy.data.materials.new("UI")
mat.use_nodes = True mat.use_nodes = True
for node in mat.node_tree.nodes: for node in mat.node_tree.nodes:
if node.type == 'OUTPUT_MATERIAL': if node.type == "OUTPUT_MATERIAL":
mat_output = node mat_output = node
else: else:
mat.node_tree.nodes.remove(node) mat.node_tree.nodes.remove(node)
emission = mat.node_tree.nodes.new('ShaderNodeEmission') emission = mat.node_tree.nodes.new("ShaderNodeEmission")
mat.node_tree.links.new(emission.outputs[0],mat_output.inputs[0]) mat.node_tree.links.new(emission.outputs[0], mat_output.inputs[0])
if not context.object.data.materials: if not context.object.data.materials:
context.object.data.materials.append(mat) context.object.data.materials.append(mat)
else: else:
context.object.material_slots[0].material = mat context.object.material_slots[0].material = mat
return {'FINISHED'} return {"FINISHED"}
class RP_OT_remove_material(bpy.types.Operator): class RP_OT_remove_material(bpy.types.Operator):
bl_label = "Remove Ui Material" bl_label = "Remove Ui Material"
bl_idname = "rigpicker.remove_mat" bl_idname = "rigpicker.remove_mat"
#bl_options = {'REGISTER', 'UNDO'} # bl_options = {'REGISTER', 'UNDO'}
def execute(self,context): def execute(self, context):
scene = context.scene scene = context.scene
#print(self.shape_type) # print(self.shape_type)
for mat in context.object.data.materials: for mat in context.object.data.materials:
bpy.data.materials.remove(mat) bpy.data.materials.remove(mat)
context.area.tag_redraw() context.area.tag_redraw()
return {'FINISHED'} return {"FINISHED"}
class RP_OT_eyedropper_material(bpy.types.Operator): class RP_OT_eyedropper_material(bpy.types.Operator):
"""Tooltip""" """Tooltip"""
bl_idname = "rigpicker.eyedropper_mat" bl_idname = "rigpicker.eyedropper_mat"
bl_label = "Eye Dropper mat" bl_label = "Eye Dropper mat"
#bl_options = {'REGISTER', 'UNDO'} # bl_options = {'REGISTER', 'UNDO'}
#first_mouse_x = IntProperty()
#first_value = FloatProperty()
# first_mouse_x = IntProperty()
# first_value = FloatProperty()
def modal(self, context, event): def modal(self, context, event):
context.area.tag_redraw() context.area.tag_redraw()
@ -66,7 +64,7 @@ class RP_OT_eyedropper_material(bpy.types.Operator):
region = context.region region = context.region
rv3d = context.region_data 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 self.mouse = event.mouse_region_x, event.mouse_region_y
dg = context.evaluated_depsgraph_get() 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) raycast = scene.ray_cast(dg, ray_origin, view_vector)
if raycast[0]==True: if raycast[0] == True:
ob = raycast[4] ob = raycast[4]
if ob.data.materials: if ob.data.materials:
mat = ob.data.materials[0] 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: if not shape.data.materials:
shape.data.materials.append(mat) shape.data.materials.append(mat)
else: else:
shape.material_slots[0].material = mat 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() context.window.cursor_modal_restore()
for ob in self.temp_ob: for ob in self.temp_ob:
bpy.data.objects.remove(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() context.window.cursor_modal_restore()
for ob in self.temp_ob: for ob in self.temp_ob:
bpy.data.objects.remove(ob) bpy.data.objects.remove(ob)
return {'CANCELLED'} return {"CANCELLED"}
return {'RUNNING_MODAL'}
return {"RUNNING_MODAL"}
def invoke(self, context, event): def invoke(self, context, event):
scene = context.scene scene = context.scene
#self.local_cursor = tuple(context.space_data.cursor.location) # self.local_cursor = tuple(context.space_data.cursor.location)
#self.cursor = tuple(context.scene.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 = [] self.temp_ob = []
@ -124,17 +123,17 @@ class RP_OT_eyedropper_material(bpy.types.Operator):
for c in curves: for c in curves:
mesh = bpy.data.meshes.new_from_object(c.evaluated_get(dg)) 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 copy.matrix_world = c.matrix_world
for mat in c.data.materials: for mat in c.data.materials:
copy.data.materials.append(mat) copy.data.materials.append(mat)
scene.collection.objects.link(copy) scene.collection.objects.link(copy)
self.temp_ob.append(copy) self.temp_ob.append(copy)
#args = (self,context) # args = (self,context)
#self._handle = context.space_data.draw_handler_add(draw_callback_px, args, 'WINDOW', 'POST_PIXEL') # self._handle = context.space_data.draw_handler_add(draw_callback_px, args, 'WINDOW', 'POST_PIXEL')
context.window_manager.modal_handler_add(self) context.window_manager.modal_handler_add(self)
return {'RUNNING_MODAL'} return {"RUNNING_MODAL"}
classes = ( classes = (

View File

@ -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.bl_utils import get_view_3d_override, split_path, eval_attr
from ..core.geometry_utils import bounding_rect from ..core.geometry_utils import bounding_rect
from ..core.picker import get_picker_path, pack_picker, unpack_picker, load_picker_data 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 .func_bgl import draw_callback_px
#from core.picker import * # from .func_bgl import select_bone
#from .utils import is_over_region # from core.picker import *
# from .utils import is_over_region
import gpu import gpu
from mathutils import Vector, Euler, Matrix from mathutils import Vector, Euler, Matrix
from gpu_extras.batch import batch_for_shader from gpu_extras.batch import batch_for_shader
@ -18,13 +19,12 @@ import json
import os import os
def is_picker_space(space_data=None): def is_picker_space(space_data=None):
if not space_data: if not space_data:
space_data = bpy.context.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 True
return False return False
@ -32,10 +32,13 @@ def is_picker_space(space_data=None):
class RP_OT_box_select(Operator): class RP_OT_box_select(Operator):
"""Box Select bones in the picker view""" """Box Select bones in the picker view"""
bl_idname = "node.rp_box_select" bl_idname = "node.rp_box_select"
bl_label = "Picker 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 @classmethod
def poll(cls, context): def poll(cls, context):
@ -45,7 +48,7 @@ class RP_OT_box_select(Operator):
ob = context.object ob = context.object
return ob and ob in PICKERS return ob and ob in PICKERS
''' """
def mode_from_event(self, event): def mode_from_event(self, event):
if event.alt: if event.alt:
return 'SUBSTRACT' return 'SUBSTRACT'
@ -53,16 +56,16 @@ class RP_OT_box_select(Operator):
return 'EXTEND' return 'EXTEND'
else: else:
return 'SET' return 'SET'
''' """
def draw_callback(self): def draw_callback(self):
#print('draw callback border') # print('draw callback border')
if not self.draw_border: if not self.draw_border:
return return
gpu.state.blend_set('ALPHA') gpu.state.blend_set("ALPHA")
#print('DRAW BORDER') # print('DRAW BORDER')
self.color_shader.bind() self.color_shader.bind()
self.color_shader.uniform_float("color", self.bg_color) self.color_shader.uniform_float("color", self.bg_color)
@ -78,63 +81,69 @@ class RP_OT_box_select(Operator):
self.contour_batch.draw(self.dash_shader) self.contour_batch.draw(self.dash_shader)
gpu.state.blend_set('NONE') gpu.state.blend_set("NONE")
def invoke(self, context, event): 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() bpy.ops.object.posemode_toggle()
self.timer = None self.timer = None
#self.mode = self.mode_from_event(event) # self.mode = self.mode_from_event(event)
#self.invoke_event = event.copy() # self.invoke_event = event.copy()
self.region = context.region self.region = context.region
self.draw_border = False self.draw_border = False
self.picker = PICKERS[context.object] self.picker = PICKERS[context.object]
self.start_mouse = event.mouse_region_x, event.mouse_region_y 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.border_color = [1, 1, 1, 1]
self.bg_color = [1, 1, 1, 0.05] self.bg_color = [1, 1, 1, 0.05]
#args = (self, context) # args = (self, context)
self.color_shader = gpu.shader.from_builtin('UNIFORM_COLOR') self.color_shader = gpu.shader.from_builtin("UNIFORM_COLOR")
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) context.window_manager.modal_handler_add(self)
self.picker.press_event(self.mode) self.picker.press_event(self.mode)
self.region.tag_redraw() self.region.tag_redraw()
return {'RUNNING_MODAL'} return {"RUNNING_MODAL"}
def modal(self, context, event): def modal(self, context, event):
self.mouse = event.mouse_region_x, event.mouse_region_y self.mouse = event.mouse_region_x, event.mouse_region_y
self.border = bounding_rect((self.start_mouse, self.mouse)) self.border = bounding_rect((self.start_mouse, self.mouse))
self.bg_batch = batch_for_shader(self.color_shader, 'TRI_FAN', {"pos": self.border}) self.bg_batch = batch_for_shader(
self.contour_batch = batch_for_shader(self.dash_shader, 'LINE_LOOP', {"pos": self.border}) 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.draw_border = True
self.region.tag_redraw() self.region.tag_redraw()
if event.value == 'RELEASE': if event.value == "RELEASE":
return self.release_event(context) return self.release_event(context)
return {'RUNNING_MODAL'} return {"RUNNING_MODAL"}
def release_event(self, context): def release_event(self, context):
if get_picker_collection(): if get_picker_collection():
self.picker.assign_bone_event() 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) self.picker.border_select(self.border, self.mode)
else: else:
self.picker.move_event(self.mouse) self.picker.move_event(self.mouse)
@ -145,23 +154,24 @@ class RP_OT_box_select(Operator):
return self.exit(context) return self.exit(context)
def exit(self, 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() context.region.tag_redraw()
return {'FINISHED'} return {"FINISHED"}
class RP_OT_picker_transform(Operator): class RP_OT_picker_transform(Operator):
"""Transform Bones in the picker view""" """Transform Bones in the picker view"""
bl_idname = "node.picker_transform" bl_idname = "node.picker_transform"
bl_label = "Transform Bone in Picker View" 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 @classmethod
def poll(cls, context): def poll(cls, context):
return context.selected_pose_bones and is_picker_space(context.space_data) return context.selected_pose_bones and is_picker_space(context.space_data)
''' """
def draw_callback(self): def draw_callback(self):
gpu.state.blend_set('ALPHA') gpu.state.blend_set('ALPHA')
gpu.state.line_width_set(2) gpu.state.line_width_set(2)
@ -179,18 +189,20 @@ class RP_OT_picker_transform(Operator):
gpu.state.line_width_set(1) gpu.state.line_width_set(1)
gpu.state.blend_set('NONE') gpu.state.blend_set('NONE')
''' """
def invoke(self, context, event): def invoke(self, context, event):
self.override = get_view_3d_override() 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_start = Vector((event.mouse_region_x, event.mouse_region_y))
self.mouse = Vector((0, 0)) self.mouse = Vector((0, 0))
transform_type = context.scene.tool_settings.transform_pivot_point transform_type = context.scene.tool_settings.transform_pivot_point
self.center = context.active_pose_bone.matrix.to_translation() 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] origins = [b.matrix.to_translation() for b in context.selected_pose_bones]
self.center = sum(origins, Vector()) / len(context.selected_pose_bones) self.center = sum(origins, Vector()) / len(context.selected_pose_bones)
# self.bone_data = {} # self.bone_data = {}
@ -198,38 +210,36 @@ class RP_OT_picker_transform(Operator):
# self.bone_data[bone] = bone.matrix.copy() # self.bone_data[bone] = bone.matrix.copy()
self.bone_data = {b: b.matrix.copy() for b in context.selected_pose_bones} 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 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 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)) self.view_vector = Vector((0, 0, 1))
if self.mode == 'ROTATE': if self.mode == "ROTATE":
self.view_vector.rotate(view_matrix) self.view_vector.rotate(view_matrix)
elif self.mode == "SCALE":
elif self.mode == 'SCALE':
self.view_vector = Vector((0, 0, 0)) self.view_vector = Vector((0, 0, 0))
self.transform_type = "VIEW" self.transform_type = "VIEW"
self.transform_types = ["VIEW", 'GLOBAL', 'LOCAL'] self.transform_types = ["VIEW", "GLOBAL", "LOCAL"]
if context.scene.transform_orientation_slots[0].type == 'LOCAL': if context.scene.transform_orientation_slots[0].type == "LOCAL":
self.transform_types = ["VIEW", 'LOCAL', 'GLOBAL'] self.transform_types = ["VIEW", "LOCAL", "GLOBAL"]
self.transform_orientation = context.scene.transform_orientation_slots[0].type 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) context.window_manager.modal_handler_add(self)
return {'RUNNING_MODAL'} return {"RUNNING_MODAL"}
def change_transform_type(self): 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) index = self.transform_types.index(self.transform_type)
new_index = indices[index] new_index = indices[index]
self.transform_type = self.transform_types[new_index] self.transform_type = self.transform_types[new_index]
@ -238,72 +248,72 @@ class RP_OT_picker_transform(Operator):
scn = bpy.context.scene scn = bpy.context.scene
# Insert keyframe # Insert keyframe
#bpy.ops.ed.undo_push(message="Transform") # bpy.ops.ed.undo_push(message="Transform")
if scn.tool_settings.use_keyframe_insert_auto: if scn.tool_settings.use_keyframe_insert_auto:
try: try:
bpy.ops.animtoolbox.insert_keyframe() bpy.ops.animtoolbox.insert_keyframe()
except Exception: 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) self.exit(context)
def exit(self, 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.window.cursor_modal_restore()
#context.region.tag_redraw() # context.region.tag_redraw()
def modal(self, context, event): def modal(self, context, event):
scn = context.scene scn = context.scene
self.mouse = Vector((event.mouse_region_x, event.mouse_region_y)) 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 delta = (event.mouse_region_x - self.mouse_start[0]) / 100
elif self.mode == 'SCALE': elif self.mode == "SCALE":
delta = 1 + (event.mouse_region_x - self.mouse_start[0]) / 100 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 # delta = (self.mouse - self.view_center).length / (self.mouse_start - self.view_center).length
transform_type = self.transform_type transform_type = self.transform_type
#self.batch = batch_for_shader(self.dash_shader, 'LINE_STRIP', {"pos": [self.mouse_start, self.mouse]}) # self.batch = batch_for_shader(self.dash_shader, 'LINE_STRIP', {"pos": [self.mouse_start, self.mouse]})
#context.area.tag_redraw() # context.area.tag_redraw()
#print(event.type, event.value) # print(event.type, event.value)
if self.mode == 'ROTATE' and event.type == "R" and event.value == 'PRESS': if self.mode == "ROTATE" and event.type == "R" and event.value == "PRESS":
with context.temp_override(**self.override): with context.temp_override(**self.override):
self.exit(context) self.exit(context)
bpy.ops.transform.trackball('INVOKE_DEFAULT') bpy.ops.transform.trackball("INVOKE_DEFAULT")
return {'FINISHED'} return {"FINISHED"}
if event.type == "LEFTMOUSE" and event.value == 'RELEASE': if event.type == "LEFTMOUSE" and event.value == "RELEASE":
self.release_event(context) self.release_event(context)
return {'FINISHED'} return {"FINISHED"}
if event.type in {"RIGHTMOUSE", "ESC"}: if event.type in {"RIGHTMOUSE", "ESC"}:
for bone, matrix in self.bone_data.items(): for bone, matrix in self.bone_data.items():
bone.matrix = matrix bone.matrix = matrix
self.exit(context) 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.change_transform_type()
self.view_vector = Vector((1, 0, 0)) 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.change_transform_type()
self.view_vector = Vector((0, 1, 0)) 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.change_transform_type()
self.view_vector = Vector((0, 0, 1)) self.view_vector = Vector((0, 0, 1))
#transform_type = self.transform_orientation # transform_type = self.transform_orientation
elif event.type == "MOUSEMOVE": elif event.type == "MOUSEMOVE":
if self.mode == "ROTATE":
if self.mode == 'ROTATE':
transform_matrix = Matrix.Rotation(delta, 4, self.view_vector) transform_matrix = Matrix.Rotation(delta, 4, self.view_vector)
elif self.mode == 'SCALE': elif self.mode == "SCALE":
if self.view_vector.length: if self.view_vector.length:
transform_matrix = Matrix.Scale(delta, 4, self.view_vector) transform_matrix = Matrix.Scale(delta, 4, self.view_vector)
else: else:
@ -314,14 +324,14 @@ class RP_OT_picker_transform(Operator):
if scn.tool_settings.transform_pivot_point == "INDIVIDUAL_ORIGINS": if scn.tool_settings.transform_pivot_point == "INDIVIDUAL_ORIGINS":
center = matrix.to_translation() center = matrix.to_translation()
if self.mode == 'ROTATE': if self.mode == "ROTATE":
if transform_type == 'LOCAL': if transform_type == "LOCAL":
view_vector = self.view_vector.copy() view_vector = self.view_vector.copy()
view_vector.rotate(matrix) view_vector.rotate(matrix)
transform_matrix = Matrix.Rotation(delta, 4, view_vector) transform_matrix = Matrix.Rotation(delta, 4, view_vector)
elif self.mode == 'SCALE': elif self.mode == "SCALE":
if transform_type == 'LOCAL': if transform_type == "LOCAL":
view_vector = self.view_vector.copy() view_vector = self.view_vector.copy()
view_vector.rotate(matrix) view_vector.rotate(matrix)
transform_matrix = Matrix.Scale(delta, 4, view_vector) transform_matrix = Matrix.Scale(delta, 4, view_vector)
@ -335,40 +345,40 @@ class RP_OT_picker_transform(Operator):
bone.matrix = mat bone.matrix = mat
return {'RUNNING_MODAL'} return {"RUNNING_MODAL"}
class RP_OT_toogle_property(Operator): class RP_OT_toogle_property(Operator):
"""Invert a bone custom property""" """Invert a bone custom property"""
bl_idname = "rigpicker.toogle_property" bl_idname = "rigpicker.toogle_property"
bl_label = 'Toogle Property' bl_label = "Toogle Property"
data_path : StringProperty() data_path: StringProperty()
@classmethod @classmethod
def poll(cls, context): def poll(cls, context):
return is_picker_space(context.space_data) return is_picker_space(context.space_data)
def execute(self,context): def execute(self, context):
ob = context.object ob = context.object
try: try:
value = ob.path_resolve(self.data_path) value = ob.path_resolve(self.data_path)
except Exception: except Exception:
return {'CANCELLED'} return {"CANCELLED"}
data = ob data = ob
prop_name = self.data_path prop_name = self.data_path
#if '.' in self.data_path: # if '.' in self.data_path:
# data, prop = prop_name.rsplit('.', 1) # data, prop = prop_name.rsplit('.', 1)
value = type(value)(not value) value = type(value)(not value)
exec(f'{repr(ob)}.{self.data_path} = {value}') exec(f"{repr(ob)}.{self.data_path} = {value}")
#setattr(data, prop_name, value) # setattr(data, prop_name, value)
bpy.ops.object.mode_set(mode='OBJECT') bpy.ops.object.mode_set(mode="OBJECT")
bpy.ops.object.mode_set(mode='POSE') bpy.ops.object.mode_set(mode="POSE")
if context.scene.tool_settings.use_keyframe_insert_auto: if context.scene.tool_settings.use_keyframe_insert_auto:
bone_name, _ = split_path(self.data_path) bone_name, _ = split_path(self.data_path)
@ -376,42 +386,45 @@ class RP_OT_toogle_property(Operator):
context.area.tag_redraw() context.area.tag_redraw()
return {'FINISHED'} return {"FINISHED"}
class RP_OT_reload_picker(Operator): class RP_OT_reload_picker(Operator):
"""Reload the picker shapes""" """Reload the picker shapes"""
bl_idname = "rigpicker.reload_picker" bl_idname: str = "rigpicker.reload_picker"
bl_label = 'Reload Picker' bl_label: str = "Reload Picker"
#@classmethod # @classmethod
#def poll(cls, context): # def poll(cls, context):
# if not is_picker_space(context): # if not is_picker_space(context):
# return # return
def execute(self, context): def execute(self, context: bpy.types.Context):
if context.object.type == 'ARMATURE': if not context.object:
return
if context.object.type == "ARMATURE":
rig = context.object rig = context.object
else: else:
collection = get_picker_collection(context.object) collection = get_picker_collection(context.object)
rig = collection.rig_picker.rig rig = collection.rig_picker.rig
#print('Reload', rig) # print('Reload', rig)
load_picker_data(rig) load_picker_data(rig)
for area in context.screen.areas: if context.screen:
if is_picker_space(area.spaces.active): for area in context.screen.areas:
area.tag_redraw() if is_picker_space(area.spaces.active):
area.tag_redraw()
return {"FINISHED"} return {"FINISHED"}
class RP_OT_toogle_bone_layer(Operator): class RP_OT_toogle_bone_layer(Operator):
"""Toogle bone layer visibility when double clicking on a bone""" """Toogle bone layer visibility when double clicking on a bone"""
bl_idname = "rigpicker.toogle_bone_layer"
bl_label = 'Toogle Bone Layer' bl_idname: str = "rigpicker.toogle_bone_layer"
bl_label: str = "Toogle Bone Layer"
@classmethod @classmethod
def poll(cls, context): def poll(cls, context):
@ -421,7 +434,7 @@ class RP_OT_toogle_bone_layer(Operator):
ob = context.object ob = context.object
picker = PICKERS.get(ob) 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 return True
def execute(self, context): def execute(self, context):
@ -442,62 +455,64 @@ class RP_OT_toogle_bone_layer(Operator):
class RP_OT_context_menu_picker(Operator): class RP_OT_context_menu_picker(Operator):
"""Display Menu with the custom properties of the hovered bone if any or the active bone""" """Display Menu with the custom properties of the hovered bone if any or the active bone"""
bl_idname = "node.context_menu_picker" bl_idname = "node.context_menu_picker"
bl_label = 'Context Menu Picker' bl_label = "Context Menu Picker"
@classmethod @classmethod
def poll(cls, context): def poll(cls, context):
return is_picker_space(context.space_data) return is_picker_space(context.space_data)
def execute(self, context): 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"} return {"FINISHED"}
class RP_OT_pack_picker(Operator): class RP_OT_pack_picker(Operator):
"""Pack Unpack the picker on the rig""" """Pack Unpack the picker on the rig"""
bl_idname = "rigpicker.pack_picker" bl_idname = "rigpicker.pack_picker"
bl_label = 'Pack Picker' bl_label = "Pack Picker"
@classmethod @classmethod
def poll(cls, context): def poll(cls, context):
ob = context.object 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): def execute(self, context):
print('Pack Pickers...') print("Pack Pickers...")
rig = context.object rig = context.object
if 'pickers' in rig.data.rig_picker.keys(): if "pickers" in rig.data.rig_picker.keys():
unpack_picker(rig) unpack_picker(rig)
self.report({"INFO"}, f'The picker is unpacked') self.report({"INFO"}, f"The picker is unpacked")
return {"FINISHED"} return {"FINISHED"}
pack_picker(rig) pack_picker(rig)
if not rig.data.rig_picker['pickers']: if not rig.data.rig_picker["pickers"]:
self.report({"ERROR"}, f'No picker have been packed') self.report({"ERROR"}, f"No picker have been packed")
return {"CANCELLED"} return {"CANCELLED"}
elif len(rig.data.rig_picker['pickers']) < len(rig.data.rig_picker.sources): elif len(rig.data.rig_picker["pickers"]) < len(rig.data.rig_picker.sources):
self.report({"WARNING"}, f'No all pickers have been packed') self.report({"WARNING"}, f"No all pickers have been packed")
return {"FINISHED"} return {"FINISHED"}
self.report({"INFO"}, f'The picker is packed') self.report({"INFO"}, f"The picker is packed")
return {"FINISHED"} return {"FINISHED"}
class RP_OT_call_operator(Operator): class RP_OT_call_operator(Operator):
bl_idname = "rigpicker.call_operator" bl_idname = "rigpicker.call_operator"
bl_label = 'Call operator' bl_label = "Call operator"
operator : StringProperty() operator: StringProperty()
arguments : StringProperty() arguments: StringProperty()
invoke: BoolProperty() invoke: BoolProperty()
view_3d : BoolProperty() view_3d: BoolProperty()
@classmethod @classmethod
def poll(cls, context): def poll(cls, context):
@ -509,10 +524,10 @@ class RP_OT_call_operator(Operator):
op = eval_attr(bpy.ops, properties.operator).get_rna_type() op = eval_attr(bpy.ops, properties.operator).get_rna_type()
return op.description return op.description
except AttributeError: except AttributeError:
return 'Call operator' return "Call operator"
def execute(self, context): def execute(self, context):
#context.area.tag_redraw() # context.area.tag_redraw()
override = {} override = {}
if self.view_3d: if self.view_3d:
@ -524,13 +539,13 @@ class RP_OT_call_operator(Operator):
try: try:
ops = eval_attr(bpy.ops, self.operator) ops = eval_attr(bpy.ops, self.operator)
if self.invoke: if self.invoke:
ops('INVOKE_DEFAULT', **arguments) ops("INVOKE_DEFAULT", **arguments)
else: else:
ops(**arguments) ops(**arguments)
except Exception as e: except Exception as e:
print(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"} return {"CANCELLED"}
context.area.tag_redraw() context.area.tag_redraw()
@ -545,61 +560,66 @@ class RP_MT_context_menu(Menu):
def draw(self, context): def draw(self, context):
layout = self.layout layout = self.layout
col = layout.column() col = layout.column()
col.operator_context = 'INVOKE_DEFAULT' col.operator_context = "INVOKE_DEFAULT"
#col.use_property_split = True # col.use_property_split = True
ob = context.object ob = context.object
picker = PICKERS.get(ob) 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 bone = picker.hover_shape.bone
else: else:
bone = context.active_pose_bone bone = context.active_pose_bone
# Draw Space Switch Operator # Draw Space Switch Operator
if getattr(ob.data, 'space_switch'): if getattr(ob.data, "space_switch"):
space_switch = ob.data.space_switch space_switch = ob.data.space_switch
data_paths = [f'pose.bones["{bone.name}"]["{k}"]' for k in bone.keys()] 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: if space_bone:
index = list(space_switch.bones).index(space_bone) index = list(space_switch.bones).index(space_bone)
value = ob.path_resolve(space_bone.data_path) 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 = col.operator(
op.index=index "spaceswitch.change_space", text=f"({space})", icon="PINNED"
)
op.index = index
col.separator() col.separator()
if bone: if bone:
for key in bone.keys(): 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("rigpicker.show_bone_layer", text="Show Bone Layer", ).type = 'ACTIVE'
#layout.operator("rigidbody.objects_add", text="B Add Passive").type = 'PASSIVE' # layout.operator("rigidbody.objects_add", text="B Add Passive").type = 'PASSIVE'
class RP_OT_add_picker_collection(Operator): class RP_OT_add_picker_collection(Operator):
bl_idname = "rigpicker.add_picker_collection" bl_idname = "rigpicker.add_picker_collection"
bl_label = "Add a Picker Collection" bl_label = "Add a Picker Collection"
bl_description = "Add a Picker Collection" bl_description = "Add a Picker Collection"
bl_options = {'UNDO', 'REGISTER'} bl_options = {"UNDO", "REGISTER"}
@classmethod @classmethod
def poll(cls, context): def poll(cls, context):
ob = context.object ob = context.object
return (ob and ob.type == 'ARMATURE') return ob and ob.type == "ARMATURE"
def execute(self, context): def execute(self, context):
scn = context.scene scn = context.scene
ob = context.object ob = context.object
name = ob.name name = ob.name
if name.endswith('_rig'): if name.endswith("_rig"):
name = name[:-4] 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_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_faces = [(0, 1, 2, 3)]
@ -608,9 +628,9 @@ class RP_OT_add_picker_collection(Operator):
canvas_mesh.update(calc_edges=True) canvas_mesh.update(calc_edges=True)
canvas_ob = bpy.data.objects.new(canvas_name, canvas_mesh) 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.enabled = True
col.rig_picker.rig = ob col.rig_picker.rig = ob
col.rig_picker.canvas = canvas_ob col.rig_picker.canvas = canvas_ob
@ -621,44 +641,44 @@ class RP_OT_add_picker_collection(Operator):
self.report({"INFO"}, f"New Picker Collection {col.name} Created") self.report({"INFO"}, f"New Picker Collection {col.name} Created")
return {'FINISHED'} return {"FINISHED"}
class RP_OT_add_picker_source(Operator): class RP_OT_add_picker_source(Operator):
bl_idname = "rigpicker.add_picker_source" bl_idname = "rigpicker.add_picker_source"
bl_label = "Add a Picker source" bl_label = "Add a Picker source"
bl_description = "Add a Picker source" bl_description = "Add a Picker source"
bl_options = {'UNDO', 'REGISTER'} bl_options = {"UNDO", "REGISTER"}
def execute(self, context): def execute(self, context):
arm = context.object.data arm = context.object.data
arm.rig_picker.sources.add() arm.rig_picker.sources.add()
return {'FINISHED'} return {"FINISHED"}
class RP_OT_remove_picker_source(Operator): class RP_OT_remove_picker_source(Operator):
bl_idname = "rigpicker.remove_picker_source" bl_idname = "rigpicker.remove_picker_source"
bl_label = "Delete a Picker source" bl_label = "Delete a Picker source"
bl_description = "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): def execute(self, context):
arm = context.object.data arm = context.object.data
arm.rig_picker.sources.remove(self.index) arm.rig_picker.sources.remove(self.index)
return {'FINISHED'} return {"FINISHED"}
class RP_OT_fit_picker(Operator): class RP_OT_fit_picker(Operator):
bl_idname = "rigpicker.fit_picker" bl_idname = "rigpicker.fit_picker"
bl_label = "Fit Picker" bl_label = "Fit Picker"
bl_description = "Fit Picker in 2d view" bl_description = "Fit Picker in 2d view"
bl_options = {'UNDO', 'REGISTER'} bl_options = {"UNDO", "REGISTER"}
index : IntProperty() index: IntProperty()
@classmethod @classmethod
def poll(cls, context): def poll(cls, context):
@ -675,17 +695,22 @@ class RP_OT_fit_picker(Operator):
view2d = context.region.view2d view2d = context.region.view2d
if self.index == -1: if self.index == -1:
#print('Picker Group') # print('Picker Group')
picker = picker_group picker = picker_group
view_rect = [view2d.view_to_region(*co, clip=False) for co in picker.rect] view_rect = [view2d.view_to_region(*co, clip=False) for co in picker.rect]
bpy.ops.view2d.zoom_border( bpy.ops.view2d.zoom_border(
xmin=round(view_rect[3][0]), xmax=round(view_rect[1][0]), xmin=round(view_rect[3][0]),
ymin=round(view_rect[3][1]), ymax=round(view_rect[1][1]), xmax=round(view_rect[1][0]),
wait_for_input=False, zoom_out=False 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_center = view2d.region_to_view(*region_center)
view_offset = Vector(view_center) + (picker.center - Vector(view_center)) view_offset = Vector(view_center) + (picker.center - Vector(view_center))
@ -695,21 +720,27 @@ class RP_OT_fit_picker(Operator):
bpy.ops.view2d.pan(deltax=round(delta[0]), deltay=round(delta[1])) bpy.ops.view2d.pan(deltax=round(delta[0]), deltay=round(delta[1]))
return {'FINISHED'} return {"FINISHED"}
keymaps = [] keymaps = []
def register_keymaps(): def register_keymaps():
wm = bpy.context.window_manager wm = bpy.context.window_manager
km = wm.keyconfigs.addon.keymaps.new(name="Node Editor", space_type="NODE_EDITOR") km = wm.keyconfigs.addon.keymaps.new(name="Node Editor", space_type="NODE_EDITOR")
for i in range(10): 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 kmi.properties.index = i - 1
keymaps.append((km, kmi)) 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.operator = "view3d.view_selected"
kmi.properties.view_3d = True kmi.properties.view_3d = True
keymaps.append((km, kmi)) keymaps.append((km, kmi))
@ -719,12 +750,16 @@ def register_keymaps():
kmi.properties.arguments = '{"action": "SELECT"}' kmi.properties.arguments = '{"action": "SELECT"}'
keymaps.append((km, kmi)) 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.operator = "pose.select_all"
kmi.properties.arguments = '{"action": "DESELECT"}' kmi.properties.arguments = '{"action": "DESELECT"}'
keymaps.append((km, kmi)) 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)) keymaps.append((km, kmi))
kmi = km.keymap_items.new("rigpicker.call_operator", type="X", value="PRESS") kmi = km.keymap_items.new("rigpicker.call_operator", type="X", value="PRESS")
@ -735,55 +770,69 @@ def register_keymaps():
kmi.properties.operator = "animtoolbox.insert_keyframe" kmi.properties.operator = "animtoolbox.insert_keyframe"
keymaps.append((km, kmi)) 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)) keymaps.append((km, kmi))
kmi = km.keymap_items.new("rigpicker.call_operator", type="G", value="PRESS") 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.view_3d = True
kmi.properties.invoke = True kmi.properties.invoke = True
keymaps.append((km, kmi)) keymaps.append((km, kmi))
kmi = km.keymap_items.new("rigpicker.call_operator", type="G", value="PRESS", alt=True) kmi = km.keymap_items.new(
kmi.properties.operator = 'pose.loc_clear' "rigpicker.call_operator", type="G", value="PRESS", alt=True
)
kmi.properties.operator = "pose.loc_clear"
keymaps.append((km, kmi)) keymaps.append((km, kmi))
kmi = km.keymap_items.new("node.picker_transform", type="R", value="PRESS") kmi = km.keymap_items.new("node.picker_transform", type="R", value="PRESS")
kmi.properties.mode = 'ROTATE' kmi.properties.mode = "ROTATE"
keymaps.append((km, kmi)) keymaps.append((km, kmi))
kmi = km.keymap_items.new("rigpicker.call_operator", type="R", value="PRESS", alt=True) kmi = km.keymap_items.new(
kmi.properties.operator = 'pose.rot_clear' "rigpicker.call_operator", type="R", value="PRESS", alt=True
)
kmi.properties.operator = "pose.rot_clear"
keymaps.append((km, kmi)) keymaps.append((km, kmi))
kmi = km.keymap_items.new("node.picker_transform", type="S", value="PRESS") kmi = km.keymap_items.new("node.picker_transform", type="S", value="PRESS")
kmi.properties.mode = 'SCALE' kmi.properties.mode = "SCALE"
keymaps.append((km, kmi)) keymaps.append((km, kmi))
kmi = km.keymap_items.new("rigpicker.call_operator", type="S", value="PRESS", alt=True) kmi = km.keymap_items.new(
kmi.properties.operator = 'pose.scale_clear' "rigpicker.call_operator", type="S", value="PRESS", alt=True
)
kmi.properties.operator = "pose.scale_clear"
keymaps.append((km, kmi)) 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)) keymaps.append((km, kmi))
kmi = km.keymap_items.new("node.rp_box_select", type="LEFTMOUSE", value="PRESS") 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)) keymaps.append((km, kmi))
kmi = km.keymap_items.new("node.rp_box_select", type="LEFTMOUSE", value="PRESS", shift=True) kmi = km.keymap_items.new(
kmi.properties.mode = 'EXTEND' "node.rp_box_select", type="LEFTMOUSE", value="PRESS", shift=True
)
kmi.properties.mode = "EXTEND"
keymaps.append((km, kmi)) 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", ctrl=True)
kmi = km.keymap_items.new("node.rp_box_select", type="LEFTMOUSE", value="PRESS", alt=True) kmi = km.keymap_items.new(
kmi.properties.mode = 'SUBSTRACT' "node.rp_box_select", type="LEFTMOUSE", value="PRESS", alt=True
)
kmi.properties.mode = "SUBSTRACT"
keymaps.append((km, kmi)) keymaps.append((km, kmi))
#km = wm.keyconfigs.addon.keymaps.new(name="View2D") # km = wm.keyconfigs.addon.keymaps.new(name="View2D")
#kmi = km.keymap_items.new("rigpicker.call_operator", type="MIDDLEMOUSE", value="PRESS") # kmi = km.keymap_items.new("rigpicker.call_operator", type="MIDDLEMOUSE", value="PRESS")
#kmi.properties.operator = "bpy.ops.view2d.pan('INVOKE_DEFAULT')" # kmi.properties.operator = "bpy.ops.view2d.pan('INVOKE_DEFAULT')"
#keymaps.append((km, kmi)) # keymaps.append((km, kmi))
def unregister_keymaps(): def unregister_keymaps():
@ -791,6 +840,7 @@ def unregister_keymaps():
km.keymap_items.remove(kmi) km.keymap_items.remove(kmi)
keymaps.clear() keymaps.clear()
classes = ( classes = (
RP_OT_box_select, RP_OT_box_select,
RP_OT_toogle_property, RP_OT_toogle_property,
@ -804,8 +854,8 @@ classes = (
RP_OT_add_picker_source, RP_OT_add_picker_source,
RP_OT_remove_picker_source, RP_OT_remove_picker_source,
RP_OT_fit_picker, RP_OT_fit_picker,
RP_OT_add_picker_collection RP_OT_add_picker_collection,
#RP_OT_ui_draw # RP_OT_ui_draw
) )

View File

@ -1,4 +1,3 @@
import json import json
from os.path import abspath from os.path import abspath
from pathlib import Path from pathlib import Path
@ -13,13 +12,13 @@ from ..core.bl_utils import link_mat_to_object, flip_name
class RP_OT_create_shape(Operator): class RP_OT_create_shape(Operator):
bl_label = 'Create UI shape' bl_label = "Create UI shape"
bl_idname = 'rigpicker.create_shape' bl_idname = "rigpicker.create_shape"
#bl_options = {'REGISTER', 'UNDO'} # bl_options = {'REGISTER', 'UNDO'}
@classmethod @classmethod
def poll(cls, context): 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): def invoke(self, context, event):
return context.window_manager.invoke_props_dialog(self) return context.window_manager.invoke_props_dialog(self)
@ -27,9 +26,9 @@ class RP_OT_create_shape(Operator):
def draw(self, context): def draw(self, context):
for col in bpy.data.collections: for col in bpy.data.collections:
if col.rig_picker.enabled: 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 scn = context.scene
vl = context.view_layer vl = context.view_layer
@ -42,13 +41,15 @@ class RP_OT_create_shape(Operator):
ob.rig_picker.name = name ob.rig_picker.name = name
verts=[(0,0,0)] verts = [(0, 0, 0)]
edges = [] edges = []
faces =[] faces = []
mesh.from_pydata(verts, edges, 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]: for col in picker_selected_cols or [scn.collection]:
col.objects.link(ob) col.objects.link(ob)
@ -64,36 +65,36 @@ class RP_OT_create_shape(Operator):
offset += 0.05 offset += 0.05
return {'FINISHED'} return {"FINISHED"}
class RP_OT_name_from_bone(Operator): class RP_OT_name_from_bone(Operator):
bl_label = 'Name Shape from selected bones' bl_label = "Name Shape from selected bones"
bl_idname = 'rigpicker.name_from_bone' bl_idname = "rigpicker.name_from_bone"
bl_description = 'Rename all shapes from related bones name' bl_description = "Rename all shapes from related bones name"
#bl_options = {'REGISTER', 'UNDO'} # bl_options = {'REGISTER', 'UNDO'}
def execute(self,context): def execute(self, context):
col = get_picker_collection(context.object) col = get_picker_collection(context.object)
for ob in col.all_objects: 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 ob.name = ob.rig_picker.name
return {'FINISHED'} return {"FINISHED"}
class RP_OT_mirror_shape(Operator): class RP_OT_mirror_shape(Operator):
bl_label = 'Mirror UI shape' bl_label = "Mirror UI shape"
bl_idname = 'rigpicker.mirror_shape' bl_idname = "rigpicker.mirror_shape"
#bl_options = g # bl_options = g
@classmethod @classmethod
def poll(cls, context): 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) collection = get_picker_collection(context.object)
objects = context.selected_objects objects = context.selected_objects
@ -103,8 +104,12 @@ class RP_OT_mirror_shape(Operator):
continue continue
for mod in ob.modifiers: for mod in ob.modifiers:
if (mod.type == 'NODES' and mod.node_group and mod.node_group.name == 'Symmetrize' and if (
mod.get('Socket_2') in objects): 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) bpy.data.objects.remove(ob)
@ -116,14 +121,14 @@ class RP_OT_mirror_shape(Operator):
for ob in objects: for ob in objects:
flipped_name = flip_name(ob.name) flipped_name = flip_name(ob.name)
if flipped_name == ob.name: if flipped_name == ob.name:
suffix = '.L' suffix = ".L"
flipped_suffix = '.R' flipped_suffix = ".R"
# Determine side of the object # Determine side of the object
if ob.matrix_world.to_translation()[0] < x_axis: if ob.matrix_world.to_translation()[0] < x_axis:
suffix, flipped_suffix = flipped_suffix, suffix suffix, flipped_suffix = flipped_suffix, suffix
flipped_name = f'{ob.name}{flipped_suffix}' flipped_name = f"{ob.name}{flipped_suffix}"
ob.name = f'{ob.name}{suffix}' ob.name = f"{ob.name}{suffix}"
flipped_object = ob.copy() flipped_object = ob.copy()
flipped_object.name = flipped_name flipped_object.name = flipped_name
@ -133,20 +138,20 @@ class RP_OT_mirror_shape(Operator):
flipped_object.modifiers.remove(mod) flipped_object.modifiers.remove(mod)
# Add symmetrize modifier TODO add it into a resource # Add symmetrize modifier TODO add it into a resource
mod = flipped_object.modifiers.new(name='Symmetrize', type='NODES') mod = flipped_object.modifiers.new(name="Symmetrize", type="NODES")
mod.node_group = bpy.data.node_groups['Symmetrize'] mod.node_group = bpy.data.node_groups["Symmetrize"]
mod['Socket_2'] = ob mod["Socket_2"] = ob
mod['Socket_3'] = collection.rig_picker.symmetry mod["Socket_3"] = collection.rig_picker.symmetry
for col in ob.users_collection: for col in ob.users_collection:
col.objects.link(flipped_object) 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) 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 = {} 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: if type(value) == list:
mirrored_value = [] mirrored_value = []
for item in value: for item in value:
@ -158,32 +163,37 @@ class RP_OT_mirror_shape(Operator):
flipped_object.rig_picker.arguments = str(args) flipped_object.rig_picker.arguments = str(args)
return {'FINISHED'} return {"FINISHED"}
class RP_OT_select_shape_type(Operator): class RP_OT_select_shape_type(Operator):
bl_label = 'Select Shape by Type' bl_label = "Select Shape by Type"
bl_idname = 'rigpicker.select_shape_type' bl_idname = "rigpicker.select_shape_type"
#bl_options = {'REGISTER', 'UNDO'} # 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 layout = self.layout
col = layout.column() 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 scene = context.scene
#print(self.shape_type) # print(self.shape_type)
canvas = context.scene.rig_picker.canvas canvas = context.scene.rig_picker.canvas
if canvas: if canvas:
for ob in [o for o in bpy.data.objects if o.layers==canvas.layers]: 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: if (
ob.type in ["MESH", "CURVE", "FONT"]
and ob.rig_picker.shape_type == self.shape_type
):
ob.select = True ob.select = True
return {'FINISHED'} return {"FINISHED"}
def invoke(self, context, event): def invoke(self, context, event):
wm = context.window_manager wm = context.window_manager
@ -191,10 +201,10 @@ class RP_OT_select_shape_type(Operator):
class RP_OT_save_picker(Operator): class RP_OT_save_picker(Operator):
bl_label = 'Store UI Data' bl_label = "Store UI Data"
bl_idname = 'rigpicker.save_picker' bl_idname = "rigpicker.save_picker"
index : IntProperty(default=-1) index: IntProperty(default=-1)
def execute(self, context): def execute(self, context):
scn = context.scene scn = context.scene
@ -205,39 +215,51 @@ class RP_OT_save_picker(Operator):
else: else:
source = context.active_object.data.rig_picker.sources[self.index].source source = context.active_object.data.rig_picker.sources[self.index].source
source = Path(bpy.path.abspath(source, library=ob.data.library)).resolve() 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 collection = next(
and Path(bpy.path.abspath(c.rig_picker.destination)).resolve() == source), None) (
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: if not collection:
self.report({"ERROR"}, 'No Picker found') self.report({"ERROR"}, "No Picker found")
return {'CANCELLED'} return {"CANCELLED"}
canvas = collection.rig_picker.canvas canvas = collection.rig_picker.canvas
#rig = collection.rig_picker.rig # rig = collection.rig_picker.rig
shapes = [o for o in collection.all_objects if o != canvas and o.type in ('MESH', 'CURVE', 'FONT')] shapes = [
o
for o in collection.all_objects
if o != canvas and o.type in ("MESH", "CURVE", "FONT")
]
if not canvas: if not canvas:
self.report({'ERROR'}, 'Choose a Canvas') self.report({"ERROR"}, "Choose a Canvas")
return {'CANCELLED'} return {"CANCELLED"}
picker_data = get_picker_data(collection) picker_data = get_picker_data(collection)
picker_path = Path(bpy.path.abspath(collection.rig_picker.destination)) 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)) picker_path.write_text(json.dumps(picker_data))
bpy.ops.rigpicker.reload_picker() bpy.ops.rigpicker.reload_picker()
return {'FINISHED'} return {"FINISHED"}
classes = ( classes = (
RP_OT_create_shape, RP_OT_create_shape,
RP_OT_name_from_bone, RP_OT_name_from_bone,
RP_OT_mirror_shape, RP_OT_mirror_shape,
RP_OT_select_shape_type, RP_OT_select_shape_type,
RP_OT_save_picker RP_OT_save_picker,
) )
register, unregister = bpy.utils.register_classes_factory(classes) register, unregister = bpy.utils.register_classes_factory(classes)

View File

@ -1,10 +1,16 @@
import bpy import bpy
from bpy.props import (EnumProperty, StringProperty, PointerProperty, BoolProperty, from bpy.props import (
CollectionProperty, IntProperty) EnumProperty,
StringProperty,
PointerProperty,
BoolProperty,
CollectionProperty,
IntProperty,
)
import inspect import inspect
''' """
def get_operator_items(self,context): def get_operator_items(self,context):
items = [] items = []
from . import func_picker as mod from . import func_picker as mod
@ -14,55 +20,58 @@ def get_operator_items(self,context):
items.append((name,name,"")) items.append((name,name,""))
return items return items
''' """
def bones_item(self,context):
def bones_item(self, context):
items = [] items = []
if context.scene.rig_picker.rig: if context.scene.rig_picker.rig:
for bone in context.scene.rig_picker.rig.pose.bones: for bone in context.scene.rig_picker.rig.pose.bones:
items.append((bone.name,bone.name,'')) items.append((bone.name, bone.name, ""))
return items return items
class RP_PG_picker_source(bpy.types.PropertyGroup): 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): class RP_PG_armature_ui_settings(bpy.types.PropertyGroup):
name: StringProperty() name: StringProperty()
source: StringProperty(subtype='FILE_PATH') source: StringProperty(subtype="FILE_PATH")
sources: CollectionProperty(type=RP_PG_picker_source) sources: CollectionProperty(type=RP_PG_picker_source)
class RP_PG_object_ui_settings(bpy.types.PropertyGroup): class RP_PG_object_ui_settings(bpy.types.PropertyGroup):
shape_type: EnumProperty(items=[(i.upper(), i, "") for i in ('Bone', 'Display', 'Operator')]) shape_type: EnumProperty(
#idname: StringProperty() items=[(i.upper(), i, "") for i in ("Bone", "Display", "Operator")]
#arguments: StringProperty() )
# idname: StringProperty()
# arguments: StringProperty()
operator: StringProperty() operator: StringProperty()
shortcut: StringProperty() shortcut: StringProperty()
name: StringProperty() name: StringProperty()
class RP_PG_collection_ui_settings(bpy.types.PropertyGroup): class RP_PG_collection_ui_settings(bpy.types.PropertyGroup):
enabled : BoolProperty(default=False) enabled: BoolProperty(default=False)
rig: PointerProperty(type=bpy.types.Object) rig: PointerProperty(type=bpy.types.Object)
canvas: PointerProperty(type=bpy.types.Object) canvas: PointerProperty(type=bpy.types.Object)
symmetry: PointerProperty(type=bpy.types.Object) symmetry: PointerProperty(type=bpy.types.Object)
destination: StringProperty(subtype='FILE_PATH') destination: StringProperty(subtype="FILE_PATH")
use_pick_bone : BoolProperty(default=False) use_pick_bone: BoolProperty(default=False)
link_shape : BoolProperty(default=False) link_shape: BoolProperty(default=False)
class RP_PG_scene_ui_settings(bpy.types.PropertyGroup): 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): class RP_OT_operator_selector(bpy.types.Operator):
bl_label = "Select function" bl_label = "Select function"
bl_idname = "rigpicker.operator_selector" bl_idname = "rigpicker.operator_selector"
bl_property = "idname" bl_property = "idname"
#bl_options = {'REGISTER', 'UNDO'} # bl_options = {'REGISTER', 'UNDO'}
idname: EnumProperty(items=[]) idname: EnumProperty(items=[])
@ -70,12 +79,12 @@ class RP_OT_operator_selector(bpy.types.Operator):
ob = context.object ob = context.object
ob.rig_picker.idname = self.idname ob.rig_picker.idname = self.idname
context.area.tag_redraw() context.area.tag_redraw()
return {'FINISHED'} return {"FINISHED"}
def invoke(self, context, event): def invoke(self, context, event):
wm = context.window_manager wm = context.window_manager
wm.invoke_search_popup(self) wm.invoke_search_popup(self)
return {'FINISHED'} return {"FINISHED"}
classes = ( classes = (
@ -87,15 +96,23 @@ classes = (
RP_OT_operator_selector, RP_OT_operator_selector,
) )
def register(): def register():
for cls in classes: for cls in classes:
bpy.utils.register_class(cls) bpy.utils.register_class(cls)
bpy.types.Armature.rig_picker = bpy.props.PointerProperty(type=RP_PG_armature_ui_settings) bpy.types.Armature.rig_picker = bpy.props.PointerProperty(
bpy.types.Object.rig_picker = bpy.props.PointerProperty(type=RP_PG_object_ui_settings) type=RP_PG_armature_ui_settings
bpy.types.Collection.rig_picker = bpy.props.PointerProperty(type=RP_PG_collection_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) bpy.types.Scene.rig_picker = bpy.props.PointerProperty(type=RP_PG_scene_ui_settings)
def unregister(): def unregister():
del bpy.types.Scene.rig_picker del bpy.types.Scene.rig_picker
del bpy.types.Collection.rig_picker del bpy.types.Collection.rig_picker
@ -104,4 +121,3 @@ def unregister():
for cls in reversed(classes): for cls in reversed(classes):
bpy.utils.unregister_class(cls) bpy.utils.unregister_class(cls)

13
pyproject.toml Normal file
View File

@ -0,0 +1,13 @@
[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",
"fake-bpy-module>=20251003",
]

View File

@ -2,11 +2,11 @@
flat in vec2 startPos; flat in vec2 startPos;
in vec2 vertPos; in vec2 vertPos;
out vec4 fragColor; // out vec4 fragColor;
uniform vec4 color; // uniform vec4 color;
uniform float dashSize; // uniform float dashSize;
uniform float gapSize; // uniform float gapSize;
void main() void main()
{ {

View File

@ -1,10 +1,10 @@
layout (location = 0) in vec2 pos; // layout (location = 0) in vec2 pos;
flat out vec2 startPos; flat out vec2 startPos;
out vec2 vertPos; out vec2 vertPos;
uniform mat4 viewMatrix; // uniform mat4 viewMatrix;
void main() void main()
{ {

150
ui.py
View File

@ -1,7 +1,8 @@
import bpy import bpy
from bpy.types import UIList 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.addon_utils import get_operator_from_id, get_picker_collection
from .core.bl_utils import get_mat from .core.bl_utils import get_mat
@ -20,17 +21,19 @@ import re
class RP_PT_picker_maker_panel(bpy.types.Panel): class RP_PT_picker_maker_panel(bpy.types.Panel):
bl_label = 'Rig Picker' bl_label = "Rig Picker"
bl_category = 'Rigging' bl_category = "Rigging"
bl_space_type = 'VIEW_3D' bl_space_type = "VIEW_3D"
bl_region_type = 'UI' bl_region_type = "UI"
@classmethod @classmethod
def poll(cls, context): def poll(cls, context):
return context.object return context.object
def draw_header_preset(self, context): 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): def draw(self, context):
ob = context.object ob = context.object
@ -41,11 +44,15 @@ class RP_PT_picker_maker_panel(bpy.types.Panel):
col = layout.column(align=False) col = layout.column(align=False)
if collection: if collection:
col.prop_search(collection.rig_picker, 'rig', scn, 'objects', text='Rig ') 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(
col.prop_search(collection.rig_picker, 'symmetry', scn, 'objects', text='Symmetry ') 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 = col.box()
box.enabled = not ob.data.library box.enabled = not ob.data.library
col = box.column(align=False) col = box.column(align=False)
@ -54,27 +61,41 @@ class RP_PT_picker_maker_panel(bpy.types.Panel):
row.separator(factor=0.5) row.separator(factor=0.5)
row.label(text="Sources") row.label(text="Sources")
is_packed = ('pickers' in ob.data.rig_picker.keys()) 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(
row.operator("rigpicker.add_picker_source", icon ='ADD', text="", emboss=False) "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): for i, item in enumerate(ob.data.rig_picker.sources):
row = col.row(align=True) row = col.row(align=True)
row.enabled = not is_packed row.enabled = not is_packed
row.prop(item, 'source', text='') row.prop(item, "source", text="")
row.operator("rigpicker.remove_picker_source", icon ='PANEL_CLOSE', text="", emboss=False).index=i row.operator(
"rigpicker.remove_picker_source",
icon="PANEL_CLOSE",
text="",
emboss=False,
).index = i
if collection: if collection:
#layout.separator() # layout.separator()
layout.prop(collection.rig_picker, 'destination', text='Filepath') layout.prop(collection.rig_picker, "destination", text="Filepath")
layout.operator('rigpicker.save_picker', icon='PASTEDOWN', text='Save Picker') layout.operator(
"rigpicker.save_picker", icon="PASTEDOWN", text="Save Picker"
)
class RP_PT_shape(bpy.types.Panel): class RP_PT_shape(bpy.types.Panel):
bl_label = 'Shape' bl_label = "Shape"
bl_category = 'Rigging' bl_category = "Rigging"
bl_space_type = 'VIEW_3D' bl_space_type = "VIEW_3D"
bl_region_type = 'UI' bl_region_type = "UI"
bl_parent_id = "RP_PT_picker_maker_panel" bl_parent_id = "RP_PT_picker_maker_panel"
def draw(self, context): def draw(self, context):
@ -86,63 +107,92 @@ class RP_PT_shape(bpy.types.Panel):
col = layout.column(align=False) col = layout.column(align=False)
if collection: 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) col = layout.column(align=True)
row = col.row(align=True) row = col.row(align=True)
row.operator('rigpicker.create_shape', icon='MESH_DATA', text='Create Shape') row.operator(
row.operator('rigpicker.mirror_shape', icon='MOD_MIRROR', text='Mirror Shape') "rigpicker.create_shape", icon="MESH_DATA", text="Create 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.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: else:
col = layout.column(align=True) col = layout.column(align=True)
row = col.row(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() box = layout.box()
col = box.column(align=False) col = box.column(align=False)
material_row = col.row(align=True) material_row = col.row(align=True)
material_row.operator('rigpicker.remove_mat', icon='REMOVE', text='') material_row.operator("rigpicker.remove_mat", icon="REMOVE", text="")
material_row.operator('rigpicker.add_mat', icon='ADD', text='') material_row.operator("rigpicker.add_mat", icon="ADD", text="")
mat = False 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) mat = get_mat(ob)
if mat and mat.node_tree: 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: 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 mat = True
if not mat: if not mat:
material_row.label(text='No Material') material_row.label(text="No Material")
material_row.operator('rigpicker.eyedropper_mat', icon='EYEDROPPER', text='') material_row.operator(
"rigpicker.eyedropper_mat", icon="EYEDROPPER", text=""
)
shape_type_row = col.row(align=True) shape_type_row = col.row(align=True)
shape_type_row.prop(ob.rig_picker,'shape_type',expand = 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.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 = col.row(align=True)
op_row.prop(ob.rig_picker, 'operator', text='') op_row.prop(ob.rig_picker, "operator", text="")
op_row.operator('rigpicker.operator_selector', text='', icon='COLLAPSEMENU') op_row.operator(
"rigpicker.operator_selector", text="", icon="COLLAPSEMENU"
)
if ob.rig_picker.operator: if ob.rig_picker.operator:
col.prop(ob.rig_picker, 'name', text='Tooltip') col.prop(ob.rig_picker, "name", text="Tooltip")
col.prop(ob.rig_picker,'shortcut', text='Shortcut') col.prop(ob.rig_picker, "shortcut", text="Shortcut")
else: 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: 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 = ( classes = (
#RP_UL_picker_source, # RP_UL_picker_source,
RP_PT_picker_maker_panel, RP_PT_picker_maker_panel,
RP_PT_shape RP_PT_shape,
) )
register, unregister = bpy.utils.register_classes_factory(classes) register, unregister = bpy.utils.register_classes_factory(classes)

126
uv.lock generated Normal file
View File

@ -0,0 +1,126 @@
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 = "fake-bpy-module"
version = "20251003"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/3a/d7/8e34a795715d18497a257929ace8776a94d6c2c744e36b61e75567cbda64/fake_bpy_module-20251003.tar.gz", hash = "sha256:43d7fd082efc34497e238de3ad4d31fb850f2d1a8bb07418ae36eae56c1c7434", size = 973644, upload-time = "2025-10-03T06:19:57.638Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/c6/24/d318f51ae1ebe953038984ed26fa0f9cd5466ef83da30c317282645695ec/fake_bpy_module-20251003-py3-none-any.whl", hash = "sha256:424f75fee6f300fc6d8e0d39abb28dbccd8777f611afa47ecdb9a03c00f2f43f", size = 1103011, upload-time = "2025-10-03T06:19:55.524Z" },
]
[[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" },
{ name = "fake-bpy-module" },
]
[package.metadata]
[package.metadata.requires-dev]
dev = [
{ name = "black", specifier = ">=25.11.0" },
{ name = "fake-bpy-module", specifier = ">=20251003" },
]