
- Use the clip mask context menu for the image editor. - Remove redundant CLIP_MT_mask_handle_type_menu. - Remove "Add" items (was only in image mask context menu) as the convention is not to include these in the context menu.
437 lines
13 KiB
Python
437 lines
13 KiB
Python
# ##### BEGIN GPL LICENSE BLOCK #####
|
|
#
|
|
# This program is free software; you can redistribute it and/or
|
|
# modify it under the terms of the GNU General Public License
|
|
# as published by the Free Software Foundation; either version 2
|
|
# of the License, or (at your option) any later version.
|
|
#
|
|
# This program is distributed in the hope that it will be useful,
|
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
# GNU General Public License for more details.
|
|
#
|
|
# You should have received a copy of the GNU General Public License
|
|
# along with this program; if not, write to the Free Software Foundation,
|
|
# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
|
#
|
|
# ##### END GPL LICENSE BLOCK #####
|
|
|
|
# <pep8-80 compliant>
|
|
|
|
# panels get subclassed (not registered directly)
|
|
# menus are referenced `as is`
|
|
|
|
from bpy.types import Menu, UIList
|
|
from bpy.app.translations import contexts as i18n_contexts
|
|
|
|
|
|
# Use by both image & clip context menus.
|
|
def draw_mask_context_menu(layout, context):
|
|
layout.operator_menu_enum("mask.handle_type_set", "type")
|
|
layout.operator("mask.switch_direction")
|
|
layout.operator("mask.cyclic_toggle")
|
|
|
|
layout.separator()
|
|
layout.operator("mask.copy_splines", icon='COPYDOWN')
|
|
layout.operator("mask.paste_splines", icon='PASTEDOWN')
|
|
|
|
layout.separator()
|
|
|
|
layout.operator("mask.shape_key_rekey", text="Re-key Shape Points")
|
|
layout.operator("mask.feather_weight_clear")
|
|
layout.operator("mask.shape_key_feather_reset", text="Reset Feather Animation")
|
|
|
|
layout.separator()
|
|
|
|
layout.operator("mask.parent_set")
|
|
layout.operator("mask.parent_clear")
|
|
|
|
layout.separator()
|
|
|
|
layout.operator("mask.delete")
|
|
|
|
|
|
class MASK_UL_layers(UIList):
|
|
def draw_item(self, _context, layout, _data, item, icon,
|
|
_active_data, _active_propname, _index):
|
|
# assert(isinstance(item, bpy.types.MaskLayer)
|
|
mask = item
|
|
if self.layout_type in {'DEFAULT', 'COMPACT'}:
|
|
layout.prop(mask, "name", text="", emboss=False, icon_value=icon)
|
|
row = layout.row(align=True)
|
|
row.prop(mask, "hide", text="", emboss=False)
|
|
row.prop(mask, "hide_select", text="", emboss=False)
|
|
row.prop(mask, "hide_render", text="", emboss=False)
|
|
elif self.layout_type == 'GRID':
|
|
layout.alignment = 'CENTER'
|
|
layout.label(text="", icon_value=icon)
|
|
|
|
|
|
class MASK_PT_mask:
|
|
# subclasses must define...
|
|
# ~ bl_space_type = 'CLIP_EDITOR'
|
|
# ~ bl_region_type = 'UI'
|
|
bl_label = "Mask Settings"
|
|
bl_options = {'DEFAULT_CLOSED'}
|
|
|
|
@classmethod
|
|
def poll(cls, context):
|
|
space_data = context.space_data
|
|
return space_data.mask and space_data.mode == 'MASK'
|
|
|
|
def draw(self, context):
|
|
layout = self.layout
|
|
layout.use_property_split = True
|
|
layout.use_property_decorate = False
|
|
|
|
sc = context.space_data
|
|
mask = sc.mask
|
|
|
|
col = layout.column(align=True)
|
|
col.prop(mask, "frame_start")
|
|
col.prop(mask, "frame_end")
|
|
|
|
|
|
class MASK_PT_layers:
|
|
# subclasses must define...
|
|
# ~ bl_space_type = 'CLIP_EDITOR'
|
|
# ~ bl_region_type = 'UI'
|
|
bl_label = "Mask Layers"
|
|
|
|
@classmethod
|
|
def poll(cls, context):
|
|
space_data = context.space_data
|
|
return space_data.mask and space_data.mode == 'MASK'
|
|
|
|
def draw(self, context):
|
|
layout = self.layout
|
|
layout.use_property_split = True
|
|
layout.use_property_decorate = False
|
|
|
|
sc = context.space_data
|
|
mask = sc.mask
|
|
active_layer = mask.layers.active
|
|
|
|
rows = 4 if active_layer else 1
|
|
|
|
row = layout.row()
|
|
row.template_list("MASK_UL_layers", "", mask, "layers",
|
|
mask, "active_layer_index", rows=rows)
|
|
|
|
sub = row.column(align=True)
|
|
|
|
sub.operator("mask.layer_new", icon='ADD', text="")
|
|
sub.operator("mask.layer_remove", icon='REMOVE', text="")
|
|
|
|
if active_layer:
|
|
sub.separator()
|
|
|
|
sub.operator("mask.layer_move", icon='TRIA_UP', text="").direction = 'UP'
|
|
sub.operator("mask.layer_move", icon='TRIA_DOWN', text="").direction = 'DOWN'
|
|
|
|
# blending
|
|
row = layout.row(align=True)
|
|
row.prop(active_layer, "alpha")
|
|
row.prop(active_layer, "invert", text="", icon='IMAGE_ALPHA')
|
|
|
|
layout.prop(active_layer, "blend")
|
|
layout.prop(active_layer, "falloff")
|
|
|
|
col = layout.column()
|
|
col.prop(active_layer, "use_fill_overlap", text="Overlap")
|
|
col.prop(active_layer, "use_fill_holes", text="Holes")
|
|
|
|
|
|
class MASK_PT_spline:
|
|
# subclasses must define...
|
|
# ~ bl_space_type = 'CLIP_EDITOR'
|
|
# ~ bl_region_type = 'UI'
|
|
bl_label = "Active Spline"
|
|
|
|
@classmethod
|
|
def poll(cls, context):
|
|
sc = context.space_data
|
|
mask = sc.mask
|
|
|
|
if mask and sc.mode == 'MASK':
|
|
return mask.layers.active and mask.layers.active.splines.active
|
|
|
|
return False
|
|
|
|
def draw(self, context):
|
|
layout = self.layout
|
|
layout.use_property_split = True
|
|
layout.use_property_decorate = False
|
|
|
|
sc = context.space_data
|
|
mask = sc.mask
|
|
spline = mask.layers.active.splines.active
|
|
|
|
col = layout.column()
|
|
col.prop(spline, "offset_mode")
|
|
col.prop(spline, "weight_interpolation", text="Interpolation")
|
|
|
|
col.prop(spline, "use_cyclic")
|
|
col.prop(spline, "use_fill")
|
|
|
|
col.prop(spline, "use_self_intersection_check")
|
|
|
|
|
|
class MASK_PT_point:
|
|
# subclasses must define...
|
|
# ~ bl_space_type = 'CLIP_EDITOR'
|
|
# ~ bl_region_type = 'UI'
|
|
bl_label = "Active Point"
|
|
|
|
@classmethod
|
|
def poll(cls, context):
|
|
sc = context.space_data
|
|
mask = sc.mask
|
|
|
|
if mask and sc.mode == 'MASK':
|
|
mask_layer_active = mask.layers.active
|
|
return (mask_layer_active and
|
|
mask_layer_active.splines.active_point)
|
|
|
|
return False
|
|
|
|
def draw(self, context):
|
|
layout = self.layout
|
|
layout.use_property_split = True
|
|
layout.use_property_decorate = False
|
|
|
|
sc = context.space_data
|
|
mask = sc.mask
|
|
point = mask.layers.active.splines.active_point
|
|
parent = point.parent
|
|
|
|
col = layout.column()
|
|
# Currently only parenting the movie-clip is allowed,
|
|
# so do not over-complicate things for now by using single template_ID
|
|
#col.template_any_ID(parent, "id", "id_type", text="")
|
|
|
|
col.label(text="Parent:")
|
|
col.prop(parent, "id", text="")
|
|
|
|
if parent.id_type == 'MOVIECLIP' and parent.id:
|
|
clip = parent.id
|
|
tracking = clip.tracking
|
|
|
|
row = col.row()
|
|
row.prop(parent, "type", expand=True)
|
|
|
|
col.prop_search(parent, "parent", tracking,
|
|
"objects", icon='OBJECT_DATA', text="Object")
|
|
|
|
tracks_list = "tracks" if parent.type == 'POINT_TRACK' else "plane_tracks"
|
|
|
|
if parent.parent in tracking.objects:
|
|
ob = tracking.objects[parent.parent]
|
|
col.prop_search(parent, "sub_parent", ob,
|
|
tracks_list, icon='ANIM_DATA', text="Track")
|
|
else:
|
|
col.prop_search(parent, "sub_parent", tracking,
|
|
tracks_list, icon='ANIM_DATA', text="Track")
|
|
|
|
|
|
class MASK_PT_display:
|
|
# subclasses must define...
|
|
# ~ bl_space_type = 'CLIP_EDITOR'
|
|
# ~ bl_region_type = 'UI'
|
|
bl_label = "Mask Display"
|
|
bl_options = {'DEFAULT_CLOSED'}
|
|
|
|
@classmethod
|
|
def poll(cls, context):
|
|
space_data = context.space_data
|
|
return space_data.mask and space_data.mode == 'MASK'
|
|
|
|
def draw(self, context):
|
|
layout = self.layout
|
|
|
|
space_data = context.space_data
|
|
row = layout.row(align=True)
|
|
row.prop(space_data, "show_mask_smooth", text="Smooth")
|
|
row.prop(space_data, "mask_display_type", text="")
|
|
row = layout.row(align=True)
|
|
row.prop(space_data, "show_mask_overlay", text="Overlay")
|
|
sub = row.row()
|
|
sub.active = space_data.show_mask_overlay
|
|
sub.prop(space_data, "mask_overlay_mode", text="")
|
|
|
|
|
|
class MASK_PT_transforms:
|
|
# subclasses must define...
|
|
# ~ bl_space_type = 'CLIP_EDITOR'
|
|
# ~ bl_region_type = 'TOOLS'
|
|
bl_label = "Transforms"
|
|
bl_category = "Mask"
|
|
|
|
@classmethod
|
|
def poll(cls, context):
|
|
space_data = context.space_data
|
|
return space_data.mask and space_data.mode == 'MASK'
|
|
|
|
def draw(self, _context):
|
|
layout = self.layout
|
|
|
|
col = layout.column(align=True)
|
|
col.label(text="Transform:")
|
|
col.operator("transform.translate")
|
|
col.operator("transform.rotate")
|
|
col.operator("transform.resize", text="Scale")
|
|
col.operator("transform.transform", text="Scale Feather").mode = 'MASK_SHRINKFATTEN'
|
|
|
|
|
|
class MASK_PT_tools:
|
|
bl_label = "Mask Tools"
|
|
bl_category = "Mask"
|
|
|
|
@classmethod
|
|
def poll(cls, context):
|
|
space_data = context.space_data
|
|
return space_data.mask and space_data.mode == 'MASK'
|
|
|
|
def draw(self, _context):
|
|
layout = self.layout
|
|
|
|
col = layout.column(align=True)
|
|
col.label(text="Spline:")
|
|
col.operator("mask.delete")
|
|
col.operator("mask.cyclic_toggle")
|
|
col.operator("mask.switch_direction")
|
|
col.operator("mask.handle_type_set").type = 'VECTOR'
|
|
col.operator("mask.feather_weight_clear")
|
|
|
|
col = layout.column(align=True)
|
|
col.label(text="Parenting:")
|
|
row = col.row(align=True)
|
|
row.operator("mask.parent_set", text="Parent")
|
|
row.operator("mask.parent_clear", text="Clear")
|
|
|
|
col = layout.column(align=True)
|
|
col.label(text="Animation:")
|
|
row = col.row(align=True)
|
|
row.operator("mask.shape_key_insert", text="Insert Key")
|
|
row.operator("mask.shape_key_clear", text="Clear Key")
|
|
col.operator("mask.shape_key_feather_reset", text="Reset Feather Animation")
|
|
col.operator("mask.shape_key_rekey", text="Re-Key Shape Points")
|
|
|
|
|
|
class MASK_MT_mask(Menu):
|
|
bl_label = "Mask"
|
|
|
|
def draw(self, _context):
|
|
layout = self.layout
|
|
|
|
layout.operator("mask.delete")
|
|
|
|
layout.separator()
|
|
layout.operator("mask.cyclic_toggle")
|
|
layout.operator("mask.switch_direction")
|
|
layout.operator("mask.normals_make_consistent")
|
|
layout.operator("mask.handle_type_set")
|
|
layout.operator("mask.feather_weight_clear") # TODO, better place?
|
|
|
|
layout.separator()
|
|
layout.operator("mask.parent_clear")
|
|
layout.operator("mask.parent_set")
|
|
|
|
layout.separator()
|
|
layout.operator("mask.copy_splines")
|
|
layout.operator("mask.paste_splines")
|
|
|
|
layout.separator()
|
|
layout.menu("MASK_MT_visibility")
|
|
layout.menu("MASK_MT_transform")
|
|
layout.menu("MASK_MT_animation")
|
|
|
|
|
|
class MASK_MT_add(Menu):
|
|
bl_idname = "MASK_MT_add"
|
|
bl_label = "Add"
|
|
bl_translation_context = i18n_contexts.operator_default
|
|
|
|
def draw(self, _context):
|
|
layout = self.layout
|
|
|
|
layout.operator_context = 'INVOKE_REGION_WIN'
|
|
layout.operator("mask.primitive_circle_add", text="Circle", icon='MESH_CIRCLE')
|
|
layout.operator("mask.primitive_square_add", text="Square", icon='MESH_PLANE')
|
|
|
|
|
|
class MASK_MT_visibility(Menu):
|
|
bl_label = "Show/Hide"
|
|
|
|
def draw(self, _context):
|
|
layout = self.layout
|
|
|
|
layout.operator("mask.hide_view_clear")
|
|
layout.operator("mask.hide_view_set", text="Hide Selected").unselected = False
|
|
layout.operator("mask.hide_view_set", text="Hide Unselected").unselected = True
|
|
|
|
|
|
class MASK_MT_transform(Menu):
|
|
bl_label = "Transform"
|
|
|
|
def draw(self, _context):
|
|
layout = self.layout
|
|
|
|
layout.operator("transform.translate")
|
|
layout.operator("transform.rotate")
|
|
layout.operator("transform.resize")
|
|
layout.operator("transform.transform", text="Scale Feather").mode = 'MASK_SHRINKFATTEN'
|
|
|
|
|
|
class MASK_MT_animation(Menu):
|
|
bl_label = "Animation"
|
|
|
|
def draw(self, _context):
|
|
layout = self.layout
|
|
|
|
layout.operator("mask.shape_key_clear")
|
|
layout.operator("mask.shape_key_insert")
|
|
layout.operator("mask.shape_key_feather_reset")
|
|
layout.operator("mask.shape_key_rekey")
|
|
|
|
|
|
class MASK_MT_select(Menu):
|
|
bl_label = "Select"
|
|
|
|
def draw(self, _context):
|
|
layout = self.layout
|
|
|
|
layout.operator("mask.select_all", text="All").action = 'SELECT'
|
|
layout.operator("mask.select_all", text="None").action = 'DESELECT'
|
|
layout.operator("mask.select_all", text="Invert").action = 'INVERT'
|
|
|
|
layout.separator()
|
|
|
|
layout.operator("mask.select_box")
|
|
layout.operator("mask.select_circle")
|
|
|
|
layout.separator()
|
|
|
|
layout.operator("mask.select_more")
|
|
layout.operator("mask.select_less")
|
|
|
|
layout.separator()
|
|
|
|
layout.operator("mask.select_linked", text="Select Linked")
|
|
|
|
|
|
classes = (
|
|
MASK_UL_layers,
|
|
MASK_MT_mask,
|
|
MASK_MT_add,
|
|
MASK_MT_visibility,
|
|
MASK_MT_transform,
|
|
MASK_MT_animation,
|
|
MASK_MT_select,
|
|
)
|
|
|
|
if __name__ == "__main__": # only for live edit.
|
|
from bpy.utils import register_class
|
|
for cls in classes:
|
|
register_class(cls)
|