black format

This commit is contained in:
2025-12-05 10:05:33 +01:00
parent 9d09823321
commit 622048d573
19 changed files with 1126 additions and 788 deletions
+13 -12
View File
@@ -1,4 +1,3 @@
import bpy
from .bl_utils import get_mat, get_collection_parents
@@ -12,8 +11,10 @@ def get_picker_collection(ob=None):
for col in ob.users_collection:
if col.rig_picker.enabled:
return col
if picker_col := next((c for c in get_collection_parents(col) if c.rig_picker.enabled), None):
if picker_col := next(
(c for c in get_collection_parents(col) if c.rig_picker.enabled), None
):
return picker_col
@@ -22,10 +23,10 @@ def is_shape(ob):
canvas = scn.rig_picker.canvas
if not canvas or ob.hide_render:
return False
shapes = {ob for col in canvas.users_collection for ob in col.all_objects}
if ob.type in ('MESH', 'CURVE', 'FONT') and ob in shapes:
if ob.type in ("MESH", "CURVE", "FONT") and ob in shapes:
return True
return False
@@ -34,20 +35,20 @@ def is_shape(ob):
def get_object_color(ob):
if not ob.data.materials:
return
mat = get_mat(ob)
if not mat or not mat.node_tree or not mat.node_tree.nodes:
return
emit_node = mat.node_tree.nodes.get('Emission')
emit_node = mat.node_tree.nodes.get("Emission")
if not emit_node:
return
return emit_node.inputs['Color'].default_value
return emit_node.inputs["Color"].default_value
def get_operator_from_id(idname):
if not '.' in idname:
if not "." in idname:
return
m, o = idname.split(".")
@@ -56,5 +57,5 @@ def get_operator_from_id(idname):
op.get_rna_type()
except Exception:
return
return op
return op
+27 -18
View File
@@ -1,4 +1,3 @@
import bpy
@@ -30,94 +29,103 @@ def get_collection_parents(col, root=None, cols=None):
cols = get_collection_parents(col, root=sub, cols=cols)
return cols
def get_view_3d_override():
windows = bpy.context.window_manager.windows
areas = [a for w in windows for a in w.screen.areas if a.type == 'VIEW_3D']
areas = [a for w in windows for a in w.screen.areas if a.type == "VIEW_3D"]
if not areas:
print('No view 3d found')
print("No view 3d found")
return
view_3d = None
for area in areas:
if area.spaces.active.camera:
view_3d = area
if not view_3d:
view_3d = max(areas, key=lambda x :x.width)
return {'area': view_3d, 'region': view_3d.regions[-1]}
if not view_3d:
view_3d = max(areas, key=lambda x: x.width)
return {"area": view_3d, "region": view_3d.regions[-1]}
def get_mat(ob):
for sl in ob.material_slots:
if sl.material:
return sl.material
def link_mat_to_object(ob):
for sl in ob.material_slots:
m = sl.material
sl.link = 'OBJECT'
sl.link = "OBJECT"
sl.material = m
def eval_attr(ob, name):
resolved = ob
for o in name.split("."):
resolved = getattr(resolved, o)
return resolved
def flip_name(name):
if not name:
return
if name.startswith('[') and name.endswith(']'): #It's a custom property
if name.startswith("[") and name.endswith("]"): # It's a custom property
flipped_name = bpy.utils.flip_name(name[:-2][2:])
return f'["{flipped_name}"]'
else:
return bpy.utils.flip_name(name)
def split_path(path) :
try :
def split_path(path):
try:
bone_name = path.split('["')[1].split('"]')[0]
except Exception:
bone_name = None
try :
try:
prop_name = path.split('["')[2].split('"]')[0]
except Exception:
prop_name = None
return bone_name, prop_name
def hide_layers(args):
""" """
ob = bpy.context.object
layers = []
for bone in [b for b in ob.pose.bones if b.bone.select]:
for i,l in enumerate(bone.bone.layers):
for i, l in enumerate(bone.bone.layers):
if l and i not in layers:
layers.append(i)
for i in layers:
ob.data.layers[i] = not ob.data.layers[i]
def select_layer(args):
ob = bpy.context.object
layers =[]
layers = []
for bone in [b for b in ob.pose.bones if b.bone.select]:
bone_layers = [i for i,l in enumerate(bone.bone.layers) if l]
bone_layers = [i for i, l in enumerate(bone.bone.layers) if l]
for l in bone_layers:
if l not in layers:
layers.append(l)
for bone in ob.pose.bones:
bone_layers = [i for i,l in enumerate(bone.bone.layers) if l]
bone_layers = [i for i, l in enumerate(bone.bone.layers) if l]
if len(set(bone_layers).intersection(layers)):
bone.bone.select = True
def hide_bones(args):
ob = bpy.context.object
selected_bone = [b for b in ob.pose.bones if b.bone.select]
@@ -129,10 +137,11 @@ def hide_bones(args):
for bone in selected_bone:
bone.bone.hide = visibility
def select_all(args):
ob = bpy.context.object
shapes = ob.data.rig_picker['shapes']
bones = [s['bone'] for s in shapes if s['shape_type']=='BONE']
shapes = ob.data.rig_picker["shapes"]
bones = [s["bone"] for s in shapes if s["shape_type"] == "BONE"]
for bone_name in bones:
bone = ob.pose.bones.get(bone_name)
+27 -21
View File
@@ -1,28 +1,31 @@
import bpy
from mathutils import Vector
from mathutils.geometry import intersect_line_line_2d
def is_over_region(self,context,event):
inside = 2 < event.mouse_region_x < context.region.width -2 and \
2 < event.mouse_region_y < context.region.height -2 and \
[a for a in context.screen.areas if a.as_pointer()==self.adress] and \
not context.screen.show_fullscreen
def is_over_region(self, context, event):
inside = (
2 < event.mouse_region_x < context.region.width - 2
and 2 < event.mouse_region_y < context.region.height - 2
and [a for a in context.screen.areas if a.as_pointer() == self.adress]
and not context.screen.show_fullscreen
)
return inside
def bound_box_center(ob):
points = [ob.matrix_world@Vector(p) for p in ob.bound_box]
points = [ob.matrix_world @ Vector(p) for p in ob.bound_box]
x = [v[0] for v in points]
y = [v[1] for v in points]
z = [v[2] for v in points]
return (sum(x) / len(points), sum(y) / len(points),sum(z) / len(points))
return (sum(x) / len(points), sum(y) / len(points), sum(z) / len(points))
def bounding_rect(points):
x_points = sorted(p[0] for p in points)
x_points = sorted(p[0] for p in points)
y_points = sorted(p[1] for p in points)
return [
@@ -32,28 +35,31 @@ def bounding_rect(points):
Vector((x_points[0], y_points[0])),
]
def intersect_rects(rect, border): # returns None if rectangles don't intersect
dx = min(border[1][0],rect[1][0]) - max(border[0][0],rect[0][0])
dy = min(border[0][1],rect[0][1]) - max(border[2][1],rect[2][1])
if (dx>=0) and (dy>=0):
return dx*dy
def intersect_rects(rect, border): # returns None if rectangles don't intersect
dx = min(border[1][0], rect[1][0]) - max(border[0][0], rect[0][0])
dy = min(border[0][1], rect[0][1]) - max(border[2][1], rect[2][1])
if (dx >= 0) and (dy >= 0):
return dx * dy
def point_inside_rect(point, rect):
return rect[0][0]< point[0]< rect[1][0] and rect[2][1]< point[1]< rect[0][1]
return rect[0][0] < point[0] < rect[1][0] and rect[2][1] < point[1] < rect[0][1]
def point_over_shape(point,verts,loops,outside_point=(-1,-1)):
def point_over_shape(point, verts, loops, outside_point=(-1, -1)):
out = Vector(outside_point)
pt = Vector(point)
intersections = 0
for loop in loops:
for i,p in enumerate(loop):
a = Vector(verts[loop[i-1]])
for i, p in enumerate(loop):
a = Vector(verts[loop[i - 1]])
b = Vector(verts[p])
if intersect_line_line_2d(pt,out,a,b):
if intersect_line_line_2d(pt, out, a, b):
intersections += 1
if intersections%2 == 1: #chek if the nb of intersection is odd
return True
if intersections % 2 == 1: # chek if the nb of intersection is odd
return True
+239 -183
View File
@@ -1,11 +1,13 @@
import bpy
import gpu
from gpu_extras.batch import batch_for_shader
import blf
from mathutils import bvhtree, Vector, Color
from mathutils.geometry import (intersect_point_quad_2d, intersect_point_tri_2d,
intersect_tri_tri_2d)
from mathutils.geometry import (
intersect_point_quad_2d,
intersect_point_tri_2d,
intersect_tri_tri_2d,
)
from ..constants import PICKERS
from .addon_utils import get_operator_from_id
from .geometry_utils import bounding_rect
@@ -15,13 +17,22 @@ import re
import json
import os
import threading
import threading
class Shape:
def __init__(self, picker, points, polygons=None, edges=None, tooltip='', color=None, source_name=''):
def __init__(
self,
picker,
points,
polygons=None,
edges=None,
tooltip="",
color=None,
source_name="",
):
self.type = 'display'
self.type = "display"
self.picker = picker
self.rig = picker.rig
self.source_name = source_name
@@ -29,11 +40,11 @@ class Shape:
self.hover = False
self.press = False
self.shader = gpu.shader.from_builtin('UNIFORM_COLOR')
self.shader = gpu.shader.from_builtin("UNIFORM_COLOR")
self.shader.bind()
#self.hover_shader = gpu.shader.from_builtin('UNIFORM_COLOR')
#self.hover_shader.uniform_float("color", [1, 1, 1, 0.1])
# self.hover_shader = gpu.shader.from_builtin('UNIFORM_COLOR')
# self.hover_shader.uniform_float("color", [1, 1, 1, 0.1])
self.color = color
self.hover_color = self.brighten_color(self.color)
@@ -43,15 +54,19 @@ class Shape:
self.polygons = polygons or []
self.edges = edges or []
self.p_batch = batch_for_shader(self.shader, 'TRIS', {"pos": self.points}, indices=self.polygons)
self.e_batch = batch_for_shader(self.shader, 'LINES', {"pos": self.points}, indices=self.edges)
self.p_batch = batch_for_shader(
self.shader, "TRIS", {"pos": self.points}, indices=self.polygons
)
self.e_batch = batch_for_shader(
self.shader, "LINES", {"pos": self.points}, indices=self.edges
)
self.rect = bounding_rect(points)
@property
def color(self):
return self._color
def brighten_color(self, color):
brighten_color = Color(color[:3])
brighten_color.v += 0.05
@@ -70,16 +85,16 @@ class Shape:
elif len(color) == 4:
color = list(color)
else:
raise Exception('color must have a len of 3 or 4')
raise Exception("color must have a len of 3 or 4")
else:
raise Exception(f'color is {type(color)} must be None or (tuple, list)')
#self.shader.uniform_float("color", color)
raise Exception(f"color is {type(color)} must be None or (tuple, list)")
# self.shader.uniform_float("color", color)
self._color = color
def draw(self):
#self.shader.bind()
# self.shader.bind()
self.shader.uniform_float("color", self.color)
if self.polygons:
@@ -92,28 +107,45 @@ class Shape:
if not intersect_point_quad_2d(location, *self.rect):
self.hover = False
return False
for p in self.polygons:
if intersect_point_tri_2d(location, *[self.points[i] for i in p]):
self.hover = True
return True
self.hover = False
return False
def press_event(self, mode='SET'):
def press_event(self, mode="SET"):
self.press = True
def release_event(self, mode='SET'):
def release_event(self, mode="SET"):
self.press = False
class BoneShape(Shape):
def __init__(self, picker, points, polygons, edges, bone, tooltip='', color=None, source_name=''):
super().__init__(picker, points=points, polygons=polygons, edges=edges,
tooltip=tooltip, color=color, source_name=source_name)
def __init__(
self,
picker,
points,
polygons,
edges,
bone,
tooltip="",
color=None,
source_name="",
):
super().__init__(
picker,
points=points,
polygons=polygons,
edges=edges,
tooltip=tooltip,
color=color,
source_name=source_name,
)
self.type = 'bone'
self.type = "bone"
self.bone = bone
self.active_color = [1, 1, 1, 0.1]
self.select_color = self.brighten_color(self.hover_color)
@@ -125,23 +157,29 @@ class BoneShape(Shape):
if not self.bone:
return False
return self.bone in (bpy.context.selected_pose_bones or []) #self.bone.bone.select
return self.bone in (
bpy.context.selected_pose_bones or []
) # self.bone.bone.select
@property
def active(self):
if not self.bone:
return False
return self.bone == bpy.context.active_pose_bone #self.rig.data.bones.active == self.bone.bone
return (
self.bone == bpy.context.active_pose_bone
) # self.rig.data.bones.active == self.bone.bone
@property
def hide(self):
if not self.bone:
return False
#return self.bone not in (bpy.context.visible_pose_bones or [])
return self.bone.bone.hide or not any(l.is_visible for l in self.bone.bone.collections)
# return self.bone not in (bpy.context.visible_pose_bones or [])
return self.bone.bone.hide or not any(
l.is_visible for l in self.bone.bone.collections
)
@property
def bone_color(self):
@@ -149,45 +187,45 @@ class BoneShape(Shape):
return [0, 0, 0, 1]
if self.select and self.active:
return self.bone_colors['active']
return self.bone_colors["active"]
elif self.select:
return self.bone_colors['select']
return self.bone_colors["select"]
elif self.hide:
return self.bone_colors['hide']
return self.bone_colors["hide"]
else:
return self.bone_colors['normal']
return self.bone_colors["normal"]
def get_bone_colors(self):
theme = bpy.context.preferences.themes['Default']
theme = bpy.context.preferences.themes["Default"]
bone_colors = {
'select': [*theme.view_3d.bone_pose, 1],
'normal': [0.05, 0.05, 0.05, 1],
'active': [*theme.view_3d.bone_pose_active, 1],
'hide': [0.85, 0.85, 0.85, 0.2],
"select": [*theme.view_3d.bone_pose, 1],
"normal": [0.05, 0.05, 0.05, 1],
"active": [*theme.view_3d.bone_pose_active, 1],
"hide": [0.85, 0.85, 0.85, 0.2],
}
if not self.bone:
return bone_colors
if self.bone.color.palette == 'CUSTOM':
if self.bone.color.palette == "CUSTOM":
bone_color = self.bone
elif self.bone.color.palette == 'DEFAULT':
elif self.bone.color.palette == "DEFAULT":
bone_color = self.bone.bone
normal_color = bone_color.color.custom.normal.copy()
normal_color.s *= 0.75
bone_colors['normal'] = [*normal_color, 1]
bone_colors['select'] = [*bone_color.color.custom.select, 1]
bone_colors['active'] = [*bone_color.color.custom.active, 1]
bone_colors['hide'] = [*normal_color, 0.1]
bone_colors["normal"] = [*normal_color, 1]
bone_colors["select"] = [*bone_color.color.custom.select, 1]
bone_colors["active"] = [*bone_color.color.custom.active, 1]
bone_colors["hide"] = [*normal_color, 0.1]
return bone_colors
def draw_hided(self):
gpu.state.blend_set('ALPHA')
gpu.state.blend_set("ALPHA")
gpu.state.line_width_set(1.0)
line_color = (1, 1, 1, 0.1)
@@ -200,17 +238,17 @@ class BoneShape(Shape):
self.shader.uniform_float("color", (*color[:3], 0.5))
self.p_batch.draw(self.shader)
self.shader.uniform_float("color", line_color)
self.e_batch.draw(self.shader)
gpu.state.blend_set('NONE')
gpu.state.blend_set("NONE")
def draw_zero_scaled(self):
gpu.state.blend_set('ALPHA')
gpu.state.blend_set("ALPHA")
gpu.state.line_width_set(1.0)
line_color = (*self.bone_colors['normal'][:3], 0.2)
line_color = (*self.bone_colors["normal"][:3], 0.2)
color = Color(self.color[:3])
if self.hover or self.select:
color.v += 0.05
@@ -219,14 +257,14 @@ class BoneShape(Shape):
self.shader.uniform_float("color", (*color[:3], 0.5))
self.p_batch.draw(self.shader)
self.shader.uniform_float("color", line_color)
self.e_batch.draw(self.shader)
gpu.state.blend_set('NONE')
gpu.state.blend_set("NONE")
def draw(self):
gpu.state.blend_set('ALPHA')
gpu.state.blend_set("ALPHA")
gpu.state.line_width_set(1.0)
if self.hide:
@@ -244,9 +282,9 @@ class BoneShape(Shape):
self.shader.uniform_float("color", color)
self.p_batch.draw(self.shader)
# Draw Outline
gpu.state.blend_set('NONE')
gpu.state.blend_set("NONE")
if self.select or self.active:
gpu.state.line_width_set(2.0)
@@ -254,46 +292,45 @@ class BoneShape(Shape):
self.e_batch.draw(self.shader)
gpu.state.line_width_set(1.0)
def assign_bone_event(self):
#print('assign_bone_event', self)
# print('assign_bone_event', self)
scn = bpy.context.scene
rig = self.picker.rig
source_object = scn.objects.get(self.source_name)
if not source_object:
print(f'Source object {self.source_name} not found')
return
active_bone = rig.data.bones.active
if not active_bone:
print('You need to have an active bone')
print(f"Source object {self.source_name} not found")
return
#print(active_bone, source_object)
#print(rig)
active_bone = rig.data.bones.active
if not active_bone:
print("You need to have an active bone")
return
# print(active_bone, source_object)
# print(rig)
source_object.name = rig.data.bones.active.name
source_object.rig_picker.name = rig.data.bones.active.name
def release_event(self, mode='SET'):
def release_event(self, mode="SET"):
super().release_event(mode)
if self.hide or not self.bone:
return
select = True
if mode == 'SUBSTRACT':
if mode == "SUBSTRACT":
select = False
self.bone.bone.select = select
if self.hover:
if mode != 'SUBSTRACT':
if mode != "SUBSTRACT":
self.rig.data.bones.active = self.bone.bone
def border_select(self, border, mode='SET'):
def border_select(self, border, mode="SET"):
if not self.bone:
return
@@ -307,28 +344,30 @@ class BoneShape(Shape):
border_tri1 = border[0], border[1], border[2]
border_tri2 = border[2], border[3], border[0]
if (not intersect_tri_tri_2d(*border_tri1, *rect_tri1) and
not intersect_tri_tri_2d(*border_tri1, *rect_tri2) and
not intersect_tri_tri_2d(*border_tri2, *rect_tri1) and
not intersect_tri_tri_2d(*border_tri2, *rect_tri2)):
if (
not intersect_tri_tri_2d(*border_tri1, *rect_tri1)
and not intersect_tri_tri_2d(*border_tri1, *rect_tri2)
and not intersect_tri_tri_2d(*border_tri2, *rect_tri1)
and not intersect_tri_tri_2d(*border_tri2, *rect_tri2)
):
return
select = True
if mode == 'SUBSTRACT':
if mode == "SUBSTRACT":
select = False
for polygon in self.polygons:
points = [self.points[i] for i in polygon]
if intersect_tri_tri_2d(*border_tri1, *points):
self.bone.bone.select = select
return
if intersect_tri_tri_2d(*border_tri2, *points):
self.bone.bone.select = select
return
'''
"""
for b in border:
if intersect_point_tri_2d(b, *points):
self.bone.bone.select = select
@@ -338,46 +377,58 @@ class BoneShape(Shape):
if intersect_point_quad_2d(p, *border):
self.bone.bone.select = select
return
'''
"""
class OperatorShape(Shape):
def __init__(self, picker, points, polygons, operator, tooltip='', color=None, source_name=''):
super().__init__(picker, points=points, polygons=polygons, tooltip=tooltip,
color=color, source_name=source_name)
def __init__(
self, picker, points, polygons, operator, tooltip="", color=None, source_name=""
):
super().__init__(
picker,
points=points,
polygons=polygons,
tooltip=tooltip,
color=color,
source_name=source_name,
)
self.type = 'operator'
self.type = "operator"
self.active_color = [1, 1, 1, 0.15]
self.press_color = [0, 0, 0, 0.25]
self.operator = operator
#self.arguments = arguments#{k:eval(v)}
#self.operator = get_operator_from_id(self.operator)
# self.arguments = arguments#{k:eval(v)}
# self.operator = get_operator_from_id(self.operator)
if not tooltip:
self.tooltip = self.operator.replace('bpy.ops.', '').replace("'INVOKE_DEFAULT', ", '')
self.tooltip = self.operator.replace("bpy.ops.", "").replace(
"'INVOKE_DEFAULT', ", ""
)
#self.reg_args = re.compile(r'(\w+)=')
'''
# self.reg_args = re.compile(r'(\w+)=')
"""
def parse_args(self):
args = self.reg_args.split(self.arguments)[1:]
#print(args, zip(args[::2], args[1::2]))
return {k: eval(v) for k, v in zip(args[::2], args[1::2])}
#return {k:eval(v) for k, v in self.reg_args.split(self.arguments)}
'''
"""
def release_event(self, mode='SET'):
def release_event(self, mode="SET"):
super().release_event(mode)
#args = self.parse_args()
# args = self.parse_args()
if not self.operator:
return
exec(self.operator)
#f'bpy.ops;{idname}'
# f'bpy.ops;{idname}'
#print(self.idname)
#print(self.arguments)
#else:
# print(self.idname)
# print(self.arguments)
# else:
# self.bone.bone.select = False
def draw(self):
@@ -390,10 +441,10 @@ class OperatorShape(Shape):
else:
return
gpu.state.blend_set('ALPHA')
gpu.state.blend_set("ALPHA")
self.shader.uniform_float("color", color)
self.p_batch.draw(self.shader)
gpu.state.blend_set('NONE')
gpu.state.blend_set("NONE")
class Picker:
@@ -406,48 +457,48 @@ class Picker:
self.shapes = []
self.box_select = None
self.hover_shape = None
self.shader = gpu.shader.from_builtin('UNIFORM_COLOR')
self.shader = gpu.shader.from_builtin("UNIFORM_COLOR")
self.mouse = None
for shape_data in shapes:
if not shape_data['points']:
if not shape_data["points"]:
continue
if shape_data['type'] in ('CANVAS', 'DISPLAY'):
if shape_data["type"] in ("CANVAS", "DISPLAY"):
shape = Shape(
self,
points=shape_data['points'],
polygons=shape_data['polygons'],
edges=shape_data['edges'],
color=shape_data['color']
points=shape_data["points"],
polygons=shape_data["polygons"],
edges=shape_data["edges"],
color=shape_data["color"],
)
elif shape_data['type'] == 'BONE':
bone = rig.pose.bones.get(shape_data['bone'])
#if not bone:
elif shape_data["type"] == "BONE":
bone = rig.pose.bones.get(shape_data["bone"])
# if not bone:
# print(f'Bone {shape_data["bone"]} not exist')
# continue
shape = BoneShape(
self,
source_name=shape_data['source_name'],
points=shape_data['points'],
polygons=shape_data['polygons'],
edges=shape_data['edges'],
bone=bone,
color=shape_data['color']
source_name=shape_data["source_name"],
points=shape_data["points"],
polygons=shape_data["polygons"],
edges=shape_data["edges"],
bone=bone,
color=shape_data["color"],
)
elif shape_data['type'] == 'OPERATOR':
elif shape_data["type"] == "OPERATOR":
shape = OperatorShape(
self,
source_name=shape_data['source_name'],
points=shape_data['points'],
polygons=shape_data['polygons'],
operator=shape_data['operator'],
color=shape_data['color'],
tooltip=shape_data['tooltip'],
source_name=shape_data["source_name"],
points=shape_data["points"],
polygons=shape_data["polygons"],
operator=shape_data["operator"],
color=shape_data["color"],
tooltip=shape_data["tooltip"],
)
self.shapes.append(shape)
@@ -456,36 +507,35 @@ class Picker:
def assign_bone_event(self):
for shape in self.shapes:
if shape.type == 'bone' and shape.hover:
if shape.type == "bone" and shape.hover:
shape.assign_bone_event()
bpy.ops.rigpicker.save_picker(index=self.index)
def press_event(self, mode='SET'):
def press_event(self, mode="SET"):
for shape in self.shapes:
#print(s)
# print(s)
if shape.hover:
shape.press_event(mode)
else:
shape.press = False
def release_event(self, mode='SET'):
def release_event(self, mode="SET"):
# bpy.ops.pose.select_all(action='DESELECT')
#bpy.ops.pose.select_all(action='DESELECT')
#print('PICKER release event', mode)
#print(f'type={event.type}, value={event.value}, ctrl={event.ctrl}, shift={event.shift}, alt={event.alt}')
# print('PICKER release event', mode)
# print(f'type={event.type}, value={event.value}, ctrl={event.ctrl}, shift={event.shift}, alt={event.alt}')
for shape in self.shapes:
if shape.hover:
shape.release_event(mode)
shape.press = False
#bpy.context.area.tag_redraw()
'''
shape.press = False
# bpy.context.area.tag_redraw()
"""
picker.tooltip_event(event='SHOW')
region.tag_redraw()
@@ -495,8 +545,9 @@ class Picker:
picker.tooltip_event(event='HIDE')
bpy.app.timers.register(partial(tooltip, context.region), first_interval=1)
'''
'''
"""
"""
def tooltip_event(self, event):
@@ -508,7 +559,7 @@ class Picker:
self.tooltip = self.hover_shape.bone.name
#bpy.context.region.tag_redraw()
'''
"""
def border_select(self, border, mode):
border = [Vector(b) - Vector(self.translation) for b in border]
@@ -516,7 +567,7 @@ class Picker:
for shape in self.shapes:
shape.press = False
shape.hover = False
if shape.type != 'bone':
if shape.type != "bone":
continue
shape.border_select(border, mode)
@@ -531,16 +582,16 @@ class Picker:
elif shape.move_event(self.mouse):
self.hover_shape = shape
#if point_inside_rect(self.end, rect):
# if point_inside_rect(self.end, rect):
# over = point_over_shape(self.end,points, edges)
#if bpy.app.timers.is_registered(self.tooltip_event):
#try:
# if bpy.app.timers.is_registered(self.tooltip_event):
# try:
# bpy.app.timers.unregister(self.tooltip_event)
#except:
# except:
# pass
#bpy.app.timers.register(self.tooltip_event, first_interval=1)
# bpy.app.timers.register(self.tooltip_event, first_interval=1)
def draw(self):
with gpu.matrix.push_pop():
@@ -565,7 +616,7 @@ class Picker:
class PickerGroup:
def __init__(self, rig, pickers):
#self.view_location = Vector((0, 0))
# self.view_location = Vector((0, 0))
self.region = bpy.context.region
self.rig = rig
self.pickers = []
@@ -577,13 +628,13 @@ class PickerGroup:
self.tooltip_shape = None
self.tooltip_mouse = Vector((0, 0))
self.tooltip = ''
self.tooltip = ""
self.timer = None
for picker in pickers:
self.add_picker(picker)
def add_picker(self, picker):
self.pickers.append(Picker(self, self.rig, picker))
@@ -592,12 +643,12 @@ class PickerGroup:
for picker in self.pickers:
height = picker.rect[1][1] - picker.rect[-1][1]
picker.translation = Vector((0, -y-height*0.5))
picker.translation = Vector((0, -y - height * 0.5))
picker.draw()
y += height + 50
#break #TODO for now only draw first picker
# break #TODO for now only draw first picker
def move_event(self, mouse):
self.mouse = mouse
@@ -618,14 +669,14 @@ class PickerGroup:
continue
picker.move_event(self.location)
if picker.hover_shape:
hover_shape = picker.hover_shape
self.hover_shape = hover_shape
if self.tooltip_shape is not self.hover_shape:
self.tooltip = ''
self.tooltip = ""
if self.timer:
self.timer.cancel()
@@ -633,8 +684,8 @@ class PickerGroup:
self.timer = threading.Timer(0.4, self.tooltip_event)
self.timer.start()
def press_event(self, mode='SET'):
#self.clear_tooltip()
def press_event(self, mode="SET"):
# self.clear_tooltip()
for picker in self.pickers:
if picker.under_mouse(self.location):
@@ -643,8 +694,8 @@ class PickerGroup:
for shape in picker.shapes:
shape.press = False
def release_event(self, mode='SET'):
if mode == 'SET':
def release_event(self, mode="SET"):
if mode == "SET":
for bone in self.rig.data.bones:
bone.select = False
@@ -663,7 +714,7 @@ class PickerGroup:
def border_select(self, border, mode):
border = [self.region.view2d.region_to_view(*b) for b in border]
if mode == 'SET':
if mode == "SET":
for bone in self.rig.data.bones:
bone.select = False
@@ -671,37 +722,38 @@ class PickerGroup:
picker.border_select(border, mode)
def clear_tooltip(self):
self.tooltip = ''
self.tooltip = ""
self.tooltip_shape = None
self.timer.cancel()
self.region.tag_redraw()
def tooltip_event(self):
#print('Tooltip Event', self)
# print('Tooltip Event', self)
#print(self.hover_shape, self.hover_shape.type)
# print(self.hover_shape, self.hover_shape.type)
if self.hover_shape and self.hover_shape.type != 'display':
if self.hover_shape.type == 'bone' and self.hover_shape.bone:
if self.hover_shape and self.hover_shape.type != "display":
if self.hover_shape.type == "bone" and self.hover_shape.bone:
self.tooltip = self.hover_shape.bone.name
else:
self.tooltip = self.hover_shape.tooltip
self.tooltip_shape = self.hover_shape
else:
return self.clear_tooltip()
self.tooltip_mouse = self.mouse
self.timer.cancel()
#print(self.tooltip)
# print(self.tooltip)
self.region.tag_redraw()
@property
def rect(self):
return bounding_rect([co - Vector(p.translation) for p in self.pickers for co in p.rect])
return bounding_rect(
[co - Vector(p.translation) for p in self.pickers for co in p.rect]
)
@property
def center(self):
@@ -711,22 +763,26 @@ class PickerGroup:
def load_picker_data(rig):
if 'pickers' in rig.data.rig_picker:
picker_datas = [[s.to_dict() for s in p] for p in rig.data.rig_picker['pickers']]
if "pickers" in rig.data.rig_picker:
picker_datas = [
[s.to_dict() for s in p] for p in rig.data.rig_picker["pickers"]
]
else:
picker_datas = []
for picker in rig.data.rig_picker.sources:
picker_path = Path(bpy.path.abspath(picker.source, library=rig.data.library))
picker_path = Path(
bpy.path.abspath(picker.source, library=rig.data.library)
)
if not picker_path.exists():
print(f'Picker path not exists: {picker_path.resolve()}')
print(f"Picker path not exists: {picker_path.resolve()}")
continue
print('Load picker from', picker_path.resolve())
picker_data = json.loads(picker_path.read_text(encoding='utf-8'))
print("Load picker from", picker_path.resolve())
picker_data = json.loads(picker_path.read_text(encoding="utf-8"))
picker_datas.append(picker_data)
PICKERS[rig] = PickerGroup(rig, picker_datas)
@@ -736,24 +792,24 @@ def get_picker_path(rig, source, start=None):
def pack_picker(rig, start=None):
if not 'rig_picker' in rig.data:
if not "rig_picker" in rig.data:
return
pickers = []
for picker_source in rig.data['rig_picker'].get('sources', []):
picker_path = get_picker_path(rig, picker_source['source'], start)
for picker_source in rig.data["rig_picker"].get("sources", []):
picker_path = get_picker_path(rig, picker_source["source"], start)
if not picker_path.exists():
print(f'{picker_path} not exists')
print(f"{picker_path} not exists")
continue
picker_data = json.loads(picker_path.read_text(encoding='utf-8'))
picker_data = json.loads(picker_path.read_text(encoding="utf-8"))
pickers.append(picker_data)
rig.data['rig_picker']['pickers'] = pickers
rig.data["rig_picker"]["pickers"] = pickers
def unpack_picker(rig):
if 'rig_picker' not in rig.data.keys():
if "rig_picker" not in rig.data.keys():
return
if 'pickers' in rig.data['rig_picker'].keys():
del rig.data['rig_picker']['pickers']
if "pickers" in rig.data["rig_picker"].keys():
del rig.data["rig_picker"]["pickers"]
+46 -44
View File
@@ -6,30 +6,29 @@ from .geometry_utils import bound_box_center
from .addon_utils import get_object_color
def border_over_shape(border,verts,loops):
def border_over_shape(border, verts, loops):
for loop in loops:
for i,p in enumerate(loop):
a = Vector(verts[loop[i-1]])
for i, p in enumerate(loop):
a = Vector(verts[loop[i - 1]])
b = Vector(verts[p])
for j in range(0,4):
c = border[j-1]
for j in range(0, 4):
c = border[j - 1]
d = border[j]
if intersect_line_line_2d(a,b,c,d):
if intersect_line_line_2d(a, b, c, d):
return True
for point in verts:
if point_inside_rect(point,border):
if point_inside_rect(point, border):
return True
for point in border:
if point_over_shape(point,verts,loops):
if point_over_shape(point, verts, loops):
return True
def border_loop(vert, loop):
border_edge =[e for e in vert.link_edges if e.is_rectary]
border_edge = [e for e in vert.link_edges if e.is_rectary]
if border_edge:
for edge in border_edge:
@@ -43,6 +42,7 @@ def border_loop(vert, loop):
else:
return [vert]
def contour_loops(bm, vert_index=0, loops=None, vert_indices=None):
loops = loops or []
vert_indices = vert_indices or [v.index for v in bm.verts]
@@ -50,7 +50,7 @@ def contour_loops(bm, vert_index=0, loops=None, vert_indices=None):
bm.verts.ensure_lookup_table()
loop = border_loop(bm.verts[vert_index], [bm.verts[vert_index]])
if len(loop) >1:
if len(loop) > 1:
loops.append(loop)
for v in loop:
@@ -75,14 +75,13 @@ def get_shape_data(ob, matrix=None, depsgraph=None):
bm = bmesh.new()
bm.from_mesh(mesh)
bmesh.ops.remove_doubles(bm, verts=bm.verts, dist=0.002)
bmesh.ops.dissolve_limit(bm, angle_limit=0.001745, verts=bm.verts, edges=bm.edges)
bmesh.ops.connect_verts_concave(bm, faces=bm.faces)
bmesh.ops.triangulate(bm, faces=bm.faces)
edges = [[v.index for v in e.verts] for e in bm.edges if len(e.link_faces)<=1]
edges = [[v.index for v in e.verts] for e in bm.edges if len(e.link_faces) <= 1]
bm.to_mesh(mesh)
mesh.update()
bm.clear()
@@ -96,11 +95,11 @@ def get_shape_data(ob, matrix=None, depsgraph=None):
points.append([round(co[0], 1), round(co[1], 1)])
depths += [co[2]]
if depths:
depth = max(depths)
else:
print(f'{ob.name} has no vertices')
print(f"{ob.name} has no vertices")
depth = 0
for face in mesh.polygons:
@@ -113,67 +112,70 @@ def get_shape_data(ob, matrix=None, depsgraph=None):
color = [0.5, 0.5, 0.5]
shape = {
'source_name': ob.name,
'tooltip': ob.rig_picker.name,
'depth': depth,
'points': points,
'polygons': polygons,
'edges': edges,
'color': color,
'type': ob.rig_picker.shape_type
"source_name": ob.name,
"tooltip": ob.rig_picker.name,
"depth": depth,
"points": points,
"polygons": polygons,
"edges": edges,
"color": color,
"type": ob.rig_picker.shape_type,
}
if shape['type'] =='OPERATOR':
shape['operator'] = ob.rig_picker.operator
shape['shortcut'] = ob.rig_picker.shortcut
if shape["type"] == "OPERATOR":
shape["operator"] = ob.rig_picker.operator
shape["shortcut"] = ob.rig_picker.shortcut
elif shape["type"] == "BONE":
shape["bone"] = ob.rig_picker.name
elif shape['type'] =='BONE':
shape['bone'] = ob.rig_picker.name
eval_ob.to_mesh_clear()
return shape
def get_picker_data(collection):
picker_data = []
depsgraph = bpy.context.evaluated_depsgraph_get()
canvas = collection.rig_picker.canvas
if canvas.type == 'CURVE':
if canvas.type == "CURVE":
canvas_points = canvas.data.splines[0].points
else:
canvas_points = canvas.data.vertices
canvas_coords = [canvas.matrix_world@Vector((p.co)) for p in canvas_points]
canvas_coords = [canvas.matrix_world @ Vector((p.co)) for p in canvas_points]
height = abs(max(co.y for co in canvas_coords) - min(co.y for co in canvas_coords))
width = abs(max(co.x for co in canvas_coords) - min(co.x for co in canvas_coords))
center = sum(canvas_coords, Vector()) / len(canvas_coords)
scale = 2048 / max(height, width)# Reference height for the canvas
scale = 2048 / max(height, width) # Reference height for the canvas
matrix = Matrix.Translation(-center)
matrix = Matrix.Scale(scale, 4) @ matrix
#sorted by their z axes
# sorted by their z axes
for ob in collection.all_objects:
if ob.instance_collection:
for shape in ob.instance_collection.all_objects:
picker_data.append(get_shape_data(shape, matrix=matrix@ob.matrix_world, depsgraph=depsgraph))
elif ob.type in ('MESH', 'CURVE', 'FONT'):
picker_data.append(
get_shape_data(
shape, matrix=matrix @ ob.matrix_world, depsgraph=depsgraph
)
)
elif ob.type in ("MESH", "CURVE", "FONT"):
picker_data.append(get_shape_data(ob, matrix=matrix, depsgraph=depsgraph))
else:
#print(f'{ob.name} of type {ob.type} not supported')
# print(f'{ob.name} of type {ob.type} not supported')
continue
picker_data.sort(key=lambda x : x['depth'])
picker_data.sort(key=lambda x: x["depth"])
#print(picker_datas)
# print(picker_datas)
return picker_data
#rig.data.rig_picker['shapes'] = picker_datas
# rig.data.rig_picker['shapes'] = picker_datas