import sys import bpy from pathlib import Path import os from bone_widget.shape_utils import get_bone class BW_context: def __init__(self): self.module_dir = Path(__file__).parent self.module_name = self.module_dir.name self.id_name = 'bonewidget' self._rig = None self._folder_items = [] self.shapes = {} # used to store vert co for the transform properties @property def addon(self): from bpy import context return context.preferences.addons[__package__] @property def prefs(self): return self.addon.preferences #def settings(self): # return bpy.context.window_manager. @property def show_transforms(self): ops = bpy.context.window_manager.operators id_names = [self.get_op_id(o) for o in ('transform_widget','create_widget', 'match_transform')] return (ops and ops[-1].bl_idname in id_names) @property def clean_widget_op(self): ops = bpy.context.window_manager.operators op_idname = self.get_op_id('clean_widget') #transforms = self.prefs.transforms if ops and ops[-1].bl_idname == op_idname: return ops[-1] # @property # def show_prefs_op(self): # ops = bpy.context.window_manager.operators # id_names = [ # self.get_op_id('show_preferences'), # self.get_op_id('add_folder'), # self.get_op_id('remove_folder') # ] # #transforms = self.prefs.transforms # if ops and ops[-1].bl_idname in id_names: # return True @property def category(self): if not self.prefs: return 'Rig' return self.prefs.category @property def rig(self): bone = self.bone if bone: return bone.id_data @property def widget_col(self): col_name = self.prefs.collection col_name = col_name.format(ob=bpy.context.object) col = bpy.data.collections.get(col_name) if not col: col = bpy.data.collections.new(col_name) col.hide_viewport = True if col not in self.get_scene_cols(): bpy.context.scene.collection.children.link(col) return col @property def widget_layer_col(self): return next((c for c in self.get_layer_cols() if c.collection == self.widget_col), None) @property def default_folder_path(self): return self.prefs.default_folder.abspath @property def custom_folder_paths(self): return [f.abspath for f in self.prefs.folders if f.path] @property def folder_paths(self): return [self.default_folder_path] + self.custom_folder_paths @property def folders(self): return list(self.prefs.folders) + [self.prefs.default_folder] @property def active_folder(self): folder = self.abspath(self.prefs.folder_enum) if folder == self.default_folder_path: return self.prefs.default_folder else: return next((f for f in self.prefs.folders if f.abspath==folder), None) @property def active_widget(self): folder = self.active_folder if not folder: return index = folder.widget_index return folder.active_widget @property def bone(self): if not self.poll(): return ob = bpy.context.object if ob.type == 'ARMATURE': return bpy.context.active_pose_bone else: return get_bone(ob) @property def bones(self): ob = bpy.context.object if ob.type == 'ARMATURE': return list(ob.pose.bones) else: widgets = self.widgets return [b for b in self.rig.pose.bones if b.custom_shape in widgets] @property def selected_bones(self): if not self.poll(): return [] ob = bpy.context.object if ob.type == 'ARMATURE': return bpy.context.selected_pose_bones #bones = ob.pose.bones #return [b for b in bones if b.bone.select] elif self.rig: widgets = self.selected_widgets return [b for b in self.rig.pose.bones if b.custom_shape in widgets] return [] @property def widget(self): if not self.poll(): return ob = bpy.context.object if ob.type == 'ARMATURE': if bpy.context.active_pose_bone: return bpy.context.active_pose_bone.custom_shape else: return ob @property def widgets(self): ob = bpy.context.object if ob.type == 'ARMATURE': return [b.custom_shape for b in self.bones if b.custom_shape] @property def selected_widgets(self): if not self.poll(): return [] ob = bpy.context.object if ob.type == 'ARMATURE': bones = self.selected_bones return [b.custom_shape for b in bones if b.custom_shape] else: objects = bpy.context.selected_objects return [o for o in objects if o.select_get() and o.type in ('MESH', 'CURVE')] @property def folder_items(self): for folder in self.folder_paths: if not any(self.abspath(f[0])==folder for f in self._folder_items): id = 0 if self._folder_items: id = max([i[-1] for i in self._folder_items])+1 self._folder_items.append((str(folder), bpy.path.display_name(folder.name), '', '', id)) for f in reversed(self._folder_items): if self.abspath(f[0]) not in self.folder_paths: self._folder_items.remove(f) return self._folder_items def get_flipped_bone(self, bone): pass #def get_bone(self, shape): # armatures = [o for o in bpy.context.scene.objects if o.type == 'ARMATURE'] # return next((b for a in armatures for b in a.pose.bones if b.custom_shape == shape), None) def get_scene_cols(self, col=None, children=None): if children is None: children = set() if col is None: col = bpy.context.scene.collection for c in col.children: children.add(c) self.get_scene_cols(c, children) return list(children) def get_layer_cols(self, col=None, children=None): if children is None: children = set() if col is None: col = bpy.context.view_layer.layer_collection for c in col.children: children.add(c) self.get_layer_cols(c, children) return list(children) def abspath(self, path): if not path: return if path.startswith('//'): path = './' + path[2:] elif path.startswith('./') or not '/' in str(path): path = self.module_dir/'widgets' / path return Path(path).absolute().resolve() def poll(self): ob = bpy.context.object if not ob: return False if ob.type == 'ARMATURE' and ob.mode == 'POSE': return True if ob.type in ('MESH', 'CURVE'): return True return False def store_shapes(self): self.shapes.clear() for widget in self.selected_widgets: coords = [0]*len(widget.data.vertices)*3 widget.data.vertices.foreach_get('co', coords) self.shapes[widget] = coords def reset_transforms(self): transforms = self.prefs.transforms transforms['size'] = 1 transforms['xz_scale'] = 1 transforms['slide'] = 0 transforms['loc'] = (0, 0, 0) transforms['rot'] = (0, 0, 0) transforms['scale'] = (1, 1, 1) def init_transforms(self): self.store_shapes() self.reset_transforms() def get_op_id(self, name): return f'{self.id_name.upper()}_OT_{name}' #def call(self, op): # ops_data = getattr(bpy.ops, ctx.id_name) # getattr(ops_data, op)()