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 [(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
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'),
('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()

View File

@ -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