gpv3 port: create empty frames - add support for group as source

add utility function to check hide-lock state of nested items
master
pullusb 2024-11-14 17:27:57 +01:00
parent b525cda28e
commit 49c70860a6
2 changed files with 99 additions and 15 deletions

View File

@ -11,18 +11,35 @@ def get_layer_list(self, context):
'''return (identifier, name, description) of enum content''' '''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] 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): class GP_OT_create_empty_frames(bpy.types.Operator):
bl_idname = "gp.create_empty_frames" bl_idname = "gp.create_empty_frames"
bl_label = "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'} bl_options = {'REGISTER','UNDO'}
layers_enum : EnumProperty( layers_enum : EnumProperty(
name="Duplicate to layers", name="Empty Keys from Layer",
description="Duplicate selected keys in active layer and send them to choosen layer", description="Reference keys from layer",
items=get_layer_list items=get_layer_list
) )
groups_enum : EnumProperty(
name="Empty Keys from Group",
description="Duplicate keys from group",
items=get_group_list
)
targeted_layers : EnumProperty( targeted_layers : EnumProperty(
name="Sources", # Empty keys from targets name="Sources", # Empty keys from targets
description="Duplicate keys as empty on current layer from selected targets", description="Duplicate keys as empty on current layer from selected targets",
@ -34,7 +51,8 @@ class GP_OT_create_empty_frames(bpy.types.Operator):
('ABOVE', 'Layer Directly Above', 'Empty frames from layer directly above'), ('ABOVE', 'Layer Directly Above', 'Empty frames from layer directly above'),
('BELOW', 'Layer Directly Below', 'Empty frames from layer directly below'), ('BELOW', 'Layer Directly Below', 'Empty frames from layer directly below'),
('ALL_VISIBLE', 'Visible', 'Empty frames from all visible layers'), ('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 # Possible preset with shortcut
# if event.alt: # if event.alt:
# self.targeted_layers = 'ALL_VISIBLE' # 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) return context.window_manager.invoke_props_dialog(self)
def draw(self, context): def draw(self, context):
@ -74,7 +99,13 @@ class GP_OT_create_empty_frames(bpy.types.Operator):
if self.layers_enum: if self.layers_enum:
layout.prop(self, 'layers_enum') layout.prop(self, 'layers_enum')
else: 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': elif self.targeted_layers == 'NUMBER':
row = layout.row() row = layout.row()
@ -91,20 +122,28 @@ class GP_OT_create_empty_frames(bpy.types.Operator):
def execute(self, context): def execute(self, context):
obj = context.object 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) print(self.targeted_layers)
if self.targeted_layers == 'ALL_ABOVE': 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': 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': 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': 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': elif self.targeted_layers == 'ALL_VISIBLE':
tgt_layers = [l for l in gpl if not l.hide and l != gpl.active] 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'} return {'CANCELLED'}
tgt_layers = [l for l in gpl if l.name == self.layers_enum] 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': elif self.targeted_layers == 'NUMBER':
if self.number == 0: if self.number == 0:
self.report({'ERROR'}, f"Can't have 0 as value") self.report({'ERROR'}, f"Can't have 0 as value")
return {'CANCELLED'} return {'CANCELLED'}
l_range = gpl.active_index + self.number l_range = active_index + self.number
print('l_range: ', l_range) print('l_range: ', l_range)
if self.number > 0: # positive 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: 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: if not tgt_layers:
self.report({'ERROR'}, f"No layers found with chosen Targets") 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: if num in current_frames:
continue continue
#Create empty frame #Create empty frame
gpl.active.frames.new(num, active=False) gpl.active.frames.new(num)
fct += 1 fct += 1
gpl.update() gpl.update()

View File

@ -155,6 +155,44 @@ def gp_stroke_to_bmesh(strokes):
### GP Drawing ### 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): def simple_draw_gp_stroke(pts, frame, width = 2, mat_id = 0):
''' '''
draw basic stroke by passing list of point 3D coordinate draw basic stroke by passing list of point 3D coordinate