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
|
# 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
|
1.6.8
|
||||||
|
|
||||||
- fix: reproject GP repeating projection on same frames
|
- fix: reproject GP repeating projection on same frames
|
||||||
|
|
|
@ -3,6 +3,7 @@ from bpy.types import Operator
|
||||||
import mathutils
|
import mathutils
|
||||||
from mathutils import Vector, Matrix, geometry
|
from mathutils import Vector, Matrix, geometry
|
||||||
from bpy_extras import view3d_utils
|
from bpy_extras import view3d_utils
|
||||||
|
from time import time
|
||||||
from .utils import get_gp_draw_plane, location_to_region, region_to_location
|
from .utils import get_gp_draw_plane, location_to_region, region_to_location
|
||||||
|
|
||||||
### passing by 2D projection
|
### passing by 2D projection
|
||||||
|
@ -23,6 +24,7 @@ def get_3d_coord_on_drawing_plane_from_2d(context, co):
|
||||||
|
|
||||||
return None, None, None
|
return None, None, None
|
||||||
|
|
||||||
|
"""
|
||||||
class GP_OT_pick_closest_material(Operator):
|
class GP_OT_pick_closest_material(Operator):
|
||||||
bl_idname = "gp.pick_closest_material"
|
bl_idname = "gp.pick_closest_material"
|
||||||
bl_label = "Get Closest Stroke Material"
|
bl_label = "Get Closest Stroke Material"
|
||||||
|
@ -147,6 +149,142 @@ class GP_OT_pick_closest_material(Operator):
|
||||||
# return {'CANCELLED'}
|
# return {'CANCELLED'}
|
||||||
|
|
||||||
# return {'RUNNING_MODAL'}
|
# 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 = []
|
addon_keymaps = []
|
||||||
|
@ -160,13 +298,9 @@ def register_keymaps():
|
||||||
idname="gp.pick_closest_material",
|
idname="gp.pick_closest_material",
|
||||||
type="S", # type="LEFTMOUSE",
|
type="S", # type="LEFTMOUSE",
|
||||||
value="PRESS",
|
value="PRESS",
|
||||||
shift=False,
|
|
||||||
ctrl=False,
|
|
||||||
alt = False,
|
|
||||||
oskey=False,
|
|
||||||
# key_modifier='S', # S like Sample
|
# key_modifier='S', # S like Sample
|
||||||
)
|
)
|
||||||
kmi.properties.fill_only = True
|
kmi.properties.stroke_filter = 'FILL'
|
||||||
addon_keymaps.append((km, kmi))
|
addon_keymaps.append((km, kmi))
|
||||||
|
|
||||||
kmi = km.keymap_items.new(
|
kmi = km.keymap_items.new(
|
||||||
|
@ -177,7 +311,7 @@ def register_keymaps():
|
||||||
alt = True,
|
alt = True,
|
||||||
# key_modifier='S', # S like Sample
|
# key_modifier='S', # S like Sample
|
||||||
)
|
)
|
||||||
|
kmi.properties.stroke_filter = 'STROKE'
|
||||||
# kmi = km.keymap_items.new('catname.opsname', type='F5', value='PRESS')
|
# kmi = km.keymap_items.new('catname.opsname', type='F5', value='PRESS')
|
||||||
addon_keymaps.append((km, kmi))
|
addon_keymaps.append((km, kmi))
|
||||||
|
|
||||||
|
|
|
@ -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, 6, 8),
|
"version": (1, 6, 9),
|
||||||
"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": "",
|
||||||
|
|
Loading…
Reference in New Issue