material pick with quick press and add enum filter
1.6.9 - change: material picker (`S` and `Alt+S`) quick trigger, cahnge is only triggered if ley is pressed less than 200ms - this is made to let other operator use functionality on long press using `S` - feat: material picker shortcut has now an enum choice to filter targeted stroke (fill, stroke, all) - by default `S` is still fill only - but `Alt+S` is now stroke only instead of allgpv2
parent
d4ea39fb40
commit
8b838b52ca
|
@ -1,5 +1,13 @@
|
|||
# Changelog
|
||||
|
||||
1.6.9
|
||||
|
||||
- change: material picker (`S` and `Alt+S`) quick trigger, cahnge is only triggered if ley is pressed less than 200ms
|
||||
- this is made to let other operator use functionality on long press using `S`
|
||||
- feat: material picker shortcut has now an enum choice to filter targeted stroke (fill, stroke, all)
|
||||
- by default `S` is still fill only
|
||||
- but `Alt+S` is now stroke only instead of all
|
||||
|
||||
1.6.8
|
||||
|
||||
- fix: reproject GP repeating projection on same frames
|
||||
|
|
|
@ -3,6 +3,7 @@ from bpy.types import Operator
|
|||
import mathutils
|
||||
from mathutils import Vector, Matrix, geometry
|
||||
from bpy_extras import view3d_utils
|
||||
from time import time
|
||||
from .utils import get_gp_draw_plane, location_to_region, region_to_location
|
||||
|
||||
### passing by 2D projection
|
||||
|
@ -23,6 +24,7 @@ def get_3d_coord_on_drawing_plane_from_2d(context, co):
|
|||
|
||||
return None, None, None
|
||||
|
||||
"""
|
||||
class GP_OT_pick_closest_material(Operator):
|
||||
bl_idname = "gp.pick_closest_material"
|
||||
bl_label = "Get Closest Stroke Material"
|
||||
|
@ -147,6 +149,142 @@ class GP_OT_pick_closest_material(Operator):
|
|||
# return {'CANCELLED'}
|
||||
|
||||
# return {'RUNNING_MODAL'}
|
||||
"""
|
||||
|
||||
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'})
|
||||
stroke_filter : bpy.props.EnumProperty(default='FILL',
|
||||
items=(
|
||||
('FILL', 'Fill', 'Target only Fill materials', 0),
|
||||
('STROKE', 'Stroke', 'Target only Stroke materials', 1),
|
||||
('ALL', 'All', 'All material', 2),
|
||||
),
|
||||
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.t0 = time()
|
||||
self.limit = self.t0 + 0.2 # 200 miliseconds
|
||||
self.init_mouse = Vector((event.mouse_region_x, event.mouse_region_y))
|
||||
self.idx = None
|
||||
context.window_manager.modal_handler_add(self)
|
||||
return {'RUNNING_MODAL'}
|
||||
|
||||
def modal(self, context, event):
|
||||
if time() > self.limit:
|
||||
return {'CANCELLED'}
|
||||
|
||||
if event.value == 'RELEASE': # if a key was release (any key in case shortcut was customised)
|
||||
if time() > self.limit:
|
||||
# dont know if condition is neeed
|
||||
return {'CANCELLED'}
|
||||
|
||||
return self.execute(context)
|
||||
# return {'FINISHED'}
|
||||
|
||||
return {'PASS_THROUGH'}
|
||||
# return {'RUNNING_MODAL'}
|
||||
|
||||
def execute(self, context):
|
||||
# self.prefs = get_addon_prefs()
|
||||
self.ob = context.object
|
||||
gp = self.ob.data
|
||||
|
||||
self.stroke_list = []
|
||||
self.inv_mat = self.ob.matrix_world.inverted()
|
||||
|
||||
if gp.use_multiedit:
|
||||
for l in gp.layers:
|
||||
if l.hide:# l.lock or
|
||||
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 gp.layers if not l.lock and not l.hide for s in l.active_frame.stokes]
|
||||
for l in gp.layers:
|
||||
if l.hide or not l.active_frame:# l.lock or
|
||||
continue
|
||||
for s in l.active_frame.strokes:
|
||||
self.stroke_list.append(s)
|
||||
|
||||
if self.stroke_filter == 'FILL':
|
||||
self.stroke_list = [s for s in self.stroke_list if self.ob.data.materials[s.material_index].grease_pencil.show_fill]
|
||||
elif self.stroke_filter == 'STROKE':
|
||||
self.stroke_list = [s for s in self.stroke_list if self.ob.data.materials[s.material_index].grease_pencil.show_stroke]
|
||||
# else ALL (no filter)
|
||||
|
||||
if not self.stroke_list:
|
||||
self.report({'ERROR'}, 'No stroke found, maybe layers are locked or hidden')
|
||||
return {'CANCELLED'}
|
||||
|
||||
|
||||
stroke, self.coord = self.filter_stroke(context)
|
||||
if isinstance(stroke, str):
|
||||
self.report({'ERROR'}, 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 @ stroke.points[self.idx].co
|
||||
# self.init_pos = [p.co.copy() for p in stroke.points] # need a copy otherwise vector is updated
|
||||
# self.pos_2d = [location_to_region(self.ob.matrix_world @ co) for co in self.init_pos]
|
||||
# self.plen = len(stroke.points)
|
||||
|
||||
self.ob.active_material_index = stroke.material_index
|
||||
## debug show trigger time
|
||||
# print(f'Trigger time {time() - self.t0:.3f}')
|
||||
self.report({'INFO'}, f'Mat: {self.ob.data.materials[stroke.material_index].name}')
|
||||
return {'FINISHED'}
|
||||
|
||||
|
||||
|
||||
addon_keymaps = []
|
||||
|
@ -160,13 +298,9 @@ def register_keymaps():
|
|||
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
|
||||
kmi.properties.stroke_filter = 'FILL'
|
||||
addon_keymaps.append((km, kmi))
|
||||
|
||||
kmi = km.keymap_items.new(
|
||||
|
@ -177,7 +311,7 @@ def register_keymaps():
|
|||
alt = True,
|
||||
# key_modifier='S', # S like Sample
|
||||
)
|
||||
|
||||
kmi.properties.stroke_filter = 'STROKE'
|
||||
# kmi = km.keymap_items.new('catname.opsname', type='F5', value='PRESS')
|
||||
addon_keymaps.append((km, kmi))
|
||||
|
||||
|
|
|
@ -15,7 +15,7 @@ bl_info = {
|
|||
"name": "GP toolbox",
|
||||
"description": "Set of tools for Grease Pencil in animation production",
|
||||
"author": "Samuel Bernou, Christophe Seux",
|
||||
"version": (1, 6, 8),
|
||||
"version": (1, 6, 9),
|
||||
"blender": (2, 91, 0),
|
||||
"location": "Sidebar (N menu) > Gpencil > Toolbox / Gpencil properties",
|
||||
"warning": "",
|
||||
|
|
Loading…
Reference in New Issue