Compare commits

...

12 Commits

Author SHA1 Message Date
pullusb 94fc926f7a Gp modifier to unified modifier 2024-11-11 18:44:35 +01:00
pullusb 0160b4eae4 keymap paint mode name 2024-11-11 17:49:22 +01:00
pullusb 1dfb8cff9c add intermediate stroke container - strokes to drawing.strokes 2024-11-11 17:48:11 +01:00
pullusb 4732110b93 multi_frame editing changed to scene toolsetting property 2024-11-11 17:34:47 +01:00
pullusb 7bc7d5d9ff fix brush context menu 2024-11-11 17:32:58 +01:00
pullusb 998bd4b0cb active_frame attribute to current_frame method 2024-11-11 17:30:33 +01:00
pullusb 25adb5beb6 partial update of GP menu types 2024-11-11 17:27:57 +01:00
pullusb 6e94ee270d point attribute co to position 2024-11-11 16:23:11 +01:00
pullusb 3ece64e517 gpv3 update info to name property 2024-11-11 15:56:43 +01:00
pullusb eae69b6f75 update to gpv3 context modes 2024-11-11 15:47:33 +01:00
pullusb f5a78601b6 Begin port to GPv3 - change GP type 2024-11-11 15:35:39 +01:00
pullusb 98ed92afe2 add link to last gpv2 release 2024-11-11 15:35:27 +01:00
29 changed files with 363 additions and 406 deletions

View File

@ -9,7 +9,7 @@ from bpy.props import (FloatProperty,
## copied from OP_key_duplicate_send
def get_layer_list(self, context):
'''return (identifier, name, description) of enum content'''
return [(l.info, l.info, '') for l in context.object.data.layers if l != context.object.data.layers.active]
return [(l.name, l.name, '') for l in context.object.data.layers if l != context.object.data.layers.active]
class GP_OT_create_empty_frames(bpy.types.Operator):
bl_idname = "gp.create_empty_frames"
@ -57,7 +57,7 @@ class GP_OT_create_empty_frames(bpy.types.Operator):
@classmethod
def poll(cls, context):
return context.active_object is not None and context.active_object.type == 'GPENCIL'
return context.active_object is not None and context.active_object.type == 'GREASEPENCIL'
def invoke(self, context, event):
# Possible preset with shortcut
@ -113,7 +113,7 @@ class GP_OT_create_empty_frames(bpy.types.Operator):
if not self.layers_enum:
self.report({'ERROR'}, f"No chosen layers, aborted")
return {'CANCELLED'}
tgt_layers = [l for l in gpl if l.info == self.layers_enum]
tgt_layers = [l for l in gpl if l.name == self.layers_enum]
elif self.targeted_layers == 'NUMBER':
if self.number == 0:
@ -168,7 +168,7 @@ class GP_OT_create_empty_frames(bpy.types.Operator):
gpl.update()
if fct:
self.report({'INFO'}, f"{fct} frame created on layer {gpl.active.info}")
self.report({'INFO'}, f"{fct} frame created on layer {gpl.active.name}")
else:
self.report({'WARNING'}, f"No frames to create !")

View File

@ -53,11 +53,14 @@ def create_gap_stroke(f, ob, tol=10, mat_id=None):
encounter = defaultdict(list)
plist = []
matrix = ob.matrix_world
for s in f.strokes:#add first and last
for s in f.drawing.strokes: #add first and last
smat = ob.material_slots[s.material_index].material
if not smat:continue#no material on line
if smat.grease_pencil.show_fill:continue# skip fill lines -> #smat.grease_pencil.show_stroke
if len(s.points) < 2:continue#avoid 0 or 1 points
if not smat:
continue #no material on line
if smat.grease_pencil.show_fill:
continue # skip fill lines -> #smat.grease_pencil.show_stroke
if len(s.points) < 2:
continue #avoid 0 or 1 points
plist.append(s.points[0])
plist.append(s.points[-1])
# plist.extend([s.points[0], s.points[-1])# is extend faster ?
@ -70,7 +73,7 @@ def create_gap_stroke(f, ob, tol=10, mat_id=None):
for op in plist:#other points
if p == op:# print('same point')
continue
gap2d = vector_length_2d(location_to_region(matrix @ p.co), location_to_region(matrix @ op.co))
gap2d = vector_length_2d(location_to_region(matrix @ p.position), location_to_region(matrix @ op.position))
# print('gap2d: ', gap2d)
if gap2d > tol:
continue
@ -102,16 +105,16 @@ def create_gap_stroke(f, ob, tol=10, mat_id=None):
encounter[p].append(op)
simple_draw_gp_stroke([p.co, op.co], f, width = 2, mat_id = mat_id)
simple_draw_gp_stroke([p.position, op.position], f, width = 2, mat_id = mat_id)
ctl += 1
print(f'{ctl} line created')
##test_call: #create_gap_stroke(C.object.data.layers.active.active_frame, C.object, mat_id=C.object.active_material_index)
##test_call: #create_gap_stroke(C.object.data.layers.active.current_frame(), C.object, mat_id=C.object.active_material_index)
def create_closing_line(tolerance=0.2):
for ob in bpy.context.selected_objects:
if ob.type != 'GPENCIL':
if ob.type != 'GREASEPENCIL':
continue
mat_id = get_closeline_mat(ob)# get a the closing material
@ -128,7 +131,7 @@ def create_closing_line(tolerance=0.2):
## filter on selected
if not l.select:continue# comment this line for all
# for f in l.frames:#not all for now
f = l.active_frame
f = l.current_frame()
## create gap stroke
create_gap_stroke(f, ob, tol=tolerance, mat_id=mat_id)
@ -143,9 +146,9 @@ def is_deviating_by(s, deviation=0.75):
pb = s.points[-2]
pc = s.points[-3]
a = location_to_region(pa.co)
b = location_to_region(pb.co)
c = location_to_region(pc.co)
a = location_to_region(pa.position)
b = location_to_region(pb.position)
c = location_to_region(pc.position)
#cb-> compare angle with ba->
angle = (b-c).angle(a-b)
@ -158,16 +161,16 @@ def extend_stroke_tips(s,f,ob,length, mat_id):
'''extend line boundary by given length'''
for id_pair in [ [1,0], [-2,-1] ]:# start and end pair
## 2D mode
# a = location_to_region(ob.matrix_world @ s.points[id_pair[0]].co)
# b_loc = ob.matrix_world @ s.points[id_pair[1]].co
# a = location_to_region(ob.matrix_world @ s.points[id_pair[0]].position)
# b_loc = ob.matrix_world @ s.points[id_pair[1]].position
# b = location_to_region(b_loc)
# c = extrapolate_points_by_length(a,b,length)#print(vector_length_2d(b,c))
# c_loc = region_to_location(c, b_loc)
# simple_draw_gp_stroke([ob.matrix_world.inverted() @ b_loc, ob.matrix_world.inverted() @ c_loc], f, width=2, mat_id=mat_id)
## 3D
a = s.points[id_pair[0]].co# ob.matrix_world @
b = s.points[id_pair[1]].co# ob.matrix_world @
a = s.points[id_pair[0]].position# ob.matrix_world @
b = s.points[id_pair[1]].position# ob.matrix_world @
c = extrapolate_points_by_length(a,b,length)#print(vector_length(b,c))
simple_draw_gp_stroke([b,c], f, width=2, mat_id=mat_id)
@ -188,15 +191,15 @@ def change_extension_length(ob, strokelist, length, selected=False):
## Change length of current length to designated
# Vector point A to point B (direction), push point B in this direction
a = s.points[-2].co
a = s.points[-2].position
bp = s.points[-1]#end-point
b = bp.co
b = bp.position
ab = b - a
if not ab:
continue
# new pos of B is A + new length in the AB direction
newb = a + (ab.normalized() * length)
bp.co = newb
bp.position = newb
ct += 1
return ct
@ -210,14 +213,14 @@ def extend_all_strokes_tips(ob, frame, length=10, selected=False):
return
# TODO need custom filters or go in GP refine strokes...
# frame = ob.data.layers.active.active_frame
# frame = ob.data.layers.active.current_frame()
if not frame: return
ct = 0
#TODO need to delete previous closing lines on frame before launching
# iterate in a copy of stroke list to avoid growing frame.strokes as we loop in !
for s in list(frame.strokes):
# iterate in a copy of stroke list to avoid growing frame.drawing.strokes as we loop in !
for s in list(frame.drawing.strokes):
if s.material_index == mat_id:#is a closeline
continue
if len(s.points) < 2:#not enough point to evaluate
@ -241,7 +244,7 @@ class GPSTK_OT_extend_lines(bpy.types.Operator):
@classmethod
def poll(cls, context):
return context.active_object is not None and context.active_object.type == 'GPENCIL'
return context.active_object is not None and context.active_object.type == 'GREASEPENCIL'
# mode : bpy.props.StringProperty(
# name="mode", description="Set mode for operator", default="render", maxlen=0, subtype='NONE', options={'ANIMATABLE'})
@ -277,14 +280,14 @@ class GPSTK_OT_extend_lines(bpy.types.Operator):
elif self.layer_tgt == 'ALL_VISIBLE':
lays = [l for l in ob.data.layers if not l.hide]
else:
lays = [l for l in ob.data.layers if not any(x in l.info for x in ('spot', 'colo'))]
lays = [l for l in ob.data.layers if not any(x in l.name for x in ('spot', 'colo'))]
fct = 0
for l in lays:
if not l.active_frame:
print(f'{l.info} has no active frame')
if not l.current_frame():
print(f'{l.name} has no active frame')
continue
fct += extend_all_strokes_tips(ob, l.active_frame, length = self.length, selected = self.selected)
fct += extend_all_strokes_tips(ob, l.current_frame(), length = self.length, selected = self.selected)
if not fct:
mess = "No strokes extended... see console"
@ -306,7 +309,7 @@ class GPSTK_OT_change_closeline_length(bpy.types.Operator):
@classmethod
def poll(cls, context):
return context.active_object is not None and context.active_object.type == 'GPENCIL'
return context.active_object is not None and context.active_object.type == 'GREASEPENCIL'
layer_tgt : bpy.props.EnumProperty(
name="Extend layers", description="Choose which layer to target",
@ -338,14 +341,14 @@ class GPSTK_OT_change_closeline_length(bpy.types.Operator):
elif self.layer_tgt == 'ALL_VISIBLE':
lays = [l for l in ob.data.layers if not l.hide]
else:
lays = [l for l in ob.data.layers if not any(x in l.info for x in ('spot', 'colo'))]
lays = [l for l in ob.data.layers if not any(x in l.name for x in ('spot', 'colo'))]
fct = 0
for l in lays:
if not l.active_frame:
print(f'{l.info} has no active frame')
if not l.current_frame():
print(f'{l.name} has no active frame')
continue
fct += change_extension_length(ob, [s for s in l.active_frame.strokes], length = self.length, selected = self.selected)
fct += change_extension_length(ob, [s for s in l.current_frame().drawing.strokes], length = self.length, selected = self.selected)
if not fct:
mess = "No extension modified... see console"
@ -367,15 +370,15 @@ class GPSTK_OT_comma_finder(bpy.types.Operator):
@classmethod
def poll(cls, context):
return context.active_object is not None and context.active_object.type == 'GPENCIL'
return context.active_object is not None and context.active_object.type == 'GREASEPENCIL'
def execute(self, context):
ct = 0
ob = context.object
lays = [l for l in ob.data.layers if not l.hide and not l.lock]
for l in lays:
if not l.active_frame:continue
for s in l.active_frame.strokes:
if not l.current_frame():continue
for s in l.current_frame().drawing.strokes:
if is_deviating_by(s, context.scene.gpcolor_props.deviation_tolerance):
ct+=1
@ -397,7 +400,7 @@ class GPSTK_PT_line_closer_panel(bpy.types.Panel):
@classmethod
def poll(cls, context):
return (context.object is not None)# and context.object.type == 'GPENCIL'
return (context.object is not None)# and context.object.type == 'GREASEPENCIL'
## draw stuff inside the header (place before main label)
# def draw_header(self, context):
@ -414,7 +417,7 @@ class GPSTK_PT_line_closer_panel(bpy.types.Panel):
layout.operator("gp.extend_close_lines", icon = 'SNAP_MIDPOINT')
#diplay closeline visibility
if context.object.type == 'GPENCIL' and context.object.data.materials.get('closeline'):
if context.object.type == 'GREASEPENCIL' and context.object.data.materials.get('closeline'):
row=layout.row()
row.prop(context.object.data.materials['closeline'].grease_pencil, 'hide', text='Stop lines')
row.operator("gp.change_close_lines_extension", text='Length', icon = 'DRIVER_DISTANCE')

View File

@ -27,7 +27,7 @@ class GPTB_OT_load_brushes(bpy.types.Operator, ImportHelper):
# @classmethod
# def poll(cls, context):
# return context.object and context.object.type == 'GPENCIL'
# return context.object and context.object.type == 'GREASEPENCIL'
filename_ext = '.blend'
@ -58,7 +58,7 @@ class GPTB_OT_brush_set(bpy.types.Operator):
@classmethod
def poll(cls, context):
return context.object and context.mode == 'PAINT_GPENCIL'
return context.object and context.mode == 'PAINT_GREASE_PENCIL'
def execute(self, context):
brush = bpy.data.brushes.get(self.brush_name)
@ -72,12 +72,12 @@ class GPTB_OT_brush_set(bpy.types.Operator):
def load_brush_ui(self, context):
prefs = get_addon_prefs()
if context.mode == 'PAINT_GPENCIL':
if context.mode == 'PAINT_GREASE_PENCIL':
self.layout.operator('gp.load_brushes', icon='KEYTYPE_JITTER_VEC').filepath = prefs.brush_path
def load_brush_top_bar_ui(self, context):
prefs = get_addon_prefs()
if context.mode == 'PAINT_GPENCIL':
if context.mode == 'PAINT_GREASE_PENCIL':
self.layout.operator('gp.load_brushes').filepath = prefs.brush_path
classes = (
@ -89,12 +89,12 @@ def register():
for cl in classes:
bpy.utils.register_class(cl)
bpy.types.VIEW3D_MT_brush_gpencil_context_menu.append(load_brush_ui)
bpy.types.VIEW3D_MT_brush_context_menu.append(load_brush_ui)
bpy.types.VIEW3D_HT_tool_header.append(load_brush_top_bar_ui)
def unregister():
bpy.types.VIEW3D_HT_tool_header.remove(load_brush_top_bar_ui)
bpy.types.VIEW3D_MT_brush_gpencil_context_menu.remove(load_brush_ui)
bpy.types.VIEW3D_MT_brush_context_menu.remove(load_brush_ui)
for cl in reversed(classes):
bpy.utils.unregister_class(cl)

View File

@ -169,13 +169,13 @@ def copycut_strokes(layers=None, copy=True, keep_empty=True):
stroke_list = [] # one stroke list for all layers.
for l in layers:
f = l.active_frame
f = l.current_frame()
if f: # active frame can be None
if not copy:
staylist = [] # init part of strokes that must survive on this layer
for s in f.strokes:
for s in f.drawing.strokes:
if s.select:
# separate in multiple stroke if parts of the strokes a selected.
sel = [i for i, p in enumerate(s.points) if p.select]
@ -192,7 +192,7 @@ def copycut_strokes(layers=None, copy=True, keep_empty=True):
if not copy:
maxindex = len(s.points)-1
if len(substrokes) == maxindex+1: # if only one substroke, then it's the full stroke
f.strokes.remove(s)
f.drawing.strokes.remove(s)
else:
neg = [i for i, p in enumerate(s.points) if not p.select]
@ -217,14 +217,14 @@ def copycut_strokes(layers=None, copy=True, keep_empty=True):
stroke_list.append(dump_gp_stroke(s,l))
#delete stroke on the fly
if not copy:
f.strokes.remove(s)
f.drawing.strokes.remove(s)
'''
if not copy:
# delete all selected strokes...
for s in f.strokes:
for s in f.drawing.strokes:
if s.select:
f.strokes.remove(s)
f.drawing.strokes.remove(s)
# ...recreate these uncutted ones
#pprint(staylist)
if staylist:
@ -234,7 +234,7 @@ def copycut_strokes(layers=None, copy=True, keep_empty=True):
#if nothing left on the frame choose to leave an empty frame or delete it (let previous frame appear)
if not copy and not keep_empty:#
if not len(f.strokes):
if not len(f.drawing.strokes):
l.frames.remove(f)
@ -268,12 +268,12 @@ def copy_all_strokes(layers=None):
stroke_list = []# one stroke list for all layers.
for l in layers:
f = l.active_frame
f = l.current_frame()
if not f:
continue# active frame can be None
for s in f.strokes:
for s in f.drawing.strokes:
## full stroke version
# if s.select:
stroke_list.append(dump_gp_stroke_range(s, None, l, obj))
@ -308,12 +308,12 @@ def copy_all_strokes_in_frame(frame=None, layers=None, obj=None,
stroke_list = []
for l in layers:
f = l.active_frame
f = l.current_frame()
if not f:
continue# active frame can be None
for s in f.strokes:
for s in f.drawing.strokes:
## full stroke version
# if s.select:
# send index of all points to get the whole stroke with "range"
@ -326,7 +326,7 @@ def copy_all_strokes_in_frame(frame=None, layers=None, obj=None,
def add_stroke(s, frame, layer, obj, select=False):
'''add stroke on a given frame, (layer is for parentage setting)'''
# print(3*'-',s)
ns = frame.strokes.new()
ns = frame.drawing.strokes.new()
for att, val in s.items():
if att not in ('points'):
@ -347,7 +347,7 @@ def add_stroke(s, frame, layer, obj, select=False):
for k, v in pt.items():
if k == 'co':
setattr(ns.points[i], k, v)
ns.points[i].co = ob_mat_inv @ mat @ ns.points[i].co # invert of object * invert of layer * coordinate
ns.points[i].position = ob_mat_inv @ mat @ ns.points[i].position # invert of object * invert of layer * coordinate
else:
setattr(ns.points[i], k, v)
if select:
@ -358,7 +358,7 @@ def add_stroke(s, frame, layer, obj, select=False):
for k, v in pt.items():
if k == 'co':
setattr(ns.points[i], k, v)
ns.points[i].co = ob_mat_inv @ ns.points[i].co# invert of object * coordinate
ns.points[i].position = ob_mat_inv @ ns.points[i].position# invert of object * coordinate
else:
setattr(ns.points[i], k, v)
if select:
@ -389,7 +389,7 @@ def add_multiple_strokes(stroke_list, layer=None, use_current_frame=True, select
fnum = scene.frame_current
target_frame = False
act = layer.active_frame
act = layer.current_frame()
## set frame if needed
if act:
if use_current_frame or act.frame_number == fnum:
@ -423,10 +423,10 @@ class GPCLIP_OT_copy_strokes(bpy.types.Operator):
#copy = bpy.props.BoolProperty(default=True)
@classmethod
def poll(cls, context):
return context.object and context.object.type == 'GPENCIL'
return context.object and context.object.type == 'GREASEPENCIL'
def execute(self, context):
# if not context.object or not context.object.type == 'GPENCIL':
# if not context.object or not context.object.type == 'GREASEPENCIL':
# self.report({'ERROR'},'No GP object selected')
# return {"CANCELLED"}
@ -452,10 +452,10 @@ class GPCLIP_OT_cut_strokes(bpy.types.Operator):
@classmethod
def poll(cls, context):
return context.object and context.object.type == 'GPENCIL'
return context.object and context.object.type == 'GREASEPENCIL'
def execute(self, context):
# if not context.object or not context.object.type == 'GPENCIL':
# if not context.object or not context.object.type == 'GREASEPENCIL':
# self.report({'ERROR'},'No GP object selected')
# return {"CANCELLED"}
@ -477,10 +477,10 @@ class GPCLIP_OT_paste_strokes(bpy.types.Operator):
@classmethod
def poll(cls, context):
return context.object and context.object.type == 'GPENCIL'
return context.object and context.object.type == 'GREASEPENCIL'
def execute(self, context):
# if not context.object or not context.object.type == 'GPENCIL':
# if not context.object or not context.object.type == 'GREASEPENCIL':
# self.report({'ERROR'},'No GP object selected to paste on')
# return {"CANCELLED"}
@ -510,7 +510,7 @@ class GPCLIP_OT_copy_multi_strokes(bpy.types.Operator):
#copy = bpy.props.BoolProperty(default=True)
@classmethod
def poll(cls, context):
return context.object and context.object.type == 'GPENCIL'
return context.object and context.object.type == 'GREASEPENCIL'
pressure : bpy.props.BoolProperty(name='pressure', default=True,
description='Dump point pressure attribute (already skipped if at default value)')
@ -566,7 +566,7 @@ class GPCLIP_OT_copy_multi_strokes(bpy.types.Operator):
frame_dic = {}
for f in l.frames:
if skip_empty_frame and not len(f.strokes):
if skip_empty_frame and not len(f.drawing.strokes):
continue
context.scene.frame_set(f.frame_number) # use matrix of this frame
strokelist = copy_all_strokes_in_frame(frame=f, layers=l, obj=obj,
@ -575,11 +575,11 @@ class GPCLIP_OT_copy_multi_strokes(bpy.types.Operator):
frame_dic[f.frame_number] = strokelist
layerdic[l.info] = frame_dic
layerdic[l.name] = frame_dic
else: # bake position: copy frame where object as moved even if frame is unchanged
for l in layerpool:
print('dump layer:', l.info)
print('dump layer:', l.name)
if not l.frames:
continue# skip empty layers
@ -603,7 +603,7 @@ class GPCLIP_OT_copy_multi_strokes(bpy.types.Operator):
break
## skip empty frame if specified
if skip_empty_frame and not len(f.strokes):
if skip_empty_frame and not len(f.drawing.strokes):
continue
strokelist = copy_all_strokes_in_frame(frame=f, layers=l, obj=obj,
@ -613,7 +613,7 @@ class GPCLIP_OT_copy_multi_strokes(bpy.types.Operator):
frame_dic[i] = strokelist
prevmat = curmat
layerdic[l.info] = frame_dic
layerdic[l.name] = frame_dic
## All to clipboard manager
bpy.context.window_manager.clipboard = json.dumps(layerdic)
@ -633,7 +633,7 @@ class GPCLIP_OT_paste_multi_strokes(bpy.types.Operator):
#copy = bpy.props.BoolProperty(default=True)
@classmethod
def poll(cls, context):
return context.object and context.object.type == 'GPENCIL'
return context.object and context.object.type == 'GREASEPENCIL'
def execute(self, context):
org_frame = context.scene.frame_current

View File

@ -13,7 +13,7 @@ class GPTB_OT_cusor_snap(bpy.types.Operator):
# @classmethod
# def poll(cls, context):
# return context.object and context.object.type == 'GPENCIL'
# return context.object and context.object.type == 'GREASEPENCIL'
def invoke(self, context, event):
#print('-!SNAP!-')
@ -23,7 +23,7 @@ class GPTB_OT_cusor_snap(bpy.types.Operator):
return {"FINISHED"}
def execute(self, context):
if not context.object or context.object.type != 'GPENCIL':
if not context.object or context.object.type != 'GREASEPENCIL':
self.report({'INFO'}, 'Not GP, Cursor surface project')
bpy.ops.view3d.cursor3d('INVOKE_DEFAULT', use_depth=True, orientation='NONE')#'NONE', 'VIEW', 'XFORM', 'GEOM'
return {"FINISHED"}

View File

@ -231,7 +231,7 @@ class GPTB_OT_eraser(Operator):
hld_stroke.points.add(count=1)
p = hld_stroke.points[-1]
p.co = mat_inv @ mouse_3d
p.position = mat_inv @ mouse_3d
p.pressure = search_radius * 2000
#context.scene.cursor.location = mouse_3d
@ -252,14 +252,14 @@ class GPTB_OT_eraser(Operator):
#print(self.cuts_data)
# for f in self.gp_frames:
# for s in [s for s in f.strokes if s.material_index==self.hld_index]:
# f.strokes.remove(s)
# for s in [s for s in f.drawing.strokes if s.material_index==self.hld_index]:
# f.drawing.strokes.remove(s)
#gp.data.materials.pop(index=self.hld_index)
#bpy.data.materials.remove(self.hld_mat)
bpy.ops.object.mode_set(mode='EDIT_GPENCIL')
bpy.ops.object.mode_set(mode='EDIT')
context.scene.tool_settings.gpencil_selectmode_edit = 'POINT'
#context.scene.tool_settings.gpencil_selectmode_edit = 'POINT'
@ -281,7 +281,7 @@ class GPTB_OT_eraser(Operator):
bpy.ops.gpencil.select_all(action='DESELECT')
bpy.ops.gpencil.select_circle(x=x, y=y, radius=radius, wait_for_input=False)
strokes = [s for f in self.gp_frames for s in f.strokes]
strokes = [s for f in self.gp_frames for s in f.drawing.strokes]
#print('select_circle', time()-t1)
t2 = time()
@ -309,18 +309,18 @@ class GPTB_OT_eraser(Operator):
bpy.ops.gpencil.stroke_subdivide(number_cuts=number_cuts, only_selected=True)
new_p1 = stroke.points[p1_index+1]
new_p1.co = mat_inv@intersects[0]
new_p1.position = mat_inv@intersects[0]
new_points += [(stroke, p1_index+1)]
#print('number_cuts', number_cuts)
if number_cuts == 2:
new_p2 = stroke.points[p1_index+2]
new_p2.co = mat_inv@( (intersects[0] + intersects[1])/2 )
new_p2.position = mat_inv@( (intersects[0] + intersects[1])/2 )
#new_points += [new_p2]
new_p3 = stroke.points[p1_index+3]
new_p3.co = mat_inv@intersects[1]
new_p3.position = mat_inv@intersects[1]
new_points += [(stroke, p1_index+3)]
#print('subdivide', time() - t3)
@ -329,7 +329,7 @@ class GPTB_OT_eraser(Operator):
bpy.ops.gpencil.select_circle(x=x, y=y, radius=radius, wait_for_input=False)
'''
selected_strokes = [s for f in self.gp_frames for s in f.strokes if s.select]
selected_strokes = [s for f in self.gp_frames for s in f.drawing.strokes if s.select]
tip_points = [p for s in selected_strokes for i, p in enumerate(s.points) if p.select and (i==0 or i == len(s.points)-1)]
bpy.ops.gpencil.select_less()
@ -341,7 +341,7 @@ class GPTB_OT_eraser(Operator):
'''
t4 = time()
selected_strokes = [s for f in self.gp_frames for s in f.strokes if s.select]
selected_strokes = [s for f in self.gp_frames for s in f.drawing.strokes if s.select]
if selected_strokes:
bpy.ops.gpencil.delete(type='POINTS')
@ -358,9 +358,9 @@ class GPTB_OT_eraser(Operator):
#bpy.ops.object.mode_set(mode='OBJECT')
context.scene.tool_settings.gpencil_selectmode_edit = self.gpencil_selectmode_edit
bpy.ops.object.mode_set(mode='PAINT_GPENCIL')
#selected_strokes = [s for s in self.gp_frame.strokes if s.select]
#bpy.ops.object.mode_set(mode='PAINT_GPENCIL')
bpy.ops.object.mode_set(mode='PAINT_GREASE_PENCIL')
#selected_strokes = [s for s in self.gp_frame.drawing.strokes if s.select]
#bpy.ops.object.mode_set(mode='PAINT_GREASE_PENCIL')
def modal(self, context, event):
self.mouse = Vector((event.mouse_region_x, event.mouse_region_y))
@ -441,22 +441,22 @@ class GPTB_OT_eraser(Operator):
t0 = time()
gp_mats = gp.data.materials
gp_layers = [l for l in gp.data.layers if not l.lock or l.hide]
self.gp_frames = [l.active_frame for l in gp_layers]
self.gp_frames = [l.current_frame() for l in gp_layers]
'''
points_data = [(s, f, gp_mats[s.material_index]) for f in gp_frames for s in f.strokes]
points_data = [(s, f, gp_mats[s.material_index]) for f in gp_frames for s in f.drawing.strokes]
points_data = [(s, f, m) for s, f, m in points_data if not m.grease_pencil.hide or m.grease_pencil.lock]
print('get_gp_points', time()-t0)
t0 = time()
#points_data = [(s, f, m, p, get_screen_co(p.co, matrix)) for s, f, m in points_data for p in reversed(s.points)]
points_data = [(s, f, m, p, org + ((matrix @ p.co)-org).normalized()*1) for s, f, m in points_data for p in reversed(s.points)]
#points_data = [(s, f, m, p, get_screen_co(p.position, matrix)) for s, f, m in points_data for p in reversed(s.points)]
points_data = [(s, f, m, p, org + ((matrix @ p.position)-org).normalized()*1) for s, f, m in points_data for p in reversed(s.points)]
print('points_to_2d', time()-t0)
#print(points_data)
self.points_data = [(s, f, m, p, co) for s, f, m, p, co in points_data if co is not None]
#for s, f, m, p, co in self.points_data:
# p.co = co
# p.position = co
t0 = time()
@ -481,7 +481,7 @@ class GPTB_OT_eraser(Operator):
self.hld_strokes = []
for f in self.gp_frames:
hld_stroke = f.strokes.new()
hld_stroke = f.drawing.strokes.new()
hld_stroke.start_cap_mode = 'ROUND'
hld_stroke.end_cap_mode = 'ROUND'
hld_stroke.material_index = self.hld_index

View File

@ -16,15 +16,15 @@ def remove_stroke_exact_duplications(apply=True):
for l in gp.layers:
for f in l.frames:
stroke_list = []
for s in reversed(f.strokes):
for s in reversed(f.drawing.strokes):
point_list = [p.co for p in s.points]
point_list = [p.position for p in s.points]
if point_list in stroke_list:
ct += 1
if apply:
# Remove redundancy
f.strokes.remove(s)
f.drawing.strokes.remove(s)
else:
stroke_list.append(point_list)
return ct
@ -120,7 +120,7 @@ class GPTB_OT_file_checker(bpy.types.Operator):
setattr(area.spaces[0], 'show_locked_time', True)
## set cursor type
if context.mode in ("EDIT_GPENCIL", "SCULPT_GPENCIL"):
if context.mode in ("EDIT_GREASE_PENCIL", "SCULPT_GREASE_PENCIL"):
tool = fix.select_active_tool
if tool != 'none':
if bpy.context.workspace.tools.from_space_view3d_mode(bpy.context.mode, create=False).idname != tool:
@ -145,7 +145,7 @@ class GPTB_OT_file_checker(bpy.types.Operator):
## GP Use light disable
if fix.set_gp_use_lights_off:
gp_with_lights = [o for o in context.scene.objects if o.type == 'GPENCIL' and o.use_grease_pencil_lights]
gp_with_lights = [o for o in context.scene.objects if o.type == 'GREASEPENCIL' and o.use_grease_pencil_lights]
if gp_with_lights:
problems.append(f'Disable "Use Lights" on {len(gp_with_lights)} Gpencil objects')
if apply:
@ -181,14 +181,6 @@ class GPTB_OT_file_checker(bpy.types.Operator):
if fix.list_gp_mod_vis_conflict:
mod_viz_ct = 0
for o in context.scene.objects:
if o.type == 'GPENCIL':
for m in o.grease_pencil_modifiers:
if m.show_viewport != m.show_render:
vp = 'Yes' if m.show_viewport else 'No'
rd = 'Yes' if m.show_render else 'No'
mod_viz_ct += 1
print(f'{o.name} - GP modifier {m.name}: viewport {vp} != render {rd}')
else:
for m in o.modifiers:
if m.show_viewport != m.show_render:
vp = 'Yes' if m.show_viewport else 'No'
@ -201,13 +193,13 @@ class GPTB_OT_file_checker(bpy.types.Operator):
## check if GP modifier have broken layer targets
if fix.list_broken_mod_targets:
for o in [o for o in bpy.context.scene.objects if o.type == 'GPENCIL']:
lay_name_list = [l.info for l in o.data.layers]
for m in o.grease_pencil_modifiers:
if not hasattr(m, 'layer'):
for o in [o for o in bpy.context.scene.objects if o.type == 'GREASEPENCIL']:
lay_name_list = [l.name for l in o.data.layers]
for m in o.modifiers:
if not hasattr(m, 'layer_filter'):
continue
if m.layer != '' and not m.layer in lay_name_list:
mess = f'Broken modifier layer target: {o.name} > {m.name} > {m.layer}'
if m.layer_filter != '' and not m.layer_filter in lay_name_list:
mess = f'Broken modifier layer target: {o.name} > {m.name} > {m.layer_filter}'
print(mess)
problems.append(mess)
@ -277,7 +269,7 @@ class GPTB_OT_file_checker(bpy.types.Operator):
# problems.append(f"{fix_kf_type} GP onion skin filter to 'All type'")
# for ob in context.scene.objects:#from object
# if ob.type == 'GPENCIL':
# if ob.type == 'GREASEPENCIL':
# ob.data.onion_keyframe_type = 'ALL'
#### --- print fix/problems report
@ -562,46 +554,6 @@ class GPTB_OT_list_object_visibility(bpy.types.Operator):
# self.report({'INFO'}, f"No Object visibility conflict on current scene")
# return {'FINISHED'}
## Only GP modifier
'''
class GPTB_OT_list_modifier_visibility(bpy.types.Operator):
bl_idname = "gp.list_modifier_visibility"
bl_label = "List GP Modifiers Visibility Conflicts"
bl_description = "List Modifier visibility conflicts, when viewport and render have different values"
bl_options = {"REGISTER"}
def invoke(self, context, event):
self.ob_list = []
for o in context.scene.objects:
if o.type != 'GPENCIL':
continue
if not len(o.grease_pencil_modifiers):
continue
mods = []
for m in o.grease_pencil_modifiers:
if m.show_viewport != m.show_render:
if not mods:
self.ob_list.append([o, mods])
mods.append(m)
return context.window_manager.invoke_props_dialog(self, width=250)
def draw(self, context):
layout = self.layout
for o in self.ob_list:
layout.label(text=o[0].name, icon='OUTLINER_OB_GREASEPENCIL')
for m in o[1]:
row = layout.row()
row.label(text='')
row.label(text=m.name, icon='MODIFIER_ON')
row.prop(m, 'show_viewport', text='', emboss=False) # invert_checkbox=True
row.prop(m, 'show_render', text='', emboss=False) # invert_checkbox=True
def execute(self, context):
return {'FINISHED'}
'''
## not exposed in UI, Check is performed in Check file (can be called in popped menu)
class GPTB_OT_list_modifier_visibility(bpy.types.Operator):
bl_idname = "gp.list_modifier_visibility"
@ -612,16 +564,6 @@ class GPTB_OT_list_modifier_visibility(bpy.types.Operator):
def invoke(self, context, event):
self.ob_list = []
for o in context.scene.objects:
if o.type == 'GPENCIL':
if not len(o.grease_pencil_modifiers):
continue
mods = []
for m in o.grease_pencil_modifiers:
if m.show_viewport != m.show_render:
if not mods:
self.ob_list.append([o, mods, 'OUTLINER_OB_GREASEPENCIL'])
mods.append(m)
else:
if not len(o.modifiers):
continue
mods = []

View File

@ -19,7 +19,7 @@ def batch_flat_reproject(obj, proj_type='VIEW', all_strokes=True, restore_frame=
oframe = bpy.context.scene.frame_current
omode = bpy.context.mode
# frame_list = [ f.frame_number for l in obj.data.layers for f in l.frames if len(f.strokes)]
# frame_list = [ f.frame_number for l in obj.data.layers for f in l.frames if len(f.drawing.strokes)]
# frame_list = list(set(frame_list))
# frame_list.sort()
# for fnum in frame_list:
@ -32,15 +32,15 @@ def batch_flat_reproject(obj, proj_type='VIEW', all_strokes=True, restore_frame=
fnum = len(l.frames)
zf = len(str(fnum))
for j, f in enumerate(reversed(l.frames)): # whynot...
print(f'{obj.name} : {i+1}/{laynum} : {l.info} : {str(j+1).zfill(zf)}/{fnum}{" "*30}', end='\r')
print(f'{obj.name} : {i+1}/{laynum} : {l.name} : {str(j+1).zfill(zf)}/{fnum}{" "*30}', end='\r')
scn.frame_set(f.frame_number) # more chance to update the matrix
bpy.context.view_layer.update() # update the matrix ?
bpy.context.scene.camera.location = bpy.context.scene.camera.location
scn.frame_current = f.frame_number
for s in f.strokes:
for s in f.drawing.strokes:
for p in s.points:
p.co = obj.matrix_world.inverted() @ region_to_location(location_to_region(obj.matrix_world @ p.co), scn.cursor.location)
p.position = obj.matrix_world.inverted() @ region_to_location(location_to_region(obj.matrix_world @ p.position), scn.cursor.location)
if restore_frame:
bpy.context.scene.frame_current = oframe
@ -67,8 +67,8 @@ def batch_flat_reproject(obj):
plane_no.rotate(cam_mat)
plane_co = scn.cursor.location
for s in f.strokes:
points_co = [obj.matrix_world @ p.co for p in s.points]
for s in f.drawing.strokes:
points_co = [obj.matrix_world @ p.position for p in s.points]
points_co = [mat_inv @ intersect_line_plane(origin, p, plane_co, plane_no) for p in points_co]
points_co = [co for vector in points_co for co in vector]
@ -76,8 +76,8 @@ def batch_flat_reproject(obj):
s.points.add(1) # update
s.points.pop() # update
#for p in s.points:
# loc_2d = location_to_region(obj.matrix_world @ p.co)
# p.co = obj.matrix_world.inverted() @ region_to_location(loc_2d, scn.cursor.location)
# loc_2d = location_to_region(obj.matrix_world @ p.position)
# p.position = obj.matrix_world.inverted() @ region_to_location(loc_2d, scn.cursor.location)
"""
def batch_flat_reproject(obj):
@ -96,14 +96,14 @@ def batch_flat_reproject(obj):
plane_co = scn.cursor.location
for l in obj.data.layers:
f = l.active_frame
f = l.current_frame()
if not f: # No active frame
continue
if f.frame_number != scn.frame_current:
f = l.frames.copy(f) # duplicate content of the previous frame
for s in f.strokes:
points_co = [obj.matrix_world @ p.co for p in s.points]
for s in f.drawing.strokes:
points_co = [obj.matrix_world @ p.position for p in s.points]
points_co = [mat_inv @ intersect_line_plane(origin, p, plane_co, plane_no) for p in points_co]
points_co = [co for vector in points_co for co in vector]
@ -119,11 +119,11 @@ class GPTB_OT_batch_flat_reproject(bpy.types.Operator):
@classmethod
def poll(cls, context):
return context.object and context.object.type == 'GPENCIL'
return context.object and context.object.type == 'GREASEPENCIL'
def execute(self, context):
for o in context.selected_objects:
if o.type != 'GPENCIL' or not o.select_get():
if o.type != 'GREASEPENCIL' or not o.select_get():
continue
batch_flat_reproject(o)
@ -132,12 +132,12 @@ class GPTB_OT_batch_flat_reproject(bpy.types.Operator):
### -- MENU ENTRY --
def flat_reproject_clean_menu(self, context):
if context.mode == 'EDIT_GPENCIL':
if context.mode == 'EDIT_GREASE_PENCIL':
self.layout.operator_context = 'INVOKE_REGION_WIN' # needed for popup (also works with 'INVOKE_DEFAULT')
self.layout.operator('gp.batch_flat_reproject', icon='KEYTYPE_JITTER_VEC')
def flat_reproject_context_menu(self, context):
if context.mode == 'EDIT_GPENCIL' and context.scene.tool_settings.gpencil_selectmode_edit == 'STROKE':
if context.mode == 'EDIT_GREASE_PENCIL' and context.scene.tool_settings.gpencil_selectmode_edit == 'STROKE':
self.layout.operator_context = 'INVOKE_REGION_WIN' # needed for popup
self.layout.operator('gp.batch_flat_reproject', icon='KEYTYPE_JITTER_VEC')
@ -149,12 +149,12 @@ def register():
for cl in classes:
bpy.utils.register_class(cl)
# bpy.types.VIEW3D_MT_gpencil_edit_context_menu.append(flat_reproject_context_menu)
# bpy.types.VIEW3D_MT_grease_pencil_edit_context_menu.append(flat_reproject_context_menu)
# bpy.types.GPENCIL_MT_cleanup.append(flat_reproject_clean_menu)
def unregister():
# bpy.types.GPENCIL_MT_cleanup.remove(flat_reproject_clean_menu)
# bpy.types.VIEW3D_MT_gpencil_edit_context_menu.remove(flat_reproject_context_menu)
# bpy.types.VIEW3D_MT_grease_pencil_edit_context_menu.remove(flat_reproject_context_menu)
for cl in reversed(classes):
bpy.utils.unregister_class(cl)

View File

@ -135,8 +135,8 @@ class GPTB_OT_go_to_object(bpy.types.Operator):
bpy.ops.object.mode_set(mode='POSE', toggle=False)
self.report({'INFO'}, f'Back to pose mode, {obj.name}')
elif obj.type == 'GPENCIL':
bpy.ops.object.mode_set(mode='PAINT_GPENCIL', toggle=False)
elif obj.type == 'GREASEPENCIL':
bpy.ops.object.mode_set(mode='PAINT_GREASE_PENCIL', toggle=False)
else:
self.report({'INFO'}, f'Back to object mode, {obj.name}')

View File

@ -79,7 +79,7 @@ class GPTB_OT_rename_data_from_obj(Operator):
@classmethod
def poll(cls, context):
return context.object and context.object.type == 'GPENCIL'
return context.object and context.object.type == 'GREASEPENCIL'
def execute(self, context):
if not self.rename_all:
@ -93,7 +93,7 @@ class GPTB_OT_rename_data_from_obj(Operator):
else:
oblist = []
for o in context.scene.objects:
if o.type == 'GPENCIL':
if o.type == 'GREASEPENCIL':
if o.name == o.data.name:
continue
oblist.append(f'{o.data.name} -> {o.name}')
@ -250,7 +250,7 @@ class GPTB_OT_draw_cam(Operator):
drawcam.parent = act
vec = Vector((0,1,0))
if act.type == 'GPENCIL':
if act.type == 'GREASEPENCIL':
#change vector according to alignement
vec = get_gp_alignement_vector(context)
@ -427,15 +427,15 @@ class GPTB_OT_toggle_mute_animation(Operator):
pool = context.scene.objects
for o in pool:
if self.mode == 'GPENCIL' and o.type != 'GPENCIL':
if self.mode == 'GREASEPENCIL' and o.type != 'GREASEPENCIL':
continue
if self.mode == 'OBJECT' and o.type in ('GPENCIL', 'CAMERA'):
if self.mode == 'OBJECT' and o.type in ('GREASEPENCIL', 'CAMERA'):
continue
if self.mode == 'CAMERA' and o.type != 'CAMERA':
continue
# mute attribute animation for GP and cameras
if o.type in ('GPENCIL', 'CAMERA') and o.data.animation_data:
if o.type in ('GREASEPENCIL', 'CAMERA') and o.data.animation_data:
gp_act = o.data.animation_data.action
if gp_act:
print(f'\n---{o.name} data:')
@ -473,9 +473,9 @@ class GPTB_OT_toggle_hide_gp_modifier(Operator):
else:
pool = context.scene.objects
for o in pool:
if o.type != 'GPENCIL':
if o.type != 'GREASEPENCIL':
continue
for m in o.grease_pencil_modifiers:
for m in o.modifier:
# skip modifier that are not visible in render
if not m.show_render:
continue
@ -506,12 +506,12 @@ class GPTB_OT_list_disabled_anims(Operator):
pool = context.scene.objects
for o in pool:
# if self.skip_gp and o.type == 'GPENCIL':
# if self.skip_gp and o.type == 'GREASEPENCIL':
# continue
# if self.skip_obj and o.type != 'GPENCIL':
# if self.skip_obj and o.type != 'GREASEPENCIL':
# continue
if o.type == 'GPENCIL':
if o.type == 'GREASEPENCIL':
if o.data.animation_data:
gp_act = o.data.animation_data.action
if gp_act:
@ -611,7 +611,7 @@ class GPTB_OT_clear_active_frame(Operator):
@classmethod
def poll(cls, context):
return context.object and context.object.type == 'GPENCIL'
return context.object and context.object.type == 'GREASEPENCIL'
def execute(self, context):
obj = context.object
@ -619,18 +619,18 @@ class GPTB_OT_clear_active_frame(Operator):
if not l:
self.report({'ERROR'}, 'No layers')
return {'CANCELLED'}
f = l.active_frame
f = l.current_frame()
if not f:
self.report({'ERROR'}, 'No active frame')
return {'CANCELLED'}
ct = len(f.strokes)
ct = len(f.drawing.strokes)
if not ct:
self.report({'ERROR'}, 'Active frame already empty')
return {'CANCELLED'}
for s in reversed(f.strokes):
f.strokes.remove(s)
for s in reversed(f.drawing.strokes):
f.drawing.strokes.remove(s)
self.report({'INFO'}, f'Cleared active frame ({ct} strokes removed)')
return {'FINISHED'}
@ -644,7 +644,7 @@ class GPTB_OT_check_canvas_alignement(Operator):
@classmethod
def poll(cls, context):
# if lock_axis is 'VIEW' then the draw axis is always aligned
return context.object and context.object.type == 'GPENCIL'# and context.scene.tool_settings.gpencil_sculpt.lock_axis != 'VIEW'
return context.object and context.object.type == 'GREASEPENCIL'# and context.scene.tool_settings.gpencil_sculpt.lock_axis != 'VIEW'
def execute(self, context):
if context.scene.tool_settings.gpencil_sculpt.lock_axis == 'VIEW':
@ -673,7 +673,7 @@ class GPTB_OT_step_select_frames(Operator):
@classmethod
def poll(cls, context):
return context.object and context.object.type == 'GPENCIL'
return context.object and context.object.type == 'GREASEPENCIL'
start : bpy.props.IntProperty(name='Start Frame',
description='Start frame of the step animation',

View File

@ -8,7 +8,7 @@ def get_layer_list(self, context):
return [('None', 'None','None')]
if not context.object:
return [('None', 'None','None')]
return [(l.info, l.info, '') for l in context.object.data.layers if l != context.object.data.layers.active]
return [(l.name, l.name, '') for l in context.object.data.layers if l != context.object.data.layers.active]
# try:
# except:
# return [("", "", "")]
@ -40,8 +40,8 @@ class GPTB_OT_duplicate_send_to_layer(Operator) :
@classmethod
def poll(cls, context):
return context.object and context.object.type == 'GPENCIL'\
and context.space_data.bl_rna.identifier == 'SpaceDopeSheetEditor' and context.space_data.ui_mode == 'GPENCIL'
return context.object and context.object.type == 'GREASEPENCIL'\
and context.space_data.bl_rna.identifier == 'SpaceDopeSheetEditor' and context.space_data.ui_mode == 'GREASEPENCIL'
# history : bpy.props.StringProperty(default='', options={'SKIP_SAVE'}) # need to have a variable to store (to get it in self)
@ -131,7 +131,7 @@ def unregister_keymaps():
def menu_duplicate_and_send_to_layer(self, context):
if context.space_data.ui_mode == 'GPENCIL':
if context.space_data.ui_mode == 'GREASEPENCIL':
self.layout.operator_context = 'INVOKE_REGION_WIN'
self.layout.operator('gp.duplicate_send_to_layer', text='Move Keys To Layer').delete_source = True
self.layout.operator('gp.duplicate_send_to_layer', text='Copy Keys To Layer')
@ -148,7 +148,7 @@ def register():
bpy.utils.register_class(cls)
register_keymaps()
bpy.types.DOPESHEET_MT_gpencil_key.append(menu_duplicate_and_send_to_layer)
bpy.types.DOPESHEET_MT_key.append(menu_duplicate_and_send_to_layer)
bpy.types.DOPESHEET_MT_context_menu.append(menu_duplicate_and_send_to_layer)
@ -157,7 +157,7 @@ def unregister():
return
bpy.types.DOPESHEET_MT_context_menu.remove(menu_duplicate_and_send_to_layer)
bpy.types.DOPESHEET_MT_gpencil_key.remove(menu_duplicate_and_send_to_layer)
bpy.types.DOPESHEET_MT_key.remove(menu_duplicate_and_send_to_layer)
unregister_keymaps()
for cls in reversed(classes):

View File

@ -10,7 +10,7 @@ class GPTB_OT_jump_gp_keyframe(bpy.types.Operator):
@classmethod
def poll(cls, context):
return context.object and context.object.type == 'GPENCIL'
return context.object and context.object.type == 'GREASEPENCIL'
next : BoolProperty(
name="Next GP keyframe", description="Go to next active GP keyframe",

View File

@ -30,7 +30,7 @@ def layer_name_build(layer, prefix='', desc='', suffix=''):
prefs = get_addon_prefs()
sep = prefs.separator
name = old = layer.info
name = old = layer.name
pattern = PATTERN.replace('_', sep) # set separator
@ -69,7 +69,7 @@ def layer_name_build(layer, prefix='', desc='', suffix=''):
# check if name is available without the increment ending
new = f'{grp}{tag}{name}{sfix}'
layer.info = new
layer.name = new
## update name in modifier targets
if old != new:
@ -78,11 +78,11 @@ def layer_name_build(layer, prefix='', desc='', suffix=''):
# maybe a more elegant way exists to find all objects users ?
# update Gpencil modifier targets
for mod in ob_user.grease_pencil_modifiers:
if not hasattr(mod, 'layer'):
for mod in ob_user.modifier:
if not hasattr(mod, 'layer_filter'):
continue
if mod.layer == old:
mod.layer = new
if mod.layer_filter == old:
mod.layer_filter = new
"""
def layer_name_build(layer, prefix='', prefix2='', desc='', suffix=''):
@ -93,7 +93,7 @@ def layer_name_build(layer, prefix='', prefix2='', desc='', suffix=''):
prefs = get_addon_prefs()
sep = prefs.separator
name = layer.info
name = layer.name
pattern = pattern.replace('_', sep) # set separator
@ -122,7 +122,7 @@ def layer_name_build(layer, prefix='', prefix2='', desc='', suffix=''):
p4 = sep + suffix.upper().strip()
new = f'{p1}{p2}{p3}{p4}'
layer.info = new
layer.name = new
"""
## multi-prefix solution (Caps letters)
@ -172,14 +172,14 @@ class GPTB_OT_layer_name_build(Operator):
def grp_toggle(l, mode='TOGGLE'):
'''take mode in (TOGGLE, GROUP, UNGROUP) '''
grp_item_id = ' - '
res = re.search(r'^(\s{1,3}-\s{0,3})(.*)', l.info)
res = re.search(r'^(\s{1,3}-\s{0,3})(.*)', l.name)
if not res and mode in ('TOGGLE', 'GROUP'):
# No gpr : add group prefix after stripping all space and dash
l.info = grp_item_id + l.info.lstrip(' -')
l.name = grp_item_id + l.name.lstrip(' -')
elif res and mode in ('TOGGLE', 'UNGROUP'):
# found : delete group prefix
l.info = res.group(2)
l.name = res.group(2)
class GPTB_OT_layer_group_toggle(Operator):
@ -220,18 +220,18 @@ class GPTB_OT_layer_new_group(Operator):
self.report({'ERROR'}, 'no layer active')
return {"CANCELLED"}
res = re.search(PATTERN, act.info)
res = re.search(PATTERN, act.name)
if not res:
self.report({'ERROR'}, 'Could not create a group name, create a layer manually')
return {"CANCELLED"}
name = res.group('name').strip(' -')
if not name:
self.report({'ERROR'}, f'No name found in {act.info}')
self.report({'ERROR'}, f'No name found in {act.name}')
return {"CANCELLED"}
if name in [l.info.strip(' -') for l in gpl]:
self.report({'WARNING'}, f'Name already exists: {act.info}')
if name in [l.name.strip(' -') for l in gpl]:
self.report({'WARNING'}, f'Name already exists: {act.name}')
return {"FINISHED"}
grp_toggle(act, mode='GROUP')
@ -261,9 +261,9 @@ def build_layers_targets_from_dopesheet(context):
if dopeset.show_only_selected:
pool = [o for o in context.selected_objects if o.type == 'GPENCIL']
pool = [o for o in context.selected_objects if o.type == 'GREASEPENCIL']
else:
pool = [o for o in context.scene.objects if o.type == 'GPENCIL']
pool = [o for o in context.scene.objects if o.type == 'GREASEPENCIL']
if not dopeset.show_hidden:
pool = [o for o in pool if o.visible_get()]
@ -272,7 +272,7 @@ def build_layers_targets_from_dopesheet(context):
# apply search filter
if dopeset.filter_text:
layer_pool = [l for l in layer_pool if (dopeset.filter_text.lower() in l.info.lower()) ^ dopeset.use_filter_invert]
layer_pool = [l for l in layer_pool if (dopeset.filter_text.lower() in l.name.lower()) ^ dopeset.use_filter_invert]
return layer_pool
@ -292,7 +292,7 @@ class GPTB_OT_select_set_same_prefix(Operator):
@classmethod
def poll(cls, context):
return context.object and context.object.type == 'GPENCIL'
return context.object and context.object.type == 'GREASEPENCIL'
mode : EnumProperty(default='SELECT', options={'SKIP_SAVE'},
items=(
@ -330,35 +330,35 @@ class GPTB_OT_select_set_same_prefix(Operator):
self.report({'ERROR'}, 'No active layer to base action')
return {"CANCELLED"}
print(f'Select/Set ref layer: {gp.name} > {gp.layers.active.info}')
print(f'Select/Set ref layer: {gp.name} > {gp.layers.active.name}')
res = re.search(PATTERN, act.info)
res = re.search(PATTERN, act.name)
if not res:
self.report({'ERROR'}, f'Error scanning {act.info}')
self.report({'ERROR'}, f'Error scanning {act.name}')
return {"CANCELLED"}
namespace = res.group('tag')
if not namespace:
self.report({'WARNING'}, f'No prefix detected in {act.info} with separator {sep}')
self.report({'WARNING'}, f'No prefix detected in {act.name} with separator {sep}')
return {"CANCELLED"}
if self.mode == 'SELECT':
## with split
# namespace = act.info.split(sep,1)[0]
# namespace_bool_list = [l.info.split(sep,1)[0] == namespace for l in gpl]
# namespace = act.name.split(sep,1)[0]
# namespace_bool_list = [l.name.split(sep,1)[0] == namespace for l in gpl]
## with reg # only active
# namespace_bool_list = [l.info.split(sep,1)[0] + sep == namespace for l in gpl]
# namespace_bool_list = [l.name.split(sep,1)[0] + sep == namespace for l in gpl]
# gpl.foreach_set('select', namespace_bool_list)
## don't work Need Foreach set per gp
# for l in pool:
# l.select = l.info.split(sep,1)[0] + sep == namespace
# l.select = l.name.split(sep,1)[0] + sep == namespace
for gp, layers in gp_dic.items():
# check namespace + restrict selection to visible layers according to filters
# TODO : Should use the regex pattern to detect and compare r.group('tag')
namespace_bool_list = [(l in layers) and (l.info.split(sep,1)[0] + sep == namespace) for l in gp.layers]
namespace_bool_list = [(l in layers) and (l.name.split(sep,1)[0] + sep == namespace) for l in gp.layers]
gp.layers.foreach_set('select', namespace_bool_list)
elif self.mode == 'SET':
@ -380,7 +380,7 @@ class GPTB_OT_select_set_same_color(Operator):
@classmethod
def poll(cls, context):
return context.object and context.object.type == 'GPENCIL'
return context.object and context.object.type == 'GREASEPENCIL'
mode : EnumProperty(default='SELECT', options={'SKIP_SAVE'},
items=(
@ -415,7 +415,7 @@ class GPTB_OT_select_set_same_color(Operator):
self.report({'ERROR'}, 'No active layer to base action')
return {"CANCELLED"}
print(f'Select/Set ref layer: {gp.name} > {gp.layers.active.info}')
print(f'Select/Set ref layer: {gp.name} > {gp.layers.active.name}')
color = act.channel_color
if self.mode == 'SELECT':
## NEED FOREACH TO APPLY SELECT
@ -426,7 +426,7 @@ class GPTB_OT_select_set_same_color(Operator):
# On multiple objects -- don't work, need foreach
# for l in pool:
# print(l.id_data.name, l.info, l.channel_color == act.channel_color)
# print(l.id_data.name, l.name, l.channel_color == act.channel_color)
# l.select = l.channel_color == act.channel_color
"""
@ -463,38 +463,38 @@ def replace_layer_name(target, replacement, selected_only=True, prefix_only=True
gpl = bpy.context.object.data.layers
if selected_only:
lays = [l for l in gpl if l.select] # exclude : l.info != 'background'
lays = [l for l in gpl if l.select] # exclude : l.name != 'background'
else:
lays = [l for l in gpl] # exclude : if l.info != 'background'
lays = [l for l in gpl] # exclude : if l.name != 'background'
ct = 0
for l in lays:
old = l.info
old = l.name
if regex:
new = re.sub(target, replacement, l.info)
new = re.sub(target, replacement, l.name)
if old != new:
l.info = new
l.name = new
print('rename:', old, '-->', new)
ct += 1
continue
if prefix_only:
if not sep in l.info:
if not sep in l.name:
# only if separator exists
continue
splited = l.info.split(sep)
splited = l.name.split(sep)
prefix = splited[0]
new_prefix = prefix.replace(target, replacement)
if prefix != new_prefix:
splited[0] = new_prefix
l.info = sep.join(splited)
print('rename:', old, '-->', l.info)
l.name = sep.join(splited)
print('rename:', old, '-->', l.name)
ct += 1
else:
new = l.info.replace(target, replacement)
new = l.name.replace(target, replacement)
if old != new:
l.info = new
l.name = new
print('rename:', old, '-->', new)
ct += 1
return ct
@ -507,7 +507,7 @@ class GPTB_OT_rename_gp_layer(Operator):
@classmethod
def poll(cls, context):
return context.object and context.object.type == 'GPENCIL'
return context.object and context.object.type == 'GREASEPENCIL'
find: StringProperty(name="Find", description="Name to replace", default="", maxlen=0, options={'ANIMATABLE'}, subtype='NONE')
replace: StringProperty(name="Repl", description="New name placed", default="", maxlen=0, options={'ANIMATABLE'}, subtype='NONE')
@ -548,7 +548,7 @@ class GPTB_OT_rename_gp_layer(Operator):
## --- UI layer panel---
def layer_name_builder_ui(self, context):
'''appended to DATA_PT_gpencil_layers'''
'''appended to DATA_PT_grease_pencil_layers'''
prefs = get_addon_prefs()
if not prefs.show_prefix_buttons:
@ -557,7 +557,7 @@ def layer_name_builder_ui(self, context):
return
layout = self.layout
# {'EDIT_GPENCIL', 'PAINT_GPENCIL','SCULPT_GPENCIL','WEIGHT_GPENCIL', 'VERTEX_GPENCIL'}
# {'EDIT_GREASE_PENCIL', 'PAINT_GREASE_PENCIL','SCULPT_GREASE_PENCIL','WEIGHT_GREASE_PENCIL', 'VERTEX_GPENCIL'}
# layout.separator()
col = layout.column()
@ -633,7 +633,7 @@ def gpencil_dopesheet_header(self, context):
'''to append in DOPESHEET_HT_header'''
layout = self.layout
st = context.space_data
if st.mode != 'GPENCIL':
if st.mode != 'GREASEPENCIL':
return
row = layout.row(align=True)
@ -653,7 +653,7 @@ def gpencil_layer_dropdown_menu(self, context):
def obj_layer_name_callback():
'''assign layer name properties so user an tweak it'''
ob = bpy.context.object
if not ob or ob.type != 'GPENCIL':
if not ob or ob.type != 'GREASEPENCIL':
return
if not ob.data.layers.active:
return
@ -663,7 +663,7 @@ def obj_layer_name_callback():
for l in ob.data.layers:
l.select = l == ob.data.layers.active
res = re.search(PATTERN, ob.data.layers.active.info.strip())
res = re.search(PATTERN, ob.data.layers.active.name.strip())
if not res:
return
if not res.group('name'):
@ -736,7 +736,7 @@ class GPTB_OT_add_gp_layer_with_rename(Operator):
bl_options = {"REGISTER", "UNDO"}
@classmethod
def poll(cls, context):
return context.object and context.object.type == 'GPENCIL'
return context.object and context.object.type == 'GREASEPENCIL'
def execute(self, context):
add_layer(context)
@ -750,7 +750,7 @@ class GPTB_OT_add_gp_layer(Operator):
bl_options = {"REGISTER", "UNDO"}
@classmethod
def poll(cls, context):
return context.object and context.object.type == 'GPENCIL'
return context.object and context.object.type == 'GREASEPENCIL'
def execute(self, context):
add_layer(context)
@ -776,7 +776,7 @@ def register_keymaps():
##---# F2 rename calls
## Direct rename active layer in Paint mode
km = addon.keymaps.new(name = "Grease Pencil Stroke Paint Mode", space_type = "EMPTY")
km = addon.keymaps.new(name = "Grease Pencil Paint Mode", space_type = "EMPTY")
kmi = km.keymap_items.new('wm.call_panel', type='F2', value='PRESS')
kmi.properties.name = 'GPTB_PT_layer_name_ui'
kmi.properties.keep_open = False
@ -817,7 +817,7 @@ def register():
for cls in classes:
bpy.utils.register_class(cls)
bpy.types.DATA_PT_gpencil_layers.prepend(layer_name_builder_ui)
bpy.types.DATA_PT_grease_pencil_layers.prepend(layer_name_builder_ui)
bpy.types.DOPESHEET_HT_header.append(gpencil_dopesheet_header)
bpy.types.GPENCIL_MT_layer_context_menu.append(gpencil_layer_dropdown_menu)
bpy.app.handlers.load_post.append(subscribe_layer_change_handler)
@ -831,7 +831,7 @@ def unregister():
bpy.app.handlers.load_post.remove(subscribe_layer_change_handler)
bpy.types.GPENCIL_MT_layer_context_menu.remove(gpencil_layer_dropdown_menu)
bpy.types.DOPESHEET_HT_header.remove(gpencil_dopesheet_header)
bpy.types.DATA_PT_gpencil_layers.remove(layer_name_builder_ui)
bpy.types.DATA_PT_grease_pencil_layers.remove(layer_name_builder_ui)
for cls in reversed(classes):
bpy.utils.unregister_class(cls)

View File

@ -127,7 +127,7 @@ addon_keymaps = []
def register_keymap():
addon = bpy.context.window_manager.keyconfigs.addon
km = addon.keymaps.new(name = "Grease Pencil Stroke Paint Mode", space_type = "EMPTY")
km = addon.keymaps.new(name = "Grease Pencil Paint Mode", space_type = "EMPTY")
kmi = km.keymap_items.new('gp.layer_nav', type='PAGE_UP', value='PRESS')
kmi.properties.direction = 'UP'

View File

@ -14,7 +14,7 @@ class GP_OT_pick_closest_layer(Operator):
@classmethod
def poll(cls, context):
return context.object and context.object.type == 'GPENCIL' and context.mode == 'PAINT_GPENCIL'
return context.object and context.object.type == 'GREASEPENCIL' and context.mode == 'PAINT_GREASE_PENCIL'
stroke_filter : bpy.props.EnumProperty(name='Target',
default='STROKE',
@ -47,7 +47,7 @@ class GP_OT_pick_closest_layer(Operator):
def draw(self, context):
layout = self.layout
if context.object.data.layers.active:
layout.label(text=f'Layer: {context.object.data.layers.active.info}')
layout.label(text=f'Layer: {context.object.data.layers.active.name}')
layout.prop(self, 'stroke_filter')
def modal(self, context, event):
@ -74,31 +74,31 @@ class GP_OT_pick_closest_layer(Operator):
self.inv_mat = self.ob.matrix_world.inverted()
self.point_pair = []
if gp.use_multiedit:
if context.scene.tool_settings.use_grease_pencil_multi_frame_editing:
for layer_id, l in enumerate(gp.layers):
if l.hide:# l.lock or
if l.hide: # l.lock or
continue
for f in l.frames:
if not f.select:
continue
for s in f.strokes:
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
self.point_pair += [(Vector((*location_to_region(mat @ p.co), 0)), layer_id) for p in s.points]
self.point_pair += [(Vector((*location_to_region(mat @ p.position), 0)), layer_id) for p in s.points]
else:
# [s for l in gp.layers if not l.lock and not l.hide for s in l.active_frame.stokes]
# [s for l in gp.layers if not l.lock and not l.hide for s in l.current_frame().stokes]
for layer_id, l in enumerate(gp.layers):
if l.hide or not l.active_frame:# l.lock or
if l.hide or not l.current_frame(): # l.lock or
continue
for s in l.active_frame.strokes:
for s in l.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
self.point_pair += [(Vector((*location_to_region(mat @ p.co), 0)), layer_id) for p in s.points]
self.point_pair += [(Vector((*location_to_region(mat @ p.position), 0)), layer_id) for p in s.points]
if not self.point_pair:
self.report({'ERROR'}, 'No stroke found, maybe layers are locked or hidden')
@ -117,7 +117,7 @@ class GP_OT_pick_closest_layer(Operator):
## debug show trigger time
# print(f'Trigger time {time() - self.t0:.3f}')
# print(f'Search time {time() - t1:.3f}')
self.report({'INFO'}, f'Layer: {self.ob.data.layers.active.info}')
self.report({'INFO'}, f'Layer: {self.ob.data.layers.active.name}')
return {'FINISHED'}
@ -125,7 +125,7 @@ class GP_OT_pick_closest_layer(Operator):
addon_keymaps = []
def register_keymaps():
addon = bpy.context.window_manager.keyconfigs.addon
km = addon.keymaps.new(name = "Grease Pencil Stroke Paint Mode", space_type = "EMPTY", region_type='WINDOW')
km = addon.keymaps.new(name = "Grease Pencil Paint Mode", space_type = "EMPTY", region_type='WINDOW')
kmi = km.keymap_items.new(
# name="",

View File

@ -11,7 +11,7 @@ from . import utils
# return [('None', 'None','None')]
# if not context.object:
# return [('None', 'None','None')]
# return [(l.info, l.info, '') for l in context.object.data.layers] # if l != context.object.data.layers.active
# return [(l.name, l.name, '') for l in context.object.data.layers] # if l != context.object.data.layers.active
## in Class
# bl_property = "layers_enum"
@ -39,7 +39,7 @@ class GPTB_OT_move_material_to_layer(Operator) :
@classmethod
def poll(cls, context):
return context.object and context.object.type == 'GPENCIL'
return context.object and context.object.type == 'GREASEPENCIL'
def invoke(self, context, event):
if self.layer_name:
@ -72,8 +72,8 @@ class GPTB_OT_move_material_to_layer(Operator) :
icon = 'GREASEPENCIL' if l == context.object.data.layers.active else 'BLANK1'
row = col.row()
row.alignment = 'LEFT'
op = col.operator('gp.move_material_to_layer', text=l.info, icon=icon, emboss=False)
op.layer_name = l.info
op = col.operator('gp.move_material_to_layer', text=l.name, icon=icon, emboss=False)
op.layer_name = l.name
op.copy = self.copy
def execute(self, context):
@ -82,7 +82,7 @@ class GPTB_OT_move_material_to_layer(Operator) :
return {'CANCELLED'}
## Active + selection
pool = [o for o in bpy.context.selected_objects if o.type == 'GPENCIL']
pool = [o for o in bpy.context.selected_objects if o.type == 'GREASEPENCIL']
if not context.object in pool:
pool.append(context.object)
@ -116,7 +116,7 @@ class GPTB_OT_move_material_to_layer(Operator) :
continue
for f in l.frames:
## skip if no stroke has active material
if not next((s for s in f.strokes if s.material_index == mat_index), None):
if not next((s for s in f.drawing.strokes if s.material_index == mat_index), None):
continue
## Get/Create a destination frame and keep a reference to it
if not (dest_key := key_dict.get(f.frame_number)):
@ -126,7 +126,7 @@ class GPTB_OT_move_material_to_layer(Operator) :
print(f'{ob.name} : frame {f.frame_number}')
## Replicate strokes in dest_keys
stroke_to_delete = []
for s in f.strokes:
for s in f.drawing.strokes:
if s.material_index == mat_index:
utils.copy_stroke_to_frame(s, dest_key)
stroke_to_delete.append(s)
@ -142,7 +142,7 @@ class GPTB_OT_move_material_to_layer(Operator) :
## Remove from source frame (f)
if not self.copy:
for s in reversed(stroke_to_delete):
f.strokes.remove(s)
f.drawing.strokes.remove(s)
## ? Remove frame if layer is empty ? -> probably not, will show previous frame
@ -165,7 +165,7 @@ class GPTB_OT_move_material_to_layer(Operator) :
# def menu_duplicate_and_send_to_layer(self, context):
# if context.space_data.ui_mode == 'GPENCIL':
# if context.space_data.ui_mode == 'GREASEPENCIL':
# self.layout.operator_context = 'INVOKE_REGION_WIN'
# self.layout.operator('gp.duplicate_send_to_layer', text='Move Keys To Layer').delete_source = True
# self.layout.operator('gp.duplicate_send_to_layer', text='Copy Keys To Layer')

View File

@ -33,20 +33,20 @@ class GP_OT_pick_closest_material(Operator):
@classmethod
def poll(cls, context):
return context.object and context.object.type == 'GPENCIL' and context.mode == 'PAINT_GPENCIL'
return context.object and context.object.type == 'GREASEPENCIL' and context.mode == 'PAINT_GREASE_PENCIL'
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
point_pair = [(p.position, 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
## Get 3D coordinate on drawing plane according to mouse 2d.position on flat 2d drawing
_ob, hit, _plane_no = get_3d_coord_on_drawing_plane_from_2d(context, self.init_mouse)
if not hit:
@ -62,7 +62,7 @@ class GP_OT_pick_closest_material(Operator):
## find point index in stroke
self.idx = None
for i, p in enumerate(s.points):
if p.co == co:
if p.position == co:
self.idx = i
break
@ -77,22 +77,22 @@ class GP_OT_pick_closest_material(Operator):
self.stroke_list = []
self.inv_mat = self.ob.matrix_world.inverted()
if self.gp.use_multiedit:
if context.scene.tool_settings.use_grease_pencil_multi_frame_editing:
for l in self.gp.layers:
if l.hide:# l.lock or
continue
for f in l.frames:
if not f.select:
continue
for s in f.strokes:
for s in f.drawing.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]
# [s for l in self.gp.layers if not l.lock and not l.hide for s in l.current_frame().stokes]
for l in self.gp.layers:
if l.hide or not l.active_frame:# l.lock or
if l.hide or not l.current_frame():# l.lock or
continue
for s in l.active_frame.strokes:
for s in l.current_frame().drawing.strokes:
self.stroke_list.append(s)
if self.fill_only:
@ -116,8 +116,8 @@ class GP_OT_pick_closest_material(Operator):
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
self.depth = self.ob.matrix_world @ self.stroke.points[self.idx].position
self.init_pos = [p.position.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]
@ -144,7 +144,7 @@ class GP_OT_pick_closest_material(Operator):
# 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]
# # self.stroke.points[i].position = self.init_pos[i]
# context.area.tag_redraw()
# return {'CANCELLED'}
@ -159,7 +159,7 @@ class GP_OT_pick_closest_material(Operator):
@classmethod
def poll(cls, context):
return context.object and context.object.type == 'GPENCIL' and context.mode == 'PAINT_GPENCIL'
return context.object and context.object.type == 'GREASEPENCIL' and context.mode == 'PAINT_GREASE_PENCIL'
# fill_only : bpy.props.BoolProperty(default=False, options={'SKIP_SAVE'})
stroke_filter : bpy.props.EnumProperty(default='FILL',
@ -172,7 +172,7 @@ class GP_OT_pick_closest_material(Operator):
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
point_pair = [(p.position, 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):
@ -195,7 +195,7 @@ class GP_OT_pick_closest_material(Operator):
## find point index in stroke
self.idx = None
for i, p in enumerate(s.points):
if p.co == co:
if p.position == co:
self.idx = i
break
@ -233,22 +233,22 @@ class GP_OT_pick_closest_material(Operator):
self.stroke_list = []
self.inv_mat = self.ob.matrix_world.inverted()
if gp.use_multiedit:
if context.scene.tool_settings.use_grease_pencil_multi_frame_editing:
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:
for s in f.drawing.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]
# [s for l in gp.layers if not l.lock and not l.hide for s in l.current_frame().stokes]
for l in gp.layers:
if l.hide or not l.active_frame:# l.lock or
if l.hide or not l.current_frame():# l.lock or
continue
for s in l.active_frame.strokes:
for s in l.current_frame().drawing.strokes:
self.stroke_list.append(s)
if self.stroke_filter == 'FILL':
@ -274,8 +274,8 @@ class GP_OT_pick_closest_material(Operator):
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.depth = self.ob.matrix_world @ stroke.points[self.idx].position
# self.init_pos = [p.position.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)
@ -291,7 +291,7 @@ 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 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="",

View File

@ -42,7 +42,7 @@ class GPTB_OT_load_default_palette(bpy.types.Operator):
# path_to_pal : bpy.props.StringProperty(name="paht to palette", description="path to the palette", default="")
@classmethod
def poll(cls, context):
return context.object and context.object.type == 'GPENCIL'
return context.object and context.object.type == 'GREASEPENCIL'
def execute(self, context):
# Start Clean (delete unuesed sh*t)
@ -82,7 +82,7 @@ class GPTB_OT_load_palette(bpy.types.Operator, ImportHelper):
# path_to_pal : bpy.props.StringProperty(name="paht to palette", description="path to the palette", default="")
@classmethod
def poll(cls, context):
return context.object and context.object.type == 'GPENCIL'
return context.object and context.object.type == 'GREASEPENCIL'
filename_ext = '.json'
@ -110,7 +110,7 @@ class GPTB_OT_save_palette(bpy.types.Operator, ExportHelper):
# path_to_pal : bpy.props.StringProperty(name="paht to palette", description="path to the palette", default="")
@classmethod
def poll(cls, context):
return context.object and context.object.type == 'GPENCIL'
return context.object and context.object.type == 'GREASEPENCIL'
filter_glob: bpy.props.StringProperty(default='*.json', options={'HIDDEN'})#*.jpg;*.jpeg;*.png;*.tif;*.tiff;*.bmp
@ -169,7 +169,7 @@ def load_blend_palette(context, filepath):
print(f'-- import palette from : {filepath} --')
for ob in context.selected_objects:
if ob.type != 'GPENCIL':
if ob.type != 'GREASEPENCIL':
print(f'{ob.name} not a GP object')
continue
@ -224,7 +224,7 @@ class GPTB_OT_load_blend_palette(bpy.types.Operator, ImportHelper):
# path_to_pal : bpy.props.StringProperty(name="paht to palette", description="path to the palette", default="")
@classmethod
def poll(cls, context):
return context.object and context.object.type == 'GPENCIL'
return context.object and context.object.type == 'GREASEPENCIL'
filename_ext = '.blend'
@ -252,7 +252,7 @@ class GPTB_OT_copy_active_to_selected_palette(bpy.types.Operator):
# path_to_pal : bpy.props.StringProperty(name="paht to palette", description="path to the palette", default="")
@classmethod
def poll(cls, context):
return context.object and context.object.type == 'GPENCIL'
return context.object and context.object.type == 'GREASEPENCIL'
def execute(self, context):
ob = context.object
@ -260,7 +260,7 @@ class GPTB_OT_copy_active_to_selected_palette(bpy.types.Operator):
self.report({'ERROR'}, 'No materials to transfer')
return {"CANCELLED"}
selection = [o for o in context.selected_objects if o.type == 'GPENCIL' and o != ob]
selection = [o for o in context.selected_objects if o.type == 'GREASEPENCIL' and o != ob]
if not selection:
self.report({'ERROR'}, 'Need to have other Grease pencil objects selected to receive active object materials')
@ -313,7 +313,7 @@ class GPTB_OT_clean_material_stack(bpy.types.Operator):
@classmethod
def poll(cls, context):
return context.object and context.object.type == 'GPENCIL'
return context.object and context.object.type == 'GREASEPENCIL'
def invoke(self, context, event):
self.ob = context.object
@ -354,7 +354,7 @@ class GPTB_OT_clean_material_stack(bpy.types.Operator):
import re
diff_ct = 0
todel = []
if ob.type != 'GPENCIL':
if ob.type != 'GREASEPENCIL':
return
if not hasattr(ob, 'material_slots'):
return
@ -410,7 +410,7 @@ class GPTB_OT_clean_material_stack(bpy.types.Operator):
# iterate in all strokes and replace with new_mat_id
for l in ob.data.layers:
for f in l.frames:
for s in f.strokes:
for s in f.drawing.strokes:
if s.material_index == i:
s.material_index = new_mat_id
@ -427,7 +427,7 @@ class GPTB_OT_clean_material_stack(bpy.types.Operator):
# if self.skip_binded_empty_slots:
# for l in ob.data.layers:
# for f in l.frames:
# for s in f.strokes:
# for s in f.drawing.strokes:
# if s.material_index == i:
# is_binded = True
# break

View File

@ -45,7 +45,7 @@ class GPTB_OT_import_obj_palette(Operator):
def execute(self, context):
## get targets
selection = [o for o in context.selected_objects if o.type == 'GPENCIL']
selection = [o for o in context.selected_objects if o.type == 'GREASEPENCIL']
if not selection:
self.report({'ERROR'}, 'Need to have at least one GP object selected in scene')
return {"CANCELLED"}
@ -98,7 +98,7 @@ class GPTB_OT_import_obj_palette(Operator):
return {"CANCELLED"}
for i in range(len(linked_objs))[::-1]: # reversed(range(len(l))) / range(len(l))[::-1]
if linked_objs[i].type != 'GPENCIL':
if linked_objs[i].type != 'GREASEPENCIL':
print(f'{linked_objs[i].name} type is "{linked_objs[i].type}"')
bpy.data.objects.remove(linked_objs.pop(i))

View File

@ -74,7 +74,7 @@ class GPT_OT_auto_tint_gp_layers(bpy.types.Operator):
# namespace_order
namespaces=[]
for l in gpl:
ns= l.info.lower().split(separator, 1)[0]
ns= l.name.lower().split(separator, 1)[0]
if ns not in namespaces:
namespaces.append(ns)
@ -88,14 +88,14 @@ class GPT_OT_auto_tint_gp_layers(bpy.types.Operator):
### step from 0.1 to 0.9
for i, l in enumerate(gpl):
if l.info.lower() not in ('background',):
if l.name.lower() not in ('background',):
print()
print('>', l.info)
ns= l.info.lower().split(separator, 1)[0]#get namespace from separator
print('>', l.name)
ns= l.name.lower().split(separator, 1)[0]#get namespace from separator
print("namespace", ns)#Dbg
if context.scene.gptoolprops.autotint_namespace:
h = get_hue_by_name(ns, hue_offset)#l.info == individuels
h = get_hue_by_name(ns, hue_offset)#l.name == individuels
else:
h = translate_range((i + hue_offset/100)%layer_ct, 0, layer_ct, 0.1, 0.9)

View File

@ -25,7 +25,7 @@ if all_strokes:
l.hide = False
l.lock = False
l.lock_frame = False
bpy.ops.object.mode_set(mode='EDIT_GPENCIL')
bpy.ops.object.mode_set(mode='EDIT')
for fnum in frame_list:
@ -54,7 +54,7 @@ def batch_reproject(obj, proj_type='VIEW', all_strokes=True, restore_frame=False
plan_co, plane_no = utils.get_gp_draw_plane(obj, orient=proj_type)
frame_list = [f.frame_number for l in obj.data.layers for f in l.frames if len(f.strokes)]
frame_list = [f.frame_number for l in obj.data.layers for f in l.frames if len(f.drawing.strokes)]
frame_list = list(set(frame_list))
frame_list.sort()
@ -78,7 +78,7 @@ def batch_reproject(obj, proj_type='VIEW', all_strokes=True, restore_frame=False
f = next((f for f in l.frames if f.frame_number == i), None)
if f is None:
continue
for s in f.strokes:
for s in f.drawing.strokes:
## Batch matrix apply (Here is slower than list comprehension).
# nb_points = len(s.points)
# coords = np.empty(nb_points * 3, dtype='float64')
@ -86,13 +86,13 @@ def batch_reproject(obj, proj_type='VIEW', all_strokes=True, restore_frame=False
# world_co_3d = utils.matrix_transform(coords.reshape((nb_points, 3)), matrix)
## list comprehension method
world_co_3d = [obj.matrix_world @ p.co for p in s.points]
world_co_3d = [obj.matrix_world @ p.position for p in s.points]
new_world_co_3d = [intersect_line_plane(origin, p, plan_co, plane_no) for p in world_co_3d]
## Basic method (Slower than foreach_set)
# for i, p in enumerate(s.points):
# p.co = obj.matrix_world.inverted() @ new_world_co_3d[i]
# p.position = obj.matrix_world.inverted() @ new_world_co_3d[i]
## Ravel new coordinate on the fly
new_local_coords = [axis for p in new_world_co_3d for axis in matrix_inv @ p]
@ -139,9 +139,9 @@ def align_global(reproject=True, ref=None, all_strokes=True):
# world_coords = []
for l in o.data.layers:
for f in l.frames:
for s in f.strokes:
for s in f.drawing.strokes:
## foreach
coords = [p.co @ mat.inverted() @ new_mat for p in s.points]
coords = [p.position @ mat.inverted() @ new_mat for p in s.points]
# print('coords: ', coords)
# print([co for v in coords for co in v])
s.points.foreach_set('co', [co for v in coords for co in v])
@ -152,11 +152,11 @@ def align_global(reproject=True, ref=None, all_strokes=True):
# for p in s.points:
## GOOD :
# world_co = mat @ p.co
# p.co = new_mat.inverted() @ world_co
# world_co = mat @ p.position
# p.position = new_mat.inverted() @ world_co
## GOOD :
# p.co = p.co @ mat.inverted() @ new_mat
# p.position = p.position @ mat.inverted() @ new_mat
if o.parent:
o.matrix_world = new_mat
@ -216,9 +216,9 @@ def align_all_frames(reproject=True, ref=None, all_strokes=True):
scale_mat = get_scale_matrix(o_scale)
new_mat = loc_mat @ rot_mat @ scale_mat
for s in f.strokes:
for s in f.drawing.strokes:
## foreach
coords = [p.co @ mat.inverted() @ new_mat for p in s.points]
coords = [p.position @ mat.inverted() @ new_mat for p in s.points]
# print('coords: ', coords)
# print([co for v in coords for co in v])
s.points.foreach_set('co', [co for v in coords for co in v])
@ -267,7 +267,7 @@ class GPTB_OT_realign(bpy.types.Operator):
@classmethod
def poll(cls, context):
return context.object and context.object.type == 'GPENCIL'
return context.object and context.object.type == 'GREASEPENCIL'
reproject : bpy.props.BoolProperty(
name='Reproject', default=True,
@ -283,7 +283,7 @@ class GPTB_OT_realign(bpy.types.Operator):
## add option to bake strokes if rotation anim is not constant ? might generate too many Keyframes
def invoke(self, context, event):
if context.object.data.use_multiedit:
if context.scene.tool_settings.use_grease_pencil_multi_frame_editing:
self.report({'ERROR'}, 'Does not work in Multiframe mode')
return {"CANCELLED"}
@ -365,7 +365,7 @@ class GPTB_OT_batch_reproject_all_frames(bpy.types.Operator):
@classmethod
def poll(cls, context):
return context.object and context.object.type == 'GPENCIL'
return context.object and context.object.type == 'GREASEPENCIL'
all_strokes : bpy.props.BoolProperty(
name='All Strokes', default=True,
@ -383,7 +383,7 @@ class GPTB_OT_batch_reproject_all_frames(bpy.types.Operator):
default='CURRENT')
def invoke(self, context, event):
if context.object.data.use_multiedit:
if context.scene.tool_settings.use_grease_pencil_multi_frame_editing:
self.report({'ERROR'}, 'Does not work in Multi-edit')
return {"CANCELLED"}
return context.window_manager.invoke_props_dialog(self)
@ -432,12 +432,12 @@ class GPTB_OT_batch_reproject_all_frames(bpy.types.Operator):
### -- MENU ENTRY --
def reproject_clean_menu(self, context):
if context.mode == 'EDIT_GPENCIL':
if context.mode == 'EDIT_GREASE_PENCIL':
self.layout.operator_context = 'INVOKE_REGION_WIN' # needed for popup (also works with 'INVOKE_DEFAULT')
self.layout.operator('gp.batch_reproject_all_frames', icon='KEYTYPE_JITTER_VEC')
def reproject_context_menu(self, context):
if context.mode == 'EDIT_GPENCIL' and context.scene.tool_settings.gpencil_selectmode_edit == 'STROKE':
if context.mode == 'EDIT_GREASE_PENCIL' and context.scene.tool_settings.gpencil_selectmode_edit == 'STROKE':
self.layout.operator_context = 'INVOKE_REGION_WIN' # needed for popup
self.layout.operator('gp.batch_reproject_all_frames', icon='KEYTYPE_JITTER_VEC')
@ -450,12 +450,12 @@ def register():
for cl in classes:
bpy.utils.register_class(cl)
bpy.types.VIEW3D_MT_gpencil_edit_context_menu.append(reproject_context_menu)
bpy.types.VIEW3D_MT_grease_pencil_edit_context_menu.append(reproject_context_menu)
bpy.types.GPENCIL_MT_cleanup.append(reproject_clean_menu)
def unregister():
bpy.types.GPENCIL_MT_cleanup.remove(reproject_clean_menu)
bpy.types.VIEW3D_MT_gpencil_edit_context_menu.remove(reproject_context_menu)
bpy.types.VIEW3D_MT_grease_pencil_edit_context_menu.remove(reproject_context_menu)
for cl in reversed(classes):
bpy.utils.unregister_class(cl)

View File

@ -4,6 +4,8 @@ Blender addon - Various tool to help with grease pencil in animation productions
**[Download latest](https://git.autourdeminuit.com/autour_de_minuit/gp_toolbox/archive/master.zip)**
**[Download for Blender 4.2 and below](https://git.autourdeminuit.com/autour_de_minuit/gp_toolbox/archive/v3.3.0.zip)**
**[Demo video](https://www.youtube.com/watch?v=Htgao_uPWNs)**
**[Readme Doc in French](README_FR.md)**

View File

@ -4,6 +4,8 @@ Blender addon - Boîte à outils de grease pencil pour la production d'animation
**[Télécharger la dernière version](https://git.autourdeminuit.com/autour_de_minuit/gp_toolbox/archive/master.zip)**
**[Télécharger pour Blender 4.2 ou inférieure](https://git.autourdeminuit.com/autour_de_minuit/gp_toolbox/archive/v3.3.0.zip)**
**[Demo video](https://www.youtube.com/watch?v=Htgao_uPWNs)**
**[English Readme Doc](README.md)**

View File

@ -7,7 +7,7 @@ from .utils import get_addon_prefs
class GPTB_WT_eraser(WorkSpaceTool):
bl_space_type = 'VIEW_3D'
bl_context_mode = 'PAINT_GPENCIL'
bl_context_mode = 'PAINT_GREASE_PENCIL'
# The prefix of the idname should be your add-on name.
bl_idname = "gp.eraser_tool"

View File

@ -20,7 +20,7 @@ class GPTB_PT_dataprop_panel(Panel):
# bl_category = "Tool"
# bl_idname = "ADDONID_PT_panel_name"# identifier, if ommited, takes the name of the class.
bl_label = "Pseudo color"# title
bl_parent_id = "DATA_PT_gpencil_layers"#subpanel of this ID
bl_parent_id = "DATA_PT_grease_pencil_layers"#subpanel of this ID
bl_options = {'DEFAULT_CLOSED'}
def draw(self, context):
@ -144,7 +144,7 @@ class GPTB_PT_sidebar_panel(Panel):
col.prop(context.scene.gptoolprops, 'keyframe_type', text='Jump On') # Keyframe Jump
# col.prop(context.space_data.overlay, 'use_gpencil_onion_skin') # not often used
if context.object and context.object.type == 'GPENCIL':
if context.object and context.object.type == 'GREASEPENCIL':
# col.prop(context.object.data, 'use_autolock_layers') # not often used
col.prop(context.object, 'show_in_front') # text='In Front'
@ -191,23 +191,23 @@ class GPTB_PT_anim_manager(Panel):
# import time
# t0 = time.perf_counter()
# objs = [o for o in context.scene.objects if o.type not in ('GPENCIL', 'CAMERA')]
# gps = [o for o in context.scene.objects if o.type == 'GPENCIL']
# objs = [o for o in context.scene.objects if o.type not in ('GREASEPENCIL', 'CAMERA')]
# gps = [o for o in context.scene.objects if o.type == 'GREASEPENCIL']
# cams = [o for o in context.scene.objects if o.type == 'CAMERA']
objs = []
gps = []
cams = []
for o in context.scene.objects:
if o.type not in ('GPENCIL', 'CAMERA'):
if o.type not in ('GREASEPENCIL', 'CAMERA'):
objs.append(o)
elif o.type == 'GPENCIL':
elif o.type == 'GREASEPENCIL':
gps.append(o)
elif o.type == 'CAMERA':
cams.append(o)
# print(f'{time.perf_counter() - t0:.8f}s')
return {'OBJECT': objs, 'GPENCIL': gps, 'CAMERA': cams}
return {'OBJECT': objs, 'GREASEPENCIL': gps, 'CAMERA': cams}
def draw(self, context):
@ -221,7 +221,7 @@ class GPTB_PT_anim_manager(Panel):
col.operator('gp.list_disabled_anims')
## Show Enable / Disable anims
for cat, cat_type in [('Obj anims:', 'OBJECT'), ('Cam anims:', 'CAMERA'), ('Gp anims:', 'GPENCIL')]:
for cat, cat_type in [('Obj anims:', 'OBJECT'), ('Cam anims:', 'CAMERA'), ('Gp anims:', 'GREASEPENCIL')]:
on_icon, off_icon = anim_status(obj_types[cat_type])
subcol = col.column()
@ -242,7 +242,7 @@ class GPTB_PT_anim_manager(Panel):
row = subcol.row(align=True)
row.label(text='Gp modifiers:')
on_icon, off_icon = gp_modifier_status(obj_types['GPENCIL'])
on_icon, off_icon = gp_modifier_status(obj_types['GREASEPENCIL'])
# subcol.alert = off_icon == 'LAYER_ACTIVE' # Turn red
row.operator('gp.toggle_hide_gp_modifier', text='ON', icon=on_icon).show = True
row.operator('gp.toggle_hide_gp_modifier', text='OFF', icon=off_icon).show = False
@ -425,7 +425,7 @@ def palette_manager_menu(self, context):
"""Palette menu to append in existing menu"""
# GPENCIL_MT_material_context_menu
layout = self.layout
# {'EDIT_GPENCIL', 'PAINT_GPENCIL','SCULPT_GPENCIL','WEIGHT_GPENCIL', 'VERTEX_GPENCIL'}
# {'EDIT_GREASE_PENCIL', 'PAINT_GREASE_PENCIL','SCULPT_GREASE_PENCIL','WEIGHT_GREASE_PENCIL', 'VERTEX_GPENCIL'}
layout.separator()
prefs = get_addon_prefs()
@ -658,7 +658,7 @@ class GPTB_PT_tools_grease_pencil_interpolate(Panel):
# settings = context.tool_settings.gpencil_interpolate # old 2.92 global settings
## access active tool settings
# settings = context.workspace.tools[0].operator_properties('gpencil.interpolate')
settings = context.workspace.tools.from_space_view3d_mode('PAINT_GPENCIL').operator_properties('gpencil.interpolate')
settings = context.workspace.tools.from_space_view3d_mode('PAINT_GREASE_PENCIL').operator_properties('gpencil.interpolate')
## custom curve access (still in gp interpolate tools)
interpolate_settings = context.tool_settings.gpencil_interpolate
@ -734,7 +734,7 @@ def interpolate_header_ui(self, context):
layout = self.layout
obj = context.active_object
if obj and obj.type == 'GPENCIL' and context.gpencil_data:
if obj and obj.type == 'GREASEPENCIL' and context.gpencil_data:
gpd = context.gpencil_data
else:
return
@ -768,7 +768,10 @@ def register():
for cls in classes:
bpy.utils.register_class(cls)
bpy.types.GPENCIL_MT_material_context_menu.append(palette_manager_menu)
bpy.types.DOPESHEET_PT_gpencil_layer_display.append(expose_use_channel_color_pref)
bpy.types.DOPESHEET_PT_grease_pencil_mode.append(expose_use_channel_color_pref)
# bpy.types.GPENCIL_MT_material_context_menu.append(palette_manager_menu)
# bpy.types.DOPESHEET_PT_gpencil_layer_display.append(expose_use_channel_color_pref)
# bpy.types.VIEW3D_HT_header.append(interpolate_header_ui) # WIP
# if bpy.app.version >= (3,0,0):
@ -777,8 +780,13 @@ def register():
def unregister():
# bpy.types.VIEW3D_HT_header.remove(interpolate_header_ui) # WIP
bpy.types.DOPESHEET_PT_gpencil_layer_display.remove(expose_use_channel_color_pref)
bpy.types.DOPESHEET_PT_grease_pencil_mode.remove(expose_use_channel_color_pref)
bpy.types.GPENCIL_MT_material_context_menu.remove(palette_manager_menu)
# bpy.types.DOPESHEET_PT_gpencil_layer_display.remove(expose_use_channel_color_pref)
# bpy.types.GPENCIL_MT_material_context_menu.remove(palette_manager_menu)
# if bpy.app.version >= (3,0,0):
# bpy.types.ASSETBROWSER_PT_metadata.remove(asset_browser_ui)

View File

@ -4,8 +4,8 @@ bl_info = {
"name": "GP toolbox",
"description": "Tool set for Grease Pencil in animation production",
"author": "Samuel Bernou, Christophe Seux",
"version": (3, 3, 0),
"blender": (4, 0, 0),
"version": (4, 0, 0),
"blender": (4, 3, 0),
"location": "Sidebar (N menu) > Gpencil > Toolbox / Gpencil properties",
"warning": "",
"doc_url": "https://git.autourdeminuit.com/autour_de_minuit/gp_toolbox",

View File

@ -99,7 +99,7 @@ def gp_stroke_angle_split (frame, strokes, angle):
splitted_loops = bm_angle_split(bm,angle)
frame.strokes.remove(stroke_info['stroke'])
frame.drawing.strokes.remove(stroke_info['stroke'])
for loop in splitted_loops :
loop_info = [{'co':v.co,'strength': v[strength], 'pressure' :v[pressure],'select':v[select]} for v in loop]
new_stroke = draw_gp_stroke(loop_info,frame,palette,width = line_width)

View File

@ -21,12 +21,12 @@ def update_layer_name(self, context):
if not self.layer_name:
# never replace by nothing (since there should be prefix/suffix)
return
if not context.object or context.object.type != 'GPENCIL':
if not context.object or context.object.type != 'GREASEPENCIL':
return
if not context.object.data.layers.active:
return
layer_name_build(context.object.data.layers.active, desc=self.layer_name)
# context.object.data.layers.active.info = self.layer_name
# context.object.data.layers.active.name = self.layer_name
class GP_PG_FixSettings(PropertyGroup):

View File

@ -29,9 +29,9 @@ def get_matrix(ob) :
return ob.matrix_world.copy()
def set_matrix(gp_frame,mat):
for stroke in gp_frame.strokes :
for stroke in gp_frame.drawing.strokes :
for point in stroke.points :
point.co = mat @ point.co
point.position = mat @ point.position
# get view vector location (the 2 methods work fine)
def get_view_origin_position():
@ -160,7 +160,7 @@ def simple_draw_gp_stroke(pts, frame, width = 2, mat_id = 0):
draw basic stroke by passing list of point 3D coordinate
the frame to draw on and optional width parameter (default = 2)
'''
stroke = frame.strokes.new()
stroke = frame.drawing.strokes.new()
stroke.line_width = width
stroke.display_mode = '3DSPACE'
stroke.material_index = mat_id
@ -173,12 +173,12 @@ def simple_draw_gp_stroke(pts, frame, width = 2, mat_id = 0):
# for i, pt in enumerate(pts):
# stroke.points.add()
# dest_point = stroke.points[i]
# dest_point.co = pt
# dest_point.position = pt
return stroke
## OLD - need update
def draw_gp_stroke(loop_info, frame, palette, width = 2) :
stroke = frame.strokes.new(palette)
stroke = frame.drawing.strokes.new(palette)
stroke.line_width = width
stroke.display_mode = '3DSPACE'# old -> draw_mode
@ -372,24 +372,24 @@ def create_gp_palette(gp_data_block,info) :
def get_gp_objects(selection=True):
'''return selected objects or only the active one'''
if not bpy.context.active_object or bpy.context.active_object.type != 'GPENCIL':
if not bpy.context.active_object or bpy.context.active_object.type != 'GREASEPENCIL':
print('No active GP object')
return []
active = bpy.context.active_object
if selection:
selection = [o for o in bpy.context.selected_objects if o.type == 'GPENCIL']
selection = [o for o in bpy.context.selected_objects if o.type == 'GREASEPENCIL']
if not active in selection:
selection += [active]
return selection
if bpy.context.active_object and bpy.context.active_object.type == 'GPENCIL':
if bpy.context.active_object and bpy.context.active_object.type == 'GREASEPENCIL':
return [active]
return []
def get_gp_datas(selection=True):
'''return selected objects or only the active one'''
if not bpy.context.active_object or bpy.context.active_object.type != 'GPENCIL':
if not bpy.context.active_object or bpy.context.active_object.type != 'GREASEPENCIL':
print('No active GP object')
return []
@ -397,15 +397,15 @@ def get_gp_datas(selection=True):
if selection:
selected = []
for o in bpy.context.selected_objects:
if o.type == 'GPENCIL':
if o.type == 'GREASEPENCIL':
if o.data not in selected:
selected.append(o.data)
# selected = [o.data for o in bpy.context.selected_objects if o.type == 'GPENCIL']
# selected = [o.data for o in bpy.context.selected_objects if o.type == 'GREASEPENCIL']
if not active_data in selected:
selected += [active_data]
return selected
if bpy.context.active_object and bpy.context.active_object.type == 'GPENCIL':
if bpy.context.active_object and bpy.context.active_object.type == 'GREASEPENCIL':
return [active_data]
print('EOL. No active GP object')
@ -440,7 +440,7 @@ def get_active_frame(layer_name=None):
if layer_name:
lay = bpy.context.scene.grease_pencil.layers.get(layer_name)
if lay:
frame = lay.active_frame
frame = lay.current_frame()
if frame:
return frame
else:
@ -449,7 +449,7 @@ def get_active_frame(layer_name=None):
print('no layers named', layer_name, 'in scene layers')
else:#active layer
frame = bpy.context.scene.grease_pencil.layers.active.active_frame
frame = bpy.context.scene.grease_pencil.layers.active.current_frame()
if frame:
return frame
else:
@ -457,7 +457,7 @@ def get_active_frame(layer_name=None):
def get_stroke_2D_coords(stroke):
'''return a list containing points 2D coordinates of passed gp stroke object'''
return [location_to_region(p.co) for p in stroke.points]
return [location_to_region(p.position) for p in stroke.points]
'''#foreach method for retreiving multiple other attribute quickly and stack them
point_nb = len(stroke.points)
@ -472,14 +472,14 @@ def get_stroke_2D_coords(stroke):
def get_all_stroke_2D_coords(frame):
'''return a list of lists with all strokes's points 2D location'''
## using modification from get_stroke_2D_coords func'
return [get_stroke_2D_coords(s) for s in frame.strokes]
return [get_stroke_2D_coords(s) for s in frame.drawing.strokes]
## direct
#return[[location_to_region(p.co) for p in s.points] for s in frame.strokes]
#return[[location_to_region(p.position) for p in s.points] for s in frame.drawing.strokes]
def selected_strokes(frame):
'''return all stroke having a point selected as a list of strokes objects'''
stlist = []
for i, s in enumerate(frame.strokes):
for i, s in enumerate(frame.drawing.strokes):
if any(pt.select for pt in s.points):
stlist.append(s)
return stlist
@ -491,7 +491,7 @@ def copy_stroke_to_frame(s, frame, select=True):
return created stroke
'''
ns = frame.strokes.new()
ns = frame.drawing.strokes.new()
## Set strokes attr
stroke_attr = [
@ -1206,7 +1206,7 @@ def all_anim_enabled(objects) -> bool:
if fcu.mute:
return False
if o.type in ('GPENCIL', 'CAMERA'):
if o.type in ('GREASEPENCIL', 'CAMERA'):
if o.data.animation_data and o.data.animation_data.action:
## Check if object data attributes fcurves are muted
for fcu in o.animation_data.action.fcurves:
@ -1219,9 +1219,9 @@ def all_anim_enabled(objects) -> bool:
def all_object_modifier_enabled(objects) -> bool:
'''Return False if one modifier of one object has GP modifier disabled in viewport but enabled in render'''
for o in objects:
if o.type != 'GPENCIL':
if o.type != 'GREASEPENCIL':
continue
for m in o.grease_pencil_modifiers:
for m in o.modifier:
if m.show_render and not m.show_viewport:
return False
@ -1247,7 +1247,7 @@ def has_fully_enabled_anim(o):
if fcu.mute:
return False
if o.type in ('GPENCIL', 'CAMERA'):
if o.type in ('GREASEPENCIL', 'CAMERA'):
if o.data.animation_data and o.data.animation_data.action:
## Check if object data attributes fcurves are muted
for fcu in o.animation_data.action.fcurves:
@ -1292,7 +1292,7 @@ def anim_status(objects) -> tuple((str, str)):
on_count += 1
count += 1
if o.type in ('GPENCIL', 'CAMERA'):
if o.type in ('GREASEPENCIL', 'CAMERA'):
datablock = o.data
if datablock.animation_data is None:
continue
@ -1320,12 +1320,12 @@ def gp_modifier_status(objects) -> tuple((str, str)):
'''return icons on/off tuple'''
on_count = off_count = count = 0
for o in objects:
if o.type != 'GPENCIL':
if o.type != 'GREASEPENCIL':
continue
## Skip hided object
if o.hide_get() and o.hide_render:
continue
for m in o.grease_pencil_modifiers:
for m in o.modifier:
if m.show_render and not m.show_viewport:
off_count += 1
else: