diff --git a/GP_guided_colorize/OP_create_empty_frames.py b/GP_guided_colorize/OP_create_empty_frames.py index e20a6ee..1fe1c2f 100644 --- a/GP_guided_colorize/OP_create_empty_frames.py +++ b/GP_guided_colorize/OP_create_empty_frames.py @@ -11,17 +11,34 @@ def get_layer_list(self, context): '''return (identifier, name, description) of enum content''' return [(l.name, l.name, '') for l in context.object.data.layers if l != context.object.data.layers.active] +def get_group_list(self, context): + return [(g.name, g.name, '') for g in context.object.data.layer_groups] + +def get_top_layer_from_group(gp, group): + upper_layer = None + for layer in gp.layers: + if layer.parent_group == group: + upper_layer = layer + return upper_layer + class GP_OT_create_empty_frames(bpy.types.Operator): bl_idname = "gp.create_empty_frames" bl_label = "Create Empty Frames" - bl_description = "Create new empty frames on active layer where there is a frame in layer above\n(usefull in color layers to match line frames)" + bl_description = "Create new empty frames on active layer where there is a frame in targeted layers\ + \n(usefull in color layers to match line frames)" bl_options = {'REGISTER','UNDO'} layers_enum : EnumProperty( - name="Duplicate to layers", - description="Duplicate selected keys in active layer and send them to choosen layer", + name="Empty Keys from Layer", + description="Reference keys from layer", items=get_layer_list ) + + groups_enum : EnumProperty( + name="Empty Keys from Group", + description="Duplicate keys from group", + items=get_group_list + ) targeted_layers : EnumProperty( name="Sources", # Empty keys from targets @@ -34,7 +51,8 @@ class GP_OT_create_empty_frames(bpy.types.Operator): ('ABOVE', 'Layer Directly Above', 'Empty frames from layer directly above'), ('BELOW', 'Layer Directly Below', 'Empty frames from layer directly below'), ('ALL_VISIBLE', 'Visible', 'Empty frames from all visible layers'), - ('CHOSEN', 'Chosen layer', ''), + ('CHOSEN', 'Chosen layer', 'Empty frames from a specific layer'), + ('CHOSEN_GROUP', 'Chosen group', 'Empty frames from a specific layer group'), ) ) @@ -63,6 +81,13 @@ class GP_OT_create_empty_frames(bpy.types.Operator): # Possible preset with shortcut # if event.alt: # self.targeted_layers = 'ALL_VISIBLE' + gp = context.grease_pencil + layer_from_group = None + if gp.layer_groups.active: + layer_from_group = get_top_layer_from_group(gp, gp.layer_groups.active) + if not gp.layers.active and not layer_from_group: + self.report({'ERROR'}, 'No active layer or active group containing layer on GP object') + return {'CANCELLED'} return context.window_manager.invoke_props_dialog(self) def draw(self, context): @@ -74,7 +99,13 @@ class GP_OT_create_empty_frames(bpy.types.Operator): if self.layers_enum: layout.prop(self, 'layers_enum') else: - layout.label(text='No other layers to match keyframe') + layout.label(text='No other layers to match keyframe!', icon='ERROR') + + if self.targeted_layers == 'CHOSEN_GROUP': + if self.groups_enum: + layout.prop(self, 'groups_enum') + else: + layout.label(text='No other groups to match keyframe!', icon='ERROR') elif self.targeted_layers == 'NUMBER': row = layout.row() @@ -91,20 +122,28 @@ class GP_OT_create_empty_frames(bpy.types.Operator): def execute(self, context): obj = context.object - gpl = obj.data.layers - + gp = obj.data + gpl = gp.layers + + if gp.layer_groups.active: + reference_layer = get_top_layer_from_group(gp, gp.layer_groups.active) + else: + reference_layer = gpl.active + + active_index = next((i for i, l in enumerate(gpl) if l == reference_layer), None) + print(self.targeted_layers) if self.targeted_layers == 'ALL_ABOVE': - tgt_layers = [l for i, l in enumerate(gpl) if i > gpl.active_index] + tgt_layers = [l for i, l in enumerate(gpl) if i > active_index] elif self.targeted_layers == 'ALL_BELOW': - tgt_layers = [l for i, l in enumerate(gpl) if i < gpl.active_index] + tgt_layers = [l for i, l in enumerate(gpl) if i < active_index] elif self.targeted_layers == 'ABOVE': - tgt_layers = [l for i, l in enumerate(gpl) if i == gpl.active_index + 1] + tgt_layers = [l for i, l in enumerate(gpl) if i == active_index + 1] elif self.targeted_layers == 'BELOW': - tgt_layers = [l for i, l in enumerate(gpl) if i == gpl.active_index - 1] + tgt_layers = [l for i, l in enumerate(gpl) if i == active_index - 1] elif self.targeted_layers == 'ALL_VISIBLE': tgt_layers = [l for l in gpl if not l.hide and l != gpl.active] @@ -115,17 +154,24 @@ class GP_OT_create_empty_frames(bpy.types.Operator): return {'CANCELLED'} tgt_layers = [l for l in gpl if l.name == self.layers_enum] + elif self.targeted_layers == 'CHOSEN_GROUP': + if not self.groups_enum: + self.report({'ERROR'}, f"No chosen groups, aborted") + return {'CANCELLED'} + group = gp.layer_groups.get(self.groups_enum) + tgt_layers = [l for l in gpl if l.parent_group == group] + elif self.targeted_layers == 'NUMBER': if self.number == 0: self.report({'ERROR'}, f"Can't have 0 as value") return {'CANCELLED'} - l_range = gpl.active_index + self.number + l_range = active_index + self.number print('l_range: ', l_range) if self.number > 0: # positive - tgt_layers = [l for i, l in enumerate(gpl) if gpl.active_index < i <= l_range] + tgt_layers = [l for i, l in enumerate(gpl) if active_index < i <= l_range] else: - tgt_layers = [l for i, l in enumerate(gpl) if gpl.active_index > i >= l_range] + tgt_layers = [l for i, l in enumerate(gpl) if active_index > i >= l_range] if not tgt_layers: self.report({'ERROR'}, f"No layers found with chosen Targets") @@ -163,7 +209,7 @@ class GP_OT_create_empty_frames(bpy.types.Operator): if num in current_frames: continue #Create empty frame - gpl.active.frames.new(num, active=False) + gpl.active.frames.new(num) fct += 1 gpl.update() diff --git a/utils.py b/utils.py index 4afeb3b..b5c75db 100644 --- a/utils.py +++ b/utils.py @@ -155,6 +155,44 @@ def gp_stroke_to_bmesh(strokes): ### GP Drawing # ----------------- +def layer_active_index(gpl): + '''Get layer list and return index of active layer + Can return None if no active layer found (active item can be a group) + ''' + return next((i for i, l in enumerate(gpl) if l == gpl.active), None) + +## Check for nested lock +def is_locked(stack_item): + '''Check if passed stack item (layer or group) is locked + either itself or by parent groups''' + if stack_item.lock: + return True + if stack_item.parent_group: + return is_locked(stack_item.parent_group) + return False + +def is_parent_locked(stack_item): + '''Check if passed stack item (layer or group) is locked by parent groups''' + if stack_item.parent_group: + return is_locked(stack_item.parent_group) + return False + +## Check for nested hide +def is_hidden(stack_item): + '''Check if passed stack item (layer or group) is hidden + either itself or by parent groups''' + if stack_item.hide: + return True + if stack_item.parent_group: + return is_hidden(stack_item.parent_group) + return False + +def is_parent_hidden(stack_item): + '''Check if passed stack item (layer or group) is hidden by parent groups''' + if stack_item.parent_group: + return is_hidden(stack_item.parent_group) + return False + def simple_draw_gp_stroke(pts, frame, width = 2, mat_id = 0): ''' draw basic stroke by passing list of point 3D coordinate