diff --git a/OP_copy_paste.py b/OP_copy_paste.py index 4c6f9a3..08f43cf 100644 --- a/OP_copy_paste.py +++ b/OP_copy_paste.py @@ -48,6 +48,7 @@ def convertAttr(Attr): def getMatrix (layer) : matrix = mathutils.Matrix.Identity(4) + ## FIXME: not "is_parent" or parent_type anymore if layer.is_parented: if layer.parent_type == 'BONE': object = layer.parent @@ -175,26 +176,30 @@ def copycut_strokes(layers=None, copy=True, keep_empty=True): if not copy: staylist = [] # init part of strokes that must survive on this layer - for s in f.drawing.strokes: - if s.select: + rm_list = [] # init strokes that must be removed from this layer + for s_index, stroke in f.drawing.strokes: + if stroke.select: # separate in multiple stroke if parts of the strokes a selected. - sel = [i for i, p in enumerate(s.points) if p.select] + sel = [i for i, p in enumerate(stroke.points) if p.select] substrokes = [] # list of list containing isolated selection - for k, g in groupby(enumerate(sel), lambda x:x[0]-x[1]):# continuity stroke have same substract result between point index and enumerator + + # continuity stroke have same substract result between point index and enumerator + for k, g in groupby(enumerate(sel), lambda x:x[0]-x[1]): group = list(map(itemgetter(1), g)) substrokes.append(group) for ss in substrokes: if len(ss) > 1: # avoid copy isolated points - stroke_list.append(dump_gp_stroke_range(s,ss,l,obj)) + stroke_list.append(dump_gp_stroke_range(stroke, ss, l, obj)) # Cutting operation if not copy: - maxindex = len(s.points)-1 + maxindex = len(stroke.points)-1 if len(substrokes) == maxindex+1: # if only one substroke, then it's the full stroke - f.drawing.strokes.remove(s) + # f.drawing.strokes.remove(stroke) + rm_list.append(stroke) else: - neg = [i for i, p in enumerate(s.points) if not p.select] + neg = [i for i, p in enumerate(stroke.points) if not p.select] staying = [] for k, g in groupby(enumerate(neg), lambda x:x[0]-x[1]): @@ -208,32 +213,32 @@ def copycut_strokes(layers=None, copy=True, keep_empty=True): for ns in staying: if len(ns) > 1: - staylist.append(dump_gp_stroke_range(s,ns,l,obj)) + staylist.append(dump_gp_stroke_range(stroke, ns, l, obj)) # make a negative list containing all last index '''#full stroke version - # if s.colorname == color: #line for future filters - stroke_list.append(dump_gp_stroke(s,l)) + # if stroke.colorname == color: #line for future filters + stroke_list.append(dump_gp_stroke(stroke,l)) #delete stroke on the fly if not copy: - f.drawing.strokes.remove(s) + f.drawing.strokes.remove(stroke) ''' - + if rm_list: + f.drawing.remove_strokes(indices=rm_list) + if not copy: + selected_ids = [i for i, s in enumerate(f.drawing.strokes) if s.select] # delete all selected strokes... - for s in f.drawing.strokes: - if s.select: - f.drawing.strokes.remove(s) + f.drawing.strokes.remove_strokes(indices=selected_ids) + # ...recreate these uncutted ones #pprint(staylist) if staylist: add_multiple_strokes(staylist, l) - #for ns in staylist:#weirdly recreate the stroke twice ! - # add_stroke(ns, f, l) - #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 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.drawing.strokes): l.frames.remove(f) diff --git a/OP_layer_manager.py b/OP_layer_manager.py index aca1fda..d787307 100644 --- a/OP_layer_manager.py +++ b/OP_layer_manager.py @@ -819,7 +819,7 @@ def register(): 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.types.GREASE_PENCIL_MT_grease_pencil_add_layer_extra.append(gpencil_layer_dropdown_menu) bpy.app.handlers.load_post.append(subscribe_layer_change_handler) register_keymaps() @@ -829,7 +829,7 @@ def register(): def unregister(): unregister_keymaps() bpy.app.handlers.load_post.remove(subscribe_layer_change_handler) - bpy.types.GPENCIL_MT_layer_context_menu.remove(gpencil_layer_dropdown_menu) + bpy.types.GREASE_PENCIL_MT_grease_pencil_add_layer_extra.remove(gpencil_layer_dropdown_menu) bpy.types.DOPESHEET_HT_header.remove(gpencil_dopesheet_header) bpy.types.DATA_PT_grease_pencil_layers.remove(layer_name_builder_ui) diff --git a/OP_realign.py b/OP_realign.py index 9f8fe70..73ecadf 100644 --- a/OP_realign.py +++ b/OP_realign.py @@ -46,7 +46,7 @@ bpy.ops.object.mode_set(mode=omode) def batch_reproject(obj, proj_type='VIEW', all_strokes=True, restore_frame=False): '''Reproject - ops method - :all_stroke: affect hided, locked layers + :all_stroke: affect hidden, locked layers ''' if restore_frame: @@ -77,8 +77,12 @@ def batch_reproject(obj, proj_type='VIEW', all_strokes=True, restore_frame=False continue f = next((f for f in l.frames if f.frame_number == i), None) if f is None: + # FIXME: some strokes are ingored + # print(f'skip {l.name}, no frame at {i}') continue for s in f.drawing.strokes: + # print(l.name, s.material_index) + ## Batch matrix apply (Here is slower than list comprehension). # nb_points = len(s.points) # coords = np.empty(nb_points * 3, dtype='float64') @@ -90,17 +94,16 @@ def batch_reproject(obj, proj_type='VIEW', all_strokes=True, restore_frame=False 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.position = obj.matrix_world.inverted() @ new_world_co_3d[i] + # Basic method (Slower than foreach_set and compatible with GPv3) + ## TODO: use low level api with curve offsets... + for i, p in enumerate(s.points): + p.position = matrix_inv @ new_world_co_3d[i] + ## GPv2: ravel and use foreach_set ## Ravel new coordinate on the fly - new_local_coords = [axis for p in new_world_co_3d for axis in matrix_inv @ p] - - ## Set points in obj local space (apply matrix slower) - # new_local_coords = utils.matrix_transform(new_world_co_3d, matrix_inv).ravel() - s.points.foreach_set('co', new_local_coords) - + ## NOTE: Set points in obj local space (apply matrix is slower): new_local_coords = utils.matrix_transform(new_world_co_3d, matrix_inv).ravel() + # new_local_coords = [axis for p in new_world_co_3d for axis in matrix_inv @ p] + # s.points.foreach_set('co', new_local_coords) if restore_frame: bpy.context.scene.frame_current = oframe @@ -142,21 +145,21 @@ def align_global(reproject=True, ref=None, all_strokes=True): for s in f.drawing.strokes: ## foreach 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]) - # s.points.update() # seem to works # but adding/deleting a point is "safer" - ## force update - s.points.add(1) - s.points.pop() - # for p in s.points: + ## GPv2 + # s.points.foreach_set('co', [co for v in coords for co in v]) + # # s.points.update() # seem to works # but adding/deleting a point is "safer" + # ## force update + # s.points.add(1) + # s.points.pop() + + for p in s.points: ## GOOD : # world_co = mat @ p.position # p.position = new_mat.inverted() @ world_co ## GOOD : - # p.position = p.position @ mat.inverted() @ new_mat + p.position = p.position @ mat.inverted() @ new_mat if o.parent: o.matrix_world = new_mat @@ -450,12 +453,12 @@ def register(): for cl in classes: bpy.utils.register_class(cl) - bpy.types.VIEW3D_MT_grease_pencil_edit_context_menu.append(reproject_context_menu) - bpy.types.GPENCIL_MT_cleanup.append(reproject_clean_menu) + bpy.types.VIEW3D_MT_greasepencil_edit_context_menu.append(reproject_context_menu) + bpy.types.VIEW3D_MT_edit_greasepencil_cleanup.append(reproject_clean_menu) def unregister(): - bpy.types.GPENCIL_MT_cleanup.remove(reproject_clean_menu) - bpy.types.VIEW3D_MT_grease_pencil_edit_context_menu.remove(reproject_context_menu) + bpy.types.VIEW3D_MT_edit_greasepencil_cleanup.remove(reproject_clean_menu) + bpy.types.VIEW3D_MT_greasepencil_edit_context_menu.remove(reproject_context_menu) for cl in reversed(classes): bpy.utils.unregister_class(cl) \ No newline at end of file diff --git a/UI_tools.py b/UI_tools.py index d9d1618..e4f1182 100644 --- a/UI_tools.py +++ b/UI_tools.py @@ -166,7 +166,9 @@ class GPTB_PT_sidebar_panel(Panel): else: col.label(text='No GP object selected') - col.prop(context.scene.gptoolprops, 'edit_lines_opacity') + + ## Gpv3: not more edit line (use Curve lines) + # col.prop(context.scene.gptoolprops, 'edit_lines_opacity') # Mention update as notice # addon_updater_ops.update_notice_box_ui(self, context) diff --git a/functions.py b/functions.py index 4fb5b7b..f9421ad 100644 --- a/functions.py +++ b/functions.py @@ -99,6 +99,7 @@ def gp_stroke_angle_split (frame, strokes, angle): splitted_loops = bm_angle_split(bm,angle) + ## FIXME: Should use -> drawing.remove_strokes(indices=(0,)) 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] @@ -123,6 +124,7 @@ def gp_stroke_uniform_density(cam, frame, strokes, max_spacing): bm_uniform_density(bm,cam,max_spacing) + ## FIXME: Should use -> drawing.remove_strokes(indices=(0,)) frame.strokes.remove(stroke_info['stroke']) bm.verts.ensure_lookup_table() diff --git a/properties.py b/properties.py index e290d81..c3ca3d2 100755 --- a/properties.py +++ b/properties.py @@ -182,9 +182,10 @@ class GP_PG_ToolsSettings(PropertyGroup): name='Cursor Follow', description="3D cursor follow active object animation when activated", default=False, update=cursor_follow_update) - edit_lines_opacity : FloatProperty( - name="Edit Lines Opacity", description="Change edit lines opacity for all grease pencils", - default=0.5, min=0.0, max=1.0, step=3, precision=2, update=change_edit_lines_opacity) + ## gpv3 : no edit line color anymore + # edit_lines_opacity : FloatProperty( + # name="Edit Lines Opacity", description="Change edit lines opacity for all grease pencils", + # default=0.5, min=0.0, max=1.0, step=3, precision=2, update=change_edit_lines_opacity) ## render name_for_current_render : StringProperty( diff --git a/utils.py b/utils.py index 761a1f4..4afeb3b 100644 --- a/utils.py +++ b/utils.py @@ -706,20 +706,7 @@ def set_collection(ob, collection, unlink=True) : # ----------------- def get_addon_prefs(): - ''' - function to read current addon preferences properties - - access a prop like this : - prefs = get_addon_prefs() - option_state = prefs.super_special_option - - oneliner : get_addon_prefs().super_special_option - ''' - import os - addon_name = os.path.splitext(__name__)[0] - preferences = bpy.context.preferences - addon_prefs = preferences.addons[addon_name].preferences - return (addon_prefs) + return bpy.context.preferences.addons[__package__].preferences def open_addon_prefs(): '''Open addon prefs windows with focus on current addon'''