layer group - named regex - fill tool mat picker
1.6.0 - feat: Namespace upgrade - support pseudo group naming - add group and indent button - feat: Fill tool shortcut for material color picker (from closest stroke) - `S` : get material closest *fill* stroke - `Àlt+S` : get closest stroke (fill or stroke)gpv2
parent
7ff96ad205
commit
3974a15ff0
|
@ -1,5 +1,14 @@
|
||||||
# Changelog
|
# Changelog
|
||||||
|
|
||||||
|
1.6.0
|
||||||
|
|
||||||
|
- feat: Namespace upgrade
|
||||||
|
- support pseudo group naming
|
||||||
|
- add group and indent button
|
||||||
|
- feat: Fill tool shortcut for material color picker (from closest stroke)
|
||||||
|
- `S` : get material closest *fill* stroke
|
||||||
|
- `Àlt+S` : get closest stroke (fill or stroke)
|
||||||
|
|
||||||
1.5.8
|
1.5.8
|
||||||
|
|
||||||
- feat: Namespace improvement:
|
- feat: Namespace improvement:
|
||||||
|
|
|
@ -9,19 +9,71 @@ from .utils import get_addon_prefs
|
||||||
|
|
||||||
# --- OPS ---
|
# --- OPS ---
|
||||||
|
|
||||||
# pattern = r'([A-Z]{2})?_?([A-Z]{2})?_?(.*)' # bad ! match whithout separator
|
# PATTERN = r'([A-Z]{2})?_?([A-Z]{2})?_?(.*)' # bad ! match whithout separator
|
||||||
# pattern = r'(?:(^[A-Z]{2})_)?(?:([A-Z]{2})_)?(.*)' # matching only two letter
|
# pattern = r'(?:(^[A-Z]{2})_)?(?:([A-Z]{2})_)?(.*)' # matching only two letter
|
||||||
# pattern = r'^([A-Z]{2}_)?([A-Z]{2}_)?(.*)' # matching letters with separator
|
# pattern = r'^([A-Z]{2}_)?([A-Z]{2}_)?(.*)' # matching letters with separator
|
||||||
|
|
||||||
# pattern = r'^([A-Z]{1,6}_)?([A-Z]{1,6}_)?(.*)' # matching capital letters from one to six
|
# pattern = r'^([A-Z]{1,6}_)?([A-Z]{1,6}_)?(.*)' # matching capital letters from one to six
|
||||||
pattern = r'^([A-Z]{1,6}_)?([A-Z]{1,6}_)?(.*?)(_[A-Z]{2})?$' # 2 letter suffix
|
# pattern = r'^([A-Z]{1,6}_)?([A-Z]{1,6}_)?(.*?)(_[A-Z]{2})?$' # 2 letter suffix
|
||||||
|
# pattern = r'^(?P<tag>[A-Z]{1,6}_)?(?P<tag2>[A-Z]{1,6}_)?(?P<name>.*?)(?P<sfix>_[A-Z]{2})?$' # named
|
||||||
|
# pattern = r'^(?P<grp>-\s)?(?P<tag>[A-Z]{2}_)?(?P<tag2>[A-Z]{1,6}_)?(?P<name>.*?)(?P<sfix>_[A-Z]{2})?$' # group start ' - '
|
||||||
|
# PATTERN = r'^(?P<grp>-\s)?(?P<tag>[A-Z]{2}_)?(?P<tag2>[A-Z]{1,6}_)?(?P<name>.*?)(?P<sfix>_[A-Z]{2})?(?P<inc>\.\d{3})?$' # numering
|
||||||
|
PATTERN = r'^(?P<grp>-\s)?(?P<tag>[A-Z]{2}_)?(?P<name>.*?)(?P<sfix>_[A-Z]{2})?(?P<inc>\.\d{3})?$' # numering
|
||||||
|
|
||||||
|
|
||||||
|
def layer_name_build(layer, prefix='', desc='', suffix=''):
|
||||||
|
'''GET a layer and argumen to build and assign name'''
|
||||||
|
|
||||||
|
|
||||||
|
prefs = get_addon_prefs()
|
||||||
|
sep = prefs.separator
|
||||||
|
name = layer.info
|
||||||
|
|
||||||
|
pattern = PATTERN.replace('_', sep) # set separator
|
||||||
|
|
||||||
|
res = re.search(pattern, name.strip())
|
||||||
|
|
||||||
|
# prefix -> tag
|
||||||
|
# prefix2 -> tag2
|
||||||
|
# desc -> name
|
||||||
|
# suffix -> sfix
|
||||||
|
grp = '' if res.group('grp') is None else res.group('grp')
|
||||||
|
tag = '' if res.group('tag') is None else res.group('tag')
|
||||||
|
# tag2 = '' if res.group('tag2') is None else res.group('tag2')
|
||||||
|
name = '' if res.group('name') is None else res.group('name')
|
||||||
|
sfix = '' if res.group('sfix') is None else res.group('sfix')
|
||||||
|
inc = '' if res.group('inc') is None else res.group('inc')
|
||||||
|
|
||||||
|
if grp:
|
||||||
|
grp = ' ' + grp # name is strip(), so grp first spaces are gones.
|
||||||
|
|
||||||
|
if prefix:
|
||||||
|
if prefix == 'prefixkillcode':
|
||||||
|
tag = ''
|
||||||
|
else:
|
||||||
|
tag = prefix.upper().strip() + sep
|
||||||
|
# if prefix2:
|
||||||
|
# tag2 = prefix2.upper().strip() + sep
|
||||||
|
if desc:
|
||||||
|
name = desc
|
||||||
|
|
||||||
|
if suffix:
|
||||||
|
if suffix == 'suffixkillcode':
|
||||||
|
sfix = ''
|
||||||
|
else:
|
||||||
|
sfix = sep + suffix.upper().strip()
|
||||||
|
|
||||||
|
# check if name is available without the increment ending
|
||||||
|
new = f'{grp}{tag}{name}{sfix}'
|
||||||
|
|
||||||
|
layer.info = new
|
||||||
|
|
||||||
|
"""
|
||||||
def layer_name_build(layer, prefix='', prefix2='', desc='', suffix=''):
|
def layer_name_build(layer, prefix='', prefix2='', desc='', suffix=''):
|
||||||
'''GET a layer and infos to build name
|
'''GET a layer and infos to build name
|
||||||
Can take one or two prefix and description/name of the layer)
|
Can take one or two prefix and description/name of the layer)
|
||||||
'''
|
'''
|
||||||
|
|
||||||
global pattern
|
|
||||||
|
|
||||||
prefs = get_addon_prefs()
|
prefs = get_addon_prefs()
|
||||||
sep = prefs.separator
|
sep = prefs.separator
|
||||||
|
@ -55,6 +107,7 @@ def layer_name_build(layer, prefix='', prefix2='', desc='', suffix=''):
|
||||||
|
|
||||||
new = f'{p1}{p2}{p3}{p4}'
|
new = f'{p1}{p2}{p3}{p4}'
|
||||||
layer.info = new
|
layer.info = new
|
||||||
|
"""
|
||||||
|
|
||||||
## multi-prefix solution (Caps letters)
|
## multi-prefix solution (Caps letters)
|
||||||
class GPTB_OT_layer_name_build(Operator):
|
class GPTB_OT_layer_name_build(Operator):
|
||||||
|
@ -68,7 +121,7 @@ class GPTB_OT_layer_name_build(Operator):
|
||||||
return True
|
return True
|
||||||
|
|
||||||
prefix : StringProperty(default='', options={'SKIP_SAVE'})
|
prefix : StringProperty(default='', options={'SKIP_SAVE'})
|
||||||
prefix2 : StringProperty(default='', options={'SKIP_SAVE'})
|
# prefix2 : StringProperty(default='', options={'SKIP_SAVE'})
|
||||||
desc : StringProperty(default='', options={'SKIP_SAVE'})
|
desc : StringProperty(default='', options={'SKIP_SAVE'})
|
||||||
suffix : StringProperty(default='', options={'SKIP_SAVE'})
|
suffix : StringProperty(default='', options={'SKIP_SAVE'})
|
||||||
|
|
||||||
|
@ -82,7 +135,69 @@ class GPTB_OT_layer_name_build(Operator):
|
||||||
|
|
||||||
for l in gpl:
|
for l in gpl:
|
||||||
if l.select or l == act:
|
if l.select or l == act:
|
||||||
layer_name_build(l, prefix=self.prefix, prefix2=self.prefix2, desc=self.desc, suffix=self.suffix)
|
layer_name_build(l, prefix=self.prefix, desc=self.desc, suffix=self.suffix)
|
||||||
|
return {"FINISHED"}
|
||||||
|
|
||||||
|
|
||||||
|
def grp_toggle(l):
|
||||||
|
grp_item_id = ' - '
|
||||||
|
res = re.search(r'^(\s{1,3}-\s{0,3})(.*)', l.info)
|
||||||
|
if not res:
|
||||||
|
# add group prefix after stripping all space and dash
|
||||||
|
l.info = grp_item_id + l.info.lstrip(' -')
|
||||||
|
else:
|
||||||
|
# delete group prefix
|
||||||
|
l.info = res.group(2)
|
||||||
|
|
||||||
|
class GPTB_OT_layer_group_toggle(Operator):
|
||||||
|
bl_idname = "gp.layer_group_toggle"
|
||||||
|
bl_label = "Group Toggle"
|
||||||
|
bl_description = "Group or ungroup a layer"
|
||||||
|
bl_options = {"REGISTER", "UNDO"}
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def poll(cls, context):
|
||||||
|
return True
|
||||||
|
|
||||||
|
# group : StringProperty(default='', options={'SKIP_SAVE'})
|
||||||
|
|
||||||
|
def execute(self, context):
|
||||||
|
ob = context.object
|
||||||
|
gpl = ob.data.layers
|
||||||
|
act = gpl.active
|
||||||
|
if not act:
|
||||||
|
self.report({'ERROR'}, 'no layer active')
|
||||||
|
return {"CANCELLED"}
|
||||||
|
for l in gpl:
|
||||||
|
if l.select or l == act:
|
||||||
|
grp_toggle(l)
|
||||||
|
return {"FINISHED"}
|
||||||
|
|
||||||
|
class GPTB_OT_layer_new_group(Operator):
|
||||||
|
bl_idname = "gp.layer_new_group"
|
||||||
|
bl_label = "New Group"
|
||||||
|
bl_description = "Create a group from active layer"
|
||||||
|
bl_options = {"REGISTER", "UNDO"}
|
||||||
|
|
||||||
|
def execute(self, context):
|
||||||
|
ob = context.object
|
||||||
|
gpl = ob.data.layers
|
||||||
|
act = gpl.active
|
||||||
|
if not act:
|
||||||
|
self.report({'ERROR'}, 'no layer active')
|
||||||
|
return {"CANCELLED"}
|
||||||
|
res = re.search(PATTERN, act.info)
|
||||||
|
if not res:
|
||||||
|
self.report({'ERROR'}, 'Could not create a group name, create a layer manually')
|
||||||
|
return {"CANCELLED"}
|
||||||
|
name = res.group('name')
|
||||||
|
if not name:
|
||||||
|
self.report({'ERROR'}, f'No name found in {act.info}')
|
||||||
|
return {"CANCELLED"}
|
||||||
|
if name in [l.info for l in gpl]:
|
||||||
|
self.report({'WARNING'}, f'Name already exists: {act.info}')
|
||||||
|
return {"FINISHED"}
|
||||||
|
gpl.new(name, set_active=False)
|
||||||
return {"FINISHED"}
|
return {"FINISHED"}
|
||||||
|
|
||||||
|
|
||||||
|
@ -120,13 +235,12 @@ class GPTB_OT_select_set_same_prefix(Operator):
|
||||||
return self.execute(context)
|
return self.execute(context)
|
||||||
|
|
||||||
def execute(self, context):
|
def execute(self, context):
|
||||||
global pattern
|
|
||||||
prefs = get_addon_prefs()
|
prefs = get_addon_prefs()
|
||||||
sep = prefs.separator # '_'
|
sep = prefs.separator # '_'
|
||||||
gpl = context.object.data.layers
|
gpl = context.object.data.layers
|
||||||
act = gpl.active
|
act = gpl.active
|
||||||
|
|
||||||
res = re.search(pattern, act.info)
|
res = re.search(PATTERN, act.info)
|
||||||
if not res:
|
if not res:
|
||||||
# self.report({'ERROR'}, f'Error scanning {act.info}')
|
# self.report({'ERROR'}, f'Error scanning {act.info}')
|
||||||
return {"CANCELLED"}
|
return {"CANCELLED"}
|
||||||
|
@ -288,7 +402,7 @@ class GPTB_OT_rename_gp_layer(Operator):
|
||||||
|
|
||||||
## --- UI layer panel---
|
## --- UI layer panel---
|
||||||
|
|
||||||
def layer_name_builder(self, context):
|
def layer_name_builder_ui(self, context):
|
||||||
'''appended to DATA_PT_gpencil_layers'''
|
'''appended to DATA_PT_gpencil_layers'''
|
||||||
|
|
||||||
prefs = get_addon_prefs()
|
prefs = get_addon_prefs()
|
||||||
|
@ -319,6 +433,8 @@ def layer_name_builder(self, context):
|
||||||
## name (description)
|
## name (description)
|
||||||
row = col.row(align=True)
|
row = col.row(align=True)
|
||||||
row.prop(context.scene.gptoolprops, 'layer_name', text='')
|
row.prop(context.scene.gptoolprops, 'layer_name', text='')
|
||||||
|
row.operator("gp.layer_new_group", text='', icon='COLLECTION_NEW')
|
||||||
|
row.operator("gp.layer_group_toggle", text='', icon='OUTLINER_OB_GROUP_INSTANCE')
|
||||||
## no need for desc ops, already trigerred from update
|
## no need for desc ops, already trigerred from update
|
||||||
# row.operator("gp.layer_name_build", text='', icon='EVENT_RETURN').desc = context.scene.gptoolprops.layer_name
|
# row.operator("gp.layer_name_build", text='', icon='EVENT_RETURN').desc = context.scene.gptoolprops.layer_name
|
||||||
|
|
||||||
|
@ -330,6 +446,8 @@ def layer_name_builder(self, context):
|
||||||
row.operator("gp.layer_name_build", text='', icon='X').suffix = 'suffixkillcode'
|
row.operator("gp.layer_name_build", text='', icon='X').suffix = 'suffixkillcode'
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## --- UI dopesheet ---
|
## --- UI dopesheet ---
|
||||||
|
|
||||||
def gpencil_dopesheet_header(self, context):
|
def gpencil_dopesheet_header(self, context):
|
||||||
|
@ -360,13 +478,16 @@ def obj_layer_name_callback():
|
||||||
return
|
return
|
||||||
if not ob.data.layers.active:
|
if not ob.data.layers.active:
|
||||||
return
|
return
|
||||||
pattern = r'^([A-Z]{1,6}_)?([A-Z]{1,6}_)?(.*?)(_[A-Z]{2})?$'
|
|
||||||
res = re.search(pattern, ob.data.layers.active.info)
|
for l in ob.data.layers:
|
||||||
|
l.select = l == ob.data.layers.active
|
||||||
|
|
||||||
|
res = re.search(PATTERN, ob.data.layers.active.info)
|
||||||
if not res:
|
if not res:
|
||||||
return
|
return
|
||||||
if not res.group(3):
|
if not res.group('name'):
|
||||||
return
|
return
|
||||||
bpy.context.scene.gptoolprops['layer_name'] = res.group(3)
|
bpy.context.scene.gptoolprops['layer_name'] = res.group('name')
|
||||||
|
|
||||||
@persistent
|
@persistent
|
||||||
def subscribe_handler(dummy):
|
def subscribe_handler(dummy):
|
||||||
|
@ -387,6 +508,8 @@ def subscribe_handler(dummy):
|
||||||
classes=(
|
classes=(
|
||||||
GPTB_OT_rename_gp_layer,
|
GPTB_OT_rename_gp_layer,
|
||||||
GPTB_OT_layer_name_build,
|
GPTB_OT_layer_name_build,
|
||||||
|
GPTB_OT_layer_group_toggle,
|
||||||
|
GPTB_OT_layer_new_group,
|
||||||
GPTB_OT_select_set_same_prefix,
|
GPTB_OT_select_set_same_prefix,
|
||||||
GPTB_OT_select_set_same_color,
|
GPTB_OT_select_set_same_color,
|
||||||
)
|
)
|
||||||
|
@ -395,7 +518,7 @@ def register():
|
||||||
for cls in classes:
|
for cls in classes:
|
||||||
bpy.utils.register_class(cls)
|
bpy.utils.register_class(cls)
|
||||||
|
|
||||||
bpy.types.DATA_PT_gpencil_layers.prepend(layer_name_builder)
|
bpy.types.DATA_PT_gpencil_layers.prepend(layer_name_builder_ui)
|
||||||
bpy.types.DOPESHEET_HT_header.append(gpencil_dopesheet_header)
|
bpy.types.DOPESHEET_HT_header.append(gpencil_dopesheet_header)
|
||||||
bpy.types.GPENCIL_MT_layer_context_menu.append(gpencil_layer_dropdown_menu)
|
bpy.types.GPENCIL_MT_layer_context_menu.append(gpencil_layer_dropdown_menu)
|
||||||
bpy.app.handlers.load_post.append(subscribe_handler) # need to restart after first activation
|
bpy.app.handlers.load_post.append(subscribe_handler) # need to restart after first activation
|
||||||
|
@ -404,7 +527,7 @@ def unregister():
|
||||||
bpy.app.handlers.load_post.remove(subscribe_handler)
|
bpy.app.handlers.load_post.remove(subscribe_handler)
|
||||||
bpy.types.GPENCIL_MT_layer_context_menu.remove(gpencil_layer_dropdown_menu)
|
bpy.types.GPENCIL_MT_layer_context_menu.remove(gpencil_layer_dropdown_menu)
|
||||||
bpy.types.DOPESHEET_HT_header.remove(gpencil_dopesheet_header)
|
bpy.types.DOPESHEET_HT_header.remove(gpencil_dopesheet_header)
|
||||||
bpy.types.DATA_PT_gpencil_layers.remove(layer_name_builder)
|
bpy.types.DATA_PT_gpencil_layers.remove(layer_name_builder_ui)
|
||||||
|
|
||||||
for cls in reversed(classes):
|
for cls in reversed(classes):
|
||||||
bpy.utils.unregister_class(cls)
|
bpy.utils.unregister_class(cls)
|
||||||
|
|
|
@ -0,0 +1,202 @@
|
||||||
|
import bpy
|
||||||
|
from bpy.types import Operator
|
||||||
|
import mathutils
|
||||||
|
from mathutils import Vector, Matrix, geometry
|
||||||
|
from bpy_extras import view3d_utils
|
||||||
|
from .utils import get_gp_draw_plane, location_to_region, region_to_location
|
||||||
|
|
||||||
|
### passing by 2D projection
|
||||||
|
def get_3d_coord_on_drawing_plane_from_2d(context, co):
|
||||||
|
plane_co, plane_no = get_gp_draw_plane(context)
|
||||||
|
rv3d = context.region_data
|
||||||
|
view_mat = rv3d.view_matrix.inverted()
|
||||||
|
if not plane_no:
|
||||||
|
plane_no = Vector((0,0,1))
|
||||||
|
plane_no.rotate(view_mat)
|
||||||
|
depth_3d = view_mat @ Vector((0, 0, -1000))
|
||||||
|
org = region_to_location(co, view_mat.to_translation())
|
||||||
|
view_point = region_to_location(co, depth_3d)
|
||||||
|
hit = geometry.intersect_line_plane(org, view_point, plane_co, plane_no)
|
||||||
|
|
||||||
|
if hit and plane_no:
|
||||||
|
return context.object, hit, plane_no
|
||||||
|
|
||||||
|
return None, None, None
|
||||||
|
|
||||||
|
class GP_OT_pick_closest_material(Operator):
|
||||||
|
bl_idname = "gp.pick_closest_material"
|
||||||
|
bl_label = "Get Closest Stroke Material"
|
||||||
|
bl_description = "Pick closest stroke material"
|
||||||
|
bl_options = {"REGISTER", "UNDO"}
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def poll(cls, context):
|
||||||
|
return context.object and context.object.type == 'GPENCIL' and context.mode == 'PAINT_GPENCIL'
|
||||||
|
|
||||||
|
fill_only : bpy.props.BoolProperty(default=False, options={'SKIP_SAVE'})
|
||||||
|
|
||||||
|
def filter_stroke(self, context):
|
||||||
|
# get stroke under mouse using kdtree
|
||||||
|
point_pair = [(p.co, s) for s in self.stroke_list for p in s.points] # local space
|
||||||
|
|
||||||
|
kd = mathutils.kdtree.KDTree(len(point_pair))
|
||||||
|
for i, pair in enumerate(point_pair):
|
||||||
|
kd.insert(pair[0], i)
|
||||||
|
kd.balance()
|
||||||
|
|
||||||
|
## Get 3D coordinate on drawing plane according to mouse 2d.co on flat 2d drawing
|
||||||
|
_ob, hit, _plane_no = get_3d_coord_on_drawing_plane_from_2d(context, self.init_mouse)
|
||||||
|
|
||||||
|
if not hit:
|
||||||
|
return 'No hit on drawing plane', None
|
||||||
|
|
||||||
|
mouse_3d = hit
|
||||||
|
mouse_local = self.inv_mat @ mouse_3d # local space
|
||||||
|
co, index, _dist = kd.find(mouse_local) # local space
|
||||||
|
# co, index, _dist = kd.find(mouse_3d) # world space
|
||||||
|
# context.scene.cursor.location = co # world space
|
||||||
|
s = point_pair[index][1]
|
||||||
|
|
||||||
|
## find point index in stroke
|
||||||
|
self.idx = None
|
||||||
|
for i, p in enumerate(s.points):
|
||||||
|
if p.co == co:
|
||||||
|
self.idx = i
|
||||||
|
break
|
||||||
|
|
||||||
|
del point_pair
|
||||||
|
return s, self.ob.matrix_world @ co
|
||||||
|
|
||||||
|
def invoke(self, context, event):
|
||||||
|
# self.prefs = get_addon_prefs()
|
||||||
|
self.ob = context.object
|
||||||
|
self.gp = self.ob.data
|
||||||
|
|
||||||
|
self.stroke_list = []
|
||||||
|
self.inv_mat = self.ob.matrix_world.inverted()
|
||||||
|
|
||||||
|
if self.gp.use_multiedit:
|
||||||
|
for l in self.gp.layers:
|
||||||
|
if l.lock or l.hide:
|
||||||
|
continue
|
||||||
|
for f in l.frames:
|
||||||
|
if not f.select:
|
||||||
|
continue
|
||||||
|
for s in f.strokes:
|
||||||
|
self.stroke_list.append(s)
|
||||||
|
|
||||||
|
else:
|
||||||
|
# [s for l in self.gp.layers if not l.lock and not l.hide for s in l.active_frame.stokes]
|
||||||
|
for l in self.gp.layers:
|
||||||
|
if l.lock or l.hide or not l.active_frame:
|
||||||
|
continue
|
||||||
|
for s in l.active_frame.strokes:
|
||||||
|
self.stroke_list.append(s)
|
||||||
|
|
||||||
|
if self.fill_only:
|
||||||
|
self.stroke_list = [s for s in self.stroke_list if self.ob.data.materials[s.material_index].grease_pencil.show_fill]
|
||||||
|
|
||||||
|
if not self.stroke_list:
|
||||||
|
self.report({'ERROR'}, 'No stroke found, maybe layers are locked or hidden')
|
||||||
|
return {'CANCELLED'}
|
||||||
|
|
||||||
|
|
||||||
|
self.init_mouse = Vector((event.mouse_region_x, event.mouse_region_y))
|
||||||
|
self.stroke, self.coord = self.filter_stroke(context)
|
||||||
|
if isinstance(self.stroke, str):
|
||||||
|
self.report({'ERROR'}, self.stroke)
|
||||||
|
return {'CANCELLED'}
|
||||||
|
|
||||||
|
|
||||||
|
del self.stroke_list
|
||||||
|
|
||||||
|
if self.idx is None:
|
||||||
|
self.report({'WARNING'}, 'No coord found')
|
||||||
|
return {'CANCELLED'}
|
||||||
|
|
||||||
|
self.depth = self.ob.matrix_world @ self.stroke.points[self.idx].co
|
||||||
|
self.init_pos = [p.co.copy() for p in self.stroke.points] # need a copy otherwise vector is updated
|
||||||
|
## directly use world position ?
|
||||||
|
# self.pos_world = [self.ob.matrix_world @ co for co in self.init_pos]
|
||||||
|
self.pos_2d = [location_to_region(self.ob.matrix_world @ co) for co in self.init_pos]
|
||||||
|
self.plen = len(self.stroke.points)
|
||||||
|
|
||||||
|
# context.scene.cursor.location = self.coord #Dbg
|
||||||
|
return self.execute(context)
|
||||||
|
# context.window_manager.modal_handler_add(self)
|
||||||
|
# return {'RUNNING_MODAL'}
|
||||||
|
|
||||||
|
def execute(self, context):
|
||||||
|
self.ob.active_material_index = self.stroke.material_index
|
||||||
|
self.report({'INFO'}, f'Mat: {self.ob.data.materials[self.stroke.material_index].name}')
|
||||||
|
return {'FINISHED'}
|
||||||
|
|
||||||
|
# def modal(self, context, event):
|
||||||
|
# if event.type == 'MOUSEMOVE':
|
||||||
|
# mouse = Vector((event.mouse_region_x, event.mouse_region_y))
|
||||||
|
# delta = mouse - self.init_mouse
|
||||||
|
|
||||||
|
# if event.type == 'LEFTMOUSE' and event.value == 'RELEASE':
|
||||||
|
# print(f'{self.stroke}, points num {len(self.stroke.points)}, material index:{self.stroke.material_index}')
|
||||||
|
# return {'FINISHED'}
|
||||||
|
|
||||||
|
# if event.type in {'RIGHTMOUSE', 'ESC'}:
|
||||||
|
# # for i, p in enumerate(self.stroke.points): # reset position
|
||||||
|
# # self.stroke.points[i].co = self.init_pos[i]
|
||||||
|
# context.area.tag_redraw()
|
||||||
|
# return {'CANCELLED'}
|
||||||
|
|
||||||
|
# return {'RUNNING_MODAL'}
|
||||||
|
|
||||||
|
|
||||||
|
addon_keymaps = []
|
||||||
|
def register_keymaps():
|
||||||
|
addon = bpy.context.window_manager.keyconfigs.addon
|
||||||
|
# km = addon.keymaps.new(name = "Grease Pencil Stroke Paint (Draw brush)", space_type = "EMPTY", region_type='WINDOW')
|
||||||
|
# km = addon.keymaps.new(name = "Grease Pencil Stroke Paint Mode", space_type = "EMPTY", region_type='WINDOW')
|
||||||
|
km = addon.keymaps.new(name = "Grease Pencil Stroke Paint (Fill)", space_type = "EMPTY", region_type='WINDOW')
|
||||||
|
kmi = km.keymap_items.new(
|
||||||
|
# name="",
|
||||||
|
idname="gp.pick_closest_material",
|
||||||
|
type="S", # type="LEFTMOUSE",
|
||||||
|
value="PRESS",
|
||||||
|
shift=False,
|
||||||
|
ctrl=False,
|
||||||
|
alt = False,
|
||||||
|
oskey=False,
|
||||||
|
# key_modifier='S', # S like Sample
|
||||||
|
)
|
||||||
|
kmi.properties.fill_only = True
|
||||||
|
addon_keymaps.append((km, kmi))
|
||||||
|
|
||||||
|
kmi = km.keymap_items.new(
|
||||||
|
# name="",
|
||||||
|
idname="gp.pick_closest_material",
|
||||||
|
type="S", # type="LEFTMOUSE",
|
||||||
|
value="PRESS",
|
||||||
|
alt = True,
|
||||||
|
# key_modifier='S', # S like Sample
|
||||||
|
)
|
||||||
|
|
||||||
|
# kmi = km.keymap_items.new('catname.opsname', type='F5', value='PRESS')
|
||||||
|
addon_keymaps.append((km, kmi))
|
||||||
|
|
||||||
|
def unregister_keymaps():
|
||||||
|
for km, kmi in addon_keymaps:
|
||||||
|
km.keymap_items.remove(kmi)
|
||||||
|
addon_keymaps.clear()
|
||||||
|
|
||||||
|
|
||||||
|
classes=(
|
||||||
|
GP_OT_pick_closest_material,
|
||||||
|
)
|
||||||
|
|
||||||
|
def register():
|
||||||
|
for cls in classes:
|
||||||
|
bpy.utils.register_class(cls)
|
||||||
|
register_keymaps()
|
||||||
|
|
||||||
|
def unregister():
|
||||||
|
unregister_keymaps()
|
||||||
|
for cls in reversed(classes):
|
||||||
|
bpy.utils.unregister_class(cls)
|
|
@ -15,7 +15,7 @@ bl_info = {
|
||||||
"name": "GP toolbox",
|
"name": "GP toolbox",
|
||||||
"description": "Set of tools for Grease Pencil in animation production",
|
"description": "Set of tools for Grease Pencil in animation production",
|
||||||
"author": "Samuel Bernou, Christophe Seux",
|
"author": "Samuel Bernou, Christophe Seux",
|
||||||
"version": (1, 5, 7),
|
"version": (1, 6, 0),
|
||||||
"blender": (2, 91, 0),
|
"blender": (2, 91, 0),
|
||||||
"location": "Sidebar (N menu) > Gpencil > Toolbox / Gpencil properties",
|
"location": "Sidebar (N menu) > Gpencil > Toolbox / Gpencil properties",
|
||||||
"warning": "",
|
"warning": "",
|
||||||
|
@ -49,6 +49,7 @@ from . import OP_realign
|
||||||
from . import OP_depth_move
|
from . import OP_depth_move
|
||||||
from . import OP_key_duplicate_send
|
from . import OP_key_duplicate_send
|
||||||
from . import OP_layer_manager
|
from . import OP_layer_manager
|
||||||
|
from . import OP_material_picker
|
||||||
from . import OP_eraser_brush
|
from . import OP_eraser_brush
|
||||||
from . import TOOL_eraser_brush
|
from . import TOOL_eraser_brush
|
||||||
from . import handler_draw_cam
|
from . import handler_draw_cam
|
||||||
|
@ -564,6 +565,7 @@ def register():
|
||||||
OP_key_duplicate_send.register()
|
OP_key_duplicate_send.register()
|
||||||
OP_layer_manager.register()
|
OP_layer_manager.register()
|
||||||
OP_eraser_brush.register()
|
OP_eraser_brush.register()
|
||||||
|
OP_material_picker.register()
|
||||||
TOOL_eraser_brush.register()
|
TOOL_eraser_brush.register()
|
||||||
handler_draw_cam.register()
|
handler_draw_cam.register()
|
||||||
UI_tools.register()
|
UI_tools.register()
|
||||||
|
@ -589,6 +591,7 @@ def unregister():
|
||||||
bpy.utils.unregister_class(cls)
|
bpy.utils.unregister_class(cls)
|
||||||
UI_tools.unregister()
|
UI_tools.unregister()
|
||||||
handler_draw_cam.unregister()
|
handler_draw_cam.unregister()
|
||||||
|
OP_material_picker.unregister()
|
||||||
OP_eraser_brush.unregister()
|
OP_eraser_brush.unregister()
|
||||||
TOOL_eraser_brush.unregister()
|
TOOL_eraser_brush.unregister()
|
||||||
OP_layer_manager.unregister()
|
OP_layer_manager.unregister()
|
||||||
|
|
Loading…
Reference in New Issue