gp_toolbox/OP_layer_picker.py

168 lines
5.9 KiB
Python
Raw Permalink Normal View History

import bpy
from bpy.types import Operator
import mathutils
from mathutils import Vector, Matrix, geometry
from bpy_extras import view3d_utils
from time import time
2024-12-16 17:29:27 +01:00
from .utils import get_gp_draw_plane, location_to_region, region_to_location, is_locked, is_hidden
class GP_OT_pick_closest_layer(Operator):
bl_idname = "gp.pick_closest_layer"
bl_label = "Get Closest Stroke Layer"
bl_description = "Pick closest stroke layer"
bl_options = {"REGISTER", "UNDO"}
@classmethod
def poll(cls, context):
2024-11-11 15:47:33 +01:00
return context.object and context.object.type == 'GREASEPENCIL' and context.mode == 'PAINT_GREASE_PENCIL'
stroke_filter : bpy.props.EnumProperty(name='Target',
default='STROKE',
items=(
('STROKE', 'Stroke', 'Target only Stroke', 0),
('FILL', 'Fill', 'Target only Fill', 1),
('ALL', 'All', 'All stroke types', 2),
),
options={'SKIP_SAVE'})
def filter_stroke(self, context):
kd = mathutils.kdtree.KDTree(len(self.point_pair))
for i, pair in enumerate(self.point_pair):
kd.insert(pair[0], i)
kd.balance()
mouse_vec3 = Vector((*self.init_mouse, 0))
co, index, _dist = kd.find(mouse_vec3)
2024-11-14 16:18:56 +01:00
layer = self.point_pair[index][1]
return layer
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 draw(self, context):
layout = self.layout
if context.object.data.layers.active:
2024-11-11 15:56:43 +01:00
layout.label(text=f'Layer: {context.object.data.layers.active.name}')
layout.prop(self, 'stroke_filter')
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):
t1 = time()
# self.prefs = get_addon_prefs()
self.ob = context.object
mat = self.ob.matrix_world
gp = self.ob.data
self.inv_mat = self.ob.matrix_world.inverted()
self.point_pair = []
if context.scene.tool_settings.use_grease_pencil_multi_frame_editing:
2024-11-14 16:18:56 +01:00
for layer in gp.layers:
2024-12-16 17:29:27 +01:00
if is_hidden(layer):
continue
2024-11-14 16:18:56 +01:00
for f in layer.frames:
if not f.select:
continue
for s in f.drawing.strokes:
if self.stroke_filter == 'STROKE' and not self.ob.data.materials[s.material_index].grease_pencil.show_stroke:
continue
elif self.stroke_filter == 'FILL' and not self.ob.data.materials[s.material_index].grease_pencil.show_fill:
continue
2024-11-14 16:18:56 +01:00
self.point_pair += [(Vector((*location_to_region(mat @ p.position), 0)), layer) for p in s.points]
else:
2024-12-16 17:29:27 +01:00
# [s for l in gp.layers if not is_locked(l) and not is_hidden(l) for s in l.current_frame().stokes]
2024-11-14 16:18:56 +01:00
for layer in gp.layers:
2024-12-16 17:29:27 +01:00
if is_hidden(layer) or not layer.current_frame():
continue
2024-11-14 16:18:56 +01:00
for s in layer.current_frame().drawing.strokes:
if self.stroke_filter == 'STROKE' and not self.ob.data.materials[s.material_index].grease_pencil.show_stroke:
continue
elif self.stroke_filter == 'FILL' and not self.ob.data.materials[s.material_index].grease_pencil.show_fill:
continue
2024-11-14 16:18:56 +01:00
self.point_pair += [(Vector((*location_to_region(mat @ p.position), 0)), layer) for p in s.points]
if not self.point_pair:
self.report({'ERROR'}, 'No stroke found, maybe layers are locked or hidden')
return {'CANCELLED'}
2024-11-14 16:18:56 +01:00
layer_target = self.filter_stroke(context)
if isinstance(layer_target, str):
self.report({'ERROR'}, layer_target)
return {'CANCELLED'}
del self.point_pair # auto garbage collected ?
2024-11-14 16:18:56 +01:00
self.ob.data.layers.active = layer_target
## debug show trigger time
# print(f'Trigger time {time() - self.t0:.3f}')
# print(f'Search time {time() - t1:.3f}')
2024-11-11 15:56:43 +01:00
self.report({'INFO'}, f'Layer: {self.ob.data.layers.active.name}')
return {'FINISHED'}
addon_keymaps = []
def register_keymaps():
addon = bpy.context.window_manager.keyconfigs.addon
2024-11-11 17:49:22 +01:00
km = addon.keymaps.new(name = "Grease Pencil Paint Mode", space_type = "EMPTY", region_type='WINDOW')
kmi = km.keymap_items.new(
# name="",
idname="gp.pick_closest_layer",
type="W",
value="PRESS",
)
kmi.properties.stroke_filter = 'STROKE'
addon_keymaps.append((km, kmi))
kmi = km.keymap_items.new(
# name="",
idname="gp.pick_closest_layer",
type="W",
value="PRESS",
alt = True,
)
kmi.properties.stroke_filter = 'FILL'
# 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_layer,
)
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)